diff --git a/qgroundcontrol.pro b/qgroundcontrol.pro index 1976dc21c74182e5ec2c75cf3c57876ba50d05fb..11a3b2f484bc5e4713f2a8d6c5868313e005dfe1 100644 --- a/qgroundcontrol.pro +++ b/qgroundcontrol.pro @@ -34,14 +34,13 @@ DebugBuild { DEFINES += DEBUG #DEFINES += SNAKE_SHOW_TIME #DEFINES += SNAKE_DEBUG - DEFINES += SNAKE_SHOW_TIME - DEFINES += DEBUG_CIRCULAR_SURVEY + #DEFINES += SNAKE_SHOW_TIME DEFINES += ROS_BRIDGE_DEBUG } else { DESTDIR = $${OUT_PWD}/release #DEFINES += ROS_BRIDGE_DEBUG - DEFINES += SNAKE_SHOW_TIME + #DEFINES += SNAKE_SHOW_TIME #DEFINES += SNAKE_DEBUG DEFINES += NDEBUG } diff --git a/src/MeasurementComplexItem/AreaData.cc b/src/MeasurementComplexItem/AreaData.cc index e4b1d796e6c327609d047961107d2ce40b1f5359..a8579e7f1b9267ad866b39781603de1febdc13a8 100644 --- a/src/MeasurementComplexItem/AreaData.cc +++ b/src/MeasurementComplexItem/AreaData.cc @@ -18,8 +18,7 @@ AreaData::AreaData(QObject *parent) : QObject(parent) {} AreaData::~AreaData() {} -AreaData::AreaData(const AreaData &other, QObject *parent) - : QObject(parent), _showErrorMessages(true) { +AreaData::AreaData(const AreaData &other, QObject *parent) : QObject(parent) { *this = other; } @@ -93,21 +92,21 @@ const QmlObjectListModel *AreaData::areaList() const { return &_areaList; } QGeoCoordinate AreaData::origin() const { return _origin; } -bool AreaData::isCorrect() { +bool AreaData::isCorrect(bool showError) { if (!initialized()) { qCWarning(AreaDataLog) << "isCorrect(): not initialized"; return false; } // Check if areas are correct - if (!_areasCorrect()) { + if (!_areasCorrect(showError)) { return false; } // Check if areas where added. MeasurementArea *measurementArea = nullptr; SafeArea *safeArea = nullptr; - if (!_getAreas(&measurementArea, &safeArea)) { + if (!_getAreas(&measurementArea, &safeArea, showError)) { return false; } @@ -128,7 +127,8 @@ bool AreaData::isCorrect() { // qDebug() << ss.str().c_str(); if (!bg::covered_by(measurementAreaENU, safeAreaENU)) { _processError(tr("Measurement Area not inside Safe Area. Please adjust " - "the Measurement Area.\n")); + "the Measurement Area.\n"), + showError); return false; } @@ -209,11 +209,11 @@ bool AreaData::initialized() { measurementArea->count() >= 3 && safeArea->count() >= 3; } -void AreaData::intersection() { - if (initialized() && _areasCorrect()) { +void AreaData::intersection(bool showError) { + if (initialized() && _areasCorrect(showError)) { MeasurementArea *measurementArea = nullptr; SafeArea *safeArea = nullptr; - if (_getAreas(&measurementArea, &safeArea)) { + if (_getAreas(&measurementArea, &safeArea, showError)) { // convert to ENU const auto origin = this->origin(); @@ -230,7 +230,8 @@ void AreaData::intersection() { if (outputENU.size() < 1 || outputENU[0].outer().size() < 4) { _processError( "Intersection did't deliver any result. Measurement Area and " - "Safe Area must touch each other."); + "Safe Area must touch each other.", + showError); return; } @@ -238,7 +239,8 @@ void AreaData::intersection() { _processError( "Hint: Only simple polygons can be displayed. If Intersection" "produces polygons with holes or multi polygons, only " - "partial information can be displayed."); + "partial information can be displayed.", + showError); } // Shrink the result if safeAreaENU doesn't cover it. @@ -247,16 +249,18 @@ void AreaData::intersection() { while (!bg::covered_by(large, safeAreaENU)) { snake::offsetPolygon(large, small, -0.1); large = std::move(small); - qDebug() << "intersection(): shrink"; } - // Convert. - measurementArea->clear(); - for (auto it = large.outer().begin(); it != large.outer().end() - 1; - ++it) { - QGeoCoordinate c; - snake::fromENU(origin, *it, c); - measurementArea->appendVertex(c); + // Check if result is different from input. + if (!bg::equals(large, measurementAreaENU)) { + // Convert. + measurementArea->clear(); + for (auto it = large.outer().begin(); it != large.outer().end() - 1; + ++it) { + QGeoCoordinate c; + snake::fromENU(origin, *it, c); + measurementArea->appendVertex(c); + } } } } @@ -302,7 +306,7 @@ bool AreaData::load(const QJsonObject &obj, QString &errorString) { // load MeasurementArea if (jsonArea[GeoArea::areaTypeKey].toString() == - MeasurementArea::name) { + MeasurementArea::nameString) { auto area = getGeoArea(_areaList); @@ -325,7 +329,8 @@ bool AreaData::load(const QJsonObject &obj, QString &errorString) { } } // load SafeArea - else if (jsonArea[GeoArea::areaTypeKey].toString() == SafeArea::name) { + else if (jsonArea[GeoArea::areaTypeKey].toString() == + SafeArea::nameString) { auto area = getGeoArea(_areaList); if (area == nullptr) { @@ -394,7 +399,6 @@ bool AreaData::save(QJsonObject &obj) { } else { qDebug(AreaDataLog) << "save(): not able to save area: " << area->objectName(); - _processError(tr("Not able to save area: ") + area->objectName()); return false; } } @@ -411,21 +415,21 @@ void AreaData::_setOrigin(const QGeoCoordinate &origin) { } } -void AreaData::_processError(const QString &str) { +void AreaData::_processError(const QString &str, bool showError) { this->_errorString = str; emit error(); - if (_showErrorMessages) { + if (showError) { qgcApp()->informationMessageBoxOnMainThread(tr("Area Editor"), this->errorString()); } } -bool AreaData::_areasCorrect() { +bool AreaData::_areasCorrect(bool showError) { // Check if areas are correct. for (int i = 0; i < _areaList.count(); ++i) { - auto *area = _areaList.value(0); + auto *area = _areaList.value(i); if (!area->isCorrect()) { - _processError(area->errorString()); + _processError(area->errorString(), showError); return false; } } @@ -433,30 +437,25 @@ bool AreaData::_areasCorrect() { return true; } -bool AreaData::_getAreas(MeasurementArea **measurementArea, - SafeArea **safeArea) { +bool AreaData::_getAreas(MeasurementArea **measurementArea, SafeArea **safeArea, + bool showError) { *measurementArea = getGeoArea(_areaList); if (*measurementArea == nullptr) { _processError( - tr("Measurement Area missing. Please define a measurement area.")); + tr("Measurement Area missing. Please define a measurement area."), + showError); return false; } *safeArea = getGeoArea(_areaList); if (*safeArea == nullptr) { - _processError(tr("Safe Area missing. Please define a safe area.")); + _processError(tr("Safe Area missing. Please define a safe area."), + showError); return false; } return true; } -void AreaData::setShowErrorMessages(bool showErrorMessages) { - if (showErrorMessages != _showErrorMessages) { - _showErrorMessages = showErrorMessages; - emit showErrorMessagesChanged(); - } -} - void AreaData::_updateOrigin() { auto *measurementArea = getGeoArea(_areaList); if (measurementArea != nullptr) { @@ -464,6 +463,4 @@ void AreaData::_updateOrigin() { } } -bool AreaData::showErrorMessages() const { return _showErrorMessages; } - QString AreaData::errorString() const { return _errorString; } diff --git a/src/MeasurementComplexItem/AreaData.h b/src/MeasurementComplexItem/AreaData.h index 20e6ae0fc42b5c9836dc15e2a454715953741e86..f2f3ac2c2f1d3cd31b2fed4638ec5d9c97369468 100644 --- a/src/MeasurementComplexItem/AreaData.h +++ b/src/MeasurementComplexItem/AreaData.h @@ -20,8 +20,7 @@ public: AreaData &operator=(const AreaData &other); Q_PROPERTY(QmlObjectListModel *areaList READ areaList NOTIFY areaListChanged) - Q_PROPERTY(bool showErrorMessages READ showErrorMessages WRITE - setShowErrorMessages NOTIFY showErrorMessagesChanged) + Q_PROPERTY(QString errorString READ errorString NOTIFY error) // Member Methodes //! @@ -51,7 +50,7 @@ public: //! \note Origin might change if the list of areas changes. QGeoCoordinate origin() const; - Q_INVOKABLE bool isCorrect(); + Q_INVOKABLE bool isCorrect(bool showError = true); //! //! \brief initialize Initializes the areas in a valid way, such that they //! area inside the bounding box. \param bottomLeft bottom left corner of the @@ -69,7 +68,7 @@ public: //! either. //! Q_INVOKABLE bool initialized(); - Q_INVOKABLE void intersection(); + Q_INVOKABLE void intersection(bool showError = true); Q_INVOKABLE MeasurementArea *measurementArea(); Q_INVOKABLE SafeArea *safeArea(); @@ -82,25 +81,21 @@ public: QString errorString() const; // Contains a message about the last error. - bool showErrorMessages() const; - void setShowErrorMessages(bool showErrorMessages); - signals: void areaListChanged(); void originChanged(); void error(); // Emitted if errorString() contains a new message. - void showErrorMessagesChanged(); private slots: void _updateOrigin(); private: void _setOrigin(const QGeoCoordinate &origin); - void _processError(const QString &str); - bool _areasCorrect(); - bool _getAreas(MeasurementArea **measurementArea, SafeArea **safeArea); + void _processError(const QString &str, bool showError); + bool _areasCorrect(bool showError); + bool _getAreas(MeasurementArea **measurementArea, SafeArea **safeArea, + bool showError); QGeoCoordinate _origin; QmlObjectListModel _areaList; QString _errorString; - bool _showErrorMessages; }; diff --git a/src/MeasurementComplexItem/CircularGenerator.cpp b/src/MeasurementComplexItem/CircularGenerator.cpp index 8e1c526faed022014de174a564fc67b1628fb1a4..91642291519da2ab333452e64cb87e1ade99a4b2 100644 --- a/src/MeasurementComplexItem/CircularGenerator.cpp +++ b/src/MeasurementComplexItem/CircularGenerator.cpp @@ -406,6 +406,7 @@ void CircularGenerator::setMeasurementArea(MeasurementArea *area) { &CircularGenerator::resetReferenceIfInvalid); connect(_measurementArea, &MeasurementArea::pathChanged, this, &GeneratorBase::generatorChanged); + resetReferenceIfInvalid(); } emit generatorChanged(); @@ -438,16 +439,13 @@ bool circularTransects(const snake::FPoint &reference, distances.reserve(polygon.outer().size()); std::vector angles; angles.reserve(polygon.outer().size()); - //#ifdef DEBUG_CIRCULAR_SURVEY // qCDebug(CircularGeneratorLog) << "circularTransects():"; - //#endif for (const auto &p : polygon.outer()) { 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; angles.push_back(alpha); - //#ifdef DEBUG_CIRCULAR_SURVEY // qCDebug(CircularGeneratorLog) << "distances, angles, // coordinates:"; qCDebug(CircularGeneratorLog) << // to_string(distance).c_str(); qCDebug(CircularGeneratorLog) @@ -455,7 +453,6 @@ bool circularTransects(const snake::FPoint &reference, // qCDebug(CircularGeneratorLog) << "x = " << p.get<0>() << "y // = " // << p.get<1>(); - //#endif } auto rMin = deltaR; // minimal circle radius @@ -484,7 +481,6 @@ bool circularTransects(const snake::FPoint &reference, vector sectors(nTran, ClipperLib::Path()); const auto nSectors = long(std::round(((alpha2 - alpha1) / deltaAlpha).value())); - //#ifdef DEBUG_CIRCULAR_SURVEY // qCDebug(CircularGeneratorLog) << "circularTransects(): sector // parameres:"; qCDebug(CircularGeneratorLog) << "alpha1: " << // to_string(snake::Degree(alpha1)).c_str(); @@ -499,7 +495,6 @@ bool circularTransects(const snake::FPoint &reference, // qCDebug(CircularGeneratorLog) // << "rMax: " << to_string(rMax).c_str(); // qCDebug(CircularGeneratorLog) << "nTran: " << nTran; - //#endif using ClipperCircle = GenericCircle; for (auto §or : sectors) { diff --git a/src/MeasurementComplexItem/GeneratorBase.cc b/src/MeasurementComplexItem/GeneratorBase.cc index 968d08f129472560903e16592eaac0004c38aa7e..166caf44945216886d9d0e2b2b68106a3375a160 100644 --- a/src/MeasurementComplexItem/GeneratorBase.cc +++ b/src/MeasurementComplexItem/GeneratorBase.cc @@ -67,6 +67,7 @@ bool GeneratorFactory::registerGenerator(const QString &type, GeneratorFactory::Creator creator) { const auto pair = _creatorMap.insert(std::make_pair(type, creator)); auto success = pair.second; + Q_ASSERT(success); return success; } diff --git a/src/MeasurementComplexItem/MeasurementComplexItem.cc b/src/MeasurementComplexItem/MeasurementComplexItem.cc index 22cdd726323cd5fd71064dff36a91162c50981ef..723ccbf4b62845abb6da8861b8642f2f906d01af 100644 --- a/src/MeasurementComplexItem/MeasurementComplexItem.cc +++ b/src/MeasurementComplexItem/MeasurementComplexItem.cc @@ -33,12 +33,12 @@ const char *MeasurementComplexItem::jsonComplexItemTypeValue = const QString MeasurementComplexItem::name(tr("Measurement")); namespace { -const char *variantKey = "Variant"; +const char *variantIndexKey = "VariantIndex"; const char *altitudeKey = "Altitude"; const char *areaDataKey = "AreaData"; const char *variantNamesKey = "VariantNames"; -const char *generatorsKey = "Generators"; -const char *variantsKey = "Variants"; +const char *generatorArrayKey = "GeneratorArray"; +const char *variantArrayKey = "VariantArray"; const char *generatorIndexKey = "GeneratorIndex"; } // namespace @@ -51,17 +51,39 @@ MeasurementComplexItem::MeasurementComplexItem( QStringLiteral(":/json/MeasurementComplexItem.SettingsGroup.json"), this)), _altitude(settingsGroup, _metaDataMap[altitudeKey]), - _variant(settingsGroup, _metaDataMap[variantKey]), + _variantIndex(settingsGroup, _metaDataMap[variantIndexKey]), _pAreaData(new AreaData(this)), _pEditorData(new AreaData(this)), _pCurrentData(_pAreaData), _pGenerator(nullptr), _pWorker(new RoutingThread(this)) { + // Setup altitude. + _altitude.setRawValue(qgcApp() + ->toolbox() + ->settingsManager() + ->appSettings() + ->defaultMissionItemAltitude() + ->rawValue()); + connect(&_altitude, &SettingsFact::rawValueChanged, [this] { + emit this->minAMSLAltitudeChanged(this->_altitude.rawValue().toDouble()); + }); + connect(&_altitude, &SettingsFact::rawValueChanged, [this] { + emit this->maxAMSLAltitudeChanged(this->_altitude.rawValue().toDouble()); + }); + connect(&_altitude, &SettingsFact::rawValueChanged, [this] { + emit this->amslEntryAltChanged(this->_altitude.rawValue().toDouble()); + }); + connect(&_altitude, &SettingsFact::rawValueChanged, [this] { + emit this->amslExitAltChanged(this->_altitude.rawValue().toDouble()); + }); + connect(&_altitude, &SettingsFact::rawValueChanged, this, + &MeasurementComplexItem::_onAltitudeChanged); + Q_UNUSED(kmlOrShpFile) _editorQml = "qrc:/qml/MeasurementItemEditor.qml"; // Connect facts. - connect(&this->_variant, &Fact::rawValueChanged, this, - &MeasurementComplexItem::_changeVariant); + connect(&this->_variantIndex, &Fact::rawValueChanged, this, + &MeasurementComplexItem::_changeVariantIndex); // Connect worker. connect(this->_pWorker, &RoutingThread::result, this, @@ -106,10 +128,6 @@ MeasurementComplexItem::MeasurementComplexItem( resetGenerators(); startEditing(); - - qCritical() << "ToDo: _altitude connections missing."; - qCritical() << "ToDo: remove generatorNameList and use GeneratorBase::name() " - "instead."; } MeasurementComplexItem::~MeasurementComplexItem() {} @@ -173,10 +191,10 @@ bool MeasurementComplexItem::load(const QJsonObject &complexObject, setSequenceNumber(sequenceNumber); startEditing(); - // load variant - if (complexObject.contains(variantKey) && - complexObject[variantKey].isDouble()) { - _variant.setRawValue(complexObject[variantKey].toInt()); + // load variant index + if (complexObject.contains(variantIndexKey) && + complexObject[variantIndexKey].isDouble()) { + _variantIndex.setRawValue(complexObject[variantIndexKey].toInt()); } // load altitude @@ -194,14 +212,11 @@ bool MeasurementComplexItem::load(const QJsonObject &complexObject, complexObject[areaDataKey].isObject()) { QString e; if (_pCurrentData->load(complexObject[areaDataKey].toObject(), e)) { - _pCurrentData->setShowErrorMessages(false); - if (!_pCurrentData->isCorrect()) { + if (!_pCurrentData->isCorrect(false /*don't show gui message*/)) { errorString.append(_pCurrentData->errorString()); - _pCurrentData->setShowErrorMessages(true); abortEditing(); return false; } - _pCurrentData->setShowErrorMessages(true); } else { // this is critical, proceeding is not @@ -218,13 +233,13 @@ bool MeasurementComplexItem::load(const QJsonObject &complexObject, } // load Generators. - if (complexObject.contains(generatorsKey) && - complexObject[generatorsKey].isArray()) { + if (complexObject.contains(generatorArrayKey) && + complexObject[generatorArrayKey].isArray()) { QVector generatorList; QObject parent; - for (const auto valueRef : complexObject[generatorsKey].toArray()) { + for (const auto valueRef : complexObject[generatorArrayKey].toArray()) { const auto jsonGen = valueRef.toObject(); if (jsonGen.contains(routing::GeneratorBase::typeKey) && @@ -276,7 +291,7 @@ bool MeasurementComplexItem::load(const QJsonObject &complexObject, // insert generators for (const auto gen : generatorList) { gen->setParent(this); - addGenerator(gen->name(), gen); + addGenerator(gen); } } } else { @@ -303,11 +318,11 @@ bool MeasurementComplexItem::load(const QJsonObject &complexObject, // load Route Variants bool variantsSuccess = true; QVector variantVector; - if (complexObject.contains(variantsKey) && - complexObject[variantsKey].isArray()) { + if (complexObject.contains(variantArrayKey) && + complexObject[variantArrayKey].isArray()) { // load variants to variantVector for further processing. - for (const auto valueRef : complexObject[variantsKey].toArray()) { + for (const auto valueRef : complexObject[variantArrayKey].toArray()) { if (valueRef.isArray()) { const auto jsonVariant = valueRef.toArray(); Variant variant; @@ -333,6 +348,16 @@ bool MeasurementComplexItem::load(const QJsonObject &complexObject, } } + // Check if variantVector and variants are non empty + if (variantVector.size() == 0) { + variantsSuccess = false; + } + for (const auto &var : variantVector) { + if (var.size() == 0) { + variantsSuccess = false; + } + } + // Check if variants are covered by safe area. if (variantsSuccess) { auto safeArea = _pCurrentData->safeArea(); @@ -362,11 +387,8 @@ bool MeasurementComplexItem::load(const QJsonObject &complexObject, variantsSuccess = false; } - if (!variantsSuccess) { - stopEditing(); // stop editing and trigger update - } else { + if (variantsSuccess) { _variantVector.swap(variantVector); - qCritical() << "add variant names and set variant"; // load variant names bool variantNamesLoaded = true; @@ -374,7 +396,7 @@ bool MeasurementComplexItem::load(const QJsonObject &complexObject, complexObject[variantNamesKey].isArray()) { QStringList variantNames; - for (const auto &name : complexObject[variantNamesKey]) { + for (const auto &name : complexObject[variantNamesKey].toArray()) { if (name.isString()) { variantNames.append(name.toString()); } else { @@ -413,11 +435,14 @@ bool MeasurementComplexItem::load(const QJsonObject &complexObject, emit variantNamesChanged(); } - _changeVariant(); - stopEditing( false /*doUpdate*/); // does noting if editing was already stopped + + _changeVariantIndex(); + } else { + stopEditing(); // stop editing and trigger update } + return true; } @@ -460,7 +485,7 @@ void MeasurementComplexItem::save(QJsonArray &planItems) { jsonComplexItemTypeValue; // Variant and altitude. - saveObject[variantKey] = double(_variant.rawValue().toUInt()); + saveObject[variantIndexKey] = double(_variantIndex.rawValue().toUInt()); saveObject[altitudeKey] = double(_altitude.rawValue().toUInt()); // Variant names. @@ -483,17 +508,16 @@ void MeasurementComplexItem::save(QJsonArray &planItems) { QJsonArray generatorArray; for (int i = 0; i < _generatorList.size(); ++i) { auto const gen = _generatorList[i]; - auto const &name = _generatorNameList[i]; QJsonObject obj; if (!gen->save(obj)) { qCDebug(MeasurementComplexItemLog) - << "save(): not able to save generator: " << name; + << "save(): not able to save generator with name: " << gen->name(); return; } else { generatorArray.append(obj); } } - saveObject[generatorsKey] = generatorArray; + saveObject[generatorArrayKey] = generatorArray; // generator index saveObject[generatorIndexKey] = generatorIndex(); @@ -509,7 +533,7 @@ void MeasurementComplexItem::save(QJsonArray &planItems) { } variantsArray.append(variant); } - saveObject[variantsKey] = variantsArray; + saveObject[variantArrayKey] = variantsArray; planItems.append(saveObject); } else { @@ -614,7 +638,6 @@ void MeasurementComplexItem::setMissionFlightStatus( void MeasurementComplexItem::applyNewAltitude(double newAltitude) { this->_altitude.setRawValue(newAltitude); - qWarning() << "applyNewAltitude(): impl. missing."; } double MeasurementComplexItem::additionalTimeDelay() const { return 0; } @@ -678,11 +701,8 @@ bool MeasurementComplexItem::_idle(MeasurementComplexItem::STATE state) { } void MeasurementComplexItem::_updateFlightpathSegments() { - if (_cTerrainCollisionSegments != 0) { - _cTerrainCollisionSegments = 0; - emit terrainCollisionChanged(false); - qCritical() << "add alt color here..."; - } + bool hasCollisionOld = _cTerrainCollisionSegments > 0; + _cTerrainCollisionSegments = 0; _flightPathSegments.beginReset(); _flightPathSegments.clearAndDeleteContents(); @@ -708,12 +728,41 @@ void MeasurementComplexItem::_updateFlightpathSegments() { _flightPathSegments.endReset(); - if (_cTerrainCollisionSegments != 0) { - emit terrainCollisionChanged(true); + // Terrain collsision. + bool hasCollision = _cTerrainCollisionSegments > 0; + if (hasCollisionOld != hasCollision) { + emit terrainCollisionChanged(hasCollision); + } + if (_pAreaData->measurementArea() != nullptr) { + _pAreaData->measurementArea()->setShowAltColor(hasCollision); } _masterController->missionController()->recalcTerrainProfile(); } + +void MeasurementComplexItem::_onAltitudeChanged() { + // Apply altitude to variants and route. + auto alt = _altitude.rawValue().toDouble(); + for (auto &var : _variantVector) { + + Variant *pVar; + if (var.size() > 0) { + pVar = &var; + } else { + pVar = &_route; + } + + for (auto &qVariant : *pVar) { + auto vertex = qVariant.value(); + vertex.setAltitude(alt); + qVariant = QVariant::fromValue(vertex); + } + } + + if (_route.size() > 0) { + emit routeChanged(); + } +} void MeasurementComplexItem::_setAreaData( MeasurementComplexItem::PtrAreaData data) { if (_pCurrentData != data) { @@ -796,9 +845,9 @@ void MeasurementComplexItem::_updateRoute() { } } -void MeasurementComplexItem::_changeVariant() { +void MeasurementComplexItem::_changeVariantIndex() { if (idle()) { - auto variant = this->_variant.rawValue().toUInt(); + auto variant = this->_variantIndex.rawValue().toUInt(); // Find old variant. Old variant corresponts with empty list. std::size_t old_variant = std::numeric_limits::max(); @@ -828,14 +877,14 @@ void MeasurementComplexItem::_changeVariant() { << "Variant out of bounds (variant =" << variant << ")."; qCDebug(MeasurementComplexItemLog) << "Resetting variant to zero."; - disconnect(&this->_variant, &Fact::rawValueChanged, this, - &MeasurementComplexItem::_changeVariant); - this->_variant.setCookedValue(QVariant(0)); - connect(&this->_variant, &Fact::rawValueChanged, this, - &MeasurementComplexItem::_changeVariant); + disconnect(&this->_variantIndex, &Fact::rawValueChanged, this, + &MeasurementComplexItem::_changeVariantIndex); + this->_variantIndex.setCookedValue(QVariant(0)); + connect(&this->_variantIndex, &Fact::rawValueChanged, this, + &MeasurementComplexItem::_changeVariantIndex); if (this->_variantVector.size() > 0) { - this->_changeVariant(); + this->_changeVariantIndex(); } } } @@ -904,63 +953,60 @@ int MeasurementComplexItem::lastSequenceNumber() const { return _sequenceNumber + std::max(0, this->_route.size() - 1); } -bool MeasurementComplexItem::addGenerator(const QString &name, - routing::GeneratorBase *g) { - if (name.isEmpty()) { - qCDebug(MeasurementComplexItemLog) << "addGenerator(): empty name string."; - return false; - } +bool MeasurementComplexItem::addGenerator(routing::GeneratorBase *g) { - if (!g) { + if (g == nullptr) { qCDebug(MeasurementComplexItemLog) << "addGenerator(): empty generator."; + Q_ASSERT(g != nullptr); return false; } - if (this->_generatorNameList.contains(name)) { - qCDebug(MeasurementComplexItemLog) << "addGenerator(): generator " - "already registered."; - return false; - } else { - this->_generatorNameList.push_back(name); - this->_generatorList.push_back(g); - if (this->_generatorList.size() == 1) { - _setGenerator(g); + for (const auto &otherGenerator : _generatorList) { + if (otherGenerator->name() == g->name()) { + qCDebug(MeasurementComplexItemLog) + << "addGenerator(): generator with name " << g->name() + << " already added."; + Q_ASSERT(otherGenerator->name() == g->name()); + return false; } + } - emit generatorNameListChanged(); - return true; + this->_generatorList.push_back(g); + if (this->_generatorList.size() == 1) { + _setGenerator(g); } + + emit generatorListChanged(); + return true; } bool MeasurementComplexItem::removeGenerator(const QString &name) { - auto index = this->_generatorNameList.indexOf(name); - if (index >= 0) { + return removeGenerator(generatorIndex(name)); +} + +bool MeasurementComplexItem::removeGenerator(int index) { + if (index >= 0 && index < this->_generatorList.size()) { // Is this the current generator? const auto &g = this->_generatorList.at(index); if (g == this->_pGenerator) { if (index > 0) { _setGenerator(this->_generatorList.at(index - 1)); + } else if (index + 1 < _generatorList.size()) { + _setGenerator(this->_generatorList.at(index + 1)); } else { _setGenerator(nullptr); } } - this->_generatorNameList.removeAt(index); auto gen = this->_generatorList.takeAt(index); - gen->deleteLater(); - emit generatorNameListChanged(); - return true; - } else { - qCDebug(MeasurementComplexItemLog) - << "removeGenerator(): generator " << name << " not registered."; - return false; - } -} + // Should the generator be deleted? + if (gen->parent() == this || gen->parent() == nullptr) { + gen->deleteLater(); + } -bool MeasurementComplexItem::removeGenerator(int index) { - if (index >= 0 && index < this->_generatorNameList.size()) { - return removeGenerator(this->_generatorNameList.at(index)); + emit generatorListChanged(); + return true; } else { qCDebug(MeasurementComplexItemLog) << "removeGenerator(): index (" << index << ") out" @@ -971,15 +1017,7 @@ bool MeasurementComplexItem::removeGenerator(int index) { } bool MeasurementComplexItem::switchToGenerator(const QString &name) { - auto index = this->_generatorNameList.indexOf(name); - if (index >= 0) { - _setGenerator(this->_generatorList.at(index)); - return true; - } else { - qCDebug(MeasurementComplexItemLog) - << "switchToGenerator(): generator " << name << " not registered."; - return false; - } + return switchToGenerator(generatorIndex(name)); } bool MeasurementComplexItem::switchToGenerator(int index) { @@ -991,7 +1029,7 @@ bool MeasurementComplexItem::switchToGenerator(int index) { << "switchToGenerator(): index (" << index << ") out" "of bounds ( " - << this->_generatorNameList.size() << " )."; + << this->_generatorList.size() << " )."; return false; } } @@ -1000,14 +1038,29 @@ void MeasurementComplexItem::resetGenerators() { while (_generatorList.size() > 0) { removeGenerator(0); } - auto lg = new routing::LinearGenerator(this->_pAreaData, this); - addGenerator(lg->name(), lg); - auto cg = new routing::CircularGenerator(this->_pAreaData, this); - addGenerator(cg->name(), cg); + + auto lg = routing::GeneratorFactory::instance()->create( + routing::LinearGenerator::typeString, this); + lg->setData(this->_pAreaData); + addGenerator(lg); + + auto cg = routing::GeneratorFactory::instance()->create( + routing::CircularGenerator::typeString, this); + cg->setData(this->_pAreaData); + addGenerator(cg); +} + +QList +MeasurementComplexItem::generatorList() const { + return _generatorList; } QStringList MeasurementComplexItem::generatorNameList() const { - return this->_generatorNameList; + QStringList list; + for (const auto gen : _generatorList) { + list.append(gen->name()); + } + return list; } routing::GeneratorBase *MeasurementComplexItem::generator() { @@ -1039,6 +1092,18 @@ int MeasurementComplexItem::generatorIndex() const { return this->_generatorList.indexOf(this->_pGenerator); } +int MeasurementComplexItem::generatorIndex(const QString &name) { + int index = -1; + for (int i = 0; i < _generatorList.size(); ++i) { + const auto gen = _generatorList[i]; + if (gen->name() == name) { + index = i; + break; + } + } + return index; +} + void MeasurementComplexItem::startEditing() { if (!editing()) { *_pEditorData = *_pAreaData; @@ -1121,11 +1186,11 @@ void MeasurementComplexItem::_storeRoutingData( emit variantNamesChanged(); // Set variant to 0. - disconnect(&this->_variant, &Fact::rawValueChanged, this, - &MeasurementComplexItem::_changeVariant); - this->_variant.setCookedValue(QVariant(0)); - connect(&this->_variant, &Fact::rawValueChanged, this, - &MeasurementComplexItem::_changeVariant); + disconnect(&this->_variantIndex, &Fact::rawValueChanged, this, + &MeasurementComplexItem::_changeVariantIndex); + this->_variantIndex.setCookedValue(QVariant(0)); + connect(&this->_variantIndex, &Fact::rawValueChanged, this, + &MeasurementComplexItem::_changeVariantIndex); // Select first variant as route. this->_route.swap(this->_variantVector.first()); @@ -1140,7 +1205,7 @@ void MeasurementComplexItem::_storeRoutingData( } } -Fact *MeasurementComplexItem::variant() { return &_variant; } +Fact *MeasurementComplexItem::variantIndex() { return &_variantIndex; } Fact *MeasurementComplexItem::altitude() { return &this->_altitude; } diff --git a/src/MeasurementComplexItem/MeasurementComplexItem.h b/src/MeasurementComplexItem/MeasurementComplexItem.h index ae244dd272023772b93360027a7e6b6fe21173d4..0c20fd1e6dcc3e2aa42a74fe8e675b9b97d6fc3f 100644 --- a/src/MeasurementComplexItem/MeasurementComplexItem.h +++ b/src/MeasurementComplexItem/MeasurementComplexItem.h @@ -34,12 +34,12 @@ public: const QString &kmlOrShpFile, QObject *parent); ~MeasurementComplexItem(); - Q_PROPERTY(Fact *variant READ variant CONSTANT) - Q_PROPERTY(Fact *altitude READ variant CONSTANT) + Q_PROPERTY(Fact *variantIndex READ variantIndex CONSTANT) + Q_PROPERTY(Fact *altitude READ altitude CONSTANT) Q_PROPERTY( QStringList variantNames READ variantNames NOTIFY variantNamesChanged) Q_PROPERTY(QStringList generatorNameList READ generatorNameList NOTIFY - generatorNameListChanged) + generatorListChanged) Q_PROPERTY(bool calculating READ calculating NOTIFY calculatingChanged) Q_PROPERTY(bool editing READ editing NOTIFY editingChanged) Q_PROPERTY(bool idle READ idle NOTIFY idleChanged) @@ -95,18 +95,56 @@ public: virtual QString abbreviation(void) const override final; // Generator - bool addGenerator(const QString &name, routing::GeneratorBase *g); + //! + //! \brief addGenerator Adds a generator. + //! + //! Adds a generator. The generator will be added to the generatorList() and + //! it's name to the generatorNameList(). Generators are identified by index + //! or name(), hence their name must be unique. A generator can not be added + //! if name() is already inside generatorNameList(). \param g Pointer to a + //! generator. \return Returns true if the generator was addes successfully, + //! false either. + //! + bool addGenerator(routing::GeneratorBase *g); + //! + //! \brief removeGenerator Removes the generator. + //! \param name The name of the generator to be removed. + //! \return Returns true if the generator was removed successfully. + //! \note The generator will be deleted if this or nullptr is it's parent. bool removeGenerator(const QString &name); + //! + //! \brief removeGenerator Removes the generator. + //! \param index The index of the generator to be removed + //! \return Returns true if the generator was removed successfully. + //! \note See above. bool removeGenerator(int index); + Q_INVOKABLE bool switchToGenerator(const QString &name); Q_INVOKABLE bool switchToGenerator(int index); + //! + //! \brief resetGenerators Resets the generators as they where after creation + //! of this object. + //! Q_INVOKABLE void resetGenerators(); + + QList generatorList() const; QStringList generatorNameList() const; + routing::GeneratorBase *generator(); const routing::GeneratorBase *generator() const; routing::GeneratorBase *generator(int index); const routing::GeneratorBase *generator(int index) const; + //! + //! \brief generatorIndex + //! \return Returns the index of the current generator. int generatorIndex() const; + //! + //! \brief generatorIndex + //! \param name + //! \return Returns the index of the generator with the name \p name, or -1 if + //! the generator is unknown. + //! + int generatorIndex(const QString &name); // Editing. //! @@ -142,9 +180,9 @@ public: AreaData *areaData(); QVariantList route(); - Fact *variant(); - Fact *altitude(); + Fact *variantIndex(); QStringList variantNames() const; + Fact *altitude(); bool calculating() const; bool editing() const; // set to true on creation @@ -158,7 +196,7 @@ public: signals: void variantNamesChanged(); - void generatorNameListChanged(); + void generatorListChanged(); void generatorChanged(); void calculatingChanged(); @@ -173,7 +211,7 @@ private slots: // Worker functions. void _storeRoutingData(PtrRoutingData pRoute); void _updateRoute(); - void _changeVariant(); + void _changeVariantIndex(); void _reverseRoute(); private: @@ -184,6 +222,7 @@ private: static bool _editing(STATE state); static bool _idle(STATE state); void _updateFlightpathSegments(); + void _onAltitudeChanged(); // Hirarcical stuff. int _sequenceNumber; @@ -195,7 +234,7 @@ private: // Facts QMap _metaDataMap; SettingsFact _altitude; - SettingsFact _variant; + SettingsFact _variantIndex; QStringList _variantNames; // Area data @@ -205,7 +244,6 @@ private: // Generators QList _generatorList; - QStringList _generatorNameList; PtrGenerator _pGenerator; // Routing. diff --git a/src/MeasurementComplexItem/geometry/GeoArea.cc b/src/MeasurementComplexItem/geometry/GeoArea.cc index 0b4a7b7586953ee9469a143ccad75c72098b3d8a..aebdeefe9c83c69a7da67eed54c262e13eb1be24 100644 --- a/src/MeasurementComplexItem/geometry/GeoArea.cc +++ b/src/MeasurementComplexItem/geometry/GeoArea.cc @@ -9,7 +9,7 @@ QGC_LOGGING_CATEGORY(GeoAreaLog, "GeoAreaLog") -const char *GeoArea::name = "GeoArea"; +const char *GeoArea::nameString = "GeoArea"; const char *GeoArea::areaTypeKey = "AreaType"; const char *GeoArea::settingsGroup = "GeoArea"; @@ -59,7 +59,7 @@ bool GeoArea::isCorrect() { std::stringstream ss; ss << bg::wkt(polygonENU); qCWarning(GeoAreaLog) << "polygonENU: " << ss.str().c_str(); - setErrorString(tr("Area invalid. Area must be a simple polygon.")); + setErrorString(this->objectName() + tr(" must be a simple polygon.")); } } return false; @@ -81,7 +81,7 @@ bool GeoArea::covers(const QGeoCoordinate &c) { } void GeoArea::init() { - this->setObjectName(name); + this->setObjectName(nameString); // connect(this, &GeoArea::pathChanged, [this] { // if (this->objectName() != "Tile") { // qDebug() << this->objectName() << " path: " << this->path() << "\n"; diff --git a/src/MeasurementComplexItem/geometry/GeoArea.h b/src/MeasurementComplexItem/geometry/GeoArea.h index e6fa59f235700af2172201d1561d41ffabf54e99..0bd637ad8ff007de4517494b8ce8ebc24ca87159 100644 --- a/src/MeasurementComplexItem/geometry/GeoArea.h +++ b/src/MeasurementComplexItem/geometry/GeoArea.h @@ -30,12 +30,13 @@ public: //! //! \brief covers Checks if GeoArea covers c. //! \param c - //! \return Returns true if c is inside, or on the border of GeoArea. + //! \return Returns true if c is inside, or on the border of GeoArea, false + //! either. //! Q_INVOKABLE bool covers(const QGeoCoordinate &c); // static Members - static const char *name; + static const char *nameString; static const char *areaTypeKey; static const char *settingsGroup; diff --git a/src/MeasurementComplexItem/geometry/MeasurementArea.cc b/src/MeasurementComplexItem/geometry/MeasurementArea.cc index 72b223fdb9aea2e79d452124df2ff733dd299082..af45d7a10f01871e41e61fae198efc61e8400d83 100644 --- a/src/MeasurementComplexItem/geometry/MeasurementArea.cc +++ b/src/MeasurementComplexItem/geometry/MeasurementArea.cc @@ -5,14 +5,22 @@ #include +#include "JsonHelper.h" #include "QGCLoggingCategory.h" +#include + #ifndef SNAKE_MAX_TILES #define SNAKE_MAX_TILES 1000 #endif QGC_LOGGING_CATEGORY(MeasurementAreaLog, "MeasurementAreaLog") +namespace { +const char *tileCenterPointsKey = "TileCenterPoints"; +const char *tileArrayKey = "TileArray"; +} // namespace + TileData::TileData() : tiles(this) {} TileData::~TileData() { tiles.clearAndDeleteContents(); } @@ -50,6 +58,82 @@ bool TileData::operator!=(const TileData &other) const { return !this->operator==(other); } +void TileData::saveToJson(QJsonObject &json) { + // save center points + QJsonValue jsonCenterPoints; + JsonHelper::saveGeoCoordinateArray(tileCenterPoints, false, jsonCenterPoints); + json[tileCenterPointsKey] = std::move(jsonCenterPoints); + + // save tiles + QJsonArray jsonTiles; + for (int i = 0; i < tiles.count(); ++i) { + auto tile = tiles.value(i); + if (tile != nullptr) { + QJsonObject jsonTile; + tile->saveToJson(jsonTile); + jsonTiles.append(jsonTile); + } else { + qCritical() << "TileData::saveToJson(): Object other than SnakeTile " + "inside tiles (QmlObjectListModel))"; + Q_ASSERT(tile != nullptr); + } + } + json[tileArrayKey] = std::move(jsonTiles); +} + +bool TileData::loadFromJson(const QJsonObject &json, QString &errorString) { + clear(); + + // load tiles + if (json.contains(tileArrayKey) && json[tileArrayKey].isArray()) { + QString e; + for (const auto &jsonTile : json[tileArrayKey].toArray()) { + auto tile = new SnakeTile(this); + if (tile->loadFromJson(jsonTile.toObject(), e)) { + tiles.append(tile); + } else { + tile->deleteLater(); + errorString.append(e); + return false; + } + } + } else { + errorString.append(tr("Not able to load tiles.\n")); + qCWarning(MeasurementAreaLog) + << "Not able to load tiles. tileArrayKey missing or wrong type."; + if (json.contains(tileArrayKey)) { + qCWarning(MeasurementAreaLog) + << "tile array type: " << json[tileArrayKey].type(); + } + return false; + } + + // load center points + if (json.contains(tileCenterPointsKey) && + json[tileCenterPointsKey].isArray()) { + QString e; + if (!JsonHelper::loadGeoCoordinateArray(json[tileCenterPointsKey], false, + tileCenterPoints, e)) { + errorString.append(e); + errorString.append("\n"); + return false; + } + + } else { + errorString.append(tr("Not able to load center points.\n")); + qCWarning(MeasurementAreaLog) + << "Not able to load center points. tileCenterPointsKey missing or " + "wrong type."; + if (json.contains(tileCenterPointsKey)) { + qCWarning(MeasurementAreaLog) + << "center points type: " << json[tileCenterPointsKey].type(); + } + return false; + } + + return true; +} + void TileData::clear() { this->tiles.clearAndDeleteContents(); this->tileCenterPoints.clear(); @@ -68,7 +152,9 @@ const char *tileHeightKey = "TileHeight"; const char *tileWidthName = "TileWidth"; const char *minTileAreaKey = "MinTileAreaPercent"; const char *showTilesKey = "ShowTiles"; -const char *MeasurementArea::name = "Measurement Area"; +const char *progressKey = "Progress"; +const char *tileKey = "Tiles"; +const char *MeasurementArea::nameString = "Measurement Area"; MeasurementArea::MeasurementArea(QObject *parent) : GeoArea(parent), @@ -193,7 +279,20 @@ bool MeasurementArea::saveToJson(QJsonObject &json) { json[tileWidthName] = _tileWidth.rawValue().toDouble(); json[minTileAreaKey] = _minTileAreaPercent.rawValue().toDouble(); json[showTilesKey] = _showTiles.rawValue().toBool(); - json[areaTypeKey] = name; + json[areaTypeKey] = nameString; + + // save progess + QJsonArray jsonProgess; + for (const auto &p : _progress) { + jsonProgess.append(p); + } + json[progressKey] = std::move(jsonProgess); + + // save tiles + QJsonObject jsonTiles; + _tileData.saveToJson(jsonTiles); + json[tileKey] = std::move(jsonTiles); + return true; } else { qCDebug(MeasurementAreaLog) @@ -211,6 +310,7 @@ bool MeasurementArea::loadFromJson(const QJsonObject &json, disableUpdate(); bool retVal = true; + // load parameters necessary for tile calculation. if (!json.contains(tileHeightKey) || !json[tileHeightKey].isDouble()) { errorString.append(tr("Could not load tile height!\n")); retVal = false; @@ -232,15 +332,59 @@ bool MeasurementArea::loadFromJson(const QJsonObject &json, _minTileAreaPercent.setRawValue(json[minTileAreaKey].toDouble()); } - if (!json.contains(showTilesKey) || !json[showTilesKey].isBool()) { - errorString.append(tr("Could not load show tiles !\n")); - retVal = false; - } else { + // load less important parameters + if (json.contains(showTilesKey) || !json[showTilesKey].isBool()) { _showTiles.setRawValue(json[showTilesKey].toBool()); } + // load tiles and progress + bool tileError = false; + if (json.contains(tileKey) && json[tileKey].isObject()) { + QString e; + if (!_tileData.loadFromJson(json[tileKey].toObject(), e)) { + qCWarning(MeasurementAreaLog) << "TileData::loadFromJson(): " << e; + tileError = true; + } else { + progressChanged(); + } + } else { + tileError = true; + } + + bool progressError = false; + QVector prog; + if (json.contains(progressKey) && json[progressKey].isArray()) { + for (const auto &p : json[progressKey].toArray()) { + if (p.isDouble()) { + prog.append(p.toDouble()); + } else { + progressError = true; + break; + } + } + + // check if entries are in range + if (!progressError) { + for (const auto &p : prog) { + if (p < 0 || p > 100) { + progressError = true; + break; + } + } + } + } else { + progressError = true; + } + if (!progressError) { + _progress.swap(prog); + progressChanged(); + } + + // do update if error occurred. enableUpdate(); - doUpdate(); + if (progressError || tileError) { + doUpdate(); + } return retVal; } else { @@ -408,7 +552,7 @@ void MeasurementArea::enableUpdate() { } void MeasurementArea::init() { - this->setObjectName(name); + this->setObjectName(nameString); connect(&this->_tileHeight, &Fact::rawValueChanged, this, &MeasurementArea::deferUpdate); connect(&this->_tileWidth, &Fact::rawValueChanged, this, diff --git a/src/MeasurementComplexItem/geometry/MeasurementArea.h b/src/MeasurementComplexItem/geometry/MeasurementArea.h index 45408d68a6182140329ae743e6990a59d41670fc..ac0a3ad8ed30e644e332a5612466d08714f48491 100644 --- a/src/MeasurementComplexItem/geometry/MeasurementArea.h +++ b/src/MeasurementComplexItem/geometry/MeasurementArea.h @@ -21,6 +21,9 @@ public: bool operator==(const TileData &other) const; bool operator!=(const TileData &other) const; + void saveToJson(QJsonObject &json); + bool loadFromJson(const QJsonObject &json, QString &errorString); + void clear(); std::size_t size() const; }; @@ -68,7 +71,7 @@ public: // Static Variables static const char *settingsGroup; - static const char *name; + static const char *nameString; signals: void tilesChanged(); diff --git a/src/MeasurementComplexItem/geometry/SafeArea.cc b/src/MeasurementComplexItem/geometry/SafeArea.cc index 4bb71f5e506c12ef50f52bd17d247fe2dab3a53f..92a04b4235cf3ed3c266e75d5e01d4f454dd64d6 100644 --- a/src/MeasurementComplexItem/geometry/SafeArea.cc +++ b/src/MeasurementComplexItem/geometry/SafeArea.cc @@ -8,7 +8,7 @@ QGC_LOGGING_CATEGORY(SafeAreaLog, "SafeAreaLog") -const char *SafeArea::name = "Safe Area"; +const char *SafeArea::nameString = "Safe Area"; const char *depotKey = "Depot Point"; SafeArea::SafeArea(QObject *parent) : GeoArea(parent) { init(); } @@ -84,7 +84,7 @@ bool SafeArea::setDepot(const QGeoCoordinate &newDepot) { bool SafeArea::saveToJson(QJsonObject &json) { if (this->GeoArea::saveToJson(json)) { - json[areaTypeKey] = name; + json[areaTypeKey] = nameString; QJsonValue jsonDepot; JsonHelper::saveGeoCoordinate(_depot, false, jsonDepot); @@ -135,6 +135,6 @@ bool SafeArea::isCorrect() { } void SafeArea::init() { - this->setObjectName(name); + this->setObjectName(nameString); connect(this, &GeoArea::pathChanged, this, &SafeArea::putDepotInside); } diff --git a/src/MeasurementComplexItem/geometry/SafeArea.h b/src/MeasurementComplexItem/geometry/SafeArea.h index 7d4a8750defb85034a15baeac8856fb9bf9b7c2f..055fc45d09dec24eaa281bea4c088621bd9d0268 100644 --- a/src/MeasurementComplexItem/geometry/SafeArea.h +++ b/src/MeasurementComplexItem/geometry/SafeArea.h @@ -27,7 +27,7 @@ public: QGeoCoordinate depotQml(void) const; // static Members - static const char *name; + static const char *nameString; signals: void depotChanged(void); diff --git a/src/MeasurementComplexItem/json/MeasurementComplexItem.SettingsGroup.json b/src/MeasurementComplexItem/json/MeasurementComplexItem.SettingsGroup.json index 5f18481ea099e59e009f25c2881d0eeb8fc4d5cf..924f16581dd5ebbc327ce54426d427ac64c53d9a 100644 --- a/src/MeasurementComplexItem/json/MeasurementComplexItem.SettingsGroup.json +++ b/src/MeasurementComplexItem/json/MeasurementComplexItem.SettingsGroup.json @@ -4,14 +4,14 @@ "QGC.MetaData.Facts": [ { - "name": "Variant", - "shrotDesc": "Route variant.", + "name": "VariantIndex", + "shrotDesc": "Route variant index.", "type": "uint64", "default": 0 }, { "name": "Altitude", - "shrotDesc": "Altitude", + "shrotDesc": "Altitude.", "type": "double", "units": "m", "min": 1, diff --git a/src/MeasurementComplexItem/qml/AreaDataEditor.qml b/src/MeasurementComplexItem/qml/AreaDataEditor.qml index 52c43cd306db88d335fad1a9085d9819d828e437..28562732558492db678d6cdd7064c4b505c2771d 100644 --- a/src/MeasurementComplexItem/qml/AreaDataEditor.qml +++ b/src/MeasurementComplexItem/qml/AreaDataEditor.qml @@ -4,29 +4,56 @@ import QtQuick.Layouts 1.11 import QtQuick.Controls 1.4 import QGroundControl.Controls 1.0 import QGroundControl.FactControls 1.0 -import QGroundControl.ScreenTools 1.0 +import QGroundControl.ScreenTools 1.0 GridLayout { - id:_root + id: _root property bool checked: true property var missionItem: undefined property int availableWidth: 300 + property bool areasCorrect: false + property string errorString: "" + + signal abort property var _areaData: missionItem.areaData - property real _margin: ScreenTools.defaultFontPixelWidth / 2 + property real _margin: ScreenTools.defaultFontPixelWidth / 2 - columnSpacing: _margin - rowSpacing: _margin - columns: 2 + columnSpacing: _margin + rowSpacing: _margin + columns: 2 Component.onCompleted: { - console.assert(missionItem !== undefined, "please set the missionItem property") + console.assert(missionItem !== undefined, + "please set the missionItem property") + if (checked) { + areasCorrectTimer.start() + } } - ExclusiveGroup{id:areaGroup} + onCheckedChanged: { + if (checked) { + areasCorrectTimer.start() + } else { + areasCorrectTimer.stop() + } + } - Repeater{ + ExclusiveGroup { + id: areaGroup + } + + QGCLabel { + id: tipLabel + wrapMode: Text.WordWrap + horizontalAlignment: Text.AlignHCenter + text: qsTr("Use the Area Editor to modify areas.") + Layout.fillWidth: true + Layout.columnSpan: parent.columns + } + + Repeater { id: areaSelector property int selectedIndex: -1 @@ -39,36 +66,37 @@ GridLayout { Layout.columnSpan: 2 onCheckedChanged: { - if (checked){ + if (checked) { areaSelector.selectedIndex = index } } Component.onCompleted: { - if (index === 0){ + if (index === 0) { checked = true } - object.interactive = Qt.binding(function(){return checked && _root.checked}) + object.interactive = Qt.binding(function () { + return checked && _root.checked + }) } - } } // area Repeater ColumnLayout { - id:editorParent + id: editorParent Layout.fillWidth: true Layout.maximumWidth: parent.width Layout.columnSpan: 2 } - Repeater{ - id:areaEditorRepeater + Repeater { + id: areaEditorRepeater Layout.maximumWidth: parent.width model: _missionItem.areaData.areaList - delegate: Item{ - id:editor + delegate: Item { + id: editor visible: index == areaSelector.selectedIndex property var _visualItem: undefined @@ -78,12 +106,19 @@ GridLayout { if (geoArea.editorQML && !_visualItem) { var component = Qt.createComponent(geoArea.editorQML) if (component.status === Component.Error) { - console.log("Error loading Qml: ", geoArea.editorQML, component.errorString()) + console.log("Error loading Qml: ", geoArea.editorQML, + component.errorString()) } else { _visualItem = component.createObject(editorParent, { - geoArea: editor.geoArea, - visible: Qt.binding(function(){return editor.visible}), - availableWidth: Qt.binding(function(){return editorParent.width})}) + "geoArea": editor.geoArea, + "visible": Qt.binding(function () { + return editor.visible + }), + "availableWidth": Qt.binding( + function () { + return editorParent.width + }) + }) } } } @@ -96,23 +131,66 @@ GridLayout { } // editor } // areaEditorRepeater + SectionHeader { + id: commandsHeader + Layout.fillWidth: true + Layout.columnSpan: parent.columns + text: qsTr("Commands and Errors") + } - QGCButton{ - text: "Check Rules" - enabled: _root.checked + GridLayout { + columnSpacing: _margin + rowSpacing: _margin + columns: 2 + Layout.columnSpan: 2 Layout.fillWidth: true - onClicked: { - _areaData.isCorrect() + visible: commandsHeader.checked + + QGCLabel { + text: qsTr("Message: ") + _root.errorString + wrapMode: Text.WordWrap + horizontalAlignment: Text.AlignHCenter + color: "orange" + Layout.columnSpan: parent.columns + Layout.fillWidth: true + visible: !_root.areasCorrect + } + + QGCButton { + text: "Intersection" + enabled: _root.checked + Layout.fillWidth: true + Layout.columnSpan: parent.columns + onClicked: { + _areaData.intersection() + } + } + + QGCButton { + text: "Reset" + onClicked: { + _root.abort() + } + Layout.fillWidth: true + Layout.columnSpan: parent.columns } } - QGCButton{ - text: "Intersection" - enabled: _root.checked - Layout.fillWidth: true - onClicked: { - _areaData.intersection() + Timer { + id: areasCorrectTimer + running: false + interval: 100 + repeat: true + + onTriggered: { + _root.areasCorrect = _missionItem.areaData.isCorrect( + false /*show gui message*/ + ) + if (!_root.areasCorrect) { + _root.errorString = _missionItem.areaData.errorString + } else { + _root.errorString = "" + } } } } - diff --git a/src/MeasurementComplexItem/qml/CircularGeneratorEditor.qml b/src/MeasurementComplexItem/qml/CircularGeneratorEditor.qml index 4e5a808a54b95c3b32820b2e85ad702781509ceb..ddcc8cc7efb3d64e49e276d12b8ea15b1dbed81f 100644 --- a/src/MeasurementComplexItem/qml/CircularGeneratorEditor.qml +++ b/src/MeasurementComplexItem/qml/CircularGeneratorEditor.qml @@ -41,7 +41,7 @@ GridLayout { } QGCButton { - text: qsTr("Reset Ref.") + text: qsTr("Reset Reference") onClicked: generator.resetReference(); Layout.fillWidth: true Layout.columnSpan: 2 diff --git a/src/MeasurementComplexItem/qml/MeasurementItemEditor.qml b/src/MeasurementComplexItem/qml/MeasurementItemEditor.qml index b9e978ad1407902804b4b0c0f5184e3b114f8add..8b84e6f1480c7919afc44af6433b4a5c1374d955 100644 --- a/src/MeasurementComplexItem/qml/MeasurementItemEditor.qml +++ b/src/MeasurementComplexItem/qml/MeasurementItemEditor.qml @@ -1,113 +1,129 @@ -import QtQuick 2.3 +import QtQuick 2.3 import QtQuick.Controls 1.2 import QtQuick.Controls.Styles 1.4 -import QtQuick.Dialogs 1.2 -import QtQuick.Extras 1.4 -import QtQuick.Layouts 1.2 - -import QGroundControl 1.0 -import QGroundControl.ScreenTools 1.0 -import QGroundControl.Vehicle 1.0 -import QGroundControl.Controls 1.0 -import QGroundControl.FactSystem 1.0 -import QGroundControl.FactControls 1.0 -import QGroundControl.Palette 1.0 -import QGroundControl.FlightMap 1.0 +import QtQuick.Dialogs 1.2 +import QtQuick.Extras 1.4 +import QtQuick.Layouts 1.3 + +import QGroundControl 1.0 +import QGroundControl.ScreenTools 1.0 +import QGroundControl.Vehicle 1.0 +import QGroundControl.Controls 1.0 +import QGroundControl.FactSystem 1.0 +import QGroundControl.FactControls 1.0 +import QGroundControl.Palette 1.0 +import QGroundControl.FlightMap 1.0 import MeasurementComplexItem 1.0 as MCI Rectangle { - id: _root - height: visible ? (mainColumn.height + (_margin * 2)) : 0 - width: availableWidth - color: qgcPal.windowShadeDark - radius: _radius + id: _root + height: visible ? (mainColumn.height + (_margin * 2)) : 0 + width: availableWidth + color: qgcPal.windowShadeDark + radius: _radius // The following properties must be available up the hierarchy chain //property real availableWidth ///< Width for control //property var missionItem ///< Mission Item for editor - - property real _margin: ScreenTools.defaultFontPixelWidth / 2 - property real _fieldWidth: ScreenTools.defaultFontPixelWidth * 10.5 - property var _vehicle: QGroundControl.multiVehicleManager.activeVehicle ? - QGroundControl.multiVehicleManager.activeVehicle : - QGroundControl.multiVehicleManager.offlineEditingVehicle + property real _margin: ScreenTools.defaultFontPixelWidth / 2 + property real _fieldWidth: ScreenTools.defaultFontPixelWidth * 10.5 + property var _vehicle: QGroundControl.multiVehicleManager.activeVehicle ? QGroundControl.multiVehicleManager.activeVehicle : QGroundControl.multiVehicleManager.offlineEditingVehicle property var _missionItem: missionItem property var _areaData: missionItem.areaData - QGCPalette { id: qgcPal; colorGroupEnabled: true } - - ColumnLayout { // main Column - id: mainColumn - anchors.margins: _margin - anchors.top: parent.top - anchors.left: parent.left - anchors.right: parent.right - - - QGCLabel { - id: tipLabel - Layout.fillWidth: true - wrapMode: Text.WordWrap - horizontalAlignment: Text.AlignHCenter - text: qsTr("Use the Area Editor to modify areas.") - visible: areaDataEditor.visible - } + QGCPalette { + id: qgcPal + colorGroupEnabled: true + } + + Column { + // main Column + id: mainColumn + anchors.margins: _margin + anchors.top: parent.top + anchors.left: parent.left + anchors.right: parent.right + + QGCTabBar { + id: tabBar + anchors.left: parent.left + anchors.right: parent.right + + enabled: !editing || editing && correct + + readonly property int areaEditorIndex: 0 + readonly property int parameterEditorIndex: 1 + readonly property int nemoControlsIndex: 2 + property bool editing: _missionItem.editing + property bool correct: false + + Component.onCompleted: currentIndex = editing ? areaEditorIndex : parameterEditorIndex + + QGCTabButton { + icon.source: "/qmlimages/PatternCamera.png" + icon.height: ScreenTools.defaultFontPixelHeight + } + QGCTabButton { + icon.source: "/qmlimages/PatternGrid.png" + icon.height: ScreenTools.defaultFontPixelHeight + } + QGCTabButton { + icon.source: "/qmlimages/PatternPresets.png" + icon.height: ScreenTools.defaultFontPixelHeight + } - GridLayout { - id: editorSelector - Layout.fillWidth: true - columnSpacing: _margin - rowSpacing: _margin - - QGCButton{ - text: "Open Area Editor" - visible: parameterEditor.visible - Layout.fillWidth: true - Layout.columnSpan: 2 - onClicked:{ - _missionItem.startEditing() + onEditingChanged: { + if (editing) { + areasCorrectTimer.start() + } else { + areasCorrectTimer.stop() } } - QGCButton{ - text: "Done" - Layout.fillWidth: true - visible: areaDataEditor.visible - onClicked: { - if (_areaData.isCorrect()){ - _missionItem.stopEditing() - } + onCurrentIndexChanged: { + if (currentIndex === areaEditorIndex) { + _missionItem.startEditing() + } else { + _missionItem.stopEditing() } } - QGCButton{ - text: "Abort" - visible: areaDataEditor.visible - Layout.fillWidth: true - onClicked:{ - missionItem.abortEditing() + Timer { + id: areasCorrectTimer + running: false + interval: 100 + repeat: true + + onTriggered: { + tabBar.correct = _missionItem.areaData.isCorrect( + false /*show gui message*/ + ) } } + } - } // editorSelector - - MCI.ParameterEditor{ - id:parameterEditor + StackLayout { + width: parent.width + currentIndex: tabBar.currentIndex - missionItem: _root._missionItem - availableWidth: mainColumn.width - checked: !_missionItem.editing - visible: checked - } + MCI.AreaDataEditor { + id: areaEditor + checked: visible + missionItem: _root._missionItem + availableWidth: parent.width + onAbort: { + missionItem.abortEditing() + tabBar.currentIndex = tabBar.parameterEditorIndex + } + } - MCI.AreaDataEditor{ - id:areaDataEditor + MCI.ParameterEditor { + id: parameterEditor - missionItem: _root._missionItem - availableWidth: mainColumn.width - checked: _missionItem.editing - visible: checked + checked: visible + missionItem: _root._missionItem + availableWidth: mainColumn.width + } } - } // main Column } // Rectangle diff --git a/src/MeasurementComplexItem/qml/ParameterEditor.qml b/src/MeasurementComplexItem/qml/ParameterEditor.qml index f322fcf69e1c03826c5ecff503f96388af06dc78..1f8a7ab37c58e079fdc81b0b6f048bd8d349a17a 100644 --- a/src/MeasurementComplexItem/qml/ParameterEditor.qml +++ b/src/MeasurementComplexItem/qml/ParameterEditor.qml @@ -85,6 +85,7 @@ GridLayout { GridLayout{ Layout.columnSpan: parent.columns Layout.maximumWidth: parent.width + columns: 3 columnSpacing: _margin rowSpacing: _margin @@ -99,6 +100,7 @@ GridLayout { delegate: QGCRadioButton { checked: index === variantIndex text: variantRepeater.names[index] ? variantRepeater.names[index]: "" + Layout.fillWidth: true property int variantIndex: missionItem.variantIndex.value