diff options
-rw-r--r-- | include/mbgl/util/geo.hpp | 8 | ||||
-rw-r--r-- | include/mbgl/util/projection.hpp | 8 | ||||
-rw-r--r-- | test/test.gypi | 1 | ||||
-rw-r--r-- | test/util/projection.cpp | 68 |
4 files changed, 80 insertions, 5 deletions
diff --git a/include/mbgl/util/geo.hpp b/include/mbgl/util/geo.hpp index 7e15d5c2f1..5db307e482 100644 --- a/include/mbgl/util/geo.hpp +++ b/include/mbgl/util/geo.hpp @@ -67,8 +67,8 @@ inline bool operator!=(const LatLng& a, const LatLng& b) { class ProjectedMeters { public: - double northing = 0; - double easting = 0; + double northing; + double easting; ProjectedMeters(double n = 0, double e = 0) : northing(n), easting(e) {} @@ -78,6 +78,10 @@ public: } }; +inline bool operator==(const ProjectedMeters& a, const ProjectedMeters& b) { + return a.northing == b.northing && a.easting == b.easting; +} + class LatLngBounds { public: // Return a bounds covering the entire (unwrapped) world. diff --git a/include/mbgl/util/projection.hpp b/include/mbgl/util/projection.hpp index 8e1c994657..76c60f5cd8 100644 --- a/include/mbgl/util/projection.hpp +++ b/include/mbgl/util/projection.hpp @@ -12,19 +12,20 @@ class Projection { public: static inline double getMetersPerPixelAtLatitude(double lat, double zoom) { - const double mapPixelWidthAtZoom = std::pow(2.0, zoom) * util::tileSize; + const double constrainedZoom = util::clamp(zoom, util::MIN_ZOOM, util::MAX_ZOOM); + const double mapPixelWidthAtZoom = std::pow(2.0, constrainedZoom) * util::tileSize; const double constrainedLatitude = util::clamp(lat, -util::LATITUDE_MAX, util::LATITUDE_MAX); - return std::cos(constrainedLatitude * util::DEG2RAD) * util::M2PI * util::EARTH_RADIUS_M / mapPixelWidthAtZoom; } static inline ProjectedMeters projectedMetersForLatLng(const LatLng& latLng) { const double constrainedLatitude = util::clamp(latLng.latitude, -util::LATITUDE_MAX, util::LATITUDE_MAX); + const double constrainedLongitude = util::clamp(latLng.longitude, -util::LONGITUDE_MAX, util::LONGITUDE_MAX); const double m = 1 - 1e-15; const double f = util::clamp(std::sin(util::DEG2RAD * constrainedLatitude), -m, m); - const double easting = util::EARTH_RADIUS_M * latLng.longitude * util::DEG2RAD; + const double easting = util::EARTH_RADIUS_M * constrainedLongitude * util::DEG2RAD; const double northing = 0.5 * util::EARTH_RADIUS_M * std::log((1 + f) / (1 - f)); return ProjectedMeters(northing, easting); @@ -35,6 +36,7 @@ public: double longitude = projectedMeters.easting * util::RAD2DEG / util::EARTH_RADIUS_M; latitude = util::clamp(latitude, -util::LATITUDE_MAX, util::LATITUDE_MAX); + longitude = util::clamp(longitude, -util::LONGITUDE_MAX, util::LONGITUDE_MAX); return LatLng(latitude, longitude); } diff --git a/test/test.gypi b/test/test.gypi index b7e7b5c32b..d93ecd1f35 100644 --- a/test/test.gypi +++ b/test/test.gypi @@ -32,6 +32,7 @@ 'util/timer.cpp', 'util/token.cpp', 'util/work_queue.cpp', + 'util/projection.cpp', 'algorithm/covered_by_children.cpp', 'algorithm/generate_clip_ids.cpp', diff --git a/test/util/projection.cpp b/test/util/projection.cpp new file mode 100644 index 0000000000..5efba380b3 --- /dev/null +++ b/test/util/projection.cpp @@ -0,0 +1,68 @@ +#include <mbgl/test/util.hpp> + +#include <mbgl/util/constants.hpp> +#include <mbgl/util/geo.hpp> +#include <mbgl/util/projection.hpp> + +#include <limits> + +using namespace mbgl; + +TEST(Projection, MetersPerPixelAtLatitude) { + double zoom = 0; + EXPECT_DOUBLE_EQ(Projection::getMetersPerPixelAtLatitude(0, zoom), 78271.516964020484); + EXPECT_DOUBLE_EQ(Projection::getMetersPerPixelAtLatitude(-util::LATITUDE_MAX, zoom), 6752.2284729446501); + EXPECT_DOUBLE_EQ(Projection::getMetersPerPixelAtLatitude(util::LATITUDE_MAX, zoom), + Projection::getMetersPerPixelAtLatitude(-util::LATITUDE_MAX, zoom)); + + zoom = 20; + EXPECT_DOUBLE_EQ(Projection::getMetersPerPixelAtLatitude(0, zoom), 0.074645535434742435); + EXPECT_DOUBLE_EQ(Projection::getMetersPerPixelAtLatitude(-util::LATITUDE_MAX, zoom), 0.0064394268731543065); + + EXPECT_DOUBLE_EQ(Projection::getMetersPerPixelAtLatitude(std::numeric_limits<double>::lowest(), zoom), + Projection::getMetersPerPixelAtLatitude(std::numeric_limits<double>::max(), zoom)); + + zoom = std::numeric_limits<double>::min(); + EXPECT_DOUBLE_EQ(Projection::getMetersPerPixelAtLatitude(0, zoom), + Projection::getMetersPerPixelAtLatitude(0, util::MIN_ZOOM)); + + zoom = std::numeric_limits<double>::lowest(); + EXPECT_DOUBLE_EQ(Projection::getMetersPerPixelAtLatitude(0, zoom), + Projection::getMetersPerPixelAtLatitude(0, util::MIN_ZOOM)); + + zoom = std::numeric_limits<double>::max(); + EXPECT_DOUBLE_EQ(Projection::getMetersPerPixelAtLatitude(0, zoom), + Projection::getMetersPerPixelAtLatitude(0, util::MAX_ZOOM)); +} + +TEST(Projection, ProjectedMeters) { + const auto southWest = LatLng { -util::LATITUDE_MAX, -util::LONGITUDE_MAX }; + const auto northEast = LatLng { util::LATITUDE_MAX, util::LONGITUDE_MAX }; + + auto latLng = LatLng {}; + auto projectedMeters = Projection::projectedMetersForLatLng(latLng); + EXPECT_EQ(projectedMeters.northing, projectedMeters.easting); + EXPECT_EQ(latLng, Projection::latLngForProjectedMeters(projectedMeters)); + + latLng = LatLng { std::numeric_limits<double>::lowest(), std::numeric_limits<double>::lowest() }; + projectedMeters = Projection::projectedMetersForLatLng(latLng); + EXPECT_EQ(projectedMeters, Projection::projectedMetersForLatLng(southWest)); + EXPECT_DOUBLE_EQ(projectedMeters.northing, -20037508.342789274); + EXPECT_DOUBLE_EQ(projectedMeters.easting, -20037508.342789244); + + latLng = LatLng { std::numeric_limits<double>::max(), std::numeric_limits<double>::max() }; + projectedMeters = Projection::projectedMetersForLatLng(latLng); + EXPECT_EQ(projectedMeters, Projection::projectedMetersForLatLng(northEast)); + EXPECT_DOUBLE_EQ(projectedMeters.northing, -Projection::projectedMetersForLatLng(southWest).northing); + EXPECT_DOUBLE_EQ(projectedMeters.easting, -Projection::projectedMetersForLatLng(southWest).easting); + + projectedMeters = ProjectedMeters { std::numeric_limits<double>::lowest(), std::numeric_limits<double>::lowest() }; + latLng = Projection::latLngForProjectedMeters(projectedMeters); + EXPECT_EQ(latLng.latitude, -util::LATITUDE_MAX); + EXPECT_EQ(latLng.longitude, -util::LONGITUDE_MAX); + + projectedMeters = ProjectedMeters { std::numeric_limits<double>::max(), std::numeric_limits<double>::max() }; + latLng = Projection::latLngForProjectedMeters(projectedMeters); + EXPECT_EQ(latLng.latitude, util::LATITUDE_MAX); + EXPECT_EQ(latLng.longitude, util::LONGITUDE_MAX); +} |