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 QString CCoordinateGeodetic::convertToQString(
bool i18n)
const
24 return ICoordinateGeodetic::convertToQString(i18n);
30 const CLatitude lat = CLatitude::fromWgs84(latitudeWgs84);
31 const CLongitude lon = CLongitude::fromWgs84(longitudeWgs84);
32 return { lat, lon, geodeticHeight };
43 if (coordinate1.
isNull() || coordinate2.
isNull()) {
return CLength::null(); }
45 constexpr
float earthRadiusMeters = 6371000.8f;
49 Q_ASSERT_X(std::isfinite(v1.
x()) && std::isfinite(v1.
y()) && std::isfinite(v1.
z()), Q_FUNC_INFO,
50 "Distance calculation: v1 non-finite argument");
51 Q_ASSERT_X(std::isfinite(v2.
x()) && std::isfinite(v2.
y()) && std::isfinite(v2.
z()), Q_FUNC_INFO,
52 "Distance calculation: v2 non-finite argument");
57 SWIFT_VERIFY_X(!std::isnan(d), Q_FUNC_INFO,
"Distance calculation: NaN in result");
60 CLogMessage().
debug(u
"Distance calculation: NaN in result (given arguments %1 %2 %3; %4 %5 %6)")
61 <<
static_cast<double>(v1.
x()) <<
static_cast<double>(v1.
y()) <<
static_cast<double>(v1.
z())
62 <<
static_cast<double>(v2.
x()) <<
static_cast<double>(v2.
y()) <<
static_cast<double>(v2.
z());
63 return CLength::null();
65 return {
static_cast<double>(d), CLengthUnit::m() };
68 CAngle calculateBearing(
const ICoordinateGeodetic &coordinate1,
const ICoordinateGeodetic &coordinate2)
70 if (coordinate1.isNull() || coordinate2.isNull()) {
return CAngle::null(); }
73 static const QVector3D northPole { 0, 0, 1 };
79 const float theta = std::atan2(sinTheta, cosTheta);
80 return {
static_cast<double>(theta), CAngleUnit::rad() };
83 double calculateEuclideanDistance(
const ICoordinateGeodetic &coordinate1,
const ICoordinateGeodetic &coordinate2)
85 return static_cast<double>(coordinate1.normalVector().distanceToPoint(coordinate2.normalVector()));
88 double calculateEuclideanDistanceSquared(
const ICoordinateGeodetic &coordinate1,
89 const ICoordinateGeodetic &coordinate2)
91 return static_cast<double>((coordinate1.normalVector() - coordinate2.normalVector()).lengthSquared());
94 bool ICoordinateGeodetic::equalNormalVectorDouble(
const std::array<double, 3> &otherVector)
const
96 static const double epsilon = std::numeric_limits<double>::epsilon();
97 const std::array<double, 3> thisVector = this->normalVectorDouble();
98 for (
unsigned int i = 0; i < otherVector.size(); i++)
100 const double d = thisVector[i] - otherVector[i];
101 if (qAbs(d) > epsilon) {
return false; }
113 return geo::calculateGreatCircleDistance((*
this), otherCoordinate);
118 if (range.
isNull()) {
return false; }
119 const CLength distance = this->calculateGreatCircleDistance(otherCoordinate);
120 if (distance.
isNull()) {
return false; }
121 return distance <= range;
126 return geo::calculateBearing((*
this), otherCoordinate);
132 return (i >=
static_cast<int>(IndexLatitude)) && (i <=
static_cast<int>(IndexNormalVector));
142 case IndexLatitude:
return this->latitude().propertyByIndex(index.
copyFrontRemoved());
143 case IndexLongitude:
return this->longitude().propertyByIndex(index.
copyFrontRemoved());
144 case IndexLatitudeAsString:
return { this->latitudeAsString() };
145 case IndexLongitudeAsString:
return { this->longitudeAsString() };
146 case IndexGeodeticHeight:
return this->geodeticHeight().propertyByIndex(index.
copyFrontRemoved());
147 case IndexGeodeticHeightAsString:
return { this->geodeticHeightAsString() };
170 case IndexLatitudeAsString:
return this->latitudeAsString().compare(compareValue.
latitudeAsString());
171 case IndexLongitudeAsString:
return this->longitudeAsString().compare(compareValue.
longitudeAsString());
172 case IndexGeodeticHeight:
173 return this->geodeticHeight().comparePropertyByIndex(index.
copyFrontRemoved(),
175 case IndexGeodeticHeightAsString:
186 QString ICoordinateGeodetic::convertToQString(
bool i18n)
const
190 return QStringLiteral(
"Geodetic: {%1/%2, %3/%4, %5}")
195 this->geodeticHeight().valueRoundedWithUnit(CLengthUnit::ft(), 2, i18n));
198 bool ICoordinateGeodetic::isNaNVector()
const
200 const QVector3D v = this->normalVector();
201 return std::isnan(v.
x()) || std::isnan(v.
y()) || std::isnan(v.
z());
204 bool ICoordinateGeodetic::isNaNVectorDouble()
const
206 const std::array<double, 3> v = this->normalVectorDouble();
207 return std::isnan(v[0]) || std::isnan(v[1]) || std::isnan(v[2]);
210 bool ICoordinateGeodetic::isInfVector()
const
212 const QVector3D v = this->normalVector();
213 return std::isinf(v.
x()) || std::isinf(v.
y()) || std::isinf(v.
z());
216 bool ICoordinateGeodetic::isInfVectorDouble()
const
218 const std::array<double, 3> v = this->normalVectorDouble();
219 return std::isinf(v[0]) || std::isinf(v[1]) || std::isinf(v[2]);
222 bool ICoordinateGeodetic::isValidVectorRange()
const
225 const std::array<double, 3> v = this->normalVectorDouble();
226 return isValidVector(v);
229 bool ICoordinateGeodetic::isValidVector(
const std::array<double, 3> &v)
231 constexpr
double l = 1.00001;
232 return v[0] <= l && v[1] <= l && v[2] <= l && v[0] >= -l && v[1] >= -l && v[2] >= -l;
235 int CCoordinateGeodetic::clampVector()
274 return (ICoordinateGeodetic::canHandleIndex(index)) ? ICoordinateGeodetic::propertyByIndex(index) :
288 case IndexGeodeticHeight: m_geodeticHeight.setPropertyByIndex(index.
copyFrontRemoved(), variant);
break;
289 case IndexLatitude: this->setLatitude(variant.
value<
CLatitude>());
break;
290 case IndexLongitude: this->setLongitude(variant.
value<
CLongitude>());
break;
291 case IndexLatitudeAsString: this->setLatitude(CLatitude::fromWgs84(variant.
toString()));
break;
292 case IndexLongitudeAsString: this->setLongitude(CLongitude::fromWgs84(variant.
toString()));
break;
293 case IndexGeodeticHeightAsString: m_geodeticHeight.parseFromString(variant.
toString());
break;
294 case IndexNormalVector: this->setNormalVector(variant.
value<
QVector3D>());
break;
302 return ICoordinateGeodetic::canHandleIndex(index) ?
303 ICoordinateGeodetic::comparePropertyByIndex(index, compareValue) :
307 CCoordinateGeodetic::CCoordinateGeodetic(
const std::array<double, 3> &normalVector)
309 this->setNormalVector(normalVector);
320 : m_x(latitude.cos() * longitude.cos()), m_y(latitude.cos() * longitude.sin()), m_z(latitude.sin()),
321 m_geodeticHeight(geodeticHeight)
325 :
CCoordinateGeodetic({ latitudeDegrees, CAngleUnit::deg() }, { longitudeDegrees, CAngleUnit::deg() },
330 :
CCoordinateGeodetic({ latitudeDegrees, CAngleUnit::deg() }, { longitudeDegrees, CAngleUnit::deg() },
331 { heightFeet, CLengthUnit::ft() })
335 : m_geodeticHeight(coordinate.geodeticHeight())
352 constexpr
double earthRadiusMeters = 6371000.8;
353 const double startLatRad = this->
latitude().
value(CAngleUnit::rad());
354 const double startLngRad = this->
longitude().
value(CAngleUnit::rad());
355 const double bearingRad = relBearing.
value(CAngleUnit::rad());
356 const double distRatio = distance.
value(CLengthUnit::m()) / earthRadiusMeters;
358 const double newLatRad =
359 asin(sin(startLatRad) * cos(distRatio) + cos(startLatRad) * sin(distRatio) * cos(bearingRad));
360 double newLngRad = 0;
362 constexpr
double epsilon = 1E-06;
363 if (cos(newLatRad) == 0 || qAbs(cos(newLatRad)) < epsilon)
364 newLngRad = startLngRad;
368 newLngRad = startLngRad + atan2(sin(bearingRad) * sin(distRatio) * cos(startLatRad),
369 cos(distRatio) - sin(startLatRad) * sin(newLatRad));
370 newLngRad = fmod(newLngRad + 3 * M_PI, 2 * M_PI) - M_PI;
374 const CLatitude lat(newLatRad, CAngleUnit::rad());
375 const CLongitude lng(newLngRad, CAngleUnit::rad());
382 return { std::atan2(m_z, std::hypot(m_x, m_y)), CAngleUnit::rad() };
388 return { std::atan2(m_y, m_x), CAngleUnit::rad() };
393 return {
static_cast<float>(m_x),
static_cast<float>(m_y),
static_cast<float>(m_z) };
429 Q_ASSERT_X(
normalVector.size() == 3, Q_FUNC_INFO,
"Wrong vector size");
504 case IndexRelativeBearing:
507 case IndexRelativeDistance:
530 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
CLatitude latitude() const
Latitude.
bool isNull() const
Is null?
std::array< double, 3 > normalVectorDouble() const
Normal vector with double precision.
CCoordinateGeodetic()=default
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.
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.
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.
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.
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.
const char * constData() const const
QString & append(QChar ch)
QByteArray toLocal8Bit() const const
QVariant fromValue(T &&value)
QString toString() const const
QVector3D crossProduct(QVector3D v1, QVector3D v2)
float dotProduct(QVector3D v1, QVector3D v2)
float length() const const
#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.