Browse Source

New CameraCalc reusable code for complex items

Converted Structure Scan to use CameraCalc such that it now works with
camera specs.
QGC4.4
DonLakeFlyer 8 years ago
parent
commit
073ab854ae
  1. 4
      qgroundcontrol.pro
  2. 3
      qgroundcontrol.qrc
  3. 77
      src/FactSystem/Fact.cc
  4. 4
      src/FactSystem/Fact.h
  5. 33
      src/FirmwarePlugin/CameraMetaData.h
  6. 62
      src/MissionManager/CameraCalc.FactMetaData.json
  7. 351
      src/MissionManager/CameraCalc.cc
  8. 102
      src/MissionManager/CameraCalc.h
  9. 71
      src/MissionManager/CameraSpec.FactMetaData.json
  10. 394
      src/MissionManager/CameraSpec.cc
  11. 100
      src/MissionManager/CameraSpec.h
  12. 39
      src/MissionManager/StructureScanComplexItem.cc
  13. 22
      src/MissionManager/StructureScanComplexItem.h
  14. 330
      src/PlanView/CameraCalc.qml
  15. 103
      src/PlanView/StructureScanEditor.qml
  16. 2
      src/QGCApplication.cc
  17. 1
      src/QmlControls/QGroundControl.Controls.qmldir

4
qgroundcontrol.pro

@ -505,7 +505,9 @@ HEADERS += \ @@ -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 += \ @@ -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 \

3
qgroundcontrol.qrc

@ -44,6 +44,7 @@ @@ -44,6 +44,7 @@
<file alias="PX4FlowSensor.qml">src/VehicleSetup/PX4FlowSensor.qml</file>
<file alias="QGroundControl/Controls/AnalyzePage.qml">src/AnalyzeView/AnalyzePage.qml</file>
<file alias="QGroundControl/Controls/AppMessages.qml">src/QmlControls/AppMessages.qml</file>
<file alias="QGroundControl/Controls/CameraCalc.qml">src/PlanView/CameraCalc.qml</file>
<file alias="QGroundControl/Controls/CameraSection.qml">src/PlanView/CameraSection.qml</file>
<file alias="QGroundControl/Controls/ClickableColor.qml">src/QmlControls/ClickableColor.qml</file>
<file alias="QGroundControl/Controls/DropButton.qml">src/QmlControls/DropButton.qml</file>
@ -203,6 +204,8 @@ @@ -203,6 +204,8 @@
<file alias="FWLandingPattern.FactMetaData.json">src/MissionManager/FWLandingPattern.FactMetaData.json</file>
<file alias="USBBoardInfo.json">src/comm/USBBoardInfo.json</file>
<file alias="CameraSection.FactMetaData.json">src/MissionManager/CameraSection.FactMetaData.json</file>
<file alias="CameraCalc.FactMetaData.json">src/MissionManager/CameraCalc.FactMetaData.json</file>
<file alias="CameraSpec.FactMetaData.json">src/MissionManager/CameraSpec.FactMetaData.json</file>
<file alias="SpeedSection.FactMetaData.json">src/MissionManager/SpeedSection.FactMetaData.json</file>
<file alias="MissionSettings.FactMetaData.json">src/MissionManager/MissionSettings.FactMetaData.json</file>
<file alias="Vehicle/VehicleFact.json">src/Vehicle/VehicleFact.json</file>

77
src/FactSystem/Fact.cc

@ -89,7 +89,7 @@ void Fact::forceSetRawValue(const QVariant& value) @@ -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) @@ -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) @@ -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) @@ -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) @@ -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 @@ -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) @@ -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) @@ -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 @@ -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 @@ -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) @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -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) @@ -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) @@ -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 @@ -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) @@ -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 @@ -610,7 +613,7 @@ double Fact::increment(void) const
if (_metaData) {
return _metaData->increment();
} else {
qWarning() << kMissingMetadata;
qWarning() << kMissingMetadata << name();
}
return std::numeric_limits<double>::quiet_NaN();
}
@ -620,7 +623,7 @@ bool Fact::hasControl(void) const @@ -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 @@ -630,7 +633,7 @@ bool Fact::readOnly(void) const
if (_metaData) {
return _metaData->readOnly();
} else {
qWarning() << kMissingMetadata;
qWarning() << kMissingMetadata << name();
return false;
}
}

4
src/FactSystem/Fact.h

@ -136,7 +136,9 @@ public: @@ -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; }

33
src/FirmwarePlugin/CameraMetaData.h

@ -7,8 +7,7 @@ @@ -7,8 +7,7 @@
*
****************************************************************************/
#ifndef CameraMetaData_H
#define CameraMetaData_H
#pragma once
#include <QObject>
@ -29,15 +28,25 @@ public: @@ -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: @@ -50,5 +59,3 @@ private:
bool _fixedOrientation;
double _minTriggerInterval;
};
#endif

62
src/MissionManager/CameraCalc.FactMetaData.json

@ -0,0 +1,62 @@ @@ -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
}
]

351
src/MissionManager/CameraCalc.cc

@ -0,0 +1,351 @@ @@ -0,0 +1,351 @@
/****************************************************************************
*
* (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 "CameraCalc.h"
#include "JsonHelper.h"
#include "Vehicle.h"
#include "CameraMetaData.h"
#include <QQmlEngine>
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<CameraMetaData*>();
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
{
#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)
{
#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 },
{ _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<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;
#endif
return false;
}

102
src/MissionManager/CameraCalc.h

@ -0,0 +1,102 @@ @@ -0,0 +1,102 @@
/****************************************************************************
*
* (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.
*
****************************************************************************/
#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<QString, FactMetaData*> _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;
};

71
src/MissionManager/CameraSpec.FactMetaData.json

@ -0,0 +1,71 @@ @@ -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
}
]

394
src/MissionManager/CameraSpec.cc

@ -0,0 +1,394 @@ @@ -0,0 +1,394 @@
/****************************************************************************
*
* (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 "CameraSpec.h"
#include "JsonHelper.h"
#include <QQmlEngine>
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
{
#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)
{
#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 },
{ _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<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;
#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

100
src/MissionManager/CameraSpec.h

@ -0,0 +1,100 @@ @@ -0,0 +1,100 @@
/****************************************************************************
*
* (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.
*
****************************************************************************/
#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<QString, FactMetaData*> _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;
};

39
src/MissionManager/StructureScanComplexItem.cc

@ -25,9 +25,6 @@ const char* StructureScanComplexItem::jsonComplexItemTypeValue = "Stru @@ -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<QString, FactMetaData*> StructureScanComplexItem::_metaDataMap;
@ -41,11 +38,9 @@ StructureScanComplexItem::StructureScanComplexItem(Vehicle* vehicle, QObject* pa @@ -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 @@ -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 @@ -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<MissionItem*>& items, QO @@ -395,7 +380,7 @@ void StructureScanComplexItem::appendMissionItems(QList<MissionItem*>& 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) @@ -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) @@ -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());
}

22
src/MissionManager/StructureScanComplexItem.h

@ -16,6 +16,7 @@ @@ -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 @@ -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: @@ -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: @@ -123,20 +120,15 @@ private:
double _timeBetweenShots;
double _cameraMinTriggerInterval;
double _cruiseSpeed;
CameraCalc _cameraCalc;
static QMap<QString, FactMetaData*> _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

330
src/PlanView/CameraCalc.qml

@ -0,0 +1,330 @@ @@ -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

103
src/PlanView/StructureScanEditor.qml

@ -60,7 +60,7 @@ Rectangle { @@ -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 { @@ -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 { @@ -152,6 +154,5 @@ Rectangle {
QGCLabel { text: qsTr("Photo interval") }
QGCLabel { text: missionItem.timeBetweenShots.toFixed(1) + " " + qsTr("secs") }
}
}
}
} // Column
} // Rectangle

2
src/QGCApplication.cc

@ -81,6 +81,7 @@ @@ -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) @@ -348,6 +349,7 @@ void QGCApplication::_initCommon(void)
qmlRegisterUncreatableType<CoordinateVector> ("QGroundControl", 1, 0, "CoordinateVector", "Reference only");
qmlRegisterUncreatableType<QmlObjectListModel> ("QGroundControl", 1, 0, "QmlObjectListModel", "Reference only");
qmlRegisterUncreatableType<MissionCommandTree> ("QGroundControl", 1, 0, "MissionCommandTree", "Reference only");
qmlRegisterUncreatableType<CameraCalc> ("QGroundControl", 1, 0, "CameraCalc", "Reference only");
qmlRegisterUncreatableType<AutoPilotPlugin> ("QGroundControl.AutoPilotPlugin", 1, 0, "AutoPilotPlugin", "Reference only");
qmlRegisterUncreatableType<VehicleComponent> ("QGroundControl.AutoPilotPlugin", 1, 0, "VehicleComponent", "Reference only");

1
src/QmlControls/QGroundControl.Controls.qmldir

@ -2,6 +2,7 @@ Module QGroundControl.Controls @@ -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

Loading…
Cancel
Save