Skip to content
SurveyItemEditor.qml 21.9 KiB
Newer Older
import QtQuick                  2.2
import QtQuick.Controls         1.2
import QtQuick.Dialogs          1.2
import QGroundControl               1.0
import QGroundControl.ScreenTools   1.0
import QGroundControl.Vehicle       1.0
import QGroundControl.Controls      1.0
import QGroundControl.FactControls  1.0
import QGroundControl.Palette       1.0

// Editor for Survery mission items
Rectangle {
    id:         _root
    height:     visible ? (editorColumn.height + (_margin * 2)) : 0
    width:      availableWidth
    color:      qgcPal.windowShadeDark
    radius:     _radius

Ricardo de Almeida Gonzaga's avatar
Ricardo de Almeida Gonzaga committed
    // The following properties must be available up the hierarchy chain
    //property real   availableWidth    ///< Width for control
    //property var    missionItem       ///< Mission Item for editor

Don Gagne's avatar
Don Gagne committed
    property real _margin: ScreenTools.defaultFontPixelWidth / 2
    property int cameraIndex: 1

    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 {
Andre Kjellstrup's avatar
Andre Kjellstrup committed
               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
           }
Don Gagne's avatar
Don Gagne committed

    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
Don Gagne's avatar
Don Gagne committed
            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
Don Gagne's avatar
Don Gagne committed
        var gridSpacing
        var cameraTriggerDistance

        altitude = (imageWidth * gsd * focalLength) / (sensorWidth * 100)

Don Gagne's avatar
Don Gagne committed
        if (cameraOrientationLandscape.checked) {
            imageSizeSideGround = (imageWidth * gsd) / 100
            imageSizeFrontGround = (imageHeight * gsd) / 100
Don Gagne's avatar
Don Gagne committed
        } else {
            imageSizeSideGround = (imageHeight * gsd) / 100
            imageSizeFrontGround = (imageWidth * gsd) / 100
        gridSpacing = imageSizeSideGround * ( (100-sideOverlap) / 100 )
        cameraTriggerDistance = imageSizeFrontGround * ( (100-frontalOverlap) / 100 )

        missionItem.gridAltitude.rawValue = altitude
Don Gagne's avatar
Don Gagne committed
        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 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"
            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

        gsd = (altitude * sensorWidth * 100) / (imageWidth * focalLength)

        if (cameraOrientationLandscape.checked) {
            imageSizeSideGround = (imageWidth * gsd) / 100
            imageSizeFrontGround = (imageHeight * gsd) / 100
        } else {
            imageSizeSideGround = (imageHeight * gsd) / 100
            imageSizeFrontGround = (imageWidth * gsd) / 100
        }

        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)
    }

    function polygonCaptureStarted() {
    	missionItem.clearPolygon()
Don Gagne's avatar
Don Gagne committed

    function polygonCaptureFinished(coordinates) {
        for (var i=0; i<coordinates.length; i++) {
            missionItem.addPolygonCoordinate(coordinates[i])
Don Gagne's avatar
Don Gagne committed
        }
Don Gagne's avatar
Don Gagne committed

    function polygonAdjustVertex(vertexIndex, vertexCoordinate) {
        missionItem.adjustPolygonCoordinate(vertexIndex, vertexCoordinate)
    function polygonAdjustStarted() { }
    function polygonAdjustFinished() { }

    QGCPalette { id: qgcPal; colorGroupEnabled: true }

Don Gagne's avatar
Don Gagne committed
    ExclusiveGroup {
        id:                 cameraOrientationGroup
        onCurrentChanged:   {
            recalcFromMissionValues()
        }
    Column {
        id:                 editorColumn
        anchors.margins:    _margin
        anchors.top:        parent.top
        anchors.left:       parent.left
        anchors.right:      parent.right
        spacing:            _margin

        QGCLabel {
            anchors.left:   parent.left
            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.")
        Repeater {
            model: [ missionItem.gridAngle, missionItem.gridSpacing, missionItem.gridAltitude, missionItem.turnaroundDist ]

            Item {
                anchors.left:   parent.left
                anchors.right:  parent.right
                height:         textField.height

                QGCLabel {
                    anchors.baseline:   textField.baseline
                    anchors.left:       parent.left
                    text:               modelData.name + ":"
                }

                FactTextField {
                    id:             textField
                    anchors.right:  parent.right
                    width:          _editFieldWidth
                    showUnits:      true
                    fact:           modelData
                    onEditingFinished: recalcFromMissionValues()
                    validator:      DoubleValidator{bottom:0.0; decimals:2}
Don Gagne's avatar
Don Gagne committed
        QGCCheckBox {
            anchors.left:   parent.left
            text:           qsTr("Relative altitude")
Don Gagne's avatar
Don Gagne committed
            checked:        missionItem.gridAltitudeRelative
            onClicked:      missionItem.gridAltitudeRelative = checked
        }

        Grid {
            columns: 2
            columnSpacing:  ScreenTools.defaultFontPixelWidth
            rowSpacing:     _margin
            verticalItemAlignment: Grid.AlignVCenter

            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}
            }

            QGCLabel { text: qsTr("Frontal Overlap:") }
            QGCTextField {
                id:         frontalOverlapField
                width:      _editFieldWidth
                unitsLabel: "%"
                showUnits:  true
                onEditingFinished: recalcFromCameraValues()
                validator:  IntValidator {bottom:0}
            }

            QGCLabel { text: qsTr("Side Overlap:") }
            QGCTextField {
                id:         sideOverlapField
                width:      _editFieldWidth
                unitsLabel: "%"
                showUnits:  true
                onEditingFinished: recalcFromCameraValues()
                validator:  IntValidator {bottom:0}
            }

            Component.onCompleted: recalcFromMissionValues()
        }

Don Gagne's avatar
Don Gagne committed
        QGCLabel { text: qsTr("Camera:") }

        Rectangle {
            anchors.left:   parent.left
            anchors.right:  parent.right
            height:         1
            color:          qgcPal.text
        }

Don Gagne's avatar
Don Gagne committed
            spacing: ScreenTools.defaultFontPixelWidth
            verticalItemAlignment: Grid.AlignVCenter
Don Gagne's avatar
Don Gagne committed

            QGCRadioButton {
                id:             cameraOrientationLandscape
                width:          _editFieldWidth
Don Gagne's avatar
Don Gagne committed
                text:           "Landscape"
                checked:        true
                exclusiveGroup: cameraOrientationGroup
            }

            QGCRadioButton {
                id:             cameraOrientationPortrait
                text:           "Portrait"
                exclusiveGroup: cameraOrientationGroup
            }

Don Gagne's avatar
Don Gagne committed
            QGCCheckBox {
                id:         cameraTrigger
                width:      _editFieldWidth
Don Gagne's avatar
Don Gagne committed
                text:       qsTr("Trigger:")
                checked:    missionItem.cameraTrigger
                onClicked:  missionItem.cameraTrigger = checked
            }

            FactTextField {
                width:      _editFieldWidth
                showUnits:  true
                fact:       missionItem.cameraTriggerDistance
                enabled:    missionItem.cameraTrigger
                onEditingFinished: recalcFromMissionValues()
                validator:  DoubleValidator{bottom:0.0; decimals:2}
Don Gagne's avatar
Don Gagne committed
            }
Don Gagne's avatar
Don Gagne committed

        Component {
            id: cameraFields
                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
                                }
                            }
                        }
                    }

                    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))
                                }
                            }
                        }
                    }
                }
                function accept() {
                    hideDialog()
                    recalcFromCameraValues()
                }
            }//QGCViewDialog
        }//Component

        Column {
            spacing: ScreenTools.defaultFontPixelHeight*0.01

            Row {
                spacing: ScreenTools.defaultFontPixelWidth

                QGCLabel { text: qsTr("Model:") }
                QGCLabel { text: cameraModelList.get(cameraIndex).text }
            Grid {
                columns: 2
                columnSpacing: ScreenTools.defaultFontPixelWidth
                rowSpacing: ScreenTools.defaultFontPixelHeight*0.01
                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
                }

                QGCLabel { text: qsTr("Image Size:") }
                QGCLabel { text: cameraModelList.get(cameraIndex).imageWidth.toFixed(0) + qsTr(" x ") +  cameraModelList.get(cameraIndex).imageHeight.toFixed(0) }

                QGCLabel { text: qsTr("Focal length:") }
                QGCLabel { text: cameraModelList.get(cameraIndex).focalLength.toFixed(2) }
Don Gagne's avatar
Don Gagne committed
            }
        QGCButton {
            id:         cameraModelChange
            text:       qsTr("Change")
            onClicked: {
                qgcView.showDialog(cameraFields, qsTr("Set Camera Model"), qgcView.showDialogDefaultWidth, StandardButton.Save)
        QGCLabel { text: qsTr("Polygon:") }
        Rectangle {
            anchors.left:   parent.left
            anchors.right:  parent.right
            height:         1
            color:          qgcPal.text
        }

        Row {
            spacing: ScreenTools.defaultFontPixelWidth

            QGCButton {
                text:       editorMap.polygonDraw.drawingPolygon ? qsTr("Finish Draw") : qsTr("Draw")
                visible:    !editorMap.polygonDraw.adjustingPolygon
                enabled:    ((editorMap.polygonDraw.drawingPolygon && editorMap.polygonDraw.polygonReady) || !editorMap.polygonDraw.drawingPolygon)

                onClicked: {
                    if (editorMap.polygonDraw.drawingPolygon) {
                        editorMap.polygonDraw.finishCapturePolygon()
                    } else {
                        editorMap.polygonDraw.startCapturePolygon(_root)
            QGCButton {
                text:       editorMap.polygonDraw.adjustingPolygon ? qsTr("Finish Adjust") : qsTr("Adjust")
                visible:    missionItem.polygonPath.length > 0 && !editorMap.polygonDraw.drawingPolygon
                onClicked: {
                    if (editorMap.polygonDraw.adjustingPolygon) {
                        editorMap.polygonDraw.finishAdjustPolygon()
                    } else {
                        editorMap.polygonDraw.startAdjustPolygon(_root, missionItem.polygonPath)

        QGCLabel { text: qsTr("Statistics:") }

        Rectangle {
            anchors.left:   parent.left
            anchors.right:  parent.right
            height:         1
            color:          qgcPal.text
        }

        Grid {
            columns: 2
            spacing: ScreenTools.defaultFontPixelWidth

            QGCLabel { text: qsTr("Survey area:") }
            QGCLabel { text: QGroundControl.squareMetersToAppSettingsAreaUnits(missionItem.coveredArea).toFixed(2) + " " + QGroundControl.appSettingsAreaUnitsString }

            QGCLabel { text: qsTr("# shots:") }
            QGCLabel { text: missionItem.cameraShots }
        }