From fd28bf0307fce94abdcc18929bad2a5a6c092cab Mon Sep 17 00:00:00 2001 From: Don Gagne Date: Fri, 30 Sep 2016 11:02:11 -0700 Subject: [PATCH] Major rework of Survey ui --- src/MissionEditor/SurveyItemEditor.qml | 756 +++++++++++++++------------- src/MissionManager/Survey.FactMetaData.json | 67 ++- src/MissionManager/SurveyMissionItem.cc | 285 ++++++++--- src/MissionManager/SurveyMissionItem.h | 143 ++++-- 4 files changed, 784 insertions(+), 467 deletions(-) diff --git a/src/MissionEditor/SurveyItemEditor.qml b/src/MissionEditor/SurveyItemEditor.qml index 70266d9..0192ed3 100644 --- a/src/MissionEditor/SurveyItemEditor.qml +++ b/src/MissionEditor/SurveyItemEditor.qml @@ -1,6 +1,7 @@ -import QtQuick 2.2 -import QtQuick.Controls 1.2 -import QtQuick.Dialogs 1.2 +import QtQuick 2.2 +import QtQuick.Controls 1.2 +import QtQuick.Dialogs 1.2 +import QtQuick.Layouts 1.2 import QGroundControl 1.0 import QGroundControl.ScreenTools 1.0 @@ -21,127 +22,165 @@ Rectangle { //property real availableWidth ///< Width for control //property var missionItem ///< Mission Item for editor - property real _margin: ScreenTools.defaultFontPixelWidth / 2 - - property int cameraIndex: 1 + property real _margin: ScreenTools.defaultFontPixelWidth / 2 + property int _cameraIndex: 1 + + readonly property int _gridTypeManual: 0 + readonly property int _gridTypeCustomCamera: 1 + readonly property int _gridTypeCamera: 2 + + Component.onCompleted: { + console.log("gridAltitude", missionItem.gridAltitude.value) + console.log("gridAltitudeRelative", missionItem.gridAltitudeRelative) + console.log("gridAngle", missionItem.gridAngle.value) + console.log("gridSpacing", missionItem.gridSpacing.value) + console.log("turnaroundDist", missionItem.turnaroundDist.value) + console.log("cameraTrigger", missionItem.cameraTrigger) + console.log("cameraTriggerDistance", missionItem.cameraTriggerDistance.value) + console.log("groundResolution", missionItem.groundResolution.value) + console.log("frontalOverlap", missionItem.frontalOverlap.value) + console.log("sideOverlap", missionItem.sideOverlap.value) + console.log("cameraSensorWidth", missionItem.cameraSensorWidth.value) + console.log("cameraSensorHeight", missionItem.cameraSensorHeight.value) + console.log("cameraResolutionWidth", missionItem.cameraResolutionWidth.value) + console.log("cameraResolutionHeight", missionItem.cameraResolutionHeight.value) + console.log("cameraFocalLength", missionItem.cameraFocalLength.value) + console.log("fixedValueIsAltitude", missionItem.fixedValueIsAltitude) + console.log("cameraOrientationLandscape", missionItem.cameraOrientationLandscape) + console.log("manualGrid", missionItem.manualGrid) + console.log("camera", missionItem.camera) + } ListModel { - id: cameraModelList - ListElement { - text: qsTr("Custom") - sensorWidth: 0 - sensorHeight: 0 - imageWidth: 0 - imageHeight: 0 - focalLength: 0 - } - ListElement { - text: qsTr("Sony ILCE-QX1") //http://www.sony.co.uk/electronics/interchangeable-lens-cameras/ilce-qx1-body-kit/specifications - sensorWidth: 23.2 //http://www.sony.com/electronics/camera-lenses/sel16f28/specifications - sensorHeight: 15.4 - imageWidth: 5456 - imageHeight: 3632 - focalLength: 16 - } - ListElement { - text: qsTr("Canon S100 PowerShot") - sensorWidth: 7.6 - sensorHeight: 5.7 - imageWidth: 4000 - imageHeight: 3000 - focalLength: 5.2 - } - ListElement { - text: qsTr("Canon SX260 HS PowerShot") - sensorWidth: 6.17 - sensorHeight: 4.55 - imageWidth: 4000 - imageHeight: 3000 - focalLength: 4.5 - } - ListElement { - text: qsTr("Canon EOS-M 22mm") - sensorWidth: 22.3 - sensorHeight: 14.9 - imageWidth: 5184 - imageHeight: 3456 - focalLength: 22 - } - ListElement { - text: qsTr("Sony a6000 16mm") //http://www.sony.co.uk/electronics/interchangeable-lens-cameras/ilce-6000-body-kit#product_details_default - sensorWidth: 23.5 - sensorHeight: 15.6 - imageWidth: 6000 - imageHeight: 4000 - focalLength: 16 - } - } + id: cameraModelList + + Component.onCompleted: { + cameraModelList.setProperty(_gridTypeCustomCamera, "sensorWidth", missionItem.cameraSensorWidth.rawValue) + cameraModelList.setProperty(_gridTypeCustomCamera, "sensorHeight", missionItem.cameraSensorHeight.rawValue) + cameraModelList.setProperty(_gridTypeCustomCamera, "imageWidth", missionItem.cameraResolutionWidth.rawValue) + cameraModelList.setProperty(_gridTypeCustomCamera, "imageHeight", missionItem.cameraResolutionHeight.rawValue) + cameraModelList.setProperty(_gridTypeCustomCamera, "focalLength", missionItem.cameraFocalLength.rawValue) + } + + ListElement { + text: qsTr("Manual Grid (no camera specs)") + } + ListElement { + text: qsTr("Custom Camera Grid") + } + ListElement { + text: qsTr("Sony ILCE-QX1") //http://www.sony.co.uk/electronics/interchangeable-lens-cameras/ilce-qx1-body-kit/specifications + sensorWidth: 23.2 //http://www.sony.com/electronics/camera-lenses/sel16f28/specifications + sensorHeight: 15.4 + imageWidth: 5456 + imageHeight: 3632 + focalLength: 16 + } + ListElement { + text: qsTr("Canon S100 PowerShot") + sensorWidth: 7.6 + sensorHeight: 5.7 + imageWidth: 4000 + imageHeight: 3000 + focalLength: 5.2 + } + ListElement { + text: qsTr("Canon SX260 HS PowerShot") + sensorWidth: 6.17 + sensorHeight: 4.55 + imageWidth: 4000 + imageHeight: 3000 + focalLength: 4.5 + } + ListElement { + text: qsTr("Canon EOS-M 22mm") + sensorWidth: 22.3 + sensorHeight: 14.9 + imageWidth: 5184 + imageHeight: 3456 + focalLength: 22 + } + ListElement { + text: qsTr("Sony a6000 16mm") //http://www.sony.co.uk/electronics/interchangeable-lens-cameras/ilce-6000-body-kit#product_details_default + sensorWidth: 23.5 + sensorHeight: 15.6 + imageWidth: 6000 + imageHeight: 4000 + focalLength: 16 + } + } function recalcFromCameraValues() { - var focalLength = cameraModelList.get(cameraIndex).focalLength - var sensorWidth = cameraModelList.get(cameraIndex).sensorWidth - var sensorHeight = cameraModelList.get(cameraIndex).sensorHeight - var imageWidth = cameraModelList.get(cameraIndex).imageWidth - var imageHeight = cameraModelList.get(cameraIndex).imageHeight - - var gsd = Number(gsdField.text) - var frontalOverlap = Number(frontalOverlapField.text) - var sideOverlap = Number(sideOverlapField.text) - - if (focalLength <= 0.0 || sensorWidth <= 0.0 || sensorHeight <= 0.0 || imageWidth < 0 || imageHeight < 0 || gsd < 0.0 || frontalOverlap < 0 || sideOverlap < 0) { - missionItem.gridAltitude.rawValue = 0 - missionItem.gridSpacing.rawValue = 0 - missionItem.cameraTriggerDistance.rawValue = 0 + 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 altitude - 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 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 - altitude = (imageWidth * gsd * focalLength) / (sensorWidth * 100) + if (missionItem.fixedValueIsAltitude) { + groundResolution = (altitude * sensorWidth * 100) / (imageWidth * focalLength) + } else { + altitude = (imageWidth * groundResolution * focalLength) / (sensorWidth * 100) + } if (cameraOrientationLandscape.checked) { - imageSizeSideGround = (imageWidth * gsd) / 100 - imageSizeFrontGround = (imageHeight * gsd) / 100 + imageSizeSideGround = (imageWidth * groundResolution) / 100 + imageSizeFrontGround = (imageHeight * groundResolution) / 100 } else { - imageSizeSideGround = (imageHeight * gsd) / 100 - imageSizeFrontGround = (imageWidth * gsd) / 100 + imageSizeSideGround = (imageHeight * groundResolution) / 100 + imageSizeFrontGround = (imageWidth * groundResolution) / 100 } gridSpacing = imageSizeSideGround * ( (100-sideOverlap) / 100 ) cameraTriggerDistance = imageSizeFrontGround * ( (100-frontalOverlap) / 100 ) - missionItem.gridAltitude.rawValue = altitude + if (missionItem.fixedValueIsAltitude) { + missionItem.groundResolution.rawValue = groundResolution + } else { + missionItem.gridAltitude.rawValue = altitude + } missionItem.gridSpacing.rawValue = gridSpacing missionItem.cameraTriggerDistance.rawValue = cameraTriggerDistance } + /* function recalcFromMissionValues() { - var focalLength = cameraModelList.get(cameraIndex).focalLength - var sensorWidth = cameraModelList.get(cameraIndex).sensorWidth - var sensorHeight = cameraModelList.get(cameraIndex).sensorHeight - var imageWidth = cameraModelList.get(cameraIndex).imageWidth - var imageHeight = cameraModelList.get(cameraIndex).imageHeight + 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 gridSpacing = missionItem.gridSpacing.rawValue var cameraTriggerDistance = missionItem.cameraTriggerDistance.rawValue if (focalLength <= 0.0 || sensorWidth <= 0.0 || sensorHeight <= 0.0 || imageWidth < 0 || imageHeight < 0 || altitude < 0.0 || gridSpacing < 0.0 || cameraTriggerDistance < 0.0) { - gsdField.text = "0.0" - sideOverlapField.text = "0" - frontalOverlapField.text = "0" + missionItem.groundResolution.rawValue = 0 + missionItem.sideOverlap = 0 + missionItem.frontalOverlap = 0 return } - var gsd - 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 groundResolution + 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 - gsd = (altitude * sensorWidth * 100) / (imageWidth * focalLength) + groundResolution = (altitude * sensorWidth * 100) / (imageWidth * focalLength) if (cameraOrientationLandscape.checked) { imageSizeSideGround = (imageWidth * gsd) / 100 @@ -154,13 +193,14 @@ Rectangle { var sideOverlap = (imageSizeSideGround == 0 ? 0 : 100 - (gridSpacing*100 / imageSizeSideGround)) var frontOverlap = (imageSizeFrontGround == 0 ? 0 : 100 - (cameraTriggerDistance*100 / imageSizeFrontGround)) - gsdField.text = gsd.toFixed(1) - sideOverlapField.text = sideOverlap.toFixed(0) - frontalOverlapField.text = frontOverlap.toFixed(0) + missionItem.groundResolution.rawValue = groundResolution + missionItem.sideOverlap.rawValue = sideOverlap + missionItem.frontalOverlap.rawValue = frontOverlap } + */ function polygonCaptureStarted() { - missionItem.clearPolygon() + missionItem.clearPolygon() } function polygonCaptureFinished(coordinates) { @@ -176,15 +216,42 @@ Rectangle { function polygonAdjustStarted() { } function polygonAdjustFinished() { } + property bool _noCameraValueRecalc: false ///< Prevents uneeded recalcs + + Connections { + target: missionItem + + onCameraValueChanged: { + if (gridTypeCombo.currentIndex >= _gridTypeCustomCamera && !_noCameraValueRecalc) { + recalcFromCameraValues() + } + } + } + + Connections { + target: missionItem.gridAltitude + + onValueChanged: { + if (gridTypeCombo.currentIndex >= _gridTypeCustomCamera && missionItem.fixedValueIsAltitude && !_noCameraValueRecalc) { + recalcFromCameraValues() + } + } + } + QGCPalette { id: qgcPal; colorGroupEnabled: true } ExclusiveGroup { - id: cameraOrientationGroup - onCurrentChanged: { - recalcFromMissionValues() + id: cameraOrientationGroup + + onCurrentChanged: { + if (gridTypeCombo.currentIndex >= _gridTypeCustomCamera) { + recalcFromCameraValues() + } } } + ExclusiveGroup { id: fixedValueGroup } + Column { id: editorColumn anchors.margins: _margin @@ -198,307 +265,292 @@ Rectangle { anchors.right: parent.right wrapMode: Text.WordWrap font.pointSize: ScreenTools.smallFontPointSize - text: qsTr("Create a flight path to fully cover a polygonal area with a camera.") + text: gridTypeCombo.currentIndex == 0 ? + qsTr("Create a flight path which covers a polygonal area by specifying all grid parameters.") : + qsTr("Create a flight path which fully covers a polygonal area using camera specifications.") } - Repeater { - model: [ missionItem.gridAngle, missionItem.gridSpacing, missionItem.gridAltitude, missionItem.turnaroundDist ] + QGCLabel { text: qsTr("Camera:") } - Item { - anchors.left: parent.left - anchors.right: parent.right - height: textField.height + Rectangle { + anchors.left: parent.left + anchors.right: parent.right + height: 1 + color: qgcPal.text + } - QGCLabel { - anchors.baseline: textField.baseline - anchors.left: parent.left - text: modelData.name + ":" + QGCComboBox { + id: gridTypeCombo + anchors.left: parent.left + anchors.right: parent.right + model: cameraModelList + currentIndex: -1 + + Component.onCompleted: { + if (missionItem.manualGrid) { + gridTypeCombo.currentIndex = _gridTypeManual + } else { + var index = gridTypeCombo.find(missionItem.camera) + if (index == -1) { + console.log("Couldn't find camera", missionItem.camera) + gridTypeCombo.currentIndex = _gridTypeManual + } else { + gridTypeCombo.currentIndex = index + } } + } - FactTextField { - id: textField - anchors.right: parent.right - width: _editFieldWidth - showUnits: true - fact: modelData - onEditingFinished: recalcFromMissionValues() - validator: DoubleValidator{bottom:0.0; decimals:2} + onActivated: { + if (index == _gridTypeManual) { + missionItem.manualGrid = true + } else { + missionItem.manualGrid = false + missionItem.camera = gridTypeCombo.textAt(index) + _noCameraValueRecalc = true + missionItem.cameraSensorWidth.rawValue = cameraModelList.get(index).sensorWidth + missionItem.cameraSensorHeight.rawValue = cameraModelList.get(index).sensorHeight + missionItem.cameraResolutionWidth.rawValue = cameraModelList.get(index).imageWidth + missionItem.cameraResolutionHeight.rawValue = cameraModelList.get(index).imageHeight + missionItem.cameraFocalLength.rawValue = cameraModelList.get(index).focalLength + _noCameraValueRecalc = false + recalcFromCameraValues() } } } - QGCCheckBox { + // Camera based grid ui + Column { anchors.left: parent.left - text: qsTr("Relative altitude") - checked: missionItem.gridAltitudeRelative - onClicked: missionItem.gridAltitudeRelative = checked - } + anchors.right: parent.right + spacing: _margin + visible: gridTypeCombo.currentIndex != _gridTypeManual - Grid { - columns: 2 - columnSpacing: ScreenTools.defaultFontPixelWidth - rowSpacing: _margin - verticalItemAlignment: Grid.AlignVCenter + Row { + spacing: _margin - QGCLabel { - text: qsTr("GSD:") - width: _editFieldWidth - } - QGCTextField { - id: gsdField - width: _editFieldWidth - unitsLabel: "cm/px" - showUnits: true - onEditingFinished: recalcFromCameraValues() - validator: DoubleValidator{bottom:0.0; decimals:2} - } + QGCRadioButton { + id: cameraOrientationLandscape + width: _editFieldWidth + text: "Landscape" + checked: true + exclusiveGroup: cameraOrientationGroup + } - QGCLabel { text: qsTr("Frontal Overlap:") } - QGCTextField { - id: frontalOverlapField - width: _editFieldWidth - unitsLabel: "%" - showUnits: true - onEditingFinished: recalcFromCameraValues() - validator: IntValidator {bottom:0} + QGCRadioButton { + id: cameraOrientationPortrait + text: "Portrait" + exclusiveGroup: cameraOrientationGroup + } } - QGCLabel { text: qsTr("Side Overlap:") } - QGCTextField { - id: sideOverlapField - width: _editFieldWidth - unitsLabel: "%" - showUnits: true - onEditingFinished: recalcFromCameraValues() - validator: IntValidator {bottom:0} - } + Column { + anchors.left: parent.left + anchors.right: parent.right + spacing: _margin + visible: gridTypeCombo.currentIndex == _gridTypeCustomCamera - Component.onCompleted: recalcFromMissionValues() - } + GridLayout { + columns: 3 + columnSpacing: _margin + rowSpacing: _margin - QGCLabel { text: qsTr("Camera:") } + property real _fieldWidth: ScreenTools.defaultFontPixelWidth * 10 - Rectangle { - anchors.left: parent.left - anchors.right: parent.right - height: 1 - color: qgcPal.text - } + QGCLabel { } + QGCLabel { text: qsTr("Width") } + QGCLabel { text: qsTr("Height") } - Grid { - columns: 2 - spacing: ScreenTools.defaultFontPixelWidth - verticalItemAlignment: Grid.AlignVCenter - - QGCRadioButton { - id: cameraOrientationLandscape - width: _editFieldWidth - text: "Landscape" - checked: true - exclusiveGroup: cameraOrientationGroup + QGCLabel { text: qsTr("Sensor:") } + FactTextField { + Layout.preferredWidth: parent._fieldWidth + fact: missionItem.cameraSensorWidth + } + FactTextField { + Layout.preferredWidth: parent._fieldWidth + fact: missionItem.cameraSensorHeight + } + + QGCLabel { text: qsTr("Image:") } + FactTextField { + Layout.preferredWidth: parent._fieldWidth + fact: missionItem.cameraResolutionWidth + } + FactTextField { + Layout.preferredWidth: parent._fieldWidth + fact: missionItem.cameraResolutionHeight + } + } + + FactTextFieldRow { + spacing: _margin + fact: missionItem.cameraFocalLength + } + } // Column - custom camera + + QGCLabel { text: qsTr("Image Overlap") } + + Row { + spacing: _margin + + Item { + width: ScreenTools.defaultFontPixelWidth * 2 + height: 1 + } + + QGCLabel { + anchors.baseline: frontalOverlapField.baseline + text: qsTr("Frontal:") + } + + FactTextField { + id: frontalOverlapField + width: ScreenTools.defaultFontPixelWidth * 7 + fact: missionItem.frontalOverlap + } + + QGCLabel { + anchors.baseline: frontalOverlapField.baseline + text: qsTr("Side:") + } + + FactTextField { + width: frontalOverlapField.width + fact: missionItem.sideOverlap + } } - QGCRadioButton { - id: cameraOrientationPortrait - text: "Portrait" - exclusiveGroup: cameraOrientationGroup + QGCLabel { text: qsTr("Grid:") } + + Rectangle { + anchors.left: parent.left + anchors.right: parent.right + height: 1 + color: qgcPal.text } - QGCCheckBox { - id: cameraTrigger - width: _editFieldWidth - text: qsTr("Trigger:") - checked: missionItem.cameraTrigger - onClicked: missionItem.cameraTrigger = checked + FactTextFieldGrid { + anchors.left: parent.left + anchors.right: parent.right + columnSpacing: _margin + rowSpacing: _margin + factList: [ missionItem.gridAngle, missionItem.turnaroundDist ] } - FactTextField { - width: _editFieldWidth - showUnits: true - fact: missionItem.cameraTriggerDistance - enabled: missionItem.cameraTrigger - onEditingFinished: recalcFromMissionValues() - validator: DoubleValidator{bottom:0.0; decimals:2} + QGCLabel { + anchors.left: parent.left + anchors.right: parent.right + wrapMode: Text.WordWrap + font.pointSize: ScreenTools.smallFontPointSize + text: qsTr("Which value would you like to keep constant as you adjust other settings:") } - } - Component { - id: cameraFields - - QGCViewDialog { - - Column { - id: dialogColumn - anchors.margins: _margin - anchors.top: parent.top - anchors.left: parent.left - anchors.right: parent.right - spacing: _margin * 5 - - Row { - spacing: ScreenTools.defaultFontPixelWidth - - QGCLabel { - id: selectCameraModelText - text: qsTr("Select Camera Model:") - } - - QGCComboBox { - id: cameraModelCombo - model: cameraModelList - width: dialogColumn.width - selectCameraModelText.width - ScreenTools.defaultFontPixelWidth - - onActivated: { - cameraIndex = index - } - - Component.onCompleted: { - var index = cameraIndex - if (index === -1) { - console.warn("Active camera model name not in combo", cameraIndex) - } else { - cameraModelCombo.currentIndex = index - } - } - } - } + RowLayout { + anchors.left: parent.left + anchors.right: parent.right + spacing: _margin + + QGCRadioButton { + id: fixedAltitudeRadio + anchors.baseline: gridAltitudeField.baseline + text: qsTr("Altitude:") + checked: missionItem.fixedValueIsAltitude + exclusiveGroup: fixedValueGroup + onClicked: missionItem.fixedValueIsAltitude = true + } - Grid { - columns: 2 - spacing: ScreenTools.defaultFontPixelWidth - verticalItemAlignment: Grid.AlignVCenter - - QGCLabel { text: qsTr("Sensor Width:") } - QGCTextField { - id: sensorWidthField - unitsLabel: "mm" - showUnits: true - text: cameraModelList.get(cameraIndex).sensorWidth.toFixed(2) - readOnly: cameraIndex != 0 - enabled: cameraIndex == 0 - validator: DoubleValidator{bottom:0.0; decimals:2} - onEditingFinished: { - if (cameraIndex == 0) { - cameraModelList.setProperty(cameraIndex, "sensorWidth", Number(text)) - } - } - } - - QGCLabel { text: qsTr("Sensor Height:") } - QGCTextField { - id: sensorHeightField - unitsLabel: "mm" - showUnits: true - text: cameraModelList.get(cameraIndex).sensorHeight.toFixed(2) - readOnly: cameraIndex != 0 - enabled: cameraIndex == 0 - validator: DoubleValidator{bottom:0.0; decimals:2} - onEditingFinished: { - if (cameraIndex == 0) { - cameraModelList.setProperty(cameraIndex, "sensorHeight", Number(text)) - } - } - } - - QGCLabel { text: qsTr("Image Width:") } - QGCTextField { - id: imageWidthField - unitsLabel: "px" - showUnits: true - text: cameraModelList.get(cameraIndex).imageWidth.toFixed(0) - readOnly: cameraIndex != 0 - enabled: cameraIndex == 0 - validator: IntValidator {bottom:0} - onEditingFinished: { - if (cameraIndex == 0) { - cameraModelList.setProperty(cameraIndex, "imageWidth", Number(text)) - } - } - } - - QGCLabel { text: qsTr("Image Height:") } - QGCTextField { - id: imageHeightField - unitsLabel: "px" - showUnits: true - text: cameraModelList.get(cameraIndex).imageHeight.toFixed(0) - readOnly: cameraIndex != 0 - enabled: cameraIndex == 0 - validator: IntValidator {bottom:0} - onEditingFinished: { - if (cameraIndex == 0) { - cameraModelList.setProperty(cameraIndex, "imageHeight", Number(text)) - } - } - } - - QGCLabel { text: qsTr("Focal Length:") } - QGCTextField { - id: focalLengthField - unitsLabel: "mm" - showUnits: true - text: cameraModelList.get(cameraIndex).focalLength.toFixed(2) - readOnly: cameraIndex != 0 - enabled: cameraIndex == 0 - validator: DoubleValidator{bottom:0.0; decimals:2} - onEditingFinished: { - if (cameraIndex == 0) { - cameraModelList.setProperty(cameraIndex, "focalLength", Number(text)) - } - } - } - } + FactTextField { + id: gridAltitudeField + Layout.fillWidth: true + fact: missionItem.gridAltitude + enabled: fixedAltitudeRadio.checked } + } - function accept() { - hideDialog() - recalcFromCameraValues() + RowLayout { + anchors.left: parent.left + anchors.right: parent.right + spacing: _margin + + QGCRadioButton { + id: fixedGroundResolutionRadio + anchors.baseline: groundResolutionField.baseline + text: qsTr("Ground res:") + checked: !missionItem.fixedValueIsAltitude + exclusiveGroup: fixedValueGroup + onClicked: missionItem.fixedValueIsAltitude = false } - }//QGCViewDialog - }//Component + FactTextField { + id: groundResolutionField + Layout.fillWidth: true + fact: missionItem.groundResolution + enabled: fixedGroundResolutionRadio.checked + } + } + } + + // Manual grid ui Column { - spacing: ScreenTools.defaultFontPixelHeight*0.01 + anchors.left: parent.left + anchors.right: parent.right + spacing: _margin + visible: gridTypeCombo.currentIndex == _gridTypeManual - Row { - spacing: ScreenTools.defaultFontPixelWidth + QGCLabel { text: qsTr("Grid:") } - QGCLabel { text: qsTr("Model:") } - QGCLabel { text: cameraModelList.get(cameraIndex).text } + Rectangle { + anchors.left: parent.left + anchors.right: parent.right + height: 1 + color: qgcPal.text } - Grid { - columns: 2 - columnSpacing: ScreenTools.defaultFontPixelWidth - rowSpacing: ScreenTools.defaultFontPixelHeight*0.01 + FactTextFieldGrid { + anchors.left: parent.left + anchors.right: parent.right + columnSpacing: _margin + rowSpacing: _margin + factList: [ missionItem.gridAngle, missionItem.gridSpacing, missionItem.gridAltitude, missionItem.turnaroundDist ] + } - QGCLabel { - text: qsTr("Sensor Size:") - width: _editFieldWidth - } - QGCLabel { - text: cameraModelList.get(cameraIndex).sensorWidth.toFixed(2) + qsTr(" x ") + cameraModelList.get(cameraIndex).sensorHeight.toFixed(2) - width: _editFieldWidth - } + QGCCheckBox { + anchors.left: parent.left + text: qsTr("Relative altitude") + checked: missionItem.gridAltitudeRelative + onClicked: missionItem.gridAltitudeRelative = checked + } - QGCLabel { text: qsTr("Image Size:") } - QGCLabel { text: cameraModelList.get(cameraIndex).imageWidth.toFixed(0) + qsTr(" x ") + cameraModelList.get(cameraIndex).imageHeight.toFixed(0) } + QGCLabel { text: qsTr("Camera:") } - QGCLabel { text: qsTr("Focal length:") } - QGCLabel { text: cameraModelList.get(cameraIndex).focalLength.toFixed(2) } + Rectangle { + anchors.left: parent.left + anchors.right: parent.right + height: 1 + color: qgcPal.text } - } - QGCButton { - id: cameraModelChange - text: qsTr("Change") + RowLayout { + anchors.left: parent.left + anchors.right: parent.right + spacing: _margin + + QGCCheckBox { + id: cameraTrigger + anchors.baseline: cameraTriggerDistanceField.baseline + text: qsTr("Trigger Distance:") + checked: missionItem.cameraTrigger + onClicked: missionItem.cameraTrigger = checked + } - onClicked: { - qgcView.showDialog(cameraFields, qsTr("Set Camera Model"), qgcView.showDialogDefaultWidth, StandardButton.Save) + FactTextField { + id: cameraTriggerDistanceField + Layout.fillWidth: true + fact: missionItem.cameraTriggerDistance + enabled: missionItem.cameraTrigger + } } } - QGCLabel { text: qsTr("Polygon:") } Rectangle { diff --git a/src/MissionManager/Survey.FactMetaData.json b/src/MissionManager/Survey.FactMetaData.json index d50d12b..bb341a6 100644 --- a/src/MissionManager/Survey.FactMetaData.json +++ b/src/MissionManager/Survey.FactMetaData.json @@ -18,20 +18,85 @@ "shortDescription": "Amount of spacing in between parallel grid lines.", "type": "double", "decimalPlaces": 2, + "min": 0.1, "units": "m" }, { - "name": "Turnaround dist.", + "name": "Turnaround dist", "shortDescription": "Amount of additional distance to add outside the grid area for vehicle turnaround.", "type": "double", "decimalPlaces": 2, + "min": 0, "units": "m" }, { + "name": "Ground resolution", + "shortDescription": "Resolution of image in relationship to ground distance.", + "type": "double", + "decimalPlaces": 2, + "min": 0, + "units": "cm/px" +}, +{ + "name": "Frontal overlap", + "shortDescription": "Amount of overlap between images in the forward facing direction.", + "type": "double", + "decimalPlaces": 0, + "max": 75, + "units": "%" +}, +{ + "name": "Side overlap", + "shortDescription": "Amount of overlap between images in the side facing direction.", + "type": "double", + "decimalPlaces": 0, + "max": 75, + "units": "%" +}, +{ + "name": "Camera sensor width", + "shortDescription": "Amount of overlap between images in the side facing direction.", + "type": "double", + "decimalPlaces": 2, + "min": 1, + "units": "mm" +}, +{ + "name": "Camera sensor height", + "shortDescription": "Amount of overlap between images in the side facing direction.", + "type": "double", + "decimalPlaces": 2, + "min": 1, + "units": "mm" +}, +{ + "name": "Camera resolution width", + "shortDescription": "Amount of overlap between images in the side facing direction.", + "type": "uint32", + "min": 1, + "units": "px" +}, +{ + "name": "Camera resolution height", + "shortDescription": "Amount of overlap between images in the side facing direction.", + "type": "uint32", + "min": 1, + "units": "px" +}, +{ + "name": "Focal length", + "shortDescription": "Amount of overlap between images in the side facing direction.", + "type": "double", + "decimalPlaces": 1, + "min": 1, + "units": "mm" +}, +{ "name": "Camera trigger distance", "shortDescription": "Distance between each triggering of the camera.", "type": "double", "decimalPlaces": 2, + "min": 0.1, "units": "m" } ] diff --git a/src/MissionManager/SurveyMissionItem.cc b/src/MissionManager/SurveyMissionItem.cc index a4531e5..e112271 100644 --- a/src/MissionManager/SurveyMissionItem.cc +++ b/src/MissionManager/SurveyMissionItem.cc @@ -17,22 +17,44 @@ QGC_LOGGING_CATEGORY(SurveyMissionItemLog, "SurveyMissionItemLog") -const char* SurveyMissionItem::_jsonTypeKey = "type"; -const char* SurveyMissionItem::_jsonPolygonKey = "polygon"; -const char* SurveyMissionItem::_jsonIdKey = "id"; -const char* SurveyMissionItem::_jsonGridAltitudeKey = "gridAltitude"; -const char* SurveyMissionItem::_jsonGridAltitudeRelativeKey = "gridAltitudeRelative"; -const char* SurveyMissionItem::_jsonGridAngleKey = "gridAngle"; -const char* SurveyMissionItem::_jsonGridSpacingKey = "gridSpacing"; -const char* SurveyMissionItem::_jsonTurnaroundDistKey = "turnaroundDist"; -const char* SurveyMissionItem::_jsonCameraTriggerKey = "cameraTrigger"; -const char* SurveyMissionItem::_jsonCameraTriggerDistanceKey = "cameraTriggerDistance"; - -const char* SurveyMissionItem::_gridAltitudeFactName = "Altitude"; -const char* SurveyMissionItem::_gridAngleFactName = "Grid angle"; -const char* SurveyMissionItem::_gridSpacingFactName = "Grid spacing"; -const char* SurveyMissionItem::_turnaroundDistFactName = "Turnaround dist."; -const char* SurveyMissionItem::_cameraTriggerDistanceFactName = "Camera trigger distance"; +const char* SurveyMissionItem::_jsonTypeKey = "type"; +const char* SurveyMissionItem::_jsonPolygonObjectKey = "polygon"; +const char* SurveyMissionItem::_jsonIdKey = "id"; +const char* SurveyMissionItem::_jsonGridObjectKey = "grid"; +const char* SurveyMissionItem::_jsonGridAltitudeKey = "altitude"; +const char* SurveyMissionItem::_jsonGridAltitudeRelativeKey = "relativeAltitude"; +const char* SurveyMissionItem::_jsonGridAngleKey = "angle"; +const char* SurveyMissionItem::_jsonGridSpacingKey = "spacing"; +const char* SurveyMissionItem::_jsonTurnaroundDistKey = "turnAroundDistance"; +const char* SurveyMissionItem::_jsonCameraTriggerKey = "cameraTrigger"; +const char* SurveyMissionItem::_jsonCameraTriggerDistanceKey = "cameraTriggerDistance"; +const char* SurveyMissionItem::_jsonGroundResolutionKey = "groundResolution"; +const char* SurveyMissionItem::_jsonFrontalOverlapKey = "imageFrontalOverlap"; +const char* SurveyMissionItem::_jsonSideOverlapKey = "imageSizeOverlap"; +const char* SurveyMissionItem::_jsonCameraSensorWidthKey = "sensorWidth"; +const char* SurveyMissionItem::_jsonCameraSensorHeightKey = "sensorHeight"; +const char* SurveyMissionItem::_jsonCameraResolutionWidthKey = "resolutionWidth"; +const char* SurveyMissionItem::_jsonCameraResolutionHeightKey = "resolutionHeight"; +const char* SurveyMissionItem::_jsonCameraFocalLengthKey = "focalLength"; +const char* SurveyMissionItem::_jsonCameraObjectKey = "camera"; +const char* SurveyMissionItem::_jsonCameraNameKey = "name"; +const char* SurveyMissionItem::_jsonManualGridKey = "manualGrid"; +const char* SurveyMissionItem::_jsonCameraOrientationLandscapeKey = "orientationLandscape"; +const char* SurveyMissionItem::_jsonFixedValueIsAltitudeKey = "fixedValueIsAltitude"; + +const char* SurveyMissionItem::_gridAltitudeFactName = "Altitude"; +const char* SurveyMissionItem::_gridAngleFactName = "Grid angle"; +const char* SurveyMissionItem::_gridSpacingFactName = "Grid spacing"; +const char* SurveyMissionItem::_turnaroundDistFactName = "Turnaround dist"; +const char* SurveyMissionItem::_cameraTriggerDistanceFactName = "Camera trigger distance"; +const char* SurveyMissionItem::_groundResolutionFactName = "Ground resolution"; +const char* SurveyMissionItem::_frontalOverlapFactName = "Frontal overlap"; +const char* SurveyMissionItem::_sideOverlapFactName = "Side overlap"; +const char* SurveyMissionItem::_cameraSensorWidthFactName = "Camera sensor width"; +const char* SurveyMissionItem::_cameraSensorHeightFactName = "Camera sensor height"; +const char* SurveyMissionItem::_cameraResolutionWidthFactName = "Camera resolution width"; +const char* SurveyMissionItem::_cameraResolutionHeightFactName = "Camera resolution height"; +const char* SurveyMissionItem::_cameraFocalLengthFactName = "Focal length"; const char* SurveyMissionItem::_complexType = "survey"; @@ -44,29 +66,57 @@ SurveyMissionItem::SurveyMissionItem(Vehicle* vehicle, QObject* parent) , _dirty(false) , _cameraTrigger(true) , _gridAltitudeRelative(true) + , _manualGrid(true) + , _cameraOrientationLandscape(true) + , _fixedValueIsAltitude(false) , _surveyDistance(0.0) , _cameraShots(0) , _coveredArea(0.0) - , _gridAltitudeFact (0, _gridAltitudeFactName, FactMetaData::valueTypeDouble) - , _gridAngleFact (0, _gridAngleFactName, FactMetaData::valueTypeDouble) - , _gridSpacingFact (0, _gridSpacingFactName, FactMetaData::valueTypeDouble) - , _turnaroundDistFact (0, _turnaroundDistFactName, FactMetaData::valueTypeDouble) - , _cameraTriggerDistanceFact(0, _cameraTriggerDistanceFactName, FactMetaData::valueTypeDouble) + , _gridAltitudeFact (0, _gridAltitudeFactName, FactMetaData::valueTypeDouble) + , _gridAngleFact (0, _gridAngleFactName, FactMetaData::valueTypeDouble) + , _gridSpacingFact (0, _gridSpacingFactName, FactMetaData::valueTypeDouble) + , _turnaroundDistFact (0, _turnaroundDistFactName, FactMetaData::valueTypeDouble) + , _cameraTriggerDistanceFact (0, _cameraTriggerDistanceFactName, FactMetaData::valueTypeDouble) + , _groundResolutionFact (0, _groundResolutionFactName, FactMetaData::valueTypeDouble) + , _frontalOverlapFact (0, _frontalOverlapFactName, FactMetaData::valueTypeDouble) + , _sideOverlapFact (0, _sideOverlapFactName, FactMetaData::valueTypeDouble) + , _cameraSensorWidthFact (0, _cameraSensorWidthFactName, FactMetaData::valueTypeDouble) + , _cameraSensorHeightFact (0, _cameraSensorHeightFactName, FactMetaData::valueTypeDouble) + , _cameraResolutionWidthFact (0, _cameraResolutionWidthFactName, FactMetaData::valueTypeUint32) + , _cameraResolutionHeightFact (0, _cameraResolutionHeightFactName, FactMetaData::valueTypeUint32) + , _cameraFocalLengthFact (0, _cameraFocalLengthFactName, FactMetaData::valueTypeDouble) { if (_metaDataMap.isEmpty()) { _metaDataMap = FactMetaData::createMapFromJsonFile(QStringLiteral(":/json/Survey.FactMetaData.json"), NULL /* metaDataParent */); } _gridAltitudeFact.setRawValue(50); - _gridSpacingFact.setRawValue(10); - _turnaroundDistFact.setRawValue(60); + _gridSpacingFact.setRawValue(30); + _turnaroundDistFact.setRawValue(_vehicle->multiRotor() ? 0 : 60); _cameraTriggerDistanceFact.setRawValue(25); + _groundResolutionFact.setRawValue(3); + _frontalOverlapFact.setRawValue(10); + _sideOverlapFact.setRawValue(10); + + _cameraSensorWidthFact.setRawValue(6.17); + _cameraSensorHeightFact.setRawValue(4.55); + _cameraResolutionWidthFact.setRawValue(4000); + _cameraResolutionHeightFact.setRawValue(3000); + _cameraFocalLengthFact.setRawValue(4.5); _gridAltitudeFact.setMetaData(_metaDataMap[_gridAltitudeFactName]); _gridAngleFact.setMetaData(_metaDataMap[_gridAngleFactName]); _gridSpacingFact.setMetaData(_metaDataMap[_gridSpacingFactName]); _turnaroundDistFact.setMetaData(_metaDataMap[_turnaroundDistFactName]); _cameraTriggerDistanceFact.setMetaData(_metaDataMap[_cameraTriggerDistanceFactName]); + _groundResolutionFact.setMetaData(_metaDataMap[_groundResolutionFactName]); + _frontalOverlapFact.setMetaData(_metaDataMap[_frontalOverlapFactName]); + _sideOverlapFact.setMetaData(_metaDataMap[_sideOverlapFactName]); + _cameraSensorWidthFact.setMetaData(_metaDataMap[_cameraSensorWidthFactName]); + _cameraSensorHeightFact.setMetaData(_metaDataMap[_cameraSensorHeightFactName]); + _cameraResolutionWidthFact.setMetaData(_metaDataMap[_cameraResolutionWidthFactName]); + _cameraResolutionHeightFact.setMetaData(_metaDataMap[_cameraResolutionHeightFactName]); + _cameraFocalLengthFact.setMetaData(_metaDataMap[_cameraFocalLengthFactName]); connect(&_gridSpacingFact, &Fact::valueChanged, this, &SurveyMissionItem::_generateGrid); connect(&_gridAngleFact, &Fact::valueChanged, this, &SurveyMissionItem::_generateGrid); @@ -74,18 +124,17 @@ SurveyMissionItem::SurveyMissionItem(Vehicle* vehicle, QObject* parent) connect(&_cameraTriggerDistanceFact, &Fact::valueChanged, this, &SurveyMissionItem::_generateGrid); connect(&_gridAltitudeFact, &Fact::valueChanged, this, &SurveyMissionItem::_updateCoordinateAltitude); - connect(this, &SurveyMissionItem::cameraTriggerChanged, this, &SurveyMissionItem::_cameraTriggerChanged); -} - -const SurveyMissionItem& SurveyMissionItem::operator=(const SurveyMissionItem& other) -{ - ComplexMissionItem::operator=(other); - - _setSurveyDistance(other._surveyDistance); - _setCameraShots(other._cameraShots); - _setCoveredArea(other._coveredArea); + // Signal to Qml when camera value changes to it can recalc + connect(&_groundResolutionFact, &Fact::valueChanged, this, &SurveyMissionItem::_cameraValueChanged); + connect(&_frontalOverlapFact, &Fact::valueChanged, this, &SurveyMissionItem::_cameraValueChanged); + connect(&_sideOverlapFact, &Fact::valueChanged, this, &SurveyMissionItem::_cameraValueChanged); + connect(&_cameraSensorWidthFact, &Fact::valueChanged, this, &SurveyMissionItem::_cameraValueChanged); + connect(&_cameraSensorHeightFact, &Fact::valueChanged, this, &SurveyMissionItem::_cameraValueChanged); + connect(&_cameraResolutionWidthFact, &Fact::valueChanged, this, &SurveyMissionItem::_cameraValueChanged); + connect(&_cameraResolutionHeightFact, &Fact::valueChanged, this, &SurveyMissionItem::_cameraValueChanged); + connect(&_cameraFocalLengthFact, &Fact::valueChanged, this, &SurveyMissionItem::_cameraValueChanged); - return *this; + connect(this, &SurveyMissionItem::cameraTriggerChanged, this, &SurveyMissionItem::_cameraTriggerChanged); } void SurveyMissionItem::_setSurveyDistance(double surveyDistance) @@ -190,16 +239,41 @@ void SurveyMissionItem::setDirty(bool dirty) void SurveyMissionItem::save(QJsonObject& saveObject) const { - saveObject[JsonHelper::jsonVersionKey] = 1; + saveObject[JsonHelper::jsonVersionKey] = 2; saveObject[_jsonTypeKey] = _complexType; saveObject[_jsonIdKey] = sequenceNumber(); - saveObject[_jsonGridAltitudeKey] = _gridAltitudeFact.rawValue().toDouble(); - saveObject[_jsonGridAltitudeRelativeKey] = _gridAltitudeRelative; - saveObject[_jsonGridAngleKey] = _gridAngleFact.rawValue().toDouble(); - saveObject[_jsonGridSpacingKey] = _gridSpacingFact.rawValue().toDouble(); - saveObject[_jsonTurnaroundDistKey] = _turnaroundDistFact.rawValue().toDouble(); saveObject[_jsonCameraTriggerKey] = _cameraTrigger; - saveObject[_jsonCameraTriggerDistanceKey] = _cameraTriggerDistanceFact.rawValue().toDouble(); + saveObject[_jsonManualGridKey] = _manualGrid; + saveObject[_jsonFixedValueIsAltitudeKey] = _fixedValueIsAltitude; + + if (_cameraTrigger) { + saveObject[_jsonCameraTriggerDistanceKey] = _cameraTriggerDistanceFact.rawValue().toDouble(); + } + + QJsonObject gridObject; + gridObject[_jsonGridAltitudeKey] = _gridAltitudeFact.rawValue().toDouble(); + gridObject[_jsonGridAltitudeRelativeKey] = _gridAltitudeRelative; + gridObject[_jsonGridAngleKey] = _gridAngleFact.rawValue().toDouble(); + gridObject[_jsonGridSpacingKey] = _gridSpacingFact.rawValue().toDouble(); + gridObject[_jsonTurnaroundDistKey] = _turnaroundDistFact.rawValue().toDouble(); + + saveObject[_jsonGridObjectKey] = gridObject; + + if (!_manualGrid) { + QJsonObject cameraObject; + cameraObject[_jsonCameraNameKey] = _camera; + cameraObject[_jsonCameraOrientationLandscapeKey] = _cameraOrientationLandscape; + 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[_jsonGroundResolutionKey] = _groundResolutionFact.rawValue().toDouble(); + cameraObject[_jsonFrontalOverlapKey] = _frontalOverlapFact.rawValue().toInt(); + cameraObject[_jsonSideOverlapKey] = _sideOverlapFact.rawValue().toInt(); + + saveObject[_jsonCameraObjectKey] = cameraObject; + } // Polygon shape @@ -213,7 +287,7 @@ void SurveyMissionItem::save(QJsonObject& saveObject) const polygonArray += jsonValue; } - saveObject[_jsonPolygonKey] = polygonArray; + saveObject[_jsonPolygonObjectKey] = polygonArray; } void SurveyMissionItem::setSequenceNumber(int sequenceNumber) @@ -234,55 +308,115 @@ void SurveyMissionItem::_clear(void) bool SurveyMissionItem::load(const QJsonObject& complexObject, QString& errorString) { - _clear(); - - // Validate requires keys - QStringList requiredKeys; - requiredKeys << JsonHelper::jsonVersionKey << _jsonTypeKey << _jsonIdKey << _jsonPolygonKey << _jsonGridAltitudeKey << _jsonGridAngleKey << _jsonGridSpacingKey << - _jsonCameraTriggerKey << _jsonCameraTriggerDistanceKey << _jsonGridAltitudeRelativeKey; - if (!JsonHelper::validateRequiredKeys(complexObject, requiredKeys, errorString)) { - _clear(); + struct jsonKeyInfo_s { + const char* key; + QJsonValue::Type type; + bool required; + }; + + QList mainKeyInfoList = { + { JsonHelper::jsonVersionKey, QJsonValue::Double, true }, + { _jsonTypeKey, QJsonValue::String, true }, + { _jsonPolygonObjectKey, QJsonValue::Array, true }, + { _jsonIdKey, QJsonValue::Double, true }, + { _jsonGridObjectKey, QJsonValue::Object, true }, + { _jsonCameraObjectKey, QJsonValue::Object, false }, + { _jsonCameraTriggerKey, QJsonValue::Bool, true }, + { _jsonCameraTriggerDistanceKey, QJsonValue::Double, false }, + { _jsonManualGridKey, QJsonValue::Bool, true }, + { _jsonFixedValueIsAltitudeKey, QJsonValue::Bool, true }, + }; + + QList gridKeyInfoList = { + { _jsonGridAltitudeKey, QJsonValue::Double, true }, + { _jsonGridAltitudeRelativeKey, QJsonValue::Bool, true }, + { _jsonGridAngleKey, QJsonValue::Double, true }, + { _jsonGridSpacingKey, QJsonValue::Double, true }, + { _jsonTurnaroundDistKey, QJsonValue::Double, true }, + }; + + 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 }, + }; + + if (!JsonHelper::validateKeys(complexObject, mainKeyInfoList, errorString)) { return false; } - - // Validate types - QStringList keyList; - QList typeList; - keyList << JsonHelper::jsonVersionKey << _jsonTypeKey << _jsonIdKey << _jsonPolygonKey << _jsonGridAltitudeKey << _jsonGridAngleKey << _jsonGridSpacingKey << _jsonTurnaroundDistKey << - _jsonCameraTriggerKey << _jsonCameraTriggerDistanceKey << _jsonGridAltitudeRelativeKey; - typeList << QJsonValue::Double << QJsonValue::String << QJsonValue::Double << QJsonValue::Array << QJsonValue::Double << QJsonValue::Double<< QJsonValue::Double << QJsonValue::Double << - QJsonValue::Bool << QJsonValue::Double << QJsonValue::Bool; - if (!JsonHelper::validateKeyTypes(complexObject, keyList, typeList, errorString)) { - _clear(); + if (!JsonHelper::validateKeys(complexObject[_jsonGridObjectKey].toObject(), gridKeyInfoList, errorString)) { return false; } // Version check - if (complexObject[JsonHelper::jsonVersionKey].toInt() != 1) { + if (complexObject[JsonHelper::jsonVersionKey].toInt() != 2) { errorString = tr("QGroundControl does not support this version of survey items"); - _clear(); return false; } QString complexType = complexObject[_jsonTypeKey].toString(); if (complexType != _complexType) { errorString = tr("QGroundControl does not support loading this complex mission item type: %1").arg(complexType); - _clear(); return false; } + _clear(); + setSequenceNumber(complexObject[_jsonIdKey].toInt()); - _cameraTrigger = complexObject[_jsonCameraTriggerKey].toBool(); - _gridAltitudeRelative = complexObject[_jsonGridAltitudeRelativeKey].toBool(); + _manualGrid = complexObject[_jsonManualGridKey].toBool(true); + _cameraTrigger = complexObject[_jsonCameraTriggerKey].toBool(false); + _fixedValueIsAltitude = complexObject[_jsonFixedValueIsAltitudeKey].toBool(true); + _gridAltitudeRelative = complexObject[_jsonGridAltitudeRelativeKey].toBool(true); - _gridAltitudeFact.setRawValue (complexObject[_jsonGridAltitudeKey].toDouble()); - _gridAngleFact.setRawValue (complexObject[_jsonGridAngleKey].toDouble()); - _gridSpacingFact.setRawValue (complexObject[_jsonGridSpacingKey].toDouble()); - _turnaroundDistFact.setRawValue (complexObject[_jsonTurnaroundDistKey].toDouble()); - _cameraTriggerDistanceFact.setRawValue (complexObject[_jsonCameraTriggerDistanceKey].toDouble()); + QJsonObject gridObject = complexObject[_jsonGridObjectKey].toObject(); + + _gridAltitudeFact.setRawValue (gridObject[_jsonGridAltitudeKey].toDouble()); + _gridAngleFact.setRawValue (gridObject[_jsonGridAngleKey].toDouble()); + _gridSpacingFact.setRawValue (gridObject[_jsonGridSpacingKey].toDouble()); + _turnaroundDistFact.setRawValue (gridObject[_jsonTurnaroundDistKey].toDouble()); + + if (_cameraTrigger) { + if (!complexObject.contains(_jsonCameraTriggerDistanceKey)) { + errorString = tr("%1 but %2 is missing").arg("cameraTrigger = true").arg("cameraTriggerDistance"); + return false; + } + _cameraTriggerDistanceFact.setRawValue(complexObject[_jsonCameraTriggerDistanceKey].toDouble()); + } + + if (!_manualGrid) { + if (!complexObject.contains(_jsonCameraObjectKey)) { + errorString = tr("%1 but %2 object is missing").arg("manualGrid = false").arg("camera"); + return false; + } + + QJsonObject cameraObject = complexObject[_jsonCameraObjectKey].toObject(); + + if (!JsonHelper::validateKeys(cameraObject, cameraKeyInfoList, errorString)) { + return false; + } + + _camera = cameraObject[_jsonCameraNameKey].toString(); + _cameraOrientationLandscape = 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()); + } // Polygon shape - QJsonArray polygonArray(complexObject[_jsonPolygonKey].toArray()); + QJsonArray polygonArray(complexObject[_jsonPolygonObjectKey].toArray()); for (int i=0; i& lineList, QLis void SurveyMissionItem::_gridGenerator(const QList& polygonPoints, QList& gridPoints) { double gridAngle = _gridAngleFact.rawValue().toDouble(); + double gridSpacing = _gridSpacingFact.rawValue().toDouble(); + + qCDebug(SurveyMissionItemLog) << "SurveyMissionItem::_gridGenerator gridSpacing:gridAngle" << gridSpacing << gridAngle; gridPoints.clear(); @@ -557,7 +694,6 @@ void SurveyMissionItem::_gridGenerator(const QList& polygonPoints, QLi // Create set of rotated parallel lines within the expanded bounding rect. Make the lines larger than the // bounding box to guarantee intersection. QList lineList; - float gridSpacing = _gridSpacingFact.rawValue().toDouble(); float x = largeBoundRect.topLeft().x() - (gridSpacing / 2); while (x < largeBoundRect.bottomRight().x()) { float yTop = largeBoundRect.topLeft().y() - 100.0; @@ -671,3 +807,8 @@ int SurveyMissionItem::cameraShots(void) const { return _cameraTrigger ? _cameraShots : 0; } + +void SurveyMissionItem::_cameraValueChanged(void) +{ + emit cameraValueChanged(); +} diff --git a/src/MissionManager/SurveyMissionItem.h b/src/MissionManager/SurveyMissionItem.h index ed34cb8..58bed7e 100644 --- a/src/MissionManager/SurveyMissionItem.h +++ b/src/MissionManager/SurveyMissionItem.h @@ -25,19 +25,29 @@ class SurveyMissionItem : public ComplexMissionItem public: SurveyMissionItem(Vehicle* vehicle, QObject* parent = NULL); - const SurveyMissionItem& operator=(const SurveyMissionItem& other); - - Q_PROPERTY(Fact* gridAltitude READ gridAltitude CONSTANT) - Q_PROPERTY(bool gridAltitudeRelative MEMBER _gridAltitudeRelative NOTIFY gridAltitudeRelativeChanged) - Q_PROPERTY(Fact* gridAngle READ gridAngle CONSTANT) - Q_PROPERTY(Fact* gridSpacing READ gridSpacing CONSTANT) - Q_PROPERTY(Fact* turnaroundDist READ turnaroundDist CONSTANT) - Q_PROPERTY(bool cameraTrigger MEMBER _cameraTrigger NOTIFY cameraTriggerChanged) - Q_PROPERTY(Fact* cameraTriggerDistance READ cameraTriggerDistance CONSTANT) - Q_PROPERTY(QVariantList polygonPath READ polygonPath NOTIFY polygonPathChanged) - Q_PROPERTY(QVariantList gridPoints READ gridPoints NOTIFY gridPointsChanged) - Q_PROPERTY(int cameraShots READ cameraShots NOTIFY cameraShotsChanged) - Q_PROPERTY(double coveredArea READ coveredArea NOTIFY coveredAreaChanged) + Q_PROPERTY(Fact* gridAltitude READ gridAltitude CONSTANT) + Q_PROPERTY(bool gridAltitudeRelative MEMBER _gridAltitudeRelative NOTIFY gridAltitudeRelativeChanged) + Q_PROPERTY(Fact* gridAngle READ gridAngle CONSTANT) + Q_PROPERTY(Fact* gridSpacing READ gridSpacing CONSTANT) + Q_PROPERTY(Fact* turnaroundDist READ turnaroundDist CONSTANT) + Q_PROPERTY(bool cameraTrigger MEMBER _cameraTrigger NOTIFY cameraTriggerChanged) + Q_PROPERTY(Fact* cameraTriggerDistance READ cameraTriggerDistance CONSTANT) + Q_PROPERTY(Fact* groundResolution READ groundResolution CONSTANT) + Q_PROPERTY(Fact* frontalOverlap READ frontalOverlap CONSTANT) + Q_PROPERTY(Fact* sideOverlap READ sideOverlap CONSTANT) + Q_PROPERTY(Fact* cameraSensorWidth READ cameraSensorWidth CONSTANT) + Q_PROPERTY(Fact* cameraSensorHeight READ cameraSensorHeight CONSTANT) + Q_PROPERTY(Fact* cameraResolutionWidth READ cameraResolutionWidth CONSTANT) + Q_PROPERTY(Fact* cameraResolutionHeight READ cameraResolutionHeight CONSTANT) + Q_PROPERTY(Fact* cameraFocalLength READ cameraFocalLength CONSTANT) + Q_PROPERTY(QVariantList polygonPath READ polygonPath NOTIFY polygonPathChanged) + Q_PROPERTY(QVariantList gridPoints READ gridPoints NOTIFY gridPointsChanged) + Q_PROPERTY(int cameraShots READ cameraShots NOTIFY cameraShotsChanged) + Q_PROPERTY(double coveredArea READ coveredArea NOTIFY coveredAreaChanged) + Q_PROPERTY(bool fixedValueIsAltitude MEMBER _fixedValueIsAltitude NOTIFY fixedValueIsAltitudeChanged) + Q_PROPERTY(bool cameraOrientationLandscape MEMBER _cameraOrientationLandscape NOTIFY cameraOrientationLandscapeChanged) + Q_PROPERTY(bool manualGrid MEMBER _manualGrid NOTIFY manualGridChanged) + Q_PROPERTY(QString camera MEMBER _camera NOTIFY cameraChanged) Q_INVOKABLE void clearPolygon(void); Q_INVOKABLE void addPolygonCoordinate(const QGeoCoordinate coordinate); @@ -46,11 +56,19 @@ public: QVariantList polygonPath(void) { return _polygonPath; } QVariantList gridPoints (void) { return _gridPoints; } - Fact* gridAltitude(void) { return &_gridAltitudeFact; } - Fact* gridAngle(void) { return &_gridAngleFact; } - Fact* gridSpacing(void) { return &_gridSpacingFact; } - Fact* turnaroundDist(void) { return &_turnaroundDistFact; } - Fact* cameraTriggerDistance(void) { return &_cameraTriggerDistanceFact; } + Fact* gridAltitude (void) { return &_gridAltitudeFact; } + Fact* gridAngle (void) { return &_gridAngleFact; } + Fact* gridSpacing (void) { return &_gridSpacingFact; } + Fact* turnaroundDist (void) { return &_turnaroundDistFact; } + Fact* cameraTriggerDistance (void) { return &_cameraTriggerDistanceFact; } + Fact* groundResolution (void) { return &_groundResolutionFact; } + Fact* frontalOverlap (void) { return &_frontalOverlapFact; } + Fact* sideOverlap (void) { return &_sideOverlapFact; } + Fact* cameraSensorWidth (void) { return &_cameraSensorWidthFact; } + Fact* cameraSensorHeight (void) { return &_cameraSensorHeightFact; } + Fact* cameraResolutionWidth (void) { return &_cameraResolutionWidthFact; } + Fact* cameraResolutionHeight (void) { return &_cameraResolutionHeightFact; } + Fact* cameraFocalLength (void) { return &_cameraFocalLengthFact; } int cameraShots(void) const; double coveredArea(void) const { return _coveredArea; } @@ -87,14 +105,20 @@ public: void save (QJsonObject& saveObject) const final; signals: - void polygonPathChanged (void); - void altitudeChanged (double altitude); - void gridAngleChanged (double gridAngle); - void gridPointsChanged (void); - void cameraTriggerChanged (bool cameraTrigger); - void gridAltitudeRelativeChanged (bool gridAltitudeRelative); - void cameraShotsChanged (int cameraShots); - void coveredAreaChanged (double coveredArea); + void polygonPathChanged (void); + void altitudeChanged (double altitude); + void gridAngleChanged (double gridAngle); + void gridPointsChanged (void); + void cameraTriggerChanged (bool cameraTrigger); + void gridAltitudeRelativeChanged (bool gridAltitudeRelative); + void cameraShotsChanged (int cameraShots); + void coveredAreaChanged (double coveredArea); + void cameraValueChanged (void); + void fixedValueIsAltitudeChanged (bool fixedValueIsAltitude); + void gridTypeChanged (QString gridType); + void cameraOrientationLandscapeChanged (bool cameraOrientationLandscape); + void cameraChanged (QString camera); + void manualGridChanged (bool manualGrid); private slots: void _cameraTriggerChanged(void); @@ -113,33 +137,46 @@ private: void _setSurveyDistance(double surveyDistance); void _setCameraShots(int cameraShots); void _setCoveredArea(double coveredArea); - - int _sequenceNumber; - bool _dirty; - QVariantList _polygonPath; - QVariantList _gridPoints; - QGeoCoordinate _coordinate; - QGeoCoordinate _exitCoordinate; - double _altitude; - double _gridAngle; - bool _cameraTrigger; - bool _gridAltitudeRelative; - - double _surveyDistance; - int _cameraShots; - double _coveredArea; + void _cameraValueChanged(void); + + int _sequenceNumber; + bool _dirty; + QVariantList _polygonPath; + QVariantList _gridPoints; + QGeoCoordinate _coordinate; + QGeoCoordinate _exitCoordinate; + double _altitude; + bool _cameraTrigger; + bool _gridAltitudeRelative; + bool _manualGrid; + QString _camera; + bool _cameraOrientationLandscape; + bool _fixedValueIsAltitude; + + double _surveyDistance; + int _cameraShots; + double _coveredArea; Fact _gridAltitudeFact; Fact _gridAngleFact; Fact _gridSpacingFact; Fact _turnaroundDistFact; Fact _cameraTriggerDistanceFact; + Fact _groundResolutionFact; + Fact _frontalOverlapFact; + Fact _sideOverlapFact; + Fact _cameraSensorWidthFact; + Fact _cameraSensorHeightFact; + Fact _cameraResolutionWidthFact; + Fact _cameraResolutionHeightFact; + Fact _cameraFocalLengthFact; static QMap _metaDataMap; static const char* _jsonTypeKey; - static const char* _jsonPolygonKey; + static const char* _jsonPolygonObjectKey; static const char* _jsonIdKey; + static const char* _jsonGridObjectKey; static const char* _jsonGridAltitudeKey; static const char* _jsonGridAltitudeRelativeKey; static const char* _jsonGridAngleKey; @@ -147,12 +184,34 @@ private: static const char* _jsonTurnaroundDistKey; static const char* _jsonCameraTriggerKey; static const char* _jsonCameraTriggerDistanceKey; + static const char* _jsonGroundResolutionKey; + static const char* _jsonFrontalOverlapKey; + static const char* _jsonSideOverlapKey; + static const char* _jsonCameraSensorWidthKey; + static const char* _jsonCameraSensorHeightKey; + static const char* _jsonCameraResolutionWidthKey; + static const char* _jsonCameraResolutionHeightKey; + static const char* _jsonCameraFocalLengthKey; + static const char* _jsonManualGridKey; + static const char* _jsonCameraObjectKey; + static const char* _jsonCameraNameKey; + static const char* _jsonCameraOrientationLandscapeKey; + static const char* _jsonFixedValueIsAltitudeKey; + static const char* _gridAltitudeFactName; static const char* _gridAngleFactName; static const char* _gridSpacingFactName; static const char* _turnaroundDistFactName; static const char* _cameraTriggerDistanceFactName; + static const char* _groundResolutionFactName; + static const char* _frontalOverlapFactName; + static const char* _sideOverlapFactName; + static const char* _cameraSensorWidthFactName; + static const char* _cameraSensorHeightFactName; + static const char* _cameraResolutionWidthFactName; + static const char* _cameraResolutionHeightFactName; + static const char* _cameraFocalLengthFactName; static const char* _complexType; };