12

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
  • 6,278
  • 5
  • 44
  • 82
  • Do you want to draw the route as well as you want to drop annotation in the middle of the route – ipraba Jan 08 '14 at 12:44
  • I want to draw the route as well (which I am already doing). – Daniel Larsson Jan 08 '14 at 12:46
  • I don't get why do you need to add an annotation to an overlay. Do you know that overlays are also annotations? – Desdenova Jan 08 '14 at 12:55
  • Here at the bottom. https://developer.apple.com/library/ios/documentation/UserExperience/Conceptual/LocationAwarenessPG/AnnotatingMaps/AnnotatingMaps.html#//apple_ref/doc/uid/TP40009497-CH6-SW23 – Desdenova Jan 08 '14 at 12:57
  • Well, there are two separate overlays, where one is an annotation. First, there is the blue route overlay. Then there is also the blue and white annotations pointing to the routes. Where on the map would you suggest adding them? – Daniel Larsson Jan 08 '14 at 13:02
  • This question is similar to [this one](http://stackoverflow.com/questions/19821940/put-mkpointannotation-on-mkdirections-at-a-specific-distance). Although the solution you've used here is reasonable, there are cases where the annotation will appear very "off center" (which you may not be worried about). –  Jan 08 '14 at 14:34
  • Thanks for the link @Anna. I actually do not care that much about the annotation hitting exact center, so the solution developed with the help of iPrabu works great for me. – Daniel Larsson Jan 08 '14 at 14:37
  • @DanielLarsson You Save my day :) :* – V.D May 02 '15 at 11:47
  • @DanielLarsson awesome work man. can you also share your fitRegionToRoute method? – Muhammad_Awaab May 15 '15 at 12:48
  • How to display that "1h 14 min" text on line... can anyone help please? – Steve Gear Aug 02 '16 at 12:09

5 Answers5

8

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];

}
Daniel Larsson
  • 6,278
  • 5
  • 44
  • 82
ipraba
  • 16,485
  • 4
  • 59
  • 58
  • 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? – Daniel Larsson Jan 08 '14 at 13:15
  • 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. – ipraba Jan 08 '14 at 13:21
  • I will try it out and get back to you when I know! – Daniel Larsson Jan 08 '14 at 13:23
  • Awesome. Waiting for the result – ipraba Jan 08 '14 at 13:24
  • 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. – Daniel Larsson Jan 08 '14 at 13:35
  • 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 – ipraba Jan 08 '14 at 13:49
  • 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 Jan 08 '14 at 13:56
  • 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 Jan 08 '14 at 14:05
  • 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". –  Jan 08 '14 at 14:26
  • 1
    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". –  Jan 08 '14 at 14:31
  • 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? – Craig Jan 08 '14 at 20:03
  • But What is the _destination? – Siten Feb 26 '14 at 11:10
1

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)
    }
}
Nico
  • 11
  • 1
0

Perhaps not the most efficient way, but should still likely be quick, would be to calculate the midpoint of your start and end points (i.e., average their lats and longs). Then, iterate through your polyline points checking the distance from each to that midpoint. Take the closest match.

Even if the line is wildly curved, the midpoint will be directly between the ends. Some point on the wild curve is the closest to that external midpoint and is likely a good place to put the annotation.

incanus
  • 5,100
  • 1
  • 13
  • 20
0

Once you have an MKRoute, the approximate centre-point can be found using:

route.polyline.coordinate

This returns a CLLocationCoordinate2D that you can use to centre your annotation.

Appreciate this is an old question but I'd been searching for something like this for a while and ended up calculating the centre manually. Turns out it's very straightforward since iOS 8.

CPourrier
  • 33
  • 3
-2

I suggest you see this , Integrated with routing.

Jagat Dave
  • 1,643
  • 3
  • 23
  • 30
Zeeshan
  • 4,194
  • 28
  • 32
  • 1
    Thank you, looks like a great framework. A bit too much for this task though, I have already implemented many of those features myself. – Daniel Larsson Jan 08 '14 at 14:11