9import QGroundControl.Controls
10import QGroundControl.FlyView
11import QGroundControl.FlightMap
15 allowGCSLocationCenter: true
16 allowVehicleLocationCenter: !_keepVehicleCentered
18 zoomLevel: QGroundControl.flightMapZoom
19 center: QGroundControl.flightMapPosition
22 property Item pipState: _pipState
23 property var rightPanelWidth
24 property var planMasterController
25 property bool pipMode: false // true: map is shown in a small pip mode
26 property var toolInsets // Insets for the center viewport area
28 property var _activeVehicle: QGroundControl.multiVehicleManager.activeVehicle
29 property var _planMasterController: planMasterController
30 property var _geoFenceController: planMasterController.geoFenceController
31 property var _rallyPointController: planMasterController.rallyPointController
32 property var _activeVehicleCoordinate: _activeVehicle ? _activeVehicle.coordinate : QtPositioning.coordinate()
33 property real _toolButtonTopMargin: parent.height - mainWindow.height + (ScreenTools.defaultFontPixelHeight / 2)
34 property real _toolsMargin: ScreenTools.defaultFontPixelWidth * 0.75
35 property var _flyViewSettings: QGroundControl.settingsManager.flyViewSettings
36 property bool _keepMapCenteredOnVehicle: _flyViewSettings.keepMapCenteredOnVehicle.rawValue
38 property bool _disableVehicleTracking: false
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: _disableVehicleTracking = true
74 onMapPanStop: panRecenterTimer.restart()
76 function pointInRect(point, rect) {
77 return point.x > rect.x &&
78 point.x < rect.x + rect.width &&
80 point.y < rect.y + rect.height;
83 property real _animatedLatitudeStart
84 property real _animatedLatitudeStop
85 property real _animatedLongitudeStart
86 property real _animatedLongitudeStop
87 property real animatedLatitude
88 property real animatedLongitude
90 onAnimatedLatitudeChanged: _root.center = QtPositioning.coordinate(animatedLatitude, animatedLongitude)
91 onAnimatedLongitudeChanged: _root.center = QtPositioning.coordinate(animatedLatitude, animatedLongitude)
93 NumberAnimation on animatedLatitude { id: animateLat; from: _animatedLatitudeStart; to: _animatedLatitudeStop; duration: 1000 }
94 NumberAnimation on animatedLongitude { id: animateLong; from: _animatedLongitudeStart; to: _animatedLongitudeStop; duration: 1000 }
96 function animatedMapRecenter(fromCoord, toCoord) {
97 _animatedLatitudeStart = fromCoord.latitude
98 _animatedLongitudeStart = fromCoord.longitude
99 _animatedLatitudeStop = toCoord.latitude
100 _animatedLongitudeStop = toCoord.longitude
105 // returns the rectangle formed by the four center insets
106 // used for checking if vehicle is under ui, and as a target for recentering the view
107 function _insetCenterRect() {
108 return Qt.rect(toolInsets.leftEdgeCenterInset,
109 toolInsets.topEdgeCenterInset,
110 _root.width - toolInsets.leftEdgeCenterInset - toolInsets.rightEdgeCenterInset,
111 _root.height - toolInsets.topEdgeCenterInset - toolInsets.bottomEdgeCenterInset)
114 // returns the four rectangles formed by the 8 corner insets
115 // used for detecting if the vehicle has flown under the instrument panel, virtual joystick etc
116 function _insetCornerRects() {
118 "topleft": Qt.rect(0,0,
119 toolInsets.leftEdgeTopInset,
120 toolInsets.topEdgeLeftInset),
121 "topright": Qt.rect(_root.width-toolInsets.rightEdgeTopInset,0,
122 toolInsets.rightEdgeTopInset,
123 toolInsets.topEdgeRightInset),
124 "bottomleft": Qt.rect(0,_root.height-toolInsets.bottomEdgeLeftInset,
125 toolInsets.leftEdgeBottomInset,
126 toolInsets.bottomEdgeLeftInset),
127 "bottomright": Qt.rect(_root.width-toolInsets.rightEdgeBottomInset,_root.height-toolInsets.bottomEdgeRightInset,
128 toolInsets.rightEdgeBottomInset,
129 toolInsets.bottomEdgeRightInset)}
133 function recenterNeeded() {
134 var vehiclePoint = _root.fromCoordinate(_activeVehicleCoordinate, false /* clipToViewport */)
135 var centerRect = _insetCenterRect()
136 //return !pointInRect(vehiclePoint,insetRect)
138 // If we are outside the center inset rectangle, recenter
139 if(!pointInRect(vehiclePoint, centerRect)){
143 // if we are inside the center inset rectangle
144 // then additionally check if we are underneath one of the corner inset rectangles
145 var cornerRects = _insetCornerRects()
146 if(pointInRect(vehiclePoint, cornerRects["topleft"])){
148 } else if(pointInRect(vehiclePoint, cornerRects["topright"])){
150 } else if(pointInRect(vehiclePoint, cornerRects["bottomleft"])){
152 } else if(pointInRect(vehiclePoint, cornerRects["bottomright"])){
156 // if we are inside the center inset rectangle, and not under any corner elements
160 function updateMapToVehiclePosition() {
161 if (animateLat.running || animateLong.running) {
164 // We let FlightMap handle first vehicle position
165 if (!_keepMapCenteredOnVehicle && firstVehiclePositionReceived && _activeVehicleCoordinate.isValid && !_disableVehicleTracking) {
166 if (_keepVehicleCentered) {
167 _root.center = _activeVehicleCoordinate
169 if (firstVehiclePositionReceived && recenterNeeded()) {
170 // Move the map such that the vehicle is centered within the inset area
171 var vehiclePoint = _root.fromCoordinate(_activeVehicleCoordinate, false /* clipToViewport */)
172 var centerInsetRect = _insetCenterRect()
173 var centerInsetPoint = Qt.point(centerInsetRect.x + centerInsetRect.width / 2, centerInsetRect.y + centerInsetRect.height / 2)
174 var centerOffset = Qt.point((_root.width / 2) - centerInsetPoint.x, (_root.height / 2) - centerInsetPoint.y)
175 var vehicleOffsetPoint = Qt.point(vehiclePoint.x + centerOffset.x, vehiclePoint.y + centerOffset.y)
176 var vehicleOffsetCoord = _root.toCoordinate(vehicleOffsetPoint, false /* clipToViewport */)
177 animatedMapRecenter(_root.center, vehicleOffsetCoord)
183 on_ActiveVehicleCoordinateChanged: {
184 if (_keepMapCenteredOnVehicle && _activeVehicleCoordinate.isValid && !_disableVehicleTracking) {
185 _root.center = _activeVehicleCoordinate
191 pipView: _root.pipView
192 isDark: _isFullWindowItemDark
200 _disableVehicleTracking = false
201 updateMapToVehiclePosition()
209 onTriggered: updateMapToVehiclePosition()
212 QGCMapPalette { id: mapPal; lightColors: isSatelliteMap }
215 target: _missionController
216 ignoreUnknownSignals: true
217 function onNewItemsFromVehicle() {
218 var visualItems = _missionController.visualItems
219 if (visualItems && visualItems.count !== 1) {
220 mapFitFunctions.fitMapViewportToMissionItems()
221 firstVehiclePositionReceived = true
227 id: mapFitFunctions // The name for this id cannot be changed without breaking references outside of this code. Beware!
229 usePlannedHomePosition: false
230 planMasterController: _planMasterController
233 ObstacleDistanceOverlayMap {
238 // Add trajectory lines to the map
240 id: trajectoryPolyline
243 z: QGroundControl.zOrderTrajectoryLines
247 target: QGroundControl.multiVehicleManager
248 function onActiveVehicleChanged(activeVehicle) {
249 trajectoryPolyline.path = _activeVehicle ? _activeVehicle.trajectoryPoints.list() : []
254 target: _activeVehicle ? _activeVehicle.trajectoryPoints : null
255 function onPointAdded(coordinate) { trajectoryPolyline.addCoordinate(coordinate) }
256 function onUpdateLastPoint(coordinate) { trajectoryPolyline.replaceCoordinate(trajectoryPolyline.pathLength() - 1, coordinate) }
257 function onPointsCleared() { trajectoryPolyline.path = [] }
261 // Add the vehicles to the map
263 model: QGroundControl.multiVehicleManager.vehicles
264 delegate: VehicleMapItem {
266 coordinate: object.coordinate
268 size: pipMode ? ScreenTools.defaultFontPixelHeight : ScreenTools.defaultFontPixelHeight * 3
269 z: QGroundControl.zOrderVehicles
272 // Add distance sensor view
274 model: QGroundControl.multiVehicleManager.vehicles
275 delegate: ProximityRadarMapView {
277 coordinate: object.coordinate
279 z: QGroundControl.zOrderVehicles
282 // Add ADSB vehicles to the map
284 model: QGroundControl.adsbVehicleManager.adsbVehicles
285 delegate: VehicleMapItem {
286 coordinate: object.coordinate
287 altitude: object.altitude
288 callsign: object.callsign
289 heading: object.heading
292 size: pipMode ? ScreenTools.defaultFontPixelHeight : ScreenTools.defaultFontPixelHeight * 2.5
293 z: QGroundControl.zOrderVehicles
297 // Add the items associated with each vehicles flight plan to the map
299 model: QGroundControl.multiVehicleManager.vehicles
303 largeMapView: !pipMode
304 planMasterController: masterController
307 property var _vehicle: object
309 PlanMasterController {
311 Component.onCompleted: startStaticActiveVehicle(object)
316 // Allow custom builds to add map items
319 largeMapView: !pipMode
324 myGeoFenceController: _geoFenceController
327 homePosition: _activeVehicle && _activeVehicle.homePosition.isValid ? _activeVehicle.homePosition : QtPositioning.coordinate()
330 // Rally points on map
332 model: _rallyPointController.points
334 delegate: MapQuickItem {
336 anchorPoint.x: sourceItem.anchorPointX
337 anchorPoint.y: sourceItem.anchorPointY
338 coordinate: object.coordinate
339 z: QGroundControl.zOrderMapItems
341 sourceItem: MissionItemIndexLabel {
343 label: qsTr("R", "rally point map item label")
348 // Camera trigger points
350 model: _activeVehicle ? _activeVehicle.cameraTriggerPoints : 0
352 delegate: CameraTriggerIndicator {
353 coordinate: object.coordinate
354 z: QGroundControl.zOrderTopMost
358 // GoTo Location forward flight circle visuals
359 QGCMapCircleVisuals {
360 id: fwdFlightGotoMapCircle
362 mapCircle: _fwdFlightGotoMapCircle
363 radiusLabelVisible: true
364 visible: gotoLocationItem.visible && _activeVehicle &&
365 _activeVehicle.inFwdFlight &&
366 !_activeVehicle.orbitActive
368 property alias coordinate: _fwdFlightGotoMapCircle.center
369 property alias radius: _fwdFlightGotoMapCircle.radius
370 property alias clockwiseRotation: _fwdFlightGotoMapCircle.clockwiseRotation
372 Component.onCompleted: {
373 // Only allow editing the radius, not the position
374 centerDragHandleVisible = false
376 globals.guidedControllerFlyView.fwdFlightGotoMapCircle = this
380 target: _fwdFlightGotoMapCircle
382 value: gotoLocationItem.coordinate
385 function startLoiterRadiusEdit() {
386 _fwdFlightGotoMapCircle.interactive = true
389 // Called when loiter edit is confirmed
390 function actionConfirmed() {
391 _fwdFlightGotoMapCircle.interactive = false
392 _fwdFlightGotoMapCircle._commitRadius()
395 // Called when loiter edit is cancelled
396 function actionCancelled() {
397 _fwdFlightGotoMapCircle.interactive = false
398 _fwdFlightGotoMapCircle._restoreRadius()
402 id: _fwdFlightGotoMapCircle
405 clockwiseRotation: true
407 property real _defaultLoiterRadius: _flyViewSettings.forwardFlightGoToLocationLoiterRad.value
408 property real _committedRadius;
411 radius.rawValue = _defaultLoiterRadius
412 // Don't commit the radius in case this operation is undone
415 Component.onCompleted: {
416 radius.rawValue = _defaultLoiterRadius
420 function _commitRadius() {
421 _committedRadius = radius.rawValue
424 function _restoreRadius() {
425 radius.rawValue = _committedRadius
430 // GoTo Location visuals
434 z: QGroundControl.zOrderMapItems
435 anchorPoint.x: sourceItem.anchorPointX
436 anchorPoint.y: sourceItem.anchorPointY
437 sourceItem: MissionItemIndexLabel {
440 label: qsTr("Go here", "Go to location waypoint")
443 property bool inGotoFlightMode: _activeVehicle ? _activeVehicle.flightMode === _activeVehicle.gotoFlightMode : false
445 property var _committedCoordinate: null
447 onInGotoFlightModeChanged: {
448 if (!inGotoFlightMode && gotoLocationItem.visible) {
449 // Hide goto indicator when vehicle falls out of guided mode
454 function show(coord) {
455 gotoLocationItem.coordinate = coord
456 gotoLocationItem.visible = true
460 gotoLocationItem.visible = false
463 function actionConfirmed() {
466 // Commit the new radius which possibly changed
467 fwdFlightGotoMapCircle.actionConfirmed()
469 // We leave the indicator visible. The handling for onInGuidedModeChanged will hide it.
472 function actionCancelled() {
475 // Also restore the loiter radius
476 fwdFlightGotoMapCircle.actionCancelled()
479 function _commitCoordinate() {
481 _committedCoordinate = QtPositioning.coordinate(
487 function _restoreCoordinate() {
488 if (_committedCoordinate) {
489 coordinate = _committedCoordinate
496 // Orbit editing visuals
497 QGCMapCircleVisuals {
500 mapCircle: _mapCircle
503 property alias center: _mapCircle.center
504 property alias clockwiseRotation: _mapCircle.clockwiseRotation
505 readonly property real defaultRadius: 30
508 target: QGroundControl.multiVehicleManager
509 function onActiveVehicleChanged(activeVehicle) {
510 if (!activeVehicle) {
511 orbitMapCircle.visible = false
516 function show(coord) {
517 _mapCircle.radius.rawValue = defaultRadius
518 orbitMapCircle.center = coord
519 orbitMapCircle.visible = true
523 orbitMapCircle.visible = false
526 function actionConfirmed() {
527 // Live orbit status is handled by telemetry so we hide here and telemetry will show again.
531 function actionCancelled() {
536 return _mapCircle.radius.rawValue
539 Component.onCompleted: globals.guidedControllerFlyView.orbitMapCircle = orbitMapCircle
546 clockwiseRotation: true
550 // ROI Location visuals
553 visible: _activeVehicle && _activeVehicle.isROIEnabled
554 z: QGroundControl.zOrderMapItems
555 anchorPoint.x: sourceItem.anchorPointX
556 anchorPoint.y: sourceItem.anchorPointY
559 target: _activeVehicle
560 function onRoiCoordChanged(centerCoord) {
561 roiLocationItem.show(centerCoord)
567 onClicked: (position) => {
568 position = Qt.point(position.x, position.y)
569 var clickCoord = _root.toCoordinate(position, false /* clipToViewPort */)
570 // For some strange reason using mainWindow in mapToItem doesn't work, so we use globals.parent instead which also gets us mainWindow
571 position = mapToItem(globals.parent, position)
572 var dropPanel = roiEditDropPanelComponent.createObject(mainWindow, { clickRect: Qt.rect(position.x, position.y, 0, 0) })
577 sourceItem: MissionItemIndexLabel {
580 label: qsTr("ROI here", "Make this a Region Of Interest")
583 //-- Visibilty controlled by actual state
584 function show(coord) {
585 roiLocationItem.coordinate = coord
589 // Orbit telemetry visuals
590 QGCMapCircleVisuals {
591 id: orbitTelemetryCircle
593 mapCircle: _activeVehicle ? _activeVehicle.orbitMapCircle : null
594 visible: _activeVehicle ? _activeVehicle.orbitActive : false
598 id: orbitCenterIndicator
599 anchorPoint.x: sourceItem.anchorPointX
600 anchorPoint.y: sourceItem.anchorPointY
601 coordinate: _activeVehicle ? _activeVehicle.orbitMapCircle.center : QtPositioning.coordinate()
602 visible: orbitTelemetryCircle.visible && !gotoLocationItem.visible
604 sourceItem: MissionItemIndexLabel {
607 label: qsTr("Orbit", "Orbit waypoint")
611 QGCPopupDialogFactory {
612 id: roiEditPositionDialogFactory
614 dialogComponent: roiEditPositionDialogComponent
618 id: roiEditPositionDialogComponent
621 title: qsTr("Edit ROI Position")
622 coordinate: roiLocationItem.coordinate
623 onCoordinateChanged: {
624 roiLocationItem.coordinate = coordinate
625 _activeVehicle.guidedModeROI(coordinate)
631 id: roiEditDropPanelComponent
636 sourceComponent: Component {
638 spacing: ScreenTools.defaultFontPixelWidth / 2
641 Layout.fillWidth: true
642 text: qsTr("Cancel ROI")
644 _activeVehicle.stopGuidedModeROI()
645 roiEditDropPanel.close()
650 Layout.fillWidth: true
651 text: qsTr("Edit Position")
653 roiEditPositionDialogFactory.open({ showSetPositionFromVehicle: false })
654 roiEditDropPanel.close()
663 id: mapClickDropPanelComponent
666 id: mapClickDropPanel
668 property var mapClickCoord
670 sourceComponent: Component {
672 spacing: ScreenTools.defaultFontPixelWidth / 2
675 Layout.fillWidth: true
676 text: qsTr("Go to location")
677 visible: globals.guidedControllerFlyView.showGotoLocation
679 mapClickDropPanel.close()
680 gotoLocationItem.show(mapClickCoord)
682 if ((_activeVehicle.flightMode == _activeVehicle.gotoFlightMode) && !_flyViewSettings.goToLocationRequiresConfirmInGuided.value) {
683 globals.guidedControllerFlyView.executeAction(globals.guidedControllerFlyView.actionGoto, mapClickCoord, gotoLocationItem)
684 gotoLocationItem.actionConfirmed() // Still need to call this to commit the new coordinate and radius
686 globals.guidedControllerFlyView.confirmAction(globals.guidedControllerFlyView.actionGoto, mapClickCoord, gotoLocationItem)
692 Layout.fillWidth: true
693 text: qsTr("Orbit at location")
694 visible: globals.guidedControllerFlyView.showOrbit
696 mapClickDropPanel.close()
697 orbitMapCircle.show(mapClickCoord)
698 globals.guidedControllerFlyView.confirmAction(globals.guidedControllerFlyView.actionOrbit, mapClickCoord, orbitMapCircle)
703 Layout.fillWidth: true
704 text: qsTr("ROI at location")
705 visible: globals.guidedControllerFlyView.showROI
707 mapClickDropPanel.close()
708 globals.guidedControllerFlyView.executeAction(globals.guidedControllerFlyView.actionROI, mapClickCoord, 0, false)
713 Layout.fillWidth: true
714 text: qsTr("Set home here")
715 visible: globals.guidedControllerFlyView.showSetHome
717 mapClickDropPanel.close()
718 globals.guidedControllerFlyView.confirmAction(globals.guidedControllerFlyView.actionSetHome, mapClickCoord)
723 Layout.fillWidth: true
724 text: qsTr("Set Estimator Origin")
725 visible: globals.guidedControllerFlyView.showSetEstimatorOrigin
727 mapClickDropPanel.close()
728 globals.guidedControllerFlyView.confirmAction(globals.guidedControllerFlyView.actionSetEstimatorOrigin, mapClickCoord)
733 Layout.fillWidth: true
734 text: qsTr("Set Heading")
735 visible: globals.guidedControllerFlyView.showChangeHeading
737 mapClickDropPanel.close()
738 globals.guidedControllerFlyView.confirmAction(globals.guidedControllerFlyView.actionChangeHeading, mapClickCoord)
744 QGCLabel { text: qsTr("Lat: %1").arg(mapClickCoord.latitude.toFixed(6)) }
745 QGCLabel { text: qsTr("Lon: %1").arg(mapClickCoord.longitude.toFixed(6)) }
752 onMapClicked: (position) => {
753 if (!globals.guidedControllerFlyView.guidedUIVisible &&
754 (globals.guidedControllerFlyView.showGotoLocation || globals.guidedControllerFlyView.showOrbit ||
755 globals.guidedControllerFlyView.showROI || globals.guidedControllerFlyView.showSetHome ||
756 globals.guidedControllerFlyView.showSetEstimatorOrigin)) {
758 position = Qt.point(position.x, position.y)
759 var clickCoord = _root.toCoordinate(position, false /* clipToViewPort */)
760 // For some strange reason using mainWindow in mapToItem doesn't work, so we use globals.parent instead which also gets us mainWindow
761 position = _root.mapToItem(globals.parent, position)
762 var dropPanel = mapClickDropPanelComponent.createObject(mainWindow, { mapClickCoord: clickCoord, clickRect: Qt.rect(position.x, position.y, 0, 0) })
769 anchors.margins: _toolsMargin
770 anchors.left: parent.left
771 anchors.top: parent.top
773 visible: !ScreenTools.isTinyScreen && QGroundControl.corePlugin.options.flyView.showMapScale && mapControl.pipState.state === mapControl.pipState.windowState
775 property real centerInset: visible ? parent.height - y : 0