From a22db7f484de9d013d88cf55e3c5844af40e5201 Mon Sep 17 00:00:00 2001 From: Valentin Platzgummer Date: Thu, 17 Sep 2020 15:02:54 +0200 Subject: [PATCH] Tiles added to WimaMeasurementArea --- src/Wima/Geometry/WimaMeasurementArea.cc | 134 ++++++++++++++---- src/Wima/Geometry/WimaMeasurementArea.h | 17 ++- src/WimaView/WimaMeasurementAreaEditor.qml | 6 + src/WimaView/WimaMeasurementAreaMapVisual.qml | 39 ++++- 4 files changed, 162 insertions(+), 34 deletions(-) diff --git a/src/Wima/Geometry/WimaMeasurementArea.cc b/src/Wima/Geometry/WimaMeasurementArea.cc index 02f0af2f1..7ab18219b 100644 --- a/src/Wima/Geometry/WimaMeasurementArea.cc +++ b/src/Wima/Geometry/WimaMeasurementArea.cc @@ -1,9 +1,14 @@ #include "WimaMeasurementArea.h" +#include "QtConcurrentRun" #include "SnakeTile.h" #include "snake.h" #include +#ifndef SNAKE_MAX_TILES +#define SNAKE_MAX_TILES 1000 +#endif + const char *WimaMeasurementArea::settingsGroup = "MeasurementArea"; const char *WimaMeasurementArea::tileHeightName = "TileHeight"; const char *WimaMeasurementArea::tileWidthName = "TileWidth"; @@ -13,6 +18,8 @@ const char *WimaMeasurementArea::minTransectLengthName = "MinTransectLength"; const char *WimaMeasurementArea::showTilesName = "ShowTiles"; const char *WimaMeasurementArea::WimaMeasurementAreaName = "Measurement Area"; +void tileDeleter(QmlObjectListModel *tiles) { tiles->clearAndDeleteContents(); } + WimaMeasurementArea::WimaMeasurementArea(QObject *parent) : WimaArea(parent), _metaDataMap(FactMetaData::createMapFromJsonFile( @@ -32,7 +39,8 @@ WimaMeasurementArea::WimaMeasurementArea(QObject *parent) this /* QObject parent */)), _showTiles(SettingsFact(settingsGroup, _metaDataMap[showTilesName], this /* QObject parent */)), - _calculating(false) { + _pTiles(new QmlObjectListModel(), &tileDeleter), _calculating(false), + _polygonValid(false) { init(); } @@ -56,7 +64,8 @@ WimaMeasurementArea::WimaMeasurementArea(const WimaMeasurementArea &other, this /* QObject parent */)), _showTiles(SettingsFact(settingsGroup, _metaDataMap[showTilesName], this /* QObject parent */)), - _calculating(false) { + _pTiles(new QmlObjectListModel(), &tileDeleter), _calculating(false), + _polygonValid(false) { init(); } @@ -73,7 +82,7 @@ operator=(const WimaMeasurementArea &other) { } WimaMeasurementArea::~WimaMeasurementArea() { - this->_tiles.clearAndDeleteContents(); + this->_pTiles->clearAndDeleteContents(); } QString WimaMeasurementArea::mapVisualQML() const { @@ -96,6 +105,12 @@ Fact *WimaMeasurementArea::minTransectLength() { return &_minTransectLength; } Fact *WimaMeasurementArea::showTiles() { return &_showTiles; } +QmlObjectListModel *WimaMeasurementArea::tiles() { return this->_pTiles.get(); } + +int WimaMeasurementArea::maxTiles() { return SNAKE_MAX_TILES; } + +bool WimaMeasurementArea::ready() { return !_calculating; } + void WimaMeasurementArea::saveToJson(QJsonObject &json) { this->WimaArea::saveToJson(json); json[tileHeightName] = _tileHeight.rawValue().toDouble(); @@ -160,41 +175,72 @@ bool WimaMeasurementArea::loadFromJson(const QJsonObject &json, return false; } } - +//! +//! \brief WimaMeasurementArea::doUpdate +//! \pre WimaMeasurementArea::deferUpdate must be called first, don't call this +//! function directly! void WimaMeasurementArea::doUpdate() { using namespace snake; using namespace boost::units; #ifdef SNAKE_SHOW_TIME auto start = std::chrono::high_resolution_clock::now(); #endif - auto polygon = this->coordinateList(); - for (auto &v : polygon) { - v.setAltitude(0); - } - if (polygon.size() > 3) { - QGeoCoordinate origin = polygon.first(); - BoostPolygon polygonENU; - areaToEnu(origin, polygon, polygonENU); - Length height = this->_tileHeight.rawValue().toDouble() * si::meter; - Length width = this->_tileWidth.rawValue().toDouble() * si::meter; - Area minArea = + const auto height = this->_tileHeight.rawValue().toDouble() * si::meter; + const auto width = this->_tileWidth.rawValue().toDouble() * si::meter; + const auto tileArea = width * height; + const auto totalArea = this->area() * si::meter * si::meter; + const auto estNumTiles = totalArea / tileArea; + if (!this->_calculating && + long(std::ceil(estNumTiles.value())) <= SNAKE_MAX_TILES && + this->count() >= 3 && this->isSimplePolygon()) { + this->_calculating = true; + if (!this->_polygonValid) { + this->_polygon = this->coordinateList(); + for (auto &v : this->_polygon) { + v.setAltitude(0); + } + this->_polygonValid = true; + } + const auto &polygon = this->_polygon; + const auto minArea = this->_minTileArea.rawValue().toDouble() * si::meter * si::meter; - std::vector tilesENU; - BoundingBox bbox; - std::string errorString; - if (snake::tiles(polygonENU, height, width, minArea, tilesENU, bbox, - errorString)) { - this->_tiles.clearAndDeleteContents(); - for (const auto &t : tilesENU) { - auto geoTile = new SnakeTile(&this->_tiles); - for (const auto &v : t.outer()) { - QGeoCoordinate geoVertex; - fromENU(origin, v, geoVertex); - geoTile->push_back(geoVertex); + auto *th = this->thread(); + auto future = QtConcurrent::run([polygon, th, height, width, minArea] { +#ifdef SNAKE_SHOW_TIME + auto start = std::chrono::high_resolution_clock::now(); +#endif + TilesPtr pTiles(new QmlObjectListModel(), &tileDeleter); + QGeoCoordinate origin = polygon.first(); + BoostPolygon polygonENU; + areaToEnu(origin, polygon, polygonENU); + std::vector tilesENU; + BoundingBox bbox; + std::string errorString; + if (snake::tiles(polygonENU, height, width, minArea, tilesENU, bbox, + errorString)) { + for (const auto &t : tilesENU) { + auto geoTile = new SnakeTile(pTiles.get()); + for (const auto &v : t.outer()) { + QGeoCoordinate geoVertex; + fromENU(origin, v, geoVertex); + geoTile->push_back(geoVertex); + } + pTiles->append(geoTile); } - this->_tiles.append(geoTile); } - } + pTiles->moveToThread(th); +#ifdef SNAKE_SHOW_TIME + qDebug() + << "WimaMeasurementArea::doUpdate concurrent update execution time: " + << std::chrono::duration_cast( + std::chrono::high_resolution_clock::now() - start) + .count() + << " ms"; +#endif + return pTiles; + }); // QtConcurrent::run() + + this->_watcher.setFuture(future); } #ifdef SNAKE_SHOW_TIME qDebug() << "WimaMeasurementArea::doUpdate execution time: " @@ -209,9 +255,30 @@ void WimaMeasurementArea::deferUpdate() { if (this->_timer.isActive()) { this->_timer.stop(); } + if (this->_pTiles->count() > 0) { + this->_pTiles->clearAndDeleteContents(); + emit this->tilesChanged(); + } this->_timer.start(100); } +void WimaMeasurementArea::storeTiles() { +#ifdef SNAKE_SHOW_TIME + auto start = std::chrono::high_resolution_clock::now(); +#endif + this->_pTiles = this->_watcher.result(); + this->_calculating = false; + emit this + ->tilesChanged(); // This is expensive. Drawing tiles is expensive too. +#ifdef SNAKE_SHOW_TIME + qDebug() << "WimaMeasurementArea::storeTiles() execution time: " + << std::chrono::duration_cast( + std::chrono::high_resolution_clock::now() - start) + .count() + << " ms"; +#endif +} + void WimaMeasurementArea::init() { this->setObjectName(WimaMeasurementAreaName); connect(&this->_tileHeight, &Fact::rawValueChanged, this, @@ -222,15 +289,20 @@ void WimaMeasurementArea::init() { &WimaMeasurementArea::deferUpdate); connect(this, &WimaArea::pathChanged, this, &WimaMeasurementArea::deferUpdate); + connect(this, &WimaArea::pathChanged, + [this] { this->_polygonValid = false; }); this->_timer.setSingleShot(true); connect(&this->_timer, &QTimer::timeout, this, &WimaMeasurementArea::doUpdate); + connect(&this->_watcher, + &QFutureWatcher>::finished, this, + &WimaMeasurementArea::storeTiles); } /*! * \class WimaMeasurementArea - * \brief Class defining the area inside which the actual drone measurements are - * performed. + * \brief Class defining the area inside which the actual drone measurements + * are performed. * * \sa WimaArea, WimaController, WimaPlaner */ diff --git a/src/Wima/Geometry/WimaMeasurementArea.h b/src/Wima/Geometry/WimaMeasurementArea.h index 06e9c5594..2db97363b 100644 --- a/src/Wima/Geometry/WimaMeasurementArea.h +++ b/src/Wima/Geometry/WimaMeasurementArea.h @@ -1,5 +1,6 @@ #pragma once +#include #include #include @@ -22,6 +23,8 @@ public: Q_PROPERTY(Fact *transectDistance READ transectDistance CONSTANT) Q_PROPERTY(Fact *minTransectLength READ minTransectLength CONSTANT) Q_PROPERTY(Fact *showTiles READ showTiles CONSTANT) + Q_PROPERTY(QmlObjectListModel *tiles READ tiles NOTIFY tilesChanged) + Q_PROPERTY(int maxTiles READ maxTiles NOTIFY maxTilesChanged) // Overrides from WimaPolygon QString mapVisualQML(void) const; @@ -33,6 +36,9 @@ public: Fact *transectDistance(); Fact *minTransectLength(); Fact *showTiles(); + QmlObjectListModel *tiles(); + int maxTiles(); + bool ready(); // Member Methodes void saveToJson(QJsonObject &json); @@ -53,12 +59,15 @@ public: static const char *WimaMeasurementAreaName; signals: + void tilesChanged(); + void maxTilesChanged(); public slots: private slots: void doUpdate(); void deferUpdate(); + void storeTiles(); private: // Member Methodes @@ -76,6 +85,10 @@ private: // Tile stuff. QTimer _timer; - std::atomic_bool _calculating; - QmlObjectListModel _tiles; + using TilesPtr = std::shared_ptr; + TilesPtr _pTiles; + QList _polygon; + QFutureWatcher _watcher; + bool _calculating; + bool _polygonValid; }; diff --git a/src/WimaView/WimaMeasurementAreaEditor.qml b/src/WimaView/WimaMeasurementAreaEditor.qml index 0eb4acd5e..939cf2cc9 100644 --- a/src/WimaView/WimaMeasurementAreaEditor.qml +++ b/src/WimaView/WimaMeasurementAreaEditor.qml @@ -178,6 +178,12 @@ Rectangle { QGCLabel { text: qsTr("Nodes") } QGCLabel { text: areaItem.count } + QGCLabel { text: qsTr("Tiles") } + QGCLabel { text: areaItem.tiles.count } + + QGCLabel { text: qsTr("Max. Tiles") } + QGCLabel { text: areaItem.maxTiles } + } } // Column } // Rectangle diff --git a/src/WimaView/WimaMeasurementAreaMapVisual.qml b/src/WimaView/WimaMeasurementAreaMapVisual.qml index da2cb6529..a29880c6e 100644 --- a/src/WimaView/WimaMeasurementAreaMapVisual.qml +++ b/src/WimaView/WimaMeasurementAreaMapVisual.qml @@ -85,7 +85,7 @@ Item { Component.onDestruction: { } - + // Polygon WimaMapPolygonVisuals { qgcView: _root.qgcView mapControl: map @@ -96,6 +96,7 @@ Item { interiorOpacity: 0.25 } + // Border Polygon WimaMapPolygonVisuals { qgcView: _root.qgcView mapControl: map @@ -106,4 +107,40 @@ Item { interiorOpacity: 1 } + // Add Snake tiles to the map + Component { + id: tileComponent + MapPolygon { + color: "transparent" + opacity: 1 + border.color: "black" + border.width: 1 + path: [] + } + } + + Repeater { + property bool enable: areaItem.showTiles.value + model: enable ? areaItem.tiles : 0 + + Item{ + property var _tileComponent + function addVisuals() { + _tileComponent = tileComponent.createObject(map) + map.addMapItem(_tileComponent) + _tileComponent.path = object.path + } + + function removeVisuals() { + _tileComponent.destroy() + } + + Component.onCompleted: { + addVisuals() + } + Component.onDestruction: { + removeVisuals() + } + } + } } -- 2.22.0