diff --git a/include/coordinate.h b/include/coordinate.h index e2bea5c..6e191d8 100644 --- a/include/coordinate.h +++ b/include/coordinate.h @@ -44,6 +44,13 @@ class ERKIR_EXPORT Coordinate DD ///< Decimal Degrees (D.D°) }; + enum class CompassPrecision + { + Cardinal, + Intercardinal, + SecondaryIntercardinal + }; + //! Constructs a coordinate by the given decimal degrees. Coordinate(double degrees); @@ -67,6 +74,19 @@ class ERKIR_EXPORT Coordinate /// Constrain degrees to range 0..360.0 (e.g. for bearings); -1 => 359, 361 => 1. static double wrap360(double degrees); + /// Returns compass point (to given precision) for supplied bearing. + /*! + @param bearing Bearing in degrees from north. + @param precision Precision (Cardinal, Intercardinal, SecondaryIntercardinal). + @returns Compass point for supplied bearing. + + @example + const point = Coordinate::compassPoint(24); // point = 'NNE' + const point = Coordinate::compassPoint(24, 1); // point = 'N' + */ + static std::string compassPoint(double bearing, + CompassPrecision precision = CompassPrecision::SecondaryIntercardinal); + protected: std::string toBaseString(Format format, int precision) const; diff --git a/src/coordinate.cpp b/src/coordinate.cpp index 4415e2e..284c85b 100644 --- a/src/coordinate.cpp +++ b/src/coordinate.cpp @@ -256,6 +256,24 @@ std::string Coordinate::toBaseString(Format format, int precision) const return ss.str(); } +std::string Coordinate::compassPoint(double bearing, CompassPrecision precision) +{ + const auto wrappedBearing = wrap360(bearing); // normalize to range 0..360° + + static constexpr const char *cardinals[] = { + "N", "NNE", "NE", "ENE", + "E", "ESE", "SE", "SSE", + "S", "SSW", "SW", "WSW", + "W", "WNW", "NW", "NNW" + }; + static constexpr const int ns[] = { 4, 8, 16 }; + + const auto p = static_cast(precision); + const auto n = ns[p]; + auto c = cardinals[(int)(std::round(wrappedBearing * n / 360.0)) % n * 16 / n]; + return c; +} + //////////////////////////////////////////////////////////////////////////////// Latitude::Latitude(double degree) diff --git a/test/test.cpp b/test/test.cpp index ecdd718..766000c 100644 --- a/test/test.cpp +++ b/test/test.cpp @@ -491,6 +491,28 @@ int main() } } + // Compass Points + { + verifyString( "N", Coordinate::compassPoint(1)); + verifyString( "N", Coordinate::compassPoint(720)); + verifyString( "N", Coordinate::compassPoint(0)); + verifyString( "N", Coordinate::compassPoint(-1)); + verifyString( "N", Coordinate::compassPoint(359)); + verifyString( "S", Coordinate::compassPoint(180)); + verifyString("NNE", Coordinate::compassPoint(24)); + verifyString( "N", Coordinate::compassPoint(24, Coordinate::CompassPrecision::Cardinal)); + verifyString( "NE", Coordinate::compassPoint(24, Coordinate::CompassPrecision::Intercardinal)); + verifyString("NNE", Coordinate::compassPoint(24, Coordinate::CompassPrecision::SecondaryIntercardinal)); + verifyString( "SW", Coordinate::compassPoint(226)); + verifyString( "W", Coordinate::compassPoint(226, Coordinate::CompassPrecision::Cardinal)); + verifyString( "SW", Coordinate::compassPoint(226, Coordinate::CompassPrecision::Intercardinal)); + verifyString( "SW", Coordinate::compassPoint(226, Coordinate::CompassPrecision::SecondaryIntercardinal)); + verifyString("WSW", Coordinate::compassPoint(237)); + verifyString( "W", Coordinate::compassPoint(237, Coordinate::CompassPrecision::Cardinal)); + verifyString( "SW", Coordinate::compassPoint(237, Coordinate::CompassPrecision::Intercardinal)); + verifyString("WSW", Coordinate::compassPoint(237, Coordinate::CompassPrecision::SecondaryIntercardinal)); + } + ////////////////////////////////////////////////////////////////////////////// // Ellipsoidal points