Skip to content

Instantly share code, notes, and snippets.

@ts95
Last active January 27, 2024 01:35
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ts95/bf113ba080c36d9ff645b322081360d6 to your computer and use it in GitHub Desktop.
Save ts95/bf113ba080c36d9ff645b322081360d6 to your computer and use it in GitHub Desktop.
CLLocationCoordinate2D + midpoint function extension
import Foundation
import CoreLocation
// based on https://gis.stackexchange.com/a/18740
extension CLLocationCoordinate2D {
var cartesian: CartesianCoordinate3D { .init(from: self) }
// Returns the coordinates of the midpoint between point a and b.
// Limitation: point a and b may not be diametrically opposite.
public static func midpoint(between a: CLLocationCoordinate2D, and b: CLLocationCoordinate2D) -> CLLocationCoordinate2D {
((a.cartesian + b.cartesian) / 2).spherical
}
}
struct CartesianCoordinate3D {
let x: Double
let y: Double
let z: Double
var spherical: CLLocationCoordinate2D {
let r = sqrt(x * x + y * y)
if r == 0 {
if z > 0 {
return .init(latitude: 90, longitude: 0)
} else if z < 0 {
return .init(latitude: -90, longitude: 0)
} else {
// (x, y, z) == (0, 0, 0)
return .init(latitude: .nan, longitude: .nan)
}
} else {
let latitude = degrees(fromRadians: atan2(z, r))
let longitude = degrees(fromRadians: atan2(y, x))
return .init(latitude: latitude, longitude: longitude)
}
}
static func + (a: CartesianCoordinate3D, b: CartesianCoordinate3D) -> CartesianCoordinate3D {
.init(x: a.x + b.x, y: a.y + b.y, z: a.z + b.z)
}
static func / (numerator: CartesianCoordinate3D, denominator: Double) -> CartesianCoordinate3D {
.init(x: numerator.x / denominator, y: numerator.y / denominator, z: numerator.z / denominator)
}
}
extension CartesianCoordinate3D {
init(from locationCoordinate2D: CLLocationCoordinate2D) {
let latitudeInRadians = radians(fromDegrees: locationCoordinate2D.latitude)
let longitudeInRadians = radians(fromDegrees: locationCoordinate2D.longitude)
x = cos(latitudeInRadians) * cos(longitudeInRadians)
y = cos(latitudeInRadians) * sin(longitudeInRadians)
z = sin(latitudeInRadians)
}
}
private func degrees(fromRadians radians: Double) -> Double {
radians * 180 / .pi
}
private func radians(fromDegrees degrees: Double) -> Double {
degrees * .pi / 180
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment