QGroundControl
Ground Control Station for MAVLink Drones
Loading...
Searching...
No Matches
FlyViewGeoMapAdapter.qml
Go to the documentation of this file.
1/****************************************************************************
2 *
3 * (c) 2009-2024 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org>
4 *
5 * QGroundControl is licensed according to the terms in the file
6 * COPYING.md in the root of the source code directory.
7 *
8 ****************************************************************************/
9
10import QtQuick
11import QtPositioning
12
13import QGroundControl
14import QGroundControl.Controls
15import QGroundControl.FlightMap
16import QGroundControl.GeoMap
17
18/// GeoMap-engine drop-in for FlyViewMap: hosts a FlyViewGeoMap and implements
19/// the mapControl contract the Fly View overlays consume (pipState,
20/// isSatelliteMap, toCoordinate/fromCoordinate, zoomLevel, ...).
21Item {
22 id: root
23
24 // mapControl contract (parity with FlyViewMap)
25 property Item pipView
26 property Item pipState: _pipState
27 property var rightPanelWidth
28 property var planMasterController
29 property bool pipMode: false // true: map is shown in a small pip mode
30 property rect viewportRect: Qt.rect(0, 0, width, height) // Map area the vehicle is kept within
31 property var occluders: [] // Rects within the viewport covered by UI
32 property real chromeTopMargin: 0 // Map controls on the right edge start below this
33 property real chromeBottomMargin: 0 // Map overlays on the bottom left stay above this
34 property string mapName
35
36 // Writable center (contract parity with FlyViewMap): assignments recentre
37 // the camera; the camera Connections below keeps it tracking afterwards
38 property var center
39 readonly property real zoomLevel: {
40 // Viewport/FOV referenced so the invokable re-evaluates on resize
41 geoMapControl.camera.viewportSize
42 geoMapControl.camera.verticalFieldOfView
43 return geoMapControl.camera.zoomLevelForDistance(geoMapControl.camera.distance)
44 }
45 readonly property bool isSatelliteMap: _mapTypeSetting.indexOf("Satellite") > -1 || _mapTypeSetting.indexOf("Hybrid") > -1
46 property alias pinchZoomDisabledByVirtualJoysticks: geoMapControl.pinchZoomDisabledByVirtualJoysticks
47
48 // Escape hatch for GeoMap-specific chrome/controls
49 readonly property var geoMap: geoMapControl
50
51 readonly property string _mapTypeSetting: QGroundControl.settingsManager.flightMapSettings.mapType.rawValue
52
53 // PiP analog of FlyViewMap._adjustMapZoomForPipMode: pull the camera back
54 // 3 zoom levels in the small window for situational context; the swap-back
55 // resize re-applies the shared saved zoom. PiP is also too small for a useful
56 // 3D view, so force 2D and restore the previous camera mode with it.
57 property int _fullViewCameraMode: GeoMapCamera.Mode2D
58
59 onPipModeChanged: {
60 const camera = geoMapControl.camera
61 if (pipMode) {
62 _fullViewCameraMode = camera.mode
63 camera.mode = GeoMapCamera.Mode2D
64 camera.distance = camera.distanceForZoomLevel(camera.zoomLevelForDistance(camera.distance) - 3)
65 } else {
66 camera.mode = _fullViewCameraMode
67 }
68 }
69
70 // Map view shared with Plan view and persisted across restarts, as
71 // FlyViewMap does via QGroundControl.flightMapPosition/flightMapZoom
72 property bool _applyingSavedZoom: false
73
74 function _restoreSavedMapView() {
75 geoMapControl.camera.center = QGroundControl.flightMapPosition
76 _applySavedZoom()
77 }
78
79 // Zoom level -> distance depends on the viewport size, so this also runs on
80 // every resize: the zoom level is kept, as FlyViewMap does. PiP keeps its own zoom.
81 function _applySavedZoom() {
82 const camera = geoMapControl.camera
83 if (pipMode || camera.viewportSize.width <= 0 || camera.viewportSize.height <= 0) {
84 return
85 }
86 _applyingSavedZoom = true
87 camera.distance = camera.distanceForZoomLevel(QGroundControl.flightMapZoom)
88 _applyingSavedZoom = false
89 }
90
91 // Only the full-view zoom is saved: PiP distance changes are not
92 function _onCameraDistanceChanged() {
93 if (_applyingSavedZoom || !visible || pipMode) {
94 return
95 }
96 const camera = geoMapControl.camera
97 QGroundControl.flightMapZoom = camera.zoomLevelForDistance(camera.distance)
98 }
99
100 onVisibleChanged: {
101 if (visible) {
102 _restoreSavedMapView()
103 }
104 }
105
106 // Breaks the center <-> camera.center cycle: camera moves echo into
107 // center via the Connections below and must not be written back
108 property bool _echoingCameraCenter: false
109
110 onCenterChanged: {
111 if (_echoingCameraCenter || !center || !center.isValid) {
112 return
113 }
114 geoMapControl.camera.center = center
115 }
116
117 // Screen point -> coordinate of the rendered terrain surface under it
118 // (clipToViewPort accepted for signature parity, screen points are
119 // always inside the viewport for the fly view use cases)
120 function toCoordinate(point, clipToViewPort) {
121 return geoMapControl.surfaceCoordinateAt(point)
122 }
123
124 // Geographic coordinate -> screen point; invalid/behind-camera projects
125 // to an off-screen point so distance math degrades instead of throwing
126 function fromCoordinate(coordinate, clipToViewPort) {
127 const screenPos = geoMapControl.scene.screenPositionFor(coordinate)
128 return (screenPos === undefined) ? Qt.point(-1, -1) : screenPos
129 }
130
131 // MapFitFunctions contract (FlightMap.setVisibleRegion parity)
132 function setVisibleRegion(region) {
133 geoMapControl.fitToRegion(region)
134 // PiP zoom is otherwise never shared: save the fit so the swap back keeps it (FlyViewMap parity)
135 if (pipMode && visible) {
136 const camera = geoMapControl.camera
137 QGroundControl.flightMapZoom = camera.zoomLevelForDistance(camera.distance)
138 }
139 }
140
141 FlyViewGeoMap {
142 id: geoMapControl
143 anchors.fill: parent
144 followViewportRect: root.viewportRect
145 followOccluders: root.occluders
146
147 // The PiP window is too small for free camera panning to be useful
148 keepVehicleCentered: root.pipMode || QGroundControl.settingsManager.flyViewSettings.keepMapCenteredOnVehicle.rawValue
149
150 // Guided-action popup on click (FlyViewMap.onMapClicked parity).
151 // Allowed at any tilt: toCoordinate picks the rendered terrain
152 // surface, so the click lands where the user sees it even in 3D.
153 onMapClicked: (position) => {
154 if (root.pipMode) {
155 return
156 }
157 if (!globals.guidedControllerFlyView.guidedUIVisible &&
158 (globals.guidedControllerFlyView.showGotoLocation || globals.guidedControllerFlyView.showOrbit ||
159 globals.guidedControllerFlyView.showROI || globals.guidedControllerFlyView.showSetHome ||
160 globals.guidedControllerFlyView.showSetEstimatorOrigin)) {
161
162 const clickCoord = root.toCoordinate(Qt.point(position.x, position.y), false /* clipToViewPort */)
163 if (!clickCoord.isValid) {
164 return
165 }
166 const windowPosition = root.mapToItem(globals.parent, position.x, position.y)
167 const dropPanel = mapClickDropPanelComponent.createObject(mainWindow, { mapClickCoord: clickCoord, clickRect: Qt.rect(windowPosition.x, windowPosition.y, 0, 0) })
168 dropPanel.open()
169 }
170 }
171 }
172
173 Component {
174 id: mapClickDropPanelComponent
175
176 FlyViewMapClickDropPanel {
177 gotoIndicator: geoMapControl.gotoIndicator
178 orbitIndicator: geoMapControl.orbitIndicator
179 }
180 }
181
182 ObstacleDistanceOverlayMap {
183 mapControl: root
184 showText: !root.pipMode
185 }
186
187 FlyViewGeoMapChrome {
188 anchors.fill: parent
189 geoMap: geoMapControl
190 visible: !root.pipMode
191 buttonsTopMargin: root.chromeTopMargin
192 overlayBottomMargin: root.chromeBottomMargin
193 }
194
195 PipState {
196 id: _pipState
197 pipView: root.pipView
198 isDark: _isFullWindowItemDark
199 }
200
201 MapFitFunctions {
202 id: mapFitFunctions
203 map: root
204 usePlannedHomePosition: false
205 planMasterController: root.planMasterController
206 }
207
208 // Zoom to a mission downloaded from the vehicle (FlyViewMap parity)
209 Connections {
210 target: root.planMasterController ? root.planMasterController.missionController : null
211
212 function onNewItemsFromVehicle() {
213 const visualItems = root.planMasterController.missionController.visualItems
214 if (visualItems && visualItems.count !== 1) {
215 mapFitFunctions.fitMapViewportToMissionItems()
216 geoMapControl.positionTracker.firstVehiclePositionReceived = true
217 }
218 }
219 }
220
221 Connections {
222 target: geoMapControl.camera
223
224 function onCenterChanged() {
225 root._echoingCameraCenter = true
226 root.center = geoMapControl.camera.center
227 root._echoingCameraCenter = false
228 // Hidden: don't overwrite the position another view is sharing
229 if (root.visible) {
230 QGroundControl.flightMapPosition = geoMapControl.camera.center
231 }
232 }
233
234 function onDistanceChanged() {
235 root._onCameraDistanceChanged()
236 }
237
238 function onViewportSizeChanged() {
239 root._applySavedZoom()
240 }
241 }
242
243 Component.onCompleted: {
244 _restoreSavedMapView()
245 _echoingCameraCenter = true
246 center = geoMapControl.camera.center
247 _echoingCameraCenter = false
248 }
249}