Last active
January 13, 2024 16:28
-
-
Save olejorgensen/f587f70cb96aefe7a61fde629117181e to your computer and use it in GitHub Desktop.
MapKit and CoreLocation extensions
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
import Foundation | |
extension Array where Element: Hashable { | |
var uniques: Array { | |
return Array(Set<Element>(self)) | |
} | |
} |
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
import Foundation | |
import CoreLocation | |
extension CLLocationCoordinate2D { | |
func distance(from: CLLocationCoordinate2D) -> Double { | |
let aLocation = CLLocation(latitude: self.latitude, longitude: self.longitude) | |
let bLocation = CLLocation(latitude: from.latitude, longitude: from.longitude) | |
return aLocation.distance(from: bLocation) | |
} | |
} |
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
import Foundation | |
extension DateFormatter { | |
static let iso8601Full: DateFormatter = { | |
let formatter = DateFormatter() | |
formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSS" | |
formatter.calendar = Calendar(identifier: .iso8601) | |
formatter.timeZone = TimeZone(secondsFromGMT: 0) | |
formatter.locale = Locale(identifier: "en_US_POSIX") | |
return formatter | |
}() | |
} |
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
import Foundation | |
import MapKit | |
import CoreLocation | |
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 | |
} | |
} | |
public static func makeSpan(latDelta: Double, longDelta: Double) -> MKCoordinateSpan? { | |
if let latDegrees = CLLocationDegrees(exactly: latDelta), let longDegrees = CLLocationDegrees(exactly: longDelta) { | |
let span = MKCoordinateSpan(latitudeDelta: latDegrees, longitudeDelta: longDegrees) | |
return span | |
} | |
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 | |
if coordinates.isEmpty { return nil } | |
// handle single coordinate | |
guard coordinates.count > 1 else { | |
guard let span = makeSpan(latDelta: 0.0125, longDelta: 0.0125) else { return nil } | |
return MKCoordinateRegion(center: coordinates[0], span: span) | |
} | |
let transformed = coordinates.map(transform) | |
// find the span | |
let buffer = 0.00125 | |
let minLat = transformed.min { $0.latitude < $1.latitude }!.latitude - buffer | |
let maxLat = transformed.max { $0.latitude < $1.latitude }!.latitude + buffer | |
let minLon = transformed.min { $0.longitude < $1.longitude }!.longitude - buffer | |
let maxLon = transformed.max { $0.longitude < $1.longitude }!.longitude + buffer | |
guard let span = makeSpan(latDelta: maxLat - minLat, longDelta: maxLon - minLon) else { return nil } | |
// find the center of the span | |
let center = inverseTransform(CLLocationCoordinate2DMake((maxLat - span.latitudeDelta / 2), maxLon - span.longitudeDelta / 2)) | |
return MKCoordinateRegion(center: center, span: span) | |
} | |
} |
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
import Foundation | |
import MapKit | |
import CoreLocation | |
extension MKMapRect { | |
static func distance(from: CLLocationCoordinate2D, to: CLLocationCoordinate2D) -> Double { | |
let aLocation = CLLocation(latitude: from.latitude, longitude: from.longitude) | |
let bLocation = CLLocation(latitude: to.latitude, longitude: to.longitude) | |
return aLocation.distance(from: bLocation) | |
} | |
var minCoordinate: CLLocationCoordinate2D { | |
get { | |
let point = MKMapPoint.init(x: self.minX, y: self.minY) | |
return point.coordinate | |
} | |
} | |
var maxCoordinate: CLLocationCoordinate2D { | |
get { | |
let point = MKMapPoint.init(x: self.maxX, y: self.maxY) | |
return point.coordinate | |
} | |
} | |
static func makeRegion(center: CLLocationCoordinate2D, rect: MKMapRect) -> CLCircularRegion { | |
let ne = MKMapPoint(x: rect.maxY, y: rect.origin.y).coordinate | |
let sw = MKMapPoint(x: rect.origin.x, y: rect.maxY).coordinate | |
let diameter = MKMapRect.distance(from: ne, to: sw) | |
let region = CLCircularRegion(center: center, radius: diameter / 2.0, identifier: "mapWindow") | |
return region | |
} | |
} |
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
import Foundation | |
public extension String { | |
func stringByAppendingPathComponent(path: String) -> String { | |
let nsSt = self as NSString | |
return nsSt.appendingPathComponent(path) | |
} | |
static let alfaNumInsensitiveCompareOptions: NSString.CompareOptions = NSString.CompareOptions.caseInsensitive.union(NSString.CompareOptions.numeric) | |
func alfaNumInsensitiveCompare(other: String) -> ComparisonResult { | |
return compare(other, options: String.alfaNumInsensitiveCompareOptions) | |
} | |
/** | |
Trim using CharacterSet.whitespacesAndNewlines | |
*/ | |
var trimmed: String { | |
get { | |
return self.trimmingCharacters(in: CharacterSet.whitespacesAndNewlines) | |
} | |
} | |
/** | |
Trim using CharacterSet.whitespacesAndNewlines | |
*/ | |
var trimmedOrNil: String? { | |
get { | |
let trimmed = self.trimmingCharacters(in: CharacterSet.whitespacesAndNewlines) | |
return trimmed.isEmpty ? nil : trimmed | |
} | |
} | |
/** | |
Encode using CharacterSet.urlQueryAllowed | |
*/ | |
var toQueryValue: String? { | |
get { | |
return self.addingPercentEncoding(withAllowedCharacters: CharacterSet.urlQueryAllowed) | |
} | |
} | |
func firstDigits(options: NSRegularExpression.Options = NSRegularExpression.Options.caseInsensitive) -> String? { | |
return firstMatch("(\\d+)", options: options) | |
} | |
func firstMatch(_ pattern: String, options: NSRegularExpression.Options = NSRegularExpression.Options.caseInsensitive) -> String? { | |
let re = try? NSRegularExpression(pattern: pattern, options: options) | |
if let match = re?.firstMatch(in: self, options: [], range: NSRange(location: 0, length: self.count)) { | |
let range = match.range(at: 1) | |
if range.location < Int.max { | |
let substring = (self as NSString).substring(with: range) | |
return substring | |
} | |
} | |
return nil | |
} | |
func isMatchedByAny(regexes: [NSRegularExpression]) -> Bool { | |
for regex in regexes { | |
if isMatchedBy(regex: regex) { | |
return true | |
} | |
} | |
return false | |
} | |
func isMatchedBy(regex: NSRegularExpression) -> Bool { | |
return regex.firstMatch(in: self, range: NSRange(location: 0, length: self.count)) != nil | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment