From 6e41664cb033ee5edf6ae5ac66ed518d9f0d1f89 Mon Sep 17 00:00:00 2001 From: "Justin R. Miller" Date: Mon, 16 Feb 2015 09:52:36 -0800 Subject: fixes #476 & #853: pixel/meter/latlng conversion routines in core & iOS --- include/mbgl/ios/MGLMapView.h | 24 +++++++++++++ include/mbgl/map/map.hpp | 19 ++++++++--- include/mbgl/map/transform.hpp | 12 +++---- include/mbgl/map/transform_state.hpp | 12 +++++++ include/mbgl/util/constants.hpp | 7 ++++ include/mbgl/util/geo.hpp | 24 +++++++++++++ include/mbgl/util/projection.hpp | 65 ++++++++++++++++++++++++++++++++++++ 7 files changed, 152 insertions(+), 11 deletions(-) create mode 100644 include/mbgl/util/geo.hpp create mode 100644 include/mbgl/util/projection.hpp (limited to 'include') diff --git a/include/mbgl/ios/MGLMapView.h b/include/mbgl/ios/MGLMapView.h index 6535138d3b..f954340262 100644 --- a/include/mbgl/ios/MGLMapView.h +++ b/include/mbgl/ios/MGLMapView.h @@ -131,6 +131,30 @@ /** Resets the map rotation to a northern heading. */ - (void)resetNorth; +#pragma mark - Converting Map Coordinates + +/** @name Converting Map Coordinates */ + +/** Converts a point in the specified view’s coordinate system to a map coordinate. +* @param point The point you want to convert. +* @param view The view that serves as the reference coordinate system for the `point` parameter. +* @return The map coordinate at the specified point. */ +- (CLLocationCoordinate2D)convertPoint:(CGPoint)point toCoordinateFromView:(UIView *)view; + +/** Converts a map coordinate to a point in the specified view. +* @param coordinate The map coordinate for which you want to find the corresponding point. +* @param view The view in whose coordinate system you want to locate the specified map coordinate. If this parameter is `nil`, the returned point is specified in the window’s coordinate system. If `view` is not `nil`, it must belong to the same window as the map view. +* @return The point (in the appropriate view or window coordinate system) corresponding to the specified latitude and longitude value. */ +- (CGPoint)convertCoordinate:(CLLocationCoordinate2D)coordinate toPointToView:(UIView *)view; + +/** Returns the distance spanned by one pixel at the specified latitude and current zoom level. +* +* The distance between pixels decreases as the latitude approaches the poles. This relationship parallels the relationship between longitudinal coordinates at different latitudes. +* +* @param latitude The latitude for which to return the value. +* @return The distance (in meters) spanned by a single pixel. */ +- (CLLocationDistance)metersPerPixelAtLatitude:(CLLocationDegrees)latitude; + #pragma mark - Styling the Map /** @name Styling the Map */ diff --git a/include/mbgl/map/map.hpp b/include/mbgl/map/map.hpp index ae0228544e..844970bb8f 100644 --- a/include/mbgl/map/map.hpp +++ b/include/mbgl/map/map.hpp @@ -2,9 +2,12 @@ #define MBGL_MAP_MAP #include +#include +#include #include #include #include +#include #include #include @@ -94,8 +97,8 @@ public: // Position void moveBy(double dx, double dy, std::chrono::steady_clock::duration duration = std::chrono::steady_clock::duration::zero()); - void setLonLat(double lon, double lat, std::chrono::steady_clock::duration duration = std::chrono::steady_clock::duration::zero()); - void getLonLat(double &lon, double &lat) const; + void setLatLng(LatLng latLng, std::chrono::steady_clock::duration duration = std::chrono::steady_clock::duration::zero()); + inline const LatLng getLatLng() const { return state.getLatLng(); } void startPanning(); void stopPanning(); void resetPosition(); @@ -106,8 +109,7 @@ public: double getScale() const; void setZoom(double zoom, std::chrono::steady_clock::duration duration = std::chrono::steady_clock::duration::zero()); double getZoom() const; - void setLonLatZoom(double lon, double lat, double zoom, std::chrono::steady_clock::duration duration = std::chrono::steady_clock::duration::zero()); - void getLonLatZoom(double &lon, double &lat, double &zoom) const; + void setLatLngZoom(LatLng latLng, double zoom, std::chrono::steady_clock::duration duration = std::chrono::steady_clock::duration::zero()); void resetZoom(); void startScaling(); void stopScaling(); @@ -127,6 +129,15 @@ public: void setAccessToken(const std::string &token); const std::string &getAccessToken() const; + // Projection + inline void getWorldBoundsMeters(ProjectedMeters &sw, ProjectedMeters &ne) const { Projection::getWorldBoundsMeters(sw, ne); } + inline void getWorldBoundsLatLng(LatLng &sw, LatLng &ne) const { Projection::getWorldBoundsLatLng(sw, ne); } + inline double getMetersPerPixelAtLatitude(const double lat, const double zoom) const { return Projection::getMetersPerPixelAtLatitude(lat, zoom); } + inline const ProjectedMeters projectedMetersForLatLng(const LatLng latLng) const { return Projection::projectedMetersForLatLng(latLng); } + inline const LatLng latLngForProjectedMeters(const ProjectedMeters projectedMeters) const { return Projection::latLngForProjectedMeters(projectedMeters); } + inline const vec2 pixelForLatLng(const LatLng latLng) const { return state.pixelForLatLng(latLng); } + inline const LatLng latLngForPixel(const vec2 pixel) const { return state.latLngForPixel(pixel); } + // Debug void setDebug(bool value); void toggleDebug(); diff --git a/include/mbgl/map/transform.hpp b/include/mbgl/map/transform.hpp index 246270da13..94bea11a45 100644 --- a/include/mbgl/map/transform.hpp +++ b/include/mbgl/map/transform.hpp @@ -2,7 +2,9 @@ #define MBGL_MAP_TRANSFORM #include +#include #include +#include #include #include @@ -26,10 +28,9 @@ public: // Position void moveBy(double dx, double dy, std::chrono::steady_clock::duration duration = std::chrono::steady_clock::duration::zero()); - void setLonLat(double lon, double lat, std::chrono::steady_clock::duration duration = std::chrono::steady_clock::duration::zero()); - void setLonLatZoom(double lon, double lat, double zoom, std::chrono::steady_clock::duration duration = std::chrono::steady_clock::duration::zero()); - void getLonLat(double& lon, double& lat) const; - void getLonLatZoom(double& lon, double& lat, double& zoom) const; + void setLatLng(LatLng latLng, std::chrono::steady_clock::duration duration = std::chrono::steady_clock::duration::zero()); + void setLatLngZoom(LatLng latLng, double zoom, std::chrono::steady_clock::duration duration = std::chrono::steady_clock::duration::zero()); + inline const LatLng getLatLng() const { return current.getLatLng(); } void startPanning(); void stopPanning(); @@ -91,9 +92,6 @@ private: const double min_scale = std::pow(2, 0); const double max_scale = std::pow(2, 18); - // cache values for spherical mercator math - double Bc, Cc; - std::forward_list> transitions; util::ptr scale_timeout; util::ptr rotate_timeout; diff --git a/include/mbgl/map/transform_state.hpp b/include/mbgl/map/transform_state.hpp index 6dc34bd873..5f2dfa49e4 100644 --- a/include/mbgl/map/transform_state.hpp +++ b/include/mbgl/map/transform_state.hpp @@ -4,6 +4,8 @@ #include #include +#include +#include #include #include @@ -34,6 +36,9 @@ public: std::array locationCoordinate(float lon, float lat) const; void getLonLat(double &lon, double &lat) const; + // Position + const LatLng getLatLng() const; + // Zoom float getNormalizedZoom() const; double getZoom() const; @@ -44,6 +49,10 @@ public: // Rotation float getAngle() const; + // Projection + const vec2 pixelForLatLng(const LatLng latLng) const; + const LatLng latLngForPixel(const vec2 pixel) const; + // Changing bool isChanging() const; @@ -61,6 +70,9 @@ private: // map scale factor float pixelRatio = 0; + // cache values for spherical mercator math + double Bc, Cc; + // animation state bool rotating = false; bool scaling = false; diff --git a/include/mbgl/util/constants.hpp b/include/mbgl/util/constants.hpp index 78c92643d2..069e6e41ae 100644 --- a/include/mbgl/util/constants.hpp +++ b/include/mbgl/util/constants.hpp @@ -8,6 +8,13 @@ namespace mbgl { namespace util { extern const float tileSize; + +extern const double DEG2RAD; +extern const double RAD2DEG; +extern const double M2PI; +extern const double EARTH_RADIUS_M; +extern const double LATITUDE_MAX; + } namespace debug { diff --git a/include/mbgl/util/geo.hpp b/include/mbgl/util/geo.hpp new file mode 100644 index 0000000000..1d9986bd91 --- /dev/null +++ b/include/mbgl/util/geo.hpp @@ -0,0 +1,24 @@ +#ifndef MBGL_UTIL_GEO +#define MBGL_UTIL_GEO + +namespace mbgl { + +struct LatLng { + double latitude = 0; + double longitude = 0; + + inline LatLng(double lat = 0, double lon = 0) + : latitude(lat), longitude(lon) {} +}; + +struct ProjectedMeters { + double northing = 0; + double easting = 0; + + inline ProjectedMeters(double n = 0, double e = 0) + : northing(n), easting(e) {} +}; + +} + +#endif diff --git a/include/mbgl/util/projection.hpp b/include/mbgl/util/projection.hpp new file mode 100644 index 0000000000..f301d93005 --- /dev/null +++ b/include/mbgl/util/projection.hpp @@ -0,0 +1,65 @@ +#ifndef MBGL_UTIL_PROJECTION +#define MBGL_UTIL_PROJECTION + +#include +#include + +#include + +namespace mbgl { + +class Projection { + +public: + static inline void getWorldBoundsMeters(ProjectedMeters &sw, ProjectedMeters &ne) { + const double d = util::EARTH_RADIUS_M * M_PI; + + sw.easting = -d; + sw.northing = -d; + + ne.easting = d; + ne.northing = d; + } + + static inline void getWorldBoundsLatLng(LatLng &sw, LatLng &ne) { + ProjectedMeters projectedMetersSW = ProjectedMeters(); + ProjectedMeters projectedMetersNE = ProjectedMeters(); + + getWorldBoundsMeters(projectedMetersSW, projectedMetersNE); + + sw = latLngForProjectedMeters(projectedMetersSW); + ne = latLngForProjectedMeters(projectedMetersNE); + } + + static inline double getMetersPerPixelAtLatitude(const double lat, const double zoom) { + const double mapPixelWidthAtZoom = std::pow(2.0, zoom) * util::tileSize; + const double constrainedLatitude = std::fmin(std::fmax(lat, -util::LATITUDE_MAX), util::LATITUDE_MAX); + + return std::cos(constrainedLatitude * util::DEG2RAD) * util::M2PI * util::EARTH_RADIUS_M / mapPixelWidthAtZoom; + } + + static inline const ProjectedMeters projectedMetersForLatLng(const LatLng latLng) { + const double constrainedLatitude = std::fmin(std::fmax(latLng.latitude, -util::LATITUDE_MAX), util::LATITUDE_MAX); + + const double m = 1 - 1e-15; + const double f = std::fmin(std::fmax(std::sin(util::DEG2RAD * constrainedLatitude), -m), m); + + const double easting = util::EARTH_RADIUS_M * latLng.longitude * util::DEG2RAD; + const double northing = 0.5 * util::EARTH_RADIUS_M * std::log((1 + f) / (1 - f)); + + return ProjectedMeters(northing, easting); + } + + static inline const LatLng latLngForProjectedMeters(const ProjectedMeters projectedMeters) { + double latitude = (2 * std::atan(std::exp(projectedMeters.northing / util::EARTH_RADIUS_M)) - (M_PI / 2)) * util::RAD2DEG; + double longitude = projectedMeters.easting * util::RAD2DEG / util::EARTH_RADIUS_M; + + latitude = std::fmin(std::fmax(latitude, -util::LATITUDE_MAX), util::LATITUDE_MAX); + + return LatLng(latitude, longitude); + } +}; + +} + +#endif -- cgit v1.2.1