SurveyItemEditor.qml 31.5 KB
Newer Older
1 2
import QtQuick          2.3
import QtQuick.Controls 1.2
3
import QtQuick.Controls.Styles 1.4
Don Gagne's avatar
Don Gagne committed
4
import QtQuick.Dialogs  1.2
5
import QtQuick.Extras   1.4
6
import QtQuick.Layouts  1.2
7

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

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

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

28
    property real   _margin:            ScreenTools.defaultFontPixelWidth / 2
29 30 31 32
    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
33
    property var    _vehicleCameraList: _vehicle ? _vehicle.staticCameraList : []
Don Gagne's avatar
Don Gagne committed
34 35 36 37 38

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

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

    function recalcFromCameraValues() {
72 73 74 75 76
        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
77

78 79 80 81
        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
82 83

        if (focalLength <= 0 || sensorWidth <= 0 || sensorHeight <= 0 || imageWidth <= 0 || imageHeight <= 0 || groundResolution <= 0) {
Don Gagne's avatar
Don Gagne committed
84 85 86
            return
        }

Don Gagne's avatar
Don Gagne committed
87 88
        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
89 90
        var gridSpacing
        var cameraTriggerDistance
91

92
        if (missionItem.fixedValueIsAltitude.value) {
93
            groundResolution = (altitude * sensorWidth * 100) / (imageWidth * focalLength)
Don Gagne's avatar
Don Gagne committed
94 95 96
        } else {
            altitude = (imageWidth * groundResolution * focalLength) / (sensorWidth * 100)
        }
97

98
        if (missionItem.cameraOrientationLandscape.value) {
99
            imageSizeSideGround  = (imageWidth  * groundResolution) / 100
Don Gagne's avatar
Don Gagne committed
100
            imageSizeFrontGround = (imageHeight * groundResolution) / 100
Don Gagne's avatar
Don Gagne committed
101
        } else {
102 103
            imageSizeSideGround  = (imageHeight * groundResolution) / 100
            imageSizeFrontGround = (imageWidth  * groundResolution) / 100
Don Gagne's avatar
Don Gagne committed
104 105
        }

106 107 108
        gridSpacing = imageSizeSideGround * ( (100-sideOverlap) / 100 )
        cameraTriggerDistance = imageSizeFrontGround * ( (100-frontalOverlap) / 100 )

109
        if (missionItem.fixedValueIsAltitude.value) {
Don Gagne's avatar
Don Gagne committed
110 111 112 113
            missionItem.groundResolution.rawValue = groundResolution
        } else {
            missionItem.gridAltitude.rawValue = altitude
        }
Don Gagne's avatar
Don Gagne committed
114 115 116 117
        missionItem.gridSpacing.rawValue = gridSpacing
        missionItem.cameraTriggerDistance.rawValue = cameraTriggerDistance
    }

118
    function polygonCaptureStarted() {
Don Gagne's avatar
Don Gagne committed
119
        missionItem.clearPolygon()
120
    }
Don Gagne's avatar
Don Gagne committed
121

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

128 129
    function polygonAdjustVertex(vertexIndex, vertexCoordinate) {
        missionItem.adjustPolygonCoordinate(vertexIndex, vertexCoordinate)
Don Gagne's avatar
Don Gagne committed
130 131
    }

132 133 134
    function polygonAdjustStarted() { }
    function polygonAdjustFinished() { }

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

137 138 139 140 141 142 143 144 145 146
    Connections {
        target: missionItem.camera

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

Don Gagne's avatar
Don Gagne committed
147 148 149 150
    Connections {
        target: missionItem.gridAltitude

        onValueChanged: {
151
            if (gridTypeCombo.currentIndex >= _gridTypeCustomCamera && missionItem.fixedValueIsAltitude.value && !_noCameraValueRecalc) {
152 153 154 155 156 157 158 159 160 161
                recalcFromCameraValues()
            }
        }
    }

    Connections {
        target: missionItem

        onCameraValueChanged: {
            if (gridTypeCombo.currentIndex >= _gridTypeCustomCamera && !_noCameraValueRecalc) {
Don Gagne's avatar
Don Gagne committed
162 163 164 165 166
                recalcFromCameraValues()
            }
        }
    }

167 168
    QGCPalette { id: qgcPal; colorGroupEnabled: true }

Don Gagne's avatar
Don Gagne committed
169
    ExclusiveGroup {
Don Gagne's avatar
Don Gagne committed
170 171 172 173 174 175
        id: cameraOrientationGroup

        onCurrentChanged: {
            if (gridTypeCombo.currentIndex >= _gridTypeCustomCamera) {
                recalcFromCameraValues()
            }
176
        }
Don Gagne's avatar
Don Gagne committed
177 178
    }

Don Gagne's avatar
Don Gagne committed
179 180
    ExclusiveGroup { id: fixedValueGroup }

181 182 183 184 185
    Column {
        id:                 editorColumn
        anchors.margins:    _margin
        anchors.top:        parent.top
        anchors.left:       parent.left
186
        anchors.right:      parent.right
187 188
        spacing:            _margin

189 190 191 192 193 194 195 196 197
        QGCLabel {
            anchors.left:   parent.left
            anchors.right:  parent.right
            text:           qsTr("WARNING: Photo interval is below minimum interval (%1 secs) supported by camera.").arg(missionItem.cameraMinTriggerInterval.toFixed(1))
            wrapMode:       Text.WordWrap
            color:          qgcPal.warningText
            visible:        missionItem.manualGrid.value !== true && missionItem.cameraShots > 0 && missionItem.cameraMinTriggerInterval !== 0 && missionItem.cameraMinTriggerInterval > missionItem.timeBetweenShots
        }

198
        SectionHeader {
199
            id:         cameraHeader
200 201 202
            text:       qsTr("Camera")
            showSpacer: false
        }
203

204
        Column {
Don Gagne's avatar
Don Gagne committed
205 206
            anchors.left:   parent.left
            anchors.right:  parent.right
207 208 209 210 211 212 213 214 215 216 217 218 219
            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
220
                        missionItem.fixedValueIsAltitude.value = true
221 222 223 224
                    } else if (index == _gridTypeCustomCamera) {
                        missionItem.manualGrid.value = false
                        missionItem.camera.value = gridTypeCombo.textAt(index)
                        missionItem.cameraOrientationFixed = false
225
                        missionItem.cameraMinTriggerInterval = 0
226 227 228 229 230 231 232 233 234 235 236 237
                    } 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
238
                        missionItem.cameraMinTriggerInterval            = _vehicleCameraList[listIndex].minTriggerInterval
239 240 241 242 243 244 245 246 247 248 249 250
                        _noCameraValueRecalc = false
                        recalcFromCameraValues()
                    }
                }
            }

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

251 252
                QGCCheckBox {
                    id:                 cameraTriggerDistanceCheckBox
253 254
                    anchors.baseline:   cameraTriggerDistanceField.baseline
                    text:               qsTr("Trigger Distance")
255 256 257 258 259 260 261 262
                    checked:            missionItem.cameraTriggerDistance.rawValue > 0
                    onClicked: {
                        if (checked) {
                            missionItem.cameraTriggerDistance.value = missionItem.cameraTriggerDistance.defaultValue
                        } else {
                            missionItem.cameraTriggerDistance.value = 0
                        }
                    }
263 264 265 266 267 268
                }

                FactTextField {
                    id:                 cameraTriggerDistanceField
                    Layout.fillWidth:   true
                    fact:               missionItem.cameraTriggerDistance
269
                    enabled:            cameraTriggerDistanceCheckBox.checked
270 271 272 273
                }
            }
        }

Don Gagne's avatar
Don Gagne committed
274 275
        // Camera based grid ui
        Column {
Don Gagne's avatar
Don Gagne committed
276
            anchors.left:   parent.left
Don Gagne's avatar
Don Gagne committed
277 278 279
            anchors.right:  parent.right
            spacing:        _margin
            visible:        gridTypeCombo.currentIndex != _gridTypeManual
Don Gagne's avatar
Don Gagne committed
280

Don Gagne's avatar
Don Gagne committed
281
            Row {
282 283 284
                spacing:                    _margin
                anchors.horizontalCenter:   parent.horizontalCenter
                visible:                    !missionItem.cameraOrientationFixed
285

Don Gagne's avatar
Don Gagne committed
286 287 288
                QGCRadioButton {
                    width:          _editFieldWidth
                    text:           "Landscape"
289
                    checked:        !!missionItem.cameraOrientationLandscape.value
Don Gagne's avatar
Don Gagne committed
290
                    exclusiveGroup: cameraOrientationGroup
291
                    onClicked:      missionItem.cameraOrientationLandscape.value = 1
Don Gagne's avatar
Don Gagne committed
292
                }
293

Don Gagne's avatar
Don Gagne committed
294 295 296
                QGCRadioButton {
                    id:             cameraOrientationPortrait
                    text:           "Portrait"
297
                    checked:        !missionItem.cameraOrientationLandscape.value
Don Gagne's avatar
Don Gagne committed
298
                    exclusiveGroup: cameraOrientationGroup
299
                    onClicked:      missionItem.cameraOrientationLandscape.value = 0
Don Gagne's avatar
Don Gagne committed
300
                }
301 302
            }

Don Gagne's avatar
Don Gagne committed
303
            Column {
304
                id:             custCameraCol
Don Gagne's avatar
Don Gagne committed
305 306 307
                anchors.left:   parent.left
                anchors.right:  parent.right
                spacing:        _margin
308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323
                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
324

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

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

355 356 357 358 359
                RowLayout {
                    anchors.left:   parent.left
                    anchors.right:  parent.right
                    spacing:        _margin
                    QGCLabel {
360
                        text:                   qsTr("Focal length")
361 362 363 364 365 366
                        Layout.fillWidth:       true
                    }
                    FactTextField {
                        Layout.preferredWidth:  _root._fieldWidth
                        fact:                   missionItem.cameraFocalLength
                    }
Don Gagne's avatar
Don Gagne committed
367 368
                }

369
            } // Column - custom camera
Don Gagne's avatar
Don Gagne committed
370

371 372 373
            RowLayout {
                anchors.left:   parent.left
                anchors.right:  parent.right
Don Gagne's avatar
Don Gagne committed
374
                spacing:        _margin
375 376 377
                Item { Layout.fillWidth: true }
                QGCLabel {
                    Layout.preferredWidth:  _root._fieldWidth
378
                    text:                   qsTr("Front Lap")
Don Gagne's avatar
Don Gagne committed
379 380
                }
                QGCLabel {
381
                    Layout.preferredWidth:  _root._fieldWidth
382
                    text:                   qsTr("Side Lap")
Don Gagne's avatar
Don Gagne committed
383
                }
384
            }
Don Gagne's avatar
Don Gagne committed
385

386 387 388 389
            RowLayout {
                anchors.left:   parent.left
                anchors.right:  parent.right
                spacing:        _margin
390
                QGCLabel { text: qsTr("Overlap"); Layout.fillWidth: true }
Don Gagne's avatar
Don Gagne committed
391
                FactTextField {
392 393
                    Layout.preferredWidth:  _root._fieldWidth
                    fact:                   missionItem.frontalOverlap
Don Gagne's avatar
Don Gagne committed
394 395
                }
                FactTextField {
396 397
                    Layout.preferredWidth:  _root._fieldWidth
                    fact:                   missionItem.sideOverlap
Don Gagne's avatar
Don Gagne committed
398
                }
Don Gagne's avatar
Don Gagne committed
399 400
            }

401 402 403 404 405 406 407 408 409 410 411 412 413 414
            FactCheckBox {
                text:       qsTr("Hover and capture image")
                fact:       missionItem.hoverAndCapture
                visible:    missionItem.hoverAndCaptureAllowed
                onClicked: {
                    if (checked) {
                        missionItem.cameraTriggerInTurnaround.rawValue = false
                    }
                }
            }

            FactCheckBox {
                text:       qsTr("Take images in turnarounds")
                fact:       missionItem.cameraTriggerInTurnaround
415
                enabled:    missionItem.hoverAndCaptureAllowed ? !missionItem.hoverAndCapture.rawValue : true
416 417
            }

418 419 420 421
            SectionHeader {
                id:     gridHeader
                text:   qsTr("Grid")
            }
Don Gagne's avatar
Don Gagne committed
422

423
            GridLayout {
Don Gagne's avatar
Don Gagne committed
424 425
                anchors.left:   parent.left
                anchors.right:  parent.right
426 427 428
                columnSpacing:  _margin
                rowSpacing:     _margin
                columns:        2
429
                visible:        gridHeader.checked
430

431 432 433 434 435
                GridLayout {
                    anchors.left:   parent.left
                    anchors.right:  parent.right
                    columnSpacing:  _margin
                    rowSpacing:     _margin
DonLakeFlyer's avatar
DonLakeFlyer committed
436
                    columns:        2
437 438 439
                    visible:        gridHeader.checked

                    QGCLabel {
DonLakeFlyer's avatar
DonLakeFlyer committed
440 441 442
                        id:                 angleText
                        text:               qsTr("Angle")
                        Layout.fillWidth:   true
443 444
                    }

DonLakeFlyer's avatar
DonLakeFlyer committed
445 446 447 448 449 450 451 452 453 454 455 456 457
                    Rectangle {
                        id:         windRoseButton
                        width:      ScreenTools.implicitTextFieldHeight
                        height:     width
                        color:      qgcPal.button
                        visible:    _vehicle.fixedWing

                        QGCColoredImage {
                            anchors.fill:   parent
                            source:         "/res/wind-rose.svg"
                            smooth:         true
                            color:          qgcPal.buttonText
                        }
458

DonLakeFlyer's avatar
DonLakeFlyer committed
459 460 461 462 463 464 465 466
                        QGCMouseArea {
                            fillItem: parent

                            onClicked: {
                                windRosePie.angle = Number(gridAngleText.text)
                                var cords = windRoseButton.mapToItem(_root, 0, 0)
                                windRosePie.popup(cords.x + windRoseButton.width / 2, cords.y + windRoseButton.height / 2)
                            }
467 468 469 470
                        }
                    }
                }

471
                FactTextField {
472
                    id:                 gridAngleText
473 474
                    fact:               missionItem.gridAngle
                    Layout.fillWidth:   true
475 476
                }

477
                QGCLabel { text: qsTr("Turnaround dist") }
478 479
                FactTextField {
                    fact:                   missionItem.turnaroundDist
480
                    Layout.fillWidth:       true
481
                }
Don Gagne's avatar
Don Gagne committed
482

483 484 485
                QGCLabel {
                    text: qsTr("Entry")
                }
DonLakeFlyer's avatar
DonLakeFlyer committed
486 487 488 489 490 491
                FactComboBox {
                    fact:                   missionItem.gridEntryLocation
                    indexModel:             false
                    Layout.fillWidth:       true
                }

492
                QGCCheckBox {
DonLakeFlyer's avatar
DonLakeFlyer committed
493 494 495 496
                    text:               qsTr("Refly at 90 degree offset")
                    checked:            missionItem.refly90Degrees
                    onClicked:          missionItem.refly90Degrees = checked
                    Layout.columnSpan:  2
497 498
                }

499
                QGCLabel {
DonLakeFlyer's avatar
DonLakeFlyer committed
500 501
                    wrapMode:               Text.WordWrap
                    text:                   qsTr("Select one:")
502
                    Layout.preferredWidth:  parent.width
DonLakeFlyer's avatar
DonLakeFlyer committed
503
                    Layout.columnSpan:      2
504
                }
Don Gagne's avatar
Don Gagne committed
505 506

                QGCRadioButton {
507
                    id:                     fixedAltitudeRadio
508
                    text:                   qsTr("Altitude")
509
                    checked:                !!missionItem.fixedValueIsAltitude.value
510
                    exclusiveGroup:         fixedValueGroup
511
                    onClicked:              missionItem.fixedValueIsAltitude.value = 1
Don Gagne's avatar
Don Gagne committed
512
                }
513

Don Gagne's avatar
Don Gagne committed
514
                FactTextField {
515 516
                    fact:                   missionItem.gridAltitude
                    enabled:                fixedAltitudeRadio.checked
517
                    Layout.fillWidth:       true
518
                }
Don Gagne's avatar
Don Gagne committed
519 520

                QGCRadioButton {
521
                    id:                     fixedGroundResolutionRadio
522
                    text:                   qsTr("Ground res")
523
                    checked:                !missionItem.fixedValueIsAltitude.value
524
                    exclusiveGroup:         fixedValueGroup
525
                    onClicked:              missionItem.fixedValueIsAltitude.value = 0
526 527
                }

Don Gagne's avatar
Don Gagne committed
528
                FactTextField {
529 530
                    fact:                   missionItem.groundResolution
                    enabled:                fixedGroundResolutionRadio.checked
531
                    Layout.fillWidth:       true
Don Gagne's avatar
Don Gagne committed
532 533 534 535 536
                }
            }
        }

        // Manual grid ui
537 538 539 540 541 542
        SectionHeader {
            id:         manualGridHeader
            text:       qsTr("Grid")
            visible:    gridTypeCombo.currentIndex == _gridTypeManual
        }

DonLakeFlyer's avatar
DonLakeFlyer committed
543
        GridLayout {
Don Gagne's avatar
Don Gagne committed
544 545
            anchors.left:   parent.left
            anchors.right:  parent.right
DonLakeFlyer's avatar
DonLakeFlyer committed
546 547 548
            columnSpacing:  _margin
            rowSpacing:     _margin
            columns:        2
549
            visible:        manualGridHeader.visible && manualGridHeader.checked
Don Gagne's avatar
Don Gagne committed
550

DonLakeFlyer's avatar
DonLakeFlyer committed
551 552
            RowLayout {
                spacing: _margin
553 554

                QGCLabel {
DonLakeFlyer's avatar
DonLakeFlyer committed
555 556
                    id:                 manualAngleText
                    text:               qsTr("Angle")
557 558 559
                    Layout.fillWidth:  true
                }

DonLakeFlyer's avatar
DonLakeFlyer committed
560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575
                Rectangle {
                    id:         manualWindRoseButton
                    width:      ScreenTools.implicitTextFieldHeight
                    height:     width
                    color:      qgcPal.button
                    visible:    _vehicle.fixedWing

                    QGCColoredImage {
                        anchors.fill:   parent
                        source:         "/res/wind-rose.svg"
                        smooth:         true
                        color:          qgcPal.buttonText
                    }

                    QGCMouseArea {
                        fillItem: parent
576

DonLakeFlyer's avatar
DonLakeFlyer committed
577 578 579 580 581
                        onClicked: {
                            windRosePie.angle = Number(gridAngleText.text)
                            var cords = manualWindRoseButton.mapToItem(_root, 0, 0)
                            windRosePie.popup(cords.x + manualWindRoseButton.width / 2, cords.y + manualWindRoseButton.height / 2)
                        }
582 583
                    }
                }
DonLakeFlyer's avatar
DonLakeFlyer committed
584
            }
585

DonLakeFlyer's avatar
DonLakeFlyer committed
586 587 588 589
            FactTextField {
                id:                 manualGridAngleText
                fact:               missionItem.gridAngle
                Layout.fillWidth:   true
590 591
            }

DonLakeFlyer's avatar
DonLakeFlyer committed
592 593 594 595 596 597 598 599 600 601 602 603 604 605 606
            QGCLabel { text: qsTr("Spacing") }
            FactTextField {
                fact:                   missionItem.gridSpacing
                Layout.fillWidth:       true
            }

            QGCLabel { text: qsTr("Altitude") }
            FactTextField {
                fact:                   missionItem.gridAltitude
                Layout.fillWidth:       true
            }
            QGCLabel { text: qsTr("Turnaround dist") }
            FactTextField {
                fact:                   missionItem.turnaroundDist
                Layout.fillWidth:       true
Don Gagne's avatar
Don Gagne committed
607
            }
608 609
            QGCLabel {
                text: qsTr("Entry")
610
                visible: !windRoseButton.visible
611
            }
DonLakeFlyer's avatar
DonLakeFlyer committed
612
            FactComboBox {
613
                id: gridAngleBox
DonLakeFlyer's avatar
DonLakeFlyer committed
614
                fact:                   missionItem.gridEntryLocation
615
                visible:                !windRoseButton.visible
DonLakeFlyer's avatar
DonLakeFlyer committed
616 617 618 619
                indexModel:             false
                Layout.fillWidth:       true
            }

620 621 622 623 624 625 626 627 628 629 630 631 632 633 634
            FactCheckBox {
                text:               qsTr("Hover and capture image")
                fact:               missionItem.hoverAndCapture
                visible:            missionItem.hoverAndCaptureAllowed
                Layout.columnSpan:  2
                onClicked: {
                    if (checked) {
                        missionItem.cameraTriggerInTurnaround.rawValue = false
                    }
                }
            }

            FactCheckBox {
                text:               qsTr("Take images in turnarounds")
                fact:               missionItem.cameraTriggerInTurnaround
635
                enabled:            missionItem.hoverAndCaptureAllowed ? !missionItem.hoverAndCapture.rawValue : true
636 637
                Layout.columnSpan:  2
            }
Don Gagne's avatar
Don Gagne committed
638

639
            QGCCheckBox {
DonLakeFlyer's avatar
DonLakeFlyer committed
640 641 642 643
                text:               qsTr("Refly at 90 degree offset")
                checked:            missionItem.refly90Degrees
                onClicked:          missionItem.refly90Degrees = checked
                Layout.columnSpan:  2
644 645
            }

646
            FactCheckBox {
DonLakeFlyer's avatar
DonLakeFlyer committed
647 648 649 650
                anchors.left:       parent.left
                text:               qsTr("Relative altitude")
                fact:               missionItem.gridAltitudeRelative
                Layout.columnSpan:  2
Don Gagne's avatar
Don Gagne committed
651
            }
Don Gagne's avatar
Don Gagne committed
652 653
        }

654 655 656
        SectionHeader {
            id:     statsHeader
            text:   qsTr("Statistics") }
657 658

        Grid {
659 660
            columns:        2
            columnSpacing:  ScreenTools.defaultFontPixelWidth
661
            visible:        statsHeader.checked
662

663
            QGCLabel { text: qsTr("Survey Area") }
664 665
            QGCLabel { text: QGroundControl.squareMetersToAppSettingsAreaUnits(missionItem.coveredArea).toFixed(2) + " " + QGroundControl.appSettingsAreaUnitsString }

666
            QGCLabel { text: qsTr("Photo Count") }
667
            QGCLabel { text: missionItem.cameraShots }
668

669
            QGCLabel { text: qsTr("Photo Interval") }
670 671 672 673 674 675 676 677 678
            QGCLabel {
                text: {
                    var timeVal = missionItem.timeBetweenShots
                    if(!isFinite(timeVal) || missionItem.cameraShots === 0) {
                        return qsTr("N/A")
                    }
                    return timeVal.toFixed(1) + " " + qsTr("secs")
                }
            }
679 680 681

            QGCLabel { text: qsTr("Trigger Distance") }
            QGCLabel { text: missionItem.cameraTriggerDistance.valueString  + " " + QGroundControl.appSettingsDistanceUnitsString }
682
        }
683
    }
684

685
    QGCColoredImage {
686 687 688 689 690 691
        id:      windRoseArrow
        source:  "/res/wind-rose-arrow.svg"
        visible: windRosePie.visible
        width:   windRosePie.width / 5
        height:  width * 1.454
        smooth:  true
692
        color:   qgcPal.colorGrey
693 694 695 696
        transform: Rotation {
            origin.x: windRoseArrow.width / 2
            origin.y: windRoseArrow.height / 2
            axis { x: 0; y: 0; z: 1 } angle: windRosePie.angle
697
        }
698 699
        x: windRosePie.x + Math.sin(- windRosePie.angle*Math.PI/180 - Math.PI/2)*(windRosePie.width/2 - windRoseArrow.width/2) + windRosePie.width / 2 - windRoseArrow.width / 2
        y: windRosePie.y + Math.cos(- windRosePie.angle*Math.PI/180 - Math.PI/2)*(windRosePie.width/2 - windRoseArrow.width/2) + windRosePie.height / 2 - windRoseArrow.height / 2
700
        z: windRosePie.z + 1
701 702
    }

703
    QGCColoredImage {
704 705 706 707 708 709
        id:      windGuru
        source:  "/res/wind-guru.svg"
        visible: windRosePie.visible
        width:   windRosePie.width / 3
        height:  width * 4.28e-1
        smooth:  true
710
        color:   qgcPal.colorGrey
711 712 713 714 715
        transform: Rotation {
            origin.x: windGuru.width / 2
            origin.y: windGuru.height / 2
            axis { x: 0; y: 0; z: 1 } angle: windRosePie.angle + 180
        }
716 717 718
        x: windRosePie.x + Math.sin(- windRosePie.angle*Math.PI/180 - 3*Math.PI/2)*(windRosePie.width/2) + windRosePie.width / 2 - windGuru.width / 2
        y: windRosePie.y + Math.cos(- windRosePie.angle*Math.PI/180 - 3*Math.PI/2)*(windRosePie.height/2) + windRosePie.height / 2 - windGuru.height / 2
        z: windRosePie.z + 1
719
    }
720

721 722 723 724 725
    Item {
        id:          windRosePie
        height:      2.6*windRoseButton.height
        width:       2.6*windRoseButton.width
        visible:     false
726
        focus:       true
727

728 729
        property string colorCircle: qgcPal.windowShade
        property string colorBackground: qgcPal.colorGrey
730
        property real lineWidth: windRoseButton.width / 3
731
        property real angle: Number(gridAngleText.text)
732 733 734 735 736 737 738 739 740 741 742 743 744 745

        Canvas {
            id: windRoseCanvas
            anchors.fill: parent

            onPaint: {
                var ctx = getContext("2d")
                var x = width / 2
                var y = height / 2
                var angleWidth = 0.03 * Math.PI
                var start = windRosePie.angle*Math.PI/180 - angleWidth
                var end = windRosePie.angle*Math.PI/180 + angleWidth
                ctx.reset()

746
                ctx.beginPath()
747 748 749 750 751
                ctx.arc(x, y, (width / 3) - windRosePie.lineWidth / 2, 0, 2*Math.PI, false)
                ctx.lineWidth = windRosePie.lineWidth
                ctx.strokeStyle = windRosePie.colorBackground
                ctx.stroke()

752
                ctx.beginPath()
753 754 755 756 757
                ctx.arc(x, y, (width / 3) - windRosePie.lineWidth / 2, start, end, false)
                ctx.lineWidth = windRosePie.lineWidth
                ctx.strokeStyle = windRosePie.colorCircle
                ctx.stroke()
            }
758 759
        }

760 761 762 763
        onFocusChanged: {
            visible = focus
        }

764 765
        function popup(x, y) {
            if (x !== undefined)
766
                windRosePie.x = x - windRosePie.width / 2
767
            if (y !== undefined)
768
                windRosePie.y = y - windRosePie.height / 2
769

770
            windRosePie.visible = true
771
            windRosePie.focus = true
772
            missionItemEditorListView.interactive = false
773
        }
774 775 776 777 778 779 780

        MouseArea {
            id: mouseArea
            anchors.fill: parent
            acceptedButtons: Qt.LeftButton | Qt.RightButton

            onClicked: {
781
                windRosePie.visible = false
782
                missionItemEditorListView.interactive = true
783
            }
784 785 786 787 788
            onPositionChanged: {
                var point = Qt.point(mouseX - parent.width / 2, mouseY - parent.height / 2)
                var angle = Math.round(Math.atan2(point.y, point.x) * 180 / Math.PI)
                windRoseCanvas.requestPaint()
                windRosePie.angle = angle
789
                gridAngleText.text = angle
790
                gridAngleText.editingFinished()
791 792 793 794 795 796 797 798 799
                if(angle > -135 && angle <= -45) {
                    gridAngleBox.activated(2) // or 3
                } else if(angle > -45 && angle <= 45) {
                    gridAngleBox.activated(2) // or 0
                } else if(angle > 45 && angle <= 135) {
                    gridAngleBox.activated(1) // or 0
                } else if(angle > 135 || angle <= -135) {
                    gridAngleBox.activated(1) // or 3
                }
800 801 802
            }
        }
    }
803
}