将 MapKit 地图的缩放/边界与 RouteMe 地图的缩放/边界相匹配
编辑:我相信我的问题是此代码适用于整数缩放级别,但我希望它适用于浮动缩放级别。
我有一个 iOS 应用程序,用户可以在其中切换 RouteMe-基于地图和基于 MapKit 的地图。
当他们切换源时,我希望能够在一个源中显示与另一个源中完全相同的区域。但是,我不知道如何使它们匹配,因为 RouteMe 和 MapKit 使用不同的数据结构来描述地图边界。
这里有一些代码可以让它有点接近,但并不准确。此代码来自: http://troybrant .net/blog/2010/01/set-the-zoom-level-of-an-mkmapview/
我不确定是否应该修复此代码,或者可能我忽略了一个更简单的解决方案。该代码从列出的最后一个方法开始执行:
#define MERCATOR_OFFSET 268435456
#define MERCATOR_RADIUS 85445659.44705395
#pragma mark -
#pragma mark Map conversion methods
- (double)longitudeToPixelSpaceX:(double)longitude {
return round(MERCATOR_OFFSET + MERCATOR_RADIUS * longitude * M_PI / 180.0);
}
- (double)latitudeToPixelSpaceY:(double)latitude {
return round(MERCATOR_OFFSET - MERCATOR_RADIUS * logf((1 + sinf(latitude * M_PI / 180.0)) / (1 - sinf(latitude * M_PI / 180.0))) / 2.0);
}
- (double)pixelSpaceXToLongitude:(double)pixelX {
return ((round(pixelX) - MERCATOR_OFFSET) / MERCATOR_RADIUS) * 180.0 / M_PI;
}
- (double)pixelSpaceYToLatitude:(double)pixelY {
return (M_PI / 2.0 - 2.0 * atan(exp((round(pixelY) - MERCATOR_OFFSET) / MERCATOR_RADIUS))) * 180.0 / M_PI;
}
- (MKCoordinateSpan)coordinateSpanWithMapView:(MKMapView *)mapView
centerCoordinate:(CLLocationCoordinate2D)centerCoordinate
andZoomLevel:(NSInteger)zoomLevel {
// convert center coordiate to pixel space
double centerPixelX = [self longitudeToPixelSpaceX:centerCoordinate.longitude];
double centerPixelY = [self latitudeToPixelSpaceY:centerCoordinate.latitude];
// determine the scale value from the zoom level
NSInteger zoomExponent = 20 - zoomLevel;
double zoomScale = pow(2, zoomExponent);
// scale the map’s size in pixel space
CGSize mapSizeInPixels = mapView.bounds.size;
double scaledMapWidth = mapSizeInPixels.width * zoomScale;
double scaledMapHeight = mapSizeInPixels.height * zoomScale;
// figure out the position of the top-left pixel
double topLeftPixelX = centerPixelX - (scaledMapWidth / 2);
double topLeftPixelY = centerPixelY - (scaledMapHeight / 2);
// find delta between left and right longitudes
CLLocationDegrees minLng = [self pixelSpaceXToLongitude:topLeftPixelX];
CLLocationDegrees maxLng = [self pixelSpaceXToLongitude:topLeftPixelX + scaledMapWidth];
CLLocationDegrees longitudeDelta = maxLng - minLng;
// find delta between top and bottom latitudes
CLLocationDegrees minLat = [self pixelSpaceYToLatitude:topLeftPixelY];
CLLocationDegrees maxLat = [self pixelSpaceYToLatitude:topLeftPixelY + scaledMapHeight];
CLLocationDegrees latitudeDelta = -1 * (maxLat - minLat);
// create and return the lat/lng span
MKCoordinateSpan span = MKCoordinateSpanMake(latitudeDelta, longitudeDelta);
return span;
}
- (void)setCenterCoordinate:(CLLocationCoordinate2D)centerCoordinate
zoomLevel:(NSUInteger)zoomLevel
animated:(BOOL)animated {
// use the zoom level to compute the region
MKCoordinateSpan span = [self coordinateSpanWithMapView:self
centerCoordinate:centerCoordinate
andZoomLevel:zoomLevel];
MKCoordinateRegion region = MKCoordinateRegionMake(centerCoordinate, span);
// set the region like normal
[self setRegion:region animated:animated];
}
EDIT: I believe my issue is that this code works for integer zoom levels, but I would like it to work for float zoom levels.
I have an iOS app in which the user can switch between a RouteMe-based map and a MapKit-based map.
When they switch sources, I would like to be able to show the exact same area in one as in the other. However, I can't figure out how to make them match because RouteMe and MapKit use different data structures to describe the map bounds.
Here is some code that gets it to be somewhat close, but it's not exact. This code comes from: http://troybrant.net/blog/2010/01/set-the-zoom-level-of-an-mkmapview/
I'm not sure whether this code should be fixed, or possibly I am overlooking a much easier solution. The code executes starting with the last method listed:
#define MERCATOR_OFFSET 268435456
#define MERCATOR_RADIUS 85445659.44705395
#pragma mark -
#pragma mark Map conversion methods
- (double)longitudeToPixelSpaceX:(double)longitude {
return round(MERCATOR_OFFSET + MERCATOR_RADIUS * longitude * M_PI / 180.0);
}
- (double)latitudeToPixelSpaceY:(double)latitude {
return round(MERCATOR_OFFSET - MERCATOR_RADIUS * logf((1 + sinf(latitude * M_PI / 180.0)) / (1 - sinf(latitude * M_PI / 180.0))) / 2.0);
}
- (double)pixelSpaceXToLongitude:(double)pixelX {
return ((round(pixelX) - MERCATOR_OFFSET) / MERCATOR_RADIUS) * 180.0 / M_PI;
}
- (double)pixelSpaceYToLatitude:(double)pixelY {
return (M_PI / 2.0 - 2.0 * atan(exp((round(pixelY) - MERCATOR_OFFSET) / MERCATOR_RADIUS))) * 180.0 / M_PI;
}
- (MKCoordinateSpan)coordinateSpanWithMapView:(MKMapView *)mapView
centerCoordinate:(CLLocationCoordinate2D)centerCoordinate
andZoomLevel:(NSInteger)zoomLevel {
// convert center coordiate to pixel space
double centerPixelX = [self longitudeToPixelSpaceX:centerCoordinate.longitude];
double centerPixelY = [self latitudeToPixelSpaceY:centerCoordinate.latitude];
// determine the scale value from the zoom level
NSInteger zoomExponent = 20 - zoomLevel;
double zoomScale = pow(2, zoomExponent);
// scale the map’s size in pixel space
CGSize mapSizeInPixels = mapView.bounds.size;
double scaledMapWidth = mapSizeInPixels.width * zoomScale;
double scaledMapHeight = mapSizeInPixels.height * zoomScale;
// figure out the position of the top-left pixel
double topLeftPixelX = centerPixelX - (scaledMapWidth / 2);
double topLeftPixelY = centerPixelY - (scaledMapHeight / 2);
// find delta between left and right longitudes
CLLocationDegrees minLng = [self pixelSpaceXToLongitude:topLeftPixelX];
CLLocationDegrees maxLng = [self pixelSpaceXToLongitude:topLeftPixelX + scaledMapWidth];
CLLocationDegrees longitudeDelta = maxLng - minLng;
// find delta between top and bottom latitudes
CLLocationDegrees minLat = [self pixelSpaceYToLatitude:topLeftPixelY];
CLLocationDegrees maxLat = [self pixelSpaceYToLatitude:topLeftPixelY + scaledMapHeight];
CLLocationDegrees latitudeDelta = -1 * (maxLat - minLat);
// create and return the lat/lng span
MKCoordinateSpan span = MKCoordinateSpanMake(latitudeDelta, longitudeDelta);
return span;
}
- (void)setCenterCoordinate:(CLLocationCoordinate2D)centerCoordinate
zoomLevel:(NSUInteger)zoomLevel
animated:(BOOL)animated {
// use the zoom level to compute the region
MKCoordinateSpan span = [self coordinateSpanWithMapView:self
centerCoordinate:centerCoordinate
andZoomLevel:zoomLevel];
MKCoordinateRegion region = MKCoordinateRegionMake(centerCoordinate, span);
// set the region like normal
[self setRegion:region animated:animated];
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
遗憾的是,这是 Google Maps API 的限制,仅设置地图缩放级别时接受整数值:当您设置 MKMapView 的显示区域时,Apple 的 MapKit 代码正在调用底层 Google Maps API,结果 - 无论您使用哪种 MapKit 方法来设置区域 -是缩小到最接近的整数缩放级别的地图。
Troy Brant 的代码带你绕了一圈,并在 MapKit API 之上放置了一个层,允许你直接设置缩放级别……但最终你无法精确控制 MKMapView 显示的区域,除非你想要的缩放级别map 恰好是一个整数。
Stack Overflow 上出现了此问题的几种变体(例如 MKMapView setRegion "snaps"预定义的缩放级别? 和 MKMapView 显示错误保存的区域),但到目前为止还没有人想出一种制作非整数缩放级别地图的编程方式,我怀疑谷歌和苹果需要合作才能实现这一目标。
Unfortunately this is a limitation of the Google Maps API, which only accepts integer values when setting the map's zoom level: Apple's MapKit code is calling the underlying Google Maps APIs when you set a MKMapView's displayed area, and the result – no matter which MapKit method you use to set the area – is a map that's zoomed out to the nearest integer zoom level.
Troy Brant's code takes you full circle, and puts a layer above the MapKit APIs that allows you to set the zoom level directly… but ultimately you don't have precise control over the area displayed by an MKMapView, unless the zoom level of your desired map happens to be an integer.
Several variations on this question have appeared on Stack Overflow (e.g., MKMapView setRegion "snaps" to predefined zoom levels? and MKMapView show incorrectly saved region), but so far no one has come up with a programmatic way to make a map with a non-integer zoom level, and I suspect it'd take cooperation between Google and Apple to ever make it happen.