Browse Source
Converted Structure Scan to use CameraCalc such that it now works with camera specs.QGC4.4
17 changed files with 1554 additions and 144 deletions
@ -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 |
||||
} |
||||
] |
@ -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; |
||||
} |
@ -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; |
||||
}; |
@ -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 |
||||
} |
||||
] |
@ -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 |
@ -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; |
||||
}; |
@ -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 |
Loading…
Reference in new issue