diff --git a/example/glide_computer/CMakeLists.txt b/example/glide_computer/CMakeLists.txt index d98b3636..7c19f4db 100644 --- a/example/glide_computer/CMakeLists.txt +++ b/example/glide_computer/CMakeLists.txt @@ -22,6 +22,6 @@ cmake_minimum_required(VERSION 3.2) -add_library(glide_computer STATIC geographic.cpp include/geographic.h glide_computer.cpp include/glide_computer.h) -target_link_libraries(glide_computer PRIVATE mp-units::core-fmt PUBLIC mp-units::si) +add_library(glide_computer STATIC include/geographic.h glide_computer.cpp include/glide_computer.h) +target_link_libraries(glide_computer PRIVATE mp-units::core-fmt PUBLIC mp-units::si example_utils) target_include_directories(glide_computer PUBLIC include) diff --git a/example/glide_computer/geographic.cpp b/example/glide_computer/geographic.cpp deleted file mode 100644 index 4150c792..00000000 --- a/example/glide_computer/geographic.cpp +++ /dev/null @@ -1,64 +0,0 @@ -// The MIT License (MIT) -// -// Copyright (c) 2018 Mateusz Pusz -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -#include "geographic.h" -#include -#include -#include - -namespace { - -using namespace units::isq::si; -inline constexpr length earth_radius(6371); - -} // namespace - -namespace geographic { - -distance spherical_distance(position from, position to) -{ - using rep = std::common_type_t; - constexpr auto p = std::numbers::pi_v / 180; - const auto lat1 = from.lat.value() * p; - const auto lon1 = from.lon.value() * p; - const auto lat2 = to.lat.value() * p; - const auto lon2 = to.lon.value() * p; - - using std::sin, std::cos, std::asin, std::sqrt; - - // https://en.wikipedia.org/wiki/Great-circle_distance#Formulae - if constexpr (sizeof(rep) >= 8) { - // spherical law of cosines - const auto central_angle = acos(sin(lat1) * sin(lat2) + cos(lat1) * cos(lat2) * cos(lon2 - lon1)); - // const auto central_angle = 2 * asin(sqrt(0.5 - cos(lat2 - lat1) / 2 + cos(lat1) * cos(lat2) * (1 - cos(lon2 - - // lon1)) / 2)); - return distance(earth_radius * central_angle); - } else { - // the haversine formula - const auto sin_lat = sin((lat2 - lat1) / 2); - const auto sin_lon = sin((lon2 - lon1) / 2); - const auto central_angle = 2 * asin(sqrt(sin_lat * sin_lat + cos(lat1) * cos(lat2) * sin_lon * sin_lon)); - return distance(earth_radius * central_angle); - } -} - -} // namespace geographic diff --git a/example/glide_computer/include/geographic.h b/example/glide_computer/include/geographic.h index 44290080..e8a56714 100644 --- a/example/glide_computer/include/geographic.h +++ b/example/glide_computer/include/geographic.h @@ -22,7 +22,9 @@ #pragma once +#include "ranged_representation.h" #include +#include #include #include #include @@ -34,88 +36,95 @@ namespace geographic { -template -struct coordinate { - using value_type = Rep; - constexpr explicit coordinate(value_type v) : value_(v) {} - constexpr value_type value() const { return value_; } - auto operator<=>(const coordinate&) const = default; -private: - value_type value_; -}; +// TODO Change to `angle` dimension in degree unit when the work on magnitudes is done +template +using latitude = units::dimensionless>; -struct latitude : coordinate { - using coordinate::coordinate; -}; +template +using longitude = units::dimensionless>; -struct longitude : coordinate { - using coordinate::coordinate; -}; - -template -std::basic_ostream& operator<<(std::basic_ostream& os, const latitude& lat) +template +std::basic_ostream& operator<<(std::basic_ostream& os, const latitude& lat) { - if (lat.value() > 0) - return os << "N" << lat.value(); + if (lat.number() > 0) + return os << "N" << lat.number(); else - return os << "S" << -lat.value(); + return os << "S" << -lat.number(); } -template -std::basic_ostream& operator<<(std::basic_ostream& os, const longitude& lon) +template +std::basic_ostream& operator<<(std::basic_ostream& os, const longitude& lon) { - if (lon.value() > 0) - return os << "E" << lon.value(); + if (lon.number() > 0) + return os << "E" << lon.number(); else - return os << "W" << -lon.value(); + return os << "W" << -lon.number(); } inline namespace literals { -constexpr auto operator"" _N(unsigned long long v) { return latitude(static_cast(v)); } -constexpr auto operator"" _N(long double v) { return latitude(static_cast(v)); } -constexpr auto operator"" _S(unsigned long long v) { return latitude(-static_cast(v)); } -constexpr auto operator"" _S(long double v) { return latitude(-static_cast(v)); } -constexpr auto operator"" _E(unsigned long long v) { return longitude(static_cast(v)); } -constexpr auto operator"" _E(long double v) { return longitude(static_cast(v)); } -constexpr auto operator"" _W(unsigned long long v) { return longitude(-static_cast(v)); } -constexpr auto operator"" _W(long double v) { return longitude(-static_cast(v)); } +constexpr auto operator"" _N(long double v) { return latitude(latitude::rep(v)); } +constexpr auto operator"" _S(long double v) { return latitude(latitude::rep(v)); } +constexpr auto operator"" _E(long double v) { return longitude(longitude::rep(v)); } +constexpr auto operator"" _W(long double v) { return longitude(longitude::rep(v)); } +constexpr auto operator"" _N(unsigned long long v) +{ + gsl_ExpectsAudit(std::in_range(v)); + return latitude(latitude::rep(static_cast(v))); +} +constexpr auto operator"" _S(unsigned long long v) +{ + gsl_ExpectsAudit(std::in_range(v)); + return latitude(-latitude::rep(static_cast(v))); +} +constexpr auto operator"" _E(unsigned long long v) +{ + gsl_ExpectsAudit(std::in_range(v)); + return longitude(longitude::rep(static_cast(v))); +} +constexpr auto operator"" _W(unsigned long long v) +{ + gsl_ExpectsAudit(std::in_range(v)); + return longitude(-longitude::rep(static_cast(v))); +} } // namespace literals } // namespace geographic -template<> -class std::numeric_limits : public numeric_limits { - static constexpr auto min() noexcept { return geographic::latitude(-90); } - static constexpr auto lowest() noexcept { return geographic::latitude(-90); } - static constexpr auto max() noexcept { return geographic::latitude(90); } +template +class std::numeric_limits> : public numeric_limits { + static constexpr auto min() noexcept { return geographic::latitude(-90); } + static constexpr auto lowest() noexcept { return geographic::latitude(-90); } + static constexpr auto max() noexcept { return geographic::latitude(90); } }; -template<> -class std::numeric_limits : public numeric_limits { - static constexpr auto min() noexcept { return geographic::longitude(-180); } - static constexpr auto lowest() noexcept { return geographic::longitude(-180); } - static constexpr auto max() noexcept { return geographic::longitude(180); } +template +class std::numeric_limits> : public numeric_limits { + static constexpr auto min() noexcept { return geographic::longitude(-180); } + static constexpr auto lowest() noexcept { return geographic::longitude(-180); } + static constexpr auto max() noexcept { return geographic::longitude(180); } }; -template<> -struct STD_FMT::formatter : formatter { +template +struct STD_FMT::formatter> : formatter { template - auto format(geographic::latitude lat, FormatContext& ctx) + auto format(geographic::latitude lat, FormatContext& ctx) { - STD_FMT::format_to(ctx.out(), "{}", lat.value() > 0 ? 'N' : 'S'); - return formatter::format(lat.value() > 0 ? lat.value() : -lat.value(), ctx); + using rep = geographic::latitude::rep; + STD_FMT::format_to(ctx.out(), "{}", lat > rep{0} ? 'N' : 'S'); + return formatter::format(lat > rep{0} ? lat.number() : -lat.number(), ctx); } }; -template<> -struct STD_FMT::formatter : formatter { +template +struct STD_FMT::formatter> : formatter { template - auto format(geographic::longitude lon, FormatContext& ctx) + auto format(geographic::longitude lon, FormatContext& ctx) { - STD_FMT::format_to(ctx.out(), "{}", lon.value() > 0 ? 'E' : 'W'); - return formatter::format(lon.value() > 0 ? lon.value() : -lon.value(), ctx); + using rep = geographic::longitude::rep; + STD_FMT::format_to(ctx.out(), "{}", lon > rep{0} ? 'E' : 'W'); + return formatter::format(lon > rep{0} ? lon.number() : -lon.number(), ctx); } }; @@ -124,11 +133,41 @@ namespace geographic { struct horizontal_kind : units::kind {}; using distance = units::quantity_kind; +template struct position { - latitude lat; - longitude lon; + latitude lat; + longitude lon; }; -distance spherical_distance(position from, position to); +template +distance spherical_distance(position from, position to) +{ + using namespace units::isq::si; + constexpr length earth_radius(6371); + + constexpr auto p = std::numbers::pi_v / 180; + const auto lat1_rad = from.lat.number() * p; + const auto lon1_rad = from.lon.number() * p; + const auto lat2_rad = to.lat.number() * p; + const auto lon2_rad = to.lon.number() * p; + + using std::sin, std::cos, std::asin, std::acos, std::sqrt; + + // https://en.wikipedia.org/wiki/Great-circle_distance#Formulae + if constexpr (sizeof(T) >= 8) { + // spherical law of cosines + const auto central_angle = + acos(sin(lat1_rad) * sin(lat2_rad) + cos(lat1_rad) * cos(lat2_rad) * cos(lon2_rad - lon1_rad)); + // const auto central_angle = 2 * asin(sqrt(0.5 - cos(lat2_rad - lat1_rad) / 2 + cos(lat1_rad) * cos(lat2_rad) * (1 + // - cos(lon2_rad - lon1_rad)) / 2)); + return distance(earth_radius * central_angle); + } else { + // the haversine formula + const auto sin_lat = sin((lat2_rad - lat1_rad) / 2); + const auto sin_lon = sin((lon2_rad - lon1_rad) / 2); + const auto central_angle = 2 * asin(sqrt(sin_lat * sin_lat + cos(lat1_rad) * cos(lat2_rad) * sin_lon * sin_lon)); + return distance(earth_radius * central_angle); + } +} } // namespace geographic diff --git a/example/glide_computer/include/glide_computer.h b/example/glide_computer/include/glide_computer.h index d545ea7a..c515868c 100644 --- a/example/glide_computer/include/glide_computer.h +++ b/example/glide_computer/include/glide_computer.h @@ -136,7 +136,7 @@ struct weather { struct waypoint { std::string name; - geographic::position pos; + geographic::position pos; altitude alt; };