/**************************************************************************** * * (c) 2009-2016 QGROUNDCONTROL PROJECT * * QGroundControl is licensed according to the terms in the file * COPYING.md in the root of the source code directory. * ****************************************************************************/ #include "CameraSpec.h" #include "JsonHelper.h" #include const char* CameraSpec::_jsonSensorWidthKey = "sensorWidth"; const char* CameraSpec::_jsonSensorHeightKey = "sensorHeight"; const char* CameraSpec::_jsonImageWidthKey = "imageWidth"; const char* CameraSpec::_jsonImageHeightKey = "imageHeight"; const char* CameraSpec::_jsonFocalLengthKey = "focalLength"; const char* CameraSpec::_jsonMinTriggerIntervalKey = "minTriggerInterval"; const char* CameraSpec::_jsonNameKey = "name"; const char* CameraSpec::_jsonLandscapeKey = "orientationLandscape"; const char* CameraSpec::_nameName = "Camera"; const char* CameraSpec::_sensorWidthName = "SensorWidth"; const char* CameraSpec::_sensorHeightName = "SensorHeight"; const char* CameraSpec::_imageWidthName = "ImageWidth"; const char* CameraSpec::_imageHeightName = "ImageHeight"; const char* CameraSpec::_focalLengthName = "FocalLength"; const char* CameraSpec::_landscapeName = "Landscape"; const char* CameraSpec::_fixedOrientationName = "FixedOrientation"; const char* CameraSpec::_minTriggerIntervalName = "MinTriggerInterval"; CameraSpec::CameraSpec(QObject* parent) : QObject (parent) , _dirty (false) , _metaDataMap (FactMetaData::createMapFromJsonFile(QStringLiteral(":/json/CameraSpec.FactMetaData.json"), this)) , _nameFact (0, _nameName, FactMetaData::valueTypeString) , _sensorWidthFact (0, _sensorWidthName, FactMetaData::valueTypeDouble) , _sensorHeightFact (0, _sensorHeightName, FactMetaData::valueTypeDouble) , _imageWidthFact (0, _imageWidthName, FactMetaData::valueTypeUint32) , _imageHeightFact (0, _imageHeightName, FactMetaData::valueTypeUint32) , _focalLengthFact (0, _focalLengthName, FactMetaData::valueTypeDouble) , _landscapeFact (0, _landscapeName, FactMetaData::valueTypeBool) , _fixedOrientationFact (0, _fixedOrientationName, FactMetaData::valueTypeBool) , _minTriggerIntervalFact (0, _minTriggerIntervalName, FactMetaData::valueTypeDouble) { _init(true); } CameraSpec::CameraSpec(const QString& name, double sensorWidth, double sensorHeight, double imageWidth, double imageHeight, double focalLength, bool landscape, bool fixedOrientation, double minTriggerInterval, QObject* parent) : QObject (parent) , _dirty (false) , _metaDataMap (FactMetaData::createMapFromJsonFile(QStringLiteral(":/json/CameraSpec.FactMetaData.json"), this)) , _nameFact (0, _nameName, FactMetaData::valueTypeString) , _sensorWidthFact (0, _sensorWidthName, FactMetaData::valueTypeDouble) , _sensorHeightFact (0, _sensorHeightName, FactMetaData::valueTypeDouble) , _imageWidthFact (0, _imageWidthName, FactMetaData::valueTypeUint32) , _imageHeightFact (0, _imageHeightName, FactMetaData::valueTypeUint32) , _focalLengthFact (0, _focalLengthName, FactMetaData::valueTypeDouble) , _landscapeFact (0, _landscapeName, FactMetaData::valueTypeBool) , _fixedOrientationFact (0, _fixedOrientationName, FactMetaData::valueTypeBool) , _minTriggerIntervalFact (0, _minTriggerIntervalName, FactMetaData::valueTypeDouble) { _init(false); _nameFact.setRawValue (name); _sensorWidthFact.setRawValue (sensorWidth); _sensorHeightFact.setRawValue (sensorHeight); _imageWidthFact.setRawValue (imageWidth); _imageHeightFact.setRawValue (imageHeight); _focalLengthFact.setRawValue (focalLength); _landscapeFact.setRawValue (landscape); _fixedOrientationFact.setRawValue (fixedOrientation); _minTriggerIntervalFact.setRawValue (minTriggerInterval); } CameraSpec::CameraSpec(const CameraSpec& other, QObject* parent) : QObject (parent) , _dirty (false) , _metaDataMap (FactMetaData::createMapFromJsonFile(QStringLiteral(":/json/CameraSpec.FactMetaData.json"), this)) , _nameFact (0, _nameName, FactMetaData::valueTypeString) , _sensorWidthFact (0, _sensorWidthName, FactMetaData::valueTypeDouble) , _sensorHeightFact (0, _sensorHeightName, FactMetaData::valueTypeDouble) , _imageWidthFact (0, _imageWidthName, FactMetaData::valueTypeUint32) , _imageHeightFact (0, _imageHeightName, FactMetaData::valueTypeUint32) , _focalLengthFact (0, _focalLengthName, FactMetaData::valueTypeDouble) , _landscapeFact (0, _landscapeName, FactMetaData::valueTypeBool) , _fixedOrientationFact (0, _fixedOrientationName, FactMetaData::valueTypeBool) , _minTriggerIntervalFact (0, _minTriggerIntervalName, FactMetaData::valueTypeDouble) { _init(false); *this = other; } const CameraSpec& CameraSpec::operator=(const CameraSpec& other) { _nameFact.setRawValue (other._nameFact.rawValue()); _sensorWidthFact.setRawValue (other._sensorWidthFact.rawValue()); _sensorHeightFact.setRawValue (other._sensorHeightFact.rawValue()); _imageWidthFact.setRawValue (other._imageWidthFact.rawValue()); _imageHeightFact.setRawValue (other._imageHeightFact.rawValue()); _focalLengthFact.setRawValue (other._focalLengthFact.rawValue()); _landscapeFact.setRawValue (other._landscapeFact.rawValue()); _fixedOrientationFact.setRawValue (other._fixedOrientationFact.rawValue()); _minTriggerIntervalFact.setRawValue (other._minTriggerIntervalFact.rawValue()); return *this; } void CameraSpec::_init(bool setDefaults) { QQmlEngine::setObjectOwnership(this, QQmlEngine::CppOwnership); _nameFact.setMetaData (_metaDataMap[_nameName], setDefaults /* setDefaultFromMetaData */); _sensorWidthFact.setMetaData (_metaDataMap[_sensorWidthName], setDefaults); _sensorHeightFact.setMetaData (_metaDataMap[_sensorHeightName], setDefaults); _imageWidthFact.setMetaData (_metaDataMap[_imageWidthName], setDefaults); _imageHeightFact.setMetaData (_metaDataMap[_imageHeightName], setDefaults); _focalLengthFact.setMetaData (_metaDataMap[_focalLengthName], setDefaults); _landscapeFact.setMetaData (_metaDataMap[_landscapeName], setDefaults); _fixedOrientationFact.setMetaData (_metaDataMap[_fixedOrientationName], setDefaults); _minTriggerIntervalFact.setMetaData (_metaDataMap[_minTriggerIntervalName], setDefaults); } void CameraSpec::setDirty(bool dirty) { if (_dirty != dirty) { _dirty = dirty; emit dirtyChanged(_dirty); } } void CameraSpec::save(QJsonObject& json) const { #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 versionKeyInfoList = { { JsonHelper::jsonVersionKey, QJsonValue::Double, true }, }; if (!JsonHelper::validateKeys(v2Object, versionKeyInfoList, errorString)) { return false; } int version = v2Object[JsonHelper::jsonVersionKey].toInt(); if (version != 2 && version != 3) { errorString = tr("%1 does not support this version of survey items").arg(qgcApp()->applicationName()); return false; } if (version == 2) { // Convert to v3 if (v2Object.contains(VisualMissionItem::jsonTypeKey) && v2Object[VisualMissionItem::jsonTypeKey].toString() == QStringLiteral("survey")) { v2Object[VisualMissionItem::jsonTypeKey] = VisualMissionItem::jsonTypeComplexItemValue; v2Object[ComplexMissionItem::jsonComplexItemTypeKey] = jsonComplexItemTypeValue; } } QList mainKeyInfoList = { { JsonHelper::jsonVersionKey, QJsonValue::Double, true }, { VisualMissionItem::jsonTypeKey, QJsonValue::String, true }, { ComplexMissionItem::jsonComplexItemTypeKey, QJsonValue::String, true }, { QGCMapPolygon::jsonPolygonKey, QJsonValue::Array, true }, { _jsonGridObjectKey, QJsonValue::Object, true }, { _jsonCameraObjectKey, QJsonValue::Object, false }, { _jsonCameraTriggerDistanceKey, QJsonValue::Double, true }, { _jsonManualGridKey, QJsonValue::Bool, true }, { _jsonFixedValueIsAltitudeKey, QJsonValue::Bool, true }, { _jsonHoverAndCaptureKey, QJsonValue::Bool, false }, { _jsonRefly90DegreesKey, QJsonValue::Bool, false }, { _jsonCameraTriggerInTurnaroundKey, QJsonValue::Bool, false }, // Should really be required, but it was missing from initial code due to bug }; if (!JsonHelper::validateKeys(v2Object, mainKeyInfoList, errorString)) { return false; } QString itemType = v2Object[VisualMissionItem::jsonTypeKey].toString(); QString complexType = v2Object[ComplexMissionItem::jsonComplexItemTypeKey].toString(); if (itemType != VisualMissionItem::jsonTypeComplexItemValue || complexType != jsonComplexItemTypeValue) { errorString = tr("%1 does not support loading this complex mission item type: %2:%3").arg(qgcApp()->applicationName()).arg(itemType).arg(complexType); return false; } _ignoreRecalc = true; _mapPolygon.clear(); setSequenceNumber(sequenceNumber); _manualGridFact.setRawValue (v2Object[_jsonManualGridKey].toBool(true)); _fixedValueIsAltitudeFact.setRawValue (v2Object[_jsonFixedValueIsAltitudeKey].toBool(true)); _gridAltitudeRelativeFact.setRawValue (v2Object[_jsonGridAltitudeRelativeKey].toBool(true)); _hoverAndCaptureFact.setRawValue (v2Object[_jsonHoverAndCaptureKey].toBool(false)); _cameraTriggerInTurnaroundFact.setRawValue (v2Object[_jsonCameraTriggerInTurnaroundKey].toBool(true)); _refly90Degrees = v2Object[_jsonRefly90DegreesKey].toBool(false); QList gridKeyInfoList = { { _jsonGridAltitudeKey, QJsonValue::Double, true }, { _jsonGridAltitudeRelativeKey, QJsonValue::Bool, true }, { _jsonGridAngleKey, QJsonValue::Double, true }, { _jsonGridSpacingKey, QJsonValue::Double, true }, { _jsonGridEntryLocationKey, QJsonValue::Double, false }, { _jsonTurnaroundDistKey, QJsonValue::Double, true }, }; QJsonObject gridObject = v2Object[_jsonGridObjectKey].toObject(); if (!JsonHelper::validateKeys(gridObject, gridKeyInfoList, errorString)) { return false; } _gridAltitudeFact.setRawValue (gridObject[_jsonGridAltitudeKey].toDouble()); _gridAngleFact.setRawValue (gridObject[_jsonGridAngleKey].toDouble()); _gridSpacingFact.setRawValue (gridObject[_jsonGridSpacingKey].toDouble()); _turnaroundDistFact.setRawValue (gridObject[_jsonTurnaroundDistKey].toDouble()); _cameraTriggerDistanceFact.setRawValue (v2Object[_jsonCameraTriggerDistanceKey].toDouble()); if (gridObject.contains(_jsonGridEntryLocationKey)) { _gridEntryLocationFact.setRawValue(gridObject[_jsonGridEntryLocationKey].toDouble()); } else { _gridEntryLocationFact.setRawValue(_gridEntryLocationFact.rawDefaultValue()); } if (!_manualGridFact.rawValue().toBool()) { if (!v2Object.contains(_jsonCameraObjectKey)) { errorString = tr("%1 but %2 object is missing").arg("manualGrid = false").arg("camera"); return false; } QJsonObject cameraObject = v2Object[_jsonCameraObjectKey].toObject(); // Older code had typo on "imageSideOverlap" incorrectly being "imageSizeOverlap" QString incorrectImageSideOverlap = "imageSizeOverlap"; if (cameraObject.contains(incorrectImageSideOverlap)) { cameraObject[_jsonSideOverlapKey] = cameraObject[incorrectImageSideOverlap]; cameraObject.remove(incorrectImageSideOverlap); } QList cameraKeyInfoList = { { _jsonGroundResolutionKey, QJsonValue::Double, true }, { _jsonFrontalOverlapKey, QJsonValue::Double, true }, { _jsonSideOverlapKey, QJsonValue::Double, true }, { _jsonCameraSensorWidthKey, QJsonValue::Double, true }, { _jsonCameraSensorHeightKey, QJsonValue::Double, true }, { _jsonCameraResolutionWidthKey, QJsonValue::Double, true }, { _jsonCameraResolutionHeightKey, QJsonValue::Double, true }, { _jsonCameraFocalLengthKey, QJsonValue::Double, true }, { _jsonCameraNameKey, QJsonValue::String, true }, { _jsonCameraOrientationLandscapeKey, QJsonValue::Bool, true }, { _jsonCameraMinTriggerIntervalKey, QJsonValue::Double, false }, }; if (!JsonHelper::validateKeys(cameraObject, cameraKeyInfoList, errorString)) { return false; } _cameraFact.setRawValue(cameraObject[_jsonCameraNameKey].toString()); _cameraOrientationLandscapeFact.setRawValue(cameraObject[_jsonCameraOrientationLandscapeKey].toBool(true)); _groundResolutionFact.setRawValue (cameraObject[_jsonGroundResolutionKey].toDouble()); _frontalOverlapFact.setRawValue (cameraObject[_jsonFrontalOverlapKey].toInt()); _sideOverlapFact.setRawValue (cameraObject[_jsonSideOverlapKey].toInt()); _cameraSensorWidthFact.setRawValue (cameraObject[_jsonCameraSensorWidthKey].toDouble()); _cameraSensorHeightFact.setRawValue (cameraObject[_jsonCameraSensorHeightKey].toDouble()); _cameraResolutionWidthFact.setRawValue (cameraObject[_jsonCameraResolutionWidthKey].toDouble()); _cameraResolutionHeightFact.setRawValue (cameraObject[_jsonCameraResolutionHeightKey].toDouble()); _cameraFocalLengthFact.setRawValue (cameraObject[_jsonCameraFocalLengthKey].toDouble()); _cameraMinTriggerInterval = cameraObject[_jsonCameraMinTriggerIntervalKey].toDouble(0); } // Polygon shape /// Load a polygon from json /// @param json Json object to load from /// @param required true: no polygon in object will generate error /// @param errorString Error string if return is false /// @return true: success, false: failure (errorString set) if (!_mapPolygon.loadFromJson(v2Object, true /* required */, errorString)) { _mapPolygon.clear(); return false; } _ignoreRecalc = false; _generateGrid(); return true; #endif return false; } #if 0 void CameraSpec::recalcImageOnGround(bool valueIsAltitude, double value) { var focalLength = missionItem.cameraFocalLength.rawValue var sensorWidth = missionItem.cameraSensorWidth.rawValue var sensorHeight = missionItem.cameraSensorHeight.rawValue var imageWidth = missionItem.cameraResolutionWidth.rawValue var imageHeight = missionItem.cameraResolutionHeight.rawValue var altitude = missionItem.gridAltitude.rawValue var groundResolution= missionItem.groundResolution.rawValue var frontalOverlap = missionItem.frontalOverlap.rawValue var sideOverlap = missionItem.sideOverlap.rawValue if (focalLength <= 0 || sensorWidth <= 0 || sensorHeight <= 0 || imageWidth <= 0 || imageHeight <= 0 || groundResolution <= 0) { return } var imageSizeSideGround //size in side (non flying) direction of the image on the ground var imageSizeFrontGround //size in front (flying) direction of the image on the ground var gridSpacing var cameraTriggerDistance if (missionItem.fixedValueIsAltitude.value) { groundResolution = (altitude * sensorWidth * 100) / (imageWidth * focalLength) } else { altitude = (imageWidth * groundResolution * focalLength) / (sensorWidth * 100) } if (missionItem.cameraOrientationLandscape.value) { imageSizeSideGround = (imageWidth * groundResolution) / 100 imageSizeFrontGround = (imageHeight * groundResolution) / 100 } else { imageSizeSideGround = (imageHeight * groundResolution) / 100 imageSizeFrontGround = (imageWidth * groundResolution) / 100 } gridSpacing = imageSizeSideGround * ( (100-sideOverlap) / 100 ) cameraTriggerDistance = imageSizeFrontGround * ( (100-frontalOverlap) / 100 ) if (missionItem.fixedValueIsAltitude.value) { missionItem.groundResolution.rawValue = groundResolution } else { missionItem.gridAltitude.rawValue = altitude } missionItem.gridSpacing.rawValue = gridSpacing missionItem.cameraTriggerDistance.rawValue = cameraTriggerDistance } #endif