Measuring distance in meters from a drawn route in MKMapView - ios

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

Related

Apple CLGeocoder,MKLocalSearch result differ from device map result

In my application search with the location string, it ll return the 5 result, but that not match with default device map app.
my code 1 : CLGeocoder
CLGeocoder *geocoder = [[CLGeocoder alloc] init];
[geocoder geocodeAddressString:searchKey completionHandler:^(NSArray* placemarks, NSError* error){
dispatch_async(dispatch_get_main_queue(), ^{
if (placemarks.count > 0) {
searchLocations = placemarks;
} else {
searchLocations = nil;
}
[searchTableView reloadData];
for (CLPlacemark *placemark in placemarks) {
NSLog(#"place dic %#",placemark.addressDictionary);
}
});
}];
Code 2 : MKLocalSearch
CLLocation *userLoc = (CLLocation *)[[MYLocationManager defaultManager] currentLocation];
MKLocalSearchRequest *request = [[MKLocalSearchRequest alloc] init];
request.region = MKCoordinateRegionMakeWithDistance(userLoc.coordinate, 100000, 100000);
request.naturalLanguageQuery = searchKey;
MKLocalSearch *search = [[MKLocalSearch alloc] initWithRequest:request];
[search startWithCompletionHandler:^(MKLocalSearchResponse * _Nullable response, NSError * _Nullable error) {
NSMutableArray *locs = [[NSMutableArray alloc]init];
for (MKMapItem *placeM in response.mapItems) {
[locs addObject:placeM.placemark];
}
if (locs.count > 0) {
searchLocations = locs;
} else {
searchLocations = nil;
}
dispatch_async(dispatch_get_main_queue(), ^{
[searchTableView reloadData];
});
}];
Both are return a same result
Device map app result :
Device map result differ from coding geo results. please help to solve this.
and my question is what type of search methodology use the default map application?
and how to get same result in coding ?
Please refer this answer. It describe all the steps needed to replicate the above result. I've tested this on simulator.
The result of the query depends on the region you're searching in.
That's the 'Local' in MKLocalSearch.
The location in your code example is the user's location in a region with a side length on 10km.
The region where the built in map searches should be equal to or dependant on the region you're showing in the App at this very moment.
The region in Apples App is probably very different from the explicit region in your App.

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

Annotation along route in MapKit

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.

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.

What is proper way to get ETA (estimated time arrival) from any location to my current location

Would like to know what is proper way to get ETA (estimated time arrival) from any location to my current location, in consideration the following situation:
a. ex. - I got from another device its location (lon/lat) and want to when the other person will pick me up... In this case what web-service can I use to get this info for the user?
Does mapkit provides that kind of option?
b. In case it will be done on the server-side and I'll just send my user location, what are the tools my server-side programmer can use to get ETA info in order to send it back to my user?
Thank you all in advance.
I saw this: Is there any way to determine the driving time between two locations using Apple's Maps API?
- the problem, as I found in other places, (to my understanding)is that google api requires use of Google Maps app that isn't installed on every iOS user now.
I know this post is a bit old but in case someone is looking at the answer since iOS 7 Apple provide an API in MapKit in order to calculate all these info.
Here is a snippet of how to use this API
MKDirectionsRequest *request = [[MKDirectionsRequest alloc] init];
[request setSource:[MKMapItem mapItemForCurrentLocation]];
[request setDestination:destination];
[request setTransportType:MKDirectionsTransportTypeAutomobile];
[request setRequestsAlternateRoutes:NO];
MKDirections *directions = [[MKDirections alloc] initWithRequest:request];
[directions calculateDirectionsWithCompletionHandler:^(MKDirectionsResponse *response, NSError *error) {
if ( ! error && [response routes] > 0) {
MKRoute *route = [[response routes] objectAtIndex:0];
//route.distance = The distance
//route.expectedTravelTime = The ETA
}
}];
This worked for me using route.distance from M to the K's answer
I was modifying code from this tutorial map directions tutorial
(IBAction)routeButtonPressed:(UIBarButtonItem *)sender {
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];
[directions calculateDirectionsWithCompletionHandler:^(MKDirectionsResponse *response, NSError *error) {
if (error) {
NSLog(#"Error %#", error.description);
} else {
routeDetails = response.routes.lastObject;
[self.mapView addOverlay:routeDetails.polyline];
self.destinationLabel.text = [placemark.addressDictionary objectForKey:#"Street"];
self.distanceLabel.text = [NSString stringWithFormat:#"%0.1f Miles", routeDetails.distance/1609.344];
self.etaLabel.text = [NSString stringWithFormat:#"%0.1f minutes",routeDetails.expectedTravelTime/60];
//self.transportLabel.text = [NSString stringWithFormat:#"%u" ,routeDetails.transportType];
self.allSteps = #"";
for (int i = 0; i < routeDetails.steps.count; i++) {
MKRouteStep *step = [routeDetails.steps objectAtIndex:i];
NSString *newStep = step.instructions;
self.allSteps = [self.allSteps stringByAppendingString:newStep];
self.allSteps = [self.allSteps stringByAppendingString:#"\n\n"];
self.steps.text = self.allSteps;
}
}
}];
}

Resources