diff --git a/qgroundcontrol.pro b/qgroundcontrol.pro
index d50187981078161eb4f839211b2e2bbef0dded2a..43bb37c29c7ade0a16dc568e4a84e25446bfd814 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 0a01d0ca96c32ca5853d42046061265d8734324d..f9679f4f4725995f1679cf46666d67d3fe1ae5c1 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 ef08fd61abce9d964c51504883187d693f9d0001..f7a06e6cfffe80eba40ef058ef80d1f7e8d7539f 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 2445975c65b4f841ffb4ff6536c37bba834a4aa7..1786fcf6bd225ff51e2ea985247e891c61956983 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 29a5eb62a73db448985169695ced9205c0dd129d..78e0a7bb017ce004f72a8829ae7aa6cf3efac4d4 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 d3ea05a6c9299fb3270ce76df450af88760895fe..88a0b4ee9d9923541ad2c5cba7d3f5adb40c8232 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,106 @@ 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 +414,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 751ad2c4bc4349486c1a077d2845da35eebd0fe4..01c1983ff05f465ee1dbb8e084a62570cafb03b9 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 32ce4e6dd1c343c9ccdecb059cad26b7ab9edc82..d7db2385319b58c33246c57f1937b55e604672e0 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 0000000000000000000000000000000000000000..d949f6ba24ec433e4aa073df3692bd887c904882
--- /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 0000000000000000000000000000000000000000..41549516c53a072a09a82fa1db9d49891de46d56
--- /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 0000000000000000000000000000000000000000..a5b27924d8d5df55874193d611bda7924b74bc91
--- /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 fcf37938841d0f1398c1613307d4e25a488dcd6e..94f38effd057f1134f379d5cbc100e7efa601c16 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 014bbe9a77f3470e169abac0cc6b04ccb9da5106..616120b29c42e6c6a8c8c9fe7a6b6b8469534103 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