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 --- android/cpp/jni.cpp | 15 ++-- bin/render.cpp | 2 +- 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 +++++++++++++++ linux/main.cpp | 7 +- macosx/main.mm | 24 +++--- platform/ios/MGLMapView.mm | 91 ++++++++++++++------- src/mbgl/map/map.cpp | 20 ++--- src/mbgl/map/transform.cpp | 52 +++++------- src/mbgl/map/transform_state.cpp | 153 ++++++++++++++++++++++++++++------- src/mbgl/renderer/painter.cpp | 13 ++- src/mbgl/renderer/painter_raster.cpp | 1 - src/mbgl/util/constants.cpp | 6 ++ test/headless/headless.cpp | 2 +- 19 files changed, 403 insertions(+), 146 deletions(-) create mode 100644 include/mbgl/util/geo.hpp create mode 100644 include/mbgl/util/projection.hpp diff --git a/android/cpp/jni.cpp b/android/cpp/jni.cpp index 56de8ff1b4..b1bfefe280 100644 --- a/android/cpp/jni.cpp +++ b/android/cpp/jni.cpp @@ -467,17 +467,16 @@ void JNICALL nativeSetLonLat(JNIEnv *env, jobject obj, jlong nativeMapViewPtr, j return; } - nativeMapView->getMap().setLonLat(lon, lat, std::chrono::milliseconds(duration)); + nativeMapView->getMap().setLatLng(mbgl::LatLng(lat, lon), std::chrono::milliseconds(duration)); } jobject JNICALL nativeGetLonLat(JNIEnv *env, jobject obj, jlong nativeMapViewPtr) { mbgl::Log::Debug(mbgl::Event::JNI, "nativeGetLonLat"); assert(nativeMapViewPtr != 0); NativeMapView *nativeMapView = reinterpret_cast(nativeMapViewPtr); - double lon = 0.0, lat = 0.0; - nativeMapView->getMap().getLonLat(lon, lat); + mbgl::LatLng latLng = nativeMapView->getMap().getLatLng(); - jobject ret = env->NewObject(lonLatClass, lonLatConstructorId, lon, lat); + jobject ret = env->NewObject(lonLatClass, lonLatConstructorId, latLng.longitude, latLng.latitude); if (ret == nullptr) { env->ExceptionDescribe(); return nullptr; @@ -569,17 +568,17 @@ void JNICALL nativeSetLonLatZoom(JNIEnv *env, jobject obj, jlong nativeMapViewPt return; } - nativeMapView->getMap().setLonLatZoom(lon, lat, zoom, std::chrono::milliseconds(duration)); + nativeMapView->getMap().setLatLngZoom(mbgl::LatLng(lat, lon), zoom, std::chrono::milliseconds(duration)); } jobject JNICALL nativeGetLonLatZoom(JNIEnv *env, jobject obj, jlong nativeMapViewPtr) { mbgl::Log::Debug(mbgl::Event::JNI, "nativeGetLonLatZoom"); assert(nativeMapViewPtr != 0); NativeMapView *nativeMapView = reinterpret_cast(nativeMapViewPtr); - double lon = 0.0, lat = 0.0, zoom = 0.0; - nativeMapView->getMap().getLonLatZoom(lon, lat, zoom); + mbgl::LatLng latLng = nativeMapView->getMap().getLatLng(); + double zoom = nativeMapView->getMap().getZoom(); - jobject ret = env->NewObject(lonLatZoomClass, lonLatZoomConstructorId, lon, lat, zoom); + jobject ret = env->NewObject(lonLatZoomClass, lonLatZoomConstructorId, latLng.longitude, latLng.latitude, zoom); if (ret == nullptr) { env->ExceptionDescribe(); return nullptr; diff --git a/bin/render.cpp b/bin/render.cpp index 827f247d6c..c791152d1d 100644 --- a/bin/render.cpp +++ b/bin/render.cpp @@ -100,7 +100,7 @@ int main(int argc, char *argv[]) { view.resize(width, height, pixelRatio); map.resize(width, height, pixelRatio); - map.setLonLatZoom(lon, lat, zoom); + map.setLatLonZoom(LatLng(lat, lon), zoom); map.setBearing(bearing); std::unique_ptr pixels; 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 diff --git a/linux/main.cpp b/linux/main.cpp index 12f77ffe58..6688905ea9 100644 --- a/linux/main.cpp +++ b/linux/main.cpp @@ -72,7 +72,7 @@ int main(int argc, char *argv[]) { // Load settings mbgl::Settings_JSON settings; - map.setLonLatZoom(settings.longitude, settings.latitude, settings.zoom); + map.setLatLngZoom(mbgl::LatLng(settings.latitude, settings.longitude), settings.zoom); map.setBearing(settings.bearing); map.setDebug(settings.debug); @@ -94,7 +94,10 @@ int main(int argc, char *argv[]) { int ret = view->run(); // Save settings - map.getLonLatZoom(settings.longitude, settings.latitude, settings.zoom); + mbgl::LatLng latLng = map.getLatLng(); + settings.latitude = latLng.latitude; + settings.longitude = latLng.longitude; + settings.zoom = map.getZoom(); settings.bearing = map.getBearing(); settings.debug = map.getDebug(); settings.save(); diff --git a/macosx/main.mm b/macosx/main.mm index d61d7d16bf..f59fbda6a8 100644 --- a/macosx/main.mm +++ b/macosx/main.mm @@ -7,6 +7,8 @@ #include #include +#include + #import @interface URLHandler : NSObject @@ -31,15 +33,16 @@ [params setObject:[parts objectAtIndex:1] forKey:[parts objectAtIndex:0]]; } - double latitude = 0, longitude = 0, zoom = 0, bearing = 0; + mbgl::LatLng latLng = mbgl::LatLng(0, 0); + double zoom = 0, bearing = 0; bool hasCenter = false, hasZoom = false, hasBearing = false; NSString *centerString = [params objectForKey:@"center"]; if (centerString) { - NSArray *latlon = [centerString componentsSeparatedByString:@","]; - if ([latlon count] == 2) { - latitude = [[latlon objectAtIndex:0] doubleValue]; - longitude = [[latlon objectAtIndex:1] doubleValue]; + NSArray *latLngValues = [centerString componentsSeparatedByString:@","]; + if ([latLngValues count] == 2) { + latLng.latitude = [latLngValues[0] doubleValue]; + latLng.longitude = [latLngValues[1] doubleValue]; hasCenter = true; } } @@ -58,9 +61,9 @@ if ([self map]) { if (hasCenter && hasZoom) { - [self map]->setLonLatZoom(longitude, latitude, zoom); + [self map]->setLatLngZoom(latLng, zoom); } else if (hasCenter) { - [self map]->setLonLat(longitude, latitude); + [self map]->setLatLng(latLng); } else if (hasZoom) { [self map]->setZoom(zoom); } @@ -120,7 +123,7 @@ int main() { // Load settings mbgl::Settings_NSUserDefaults settings; - map.setLonLatZoom(settings.longitude, settings.latitude, settings.zoom); + map.setLatLngZoom(mbgl::LatLng(settings.latitude, settings.longitude), settings.zoom); map.setBearing(settings.bearing); map.setDebug(settings.debug); @@ -137,7 +140,10 @@ int main() { [reachability stopNotifier]; // Save settings - map.getLonLatZoom(settings.longitude, settings.latitude, settings.zoom); + mbgl::LatLng latLng = map.getLatLng(); + settings.latitude = latLng.latitude; + settings.longitude = latLng.longitude; + settings.zoom = map.getZoom(); settings.bearing = map.getBearing(); settings.debug = map.getDebug(); settings.save(); diff --git a/platform/ios/MGLMapView.mm b/platform/ios/MGLMapView.mm index 7c1f9eb657..a83fcdb93b 100644 --- a/platform/ios/MGLMapView.mm +++ b/platform/ios/MGLMapView.mm @@ -12,6 +12,7 @@ #include #include #include +#include #import "MGLTypes.h" #import "MGLStyleFunctionValue.h" @@ -92,7 +93,7 @@ NSTimeInterval const MGLAnimationDuration = 0.3; @implementation MGLMapView -#pragma mark - Setup - +#pragma mark - Setup & Teardown - @dynamic debugActive; @@ -323,7 +324,7 @@ mbgl::DefaultFileSource *mbglFileSource = nullptr; // set initial position // - mbglMap->setLonLatZoom(0, 0, mbglMap->getMinZoom()); + mbglMap->setLatLngZoom(mbgl::LatLng(0, 0), mbglMap->getMinZoom()); // setup change delegate queue // @@ -498,18 +499,6 @@ mbgl::DefaultFileSource *mbglFileSource = nullptr; [super layoutSubviews]; } -#pragma mark - Conversions - - -+ (CGFloat)degreesToRadians:(CGFloat)degrees -{ - return degrees * M_PI / 180; -} - -+ (CGFloat)radiansToDegrees:(CGFloat)radians -{ - return radians * 180 / M_PI; -} - #pragma mark - Life Cycle - #pragma clang diagnostic push @@ -529,6 +518,17 @@ mbgl::DefaultFileSource *mbglFileSource = nullptr; mbglMap->start(); } +- (void)tintColorDidChange +{ + for (UIView *subview in self.subviews) + { + if ([subview respondsToSelector:@selector(setTintColor:)]) + { + subview.tintColor = self.tintColor; + } + } +} + #pragma mark - Gestures - - (void)handleCompassTapGesture:(id)sender @@ -755,18 +755,7 @@ mbgl::DefaultFileSource *mbglFileSource = nullptr; return ([validSimultaneousGestures containsObject:gestureRecognizer] && [validSimultaneousGestures containsObject:otherGestureRecognizer]); } -#pragma mark - Settings - - -- (void)tintColorDidChange -{ - for (UIView *subview in self.subviews) - { - if ([subview respondsToSelector:@selector(setTintColor:)]) - { - subview.tintColor = self.tintColor; - } - } -} +#pragma mark - Properties - #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wunused-parameter" @@ -827,11 +816,13 @@ mbgl::DefaultFileSource *mbglFileSource = nullptr; mbglMap->toggleDebug(); } +#pragma mark - Geography - + - (void)setCenterCoordinate:(CLLocationCoordinate2D)coordinate animated:(BOOL)animated { CGFloat duration = (animated ? MGLAnimationDuration : 0); - mbglMap->setLonLat(coordinate.longitude, coordinate.latitude, secondsAsDuration(duration)); + mbglMap->setLatLng(mbgl::LatLng(coordinate.latitude, coordinate.longitude), secondsAsDuration(duration)); } - (void)setCenterCoordinate:(CLLocationCoordinate2D)centerCoordinate @@ -841,17 +832,16 @@ mbgl::DefaultFileSource *mbglFileSource = nullptr; - (CLLocationCoordinate2D)centerCoordinate { - double lon, lat; - mbglMap->getLonLat(lon, lat); + mbgl::LatLng latLng = mbglMap->getLatLng(); - return CLLocationCoordinate2DMake(lat, lon); + return CLLocationCoordinate2DMake(latLng.latitude, latLng.longitude); } - (void)setCenterCoordinate:(CLLocationCoordinate2D)centerCoordinate zoomLevel:(double)zoomLevel animated:(BOOL)animated { CGFloat duration = (animated ? MGLAnimationDuration : 0); - mbglMap->setLonLatZoom(centerCoordinate.longitude, centerCoordinate.latitude, zoomLevel, secondsAsDuration(duration)); + mbglMap->setLatLngZoom(mbgl::LatLng(centerCoordinate.latitude, centerCoordinate.longitude), zoomLevel, secondsAsDuration(duration)); [self unrotateIfNeededAnimated:animated]; } @@ -899,6 +889,35 @@ mbgl::DefaultFileSource *mbglFileSource = nullptr; [self setDirection:direction animated:NO]; } +- (CLLocationCoordinate2D)convertPoint:(CGPoint)point toCoordinateFromView:(UIView *)view +{ + CGPoint convertedPoint = [self convertPoint:point fromView:view]; + + // flip y coordinate for iOS view origin top left + // + convertedPoint.y = self.bounds.size.height - convertedPoint.y; + + mbgl::LatLng latLng = mbglMap->latLngForPixel(mbgl::vec2(convertedPoint.x, convertedPoint.y)); + + return CLLocationCoordinate2DMake(latLng.latitude, latLng.longitude); +} + +- (CGPoint)convertCoordinate:(CLLocationCoordinate2D)coordinate toPointToView:(UIView *)view +{ + mbgl::vec2 pixel = mbglMap->pixelForLatLng(mbgl::LatLng(coordinate.latitude, coordinate.longitude)); + + // flip y coordinate for iOS view origin in top left + // + pixel.y = self.bounds.size.height - pixel.y; + + return [self convertPoint:CGPointMake(pixel.x, pixel.y) toView:view]; +} + +- (CLLocationDistance)metersPerPixelAtLatitude:(CLLocationDegrees)latitude +{ + return mbglMap->getMetersPerPixelAtLatitude(latitude, self.zoomLevel); +} + #pragma mark - Styling - - (NSDictionary *)getRawStyle @@ -1337,6 +1356,16 @@ mbgl::DefaultFileSource *mbglFileSource = nullptr; #pragma mark - Utility - ++ (CGFloat)degreesToRadians:(CGFloat)degrees +{ + return degrees * M_PI / 180; +} + ++ (CGFloat)radiansToDegrees:(CGFloat)radians +{ + return radians * 180 / M_PI; +} + - (void)animateWithDelay:(NSTimeInterval)delay animations:(void (^)(void))animations { dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delay * NSEC_PER_SEC)), dispatch_get_main_queue(), animations); diff --git a/src/mbgl/map/map.cpp b/src/mbgl/map/map.cpp index 5aff7a2b78..ed20ec24a6 100644 --- a/src/mbgl/map/map.cpp +++ b/src/mbgl/map/map.cpp @@ -407,15 +407,11 @@ void Map::moveBy(double dx, double dy, std::chrono::steady_clock::duration durat update(); } -void Map::setLonLat(double lon, double lat, std::chrono::steady_clock::duration duration) { - transform.setLonLat(lon, lat, duration); +void Map::setLatLng(LatLng latLng, std::chrono::steady_clock::duration duration) { + transform.setLatLng(latLng, duration); update(); } -void Map::getLonLat(double& lon, double& lat) const { - transform.getLonLat(lon, lat); -} - void Map::startPanning() { transform.startPanning(); update(); @@ -428,7 +424,7 @@ void Map::stopPanning() { void Map::resetPosition() { transform.setAngle(0); - transform.setLonLat(0, 0); + transform.setLatLng(LatLng(0, 0)); transform.setZoom(0); update(); } @@ -459,15 +455,11 @@ double Map::getZoom() const { return transform.getZoom(); } -void Map::setLonLatZoom(double lon, double lat, double zoom, std::chrono::steady_clock::duration duration) { - transform.setLonLatZoom(lon, lat, zoom, duration); +void Map::setLatLngZoom(LatLng latLng, double zoom, std::chrono::steady_clock::duration duration) { + transform.setLatLngZoom(latLng, zoom, duration); update(); } -void Map::getLonLatZoom(double& lon, double& lat, double& zoom) const { - transform.getLonLatZoom(lon, lat, zoom); -} - void Map::resetZoom() { setZoom(0); } @@ -697,7 +689,7 @@ void Map::prepare() { void Map::render() { assert(painter); painter->render(*style, activeSources, - state, animationTime); + state, animationTime); // Schedule another rerender when we definitely need a next frame. if (transform.needsTransition() || style->hasTransitions()) { update(); diff --git a/src/mbgl/map/transform.cpp b/src/mbgl/map/transform.cpp index 7952531192..f3b14731e1 100644 --- a/src/mbgl/map/transform.cpp +++ b/src/mbgl/map/transform.cpp @@ -11,9 +11,6 @@ using namespace mbgl; -const double D2R = M_PI / 180.0; -const double M2PI = 2 * M_PI; - Transform::Transform(View &view_) : view(view_) { @@ -83,44 +80,34 @@ void Transform::_moveBy(const double dx, const double dy, const std::chrono::ste duration); } -void Transform::setLonLat(const double lon, const double lat, const std::chrono::steady_clock::duration duration) { +void Transform::setLatLng(const LatLng latLng, const std::chrono::steady_clock::duration duration) { std::lock_guard lock(mtx); - const double f = std::fmin(std::fmax(std::sin(D2R * lat), -0.9999), 0.9999); - double xn = -lon * Bc; - double yn = 0.5 * Cc * std::log((1 + f) / (1 - f)); + const double m = 1 - 1e-15; + const double f = std::fmin(std::fmax(std::sin(util::DEG2RAD * latLng.latitude), -m), m); + + double xn = -latLng.longitude * current.Bc; + double yn = 0.5 * current.Cc * std::log((1 + f) / (1 - f)); _setScaleXY(current.scale, xn, yn, duration); } -void Transform::setLonLatZoom(const double lon, const double lat, const double zoom, - const std::chrono::steady_clock::duration duration) { +void Transform::setLatLngZoom(const LatLng latLng, const double zoom, const std::chrono::steady_clock::duration duration) { std::lock_guard lock(mtx); double new_scale = std::pow(2.0, zoom); const double s = new_scale * util::tileSize; - Bc = s / 360; - Cc = s / (2 * M_PI); - - const double f = std::fmin(std::fmax(std::sin(D2R * lat), -0.9999), 0.9999); - double xn = -lon * Bc; - double yn = 0.5 * Cc * log((1 + f) / (1 - f)); - - _setScaleXY(new_scale, xn, yn, duration); -} + current.Bc = s / 360; + current.Cc = s / util::M2PI; -void Transform::getLonLat(double &lon, double &lat) const { - std::lock_guard lock(mtx); - - final.getLonLat(lon, lat); -} + const double m = 1 - 1e-15; + const double f = std::fmin(std::fmax(std::sin(util::DEG2RAD * latLng.latitude), -m), m); -void Transform::getLonLatZoom(double &lon, double &lat, double &zoom) const { - std::lock_guard lock(mtx); + double xn = -latLng.longitude * current.Bc; + double yn = 0.5 * current.Cc * std::log((1 + f) / (1 - f)); - getLonLat(lon, lat); - zoom = getZoom(); + _setScaleXY(new_scale, xn, yn, duration); } void Transform::startPanning() { @@ -181,7 +168,7 @@ void Transform::setZoom(const double zoom, const std::chrono::steady_clock::dura double Transform::getZoom() const { std::lock_guard lock(mtx); - return std::log(final.scale) / M_LN2; + return final.getZoom(); } double Transform::getScale() const { @@ -293,8 +280,8 @@ void Transform::_setScaleXY(const double new_scale, const double xn, const doubl } const double s = final.scale * util::tileSize; - Bc = s / 360; - Cc = s / (2 * M_PI); + current.Bc = s / 360; + current.Cc = s / util::M2PI; view.notifyMapChange(duration != std::chrono::steady_clock::duration::zero() ? MapChangeRegionDidChangeAnimated : @@ -380,9 +367,9 @@ void Transform::_setAngle(double new_angle, const std::chrono::steady_clock::dur MapChangeRegionWillChange); while (new_angle > M_PI) - new_angle -= M2PI; + new_angle -= util::M2PI; while (new_angle <= -M_PI) - new_angle += M2PI; + new_angle += util::M2PI; final.angle = new_angle; @@ -434,6 +421,7 @@ void Transform::_clearRotating() { } } + #pragma mark - Transition bool Transform::needsTransition() const { diff --git a/src/mbgl/map/transform_state.cpp b/src/mbgl/map/transform_state.cpp index a7da8ccab2..8c007210e0 100644 --- a/src/mbgl/map/transform_state.cpp +++ b/src/mbgl/map/transform_state.cpp @@ -1,11 +1,10 @@ #include +#include #include #include using namespace mbgl; -const double R2D = 180.0 / M_PI; - #pragma mark - Matrix void TransformState::matrixFor(mat4& matrix, const Tile::ID& id) const { @@ -90,34 +89,43 @@ float TransformState::getPixelRatio() const { return pixelRatio; } -float TransformState::worldSize() const { - return scale * util::tileSize; -} - -float TransformState::lngX(float lon) const { - return (180 + lon) * worldSize() / 360; -} - -float TransformState::latY(float lat) const { - float lat_y = 180 / M_PI * std::log(std::tan(M_PI / 4 + lat * M_PI / 360)); - return (180 - lat_y) * worldSize() / 360; -} -std::array TransformState::locationCoordinate(float lon, float lat) const { - float k = std::pow(2, getIntegerZoom()) / worldSize(); - return {{ - lngX(lon) * k, - latY(lat) * k - }}; -} - -void TransformState::getLonLat(double &lon, double &lat) const { - const double s = scale * util::tileSize; - const double Bc = s / 360; - const double Cc = s / (2 * M_PI); - - lon = -x / Bc; - lat = R2D * (2 * std::atan(std::exp(y / Cc)) - 0.5 * M_PI); +#pragma mark - Position + +const LatLng TransformState::getLatLng() const { + LatLng ll; + + ll.longitude = -x / Bc; + ll.latitude = util::RAD2DEG * (2 * std::atan(std::exp(y / Cc)) - 0.5 * M_PI); + + // adjust for world wrap + while (ll.longitude > 180) ll.longitude -= 180; + while (ll.longitude < -180) ll.longitude += 180; + + // adjust for date line + double w = util::tileSize * scale / 2; + double x_ = x; + if (x_ > w) { + while (x_ > w) { + x_ -= w; + if (ll.longitude < 0) { + ll.longitude += 180; + } else if (ll.longitude > 0) { + ll.longitude -= 180; + } + } + } else if (x_ < -w) { + while (x_ < -w) { + x_ += w; + if (ll.longitude < 0) { + ll.longitude -= 180; + } else if (ll.longitude > 0) { + ll.longitude -= 180; + } + } + } + + return ll; } @@ -151,6 +159,93 @@ float TransformState::getAngle() const { } +#pragma mark - Projection + +const vec2 TransformState::pixelForLatLng(const LatLng latLng) const { + LatLng ll = getLatLng(); + double zoom = getZoom(); + + const double centerX = width / 2; + const double centerY = height / 2; + + const double m = Projection::getMetersPerPixelAtLatitude(0, zoom); + + const double angle_sin = std::sin(-angle); + const double angle_cos = std::cos(-angle); + + const ProjectedMeters givenMeters = Projection::projectedMetersForLatLng(latLng); + + const double givenAbsoluteX = givenMeters.easting / m; + const double givenAbsoluteY = givenMeters.northing / m; + + const ProjectedMeters centerMeters = Projection::projectedMetersForLatLng(ll); + + const double centerAbsoluteX = centerMeters.easting / m; + const double centerAbsoluteY = centerMeters.northing / m; + + const double deltaX = givenAbsoluteX - centerAbsoluteX; + const double deltaY = givenAbsoluteY - centerAbsoluteY; + + const double translatedX = deltaX + centerX; + const double translatedY = deltaY + centerY; + + const double rotatedX = translatedX * angle_cos - translatedY * angle_sin; + const double rotatedY = translatedX * angle_sin + translatedY * angle_cos; + + const double rotatedCenterX = centerX * angle_cos - centerY * angle_sin; + const double rotatedCenterY = centerX * angle_sin + centerY * angle_cos; + + double x_ = rotatedX + (centerX - rotatedCenterX); + double y_ = rotatedY + (centerY - rotatedCenterY); + + return vec2(x_, y_); +} + +const LatLng TransformState::latLngForPixel(const vec2 pixel) const { + LatLng ll = getLatLng(); + double zoom = getZoom(); + + const double centerX = width / 2; + const double centerY = height / 2; + + const double m = Projection::getMetersPerPixelAtLatitude(0, zoom); + + const double angle_sin = std::sin(angle); + const double angle_cos = std::cos(angle); + + const double unrotatedCenterX = centerX * angle_cos - centerY * angle_sin; + const double unrotatedCenterY = centerX * angle_sin + centerY * angle_cos; + + const double unrotatedX = pixel.x * angle_cos - pixel.y * angle_sin; + const double unrotatedY = pixel.x * angle_sin + pixel.y * angle_cos; + + const double givenX = unrotatedX + (centerX - unrotatedCenterX); + const double givenY = unrotatedY + (centerY - unrotatedCenterY); + + const ProjectedMeters centerMeters = Projection::projectedMetersForLatLng(ll); + + const double centerAbsoluteX = centerMeters.easting / m; + const double centerAbsoluteY = centerMeters.northing / m; + + const double givenAbsoluteX = givenX + centerAbsoluteX - centerX; + const double givenAbsoluteY = givenY + centerAbsoluteY - centerY; + + ProjectedMeters givenMeters = ProjectedMeters(givenAbsoluteY * m, givenAbsoluteX * m); + + // adjust for date line + ProjectedMeters sw, ne; + Projection::getWorldBoundsMeters(sw, ne); + double d = ne.easting - sw.easting; + if (ll.longitude > 0 && givenMeters.easting > centerMeters.easting) givenMeters.easting -= d; + + // adjust for world wrap + while (givenMeters.easting < sw.easting) givenMeters.easting += d; + while (givenMeters.easting > ne.easting) givenMeters.easting -= d; + + return Projection::latLngForProjectedMeters(givenMeters); +} + + #pragma mark - Changing bool TransformState::isChanging() const { diff --git a/src/mbgl/renderer/painter.cpp b/src/mbgl/renderer/painter.cpp index 116ae3ad2f..044604f5b4 100644 --- a/src/mbgl/renderer/painter.cpp +++ b/src/mbgl/renderer/painter.cpp @@ -392,9 +392,8 @@ void Painter::renderBackground(util::ptr layer_desc) { patternShader->u_mix = properties.image.t; patternShader->u_opacity = properties.opacity; - double lon, lat; - state.getLonLat(lon, lat); - std::array center = state.locationCoordinate(lon, lat); + LatLng latLng = state.getLatLng(); + vec2 center = state.pixelForLatLng(latLng); float scale = 1 / std::pow(2, zoomFraction); std::array sizeA = imagePosA.size; @@ -404,8 +403,8 @@ void Painter::renderBackground(util::ptr layer_desc) { 1.0f / (sizeA[0] * properties.image.fromScale), 1.0f / (sizeA[1] * properties.image.fromScale)); matrix::translate(matrixA, matrixA, - std::fmod(center[0] * 512, sizeA[0] * properties.image.fromScale), - std::fmod(center[1] * 512, sizeA[1] * properties.image.fromScale)); + std::fmod(center.x * 512, sizeA[0] * properties.image.fromScale), + std::fmod(center.y * 512, sizeA[1] * properties.image.fromScale)); matrix::rotate(matrixA, matrixA, -state.getAngle()); matrix::scale(matrixA, matrixA, scale * state.getWidth() / 2, @@ -418,8 +417,8 @@ void Painter::renderBackground(util::ptr layer_desc) { 1.0f / (sizeB[0] * properties.image.toScale), 1.0f / (sizeB[1] * properties.image.toScale)); matrix::translate(matrixB, matrixB, - std::fmod(center[0] * 512, sizeB[0] * properties.image.toScale), - std::fmod(center[1] * 512, sizeB[1] * properties.image.toScale)); + std::fmod(center.x * 512, sizeB[0] * properties.image.toScale), + std::fmod(center.y * 512, sizeB[1] * properties.image.toScale)); matrix::rotate(matrixB, matrixB, -state.getAngle()); matrix::scale(matrixB, matrixB, scale * state.getWidth() / 2, diff --git a/src/mbgl/renderer/painter_raster.cpp b/src/mbgl/renderer/painter_raster.cpp index a85b773788..4ece54977b 100644 --- a/src/mbgl/renderer/painter_raster.cpp +++ b/src/mbgl/renderer/painter_raster.cpp @@ -5,7 +5,6 @@ #include #include #include -#include using namespace mbgl; diff --git a/src/mbgl/util/constants.cpp b/src/mbgl/util/constants.cpp index 3d1422e6c7..ccdbeba23a 100644 --- a/src/mbgl/util/constants.cpp +++ b/src/mbgl/util/constants.cpp @@ -2,6 +2,12 @@ const float mbgl::util::tileSize = 512.0f; +const double mbgl::util::DEG2RAD = M_PI / 180.0; +const double mbgl::util::RAD2DEG = 180.0 / M_PI; +const double mbgl::util::M2PI = 2 * M_PI; +const double mbgl::util::EARTH_RADIUS_M = 6378137; +const double mbgl::util::LATITUDE_MAX = 85.05112878; + #if defined(DEBUG) const bool mbgl::debug::tileParseWarnings = false; const bool mbgl::debug::styleParseWarnings = false; diff --git a/test/headless/headless.cpp b/test/headless/headless.cpp index e7e1153923..b92002aa23 100644 --- a/test/headless/headless.cpp +++ b/test/headless/headless.cpp @@ -147,7 +147,7 @@ TEST_P(HeadlessTest, render) { view.resize(width, height, pixelRatio); map.resize(width, height, pixelRatio); - map.setLonLatZoom(longitude, latitude, zoom); + map.setLatLngZoom(mbgl::LatLng(latitude, longitude), zoom); map.setBearing(bearing); // Run the loop. It will terminate when we don't have any further listeners. -- cgit v1.2.1