Last active
March 4, 2017 19:51
-
-
Save kayoslab/90ffed97919e6413ae95df5cf86aa2f7 to your computer and use it in GitHub Desktop.
CLLocationCoordinate2D Extension to get a CoordinateRange struct within a given maximum Distance in Swift 3.0
This file contains hidden or 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
/* | |
* Copyright (C) kayoslabs - All Rights Reserved | |
* Description from Jan Philip Matuschek | |
* (http://janmatuschek.de/LatitudeLongitudeBoundingCoordinates) | |
* Written by kayoslabs, March 2017 | |
*/ | |
import Foundation | |
import MapKit | |
extension CLLocationCoordinate2D { | |
internal func range(maxDist:Double) -> (latitude: CoordinateRange, longitude: CoordinateRange)? { | |
if maxDist > 0.0 { | |
let latRad:Double = self.latitude * (Double.pi / 180.0) | |
let lonRad:Double = self.longitude * (Double.pi / 180.0) | |
// Minimum | |
let kLatMin:Double = -90.0 | |
let kLatMax:Double = 90.0 | |
let kLonMin:Double = -180.0 | |
let kLonMax:Double = 180.0 | |
// Assuming a spherical approximationa of the Earth with radius: | |
// R = 6371 km | |
let R:Double = 6371.0 | |
// We can define r as the angular radius of the query circle: | |
// r = d/R = (1000 km)/(6371 km) = 0.1570 | |
// angular distance in radians on a great circle | |
let radDist:Double = maxDist / R | |
// latmin = lat - r = 1.2393 rad | |
// latmax = lat + r = 1.5532 rad | |
var latMin:Double = latRad - radDist | |
var latMax:Double = latRad + radDist | |
// If latmax is greater than π/2, then the North Pole is within the query circle | |
var lonMin:Double = 0.0 | |
var lonMax:Double = 0.0 | |
if latMin > kLatMin && latMax < kLatMax { | |
// Computing the Minimum and Maximum Longitude | |
// latT = arcsin(sin(lat)/cos(r)) = 1.4942 rad | |
// lonmin = lonT1 = lon - Δlon = -1.8184 rad | |
// lonmax = lonT2 = lon + Δlon = 0.4221 rad | |
// Δlon = arccos( ( cos(r) - sin(latT) · sin(lat) ) / ( cos(latT) · cos(lat) ) ) | |
// = arcsin(sin(r)/cos(lat)) = 1.1202 rad | |
let deltaLon:Double = asin(sin(radDist)/cos(latRad)) | |
lonMin = lonRad - deltaLon | |
lonMax = lonRad + deltaLon | |
// If one of lonmin/max is outside the range of valid longitude values [-π, π], | |
// the 180th meridian is within the query circle. | |
if lonMin < kLonMin { | |
lonMin += 2.0 * Double.pi | |
} | |
if lonMax > kLonMax { | |
lonMax -= 2.0 * Double.pi | |
} | |
} else { | |
// a pole is within the distance | |
latMin = max(latMin, kLatMin) | |
latMax = min(latMax, kLatMax) | |
lonMin = kLonMin | |
lonMax = kLonMax | |
} | |
let range = (latitude:CoordinateRange(minimum: latMin * (180.0 / Double.pi), maximum: latMax * (180.0 / Double.pi)), | |
longitude:CoordinateRange(minimum: lonMin * (180.0 / Double.pi), maximum: lonMax * (180.0 / Double.pi))) | |
return range | |
} else { | |
return nil | |
} | |
} | |
} | |
struct CoordinateRange { | |
internal var minimum:Double | |
internal var maximum:Double | |
init(minimum:Double, maximum:Double) { | |
self.minimum = minimum | |
self.maximum = maximum | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment