Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Getting MKMapRect from MKTileOverlayPath

Tags:

ios

mapkit

I searched a lot for an answer & I can't imagine that such method is not existing!! I've an MKTileOverlayPath (x , y & z) and also was able to get lon/lat of the center of this rectangle. Now I need to get span of each rect, is that possible?

like image 695
Elsammak Avatar asked Dec 14 '22 18:12

Elsammak


1 Answers

Yes, though it's not entirely obvious with only MapKit.

Tiles & overlay paths are defined in a grid system where the number of points on a side of the (square) map equals 2^z * 256 since the default screen tile size is 256px square. z0 is one 256px square tile, z1 is four tiles for a total world of 512px square, etc. Tile coordinates in this system are linear (all squares in a square map).

MKMapPoint is derived from z20 such that MKMapSizeWorld is 268,435,456 map points on a side. If you do the math, 2^20 * 256 = 268,435,456. Map points are also linear (pixels/points in a square map).

Latitude and longitude of course is not linear since this is a projected representation which is why you have functions like MKMetersPerMapPointAtLatitude().

If you have the following MKTileOverlayPath:

  • z = 10
  • x = 12
  • y = 8

You know that the world point size is 2^10 * 256 = 262,144 and that the world is 2^10 = 1,024 tiles on a side.

The tile's left edge is 256 * 12 = 3,072 points in and the top edge is 256 * 8 = 2,048 points down. These are in relation to z10, which is 268,435,456 / 262,144 = 1,024 times smaller scale than z20.

This is an MKMapPoint of { x: (3,072 * 1,024 = 3,145,728), y: (2,048 * 1,024 = 2,097,152) }.

The bottom right is similarly { x: 3,407,872, y: 2,359,296 } (add the tile's size of 256 * 1,024 = 262,144 to each).

You can use MKCoordinateForMapPoint() on each to get CLLocationCoordinate2D out, as well as subtract their differences to get the MKCoordinateSpan.

  • Top left: { latitude: 84.8024737243345, longitude: -175.78125 }
  • Bottom right: { latitude: 84.7705283207591, longitude: -175.4296875 }
  • Span: { latitudeDelta: 0.0319454035754205, longitudeDelta: 0.3515625 }

Yes, these points are very close to the top-left Alaska region of the map, but that's logical given that x = 12 and y = 8 out of 1,024 tiles numbered from the top left.

@interface GridTileOverlay : MKTileOverlay

@end

@implementation GridTileOverlay
-(void)loadTileAtPath:(MKTileOverlayPath)path result:(void (^)(NSData *, NSError *))result {
    NSLog(@"Loading tile x/y/z: %ld/%ld/%ld",(long)path.x,(long)path.y,(long)path.z);

    int worldPointSize = pow(2,(int)path.z)*256; // 2^10 * 256 = 262,144
    int tilesOnASide = pow(2,(int)path.z);       // 2^10 = 1,024
    long leftEdge = path.x *256;                 // 256 * 12 = 3,072 points
    long topEdge = path.y *256;                  // 256 * 8  = 2,048
    int w = self.boundingMapRect.size.width;     // 2^20 * 256 = 268,435,456
    int zScale =  w / worldPointSize;            // 268,435,456 / 262,144 = 1,024
    int tileSize = 256 * zScale;                 // 256 * 1,024 = 262,144
    long x0 = leftEdge*zScale;                   // 3,072 * 1,024 = 3,145,728
    long y0 = topEdge*zScale;                    // 2,048 * 1,024 = 3,145,728
    long x1 = x0 + tileSize;
    long y1 = y0 + tileSize;

    MKMapPoint ul = MKMapPointMake(x0, y0);      // upper left
    MKMapPoint lr = MKMapPointMake(x1, y1);      // lower right
    MKMapRect mapRect = MKMapRectMake (fmin(ul.x, lr.x),
                              fmin(ul.y, lr.y),
                              fabs(ul.x - lr.x),
                              fabs(ul.y - lr.y));
}
@end  
like image 63
incanus Avatar answered Dec 30 '22 01:12

incanus