Continuous zooming with MKMapView? - iphone

I am working with a MKMapView thats visible area (zoom level) is set with a slider.
I set the visible area with a MKCoordinateRegion and setRegion:animated:.
The problem is that I don't seem to be able to get extremely precise control over the visible area. It's as if the latitude\longitude deltas snap to the default ~21 zoom levels provided by Google.
What I really want is similar to the behavior of the Map.app when pinching to zoom. It scales the view until the threshold for a new zoom level is reached, and then it renders the new map level.
Is there a simple way to access\emulate this behavior? How does it work?
Code I'm using:
MKCoordinateRegion region;
MKCoordinateSpan span;
CLLocationCoordinate2D center = {45.475969,-73.64095};
region.center = center;
span.latitudeDelta = 0.01;
span.longitudeDelta = 0.01;
region.span = span;
[mapView setRegion:region animated:TRUE];
If I use a delta of 0.01 or 0.013, I get the exact same map.

The simple answer is that MapKit does not allow you to programatically set a zoom level other than the default 21 levels supplied by google. The region span you set will always "snap" to the closest one.

Are you using setRegion:animated: ?
http://developer.apple.com/library/ios/#documentation/MapKit/Reference/MKMapView_Class/MKMapView/MKMapView.html#//apple_ref/occ/instm/MKMapView/setRegion:animated:

You can set mapview's visibleMapRect to achieve continuous zooming.
Following code shows how to zoom in 1 level (it's a subclass of a MKMapView so self is an instance of MKMapView).
MKMapRect visibleRect = self.visibleMapRect;
MKMapSize size = visibleRect.size;
CGFloat aspectRatio = size.width / size.height;
CGPoint center = CGPointMake(visibleRect.origin.x + size.width/2, visibleRect.origin.y + size.height/2);
CGFloat zoomedHeight = size.height / 2; // By divide by 2, will zoom in exact 1 level
CGFloat zoomedWidth = zoomedHeight * aspectRatio;
visibleRect = MKMapRectMake(center.x - zoomedWidth/2, center.y - zoomedHeight/2, zoomedWidth, zoomedHeight);
self.visibleMapRect = visibleRect;

Related

Different map zoom on iPhone 4 and iPhone 5

I am using MapKit for my project and so far it has been very good. Here is a chunk of code I use for displaying and centering the map.
CLLocation *locSouthWest = [[CLLocation alloc] initWithLatitude:southWest.latitude longitude:southWest.longitude];
CLLocation *locNorthEast = [[CLLocation alloc] initWithLatitude:northEast.latitude longitude:northEast.longitude];
CLLocationDistance meters = [locSouthWest distanceFromLocation:locNorthEast];
MKCoordinateRegion region;
region.center.latitude = (southWest.latitude + northEast.latitude) / 2.0;
region.center.longitude = (southWest.longitude + northEast.longitude) / 2.0;
region.span.latitudeDelta = meters / 111319.5;
region.span.longitudeDelta = 0.0;
[self.mapView setRegion:region animated:YES];
The problem is, that it works differently on iPhone 4 and iPhone 5.
Here is iPhone 4 (same results for iOS5 and iOS6):
and here is iPhone 5 (using the same coordinates):
Anybody experiencing the same?
MapKit has fixed zoom levels. Setting the map's region ensures that the region will be visible in the map, but does not set the exact zoom. This has several benefits, the primary one being you can't create a map that scales latitude and longitude disproportionately (leading to a confusing and/or misleading map). The frames of the maps are different sizes, so your selected region can display at different zoom levels on each device.
As an experiment, try setting the frame of the map view to the same size on both devices. Then, if you absolutely need the maps to display at the same scale you could do some math to compute the appropriate region based on the frame of the map.
These are the lines of code that are causing the difference:
region.center.latitude = (southWest.latitude + northEast.latitude) / 2.0;
region.center.longitude = (southWest.longitude + northEast.longitude) / 2.0;
Which are bound to create a difference since the screen sizes vary.
What you can do here to center the map is to use mapView.centerCoordinate

MKCoordinateregion's center changes when span changed - iphone

I have a MKCoordinateregion called region. I want to change span of this region without changing it's center. I am using following method to change zoom.
-(void) changeZoom
{
NSLog(#"before zoom span, center are %f, %f,%f,%f", region.span.latitudeDelta, region.span.longitudeDelta, mymap.centerCoordinate.latitude, mymap.centerCoordinate.longitude);
region.span.latitudeDelta = current_factor ;
region.span.longitudeDelta = current_factor ;
region.center.latitude = mymap.centerCoordinate.latitude ;
region.center.longitude = mymap.centerCoordinate.longitude ;
[mymap setRegion:region animated:TRUE];
NSLog(#"after zoom span, center are %f, %f,%f,%f", region.span.latitudeDelta, region.span.longitudeDelta, mymap.centerCoordinate.latitude, mymap.centerCoordinate.longitude);
}
Log shows :
before zoom span, center are 0.021950, 0.021950,19.068080,72.838111
after zoom span, center are 0.043901, 0.043901,19.068040,72.838154
This method set's span accurately as desired.But I dont want any change in the center's coordinate, Because when I zoom out and then zoom back I end up at a location different from starting location.
Is there any method so that zoom in and out are possible without any change in center?
Thanks for any help in advance.
Your before and after center coordinates are almost exactly the same, so it shouldn't be a problem. If you really want the exact same center coordinates, call setCenterCoordinate:animated: right after you use setRegion:animated: and pass some saved center coordinates.
CLLocationCoordinate2D originalCenter = mymap.centerCoordinate;
// ... adjust region
[mymap setCenterCoordinate:originalCenter animated:NO];
Also, you should use YES instead of TRUE for booleans:
[mymap setRegion:region animated:YES];
You need to adjust the region of the map to make sure the aspect ratio fits the frame size of the map view.
Once the region with the desired center is calculated, adjust the region as,
MKCoordinateRegion adjustedRegion = [mymap regionThatFits:region];
[mymap setRegion:adjustedRegion animated:TRUE];
See docs.

How to find the radius of visible MKMapView visible screen area?

I want to know the radius of visible area in iphone screen, as I will zoomout and zoom in the visible area will change, so I want to know the radius of that particular area, how can I do it?
Its not radius what is required.
You need to use the region parameter from mapView.
Check out apple docs, it is pretty much clear from those.
Go thru this tutorial. It will help you a lot
icode blog mapkit demo
specifically you need to set something like this..
MKCoordinateSpan span = [self coordinateSpanWithMapView:self centerCoordinate:centerCoordinate andZoomLevel:zoomLevel];
MKCoordinateRegion region = MKCoordinateRegionMake(centerCoordinate, span);
[self setRegion:region animated:animated];
where span can be calculated as
- (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;
}
Cheers :)
I might be misunderstanding the question, but isn't it as simple as:
- (void)mapView:(MKMapView *)mapView regionDidChangeAnimated:(BOOL)animated {
CGFloat latD = mapView.region.span.latitudeDelta;
CGFloat lngD = mapView.region.span.longitudeDelta;
NSLog(#"This is the latitude delta of the visible map: %f", latD);
NSLog(#"This is the longitude delta of the visible map: %f", lngD);
}

Slight zoom on MKCoordinateRegion?

I am zooming an MKMapView to fit the bounding region of a collection of pins, however when the pins are displayed I have noticed that the zoom could ideally do with being a little tighter. My proposed solution to this was to make the region deltas slightly smaller:
// SMALL ZOOM
region.span.latitudeDelta = (upper.latitude - lower.latitude) * 0.9;
region.span.longitudeDelta = (upper.longitude - lower.longitude) * 0.9;
However I have noticed that fine adjustments don't seem to translate to a small zoom increase, is there some form of snapping on the zoom? Really small values work, as do really big ones, but just adjusting the region size by a few percent does not seem to work with the view nearly always jumping/zooming in to far and clipping my pins.
EDIT:
Quick tests showing the results of different scaling factors on the region:
// SCALE FACTOR
// V
region.span.latitudeDelta = (upper.latitude - lower.latitude) * 0.9;
region.span.longitudeDelta = (upper.longitude - lower.longitude) * 0.9;
Here are the results:
x0.5 region too small, some annotations off screen
x0.6 Same as using 1.0
x0.7 Same as using 1.0
x0.8 Same as using 1.0
x0.9 Same as using 1.0
x1.0 Original fit
x1.1 region too big, annotations too small on screen
My point is that very small adjustments (e.g. 0.6 to 0.9) don't seem to make any difference.
Smaller adjustments will never make the mapview zoom level change. When you pass a region, the mapview decides on what zoom level to use for the best fit. It will never use an "in-between" level. The reason is that the "in-between" zoom levels look fuzzy. You give it the region you want to show and it makes a zoom level that includes that whole region. Giving your desired region to regionThatFits: should return the level that it uses.
If you're zooming the map with a pinch, you can get to a level between two zoom levels, but if you double-tap (to zoom in) or do a 2-finger tap (to zoom out) you will only see the "standard" zoom levels.
I'm talking about zoom levels here, but really they don't exist in iOS in the same way they exist in Google Maps. Only regions exist as far as setting the map to a certain level.
With your problem of getting the best fit for pins, I found that something changed in iOS 4, and the code I'd used to fit pins suddenly gave too much space. I divided the deltas by 3 and it worked again. You might want to wrap this in a conditional to target only iOS 4.
region.span.longitudeDelta = (maxCoord.longitude - minCoord.longitude) / 3.0;
region.span.latitudeDelta = (maxCoord.latitude - minCoord.latitude) / 3.0;
Looking at your code, you use * 0.9 to get the exact same thing.
One of the strange things I found was that the value returned by regionThatFits: wasn't always the region that the mapview ended up setting. It might be a bug, but it's been there since iOS 4.0. You can test this yourself by logging the MKCoordinateRegion from regionThatFits: and comparing it to the mapview's region after zooming. I seem to remember it coming up on the Apple Developer Forums.
I have found this method to be extremely useful. All you need to do is call it and pass your MKMapView as the argument, and it will determine the best zoom level to fit all of your annotations. The "tightness" can be adjusted by modifying the constant multiplier on the commented lines (currently 1.1).
What's the best way to zoom out and fit all annotations in MapKit
- (void)zoomToFitMapAnnotations:(MKMapView *)mapView
{
if ([mapView.annotations count] == 0)
return;
CLLocationCoordinate2D topLeftCoord;
topLeftCoord.latitude = -90;
topLeftCoord.longitude = 180;
CLLocationCoordinate2D bottomRightCoord;
bottomRightCoord.latitude = 90;
bottomRightCoord.longitude = -180;
for (MapAnnotation *annotation in mapView.annotations)
{
topLeftCoord.longitude = fmin(topLeftCoord.longitude, annotation.coordinate.longitude);
topLeftCoord.latitude = fmax(topLeftCoord.latitude, annotation.coordinate.latitude);
bottomRightCoord.longitude = fmax(bottomRightCoord.longitude, annotation.coordinate.longitude);
bottomRightCoord.latitude = fmin(bottomRightCoord.latitude, annotation.coordinate.latitude);
}
MKCoordinateRegion region;
region.center.latitude = topLeftCoord.latitude - (topLeftCoord.latitude - bottomRightCoord.latitude) * 0.5;
region.center.longitude = topLeftCoord.longitude + (bottomRightCoord.longitude - topLeftCoord.longitude) * 0.5;
region.span.latitudeDelta = fabs(topLeftCoord.latitude - bottomRightCoord.latitude) * 1.1; // Add a little extra space on the sides
region.span.longitudeDelta = fabs(bottomRightCoord.longitude - topLeftCoord.longitude) * 1.1; // Add a little extra space on the sides
region = [mapView regionThatFits:region];
[mapView setRegion:region animated:YES];
}

How to get left-top and right-buttom latitude and longitude of map in MapKit For iPad?

i'm looking for answer about getting latitude and longitude from uimapkit. there is same problem that has been answered, but it didn't give right coordinate. http://yit.me/3ddp73
it's code from thread i'm linking above.
CLLocationCoordinate2D topLeft, bottomRight;
topLeft = [mapView convertPoint:CGPointMake(0,0) toCoordinateFromView:mapView];
CGPoint pointBottomRight = CGPointMake(mapView.frame.size.width, mapView.frame.size.height);
bottomRight = [mapView convertPoint:pointBottomRight toCoordinateFromView:mapView];
NSLog(#"topleft = %f", topLeft);
NSLog(#"bottom right = %f", bottomRight);
any idea to fix this issue ?
thanks
The map view has a region property.
MKCoordinateRegion region = mapView.region;
the region contains the center and the 2D span, so
CLLocationCoordinate2D topLeft = CLLocationCoordinate2DMake(
region.center.latitude - region.span.latitudeDelta/2,
region.center.longitude - region.span.longitudeDelta/2,
);
etc.
The original solution is close. The view's coordinate system has the origin at the bottom left. Therefore CGPoint(0,0) is actually bottom left, not top. And the other coordinate will be your top right.