I used spherical excess to calculate the area. The article in StefanS’s answer has a great explanation on how spherical excess works. It uses triangles, with one edge being the polygon segment, and the other two edges are meridians connecting to a pole. Wikipedia suggested using a “spherical quadrangle” with the same principle’s. It uses the polygon segment, the equator, and two edges that are meridians connecting to the equator.
StefanS’s answer uses an equation for the approximation of area. I wanted to be more exact. I implemented 3 different functions. The approximation, the exact spherical triangles, and the exact spherical quadrangles.
The time was negligible in my use case, but here are the baselines:
0.208- approximation time baseline
0.517- exact spherical quadrangles time baseline
0.779- exact spherical triangles time baseline
As a bonus, the quadrangle solution didn’t need any adjustment for the antimeridian, whereas the approximation did. The quadrangle solution is a lot simpler than the triangle solution. In my tests, the largest difference in results between the quadrangle and triangle solutions were about 0.0000001%.
I used this equation from wikipedia's Spherical Trigonometry, Area and Spherical Excess: https://en.wikipedia.org/wiki/Spherical_trigonometry#Area_and_spherical_excess
And StefanS's article:
“The Spherical Case” equation is detailed in “Some Algorithms for Polygons on a Sphere” by Chamberlain & Duquette (JPL Publication 07-3, California Institute of Technology, 2007)
func areaUsingQuadrilateralSphericalExcess(_ coordinates: [CLLocationCoordinate2D]) -> Double {
// the poles cannot be in the polygon
guard coordinates.count > 2 else { return 0 }
let kEarthRadius = 6378137.0
var sum = 0.0
for i in 0..<coordinates.count {
let p1Latitude = coordinates[i].latitude.degreesToRadians
let p1Longitude = coordinates[i].longitude.degreesToRadians
let p2Latitude = coordinates[i + 1].latitude.degreesToRadians
let p2Longitude = coordinates[i + 1].longitude.degreesToRadians
let sphericalExcess = 2 * atan((sin(0.5 * (p2Latitude + p1Latitude))/cos(0.5 * (p2Latitude - p1Latitude))) * tan((p2Longitude - p1Longitude)/2))
sum += sphericalExcess
}
return abs(sum * kEarthRadius * kEarthRadius) // if a clockwise polygon, sum will be negative
}