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 property: "zoomLevel"
145
146 }
147
148 // 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.
149 // Causes all sorts of crazy problems where dragging/scrolling no longerr works on items above in the hierarchy.
150 // Since we are using a MouseArea we also can't use TapHandler for clicks. So we handle that here as well.
151 MultiPointTouchArea {
152 id: multiTouchArea
153 anchors.fill: parent
154 maximumTouchPoints: 1
155 mouseEnabled: true
156
157 property bool dragActive: false
158 property real lastMouseX
159 property real lastMouseY
160 property bool isPressed: false
161 property bool pressAndHold: false
162
163 onPressed: (touchPoints) => {
164 lastMouseX = touchPoints[0].x
165 lastMouseY = touchPoints[0].y
166 isPressed = true
167 pressAndHold = false
168 pressAndHoldTimer.start()
169 }
170
171 onGestureStarted: (gesture) => {
172 dragActive = true
173 gesture.grab()
174 mapPanStart()
175 }
176
177 onUpdated: (touchPoints) => {
178 if (dragActive) {
179 let deltaX = touchPoints[0].x - lastMouseX
180 let deltaY = touchPoints[0].y - lastMouseY
181 if (Math.abs(deltaX) >= 1.0 || Math.abs(deltaY) >= 1.0) {
182 _map.pan(lastMouseX - touchPoints[0].x, lastMouseY - touchPoints[0].y)
183 lastMouseX = touchPoints[0].x
184 lastMouseY = touchPoints[0].y
185 }
186 }
187 }
188
189 onReleased: (touchPoints) => {
190 isPressed = false
191 pressAndHoldTimer.stop()
192 if (dragActive) {
193 _map.pan(lastMouseX - touchPoints[0].x, lastMouseY - touchPoints[0].y)
194 dragActive = false
195 mapPanStop()
196 } else if (!pressAndHold) {
197 mapClicked(Qt.point(touchPoints[0].x, touchPoints[0].y))
198 }
199 pressAndHold = false
200 }
201
202 Timer {
203 id: pressAndHoldTimer
204 interval: 600 // hold duration in ms
205 repeat: false
206
207 onTriggered: {
208 if (multiTouchArea.isPressed && !multiTouchArea.dragActive) {
209 multiTouchArea.pressAndHold = true
210 mapPressAndHold(Qt.point(multiTouchArea.lastMouseX, multiTouchArea.lastMouseY))
211 }
212 }
213 }
214 }
215
216 MouseArea {
217 anchors.fill: parent
218 acceptedButtons: Qt.RightButton
219 propagateComposedEvents: true
220
221 onPressed: (mouseEvent) => {
222 if (mouseEvent.button === Qt.RightButton) {
223 mapRightClicked(Qt.point(mouseEvent.x, mouseEvent.y))
224 }
225 }
226 }
227
228 /// Ground Station location
229 MapQuickItem {
230 anchorPoint.x: sourceItem.width / 2
231 anchorPoint.y: sourceItem.height / 2
232 visible: gcsPosition.isValid
233 coordinate: gcsPosition
234
235 sourceItem: Image {
236 id: mapItemImage
237 source: isNaN(gcsHeading) ? "/res/QGCLogoFull.svg" : "/res/QGCLogoArrow.svg"
238 mipmap: true
239 antialiasing: true
240 fillMode: Image.PreserveAspectFit
241 height: ScreenTools.defaultFontPixelHeight * (isNaN(gcsHeading) ? 1.75 : 2.5 )
242 sourceSize.height: height
243 transform: Rotation {
244 origin.x: mapItemImage.width / 2
245 origin.y: mapItemImage.height / 2
246 angle: isNaN(gcsHeading) ? 0 : gcsHeading
247 }
248 }
249 }
250} // Map