// 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. #pragma once #include "ranged_representation.h" #include #include #include #include #include #include #include #include namespace geographic { template using latitude = mp_units::quantity>; template using longitude = mp_units::quantity>; template std::basic_ostream& operator<<(std::basic_ostream& os, const latitude& lat) { if (lat.number() > 0) return os << "N" << lat.number(); else return os << "S" << -lat.number(); } template std::basic_ostream& operator<<(std::basic_ostream& os, const longitude& lon) { if (lon.number() > 0) return os << "E" << lon.number(); else return os << "W" << -lon.number(); } inline namespace literals { 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::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 auto format(geographic::latitude lat, FormatContext& ctx) { STD_FMT::format_to(ctx.out(), "{}", lat > geographic::latitude::zero() ? 'N' : 'S'); return formatter::format(lat > geographic::latitude::zero() ? lat.number() : -lat.number(), ctx); } }; template struct STD_FMT::formatter> : formatter { template auto format(geographic::longitude lon, FormatContext& ctx) { STD_FMT::format_to(ctx.out(), "{}", lon > geographic::longitude::zero() ? 'E' : 'W'); return formatter::format(lon > geographic::longitude::zero() ? lon.number() : -lon.number(), ctx); } }; namespace geographic { using distance = mp_units::quantity]>; template struct position { latitude lat; longitude lon; }; template distance spherical_distance(position from, position to) { using namespace mp_units; constexpr auto earth_radius = 6371 * isq::radius[si::kilo]; 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)); // TODO can we improve the below // return quantity_cast(earth_radius * central_angle); return earth_radius.number() * central_angle * isq::distance[earth_radius.unit]; } 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)); // TODO can we improve the below // return quantity_cast(earth_radius * central_angle); return earth_radius.number() * central_angle * isq::distance[earth_radius.unit]; } } } // namespace geographic