您的位置:首页 > 移动开发 > IOS开发

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 
setCenter
 JavaScript 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, 
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
 of
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 
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

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 
MKMapView
 that
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 
MKMapView
 instance,
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 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

By using the 
MKMapView+ZoomLevel
 category, 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 mapkitmaps


60 Responses to “Set the Zoom Level of an MKMapView”

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???



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
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: