Commit a0c951cb authored by Valentin Platzgummer's avatar Valentin Platzgummer

234

parent 0510d157
This source diff could not be displayed because it is too large. You can view the blob instead.
#include "WimaMeasurementArea.h" #include "WimaMeasurementArea.h"
#include "SnakeTile.h"
#include "snake.h"
#include <boost/units/systems/si.hpp>
const char *WimaMeasurementArea::settingsGroup = "MeasurementArea"; const char *WimaMeasurementArea::settingsGroup = "MeasurementArea";
const char *WimaMeasurementArea::bottomLayerAltitudeName = const char *WimaMeasurementArea::tileHeightName = "TileHeight";
"BottomLayerAltitude"; const char *WimaMeasurementArea::tileWidthName = "TileWidth";
const char *WimaMeasurementArea::numberOfLayersName = "NumberOfLayers"; const char *WimaMeasurementArea::minTileAreaName = "MinTileArea";
const char *WimaMeasurementArea::layerDistanceName = "LayerDistance"; const char *WimaMeasurementArea::transectDistanceName = "TransectDistance";
const char *WimaMeasurementArea::minTransectLengthName = "MinTransectLength";
const char *WimaMeasurementArea::showTilesName = "ShowTiles";
const char *WimaMeasurementArea::WimaMeasurementAreaName = "Measurement Area"; const char *WimaMeasurementArea::WimaMeasurementAreaName = "Measurement Area";
WimaMeasurementArea::WimaMeasurementArea(QObject *parent) WimaMeasurementArea::WimaMeasurementArea(QObject *parent)
...@@ -12,15 +18,21 @@ WimaMeasurementArea::WimaMeasurementArea(QObject *parent) ...@@ -12,15 +18,21 @@ WimaMeasurementArea::WimaMeasurementArea(QObject *parent)
_metaDataMap(FactMetaData::createMapFromJsonFile( _metaDataMap(FactMetaData::createMapFromJsonFile(
QStringLiteral(":/json/WimaMeasurementArea.SettingsGroup.json"), QStringLiteral(":/json/WimaMeasurementArea.SettingsGroup.json"),
this /* QObject parent */)), this /* QObject parent */)),
_bottomLayerAltitude(SettingsFact(settingsGroup, _tileHeight(SettingsFact(settingsGroup, _metaDataMap[tileHeightName],
_metaDataMap[bottomLayerAltitudeName], this /* QObject parent */)),
this /* QObject parent */)), _tileWidth(SettingsFact(settingsGroup, _metaDataMap[tileWidthName],
_numberOfLayers(SettingsFact(settingsGroup, this /* QObject parent */)),
_metaDataMap[numberOfLayersName], _minTileArea(SettingsFact(settingsGroup, _metaDataMap[minTileAreaName],
this /* QObject parent */)), this /* QObject parent */)),
_layerDistance(SettingsFact(settingsGroup, _transectDistance(SettingsFact(settingsGroup,
_metaDataMap[layerDistanceName], _metaDataMap[transectDistanceName],
this /* QObject parent */)) { this /* QObject parent */)),
_minTransectLength(SettingsFact(settingsGroup,
_metaDataMap[minTransectLengthName],
this /* QObject parent */)),
_showTiles(SettingsFact(settingsGroup, _metaDataMap[showTilesName],
this /* QObject parent */)),
_calculating(false) {
init(); init();
} }
...@@ -30,15 +42,21 @@ WimaMeasurementArea::WimaMeasurementArea(const WimaMeasurementArea &other, ...@@ -30,15 +42,21 @@ WimaMeasurementArea::WimaMeasurementArea(const WimaMeasurementArea &other,
_metaDataMap(FactMetaData::createMapFromJsonFile( _metaDataMap(FactMetaData::createMapFromJsonFile(
QStringLiteral(":/json/WimaMeasurementArea.SettingsGroup.json"), QStringLiteral(":/json/WimaMeasurementArea.SettingsGroup.json"),
this /* QObject parent */)), this /* QObject parent */)),
_bottomLayerAltitude(SettingsFact(settingsGroup, _tileHeight(SettingsFact(settingsGroup, _metaDataMap[tileHeightName],
_metaDataMap[bottomLayerAltitudeName], this /* QObject parent */)),
this /* QObject parent */)), _tileWidth(SettingsFact(settingsGroup, _metaDataMap[tileWidthName],
_numberOfLayers(SettingsFact(settingsGroup, this /* QObject parent */)),
_metaDataMap[numberOfLayersName], _minTileArea(SettingsFact(settingsGroup, _metaDataMap[minTileAreaName],
this /* QObject parent */)), this /* QObject parent */)),
_layerDistance(SettingsFact(settingsGroup, _transectDistance(SettingsFact(settingsGroup,
_metaDataMap[layerDistanceName], _metaDataMap[transectDistanceName],
this /* QObject parent */)) { this /* QObject parent */)),
_minTransectLength(SettingsFact(settingsGroup,
_metaDataMap[minTransectLengthName],
this /* QObject parent */)),
_showTiles(SettingsFact(settingsGroup, _metaDataMap[showTilesName],
this /* QObject parent */)),
_calculating(false) {
init(); init();
} }
...@@ -54,6 +72,10 @@ operator=(const WimaMeasurementArea &other) { ...@@ -54,6 +72,10 @@ operator=(const WimaMeasurementArea &other) {
return *this; return *this;
} }
WimaMeasurementArea::~WimaMeasurementArea() {
this->_tiles.clearAndDeleteContents();
}
QString WimaMeasurementArea::mapVisualQML() const { QString WimaMeasurementArea::mapVisualQML() const {
return "WimaMeasurementAreaMapVisual.qml"; return "WimaMeasurementAreaMapVisual.qml";
} }
...@@ -64,11 +86,24 @@ QString WimaMeasurementArea::editorQML() const { ...@@ -64,11 +86,24 @@ QString WimaMeasurementArea::editorQML() const {
Fact *WimaMeasurementArea::tileHeight() { return &_tileHeight; } Fact *WimaMeasurementArea::tileHeight() { return &_tileHeight; }
Fact *WimaMeasurementArea::tileWidth() { return &_tileWidth; }
Fact *WimaMeasurementArea::minTileArea() { return &_minTileArea; }
Fact *WimaMeasurementArea::transectDistance() { return &_transectDistance; }
Fact *WimaMeasurementArea::minTransectLength() { return &_minTransectLength; }
Fact *WimaMeasurementArea::showTiles() { return &_showTiles; }
void WimaMeasurementArea::saveToJson(QJsonObject &json) { void WimaMeasurementArea::saveToJson(QJsonObject &json) {
this->WimaArea::saveToJson(json); this->WimaArea::saveToJson(json);
json[bottomLayerAltitudeName] = _bottomLayerAltitude.rawValue().toDouble(); json[tileHeightName] = _tileHeight.rawValue().toDouble();
json[numberOfLayersName] = _numberOfLayers.rawValue().toInt(); json[tileWidthName] = _tileWidth.rawValue().toDouble();
json[layerDistanceName] = _layerDistance.rawValue().toDouble(); json[minTileAreaName] = _minTileArea.rawValue().toDouble();
json[transectDistanceName] = _transectDistance.rawValue().toDouble();
json[minTransectLengthName] = _minTransectLength.rawValue().toDouble();
json[showTilesName] = _showTiles.rawValue().toBool();
json[areaTypeName] = WimaMeasurementAreaName; json[areaTypeName] = WimaMeasurementAreaName;
} }
...@@ -77,79 +112,119 @@ bool WimaMeasurementArea::loadFromJson(const QJsonObject &json, ...@@ -77,79 +112,119 @@ bool WimaMeasurementArea::loadFromJson(const QJsonObject &json,
if (this->WimaArea::loadFromJson(json, errorString)) { if (this->WimaArea::loadFromJson(json, errorString)) {
bool retVal = true; bool retVal = true;
if (json.contains(bottomLayerAltitudeName) && if (json.contains(tileHeightName) && json[tileHeightName].isDouble()) {
json[bottomLayerAltitudeName].isDouble()) { _tileHeight.setRawValue(json[tileHeightName].toDouble());
_bottomLayerAltitude.setRawValue(
json[bottomLayerAltitudeName].toDouble());
} else { } else {
errorString.append(tr("Could not load Bottom Layer Altitude!\n")); errorString.append(tr("Could not load tile height!\n"));
retVal = false; retVal = false;
} }
if (json.contains(numberOfLayersName) && if (json.contains(tileWidthName) && json[tileWidthName].isDouble()) {
json[numberOfLayersName].isDouble()) { _tileHeight.setRawValue(json[tileWidthName].toDouble());
_numberOfLayers.setRawValue(json[numberOfLayersName].toInt());
} else { } else {
errorString.append(tr("Could not load Number of Layers!\n")); errorString.append(tr("Could not load tile width!\n"));
retVal = false; retVal = false;
} }
if (json.contains(layerDistanceName) && if (json.contains(minTileAreaName) && json[minTileAreaName].isDouble()) {
json[layerDistanceName].isDouble()) { _tileHeight.setRawValue(json[minTileAreaName].toDouble());
_layerDistance.setRawValue(json[layerDistanceName].toDouble());
} else { } else {
errorString.append(tr("Could not load Layer Distance!\n")); errorString.append(tr("Could not load minimal tile area!\n"));
retVal = false; retVal = false;
} }
if (json.contains(transectDistanceName) &&
json[transectDistanceName].isDouble()) {
_tileHeight.setRawValue(json[transectDistanceName].toDouble());
} else {
errorString.append(tr("Could not load transect distance!\n"));
retVal = false;
}
if (json.contains(minTransectLengthName) &&
json[minTransectLengthName].isDouble()) {
_tileHeight.setRawValue(json[minTransectLengthName].toDouble());
} else {
errorString.append(tr("Could not load minimal transect length!\n"));
retVal = false;
}
if (json.contains(showTilesName) && json[showTilesName].isBool()) {
_tileHeight.setRawValue(json[showTilesName].toDouble());
} else {
errorString.append(tr("Could not load show tiles !\n"));
retVal = false;
}
return retVal; return retVal;
} else { } else {
return false; return false;
} }
} }
void WimaMeasurementArea::setBottomLayerAltitude(double altitude) { void WimaMeasurementArea::doUpdate() {
if (!qFuzzyCompare(_bottomLayerAltitude.rawValue().toDouble(), altitude)) { using namespace snake;
_bottomLayerAltitude.setRawValue(altitude); using namespace boost::units;
#ifdef SNAKE_SHOW_TIME
emit bottomLayerAltitudeChanged(); 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();
void WimaMeasurementArea::setNumberOfLayers(double numLayers) { BoostPolygon polygonENU;
if (!qFuzzyCompare(_numberOfLayers.rawValue().toDouble(), numLayers)) { areaToEnu(origin, polygon, polygonENU);
_numberOfLayers.setRawValue(numLayers); Length height = this->_tileHeight.rawValue().toDouble() * si::meter;
Length width = this->_tileWidth.rawValue().toDouble() * si::meter;
emit numberOfLayersChanged(); Area minArea =
this->_minTileArea.rawValue().toDouble() * si::meter * si::meter;
std::vector<BoostPolygon> 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);
}
this->_tiles.append(geoTile);
}
}
} }
#ifdef SNAKE_SHOW_TIME
qDebug() << "WimaMeasurementArea::doUpdate execution time: "
<< std::chrono::duration_cast<std::chrono::milliseconds>(
std::chrono::high_resolution_clock::now() - start)
.count()
<< " ms";
#endif
} }
void WimaMeasurementArea::setLayerDistance(double layerDistance) { void WimaMeasurementArea::deferUpdate() {
if (!qFuzzyCompare(_layerDistance.rawValue().toDouble(), layerDistance)) { if (this->_timer.isActive()) {
_layerDistance.setRawValue(layerDistance); this->_timer.stop();
emit layerDistanceChanged();
} }
} this->_timer.start(100);
void print(const WimaMeasurementArea &area) {
QString message;
print(area, message);
qWarning() << message;
}
void print(const WimaMeasurementArea &area, QString outputStr) {
print(static_cast<const WimaArea &>(area), outputStr);
outputStr.append(QString("Bottom Layer Altitude: %1\n")
.arg(area._bottomLayerAltitude.rawValue().toDouble()));
outputStr.append(QString("Number of Layers: %1\n")
.arg(area._numberOfLayers.rawValue().toInt()));
outputStr.append(QString("Layer Distance: %1\n")
.arg(area._layerDistance.rawValue().toDouble()));
} }
void WimaMeasurementArea::init() { void WimaMeasurementArea::init() {
this->setObjectName(WimaMeasurementAreaName); this->setObjectName(WimaMeasurementAreaName);
connect(&this->_tileHeight, &Fact::rawValueChanged, this,
&WimaMeasurementArea::deferUpdate);
connect(&this->_tileWidth, &Fact::rawValueChanged, this,
&WimaMeasurementArea::deferUpdate);
connect(&this->_minTileArea, &Fact::rawValueChanged, this,
&WimaMeasurementArea::deferUpdate);
connect(this, &WimaArea::pathChanged, this,
&WimaMeasurementArea::deferUpdate);
this->_timer.setSingleShot(true);
connect(&this->_timer, &QTimer::timeout, this,
&WimaMeasurementArea::doUpdate);
} }
/*! /*!
......
#pragma once #pragma once
#include "QScopedPointer"
#include <QObject> #include <QObject>
#include <QTimer>
#include "WimaArea.h" #include "WimaArea.h"
...@@ -14,14 +14,25 @@ public: ...@@ -14,14 +14,25 @@ public:
WimaMeasurementArea(const WimaMeasurementArea &other, WimaMeasurementArea(const WimaMeasurementArea &other,
QObject *parent = nullptr); QObject *parent = nullptr);
WimaMeasurementArea &operator=(const WimaMeasurementArea &other); WimaMeasurementArea &operator=(const WimaMeasurementArea &other);
~WimaMeasurementArea();
Q_PROPERTY(Fact *borderPolygonOffset READ borderPolygonOffsetFact CONSTANT) Q_PROPERTY(Fact *tileHeight READ tileHeight CONSTANT)
Q_PROPERTY(Fact *tileWidth READ tileWidth CONSTANT)
Q_PROPERTY(Fact *minTileArea READ minTileArea CONSTANT)
Q_PROPERTY(Fact *transectDistance READ transectDistance CONSTANT)
Q_PROPERTY(Fact *minTransectLength READ minTransectLength CONSTANT)
Q_PROPERTY(Fact *showTiles READ showTiles CONSTANT)
// Overrides from WimaPolygon // Overrides from WimaPolygon
QString mapVisualQML(void) const { QString mapVisualQML(void) const;
return "WimaMeasurementAreaMapVisual.qml"; QString editorQML(void) const;
}
QString editorQML(void) const { return "WimaMeasurementAreaEditor.qml"; } Fact *tileHeight();
Fact *tileWidth();
Fact *minTileArea();
Fact *transectDistance();
Fact *minTransectLength();
Fact *showTiles();
// Member Methodes // Member Methodes
void saveToJson(QJsonObject &json); void saveToJson(QJsonObject &json);
...@@ -36,14 +47,19 @@ public: ...@@ -36,14 +47,19 @@ public:
static const char *tileHeightName; static const char *tileHeightName;
static const char *tileWidthName; static const char *tileWidthName;
static const char *minTileAreaName; static const char *minTileAreaName;
static const char *transcetDistanceName; static const char *transectDistanceName;
static const char *minTransectLength; static const char *minTransectLengthName;
static const char *showTilesName;
static const char *WimaMeasurementAreaName; static const char *WimaMeasurementAreaName;
signals: signals:
public slots: public slots:
private slots:
void doUpdate();
void deferUpdate();
private: private:
// Member Methodes // Member Methodes
void init(); void init();
...@@ -51,9 +67,15 @@ private: ...@@ -51,9 +67,15 @@ private:
// Members // Members
QMap<QString, FactMetaData *> _metaDataMap; QMap<QString, FactMetaData *> _metaDataMap;
SettingsFact _tileWidth;
SettingsFact _tileHeight; SettingsFact _tileHeight;
SettingsFact _tileWidth;
SettingsFact _minTileArea; SettingsFact _minTileArea;
SettingsFact _transectDistance; SettingsFact _transectDistance;
SettingsFact _minTransectLength; SettingsFact _minTransectLength;
SettingsFact _showTiles;
// Tile stuff.
QTimer _timer;
std::atomic_bool _calculating;
QmlObjectListModel _tiles;
}; };
[ [
{ {
"name": "BottomLayerAltitude", "name": "TileHeight",
"shortDescription": "The distance between the terrain and the first layer of the measurement path.", "shortDescription": "The height of a tile",
"type": "double", "type": "double",
"units": "m", "units": "m",
"min": 1, "min": 0.3,
"decimalPlaces": 2, "decimalPlaces": 2,
"defaultValue": 5 "defaultValue": 5
}, },
{ {
"name": "NumberOfLayers", "name": "TileWidth",
"shortDescription": "The number of layers.", "shortDescription": "The height of a tile",
"type": "uint32", "type": "double",
"units": "m", "units": "m",
"min": 1, "min": 0.3,
"decimalPlaces": 0, "decimalPlaces": 2,
"defaultValue": 1 "defaultValue": 5
}, },
{ {
"name": "LayerDistance", "name": "MinTileArea",
"shortDescription": "Specify the time between each photo", "shortDescription": "The minimal allowed area of a tile",
"type": "double",
"units": "m^2",
"min": 0,
"decimalPlaces": 2,
"defaultValue": 5
},
{
"name": "TransectDistance",
"shortDescription": "The transect distance",
"type": "double",
"units": "m",
"min": 0.3,
"decimalPlaces": 2,
"defaultValue": 2
},
{
"name": "MinTransectLength",
"shortDescription": "The minimal transect length",
"type": "double", "type": "double",
"units": "m", "units": "m",
"min": 0, "min": 0,
"decimalPlaces": 2, "decimalPlaces": 2,
"defaultValue": 1 "defaultValue": 1
}, },
{
"name": "ShowTiles",
"shortDescription": "Show tiles",
"type": "bool",
"defaultValue": true
},
{ {
"name": "BorderPolygonOffset", "name": "BorderPolygonOffset",
"shortDescription": "The distance between the measurement area and it's enclosing polygon", "shortDescription": "The distance between the measurement area and it's enclosing polygon",
......
...@@ -97,7 +97,12 @@ public: ...@@ -97,7 +97,12 @@ public:
bool doTopicServiceSetup(); bool doTopicServiceSetup();
// Internal data. // Internal data.
snake::Scenario scenario; snake::BoostPolygon mAreaENU;
snake::BoostPolygon sAreaENU;
snake::BoostPolygon corridorENU;
snake::BoostPolygon jAreaENU;
std::vector<snake::BoostPolygon> tilesENU;
QGeoCoordinate ENUOrigin;
SnakeThread *parent; SnakeThread *parent;
// Input // Input
...@@ -225,36 +230,35 @@ void SnakeThread::setLineDistance(Length lineDistance) { ...@@ -225,36 +230,35 @@ void SnakeThread::setLineDistance(Length lineDistance) {
Area SnakeThread::minTileArea() const { Area SnakeThread::minTileArea() const {
SharedLock lk(this->pImpl->input.mutex); SharedLock lk(this->pImpl->input.mutex);
return this->pImpl->scenario.minTileArea(); return this->pImpl->input.minTileArea;
} }
void SnakeThread::setMinTileArea(Area minTileArea) { void SnakeThread::setMinTileArea(Area minTileArea) {
this->pImpl->input.scenarioChanged = true;
this->pImpl->input.scenarioChanged = true; this->pImpl->input.scenarioChanged = true;
UniqueLock lk(this->pImpl->input.mutex); UniqueLock lk(this->pImpl->input.mutex);
this->pImpl->scenario.setMinTileArea(minTileArea); this->pImpl->input.minTileArea = minTileArea;
} }
Length SnakeThread::tileHeight() const { Length SnakeThread::tileHeight() const {
SharedLock lk(this->pImpl->input.mutex); SharedLock lk(this->pImpl->input.mutex);
return this->pImpl->scenario.tileHeight(); return this->pImpl->input.tileHeight;
} }
void SnakeThread::setTileHeight(Length tileHeight) { void SnakeThread::setTileHeight(Length tileHeight) {
this->pImpl->input.scenarioChanged = true; this->pImpl->input.scenarioChanged = true;
UniqueLock lk(this->pImpl->input.mutex); UniqueLock lk(this->pImpl->input.mutex);
this->pImpl->scenario.setTileHeight(tileHeight); this->pImpl->input.tileHeight = tileHeight;
} }
Length SnakeThread::tileWidth() const { Length SnakeThread::tileWidth() const {
SharedLock lk(this->pImpl->input.mutex); SharedLock lk(this->pImpl->input.mutex);
return this->pImpl->scenario.tileWidth(); return this->pImpl->input.tileWidth;
} }
void SnakeThread::setTileWidth(Length tileWidth) { void SnakeThread::setTileWidth(Length tileWidth) {
this->pImpl->input.scenarioChanged = true; this->pImpl->input.scenarioChanged = true;
UniqueLock lk(this->pImpl->input.mutex); UniqueLock lk(this->pImpl->input.mutex);
this->pImpl->scenario.setTileWidth(tileWidth); this->pImpl->input.tileWidth = tileWidth;
} }
void SnakeThread::run() { void SnakeThread::run() {
...@@ -279,10 +283,13 @@ void SnakeThread::run() { ...@@ -279,10 +283,13 @@ void SnakeThread::run() {
this->pImpl->output.calcInProgress.store(false); this->pImpl->output.calcInProgress.store(false);
emit calcInProgressChanged(this->pImpl->output.calcInProgress.load()); emit calcInProgressChanged(this->pImpl->output.calcInProgress.load());
}; };
CommandRAII<decltype(onExit)> onExitRAII(onExit); CommandRAII<decltype(onExit)> commandRAII(onExit);
bool tilesValid = true; bool tilesValid = true;
QGeoCoordinate origin; bool progressValid = false;
snake::Length lineDistance;
snake::Length minTransectLength;
std::vector<int> progress;
{ {
// Check preconditions. // Check preconditions.
SharedLock lk(this->pImpl->input.mutex); SharedLock lk(this->pImpl->input.mutex);
...@@ -295,118 +302,100 @@ void SnakeThread::run() { ...@@ -295,118 +302,100 @@ void SnakeThread::run() {
this->pImpl->output.errorMessage = "Service area invalid: size < 3."; this->pImpl->output.errorMessage = "Service area invalid: size < 3.";
tilesValid = false; tilesValid = false;
} else { } else {
// Set Origin
origin = this->pImpl->input.mArea.front();
// Update Scenario if necessary // Update Scenario if necessary
if (this->pImpl->input.scenarioChanged) { if (this->pImpl->input.scenarioChanged) {
// Set Origin
this->pImpl->ENUOrigin = this->pImpl->input.mArea.front();
// Update measurement area. // Update measurement area.
auto &mAreaEnu = this->pImpl->scenario.measurementArea(); this->pImpl->mAreaENU.clear();
auto &mArea = this->pImpl->input.mArea; snake::areaToEnu(this->pImpl->ENUOrigin, this->pImpl->input.mArea,
mAreaEnu.clear(); this->pImpl->mAreaENU);
for (auto geoVertex : mArea) {
snake::BoostPoint p;
snake::toENU(origin, geoVertex, p);
mAreaEnu.outer().push_back(p);
}
// Update service area. // Update service area.
auto &sAreaEnu = this->pImpl->scenario.serviceArea(); this->pImpl->sAreaENU.clear();
auto &sArea = this->pImpl->input.sArea; snake::areaToEnu(this->pImpl->ENUOrigin, this->pImpl->input.sArea,
sAreaEnu.clear(); this->pImpl->sAreaENU);
for (auto geoVertex : sArea) {
snake::BoostPoint p;
snake::toENU(origin, geoVertex, p);
sAreaEnu.outer().push_back(p);
}
// Update corridor. // Update corridor.
auto &corridorEnu = this->pImpl->scenario.corridor(); this->pImpl->corridorENU.clear();
auto &corridor = this->pImpl->input.corridor; snake::areaToEnu(this->pImpl->ENUOrigin, this->pImpl->input.corridor,
corridorEnu.clear(); this->pImpl->corridorENU);
for (auto geoVertex : corridor) { // Fetch parametes.
snake::BoostPoint p; auto tileHeight = this->pImpl->input.tileHeight;
snake::toENU(origin, geoVertex, p); auto tileWidth = this->pImpl->input.tileWidth;
corridorEnu.outer().push_back(p); auto minTileArea = this->pImpl->input.minTileArea;
} // Update tiles.
// Update parametes. this->pImpl->tilesENU.clear();
this->pImpl->scenario.setTileHeight(this->pImpl->input.tileHeight); this->pImpl->jAreaENU.clear();
this->pImpl->scenario.setTileWidth(this->pImpl->input.tileWidth); std::string errorString;
this->pImpl->scenario.setMinTileArea(this->pImpl->input.minTileArea); snake::BoundingBox bbox;
if (!snake::joinedArea(this->pImpl->mAreaENU, this->pImpl->sAreaENU,
if (!this->pImpl->scenario.update()) { this->pImpl->corridorENU, this->pImpl->jAreaENU,
this->pImpl->output.errorMessage = errorString) ||
this->pImpl->scenario.errorString.c_str(); !snake::tiles(this->pImpl->mAreaENU, tileHeight, tileWidth,
minTileArea, this->pImpl->tilesENU, bbox,
errorString)) {
UniqueLock uLock(this->pImpl->output.mutex);
this->pImpl->output.errorMessage = errorString.c_str();
tilesValid = false; tilesValid = false;
} }
} }
if (tilesValid) {
// Sample data
lineDistance = this->pImpl->input.lineDistance;
minTransectLength = this->pImpl->input.minTransectLength;
// Verify progress.
size_t nTiles = this->pImpl->tilesENU.size();
if (size_t(this->pImpl->input.progress.progress().size()) != nTiles) {
for (size_t i = 0; i < nTiles; ++i) {
progress.push_back(0);
}
} else {
for (size_t i = 0; i < nTiles; ++i) {
progress.push_back(this->pImpl->input.progress.progress()[i]);
}
}
progressValid = tilesValid;
}
} }
} }
bool waypointsValid = tilesValid; bool waypointsValid = true;
bool progressValid = tilesValid; snake::Transects transects;
snake::flight_plan::Transects transects; snake::Transects transectsRouted;
snake::flight_plan::Transects transectsRouted; snake::Route route;
snake::flight_plan::Route route;
std::vector<int> progress;
bool needWaypointUpdate = this->pImpl->input.scenarioChanged || bool needWaypointUpdate = this->pImpl->input.scenarioChanged ||
this->pImpl->input.routeParametersChanged || this->pImpl->input.routeParametersChanged ||
this->pImpl->input.progressChanged; this->pImpl->input.progressChanged;
if (tilesValid) { if (tilesValid) {
if (needWaypointUpdate) { if (needWaypointUpdate) {
// Sample data // Data needed for transects.
SharedLock inputLock(this->pImpl->input.mutex);
// Verify progress.
size_t nTiles = this->pImpl->scenario.tiles().size();
if (size_t(this->pImpl->input.progress.progress().size()) != nTiles) {
for (size_t i = 0; i < nTiles; ++i) {
progress.push_back(0);
}
} else {
for (size_t i = 0; i < nTiles; ++i) {
progress.push_back(this->pImpl->input.progress.progress()[i]);
}
}
// Copy remaining parameters and release lock.
const auto &scenario = this->pImpl->scenario;
const auto lineDistance = this->pImpl->input.lineDistance;
const auto minTransectLength = this->pImpl->input.minTransectLength;
inputLock.unlock();
// Create transects.
std::string errorString; std::string errorString;
snake::Angle alpha(scenario.mAreaBoundingBox().angle * si::radian); snake::BoundingBox bbox;
std::cout << "Transects angle: " << alpha << std::endl; snake::minimalBoundingBox(this->pImpl->mAreaENU, bbox);
transects.push_back( snake::Angle alpha(bbox.angle * si::radian);
bg::model::linestring<snake::BoostPoint>{scenario.homePositon()}); snake::BoostPoint home;
snake::polygonCenter(this->pImpl->sAreaENU, home);
bool success = snake::flight_plan::transectsFromScenario( transects.push_back(bg::model::linestring<snake::BoostPoint>{home});
lineDistance, minTransectLength, alpha, scenario, progress, transects, // Create transects.
errorString); bool success = snake::transectsFromScenario(
lineDistance, minTransectLength, alpha, this->pImpl->mAreaENU,
this->pImpl->tilesENU, progress, transects, errorString);
if (!success) { if (!success) {
UniqueLock lk(this->pImpl->output.mutex); UniqueLock lk(this->pImpl->output.mutex);
this->pImpl->output.errorMessage = errorString.c_str(); this->pImpl->output.errorMessage = errorString.c_str();
waypointsValid = false; waypointsValid = false;
progressValid = true;
} else if (transects.size() > 1) { } else if (transects.size() > 1) {
success = // Route transects.
snake::flight_plan::route(scenario.joinedArea(), transects, success = snake::route(this->pImpl->jAreaENU, transects,
transectsRouted, route, errorString); transectsRouted, route, errorString);
if (!success) { if (!success) {
UniqueLock lk(this->pImpl->output.mutex); UniqueLock lk(this->pImpl->output.mutex);
this->pImpl->output.errorMessage = errorString.c_str(); this->pImpl->output.errorMessage = errorString.c_str();
waypointsValid = false; waypointsValid = false;
progressValid = true;
} else {
waypointsValid = true;
progressValid = true;
} }
} else { } else {
// No transects // No transects
waypointsValid = false; waypointsValid = false;
progressValid = true;
} }
} else {
// No update necessary
waypointsValid = true;
progressValid = true;
} }
} }
...@@ -428,26 +417,25 @@ void SnakeThread::run() { ...@@ -428,26 +417,25 @@ void SnakeThread::run() {
this->pImpl->output.tileCenterPoints.clear(); this->pImpl->output.tileCenterPoints.clear();
this->pImpl->output.tilesENU.polygons().clear(); this->pImpl->output.tilesENU.polygons().clear();
this->pImpl->output.tileCenterPointsENU.clear(); this->pImpl->output.tileCenterPointsENU.clear();
this->pImpl->output.ENUOrigin = origin;
// Convert and store scenario data. // Convert and store scenario data.
const auto &tiles = this->pImpl->scenario.tiles(); const auto &tiles = this->pImpl->tilesENU;
const auto &centerPoints = this->pImpl->scenario.tileCenterPoints();
for (unsigned int i = 0; i < tiles.size(); ++i) { for (unsigned int i = 0; i < tiles.size(); ++i) {
const auto &tile = tiles[i]; const auto &tile = tiles[i];
SnakeTile geoTile; SnakeTile geoTile;
SnakeTileLocal enuTile; SnakeTileLocal enuTile;
for (size_t i = 0; i < tile.outer().size() - 1; ++i) { for (size_t i = 0; i < tile.outer().size() - 1; ++i) {
auto &p = tile.outer()[i]; const auto &p = tile.outer()[i];
QPointF enuVertex(p.get<0>(), p.get<1>()); QPointF enuVertex{p.get<0>(), p.get<1>()};
QGeoCoordinate geoVertex; QGeoCoordinate geoVertex;
snake::fromENU(origin, p, geoVertex); snake::fromENU(this->pImpl->ENUOrigin, p, geoVertex);
enuTile.polygon().points().push_back(enuVertex); enuTile.polygon().points().push_back(enuVertex);
geoTile.push_back(geoVertex); geoTile.push_back(geoVertex);
} }
const auto &boostPoint = centerPoints[i]; snake::BoostPoint centerPoint;
QPointF enuVertex(boostPoint.get<0>(), boostPoint.get<1>()); snake::polygonCenter(tile, centerPoint);
QPointF enuVertex(centerPoint.get<0>(), centerPoint.get<1>());
QGeoCoordinate geoVertex; QGeoCoordinate geoVertex;
snake::fromENU(origin, boostPoint, geoVertex); snake::fromENU(this->pImpl->ENUOrigin, centerPoint, geoVertex);
geoTile.setCenter(geoVertex); geoTile.setCenter(geoVertex);
this->pImpl->output.tiles.polygons().push_back(geoTile); this->pImpl->output.tiles.polygons().push_back(geoTile);
this->pImpl->output.tileCenterPoints.push_back( this->pImpl->output.tileCenterPoints.push_back(
...@@ -463,7 +451,7 @@ void SnakeThread::run() { ...@@ -463,7 +451,7 @@ void SnakeThread::run() {
this->pImpl->input.progress.progress().clear(); this->pImpl->input.progress.progress().clear();
this->pImpl->output.progressUpdated = true; this->pImpl->output.progressUpdated = true;
} else if (this->pImpl->input.progressChanged) { } else if (this->pImpl->input.progressChanged) {
if (progress.size() == this->pImpl->scenario.tiles().size()) { if (progress.size() == this->pImpl->tilesENU.size()) {
auto &qProgress = this->pImpl->input.progress; auto &qProgress = this->pImpl->input.progress;
qProgress.progress().clear(); qProgress.progress().clear();
for (const auto &p : progress) { for (const auto &p : progress) {
...@@ -501,7 +489,7 @@ void SnakeThread::run() { ...@@ -501,7 +489,7 @@ void SnakeThread::run() {
} }
QPointF enuVertex{boostVertex.get<0>(), boostVertex.get<1>()}; QPointF enuVertex{boostVertex.get<0>(), boostVertex.get<1>()};
QGeoCoordinate geoVertex; QGeoCoordinate geoVertex;
snake::fromENU(origin, boostVertex, geoVertex); snake::fromENU(this->pImpl->ENUOrigin, boostVertex, geoVertex);
this->pImpl->output.arrivalPathENU.push_back(enuVertex); this->pImpl->output.arrivalPathENU.push_back(enuVertex);
this->pImpl->output.arrivalPath.push_back(geoVertex); this->pImpl->output.arrivalPath.push_back(geoVertex);
} }
...@@ -516,7 +504,7 @@ void SnakeThread::run() { ...@@ -516,7 +504,7 @@ void SnakeThread::run() {
} }
QPointF enuVertex{boostVertex.get<0>(), boostVertex.get<1>()}; QPointF enuVertex{boostVertex.get<0>(), boostVertex.get<1>()};
QGeoCoordinate geoVertex; QGeoCoordinate geoVertex;
snake::fromENU(origin, boostVertex, geoVertex); snake::fromENU(this->pImpl->ENUOrigin, boostVertex, geoVertex);
this->pImpl->output.returnPathENU.push_back(enuVertex); this->pImpl->output.returnPathENU.push_back(enuVertex);
this->pImpl->output.returnPath.push_back(geoVertex); this->pImpl->output.returnPath.push_back(geoVertex);
} }
...@@ -525,7 +513,7 @@ void SnakeThread::run() { ...@@ -525,7 +513,7 @@ void SnakeThread::run() {
const auto &boostVertex = route[i]; const auto &boostVertex = route[i];
QPointF enuVertex{boostVertex.get<0>(), boostVertex.get<1>()}; QPointF enuVertex{boostVertex.get<0>(), boostVertex.get<1>()};
QGeoCoordinate geoVertex; QGeoCoordinate geoVertex;
snake::fromENU(origin, boostVertex, geoVertex); snake::fromENU(this->pImpl->ENUOrigin, boostVertex, geoVertex);
this->pImpl->output.waypointsENU.push_back(enuVertex); this->pImpl->output.waypointsENU.push_back(enuVertex);
this->pImpl->output.waypoints.push_back(geoVertex); this->pImpl->output.waypoints.push_back(geoVertex);
} }
......
#include "SnakeTile.h" #include "SnakeTile.h"
SnakeTile::SnakeTile() : WimaAreaData() {} SnakeTile::SnakeTile(QObject *parent) : WimaAreaData(parent) {}
SnakeTile::SnakeTile(const SnakeTile &other, QObject *parent) SnakeTile::SnakeTile(const SnakeTile &other, QObject *parent)
: WimaAreaData(parent) { : WimaAreaData(parent) {
......
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
class SnakeTile : public WimaAreaData { class SnakeTile : public WimaAreaData {
public: public:
SnakeTile(); SnakeTile(QObject *parent = nullptr);
SnakeTile(const SnakeTile &other, QObject *parent = nullptr); SnakeTile(const SnakeTile &other, QObject *parent = nullptr);
QString type() const { return "Tile"; } QString type() const { return "Tile"; }
......
...@@ -375,160 +375,71 @@ void shortestPathFromGraph(const Matrix<double> &graph, size_t startIndex, ...@@ -375,160 +375,71 @@ void shortestPathFromGraph(const Matrix<double> &graph, size_t startIndex,
assert(ret); assert(ret);
(void)ret; (void)ret;
} }
//=========================================================================
// Scenario calculation.
//=========================================================================
}
Scenario::Scenario()
: _tileWidth(5 * bu::si::meter), _tileHeight(5 * bu::si::meter),
_minTileArea(0 * bu::si::meter * bu::si::meter), _needsUpdate(true) {}
void Scenario::setMeasurementArea(const BoostPolygon &area) {
_needsUpdate = true;
_mArea = area;
}
void Scenario::setServiceArea(const BoostPolygon &area) {
_needsUpdate = true;
_sArea = area;
}
void Scenario::setCorridor(const BoostPolygon &area) {
_needsUpdate = true;
_corridor = area;
}
BoostPolygon &Scenario::measurementArea() {
_needsUpdate = true;
return _mArea;
}
BoostPolygon &Scenario::serviceArea() {
_needsUpdate = true;
return _sArea;
}
BoostPolygon &Scenario::corridor() {
_needsUpdate = true;
return _corridor;
}
const BoundingBox &Scenario::mAreaBoundingBox() const {
return _mAreaBoundingBox;
} }
const BoostPolygon &Scenario::measurementArea() const { return _mArea; } bool tiles(const BoostPolygon &area, Length tileHeight, Length tileWidth,
Area minTileArea, std::vector<BoostPolygon> &tiles,
const BoostPolygon &Scenario::serviceArea() const { return _sArea; } BoundingBox &bbox, string &errorString) {
if (area.outer().empty() || area.outer().size() < 4) {
const BoostPolygon &Scenario::corridor() const { return _corridor; } errorString = "Area has to few vertices.";
const BoostPolygon &Scenario::joinedArea() const { return _jArea; }
const vector<BoostPolygon> &Scenario::tiles() const { return _tiles; }
const BoostLineString &Scenario::tileCenterPoints() const {
return _tileCenterPoints;
}
const BoundingBox &Scenario::measurementAreaBBox() const {
return _mAreaBoundingBox;
}
const BoostPoint &Scenario::homePositon() const { return _homePosition; }
bool Scenario::update() {
if (!_needsUpdate)
return true;
bg::correct(_mArea);
bg::correct(_sArea);
bg::correct(_corridor);
polygonCenter(_sArea, _homePosition);
if (!_calculateJoinedArea())
return false;
if (!_calculateBoundingBox())
return false; return false;
if (!_calculateTiles()) }
return false;
_needsUpdate = false;
return true;
}
bool Scenario::_calculateBoundingBox() const {
return minimalBoundingBox(_mArea, _mAreaBoundingBox);
}
/** if (tileWidth <= 0 * bu::si::meter || tileHeight <= 0 * bu::si::meter ||
* Devides the (measurement area) bounding box into tiles and clips it to the minTileArea < 0 * bu::si::meter * bu::si::meter) {
* measurement area.
*
* Devides the (measurement area) bounding box into tiles of width \p tileWidth
* and height \p tileHeight. Clips the resulting tiles to the measurement area.
* Tiles are rejected, if their area is smaller than \p minTileArea. The
* function assumes that \a _mArea and \a _mAreaBoundingBox have correct values.
* \see \ref Scenario::_areas2enu() and \ref Scenario::_calculateBoundingBox().
*
* @param tileWidth The width (>0) of a tile.
* @param tileHeight The heigth (>0) of a tile.
* @param minTileArea The minimal area (>0) of a tile.
*
* @return Returns true if successful.
*/
bool Scenario::_calculateTiles() const {
_tiles.clear();
_tileCenterPoints.clear();
if (_tileWidth <= 0 * bu::si::meter || _tileHeight <= 0 * bu::si::meter ||
_minTileArea < 0 * bu::si::meter * bu::si::meter) {
std::stringstream ss; std::stringstream ss;
ss << "Parameters tileWidth (" << _tileWidth << "), tileHeight (" ss << "Parameters tileWidth (" << tileWidth << "), tileHeight ("
<< _tileHeight << "), minTileArea (" << _minTileArea << tileHeight << "), minTileArea (" << minTileArea
<< ") must be positive."; << ") must be positive.";
errorString = ss.str(); errorString = ss.str();
return false; return false;
} }
double bboxWidth = _mAreaBoundingBox.width; if (bbox.corners.outer().size() != 5) {
double bboxHeight = _mAreaBoundingBox.height; bbox.corners.clear();
minimalBoundingBox(area, bbox);
}
BoostPoint origin = _mAreaBoundingBox.corners.outer()[0]; if (bbox.corners.outer().size() < 5)
return false;
double bboxWidth = bbox.width;
double bboxHeight = bbox.height;
BoostPoint origin = bbox.corners.outer()[0];
// cout << "Origin: " << origin[0] << " " << origin[1] << endl; // cout << "Origin: " << origin[0] << " " << origin[1] << endl;
// Transform _mArea polygon to bounding box coordinate system. // Transform _mArea polygon to bounding box coordinate system.
trans::rotate_transformer<boost::geometry::degree, double, 2, 2> rotate( trans::rotate_transformer<boost::geometry::degree, double, 2, 2> rotate(
_mAreaBoundingBox.angle * 180 / M_PI); bbox.angle * 180 / M_PI);
trans::translate_transformer<double, 2, 2> translate(-origin.get<0>(), trans::translate_transformer<double, 2, 2> translate(-origin.get<0>(),
-origin.get<1>()); -origin.get<1>());
BoostPolygon translated_polygon; BoostPolygon translated_polygon;
BoostPolygon rotated_polygon; BoostPolygon rotated_polygon;
boost::geometry::transform(_mArea, translated_polygon, translate); boost::geometry::transform(area, translated_polygon, translate);
boost::geometry::transform(translated_polygon, rotated_polygon, rotate); boost::geometry::transform(translated_polygon, rotated_polygon, rotate);
bg::correct(rotated_polygon); bg::correct(rotated_polygon);
// cout << bg::wkt<BoostPolygon2D>(rotated_polygon) << endl; // cout << bg::wkt<BoostPolygon2D>(rotated_polygon) << endl;
size_t iMax = ceil(bboxWidth / _tileWidth.value()); size_t iMax = ceil(bboxWidth / tileWidth.value());
size_t jMax = ceil(bboxHeight / _tileHeight.value()); size_t jMax = ceil(bboxHeight / tileHeight.value());
if (iMax < 1 || jMax < 1) { if (iMax < 1 || jMax < 1) {
std::stringstream ss; std::stringstream ss;
ss << "Tile width (" << _tileWidth << ") or tile height (" << _tileHeight ss << "Tile width (" << tileWidth << ") or tile height (" << tileHeight
<< ") to large for measurement area."; << ") to large for measurement area.";
errorString = ss.str(); errorString = ss.str();
return false; return false;
} }
trans::rotate_transformer<boost::geometry::degree, double, 2, 2> rotate_back( trans::rotate_transformer<boost::geometry::degree, double, 2, 2> rotate_back(
-_mAreaBoundingBox.angle * 180 / M_PI); -bbox.angle * 180 / M_PI);
trans::translate_transformer<double, 2, 2> translate_back(origin.get<0>(), trans::translate_transformer<double, 2, 2> translate_back(origin.get<0>(),
origin.get<1>()); origin.get<1>());
for (size_t i = 0; i < iMax; ++i) { for (size_t i = 0; i < iMax; ++i) {
double x_min = _tileWidth.value() * i; double x_min = tileWidth.value() * i;
double x_max = x_min + _tileWidth.value(); double x_max = x_min + tileWidth.value();
for (size_t j = 0; j < jMax; ++j) { for (size_t j = 0; j < jMax; ++j) {
double y_min = _tileHeight.value() * j; double y_min = tileHeight.value() * j;
double y_max = y_min + _tileHeight.value(); double y_max = y_min + tileHeight.value();
BoostPolygon tile_unclipped; BoostPolygon tile_unclipped;
tile_unclipped.outer().push_back(BoostPoint{x_min, y_min}); tile_unclipped.outer().push_back(BoostPoint{x_min, y_min});
...@@ -543,7 +454,7 @@ bool Scenario::_calculateTiles() const { ...@@ -543,7 +454,7 @@ bool Scenario::_calculateTiles() const {
continue; continue;
for (BoostPolygon t : boost_tiles) { for (BoostPolygon t : boost_tiles) {
if (bg::area(t) > _minTileArea.value()) { if (bg::area(t) > minTileArea.value()) {
// Transform boost_tile to world coordinate system. // Transform boost_tile to world coordinate system.
BoostPolygon rotated_tile; BoostPolygon rotated_tile;
BoostPolygon translated_tile; BoostPolygon translated_tile;
...@@ -552,18 +463,15 @@ bool Scenario::_calculateTiles() const { ...@@ -552,18 +463,15 @@ bool Scenario::_calculateTiles() const {
translate_back); translate_back);
// Store tile and calculate center point. // Store tile and calculate center point.
_tiles.push_back(translated_tile); tiles.push_back(translated_tile);
BoostPoint tile_center;
polygonCenter(translated_tile, tile_center);
_tileCenterPoints.push_back(tile_center);
} }
} }
} }
} }
if (_tiles.size() < 1) { if (tiles.size() < 1) {
std::stringstream ss; std::stringstream ss;
ss << "No tiles calculated. Is the minTileArea (" << _minTileArea ss << "No tiles calculated. Is the minTileArea (" << minTileArea
<< ") parameter large enough?"; << ") parameter large enough?";
errorString = ss.str(); errorString = ss.str();
return false; return false;
...@@ -572,19 +480,20 @@ bool Scenario::_calculateTiles() const { ...@@ -572,19 +480,20 @@ bool Scenario::_calculateTiles() const {
return true; return true;
} }
bool Scenario::_calculateJoinedArea() const { bool joinedArea(const BoostPolygon &mArea, const BoostPolygon &sArea,
_jArea.clear(); const BoostPolygon &corridor, BoostPolygon &jArea,
std::string &errorString) {
// Measurement area and service area overlapping? // Measurement area and service area overlapping?
bool overlapingSerMeas = bg::intersects(_mArea, _sArea) ? true : false; bool overlapingSerMeas = bg::intersects(mArea, sArea) ? true : false;
bool corridorValid = _corridor.outer().size() > 0 ? true : false; bool corridorValid = corridor.outer().size() > 0 ? true : false;
// Check if corridor is connecting measurement area and service area. // Check if corridor is connecting measurement area and service area.
bool corridor_is_connection = false; bool corridor_is_connection = false;
if (corridorValid) { if (corridorValid) {
// Corridor overlaping with measurement area? // Corridor overlaping with measurement area?
if (bg::intersects(_corridor, _mArea)) { if (bg::intersects(corridor, mArea)) {
// Corridor overlaping with service area? // Corridor overlaping with service area?
if (bg::intersects(_corridor, _sArea)) { if (bg::intersects(corridor, sArea)) {
corridor_is_connection = true; corridor_is_connection = true;
} }
} }
...@@ -592,13 +501,13 @@ bool Scenario::_calculateJoinedArea() const { ...@@ -592,13 +501,13 @@ bool Scenario::_calculateJoinedArea() const {
// Are areas joinable? // Are areas joinable?
std::deque<BoostPolygon> sol; std::deque<BoostPolygon> sol;
BoostPolygon partialArea = _mArea; BoostPolygon partialArea = mArea;
if (overlapingSerMeas) { if (overlapingSerMeas) {
if (corridor_is_connection) { if (corridor_is_connection) {
bg::union_(partialArea, _corridor, sol); bg::union_(partialArea, corridor, sol);
} }
} else if (corridor_is_connection) { } else if (corridor_is_connection) {
bg::union_(partialArea, _corridor, sol); bg::union_(partialArea, corridor, sol);
} else { } else {
std::stringstream ss; std::stringstream ss;
auto printPoint = [&ss](const BoostPoint &p) { auto printPoint = [&ss](const BoostPoint &p) {
...@@ -606,13 +515,13 @@ bool Scenario::_calculateJoinedArea() const { ...@@ -606,13 +515,13 @@ bool Scenario::_calculateJoinedArea() const {
}; };
ss << "Areas are not overlapping." << std::endl; ss << "Areas are not overlapping." << std::endl;
ss << "Measurement area:"; ss << "Measurement area:";
bg::for_each_point(_mArea, printPoint); bg::for_each_point(mArea, printPoint);
ss << std::endl; ss << std::endl;
ss << "Service area:"; ss << "Service area:";
bg::for_each_point(_sArea, printPoint); bg::for_each_point(sArea, printPoint);
ss << std::endl; ss << std::endl;
ss << "Corridor:"; ss << "Corridor:";
bg::for_each_point(_corridor, printPoint); bg::for_each_point(corridor, printPoint);
ss << std::endl; ss << std::endl;
errorString = ss.str(); errorString = ss.str();
return false; return false;
...@@ -624,62 +533,46 @@ bool Scenario::_calculateJoinedArea() const { ...@@ -624,62 +533,46 @@ bool Scenario::_calculateJoinedArea() const {
} }
// Join areas. // Join areas.
bg::union_(partialArea, _sArea, sol); bg::union_(partialArea, sArea, sol);
if (sol.size() > 0) { if (sol.size() > 0) {
_jArea = sol[0]; jArea = sol[0];
} else { } else {
std::stringstream ss;
auto printPoint = [&ss](const BoostPoint &p) {
ss << " (" << p.get<0>() << ", " << p.get<1>() << ")";
};
ss << "Areas not joinable." << std::endl;
ss << "Measurement area:";
bg::for_each_point(mArea, printPoint);
ss << std::endl;
ss << "Service area:";
bg::for_each_point(sArea, printPoint);
ss << std::endl;
ss << "Corridor:";
bg::for_each_point(corridor, printPoint);
ss << std::endl;
errorString = ss.str();
return false; return false;
} }
return true; return true;
} }
Area Scenario::minTileArea() const { return _minTileArea; } bool joinedArea(const std::vector<BoostPolygon *> &areas,
BoostPolygon &joinedArea) {
void Scenario::setMinTileArea(Area minTileArea) {
if (minTileArea >= 0 * bu::si::meter * bu::si::meter) {
_needsUpdate = true;
_minTileArea = minTileArea;
}
}
Length Scenario::tileHeight() const { return _tileHeight; }
void Scenario::setTileHeight(Length tileHeight) {
if (tileHeight > 0 * bu::si::meter) {
_needsUpdate = true;
_tileHeight = tileHeight;
}
}
Length Scenario::tileWidth() const { return _tileWidth; }
void Scenario::setTileWidth(Length tileWidth) {
if (tileWidth > 0 * bu::si::meter) {
_needsUpdate = true;
_tileWidth = tileWidth;
}
}
//=========================================================================
// Tile calculation.
//=========================================================================
bool joinAreas(const std::vector<BoostPolygon> &areas,
BoostPolygon &joinedArea) {
if (areas.size() < 1) if (areas.size() < 1)
return false; return false;
joinedArea = *areas[0];
std::deque<std::size_t> idxList; std::deque<std::size_t> idxList;
for (size_t i = 1; i < areas.size(); ++i) for (size_t i = 1; i < areas.size(); ++i)
idxList.push_back(i); idxList.push_back(i);
joinedArea = areas[0];
std::deque<BoostPolygon> sol; std::deque<BoostPolygon> sol;
while (idxList.size() > 0) { while (idxList.size() > 0) {
bool success = false; bool success = false;
for (auto it = idxList.begin(); it != idxList.end(); ++it) { for (auto it = idxList.begin(); it != idxList.end(); ++it) {
bg::union_(joinedArea, areas[*it], sol); bg::union_(joinedArea, *areas[*it], sol);
if (sol.size() > 0) { if (sol.size() > 0) {
joinedArea = sol[0]; joinedArea = sol[0];
sol.clear(); sol.clear();
...@@ -704,13 +597,12 @@ void BoundingBox::clear() { ...@@ -704,13 +597,12 @@ void BoundingBox::clear() {
corners.clear(); corners.clear();
} }
bool flight_plan::transectsFromScenario(Length distance, Length minLength, bool transectsFromScenario(Length distance, Length minLength, Angle angle,
Angle angle, const Scenario &scenario, const BoostPolygon &mArea,
const Progress &p, const std::vector<BoostPolygon> &tiles,
flight_plan::Transects &t, const Progress &p, Transects &t,
string &errorString) { string &errorString) {
// Rotate measurement area by angle and calculate bounding box. // Rotate measurement area by angle and calculate bounding box.
auto &mArea = scenario.measurementArea();
BoostPolygon mAreaRotated; BoostPolygon mAreaRotated;
trans::rotate_transformer<bg::degree, double, 2, 2> rotate(angle.value() * trans::rotate_transformer<bg::degree, double, 2, 2> rotate(angle.value() *
180 / M_PI); 180 / M_PI);
...@@ -778,14 +670,13 @@ bool flight_plan::transectsFromScenario(Length distance, Length minLength, ...@@ -778,14 +670,13 @@ bool flight_plan::transectsFromScenario(Length distance, Length minLength,
ClipperLib::pftNonZero, ClipperLib::pftNonZero); ClipperLib::pftNonZero, ClipperLib::pftNonZero);
const auto *transects = &clippedTransecs; const auto *transects = &clippedTransecs;
bool ignoreProgress = p.size() != scenario.tiles().size(); bool ignoreProgress = p.size() != tiles.size();
ClipperLib::PolyTree clippedTransecs2; ClipperLib::PolyTree clippedTransecs2;
if (!ignoreProgress) { if (!ignoreProgress) {
// Calculate processed tiles (_progress[i] == 100) and subtract them from // Calculate processed tiles (_progress[i] == 100) and subtract them from
// measurement area. // measurement area.
size_t numTiles = p.size(); size_t numTiles = p.size();
vector<BoostPolygon> processedTiles; vector<BoostPolygon> processedTiles;
const auto &tiles = scenario.tiles();
for (size_t i = 0; i < numTiles; ++i) { for (size_t i = 0; i < numTiles; ++i) {
if (p[i] == 100) { if (p[i] == 100) {
processedTiles.push_back(tiles[i]); processedTiles.push_back(tiles[i]);
...@@ -889,10 +780,8 @@ void generateRoutingModel(const BoostLineString &vertices, ...@@ -889,10 +780,8 @@ void generateRoutingModel(const BoostLineString &vertices,
dataModel.depot = 0; dataModel.depot = 0;
} }
bool flight_plan::route(const BoostPolygon &area, bool route(const BoostPolygon &area, const Transects &transects,
const flight_plan::Transects &transects, Transects &transectsRouted, Route &route, string &errorString) {
Transects &transectsRouted, flight_plan::Route &route,
string &errorString) {
//======================================= //=======================================
// Route Transects using Google or-tools. // Route Transects using Google or-tools.
//======================================= //=======================================
......
...@@ -103,6 +103,7 @@ void toENU(const GeoPoint &origin, const GeoPoint &in, BoostPoint &out) { ...@@ -103,6 +103,7 @@ void toENU(const GeoPoint &origin, const GeoPoint &in, BoostPoint &out) {
out.set<1>(y); out.set<1>(y);
(void)z; (void)z;
} }
template <class GeoPoint> template <class GeoPoint>
void fromENU(const GeoPoint &origin, const BoostPoint &in, GeoPoint &out) { void fromENU(const GeoPoint &origin, const BoostPoint &in, GeoPoint &out) {
GeographicLib::Geocentric earth(GeographicLib::Constants::WGS84_a(), GeographicLib::Geocentric earth(GeographicLib::Constants::WGS84_a(),
...@@ -117,88 +118,6 @@ void fromENU(const GeoPoint &origin, const BoostPoint &in, GeoPoint &out) { ...@@ -117,88 +118,6 @@ void fromENU(const GeoPoint &origin, const BoostPoint &in, GeoPoint &out) {
out.setAltitude(alt); out.setAltitude(alt);
} }
void polygonCenter(const BoostPolygon &polygon, BoostPoint &center);
bool minimalBoundingBox(const BoostPolygon &polygon, BoundingBox &minBBox);
void offsetPolygon(const BoostPolygon &polygon, BoostPolygon &polygonOffset,
double offset);
void graphFromPolygon(const BoostPolygon &polygon,
const BoostLineString &vertices, Matrix<double> &graph);
void toDistanceMatrix(Matrix<double> &graph);
bool dijkstraAlgorithm(
const size_t numElements, size_t startIndex, size_t endIndex,
std::vector<size_t> &elementPath,
std::function<double(const size_t, const size_t)> distanceDij);
void shortestPathFromGraph(const Matrix<double> &graph, size_t startIndex,
size_t endIndex, std::vector<size_t> &pathIdx);
//=========================================================================
// Scenario.
//=========================================================================
typedef bu::quantity<bu::si::length> Length;
typedef bu::quantity<bu::si::area> Area;
typedef bu::quantity<bu::si::plane_angle> Angle;
class Scenario {
public:
Scenario();
void setMeasurementArea(const BoostPolygon &area);
void setServiceArea(const BoostPolygon &area);
void setCorridor(const BoostPolygon &area);
const BoundingBox &mAreaBoundingBox() const;
const BoostPolygon &measurementArea() const;
const BoostPolygon &serviceArea() const;
const BoostPolygon &corridor() const;
BoostPolygon &measurementArea();
BoostPolygon &serviceArea();
BoostPolygon &corridor();
Length tileWidth() const;
void setTileWidth(Length tileWidth);
Length tileHeight() const;
void setTileHeight(Length tileHeight);
Area minTileArea() const;
void setMinTileArea(Area minTileArea);
const BoostPolygon &joinedArea() const;
const vector<BoostPolygon> &tiles() const;
const BoostLineString &tileCenterPoints() const;
const BoundingBox &measurementAreaBBox() const;
const BoostPoint &homePositon() const;
bool update();
mutable string errorString;
private:
bool _calculateBoundingBox() const;
bool _calculateTiles() const;
bool _calculateJoinedArea() const;
Length _tileWidth;
Length _tileHeight;
Area _minTileArea;
mutable bool _needsUpdate;
BoostPolygon _mArea;
BoostPolygon _sArea;
BoostPolygon _corridor;
mutable BoostPolygon _jArea;
mutable BoundingBox _mAreaBoundingBox;
mutable vector<BoostPolygon> _tiles;
mutable BoostLineString _tileCenterPoints;
mutable BoostPoint _homePosition;
};
template <class GeoPoint, template <class, class...> class Container> template <class GeoPoint, template <class, class...> class Container>
void areaToEnu(const GeoPoint &origin, const Container<GeoPoint> &in, void areaToEnu(const GeoPoint &origin, const Container<GeoPoint> &in,
BoostPolygon &out) { BoostPolygon &out) {
...@@ -220,25 +139,43 @@ void areaFromEnu(const GeoPoint &origin, BoostPolygon &in, ...@@ -220,25 +139,43 @@ void areaFromEnu(const GeoPoint &origin, BoostPolygon &in,
} }
} }
bool joinAreas(const std::vector<BoostPolygon> &areas, void polygonCenter(const BoostPolygon &polygon, BoostPoint &center);
BoostPolygon &joinedArea); bool minimalBoundingBox(const BoostPolygon &polygon, BoundingBox &minBBox);
void offsetPolygon(const BoostPolygon &polygon, BoostPolygon &polygonOffset,
double offset);
void graphFromPolygon(const BoostPolygon &polygon,
const BoostLineString &vertices, Matrix<double> &graph);
void toDistanceMatrix(Matrix<double> &graph);
bool dijkstraAlgorithm(
const size_t numElements, size_t startIndex, size_t endIndex,
std::vector<size_t> &elementPath,
std::function<double(const size_t, const size_t)> distanceDij);
void shortestPathFromGraph(const Matrix<double> &graph, size_t startIndex,
size_t endIndex, std::vector<size_t> &pathIdx);
//======================================================================================== typedef bu::quantity<bu::si::length> Length;
// flight_plan typedef bu::quantity<bu::si::area> Area;
//======================================================================================== typedef bu::quantity<bu::si::plane_angle> Angle;
namespace flight_plan { bool joinedArea(const std::vector<BoostPolygon *> &areas, BoostPolygon &jArea);
bool joinedArea(const BoostPolygon &mArea, const BoostPolygon &sArea,
const BoostPolygon &corridor, BoostPolygon &jArea,
std::string &errorString);
bool tiles(const BoostPolygon &area, Length tileHeight, Length tileWidth,
Area minTileArea, std::vector<BoostPolygon> &tiles,
BoundingBox &bbox, std::string &errorString);
using Transects = vector<BoostLineString>; using Transects = vector<BoostLineString>;
using Progress = vector<int>; using Progress = vector<int>;
using Route = vector<BoostPoint>; using Route = vector<BoostPoint>;
bool transectsFromScenario(Length distance, Length minLength, Angle angle, bool transectsFromScenario(Length distance, Length minLength, Angle angle,
const Scenario &scenario, const Progress &p, const BoostPolygon &mArea,
Transects &t, string &errorString); const std::vector<BoostPolygon> &tiles,
const Progress &p, Transects &t,
string &errorString);
bool route(const BoostPolygon &area, const Transects &transects, bool route(const BoostPolygon &area, const Transects &transects,
Transects &transectsRouted, Route &route, string &errorString); Transects &transectsRouted, Route &route, string &errorString);
} // namespace flight_plan
namespace detail { namespace detail {
const double offsetConstant = const double offsetConstant =
......
...@@ -43,7 +43,7 @@ Rectangle { ...@@ -43,7 +43,7 @@ Rectangle {
SectionHeader { SectionHeader {
id: settingsHeader id: settingsHeader
text: qsTr("Settings") text: qsTr("General")
} }
Column { Column {
...@@ -79,12 +79,18 @@ Rectangle { ...@@ -79,12 +79,18 @@ Rectangle {
} }
} // Column - Settings } // Column - Settings
SectionHeader { SectionHeader {
id: snakeHeader id: tilesHeader
text: qsTr("Snake") text: qsTr("Tiles")
} }
Column { Column {
anchors.left: parent.left
anchors.right: parent.right
spacing: _margin
visible: tilesHeader.checked
GridLayout { GridLayout {
anchors.left: parent.left anchors.left: parent.left
anchors.right: parent.right anchors.right: parent.right
...@@ -92,44 +98,69 @@ Rectangle { ...@@ -92,44 +98,69 @@ Rectangle {
rowSpacing: _margin rowSpacing: _margin
columns: 2 columns: 2
QGCLabel { text: qsTr("Tile Height") } QGCLabel { text: qsTr("Height") }
FactTextField { FactTextField {
//fact: areaItem.borderPolygonOffset fact: areaItem.tileHeight
Layout.fillWidth: true Layout.fillWidth: true
} }
QGCLabel { text: qsTr("Tile Width") } QGCLabel { text: qsTr("Width") }
FactTextField { FactTextField {
//fact: areaItem.borderPolygonOffset fact: areaItem.tileWidth
Layout.fillWidth: true Layout.fillWidth: true
} }
QGCLabel { text: qsTr("Min. Tile Area") } QGCLabel { text: qsTr("Min. Area") }
FactTextField { FactTextField {
//fact: areaItem.borderPolygonOffset fact: areaItem.minTileArea
Layout.fillWidth: true Layout.fillWidth: true
} }
QGCLabel { text: qsTr("Transect Distance") }
FactCheckBox {
text: qsTr("Show Tiles")
fact: areaItem.showTiles
}
} // Tile GridLayout
} // Tile Column
SectionHeader {
id: transectsHeader
text: qsTr("Transects")
}
Column {
anchors.left: parent.left
anchors.right: parent.right
spacing: _margin
visible: transectsHeader.checked
GridLayout {
anchors.left: parent.left
anchors.right: parent.right
columnSpacing: _margin
rowSpacing: _margin
columns: 2
QGCLabel { text: qsTr("Distance") }
FactTextField { FactTextField {
//fact: areaItem.borderPolygonOffset fact: areaItem.transectDistance
Layout.fillWidth: true Layout.fillWidth: true
} }
QGCLabel { text: qsTr("Min. Transect Length") } QGCLabel { text: qsTr("Min. Length") }
FactTextField { FactTextField {
//fact: areaItem.borderPolygonOffset fact: areaItem.minTransectLength
Layout.fillWidth: true Layout.fillWidth: true
} }
} } // Transects GridLayout
} // Transects Column
} // Snake
SectionHeader { SectionHeader {
id: statsHeader id: statsHeader
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment