mirror of
https://github.com/swift-project/pilotclient.git
synced 2026-03-22 23:05:36 +08:00
Ref T125, altitude parse and FP string functions
* asFpAltitudeString * asFpAltitudeSimpleVatsimString (non standard format) * parseFromFpAltitudeString
This commit is contained in:
@@ -96,10 +96,196 @@ namespace BlackMisc
|
||||
rd = AboveGround;
|
||||
}
|
||||
|
||||
CLength l = BlackMisc::PhysicalQuantities::CPqString::parse<CLength>(v, mode);
|
||||
const CLength l = BlackMisc::PhysicalQuantities::CPqString::parse<CLength>(v, mode);
|
||||
*this = CAltitude(l, rd);
|
||||
}
|
||||
|
||||
bool CAltitude::parseFromFpAltitudeString(const QString &value, CStatusMessageList *msgs)
|
||||
{
|
||||
QString v(value.trimmed());
|
||||
this->setNull();
|
||||
if (v.isEmpty() || v.length() < 3)
|
||||
{
|
||||
if (msgs) { msgs->push_back(CStatusMessage(this).validationError("Altitude empty or too short")); }
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!fpAltitudeRegExp().globalMatch(v).hasNext())
|
||||
{
|
||||
if (msgs) { msgs->push_back(CStatusMessage(this).validationError("Altitude '%1' needs to match format '%2'") << v << fpAltitudeExamples()); }
|
||||
return false;
|
||||
}
|
||||
|
||||
// normalize characters to upper/lower
|
||||
// in same step get numeric value only
|
||||
bool beforeDigit = true;
|
||||
QString numericPart;
|
||||
for (int i = 0; i < v.length(); i++)
|
||||
{
|
||||
const QChar c = v[i];
|
||||
if (c.isDigit())
|
||||
{
|
||||
beforeDigit = false;
|
||||
numericPart.push_back(c);
|
||||
continue;
|
||||
}
|
||||
v[i] = beforeDigit ? c.toUpper() : c.toLower();
|
||||
}
|
||||
|
||||
if (numericPart.isEmpty())
|
||||
{
|
||||
if (msgs) { msgs->push_back(CStatusMessage(this).validationError("Altitude '%1' has no numeric part")); }
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ok;
|
||||
if (v.startsWith("F"))
|
||||
{
|
||||
this->setUnit(CLengthUnit::ft());
|
||||
this->setValueSameUnit(numericPart.toInt(&ok) * 100);
|
||||
m_datum = FlightLevel;
|
||||
}
|
||||
else if (v.startsWith("S"))
|
||||
{
|
||||
this->setUnit(CLengthUnit::m());
|
||||
this->setValueSameUnit(numericPart.toInt(&ok) * 10);
|
||||
m_datum = FlightLevel;
|
||||
}
|
||||
else if (v.startsWith("A"))
|
||||
{
|
||||
this->setUnit(CLengthUnit::ft());
|
||||
this->setValueSameUnit(numericPart.toInt(&ok) * 100);
|
||||
m_datum = MeanSeaLevel;
|
||||
}
|
||||
else if (v.startsWith("M"))
|
||||
{
|
||||
this->setUnit(CLengthUnit::m());
|
||||
this->setValueSameUnit(numericPart.toInt(&ok) * 10);
|
||||
m_datum = MeanSeaLevel;
|
||||
}
|
||||
else if (v.endsWith(CLengthUnit::m().getSymbol()))
|
||||
{
|
||||
this->setUnit(CLengthUnit::m());
|
||||
this->setValueSameUnit(numericPart.toInt(&ok));
|
||||
m_datum = MeanSeaLevel;
|
||||
}
|
||||
else if (v.endsWith(CLengthUnit::ft().getSymbol()))
|
||||
{
|
||||
this->setUnit(CLengthUnit::ft());
|
||||
this->setValueSameUnit(numericPart.toInt(&ok));
|
||||
m_datum = MeanSeaLevel;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (msgs) { msgs->push_back(CStatusMessage(this).validationError("Altitude '%1' needs to match format '%2'") << v << fpAltitudeExamples()); }
|
||||
return false;
|
||||
}
|
||||
|
||||
// further checks
|
||||
const bool valid = this->isValidFpAltitude(msgs);
|
||||
if (!valid) { this->setNull(); }
|
||||
return valid;
|
||||
}
|
||||
|
||||
bool CAltitude::isValidFpAltitude(CStatusMessageList *msgs) const
|
||||
{
|
||||
if (this->isNull())
|
||||
{
|
||||
if (msgs) { msgs->push_back(CStatusMessage(this).validationError("Altitude NULL value")); }
|
||||
return false;
|
||||
}
|
||||
if (!(this->getReferenceDatum() == FlightLevel || this->getReferenceDatum() == MeanSeaLevel))
|
||||
{
|
||||
if (msgs) { msgs->push_back(CStatusMessage(this).validationError("Altitude, must be FL or MSL")); }
|
||||
return false;
|
||||
}
|
||||
if (!(this->getUnit() == CLengthUnit::m() || this->getUnit() == CLengthUnit::ft()))
|
||||
{
|
||||
if (msgs) { msgs->push_back(CStatusMessage(this).validationError("Altitude, valid unit must be 'ft' or 'm'")); }
|
||||
return false;
|
||||
}
|
||||
if (this->isNegativeWithEpsilonConsidered())
|
||||
{
|
||||
if (msgs) { msgs->push_back(CStatusMessage(this).validationError("Altitude must be positive")); }
|
||||
return false;
|
||||
}
|
||||
if (this->isFlightLevel())
|
||||
{
|
||||
if (!this->isInteger())
|
||||
{
|
||||
if (msgs) { msgs->push_back(CStatusMessage(this).validationError("FL needs to be positive integer")); }
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this->getUnit() == CLengthUnit::ft())
|
||||
{
|
||||
const int flInt = this->valueInteger() / 100; // internally represented as ft: FL10->1000
|
||||
if (flInt < 10 || flInt >= 1000)
|
||||
{
|
||||
if (msgs) { msgs->push_back(CStatusMessage(this).validationError("FL range is 10-999")); }
|
||||
return false;
|
||||
}
|
||||
if (fmod(flInt, 5) != 0)
|
||||
{
|
||||
if (msgs) { msgs->push_back(CStatusMessage(this).validationError("FL needs to end with 0 or 5")); }
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
QString CAltitude::asFpAltitudeString() const
|
||||
{
|
||||
if (this->isNull()) { return QStringLiteral(""); }
|
||||
|
||||
|
||||
if (this->isFlightLevel())
|
||||
{
|
||||
if (this->getUnit() == CLengthUnit::m())
|
||||
{
|
||||
int m = this->valueInteger() / 10;
|
||||
return QString("S%1").arg(m, 4, 10, QChar('0'));
|
||||
}
|
||||
int ft = this->valueInteger(CLengthUnit::ft()) / 100;
|
||||
return QString("FL%1").arg(ft, 3, 10, QChar('0'));
|
||||
}
|
||||
|
||||
if (this->getUnit() == CLengthUnit::m())
|
||||
{
|
||||
int m = this->valueInteger() / 10;
|
||||
return QString("M%1").arg(m, 4, 10, QChar('0'));
|
||||
}
|
||||
int ft = this->valueInteger(CLengthUnit::ft()) / 100;
|
||||
return QString("A%1").arg(ft, 3, 10, QChar('0'));
|
||||
}
|
||||
|
||||
QString CAltitude::asFpAltitudeSimpleVatsimString() const
|
||||
{
|
||||
CAltitude copy(*this);
|
||||
copy.switchUnit(CLengthUnit::ft());
|
||||
if (copy.isFlightLevel()) { return copy.asFpAltitudeString(); }
|
||||
return QString::number(copy.valueInteger()); // ft altitude without unit
|
||||
}
|
||||
|
||||
const QRegularExpression &CAltitude::fpAltitudeRegExp()
|
||||
{
|
||||
static thread_local QRegularExpression re("((FL|F)\\d{2,3})|(S\\d{2,4})|(A\\d{2,3})|(M\\d{2,4})|(\\d{3,5}(ft|m))");
|
||||
return re;
|
||||
}
|
||||
|
||||
QString CAltitude::fpAltitudeInfo(const QString &separator)
|
||||
{
|
||||
static const QString e("FL085, F85 flight level%1S0150 metric level in tens of metres%1A055 altitude in hundreds of feet%12000ft altitude in ft%1M0610 altitude in tens of metres%16100m altitude in meters");
|
||||
return e.arg(separator);
|
||||
}
|
||||
|
||||
QString CAltitude::fpAltitudeExamples()
|
||||
{
|
||||
static const QString e("FL085, F85, S0150, A055, 1200ft, M0610, 6100m");
|
||||
return e;
|
||||
}
|
||||
|
||||
CIcon CAltitude::toIcon() const
|
||||
{
|
||||
return BlackMisc::CIcon::iconByIndex(CIcons::GeoPosition);
|
||||
|
||||
@@ -12,21 +12,23 @@
|
||||
#ifndef BLACKMISC_AVIATION_ALTITUDE_H
|
||||
#define BLACKMISC_AVIATION_ALTITUDE_H
|
||||
|
||||
#include "blackmisc/blackmiscexport.h"
|
||||
#include "blackmisc/pq/length.h"
|
||||
#include "blackmisc/pq/pqstring.h"
|
||||
#include "blackmisc/pq/units.h"
|
||||
#include "blackmisc/statusmessagelist.h"
|
||||
#include "blackmisc/compare.h"
|
||||
#include "blackmisc/dbus.h"
|
||||
#include "blackmisc/dictionary.h"
|
||||
#include "blackmisc/icon.h"
|
||||
#include "blackmisc/metaclass.h"
|
||||
#include "blackmisc/pq/length.h"
|
||||
#include "blackmisc/pq/pqstring.h"
|
||||
#include "blackmisc/pq/units.h"
|
||||
#include "blackmisc/propertyindexvariantmap.h"
|
||||
#include "blackmisc/stringutils.h"
|
||||
#include "blackmisc/variant.h"
|
||||
#include "blackmisc/blackmiscexport.h"
|
||||
|
||||
#include <QMetaType>
|
||||
#include <QString>
|
||||
#include <QRegularExpression>
|
||||
|
||||
namespace BlackMisc
|
||||
{
|
||||
@@ -72,20 +74,20 @@ namespace BlackMisc
|
||||
//! \copydoc BlackMisc::Mixin::String::toQString
|
||||
QString convertToQString(bool i18n = false) const;
|
||||
|
||||
//! Default constructor: 0 Altitude true
|
||||
CAltitude() : CLength(0, BlackMisc::PhysicalQuantities::CLengthUnit::m()), m_datum(MeanSeaLevel) {}
|
||||
//! Default constructor: 0m Altitude MSL
|
||||
CAltitude() : CLength(0, PhysicalQuantities::CLengthUnit::m()), m_datum(MeanSeaLevel) {}
|
||||
|
||||
//! Constructor
|
||||
CAltitude(double value, ReferenceDatum datum, const BlackMisc::PhysicalQuantities::CLengthUnit &unit) : CLength(value, unit), m_datum(datum) {}
|
||||
CAltitude(double value, ReferenceDatum datum, const PhysicalQuantities::CLengthUnit &unit) : CLength(value, unit), m_datum(datum) {}
|
||||
|
||||
//! Constructor, value as CAltitude::MeanSeaLevel
|
||||
CAltitude(double value, const BlackMisc::PhysicalQuantities::CLengthUnit &unit) : CLength(value, unit), m_datum(MeanSeaLevel) {}
|
||||
CAltitude(double value, const PhysicalQuantities::CLengthUnit &unit) : CLength(value, unit), m_datum(MeanSeaLevel) {}
|
||||
|
||||
//! Altitude as string
|
||||
CAltitude(const QString &altitudeAsString, BlackMisc::PhysicalQuantities::CPqString::SeparatorMode mode = BlackMisc::PhysicalQuantities::CPqString::SeparatorsLocale);
|
||||
CAltitude(const QString &altitudeAsString, PhysicalQuantities::CPqString::SeparatorMode mode = PhysicalQuantities::CPqString::SeparatorsLocale);
|
||||
|
||||
//! Constructor by CLength
|
||||
CAltitude(const BlackMisc::PhysicalQuantities::CLength &altitude, ReferenceDatum datum) : CLength(altitude), m_datum(datum) {}
|
||||
CAltitude(const PhysicalQuantities::CLength &altitude, ReferenceDatum datum) : CLength(altitude), m_datum(datum) {}
|
||||
|
||||
//! AGL Above ground level?
|
||||
bool isAboveGroundLevel() const { return AboveGround == this->m_datum; }
|
||||
@@ -109,7 +111,36 @@ namespace BlackMisc
|
||||
void parseFromString(const QString &value);
|
||||
|
||||
//! Parse value from string, with specified separator
|
||||
void parseFromString(const QString &value, BlackMisc::PhysicalQuantities::CPqString::SeparatorMode mode);
|
||||
void parseFromString(const QString &value, PhysicalQuantities::CPqString::SeparatorMode mode);
|
||||
|
||||
//! Parse from FP altitude string
|
||||
//! \sa CFlightPlan::asFpAltitudeString
|
||||
bool parseFromFpAltitudeString(const QString &value, CStatusMessageList *msgs = nullptr);
|
||||
|
||||
//! Is this a valid FP altitude
|
||||
//! \sa CFlightPlan::asFpAltitudeString
|
||||
bool isValidFpAltitude(CStatusMessageList *msgs = nullptr) const;
|
||||
|
||||
//! Altitude string (official version)
|
||||
//! * flight level, expressed as "F" followed by 3 figures, example: F085 (which means flight level 085),
|
||||
//! * standard metric level in tens of meters, expressed as "S" followed by 4 figures, example: S0150 (which means 1500 metres)
|
||||
//! * altitude in hundreds of feet, expressed as "A" followed by 3 figures, example: A055 (which means 5500 feet altitude)
|
||||
//! * altitude in tens of meters expressed as "M" followed by 4 figures, example: M0610 (which means 6100 metres altitude)
|
||||
QString asFpAltitudeString() const;
|
||||
|
||||
//! As simple VATSIM string, only FLxxx or altitude as ft
|
||||
QString asFpAltitudeSimpleVatsimString() const;
|
||||
|
||||
//! Checking FP altitude strings like "A20", "FL100"
|
||||
//! \sa CFlightPlan::asFpAltitudeString
|
||||
static const QRegularExpression &fpAltitudeRegExp();
|
||||
|
||||
//! Info for FP altitude strings
|
||||
//! \sa CFlightPlan::asFpAltitudeString
|
||||
static QString fpAltitudeInfo(const QString &separator = ", ");
|
||||
|
||||
//! Examples of FP altitude strings
|
||||
static QString fpAltitudeExamples();
|
||||
|
||||
//! \copydoc BlackMisc::Mixin::Icon::toIcon
|
||||
BlackMisc::CIcon toIcon() const;
|
||||
@@ -125,8 +156,8 @@ namespace BlackMisc
|
||||
BLACK_METAMEMBER(datum)
|
||||
);
|
||||
};
|
||||
}
|
||||
}
|
||||
} // ns
|
||||
} // ns
|
||||
|
||||
Q_DECLARE_METATYPE(BlackMisc::Aviation::CAltitude)
|
||||
Q_DECLARE_METATYPE(BlackMisc::Aviation::CAltitude::ReferenceDatum)
|
||||
|
||||
Reference in New Issue
Block a user