iOS/Sources/Shared/Common/Extensions/CLLocation+Extensions.swift

133 lines
5.0 KiB
Swift
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import CoreLocation
import Foundation
public extension CLLocationCoordinate2D {
func toArray() -> [Double] {
[latitude, longitude]
}
func bearing(to destination: CLLocationCoordinate2D) -> Measurement<UnitAngle> {
let sourceLatitude: Measurement<UnitAngle> = .init(value: latitude, unit: .degrees)
let sourceLongitude: Measurement<UnitAngle> = .init(value: longitude, unit: .degrees)
let destinationLatitude: Measurement<UnitAngle> = .init(value: destination.latitude, unit: .degrees)
let destinationLongitude: Measurement<UnitAngle> = .init(value: destination.longitude, unit: .degrees)
// tanθ = sinΔλcosφ2 / cosφ1sinφ2 sinφ1cosφ2cosΔλ
// see mathforum.org/library/drmath/view/55417.html for derivation
// https://www.movable-type.co.uk/scripts/latlong.html
let φ1 = sourceLatitude.converted(to: .radians).value
let φ2 = destinationLatitude.converted(to: .radians).value
let Δλ = (destinationLongitude - sourceLongitude).converted(to: .radians).value
// everything in here is now in radians
let x = cos(φ1) * sin(φ2) - sin(φ1) * cos(φ2) * cos(Δλ)
let y = sin(Δλ) * cos(φ2)
var θ = atan2(y, x)
while θ < 0 {
// normalize to positive -- doesn't change the math, but makes logging/tests easier
θ += 2.0 * Double.pi
}
return .init(value: θ, unit: .radians)
}
func moving(
distance: Measurement<UnitLength>,
direction: Measurement<UnitAngle>
) -> CLLocationCoordinate2D {
let latitudeMeasurement: Measurement<UnitAngle> = .init(value: latitude, unit: .degrees)
let longitudeMeasurement: Measurement<UnitAngle> = .init(value: longitude, unit: .degrees)
let earthRadius: Measurement<UnitLength> = .init(value: 6371, unit: .kilometers)
// sinφ2 = sinφ1cosδ + cosφ1sinδcosθ
// tanΔλ = sinθsinδcosφ1 / cosδsinφ1sinφ2
// see mathforum.org/library/drmath/view/52049.html for derivation
// https://www.movable-type.co.uk/scripts/latlong.html
let θ = direction.converted(to: .radians).value
let δ = distance.converted(to: .meters).value / earthRadius.converted(to: .meters).value
let φ1 = latitudeMeasurement.converted(to: .radians).value
let λ1 = longitudeMeasurement.converted(to: .radians).value
// everything in here is now in radians
let sinφ2 = sin(φ1) * cos(δ) + cos(φ1) * sin(δ) * cos(θ)
let φ2 = asin(sinφ2)
let y = sin(θ) * sin(δ) * cos(φ1)
let x = cos(δ) - sin(φ1) * sinφ2
let λ2 = λ1 + atan2(y, x)
let resultLatitudeMeasurement: Measurement<UnitAngle> = .init(value: φ2, unit: .radians)
let resultLongitudeMeasurement: Measurement<UnitAngle> = .init(value: λ2, unit: .radians)
return .init(
latitude: resultLatitudeMeasurement.converted(to: .degrees).value,
longitude: resultLongitudeMeasurement.converted(to: .degrees).value
)
}
}
public extension CLLocationDegrees {
init?(templateValue value: Any?) {
if let value = value as? String {
self.init(value)
} else if let value = value as? Double {
self.init(value)
} else if let value = value as? Int {
self.init(value)
} else {
return nil
}
}
}
public extension CLCircularRegion {
func distanceWithAccuracy(from location: CLLocation) -> CLLocationDistance {
let centerLocation = CLLocation(latitude: center.latitude, longitude: center.longitude)
return
// how far away from the center we are
location.distance(from: centerLocation)
// to get to the outer radius (perimeter)
- radius
// adding the accuracy amount we have already
- location.horizontalAccuracy
}
func containsWithAccuracy(_ location: CLLocation) -> Bool {
distanceWithAccuracy(from: location) <= 0
}
}
public extension CLLocation {
func fuzzingAccuracy(by amount: CLLocationDistance) -> CLLocation {
CLLocation(
coordinate: coordinate,
altitude: altitude,
horizontalAccuracy: horizontalAccuracy + amount + 1,
verticalAccuracy: verticalAccuracy,
course: course,
courseAccuracy: courseAccuracy,
speed: speed,
speedAccuracy: speedAccuracy,
timestamp: timestamp
)
}
func changingCoordinate(to fuzzedCoordinate: CLLocationCoordinate2D) -> CLLocation {
CLLocation(
coordinate: fuzzedCoordinate,
altitude: altitude,
horizontalAccuracy: horizontalAccuracy,
verticalAccuracy: verticalAccuracy,
course: course,
courseAccuracy: courseAccuracy,
speed: speed,
speedAccuracy: speedAccuracy,
timestamp: timestamp
)
}
}