SurveyItemEditor.qml 20.3 KB
Newer Older
1 2
import QtQuick          2.3
import QtQuick.Controls 1.2
Don Gagne's avatar
Don Gagne committed
3
import QtQuick.Dialogs  1.2
4
import QtQuick.Layouts  1.2
5

6
import QGroundControl               1.0
7 8 9 10 11
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
12
import QGroundControl.FlightMap     1.0
13 14 15 16

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

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

26
    property real   _margin:            ScreenTools.defaultFontPixelWidth / 2
27 28 29 30 31
    property int    _cameraIndex:       1
    property real   _fieldWidth:        ScreenTools.defaultFontPixelWidth * 10.5
    property var    _cameraList:        [ qsTr("Manual Grid (no camera specs)"), qsTr("Custom Camera Grid") ]
    property var    _vehicle:           QGroundControl.multiVehicleManager.activeVehicle ? QGroundControl.multiVehicleManager.activeVehicle : QGroundControl.multiVehicleManager.offlineEditingVehicle
    property var    _vehicleCameraList: _vehicle.cameraList
Don Gagne's avatar
Don Gagne committed
32 33 34 35 36

    readonly property int _gridTypeManual:          0
    readonly property int _gridTypeCustomCamera:    1
    readonly property int _gridTypeCamera:          2

37 38 39
    Component.onCompleted: {
        for (var i=0; i<_vehicle.cameraList.length; i++) {
            _cameraList.push(_vehicle.cameraList[i].name)
Don Gagne's avatar
Don Gagne committed
40
        }
41
        gridTypeCombo.model = _cameraList
42
        if (missionItem.manualGrid.value) {
43 44
            gridTypeCombo.currentIndex = _gridTypeManual
        } else {
45 46 47 48 49 50
            var index = -1
            for (index=0; index<_cameraList.length; index++) {
                if (_cameraList[index] == missionItem.camera.value) {
                    break;
                }
            }
51
            missionItem.cameraOrientationFixed = false
52 53 54 55
            if (index == -1) {
                gridTypeCombo.currentIndex = _gridTypeManual
            } else {
                gridTypeCombo.currentIndex = index
56 57 58 59
                if (index != 1) {
                    // Specific camera is selected
                    missionItem.cameraOrientationFixed = _vehicleCameraList[index - _gridTypeCamera].fixedOrientation
                }
60
            }
Don Gagne's avatar
Don Gagne committed
61 62
        }
    }
Don Gagne's avatar
Don Gagne committed
63 64

    function recalcFromCameraValues() {
65 66 67 68 69
        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
Don Gagne's avatar
Don Gagne committed
70

71 72 73 74
        var altitude        = missionItem.gridAltitude.rawValue
        var groundResolution= missionItem.groundResolution.rawValue
        var frontalOverlap  = missionItem.frontalOverlap.rawValue
        var sideOverlap     = missionItem.sideOverlap.rawValue
Don Gagne's avatar
Don Gagne committed
75 76

        if (focalLength <= 0 || sensorWidth <= 0 || sensorHeight <= 0 || imageWidth <= 0 || imageHeight <= 0 || groundResolution <= 0) {
Don Gagne's avatar
Don Gagne committed
77 78 79
            return
        }

Don Gagne's avatar
Don Gagne committed
80 81
        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
82 83
        var gridSpacing
        var cameraTriggerDistance
84

85
        if (missionItem.fixedValueIsAltitude.value) {
86
            groundResolution = (altitude * sensorWidth * 100) / (imageWidth * focalLength)
Don Gagne's avatar
Don Gagne committed
87 88 89
        } else {
            altitude = (imageWidth * groundResolution * focalLength) / (sensorWidth * 100)
        }
90

91
        if (missionItem.cameraOrientationLandscape.value) {
92
            imageSizeSideGround  = (imageWidth  * groundResolution) / 100
Don Gagne's avatar
Don Gagne committed
93
            imageSizeFrontGround = (imageHeight * groundResolution) / 100
Don Gagne's avatar
Don Gagne committed
94
        } else {
95 96
            imageSizeSideGround  = (imageHeight * groundResolution) / 100
            imageSizeFrontGround = (imageWidth  * groundResolution) / 100
Don Gagne's avatar
Don Gagne committed
97 98
        }

99 100 101
        gridSpacing = imageSizeSideGround * ( (100-sideOverlap) / 100 )
        cameraTriggerDistance = imageSizeFrontGround * ( (100-frontalOverlap) / 100 )

102
        if (missionItem.fixedValueIsAltitude.value) {
Don Gagne's avatar
Don Gagne committed
103 104 105 106
            missionItem.groundResolution.rawValue = groundResolution
        } else {
            missionItem.gridAltitude.rawValue = altitude
        }
Don Gagne's avatar
Don Gagne committed
107 108 109 110
        missionItem.gridSpacing.rawValue = gridSpacing
        missionItem.cameraTriggerDistance.rawValue = cameraTriggerDistance
    }

111
    function polygonCaptureStarted() {
Don Gagne's avatar
Don Gagne committed
112
        missionItem.clearPolygon()
113
    }
Don Gagne's avatar
Don Gagne committed
114

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

121 122
    function polygonAdjustVertex(vertexIndex, vertexCoordinate) {
        missionItem.adjustPolygonCoordinate(vertexIndex, vertexCoordinate)
Don Gagne's avatar
Don Gagne committed
123 124
    }

125 126 127
    function polygonAdjustStarted() { }
    function polygonAdjustFinished() { }

Don Gagne's avatar
Don Gagne committed
128 129
    property bool _noCameraValueRecalc: false   ///< Prevents uneeded recalcs

130 131 132 133 134 135 136 137 138 139
    Connections {
        target: missionItem.camera

        onValueChanged: {
            if (gridTypeCombo.currentIndex >= _gridTypeCustomCamera && !_noCameraValueRecalc) {
                recalcFromCameraValues()
            }
        }
    }

Don Gagne's avatar
Don Gagne committed
140 141 142 143
    Connections {
        target: missionItem.gridAltitude

        onValueChanged: {
144
            if (gridTypeCombo.currentIndex >= _gridTypeCustomCamera && missionItem.fixedValueIsAltitude.value && !_noCameraValueRecalc) {
145 146 147 148 149 150 151 152 153 154
                recalcFromCameraValues()
            }
        }
    }

    Connections {
        target: missionItem

        onCameraValueChanged: {
            if (gridTypeCombo.currentIndex >= _gridTypeCustomCamera && !_noCameraValueRecalc) {
Don Gagne's avatar
Don Gagne committed
155 156 157 158 159
                recalcFromCameraValues()
            }
        }
    }

160 161
    QGCPalette { id: qgcPal; colorGroupEnabled: true }

Don Gagne's avatar
Don Gagne committed
162
    ExclusiveGroup {
Don Gagne's avatar
Don Gagne committed
163 164 165 166 167 168
        id: cameraOrientationGroup

        onCurrentChanged: {
            if (gridTypeCombo.currentIndex >= _gridTypeCustomCamera) {
                recalcFromCameraValues()
            }
169
        }
Don Gagne's avatar
Don Gagne committed
170 171
    }

Don Gagne's avatar
Don Gagne committed
172 173
    ExclusiveGroup { id: fixedValueGroup }

174 175 176 177 178
    Column {
        id:                 editorColumn
        anchors.margins:    _margin
        anchors.top:        parent.top
        anchors.left:       parent.left
179
        anchors.right:      parent.right
180 181
        spacing:            _margin

182
        SectionHeader {
183
            id:         cameraHeader
184 185 186
            text:       qsTr("Camera")
            showSpacer: false
        }
187

188
        Column {
Don Gagne's avatar
Don Gagne committed
189 190
            anchors.left:   parent.left
            anchors.right:  parent.right
191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231
            spacing:        _margin
            visible:        cameraHeader.checked

            QGCComboBox {
                id:             gridTypeCombo
                anchors.left:   parent.left
                anchors.right:  parent.right
                model:          _cameraList
                currentIndex:   -1

                onActivated: {
                    if (index == _gridTypeManual) {
                        missionItem.manualGrid.value = true
                    } else if (index == _gridTypeCustomCamera) {
                        missionItem.manualGrid.value = false
                        missionItem.camera.value = gridTypeCombo.textAt(index)
                        missionItem.cameraOrientationFixed = false
                    } else {
                        missionItem.manualGrid.value = false
                        missionItem.camera.value = gridTypeCombo.textAt(index)
                        _noCameraValueRecalc = true
                        var listIndex = index - _gridTypeCamera
                        missionItem.cameraSensorWidth.rawValue          = _vehicleCameraList[listIndex].sensorWidth
                        missionItem.cameraSensorHeight.rawValue         = _vehicleCameraList[listIndex].sensorHeight
                        missionItem.cameraResolutionWidth.rawValue      = _vehicleCameraList[listIndex].imageWidth
                        missionItem.cameraResolutionHeight.rawValue     = _vehicleCameraList[listIndex].imageHeight
                        missionItem.cameraFocalLength.rawValue          = _vehicleCameraList[listIndex].focalLength
                        missionItem.cameraOrientationLandscape.rawValue = _vehicleCameraList[listIndex].landscape ? 1 : 0
                        missionItem.cameraOrientationFixed              = _vehicleCameraList[listIndex].fixedOrientation
                        _noCameraValueRecalc = false
                        recalcFromCameraValues()
                    }
                }
            }

            RowLayout {
                anchors.left:   parent.left
                anchors.right:  parent.right
                spacing:        _margin
                visible:        missionItem.manualGrid.value == true

232 233
                QGCCheckBox {
                    id:                 cameraTriggerDistanceCheckBox
234 235
                    anchors.baseline:   cameraTriggerDistanceField.baseline
                    text:               qsTr("Trigger Distance")
236 237 238 239 240 241 242 243
                    checked:            missionItem.cameraTriggerDistance.rawValue > 0
                    onClicked: {
                        if (checked) {
                            missionItem.cameraTriggerDistance.value = missionItem.cameraTriggerDistance.defaultValue
                        } else {
                            missionItem.cameraTriggerDistance.value = 0
                        }
                    }
244 245 246 247 248 249
                }

                FactTextField {
                    id:                 cameraTriggerDistanceField
                    Layout.fillWidth:   true
                    fact:               missionItem.cameraTriggerDistance
250
                    enabled:            cameraTriggerDistanceCheckBox.checked
251 252
                }
            }
253 254

            FactCheckBox {
DonLakeFlyer's avatar
DonLakeFlyer committed
255 256 257
                text:       qsTr("Hover and capture image")
                fact:       missionItem.hoverAndCapture
                visible:    missionItem.hoverAndCaptureAllowed
258
            }
259 260
        }

Don Gagne's avatar
Don Gagne committed
261 262
        // Camera based grid ui
        Column {
Don Gagne's avatar
Don Gagne committed
263
            anchors.left:   parent.left
Don Gagne's avatar
Don Gagne committed
264 265 266
            anchors.right:  parent.right
            spacing:        _margin
            visible:        gridTypeCombo.currentIndex != _gridTypeManual
Don Gagne's avatar
Don Gagne committed
267

Don Gagne's avatar
Don Gagne committed
268
            Row {
269 270 271
                spacing:                    _margin
                anchors.horizontalCenter:   parent.horizontalCenter
                visible:                    !missionItem.cameraOrientationFixed
272

Don Gagne's avatar
Don Gagne committed
273 274 275
                QGCRadioButton {
                    width:          _editFieldWidth
                    text:           "Landscape"
276
                    checked:        !!missionItem.cameraOrientationLandscape.value
Don Gagne's avatar
Don Gagne committed
277
                    exclusiveGroup: cameraOrientationGroup
278
                    onClicked:      missionItem.cameraOrientationLandscape.value = 1
Don Gagne's avatar
Don Gagne committed
279
                }
280

Don Gagne's avatar
Don Gagne committed
281 282 283
                QGCRadioButton {
                    id:             cameraOrientationPortrait
                    text:           "Portrait"
284
                    checked:        !missionItem.cameraOrientationLandscape.value
Don Gagne's avatar
Don Gagne committed
285
                    exclusiveGroup: cameraOrientationGroup
286
                    onClicked:      missionItem.cameraOrientationLandscape.value = 0
Don Gagne's avatar
Don Gagne committed
287
                }
288 289
            }

Don Gagne's avatar
Don Gagne committed
290
            Column {
291
                id:             custCameraCol
Don Gagne's avatar
Don Gagne committed
292 293 294
                anchors.left:   parent.left
                anchors.right:  parent.right
                spacing:        _margin
295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310
                visible:        gridTypeCombo.currentIndex === _gridTypeCustomCamera

                RowLayout {
                    anchors.left:   parent.left
                    anchors.right:  parent.right
                    spacing:        _margin
                    Item { Layout.fillWidth: true }
                    QGCLabel {
                        Layout.preferredWidth:  _root._fieldWidth
                        text:                   qsTr("Width")
                    }
                    QGCLabel {
                        Layout.preferredWidth:  _root._fieldWidth
                        text:                   qsTr("Height")
                    }
                }
Don Gagne's avatar
Don Gagne committed
311

312 313 314 315
                RowLayout {
                    anchors.left:   parent.left
                    anchors.right:  parent.right
                    spacing:        _margin
316
                    QGCLabel { text: qsTr("Sensor"); Layout.fillWidth: true }
Don Gagne's avatar
Don Gagne committed
317
                    FactTextField {
318
                        Layout.preferredWidth:  _root._fieldWidth
Don Gagne's avatar
Don Gagne committed
319 320 321
                        fact:                   missionItem.cameraSensorWidth
                    }
                    FactTextField {
322
                        Layout.preferredWidth:  _root._fieldWidth
Don Gagne's avatar
Don Gagne committed
323 324
                        fact:                   missionItem.cameraSensorHeight
                    }
325
                }
Don Gagne's avatar
Don Gagne committed
326

327 328 329 330
                RowLayout {
                    anchors.left:   parent.left
                    anchors.right:  parent.right
                    spacing:        _margin
331
                    QGCLabel { text: qsTr("Image"); Layout.fillWidth: true }
Don Gagne's avatar
Don Gagne committed
332
                    FactTextField {
333
                        Layout.preferredWidth:  _root._fieldWidth
Don Gagne's avatar
Don Gagne committed
334 335 336
                        fact:                   missionItem.cameraResolutionWidth
                    }
                    FactTextField {
337
                        Layout.preferredWidth:  _root._fieldWidth
Don Gagne's avatar
Don Gagne committed
338 339 340 341
                        fact:                   missionItem.cameraResolutionHeight
                    }
                }

342 343 344 345 346
                RowLayout {
                    anchors.left:   parent.left
                    anchors.right:  parent.right
                    spacing:        _margin
                    QGCLabel {
347
                        text:                   qsTr("Focal length")
348 349 350 351 352 353
                        Layout.fillWidth:       true
                    }
                    FactTextField {
                        Layout.preferredWidth:  _root._fieldWidth
                        fact:                   missionItem.cameraFocalLength
                    }
Don Gagne's avatar
Don Gagne committed
354 355
                }

356
            } // Column - custom camera
Don Gagne's avatar
Don Gagne committed
357

358 359 360
            RowLayout {
                anchors.left:   parent.left
                anchors.right:  parent.right
Don Gagne's avatar
Don Gagne committed
361
                spacing:        _margin
362 363 364 365
                Item { Layout.fillWidth: true }
                QGCLabel {
                    Layout.preferredWidth:  _root._fieldWidth
                    text:                   qsTr("Frontal")
Don Gagne's avatar
Don Gagne committed
366 367
                }
                QGCLabel {
368 369
                    Layout.preferredWidth:  _root._fieldWidth
                    text:                   qsTr("Side")
Don Gagne's avatar
Don Gagne committed
370
                }
371
            }
Don Gagne's avatar
Don Gagne committed
372

373 374 375 376
            RowLayout {
                anchors.left:   parent.left
                anchors.right:  parent.right
                spacing:        _margin
377
                QGCLabel { text: qsTr("Overlap"); Layout.fillWidth: true }
Don Gagne's avatar
Don Gagne committed
378
                FactTextField {
379 380
                    Layout.preferredWidth:  _root._fieldWidth
                    fact:                   missionItem.frontalOverlap
Don Gagne's avatar
Don Gagne committed
381 382
                }
                FactTextField {
383 384
                    Layout.preferredWidth:  _root._fieldWidth
                    fact:                   missionItem.sideOverlap
Don Gagne's avatar
Don Gagne committed
385
                }
Don Gagne's avatar
Don Gagne committed
386 387
            }

388 389 390 391
            SectionHeader {
                id:     gridHeader
                text:   qsTr("Grid")
            }
Don Gagne's avatar
Don Gagne committed
392

393
            GridLayout {
Don Gagne's avatar
Don Gagne committed
394 395
                anchors.left:   parent.left
                anchors.right:  parent.right
396 397 398
                columnSpacing:  _margin
                rowSpacing:     _margin
                columns:        2
399
                visible:        gridHeader.checked
400 401

                QGCLabel { text: qsTr("Angle") }
402
                FactTextField {
403 404
                    fact:               missionItem.gridAngle
                    Layout.fillWidth:   true
405 406
                }

407
                QGCLabel { text: qsTr("Turnaround dist") }
408 409
                FactTextField {
                    fact:                   missionItem.turnaroundDist
410
                    Layout.fillWidth:       true
411
                }
Don Gagne's avatar
Don Gagne committed
412

413 414 415 416 417 418 419
                QGCCheckBox {
                    text:       qsTr("Refly at 90 degree offset")
                    checked:    missionItem.refly90Degrees
                    onClicked:  missionItem.refly90Degrees = checked
                    Layout.columnSpan: 2
                }

420
                QGCLabel {
DonLakeFlyer's avatar
DonLakeFlyer committed
421 422
                    wrapMode:               Text.WordWrap
                    text:                   qsTr("Select one:")
423
                    Layout.preferredWidth:  parent.width
DonLakeFlyer's avatar
DonLakeFlyer committed
424
                    Layout.columnSpan:      2
425
                }
Don Gagne's avatar
Don Gagne committed
426 427

                QGCRadioButton {
428
                    id:                     fixedAltitudeRadio
429
                    text:                   qsTr("Altitude")
430
                    checked:                !!missionItem.fixedValueIsAltitude.value
431
                    exclusiveGroup:         fixedValueGroup
432
                    onClicked:              missionItem.fixedValueIsAltitude.value = 1
Don Gagne's avatar
Don Gagne committed
433
                }
434

Don Gagne's avatar
Don Gagne committed
435
                FactTextField {
436 437
                    fact:                   missionItem.gridAltitude
                    enabled:                fixedAltitudeRadio.checked
438
                    Layout.fillWidth:       true
439
                }
Don Gagne's avatar
Don Gagne committed
440 441

                QGCRadioButton {
442
                    id:                     fixedGroundResolutionRadio
443
                    text:                   qsTr("Ground res")
444
                    checked:                !missionItem.fixedValueIsAltitude.value
445
                    exclusiveGroup:         fixedValueGroup
446
                    onClicked:              missionItem.fixedValueIsAltitude.value = 0
447 448
                }

Don Gagne's avatar
Don Gagne committed
449
                FactTextField {
450 451
                    fact:                   missionItem.groundResolution
                    enabled:                fixedGroundResolutionRadio.checked
452
                    Layout.fillWidth:       true
Don Gagne's avatar
Don Gagne committed
453 454 455 456 457
                }
            }
        }

        // Manual grid ui
458 459 460 461 462 463
        SectionHeader {
            id:         manualGridHeader
            text:       qsTr("Grid")
            visible:    gridTypeCombo.currentIndex == _gridTypeManual
        }

464
        Column {
Don Gagne's avatar
Don Gagne committed
465 466 467
            anchors.left:   parent.left
            anchors.right:  parent.right
            spacing:        _margin
468
            visible:        manualGridHeader.visible && manualGridHeader.checked
Don Gagne's avatar
Don Gagne committed
469

Don Gagne's avatar
Don Gagne committed
470 471 472
            FactTextFieldGrid {
                anchors.left:   parent.left
                anchors.right:  parent.right
473
                columnSpacing:  ScreenTools.defaultFontPixelWidth
Don Gagne's avatar
Don Gagne committed
474 475
                rowSpacing:     _margin
                factList:       [ missionItem.gridAngle, missionItem.gridSpacing, missionItem.gridAltitude, missionItem.turnaroundDist ]
476
                factLabels:     [ qsTr("Angle"), qsTr("Spacing"), qsTr("Altitude"), qsTr("Turnaround dist")]
Don Gagne's avatar
Don Gagne committed
477
            }
Don Gagne's avatar
Don Gagne committed
478

479 480 481 482 483 484
            QGCCheckBox {
                text:       qsTr("Refly at 90 degree offset")
                checked:    missionItem.refly90Degrees
                onClicked:  missionItem.refly90Degrees = checked
            }

485
            FactCheckBox {
Don Gagne's avatar
Don Gagne committed
486 487
                anchors.left:   parent.left
                text:           qsTr("Relative altitude")
488
                fact:           missionItem.gridAltitudeRelative
Don Gagne's avatar
Don Gagne committed
489
            }
Don Gagne's avatar
Don Gagne committed
490 491
        }

492 493 494
        SectionHeader {
            id:     statsHeader
            text:   qsTr("Statistics") }
495 496

        Grid {
497 498
            columns:        2
            columnSpacing:  ScreenTools.defaultFontPixelWidth
499
            visible:        statsHeader.checked
500

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

504
            QGCLabel { text: qsTr("Photo count") }
505
            QGCLabel { text: missionItem.cameraShots }
506

507
            QGCLabel { text: qsTr("Photo interval") }
508 509 510 511 512 513 514 515 516
            QGCLabel {
                text: {
                    var timeVal = missionItem.timeBetweenShots
                    if(!isFinite(timeVal) || missionItem.cameraShots === 0) {
                        return qsTr("N/A")
                    }
                    return timeVal.toFixed(1) + " " + qsTr("secs")
                }
            }
517
        }
518 519
    }
}