diff --git a/qgroundcontrol.pro b/qgroundcontrol.pro index dfcc493f4812a70142dcc076d76b2bb6a451a4e5..808f020b8392e730eff2e6d3c2e29e931cbd425d 100644 --- a/qgroundcontrol.pro +++ b/qgroundcontrol.pro @@ -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 diff --git a/qgroundcontrol.qrc b/qgroundcontrol.qrc index 82b70a88178ee45b1f121acf5b9bf7aa1c81a27c..f9202a72bfd39f62460574022a152b5ffa323d22 100644 --- a/qgroundcontrol.qrc +++ b/qgroundcontrol.qrc @@ -287,6 +287,8 @@ src/Wima/Geometry/json/WimaArea.SettingsGroup.json src/Wima/json/WimaController.SettingsGroup.json src/Settings/Wima.SettingsGroup.json + src/Wima/Snake/json/CircularGenerator.SettingsGroup.json + src/Wima/Snake/json/LinearGenerator.SettingsGroup.json src/comm/APMArduCopterMockLink.params diff --git a/src/Wima/CircularSurvey.cc b/src/Wima/CircularSurvey.cc index 460abb69e707bbe3b8dc8bf4f5dd909e20f3839a..828d7ce429d1396e8455a7737ef6024ab0b01e27 100644 --- a/src/Wima/CircularSurvey.cc +++ b/src/Wima/CircularSurvey.cc @@ -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 CommandRAII { -public: - CommandRAII(Functor f) : fun(f) {} - ~CommandRAII() { fun(); } - -private: - Functor fun; -}; - template constexpr typename std::underlying_type::type integral(T value) { return static_cast::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); diff --git a/src/Wima/RoutingThread.cpp b/src/Wima/RoutingThread.cpp index 20f05f51619d8c6107fa5a877baa6f6ea4b8a32e..5b34bdd7e2a2f3a5e75ceffd9d3f33d4aeb6e353 100644 --- a/src/Wima/RoutingThread.cpp +++ b/src/Wima/RoutingThread.cpp @@ -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; diff --git a/src/Wima/RoutingThread.h b/src/Wima/RoutingThread.h index 46ba2eb757aff864e10ad89c1bf4452e5cdfb41b..ee8444ab7aedc7864272b90f999717450122c3e5 100644 --- a/src/Wima/RoutingThread.h +++ b/src/Wima/RoutingThread.h @@ -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; }; //! diff --git a/src/Wima/Snake/CircularGenerator.cpp b/src/Wima/Snake/CircularGenerator.cpp index 637ff4d2bbc47f77d57722ace308565b160c89d8..5e10112f9394d0185b0854c6808d461a28618978 100644 --- a/src/Wima/Snake/CircularGenerator.cpp +++ b/src/Wima/Snake/CircularGenerator.cpp @@ -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 &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::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>(); + if (progress.size() == tiles->count()) { + for (int i = 0; i < tiles->count(); ++i) { + if (progress[i] == 100) { + const auto *tile = tiles->value(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 &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; for (auto §or : sectors) { - ClipperCircle circle(rScaled, originScaled); + ClipperCircle circle(rScaled, referenceScaled); approximate(circle, nSectors, sector); rScaled += deltaRScaled; } diff --git a/src/Wima/Snake/CircularGenerator.h b/src/Wima/Snake/CircularGenerator.h index eb9e58ce0007cd94fce749084103a6aa91a6819d..57b7c5f65aa3515e342e9ce15207345fe08435ce 100644 --- a/src/Wima/Snake/CircularGenerator.h +++ b/src/Wima/Snake/CircularGenerator.h @@ -1,12 +1,20 @@ #include "GeneratorBase.h" -#include "StandardData.h" + +#include 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 _metaDataMap; + SettingsFact _distance; + SettingsFact _deltaAlpha; + SettingsFact _minLength; }; } // namespace routing diff --git a/src/Wima/Snake/GeneratorBase.cc b/src/Wima/Snake/GeneratorBase.cc index 32bcaeede1c93e4124003ca12beb7ab320372cb8..77aa3c7731c4a7a296e6673717d6d088a1d91fda 100644 --- a/src/Wima/Snake/GeneratorBase.cc +++ b/src/Wima/Snake/GeneratorBase.cc @@ -2,12 +2,26 @@ namespace routing { -GeneratorBase::GeneratorBase(QObject *parent) : QObject(parent) {} +GeneratorBase::GeneratorBase(QObject *parent) + : GeneratorBase(nullptr, parent) {} -GeneratorBase::GeneratorBase(std::shared_ptr 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 diff --git a/src/Wima/Snake/GeneratorBase.h b/src/Wima/Snake/GeneratorBase.h index 6bf36889ace3baef97846bbcc5c250fcec1dd817..c60249d055f76351df0549e18e8ad807fd60b249 100644 --- a/src/Wima/Snake/GeneratorBase.h +++ b/src/Wima/Snake/GeneratorBase.h @@ -12,9 +12,11 @@ namespace routing { class GeneratorBase : public QObject { Q_OBJECT public: + using Data = std::shared_ptr; using Generator = std::function; 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 diff --git a/src/Wima/Snake/GeneratorData.h b/src/Wima/Snake/GeneratorData.h deleted file mode 100644 index 2d67a27b946e54468ab92a9fb627e61daa817d44..0000000000000000000000000000000000000000 --- a/src/Wima/Snake/GeneratorData.h +++ /dev/null @@ -1,9 +0,0 @@ -#pragma once - -#include - -class GeneratorData : public QObject { - Q_OBJECT -public: - explicit GeneratorData(QObject *parent = nullptr); -}; diff --git a/src/Wima/Snake/LinearGenerator.cpp b/src/Wima/Snake/LinearGenerator.cpp new file mode 100644 index 0000000000000000000000000000000000000000..0a019de8dde2c9a91906c0d709cc45624e19d59d --- /dev/null +++ b/src/Wima/Snake/LinearGenerator.cpp @@ -0,0 +1,332 @@ +#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 &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::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>(); + if (progress.size() == tiles->count()) { + for (int i = 0; i < tiles->count(); ++i) { + if (progress[i] == 100) { + const auto *tile = tiles->value(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 &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 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 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 rotate_back( + -angle.value() * 180 / M_PI); + bg::transform(transect, temp_transect, rotate_back); + // to clipper + ClipperLib::IntPoint c1{static_cast( + temp_transect[0].get<0>() * CLIPPER_SCALE), + static_cast( + temp_transect[0].get<1>() * CLIPPER_SCALE)}; + ClipperLib::IntPoint c2{static_cast( + temp_transect[1].get<0>() * CLIPPER_SCALE), + static_cast( + 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(vertex.get<0>() * CLIPPER_SCALE), + static_cast(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 processedTiles; + for (const auto &tile : tiles) { + ClipperLib::Path path; + for (const auto &v : tile.outer()) { + path.push_back(ClipperLib::IntPoint{ + static_cast(v.get<0>() * CLIPPER_SCALE), + static_cast(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(clipperTransect[0].X) / CLIPPER_SCALE, + static_cast(clipperTransect[0].Y) / CLIPPER_SCALE}; + snake::FPoint v2{ + static_cast(clipperTransect[1].X) / CLIPPER_SCALE, + static_cast(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::high_resolution_clock::now() - s1) + .count() + << " ms"; + return true; + } + } + return false; +} +} // namespace routing diff --git a/src/Wima/Snake/LinearGenerator.h b/src/Wima/Snake/LinearGenerator.h new file mode 100644 index 0000000000000000000000000000000000000000..eb1c1bdcbb74d3cc803fd173706d93e595699f3d --- /dev/null +++ b/src/Wima/Snake/LinearGenerator.h @@ -0,0 +1,47 @@ +#include "GeneratorBase.h" + +#include + +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 _metaDataMap; + SettingsFact _distance; + SettingsFact _alpha; + SettingsFact _minLength; +}; + +} // namespace routing diff --git a/src/Wima/Snake/StandardData.cpp b/src/Wima/Snake/StandardData.cpp deleted file mode 100644 index 171c4faa713cd4f3a8274ea1655306800891b572..0000000000000000000000000000000000000000 --- a/src/Wima/Snake/StandardData.cpp +++ /dev/null @@ -1,3 +0,0 @@ -#include "StandardData.h" - -StandardParameter::StandardParameter() {} diff --git a/src/Wima/Snake/StandardData.h b/src/Wima/Snake/StandardData.h deleted file mode 100644 index b2075c6e0ae1d2b73c9da0dc8772992ecd48db85..0000000000000000000000000000000000000000 --- a/src/Wima/Snake/StandardData.h +++ /dev/null @@ -1,9 +0,0 @@ -#pragma once - -#include "GeneratorData.h" - -namespace routing { - -class StandardData : public GeneratorData {}; - -} // namespace routing diff --git a/src/Wima/Snake/json/CircularGenerator.SettingsGroup.json b/src/Wima/Snake/json/CircularGenerator.SettingsGroup.json new file mode 100644 index 0000000000000000000000000000000000000000..001a3df5fcfa545657a0fe98b32ce8dfc8032365 --- /dev/null +++ b/src/Wima/Snake/json/CircularGenerator.SettingsGroup.json @@ -0,0 +1,30 @@ +[ +{ + "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 +} +] diff --git a/src/Wima/Snake/json/LinearGenerator.SettingsGroup.json b/src/Wima/Snake/json/LinearGenerator.SettingsGroup.json new file mode 100644 index 0000000000000000000000000000000000000000..cc3bc7130e880d8a52eaffb91a3d289a92929476 --- /dev/null +++ b/src/Wima/Snake/json/LinearGenerator.SettingsGroup.json @@ -0,0 +1,30 @@ +[ +{ + "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 +} +] diff --git a/src/Wima/WimaController.cc b/src/Wima/WimaController.cc index 7a9aa89ffc37dc2ad13ad0dda92661838f6b9736..7c9a050b06138c90293b8c9827802221699fb58b 100644 --- a/src/Wima/WimaController.cc +++ b/src/Wima/WimaController.cc @@ -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 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(areaData); - areaCounter++; - _areas.append(&_serviceArea); - - continue; - } - if (areaData->type() == - WimaMeasurementAreaData::typeString) { // is it a measurement area? - _measurementArea = - *qobject_cast(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(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(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() { diff --git a/src/Wima/WimaPlanData.cc b/src/Wima/WimaPlanData.cc index b70c40280bcf188b00e3b4478911e72c5f568ec6..139a1d15553c2627fb3731df91410488a8d688c3 100644 --- a/src/Wima/WimaPlanData.cc +++ b/src/Wima/WimaPlanData.cc @@ -1,25 +1,17 @@ #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(); } } diff --git a/src/Wima/WimaPlanData.h b/src/Wima/WimaPlanData.h index 9fb0adc29c987986ce45756593929a7e4e36551f..bed16c9852d8ce6f0cdfa2416d385972dabd7858 100644 --- a/src/Wima/WimaPlanData.h +++ b/src/Wima/WimaPlanData.h @@ -1,8 +1,5 @@ #pragma once -#include -#include - #include #include @@ -16,15 +13,6 @@ class WimaPlanData : public QObject { Q_OBJECT public: - class Guard { - public: - Guard(std::function fun) : fun_(fun) {} - ~Guard() { fun_(); } - - private: - std::function 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> _queuedSignals; - - bool _editing; + QGeoCoordinate _origin; }; diff --git a/src/Wima/WimaPlaner.cc b/src/Wima/WimaPlaner.cc index 5e4672cfe41078fa139ca0ed97caee8cce358ed7..2be79d99706f04aeca92a9d3900e6892de954985 100644 --- a/src/Wima/WimaPlaner.cc +++ b/src/Wima/WimaPlaner.cc @@ -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; }