FWLandingPatternMapVisual.qml 13.5 KB
Newer Older
1 2 3 4 5 6 7 8 9
/****************************************************************************
 *
 *   (c) 2009-2016 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org>
 *
 * QGroundControl is licensed according to the terms in the file
 * COPYING.md in the root of the source code directory.
 *
 ****************************************************************************/

10 11 12 13
import QtQuick          2.3
import QtQuick.Controls 1.2
import QtLocation       5.3
import QtPositioning    5.3
14

15
import QGroundControl               1.0
16 17 18
import QGroundControl.ScreenTools   1.0
import QGroundControl.Palette       1.0
import QGroundControl.Controls      1.0
19
import QGroundControl.FlightMap     1.0
20 21 22

/// Fixed Wing Landing Pattern map visuals
Item {
23 24
    id: _root

DonLakeFlyer's avatar
DonLakeFlyer committed
25
    property var map        ///< Map control to place item in
26

27 28
    signal clicked(int sequenceNumber)

Don Gagne's avatar
Don Gagne committed
29 30 31 32 33 34 35 36 37 38 39
    readonly property real _landingWidthMeters:     15
    readonly property real _landingLengthMeters:    100

    property var    _missionItem:           object
    property var    _itemVisuals:           [ ]
    property var    _mouseArea
    property var    _dragAreas:             [ ]
    property var    _flightPath
    property real   _landingAreaBearing:    _missionItem.landingCoordinate.azimuthTo(_missionItem.loiterTangentCoordinate)
    property var    _loiterPointObject
    property var    _landingPointObject
40

41
    function hideItemVisuals() {
Don Gagne's avatar
Don Gagne committed
42 43
        for (var i=0; i<_itemVisuals.length; i++) {
            _itemVisuals[i].destroy()
44
        }
Don Gagne's avatar
Don Gagne committed
45
        _itemVisuals = [ ]
46 47 48
    }

    function showItemVisuals() {
Don Gagne's avatar
Don Gagne committed
49 50
        if (_itemVisuals.length === 0) {            
            var itemVisual = loiterPointComponent.createObject(map)
Don Gagne's avatar
Don Gagne committed
51
            map.addMapItem(itemVisual)
Don Gagne's avatar
Don Gagne committed
52 53 54 55
            _itemVisuals.push(itemVisual)
            _loiterPointObject = itemVisual

            itemVisual = landingPointComponent.createObject(map)
Don Gagne's avatar
Don Gagne committed
56
            map.addMapItem(itemVisual)
Don Gagne's avatar
Don Gagne committed
57 58 59 60 61 62 63 64 65
            _itemVisuals.push(itemVisual)
            _landingPointObject = itemVisual

            var rgComponents = [ flightPathComponent, loiterRadiusComponent, landingAreaComponent, landingAreaLabelComponent, glideSlopeComponent, glideSlopeLabelComponent ]
            for (var i=0; i<rgComponents.length; i++) {
                var obj = rgComponents[i].createObject(map)
                _itemVisuals.push(obj)
                map.addMapItem(obj)
            }
66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83
        }
    }

    function hideMouseArea() {
        if (_mouseArea) {
            _mouseArea.destroy()
            _mouseArea = undefined
        }
    }

    function showMouseArea() {
        if (!_mouseArea) {
            _mouseArea = mouseAreaComponent.createObject(map)
            map.addMapItem(_mouseArea)
        }
    }

    function hideDragAreas() {
Don Gagne's avatar
Don Gagne committed
84 85
        for (var i=0; i<_dragAreas.length; i++) {
            _dragAreas[i].destroy()
86
        }
Don Gagne's avatar
Don Gagne committed
87
        _dragAreas = [ ]
88 89 90
    }

    function showDragAreas() {
Don Gagne's avatar
Don Gagne committed
91
        if (_dragAreas.length === 0) {
92 93
            _dragAreas.push(loiterDragAreaComponent.createObject(map))
            _dragAreas.push(landDragAreaComponent.createObject(map))
94 95 96
        }
    }

97 98
    function _setFlightPath() {
        _flightPath = [ _missionItem.loiterTangentCoordinate, _missionItem.landingCoordinate ]
99 100
    }

101
    Component.onCompleted: {
102 103
        if (_missionItem.landingCoordSet) {
            showItemVisuals()
104
            if (!_missionItem.flyView && _missionItem.isCurrentItem) {
105 106
                showDragAreas()
            }
107
            _setFlightPath()
108
        } else if (!_missionItem.flyView && _missionItem.isCurrentItem) {
109 110
            showMouseArea()
        }
111 112 113
    }

    Component.onDestruction: {
114 115 116 117 118 119 120 121 122
        hideDragAreas()
        hideMouseArea()
        hideItemVisuals()
    }

    Connections {
        target: _missionItem

        onIsCurrentItemChanged: {
123 124 125
            if (_missionItem.flyView) {
                return
            }
126 127 128 129 130 131 132 133 134 135 136 137 138
            if (_missionItem.isCurrentItem) {
                if (_missionItem.landingCoordSet) {
                    showDragAreas()
                } else {
                    showMouseArea()
                }
            } else {
                hideMouseArea()
                hideDragAreas()
            }
        }

        onLandingCoordSetChanged: {
139 140 141
            if (_missionItem.flyView) {
                return
            }
142 143 144 145
            if (_missionItem.landingCoordSet) {
                hideMouseArea()
                showItemVisuals()
                showDragAreas()
146
                _setFlightPath()
147 148 149 150 151
            } else if (_missionItem.isCurrentItem) {
                hideDragAreas()
                showMouseArea()
            }
        }
152

153 154
        onLandingCoordinateChanged:         _setFlightPath()
        onLoiterTangentCoordinateChanged:   _setFlightPath()
155 156 157 158 159 160 161
    }

    // Mouse area to capture landing point coordindate
    Component {
        id:  mouseAreaComponent

        MouseArea {
162 163
            anchors.fill:   map
            z:              QGroundControl.zOrderMapItems + 1   // Over item indicators
164 165

            onClicked: {
DonLakeFlyer's avatar
DonLakeFlyer committed
166
                var coordinate = map.toCoordinate(Qt.point(mouse.x, mouse.y), false /* clipToViewPort */)
167 168 169 170 171 172 173 174
                coordinate.latitude = coordinate.latitude.toFixed(_decimalPlaces)
                coordinate.longitude = coordinate.longitude.toFixed(_decimalPlaces)
                coordinate.altitude = coordinate.altitude.toFixed(_decimalPlaces)
                _missionItem.landingCoordinate = coordinate
            }
        }
    }

175
    // Control which is used to drag the loiter point
176
    Component {
177
        id: loiterDragAreaComponent
178

179
        MissionItemIndicatorDrag {
180
            mapControl:     _root.map
Don Gagne's avatar
Don Gagne committed
181
            itemIndicator:  _loiterPointObject
182
            itemCoordinate: _missionItem.loiterCoordinate
183

184
            onItemCoordinateChanged: _missionItem.loiterCoordinate = itemCoordinate
185 186 187 188 189 190 191 192
        }
    }

    // Control which is used to drag the loiter point
    Component {
        id: landDragAreaComponent

        MissionItemIndicatorDrag {
193
            mapControl:     _root.map
Don Gagne's avatar
Don Gagne committed
194
            itemIndicator:  _landingPointObject
195
            itemCoordinate: _missionItem.landingCoordinate
196

197
            onItemCoordinateChanged: _missionItem.landingCoordinate = itemCoordinate
198
        }
199 200 201 202 203 204 205
    }

    // Flight path
    Component {
        id: flightPathComponent

        MapPolyline {
206
            z:          QGroundControl.zOrderMapItems - 1   // Under item indicators
Don Gagne's avatar
Don Gagne committed
207
            line.color: "#be781c"
208
            line.width: 2
209
            path:       _flightPath
210 211 212 213 214
        }
    }

    // Loiter point
    Component {
215
        id: loiterPointComponent
216 217

        MapQuickItem {
218 219
            anchorPoint.x:  sourceItem.anchorPointX
            anchorPoint.y:  sourceItem.anchorPointY
220 221
            z:              QGroundControl.zOrderMapItems
            coordinate:     _missionItem.loiterCoordinate
222 223 224

            sourceItem:
                MissionItemIndexLabel {
225
                index:      _missionItem.sequenceNumber
226 227 228
                label:      "Loiter"
                checked:    _missionItem.isCurrentItem

229
                onClicked: _root.clicked(_missionItem.sequenceNumber)
230 231 232 233
            }
        }
    }

Don Gagne's avatar
Don Gagne committed
234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253
    // Landing point
    Component {
        id: landingPointComponent

        MapQuickItem {
            anchorPoint.x:  sourceItem.anchorPointX
            anchorPoint.y:  sourceItem.anchorPointY
            z:              QGroundControl.zOrderMapItems
            coordinate:     _missionItem.landingCoordinate

            sourceItem:
                MissionItemIndexLabel {
                index:      _missionItem.sequenceNumber
                checked:    _missionItem.isCurrentItem

                onClicked: _root.clicked(_missionItem.sequenceNumber)
            }
        }
    }

Don Gagne's avatar
Don Gagne committed
254 255 256 257 258 259
    Component {
        id: loiterRadiusComponent

        MapCircle {
            z:              QGroundControl.zOrderMapItems
            center:         _missionItem.loiterCoordinate
260
            radius:         _missionItem.loiterRadius.rawValue
Don Gagne's avatar
Don Gagne committed
261 262 263 264 265 266
            border.width:   2
            border.color:   "green"
            color:          "transparent"
        }
    }

267
    Component {
Don Gagne's avatar
Don Gagne committed
268
        id: landingAreaLabelComponent
269 270

        MapQuickItem {
Don Gagne's avatar
Don Gagne committed
271 272
            anchorPoint.x:  sourceItem.contentWidth / 2
            anchorPoint.y:  sourceItem.contentHeight / 2
273 274
            z:              QGroundControl.zOrderMapItems
            coordinate:     _missionItem.landingCoordinate
Don Gagne's avatar
Don Gagne committed
275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294
            visible:        _missionItem.isCurrentItem

            sourceItem: QGCLabel {
                id:     landingAreaLabel
                text:   qsTr("Landing Area")
                color:  "white"

                property real _rawBearing:      _landingAreaBearing
                property real _adjustedBearing

                on_RawBearingChanged: {
                    _adjustedBearing = _rawBearing
                    if (_adjustedBearing > 180) {
                        _adjustedBearing -= 180
                    }
                    _adjustedBearing -= 90
                    if (_adjustedBearing < 0) {
                        _adjustedBearing += 360
                    }
                }
295

Don Gagne's avatar
Don Gagne committed
296 297 298 299 300 301 302 303
                transform: Rotation {
                    origin.x:   landingAreaLabel.width / 2
                    origin.y:   landingAreaLabel.height / 2
                    angle:      landingAreaLabel._adjustedBearing
                }
            }
        }
    }
304

Don Gagne's avatar
Don Gagne committed
305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349
    Component {
        id: glideSlopeLabelComponent

        MapQuickItem {
            anchorPoint.x:  0
            anchorPoint.y:  sourceItem.contentHeight / 2
            z:              QGroundControl.zOrderMapItems
            visible:        _missionItem.isCurrentItem

            sourceItem: QGCLabel {
                id:     glideSlopeLabel
                text:   qsTr("Glide Slope")
                color:  "white"

                property real _rawBearing:      _landingAreaBearing
                property real _adjustedBearing

                on_RawBearingChanged: {
                    _adjustedBearing = _rawBearing
                    if (_adjustedBearing > 180) {
                        _adjustedBearing -= 180
                    }
                    _adjustedBearing -= 90
                    if (_adjustedBearing < 0) {
                        _adjustedBearing += 360
                    }
                }

                transform: Rotation {
                    origin.x:   0
                    origin.y:   glideSlopeLabel.contentHeight / 2
                    angle:      glideSlopeLabel._adjustedBearing
                }
            }

            function recalc() {
                coordinate = _missionItem.landingCoordinate.atDistanceAndAzimuth(_landingLengthMeters / 2 + 2, _landingAreaBearing)
            }

            Component.onCompleted: recalc()

            Connections {
                target:                             _missionItem
                onLandingCoordinateChanged:         recalc()
                onLoiterTangentCoordinateChanged:   recalc()
350
            }
351 352
        }
    }
DonLakeFlyer's avatar
DonLakeFlyer committed
353 354 355 356

    Component {
        id: landingAreaComponent

DonLakeFlyer's avatar
DonLakeFlyer committed
357
        MapPolygon {
DonLakeFlyer's avatar
DonLakeFlyer committed
358 359 360 361 362 363
            z:              QGroundControl.zOrderMapItems
            border.width:   1
            border.color:   "black"
            color:          "green"
            opacity:        0.5

Don Gagne's avatar
Don Gagne committed
364
            readonly property real angleRadians:    Math.atan((_landingWidthMeters / 2) / (_landingLengthMeters / 2))
DonLakeFlyer's avatar
DonLakeFlyer committed
365
            readonly property real angleDegrees:    (angleRadians * (180 / Math.PI))
Don Gagne's avatar
Don Gagne committed
366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394
            readonly property real hypotenuse:      (_landingWidthMeters / 2) / Math.sin(angleRadians)

            function recalc() {
                path = [ ]
                addCoordinate(_missionItem.landingCoordinate.atDistanceAndAzimuth(hypotenuse, _landingAreaBearing - angleDegrees))
                addCoordinate(_missionItem.landingCoordinate.atDistanceAndAzimuth(hypotenuse, _landingAreaBearing + angleDegrees))
                addCoordinate(_missionItem.landingCoordinate.atDistanceAndAzimuth(hypotenuse, _landingAreaBearing + (180 - angleDegrees)))
                addCoordinate(_missionItem.landingCoordinate.atDistanceAndAzimuth(hypotenuse, _landingAreaBearing - (180 - angleDegrees)))
            }

            Component.onCompleted: recalc()

            Connections {
                target:                             _missionItem
                onLandingCoordinateChanged:         recalc()
                onLoiterTangentCoordinateChanged:   recalc()
            }
        }
    }

    Component {
        id: glideSlopeComponent

        MapPolygon {
            z:              QGroundControl.zOrderMapItems
            border.width:   1
            border.color:   "black"
            color:          "orange"
            opacity:        0.5
DonLakeFlyer's avatar
DonLakeFlyer committed
395

Don Gagne's avatar
Don Gagne committed
396 397 398
            readonly property real angleRadians:    Math.atan((_landingWidthMeters / 2) / (_landingLengthMeters / 2))
            readonly property real angleDegrees:    (angleRadians * (180 / Math.PI))
            readonly property real hypotenuse:      (_landingWidthMeters / 2) / Math.sin(angleRadians)
DonLakeFlyer's avatar
DonLakeFlyer committed
399

Don Gagne's avatar
Don Gagne committed
400
            function recalc() {
DonLakeFlyer's avatar
DonLakeFlyer committed
401
                path = [ ]
Don Gagne's avatar
Don Gagne committed
402 403 404
                addCoordinate(_missionItem.landingCoordinate.atDistanceAndAzimuth(hypotenuse, _landingAreaBearing - angleDegrees))
                addCoordinate(_missionItem.landingCoordinate.atDistanceAndAzimuth(hypotenuse, _landingAreaBearing + angleDegrees))
                addCoordinate(_missionItem.loiterTangentCoordinate)
DonLakeFlyer's avatar
DonLakeFlyer committed
405 406
            }

Don Gagne's avatar
Don Gagne committed
407
            Component.onCompleted: recalc()
DonLakeFlyer's avatar
DonLakeFlyer committed
408 409

            Connections {
Don Gagne's avatar
Don Gagne committed
410 411 412
                target:                             _missionItem
                onLandingCoordinateChanged:         recalc()
                onLoiterTangentCoordinateChanged:   recalc()
DonLakeFlyer's avatar
DonLakeFlyer committed
413
            }
DonLakeFlyer's avatar
DonLakeFlyer committed
414 415
        }
    }
416
}