Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Create an MKCoordinateRegion from an array of coordinates. Safely handles coordinates that cross the 180th meridian.
import MapKit
extension MKCoordinateRegion {
init?(coordinates: [CLLocationCoordinate2D]) {
// first create a region centered around the prime meridian
let primeRegion = MKCoordinateRegion.region(for: coordinates, transform: { $0 }, inverseTransform: { $0 })
// next create a region centered around the 180th meridian
let transformedRegion = MKCoordinateRegion.region(for: coordinates, transform: MKCoordinateRegion.transform, inverseTransform: MKCoordinateRegion.inverseTransform)
// return the region that has the smallest longitude delta
if let a = primeRegion,
let b = transformedRegion,
let min = [a, b].min(by: { $0.span.longitudeDelta < $1.span.longitudeDelta }) {
self = min
}
else if let a = primeRegion {
self = a
}
else if let b = transformedRegion {
self = b
}
else {
return nil
}
}
// Latitude -180...180 -> 0...360
private static func transform(c: CLLocationCoordinate2D) -> CLLocationCoordinate2D {
if c.longitude < 0 { return CLLocationCoordinate2DMake(c.latitude, 360 + c.longitude) }
return c
}
// Latitude 0...360 -> -180...180
private static func inverseTransform(c: CLLocationCoordinate2D) -> CLLocationCoordinate2D {
if c.longitude > 180 { return CLLocationCoordinate2DMake(c.latitude, -360 + c.longitude) }
return c
}
private typealias Transform = (CLLocationCoordinate2D) -> (CLLocationCoordinate2D)
private static func region(for coordinates: [CLLocationCoordinate2D], transform: Transform, inverseTransform: Transform) -> MKCoordinateRegion? {
// handle empty array
guard !coordinates.isEmpty else { return nil }
// handle single coordinate
guard coordinates.count > 1 else {
return MKCoordinateRegion(center: coordinates[0], span: MKCoordinateSpanMake(1, 1))
}
let transformed = coordinates.map(transform)
// find the span
let minLat = transformed.min { $0.latitude < $1.latitude }!.latitude
let maxLat = transformed.max { $0.latitude < $1.latitude }!.latitude
let minLon = transformed.min { $0.longitude < $1.longitude }!.longitude
let maxLon = transformed.max { $0.longitude < $1.longitude }!.longitude
let span = MKCoordinateSpanMake(maxLat - minLat, maxLon - minLon)
// find the center of the span
let center = inverseTransform(CLLocationCoordinate2DMake((maxLat - span.latitudeDelta / 2), maxLon - span.longitudeDelta / 2))
return MKCoordinateRegionMake(center, span)
}
}
@AdrianBinDC

This comment has been minimized.

Copy link

AdrianBinDC commented Jul 10, 2018

Thank you for posting this. If you're looking for something that gets regions from continents and major oceans, I threw this together.

https://gist.github.com/AdrianBinDC/19d283f31cdd04a467ff3adad5c12413

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.