Commit 58b2b82d authored by Valentin Platzgummer's avatar Valentin Platzgummer

circular and linera transect generators added

parent 06e9bcab
......@@ -420,8 +420,7 @@ HEADERS += \
src/Wima/RoutingThread.h \
src/Wima/Snake/CircularGenerator.h \
src/Wima/Snake/GeneratorBase.h \
src/Wima/Snake/GeneratorData.h \
src/Wima/Snake/StandardData.h \
src/Wima/Snake/LinearGenerator.h \
src/Wima/Snake/clipper/clipper.hpp \
src/Wima/Snake/mapbox/feature.hpp \
src/Wima/Snake/mapbox/geometry.hpp \
......@@ -516,7 +515,7 @@ SOURCES += \
src/Wima/RoutingThread.cpp \
src/Wima/Snake/CircularGenerator.cpp \
src/Wima/Snake/GeneratorBase.cc \
src/Wima/Snake/StandardData.cpp \
src/Wima/Snake/LinearGenerator.cpp \
src/Wima/Snake/clipper/clipper.cpp \
src/Wima/Snake/snake.cpp \
src/Wima/Geometry/GeoPoint3D.cpp \
......@@ -1456,5 +1455,8 @@ contains (CONFIG, QGC_DISABLE_INSTALLER_SETUP) {
}
DISTFILES += \
src/Wima/Routing/json/CircularGenerator.SettingsGroup.json \
src/Wima/Snake/json/LinearGenerator.SettingsGroup.json \
src/Wima/json/CircularSurvey.SettingsGroup.json \
src/WimaView/WimaMeasurementAreaEditor.qml \
src/Settings/Wima.SettingsGroup.json
......@@ -287,6 +287,8 @@
<file alias="WimaArea.SettingsGroup.json">src/Wima/Geometry/json/WimaArea.SettingsGroup.json</file>
<file alias="WimaController.SettingsGroup.json">src/Wima/json/WimaController.SettingsGroup.json</file>
<file alias="Wima.SettingsGroup.json">src/Settings/Wima.SettingsGroup.json</file>
<file alias="CircularGenerator.SettingsGroup.json">src/Wima/Snake/json/CircularGenerator.SettingsGroup.json</file>
<file alias="LinearGenerator.SettingsGroup.json">src/Wima/Snake/json/LinearGenerator.SettingsGroup.json</file>
</qresource>
<qresource prefix="/MockLink">
<file alias="APMArduCopterMockLink.params">src/comm/APMArduCopterMockLink.params</file>
......
......@@ -9,6 +9,10 @@
#define CLIPPER_SCALE 1000000
#include "clipper/clipper.hpp"
using namespace ClipperLib;
template <> inline auto get<0>(const IntPoint &p) { return p.X; }
template <> inline auto get<1>(const IntPoint &p) { return p.Y; }
#include "Geometry/GenericCircle.h"
#include "Snake/SnakeTile.h"
......@@ -18,19 +22,6 @@
QGC_LOGGING_CATEGORY(CircularSurveyLog, "CircularSurveyLog")
using namespace ClipperLib;
template <> auto get<0>(const IntPoint &p) { return p.X; }
template <> auto get<1>(const IntPoint &p) { return p.Y; }
template <class Functor> class CommandRAII {
public:
CommandRAII(Functor f) : fun(f) {}
~CommandRAII() { fun(); }
private:
Functor fun;
};
template <typename T>
constexpr typename std::underlying_type<T>::type integral(T value) {
return static_cast<typename std::underlying_type<T>::type>(value);
......@@ -456,7 +447,7 @@ void CircularSurvey::_updateWorker() {
// Routing par.
RoutingParameter par;
par.numSolutionsPerRun = 5;
par.numSolutions = 5;
if (this->_numRuns.rawValue().toUInt() < 1) {
disconnect(&this->_numRuns, &Fact::rawValueChanged, this,
&CircularSurvey::_rebuildTransects);
......
......@@ -59,7 +59,7 @@ void RoutingThread::run() {
lk.unlock();
auto safeAreaENU = par.safeArea;
auto numRuns = par.numRuns;
auto numSolutionsPerRun = par.numSolutionsPerRun;
auto numSolutionsPerRun = par.numSolutions;
PtrRoutingData pRouteData(new RoutingData());
auto &transectsENU = pRouteData->transects;
......
......@@ -17,9 +17,9 @@ struct RoutingData {
};
struct RoutingParameter {
RoutingParameter() : numSolutionsPerRun(1), numRuns(1) {}
RoutingParameter() : numSolutions(1), numRuns(1) {}
snake::FPolygon safeArea;
std::size_t numSolutionsPerRun;
std::size_t numSolutions;
std::size_t numRuns;
};
//!
......
......@@ -6,19 +6,39 @@ QGC_LOGGING_CATEGORY(CircularGeneratorLog, "CircularGeneratorLog")
#define CLIPPER_SCALE 1000000
#include "Wima/Geometry/GenericCircle.h"
#include "clipper/clipper.hpp"
// Clipper and GenericCircle
using namespace ClipperLib;
template <> auto get<0>(const IntPoint &p) { return p.X; }
template <> auto get<1>(const IntPoint &p) { return p.Y; }
template <> inline auto get<0>(const IntPoint &p) { return p.X; }
template <> inline auto get<1>(const IntPoint &p) { return p.Y; }
#include "SnakeTile.h"
#include "Wima/RoutingThread.h"
namespace routing {
bool circularTransects(const snake::FPolygon &polygon,
bool circularTransects(const snake::FPoint &reference,
const snake::FPolygon &polygon,
const std::vector<snake::FPolygon> &tiles,
snake::Length deltaR, snake::Angle deltaAlpha,
snake::Length minLength, snake::Transects &transects);
CircularGenerator::CircularGenerator(QObject *parent) : GeneratorBase(parent) {}
const char *CircularGenerator::settingsGroup = "CircularGenerator";
const char *CircularGenerator::distanceName = "TransectDistance";
const char *CircularGenerator::deltaAlphaName = "DeltaAlpha";
const char *CircularGenerator::minLengthName = "MinLength";
CircularGenerator::CircularGenerator(QObject *parent)
: CircularGenerator(nullptr, parent) {}
CircularGenerator::CircularGenerator(GeneratorBase::Data d, QObject *parent)
: GeneratorBase(d, parent), _connectionsEstablished(false),
_metaDataMap(FactMetaData::createMapFromJsonFile(
QStringLiteral(":/json/CircularGenerator.SettingsGroup.json"), this)),
_distance(settingsGroup, _metaDataMap[distanceName]),
_deltaAlpha(settingsGroup, _metaDataMap[deltaAlphaName]),
_minLength(settingsGroup, _metaDataMap[minLengthName]) {
establishConnections();
}
QString CircularGenerator::editorQML() {
return QStringLiteral("CircularGeneratorEditor.qml");
......@@ -34,18 +54,171 @@ QString CircularGenerator::name() {
QString CircularGenerator::abbreviation() { return QStringLiteral("C. Gen."); }
bool CircularGenerator::get(const WimaPlanData &data,
GeneratorBase &generator) {
auto generator = [depot, pPolygon, pTiles, distance, alpha,
minLength](snake::Transects &transects) -> bool {
bool value = circularTransects(*pPolygon, *pTiles, distance, alpha,
minLength, transects);
transects.insert(transects.begin(), snake::FLineString{depot});
return value;
};
bool CircularGenerator::get(Generator &generator) {
if (this->_d) {
if (this->_d->isValid()) {
// Prepare data.
const auto &origin = this->_d->origin();
if (!origin.isValid()) {
qCWarning(CircularGeneratorLog) << "get(): origin invalid." << origin;
return false;
}
const auto &ref = this->_reference;
if (!ref.isValid()) {
qCWarning(CircularGeneratorLog) << "get(): reference invalid." << ref;
return false;
}
snake::FPoint reference;
snake::toENU(origin, ref, reference);
auto geoPolygon = this->_d->measurementArea().coordinateList();
for (auto &v : geoPolygon) {
if (v.isValid()) {
v.setAltitude(0);
} else {
qCWarning(CircularGeneratorLog) << "get(): measurement area invalid.";
for (const auto &w : geoPolygon) {
qCWarning(CircularGeneratorLog) << w;
}
return false;
}
}
auto pPolygon = std::make_shared<snake::FPolygon>();
snake::areaToEnu(origin, geoPolygon, *pPolygon);
// Progress and tiles.
const auto &progress = this->_d->measurementArea().progress();
const auto *tiles = this->_d->measurementArea().tiles();
auto pTiles = std::make_shared<std::vector<snake::FPolygon>>();
if (progress.size() == tiles->count()) {
for (int i = 0; i < tiles->count(); ++i) {
if (progress[i] == 100) {
const auto *tile = tiles->value<const SnakeTile *>(i);
if (tile != nullptr) {
snake::FPolygon tileENU;
snake::areaToEnu(origin, tile->coordinateList(), tileENU);
pTiles->push_back(std::move(tileENU));
} else {
qCWarning(CircularGeneratorLog)
<< "get(): progress.size() != tiles->count().";
return false;
}
}
}
} else {
qCWarning(CircularGeneratorLog)
<< "get(): progress.size() != tiles->count().";
return false;
}
auto geoDepot = this->_d->serviceArea().depot();
if (!geoDepot.isValid()) {
qCWarning(CircularGeneratorLog) << "get(): depot invalid." << geoDepot;
return false;
}
snake::FPoint depot;
snake::toENU(origin, geoDepot, depot);
// Fetch transect parameter.
auto distance =
snake::Length(this->_distance.rawValue().toDouble() * bu::si::meter);
auto minLength =
snake::Length(this->_minLength.rawValue().toDouble() * bu::si::meter);
auto alpha = snake::Angle(this->_deltaAlpha.rawValue().toDouble() *
bu::degree::degree);
generator = [reference, depot, pPolygon, pTiles, distance, alpha,
minLength](snake::Transects &transects) -> bool {
bool value = circularTransects(reference, *pPolygon, *pTiles, distance,
alpha, minLength, transects);
transects.insert(transects.begin(), snake::FLineString{depot});
return value;
};
return true;
} else {
qCWarning(CircularGeneratorLog) << "get(): data invalid.";
return false;
}
} else {
qCWarning(CircularGeneratorLog) << "get(): data member not set.";
return false;
}
}
QGeoCoordinate CircularGenerator::reference() const { return _reference; }
void CircularGenerator::setReference(const QGeoCoordinate &reference) {
if (_reference != reference) {
_reference = reference;
emit referenceChanged();
}
}
void CircularGenerator::resetReference() {
setReference(_d->measurementArea().center());
}
void CircularGenerator::establishConnections() {
if (this->_d && !this->_connectionsEstablished) {
connect(this->_d.get(), &WimaPlanData::measurementAreaChanged, this,
&GeneratorBase::generatorChanged);
connect(this->_d.get(), &WimaPlanData::originChanged, this,
&GeneratorBase::generatorChanged);
connect(&this->_d->measurementArea(),
&WimaMeasurementAreaData::progressChanged, this,
&GeneratorBase::generatorChanged);
connect(&this->_d->measurementArea(),
&WimaMeasurementAreaData::tileDataChanged, this,
&GeneratorBase::generatorChanged);
connect(&this->_d->serviceArea(), &WimaServiceAreaData::depotChanged, this,
&GeneratorBase::generatorChanged);
connect(this->distance(), &Fact::rawValueChanged, this,
&GeneratorBase::generatorChanged);
connect(this->deltaAlpha(), &Fact::rawValueChanged, this,
&GeneratorBase::generatorChanged);
connect(this->minLength(), &Fact::rawValueChanged, this,
&GeneratorBase::generatorChanged);
connect(this, &CircularGenerator::referenceChanged, this,
&GeneratorBase::generatorChanged);
this->_connectionsEstablished = true;
}
}
void CircularGenerator::deleteConnections() {
if (this->_d && this->_connectionsEstablished) {
disconnect(this->_d.get(), &WimaPlanData::measurementAreaChanged, this,
&GeneratorBase::generatorChanged);
disconnect(this->_d.get(), &WimaPlanData::originChanged, this,
&GeneratorBase::generatorChanged);
disconnect(&this->_d->measurementArea(),
&WimaMeasurementAreaData::progressChanged, this,
&GeneratorBase::generatorChanged);
disconnect(&this->_d->measurementArea(),
&WimaMeasurementAreaData::tileDataChanged, this,
&GeneratorBase::generatorChanged);
disconnect(&this->_d->serviceArea(), &WimaServiceAreaData::depotChanged,
this, &GeneratorBase::generatorChanged);
disconnect(this->distance(), &Fact::rawValueChanged, this,
&GeneratorBase::generatorChanged);
disconnect(this->deltaAlpha(), &Fact::rawValueChanged, this,
&GeneratorBase::generatorChanged);
disconnect(this->minLength(), &Fact::rawValueChanged, this,
&GeneratorBase::generatorChanged);
disconnect(this, &CircularGenerator::referenceChanged, this,
&GeneratorBase::generatorChanged);
this->_connectionsEstablished = false;
}
}
bool circularTransects(const snake::FPolygon &polygon,
Fact *CircularGenerator::distance() { return &_distance; }
Fact *CircularGenerator::deltaAlpha() { return &_deltaAlpha; }
Fact *CircularGenerator::minLength() { return &_minLength; }
bool circularTransects(const snake::FPoint &reference,
const snake::FPolygon &polygon,
const std::vector<snake::FPolygon> &tiles,
snake::Length deltaR, snake::Angle deltaAlpha,
snake::Length minLength, snake::Transects &transects) {
......@@ -55,7 +228,6 @@ bool circularTransects(const snake::FPolygon &polygon,
if (polygon.outer().size() >= 3) {
using namespace boost::units;
// Convert geo polygon to ENU polygon.
snake::FPoint origin{0, 0};
std::string error;
// Check validity.
if (!bg::is_valid(polygon, error)) {
......@@ -75,7 +247,7 @@ bool circularTransects(const snake::FPolygon &polygon,
// qCWarning(CircularGeneratorLog) << "circularTransects():";
//#endif
for (const auto &p : polygon.outer()) {
snake::Length distance = bg::distance(origin, p) * si::meter;
snake::Length distance = bg::distance(reference, p) * si::meter;
distances.push_back(distance);
snake::Angle alpha = (std::atan2(p.get<1>(), p.get<0>())) * si::radian;
alpha = alpha < 0 * si::radian ? alpha + 2 * M_PI * si::radian : alpha;
......@@ -95,8 +267,8 @@ bool circularTransects(const snake::FPolygon &polygon,
snake::Angle alpha1(0 * degree::degree);
snake::Angle alpha2(360 * degree::degree);
// Determine r_min by successive approximation
if (!bg::within(origin, polygon.outer())) {
rMin = bg::distance(origin, polygon) * si::meter;
if (!bg::within(reference, polygon.outer())) {
rMin = bg::distance(reference, polygon) * si::meter;
}
auto rMax = (*std::max_element(distances.begin(),
......@@ -107,9 +279,9 @@ bool circularTransects(const snake::FPolygon &polygon,
ClipperLib::cInt(std::round(rMin.value() * CLIPPER_SCALE));
const auto deltaRScaled =
ClipperLib::cInt(std::round(deltaR.value() * CLIPPER_SCALE));
auto originScaled =
ClipperLib::IntPoint{ClipperLib::cInt(std::round(origin.get<0>())),
ClipperLib::cInt(std::round(origin.get<1>()))};
auto referenceScaled = ClipperLib::IntPoint{
ClipperLib::cInt(std::round(reference.get<0>())),
ClipperLib::cInt(std::round(reference.get<1>()))};
// Generate circle sectors.
auto rScaled = rMinScaled;
......@@ -136,7 +308,7 @@ bool circularTransects(const snake::FPolygon &polygon,
using ClipperCircle =
GenericCircle<ClipperLib::cInt, ClipperLib::IntPoint>;
for (auto &sector : sectors) {
ClipperCircle circle(rScaled, originScaled);
ClipperCircle circle(rScaled, referenceScaled);
approximate(circle, nSectors, sector);
rScaled += deltaRScaled;
}
......
#include "GeneratorBase.h"
#include "StandardData.h"
#include <QGeoCoordinate>
namespace routing {
class CircularGenerator : public GeneratorBase {
Q_OBJECT
public:
CircularGenerator(QObject *parent = nullptr) override;
CircularGenerator(QObject *parent = nullptr);
CircularGenerator(Data d, QObject *parent = nullptr);
Q_PROPERTY(QGeoCoordinate reference READ reference WRITE setReference NOTIFY
referenceChanged)
Q_PROPERTY(Fact *distance READ distance CONSTANT)
Q_PROPERTY(Fact *deltaAlpha READ deltaAlpha CONSTANT)
Q_PROPERTY(Fact *minLength READ minLength CONSTANT)
virtual QString editorQML() override;
virtual QString mapVisualQML() override;
......@@ -14,7 +22,36 @@ public:
virtual QString name() override;
virtual QString abbreviation() override;
virtual bool get(const WimaPlanData &data, GeneratorBase &generator);
virtual bool get(Generator &generator) override;
QGeoCoordinate reference() const;
Fact *distance();
Fact *deltaAlpha();
Fact *minLength();
void setReference(const QGeoCoordinate &reference);
void resetReference();
static const char *settingsGroup;
static const char *distanceName;
static const char *deltaAlphaName;
static const char *minLengthName;
signals:
void referenceChanged();
protected:
virtual void establishConnections() override;
virtual void deleteConnections() override;
private:
bool _connectionsEstablished;
QGeoCoordinate _reference;
QMap<QString, FactMetaData *> _metaDataMap;
SettingsFact _distance;
SettingsFact _deltaAlpha;
SettingsFact _minLength;
};
} // namespace routing
......@@ -2,12 +2,26 @@
namespace routing {
GeneratorBase::GeneratorBase(QObject *parent) : QObject(parent) {}
GeneratorBase::GeneratorBase(QObject *parent)
: GeneratorBase(nullptr, parent) {}
GeneratorBase::GeneratorBase(std::shared_ptr<GeneratorData> par,
QObject *parent)
: QObject(parent) {}
GeneratorBase::GeneratorBase(GeneratorBase::Data d, QObject *parent)
: QObject(parent), _d(d) {
establishConnections();
}
GeneratorBase::~GeneratorBase() {}
GeneratorBase::Data GeneratorBase::data() const { return _d; }
void GeneratorBase::setData(const Data &d) {
deleteConnections();
_d = d;
establishConnections();
}
void GeneratorBase::establishConnections() {}
void GeneratorBase::deleteConnections() {}
} // namespace routing
......@@ -12,9 +12,11 @@ namespace routing {
class GeneratorBase : public QObject {
Q_OBJECT
public:
using Data = std::shared_ptr<WimaPlanData>;
using Generator = std::function<bool(snake::Transects &)>;
explicit GeneratorBase(QObject *parent = nullptr);
explicit GeneratorBase(Data d, QObject *parent = nullptr);
~GeneratorBase();
virtual QString editorQML() = 0;
......@@ -23,10 +25,18 @@ public:
virtual QString name() = 0;
virtual QString abbreviation() = 0;
virtual bool get(const WimaPlanData &data, GeneratorBase &generator) = 0;
virtual bool get(Generator &generator) = 0;
Data data() const;
void setData(const Data &d);
signals:
void generatorChanged();
protected:
virtual void establishConnections();
virtual void deleteConnections();
Data _d;
};
} // namespace routing
#pragma once
#include <QObject>
class GeneratorData : public QObject {
Q_OBJECT
public:
explicit GeneratorData(QObject *parent = nullptr);
};
#include "LinearGenerator.h"
#include "QGCLoggingCategory.h"
QGC_LOGGING_CATEGORY(LinearGeneratorLog, "LinearGeneratorLog")
#define CLIPPER_SCALE 1000000
#include "clipper/clipper.hpp"
#include "SnakeTile.h"
#include "Wima/RoutingThread.h"
namespace routing {
bool linearTransects(const snake::FPolygon &polygon,
const std::vector<snake::FPolygon> &tiles,
snake::Length distance, snake::Angle angle,
snake::Length minLength, snake::Transects &transects);
const char *LinearGenerator::settingsGroup = "LinearGenerator";
const char *LinearGenerator::distanceName = "TransectDistance";
const char *LinearGenerator::alphaName = "Alpha";
const char *LinearGenerator::minLengthName = "MinLength";
LinearGenerator::LinearGenerator(QObject *parent)
: LinearGenerator(nullptr, parent) {}
LinearGenerator::LinearGenerator(GeneratorBase::Data d, QObject *parent)
: GeneratorBase(d, parent),
_metaDataMap(FactMetaData::createMapFromJsonFile(
QStringLiteral(":/json/LinearGenerator.SettingsGroup.json"), this)),
_distance(settingsGroup, _metaDataMap[distanceName]),
_alpha(settingsGroup, _metaDataMap[alphaName]),
_minLength(settingsGroup, _metaDataMap[minLengthName]) {
establishConnections();
}
QString LinearGenerator::editorQML() {
return QStringLiteral("LinearGeneratorEditor.qml");
}
QString LinearGenerator::mapVisualQML() {
return QStringLiteral("LinearGeneratorMapVisual.qml");
}
QString LinearGenerator::name() { return QStringLiteral("Linear Generator"); }
QString LinearGenerator::abbreviation() { return QStringLiteral("L. Gen."); }
bool LinearGenerator::get(Generator &generator) {
if (_d) {
if (this->_d->isValid()) {
// Prepare data.
const auto &origin = this->_d->origin();
auto geoPolygon = this->_d->measurementArea().coordinateList();
for (auto &v : geoPolygon) {
if (v.isValid()) {
v.setAltitude(0);
} else {
qCWarning(LinearGeneratorLog) << "get(): measurement area invalid.";
for (const auto &w : geoPolygon) {
qCWarning(LinearGeneratorLog) << w;
}
return false;
}
}
auto pPolygon = std::make_shared<snake::FPolygon>();
snake::areaToEnu(origin, geoPolygon, *pPolygon);
// Progress and tiles.
const auto &progress = this->_d->measurementArea().progress();
const auto *tiles = this->_d->measurementArea().tiles();
auto pTiles = std::make_shared<std::vector<snake::FPolygon>>();
if (progress.size() == tiles->count()) {
for (int i = 0; i < tiles->count(); ++i) {
if (progress[i] == 100) {
const auto *tile = tiles->value<const SnakeTile *>(i);
if (tile != nullptr) {
snake::FPolygon tileENU;
snake::areaToEnu(origin, tile->coordinateList(), tileENU);
pTiles->push_back(std::move(tileENU));
} else {
qCWarning(LinearGeneratorLog)
<< "get(): progress.size() != tiles->count().";
return false;
}
}
}
} else {
qCWarning(LinearGeneratorLog)
<< "get(): progress.size() != tiles->count().";
return false;
}
auto geoDepot = this->_d->serviceArea().depot();
if (!geoDepot.isValid()) {
qCWarning(LinearGeneratorLog) << "get(): depot invalid." << geoDepot;
return false;
}
snake::FPoint depot;
snake::toENU(origin, geoDepot, depot);
// Fetch transect parameter.
auto distance =
snake::Length(this->_distance.rawValue().toDouble() * bu::si::meter);
auto minLength =
snake::Length(this->_minLength.rawValue().toDouble() * bu::si::meter);
auto alpha =
snake::Angle(this->_alpha.rawValue().toDouble() * bu::degree::degree);
generator = [depot, pPolygon, pTiles, distance, alpha,
minLength](snake::Transects &transects) -> bool {
bool value = linearTransects(*pPolygon, *pTiles, distance, alpha,
minLength, transects);
transects.insert(transects.begin(), snake::FLineString{depot});
return value;
};
return true;
} else {
qCWarning(LinearGeneratorLog) << "get(): data invalid.";
return false;
}
} else {
qCWarning(LinearGeneratorLog) << "get(): data member not set.";
return false;
}
}
Fact *LinearGenerator::distance() { return &_distance; }
Fact *LinearGenerator::alpha() { return &_alpha; }
Fact *LinearGenerator::minLength() { return &_minLength; }
void LinearGenerator::establishConnections() {
if (this->_d && !this->_connectionsEstablished) {
connect(this->_d.get(), &WimaPlanData::measurementAreaChanged, this,
&GeneratorBase::generatorChanged);
connect(this->_d.get(), &WimaPlanData::originChanged, this,
&GeneratorBase::generatorChanged);
connect(&this->_d->measurementArea(),
&WimaMeasurementAreaData::progressChanged, this,
&GeneratorBase::generatorChanged);
connect(&this->_d->measurementArea(),
&WimaMeasurementAreaData::tileDataChanged, this,
&GeneratorBase::generatorChanged);
connect(&this->_d->serviceArea(), &WimaServiceAreaData::depotChanged, this,
&GeneratorBase::generatorChanged);
connect(this->distance(), &Fact::rawValueChanged, this,
&GeneratorBase::generatorChanged);
connect(this->alpha(), &Fact::rawValueChanged, this,
&GeneratorBase::generatorChanged);
connect(this->minLength(), &Fact::rawValueChanged, this,
&GeneratorBase::generatorChanged);
this->_connectionsEstablished = true;
}
}
void LinearGenerator::deleteConnections() {
if (this->_d && this->_connectionsEstablished) {
disconnect(this->_d.get(), &WimaPlanData::measurementAreaChanged, this,
&GeneratorBase::generatorChanged);
disconnect(this->_d.get(), &WimaPlanData::originChanged, this,
&GeneratorBase::generatorChanged);
disconnect(&this->_d->measurementArea(),
&WimaMeasurementAreaData::progressChanged, this,
&GeneratorBase::generatorChanged);
disconnect(&this->_d->measurementArea(),
&WimaMeasurementAreaData::tileDataChanged, this,
&GeneratorBase::generatorChanged);
disconnect(&this->_d->serviceArea(), &WimaServiceAreaData::depotChanged,
this, &GeneratorBase::generatorChanged);
disconnect(this->distance(), &Fact::rawValueChanged, this,
&GeneratorBase::generatorChanged);
disconnect(this->alpha(), &Fact::rawValueChanged, this,
&GeneratorBase::generatorChanged);
disconnect(this->minLength(), &Fact::rawValueChanged, this,
&GeneratorBase::generatorChanged);
this->_connectionsEstablished = false;
}
}
bool linearTransects(const snake::FPolygon &polygon,
const std::vector<snake::FPolygon> &tiles,
snake::Length distance, snake::Angle angle,
snake::Length minLength, snake::Transects &transects) {
namespace tr = bg::strategy::transform;
auto s1 = std::chrono::high_resolution_clock::now();
// Check preconitions
if (polygon.outer().size() >= 3) {
// Convert to ENU system.
std::string error;
// Check validity.
if (!bg::is_valid(polygon, error)) {
std::stringstream ss;
ss << bg::wkt(polygon);
qCWarning(LinearGeneratorLog) << "linearTransects(): "
"invalid polygon. "
<< error.c_str() << ss.str().c_str();
} else {
tr::rotate_transformer<bg::degree, double, 2, 2> rotate(angle.value() *
180 / M_PI);
// Rotate polygon by angle and calculate bounding box.
snake::FPolygon polygonENURotated;
bg::transform(polygon.outer(), polygonENURotated.outer(), rotate);
snake::FBox box;
boost::geometry::envelope(polygonENURotated, box);
double x0 = box.min_corner().get<0>();
double y0 = box.min_corner().get<1>();
double x1 = box.max_corner().get<0>();
double y1 = box.max_corner().get<1>();
// Generate transects and convert them to clipper path.
size_t num_t = ceil((y1 - y0) / distance.value()); // number of transects
vector<ClipperLib::Path> transectsClipper;
transectsClipper.reserve(num_t);
for (size_t i = 0; i < num_t; ++i) {
// calculate transect
snake::FPoint v1{x0, y0 + i * distance.value()};
snake::FPoint v2{x1, y0 + i * distance.value()};
snake::FLineString transect;
transect.push_back(v1);
transect.push_back(v2);
// transform back
snake::FLineString temp_transect;
tr::rotate_transformer<bg::degree, double, 2, 2> rotate_back(
-angle.value() * 180 / M_PI);
bg::transform(transect, temp_transect, rotate_back);
// to clipper
ClipperLib::IntPoint c1{static_cast<ClipperLib::cInt>(
temp_transect[0].get<0>() * CLIPPER_SCALE),
static_cast<ClipperLib::cInt>(
temp_transect[0].get<1>() * CLIPPER_SCALE)};
ClipperLib::IntPoint c2{static_cast<ClipperLib::cInt>(
temp_transect[1].get<0>() * CLIPPER_SCALE),
static_cast<ClipperLib::cInt>(
temp_transect[1].get<1>() * CLIPPER_SCALE)};
ClipperLib::Path path{c1, c2};
transectsClipper.push_back(path);
}
if (transectsClipper.size() == 0) {
std::stringstream ss;
ss << "Not able to generate transects. Parameter: distance = "
<< distance << std::endl;
qCWarning(LinearGeneratorLog)
<< "linearTransects(): " << ss.str().c_str();
return false;
}
// Convert measurement area to clipper path.
snake::FPolygon shrinked;
snake::offsetPolygon(polygon, shrinked, -0.2);
auto &outer = shrinked.outer();
ClipperLib::Path polygonClipper;
for (auto vertex : outer) {
polygonClipper.push_back(ClipperLib::IntPoint{
static_cast<ClipperLib::cInt>(vertex.get<0>() * CLIPPER_SCALE),
static_cast<ClipperLib::cInt>(vertex.get<1>() * CLIPPER_SCALE)});
}
// Perform clipping.
// Clip transects to measurement area.
ClipperLib::Clipper clipper;
clipper.AddPath(polygonClipper, ClipperLib::ptClip, true);
clipper.AddPaths(transectsClipper, ClipperLib::ptSubject, false);
ClipperLib::PolyTree clippedTransecs;
clipper.Execute(ClipperLib::ctIntersection, clippedTransecs,
ClipperLib::pftNonZero, ClipperLib::pftNonZero);
// Subtract holes.
if (tiles.size() > 0) {
vector<ClipperLib::Path> processedTiles;
for (const auto &tile : tiles) {
ClipperLib::Path path;
for (const auto &v : tile.outer()) {
path.push_back(ClipperLib::IntPoint{
static_cast<ClipperLib::cInt>(v.get<0>() * CLIPPER_SCALE),
static_cast<ClipperLib::cInt>(v.get<1>() * CLIPPER_SCALE)});
}
processedTiles.push_back(std::move(path));
}
clipper.Clear();
for (const auto &child : clippedTransecs.Childs) {
clipper.AddPath(child->Contour, ClipperLib::ptSubject, false);
}
clipper.AddPaths(processedTiles, ClipperLib::ptClip, true);
clippedTransecs.Clear();
clipper.Execute(ClipperLib::ctDifference, clippedTransecs,
ClipperLib::pftNonZero, ClipperLib::pftNonZero);
}
// Extract transects from PolyTree and convert them to BoostLineString
for (const auto &child : clippedTransecs.Childs) {
const auto &clipperTransect = child->Contour;
snake::FPoint v1{
static_cast<double>(clipperTransect[0].X) / CLIPPER_SCALE,
static_cast<double>(clipperTransect[0].Y) / CLIPPER_SCALE};
snake::FPoint v2{
static_cast<double>(clipperTransect[1].X) / CLIPPER_SCALE,
static_cast<double>(clipperTransect[1].Y) / CLIPPER_SCALE};
snake::FLineString transect{v1, v2};
if (bg::length(transect) >= minLength.value()) {
transects.push_back(transect);
}
}
if (transects.size() == 0) {
std::stringstream ss;
ss << "Not able to generatetransects. Parameter: minLength = "
<< minLength << std::endl;
qCWarning(LinearGeneratorLog)
<< "linearTransects(): " << ss.str().c_str();
return false;
}
qCWarning(LinearGeneratorLog)
<< "linearTransects(): time: "
<< std::chrono::duration_cast<std::chrono::milliseconds>(
std::chrono::high_resolution_clock::now() - s1)
.count()
<< " ms";
return true;
}
}
return false;
}
} // namespace routing
#include "GeneratorBase.h"
#include <QGeoCoordinate>
namespace routing {
class LinearGenerator : public GeneratorBase {
Q_OBJECT
public:
LinearGenerator(QObject *parent = nullptr);
LinearGenerator(Data d, QObject *parent = nullptr);
Q_PROPERTY(Fact *distance READ distance CONSTANT)
Q_PROPERTY(Fact *alpha READ alpha CONSTANT)
Q_PROPERTY(Fact *minLength READ minLength CONSTANT)
virtual QString editorQML() override;
virtual QString mapVisualQML() override;
virtual QString name() override;
virtual QString abbreviation() override;
virtual bool get(Generator &generator) override;
Fact *distance();
Fact *alpha();
Fact *minLength();
static const char *settingsGroup;
static const char *distanceName;
static const char *alphaName;
static const char *minLengthName;
protected:
virtual void establishConnections() override;
virtual void deleteConnections() override;
private:
bool _connectionsEstablished;
QMap<QString, FactMetaData *> _metaDataMap;
SettingsFact _distance;
SettingsFact _alpha;
SettingsFact _minLength;
};
} // namespace routing
#include "StandardData.h"
StandardParameter::StandardParameter() {}
#pragma once
#include "GeneratorData.h"
namespace routing {
class StandardData : public GeneratorData {};
} // namespace routing
[
{
"name": "TransectDistance",
"shortDescription": "The distance between transects.",
"type": "double",
"units": "m",
"min": 0.3,
"decimalPlaces": 1,
"defaultValue": 5.0
},
{
"name": "DeltaAlpha",
"shortDescription": "Angle discretisation.",
"type": "double",
"units": "Deg",
"min": 0.3,
"max": 90,
"decimalPlaces": 1,
"defaultValue": 5.0
},
{
"name": "MinLength",
"shortDescription": "The minimal transect length.",
"type": "double",
"units": "m",
"min": 0.3,
"decimalPlaces": 1,
"defaultValue": 5.0
}
]
[
{
"name": "TransectDistance",
"shortDescription": "The distance between transects.",
"type": "double",
"units": "m",
"min": 0.3,
"decimalPlaces": 1,
"defaultValue": 5.0
},
{
"name": "Alpha",
"shortDescription": "Transect angle.",
"type": "double",
"units": "Deg",
"min": 0,
"max": 180,
"decimalPlaces": 1,
"defaultValue": 0.0
},
{
"name": "MinLength",
"shortDescription": "The minimal transect length.",
"type": "double",
"units": "m",
"min": 0.3,
"decimalPlaces": 1,
"defaultValue": 5.0
}
]
......@@ -230,74 +230,53 @@ void WimaController::planDataChangedHandler() {
_serviceArea = WimaServiceAreaData();
_corridor = WimaCorridorData();
_joinedArea = WimaJoinedAreaData();
_planDataValid = false;
emit visualItemsChanged();
emit missionItemsChanged();
emit waypointPathChanged();
_planDataValid = false;
// Extract areas.
auto planData = WimaBridge::instance()->planData();
// extract list with WimaAreas
QList<const WimaAreaData *> areaList = planData.areaList();
int areaCounter = 0;
const int numAreas = 4; // extract only numAreas Areas, if there are more
// they are invalid and ignored
for (int i = 0; i < areaList.size(); i++) {
const WimaAreaData *areaData = areaList[i];
if (areaData->type() ==
WimaServiceAreaData::typeString) { // is it a service area?
_serviceArea = *qobject_cast<const WimaServiceAreaData *>(areaData);
areaCounter++;
_areas.append(&_serviceArea);
continue;
}
if (areaData->type() ==
WimaMeasurementAreaData::typeString) { // is it a measurement area?
_measurementArea =
*qobject_cast<const WimaMeasurementAreaData *>(areaData);
areaCounter++;
_areas.append(&_measurementArea);
// Measurement Area.
if (planData.measurementArea().coordinateList().size() >= 3) {
_measurementArea = planData.measurementArea();
_areas.append(&_measurementArea);
continue;
}
// Service Area.
if (planData.serviceArea().coordinateList().size() >= 3) {
_serviceArea = planData.serviceArea();
_areas.append(&_serviceArea);
if (areaData->type() == WimaCorridorData::typeString) { // is it a corridor?
_corridor = *qobject_cast<const WimaCorridorData *>(areaData);
areaCounter++;
//_visualItems.append(&_corridor); // not needed
_WMSettings.setHomePosition(
QGeoCoordinate(_serviceArea.depot().latitude(),
_serviceArea.depot().longitude(), 0));
continue;
}
// Joined Area.
if (planData.joinedArea().coordinateList().size() >= 3) {
_joinedArea = planData.joinedArea();
_areas.append(&_joinedArea);
if (areaData->type() ==
WimaJoinedAreaData::typeString) { // is it a corridor?
_joinedArea = *qobject_cast<const WimaJoinedAreaData *>(areaData);
areaCounter++;
_areas.append(&_joinedArea);
_planDataValid = true;
continue;
// Corridor.
if (planData.corridor().coordinateList().size() >= 3) {
_corridor = planData.corridor();
}
}
}
if (areaCounter >= numAreas)
break;
}
if (areaCounter != numAreas) {
Q_ASSERT(false);
return;
if (_planDataValid) {
emit visualItemsChanged();
} else {
_areas.clear();
_measurementArea = WimaMeasurementAreaData();
_serviceArea = WimaServiceAreaData();
_corridor = WimaCorridorData();
_joinedArea = WimaJoinedAreaData();
}
emit visualItemsChanged();
_WMSettings.setHomePosition(QGeoCoordinate(
_serviceArea.depot().latitude(), _serviceArea.depot().longitude(), 0));
_planDataValid = true;
}
void WimaController::progressChangedHandler() {
......
#include "WimaPlanData.h"
enum Signal {
measuremtAreaChanged,
serviceAreaChanged,
joinedAreaChanged,
corridorChanged,
};
WimaPlanData::WimaPlanData(QObject *parent)
: QObject(parent), _editing(false) {}
WimaPlanData::WimaPlanData(QObject *parent) : QObject(parent) {}
WimaPlanData::WimaPlanData(const WimaPlanData &other, QObject *parent)
: QObject(parent), _editing(false) {
: QObject(parent) {
*this = other;
}
WimaPlanData &WimaPlanData::operator=(const WimaPlanData &other) {
this->_measurementArea = other._measurementArea;
this->_serviceArea = other._serviceArea;
this->_joinedArea = other._joinedArea;
this->_corridor = other._corridor;
this->set(other.measurementArea());
this->set(other.serviceArea());
this->set(other.joinedArea());
this->set(other.corridor());
return *this;
}
......@@ -27,36 +19,44 @@ WimaPlanData &WimaPlanData::operator=(const WimaPlanData &other) {
void WimaPlanData::set(const WimaJoinedAreaData &areaData) {
if (_joinedArea != areaData) {
_joinedArea = areaData;
emitJoinedAreaChanged();
emit joinedAreaChanged();
}
}
void WimaPlanData::set(const WimaServiceAreaData &areaData) {
if (_serviceArea != areaData) {
_serviceArea = areaData;
emitServiceAreaChanged();
emit serviceAreaChanged();
}
}
void WimaPlanData::set(const WimaCorridorData &areaData) {
if (_corridor != areaData) {
_corridor = areaData;
emitCorridorChanged();
emit corridorChanged();
}
}
void WimaPlanData::set(const WimaMeasurementAreaData &areaData) {
if (_measurementArea != areaData) {
_measurementArea = areaData;
emitMeasurementAreaChanged();
emit measurementAreaChanged();
if (_measurementArea.coordinateList().size() > 0) {
setOrigin(_measurementArea.coordinateList().first());
} else {
setOrigin(QGeoCoordinate());
}
}
}
void WimaPlanData::clear() {
_joinedArea = WimaJoinedAreaData();
_serviceArea = WimaServiceAreaData();
_corridor = WimaCorridorData();
_measurementArea = WimaMeasurementAreaData();
void WimaPlanData::clear() { *this = WimaPlanData(); }
QGeoCoordinate WimaPlanData::origin() { return _origin; }
bool WimaPlanData::isValid() {
return _measurementArea.coordinateList().size() >= 3 &&
_serviceArea.coordinateList().size() >= 3 && _origin.isValid();
}
const WimaJoinedAreaData &WimaPlanData::joinedArea() const {
......@@ -75,24 +75,14 @@ const WimaMeasurementAreaData &WimaPlanData::measurementArea() const {
return this->_measurementArea;
}
void WimaPlanData::startEditing() {
if (!this->_editing) {
this->_editing = true;
}
}
WimaJoinedAreaData &WimaPlanData::joinedArea() { return this->_joinedArea; }
void WimaPlanData::stopEditing() {
if (this->_editing) {
this->_editing = false;
for (auto &s : this->_queuedSignals) {
s.second();
}
this->_queuedSignals.clear();
}
}
WimaServiceAreaData &WimaPlanData::serviceArea() { return this->_serviceArea; }
WimaCorridorData &WimaPlanData::corridor() { return this->_corridor; }
WimaPlanData::Guard WimaPlanData::guard() {
return Guard(std::bind(&WimaPlanData::stopEditing, this));
WimaMeasurementAreaData &WimaPlanData::measurementArea() {
return this->_measurementArea;
}
bool WimaPlanData::operator==(const WimaPlanData &other) const {
......@@ -105,42 +95,9 @@ bool WimaPlanData::operator!=(const WimaPlanData &other) const {
return !(*this == other);
}
void WimaPlanData::emitJoinedAreaChanged() {
if (!this->_editing) {
emit joinedAreaChanged();
} else {
this->_queuedSignals.insert(
std::make_pair(Signal::joinedAreaChanged,
std::bind(&WimaPlanData::joinedAreaChanged, this)));
}
}
void WimaPlanData::emitMeasurementAreaChanged() {
if (!this->_editing) {
emit measurementAreaChanged();
} else {
this->_queuedSignals.insert(
std::make_pair(Signal::measuremtAreaChanged,
std::bind(&WimaPlanData::measurementAreaChanged, this)));
}
}
void WimaPlanData::emitServiceAreaChanged() {
if (!this->_editing) {
emit serviceAreaChanged();
} else {
this->_queuedSignals.insert(
std::make_pair(Signal::serviceAreaChanged,
std::bind(&WimaPlanData::serviceAreaChanged, this)));
}
}
void WimaPlanData::emitCorridorChanged() {
if (!this->_editing) {
emit corridorChanged();
} else {
this->_queuedSignals.insert(
std::make_pair(Signal::corridorChanged,
std::bind(&WimaPlanData::corridorChanged, this)));
void WimaPlanData::setOrigin(const QGeoCoordinate &origin) {
if (this->_origin != origin) {
this->_origin = origin;
emit originChanged();
}
}
#pragma once
#include <functional>
#include <map>
#include <QGeoCoordinate>
#include <QObject>
......@@ -16,15 +13,6 @@
class WimaPlanData : public QObject {
Q_OBJECT
public:
class Guard {
public:
Guard(std::function<void(void)> fun) : fun_(fun) {}
~Guard() { fun_(); }
private:
std::function<void(void)> fun_;
};
WimaPlanData(QObject *parent = nullptr);
WimaPlanData(const WimaPlanData &other, QObject *parent = nullptr);
WimaPlanData &operator=(const WimaPlanData &other);
......@@ -41,9 +29,13 @@ public:
const WimaCorridorData &corridor() const;
const WimaMeasurementAreaData &measurementArea() const;
void startEditing();
void stopEditing();
Guard guard();
WimaJoinedAreaData &joinedArea();
WimaServiceAreaData &serviceArea();
WimaCorridorData &corridor();
WimaMeasurementAreaData &measurementArea();
QGeoCoordinate origin();
bool isValid();
bool operator==(const WimaPlanData &other) const;
bool operator!=(const WimaPlanData &other) const;
......@@ -53,19 +45,15 @@ signals:
void serviceAreaChanged();
void corridorChanged();
void measurementAreaChanged();
void originChanged();
private:
void emitJoinedAreaChanged();
void emitServiceAreaChanged();
void emitCorridorChanged();
void emitMeasurementAreaChanged();
void setOrigin(const QGeoCoordinate &origin);
WimaJoinedAreaData _joinedArea;
WimaServiceAreaData _serviceArea;
WimaCorridorData _corridor;
WimaMeasurementAreaData _measurementArea;
std::map<int, std::function<void(void)>> _queuedSignals;
bool _editing;
QGeoCoordinate _origin;
};
......@@ -955,10 +955,10 @@ void WimaPlaner::setInteractive() {
bool WimaPlaner::toPlanData(WimaPlanData &planData) {
// store areas
planData.append(WimaMeasurementAreaData(_measurementArea));
planData.append(WimaServiceAreaData(_serviceArea));
planData.append(WimaCorridorData(_corridor));
planData.append(WimaJoinedAreaData(_joinedArea));
planData.set(WimaMeasurementAreaData(_measurementArea));
planData.set(WimaServiceAreaData(_serviceArea));
planData.set(WimaCorridorData(_corridor));
planData.set(WimaJoinedAreaData(_joinedArea));
return true;
}
......
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