8import QGroundControl.Controls
9import QGroundControl.FlightMap
13 allowGCSLocationCenter: true
14 allowVehicleLocationCenter: false
17 property var tileSet: null
19 property var _settingsManager: QGroundControl.settingsManager
20 property var _settings: _settingsManager.offlineMapsSettings
21 property var _fmSettings: _settingsManager.flightMapSettings
23 property string mapType: _fmSettings.mapProvider.value + " " + _fmSettings.mapType.value
24 property bool isMapInteractive: false
25 property bool _showPreview: true
26 property bool _defaultSet: tileSet && tileSet.defaultSet
27 property real _margins: ScreenTools.defaultFontPixelWidth * 0.5
29 property bool _saveRealEstate: ScreenTools.isTinyScreen || ScreenTools.isShortScreen
30 property real _adjustableFontPointSize: _saveRealEstate ? ScreenTools.smallFontPointSize : ScreenTools.defaultFontPointSize
32 property string _mapAdjustedColor: _map.isSatelliteMap ? "white" : "black"
33 property bool _tooManyTiles: QGroundControl.mapEngineManager.tileCount > _maxTilesForDownload
34 property var _addNewSetViewObject: null
36 readonly property real minZoomLevel: 1
37 readonly property real maxZoomLevel: 20
38 readonly property real sliderTouchArea: ScreenTools.defaultFontPixelWidth * (ScreenTools.isTinyScreen ? 5 : (ScreenTools.isMobile ? 6 : 3))
40 readonly property int _maxTilesForDownload: _settings.maxTilesForDownload.rawValue
42 property bool isSatelliteMap: activeMapType.name.indexOf("Satellite") > -1 || activeMapType.name.indexOf("Hybrid") > -1
44 QGCPalette { id: qgcPal }
46 Component.onCompleted: {
47 QGroundControl.mapEngineManager.loadTileSets()
53 target: QGroundControl.mapEngineManager
54 function onErrorMessageChanged() { errorDialogFactory.open() }
57 function handleChanges() {
58 if (isMapInteractive) {
59 var xr = Math.round(_map.width) - 1 // Must be within boundaries of visible map
60 var yr = Math.round(_map.height) - 1 // Must be within boundaries of visible map
61 var c0 = _map.toCoordinate(Qt.point(0, 0), false /* clipToViewPort */)
62 var c1 = _map.toCoordinate(Qt.point(xr, yr), false /* clipToViewPort */)
63 QGroundControl.mapEngineManager.updateForCurrentView(c0.longitude, c0.latitude, c1.longitude, c1.latitude, _addNewSetViewObject.sliderMinZoom.value, _addNewSetViewObject.sliderMaxZoom.value, mapType)
67 function updateMap() {
68 for (var i = 0; i < _map.supportedMapTypes.length; i++) {
69 if (mapType === _map.supportedMapTypes[i].name) {
70 _map.activeMapType = _map.supportedMapTypes[i]
77 function addNewSet() {
78 _addNewSetViewObject = addNewSetViewComponent.createObject(_map)
79 isMapInteractive = true
80 mapType = _fmSettings.mapProvider.value + " " + _fmSettings.mapType.value
86 isMapInteractive = false
87 if (!tileSet.defaultSet) {
88 mapType = tileSet.mapTypeStr
89 _map.center = midPoint(tileSet.topleftLat, tileSet.bottomRightLat, tileSet.topleftLon, tileSet.bottomRightLon)
90 //-- Delineate Set Region
91 var x0 = tileSet.topleftLon
92 var x1 = tileSet.bottomRightLon
93 var y0 = tileSet.topleftLat
94 var y1 = tileSet.bottomRightLat
95 mapBoundary.topLeft = QtPositioning.coordinate(y0, x0)
96 mapBoundary.bottomRight = QtPositioning.coordinate(y1, x1)
97 mapBoundary.visible = true
98 // Some times, for whatever reason, the bounding box is correct (around ETH for instance), but the rectangle is drawn across the planet.
99 // When that happens, the "_map.fitViewportToMapItems()" below makes the map to zoom to the entire earth.
100 _map.fitViewportToVisibleMapItems()
102 infoViewComponent.createObject(_map)
105 function toRadian(deg) {
106 return deg * Math.PI / 180
109 function toDegree(rad) {
110 return rad * 180 / Math.PI
113 function midPoint(lat1, lat2, lon1, lon2) {
114 var dLon = toRadian(lon2 - lon1)
115 lat1 = toRadian(lat1)
116 lat2 = toRadian(lat2)
117 lon1 = toRadian(lon1)
118 var Bx = Math.cos(lat2) * Math.cos(dLon)
119 var By = Math.cos(lat2) * Math.sin(dLon)
120 var lat3 = Math.atan2(Math.sin(lat1) + Math.sin(lat2), Math.sqrt((Math.cos(lat1) + Bx) * (Math.cos(lat1) + Bx) + By * By))
121 var lon3 = lon1 + Math.atan2(By, Math.cos(lat1) + Bx)
122 return QtPositioning.coordinate(toDegree(lat3), toDegree(lon3))
125 function resetMapToDefaults() {
126 _map.center = QGroundControl.flightMapPosition
127 _map.zoomLevel = QGroundControl.flightMapZoom
130 onMapTypeChanged: updateMap()
136 color: Qt.rgba(1, 0, 0, 0.05)
142 onCenterChanged: handleChanges()
143 onZoomLevelChanged: handleChanges()
144 onWidthChanged: handleChanges()
145 onHeightChanged: handleChanges()
148 anchors.leftMargin: ScreenTools.defaultFontPixelWidth / 2
149 anchors.bottomMargin: anchors.leftMargin
150 anchors.left: parent.left
151 anchors.bottom: parent.bottom
155 //-----------------------------------------------------------------
158 id: infoViewComponent
162 anchors.margins: ScreenTools.defaultFontPixelHeight
163 anchors.right: parent.right
164 anchors.verticalCenter: parent.verticalCenter
165 width: tileInfoColumn.width + ScreenTools.defaultFontPixelWidth * 2
166 height: tileInfoColumn.height + ScreenTools.defaultFontPixelHeight * 2
167 color: Qt.rgba(qgcPal.window.r, qgcPal.window.g, qgcPal.window.b, 0.85)
168 radius: ScreenTools.defaultFontPixelWidth * 0.5
170 property bool _extraButton: !_defaultSet && tileSet && !tileSet.complete
172 property real _labelWidth: ScreenTools.defaultFontPixelWidth * 10
173 property real _valueWidth: ScreenTools.defaultFontPixelWidth * 14
176 spacing: ScreenTools.defaultFontPixelHeight * 0.5
177 anchors.centerIn: parent
179 anchors.left: parent.left
180 anchors.right: parent.right
181 wrapMode: Text.WordWrap
182 text: tileSet ? tileSet.name : ""
183 font.pointSize: _saveRealEstate ? ScreenTools.defaultFontPointSize : ScreenTools.mediumFontPointSize
184 horizontalAlignment: Text.AlignHCenter
189 anchors.left: parent.left
190 anchors.right: parent.right
191 visible: !_defaultSet
192 text: tileSet ? tileSet.name : ""
195 anchors.left: parent.left
196 anchors.right: parent.right
197 wrapMode: Text.WordWrap
200 if (tileSet.defaultSet)
201 return qsTr("System Wide Tile Cache")
203 return "(" + tileSet.mapTypeStr + ")"
207 horizontalAlignment: Text.AlignHCenter
211 spacing: ScreenTools.defaultFontPixelWidth
212 anchors.horizontalCenter: parent.horizontalCenter
213 visible: !_defaultSet && mapType !== QGroundControl.elevationProviderName
214 QGCLabel { text: qsTr("Zoom Levels:"); width: infoView._labelWidth }
215 QGCLabel { text: tileSet ? (tileSet.minZoom + " - " + tileSet.maxZoom) : ""; horizontalAlignment: Text.AlignRight; width: infoView._valueWidth }
218 spacing: ScreenTools.defaultFontPixelWidth
219 anchors.horizontalCenter: parent.horizontalCenter
220 visible: !_defaultSet
221 QGCLabel { text: qsTr("Total:"); width: infoView._labelWidth }
222 QGCLabel { text: (tileSet ? tileSet.totalTileCountStr : "") + " (" + (tileSet ? tileSet.totalTilesSizeStr : "") + ")"; horizontalAlignment: Text.AlignRight; width: infoView._valueWidth }
225 spacing: ScreenTools.defaultFontPixelWidth
226 anchors.horizontalCenter: parent.horizontalCenter
227 visible: tileSet && !_defaultSet && tileSet.uniqueTileCount > 0
228 QGCLabel { text: qsTr("Unique:"); width: infoView._labelWidth }
229 QGCLabel { text: (tileSet ? tileSet.uniqueTileCountStr : "") + " (" + (tileSet ? tileSet.uniqueTileSizeStr : "") + ")"; horizontalAlignment: Text.AlignRight; width: infoView._valueWidth }
233 spacing: ScreenTools.defaultFontPixelWidth
234 anchors.horizontalCenter: parent.horizontalCenter
235 visible: tileSet && !_defaultSet && !tileSet.complete
236 QGCLabel { text: qsTr("Downloaded:"); width: infoView._labelWidth }
237 QGCLabel { text: (tileSet ? tileSet.savedTileCountStr : "") + " (" + (tileSet ? tileSet.savedTileSizeStr : "") + ")"; horizontalAlignment: Text.AlignRight; width: infoView._valueWidth }
240 spacing: ScreenTools.defaultFontPixelWidth
241 anchors.horizontalCenter: parent.horizontalCenter
242 visible: tileSet && !_defaultSet && !tileSet.complete && tileSet.errorCount > 0
243 QGCLabel { text: qsTr("Error Count:"); width: infoView._labelWidth }
244 QGCLabel { text: tileSet ? tileSet.errorCountStr : ""; horizontalAlignment: Text.AlignRight; width: infoView._valueWidth }
246 //-- Default Tile Set
248 spacing: ScreenTools.defaultFontPixelWidth
249 anchors.horizontalCenter: parent.horizontalCenter
251 QGCLabel { text: qsTr("Size:"); width: infoView._labelWidth }
252 QGCLabel { text: tileSet ? tileSet.savedTileSizeStr : ""; horizontalAlignment: Text.AlignRight; width: infoView._valueWidth }
255 spacing: ScreenTools.defaultFontPixelWidth
256 anchors.horizontalCenter: parent.horizontalCenter
258 QGCLabel { text: qsTr("Tile Count:"); width: infoView._labelWidth }
259 QGCLabel { text: tileSet ? tileSet.savedTileCountStr : ""; horizontalAlignment: Text.AlignRight; width: infoView._valueWidth }
262 spacing: ScreenTools.defaultFontPixelWidth
263 anchors.horizontalCenter: parent.horizontalCenter
265 text: qsTr("Resume Download")
266 visible: tileSet && !_defaultSet && !tileSet.complete && !tileSet.downloading
267 width: ScreenTools.defaultFontPixelWidth * 16
268 onClicked: tileSet.resumeDownloadTask()
271 text: qsTr("Cancel Download")
272 visible: tileSet && !_defaultSet && !tileSet.complete && tileSet.downloading
273 width: ScreenTools.defaultFontPixelWidth * 16
274 onClicked: tileSet.cancelDownloadTask()
278 width: ScreenTools.defaultFontPixelWidth * (infoView._extraButton ? 6 : 10)
279 onClicked: deleteConfirmationDialogFactory.open()
280 enabled: tileSet && tileSet.savedTileSize > 0
284 width: ScreenTools.defaultFontPixelWidth * (infoView._extraButton ? 6 : 10)
285 visible: !_defaultSet
286 enabled: editSetName.text !== ""
288 if (editSetName.text !== tileSet.name) {
289 QGroundControl.mapEngineManager.renameTileSet(tileSet, editSetName.text)
295 text: _defaultSet ? qsTr("Close") : qsTr("Cancel")
296 width: ScreenTools.defaultFontPixelWidth * (infoView._extraButton ? 6 : 10)
297 onClicked: _map.destroy()
305 id: addNewSetViewComponent
311 property var sliderMinZoom: sliderMinZoom
312 property var sliderMaxZoom: sliderMaxZoom
315 anchors.verticalCenter: parent.verticalCenter
316 anchors.leftMargin: _margins
317 anchors.left: parent.left
321 text: qsTr("Show zoom previews")
322 visible: !_showPreview
323 onClicked: _showPreview = !_showPreview
328 width: addNewSetView.width / 4
329 height: addNewSetView.height / 4
331 activeMapType: _map.activeMapType
332 zoomLevel: sliderMinZoom.value
333 visible: _showPreview
335 property bool isSatelliteMap: activeMapType.name.indexOf("Satellite") > -1 || activeMapType.name.indexOf("Hybrid") > -1
337 plugin: Plugin { name: "QGroundControl" }
340 anchors.leftMargin: ScreenTools.defaultFontPixelWidth / 2
341 anchors.bottomMargin: anchors.leftMargin
342 anchors.left: parent.left
343 anchors.bottom: parent.bottom
349 border.color: _mapAdjustedColor
353 anchors.centerIn: parent
355 text: qsTr("Min Zoom: %1").arg(sliderMinZoom.value)
359 onClicked: _showPreview = false
366 width: minZoomPreview.width
367 height: minZoomPreview.height
369 activeMapType: _map.activeMapType
370 zoomLevel: sliderMaxZoom.value
371 visible: _showPreview
373 property bool isSatelliteMap: activeMapType.name.indexOf("Satellite") > -1 || activeMapType.name.indexOf("Hybrid") > -1
375 plugin: Plugin { name: "QGroundControl" }
378 anchors.leftMargin: ScreenTools.defaultFontPixelWidth / 2
379 anchors.bottomMargin: anchors.leftMargin
380 anchors.left: parent.left
381 anchors.bottom: parent.bottom
387 border.color: _mapAdjustedColor
391 anchors.centerIn: parent
393 text: qsTr("Max Zoom: %1").arg(sliderMaxZoom.value)
397 onClicked: _showPreview = false
404 anchors.margins: ScreenTools.defaultFontPixelWidth
405 anchors.verticalCenter: parent.verticalCenter
406 anchors.right: parent.right
407 width: ScreenTools.defaultFontPixelWidth * (ScreenTools.isTinyScreen ? 24 : 28)
408 height: Math.min(parent.height - (anchors.margins * 2), addNewSetFlickable.y + addNewSetColumn.height + addNewSetLabel.anchors.margins)
409 color: Qt.rgba(qgcPal.window.r, qgcPal.window.g, qgcPal.window.b, 0.85)
410 radius: ScreenTools.defaultFontPixelWidth * 0.5
412 //-- Eat mouse events
419 anchors.margins: ScreenTools.defaultFontPixelHeight / 2
420 anchors.top: parent.top
421 anchors.left: parent.left
422 anchors.right: parent.right
423 wrapMode: Text.WordWrap
424 text: qsTr("Add New Set")
425 font.pointSize: _saveRealEstate ? ScreenTools.defaultFontPointSize : ScreenTools.mediumFontPointSize
426 horizontalAlignment: Text.AlignHCenter
430 id: addNewSetFlickable
431 anchors.leftMargin: ScreenTools.defaultFontPixelWidth
432 anchors.rightMargin: anchors.leftMargin
433 anchors.topMargin: ScreenTools.defaultFontPixelWidth / 3
434 anchors.bottomMargin: anchors.topMargin
435 anchors.top: addNewSetLabel.bottom
436 anchors.left: parent.left
437 anchors.right: parent.right
438 anchors.bottom: parent.bottom
440 contentHeight: addNewSetColumn.height
444 anchors.left: parent.left
445 anchors.right: parent.right
446 spacing: ScreenTools.defaultFontPixelHeight * (ScreenTools.isTinyScreen ? 0.25 : 0.5)
449 spacing: ScreenTools.isTinyScreen ? 0 : ScreenTools.defaultFontPixelHeight * 0.25
450 anchors.left: parent.left
451 anchors.right: parent.right
452 QGCLabel { text: qsTr("Name:") }
455 anchors.left: parent.left
456 anchors.right: parent.right
457 Component.onCompleted: text = QGroundControl.mapEngineManager.getUniqueName()
458 onTextChanged: duplicateName.visible = false
460 target: QGroundControl.mapEngineManager
461 function onTileSetsChanged() { setName.text = QGroundControl.mapEngineManager.getUniqueName() }
467 spacing: ScreenTools.isTinyScreen ? 0 : ScreenTools.defaultFontPixelHeight * 0.25
468 anchors.left: parent.left
469 anchors.right: parent.right
471 text: qsTr("Map type:")
472 visible: !_saveRealEstate
476 anchors.left: parent.left
477 anchors.right: parent.right
478 model: QGroundControl.mapEngineManager.mapList
479 onActivated: (index) => {
480 mapType = textAt(index)
482 Component.onCompleted: {
483 var index = mapCombo.find(mapType)
485 console.warn("Active map name not in combo", mapType)
487 mapCombo.currentIndex = index
492 anchors.left: parent.left
493 anchors.right: parent.right
494 text: qsTr("Fetch elevation data")
495 checked: QGroundControl.mapEngineManager.fetchElevation
497 QGroundControl.mapEngineManager.fetchElevation = checked
504 anchors.left: parent.left
505 anchors.right: parent.right
506 height: zoomColumn.height + ScreenTools.defaultFontPixelHeight * 0.5
508 border.color: qgcPal.text
509 radius: ScreenTools.defaultFontPixelWidth * 0.5
513 spacing: ScreenTools.isTinyScreen ? 0 : ScreenTools.defaultFontPixelHeight * 0.5
514 anchors.margins: ScreenTools.defaultFontPixelHeight * 0.25
515 anchors.top: parent.top
516 anchors.left: parent.left
517 anchors.right: parent.right
520 text: qsTr("Min/Max Zoom Levels")
521 font.pointSize: _adjustableFontPointSize
522 anchors.horizontalCenter: parent.horizontalCenter
527 anchors.left: parent.left
528 anchors.right: parent.right
529 height: sliderTouchArea * 1.25
534 property bool _updateSetting: false
535 Component.onCompleted: {
536 sliderMinZoom.value = _settings.minZoomLevelDownload.rawValue
537 _updateSetting = true
540 if (sliderMinZoom.value > sliderMaxZoom.value) {
541 sliderMaxZoom.value = sliderMinZoom.value
543 if (_updateSetting) {
544 // Don't update setting until after Component.onCompleted since bad values come through before that
545 _settings.minZoomLevelDownload.rawValue = value
550 x: sliderMinZoom.leftPadding + sliderMinZoom.visualPosition * (sliderMinZoom.availableWidth - width)
551 y: sliderMinZoom.topPadding + sliderMinZoom.availableHeight * 0.5 - height * 0.5
552 implicitWidth: sliderTouchArea
553 implicitHeight: sliderTouchArea
554 radius: sliderTouchArea * 0.5
557 border.color: qgcPal.buttonText
559 text: sliderMinZoom.value
560 anchors.centerIn: parent
561 font.family: ScreenTools.normalFontFamily
562 font.pointSize: ScreenTools.smallFontPointSize
563 color: qgcPal.buttonText
566 } // Slider - min zoom
570 anchors.left: parent.left
571 anchors.right: parent.right
572 height: sliderTouchArea * 1.25
577 property bool _updateSetting: false
578 Component.onCompleted: {
579 sliderMaxZoom.value = _settings.maxZoomLevelDownload.rawValue
580 _updateSetting = true
583 if (sliderMaxZoom.value < sliderMinZoom.value) {
584 sliderMinZoom.value = sliderMaxZoom.value
586 if (_updateSetting) {
587 // Don't update setting until after Component.onCompleted since bad values come through before that
588 _settings.maxZoomLevelDownload.rawValue = value
593 x: sliderMaxZoom.leftPadding + sliderMaxZoom.visualPosition * (sliderMaxZoom.availableWidth - width)
594 y: sliderMaxZoom.topPadding + sliderMaxZoom.availableHeight * 0.5 - height * 0.5
595 implicitWidth: sliderTouchArea
596 implicitHeight: sliderTouchArea
597 radius: sliderTouchArea * 0.5
600 border.color: qgcPal.buttonText
602 text: sliderMaxZoom.value
603 anchors.centerIn: parent
604 font.family: ScreenTools.normalFontFamily
605 font.pointSize: ScreenTools.smallFontPointSize
606 color: qgcPal.buttonText
609 } // Slider - max zoom
613 rowSpacing: ScreenTools.isTinyScreen ? 0 : ScreenTools.defaultFontPixelHeight * 0.5
615 text: qsTr("Tile Count:")
616 font.pointSize: _adjustableFontPointSize
619 text: QGroundControl.mapEngineManager.tileCountStr
620 font.pointSize: _adjustableFontPointSize
624 text: qsTr("Est Size:")
625 font.pointSize: _adjustableFontPointSize
628 text: QGroundControl.mapEngineManager.tileSizeStr
629 font.pointSize: _adjustableFontPointSize
632 } // Column - Zoom info
633 } // Rectangle - Zoom info
636 text: qsTr("Too many tiles")
637 visible: _tooManyTiles
638 color: qgcPal.warningText
639 anchors.horizontalCenter: parent.horizontalCenter
644 text: qsTr("Tile set with this name already exists")
646 color: qgcPal.warningText
647 anchors.horizontalCenter: parent.horizontalCenter
652 spacing: ScreenTools.defaultFontPixelWidth
653 anchors.horizontalCenter: parent.horizontalCenter
655 text: qsTr("Download")
656 width: (addNewSetColumn.width * 0.5) - (addButtonRow.spacing * 0.5)
657 enabled: !_tooManyTiles && setName.text.length > 0
659 if (QGroundControl.mapEngineManager.findName(setName.text)) {
660 duplicateName.visible = true
662 QGroundControl.mapEngineManager.startDownload(setName.text, mapType)
669 width: (addNewSetColumn.width * 0.5) - (addButtonRow.spacing * 0.5)
670 onClicked: _map.destroy()
679 CenterMapDropButton {
682 anchors.margins: _margins
683 anchors.left: parent.left
684 anchors.top: parent.top
688 visible: _addNewSetViewObject
691 QGCPopupDialogFactory {
692 id: errorDialogFactory
694 dialogComponent: errorDialogComponent
698 id: errorDialogComponent
700 QGCSimpleMessageDialog {
701 title: qsTr("Error Message")
702 text: QGroundControl.mapEngineManager.errorMessage
703 buttons: Dialog.Close
707 QGCPopupDialogFactory {
708 id: deleteConfirmationDialogFactory
710 dialogComponent: deleteConfirmationDialogComponent
714 id: deleteConfirmationDialogComponent
716 QGCSimpleMessageDialog {
717 title: qsTr("Confirm Delete")
718 text: tileSet.defaultSet ?
719 qsTr("This will delete all tiles INCLUDING the tile sets you have created yourself.\n\nIs this really what you want?") :
720 qsTr("Delete %1 and all its tiles.\n\nIs this really what you want?").arg(tileSet.name)
721 buttons: Dialog.Yes | Dialog.No
724 QGroundControl.mapEngineManager.deleteTileSet(tileSet)