Annotation along route in MapKit

10,384

Solution 1

Questioner's edit:

Finally made it work with the help of this answer. I added this to the code below, where it says Here do the magic:

MKMapPoint middlePoint = route.polyline.points[route.polyline.pointCount/2];
[self createAndAddAnnotationForCoordinate:MKCoordinateForMapPoint(middlePoint)];

Original answer:

I don't know whether this will work or not. Just my idea on your question.

I guess you would have created the routes as following (Check my inline comments)

MKDirectionsRequest *request = 
       [[MKDirectionsRequest alloc] init];
request.source = [MKMapItem mapItemForCurrentLocation];
request.destination = _destination;
request.requestsAlternateRoutes = NO;
MKDirections *directions = 
       [[MKDirections alloc] initWithRequest:request];

    [directions calculateDirectionsWithCompletionHandler:
 ^(MKDirectionsResponse *response, NSError *error) {
     if (error) {
         // Handle error
     } else {
         for (MKRoute *route in response.routes)
         {
             [_routeMap addOverlay:route.polyline level:MKOverlayLevelAboveRoads];
             //Here do the magic
             //MKPolyline confronts to MKOverlay so you can get the coordinate like 
             //route.polyline.coordinate once you get the coordinate then you can build
             //a annotation. A annotation is nothing but a coordinate with some title.
             //According to MKOverlay coordinate property it justs gives you the 
             //center point of the overlay area
             [self createAndAddAnnotationForCoordinate:route.polyline.coordinate]
         }
     }
 }];

Adding Annotation

-(void) createAndAddAnnotationForCoordinate : (CLLocationCoordinate2D) coordinate{
    MyAnnotation* annotation= [[MyAnnotation alloc] init];
    annotation.coordinate = coordinate;

    annotation.title = @"Any Title";
    annotation.subtitle = @"Any Subtitle";

   [yourMap addAnnotation: annotation];

}

Solution 2

If you want to know the middle for swift you can use the following code :

MKCoordinateForMapPoint(route.polyline.points()[route.polyline.pointCount/2])

Exemple of use :

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

     if error == nil {
        self.showRoute(response!)
     }
     else{
        print("some error")    
     }
})

func showRoute(response:MKDirectionsResponse){
    for route in response.routes {
        self.map.addOverlay(route.polyline, level: MKOverlayLevel.AboveRoads)
        self.map.setCenterCoordinate(route.polyline.coordinate, animated: true)
        self.map.setRegion(MKCoordinateRegionMakeWithDistance(route.polyline.coordinate, route.distance*0.75, route.distance*0.75), animated: true)

        let routeAnnotation = MKPointAnnotation()
        routeAnnotation.coordinate = MKCoordinateForMapPoint(route.polyline.points()[route.polyline.pointCount/2])
        map.addAnnotation(routeAnnotation)
    }
}
Share:
10,384
Daniel Larsson
Author by

Daniel Larsson

Updated on June 04, 2022

Comments

  • Daniel Larsson
    Daniel Larsson almost 2 years

    I'm using MapKit to display directions between locations, and I'm looking for a way to add an annotation that works similarly to the route annotation in the Apple Maps app, where annotations are showing each route's travel time (as shown in the image below). I am already drawing the directions correctly, the problem at hand is how to calculate a pair of coordinates along the route. That is, where to drop the annotation.

    I thought about somehow using the MKDirection (which contains complete directions, step by step) but I am not sure how I would generate a pair of coordinates that are somewhere in the middle of the route.

    I have not been able to find any kind of support for this in the MapKit documentation. Any ideas?

    enter image description here

    This is how I generate the route and display it.

    - (void)generateRoute {
        MKDirectionsRequest *request = [[MKDirectionsRequest alloc] init];
        request.source = [MKMapItem mapItemForCurrentLocation];
        request.destination = self.destinationMapItem;
        MKDirections *directions = [[MKDirections alloc] initWithRequest:request];
    
        [directions calculateDirectionsWithCompletionHandler:
         ^(MKDirectionsResponse *response, NSError *error) {
             if (error) {
                 // Handle Error
             } else {
                 [self showRoute:response];
             }
         }];
    }
    
    - (void)showRoute:(MKDirectionsResponse *)response {
        [self.mapView removeOverlays:self.mapView.overlays];
        for (MKRoute *route in response.routes)
        {
            [self.mapView addOverlay:route.polyline level:MKOverlayLevelAboveRoads];
        }
        [self fitRegionToRoute];
    }
    
    - (MKOverlayRenderer *)mapView:(MKMapView *)mapView rendererForOverlay:(id < MKOverlay >)overlay
    {
        MKPolylineRenderer *renderer = [[MKPolylineRenderer alloc] initWithOverlay:overlay];
        renderer.strokeColor = [UIColor blueColor];
        renderer.alpha = 0.7;
        renderer.lineWidth = 4.0;
    
        return renderer;
    }
    
  • Daniel Larsson
    Daniel Larsson over 10 years
    I like your idea, but this would only look good when the route is a straight line. Consider a route that goes in a circle, wouldn't the annotation end up in the middle of the circle instead of on the route?
  • ipraba
    ipraba over 10 years
    If we are considering a MKCircle your question would be correct but as we are considering a MKPolyine i guess coordinate of MKPolyline should be on the line i guess. My suggestion is worth a try. I m not sure of the output.
  • Daniel Larsson
    Daniel Larsson over 10 years
    I will try it out and get back to you when I know!
  • ipraba
    ipraba over 10 years
    Awesome. Waiting for the result
  • Daniel Larsson
    Daniel Larsson over 10 years
    Tried it out, the annotation ends up in between the two locations. Therefore, If the route has the form of an L, the annotation will be placed right between the start and finish, a long way from the actual route. Great suggestion though.
  • ipraba
    ipraba over 10 years
    Ok. Now try another MKOverlay property boundingMapRect. This will give you MapRect. MapRect contains MKMapPoints and MKMapPoint can be converted into coordinates using MKCoordinateForMapPoint. So if you are able to retrieve any starting point of a overlay that can be your annotation. Check for all possiblities of getting a coordinate from route.polyline
  • Daniel Larsson
    Daniel Larsson over 10 years
    Yeah that is what I am trying out right now. Also trying to use the points that are in polyline.points. Will let you know when I have tried it!
  • Daniel Larsson
    Daniel Larsson over 10 years
    It's working! What I did was to get the middle point through MKMapPoint middlePoint = route.polyline.points[route.polyline.pointCount/2]; and then [self createAndAddAnnotationForCoordinate:MKCoordinateForMapPoint(‌​middlePoint)];. Put that in your answer and I will accept it!
  • Daniel Larsson
    Daniel Larsson over 10 years
    Thank you, looks like a great framework. A bit too much for this task though, I have already implemented many of those features myself.
  • Admin
    Admin over 10 years
    As @daniellarsson already noted, the coordinate property of an overlay does not give a point on the line (it's just the geometric center of the rectangle that bounds the overlay) so unless the overlay is a single straight line, it's not the "middle of the line".
  • Admin
    Admin over 10 years
    Additionally, the solution of using the coordinate from the midpoint of the array of points in the polyline is reasonable only when the line segments in the polyline are about equal in length. Eg. If you have a polyline with 3 points (2 line segments) and the first line segment is much shorter than the second line segment, using the coordinate at the midpoint of the array will not put an annotation "midway along the polyline".
  • Craig
    Craig over 10 years
    An example of Anna's point could be that it takes 10 turns to get to the motorway from your house but on one to get off the motorway to the store, the midpoint of the direction array is much closer to the start than the end. To counteract that you could find the center point of the bounding rect as you originally did, and then cycle through the end points and center points of the individual directions to find the closest one to the center. It'll be near to the geographical center of the route and on the line. It'll be tripped up by horseshoe shaped routes, but who does that anyway?
  • Siten
    Siten over 10 years
    But What is the _destination?