Commit 801221a8 authored by Don Gagne's avatar Don Gagne

Initial Structure Scan support

parent de55d6e8
......@@ -522,6 +522,7 @@ HEADERS += \
src/MissionManager/SimpleMissionItem.h \
src/MissionManager/Section.h \
src/MissionManager/SpeedSection.h \
src/MissionManager/StructureScanComplexItem.h \
src/MissionManager/SurveyMissionItem.h \
src/MissionManager/VisualMissionItem.h \
src/PositionManager/PositionManager.h \
......@@ -707,6 +708,7 @@ SOURCES += \
src/MissionManager/RallyPointManager.cc \
src/MissionManager/SimpleMissionItem.cc \
src/MissionManager/SpeedSection.cc \
src/MissionManager/StructureScanComplexItem.cc \
src/MissionManager/SurveyMissionItem.cc \
src/MissionManager/VisualMissionItem.cc \
src/PositionManager/PositionManager.cpp \
......
......@@ -115,6 +115,7 @@
<file alias="QGroundControl/Controls/SignalStrength.qml">src/ui/toolbar/SignalStrength.qml</file>
<file alias="QGroundControl/Controls/SimpleItemMapVisual.qml">src/PlanView/SimpleItemMapVisual.qml</file>
<file alias="QGroundControl/Controls/SliderSwitch.qml">src/QmlControls/SliderSwitch.qml</file>
<file alias="QGroundControl/Controls/StructureScanMapVisual.qml">src/PlanView/StructureScanMapVisual.qml</file>
<file alias="QGroundControl/Controls/SubMenuButton.qml">src/QmlControls/SubMenuButton.qml</file>
<file alias="QGroundControl/Controls/SurveyMapVisual.qml">src/PlanView/SurveyMapVisual.qml</file>
<file alias="QGroundControl/Controls/VehicleRotationCal.qml">src/QmlControls/VehicleRotationCal.qml</file>
......@@ -179,7 +180,7 @@
<file alias="SetupParameterEditor.qml">src/VehicleSetup/SetupParameterEditor.qml</file>
<file alias="SetupView.qml">src/VehicleSetup/SetupView.qml</file>
<file alias="SimpleItemEditor.qml">src/PlanView/SimpleItemEditor.qml</file>
<file alias="SurveyItemEditor.qml">src/PlanView/SurveyItemEditor.qml</file>
<file alias="StructureScanEditor.qml">src/PlanView/StructureScanEditor.qml</file>
<file alias="FWLandingPatternEditor.qml">src/PlanView/FWLandingPatternEditor.qml</file>
<file alias="MissionSettingsEditor.qml">src/PlanView/MissionSettingsEditor.qml</file>
<file alias="TcpSettings.qml">src/ui/preferences/TcpSettings.qml</file>
......@@ -206,6 +207,7 @@
<file alias="QGCMapCircle.Facts.json">src/MissionManager/QGCMapCircle.Facts.json</file>
<file alias="RTK.SettingsGroup.json">src/Settings/RTK.SettingsGroup.json</file>
<file alias="Survey.SettingsGroup.json">src/MissionManager/Survey.SettingsGroup.json</file>
<file alias="StructureScan.SettingsGroup.json">src/MissionManager/StructureScan.SettingsGroup.json</file>
<file alias="Units.SettingsGroup.json">src/Settings/Units.SettingsGroup.json</file>
<file alias="Video.SettingsGroup.json">src/Settings/Video.SettingsGroup.json</file>
<file alias="RallyPoint.FactMetaData.json">src/MissionManager/RallyPoint.FactMetaData.json</file>
......
......@@ -24,6 +24,7 @@ public:
Q_PROPERTY(double complexDistance READ complexDistance NOTIFY complexDistanceChanged)
/// @return The distance covered the complex mission item in meters.
/// Signals complexDistanceChanged
virtual double complexDistance(void) const = 0;
/// @return Amount of additional time delay in seconds needed to fly the complex item
......@@ -39,6 +40,7 @@ public:
/// Get the point of complex mission item furthest away from a coordinate
/// @param other QGeoCoordinate to which distance is calculated
/// @return the greatest distance from any point of the complex item to some coordinate
/// Signals greatestDistanceToChanged
virtual double greatestDistanceTo(const QGeoCoordinate &other) const = 0;
/// This mission item attribute specifies the type of the complex item.
......@@ -46,6 +48,7 @@ public:
signals:
void complexDistanceChanged (double complexDistance);
void greatestDistanceToChanged (void);
void additionalTimeDelayChanged (double additionalTimeDelay);
};
......
......@@ -18,6 +18,7 @@
#include "SimpleMissionItem.h"
#include "SurveyMissionItem.h"
#include "FixedWingLandingComplexItem.h"
#include "StructureScanComplexItem.h"
#include "JsonHelper.h"
#include "ParameterManager.h"
#include "QGroundControlQmlGlobal.h"
......@@ -60,6 +61,7 @@ MissionController::MissionController(PlanMasterController* masterController, QOb
, _itemsRequested(false)
, _surveyMissionItemName(tr("Survey"))
, _fwLandingMissionItemName(tr("Fixed Wing Landing"))
, _structureScanMissionItemName(tr("Structure Scan"))
, _appSettings(qgcApp()->toolbox()->settingsManager()->appSettings())
, _progressPct(0)
{
......@@ -379,6 +381,8 @@ int MissionController::insertComplexMissionItem(QString itemName, QGeoCoordinate
}
} else if (itemName == _fwLandingMissionItemName) {
newItem = new FixedWingLandingComplexItem(_controllerVehicle, _visualItems);
} else if (itemName == _structureScanMissionItemName) {
newItem = new StructureScanComplexItem(_controllerVehicle, _visualItems);
} else {
qWarning() << "Internal error: Unknown complex item:" << itemName;
return sequenceNumber;
......@@ -1475,6 +1479,7 @@ void MissionController::_initVisualItem(VisualMissionItem* visualItem)
ComplexMissionItem* complexItem = qobject_cast<ComplexMissionItem*>(visualItem);
if (complexItem) {
connect(complexItem, &ComplexMissionItem::complexDistanceChanged, this, &MissionController::_recalcMissionFlightStatus);
connect(complexItem, &ComplexMissionItem::greatestDistanceToChanged, this, &MissionController::_recalcMissionFlightStatus);
connect(complexItem, &ComplexMissionItem::additionalTimeDelayChanged, this, &MissionController::_recalcMissionFlightStatus);
} else {
qWarning() << "ComplexMissionItem not found";
......@@ -1769,6 +1774,9 @@ QStringList MissionController::complexMissionItemNames(void) const
if (_controllerVehicle->fixedWing()) {
complexItems.append(_fwLandingMissionItemName);
}
if (_controllerVehicle->multiRotor() || _controllerVehicle->vtol()) {
complexItems.append(_structureScanMissionItemName);
}
return complexItems;
}
......
......@@ -231,6 +231,7 @@ private:
MissionFlightStatus_t _missionFlightStatus;
QString _surveyMissionItemName;
QString _fwLandingMissionItemName;
QString _structureScanMissionItemName;
AppSettings* _appSettings;
double _progressPct;
......
......@@ -354,3 +354,13 @@ void QGCMapPolygon::setInteractive(bool interactive)
emit interactiveChanged(interactive);
}
}
QGeoCoordinate QGCMapPolygon::vertexCoordinate(int vertex) const
{
if (vertex >= 0 && vertex < _polygonPath.count()) {
return _polygonPath[vertex].value<QGeoCoordinate>();
} else {
qWarning() << "QGCMapPolygon::vertexCoordinate bad vertex requested";
return QGeoCoordinate();
}
}
......@@ -55,6 +55,9 @@ public:
/// Returns the path in a list of QGeoCoordinate's format
QList<QGeoCoordinate> coordinateList(void) const;
/// Returns the QGeoCoordinate for the vertex specified
QGeoCoordinate vertexCoordinate(int vertex) const;
/// Saves the polygon to the json object.
/// @param json Json object to save to
void saveToJson(QJsonObject& json);
......
[
{
"name": "Altitude",
"shortDescription": "Altitude for the bottom layer of the structure scan.",
"type": "double",
"units": "m",
"decimalPlaces": 1,
"defaultValue": 50
},
{
"name": "Layers",
"shortDescription": "Number of scan layers.",
"type": "uint32",
"min": 1,
"defaultValue": 1
},
{
"name": "Layer distance",
"shortDescription": "Distance between each layer.",
"type": "double",
"decimalPlaces": 2,
"min": 0,
"units": "m",
"defaultValue": 25
},
{
"name": "Trigger distance",
"shortDescription": "Distance between each triggering of the camera. 0 specifies not camera trigger.",
"type": "double",
"decimalPlaces": 2,
"min": 0,
"units": "m",
"defaultValue": 25
}
]
/****************************************************************************
*
* (c) 2009-2016 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org>
*
* QGroundControl is licensed according to the terms in the file
* COPYING.md in the root of the source code directory.
*
****************************************************************************/
#include "StructureScanComplexItem.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 <QPolygonF>
QGC_LOGGING_CATEGORY(StructureScanComplexItemLog, "StructureScanComplexItemLog")
const char* StructureScanComplexItem::jsonComplexItemTypeValue = "StructureScan-WIP";
const char* StructureScanComplexItem::_jsonGridObjectKey = "grid";
const char* StructureScanComplexItem::_jsonGridAltitudeKey = "altitude";
const char* StructureScanComplexItem::_jsonGridAltitudeRelativeKey = "relativeAltitude";
const char* StructureScanComplexItem::_jsonGridAngleKey = "angle";
const char* StructureScanComplexItem::_jsonGridSpacingKey = "spacing";
const char* StructureScanComplexItem::_jsonGridEntryLocationKey = "entryLocation";
const char* StructureScanComplexItem::_jsonTurnaroundDistKey = "turnAroundDistance";
const char* StructureScanComplexItem::_jsonCameraTriggerDistanceKey = "cameraTriggerDistance";
const char* StructureScanComplexItem::_jsonCameraTriggerInTurnaroundKey = "cameraTriggerInTurnaround";
const char* StructureScanComplexItem::_jsonHoverAndCaptureKey = "hoverAndCapture";
const char* StructureScanComplexItem::_jsonGroundResolutionKey = "groundResolution";
const char* StructureScanComplexItem::_jsonFrontalOverlapKey = "imageFrontalOverlap";
const char* StructureScanComplexItem::_jsonSideOverlapKey = "imageSideOverlap";
const char* StructureScanComplexItem::_jsonCameraSensorWidthKey = "sensorWidth";
const char* StructureScanComplexItem::_jsonCameraSensorHeightKey = "sensorHeight";
const char* StructureScanComplexItem::_jsonCameraResolutionWidthKey = "resolutionWidth";
const char* StructureScanComplexItem::_jsonCameraResolutionHeightKey = "resolutionHeight";
const char* StructureScanComplexItem::_jsonCameraFocalLengthKey = "focalLength";
const char* StructureScanComplexItem::_jsonCameraMinTriggerIntervalKey = "minTriggerInterval";
const char* StructureScanComplexItem::_jsonCameraObjectKey = "camera";
const char* StructureScanComplexItem::_jsonCameraNameKey = "name";
const char* StructureScanComplexItem::_jsonManualGridKey = "manualGrid";
const char* StructureScanComplexItem::_jsonCameraOrientationLandscapeKey = "orientationLandscape";
const char* StructureScanComplexItem::_jsonFixedValueIsAltitudeKey = "fixedValueIsAltitude";
const char* StructureScanComplexItem::_jsonRefly90DegreesKey = "refly90Degrees";
const char* StructureScanComplexItem::_altitudeFactName = "Altitude";
const char* StructureScanComplexItem::_layersFactName = "Layers";
const char* StructureScanComplexItem::_layerDistanceFactName = "Layer distance";
const char* StructureScanComplexItem::_cameraTriggerDistanceFactName = "Trigger distance";
QMap<QString, FactMetaData*> StructureScanComplexItem::_metaDataMap;
StructureScanComplexItem::StructureScanComplexItem(Vehicle* vehicle, QObject* parent)
: ComplexMissionItem (vehicle, parent)
, _sequenceNumber (0)
, _dirty (false)
, _altitudeRelative (true)
, _entryVertex (0)
, _ignoreRecalc (false)
, _scanDistance (0.0)
, _cameraShots (0)
, _cameraMinTriggerInterval (0)
, _altitudeFact (0, _altitudeFactName, FactMetaData::valueTypeDouble)
, _layersFact (0, _layersFactName, FactMetaData::valueTypeUint32)
, _layerDistanceFact (0, _layerDistanceFactName, FactMetaData::valueTypeDouble)
, _cameraTriggerDistanceFact(0, _cameraTriggerDistanceFactName, FactMetaData::valueTypeDouble)
{
_editorQml = "qrc:/qml/StructureScanEditor.qml";
if (_metaDataMap.isEmpty()) {
_metaDataMap = FactMetaData::createMapFromJsonFile(QStringLiteral(":/json/StructureScan.SettingsGroup.json"), this);
}
_altitudeFact.setMetaData (_metaDataMap[_altitudeFactName]);
_layersFact.setMetaData (_metaDataMap[_layersFactName]);
_layerDistanceFact.setMetaData (_metaDataMap[_layerDistanceFactName]);
_cameraTriggerDistanceFact.setMetaData (_metaDataMap[_cameraTriggerDistanceFactName]);
_altitudeFact.setRawValue (_altitudeFact.rawDefaultValue());
_layersFact.setRawValue (_layersFact.rawDefaultValue());
_layerDistanceFact.setRawValue (_layerDistanceFact.rawDefaultValue());
_cameraTriggerDistanceFact.setRawValue (_cameraTriggerDistanceFact.rawDefaultValue());
_altitudeFact.setRawValue(qgcApp()->toolbox()->settingsManager()->appSettings()->defaultMissionItemAltitude()->rawValue());
connect(&_altitudeFact, &Fact::valueChanged, this, &StructureScanComplexItem::_setDirty);
connect(&_layersFact, &Fact::valueChanged, this, &StructureScanComplexItem::_setDirty);
connect(&_layerDistanceFact, &Fact::valueChanged, this, &StructureScanComplexItem::_setDirty);
connect(&_cameraTriggerDistanceFact, &Fact::valueChanged, this, &StructureScanComplexItem::_setDirty);
connect(this, &StructureScanComplexItem::altitudeRelativeChanged, this, &StructureScanComplexItem::_setDirty);
connect(this, &StructureScanComplexItem::altitudeRelativeChanged, this, &StructureScanComplexItem::coordinateHasRelativeAltitudeChanged);
connect(this, &StructureScanComplexItem::altitudeRelativeChanged, this, &StructureScanComplexItem::exitCoordinateHasRelativeAltitudeChanged);
connect(&_altitudeFact, &Fact::valueChanged, this, &StructureScanComplexItem::_updateCoordinateAltitudes);
connect(&_mapPolygon, &QGCMapPolygon::dirtyChanged, this, &StructureScanComplexItem::_polygonDirtyChanged);
connect(&_mapPolygon, &QGCMapPolygon::countChanged, this, &StructureScanComplexItem::_polygonCountChanged);
connect(&_mapPolygon, &QGCMapPolygon::pathChanged, this, &StructureScanComplexItem::_polygonPathChanged);
}
void StructureScanComplexItem::_setScanDistance(double scanDistance)
{
if (!qFuzzyCompare(_scanDistance, scanDistance)) {
_scanDistance = scanDistance;
emit complexDistanceChanged(_scanDistance);
}
}
void StructureScanComplexItem::_setCameraShots(int cameraShots)
{
if (_cameraShots != cameraShots) {
_cameraShots = cameraShots;
emit cameraShotsChanged(this->cameraShots());
}
}
void StructureScanComplexItem::_clearInternal(void)
{
setDirty(true);
emit specifiesCoordinateChanged();
emit lastSequenceNumberChanged(lastSequenceNumber());
}
void StructureScanComplexItem::_polygonCountChanged(int count)
{
Q_UNUSED(count);
emit lastSequenceNumberChanged(lastSequenceNumber());
}
int StructureScanComplexItem::lastSequenceNumber(void) const
{
return _sequenceNumber +
((_mapPolygon.count() + 1) * _layersFact.rawValue().toInt()) + // 1 waypoint for each polygon vertex + 1 to go back to first polygon vertex
1; // Gimbal yaw command
}
void StructureScanComplexItem::setDirty(bool dirty)
{
if (_dirty != dirty) {
_dirty = dirty;
emit dirtyChanged(_dirty);
}
}
void StructureScanComplexItem::save(QJsonArray& missionItems)
{
Q_UNUSED(missionItems);
#if 0
QJsonObject saveObject;
saveObject[JsonHelper::jsonVersionKey] = 3;
saveObject[VisualMissionItem::jsonTypeKey] = VisualMissionItem::jsonTypeComplexItemValue;
saveObject[ComplexMissionItem::jsonComplexItemTypeKey] = jsonComplexItemTypeValue;
saveObject[_jsonManualGridKey] = _manualGridFact.rawValue().toBool();
saveObject[_jsonFixedValueIsAltitudeKey] = _fixedValueIsAltitudeFact.rawValue().toBool();
saveObject[_jsonHoverAndCaptureKey] = _hoverAndCaptureFact.rawValue().toBool();
saveObject[_jsonRefly90DegreesKey] = _refly90Degrees;
saveObject[_jsonCameraTriggerDistanceKey] = _cameraTriggerDistanceFact.rawValue().toDouble();
QJsonObject gridObject;
gridObject[_jsonGridAltitudeKey] = _gridAltitudeFact.rawValue().toDouble();
gridObject[_jsonGridAltitudeRelativeKey] = _gridAltitudeRelativeFact.rawValue().toBool();
gridObject[_jsonGridAngleKey] = _gridAngleFact.rawValue().toDouble();
gridObject[_jsonGridSpacingKey] = _gridSpacingFact.rawValue().toDouble();
gridObject[_jsonGridEntryLocationKey] = _gridEntryLocationFact.rawValue().toDouble();
gridObject[_jsonTurnaroundDistKey] = _turnaroundDistFact.rawValue().toDouble();
saveObject[_jsonGridObjectKey] = gridObject;
if (!_manualGridFact.rawValue().toBool()) {
QJsonObject cameraObject;
cameraObject[_jsonCameraNameKey] = _cameraFact.rawValue().toString();
cameraObject[_jsonCameraOrientationLandscapeKey] = _cameraOrientationLandscapeFact.rawValue().toBool();
cameraObject[_jsonCameraSensorWidthKey] = _cameraSensorWidthFact.rawValue().toDouble();
cameraObject[_jsonCameraSensorHeightKey] = _cameraSensorHeightFact.rawValue().toDouble();
cameraObject[_jsonCameraResolutionWidthKey] = _cameraResolutionWidthFact.rawValue().toDouble();
cameraObject[_jsonCameraResolutionHeightKey] = _cameraResolutionHeightFact.rawValue().toDouble();
cameraObject[_jsonCameraFocalLengthKey] = _cameraFocalLengthFact.rawValue().toDouble();
cameraObject[_jsonCameraMinTriggerIntervalKey] = _cameraMinTriggerInterval;
cameraObject[_jsonGroundResolutionKey] = _groundResolutionFact.rawValue().toDouble();
cameraObject[_jsonFrontalOverlapKey] = _frontalOverlapFact.rawValue().toInt();
cameraObject[_jsonSideOverlapKey] = _sideOverlapFact.rawValue().toInt();
saveObject[_jsonCameraObjectKey] = cameraObject;
}
// Polygon shape
_mapPolygon.saveToJson(saveObject);
missionItems.append(saveObject);
#endif
}
void StructureScanComplexItem::setSequenceNumber(int sequenceNumber)
{
if (_sequenceNumber != sequenceNumber) {
_sequenceNumber = sequenceNumber;
emit sequenceNumberChanged(sequenceNumber);
emit lastSequenceNumberChanged(lastSequenceNumber());
}
}
bool StructureScanComplexItem::load(const QJsonObject& complexObject, int sequenceNumber, QString& errorString)
{
#if 0
QJsonObject v2Object = complexObject;
// We need to pull version first to determine what validation/conversion needs to be performed.
QList<JsonHelper::KeyValidateInfo> versionKeyInfoList = {
{ JsonHelper::jsonVersionKey, QJsonValue::Double, true },
};
if (!JsonHelper::validateKeys(v2Object, versionKeyInfoList, errorString)) {
return false;
}
int version = v2Object[JsonHelper::jsonVersionKey].toInt();
if (version != 2 && version != 3) {
errorString = tr("%1 does not support this version of survey items").arg(qgcApp()->applicationName());
return false;
}
if (version == 2) {
// Convert to v3
if (v2Object.contains(VisualMissionItem::jsonTypeKey) && v2Object[VisualMissionItem::jsonTypeKey].toString() == QStringLiteral("survey")) {
v2Object[VisualMissionItem::jsonTypeKey] = VisualMissionItem::jsonTypeComplexItemValue;
v2Object[ComplexMissionItem::jsonComplexItemTypeKey] = jsonComplexItemTypeValue;
}
}
QList<JsonHelper::KeyValidateInfo> mainKeyInfoList = {
{ JsonHelper::jsonVersionKey, QJsonValue::Double, true },
{ VisualMissionItem::jsonTypeKey, QJsonValue::String, true },
{ ComplexMissionItem::jsonComplexItemTypeKey, QJsonValue::String, true },
{ QGCMapPolygon::jsonPolygonKey, QJsonValue::Array, true },
{ _jsonGridObjectKey, QJsonValue::Object, true },
{ _jsonCameraObjectKey, QJsonValue::Object, false },
{ _jsonCameraTriggerDistanceKey, QJsonValue::Double, true },
{ _jsonManualGridKey, QJsonValue::Bool, true },
{ _jsonFixedValueIsAltitudeKey, QJsonValue::Bool, true },
{ _jsonHoverAndCaptureKey, QJsonValue::Bool, false },
{ _jsonRefly90DegreesKey, QJsonValue::Bool, false },
};
if (!JsonHelper::validateKeys(v2Object, mainKeyInfoList, errorString)) {
return false;
}
QString itemType = v2Object[VisualMissionItem::jsonTypeKey].toString();
QString complexType = v2Object[ComplexMissionItem::jsonComplexItemTypeKey].toString();
if (itemType != VisualMissionItem::jsonTypeComplexItemValue || complexType != jsonComplexItemTypeValue) {
errorString = tr("%1 does not support loading this complex mission item type: %2:%3").arg(qgcApp()->applicationName()).arg(itemType).arg(complexType);
return false;
}
_ignoreRecalc = true;
_mapPolygon.clear();
setSequenceNumber(sequenceNumber);
_manualGridFact.setRawValue (v2Object[_jsonManualGridKey].toBool(true));
_fixedValueIsAltitudeFact.setRawValue (v2Object[_jsonFixedValueIsAltitudeKey].toBool(true));
_gridAltitudeRelativeFact.setRawValue (v2Object[_jsonGridAltitudeRelativeKey].toBool(true));
_hoverAndCaptureFact.setRawValue (v2Object[_jsonHoverAndCaptureKey].toBool(false));
_refly90Degrees = v2Object[_jsonRefly90DegreesKey].toBool(false);
QList<JsonHelper::KeyValidateInfo> gridKeyInfoList = {
{ _jsonGridAltitudeKey, QJsonValue::Double, true },
{ _jsonGridAltitudeRelativeKey, QJsonValue::Bool, true },
{ _jsonGridAngleKey, QJsonValue::Double, true },
{ _jsonGridSpacingKey, QJsonValue::Double, true },
{ _jsonGridEntryLocationKey, QJsonValue::Double, false },
{ _jsonTurnaroundDistKey, QJsonValue::Double, true },
};
QJsonObject gridObject = v2Object[_jsonGridObjectKey].toObject();
if (!JsonHelper::validateKeys(gridObject, gridKeyInfoList, errorString)) {
return false;
}
_gridAltitudeFact.setRawValue (gridObject[_jsonGridAltitudeKey].toDouble());
_gridAngleFact.setRawValue (gridObject[_jsonGridAngleKey].toDouble());
_gridSpacingFact.setRawValue (gridObject[_jsonGridSpacingKey].toDouble());
_turnaroundDistFact.setRawValue (gridObject[_jsonTurnaroundDistKey].toDouble());
_cameraTriggerDistanceFact.setRawValue (v2Object[_jsonCameraTriggerDistanceKey].toDouble());
if (gridObject.contains(_jsonGridEntryLocationKey)) {
_gridEntryLocationFact.setRawValue(gridObject[_jsonGridEntryLocationKey].toDouble());
} else {
_gridEntryLocationFact.setRawValue(_gridEntryLocationFact.rawDefaultValue());
}
if (!_manualGridFact.rawValue().toBool()) {
if (!v2Object.contains(_jsonCameraObjectKey)) {
errorString = tr("%1 but %2 object is missing").arg("manualGrid = false").arg("camera");
return false;
}
QJsonObject cameraObject = v2Object[_jsonCameraObjectKey].toObject();
// Older code had typo on "imageSideOverlap" incorrectly being "imageSizeOverlap"
QString incorrectImageSideOverlap = "imageSizeOverlap";
if (cameraObject.contains(incorrectImageSideOverlap)) {
cameraObject[_jsonSideOverlapKey] = cameraObject[incorrectImageSideOverlap];
cameraObject.remove(incorrectImageSideOverlap);
}
QList<JsonHelper::KeyValidateInfo> cameraKeyInfoList = {
{ _jsonGroundResolutionKey, QJsonValue::Double, true },
{ _jsonFrontalOverlapKey, QJsonValue::Double, true },
{ _jsonSideOverlapKey, QJsonValue::Double, true },
{ _jsonCameraSensorWidthKey, QJsonValue::Double, true },
{ _jsonCameraSensorHeightKey, QJsonValue::Double, true },
{ _jsonCameraResolutionWidthKey, QJsonValue::Double, true },
{ _jsonCameraResolutionHeightKey, QJsonValue::Double, true },
{ _jsonCameraFocalLengthKey, QJsonValue::Double, true },
{ _jsonCameraNameKey, QJsonValue::String, true },
{ _jsonCameraOrientationLandscapeKey, QJsonValue::Bool, true },
{ _jsonCameraMinTriggerIntervalKey, QJsonValue::Double, false },
};
if (!JsonHelper::validateKeys(cameraObject, cameraKeyInfoList, errorString)) {
return false;
}
_cameraFact.setRawValue(cameraObject[_jsonCameraNameKey].toString());
_cameraOrientationLandscapeFact.setRawValue(cameraObject[_jsonCameraOrientationLandscapeKey].toBool(true));
_groundResolutionFact.setRawValue (cameraObject[_jsonGroundResolutionKey].toDouble());
_frontalOverlapFact.setRawValue (cameraObject[_jsonFrontalOverlapKey].toInt());
_sideOverlapFact.setRawValue (cameraObject[_jsonSideOverlapKey].toInt());
_cameraSensorWidthFact.setRawValue (cameraObject[_jsonCameraSensorWidthKey].toDouble());
_cameraSensorHeightFact.setRawValue (cameraObject[_jsonCameraSensorHeightKey].toDouble());
_cameraResolutionWidthFact.setRawValue (cameraObject[_jsonCameraResolutionWidthKey].toDouble());
_cameraResolutionHeightFact.setRawValue (cameraObject[_jsonCameraResolutionHeightKey].toDouble());
_cameraFocalLengthFact.setRawValue (cameraObject[_jsonCameraFocalLengthKey].toDouble());
_cameraMinTriggerInterval = cameraObject[_jsonCameraMinTriggerIntervalKey].toDouble(0);
}
// Polygon shape
/// Load a polygon from json
/// @param json Json object to load from
/// @param required true: no polygon in object will generate error
/// @param errorString Error string if return is false
/// @return true: success, false: failure (errorString set)
if (!_mapPolygon.loadFromJson(v2Object, true /* required */, errorString)) {
_mapPolygon.clear();
return false;
}
_ignoreRecalc = false;
_generateGrid();
return true;
#else
Q_UNUSED(complexObject);
Q_UNUSED(sequenceNumber);
Q_UNUSED(errorString);
return false;
#endif
}
void StructureScanComplexItem::_polygonPathChanged(void)
{
emit coordinateChanged(coordinate());
emit exitCoordinateChanged(exitCoordinate());
emit greatestDistanceToChanged();
}
double StructureScanComplexItem::greatestDistanceTo(const QGeoCoordinate &other) const
{
double greatestDistance = 0.0;
QList<QGeoCoordinate> vertices = _mapPolygon.coordinateList();
for (int i=0; i<vertices.count(); i++) {
QGeoCoordinate vertex = vertices[i];
double distance = vertex.distanceTo(other);
if (distance > greatestDistance) {
greatestDistance = distance;
}
}
return greatestDistance;
}
bool StructureScanComplexItem::specifiesCoordinate(void) const
{
return _mapPolygon.count() > 2;
}
void StructureScanComplexItem::appendMissionItems(QList<MissionItem*>& items, QObject* missionItemParent)
{
int seqNum = _sequenceNumber;
double baseAltitude = _altitudeFact.rawValue().toDouble();
MissionItem* item = new MissionItem(seqNum++,
MAV_CMD_DO_MOUNT_CONTROL,
MAV_FRAME_MISSION,
0, // Gimbal pitch
0, // Gimbal roll
90, // Gimbal yaw
0, 0, 0, // param 4-6 not used
MAV_MOUNT_MODE_MAVLINK_TARGETING,
true, // autoContinue
false, // isCurrentItem
missionItemParent);
items.append(item);
for (int layer=0; layer<_layersFact.rawValue().toInt(); layer++) {
double layerAltitude = baseAltitude + (layer * _layerDistanceFact.rawValue().toDouble());
for (int i=0; i<_mapPolygon.count(); i++) {
QGeoCoordinate vertexCoord = _mapPolygon.pathModel().value<QGCQGeoCoordinate*>(i)->coordinate();
MissionItem* item = new MissionItem(seqNum++,
MAV_CMD_NAV_WAYPOINT,
_altitudeRelative ? MAV_FRAME_GLOBAL_RELATIVE_ALT : MAV_FRAME_GLOBAL,
0, // No hold time
0.0, // No acceptance radius specified
0.0, // Pass through waypoint
90, //std::numeric_limits<double>::quiet_NaN(), // Yaw unchanged
vertexCoord.latitude(),
vertexCoord.longitude(),
layerAltitude,
true, // autoContinue
false, // isCurrentItem
missionItemParent);
items.append(item);
}
QGeoCoordinate vertexCoord = _mapPolygon.pathModel().value<QGCQGeoCoordinate*>(0)->coordinate();
MissionItem* item = new MissionItem(seqNum++,
MAV_CMD_NAV_WAYPOINT,
_altitudeRelative ? 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<double>::quiet_NaN(), // Yaw unchanged
vertexCoord.latitude(),
vertexCoord.longitude(),
layerAltitude,
true, // autoContinue
false, // isCurrentItem
missionItemParent);
items.append(item);
}
}
int StructureScanComplexItem::cameraShots(void) const
{
return true /*_triggerCamera()*/ ? _cameraShots : 0;
}
void StructureScanComplexItem::setMissionFlightStatus(MissionController::MissionFlightStatus_t& missionFlightStatus)
{
ComplexMissionItem::setMissionFlightStatus(missionFlightStatus);
if (!qFuzzyCompare(_cruiseSpeed, missionFlightStatus.vehicleSpeed)) {
_cruiseSpeed = missionFlightStatus.vehicleSpeed;
emit timeBetweenShotsChanged();
}
}
void StructureScanComplexItem::_setDirty(void)
{
setDirty(true);
}
void StructureScanComplexItem::applyNewAltitude(double newAltitude)
{
_altitudeFact.setRawValue(newAltitude);
}
void StructureScanComplexItem::_polygonDirtyChanged(bool dirty)
{
if (dirty) {
setDirty(true);
}
}
double StructureScanComplexItem::timeBetweenShots(void) const
{
return _cruiseSpeed == 0 ? 0 :_cameraTriggerDistanceFact.rawValue().toDouble() / _cruiseSpeed;
}
QGeoCoordinate StructureScanComplexItem::coordinate(void) const
{
if (_mapPolygon.count() > 0) {
int entryVertex = qMax(qMin(_entryVertex, _mapPolygon.count() - 1), 0);
return _mapPolygon.vertexCoordinate(entryVertex);
} else {
return QGeoCoordinate();
}
}
QGeoCoordinate StructureScanComplexItem::exitCoordinate(void) const
{
return coordinate();
}
void StructureScanComplexItem::_updateCoordinateAltitudes(void)
{
emit coordinateChanged(coordinate());
emit exitCoordinateChanged(exitCoordinate());
}
void StructureScanComplexItem::rotateEntryPoint(void)
{
_entryVertex++;
if (_entryVertex >= _mapPolygon.count()) {
_entryVertex = 0;
}
emit coordinateChanged(coordinate());
emit exitCoordinateChanged(exitCoordinate());
}
/****************************************************************************
*
* (c) 2009-2016 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org>
*
* QGroundControl is licensed according to the terms in the file
* COPYING.md in the root of the source code directory.
*
****************************************************************************/
#ifndef StructureScanComplexItem_H
#define StructureScanComplexItem_H
#include "ComplexMissionItem.h"
#include "MissionItem.h"
#include "SettingsFact.h"
#include "QGCLoggingCategory.h"
#include "QGCMapPolygon.h"
Q_DECLARE_LOGGING_CATEGORY(StructureScanComplexItemLog)
class StructureScanComplexItem : public ComplexMissionItem
{
Q_OBJECT
public:
StructureScanComplexItem(Vehicle* vehicle, QObject* parent = NULL);
Q_PROPERTY(Fact* altitude READ altitude CONSTANT)
Q_PROPERTY(Fact* layers READ layers CONSTANT)
Q_PROPERTY(Fact* layerDistance READ layerDistance CONSTANT)
Q_PROPERTY(Fact* cameraTriggerDistance READ cameraTriggerDistance CONSTANT)
Q_PROPERTY(bool altitudeRelative MEMBER _altitudeRelative NOTIFY altitudeRelativeChanged)
Q_PROPERTY(int cameraShots READ cameraShots NOTIFY cameraShotsChanged)
Q_PROPERTY(double timeBetweenShots READ timeBetweenShots NOTIFY timeBetweenShotsChanged)
Q_PROPERTY(double cameraMinTriggerInterval MEMBER _cameraMinTriggerInterval NOTIFY cameraMinTriggerIntervalChanged)
Q_PROPERTY(QGCMapPolygon* mapPolygon READ mapPolygon CONSTANT)
Fact* altitude (void) { return &_altitudeFact; }
Fact* layers (void) { return &_layersFact; }
Fact* layerDistance (void) { return &_layerDistanceFact; }
Fact* cameraTriggerDistance (void) { return &_cameraTriggerDistanceFact; }
int cameraShots (void) const;
double timeBetweenShots(void) const;
QGCMapPolygon* mapPolygon (void) { return &_mapPolygon; }
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("StructureScanMapVisual.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("Structure Scan"); }
QString commandName (void) const final { return tr("Structure Scan"); }
QString abbreviation (void) const final { return "S"; }
QGeoCoordinate coordinate (void) const final;
QGeoCoordinate exitCoordinate (void) const final;
int sequenceNumber (void) const final { return _sequenceNumber; }
double specifiedFlightSpeed (void) final { return std::numeric_limits<double>::quiet_NaN(); }
double specifiedGimbalYaw (void) final { return std::numeric_limits<double>::quiet_NaN(); }
void appendMissionItems (QList<MissionItem*>& items, QObject* missionItemParent) final;
void setMissionFlightStatus (MissionController::MissionFlightStatus_t& missionFlightStatus) final;
void applyNewAltitude (double newAltitude) final;
bool coordinateHasRelativeAltitude (void) const final { return _altitudeRelative; }
bool exitCoordinateHasRelativeAltitude (void) const final { return _altitudeRelative; }
bool exitCoordinateSameAsEntry (void) const final { return true; }
void setDirty (bool dirty) final;
void setCoordinate (const QGeoCoordinate& coordinate) final { Q_UNUSED(coordinate); }
void setSequenceNumber (int sequenceNumber) final;
void save (QJsonArray& missionItems) final;
static const char* jsonComplexItemTypeValue;
signals:
void cameraShotsChanged (int cameraShots);
void timeBetweenShotsChanged (void);
void cameraMinTriggerIntervalChanged(double cameraMinTriggerInterval);
void altitudeRelativeChanged (bool altitudeRelative);
private slots:
void _setDirty(void);
void _polygonDirtyChanged(bool dirty);
void _polygonCountChanged(int count);
void _polygonPathChanged(void);
void _clearInternal(void);
void _updateCoordinateAltitudes(void);
private:
void _setExitCoordinate(const QGeoCoordinate& coordinate);
void _setScanDistance(double scanDistance);
void _setCameraShots(int cameraShots);
double _triggerDistance(void) const;
int _sequenceNumber;
bool _dirty;
QGCMapPolygon _mapPolygon;
bool _altitudeRelative;
int _entryVertex; // Polygon vertext which is used as the mission entry point
bool _ignoreRecalc;
double _scanDistance;
int _cameraShots;
double _timeBetweenShots;
double _cameraMinTriggerInterval;
double _cruiseSpeed;
static QMap<QString, FactMetaData*> _metaDataMap;
Fact _altitudeFact;
Fact _layersFact;
Fact _layerDistanceFact;
Fact _cameraTriggerDistanceFact;
static const char* _altitudeFactName;
static const char* _layersFactName;
static const char* _layerDistanceFactName;
static const char* _cameraTriggerDistanceFactName;
static const char* _jsonGridObjectKey;
static const char* _jsonGridAltitudeKey;
static const char* _jsonGridAltitudeRelativeKey;
static const char* _jsonGridAngleKey;
static const char* _jsonGridSpacingKey;
static const char* _jsonGridEntryLocationKey;
static const char* _jsonTurnaroundDistKey;
static const char* _jsonCameraTriggerDistanceKey;
static const char* _jsonCameraTriggerInTurnaroundKey;
static const char* _jsonHoverAndCaptureKey;
static const char* _jsonGroundResolutionKey;
static const char* _jsonFrontalOverlapKey;
static const char* _jsonSideOverlapKey;
static const char* _jsonCameraSensorWidthKey;
static const char* _jsonCameraSensorHeightKey;
static const char* _jsonCameraResolutionWidthKey;
static const char* _jsonCameraResolutionHeightKey;
static const char* _jsonCameraFocalLengthKey;
static const char* _jsonCameraMinTriggerIntervalKey;
static const char* _jsonManualGridKey;
static const char* _jsonCameraObjectKey;
static const char* _jsonCameraNameKey;
static const char* _jsonCameraOrientationLandscapeKey;
static const char* _jsonFixedValueIsAltitudeKey;
static const char* _jsonRefly90DegreesKey;
static const int _hoverAndCaptureDelaySeconds = 1;
};
#endif
......@@ -1322,7 +1322,7 @@ double SurveyMissionItem::timeBetweenShots(void) const
return _cruiseSpeed == 0 ? 0 : _triggerDistance() / _cruiseSpeed;
}
void SurveyMissionItem::setMissionFlightStatus (MissionController::MissionFlightStatus_t& missionFlightStatus)
void SurveyMissionItem::setMissionFlightStatus(MissionController::MissionFlightStatus_t& missionFlightStatus)
{
ComplexMissionItem::setMissionFlightStatus(missionFlightStatus);
if (!qFuzzyCompare(_cruiseSpeed, missionFlightStatus.vehicleSpeed)) {
......
import QtQuick 2.3
import QtQuick.Controls 1.2
import QtQuick.Controls.Styles 1.4
import QtQuick.Dialogs 1.2
import QtQuick.Extras 1.4
import QtQuick.Layouts 1.2
import QGroundControl 1.0
import QGroundControl.ScreenTools 1.0
import QGroundControl.Vehicle 1.0
import QGroundControl.Controls 1.0
import QGroundControl.FactControls 1.0
import QGroundControl.Palette 1.0
import QGroundControl.FlightMap 1.0
// Editor for Survery mission items
Rectangle {
id: _root
height: visible ? (editorColumn.height + (_margin * 2)) : 0
width: availableWidth
color: qgcPal.windowShadeDark
radius: _radius
// The following properties must be available up the hierarchy chain
//property real availableWidth ///< Width for control
//property var missionItem ///< Mission Item for editor
property real _margin: ScreenTools.defaultFontPixelWidth / 2
property real _fieldWidth: ScreenTools.defaultFontPixelWidth * 10.5
property var _vehicle: QGroundControl.multiVehicleManager.activeVehicle ? QGroundControl.multiVehicleManager.activeVehicle : QGroundControl.multiVehicleManager.offlineEditingVehicle
function polygonCaptureStarted() {
missionItem.clearPolygon()
}
function polygonCaptureFinished(coordinates) {
for (var i=0; i<coordinates.length; i++) {
missionItem.addPolygonCoordinate(coordinates[i])
}
}
function polygonAdjustVertex(vertexIndex, vertexCoordinate) {
missionItem.adjustPolygonCoordinate(vertexIndex, vertexCoordinate)
}
function polygonAdjustStarted() { }
function polygonAdjustFinished() { }
QGCPalette { id: qgcPal; colorGroupEnabled: true }
Column {
id: editorColumn
anchors.margins: _margin
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
spacing: _margin
QGCLabel {
anchors.left: parent.left
anchors.right: parent.right
text: qsTr("WARNING: WORK IN PROGRESS. USE AT YOUR OWN RISK.")
wrapMode: Text.WordWrap
color: qgcPal.warningText
}
QGCLabel {
anchors.left: parent.left
anchors.right: parent.right
text: qsTr("WARNING: Photo interval is below minimum interval (%1 secs) supported by camera.").arg(missionItem.cameraMinTriggerInterval.toFixed(1))
wrapMode: Text.WordWrap
color: qgcPal.warningText
visible: missionItem.cameraShots > 0 && missionItem.cameraMinTriggerInterval !== 0 && missionItem.cameraMinTriggerInterval > missionItem.timeBetweenShots
}
GridLayout {
anchors.left: parent.left
anchors.right: parent.right
columnSpacing: _margin
rowSpacing: _margin
columns: 2
QGCLabel { text: qsTr("Altitude") }
FactTextField {
fact: missionItem.altitude
Layout.fillWidth: true
}
QGCLabel { text: qsTr("Layers") }
FactTextField {
fact: missionItem.layers
Layout.fillWidth: true
}
QGCLabel { text: qsTr("Layer distance") }
FactTextField {
fact: missionItem.layerDistance
Layout.fillWidth: true
}
QGCLabel { text: qsTr("Trigger Distance") }
FactTextField {
fact: missionItem.cameraTriggerDistance
Layout.fillWidth: true
}
QGCCheckBox {
text: qsTr("Relative altitude")
checked: missionItem.altitudeRelative
Layout.columnSpan: 2
onClicked: missionItem.altitudeRelative = checked
}
}
QGCLabel { text: qsTr("Point camera to structure using:") }
QGCRadioButton { text: qsTr("Vehicle yaw"); enabled: false }
QGCRadioButton { text: qsTr("Gimbal yaw"); checked: true; enabled: false }
QGCButton {
text: qsTr("Rotate entry point")
onClicked: missionItem.rotateEntryPoint()
}
SectionHeader {
id: statsHeader
text: qsTr("Statistics")
}
Grid {
columns: 2
columnSpacing: ScreenTools.defaultFontPixelWidth
visible: statsHeader.checked
QGCLabel { text: qsTr("Photo count") }
QGCLabel { text: missionItem.cameraShots }
QGCLabel { text: qsTr("Photo interval") }
QGCLabel { text: missionItem.timeBetweenShots.toFixed(1) + " " + qsTr("secs") }
}
}
}
/****************************************************************************
*
* (c) 2009-2016 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org>
*
* QGroundControl is licensed according to the terms in the file
* COPYING.md in the root of the source code directory.
*
****************************************************************************/
import QtQuick 2.3
import QtQuick.Controls 1.2
import QtLocation 5.3
import QtPositioning 5.3
import QGroundControl 1.0
import QGroundControl.ScreenTools 1.0
import QGroundControl.Palette 1.0
import QGroundControl.Controls 1.0
import QGroundControl.FlightMap 1.0
/// Survey Complex Mission Item visuals
Item {
id: _root
property var map ///< Map control to place item in
property var _missionItem: object
property var _mapPolygon: object.mapPolygon
property var _gridComponent
property var _entryCoordinate
property var _exitCoordinate
signal clicked(int sequenceNumber)
function _addVisualElements() {
_gridComponent = gridComponent.createObject(map)
_entryCoordinate = entryPointComponent.createObject(map)
_exitCoordinate = exitPointComponent.createObject(map)
map.addMapItem(_gridComponent)
map.addMapItem(_entryCoordinate)
map.addMapItem(_exitCoordinate)
}
function _destroyVisualElements() {
_gridComponent.destroy()
_entryCoordinate.destroy()
_exitCoordinate.destroy()
}
/// Add an initial 4 sided polygon if there is none
function _addInitialPolygon() {
if (_mapPolygon.count < 3) {
// Initial polygon is inset to take 2/3rds space
var rect = Qt.rect(map.centerViewport.x, map.centerViewport.y, map.centerViewport.width, map.centerViewport.height)
rect.x += (rect.width * 0.25) / 2
rect.y += (rect.height * 0.25) / 2
rect.width *= 0.75
rect.height *= 0.75
var centerCoord = map.toCoordinate(Qt.point(rect.x + (rect.width / 2), rect.y + (rect.height / 2)), false /* clipToViewPort */)
var topLeftCoord = map.toCoordinate(Qt.point(rect.x, rect.y), false /* clipToViewPort */)
var topRightCoord = map.toCoordinate(Qt.point(rect.x + rect.width, rect.y), false /* clipToViewPort */)
var bottomLeftCoord = map.toCoordinate(Qt.point(rect.x, rect.y + rect.height), false /* clipToViewPort */)
var bottomRightCoord = map.toCoordinate(Qt.point(rect.x + rect.width, rect.y + rect.height), false /* clipToViewPort */)
// Initial polygon has max width and height of 3000 meters
var halfWidthMeters = Math.min(topLeftCoord.distanceTo(topRightCoord), 3000) / 2
var halfHeightMeters = Math.min(topLeftCoord.distanceTo(bottomLeftCoord), 3000) / 2
topLeftCoord = centerCoord.atDistanceAndAzimuth(halfWidthMeters, -90).atDistanceAndAzimuth(halfHeightMeters, 0)
topRightCoord = centerCoord.atDistanceAndAzimuth(halfWidthMeters, 90).atDistanceAndAzimuth(halfHeightMeters, 0)
bottomLeftCoord = centerCoord.atDistanceAndAzimuth(halfWidthMeters, -90).atDistanceAndAzimuth(halfHeightMeters, 180)
bottomRightCoord = centerCoord.atDistanceAndAzimuth(halfWidthMeters, 90).atDistanceAndAzimuth(halfHeightMeters, 180)
_mapPolygon.appendVertex(topLeftCoord)
_mapPolygon.appendVertex(topRightCoord)
_mapPolygon.appendVertex(bottomRightCoord)
_mapPolygon.appendVertex(bottomLeftCoord)
}
}
Component.onCompleted: {
_addInitialPolygon()
_addVisualElements()
}
Component.onDestruction: {
_destroyVisualElements()
}
QGCMapPolygonVisuals {
id: mapPolygonVisuals
mapControl: map
mapPolygon: _mapPolygon
interactive: _missionItem.isCurrentItem
borderWidth: 1
borderColor: "black"
interiorColor: "green"
interiorOpacity: 0.5
}
// Survey grid lines
Component {
id: gridComponent
MapPolyline {
line.color: "white"
line.width: 2
path: _missionItem.gridPoints
}
}
// Entry point
Component {
id: entryPointComponent
MapQuickItem {
anchorPoint.x: sourceItem.anchorPointX
anchorPoint.y: sourceItem.anchorPointY
z: QGroundControl.zOrderMapItems
coordinate: _missionItem.coordinate
visible: _missionItem.exitCoordinate.isValid
sourceItem: MissionItemIndexLabel {
index: _missionItem.sequenceNumber
label: "Entry"
checked: _missionItem.isCurrentItem
onClicked: _root.clicked(_missionItem.sequenceNumber)
}
}
}
// Exit point
Component {
id: exitPointComponent
MapQuickItem {
anchorPoint.x: sourceItem.anchorPointX
anchorPoint.y: sourceItem.anchorPointY
z: QGroundControl.zOrderMapItems
coordinate: _missionItem.exitCoordinate
visible: _missionItem.exitCoordinate.isValid
sourceItem: MissionItemIndexLabel {
index: _missionItem.lastSequenceNumber
label: "Exit"
checked: _missionItem.isCurrentItem
onClicked: _root.clicked(_missionItem.sequenceNumber)
}
}
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment