1

I am trying to learn how to generate a route between two locations, so I created a project with fake locations and stuff so that once I "get it" I can implement it in my real app.

I am trying to generate a route between "location" and "next", but am getting a bad error.

Here is the code:

import UIKit
import MapKit
import CoreLocation

class ViewController: UIViewController, MKMapViewDelegate, CLLocationManagerDelegate {

@IBOutlet var map: MKMapView!

var geocoder:CLGeocoder = CLGeocoder()
var location:CLLocation = CLLocation(latitude: 38, longitude: -77)
var next:CLLocation = CLLocation(latitude: 38.21, longitude: -77.21)
var locMark:MKPlacemark?
var destMark:MKPlacemark?
var manager:CLLocationManager = CLLocationManager()
var source:MKMapItem?
var destination:MKMapItem?
var request:MKDirectionsRequest = MKDirectionsRequest()
var directions:MKDirections = MKDirections()
var directionsResponse:MKDirectionsResponse = MKDirectionsResponse()
var route:MKRoute = MKRoute()


override func viewDidLoad() {
    super.viewDidLoad()

    manager.requestAlwaysAuthorization()

    map.mapType = MKMapType.Satellite

    locMark = MKPlacemark(coordinate: CLLocationCoordinate2DMake(location.coordinate.latitude, location.coordinate.longitude), addressDictionary: nil)
    destMark = MKPlacemark(coordinate: CLLocationCoordinate2DMake(next.coordinate.latitude, next.coordinate.longitude), addressDictionary: nil)

    source = MKMapItem(placemark: locMark)
    destination = MKMapItem(placemark: destMark)

    request.setSource(source)
    request.setDestination(destination)
    request.transportType = MKDirectionsTransportType.Automobile
    request.requestsAlternateRoutes = true

    directions = MKDirections(request: request)

    directions.calculateDirectionsWithCompletionHandler { (response:MKDirectionsResponse?, error:NSError?) -> Void in

        if error == nil {
            self.directionsResponse = response!
            self.route = self.directionsResponse.routes[0] as! MKRoute
            map.addOverlay(route.polyline, level: MKOverlayLevel.AboveRoads)
        } else {
            println(error)
        }
    } 
}

The route is not showing up.

MantisShrimp
  • 171
  • 2
  • 12
  • It looks to me like the problem is your geocoding. There is no reason for that. You can create your place marks, and thus your map items, directly from coordinates. There's no reason to convert the coordinates to addresses. And the code you've posted isn't waiting for the async geocoding call to finish, so that is likely causing your latest problem. – Duncan C May 13 '15 at 23:48
  • @DuncanC You have me _so_ close, Duncan. If you would please read over it once more as well as the resulting error, I would immensely appreciate it. – MantisShrimp May 14 '15 at 00:06
  • 1
    What line crashes now? – Duncan C May 14 '15 at 00:18
  • @DuncanC map.addOverlay(route.polyline, level: MKOverlayLevel.AboveRoads). The last line of code. It unexpectedly finds nil while unwrapping an Optional value. Does this mean that it failed and I didn't actually successfully find a route? – MantisShrimp May 14 '15 at 00:24
  • No, your code is wrong again. You have to put the code to deal with the route inside the completion block of the call to directions.calculateDirectionsWithCompletionHandler. – Duncan C May 14 '15 at 00:51
  • Remember, with async functions, they return immediately and the next line of code executes, long before the network request is even sent, much less before the server has a chance to receive, process, and return the directions request over the internet. – Duncan C May 14 '15 at 00:53
  • Okay, putting that last line inside the calculateDirectionsWithCompletionsHandler yields zero error messages, but a route does not display. I am assuming it is not as simple as adding that line in. – MantisShrimp May 14 '15 at 01:00
  • At that point you're on your own. The code I wrote generates turn-by-turn directions. I didn't try to add poly-lines to the map. – Duncan C May 14 '15 at 01:21
  • @DuncanC Yeah, totally understand. Thank you for ALL of your suggestions and assistance, and have a fantastic night. – MantisShrimp May 14 '15 at 01:25

2 Answers2

3

I'm thinking it's because latitudes range from -90 to 90, and 120 is out of range. Thus you're specifying invalid data.

The first number, latitude, ranges from -90 to +90. The second number ranges from -180 to +180.

Duncan C
  • 128,072
  • 22
  • 173
  • 272
  • Hmm, this sounded promising, but I changed the source coordinates to somewhere near my home, and the destination coordinates to somewhere near that, but I get the same error. Thanks for the attempt, though. – MantisShrimp May 13 '15 at 20:37
  • Also change all your instance variables to not be implicitly unwrapped optionals (get rid of the explanation points.) That will force you to unwrap them when you create objects and store them, which will trigger an immediate crash, or check for nil before assigning. – Duncan C May 13 '15 at 20:42
  • Post your new code. As it happens I wrote code similar to yours a couple of weeks ago that generates driving directions based on a couple of lat/longs. It's code for a client so I can't post it however. – Duncan C May 13 '15 at 20:43
  • I have revised my code. It now doesn't crash, but returns the nasty error below my code above. – MantisShrimp May 13 '15 at 23:39
2

it maybe can solve your problem

How to use MKPolylineView in Swift

First, you must create an MKPolylineRenderer, bacause MKPolylineView has been deprecated since iOS 7. MKPolylineView is not supported in Swift.

Second, you must create and return an MKPolylineRenderer in the rendererForOverlay delegate method

Then implement the rendererForOverlay method:

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

    return nil
}

Make sure the map view's delegate is set otherwise the delegate method won't get called and the overlay will not appear, remember connect the delegate outlet or set it in your code.

self.map.delegate = self
Community
  • 1
  • 1