Annotation along route in MapKit - ios

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?
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;
}

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

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)
}
}

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.

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.

I suggest you see this , Integrated with routing.

Related

GMSPanoramaService requestPanoramaNearCoordinate no result

Trying to request Google Panorama for a coordinate along with a specified radius.
Is there any way to get maximum supported radius for a coordinate ?
Just not sure what radius I can put into requestPanoramaNearCoordinate,
because even if I put 10000 (meters) it does not return any panorama (blank grey screen) where there are in fact panoramas within 50meters.
The requestPanoramaNearCoordinate:
[panoSvc requestPanoramaNearCoordinate:self.coordinate radius:1000 callback:^(GMSPanorama *panorama, NSError *error) {
if (error) {
NSLog(#"StreetView is not available at latlong = %f,%f", self.coordinate.latitude, self.coordinate.longitude);
return;
}
else{
GMSMarker *marker = [GMSMarker markerWithPosition:self.coordinate];
marker.panoramaView = panoView_;
[panoView_ moveNearCoordinate:self.coordinate];
}
}];
So the panoramaView should be moved to panorama.coordinate, not to where the destination coordinate is (which is self.coordinate and where the marker is),
see the code following the comment below.
[panoSvc requestPanoramaNearCoordinate:self.coordinate radius:1000 callback:^(GMSPanorama *panorama, NSError *error) {
if (error) {
NSLog(#"StreetView is not available at latlong = %f,%f", self.coordinate.latitude, self.coordinate.longitude);
return;
}
else{
GMSMarker *marker = [GMSMarker markerWithPosition:self.coordinate];
marker.panoramaView = panoView_;
//------------------
//so I should move panoramaView to panorama coordinate, not to where the destination coordinate is (which is self.coordinate and where the marker is)
//------------------
[panoView_ moveNearCoordinate:panorama.coordinate];
}
}];

Measuring distance in meters from a drawn route in MKMapView

How can I calculate the route distance between two coordinates in MKMapView? I'm not asking for the straight line distance but the distance for a route with turns.
I'm assuming you're using an MKDirectionsRequest to get a MKDirectionsResponse from which you're getting your route. For example:
MKDirectionsRequest *request = [[MKDirectionsRequest alloc] init];
// source and destination are the relevant MKMapItem's
request.source = source;
request.destination = destination;
// Specify the transportation type
request.transportType = MKDirectionsTransportTypeAutomobile;
// If you're open to getting more than one route, requestsAlternateRoutes = YES; else requestsAlternateRoutes = NO;
request.requestsAlternateRoutes = YES;
MKDirections *directions = [[MKDirections alloc] initWithRequest:request];
[directions calculateDirectionsWithCompletionHandler:^(MKDirectionsResponse *response, NSError *error) {
if (!error) {
self.directionsResponse = response;
}
}];
Once you get the MKDirectionsResponse (in this case self.directionsResponse) and decide on a specific route index from that response, the CLLocationDistance of that route (measured in meters) can be found using:
MKRoute *route = self.directionsResponse.routes[currentRoute];
CLLocationDistance distance = route.distance;
And if you don't know which specific route you want to use -- ex. if you want to decide on a route based on the distance -- you can go through the directionsResponse.route array with a loop to get all the route distances.
Edit: Furthermore, if you want to find the distance in time (measured in seconds), you can do so using:
NSTimeInterval seconds = route.expectedTravelTime;
And in Swift:
let request:MKDirectionsRequest = MKDirectionsRequest()
// source and destination are the relevant MKMapItems
request.setSource(source)
request.setDestination(destination)
// Specify the transportation type
request.transportType = MKDirectionsTransportType.Automobile;
// If you're open to getting more than one route,
// requestsAlternateRoutes = true; else requestsAlternateRoutes = false;
request.requestsAlternateRoutes = true
let directions = MKDirections(request: request)
directions.calculateDirectionsWithCompletionHandler ({
(response: MKDirectionsResponse?, error: NSError?) in
if error == nil {
self.directionsResponse = response
}
})
To get the distance:
let route = directionsResponse.routes[currentRoute] as MKRoute
let distance = route.distance
To get the expected travel time:
let seconds = route.expectedTravelTime
iOS 7 has introduced direction based API known as MKDirections API this helps with root based direction data by which you can play in your application.Refer to below link for more documentation on MKDirection API
MKDirection API

objective-c google maps delegate method reverseGeocodeCoordinate method

I'm not sure if this is new with google maps but I think it looks like this version of Google Maps for iOS already has a reverse geocoding method...one which I can't really figure out how to use. In their documentation page, under section Camera Position, they have a function, but it doesn't look like the one that came with the SDK...or maybe there's something I don't understand here. Can someone help out? Here's the function that comes up in XCode:
- (void)reverseGeocodeCoordinate:(CLLocationCoordinate2D)coordinate
completionHandler:(GMSReverseGeocodeCallback)handler{}
How can I use that if I have my coordinates in an array? How do I get the address and country and all the possible results?
By the way, this is what they have...how does it compare:
- (void)mapView:(GMSMapView *)mapView
idleAtCameraPosition:(GMSCameraPosition *)cameraPosition {
id handler = ^(GMSReverseGeocodeResponse *response, NSError *error) {
if (error == nil) {
GMSReverseGeocodeResult *result = response.firstResult;
GMSMarker *marker = [GMSMarker markerWithPosition:cameraPosition.target];
marker.title = result.addressLine1;
marker.snippet = result.addressLine2;
marker.map = mapView;
}
};
[geocoder_ reverseGeocodeCoordinate:cameraPosition.target completionHandler:handler];
}
You can try this code
[[GMSGeocoder geocoder]reverseGeocodeCoordinate:CLLocationCoordinate2DMake(latitude, longitude)
completionHandler:^(GMSReverseGeocodeResponse * response, NSError *error){
for(GMSReverseGeocodeResult *result in response.results){
NSLog(#"addressLine1:%#", result.addressLine1);
NSLog(#"addressLine2:%#", result.addressLine2);
}
}];

mapItemForCurrentLocation does not find my current location

I am working on the mapkit in ios where I have to trace my route from my current location.
Now, I had assigned my current location i.e. latitude and longitude using the simulator(Debug->location->custom location) but when I try to trace my source I get my source location as nil and due to which I am not able to get the route.
[directionsRequest setSource:[MKMapItem mapItemForCurrentLocation]];
Can any one suggest the possible solution for my problem
Code
MKDirectionsRequest *directionsRequest = [[MKDirectionsRequest alloc] init];
MKPlacemark *placemark = [[MKPlacemark alloc] initWithPlacemark:thePlacemark];
[directionsRequest setSource:[MKMapItem mapItemForCurrentLocation]];
[directionsRequest setDestination:[[MKMapItem alloc] initWithPlacemark:placemark]];
directionsRequest.transportType = MKDirectionsTransportTypeAutomobile;
MKDirections *directions = [[MKDirections alloc] initWithRequest:directionsRequest];
If you are using MapView then there is one delegate method in which you will get your current location without using CLLocationManager.
-(void)viewDidLoad
{
mapView.delegate=self;
}
-(void)mapView:(MKMapView *)mapView didUpdateUserLocation:(MKUserLocation *)userLocation
{
`enter code here` //Here MKUserLocation will give you current Location and every time will method will call when the location is change...'
}
and if you want only user location then use
MKUserLocation *userlocation=mapView.userlocation
I think you are getting nil location because you are testing it in simulator please run it on real device
https://developer.apple.com/library/ios/documentation/MapKit/Reference/MKMapItem_class/ states mapItemForCurrentLocation does not contain coordinate data for privacy reasons, but here is a simple workaround if you are using a map view and showsUserLocation true:
MKUserLocation* userLocation = self.mapView.userLocation;
MKMapItem* mapItemForCurrentLocation;
if(userLocation.isUpdating && userLocation.isBeingUpdated){
mapItemForCurrentLocation = [[MKMapItem alloc] initWithPlacemark:[[MKPlacemark alloc] initWithCoordinate:userLocation.location.coordinate addressDictionary:nil]];
mapItemForCurrentLocation.name = #"Current Location";
}

Showing label next to MKPolyline [duplicate]

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?
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;
}
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];
}
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)
}
}
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.
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.
I suggest you see this , Integrated with routing.

Resources