Question

I have an app, that uses MapKit.
I calculate current zoom after resizing a map.
By define (from MKGeometry.h file)

MKZoomScale provides a conversion factor between MKMapPoints and screen points.
When MKZoomScale = 1, 1 screen point = 1 MKMapPoint.  When MKZoomScale is
0.5, 1 screen point = 2 MKMapPoints.

So, I calculate it in this way:

- (void)mapView:(MKMapView *)mapView regionDidChangeAnimated:(BOOL)animated
{
    CGSize screenSize = mapView.bounds.size;
    MKMapRect mapRect = mapView.visibleMapRect;
    MKZoomScale zoomScale = screenSize.width * screenSize.height / (mapRect.size.width * mapRect.size.height);
}

The calculated zoomScale conforms to definition (I've checked).

I also draw an overlay on my mapView in method

-(void)drawMapRect:(MKMapRect)mapRect zoomScale:(MKZoomScale)zoomScale inContext:(CGContextRef)context

The problem is, that my calculated zoomScale is not equal to that one, that passed to this method by system. I expect them to be equal, because drawMapRect: calls just after resizing (actually, resizing causes this method to be called).

What is wrong here?

I also tried to use

currentZoomScale = mapView.bounds.size.width / mapView.visibleMapRect.size.width;

, suggested here, but this currentZoomScale is not equal to passed to drawMapRect: as well.

Was it helpful?

Solution

A coworker and I did discover some weirdness today that might explain this behavior: the MKZoomScale that's passed into the drawMapRect: method always reflects the current zoom level of the map tiles that are drawn in the MKMapView.

For example, if you manipulate the map to be at precisely zoom level 7, it draws map tiles from zoom level 7, and therefore, the zoom scale in the MKOverlay drawMapRect: is 1 / (2^(20-level)) = ~0.000122. Now, if you zoom in just a little further, but not so much that the map has to use the level 8 tiles, you would expect that the zoom scale would increase when drawMapRect: gets called again. However, the zoom scale stays the same.

One behavior that tipped off why this occurs is the following: put a breakpoint inside drawMapRect: and zoom just a little bit -- again, not enough to use a new set of tiles. You'll notice that drawMapRect: doesn't actually get called! In fact, there are only two ways to get it to be called:

  1. Pan the map over until a new set of tiles has to be drawn
  2. Zoom in or out until you reach the threshold where a new level of tiles has to be rendered

In retrospect, this makes perfect sense. Core Graphics is highly optimized and wants to do as little drawing as possible. When the user zooms in or out just a little bit, it most likely uses a cached image of the overlay tiles and just scales it up or down appropriately.

We can then reasonably assume that even though the MKMapView may have a zoom scale of any fractional decimal number between 0 and 1, the MKOverlay only gets drawn when the zoom scale reaches the threshold where it needs to be redrawn, i.e. at whole number zoom levels from 1-20, i.e. at zoom scale 1, 1/2, 1/4, 1/8, 1/16...

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top