iosswiftmapkitmkmapviewmkmaprect

Determine MapKit tiles for visibleMapRect


I saw simple code for calculating a grid of tile bounds needed for a given MKMapRect but I can't find it now. IIRC it was a function built-in to MapKit.

Given a MKMapRect like mapview.visibleMapRect and zoom level, how can I calculate an array of tile paths that will be used for the given rect?

import MapKit

class ViewController: UIViewController {

    @IBOutlet weak var mapview: MKMapView!

    override func viewDidLoad() {
        super.viewDidLoad()
        mapview.delegate = self

        let location = CLLocationCoordinate2D(latitude: 44.0, longitude: -120.0)
        let span = MKCoordinateSpanMake(1.0, 1.0)
        let region = MKCoordinateRegion(center: location, span: span)
        mapview.setRegion(region, animated: true)

        let rect = mapview.visibleMapRect
    
        let requiredTiles = ?
    }
}

Solution

  • I've been working with MapKit and MKTileOverlays for years, and I don't recall ever seeing a built-in way to calculate tiles in an area. But there are some libraries out there that can help with it. For example, gis-tools, and the MapTile struct.

    You can initialize a MapTile with a coordinate and zoom level, so you can calculate the northwest and southeast corners of your MKMapRect, convert them to coordinates and then to MapTiles, and then use a simple nested for loop to go through the tile keys.

    That would look something like this:

    let nwCoord = Coordinate3D(mapRect.origin.coordinate)
    let seCoord = Coordinate3D(MKMapPoint(x: mapRect.maxX, y: mapRect.maxY).coordinate)
    let nwTile = MapTile(coordinate: nwCoord, atZoom: zoom)
    let seTile = MapTile(coordinate: seCoord, atZoom: zoom)
    
    var result = Set<MapTile>()
    for x in nwTile.x...seTile.x {
        for y in nwTile.y...seTile.y {
            result.insert(MapTile(x: x, y: y, z: zoom))
        }
    }
    

    Maybe not the exact elegant solution you were looking for, but hopefully it's helpful.