diff --git a/src/blackmisc/coordinategeodetic.cpp b/src/blackmisc/coordinategeodetic.cpp index 310e57c40..3c90706b0 100644 --- a/src/blackmisc/coordinategeodetic.cpp +++ b/src/blackmisc/coordinategeodetic.cpp @@ -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(IndexLatitude) && + index <= static_cast(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()); + break; + case IndexLatitude: + this->setLatitude(variant.value()); + break; + case IndexLongitude: + this->setLongitude(variant.value()); + 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 diff --git a/src/blackmisc/coordinategeodetic.h b/src/blackmisc/coordinategeodetic.h index 8053bff6d..b2c368874 100644 --- a/src/blackmisc/coordinategeodetic.h +++ b/src/blackmisc/coordinategeodetic.h @@ -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 diff --git a/src/blackmisc/mathematics.cpp b/src/blackmisc/mathematics.cpp index dd71e7c26..44ba29c0c 100644 --- a/src/blackmisc/mathematics.cpp +++ b/src/blackmisc/mathematics.cpp @@ -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 diff --git a/src/blackmisc/mathematics.h b/src/blackmisc/mathematics.h index 9742b93a2..64122dc40 100644 --- a/src/blackmisc/mathematics.h +++ b/src/blackmisc/mathematics.h @@ -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: /*!