mirror of
https://github.com/swift-project/pilotclient.git
synced 2026-03-30 11:55:35 +08:00
refs #291, bearing
* method to calculate bearing between coordinates * Helper methods to normalize degrees (0..360deg), e.g. -10deg -> 350deg
This commit is contained in:
@@ -163,6 +163,29 @@ namespace BlackMisc
|
||||
return CLength(qAbs(dist), CLengthUnit::NM());
|
||||
}
|
||||
|
||||
PhysicalQuantities::CAngle initialBearing(const ICoordinateGeodetic &coordinate1, const ICoordinateGeodetic &coordinate2)
|
||||
{
|
||||
// same coordinate results in 0 distance
|
||||
if (coordinate1.latitude() == coordinate2.latitude() && coordinate1.longitude() == coordinate2.longitude())
|
||||
{
|
||||
return CAngle(0, CAngleUnit::deg());
|
||||
}
|
||||
|
||||
// http://www.yourhomenow.com/house/haversine.html
|
||||
double lon1rad = coordinate1.longitude().value(CAngleUnit::rad());
|
||||
double lon2rad = coordinate2.longitude().value(CAngleUnit::rad());
|
||||
double lat1rad = coordinate1.latitude().value(CAngleUnit::rad());
|
||||
double lat2rad = coordinate2.latitude().value(CAngleUnit::rad());
|
||||
double dLon = lon1rad - lon2rad;
|
||||
double y = qSin(dLon) * qCos(lat2rad);
|
||||
double x = qCos(lat1rad) * qSin(lat2rad) -
|
||||
qSin(lat1rad) * qCos(lat2rad) * qCos(dLon);
|
||||
double bearing = qAtan2(y, x);
|
||||
bearing = CMath::rad2deg(bearing); // now in deg
|
||||
bearing = CMath::normalizeDegrees(bearing); // normalize
|
||||
return CAngle(bearing, CAngleUnit::deg());
|
||||
}
|
||||
|
||||
/*
|
||||
* Great circle distance
|
||||
*/
|
||||
@@ -171,5 +194,99 @@ namespace BlackMisc
|
||||
return Geo::greatCircleDistance((*this), otherCoordinate);
|
||||
}
|
||||
|
||||
/*
|
||||
* Initial bearing
|
||||
*/
|
||||
CAngle ICoordinateGeodetic::initialBearing(const ICoordinateGeodetic &otherCoordinate)
|
||||
{
|
||||
return Geo::initialBearing((*this), otherCoordinate);
|
||||
}
|
||||
|
||||
/*
|
||||
* My index
|
||||
*/
|
||||
bool ICoordinateGeodetic::indexInRange(int index)
|
||||
{
|
||||
return index >= static_cast<int>(IndexLatitude) &&
|
||||
index <= static_cast<int>(IndexLongitudeAsString);
|
||||
}
|
||||
|
||||
/*
|
||||
* Property by index
|
||||
*/
|
||||
QVariant ICoordinateGeodetic::propertyByIndex(int index) const
|
||||
{
|
||||
|
||||
switch (index)
|
||||
{
|
||||
case IndexLatitude:
|
||||
return this->latitude().toQVariant();
|
||||
case IndexLongitude:
|
||||
return this->longitude().toQVariant();
|
||||
case IndexLatitudeAsString:
|
||||
return QVariant(this->latitudeAsString());
|
||||
case IndexLongitudeAsString:
|
||||
return QVariant(this->longitudeAsString());
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
Q_ASSERT_X(false, "ICoordinateGeodetic", "index unknown");
|
||||
QString m = QString("no property, index ").append(QString::number(index));
|
||||
return QVariant::fromValue(m);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Property by index
|
||||
*/
|
||||
QVariant CCoordinateGeodetic::propertyByIndex(int index) const
|
||||
{
|
||||
if (ICoordinateGeodetic::indexInRange(index))
|
||||
{
|
||||
return ICoordinateGeodetic::propertyByIndex(index);
|
||||
}
|
||||
|
||||
switch (index)
|
||||
{
|
||||
case IndexGeodeticHeight:
|
||||
return this->m_geodeticHeight.toQVariant();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
Q_ASSERT_X(false, "CCoordinateGeodetic", "index unknown");
|
||||
QString m = QString("no property, index ").append(QString::number(index));
|
||||
return QVariant::fromValue(m);
|
||||
}
|
||||
|
||||
/*
|
||||
* Set property as index
|
||||
*/
|
||||
void CCoordinateGeodetic::setPropertyByIndex(const QVariant &variant, int index)
|
||||
{
|
||||
switch (index)
|
||||
{
|
||||
case IndexGeodeticHeight:
|
||||
this->setGeodeticHeight(variant.value<CLength>());
|
||||
break;
|
||||
case IndexLatitude:
|
||||
this->setLatitude(variant.value<CLatitude>());
|
||||
break;
|
||||
case IndexLongitude:
|
||||
this->setLongitude(variant.value<CLongitude>());
|
||||
break;
|
||||
case IndexLatitudeAsString:
|
||||
this->setLatitude(CLatitude::fromWgs84(variant.toString()));
|
||||
break;
|
||||
case IndexLongitudeAsString:
|
||||
this->setLongitude(CLongitude::fromWgs84(variant.toString()));
|
||||
break;
|
||||
default:
|
||||
Q_ASSERT_X(false, "CCoordinateGeodetic", "index unknown (setter)");
|
||||
break;
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
} // namespace
|
||||
|
||||
@@ -24,6 +24,18 @@ namespace BlackMisc
|
||||
{
|
||||
public:
|
||||
|
||||
//! Properties by index
|
||||
enum ColumnIndex
|
||||
{
|
||||
IndexLatitude = 6000,
|
||||
IndexLongitude,
|
||||
IndexLatitudeAsString,
|
||||
IndexLongitudeAsString
|
||||
};
|
||||
|
||||
//! Destructor
|
||||
virtual ~ICoordinateGeodetic() {}
|
||||
|
||||
//! Latitude
|
||||
virtual const CLatitude &latitude() const = 0;
|
||||
|
||||
@@ -44,17 +56,40 @@ namespace BlackMisc
|
||||
|
||||
//! Great circle distance
|
||||
BlackMisc::PhysicalQuantities::CLength greatCircleDistance(const ICoordinateGeodetic &otherCoordinate);
|
||||
|
||||
//! Initial bearing
|
||||
BlackMisc::PhysicalQuantities::CAngle initialBearing(const ICoordinateGeodetic &otherCoordinate);
|
||||
|
||||
//! In range
|
||||
static bool indexInRange(int index);
|
||||
|
||||
//! \copydoc CValueObject::propertyByIndex
|
||||
virtual QVariant propertyByIndex(int index) const;
|
||||
};
|
||||
|
||||
//! Great circle distance between points
|
||||
BlackMisc::PhysicalQuantities::CLength greatCircleDistance(const ICoordinateGeodetic &coordinate1, const ICoordinateGeodetic &coordinate2);
|
||||
|
||||
//! Initial bearing
|
||||
BlackMisc::PhysicalQuantities::CAngle initialBearing(const ICoordinateGeodetic &coordinate1, const ICoordinateGeodetic &coordinate2);
|
||||
|
||||
//! Geodetic coordinate
|
||||
//! \sa http://www.esri.com/news/arcuser/0703/geoid1of3.html
|
||||
//! \sa http://http://www.gmat.unsw.edu.au/snap/gps/clynch_pdfs/coordcvt.pdf (page 5)
|
||||
//! \sa http://en.wikipedia.org/wiki/Geodetic_datum#Vertical_datum
|
||||
class CCoordinateGeodetic : public CValueObject, public ICoordinateGeodetic
|
||||
{
|
||||
public:
|
||||
//! Column index
|
||||
enum ColumnIndex
|
||||
{
|
||||
IndexLatitude = 6000,
|
||||
IndexLongitude,
|
||||
IndexLatitudeAsString,
|
||||
IndexLongitudeAsString,
|
||||
IndexGeodeticHeight
|
||||
};
|
||||
|
||||
private:
|
||||
BLACK_ENABLE_TUPLE_CONVERSION(CCoordinateGeodetic)
|
||||
BlackMisc::Geo::CLatitude m_latitude; //!< Latitude
|
||||
@@ -107,6 +142,12 @@ namespace BlackMisc
|
||||
//! \copydoc CValueObject::toQVariant
|
||||
virtual QVariant toQVariant() const override { return QVariant::fromValue(*this); }
|
||||
|
||||
//! \copydoc CValueObject::propertyByIndex
|
||||
virtual QVariant propertyByIndex(int index) const override;
|
||||
|
||||
//! \copydoc CValueObject::setPropertyByIndex
|
||||
virtual void setPropertyByIndex(const QVariant &variant, int index) override;
|
||||
|
||||
//! Switch unit of latitude / longitude
|
||||
CCoordinateGeodetic &switchUnit(const BlackMisc::PhysicalQuantities::CAngleUnit &unit)
|
||||
{
|
||||
@@ -154,6 +195,7 @@ namespace BlackMisc
|
||||
|
||||
//! Coordinate by WGS84 position data
|
||||
static CCoordinateGeodetic fromWgs84(const QString &latitudeWgs84, const QString &longitudeWgs84, const BlackMisc::PhysicalQuantities::CLength geodeticHeight = BlackMisc::PhysicalQuantities::CLength());
|
||||
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
@@ -68,7 +68,7 @@ namespace BlackMisc
|
||||
bool CMath::epsilonEqual(double v1, double v2, double epsilon)
|
||||
{
|
||||
if (v1 == v2) return true;
|
||||
return qAbs(v1-v2) <= epsilon;
|
||||
return qAbs(v1 - v2) <= epsilon;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -87,5 +87,28 @@ namespace BlackMisc
|
||||
return radians * 180.0 / CMath::PI();
|
||||
}
|
||||
|
||||
/*
|
||||
* Normalize degrees
|
||||
*/
|
||||
double CMath::normalizeDegrees(double degrees)
|
||||
{
|
||||
if (degrees == 0.0 || degrees == 360.0 || degrees == -360.0) return 0.0;
|
||||
if (degrees > 0)
|
||||
{
|
||||
while (degrees >= 360.0)
|
||||
{
|
||||
degrees -= 360.0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
while (degrees < 0.0)
|
||||
{
|
||||
degrees += 360.0;
|
||||
}
|
||||
}
|
||||
return degrees;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace
|
||||
|
||||
@@ -144,6 +144,12 @@ namespace BlackMisc
|
||||
*/
|
||||
static double rad2deg(double radians);
|
||||
|
||||
/*!
|
||||
* \brief Normalize to 0..360 degrees
|
||||
* \param degrees
|
||||
* \return
|
||||
*/
|
||||
static double normalizeDegrees(double degrees);
|
||||
|
||||
private:
|
||||
/*!
|
||||
|
||||
Reference in New Issue
Block a user