Draw polyline using Swift
Asked Answered
F

3

11

I'm trying to get an understanding of how to draw polylines using Swift. I've looked at the documentation, referenced some tutorials, and checked out some other SO posts, but I still can't get the thing to draw a line on my map. Here's my code. Anyone tell me what I'm doing wrong here?

import UIKit
import MapKit

class FirstViewController: UIViewController {

    @IBOutlet weak var map: MKMapView!

    override func viewDidAppear(animated: Bool) {
        super.viewDidAppear(animated)

        let location = CLLocationCoordinate2D(
            latitude: -73.761105,
            longitude: 41.017791
        )

        let span = MKCoordinateSpanMake(0.07, 0.07)
        let region = MKCoordinateRegion(center: location, span: span)
        map.setRegion(region, animated: true)

        let annotation = MKPointAnnotation()
        annotation.setCoordinate(location)
        annotation.title = "White Plains"
        annotation.subtitle = "Westchester County"
        map.addAnnotation(annotation)

        var locations = [CLLocation(latitude: -73.761105, longitude: 41.017791), CLLocation(latitude: -73.760701,longitude: 41.019348), CLLocation(latitude: -73.757201, longitude: 41.019267), CLLocation(latitude: -73.757482, longitude: 41.016375), CLLocation(latitude: -73.761105, longitude: 41.017791)]
        var coordinates = locations.map({(location: CLLocation!) -> CLLocationCoordinate2D in return location.coordinate})
        var polyline = MKPolyline(coordinates: &coordinates, count: locations.count)

        self.map.addOverlay(polyline)

    }

    func mapView(mapView: MKMapView!, rendererForOverlay overlay: MKOverlay!) -> MKOverlayRenderer! {
        if overlay is MKPolyline {
            var polylineRenderer = MKPolylineRenderer(overlay: overlay)
            polylineRenderer.strokeColor = UIColor.blueColor()
            polylineRenderer.lineWidth = 5
            return polylineRenderer
        }

        return nil
    }

}

Thanks!

Forbiddance answered 19/12, 2014 at 2:28 Comment(2)
The coordinates may be backwards. White Plains is at longitude -73.xxx (not latitude). Right now, the line may be in Antarctica.Counterspy
Thanks Anna! Dumb mistake. Wish I could "accept" this comment.Forbiddance
C
16

Here MKGeodesicPolyline will solve your problem. Add object of MKGeodesicPolyline instead of MKPolyline.

In your code remove below two lines:

    let polyline = MKPolyline(coordinates: &coordinates, count: locations.count)
    map.add(polyline)

And add these lines:

    let geodesic = MKGeodesicPolyline(coordinates: coordinates, count: 2)
    map.addOverlay(geodesic)

Swift 5.0:

func createPolyline(mapView: MKMapView) {
    let point1 = CLLocationCoordinate2DMake(-73.761105, 41.017791);
    let point2 = CLLocationCoordinate2DMake(-73.760701, 41.019348);
    let point3 = CLLocationCoordinate2DMake(-73.757201, 41.019267);
    let point4 = CLLocationCoordinate2DMake(-73.757482, 41.016375);
    let point5 = CLLocationCoordinate2DMake(-73.761105, 41.017791);
    
    let points: [CLLocationCoordinate2D]
    points = [point1, point2, point3, point4, point5]
    
    let geodesic = MKGeodesicPolyline(coordinates: points, count: 5)
    map.addOverlay(geodesic)
    
    UIView.animate(withDuration: 1.5, animations: { () -> Void in
        let span = MKCoordinateSpan(latitudeDelta: 0.01, longitudeDelta: 0.01)
        let region1 = MKCoordinateRegion(center: point1, span: span)
        self.map.setRegion(region1, animated: true)
    })
}

Objective C code:

- (void) createGeoPolyline {
    
    CLLocationCoordinate2D point1 = { -73.761105, 41.017791 };
    CLLocationCoordinate2D point2 = { -73.760701, 41.019348 };
    CLLocationCoordinate2D point3 = { -73.757201, 41.019267 };
    CLLocationCoordinate2D point4 = { -73.757482, 41.016375 };
    CLLocationCoordinate2D point5 = { -73.761105, 41.017791 };

   CLLocationCoordinate2D points[] = {point1, point2, point3, point4, point5};
    
    MKGeodesicPolyline *geodesic = [MKGeodesicPolyline polylineWithCoordinates:&points[0] count:5];
    [self.mapView addOverlay:geodesic];
    
    [UIView animateWithDuration:1.5 animations:^{
        MKCoordinateRegion region;
        region.center = point1;
        
        MKCoordinateSpan span;
        span.latitudeDelta  = 0.01;
        span.longitudeDelta = 0.01;
        region.span = span;
        [self.mapView setRegion:region animated:YES];
    }];
}

Above Objective C code works perfect and it will show overlay below:

enter image description here

But if you try Swift code it will not. I tried as much as I can to solve it out but It won't change. May be it is bug from MapKit framework.

Christchurch answered 19/12, 2014 at 5:6 Comment(7)
There is no problem with the OP's code except that the coordinates are backwards. I don't understand what "problem" using a geodesic will solve if the coordinates are wrong in the first place.Counterspy
Both the OP's code and this code draw lines in Antarctica. This code actually doesn't draw the line the OP intended.Counterspy
What is the difference?Christchurch
Thank you very much for your reply. It definitely helped - at least now I have a line on the map. But now I have another problem. The line starts in White Plains, NY (having reversed my coords per Anna's helpful remark) and stretches across the Atlantic Ocean in an arc, ending just outside of Africa. The coords I used should have drawn a very small area in White Plains. Any idea what's going on here?Forbiddance
@TJRogers- Yes, I saw that overlay is spread out, I have tried it in Objective C, works perfect. But not in Swift, I think it is bug from MapKit.Christchurch
the swift code got an error.. [here] is how to fix it: (#38272427)Prosciutto
@PavlosNicolaou: Edited answer to support Swift 3.0Christchurch
G
3

UPDATE: This seems to be fixed on Swift 3+. See accepted answer.

On this line:

var polyline = MKPolyline(coordinates: &coordinates, count: locations.count)

You're casting a Swift array reference as an UnsafePointer<CLLocationCoordinate2D>.

That's pretty dangerous and I'm not sure why Swift allows it to compile. Best case scenario you get the line drawn, worse case (which seems to be your case) you get nothing.

The MKPolyline constructors wants an UsafePointer<CLLocationCoordinate2D> and that's what you should pass.

I usually add a private category to MKPolyline to create a convenience init method that accepts a normal Swift array:

private extension MKPolyline {
    convenience init(coordinates coords: Array<CLLocationCoordinate2D>) {
        let unsafeCoordinates = UnsafeMutablePointer<CLLocationCoordinate2D>.alloc(coords.count)
        unsafeCoordinates.initializeFrom(coords)
        self.init(coordinates: unsafeCoordinates, count: coords.count)
        unsafeCoordinates.dealloc(coords.count)
    }
}
Gathering answered 27/9, 2016 at 15:29 Comment(2)
I'm getting a warning in Xcode 8.3.1: 'initialize(from:)' is deprecated: it will be removed in Swift 4.0. Please use 'UnsafeMutableBufferPointer.initialize(from:)' insteadHypocrisy
@BrandonNott You don't actually have to do this. Make your array specifically of type [CLLocationCoordinate2D], and pass the array itself. You don't have to include the & or anything.Willmert
A
0

Create polyline on apple map :

import MapKit
import CoreLocation

<CLLocationManagerDelegate,MKMapViewDelegate>

Code

    self.mapView.delegate = self

    var coordinateArray: [CLLocationCoordinate2D] = []
    let destination1 = CLLocationCoordinate2DMake(22.3039, 70.8022)
    let destination2 = CLLocationCoordinate2DMake(23.0225, 72.5714)
    coordinateArray.append(destination1)
    coordinateArray.append(destination2)

    let polygon = MKPolyline(coordinates: coordinateArray, count: coordinateArray.count)
    self.mapView.addOverlay(polygon)

Delegate Method

func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer {
    if overlay.isKind(of: MKPolyline.self){
        let polylineRenderer = MKPolylineRenderer(overlay: overlay)
            polylineRenderer.fillColor = UIColor.blue
            polylineRenderer.strokeColor = UIColor.blue
            polylineRenderer.lineWidth = 2
        
        return polylineRenderer
 }
    return MKOverlayRenderer(overlay: overlay)
}
Appurtenant answered 9/9, 2020 at 5:24 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.