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