Commit 4948045b authored by Valentin Platzgummer's avatar Valentin Platzgummer

MeasurementArea prepared for new NemoInterface

parent 0e658bb3
......@@ -226,11 +226,6 @@ contains (DEFINES, DISABLE_AIRMAP) {
}
}
# GeograpicLib (TODO: add Windows support!)
LinuxBuild {
LIBS += -L$$PWD/libs/libGeographic -lGeographic # libGeograpic.so.17
}
# google or-tools (TODO: add Windows support!)
LinuxBuild {
OR_TOOLS_PATH = $$PWD/libs/or-tools-src-ubuntu
......
......@@ -445,7 +445,11 @@ contains (DEFINES, QGC_ENABLE_PAIRING) {
#
HEADERS += \
src/MeasurementComplexItem/geometry/ProgressArray.h \
src/MeasurementComplexItem/geometry/TileDiff.h \
src/MeasurementComplexItem/geometry/geometry.h \
src/MeasurementComplexItem/HashFunctions.h \
src/MeasurementComplexItem/nemo_interface/MeasurementTile.h \
src/QmlControls/QmlUnitsConversion.h \
src/MeasurementComplexItem/geometry/GeoArea.h \
src/MeasurementComplexItem/geometry/MeasurementArea.h \
......@@ -487,7 +491,6 @@ HEADERS += \
src/MeasurementComplexItem/nemo_interface/QNemoHeartbeat.h \
src/MeasurementComplexItem/nemo_interface/QNemoProgress.h \
src/MeasurementComplexItem/nemo_interface/QNemoProgress.h \
src/MeasurementComplexItem/nemo_interface/SnakeTile.h \
src/MeasurementComplexItem/nemo_interface/SnakeTileLocal.h \
src/MeasurementComplexItem/nemo_interface/SnakeTiles.h \
src/MeasurementComplexItem/nemo_interface/SnakeTilesLocal.h \
......@@ -498,6 +501,7 @@ HEADERS += \
src/api/QmlComponentInfo.h \
src/GPS/Drivers/src/base_station.h \
src/Settings/WimaSettings.h \
src/comm/QmlObjectListHelper.h \
src/comm/ros_bridge/include/RosBridgeClient.h \
src/comm/ros_bridge/include/com_private.h \
src/comm/ros_bridge/include/message_traits.h \
......@@ -525,6 +529,8 @@ SOURCES += \
src/MeasurementComplexItem/geometry/MeasurementArea.cc \
src/MeasurementComplexItem/geometry/SafeArea.cc \
src/MeasurementComplexItem/geometry/geometry.cpp \
src/MeasurementComplexItem/HashFunctions.cpp \
src/MeasurementComplexItem/nemo_interface/MeasurementTile.cpp \
src/Vehicle/VehicleEscStatusFactGroup.cc \
src/MeasurementComplexItem/AreaData.cc \
src/api/QGCCorePlugin.cc \
......@@ -541,7 +547,7 @@ SOURCES += \
src/MeasurementComplexItem/geometry/GeoPoint3D.cpp \
src/MeasurementComplexItem/NemoInterface.cpp \
src/MeasurementComplexItem/nemo_interface/QNemoProgress.cc \
src/MeasurementComplexItem/nemo_interface/SnakeTile.cpp \
src/comm/QmlObjectListHelper.cpp \
src/comm/ros_bridge/include/RosBridgeClient.cpp \
src/comm/ros_bridge/include/com_private.cpp \
src/comm/ros_bridge/include/messages/geographic_msgs/geopoint.cpp \
......
......@@ -10,7 +10,7 @@
#include "geometry/MeasurementArea.h"
#include "geometry/SafeArea.h"
#include "geometry/clipper/clipper.hpp"
#include "nemo_interface/SnakeTile.h"
#include "nemo_interface/MeasurementTile.h"
QGC_LOGGING_CATEGORY(CircularGeneratorLog, "CircularGeneratorLog")
......@@ -118,31 +118,22 @@ bool CircularGenerator::get(Work &work) {
auto pPolygon = std::make_shared<geometry::FPolygon>();
geometry::areaToEnu(origin, geoPolygon, *pPolygon);
// Progress and tiles.
const auto &progress = measurementArea->progress();
// Collect tiles with progress == 100 %.
const auto *tiles = measurementArea->tiles();
auto pTiles = std::make_shared<std::vector<geometry::FPolygon>>();
if (progress.size() == tiles->count()) {
for (int i = 0; i < tiles->count(); ++i) {
if (progress[i] == 100) {
const auto *obj = (*tiles)[int(i)];
const auto *tile = qobject_cast<const SnakeTile *>(obj);
if (tile != nullptr) {
geometry::FPolygon tileENU;
geometry::areaToEnu(origin, tile->coordinateList(), tileENU);
pTiles->push_back(std::move(tileENU));
} else {
qCDebug(CircularGeneratorLog)
<< "get(): progress.size() != tiles->count().";
return false;
}
for (int i = 0; i < tiles->count(); ++i) {
const auto tile =
qobject_cast<const MeasurementTile *>(tiles->operator[](i));
if (tile != nullptr) {
if (qFuzzyCompare(tile->progress(), 100)) {
geometry::FPolygon tileENU;
geometry::areaToEnu(origin, tile->coordinateList(), tileENU);
pTiles->push_back(std::move(tileENU));
}
} else {
qCDebug(CircularGeneratorLog) << "get(): tile == nullptr.";
return false;
}
} else {
qCDebug(CircularGeneratorLog)
<< "get(): progress.size() != tiles->count().";
return false;
}
auto serviceArea = getGeoArea<const SafeArea *>(*this->_d->areaList());
......
#ifndef HASHFUNCTIONS_H
#define HASHFUNCTIONS_H
#include <QGeoCoordinate>
#include <functional>
#include <QmlObjectListModel.h>
namespace std {
template <> struct hash<QGeoCoordinate> {
std::size_t operator()(const QGeoCoordinate &c) {
hash<double> h;
return h(c.latitude()) ^ h(c.longitude()) ^ h(c.altitude());
}
};
template <template <class> class Container, class EntryType>
struct hash<Container<EntryType>> {
std::size_t operator()(const Container<EntryType> &list) {
std::size_t value = 0;
hash<EntryType> h;
for (const auto it = std::begin(list); it != std::end(list); ++it) {
value ^= h(*it);
}
return value;
}
};
} // namespace std
#endif // HASHFUNCTIONS_H
......@@ -9,7 +9,7 @@
#include "geometry/clipper/clipper.hpp"
#include "RoutingThread.h"
#include "nemo_interface/SnakeTile.h"
#include "nemo_interface/MeasurementTile.h"
namespace routing {
......@@ -100,29 +100,21 @@ bool LinearGenerator::get(Work &generator) {
geometry::areaToEnu(origin, geoPolygon, *pPolygon);
// Progress and tiles.
const auto &progress = measurementArea->progress();
const auto *tiles = measurementArea->tiles();
auto pTiles = std::make_shared<std::vector<geometry::FPolygon>>();
if (progress.size() == tiles->count()) {
for (int i = 0; i < tiles->count(); ++i) {
if (progress[i] == 100) {
const QObject *obj = (*tiles)[int(i)];
const auto *tile = qobject_cast<const SnakeTile *>(obj);
if (tile != nullptr) {
geometry::FPolygon tileENU;
geometry::areaToEnu(origin, tile->coordinateList(), tileENU);
pTiles->push_back(std::move(tileENU));
} else {
qCDebug(LinearGeneratorLog) << "get(): tile == nullptr";
return false;
}
for (int i = 0; i < tiles->count(); ++i) {
const auto tile =
qobject_cast<const MeasurementTile *>(tiles->operator[](i));
if (qFuzzyCompare(tile->progress(), 100)) {
if (tile != nullptr) {
geometry::FPolygon tileENU;
geometry::areaToEnu(origin, tile->coordinateList(), tileENU);
pTiles->push_back(std::move(tileENU));
} else {
qCDebug(LinearGeneratorLog) << "get(): tile == nullptr";
return false;
}
}
} else {
qCDebug(LinearGeneratorLog)
<< "get(): progress.size() != tiles->count().";
return false;
}
auto serviceArea = getGeoArea<const SafeArea *>(*this->_d->areaList());
......
......@@ -8,7 +8,7 @@
#include "geometry/SafeArea.h"
#include "geometry/clipper/clipper.hpp"
#include "geometry/geometry.h"
#include "nemo_interface/SnakeTile.h"
#include "nemo_interface/MeasurementTile.h"
// QGC
#include "JsonHelper.h"
......
......@@ -17,7 +17,7 @@
#include "geometry/geometry.h"
#include "nemo_interface/QNemoHeartbeat.h"
#include "nemo_interface/QNemoProgress.h"
#include "nemo_interface/SnakeTile.h"
#include "nemo_interface/MeasurementTile.h"
#include "ros_bridge/include/messages/geographic_msgs/geopoint.h"
#include "ros_bridge/include/messages/jsk_recognition_msgs/polygon_array.h"
......@@ -158,7 +158,7 @@ void NemoInterface::Impl::setTileData(const TileData &tileData) {
UniqueLock lk2(this->tilesENUMutex, std::adopt_lock);
const auto *obj = tileData.tiles[0];
const auto *tile = qobject_cast<const SnakeTile *>(obj);
const auto *tile = qobject_cast<const MeasurementTile *>(obj);
if (tile != nullptr) {
if (tile->coordinateList().size() > 0) {
if (tile->coordinateList().first().isValid()) {
......@@ -167,7 +167,7 @@ void NemoInterface::Impl::setTileData(const TileData &tileData) {
this->tilesENU.polygons().clear();
for (int i = 0; i < tileData.tiles.count(); ++i) {
obj = tileData.tiles[i];
tile = qobject_cast<const SnakeTile *>(obj);
tile = qobject_cast<const MeasurementTile *>(obj);
if (tile != nullptr) {
SnakeTileLocal tileENU;
geometry::areaToEnu(origin, tile->coordinateList(), tileENU.path());
......
#include "MeasurementArea.h"
#include "QtConcurrentRun"
#include "HashFunctions.h"
#include "geometry.h"
#include "nemo_interface/SnakeTile.h"
#include "nemo_interface/MeasurementTile.h"
#include <ctime>
#include "QtConcurrentRun"
#include <QJsonArray>
#include <QQmlEngine>
#include <boost/units/systems/si.hpp>
#include "JsonHelper.h"
#include "QGCLoggingCategory.h"
#include "QmlObjectListHelper.h"
#include <QJsonArray>
#ifndef SNAKE_MAX_TILES
#define SNAKE_MAX_TILES 1000
#ifndef MAX_TILES
#define MAX_TILES 1000
#endif
using namespace geometry;
......@@ -26,142 +30,31 @@ bool getTiles(const FPolygon &area, Length tileHeight, Length tileWidth,
QGC_LOGGING_CATEGORY(MeasurementAreaLog, "MeasurementAreaLog")
namespace {
const char *tileCenterPointsKey = "TileCenterPoints";
const char *tileArrayKey = "TileArray";
} // namespace
TileData::TileData() : tiles(this) {}
TileData::~TileData() { tiles.clearAndDeleteContents(); }
TileData &TileData::operator=(const TileData &other) {
this->tiles.clearAndDeleteContents();
for (std::size_t i = 0; i < std::size_t(other.tiles.count()); ++i) {
const auto *obj = other.tiles[i];
const auto *tile = qobject_cast<const SnakeTile *>(obj);
if (tile != nullptr) {
this->tiles.append(tile->clone(this));
} else {
qCWarning(MeasurementAreaLog) << "TileData::operator=: nullptr";
}
}
this->tileCenterPoints = other.tileCenterPoints;
return *this;
}
bool TileData::operator==(const TileData &other) const {
if (this->tileCenterPoints == other.tileCenterPoints &&
this->tiles.count() == other.tiles.count()) {
for (int i = 0; i < other.tiles.count(); ++i) {
if (this->tiles[i] != other.tiles[i]) {
return false;
}
}
return true;
} else {
return false;
}
}
TileData::TileData() {}
TileData::~TileData() {}
TileData &TileData::operator=(const TileData &other) { return *this; }
bool TileData::operator==(const TileData &other) const { return false; }
bool TileData::operator!=(const TileData &other) const {
return !this->operator==(other);
}
void TileData::saveToJson(QJsonObject &json) {
// save center points
QJsonValue jsonCenterPoints;
JsonHelper::saveGeoCoordinateArray(tileCenterPoints, false, jsonCenterPoints);
json[tileCenterPointsKey] = std::move(jsonCenterPoints);
// save tiles
QJsonArray jsonTiles;
for (int i = 0; i < tiles.count(); ++i) {
auto tile = tiles.value<SnakeTile *>(i);
if (tile != nullptr) {
QJsonObject jsonTile;
tile->saveToJson(jsonTile);
jsonTiles.append(jsonTile);
} else {
qCritical() << "TileData::saveToJson(): Object other than SnakeTile "
"inside tiles (QmlObjectListModel))";
Q_ASSERT(tile != nullptr);
}
}
json[tileArrayKey] = std::move(jsonTiles);
return operator==(other);
}
void TileData::saveToJson(QJsonObject &json) {}
bool TileData::loadFromJson(const QJsonObject &json, QString &errorString) {
clear();
// load tiles
if (json.contains(tileArrayKey) && json[tileArrayKey].isArray()) {
QString e;
for (const auto &jsonTile : json[tileArrayKey].toArray()) {
auto tile = new SnakeTile(this);
if (tile->loadFromJson(jsonTile.toObject(), e)) {
tiles.append(tile);
} else {
tile->deleteLater();
errorString.append(e);
return false;
}
}
} else {
errorString.append(tr("Not able to load tiles.\n"));
qCWarning(MeasurementAreaLog)
<< "Not able to load tiles. tileArrayKey missing or wrong type.";
if (json.contains(tileArrayKey)) {
qCWarning(MeasurementAreaLog)
<< "tile array type: " << json[tileArrayKey].type();
}
return false;
}
// load center points
if (json.contains(tileCenterPointsKey) &&
json[tileCenterPointsKey].isArray()) {
QString e;
if (!JsonHelper::loadGeoCoordinateArray(json[tileCenterPointsKey], false,
tileCenterPoints, e)) {
errorString.append(e);
errorString.append("\n");
return false;
}
} else {
errorString.append(tr("Not able to load center points.\n"));
qCWarning(MeasurementAreaLog)
<< "Not able to load center points. tileCenterPointsKey missing or "
"wrong type.";
if (json.contains(tileCenterPointsKey)) {
qCWarning(MeasurementAreaLog)
<< "center points type: " << json[tileCenterPointsKey].type();
}
return false;
}
return true;
}
void TileData::clear() {
this->tiles.clearAndDeleteContents();
this->tileCenterPoints.clear();
return false;
}
size_t TileData::size() const {
if (tiles.count() == tileCenterPoints.size()) {
return tiles.count();
} else {
return 0;
}
}
void TileData::clear() {}
std::size_t TileData::size() const { return 0; }
const char *MeasurementArea::settingsGroup = "MeasurementArea";
const char *tileHeightKey = "TileHeight";
const char *tileWidthName = "TileWidth";
const char *minTileAreaKey = "MinTileAreaPercent";
const char *showTilesKey = "ShowTiles";
const char *progressKey = "Progress";
const char *tileKey = "Tiles";
const char *MeasurementArea::nameString = "Measurement Area";
......@@ -179,7 +72,8 @@ MeasurementArea::MeasurementArea(QObject *parent)
this /* QObject parent */)),
_showTiles(SettingsFact(settingsGroup, _metaDataMap[showTilesKey],
this /* QObject parent */)),
_holdProgress(false), _state(STATE::IDLE) {
_tiles(new QmlObjectListModel()), _holdProgress(false),
_state(STATE::IDLE) {
init();
}
......@@ -197,7 +91,8 @@ MeasurementArea::MeasurementArea(const MeasurementArea &other, QObject *parent)
this /* QObject parent */)),
_showTiles(SettingsFact(settingsGroup, _metaDataMap[showTilesKey],
this /* QObject parent */)),
_holdProgress(false), _state(STATE::IDLE) {
_tiles(new QmlObjectListModel()), _holdProgress(false),
_state(STATE::IDLE) {
init();
disableUpdate();
......@@ -207,8 +102,12 @@ MeasurementArea::MeasurementArea(const MeasurementArea &other, QObject *parent)
_showTiles = other._showTiles;
if (other.ready()) {
_progress = other._progress;
_tileData = other._tileData;
for (int i = 0; i < other._tiles->count(); ++i) {
_tiles->append(
qobject_cast<const MeasurementTile *>(other._tiles->operator[](i))
->clone(_tiles.get()));
}
_tileMap = other._tileMap;
enableUpdate();
} else {
enableUpdate();
......@@ -226,8 +125,13 @@ MeasurementArea &MeasurementArea::operator=(const MeasurementArea &other) {
_showTiles = other._showTiles;
if (other.ready()) {
_progress = other._progress;
_tileData = other._tileData;
_tiles->clearAndDeleteContents();
for (int i = 0; i < other._tiles->count(); ++i) {
_tiles->append(
qobject_cast<const MeasurementTile *>(other._tiles->operator[](i))
->clone(_tiles.get()));
}
_tileMap = other._tileMap;
enableUpdate();
} else {
enableUpdate();
......@@ -236,7 +140,7 @@ MeasurementArea &MeasurementArea::operator=(const MeasurementArea &other) {
return *this;
}
MeasurementArea::~MeasurementArea() {}
MeasurementArea::~MeasurementArea() { _tiles->clearAndDeleteContents(); }
QString MeasurementArea::mapVisualQML() const {
return QStringLiteral("MeasurementAreaMapVisual.qml");
......@@ -259,32 +163,21 @@ Fact *MeasurementArea::minTileArea() { return &_minTileAreaPercent; }
Fact *MeasurementArea::showTiles() { return &_showTiles; }
QmlObjectListModel *MeasurementArea::tiles() { return &this->_tileData.tiles; }
const QVector<int> &MeasurementArea::progress() const {
return this->_progress;
}
QVector<int> MeasurementArea::progressQml() const { return this->_progress; }
QmlObjectListModel *MeasurementArea::tiles() { return _tiles.get(); }
const QmlObjectListModel *MeasurementArea::tiles() const {
return &this->_tileData.tiles;
return _tiles.get();
}
const QVariantList &MeasurementArea::tileCenterPoints() const {
return this->_tileData.tileCenterPoints;
}
const TileData &MeasurementArea::tileData() const { return this->_tileData; }
int MeasurementArea::maxTiles() const { return SNAKE_MAX_TILES; }
int MeasurementArea::maxTiles() const { return MAX_TILES; }
bool MeasurementArea::ready() const { return this->_state == STATE::IDLE; }
bool MeasurementArea::measurementCompleted() const {
if (ready()) {
for (const auto &p : _progress) {
if (p != 100) {
for (int i = 0; i < _tiles->count(); ++i) {
const auto tile = qobject_cast<const MeasurementTile *>(_tiles->get(i));
if (!qFuzzyCompare(tile->progress(), 100)) {
return false;
}
}
......@@ -303,17 +196,15 @@ bool MeasurementArea::saveToJson(QJsonObject &json) {
json[showTilesKey] = _showTiles.rawValue().toBool();
json[areaTypeKey] = nameString;
// save progess
QJsonArray jsonProgess;
for (const auto &p : _progress) {
jsonProgess.append(p);
}
json[progressKey] = std::move(jsonProgess);
// save tiles
QJsonObject jsonTiles;
_tileData.saveToJson(jsonTiles);
json[tileKey] = std::move(jsonTiles);
QJsonArray jsonTileArray;
for (int i = 0; i < _tiles->count(); ++i) {
auto tile = qobject_cast<MeasurementTile *>(_tiles->get(i));
QJsonObject jsonTile;
tile->saveToJson(jsonTile);
jsonTileArray.append(jsonTile);
}
json[tileArrayKey] = std::move(jsonTileArray);
return true;
} else {
......@@ -359,52 +250,39 @@ bool MeasurementArea::loadFromJson(const QJsonObject &json,
_showTiles.setRawValue(json[showTilesKey].toBool());
}
// load tiles and progress
// load tiles
bool tileError = false;
if (json.contains(tileKey) && json[tileKey].isObject()) {
if (json.contains(tileArrayKey) && json[tileArrayKey].isArray()) {
QString e;
if (!_tileData.loadFromJson(json[tileKey].toObject(), e)) {
qCWarning(MeasurementAreaLog) << "TileData::loadFromJson(): " << e;
tileError = true;
} else {
progressChanged();
}
} else {
tileError = true;
}
_tiles->clearAndDeleteContents();
for (auto &&jsonTile : json[tileArrayKey].toArray()) {
bool progressError = false;
QVector<int> prog;
if (json.contains(progressKey) && json[progressKey].isArray()) {
for (const auto &p : json[progressKey].toArray()) {
if (p.isDouble()) {
prog.append(p.toDouble());
auto tile = new MeasurementTile(this);
if (tile->loadFromJson(jsonTile.toObject(), e)) {
_tiles->append(tile);
} else {
progressError = true;
tile->deleteLater();
qCWarning(MeasurementAreaLog) << e;
tileError = true;
break;
}
}
// check if entries are in range
if (!progressError) {
for (const auto &p : prog) {
if (p < 0 || p > 100) {
progressError = true;
break;
}
}
}
} else {
progressError = true;
}
if (!progressError) {
_progress.swap(prog);
emit progressChanged();
qCWarning(MeasurementAreaLog)
<< "Not able to load tiles. tileArrayKey missing or wrong type.";
if (json.contains(tileArrayKey)) {
qCWarning(MeasurementAreaLog)
<< "tile array type: " << json[tileArrayKey].type();
}
tileError = true;
}
// do update if error occurred.
enableUpdate();
if (progressError || tileError) {
if (tileError) {
doUpdate();
}
......@@ -426,28 +304,44 @@ bool MeasurementArea::isCorrect() {
return false;
}
bool MeasurementArea::setProgress(const QVector<int> &p) {
if (ready()) {
if (!_holdProgress && p.size() == this->tiles()->count() &&
this->_progress != p) {
this->_progress = p;
void MeasurementArea::updateProgress(const ProgressArray &array) {
if (ready() && !_holdProgress && array.size() > 0) {
bool anyChanges = false;
for (const auto &pair : array) {
const auto &id = pair.first;
const auto &progress = pair.second;
auto it = _tileMap.find(id);
if (it != _tileMap.end()) {
if (!qFuzzyCompare(progress, it->second->progress())) {
it->second->setProgress(progress);
anyChanges = true;
}
}
}
if (anyChanges) {
emit progressChanged();
emit progressAccepted();
return true;
}
}
emit progressNotAccepted();
return false;
}
void MeasurementArea::randomProgress() {
if (ready()) {
std::srand(std::time(nullptr));
for (auto &p : _progress) {
for (int i = 0; i < _tiles->count(); ++i) {
auto tile = _tiles->value<MeasurementTile *>(i);
Q_ASSERT(tile != nullptr);
auto p = tile->progress();
p += std::rand() % 125;
if (p > 100) {
p = 100;
}
tile->setProgress(p);
}
emit progressChanged();
......@@ -456,13 +350,25 @@ void MeasurementArea::randomProgress() {
void MeasurementArea::resetProgress() {
if (ready()) {
for (auto &p : _progress) {
p = 0;
bool anyChanges = false;
for (int i = 0; i < _tiles->count(); ++i) {
auto tile = _tiles->value<MeasurementTile *>(i);
Q_ASSERT(tile != nullptr);
if (!qFuzzyCompare(tile->progress(), 0)) {
tile->setProgress(0);
anyChanges = true;
}
}
emit progressChanged();
if (anyChanges) {
emit progressChanged();
}
}
}
//!
//! \brief MeasurementArea::doUpdate
//! \pre MeasurementArea::deferUpdate must be called first, don't call
......@@ -480,7 +386,7 @@ void MeasurementArea::doUpdate() {
const auto totalArea = this->area() * si::meter * si::meter;
const auto estNumTiles = totalArea / tileArea;