/**************************************************************************** * * (c) 2009-2016 QGROUNDCONTROL PROJECT * * QGroundControl is licensed according to the terms in the file * COPYING.md in the root of the source code directory. * ****************************************************************************/ import QtQuick 2.3 import QtQuick.Controls 1.2 import QtLocation 5.3 import QtPositioning 5.3 import QtQuick.Dialogs 1.2 import QGroundControl 1.0 import QGroundControl.Airspace 1.0 import QGroundControl.Controllers 1.0 import QGroundControl.Controls 1.0 import QGroundControl.FlightDisplay 1.0 import QGroundControl.FlightMap 1.0 import QGroundControl.Palette 1.0 import QGroundControl.ScreenTools 1.0 import QGroundControl.Vehicle 1.0 FlightMap { id: flightMap anchors.fill: parent mapName: _mapName allowGCSLocationCenter: !userPanned allowVehicleLocationCenter: !_keepVehicleCentered planView: false property alias scaleState: mapScale.state // The following properties must be set by the consumer property var planMasterController property var wimaController property var guidedActionsController property var flightWidgets property var rightPanelWidth property var qgcView ///< QGCView control which contains this map property var multiVehicleView ///< true: multi-vehicle view, false: single vehicle view property rect centerViewport: Qt.rect(0, 0, width, height) property var _planMasterController: planMasterController property var _missionController: _planMasterController.missionController property var _geoFenceController: _planMasterController.geoFenceController property var _rallyPointController: _planMasterController.rallyPointController property var _activeVehicle: QGroundControl.multiVehicleManager.activeVehicle property var _activeVehicleCoordinate: _activeVehicle ? _activeVehicle.coordinate : QtPositioning.coordinate() property real _toolButtonTopMargin: parent.height - ScreenTools.availableHeight + (ScreenTools.defaultFontPixelHeight / 2) property bool _airspaceEnabled: QGroundControl.airmapSupported ? (QGroundControl.settingsManager.airMapSettings.enableAirMap.rawValue && QGroundControl.airspaceManager.connected): false property bool _disableVehicleTracking: false property bool _keepVehicleCentered: _mainIsMap ? false : true property bool _wimaEnabled: wimaController.enableWimaController.value property bool _showAllWimaItems: wimaController.showAllMissionItems.value property bool _showCurrentWimaItems: wimaController.showCurrentMissionItems.value function updateAirspace(reset) { if(_airspaceEnabled) { var coordinateNW = flightMap.toCoordinate(Qt.point(0,0), false /* clipToViewPort */) var coordinateSE = flightMap.toCoordinate(Qt.point(width,height), false /* clipToViewPort */) if(coordinateNW.isValid && coordinateSE.isValid) { QGroundControl.airspaceManager.setROI(coordinateNW, coordinateSE, false /*planView*/, reset) } } } // Track last known map position and zoom from Fly view in settings onZoomLevelChanged: { QGroundControl.flightMapZoom = zoomLevel updateAirspace(false) } onCenterChanged: { QGroundControl.flightMapPosition = center updateAirspace(false) } // When the user pans the map we stop responding to vehicle coordinate updates until the panRecenterTimer fires onUserPannedChanged: { if (userPanned) { console.log("user panned") userPanned = false _disableVehicleTracking = true panRecenterTimer.restart() } } on_AirspaceEnabledChanged: { updateAirspace(true) } function pointInRect(point, rect) { return point.x > rect.x && point.x < rect.x + rect.width && point.y > rect.y && point.y < rect.y + rect.height; } property real _animatedLatitudeStart property real _animatedLatitudeStop property real _animatedLongitudeStart property real _animatedLongitudeStop property real animatedLatitude property real animatedLongitude onAnimatedLatitudeChanged: flightMap.center = QtPositioning.coordinate(animatedLatitude, animatedLongitude) onAnimatedLongitudeChanged: flightMap.center = QtPositioning.coordinate(animatedLatitude, animatedLongitude) NumberAnimation on animatedLatitude { id: animateLat; from: _animatedLatitudeStart; to: _animatedLatitudeStop; duration: 1000 } NumberAnimation on animatedLongitude { id: animateLong; from: _animatedLongitudeStart; to: _animatedLongitudeStop; duration: 1000 } function animatedMapRecenter(fromCoord, toCoord) { _animatedLatitudeStart = fromCoord.latitude _animatedLongitudeStart = fromCoord.longitude _animatedLatitudeStop = toCoord.latitude _animatedLongitudeStop = toCoord.longitude animateLat.start() animateLong.start() } function recenterNeeded() { var vehiclePoint = flightMap.fromCoordinate(_activeVehicleCoordinate, false /* clipToViewport */) var toolStripRightEdge = mapFromItem(toolStrip, toolStrip.x, 0).x + toolStrip.width var instrumentsWidth = 0 if (QGroundControl.corePlugin.options.instrumentWidget && QGroundControl.corePlugin.options.instrumentWidget.widgetPosition === CustomInstrumentWidget.POS_TOP_RIGHT) { // Assume standard instruments instrumentsWidth = flightDisplayViewWidgets.getPreferredInstrumentWidth() } var centerViewport = Qt.rect(toolStripRightEdge, 0, width - toolStripRightEdge - instrumentsWidth, height) return !pointInRect(vehiclePoint, centerViewport) } function updateMapToVehiclePosition() { // We let FlightMap handle first vehicle position if (firstVehiclePositionReceived && _activeVehicleCoordinate.isValid && !_disableVehicleTracking) { if (_keepVehicleCentered) { flightMap.center = _activeVehicleCoordinate } else { if (firstVehiclePositionReceived && recenterNeeded()) { animatedMapRecenter(flightMap.center, _activeVehicleCoordinate) } } } } Timer { id: panRecenterTimer interval: 10000 running: false onTriggered: { _disableVehicleTracking = false updateMapToVehiclePosition() } } Timer { interval: 500 running: true repeat: true onTriggered: updateMapToVehiclePosition() } QGCPalette { id: qgcPal; colorGroupEnabled: true } QGCMapPalette { id: mapPal; lightColors: isSatelliteMap } Connections { target: _missionController onNewItemsFromVehicle: { var visualItems = _missionController.visualItems if (visualItems && visualItems.count !== 1) { if (recenterNeeded()){ mapFitFunctions.fitMapViewportToMissionItems() } firstVehiclePositionReceived = true } } } ExclusiveGroup { id: _mapTypeButtonsExclusiveGroup } MapFitFunctions { id: mapFitFunctions // The name for this id cannot be changed without breaking references outside of this code. Beware! map: _flightMap usePlannedHomePosition: false planMasterController: _planMasterController property real leftToolWidth: toolStrip.x + toolStrip.width } // Add wima Areas to the Map MapItemView { property bool _enableWima: wimaController.enableWimaController.value model: _enableWima ? wimaController.visualItems : 0 delegate: MapPolygon{ path: object.path; border.color: "black" color: object.type === "WimaJoinedAreaData" ? "gray" : object.type === "WimaServiceAreaData" ? "yellow" : object.type === "WimaMeasurementAreaData" ? "green" : "transparent" opacity: 0.25 z: 0 } } // Add mission items generated by wima planer to the map // all Items WimaPlanMapItems { map: flightMap largeMapView: _mainIsMap missionItems: wimaController.missionItems path: wimaController.waypointPath showItems: _wimaEnabled && _showAllWimaItems zOrderWP: QGroundControl.zOrderWimaAllWaypointIndicators zOrderLines: QGroundControl.zOrderWimaAllWaypointLines color: "gray" } // current Items WimaPlanMapItems { map: flightMap largeMapView: _mainIsMap missionItems: wimaController.currentMissionItems path: wimaController.currentWaypointPath showItems: _wimaEnabled && _showCurrentWimaItems zOrderWP: QGroundControl.zOrderWimaCurrentWaypointIndicators zOrderLines: QGroundControl.zOrderWimaCurrentWaypointLines color: "green" } // Add Snake tile center points to the map MapItemView { property bool _enable: wimaController.enableWimaController.value && wimaController.enableSnake.value model: _enable ? wimaController.snakeTileCenterPoints : 0 delegate: ProgressIndicator{ coordinate: modelData currentValue: getProgress() width: 10 height: 10 z: 1 function getProgress() { var progress = wimaController.nemoProgress[index] if (progress < 0) progress = 0 if (progress > 100) progress = 100 return progress } } } // Add Snake tiles to the map MapItemView { property bool _enable: wimaController.enableWimaController.value && wimaController.enableSnake.value model: _enable ? wimaController.snakeTiles : 0 delegate: MapPolygon{ path: object.path; border.color: "black" border.width: 1 color: "transparent" opacity: 1 z: 2 } } // Add trajectory points to the map MapItemView { model: _mainIsMap ? _activeVehicle ? _activeVehicle.trajectoryPoints : 0 : 0 delegate: MapPolyline { line.width: 3 line.color: "red" z: QGroundControl.zOrderTrajectoryLines path: [ object.coordinate1, object.coordinate2, ] } } // Add the vehicles to the map MapItemView { model: QGroundControl.multiVehicleManager.vehicles delegate: VehicleMapItem { vehicle: object coordinate: object.coordinate map: flightMap size: _mainIsMap ? ScreenTools.defaultFontPixelHeight * 3 : ScreenTools.defaultFontPixelHeight z: QGroundControl.zOrderVehicles } } // Add ADSB vehicles to the map MapItemView { model: _activeVehicle ? _activeVehicle.adsbVehicles : [] property var _activeVehicle: QGroundControl.multiVehicleManager.activeVehicle delegate: VehicleMapItem { coordinate: object.coordinate altitude: object.altitude callsign: object.callsign heading: object.heading alert: object.alert map: flightMap z: QGroundControl.zOrderVehicles } } // Add the items associated with each vehicles flight plan to the map Repeater { model: QGroundControl.multiVehicleManager.vehicles PlanMapItems { map: flightMap largeMapView: _mainIsMap masterController: masterController isActiveVehicle: _vehicle.active property var _vehicle: object PlanMasterController { id: masterController Component.onCompleted: startStaticActiveVehicle(object) } } } // Allow custom builds to add map items CustomMapItems { map: flightMap largeMapView: _mainIsMap } GeoFenceMapVisuals { map: flightMap myGeoFenceController: _geoFenceController interactive: false planView: false homePosition: _activeVehicle && _activeVehicle.homePosition.isValid ? _activeVehicle.homePosition : QtPositioning.coordinate() } // Rally points on map MapItemView { model: _rallyPointController.points delegate: MapQuickItem { id: itemIndicator anchorPoint.x: sourceItem.anchorPointX anchorPoint.y: sourceItem.anchorPointY coordinate: object.coordinate z: QGroundControl.zOrderMapItems sourceItem: MissionItemIndexLabel { id: itemIndexLabel label: qsTr("R", "rally point map item label") } } } // Camera trigger points MapItemView { model: _activeVehicle ? _activeVehicle.cameraTriggerPoints : 0 delegate: CameraTriggerIndicator { coordinate: object.coordinate z: QGroundControl.zOrderTopMost } } // GoTo Location visuals MapQuickItem { id: gotoLocationItem visible: false z: QGroundControl.zOrderMapItems anchorPoint.x: sourceItem.anchorPointX anchorPoint.y: sourceItem.anchorPointY sourceItem: MissionItemIndexLabel { checked: true index: -1 label: qsTr("Goto here", "Goto here waypoint") } property bool inGotoFlightMode: _activeVehicle ? _activeVehicle.flightMode === _activeVehicle.gotoFlightMode : false property var activeVehicle: _activeVehicle onInGotoFlightModeChanged: { if (!inGotoFlightMode && visible) { // Hide goto indicator when vehicle falls out of guided mode visible = false } } onActiveVehicleChanged: { if (!_activeVehicle) { visible = false } } function show(coord) { gotoLocationItem.coordinate = coord gotoLocationItem.visible = true } function hide() { gotoLocationItem.visible = false } function actionConfirmed() { // We leave the indicator visible. The handling for onInGuidedModeChanged will hide it. } function actionCancelled() { hide() } } // Orbit editing visuals QGCMapCircleVisuals { id: orbitMapCircle mapControl: parent mapCircle: _mapCircle visible: false property alias center: _mapCircle.center property alias clockwiseRotation: _mapCircle.clockwiseRotation property var activeVehicle: _activeVehicle readonly property real defaultRadius: 30 onActiveVehicleChanged: { if (!_activeVehicle) { visible = false } } function show(coord) { _mapCircle.radius.rawValue = defaultRadius orbitMapCircle.center = coord orbitMapCircle.visible = true } function hide() { orbitMapCircle.visible = false } function actionConfirmed() { // Live orbit status is handled by telemetry so we hide here and telemetry will show again. hide() } function actionCancelled() { hide() } function radius() { return _mapCircle.radius.rawValue } Component.onCompleted: guidedActionsController.orbitMapCircle = orbitMapCircle QGCMapCircle { id: _mapCircle interactive: true radius.rawValue: 30 showRotation: true clockwiseRotation: true } } // Orbit telemetry visuals QGCMapCircleVisuals { id: orbitTelemetryCircle mapControl: parent mapCircle: _activeVehicle ? _activeVehicle.orbitMapCircle : null visible: _activeVehicle ? _activeVehicle.orbitActive : false } MapQuickItem { id: orbitCenterIndicator anchorPoint.x: sourceItem.anchorPointX anchorPoint.y: sourceItem.anchorPointY coordinate: _activeVehicle ? _activeVehicle.orbitMapCircle.center : QtPositioning.coordinate() visible: orbitTelemetryCircle.visible sourceItem: MissionItemIndexLabel { checked: true index: -1 label: qsTr("Orbit", "Orbit waypoint") } } // Handle guided mode clicks MouseArea { anchors.fill: parent Menu { id: clickMenu property var coord MenuItem { text: qsTr("Go to location") visible: guidedActionsController.showGotoLocation onTriggered: { gotoLocationItem.show(clickMenu.coord) orbitMapCircle.hide() guidedActionsController.confirmAction(guidedActionsController.actionGoto, clickMenu.coord, gotoLocationItem) } } MenuItem { text: qsTr("Orbit at location") visible: guidedActionsController.showOrbit onTriggered: { orbitMapCircle.show(clickMenu.coord) gotoLocationItem.hide() guidedActionsController.confirmAction(guidedActionsController.actionOrbit, clickMenu.coord, orbitMapCircle) } } } onClicked: { if (guidedActionsController.guidedUIVisible || (!guidedActionsController.showGotoLocation && !guidedActionsController.showOrbit)) { return } orbitMapCircle.hide() gotoLocationItem.hide() var clickCoord = flightMap.toCoordinate(Qt.point(mouse.x, mouse.y), false /* clipToViewPort */) if (guidedActionsController.showGotoLocation && guidedActionsController.showOrbit) { clickMenu.coord = clickCoord clickMenu.popup() } else if (guidedActionsController.showGotoLocation) { gotoLocationItem.show(clickCoord) guidedActionsController.confirmAction(guidedActionsController.actionGoto, clickCoord) } else if (guidedActionsController.showOrbit) { orbitMapCircle.show(clickCoord) guidedActionsController.confirmAction(guidedActionsController.actionOrbit, clickCoord) } } } MapScale { id: mapScale anchors.right: parent.right anchors.margins: ScreenTools.defaultFontPixelHeight * (0.33) anchors.topMargin: ScreenTools.defaultFontPixelHeight * (0.33) + state === "bottomMode" ? 0 : ScreenTools.toolbarHeight anchors.bottomMargin: ScreenTools.defaultFontPixelHeight * (0.33) mapControl: flightMap visible: !ScreenTools.isTinyScreen state: "bottomMode" states: [ State { name: "topMode" AnchorChanges { target: mapScale anchors.top: parent.top anchors.bottom: undefined } }, State { name: "bottomMode" AnchorChanges { target: mapScale anchors.top: undefined anchors.bottom: parent.bottom } } ] } // Airspace overlap support MapItemView { model: _airspaceEnabled && QGroundControl.settingsManager.airMapSettings.enableAirspace && QGroundControl.airspaceManager.airspaceVisible ? QGroundControl.airspaceManager.airspaces.circles : [] delegate: MapCircle { center: object.center radius: object.radius color: object.color border.color: object.lineColor border.width: object.lineWidth } } MapItemView { model: _airspaceEnabled && QGroundControl.settingsManager.airMapSettings.enableAirspace && QGroundControl.airspaceManager.airspaceVisible ? QGroundControl.airspaceManager.airspaces.polygons : [] delegate: MapPolygon { path: object.polygon color: object.color border.color: object.lineColor border.width: object.lineWidth } } }