How to draw an arc between two coordinate points in Google Maps like in this image and same like facebook post in iOS ?
None of the answers mentioned is a full proof solution. For a few locations, it draws a circle instead of a polyline. To resolve this we will calculate bearing(degrees clockwise from true north) and if it is less than zero, swap the start and end location.
func createArc(
startLocation: CLLocationCoordinate2D,
endLocation: CLLocationCoordinate2D) -> GMSPolyline {
var start = startLocation
var end = endLocation
if start.bearing(to: end) < 0.0 {
start = endLocation
end = startLocation
}
let angle = start.bearing(to: end) * Double.pi / 180.0
let k = abs(0.3 * sin(angle))
let path = GMSMutablePath()
let d = GMSGeometryDistance(start, end)
let h = GMSGeometryHeading(start, end)
let p = GMSGeometryOffset(start, d * 0.5, h)
let x = (1 - k * k) * d * 0.5 / (2 * k)
let r = (1 + k * k) * d * 0.5 / (2 * k)
let c = GMSGeometryOffset(p, x, h + 90.0)
var h1 = GMSGeometryHeading(c, start)
var h2 = GMSGeometryHeading(c, end)
if (h1 > 180) {
h1 = h1 - 360
}
if (h2 > 180) {
h2 = h2 - 360
}
let numpoints = 100.0
let step = ((h2 - h1) / Double(numpoints))
for i in stride(from: 0.0, to: numpoints, by: 1) {
let pi = GMSGeometryOffset(c, r, h1 + i * step)
path.add(pi)
}
path.add(end)
let polyline = GMSPolyline(path: path)
polyline.strokeWidth = 3.0
polyline.spans = GMSStyleSpans(
polyline.path!,
[GMSStrokeStyle.solidColor(UIColor(hex: "#2962ff"))],
[20, 20], .rhumb
)
return polyline
}
The bearing is the direction in which a vertical line on the map points, measured in degrees clockwise from north.
func bearing(to point: CLLocationCoordinate2D) -> Double {
func degreesToRadians(_ degrees: Double) -> Double { return degrees * Double.pi / 180.0 }
func radiansToDegrees(_ radians: Double) -> Double { return radians * 180.0 / Double.pi }
let lat1 = degreesToRadians(latitude)
let lon1 = degreesToRadians(longitude)
let lat2 = degreesToRadians(point.latitude);
let lon2 = degreesToRadians(point.longitude);
let dLon = lon2 - lon1;
let y = sin(dLon) * cos(lat2);
let x = cos(lat1) * sin(lat2) - sin(lat1) * cos(lat2) * cos(dLon);
let radiansBearing = atan2(y, x);
return radiansToDegrees(radiansBearing)
}