9import QGroundControl.Controls
10import QGroundControl.FlyView
11import QGroundControl.FlightMap
12import QGroundControl.PlanView
16 allowGCSLocationCenter: true
17 allowVehicleLocationCenter: !_keepVehicleCentered
19 zoomLevel: QGroundControl.flightMapZoom
20 center: QGroundControl.flightMapPosition
23 property Item pipState: _pipState
24 property var rightPanelWidth
25 property var planMasterController
26 property bool pipMode: false // true: map is shown in a small pip mode
27 property rect viewportRect: Qt.rect(0, 0, width, height) // Map area the vehicle is kept within
28 property var occluders: [] // Rects within the viewport covered by UI
30 property var _activeVehicle: QGroundControl.multiVehicleManager.activeVehicle
31 property var _planMasterController: planMasterController
32 property var _geoFenceController: planMasterController.geoFenceController
33 property var _rallyPointController: planMasterController.rallyPointController
34 property var _activeVehicleCoordinate: _activeVehicle ? _activeVehicle.coordinate : QtPositioning.coordinate()
35 property real _toolButtonTopMargin: parent.height - mainWindow.height + (ScreenTools.defaultFontPixelHeight / 2)
36 property real _toolsMargin: ScreenTools.defaultFontPixelWidth * 0.75
37 property var _flyViewSettings: QGroundControl.settingsManager.flyViewSettings
38 property bool _keepMapCenteredOnVehicle: _flyViewSettings.keepMapCenteredOnVehicle.rawValue
40 property bool _keepVehicleCentered: pipMode ? true : false
41 property bool _saveZoomLevelSetting: true
43 function _adjustMapZoomForPipMode() {
44 _saveZoomLevelSetting = false
46 if (QGroundControl.flightMapZoom > 3) {
47 zoomLevel = QGroundControl.flightMapZoom - 3
50 zoomLevel = QGroundControl.flightMapZoom
52 _saveZoomLevelSetting = true
55 onPipModeChanged: _adjustMapZoomForPipMode()
59 // Synchronize center position with Plan View
60 center = QGroundControl.flightMapPosition
65 if (_saveZoomLevelSetting) {
66 QGroundControl.flightMapZoom = _root.zoomLevel
70 QGroundControl.flightMapPosition = _root.center
73 // We track whether the user has panned or not to correctly handle automatic map positioning
74 onMapPanStart: positionTracker.userInteracting = true
75 onMapPanStop: positionTracker.userInteracting = false
77 // Follow behavior on top of FlightMap's one-shot centering: hard follow while
78 // the keep-centered setting or pip mode is active, occluder follow otherwise
80 target: positionTracker
81 property: "keepVehicleCentered"
82 value: _keepMapCenteredOnVehicle || _keepVehicleCentered
86 target: positionTracker
88 value: animateLat.running || animateLong.running
92 target: positionTracker
93 function onRecenterVehicleTo(screenPoint) {
94 // Move the map such that the vehicle lands on screenPoint
95 let vehiclePoint = _root.fromCoordinate(_activeVehicleCoordinate, false /* clipToViewport */)
96 let centerOffset = Qt.point((_root.width / 2) - screenPoint.x, (_root.height / 2) - screenPoint.y)
97 let vehicleOffsetPoint = Qt.point(vehiclePoint.x + centerOffset.x, vehiclePoint.y + centerOffset.y)
98 let vehicleOffsetCoord = _root.toCoordinate(vehicleOffsetPoint, false /* clipToViewport */)
99 animatedMapRecenter(_root.center, vehicleOffsetCoord)
103 property real _animatedLatitudeStart
104 property real _animatedLatitudeStop
105 property real _animatedLongitudeStart
106 property real _animatedLongitudeStop
107 property real animatedLatitude
108 property real animatedLongitude
110 onAnimatedLatitudeChanged: _root.center = QtPositioning.coordinate(animatedLatitude, animatedLongitude)
111 onAnimatedLongitudeChanged: _root.center = QtPositioning.coordinate(animatedLatitude, animatedLongitude)
113 NumberAnimation on animatedLatitude { id: animateLat; from: _animatedLatitudeStart; to: _animatedLatitudeStop; duration: 1000 }
114 NumberAnimation on animatedLongitude { id: animateLong; from: _animatedLongitudeStart; to: _animatedLongitudeStop; duration: 1000 }
116 function animatedMapRecenter(fromCoord, toCoord) {
117 _animatedLatitudeStart = fromCoord.latitude
118 _animatedLongitudeStart = fromCoord.longitude
119 _animatedLatitudeStop = toCoord.latitude
120 _animatedLongitudeStop = toCoord.longitude
127 pipView: _root.pipView
128 isDark: _isFullWindowItemDark
136 let vehiclePoint = _root.fromCoordinate(_activeVehicleCoordinate, false /* clipToViewport */)
137 positionTracker.evaluateOccluderFollow(vehiclePoint, viewportRect, occluders)
141 QGCMapPalette { id: mapPal; lightColors: isSatelliteMap }
144 target: _missionController
145 ignoreUnknownSignals: true
146 function onNewItemsFromVehicle() {
147 var visualItems = _missionController.visualItems
148 if (visualItems && visualItems.count !== 1) {
149 mapFitFunctions.fitMapViewportToMissionItems()
150 firstVehiclePositionReceived = true
156 id: mapFitFunctions // The name for this id cannot be changed without breaking references outside of this code. Beware!
158 usePlannedHomePosition: false
159 planMasterController: _planMasterController
162 ObstacleDistanceOverlayMap {
168 // Add trajectory lines to the map
170 id: trajectoryPolyline
173 z: QGroundControl.zOrderTrajectoryLines
177 target: QGroundControl.multiVehicleManager
178 function onActiveVehicleChanged(activeVehicle) {
179 trajectoryPolyline.path = _activeVehicle ? _activeVehicle.trajectoryPoints.list() : []
184 target: _activeVehicle ? _activeVehicle.trajectoryPoints : null
185 function onPointAdded(coordinate) { trajectoryPolyline.addCoordinate(coordinate) }
186 function onUpdateLastPoint(coordinate) { trajectoryPolyline.replaceCoordinate(trajectoryPolyline.pathLength() - 1, coordinate) }
187 function onPointsCleared() { trajectoryPolyline.path = [] }
191 // Add the vehicles to the map
193 model: QGroundControl.multiVehicleManager.vehicles
194 delegate: VehicleMapItem {
196 coordinate: object.coordinate
198 size: pipMode ? ScreenTools.defaultFontPixelHeight : ScreenTools.defaultFontPixelHeight * 3
199 z: QGroundControl.zOrderVehicles
202 // Add distance sensor view
204 model: QGroundControl.multiVehicleManager.vehicles
205 delegate: ProximityRadarMapView {
207 coordinate: object.coordinate
209 z: QGroundControl.zOrderVehicles
212 // Add ADSB vehicles to the map
214 model: QGroundControl.adsbVehicleManager.adsbVehicles
215 delegate: ADSBVehicleMapItem {
216 coordinate: object.coordinate
217 altitude: object.altitude
218 callsign: object.callsign
219 heading: object.heading
222 size: pipMode ? ScreenTools.defaultFontPixelHeight : ScreenTools.defaultFontPixelHeight * 2.5
223 z: QGroundControl.zOrderVehicles
227 // Add the items associated with each vehicles flight plan to the map
229 model: QGroundControl.multiVehicleManager.vehicles
233 largeMapView: !pipMode
234 planMasterController: masterController
237 property var _vehicle: object
239 PlanMasterController {
241 Component.onCompleted: startStaticActiveVehicle(object)
246 // Allow custom builds to add map items
249 largeMapView: !pipMode
254 myGeoFenceController: _geoFenceController
257 homePosition: _activeVehicle && _activeVehicle.homePosition.isValid ? _activeVehicle.homePosition : QtPositioning.coordinate()
260 // Rally points on map
262 model: _rallyPointController.points
264 delegate: MapQuickItem {
266 anchorPoint.x: sourceItem.anchorPointX
267 anchorPoint.y: sourceItem.anchorPointY
268 coordinate: object.coordinate
269 z: QGroundControl.zOrderMapItems
271 sourceItem: MissionItemIndexLabel {
273 label: qsTr("R", "rally point map item label")
278 // Camera trigger points
280 model: _activeVehicle ? _activeVehicle.cameraTriggerPoints : 0
282 delegate: CameraTriggerIndicator {
283 coordinate: object.coordinate
284 z: QGroundControl.zOrderTopMost
288 // GoTo Location forward flight circle visuals
289 QGCMapCircleVisuals {
290 id: fwdFlightGotoMapCircle
292 mapCircle: _fwdFlightGotoMapCircle
293 radiusLabelVisible: true
294 // PX4 ignores the commanded loiter radius (flies NAV_LOITER_RAD), so the circle size is unknown
295 visible: gotoLocationItem.visible && _activeVehicle &&
296 !_activeVehicle.px4Firmware &&
297 _activeVehicle.inFwdFlight &&
298 !_activeVehicle.orbitActive
300 property alias coordinate: _fwdFlightGotoMapCircle.center
301 property alias radius: _fwdFlightGotoMapCircle.radius
302 property alias clockwiseRotation: _fwdFlightGotoMapCircle.clockwiseRotation
304 Component.onCompleted: {
305 // Only allow editing the radius, not the position
306 centerDragHandleVisible = false
308 globals.guidedControllerFlyView.fwdFlightGotoMapCircle = this
312 target: _fwdFlightGotoMapCircle
314 value: gotoLocationItem.coordinate
317 function startLoiterRadiusEdit() {
318 _fwdFlightGotoMapCircle.interactive = true
321 // Called when loiter edit is confirmed
322 function actionConfirmed() {
323 _fwdFlightGotoMapCircle.interactive = false
324 _fwdFlightGotoMapCircle._commitRadius()
327 // Called when loiter edit is cancelled
328 function actionCancelled() {
329 _fwdFlightGotoMapCircle.interactive = false
330 _fwdFlightGotoMapCircle._restoreRadius()
334 id: _fwdFlightGotoMapCircle
337 clockwiseRotation: true
339 property real _defaultLoiterRadius: _flyViewSettings.forwardFlightGoToLocationLoiterRad.rawValue
340 property real _committedRadius;
343 radius.rawValue = _defaultLoiterRadius
344 // Don't commit the radius in case this operation is undone
347 Component.onCompleted: {
348 radius.rawValue = _defaultLoiterRadius
352 function _commitRadius() {
353 _committedRadius = radius.rawValue
356 function _restoreRadius() {
357 radius.rawValue = _committedRadius
362 // GoTo Location visuals
366 z: QGroundControl.zOrderMapItems
367 anchorPoint.x: sourceItem.anchorPointX
368 anchorPoint.y: sourceItem.anchorPointY
369 sourceItem: MissionItemIndexLabel {
372 label: qsTr("Go here", "Go to location waypoint")
375 property bool inGotoFlightMode: _activeVehicle ? _activeVehicle.flightMode === _activeVehicle.gotoFlightMode : false
377 property var _committedCoordinate: null
379 onInGotoFlightModeChanged: {
380 if (!inGotoFlightMode && gotoLocationItem.visible) {
381 // Hide goto indicator when vehicle falls out of guided mode
386 function show(coord) {
387 gotoLocationItem.coordinate = coord
388 gotoLocationItem.visible = true
392 gotoLocationItem.visible = false
395 function actionConfirmed() {
398 // Commit the new radius which possibly changed
399 fwdFlightGotoMapCircle.actionConfirmed()
401 // We leave the indicator visible. The handling for onInGuidedModeChanged will hide it.
404 function actionCancelled() {
407 // Also restore the loiter radius
408 fwdFlightGotoMapCircle.actionCancelled()
411 function _commitCoordinate() {
413 _committedCoordinate = QtPositioning.coordinate(
419 function _restoreCoordinate() {
420 if (_committedCoordinate) {
421 coordinate = _committedCoordinate
428 // Orbit editing visuals
429 QGCMapCircleVisuals {
432 mapCircle: _mapCircle
435 property alias center: _mapCircle.center
436 property alias clockwiseRotation: _mapCircle.clockwiseRotation
437 readonly property real defaultRadius: 30
440 target: QGroundControl.multiVehicleManager
441 function onActiveVehicleChanged(activeVehicle) {
442 if (!activeVehicle) {
443 orbitMapCircle.visible = false
448 function show(coord) {
449 _mapCircle.radius.rawValue = defaultRadius
450 orbitMapCircle.center = coord
451 orbitMapCircle.visible = true
455 orbitMapCircle.visible = false
458 function actionConfirmed() {
459 // Live orbit status is handled by telemetry so we hide here and telemetry will show again.
463 function actionCancelled() {
468 return _mapCircle.radius.rawValue
471 Component.onCompleted: globals.guidedControllerFlyView.orbitMapCircle = orbitMapCircle
478 clockwiseRotation: true
482 // ROI Location visuals
485 visible: _activeVehicle && _activeVehicle.isROIEnabled
486 z: QGroundControl.zOrderMapItems
487 anchorPoint.x: sourceItem.anchorPointX
488 anchorPoint.y: sourceItem.anchorPointY
491 target: _activeVehicle
492 function onRoiCoordChanged(centerCoord) {
493 roiLocationItem.show(centerCoord)
499 onClicked: (position) => {
500 position = Qt.point(position.x, position.y)
501 var clickCoord = _root.toCoordinate(position, false /* clipToViewPort */)
502 // For some strange reason using mainWindow in mapToItem doesn't work, so we use globals.parent instead which also gets us mainWindow
503 position = mapToItem(globals.parent, position)
504 var dropPanel = roiEditDropPanelComponent.createObject(mainWindow, { clickRect: Qt.rect(position.x, position.y, 0, 0) })
509 sourceItem: MissionItemIndexLabel {
512 label: qsTr("ROI here", "Make this a Region Of Interest")
515 //-- Visibilty controlled by actual state
516 function show(coord) {
517 roiLocationItem.coordinate = coord
521 // Orbit telemetry visuals
522 QGCMapCircleVisuals {
523 id: orbitTelemetryCircle
525 mapCircle: _activeVehicle ? _activeVehicle.orbitMapCircle : null
526 visible: _activeVehicle ? _activeVehicle.orbitActive : false
530 id: orbitCenterIndicator
531 anchorPoint.x: sourceItem.anchorPointX
532 anchorPoint.y: sourceItem.anchorPointY
533 coordinate: _activeVehicle ? _activeVehicle.orbitMapCircle.center : QtPositioning.coordinate()
534 visible: orbitTelemetryCircle.visible && !gotoLocationItem.visible
536 sourceItem: MissionItemIndexLabel {
539 label: qsTr("Orbit", "Orbit waypoint")
543 QGCPopupDialogFactory {
544 id: roiEditPositionDialogFactory
546 dialogComponent: roiEditPositionDialogComponent
550 id: roiEditPositionDialogComponent
553 title: qsTr("Edit ROI Position")
554 coordinate: roiLocationItem.coordinate
556 readonly property var _activeVehicle: QGroundControl.multiVehicleManager.activeVehicle
558 // The ROI belongs to the vehicle the dialog was opened for; close
559 // if that vehicle goes away or the active vehicle changes
560 on_ActiveVehicleChanged: close()
562 onCoordinateChanged: {
563 roiLocationItem.coordinate = coordinate
564 _activeVehicle.guidedModeROI(coordinate, _activeVehicle.roiRelativeAltitudeMeters)
570 id: roiEditDropPanelComponent
575 readonly property var _activeVehicle: QGroundControl.multiVehicleManager.activeVehicle
577 // The ROI belongs to the vehicle the panel was opened for; close
578 // if that vehicle goes away or the active vehicle changes
579 on_ActiveVehicleChanged: close()
581 // Created dynamically per ROI click; close() alone would leak the panel
584 sourceComponent: Component {
586 spacing: ScreenTools.defaultFontPixelWidth / 2
589 Layout.fillWidth: true
590 text: qsTr("Cancel ROI")
592 _activeVehicle.stopGuidedModeROI()
593 roiEditDropPanel.close()
598 Layout.fillWidth: true
599 text: qsTr("Edit Position")
601 roiEditPositionDialogFactory.open()
602 roiEditDropPanel.close()
611 id: mapClickDropPanelComponent
613 FlyViewMapClickDropPanel {
614 gotoIndicator: gotoLocationItem
615 orbitIndicator: orbitMapCircle
619 onMapClicked: (position) => {
620 if (!globals.guidedControllerFlyView.guidedUIVisible &&
621 (globals.guidedControllerFlyView.showGotoLocation || globals.guidedControllerFlyView.showOrbit ||
622 globals.guidedControllerFlyView.showROI || globals.guidedControllerFlyView.showSetHome ||
623 globals.guidedControllerFlyView.showSetEstimatorOrigin)) {
625 position = Qt.point(position.x, position.y)
626 var clickCoord = _root.toCoordinate(position, false /* clipToViewPort */)
627 // For some strange reason using mainWindow in mapToItem doesn't work, so we use globals.parent instead which also gets us mainWindow
628 position = _root.mapToItem(globals.parent, position)
629 var dropPanel = mapClickDropPanelComponent.createObject(mainWindow, { mapClickCoord: clickCoord, clickRect: Qt.rect(position.x, position.y, 0, 0) })
636 anchors.margins: _toolsMargin
637 anchors.left: parent.left
638 anchors.top: parent.top
640 visible: !ScreenTools.isTinyScreen && QGroundControl.corePlugin.options.flyView.showMapScale && mapControl.pipState.state === mapControl.pipState.windowState