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);
setCenter
MapKit
on the iPhone does not include a way to set the zoom level. Instead, the zoom level is set implicitly by defining the MKCoordinateRegion
MKMapView
explicitly. 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
MKMapView
// MKMapView+ZoomLevel.h
#import
@interface
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
#pragma mark -
#pragma mark Map conversion methodsdouble)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 methodsMKCoordinateSpan)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 methodsvoid)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.
how it works, just that it does
Test the Code
MKMapView
// 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 the left 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
MKMapView+ZoomLevel
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”
1. RAVI KB
November 22, 2013 at 6:36 am
|
Permalink
Great job
But from this how do I will get current zoom level of map view???2.
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.
// John3. 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).4. Kyle R
March 13, 2014 at 4:39 pm
|
Permalink
I too want to get the zoom level of a map.5. 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?6. 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 advance7.
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/EBCExtensions8. 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
-
プロムドレスのアクセサリーの多くには、これまでに中立的なヒョウ?プリントを混合することを見つけるのは喜ぶかもしれません。それを変形できるので決してかつらを折ってください。金属閉鎖なしでポニーテールホルダーを使用してここでは、あなたは男性の髪のためのいくつかのヘアケアのヒントを与えます。暗い色が全体的な体はあなたの顔のモデルにするのを助けるかもしれない内に支払う思考の素晴らしい方法です。この製品の高度な技術とその顕著な特徴の一つは、小さな板を使用しています。父のカレン?メドレー株に、マイケルコースのハンドバッグ
ヘアアクセ http://ljvbw.ditib.de/cli/PYY/dhCb
服务器托管,北京服务器托管,服务器租用 http://www.fwqtg.net
机房租用,北京机房租用,IDC机房托管, http://www.fwqtg.net
创建阻塞的服务器 当 ServerSocketChannel 与 SockelChannel 采用默认的阻塞模式时,为了同时处理多个客户的连接,必须使用多线程 public class EchoServer { private int port = 8000;…