17 changed files with 1561 additions and 144 deletions
@ -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,355 @@ |
|||||||
|
/****************************************************************************
|
||||||
|
* |
||||||
|
* (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 |
||||||
|
{ |
||||||
|
Q_UNUSED(json); |
||||||
|
|
||||||
|
#if 0 |
||||||
|
QJsonObject saveObject; |
||||||
|
|
||||||
|
saveObject[JsonHelper::jsonVersionKey] = 3; |
||||||
|
saveObject[VisualMissionItem::jsonTypeKey] = VisualMissionItem::jsonTypeComplexItemValue; |
||||||
|
saveObject[ComplexMissionItem::jsonComplexItemTypeKey] = jsonComplexItemTypeValue; |
||||||
|
saveObject[_jsonManualGridKey] = _manualGridFact.rawValue().toBool(); |
||||||
|
saveObject[_jsonFixedValueIsAltitudeKey] = _fixedValueIsAltitudeFact.rawValue().toBool(); |
||||||
|
saveObject[_jsonHoverAndCaptureKey] = _hoverAndCaptureFact.rawValue().toBool(); |
||||||
|
saveObject[_jsonRefly90DegreesKey] = _refly90Degrees; |
||||||
|
saveObject[_jsonCameraTriggerDistanceKey] = _cameraTriggerDistanceFact.rawValue().toDouble(); |
||||||
|
saveObject[_jsonCameraTriggerInTurnaroundKey] = _cameraTriggerInTurnaroundFact.rawValue().toBool(); |
||||||
|
|
||||||
|
QJsonObject gridObject; |
||||||
|
gridObject[_jsonGridAltitudeKey] = _gridAltitudeFact.rawValue().toDouble(); |
||||||
|
gridObject[_jsonGridAltitudeRelativeKey] = _gridAltitudeRelativeFact.rawValue().toBool(); |
||||||
|
gridObject[_jsonGridAngleKey] = _gridAngleFact.rawValue().toDouble(); |
||||||
|
gridObject[_jsonGridSpacingKey] = _gridSpacingFact.rawValue().toDouble(); |
||||||
|
gridObject[_jsonGridEntryLocationKey] = _gridEntryLocationFact.rawValue().toDouble(); |
||||||
|
gridObject[_jsonTurnaroundDistKey] = _turnaroundDistFact.rawValue().toDouble(); |
||||||
|
|
||||||
|
saveObject[_jsonGridObjectKey] = gridObject; |
||||||
|
|
||||||
|
if (!_manualGridFact.rawValue().toBool()) { |
||||||
|
QJsonObject cameraObject; |
||||||
|
cameraObject[_jsonCameraNameKey] = _cameraFact.rawValue().toString(); |
||||||
|
cameraObject[_jsonCameraOrientationLandscapeKey] = _cameraOrientationLandscapeFact.rawValue().toBool(); |
||||||
|
cameraObject[_jsonCameraSensorWidthKey] = _cameraSensorWidthFact.rawValue().toDouble(); |
||||||
|
cameraObject[_jsonCameraSensorHeightKey] = _cameraSensorHeightFact.rawValue().toDouble(); |
||||||
|
cameraObject[_jsonCameraResolutionWidthKey] = _cameraResolutionWidthFact.rawValue().toDouble(); |
||||||
|
cameraObject[_jsonCameraResolutionHeightKey] = _cameraResolutionHeightFact.rawValue().toDouble(); |
||||||
|
cameraObject[_jsonCameraFocalLengthKey] = _cameraFocalLengthFact.rawValue().toDouble(); |
||||||
|
cameraObject[_jsonCameraMinTriggerIntervalKey] = _cameraMinTriggerInterval; |
||||||
|
cameraObject[_jsonGroundResolutionKey] = _groundResolutionFact.rawValue().toDouble(); |
||||||
|
cameraObject[_jsonFrontalOverlapKey] = _frontalOverlapFact.rawValue().toInt(); |
||||||
|
cameraObject[_jsonSideOverlapKey] = _sideOverlapFact.rawValue().toInt(); |
||||||
|
|
||||||
|
saveObject[_jsonCameraObjectKey] = cameraObject; |
||||||
|
} |
||||||
|
|
||||||
|
// Polygon shape
|
||||||
|
_mapPolygon.saveToJson(saveObject); |
||||||
|
|
||||||
|
missionItems.append(saveObject); |
||||||
|
#endif |
||||||
|
} |
||||||
|
|
||||||
|
bool CameraCalc::load(const QJsonObject& complexObject, QString& errorString) |
||||||
|
{ |
||||||
|
Q_UNUSED(complexObject); |
||||||
|
Q_UNUSED(errorString); |
||||||
|
#if 0 |
||||||
|
QJsonObject v2Object = complexObject; |
||||||
|
|
||||||
|
// We need to pull version first to determine what validation/conversion needs to be performed.
|
||||||
|
QList<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 @@ |
|||||||
|
/****************************************************************************
|
||||||
|
* |
||||||
|
* (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 @@ |
|||||||
|
[ |
||||||
|
{ |
||||||
|
"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,397 @@ |
|||||||
|
/****************************************************************************
|
||||||
|
* |
||||||
|
* (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 |
||||||
|
{ |
||||||
|
Q_UNUSED(json); |
||||||
|
#if 0 |
||||||
|
QJsonObject saveObject; |
||||||
|
|
||||||
|
saveObject[JsonHelper::jsonVersionKey] = 3; |
||||||
|
saveObject[VisualMissionItem::jsonTypeKey] = VisualMissionItem::jsonTypeComplexItemValue; |
||||||
|
saveObject[ComplexMissionItem::jsonComplexItemTypeKey] = jsonComplexItemTypeValue; |
||||||
|
saveObject[_jsonManualGridKey] = _manualGridFact.rawValue().toBool(); |
||||||
|
saveObject[_jsonFixedValueIsAltitudeKey] = _fixedValueIsAltitudeFact.rawValue().toBool(); |
||||||
|
saveObject[_jsonHoverAndCaptureKey] = _hoverAndCaptureFact.rawValue().toBool(); |
||||||
|
saveObject[_jsonRefly90DegreesKey] = _refly90Degrees; |
||||||
|
saveObject[_jsonCameraTriggerDistanceKey] = _cameraTriggerDistanceFact.rawValue().toDouble(); |
||||||
|
saveObject[_jsonCameraTriggerInTurnaroundKey] = _cameraTriggerInTurnaroundFact.rawValue().toBool(); |
||||||
|
|
||||||
|
QJsonObject gridObject; |
||||||
|
gridObject[_jsonGridAltitudeKey] = _gridAltitudeFact.rawValue().toDouble(); |
||||||
|
gridObject[_jsonGridAltitudeRelativeKey] = _gridAltitudeRelativeFact.rawValue().toBool(); |
||||||
|
gridObject[_jsonGridAngleKey] = _gridAngleFact.rawValue().toDouble(); |
||||||
|
gridObject[_jsonGridSpacingKey] = _gridSpacingFact.rawValue().toDouble(); |
||||||
|
gridObject[_jsonGridEntryLocationKey] = _gridEntryLocationFact.rawValue().toDouble(); |
||||||
|
gridObject[_jsonTurnaroundDistKey] = _turnaroundDistFact.rawValue().toDouble(); |
||||||
|
|
||||||
|
saveObject[_jsonGridObjectKey] = gridObject; |
||||||
|
|
||||||
|
if (!_manualGridFact.rawValue().toBool()) { |
||||||
|
QJsonObject cameraObject; |
||||||
|
cameraObject[_jsonCameraNameKey] = _cameraFact.rawValue().toString(); |
||||||
|
cameraObject[_jsonCameraOrientationLandscapeKey] = _cameraOrientationLandscapeFact.rawValue().toBool(); |
||||||
|
cameraObject[_jsonCameraSensorWidthKey] = _cameraSensorWidthFact.rawValue().toDouble(); |
||||||
|
cameraObject[_jsonCameraSensorHeightKey] = _cameraSensorHeightFact.rawValue().toDouble(); |
||||||
|
cameraObject[_jsonCameraResolutionWidthKey] = _cameraResolutionWidthFact.rawValue().toDouble(); |
||||||
|
cameraObject[_jsonCameraResolutionHeightKey] = _cameraResolutionHeightFact.rawValue().toDouble(); |
||||||
|
cameraObject[_jsonCameraFocalLengthKey] = _cameraFocalLengthFact.rawValue().toDouble(); |
||||||
|
cameraObject[_jsonCameraMinTriggerIntervalKey] = _cameraMinTriggerInterval; |
||||||
|
cameraObject[_jsonGroundResolutionKey] = _groundResolutionFact.rawValue().toDouble(); |
||||||
|
cameraObject[_jsonFrontalOverlapKey] = _frontalOverlapFact.rawValue().toInt(); |
||||||
|
cameraObject[_jsonSideOverlapKey] = _sideOverlapFact.rawValue().toInt(); |
||||||
|
|
||||||
|
saveObject[_jsonCameraObjectKey] = cameraObject; |
||||||
|
} |
||||||
|
|
||||||
|
// Polygon shape
|
||||||
|
_mapPolygon.saveToJson(saveObject); |
||||||
|
|
||||||
|
missionItems.append(saveObject); |
||||||
|
#endif |
||||||
|
} |
||||||
|
|
||||||
|
bool CameraSpec::load(const QJsonObject& complexObject, QString& errorString) |
||||||
|
{ |
||||||
|
Q_UNUSED(complexObject); |
||||||
|
Q_UNUSED(errorString); |
||||||
|
#if 0 |
||||||
|
QJsonObject v2Object = complexObject; |
||||||
|
|
||||||
|
// We need to pull version first to determine what validation/conversion needs to be performed.
|
||||||
|
QList<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 @@ |
|||||||
|
/****************************************************************************
|
||||||
|
* |
||||||
|
* (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 @@ |
|||||||
|
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