Ref T125, altitude parse and FP string functions

* asFpAltitudeString
* asFpAltitudeSimpleVatsimString (non standard format)
* parseFromFpAltitudeString
This commit is contained in:
Klaus Basan
2018-01-04 04:26:11 +01:00
parent 7b414121df
commit d7bc247469
2 changed files with 231 additions and 14 deletions

View File

@@ -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);