1
0
mirror of https://github.com/f4exb/sdrangel.git synced 2026-05-13 12:52:28 -04:00
Jon Beniston 9c7aa8b333 Map Updates
Allow OpenSkyNetwork DB, OpenAIP and OurAirports DB stuctures to be
shared by different plugins, to speed up loading.
Perform map anti-aliasing on the whole map, rather than just info boxes,
to improve rendering speed when there are many items. Add map
multisampling as a preference.
Add plotting of airspaces, airports, navaids on Map feature.
Add support for polylines and polygons to be plotted on Map feature.
Add support for images to 2D Map feature.
Add distance and name filters to Map feature.
Filter map items when zoomed out or if off screen, to improve rendering
performance.
Add UK DAB, FM and AM transmitters to Map feature.
Use labelless maps for 2D transmit maps in Map feature (same as in ADS-B
demod).
2023-02-14 14:46:08 +00:00

398 lines
15 KiB
QML

import QtQuick 2.14
import QtQuick.Window 2.14
import QtQuick.Controls 2.14
import QtLocation 5.14
import QtPositioning 5.14
Item {
id: qmlMap
property int mapZoomLevel: 11
property string mapProvider: "osm"
property variant mapPtr
property variant guiPtr
function createMap(pluginParameters, gui) {
guiPtr = gui
var paramString = ""
for (var prop in pluginParameters) {
var parameter = 'PluginParameter { name: "' + prop + '"; value: "' + pluginParameters[prop] + '"}'
paramString = paramString + parameter
}
var pluginString = 'import QtLocation 5.14; Plugin{ name:"' + mapProvider + '"; ' + paramString + '}'
var plugin = Qt.createQmlObject (pluginString, qmlMap)
if (mapPtr) {
// Objects aren't destroyed immediately, so don't call findChild("map")
mapPtr.destroy()
mapPtr = null
}
mapPtr = actualMapComponent.createObject(page)
mapPtr.plugin = plugin;
mapPtr.forceActiveFocus()
return mapPtr
}
function getMapTypes() {
var mapTypes = []
if (mapPtr) {
for (var i = 0; i < mapPtr.supportedMapTypes.length; i++) {
mapTypes[i] = mapPtr.supportedMapTypes[i].name
}
}
return mapTypes
}
function setMapType(mapTypeIndex) {
if (mapPtr && (mapTypeIndex < mapPtr.supportedMapTypes.length)) {
if (mapPtr.supportedMapTypes[mapTypeIndex] !== undefined) {
mapPtr.activeMapType = mapPtr.supportedMapTypes[mapTypeIndex]
}
}
}
Item {
id: page
anchors.fill: parent
}
Component {
id: actualMapComponent
Map {
id: map
objectName: "map"
anchors.fill: parent
center: QtPositioning.coordinate(51.5, 0.125) // London
zoomLevel: 10
gesture.enabled: true
gesture.acceptedGestures: MapGestureArea.PinchGesture | MapGestureArea.PanGesture
MapItemView {
model: imageModelFiltered
delegate: imageComponent
}
MapItemView {
model: polygonModelFiltered
delegate: polygonComponent
}
MapItemView {
model: polygonModelFiltered
delegate: polygonNameComponent
}
MapItemView {
model: polylineModelFiltered
delegate: polylineComponent
}
MapItemView {
model: polylineModelFiltered
delegate: polylineNameComponent
}
// Tracks first, so drawn under other items
MapItemView {
model: mapModelFiltered
delegate: groundTrack1Component
}
MapItemView {
model: mapModelFiltered
delegate: groundTrack2Component
}
MapItemView {
model: mapModelFiltered
delegate: predictedGroundTrack1Component
}
MapItemView {
model: mapModelFiltered
delegate: predictedGroundTrack2Component
}
MapItemView {
model: mapModelFiltered
delegate: mapComponent
}
onZoomLevelChanged: {
mapZoomLevel = zoomLevel
mapModelFiltered.viewChanged(visibleRegion.boundingGeoRectangle().topLeft.longitude, visibleRegion.boundingGeoRectangle().topLeft.latitude, visibleRegion.boundingGeoRectangle().bottomRight.longitude, visibleRegion.boundingGeoRectangle().bottomRight.latitude, zoomLevel);
imageModelFiltered.viewChanged(visibleRegion.boundingGeoRectangle().topLeft.longitude, visibleRegion.boundingGeoRectangle().topLeft.latitude, visibleRegion.boundingGeoRectangle().bottomRight.longitude, visibleRegion.boundingGeoRectangle().bottomRight.latitude, zoomLevel);
polygonModelFiltered.viewChanged(visibleRegion.boundingGeoRectangle().topLeft.longitude, visibleRegion.boundingGeoRectangle().topLeft.latitude, visibleRegion.boundingGeoRectangle().bottomRight.longitude, visibleRegion.boundingGeoRectangle().bottomRight.latitude, zoomLevel);
polylineModelFiltered.viewChanged(visibleRegion.boundingGeoRectangle().topLeft.longitude, visibleRegion.boundingGeoRectangle().topLeft.latitude, visibleRegion.boundingGeoRectangle().bottomRight.longitude, visibleRegion.boundingGeoRectangle().bottomRight.latitude, zoomLevel);
}
// The map displays MapPolyLines in the wrong place (+360 degrees) if
// they start to the left of the visible region, so we need to
// split them so they don't, each time the visible region is changed. meh.
onCenterChanged: {
polylineModelFiltered.viewChanged(visibleRegion.boundingGeoRectangle().topLeft.longitude, visibleRegion.boundingGeoRectangle().topLeft.latitude, visibleRegion.boundingGeoRectangle().bottomRight.longitude, visibleRegion.boundingGeoRectangle().bottomRight.latitude, zoomLevel);
polygonModelFiltered.viewChanged(visibleRegion.boundingGeoRectangle().topLeft.longitude, visibleRegion.boundingGeoRectangle().topLeft.latitude, visibleRegion.boundingGeoRectangle().bottomRight.longitude, visibleRegion.boundingGeoRectangle().bottomRight.latitude, zoomLevel);
imageModelFiltered.viewChanged(visibleRegion.boundingGeoRectangle().topLeft.longitude, visibleRegion.boundingGeoRectangle().topLeft.latitude, visibleRegion.boundingGeoRectangle().bottomRight.longitude, visibleRegion.boundingGeoRectangle().bottomRight.latitude, zoomLevel);
mapModelFiltered.viewChanged(visibleRegion.boundingGeoRectangle().topLeft.longitude, visibleRegion.boundingGeoRectangle().topLeft.latitude, visibleRegion.boundingGeoRectangle().bottomRight.longitude, visibleRegion.boundingGeoRectangle().bottomRight.latitude, zoomLevel);
mapModel.viewChanged(visibleRegion.boundingGeoRectangle().bottomLeft.longitude, visibleRegion.boundingGeoRectangle().bottomRight.longitude);
}
onSupportedMapTypesChanged : {
guiPtr.supportedMapsChanged()
}
}
}
function mapRect() {
if (mapPtr)
return mapPtr.visibleRegion.boundingGeoRectangle();
else
return null;
}
Component {
id: imageComponent
MapQuickItem {
coordinate: position
anchorPoint.x: imageId.width/2
anchorPoint.y: imageId.height/2
zoomLevel: imageZoomLevel
sourceItem: Image {
id: imageId
source: imageData
}
autoFadeIn: false
}
}
Component {
id: polygonComponent
MapPolygon {
border.width: 1
border.color: borderColor
color: fillColor
path: polygon
autoFadeIn: false
}
}
Component {
id: polygonNameComponent
MapQuickItem {
coordinate: position
anchorPoint.x: polygonText.width/2
anchorPoint.y: polygonText.height/2
zoomLevel: mapZoomLevel > 11 ? mapZoomLevel : 11
sourceItem: Grid {
columns: 1
Grid {
//layer.enabled: true
//layer.smooth: true
horizontalItemAlignment: Grid.AlignHCenter
Text {
id: polygonText
text: label
}
}
}
}
}
Component {
id: polylineComponent
MapPolyline {
line.width: 1
line.color: lineColor
path: coordinates
autoFadeIn: false
}
}
Component {
id: polylineNameComponent
MapQuickItem {
coordinate: position
anchorPoint.x: polylineText.width/2
anchorPoint.y: polylineText.height/2
zoomLevel: mapZoomLevel > 11 ? mapZoomLevel : 11
sourceItem: Grid {
columns: 1
Grid {
//layer.enabled: true
//layer.smooth: true
horizontalItemAlignment: Grid.AlignHCenter
Text {
id: polylineText
text: label
}
}
}
}
}
Component {
id: mapComponent
MapQuickItem {
id: mapElement
anchorPoint.x: image.width/2
anchorPoint.y: image.height/2
coordinate: position
zoomLevel: mapZoomLevel > mapImageMinZoom ? mapZoomLevel : mapImageMinZoom
autoFadeIn: false // not in 5.12
sourceItem: Grid {
id: gridItem
columns: 1
Grid {
horizontalItemAlignment: Grid.AlignHCenter
columnSpacing: 5
// This is very slow with lots of items, so we use MSAA for the whole map instead
//layer.enabled: true
//layer.smooth: true
Image {
id: image
rotation: mapImageRotation
source: mapImage
visible: mapImageVisible
MouseArea {
anchors.fill: parent
hoverEnabled: true
acceptedButtons: Qt.LeftButton | Qt.RightButton
onClicked: {
if (mouse.button === Qt.LeftButton) {
selected = !selected
if (selected) {
mapModel.moveToFront(mapModelFiltered.mapRowToSource(index))
}
} else if (mouse.button === Qt.RightButton) {
if (frequency > 0) {
freqMenuItem.text = "Set frequency to " + frequencyString
freqMenuItem.enabled = true
} else {
freqMenuItem.text = "No frequency available"
freqMenuItem.enabled = false
}
var c = mapPtr.toCoordinate(Qt.point(mouse.x, mouse.y))
coordsMenuItem.text = "Coords: " + c.latitude.toFixed(6) + ", " + c.longitude.toFixed(6)
contextMenu.popup()
}
}
}
}
Rectangle {
id: bubble
color: bubbleColour
border.width: 1
width: text.width + 5
height: text.height + 5
radius: 5
visible: mapTextVisible
Text {
id: text
anchors.centerIn: parent
text: mapText
textFormat: TextEdit.RichText
}
MouseArea {
anchors.fill: parent
hoverEnabled: true
acceptedButtons: Qt.LeftButton | Qt.RightButton
onClicked: {
if (mouse.button === Qt.LeftButton) {
selected = !selected
if (selected) {
mapModel.moveToFront(mapModelFiltered.mapRowToSource(index))
}
} else if (mouse.button === Qt.RightButton) {
if (frequency > 0) {
freqMenuItem.text = "Set frequency to " + frequencyString
freqMenuItem.enabled = true
} else {
freqMenuItem.text = "No frequency available"
freqMenuItem.enabled = false
}
var c = mapPtr.toCoordinate(Qt.point(mouse.x, mouse.y))
coordsMenuItem.text = "Coords: " + c.latitude.toFixed(6) + ", " + c.longitude.toFixed(6)
contextMenu.popup()
}
}
Menu {
id: contextMenu
MenuItem {
text: "Set as target"
onTriggered: target = true
}
MenuItem {
id: freqMenuItem
text: "Not set"
onTriggered: mapModel.setFrequency(frequency)
}
MenuItem {
text: "Move to front"
onTriggered: mapModel.moveToFront(mapModelFiltered.mapRowToSource(index))
}
MenuItem {
text: "Move to back"
onTriggered: mapModel.moveToBack(mapModelFiltered.mapRowToSource(index))
}
MenuItem {
text: "Track on 3D map"
onTriggered: mapModel.track3D(mapModelFiltered.mapRowToSource(index))
}
MenuItem {
id: coordsMenuItem
text: ""
}
}
}
}
}
}
}
}
Component {
id: predictedGroundTrack1Component
MapPolyline {
line.width: 2
line.color: predictedGroundTrackColor
path: predictedGroundTrack1
autoFadeIn: false
}
}
// Part of the line that crosses edge of map
Component {
id: predictedGroundTrack2Component
MapPolyline {
line.width: 2
line.color: predictedGroundTrackColor
path: predictedGroundTrack2
autoFadeIn: false
}
}
Component {
id: groundTrack1Component
MapPolyline {
line.width: 2
line.color: groundTrackColor
path: groundTrack1
autoFadeIn: false
}
}
// Part of the line that crosses edge of map
Component {
id: groundTrack2Component
MapPolyline {
line.width: 2
line.color: groundTrackColor
path: groundTrack2
autoFadeIn: false
}
}
}