5

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.

JLamkin
  • 721
  • 6
  • 17
fuzzygoat
  • 26,573
  • 48
  • 165
  • 294
  • Is your objective automatically zooming and panning to fit your annotations or doing this manually? – Evan Mulawski Jan 18 '11 at 15:56
  • I am placing the annotations on the map and then using MKCoordinateRegion region = MKCoordinateRegionMake(locationCenter, locationSpan); to fit then to the view. What I was truing to do was adjust the locationSpan slightly to get a slightly (i.e. 5%) tighter zoom. – fuzzygoat Jan 18 '11 at 16:16

2 Answers2

6

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.

nevan king
  • 112,709
  • 45
  • 203
  • 241
  • To expand on nevan's answer, the map tiles are pre-rendered at a series of different resolutions which necessarily have discrete steps and as a practical matter often have pretty large steps (storage is cheap, but this is the *entire planet* we're talking about :) – Frank Schmitt Jan 25 '11 at 23:37
  • It's true, I should have said that. I think that the steps are all just a halving of width (to make the math easier). So zooming in a 512x512 tile uses 4 512x512 tiles to show the same area. This post has one of the best explanations I've ever read: http://troybrant.net/blog/2010/01/mkmapview-and-zoom-levels-a-visual-guide/ – nevan king Jan 25 '11 at 23:43
  • Much appreciated, that makes perfect sense. Half my reason for asking was just to find out "why". Dividing the deltas by 3.0 zooms very far in for me, I will have a play and see what I can come up with, but at least now I am on the right track. – fuzzygoat Jan 26 '11 at 17:30
  • No problem. Strange that its' taking different numbers to get a good fit. I looked through my code to see if anything was wrong and couldn't find what caused this problem. – nevan king Jan 27 '11 at 21:31
4

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];
}
Community
  • 1
  • 1
Evan Mulawski
  • 54,662
  • 15
  • 117
  • 144
  • Hi Evan, thats pretty much what I have been doing in my own routine. I did miss "regionThatFits" but I have just added that and it seems to make no difference. My problem is that using small multipliers (to tighten the region) like x0.6, x0.7, x0.8, x0.9 which should all shrink the region about its center all produce the same result. x1.0 is too far out, x0.9 is too far in there does not seem to be a happy medium. – fuzzygoat Jan 18 '11 at 21:55
  • Have you tried to adjust the 1.1x multiplier on the commented lines? This should produce a small zooming effect. – Evan Mulawski Jan 19 '11 at 00:09
  • The 1.1x multiplier is what I have been changing. 1.1 is doing a pullback, making the region bigger and giving more space at the sides. What I want is a pushin/zoom, making the region smaller so the region fills the screen tighter. – fuzzygoat Jan 19 '11 at 12:50
  • Then you would change the multiplier to 0.9, and adjust it to your liking. – Evan Mulawski Jan 19 '11 at 15:38
  • That just it though you can,t adjust to your liking! setting 0.9 looks the same as setting 1.0, even setting 0.6 looks the same as 1.0. Its only when you get to 0.5 that the view moves in closer and at that point its too close. – fuzzygoat Jan 19 '11 at 15:42
  • Is this behavior with the code I posted or the code you have been using? – Evan Mulawski Jan 19 '11 at 15:44
  • Both, @nevan explains what I am seeing well. – fuzzygoat Jan 26 '11 at 19:00