3

I am using the following Swift code to create arcs using Apple's MKMap framework. This should give an idea how things are being calculated...

import Foundation
import UIKit
import MapKit

class IGAArcRenderer: MKOverlayPathRenderer {
    var polyline: MKPolyline
    var arcCentreMapPoint: MKMapPoint
    var initialBearing : Double
    var finalBearing : Double
    
    init(polylineIn: MKPolyline,centreCoordinates: CLLocationCoordinate2D,initBearing: Double,finalBearing: Double) {
        polyline = polylineIn
        self.arcCentreMapPoint = MKMapPoint(centreCoordinates)
        self.initialBearing = initBearing
        self.finalBearing = finalBearing
        super.init(overlay: polyline)
        setup()
    }
    

    override func createPath() {
        let points = polyline.points()
        let startPoint = point(for: points[0])
        let endPoint = point(for: points[1])

        // Defining our new curved path using Bezier path
        let myPath = UIBezierPath()
        myPath.move(to: startPoint)
            
        let arcCentre = point(for: arcCentreMapPoint)

        let startAngle = deg2rad(Deg: CGFloat(self.initialBearing+90))
        let endAngle = deg2rad(Deg: CGFloat(self.finalBearing+90))
            
        // Radius to Start and End Points
        let radiusStart = CGPointDistance(from: arcCentre, to: startPoint)
        let radiusEnd = CGPointDistance(from: arcCentre, to: endPoint)
            
       // Calculate which direction the arc should go when is drawn.
       // If the following value is greater than 0, go clockwise
       //let c = (startPoint.x-arcCentre.x)*(endPoint.y-arcCentre.y)-(startPoint.y-arcCentre.y)*(endPoint.x-arcCentre.x)
       //let direction = c<0 ? false : true


        /* NOTE: This code is working and draws an arc, which is not always perfect due to differences in radius
        myPath.addArc(withCenter: arcCentre,
                    radius: radius,
                    startAngle: startAngle,
                    endAngle: endAngle,
                    clockwise: direction)
        myPath.addLine(to: endPoint)
        */
            
        // Draw an arc by adding a number of lines determined by delta radius and delta angle
        let numpoints = 100
        let stepRadius = (radiusEnd-radiusStart)/CGFloat(numpoints)
        var stepAngle : CGFloat = 0.0
        
        if startAngle>=0 && endAngle>=0 {
            stepAngle = (endAngle-startAngle)/CGFloat(numpoints)
        }
        else if startAngle<0 && endAngle>=0 {
            stepAngle = (endAngle+startAngle)/CGFloat(numpoints)
        }
        else if startAngle>=0 && endAngle<0 {
            stepAngle = (endAngle+startAngle)/CGFloat(numpoints)
        }
        else if startAngle<0 && endAngle<0 {
            stepAngle = (endAngle-startAngle)/CGFloat(numpoints)
        }
        
        print("........ C \(c)")
        print("........ START ANGLE \(startAngle)")
        print("........ END ANGLE \(endAngle)")
        print("........ STEP ANGLE 11 \(endAngle-startAngle)")
        print("........ STEP ANGLE 22 \(stepAngle)")        
        
        // For each point, calculate the CGPoint based on the radius and angle 
        // and draw a line to this point
        for i in 0..<numpoints {
                
            var angleCurrent : CGFloat = 0.0
                
            // Trying to play here to get the right direction
            if startAngle>=0 && startAngle>endAngle {
                angleCurrent = startAngle+CGFloat(i)*stepAngle // ORIGINAL
            }
            else if startAngle<0 && startAngle>endAngle {
                angleCurrent = startAngle-CGFloat(i)*stepAngle // ORIGINAL
            }
            if startAngle>=0 && startAngle<endAngle {
                angleCurrent = startAngle-CGFloat(i)*stepAngle // ORIGINAL
            }
            else {
                angleCurrent = startAngle+CGFloat(i)*stepAngle // ORIGINAL
            }
                
            // Next radius
            let radiusCurrent = radiusStart+CGFloat(i)*stepRadius
                
            // Find the CGPoint
            let pi_x = cos(angleCurrent)*((arcCentre.x-radiusCurrent)-arcCentre.x)-sin(angleCurrent)*((arcCentre.y)-arcCentre.y)+arcCentre.x
            let pi_y = sin(angleCurrent)*((arcCentre.x-radiusCurrent)-arcCentre.x)+cos(angleCurrent)*((arcCentre.y)-arcCentre.y)+arcCentre.y;
            let newLocation = CGPoint(x: pi_x, y: pi_y)

            myPath.addLine(to: newLocation)
        }
            
        myPath.addLine(to: endPoint)

        path = myPath.cgPath 
    }
        
    func CGPointDistanceSquared(from: CGPoint, to: CGPoint) -> CGFloat {
        return (from.x - to.x) * (from.x - to.x) + (from.y - to.y) * (from.y - to.y)
    }

    func CGPointDistance(from: CGPoint, to: CGPoint) -> CGFloat {
        return sqrt(CGPointDistanceSquared(from: from, to: to))
    }
    
    func deg2rad(Deg deg: CGFloat) -> CGFloat {
        return (deg * .pi / 180.0);
    }
    
    func rad2deg(Rad rad: CGFloat) -> CGFloat {
        return (rad * 180 / .pi);
    }
}

Sometimes, the arcs are drawn perfectly, sometimes there are problems with finding the right direction and calculation of angle step value. Here are some examples of what I got with the key values. For the first image, there is a problem with calculating the proper angle step for the first arc (should be around 0.017), but the second arc is drawn perfectly. For the third image, the arc is going the wrong direction, whereas in all other cases, the direction is good.

Case 1
START POINT (latitude: 25.41505833, longitude: 85.23135556)
CENTER POINT (latitude: 25.49688333, longitude: 84.91539167)
END POINT (latitude: 25.59042, longitude: 85.08992)

Case 3
START POINT (latitude: 25.37544722, longitude: 85.0547)
CENTER POINT (latitude: 25.62390278, longitude: 85.28933611)
END POINT (latitude: 25.59042, longitude: 85.08992)

Is there anyone here with good knowledge of geometry?

enter image description here

enter image description here

enter image description here

enter image description here

1 Answer 1

0

Solved this by normalizing calculated headings (ranging from -180 to +180) to 360 degrees. Now everything is drawn correctly. So simple!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.