QGroundControl
Ground Control Station for MAVLink Drones
Loading...
Searching...
No Matches
FlightMap.qml
Go to the documentation of this file.
1import QtQuick
2import QtQuick.Controls
3import QtLocation
4import QtPositioning
5import QtQuick.Dialogs
6import Qt.labs.animation
7
8import QGroundControl
9import QGroundControl.Controls
10import QGroundControl.FlightMap
11
12Map {
13 id: _map
14
15 plugin: Plugin { name: "QGroundControl" }
16 opacity: 0.99 // https://bugreports.qt.io/browse/QTBUG-82185
17
18 property string mapName: 'defaultMap'
19 property bool isSatelliteMap: activeMapType.name.indexOf("Satellite") > -1 || activeMapType.name.indexOf("Hybrid") > -1
20 property var gcsPosition: QGroundControl.qgcPositionManger.gcsPosition
21 property real gcsHeading: QGroundControl.qgcPositionManger.gcsHeading
22 property bool allowGCSLocationCenter: false ///< true: map will center/zoom to gcs location one time
23 property bool allowVehicleLocationCenter: false ///< true: map will center/zoom to vehicle location one time
24 property bool firstGCSPositionReceived: false ///< true: first gcs position update was responded to
25 property bool firstVehiclePositionReceived: false ///< true: first vehicle position update was responded to
26 property bool planView: false ///< true: map being using for Plan view, items should be draggable
27
28 property var _activeVehicle: QGroundControl.multiVehicleManager.activeVehicle
29 property var _activeVehicleCoordinate: _activeVehicle ? _activeVehicle.coordinate : QtPositioning.coordinate()
30
31 function setVisibleRegion(region) {
32 // TODO: Is this still necessary with Qt 5.11?
33 // This works around a bug on Qt where if you set a visibleRegion and then the user moves or zooms the map
34 // and then you set the same visibleRegion the map will not move/scale appropriately since it thinks there
35 // is nothing to do.
36 let maxZoomLevel = 20
37 _map.visibleRegion = QtPositioning.rectangle(QtPositioning.coordinate(0, 0), QtPositioning.coordinate(0, 0))
38 _map.visibleRegion = region
39 if (_map.zoomLevel > maxZoomLevel) {
40 _map.zoomLevel = maxZoomLevel
41 }
42 }
43
44 function _possiblyCenterToVehiclePosition() {
45 if (!firstVehiclePositionReceived && allowVehicleLocationCenter && _activeVehicleCoordinate.isValid) {
46 firstVehiclePositionReceived = true
47 center = _activeVehicleCoordinate
48 zoomLevel = QGroundControl.flightMapInitialZoom
49 }
50 }
51
52 function centerToSpecifiedLocation() {
53 specifyMapPositionDialogFactory.open()
54 }
55
56 QGCPopupDialogFactory {
57 id: specifyMapPositionDialogFactory
58
59 dialogComponent: specifyMapPositionDialog
60 }
61
62 Component {
63 id: specifyMapPositionDialog
64 EditPositionDialog {
65 title: qsTr("Specify Position")
66 coordinate: center
67 onCoordinateChanged: center = coordinate
68 }
69 }
70
71 // Center map to gcs location
72 onGcsPositionChanged: {
73 if (gcsPosition.isValid && allowGCSLocationCenter && !firstGCSPositionReceived && !firstVehiclePositionReceived) {
74 firstGCSPositionReceived = true
75 //-- Only center on gsc if we have no vehicle (and we are supposed to do so)
76 var _activeVehicleCoordinate = _activeVehicle ? _activeVehicle.coordinate : QtPositioning.coordinate()
77 if(QGroundControl.settingsManager.flyViewSettings.keepMapCenteredOnVehicle.rawValue || !_activeVehicleCoordinate.isValid)
78 center = gcsPosition
79 }
80 }
81
82 function updateActiveMapType() {
83 var settings = QGroundControl.settingsManager.flightMapSettings
84 var fullMapName = settings.mapProvider.value + " " + settings.mapType.value
85
86 for (var i = 0; i < _map.supportedMapTypes.length; i++) {
87 if (fullMapName === _map.supportedMapTypes[i].name) {
88 _map.activeMapType = _map.supportedMapTypes[i]
89 return
90 }
91 }
92 }
93
94 on_ActiveVehicleCoordinateChanged: _possiblyCenterToVehiclePosition()
95
96 onMapReadyChanged: {
97 if (_map.mapReady) {
98 updateActiveMapType()
99 _possiblyCenterToVehiclePosition()
100 }
101 }
102
103 Connections {
104 target: QGroundControl.settingsManager.flightMapSettings.mapType
105 function onRawValueChanged() { updateActiveMapType() }
106 }
107
108 Connections {
109 target: QGroundControl.settingsManager.flightMapSettings.mapProvider
110 function onRawValueChanged() { updateActiveMapType() }
111 }
112
113 signal mapPanStart
114 signal mapPanStop
115 signal mapClicked(var position)
116 signal mapRightClicked(var position)
117 signal mapPressAndHold(var position)
118
119 PinchHandler {
120 id: pinchHandler
121 target: null
122
123 property var pinchStartCentroid
124
125 onActiveChanged: {
126 if (active) {
127 pinchStartCentroid = _map.toCoordinate(pinchHandler.centroid.position, false)
128 }
129 }
130 onScaleChanged: (delta) => {
131 let newZoomLevel = Math.max(_map.zoomLevel + Math.log2(delta), 0)
132 _map.zoomLevel = newZoomLevel
133 _map.alignCoordinateToPoint(pinchStartCentroid, pinchHandler.centroid.position)
134 }
135 }
136
137 WheelHandler {
138 // workaround for QTBUG-87646 / QTBUG-112394 / QTBUG-112432:
139 // Magic Mouse pretends to be a trackpad but doesn't work with PinchHandler
140 // and we don't yet distinguish mice and trackpads on Wayland either
141 acceptedDevices: Qt.platform.pluginName === "cocoa" || Qt.platform.pluginName === "wayland" ?
142 PointerDevice.Mouse | PointerDevice.TouchPad : PointerDevice.Mouse
143 rotationScale: 1 / 120
144
145 onWheel: (event) => {
146 const zoomDelta = event.angleDelta.y * rotationScale
147 const mouseGeoPos = _map.toCoordinate(Qt.point(event.x, event.y), false)
148 _map.zoomLevel = Math.max(_map.zoomLevel + zoomDelta, 0)
149 _map.alignCoordinateToPoint(mouseGeoPos, Qt.point(event.x, event.y))
150 }
151 }
152
153 // We specifically do not use a DragHandler for panning. It just causes too many problems if you overlay anything else like a Flickable above it.
154 // Causes all sorts of crazy problems where dragging/scrolling no longerr works on items above in the hierarchy.
155 // Since we are using a MouseArea we also can't use TapHandler for clicks. So we handle that here as well.
156 MultiPointTouchArea {
157 id: multiTouchArea
158 anchors.fill: parent
159 maximumTouchPoints: 1
160 mouseEnabled: true
161
162 property bool dragActive: false
163 property real lastMouseX
164 property real lastMouseY
165 property bool isPressed: false
166 property bool pressAndHold: false
167
168 onPressed: (touchPoints) => {
169 lastMouseX = touchPoints[0].x
170 lastMouseY = touchPoints[0].y
171 isPressed = true
172 pressAndHold = false
173 pressAndHoldTimer.start()
174 }
175
176 onGestureStarted: (gesture) => {
177 dragActive = true
178 gesture.grab()
179 mapPanStart()
180 }
181
182 onUpdated: (touchPoints) => {
183 if (dragActive) {
184 let deltaX = touchPoints[0].x - lastMouseX
185 let deltaY = touchPoints[0].y - lastMouseY
186 if (Math.abs(deltaX) >= 1.0 || Math.abs(deltaY) >= 1.0) {
187 _map.pan(lastMouseX - touchPoints[0].x, lastMouseY - touchPoints[0].y)
188 lastMouseX = touchPoints[0].x
189 lastMouseY = touchPoints[0].y
190 }
191 }
192 }
193
194 onReleased: (touchPoints) => {
195 isPressed = false
196 pressAndHoldTimer.stop()
197 if (dragActive) {
198 _map.pan(lastMouseX - touchPoints[0].x, lastMouseY - touchPoints[0].y)
199 dragActive = false
200 mapPanStop()
201 } else if (!pressAndHold) {
202 mapClicked(Qt.point(touchPoints[0].x, touchPoints[0].y))
203 }
204 pressAndHold = false
205 }
206
207 Timer {
208 id: pressAndHoldTimer
209 interval: 600 // hold duration in ms
210 repeat: false
211
212 onTriggered: {
213 if (multiTouchArea.isPressed && !multiTouchArea.dragActive) {
214 multiTouchArea.pressAndHold = true
215 mapPressAndHold(Qt.point(multiTouchArea.lastMouseX, multiTouchArea.lastMouseY))
216 }
217 }
218 }
219 }
220
221 MouseArea {
222 anchors.fill: parent
223 acceptedButtons: Qt.RightButton
224 propagateComposedEvents: true
225
226 onPressed: (mouseEvent) => {
227 if (mouseEvent.button === Qt.RightButton) {
228 mapRightClicked(Qt.point(mouseEvent.x, mouseEvent.y))
229 }
230 }
231 }
232
233 /// Ground Station location
234 MapQuickItem {
235 anchorPoint.x: sourceItem.width / 2
236 anchorPoint.y: sourceItem.height / 2
237 visible: gcsPosition.isValid && !planView
238 coordinate: gcsPosition
239
240 sourceItem: Image {
241 id: mapItemImage
242 source: isNaN(gcsHeading) ? "/res/QGCLogoFull.svg" : "/res/QGCLogoArrow.svg"
243 mipmap: true
244 antialiasing: true
245 fillMode: Image.PreserveAspectFit
246 height: ScreenTools.defaultFontPixelHeight * (isNaN(gcsHeading) ? 1.75 : 2.5 )
247 sourceSize.height: height
248 transform: Rotation {
249 origin.x: mapItemImage.width / 2
250 origin.y: mapItemImage.height / 2
251 angle: isNaN(gcsHeading) ? 0 : gcsHeading
252 }
253 }
254 }
255} // Map