8 #include <QStringBuilder>
14 using namespace swift::misc::aviation;
15 using namespace swift::misc::physical_quantities;
16 using namespace swift::misc::math;
20 namespace swift::misc::geo
22 ICoordinateGeodetic::~ICoordinateGeodetic() {}
24 QString CCoordinateGeodetic::convertToQString(
bool i18n)
const
26 return ICoordinateGeodetic::convertToQString(i18n);
29 CCoordinateGeodetic CCoordinateGeodetic::fromWgs84(
const QString &latitudeWgs84,
const QString &longitudeWgs84,
32 const CLatitude lat = CLatitude::fromWgs84(latitudeWgs84);
33 const CLongitude lon = CLongitude::fromWgs84(longitudeWgs84);
45 if (coordinate1.
isNull() || coordinate2.
isNull()) {
return CLength::null(); }
47 constexpr
float earthRadiusMeters = 6371000.8f;
51 Q_ASSERT_X(std::isfinite(v1.x()) && std::isfinite(v1.y()) && std::isfinite(v1.z()), Q_FUNC_INFO,
52 "Distance calculation: v1 non-finite argument");
53 Q_ASSERT_X(std::isfinite(v2.x()) && std::isfinite(v2.y()) && std::isfinite(v2.z()), Q_FUNC_INFO,
54 "Distance calculation: v2 non-finite argument");
57 earthRadiusMeters * std::atan2(QVector3D::crossProduct(v1, v2).length(), QVector3D::dotProduct(v1, v2));
59 SWIFT_VERIFY_X(!std::isnan(d), Q_FUNC_INFO,
"Distance calculation: NaN in result");
62 CLogMessage().
debug(u
"Distance calculation: NaN in result (given arguments %1 %2 %3; %4 %5 %6)")
63 <<
static_cast<double>(v1.x()) <<
static_cast<double>(v1.y()) <<
static_cast<double>(v1.z())
64 <<
static_cast<double>(v2.x()) <<
static_cast<double>(v2.y()) <<
static_cast<double>(v2.z());
65 return CLength::null();
67 return {
static_cast<double>(d), CLengthUnit::m() };
70 CAngle calculateBearing(
const ICoordinateGeodetic &coordinate1,
const ICoordinateGeodetic &coordinate2)
72 if (coordinate1.isNull() || coordinate2.isNull()) {
return CAngle::null(); }
75 static const QVector3D northPole { 0, 0, 1 };
76 const QVector3D c1 = QVector3D::crossProduct(coordinate1.normalVector(), coordinate2.normalVector());
77 const QVector3D c2 = QVector3D::crossProduct(coordinate1.normalVector(), northPole);
78 const QVector3D cross = QVector3D::crossProduct(c1, c2);
79 const float sinTheta = std::copysign(cross.length(), QVector3D::dotProduct(cross, coordinate1.normalVector()));
80 const float cosTheta = QVector3D::dotProduct(c1, c2);
81 const float theta = std::atan2(sinTheta, cosTheta);
82 return {
static_cast<double>(theta), CAngleUnit::rad() };
85 double calculateEuclideanDistance(
const ICoordinateGeodetic &coordinate1,
const ICoordinateGeodetic &coordinate2)
87 return static_cast<double>(coordinate1.normalVector().distanceToPoint(coordinate2.normalVector()));
90 double calculateEuclideanDistanceSquared(
const ICoordinateGeodetic &coordinate1,
91 const ICoordinateGeodetic &coordinate2)
93 return static_cast<double>((coordinate1.normalVector() - coordinate2.normalVector()).lengthSquared());
96 bool ICoordinateGeodetic::equalNormalVectorDouble(
const std::array<double, 3> &otherVector)
const
98 static const double epsilon = std::numeric_limits<double>::epsilon();
99 const std::array<double, 3> thisVector = this->normalVectorDouble();
100 for (
unsigned int i = 0; i < otherVector.size(); i++)
102 const double d = thisVector[i] - otherVector[i];
103 if (qAbs(d) > epsilon) {
return false; }
115 return geo::calculateGreatCircleDistance((*
this), otherCoordinate);
120 if (range.
isNull()) {
return false; }
121 const CLength distance = this->calculateGreatCircleDistance(otherCoordinate);
122 if (distance.
isNull()) {
return false; }
123 return distance <= range;
128 return geo::calculateBearing((*
this), otherCoordinate);
134 return (i >=
static_cast<int>(IndexLatitude)) && (i <=
static_cast<int>(IndexNormalVector));
144 case IndexLatitude:
return this->latitude().propertyByIndex(index.
copyFrontRemoved());
145 case IndexLongitude:
return this->longitude().propertyByIndex(index.
copyFrontRemoved());
146 case IndexLatitudeAsString:
return QVariant(this->latitudeAsString());
147 case IndexLongitudeAsString:
return QVariant(this->longitudeAsString());
148 case IndexGeodeticHeight:
return this->geodeticHeight().propertyByIndex(index.
copyFrontRemoved());
149 case IndexGeodeticHeightAsString:
return QVariant(this->geodeticHeightAsString());
150 case IndexNormalVector:
return QVariant::fromValue(this->normalVector());
155 const QString m = QString(
"no property, index ").append(index.
toQString());
157 return QVariant::fromValue(m);
172 case IndexLatitudeAsString:
return this->latitudeAsString().compare(compareValue.
latitudeAsString());
173 case IndexLongitudeAsString:
return this->longitudeAsString().compare(compareValue.
longitudeAsString());
174 case IndexGeodeticHeight:
175 return this->geodeticHeight().comparePropertyByIndex(index.
copyFrontRemoved(),
177 case IndexGeodeticHeightAsString:
183 const QString m = QString(
"no property, index ").append(index.
toQString());
188 QString ICoordinateGeodetic::convertToQString(
bool i18n)
const
192 return QStringLiteral(
"Geodetic: {%1/%2, %3/%4, %5}")
197 this->geodeticHeight().valueRoundedWithUnit(CLengthUnit::ft(), 2, i18n));
200 bool ICoordinateGeodetic::isNaNVector()
const
202 const QVector3D v = this->normalVector();
203 return std::isnan(v.x()) || std::isnan(v.y()) || std::isnan(v.z());
206 bool ICoordinateGeodetic::isNaNVectorDouble()
const
208 const std::array<double, 3> v = this->normalVectorDouble();
209 return std::isnan(v[0]) || std::isnan(v[1]) || std::isnan(v[2]);
212 bool ICoordinateGeodetic::isInfVector()
const
214 const QVector3D v = this->normalVector();
215 return std::isinf(v.x()) || std::isinf(v.y()) || std::isinf(v.z());
218 bool ICoordinateGeodetic::isInfVectorDouble()
const
220 const std::array<double, 3> v = this->normalVectorDouble();
221 return std::isinf(v[0]) || std::isinf(v[1]) || std::isinf(v[2]);
224 bool ICoordinateGeodetic::isValidVectorRange()
const
227 const std::array<double, 3> v = this->normalVectorDouble();
228 return isValidVector(v);
231 bool ICoordinateGeodetic::isValidVector(
const std::array<double, 3> &v)
233 constexpr
double l = 1.00001;
234 return v[0] <= l && v[1] <= l && v[2] <= l && v[0] >= -l && v[1] >= -l && v[2] >= -l;
237 int CCoordinateGeodetic::clampVector()
277 if (index.
isMyself()) {
return QVariant::fromValue(*
this); }
278 return (ICoordinateGeodetic::canHandleIndex(index)) ? ICoordinateGeodetic::propertyByIndex(index) :
292 case IndexGeodeticHeight: m_geodeticHeight.setPropertyByIndex(index.
copyFrontRemoved(), variant);
break;
293 case IndexLatitude: this->setLatitude(variant.value<
CLatitude>());
break;
294 case IndexLongitude: this->setLongitude(variant.value<
CLongitude>());
break;
295 case IndexLatitudeAsString: this->setLatitude(CLatitude::fromWgs84(variant.toString()));
break;
296 case IndexLongitudeAsString: this->setLongitude(CLongitude::fromWgs84(variant.toString()));
break;
297 case IndexGeodeticHeightAsString: m_geodeticHeight.parseFromString(variant.toString());
break;
298 case IndexNormalVector: this->setNormalVector(variant.value<QVector3D>());
break;
306 return ICoordinateGeodetic::canHandleIndex(index) ?
307 ICoordinateGeodetic::comparePropertyByIndex(index, compareValue) :
311 CCoordinateGeodetic::CCoordinateGeodetic(
const std::array<double, 3> &normalVector)
313 this->setNormalVector(normalVector);
324 : m_x(latitude.cos() * longitude.cos()), m_y(latitude.cos() * longitude.sin()), m_z(latitude.sin()),
325 m_geodeticHeight(geodeticHeight)
329 :
CCoordinateGeodetic({ latitudeDegrees, CAngleUnit::deg() }, { longitudeDegrees, CAngleUnit::deg() },
334 :
CCoordinateGeodetic({ latitudeDegrees, CAngleUnit::deg() }, { longitudeDegrees, CAngleUnit::deg() },
335 { heightFeet, CLengthUnit::ft() })
339 : m_geodeticHeight(coordinate.geodeticHeight())
356 constexpr
double earthRadiusMeters = 6371000.8;
357 const double startLatRad = this->
latitude().
value(CAngleUnit::rad());
358 const double startLngRad = this->
longitude().
value(CAngleUnit::rad());
359 const double bearingRad = relBearing.
value(CAngleUnit::rad());
360 const double distRatio = distance.
value(CLengthUnit::m()) / earthRadiusMeters;
362 const double newLatRad =
363 asin(sin(startLatRad) * cos(distRatio) + cos(startLatRad) * sin(distRatio) * cos(bearingRad));
364 double newLngRad = 0;
366 constexpr
double epsilon = 1E-06;
367 if (cos(newLatRad) == 0 || qAbs(cos(newLatRad)) < epsilon)
368 newLngRad = startLngRad;
372 newLngRad = startLngRad + atan2(sin(bearingRad) * sin(distRatio) * cos(startLatRad),
373 cos(distRatio) - sin(startLatRad) * sin(newLatRad));
374 newLngRad = fmod(newLngRad + 3 * M_PI, 2 * M_PI) - M_PI;
378 const CLatitude lat(newLatRad, CAngleUnit::rad());
379 const CLongitude lng(newLngRad, CAngleUnit::rad());
386 return { std::atan2(m_z, std::hypot(m_x, m_y)), CAngleUnit::rad() };
392 return { std::atan2(m_y, m_x), CAngleUnit::rad() };
397 return {
static_cast<float>(m_x),
static_cast<float>(m_y),
static_cast<float>(m_z) };
433 Q_ASSERT_X(
normalVector.size() == 3, Q_FUNC_INFO,
"Wrong vector size");
472 const QString m = QString(
"no property, index ").append(index.
toQString());
474 return QVariant::fromValue(m);
488 const QString m = QString(
"no property, index ").append(index.
toQString());
508 case IndexRelativeBearing:
511 case IndexRelativeDistance:
515 const QString m = QString(
"no property, index ").append(index.
toQString());
516 Q_ASSERT_X(
false, Q_FUNC_INFO, m.toLocal8Bit().constData());
536 return (i >=
static_cast<int>(IndexRelativeDistance)) && (i <=
static_cast<int>(IndexRelativeBearing));
Class for emitting a log message.
Derived & debug()
Set the severity to debug.
Non-owning reference to a CPropertyIndex with a subset of its features.
Q_REQUIRED_RESULT CPropertyIndexRef copyFrontRemoved() const
Copy with first element removed.
QString toQString(bool i18n=false) const
Cast as QString.
CastType frontCasted() const
First element casted to given type, usually the PropertIndex enum.
bool isMyself() const
Myself index, used with nesting.
Altitude as used in aviation, can be AGL or MSL altitude.
CAltitude & switchUnit(const physical_quantities::CLengthUnit &newUnit)
Value in switched unit.
void setLatLongFromWgs84(const QString &latitude, const QString &longitude)
Set latitude and longitude.
void setLatitude(const CLatitude &latitude)
Set latitude.
static const CCoordinateGeodetic & null()
null coordinate
virtual CLatitude latitude() const
Latitude.
virtual std::array< double, 3 > normalVectorDouble() const
Normal vector with double precision.
CCoordinateGeodetic()
Default constructor (null coordinate)
CCoordinateGeodetic calculatePosition(const physical_quantities::CLength &distance, const physical_quantities::CAngle &relBearing) const
Calculate a position in distance/bearing.
CCoordinateGeodetic & switchUnit(const physical_quantities::CLengthUnit &unit)
Switch unit of height.
virtual bool isNull() const
Is null?
void setGeodeticHeightToNull()
Set height to NULL.
void setGeodeticHeight(const aviation::CAltitude &height)
Set height (ellipsoidal or geodetic height)
void setLatLong(const CLatitude &latitude, const CLongitude &longitude)
Set latitude and longitude.
virtual QVector3D normalVector() const
Normal vector.
void setNormalVector(const QVector3D &normal)
Set normal vector.
void setLatitudeFromWgs84(const QString &wgs)
Set latitude.
void setLongitudeFromWgs84(const QString &wgs)
Set longitude.
virtual CLongitude longitude() const
Longitude.
void setLongitude(const CLongitude &longitude)
Set longitude.
static CLatitude fromWgs84(const QString &wgsCoordinate)
Latitude / Longitude from a WGS string such as.
Geodetic coordinate, a position in 3D space relative to the reference geoid.
virtual QVector3D normalVector() const =0
Normal vector.
int comparePropertyByIndex(CPropertyIndexRef index, const ICoordinateGeodetic &compareValue) const
Compare for index.
QString convertToQString(bool i18n=false) const
Cast as QString.
virtual CLongitude longitude() const =0
Longitude.
QVariant propertyByIndex(CPropertyIndexRef index) const
Property by index.
virtual std::array< double, 3 > normalVectorDouble() const =0
Normal vector with double precision.
ColumnIndex
Properties by index.
static bool canHandleIndex(CPropertyIndexRef index)
Can given index be handled?
QString longitudeAsString() const
Longitude as string.
virtual bool isNull() const
Is null, means vector x, y, z == 0.
virtual const aviation::CAltitude & geodeticHeight() const =0
Height, ellipsoidal or geodetic height (used in GPS)
QString latitudeAsString() const
Latitude as string.
QString geodeticHeightAsString() const
Height as string.
virtual CLatitude latitude() const =0
Latitude.
Interface (actually more an abstract class) of coordinates and relative position to something (normal...
const physical_quantities::CLength & getRelativeDistance() const
Get the distance.
static bool canHandleIndex(CPropertyIndexRef index)
Can given index be handled?
const physical_quantities::CAngle & getRelativeBearing() const
Get the relative bearing.
physical_quantities::CAngle m_relativeBearing
temporary stored value
QVariant propertyByIndex(CPropertyIndexRef index) const
Property by index.
physical_quantities::CLength calculcateAndUpdateRelativeDistance(const geo::ICoordinateGeodetic &position)
Calculcate distance, set it, and return distance.
physical_quantities::CLength m_relativeDistance
temporary stored value
physical_quantities::CLength calculcateAndUpdateRelativeDistanceAndBearing(const geo::ICoordinateGeodetic &position)
Calculcate distance and bearing to plane, set it, and return distance.
int comparePropertyByIndex(CPropertyIndexRef index, const ICoordinateWithRelativePosition &compareValue) const
Compare for index.
QString convertToQString(bool i18n=false) const
Cast as QString.
ICoordinateWithRelativePosition()
Constructor.
void setPropertyByIndex(CPropertyIndexRef index, const QVariant &variant)
Set property by index.
int comparePropertyByIndex(CPropertyIndexRef index, const Derived &compareValue) const
Compare for index.
void setPropertyByIndex(CPropertyIndexRef index, const QVariant &variant)
Set property by index.
QVariant propertyByIndex(CPropertyIndexRef index) const
Property by index.
QString toQString(bool i18n=false) const
Cast as QString.
Physical unit angle (radians, degrees)
double cos() const
Cosine of angle.
double sin() const
Sine of angle.
Physical unit length (length)
Specialized class for distance units (meter, foot, nautical miles).
bool isNegativeWithEpsilonConsidered() const
Value <= 0 epsilon considered.
bool isNull() const
Is quantity null?
void setPropertyByIndex(CPropertyIndexRef index, const QVariant &variant)
Set property by index.
int comparePropertyByIndex(CPropertyIndexRef index, const PQ &pq) const
Compare for index.
double value(MU unit) const
Value in given unit.
QVariant propertyByIndex(CPropertyIndexRef index) const
Property by index.
QString valueRoundedWithUnit(const MU &unit, int digits=-1, bool withGroupSeparator=false, bool i18n=false) const
Value to QString with the given unit, e.g. "5.00m".
bool isZeroEpsilonConsidered() const
Quantity value <= epsilon.
#define SWIFT_DEFINE_VALUEOBJECT_MIXINS(Namespace, Class)
Explicit template definition of mixins for a CValueObject subclass.
#define SWIFT_VERIFY_X(COND, WHERE, WHAT)
A weaker kind of assert.