mirror of
https://github.com/f4exb/sdrangel.git
synced 2025-08-25 00:42:35 -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);
|
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
|
// Play glTF model animation for the map item with the specified name
|
||||||
void CesiumInterface::playAnimation(const QString &name, Animation *animation)
|
void CesiumInterface::playAnimation(const QString &name, Animation *animation)
|
||||||
{
|
{
|
||||||
|
@ -56,6 +56,7 @@ public:
|
|||||||
CesiumInterface(const MapSettings *settings, QObject *parent = nullptr);
|
CesiumInterface(const MapSettings *settings, QObject *parent = nullptr);
|
||||||
void setHomeView(float latitude, float longitude, float angle=1.0f);
|
void setHomeView(float latitude, float longitude, float angle=1.0f);
|
||||||
void setView(float latitude, float longitude, float altitude=60000);
|
void setView(float latitude, float longitude, float altitude=60000);
|
||||||
|
void setViewFirstPerson(bool firstPerson);
|
||||||
void playAnimation(const QString &name, Animation *animation);
|
void playAnimation(const QString &name, Animation *animation);
|
||||||
void setDateTime(QDateTime dateTime);
|
void setDateTime(QDateTime dateTime);
|
||||||
void getDateTime();
|
void getDateTime();
|
||||||
@ -69,6 +70,7 @@ public:
|
|||||||
void setHDR(bool enabled);
|
void setHDR(bool enabled);
|
||||||
void setFog(bool enabled);
|
void setFog(bool enabled);
|
||||||
void showFPS(bool show);
|
void showFPS(bool show);
|
||||||
|
void showPFD(bool show);
|
||||||
void showMUF(bool show);
|
void showMUF(bool show);
|
||||||
void showfoF2(bool show);
|
void showfoF2(bool show);
|
||||||
void showMagDec(bool show);
|
void showMagDec(bool show);
|
||||||
|
@ -23,6 +23,8 @@
|
|||||||
|
|
||||||
#include "util/coordinates.h"
|
#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"};
|
const QStringList CZML::m_heightReferences = {"NONE", "CLAMP_TO_GROUND", "RELATIVE_TO_GROUND", "CLIP_TO_GROUND"};
|
||||||
|
|
||||||
CZML::CZML(const MapSettings *settings) :
|
CZML::CZML(const MapSettings *settings) :
|
||||||
@ -48,7 +50,8 @@ bool CZML::filter(const MapItem *mapItem) const
|
|||||||
|
|
||||||
QJsonObject CZML::init()
|
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 stop = QDateTime::currentDateTimeUtc().addSecs(60*60).toString(Qt::ISODate);
|
||||||
QString interval = QString("%1/%2").arg(start).arg(stop);
|
QString interval = QString("%1/%2").arg(start).arg(stop);
|
||||||
|
|
||||||
@ -241,6 +244,48 @@ QJsonObject CZML::update(PolylineMapItem *mapItem)
|
|||||||
return obj;
|
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)
|
QJsonObject CZML::update(ObjectMapItem *mapItem, bool isTarget, bool isSelected)
|
||||||
{
|
{
|
||||||
(void) isTarget;
|
(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
|
// Keep a hash of the time we first saw each item
|
||||||
bool existingId = m_ids.contains(id);
|
bool existingId = m_ids.contains(id);
|
||||||
|
State& state = m_ids[id];
|
||||||
if (!existingId) {
|
if (!existingId) {
|
||||||
m_ids.insert(id, dt);
|
state.m_firstSeenDateTime = dt;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool removeObj = false;
|
bool removeObj = false;
|
||||||
@ -318,14 +364,49 @@ QJsonObject CZML::update(ObjectMapItem *mapItem, bool isTarget, bool isSelected)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
{
|
||||||
|
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
|
// Only send latest position, to reduce processing
|
||||||
if (!fixedPosition && mapItem->m_positionDateTime.isValid()) {
|
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_positionDateTime.toString(Qt::ISODateWithMs));
|
||||||
}
|
}
|
||||||
coords.push_back(mapItem->m_longitude);
|
coords.push_back(mapItem->m_longitude);
|
||||||
coords.push_back(mapItem->m_latitude);
|
coords.push_back(mapItem->m_latitude);
|
||||||
coords.push_back(mapItem->m_altitude);
|
}
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
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
|
// Need 2 different positions to enable extrapolation, otherwise entity may not appear
|
||||||
bool hasMoved = m_hasMoved.contains(id);
|
bool hasMoved = m_hasMoved.contains(id);
|
||||||
|
|
||||||
if (!hasMoved && m_lastPosition.contains(id) && (m_lastPosition.value(id) != coords))
|
if (!hasMoved && m_lastPosition.contains(id) && (m_lastPosition.value(id) != coords))
|
||||||
{
|
{
|
||||||
hasMoved = true;
|
hasMoved = true;
|
||||||
@ -351,12 +433,16 @@ QJsonObject CZML::update(ObjectMapItem *mapItem, bool isTarget, bool isSelected)
|
|||||||
if (hasMoved && (mapItem->m_itemSettings->m_extrapolate > 0))
|
if (hasMoved && (mapItem->m_itemSettings->m_extrapolate > 0))
|
||||||
{
|
{
|
||||||
position.insert("forwardExtrapolationType", "EXTRAPOLATE");
|
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);
|
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("interpolationAlgorithm", "HERMITE");
|
||||||
//position.insert("interpolationDegree", "2");
|
//position.insert("interpolationDegree", 2);
|
||||||
//position.insert("interpolationAlgorithm", "LAGRANGE");
|
//position.insert("interpolationAlgorithm", "LAGRANGE");
|
||||||
//position.insert("interpolationDegree", "5");
|
//position.insert("interpolationDegree", 5); // crazy interpolation for LAGRANGE
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -365,11 +451,11 @@ QJsonObject CZML::update(ObjectMapItem *mapItem, bool isTarget, bool isSelected)
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Interpolation goes wrong at end points
|
// Interpolation goes wrong at end points. FIXME: Check if still true
|
||||||
//position.insert("interpolationAlgorithm", "LAGRANGE");
|
//position.insert("interpolationAlgorithm", "LAGRANGE");
|
||||||
//position.insert("interpolationDegree", "5");
|
//position.insert("interpolationDegree", 5);
|
||||||
//position.insert("interpolationAlgorithm", "HERMITE");
|
//position.insert("interpolationAlgorithm", "HERMITE");
|
||||||
//position.insert("interpolationDegree", "2");
|
//position.insert("interpolationDegree", 2);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -398,8 +484,257 @@ QJsonObject CZML::update(ObjectMapItem *mapItem, bool isTarget, bool isSelected)
|
|||||||
{"forwardExtrapolationType", "NONE"}
|
{"forwardExtrapolationType", "NONE"}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
if (mapItem->m_useHeadingPitchRoll) {
|
||||||
|
obj.insert("orientation", orientation);
|
||||||
|
} else {
|
||||||
|
obj.insert("orientation", orientationPosition);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Point
|
// Point
|
||||||
QColor pointColor = QColor::fromRgba(mapItem->m_itemSettings->m_3DPointColor);
|
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 {
|
QJsonArray pointRGBA {
|
||||||
pointColor.red(), pointColor.green(), pointColor.blue(), pointColor.alpha()
|
pointColor.red(), pointColor.green(), pointColor.blue(), pointColor.alpha()
|
||||||
};
|
};
|
||||||
@ -409,68 +744,32 @@ QJsonObject CZML::update(ObjectMapItem *mapItem, bool isTarget, bool isSelected)
|
|||||||
QJsonObject point {
|
QJsonObject point {
|
||||||
{"pixelSize", 8},
|
{"pixelSize", 8},
|
||||||
{"color", pointColorObj},
|
{"color", pointColorObj},
|
||||||
{"heightReference", heightReferences[mapItem->m_altitudeReference]},
|
{"heightReference", heightReferences[pointAltitudeReference]},
|
||||||
{"show", mapItem->m_itemSettings->m_enabled && mapItem->m_itemSettings->m_display3DPoint}
|
{"show", pointShow}
|
||||||
};
|
};
|
||||||
|
obj.insert("point", point);
|
||||||
|
|
||||||
// Model
|
state.m_pointColorInt = pointColorInt;
|
||||||
QJsonArray node0Cartesian {
|
state.m_pointAltitudeReference = pointAltitudeReference;
|
||||||
{0.0, mapItem->m_modelAltitudeOffset, 0.0}
|
state.m_pointShow = pointShow;
|
||||||
};
|
|
||||||
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
|
// 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
|
// Prevent labels from being too cluttered when zoomed out
|
||||||
// FIXME: These values should come from mapItem or mapItemSettings
|
// FIXME: These values should come from mapItem or mapItemSettings
|
||||||
float displayDistanceMax = std::numeric_limits<float>::max();
|
float displayDistanceMax = std::numeric_limits<float>::max();
|
||||||
@ -499,7 +798,7 @@ QJsonObject CZML::update(ObjectMapItem *mapItem, bool isTarget, bool isSelected)
|
|||||||
{"cartesian2", labelPixelOffsetArray}
|
{"cartesian2", labelPixelOffsetArray}
|
||||||
};
|
};
|
||||||
QJsonArray labelEyeOffsetArray {
|
QJsonArray labelEyeOffsetArray {
|
||||||
0, mapItem->m_labelAltitudeOffset, 0 // Position above the object, dependent on the height of the model
|
0, labelAltitudeOffset, 0 // Position above the object, dependent on the height of the model
|
||||||
};
|
};
|
||||||
QJsonObject labelEyeOffset {
|
QJsonObject labelEyeOffset {
|
||||||
{"cartesian", labelEyeOffsetArray}
|
{"cartesian", labelEyeOffsetArray}
|
||||||
@ -513,23 +812,101 @@ QJsonObject CZML::update(ObjectMapItem *mapItem, bool isTarget, bool isSelected)
|
|||||||
QJsonObject labelDistanceDisplayCondition {
|
QJsonObject labelDistanceDisplayCondition {
|
||||||
{"distanceDisplayCondition", labelDisplayDistance}
|
{"distanceDisplayCondition", labelDisplayDistance}
|
||||||
};
|
};
|
||||||
QString labelText = mapItem->m_label;
|
|
||||||
labelText.replace("<br>", "\n");
|
labelText.replace("<br>", "\n");
|
||||||
QJsonObject label {
|
QJsonObject label {
|
||||||
{"text", labelText},
|
//{"text", labelText},
|
||||||
{"show", m_settings->m_displayNames && mapItem->m_itemSettings->m_enabled && mapItem->m_itemSettings->m_display3DLabel},
|
{"show", labelShow},
|
||||||
{"scale", mapItem->m_itemSettings->m_3DLabelScale},
|
{"scale", labelScale},
|
||||||
{"pixelOffset", labelPixelOffset},
|
{"pixelOffset", labelPixelOffset},
|
||||||
{"pixelOffsetScaleByDistance", labelPixelOffsetScaleObject},
|
{"pixelOffsetScaleByDistance", labelPixelOffsetScaleObject},
|
||||||
{"eyeOffset", labelEyeOffset},
|
{"eyeOffset", labelEyeOffset},
|
||||||
{"verticalOrigin", "BASELINE"},
|
{"verticalOrigin", "BASELINE"},
|
||||||
{"horizontalOrigin", "LEFT"},
|
{"horizontalOrigin", "LEFT"},
|
||||||
{"heightReference", heightReferences[mapItem->m_altitudeReference]},
|
{"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()) {
|
if (displayDistanceMax != std::numeric_limits<float>::max()) {
|
||||||
label.insert("distanceDisplayCondition", labelDistanceDisplayCondition);
|
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
|
// Use billboard for APRS as we don't currently have 3D objects
|
||||||
QString imageURL = mapItem->m_image;
|
QString imageURL = mapItem->m_image;
|
||||||
if (imageURL.startsWith("qrc://")) {
|
if (imageURL.startsWith("qrc://")) {
|
||||||
@ -541,27 +918,64 @@ QJsonObject CZML::update(ObjectMapItem *mapItem, bool isTarget, bool isSelected)
|
|||||||
{"verticalOrigin", "BOTTOM"} // To stop it being cut in half when zoomed out
|
{"verticalOrigin", "BOTTOM"} // To stop it being cut in half when zoomed out
|
||||||
};
|
};
|
||||||
|
|
||||||
if (!removeObj)
|
|
||||||
{
|
|
||||||
obj.insert("position", position);
|
|
||||||
if (!fixedPosition)
|
|
||||||
{
|
|
||||||
if (mapItem->m_useHeadingPitchRoll) {
|
|
||||||
obj.insert("orientation", orientation);
|
|
||||||
} else {
|
|
||||||
obj.insert("orientation", orientationPosition);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
obj.insert("point", point);
|
|
||||||
if (!mapItem->m_model.isEmpty()) {
|
|
||||||
obj.insert("model", model);
|
|
||||||
} else {
|
|
||||||
obj.insert("billboard", billboard);
|
obj.insert("billboard", billboard);
|
||||||
}
|
}
|
||||||
obj.insert("label", label);
|
|
||||||
obj.insert("description", mapItem->m_text);
|
// Description
|
||||||
if (!fixedPosition) {
|
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);
|
obj.insert("path", path);
|
||||||
|
|
||||||
|
state.m_pathColorInt = pathColorInt;
|
||||||
|
state.m_pathShow = pathShow;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!fixedPosition)
|
if (!fixedPosition)
|
||||||
@ -577,7 +991,7 @@ QJsonObject CZML::update(ObjectMapItem *mapItem, bool isTarget, bool isSelected)
|
|||||||
{
|
{
|
||||||
if (mapItem->m_availableUntil.isValid())
|
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);
|
obj.insert("availability", period);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -586,7 +1000,7 @@ QJsonObject CZML::update(ObjectMapItem *mapItem, bool isTarget, bool isSelected)
|
|||||||
{
|
{
|
||||||
if (mapItem->m_availableUntil.isValid())
|
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);
|
obj.insert("availability", period);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -599,13 +1013,24 @@ QJsonObject CZML::update(ObjectMapItem *mapItem, bool isTarget, bool isSelected)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Use our own clipping routine, due to
|
// Use our own clipping routine, due to
|
||||||
// https://github.com/CesiumGS/cesium/issues/4049
|
// https://github.com/CesiumGS/cesium/issues/4049 - Has now been fixed
|
||||||
if (mapItem->m_altitudeReference == 3) {
|
//if (mapItem->m_altitudeReference == 3) {
|
||||||
obj.insert("altitudeReference", "CLIP_TO_GROUND");
|
// obj.insert("altitudeReference", "CLIP_TO_GROUND");
|
||||||
}
|
//}
|
||||||
|
|
||||||
//qDebug() << obj;
|
//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;
|
return obj;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -24,10 +24,13 @@
|
|||||||
#define INCLUDE_FEATURE_CZML_H_
|
#define INCLUDE_FEATURE_CZML_H_
|
||||||
|
|
||||||
#include <QHash>
|
#include <QHash>
|
||||||
|
#include <QFile>
|
||||||
#include <QJsonArray>
|
#include <QJsonArray>
|
||||||
#include <QJsonObject>
|
#include <QJsonObject>
|
||||||
#include <QGeoCoordinate>
|
#include <QGeoCoordinate>
|
||||||
|
|
||||||
|
#include "mapaircraftstate.h"
|
||||||
|
|
||||||
struct MapSettings;
|
struct MapSettings;
|
||||||
class MapItem;
|
class MapItem;
|
||||||
class ObjectMapItem;
|
class ObjectMapItem;
|
||||||
@ -36,13 +39,59 @@ class PolylineMapItem;
|
|||||||
|
|
||||||
class CZML
|
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:
|
private:
|
||||||
const MapSettings *m_settings;
|
const MapSettings *m_settings;
|
||||||
QHash<QString, QString> m_ids;
|
QHash<QString, State> m_ids;
|
||||||
QHash<QString, QJsonArray> m_lastPosition;
|
QHash<QString, QJsonArray> m_lastPosition;
|
||||||
QHash<QString, bool> m_hasMoved;
|
QHash<QString, bool> m_hasMoved;
|
||||||
QGeoCoordinate m_position;
|
QGeoCoordinate m_position;
|
||||||
static const QStringList m_heightReferences;
|
static const QStringList m_heightReferences;
|
||||||
|
QFile m_file;
|
||||||
|
QFile m_csvFile;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
CZML(const MapSettings *settings);
|
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
@ -5,6 +5,7 @@
|
|||||||
<script src="/Cesium/Cesium.js"></script>
|
<script src="/Cesium/Cesium.js"></script>
|
||||||
<style>
|
<style>
|
||||||
@import url(/Cesium/Widgets/widgets.css);
|
@import url(/Cesium/Widgets/widgets.css);
|
||||||
|
|
||||||
html,
|
html,
|
||||||
body,
|
body,
|
||||||
#cesiumContainer {
|
#cesiumContainer {
|
||||||
@ -15,20 +16,19 @@
|
|||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
<meta
|
<meta name="viewport"
|
||||||
name="viewport"
|
content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" />
|
||||||
content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no"
|
|
||||||
/>
|
|
||||||
</head>
|
</head>
|
||||||
<body style="margin:0;padding:0">
|
<body style="margin:0;padding:0">
|
||||||
<div id="cesiumContainer"></div>
|
<div id="cesiumContainer"></div>
|
||||||
<script src="grid.js"></script>
|
<script src="grid.js"></script>
|
||||||
|
<script>
|
||||||
|
|
||||||
// See: https://community.cesium.com/t/how-to-run-an-animation-for-an-entity-model/16932
|
// See: https://community.cesium.com/t/how-to-run-an-animation-for-an-entity-model/16932
|
||||||
function getActiveAnimations(viewer, entity) {
|
function getActiveAnimations(viewer, entity) {
|
||||||
var primitives = viewer.scene.primitives;
|
var primitives = viewer.scene.primitives;
|
||||||
var length = primitives.length;
|
var length = primitives.length;
|
||||||
for(var i = 0; i < length; i++) {
|
for (var i = 0; i < length; i++) {
|
||||||
var primitive = primitives.get(i);
|
var primitive = primitives.get(i);
|
||||||
if (primitive.id === entity && primitive instanceof Cesium.Model && primitive.ready) {
|
if (primitive.id === entity && primitive instanceof Cesium.Model && primitive.ready) {
|
||||||
return primitive.activeAnimations;
|
return primitive.activeAnimations;
|
||||||
@ -48,7 +48,7 @@
|
|||||||
startOffset: command.startOffset,
|
startOffset: command.startOffset,
|
||||||
reverse: command.reverse,
|
reverse: command.reverse,
|
||||||
loop: command.loop ? Cesium.ModelAnimationLoop.REPEAT : Cesium.ModelAnimationLoop.NONE,
|
loop: command.loop ? Cesium.ModelAnimationLoop.REPEAT : Cesium.ModelAnimationLoop.NONE,
|
||||||
multiplier: command.multiplier,
|
multiplier: 0.2 // command.multiplier,
|
||||||
};
|
};
|
||||||
options.startTime = Cesium.JulianDate.fromIso8601(command.startDateTime);
|
options.startTime = Cesium.JulianDate.fromIso8601(command.startDateTime);
|
||||||
// https://github.com/CesiumGS/cesium/issues/10048
|
// https://github.com/CesiumGS/cesium/issues/10048
|
||||||
@ -60,7 +60,7 @@
|
|||||||
if (command.duration != 0) {
|
if (command.duration != 0) {
|
||||||
options.stopTime = Cesium.JulianDate.addSeconds(options.startTime, command.duration, new Cesium.JulianDate());
|
options.stopTime = Cesium.JulianDate.addSeconds(options.startTime, command.duration, new Cesium.JulianDate());
|
||||||
}
|
}
|
||||||
animations.add(options);
|
const anim = animations.add(options);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
// Note we get TypeError instead of DeveloperError, if running minified version of Cesium
|
// Note we get TypeError instead of DeveloperError, if running minified version of Cesium
|
||||||
if ((e instanceof Cesium.DeveloperError) || (e instanceof TypeError)) {
|
if ((e instanceof Cesium.DeveloperError) || (e instanceof TypeError)) {
|
||||||
@ -76,9 +76,9 @@
|
|||||||
// Give Entity time to create primitive
|
// Give Entity time to create primitive
|
||||||
// No ready promise in entity API - https://github.com/CesiumGS/cesium/issues/4727
|
// No ready promise in entity API - https://github.com/CesiumGS/cesium/issues/4727
|
||||||
if (retries > 0) {
|
if (retries > 0) {
|
||||||
setTimeout(function() {
|
setTimeout(function () {
|
||||||
//console.log(`Retrying animation for entity ${command.id}`);
|
//console.log(`Retrying animation for entity ${command.id}`);
|
||||||
playAnimation(viewer, command, retries-1);
|
playAnimation(viewer, command, retries - 1);
|
||||||
}, 1000);
|
}, 1000);
|
||||||
} else {
|
} else {
|
||||||
console.log(`Gave up trying to play animation for entity ${command.id}`);
|
console.log(`Gave up trying to play animation for entity ${command.id}`);
|
||||||
@ -87,9 +87,9 @@
|
|||||||
} else {
|
} else {
|
||||||
// It seems in some cases, entities aren't created immediately, so wait and retry
|
// It seems in some cases, entities aren't created immediately, so wait and retry
|
||||||
if (retries > 0) {
|
if (retries > 0) {
|
||||||
setTimeout(function() {
|
setTimeout(function () {
|
||||||
//console.log(`Retrying entity ${command.id}`);
|
//console.log(`Retrying entity ${command.id}`);
|
||||||
playAnimation(viewer, command, retries-1);
|
playAnimation(viewer, command, retries - 1);
|
||||||
}, 1000);
|
}, 1000);
|
||||||
} else {
|
} else {
|
||||||
console.log(`Gave up trying to find entity ${command.id}`);
|
console.log(`Gave up trying to find entity ${command.id}`);
|
||||||
@ -127,7 +127,7 @@
|
|||||||
loop: anim.loop,
|
loop: anim.loop,
|
||||||
multiplier: anim.multiplier,
|
multiplier: anim.multiplier,
|
||||||
startTime: anim.startTime,
|
startTime: anim.startTime,
|
||||||
stopTime: Cesium.JulianDate.fromIso8601(command.startDateTime)
|
stopTime: Cesium.JulianDate.fromIso8601(command.startDateTime) // FIXME: Should this be stopDateTime?
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -148,8 +148,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Polygons (such as for airspaces) should be prioritized behind other entities
|
// Polygons (such as for airspaces) should be prioritized behind other entities
|
||||||
function pickEntityPrioritized(e)
|
function pickEntityPrioritized(e) {
|
||||||
{
|
|
||||||
var picked = viewer.scene.drillPick(e.position);
|
var picked = viewer.scene.drillPick(e.position);
|
||||||
if (Cesium.defined(picked)) {
|
if (Cesium.defined(picked)) {
|
||||||
var firstPolygon = null;
|
var firstPolygon = null;
|
||||||
@ -171,6 +170,24 @@
|
|||||||
viewer.selectedEntity = pickEntityPrioritized(e);
|
viewer.selectedEntity = pickEntityPrioritized(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function pickAndTrack(e) {
|
||||||
|
const entity = pickEntityPrioritized(e);
|
||||||
|
if (Cesium.defined(entity)) {
|
||||||
|
if (viewFirstPerson) {
|
||||||
|
setFirstPersonView(entity);
|
||||||
|
} else {
|
||||||
|
if (Cesium.Property.getValueOrUndefined(entity.position, viewer.clock.currentTime)) {
|
||||||
|
setThirdPersonView(entity);
|
||||||
|
} else {
|
||||||
|
viewer.zoomTo(entity);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (Cesium.defined(viewer.trackedEntity)) {
|
||||||
|
//viewer.trackedEntity = undefined;
|
||||||
|
setThirdPersonView(undefined);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function showCoords(e) {
|
function showCoords(e) {
|
||||||
if (viewer.terrainProvider instanceof Cesium.EllipsoidTerrainProvider) {
|
if (viewer.terrainProvider instanceof Cesium.EllipsoidTerrainProvider) {
|
||||||
var cartesian = viewer.camera.pickEllipsoid(e.position);
|
var cartesian = viewer.camera.pickEllipsoid(e.position);
|
||||||
@ -189,8 +206,9 @@
|
|||||||
const ray = viewer.camera.getPickRay(e.position);
|
const ray = viewer.camera.getPickRay(e.position);
|
||||||
const cartesian = viewer.scene.globe.pick(ray, viewer.scene);
|
const cartesian = viewer.scene.globe.pick(ray, viewer.scene);
|
||||||
var cartographic = Cesium.Cartographic.fromCartesian(cartesian);
|
var cartographic = Cesium.Cartographic.fromCartesian(cartesian);
|
||||||
var promise = Cesium.sampleTerrainMostDetailed(viewer.terrainProvider, [cartographic]);
|
Promise.resolve(
|
||||||
Cesium.when(promise, function(updatedPositions) {
|
Cesium.sampleTerrainMostDetailed(viewer.terrainProvider, [cartographic]),
|
||||||
|
).then((updatedPositions) => {
|
||||||
longitudeString = Cesium.Math.toDegrees(cartographic.longitude).toFixed(6);
|
longitudeString = Cesium.Math.toDegrees(cartographic.longitude).toFixed(6);
|
||||||
latitudeString = Cesium.Math.toDegrees(cartographic.latitude).toFixed(6);
|
latitudeString = Cesium.Math.toDegrees(cartographic.latitude).toFixed(6);
|
||||||
heightString = updatedPositions[0].height.toFixed(1);
|
heightString = updatedPositions[0].height.toFixed(1);
|
||||||
@ -201,8 +219,6 @@
|
|||||||
`Lon: ${` ${longitudeString}`}\u00B0` +
|
`Lon: ${` ${longitudeString}`}\u00B0` +
|
||||||
`\nLat: ${` ${latitudeString}`}\u00B0` +
|
`\nLat: ${` ${latitudeString}`}\u00B0` +
|
||||||
`\nAlt: ${` ${heightString}`}m`;
|
`\nAlt: ${` ${heightString}`}m`;
|
||||||
}, function() {
|
|
||||||
console.log(`Terrain doesn't support sampleTerrainMostDetailed`);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -213,9 +229,14 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
Cesium.Ion.defaultAccessToken = '$CESIUM_ION_API_KEY$';
|
Cesium.Ion.defaultAccessToken = '$CESIUM_ION_API_KEY$';
|
||||||
|
if ('$ARCGIS_API_KEY$' != '') {
|
||||||
|
Cesium.ArcGisMapService.defaultAccessToken = '$ARCGIS_API_KEY$';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start time is set via CZML::init()
|
||||||
const viewer = new Cesium.Viewer('cesiumContainer', {
|
const viewer = new Cesium.Viewer('cesiumContainer', {
|
||||||
terrainProvider: Cesium.createWorldTerrain(),
|
baseLayer: false,
|
||||||
|
terrainProvider: Cesium.createWorldTerrainAsync(),
|
||||||
animation: true,
|
animation: true,
|
||||||
shouldAnimate: true,
|
shouldAnimate: true,
|
||||||
timeline: true,
|
timeline: true,
|
||||||
@ -225,26 +246,39 @@
|
|||||||
navigationInstructionsInitiallyVisible: false,
|
navigationInstructionsInitiallyVisible: false,
|
||||||
terrainProviderViewModels: [] // User should adjust terrain via dialog, so depthTestAgainstTerrain doesn't get set
|
terrainProviderViewModels: [] // User should adjust terrain via dialog, so depthTestAgainstTerrain doesn't get set
|
||||||
});
|
});
|
||||||
|
//viewer.scene.debugShowFramesPerSecond = true; // FIXME: Embedded Chrome only runs at 60fps
|
||||||
viewer.scene.globe.depthTestAgainstTerrain = false; // So labels/points aren't clipped by terrain (this prevents pickPosition from working)
|
viewer.scene.globe.depthTestAgainstTerrain = false; // So labels/points aren't clipped by terrain (this prevents pickPosition from working)
|
||||||
|
viewer.scene.globe.tileCacheSize = 5000; // FIXME: Embedded Chrome is slower at loading from cache
|
||||||
|
viewer.scene.moon.onlySunLighting = false; // Moon can be just a black dot if default of true
|
||||||
viewer.screenSpaceEventHandler.setInputAction(pickEntity, Cesium.ScreenSpaceEventType.LEFT_CLICK);
|
viewer.screenSpaceEventHandler.setInputAction(pickEntity, Cesium.ScreenSpaceEventType.LEFT_CLICK);
|
||||||
|
viewer.screenSpaceEventHandler.setInputAction(pickAndTrack, Cesium.ScreenSpaceEventType.LEFT_DOUBLE_CLICK);
|
||||||
viewer.screenSpaceEventHandler.setInputAction(showCoords, Cesium.ScreenSpaceEventType.LEFT_DOUBLE_CLICK, Cesium.KeyboardEventModifier.SHIFT);
|
viewer.screenSpaceEventHandler.setInputAction(showCoords, Cesium.ScreenSpaceEventType.LEFT_DOUBLE_CLICK, Cesium.KeyboardEventModifier.SHIFT);
|
||||||
viewer.screenSpaceEventHandler.setInputAction(hideCoords, Cesium.ScreenSpaceEventType.RIGHT_CLICK);
|
viewer.screenSpaceEventHandler.setInputAction(hideCoords, Cesium.ScreenSpaceEventType.RIGHT_CLICK);
|
||||||
|
|
||||||
|
viewer.useBrowserRecommendedResolution = false; // Improves label quality when false, as drawn at higher res
|
||||||
|
|
||||||
viewer.infoBox.frame.setAttribute('sandbox', 'allow-same-origin allow-popups allow-forms allow-scripts allow-top-navigation');
|
viewer.infoBox.frame.setAttribute('sandbox', 'allow-same-origin allow-popups allow-forms allow-scripts allow-top-navigation');
|
||||||
viewer.infoBox.frame.src = "about:blank"; // Force reload so new attributes are applied
|
viewer.infoBox.frame.src = "about:blank"; // Force reload so new attributes are applied
|
||||||
|
|
||||||
|
viewer.infoBox.viewModel.cameraClicked.removeEventListener(Cesium.Viewer.prototype._onInfoBoxCameraClicked, viewer); // Override info box camera button being clicked
|
||||||
|
viewer.infoBox.viewModel.cameraClicked.addEventListener(infoBoxCameraClicked, viewer);
|
||||||
|
|
||||||
|
var pfdTimer = undefined;
|
||||||
|
var pfdRadioAltTimer = undefined;
|
||||||
|
|
||||||
var buildings = undefined;
|
var buildings = undefined;
|
||||||
const images = new Map();
|
const images = new Map();
|
||||||
|
|
||||||
var mufGeoJSONStream = null;
|
var mufGeoJSONStream = null;
|
||||||
var foF2GeoJSONStream = null;
|
var foF2GeoJSONStream = null;
|
||||||
|
var wmmGeoJSONStream = null;
|
||||||
|
|
||||||
const positionMarker = viewer.entities.add({
|
const positionMarker = viewer.entities.add({
|
||||||
id: 'Position marker',
|
id: 'Position marker',
|
||||||
point : {
|
point: {
|
||||||
show: false,
|
show: false,
|
||||||
pixelSize : 8,
|
pixelSize: 8,
|
||||||
color : Cesium.Color.RED,
|
color: Cesium.Color.RED,
|
||||||
heightReference: Cesium.HeightReference.RELATIVE_TO_GROUND
|
heightReference: Cesium.HeightReference.RELATIVE_TO_GROUND
|
||||||
},
|
},
|
||||||
label: {
|
label: {
|
||||||
@ -284,6 +318,18 @@
|
|||||||
return html;
|
return html;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Generate HTML for WMM contour info box from properties in GeoJSON
|
||||||
|
function describeWMM(properties, nameProperty) {
|
||||||
|
let html = "";
|
||||||
|
if (properties.hasOwnProperty("Contour")) {
|
||||||
|
const value = properties["Contour"];
|
||||||
|
if (Cesium.defined(value)) {
|
||||||
|
html = `<p>Magnetic declination: ${value} degrees`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return html;
|
||||||
|
}
|
||||||
|
|
||||||
// Use CZML to stream data from Map plugin to Cesium
|
// Use CZML to stream data from Map plugin to Cesium
|
||||||
var czmlStream = new Cesium.CzmlDataSource();
|
var czmlStream = new Cesium.CzmlDataSource();
|
||||||
|
|
||||||
@ -293,6 +339,72 @@
|
|||||||
viewer.scene.light.direction = Cesium.Cartesian3.clone(scene.camera.directionWC, viewer.scene.light.direction);
|
viewer.scene.light.direction = Cesium.Cartesian3.clone(scene.camera.directionWC, viewer.scene.light.direction);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var velocityVectorProperty = undefined;
|
||||||
|
const velocityVector = new Cesium.Cartesian3();
|
||||||
|
|
||||||
|
var viewFirstPerson = false;
|
||||||
|
var firstPersonEntity;
|
||||||
|
var firstPersonOffset = 0.0;
|
||||||
|
var cameraSavedPositionValid = false;
|
||||||
|
var cameraSavedPosition;
|
||||||
|
var cameraSavedHeading;
|
||||||
|
var cameraSavedPitch;
|
||||||
|
var cameraSavedRoll;
|
||||||
|
var cameraSavedTransform;
|
||||||
|
var cameraInitPos = false;
|
||||||
|
|
||||||
|
// First person camera
|
||||||
|
function cameraFirstPerson(scene, time) {
|
||||||
|
const entity = firstPersonEntity;
|
||||||
|
if (!Cesium.defined(entity)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const position = entity.position.getValue(time);
|
||||||
|
if (!Cesium.defined(position)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let transform;
|
||||||
|
if (!Cesium.defined(entity.orientation)) {
|
||||||
|
transform = Cesium.Transforms.eastNorthUpToFixedFrame(position);
|
||||||
|
} else {
|
||||||
|
const orientation = entity.orientation.getValue(time);
|
||||||
|
if (!Cesium.defined(orientation)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
transform = Cesium.Matrix4.fromRotationTranslation(
|
||||||
|
Cesium.Matrix3.fromQuaternion(orientation),
|
||||||
|
position,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const camera = viewer.camera;
|
||||||
|
|
||||||
|
if (cameraInitPos) {
|
||||||
|
camera.position = new Cesium.Cartesian3(firstPersonOffset, 0.0, 0.0);
|
||||||
|
camera.direction = new Cesium.Cartesian3(1.0, 0.0, 0.0);
|
||||||
|
camera.up = new Cesium.Cartesian3(0.0, 0.0, 1.0);
|
||||||
|
camera.right = new Cesium.Cartesian3(0.0, -1.0, 0.0);
|
||||||
|
cameraInitPos = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Save camera state
|
||||||
|
const offset = Cesium.Cartesian3.clone(camera.position);
|
||||||
|
const direction = Cesium.Cartesian3.clone(camera.direction);
|
||||||
|
const up = Cesium.Cartesian3.clone(camera.up);
|
||||||
|
|
||||||
|
// Set camera to be in model's reference frame.
|
||||||
|
camera.lookAtTransform(transform);
|
||||||
|
|
||||||
|
// Reset the camera state to the saved state so it appears fixed in the model's frame.
|
||||||
|
Cesium.Cartesian3.clone(offset, camera.position);
|
||||||
|
Cesium.Cartesian3.clone(direction, camera.direction);
|
||||||
|
Cesium.Cartesian3.clone(up, camera.up);
|
||||||
|
Cesium.Cartesian3.cross(direction, up, camera.right);
|
||||||
|
}
|
||||||
|
|
||||||
// Image overlays
|
// Image overlays
|
||||||
|
|
||||||
function dataCallback(interval, index) {
|
function dataCallback(interval, index) {
|
||||||
@ -321,6 +433,7 @@
|
|||||||
// See https://wiki.earthdata.nasa.gov/display/GIBS/GIBS+API+for+Developers#GIBSAPIforDevelopers-OGCWebMapService(WMS)
|
// See https://wiki.earthdata.nasa.gov/display/GIBS/GIBS+API+for+Developers#GIBSAPIforDevelopers-OGCWebMapService(WMS)
|
||||||
var gibsProvider = new Cesium.WebMapTileServiceImageryProvider({
|
var gibsProvider = new Cesium.WebMapTileServiceImageryProvider({
|
||||||
url: "https://gibs.earthdata.nasa.gov/wmts/epsg4326/best/MODIS_Terra_CorrectedReflectance_TrueColor/default/{Time}/{TileMatrixSet}/{TileMatrix}/{TileRow}/{TileCol}.jpg",
|
url: "https://gibs.earthdata.nasa.gov/wmts/epsg4326/best/MODIS_Terra_CorrectedReflectance_TrueColor/default/{Time}/{TileMatrixSet}/{TileMatrix}/{TileRow}/{TileCol}.jpg",
|
||||||
|
layer: '', // FIXME
|
||||||
style: "default",
|
style: "default",
|
||||||
tileMatrixSetID: "250m",
|
tileMatrixSetID: "250m",
|
||||||
format: "image/jpeg",
|
format: "image/jpeg",
|
||||||
@ -339,6 +452,11 @@
|
|||||||
var cloudProvider = new Cesium.UrlTemplateImageryProvider({
|
var cloudProvider = new Cesium.UrlTemplateImageryProvider({
|
||||||
url: "https://tilecache.rainviewer.com/v2/satellite/0000000000/256/{z}/{x}/{y}/0/0_0.png"
|
url: "https://tilecache.rainviewer.com/v2/satellite/0000000000/256/{z}/{x}/{y}/0/0_0.png"
|
||||||
});
|
});
|
||||||
|
var auroraProvider = new Cesium.SingleTileImageryProvider({
|
||||||
|
url: "aurora.png",
|
||||||
|
tileWidth: 360,
|
||||||
|
tileHeight: 181
|
||||||
|
});
|
||||||
|
|
||||||
var gibsLayer = new Cesium.ImageryLayer(gibsProvider);
|
var gibsLayer = new Cesium.ImageryLayer(gibsProvider);
|
||||||
gibsLayer.show = false;
|
gibsLayer.show = false;
|
||||||
@ -355,13 +473,17 @@
|
|||||||
const railwaysLayer = new Cesium.ImageryLayer(railwaysProvider);
|
const railwaysLayer = new Cesium.ImageryLayer(railwaysProvider);
|
||||||
railwaysLayer.show = false;
|
railwaysLayer.show = false;
|
||||||
viewer.imageryLayers.add(railwaysLayer);
|
viewer.imageryLayers.add(railwaysLayer);
|
||||||
|
var auroraLayer = new Cesium.ImageryLayer(auroraProvider);
|
||||||
|
auroraLayer.show = false;
|
||||||
|
viewer.imageryLayers.add(auroraLayer);
|
||||||
|
|
||||||
const layers = new Map([
|
const layers = new Map([
|
||||||
["nasaGlobalImagery", gibsLayer],
|
["nasaGlobalImagery", gibsLayer],
|
||||||
["clouds", cloudLayer],
|
["clouds", cloudLayer],
|
||||||
["rain", rainLayer],
|
["rain", rainLayer],
|
||||||
["seaMarks", seaMarksLayer],
|
["seaMarks", seaMarksLayer],
|
||||||
["railways", railwaysLayer]
|
["railways", railwaysLayer],
|
||||||
|
["aurora", auroraLayer]
|
||||||
]);
|
]);
|
||||||
|
|
||||||
function downloadBlob(filename, blob) {
|
function downloadBlob(filename, blob) {
|
||||||
@ -415,7 +537,7 @@
|
|||||||
// and sending events back to it
|
// and sending events back to it
|
||||||
let socket = new WebSocket("ws://127.0.0.1:$WS_PORT$");
|
let socket = new WebSocket("ws://127.0.0.1:$WS_PORT$");
|
||||||
|
|
||||||
socket.onmessage = function(event) {
|
socket.onmessage = function (event) {
|
||||||
try {
|
try {
|
||||||
const command = JSON.parse(event.data);
|
const command = JSON.parse(event.data);
|
||||||
|
|
||||||
@ -440,13 +562,22 @@
|
|||||||
heading: 0,
|
heading: 0,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
} else if (command.command == "setViewFirstPerson") {
|
||||||
|
if (command.firstPerson) {
|
||||||
|
// Use first person view from entity
|
||||||
|
viewFirstPerson = true;
|
||||||
|
setFirstPersonView(viewer.trackedEntity);
|
||||||
|
} else {
|
||||||
|
viewFirstPerson = false;
|
||||||
|
setThirdPersonView(firstPersonEntity);
|
||||||
|
}
|
||||||
} else if (command.command == "playAnimation") {
|
} else if (command.command == "playAnimation") {
|
||||||
// Play model animation
|
// Play model animation
|
||||||
if (command.stop) {
|
if (command.stop) {
|
||||||
//console.log(`stopping animation ${command.animation} for ${command.id}`);
|
//console.log(`stopping animation ${command.animation} for ${command.id}`);
|
||||||
stopAnimation(viewer, command);
|
stopAnimation(viewer, command);
|
||||||
} else {
|
} else {
|
||||||
//console.log(`playing animation ${command.animation} for ${command.id}`);
|
//console.log(`playing animation ${command.animation} for ${command.id} command` + JSON.stringify(command));
|
||||||
playAnimation(viewer, command, 30);
|
playAnimation(viewer, command, 30);
|
||||||
}
|
}
|
||||||
} else if (command.command == "setDateTime") {
|
} else if (command.command == "setDateTime") {
|
||||||
@ -465,15 +596,35 @@
|
|||||||
viewer.terrainProvider = new Cesium.EllipsoidTerrainProvider();
|
viewer.terrainProvider = new Cesium.EllipsoidTerrainProvider();
|
||||||
}
|
}
|
||||||
} else if (command.provider == "Cesium World Terrain") {
|
} else if (command.provider == "Cesium World Terrain") {
|
||||||
viewer.terrainProvider = Cesium.createWorldTerrain();
|
viewer.scene.setTerrain(
|
||||||
|
Cesium.Terrain.fromWorldTerrain({
|
||||||
|
requestWaterMask: command.water,
|
||||||
|
requestVertexNormals: command.terrainLighting
|
||||||
|
})
|
||||||
|
);
|
||||||
} else if (command.provider == "CesiumTerrainProvider") {
|
} else if (command.provider == "CesiumTerrainProvider") {
|
||||||
viewer.terrainProvider = new Cesium.CesiumTerrainProvider({
|
viewer.scene.setTerrain(
|
||||||
url: command.url
|
new Cesium.Terrain(
|
||||||
});
|
Cesium.CesiumTerrainProvider.fromUrl(
|
||||||
|
command.url,
|
||||||
|
{
|
||||||
|
requestWaterMask: command.water,
|
||||||
|
requestVertexNormals: command.terrainLighting
|
||||||
|
}
|
||||||
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
} else if (command.provider == "ArcGISTiledElevationTerrainProvider") {
|
} else if (command.provider == "ArcGISTiledElevationTerrainProvider") {
|
||||||
viewer.terrainProvider = new Cesium.ArcGISTiledElevationTerrainProvider({
|
viewer.scene.setTerrain(
|
||||||
url: command.url
|
new Cesium.Terrain(
|
||||||
});
|
Cesium.ArcGISTiledElevationTerrainProvider.fromUrl(
|
||||||
|
command.url,
|
||||||
|
{
|
||||||
|
requestWaterMask: command.water,
|
||||||
|
requestVertexNormals: command.terrainLighting
|
||||||
|
})
|
||||||
|
)
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
console.log(`Unknown terrain ${command.terrain}`);
|
console.log(`Unknown terrain ${command.terrain}`);
|
||||||
}
|
}
|
||||||
@ -486,16 +637,21 @@
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (buildings === undefined) {
|
if (buildings === undefined) {
|
||||||
buildings = viewer.scene.primitives.add(Cesium.createOsmBuildings());
|
Promise.resolve(
|
||||||
|
Cesium.createOsmBuildingsAsync()
|
||||||
|
).then((osmBuildingsTileset) => {
|
||||||
|
buildings = viewer.scene.primitives.add(osmBuildingsTileset);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (command.command == "setSunLight") {
|
} else if (command.command == "setLighting") {
|
||||||
// Enable illumination of the globe from the direction of the Sun or camera
|
|
||||||
viewer.scene.globe.enableLighting = command.useSunLight;
|
viewer.scene.globe.enableLighting = command.useSunLight;
|
||||||
viewer.scene.globe.nightFadeOutDistance = 0.0;
|
//viewer.scene.globe.nightFadeOutDistance = 0.0; // FIXME: Can't be nearly 0. Causes terrain above horizon to be blacked out in 1.129
|
||||||
|
// Currently Cesium only supports a single light source, either Sun or Directional
|
||||||
if (!command.useSunLight) {
|
if (!command.useSunLight) {
|
||||||
viewer.scene.light = new Cesium.DirectionalLight({
|
viewer.scene.light = new Cesium.DirectionalLight({
|
||||||
direction: new Cesium.Cartesian3(1, 0, 0)
|
direction: new Cesium.Cartesian3(1, 0, 0),
|
||||||
|
intensity: command.cameraLightIntensity
|
||||||
});
|
});
|
||||||
viewer.scene.preRender.addEventListener(cameraLight);
|
viewer.scene.preRender.addEventListener(cameraLight);
|
||||||
} else {
|
} else {
|
||||||
@ -509,10 +665,40 @@
|
|||||||
viewer.scene.postUpdate.removeEventListener(icrf);
|
viewer.scene.postUpdate.removeEventListener(icrf);
|
||||||
}
|
}
|
||||||
} else if (command.command == "setAntiAliasing") {
|
} else if (command.command == "setAntiAliasing") {
|
||||||
if (command.antiAliasing == "FXAA") {
|
viewer.scene.postProcessStages.fxaa.enabled = command.fxaa;
|
||||||
viewer.scene.postProcessStages.fxaa.enabled = true;
|
viewer.scene.msaaSamples = command.msaa;
|
||||||
|
} else if (command.command == "setHDR") {
|
||||||
|
if (command.hdr) {
|
||||||
|
viewer.scene.highDynamicRange = true;
|
||||||
} else {
|
} else {
|
||||||
viewer.scene.postProcessStages.fxaa.enabled = false;
|
viewer.scene.highDynamicRange = false;
|
||||||
|
}
|
||||||
|
} else if (command.command == "setFog") {
|
||||||
|
if (command.fog) {
|
||||||
|
viewer.scene.fog.enabled = true;
|
||||||
|
} else {
|
||||||
|
viewer.scene.fog.enabled = false;
|
||||||
|
}
|
||||||
|
} else if (command.command == "showFPS") {
|
||||||
|
if (command.show) {
|
||||||
|
viewer.scene.debugShowFramesPerSecond = true;
|
||||||
|
} else {
|
||||||
|
viewer.scene.debugShowFramesPerSecond = false;
|
||||||
|
}
|
||||||
|
} else if (command.command == "showPFD") {
|
||||||
|
const pfdCanvas = document.getElementById("pfdCanvas");
|
||||||
|
if (command.show == true) {
|
||||||
|
if (pfdTimer === undefined) {
|
||||||
|
pfdTimer = setInterval(updatePFD, 10);
|
||||||
|
pfdRadioAltTimer = setInterval(updateRadioAlt, 250);
|
||||||
|
}
|
||||||
|
canvas.removeAttribute("hidden");
|
||||||
|
} else {
|
||||||
|
canvas.setAttribute("hidden", "hidden");
|
||||||
|
clearInterval(pfdTimer);
|
||||||
|
clearInterval(pfdRadioAltTimer);
|
||||||
|
pfdTimer = undefined;
|
||||||
|
pfdRadioAltTimer = undefined;
|
||||||
}
|
}
|
||||||
} else if (command.command == "showMUF") {
|
} else if (command.command == "showMUF") {
|
||||||
if (command.show == true) {
|
if (command.show == true) {
|
||||||
@ -550,12 +736,48 @@
|
|||||||
viewer.dataSources.remove(foF2GeoJSONStream, true);
|
viewer.dataSources.remove(foF2GeoJSONStream, true);
|
||||||
foF2GeoJSONStream = null;
|
foF2GeoJSONStream = null;
|
||||||
}
|
}
|
||||||
|
} else if (command.command == "showMagneticDeclination") {
|
||||||
|
if (command.show == true) {
|
||||||
|
viewer.dataSources.add(
|
||||||
|
Cesium.GeoJsonDataSource.load(
|
||||||
|
"/map/data/wmm.geojson",
|
||||||
|
{ describe: describeWMM }
|
||||||
|
)
|
||||||
|
).then(function (dataSource) {
|
||||||
|
if (wmmGeoJSONStream != null) {
|
||||||
|
viewer.dataSources.remove(wmmGeoJSONStream, true);
|
||||||
|
wmmGeoJSONStream = null;
|
||||||
|
}
|
||||||
|
wmmGeoJSONStream = dataSource;
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
viewer.dataSources.remove(wmmGeoJSONStream, true);
|
||||||
|
wmmGeoJSONStream = null;
|
||||||
|
}
|
||||||
|
} else if (command.command == "showMaidenheadGrid") {
|
||||||
|
showGrid(command.show);
|
||||||
|
|
||||||
|
} else if (command.command == "setDefaultImagery") {
|
||||||
|
// For indexes, see pacakges/widgets/Source/BaseLayerPicker/createDefaultImageryProviderViewModels.js
|
||||||
|
if (command.imagery == "Bing Maps Aerial") {
|
||||||
|
viewer.baseLayerPicker.viewModel.selectedImagery = viewer.baseLayerPicker.viewModel.imageryProviderViewModels[0];
|
||||||
|
} else if (command.imagery == "ArcGIS world imagery") {
|
||||||
|
viewer.baseLayerPicker.viewModel.selectedImagery = viewer.baseLayerPicker.viewModel.imageryProviderViewModels[3];
|
||||||
|
} else if (command.imagery == "Ersi world ocean") {
|
||||||
|
viewer.baseLayerPicker.viewModel.selectedImagery = viewer.baseLayerPicker.viewModel.imageryProviderViewModels[5];
|
||||||
|
} else if (command.imagery == "Sentinel-2") {
|
||||||
|
viewer.baseLayerPicker.viewModel.selectedImagery = viewer.baseLayerPicker.viewModel.imageryProviderViewModels[11];
|
||||||
|
} else if (command.imagery == "Earth at night") {
|
||||||
|
viewer.baseLayerPicker.viewModel.selectedImagery = viewer.baseLayerPicker.viewModel.imageryProviderViewModels[13];
|
||||||
|
} else {
|
||||||
|
console.log(`Unknown imagery ${command.imagery}`);
|
||||||
|
}
|
||||||
} else if (command.command == "showLayer") {
|
} else if (command.command == "showLayer") {
|
||||||
layers.get(command.layer).show = command.show;
|
layers.get(command.layer).show = command.show;
|
||||||
} else if (command.command == "setLayerSettings") {
|
} else if (command.command == "setLayerSettings") {
|
||||||
if (command.layer == "NASAGlobalImagery") {
|
if (command.layer == "NASAGlobalImagery") {
|
||||||
if ('url' in command) {
|
if ('url' in command) {
|
||||||
console.log("Using URL: " + command.url + " format: " + command.format + " matrixSet: " + command.tileMatrixSet + " dates:" + command.dates + " length: " + command.dates.length + " typeof: " + typeof(command.dates));
|
console.log("Using URL: " + command.url + " format: " + command.format + " matrixSet: " + command.tileMatrixSet + " dates:" + command.dates + " length: " + command.dates.length + " typeof: " + typeof (command.dates));
|
||||||
|
|
||||||
viewer.imageryLayers.remove(gibsLayer, true);
|
viewer.imageryLayers.remove(gibsLayer, true);
|
||||||
|
|
||||||
@ -585,6 +807,7 @@
|
|||||||
|
|
||||||
gibsProvider = new Cesium.WebMapTileServiceImageryProvider({
|
gibsProvider = new Cesium.WebMapTileServiceImageryProvider({
|
||||||
url: command.url,
|
url: command.url,
|
||||||
|
layer: '', // FIXME
|
||||||
style: "default",
|
style: "default",
|
||||||
tileMatrixSetID: command.tileMatrixSet,
|
tileMatrixSetID: command.tileMatrixSet,
|
||||||
format: command.format,
|
format: command.format,
|
||||||
@ -619,26 +842,37 @@
|
|||||||
rainLayer.show = command.show;
|
rainLayer.show = command.show;
|
||||||
viewer.imageryLayers.add(rainLayer);
|
viewer.imageryLayers.add(rainLayer);
|
||||||
layers.set(command.layer, rainLayer);
|
layers.set(command.layer, rainLayer);
|
||||||
|
} else if (command.layer == "aurora") {
|
||||||
|
viewer.imageryLayers.remove(auroraLayer, true);
|
||||||
|
auroraProvider = new Cesium.SingleTileImageryProvider({
|
||||||
|
url: "aurora.png",
|
||||||
|
tileWidth: 360,
|
||||||
|
tileHeight: 181
|
||||||
|
});
|
||||||
|
auroraLayer = new Cesium.ImageryLayer(auroraProvider);
|
||||||
|
auroraLayer.show = command.show;
|
||||||
|
viewer.imageryLayers.add(auroraLayer);
|
||||||
|
layers.set(command.layer, auroraLayer);
|
||||||
} else {
|
} else {
|
||||||
console.log("Unknown layer: " + command.layer);
|
console.log("Unknown layer: " + command.layer);
|
||||||
}
|
}
|
||||||
} else if (command.command == "updateImage") {
|
} else if (command.command == "updateImage") {
|
||||||
|
|
||||||
// Textures on entities can flash white when changed: https://github.com/CesiumGS/cesium/issues/1640
|
// Textures on entities can flash white when changed: https://github.com/CesiumGS/cesium/issues/1640
|
||||||
// so we use a primitive instead of an entity
|
// so we use a primitive instead of an entity - FIXME: No longer working
|
||||||
// Can't modify geometry of primitives, so need to create a new primitive each time
|
// Can't modify geometry of primitives, so need to create a new primitive each time
|
||||||
// Material needs to be set as translucent in order to allow camera to zoom through it
|
// Material needs to be set as translucent in order to allow camera to zoom through it
|
||||||
var oldImage = images.get(command.name);
|
var oldImage = images.get(command.name);
|
||||||
var image = viewer.scene.primitives.add(new Cesium.Primitive({
|
var image = viewer.scene.primitives.add(new Cesium.Primitive({
|
||||||
geometryInstances : new Cesium.GeometryInstance({
|
geometryInstances: new Cesium.GeometryInstance({
|
||||||
geometry : new Cesium.RectangleGeometry({
|
geometry: new Cesium.RectangleGeometry({
|
||||||
rectangle : Cesium.Rectangle.fromDegrees(command.west, command.south, command.east, command.north),
|
rectangle: Cesium.Rectangle.fromDegrees(command.west, command.south, command.east, command.north),
|
||||||
vertexFormat : Cesium.EllipsoidSurfaceAppearance.VERTEX_FORMAT,
|
vertexFormat: Cesium.EllipsoidSurfaceAppearance.VERTEX_FORMAT,
|
||||||
height: command.altitude
|
height: command.altitude
|
||||||
})
|
})
|
||||||
}),
|
}),
|
||||||
appearance : new Cesium.EllipsoidSurfaceAppearance({
|
appearance: new Cesium.EllipsoidSurfaceAppearance({
|
||||||
aboveGround : false,
|
aboveGround: false,
|
||||||
material: new Cesium.Material({
|
material: new Cesium.Material({
|
||||||
fabric: {
|
fabric: {
|
||||||
type: 'Image',
|
type: 'Image',
|
||||||
@ -652,8 +886,12 @@
|
|||||||
}));
|
}));
|
||||||
images.set(command.name, image);
|
images.set(command.name, image);
|
||||||
if (oldImage !== undefined) {
|
if (oldImage !== undefined) {
|
||||||
image.readyPromise.then(function(prim) {
|
const removeListener = viewer.scene.postRender.addEventListener(() => {
|
||||||
|
if (!image.ready) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
viewer.scene.primitives.remove(oldImage);
|
viewer.scene.primitives.remove(oldImage);
|
||||||
|
removeListener();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
} else if (command.command == "removeImage") {
|
} else if (command.command == "removeImage") {
|
||||||
@ -664,13 +902,13 @@
|
|||||||
console.log(`Can't find image ${command.name} to remove it`);
|
console.log(`Can't find image ${command.name} to remove it`);
|
||||||
}
|
}
|
||||||
} else if (command.command == "removeAllImages") {
|
} else if (command.command == "removeAllImages") {
|
||||||
for (let [k,image] of images) {
|
for (let [k, image] of images) {
|
||||||
viewer.scene.primitives.remove(image);
|
viewer.scene.primitives.remove(image);
|
||||||
}
|
}
|
||||||
} else if (command.command == "removeAllCZMLEntities") {
|
} else if (command.command == "removeAllCZMLEntities") {
|
||||||
czmlStream.entities.removeAll();
|
czmlStream.entities.removeAll();
|
||||||
} else if (command.command == "czml") {
|
} else if (command.command == "czml") {
|
||||||
// Implement CLIP_TO_GROUND, to work around https://github.com/CesiumGS/cesium/issues/4049
|
// Implement CLIP_TO_GROUND, to work around https://github.com/CesiumGS/cesium/issues/4049 - Now fixed, so this may be obsolete
|
||||||
if (command.hasOwnProperty('altitudeReference') && command.hasOwnProperty('position') && command.position.hasOwnProperty('cartographicDegrees')) {
|
if (command.hasOwnProperty('altitudeReference') && command.hasOwnProperty('position') && command.position.hasOwnProperty('cartographicDegrees')) {
|
||||||
var size = command.position.cartographicDegrees.length;
|
var size = command.position.cartographicDegrees.length;
|
||||||
if ((size == 3) || (size == 4)) {
|
if ((size == 3) || (size == 4)) {
|
||||||
@ -694,8 +932,9 @@
|
|||||||
}
|
}
|
||||||
czmlStream.process(command);
|
czmlStream.process(command);
|
||||||
} else {
|
} else {
|
||||||
var promise = Cesium.sampleTerrainMostDetailed(viewer.terrainProvider, [position]);
|
Promise.resolve(
|
||||||
Cesium.when(promise, function (updatedPositions) {
|
Cesium.sampleTerrainMostDetailed(viewer.terrainProvider, [position]),
|
||||||
|
).then((updatedPositions) => {
|
||||||
if (height < updatedPositions[0].height) {
|
if (height < updatedPositions[0].height) {
|
||||||
if (size == 3) {
|
if (size == 3) {
|
||||||
command.position.cartographicDegrees[2] = updatedPositions[0].height;
|
command.position.cartographicDegrees[2] = updatedPositions[0].height;
|
||||||
@ -704,13 +943,10 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
czmlStream.process(command);
|
czmlStream.process(command);
|
||||||
}, function () {
|
|
||||||
console.log(`Terrain doesn't support sampleTerrainMostDetailed`);
|
|
||||||
czmlStream.process(command);
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
console.log(`Can't currently use altitudeReference when more than one position`);
|
console.log(`Can't currently use altitudeReference when more than one position`, command.position);
|
||||||
czmlStream.process(command);
|
czmlStream.process(command);
|
||||||
}
|
}
|
||||||
} else if ((command.hasOwnProperty('polygon') && command.polygon.hasOwnProperty('altitudeReference'))
|
} else if ((command.hasOwnProperty('polygon') && command.polygon.hasOwnProperty('altitudeReference'))
|
||||||
@ -739,8 +975,9 @@
|
|||||||
for (let i = 0; i < positionCount; i++) {
|
for (let i = 0; i < positionCount; i++) {
|
||||||
positions[i] = Cesium.Cartographic.fromDegrees(prim.positions.cartographicDegrees[i * 3 + 0], prim.positions.cartographicDegrees[i * 3 + 1]);
|
positions[i] = Cesium.Cartographic.fromDegrees(prim.positions.cartographicDegrees[i * 3 + 0], prim.positions.cartographicDegrees[i * 3 + 1]);
|
||||||
}
|
}
|
||||||
var promise = Cesium.sampleTerrainMostDetailed(viewer.terrainProvider, positions);
|
Promise.resolve(
|
||||||
Cesium.when(promise, function (updatedPositions) {
|
Cesium.sampleTerrainMostDetailed(viewer.terrainProvider, positions),
|
||||||
|
).then((updatedPositions) => {
|
||||||
if (clampToGround) {
|
if (clampToGround) {
|
||||||
for (let i = 0; i < positionCount; i++) {
|
for (let i = 0; i < positionCount; i++) {
|
||||||
prim.positions.cartographicDegrees[i * 3 + 2] = updatedPositions[i].height;
|
prim.positions.cartographicDegrees[i * 3 + 2] = updatedPositions[i].height;
|
||||||
@ -753,9 +990,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
czmlStream.process(command);
|
czmlStream.process(command);
|
||||||
}, function () {
|
|
||||||
console.log(`Terrain doesn't support sampleTerrainMostDetailed`);
|
|
||||||
czmlStream.process(command);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -779,24 +1013,112 @@
|
|||||||
console.log(`Unknown command ${command.command}`);
|
console.log(`Unknown command ${command.command}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
} catch(e) {
|
} catch (e) {
|
||||||
console.log(`Erroring processing received message:\n${e}\n${event.data}`);
|
console.log(`Erroring processing received message:\n${e}\n${event.data}`);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
viewer.selectedEntityChanged.addEventListener(function(selectedEntity) {
|
function setFirstPersonView(entity) {
|
||||||
if (Cesium.defined(selectedEntity) && Cesium.defined(selectedEntity.id)) {
|
if (!Cesium.defined(entity)) {
|
||||||
socket.send(JSON.stringify({event: "selected", id: selectedEntity.id}));
|
viewer.scene.postUpdate.removeEventListener(cameraFirstPerson);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const camera = viewer.camera;
|
||||||
|
|
||||||
|
// Save current camera position/orientation so we can restore it, when going back to third person
|
||||||
|
/*
|
||||||
|
cameraSavedPosition = camera.positionWC.clone(cameraSavedPosition);
|
||||||
|
cameraSavedHeading = camera.heading;
|
||||||
|
cameraSavedPitch = camera.pitch;
|
||||||
|
cameraSavedRoll = camera.roll;
|
||||||
|
cameraSavedTransform = camera.transform.clone(cameraSavedTransform);
|
||||||
|
cameraSavedPositionValid = true;
|
||||||
|
console.log("******* SAVED POSITION", cameraSavedPosition, cameraSavedHeading, cameraSavedPitch, cameraSavedRoll, cameraSavedTransform);*/
|
||||||
|
|
||||||
|
viewer.trackedEntity = entity; // So infobox camera icon indicates we're trackingd
|
||||||
|
viewer.cesiumWidget._needTrackedEntityUpdate = false; // Prevent camera from zooming to it a bit later, and overwriting the position we set
|
||||||
|
|
||||||
|
firstPersonEntity = entity;
|
||||||
|
|
||||||
|
// Get size of model, so we can position camera at front of it, rather than in the middle
|
||||||
|
if (Cesium.defined(firstPersonEntity) && Cesium.defined(firstPersonEntity.id)) {
|
||||||
|
var primitives = viewer.scene.primitives;
|
||||||
|
var length = primitives.length;
|
||||||
|
for (var i = 0; i < length; i++) {
|
||||||
|
var primitive = primitives.get(i);
|
||||||
|
if (primitive.id === firstPersonEntity && primitive instanceof Cesium.Model && primitive.ready) {
|
||||||
|
firstPersonOffset = primitive.boundingSphere.radius;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cameraInitPos = true;
|
||||||
|
|
||||||
|
viewer.scene.postUpdate.addEventListener(cameraFirstPerson);
|
||||||
|
}
|
||||||
|
|
||||||
|
function setThirdPersonView(entity) {
|
||||||
|
viewer.trackedEntity = undefined; // If we're switching from first to third, ensure trackedEntity changes, so camera switches to it
|
||||||
|
viewer.trackedEntity = entity;
|
||||||
|
viewer.scene.postUpdate.removeEventListener(cameraFirstPerson);
|
||||||
|
firstPersonEntity = undefined;
|
||||||
|
/*if (cameraSavedPositionValid && !Cesium.defined(entity)) {
|
||||||
|
console.log("******* SAVED POSITION RESTORED", cameraSavedPosition, cameraSavedHeading, cameraSavedPitch, cameraSavedRoll, cameraSavedTransform);
|
||||||
|
const camera = viewer.camera;
|
||||||
|
camera.setView({
|
||||||
|
destination: cameraSavedPosition,
|
||||||
|
orientation: {
|
||||||
|
heading: cameraSavedHeading,
|
||||||
|
pitch: cameraSavedPitch,
|
||||||
|
roll: cameraSavedRoll
|
||||||
|
},
|
||||||
|
endTransform: cameraSavedTransform
|
||||||
|
});
|
||||||
|
//camera.transform = Cesium.Matrix4.clone(cameraSavedTransform, camera.transform);
|
||||||
|
cameraSavedPositionValid = false;
|
||||||
} else {
|
} else {
|
||||||
socket.send(JSON.stringify({event: "selected"}));
|
console.log("******* SAVED POSITION NOT RESTORED");
|
||||||
|
}*/
|
||||||
|
}
|
||||||
|
|
||||||
|
function infoBoxCameraClicked(infoBoxViewModel) {
|
||||||
|
if (infoBoxViewModel.isCameraTracking && viewer.trackedEntity === viewer.selectedEntity) {
|
||||||
|
if (viewFirstPerson === true) {
|
||||||
|
setThirdPersonView(undefined);
|
||||||
|
} else {
|
||||||
|
viewer.trackedEntity = undefined;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const selectedEntity = viewer.selectedEntity;
|
||||||
|
if (viewFirstPerson === true) {
|
||||||
|
setFirstPersonView(selectedEntity);
|
||||||
|
} else {
|
||||||
|
const position = selectedEntity.position;
|
||||||
|
if (Cesium.defined(position)) {
|
||||||
|
setThirdPersonView(selectedEntity);
|
||||||
|
} else {
|
||||||
|
viewer.zoomTo(viewer.selectedEntity);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
viewer.selectedEntityChanged.addEventListener(function (selectedEntity) {
|
||||||
|
if (Cesium.defined(selectedEntity) && Cesium.defined(selectedEntity.id)) {
|
||||||
|
socket.send(JSON.stringify({ event: "selected", id: selectedEntity.id }));
|
||||||
|
// Calculate it's velocity for PFD
|
||||||
|
velocityVectorProperty = new Cesium.VelocityVectorProperty(selectedEntity.position, false);
|
||||||
|
} else {
|
||||||
|
socket.send(JSON.stringify({ event: "selected" }));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
viewer.trackedEntityChanged.addEventListener(function(trackedEntity) {
|
viewer.trackedEntityChanged.addEventListener(function (trackedEntity) {
|
||||||
if (Cesium.defined(trackedEntity) && Cesium.defined(trackedEntity.id)) {
|
if (Cesium.defined(trackedEntity) && Cesium.defined(trackedEntity.id)) {
|
||||||
socket.send(JSON.stringify({event: "tracking", id: trackedEntity.id}));
|
socket.send(JSON.stringify({ event: "tracking", id: trackedEntity.id }));
|
||||||
} else {
|
} else {
|
||||||
socket.send(JSON.stringify({event: "tracking"}));
|
socket.send(JSON.stringify({ event: "tracking" }));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -822,10 +1144,28 @@
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
Cesium.knockout.getObservable(viewer.clockViewModel, 'shouldAnimate').subscribe(function(isAnimating) {
|
// Use WASD keys to move camera
|
||||||
|
document.addEventListener('keydown', function (event) {
|
||||||
|
var amount = 0.5;
|
||||||
|
if (event.key == 'w') {
|
||||||
|
viewer.camera.moveUp(amount);
|
||||||
|
} else if (event.key == 's') {
|
||||||
|
viewer.camera.moveDown(amount);
|
||||||
|
} else if (event.key == 'a') {
|
||||||
|
viewer.camera.moveLeft(amount);
|
||||||
|
} else if (event.key == 'd') {
|
||||||
|
viewer.camera.moveRight(amount);
|
||||||
|
} else if (event.key == 'q') {
|
||||||
|
viewer.camera.moveForward(amount);
|
||||||
|
} else if (event.key == 'e') {
|
||||||
|
viewer.camera.moveBackward(amount);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Cesium.knockout.getObservable(viewer.clockViewModel, 'shouldAnimate').subscribe(function (isAnimating) {
|
||||||
reportClock();
|
reportClock();
|
||||||
});
|
});
|
||||||
Cesium.knockout.getObservable(viewer.clockViewModel, 'multiplier').subscribe(function(multiplier) {
|
Cesium.knockout.getObservable(viewer.clockViewModel, 'multiplier').subscribe(function (multiplier) {
|
||||||
reportClock();
|
reportClock();
|
||||||
});
|
});
|
||||||
// This is called every frame, which is too fast, so instead use setInterval with 1 second period
|
// This is called every frame, which is too fast, so instead use setInterval with 1 second period
|
||||||
@ -843,6 +1183,166 @@
|
|||||||
|
|
||||||
</script>
|
</script>
|
||||||
</div>
|
</div>
|
||||||
|
<canvas id="pfdCanvas" width="1000" height="1000" style="border:1px solid #000000;" hidden>
|
||||||
|
Browser does not support canvas.
|
||||||
|
</canvas>
|
||||||
|
<style>
|
||||||
|
#pfdCanvas {
|
||||||
|
position: absolute;
|
||||||
|
width: 500px;
|
||||||
|
height: 500px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<script src="cockpit.js"></script>
|
||||||
|
<script>
|
||||||
|
|
||||||
|
// Position PFD in centre at bottom
|
||||||
|
const pfdCanvas = document.getElementById("pfdCanvas");
|
||||||
|
pfdCanvas.style.left = ((window.innerWidth / 2) - 250).toString() + "px";
|
||||||
|
pfdCanvas.style.top = (window.innerHeight - 500 - 30).toString() + "px";
|
||||||
|
if (pfdCanvas.style.width > window.innerWidth) {
|
||||||
|
pfdCanvas.style.width = Math.max(window.innerWidth, 250);
|
||||||
|
}
|
||||||
|
if (pfdCanvas.style.height > window.innerHeight) {
|
||||||
|
pfdCanvas.style.height = Math.max(window.innerHeight, 250);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getPropertyValue(entity, propertyName) {
|
||||||
|
const property = entity.properties[propertyName];
|
||||||
|
if (Cesium.defined(property)) {
|
||||||
|
return property.getValue(viewer.clock.currentTime);
|
||||||
|
} else {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Only valid for TimeIntervalCollection properties
|
||||||
|
function getTimerIntervalPropertyValueAt(entity, propertyName, time) {
|
||||||
|
var value = undefined;
|
||||||
|
const property = entity.properties[propertyName];
|
||||||
|
|
||||||
|
if (Cesium.defined(property)) {
|
||||||
|
value = property.getValue(time);
|
||||||
|
if (!Cesium.defined(value)) {
|
||||||
|
value = property.intervals.get(0).data; // Get first available value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Only valid for SampledProperty properties
|
||||||
|
function getSampledPropertyValueAt(entity, propertyName, time) {
|
||||||
|
var value = undefined;
|
||||||
|
const property = entity.properties[propertyName];
|
||||||
|
|
||||||
|
if (Cesium.defined(property)) {
|
||||||
|
value = property.getValue(time);
|
||||||
|
if (!Cesium.defined(value)) {
|
||||||
|
value = property.getValue(property.getSample(0)); // Get first available value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
var pfdEntity;
|
||||||
|
var pfd60SecsAgo = new Cesium.JulianDate();
|
||||||
|
var pfdPrevClock;
|
||||||
|
var pfdRadioAltitude;
|
||||||
|
var pfdRadioAltitudeEntity;
|
||||||
|
var pfdRadioAltPosition;
|
||||||
|
|
||||||
|
function updatePFD() {
|
||||||
|
// Display PFD for last selected aircraft
|
||||||
|
const entity = viewer.selectedEntity;
|
||||||
|
if (Cesium.defined(entity) && Cesium.defined(entity.properties) && ((entity.properties.hasProperty("pfdAltitude") || entity.properties.hasProperty("pfdOnSurface")))) {
|
||||||
|
if (entity !== pfdEntity) {
|
||||||
|
pfdRadioAltitude = undefined;
|
||||||
|
}
|
||||||
|
pfdEntity = entity;
|
||||||
|
}
|
||||||
|
if (Cesium.defined(pfdEntity)) {
|
||||||
|
var callsign;
|
||||||
|
var aircraftType;
|
||||||
|
if (pfdEntity.properties.hasProperty("pfdCallsign")) {
|
||||||
|
callsign = pfdEntity.properties["pfdCallsign"].getValue();
|
||||||
|
} else {
|
||||||
|
callsign = "";
|
||||||
|
}
|
||||||
|
if (pfdEntity.properties.hasProperty("pfdAircraftType")) {
|
||||||
|
aircraftType = pfdEntity.properties["pfdAircraftType"].getValue();
|
||||||
|
} else {
|
||||||
|
aircraftType = "";
|
||||||
|
}
|
||||||
|
pfd60SecsAgo = Cesium.JulianDate.addSeconds(viewer.clock.currentTime, -60, pfd60SecsAgo);
|
||||||
|
const onSurface = pfdEntity.properties["pfdOnSurface"].getValue(viewer.clock.currentTime);
|
||||||
|
const wasOnSurface60SecsAgo = getTimerIntervalPropertyValueAt(pfdEntity, "pfdOnSurface", pfd60SecsAgo);
|
||||||
|
const indicatedAirspeed = getPropertyValue(pfdEntity, "pfdIndicatedAirspeed");
|
||||||
|
const trueAirspeed = getPropertyValue(pfdEntity, "pfdTrueAirspeed");
|
||||||
|
const groundspeed = getPropertyValue(pfdEntity, "pfdGroundspeed");
|
||||||
|
const mach = getPropertyValue(pfdEntity, "pfdMach");
|
||||||
|
const altitude = getPropertyValue(pfdEntity, "pfdAltitude");
|
||||||
|
var runwayAltitudeEstimate = undefined;
|
||||||
|
if ((onSurface === 0) && (wasOnSurface60SecsAgo > 0)) {
|
||||||
|
runwayAltitudeEstimate = getSampledPropertyValueAt(pfdEntity, "pfdAltitude", pfd60SecsAgo);
|
||||||
|
}
|
||||||
|
const qnh = getPropertyValue(pfdEntity, "pfdQNH");
|
||||||
|
const verticalSpeed = getPropertyValue(pfdEntity, "pfdVerticalSpeed");
|
||||||
|
const heading = getPropertyValue(pfdEntity, "pfdHeading");
|
||||||
|
const track = getPropertyValue(pfdEntity, "pfdTrack");
|
||||||
|
const roll = getPropertyValue(pfdEntity, "pfdRoll");
|
||||||
|
const selectedAltitude = getPropertyValue(pfdEntity, "pfdSelectedAltitude");
|
||||||
|
const selectedHeading = getPropertyValue(pfdEntity, "pfdSelectedHeading");
|
||||||
|
const autopilot = getPropertyValue(pfdEntity, "pfdAutopilot");
|
||||||
|
const verticalMode = getPropertyValue(pfdEntity, "pfdVerticalMode");
|
||||||
|
const lateralMode = getPropertyValue(pfdEntity, "pfdLateralMode");
|
||||||
|
const tcasMode = getPropertyValue(pfdEntity, "pfdTCASMode");
|
||||||
|
const windSpeed = getPropertyValue(pfdEntity, "pfdWindSpeed");
|
||||||
|
const windDirection = getPropertyValue(pfdEntity, "pfdWindDirection");
|
||||||
|
const staticAirTemperature = getPropertyValue(pfdEntity, "pfdStaticAirTemperature");
|
||||||
|
|
||||||
|
velocityVectorProperty.getValue(viewer.clock.currentTime, velocityVector);
|
||||||
|
const modelSpeedMps = Cesium.Cartesian3.magnitude(velocityVector);
|
||||||
|
const modelSpeedKnots = Math.round(modelSpeedMps * 1.944);
|
||||||
|
|
||||||
|
// Is the clock moving forwards
|
||||||
|
const forward = pfdPrevClock === undefined ? true : Cesium.JulianDate.compare(viewer.clock.currentTime, pfdPrevClock) > 0;
|
||||||
|
|
||||||
|
setPFDData(forward, pfdEntity.id, callsign, aircraftType, onSurface, wasOnSurface60SecsAgo, runwayAltitudeEstimate,
|
||||||
|
modelSpeedKnots, indicatedAirspeed, trueAirspeed, groundspeed, mach, altitude, pfdRadioAltitude, qnh, verticalSpeed, heading, track, roll,
|
||||||
|
selectedAltitude, selectedHeading, autopilot, verticalMode, lateralMode, tcasMode,
|
||||||
|
windSpeed, windDirection, staticAirTemperature
|
||||||
|
);
|
||||||
|
}
|
||||||
|
drawPFD();
|
||||||
|
pfdPrevClock = Cesium.JulianDate.clone(viewer.clock.currentTime, pfdPrevClock);
|
||||||
|
}
|
||||||
|
|
||||||
|
function convertToNearest10Foot(metres) {
|
||||||
|
return Math.round((metres * 3.28084) / 10.0) * 10.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateRadioAlt() {
|
||||||
|
if (Cesium.defined(pfdEntity)) {
|
||||||
|
pfdRadioAltitudeEntity = pfdEntity;
|
||||||
|
pfdRadioAltPosition = pfdRadioAltitudeEntity.position.getValue(viewer.clock.currentTime, pfdRadioAltPosition);
|
||||||
|
if (Cesium.defined(pfdRadioAltPosition)) {
|
||||||
|
if (viewer.terrainProvider instanceof Cesium.EllipsoidTerrainProvider) {
|
||||||
|
pfdRadioAltitude = convertToNearest10Foot(Cesium.Cartographic.fromCartesian(pfdRadioAltPosition).height);
|
||||||
|
} else {
|
||||||
|
Promise.resolve(
|
||||||
|
Cesium.sampleTerrainMostDetailed(viewer.terrainProvider, [pfdRadioAltPosition]),
|
||||||
|
).then((updatedPositions) => {
|
||||||
|
if (pfdRadioAltitudeEntity === pfdEntity) {
|
||||||
|
pfdRadioAltitude = convertToNearest10Foot(Cesium.Cartographic.fromCartesian(updatedPositions[0]).height);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
||||||
|
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://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("http://webglreport.com/")));
|
||||||
//ui->web->load(QUrl(QString("https://sandcastle.cesium.com/")));
|
//ui->web->load(QUrl(QString("https://sandcastle.cesium.com/")));
|
||||||
//ui->web->load(QUrl("chrome://gpu/"));
|
|
||||||
ui->web->show();
|
ui->web->show();
|
||||||
}
|
}
|
||||||
else if (!m_settings.m_map3DEnabled && (m_cesium != nullptr))
|
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->setCameraReferenceFrame(m_settings.m_eciCamera);
|
||||||
m_cesium->setAntiAliasing(m_settings.m_fxaa, m_settings.m_msaa);
|
m_cesium->setAntiAliasing(m_settings.m_fxaa, m_settings.m_msaa);
|
||||||
m_cesium->getDateTime();
|
m_cesium->getDateTime();
|
||||||
|
m_cesium->setViewFirstPerson(m_settings.m_viewFirstPerson);
|
||||||
m_cesium->setHDR(m_settings.m_hdr);
|
m_cesium->setHDR(m_settings.m_hdr);
|
||||||
m_cesium->setFog(m_settings.m_fog);
|
m_cesium->setFog(m_settings.m_fog);
|
||||||
m_cesium->showFPS(m_settings.m_fps);
|
m_cesium->showFPS(m_settings.m_fps);
|
||||||
|
m_cesium->showPFD(m_settings.m_displayPFD);
|
||||||
m_cesium->showMUF(m_settings.m_displayMUF);
|
m_cesium->showMUF(m_settings.m_displayMUF);
|
||||||
m_cesium->showfoF2(m_settings.m_displayfoF2);
|
m_cesium->showfoF2(m_settings.m_displayfoF2);
|
||||||
m_cesium->showMagDec(m_settings.m_displayMagDec);
|
m_cesium->showMagDec(m_settings.m_displayMagDec);
|
||||||
@ -1968,10 +1969,13 @@ void MapGUI::applyMap3DSettings(bool reloadMap)
|
|||||||
m_polylineMapModel.allUpdated();
|
m_polylineMapModel.allUpdated();
|
||||||
}
|
}
|
||||||
MapSettings::MapItemSettings *ionosondeItemSettings = getItemSettings("Ionosonde Stations");
|
MapSettings::MapItemSettings *ionosondeItemSettings = getItemSettings("Ionosonde Stations");
|
||||||
|
if (m_giro)
|
||||||
|
{
|
||||||
m_giro->getIndexPeriodically((m_settings.m_displayMUF || m_settings.m_displayfoF2) ? 15 : 0);
|
m_giro->getIndexPeriodically((m_settings.m_displayMUF || m_settings.m_displayfoF2) ? 15 : 0);
|
||||||
if (ionosondeItemSettings) {
|
if (ionosondeItemSettings) {
|
||||||
m_giro->getDataPeriodically(ionosondeItemSettings->m_enabled ? 2 : 0);
|
m_giro->getDataPeriodically(ionosondeItemSettings->m_enabled ? 2 : 0);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
if (m_aurora) {
|
if (m_aurora) {
|
||||||
m_aurora->getDataPeriodically(m_settings.m_displayAurora ? 30 : 0);
|
m_aurora->getDataPeriodically(m_settings.m_displayAurora ? 30 : 0);
|
||||||
}
|
}
|
||||||
@ -2084,6 +2088,8 @@ void MapGUI::displaySettings()
|
|||||||
setTitle(m_settings.m_title);
|
setTitle(m_settings.m_title);
|
||||||
blockApplySettings(true);
|
blockApplySettings(true);
|
||||||
ui->displayNames->setChecked(m_settings.m_displayNames);
|
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->displaySelectedGroundTracks->setChecked(m_settings.m_displaySelectedGroundTracks);
|
||||||
ui->displayAllGroundTracks->setChecked(m_settings.m_displayAllGroundTracks);
|
ui->displayAllGroundTracks->setChecked(m_settings.m_displayAllGroundTracks);
|
||||||
ui->displayRain->setChecked(m_settings.m_displayRain);
|
ui->displayRain->setChecked(m_settings.m_displayRain);
|
||||||
@ -2202,6 +2208,24 @@ void MapGUI::on_maidenhead_clicked()
|
|||||||
dialog.exec();
|
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)
|
void MapGUI::on_displayNames_clicked(bool checked)
|
||||||
{
|
{
|
||||||
m_settings.m_displayNames = checked;
|
m_settings.m_displayNames = checked;
|
||||||
@ -2415,10 +2439,10 @@ void MapGUI::on_displayMUF_clicked(bool checked)
|
|||||||
m_displayMUF->setChecked(checked);
|
m_displayMUF->setChecked(checked);
|
||||||
}
|
}
|
||||||
m_settings.m_displayMUF = checked;
|
m_settings.m_displayMUF = checked;
|
||||||
// Only call show if disabling, so we don't get two updates
|
if (m_giro) {
|
||||||
// (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);
|
m_giro->getIndexPeriodically((m_settings.m_displayMUF || m_settings.m_displayfoF2) ? 15 : 0);
|
||||||
if (m_cesium && !m_settings.m_displayMUF) {
|
}
|
||||||
|
if (m_cesium) {
|
||||||
m_cesium->showMUF(m_settings.m_displayMUF);
|
m_cesium->showMUF(m_settings.m_displayMUF);
|
||||||
}
|
}
|
||||||
applySetting("displayMUF");
|
applySetting("displayMUF");
|
||||||
@ -2433,8 +2457,10 @@ void MapGUI::on_displayfoF2_clicked(bool checked)
|
|||||||
m_displayfoF2->setChecked(checked);
|
m_displayfoF2->setChecked(checked);
|
||||||
}
|
}
|
||||||
m_settings.m_displayfoF2 = checked;
|
m_settings.m_displayfoF2 = checked;
|
||||||
|
if (m_giro) {
|
||||||
m_giro->getIndexPeriodically((m_settings.m_displayMUF || m_settings.m_displayfoF2) ? 15 : 0);
|
m_giro->getIndexPeriodically((m_settings.m_displayMUF || m_settings.m_displayfoF2) ? 15 : 0);
|
||||||
if (m_cesium && !m_settings.m_displayfoF2) {
|
}
|
||||||
|
if (m_cesium) {
|
||||||
m_cesium->showfoF2(m_settings.m_displayfoF2);
|
m_cesium->showfoF2(m_settings.m_displayfoF2);
|
||||||
}
|
}
|
||||||
applySetting("displayfoF2");
|
applySetting("displayfoF2");
|
||||||
@ -3009,6 +3035,8 @@ void MapGUI::preferenceChanged(int elementType)
|
|||||||
void MapGUI::makeUIConnections()
|
void MapGUI::makeUIConnections()
|
||||||
{
|
{
|
||||||
QObject::connect(ui->displayNames, &ButtonSwitch::clicked, this, &MapGUI::on_displayNames_clicked);
|
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->displayAllGroundTracks, &ButtonSwitch::clicked, this, &MapGUI::on_displayAllGroundTracks_clicked);
|
||||||
QObject::connect(ui->displaySelectedGroundTracks, &ButtonSwitch::clicked, this, &MapGUI::on_displaySelectedGroundTracks_clicked);
|
QObject::connect(ui->displaySelectedGroundTracks, &ButtonSwitch::clicked, this, &MapGUI::on_displaySelectedGroundTracks_clicked);
|
||||||
QObject::connect(ui->displayRain, &ButtonSwitch::clicked, this, &MapGUI::on_displayRain_clicked);
|
QObject::connect(ui->displayRain, &ButtonSwitch::clicked, this, &MapGUI::on_displayRain_clicked);
|
||||||
|
@ -326,6 +326,8 @@ private slots:
|
|||||||
void onMenuDialogCalled(const QPoint &p);
|
void onMenuDialogCalled(const QPoint &p);
|
||||||
void onWidgetRolled(QWidget* widget, bool rollDown);
|
void onWidgetRolled(QWidget* widget, bool rollDown);
|
||||||
void handleInputMessages();
|
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_displayNames_clicked(bool checked=false);
|
||||||
void on_displayAllGroundTracks_clicked(bool checked=false);
|
void on_displayAllGroundTracks_clicked(bool checked=false);
|
||||||
void on_displaySelectedGroundTracks_clicked(bool checked=false);
|
void on_displaySelectedGroundTracks_clicked(bool checked=false);
|
||||||
|
@ -29,7 +29,7 @@
|
|||||||
</font>
|
</font>
|
||||||
</property>
|
</property>
|
||||||
<property name="focusPolicy">
|
<property name="focusPolicy">
|
||||||
<enum>Qt::StrongFocus</enum>
|
<enum>Qt::FocusPolicy::StrongFocus</enum>
|
||||||
</property>
|
</property>
|
||||||
<property name="windowTitle">
|
<property name="windowTitle">
|
||||||
<string>Map</string>
|
<string>Map</string>
|
||||||
@ -39,7 +39,7 @@
|
|||||||
<rect>
|
<rect>
|
||||||
<x>0</x>
|
<x>0</x>
|
||||||
<y>0</y>
|
<y>0</y>
|
||||||
<width>1221</width>
|
<width>1251</width>
|
||||||
<height>41</height>
|
<height>41</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
@ -166,7 +166,7 @@
|
|||||||
<string>IBP</string>
|
<string>IBP</string>
|
||||||
</property>
|
</property>
|
||||||
<property name="icon">
|
<property name="icon">
|
||||||
<iconset resource="icons.qrc">
|
<iconset resource="mapicons.qrc">
|
||||||
<normaloff>:/map/icons/ibp.png</normaloff>:/map/icons/ibp.png</iconset>
|
<normaloff>:/map/icons/ibp.png</normaloff>:/map/icons/ibp.png</iconset>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
@ -180,7 +180,7 @@
|
|||||||
<string/>
|
<string/>
|
||||||
</property>
|
</property>
|
||||||
<property name="icon">
|
<property name="icon">
|
||||||
<iconset resource="icons.qrc">
|
<iconset resource="mapicons.qrc">
|
||||||
<normaloff>:/map/icons/clock.png</normaloff>:/map/icons/clock.png</iconset>
|
<normaloff>:/map/icons/clock.png</normaloff>:/map/icons/clock.png</iconset>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
@ -191,11 +191,11 @@
|
|||||||
<string/>
|
<string/>
|
||||||
</property>
|
</property>
|
||||||
<property name="icon">
|
<property name="icon">
|
||||||
<iconset resource="icons.qrc">
|
<iconset resource="mapicons.qrc">
|
||||||
<normaloff>:/map/icons/layers.png</normaloff>:/map/icons/layers.png</iconset>
|
<normaloff>:/map/icons/layers.png</normaloff>:/map/icons/layers.png</iconset>
|
||||||
</property>
|
</property>
|
||||||
<property name="popupMode">
|
<property name="popupMode">
|
||||||
<enum>QToolButton::InstantPopup</enum>
|
<enum>QToolButton::ToolButtonPopupMode::InstantPopup</enum>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
@ -205,10 +205,10 @@
|
|||||||
<string>Display satellite infra-red (clouds)</string>
|
<string>Display satellite infra-red (clouds)</string>
|
||||||
</property>
|
</property>
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>^</string>
|
<string/>
|
||||||
</property>
|
</property>
|
||||||
<property name="icon">
|
<property name="icon">
|
||||||
<iconset resource="icons.qrc">
|
<iconset resource="mapicons.qrc">
|
||||||
<normaloff>:/map/icons/cloud.png</normaloff>:/map/icons/cloud.png</iconset>
|
<normaloff>:/map/icons/cloud.png</normaloff>:/map/icons/cloud.png</iconset>
|
||||||
</property>
|
</property>
|
||||||
<property name="checkable">
|
<property name="checkable">
|
||||||
@ -225,10 +225,10 @@
|
|||||||
<string>Display weather radar (rain/snow)</string>
|
<string>Display weather radar (rain/snow)</string>
|
||||||
</property>
|
</property>
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>^</string>
|
<string/>
|
||||||
</property>
|
</property>
|
||||||
<property name="icon">
|
<property name="icon">
|
||||||
<iconset resource="icons.qrc">
|
<iconset resource="mapicons.qrc">
|
||||||
<normaloff>:/map/icons/precipitation.png</normaloff>:/map/icons/precipitation.png</iconset>
|
<normaloff>:/map/icons/precipitation.png</normaloff>:/map/icons/precipitation.png</iconset>
|
||||||
</property>
|
</property>
|
||||||
<property name="checkable">
|
<property name="checkable">
|
||||||
@ -245,10 +245,10 @@
|
|||||||
<string>Display sea marks</string>
|
<string>Display sea marks</string>
|
||||||
</property>
|
</property>
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>^</string>
|
<string/>
|
||||||
</property>
|
</property>
|
||||||
<property name="icon">
|
<property name="icon">
|
||||||
<iconset resource="icons.qrc">
|
<iconset resource="mapicons.qrc">
|
||||||
<normaloff>:/map/icons/anchor.png</normaloff>:/map/icons/anchor.png</iconset>
|
<normaloff>:/map/icons/anchor.png</normaloff>:/map/icons/anchor.png</iconset>
|
||||||
</property>
|
</property>
|
||||||
<property name="checkable">
|
<property name="checkable">
|
||||||
@ -265,10 +265,10 @@
|
|||||||
<string>Display railways</string>
|
<string>Display railways</string>
|
||||||
</property>
|
</property>
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>^</string>
|
<string/>
|
||||||
</property>
|
</property>
|
||||||
<property name="icon">
|
<property name="icon">
|
||||||
<iconset resource="icons.qrc">
|
<iconset resource="mapicons.qrc">
|
||||||
<normaloff>:/map/icons/railway.png</normaloff>:/map/icons/railway.png</iconset>
|
<normaloff>:/map/icons/railway.png</normaloff>:/map/icons/railway.png</iconset>
|
||||||
</property>
|
</property>
|
||||||
<property name="checkable">
|
<property name="checkable">
|
||||||
@ -285,10 +285,10 @@
|
|||||||
<string>Display MUF (Maximum Usable Frequency) contours (3D only)</string>
|
<string>Display MUF (Maximum Usable Frequency) contours (3D only)</string>
|
||||||
</property>
|
</property>
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>^</string>
|
<string/>
|
||||||
</property>
|
</property>
|
||||||
<property name="icon">
|
<property name="icon">
|
||||||
<iconset resource="icons.qrc">
|
<iconset resource="mapicons.qrc">
|
||||||
<normaloff>:/map/icons/muf.png</normaloff>:/map/icons/muf.png</iconset>
|
<normaloff>:/map/icons/muf.png</normaloff>:/map/icons/muf.png</iconset>
|
||||||
</property>
|
</property>
|
||||||
<property name="checkable">
|
<property name="checkable">
|
||||||
@ -305,10 +305,10 @@
|
|||||||
<string>Display foF2 (F2 layer critical frequency) contours (3D only)</string>
|
<string>Display foF2 (F2 layer critical frequency) contours (3D only)</string>
|
||||||
</property>
|
</property>
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>^</string>
|
<string/>
|
||||||
</property>
|
</property>
|
||||||
<property name="icon">
|
<property name="icon">
|
||||||
<iconset resource="icons.qrc">
|
<iconset resource="mapicons.qrc">
|
||||||
<normaloff>:/map/icons/fof2.png</normaloff>:/map/icons/fof2.png</iconset>
|
<normaloff>:/map/icons/fof2.png</normaloff>:/map/icons/fof2.png</iconset>
|
||||||
</property>
|
</property>
|
||||||
<property name="checkable">
|
<property name="checkable">
|
||||||
@ -365,10 +365,10 @@
|
|||||||
<string>Display NASA GIBS data</string>
|
<string>Display NASA GIBS data</string>
|
||||||
</property>
|
</property>
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>^</string>
|
<string/>
|
||||||
</property>
|
</property>
|
||||||
<property name="icon">
|
<property name="icon">
|
||||||
<iconset resource="icons.qrc">
|
<iconset resource="mapicons.qrc">
|
||||||
<normaloff>:/map/icons/earthsat.png</normaloff>:/map/icons/earthsat.png</iconset>
|
<normaloff>:/map/icons/earthsat.png</normaloff>:/map/icons/earthsat.png</iconset>
|
||||||
</property>
|
</property>
|
||||||
<property name="checkable">
|
<property name="checkable">
|
||||||
@ -430,7 +430,7 @@
|
|||||||
<string>Display names</string>
|
<string>Display names</string>
|
||||||
</property>
|
</property>
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>^</string>
|
<string/>
|
||||||
</property>
|
</property>
|
||||||
<property name="icon">
|
<property name="icon">
|
||||||
<iconset resource="../../../sdrgui/resources/res.qrc">
|
<iconset resource="../../../sdrgui/resources/res.qrc">
|
||||||
@ -444,13 +444,54 @@
|
|||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</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>
|
<item>
|
||||||
<widget class="ButtonSwitch" name="displaySelectedGroundTracks">
|
<widget class="ButtonSwitch" name="displaySelectedGroundTracks">
|
||||||
<property name="toolTip">
|
<property name="toolTip">
|
||||||
<string>Display ground tracks for selected item</string>
|
<string>Display ground tracks for selected item</string>
|
||||||
</property>
|
</property>
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>^</string>
|
<string/>
|
||||||
</property>
|
</property>
|
||||||
<property name="icon">
|
<property name="icon">
|
||||||
<iconset resource="../../../sdrgui/resources/res.qrc">
|
<iconset resource="../../../sdrgui/resources/res.qrc">
|
||||||
@ -470,10 +511,10 @@
|
|||||||
<string>Display all ground tracks</string>
|
<string>Display all ground tracks</string>
|
||||||
</property>
|
</property>
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>^</string>
|
<string/>
|
||||||
</property>
|
</property>
|
||||||
<property name="icon">
|
<property name="icon">
|
||||||
<iconset resource="icons.qrc">
|
<iconset resource="mapicons.qrc">
|
||||||
<normaloff>:/map/icons/groundtracks.png</normaloff>:/map/icons/groundtracks.png</iconset>
|
<normaloff>:/map/icons/groundtracks.png</normaloff>:/map/icons/groundtracks.png</iconset>
|
||||||
</property>
|
</property>
|
||||||
<property name="checkable">
|
<property name="checkable">
|
||||||
@ -573,7 +614,7 @@
|
|||||||
</sizepolicy>
|
</sizepolicy>
|
||||||
</property>
|
</property>
|
||||||
<property name="orientation">
|
<property name="orientation">
|
||||||
<enum>Qt::Vertical</enum>
|
<enum>Qt::Orientation::Vertical</enum>
|
||||||
</property>
|
</property>
|
||||||
<widget class="QQuickWidget" name="map">
|
<widget class="QQuickWidget" name="map">
|
||||||
<property name="sizePolicy">
|
<property name="sizePolicy">
|
||||||
@ -592,7 +633,7 @@
|
|||||||
<string>Map</string>
|
<string>Map</string>
|
||||||
</property>
|
</property>
|
||||||
<property name="resizeMode">
|
<property name="resizeMode">
|
||||||
<enum>QQuickWidget::SizeRootObjectToView</enum>
|
<enum>QQuickWidget::ResizeMode::SizeRootObjectToView</enum>
|
||||||
</property>
|
</property>
|
||||||
<property name="source">
|
<property name="source">
|
||||||
<url>
|
<url>
|
||||||
@ -655,7 +696,7 @@
|
|||||||
</tabstops>
|
</tabstops>
|
||||||
<resources>
|
<resources>
|
||||||
<include location="../../../sdrgui/resources/res.qrc"/>
|
<include location="../../../sdrgui/resources/res.qrc"/>
|
||||||
<include location="icons.qrc"/>
|
<include location="mapicons.qrc"/>
|
||||||
</resources>
|
</resources>
|
||||||
<connections/>
|
<connections/>
|
||||||
</ui>
|
</ui>
|
||||||
|
@ -15,6 +15,7 @@
|
|||||||
<file>icons/waypoints.png</file>
|
<file>icons/waypoints.png</file>
|
||||||
<file>icons/earthsat.png</file>
|
<file>icons/earthsat.png</file>
|
||||||
<file>icons/aurora.png</file>
|
<file>icons/aurora.png</file>
|
||||||
|
<file>icons/pfd.png</file>
|
||||||
<file>icons/compass.png</file>
|
<file>icons/compass.png</file>
|
||||||
<file>icons/grid.png</file>
|
<file>icons/grid.png</file>
|
||||||
<file>icons/thirdperson.png</file>
|
<file>icons/thirdperson.png</file>
|
||||||
|
@ -34,6 +34,11 @@ void MapItem::update(SWGSDRangel::SWGMapItem *mapItem)
|
|||||||
} else {
|
} else {
|
||||||
m_label = "";
|
m_label = "";
|
||||||
}
|
}
|
||||||
|
if (mapItem->getLabelDateTime()) {
|
||||||
|
m_labelDateTime = QDateTime::fromString(*mapItem->getLabelDateTime(), Qt::ISODateWithMs);
|
||||||
|
} else {
|
||||||
|
m_labelDateTime = QDateTime();
|
||||||
|
}
|
||||||
m_latitude = mapItem->getLatitude();
|
m_latitude = mapItem->getLatitude();
|
||||||
m_longitude = mapItem->getLongitude();
|
m_longitude = mapItem->getLongitude();
|
||||||
m_altitude = mapItem->getAltitude();
|
m_altitude = mapItem->getAltitude();
|
||||||
@ -47,6 +52,8 @@ QGeoCoordinate MapItem::getCoordinates()
|
|||||||
return coords;
|
return coords;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
WhittakerEilers ObjectMapItem::m_filter;
|
||||||
|
|
||||||
void ObjectMapItem::update(SWGSDRangel::SWGMapItem *mapItem)
|
void ObjectMapItem::update(SWGSDRangel::SWGMapItem *mapItem)
|
||||||
{
|
{
|
||||||
MapItem::update(mapItem);
|
MapItem::update(mapItem);
|
||||||
@ -55,6 +62,11 @@ void ObjectMapItem::update(SWGSDRangel::SWGMapItem *mapItem)
|
|||||||
} else {
|
} else {
|
||||||
m_positionDateTime = QDateTime();
|
m_positionDateTime = QDateTime();
|
||||||
}
|
}
|
||||||
|
if (mapItem->getAltitudeDateTime()) {
|
||||||
|
m_altitudeDateTime = QDateTime::fromString(*mapItem->getAltitudeDateTime(), Qt::ISODateWithMs);
|
||||||
|
} else {
|
||||||
|
m_altitudeDateTime = QDateTime();
|
||||||
|
}
|
||||||
m_useHeadingPitchRoll = mapItem->getOrientation() == 1;
|
m_useHeadingPitchRoll = mapItem->getOrientation() == 1;
|
||||||
m_heading = mapItem->getHeading();
|
m_heading = mapItem->getHeading();
|
||||||
m_pitch = mapItem->getPitch();
|
m_pitch = mapItem->getPitch();
|
||||||
@ -79,6 +91,14 @@ void ObjectMapItem::update(SWGSDRangel::SWGMapItem *mapItem)
|
|||||||
}
|
}
|
||||||
m_labelAltitudeOffset = mapItem->getLabelAltitudeOffset();
|
m_labelAltitudeOffset = mapItem->getLabelAltitudeOffset();
|
||||||
m_modelAltitudeOffset = mapItem->getModelAltitudeOffset();
|
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_altitudeReference = mapItem->getAltitudeReference();
|
||||||
m_fixedPosition = mapItem->getFixedPosition();
|
m_fixedPosition = mapItem->getFixedPosition();
|
||||||
QList<SWGSDRangel::SWGMapAnimation *> *animations = mapItem->getAnimations();
|
QList<SWGSDRangel::SWGMapAnimation *> *animations = mapItem->getAnimations();
|
||||||
@ -91,7 +111,7 @@ void ObjectMapItem::update(SWGSDRangel::SWGMapItem *mapItem)
|
|||||||
findFrequencies();
|
findFrequencies();
|
||||||
if (!m_fixedPosition)
|
if (!m_fixedPosition)
|
||||||
{
|
{
|
||||||
updateTrack(mapItem->getTrack());
|
updateTrack(mapItem->getTrack(), m_itemSettings);
|
||||||
updatePredictedTrack(mapItem->getPredictedTrack());
|
updatePredictedTrack(mapItem->getPredictedTrack());
|
||||||
}
|
}
|
||||||
if (mapItem->getAvailableFrom()) {
|
if (mapItem->getAvailableFrom()) {
|
||||||
@ -104,6 +124,47 @@ void ObjectMapItem::update(SWGSDRangel::SWGMapItem *mapItem)
|
|||||||
} else {
|
} else {
|
||||||
m_availableUntil = QDateTime();
|
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)
|
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)
|
if (track != nullptr)
|
||||||
{
|
{
|
||||||
@ -233,6 +432,8 @@ void ObjectMapItem::updateTrack(QList<SWGSDRangel::SWGMapCoordinate *> *track)
|
|||||||
m_takenTrackCoords.clear();
|
m_takenTrackCoords.clear();
|
||||||
qDeleteAll(m_takenTrackDateTimes);
|
qDeleteAll(m_takenTrackDateTimes);
|
||||||
m_takenTrackDateTimes.clear();
|
m_takenTrackDateTimes.clear();
|
||||||
|
m_takenTrackPositionExtrapolated.clear();
|
||||||
|
m_takenTrackAltitudeExtrapolated.clear();
|
||||||
m_takenTrack.clear();
|
m_takenTrack.clear();
|
||||||
m_takenTrack1.clear();
|
m_takenTrack1.clear();
|
||||||
m_takenTrack2.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));
|
QDateTime *d = new QDateTime(QDateTime::fromString(*p->getDateTime(), Qt::ISODate));
|
||||||
m_takenTrackCoords.push_back(c);
|
m_takenTrackCoords.push_back(c);
|
||||||
m_takenTrackDateTimes.push_back(d);
|
m_takenTrackDateTimes.push_back(d);
|
||||||
|
m_takenTrackPositionExtrapolated.push_back(false);
|
||||||
|
m_takenTrackAltitudeExtrapolated.push_back(false);
|
||||||
m_takenTrack.push_back(QVariant::fromValue(*c));
|
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);
|
QGeoCoordinate *c = new QGeoCoordinate(m_latitude, m_longitude, m_altitude);
|
||||||
m_takenTrackCoords.push_back(c);
|
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));
|
m_takenTrackDateTimes.push_back(new QDateTime(m_positionDateTime));
|
||||||
} else {
|
} else {
|
||||||
m_takenTrackDateTimes.push_back(new QDateTime(QDateTime::currentDateTime()));
|
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));
|
m_takenTrack.push_back(QVariant::fromValue(*c));
|
||||||
}
|
}
|
||||||
else
|
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();
|
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);
|
if (interpolate)
|
||||||
m_takenTrackCoords.push_back(c);
|
{
|
||||||
if (m_positionDateTime.isValid()) {
|
for (int i = m_takenTrackAltitudeExtrapolated.size() - 1; (i >= 0) && m_takenTrackAltitudeExtrapolated[i]; i--)
|
||||||
m_takenTrackDateTimes.push_back(new QDateTime(m_positionDateTime));
|
{
|
||||||
} else {
|
interpolateAltitude(i, m_altitude, m_altitudeDateTime);
|
||||||
m_takenTrackDateTimes.push_back(new QDateTime(QDateTime::currentDateTime()));
|
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 "mapsettings.h"
|
||||||
#include "cesiuminterface.h"
|
#include "cesiuminterface.h"
|
||||||
|
#include "util/whittakereilers.h"
|
||||||
|
#include "mapaircraftstate.h"
|
||||||
|
|
||||||
#include "SWGMapItem.h"
|
#include "SWGMapItem.h"
|
||||||
|
|
||||||
@ -58,6 +60,7 @@ protected:
|
|||||||
|
|
||||||
QString m_name; // Unique id
|
QString m_name; // Unique id
|
||||||
QString m_label;
|
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_latitude; // Position for label
|
||||||
float m_longitude;
|
float m_longitude;
|
||||||
float m_altitude; // In metres
|
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
|
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
|
// Information required about each item displayed on the map
|
||||||
class ObjectMapItem : public MapItem {
|
class ObjectMapItem : public MapItem {
|
||||||
|
|
||||||
public:
|
public:
|
||||||
ObjectMapItem(const QObject *sourcePipe, const QString &group, MapSettings::MapItemSettings *itemSettings, SWGSDRangel::SWGMapItem *mapItem) :
|
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);
|
update(mapItem);
|
||||||
}
|
}
|
||||||
@ -79,12 +82,17 @@ public:
|
|||||||
|
|
||||||
protected:
|
protected:
|
||||||
void findFrequencies();
|
void findFrequencies();
|
||||||
void updateTrack(QList<SWGSDRangel::SWGMapCoordinate *> *track);
|
void updateTrack(QList<SWGSDRangel::SWGMapCoordinate *> *track, MapSettings::MapItemSettings *itemSettings);
|
||||||
void updatePredictedTrack(QList<SWGSDRangel::SWGMapCoordinate *> *track);
|
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 ObjectMapModel;
|
||||||
friend CZML;
|
friend CZML;
|
||||||
QDateTime m_positionDateTime;
|
QDateTime m_positionDateTime;
|
||||||
|
QDateTime m_altitudeDateTime;
|
||||||
bool m_useHeadingPitchRoll;
|
bool m_useHeadingPitchRoll;
|
||||||
float m_heading;
|
float m_heading;
|
||||||
float m_pitch;
|
float m_pitch;
|
||||||
@ -107,6 +115,8 @@ protected:
|
|||||||
QGeoCoordinate m_predictedEnd2;
|
QGeoCoordinate m_predictedEnd2;
|
||||||
QList<QGeoCoordinate *> m_takenTrackCoords;
|
QList<QGeoCoordinate *> m_takenTrackCoords;
|
||||||
QList<QDateTime *> m_takenTrackDateTimes;
|
QList<QDateTime *> m_takenTrackDateTimes;
|
||||||
|
QList<bool> m_takenTrackPositionExtrapolated;
|
||||||
|
QList<bool> m_takenTrackAltitudeExtrapolated;
|
||||||
QVariantList m_takenTrack; // Line showing where the object has been
|
QVariantList m_takenTrack; // Line showing where the object has been
|
||||||
QVariantList m_takenTrack1;
|
QVariantList m_takenTrack1;
|
||||||
QVariantList m_takenTrack2;
|
QVariantList m_takenTrack2;
|
||||||
@ -114,6 +124,8 @@ protected:
|
|||||||
QGeoCoordinate m_takenStart2;
|
QGeoCoordinate m_takenStart2;
|
||||||
QGeoCoordinate m_takenEnd1;
|
QGeoCoordinate m_takenEnd1;
|
||||||
QGeoCoordinate m_takenEnd2;
|
QGeoCoordinate m_takenEnd2;
|
||||||
|
QList<QGeoCoordinate *> m_interpolatedCoords;
|
||||||
|
QList<QDateTime *> m_interpolatedDateTimes;
|
||||||
|
|
||||||
// For 3D map
|
// For 3D map
|
||||||
QString m_model;
|
QString m_model;
|
||||||
@ -121,6 +133,9 @@ protected:
|
|||||||
float m_labelAltitudeOffset;
|
float m_labelAltitudeOffset;
|
||||||
float m_modelAltitudeOffset;
|
float m_modelAltitudeOffset;
|
||||||
QList<CesiumInterface::Animation *> m_animations;
|
QList<CesiumInterface::Animation *> m_animations;
|
||||||
|
MapAircraftState *m_aircraftState;
|
||||||
|
|
||||||
|
static WhittakerEilers m_filter; // For smoothing/interpolating position
|
||||||
};
|
};
|
||||||
|
|
||||||
class PolygonMapItem : public MapItem {
|
class PolygonMapItem : public MapItem {
|
||||||
|
@ -269,7 +269,6 @@ QByteArray MapSettings::serialize() const
|
|||||||
s.writeBool(29, m_sunLightEnabled);
|
s.writeBool(29, m_sunLightEnabled);
|
||||||
s.writeBool(30, m_eciCamera);
|
s.writeBool(30, m_eciCamera);
|
||||||
s.writeString(31, m_cesiumIonAPIKey);
|
s.writeString(31, m_cesiumIonAPIKey);
|
||||||
s.writeString(32, m_antiAliasing);
|
|
||||||
s.writeS32(33, m_workspaceIndex);
|
s.writeS32(33, m_workspaceIndex);
|
||||||
s.writeBlob(34, m_geometryBytes);
|
s.writeBlob(34, m_geometryBytes);
|
||||||
|
|
||||||
@ -292,6 +291,9 @@ QByteArray MapSettings::serialize() const
|
|||||||
s.writeBool(48, m_displayMaidenheadGrid);
|
s.writeBool(48, m_displayMaidenheadGrid);
|
||||||
s.writeString(49, m_defaultImagery);
|
s.writeString(49, m_defaultImagery);
|
||||||
s.writeString(50, m_arcGISAPIKey);
|
s.writeString(50, m_arcGISAPIKey);
|
||||||
|
s.writeBool(51, m_displayPFD);
|
||||||
|
s.writeBool(52, m_viewFirstPerson);
|
||||||
|
|
||||||
s.writeBool(53, m_terrainLighting);
|
s.writeBool(53, m_terrainLighting);
|
||||||
s.writeBool(54, m_water);
|
s.writeBool(54, m_water);
|
||||||
s.writeBool(55, m_hdr);
|
s.writeBool(55, m_hdr);
|
||||||
@ -299,6 +301,7 @@ QByteArray MapSettings::serialize() const
|
|||||||
s.writeBool(57, m_fps);
|
s.writeBool(57, m_fps);
|
||||||
s.writeBool(58, m_fxaa);
|
s.writeBool(58, m_fxaa);
|
||||||
s.writeS32(59, m_msaa);
|
s.writeS32(59, m_msaa);
|
||||||
|
|
||||||
return s.final();
|
return s.final();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -369,7 +372,6 @@ bool MapSettings::deserialize(const QByteArray& data)
|
|||||||
d.readBool(29, &m_sunLightEnabled, true);
|
d.readBool(29, &m_sunLightEnabled, true);
|
||||||
d.readBool(30, &m_eciCamera, false);
|
d.readBool(30, &m_eciCamera, false);
|
||||||
d.readString(31, &m_cesiumIonAPIKey, "");
|
d.readString(31, &m_cesiumIonAPIKey, "");
|
||||||
d.readString(32, &m_antiAliasing, "None");
|
|
||||||
d.readS32(33, &m_workspaceIndex, 0);
|
d.readS32(33, &m_workspaceIndex, 0);
|
||||||
d.readBlob(34, &m_geometryBytes);
|
d.readBlob(34, &m_geometryBytes);
|
||||||
|
|
||||||
@ -391,6 +393,9 @@ bool MapSettings::deserialize(const QByteArray& data)
|
|||||||
d.readBool(48, &m_displayMaidenheadGrid, false);
|
d.readBool(48, &m_displayMaidenheadGrid, false);
|
||||||
d.readString(49, &m_defaultImagery, "Sentinel-2");
|
d.readString(49, &m_defaultImagery, "Sentinel-2");
|
||||||
d.readString(50, &m_arcGISAPIKey, "");
|
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(53, &m_terrainLighting, true);
|
||||||
d.readBool(54, &m_water, false);
|
d.readBool(54, &m_water, false);
|
||||||
d.readBool(55, &m_hdr, true);
|
d.readBool(55, &m_hdr, true);
|
||||||
@ -398,6 +403,7 @@ bool MapSettings::deserialize(const QByteArray& data)
|
|||||||
d.readBool(57, &m_fps, false);
|
d.readBool(57, &m_fps, false);
|
||||||
d.readBool(58, &m_fxaa, false);
|
d.readBool(58, &m_fxaa, false);
|
||||||
d.readS32(59, &m_msaa, 1);
|
d.readS32(59, &m_msaa, 1);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -451,6 +457,8 @@ void MapSettings::MapItemSettings::resetToDefaults()
|
|||||||
m_filterName = "";
|
m_filterName = "";
|
||||||
m_filterDistance = 0;
|
m_filterDistance = 0;
|
||||||
m_extrapolate = 60;
|
m_extrapolate = 60;
|
||||||
|
m_smoothingWindow = 0;
|
||||||
|
m_smoothingLambda = 100;
|
||||||
}
|
}
|
||||||
|
|
||||||
QByteArray MapSettings::MapItemSettings::serialize() const
|
QByteArray MapSettings::MapItemSettings::serialize() const
|
||||||
@ -475,6 +483,8 @@ QByteArray MapSettings::MapItemSettings::serialize() const
|
|||||||
s.writeString(16, m_filterName);
|
s.writeString(16, m_filterName);
|
||||||
s.writeS32(17, m_filterDistance);
|
s.writeS32(17, m_filterDistance);
|
||||||
s.writeS32(18, m_extrapolate);
|
s.writeS32(18, m_extrapolate);
|
||||||
|
s.writeS32(19, m_smoothingWindow);
|
||||||
|
s.writeFloat(20, m_smoothingLambda);
|
||||||
|
|
||||||
return s.final();
|
return s.final();
|
||||||
}
|
}
|
||||||
@ -511,6 +521,16 @@ bool MapSettings::MapItemSettings::deserialize(const QByteArray& data)
|
|||||||
d.readS32(18, &m_extrapolate, 60);
|
d.readS32(18, &m_extrapolate, 60);
|
||||||
m_filterNameRE.setPattern(m_filterName);
|
m_filterNameRE.setPattern(m_filterName);
|
||||||
m_filterNameRE.optimize();
|
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;
|
return true;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -703,6 +723,12 @@ void MapSettings::applySettings(const QStringList& settingsKeys, const MapSettin
|
|||||||
if (settingsKeys.contains("arcGISAPIKey")) {
|
if (settingsKeys.contains("arcGISAPIKey")) {
|
||||||
m_arcGISAPIKey = settings.m_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")) {
|
if (settingsKeys.contains("workspaceIndex")) {
|
||||||
m_workspaceIndex = settings.m_workspaceIndex;
|
m_workspaceIndex = settings.m_workspaceIndex;
|
||||||
}
|
}
|
||||||
@ -847,6 +873,12 @@ QString MapSettings::getDebugString(const QStringList& settingsKeys, bool force)
|
|||||||
if (settingsKeys.contains("arcGISAPIKey") || force) {
|
if (settingsKeys.contains("arcGISAPIKey") || force) {
|
||||||
ostr << " m_arcGISAPIKey: " << m_arcGISAPIKey.toStdString();
|
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) {
|
if (settingsKeys.contains("workspaceIndex") || force) {
|
||||||
ostr << " m_workspaceIndex: " << m_workspaceIndex;
|
ostr << " m_workspaceIndex: " << m_workspaceIndex;
|
||||||
}
|
}
|
||||||
|
@ -52,6 +52,8 @@ struct MapSettings
|
|||||||
QRegularExpression m_filterNameRE;
|
QRegularExpression m_filterNameRE;
|
||||||
int m_filterDistance; // Filter items > this distance in metres away from My Position. <= 0 don't filter
|
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_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 QString& group, bool enabled, const QColor color, bool display2DTrack=true, bool display3DPoint=true, int minZoom=11, int modelMinPixelSize=0);
|
||||||
MapItemSettings(const QByteArray& data);
|
MapItemSettings(const QByteArray& data);
|
||||||
@ -120,6 +122,8 @@ struct MapSettings
|
|||||||
bool m_displayAurora;
|
bool m_displayAurora;
|
||||||
bool m_displayMagDec;
|
bool m_displayMagDec;
|
||||||
bool m_displayMaidenheadGrid;
|
bool m_displayMaidenheadGrid;
|
||||||
|
bool m_displayPFD;
|
||||||
|
bool m_viewFirstPerson;
|
||||||
QString m_defaultImagery;
|
QString m_defaultImagery;
|
||||||
|
|
||||||
QString m_checkWXAPIKey; //!< checkwxapi.com API key
|
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->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
|
||||||
m_filterDistance->setSpecialValueText(" ");
|
m_filterDistance->setSpecialValueText(" ");
|
||||||
m_filterDistance->setCorrectionMode(QAbstractSpinBox::CorrectToNearestValue);
|
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_2D_MIN_ZOOM, m_minZoom);
|
||||||
table->setCellWidget(row, MapSettingsDialog::COL_3D_MIN_PIXELS, m_minPixels);
|
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_3D_LABEL_SCALE, m_labelScale);
|
||||||
table->setCellWidget(row, MapSettingsDialog::COL_FILTER_DISTANCE, m_filterDistance);
|
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) :
|
MapSettingsDialog::MapSettingsDialog(MapSettings *settings, QWidget* parent) :
|
||||||
@ -142,6 +152,12 @@ MapSettingsDialog::MapSettingsDialog(MapSettings *settings, QWidget* parent) :
|
|||||||
item = new QTableWidgetItem(itemSettings->m_filterName);
|
item = new QTableWidgetItem(itemSettings->m_filterName);
|
||||||
ui->mapItemSettings->setItem(row, COL_FILTER_NAME, item);
|
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);
|
MapItemSettingsGUI *gui = new MapItemSettingsGUI(ui->mapItemSettings, row, itemSettings);
|
||||||
m_mapItemSettingsGUIs.append(gui);
|
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_POINT);
|
||||||
ui->mapItemSettings->hideColumn(COL_3D_TRACK);
|
ui->mapItemSettings->hideColumn(COL_3D_TRACK);
|
||||||
ui->mapItemSettings->hideColumn(COL_3D_LABEL_SCALE);
|
ui->mapItemSettings->hideColumn(COL_3D_LABEL_SCALE);
|
||||||
|
ui->mapItemSettings->hideColumn(COL_SMOOTHING_WINDOW);
|
||||||
|
ui->mapItemSettings->hideColumn(COL_SMOOTHING_LAMBDA);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -203,7 +221,6 @@ void MapSettingsDialog::accept()
|
|||||||
m_settings->m_mapBoxAPIKey = mapBoxAPIKey;
|
m_settings->m_mapBoxAPIKey = mapBoxAPIKey;
|
||||||
m_settings->m_osmURL = osmURL;
|
m_settings->m_osmURL = osmURL;
|
||||||
m_settings->m_mapBoxStyles = mapBoxStyles;
|
m_settings->m_mapBoxStyles = mapBoxStyles;
|
||||||
m_settings->m_cesiumIonAPIKey = cesiumIonAPIKey;
|
|
||||||
m_map2DSettingsChanged = true;
|
m_map2DSettingsChanged = true;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -223,7 +240,6 @@ void MapSettingsDialog::accept()
|
|||||||
{
|
{
|
||||||
m_map3DSettingsChanged = false;
|
m_map3DSettingsChanged = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_settings->m_map2DEnabled != ui->map2DEnabled->isChecked())
|
if (m_settings->m_map2DEnabled != ui->map2DEnabled->isChecked())
|
||||||
{
|
{
|
||||||
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.setPattern(itemSettings->m_filterName);
|
||||||
itemSettings->m_filterNameRE.optimize();
|
itemSettings->m_filterNameRE.optimize();
|
||||||
itemSettings->m_filterDistance = gui->m_filterDistance->value() * 1000;
|
itemSettings->m_filterDistance = gui->m_filterDistance->value() * 1000;
|
||||||
|
itemSettings->m_smoothingWindow = gui->m_smoothingWindow->value();
|
||||||
|
itemSettings->m_smoothingLambda = gui->m_smoothingLambda->value();
|
||||||
}
|
}
|
||||||
|
|
||||||
QDialog::accept();
|
QDialog::accept();
|
||||||
@ -359,6 +377,8 @@ void MapSettingsDialog::on_map3DEnabled_clicked(bool checked)
|
|||||||
ui->mapItemSettings->showColumn(COL_3D_POINT);
|
ui->mapItemSettings->showColumn(COL_3D_POINT);
|
||||||
ui->mapItemSettings->showColumn(COL_3D_TRACK);
|
ui->mapItemSettings->showColumn(COL_3D_TRACK);
|
||||||
ui->mapItemSettings->showColumn(COL_3D_LABEL_SCALE);
|
ui->mapItemSettings->showColumn(COL_3D_LABEL_SCALE);
|
||||||
|
ui->mapItemSettings->showColumn(COL_SMOOTHING_WINDOW);
|
||||||
|
ui->mapItemSettings->showColumn(COL_SMOOTHING_LAMBDA);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -368,6 +388,8 @@ void MapSettingsDialog::on_map3DEnabled_clicked(bool checked)
|
|||||||
ui->mapItemSettings->hideColumn(COL_3D_POINT);
|
ui->mapItemSettings->hideColumn(COL_3D_POINT);
|
||||||
ui->mapItemSettings->hideColumn(COL_3D_TRACK);
|
ui->mapItemSettings->hideColumn(COL_3D_TRACK);
|
||||||
ui->mapItemSettings->hideColumn(COL_3D_LABEL_SCALE);
|
ui->mapItemSettings->hideColumn(COL_3D_LABEL_SCALE);
|
||||||
|
ui->mapItemSettings->hideColumn(COL_SMOOTHING_WINDOW);
|
||||||
|
ui->mapItemSettings->hideColumn(COL_SMOOTHING_LAMBDA);
|
||||||
}
|
}
|
||||||
ui->terrain->setEnabled(checked);
|
ui->terrain->setEnabled(checked);
|
||||||
ui->buildings->setEnabled(checked);
|
ui->buildings->setEnabled(checked);
|
||||||
|
@ -48,6 +48,8 @@ public:
|
|||||||
QSpinBox *m_minPixels;
|
QSpinBox *m_minPixels;
|
||||||
QDoubleSpinBox *m_labelScale;
|
QDoubleSpinBox *m_labelScale;
|
||||||
QSpinBox *m_filterDistance;
|
QSpinBox *m_filterDistance;
|
||||||
|
QSpinBox *m_smoothingWindow;
|
||||||
|
QDoubleSpinBox *m_smoothingLambda;
|
||||||
};
|
};
|
||||||
|
|
||||||
class MapSettingsDialog : public QDialog {
|
class MapSettingsDialog : public QDialog {
|
||||||
@ -70,7 +72,9 @@ public:
|
|||||||
COL_3D_TRACK,
|
COL_3D_TRACK,
|
||||||
COL_3D_LABEL_SCALE,
|
COL_3D_LABEL_SCALE,
|
||||||
COL_FILTER_NAME,
|
COL_FILTER_NAME,
|
||||||
COL_FILTER_DISTANCE
|
COL_FILTER_DISTANCE,
|
||||||
|
COL_SMOOTHING_WINDOW,
|
||||||
|
COL_SMOOTHING_LAMBDA
|
||||||
};
|
};
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
<x>0</x>
|
<x>0</x>
|
||||||
<y>0</y>
|
<y>0</y>
|
||||||
<width>1267</width>
|
<width>1267</width>
|
||||||
<height>648</height>
|
<height>775</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
<property name="font">
|
<property name="font">
|
||||||
@ -153,13 +153,52 @@
|
|||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="1" column="0">
|
<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">
|
<widget class="QLabel" name="terrainLabel">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Terrain</string>
|
<string>Terrain</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="1" column="1">
|
<item row="2" column="1">
|
||||||
<widget class="QComboBox" name="terrain">
|
<widget class="QComboBox" name="terrain">
|
||||||
<item>
|
<item>
|
||||||
<property name="text">
|
<property name="text">
|
||||||
@ -183,14 +222,48 @@
|
|||||||
</item>
|
</item>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</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">
|
<widget class="QLabel" name="buildingsLabel">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Buildings</string>
|
<string>Buildings</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="2" column="1">
|
<item row="5" column="1">
|
||||||
<widget class="QComboBox" name="buildings">
|
<widget class="QComboBox" name="buildings">
|
||||||
<item>
|
<item>
|
||||||
<property name="text">
|
<property name="text">
|
||||||
@ -204,14 +277,14 @@
|
|||||||
</item>
|
</item>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="3" column="0">
|
<item row="6" column="0">
|
||||||
<widget class="QLabel" name="sunLightEnabledLabel">
|
<widget class="QLabel" name="sunLightEnabledLabel">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Lighting</string>
|
<string>Lighting</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="3" column="1">
|
<item row="6" column="1">
|
||||||
<widget class="QComboBox" name="sunLightEnabled">
|
<widget class="QComboBox" name="sunLightEnabled">
|
||||||
<property name="toolTip">
|
<property name="toolTip">
|
||||||
<string>Whether lighting is from the Sun or Camera</string>
|
<string>Whether lighting is from the Sun or Camera</string>
|
||||||
@ -228,14 +301,71 @@
|
|||||||
</item>
|
</item>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</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">
|
<widget class="QLabel" name="eciCameraLabel">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Camera reference frame</string>
|
<string>Camera reference frame</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="4" column="1">
|
<item row="11" column="1">
|
||||||
<widget class="QComboBox" name="eciCamera">
|
<widget class="QComboBox" name="eciCamera">
|
||||||
<property name="toolTip">
|
<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>
|
<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>
|
</item>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="5" column="0">
|
<item row="14" column="0">
|
||||||
<widget class="QLabel" name="antiAliasingLabel">
|
<widget class="QLabel" name="fpsLabel">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Anti-aliasing</string>
|
<string>Display FPS</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="5" column="1">
|
<item row="14" column="1">
|
||||||
<widget class="QComboBox" name="antiAliasing">
|
<widget class="QCheckBox" name="fps">
|
||||||
<property name="toolTip">
|
<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>
|
</property>
|
||||||
<item>
|
<item>
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>None</string>
|
<string>Off</string>
|
||||||
</property>
|
</property>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
|
<property name="text">
|
||||||
|
<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">
|
<property name="text">
|
||||||
<string>FXAA</string>
|
<string>FXAA</string>
|
||||||
</property>
|
</property>
|
||||||
|
</widget>
|
||||||
</item>
|
</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>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
@ -282,7 +461,7 @@
|
|||||||
<item>
|
<item>
|
||||||
<spacer name="verticalSpacer">
|
<spacer name="verticalSpacer">
|
||||||
<property name="orientation">
|
<property name="orientation">
|
||||||
<enum>Qt::Vertical</enum>
|
<enum>Qt::Orientation::Vertical</enum>
|
||||||
</property>
|
</property>
|
||||||
<property name="sizeHint" stdset="0">
|
<property name="sizeHint" stdset="0">
|
||||||
<size>
|
<size>
|
||||||
@ -313,7 +492,7 @@
|
|||||||
<string/>
|
<string/>
|
||||||
</property>
|
</property>
|
||||||
<property name="icon">
|
<property name="icon">
|
||||||
<iconset resource="icons.qrc">
|
<iconset resource="mapicons.qrc">
|
||||||
<normaloff>:/map/icons/controltower.png</normaloff>:/map/icons/controltower.png</iconset>
|
<normaloff>:/map/icons/controltower.png</normaloff>:/map/icons/controltower.png</iconset>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
@ -327,7 +506,7 @@
|
|||||||
<string/>
|
<string/>
|
||||||
</property>
|
</property>
|
||||||
<property name="icon">
|
<property name="icon">
|
||||||
<iconset resource="icons.qrc">
|
<iconset resource="mapicons.qrc">
|
||||||
<normaloff>:/map/icons/vor.png</normaloff>:/map/icons/vor.png</iconset>
|
<normaloff>:/map/icons/vor.png</normaloff>:/map/icons/vor.png</iconset>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
@ -341,7 +520,7 @@
|
|||||||
<string/>
|
<string/>
|
||||||
</property>
|
</property>
|
||||||
<property name="icon">
|
<property name="icon">
|
||||||
<iconset resource="icons.qrc">
|
<iconset resource="mapicons.qrc">
|
||||||
<normaloff>:/map/icons/waypoints.png</normaloff>:/map/icons/waypoints.png</iconset>
|
<normaloff>:/map/icons/waypoints.png</normaloff>:/map/icons/waypoints.png</iconset>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
@ -349,7 +528,7 @@
|
|||||||
<item>
|
<item>
|
||||||
<spacer name="horizontalSpacer">
|
<spacer name="horizontalSpacer">
|
||||||
<property name="orientation">
|
<property name="orientation">
|
||||||
<enum>Qt::Horizontal</enum>
|
<enum>Qt::Orientation::Horizontal</enum>
|
||||||
</property>
|
</property>
|
||||||
<property name="sizeHint" stdset="0">
|
<property name="sizeHint" stdset="0">
|
||||||
<size>
|
<size>
|
||||||
@ -371,7 +550,7 @@
|
|||||||
<item>
|
<item>
|
||||||
<widget class="QTableWidget" name="mapItemSettings">
|
<widget class="QTableWidget" name="mapItemSettings">
|
||||||
<property name="selectionMode">
|
<property name="selectionMode">
|
||||||
<enum>QAbstractItemView::NoSelection</enum>
|
<enum>QAbstractItemView::SelectionMode::NoSelection</enum>
|
||||||
</property>
|
</property>
|
||||||
<column>
|
<column>
|
||||||
<property name="text">
|
<property name="text">
|
||||||
@ -468,6 +647,22 @@
|
|||||||
<string>Filter objects further than this distance in km away from My Position</string>
|
<string>Filter objects further than this distance in km away from My Position</string>
|
||||||
</property>
|
</property>
|
||||||
</column>
|
</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>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
@ -563,20 +758,34 @@
|
|||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="4" column="0">
|
<item row="5" column="0">
|
||||||
<widget class="QLabel" name="checkWXAPIKeyLabel">
|
<widget class="QLabel" name="checkWXAPIKeyLabel">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>CheckWX API key</string>
|
<string>CheckWX API key</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="4" column="1">
|
<item row="5" column="1">
|
||||||
<widget class="QLineEdit" name="checkWXAPIKey">
|
<widget class="QLineEdit" name="checkWXAPIKey">
|
||||||
<property name="toolTip">
|
<property name="toolTip">
|
||||||
<string>checkwxapi.com API key for accessing airport weather (METARs)</string>
|
<string>checkwxapi.com API key for accessing airport weather (METARs)</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</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>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
@ -590,10 +799,10 @@
|
|||||||
<item>
|
<item>
|
||||||
<widget class="QDialogButtonBox" name="buttonBox">
|
<widget class="QDialogButtonBox" name="buttonBox">
|
||||||
<property name="orientation">
|
<property name="orientation">
|
||||||
<enum>Qt::Horizontal</enum>
|
<enum>Qt::Orientation::Horizontal</enum>
|
||||||
</property>
|
</property>
|
||||||
<property name="standardButtons">
|
<property name="standardButtons">
|
||||||
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
|
<set>QDialogButtonBox::StandardButton::Cancel|QDialogButtonBox::StandardButton::Ok</set>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
@ -609,7 +818,7 @@
|
|||||||
<tabstop>buildings</tabstop>
|
<tabstop>buildings</tabstop>
|
||||||
<tabstop>sunLightEnabled</tabstop>
|
<tabstop>sunLightEnabled</tabstop>
|
||||||
<tabstop>eciCamera</tabstop>
|
<tabstop>eciCamera</tabstop>
|
||||||
<tabstop>antiAliasing</tabstop>
|
<tabstop>msaa</tabstop>
|
||||||
<tabstop>downloadModels</tabstop>
|
<tabstop>downloadModels</tabstop>
|
||||||
<tabstop>thunderforestAPIKey</tabstop>
|
<tabstop>thunderforestAPIKey</tabstop>
|
||||||
<tabstop>maptilerAPIKey</tabstop>
|
<tabstop>maptilerAPIKey</tabstop>
|
||||||
@ -617,7 +826,7 @@
|
|||||||
<tabstop>cesiumIonAPIKey</tabstop>
|
<tabstop>cesiumIonAPIKey</tabstop>
|
||||||
</tabstops>
|
</tabstops>
|
||||||
<resources>
|
<resources>
|
||||||
<include location="icons.qrc"/>
|
<include location="mapicons.qrc"/>
|
||||||
</resources>
|
</resources>
|
||||||
<connections>
|
<connections>
|
||||||
<connection>
|
<connection>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user