mirror of
https://github.com/f4exb/sdrangel.git
synced 2024-12-23 01:55:48 -05:00
Map updates.
Add support for different map types (street/satellite) and different map providers. Support finding real world addresses on the map. Add Maidenhead locator converter. Add Beacons. Allow data sources to be selected by a user. Add context menu to allow setting an object as a target, setting center frequency and adjusting display order.
This commit is contained in:
parent
c8d07396d2
commit
446749cbbb
@ -308,6 +308,7 @@ if (BUILD_GUI)
|
||||
find_package(Qt5 COMPONENTS Quick)
|
||||
find_package(Qt5 COMPONENTS QuickWidgets)
|
||||
find_package(Qt5 COMPONENTS Positioning)
|
||||
find_package(Qt5 COMPONENTS Location)
|
||||
find_package(Qt5 COMPONENTS Charts)
|
||||
endif()
|
||||
|
||||
|
Binary file not shown.
Before Width: | Height: | Size: 1.4 MiB After Width: | Height: | Size: 545 KiB |
BIN
doc/img/Map_plugin_beacon_dialog.png
Normal file
BIN
doc/img/Map_plugin_beacon_dialog.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 29 KiB |
BIN
doc/img/Map_plugin_beacons.png
Normal file
BIN
doc/img/Map_plugin_beacons.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 547 KiB |
@ -86,8 +86,3 @@ target_link_libraries(${TARGET_NAME}
|
||||
|
||||
install(TARGETS ${TARGET_NAME} DESTINATION ${INSTALL_FOLDER})
|
||||
|
||||
if(WIN32)
|
||||
# Run deployqt for QtQuick etc
|
||||
include(DeployQt)
|
||||
windeployqt(${TARGET_NAME} ${SDRANGEL_BINARY_BIN_DIR} ${PROJECT_SOURCE_DIR}/map)
|
||||
endif()
|
||||
|
@ -468,6 +468,7 @@ void ADSBDemodGUI::updatePosition(Aircraft *aircraft)
|
||||
swgMapItem->setName(new QString(QString("%1").arg(aircraft->m_icao, 0, 16)));
|
||||
swgMapItem->setLatitude(aircraft->m_latitude);
|
||||
swgMapItem->setLongitude(aircraft->m_longitude);
|
||||
swgMapItem->setAltitude(Units::feetToMetres(aircraft->m_altitude));
|
||||
swgMapItem->setImage(new QString(QString("qrc:///map/%1").arg(aircraft->getImage())));
|
||||
swgMapItem->setImageRotation(aircraft->m_heading);
|
||||
swgMapItem->setText(new QString(aircraft->getText(true)));
|
||||
|
@ -22,7 +22,7 @@
|
||||
#include <QHash>
|
||||
|
||||
// Extract string from CSV line, updating pp to next column
|
||||
static inline char *csvNext(char **pp)
|
||||
static inline char *csvNext(char **pp, char delimiter=',')
|
||||
{
|
||||
char *p = *pp;
|
||||
|
||||
@ -31,7 +31,7 @@ static inline char *csvNext(char **pp)
|
||||
|
||||
char *start = p;
|
||||
|
||||
while ((*p != ',') && (*p != '\n'))
|
||||
while ((*p != delimiter) && (*p != '\n'))
|
||||
p++;
|
||||
*p++ = '\0';
|
||||
*pp = p;
|
||||
|
@ -4,8 +4,10 @@ if (Qt5SerialPort_FOUND)
|
||||
add_subdirectory(gs232controller)
|
||||
endif()
|
||||
|
||||
if (Qt5Quick_FOUND AND Qt5QuickWidgets_FOUND AND Qt5Positioning_FOUND)
|
||||
if (Qt5Quick_FOUND AND Qt5QuickWidgets_FOUND AND Qt5Positioning_FOUND AND Qt5Location_FOUND)
|
||||
add_subdirectory(map)
|
||||
endif()
|
||||
if (Qt5Quick_FOUND AND Qt5QuickWidgets_FOUND AND Qt5Positioning_FOUND)
|
||||
add_subdirectory(vorlocalizer)
|
||||
endif()
|
||||
|
||||
|
@ -354,6 +354,7 @@ bool APRSGUI::handleMessage(const Message& message)
|
||||
swgMapItem->setName(new QString(aprs->m_from));
|
||||
swgMapItem->setLatitude(aprs->m_latitude);
|
||||
swgMapItem->setLongitude(aprs->m_longitude);
|
||||
swgMapItem->setAltitude(aprs->m_hasAltitude ? Units::feetToMetres(aprs->m_altitudeFt) : 0);
|
||||
if (aprs->m_objectKilled)
|
||||
{
|
||||
swgMapItem->setImage(new QString(""));
|
||||
@ -364,7 +365,7 @@ bool APRSGUI::handleMessage(const Message& message)
|
||||
swgMapItem->setImage(new QString(QString("qrc:///%1").arg(aprs->m_symbolImage)));
|
||||
swgMapItem->setText(new QString(aprs->toText()));
|
||||
}
|
||||
swgMapItem->setImageFixedSize(0);
|
||||
swgMapItem->setImageMinZoom(11);
|
||||
|
||||
MainCore::MsgMapItem *msg = MainCore::MsgMapItem::create(m_aprs, swgMapItem);
|
||||
(*it)->push(msg);
|
||||
|
@ -25,11 +25,13 @@
|
||||
|
||||
const QStringList GS232ControllerSettings::m_pipeTypes = {
|
||||
QStringLiteral("ADSBDemod"),
|
||||
QStringLiteral("Map"),
|
||||
QStringLiteral("StarTracker")
|
||||
};
|
||||
|
||||
const QStringList GS232ControllerSettings::m_pipeURIs = {
|
||||
QStringLiteral("sdrangel.channel.adsbdemod"),
|
||||
QStringLiteral("sdrangel.feature.map"),
|
||||
QStringLiteral("sdrangel.feature.startracker")
|
||||
};
|
||||
|
||||
|
@ -4,7 +4,7 @@
|
||||
|
||||
The GS-232 Rotator Controller feature plugin allows SDRangel to send commands to GS-232 rotators. This allows SDRangel to point antennas mounted on a rotator to a specified azimuth and elevation.
|
||||
|
||||
Azimuth and elevation can be set manually by a user in the GUI, via the REST API, or via another plugin, such as the ADS-B Demodulator, which can track a selected aircraft, or the Star Tracker, for radio astronomy or EME communication.
|
||||
Azimuth and elevation can be set manually by a user in the GUI, via the REST API, or via another plugin, such as the Map Feature, the ADS-B Demodulator, or the Star Tracker.
|
||||
|
||||
<h2>Interface</h2>
|
||||
|
||||
|
@ -13,6 +13,7 @@ set(map_HEADERS
|
||||
mapplugin.h
|
||||
mapreport.h
|
||||
mapwebapiadapter.h
|
||||
beacon.h
|
||||
)
|
||||
|
||||
include_directories(
|
||||
@ -24,15 +25,27 @@ if(NOT SERVER_MODE)
|
||||
${map_SOURCES}
|
||||
mapgui.cpp
|
||||
mapgui.ui
|
||||
maplocationdialog.cpp
|
||||
maplocationdialog.ui
|
||||
mapmaidenheaddialog.cpp
|
||||
mapmaidenheaddialog.ui
|
||||
mapsettingsdialog.cpp
|
||||
mapsettingsdialog.ui
|
||||
mapbeacondialog.cpp
|
||||
mapbeacondialog.ui
|
||||
map.qrc
|
||||
)
|
||||
set(map_HEADERS
|
||||
${map_HEADERS}
|
||||
mapgui.h
|
||||
maplocationdialog.h
|
||||
mapmaidenheaddialog.h
|
||||
mapsettingsdialog.h
|
||||
mapbeacondialog.h
|
||||
)
|
||||
|
||||
set(TARGET_NAME map)
|
||||
set(TARGET_LIB "Qt5::Widgets" Qt5::Quick Qt5::QuickWidgets Qt5::Positioning)
|
||||
set(TARGET_LIB "Qt5::Widgets" Qt5::Quick Qt5::QuickWidgets Qt5::Positioning Qt5::Location)
|
||||
set(TARGET_LIB_GUI "sdrgui")
|
||||
set(INSTALL_FOLDER ${INSTALL_PLUGINS_DIR})
|
||||
else()
|
||||
@ -54,3 +67,9 @@ target_link_libraries(${TARGET_NAME}
|
||||
)
|
||||
|
||||
install(TARGETS ${TARGET_NAME} DESTINATION ${INSTALL_FOLDER})
|
||||
|
||||
if(WIN32)
|
||||
# Run deployqt for QtQuick etc
|
||||
include(DeployQt)
|
||||
windeployqt(${TARGET_NAME} ${SDRANGEL_BINARY_BIN_DIR} ${PROJECT_SOURCE_DIR}/map)
|
||||
endif()
|
||||
|
238
plugins/feature/map/beacon.h
Normal file
238
plugins/feature/map/beacon.h
Normal file
@ -0,0 +1,238 @@
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (C) 2021 Jon Beniston, M7RCE //
|
||||
// //
|
||||
// This program is free software; you can redistribute it and/or modify //
|
||||
// it under the terms of the GNU General Public License as published by //
|
||||
// the Free Software Foundation as version 3 of the License, or //
|
||||
// (at your option) any later version. //
|
||||
// //
|
||||
// This program is distributed in the hope that it will be useful, //
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of //
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
|
||||
// GNU General Public License V3 for more details. //
|
||||
// //
|
||||
// You should have received a copy of the GNU General Public License //
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>. //
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifndef INCLUDE_BEACON_H
|
||||
#define INCLUDE_BEACON_H
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <QString>
|
||||
#include <QList>
|
||||
|
||||
#include "util/units.h"
|
||||
#include "util/maidenhead.h"
|
||||
#include "../../channelrx/demodadsb/csv.h"
|
||||
|
||||
#define IARU_BEACONS_URL "https://iaru-r1-c5-beacons.org/wp-content/uploads/beacons.csv"
|
||||
|
||||
struct Beacon {
|
||||
|
||||
QString m_callsign;
|
||||
quint64 m_frequency; // In Hz
|
||||
QString m_locator;
|
||||
float m_latitude;
|
||||
float m_longitude;
|
||||
float m_altitude; // In metres above sea-level
|
||||
QString m_power; // In Watts - sometimes a string with extra infos
|
||||
QString m_polarization; // H or V
|
||||
QString m_pattern; // Omni or 30deg etc
|
||||
QString m_key; // F1A, F1B
|
||||
QString m_mgm; // Machine mode
|
||||
|
||||
QString getText()
|
||||
{
|
||||
QStringList list;
|
||||
list.append("Beacon");
|
||||
list.append(QString("Callsign: %1").arg(m_callsign));
|
||||
list.append(QString("Frequency: %1").arg(getFrequencyText()));
|
||||
if (!m_power.isEmpty())
|
||||
list.append(QString("Power: %1 Watts ERP").arg(m_power));
|
||||
if (!m_polarization.isEmpty())
|
||||
list.append(QString("Polarization: %1").arg(m_polarization));
|
||||
if (!m_pattern.isEmpty())
|
||||
list.append(QString("Pattern: %1").arg(m_pattern));
|
||||
if (!m_key.isEmpty())
|
||||
list.append(QString("Key: %1").arg(m_key));
|
||||
if (!m_mgm.isEmpty())
|
||||
list.append(QString("MGM: %1").arg(m_mgm));
|
||||
list.append(QString("Locator: %1").arg(m_locator));
|
||||
return list.join("\n");
|
||||
}
|
||||
|
||||
QString getFrequencyText()
|
||||
{
|
||||
if (m_frequency > 1000000000)
|
||||
return QString("%1 GHz").arg(m_frequency/1000000000.0, 0, ',', 6);
|
||||
else if (m_frequency > 1000000)
|
||||
return QString("%1 MHz").arg(m_frequency/1000000.0, 0, ',', 3);
|
||||
else
|
||||
return QString("%1 kHz").arg(m_frequency/1000.0, 0, ',', 3);
|
||||
}
|
||||
|
||||
// Uses ; rather than ,
|
||||
static QList<Beacon *> *readIARUCSV(const QString &filename)
|
||||
{
|
||||
int cnt = 0;
|
||||
QList<Beacon *> *beacons = nullptr;
|
||||
|
||||
// Column numbers used for the data as of 2021/1/20
|
||||
int callsignCol = 0;
|
||||
int qrgCol = 1;
|
||||
int locatorCol = 2;
|
||||
int heightCol = 5;
|
||||
int patternCol = 7;
|
||||
int polarizationCol = 9;
|
||||
int powerCol = 10;
|
||||
int keyCol = 11;
|
||||
int mgmCol = 12;
|
||||
|
||||
FILE *file;
|
||||
QByteArray utfFilename = filename.toUtf8();
|
||||
if ((file = fopen(utfFilename.constData(), "r")) != nullptr)
|
||||
{
|
||||
char row[2048];
|
||||
|
||||
if (fgets(row, sizeof(row), file))
|
||||
{
|
||||
beacons = new QList<Beacon *>();
|
||||
|
||||
// Read header
|
||||
int idx = 0;
|
||||
char *p = strtok(row, ";");
|
||||
while (p != nullptr)
|
||||
{
|
||||
if (!strcmp(p, "Callsign"))
|
||||
callsignCol = idx;
|
||||
else if (!strcmp(p, "QRG"))
|
||||
qrgCol = idx;
|
||||
else if (!strcmp(p, "Locator"))
|
||||
locatorCol = idx;
|
||||
else if (!strcmp(p, "Hight ASL") || !strcmp(p, "Height ASL"))
|
||||
heightCol = idx;
|
||||
else if (!strcmp(p, "Pattern"))
|
||||
patternCol = idx;
|
||||
else if (!strcmp(p, "H/V"))
|
||||
polarizationCol = idx;
|
||||
else if (!strcmp(p, "Power"))
|
||||
powerCol = idx;
|
||||
else if (!strcmp(p, "Keying"))
|
||||
keyCol = idx;
|
||||
else if (!strcmp(p, "MGM"))
|
||||
mgmCol = idx;
|
||||
p = strtok(nullptr, ",");
|
||||
idx++;
|
||||
}
|
||||
// Read data
|
||||
while (fgets(row, sizeof(row), file))
|
||||
{
|
||||
int id = 0;
|
||||
char *callsign = nullptr;
|
||||
size_t callsignLen = 0;
|
||||
char *frequencyString = nullptr;
|
||||
quint64 frequency;
|
||||
char *locator = nullptr;
|
||||
int height = 0;
|
||||
char *heightString = nullptr;
|
||||
char *pattern = nullptr;
|
||||
char *polarization = nullptr;
|
||||
char *power = nullptr;
|
||||
char *key = nullptr;
|
||||
char *mgm = nullptr;
|
||||
|
||||
char *q = row;
|
||||
idx = 0;
|
||||
while ((p = csvNext(&q, ';')) != nullptr)
|
||||
{
|
||||
// Read strings, stripping quotes
|
||||
if ((idx == callsignCol) && (p[0] == '\"'))
|
||||
{
|
||||
callsign = p+1;
|
||||
callsignLen = strlen(callsign)-1;
|
||||
callsign[callsignLen] = '\0';
|
||||
}
|
||||
else if ((idx == qrgCol) && (p[0] == '\"'))
|
||||
{
|
||||
frequencyString = p+1;
|
||||
frequencyString[strlen(frequencyString)-1] = '\0';
|
||||
frequency = QString(frequencyString).toLongLong();
|
||||
}
|
||||
else if ((idx == locatorCol) && (p[0] == '\"'))
|
||||
{
|
||||
locator = p+1;
|
||||
locator[strlen(locator)-1] = '\0';
|
||||
}
|
||||
else if ((idx == heightCol) && (p[0] == '\"'))
|
||||
{
|
||||
heightString = p+1;
|
||||
heightString[strlen(heightString)-1] = '\0';
|
||||
height = atoi(heightString);
|
||||
}
|
||||
else if ((idx == patternCol) && (p[0] == '\"'))
|
||||
{
|
||||
pattern = p+1;
|
||||
pattern[strlen(pattern)-1] = '\0';
|
||||
}
|
||||
else if ((idx == polarizationCol) && (p[0] == '\"'))
|
||||
{
|
||||
polarization = p+1;
|
||||
polarization[strlen(polarization)-1] = '\0';
|
||||
}
|
||||
else if ((idx == powerCol) && (p[0] == '\"'))
|
||||
{
|
||||
power = p+1;
|
||||
power[strlen(power)-1] = '\0';
|
||||
}
|
||||
else if ((idx == keyCol) && (p[0] == '\"'))
|
||||
{
|
||||
key = p+1;
|
||||
key[strlen(key)-1] = '\0';
|
||||
}
|
||||
else if ((idx == mgmCol) && (p[0] == '\"'))
|
||||
{
|
||||
mgm = p+1;
|
||||
mgm[strlen(mgm)-1] = '\0';
|
||||
}
|
||||
idx++;
|
||||
}
|
||||
float latitude, longitude;
|
||||
if (callsign && frequency && locator && Maidenhead::fromMaidenhead(locator, latitude, longitude))
|
||||
{
|
||||
Beacon *beacon = new Beacon();
|
||||
beacon->m_callsign = callsign;
|
||||
beacon->m_frequency = frequency * 1000; // kHz to Hz
|
||||
beacon->m_locator = locator;
|
||||
beacon->m_latitude = latitude;
|
||||
beacon->m_longitude = longitude;
|
||||
beacon->m_altitude = height;
|
||||
if (!QString("omni").compare(pattern, Qt::CaseInsensitive))
|
||||
beacon->m_pattern = "Omni"; // Eliminate usage of mixed case
|
||||
else
|
||||
beacon->m_pattern = pattern;
|
||||
beacon->m_polarization = polarization;
|
||||
beacon->m_power = power;
|
||||
beacon->m_key = key;
|
||||
beacon->m_mgm = mgm;
|
||||
|
||||
beacons->append(beacon);
|
||||
cnt++;
|
||||
}
|
||||
}
|
||||
}
|
||||
fclose(file);
|
||||
}
|
||||
else
|
||||
qDebug() << "Beacon::readIARUCSV: Failed to open " << filename;
|
||||
|
||||
qDebug() << "Beacon::readIARUCSV: Read " << cnt << " beacons";
|
||||
|
||||
return beacons;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
#endif // INCLUDE_BEACON_H
|
@ -1,7 +1,6 @@
|
||||
<RCC>
|
||||
<qresource prefix="/map/">
|
||||
<file>map/map.qml</file>
|
||||
<file>map/MapStation.qml</file>
|
||||
<file>map/antenna.png</file>
|
||||
</qresource>
|
||||
</RCC>
|
||||
|
@ -1,40 +0,0 @@
|
||||
import QtQuick 2.12
|
||||
import QtLocation 5.12
|
||||
import QtPositioning 5.12
|
||||
|
||||
MapQuickItem {
|
||||
id: station
|
||||
property string stationName // Name of the station, E.g. Home
|
||||
|
||||
coordinate: QtPositioning.coordinate(51.5, 0.125) // Location of the antenna (QTH) - London
|
||||
zoomLevel: 11
|
||||
|
||||
anchorPoint.x: image.width/2
|
||||
anchorPoint.y: image.height/2
|
||||
|
||||
sourceItem: Grid {
|
||||
columns: 1
|
||||
Grid {
|
||||
horizontalItemAlignment: Grid.AlignHCenter
|
||||
layer.enabled: true
|
||||
layer.smooth: true
|
||||
Image {
|
||||
id: image
|
||||
source: "antenna.png"
|
||||
}
|
||||
Rectangle {
|
||||
id: bubble
|
||||
color: "lightblue"
|
||||
border.width: 1
|
||||
width: text.width * 1.3
|
||||
height: text.height * 1.3
|
||||
radius: 5
|
||||
Text {
|
||||
id: text
|
||||
anchors.centerIn: parent
|
||||
text: stationName
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,48 +1,82 @@
|
||||
import QtQuick 2.12
|
||||
import QtQuick.Window 2.12
|
||||
import QtQuick.Controls 2.12
|
||||
import QtLocation 5.12
|
||||
import QtPositioning 5.12
|
||||
|
||||
Item {
|
||||
id: qmlMap
|
||||
property int mapZoomLevel: 11
|
||||
property string mapProvider: "osm"
|
||||
property variant mapParameters
|
||||
property variant mapPtr
|
||||
|
||||
Plugin {
|
||||
id: mapPlugin
|
||||
name: "osm"
|
||||
function createMap(pluginParameters) {
|
||||
var parameters = new Array()
|
||||
for (var prop in pluginParameters) {
|
||||
var parameter = Qt.createQmlObject('import QtLocation 5.6; PluginParameter{ name: "'+ prop + '"; value: "' + pluginParameters[prop]+'"}', qmlMap)
|
||||
parameters.push(parameter)
|
||||
}
|
||||
qmlMap.mapParameters = parameters
|
||||
|
||||
var plugin
|
||||
if (mapParameters && mapParameters.length > 0)
|
||||
plugin = Qt.createQmlObject ('import QtLocation 5.12; Plugin{ name:"' + mapProvider + '"; parameters: qmlMap.mapParameters}', qmlMap)
|
||||
else
|
||||
plugin = Qt.createQmlObject ('import QtLocation 5.12; Plugin{ name:"' + mapProvider + '"}', qmlMap)
|
||||
if (mapPtr) {
|
||||
// Objects aren't destroyed immediately, so rename the old
|
||||
// map, so any C++ code that calls findChild("map") doesn't find
|
||||
// the old map
|
||||
mapPtr.objectName = "oldMap";
|
||||
mapPtr.destroy()
|
||||
}
|
||||
mapPtr = actualMapComponent.createObject(page)
|
||||
mapPtr.plugin = plugin;
|
||||
mapPtr.forceActiveFocus()
|
||||
mapPtr.objectName = "map";
|
||||
}
|
||||
|
||||
Map {
|
||||
id: map
|
||||
objectName: "map"
|
||||
anchors.fill: parent
|
||||
plugin: mapPlugin
|
||||
center: QtPositioning.coordinate(51.5, 0.125) // London
|
||||
zoomLevel: 10
|
||||
|
||||
|
||||
MapItemView {
|
||||
model: mapModel
|
||||
delegate: mapComponent
|
||||
}
|
||||
|
||||
MapStation {
|
||||
id: station
|
||||
objectName: "station"
|
||||
stationName: "Home"
|
||||
coordinate: QtPositioning.coordinate(51.5, 0.125)
|
||||
}
|
||||
|
||||
onZoomLevelChanged: {
|
||||
if (zoomLevel > 11) {
|
||||
station.zoomLevel = zoomLevel
|
||||
mapZoomLevel = zoomLevel
|
||||
} else {
|
||||
station.zoomLevel = 11
|
||||
mapZoomLevel = 11
|
||||
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)
|
||||
mapPtr.activeMapType = mapPtr.supportedMapTypes[mapTypeIndex]
|
||||
}
|
||||
|
||||
Item {
|
||||
id: page
|
||||
anchors.fill: parent
|
||||
}
|
||||
|
||||
Component {
|
||||
id: actualMapComponent
|
||||
|
||||
Map {
|
||||
id: map
|
||||
anchors.fill: parent
|
||||
center: QtPositioning.coordinate(51.5, 0.125) // London
|
||||
zoomLevel: 10
|
||||
|
||||
MapItemView {
|
||||
model: mapModel
|
||||
delegate: mapComponent
|
||||
}
|
||||
|
||||
onZoomLevelChanged: {
|
||||
mapZoomLevel = zoomLevel
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
@ -52,9 +86,10 @@ Item {
|
||||
anchorPoint.x: image.width/2
|
||||
anchorPoint.y: image.height/2
|
||||
coordinate: position
|
||||
zoomLevel: mapImageFixedSize ? zoomLevel : mapZoomLevel
|
||||
zoomLevel: mapZoomLevel > mapImageMinZoom ? mapZoomLevel : mapImageMinZoom
|
||||
|
||||
sourceItem: Grid {
|
||||
id: gridItem
|
||||
columns: 1
|
||||
Grid {
|
||||
horizontalItemAlignment: Grid.AlignHCenter
|
||||
@ -65,11 +100,27 @@ Item {
|
||||
id: image
|
||||
rotation: mapImageRotation
|
||||
source: mapImage
|
||||
visible: mapImageVisible
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
onClicked: (mouse) => {
|
||||
selected = !selected
|
||||
acceptedButtons: Qt.LeftButton | Qt.RightButton
|
||||
onClicked: {
|
||||
if (mouse.button === Qt.LeftButton) {
|
||||
selected = !selected
|
||||
if (selected) {
|
||||
mapModel.moveToFront(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
|
||||
}
|
||||
contextMenu.popup()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -89,8 +140,43 @@ Item {
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
onClicked: (mouse) => {
|
||||
selected = !selected
|
||||
acceptedButtons: Qt.LeftButton | Qt.RightButton
|
||||
onClicked: {
|
||||
if (mouse.button === Qt.LeftButton) {
|
||||
selected = !selected
|
||||
if (selected) {
|
||||
mapModel.moveToFront(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
|
||||
}
|
||||
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(index)
|
||||
}
|
||||
MenuItem {
|
||||
text: "Move to back"
|
||||
onTriggered: mapModel.moveToBack(index)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
221
plugins/feature/map/mapbeacondialog.cpp
Normal file
221
plugins/feature/map/mapbeacondialog.cpp
Normal file
@ -0,0 +1,221 @@
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (C) 2021 Jon Beniston, M7RCE //
|
||||
// //
|
||||
// This program is free software; you can redistribute it and/or modify //
|
||||
// it under the terms of the GNU General Public License as published by //
|
||||
// the Free Software Foundation as version 3 of the License, or //
|
||||
// (at your option) any later version. //
|
||||
// //
|
||||
// This program is distributed in the hope that it will be useful, //
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of //
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
|
||||
// GNU General Public License V3 for more details. //
|
||||
// //
|
||||
// You should have received a copy of the GNU General Public License //
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>. //
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "mapbeacondialog.h"
|
||||
|
||||
#include <QDebug>
|
||||
#include <QUrl>
|
||||
#include <QMessageBox>
|
||||
|
||||
#include "channel/channelwebapiutils.h"
|
||||
#include "mapgui.h"
|
||||
|
||||
MapBeaconDialog::MapBeaconDialog(MapGUI *gui, QWidget* parent) :
|
||||
QDialog(parent),
|
||||
m_gui(gui),
|
||||
ui(new Ui::MapBeaconDialog),
|
||||
m_progressDialog(nullptr)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
connect(&m_dlm, &HttpDownloadManager::downloadComplete, this, &MapBeaconDialog::downloadFinished);
|
||||
}
|
||||
|
||||
MapBeaconDialog::~MapBeaconDialog()
|
||||
{
|
||||
delete ui;
|
||||
}
|
||||
|
||||
void MapBeaconDialog::updateTable()
|
||||
{
|
||||
AzEl azEl = *m_gui->getAzEl();
|
||||
ui->beacons->setSortingEnabled(false);
|
||||
ui->beacons->setRowCount(0);
|
||||
QList<Beacon *> *beacons = m_gui->getBeacons();
|
||||
if (beacons != nullptr)
|
||||
{
|
||||
ui->beacons->setRowCount(beacons->size());
|
||||
QListIterator<Beacon *> i(*beacons);
|
||||
int row = 0;
|
||||
while (i.hasNext())
|
||||
{
|
||||
Beacon *beacon = i.next();
|
||||
ui->beacons->setItem(row, BEACON_COL_CALLSIGN, new QTableWidgetItem(beacon->m_callsign));
|
||||
QTableWidgetItem *freq = new QTableWidgetItem();
|
||||
freq->setText(beacon->getFrequencyText());
|
||||
freq->setData(Qt::UserRole, beacon->m_frequency);
|
||||
ui->beacons->setItem(row, BEACON_COL_FREQUENCY, freq);
|
||||
ui->beacons->setItem(row, BEACON_COL_LOCATION, new QTableWidgetItem(beacon->m_locator));
|
||||
ui->beacons->setItem(row, BEACON_COL_POWER, new QTableWidgetItem(beacon->m_power));
|
||||
ui->beacons->setItem(row, BEACON_COL_POLARIZATION, new QTableWidgetItem(beacon->m_polarization));
|
||||
ui->beacons->setItem(row, BEACON_COL_PATTERN, new QTableWidgetItem(beacon->m_pattern));
|
||||
ui->beacons->setItem(row, BEACON_COL_KEY, new QTableWidgetItem(beacon->m_key));
|
||||
ui->beacons->setItem(row, BEACON_COL_MGM, new QTableWidgetItem(beacon->m_mgm));
|
||||
azEl.setTarget(beacon->m_latitude, beacon->m_longitude, beacon->m_altitude);
|
||||
azEl.calculate();
|
||||
ui->beacons->setItem(row, BEACON_COL_AZIMUTH, new QTableWidgetItem(QString("%1").arg(round(azEl.getAzimuth()))));
|
||||
ui->beacons->setItem(row, BEACON_COL_ELEVATION, new QTableWidgetItem(QString("%1").arg(round(azEl.getElevation()))));
|
||||
int km = round(azEl.getDistance()/1000);
|
||||
QTableWidgetItem *dist = new QTableWidgetItem();
|
||||
dist->setData(Qt::DisplayRole, km);
|
||||
ui->beacons->setItem(row, BEACON_COL_DISTANCE, dist);
|
||||
row++;
|
||||
}
|
||||
}
|
||||
ui->beacons->setSortingEnabled(true);
|
||||
ui->beacons->resizeColumnsToContents();
|
||||
}
|
||||
|
||||
qint64 MapBeaconDialog::fileAgeInDays(QString filename)
|
||||
{
|
||||
QFile file(filename);
|
||||
if (file.exists())
|
||||
{
|
||||
QDateTime modified = file.fileTime(QFileDevice::FileModificationTime);
|
||||
if (modified.isValid())
|
||||
return modified.daysTo(QDateTime::currentDateTime());
|
||||
else
|
||||
return -1;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
bool MapBeaconDialog::confirmDownload(QString filename)
|
||||
{
|
||||
qint64 age = fileAgeInDays(filename);
|
||||
if ((age == -1) || (age > 100))
|
||||
return true;
|
||||
else
|
||||
{
|
||||
QMessageBox::StandardButton reply;
|
||||
if (age == 0)
|
||||
reply = QMessageBox::question(this, "Confirm download", "This file was last downloaded today. Are you sure you wish to redownload it?", QMessageBox::Yes|QMessageBox::No);
|
||||
else if (age == 1)
|
||||
reply = QMessageBox::question(this, "Confirm download", "This file was last downloaded yesterday. Are you sure you wish to redownload it?", QMessageBox::Yes|QMessageBox::No);
|
||||
else
|
||||
reply = QMessageBox::question(this, "Confirm download", QString("This file was last downloaded %1 days ago. Are you sure you wish to redownload this file?").arg(age), QMessageBox::Yes|QMessageBox::No);
|
||||
return reply == QMessageBox::Yes;
|
||||
}
|
||||
}
|
||||
|
||||
void MapBeaconDialog::updateDownloadProgress(qint64 bytesRead, qint64 totalBytes)
|
||||
{
|
||||
m_progressDialog->setMaximum(totalBytes);
|
||||
m_progressDialog->setValue(bytesRead);
|
||||
}
|
||||
|
||||
void MapBeaconDialog::accept()
|
||||
{
|
||||
QDialog::accept();
|
||||
}
|
||||
|
||||
void MapBeaconDialog::on_downloadIARU_clicked()
|
||||
{
|
||||
if (m_progressDialog == nullptr)
|
||||
{
|
||||
QString beaconFile = MapGUI::getBeaconFilename();
|
||||
if (confirmDownload(beaconFile))
|
||||
{
|
||||
// Download IARU beacons database to disk
|
||||
QUrl dbURL(QString(IARU_BEACONS_URL));
|
||||
m_progressDialog = new QProgressDialog(this);
|
||||
m_progressDialog->setAttribute(Qt::WA_DeleteOnClose);
|
||||
m_progressDialog->setCancelButton(nullptr);
|
||||
m_progressDialog->setMinimumDuration(500);
|
||||
m_progressDialog->setLabelText(QString("Downloading %1.").arg(IARU_BEACONS_URL));
|
||||
QNetworkReply *reply = m_dlm.download(dbURL, beaconFile);
|
||||
connect(reply, SIGNAL(downloadProgress(qint64,qint64)), this, SLOT(updateDownloadProgress(qint64,qint64)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MapBeaconDialog::downloadFinished(const QString& filename, bool success)
|
||||
{
|
||||
if (success)
|
||||
{
|
||||
if (filename == MapGUI::getBeaconFilename())
|
||||
{
|
||||
QList<Beacon *> *beacons = Beacon::readIARUCSV(filename);
|
||||
if (beacons != nullptr)
|
||||
m_gui->setBeacons(beacons);
|
||||
m_progressDialog->close();
|
||||
m_progressDialog = nullptr;
|
||||
}
|
||||
else
|
||||
{
|
||||
qDebug() << "MapBeaconDialog::downloadFinished: Unexpected filename: " << filename;
|
||||
m_progressDialog->close();
|
||||
m_progressDialog = nullptr;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
qDebug() << "MapBeaconDialog::downloadFinished: Failed: " << filename;
|
||||
m_progressDialog->close();
|
||||
m_progressDialog = nullptr;
|
||||
QMessageBox::warning(this, "Download failed", QString("Failed to download %1").arg(filename));
|
||||
}
|
||||
}
|
||||
|
||||
void MapBeaconDialog::on_beacons_cellDoubleClicked(int row, int column)
|
||||
{
|
||||
if ((column == BEACON_COL_CALLSIGN) || (column == BEACON_COL_LOCATION))
|
||||
{
|
||||
// Find beacon on map
|
||||
QString location = ui->beacons->item(row, column)->text();
|
||||
m_gui->find(location);
|
||||
}
|
||||
else if (column == BEACON_COL_FREQUENCY)
|
||||
{
|
||||
// Tune to beacon freq
|
||||
ChannelWebAPIUtils::setCenterFrequency(0, ui->beacons->item(row, column)->data(Qt::UserRole).toDouble());
|
||||
}
|
||||
}
|
||||
|
||||
void MapBeaconDialog::on_filter_currentIndexChanged(int index)
|
||||
{
|
||||
for (int row = 0; row < ui->beacons->rowCount(); row++)
|
||||
{
|
||||
bool hidden = false;
|
||||
QTableWidgetItem *item = ui->beacons->item(row, BEACON_COL_FREQUENCY);
|
||||
qint64 freq = item->data(Qt::UserRole).toLongLong();
|
||||
qint64 band = freq/1000000;
|
||||
switch (index)
|
||||
{
|
||||
case 0: // All
|
||||
break;
|
||||
case 1:
|
||||
hidden = band != 50;
|
||||
break;
|
||||
case 2:
|
||||
hidden = band != 70;
|
||||
break;
|
||||
case 3:
|
||||
hidden = band != 144;
|
||||
break;
|
||||
case 4:
|
||||
hidden = band != 432;
|
||||
break;
|
||||
case 5:
|
||||
hidden = band != 1296;
|
||||
break;
|
||||
case 6:
|
||||
hidden = band <= 1296;
|
||||
break;
|
||||
}
|
||||
ui->beacons->setRowHidden(row, hidden);
|
||||
}
|
||||
}
|
71
plugins/feature/map/mapbeacondialog.h
Normal file
71
plugins/feature/map/mapbeacondialog.h
Normal file
@ -0,0 +1,71 @@
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (C) 2021 Jon Beniston, M7RCE //
|
||||
// //
|
||||
// This program is free software; you can redistribute it and/or modify //
|
||||
// it under the terms of the GNU General Public License as published by //
|
||||
// the Free Software Foundation as version 3 of the License, or //
|
||||
// (at your option) any later version. //
|
||||
// //
|
||||
// This program is distributed in the hope that it will be useful, //
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of //
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
|
||||
// GNU General Public License V3 for more details. //
|
||||
// //
|
||||
// You should have received a copy of the GNU General Public License //
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>. //
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifndef INCLUDE_FEATURE_MAPBEACONDIALOG_H
|
||||
#define INCLUDE_FEATURE_MAPBEACONDIALOG_H
|
||||
|
||||
#include "ui_mapbeacondialog.h"
|
||||
|
||||
#include <QProgressDialog>
|
||||
|
||||
#include "util/httpdownloadmanager.h"
|
||||
#include "beacon.h"
|
||||
|
||||
class MapGUI;
|
||||
|
||||
class MapBeaconDialog : public QDialog {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit MapBeaconDialog(MapGUI *gui, QWidget* parent = 0);
|
||||
~MapBeaconDialog();
|
||||
void updateTable();
|
||||
|
||||
private:
|
||||
qint64 fileAgeInDays(QString filename);
|
||||
bool confirmDownload(QString filename);
|
||||
void downloadFinished(const QString& filename, bool success);
|
||||
|
||||
private slots:
|
||||
void accept();
|
||||
void on_downloadIARU_clicked();
|
||||
void updateDownloadProgress(qint64 bytesRead, qint64 totalBytes);
|
||||
void on_beacons_cellDoubleClicked(int row, int column);
|
||||
void on_filter_currentIndexChanged(int index);
|
||||
|
||||
private:
|
||||
MapGUI *m_gui;
|
||||
Ui::MapBeaconDialog* ui;
|
||||
HttpDownloadManager m_dlm;
|
||||
QProgressDialog *m_progressDialog;
|
||||
|
||||
enum BeaconCol {
|
||||
BEACON_COL_CALLSIGN,
|
||||
BEACON_COL_FREQUENCY,
|
||||
BEACON_COL_LOCATION,
|
||||
BEACON_COL_POWER,
|
||||
BEACON_COL_POLARIZATION,
|
||||
BEACON_COL_PATTERN,
|
||||
BEACON_COL_KEY,
|
||||
BEACON_COL_MGM,
|
||||
BEACON_COL_AZIMUTH,
|
||||
BEACON_COL_ELEVATION,
|
||||
BEACON_COL_DISTANCE
|
||||
};
|
||||
};
|
||||
|
||||
#endif // INCLUDE_FEATURE_MAPBEACONDIALOG_H
|
226
plugins/feature/map/mapbeacondialog.ui
Normal file
226
plugins/feature/map/mapbeacondialog.ui
Normal file
@ -0,0 +1,226 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>MapBeaconDialog</class>
|
||||
<widget class="QDialog" name="MapBeaconDialog">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>1027</width>
|
||||
<height>349</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="font">
|
||||
<font>
|
||||
<family>Liberation Sans</family>
|
||||
<pointsize>9</pointsize>
|
||||
</font>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Beacons</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<widget class="QGroupBox" name="groupBox">
|
||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="filterLabel">
|
||||
<property name="text">
|
||||
<string>Show</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QComboBox" name="filter">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>100</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>All</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>50MHz (6m)</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>70MHz (4m)</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>144MHz (2m)</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>432MHz (70cm)</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>1.296GHz (23cm)</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>>2.3GHz (<13cm)</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="downloadIARU">
|
||||
<property name="toolTip">
|
||||
<string>Download IARU Beacon list</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="../../../sdrgui/resources/res.qrc">
|
||||
<normaloff>:/antenna.png</normaloff>:/antenna.png</iconset>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QTableWidget" name="beacons">
|
||||
<property name="toolTip">
|
||||
<string/>
|
||||
</property>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Callsign</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Frequency</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Location</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Power</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Polarization</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Pattern</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Key</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>MGM</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Azimuth</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Elevation</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Distance (km)</string>
|
||||
</property>
|
||||
</column>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QDialogButtonBox" name="buttonBox">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="standardButtons">
|
||||
<set>QDialogButtonBox::Close</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources>
|
||||
<include location="../../../sdrgui/resources/res.qrc"/>
|
||||
</resources>
|
||||
<connections>
|
||||
<connection>
|
||||
<sender>buttonBox</sender>
|
||||
<signal>accepted()</signal>
|
||||
<receiver>MapBeaconDialog</receiver>
|
||||
<slot>accept()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>248</x>
|
||||
<y>254</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>157</x>
|
||||
<y>274</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>buttonBox</sender>
|
||||
<signal>rejected()</signal>
|
||||
<receiver>MapBeaconDialog</receiver>
|
||||
<slot>reject()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>316</x>
|
||||
<y>260</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>286</x>
|
||||
<y>274</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
</connections>
|
||||
</ui>
|
@ -20,21 +20,54 @@
|
||||
#include <QMessageBox>
|
||||
#include <QLineEdit>
|
||||
#include <QQmlContext>
|
||||
#include <QQmlProperty>
|
||||
#include <QQuickItem>
|
||||
#include <QGeoLocation>
|
||||
#include <QGeoCoordinate>
|
||||
#include <QGeoCodingManager>
|
||||
#include <QGeoServiceProvider>
|
||||
#include <QRegExp>
|
||||
|
||||
#include "feature/featureuiset.h"
|
||||
#include "gui/basicfeaturesettingsdialog.h"
|
||||
#include "channel/channelwebapiutils.h"
|
||||
#include "mainwindow.h"
|
||||
#include "device/deviceuiset.h"
|
||||
#include "util/units.h"
|
||||
#include "util/maidenhead.h"
|
||||
#include "maplocationdialog.h"
|
||||
#include "mapmaidenheaddialog.h"
|
||||
#include "mapsettingsdialog.h"
|
||||
|
||||
#include "ui_mapgui.h"
|
||||
#include "map.h"
|
||||
#include "mapgui.h"
|
||||
#include "SWGMapItem.h"
|
||||
#include "SWGTargetAzimuthElevation.h"
|
||||
|
||||
void MapItem::findFrequency()
|
||||
{
|
||||
// Look for a frequency in the text for this object
|
||||
QRegExp re("(([0-9]+(\\.[0-9]+)?) *([kMG])?Hz)");
|
||||
if (re.indexIn(m_text) != -1)
|
||||
{
|
||||
QStringList capture = re.capturedTexts();
|
||||
m_frequency = capture[2].toDouble();
|
||||
if (capture.length() == 5)
|
||||
{
|
||||
QChar unit = capture[4][0];
|
||||
if (unit == 'k')
|
||||
m_frequency *= 1000.0;
|
||||
else if (unit == 'M')
|
||||
m_frequency *= 1000000.0;
|
||||
else if (unit == 'G')
|
||||
m_frequency *= 1000000000.0;
|
||||
}
|
||||
m_frequencyString = capture[0];
|
||||
}
|
||||
else
|
||||
m_frequency = 0.0;
|
||||
}
|
||||
|
||||
QVariant MapModel::data(const QModelIndex &index, int role) const
|
||||
{
|
||||
@ -53,14 +86,27 @@ QVariant MapModel::data(const QModelIndex &index, int role) const
|
||||
else if (role == MapModel::mapTextRole)
|
||||
{
|
||||
// Create the text to go in the bubble next to the image
|
||||
if (m_selected[row])
|
||||
if (row == m_target)
|
||||
{
|
||||
AzEl *azEl = m_gui->getAzEl();
|
||||
QString text = QString("%1\nAz: %2 El: %3")
|
||||
.arg(m_selected[row] ? m_items[row]->m_text : m_items[row]->m_name)
|
||||
.arg(std::round(azEl->getAzimuth()))
|
||||
.arg(std::round(azEl->getElevation()));
|
||||
return QVariant::fromValue(text);
|
||||
}
|
||||
else if (m_selected[row])
|
||||
return QVariant::fromValue(m_items[row]->m_text);
|
||||
else
|
||||
return QVariant::fromValue(m_items[row]->m_name);
|
||||
}
|
||||
else if (role == MapModel::mapTextVisibleRole)
|
||||
{
|
||||
return QVariant::fromValue(m_selected[row] || m_displayNames);
|
||||
return QVariant::fromValue((m_selected[row] || m_displayNames) && (m_sources & m_items[row]->m_sourceMask));
|
||||
}
|
||||
else if (role == MapModel::mapImageVisibleRole)
|
||||
{
|
||||
return QVariant::fromValue((m_sources & m_items[row]->m_sourceMask) != 0);
|
||||
}
|
||||
else if (role == MapModel::mapImageRole)
|
||||
{
|
||||
@ -72,10 +118,10 @@ QVariant MapModel::data(const QModelIndex &index, int role) const
|
||||
// Angle to rotate image by
|
||||
return QVariant::fromValue(m_items[row]->m_imageRotation);
|
||||
}
|
||||
else if (role == MapModel::mapImageFixedSizeRole)
|
||||
else if (role == MapModel::mapImageMinZoomRole)
|
||||
{
|
||||
// Whether image changes size with zoom level
|
||||
return QVariant::fromValue(m_items[row]->m_imageFixedSize);
|
||||
// Minimum zoom level
|
||||
return QVariant::fromValue(m_items[row]->m_imageMinZoom);
|
||||
}
|
||||
else if (role == MapModel::bubbleColourRole)
|
||||
{
|
||||
@ -87,23 +133,108 @@ QVariant MapModel::data(const QModelIndex &index, int role) const
|
||||
}
|
||||
else if (role == MapModel::selectedRole)
|
||||
return QVariant::fromValue(m_selected[row]);
|
||||
else if (role == MapModel::targetRole)
|
||||
return QVariant::fromValue(m_target == row);
|
||||
else if (role == MapModel::frequencyRole)
|
||||
return QVariant::fromValue(m_items[row]->m_frequency);
|
||||
else if (role == MapModel::frequencyStringRole)
|
||||
return QVariant::fromValue(m_items[row]->m_frequencyString);
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
bool MapModel::setData(const QModelIndex &index, const QVariant& value, int role)
|
||||
bool MapModel::setData(const QModelIndex &idx, const QVariant& value, int role)
|
||||
{
|
||||
int row = index.row();
|
||||
int row = idx.row();
|
||||
if ((row < 0) || (row >= m_items.count()))
|
||||
return false;
|
||||
if (role == MapModel::selectedRole)
|
||||
{
|
||||
m_selected[row] = value.toBool();
|
||||
emit dataChanged(index, index);
|
||||
emit dataChanged(idx, idx);
|
||||
return true;
|
||||
}
|
||||
else if (role == MapModel::targetRole)
|
||||
{
|
||||
if (m_target >= 0)
|
||||
{
|
||||
// Update text bubble for old target
|
||||
QModelIndex oldIdx = index(m_target);
|
||||
m_target = -1;
|
||||
emit dataChanged(oldIdx, oldIdx);
|
||||
}
|
||||
m_target = row;
|
||||
updateTarget();
|
||||
emit dataChanged(idx, idx);
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void MapModel::setFrequency(double frequency)
|
||||
{
|
||||
// Set as centre frequency
|
||||
ChannelWebAPIUtils::setCenterFrequency(0, frequency);
|
||||
}
|
||||
|
||||
void MapModel::update(const PipeEndPoint *sourcePipe, SWGSDRangel::SWGMapItem *swgMapItem, quint32 sourceMask)
|
||||
{
|
||||
QString name = *swgMapItem->getName();
|
||||
// Add, update or delete and item
|
||||
MapItem *item = findMapItem(sourcePipe, name);
|
||||
if (item != nullptr)
|
||||
{
|
||||
QString image = *swgMapItem->getImage();
|
||||
if (image.isEmpty())
|
||||
{
|
||||
// Delete the item
|
||||
remove(item);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Update the item
|
||||
item->update(swgMapItem);
|
||||
update(item);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Make sure not a duplicate request to delete
|
||||
QString image = *swgMapItem->getImage();
|
||||
if (!image.isEmpty())
|
||||
{
|
||||
if (!sourceMask)
|
||||
sourceMask = m_gui->getSourceMask(sourcePipe);
|
||||
// Add new item
|
||||
add(new MapItem(sourcePipe, sourceMask, swgMapItem));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MapModel::updateTarget()
|
||||
{
|
||||
// Calculate range, azimuth and elevation to object from station
|
||||
AzEl *azEl = m_gui->getAzEl();
|
||||
azEl->setTarget(m_items[m_target]->m_latitude, m_items[m_target]->m_longitude, m_items[m_target]->m_altitude);
|
||||
azEl->calculate();
|
||||
|
||||
// Send to Rotator Controllers
|
||||
MessagePipes& messagePipes = MainCore::instance()->getMessagePipes();
|
||||
QList<MessageQueue*> *mapMessageQueues = messagePipes.getMessageQueues(m_gui->getMap(), "target");
|
||||
if (mapMessageQueues)
|
||||
{
|
||||
QList<MessageQueue*>::iterator it = mapMessageQueues->begin();
|
||||
|
||||
for (; it != mapMessageQueues->end(); ++it)
|
||||
{
|
||||
SWGSDRangel::SWGTargetAzimuthElevation *swgTarget = new SWGSDRangel::SWGTargetAzimuthElevation();
|
||||
swgTarget->setName(new QString(m_items[m_target]->m_name));
|
||||
swgTarget->setAzimuth(azEl->getAzimuth());
|
||||
swgTarget->setElevation(azEl->getElevation());
|
||||
(*it)->push(MainCore::MsgTargetAzimuthElevation::create(m_gui->getMap(), swgTarget));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MapGUI* MapGUI::create(PluginAPI* pluginAPI, FeatureUISet *featureUISet, Feature *feature)
|
||||
{
|
||||
MapGUI* gui = new MapGUI(pluginAPI, featureUISet, feature);
|
||||
@ -159,7 +290,6 @@ bool MapGUI::handleMessage(const Message& message)
|
||||
{
|
||||
PipeEndPoint::MsgReportPipes& report = (PipeEndPoint::MsgReportPipes&) message;
|
||||
m_availablePipes = report.getAvailablePipes();
|
||||
updatePipeList();
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -204,7 +334,9 @@ MapGUI::MapGUI(PluginAPI* pluginAPI, FeatureUISet *featureUISet, Feature *featur
|
||||
m_pluginAPI(pluginAPI),
|
||||
m_featureUISet(featureUISet),
|
||||
m_doApplySettings(true),
|
||||
m_mapModel(this)
|
||||
m_mapModel(this),
|
||||
m_beacons(nullptr),
|
||||
m_beaconDialog(this)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
|
||||
@ -219,38 +351,45 @@ MapGUI::MapGUI(PluginAPI* pluginAPI, FeatureUISet *featureUISet, Feature *featur
|
||||
|
||||
m_featureUISet->addRollupWidget(this);
|
||||
|
||||
connect(this, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(onMenuDialogCalled(const QPoint &)));
|
||||
connect(getInputMessageQueue(), SIGNAL(messageEnqueued()), this, SLOT(handleInputMessages()));
|
||||
|
||||
displaySettings();
|
||||
applySettings(true);
|
||||
|
||||
// Get station position
|
||||
float stationLatitude = MainCore::instance()->getSettings().getLatitude();
|
||||
float stationLongitude = MainCore::instance()->getSettings().getLongitude();
|
||||
float stationAltitude = MainCore::instance()->getSettings().getAltitude();
|
||||
m_azEl.setLocation(stationLatitude, stationLongitude, stationAltitude);
|
||||
|
||||
// Centre map at My Position
|
||||
QQuickItem *item = ui->map->rootObject();
|
||||
QObject *object = item->findChild<QObject*>("map");
|
||||
if(object != NULL)
|
||||
if (object != nullptr)
|
||||
{
|
||||
QGeoCoordinate coords = object->property("center").value<QGeoCoordinate>();
|
||||
coords.setLatitude(stationLatitude);
|
||||
coords.setLongitude(stationLongitude);
|
||||
object->setProperty("center", QVariant::fromValue(coords));
|
||||
}
|
||||
// Move antenna icon to My Position to start with
|
||||
QObject *stationObject = item->findChild<QObject*>("station");
|
||||
if(stationObject != NULL)
|
||||
{
|
||||
QGeoCoordinate coords = stationObject->property("coordinate").value<QGeoCoordinate>();
|
||||
coords.setLatitude(stationLatitude);
|
||||
coords.setLongitude(stationLongitude);
|
||||
coords.setAltitude(stationAltitude);
|
||||
stationObject->setProperty("coordinate", QVariant::fromValue(coords));
|
||||
stationObject->setProperty("stationName", QVariant::fromValue(MainCore::instance()->getSettings().getStationName()));
|
||||
}
|
||||
|
||||
connect(this, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(onMenuDialogCalled(const QPoint &)));
|
||||
connect(getInputMessageQueue(), SIGNAL(messageEnqueued()), this, SLOT(handleInputMessages()));
|
||||
// Create antenna at My Position
|
||||
SWGSDRangel::SWGMapItem antennaMapItem;
|
||||
antennaMapItem.setName(new QString(MainCore::instance()->getSettings().getStationName()));
|
||||
antennaMapItem.setLatitude(stationLatitude);
|
||||
antennaMapItem.setLongitude(stationLongitude);
|
||||
antennaMapItem.setAltitude(stationAltitude);
|
||||
antennaMapItem.setImage(new QString("antenna.png"));
|
||||
antennaMapItem.setImageRotation(0);
|
||||
antennaMapItem.setImageMinZoom(11);
|
||||
antennaMapItem.setText(new QString(MainCore::instance()->getSettings().getStationName()));
|
||||
m_mapModel.update(m_map, &antennaMapItem, MapSettings::SOURCE_STATION);
|
||||
|
||||
displaySettings();
|
||||
applySettings(true);
|
||||
// Read beacons, if they exist
|
||||
QList<Beacon *> *beacons = Beacon::readIARUCSV(MapGUI::getBeaconFilename());
|
||||
if (beacons != nullptr)
|
||||
setBeacons(beacons);
|
||||
}
|
||||
|
||||
MapGUI::~MapGUI()
|
||||
@ -258,11 +397,90 @@ MapGUI::~MapGUI()
|
||||
delete ui;
|
||||
}
|
||||
|
||||
void MapGUI::setBeacons(QList<Beacon *> *beacons)
|
||||
{
|
||||
delete m_beacons;
|
||||
m_beacons = beacons;
|
||||
m_beaconDialog.updateTable();
|
||||
// Add to Map
|
||||
QListIterator<Beacon *> i(*m_beacons);
|
||||
while (i.hasNext())
|
||||
{
|
||||
Beacon *beacon = i.next();
|
||||
SWGSDRangel::SWGMapItem beaconMapItem;
|
||||
beaconMapItem.setName(new QString(beacon->m_callsign));
|
||||
beaconMapItem.setLatitude(beacon->m_latitude);
|
||||
beaconMapItem.setLongitude(beacon->m_longitude);
|
||||
beaconMapItem.setAltitude(beacon->m_altitude);
|
||||
beaconMapItem.setImage(new QString("antenna.png"));
|
||||
beaconMapItem.setImageRotation(0);
|
||||
beaconMapItem.setImageMinZoom(8);
|
||||
beaconMapItem.setText(new QString(beacon->getText()));
|
||||
m_mapModel.update(m_map, &beaconMapItem, MapSettings::SOURCE_BEACONS);
|
||||
}
|
||||
}
|
||||
|
||||
void MapGUI::blockApplySettings(bool block)
|
||||
{
|
||||
m_doApplySettings = !block;
|
||||
}
|
||||
|
||||
void MapGUI::applyMapSettings()
|
||||
{
|
||||
QQuickItem *item = ui->map->rootObject();
|
||||
|
||||
// Save existing position of map
|
||||
QObject *object = item->findChild<QObject*>("map");
|
||||
QGeoCoordinate coords;
|
||||
if (object != nullptr)
|
||||
coords = object->property("center").value<QGeoCoordinate>();
|
||||
|
||||
// Create the map using the specified provider
|
||||
QQmlProperty::write(item, "mapProvider", m_settings.m_mapProvider);
|
||||
QVariantMap parameters;
|
||||
if (!m_settings.m_mapBoxApiKey.isEmpty() && m_settings.m_mapProvider == "mapbox")
|
||||
{
|
||||
parameters["mapbox.map_id"] = "mapbox.satellite"; // The only one that works
|
||||
parameters["mapbox.access_token"] = m_settings.m_mapBoxApiKey;
|
||||
}
|
||||
if (!m_settings.m_mapBoxApiKey.isEmpty() && m_settings.m_mapProvider == "mapboxgl")
|
||||
{
|
||||
parameters["mapboxgl.access_token"] = m_settings.m_mapBoxApiKey;
|
||||
if (!m_settings.m_mapBoxStyles.isEmpty())
|
||||
parameters["mapboxgl.mapping.additional_style_urls"] = m_settings.m_mapBoxStyles;
|
||||
}
|
||||
//QQmlProperty::write(item, "mapParameters", parameters);
|
||||
QMetaObject::invokeMethod(item, "createMap", Q_ARG(QVariant, QVariant::fromValue(parameters)));
|
||||
|
||||
// Restore position of map
|
||||
object = item->findChild<QObject*>("map");
|
||||
if ((object != nullptr) && coords.isValid())
|
||||
object->setProperty("center", QVariant::fromValue(coords));
|
||||
|
||||
// Get list of map types
|
||||
ui->mapTypes->clear();
|
||||
if (object != nullptr)
|
||||
{
|
||||
// Mapbox plugin only works for Satellite imagary, despite what is indicated
|
||||
if (m_settings.m_mapProvider == "mapbox")
|
||||
ui->mapTypes->addItem("Satellite");
|
||||
else
|
||||
{
|
||||
QVariant mapTypesVariant;
|
||||
QMetaObject::invokeMethod(item, "getMapTypes", Q_RETURN_ARG(QVariant, mapTypesVariant));
|
||||
QStringList mapTypes = mapTypesVariant.value<QStringList>();
|
||||
for (int i = 0; i < mapTypes.size(); i++)
|
||||
ui->mapTypes->addItem(mapTypes[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MapGUI::on_mapTypes_currentIndexChanged(int index)
|
||||
{
|
||||
QVariant mapType = index;
|
||||
QMetaObject::invokeMethod(ui->map->rootObject(), "setMapType", Q_ARG(QVariant, mapType));
|
||||
}
|
||||
|
||||
void MapGUI::displaySettings()
|
||||
{
|
||||
setTitleColor(m_settings.m_rgbColor);
|
||||
@ -270,24 +488,11 @@ void MapGUI::displaySettings()
|
||||
blockApplySettings(true);
|
||||
ui->displayNames->setChecked(m_settings.m_displayNames);
|
||||
m_mapModel.setDisplayNames(m_settings.m_displayNames);
|
||||
m_mapModel.setSources(m_settings.m_sources);
|
||||
applyMapSettings();
|
||||
blockApplySettings(false);
|
||||
}
|
||||
|
||||
void MapGUI::updatePipeList()
|
||||
{
|
||||
ui->pipes->blockSignals(true);
|
||||
ui->pipes->clear();
|
||||
|
||||
QList<PipeEndPoint::AvailablePipeSource>::const_iterator it = m_availablePipes.begin();
|
||||
|
||||
for (int i = 0; it != m_availablePipes.end(); ++it, i++)
|
||||
{
|
||||
ui->pipes->addItem(it->getName());
|
||||
}
|
||||
|
||||
ui->pipes->blockSignals(false);
|
||||
}
|
||||
|
||||
void MapGUI::leaveEvent(QEvent*)
|
||||
{
|
||||
}
|
||||
@ -338,6 +543,12 @@ void MapGUI::applySettings(bool force)
|
||||
}
|
||||
}
|
||||
|
||||
void MapGUI::on_maidenhead_clicked(bool checked)
|
||||
{
|
||||
MapMaidenheadDialog dialog;
|
||||
dialog.exec();
|
||||
}
|
||||
|
||||
void MapGUI::on_displayNames_clicked(bool checked)
|
||||
{
|
||||
m_settings.m_displayNames = checked;
|
||||
@ -349,14 +560,51 @@ void MapGUI::on_find_returnPressed()
|
||||
find(ui->find->text().trimmed());
|
||||
}
|
||||
|
||||
void MapGUI::geoReply()
|
||||
{
|
||||
QGeoCodeReply *pQGeoCode = dynamic_cast<QGeoCodeReply*>(sender());
|
||||
|
||||
if ((pQGeoCode != nullptr) && (pQGeoCode->error() == QGeoCodeReply::NoError))
|
||||
{
|
||||
QList<QGeoLocation> qGeoLocs = pQGeoCode->locations();
|
||||
QQuickItem *item = ui->map->rootObject();
|
||||
QObject *map = item->findChild<QObject*>("map");
|
||||
if (qGeoLocs.size() == 1)
|
||||
{
|
||||
// Only one result, so centre map on that
|
||||
map->setProperty("center", QVariant::fromValue(qGeoLocs.at(0).coordinate()));
|
||||
}
|
||||
else if (qGeoLocs.size() == 0)
|
||||
{
|
||||
qDebug() << "MapGUI::geoReply: No location found for address";
|
||||
QApplication::beep();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Show dialog allowing user to select from the results
|
||||
MapLocationDialog dialog(qGeoLocs);
|
||||
if (dialog.exec() == QDialog::Accepted)
|
||||
map->setProperty("center", QVariant::fromValue(dialog.m_selectedLocation.coordinate()));
|
||||
}
|
||||
}
|
||||
else
|
||||
qWarning() << "MapGUI::geoReply: GeoCode error: " << pQGeoCode->error();
|
||||
pQGeoCode->deleteLater();
|
||||
}
|
||||
|
||||
void MapGUI::find(const QString& target)
|
||||
{
|
||||
if (!target.isEmpty())
|
||||
{
|
||||
QQuickItem *item = ui->map->rootObject();
|
||||
QObject *map = item->findChild<QObject*>("map");
|
||||
if (map != NULL)
|
||||
if (map != nullptr)
|
||||
{
|
||||
// Search as:
|
||||
// latitude and longitude
|
||||
// Maidenhead locator
|
||||
// object name
|
||||
// address
|
||||
float latitude, longitude;
|
||||
if (Units::stringToLatitudeAndLongitude(target, latitude, longitude))
|
||||
{
|
||||
@ -371,6 +619,22 @@ void MapGUI::find(const QString& target)
|
||||
MapItem *mapItem = m_mapModel.findMapItem(target);
|
||||
if (mapItem != nullptr)
|
||||
map->setProperty("center", QVariant::fromValue(mapItem->getCoordinates()));
|
||||
else
|
||||
{
|
||||
QGeoServiceProvider* geoSrv = new QGeoServiceProvider("osm");
|
||||
if (geoSrv != nullptr)
|
||||
{
|
||||
QLocale qLocaleC(QLocale::C, QLocale::AnyCountry);
|
||||
geoSrv->setLocale(qLocaleC);
|
||||
QGeoCodeReply *pQGeoCode = geoSrv->geocodingManager()->geocode(target);
|
||||
if (pQGeoCode)
|
||||
QObject::connect(pQGeoCode, &QGeoCodeReply::finished, this, &MapGUI::geoReply);
|
||||
else
|
||||
qDebug() << "MapGUI::find: GeoCoding failed";
|
||||
}
|
||||
else
|
||||
qDebug() << "MapGUI::find: osm not available";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -380,3 +644,50 @@ void MapGUI::on_deleteAll_clicked()
|
||||
{
|
||||
m_mapModel.removeAll();
|
||||
}
|
||||
|
||||
void MapGUI::on_displaySettings_clicked()
|
||||
{
|
||||
MapSettingsDialog dialog(&m_settings);
|
||||
if (dialog.exec() == QDialog::Accepted)
|
||||
{
|
||||
if (dialog.m_mapSettingsChanged)
|
||||
applyMapSettings();
|
||||
applySettings();
|
||||
if (dialog.m_sourcesChanged)
|
||||
m_mapModel.setSources(m_settings.m_sources);
|
||||
}
|
||||
}
|
||||
|
||||
void MapGUI::on_beacons_clicked()
|
||||
{
|
||||
m_beaconDialog.show();
|
||||
}
|
||||
|
||||
quint32 MapGUI::getSourceMask(const PipeEndPoint *sourcePipe)
|
||||
{
|
||||
for (int i = 0; i < m_availablePipes.size(); i++)
|
||||
{
|
||||
if (m_availablePipes[i].m_source == sourcePipe)
|
||||
{
|
||||
for (int j = 0; j < MapSettings::m_pipeTypes.size(); j++)
|
||||
{
|
||||
if (m_availablePipes[i].m_id == MapSettings::m_pipeTypes[j])
|
||||
return 1 << j;
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
QString MapGUI::getDataDir()
|
||||
{
|
||||
// Get directory to store app data in (aircraft & airport databases and user-definable icons)
|
||||
QStringList locations = QStandardPaths::standardLocations(QStandardPaths::AppDataLocation);
|
||||
// First dir is writable
|
||||
return locations[0];
|
||||
}
|
||||
|
||||
QString MapGUI::getBeaconFilename()
|
||||
{
|
||||
return MapGUI::getDataDir() + "/iaru_beacons.csv";
|
||||
}
|
||||
|
@ -25,9 +25,11 @@
|
||||
|
||||
#include "feature/featuregui.h"
|
||||
#include "util/messagequeue.h"
|
||||
#include "util/azel.h"
|
||||
#include "pipes/pipeendpoint.h"
|
||||
#include "mapsettings.h"
|
||||
#include "SWGMapItem.h"
|
||||
#include "mapbeacondialog.h"
|
||||
|
||||
class PluginAPI;
|
||||
class FeatureUISet;
|
||||
@ -39,31 +41,41 @@ namespace Ui {
|
||||
|
||||
class MapGUI;
|
||||
class MapModel;
|
||||
struct Beacon;
|
||||
|
||||
// Information required about each item displayed on the map
|
||||
class MapItem {
|
||||
|
||||
public:
|
||||
MapItem(const PipeEndPoint *source, SWGSDRangel::SWGMapItem *mapItem)
|
||||
MapItem(const PipeEndPoint *sourcePipe, quint32 sourceMask, SWGSDRangel::SWGMapItem *mapItem)
|
||||
{
|
||||
m_source = source;
|
||||
m_sourcePipe = sourcePipe;
|
||||
m_sourceMask = sourceMask;
|
||||
m_name = *mapItem->getName();
|
||||
m_latitude = mapItem->getLatitude();
|
||||
m_longitude = mapItem->getLongitude();
|
||||
m_altitude = mapItem->getAltitude();
|
||||
m_image = *mapItem->getImage();
|
||||
m_imageRotation = mapItem->getImageRotation();
|
||||
m_imageFixedSize = mapItem->getImageFixedSize() == 1;
|
||||
m_text = *mapItem->getText();
|
||||
m_imageMinZoom = mapItem->getImageMinZoom();
|
||||
QString *text = mapItem->getText();
|
||||
if (text != nullptr)
|
||||
m_text = *text;
|
||||
findFrequency();
|
||||
}
|
||||
|
||||
void update(SWGSDRangel::SWGMapItem *mapItem)
|
||||
{
|
||||
m_latitude = mapItem->getLatitude();
|
||||
m_longitude = mapItem->getLongitude();
|
||||
m_altitude = mapItem->getAltitude();
|
||||
m_image = *mapItem->getImage();
|
||||
m_imageRotation = mapItem->getImageRotation();
|
||||
m_imageFixedSize = mapItem->getImageFixedSize() == 1;
|
||||
m_text = *mapItem->getText();
|
||||
m_imageMinZoom = mapItem->getImageMinZoom();
|
||||
QString *text = mapItem->getText();
|
||||
if (text != nullptr)
|
||||
m_text = *text;
|
||||
findFrequency();
|
||||
}
|
||||
|
||||
QGeoCoordinate getCoordinates()
|
||||
@ -75,15 +87,22 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
void findFrequency();
|
||||
|
||||
friend MapModel;
|
||||
const PipeEndPoint *m_source; // Channel/feature that created the item
|
||||
const PipeEndPoint *m_sourcePipe; // Channel/feature that created the item
|
||||
quint32 m_sourceMask; // Source bitmask as per MapSettings::SOURCE_* constants
|
||||
QString m_name;
|
||||
float m_latitude;
|
||||
float m_longitude;
|
||||
float m_altitude; // In metres
|
||||
QString m_image;
|
||||
int m_imageRotation;
|
||||
bool m_imageFixedSize; // Keep image same size when map is zoomed
|
||||
int m_imageMinZoom;
|
||||
QString m_text;
|
||||
double m_frequency; // Frequency to set
|
||||
QString m_frequencyString;
|
||||
};
|
||||
|
||||
// Model used for each item on the map
|
||||
@ -96,15 +115,21 @@ public:
|
||||
positionRole = Qt::UserRole + 1,
|
||||
mapTextRole = Qt::UserRole + 2,
|
||||
mapTextVisibleRole = Qt::UserRole + 3,
|
||||
mapImageRole = Qt::UserRole + 4,
|
||||
mapImageRotationRole = Qt::UserRole + 5,
|
||||
mapImageFixedSizeRole = Qt::UserRole + 6,
|
||||
bubbleColourRole = Qt::UserRole + 7,
|
||||
selectedRole = Qt::UserRole + 8
|
||||
mapImageVisibleRole = Qt::UserRole + 4,
|
||||
mapImageRole = Qt::UserRole + 5,
|
||||
mapImageRotationRole = Qt::UserRole + 6,
|
||||
mapImageMinZoomRole = Qt::UserRole + 7,
|
||||
bubbleColourRole = Qt::UserRole + 8,
|
||||
selectedRole = Qt::UserRole + 9,
|
||||
targetRole = Qt::UserRole + 10,
|
||||
frequencyRole = Qt::UserRole + 11,
|
||||
frequencyStringRole = Qt::UserRole + 12
|
||||
};
|
||||
|
||||
MapModel(MapGUI *gui) :
|
||||
m_gui(gui)
|
||||
m_gui(gui),
|
||||
m_target(-1),
|
||||
m_sources(-1)
|
||||
{
|
||||
}
|
||||
|
||||
@ -116,37 +141,9 @@ public:
|
||||
endInsertRows();
|
||||
}
|
||||
|
||||
void update(const PipeEndPoint *source, SWGSDRangel::SWGMapItem *swgMapItem)
|
||||
{
|
||||
QString name = *swgMapItem->getName();
|
||||
// Add, update or delete and item
|
||||
MapItem *item = findMapItem(source, name);
|
||||
if (item != nullptr)
|
||||
{
|
||||
QString image = *swgMapItem->getImage();
|
||||
if (image.isEmpty())
|
||||
{
|
||||
// Delete the item
|
||||
remove(item);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Update the item
|
||||
item->update(swgMapItem);
|
||||
update(item);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Make sure not a duplicate request to delete
|
||||
QString image = *swgMapItem->getImage();
|
||||
if (!image.isEmpty())
|
||||
{
|
||||
// Add new item
|
||||
add(new MapItem(source, swgMapItem));
|
||||
}
|
||||
}
|
||||
}
|
||||
void update(const PipeEndPoint *source, SWGSDRangel::SWGMapItem *swgMapItem, quint32 sourceMask=0);
|
||||
|
||||
void updateTarget();
|
||||
|
||||
void update(MapItem *item)
|
||||
{
|
||||
@ -155,6 +152,8 @@ public:
|
||||
{
|
||||
QModelIndex idx = index(row);
|
||||
emit dataChanged(idx, idx);
|
||||
if (row == m_target)
|
||||
updateTarget();
|
||||
}
|
||||
}
|
||||
|
||||
@ -166,10 +165,52 @@ public:
|
||||
beginRemoveRows(QModelIndex(), row, row);
|
||||
m_items.removeAt(row);
|
||||
m_selected.removeAt(row);
|
||||
if (row == m_target)
|
||||
m_target = -1;
|
||||
endRemoveRows();
|
||||
}
|
||||
}
|
||||
|
||||
Q_INVOKABLE void moveToFront(int oldRow)
|
||||
{
|
||||
// Last item in list is drawn on top, so remove than add to end of list
|
||||
if (oldRow < m_items.size() - 1)
|
||||
{
|
||||
bool wasTarget = m_target == oldRow;
|
||||
MapItem *item = m_items[oldRow];
|
||||
bool wasSelected = m_selected[oldRow];
|
||||
remove(item);
|
||||
add(item);
|
||||
int newRow = m_items.size() - 1;
|
||||
if (wasTarget)
|
||||
m_target = newRow;
|
||||
m_selected[newRow] = wasSelected;
|
||||
QModelIndex idx = index(newRow);
|
||||
emit dataChanged(idx, idx);
|
||||
}
|
||||
}
|
||||
|
||||
Q_INVOKABLE void moveToBack(int oldRow)
|
||||
{
|
||||
// First item in list is drawn first, so remove item then add to front of list
|
||||
if ((oldRow < m_items.size()) && (oldRow > 0))
|
||||
{
|
||||
bool wasTarget = m_target == oldRow;
|
||||
int newRow = 0;
|
||||
// See: https://forum.qt.io/topic/122991/changing-the-order-mapquickitems-are-drawn-on-a-map
|
||||
//QModelIndex parent;
|
||||
//beginMoveRows(parent, oldRow, oldRow, parent, newRow);
|
||||
beginResetModel();
|
||||
m_items.move(oldRow, newRow);
|
||||
m_selected.move(oldRow, newRow);
|
||||
if (wasTarget)
|
||||
m_target = newRow;
|
||||
//endMoveRows();
|
||||
endResetModel();
|
||||
//emit dataChanged(index(oldRow), index(newRow));
|
||||
}
|
||||
}
|
||||
|
||||
MapItem *findMapItem(const PipeEndPoint *source, const QString& name)
|
||||
{
|
||||
// FIXME: Should consider adding a QHash for this
|
||||
@ -177,7 +218,7 @@ public:
|
||||
while (i.hasNext())
|
||||
{
|
||||
MapItem *item = i.next();
|
||||
if ((item->m_name == name) && (item->m_source == source))
|
||||
if ((item->m_name == name) && (item->m_sourcePipe == source))
|
||||
return item;
|
||||
}
|
||||
return nullptr;
|
||||
@ -234,25 +275,40 @@ public:
|
||||
allUpdated();
|
||||
}
|
||||
|
||||
Q_INVOKABLE void setFrequency(double frequency);
|
||||
|
||||
QHash<int, QByteArray> roleNames() const
|
||||
{
|
||||
QHash<int, QByteArray> roles;
|
||||
roles[positionRole] = "position";
|
||||
roles[mapTextRole] = "mapText";
|
||||
roles[mapTextVisibleRole] = "mapTextVisible";
|
||||
roles[mapImageVisibleRole] = "mapImageVisible";
|
||||
roles[mapImageRole] = "mapImage";
|
||||
roles[mapImageRotationRole] = "mapImageRotation";
|
||||
roles[mapImageFixedSizeRole] = "mapImageFixedSize";
|
||||
roles[mapImageMinZoomRole] = "mapImageMinZoom";
|
||||
roles[bubbleColourRole] = "bubbleColour";
|
||||
roles[selectedRole] = "selected";
|
||||
roles[targetRole] = "target";
|
||||
roles[frequencyRole] = "frequency";
|
||||
roles[frequencyStringRole] = "frequencyString";
|
||||
return roles;
|
||||
}
|
||||
|
||||
// Set the sources of data we should display
|
||||
void setSources(quint32 sources)
|
||||
{
|
||||
m_sources = sources;
|
||||
allUpdated();
|
||||
}
|
||||
|
||||
private:
|
||||
MapGUI *m_gui;
|
||||
QList<MapItem *> m_items;
|
||||
QList<bool> m_selected;
|
||||
int m_target; // Row number of current target, or -1 for none
|
||||
bool m_displayNames;
|
||||
quint32 m_sources;
|
||||
};
|
||||
|
||||
class MapGUI : public FeatureGUI {
|
||||
@ -265,6 +321,13 @@ public:
|
||||
QByteArray serialize() const;
|
||||
bool deserialize(const QByteArray& data);
|
||||
virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; }
|
||||
AzEl *getAzEl() { return &m_azEl; }
|
||||
Map *getMap() { return m_map; }
|
||||
quint32 getSourceMask(const PipeEndPoint *sourcePipe);
|
||||
static QString getBeaconFilename();
|
||||
QList<Beacon *> *getBeacons() { return m_beacons; }
|
||||
void setBeacons(QList<Beacon *> *beacons);
|
||||
void find(const QString& target);
|
||||
|
||||
private:
|
||||
Ui::MapGUI* ui;
|
||||
@ -277,27 +340,36 @@ private:
|
||||
Map* m_map;
|
||||
MessageQueue m_inputMessageQueue;
|
||||
MapModel m_mapModel;
|
||||
AzEl m_azEl; // Position of station
|
||||
QList<Beacon *> *m_beacons;
|
||||
MapBeaconDialog m_beaconDialog;
|
||||
|
||||
explicit MapGUI(PluginAPI* pluginAPI, FeatureUISet *featureUISet, Feature *feature, QWidget* parent = nullptr);
|
||||
virtual ~MapGUI();
|
||||
|
||||
void blockApplySettings(bool block);
|
||||
void applySettings(bool force = false);
|
||||
void applyMapSettings();
|
||||
void displaySettings();
|
||||
void updatePipeList();
|
||||
bool handleMessage(const Message& message);
|
||||
void find(const QString& target);
|
||||
void geoReply();
|
||||
|
||||
void leaveEvent(QEvent*);
|
||||
void enterEvent(QEvent*);
|
||||
|
||||
static QString getDataDir();
|
||||
|
||||
private slots:
|
||||
void onMenuDialogCalled(const QPoint &p);
|
||||
void onWidgetRolled(QWidget* widget, bool rollDown);
|
||||
void handleInputMessages();
|
||||
void on_displayNames_clicked(bool checked=false);
|
||||
void on_find_returnPressed();
|
||||
void on_maidenhead_clicked(bool checked=false);
|
||||
void on_deleteAll_clicked();
|
||||
void on_displaySettings_clicked();
|
||||
void on_mapTypes_currentIndexChanged(int index);
|
||||
void on_beacons_clicked();
|
||||
};
|
||||
|
||||
|
||||
|
@ -11,7 +11,7 @@
|
||||
</rect>
|
||||
</property>
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
@ -73,26 +73,6 @@
|
||||
</property>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="buttonLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="pipesLabel">
|
||||
<property name="text">
|
||||
<string>Sources</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QComboBox" name="pipes">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>150</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Data source channels and features</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="findLabel">
|
||||
<property name="text">
|
||||
@ -102,8 +82,14 @@
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLineEdit" name="find">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>150</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Enter name of object to find, latitude and longitude or Maidenhead locator</string>
|
||||
<string>Enter name of object to find, latitude and longitude, Maidenhead locator or an address</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
@ -120,6 +106,47 @@
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QComboBox" name="mapTypes">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>120</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Select type of map to display</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="maidenhead">
|
||||
<property name="toolTip">
|
||||
<string>Maidenhead locator conversion</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="../../../sdrgui/resources/res.qrc">
|
||||
<normaloff>:/mem.png</normaloff>:/mem.png</iconset>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="beacons">
|
||||
<property name="toolTip">
|
||||
<string>Display Beacon dialog</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="../../../sdrgui/resources/res.qrc">
|
||||
<normaloff>:/antenna.png</normaloff>:/antenna.png</iconset>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="ButtonSwitch" name="displayNames">
|
||||
<property name="toolTip">
|
||||
@ -154,6 +181,20 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="displaySettings">
|
||||
<property name="toolTip">
|
||||
<string>Show settings dialog</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="../../../sdrgui/resources/res.qrc">
|
||||
<normaloff>:/listing.png</normaloff>:/listing.png</iconset>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
@ -241,6 +282,13 @@
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<tabstops>
|
||||
<tabstop>find</tabstop>
|
||||
<tabstop>mapTypes</tabstop>
|
||||
<tabstop>maidenhead</tabstop>
|
||||
<tabstop>beacons</tabstop>
|
||||
<tabstop>displayNames</tabstop>
|
||||
<tabstop>deleteAll</tabstop>
|
||||
<tabstop>displaySettings</tabstop>
|
||||
<tabstop>map</tabstop>
|
||||
</tabstops>
|
||||
<resources>
|
||||
|
47
plugins/feature/map/maplocationdialog.cpp
Normal file
47
plugins/feature/map/maplocationdialog.cpp
Normal file
@ -0,0 +1,47 @@
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (C) 2021 Jon Beniston, M7RCE //
|
||||
// //
|
||||
// This program is free software; you can redistribute it and/or modify //
|
||||
// it under the terms of the GNU General Public License as published by //
|
||||
// the Free Software Foundation as version 3 of the License, or //
|
||||
// (at your option) any later version. //
|
||||
// //
|
||||
// This program is distributed in the hope that it will be useful, //
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of //
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
|
||||
// GNU General Public License V3 for more details. //
|
||||
// //
|
||||
// You should have received a copy of the GNU General Public License //
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>. //
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "maplocationdialog.h"
|
||||
#include <QDebug>
|
||||
#include <QGeoAddress>
|
||||
|
||||
MapLocationDialog::MapLocationDialog(const QList<QGeoLocation>& locations,
|
||||
QWidget* parent) :
|
||||
QDialog(parent),
|
||||
ui(new Ui::MapLocationDialog)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
for (const QGeoLocation& location : locations)
|
||||
{
|
||||
QGeoAddress address = location.address();
|
||||
ui->locations->addItem(address.text());
|
||||
}
|
||||
ui->locations->setCurrentRow(0);
|
||||
m_locations = &locations;
|
||||
}
|
||||
|
||||
MapLocationDialog::~MapLocationDialog()
|
||||
{
|
||||
delete ui;
|
||||
}
|
||||
|
||||
void MapLocationDialog::accept()
|
||||
{
|
||||
int row = ui->locations->currentRow();
|
||||
m_selectedLocation = m_locations->at(row);
|
||||
QDialog::accept();
|
||||
}
|
43
plugins/feature/map/maplocationdialog.h
Normal file
43
plugins/feature/map/maplocationdialog.h
Normal file
@ -0,0 +1,43 @@
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (C) 2021 Jon Beniston, M7RCE //
|
||||
// //
|
||||
// This program is free software; you can redistribute it and/or modify //
|
||||
// it under the terms of the GNU General Public License as published by //
|
||||
// the Free Software Foundation as version 3 of the License, or //
|
||||
// (at your option) any later version. //
|
||||
// //
|
||||
// This program is distributed in the hope that it will be useful, //
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of //
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
|
||||
// GNU General Public License V3 for more details. //
|
||||
// //
|
||||
// You should have received a copy of the GNU General Public License //
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>. //
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifndef INCLUDE_FEATURE_MAPLOCATIONDIALOG_H
|
||||
#define INCLUDE_FEATURE_MAPLOCATIONDIALOG_H
|
||||
|
||||
#include "ui_maplocationdialog.h"
|
||||
|
||||
#include <QList>
|
||||
#include <QGeoLocation>
|
||||
|
||||
class MapLocationDialog : public QDialog {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit MapLocationDialog(const QList<QGeoLocation>& locations, QWidget* parent = 0);
|
||||
~MapLocationDialog();
|
||||
|
||||
const QList<QGeoLocation> *m_locations;
|
||||
QGeoLocation m_selectedLocation;
|
||||
|
||||
private slots:
|
||||
void accept();
|
||||
|
||||
private:
|
||||
Ui::MapLocationDialog* ui;
|
||||
};
|
||||
|
||||
#endif // INCLUDE_FEATURE_MAPLOCATIONDIALOG_H
|
93
plugins/feature/map/maplocationdialog.ui
Normal file
93
plugins/feature/map/maplocationdialog.ui
Normal file
@ -0,0 +1,93 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>MapLocationDialog</class>
|
||||
<widget class="QDialog" name="MapLocationDialog">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>565</width>
|
||||
<height>349</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="font">
|
||||
<font>
|
||||
<family>Liberation Sans</family>
|
||||
<pointsize>9</pointsize>
|
||||
</font>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Select a Location</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<widget class="QGroupBox" name="groupBox">
|
||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||
<item>
|
||||
<widget class="QLabel" name="locationsLabel">
|
||||
<property name="text">
|
||||
<string>Select a location:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QListWidget" name="locations">
|
||||
<property name="toolTip">
|
||||
<string>Select a location</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QDialogButtonBox" name="buttonBox">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="standardButtons">
|
||||
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<tabstops>
|
||||
<tabstop>locations</tabstop>
|
||||
</tabstops>
|
||||
<resources/>
|
||||
<connections>
|
||||
<connection>
|
||||
<sender>buttonBox</sender>
|
||||
<signal>accepted()</signal>
|
||||
<receiver>MapLocationDialog</receiver>
|
||||
<slot>accept()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>248</x>
|
||||
<y>254</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>157</x>
|
||||
<y>274</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>buttonBox</sender>
|
||||
<signal>rejected()</signal>
|
||||
<receiver>MapLocationDialog</receiver>
|
||||
<slot>reject()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>316</x>
|
||||
<y>260</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>286</x>
|
||||
<y>274</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
</connections>
|
||||
</ui>
|
144
plugins/feature/map/mapmaidenheaddialog.cpp
Normal file
144
plugins/feature/map/mapmaidenheaddialog.cpp
Normal file
@ -0,0 +1,144 @@
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (C) 2021 Jon Beniston, M7RCE //
|
||||
// //
|
||||
// This program is free software; you can redistribute it and/or modify //
|
||||
// it under the terms of the GNU General Public License as published by //
|
||||
// the Free Software Foundation as version 3 of the License, or //
|
||||
// (at your option) any later version. //
|
||||
// //
|
||||
// This program is distributed in the hope that it will be useful, //
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of //
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
|
||||
// GNU General Public License V3 for more details. //
|
||||
// //
|
||||
// You should have received a copy of the GNU General Public License //
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>. //
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include <QDebug>
|
||||
#include <QGeoCoordinate>
|
||||
#include <QGeoCodingManager>
|
||||
#include <QGeoServiceProvider>
|
||||
|
||||
#include "util/units.h"
|
||||
#include "util/maidenhead.h"
|
||||
|
||||
#include "mapmaidenheaddialog.h"
|
||||
#include "maplocationdialog.h"
|
||||
|
||||
MapMaidenheadDialog::MapMaidenheadDialog(QWidget* parent) :
|
||||
QDialog(parent),
|
||||
ui(new Ui::MapMaidenheadDialog)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
}
|
||||
|
||||
MapMaidenheadDialog::~MapMaidenheadDialog()
|
||||
{
|
||||
delete ui;
|
||||
}
|
||||
|
||||
void MapMaidenheadDialog::on_address_returnPressed()
|
||||
{
|
||||
QString address = ui->address->text().trimmed();
|
||||
if (!address.isEmpty())
|
||||
{
|
||||
ui->latAndLong->setText("");
|
||||
ui->error->setText("");
|
||||
QGeoServiceProvider* geoSrv = new QGeoServiceProvider("osm");
|
||||
if (geoSrv != nullptr)
|
||||
{
|
||||
QLocale qLocaleC(QLocale::C, QLocale::AnyCountry);
|
||||
geoSrv->setLocale(qLocaleC);
|
||||
QGeoCodeReply *pQGeoCode = geoSrv->geocodingManager()->geocode(address);
|
||||
if (pQGeoCode)
|
||||
QObject::connect(pQGeoCode, &QGeoCodeReply::finished, this, &MapMaidenheadDialog::geoReply);
|
||||
else
|
||||
ui->error->setText("GeoCoding failed");
|
||||
}
|
||||
else
|
||||
ui->error->setText("OpenStreetMap Location services not available");
|
||||
}
|
||||
}
|
||||
|
||||
void MapMaidenheadDialog::on_latAndLong_returnPressed()
|
||||
{
|
||||
float latitude, longitude;
|
||||
QString coords = ui->latAndLong->text();
|
||||
|
||||
if (Units::stringToLatitudeAndLongitude(coords, latitude, longitude))
|
||||
{
|
||||
ui->error->setText("");
|
||||
ui->maidenhead->setText(Maidenhead::toMaidenhead(latitude, longitude));
|
||||
}
|
||||
else
|
||||
{
|
||||
ui->error->setText("Not a valid latitude and longitude");
|
||||
ui->maidenhead->setText("");
|
||||
QApplication::beep();
|
||||
}
|
||||
ui->address->setText("");
|
||||
}
|
||||
|
||||
void MapMaidenheadDialog::on_maidenhead_returnPressed()
|
||||
{
|
||||
float latitude, longitude;
|
||||
QString locator = ui->maidenhead->text();
|
||||
|
||||
if (Maidenhead::fromMaidenhead(locator, latitude, longitude))
|
||||
{
|
||||
ui->error->setText("");
|
||||
ui->latAndLong->setText(QString("%1,%2").arg(latitude).arg(longitude));
|
||||
}
|
||||
else
|
||||
{
|
||||
ui->error->setText("Not a valid Maidenhead locator");
|
||||
ui->latAndLong->setText("");
|
||||
QApplication::beep();
|
||||
}
|
||||
ui->address->setText("");
|
||||
}
|
||||
|
||||
void MapMaidenheadDialog::geoReply()
|
||||
{
|
||||
QGeoCodeReply *pQGeoCode = dynamic_cast<QGeoCodeReply*>(sender());
|
||||
|
||||
if ((pQGeoCode != nullptr) && (pQGeoCode->error() == QGeoCodeReply::NoError))
|
||||
{
|
||||
QList<QGeoLocation> qGeoLocs = pQGeoCode->locations();
|
||||
if (qGeoLocs.size() == 0)
|
||||
{
|
||||
ui->error->setText("No location found for address");
|
||||
QApplication::beep();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (qGeoLocs.size() == 1)
|
||||
{
|
||||
QGeoCoordinate c = qGeoLocs.at(0).coordinate();
|
||||
ui->latAndLong->setText(QString("%1,%2").arg(c.latitude()).arg(c.longitude()));
|
||||
ui->maidenhead->setText(Maidenhead::toMaidenhead(c.latitude(), c.longitude()));
|
||||
}
|
||||
else
|
||||
{
|
||||
// Show dialog allowing user to select from the results
|
||||
MapLocationDialog dialog(qGeoLocs, this);
|
||||
if (dialog.exec() == QDialog::Accepted)
|
||||
{
|
||||
QGeoCoordinate c = dialog.m_selectedLocation.coordinate();
|
||||
ui->latAndLong->setText(QString("%1,%2").arg(c.latitude()).arg(c.longitude()));
|
||||
ui->maidenhead->setText(Maidenhead::toMaidenhead(c.latitude(), c.longitude()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
ui->error->setText(QString("GeoCode error: %1").arg(pQGeoCode->error()));
|
||||
pQGeoCode->deleteLater();
|
||||
}
|
||||
|
||||
// Use a custom close button, so pressing enter doesn't close the dialog
|
||||
void MapMaidenheadDialog::on_close_clicked()
|
||||
{
|
||||
QDialog::accept();
|
||||
}
|
42
plugins/feature/map/mapmaidenheaddialog.h
Normal file
42
plugins/feature/map/mapmaidenheaddialog.h
Normal file
@ -0,0 +1,42 @@
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (C) 2021 Jon Beniston, M7RCE //
|
||||
// //
|
||||
// This program is free software; you can redistribute it and/or modify //
|
||||
// it under the terms of the GNU General Public License as published by //
|
||||
// the Free Software Foundation as version 3 of the License, or //
|
||||
// (at your option) any later version. //
|
||||
// //
|
||||
// This program is distributed in the hope that it will be useful, //
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of //
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
|
||||
// GNU General Public License V3 for more details. //
|
||||
// //
|
||||
// You should have received a copy of the GNU General Public License //
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>. //
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifndef INCLUDE_FEATURE_MAPMAIDENHEADDIALOG_H
|
||||
#define INCLUDE_FEATURE_MAPMAIDENHEADDIALOG_H
|
||||
|
||||
#include "ui_mapmaidenheaddialog.h"
|
||||
|
||||
class MapMaidenheadDialog : public QDialog {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit MapMaidenheadDialog(QWidget* parent = 0);
|
||||
~MapMaidenheadDialog();
|
||||
|
||||
void geoReply();
|
||||
|
||||
private slots:
|
||||
void on_address_returnPressed();
|
||||
void on_latAndLong_returnPressed();
|
||||
void on_maidenhead_returnPressed();
|
||||
void on_close_clicked();
|
||||
|
||||
private:
|
||||
Ui::MapMaidenheadDialog* ui;
|
||||
};
|
||||
|
||||
#endif // INCLUDE_FEATURE_MAPMAIDENHEADDIALOG_H
|
145
plugins/feature/map/mapmaidenheaddialog.ui
Normal file
145
plugins/feature/map/mapmaidenheaddialog.ui
Normal file
@ -0,0 +1,145 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>MapMaidenheadDialog</class>
|
||||
<widget class="QDialog" name="MapMaidenheadDialog">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>565</width>
|
||||
<height>194</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="font">
|
||||
<font>
|
||||
<family>Liberation Sans</family>
|
||||
<pointsize>9</pointsize>
|
||||
</font>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Maidenhead Locator Converter</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Location conversion</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<widget class="QGroupBox" name="groupBox">
|
||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||
<item>
|
||||
<widget class="QLabel" name="infoLabel">
|
||||
<property name="text">
|
||||
<string>Enter a location to convert and press enter:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QFormLayout" name="formLayout">
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="addressLabel">
|
||||
<property name="text">
|
||||
<string>Address</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QLineEdit" name="address">
|
||||
<property name="toolTip">
|
||||
<string>Enter an address to convert to latitude/longitude and a Maidenhead locator</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="latAndLongLabel">
|
||||
<property name="text">
|
||||
<string>Latitude and longitude</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="maidenheadLabel">
|
||||
<property name="text">
|
||||
<string>Maidenhead locator</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="QLineEdit" name="maidenhead">
|
||||
<property name="toolTip">
|
||||
<string>Enter a Maidenhead locator to convert to latitude and longitude</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QLineEdit" name="latAndLong">
|
||||
<property name="focusPolicy">
|
||||
<enum>Qt::StrongFocus</enum>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Enter latitude and longitude to convert to a Maidenhead locator</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="error">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
<spacer name="horizontalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="close">
|
||||
<property name="focusPolicy">
|
||||
<enum>Qt::NoFocus</enum>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Close</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<tabstops>
|
||||
<tabstop>address</tabstop>
|
||||
<tabstop>latAndLong</tabstop>
|
||||
<tabstop>maidenhead</tabstop>
|
||||
<tabstop>close</tabstop>
|
||||
</tabstops>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
@ -35,6 +35,14 @@ const QStringList MapSettings::m_pipeURIs = {
|
||||
QStringLiteral("sdrangel.feature.startracker")
|
||||
};
|
||||
|
||||
// GUI combo box should match ordering in this list
|
||||
const QStringList MapSettings::m_mapProviders = {
|
||||
QStringLiteral("osm"),
|
||||
QStringLiteral("esri"),
|
||||
QStringLiteral("mapbox"),
|
||||
QStringLiteral("mapboxgl")
|
||||
};
|
||||
|
||||
MapSettings::MapSettings()
|
||||
{
|
||||
resetToDefaults();
|
||||
@ -43,6 +51,10 @@ MapSettings::MapSettings()
|
||||
void MapSettings::resetToDefaults()
|
||||
{
|
||||
m_displayNames = true;
|
||||
m_mapProvider = "osm";
|
||||
m_mapBoxApiKey = "";
|
||||
m_mapBoxStyles = "";
|
||||
m_sources = -1;
|
||||
m_title = "Map";
|
||||
m_rgbColor = QColor(225, 25, 99).rgb();
|
||||
m_useReverseAPI = false;
|
||||
@ -57,6 +69,10 @@ QByteArray MapSettings::serialize() const
|
||||
SimpleSerializer s(1);
|
||||
|
||||
s.writeBool(1, m_displayNames);
|
||||
s.writeString(2, m_mapProvider);
|
||||
s.writeString(3, m_mapBoxApiKey);
|
||||
s.writeString(4, m_mapBoxStyles);
|
||||
s.writeU32(5, m_sources);
|
||||
s.writeString(8, m_title);
|
||||
s.writeU32(9, m_rgbColor);
|
||||
s.writeBool(10, m_useReverseAPI);
|
||||
@ -85,6 +101,10 @@ bool MapSettings::deserialize(const QByteArray& data)
|
||||
QString strtmp;
|
||||
|
||||
d.readBool(1, &m_displayNames, true);
|
||||
d.readString(2, &m_mapProvider, "osm");
|
||||
d.readString(3, &m_mapBoxApiKey, "");
|
||||
d.readString(4, &m_mapBoxStyles, "");
|
||||
d.readU32(5, &m_sources, -1);
|
||||
d.readString(8, &m_title, "Map");
|
||||
d.readU32(9, &m_rgbColor, QColor(225, 25, 99).rgb());
|
||||
d.readBool(10, &m_useReverseAPI, false);
|
||||
|
@ -29,20 +29,11 @@ class PipeEndPoint;
|
||||
|
||||
struct MapSettings
|
||||
{
|
||||
struct AvailablePipe
|
||||
{
|
||||
enum {RX, TX, Feature} m_type;
|
||||
int m_setIndex;
|
||||
int m_index;
|
||||
PipeEndPoint *m_source;
|
||||
QString m_id;
|
||||
|
||||
AvailablePipe() = default;
|
||||
AvailablePipe(const AvailablePipe&) = default;
|
||||
AvailablePipe& operator=(const AvailablePipe&) = default;
|
||||
};
|
||||
|
||||
bool m_displayNames;
|
||||
QString m_mapProvider;
|
||||
QString m_mapBoxApiKey;
|
||||
QString m_mapBoxStyles;
|
||||
quint32 m_sources; // Bitmask of SOURCE_*
|
||||
QString m_title;
|
||||
quint32 m_rgbColor;
|
||||
bool m_useReverseAPI;
|
||||
@ -58,6 +49,15 @@ struct MapSettings
|
||||
|
||||
static const QStringList m_pipeTypes;
|
||||
static const QStringList m_pipeURIs;
|
||||
|
||||
static const QStringList m_mapProviders;
|
||||
|
||||
// The first few should match the order in m_pipeTypes for MapGUI::getSourceMask to work
|
||||
static const quint32 SOURCE_ADSB = 0x1;
|
||||
static const quint32 SOURCE_APRS = 0x2;
|
||||
static const quint32 SOURCE_STAR_TRACKER = 0x4;
|
||||
static const quint32 SOURCE_BEACONS = 0x8;
|
||||
static const quint32 SOURCE_STATION = 0x10;
|
||||
};
|
||||
|
||||
#endif // INCLUDE_FEATURE_MAPSETTINGS_H_
|
||||
|
66
plugins/feature/map/mapsettingsdialog.cpp
Normal file
66
plugins/feature/map/mapsettingsdialog.cpp
Normal file
@ -0,0 +1,66 @@
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (C) 2021 Jon Beniston, M7RCE //
|
||||
// //
|
||||
// This program is free software; you can redistribute it and/or modify //
|
||||
// it under the terms of the GNU General Public License as published by //
|
||||
// the Free Software Foundation as version 3 of the License, or //
|
||||
// (at your option) any later version. //
|
||||
// //
|
||||
// This program is distributed in the hope that it will be useful, //
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of //
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
|
||||
// GNU General Public License V3 for more details. //
|
||||
// //
|
||||
// You should have received a copy of the GNU General Public License //
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>. //
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include <QDebug>
|
||||
|
||||
#include "util/units.h"
|
||||
|
||||
#include "mapsettingsdialog.h"
|
||||
#include "maplocationdialog.h"
|
||||
|
||||
MapSettingsDialog::MapSettingsDialog(MapSettings *settings, QWidget* parent) :
|
||||
QDialog(parent),
|
||||
m_settings(settings),
|
||||
ui(new Ui::MapSettingsDialog)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
ui->mapProvider->setCurrentIndex(MapSettings::m_mapProviders.indexOf(settings->m_mapProvider));
|
||||
ui->mapBoxApiKey->setText(settings->m_mapBoxApiKey);
|
||||
ui->mapBoxStyles->setText(settings->m_mapBoxStyles);
|
||||
for (int i = 0; i < ui->sourceList->count(); i++)
|
||||
ui->sourceList->item(i)->setCheckState((m_settings->m_sources & (1 << i)) ? Qt::Checked : Qt::Unchecked);
|
||||
}
|
||||
|
||||
MapSettingsDialog::~MapSettingsDialog()
|
||||
{
|
||||
delete ui;
|
||||
}
|
||||
|
||||
void MapSettingsDialog::accept()
|
||||
{
|
||||
QString mapProvider = MapSettings::m_mapProviders[ui->mapProvider->currentIndex()];
|
||||
QString mapBoxApiKey = ui->mapBoxApiKey->text();
|
||||
QString mapBoxStyles = ui->mapBoxStyles->text();
|
||||
if ((mapProvider != m_settings->m_mapProvider)
|
||||
|| (mapBoxApiKey != m_settings->m_mapBoxApiKey)
|
||||
|| (mapBoxStyles != m_settings->m_mapBoxStyles))
|
||||
{
|
||||
m_settings->m_mapProvider = mapProvider;
|
||||
m_settings->m_mapBoxApiKey = mapBoxApiKey;
|
||||
m_settings->m_mapBoxStyles = mapBoxStyles;
|
||||
m_mapSettingsChanged = true;
|
||||
}
|
||||
else
|
||||
m_mapSettingsChanged = false;
|
||||
m_settings->m_sources = 0;
|
||||
quint32 sources = MapSettings::SOURCE_STATION;
|
||||
for (int i = 0; i < ui->sourceList->count(); i++)
|
||||
sources |= (ui->sourceList->item(i)->checkState() == Qt::Checked) << i;
|
||||
m_sourcesChanged = sources != m_settings->m_sources;
|
||||
m_settings->m_sources = sources;
|
||||
QDialog::accept();
|
||||
}
|
42
plugins/feature/map/mapsettingsdialog.h
Normal file
42
plugins/feature/map/mapsettingsdialog.h
Normal file
@ -0,0 +1,42 @@
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (C) 2021 Jon Beniston, M7RCE //
|
||||
// //
|
||||
// This program is free software; you can redistribute it and/or modify //
|
||||
// it under the terms of the GNU General Public License as published by //
|
||||
// the Free Software Foundation as version 3 of the License, or //
|
||||
// (at your option) any later version. //
|
||||
// //
|
||||
// This program is distributed in the hope that it will be useful, //
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of //
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
|
||||
// GNU General Public License V3 for more details. //
|
||||
// //
|
||||
// You should have received a copy of the GNU General Public License //
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>. //
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifndef INCLUDE_FEATURE_MAPSETTINGSDIALOG_H
|
||||
#define INCLUDE_FEATURE_MAPSETTINGSDIALOG_H
|
||||
|
||||
#include "ui_mapsettingsdialog.h"
|
||||
#include "mapsettings.h"
|
||||
|
||||
class MapSettingsDialog : public QDialog {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit MapSettingsDialog(MapSettings *settings, QWidget* parent = 0);
|
||||
~MapSettingsDialog();
|
||||
|
||||
MapSettings *m_settings;
|
||||
bool m_mapSettingsChanged;
|
||||
bool m_sourcesChanged;
|
||||
|
||||
private slots:
|
||||
void accept();
|
||||
|
||||
private:
|
||||
Ui::MapSettingsDialog* ui;
|
||||
};
|
||||
|
||||
#endif // INCLUDE_FEATURE_MAPSETTINGSDIALOG_H
|
199
plugins/feature/map/mapsettingsdialog.ui
Normal file
199
plugins/feature/map/mapsettingsdialog.ui
Normal file
@ -0,0 +1,199 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>MapSettingsDialog</class>
|
||||
<widget class="QDialog" name="MapSettingsDialog">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>436</width>
|
||||
<height>491</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="font">
|
||||
<font>
|
||||
<family>Liberation Sans</family>
|
||||
<pointsize>9</pointsize>
|
||||
</font>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Select a Location</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<widget class="QGroupBox" name="groupBox">
|
||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QLabel" name="locationsLabel">
|
||||
<property name="text">
|
||||
<string>Select data to display:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QListWidget" name="sourceList">
|
||||
<property name="selectionMode">
|
||||
<enum>QAbstractItemView::MultiSelection</enum>
|
||||
</property>
|
||||
<property name="sortingEnabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>ADS-B</string>
|
||||
</property>
|
||||
<property name="checkState">
|
||||
<enum>Checked</enum>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>APRS</string>
|
||||
</property>
|
||||
<property name="checkState">
|
||||
<enum>Checked</enum>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Star Tracker</string>
|
||||
</property>
|
||||
<property name="checkState">
|
||||
<enum>Checked</enum>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Beacons</string>
|
||||
</property>
|
||||
<property name="checkState">
|
||||
<enum>Checked</enum>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="groupBox_2">
|
||||
<property name="title">
|
||||
<string>Map Provider Settings</string>
|
||||
</property>
|
||||
<layout class="QFormLayout" name="formLayout_2">
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="mapProviderLabel">
|
||||
<property name="text">
|
||||
<string>Map provider</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QComboBox" name="mapProvider">
|
||||
<property name="toolTip">
|
||||
<string>Select map provider</string>
|
||||
</property>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>OpenStreetMap</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>ESRI</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Mapbox</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>MapboxGL</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="mapBoxApiKeyLabel">
|
||||
<property name="text">
|
||||
<string>Mapbox API Key</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QLineEdit" name="mapBoxApiKey">
|
||||
<property name="toolTip">
|
||||
<string>Enter a Mapbox API key in order to use Mapbox maps</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="mapBoxStylesLabel">
|
||||
<property name="text">
|
||||
<string>MapboxGL Styles</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="QLineEdit" name="mapBoxStyles">
|
||||
<property name="toolTip">
|
||||
<string>Comma separated list of MapBox styles</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QDialogButtonBox" name="buttonBox">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="standardButtons">
|
||||
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections>
|
||||
<connection>
|
||||
<sender>buttonBox</sender>
|
||||
<signal>accepted()</signal>
|
||||
<receiver>MapSettingsDialog</receiver>
|
||||
<slot>accept()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>248</x>
|
||||
<y>254</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>157</x>
|
||||
<y>274</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>buttonBox</sender>
|
||||
<signal>rejected()</signal>
|
||||
<receiver>MapSettingsDialog</receiver>
|
||||
<slot>reject()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>316</x>
|
||||
<y>260</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>286</x>
|
||||
<y>274</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
</connections>
|
||||
</ui>
|
@ -2,39 +2,79 @@
|
||||
|
||||
<h2>Introduction</h2>
|
||||
|
||||
The Map Feature plugin displays a world map. On top of this, it can plot data from other plugins, such as APRS symbols from the APRS Feature, aircraft from the ADS-B Demodulator or the Sun, Moon and Stars from the Star Tracker.
|
||||
The Map Feature plugin displays a world map. It can display street maps, satellite imagery as well as custom map types.
|
||||
On top of this, it can plot data from other plugins, such as APRS symbols from the APRS Feature, aircraft from the ADS-B Demodulator
|
||||
or the Sun, Moon and Stars from the Star Tracker. It can also display beacon locations based on the IARU Region 1 beacon database.
|
||||
|
||||
![Map feature](../../../doc/img/Map_plugin_beacons.png)
|
||||
|
||||
<h2>Interface</h2>
|
||||
|
||||
![Map feature plugin GUI](../../../doc/img/Map_plugin.png)
|
||||
|
||||
<h3>1: Source Channels</h3>
|
||||
|
||||
This displays the list of channels the Map is displaying data from.
|
||||
|
||||
<h3>2: Find</h3>
|
||||
<h3>1: Find</h3>
|
||||
|
||||
To centre the map on an object or location, enter:
|
||||
|
||||
* An object name.
|
||||
* Latitude and longitude. This can be in decimal degrees (E.g: -23.666413, -46.573550) or degrees, minutes and seconds (E.g: 50°40'46.461"N 95°48'26.533"W or 33d51m54.5148sS 151d12m35.6400sE).
|
||||
* A Maidenhead locator (E.g: IO86av).
|
||||
* An address (E.g: St Katharine's & Wapping, London EC3N 4AB)
|
||||
|
||||
<h3>3: Display Names</h3>
|
||||
<h3>2: Map Type</h3>
|
||||
|
||||
Allows you to select a map type. The available types will depend upon the Map provider
|
||||
selected under Display Settings (7).
|
||||
|
||||
<h3>3: Maidenhead locator conversion</h3>
|
||||
|
||||
When checked, opens the Maidenhead locator converter dialog, which allows conversion between addresses, latitude and longitude and Maidenhead locators.
|
||||
|
||||
<h3>4: Display Beacon dialog</h3>
|
||||
|
||||
When clicked, opens the Beacon dialog. Initially, no beacons will be listed. To download the IARU Region 1 beacon list, click the download button in the top right.
|
||||
The beacons will then be displayed in the table and on the map.
|
||||
|
||||
* Double clicking in a cell in the beacon table in the Callsign or Location columns, will centre the map on that beacon.
|
||||
* Double clicking on the Frequency column will set the Device center frequency.
|
||||
|
||||
![Beacon dialog](../../../doc/img/Map_plugin_beacon_dialog.png)
|
||||
|
||||
<h3>5: Display Names</h3>
|
||||
|
||||
When checked, names of objects are displayed in a bubble next to each object.
|
||||
|
||||
<h3>4: Delete</h3>
|
||||
<h3>6: Delete</h3>
|
||||
|
||||
When clicked, all items will be deleted from the map.
|
||||
|
||||
<h3>7: Display settings</h3>
|
||||
|
||||
When clicked, opens the Map Display Settings dialog, which allows setting:
|
||||
|
||||
* Which data the Map will display.
|
||||
* Which Map provider will be used to source the map image.
|
||||
|
||||
In order to display Mapbox maps, you will need to enter an API Key. A key can be obtained by registering at: http://www.mapbox.com/
|
||||
Note that it is not currently possible to support entering an API Key for Open Street Maps, in order to remove the watermarks.
|
||||
|
||||
<h3>Map</h3>
|
||||
|
||||
The map displays objects reported by other SDRangel channels and features.
|
||||
The map displays objects reported by other SDRangel channels and features, as well as beacon locations.
|
||||
|
||||
* The antenna location is placed according to My Position set under the Preferences > My Position menu. The position is only updated when the Map plugin is first opened.
|
||||
* The "Home" antenna location is placed according to My Position set under the Preferences > My Position menu. The position is only updated when the Map plugin is first opened.
|
||||
* To pan around the map, click the left mouse button and drag. To zoom in or out, use the mouse scroll wheel.
|
||||
* Clicking on an object in the map will display a text bubble with additional information about the object.
|
||||
* Single clicking on an object in the map will display a text bubble with additional information about the object.
|
||||
* Right clicking on a object will open a context menu, which allows:
|
||||
* To set an object as the target. The target object with have its azimuth and elevation displayed in the text bubble and sent to the Rotator Controller feature.
|
||||
* Setting the center frequency to the first frequency found in the text bubble for the object.
|
||||
* Changing the order in which the objects are drawn, which can be help to cycle through multiple objects that are at the same position.
|
||||
|
||||
<h2>Attribution</h2>
|
||||
|
||||
IARU Region 1 beacon list used with permission from: https://iaru-r1-c5-beacons.org/ To add or update a beacon, see: https://iaru-r1-c5-beacons.org/index.php/beacon-update/
|
||||
|
||||
Mapping and geolocation services are by Open Street Map: https://www.openstreetmap.org/ esri: https://www.esri.com/ and Mapbox: https://www.mapbox.com/
|
||||
|
||||
<h2>API</h2>
|
||||
|
||||
|
@ -391,7 +391,7 @@ void StarTrackerWorker::sendToMap(QList<MessageQueue*> *mapMessageQueues, QStrin
|
||||
swgMapItem->setImage(new QString(image));
|
||||
swgMapItem->setImageRotation(rotation);
|
||||
swgMapItem->setText(new QString(text));
|
||||
swgMapItem->setImageFixedSize(1);
|
||||
swgMapItem->setImageMinZoom(3);
|
||||
|
||||
MainCore::MsgMapItem *msg = MainCore::MsgMapItem::create(m_starTracker, swgMapItem);
|
||||
(*it)->push(msg);
|
||||
|
@ -52,10 +52,15 @@ bool Maidenhead::fromMaidenhead(const QString& maidenhead, float& latitude, floa
|
||||
int lat1 = maidenhead[1].toUpper().toLatin1() - 'A';
|
||||
int lon2 = maidenhead[2].toLatin1() - '0';
|
||||
int lat2 = maidenhead[3].toLatin1() - '0';
|
||||
int lon3 = maidenhead[4].toUpper().toLatin1() - 'A';
|
||||
int lat3 = maidenhead[5].toUpper().toLatin1() - 'A';
|
||||
int lon3 = 0;
|
||||
int lat3 = 0;
|
||||
int lon4 = 0;
|
||||
int lat4 = 0;
|
||||
if (maidenhead.length() >= 6)
|
||||
{
|
||||
lon3 = maidenhead[4].toUpper().toLatin1() - 'A';
|
||||
lat3 = maidenhead[5].toUpper().toLatin1() - 'A';
|
||||
}
|
||||
if (maidenhead.length() == 8)
|
||||
{
|
||||
lon4 = maidenhead[6].toLatin1() - '0';
|
||||
@ -76,8 +81,8 @@ bool Maidenhead::fromMaidenhead(const QString& maidenhead, float& latitude, floa
|
||||
bool Maidenhead::isMaidenhead(const QString& maidenhead)
|
||||
{
|
||||
int length = maidenhead.length();
|
||||
if ((length != 6) && (length != 8))
|
||||
if ((length != 4) && (length != 6) && (length != 8))
|
||||
return false;
|
||||
QRegExp re("[A-Ra-r][A-Ra-r][0-9][0-9][A-Xa-x][A-Xa-x]([0-9][0-9])?");
|
||||
QRegExp re("[A-Ra-r][A-Ra-r][0-9][0-9]([A-Xa-x][A-Xa-x]([0-9][0-9])?)?");
|
||||
return re.exactMatch(maidenhead);
|
||||
}
|
||||
|
@ -41,9 +41,11 @@ MapItem:
|
||||
imageRotation:
|
||||
description: "Angle to rotate the image by"
|
||||
type: integer
|
||||
imageFixedSize:
|
||||
description: "Keep the image the same size, regardless of map zoom level (1 for yes, 0 for no)"
|
||||
default: 0
|
||||
imageMinZoom:
|
||||
description: "Minimim zoom value"
|
||||
type: integer
|
||||
default: 11
|
||||
text:
|
||||
descrption: "Text to draw on the map when item is selected"
|
||||
type: string
|
||||
@ -55,3 +57,7 @@ MapItem:
|
||||
description: "Longitude in decimal degrees, positive to the east"
|
||||
type: number
|
||||
format: float
|
||||
altitude:
|
||||
description: "Altitude / height above sea level in metres"
|
||||
type: number
|
||||
format: float
|
||||
|
@ -34,14 +34,16 @@ SWGMapItem::SWGMapItem() {
|
||||
m_image_isSet = false;
|
||||
image_rotation = 0;
|
||||
m_image_rotation_isSet = false;
|
||||
image_fixed_size = 0;
|
||||
m_image_fixed_size_isSet = false;
|
||||
image_min_zoom = 0;
|
||||
m_image_min_zoom_isSet = false;
|
||||
text = nullptr;
|
||||
m_text_isSet = false;
|
||||
latitude = 0.0f;
|
||||
m_latitude_isSet = false;
|
||||
longitude = 0.0f;
|
||||
m_longitude_isSet = false;
|
||||
altitude = 0.0f;
|
||||
m_altitude_isSet = false;
|
||||
}
|
||||
|
||||
SWGMapItem::~SWGMapItem() {
|
||||
@ -56,14 +58,16 @@ SWGMapItem::init() {
|
||||
m_image_isSet = false;
|
||||
image_rotation = 0;
|
||||
m_image_rotation_isSet = false;
|
||||
image_fixed_size = 0;
|
||||
m_image_fixed_size_isSet = false;
|
||||
image_min_zoom = 0;
|
||||
m_image_min_zoom_isSet = false;
|
||||
text = new QString("");
|
||||
m_text_isSet = false;
|
||||
latitude = 0.0f;
|
||||
m_latitude_isSet = false;
|
||||
longitude = 0.0f;
|
||||
m_longitude_isSet = false;
|
||||
altitude = 0.0f;
|
||||
m_altitude_isSet = false;
|
||||
}
|
||||
|
||||
void
|
||||
@ -81,6 +85,7 @@ SWGMapItem::cleanup() {
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
SWGMapItem*
|
||||
@ -100,7 +105,7 @@ SWGMapItem::fromJsonObject(QJsonObject &pJson) {
|
||||
|
||||
::SWGSDRangel::setValue(&image_rotation, pJson["imageRotation"], "qint32", "");
|
||||
|
||||
::SWGSDRangel::setValue(&image_fixed_size, pJson["imageFixedSize"], "qint32", "");
|
||||
::SWGSDRangel::setValue(&image_min_zoom, pJson["imageMinZoom"], "qint32", "");
|
||||
|
||||
::SWGSDRangel::setValue(&text, pJson["text"], "QString", "QString");
|
||||
|
||||
@ -108,6 +113,8 @@ SWGMapItem::fromJsonObject(QJsonObject &pJson) {
|
||||
|
||||
::SWGSDRangel::setValue(&longitude, pJson["longitude"], "float", "");
|
||||
|
||||
::SWGSDRangel::setValue(&altitude, pJson["altitude"], "float", "");
|
||||
|
||||
}
|
||||
|
||||
QString
|
||||
@ -133,8 +140,8 @@ SWGMapItem::asJsonObject() {
|
||||
if(m_image_rotation_isSet){
|
||||
obj->insert("imageRotation", QJsonValue(image_rotation));
|
||||
}
|
||||
if(m_image_fixed_size_isSet){
|
||||
obj->insert("imageFixedSize", QJsonValue(image_fixed_size));
|
||||
if(m_image_min_zoom_isSet){
|
||||
obj->insert("imageMinZoom", QJsonValue(image_min_zoom));
|
||||
}
|
||||
if(text != nullptr && *text != QString("")){
|
||||
toJsonValue(QString("text"), text, obj, QString("QString"));
|
||||
@ -145,6 +152,9 @@ SWGMapItem::asJsonObject() {
|
||||
if(m_longitude_isSet){
|
||||
obj->insert("longitude", QJsonValue(longitude));
|
||||
}
|
||||
if(m_altitude_isSet){
|
||||
obj->insert("altitude", QJsonValue(altitude));
|
||||
}
|
||||
|
||||
return obj;
|
||||
}
|
||||
@ -180,13 +190,13 @@ SWGMapItem::setImageRotation(qint32 image_rotation) {
|
||||
}
|
||||
|
||||
qint32
|
||||
SWGMapItem::getImageFixedSize() {
|
||||
return image_fixed_size;
|
||||
SWGMapItem::getImageMinZoom() {
|
||||
return image_min_zoom;
|
||||
}
|
||||
void
|
||||
SWGMapItem::setImageFixedSize(qint32 image_fixed_size) {
|
||||
this->image_fixed_size = image_fixed_size;
|
||||
this->m_image_fixed_size_isSet = true;
|
||||
SWGMapItem::setImageMinZoom(qint32 image_min_zoom) {
|
||||
this->image_min_zoom = image_min_zoom;
|
||||
this->m_image_min_zoom_isSet = true;
|
||||
}
|
||||
|
||||
QString*
|
||||
@ -219,6 +229,16 @@ SWGMapItem::setLongitude(float longitude) {
|
||||
this->m_longitude_isSet = true;
|
||||
}
|
||||
|
||||
float
|
||||
SWGMapItem::getAltitude() {
|
||||
return altitude;
|
||||
}
|
||||
void
|
||||
SWGMapItem::setAltitude(float altitude) {
|
||||
this->altitude = altitude;
|
||||
this->m_altitude_isSet = true;
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
SWGMapItem::isSet(){
|
||||
@ -233,7 +253,7 @@ SWGMapItem::isSet(){
|
||||
if(m_image_rotation_isSet){
|
||||
isObjectUpdated = true; break;
|
||||
}
|
||||
if(m_image_fixed_size_isSet){
|
||||
if(m_image_min_zoom_isSet){
|
||||
isObjectUpdated = true; break;
|
||||
}
|
||||
if(text && *text != QString("")){
|
||||
@ -245,6 +265,9 @@ SWGMapItem::isSet(){
|
||||
if(m_longitude_isSet){
|
||||
isObjectUpdated = true; break;
|
||||
}
|
||||
if(m_altitude_isSet){
|
||||
isObjectUpdated = true; break;
|
||||
}
|
||||
}while(false);
|
||||
return isObjectUpdated;
|
||||
}
|
||||
|
@ -51,8 +51,8 @@ public:
|
||||
qint32 getImageRotation();
|
||||
void setImageRotation(qint32 image_rotation);
|
||||
|
||||
qint32 getImageFixedSize();
|
||||
void setImageFixedSize(qint32 image_fixed_size);
|
||||
qint32 getImageMinZoom();
|
||||
void setImageMinZoom(qint32 image_min_zoom);
|
||||
|
||||
QString* getText();
|
||||
void setText(QString* text);
|
||||
@ -63,6 +63,9 @@ public:
|
||||
float getLongitude();
|
||||
void setLongitude(float longitude);
|
||||
|
||||
float getAltitude();
|
||||
void setAltitude(float altitude);
|
||||
|
||||
|
||||
virtual bool isSet() override;
|
||||
|
||||
@ -76,8 +79,8 @@ private:
|
||||
qint32 image_rotation;
|
||||
bool m_image_rotation_isSet;
|
||||
|
||||
qint32 image_fixed_size;
|
||||
bool m_image_fixed_size_isSet;
|
||||
qint32 image_min_zoom;
|
||||
bool m_image_min_zoom_isSet;
|
||||
|
||||
QString* text;
|
||||
bool m_text_isSet;
|
||||
@ -88,6 +91,9 @@ private:
|
||||
float longitude;
|
||||
bool m_longitude_isSet;
|
||||
|
||||
float altitude;
|
||||
bool m_altitude_isSet;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -34,14 +34,16 @@ SWGMapItem_2::SWGMapItem_2() {
|
||||
m_image_isSet = false;
|
||||
image_rotation = 0;
|
||||
m_image_rotation_isSet = false;
|
||||
image_fixed_size = 0;
|
||||
m_image_fixed_size_isSet = false;
|
||||
image_min_zoom = 0;
|
||||
m_image_min_zoom_isSet = false;
|
||||
text = nullptr;
|
||||
m_text_isSet = false;
|
||||
latitude = 0.0f;
|
||||
m_latitude_isSet = false;
|
||||
longitude = 0.0f;
|
||||
m_longitude_isSet = false;
|
||||
altitude = 0.0f;
|
||||
m_altitude_isSet = false;
|
||||
}
|
||||
|
||||
SWGMapItem_2::~SWGMapItem_2() {
|
||||
@ -56,14 +58,16 @@ SWGMapItem_2::init() {
|
||||
m_image_isSet = false;
|
||||
image_rotation = 0;
|
||||
m_image_rotation_isSet = false;
|
||||
image_fixed_size = 0;
|
||||
m_image_fixed_size_isSet = false;
|
||||
image_min_zoom = 0;
|
||||
m_image_min_zoom_isSet = false;
|
||||
text = new QString("");
|
||||
m_text_isSet = false;
|
||||
latitude = 0.0f;
|
||||
m_latitude_isSet = false;
|
||||
longitude = 0.0f;
|
||||
m_longitude_isSet = false;
|
||||
altitude = 0.0f;
|
||||
m_altitude_isSet = false;
|
||||
}
|
||||
|
||||
void
|
||||
@ -81,6 +85,7 @@ SWGMapItem_2::cleanup() {
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
SWGMapItem_2*
|
||||
@ -100,7 +105,7 @@ SWGMapItem_2::fromJsonObject(QJsonObject &pJson) {
|
||||
|
||||
::SWGSDRangel::setValue(&image_rotation, pJson["imageRotation"], "qint32", "");
|
||||
|
||||
::SWGSDRangel::setValue(&image_fixed_size, pJson["imageFixedSize"], "qint32", "");
|
||||
::SWGSDRangel::setValue(&image_min_zoom, pJson["imageMinZoom"], "qint32", "");
|
||||
|
||||
::SWGSDRangel::setValue(&text, pJson["text"], "QString", "QString");
|
||||
|
||||
@ -108,6 +113,8 @@ SWGMapItem_2::fromJsonObject(QJsonObject &pJson) {
|
||||
|
||||
::SWGSDRangel::setValue(&longitude, pJson["longitude"], "float", "");
|
||||
|
||||
::SWGSDRangel::setValue(&altitude, pJson["altitude"], "float", "");
|
||||
|
||||
}
|
||||
|
||||
QString
|
||||
@ -133,8 +140,8 @@ SWGMapItem_2::asJsonObject() {
|
||||
if(m_image_rotation_isSet){
|
||||
obj->insert("imageRotation", QJsonValue(image_rotation));
|
||||
}
|
||||
if(m_image_fixed_size_isSet){
|
||||
obj->insert("imageFixedSize", QJsonValue(image_fixed_size));
|
||||
if(m_image_min_zoom_isSet){
|
||||
obj->insert("imageMinZoom", QJsonValue(image_min_zoom));
|
||||
}
|
||||
if(text != nullptr && *text != QString("")){
|
||||
toJsonValue(QString("text"), text, obj, QString("QString"));
|
||||
@ -145,6 +152,9 @@ SWGMapItem_2::asJsonObject() {
|
||||
if(m_longitude_isSet){
|
||||
obj->insert("longitude", QJsonValue(longitude));
|
||||
}
|
||||
if(m_altitude_isSet){
|
||||
obj->insert("altitude", QJsonValue(altitude));
|
||||
}
|
||||
|
||||
return obj;
|
||||
}
|
||||
@ -180,13 +190,13 @@ SWGMapItem_2::setImageRotation(qint32 image_rotation) {
|
||||
}
|
||||
|
||||
qint32
|
||||
SWGMapItem_2::getImageFixedSize() {
|
||||
return image_fixed_size;
|
||||
SWGMapItem_2::getImageMinZoom() {
|
||||
return image_min_zoom;
|
||||
}
|
||||
void
|
||||
SWGMapItem_2::setImageFixedSize(qint32 image_fixed_size) {
|
||||
this->image_fixed_size = image_fixed_size;
|
||||
this->m_image_fixed_size_isSet = true;
|
||||
SWGMapItem_2::setImageMinZoom(qint32 image_min_zoom) {
|
||||
this->image_min_zoom = image_min_zoom;
|
||||
this->m_image_min_zoom_isSet = true;
|
||||
}
|
||||
|
||||
QString*
|
||||
@ -219,6 +229,16 @@ SWGMapItem_2::setLongitude(float longitude) {
|
||||
this->m_longitude_isSet = true;
|
||||
}
|
||||
|
||||
float
|
||||
SWGMapItem_2::getAltitude() {
|
||||
return altitude;
|
||||
}
|
||||
void
|
||||
SWGMapItem_2::setAltitude(float altitude) {
|
||||
this->altitude = altitude;
|
||||
this->m_altitude_isSet = true;
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
SWGMapItem_2::isSet(){
|
||||
@ -233,7 +253,7 @@ SWGMapItem_2::isSet(){
|
||||
if(m_image_rotation_isSet){
|
||||
isObjectUpdated = true; break;
|
||||
}
|
||||
if(m_image_fixed_size_isSet){
|
||||
if(m_image_min_zoom_isSet){
|
||||
isObjectUpdated = true; break;
|
||||
}
|
||||
if(text && *text != QString("")){
|
||||
@ -245,6 +265,9 @@ SWGMapItem_2::isSet(){
|
||||
if(m_longitude_isSet){
|
||||
isObjectUpdated = true; break;
|
||||
}
|
||||
if(m_altitude_isSet){
|
||||
isObjectUpdated = true; break;
|
||||
}
|
||||
}while(false);
|
||||
return isObjectUpdated;
|
||||
}
|
||||
|
@ -51,8 +51,8 @@ public:
|
||||
qint32 getImageRotation();
|
||||
void setImageRotation(qint32 image_rotation);
|
||||
|
||||
qint32 getImageFixedSize();
|
||||
void setImageFixedSize(qint32 image_fixed_size);
|
||||
qint32 getImageMinZoom();
|
||||
void setImageMinZoom(qint32 image_min_zoom);
|
||||
|
||||
QString* getText();
|
||||
void setText(QString* text);
|
||||
@ -63,6 +63,9 @@ public:
|
||||
float getLongitude();
|
||||
void setLongitude(float longitude);
|
||||
|
||||
float getAltitude();
|
||||
void setAltitude(float altitude);
|
||||
|
||||
|
||||
virtual bool isSet() override;
|
||||
|
||||
@ -76,8 +79,8 @@ private:
|
||||
qint32 image_rotation;
|
||||
bool m_image_rotation_isSet;
|
||||
|
||||
qint32 image_fixed_size;
|
||||
bool m_image_fixed_size_isSet;
|
||||
qint32 image_min_zoom;
|
||||
bool m_image_min_zoom_isSet;
|
||||
|
||||
QString* text;
|
||||
bool m_text_isSet;
|
||||
@ -88,6 +91,9 @@ private:
|
||||
float longitude;
|
||||
bool m_longitude_isSet;
|
||||
|
||||
float altitude;
|
||||
bool m_altitude_isSet;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user