diff --git a/qgroundcontrol.pro b/qgroundcontrol.pro
index 32c149c5eb8d101e4138a1b7d73a01554b03e1d7..d64e82534a3878baabd36853bfe36cc7e8f7a415 100644
--- a/qgroundcontrol.pro
+++ b/qgroundcontrol.pro
@@ -505,7 +505,9 @@ HEADERS += \
src/JsonHelper.h \
src/LogCompressor.h \
src/MG.h \
+ src/MissionManager/CameraCalc.h \
src/MissionManager/CameraSection.h \
+ src/MissionManager/CameraSpec.h \
src/MissionManager/ComplexMissionItem.h \
src/MissionManager/FixedWingLandingComplexItem.h \
src/MissionManager/GeoFenceController.h \
@@ -693,7 +695,9 @@ SOURCES += \
src/Joystick/JoystickManager.cc \
src/JsonHelper.cc \
src/LogCompressor.cc \
+ src/MissionManager/CameraCalc.cc \
src/MissionManager/CameraSection.cc \
+ src/MissionManager/CameraSpec.cc \
src/MissionManager/ComplexMissionItem.cc \
src/MissionManager/FixedWingLandingComplexItem.cc \
src/MissionManager/GeoFenceController.cc \
diff --git a/qgroundcontrol.qrc b/qgroundcontrol.qrc
index a1860945631ba23ee5f676f9f8c77e956add0bd9..5a743baf9caed70fc9b10fd98611dd23a4dd7f83 100644
--- a/qgroundcontrol.qrc
+++ b/qgroundcontrol.qrc
@@ -44,6 +44,7 @@
src/VehicleSetup/PX4FlowSensor.qml
src/AnalyzeView/AnalyzePage.qml
src/QmlControls/AppMessages.qml
+ src/PlanView/CameraCalc.qml
src/PlanView/CameraSection.qml
src/QmlControls/ClickableColor.qml
src/QmlControls/DropButton.qml
@@ -203,6 +204,8 @@
src/MissionManager/FWLandingPattern.FactMetaData.json
src/comm/USBBoardInfo.json
src/MissionManager/CameraSection.FactMetaData.json
+ src/MissionManager/CameraCalc.FactMetaData.json
+ src/MissionManager/CameraSpec.FactMetaData.json
src/MissionManager/SpeedSection.FactMetaData.json
src/MissionManager/MissionSettings.FactMetaData.json
src/Vehicle/VehicleFact.json
diff --git a/src/FactSystem/Fact.cc b/src/FactSystem/Fact.cc
index a1fcf413e370aadba168302ad4192fe31fd51f0c..83fe83e9223ba126e333c3ea5a1a460e086e642f 100644
--- a/src/FactSystem/Fact.cc
+++ b/src/FactSystem/Fact.cc
@@ -89,7 +89,7 @@ void Fact::forceSetRawValue(const QVariant& value)
emit rawValueChanged(_rawValue);
}
} else {
- qWarning() << kMissingMetadata;
+ qWarning() << kMissingMetadata << name();
}
}
@@ -109,7 +109,7 @@ void Fact::setRawValue(const QVariant& value)
}
}
} else {
- qWarning() << kMissingMetadata;
+ qWarning() << kMissingMetadata << name();
}
}
@@ -118,7 +118,7 @@ void Fact::setCookedValue(const QVariant& value)
if (_metaData) {
setRawValue(_metaData->cookedTranslator()(value));
} else {
- qWarning() << kMissingMetadata;
+ qWarning() << kMissingMetadata << name();
}
}
@@ -130,7 +130,7 @@ void Fact::setEnumStringValue(const QString& value)
setCookedValue(_metaData->enumValues()[index]);
}
} else {
- qWarning() << kMissingMetadata;
+ qWarning() << kMissingMetadata << name();
}
}
@@ -139,7 +139,7 @@ void Fact::setEnumIndex(int index)
if (_metaData) {
setCookedValue(_metaData->enumValues()[index]);
} else {
- qWarning() << kMissingMetadata;
+ qWarning() << kMissingMetadata << name();
}
}
@@ -168,7 +168,7 @@ QVariant Fact::cookedValue(void) const
if (_metaData) {
return _metaData->rawTranslator()(_rawValue);
} else {
- qWarning() << kMissingMetadata;
+ qWarning() << kMissingMetadata << name();
return _rawValue;
}
}
@@ -181,7 +181,7 @@ QString Fact::enumStringValue(void)
return _metaData->enumStrings()[enumIndex];
}
} else {
- qWarning() << kMissingMetadata;
+ qWarning() << kMissingMetadata << name();
}
return QString();
@@ -213,7 +213,7 @@ int Fact::enumIndex(void)
return index;
}
} else {
- qWarning() << kMissingMetadata;
+ qWarning() << kMissingMetadata << name();
}
return -1;
}
@@ -223,7 +223,7 @@ QStringList Fact::enumStrings(void) const
if (_metaData) {
return _metaData->enumStrings();
} else {
- qWarning() << kMissingMetadata;
+ qWarning() << kMissingMetadata << name();
return QStringList();
}
}
@@ -233,7 +233,7 @@ QVariantList Fact::enumValues(void) const
if (_metaData) {
return _metaData->enumValues();
} else {
- qWarning() << kMissingMetadata;
+ qWarning() << kMissingMetadata << name();
return QVariantList();
}
}
@@ -243,7 +243,7 @@ void Fact::setEnumInfo(const QStringList& strings, const QVariantList& values)
if (_metaData) {
_metaData->setEnumInfo(strings, values);
} else {
- qWarning() << kMissingMetadata;
+ qWarning() << kMissingMetadata << name();
}
}
@@ -252,7 +252,7 @@ QStringList Fact::bitmaskStrings(void) const
if (_metaData) {
return _metaData->bitmaskStrings();
} else {
- qWarning() << kMissingMetadata;
+ qWarning() << kMissingMetadata << name();
return QStringList();
}
}
@@ -262,7 +262,7 @@ QVariantList Fact::bitmaskValues(void) const
if (_metaData) {
return _metaData->bitmaskValues();
} else {
- qWarning() << kMissingMetadata;
+ qWarning() << kMissingMetadata << name();
return QVariantList();
}
}
@@ -336,7 +336,7 @@ QVariant Fact::rawDefaultValue(void) const
}
return _metaData->rawDefaultValue();
} else {
- qWarning() << kMissingMetadata;
+ qWarning() << kMissingMetadata << name();
return QVariant(0);
}
}
@@ -349,7 +349,7 @@ QVariant Fact::cookedDefaultValue(void) const
}
return _metaData->cookedDefaultValue();
} else {
- qWarning() << kMissingMetadata;
+ qWarning() << kMissingMetadata << name();
return QVariant(0);
}
}
@@ -369,7 +369,7 @@ QString Fact::shortDescription(void) const
if (_metaData) {
return _metaData->shortDescription();
} else {
- qWarning() << kMissingMetadata;
+ qWarning() << kMissingMetadata << name();
return QString();
}
}
@@ -379,7 +379,7 @@ QString Fact::longDescription(void) const
if (_metaData) {
return _metaData->longDescription();
} else {
- qWarning() << kMissingMetadata;
+ qWarning() << kMissingMetadata << name();
return QString();
}
}
@@ -389,7 +389,7 @@ QString Fact::rawUnits(void) const
if (_metaData) {
return _metaData->rawUnits();
} else {
- qWarning() << kMissingMetadata;
+ qWarning() << kMissingMetadata << name();
return QString();
}
}
@@ -399,7 +399,7 @@ QString Fact::cookedUnits(void) const
if (_metaData) {
return _metaData->cookedUnits();
} else {
- qWarning() << kMissingMetadata;
+ qWarning() << kMissingMetadata << name();
return QString();
}
}
@@ -409,7 +409,7 @@ QVariant Fact::rawMin(void) const
if (_metaData) {
return _metaData->rawMin();
} else {
- qWarning() << kMissingMetadata;
+ qWarning() << kMissingMetadata << name();
return QVariant(0);
}
}
@@ -419,7 +419,7 @@ QVariant Fact::cookedMin(void) const
if (_metaData) {
return _metaData->cookedMin();
} else {
- qWarning() << kMissingMetadata;
+ qWarning() << kMissingMetadata << name();
return QVariant(0);
}
}
@@ -434,7 +434,7 @@ QVariant Fact::rawMax(void) const
if (_metaData) {
return _metaData->rawMax();
} else {
- qWarning() << kMissingMetadata;
+ qWarning() << kMissingMetadata << name();
return QVariant(0);
}
}
@@ -444,7 +444,7 @@ QVariant Fact::cookedMax(void) const
if (_metaData) {
return _metaData->cookedMax();
} else {
- qWarning() << kMissingMetadata;
+ qWarning() << kMissingMetadata << name();
return QVariant(0);
}
}
@@ -459,7 +459,7 @@ bool Fact::minIsDefaultForType(void) const
if (_metaData) {
return _metaData->minIsDefaultForType();
} else {
- qWarning() << kMissingMetadata;
+ qWarning() << kMissingMetadata << name();
return false;
}
}
@@ -469,7 +469,7 @@ bool Fact::maxIsDefaultForType(void) const
if (_metaData) {
return _metaData->maxIsDefaultForType();
} else {
- qWarning() << kMissingMetadata;
+ qWarning() << kMissingMetadata << name();
return false;
}
}
@@ -479,7 +479,7 @@ int Fact::decimalPlaces(void) const
if (_metaData) {
return _metaData->decimalPlaces();
} else {
- qWarning() << kMissingMetadata;
+ qWarning() << kMissingMetadata << name();
return FactMetaData::defaultDecimalPlaces;
}
}
@@ -489,14 +489,17 @@ QString Fact::group(void) const
if (_metaData) {
return _metaData->group();
} else {
- qWarning() << kMissingMetadata;
+ qWarning() << kMissingMetadata << name();
return QString();
}
}
-void Fact::setMetaData(FactMetaData* metaData)
+void Fact::setMetaData(FactMetaData* metaData, bool setDefaultFromMetaData)
{
_metaData = metaData;
+ if (setDefaultFromMetaData) {
+ setRawValue(rawDefaultValue());
+ }
emit valueChanged(cookedValue());
}
@@ -509,7 +512,7 @@ bool Fact::valueEqualsDefault(void) const
return false;
}
} else {
- qWarning() << kMissingMetadata;
+ qWarning() << kMissingMetadata << name();
return false;
}
}
@@ -519,7 +522,7 @@ bool Fact::defaultValueAvailable(void) const
if (_metaData) {
return _metaData->defaultValueAvailable();
} else {
- qWarning() << kMissingMetadata;
+ qWarning() << kMissingMetadata << name();
return false;
}
}
@@ -534,7 +537,7 @@ QString Fact::validate(const QString& cookedValue, bool convertOnly)
return errorString;
} else {
- qWarning() << kMissingMetadata;
+ qWarning() << kMissingMetadata << name();
return QString("Internal error: Meta data pointer missing");
}
}
@@ -550,7 +553,7 @@ QVariant Fact::clamp(const QString& cookedValue)
return rawValue();
}
} else {
- qWarning() << kMissingMetadata;
+ qWarning() << kMissingMetadata << name();
}
return QVariant();
}
@@ -560,7 +563,7 @@ bool Fact::rebootRequired(void) const
if (_metaData) {
return _metaData->rebootRequired();
} else {
- qWarning() << kMissingMetadata;
+ qWarning() << kMissingMetadata << name();
return false;
}
}
@@ -600,7 +603,7 @@ QString Fact::enumOrValueString(void)
return cookedValueString();
}
} else {
- qWarning() << kMissingMetadata;
+ qWarning() << kMissingMetadata << name();
}
return QString();
}
@@ -610,7 +613,7 @@ double Fact::increment(void) const
if (_metaData) {
return _metaData->increment();
} else {
- qWarning() << kMissingMetadata;
+ qWarning() << kMissingMetadata << name();
}
return std::numeric_limits::quiet_NaN();
}
@@ -620,7 +623,7 @@ bool Fact::hasControl(void) const
if (_metaData) {
return _metaData->hasControl();
} else {
- qWarning() << kMissingMetadata;
+ qWarning() << kMissingMetadata << name();
return false;
}
}
@@ -630,7 +633,7 @@ bool Fact::readOnly(void) const
if (_metaData) {
return _metaData->readOnly();
} else {
- qWarning() << kMissingMetadata;
+ qWarning() << kMissingMetadata << name();
return false;
}
}
diff --git a/src/FactSystem/Fact.h b/src/FactSystem/Fact.h
index b8693b86c3308cf70f5df2b21b39ccf23db5b167..63ee9a584dbc13d1aecd13ba05632bc6d7cac284 100644
--- a/src/FactSystem/Fact.h
+++ b/src/FactSystem/Fact.h
@@ -136,7 +136,9 @@ public:
void forceSetRawValue(const QVariant& value);
/// Sets the meta data associated with the Fact.
- void setMetaData(FactMetaData* metaData);
+ /// @param metaData FactMetaData for Fact
+ /// @param setDefaultFromMetaData true: set the fact value to the default specified in the meta data
+ void setMetaData(FactMetaData* metaData, bool setDefaultFromMetaData = false);
FactMetaData* metaData() { return _metaData; }
diff --git a/src/FirmwarePlugin/CameraMetaData.h b/src/FirmwarePlugin/CameraMetaData.h
index 50ab89495933c4d311bde0479804f8a99a83f097..2c146f1a33f998562567b55c3b3e562fffde7046 100644
--- a/src/FirmwarePlugin/CameraMetaData.h
+++ b/src/FirmwarePlugin/CameraMetaData.h
@@ -7,8 +7,7 @@
*
****************************************************************************/
-#ifndef CameraMetaData_H
-#define CameraMetaData_H
+#pragma once
#include
@@ -29,15 +28,25 @@ public:
double minTriggerInterval,
QObject* parent = NULL);
- Q_PROPERTY(QString name MEMBER _name CONSTANT) ///< Camera name
- Q_PROPERTY(double sensorWidth MEMBER _sensorWidth CONSTANT) ///< Sensor size in millimeters
- Q_PROPERTY(double sensorHeight MEMBER _sensorHeight CONSTANT) ///< Sensor size in millimeters
- Q_PROPERTY(double imageWidth MEMBER _imageWidth CONSTANT) ///< Image size in pixels
- Q_PROPERTY(double imageHeight MEMBER _imageHeight CONSTANT) ///< Image size in pixels
- Q_PROPERTY(double focalLength MEMBER _focalLength CONSTANT) ///< Focal length in millimeters
- Q_PROPERTY(bool landscape MEMBER _landscape CONSTANT) ///< true: camera is in landscape orientation
- Q_PROPERTY(bool fixedOrientation MEMBER _fixedOrientation CONSTANT) ///< true: camera is in fixed orientation
- Q_PROPERTY(double minTriggerInterval MEMBER _minTriggerInterval CONSTANT) ///< Minimum time in seconds between each photo taken, 0 for not specified
+ Q_PROPERTY(QString name READ name CONSTANT) ///< Camera name
+ Q_PROPERTY(double sensorWidth READ sensorWidth CONSTANT) ///< Sensor size in millimeters
+ Q_PROPERTY(double sensorHeight READ sensorHeight CONSTANT) ///< Sensor size in millimeters
+ Q_PROPERTY(double imageWidth READ imageWidth CONSTANT) ///< Image size in pixels
+ Q_PROPERTY(double imageHeight READ imageHeight CONSTANT) ///< Image size in pixels
+ Q_PROPERTY(double focalLength READ focalLength CONSTANT) ///< Focal length in millimeters
+ Q_PROPERTY(bool landscape READ landscape CONSTANT) ///< true: camera is in landscape orientation
+ Q_PROPERTY(bool fixedOrientation READ fixedOrientation CONSTANT) ///< true: camera is in fixed orientation
+ Q_PROPERTY(double minTriggerInterval READ minTriggerInterval CONSTANT) ///< Minimum time in seconds between each photo taken, 0 for not specified
+
+ QString name (void) const { return _name; }
+ double sensorWidth (void) const { return _sensorWidth; }
+ double sensorHeight (void) const { return _sensorHeight; }
+ double imageWidth (void) const { return _imageWidth; }
+ double imageHeight (void) const { return _imageHeight; }
+ double focalLength (void) const { return _focalLength; }
+ bool landscape (void) const { return _landscape; }
+ bool fixedOrientation (void) const { return _fixedOrientation; }
+ double minTriggerInterval (void) const { return _minTriggerInterval; }
private:
QString _name;
@@ -50,5 +59,3 @@ private:
bool _fixedOrientation;
double _minTriggerInterval;
};
-
-#endif
diff --git a/src/MissionManager/CameraCalc.FactMetaData.json b/src/MissionManager/CameraCalc.FactMetaData.json
new file mode 100644
index 0000000000000000000000000000000000000000..2778a493a127fc7f7cbf134e67f20104919d774f
--- /dev/null
+++ b/src/MissionManager/CameraCalc.FactMetaData.json
@@ -0,0 +1,62 @@
+[
+{
+ "name": "ValueSetIsDistance",
+ "shortDescription": "Value specified is distance to surface.",
+ "type": "bool",
+ "defaultValue": 1
+},
+{
+ "name": "DistanceToSurface",
+ "shortDescription": "Distance vehicle is away from surface.",
+ "type": "double",
+ "min": 0.1,
+ "units": "m",
+ "decimalPlaces": 2,
+ "defaultValue": 100.0
+},
+{
+ "name": "ImageDensity",
+ "shortDescription": "Image desity at surface.",
+ "type": "double",
+ "min": 0,
+ "units": "cm/px",
+ "decimalPlaces": 1,
+ "defaultValue": 25
+},
+{
+ "name": "FrontalOverlap",
+ "shortDescription": "Amount of overlap between images in the forward facing direction.",
+ "type": "double",
+ "decimalPlaces": 0,
+ "min": 0,
+ "max": 85,
+ "units": "%",
+ "defaultValue": 70
+},
+{
+ "name": "SideOverlap",
+ "shortDescription": "Amount of overlap between images in the side facing direction.",
+ "type": "double",
+ "decimalPlaces": 0,
+ "min": 0,
+ "max": 85,
+ "units": "%",
+ "defaultValue": 70
+},
+{
+ "name": "AdjustedFootprintFrontal",
+ "type": "double",
+ "decimalPlaces": 2,
+ "min": 0,
+ "units": "m",
+ "defaultValue": 25
+},
+{
+ "name": "AdjustedFootprintSide",
+ "type": "double",
+ "decimalPlaces": 2,
+ "min": 0,
+ "units": "m",
+ "defaultValue": 25
+}
+]
diff --git a/src/MissionManager/CameraCalc.cc b/src/MissionManager/CameraCalc.cc
new file mode 100644
index 0000000000000000000000000000000000000000..1513961fbda11787ad7ae0889c3413e2c9da869f
--- /dev/null
+++ b/src/MissionManager/CameraCalc.cc
@@ -0,0 +1,355 @@
+/****************************************************************************
+ *
+ * (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 "CameraCalc.h"
+#include "JsonHelper.h"
+#include "Vehicle.h"
+#include "CameraMetaData.h"
+
+#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";
+
+CameraCalc::CameraCalc(Vehicle* vehicle, QObject* parent)
+ : CameraSpec (parent)
+ , _vehicle (vehicle)
+ , _dirty (false)
+ , _cameraSpecType (CameraSpecNone)
+ , _disableRecalc (false)
+ , _metaDataMap (FactMetaData::createMapFromJsonFile(QStringLiteral(":/json/CameraCalc.FactMetaData.json"), this))
+ , _valueSetIsDistanceFact (0, _valueSetIsDistanceName, FactMetaData::valueTypeBool)
+ , _distanceToSurfaceFact (0, _distanceToSurfaceName, FactMetaData::valueTypeDouble)
+ , _imageDensityFact (0, _imageDensityName, FactMetaData::valueTypeDouble)
+ , _frontalOverlapFact (0, _frontalOverlapName, FactMetaData::valueTypeDouble)
+ , _sideOverlapFact (0, _sideOverlapName, FactMetaData::valueTypeDouble)
+ , _adjustedFootprintSideFact (0, _adjustedFootprintSideName, FactMetaData::valueTypeDouble)
+ , _adjustedFootprintFrontalFact (0, _adjustedFootprintFrontalName, FactMetaData::valueTypeDouble)
+ , _imageFootprintSide (0)
+ , _imageFootprintFrontal (0)
+ , _knownCameraList (_vehicle->staticCameraList())
+{
+ QQmlEngine::setObjectOwnership(this, QQmlEngine::CppOwnership);
+
+ _valueSetIsDistanceFact.setMetaData (_metaDataMap[_valueSetIsDistanceName], true /* setDefaultFromMetaData */);
+ _distanceToSurfaceFact.setMetaData (_metaDataMap[_distanceToSurfaceName], true);
+ _imageDensityFact.setMetaData (_metaDataMap[_imageDensityName], true);
+ _frontalOverlapFact.setMetaData (_metaDataMap[_frontalOverlapName], true);
+ _sideOverlapFact.setMetaData (_metaDataMap[_sideOverlapName], true);
+ _adjustedFootprintSideFact.setMetaData (_metaDataMap[_adjustedFootprintSideName], false);
+ _adjustedFootprintFrontalFact.setMetaData (_metaDataMap[_adjustedFootprintFrontalName], false);
+
+ connect(this, &CameraCalc::knownCameraNameChanged, this, &CameraCalc::_knownCameraNameChanged);
+
+ connect(this, &CameraCalc::cameraSpecTypeChanged, this, &CameraCalc::_recalcTriggerDistance);
+
+ connect(&_distanceToSurfaceFact, &Fact::rawValueChanged, this, &CameraCalc::_recalcTriggerDistance);
+ connect(&_imageDensityFact, &Fact::rawValueChanged, this, &CameraCalc::_recalcTriggerDistance);
+ connect(&_frontalOverlapFact, &Fact::rawValueChanged, this, &CameraCalc::_recalcTriggerDistance);
+ connect(&_sideOverlapFact, &Fact::rawValueChanged, this, &CameraCalc::_recalcTriggerDistance);
+ connect(landscape(), &Fact::rawValueChanged, this, &CameraCalc::_recalcTriggerDistance);
+
+ _recalcTriggerDistance();
+}
+
+void CameraCalc::setDirty(bool dirty)
+{
+ if (_dirty != dirty) {
+ _dirty = dirty;
+ emit dirtyChanged(_dirty);
+ }
+}
+
+void CameraCalc::_knownCameraNameChanged(QString knownCameraName)
+{
+ if (_cameraSpecType == CameraSpecKnown) {
+ CameraMetaData* cameraMetaData = NULL;
+
+ // Update camera specs to new camera
+ for (int cameraIndex=0; cameraIndex<_knownCameraList.count(); cameraIndex++) {
+ cameraMetaData = _knownCameraList[cameraIndex].value();
+ if (knownCameraName == cameraMetaData->name()) {
+ break;
+ }
+ }
+
+ _disableRecalc = true;
+ if (cameraMetaData) {
+ sensorWidth()->setRawValue (cameraMetaData->sensorWidth());
+ sensorHeight()->setRawValue (cameraMetaData->sensorHeight());
+ imageWidth()->setRawValue (cameraMetaData->imageWidth());
+ imageHeight()->setRawValue (cameraMetaData->imageHeight());
+ focalLength()->setRawValue (cameraMetaData->focalLength());
+ landscape()->setRawValue (cameraMetaData->landscape());
+ fixedOrientation()->setRawValue (cameraMetaData->fixedOrientation());
+ minTriggerInterval()->setRawValue (cameraMetaData->minTriggerInterval());
+ } else {
+ // We don't know this camera, switch back to custom
+ _cameraSpecType = CameraSpecCustom;
+ emit cameraSpecTypeChanged(_cameraSpecType);
+ }
+ _disableRecalc = false;
+
+ _recalcTriggerDistance();
+ }
+}
+
+void CameraCalc::_recalcTriggerDistance(void)
+{
+ if (_disableRecalc || _cameraSpecType == CameraSpecNone) {
+ return;
+ }
+
+ _disableRecalc = true;
+
+ double focalLength = this->focalLength()->rawValue().toDouble();
+ double sensorWidth = this->sensorWidth()->rawValue().toDouble();
+ double sensorHeight = this->sensorHeight()->rawValue().toDouble();
+ double imageWidth = this->imageWidth()->rawValue().toDouble();
+ double imageHeight = this->imageHeight()->rawValue().toDouble();
+ double imageDensity = _imageDensityFact.rawValue().toDouble();
+
+ if (focalLength <= 0 || sensorWidth <= 0 || sensorHeight <= 0 || imageWidth <= 0 || imageHeight <= 0 || imageDensity <= 0) {
+ return;
+ }
+
+ if (_valueSetIsDistanceFact.rawValue().toBool()) {
+ _imageDensityFact.setRawValue((_distanceToSurfaceFact.rawValue().toDouble() * sensorWidth * 100.0) / (imageWidth * focalLength));
+ } else {
+ _distanceToSurfaceFact.setRawValue((imageWidth * _imageDensityFact.rawValue().toDouble() * focalLength) / (sensorWidth * 100.0));
+ }
+
+ imageDensity = _imageDensityFact.rawValue().toDouble();
+
+ if (landscape()->rawValue().toBool()) {
+ _imageFootprintSide = (imageWidth * imageDensity) / 100.0;
+ _imageFootprintFrontal = (imageHeight * imageDensity) / 100.0;
+ } else {
+ _imageFootprintSide = (imageHeight * imageDensity) / 100.0;
+ _imageFootprintFrontal = (imageWidth * imageDensity) / 100.0;
+ }
+ _adjustedFootprintSideFact.setRawValue (_imageFootprintSide * ((100.0 - _sideOverlapFact.rawValue().toDouble()) / 100.0));
+ _adjustedFootprintFrontalFact.setRawValue (_imageFootprintFrontal * ((100.0 - _frontalOverlapFact.rawValue().toDouble()) / 100.0));
+
+ emit imageFootprintSideChanged (_imageFootprintSide);
+ emit imageFootprintFrontalChanged (_imageFootprintFrontal);
+
+ _disableRecalc = false;
+}
+
+void CameraCalc::save(QJsonObject& json) const
+{
+ Q_UNUSED(json);
+
+#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();
+ saveObject[_jsonCameraTriggerInTurnaroundKey] = _cameraTriggerInTurnaroundFact.rawValue().toBool();
+
+ 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
+}
+
+bool CameraCalc::load(const QJsonObject& complexObject, QString& errorString)
+{
+ Q_UNUSED(complexObject);
+ Q_UNUSED(errorString);
+#if 0
+ QJsonObject v2Object = complexObject;
+
+ // We need to pull version first to determine what validation/conversion needs to be performed.
+ QList 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 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 },
+ { _jsonCameraTriggerInTurnaroundKey, QJsonValue::Bool, false }, // Should really be required, but it was missing from initial code due to bug
+ };
+ 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));
+ _cameraTriggerInTurnaroundFact.setRawValue (v2Object[_jsonCameraTriggerInTurnaroundKey].toBool(true));
+
+ _refly90Degrees = v2Object[_jsonRefly90DegreesKey].toBool(false);
+
+ QList 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 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;
+#endif
+ return false;
+}
diff --git a/src/MissionManager/CameraCalc.h b/src/MissionManager/CameraCalc.h
new file mode 100644
index 0000000000000000000000000000000000000000..32864432058528c030cf86ba0fc5f4640a1caad6
--- /dev/null
+++ b/src/MissionManager/CameraCalc.h
@@ -0,0 +1,102 @@
+/****************************************************************************
+ *
+ * (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 "CameraSpec.h"
+
+class Vehicle;
+
+class CameraCalc : public CameraSpec
+{
+ Q_OBJECT
+
+public:
+ CameraCalc(Vehicle* vehicle, QObject* parent = NULL);
+
+ Q_ENUMS(CameraSpecType)
+
+ Q_PROPERTY(CameraSpecType cameraSpecType MEMBER _cameraSpecType NOTIFY cameraSpecTypeChanged)
+ Q_PROPERTY(QString knownCameraName MEMBER _knownCameraName NOTIFY knownCameraNameChanged)
+ 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
+
+ // The following values are calculated from the camera properties
+ Q_PROPERTY(double imageFootprintSide READ imageFootprintSide NOTIFY imageFootprintSideChanged) ///< Size of image size side in meters
+ Q_PROPERTY(double imageFootprintFrontal READ imageFootprintFrontal NOTIFY imageFootprintFrontalChanged) ///< Size of image size frontal in meters
+
+ enum CameraSpecType {
+ CameraSpecNone,
+ CameraSpecCustom,
+ CameraSpecKnown
+ };
+
+ Fact* valueSetIsDistance (void) { return &_valueSetIsDistanceFact; }
+ Fact* distanceToSurface (void) { return &_distanceToSurfaceFact; }
+ Fact* imageDensity (void) { return &_imageDensityFact; }
+ Fact* frontalOverlap (void) { return &_frontalOverlapFact; }
+ Fact* sideOverlap (void) { return &_sideOverlapFact; }
+ Fact* adjustedFootprintSide (void) { return &_adjustedFootprintSideFact; }
+ Fact* adjustedFootprintFrontal (void) { return &_adjustedFootprintFrontalFact; }
+
+ double imageFootprintSide (void) const { return _imageFootprintSide; }
+ double imageFootprintFrontal (void) const { return _imageFootprintFrontal; }
+
+ bool dirty (void) const { return _dirty; }
+ void setDirty (bool dirty);
+
+ void save(QJsonObject& json) const;
+ bool load(const QJsonObject& json, QString& errorString);
+
+signals:
+ void cameraSpecTypeChanged (CameraSpecType cameraSpecType);
+ void knownCameraNameChanged (QString knownCameraName);
+ void dirtyChanged (bool dirty);
+ void imageFootprintSideChanged (double imageFootprintSide);
+ void imageFootprintFrontalChanged (double imageFootprintFrontal);
+
+private slots:
+ void _knownCameraNameChanged(QString knownCameraName);
+ void _recalcTriggerDistance(void);
+
+private:
+ Vehicle* _vehicle;
+ bool _dirty;
+ CameraSpecType _cameraSpecType;
+ QString _knownCameraName;
+ bool _disableRecalc;
+
+ QMap _metaDataMap;
+
+ Fact _valueSetIsDistanceFact;
+ Fact _distanceToSurfaceFact;
+ Fact _imageDensityFact;
+ Fact _frontalOverlapFact;
+ Fact _sideOverlapFact;
+ Fact _adjustedFootprintSideFact;
+ Fact _adjustedFootprintFrontalFact;
+
+ double _imageFootprintSide;
+ double _imageFootprintFrontal;
+
+ QVariantList _knownCameraList;
+
+ static const char* _valueSetIsDistanceName;
+ static const char* _distanceToSurfaceName;
+ static const char* _imageDensityName;
+ static const char* _frontalOverlapName;
+ static const char* _sideOverlapName;
+ static const char* _adjustedFootprintSideName;
+ static const char* _adjustedFootprintFrontalName;
+};
diff --git a/src/MissionManager/CameraSpec.FactMetaData.json b/src/MissionManager/CameraSpec.FactMetaData.json
new file mode 100644
index 0000000000000000000000000000000000000000..f48ce287f4045180cd6dda3ea610e5a82cdc374a
--- /dev/null
+++ b/src/MissionManager/CameraSpec.FactMetaData.json
@@ -0,0 +1,71 @@
+[
+{
+ "name": "Name",
+ "shortDescription": "Camera name.",
+ "type": "string",
+ "defaultValue": ""
+},
+{
+ "name": "SensorWidth",
+ "shortDescription": "Width of camera image sensor.",
+ "type": "double",
+ "decimalPlaces": 2,
+ "min": 1,
+ "units": "mm",
+ "defaultValue": 6.17
+},
+{
+ "name": "SensorHeight",
+ "shortDescription": "Height of camera image sensor.",
+ "type": "double",
+ "decimalPlaces": 2,
+ "min": 1,
+ "units": "mm",
+ "defaultValue": 4.55
+},
+{
+ "name": "ImageWidth",
+ "shortDescription": "Camera image resolution width.",
+ "type": "uint32",
+ "min": 1,
+ "units": "px",
+ "defaultValue": 4000
+},
+{
+ "name": "ImageHeight",
+ "shortDescription": "Camera image resolution height.",
+ "type": "uint32",
+ "min": 1,
+ "units": "px",
+ "defaultValue": 3000
+},
+{
+ "name": "FocalLength",
+ "shortDescription": "Focal length of camera lens.",
+ "type": "double",
+ "decimalPlaces": 1,
+ "min": 1,
+ "units": "mm",
+ "defaultValue": 4.5
+},
+{
+ "name": "Landscape",
+ "shortDescription": "Camera on vehicle is in landscape orientation.",
+ "type": "bool",
+ "defaultValue": 1
+},
+{
+ "name": "FixedOrientation",
+ "shortDescription": "Camera orientation ix fixed and cannot be changed.",
+ "type": "bool",
+ "defaultValue": 0
+},
+{
+ "name": "MinTriggerInterval",
+ "shortDescription": "Minimum amount of time between each camera trigger.",
+ "type": "double",
+ "min": 0.1,
+ "units": "secs",
+ "defaultValue": 1.0
+}
+]
diff --git a/src/MissionManager/CameraSpec.cc b/src/MissionManager/CameraSpec.cc
new file mode 100644
index 0000000000000000000000000000000000000000..7b7dc324bee9f21c4aed5e81993f390eefe06878
--- /dev/null
+++ b/src/MissionManager/CameraSpec.cc
@@ -0,0 +1,397 @@
+/****************************************************************************
+ *
+ * (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 "CameraSpec.h"
+#include "JsonHelper.h"
+
+#include
+
+const char* CameraSpec::_jsonSensorWidthKey = "sensorWidth";
+const char* CameraSpec::_jsonSensorHeightKey = "sensorHeight";
+const char* CameraSpec::_jsonImageWidthKey = "imageWidth";
+const char* CameraSpec::_jsonImageHeightKey = "imageHeight";
+const char* CameraSpec::_jsonFocalLengthKey = "focalLength";
+const char* CameraSpec::_jsonMinTriggerIntervalKey = "minTriggerInterval";
+const char* CameraSpec::_jsonNameKey = "name";
+const char* CameraSpec::_jsonLandscapeKey = "orientationLandscape";
+
+const char* CameraSpec::_nameName = "Camera";
+const char* CameraSpec::_sensorWidthName = "SensorWidth";
+const char* CameraSpec::_sensorHeightName = "SensorHeight";
+const char* CameraSpec::_imageWidthName = "ImageWidth";
+const char* CameraSpec::_imageHeightName = "ImageHeight";
+const char* CameraSpec::_focalLengthName = "FocalLength";
+const char* CameraSpec::_landscapeName = "Landscape";
+const char* CameraSpec::_fixedOrientationName = "FixedOrientation";
+const char* CameraSpec::_minTriggerIntervalName = "MinTriggerInterval";
+
+CameraSpec::CameraSpec(QObject* parent)
+ : QObject (parent)
+ , _dirty (false)
+ , _metaDataMap (FactMetaData::createMapFromJsonFile(QStringLiteral(":/json/CameraSpec.FactMetaData.json"), this))
+ , _nameFact (0, _nameName, FactMetaData::valueTypeString)
+ , _sensorWidthFact (0, _sensorWidthName, FactMetaData::valueTypeDouble)
+ , _sensorHeightFact (0, _sensorHeightName, FactMetaData::valueTypeDouble)
+ , _imageWidthFact (0, _imageWidthName, FactMetaData::valueTypeUint32)
+ , _imageHeightFact (0, _imageHeightName, FactMetaData::valueTypeUint32)
+ , _focalLengthFact (0, _focalLengthName, FactMetaData::valueTypeDouble)
+ , _landscapeFact (0, _landscapeName, FactMetaData::valueTypeBool)
+ , _fixedOrientationFact (0, _fixedOrientationName, FactMetaData::valueTypeBool)
+ , _minTriggerIntervalFact (0, _minTriggerIntervalName, FactMetaData::valueTypeDouble)
+{
+ _init(true);
+}
+
+CameraSpec::CameraSpec(const QString& name,
+ double sensorWidth,
+ double sensorHeight,
+ double imageWidth,
+ double imageHeight,
+ double focalLength,
+ bool landscape,
+ bool fixedOrientation,
+ double minTriggerInterval,
+ QObject* parent)
+ : QObject (parent)
+ , _dirty (false)
+ , _metaDataMap (FactMetaData::createMapFromJsonFile(QStringLiteral(":/json/CameraSpec.FactMetaData.json"), this))
+ , _nameFact (0, _nameName, FactMetaData::valueTypeString)
+ , _sensorWidthFact (0, _sensorWidthName, FactMetaData::valueTypeDouble)
+ , _sensorHeightFact (0, _sensorHeightName, FactMetaData::valueTypeDouble)
+ , _imageWidthFact (0, _imageWidthName, FactMetaData::valueTypeUint32)
+ , _imageHeightFact (0, _imageHeightName, FactMetaData::valueTypeUint32)
+ , _focalLengthFact (0, _focalLengthName, FactMetaData::valueTypeDouble)
+ , _landscapeFact (0, _landscapeName, FactMetaData::valueTypeBool)
+ , _fixedOrientationFact (0, _fixedOrientationName, FactMetaData::valueTypeBool)
+ , _minTriggerIntervalFact (0, _minTriggerIntervalName, FactMetaData::valueTypeDouble)
+{
+ _init(false);
+
+ _nameFact.setRawValue (name);
+ _sensorWidthFact.setRawValue (sensorWidth);
+ _sensorHeightFact.setRawValue (sensorHeight);
+ _imageWidthFact.setRawValue (imageWidth);
+ _imageHeightFact.setRawValue (imageHeight);
+ _focalLengthFact.setRawValue (focalLength);
+ _landscapeFact.setRawValue (landscape);
+ _fixedOrientationFact.setRawValue (fixedOrientation);
+ _minTriggerIntervalFact.setRawValue (minTriggerInterval);
+}
+
+CameraSpec::CameraSpec(const CameraSpec& other, QObject* parent)
+ : QObject (parent)
+ , _dirty (false)
+ , _metaDataMap (FactMetaData::createMapFromJsonFile(QStringLiteral(":/json/CameraSpec.FactMetaData.json"), this))
+ , _nameFact (0, _nameName, FactMetaData::valueTypeString)
+ , _sensorWidthFact (0, _sensorWidthName, FactMetaData::valueTypeDouble)
+ , _sensorHeightFact (0, _sensorHeightName, FactMetaData::valueTypeDouble)
+ , _imageWidthFact (0, _imageWidthName, FactMetaData::valueTypeUint32)
+ , _imageHeightFact (0, _imageHeightName, FactMetaData::valueTypeUint32)
+ , _focalLengthFact (0, _focalLengthName, FactMetaData::valueTypeDouble)
+ , _landscapeFact (0, _landscapeName, FactMetaData::valueTypeBool)
+ , _fixedOrientationFact (0, _fixedOrientationName, FactMetaData::valueTypeBool)
+ , _minTriggerIntervalFact (0, _minTriggerIntervalName, FactMetaData::valueTypeDouble)
+{
+ _init(false);
+
+ *this = other;
+}
+
+const CameraSpec& CameraSpec::operator=(const CameraSpec& other)
+{
+ _nameFact.setRawValue (other._nameFact.rawValue());
+ _sensorWidthFact.setRawValue (other._sensorWidthFact.rawValue());
+ _sensorHeightFact.setRawValue (other._sensorHeightFact.rawValue());
+ _imageWidthFact.setRawValue (other._imageWidthFact.rawValue());
+ _imageHeightFact.setRawValue (other._imageHeightFact.rawValue());
+ _focalLengthFact.setRawValue (other._focalLengthFact.rawValue());
+ _landscapeFact.setRawValue (other._landscapeFact.rawValue());
+ _fixedOrientationFact.setRawValue (other._fixedOrientationFact.rawValue());
+ _minTriggerIntervalFact.setRawValue (other._minTriggerIntervalFact.rawValue());
+
+ return *this;
+}
+
+void CameraSpec::_init(bool setDefaults)
+{
+ QQmlEngine::setObjectOwnership(this, QQmlEngine::CppOwnership);
+
+ _nameFact.setMetaData (_metaDataMap[_nameName], setDefaults /* setDefaultFromMetaData */);
+ _sensorWidthFact.setMetaData (_metaDataMap[_sensorWidthName], setDefaults);
+ _sensorHeightFact.setMetaData (_metaDataMap[_sensorHeightName], setDefaults);
+ _imageWidthFact.setMetaData (_metaDataMap[_imageWidthName], setDefaults);
+ _imageHeightFact.setMetaData (_metaDataMap[_imageHeightName], setDefaults);
+ _focalLengthFact.setMetaData (_metaDataMap[_focalLengthName], setDefaults);
+ _landscapeFact.setMetaData (_metaDataMap[_landscapeName], setDefaults);
+ _fixedOrientationFact.setMetaData (_metaDataMap[_fixedOrientationName], setDefaults);
+ _minTriggerIntervalFact.setMetaData (_metaDataMap[_minTriggerIntervalName], setDefaults);
+}
+
+void CameraSpec::setDirty(bool dirty)
+{
+ if (_dirty != dirty) {
+ _dirty = dirty;
+ emit dirtyChanged(_dirty);
+ }
+}
+
+void CameraSpec::save(QJsonObject& json) const
+{
+ Q_UNUSED(json);
+#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();
+ saveObject[_jsonCameraTriggerInTurnaroundKey] = _cameraTriggerInTurnaroundFact.rawValue().toBool();
+
+ 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
+}
+
+bool CameraSpec::load(const QJsonObject& complexObject, QString& errorString)
+{
+ Q_UNUSED(complexObject);
+ Q_UNUSED(errorString);
+#if 0
+ QJsonObject v2Object = complexObject;
+
+ // We need to pull version first to determine what validation/conversion needs to be performed.
+ QList 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 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 },
+ { _jsonCameraTriggerInTurnaroundKey, QJsonValue::Bool, false }, // Should really be required, but it was missing from initial code due to bug
+ };
+ 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));
+ _cameraTriggerInTurnaroundFact.setRawValue (v2Object[_jsonCameraTriggerInTurnaroundKey].toBool(true));
+
+ _refly90Degrees = v2Object[_jsonRefly90DegreesKey].toBool(false);
+
+ QList 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 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;
+#endif
+ return false;
+}
+
+#if 0
+void CameraSpec::recalcImageOnGround(bool valueIsAltitude, double value)
+{
+ var focalLength = missionItem.cameraFocalLength.rawValue
+ var sensorWidth = missionItem.cameraSensorWidth.rawValue
+ var sensorHeight = missionItem.cameraSensorHeight.rawValue
+ var imageWidth = missionItem.cameraResolutionWidth.rawValue
+ var imageHeight = missionItem.cameraResolutionHeight.rawValue
+
+ var altitude = missionItem.gridAltitude.rawValue
+ var groundResolution= missionItem.groundResolution.rawValue
+ var frontalOverlap = missionItem.frontalOverlap.rawValue
+ var sideOverlap = missionItem.sideOverlap.rawValue
+
+ if (focalLength <= 0 || sensorWidth <= 0 || sensorHeight <= 0 || imageWidth <= 0 || imageHeight <= 0 || groundResolution <= 0) {
+ return
+ }
+
+ var imageSizeSideGround //size in side (non flying) direction of the image on the ground
+ var imageSizeFrontGround //size in front (flying) direction of the image on the ground
+ var gridSpacing
+ var cameraTriggerDistance
+
+ if (missionItem.fixedValueIsAltitude.value) {
+ groundResolution = (altitude * sensorWidth * 100) / (imageWidth * focalLength)
+ } else {
+ altitude = (imageWidth * groundResolution * focalLength) / (sensorWidth * 100)
+ }
+
+ if (missionItem.cameraOrientationLandscape.value) {
+ imageSizeSideGround = (imageWidth * groundResolution) / 100
+ imageSizeFrontGround = (imageHeight * groundResolution) / 100
+ } else {
+ imageSizeSideGround = (imageHeight * groundResolution) / 100
+ imageSizeFrontGround = (imageWidth * groundResolution) / 100
+ }
+
+ gridSpacing = imageSizeSideGround * ( (100-sideOverlap) / 100 )
+ cameraTriggerDistance = imageSizeFrontGround * ( (100-frontalOverlap) / 100 )
+
+ if (missionItem.fixedValueIsAltitude.value) {
+ missionItem.groundResolution.rawValue = groundResolution
+ } else {
+ missionItem.gridAltitude.rawValue = altitude
+ }
+ missionItem.gridSpacing.rawValue = gridSpacing
+ missionItem.cameraTriggerDistance.rawValue = cameraTriggerDistance
+}
+#endif
diff --git a/src/MissionManager/CameraSpec.h b/src/MissionManager/CameraSpec.h
new file mode 100644
index 0000000000000000000000000000000000000000..9b1f87aa59c4e6419eed8dae64369dc49e31c4ef
--- /dev/null
+++ b/src/MissionManager/CameraSpec.h
@@ -0,0 +1,100 @@
+/****************************************************************************
+ *
+ * (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 "Fact.h"
+
+class CameraSpec : public QObject
+{
+ Q_OBJECT
+
+public:
+ CameraSpec(QObject* parent = NULL);
+ CameraSpec(const QString& name,
+ double sensorWidth,
+ double sensorHeight,
+ double imageWidth,
+ double imageHeight,
+ double focalLength,
+ bool landscape,
+ bool fixedOrientation,
+ double minTriggerInterval,
+ QObject* parent = NULL);
+ CameraSpec(const CameraSpec& other, QObject* parent);
+
+ const CameraSpec& operator=(const CameraSpec& other);
+
+ // These properties are persisted to Json
+ Q_PROPERTY(Fact* name READ name CONSTANT) ///< Camera name
+ Q_PROPERTY(Fact* sensorWidth READ sensorWidth CONSTANT) ///< Sensor size in millimeters
+ Q_PROPERTY(Fact* sensorHeight READ sensorHeight CONSTANT) ///< Sensor size in millimeters
+ Q_PROPERTY(Fact* imageWidth READ imageWidth CONSTANT) ///< Image size in pixels
+ Q_PROPERTY(Fact* imageHeight READ imageHeight CONSTANT) ///< Image size in pixels
+ Q_PROPERTY(Fact* focalLength READ focalLength CONSTANT) ///< Focal length in millimeters
+ Q_PROPERTY(Fact* landscape READ landscape CONSTANT) ///< true: camera is in landscape orientation
+ Q_PROPERTY(Fact* fixedOrientation READ fixedOrientation CONSTANT) ///< true: camera is in fixed orientation
+ Q_PROPERTY(Fact* minTriggerInterval READ minTriggerInterval CONSTANT) ///< Minimum time in seconds between each photo taken, 0 for not specified
+
+ Fact* name (void) { return &_nameFact; }
+ Fact* sensorWidth (void) { return &_sensorWidthFact; }
+ Fact* sensorHeight (void) { return &_sensorHeightFact; }
+ Fact* imageWidth (void) { return &_imageWidthFact; }
+ Fact* imageHeight (void) { return &_imageHeightFact; }
+ Fact* focalLength (void) { return &_focalLengthFact; }
+ Fact* landscape (void) { return &_landscapeFact; }
+ Fact* fixedOrientation (void) { return &_fixedOrientationFact; }
+ Fact* minTriggerInterval(void) { return &_minTriggerIntervalFact; }
+
+ bool dirty (void) const { return _dirty; }
+ void setDirty (bool dirty);
+
+ void save(QJsonObject& json) const;
+ bool load(const QJsonObject& json, QString& errorString);
+
+signals:
+ void dirtyChanged(bool dirty);
+
+private:
+ void _init(bool setDefaults);
+
+ bool _dirty;
+
+ QMap _metaDataMap;
+
+ Fact _nameFact;
+ Fact _sensorWidthFact;
+ Fact _sensorHeightFact;
+ Fact _imageWidthFact;
+ Fact _imageHeightFact;
+ Fact _focalLengthFact;
+ Fact _landscapeFact;
+ Fact _fixedOrientationFact;
+ Fact _minTriggerIntervalFact;
+
+ static const char* _nameName;
+ static const char* _sensorWidthName;
+ static const char* _sensorHeightName;
+ static const char* _imageWidthName;
+ static const char* _imageHeightName;
+ static const char* _focalLengthName;
+ static const char* _landscapeName;
+ static const char* _fixedOrientationName;
+ static const char* _minTriggerIntervalName;
+
+ static const char* _jsonNameKey;
+ static const char* _jsonSensorWidthKey;
+ static const char* _jsonSensorHeightKey;
+ static const char* _jsonImageWidthKey;
+ static const char* _jsonImageHeightKey;
+ static const char* _jsonFocalLengthKey;
+ static const char* _jsonLandscapeKey;
+ static const char* _jsonFixedOrientationKey;
+ static const char* _jsonMinTriggerIntervalKey;
+};
diff --git a/src/MissionManager/StructureScanComplexItem.cc b/src/MissionManager/StructureScanComplexItem.cc
index 2407ea35a7c05fceee926ad1d5b7b948c249c08c..d2e4859fedc5e10d1d87dcaafe7d2251537fb229 100644
--- a/src/MissionManager/StructureScanComplexItem.cc
+++ b/src/MissionManager/StructureScanComplexItem.cc
@@ -25,9 +25,6 @@ const char* StructureScanComplexItem::jsonComplexItemTypeValue = "Stru
const char* StructureScanComplexItem::_altitudeFactName = "Altitude";
const char* StructureScanComplexItem::_layersFactName = "Layers";
-const char* StructureScanComplexItem::_layerDistanceFactName = "Layer distance";
-const char* StructureScanComplexItem::_cameraTriggerDistanceFactName = "Trigger distance";
-const char* StructureScanComplexItem::_scanDistanceFactName = "Scan distance";
QMap StructureScanComplexItem::_metaDataMap;
@@ -41,11 +38,9 @@ StructureScanComplexItem::StructureScanComplexItem(Vehicle* vehicle, QObject* pa
, _scanDistance (0.0)
, _cameraShots (0)
, _cameraMinTriggerInterval (0)
+ , _cameraCalc (vehicle)
, _altitudeFact (0, _altitudeFactName, FactMetaData::valueTypeDouble)
, _layersFact (0, _layersFactName, FactMetaData::valueTypeUint32)
- , _layerDistanceFact (0, _layerDistanceFactName, FactMetaData::valueTypeDouble)
- , _cameraTriggerDistanceFact(0, _cameraTriggerDistanceFactName, FactMetaData::valueTypeDouble)
- , _scanDistanceFact (0, _scanDistanceFactName, FactMetaData::valueTypeDouble)
{
_editorQml = "qrc:/qml/StructureScanEditor.qml";
@@ -53,26 +48,16 @@ StructureScanComplexItem::StructureScanComplexItem(Vehicle* vehicle, QObject* pa
_metaDataMap = FactMetaData::createMapFromJsonFile(QStringLiteral(":/json/StructureScan.SettingsGroup.json"), NULL /* QObject parent */);
}
- qDebug() << _metaDataMap[_altitudeFactName];
- _altitudeFact.setMetaData (_metaDataMap[_altitudeFactName]);
- _layersFact.setMetaData (_metaDataMap[_layersFactName]);
- _layerDistanceFact.setMetaData (_metaDataMap[_layerDistanceFactName]);
- _cameraTriggerDistanceFact.setMetaData (_metaDataMap[_cameraTriggerDistanceFactName]);
- _scanDistanceFact.setMetaData (_metaDataMap[_scanDistanceFactName]);
+ _altitudeFact.setMetaData (_metaDataMap[_altitudeFactName]);
+ _layersFact.setMetaData (_metaDataMap[_layersFactName]);
- _altitudeFact.setRawValue (_altitudeFact.rawDefaultValue());
- _layersFact.setRawValue (_layersFact.rawDefaultValue());
- _layerDistanceFact.setRawValue (_layerDistanceFact.rawDefaultValue());
- _cameraTriggerDistanceFact.setRawValue (_cameraTriggerDistanceFact.rawDefaultValue());
- _scanDistanceFact.setRawValue (_scanDistanceFact.rawDefaultValue());
+ _altitudeFact.setRawValue (_altitudeFact.rawDefaultValue());
+ _layersFact.setRawValue (_layersFact.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(&_scanDistanceFact, &Fact::valueChanged, this, &StructureScanComplexItem::_setDirty);
+ connect(&_altitudeFact, &Fact::valueChanged, this, &StructureScanComplexItem::_setDirty);
+ connect(&_layersFact, &Fact::valueChanged, this, &StructureScanComplexItem::_setDirty);
connect(this, &StructureScanComplexItem::altitudeRelativeChanged, this, &StructureScanComplexItem::_setDirty);
connect(this, &StructureScanComplexItem::altitudeRelativeChanged, this, &StructureScanComplexItem::coordinateHasRelativeAltitudeChanged);
@@ -86,7 +71,7 @@ StructureScanComplexItem::StructureScanComplexItem(Vehicle* vehicle, QObject* pa
connect(&_flightPolygon, &QGCMapPolygon::pathChanged, this, &StructureScanComplexItem::_flightPathChanged);
- connect(&_scanDistanceFact, &Fact::valueChanged, this, &StructureScanComplexItem::_rebuildFlightPolygon);
+ connect(_cameraCalc.distanceToSurface(), &Fact::valueChanged, this, &StructureScanComplexItem::_rebuildFlightPolygon);
}
void StructureScanComplexItem::_setScanDistance(double scanDistance)
@@ -395,7 +380,7 @@ void StructureScanComplexItem::appendMissionItems(QList& items, QO
items.append(item);
for (int layer=0; layer<_layersFact.rawValue().toInt(); layer++) {
- double layerAltitude = baseAltitude + (layer * _layerDistanceFact.rawValue().toDouble());
+ double layerAltitude = baseAltitude + (layer * _cameraCalc.adjustedFootprintFrontal()->rawValue().toDouble());
for (int i=0; i<_flightPolygon.count(); i++) {
QGeoCoordinate vertexCoord = _flightPolygon.vertexCoordinate(i);
@@ -466,9 +451,9 @@ void StructureScanComplexItem::_polygonDirtyChanged(bool dirty)
}
}
-double StructureScanComplexItem::timeBetweenShots(void) const
+double StructureScanComplexItem::timeBetweenShots(void)
{
- return _cruiseSpeed == 0 ? 0 :_cameraTriggerDistanceFact.rawValue().toDouble() / _cruiseSpeed;
+ return _cruiseSpeed == 0 ? 0 : _cameraCalc.adjustedFootprintSide()->rawValue().toDouble() / _cruiseSpeed;
}
QGeoCoordinate StructureScanComplexItem::coordinate(void) const
@@ -505,5 +490,5 @@ void StructureScanComplexItem::rotateEntryPoint(void)
void StructureScanComplexItem::_rebuildFlightPolygon(void)
{
_flightPolygon = _structurePolygon;
- _flightPolygon.offset(_scanDistanceFact.rawValue().toDouble());
+ _flightPolygon.offset(_cameraCalc.distanceToSurface()->rawValue().toDouble());
}
diff --git a/src/MissionManager/StructureScanComplexItem.h b/src/MissionManager/StructureScanComplexItem.h
index 22dd53de67455ddaed5354e9a9b922a08354ef97..95bd06d6aedb44cb6f7155fe0f9a8a42acd2b04b 100644
--- a/src/MissionManager/StructureScanComplexItem.h
+++ b/src/MissionManager/StructureScanComplexItem.h
@@ -16,6 +16,7 @@
#include "SettingsFact.h"
#include "QGCLoggingCategory.h"
#include "QGCMapPolygon.h"
+#include "CameraCalc.h"
Q_DECLARE_LOGGING_CATEGORY(StructureScanComplexItemLog)
@@ -26,11 +27,9 @@ class StructureScanComplexItem : public ComplexMissionItem
public:
StructureScanComplexItem(Vehicle* vehicle, QObject* parent = NULL);
+ Q_PROPERTY(CameraCalc* cameraCalc READ cameraCalc CONSTANT)
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(Fact* scanDistance READ scanDistance 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)
@@ -38,14 +37,12 @@ public:
Q_PROPERTY(QGCMapPolygon* structurePolygon READ structurePolygon CONSTANT)
Q_PROPERTY(QGCMapPolygon* flightPolygon READ flightPolygon CONSTANT)
- Fact* altitude (void) { return &_altitudeFact; }
- Fact* layers (void) { return &_layersFact; }
- Fact* layerDistance (void) { return &_layerDistanceFact; }
- Fact* cameraTriggerDistance (void) { return &_cameraTriggerDistanceFact; }
- Fact* scanDistance (void) { return &_scanDistanceFact; }
+ CameraCalc* cameraCalc (void) { return &_cameraCalc; }
+ Fact* altitude (void) { return &_altitudeFact; }
+ Fact* layers (void) { return &_layersFact; }
int cameraShots (void) const;
- double timeBetweenShots(void) const;
+ double timeBetweenShots(void);
QGCMapPolygon* structurePolygon(void) { return &_structurePolygon; }
QGCMapPolygon* flightPolygon (void) { return &_flightPolygon; }
@@ -123,20 +120,15 @@ private:
double _timeBetweenShots;
double _cameraMinTriggerInterval;
double _cruiseSpeed;
+ CameraCalc _cameraCalc;
static QMap _metaDataMap;
Fact _altitudeFact;
Fact _layersFact;
- Fact _layerDistanceFact;
- Fact _cameraTriggerDistanceFact;
- Fact _scanDistanceFact;
static const char* _altitudeFactName;
static const char* _layersFactName;
- static const char* _layerDistanceFactName;
- static const char* _cameraTriggerDistanceFactName;
- static const char* _scanDistanceFactName;
};
#endif
diff --git a/src/PlanView/CameraCalc.qml b/src/PlanView/CameraCalc.qml
new file mode 100644
index 0000000000000000000000000000000000000000..7e51f211a519cd81f09692a8cc92ae971dc35004
--- /dev/null
+++ b/src/PlanView/CameraCalc.qml
@@ -0,0 +1,330 @@
+import QtQuick 2.3
+import QtQuick.Controls 1.2
+import QtQuick.Layouts 1.2
+
+import QGroundControl 1.0
+import QGroundControl.ScreenTools 1.0
+import QGroundControl.Controls 1.0
+import QGroundControl.FactControls 1.0
+import QGroundControl.Palette 1.0
+
+// Camera calculator section for mission item editors
+Column {
+ anchors.left: parent.left
+ anchors.right: parent.right
+ spacing: _margin
+
+ property var cameraCalc
+ property bool vehicleFlightIsFrontal: true
+ property string distanceToSurfaceLabel
+ property string frontalDistanceLabel
+ property string sideDistanceLabel
+
+ property real _margin: ScreenTools.defaultFontPixelWidth / 2
+ property int _cameraIndex: 1
+ property real _fieldWidth: ScreenTools.defaultFontPixelWidth * 10.5
+ property var _cameraList: [ qsTr("Manual (no camera specs)"), qsTr("Custom Camera") ]
+ property var _vehicle: QGroundControl.multiVehicleManager.activeVehicle ? QGroundControl.multiVehicleManager.activeVehicle : QGroundControl.multiVehicleManager.offlineEditingVehicle
+ property var _vehicleCameraList: _vehicle ? _vehicle.staticCameraList : []
+
+ readonly property int _gridTypeManual: 0
+ readonly property int _gridTypeCustomCamera: 1
+ readonly property int _gridTypeCamera: 2
+
+ Component.onCompleted: {
+ for (var i=0; i<_vehicle.staticCameraList.length; i++) {
+ _cameraList.push(_vehicle.staticCameraList[i].name)
+ }
+ gridTypeCombo.model = _cameraList
+ if (cameraCalc.cameraSpecType === CameraCalc.CameraSpecNone) {
+ gridTypeCombo.currentIndex = _gridTypeManual
+ } else {
+ var index = -1
+ for (index=0; index<_cameraList.length; index++) {
+ if (_cameraList[index] == cameraCalc.knownCameraName) {
+ break;
+ }
+ }
+ cameraCalc.fixedOrientation.value = false
+ if (index == _cameraList.length) {
+ gridTypeCombo.currentIndex = _gridTypeCustomCamera
+ } else {
+ gridTypeCombo.currentIndex = index
+ if (index != 1) {
+ // Specific camera is selected
+ var camera = _vehicleCameraList[index - _gridTypeCamera]
+ cameraCalc.fixedOrientation.value = camera.fixedOrientation
+ cameraCalc.minTriggerInterval.value = camera.minTriggerInterval
+ }
+ }
+ }
+ }
+
+ QGCPalette { id: qgcPal; colorGroupEnabled: true }
+
+ ExclusiveGroup {
+ id: cameraOrientationGroup
+ }
+
+ ExclusiveGroup { id: fixedValueGroup }
+
+ SectionHeader {
+ id: cameraHeader
+ text: qsTr("Camera")
+ showSpacer: false
+ }
+
+ Column {
+ anchors.left: parent.left
+ anchors.right: parent.right
+ spacing: _margin
+ visible: cameraHeader.checked
+
+ QGCComboBox {
+ id: gridTypeCombo
+ anchors.left: parent.left
+ anchors.right: parent.right
+ model: _cameraList
+ currentIndex: -1
+
+ onActivated: {
+ if (index == _gridTypeManual) {
+ cameraCalc.cameraSpecType = CameraCalc.CameraSpecNone
+ cameraCalc.valueSetIsDistance.value = false
+ } else if (index == _gridTypeCustomCamera) {
+ cameraCalc.cameraSpecType = CameraCalc.CameraSpecCustom
+ cameraCalc.knownCameraName = gridTypeCombo.textAt(index)
+ cameraCalc.fixedOrientation.value = false
+ cameraCalc.minTriggerInterval.value = 0
+ } else {
+ cameraCalc.cameraSpecType = CameraCalc.CameraSpecKnown
+ cameraCalc.knownCameraName = gridTypeCombo.textAt(index)
+ }
+ }
+ } // QGCComboxBox
+
+ // Camera based grid ui
+ Column {
+ anchors.left: parent.left
+ anchors.right: parent.right
+ spacing: _margin
+ visible: cameraCalc.cameraSpecType !== CameraCalc.CameraSpecNone
+
+ Row {
+ spacing: _margin
+ anchors.horizontalCenter: parent.horizontalCenter
+ visible: !cameraCalc.fixedOrientation.value
+
+ QGCRadioButton {
+ width: _editFieldWidth
+ text: "Landscape"
+ checked: !!cameraCalc.landscape.value
+ exclusiveGroup: cameraOrientationGroup
+ onClicked: cameraCalc.landscape.value = 1
+ }
+
+ QGCRadioButton {
+ id: cameraOrientationPortrait
+ text: "Portrait"
+ checked: !cameraCalc.landscape.value
+ exclusiveGroup: cameraOrientationGroup
+ onClicked: cameraCalc.landscape.value = 0
+ }
+ }
+
+ // Custom camera specs
+ Column {
+ id: custCameraCol
+ anchors.left: parent.left
+ anchors.right: parent.right
+ spacing: _margin
+ visible: cameraCalc.cameraSpecType === CameraCalc.CameraSpecCustom
+
+ RowLayout {
+ anchors.left: parent.left
+ anchors.right: parent.right
+ spacing: _margin
+ Item { Layout.fillWidth: true }
+ QGCLabel {
+ Layout.preferredWidth: _root._fieldWidth
+ text: qsTr("Width")
+ }
+ QGCLabel {
+ Layout.preferredWidth: _root._fieldWidth
+ text: qsTr("Height")
+ }
+ }
+
+ RowLayout {
+ anchors.left: parent.left
+ anchors.right: parent.right
+ spacing: _margin
+ QGCLabel { text: qsTr("Sensor"); Layout.fillWidth: true }
+ FactTextField {
+ Layout.preferredWidth: _root._fieldWidth
+ fact: cameraCalc.sensorWidth
+ }
+ FactTextField {
+ Layout.preferredWidth: _root._fieldWidth
+ fact: cameraCalc.sensorHeight
+ }
+ }
+
+ RowLayout {
+ anchors.left: parent.left
+ anchors.right: parent.right
+ spacing: _margin
+ QGCLabel { text: qsTr("Image"); Layout.fillWidth: true }
+ FactTextField {
+ Layout.preferredWidth: _root._fieldWidth
+ fact: cameraCalc.imageWidth
+ }
+ FactTextField {
+ Layout.preferredWidth: _root._fieldWidth
+ fact: cameraCalc.imageHeight
+ }
+ }
+
+ RowLayout {
+ anchors.left: parent.left
+ anchors.right: parent.right
+ spacing: _margin
+ QGCLabel {
+ text: qsTr("Focal length")
+ Layout.fillWidth: true
+ }
+ FactTextField {
+ Layout.preferredWidth: _root._fieldWidth
+ fact: cameraCalc.focalLength
+ }
+ }
+
+ } // Column - custom camera specs
+
+ RowLayout {
+ anchors.left: parent.left
+ anchors.right: parent.right
+ spacing: _margin
+ Item { Layout.fillWidth: true }
+ QGCLabel {
+ Layout.preferredWidth: _root._fieldWidth
+ text: qsTr("Front Lap")
+ }
+ QGCLabel {
+ Layout.preferredWidth: _root._fieldWidth
+ text: qsTr("Side Lap")
+ }
+ }
+
+ RowLayout {
+ anchors.left: parent.left
+ anchors.right: parent.right
+ spacing: _margin
+ QGCLabel { text: qsTr("Overlap"); Layout.fillWidth: true }
+ FactTextField {
+ Layout.preferredWidth: _root._fieldWidth
+ fact: cameraCalc.frontalOverlap
+ }
+ FactTextField {
+ Layout.preferredWidth: _root._fieldWidth
+ fact: cameraCalc.sideOverlap
+ }
+ }
+
+ QGCLabel {
+ wrapMode: Text.WordWrap
+ text: qsTr("Select one:")
+ Layout.preferredWidth: parent.width
+ Layout.columnSpan: 2
+ }
+
+ GridLayout {
+ anchors.left: parent.left
+ anchors.right: parent.right
+ columnSpacing: _margin
+ rowSpacing: _margin
+ columns: 2
+
+ QGCRadioButton {
+ id: fixedDistanceRadio
+ text: distanceToSurfaceLabel
+ checked: !!cameraCalc.valueSetIsDistance.value
+ exclusiveGroup: fixedValueGroup
+ onClicked: cameraCalc.valueSetIsDistance.value = 1
+ }
+
+ FactTextField {
+ fact: cameraCalc.distanceToSurface
+ enabled: fixedDistanceRadio.checked
+ Layout.fillWidth: true
+ }
+
+ QGCRadioButton {
+ id: fixedImageDensityRadio
+ text: qsTr("Image density")
+ checked: !cameraCalc.valueSetIsDistance.value
+ exclusiveGroup: fixedValueGroup
+ onClicked: cameraCalc.valueSetIsDistance.value = 0
+ }
+
+ FactTextField {
+ fact: cameraCalc.imageDensity
+ enabled: fixedImageDensityRadio.checked
+ Layout.fillWidth: true
+ }
+ }
+
+ // Calculated values
+ GridLayout {
+ anchors.left: parent.left
+ anchors.right: parent.right
+ columnSpacing: _margin
+ rowSpacing: _margin
+ columns: 2
+
+ QGCLabel { text: frontalDistanceLabel }
+ FactTextField {
+ Layout.fillWidth: true
+ fact: cameraCalc.adjustedFootprintFrontal
+ enabled: false
+ }
+
+ QGCLabel { text: sideDistanceLabel }
+ FactTextField {
+ Layout.fillWidth: true
+ fact: cameraCalc.adjustedFootprintSide
+ enabled: false
+ }
+ } // GridLayout
+
+ } // Column - Camera spec based ui
+
+ // No camera spec ui
+ GridLayout {
+ anchors.left: parent.left
+ anchors.right: parent.right
+ columnSpacing: _margin
+ rowSpacing: _margin
+ columns: 2
+ visible: cameraCalc.cameraSpecType === CameraCalc.CameraSpecNone
+
+ QGCLabel { text: distanceToSurfaceLabel }
+ FactTextField {
+ fact: cameraCalc.distanceToSurface
+ Layout.fillWidth: true
+ }
+
+ QGCLabel { text: frontalDistanceLabel }
+ FactTextField {
+ Layout.fillWidth: true
+ fact: cameraCalc.adjustedFootprintFrontal
+ }
+
+ QGCLabel { text: sideDistanceLabel }
+ FactTextField {
+ Layout.fillWidth: true
+ fact: cameraCalc.adjustedFootprintSide
+ }
+ } // GridLayout
+ } // Column - Camera Section
+} // Column
diff --git a/src/PlanView/StructureScanEditor.qml b/src/PlanView/StructureScanEditor.qml
index 34301478849f0288c291312ac436d07b5093c3f6..e8f6a4c6ff65e2486ac289f1a73ab0199b77a495 100644
--- a/src/PlanView/StructureScanEditor.qml
+++ b/src/PlanView/StructureScanEditor.qml
@@ -60,7 +60,7 @@ Rectangle {
QGCLabel {
anchors.left: parent.left
anchors.right: parent.right
- text: qsTr("WARNING: WORK IN PROGRESS. USE AT YOUR OWN RISK. MEANT FOR DISCUSSION ONLY. DO NOT REPORT BUGS.")
+ text: qsTr("WARNING: WORK IN PROGRESS. DO NOT FLY. NO BUG REPORTS.")
wrapMode: Text.WordWrap
color: qgcPal.warningText
}
@@ -82,59 +82,61 @@ Rectangle {
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
- }
+ CameraCalc {
+ cameraCalc: missionItem.cameraCalc
+ vehicleFlightIsFrontal: false
+ distanceToSurfaceLabel: qsTr("Scan Distance")
+ frontalDistanceLabel: qsTr("Layer Height")
+ sideDistanceLabel: qsTr("Trigger Distance")
+ }
- QGCLabel { text: qsTr("Layer distance") }
- FactTextField {
- fact: missionItem.layerDistance
- Layout.fillWidth: true
- }
+ SectionHeader {
+ id: scanHeader
+ text: qsTr("Scan")
+ }
- QGCLabel { text: qsTr("Scan distance") }
- FactTextField {
- fact: missionItem.scanDistance
- Layout.fillWidth: true
+ Column {
+ anchors.left: parent.left
+ anchors.right: parent.right
+ spacing: _margin
+ visible: scanHeader.checked
+
+ GridLayout {
+ anchors.left: parent.left
+ anchors.right: parent.right
+ columnSpacing: _margin
+ rowSpacing: _margin
+ columns: 2
+
+ QGCLabel { text: qsTr("Layers") }
+ FactTextField {
+ fact: missionItem.layers
+ Layout.fillWidth: true
+ }
+
+ QGCLabel { text: qsTr("Altitude") }
+ FactTextField {
+ fact: missionItem.altitude
+ Layout.fillWidth: true
+ }
+
+ QGCCheckBox {
+ text: qsTr("Relative altitude")
+ checked: missionItem.altitudeRelative
+ Layout.columnSpan: 2
+ onClicked: missionItem.altitudeRelative = checked
+ }
}
- QGCLabel { text: qsTr("Trigger Distance") }
- FactTextField {
- fact: missionItem.cameraTriggerDistance
- Layout.fillWidth: true
- }
+ QGCLabel { text: qsTr("Point camera to structure using:") }
+ QGCRadioButton { text: qsTr("Vehicle yaw"); enabled: false }
+ QGCRadioButton { text: qsTr("Gimbal yaw"); checked: true; enabled: false }
- QGCCheckBox {
- text: qsTr("Relative altitude")
- checked: missionItem.altitudeRelative
- Layout.columnSpan: 2
- onClicked: missionItem.altitudeRelative = checked
+ QGCButton {
+ text: qsTr("Rotate entry point")
+ onClicked: missionItem.rotateEntryPoint()
}
- }
-
- 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()
- }
+ } // Column - Scan
SectionHeader {
id: statsHeader
@@ -152,6 +154,5 @@ Rectangle {
QGCLabel { text: qsTr("Photo interval") }
QGCLabel { text: missionItem.timeBetweenShots.toFixed(1) + " " + qsTr("secs") }
}
- }
-}
-
+ } // Column
+} // Rectangle
diff --git a/src/QGCApplication.cc b/src/QGCApplication.cc
index 508f21d222a4f8ae0751977f04f7f87252eaed17..5b61c08937c3e56589b33f83e049bcd583a95903 100644
--- a/src/QGCApplication.cc
+++ b/src/QGCApplication.cc
@@ -81,6 +81,7 @@
#include "SettingsManager.h"
#include "QGCCorePlugin.h"
#include "QGCCameraManager.h"
+#include "CameraCalc.h"
#ifndef NO_SERIAL_LINK
#include "SerialLink.h"
@@ -348,6 +349,7 @@ void QGCApplication::_initCommon(void)
qmlRegisterUncreatableType ("QGroundControl", 1, 0, "CoordinateVector", "Reference only");
qmlRegisterUncreatableType ("QGroundControl", 1, 0, "QmlObjectListModel", "Reference only");
qmlRegisterUncreatableType ("QGroundControl", 1, 0, "MissionCommandTree", "Reference only");
+ qmlRegisterUncreatableType ("QGroundControl", 1, 0, "CameraCalc", "Reference only");
qmlRegisterUncreatableType ("QGroundControl.AutoPilotPlugin", 1, 0, "AutoPilotPlugin", "Reference only");
qmlRegisterUncreatableType ("QGroundControl.AutoPilotPlugin", 1, 0, "VehicleComponent", "Reference only");
diff --git a/src/QmlControls/QGroundControl.Controls.qmldir b/src/QmlControls/QGroundControl.Controls.qmldir
index ee2a59064d9a97fe243c4fade18a27996c91c2a4..ad49c42db7e48ea25142897cd184e6ab7a540404 100644
--- a/src/QmlControls/QGroundControl.Controls.qmldir
+++ b/src/QmlControls/QGroundControl.Controls.qmldir
@@ -2,6 +2,7 @@ Module QGroundControl.Controls
AnalyzePage 1.0 AnalyzePage.qml
AppMessages 1.0 AppMessages.qml
+CameraCalc 1.0 CameraCalc.qml
CameraSection 1.0 CameraSection.qml
ClickableColor 1.0 ClickableColor.qml
DropButton 1.0 DropButton.qml