mirror of
https://github.com/f4exb/sdrangel.git
synced 2025-08-22 07:22:27 -04:00
Map: Add PFD, first person view and path smoothing. Only send changes via CZML.
This commit is contained in:
parent
29f7d534e5
commit
ff3b3f4ef5
@ -50,6 +50,15 @@ void CesiumInterface::setView(float latitude, float longitude, float altitude)
|
||||
send(obj);
|
||||
}
|
||||
|
||||
void CesiumInterface::setViewFirstPerson(bool firstPerson)
|
||||
{
|
||||
QJsonObject obj {
|
||||
{"command", "setViewFirstPerson"},
|
||||
{"firstPerson", firstPerson}
|
||||
};
|
||||
send(obj);
|
||||
}
|
||||
|
||||
// Play glTF model animation for the map item with the specified name
|
||||
void CesiumInterface::playAnimation(const QString &name, Animation *animation)
|
||||
{
|
||||
|
@ -56,6 +56,7 @@ public:
|
||||
CesiumInterface(const MapSettings *settings, QObject *parent = nullptr);
|
||||
void setHomeView(float latitude, float longitude, float angle=1.0f);
|
||||
void setView(float latitude, float longitude, float altitude=60000);
|
||||
void setViewFirstPerson(bool firstPerson);
|
||||
void playAnimation(const QString &name, Animation *animation);
|
||||
void setDateTime(QDateTime dateTime);
|
||||
void getDateTime();
|
||||
@ -69,6 +70,7 @@ public:
|
||||
void setHDR(bool enabled);
|
||||
void setFog(bool enabled);
|
||||
void showFPS(bool show);
|
||||
void showPFD(bool show);
|
||||
void showMUF(bool show);
|
||||
void showfoF2(bool show);
|
||||
void showMagDec(bool show);
|
||||
|
@ -23,6 +23,8 @@
|
||||
|
||||
#include "util/coordinates.h"
|
||||
|
||||
// FIXME: Cesium now has some additional options: CLAMP_TO_TERRAIN, RELATIVE_TO_TERRAIN, CLAMP_TO_3D_TILE, RELATIVE_TO_3D_TILE
|
||||
// CLIP_TO_GROUND is our own addition
|
||||
const QStringList CZML::m_heightReferences = {"NONE", "CLAMP_TO_GROUND", "RELATIVE_TO_GROUND", "CLIP_TO_GROUND"};
|
||||
|
||||
CZML::CZML(const MapSettings *settings) :
|
||||
@ -48,7 +50,8 @@ bool CZML::filter(const MapItem *mapItem) const
|
||||
|
||||
QJsonObject CZML::init()
|
||||
{
|
||||
QString start = QDateTime::currentDateTimeUtc().toString(Qt::ISODate);
|
||||
// Start a few seconds in the past, to allow for data to be received
|
||||
QString start = QDateTime::currentDateTimeUtc().addSecs(-4).toString(Qt::ISODate);
|
||||
QString stop = QDateTime::currentDateTimeUtc().addSecs(60*60).toString(Qt::ISODate);
|
||||
QString interval = QString("%1/%2").arg(start).arg(stop);
|
||||
|
||||
@ -241,6 +244,48 @@ QJsonObject CZML::update(PolylineMapItem *mapItem)
|
||||
return obj;
|
||||
}
|
||||
|
||||
static void insertConstantProperty(QJsonObject& properties, const QString& name, const QString& value)
|
||||
{
|
||||
properties.insert(name, value);
|
||||
}
|
||||
|
||||
static void insertProperty(QJsonObject& properties, const QString& name, const QString& dateTime, float value)
|
||||
{
|
||||
if (!std::isnan(value))
|
||||
{
|
||||
QJsonArray ar;
|
||||
ar.push_back(dateTime);
|
||||
ar.push_back(value);
|
||||
QJsonObject obj {
|
||||
{"number", ar},
|
||||
{"backwardExtrapolationType", "HOLD"},
|
||||
{"backwardExtrapolationDuration", 30},
|
||||
{"forwardExtrapolationType", "HOLD"},
|
||||
{"forwardExtrapolationDuration", 30},
|
||||
};
|
||||
properties.insert(name, obj);
|
||||
}
|
||||
}
|
||||
|
||||
// SampledProperties are interpolated
|
||||
// Need to use intervals to avoid interpolation
|
||||
// See: https://sandcastle.cesium.com/?src=CZML%20Custom%20Properties.html&label=All
|
||||
static void insertProperty0(QJsonObject& properties, const QString& name, const QString& dateTime, float value)
|
||||
{
|
||||
if (!std::isnan(value))
|
||||
{
|
||||
QJsonObject obj {
|
||||
{"interval", dateTime + "/3000"}, // Year 3000
|
||||
{"number", value}
|
||||
};
|
||||
|
||||
QJsonArray array {
|
||||
obj
|
||||
};
|
||||
properties.insert(name, array);
|
||||
}
|
||||
}
|
||||
|
||||
QJsonObject CZML::update(ObjectMapItem *mapItem, bool isTarget, bool isSelected)
|
||||
{
|
||||
(void) isTarget;
|
||||
@ -274,8 +319,9 @@ QJsonObject CZML::update(ObjectMapItem *mapItem, bool isTarget, bool isSelected)
|
||||
|
||||
// Keep a hash of the time we first saw each item
|
||||
bool existingId = m_ids.contains(id);
|
||||
State& state = m_ids[id];
|
||||
if (!existingId) {
|
||||
m_ids.insert(id, dt);
|
||||
state.m_firstSeenDateTime = dt;
|
||||
}
|
||||
|
||||
bool removeObj = false;
|
||||
@ -319,13 +365,48 @@ QJsonObject CZML::update(ObjectMapItem *mapItem, bool isTarget, bool isSelected)
|
||||
}
|
||||
else
|
||||
{
|
||||
// Only send latest position, to reduce processing
|
||||
if (!fixedPosition && mapItem->m_positionDateTime.isValid()) {
|
||||
coords.push_back(mapItem->m_positionDateTime.toString(Qt::ISODateWithMs));
|
||||
bool useDateTime = !fixedPosition && mapItem->m_positionDateTime.isValid();
|
||||
|
||||
// Update positions that have been recalculated with interpolation
|
||||
if (!mapItem->m_interpolatedCoords.isEmpty())
|
||||
{
|
||||
for (int i = 0; i < mapItem->m_interpolatedCoords.size(); i++)
|
||||
{
|
||||
if (useDateTime) {
|
||||
coords.push_back(mapItem->m_interpolatedDateTimes[i]->toString(Qt::ISODateWithMs));
|
||||
}
|
||||
coords.push_back(mapItem->m_interpolatedCoords[i]->longitude());
|
||||
coords.push_back(mapItem->m_interpolatedCoords[i]->latitude());
|
||||
coords.push_back(mapItem->m_interpolatedCoords[i]->altitude());
|
||||
}
|
||||
mapItem->m_interpolatedCoords.clear();
|
||||
mapItem->m_interpolatedDateTimes.clear();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Only send latest position, to reduce processing
|
||||
if (!mapItem->m_takenTrackPositionExtrapolated.isEmpty() && mapItem->m_takenTrackPositionExtrapolated.back())
|
||||
{
|
||||
if (useDateTime) {
|
||||
coords.push_back(mapItem->m_takenTrackDateTimes.back()->toString(Qt::ISODateWithMs));
|
||||
}
|
||||
coords.push_back(mapItem->m_takenTrackCoords.back()->longitude());
|
||||
coords.push_back(mapItem->m_takenTrackCoords.back()->latitude());
|
||||
}
|
||||
else
|
||||
{
|
||||
if (useDateTime) {
|
||||
coords.push_back(mapItem->m_positionDateTime.toString(Qt::ISODateWithMs));
|
||||
}
|
||||
coords.push_back(mapItem->m_longitude);
|
||||
coords.push_back(mapItem->m_latitude);
|
||||
}
|
||||
if (!mapItem->m_takenTrackAltitudeExtrapolated.isEmpty() && mapItem->m_takenTrackAltitudeExtrapolated.back()) {
|
||||
coords.push_back(mapItem->m_takenTrackCoords.back()->altitude());
|
||||
} else {
|
||||
coords.push_back(mapItem->m_altitude + mapItem->m_modelAltitudeOffset); // See nodeTransformations comment below, as to why we use this here
|
||||
}
|
||||
}
|
||||
coords.push_back(mapItem->m_longitude);
|
||||
coords.push_back(mapItem->m_latitude);
|
||||
coords.push_back(mapItem->m_altitude);
|
||||
}
|
||||
}
|
||||
else
|
||||
@ -343,6 +424,7 @@ QJsonObject CZML::update(ObjectMapItem *mapItem, bool isTarget, bool isSelected)
|
||||
{
|
||||
// Need 2 different positions to enable extrapolation, otherwise entity may not appear
|
||||
bool hasMoved = m_hasMoved.contains(id);
|
||||
|
||||
if (!hasMoved && m_lastPosition.contains(id) && (m_lastPosition.value(id) != coords))
|
||||
{
|
||||
hasMoved = true;
|
||||
@ -351,12 +433,16 @@ QJsonObject CZML::update(ObjectMapItem *mapItem, bool isTarget, bool isSelected)
|
||||
if (hasMoved && (mapItem->m_itemSettings->m_extrapolate > 0))
|
||||
{
|
||||
position.insert("forwardExtrapolationType", "EXTRAPOLATE");
|
||||
//position.insert("forwardExtrapolationType", "LINEAR_EXTRAPOLATE"); // LAGRANGE is poor for extrapolation
|
||||
//position.insert("forwardExtrapolationType", "HOLD"); // Keeps at last position
|
||||
position.insert("forwardExtrapolationDuration", mapItem->m_itemSettings->m_extrapolate);
|
||||
// Use linear interpolation for now - other two can go crazy with aircraft on the ground
|
||||
// To calc acceleration, we need to use non-linear interpolation.
|
||||
position.insert("interpolationAlgorithm", "LINEAR");
|
||||
position.insert("interpolationDegree", 2);
|
||||
//position.insert("interpolationAlgorithm", "HERMITE");
|
||||
//position.insert("interpolationDegree", "2");
|
||||
//position.insert("interpolationDegree", 2);
|
||||
//position.insert("interpolationAlgorithm", "LAGRANGE");
|
||||
//position.insert("interpolationDegree", "5");
|
||||
//position.insert("interpolationDegree", 5); // crazy interpolation for LAGRANGE
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -365,11 +451,11 @@ QJsonObject CZML::update(ObjectMapItem *mapItem, bool isTarget, bool isSelected)
|
||||
}
|
||||
else
|
||||
{
|
||||
// Interpolation goes wrong at end points
|
||||
// Interpolation goes wrong at end points. FIXME: Check if still true
|
||||
//position.insert("interpolationAlgorithm", "LAGRANGE");
|
||||
//position.insert("interpolationDegree", "5");
|
||||
//position.insert("interpolationDegree", 5);
|
||||
//position.insert("interpolationAlgorithm", "HERMITE");
|
||||
//position.insert("interpolationDegree", "2");
|
||||
//position.insert("interpolationDegree", 2);
|
||||
}
|
||||
}
|
||||
|
||||
@ -398,151 +484,235 @@ QJsonObject CZML::update(ObjectMapItem *mapItem, bool isTarget, bool isSelected)
|
||||
{"forwardExtrapolationType", "NONE"}
|
||||
};
|
||||
|
||||
// Point
|
||||
QColor pointColor = QColor::fromRgba(mapItem->m_itemSettings->m_3DPointColor);
|
||||
QJsonArray pointRGBA {
|
||||
pointColor.red(), pointColor.green(), pointColor.blue(), pointColor.alpha()
|
||||
};
|
||||
QJsonObject pointColorObj {
|
||||
{"rgba", pointRGBA}
|
||||
};
|
||||
QJsonObject point {
|
||||
{"pixelSize", 8},
|
||||
{"color", pointColorObj},
|
||||
{"heightReference", heightReferences[mapItem->m_altitudeReference]},
|
||||
{"show", mapItem->m_itemSettings->m_enabled && mapItem->m_itemSettings->m_display3DPoint}
|
||||
};
|
||||
|
||||
// Model
|
||||
QJsonArray node0Cartesian {
|
||||
{0.0, mapItem->m_modelAltitudeOffset, 0.0}
|
||||
};
|
||||
QJsonObject node0Translation {
|
||||
{"cartesian", node0Cartesian}
|
||||
};
|
||||
QJsonObject node0Transform {
|
||||
{"translation", node0Translation}
|
||||
};
|
||||
QJsonObject nodeTransforms {
|
||||
{"node0", node0Transform},
|
||||
};
|
||||
QJsonObject model {
|
||||
{"gltf", m_settings->m_modelURL + mapItem->m_model},
|
||||
{"incrementallyLoadTextures", false}, // Aircraft will flash as they appear without textures if this is the default of true
|
||||
{"heightReference", heightReferences[mapItem->m_altitudeReference]},
|
||||
{"runAnimations", false},
|
||||
{"show", mapItem->m_itemSettings->m_enabled && mapItem->m_itemSettings->m_display3DModel},
|
||||
{"minimumPixelSize", mapItem->m_itemSettings->m_3DModelMinPixelSize},
|
||||
{"maximumScale", 20000} // Stop it getting too big when zoomed really far out
|
||||
};
|
||||
if (mapItem->m_modelAltitudeOffset != 0.0) {
|
||||
model.insert("nodeTransformations", nodeTransforms);
|
||||
}
|
||||
|
||||
// Path
|
||||
QColor pathColor = QColor::fromRgba(mapItem->m_itemSettings->m_3DTrackColor);
|
||||
QJsonArray pathColorRGBA {
|
||||
pathColor.red(), pathColor.green(), pathColor.blue(), pathColor.alpha()
|
||||
};
|
||||
QJsonObject pathColorObj {
|
||||
{"rgba", pathColorRGBA}
|
||||
};
|
||||
// Paths can't be clamped to ground, so AIS paths can be underground if terrain is used
|
||||
// See: https://github.com/CesiumGS/cesium/issues/7133
|
||||
QJsonObject pathSolidColorMaterial {
|
||||
{"color", pathColorObj}
|
||||
};
|
||||
QJsonObject pathMaterial {
|
||||
{"solidColor", pathSolidColorMaterial}
|
||||
};
|
||||
bool showPath = mapItem->m_itemSettings->m_enabled
|
||||
&& mapItem->m_itemSettings->m_display3DTrack
|
||||
&& ( m_settings->m_displayAllGroundTracks
|
||||
|| (m_settings->m_displaySelectedGroundTracks && isSelected));
|
||||
QJsonObject path {
|
||||
// We want full paths for sat tracker, so leadTime and trailTime should be 0
|
||||
// Should be configurable.. 6000=100mins ~> 1 orbit for LEO
|
||||
//{"leadTime", "6000"},
|
||||
//{"trailTime", "6000"},
|
||||
{"width", "3"},
|
||||
{"material", pathMaterial},
|
||||
{"show", showPath}
|
||||
};
|
||||
|
||||
// Label
|
||||
|
||||
// Prevent labels from being too cluttered when zoomed out
|
||||
// FIXME: These values should come from mapItem or mapItemSettings
|
||||
float displayDistanceMax = std::numeric_limits<float>::max();
|
||||
if ((mapItem->m_group == "Beacons")
|
||||
|| (mapItem->m_group == "AM") || (mapItem->m_group == "FM") || (mapItem->m_group == "DAB")
|
||||
|| (mapItem->m_group == "NavAid")
|
||||
|| (mapItem->m_group == "Waypoints")
|
||||
) {
|
||||
displayDistanceMax = 1000000;
|
||||
} else if ((mapItem->m_group == "Station") || (mapItem->m_group == "Radar") || (mapItem->m_group == "Radio Time Transmitters")) {
|
||||
displayDistanceMax = 10000000;
|
||||
} else if (mapItem->m_group == "Ionosonde Stations") {
|
||||
displayDistanceMax = 30000000;
|
||||
}
|
||||
|
||||
QJsonArray labelPixelOffsetScaleArray {
|
||||
1000000, 20, 10000000, 5
|
||||
};
|
||||
QJsonObject labelPixelOffsetScaleObject {
|
||||
{"nearFarScalar", labelPixelOffsetScaleArray}
|
||||
};
|
||||
QJsonArray labelPixelOffsetArray {
|
||||
1, 0
|
||||
};
|
||||
QJsonObject labelPixelOffset {
|
||||
{"cartesian2", labelPixelOffsetArray}
|
||||
};
|
||||
QJsonArray labelEyeOffsetArray {
|
||||
0, mapItem->m_labelAltitudeOffset, 0 // Position above the object, dependent on the height of the model
|
||||
};
|
||||
QJsonObject labelEyeOffset {
|
||||
{"cartesian", labelEyeOffsetArray}
|
||||
};
|
||||
QJsonObject labelHorizontalOrigin {
|
||||
{"horizontalOrigin", "LEFT"}
|
||||
};
|
||||
QJsonArray labelDisplayDistance {
|
||||
0, displayDistanceMax
|
||||
};
|
||||
QJsonObject labelDistanceDisplayCondition {
|
||||
{"distanceDisplayCondition", labelDisplayDistance}
|
||||
};
|
||||
QString labelText = mapItem->m_label;
|
||||
labelText.replace("<br>", "\n");
|
||||
QJsonObject label {
|
||||
{"text", labelText},
|
||||
{"show", m_settings->m_displayNames && mapItem->m_itemSettings->m_enabled && mapItem->m_itemSettings->m_display3DLabel},
|
||||
{"scale", mapItem->m_itemSettings->m_3DLabelScale},
|
||||
{"pixelOffset", labelPixelOffset},
|
||||
{"pixelOffsetScaleByDistance", labelPixelOffsetScaleObject},
|
||||
{"eyeOffset", labelEyeOffset},
|
||||
{"verticalOrigin", "BASELINE"},
|
||||
{"horizontalOrigin", "LEFT"},
|
||||
{"heightReference", heightReferences[mapItem->m_altitudeReference]},
|
||||
};
|
||||
if (displayDistanceMax != std::numeric_limits<float>::max()) {
|
||||
label.insert("distanceDisplayCondition", labelDistanceDisplayCondition);
|
||||
}
|
||||
|
||||
// Use billboard for APRS as we don't currently have 3D objects
|
||||
QString imageURL = mapItem->m_image;
|
||||
if (imageURL.startsWith("qrc://")) {
|
||||
imageURL = imageURL.mid(6); // Redirect to our embedded webserver, which will check resources
|
||||
}
|
||||
QJsonObject billboard {
|
||||
{"image", imageURL},
|
||||
{"heightReference", heightReferences[mapItem->m_altitudeReference]},
|
||||
{"verticalOrigin", "BOTTOM"} // To stop it being cut in half when zoomed out
|
||||
};
|
||||
|
||||
if (!removeObj)
|
||||
{
|
||||
if (mapItem->m_aircraftState)
|
||||
{
|
||||
QJsonObject properties;
|
||||
QDateTime aircraftStateDateTime = mapItem->m_positionDateTime;
|
||||
QString dateTime = aircraftStateDateTime.toString(Qt::ISODateWithMs);
|
||||
bool aircraftStateDateTimeChanged = aircraftStateDateTime != state.m_aircraftStateDateTime;
|
||||
QString aircraftCallsign = mapItem->m_aircraftState->m_callsign;
|
||||
QString aircraftType = mapItem->m_aircraftState->m_aircraftType;
|
||||
QString aircraftIndicatedAirspeedDateTime = mapItem->m_aircraftState->m_indicatedAirspeedDateTime;
|
||||
float aircraftIndicatedAirspeed = mapItem->m_aircraftState->m_indicatedAirspeed;
|
||||
float aircraftTrueAirspeed = mapItem->m_aircraftState->m_trueAirspeed;
|
||||
float aircraftGroundspeed = mapItem->m_aircraftState->m_groundspeed;
|
||||
QString aircraftAltitudeDateTime = mapItem->m_aircraftState->m_altitudeDateTime;
|
||||
float aircraftAltitude = mapItem->m_aircraftState->m_altitude;
|
||||
int aircraftOnSurface = mapItem->m_aircraftState->m_onSurface;
|
||||
float aircraftMach = mapItem->m_aircraftState->m_mach;
|
||||
float aircraftQNH = mapItem->m_aircraftState->m_qnh;
|
||||
float aircraftVerticalSpeed = mapItem->m_aircraftState->m_verticalSpeed;
|
||||
float aircraftHeading = mapItem->m_aircraftState->m_heading;
|
||||
float aircraftTrack = mapItem->m_aircraftState->m_track;
|
||||
float aircraftRoll = mapItem->m_roll;
|
||||
float aircraftSelectedAltitude = mapItem->m_aircraftState->m_selectedAltitude;
|
||||
float aircraftSelectedHeading = mapItem->m_aircraftState->m_selectedHeading;
|
||||
int aircraftAutopilot = mapItem->m_aircraftState->m_autopilot;
|
||||
MapAircraftState::VerticalMode aircraftVerticalMode = mapItem->m_aircraftState->m_verticalMode;
|
||||
MapAircraftState::LateralMode aircraftLateralMode = mapItem->m_aircraftState->m_lateralMode;
|
||||
MapAircraftState::TCASMode aircraftTCASMode = mapItem->m_aircraftState->m_tcasMode;
|
||||
float aircraftWindSpeed = mapItem->m_aircraftState->m_windSpeed;
|
||||
float aircraftWindDirection = mapItem->m_aircraftState->m_windDirection;
|
||||
float aircraftStaticAirTemperature = mapItem->m_aircraftState->m_staticAirTemperature;
|
||||
|
||||
if ( (!existingId && !aircraftCallsign.isEmpty())
|
||||
|| (aircraftCallsign != state.m_aircraftState.m_callsign)
|
||||
)
|
||||
{
|
||||
insertConstantProperty(properties, "pfdCallsign", aircraftCallsign);
|
||||
state.m_aircraftState.m_callsign = aircraftCallsign;
|
||||
}
|
||||
if ( (!existingId && !aircraftType.isEmpty())
|
||||
|| (aircraftType != state.m_aircraftState.m_aircraftType)
|
||||
)
|
||||
{
|
||||
insertConstantProperty(properties, "pfdAircraftType", aircraftType);
|
||||
state.m_aircraftState.m_aircraftType = aircraftType;
|
||||
}
|
||||
if ( (!existingId && !aircraftIndicatedAirspeedDateTime.isEmpty())
|
||||
|| (aircraftIndicatedAirspeedDateTime != state.m_aircraftState.m_indicatedAirspeedDateTime)
|
||||
|| (aircraftIndicatedAirspeed != state.m_aircraftState.m_indicatedAirspeed)
|
||||
)
|
||||
{
|
||||
insertProperty(properties, "pfdIndicatedAirspeed", aircraftIndicatedAirspeedDateTime, aircraftIndicatedAirspeed);
|
||||
state.m_aircraftState.m_indicatedAirspeedDateTime = aircraftIndicatedAirspeedDateTime;
|
||||
state.m_aircraftState.m_indicatedAirspeed = aircraftIndicatedAirspeed;
|
||||
}
|
||||
if ( !existingId
|
||||
|| aircraftStateDateTimeChanged
|
||||
|| (aircraftTrueAirspeed != state.m_aircraftState.m_trueAirspeed)
|
||||
)
|
||||
{
|
||||
insertProperty(properties, "pfdTrueAirspeed", dateTime, aircraftTrueAirspeed);
|
||||
state.m_aircraftState.m_trueAirspeed = aircraftTrueAirspeed;
|
||||
}
|
||||
if ( !existingId
|
||||
|| aircraftStateDateTimeChanged
|
||||
|| (aircraftGroundspeed != state.m_aircraftState.m_groundspeed)
|
||||
)
|
||||
{
|
||||
insertProperty(properties, "pfdGroundspeed", dateTime, aircraftGroundspeed);
|
||||
state.m_aircraftState.m_groundspeed = aircraftGroundspeed;
|
||||
}
|
||||
if ( (!existingId && !aircraftAltitudeDateTime.isEmpty())
|
||||
|| (aircraftAltitudeDateTime != state.m_aircraftState.m_altitudeDateTime)
|
||||
|| (aircraftAltitude != state.m_aircraftState.m_altitude)
|
||||
)
|
||||
{
|
||||
insertProperty(properties, "pfdAltitude", aircraftAltitudeDateTime, aircraftAltitude);
|
||||
state.m_aircraftState.m_altitudeDateTime = aircraftAltitudeDateTime;
|
||||
state.m_aircraftState.m_altitude = aircraftAltitude;
|
||||
}
|
||||
if ( !existingId
|
||||
|| aircraftStateDateTimeChanged
|
||||
|| (aircraftOnSurface != state.m_aircraftState.m_onSurface)
|
||||
)
|
||||
{
|
||||
insertProperty0(properties, "pfdOnSurface", dateTime, aircraftOnSurface);
|
||||
state.m_aircraftState.m_onSurface = aircraftOnSurface;
|
||||
}
|
||||
if ( !existingId
|
||||
|| aircraftStateDateTimeChanged
|
||||
|| (aircraftMach != state.m_aircraftState.m_mach)
|
||||
)
|
||||
{
|
||||
insertProperty(properties, "pfdMach", dateTime, aircraftMach);
|
||||
state.m_aircraftState.m_mach = aircraftMach;
|
||||
|
||||
}
|
||||
if ( !existingId
|
||||
|| aircraftStateDateTimeChanged
|
||||
|| (aircraftQNH != state.m_aircraftState.m_qnh)
|
||||
)
|
||||
{
|
||||
insertProperty0(properties, "pfdQNH", dateTime, aircraftQNH);
|
||||
state.m_aircraftState.m_qnh = aircraftQNH;
|
||||
}
|
||||
if ( !existingId
|
||||
|| aircraftStateDateTimeChanged
|
||||
|| (aircraftVerticalSpeed != state.m_aircraftState.m_verticalSpeed)
|
||||
)
|
||||
{
|
||||
insertProperty(properties, "pfdVerticalSpeed", dateTime, aircraftVerticalSpeed);
|
||||
state.m_aircraftState.m_verticalSpeed = aircraftVerticalSpeed;
|
||||
}
|
||||
if ( !existingId
|
||||
|| aircraftStateDateTimeChanged
|
||||
|| (aircraftHeading != state.m_aircraftState.m_heading)
|
||||
)
|
||||
{
|
||||
insertProperty(properties, "pfdHeading", dateTime, aircraftHeading);
|
||||
state.m_aircraftState.m_heading = aircraftHeading;
|
||||
}
|
||||
if ( !existingId
|
||||
|| aircraftStateDateTimeChanged
|
||||
|| (aircraftTrack != state.m_aircraftState.m_track)
|
||||
)
|
||||
{
|
||||
insertProperty(properties, "pfdTrack", dateTime, aircraftTrack);
|
||||
state.m_aircraftState.m_track = aircraftTrack;
|
||||
}
|
||||
if ( !existingId
|
||||
|| aircraftStateDateTimeChanged
|
||||
|| (aircraftRoll != state.m_aircraftRoll)
|
||||
)
|
||||
{
|
||||
insertProperty(properties, "pfdRoll", dateTime, aircraftRoll);
|
||||
state.m_aircraftRoll = aircraftRoll;
|
||||
}
|
||||
if ( !existingId
|
||||
|| aircraftStateDateTimeChanged
|
||||
|| (aircraftSelectedAltitude != state.m_aircraftState.m_selectedAltitude)
|
||||
)
|
||||
{
|
||||
insertProperty0(properties, "pfdSelectedAltitude", dateTime, aircraftSelectedAltitude);
|
||||
state.m_aircraftState.m_selectedAltitude = aircraftSelectedAltitude;
|
||||
}
|
||||
if ( !existingId
|
||||
|| aircraftStateDateTimeChanged
|
||||
|| (aircraftSelectedHeading != state.m_aircraftState.m_selectedHeading)
|
||||
)
|
||||
{
|
||||
insertProperty0(properties, "pfdSelectedHeading", dateTime, aircraftSelectedHeading);
|
||||
state.m_aircraftState.m_selectedHeading = aircraftSelectedHeading;
|
||||
}
|
||||
if ( !existingId
|
||||
|| aircraftStateDateTimeChanged
|
||||
|| (aircraftAutopilot != state.m_aircraftState.m_autopilot)
|
||||
)
|
||||
{
|
||||
insertProperty0(properties, "pfdAutopilot", dateTime, aircraftAutopilot);
|
||||
state.m_aircraftState.m_autopilot = aircraftAutopilot;
|
||||
}
|
||||
if ( !existingId
|
||||
|| aircraftStateDateTimeChanged
|
||||
|| (aircraftVerticalMode != state.m_aircraftState.m_verticalMode)
|
||||
)
|
||||
{
|
||||
insertProperty0(properties, "pfdVerticalMode", dateTime, aircraftVerticalMode);
|
||||
state.m_aircraftState.m_verticalMode = aircraftVerticalMode;
|
||||
}
|
||||
if ( !existingId
|
||||
|| aircraftStateDateTimeChanged
|
||||
|| (aircraftLateralMode != state.m_aircraftState.m_lateralMode)
|
||||
)
|
||||
{
|
||||
insertProperty0(properties, "pfdLateralMode", dateTime, aircraftLateralMode);
|
||||
state.m_aircraftState.m_lateralMode = aircraftLateralMode;
|
||||
}
|
||||
if ( !existingId
|
||||
|| aircraftStateDateTimeChanged
|
||||
|| (aircraftTCASMode != state.m_aircraftState.m_tcasMode)
|
||||
)
|
||||
{
|
||||
insertProperty0(properties, "pfdTCASMode", dateTime, aircraftTCASMode);
|
||||
state.m_aircraftState.m_tcasMode = aircraftTCASMode;
|
||||
}
|
||||
if ( !existingId
|
||||
|| aircraftStateDateTimeChanged
|
||||
|| (aircraftWindSpeed != state.m_aircraftState.m_windSpeed)
|
||||
)
|
||||
{
|
||||
insertProperty0(properties, "pfdWindSpeed", dateTime, aircraftWindSpeed);
|
||||
state.m_aircraftState.m_windSpeed = aircraftWindSpeed;
|
||||
}
|
||||
if ( !existingId
|
||||
|| aircraftStateDateTimeChanged
|
||||
|| (aircraftWindDirection != state.m_aircraftState.m_windDirection)
|
||||
)
|
||||
{
|
||||
insertProperty0(properties, "pfdWindDirection", dateTime, aircraftWindDirection);
|
||||
state.m_aircraftState.m_windDirection = aircraftWindDirection;
|
||||
}
|
||||
|
||||
if ( !existingId
|
||||
|| aircraftStateDateTimeChanged
|
||||
|| (aircraftStaticAirTemperature != state.m_aircraftState.m_staticAirTemperature)
|
||||
)
|
||||
{
|
||||
insertProperty0(properties, "pfdStaticAirTemperature", dateTime, aircraftStaticAirTemperature);
|
||||
state.m_aircraftState.m_staticAirTemperature = aircraftStaticAirTemperature;
|
||||
}
|
||||
|
||||
//QJsonObject speedProperty {
|
||||
// {"velocityReference", "#position"},
|
||||
//};
|
||||
//properties.insert("pfdSpeed", speedProperty);
|
||||
|
||||
if ( !existingId
|
||||
|| aircraftStateDateTimeChanged
|
||||
)
|
||||
{
|
||||
state.m_aircraftStateDateTime = aircraftStateDateTime;
|
||||
}
|
||||
|
||||
if (properties.size() > 0) {
|
||||
obj.insert("properties", properties);
|
||||
}
|
||||
}
|
||||
|
||||
obj.insert("position", position);
|
||||
if (!fixedPosition)
|
||||
{
|
||||
@ -552,16 +722,260 @@ QJsonObject CZML::update(ObjectMapItem *mapItem, bool isTarget, bool isSelected)
|
||||
obj.insert("orientation", orientationPosition);
|
||||
}
|
||||
}
|
||||
obj.insert("point", point);
|
||||
if (!mapItem->m_model.isEmpty()) {
|
||||
obj.insert("model", model);
|
||||
} else {
|
||||
|
||||
// Point
|
||||
quint32 pointColorInt = mapItem->m_itemSettings->m_3DPointColor;
|
||||
int pointAltitudeReference = mapItem->m_altitudeReference;
|
||||
bool pointShow = mapItem->m_itemSettings->m_enabled && mapItem->m_itemSettings->m_display3DPoint;
|
||||
|
||||
if ( !existingId
|
||||
|| (pointColorInt != state.m_pointColorInt)
|
||||
|| (pointAltitudeReference != state.m_pointAltitudeReference)
|
||||
|| (pointShow != state.m_pointShow)
|
||||
)
|
||||
{
|
||||
QColor pointColor = QColor::fromRgba(pointColorInt);
|
||||
QJsonArray pointRGBA {
|
||||
pointColor.red(), pointColor.green(), pointColor.blue(), pointColor.alpha()
|
||||
};
|
||||
QJsonObject pointColorObj {
|
||||
{"rgba", pointRGBA}
|
||||
};
|
||||
QJsonObject point {
|
||||
{"pixelSize", 8},
|
||||
{"color", pointColorObj},
|
||||
{"heightReference", heightReferences[pointAltitudeReference]},
|
||||
{"show", pointShow}
|
||||
};
|
||||
obj.insert("point", point);
|
||||
|
||||
state.m_pointColorInt = pointColorInt;
|
||||
state.m_pointAltitudeReference = pointAltitudeReference;
|
||||
state.m_pointShow = pointShow;
|
||||
}
|
||||
|
||||
// Label
|
||||
|
||||
float labelAltitudeOffset = mapItem->m_labelAltitudeOffset;
|
||||
QString labelText = mapItem->m_label;
|
||||
bool labelShow = m_settings->m_displayNames && mapItem->m_itemSettings->m_enabled && mapItem->m_itemSettings->m_display3DLabel;
|
||||
float labelScale = mapItem->m_itemSettings->m_3DLabelScale;
|
||||
int labelAltitudeReference = mapItem->m_altitudeReference;
|
||||
|
||||
if ( !existingId
|
||||
|| (labelAltitudeOffset != state.m_lableAltitudeOffset)
|
||||
|| (labelText != state.m_labelText)
|
||||
|| (labelShow != state.m_labelShow)
|
||||
|| (labelScale != state.m_labelScale)
|
||||
|| (labelAltitudeReference != state.m_labelAltitudeReference)
|
||||
)
|
||||
{
|
||||
// Prevent labels from being too cluttered when zoomed out
|
||||
// FIXME: These values should come from mapItem or mapItemSettings
|
||||
float displayDistanceMax = std::numeric_limits<float>::max();
|
||||
if ((mapItem->m_group == "Beacons")
|
||||
|| (mapItem->m_group == "AM") || (mapItem->m_group == "FM") || (mapItem->m_group == "DAB")
|
||||
|| (mapItem->m_group == "NavAid")
|
||||
|| (mapItem->m_group == "Waypoints")
|
||||
) {
|
||||
displayDistanceMax = 1000000;
|
||||
} else if ((mapItem->m_group == "Station") || (mapItem->m_group == "Radar") || (mapItem->m_group == "Radio Time Transmitters")) {
|
||||
displayDistanceMax = 10000000;
|
||||
} else if (mapItem->m_group == "Ionosonde Stations") {
|
||||
displayDistanceMax = 30000000;
|
||||
}
|
||||
|
||||
QJsonArray labelPixelOffsetScaleArray {
|
||||
1000000, 20, 10000000, 5
|
||||
};
|
||||
QJsonObject labelPixelOffsetScaleObject {
|
||||
{"nearFarScalar", labelPixelOffsetScaleArray}
|
||||
};
|
||||
QJsonArray labelPixelOffsetArray {
|
||||
1, 0
|
||||
};
|
||||
QJsonObject labelPixelOffset {
|
||||
{"cartesian2", labelPixelOffsetArray}
|
||||
};
|
||||
QJsonArray labelEyeOffsetArray {
|
||||
0, labelAltitudeOffset, 0 // Position above the object, dependent on the height of the model
|
||||
};
|
||||
QJsonObject labelEyeOffset {
|
||||
{"cartesian", labelEyeOffsetArray}
|
||||
};
|
||||
QJsonObject labelHorizontalOrigin {
|
||||
{"horizontalOrigin", "LEFT"}
|
||||
};
|
||||
QJsonArray labelDisplayDistance {
|
||||
0, displayDistanceMax
|
||||
};
|
||||
QJsonObject labelDistanceDisplayCondition {
|
||||
{"distanceDisplayCondition", labelDisplayDistance}
|
||||
};
|
||||
|
||||
labelText.replace("<br>", "\n");
|
||||
QJsonObject label {
|
||||
//{"text", labelText},
|
||||
{"show", labelShow},
|
||||
{"scale", labelScale},
|
||||
{"pixelOffset", labelPixelOffset},
|
||||
{"pixelOffsetScaleByDistance", labelPixelOffsetScaleObject},
|
||||
{"eyeOffset", labelEyeOffset},
|
||||
{"verticalOrigin", "BASELINE"},
|
||||
{"horizontalOrigin", "LEFT"},
|
||||
{"heightReference", heightReferences[labelAltitudeReference]},
|
||||
{"style", "FILL_AND_OUTLINE"},
|
||||
};
|
||||
if (!mapItem->m_labelDateTime.isValid())
|
||||
{
|
||||
label.insert("text", labelText);
|
||||
}
|
||||
else
|
||||
{
|
||||
QString interval = mapItem->m_labelDateTime.toString(Qt::ISODateWithMs) + "/2999-12-31";
|
||||
QJsonObject labelInterval {
|
||||
{"interval", interval},
|
||||
{"string", labelText}
|
||||
};
|
||||
QJsonArray labelIntervalArray {
|
||||
labelInterval
|
||||
};
|
||||
label.insert("text", labelIntervalArray);
|
||||
}
|
||||
if (displayDistanceMax != std::numeric_limits<float>::max()) {
|
||||
label.insert("distanceDisplayCondition", labelDistanceDisplayCondition);
|
||||
}
|
||||
|
||||
obj.insert("label", label);
|
||||
|
||||
state.m_lableAltitudeOffset = labelAltitudeOffset;
|
||||
state.m_labelText = labelText;
|
||||
state.m_labelShow = labelShow;
|
||||
state.m_labelScale = labelScale;
|
||||
state.m_labelAltitudeReference = labelAltitudeReference;
|
||||
}
|
||||
|
||||
if (!mapItem->m_model.isEmpty())
|
||||
{
|
||||
// Model
|
||||
QString modelGLTF = m_settings->m_modelURL + mapItem->m_model;
|
||||
int modelAltitudeReference = mapItem->m_altitudeReference;
|
||||
bool modelShow = mapItem->m_itemSettings->m_enabled && mapItem->m_itemSettings->m_display3DModel;
|
||||
int modelMinimumPixelSize = mapItem->m_itemSettings->m_3DModelMinPixelSize;
|
||||
|
||||
if ( !existingId
|
||||
|| (modelGLTF != state.m_modelGLTF)
|
||||
|| (modelAltitudeReference != state.m_modelAltitudeReference)
|
||||
|| (modelShow != state.m_modelShow)
|
||||
|| (modelMinimumPixelSize != state.m_modelMinimumPixelSize)
|
||||
)
|
||||
{
|
||||
QJsonObject model {
|
||||
{"gltf", modelGLTF},
|
||||
//{"incrementallyLoadTextures", false}, // Aircraft will flash as they appear without textures if this is the default of true
|
||||
{"heightReference", heightReferences[modelAltitudeReference]},
|
||||
{"runAnimations", false},
|
||||
{"show", modelShow},
|
||||
{"minimumPixelSize", modelMinimumPixelSize},
|
||||
{"maximumScale", 20000} // Stop it getting too big when zoomed really far out
|
||||
};
|
||||
// Using nodeTransformations stops animations from running.
|
||||
// See: https://github.com/CesiumGS/cesium/issues/11566
|
||||
/*QJsonArray node0Cartesian {
|
||||
{0.0, mapItem->m_modelAltitudeOffset, 0.0}
|
||||
};
|
||||
QJsonObject node0Translation {
|
||||
{"cartesian", node0Cartesian}
|
||||
};
|
||||
QJsonObject node0Transform {
|
||||
{"translation", node0Translation}
|
||||
};
|
||||
QJsonObject nodeTransforms {
|
||||
{"node0", node0Transform},
|
||||
};
|
||||
if (mapItem->m_modelAltitudeOffset != 0.0) {
|
||||
model.insert("nodeTransformations", nodeTransforms);
|
||||
}*/
|
||||
|
||||
obj.insert("model", model);
|
||||
|
||||
state.m_modelGLTF = modelGLTF;
|
||||
state.m_modelAltitudeReference = modelAltitudeReference;
|
||||
state.m_modelShow = modelShow;
|
||||
state.m_modelMinimumPixelSize = modelMinimumPixelSize;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Use billboard for APRS as we don't currently have 3D objects
|
||||
QString imageURL = mapItem->m_image;
|
||||
if (imageURL.startsWith("qrc://")) {
|
||||
imageURL = imageURL.mid(6); // Redirect to our embedded webserver, which will check resources
|
||||
}
|
||||
QJsonObject billboard {
|
||||
{"image", imageURL},
|
||||
{"heightReference", heightReferences[mapItem->m_altitudeReference]},
|
||||
{"verticalOrigin", "BOTTOM"} // To stop it being cut in half when zoomed out
|
||||
};
|
||||
|
||||
obj.insert("billboard", billboard);
|
||||
}
|
||||
obj.insert("label", label);
|
||||
obj.insert("description", mapItem->m_text);
|
||||
if (!fixedPosition) {
|
||||
obj.insert("path", path);
|
||||
|
||||
// Description
|
||||
QString description = mapItem->m_text;
|
||||
if ( !existingId
|
||||
|| (description != state.m_description)
|
||||
)
|
||||
{
|
||||
obj.insert("description", description);
|
||||
state.m_description = description;
|
||||
}
|
||||
|
||||
// Path
|
||||
if (!fixedPosition)
|
||||
{
|
||||
quint32 pathColorInt = mapItem->m_itemSettings->m_3DTrackColor;
|
||||
bool pathShow = mapItem->m_itemSettings->m_enabled
|
||||
&& mapItem->m_itemSettings->m_display3DTrack
|
||||
&& ( m_settings->m_displayAllGroundTracks
|
||||
|| (m_settings->m_displaySelectedGroundTracks && isSelected));
|
||||
|
||||
if ( !existingId
|
||||
|| (pathColorInt != state.m_pathColorInt)
|
||||
|| (pathShow != state.m_pathShow)
|
||||
)
|
||||
{
|
||||
QColor pathColor = QColor::fromRgba(pathColorInt);
|
||||
QJsonArray pathColorRGBA {
|
||||
pathColor.red(), pathColor.green(), pathColor.blue(), pathColor.alpha()
|
||||
};
|
||||
QJsonObject pathColorObj {
|
||||
{"rgba", pathColorRGBA}
|
||||
};
|
||||
// Paths can't be clamped to ground, so AIS paths can be underground if terrain is used
|
||||
// See: https://github.com/CesiumGS/cesium/issues/7133
|
||||
QJsonObject pathSolidColorMaterial {
|
||||
{"color", pathColorObj}
|
||||
};
|
||||
QJsonObject pathMaterial {
|
||||
{"solidColor", pathSolidColorMaterial}
|
||||
};
|
||||
QJsonObject path {
|
||||
// We want full paths for sat tracker, so leadTime and trailTime should be 0
|
||||
// Should be configurable.. 6000=100mins ~> 1 orbit for LEO
|
||||
//{"leadTime", "6000"},
|
||||
//{"trailTime", "6000"},
|
||||
{"width", "3"},
|
||||
{"material", pathMaterial},
|
||||
{"show", pathShow},
|
||||
{"resolution", 1}
|
||||
};
|
||||
|
||||
obj.insert("path", path);
|
||||
|
||||
state.m_pathColorInt = pathColorInt;
|
||||
state.m_pathShow = pathShow;
|
||||
}
|
||||
}
|
||||
|
||||
if (!fixedPosition)
|
||||
@ -577,7 +991,7 @@ QJsonObject CZML::update(ObjectMapItem *mapItem, bool isTarget, bool isSelected)
|
||||
{
|
||||
if (mapItem->m_availableUntil.isValid())
|
||||
{
|
||||
QString period = QString("%1/%2").arg(m_ids[id]).arg(mapItem->m_availableUntil.toString(Qt::ISODateWithMs));
|
||||
QString period = QString("%1/%2").arg(state.m_firstSeenDateTime).arg(mapItem->m_availableUntil.toString(Qt::ISODateWithMs));
|
||||
obj.insert("availability", period);
|
||||
}
|
||||
}
|
||||
@ -586,7 +1000,7 @@ QJsonObject CZML::update(ObjectMapItem *mapItem, bool isTarget, bool isSelected)
|
||||
{
|
||||
if (mapItem->m_availableUntil.isValid())
|
||||
{
|
||||
QString period = QString("%1/%2").arg(m_ids[id]).arg(mapItem->m_availableUntil.toString(Qt::ISODateWithMs));
|
||||
QString period = QString("%1/%2").arg(state.m_firstSeenDateTime).arg(mapItem->m_availableUntil.toString(Qt::ISODateWithMs));
|
||||
obj.insert("availability", period);
|
||||
}
|
||||
}
|
||||
@ -599,13 +1013,24 @@ QJsonObject CZML::update(ObjectMapItem *mapItem, bool isTarget, bool isSelected)
|
||||
}
|
||||
|
||||
// Use our own clipping routine, due to
|
||||
// https://github.com/CesiumGS/cesium/issues/4049
|
||||
if (mapItem->m_altitudeReference == 3) {
|
||||
obj.insert("altitudeReference", "CLIP_TO_GROUND");
|
||||
}
|
||||
// https://github.com/CesiumGS/cesium/issues/4049 - Has now been fixed
|
||||
//if (mapItem->m_altitudeReference == 3) {
|
||||
// obj.insert("altitudeReference", "CLIP_TO_GROUND");
|
||||
//}
|
||||
|
||||
//qDebug() << obj;
|
||||
|
||||
/*if (id == "400b00")
|
||||
{
|
||||
QJsonDocument doc(obj);
|
||||
if (!m_file.isOpen())
|
||||
{
|
||||
m_file.setFileName(QString("%1.czml").arg(id));
|
||||
m_file.open(QIODeviceBase::WriteOnly);
|
||||
}
|
||||
m_file.write(doc.toJson());
|
||||
m_file.write(",\n");
|
||||
}*/
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
|
@ -24,10 +24,13 @@
|
||||
#define INCLUDE_FEATURE_CZML_H_
|
||||
|
||||
#include <QHash>
|
||||
#include <QFile>
|
||||
#include <QJsonArray>
|
||||
#include <QJsonObject>
|
||||
#include <QGeoCoordinate>
|
||||
|
||||
#include "mapaircraftstate.h"
|
||||
|
||||
struct MapSettings;
|
||||
class MapItem;
|
||||
class ObjectMapItem;
|
||||
@ -36,13 +39,59 @@ class PolylineMapItem;
|
||||
|
||||
class CZML
|
||||
{
|
||||
// Record previous state of object, so we only send changes in state
|
||||
struct State {
|
||||
QString m_firstSeenDateTime;
|
||||
|
||||
QString m_modelGLTF;
|
||||
int m_modelAltitudeReference;
|
||||
bool m_modelShow;
|
||||
int m_modelMinimumPixelSize;
|
||||
|
||||
quint32 m_pointColorInt;
|
||||
int m_pointAltitudeReference;
|
||||
bool m_pointShow;
|
||||
|
||||
float m_lableAltitudeOffset;
|
||||
QString m_labelText;
|
||||
bool m_labelShow;
|
||||
float m_labelScale;
|
||||
int m_labelAltitudeReference;
|
||||
|
||||
quint32 m_pathColorInt;
|
||||
bool m_pathShow;
|
||||
|
||||
QString m_description;
|
||||
|
||||
QDateTime m_aircraftStateDateTime;
|
||||
MapAircraftState m_aircraftState;
|
||||
float m_aircraftRoll;
|
||||
|
||||
State() :
|
||||
m_modelAltitudeReference(0),
|
||||
m_modelShow(false),
|
||||
m_modelMinimumPixelSize(0),
|
||||
m_pointColorInt(0),
|
||||
m_pointAltitudeReference(0),
|
||||
m_pointShow(false),
|
||||
m_lableAltitudeOffset(0.0),
|
||||
m_labelShow(false),
|
||||
m_labelScale(0.0),
|
||||
m_labelAltitudeReference(0),
|
||||
m_pathColorInt(0),
|
||||
m_pathShow(false)
|
||||
{ }
|
||||
};
|
||||
|
||||
private:
|
||||
const MapSettings *m_settings;
|
||||
QHash<QString, QString> m_ids;
|
||||
QHash<QString, State> m_ids;
|
||||
QHash<QString, QJsonArray> m_lastPosition;
|
||||
QHash<QString, bool> m_hasMoved;
|
||||
QGeoCoordinate m_position;
|
||||
static const QStringList m_heightReferences;
|
||||
QFile m_file;
|
||||
QFile m_csvFile;
|
||||
|
||||
public:
|
||||
CZML(const MapSettings *settings);
|
||||
|
BIN
plugins/feature/map/icons/firstperson.png
Normal file
BIN
plugins/feature/map/icons/firstperson.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 342 B |
BIN
plugins/feature/map/icons/pfd.png
Normal file
BIN
plugins/feature/map/icons/pfd.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 377 B |
BIN
plugins/feature/map/icons/thirdperson.png
Normal file
BIN
plugins/feature/map/icons/thirdperson.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 303 B |
1463
plugins/feature/map/map/cockpit.js
Normal file
1463
plugins/feature/map/map/cockpit.js
Normal file
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
61
plugins/feature/map/mapaircraftstate.h
Normal file
61
plugins/feature/map/mapaircraftstate.h
Normal file
@ -0,0 +1,61 @@
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (C) 2025 Jon Beniston, M7RCE <jon@beniston.com> //
|
||||
// //
|
||||
// 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_MAPAIRCRAFTSTATE_H_
|
||||
#define INCLUDE_FEATURE_MAPAIRCRAFTSTATE_H_
|
||||
|
||||
struct MapAircraftState {
|
||||
QString m_callsign;
|
||||
QString m_aircraftType;
|
||||
int m_onSurface; // -1 for unknown
|
||||
float m_indicatedAirspeed; // NaN for unknown
|
||||
QString m_indicatedAirspeedDateTime;
|
||||
float m_trueAirspeed;
|
||||
float m_groundspeed;
|
||||
float m_mach;
|
||||
float m_altitude;
|
||||
QString m_altitudeDateTime;
|
||||
float m_qnh;
|
||||
float m_verticalSpeed;
|
||||
float m_heading;
|
||||
float m_track;
|
||||
float m_selectedAltitude;
|
||||
float m_selectedHeading;
|
||||
int m_autopilot; // -1 for unknown
|
||||
enum VerticalMode {
|
||||
UNKNOWN_VERTICAL_MODE,
|
||||
VNAV,
|
||||
ALT_HOLD,
|
||||
GS
|
||||
} m_verticalMode;
|
||||
enum LateralMode {
|
||||
UNKNOWN_LATERAL_MODE,
|
||||
LNAV,
|
||||
LOC
|
||||
} m_lateralMode;
|
||||
enum TCASMode {
|
||||
UNKNOWN_TCAS_MODE,
|
||||
TCAS_OFF,
|
||||
TA,
|
||||
TA_RA
|
||||
} m_tcasMode;
|
||||
float m_windSpeed;
|
||||
float m_windDirection;
|
||||
float m_staticAirTemperature;
|
||||
};
|
||||
|
||||
#endif // INCLUDE_FEATURE_MAPAIRCRAFTSTATE_H_
|
@ -1929,7 +1929,6 @@ void MapGUI::applyMap3DSettings(bool reloadMap)
|
||||
ui->web->load(QUrl(QString("http://127.0.0.1:%1/map/map/map3d.html").arg(m_webPort)));
|
||||
//ui->web->load(QUrl(QString("http://webglreport.com/")));
|
||||
//ui->web->load(QUrl(QString("https://sandcastle.cesium.com/")));
|
||||
//ui->web->load(QUrl("chrome://gpu/"));
|
||||
ui->web->show();
|
||||
}
|
||||
else if (!m_settings.m_map3DEnabled && (m_cesium != nullptr))
|
||||
@ -1948,9 +1947,11 @@ void MapGUI::applyMap3DSettings(bool reloadMap)
|
||||
m_cesium->setCameraReferenceFrame(m_settings.m_eciCamera);
|
||||
m_cesium->setAntiAliasing(m_settings.m_fxaa, m_settings.m_msaa);
|
||||
m_cesium->getDateTime();
|
||||
m_cesium->setViewFirstPerson(m_settings.m_viewFirstPerson);
|
||||
m_cesium->setHDR(m_settings.m_hdr);
|
||||
m_cesium->setFog(m_settings.m_fog);
|
||||
m_cesium->showFPS(m_settings.m_fps);
|
||||
m_cesium->showPFD(m_settings.m_displayPFD);
|
||||
m_cesium->showMUF(m_settings.m_displayMUF);
|
||||
m_cesium->showfoF2(m_settings.m_displayfoF2);
|
||||
m_cesium->showMagDec(m_settings.m_displayMagDec);
|
||||
@ -1968,9 +1969,12 @@ void MapGUI::applyMap3DSettings(bool reloadMap)
|
||||
m_polylineMapModel.allUpdated();
|
||||
}
|
||||
MapSettings::MapItemSettings *ionosondeItemSettings = getItemSettings("Ionosonde Stations");
|
||||
m_giro->getIndexPeriodically((m_settings.m_displayMUF || m_settings.m_displayfoF2) ? 15 : 0);
|
||||
if (ionosondeItemSettings) {
|
||||
m_giro->getDataPeriodically(ionosondeItemSettings->m_enabled ? 2 : 0);
|
||||
if (m_giro)
|
||||
{
|
||||
m_giro->getIndexPeriodically((m_settings.m_displayMUF || m_settings.m_displayfoF2) ? 15 : 0);
|
||||
if (ionosondeItemSettings) {
|
||||
m_giro->getDataPeriodically(ionosondeItemSettings->m_enabled ? 2 : 0);
|
||||
}
|
||||
}
|
||||
if (m_aurora) {
|
||||
m_aurora->getDataPeriodically(m_settings.m_displayAurora ? 30 : 0);
|
||||
@ -2084,6 +2088,8 @@ void MapGUI::displaySettings()
|
||||
setTitle(m_settings.m_title);
|
||||
blockApplySettings(true);
|
||||
ui->displayNames->setChecked(m_settings.m_displayNames);
|
||||
ui->viewFirstPerson->setChecked(m_settings.m_viewFirstPerson);
|
||||
ui->displayPFD->setChecked(m_settings.m_displayPFD);
|
||||
ui->displaySelectedGroundTracks->setChecked(m_settings.m_displaySelectedGroundTracks);
|
||||
ui->displayAllGroundTracks->setChecked(m_settings.m_displayAllGroundTracks);
|
||||
ui->displayRain->setChecked(m_settings.m_displayRain);
|
||||
@ -2202,6 +2208,24 @@ void MapGUI::on_maidenhead_clicked()
|
||||
dialog.exec();
|
||||
}
|
||||
|
||||
void MapGUI::on_viewFirstPerson_clicked(bool checked)
|
||||
{
|
||||
m_settings.m_viewFirstPerson = checked;
|
||||
if (m_cesium) {
|
||||
m_cesium->setViewFirstPerson(checked);
|
||||
}
|
||||
applySetting("viewFirstPerson");
|
||||
}
|
||||
|
||||
void MapGUI::on_displayPFD_clicked(bool checked)
|
||||
{
|
||||
m_settings.m_displayPFD = checked;
|
||||
if (m_cesium) {
|
||||
m_cesium->showPFD(checked);
|
||||
}
|
||||
applySetting("viewFirstPerson");
|
||||
}
|
||||
|
||||
void MapGUI::on_displayNames_clicked(bool checked)
|
||||
{
|
||||
m_settings.m_displayNames = checked;
|
||||
@ -2415,10 +2439,10 @@ void MapGUI::on_displayMUF_clicked(bool checked)
|
||||
m_displayMUF->setChecked(checked);
|
||||
}
|
||||
m_settings.m_displayMUF = checked;
|
||||
// Only call show if disabling, so we don't get two updates
|
||||
// (as getMUFPeriodically results in a call to showMUF when the data is available)
|
||||
m_giro->getIndexPeriodically((m_settings.m_displayMUF || m_settings.m_displayfoF2) ? 15 : 0);
|
||||
if (m_cesium && !m_settings.m_displayMUF) {
|
||||
if (m_giro) {
|
||||
m_giro->getIndexPeriodically((m_settings.m_displayMUF || m_settings.m_displayfoF2) ? 15 : 0);
|
||||
}
|
||||
if (m_cesium) {
|
||||
m_cesium->showMUF(m_settings.m_displayMUF);
|
||||
}
|
||||
applySetting("displayMUF");
|
||||
@ -2433,8 +2457,10 @@ void MapGUI::on_displayfoF2_clicked(bool checked)
|
||||
m_displayfoF2->setChecked(checked);
|
||||
}
|
||||
m_settings.m_displayfoF2 = checked;
|
||||
m_giro->getIndexPeriodically((m_settings.m_displayMUF || m_settings.m_displayfoF2) ? 15 : 0);
|
||||
if (m_cesium && !m_settings.m_displayfoF2) {
|
||||
if (m_giro) {
|
||||
m_giro->getIndexPeriodically((m_settings.m_displayMUF || m_settings.m_displayfoF2) ? 15 : 0);
|
||||
}
|
||||
if (m_cesium) {
|
||||
m_cesium->showfoF2(m_settings.m_displayfoF2);
|
||||
}
|
||||
applySetting("displayfoF2");
|
||||
@ -3009,6 +3035,8 @@ void MapGUI::preferenceChanged(int elementType)
|
||||
void MapGUI::makeUIConnections()
|
||||
{
|
||||
QObject::connect(ui->displayNames, &ButtonSwitch::clicked, this, &MapGUI::on_displayNames_clicked);
|
||||
QObject::connect(ui->viewFirstPerson, &ButtonSwitch::clicked, this, &MapGUI::on_viewFirstPerson_clicked);
|
||||
QObject::connect(ui->displayPFD, &ButtonSwitch::clicked, this, &MapGUI::on_displayPFD_clicked);
|
||||
QObject::connect(ui->displayAllGroundTracks, &ButtonSwitch::clicked, this, &MapGUI::on_displayAllGroundTracks_clicked);
|
||||
QObject::connect(ui->displaySelectedGroundTracks, &ButtonSwitch::clicked, this, &MapGUI::on_displaySelectedGroundTracks_clicked);
|
||||
QObject::connect(ui->displayRain, &ButtonSwitch::clicked, this, &MapGUI::on_displayRain_clicked);
|
||||
|
@ -326,6 +326,8 @@ private slots:
|
||||
void onMenuDialogCalled(const QPoint &p);
|
||||
void onWidgetRolled(QWidget* widget, bool rollDown);
|
||||
void handleInputMessages();
|
||||
void on_viewFirstPerson_clicked(bool checked=false);
|
||||
void on_displayPFD_clicked(bool checked=false);
|
||||
void on_displayNames_clicked(bool checked=false);
|
||||
void on_displayAllGroundTracks_clicked(bool checked=false);
|
||||
void on_displaySelectedGroundTracks_clicked(bool checked=false);
|
||||
|
@ -29,7 +29,7 @@
|
||||
</font>
|
||||
</property>
|
||||
<property name="focusPolicy">
|
||||
<enum>Qt::StrongFocus</enum>
|
||||
<enum>Qt::FocusPolicy::StrongFocus</enum>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Map</string>
|
||||
@ -39,7 +39,7 @@
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>1221</width>
|
||||
<width>1251</width>
|
||||
<height>41</height>
|
||||
</rect>
|
||||
</property>
|
||||
@ -166,7 +166,7 @@
|
||||
<string>IBP</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="icons.qrc">
|
||||
<iconset resource="mapicons.qrc">
|
||||
<normaloff>:/map/icons/ibp.png</normaloff>:/map/icons/ibp.png</iconset>
|
||||
</property>
|
||||
</widget>
|
||||
@ -180,7 +180,7 @@
|
||||
<string/>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="icons.qrc">
|
||||
<iconset resource="mapicons.qrc">
|
||||
<normaloff>:/map/icons/clock.png</normaloff>:/map/icons/clock.png</iconset>
|
||||
</property>
|
||||
</widget>
|
||||
@ -191,11 +191,11 @@
|
||||
<string/>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="icons.qrc">
|
||||
<iconset resource="mapicons.qrc">
|
||||
<normaloff>:/map/icons/layers.png</normaloff>:/map/icons/layers.png</iconset>
|
||||
</property>
|
||||
<property name="popupMode">
|
||||
<enum>QToolButton::InstantPopup</enum>
|
||||
<enum>QToolButton::ToolButtonPopupMode::InstantPopup</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
@ -205,10 +205,10 @@
|
||||
<string>Display satellite infra-red (clouds)</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>^</string>
|
||||
<string/>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="icons.qrc">
|
||||
<iconset resource="mapicons.qrc">
|
||||
<normaloff>:/map/icons/cloud.png</normaloff>:/map/icons/cloud.png</iconset>
|
||||
</property>
|
||||
<property name="checkable">
|
||||
@ -225,10 +225,10 @@
|
||||
<string>Display weather radar (rain/snow)</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>^</string>
|
||||
<string/>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="icons.qrc">
|
||||
<iconset resource="mapicons.qrc">
|
||||
<normaloff>:/map/icons/precipitation.png</normaloff>:/map/icons/precipitation.png</iconset>
|
||||
</property>
|
||||
<property name="checkable">
|
||||
@ -245,10 +245,10 @@
|
||||
<string>Display sea marks</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>^</string>
|
||||
<string/>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="icons.qrc">
|
||||
<iconset resource="mapicons.qrc">
|
||||
<normaloff>:/map/icons/anchor.png</normaloff>:/map/icons/anchor.png</iconset>
|
||||
</property>
|
||||
<property name="checkable">
|
||||
@ -265,10 +265,10 @@
|
||||
<string>Display railways</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>^</string>
|
||||
<string/>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="icons.qrc">
|
||||
<iconset resource="mapicons.qrc">
|
||||
<normaloff>:/map/icons/railway.png</normaloff>:/map/icons/railway.png</iconset>
|
||||
</property>
|
||||
<property name="checkable">
|
||||
@ -285,10 +285,10 @@
|
||||
<string>Display MUF (Maximum Usable Frequency) contours (3D only)</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>^</string>
|
||||
<string/>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="icons.qrc">
|
||||
<iconset resource="mapicons.qrc">
|
||||
<normaloff>:/map/icons/muf.png</normaloff>:/map/icons/muf.png</iconset>
|
||||
</property>
|
||||
<property name="checkable">
|
||||
@ -305,10 +305,10 @@
|
||||
<string>Display foF2 (F2 layer critical frequency) contours (3D only)</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>^</string>
|
||||
<string/>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="icons.qrc">
|
||||
<iconset resource="mapicons.qrc">
|
||||
<normaloff>:/map/icons/fof2.png</normaloff>:/map/icons/fof2.png</iconset>
|
||||
</property>
|
||||
<property name="checkable">
|
||||
@ -365,10 +365,10 @@
|
||||
<string>Display NASA GIBS data</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>^</string>
|
||||
<string/>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="icons.qrc">
|
||||
<iconset resource="mapicons.qrc">
|
||||
<normaloff>:/map/icons/earthsat.png</normaloff>:/map/icons/earthsat.png</iconset>
|
||||
</property>
|
||||
<property name="checkable">
|
||||
@ -430,7 +430,7 @@
|
||||
<string>Display names</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>^</string>
|
||||
<string/>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="../../../sdrgui/resources/res.qrc">
|
||||
@ -444,13 +444,54 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="ButtonSwitch" name="viewFirstPerson">
|
||||
<property name="toolTip">
|
||||
<string>First person / third person view on 3D map</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="mapicons.qrc">
|
||||
<normaloff>:/map/icons/thirdperson.png</normaloff>
|
||||
<normalon>:/map/icons/firstperson.png</normalon>:/map/icons/thirdperson.png</iconset>
|
||||
</property>
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="ButtonSwitch" name="displayPFD">
|
||||
<property name="toolTip">
|
||||
<string>Display aircraft PFD (Primary Flight Display) on 3D map</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="mapicons.qrc">
|
||||
<normaloff>:/map/icons/pfd.png</normaloff>:/map/icons/pfd.png</iconset>
|
||||
</property>
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="ButtonSwitch" name="displaySelectedGroundTracks">
|
||||
<property name="toolTip">
|
||||
<string>Display ground tracks for selected item</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>^</string>
|
||||
<string/>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="../../../sdrgui/resources/res.qrc">
|
||||
@ -470,10 +511,10 @@
|
||||
<string>Display all ground tracks</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>^</string>
|
||||
<string/>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="icons.qrc">
|
||||
<iconset resource="mapicons.qrc">
|
||||
<normaloff>:/map/icons/groundtracks.png</normaloff>:/map/icons/groundtracks.png</iconset>
|
||||
</property>
|
||||
<property name="checkable">
|
||||
@ -573,7 +614,7 @@
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
<enum>Qt::Orientation::Vertical</enum>
|
||||
</property>
|
||||
<widget class="QQuickWidget" name="map">
|
||||
<property name="sizePolicy">
|
||||
@ -592,7 +633,7 @@
|
||||
<string>Map</string>
|
||||
</property>
|
||||
<property name="resizeMode">
|
||||
<enum>QQuickWidget::SizeRootObjectToView</enum>
|
||||
<enum>QQuickWidget::ResizeMode::SizeRootObjectToView</enum>
|
||||
</property>
|
||||
<property name="source">
|
||||
<url>
|
||||
@ -655,7 +696,7 @@
|
||||
</tabstops>
|
||||
<resources>
|
||||
<include location="../../../sdrgui/resources/res.qrc"/>
|
||||
<include location="icons.qrc"/>
|
||||
<include location="mapicons.qrc"/>
|
||||
</resources>
|
||||
<connections/>
|
||||
</ui>
|
||||
|
@ -15,6 +15,7 @@
|
||||
<file>icons/waypoints.png</file>
|
||||
<file>icons/earthsat.png</file>
|
||||
<file>icons/aurora.png</file>
|
||||
<file>icons/pfd.png</file>
|
||||
<file>icons/compass.png</file>
|
||||
<file>icons/grid.png</file>
|
||||
<file>icons/thirdperson.png</file>
|
||||
|
@ -34,6 +34,11 @@ void MapItem::update(SWGSDRangel::SWGMapItem *mapItem)
|
||||
} else {
|
||||
m_label = "";
|
||||
}
|
||||
if (mapItem->getLabelDateTime()) {
|
||||
m_labelDateTime = QDateTime::fromString(*mapItem->getLabelDateTime(), Qt::ISODateWithMs);
|
||||
} else {
|
||||
m_labelDateTime = QDateTime();
|
||||
}
|
||||
m_latitude = mapItem->getLatitude();
|
||||
m_longitude = mapItem->getLongitude();
|
||||
m_altitude = mapItem->getAltitude();
|
||||
@ -47,6 +52,8 @@ QGeoCoordinate MapItem::getCoordinates()
|
||||
return coords;
|
||||
}
|
||||
|
||||
WhittakerEilers ObjectMapItem::m_filter;
|
||||
|
||||
void ObjectMapItem::update(SWGSDRangel::SWGMapItem *mapItem)
|
||||
{
|
||||
MapItem::update(mapItem);
|
||||
@ -55,6 +62,11 @@ void ObjectMapItem::update(SWGSDRangel::SWGMapItem *mapItem)
|
||||
} else {
|
||||
m_positionDateTime = QDateTime();
|
||||
}
|
||||
if (mapItem->getAltitudeDateTime()) {
|
||||
m_altitudeDateTime = QDateTime::fromString(*mapItem->getAltitudeDateTime(), Qt::ISODateWithMs);
|
||||
} else {
|
||||
m_altitudeDateTime = QDateTime();
|
||||
}
|
||||
m_useHeadingPitchRoll = mapItem->getOrientation() == 1;
|
||||
m_heading = mapItem->getHeading();
|
||||
m_pitch = mapItem->getPitch();
|
||||
@ -79,6 +91,14 @@ void ObjectMapItem::update(SWGSDRangel::SWGMapItem *mapItem)
|
||||
}
|
||||
m_labelAltitudeOffset = mapItem->getLabelAltitudeOffset();
|
||||
m_modelAltitudeOffset = mapItem->getModelAltitudeOffset();
|
||||
// FIXME: See nodeTransformations comment in czml.cpp
|
||||
// We can't use nodeTransformations, so adjust altitude instead
|
||||
if (m_modelAltitudeOffset != 0)
|
||||
{
|
||||
m_labelAltitudeOffset -= m_modelAltitudeOffset;
|
||||
m_altitude += m_modelAltitudeOffset;
|
||||
m_modelAltitudeOffset = 0;
|
||||
}
|
||||
m_altitudeReference = mapItem->getAltitudeReference();
|
||||
m_fixedPosition = mapItem->getFixedPosition();
|
||||
QList<SWGSDRangel::SWGMapAnimation *> *animations = mapItem->getAnimations();
|
||||
@ -91,7 +111,7 @@ void ObjectMapItem::update(SWGSDRangel::SWGMapItem *mapItem)
|
||||
findFrequencies();
|
||||
if (!m_fixedPosition)
|
||||
{
|
||||
updateTrack(mapItem->getTrack());
|
||||
updateTrack(mapItem->getTrack(), m_itemSettings);
|
||||
updatePredictedTrack(mapItem->getPredictedTrack());
|
||||
}
|
||||
if (mapItem->getAvailableFrom()) {
|
||||
@ -104,6 +124,47 @@ void ObjectMapItem::update(SWGSDRangel::SWGMapItem *mapItem)
|
||||
} else {
|
||||
m_availableUntil = QDateTime();
|
||||
}
|
||||
if (mapItem->getAircraftState()) {
|
||||
if (!m_aircraftState) {
|
||||
m_aircraftState = new MapAircraftState();
|
||||
}
|
||||
SWGSDRangel::SWGMapAircraftState *as = mapItem->getAircraftState();
|
||||
if (as->getCallsign()) {
|
||||
m_aircraftState->m_callsign = *as->getCallsign();
|
||||
}
|
||||
if (as->getAircraftType()) {
|
||||
m_aircraftState->m_aircraftType = *as->getAircraftType();
|
||||
}
|
||||
m_aircraftState->m_onSurface = as->getOnSurface();
|
||||
m_aircraftState->m_indicatedAirspeed = as->getAirspeed();
|
||||
if (as->getAirspeedDateTime()) {
|
||||
m_aircraftState->m_indicatedAirspeedDateTime = *as->getAirspeedDateTime();
|
||||
} else {
|
||||
m_aircraftState->m_indicatedAirspeedDateTime = QString();
|
||||
}
|
||||
m_aircraftState->m_trueAirspeed = as->getTrueAirspeed();
|
||||
m_aircraftState->m_groundspeed = as->getGroundspeed();
|
||||
m_aircraftState->m_mach = as->getMach();
|
||||
m_aircraftState->m_altitude = as->getAltitude();
|
||||
if (as->getAltitudeDateTime()) {
|
||||
m_aircraftState->m_altitudeDateTime = *as->getAltitudeDateTime();
|
||||
} else {
|
||||
m_aircraftState->m_altitudeDateTime = QString();
|
||||
}
|
||||
m_aircraftState->m_qnh = as->getQnh();
|
||||
m_aircraftState->m_verticalSpeed = as->getVerticalSpeed();
|
||||
m_aircraftState->m_heading = as->getHeading();
|
||||
m_aircraftState->m_track = as->getTrack();
|
||||
m_aircraftState->m_selectedAltitude = as->getSelectedAltitude();
|
||||
m_aircraftState->m_selectedHeading = as->getSelectedHeading();
|
||||
m_aircraftState->m_autopilot = as->getAutopilot();
|
||||
m_aircraftState->m_verticalMode = (MapAircraftState::VerticalMode) as->getVerticalMode();
|
||||
m_aircraftState->m_lateralMode = (MapAircraftState::LateralMode) as->getLateralMode();
|
||||
m_aircraftState->m_tcasMode = (MapAircraftState::TCASMode) as->getTcasMode();
|
||||
m_aircraftState->m_windSpeed = as->getWindSpeed();
|
||||
m_aircraftState->m_windDirection = as->getWindDirection();
|
||||
m_aircraftState->m_staticAirTemperature = as->getStaticAirTemperature();
|
||||
}
|
||||
}
|
||||
|
||||
void ImageMapItem::update(SWGSDRangel::SWGMapItem *mapItem)
|
||||
@ -225,7 +286,145 @@ void ObjectMapItem::findFrequencies()
|
||||
}
|
||||
}
|
||||
|
||||
void ObjectMapItem::updateTrack(QList<SWGSDRangel::SWGMapCoordinate *> *track)
|
||||
void ObjectMapItem::extrapolatePosition(QGeoCoordinate *c, const QDateTime& dateTime)
|
||||
{
|
||||
int p1;
|
||||
int p2;
|
||||
|
||||
// Find last two non extrapolated position
|
||||
for (p2 = m_takenTrackPositionExtrapolated.size() - 1 ; p2 >= 0; p2--)
|
||||
{
|
||||
if (!m_takenTrackPositionExtrapolated[p2]) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
for (p1 = p2 - 1 ; p1 >= 0; p1--)
|
||||
{
|
||||
if (!m_takenTrackPositionExtrapolated[p1]) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (p1 < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
qint64 t1 = m_takenTrackDateTimes[p1]->msecsTo(*m_takenTrackDateTimes[p2]);
|
||||
qint64 t2 = m_takenTrackDateTimes[p2]->msecsTo(dateTime);
|
||||
|
||||
double latV = (m_takenTrackCoords[p2]->latitude() - m_takenTrackCoords[p1]->latitude()) / t1;
|
||||
double lonV = (m_takenTrackCoords[p2]->longitude() - m_takenTrackCoords[p1]->longitude()) / t1;
|
||||
|
||||
double newLat = m_takenTrackCoords[p2]->latitude() + latV * t2;
|
||||
double newLon = m_takenTrackCoords[p2]->longitude() + lonV * t2;
|
||||
|
||||
c->setLatitude(newLat);
|
||||
c->setLongitude(newLon);
|
||||
}
|
||||
|
||||
void ObjectMapItem::extrapolateAltitude(QGeoCoordinate *c, const QDateTime& dateTime)
|
||||
{
|
||||
int p1;
|
||||
int p2;
|
||||
|
||||
// Find last two non extrapolated position
|
||||
for (p2 = m_takenTrackPositionExtrapolated.size() - 1 ; p2 >= 0; p2--)
|
||||
{
|
||||
if (!m_takenTrackPositionExtrapolated[p2]) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
for (p1 = p2 - 1 ; p1 >= 0; p1--)
|
||||
{
|
||||
if (!m_takenTrackPositionExtrapolated[p1]) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (p1 < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
qint64 t1 = m_takenTrackDateTimes[p1]->msecsTo(*m_takenTrackDateTimes[p2]);
|
||||
qint64 t2 = m_takenTrackDateTimes[p2]->msecsTo(dateTime);
|
||||
|
||||
double vertV = (m_takenTrackCoords[p2]->altitude() - m_takenTrackCoords[p1]->altitude()) / t1;
|
||||
|
||||
double newAlt = m_takenTrackCoords[p2]->latitude() + vertV * t2;
|
||||
|
||||
c->setAltitude(newAlt);
|
||||
}
|
||||
|
||||
void ObjectMapItem::interpolatePosition(int p2, const float p3Latitude, const float p3Longitude, const QDateTime &p3DateTime)
|
||||
{
|
||||
// p1 last non extrapolated position
|
||||
// p2 interpolated position
|
||||
// p3 current non extrapolated position
|
||||
|
||||
// Find last non extrapolated position
|
||||
int p1;
|
||||
|
||||
for (p1 = p2 - 1; p1 >= 0; p1--)
|
||||
{
|
||||
if (!m_takenTrackPositionExtrapolated[p1]) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (p1 < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
qint64 t1 = m_takenTrackDateTimes[p1]->msecsTo(p3DateTime);
|
||||
qint64 t2 = m_takenTrackDateTimes[p1]->msecsTo(*m_takenTrackDateTimes[p2]);
|
||||
|
||||
double latV = (p3Latitude - m_takenTrackCoords[p1]->latitude()) / t1;
|
||||
double lonV = (p3Longitude - m_takenTrackCoords[p1]->longitude()) / t1;
|
||||
|
||||
double newLat = m_takenTrackCoords[p1]->latitude() + latV * t2;
|
||||
double newLon = m_takenTrackCoords[p1]->longitude() + lonV * t2;
|
||||
|
||||
m_takenTrackCoords[p2]->setLatitude(newLat);
|
||||
m_takenTrackCoords[p2]->setLongitude(newLon);
|
||||
|
||||
m_interpolatedCoords.append(m_takenTrackCoords[p2]);
|
||||
m_interpolatedDateTimes.append(m_takenTrackDateTimes[p2]);
|
||||
}
|
||||
|
||||
void ObjectMapItem::interpolateAltitude(int p2, const float p3Altitude, const QDateTime &p3DateTime)
|
||||
{
|
||||
// p1 last non extrapolated position
|
||||
// p2 interpolated position
|
||||
// p3 current non extrapolated position
|
||||
|
||||
// Find last non extrapolated position
|
||||
int p1;
|
||||
|
||||
for (p1 = p2 - 1; p1 >= 0; p1--)
|
||||
{
|
||||
if (!m_takenTrackPositionExtrapolated[p1]) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (p1 < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
qint64 t1 = m_takenTrackDateTimes[p1]->msecsTo(p3DateTime);
|
||||
qint64 t2 = m_takenTrackDateTimes[p1]->msecsTo(*m_takenTrackDateTimes[p2]);
|
||||
|
||||
double vertV = (p3Altitude - m_takenTrackCoords[p1]->altitude()) / t1;
|
||||
|
||||
double newAlt = m_takenTrackCoords[p1]->altitude() + vertV * t2;
|
||||
|
||||
m_takenTrackCoords[p2]->setAltitude(newAlt);
|
||||
|
||||
m_interpolatedCoords.append(m_takenTrackCoords[p2]);
|
||||
m_interpolatedDateTimes.append(m_takenTrackDateTimes[p2]);
|
||||
}
|
||||
|
||||
void ObjectMapItem::updateTrack(QList<SWGSDRangel::SWGMapCoordinate *> *track, MapSettings::MapItemSettings *itemSettings)
|
||||
{
|
||||
if (track != nullptr)
|
||||
{
|
||||
@ -233,6 +432,8 @@ void ObjectMapItem::updateTrack(QList<SWGSDRangel::SWGMapCoordinate *> *track)
|
||||
m_takenTrackCoords.clear();
|
||||
qDeleteAll(m_takenTrackDateTimes);
|
||||
m_takenTrackDateTimes.clear();
|
||||
m_takenTrackPositionExtrapolated.clear();
|
||||
m_takenTrackAltitudeExtrapolated.clear();
|
||||
m_takenTrack.clear();
|
||||
m_takenTrack1.clear();
|
||||
m_takenTrack2.clear();
|
||||
@ -243,6 +444,8 @@ void ObjectMapItem::updateTrack(QList<SWGSDRangel::SWGMapCoordinate *> *track)
|
||||
QDateTime *d = new QDateTime(QDateTime::fromString(*p->getDateTime(), Qt::ISODate));
|
||||
m_takenTrackCoords.push_back(c);
|
||||
m_takenTrackDateTimes.push_back(d);
|
||||
m_takenTrackPositionExtrapolated.push_back(false);
|
||||
m_takenTrackAltitudeExtrapolated.push_back(false);
|
||||
m_takenTrack.push_back(QVariant::fromValue(*c));
|
||||
}
|
||||
}
|
||||
@ -253,28 +456,188 @@ void ObjectMapItem::updateTrack(QList<SWGSDRangel::SWGMapCoordinate *> *track)
|
||||
{
|
||||
QGeoCoordinate *c = new QGeoCoordinate(m_latitude, m_longitude, m_altitude);
|
||||
m_takenTrackCoords.push_back(c);
|
||||
if (m_positionDateTime.isValid()) {
|
||||
if (m_altitudeDateTime.isValid()) {
|
||||
m_takenTrackDateTimes.push_back(new QDateTime(m_altitudeDateTime));
|
||||
} else if (m_positionDateTime.isValid()) {
|
||||
m_takenTrackDateTimes.push_back(new QDateTime(m_positionDateTime));
|
||||
} else {
|
||||
m_takenTrackDateTimes.push_back(new QDateTime(QDateTime::currentDateTime()));
|
||||
}
|
||||
m_takenTrackPositionExtrapolated.push_back(false);
|
||||
m_takenTrackAltitudeExtrapolated.push_back(false);
|
||||
m_takenTrack.push_back(QVariant::fromValue(*c));
|
||||
}
|
||||
else
|
||||
{
|
||||
QGeoCoordinate *prev = m_takenTrackCoords.last();
|
||||
// For Whittaker-Eilers filtering, we need to make sure we don't have 2 data with the same time
|
||||
// so we just update the last item if the prev time is the same
|
||||
// To reduce size of list for stationary items, we only store two items with same position
|
||||
// We store two, rather than one, so that we have the times this position was arrived at and left
|
||||
|
||||
const bool interpolate = false;
|
||||
|
||||
QGeoCoordinate *prev1 = m_takenTrackCoords.last();
|
||||
bool samePos1 = (prev1->latitude() == m_latitude) && (prev1->longitude() == m_longitude) && (prev1->altitude() == m_altitude);
|
||||
QGeoCoordinate *prev2 = m_takenTrackCoords.size() > 1 ? m_takenTrackCoords[m_takenTrackCoords.size() - 2] : nullptr;
|
||||
bool samePos2 = prev2 && samePos1 ? (prev2->latitude() == m_latitude) && (prev2->longitude() == m_longitude) && (prev2->altitude() == m_altitude) : false;
|
||||
|
||||
QDateTime *prevDateTime = m_takenTrackDateTimes.last();
|
||||
if ((prev->latitude() != m_latitude) || (prev->longitude() != m_longitude)
|
||||
|| (prev->altitude() != m_altitude) || (*prevDateTime != m_positionDateTime))
|
||||
|
||||
QGeoCoordinate c(m_latitude, m_longitude, m_altitude);
|
||||
int prevSize = m_takenTrackPositionExtrapolated.size();
|
||||
|
||||
if (m_altitudeDateTime.isValid() && m_positionDateTime.isValid() && (m_altitudeDateTime > m_positionDateTime))
|
||||
{
|
||||
QGeoCoordinate *c = new QGeoCoordinate(m_latitude, m_longitude, m_altitude);
|
||||
m_takenTrackCoords.push_back(c);
|
||||
if (m_positionDateTime.isValid()) {
|
||||
m_takenTrackDateTimes.push_back(new QDateTime(m_positionDateTime));
|
||||
} else {
|
||||
m_takenTrackDateTimes.push_back(new QDateTime(QDateTime::currentDateTime()));
|
||||
if (interpolate)
|
||||
{
|
||||
for (int i = m_takenTrackAltitudeExtrapolated.size() - 1; (i >= 0) && m_takenTrackAltitudeExtrapolated[i]; i--)
|
||||
{
|
||||
interpolateAltitude(i, m_altitude, m_altitudeDateTime);
|
||||
m_takenTrackAltitudeExtrapolated[i] = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (samePos2)
|
||||
{
|
||||
*m_takenTrackDateTimes.last() = m_altitudeDateTime;
|
||||
}
|
||||
else
|
||||
{
|
||||
extrapolatePosition(&c, m_altitudeDateTime);
|
||||
if (m_altitudeDateTime == *prevDateTime)
|
||||
{
|
||||
m_takenTrackPositionExtrapolated[m_takenTrackPositionExtrapolated.size() - 1] = true;
|
||||
*m_takenTrackCoords.last() = c;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_takenTrackDateTimes.push_back(new QDateTime(m_altitudeDateTime));
|
||||
m_takenTrackPositionExtrapolated.push_back(true);
|
||||
m_takenTrackAltitudeExtrapolated.push_back(false);
|
||||
m_takenTrackCoords.push_back(new QGeoCoordinate(c));
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (m_positionDateTime.isValid())
|
||||
{
|
||||
if (interpolate)
|
||||
{
|
||||
for (int i = m_takenTrackPositionExtrapolated.size() - 1; (i >= 0) && m_takenTrackPositionExtrapolated[i]; i--)
|
||||
{
|
||||
interpolatePosition(i, m_latitude, m_longitude, m_positionDateTime);
|
||||
m_takenTrackPositionExtrapolated[i] = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (m_positionDateTime > *m_takenTrackDateTimes.last())
|
||||
{
|
||||
if (samePos2)
|
||||
{
|
||||
*m_takenTrackDateTimes.last() = m_positionDateTime;
|
||||
}
|
||||
else
|
||||
{
|
||||
bool extrapolateAlt = m_altitudeDateTime.isValid() && (m_positionDateTime > m_altitudeDateTime);
|
||||
if (extrapolateAlt) {
|
||||
extrapolateAltitude(&c, m_positionDateTime);
|
||||
}
|
||||
if (m_positionDateTime == *prevDateTime)
|
||||
{
|
||||
m_takenTrackAltitudeExtrapolated[m_takenTrackPositionExtrapolated.size() - 1] = extrapolateAlt;
|
||||
*m_takenTrackCoords.last() = c;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_takenTrackDateTimes.push_back(new QDateTime(m_positionDateTime));
|
||||
m_takenTrackPositionExtrapolated.push_back(false);
|
||||
m_takenTrackAltitudeExtrapolated.push_back(extrapolateAlt);
|
||||
m_takenTrackCoords.push_back(new QGeoCoordinate(c));
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
//qDebug() << "m_positionDateTime matches last datetime" << samePos1 << samePos2;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
m_takenTrackDateTimes.push_back(new QDateTime(QDateTime::currentDateTime()));
|
||||
m_takenTrackPositionExtrapolated.push_back(false);
|
||||
m_takenTrackAltitudeExtrapolated.push_back(false);
|
||||
//m_takenTrackCoords.push_back(c);
|
||||
m_takenTrackCoords.push_back(new QGeoCoordinate(c));
|
||||
}
|
||||
//m_takenTrack.push_back(QVariant::fromValue(*c));
|
||||
m_takenTrack.push_back(QVariant::fromValue(c));
|
||||
|
||||
if (m_takenTrackDateTimes.size() >= 2) {
|
||||
if (*m_takenTrackDateTimes[m_takenTrackDateTimes.size() - 1] < *m_takenTrackDateTimes[m_takenTrackDateTimes.size() - 2]) {
|
||||
qDebug() << "Out of order";
|
||||
}
|
||||
}
|
||||
|
||||
if ((m_takenTrackPositionExtrapolated.size() > 0) && (prevSize != m_takenTrackPositionExtrapolated.size()))
|
||||
{
|
||||
const int filterLen = itemSettings->m_smoothingWindow;
|
||||
if ((filterLen > 0) && (m_takenTrackCoords.size() >= filterLen) && (m_takenTrackCoords.size() % (filterLen/2)) == 0)
|
||||
{
|
||||
// Filter last filterLen coords
|
||||
QVector<double> x(filterLen);
|
||||
QVector<double> y1(filterLen);
|
||||
QVector<double> y2(filterLen);
|
||||
QVector<double> y3(filterLen);
|
||||
QVector<double> w1(filterLen);
|
||||
QVector<double> w3(filterLen);
|
||||
|
||||
//qDebug() << "Filter from" << (m_takenTrackCoords.size() - (filterLen - 0)) << "to" << (m_takenTrackCoords.size() - (filterLen - (filterLen - 1)));
|
||||
for (int i = 0; i < filterLen; i++)
|
||||
{
|
||||
int idx = m_takenTrackCoords.size() - (filterLen - i);
|
||||
x[i] = (m_takenTrackDateTimes[idx]->toMSecsSinceEpoch() - m_takenTrackDateTimes[0]->toMSecsSinceEpoch()) / 1000.0;
|
||||
y1[i] = m_takenTrackCoords[idx]->latitude();
|
||||
y2[i] = m_takenTrackCoords[idx]->longitude();
|
||||
y3[i] = m_takenTrackCoords[idx]->altitude();
|
||||
if (i < (filterLen / 4))
|
||||
{
|
||||
w1[i] = 10.0; // Try to avoid discontinuities between windows
|
||||
w3[i] = 10.0;
|
||||
}
|
||||
else if (i == filterLen - 1)
|
||||
{
|
||||
w1[i] = 1.0;
|
||||
w3[i] = 1.0;
|
||||
}
|
||||
else
|
||||
{
|
||||
w1[i] = m_takenTrackPositionExtrapolated[idx] ? 0.0 : 1.0;
|
||||
w3[i] = m_takenTrackAltitudeExtrapolated[idx] ? 0.0 : 1.0;
|
||||
}
|
||||
}
|
||||
|
||||
const double lambda = itemSettings->m_smoothingLambda;
|
||||
m_filter.filter(x.data(), y1.data(), w1.data(), filterLen, lambda);
|
||||
m_filter.filter(x.data(), y2.data(), w1.data(), filterLen, lambda);
|
||||
m_filter.filter(x.data(), y3.data(), w3.data(), filterLen, lambda);
|
||||
|
||||
for (int i = 0; i < filterLen; i++)
|
||||
{
|
||||
int idx = m_takenTrackCoords.size() - (filterLen - i);
|
||||
m_takenTrackCoords[idx]->setLatitude(y1[i]);
|
||||
m_takenTrackCoords[idx]->setLongitude(y2[i]);
|
||||
m_takenTrackCoords[idx]->setAltitude(y3[i]);
|
||||
m_takenTrackPositionExtrapolated[idx] = false;
|
||||
m_takenTrackAltitudeExtrapolated[idx] = false;
|
||||
|
||||
m_interpolatedCoords.append(m_takenTrackCoords[idx]);
|
||||
m_interpolatedDateTimes.append(m_takenTrackDateTimes[idx]);
|
||||
}
|
||||
|
||||
// Update current position
|
||||
m_latitude = m_takenTrackCoords[filterLen-1]->latitude();
|
||||
m_longitude = m_takenTrackCoords[filterLen-1]->longitude();
|
||||
m_altitude = m_takenTrackCoords[filterLen-1]->altitude();
|
||||
}
|
||||
m_takenTrack.push_back(QVariant::fromValue(*c));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -25,6 +25,8 @@
|
||||
|
||||
#include "mapsettings.h"
|
||||
#include "cesiuminterface.h"
|
||||
#include "util/whittakereilers.h"
|
||||
#include "mapaircraftstate.h"
|
||||
|
||||
#include "SWGMapItem.h"
|
||||
|
||||
@ -58,6 +60,7 @@ protected:
|
||||
|
||||
QString m_name; // Unique id
|
||||
QString m_label;
|
||||
QDateTime m_labelDateTime; // Date & time from which this label is valid from (for 3D map). Invalid date/time is forever
|
||||
float m_latitude; // Position for label
|
||||
float m_longitude;
|
||||
float m_altitude; // In metres
|
||||
@ -65,13 +68,13 @@ protected:
|
||||
QDateTime m_availableUntil; // Date & time this item is visible until (for 3D map). Invalid date/time is forever
|
||||
};
|
||||
|
||||
|
||||
// Information required about each item displayed on the map
|
||||
class ObjectMapItem : public MapItem {
|
||||
|
||||
public:
|
||||
ObjectMapItem(const QObject *sourcePipe, const QString &group, MapSettings::MapItemSettings *itemSettings, SWGSDRangel::SWGMapItem *mapItem) :
|
||||
MapItem(sourcePipe, group, itemSettings, mapItem)
|
||||
MapItem(sourcePipe, group, itemSettings, mapItem),
|
||||
m_aircraftState(nullptr)
|
||||
{
|
||||
update(mapItem);
|
||||
}
|
||||
@ -79,12 +82,17 @@ public:
|
||||
|
||||
protected:
|
||||
void findFrequencies();
|
||||
void updateTrack(QList<SWGSDRangel::SWGMapCoordinate *> *track);
|
||||
void updateTrack(QList<SWGSDRangel::SWGMapCoordinate *> *track, MapSettings::MapItemSettings *itemSettings);
|
||||
void updatePredictedTrack(QList<SWGSDRangel::SWGMapCoordinate *> *track);
|
||||
void extrapolatePosition(QGeoCoordinate *c, const QDateTime& dateTime);
|
||||
void extrapolateAltitude(QGeoCoordinate *c, const QDateTime& dateTime);
|
||||
void interpolatePosition(int i, const float latitude, const float longitude, const QDateTime &dateTime);
|
||||
void interpolateAltitude(int i, const float altitude, const QDateTime &dateTime);
|
||||
|
||||
friend ObjectMapModel;
|
||||
friend CZML;
|
||||
QDateTime m_positionDateTime;
|
||||
QDateTime m_altitudeDateTime;
|
||||
bool m_useHeadingPitchRoll;
|
||||
float m_heading;
|
||||
float m_pitch;
|
||||
@ -107,6 +115,8 @@ protected:
|
||||
QGeoCoordinate m_predictedEnd2;
|
||||
QList<QGeoCoordinate *> m_takenTrackCoords;
|
||||
QList<QDateTime *> m_takenTrackDateTimes;
|
||||
QList<bool> m_takenTrackPositionExtrapolated;
|
||||
QList<bool> m_takenTrackAltitudeExtrapolated;
|
||||
QVariantList m_takenTrack; // Line showing where the object has been
|
||||
QVariantList m_takenTrack1;
|
||||
QVariantList m_takenTrack2;
|
||||
@ -114,6 +124,8 @@ protected:
|
||||
QGeoCoordinate m_takenStart2;
|
||||
QGeoCoordinate m_takenEnd1;
|
||||
QGeoCoordinate m_takenEnd2;
|
||||
QList<QGeoCoordinate *> m_interpolatedCoords;
|
||||
QList<QDateTime *> m_interpolatedDateTimes;
|
||||
|
||||
// For 3D map
|
||||
QString m_model;
|
||||
@ -121,6 +133,9 @@ protected:
|
||||
float m_labelAltitudeOffset;
|
||||
float m_modelAltitudeOffset;
|
||||
QList<CesiumInterface::Animation *> m_animations;
|
||||
MapAircraftState *m_aircraftState;
|
||||
|
||||
static WhittakerEilers m_filter; // For smoothing/interpolating position
|
||||
};
|
||||
|
||||
class PolygonMapItem : public MapItem {
|
||||
|
@ -269,7 +269,6 @@ QByteArray MapSettings::serialize() const
|
||||
s.writeBool(29, m_sunLightEnabled);
|
||||
s.writeBool(30, m_eciCamera);
|
||||
s.writeString(31, m_cesiumIonAPIKey);
|
||||
s.writeString(32, m_antiAliasing);
|
||||
s.writeS32(33, m_workspaceIndex);
|
||||
s.writeBlob(34, m_geometryBytes);
|
||||
|
||||
@ -292,6 +291,9 @@ QByteArray MapSettings::serialize() const
|
||||
s.writeBool(48, m_displayMaidenheadGrid);
|
||||
s.writeString(49, m_defaultImagery);
|
||||
s.writeString(50, m_arcGISAPIKey);
|
||||
s.writeBool(51, m_displayPFD);
|
||||
s.writeBool(52, m_viewFirstPerson);
|
||||
|
||||
s.writeBool(53, m_terrainLighting);
|
||||
s.writeBool(54, m_water);
|
||||
s.writeBool(55, m_hdr);
|
||||
@ -299,6 +301,7 @@ QByteArray MapSettings::serialize() const
|
||||
s.writeBool(57, m_fps);
|
||||
s.writeBool(58, m_fxaa);
|
||||
s.writeS32(59, m_msaa);
|
||||
|
||||
return s.final();
|
||||
}
|
||||
|
||||
@ -369,7 +372,6 @@ bool MapSettings::deserialize(const QByteArray& data)
|
||||
d.readBool(29, &m_sunLightEnabled, true);
|
||||
d.readBool(30, &m_eciCamera, false);
|
||||
d.readString(31, &m_cesiumIonAPIKey, "");
|
||||
d.readString(32, &m_antiAliasing, "None");
|
||||
d.readS32(33, &m_workspaceIndex, 0);
|
||||
d.readBlob(34, &m_geometryBytes);
|
||||
|
||||
@ -391,6 +393,9 @@ bool MapSettings::deserialize(const QByteArray& data)
|
||||
d.readBool(48, &m_displayMaidenheadGrid, false);
|
||||
d.readString(49, &m_defaultImagery, "Sentinel-2");
|
||||
d.readString(50, &m_arcGISAPIKey, "");
|
||||
d.readBool(51, &m_displayPFD, false);
|
||||
d.readBool(52, &m_viewFirstPerson, false);
|
||||
|
||||
d.readBool(53, &m_terrainLighting, true);
|
||||
d.readBool(54, &m_water, false);
|
||||
d.readBool(55, &m_hdr, true);
|
||||
@ -398,6 +403,7 @@ bool MapSettings::deserialize(const QByteArray& data)
|
||||
d.readBool(57, &m_fps, false);
|
||||
d.readBool(58, &m_fxaa, false);
|
||||
d.readS32(59, &m_msaa, 1);
|
||||
|
||||
return true;
|
||||
}
|
||||
else
|
||||
@ -451,6 +457,8 @@ void MapSettings::MapItemSettings::resetToDefaults()
|
||||
m_filterName = "";
|
||||
m_filterDistance = 0;
|
||||
m_extrapolate = 60;
|
||||
m_smoothingWindow = 0;
|
||||
m_smoothingLambda = 100;
|
||||
}
|
||||
|
||||
QByteArray MapSettings::MapItemSettings::serialize() const
|
||||
@ -475,6 +483,8 @@ QByteArray MapSettings::MapItemSettings::serialize() const
|
||||
s.writeString(16, m_filterName);
|
||||
s.writeS32(17, m_filterDistance);
|
||||
s.writeS32(18, m_extrapolate);
|
||||
s.writeS32(19, m_smoothingWindow);
|
||||
s.writeFloat(20, m_smoothingLambda);
|
||||
|
||||
return s.final();
|
||||
}
|
||||
@ -511,6 +521,16 @@ bool MapSettings::MapItemSettings::deserialize(const QByteArray& data)
|
||||
d.readS32(18, &m_extrapolate, 60);
|
||||
m_filterNameRE.setPattern(m_filterName);
|
||||
m_filterNameRE.optimize();
|
||||
if (m_group == "ADSBDemod")
|
||||
{
|
||||
d.readS32(19, &m_smoothingWindow, 10);
|
||||
d.readFloat(20, &m_smoothingLambda, 100);
|
||||
}
|
||||
else
|
||||
{
|
||||
d.readS32(19, &m_smoothingWindow, 0);
|
||||
d.readFloat(20, &m_smoothingLambda, 100);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
else
|
||||
@ -703,6 +723,12 @@ void MapSettings::applySettings(const QStringList& settingsKeys, const MapSettin
|
||||
if (settingsKeys.contains("arcGISAPIKey")) {
|
||||
m_arcGISAPIKey = settings.m_arcGISAPIKey;
|
||||
}
|
||||
if (settingsKeys.contains("displayPFD")) {
|
||||
m_displayPFD = settings.m_displayPFD;
|
||||
}
|
||||
if (settingsKeys.contains("viewFirstPerson")) {
|
||||
m_viewFirstPerson = settings.m_viewFirstPerson;
|
||||
}
|
||||
if (settingsKeys.contains("workspaceIndex")) {
|
||||
m_workspaceIndex = settings.m_workspaceIndex;
|
||||
}
|
||||
@ -847,6 +873,12 @@ QString MapSettings::getDebugString(const QStringList& settingsKeys, bool force)
|
||||
if (settingsKeys.contains("arcGISAPIKey") || force) {
|
||||
ostr << " m_arcGISAPIKey: " << m_arcGISAPIKey.toStdString();
|
||||
}
|
||||
if (settingsKeys.contains("displayPFD") || force) {
|
||||
ostr << " m_displayPFD: " << m_displayPFD;
|
||||
}
|
||||
if (settingsKeys.contains("viewFirstPerson") || force) {
|
||||
ostr << " m_viewFirstPerson: " << m_viewFirstPerson;
|
||||
}
|
||||
if (settingsKeys.contains("workspaceIndex") || force) {
|
||||
ostr << " m_workspaceIndex: " << m_workspaceIndex;
|
||||
}
|
||||
|
@ -52,6 +52,8 @@ struct MapSettings
|
||||
QRegularExpression m_filterNameRE;
|
||||
int m_filterDistance; // Filter items > this distance in metres away from My Position. <= 0 don't filter
|
||||
int m_extrapolate; // Extrapolate duration in seconds on 3D map
|
||||
int m_smoothingWindow; // Window size (numer of points) to smooth over. 0 for no smoothing
|
||||
float m_smoothingLambda; // Lambda parameter for WhittakerEilers, controls how much smoothing to apply
|
||||
|
||||
MapItemSettings(const QString& group, bool enabled, const QColor color, bool display2DTrack=true, bool display3DPoint=true, int minZoom=11, int modelMinPixelSize=0);
|
||||
MapItemSettings(const QByteArray& data);
|
||||
@ -120,6 +122,8 @@ struct MapSettings
|
||||
bool m_displayAurora;
|
||||
bool m_displayMagDec;
|
||||
bool m_displayMaidenheadGrid;
|
||||
bool m_displayPFD;
|
||||
bool m_viewFirstPerson;
|
||||
QString m_defaultImagery;
|
||||
|
||||
QString m_checkWXAPIKey; //!< checkwxapi.com API key
|
||||
|
@ -53,10 +53,20 @@ MapItemSettingsGUI::MapItemSettingsGUI(QTableWidget *table, int row, MapSettings
|
||||
m_filterDistance->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
|
||||
m_filterDistance->setSpecialValueText(" ");
|
||||
m_filterDistance->setCorrectionMode(QAbstractSpinBox::CorrectToNearestValue);
|
||||
m_smoothingWindow = new QSpinBox(table);
|
||||
m_smoothingWindow->setRange(0, 1000);
|
||||
m_smoothingWindow->setValue(settings->m_smoothingWindow);
|
||||
m_smoothingWindow->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
|
||||
m_smoothingLambda = new QDoubleSpinBox(table);
|
||||
m_smoothingLambda->setRange(0, 1e9);
|
||||
m_smoothingLambda->setValue(settings->m_smoothingLambda);
|
||||
m_smoothingLambda->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
|
||||
table->setCellWidget(row, MapSettingsDialog::COL_2D_MIN_ZOOM, m_minZoom);
|
||||
table->setCellWidget(row, MapSettingsDialog::COL_3D_MIN_PIXELS, m_minPixels);
|
||||
table->setCellWidget(row, MapSettingsDialog::COL_3D_LABEL_SCALE, m_labelScale);
|
||||
table->setCellWidget(row, MapSettingsDialog::COL_FILTER_DISTANCE, m_filterDistance);
|
||||
table->setCellWidget(row, MapSettingsDialog::COL_SMOOTHING_WINDOW, m_smoothingWindow);
|
||||
table->setCellWidget(row, MapSettingsDialog::COL_SMOOTHING_LAMBDA, m_smoothingLambda);
|
||||
}
|
||||
|
||||
MapSettingsDialog::MapSettingsDialog(MapSettings *settings, QWidget* parent) :
|
||||
@ -142,6 +152,12 @@ MapSettingsDialog::MapSettingsDialog(MapSettings *settings, QWidget* parent) :
|
||||
item = new QTableWidgetItem(itemSettings->m_filterName);
|
||||
ui->mapItemSettings->setItem(row, COL_FILTER_NAME, item);
|
||||
|
||||
item = new QTableWidgetItem();
|
||||
ui->mapItemSettings->setItem(row, COL_SMOOTHING_WINDOW, item);
|
||||
|
||||
item = new QTableWidgetItem();
|
||||
ui->mapItemSettings->setItem(row, COL_SMOOTHING_LAMBDA, item);
|
||||
|
||||
MapItemSettingsGUI *gui = new MapItemSettingsGUI(ui->mapItemSettings, row, itemSettings);
|
||||
m_mapItemSettingsGUIs.append(gui);
|
||||
}
|
||||
@ -170,6 +186,8 @@ MapSettingsDialog::MapSettingsDialog(MapSettings *settings, QWidget* parent) :
|
||||
ui->mapItemSettings->hideColumn(COL_3D_POINT);
|
||||
ui->mapItemSettings->hideColumn(COL_3D_TRACK);
|
||||
ui->mapItemSettings->hideColumn(COL_3D_LABEL_SCALE);
|
||||
ui->mapItemSettings->hideColumn(COL_SMOOTHING_WINDOW);
|
||||
ui->mapItemSettings->hideColumn(COL_SMOOTHING_LAMBDA);
|
||||
#endif
|
||||
}
|
||||
|
||||
@ -203,7 +221,6 @@ void MapSettingsDialog::accept()
|
||||
m_settings->m_mapBoxAPIKey = mapBoxAPIKey;
|
||||
m_settings->m_osmURL = osmURL;
|
||||
m_settings->m_mapBoxStyles = mapBoxStyles;
|
||||
m_settings->m_cesiumIonAPIKey = cesiumIonAPIKey;
|
||||
m_map2DSettingsChanged = true;
|
||||
}
|
||||
else
|
||||
@ -223,7 +240,6 @@ void MapSettingsDialog::accept()
|
||||
{
|
||||
m_map3DSettingsChanged = false;
|
||||
}
|
||||
|
||||
if (m_settings->m_map2DEnabled != ui->map2DEnabled->isChecked())
|
||||
{
|
||||
m_settings->m_map2DEnabled = ui->map2DEnabled->isChecked();
|
||||
@ -323,6 +339,8 @@ void MapSettingsDialog::accept()
|
||||
itemSettings->m_filterNameRE.setPattern(itemSettings->m_filterName);
|
||||
itemSettings->m_filterNameRE.optimize();
|
||||
itemSettings->m_filterDistance = gui->m_filterDistance->value() * 1000;
|
||||
itemSettings->m_smoothingWindow = gui->m_smoothingWindow->value();
|
||||
itemSettings->m_smoothingLambda = gui->m_smoothingLambda->value();
|
||||
}
|
||||
|
||||
QDialog::accept();
|
||||
@ -359,6 +377,8 @@ void MapSettingsDialog::on_map3DEnabled_clicked(bool checked)
|
||||
ui->mapItemSettings->showColumn(COL_3D_POINT);
|
||||
ui->mapItemSettings->showColumn(COL_3D_TRACK);
|
||||
ui->mapItemSettings->showColumn(COL_3D_LABEL_SCALE);
|
||||
ui->mapItemSettings->showColumn(COL_SMOOTHING_WINDOW);
|
||||
ui->mapItemSettings->showColumn(COL_SMOOTHING_LAMBDA);
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -368,6 +388,8 @@ void MapSettingsDialog::on_map3DEnabled_clicked(bool checked)
|
||||
ui->mapItemSettings->hideColumn(COL_3D_POINT);
|
||||
ui->mapItemSettings->hideColumn(COL_3D_TRACK);
|
||||
ui->mapItemSettings->hideColumn(COL_3D_LABEL_SCALE);
|
||||
ui->mapItemSettings->hideColumn(COL_SMOOTHING_WINDOW);
|
||||
ui->mapItemSettings->hideColumn(COL_SMOOTHING_LAMBDA);
|
||||
}
|
||||
ui->terrain->setEnabled(checked);
|
||||
ui->buildings->setEnabled(checked);
|
||||
|
@ -48,6 +48,8 @@ public:
|
||||
QSpinBox *m_minPixels;
|
||||
QDoubleSpinBox *m_labelScale;
|
||||
QSpinBox *m_filterDistance;
|
||||
QSpinBox *m_smoothingWindow;
|
||||
QDoubleSpinBox *m_smoothingLambda;
|
||||
};
|
||||
|
||||
class MapSettingsDialog : public QDialog {
|
||||
@ -70,7 +72,9 @@ public:
|
||||
COL_3D_TRACK,
|
||||
COL_3D_LABEL_SCALE,
|
||||
COL_FILTER_NAME,
|
||||
COL_FILTER_DISTANCE
|
||||
COL_FILTER_DISTANCE,
|
||||
COL_SMOOTHING_WINDOW,
|
||||
COL_SMOOTHING_LAMBDA
|
||||
};
|
||||
|
||||
public:
|
||||
|
@ -7,7 +7,7 @@
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>1267</width>
|
||||
<height>648</height>
|
||||
<height>775</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="font">
|
||||
@ -153,13 +153,52 @@
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="defaultImageryLabel">
|
||||
<property name="text">
|
||||
<string>Default imagery</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QComboBox" name="defaultImagery">
|
||||
<property name="toolTip">
|
||||
<string>Default imagery (Note there is a quota on Bing Maps Ariel usage)</string>
|
||||
</property>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>ArcGIS world imagery</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Bing Maps Aerial</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Sentinel-2</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Earth at night</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Ersi world ocean</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="terrainLabel">
|
||||
<property name="text">
|
||||
<string>Terrain</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<item row="2" column="1">
|
||||
<widget class="QComboBox" name="terrain">
|
||||
<item>
|
||||
<property name="text">
|
||||
@ -183,14 +222,48 @@
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<item row="3" column="0">
|
||||
<widget class="QLabel" name="terrainLightingLabel">
|
||||
<property name="text">
|
||||
<string>Terrain lighting</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="1">
|
||||
<widget class="QCheckBox" name="terrainLighting">
|
||||
<property name="toolTip">
|
||||
<string>Enable terrain lighting</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="0">
|
||||
<widget class="QLabel" name="waterLabel">
|
||||
<property name="text">
|
||||
<string>Water effects</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="1">
|
||||
<widget class="QCheckBox" name="water">
|
||||
<property name="toolTip">
|
||||
<string>Enable water effects such as waves</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="5" column="0">
|
||||
<widget class="QLabel" name="buildingsLabel">
|
||||
<property name="text">
|
||||
<string>Buildings</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<item row="5" column="1">
|
||||
<widget class="QComboBox" name="buildings">
|
||||
<item>
|
||||
<property name="text">
|
||||
@ -204,14 +277,14 @@
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0">
|
||||
<item row="6" column="0">
|
||||
<widget class="QLabel" name="sunLightEnabledLabel">
|
||||
<property name="text">
|
||||
<string>Lighting</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="1">
|
||||
<item row="6" column="1">
|
||||
<widget class="QComboBox" name="sunLightEnabled">
|
||||
<property name="toolTip">
|
||||
<string>Whether lighting is from the Sun or Camera</string>
|
||||
@ -228,14 +301,71 @@
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="0">
|
||||
<item row="7" column="0">
|
||||
<widget class="QLabel" name="lightIntensityLabel">
|
||||
<property name="text">
|
||||
<string>Camera light Intensity</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="7" column="1">
|
||||
<widget class="QDoubleSpinBox" name="lightIntensity">
|
||||
<property name="toolTip">
|
||||
<string>Intensity of camera light</string>
|
||||
</property>
|
||||
<property name="decimals">
|
||||
<number>1</number>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<double>100.000000000000000</double>
|
||||
</property>
|
||||
<property name="value">
|
||||
<double>3.000000000000000</double>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="8" column="0">
|
||||
<widget class="QLabel" name="hdrLabel">
|
||||
<property name="text">
|
||||
<string>HDR</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="8" column="1">
|
||||
<widget class="QCheckBox" name="hdr">
|
||||
<property name="toolTip">
|
||||
<string>High dynamic range</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="9" column="0">
|
||||
<widget class="QLabel" name="fogLabel">
|
||||
<property name="text">
|
||||
<string>Fog</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="9" column="1">
|
||||
<widget class="QCheckBox" name="fog">
|
||||
<property name="toolTip">
|
||||
<string>Enable fog effect</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="11" column="0">
|
||||
<widget class="QLabel" name="eciCameraLabel">
|
||||
<property name="text">
|
||||
<string>Camera reference frame</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="1">
|
||||
<item row="11" column="1">
|
||||
<widget class="QComboBox" name="eciCamera">
|
||||
<property name="toolTip">
|
||||
<string>Selects camera reference frame. For ECEF the camera rotates with the Earth. For ECI, the camera position is fixed relative to the stars and the Earth's rotation will be visible.</string>
|
||||
@ -252,28 +382,77 @@
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="5" column="0">
|
||||
<widget class="QLabel" name="antiAliasingLabel">
|
||||
<item row="14" column="0">
|
||||
<widget class="QLabel" name="fpsLabel">
|
||||
<property name="text">
|
||||
<string>Anti-aliasing</string>
|
||||
<string>Display FPS</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="5" column="1">
|
||||
<widget class="QComboBox" name="antiAliasing">
|
||||
<item row="14" column="1">
|
||||
<widget class="QCheckBox" name="fps">
|
||||
<property name="toolTip">
|
||||
<string>Set anti-aliasing to use. This can remove jagged pixels on the edge of 3D models.</string>
|
||||
<string>Display frames per second (FPS)</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="13" column="0">
|
||||
<widget class="QLabel" name="msaaLabel">
|
||||
<property name="text">
|
||||
<string>MSAA</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="13" column="1">
|
||||
<widget class="QComboBox" name="msaa">
|
||||
<property name="toolTip">
|
||||
<string>Multisample Anti-Aliasing</string>
|
||||
</property>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>None</string>
|
||||
<string>Off</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>FXAA</string>
|
||||
<string>2</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>4</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>8</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>16</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="12" column="0">
|
||||
<widget class="QLabel" name="fxaaLabel">
|
||||
<property name="text">
|
||||
<string>FXAA</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="12" column="1">
|
||||
<widget class="QCheckBox" name="fxaa">
|
||||
<property name="toolTip">
|
||||
<string>Fast Approximate Anti-aliasing</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
@ -282,7 +461,7 @@
|
||||
<item>
|
||||
<spacer name="verticalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
<enum>Qt::Orientation::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
@ -313,7 +492,7 @@
|
||||
<string/>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="icons.qrc">
|
||||
<iconset resource="mapicons.qrc">
|
||||
<normaloff>:/map/icons/controltower.png</normaloff>:/map/icons/controltower.png</iconset>
|
||||
</property>
|
||||
</widget>
|
||||
@ -327,7 +506,7 @@
|
||||
<string/>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="icons.qrc">
|
||||
<iconset resource="mapicons.qrc">
|
||||
<normaloff>:/map/icons/vor.png</normaloff>:/map/icons/vor.png</iconset>
|
||||
</property>
|
||||
</widget>
|
||||
@ -341,7 +520,7 @@
|
||||
<string/>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="icons.qrc">
|
||||
<iconset resource="mapicons.qrc">
|
||||
<normaloff>:/map/icons/waypoints.png</normaloff>:/map/icons/waypoints.png</iconset>
|
||||
</property>
|
||||
</widget>
|
||||
@ -349,7 +528,7 @@
|
||||
<item>
|
||||
<spacer name="horizontalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
<enum>Qt::Orientation::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
@ -371,7 +550,7 @@
|
||||
<item>
|
||||
<widget class="QTableWidget" name="mapItemSettings">
|
||||
<property name="selectionMode">
|
||||
<enum>QAbstractItemView::NoSelection</enum>
|
||||
<enum>QAbstractItemView::SelectionMode::NoSelection</enum>
|
||||
</property>
|
||||
<column>
|
||||
<property name="text">
|
||||
@ -468,6 +647,22 @@
|
||||
<string>Filter objects further than this distance in km away from My Position</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Smoothing Window</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>How many coordinates to apply smoothing filter to. Set to 0 for no smoothing.</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Smoothing Lambda</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Smoothing parameter. Higher values result in more smoothing.</string>
|
||||
</property>
|
||||
</column>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
@ -563,20 +758,34 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="0">
|
||||
<item row="5" column="0">
|
||||
<widget class="QLabel" name="checkWXAPIKeyLabel">
|
||||
<property name="text">
|
||||
<string>CheckWX API key</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="1">
|
||||
<item row="5" column="1">
|
||||
<widget class="QLineEdit" name="checkWXAPIKey">
|
||||
<property name="toolTip">
|
||||
<string>checkwxapi.com API key for accessing airport weather (METARs)</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="0">
|
||||
<widget class="QLabel" name="arcGISAPIKeyLabel">
|
||||
<property name="text">
|
||||
<string>ArcGIS API Key</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="1">
|
||||
<widget class="QLineEdit" name="arcGISAPIKey">
|
||||
<property name="toolTip">
|
||||
<string>Enter an ArcGIS API Key</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
@ -590,10 +799,10 @@
|
||||
<item>
|
||||
<widget class="QDialogButtonBox" name="buttonBox">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
<enum>Qt::Orientation::Horizontal</enum>
|
||||
</property>
|
||||
<property name="standardButtons">
|
||||
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
|
||||
<set>QDialogButtonBox::StandardButton::Cancel|QDialogButtonBox::StandardButton::Ok</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
@ -609,7 +818,7 @@
|
||||
<tabstop>buildings</tabstop>
|
||||
<tabstop>sunLightEnabled</tabstop>
|
||||
<tabstop>eciCamera</tabstop>
|
||||
<tabstop>antiAliasing</tabstop>
|
||||
<tabstop>msaa</tabstop>
|
||||
<tabstop>downloadModels</tabstop>
|
||||
<tabstop>thunderforestAPIKey</tabstop>
|
||||
<tabstop>maptilerAPIKey</tabstop>
|
||||
@ -617,7 +826,7 @@
|
||||
<tabstop>cesiumIonAPIKey</tabstop>
|
||||
</tabstops>
|
||||
<resources>
|
||||
<include location="icons.qrc"/>
|
||||
<include location="mapicons.qrc"/>
|
||||
</resources>
|
||||
<connections>
|
||||
<connection>
|
||||
|
Loading…
x
Reference in New Issue
Block a user