Mkpolyline is not being drawn - ios

The route is clearly being made but the polyline is not drawn. I am able to find the total distance, and have verified that the coordinates we are using are not (0,0). Is there something wrong with the delegate, since it seems that both the addOverlay and addAnnotation (called in a custom method shown below called createAndAddAnnotationForCoordinate) methods are not working?
-(void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
//generates map view
mapView = [[MKMapView alloc]initWithFrame:CGRectMake(0, 0, self.view.bounds.size.width,
self.view.bounds.size.height)];
//setting delegate to self is not fixing the error
mapView.delegate = self;
mapView.mapType = MKMapTypeStandard;
//converting CLLocationCoordinate2D to MKPlacemark
MKPlacemark *startPlacemark = [[MKPlacemark alloc]
initWithCoordinate: addressOneCoords addressDictionary:
[NSDictionary dictionaryWithObjectsAndKeys:nil]];
MKPlacemark *endPlacemark = [[MKPlacemark alloc]initWithCoordinate: addressTwoCoords addressDictionary:[NSDictionary dictionaryWithObjectsAndKeys:nil]];
//converting MKPlacemark to MKMapItem
MKMapItem *start = [[MKMapItem alloc ]initWithPlacemark:startPlacemark];
MKMapItem *end = [[MKMapItem alloc]initWithPlacemark:endPlacemark];
MKDirectionsRequest *request = [MKDirectionsRequest new];
[request setSource:start];
[request setDestination:end];
[request setTransportType:MKDirectionsTransportTypeAutomobile];
request.requestsAlternateRoutes = YES;
//Just to check if the coordinates were transferred successfully between view controllers and they did transfer successfully
NSLog(#"address one lat is %f",addressOneCoords.latitude);
NSLog(#"address one lon is %f",addressOneCoords.longitude);
NSLog(#"address two lat is %f",addressTwoCoords.latitude);
NSLog(#"address two lon is %f",addressTwoCoords.longitude);
MKDirections *directions = [[MKDirections alloc]initWithRequest:request];
[directions calculateDirectionsWithCompletionHandler:^(MKDirectionsResponse response, NSError error){
//if the route can't be created
if(error){
UIAlertView *alert = [[UIAlertView alloc]initWithTitle:#"Unable to create route" message:#"Go back to check if addresses are valid" delegate:nil cancelButtonTitle:#"Ok"otherButtonTitles:nil];
[alert show];
}
else{
[mapView removeOverlays:self.mapView.overlays];
MKRoute *mainRoute = [response.routes firstObject];
routeLine = mainRoute.polyline;
if(routeLine){
[self.mapView removeOverlay:routeLine];
}
//the addOverlay method is not drawing the polyline
[self.mapView addOverlay: routeLine level:MKOverlayLevelAboveRoads];
//proof that route is being created successfully
NSLog(#"Total distance is %f", mainRoute.distance);
MKMapPoint middlePoint = mainRoute.polyline.points[mainRoute.polyline.pointCount/2];
//also, the addannotation method is not being called either it seems like
[self createAndAddAnnotationForCoordinate:MKCoordinateForMapPoint(middlePoint)];
}
}];
}
Our createAndAddAnnotationForCoordinate method,
-(void) createAndAddAnnotationForCoordinate : (CLLocationCoordinate2D)coordinate{
MKPointAnnotation* annotation = [[MKPointAnnotation alloc]init];
annotation.coordinate = coordinate;
annotation.title = #"Point";
annotation.subtitle = #"subtitle";
[mapView addAnnotation:annotation];
}
Our overridden mapviewdelegate method,
-(MKOverlayRenderer )mapView:(MKMapView )mapView rendererForOverlay:(id<MKOverlay>)overlay{
if([overlay isKindOfClass:[MKPolyline class]]){
MKPolylineRenderer *renderer = [[MKPolylineRenderer alloc]initWithOverlay:overlay];
renderer.strokeColor = [UIColor redColor];
renderer.lineWidth = 10.0f;
return renderer;
}
else
return nil;
}
The output if address one was somewhere in NJ and address two was somewhere in CA:
address one lat is 40.902599
address one lon is -74.407097
address two lat is 34.054435
address two lon is -118.253393
Total distance is 4459771.000000

Ron, I'm suspecting your polyline (mainRoute.polyline) - your rendererForOverlay looks almost exactly like one I am using successfully. Barring the very basic mistakes like not setting the MKMapView delegate, or setting it to some other object, I would be almost sure the polyline you add to the map does not contain valid CLLocationCoordinate2D structs.
In Swift, creation goes like
var coordinatePtr = UnsafeMutablePointer<CLLocationCoordinate2D>(track2DCoordinates)
let trackPolygon = MKPolyline(coordinates: coordinatePtr, count: track2DCoordinates.count)
mapView.removeOverlays(mapView.overlays)
mapView.addOverlay(trackPolygon)
Start by verifying that you really have a valid MKPolyline.
I'm also not sure about your middlePoint calculation.
MKMapPoint middlePoint = mainRoute.polyline.points[mainRoute.polyline.pointCount/2];
This kind of thing probably works right now but in Swift you need to be a lot more careful of the data types used as index. What if you have an odd number of points, or zero?

Related

Make the map go direct to the place I want by name

I got 2 ViewControllers:
In the first I got TableView with name of countries.
When I click on one of the countries's cell it moves to the second ViewControl with performSegueWithIdentifier & saves the cell's name (for example: "France") in a variable outside the ViewController. in the seconds ViewController I got a MapKit View. I don't know how to make the map to go to "France".
Any suggestions?
- (void)performSearchWithLocationName:(NSString*)CountryName{
//#define METERS_PER_MILE 1.344
MKLocalSearchRequest *request =
[[MKLocalSearchRequest alloc] init];
request.naturalLanguageQuery = CountryName;
request.region = _mapView.region;
MKLocalSearch *search =
[[MKLocalSearch alloc]initWithRequest:request];
[search startWithCompletionHandler:^(MKLocalSearchResponse
*response, NSError *error) {
if (response.mapItems.count == 0){
NSLog(#"No Matches");
}
else{
for (MKMapItem *item in response.mapItems)
{
MKPointAnnotation *annotation =
[[MKPointAnnotation alloc]init];
annotation.coordinate = item.placemark.coordinate;
annotation.title = item.name;
NSLog(#"%#", annotation.title);
[_mapView addAnnotation:annotation];
MKCoordinateRegion region;
region = MKCoordinateRegionMakeWithDistance(annotation.coordinate,0.3*METERS_PER_MILE, 0.3*METERS_PER_MILE);
[self.mapView setRegion:[self.mapView regionThatFits:region] animated:YES];
}
}
}];
}
//You can implement map delegate
-(void)mapView:(MKMapView *)mapView didSelectAnnotationView:(MKAnnotationView *)view{
NSLog(#"selected annotation and it's coordinates =>%f & %f",[view.annotation coordinate].latitude,[view.annotation coordinate].longitude);
}
Setp 1 : Getting latitude & longitude using location name
Setp 2 : Create the CLLocationCoordinate2D Using that latitude and longitude
Setp 3 : Create the MKCoordinateRegion
Setp 2 : Change the Mapview Region

iOS : How to plot polyline on existing route?

I plot the route between source and destination using the following piece of code. After plotting the route sucessfully, I get a list of coordinates where I wanted to plot another route on top of existing one with different color.
The use case I am trying to code is, we set source and destination, plot the dotted route once, then plot the actual line based on the list of locations
From the code, I can see the dotted line but can not see the another line overlapping on it.
My code is:
-(void) getDirection {
CLLocationCoordinate2D sourceCoords = CLLocationCoordinate2DMake(42.130655, -71.041158);
MKPlacemark *sourcePlacemark = [[MKPlacemark alloc] initWithCoordinate:sourceCoords addressDictionary:nil];
MKMapItem *source = [[MKMapItem alloc] initWithPlacemark:sourcePlacemark];
// Make the destination location
CLLocationCoordinate2D destinationCoords = CLLocationCoordinate2DMake(42.128121, -70.936151); MKPlacemark *destinationPlacemark = [[MKPlacemark alloc] initWithCoordinate:destinationCoords addressDictionary:nil];
MKMapItem *destination = [[MKMapItem alloc] initWithPlacemark:destinationPlacemark];
MKDirectionsRequest *directionsRequest = [MKDirectionsRequest new];
[directionsRequest setSource:source];
[directionsRequest setDestination:destination];
[self sourceMarker:sourcePlacemark];
[self destinationMarker:destinationPlacemark];
//
//routeCoords
NSInteger nuberOfStesps = routeCoords.count;
CLLocationCoordinate2D *pointsCoordinate = (CLLocationCoordinate2D *)malloc(sizeof(CLLocationCoordinate2D) * nuberOfStesps);
for (NSInteger index=0; index < nuberOfStesps; index++) {
CLLocation *location = [routeCoords objectAtIndex:index];
coordinate2 = location.coordinate;
//NSLog(#"Location Latitide : %f", coordinate2.latitude);
//NSLog(#"Location Longitude : %f", coordinate2.longitude);
pointsCoordinate[index] = CLLocationCoordinate2DMake(coordinate2.latitude, coordinate2.longitude);
}
routeLine = [MKPolyline polylineWithCoordinates:pointsCoordinate count:nuberOfStesps];
// free(pointsCoordinate);
routeLine.title = #"New points";
[self.mapView addOverlay:routeLine];
//
MKDirections *directions = [[MKDirections alloc] initWithRequest:directionsRequest];
[directions calculateDirectionsWithCompletionHandler:^(MKDirectionsResponse *response, NSError *error) {
if (error) {
NSLog(#"There was an error getting your directions");
return;
}else {
[self showRoute:response];
}
}];
}
- (void) showRoute:(MKDirectionsResponse * ) response {
_currentRoute = [response.routes firstObject];
[self.mapView setVisibleMapRect:_currentRoute.polyline.boundingMapRect animated:NO];
[self.mapView removeOverlays:self.mapView.overlays];
[self.mapView addOverlay:_currentRoute.polyline level:MKOverlayLevelAboveRoads];
}
- (MKOverlayRenderer *)mapView:(MKMapView *)mapView rendererForOverlay:(id<MKOverlay>)overlay {
if ([overlay isKindOfClass:[MKPolyline class]]) {
MKPolylineRenderer *renderer = [[MKPolylineRenderer alloc] initWithOverlay:overlay];
[renderer setStrokeColor:[UIColor redColor]];
[renderer setLineWidth:4.0];
[renderer setLineDashPattern:#[#2, #5]];
[renderer setStrokeColor:[UIColor redColor]];
return renderer;
}
return nil;
}
I followed this thread iOS Maps draw route (line) between several points (geopoints)
But it did not work for me. What am I doing wrong?

How to show the path between two location in mapkit

I want to show two location's distance in mapkit, I have tried my process its not showing the distance between the location. tried below codes its not working, can any one help me in coding.
MKMapView * mapView = [[MKMapView alloc]initWithFrame:CGRectMake(0, 294, 320, 122)];
mapView.showsUserLocation = YES;
mapView.delegate = self;
MKDirectionsRequest *request = [[MKDirectionsRequest alloc] init];
MKPlacemark *placemark1 = [[MKPlacemark alloc] initWithCoordinate:CLLocationCoordinate2DMake(9.9176458, 78.1228237) addressDictionary:nil];
[request setSource:[[MKMapItem alloc] initWithPlacemark:placemark1]];
MKPlacemark *placemark = [[MKPlacemark alloc] initWithCoordinate:CLLocationCoordinate2DMake(13.0475604, 80.2089535) addressDictionary:nil];
request.destination = [[MKMapItem alloc] initWithPlacemark:placemark];
// [request setDestination:myMapItem];
[request setTransportType:MKDirectionsTransportTypeAutomobile]; // This can be limited to automobile and walking directions.
[request setRequestsAlternateRoutes:YES]; // Gives you several route options.
MKDirections *directions = [[MKDirections alloc] initWithRequest:request];
[directions calculateDirectionsWithCompletionHandler:^(MKDirectionsResponse *response, NSError *error) {
if (!error) {
for (MKRoute *route in [response routes]) {
[mapView addOverlay:[route polyline] level:MKOverlayLevelAboveRoads]; // Draws the route above roads, but below labels.
// You can also get turn-by-turn steps, distance, advisory notices, ETA, etc by accessing various route properties.
}
}
}];
[self.view addSubview:mapView];
- (MKOverlayRenderer *)mapView:(MKMapView *)mapView rendererForOverlay:(id < MKOverlay >)overlay
{
MKPolylineRenderer *renderer =
[[MKPolylineRenderer alloc] initWithOverlay:overlay];
renderer.strokeColor = [UIColor blueColor];
renderer.lineWidth = 5.0;
return renderer;
}
Please any help me .
If you check error in the -calculateDirectionsWithCompletionHandler:
[directions calculateDirectionsWithCompletionHandler:^(MKDirectionsResponse *response, NSError *error) {
if (!error) {
for (MKRoute *route in [response routes]) {
[mapView addOverlay:[route polyline] level:MKOverlayLevelAboveRoads]; // Draws the route above roads, but below labels.
// You can also get turn-by-turn steps, distance, advisory notices, ETA, etc by accessing various route properties.
}
}
else {
NSLog(#"%#", error);
}
}];
You will probably see
Error Domain=MKErrorDomain Code=5 "Directions Not Available" UserInfo=0x7fefe3bb5660 {NSLocalizedFailureReason=A route to the nearest road cannot be determined., MKErrorGEOError=-403, MKDirectionsErrorCode=6, NSLocalizedDescription=Directions Not Available}
Which means that it is not possible to calculate directions from given location.
Here is my code; I use CLLocationManager Delegate to compute distance between two location and MKMapView Delegate. here is my code, it may help you.
- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)location{
float distance = 0.0f;
NSMutableArray *locationArray = [[NSMutableArray alloc] init];
for (CLLocation *newLocation in location) {
[locationArray addObject:newLocation];
}
for (int i = 0 ; i < locationArray.count; i++) {
CLLocation *newLocation = [locationArray objectAtIndex:i];
distance += [newLocation distanceFromLocation:[locationArray objectAtIndex:i+1]];
}
NSLog(#"%f",distance);
CLLocationCoordinate2D cordinates [locationsArray.count];
MKPolyline *userLocationsPolyLine = [MKPolyline polylineWithCoordinates:cordinates count:locationsArray.count];
[userMapView setDelegate:self];
[userMapView addOverlay:userLocationsPolyLine];
}
- (MKOverlayRenderer *)mapView:(MKMapView *)mapView rendererForOverlay:(id<MKOverlay>)overlay{
if ([overlay isKindOfClass:[MKPolyline class]]){
MKPolylineRenderer *renderer = [[MKPolylineRenderer alloc] initWithPolyline:overlay];
renderer.strokeColor = [[UIColor blueColor] colorWithAlphaComponent:0.7];
renderer.lineWidth = 3;
return renderer;
}
return nil;
}

Map view shows my location and destination but doesn't show directions between them

When I initialize the map, it shows both my location and the destination (forward geocoded from a string) but it does not draw directions between them.
Here is my code :
#import "EventDetailMapViewController.h"
#interface EventDetailMapViewController ()
#property (nonatomic,strong) MKMapItem *destination;
#end
#implementation EventDetailMapViewController
CLPlacemark *thePlacemark;
MKRoute *routeDetails;
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view.
_mapView.showsUserLocation = YES;
self.navigationController.toolbarHidden = NO;
_mapView.delegate = self;
[self getRoute];
}
- (void)addAnnotation:(CLPlacemark *)placemark {
MKPointAnnotation *point = [[MKPointAnnotation alloc] init];
point.coordinate = CLLocationCoordinate2DMake(placemark.location.coordinate.latitude, placemark.location.coordinate.longitude);
point.title = [placemark.addressDictionary objectForKey:#"Street"];
point.subtitle = [placemark.addressDictionary objectForKey:#"City"];
[self.mapView addAnnotation:point];
}
-(void)showRoute:(MKDirectionsResponse *)response{
for (MKRoute *route in response.routes)
{
[_mapView
addOverlay:route.polyline level:MKOverlayLevelAboveRoads];
for (MKRouteStep *step in route.steps){
NSLog(#"%#",step.instructions);
}
}
}
-(MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id<MKAnnotation>)annotation {
// If it's the user location, just return nil.
if ([annotation isKindOfClass:[MKUserLocation class]])
return nil;
// Handle any custom annotations.
if ([annotation isKindOfClass:[MKPointAnnotation class]]) {
// Try to dequeue an existing pin view first.
MKPinAnnotationView *pinView = (MKPinAnnotationView*)[self.mapView dequeueReusableAnnotationViewWithIdentifier:#"CustomPinAnnotationView"];
if (!pinView)
{
// If an existing pin view was not available, create one.
pinView = [[MKPinAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:#"CustomPinAnnotationView"];
pinView.canShowCallout = YES;
} else {
pinView.annotation = annotation;
}
return pinView;
}
return nil;
}
-(void)getRoute {
[self getLocationFromString];
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];
}
}];
}
-(MKOverlayRenderer *)mapView:(MKMapView *)mapView rendererForOverlay:(id<MKOverlay>)overlay {
MKPolylineRenderer * routeLineRenderer = [[MKPolylineRenderer alloc] initWithPolyline:routeDetails.polyline];
routeLineRenderer.strokeColor = [UIColor redColor];
routeLineRenderer.lineWidth = 5;
return routeLineRenderer;
}
- (IBAction)changeMapType:(id)sender {
if (_mapView.mapType == MKMapTypeStandard)
_mapView.mapType = MKMapTypeSatellite;
else
_mapView.mapType = MKMapTypeStandard;
}
-(void)getLocationFromString{
CLGeocoder *geocoder = [[CLGeocoder alloc] init];
[geocoder geocodeAddressString:self.agendaEntry.address completionHandler:^(NSArray *placemarks, NSError *error) {
if (error) {
NSLog(#"%#", error);
} else {
thePlacemark = [placemarks lastObject];
float spanX = 1.00725;
float spanY = 1.00725;
MKCoordinateRegion region;
region.center.latitude = thePlacemark.location.coordinate.latitude;
region.center.longitude = thePlacemark.location.coordinate.longitude;
region.span = MKCoordinateSpanMake(spanX, spanY);
[self.mapView setRegion:region animated:YES];
[self addAnnotation:thePlacemark];
}
}];
}
#end
I'm relatively new to this so some of this is probably wrong. What I want is when I push a button (in another view) the "agendaEntry" gets passed on the segue. It contains a string with an address, this address gets forward-geocoded and the map displays directions/routes from the user's location to the address in that string.
I don't know how to make it show driving/walking directions or to make it show multiple routes. But for starters, it would be great if it would work.
The reason it doesn't draw directions is because the directions request is being made before the destination placemark (thePlacemark) is actually set by the geocodeAddressString completion handler block.
Note what the documentation says about geocodeAddressString:completionHandler::
This method submits the specified location data to the geocoding server asynchronously and returns.
So even though getLocationFromString (which calls geocodeAddressString) is called before the directions request, execution returns and continues in getRoute immediately after the geocodeAddressString is initiated (but not completed).
Therefore, thePlacemark is still nil when getRoute sets up directionsRequest and you're probably getting an error logged such as "directions not available".
To account for this, you can move the call to the directions-request-set-up code to inside the geocodeAddressString completion handler block (after the add-annotation code).
There are three things to change for this:
In viewDidLoad, do [self getLocationFromString]; instead of [self getRoute];.
In getRoute, remove the call to getLocationFromString.
In getLocationFromString, in the completion handler block, after [self addAnnotation:thePlacemark];, do [self getRoute];.
Regarding showing multiple routes, first you'll need to actually request alternate routes (default is NO):
directionsRequest.requestsAlternateRoutes = YES;
The other part of the problem you may have experienced is due to this line in rendererForOverlay:
MKPolylineRenderer * routeLineRenderer = [[MKPolylineRenderer alloc]
initWithPolyline:routeDetails.polyline];
It's creating a polyline renderer using an external instance variable instead of the overlay parameter provided to the delegate method. This basically means the delegate method will only work for that one specific overlay (routeDetails.polyline) and since there's no control over when exactly the delegate method will be called by the map view, you can't reliably set routeDetails.polyline from outside the delegate method to be sure it points to the right overlay. Each alternate route is a separate polyline and the map view will make separate calls to rendererForOverlay for each one.
Instead, create the polyline renderer using the overlay parameter (and first check if overlay is an MKPolyline):
-(MKOverlayRenderer *)mapView:(MKMapView *)mapView rendererForOverlay:(id<MKOverlay>)overlay {
if ([overlay isKindOfClass:[MKPolyline class]])
{
MKPolylineRenderer * routeLineRenderer =
[[MKPolylineRenderer alloc] initWithPolyline:overlay];
routeLineRenderer.strokeColor = [UIColor redColor];
routeLineRenderer.lineWidth = 5;
return routeLineRenderer;
}
return nil;
}
Then in getRoute, instead of this:
routeDetails = response.routes.lastObject;
[self.mapView addOverlay:routeDetails.polyline];
call the showRoute method you already have which adds all the routes:
[self showRoute:response];

Getting Bad Access error with iOS Annotations and Arrays

The code below is for conducting search with MKLocalSearch and loading the results into an array.
This array gets passed to my [self.mapView addAnnotations:annotations] method. Everything works great until I try to dismiss this viewcontroller by tapping the back button (in my navigation bar for storyboards).
I get EXC_BAD_ACCESS(code=1, address=0x4). If I comment out the Show Pins section below the problem goes away (but of course I am now not loading my annotations).
Please help!
-(void)issueLocalSearchLookup:(NSString *)searchString usingPlacemarksArray:(NSArray *)placemarks {
self.coords = mapView.userLocation.coordinate;
// Set the size of the region we want to get search results for.
MKCoordinateSpan span = MKCoordinateSpanMake(0.001250, 0.001250);
MKCoordinateRegion region = MKCoordinateRegionMake(mapView.userLocation.coordinate, span);
[self.mapView setRegion:region animated:YES];
// Create the search request
self.localSearchRequest = [[MKLocalSearchRequest alloc] init];
self.localSearchRequest.region = region;
self.localSearchRequest.naturalLanguageQuery = searchString;
// Perform the search request...
self.localSearch = [[MKLocalSearch alloc] initWithRequest:self.localSearchRequest];
[self.localSearch startWithCompletionHandler:^(MKLocalSearchResponse *response, NSError *error) {
if(error){
NSLog(#"localSearch startWithCompletionHandlerFailed! Error: %#", error);
return;
} else {
// We are here because we have data!
for(MKMapItem *mapItem in response.mapItems){
// Show pins...
NSMutableArray *annotations = [NSMutableArray array];
Annotation *annotation = [[Annotation alloc] initWithCoordinate: mapItem.placemark.location.coordinate];
annotation.title = mapItem.name;
annotation.subtitle = mapItem.placemark.addressDictionary[(NSString *)kABPersonAddressStreetKey];
[mapView addAnnotation:annotation];
NSLog(#"Name for result: = %#", mapItem.name);
[self.mapView addAnnotations:annotations];
NSLog(#"Name for result: = %#", mapItem.name);
}
MKCoordinateSpan span = MKCoordinateSpanMake(0.01, 0.01);
MKCoordinateRegion region = MKCoordinateRegionMake(self.coords, span);
[self.mapView setRegion:region animated:YES];
}
}];
}
I had set my custom annotation to be a subclass of NKPlacemark....I needed to have it be a subclass of NSObject.

Resources