iOS/Sources/Shared/Common/Extensions/CLLocation+Extensions.swift
Michal Šrůtek 3146e08aac
Available checks cleanup for iOS 15 and watchOS 8 (#2622)
<!-- Thank you for submitting a Pull Request and helping to improve Home
Assistant. Please complete the following sections to help the processing
and review of your changes. Please do not delete anything from this
template. -->

## Summary
<!-- Provide a brief summary of the changes you have made and most
importantly what they aim to achieve -->

As the [minimum watchOS is
8](https://github.com/home-assistant/iOS/pull/2609) and [minimum iOS
15](https://github.com/home-assistant/iOS/pull/2469), this PR removes
redundant `#available` checks.

Co-authored-by: Bruno Pantaleão Gonçalves <bruno.ing879@gmail.com>
2024-03-04 15:36:55 +01:00

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
)
}
}