Ref T111, lat/lng/angle changes

* get struct CAngle::DegMinSecFractionalSec to obtain parts
* round to epsilon utility functions and fix (qint64)
This commit is contained in:
Klaus Basan
2017-07-26 14:38:49 +02:00
committed by Mathew Sutcliffe
parent 77546a46b1
commit e55480737e
15 changed files with 328 additions and 102 deletions

View File

@@ -14,10 +14,60 @@
#include <cmath>
using namespace BlackMisc::Math;
namespace BlackMisc
{
namespace PhysicalQuantities
{
CAngle::CAngle(int degrees, int minutes, double seconds) :
CPhysicalQuantity(
degrees + minutes / 100.0 + seconds / 10000.0,
CAngleUnit::sexagesimalDeg())
{
Q_ASSERT_X((degrees >= 0 && minutes >= 0 && seconds >= 0) ||
(degrees <= 0 && minutes <= 0 && seconds <= 0), Q_FUNC_INFO, "Same sign required");
}
CAngle::CAngle(int degrees, double minutes) :
CPhysicalQuantity(
degrees + minutes / 100.0,
CAngleUnit::sexagesimalDeg())
{
Q_ASSERT_X((degrees >= 0 && minutes >= 0) || (degrees <= 0 && minutes <= 0),
Q_FUNC_INFO, "Same sign required");
}
void CAngle::unifySign(int &degrees, int &minutes, double &seconds)
{
if (degrees < 0)
{
if (minutes > 0) minutes *= -1;
if (seconds > 0) seconds *= -1.0;
}
else if (degrees > 0)
{
if (minutes < 0) minutes *= -1;
if (seconds < 0) seconds *= -1.0;
}
else
{
// degrees was 0
if ((minutes > 0 && seconds < 0) || (minutes < 0 && seconds > 0))
{
seconds *= -1.0;
}
}
}
void CAngle::unifySign(int &degrees, int &minutes)
{
if ((degrees > 0 && minutes < 0) || (degrees < 0 && minutes > 0))
{
minutes *= -1.0;
}
}
BlackMisc::CIcon CAngle::toIcon() const
{
BlackMisc::CIcon i = CIcon::iconByIndex(CIcons::StandardIconArrowMediumNorth16);
@@ -25,6 +75,37 @@ namespace BlackMisc
return i;
}
CAngle::DegMinSecFractionalSec CAngle::asSexagesimalDegMinSec(bool range180Degrees) const
{
double v = this->value(CAngleUnit::deg());
v = CAngleUnit::deg().roundToEpsilon(v);
// range -179-180 ?
if (range180Degrees)
{
v = std::fmod(v + 180.0, 360.0);
v += (v < 0) ? 180.0 : -180.0;
}
DegMinSecFractionalSec values;
if (v < 0)
{
values.sign = -1;
v *= -1.0;
}
values.deg = v;
v -= values.deg;
v = v * 100.0 * 0.6;
values.min = v;
v -= values.min;
v = v * 100.0 * 0.6;
values.sec = v;
v -= values.sec;
values.fractionalSec = CMathUtils::round(v, 6);
return values;
}
double CAngle::piFactor() const
{
return BlackMisc::Math::CMathUtils::round(this->value(CAngleUnit::rad()) / BlackMisc::Math::CMathUtils::PI() , 6);
@@ -49,5 +130,5 @@ namespace BlackMisc
{
return std::tan(this->value(CAngleUnit::rad()));
}
}
}
} // ns
} // ns

View File

@@ -25,7 +25,6 @@ namespace BlackMisc
{
namespace PhysicalQuantities
{
//! Physical unit angle (radians, degrees)
class BLACKMISC_EXPORT CAngle : public CPhysicalQuantity<CAngleUnit, CAngle>
{
@@ -39,27 +38,50 @@ namespace BlackMisc
//! \copydoc CPhysicalQuantity(const QString &unitString)
CAngle(const QString &unitString) : CPhysicalQuantity(unitString) {}
/*!
* \brief Init as sexagesimal degrees, minutes, seconds
* The sign of all parameters must be the same, either all positive or all negative.
*/
CAngle(int degrees, int minutes, double seconds) :
CPhysicalQuantity(
degrees + minutes / 100.0 + seconds / 10000.0,
CAngleUnit::sexagesimalDeg()) {}
//! Value as individual values
struct DegMinSecFractionalSec
{
int sign = 1; //!< 1/-1
int deg = 0; //!< 0-359
int min = 0; //!< 0-59
int sec = 0; //!< 0-59
double fractionalSec = 0; //!< value < 1.0
/*!
* \brief Init as sexagesimal degrees, minutes
* The sign of both parameters must be the same, either both positive or both negative.
*/
CAngle(int degrees, double minutes) :
CPhysicalQuantity(
degrees + minutes / 100.0,
CAngleUnit::sexagesimalDegMin()) {}
//! Degrees as string
QString degAsString() const { return QString::number(deg); }
//! Minutes as string
QString minAsString() const { return QString::number(min); }
//! Seconds as string
QString secAsString() const { return QString::number(sec); }
//! Fractional seconds as string
QString fractionalSecAsString(int width = -1) const { return BlackMisc::Math::CMathUtils::fractionalPartAsString(fractionalSec, width); }
};
//! \brief Init as sexagesimal degrees, minutes, seconds
//! The sign of all parameters must be the same, either all positive or all negative.
//! \see CAngle::unifySign(int &, int &, double &)
CAngle(int degrees, int minutes, double seconds);
//! \brief Init as sexagesimal degrees, minutes
//! The sign of both parameters must be the same, either both positive or both negative.
//! \see CAngle::unifySign(int &, double &)
CAngle(int degrees, double minutes);
//! Minutes and secods will get same sign as degrees
void static unifySign(int &degrees, int &minutes, double &seconds);
//! Minutes will get same sign as degrees
void static unifySign(int &degrees, int &minutes);
//! \copydoc BlackMisc::Mixin::Icon::toIcon
BlackMisc::CIcon toIcon() const;
//! As individual values
DegMinSecFractionalSec asSexagesimalDegMinSec(bool range180Degrees = false) const;
//! Value as factor of PI (e.g. 0.5PI)
double piFactor() const;
@@ -75,8 +97,8 @@ namespace BlackMisc
//! Tangent of angle
double tan() const;
};
}
}
} // ns
} // ns
Q_DECLARE_METATYPE(BlackMisc::PhysicalQuantities::CAngle)

View File

@@ -17,7 +17,6 @@ namespace BlackMisc
{
namespace PhysicalQuantities
{
bool CMeasurementUnit::operator ==(const CMeasurementUnit &other) const
{
if (this == &other) return true;
@@ -43,17 +42,23 @@ namespace BlackMisc
double CMeasurementUnit::roundValue(double value, int digits) const
{
if (digits < 0) digits = this->m_data->m_displayDigits;
if (digits < 0) { digits = this->m_data->m_displayDigits; }
return CMathUtils::round(value, digits);
}
QString CMeasurementUnit::makeRoundedQString(double value, int digits, bool /* i18n */) const
double CMeasurementUnit::roundToEpsilon(double value) const
{
if (digits < 0) digits = this->m_data->m_displayDigits;
double v = CMathUtils::round(value, digits);
QString s = QLocale::system().toString(v, 'f', digits);
return s;
if (this->getEpsilon() == 0 || this->isNull()) { return value; }
return CMathUtils::roundEpsilon(value, this->getEpsilon());
}
QString CMeasurementUnit::makeRoundedQString(double value, int digits, bool i18n) const
{
Q_UNUSED(i18n);
if (digits < 0) digits = this->m_data->m_displayDigits;
const double v = CMathUtils::round(value, digits);
const QString s = QLocale::system().toString(v, 'f', digits);
return s;
}
} // namespace
} // namespace

View File

@@ -36,7 +36,6 @@ namespace BlackMisc
{
namespace PhysicalQuantities
{
/*!
* Base class for all units, such as meter, hertz.
*/
@@ -219,11 +218,11 @@ namespace BlackMisc
: m_name(name), m_symbol(symbol)
{}
QLatin1String m_name; //!< name, e.g. "meter"
QLatin1String m_symbol; //!< unit name, e.g. "m"
double m_epsilon = 0.0; //!< values with differences below epsilon are the equal
int m_displayDigits = 0; //!< standard rounding for string conversions
ConverterFunction m_toDefault = nullptr; //!< convert from this unit to default unit
QLatin1String m_name; //!< name, e.g. "meter"
QLatin1String m_symbol; //!< unit name, e.g. "m"
double m_epsilon = 0.0; //!< values with differences below epsilon are the equal
int m_displayDigits = 0; //!< standard rounding for string conversions
ConverterFunction m_toDefault = nullptr; //!< convert from this unit to default unit
ConverterFunction m_fromDefault = nullptr; //!< convert to this unit from default unit
};
@@ -291,16 +290,31 @@ namespace BlackMisc
return i18n ? QCoreApplication::translate("CMeasurementUnit", this->m_data->m_symbol.latin1()) : this->m_data->m_symbol;
}
//! Does a string end with name or symbol? E.g. 3meter, 3m, 3deg
bool endsStringWithNameOrSymbol(const QString &candidate, Qt::CaseSensitivity cs = Qt::CaseSensitive)
{
const QString c = candidate.trimmed();
return c.endsWith(this->getName(false), cs) || c.endsWith(this->getName(true)) ||
c.endsWith(this->getSymbol(false), cs) || c.endsWith(this->getSymbol(true));
}
//! Rounded value
//! \note default digits is CMeasurementUnit::getDisplayDigits
double roundValue(double value, int digits = -1) const;
//! Rounded to the nearest CMeasurementUnit::getEpsilon
//! \remark uses CMathUtils::roundEpsilon
double roundToEpsilon(double value) const;
//! Rounded string utility method, virtual so units can have specialized formatting
//! \note default digits is CMeasurementUnit::getDisplayDigits
virtual QString makeRoundedQString(double value, int digits = -1, bool i18n = false) const;
//! Value rounded with unit, e.g. "5.00m", "30kHz"
//! \note default digits is CMeasurementUnit::getDisplayDigits
virtual QString makeRoundedQStringWithUnit(double value, int digits = -1, bool i18n = false) const;
//! Threshold for rounding
//! Threshold for comparions
double getEpsilon() const
{
return this->m_data->m_epsilon;
@@ -371,8 +385,7 @@ namespace BlackMisc
return none;
}
};
}
}
} // ns
} // ns
#endif // guard

View File

@@ -300,6 +300,13 @@ namespace BlackMisc
return this->valueRoundedWithUnit(this->m_unit, digits, i18n);
}
template<class MU, class PQ>
void CPhysicalQuantity<MU, PQ>::roundToEpsilon()
{
if (this->isNull()) { return; }
this->m_value = this->m_unit.roundToEpsilon(this->m_value);
}
template <class MU, class PQ>
double CPhysicalQuantity<MU, PQ>::valueRounded(MU unit, int digits) const
{

View File

@@ -101,20 +101,28 @@ namespace BlackMisc
void setCurrentUnitValue(double value);
//! Rounded value in given unit
//! \note default digits is CMeasurementUnit::getDisplayDigits
double valueRounded(MU unit, int digits = -1) const;
//! As integer value
int valueInteger(MU unit) const;
//! Rounded value in current unit
//! \note default digits is CMeasurementUnit::getDisplayDigits
double valueRounded(int digits = -1) const;
//! Value to QString with the given unit, e.g. "5.00m"
//! \note default digits is CMeasurementUnit::getDisplayDigits
QString valueRoundedWithUnit(MU unit, int digits = -1, bool i18n = false) const;
//! Value to QString with the current unit, e.g. "5.00m"
//! \note default digits is CMeasurementUnit::getDisplayDigits
QString valueRoundedWithUnit(int digits = -1, bool i18n = false) const;
//! Round current value in current unit to epsilon
//! \sa CMeasurementUnit::roundToEpsilon
void roundToEpsilon();
//! Change value without changing unit
void setValueSameUnit(double value);