FlightDisplayViewMap.qml 13.6 KB
Newer Older
1 2 3 4 5 6 7 8
/****************************************************************************
 *
 *   (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.
 *
 ****************************************************************************/
9 10


11 12 13 14 15
import QtQuick          2.3
import QtQuick.Controls 1.2
import QtLocation       5.3
import QtPositioning    5.3
import QtQuick.Dialogs  1.2
16 17 18 19 20 21 22 23 24 25 26 27 28 29 30

import QGroundControl               1.0
import QGroundControl.FlightDisplay 1.0
import QGroundControl.FlightMap     1.0
import QGroundControl.ScreenTools   1.0
import QGroundControl.Controls      1.0
import QGroundControl.Palette       1.0
import QGroundControl.Vehicle       1.0
import QGroundControl.Controllers   1.0

FlightMap {
    id:             flightMap
    anchors.fill:   parent
    mapName:        _mapName

31
    property alias  missionController: missionController
Don Gagne's avatar
Don Gagne committed
32
    property var    flightWidgets
33
    property var    rightPanelWidth
34
    property var    qgcView             ///< QGCView control which contains this map
Don Gagne's avatar
Don Gagne committed
35 36

    property var    _activeVehicle:                 QGroundControl.multiVehicleManager.activeVehicle
37
    property var    _activeVehicleCoordinate:       _activeVehicle ? _activeVehicle.coordinate : QtPositioning.coordinate()
Don Gagne's avatar
Don Gagne committed
38
    property var    _gotoHereCoordinate:            QtPositioning.coordinate()
39
    property int    _retaskSequence:                0
40
    property real   _toolButtonTopMargin:           parent.height - ScreenTools.availableHeight + (ScreenTools.defaultFontPixelHeight / 2)
41 42 43

    property bool   _disableVehicleTracking:        false
    property bool   _keepVehicleCentered:           _mainIsMap ? false : true
44 45
    property bool   _firstVehiclePositionReceived:  false
    property bool   _userPanned:                    false
46

47 48 49
    Component.onCompleted: {
        QGroundControl.flightMapPosition = center
        QGroundControl.flightMapZoom = zoomLevel
50
        possibleCenterToGCSPosition()
51
    }
52

53
    // Track last known map position and zoom in settings
54
    onZoomLevelChanged: QGroundControl.flightMapZoom = zoomLevel
55
    onCenterChanged:    QGroundControl.flightMapPosition = center
56

57 58 59 60 61 62 63 64 65 66 67 68
    // We move the map to the gcs position id:
    //  - We don't have a vehicle position yet
    //  - The user has not futzed with the map
    onGcsPositionChanged: possibleCenterToGCSPosition()

    function possibleCenterToGCSPosition() {
        if (!_firstVehiclePositionReceived && !_userPanned && gcsPosition.isValid) {
            center = gcsPosition
        }
    }

    // When the user pans the map we stop responding to vehicle coordinate updates until the panRecenterTimer fires
69 70 71 72
    Connections {
        target: gesture

        onPanFinished: {
73
            _userPanned = true
74 75
            _disableVehicleTracking = true
            panRecenterTimer.start()
76 77
        }

78
        onFlickFinished: {
79
            _userPanned = true
80 81 82
            _disableVehicleTracking = true
            panRecenterTimer.start()
        }
83 84
    }

85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114
    function pointInRect(point, rect) {
        return point.x > rect.x &&
                point.x < rect.x + rect.width &&
                point.y > rect.y &&
                point.y < rect.y + rect.height;
    }

    property real _animatedLatitudeStart
    property real _animatedLatitudeStop
    property real _animatedLongitudeStart
    property real _animatedLongitudeStop
    property real animatedLatitude
    property real animatedLongitude

    onAnimatedLatitudeChanged: flightMap.center = QtPositioning.coordinate(animatedLatitude, animatedLongitude)
    onAnimatedLongitudeChanged: flightMap.center = QtPositioning.coordinate(animatedLatitude, animatedLongitude)

    NumberAnimation on animatedLatitude { id: animateLat; from: _animatedLatitudeStart; to: _animatedLatitudeStop; duration: 1000 }
    NumberAnimation on animatedLongitude { id: animateLong; from: _animatedLongitudeStart; to: _animatedLongitudeStop; duration: 1000 }

    function animatedMapRecenter(fromCoord, toCoord) {
        _animatedLatitudeStart = fromCoord.latitude
        _animatedLongitudeStart = fromCoord.longitude
        _animatedLatitudeStop = toCoord.latitude
        _animatedLongitudeStop = toCoord.longitude
        animateLat.start()
        animateLong.start()
    }

    function recenterNeeded() {
115
        var vehiclePoint = flightMap.fromCoordinate(_activeVehicleCoordinate, false /* clipToViewport */)
116 117
        var centerViewport = Qt.rect(0, 0, width, height)
        return !pointInRect(vehiclePoint, centerViewport)
118 119 120
    }

    function updateMapToVehiclePosition() {
121
        if (_activeVehicleCoordinate.isValid && !_disableVehicleTracking) {
122
            if (_keepVehicleCentered) {
123 124
                _firstVehiclePositionReceived = true
                flightMap.center = _activeVehicleCoordinate
125
            } else {
126 127 128
                if (!_firstVehiclePositionReceived) {
                    _firstVehiclePositionReceived = true
                    flightMap.center = _activeVehicleCoordinate
129
                } else if (recenterNeeded()) {
130
                    animatedMapRecenter(flightMap.center, _activeVehicleCoordinate)
131 132
                }
            }
133 134 135 136
        }
    }

    Timer {
137 138 139
        id:         panRecenterTimer
        interval:   10000
        running:    false
140 141

        onTriggered: {
142
            _disableVehicleTracking = false
143
            updateMapToVehiclePosition()
144 145 146
        }
    }

147 148 149 150 151 152 153
    Timer {
        interval:       500
        running:        true
        repeat:         true
        onTriggered:    updateMapToVehiclePosition()
    }

Don Gagne's avatar
Don Gagne committed
154
    QGCPalette { id: qgcPal; colorGroupEnabled: true }
155
    QGCMapPalette { id: mapPal; lightColors: isSatelliteMap }
Don Gagne's avatar
Don Gagne committed
156

157
    MissionController {
158
        id: missionController
159 160 161
        Component.onCompleted: start(false /* editMode */)
    }

162
    GeoFenceController {
163
        id: geoFenceController
164 165 166
        Component.onCompleted: start(false /* editMode */)
    }

167 168 169 170 171
    RallyPointController {
        id: rallyPointController
        Component.onCompleted: start(false /* editMode */)
    }

172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214
    // The following code is used to track vehicle states such that we prompt to remove mission from vehicle when mission completes

    property bool vehicleArmed:                 _activeVehicle ? _activeVehicle.armed : false
    property bool vehicleWasArmed:              false
    property bool vehicleInMissionFlightMode:   _activeVehicle ? (_activeVehicle.flightMode === _activeVehicle.missionFlightMode) : false
    property bool promptForMissionRemove:       false

    onVehicleArmedChanged: {
        if (vehicleArmed) {
            if (!promptForMissionRemove) {
                promptForMissionRemove = vehicleInMissionFlightMode
                vehicleWasArmed = true
            }
        } else {
            if (promptForMissionRemove && (missionController.containsItems || geoFenceController.containsItems || rallyPointController.containsItems)) {
                qgcView.showDialog(removeMissionDialogComponent, qsTr("Flight complete"), showDialogDefaultWidth, StandardButton.No | StandardButton.Yes)
            }
            promptForMissionRemove = false
        }
    }

    onVehicleInMissionFlightModeChanged: {
        if (!promptForMissionRemove && vehicleArmed) {
            promptForMissionRemove = true
        }
    }

    Component {
        id: removeMissionDialogComponent

        QGCViewMessage {
            message: qsTr("Do you want to remove the mission from the vehicle?")

            function accept() {
                missionController.removeAllFromVehicle()
                geoFenceController.removeAllFromVehicle()
                rallyPointController.removeAllFromVehicle()
                hideDialog()

            }
        }
    }

215
    ExclusiveGroup {
Don Gagne's avatar
Don Gagne committed
216
        id: _mapTypeButtonsExclusiveGroup
217 218
    }

Don Gagne's avatar
Don Gagne committed
219 220 221 222 223 224 225 226 227
    ToolStrip {
        id:                 toolStrip
        anchors.leftMargin: ScreenTools.defaultFontPixelWidth
        anchors.left:       parent.left
        anchors.topMargin:  _toolButtonTopMargin
        anchors.top:        parent.top
        color:              qgcPal.window
        title:              qsTr("Fly")
        z:                  QGroundControl.zOrderWidgets
228
        buttonVisible:      [ true, _showZoom, _showZoom ]
229
        maxHeight:          (_flightVideo.visible ? _flightVideo.y : parent.height) - toolStrip.y   // Massive reach across hack
Don Gagne's avatar
Don Gagne committed
230

Don Gagne's avatar
Don Gagne committed
231
        property bool _showZoom: !ScreenTools.isMobile
Don Gagne's avatar
Don Gagne committed
232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247

        model: [
            {
                name:               "Center",
                iconSource:         "/qmlimages/MapCenter.svg",
                dropPanelComponent: centerMapDropPanel
            },
            {
                name:               "In",
                iconSource:         "/qmlimages/ZoomPlus.svg"
            },
            {
                name:               "Out",
                iconSource:         "/qmlimages/ZoomMinus.svg"
            }
        ]
248 249

        onClicked: {
Don Gagne's avatar
Don Gagne committed
250
            switch (index) {
Donald Gagne's avatar
Donald Gagne committed
251
            case 1:
Don Gagne's avatar
Don Gagne committed
252 253
                _flightMap.zoomLevel += 0.5
                break
Donald Gagne's avatar
Donald Gagne committed
254
            case 2:
Don Gagne's avatar
Don Gagne committed
255 256
                _flightMap.zoomLevel -= 0.5
                break
257 258 259 260
            }
        }
    }

Don Gagne's avatar
Don Gagne committed
261
    // Toolstrip drop panel compomnents
Don Gagne's avatar
Don Gagne committed
262

Don Gagne's avatar
Don Gagne committed
263 264 265 266 267 268 269 270
    MapFitFunctions {
        id:                         mapFitFunctions
        map:                        _flightMap
        mapFitViewport:             Qt.rect(leftToolWidth, _toolButtonTopMargin, flightMap.width - leftToolWidth - rightPanelWidth, flightMap.height - _toolButtonTopMargin)
        usePlannedHomePosition:     false
        mapMissionController:      missionController
        mapGeoFenceController:     geoFenceController
        mapRallyPointController:   rallyPointController
271

Don Gagne's avatar
Don Gagne committed
272 273
        property real leftToolWidth:    toolStrip.x + toolStrip.width
    }
274

Don Gagne's avatar
Don Gagne committed
275 276
    Component {
        id: centerMapDropPanel
277

Don Gagne's avatar
Don Gagne committed
278
        CenterMapDropPanel {
279 280
            map:                _flightMap
            fitFunctions:       mapFitFunctions
Don Gagne's avatar
Don Gagne committed
281 282
        }
    }
283

284 285
    // Add trajectory points to the map
    MapItemView {
286
        model: _mainIsMap ? _activeVehicle ? _activeVehicle.trajectoryPoints : 0 : 0
287 288
        delegate:
            MapPolyline {
Don Gagne's avatar
Don Gagne committed
289 290
            line.width: 3
            line.color: "red"
291
            z:          QGroundControl.zOrderMapItems - 2
Don Gagne's avatar
Don Gagne committed
292
            path: [
293 294
                object.coordinate1,
                object.coordinate2,
Don Gagne's avatar
Don Gagne committed
295 296
            ]
        }
297 298 299 300
    }

    // Add the vehicles to the map
    MapItemView {
301
        model: QGroundControl.multiVehicleManager.vehicles
302 303
        delegate:
            VehicleMapItem {
Don Gagne's avatar
Don Gagne committed
304 305 306
            vehicle:        object
            coordinate:     object.coordinate
            isSatellite:    flightMap.isSatelliteMap
Gus Grubba's avatar
Gus Grubba committed
307
            size:           _mainIsMap ? ScreenTools.defaultFontPixelHeight * 3 : ScreenTools.defaultFontPixelHeight
308
            z:              QGroundControl.zOrderMapItems - 1
Don Gagne's avatar
Don Gagne committed
309
        }
310 311
    }

312 313
    // Add the mission item visuals to the map
    Repeater {
314
        model: _mainIsMap ? missionController.visualItems : 0
315 316

        delegate: MissionItemMapVisual {
317 318 319 320 321 322
            map:        flightMap

            onClicked: {
                _retaskSequence = object.sequenceNumber
                flightWidgets.guidedModeBar.confirmAction(parent.flightWidgets.guidedModeBar.confirmRetask)
            }
323
        }
324 325 326 327
    }

    // Add lines between waypoints
    MissionLineView {
328
        model: _mainIsMap ? missionController.waypointLines : 0
329 330
    }

331 332 333 334 335
    GeoFenceMapVisuals {
        map:                    flightMap
        myGeoFenceController:   geoFenceController
        interactive:            false
        homePosition:           _activeVehicle && _activeVehicle.homePositionAvailable ? _activeVehicle.homePosition : undefined
336 337
    }

338 339 340 341 342 343
    // Rally points on map
    MapItemView {
        model: rallyPointController.points

        delegate: MapQuickItem {
            id:             itemIndicator
344 345
            anchorPoint.x:  sourceItem.anchorPointX
            anchorPoint.y:  sourceItem.anchorPointY
346 347 348 349 350 351 352 353 354 355
            coordinate:     object.coordinate
            z:              QGroundControl.zOrderMapItems

            sourceItem: MissionItemIndexLabel {
                id:         itemIndexLabel
                label:      qsTr("R", "rally point map item label")
            }
        }
    }

Don Gagne's avatar
Don Gagne committed
356 357 358
    // GoTo here waypoint
    MapQuickItem {
        coordinate:     _gotoHereCoordinate
Don Gagne's avatar
Don Gagne committed
359
        visible:        _activeVehicle && _activeVehicle.guidedMode && _gotoHereCoordinate.isValid
Don Gagne's avatar
Don Gagne committed
360
        z:              QGroundControl.zOrderMapItems
361 362
        anchorPoint.x:  sourceItem.anchorPointX
        anchorPoint.y:  sourceItem.anchorPointY
Don Gagne's avatar
Don Gagne committed
363 364

        sourceItem: MissionItemIndexLabel {
365 366
            checked: true
            label:   qsTr("G", "Goto here waypoint") // second string is translator's hint.
Don Gagne's avatar
Don Gagne committed
367
        }
368 369 370 371 372 373 374 375 376
    }    

    MapScale {
        anchors.bottomMargin:   ScreenTools.defaultFontPixelHeight * (0.66)
        anchors.rightMargin:    ScreenTools.defaultFontPixelHeight * (0.33)
        anchors.bottom:         parent.bottom
        anchors.right:          parent.right
        mapControl:             flightMap
        visible:                !ScreenTools.isTinyScreen
Don Gagne's avatar
Don Gagne committed
377 378 379
    }

    // Handle guided mode clicks
380 381
    MouseArea {
        anchors.fill: parent
Don Gagne's avatar
Don Gagne committed
382 383

        onClicked: {
Don Gagne's avatar
Don Gagne committed
384
            if (_activeVehicle) {
385
                if (flightWidgets.guidedModeBar.state != "Shown") {
Don Gagne's avatar
Don Gagne committed
386
                    flightWidgets.guidedModeBar.state = "Shown"
387 388
                } else {
                    if (flightWidgets.gotoEnabled) {
DonLakeFlyer's avatar
DonLakeFlyer committed
389
                        _gotoHereCoordinate = flightMap.toCoordinate(Qt.point(mouse.x, mouse.y), false /* clipToViewPort */)
390 391
                        flightWidgets.guidedModeBar.confirmAction(flightWidgets.guidedModeBar.confirmGoTo)
                    }
Don Gagne's avatar
Don Gagne committed
392
                }
Don Gagne's avatar
Don Gagne committed
393 394
            }
        }
395 396
    }
}