inline FloatLatitude yToLat(const double y) { const auto clamped_y = std::max(-180., std::min(180., y)); const double normalized_lat = detail::RAD_TO_DEGREE * 2. * std::atan(std::exp(clamped_y * detail::DEGREE_TO_RAD)); return FloatLatitude(std::round(normalized_lat * COORDINATE_PRECISION) / COORDINATE_PRECISION - 90.); }
inline double latToYapprox(const FloatLatitude latitude) { if (latitude < FloatLatitude(-70.) || latitude > FloatLatitude(70.)) return latToY(latitude); // Approximate the inverse Gudermannian function with the Padé approximant [11/11]: deg → deg // Coefficients are computed for the argument range [-70°,70°] by Remez algorithm |err|_∞=3.387e-12 const auto x = static_cast<double>(latitude); return horner(x, 0.00000000000000000000000000e+00, 1.00000000000089108431373566e+00, 2.34439410386997223035693483e-06, -3.21291701673364717170998957e-04, -6.62778508496089940141103135e-10, 3.68188055470304769936079078e-08, 6.31192702320492485752941578e-14, -1.77274453235716299127325443e-12, -2.24563810831776747318521450e-18, 3.13524754818073129982475171e-17, 2.09014225025314211415458228e-23, -9.82938075991732185095509716e-23) / horner(x, 1.00000000000000000000000000e+00, 2.34439410398970701719081061e-06, -3.72061271627251952928813333e-04, -7.81802389685429267252612620e-10, 5.18418724186576447072888605e-08, 9.37468561198098681003717477e-14, -3.30833288607921773936702558e-12, -4.78446279888774903983338274e-18, 9.32999229169156878168234191e-17, 9.17695141954265959600965170e-23, -8.72130728982012387640166055e-22, -3.23083224835967391884404730e-28); }
inline std::pair<double, FloatCoordinate> projectPointOnSegment(const FloatCoordinate &source, const FloatCoordinate &target, const FloatCoordinate &coordinate) { const FloatCoordinate slope_vector{target.lon - source.lon, target.lat - source.lat}; const FloatCoordinate rel_coordinate{coordinate.lon - source.lon, coordinate.lat - source.lat}; // dot product of two un-normed vectors const auto unnormed_ratio = static_cast<double>(slope_vector.lon * rel_coordinate.lon) + static_cast<double>(slope_vector.lat * rel_coordinate.lat); // squared length of the slope vector const auto squared_length = static_cast<double>(slope_vector.lon * slope_vector.lon) + static_cast<double>(slope_vector.lat * slope_vector.lat); if (squared_length < std::numeric_limits<double>::epsilon()) { return {0, source}; } const double normed_ratio = unnormed_ratio / squared_length; double clamped_ratio = normed_ratio; if (clamped_ratio > 1.) { clamped_ratio = 1.; } else if (clamped_ratio < 0.) { clamped_ratio = 0.; } return {clamped_ratio, { FloatLongitude(1.0 - clamped_ratio) * source.lon + target.lon * FloatLongitude(clamped_ratio), FloatLatitude(1.0 - clamped_ratio) * source.lat + target.lat * FloatLatitude(clamped_ratio), }}; }
bool FloatCoordinate::IsValid() const { return !(lat > FloatLatitude(90) || lat < FloatLatitude(-90) || lon > FloatLongitude(180) || lon < FloatLongitude(-180)); }
inline FloatLatitude clamp(const FloatLatitude lat) { return std::max(std::min(lat, FloatLatitude(detail::MAX_LATITUDE)), FloatLatitude(-detail::MAX_LATITUDE)); }