mirror of
https://github.com/swift-project/pilotclient.git
synced 2026-04-20 20:40:29 +08:00
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:
committed by
Mathew Sutcliffe
parent
77546a46b1
commit
e55480737e
@@ -212,6 +212,13 @@ namespace BlackMisc
|
|||||||
CCoordinateGeodetic::CCoordinateGeodetic(double latitudeDegrees, double longitudeDegrees, double heightFeet) :
|
CCoordinateGeodetic::CCoordinateGeodetic(double latitudeDegrees, double longitudeDegrees, double heightFeet) :
|
||||||
CCoordinateGeodetic( { latitudeDegrees, BlackMisc::PhysicalQuantities::CAngleUnit::deg() }, { longitudeDegrees, BlackMisc::PhysicalQuantities::CAngleUnit::deg() }, { heightFeet, BlackMisc::PhysicalQuantities::CLengthUnit::ft() }) {}
|
CCoordinateGeodetic( { latitudeDegrees, BlackMisc::PhysicalQuantities::CAngleUnit::deg() }, { longitudeDegrees, BlackMisc::PhysicalQuantities::CAngleUnit::deg() }, { heightFeet, BlackMisc::PhysicalQuantities::CLengthUnit::ft() }) {}
|
||||||
|
|
||||||
|
CCoordinateGeodetic::CCoordinateGeodetic(const ICoordinateGeodetic &coordinate) :
|
||||||
|
m_x(coordinate.normalVectorDouble()[0]),
|
||||||
|
m_y(coordinate.normalVectorDouble()[1]),
|
||||||
|
m_z(coordinate.normalVectorDouble()[2]),
|
||||||
|
m_geodeticHeight(coordinate.geodeticHeight())
|
||||||
|
{ }
|
||||||
|
|
||||||
CLatitude CCoordinateGeodetic::latitude() const
|
CLatitude CCoordinateGeodetic::latitude() const
|
||||||
{
|
{
|
||||||
return { std::atan2(m_z, std::hypot(m_x, m_y)), PhysicalQuantities::CAngleUnit::rad() };
|
return { std::atan2(m_z, std::hypot(m_x, m_y)), PhysicalQuantities::CAngleUnit::rad() };
|
||||||
|
|||||||
@@ -187,8 +187,7 @@ namespace BlackMisc
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
//! Default constructor
|
//! Default constructor
|
||||||
CCoordinateGeodetic() :
|
CCoordinateGeodetic() : CCoordinateGeodetic(0, 0, 0) {}
|
||||||
CCoordinateGeodetic(0, 0, 0) {}
|
|
||||||
|
|
||||||
//! Constructor by normal vector
|
//! Constructor by normal vector
|
||||||
CCoordinateGeodetic(const QVector3D &normal) : m_x(normal.x()), m_y(normal.y()), m_z(normal.z()) {}
|
CCoordinateGeodetic(const QVector3D &normal) : m_x(normal.x()), m_y(normal.y()), m_z(normal.z()) {}
|
||||||
@@ -202,6 +201,9 @@ namespace BlackMisc
|
|||||||
//! Constructor by values
|
//! Constructor by values
|
||||||
CCoordinateGeodetic(double latitudeDegrees, double longitudeDegrees, double heightFeet);
|
CCoordinateGeodetic(double latitudeDegrees, double longitudeDegrees, double heightFeet);
|
||||||
|
|
||||||
|
//! Constructor by interface
|
||||||
|
CCoordinateGeodetic(const ICoordinateGeodetic &coordinate);
|
||||||
|
|
||||||
//! \copydoc ICoordinateGeodetic::latitude
|
//! \copydoc ICoordinateGeodetic::latitude
|
||||||
virtual CLatitude latitude() const override;
|
virtual CLatitude latitude() const override;
|
||||||
|
|
||||||
|
|||||||
@@ -17,11 +17,12 @@
|
|||||||
#include <QtGlobal>
|
#include <QtGlobal>
|
||||||
#include <QtMath>
|
#include <QtMath>
|
||||||
|
|
||||||
|
using namespace BlackMisc::PhysicalQuantities;
|
||||||
|
|
||||||
namespace BlackMisc
|
namespace BlackMisc
|
||||||
{
|
{
|
||||||
namespace Geo
|
namespace Geo
|
||||||
{
|
{
|
||||||
|
|
||||||
template <class LATorLON>
|
template <class LATorLON>
|
||||||
CEarthAngle<LATorLON> &CEarthAngle<LATorLON>::operator +=(const CEarthAngle &latOrLon)
|
CEarthAngle<LATorLON> &CEarthAngle<LATorLON>::operator +=(const CEarthAngle &latOrLon)
|
||||||
{
|
{
|
||||||
@@ -70,49 +71,64 @@ namespace BlackMisc
|
|||||||
template <class LATorLON>
|
template <class LATorLON>
|
||||||
LATorLON CEarthAngle<LATorLON>::fromWgs84(const QString &wgsCoordinate)
|
LATorLON CEarthAngle<LATorLON>::fromWgs84(const QString &wgsCoordinate)
|
||||||
{
|
{
|
||||||
// http://www.regular-expressions.info/floatingpoint.html
|
|
||||||
const QString wgs = wgsCoordinate.simplified().trimmed();
|
const QString wgs = wgsCoordinate.simplified().trimmed();
|
||||||
thread_local const QRegularExpression rx("([-+]?[0-9]*\\.?[0-9]+)");
|
if (wgs.isEmpty()) { return LATorLON(); }
|
||||||
qint32 deg = 0;
|
|
||||||
qint32 min = 0;
|
// support for 5deg, 1.2rad
|
||||||
double sec = 0.0;
|
if (CAngleUnit::deg().endsStringWithNameOrSymbol(wgs) || CAngleUnit::rad().endsStringWithNameOrSymbol(wgs))
|
||||||
double secFragment = 0.0;
|
|
||||||
int fragmentLength = 0;
|
|
||||||
int c = 0;
|
|
||||||
int pos = 0;
|
|
||||||
QRegularExpressionMatch match = rx.match(wgs, pos);
|
|
||||||
while (match.hasMatch())
|
|
||||||
{
|
{
|
||||||
QString cap = match.captured(1);
|
LATorLON latOrLon;
|
||||||
pos += match.capturedLength(1);
|
latOrLon.parseFromString(wgs);
|
||||||
|
return latOrLon;
|
||||||
|
}
|
||||||
|
|
||||||
|
// number only -> parsed as degrees
|
||||||
|
bool isDouble;
|
||||||
|
const double valueDegrees = wgs.toDouble(&isDouble);
|
||||||
|
if (isDouble)
|
||||||
|
{
|
||||||
|
CAngle a(valueDegrees, CAngleUnit::deg());
|
||||||
|
return LATorLON(a);
|
||||||
|
}
|
||||||
|
|
||||||
|
// http://www.regular-expressions.info/floatingpoint.html
|
||||||
|
thread_local const QRegularExpression rx("[+-]?\\d+(?:\\.\\d+)?");
|
||||||
|
int deg = 0;
|
||||||
|
int min = 0;
|
||||||
|
double sec = 0.0;
|
||||||
|
int c = 0;
|
||||||
|
QRegularExpressionMatchIterator i = rx.globalMatch(wgs);
|
||||||
|
while (i.hasNext() && c < 3)
|
||||||
|
{
|
||||||
|
const QRegularExpressionMatch match = i.next();
|
||||||
|
bool ok;
|
||||||
|
if (match.hasMatch())
|
||||||
|
{
|
||||||
|
const QString cap = match.captured(0);
|
||||||
switch (c++)
|
switch (c++)
|
||||||
{
|
{
|
||||||
case 0:
|
case 0:
|
||||||
deg = cap.toInt();
|
deg = cap.toInt(&ok);
|
||||||
break;
|
break;
|
||||||
case 1:
|
case 1:
|
||||||
min = cap.toInt();
|
min = cap.toInt(&ok);
|
||||||
break;
|
break;
|
||||||
case 2:
|
case 2:
|
||||||
sec = cap.toDouble();
|
sec = cap.toDouble(&ok);
|
||||||
break;
|
|
||||||
case 3:
|
|
||||||
secFragment = cap.toDouble();
|
|
||||||
fragmentLength = cap.length();
|
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
match = rx.match(wgs, pos);
|
|
||||||
}
|
}
|
||||||
if (fragmentLength > 0)
|
Q_UNUSED(ok); // ok for debugging purposes
|
||||||
{
|
|
||||||
// we do have given ms in string
|
|
||||||
sec += secFragment / qPow(10, fragmentLength);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (wgs.contains('S', Qt::CaseInsensitive) ||
|
if (wgs.contains('S', Qt::CaseInsensitive) || wgs.contains('W', Qt::CaseInsensitive))
|
||||||
wgs.contains('W', Qt::CaseInsensitive)) deg *= -1;
|
{
|
||||||
|
deg *= -1;
|
||||||
|
min *= -1;
|
||||||
|
sec *= -1;
|
||||||
|
}
|
||||||
|
|
||||||
PhysicalQuantities::CAngle a(deg, min, sec);
|
PhysicalQuantities::CAngle a(deg, min, sec);
|
||||||
return LATorLON(a);
|
return LATorLON(a);
|
||||||
@@ -151,6 +167,19 @@ namespace BlackMisc
|
|||||||
return BlackMisc::CIcon::iconByIndex(CIcons::GeoPosition);
|
return BlackMisc::CIcon::iconByIndex(CIcons::GeoPosition);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<class LATorLON>
|
||||||
|
QString CEarthAngle<LATorLON>::toWgs84(const QChar pos, const QChar neg, int fractionalDigits) const
|
||||||
|
{
|
||||||
|
const CAngle::DegMinSecFractionalSec v = this->asSexagesimalDegMinSec(true);
|
||||||
|
const QChar pn = v.sign < 0 ? neg : pos;
|
||||||
|
|
||||||
|
static const QString vs("%1° %2' %3\" %4");
|
||||||
|
if (fractionalDigits < 1) { return vs.arg(v.deg).arg(v.min).arg(v.sec).arg(pn); }
|
||||||
|
|
||||||
|
static const QString vsf("%1° %2' %3.%4\" %5");
|
||||||
|
return vsf.arg(v.deg).arg(v.min).arg(v.sec).arg(v.fractionalSecAsString(fractionalDigits)).arg(pn);
|
||||||
|
}
|
||||||
|
|
||||||
// see here for the reason of thess forward instantiations
|
// see here for the reason of thess forward instantiations
|
||||||
// https://isocpp.org/wiki/faq/templates#separate-template-fn-defn-from-decl
|
// https://isocpp.org/wiki/faq/templates#separate-template-fn-defn-from-decl
|
||||||
//! \cond PRIVATE
|
//! \cond PRIVATE
|
||||||
|
|||||||
@@ -18,7 +18,6 @@ namespace BlackMisc
|
|||||||
{
|
{
|
||||||
namespace Geo
|
namespace Geo
|
||||||
{
|
{
|
||||||
|
|
||||||
class CLatitude;
|
class CLatitude;
|
||||||
class CLongitude;
|
class CLongitude;
|
||||||
|
|
||||||
@@ -66,6 +65,9 @@ namespace BlackMisc
|
|||||||
//! Init by CAngle value
|
//! Init by CAngle value
|
||||||
CEarthAngle(const BlackMisc::PhysicalQuantities::CAngle &angle);
|
CEarthAngle(const BlackMisc::PhysicalQuantities::CAngle &angle);
|
||||||
|
|
||||||
|
//! To WGS84 string
|
||||||
|
QString toWgs84(const QChar pos, const QChar neg, int fractionalDigits = 3) const;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
//! \copydoc BlackMisc::Mixin::String::toQString
|
//! \copydoc BlackMisc::Mixin::String::toQString
|
||||||
QString convertToQString(bool i18n = false) const;
|
QString convertToQString(bool i18n = false) const;
|
||||||
@@ -82,8 +84,7 @@ namespace BlackMisc
|
|||||||
extern template class BLACKMISC_EXPORT_DECLARE_TEMPLATE CEarthAngle<CLatitude>;
|
extern template class BLACKMISC_EXPORT_DECLARE_TEMPLATE CEarthAngle<CLatitude>;
|
||||||
extern template class BLACKMISC_EXPORT_DECLARE_TEMPLATE CEarthAngle<CLongitude>;
|
extern template class BLACKMISC_EXPORT_DECLARE_TEMPLATE CEarthAngle<CLongitude>;
|
||||||
//! \endcond
|
//! \endcond
|
||||||
|
} // ns
|
||||||
}
|
} // ns
|
||||||
}
|
|
||||||
|
|
||||||
#endif // guard
|
#endif // guard
|
||||||
|
|||||||
@@ -19,7 +19,6 @@ namespace BlackMisc
|
|||||||
{
|
{
|
||||||
namespace Geo
|
namespace Geo
|
||||||
{
|
{
|
||||||
|
|
||||||
//! Latitude
|
//! Latitude
|
||||||
class BLACKMISC_EXPORT CLatitude :
|
class BLACKMISC_EXPORT CLatitude :
|
||||||
public CEarthAngle<CLatitude>,
|
public CEarthAngle<CLatitude>,
|
||||||
@@ -36,6 +35,12 @@ namespace BlackMisc
|
|||||||
BLACKMISC_DECLARE_USING_MIXIN_STRING(CLatitude)
|
BLACKMISC_DECLARE_USING_MIXIN_STRING(CLatitude)
|
||||||
BLACKMISC_DECLARE_USING_MIXIN_INDEX(CLatitude)
|
BLACKMISC_DECLARE_USING_MIXIN_INDEX(CLatitude)
|
||||||
|
|
||||||
|
//! To WGS84 string
|
||||||
|
QString toWgs84(int fractionalDigits = 3) const
|
||||||
|
{
|
||||||
|
return CEarthAngle<CLatitude>::toWgs84('N', 'S', fractionalDigits);
|
||||||
|
}
|
||||||
|
|
||||||
//! \copydoc BlackMisc::Mixin::String::toQString
|
//! \copydoc BlackMisc::Mixin::String::toQString
|
||||||
QString convertToQString(bool i18n = false) const
|
QString convertToQString(bool i18n = false) const
|
||||||
{
|
{
|
||||||
@@ -54,11 +59,11 @@ namespace BlackMisc
|
|||||||
explicit CLatitude(const BlackMisc::PhysicalQuantities::CAngle &angle) : CEarthAngle(angle) {}
|
explicit CLatitude(const BlackMisc::PhysicalQuantities::CAngle &angle) : CEarthAngle(angle) {}
|
||||||
|
|
||||||
//! Init by double value
|
//! Init by double value
|
||||||
|
//! \remark Latitude measurements range from 0° to (+/–)90°
|
||||||
CLatitude(double value, const BlackMisc::PhysicalQuantities::CAngleUnit &unit) : CEarthAngle(value, unit) {}
|
CLatitude(double value, const BlackMisc::PhysicalQuantities::CAngleUnit &unit) : CEarthAngle(value, unit) {}
|
||||||
};
|
};
|
||||||
|
} // ns
|
||||||
}
|
} // ns
|
||||||
}
|
|
||||||
|
|
||||||
Q_DECLARE_METATYPE(BlackMisc::Geo::CLatitude)
|
Q_DECLARE_METATYPE(BlackMisc::Geo::CLatitude)
|
||||||
|
|
||||||
|
|||||||
@@ -19,7 +19,6 @@ namespace BlackMisc
|
|||||||
{
|
{
|
||||||
namespace Geo
|
namespace Geo
|
||||||
{
|
{
|
||||||
|
|
||||||
//! Longitude
|
//! Longitude
|
||||||
class BLACKMISC_EXPORT CLongitude :
|
class BLACKMISC_EXPORT CLongitude :
|
||||||
public CEarthAngle<CLongitude>,
|
public CEarthAngle<CLongitude>,
|
||||||
@@ -36,12 +35,20 @@ namespace BlackMisc
|
|||||||
BLACKMISC_DECLARE_USING_MIXIN_STRING(CLongitude)
|
BLACKMISC_DECLARE_USING_MIXIN_STRING(CLongitude)
|
||||||
BLACKMISC_DECLARE_USING_MIXIN_INDEX(CLongitude)
|
BLACKMISC_DECLARE_USING_MIXIN_INDEX(CLongitude)
|
||||||
|
|
||||||
|
//! To WGS84 string
|
||||||
|
QString toWgs84(int withFragmentSecDigits = 3) const
|
||||||
|
{
|
||||||
|
return CEarthAngle<CLongitude>::toWgs84('E', 'W', withFragmentSecDigits);
|
||||||
|
}
|
||||||
|
|
||||||
//! \copydoc BlackMisc::Mixin::String::toQString
|
//! \copydoc BlackMisc::Mixin::String::toQString
|
||||||
QString convertToQString(bool i18n = false) const
|
QString convertToQString(bool i18n = false) const
|
||||||
{
|
{
|
||||||
QString s(CEarthAngle::convertToQString(i18n));
|
QString s(CEarthAngle::convertToQString(i18n));
|
||||||
if (!this->isZeroEpsilonConsidered())
|
if (!this->isZeroEpsilonConsidered())
|
||||||
|
{
|
||||||
s.append(this->isNegativeWithEpsilonConsidered() ? " W" : " E");
|
s.append(this->isNegativeWithEpsilonConsidered() ? " W" : " E");
|
||||||
|
}
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -52,11 +59,11 @@ namespace BlackMisc
|
|||||||
explicit CLongitude(const BlackMisc::PhysicalQuantities::CAngle &angle) : CEarthAngle(angle) {}
|
explicit CLongitude(const BlackMisc::PhysicalQuantities::CAngle &angle) : CEarthAngle(angle) {}
|
||||||
|
|
||||||
//! Init by double value
|
//! Init by double value
|
||||||
|
//! Longitude measurements range from 0° to (+/–)180°.
|
||||||
CLongitude(double value, const BlackMisc::PhysicalQuantities::CAngleUnit &unit) : CEarthAngle(value, unit) {}
|
CLongitude(double value, const BlackMisc::PhysicalQuantities::CAngleUnit &unit) : CEarthAngle(value, unit) {}
|
||||||
};
|
};
|
||||||
|
} // ns
|
||||||
}
|
} // ns
|
||||||
}
|
|
||||||
|
|
||||||
Q_DECLARE_METATYPE(BlackMisc::Geo::CLongitude)
|
Q_DECLARE_METATYPE(BlackMisc::Geo::CLongitude)
|
||||||
|
|
||||||
|
|||||||
@@ -53,10 +53,12 @@ namespace BlackMisc
|
|||||||
|
|
||||||
double CMathUtils::roundEpsilon(double value, double epsilon)
|
double CMathUtils::roundEpsilon(double value, double epsilon)
|
||||||
{
|
{
|
||||||
|
if (epsilon == 0) { return value; } // avoid division by 0
|
||||||
double fractpart, intpart;
|
double fractpart, intpart;
|
||||||
fractpart = modf(value, &intpart);
|
fractpart = modf(value, &intpart);
|
||||||
if (fractpart == 0) return value; // do not mess any "integers" to the worse
|
if (fractpart == 0) { return value; } // do not mess any "integers" to the worse
|
||||||
qint64 ri = qRound(value / epsilon);
|
const double roundValue = value / epsilon;
|
||||||
|
qint64 ri = qRound64(roundValue);
|
||||||
double rv = static_cast<double>(ri) * epsilon; // do not loose any range here
|
double rv = static_cast<double>(ri) * epsilon; // do not loose any range here
|
||||||
return rv;
|
return rv;
|
||||||
}
|
}
|
||||||
@@ -113,5 +115,15 @@ namespace BlackMisc
|
|||||||
return multiplier * divisor;
|
return multiplier * divisor;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QString CMathUtils::fractionalPartAsString(double value, int width)
|
||||||
|
{
|
||||||
|
double intpart;
|
||||||
|
const double fractpart = modf(value, &intpart);
|
||||||
|
const QString f = QString::number(fractpart);
|
||||||
|
const QString fInt = f.length() < 3 ? QString("0") : f.mid(2);
|
||||||
|
if (width < 0) { return fInt; }
|
||||||
|
if (fInt.length() >= width) { return fInt.left(width); }
|
||||||
|
return fInt.leftJustified(width, '0');
|
||||||
|
}
|
||||||
} // namespace
|
} // namespace
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|||||||
@@ -21,7 +21,6 @@ namespace BlackMisc
|
|||||||
{
|
{
|
||||||
namespace Math
|
namespace Math
|
||||||
{
|
{
|
||||||
|
|
||||||
//! Math utils
|
//! Math utils
|
||||||
class BLACKMISC_EXPORT CMathUtils
|
class BLACKMISC_EXPORT CMathUtils
|
||||||
{
|
{
|
||||||
@@ -103,10 +102,13 @@ namespace BlackMisc
|
|||||||
//! Random number between low and high
|
//! Random number between low and high
|
||||||
static int randomInteger(int low, int high);
|
static int randomInteger(int low, int high);
|
||||||
|
|
||||||
//! Round numToRound to the nearest multiple of multiple
|
//! Round numToRound to the nearest multiple of divisor
|
||||||
static int roundToMultipleOf(int value, int divisor);
|
static int roundToMultipleOf(int value, int divisor);
|
||||||
};
|
|
||||||
|
|
||||||
|
//! Fractional part as integer string, e.g. 3.12 -> 12 / 3.012 -> 012
|
||||||
|
//! \remark because of leading 0 returned as string
|
||||||
|
static QString fractionalPartAsString(double value, int width = -1);
|
||||||
|
};
|
||||||
} // namespace
|
} // namespace
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
|
|||||||
@@ -14,10 +14,60 @@
|
|||||||
|
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
|
|
||||||
|
using namespace BlackMisc::Math;
|
||||||
|
|
||||||
namespace BlackMisc
|
namespace BlackMisc
|
||||||
{
|
{
|
||||||
namespace PhysicalQuantities
|
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 °rees, 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 °rees, int &minutes)
|
||||||
|
{
|
||||||
|
if ((degrees > 0 && minutes < 0) || (degrees < 0 && minutes > 0))
|
||||||
|
{
|
||||||
|
minutes *= -1.0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
BlackMisc::CIcon CAngle::toIcon() const
|
BlackMisc::CIcon CAngle::toIcon() const
|
||||||
{
|
{
|
||||||
BlackMisc::CIcon i = CIcon::iconByIndex(CIcons::StandardIconArrowMediumNorth16);
|
BlackMisc::CIcon i = CIcon::iconByIndex(CIcons::StandardIconArrowMediumNorth16);
|
||||||
@@ -25,6 +75,37 @@ namespace BlackMisc
|
|||||||
return i;
|
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
|
double CAngle::piFactor() const
|
||||||
{
|
{
|
||||||
return BlackMisc::Math::CMathUtils::round(this->value(CAngleUnit::rad()) / BlackMisc::Math::CMathUtils::PI() , 6);
|
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()));
|
return std::tan(this->value(CAngleUnit::rad()));
|
||||||
}
|
}
|
||||||
}
|
} // ns
|
||||||
}
|
} // ns
|
||||||
|
|||||||
@@ -25,7 +25,6 @@ namespace BlackMisc
|
|||||||
{
|
{
|
||||||
namespace PhysicalQuantities
|
namespace PhysicalQuantities
|
||||||
{
|
{
|
||||||
|
|
||||||
//! Physical unit angle (radians, degrees)
|
//! Physical unit angle (radians, degrees)
|
||||||
class BLACKMISC_EXPORT CAngle : public CPhysicalQuantity<CAngleUnit, CAngle>
|
class BLACKMISC_EXPORT CAngle : public CPhysicalQuantity<CAngleUnit, CAngle>
|
||||||
{
|
{
|
||||||
@@ -39,27 +38,50 @@ namespace BlackMisc
|
|||||||
//! \copydoc CPhysicalQuantity(const QString &unitString)
|
//! \copydoc CPhysicalQuantity(const QString &unitString)
|
||||||
CAngle(const QString &unitString) : CPhysicalQuantity(unitString) {}
|
CAngle(const QString &unitString) : CPhysicalQuantity(unitString) {}
|
||||||
|
|
||||||
/*!
|
//! Value as individual values
|
||||||
* \brief Init as sexagesimal degrees, minutes, seconds
|
struct DegMinSecFractionalSec
|
||||||
* The sign of all parameters must be the same, either all positive or all negative.
|
{
|
||||||
*/
|
int sign = 1; //!< 1/-1
|
||||||
CAngle(int degrees, int minutes, double seconds) :
|
int deg = 0; //!< 0-359
|
||||||
CPhysicalQuantity(
|
int min = 0; //!< 0-59
|
||||||
degrees + minutes / 100.0 + seconds / 10000.0,
|
int sec = 0; //!< 0-59
|
||||||
CAngleUnit::sexagesimalDeg()) {}
|
double fractionalSec = 0; //!< value < 1.0
|
||||||
|
|
||||||
/*!
|
//! Degrees as string
|
||||||
* \brief Init as sexagesimal degrees, minutes
|
QString degAsString() const { return QString::number(deg); }
|
||||||
* The sign of both parameters must be the same, either both positive or both negative.
|
|
||||||
*/
|
//! Minutes as string
|
||||||
CAngle(int degrees, double minutes) :
|
QString minAsString() const { return QString::number(min); }
|
||||||
CPhysicalQuantity(
|
|
||||||
degrees + minutes / 100.0,
|
//! Seconds as string
|
||||||
CAngleUnit::sexagesimalDegMin()) {}
|
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 °rees, int &minutes, double &seconds);
|
||||||
|
|
||||||
|
//! Minutes will get same sign as degrees
|
||||||
|
void static unifySign(int °rees, int &minutes);
|
||||||
|
|
||||||
//! \copydoc BlackMisc::Mixin::Icon::toIcon
|
//! \copydoc BlackMisc::Mixin::Icon::toIcon
|
||||||
BlackMisc::CIcon toIcon() const;
|
BlackMisc::CIcon toIcon() const;
|
||||||
|
|
||||||
|
//! As individual values
|
||||||
|
DegMinSecFractionalSec asSexagesimalDegMinSec(bool range180Degrees = false) const;
|
||||||
|
|
||||||
//! Value as factor of PI (e.g. 0.5PI)
|
//! Value as factor of PI (e.g. 0.5PI)
|
||||||
double piFactor() const;
|
double piFactor() const;
|
||||||
|
|
||||||
@@ -75,8 +97,8 @@ namespace BlackMisc
|
|||||||
//! Tangent of angle
|
//! Tangent of angle
|
||||||
double tan() const;
|
double tan() const;
|
||||||
};
|
};
|
||||||
}
|
} // ns
|
||||||
}
|
} // ns
|
||||||
|
|
||||||
Q_DECLARE_METATYPE(BlackMisc::PhysicalQuantities::CAngle)
|
Q_DECLARE_METATYPE(BlackMisc::PhysicalQuantities::CAngle)
|
||||||
|
|
||||||
|
|||||||
@@ -17,7 +17,6 @@ namespace BlackMisc
|
|||||||
{
|
{
|
||||||
namespace PhysicalQuantities
|
namespace PhysicalQuantities
|
||||||
{
|
{
|
||||||
|
|
||||||
bool CMeasurementUnit::operator ==(const CMeasurementUnit &other) const
|
bool CMeasurementUnit::operator ==(const CMeasurementUnit &other) const
|
||||||
{
|
{
|
||||||
if (this == &other) return true;
|
if (this == &other) return true;
|
||||||
@@ -43,17 +42,23 @@ namespace BlackMisc
|
|||||||
|
|
||||||
double CMeasurementUnit::roundValue(double value, int digits) const
|
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);
|
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;
|
if (this->getEpsilon() == 0 || this->isNull()) { return value; }
|
||||||
double v = CMathUtils::round(value, digits);
|
return CMathUtils::roundEpsilon(value, this->getEpsilon());
|
||||||
QString s = QLocale::system().toString(v, 'f', digits);
|
|
||||||
return s;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|||||||
@@ -36,7 +36,6 @@ namespace BlackMisc
|
|||||||
{
|
{
|
||||||
namespace PhysicalQuantities
|
namespace PhysicalQuantities
|
||||||
{
|
{
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* Base class for all units, such as meter, hertz.
|
* Base class for all units, such as meter, hertz.
|
||||||
*/
|
*/
|
||||||
@@ -291,16 +290,31 @@ namespace BlackMisc
|
|||||||
return i18n ? QCoreApplication::translate("CMeasurementUnit", this->m_data->m_symbol.latin1()) : this->m_data->m_symbol;
|
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
|
//! Rounded value
|
||||||
|
//! \note default digits is CMeasurementUnit::getDisplayDigits
|
||||||
double roundValue(double value, int digits = -1) const;
|
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
|
//! 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;
|
virtual QString makeRoundedQString(double value, int digits = -1, bool i18n = false) const;
|
||||||
|
|
||||||
//! Value rounded with unit, e.g. "5.00m", "30kHz"
|
//! 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;
|
virtual QString makeRoundedQStringWithUnit(double value, int digits = -1, bool i18n = false) const;
|
||||||
|
|
||||||
//! Threshold for rounding
|
//! Threshold for comparions
|
||||||
double getEpsilon() const
|
double getEpsilon() const
|
||||||
{
|
{
|
||||||
return this->m_data->m_epsilon;
|
return this->m_data->m_epsilon;
|
||||||
@@ -371,8 +385,7 @@ namespace BlackMisc
|
|||||||
return none;
|
return none;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
} // ns
|
||||||
}
|
} // ns
|
||||||
}
|
|
||||||
|
|
||||||
#endif // guard
|
#endif // guard
|
||||||
|
|||||||
@@ -300,6 +300,13 @@ namespace BlackMisc
|
|||||||
return this->valueRoundedWithUnit(this->m_unit, digits, i18n);
|
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>
|
template <class MU, class PQ>
|
||||||
double CPhysicalQuantity<MU, PQ>::valueRounded(MU unit, int digits) const
|
double CPhysicalQuantity<MU, PQ>::valueRounded(MU unit, int digits) const
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -101,20 +101,28 @@ namespace BlackMisc
|
|||||||
void setCurrentUnitValue(double value);
|
void setCurrentUnitValue(double value);
|
||||||
|
|
||||||
//! Rounded value in given unit
|
//! Rounded value in given unit
|
||||||
|
//! \note default digits is CMeasurementUnit::getDisplayDigits
|
||||||
double valueRounded(MU unit, int digits = -1) const;
|
double valueRounded(MU unit, int digits = -1) const;
|
||||||
|
|
||||||
//! As integer value
|
//! As integer value
|
||||||
int valueInteger(MU unit) const;
|
int valueInteger(MU unit) const;
|
||||||
|
|
||||||
//! Rounded value in current unit
|
//! Rounded value in current unit
|
||||||
|
//! \note default digits is CMeasurementUnit::getDisplayDigits
|
||||||
double valueRounded(int digits = -1) const;
|
double valueRounded(int digits = -1) const;
|
||||||
|
|
||||||
//! Value to QString with the given unit, e.g. "5.00m"
|
//! 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;
|
QString valueRoundedWithUnit(MU unit, int digits = -1, bool i18n = false) const;
|
||||||
|
|
||||||
//! Value to QString with the current unit, e.g. "5.00m"
|
//! 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;
|
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
|
//! Change value without changing unit
|
||||||
void setValueSameUnit(double value);
|
void setValueSameUnit(double value);
|
||||||
|
|
||||||
|
|||||||
@@ -31,10 +31,25 @@ namespace BlackMiscTest
|
|||||||
{
|
{
|
||||||
void CTestGeo::geoBasics()
|
void CTestGeo::geoBasics()
|
||||||
{
|
{
|
||||||
CLatitude lati(10, CAngleUnit::deg());
|
CLatitude lat(10, CAngleUnit::deg());
|
||||||
QVERIFY2(lati * 2 == lati + lati, "Latitude addition should be equal");
|
QVERIFY2(lat * 2 == lat + lat, "Latitude addition should be equal");
|
||||||
lati += CLatitude(20, CAngleUnit::deg());
|
lat += CLatitude(20, CAngleUnit::deg());
|
||||||
QVERIFY2(lati.valueRounded() == 30.0, "Latitude should be 30 degrees");
|
QVERIFY2(lat.valueRounded() == 30.0, "Latitude should be 30 degrees");
|
||||||
|
|
||||||
|
CAngle a(20, 0);
|
||||||
|
lat = CLatitude(a);
|
||||||
|
double v = lat.valueRounded(CAngleUnit::deg());
|
||||||
|
QVERIFY2(v == 20.0, "Values shall be the same");
|
||||||
|
|
||||||
|
a = CAngle(28, 0);
|
||||||
|
lat = CLatitude(a);
|
||||||
|
v = lat.valueRounded(CAngleUnit::deg());
|
||||||
|
QVERIFY2(v == 28.0, "Values shall be the same");
|
||||||
|
|
||||||
|
a = CAngle(30, 0, 0);
|
||||||
|
lat = CLatitude(a);
|
||||||
|
v = lat.valueRounded(CAngleUnit::deg());
|
||||||
|
QVERIFY2(v == 30.0, "Values shall be the same");
|
||||||
}
|
}
|
||||||
|
|
||||||
void CTestGeo::coordinateGeodetic()
|
void CTestGeo::coordinateGeodetic()
|
||||||
@@ -44,7 +59,17 @@ namespace BlackMiscTest
|
|||||||
QCOMPARE(calculateEuclideanDistance(northPole, southPole), 2.0);
|
QCOMPARE(calculateEuclideanDistance(northPole, southPole), 2.0);
|
||||||
CCoordinateGeodetic equator = { 0.0, 70.354683 };
|
CCoordinateGeodetic equator = { 0.0, 70.354683 };
|
||||||
QCOMPARE(calculateEuclideanDistance(northPole, equator), std::sqrt(2.0f));
|
QCOMPARE(calculateEuclideanDistance(northPole, equator), std::sqrt(2.0f));
|
||||||
|
|
||||||
|
CCoordinateGeodetic testCoordinate = northPole;
|
||||||
|
double latValue = testCoordinate.latitude().value(CAngleUnit::deg());
|
||||||
|
double lngValue = testCoordinate.longitude().value(CAngleUnit::deg());
|
||||||
|
QVERIFY2(latValue == 90.0, "Latitude value supposed to be 90");
|
||||||
|
QVERIFY2(lngValue == 0.0, "Longitude value supposed to be 0");
|
||||||
|
CLatitude newLat(90.0, CAngleUnit::deg());
|
||||||
|
testCoordinate.setLatitude(newLat);
|
||||||
|
latValue = testCoordinate.latitude().value(CAngleUnit::deg());
|
||||||
|
QVERIFY2(latValue == newLat.value(CAngleUnit::deg()), "Latitude value supposed to be equal");
|
||||||
}
|
}
|
||||||
} // namespace
|
} // ns
|
||||||
|
|
||||||
//! \endcond
|
//! \endcond
|
||||||
|
|||||||
Reference in New Issue
Block a user