diff --git a/include/util/web_mercator.hpp b/include/util/web_mercator.hpp index 8c23de143..a2bea88d1 100644 --- a/include/util/web_mercator.hpp +++ b/include/util/web_mercator.hpp @@ -35,7 +35,7 @@ inline FloatLatitude yToLat(const double y) const double normalized_lat = detail::RAD_TO_DEGREE * 2. * std::atan(std::exp(clamped_y * detail::DEGREE_TO_RAD)); - return FloatLatitude(normalized_lat - 90.); + return FloatLatitude(std::round(normalized_lat * COORDINATE_PRECISION) / COORDINATE_PRECISION - 90.); } inline double latToY(const FloatLatitude latitude) @@ -47,6 +47,31 @@ inline double latToY(const FloatLatitude latitude) return clamped_y; } +template +constexpr double horner(double, T an) { return an; } + +template +constexpr double horner(double x, T an, U ...a) { return horner(x, a...) * x + an; } + +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(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 FloatLatitude clamp(const FloatLatitude lat) { return std::max(std::min(lat, FloatLatitude(detail::MAX_LATITUDE)), @@ -87,7 +112,7 @@ inline double degreeToPixel(FloatLatitude lat, unsigned zoom) inline FloatCoordinate fromWGS84(const FloatCoordinate &wgs84_coordinate) { - return {wgs84_coordinate.lon, FloatLatitude{latToY(wgs84_coordinate.lat)}}; + return {wgs84_coordinate.lon, FloatLatitude{latToYapprox(wgs84_coordinate.lat)}}; } inline FloatCoordinate toWGS84(const FloatCoordinate &mercator_coordinate)