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 _disableVehicleTracking: false
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: _disableVehicleTracking = true
75 onMapPanStop: panRecenterTimer.restart()
77 function pointInRect(point, rect) {
78 return point.x > rect.x &&
79 point.x < rect.x + rect.width &&
81 point.y < rect.y + rect.height;
84 property real _animatedLatitudeStart
85 property real _animatedLatitudeStop
86 property real _animatedLongitudeStart
87 property real _animatedLongitudeStop
88 property real animatedLatitude
89 property real animatedLongitude
91 onAnimatedLatitudeChanged: _root.center = QtPositioning.coordinate(animatedLatitude, animatedLongitude)
92 onAnimatedLongitudeChanged: _root.center = QtPositioning.coordinate(animatedLatitude, animatedLongitude)
94 NumberAnimation on animatedLatitude { id: animateLat; from: _animatedLatitudeStart; to: _animatedLatitudeStop; duration: 1000 }
95 NumberAnimation on animatedLongitude { id: animateLong; from: _animatedLongitudeStart; to: _animatedLongitudeStop; duration: 1000 }
97 function animatedMapRecenter(fromCoord, toCoord) {
98 _animatedLatitudeStart = fromCoord.latitude
99 _animatedLongitudeStart = fromCoord.longitude
100 _animatedLatitudeStop = toCoord.latitude
101 _animatedLongitudeStop = toCoord.longitude
106 // returns the rectangle formed by the four center insets
107 // used for checking if vehicle is under ui, and as a target for recentering the view
108 function _insetCenterRect() {
109 return Qt.rect(toolInsets.leftEdgeCenterInset,
110 toolInsets.topEdgeCenterInset,
111 _root.width - toolInsets.leftEdgeCenterInset - toolInsets.rightEdgeCenterInset,
112 _root.height - toolInsets.topEdgeCenterInset - toolInsets.bottomEdgeCenterInset)
115 // returns the four rectangles formed by the 8 corner insets
116 // used for detecting if the vehicle has flown under the instrument panel, virtual joystick etc
117 function _insetCornerRects() {
119 "topleft": Qt.rect(0,0,
120 toolInsets.leftEdgeTopInset,
121 toolInsets.topEdgeLeftInset),
122 "topright": Qt.rect(_root.width-toolInsets.rightEdgeTopInset,0,
123 toolInsets.rightEdgeTopInset,
124 toolInsets.topEdgeRightInset),
125 "bottomleft": Qt.rect(0,_root.height-toolInsets.bottomEdgeLeftInset,
126 toolInsets.leftEdgeBottomInset,
127 toolInsets.bottomEdgeLeftInset),
128 "bottomright": Qt.rect(_root.width-toolInsets.rightEdgeBottomInset,_root.height-toolInsets.bottomEdgeRightInset,
129 toolInsets.rightEdgeBottomInset,
130 toolInsets.bottomEdgeRightInset)}
134 function recenterNeeded() {
135 var vehiclePoint = _root.fromCoordinate(_activeVehicleCoordinate, false /* clipToViewport */)
136 var centerRect = _insetCenterRect()
137 //return !pointInRect(vehiclePoint,insetRect)
139 // If we are outside the center inset rectangle, recenter
140 if(!pointInRect(vehiclePoint, centerRect)){
144 // if we are inside the center inset rectangle
145 // then additionally check if we are underneath one of the corner inset rectangles
146 var cornerRects = _insetCornerRects()
147 if(pointInRect(vehiclePoint, cornerRects["topleft"])){
149 } else if(pointInRect(vehiclePoint, cornerRects["topright"])){
151 } else if(pointInRect(vehiclePoint, cornerRects["bottomleft"])){
153 } else if(pointInRect(vehiclePoint, cornerRects["bottomright"])){
157 // if we are inside the center inset rectangle, and not under any corner elements
161 function updateMapToVehiclePosition() {
162 if (animateLat.running || animateLong.running) {
165 // We let FlightMap handle first vehicle position
166 if (!_keepMapCenteredOnVehicle && firstVehiclePositionReceived && _activeVehicleCoordinate.isValid && !_disableVehicleTracking) {
167 if (_keepVehicleCentered) {
168 _root.center = _activeVehicleCoordinate
170 if (firstVehiclePositionReceived && recenterNeeded()) {
171 // Move the map such that the vehicle is centered within the inset area
172 var vehiclePoint = _root.fromCoordinate(_activeVehicleCoordinate, false /* clipToViewport */)
173 var centerInsetRect = _insetCenterRect()
174 var centerInsetPoint = Qt.point(centerInsetRect.x + centerInsetRect.width / 2, centerInsetRect.y + centerInsetRect.height / 2)
175 var centerOffset = Qt.point((_root.width / 2) - centerInsetPoint.x, (_root.height / 2) - centerInsetPoint.y)
176 var vehicleOffsetPoint = Qt.point(vehiclePoint.x + centerOffset.x, vehiclePoint.y + centerOffset.y)
177 var vehicleOffsetCoord = _root.toCoordinate(vehicleOffsetPoint, false /* clipToViewport */)
178 animatedMapRecenter(_root.center, vehicleOffsetCoord)
184 on_ActiveVehicleCoordinateChanged: {
185 if (_keepMapCenteredOnVehicle && _activeVehicleCoordinate.isValid && !_disableVehicleTracking) {
186 _root.center = _activeVehicleCoordinate
192 pipView: _root.pipView
193 isDark: _isFullWindowItemDark
201 _disableVehicleTracking = false
202 updateMapToVehiclePosition()
210 onTriggered: updateMapToVehiclePosition()
213 QGCMapPalette { id: mapPal; lightColors: isSatelliteMap }
216 target: _missionController
217 ignoreUnknownSignals: true
218 function onNewItemsFromVehicle() {
219 var visualItems = _missionController.visualItems
220 if (visualItems && visualItems.count !== 1) {
221 mapFitFunctions.fitMapViewportToMissionItems()
222 firstVehiclePositionReceived = true
228 id: mapFitFunctions // The name for this id cannot be changed without breaking references outside of this code. Beware!
230 usePlannedHomePosition: false
231 planMasterController: _planMasterController
234 ObstacleDistanceOverlayMap {
239 // Add trajectory lines to the map
241 id: trajectoryPolyline
244 z: QGroundControl.zOrderTrajectoryLines
248 target: QGroundControl.multiVehicleManager
249 function onActiveVehicleChanged(activeVehicle) {
250 trajectoryPolyline.path = _activeVehicle ? _activeVehicle.trajectoryPoints.list() : []
255 target: _activeVehicle ? _activeVehicle.trajectoryPoints : null
256 function onPointAdded(coordinate) { trajectoryPolyline.addCoordinate(coordinate) }
257 function onUpdateLastPoint(coordinate) { trajectoryPolyline.replaceCoordinate(trajectoryPolyline.pathLength() - 1, coordinate) }
258 function onPointsCleared() { trajectoryPolyline.path = [] }
262 // Add the vehicles to the map
264 model: QGroundControl.multiVehicleManager.vehicles
265 delegate: VehicleMapItem {
267 coordinate: object.coordinate
269 size: pipMode ? ScreenTools.defaultFontPixelHeight : ScreenTools.defaultFontPixelHeight * 3
270 z: QGroundControl.zOrderVehicles
273 // Add distance sensor view
275 model: QGroundControl.multiVehicleManager.vehicles
276 delegate: ProximityRadarMapView {
278 coordinate: object.coordinate
280 z: QGroundControl.zOrderVehicles
283 // Add ADSB vehicles to the map
285 model: QGroundControl.adsbVehicleManager.adsbVehicles
286 delegate: VehicleMapItem {
287 coordinate: object.coordinate
288 altitude: object.altitude
289 callsign: object.callsign
290 heading: object.heading
293 size: pipMode ? ScreenTools.defaultFontPixelHeight : ScreenTools.defaultFontPixelHeight * 2.5
294 z: QGroundControl.zOrderVehicles
298 // Add the items associated with each vehicles flight plan to the map
300 model: QGroundControl.multiVehicleManager.vehicles
304 largeMapView: !pipMode
305 planMasterController: masterController
308 property var _vehicle: object
310 PlanMasterController {
312 Component.onCompleted: startStaticActiveVehicle(object)
317 // Allow custom builds to add map items
320 largeMapView: !pipMode
325 myGeoFenceController: _geoFenceController
328 homePosition: _activeVehicle && _activeVehicle.homePosition.isValid ? _activeVehicle.homePosition : QtPositioning.coordinate()
331 // Rally points on map
333 model: _rallyPointController.points
335 delegate: MapQuickItem {
337 anchorPoint.x: sourceItem.anchorPointX
338 anchorPoint.y: sourceItem.anchorPointY
339 coordinate: object.coordinate
340 z: QGroundControl.zOrderMapItems
342 sourceItem: MissionItemIndexLabel {
344 label: qsTr("R", "rally point map item label")
349 // Camera trigger points
351 model: _activeVehicle ? _activeVehicle.cameraTriggerPoints : 0
353 delegate: CameraTriggerIndicator {
354 coordinate: object.coordinate
355 z: QGroundControl.zOrderTopMost
359 // GoTo Location forward flight circle visuals
360 QGCMapCircleVisuals {
361 id: fwdFlightGotoMapCircle
363 mapCircle: _fwdFlightGotoMapCircle
364 radiusLabelVisible: true
365 visible: gotoLocationItem.visible && _activeVehicle &&
366 _activeVehicle.inFwdFlight &&
367 !_activeVehicle.orbitActive
369 property alias coordinate: _fwdFlightGotoMapCircle.center
370 property alias radius: _fwdFlightGotoMapCircle.radius
371 property alias clockwiseRotation: _fwdFlightGotoMapCircle.clockwiseRotation
373 Component.onCompleted: {
374 // Only allow editing the radius, not the position
375 centerDragHandleVisible = false
377 globals.guidedControllerFlyView.fwdFlightGotoMapCircle = this
381 target: _fwdFlightGotoMapCircle
383 value: gotoLocationItem.coordinate
386 function startLoiterRadiusEdit() {
387 _fwdFlightGotoMapCircle.interactive = true
390 // Called when loiter edit is confirmed
391 function actionConfirmed() {
392 _fwdFlightGotoMapCircle.interactive = false
393 _fwdFlightGotoMapCircle._commitRadius()
396 // Called when loiter edit is cancelled
397 function actionCancelled() {
398 _fwdFlightGotoMapCircle.interactive = false
399 _fwdFlightGotoMapCircle._restoreRadius()
403 id: _fwdFlightGotoMapCircle
406 clockwiseRotation: true
408 property real _defaultLoiterRadius: _flyViewSettings.forwardFlightGoToLocationLoiterRad.value
409 property real _committedRadius;
412 radius.rawValue = _defaultLoiterRadius
413 // Don't commit the radius in case this operation is undone
416 Component.onCompleted: {
417 radius.rawValue = _defaultLoiterRadius
421 function _commitRadius() {
422 _committedRadius = radius.rawValue
425 function _restoreRadius() {
426 radius.rawValue = _committedRadius
431 // GoTo Location visuals
435 z: QGroundControl.zOrderMapItems
436 anchorPoint.x: sourceItem.anchorPointX
437 anchorPoint.y: sourceItem.anchorPointY
438 sourceItem: MissionItemIndexLabel {
441 label: qsTr("Go here", "Go to location waypoint")
444 property bool inGotoFlightMode: _activeVehicle ? _activeVehicle.flightMode === _activeVehicle.gotoFlightMode : false
446 property var _committedCoordinate: null
448 onInGotoFlightModeChanged: {
449 if (!inGotoFlightMode && gotoLocationItem.visible) {
450 // Hide goto indicator when vehicle falls out of guided mode
455 function show(coord) {
456 gotoLocationItem.coordinate = coord
457 gotoLocationItem.visible = true
461 gotoLocationItem.visible = false
464 function actionConfirmed() {
467 // Commit the new radius which possibly changed
468 fwdFlightGotoMapCircle.actionConfirmed()
470 // We leave the indicator visible. The handling for onInGuidedModeChanged will hide it.
473 function actionCancelled() {
476 // Also restore the loiter radius
477 fwdFlightGotoMapCircle.actionCancelled()
480 function _commitCoordinate() {
482 _committedCoordinate = QtPositioning.coordinate(
488 function _restoreCoordinate() {
489 if (_committedCoordinate) {
490 coordinate = _committedCoordinate
497 // Orbit editing visuals
498 QGCMapCircleVisuals {
501 mapCircle: _mapCircle
504 property alias center: _mapCircle.center
505 property alias clockwiseRotation: _mapCircle.clockwiseRotation
506 readonly property real defaultRadius: 30
509 target: QGroundControl.multiVehicleManager
510 function onActiveVehicleChanged(activeVehicle) {
511 if (!activeVehicle) {
512 orbitMapCircle.visible = false
517 function show(coord) {
518 _mapCircle.radius.rawValue = defaultRadius
519 orbitMapCircle.center = coord
520 orbitMapCircle.visible = true
524 orbitMapCircle.visible = false
527 function actionConfirmed() {
528 // Live orbit status is handled by telemetry so we hide here and telemetry will show again.
532 function actionCancelled() {
537 return _mapCircle.radius.rawValue
540 Component.onCompleted: globals.guidedControllerFlyView.orbitMapCircle = orbitMapCircle
547 clockwiseRotation: true
551 // ROI Location visuals
554 visible: _activeVehicle && _activeVehicle.isROIEnabled
555 z: QGroundControl.zOrderMapItems
556 anchorPoint.x: sourceItem.anchorPointX
557 anchorPoint.y: sourceItem.anchorPointY
560 target: _activeVehicle
561 function onRoiCoordChanged(centerCoord) {
562 roiLocationItem.show(centerCoord)
568 onClicked: (position) => {
569 position = Qt.point(position.x, position.y)
570 var clickCoord = _root.toCoordinate(position, false /* clipToViewPort */)
571 // For some strange reason using mainWindow in mapToItem doesn't work, so we use globals.parent instead which also gets us mainWindow
572 position = mapToItem(globals.parent, position)
573 var dropPanel = roiEditDropPanelComponent.createObject(mainWindow, { clickRect: Qt.rect(position.x, position.y, 0, 0) })
578 sourceItem: MissionItemIndexLabel {
581 label: qsTr("ROI here", "Make this a Region Of Interest")
584 //-- Visibilty controlled by actual state
585 function show(coord) {
586 roiLocationItem.coordinate = coord
590 // Orbit telemetry visuals
591 QGCMapCircleVisuals {
592 id: orbitTelemetryCircle
594 mapCircle: _activeVehicle ? _activeVehicle.orbitMapCircle : null
595 visible: _activeVehicle ? _activeVehicle.orbitActive : false
599 id: orbitCenterIndicator
600 anchorPoint.x: sourceItem.anchorPointX
601 anchorPoint.y: sourceItem.anchorPointY
602 coordinate: _activeVehicle ? _activeVehicle.orbitMapCircle.center : QtPositioning.coordinate()
603 visible: orbitTelemetryCircle.visible && !gotoLocationItem.visible
605 sourceItem: MissionItemIndexLabel {
608 label: qsTr("Orbit", "Orbit waypoint")
612 QGCPopupDialogFactory {
613 id: roiEditPositionDialogFactory
615 dialogComponent: roiEditPositionDialogComponent
619 id: roiEditPositionDialogComponent
622 title: qsTr("Edit ROI Position")
623 coordinate: roiLocationItem.coordinate
624 onCoordinateChanged: {
625 roiLocationItem.coordinate = coordinate
626 _activeVehicle.guidedModeROI(coordinate)
632 id: roiEditDropPanelComponent
637 sourceComponent: Component {
639 spacing: ScreenTools.defaultFontPixelWidth / 2
642 Layout.fillWidth: true
643 text: qsTr("Cancel ROI")
645 _activeVehicle.stopGuidedModeROI()
646 roiEditDropPanel.close()
651 Layout.fillWidth: true
652 text: qsTr("Edit Position")
654 roiEditPositionDialogFactory.open()
655 roiEditDropPanel.close()
664 id: mapClickDropPanelComponent
667 id: mapClickDropPanel
669 property var mapClickCoord
671 sourceComponent: Component {
673 spacing: ScreenTools.defaultFontPixelWidth / 2
676 Layout.fillWidth: true
677 text: qsTr("Go to location")
678 visible: globals.guidedControllerFlyView.showGotoLocation
680 mapClickDropPanel.close()
681 gotoLocationItem.show(mapClickCoord)
683 if ((_activeVehicle.flightMode == _activeVehicle.gotoFlightMode) && !_flyViewSettings.goToLocationRequiresConfirmInGuided.value) {
684 if (globals.guidedControllerFlyView.executeAction(globals.guidedControllerFlyView.actionGoto, mapClickCoord)) {
685 gotoLocationItem.actionConfirmed() // Still need to call this to commit the new coordinate and radius
687 gotoLocationItem.actionCancelled()
690 globals.guidedControllerFlyView.confirmAction(globals.guidedControllerFlyView.actionGoto, mapClickCoord, gotoLocationItem)
696 Layout.fillWidth: true
697 text: qsTr("Orbit at location")
698 visible: globals.guidedControllerFlyView.showOrbit
700 mapClickDropPanel.close()
701 orbitMapCircle.show(mapClickCoord)
702 globals.guidedControllerFlyView.confirmAction(globals.guidedControllerFlyView.actionOrbit, mapClickCoord, orbitMapCircle)
707 Layout.fillWidth: true
708 text: qsTr("ROI at location")
709 visible: globals.guidedControllerFlyView.showROI
711 mapClickDropPanel.close()
712 globals.guidedControllerFlyView.executeAction(globals.guidedControllerFlyView.actionROI, mapClickCoord, 0, false)
717 Layout.fillWidth: true
718 text: qsTr("Set home here")
719 visible: globals.guidedControllerFlyView.showSetHome
721 mapClickDropPanel.close()
722 globals.guidedControllerFlyView.confirmAction(globals.guidedControllerFlyView.actionSetHome, mapClickCoord)
727 Layout.fillWidth: true
728 text: qsTr("Set Estimator Origin")
729 visible: globals.guidedControllerFlyView.showSetEstimatorOrigin
731 mapClickDropPanel.close()
732 globals.guidedControllerFlyView.confirmAction(globals.guidedControllerFlyView.actionSetEstimatorOrigin, mapClickCoord)
737 Layout.fillWidth: true
738 text: qsTr("Set Heading")
739 visible: globals.guidedControllerFlyView.showChangeHeading
741 mapClickDropPanel.close()
742 globals.guidedControllerFlyView.confirmAction(globals.guidedControllerFlyView.actionChangeHeading, mapClickCoord)
748 QGCLabel { text: qsTr("Lat: %1").arg(mapClickCoord.latitude.toFixed(6)) }
749 QGCLabel { text: qsTr("Lon: %1").arg(mapClickCoord.longitude.toFixed(6)) }
756 onMapClicked: (position) => {
757 if (!globals.guidedControllerFlyView.guidedUIVisible &&
758 (globals.guidedControllerFlyView.showGotoLocation || globals.guidedControllerFlyView.showOrbit ||
759 globals.guidedControllerFlyView.showROI || globals.guidedControllerFlyView.showSetHome ||
760 globals.guidedControllerFlyView.showSetEstimatorOrigin)) {
762 position = Qt.point(position.x, position.y)
763 var clickCoord = _root.toCoordinate(position, false /* clipToViewPort */)
764 // For some strange reason using mainWindow in mapToItem doesn't work, so we use globals.parent instead which also gets us mainWindow
765 position = _root.mapToItem(globals.parent, position)
766 var dropPanel = mapClickDropPanelComponent.createObject(mainWindow, { mapClickCoord: clickCoord, clickRect: Qt.rect(position.x, position.y, 0, 0) })
773 anchors.margins: _toolsMargin
774 anchors.left: parent.left
775 anchors.top: parent.top
777 visible: !ScreenTools.isTinyScreen && QGroundControl.corePlugin.options.flyView.showMapScale && mapControl.pipState.state === mapControl.pipState.windowState