From d44fdf483b3c46645a2ad4a3c30c4a27529c2462 Mon Sep 17 00:00:00 2001 From: DonLakeFlyer Date: Sat, 3 Feb 2018 21:12:38 -0800 Subject: [PATCH] New TransectStyleComplexItem base class --- qgroundcontrol.pro | 2 + qgroundcontrol.qrc | 1 + src/MissionManager/CameraCalc.cc | 44 ++- src/MissionManager/CameraCalc.h | 49 +-- .../CorridorScan.SettingsGroup.json | 4 +- src/MissionManager/CorridorScanComplexItem.cc | 331 +++++++++--------- src/MissionManager/CorridorScanComplexItem.h | 117 ++----- .../CorridorScanComplexItemTest.cc | 2 +- .../TransectStyle.SettingsGroup.json | 38 ++ .../TransectStyleComplexItem.cc | 204 +++++++++++ src/MissionManager/TransectStyleComplexItem.h | 153 ++++++++ src/PlanView/CorridorScanEditor.qml | 33 ++ src/PlanView/CorridorScanMapVisual.qml | 2 +- 13 files changed, 694 insertions(+), 286 deletions(-) create mode 100644 src/MissionManager/TransectStyle.SettingsGroup.json create mode 100644 src/MissionManager/TransectStyleComplexItem.cc create mode 100644 src/MissionManager/TransectStyleComplexItem.h diff --git a/qgroundcontrol.pro b/qgroundcontrol.pro index d50187981..43bb37c29 100644 --- a/qgroundcontrol.pro +++ b/qgroundcontrol.pro @@ -540,6 +540,7 @@ HEADERS += \ src/MissionManager/SpeedSection.h \ src/MissionManager/StructureScanComplexItem.h \ src/MissionManager/SurveyMissionItem.h \ + src/MissionManager/TransectStyleComplexItem.h \ src/MissionManager/VisualMissionItem.h \ src/PositionManager/PositionManager.h \ src/PositionManager/SimulatedPosition.h \ @@ -733,6 +734,7 @@ SOURCES += \ src/MissionManager/SpeedSection.cc \ src/MissionManager/StructureScanComplexItem.cc \ src/MissionManager/SurveyMissionItem.cc \ + src/MissionManager/TransectStyleComplexItem.cc \ src/MissionManager/VisualMissionItem.cc \ src/PositionManager/PositionManager.cpp \ src/PositionManager/SimulatedPosition.cc \ diff --git a/qgroundcontrol.qrc b/qgroundcontrol.qrc index 0a01d0ca9..f9679f4f4 100644 --- a/qgroundcontrol.qrc +++ b/qgroundcontrol.qrc @@ -204,6 +204,7 @@ src/Settings/Guided.SettingsGroup.json src/MissionManager/QGCMapCircle.Facts.json src/Settings/RTK.SettingsGroup.json + src/MissionManager/TransectStyle.SettingsGroup.json src/MissionManager/Survey.SettingsGroup.json src/MissionManager/CorridorScan.SettingsGroup.json src/MissionManager/StructureScan.SettingsGroup.json diff --git a/src/MissionManager/CameraCalc.cc b/src/MissionManager/CameraCalc.cc index ef08fd61a..f7a06e6cf 100644 --- a/src/MissionManager/CameraCalc.cc +++ b/src/MissionManager/CameraCalc.cc @@ -14,15 +14,16 @@ #include -const char* CameraCalc::_valueSetIsDistanceName = "ValueSetIsDistance"; -const char* CameraCalc::_distanceToSurfaceName = "DistanceToSurface"; -const char* CameraCalc::_imageDensityName = "ImageDensity"; -const char* CameraCalc::_frontalOverlapName = "FrontalOverlap"; -const char* CameraCalc::_sideOverlapName = "SideOverlap"; -const char* CameraCalc::_adjustedFootprintFrontalName = "AdjustedFootprintFrontal"; -const char* CameraCalc::_adjustedFootprintSideName = "AdjustedFootprintSide"; -const char* CameraCalc::_jsonCameraNameKey = "CameraName"; -const char* CameraCalc::_jsonCameraSpecTypeKey = "CameraSpecType"; +const char* CameraCalc::_valueSetIsDistanceName = "ValueSetIsDistance"; +const char* CameraCalc::_distanceToSurfaceName = "DistanceToSurface"; +const char* CameraCalc::_distanceToSurfaceRelativeName = "DistanceToSurfaceRelative"; +const char* CameraCalc::_imageDensityName = "ImageDensity"; +const char* CameraCalc::_frontalOverlapName = "FrontalOverlap"; +const char* CameraCalc::_sideOverlapName = "SideOverlap"; +const char* CameraCalc::_adjustedFootprintFrontalName = "AdjustedFootprintFrontal"; +const char* CameraCalc::_adjustedFootprintSideName = "AdjustedFootprintSide"; +const char* CameraCalc::_jsonCameraNameKey = "CameraName"; +const char* CameraCalc::_jsonCameraSpecTypeKey = "CameraSpecType"; CameraCalc::CameraCalc(Vehicle* vehicle, QObject* parent) : CameraSpec (parent) @@ -30,6 +31,7 @@ CameraCalc::CameraCalc(Vehicle* vehicle, QObject* parent) , _dirty (false) , _cameraName (manualCameraName()) , _disableRecalc (false) + , _distanceToSurfaceRelative (true) , _metaDataMap (FactMetaData::createMapFromJsonFile(QStringLiteral(":/json/CameraCalc.FactMetaData.json"), this)) , _valueSetIsDistanceFact (0, _valueSetIsDistanceName, FactMetaData::valueTypeBool) , _distanceToSurfaceFact (0, _distanceToSurfaceName, FactMetaData::valueTypeDouble) @@ -53,6 +55,7 @@ CameraCalc::CameraCalc(Vehicle* vehicle, QObject* parent) _adjustedFootprintFrontalFact.setMetaData (_metaDataMap[_adjustedFootprintFrontalName], true); connect(this, &CameraCalc::cameraNameChanged, this, &CameraCalc::_recalcTriggerDistance); + connect(this, &CameraCalc::cameraNameChanged, this, &CameraCalc::_adjustDistanceToSurfaceRelative); connect(&_distanceToSurfaceFact, &Fact::rawValueChanged, this, &CameraCalc::_recalcTriggerDistance); connect(&_imageDensityFact, &Fact::rawValueChanged, this, &CameraCalc::_recalcTriggerDistance); @@ -167,8 +170,9 @@ void CameraCalc::save(QJsonObject& json) const json[JsonHelper::jsonVersionKey] = 1; json[_adjustedFootprintSideName] = _adjustedFootprintSideFact.rawValue().toDouble(); json[_adjustedFootprintFrontalName] = _adjustedFootprintFrontalFact.rawValue().toDouble(); - json[_distanceToSurfaceName] = _distanceToSurfaceFact.rawValue().toDouble(); - json[_jsonCameraNameKey] = _cameraName; + json[_distanceToSurfaceName] = _distanceToSurfaceFact.rawValue().toDouble(); + json[_distanceToSurfaceRelativeName] = _distanceToSurfaceRelative; + json[_jsonCameraNameKey] = _cameraName; if (_cameraName != manualCameraName()) { CameraSpec::save(json); @@ -209,6 +213,7 @@ bool CameraCalc::load(const QJsonObject& json, QString& errorString) { _adjustedFootprintSideName, QJsonValue::Double, true }, { _adjustedFootprintFrontalName, QJsonValue::Double, true }, { _distanceToSurfaceName, QJsonValue::Double, true }, + { _distanceToSurfaceRelativeName, QJsonValue::Bool, true }, }; if (!JsonHelper::validateKeys(v1Json, keyInfoList1, errorString)) { return false; @@ -221,6 +226,8 @@ bool CameraCalc::load(const QJsonObject& json, QString& errorString) _adjustedFootprintFrontalFact.setRawValue (v1Json[_adjustedFootprintFrontalName].toDouble()); _distanceToSurfaceFact.setRawValue (v1Json[_distanceToSurfaceName].toDouble()); + _distanceToSurfaceRelative = v1Json[_distanceToSurfaceRelativeName].toBool(); + if (_cameraName != manualCameraName()) { QList keyInfoList2 = { { _valueSetIsDistanceName, QJsonValue::Bool, true }, @@ -257,3 +264,18 @@ QString CameraCalc::manualCameraName(void) { return tr("Manual (no camera specs)"); } + +void CameraCalc::_adjustDistanceToSurfaceRelative(void) +{ + if (!isManualCamera()) { + setDistanceToSurfaceRelative(true); + } +} + +void CameraCalc::setDistanceToSurfaceRelative(bool distanceToSurfaceRelative) +{ + if (distanceToSurfaceRelative != _distanceToSurfaceRelative) { + _distanceToSurfaceRelative = distanceToSurfaceRelative; + emit distanceToSurfaceRelativeChanged(distanceToSurfaceRelative); + } +} diff --git a/src/MissionManager/CameraCalc.h b/src/MissionManager/CameraCalc.h index 2445975c6..1786fcf6b 100644 --- a/src/MissionManager/CameraCalc.h +++ b/src/MissionManager/CameraCalc.h @@ -20,17 +20,18 @@ class CameraCalc : public CameraSpec public: CameraCalc(Vehicle* vehicle, QObject* parent = NULL); - Q_PROPERTY(QString cameraName READ cameraName WRITE setCameraName NOTIFY cameraNameChanged) - Q_PROPERTY(QString customCameraName READ customCameraName CONSTANT) ///< Camera name for custom camera setting - Q_PROPERTY(QString manualCameraName READ manualCameraName CONSTANT) ///< Camera name for manual camera setting - Q_PROPERTY(bool isManualCamera READ isManualCamera NOTIFY cameraNameChanged) ///< true: using manual camera - Q_PROPERTY(Fact* valueSetIsDistance READ valueSetIsDistance CONSTANT) ///< true: distance specified, resolution calculated - Q_PROPERTY(Fact* distanceToSurface READ distanceToSurface CONSTANT) ///< Distance to surface for image foot print calculation - Q_PROPERTY(Fact* imageDensity READ imageDensity CONSTANT) ///< Image density on surface (cm/px) - Q_PROPERTY(Fact* frontalOverlap READ frontalOverlap CONSTANT) - Q_PROPERTY(Fact* sideOverlap READ sideOverlap CONSTANT) - Q_PROPERTY(Fact* adjustedFootprintSide READ adjustedFootprintSide CONSTANT) ///< Side footprint adjusted down for overlap - Q_PROPERTY(Fact* adjustedFootprintFrontal READ adjustedFootprintFrontal CONSTANT) ///< Frontal footprint adjusted down for overlap + Q_PROPERTY(QString cameraName READ cameraName WRITE setCameraName NOTIFY cameraNameChanged) + Q_PROPERTY(QString customCameraName READ customCameraName CONSTANT) ///< Camera name for custom camera setting + Q_PROPERTY(QString manualCameraName READ manualCameraName CONSTANT) ///< Camera name for manual camera setting + Q_PROPERTY(bool isManualCamera READ isManualCamera NOTIFY cameraNameChanged) ///< true: using manual camera + Q_PROPERTY(Fact* valueSetIsDistance READ valueSetIsDistance CONSTANT) ///< true: distance specified, resolution calculated + Q_PROPERTY(Fact* distanceToSurface READ distanceToSurface CONSTANT) ///< Distance to surface for image foot print calculation + Q_PROPERTY(Fact* imageDensity READ imageDensity CONSTANT) ///< Image density on surface (cm/px) + Q_PROPERTY(Fact* frontalOverlap READ frontalOverlap CONSTANT) + Q_PROPERTY(Fact* sideOverlap READ sideOverlap CONSTANT) + Q_PROPERTY(Fact* adjustedFootprintSide READ adjustedFootprintSide CONSTANT) ///< Side footprint adjusted down for overlap + Q_PROPERTY(Fact* adjustedFootprintFrontal READ adjustedFootprintFrontal CONSTANT) ///< Frontal footprint adjusted down for overlap + Q_PROPERTY(bool distanceToSurfaceRelative READ distanceToSurfaceRelative WRITE setDistanceToSurfaceRelative NOTIFY distanceToSurfaceRelativeChanged) // The following values are calculated from the camera properties Q_PROPERTY(double imageFootprintSide READ imageFootprintSide NOTIFY imageFootprintSideChanged) ///< Size of image size side in meters @@ -57,30 +58,35 @@ public: const Fact* adjustedFootprintSide (void) const { return &_adjustedFootprintSideFact; } const Fact* adjustedFootprintFrontal (void) const { return &_adjustedFootprintFrontalFact; } - bool isManualCamera (void) { return cameraName() == manualCameraName(); } - double imageFootprintSide (void) const { return _imageFootprintSide; } - double imageFootprintFrontal (void) const { return _imageFootprintFrontal; } + bool dirty (void) const { return _dirty; } + bool isManualCamera (void) { return cameraName() == manualCameraName(); } + double imageFootprintSide (void) const { return _imageFootprintSide; } + double imageFootprintFrontal (void) const { return _imageFootprintFrontal; } + bool distanceToSurfaceRelative (void) const { return _distanceToSurfaceRelative; } - bool dirty (void) const { return _dirty; } - void setDirty (bool dirty); + void setDirty (bool dirty); + void setDistanceToSurfaceRelative (bool distanceToSurfaceRelative); void save(QJsonObject& json) const; bool load(const QJsonObject& json, QString& errorString); signals: - void cameraNameChanged (QString cameraName); - void dirtyChanged (bool dirty); - void imageFootprintSideChanged (double imageFootprintSide); - void imageFootprintFrontalChanged (double imageFootprintFrontal); + void cameraNameChanged (QString cameraName); + void dirtyChanged (bool dirty); + void imageFootprintSideChanged (double imageFootprintSide); + void imageFootprintFrontalChanged (double imageFootprintFrontal); + void distanceToSurfaceRelativeChanged (bool distanceToSurfaceRelative); private slots: - void _recalcTriggerDistance(void); + void _recalcTriggerDistance (void); + void _adjustDistanceToSurfaceRelative (void); private: Vehicle* _vehicle; bool _dirty; QString _cameraName; bool _disableRecalc; + bool _distanceToSurfaceRelative; QMap _metaDataMap; @@ -99,6 +105,7 @@ private: static const char* _valueSetIsDistanceName; static const char* _distanceToSurfaceName; + static const char* _distanceToSurfaceRelativeName; static const char* _imageDensityName; static const char* _frontalOverlapName; static const char* _sideOverlapName; diff --git a/src/MissionManager/CorridorScan.SettingsGroup.json b/src/MissionManager/CorridorScan.SettingsGroup.json index 29a5eb62a..78e0a7bb0 100644 --- a/src/MissionManager/CorridorScan.SettingsGroup.json +++ b/src/MissionManager/CorridorScan.SettingsGroup.json @@ -35,8 +35,8 @@ "defaultValue": 30 }, { - "name": "TurnaroundDist", - "shortDescription": "Amount of additional distance to add outside the grid area for vehicle turnaround.", + "name": "TurnaroundDistance", + "shortDescription": "Amount of additional distance to add outside the survey area for vehicle turnaround.", "type": "double", "decimalPlaces": 2, "min": 0, diff --git a/src/MissionManager/CorridorScanComplexItem.cc b/src/MissionManager/CorridorScanComplexItem.cc index d3ea05a6c..e302310d3 100644 --- a/src/MissionManager/CorridorScanComplexItem.cc +++ b/src/MissionManager/CorridorScanComplexItem.cc @@ -21,34 +21,21 @@ QGC_LOGGING_CATEGORY(CorridorScanComplexItemLog, "CorridorScanComplexItemLog") -const char* CorridorScanComplexItem::_corridorWidthFactName = "CorridorWidth"; +const char* CorridorScanComplexItem::settingsGroup = "CorridorScan"; +const char* CorridorScanComplexItem::corridorWidthName = "CorridorWidth"; +const char* CorridorScanComplexItem::_entryPointName = "EntryPoint"; -const char* CorridorScanComplexItem::jsonComplexItemTypeValue = "CorridorScan"; -const char* CorridorScanComplexItem::_jsonCameraCalcKey = "CameraCalc"; - -QMap CorridorScanComplexItem::_metaDataMap; +const char* CorridorScanComplexItem::jsonComplexItemTypeValue = "CorridorScan"; CorridorScanComplexItem::CorridorScanComplexItem(Vehicle* vehicle, QObject* parent) - : ComplexMissionItem (vehicle, parent) - , _sequenceNumber (0) - , _dirty (false) - , _corridorWidthFact (0, _corridorWidthFactName, FactMetaData::valueTypeDouble) + : TransectStyleComplexItem (vehicle, settingsGroup, parent) , _ignoreRecalc (false) - , _scanDistance (0.0) - , _cameraShots (0) - , _cameraMinTriggerInterval (0) - , _cameraCalc (vehicle) + , _entryPoint (0) + , _metaDataMap (FactMetaData::createMapFromJsonFile(QStringLiteral(":/json/CorridorScan.SettingsGroup.json"), this)) + , _corridorWidthFact (settingsGroup, _metaDataMap[corridorWidthName]) { _editorQml = "qrc:/qml/CorridorScanEditor.qml"; - if (_metaDataMap.isEmpty()) { - _metaDataMap = FactMetaData::createMapFromJsonFile(QStringLiteral(":/json/CorridorScan.SettingsGroup.json"), NULL /* QObject parent */); - } - - _corridorWidthFact.setMetaData(_metaDataMap[_corridorWidthFactName]); - - _corridorWidthFact.setRawValue(_corridorWidthFact.rawDefaultValue()); - connect(&_corridorWidthFact, &Fact::valueChanged, this, &CorridorScanComplexItem::_setDirty); connect(&_corridorPolyline, &QGCMapPolyline::pathChanged, this, &CorridorScanComplexItem::_setDirty); connect(this, &CorridorScanComplexItem::altitudeRelativeChanged, this, &CorridorScanComplexItem::_setDirty); @@ -68,38 +55,12 @@ CorridorScanComplexItem::CorridorScanComplexItem(Vehicle* vehicle, QObject* pare connect(&_corridorWidthFact, &Fact::valueChanged, this, &CorridorScanComplexItem::_signalLastSequenceNumberChanged); connect(_cameraCalc.adjustedFootprintSide(), &Fact::valueChanged, this, &CorridorScanComplexItem::_signalLastSequenceNumberChanged); - connect(&_corridorPolygon, &QGCMapPolygon::pathChanged, this, &CorridorScanComplexItem::coveredAreaChanged); - connect(this, &CorridorScanComplexItem::transectPointsChanged, this, &CorridorScanComplexItem::complexDistanceChanged); connect(this, &CorridorScanComplexItem::transectPointsChanged, this, &CorridorScanComplexItem::greatestDistanceToChanged); _rebuildCorridor(); } -void CorridorScanComplexItem::_setScanDistance(double scanDistance) -{ - if (!qFuzzyCompare(_scanDistance, scanDistance)) { - _scanDistance = scanDistance; - emit complexDistanceChanged(); - } -} - -void CorridorScanComplexItem::_setCameraShots(int cameraShots) -{ - if (_cameraShots != cameraShots) { - _cameraShots = cameraShots; - emit cameraShotsChanged(); - } -} - -void CorridorScanComplexItem::_clearInternal(void) -{ - setDirty(true); - - emit specifiesCoordinateChanged(); - emit lastSequenceNumberChanged(lastSequenceNumber()); -} - void CorridorScanComplexItem::_polylineCountChanged(int count) { Q_UNUSED(count); @@ -108,15 +69,7 @@ void CorridorScanComplexItem::_polylineCountChanged(int count) int CorridorScanComplexItem::lastSequenceNumber(void) const { - return _sequenceNumber + ((_corridorPolyline.count() + 2 /* trigger start/stop */) * _transectCount()); -} - -void CorridorScanComplexItem::setDirty(bool dirty) -{ - if (_dirty != dirty) { - _dirty = dirty; - emit dirtyChanged(_dirty); - } + return _sequenceNumber + ((_corridorPolyline.count() + 2 /* trigger start/stop */ + (_hasTurnaround() ? 2 : 0)) * _transectCount()); } void CorridorScanComplexItem::save(QJsonArray& missionItems) @@ -126,7 +79,9 @@ void CorridorScanComplexItem::save(QJsonArray& missionItems) saveObject[JsonHelper::jsonVersionKey] = 1; saveObject[VisualMissionItem::jsonTypeKey] = VisualMissionItem::jsonTypeComplexItemValue; saveObject[ComplexMissionItem::jsonComplexItemTypeKey] = jsonComplexItemTypeValue; - saveObject[_corridorWidthFactName] = _corridorWidthFact.rawValue().toDouble(); + saveObject[corridorWidthName] = _corridorWidthFact.rawValue().toDouble(); + saveObject[turnAroundDistanceName] = _turnAroundDistanceFact.rawValue().toDouble(); + saveObject[_entryPointName] = _entryPoint; QJsonObject cameraCalcObject; _cameraCalc.save(cameraCalcObject); @@ -134,16 +89,9 @@ void CorridorScanComplexItem::save(QJsonArray& missionItems) _corridorPolyline.saveToJson(saveObject); - missionItems.append(saveObject); -} + _save(saveObject); -void CorridorScanComplexItem::setSequenceNumber(int sequenceNumber) -{ - if (_sequenceNumber != sequenceNumber) { - _sequenceNumber = sequenceNumber; - emit sequenceNumberChanged(sequenceNumber); - emit lastSequenceNumberChanged(lastSequenceNumber()); - } + missionItems.append(saveObject); } bool CorridorScanComplexItem::load(const QJsonObject& complexObject, int sequenceNumber, QString& errorString) @@ -152,7 +100,9 @@ bool CorridorScanComplexItem::load(const QJsonObject& complexObject, int sequenc { JsonHelper::jsonVersionKey, QJsonValue::Double, true }, { VisualMissionItem::jsonTypeKey, QJsonValue::String, true }, { ComplexMissionItem::jsonComplexItemTypeKey, QJsonValue::String, true }, - { _corridorWidthFactName, QJsonValue::Double, true }, + { corridorWidthName, QJsonValue::Double, true }, + { turnAroundDistanceName, QJsonValue::Double, true }, + { _entryPointName, QJsonValue::Double, true }, { QGCMapPolyline::jsonPolylineKey, QJsonValue::Array, true }, { _jsonCameraCalcKey, QJsonValue::Object, true }, }; @@ -177,35 +127,19 @@ bool CorridorScanComplexItem::load(const QJsonObject& complexObject, int sequenc setSequenceNumber(sequenceNumber); - if (!_cameraCalc.load(complexObject[_jsonCameraCalcKey].toObject(), errorString)) { + if (!_load(complexObject, errorString)) { return false; } - if (!_corridorPolyline.loadFromJson(complexObject, true /* required */, errorString)) { - _corridorPolyline.clear(); - _rebuildCorridor(); - return false; - } + _corridorWidthFact.setRawValue (complexObject[corridorWidthName].toDouble()); + + _entryPoint = complexObject[_entryPointName].toInt(); _rebuildCorridor(); return true; } -double CorridorScanComplexItem::greatestDistanceTo(const QGeoCoordinate &other) const -{ - double greatestDistance = 0.0; - for (int i=0; i<_transectPoints.count(); i++) { - QGeoCoordinate vertex = _transectPoints[i].value(); - double distance = vertex.distanceTo(other); - if (distance > greatestDistance) { - greatestDistance = distance; - } - } - - return greatestDistance; -} - bool CorridorScanComplexItem::specifiesCoordinate(void) const { return _corridorPolyline.count() > 1; @@ -224,14 +158,30 @@ void CorridorScanComplexItem::appendMissionItems(QList& items, QOb int pointIndex = 0; while (pointIndex < _transectPoints.count()) { - bool addTrigger = true; + if (_hasTurnaround()) { + QGeoCoordinate vertexCoord = _transectPoints[pointIndex++].value(); + MissionItem* item = new MissionItem(seqNum++, + MAV_CMD_NAV_WAYPOINT, + _cameraCalc.distanceToSurfaceRelative() ? MAV_FRAME_GLOBAL_RELATIVE_ALT : MAV_FRAME_GLOBAL, + 0, // No hold time + 0.0, // No acceptance radius specified + 0.0, // Pass through waypoint + std::numeric_limits::quiet_NaN(), // Yaw unchanged + vertexCoord.latitude(), + vertexCoord.longitude(), + _cameraCalc.distanceToSurface()->rawValue().toDouble(), // Altitude + true, // autoContinue + false, // isCurrentItem + missionItemParent); + items.append(item); + } + bool addTrigger = true; for (int i=0; i<_corridorPolyline.count(); i++) { QGeoCoordinate vertexCoord = _transectPoints[pointIndex++].value(); - MissionItem* item = new MissionItem(seqNum++, MAV_CMD_NAV_WAYPOINT, - MAV_FRAME_GLOBAL_RELATIVE_ALT, // FIXME: Manual camera should support AMSL alt + _cameraCalc.distanceToSurfaceRelative() ? MAV_FRAME_GLOBAL_RELATIVE_ALT : MAV_FRAME_GLOBAL, 0, // No hold time 0.0, // No acceptance radius specified 0.0, // Pass through waypoint @@ -271,24 +221,27 @@ void CorridorScanComplexItem::appendMissionItems(QList& items, QOb false, // isCurrentItem missionItemParent); items.append(item); - } -} - -void CorridorScanComplexItem::setMissionFlightStatus(MissionController::MissionFlightStatus_t& missionFlightStatus) -{ - ComplexMissionItem::setMissionFlightStatus(missionFlightStatus); - if (!qFuzzyCompare(_cruiseSpeed, missionFlightStatus.vehicleSpeed)) { - _cruiseSpeed = missionFlightStatus.vehicleSpeed; - emit timeBetweenShotsChanged(); + if (_hasTurnaround()) { + QGeoCoordinate vertexCoord = _transectPoints[pointIndex++].value(); + MissionItem* item = new MissionItem(seqNum++, + MAV_CMD_NAV_WAYPOINT, + _cameraCalc.distanceToSurfaceRelative() ? MAV_FRAME_GLOBAL_RELATIVE_ALT : MAV_FRAME_GLOBAL, + 0, // No hold time + 0.0, // No acceptance radius specified + 0.0, // Pass through waypoint + std::numeric_limits::quiet_NaN(), // Yaw unchanged + vertexCoord.latitude(), + vertexCoord.longitude(), + _cameraCalc.distanceToSurface()->rawValue().toDouble(), // Altitude + true, // autoContinue + false, // isCurrentItem + missionItemParent); + items.append(item); + } } } -void CorridorScanComplexItem::_setDirty(void) -{ - setDirty(true); -} - void CorridorScanComplexItem::applyNewAltitude(double newAltitude) { Q_UNUSED(newAltitude); @@ -303,33 +256,20 @@ void CorridorScanComplexItem::_polylineDirtyChanged(bool dirty) } } -double CorridorScanComplexItem::timeBetweenShots(void) -{ - return _cruiseSpeed == 0 ? 0 : _cameraCalc.adjustedFootprintSide()->rawValue().toDouble() / _cruiseSpeed; -} - -void CorridorScanComplexItem::_updateCoordinateAltitudes(void) -{ - emit coordinateChanged(coordinate()); - emit exitCoordinateChanged(exitCoordinate()); -} - void CorridorScanComplexItem::rotateEntryPoint(void) { -#if 0 - _entryVertex++; - if (_entryVertex >= _flightPolygon.count()) { - _entryVertex = 0; + _entryPoint++; + if (_entryPoint > 3) { + _entryPoint = 0; } - emit coordinateChanged(coordinate()); - emit exitCoordinateChanged(exitCoordinate()); -#endif + + _rebuildCorridor(); } void CorridorScanComplexItem::_rebuildCorridorPolygon(void) { if (_corridorPolyline.count() < 2) { - _corridorPolygon.clear(); + _surveyAreaPolygon.clear(); return; } @@ -338,19 +278,19 @@ void CorridorScanComplexItem::_rebuildCorridorPolygon(void) QList firstSideVertices = _corridorPolyline.offsetPolyline(halfWidth); QList secondSideVertices = _corridorPolyline.offsetPolyline(-halfWidth); - _corridorPolygon.clear(); + _surveyAreaPolygon.clear(); foreach (const QGeoCoordinate& vertex, firstSideVertices) { - _corridorPolygon.appendVertex(vertex); + _surveyAreaPolygon.appendVertex(vertex); } for (int i=secondSideVertices.count() - 1; i >= 0; i--) { - _corridorPolygon.appendVertex(secondSideVertices[i]); + _surveyAreaPolygon.appendVertex(secondSideVertices[i]); } } void CorridorScanComplexItem::_rebuildTransects(void) { - _transectPoints.clear(); + _cameraShots = 0; double transectSpacing = _cameraCalc.adjustedFootprintSide()->rawValue().toDouble(); double fullWidth = _corridorWidthFact.rawValue().toDouble(); @@ -358,38 +298,115 @@ void CorridorScanComplexItem::_rebuildTransects(void) int transectCount = _transectCount(); double normalizedTransectPosition = transectSpacing / 2.0; - _cameraShots = 0; - int singleTransectImageCount = qCeil(_corridorPolyline.length() / _cameraCalc.adjustedFootprintFrontal()->rawValue().toDouble()); - - bool reverseVertices = false; - for (int i=0; i= 2) { + int singleTransectImageCount = qCeil(_corridorPolyline.length() / _cameraCalc.adjustedFootprintFrontal()->rawValue().toDouble()); + + // First build up the transects all going the same direction + QList> transects; + for (int i=0; i transect = _corridorPolyline.offsetPolyline(offsetDistance); + if (_hasTurnaround()) { + QGeoCoordinate extensionCoord; + + // Extend the transect ends for turnaround + double azimuth = transect[0].azimuthTo(transect[1]); + extensionCoord = transect[0].atDistanceAndAzimuth(-_turnAroundDistanceFact.rawValue().toDouble(), azimuth); + transect.prepend(extensionCoord); + azimuth = transect.last().azimuthTo(transect[transect.count() - 2]); + extensionCoord = transect.last().atDistanceAndAzimuth(-_turnAroundDistanceFact.rawValue().toDouble(), azimuth); + transect.append(extensionCoord); + } + + transects.append(transect); + normalizedTransectPosition += transectSpacing; } - QList transectVertices = _corridorPolyline.offsetPolyline(offsetDistance); - if (reverseVertices) { + // Now deal with fixing up the entry point: + // 0: Leave alone + // 1: Start at same end, opposite side of center + // 2: Start at opposite end, same side + // 3: Start at opposite end, opposite side + + bool reverseTransects = false; + bool reverseVertices = false; + switch (_entryPoint) { + case 0: + reverseTransects = false; reverseVertices = false; - QList reversedVertices; - for (int j=transectVertices.count()-1; j>=0; j--) { - reversedVertices.append(transectVertices[j]); - } - transectVertices = reversedVertices; - } else { + break; + case 1: + reverseTransects = true; + reverseVertices = false; + break; + case 2: + reverseTransects = false; + reverseVertices = true; + break; + case 3: + reverseTransects = true; reverseVertices = true; + break; } - for (int i=0; i> reversedTransects; + foreach (const QList& transect, transects) { + reversedTransects.prepend(transect); + } + transects = reversedTransects; + } + if (reverseVertices) { + for (int i=0; i reversedVertices; + foreach (const QGeoCoordinate& vertex, transects[i]) { + reversedVertices.prepend(vertex); + } + transects[i] = reversedVertices; + } } - normalizedTransectPosition += transectSpacing; + // Convert the list of transects to grid points + reverseVertices = false; + for (int i=0; i transectVertices = transects[i]; + if (reverseVertices) { + reverseVertices = false; + QList reversedVertices; + for (int j=transectVertices.count()-1; j>=0; j--) { + reversedVertices.append(transectVertices[j]); + } + transectVertices = reversedVertices; + } else { + reverseVertices = true; + } + for (int i=0; i() : QGeoCoordinate(); @@ -406,13 +423,3 @@ void CorridorScanComplexItem::_rebuildCorridor(void) _rebuildCorridorPolygon(); _rebuildTransects(); } - -void CorridorScanComplexItem::_signalLastSequenceNumberChanged(void) -{ - emit lastSequenceNumberChanged(lastSequenceNumber()); -} - -double CorridorScanComplexItem::coveredArea(void) const -{ - return _corridorPolygon.area(); -} diff --git a/src/MissionManager/CorridorScanComplexItem.h b/src/MissionManager/CorridorScanComplexItem.h index 751ad2c4b..01c1983ff 100644 --- a/src/MissionManager/CorridorScanComplexItem.h +++ b/src/MissionManager/CorridorScanComplexItem.h @@ -9,7 +9,7 @@ #pragma once -#include "ComplexMissionItem.h" +#include "TransectStyleComplexItem.h" #include "MissionItem.h" #include "SettingsFact.h" #include "QGCLoggingCategory.h" @@ -19,120 +19,61 @@ Q_DECLARE_LOGGING_CATEGORY(CorridorScanComplexItemLog) -class CorridorScanComplexItem : public ComplexMissionItem +class CorridorScanComplexItem : public TransectStyleComplexItem { Q_OBJECT public: CorridorScanComplexItem(Vehicle* vehicle, QObject* parent = NULL); - Q_PROPERTY(CameraCalc* cameraCalc READ cameraCalc CONSTANT) - Q_PROPERTY(QGCMapPolyline* corridorPolyline READ corridorPolyline CONSTANT) - Q_PROPERTY(QGCMapPolygon* corridorPolygon READ corridorPolygon CONSTANT) - Q_PROPERTY(Fact* corridorWidth READ corridorWidth CONSTANT) - Q_PROPERTY(int cameraShots READ cameraShots NOTIFY cameraShotsChanged) - Q_PROPERTY(double timeBetweenShots READ timeBetweenShots NOTIFY timeBetweenShotsChanged) - Q_PROPERTY(double coveredArea READ coveredArea NOTIFY coveredAreaChanged) - Q_PROPERTY(double cameraMinTriggerInterval MEMBER _cameraMinTriggerInterval NOTIFY cameraMinTriggerIntervalChanged) - Q_PROPERTY(QVariantList transectPoints READ transectPoints NOTIFY transectPointsChanged) - - CameraCalc* cameraCalc (void) { return &_cameraCalc; } - QGCMapPolyline* corridorPolyline(void) { return &_corridorPolyline; } - QGCMapPolygon* corridorPolygon (void) { return &_corridorPolygon; } - Fact* corridorWidth (void) { return &_corridorWidthFact; } - QVariantList transectPoints (void) { return _transectPoints; } + Q_PROPERTY(CameraCalc* cameraCalc READ cameraCalc CONSTANT) + Q_PROPERTY(QGCMapPolyline* corridorPolyline READ corridorPolyline CONSTANT) + Q_PROPERTY(Fact* corridorWidth READ corridorWidth CONSTANT) - int cameraShots (void) const { return _cameraShots; } - double timeBetweenShots (void); - double coveredArea (void) const; + Fact* corridorWidth (void) { return &_corridorWidthFact; } + QGCMapPolyline* corridorPolyline(void) { return &_corridorPolyline; } Q_INVOKABLE void rotateEntryPoint(void); // Overrides from ComplexMissionItem - double complexDistance (void) const final { return _scanDistance; } - int lastSequenceNumber (void) const final; - bool load (const QJsonObject& complexObject, int sequenceNumber, QString& errorString) final; - double greatestDistanceTo (const QGeoCoordinate &other) const final; - QString mapVisualQML (void) const final { return QStringLiteral("CorridorScanMapVisual.qml"); } - - // Overrides from VisualMissionItem - - bool dirty (void) const final { return _dirty; } - bool isSimpleItem (void) const final { return false; } - bool isStandaloneCoordinate (void) const final { return false; } - bool specifiesCoordinate (void) const final; - bool specifiesAltitudeOnly (void) const final { return false; } - QString commandDescription (void) const final { return tr("Corridor Scan"); } - QString commandName (void) const final { return tr("Corridor Scan"); } - QString abbreviation (void) const final { return "S"; } - QGeoCoordinate coordinate (void) const final { return _coordinate; } - QGeoCoordinate exitCoordinate (void) const final { return _exitCoordinate; } - int sequenceNumber (void) const final { return _sequenceNumber; } - double specifiedFlightSpeed (void) final { return std::numeric_limits::quiet_NaN(); } - double specifiedGimbalYaw (void) final { return std::numeric_limits::quiet_NaN(); } - double specifiedGimbalPitch (void) final { return std::numeric_limits::quiet_NaN(); } - void appendMissionItems (QList& items, QObject* missionItemParent) final; - void setMissionFlightStatus (MissionController::MissionFlightStatus_t& missionFlightStatus) final; - void applyNewAltitude (double newAltitude) final; - - bool coordinateHasRelativeAltitude (void) const final { return true /*_altitudeRelative*/; } - bool exitCoordinateHasRelativeAltitude (void) const final { return true /*_altitudeRelative*/; } - bool exitCoordinateSameAsEntry (void) const final { return false; } - - void setDirty (bool dirty) final; - void setCoordinate (const QGeoCoordinate& coordinate) final { Q_UNUSED(coordinate); } - void setSequenceNumber (int sequenceNumber) final; - void save (QJsonArray& missionItems) final; + int lastSequenceNumber (void) const final; + bool load (const QJsonObject& complexObject, int sequenceNumber, QString& errorString) final; + QString mapVisualQML (void) const final { return QStringLiteral("CorridorScanMapVisual.qml"); } + + // Overrides from TransectStyleComplexItem + + void save (QJsonArray& missionItems) final; + bool specifiesCoordinate (void) const final; + void appendMissionItems (QList& items, QObject* missionItemParent) final; + void applyNewAltitude (double newAltitude) final; static const char* jsonComplexItemTypeValue; -signals: - void cameraShotsChanged (void); - void timeBetweenShotsChanged (void); - void cameraMinTriggerIntervalChanged(double cameraMinTriggerInterval); - void altitudeRelativeChanged (bool altitudeRelative); - void transectPointsChanged (void); - void coveredAreaChanged (void); + static const char* settingsGroup; + static const char* corridorWidthName; private slots: - void _setDirty (void); void _polylineDirtyChanged (bool dirty); void _polylineCountChanged (int count); - void _clearInternal (void); - void _updateCoordinateAltitudes (void); - void _signalLastSequenceNumberChanged (void); void _rebuildCorridor (void); - void _rebuildTransects (void); + + // Overrides from TransectStyleComplexItem + virtual void _rebuildTransects (void) final; private: - void _setExitCoordinate (const QGeoCoordinate& coordinate); - void _setScanDistance (double scanDistance); - void _setCameraShots (int cameraShots); - double _triggerDistance (void) const; int _transectCount (void) const; void _rebuildCorridorPolygon(void); - int _sequenceNumber; - bool _dirty; - QGeoCoordinate _coordinate; - QGeoCoordinate _exitCoordinate; - QGCMapPolyline _corridorPolyline; - QGCMapPolygon _corridorPolygon; - Fact _corridorWidthFact; - QVariantList _transectPoints; - bool _ignoreRecalc; - double _scanDistance; - int _cameraShots; - double _timeBetweenShots; - double _cameraMinTriggerInterval; - double _cruiseSpeed; - CameraCalc _cameraCalc; + QGCMapPolyline _corridorPolyline; + QList> _transectSegments; ///< Internal transect segments including grid exit, turnaround and internal camera points - static QMap _metaDataMap; + bool _ignoreRecalc; + int _entryPoint; - static const char* _corridorWidthFactName; + QMap _metaDataMap; + SettingsFact _corridorWidthFact; - static const char* _jsonCameraCalcKey; + static const char* _entryPointName; }; diff --git a/src/MissionManager/CorridorScanComplexItemTest.cc b/src/MissionManager/CorridorScanComplexItemTest.cc index 32ce4e6dd..d7db23853 100644 --- a/src/MissionManager/CorridorScanComplexItemTest.cc +++ b/src/MissionManager/CorridorScanComplexItemTest.cc @@ -43,7 +43,7 @@ void CorridorScanComplexItemTest::init(void) _rgCorridorPolygonSignals[corridorPolygonPathChangedIndex] = SIGNAL(pathChanged()); _multiSpyCorridorPolygon = new MultiSignalSpy(); - QCOMPARE(_multiSpyCorridorPolygon->init(_corridorItem->corridorPolygon(), _rgCorridorPolygonSignals, _cCorridorPolygonSignals), true); + QCOMPARE(_multiSpyCorridorPolygon->init(_corridorItem->surveyAreaPolygon(), _rgCorridorPolygonSignals, _cCorridorPolygonSignals), true); } void CorridorScanComplexItemTest::cleanup(void) diff --git a/src/MissionManager/TransectStyle.SettingsGroup.json b/src/MissionManager/TransectStyle.SettingsGroup.json new file mode 100644 index 000000000..d949f6ba2 --- /dev/null +++ b/src/MissionManager/TransectStyle.SettingsGroup.json @@ -0,0 +1,38 @@ +[ +{ + "name": "TurnAroundDistance", + "shortDescription": "Amount of additional distance to add outside the survey area for vehicle turn around.", + "type": "double", + "decimalPlaces": 2, + "min": 0, + "units": "m", + "defaultValue": 30 +}, +{ + "name": "TurnAroundDistanceMultiRotor", + "shortDescription": "Amount of additional distance to add outside the survey area for vehicle turn around.", + "type": "double", + "decimalPlaces": 2, + "min": 0, + "units": "m", + "defaultValue": 10 +}, +{ + "name": "CameraTriggerInTurnAround", + "shortDescription": "Camera continues taking images in turn arounds.", + "type": "bool", + "defaultValue": true +}, +{ + "name": "HoverAndCapture", + "shortDescription": "Stop and Hover at each image point before taking image", + "type": "bool", + "defaultValue": false +}, +{ + "name": "Refly90Degrees", + "shortDescription": "Refly the pattern at a 90 degree angle", + "type": "bool", + "defaultValue": false +} +] diff --git a/src/MissionManager/TransectStyleComplexItem.cc b/src/MissionManager/TransectStyleComplexItem.cc new file mode 100644 index 000000000..41549516c --- /dev/null +++ b/src/MissionManager/TransectStyleComplexItem.cc @@ -0,0 +1,204 @@ +/**************************************************************************** + * + * (c) 2009-2016 QGROUNDCONTROL PROJECT + * + * QGroundControl is licensed according to the terms in the file + * COPYING.md in the root of the source code directory. + * + ****************************************************************************/ + +#include "TransectStyleComplexItem.h" +#include "JsonHelper.h" +#include "MissionController.h" +#include "QGCGeo.h" +#include "QGroundControlQmlGlobal.h" +#include "QGCQGeoCoordinate.h" +#include "SettingsManager.h" +#include "AppSettings.h" +#include "QGCQGeoCoordinate.h" + +#include + +QGC_LOGGING_CATEGORY(TransectStyleComplexItemLog, "TransectStyleComplexItemLog") + +const char* TransectStyleComplexItem::turnAroundDistanceName = "TurnAroundDistance"; +const char* TransectStyleComplexItem::turnAroundDistanceMultiRotorName = "TurnAroundDistanceMultiRotor"; +const char* TransectStyleComplexItem::cameraTriggerInTurnAroundName = "CameraTriggerInTurnAround"; +const char* TransectStyleComplexItem::hoverAndCaptureName = "HoverAndCapture"; +const char* TransectStyleComplexItem::refly90DegreesName = "Refly90Degrees"; + +const char* TransectStyleComplexItem::_jsonCameraCalcKey = "CameraCalc"; + +TransectStyleComplexItem::TransectStyleComplexItem(Vehicle* vehicle, QString settingsGroup, QObject* parent) + : ComplexMissionItem (vehicle, parent) + , _settingsGroup (settingsGroup) + , _sequenceNumber (0) + , _dirty (false) + , _ignoreRecalc (false) + , _scanDistance (0.0) + , _cameraShots (0) + , _cameraMinTriggerInterval (0) + , _cameraCalc (vehicle) + , _metaDataMap (FactMetaData::createMapFromJsonFile(QStringLiteral(":/json/TransectStyle.SettingsGroup.json"), this)) + , _turnAroundDistanceFact (_settingsGroup, _metaDataMap[_vehicle->multiRotor() ? turnAroundDistanceMultiRotorName : turnAroundDistanceName]) + , _cameraTriggerInTurnAroundFact(_settingsGroup, _metaDataMap[cameraTriggerInTurnAroundName]) + , _hoverAndCaptureFact (_settingsGroup, _metaDataMap[hoverAndCaptureName]) + , _refly90DegreesFact (_settingsGroup, _metaDataMap[refly90DegreesName]) +{ + connect(this, &TransectStyleComplexItem::altitudeRelativeChanged, this, &TransectStyleComplexItem::_setDirty); + + connect(this, &TransectStyleComplexItem::altitudeRelativeChanged, this, &TransectStyleComplexItem::coordinateHasRelativeAltitudeChanged); + connect(this, &TransectStyleComplexItem::altitudeRelativeChanged, this, &TransectStyleComplexItem::exitCoordinateHasRelativeAltitudeChanged); + + connect(_cameraCalc.adjustedFootprintSide(), &Fact::valueChanged, this, &TransectStyleComplexItem::_rebuildTransects); + connect(_cameraCalc.adjustedFootprintSide(), &Fact::valueChanged, this, &TransectStyleComplexItem::_signalLastSequenceNumberChanged); + + connect(&_turnAroundDistanceFact, &Fact::valueChanged, this, &TransectStyleComplexItem::_rebuildTransects); + + connect(&_surveyAreaPolygon, &QGCMapPolygon::pathChanged, this, &TransectStyleComplexItem::coveredAreaChanged); + + connect(this, &TransectStyleComplexItem::transectPointsChanged, this, &TransectStyleComplexItem::complexDistanceChanged); + connect(this, &TransectStyleComplexItem::transectPointsChanged, this, &TransectStyleComplexItem::greatestDistanceToChanged); +} + +void TransectStyleComplexItem::_setScanDistance(double scanDistance) +{ + if (!qFuzzyCompare(_scanDistance, scanDistance)) { + _scanDistance = scanDistance; + emit complexDistanceChanged(); + } +} + +void TransectStyleComplexItem::_setCameraShots(int cameraShots) +{ + if (_cameraShots != cameraShots) { + _cameraShots = cameraShots; + emit cameraShotsChanged(); + } +} + +void TransectStyleComplexItem::setDirty(bool dirty) +{ + if (_dirty != dirty) { + _dirty = dirty; + emit dirtyChanged(_dirty); + } +} + +void TransectStyleComplexItem::_save(QJsonObject& complexObject) +{ + complexObject[turnAroundDistanceName] = _turnAroundDistanceFact.rawValue().toDouble(); + complexObject[cameraTriggerInTurnAroundName] = _cameraTriggerInTurnAroundFact.rawValue().toBool(); + complexObject[hoverAndCaptureName] = _hoverAndCaptureFact.rawValue().toBool(); + complexObject[refly90DegreesName] = _refly90DegreesFact.rawValue().toBool(); + + QJsonObject cameraCalcObject; + _cameraCalc.save(cameraCalcObject); + complexObject[_jsonCameraCalcKey] = cameraCalcObject; +} + +void TransectStyleComplexItem::setSequenceNumber(int sequenceNumber) +{ + if (_sequenceNumber != sequenceNumber) { + _sequenceNumber = sequenceNumber; + emit sequenceNumberChanged(sequenceNumber); + emit lastSequenceNumberChanged(lastSequenceNumber()); + } +} + +bool TransectStyleComplexItem::_load(const QJsonObject& complexObject, QString& errorString) +{ + QList keyInfoList = { + { turnAroundDistanceName, QJsonValue::Double, true }, + { cameraTriggerInTurnAroundName, QJsonValue::Bool, true }, + { hoverAndCaptureName, QJsonValue::Bool, true }, + { refly90DegreesName, QJsonValue::Bool, true }, + { _jsonCameraCalcKey, QJsonValue::Object, true }, + }; + if (!JsonHelper::validateKeys(complexObject, keyInfoList, errorString)) { + return false; + } + + if (!_cameraCalc.load(complexObject[_jsonCameraCalcKey].toObject(), errorString)) { + return false; + } + + _turnAroundDistanceFact.setRawValue (complexObject[turnAroundDistanceName].toDouble()); + _cameraTriggerInTurnAroundFact.setRawValue (complexObject[cameraTriggerInTurnAroundName].toBool()); + _hoverAndCaptureFact.setRawValue (complexObject[hoverAndCaptureName].toBool()); + _hoverAndCaptureFact.setRawValue (complexObject[refly90DegreesName].toBool()); + + return true; +} + +double TransectStyleComplexItem::greatestDistanceTo(const QGeoCoordinate &other) const +{ + double greatestDistance = 0.0; + for (int i=0; i<_transectPoints.count(); i++) { + QGeoCoordinate vertex = _transectPoints[i].value(); + double distance = vertex.distanceTo(other); + if (distance > greatestDistance) { + greatestDistance = distance; + } + } + + return greatestDistance; +} + +void TransectStyleComplexItem::setMissionFlightStatus(MissionController::MissionFlightStatus_t& missionFlightStatus) +{ + ComplexMissionItem::setMissionFlightStatus(missionFlightStatus); + if (!qFuzzyCompare(_cruiseSpeed, missionFlightStatus.vehicleSpeed)) { + _cruiseSpeed = missionFlightStatus.vehicleSpeed; + emit timeBetweenShotsChanged(); + } +} + +void TransectStyleComplexItem::_setDirty(void) +{ + setDirty(true); +} + +void TransectStyleComplexItem::applyNewAltitude(double newAltitude) +{ + Q_UNUSED(newAltitude); + // FIXME: NYI + //_altitudeFact.setRawValue(newAltitude); +} + +double TransectStyleComplexItem::timeBetweenShots(void) +{ + return _cruiseSpeed == 0 ? 0 : _cameraCalc.adjustedFootprintSide()->rawValue().toDouble() / _cruiseSpeed; +} + +void TransectStyleComplexItem::_updateCoordinateAltitudes(void) +{ + emit coordinateChanged(coordinate()); + emit exitCoordinateChanged(exitCoordinate()); +} + +void TransectStyleComplexItem::_signalLastSequenceNumberChanged(void) +{ + emit lastSequenceNumberChanged(lastSequenceNumber()); +} + +double TransectStyleComplexItem::coveredArea(void) const +{ + return _surveyAreaPolygon.area(); +} + +bool TransectStyleComplexItem::_hasTurnaround(void) const +{ + return _turnaroundDistance() > 0; +} + +double TransectStyleComplexItem::_turnaroundDistance(void) const +{ + return _turnAroundDistanceFact.rawValue().toDouble(); +} + +bool TransectStyleComplexItem::hoverAndCaptureAllowed(void) const +{ + return _vehicle->multiRotor() || _vehicle->vtol(); +} + diff --git a/src/MissionManager/TransectStyleComplexItem.h b/src/MissionManager/TransectStyleComplexItem.h new file mode 100644 index 000000000..a5b27924d --- /dev/null +++ b/src/MissionManager/TransectStyleComplexItem.h @@ -0,0 +1,153 @@ +/**************************************************************************** + * + * (c) 2009-2016 QGROUNDCONTROL PROJECT + * + * QGroundControl is licensed according to the terms in the file + * COPYING.md in the root of the source code directory. + * + ****************************************************************************/ + +#pragma once + +#include "ComplexMissionItem.h" +#include "MissionItem.h" +#include "SettingsFact.h" +#include "QGCLoggingCategory.h" +#include "QGCMapPolyline.h" +#include "QGCMapPolygon.h" +#include "CameraCalc.h" + +Q_DECLARE_LOGGING_CATEGORY(TransectStyleComplexItemLog) + +class TransectStyleComplexItem : public ComplexMissionItem +{ + Q_OBJECT + +public: + TransectStyleComplexItem(Vehicle* vehicle, QString settignsGroup, QObject* parent = NULL); + + Q_PROPERTY(QGCMapPolygon* surveyAreaPolygon READ surveyAreaPolygon CONSTANT) + Q_PROPERTY(CameraCalc* cameraCalc READ cameraCalc CONSTANT) + Q_PROPERTY(Fact* turnAroundDistance READ turnAroundDistance CONSTANT) + Q_PROPERTY(Fact* cameraTriggerInTurnAround READ cameraTriggerInTurnAround CONSTANT) + Q_PROPERTY(Fact* hoverAndCapture READ hoverAndCapture CONSTANT) + Q_PROPERTY(Fact* refly90Degrees READ refly90Degrees CONSTANT) + + Q_PROPERTY(int cameraShots READ cameraShots NOTIFY cameraShotsChanged) + Q_PROPERTY(double timeBetweenShots READ timeBetweenShots NOTIFY timeBetweenShotsChanged) + Q_PROPERTY(double coveredArea READ coveredArea NOTIFY coveredAreaChanged) + Q_PROPERTY(double cameraMinTriggerInterval READ cameraMinTriggerInterval NOTIFY cameraMinTriggerIntervalChanged) + Q_PROPERTY(bool hoverAndCaptureAllowed READ hoverAndCaptureAllowed CONSTANT) + Q_PROPERTY(QVariantList transectPoints READ transectPoints NOTIFY transectPointsChanged) + + QGCMapPolygon* surveyAreaPolygon (void) { return &_surveyAreaPolygon; } + CameraCalc* cameraCalc (void) { return &_cameraCalc; } + QVariantList transectPoints (void) { return _transectPoints; } + + Fact* turnAroundDistance (void) { return &_turnAroundDistanceFact; } + Fact* cameraTriggerInTurnAround (void) { return &_cameraTriggerInTurnAroundFact; } + Fact* hoverAndCapture (void) { return &_hoverAndCaptureFact; } + Fact* refly90Degrees (void) { return &_refly90DegreesFact; } + + int cameraShots (void) const { return _cameraShots; } + double timeBetweenShots (void); + double coveredArea (void) const; + double cameraMinTriggerInterval(void) const { return _cameraMinTriggerInterval; } + bool hoverAndCaptureAllowed (void) const; + + // Overrides from ComplexMissionItem + + int lastSequenceNumber (void) const override = 0; + QString mapVisualQML (void) const override = 0; + bool load (const QJsonObject& complexObject, int sequenceNumber, QString& errorString) override = 0; + + double complexDistance (void) const final { return _scanDistance; } + double greatestDistanceTo (const QGeoCoordinate &other) const final; + + // Overrides from VisualMissionItem + + void save (QJsonArray& missionItems) override = 0; + bool specifiesCoordinate (void) const override = 0; + void appendMissionItems (QList& items, QObject* missionItemParent) override = 0; + void applyNewAltitude (double newAltitude) override = 0; + + bool dirty (void) const final { return _dirty; } + bool isSimpleItem (void) const final { return false; } + bool isStandaloneCoordinate (void) const final { return false; } + bool specifiesAltitudeOnly (void) const final { return false; } + QString commandDescription (void) const final { return tr("Corridor Scan"); } + QString commandName (void) const final { return tr("Corridor Scan"); } + QString abbreviation (void) const final { return "S"; } + QGeoCoordinate coordinate (void) const final { return _coordinate; } + QGeoCoordinate exitCoordinate (void) const final { return _exitCoordinate; } + int sequenceNumber (void) const final { return _sequenceNumber; } + double specifiedFlightSpeed (void) final { return std::numeric_limits::quiet_NaN(); } + double specifiedGimbalYaw (void) final { return std::numeric_limits::quiet_NaN(); } + double specifiedGimbalPitch (void) final { return std::numeric_limits::quiet_NaN(); } + void setMissionFlightStatus (MissionController::MissionFlightStatus_t& missionFlightStatus) final; + + bool coordinateHasRelativeAltitude (void) const final { return true /*_altitudeRelative*/; } + bool exitCoordinateHasRelativeAltitude (void) const final { return true /*_altitudeRelative*/; } + bool exitCoordinateSameAsEntry (void) const final { return false; } + + void setDirty (bool dirty) final; + void setCoordinate (const QGeoCoordinate& coordinate) final { Q_UNUSED(coordinate); } + void setSequenceNumber (int sequenceNumber) final; + + static const char* turnAroundDistanceName; + static const char* turnAroundDistanceMultiRotorName; + static const char* cameraTriggerInTurnAroundName; + static const char* hoverAndCaptureName; + static const char* refly90DegreesName; + +signals: + void cameraShotsChanged (void); + void timeBetweenShotsChanged (void); + void cameraMinTriggerIntervalChanged(double cameraMinTriggerInterval); + void altitudeRelativeChanged (bool altitudeRelative); + void transectPointsChanged (void); + void coveredAreaChanged (void); + +protected slots: + virtual void _rebuildTransects (void) = 0; + + void _setDirty (void); + void _updateCoordinateAltitudes (void); + void _signalLastSequenceNumberChanged (void); + +protected: + void _save (QJsonObject& saveObject); + bool _load (const QJsonObject& complexObject, QString& errorString); + void _setExitCoordinate (const QGeoCoordinate& coordinate); + void _setScanDistance (double scanDistance); + void _setCameraShots (int cameraShots); + double _triggerDistance (void) const; + int _transectCount (void) const; + bool _hasTurnaround (void) const; + double _turnaroundDistance (void) const; + + QString _settingsGroup; + int _sequenceNumber; + bool _dirty; + QGeoCoordinate _coordinate; + QGeoCoordinate _exitCoordinate; + QVariantList _transectPoints; + QGCMapPolygon _surveyAreaPolygon; + + bool _ignoreRecalc; + double _scanDistance; + int _cameraShots; + double _timeBetweenShots; + double _cameraMinTriggerInterval; + double _cruiseSpeed; + CameraCalc _cameraCalc; + + QMap _metaDataMap; + + SettingsFact _turnAroundDistanceFact; + SettingsFact _cameraTriggerInTurnAroundFact; + SettingsFact _hoverAndCaptureFact; + SettingsFact _refly90DegreesFact; + + static const char* _jsonCameraCalcKey; +}; diff --git a/src/PlanView/CorridorScanEditor.qml b/src/PlanView/CorridorScanEditor.qml index fcf379388..94f38effd 100644 --- a/src/PlanView/CorridorScanEditor.qml +++ b/src/PlanView/CorridorScanEditor.qml @@ -100,6 +100,39 @@ Rectangle { fact: missionItem.corridorWidth Layout.fillWidth: true } + + QGCLabel { text: qsTr("Turnaround dist") } + FactTextField { + fact: missionItem.turnAroundDistance + Layout.fillWidth: true + } + + FactCheckBox { + text: qsTr("Take images in turnarounds") + fact: missionItem.cameraTriggerInTurnAround + enabled: missionItem.hoverAndCaptureAllowed ? !missionItem.hoverAndCapture.rawValue : true + Layout.columnSpan: 2 + } + + QGCCheckBox { + id: relAlt + anchors.left: parent.left + text: qsTr("Relative altitude") + checked: missionItem.cameraCalc.distanceToSurfaceRelative + enabled: missionItem.cameraCalc.isManualCamera + Layout.columnSpan: 2 + onClicked: missionItem.cameraCalc.distanceToSurfaceRelative = checked + + Connections { + target: missionItem.cameraCalc + onDistanceToSurfaceRelativeChanged: relAlt.checked = missionItem.cameraCalc.distanceToSurfaceRelative + } + } + } + + QGCButton { + text: qsTr("Rotate Entry Point") + onClicked: missionItem.rotateEntryPoint() } SectionHeader { diff --git a/src/PlanView/CorridorScanMapVisual.qml b/src/PlanView/CorridorScanMapVisual.qml index 014bbe9a7..616120b29 100644 --- a/src/PlanView/CorridorScanMapVisual.qml +++ b/src/PlanView/CorridorScanMapVisual.qml @@ -56,7 +56,7 @@ Item { QGCMapPolygonVisuals { qgcView: _root.qgcView mapControl: map - mapPolygon: object.corridorPolygon + mapPolygon: object.surveyAreaPolygon interactive: false interiorColor: "green" interiorOpacity: 0.25 -- 2.22.0