SurveyItemEditor.qml 20 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 232 233 234 235 236 237 238 239 240 241 242
            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

                FactCheckBox {
                    anchors.baseline:   cameraTriggerDistanceField.baseline
                    text:               qsTr("Trigger Distance")
                    fact:               missionItem.cameraTrigger
                }

                FactTextField {
                    id:                 cameraTriggerDistanceField
                    Layout.fillWidth:   true
                    fact:               missionItem.cameraTriggerDistance
                    enabled:            missionItem.cameraTrigger.value
243 244
                }
            }
245 246

            FactCheckBox {
DonLakeFlyer's avatar
DonLakeFlyer committed
247 248 249
                text:       qsTr("Hover and capture image")
                fact:       missionItem.hoverAndCapture
                visible:    missionItem.hoverAndCaptureAllowed
250
            }
251 252
        }

Don Gagne's avatar
Don Gagne committed
253 254
        // Camera based grid ui
        Column {
Don Gagne's avatar
Don Gagne committed
255
            anchors.left:   parent.left
Don Gagne's avatar
Don Gagne committed
256 257 258
            anchors.right:  parent.right
            spacing:        _margin
            visible:        gridTypeCombo.currentIndex != _gridTypeManual
Don Gagne's avatar
Don Gagne committed
259

Don Gagne's avatar
Don Gagne committed
260
            Row {
261 262 263
                spacing:                    _margin
                anchors.horizontalCenter:   parent.horizontalCenter
                visible:                    !missionItem.cameraOrientationFixed
264

Don Gagne's avatar
Don Gagne committed
265 266 267
                QGCRadioButton {
                    width:          _editFieldWidth
                    text:           "Landscape"
268
                    checked:        !!missionItem.cameraOrientationLandscape.value
Don Gagne's avatar
Don Gagne committed
269
                    exclusiveGroup: cameraOrientationGroup
270
                    onClicked:      missionItem.cameraOrientationLandscape.value = 1
Don Gagne's avatar
Don Gagne committed
271
                }
272

Don Gagne's avatar
Don Gagne committed
273 274 275
                QGCRadioButton {
                    id:             cameraOrientationPortrait
                    text:           "Portrait"
276
                    checked:        !missionItem.cameraOrientationLandscape.value
Don Gagne's avatar
Don Gagne committed
277
                    exclusiveGroup: cameraOrientationGroup
278
                    onClicked:      missionItem.cameraOrientationLandscape.value = 0
Don Gagne's avatar
Don Gagne committed
279
                }
280 281
            }

Don Gagne's avatar
Don Gagne committed
282
            Column {
283
                id:             custCameraCol
Don Gagne's avatar
Don Gagne committed
284 285 286
                anchors.left:   parent.left
                anchors.right:  parent.right
                spacing:        _margin
287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302
                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
303

304 305 306 307
                RowLayout {
                    anchors.left:   parent.left
                    anchors.right:  parent.right
                    spacing:        _margin
308
                    QGCLabel { text: qsTr("Sensor"); Layout.fillWidth: true }
Don Gagne's avatar
Don Gagne committed
309
                    FactTextField {
310
                        Layout.preferredWidth:  _root._fieldWidth
Don Gagne's avatar
Don Gagne committed
311 312 313
                        fact:                   missionItem.cameraSensorWidth
                    }
                    FactTextField {
314
                        Layout.preferredWidth:  _root._fieldWidth
Don Gagne's avatar
Don Gagne committed
315 316
                        fact:                   missionItem.cameraSensorHeight
                    }
317
                }
Don Gagne's avatar
Don Gagne committed
318

319 320 321 322
                RowLayout {
                    anchors.left:   parent.left
                    anchors.right:  parent.right
                    spacing:        _margin
323
                    QGCLabel { text: qsTr("Image"); Layout.fillWidth: true }
Don Gagne's avatar
Don Gagne committed
324
                    FactTextField {
325
                        Layout.preferredWidth:  _root._fieldWidth
Don Gagne's avatar
Don Gagne committed
326 327 328
                        fact:                   missionItem.cameraResolutionWidth
                    }
                    FactTextField {
329
                        Layout.preferredWidth:  _root._fieldWidth
Don Gagne's avatar
Don Gagne committed
330 331 332 333
                        fact:                   missionItem.cameraResolutionHeight
                    }
                }

334 335 336 337 338
                RowLayout {
                    anchors.left:   parent.left
                    anchors.right:  parent.right
                    spacing:        _margin
                    QGCLabel {
339
                        text:                   qsTr("Focal length")
340 341 342 343 344 345
                        Layout.fillWidth:       true
                    }
                    FactTextField {
                        Layout.preferredWidth:  _root._fieldWidth
                        fact:                   missionItem.cameraFocalLength
                    }
Don Gagne's avatar
Don Gagne committed
346 347
                }

348
            } // Column - custom camera
Don Gagne's avatar
Don Gagne committed
349

350 351 352
            RowLayout {
                anchors.left:   parent.left
                anchors.right:  parent.right
Don Gagne's avatar
Don Gagne committed
353
                spacing:        _margin
354 355 356 357
                Item { Layout.fillWidth: true }
                QGCLabel {
                    Layout.preferredWidth:  _root._fieldWidth
                    text:                   qsTr("Frontal")
Don Gagne's avatar
Don Gagne committed
358 359
                }
                QGCLabel {
360 361
                    Layout.preferredWidth:  _root._fieldWidth
                    text:                   qsTr("Side")
Don Gagne's avatar
Don Gagne committed
362
                }
363
            }
Don Gagne's avatar
Don Gagne committed
364

365 366 367 368
            RowLayout {
                anchors.left:   parent.left
                anchors.right:  parent.right
                spacing:        _margin
369
                QGCLabel { text: qsTr("Overlap"); Layout.fillWidth: true }
Don Gagne's avatar
Don Gagne committed
370
                FactTextField {
371 372
                    Layout.preferredWidth:  _root._fieldWidth
                    fact:                   missionItem.frontalOverlap
Don Gagne's avatar
Don Gagne committed
373 374
                }
                FactTextField {
375 376
                    Layout.preferredWidth:  _root._fieldWidth
                    fact:                   missionItem.sideOverlap
Don Gagne's avatar
Don Gagne committed
377
                }
Don Gagne's avatar
Don Gagne committed
378 379
            }

380 381 382 383
            SectionHeader {
                id:     gridHeader
                text:   qsTr("Grid")
            }
Don Gagne's avatar
Don Gagne committed
384

385
            GridLayout {
Don Gagne's avatar
Don Gagne committed
386 387
                anchors.left:   parent.left
                anchors.right:  parent.right
388 389 390
                columnSpacing:  _margin
                rowSpacing:     _margin
                columns:        2
391
                visible:        gridHeader.checked
392 393

                QGCLabel { text: qsTr("Angle") }
394
                FactTextField {
395 396
                    fact:               missionItem.gridAngle
                    Layout.fillWidth:   true
397 398
                }

399
                QGCLabel { text: qsTr("Turnaround dist") }
400 401
                FactTextField {
                    fact:                   missionItem.turnaroundDist
402
                    Layout.fillWidth:       true
403
                }
Don Gagne's avatar
Don Gagne committed
404

405 406 407 408 409 410 411
                QGCCheckBox {
                    text:       qsTr("Refly at 90 degree offset")
                    checked:    missionItem.refly90Degrees
                    onClicked:  missionItem.refly90Degrees = checked
                    Layout.columnSpan: 2
                }

412 413 414 415 416 417 418
                QGCLabel {
                    wrapMode:       Text.WordWrap
                    font.pointSize: ScreenTools.smallFontPointSize
                    text:           qsTr("Which value would you like to keep constant as you adjust other settings")
                    Layout.preferredWidth:  parent.width
                    Layout.columnSpan: 2
                }
Don Gagne's avatar
Don Gagne committed
419 420

                QGCRadioButton {
421
                    id:                     fixedAltitudeRadio
422
                    text:                   qsTr("Altitude")
423
                    checked:                !!missionItem.fixedValueIsAltitude.value
424
                    exclusiveGroup:         fixedValueGroup
425
                    onClicked:              missionItem.fixedValueIsAltitude.value = 1
Don Gagne's avatar
Don Gagne committed
426
                }
427

Don Gagne's avatar
Don Gagne committed
428
                FactTextField {
429 430
                    fact:                   missionItem.gridAltitude
                    enabled:                fixedAltitudeRadio.checked
431
                    Layout.fillWidth:       true
432
                }
Don Gagne's avatar
Don Gagne committed
433 434

                QGCRadioButton {
435
                    id:                     fixedGroundResolutionRadio
436
                    text:                   qsTr("Ground res")
437
                    checked:                !missionItem.fixedValueIsAltitude.value
438
                    exclusiveGroup:         fixedValueGroup
439
                    onClicked:              missionItem.fixedValueIsAltitude.value = 0
440 441
                }

Don Gagne's avatar
Don Gagne committed
442
                FactTextField {
443 444
                    fact:                   missionItem.groundResolution
                    enabled:                fixedGroundResolutionRadio.checked
445
                    Layout.fillWidth:       true
Don Gagne's avatar
Don Gagne committed
446 447 448 449 450
                }
            }
        }

        // Manual grid ui
451 452 453 454 455 456
        SectionHeader {
            id:         manualGridHeader
            text:       qsTr("Grid")
            visible:    gridTypeCombo.currentIndex == _gridTypeManual
        }

457
        Column {
Don Gagne's avatar
Don Gagne committed
458 459 460
            anchors.left:   parent.left
            anchors.right:  parent.right
            spacing:        _margin
461
            visible:        manualGridHeader.visible && manualGridHeader.checked
Don Gagne's avatar
Don Gagne committed
462

Don Gagne's avatar
Don Gagne committed
463 464 465
            FactTextFieldGrid {
                anchors.left:   parent.left
                anchors.right:  parent.right
466
                columnSpacing:  ScreenTools.defaultFontPixelWidth
Don Gagne's avatar
Don Gagne committed
467 468
                rowSpacing:     _margin
                factList:       [ missionItem.gridAngle, missionItem.gridSpacing, missionItem.gridAltitude, missionItem.turnaroundDist ]
469
                factLabels:     [ qsTr("Angle"), qsTr("Spacing"), qsTr("Altitude"), qsTr("Turnaround dist")]
Don Gagne's avatar
Don Gagne committed
470
            }
Don Gagne's avatar
Don Gagne committed
471

472 473 474 475 476 477
            QGCCheckBox {
                text:       qsTr("Refly at 90 degree offset")
                checked:    missionItem.refly90Degrees
                onClicked:  missionItem.refly90Degrees = checked
            }

478
            FactCheckBox {
Don Gagne's avatar
Don Gagne committed
479 480
                anchors.left:   parent.left
                text:           qsTr("Relative altitude")
481
                fact:           missionItem.gridAltitudeRelative
Don Gagne's avatar
Don Gagne committed
482
            }
Don Gagne's avatar
Don Gagne committed
483 484
        }

485 486 487
        SectionHeader {
            id:     statsHeader
            text:   qsTr("Statistics") }
488 489

        Grid {
490 491
            columns:        2
            columnSpacing:  ScreenTools.defaultFontPixelWidth
492
            visible:        statsHeader.checked
493

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

497
            QGCLabel { text: qsTr("Photo count") }
498
            QGCLabel { text: missionItem.cameraShots }
499

500
            QGCLabel { text: qsTr("Photo interval") }
501 502 503 504 505 506 507 508 509
            QGCLabel {
                text: {
                    var timeVal = missionItem.timeBetweenShots
                    if(!isFinite(timeVal) || missionItem.cameraShots === 0) {
                        return qsTr("N/A")
                    }
                    return timeVal.toFixed(1) + " " + qsTr("secs")
                }
            }
510
        }
511 512
    }
}