iOS 缩放等级 Set the Zoom Level of an MKMapView
2016-01-08 12:01
453 查看
Set
the Zoom Level of an MKMapView
http://troybrant.net/blog/2010/01/set-the-zoom-level-of-an-mkmapview/If you have ever built a web application using the Google Maps API, you are likely intimately familiar with this line of code:
map.setCenter(new google.maps.LatLng(37.4419,
-122.1419), 13);
The
setCenterJavaScript method takes in the center coordinate and
a zoom level. The zoom level, as you might expect, determines how far the map should zoom in. The zoom level ranges from 0 (all the way zoomed out) to some upper value (all the way zoomed in). The max zoom level for a particular area depends on the location
(for instance, you can’t zoom in too far on North Korea) and the map type (default, satellite, hybrid, terrain, etc). Typically, the max zoom level for an area is somewhere between 15 – 21.
Unfortunately,
MapKiton the iPhone does not include a way to set the
zoom level. Instead, the zoom level is set implicitly by defining the
MKCoordinateRegionof
the map’s viewport. When initializing the region, you specify the amount of distance the map displays in the horizontal and vertical directions. The zoom level is set implicitly based on these distance values.
Instead of dealing with this region business, I wrote a category that adds support for setting the zoom level of an
MKMapViewexplicitly.
In this post, I’ll give you code you can drop into your own projects and start using immediately. My
next post will detail exactly how it works.
The Code
Instead of force-feeding you everything I learned while working on this mini-project, I’ll give you the goods up front. The code below defines a category on MKMapViewthat
gives you the ability to set the zoom level for your map:
// MKMapView+ZoomLevel.h
#import <MapKit/MapKit.h>
@interface MKMapView (ZoomLevel)
- (void)setCenterCoordinate:(CLLocationCoordinate2D)centerCoordinate
zoomLevel:(NSUInteger)zoomLevel
animated:(BOOL)animated;
@end
// MKMapView+ZoomLevel.m
#import "MKMapView+ZoomLevel.h"
#define MERCATOR_OFFSET 268435456
#define MERCATOR_RADIUS 85445659.44705395
@implementation MKMapView (ZoomLevel)
#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;
}
#pragma mark -
#pragma mark Helper methods
- (MKCoordinateSpan)coordinateSpanWithMapView:(MKMapView *)mapView
centerCoordinate:(CLLocationCoordinate2D)centerCoordinate
andZoomLevel:(NSUInteger)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;
}
#pragma mark -
#pragma mark Public methods
- (void)setCenterCoordinate:(CLLocationCoordinate2D)centerCoordinate
zoomLevel:(NSUInteger)zoomLevel
animated:(BOOL)animated
{
// clamp large numbers to 28
zoomLevel = MIN(zoomLevel, 28);
// 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];
}
@end
If you’re wondering why this works, check out my
next post where I describe in gruesome detail the math behind the code.
On the other hand, if you don’t really care how it works, just that it does work,
copy and paste away, my friend.
Test the Code
To test the category, assuming you have a view controller with an MKMapViewinstance,
you can use the following code:
// ZoomLevelTestViewController.m
#import "MKMapView+ZoomLevel.h"
#define GEORGIA_TECH_LATITUDE 33.777328
#define GEORGIA_TECH_LONGITUDE -84.397348
#define ZOOM_LEVEL 14
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
CLLocationCoordinate2D centerCoord =
{ GEORGIA_TECH_LATITUDE, GEORGIA_TECH_LONGITUDE };
[mapView setCenterCoordinate:centerCoord zoomLevel:ZOOM_LEVEL animated:NO];
}
And, viola! Your map should zoom in to where you can see the Georgia Tech campus.
Results
To verify that the zoom level is set correctly, I wrote a simple web-based Google Maps application to make sure the web and native zoom levels matched. Both apps were centered at {33.777328, -84.397348} (Georgia Tech). In the images below, the iPhone on theleft is running the native app and the iPhone on the right is running the web app:
As you can see, they match.
That’s a Wrap
By using the MKMapView+ZoomLevelcategory, you won’t have to bother
setting the region at all. If you are like me and have no intuition for how to set the map’s region, then hopefully this code will give you a bit more control in setting your map’s zoom level.
Next
time, I’ll go over exactly why the code above works. But, for now, enjoy the freedom to set zoom levels!
Posted
in iPhone
DevelopmentTagged mapkit, maps
60 Responses to “Set the Zoom Level of an MKMapView”
RAVI KBNovember 22, 2013 at 6:36 am
| Permalink
Great job
But from this how do I will get current zoom level of map view???
John
Gazzini
December 19, 2013 at 4:01 am
| Permalink
Thanks; this saved me several minutes of my life. Like… at least 4 minutes. It looks like you posted this a few years ago, but wherever you are, I hope you’re wildly successful.
// John
Nordin B
March 13, 2014 at 1:49 pm
| Permalink
Thanks a lot for this post my friend,
I just started developing for iOS and I was surprised why there was no way set a zoomlevel. When a user zooms, it’s because the user wants to know the exact spot of the location and not the area.
By the way, you’re post was very easy to follow (you should be a teacher some day).
Kyle R
March 13, 2014 at 4:39 pm
| Permalink
I too want to get the zoom level of a map.
kups
May 10, 2014 at 6:01 am
| Permalink
Thanks for the great tutorial. But When i used this class with 100′s of map points in same lat longs, the map points are over lapping each other and unable to click particular map point Please check this link i want to display using spiderfy leafleft animation..
SO by using spiderfy animation we can achive this http://www.yourmapper.com/demo/spiderfy.htm .
any idea?
Thai
May 16, 2015 at 4:50 am
| Permalink
Your post was very helpful that can help me convert a zoom from Google map to a span on Apple map. Thanks a lot.
Do you know how to get a zoom level if I have a span. I think I need to do the contrary but I not very good at math and those calculation are really complicated
Thanks in advance
Mike
June 9, 2015 at 3:28 am
| Permalink
If anyone needs it I translated this to a Swit extension for use in a project I’m working on.
I’ll be adding the inverse operation soon (need it for same project). Just working out some kinks in it now.
Thanks Troy for this and thanks to Dave in the comments for the inverse function.
https://github.com/goto10/EBCExtensions
Ahmed ESS
June 14, 2015 at 12:07 pm
| Permalink
For those who need to get zoom level, use that code :
@interface MKMapView (ZoomLevelGetter)
- (int)getZoomLevel;
@end
@implementation MKMapView (ZoomLevelGetter)
- (int)getZoomLevel
{
CLLocationDegrees longitudeDelta = self.region.span.longitudeDelta;
CGFloat mapWidthInPixels = self.bounds.size.width*2;//2 is for retina display
double zoomScale = longitudeDelta * MERCATOR_RADIUS * M_PI / (180.0 * mapWidthInPixels);
double zoomer = MAX_GOOGLE_LEVELS – log2( zoomScale );
if ( zoomer < 0 ) zoomer = 0;
zoomer = round(zoomer);
return (int)zoomer;
}
@end
Mike Lanza
September 29, 2015 at 9:03 pm
| Permalink
Thanks to Mike for the Swift Extension! I do need the inverse function, so I hope you get around to posting it.
ヘアアクセ
November 27, 2015 at 6:57 am
| Permalink
プロムドレスのアクセサリーの多くには、これまでに中立的なヒョウ?プリントを混合することを見つけるのは喜ぶかもしれません。それを変形できるので決してかつらを折ってください。金属閉鎖なしでポニーテールホルダーを使用してここでは、あなたは男性の髪のためのいくつかのヘアケアのヒントを与えます。暗い色が全体的な体はあなたの顔のモデルにするのを助けるかもしれない内に支払う思考の素晴らしい方法です。この製品の高度な技術とその顕著な特徴の一つは、小さな板を使用しています。父のカレン?メドレー株に、マイケルコースのハンドバッグ
ヘアアクセ http://ljvbw.ditib.de/cli/PYY/dhCb
相关文章推荐
- 简单的时间段选择控件
- iOS绘图教程
- ios开发的建议学习
- 学习ios一些建议
- iOS开发知名个人博客一览表
- iOS欢迎界面Launch Screen动态加载广告
- IOS 后台运行
- iOS常用插件
- IOS中对图片进行重绘处理的方法总结
- iOS中的delegate的用法和规范
- 去除ios系统a标签点击时的灰色背景
- iOS 中检测硬件和传感器
- iOS 项目集成支付宝SDK的经典错误
- iOS 各类传感器
- iOS多线程编程技术NSThread、Cocoa NSOperation、GCD三者使用详解
- iOS开发 NSDictionary初始化的坑
- 修改iOS导航栏字体大小颜色self.navigationItem.title的颜色
- iOS 点击图片全屏 再次点击取消
- iOS - NSError用法规范
- iOS开发 多线程和GCD