FWLandingPatternMapVisual.qml 9.22 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 26
    property var map        ///< Map control to place item in
    property var qgcView    ///< QGCView to use for popping dialogs
27

28 29
    signal clicked(int sequenceNumber)

30
    property var _missionItem:  object
Don Gagne's avatar
Don Gagne committed
31
    property var _itemVisuals: [ ]
32
    property var _mouseArea
Don Gagne's avatar
Don Gagne committed
33
    property var _dragAreas: [ ]
34
    property var _flightPath
Don Gagne's avatar
Don Gagne committed
35 36 37 38

    readonly property int _flightPathIndex:     0
    readonly property int _loiterPointIndex:    1
    readonly property int _loiterRadiusIndex:   2
DonLakeFlyer's avatar
DonLakeFlyer committed
39 40
    readonly property int _landingAreaIndex:    3
    readonly property int _landPointIndex:      4
41

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

    function showItemVisuals() {
Don Gagne's avatar
Don Gagne committed
50 51 52 53 54 55 56 57 58 59
        if (_itemVisuals.length === 0) {
            var itemVisual = flightPathComponent.createObject(map)
            map.addMapItem(itemVisual)
            _itemVisuals[_flightPathIndex] =itemVisual
            itemVisual = loiterPointComponent.createObject(map)
            map.addMapItem(itemVisual)
            _itemVisuals[_loiterPointIndex] = itemVisual
            itemVisual = loiterRadiusComponent.createObject(map)
            map.addMapItem(itemVisual)
            _itemVisuals[_loiterRadiusIndex] = itemVisual
DonLakeFlyer's avatar
DonLakeFlyer committed
60 61 62
            itemVisual = landingAreaComponent.createObject(map)
            map.addMapItem(itemVisual)
            _itemVisuals[_landingAreaIndex] = itemVisual
DonLakeFlyer's avatar
DonLakeFlyer committed
63 64 65
            itemVisual = landPointComponent.createObject(map)
            map.addMapItem(itemVisual)
            _itemVisuals[_landPointIndex] = itemVisual
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 104 105 106
        if (_missionItem.landingCoordSet) {
            showItemVisuals()
            if (_missionItem.isCurrentItem) {
                showDragAreas()
            }
107
            _setFlightPath()
108 109 110
        } else if (_missionItem.isCurrentItem) {
            showMouseArea()
        }
111 112 113
    }

    Component.onDestruction: {
114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139
        hideDragAreas()
        hideMouseArea()
        hideItemVisuals()
    }

    Connections {
        target: _missionItem

        onIsCurrentItemChanged: {
            if (_missionItem.isCurrentItem) {
                if (_missionItem.landingCoordSet) {
                    showDragAreas()
                } else {
                    showMouseArea()
                }
            } else {
                hideMouseArea()
                hideDragAreas()
            }
        }

        onLandingCoordSetChanged: {
            if (_missionItem.landingCoordSet) {
                hideMouseArea()
                showItemVisuals()
                showDragAreas()
140
                _setFlightPath()
141 142 143 144 145
            } else if (_missionItem.isCurrentItem) {
                hideDragAreas()
                showMouseArea()
            }
        }
146

147 148
        onLandingCoordinateChanged:         _setFlightPath()
        onLoiterTangentCoordinateChanged:   _setFlightPath()
149 150 151 152 153 154 155 156 157 158
    }

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

        MouseArea {
            anchors.fill: map

            onClicked: {
DonLakeFlyer's avatar
DonLakeFlyer committed
159
                var coordinate = map.toCoordinate(Qt.point(mouse.x, mouse.y), false /* clipToViewPort */)
160 161 162 163 164 165 166 167
                coordinate.latitude = coordinate.latitude.toFixed(_decimalPlaces)
                coordinate.longitude = coordinate.longitude.toFixed(_decimalPlaces)
                coordinate.altitude = coordinate.altitude.toFixed(_decimalPlaces)
                _missionItem.landingCoordinate = coordinate
            }
        }
    }

168
    // Control which is used to drag the loiter point
169
    Component {
170
        id: loiterDragAreaComponent
171

172
        MissionItemIndicatorDrag {
173 174
            itemIndicator:  _itemVisuals[_loiterPointIndex]
            itemCoordinate: _missionItem.loiterCoordinate
175

176
            onItemCoordinateChanged: _missionItem.loiterCoordinate = itemCoordinate
177 178 179 180 181 182 183 184
        }
    }

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

        MissionItemIndicatorDrag {
185 186
            itemIndicator:  _itemVisuals[_landPointIndex]
            itemCoordinate: _missionItem.landingCoordinate
187

188
            onItemCoordinateChanged: _missionItem.landingCoordinate = itemCoordinate
189
        }
190 191 192 193 194 195 196
    }

    // Flight path
    Component {
        id: flightPathComponent

        MapPolyline {
197
            z:          QGroundControl.zOrderMapItems - 1   // Under item indicators
Don Gagne's avatar
Don Gagne committed
198
            line.color: "#be781c"
199
            line.width: 2
200
            path:       _flightPath
201 202 203 204 205
        }
    }

    // Loiter point
    Component {
206
        id: loiterPointComponent
207 208

        MapQuickItem {
209 210
            anchorPoint.x:  sourceItem.anchorPointX
            anchorPoint.y:  sourceItem.anchorPointY
211 212
            z:              QGroundControl.zOrderMapItems
            coordinate:     _missionItem.loiterCoordinate
213 214 215

            sourceItem:
                MissionItemIndexLabel {
216
                index:      _missionItem.sequenceNumber
217 218 219
                label:      "Loiter"
                checked:    _missionItem.isCurrentItem

220
                onClicked: _root.clicked(_missionItem.sequenceNumber)
221 222 223 224
            }
        }
    }

Don Gagne's avatar
Don Gagne committed
225 226 227 228 229 230
    Component {
        id: loiterRadiusComponent

        MapCircle {
            z:              QGroundControl.zOrderMapItems
            center:         _missionItem.loiterCoordinate
231
            radius:         _missionItem.loiterRadius.rawValue
Don Gagne's avatar
Don Gagne committed
232 233 234 235 236 237
            border.width:   2
            border.color:   "green"
            color:          "transparent"
        }
    }

238 239 240 241
    Component {
        id: landPointComponent

        MapQuickItem {
242 243
            anchorPoint.x:  sourceItem.anchorPointX
            anchorPoint.y:  sourceItem.anchorPointY
244 245 246 247 248
            z:              QGroundControl.zOrderMapItems
            coordinate:     _missionItem.landingCoordinate

            sourceItem:
                MissionItemIndexLabel {
249
                index:      _missionItem.lastSequenceNumber
250
                label:      "Land"
Don Gagne's avatar
Don Gagne committed
251
                checked:    _missionItem.isCurrentItem
252

253
                onClicked: _root.clicked(_missionItem.sequenceNumber)
254
            }
255 256
        }
    }
DonLakeFlyer's avatar
DonLakeFlyer committed
257 258 259 260

    Component {
        id: landingAreaComponent

DonLakeFlyer's avatar
DonLakeFlyer committed
261
        MapPolygon {
DonLakeFlyer's avatar
DonLakeFlyer committed
262 263 264 265 266 267
            z:              QGroundControl.zOrderMapItems
            border.width:   1
            border.color:   "black"
            color:          "green"
            opacity:        0.5

DonLakeFlyer's avatar
DonLakeFlyer committed
268
            readonly property real landingWidth:    15
DonLakeFlyer's avatar
DonLakeFlyer committed
269 270
            readonly property real landingLength:   100
            readonly property real angleRadians:    Math.atan((landingWidth / 2) / (landingLength / 2))
DonLakeFlyer's avatar
DonLakeFlyer committed
271
            readonly property real angleDegrees:    (angleRadians * (180 / Math.PI))
DonLakeFlyer's avatar
DonLakeFlyer committed
272
            readonly property real hypotenuse:      (landingWidth / 2) / Math.sin(angleRadians)
DonLakeFlyer's avatar
DonLakeFlyer committed
273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290

            property real landingAreaAngle: _missionItem.landingCoordinate.azimuthTo(_missionItem.loiterTangentCoordinate)

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

            Component.onCompleted: calcPoly()

            Connections {
                target: _missionItem
                onLandingCoordinateChanged: calcPoly()
                onLoiterTangentCoordinateChanged:  calcPoly()
            }
DonLakeFlyer's avatar
DonLakeFlyer committed
291 292
        }
    }
293
}