Last active
January 27, 2024 00:54
-
-
Save dionc/46f7e7ee9db7dbd7bddec56bd5418ca6 to your computer and use it in GitHub Desktop.
Create an MKCoordinateRegion from an array of coordinates. Safely handles coordinates that cross the 180th meridian.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) | |
} | |
} |
Thanks for this!
This helped me out with making my first iOS project! I'll share your github on my blog! thank you again :)
This is amazing, but how can I add some padding for the edges? in other words I need the zoom level to be larger so pins are not on the edges of the screen. @dionc
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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