mirror of
https://github.com/f4exb/sdrangel.git
synced 2024-11-26 17:58:43 -05:00
536 lines
20 KiB
QML
536 lines
20 KiB
QML
|
import QtQuick 2.14
|
||
|
import QtQuick.Window 2.14
|
||
|
import QtQuick.Controls 2.14
|
||
|
import QtPositioning 6.5
|
||
|
import QtLocation 6.5
|
||
|
import Qt5Compat.GraphicalEffects
|
||
|
|
||
|
Item {
|
||
|
id: qmlMap
|
||
|
property int aircraftZoomLevel: 11
|
||
|
property int aircraftMinZoomLevel: 11
|
||
|
property int airportZoomLevel: 11
|
||
|
property string mapProvider: "osm"
|
||
|
property variant mapPtr
|
||
|
property string requestedMapType
|
||
|
property bool lightIcons
|
||
|
property variant guiPtr
|
||
|
property bool smoothing
|
||
|
|
||
|
function createMap(pluginParameters, requestedMap, gui) {
|
||
|
requestedMapType = requestedMap
|
||
|
guiPtr = gui
|
||
|
|
||
|
var paramString = ""
|
||
|
for (var prop in pluginParameters) {
|
||
|
var parameter = 'PluginParameter { name: "' + prop + '"; value: "' + pluginParameters[prop] + '"}'
|
||
|
paramString = paramString + parameter
|
||
|
}
|
||
|
var pluginString = 'import QtLocation 6.5; 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.map.plugin = plugin
|
||
|
mapPtr.map.forceActiveFocus()
|
||
|
return mapPtr
|
||
|
}
|
||
|
|
||
|
Item {
|
||
|
id: page
|
||
|
anchors.fill: parent
|
||
|
}
|
||
|
|
||
|
Component {
|
||
|
id: actualMapComponent
|
||
|
|
||
|
ModifiedMapView {
|
||
|
id: mapView
|
||
|
objectName: "mapView"
|
||
|
anchors.fill: parent
|
||
|
map.center: QtPositioning.coordinate(51.5, 0.125) // London
|
||
|
map.zoomLevel: 10
|
||
|
map.objectName: "map"
|
||
|
|
||
|
// not in 6
|
||
|
//gesture.enabled: true
|
||
|
//gesture.acceptedGestures: MapGestureArea.PinchGesture | MapGestureArea.PanGesture
|
||
|
|
||
|
MouseArea {
|
||
|
anchors.fill: parent
|
||
|
propagateComposedEvents: true
|
||
|
onClicked: {
|
||
|
// Unhighlight current aircraft
|
||
|
guiPtr.clearHighlighted()
|
||
|
mouse.accepted = false
|
||
|
}
|
||
|
}
|
||
|
|
||
|
MapStation {
|
||
|
id: station
|
||
|
objectName: "station"
|
||
|
stationName: "Home"
|
||
|
}
|
||
|
|
||
|
MapItemView {
|
||
|
model: airspaceModel
|
||
|
delegate: airspaceComponent
|
||
|
parent: mapView.map
|
||
|
}
|
||
|
|
||
|
MapItemView {
|
||
|
model: navAidModel
|
||
|
delegate: navAidComponent
|
||
|
parent: mapView.map
|
||
|
}
|
||
|
|
||
|
MapItemView {
|
||
|
model: airspaceModel
|
||
|
delegate: airspaceNameComponent
|
||
|
parent: mapView.map
|
||
|
}
|
||
|
|
||
|
MapItemView {
|
||
|
model: airportModel
|
||
|
delegate: airportComponent
|
||
|
parent: mapView.map
|
||
|
}
|
||
|
|
||
|
// This needs to be before aircraftComponent MapItemView, so it's drawn underneath
|
||
|
MapItemView {
|
||
|
model: aircraftModel
|
||
|
delegate: aircraftPathComponent
|
||
|
parent: mapView.map
|
||
|
}
|
||
|
|
||
|
MapItemView {
|
||
|
model: aircraftModel
|
||
|
delegate: aircraftComponent
|
||
|
parent: mapView.map
|
||
|
}
|
||
|
|
||
|
map.onZoomLevelChanged: {
|
||
|
if (map.zoomLevel > aircraftMinZoomLevel) {
|
||
|
aircraftZoomLevel = map.zoomLevel
|
||
|
} else {
|
||
|
aircraftZoomLevel = aircraftMinZoomLevel
|
||
|
}
|
||
|
if (map.zoomLevel > 11) {
|
||
|
station.zoomLevel = map.zoomLevel
|
||
|
airportZoomLevel = map.zoomLevel
|
||
|
} else {
|
||
|
station.zoomLevel = 11
|
||
|
airportZoomLevel = 11
|
||
|
}
|
||
|
}
|
||
|
|
||
|
map.onSupportedMapTypesChanged : {
|
||
|
for (var i = 0; i < map.supportedMapTypes.length; i++) {
|
||
|
if (requestedMapType == map.supportedMapTypes[i].name) {
|
||
|
map.activeMapType = map.supportedMapTypes[i]
|
||
|
}
|
||
|
}
|
||
|
lightIcons = (requestedMapType == "Night Transit Map") || (requestedMapType == "mapbox://styles/mapbox/dark-v9")
|
||
|
}
|
||
|
|
||
|
}
|
||
|
}
|
||
|
|
||
|
Component {
|
||
|
id: navAidComponent
|
||
|
MapQuickItem {
|
||
|
id: navAid
|
||
|
anchorPoint.x: image.width/2
|
||
|
anchorPoint.y: image.height/2
|
||
|
coordinate: position
|
||
|
zoomLevel: airportZoomLevel
|
||
|
|
||
|
sourceItem: Grid {
|
||
|
columns: 1
|
||
|
Grid {
|
||
|
horizontalItemAlignment: Grid.AlignHCenter
|
||
|
columnSpacing: 5
|
||
|
layer.enabled: smoothing
|
||
|
layer.smooth: smoothing
|
||
|
Image {
|
||
|
id: image
|
||
|
source: navAidImage
|
||
|
visible: !lightIcons
|
||
|
MouseArea {
|
||
|
anchors.fill: parent
|
||
|
onClicked: (mouse) => {
|
||
|
selected = !selected
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
ColorOverlay {
|
||
|
cached: true
|
||
|
width: image.width
|
||
|
height: image.height
|
||
|
source: image
|
||
|
color: "#c0ffffff"
|
||
|
visible: lightIcons
|
||
|
}
|
||
|
Rectangle {
|
||
|
id: bubble
|
||
|
color: bubbleColour
|
||
|
border.width: 1
|
||
|
width: text.width + 5
|
||
|
height: text.height + 5
|
||
|
radius: 5
|
||
|
Text {
|
||
|
id: text
|
||
|
anchors.centerIn: parent
|
||
|
text: navAidData
|
||
|
}
|
||
|
MouseArea {
|
||
|
anchors.fill: parent
|
||
|
hoverEnabled: true
|
||
|
onClicked: (mouse) => {
|
||
|
selected = !selected
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
Component {
|
||
|
id: airspaceComponent
|
||
|
MapPolygon {
|
||
|
border.width: 1
|
||
|
border.color: airspaceBorderColor
|
||
|
color: airspaceFillColor
|
||
|
path: airspacePolygon
|
||
|
}
|
||
|
}
|
||
|
|
||
|
Component {
|
||
|
id: airspaceNameComponent
|
||
|
MapQuickItem {
|
||
|
coordinate: position
|
||
|
anchorPoint.x: airspaceText.width/2
|
||
|
anchorPoint.y: airspaceText.height/2
|
||
|
zoomLevel: airportZoomLevel
|
||
|
sourceItem: Grid {
|
||
|
columns: 1
|
||
|
Grid {
|
||
|
layer.enabled: smoothing
|
||
|
layer.smooth: smoothing
|
||
|
horizontalItemAlignment: Grid.AlignHCenter
|
||
|
Text {
|
||
|
id: airspaceText
|
||
|
text: details
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
Component {
|
||
|
id: aircraftPathComponent
|
||
|
MapPolyline {
|
||
|
line.width: 2
|
||
|
line.color: 'gray'
|
||
|
path: aircraftPath
|
||
|
}
|
||
|
}
|
||
|
|
||
|
Component {
|
||
|
id: aircraftComponent
|
||
|
MapQuickItem {
|
||
|
id: aircraft
|
||
|
anchorPoint.x: image.width/2
|
||
|
anchorPoint.y: image.height/2
|
||
|
coordinate: position
|
||
|
zoomLevel: aircraftZoomLevel
|
||
|
|
||
|
sourceItem: Grid {
|
||
|
columns: 1
|
||
|
Grid {
|
||
|
layer.enabled: smoothing
|
||
|
layer.smooth: smoothing
|
||
|
horizontalItemAlignment: Grid.AlignHCenter
|
||
|
Image {
|
||
|
id: image
|
||
|
rotation: heading
|
||
|
source: aircraftImage
|
||
|
visible: !lightIcons
|
||
|
MouseArea {
|
||
|
anchors.fill: parent
|
||
|
acceptedButtons: Qt.LeftButton | Qt.RightButton
|
||
|
onClicked: {
|
||
|
if (mouse.button === Qt.LeftButton) {
|
||
|
highlighted = true
|
||
|
console.log("z=" + aircraft.sourceItem.z)
|
||
|
aircraft.sourceItem.z = aircraft.sourceItem.z + 1
|
||
|
} else if (mouse.button === Qt.RightButton) {
|
||
|
contextMenu.popup()
|
||
|
}
|
||
|
}
|
||
|
onDoubleClicked: {
|
||
|
target = true
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
ColorOverlay {
|
||
|
cached: true
|
||
|
width: image.width
|
||
|
height: image.height
|
||
|
rotation: heading
|
||
|
source: image
|
||
|
color: "#c0ffffff"
|
||
|
visible: lightIcons
|
||
|
MouseArea {
|
||
|
anchors.fill: parent
|
||
|
onClicked: {
|
||
|
highlighted = true
|
||
|
}
|
||
|
onDoubleClicked: {
|
||
|
target = true
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
Rectangle {
|
||
|
id: bubble
|
||
|
color: bubbleColour
|
||
|
border.width: 1
|
||
|
width: text.width * 1.1
|
||
|
height: text.height * 1.1
|
||
|
radius: 5
|
||
|
Text {
|
||
|
id: text
|
||
|
anchors.centerIn: parent
|
||
|
text: adsbData
|
||
|
textFormat: TextEdit.RichText
|
||
|
}
|
||
|
MouseArea {
|
||
|
anchors.fill: parent
|
||
|
acceptedButtons: Qt.LeftButton | Qt.RightButton
|
||
|
onClicked: {
|
||
|
if (mouse.button === Qt.LeftButton) {
|
||
|
showAll = !showAll
|
||
|
} else if (mouse.button === Qt.RightButton) {
|
||
|
contextMenu.popup()
|
||
|
}
|
||
|
}
|
||
|
Menu {
|
||
|
id: contextMenu
|
||
|
MenuItem {
|
||
|
text: "Set as target"
|
||
|
onTriggered: target = true
|
||
|
}
|
||
|
MenuItem {
|
||
|
text: "Find on feature map"
|
||
|
onTriggered: aircraftModel.findOnMap(index)
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
Component {
|
||
|
id: airportComponent
|
||
|
MapItemGroup {
|
||
|
MapItemGroup {
|
||
|
property var groupVisible: false
|
||
|
id: rangeGroup
|
||
|
MapCircle {
|
||
|
id: circle5nm
|
||
|
center: position
|
||
|
color: "transparent"
|
||
|
border.color: "gray"
|
||
|
radius: 9260 // 5nm in metres
|
||
|
visible: rangeGroup.groupVisible
|
||
|
}
|
||
|
MapCircle {
|
||
|
id: circle10nm
|
||
|
center: position
|
||
|
color: "transparent"
|
||
|
border.color: "gray"
|
||
|
radius: 18520
|
||
|
visible: rangeGroup.groupVisible
|
||
|
}
|
||
|
MapCircle {
|
||
|
id: circle15nm
|
||
|
center: airport.coordinate
|
||
|
color: "transparent"
|
||
|
border.color: "gray"
|
||
|
radius: 27780
|
||
|
visible: rangeGroup.groupVisible
|
||
|
}
|
||
|
MapQuickItem {
|
||
|
id: text5nm
|
||
|
coordinate {
|
||
|
latitude: position.latitude
|
||
|
longitude: position.longitude + (5/60)/Math.cos(Math.abs(position.latitude)*Math.PI/180)
|
||
|
}
|
||
|
anchorPoint.x: 0
|
||
|
anchorPoint.y: height/2
|
||
|
sourceItem: Text {
|
||
|
color: "grey"
|
||
|
text: "5nm"
|
||
|
}
|
||
|
visible: rangeGroup.groupVisible
|
||
|
}
|
||
|
MapQuickItem {
|
||
|
id: text10nm
|
||
|
coordinate {
|
||
|
latitude: position.latitude
|
||
|
longitude: position.longitude + (10/60)/Math.cos(Math.abs(position.latitude)*Math.PI/180)
|
||
|
}
|
||
|
anchorPoint.x: 0
|
||
|
anchorPoint.y: height/2
|
||
|
sourceItem: Text {
|
||
|
color: "grey"
|
||
|
text: "10nm"
|
||
|
}
|
||
|
visible: rangeGroup.groupVisible
|
||
|
}
|
||
|
MapQuickItem {
|
||
|
id: text15nm
|
||
|
coordinate {
|
||
|
latitude: position.latitude
|
||
|
longitude: position.longitude + (15/60)/Math.cos(Math.abs(position.latitude)*Math.PI/180)
|
||
|
}
|
||
|
anchorPoint.x: 0
|
||
|
anchorPoint.y: height/2
|
||
|
sourceItem: Text {
|
||
|
color: "grey"
|
||
|
text: "15nm"
|
||
|
}
|
||
|
visible: rangeGroup.groupVisible
|
||
|
}
|
||
|
}
|
||
|
|
||
|
MapQuickItem {
|
||
|
id: airport
|
||
|
anchorPoint.x: image.width/2
|
||
|
anchorPoint.y: image.height/2
|
||
|
coordinate: position
|
||
|
zoomLevel: airportZoomLevel
|
||
|
sourceItem: Grid {
|
||
|
columns: 1
|
||
|
Grid {
|
||
|
horizontalItemAlignment: Grid.AlignHCenter
|
||
|
layer.enabled: smoothing
|
||
|
layer.smooth: smoothing
|
||
|
Image {
|
||
|
id: image
|
||
|
source: airportImage
|
||
|
visible: !lightIcons
|
||
|
MouseArea {
|
||
|
anchors.fill: parent
|
||
|
acceptedButtons: Qt.LeftButton | Qt.RightButton
|
||
|
onClicked: (mouse) => {
|
||
|
if (mouse.button === Qt.RightButton) {
|
||
|
showRangeItem.visible = !rangeGroup.groupVisible
|
||
|
hideRangeItem.visible = rangeGroup.groupVisible
|
||
|
menuItems.clear()
|
||
|
var scanners = airportModel.getFreqScanners()
|
||
|
for (var i = 0; i < scanners.length; i++) {
|
||
|
menuItems.append({
|
||
|
text: "Send to Frequency Scanner " + scanners[i],
|
||
|
airport: index,
|
||
|
scanner: scanners[i]
|
||
|
})
|
||
|
}
|
||
|
contextMenu.popup()
|
||
|
}
|
||
|
}
|
||
|
onDoubleClicked: (mouse) => {
|
||
|
rangeGroup.groupVisible = !rangeGroup.groupVisible
|
||
|
}
|
||
|
|
||
|
ListModel {
|
||
|
id: menuItems
|
||
|
}
|
||
|
|
||
|
Menu {
|
||
|
id: contextMenu
|
||
|
MenuItem {
|
||
|
id: showRangeItem
|
||
|
text: "Show range rings"
|
||
|
onTriggered: rangeGroup.groupVisible = true
|
||
|
height: visible ? implicitHeight : 0
|
||
|
}
|
||
|
MenuItem {
|
||
|
id: hideRangeItem
|
||
|
text: "Hide range rings"
|
||
|
onTriggered: rangeGroup.groupVisible = false
|
||
|
height: visible ? implicitHeight : 0
|
||
|
}
|
||
|
Instantiator {
|
||
|
model: menuItems
|
||
|
MenuItem {
|
||
|
text: model.text
|
||
|
onTriggered: airportModel.sendToFreqScanner(model.airport, model.scanner)
|
||
|
}
|
||
|
onObjectAdded: function(index, object) {
|
||
|
contextMenu.insertItem(index, object)
|
||
|
}
|
||
|
onObjectRemoved: function(index, object) {
|
||
|
contextMenu.removeItem(object)
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
ColorOverlay {
|
||
|
cached: true
|
||
|
width: image.width
|
||
|
height: image.height
|
||
|
source: image
|
||
|
color: "#c0ffffff"
|
||
|
visible: lightIcons
|
||
|
}
|
||
|
Rectangle {
|
||
|
id: bubble
|
||
|
color: bubbleColour
|
||
|
border.width: 1
|
||
|
width: text.width + 5
|
||
|
height: text.height + 5
|
||
|
radius: 5
|
||
|
Text {
|
||
|
id: text
|
||
|
anchors.centerIn: parent
|
||
|
text: airportData
|
||
|
}
|
||
|
MouseArea {
|
||
|
anchors.fill: parent
|
||
|
onClicked: (mouse) => {
|
||
|
if (showFreq) {
|
||
|
var freqIdx = Math.floor((mouse.y-5)/((height-10)/airportDataRows))
|
||
|
if (freqIdx == 0) {
|
||
|
showFreq = false
|
||
|
}
|
||
|
} else {
|
||
|
showFreq = true
|
||
|
}
|
||
|
}
|
||
|
onDoubleClicked: (mouse) => {
|
||
|
if (showFreq) {
|
||
|
var freqIdx = Math.floor((mouse.y-5)/((height-10)/airportDataRows))
|
||
|
if (freqIdx != 0) {
|
||
|
selectedFreq = freqIdx - 1
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
}
|