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.
原文地址:http://troybrant.net/blog/2010/01/set-the-zoom-level-of-an-mkmapview/