title: Calculating tiles to display in a MapRect when "over-zoomed" beyond the overlay tile set
body: <p>I am working on an app that uses MKOverlay views to layer my own custom maps on top of the Google base map.  I have been using Apple's excellent TileMap sample code (from WWDC 2010) as a guide.</p>
  
  <p>My problem - when "overzoomed" to a level of detail deeper than my generated tile set, the code displays nothing because there are no tiles available at the calculated Z level.</p>
  
  <p>The behavior I want - when "overzoomed" the app should just keep magnifying the deepest level of tiles.  It is a good user experience for the overlay to become blurrier - it is a very bad experience to have the overlay vanish.</p>
  
  <p>Here is the code which returns the tiles to draw - I need to figure out how to modify this to cap the Z-depth without breaking the scaling of the frame being calculated for the overlay tile.  Any thoughts???</p>
  
  <hr>
  
  <pre><code>- (NSArray *)tilesInMapRect:(MKMapRect)rect zoomScale:(MKZoomScale)scale
  {
      NSInteger z = zoomScaleToZoomLevel(scale);
  
      // PROBLEM: I need to find a way to cap z at my maximum tile directory depth.
  
      // Number of tiles wide or high (but not wide * high)
      NSInteger tilesAtZ = pow(2, z);
  
      NSInteger minX = floor((MKMapRectGetMinX(rect) * scale) / TILE_SIZE);
      NSInteger maxX = floor((MKMapRectGetMaxX(rect) * scale) / TILE_SIZE);
      NSInteger minY = floor((MKMapRectGetMinY(rect) * scale) / TILE_SIZE);
      NSInteger maxY = floor((MKMapRectGetMaxY(rect) * scale) / TILE_SIZE);
  
      NSMutableArray *tiles = nil;
  
      for (NSInteger x = minX; x &lt;= maxX; x++) {
          for (NSInteger y = minY; y &lt;= maxY; y++) {
              // As in initWithTilePath, need to flip y index
              // to match the gdal2tiles.py convention.
              NSInteger flippedY = abs(y + 1 - tilesAtZ);
  
              NSString *tileKey = [[NSString alloc] 
                                    initWithFormat:@"%d/%d/%d", z, x, flippedY];
              if ([tilePaths containsObject:tileKey]) {
                  if (!tiles) {
                      tiles = [NSMutableArray array];
                  }
  
                  MKMapRect frame = MKMapRectMake((double)(x * TILE_SIZE) / scale,
                                                  (double)(y * TILE_SIZE) / scale,
                                                  TILE_SIZE / scale,
                                                  TILE_SIZE / scale);
  
                  NSString *path = [[NSString alloc] initWithFormat:@"%@/%@.png",
                        tileBase, tileKey];
                  ImageTile *tile = [[ImageTile alloc] initWithFrame:frame path:path];
                  [path release];
                  [tiles addObject:tile];
                  [tile release];
              }
              [tileKey release];
          }
      }
  
      return tiles;
  }
  </code></pre>
  
  <p>FYI, here is the zoomScaleToZoomLevel helper function that someone asked about:</p>
  
  <pre><code>// Convert an MKZoomScale to a zoom level where level 0 contains 4 256px square tiles,
  // which is the convention used by gdal2tiles.py.
  static NSInteger zoomScaleToZoomLevel(MKZoomScale scale) {
      double numTilesAt1_0 = MKMapSizeWorld.width / TILE_SIZE;
      NSInteger zoomLevelAt1_0 = log2(numTilesAt1_0);  // add 1 because the convention skips a virtual level with 1 tile.
      NSInteger zoomLevel = MAX(0, zoomLevelAt1_0 + floor(log2f(scale) + 0.5));
      return zoomLevel;
  }
  </code></pre>