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 var toolInsets // Insets for the center viewport area
29 property var _activeVehicle: QGroundControl.multiVehicleManager.activeVehicle
30 property var _planMasterController: planMasterController
31 property var _geoFenceController: planMasterController.geoFenceController
32 property var _rallyPointController: planMasterController.rallyPointController
33 property var _activeVehicleCoordinate: _activeVehicle ? _activeVehicle.coordinate : QtPositioning.coordinate()
34 property real _toolButtonTopMargin: parent.height - mainWindow.height + (ScreenTools.defaultFontPixelHeight / 2)
35 property real _toolsMargin: ScreenTools.defaultFontPixelWidth * 0.75
36 property var _flyViewSettings: QGroundControl.settingsManager.flyViewSettings
37 property bool _keepMapCenteredOnVehicle: _flyViewSettings.keepMapCenteredOnVehicle.rawValue
39 property bool _keepVehicleCentered: pipMode ? true : false
40 property bool _saveZoomLevelSetting: true
42 function _adjustMapZoomForPipMode() {
43 _saveZoomLevelSetting = false
45 if (QGroundControl.flightMapZoom > 3) {
46 zoomLevel = QGroundControl.flightMapZoom - 3
49 zoomLevel = QGroundControl.flightMapZoom
51 _saveZoomLevelSetting = true
54 onPipModeChanged: _adjustMapZoomForPipMode()
58 // Synchronize center position with Plan View
59 center = QGroundControl.flightMapPosition
64 if (_saveZoomLevelSetting) {
65 QGroundControl.flightMapZoom = _root.zoomLevel
69 QGroundControl.flightMapPosition = _root.center
72 // We track whether the user has panned or not to correctly handle automatic map positioning
73 onMapPanStart: positionTracker.userInteracting = true
74 onMapPanStop: positionTracker.userInteracting = false
76 // Follow behavior on top of FlightMap's one-shot centering: hard follow while
77 // the keep-centered setting or pip mode is active, inset-rect follow otherwise
79 target: positionTracker
80 property: "keepVehicleCentered"
81 value: _keepMapCenteredOnVehicle || _keepVehicleCentered
85 target: positionTracker
87 value: animateLat.running || animateLong.running
91 target: positionTracker
92 function onRecenterVehicleTo(screenPoint) {
93 // Move the map such that the vehicle lands on screenPoint
94 let vehiclePoint = _root.fromCoordinate(_activeVehicleCoordinate, false /* clipToViewport */)
95 let centerOffset = Qt.point((_root.width / 2) - screenPoint.x, (_root.height / 2) - screenPoint.y)
96 let vehicleOffsetPoint = Qt.point(vehiclePoint.x + centerOffset.x, vehiclePoint.y + centerOffset.y)
97 let vehicleOffsetCoord = _root.toCoordinate(vehicleOffsetPoint, false /* clipToViewport */)
98 animatedMapRecenter(_root.center, vehicleOffsetCoord)
102 property real _animatedLatitudeStart
103 property real _animatedLatitudeStop
104 property real _animatedLongitudeStart
105 property real _animatedLongitudeStop
106 property real animatedLatitude
107 property real animatedLongitude
109 onAnimatedLatitudeChanged: _root.center = QtPositioning.coordinate(animatedLatitude, animatedLongitude)
110 onAnimatedLongitudeChanged: _root.center = QtPositioning.coordinate(animatedLatitude, animatedLongitude)
112 NumberAnimation on animatedLatitude { id: animateLat; from: _animatedLatitudeStart; to: _animatedLatitudeStop; duration: 1000 }
113 NumberAnimation on animatedLongitude { id: animateLong; from: _animatedLongitudeStart; to: _animatedLongitudeStop; duration: 1000 }
115 function animatedMapRecenter(fromCoord, toCoord) {
116 _animatedLatitudeStart = fromCoord.latitude
117 _animatedLongitudeStart = fromCoord.longitude
118 _animatedLatitudeStop = toCoord.latitude
119 _animatedLongitudeStop = toCoord.longitude
124 // returns the rectangle formed by the four center insets
125 // used for checking if vehicle is under ui, and as a target for recentering the view
126 function _insetCenterRect() {
127 return Qt.rect(toolInsets.leftEdgeCenterInset,
128 toolInsets.topEdgeCenterInset,
129 _root.width - toolInsets.leftEdgeCenterInset - toolInsets.rightEdgeCenterInset,
130 _root.height - toolInsets.topEdgeCenterInset - toolInsets.bottomEdgeCenterInset)
133 // returns the four rectangles formed by the 8 corner insets
134 // used for detecting if the vehicle has flown under the instrument panel, virtual joystick etc
135 function _insetCornerRects() {
138 toolInsets.leftEdgeTopInset,
139 toolInsets.topEdgeLeftInset),
140 Qt.rect(_root.width-toolInsets.rightEdgeTopInset,0,
141 toolInsets.rightEdgeTopInset,
142 toolInsets.topEdgeRightInset),
143 Qt.rect(0,_root.height-toolInsets.bottomEdgeLeftInset,
144 toolInsets.leftEdgeBottomInset,
145 toolInsets.bottomEdgeLeftInset),
146 Qt.rect(_root.width-toolInsets.rightEdgeBottomInset,_root.height-toolInsets.bottomEdgeRightInset,
147 toolInsets.rightEdgeBottomInset,
148 toolInsets.bottomEdgeRightInset)]
153 pipView: _root.pipView
154 isDark: _isFullWindowItemDark
162 let vehiclePoint = _root.fromCoordinate(_activeVehicleCoordinate, false /* clipToViewport */)
163 positionTracker.evaluateInsetFollow(vehiclePoint, _insetCenterRect(), _insetCornerRects())
167 QGCMapPalette { id: mapPal; lightColors: isSatelliteMap }
170 target: _missionController
171 ignoreUnknownSignals: true
172 function onNewItemsFromVehicle() {
173 var visualItems = _missionController.visualItems
174 if (visualItems && visualItems.count !== 1) {
175 mapFitFunctions.fitMapViewportToMissionItems()
176 firstVehiclePositionReceived = true
182 id: mapFitFunctions // The name for this id cannot be changed without breaking references outside of this code. Beware!
184 usePlannedHomePosition: false
185 planMasterController: _planMasterController
188 ObstacleDistanceOverlayMap {
193 // Add trajectory lines to the map
195 id: trajectoryPolyline
198 z: QGroundControl.zOrderTrajectoryLines
202 target: QGroundControl.multiVehicleManager
203 function onActiveVehicleChanged(activeVehicle) {
204 trajectoryPolyline.path = _activeVehicle ? _activeVehicle.trajectoryPoints.list() : []
209 target: _activeVehicle ? _activeVehicle.trajectoryPoints : null
210 function onPointAdded(coordinate) { trajectoryPolyline.addCoordinate(coordinate) }
211 function onUpdateLastPoint(coordinate) { trajectoryPolyline.replaceCoordinate(trajectoryPolyline.pathLength() - 1, coordinate) }
212 function onPointsCleared() { trajectoryPolyline.path = [] }
216 // Add the vehicles to the map
218 model: QGroundControl.multiVehicleManager.vehicles
219 delegate: VehicleMapItem {
221 coordinate: object.coordinate
223 size: pipMode ? ScreenTools.defaultFontPixelHeight : ScreenTools.defaultFontPixelHeight * 3
224 z: QGroundControl.zOrderVehicles
227 // Add distance sensor view
229 model: QGroundControl.multiVehicleManager.vehicles
230 delegate: ProximityRadarMapView {
232 coordinate: object.coordinate
234 z: QGroundControl.zOrderVehicles
237 // Add ADSB vehicles to the map
239 model: QGroundControl.adsbVehicleManager.adsbVehicles
240 delegate: ADSBVehicleMapItem {
241 coordinate: object.coordinate
242 altitude: object.altitude
243 callsign: object.callsign
244 heading: object.heading
247 size: pipMode ? ScreenTools.defaultFontPixelHeight : ScreenTools.defaultFontPixelHeight * 2.5
248 z: QGroundControl.zOrderVehicles
252 // Add the items associated with each vehicles flight plan to the map
254 model: QGroundControl.multiVehicleManager.vehicles
258 largeMapView: !pipMode
259 planMasterController: masterController
262 property var _vehicle: object
264 PlanMasterController {
266 Component.onCompleted: startStaticActiveVehicle(object)
271 // Allow custom builds to add map items
274 largeMapView: !pipMode
279 myGeoFenceController: _geoFenceController
282 homePosition: _activeVehicle && _activeVehicle.homePosition.isValid ? _activeVehicle.homePosition : QtPositioning.coordinate()
285 // Rally points on map
287 model: _rallyPointController.points
289 delegate: MapQuickItem {
291 anchorPoint.x: sourceItem.anchorPointX
292 anchorPoint.y: sourceItem.anchorPointY
293 coordinate: object.coordinate
294 z: QGroundControl.zOrderMapItems
296 sourceItem: MissionItemIndexLabel {
298 label: qsTr("R", "rally point map item label")
303 // Camera trigger points
305 model: _activeVehicle ? _activeVehicle.cameraTriggerPoints : 0
307 delegate: CameraTriggerIndicator {
308 coordinate: object.coordinate
309 z: QGroundControl.zOrderTopMost
313 // GoTo Location forward flight circle visuals
314 QGCMapCircleVisuals {
315 id: fwdFlightGotoMapCircle
317 mapCircle: _fwdFlightGotoMapCircle
318 radiusLabelVisible: true
319 visible: gotoLocationItem.visible && _activeVehicle &&
320 _activeVehicle.inFwdFlight &&
321 !_activeVehicle.orbitActive
323 property alias coordinate: _fwdFlightGotoMapCircle.center
324 property alias radius: _fwdFlightGotoMapCircle.radius
325 property alias clockwiseRotation: _fwdFlightGotoMapCircle.clockwiseRotation
327 Component.onCompleted: {
328 // Only allow editing the radius, not the position
329 centerDragHandleVisible = false
331 globals.guidedControllerFlyView.fwdFlightGotoMapCircle = this
335 target: _fwdFlightGotoMapCircle
337 value: gotoLocationItem.coordinate
340 function startLoiterRadiusEdit() {
341 _fwdFlightGotoMapCircle.interactive = true
344 // Called when loiter edit is confirmed
345 function actionConfirmed() {
346 _fwdFlightGotoMapCircle.interactive = false
347 _fwdFlightGotoMapCircle._commitRadius()
350 // Called when loiter edit is cancelled
351 function actionCancelled() {
352 _fwdFlightGotoMapCircle.interactive = false
353 _fwdFlightGotoMapCircle._restoreRadius()
357 id: _fwdFlightGotoMapCircle
360 clockwiseRotation: true
362 property real _defaultLoiterRadius: _flyViewSettings.forwardFlightGoToLocationLoiterRad.value
363 property real _committedRadius;
366 radius.rawValue = _defaultLoiterRadius
367 // Don't commit the radius in case this operation is undone
370 Component.onCompleted: {
371 radius.rawValue = _defaultLoiterRadius
375 function _commitRadius() {
376 _committedRadius = radius.rawValue
379 function _restoreRadius() {
380 radius.rawValue = _committedRadius
385 // GoTo Location visuals
389 z: QGroundControl.zOrderMapItems
390 anchorPoint.x: sourceItem.anchorPointX
391 anchorPoint.y: sourceItem.anchorPointY
392 sourceItem: MissionItemIndexLabel {
395 label: qsTr("Go here", "Go to location waypoint")
398 property bool inGotoFlightMode: _activeVehicle ? _activeVehicle.flightMode === _activeVehicle.gotoFlightMode : false
400 property var _committedCoordinate: null
402 onInGotoFlightModeChanged: {
403 if (!inGotoFlightMode && gotoLocationItem.visible) {
404 // Hide goto indicator when vehicle falls out of guided mode
409 function show(coord) {
410 gotoLocationItem.coordinate = coord
411 gotoLocationItem.visible = true
415 gotoLocationItem.visible = false
418 function actionConfirmed() {
421 // Commit the new radius which possibly changed
422 fwdFlightGotoMapCircle.actionConfirmed()
424 // We leave the indicator visible. The handling for onInGuidedModeChanged will hide it.
427 function actionCancelled() {
430 // Also restore the loiter radius
431 fwdFlightGotoMapCircle.actionCancelled()
434 function _commitCoordinate() {
436 _committedCoordinate = QtPositioning.coordinate(
442 function _restoreCoordinate() {
443 if (_committedCoordinate) {
444 coordinate = _committedCoordinate
451 // Orbit editing visuals
452 QGCMapCircleVisuals {
455 mapCircle: _mapCircle
458 property alias center: _mapCircle.center
459 property alias clockwiseRotation: _mapCircle.clockwiseRotation
460 readonly property real defaultRadius: 30
463 target: QGroundControl.multiVehicleManager
464 function onActiveVehicleChanged(activeVehicle) {
465 if (!activeVehicle) {
466 orbitMapCircle.visible = false
471 function show(coord) {
472 _mapCircle.radius.rawValue = defaultRadius
473 orbitMapCircle.center = coord
474 orbitMapCircle.visible = true
478 orbitMapCircle.visible = false
481 function actionConfirmed() {
482 // Live orbit status is handled by telemetry so we hide here and telemetry will show again.
486 function actionCancelled() {
491 return _mapCircle.radius.rawValue
494 Component.onCompleted: globals.guidedControllerFlyView.orbitMapCircle = orbitMapCircle
501 clockwiseRotation: true
505 // ROI Location visuals
508 visible: _activeVehicle && _activeVehicle.isROIEnabled
509 z: QGroundControl.zOrderMapItems
510 anchorPoint.x: sourceItem.anchorPointX
511 anchorPoint.y: sourceItem.anchorPointY
514 target: _activeVehicle
515 function onRoiCoordChanged(centerCoord) {
516 roiLocationItem.show(centerCoord)
522 onClicked: (position) => {
523 position = Qt.point(position.x, position.y)
524 var clickCoord = _root.toCoordinate(position, false /* clipToViewPort */)
525 // For some strange reason using mainWindow in mapToItem doesn't work, so we use globals.parent instead which also gets us mainWindow
526 position = mapToItem(globals.parent, position)
527 var dropPanel = roiEditDropPanelComponent.createObject(mainWindow, { clickRect: Qt.rect(position.x, position.y, 0, 0) })
532 sourceItem: MissionItemIndexLabel {
535 label: qsTr("ROI here", "Make this a Region Of Interest")
538 //-- Visibilty controlled by actual state
539 function show(coord) {
540 roiLocationItem.coordinate = coord
544 // Orbit telemetry visuals
545 QGCMapCircleVisuals {
546 id: orbitTelemetryCircle
548 mapCircle: _activeVehicle ? _activeVehicle.orbitMapCircle : null
549 visible: _activeVehicle ? _activeVehicle.orbitActive : false
553 id: orbitCenterIndicator
554 anchorPoint.x: sourceItem.anchorPointX
555 anchorPoint.y: sourceItem.anchorPointY
556 coordinate: _activeVehicle ? _activeVehicle.orbitMapCircle.center : QtPositioning.coordinate()
557 visible: orbitTelemetryCircle.visible && !gotoLocationItem.visible
559 sourceItem: MissionItemIndexLabel {
562 label: qsTr("Orbit", "Orbit waypoint")
566 QGCPopupDialogFactory {
567 id: roiEditPositionDialogFactory
569 dialogComponent: roiEditPositionDialogComponent
573 id: roiEditPositionDialogComponent
576 title: qsTr("Edit ROI Position")
577 coordinate: roiLocationItem.coordinate
578 onCoordinateChanged: {
579 roiLocationItem.coordinate = coordinate
580 _activeVehicle.guidedModeROI(coordinate)
586 id: roiEditDropPanelComponent
591 sourceComponent: Component {
593 spacing: ScreenTools.defaultFontPixelWidth / 2
596 Layout.fillWidth: true
597 text: qsTr("Cancel ROI")
599 _activeVehicle.stopGuidedModeROI()
600 roiEditDropPanel.close()
605 Layout.fillWidth: true
606 text: qsTr("Edit Position")
608 roiEditPositionDialogFactory.open()
609 roiEditDropPanel.close()
618 id: mapClickDropPanelComponent
621 id: mapClickDropPanel
623 property var mapClickCoord
625 sourceComponent: Component {
627 spacing: ScreenTools.defaultFontPixelWidth / 2
630 Layout.fillWidth: true
631 text: qsTr("Go to location")
632 visible: globals.guidedControllerFlyView.showGotoLocation
634 mapClickDropPanel.close()
635 gotoLocationItem.show(mapClickCoord)
637 if ((_activeVehicle.flightMode == _activeVehicle.gotoFlightMode) && !_flyViewSettings.goToLocationRequiresConfirmInGuided.value) {
638 if (globals.guidedControllerFlyView.executeAction(globals.guidedControllerFlyView.actionGoto, mapClickCoord)) {
639 gotoLocationItem.actionConfirmed() // Still need to call this to commit the new coordinate and radius
641 gotoLocationItem.actionCancelled()
644 globals.guidedControllerFlyView.confirmAction(globals.guidedControllerFlyView.actionGoto, mapClickCoord, gotoLocationItem)
650 Layout.fillWidth: true
651 text: qsTr("Orbit at location")
652 visible: globals.guidedControllerFlyView.showOrbit
654 mapClickDropPanel.close()
655 orbitMapCircle.show(mapClickCoord)
656 globals.guidedControllerFlyView.confirmAction(globals.guidedControllerFlyView.actionOrbit, mapClickCoord, orbitMapCircle)
661 Layout.fillWidth: true
662 text: qsTr("ROI at location")
663 visible: globals.guidedControllerFlyView.showROI
665 mapClickDropPanel.close()
666 globals.guidedControllerFlyView.executeAction(globals.guidedControllerFlyView.actionROI, mapClickCoord, 0, false)
671 Layout.fillWidth: true
672 text: qsTr("Set home here")
673 visible: globals.guidedControllerFlyView.showSetHome
675 mapClickDropPanel.close()
676 globals.guidedControllerFlyView.confirmAction(globals.guidedControllerFlyView.actionSetHome, mapClickCoord)
681 Layout.fillWidth: true
682 text: qsTr("Set Estimator Origin")
683 visible: globals.guidedControllerFlyView.showSetEstimatorOrigin
685 mapClickDropPanel.close()
686 globals.guidedControllerFlyView.confirmAction(globals.guidedControllerFlyView.actionSetEstimatorOrigin, mapClickCoord)
691 Layout.fillWidth: true
692 text: qsTr("Set Heading")
693 visible: globals.guidedControllerFlyView.showChangeHeading
695 mapClickDropPanel.close()
696 globals.guidedControllerFlyView.confirmAction(globals.guidedControllerFlyView.actionChangeHeading, mapClickCoord)
702 QGCLabel { text: qsTr("Lat: %1").arg(mapClickCoord.latitude.toFixed(6)) }
703 QGCLabel { text: qsTr("Lon: %1").arg(mapClickCoord.longitude.toFixed(6)) }
710 onMapClicked: (position) => {
711 if (!globals.guidedControllerFlyView.guidedUIVisible &&
712 (globals.guidedControllerFlyView.showGotoLocation || globals.guidedControllerFlyView.showOrbit ||
713 globals.guidedControllerFlyView.showROI || globals.guidedControllerFlyView.showSetHome ||
714 globals.guidedControllerFlyView.showSetEstimatorOrigin)) {
716 position = Qt.point(position.x, position.y)
717 var clickCoord = _root.toCoordinate(position, false /* clipToViewPort */)
718 // For some strange reason using mainWindow in mapToItem doesn't work, so we use globals.parent instead which also gets us mainWindow
719 position = _root.mapToItem(globals.parent, position)
720 var dropPanel = mapClickDropPanelComponent.createObject(mainWindow, { mapClickCoord: clickCoord, clickRect: Qt.rect(position.x, position.y, 0, 0) })
727 anchors.margins: _toolsMargin
728 anchors.left: parent.left
729 anchors.top: parent.top
731 visible: !ScreenTools.isTinyScreen && QGroundControl.corePlugin.options.flyView.showMapScale && mapControl.pipState.state === mapControl.pipState.windowState