Merge pull request #886 from mapbox/conversions-squash
fixes #476 & #853: pixel/meter/latlng conversion routines in core & iOS
This commit is contained in:
@@ -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<NativeMapView *>(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<NativeMapView *>(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;
|
||||
|
||||
@@ -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<uint32_t[]> pixels;
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -2,9 +2,12 @@
|
||||
#define MBGL_MAP_MAP
|
||||
|
||||
#include <mbgl/map/transform.hpp>
|
||||
#include <mbgl/util/geo.hpp>
|
||||
#include <mbgl/util/projection.hpp>
|
||||
#include <mbgl/util/noncopyable.hpp>
|
||||
#include <mbgl/util/uv.hpp>
|
||||
#include <mbgl/util/ptr.hpp>
|
||||
#include <mbgl/util/vec.hpp>
|
||||
|
||||
#include <cstdint>
|
||||
#include <atomic>
|
||||
@@ -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<double> pixelForLatLng(const LatLng latLng) const { return state.pixelForLatLng(latLng); }
|
||||
inline const LatLng latLngForPixel(const vec2<double> pixel) const { return state.latLngForPixel(pixel); }
|
||||
|
||||
// Debug
|
||||
void setDebug(bool value);
|
||||
void toggleDebug();
|
||||
|
||||
@@ -2,7 +2,9 @@
|
||||
#define MBGL_MAP_TRANSFORM
|
||||
|
||||
#include <mbgl/map/transform_state.hpp>
|
||||
#include <mbgl/util/geo.hpp>
|
||||
#include <mbgl/util/noncopyable.hpp>
|
||||
#include <mbgl/util/vec.hpp>
|
||||
|
||||
#include <cstdint>
|
||||
#include <cmath>
|
||||
@@ -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<util::ptr<util::transition>> transitions;
|
||||
util::ptr<util::transition> scale_timeout;
|
||||
util::ptr<util::transition> rotate_timeout;
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
#include <mbgl/map/tile.hpp>
|
||||
|
||||
#include <mbgl/util/mat4.hpp>
|
||||
#include <mbgl/util/geo.hpp>
|
||||
#include <mbgl/util/vec.hpp>
|
||||
|
||||
#include <cstdint>
|
||||
#include <array>
|
||||
@@ -34,6 +36,9 @@ public:
|
||||
std::array<float, 2> 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<double> pixelForLatLng(const LatLng latLng) const;
|
||||
const LatLng latLngForPixel(const vec2<double> 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;
|
||||
|
||||
@@ -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 {
|
||||
|
||||
24
include/mbgl/util/geo.hpp
Normal file
24
include/mbgl/util/geo.hpp
Normal file
@@ -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
|
||||
65
include/mbgl/util/projection.hpp
Normal file
65
include/mbgl/util/projection.hpp
Normal file
@@ -0,0 +1,65 @@
|
||||
#ifndef MBGL_UTIL_PROJECTION
|
||||
#define MBGL_UTIL_PROJECTION
|
||||
|
||||
#include <mbgl/util/constants.hpp>
|
||||
#include <mbgl/util/geo.hpp>
|
||||
|
||||
#include <cmath>
|
||||
|
||||
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
|
||||
@@ -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();
|
||||
|
||||
@@ -7,6 +7,8 @@
|
||||
#include <mbgl/storage/default/sqlite_cache.hpp>
|
||||
#include <mbgl/storage/network_status.hpp>
|
||||
|
||||
#include <mbgl/util/geo.hpp>
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@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();
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
#include <mbgl/storage/default_file_source.hpp>
|
||||
#include <mbgl/storage/default/sqlite_cache.hpp>
|
||||
#include <mbgl/storage/network_status.hpp>
|
||||
#include <mbgl/util/geo.hpp>
|
||||
|
||||
#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<double>(convertedPoint.x, convertedPoint.y));
|
||||
|
||||
return CLLocationCoordinate2DMake(latLng.latitude, latLng.longitude);
|
||||
}
|
||||
|
||||
- (CGPoint)convertCoordinate:(CLLocationCoordinate2D)coordinate toPointToView:(UIView *)view
|
||||
{
|
||||
mbgl::vec2<double> 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);
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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,46 +80,36 @@ 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<std::recursive_mutex> 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<std::recursive_mutex> 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);
|
||||
current.Bc = s / 360;
|
||||
current.Cc = s / util::M2PI;
|
||||
|
||||
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));
|
||||
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(new_scale, xn, yn, duration);
|
||||
}
|
||||
|
||||
void Transform::getLonLat(double &lon, double &lat) const {
|
||||
std::lock_guard<std::recursive_mutex> lock(mtx);
|
||||
|
||||
final.getLonLat(lon, lat);
|
||||
}
|
||||
|
||||
void Transform::getLonLatZoom(double &lon, double &lat, double &zoom) const {
|
||||
std::lock_guard<std::recursive_mutex> lock(mtx);
|
||||
|
||||
getLonLat(lon, lat);
|
||||
zoom = getZoom();
|
||||
}
|
||||
|
||||
void Transform::startPanning() {
|
||||
std::lock_guard<std::recursive_mutex> lock(mtx);
|
||||
|
||||
@@ -181,7 +168,7 @@ void Transform::setZoom(const double zoom, const std::chrono::steady_clock::dura
|
||||
double Transform::getZoom() const {
|
||||
std::lock_guard<std::recursive_mutex> 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 {
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
#include <mbgl/map/transform_state.hpp>
|
||||
#include <mbgl/util/projection.hpp>
|
||||
#include <mbgl/util/constants.hpp>
|
||||
#include <mbgl/util/box.hpp>
|
||||
|
||||
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;
|
||||
}
|
||||
#pragma mark - Position
|
||||
|
||||
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;
|
||||
}
|
||||
const LatLng TransformState::getLatLng() const {
|
||||
LatLng ll;
|
||||
|
||||
std::array<float, 2> TransformState::locationCoordinate(float lon, float lat) const {
|
||||
float k = std::pow(2, getIntegerZoom()) / worldSize();
|
||||
return {{
|
||||
lngX(lon) * k,
|
||||
latY(lat) * k
|
||||
}};
|
||||
}
|
||||
ll.longitude = -x / Bc;
|
||||
ll.latitude = util::RAD2DEG * (2 * std::atan(std::exp(y / Cc)) - 0.5 * M_PI);
|
||||
|
||||
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);
|
||||
// adjust for world wrap
|
||||
while (ll.longitude > 180) ll.longitude -= 180;
|
||||
while (ll.longitude < -180) ll.longitude += 180;
|
||||
|
||||
lon = -x / Bc;
|
||||
lat = R2D * (2 * std::atan(std::exp(y / Cc)) - 0.5 * M_PI);
|
||||
// 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<double> 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<double>(x_, y_);
|
||||
}
|
||||
|
||||
const LatLng TransformState::latLngForPixel(const vec2<double> 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 {
|
||||
|
||||
@@ -392,9 +392,8 @@ void Painter::renderBackground(util::ptr<StyleLayer> layer_desc) {
|
||||
patternShader->u_mix = properties.image.t;
|
||||
patternShader->u_opacity = properties.opacity;
|
||||
|
||||
double lon, lat;
|
||||
state.getLonLat(lon, lat);
|
||||
std::array<float, 2> center = state.locationCoordinate(lon, lat);
|
||||
LatLng latLng = state.getLatLng();
|
||||
vec2<double> center = state.pixelForLatLng(latLng);
|
||||
float scale = 1 / std::pow(2, zoomFraction);
|
||||
|
||||
std::array<float, 2> sizeA = imagePosA.size;
|
||||
@@ -404,8 +403,8 @@ void Painter::renderBackground(util::ptr<StyleLayer> 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<StyleLayer> 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,
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
#include <mbgl/style/style_layer_group.hpp>
|
||||
#include <mbgl/util/std.hpp>
|
||||
#include <mbgl/map/map.hpp>
|
||||
#include <mbgl/map/transform.hpp>
|
||||
|
||||
using namespace mbgl;
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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.
|
||||
|
||||
Reference in New Issue
Block a user