How to get MKPolyline (Mapkit) using MBRoute (Mapbox) - ios

I am developing two apps similar to Ola/Uber, a driver app and the other one is a rider app. The driver app uses mapbox for navigation while the rider app uses simple mapkit and google APIs. My problem starts when the driver re-routes while undergoing a ride and the rider app has to trace the driver's new path also. Right now the logic applied is as following: in didRerouteAlongRoute delegate of mapbox the driver app informs the server that it has re-routed along this particular MBRoute route. The server in turn informs and passes this information to the rider app. Problem is that this MBRoute data type is not usable at the rider end as it uses mapkit and not mapbox and I have to convert this information somehow so that I can make the same new route as the driver app using MKPolyline at the rider end. Any help is appreciated.
This api is being used at the rider end for making the route polyline originally: https://maps.googleapis.com/maps/api/directions/json

Finally I managed a way to accomodate the requirements. It includes the following 3 steps:
At the driver app (using the mapbox framework), in the didRerouteAlongRoute delegate, create an array containing the new route's latitude/longitude dictionaries like this:
-(void)navigationViewController:(MBNavigationViewController*)navigationViewController didRerouteAlongRoute:(MBRoute*)route{
CLLocationCoordinate2D *routeCoordinates = malloc(route.coordinateCount *sizeof(CLLocationCoordinate2D));
[route getCoordinates:routeCoordinates];
NSMutableArray *routeArray = [NSMutableArray new];
for (NSValue *value in route.coordinates) {
CLLocationCoordinate2D coordinate;
[value getValue:&coordinate];
NSDictionary *coDic = #{#"latitude" : [NSNumber numberWithDouble: coordinate.latitude],
#"longitude": [NSNumber numberWithDouble: coordinate.longitude]};
[routeArray addObject:coDic];
}
}
Then send this new route's information to the server through an API after serialising this array (reRouteJSONString) as following:
NSError *error;
NSString *reRouteJSONString = #"";
NSData *reRouteJSONData = [NSJSONSerialization dataWithJSONObject: routeArray options:NSJSONWritingPrettyPrinted error:&error];
reRouteJSONString = [[NSString alloc] initWithData: reRouteJSONData encoding:NSUTF8StringEncoding] ;
And now at the rider app, manipulate this information as following and form your new route polyline:
-(void)makeReroutePolyline:(NSString*)serialisedString{
MKMapView *mapView;
mapView.delegate = self;
NSError *jsonError;
NSData *objectData = [serialisedString dataUsingEncoding:NSUTF8StringEncoding];
NSArray *json = [NSJSONSerialization JSONObjectWithData:objectData options:NSJSONReadingMutableContainers error:&jsonError];
CLLocationCoordinate2D coordinates[json.count];
for (NSInteger index = 0; index < json.count; index++) {
CLLocationCoordinate2D coordinate = { [[json objectAtIndex:index][#"latitude"] doubleValue], [[json objectAtIndex:index][#"longitude"] doubleValue] };
coordinates[index] = coordinate;
}
MKPolyline *routeLine;
routeLine = [MKPolyline polylineWithCoordinates:coordinates count:json.count];
[mapView addOverlay:routeLine];
[mapView setVisibleMapRect:[routeLine boundingMapRect] edgePadding:UIEdgeInsetsZero animated:YES];
}

Related

Google Maps decoded GMSPath incorrect iOS

So, I use the directions API in my app. However, when I decode the polyline from the response, it does not show up correctly. Here is my code:
//Construct request URL
NSString *urlString = [NSString stringWithFormat:
#"%#?origin=%f,%f&destination=%f,%f&sensor=true&key=%#",
#"https://maps.googleapis.com/maps/api/directions/json",
userMarker.position.latitude,
userMarker.position.longitude,
place.coordinate.latitude,
place.coordinate.longitude,
#"AIzaSyDrtHA-AMiVVylUPcp46_Vf1eZJJFBwRCY"];
NSURL *directionsURL = [NSURL URLWithString:urlString];
//Get directions in JSON format
dispatch_async(dispatch_get_main_queue(), ^{
NSData* data = [NSData dataWithContentsOfURL:directionsURL];
NSError* error;
if(data){
NSDictionary *json = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:&error];
//Parse JSON and plot route on map
NSDictionary *routes = [json objectForKey:#"routes"][0];
NSDictionary *route = [routes objectForKey:#"overview_polyline"];
NSString *overview_route = [route objectForKey:#"points"];
//Clear map from previous polylines
[self.mapView clear];
//Make polyline
GMSPath *path = [GMSPath pathFromEncodedPath:overview_route];
GMSPolyline *polyline = [GMSPolyline polylineWithPath:path];
polyline.strokeWidth = 4;
polyline.strokeColor = [UIColor darkGrayColor];
polyline.map = self.mapView;
});
And here is the polyline:
As you can,see it does not follow the road properly. However, to me it seems as if there are not enough points to have proper bends.
EDIT: This only happens 50% of the time, sometimes it shows up correctly, sometimes it does not.
What could I be doing wrong?
Going by this (which is using the js API instead of the json API that you're using):
Get a polyline from Google maps directions V3
It looks like you should use the lines in each of the steps of the legs in the response, instead of the overview_polyline which is probably meant to be just a rough approximate line.

Attempting to drop pins based on MKMap from values from array

As the question says I am trying to add pins to my map based on the coordinates returned by my php file. Said file returns the following results
[{"dogid":"1","latitude":"15.435786","longitude":"-21.318447"},{"dogid":"1","latitude":"14.00000","longitude":"-18.536711"}]
What I am doing (well I believe i am) is taking the values from the link and saving them to a string. Secondly, save that string value to an array. Then, I go thru this array and save out the latitude and longitude and assign it to CLLocationCordinate 2dcoord. After whch I expect both pins to be dropped on whatever location they received.
However, what occurs is: Upon running the program, when it arrives on this lin
for (NSDictionary *row in locations) {
the loop is not run to assign the values, and it jumps to the end. Oddly, a single pin is dropped on the map (thou location doesnt appear to be the values that it waas passed).
Would appreciate a little incite into the matter.
Thanks
- (void)viewDidAppear:(BOOL)animated
{
NSMutableArray *annotations = [[NSMutableArray alloc] init];
NSURL *myURL =[NSURL URLWithString:#"link.php"];
NSError *error=nil;
NSString *str=[NSString stringWithContentsOfURL:myURL encoding:NSUTF8StringEncoding error:&error];
CLLocationCoordinate2D coord;
NSArray *locations=[NSArray arrayWithContentsOfFile:str];
for (NSDictionary *row in locations) {
NSNumber *latitude = [row objectForKey:#"latitude"];
NSNumber *longitude = [row objectForKey:#"longitude"];
// NSString *title = [row objectForKey:#"title"];
//Create coordinates from the latitude and longitude values
coord.latitude = latitude.doubleValue;
coord.longitude = longitude.doubleValue;
}
MKPointAnnotation *pin = [[MKPointAnnotation alloc] init];
pin.coordinate = coord;
[self.mapView addAnnotation:pin];
}
It looks like you are trying to save api response to and Array.
Api always returns json string which is NSString.
You need to convert decode json string.
In your case
NSString *str=[NSString stringWithContentsOfURL:myURL encoding:NSUTF8StringEncoding error:&error];
you need to decode str with [NSJSONSerialization JSONObjectWithData:<#(NSData )#> options:<#(NSJSONReadingOptions)#> error:<#(NSError *)#>] which give you proper array of dictionary.
Hope it will help you

Google Maps - Make route line follow streets when map zoomed in

I'm getting the same issue as described in following SO questions:
(The route lines is not following the streets when I zoom in)
MapKit - Make route line follow streets when map zoomed in
and
Route drawing on Google Maps for iOS not following the street lines
But seems there are no any answer which solved mentioned issue.
I'm adding to points to the my GMSMapView map by following function:
-(void) addPointToMap:(CLLocationCoordinate2D) coordinate
{
CLLocationCoordinate2D position = CLLocationCoordinate2DMake(
coordinate.latitude,
coordinate.longitude);
GMSMarker *marker = [GMSMarker markerWithPosition:position];
marker.map = mapView_;
[waypoints_ addObject:marker];
NSString *positionString = [[NSString alloc] initWithFormat:#"%f,%f",
coordinate.latitude,coordinate.longitude];
[waypointStrings_ addObject:positionString];
if([waypoints_ count]>1){
NSString *sensor = #"false";
NSArray *parameters = [NSArray arrayWithObjects:sensor, waypointStrings_,
nil];
NSArray *keys = [NSArray arrayWithObjects:#"sensor", #"waypoints", nil];
NSDictionary *query = [NSDictionary dictionaryWithObjects:parameters
forKeys:keys];
MDDirectionService *mds=[[MDDirectionService alloc] init];
SEL selector = #selector(addDirections:);
[mds setDirectionsQuery:query
withSelector:selector
withDelegate:self];
}
}
and here are setDirectionsQuery function:
static NSString *kMDDirectionsURL = #"http://maps.googleapis.com/maps/api/directions/json?";
- (void)setDirectionsQuery:(NSDictionary *)query withSelector:(SEL)selector
withDelegate:(id)delegate{
NSArray *waypoints = [query objectForKey:#"waypoints"];
NSString *origin = [waypoints objectAtIndex:0];
int waypointCount = [waypoints count];
int destinationPos = waypointCount -1;
NSString *destination = [waypoints objectAtIndex:destinationPos];
NSString *sensor = [query objectForKey:#"sensor"];
NSMutableString *url =
[NSMutableString stringWithFormat:#"%#&origin=%#&destination=%#&sensor=%#",
kMDDirectionsURL,origin,destination, sensor];
if(waypointCount>2) {
[url appendString:#"&waypoints=optimize:true"];
int wpCount = waypointCount-2;
for(int i=1;i<wpCount;i++){
[url appendString: #"|"];
[url appendString:[waypoints objectAtIndex:i]];
}
}
url = [url
stringByAddingPercentEscapesUsingEncoding: NSASCIIStringEncoding];
_directionsURL = [NSURL URLWithString:url];
[self retrieveDirections:selector withDelegate:delegate];
}
Note: I have followed this Google tutorial and modified it a little bit:
https://www.youtube.com/watch?v=AdV7bCWuDYg
Thanks in advance, any help will be appreciated!
Finally I have found solution, Thanks to the WWJD's last edit in his question!
Route drawing on Google Maps for iOS not following the street lines
From the answer:
What I basically did before was that I was getting and working only with the information I'm receiving in the routes while if you check the JSON file you're receiving from Google Directions API, you'll see that you receive much more information in the and the . This is the information we need to produce the proper results and the right polyline.

Search on Google Map Sdk

I need to implement the map view in my app to locate the required place. I had tried with the SVGeocoder concept.
[SVGeocoder geocode:searchfield.text
completion:^(NSArray *placemarks, NSHTTPURLResponse *urlResponse, NSError *error) {
}
But suppose I am trying to search any restaurent then the result is nil.
I was looking on Google map sdk but don't know how to do search functionality on GMSCameraPosition class.
GMSCameraPosition *camera = [GMSCameraPosition cameraWithLatitude:latitude
longitude:longitude
zoom:5];
how to search with the address using google sdk.
Thanks in advance.
If I understood it correctly, you need the location co-ordinates from a address string. Its Forward geo-coding. You can take a look at Google's free api for this: Link1
You will need a API key from your google account to access this api and there is way to select a free or business plan depending on your number of requests.
You need to use a CLLocation object for getting co-ordinates from your address. I wrote a similar function. CLLocation* temp_location=[[CLLocation alloc]init];
temp_location=[GeoCoding findAddressCordinates:sourceAddressTxtField.text];
// Class GeoCoding to find Co-ordinates
#import <Foundation/Foundation.h>
#interface GeoCoding : NSObject {
}
+(CLLocation*)findAddressCordinates:(NSString*)addressString;
#end
#import "GeoCoding.h"
#import <CoreLocation/CLAvailability.h>
#implementation GeoCoding
+(CLLocation*)findAddressCordinates:(NSString*)addressString {
CLLocation *location;
NSString *url = [NSString stringWithFormat:#"http://maps.googleapis.com/maps/api/geocode/json?address=%#&sensor=true", addressString];
url = [url stringByReplacingOccurrencesOfString:#" " withString:#"%20"];
NSURL *wurl = [NSURL URLWithString:url];
NSData *data = [NSData dataWithContentsOfURL: wurl];
// Fail to get data from server
if (nil == data) {
NSLog(#"Error: Fail to get data");
}
else{
// Parse the json data
NSError *error;
NSDictionary *json = [NSJSONSerialization
JSONObjectWithData:data
options:kNilOptions
error:&error];
// Check status of result
NSString *resultStatus = [json valueForKey:#"status"];
// If responce is valid
if ( (nil == error) && [resultStatus isEqualToString:#"OK"] ) {
NSDictionary *locationDict=[json objectForKey:#"results"] ;
NSArray *temp_array=[locationDict valueForKey:#"geometry"];
NSArray *temp_array2=[temp_array valueForKey:#"location"];
NSEnumerator *enumerator = [temp_array2 objectEnumerator];
id object;
while ((object = [enumerator nextObject])) {
double latitude=[[object valueForKey:#"lat"] doubleValue];
double longitude=[[object valueForKey:#"lng"] doubleValue];
location=[[CLLocation alloc] initWithLatitude:latitude longitude:longitude];
NSLog(#"CLLocation lat is %f -------------& long %f",location.coordinate.latitude, location.coordinate.longitude);
}
}
}
return location;
}
#end
You can then use this co-ordinates in your Google Map to focus your camera position.

iOS : MapKit place an annotation relative to the map, and not the bounds of the screen

I am writing an application with a mapview inside.
I download coordinates for some annotations from my server via a HTTP request.
I am supposed to decode the JsonResponse and add the annotations to my map.
Excerpt from my JSON response
...{
"id": 1,
"lat": 20.34226,
"long": 19.988363
},..
I add my annotations within this method, which is called when the download of the above JSON is complete. (i have a custom annotation class.)
-(void)connectionDidFinishLoading:(NSURLConnection *)connection {
NSLog(#"connectionDidFinishLoading");
NSLog(#"Succeeded! Received %d bytes of data",[self.responseData length]);
// convert to JSON
NSError *myError = nil;
NSDictionary *res = [NSJSONSerialization JSONObjectWithData:self.responseData options:NSJSONReadingMutableLeaves error:&myError];
// show all values
for(NSDictionary *dict in res) {
CGFloat a = [[dict objectForKey:#"lat"] floatValue];
CGFloat b = [[dict objectForKey:#"long"] floatValue];
// CLLocationCoordinate2D location;
// location.latitude = a;
// location.longitude = b;
//
CLLocationCoordinate2D touchMapCoordinate
= [self.mapView convertPoint:CGPointMake(b,a) toCoordinateFromView:self.mapView];
MapViewAnnotation *myPin = [[MapViewAnnotation alloc] initWithCoordinate:touchMapCoordinate]; // Or whatever coordinates...
[self.mapView addAnnotation:myPin];
}
}
When the annotations are added, they are added relative to my screens bounds , and not to the whole mapview.
I do understand that the method call [self.mapView convertPoint:CGPointMake(a,b) toCoordinateFromView:self.mapView] is the cause of this.
I would like to perform the code that is now in comments, and skip the
CLLocationCoordinate2D touchMapCoordinate
= [self.mapView convertPoint:CGPointMake(b,a) toCoordinateFromView:self.mapView];
and go directly for MapViewAnnotation *myPin = [[MapViewAnnotation alloc] initWithCoordinate:location];
However, when i do this i get the following error:
An instance 0x858cc30 of class MapViewAnnotation was deallocated while key value observers were still registered with it. Observation info was leaked, and may even become mistakenly attached to some other object. Set a breakpoint on NSKVODeallocateBreak to stop here in the debugger. Here's the current observation info:
<NSKeyValueObservationInfo 0x1a000890> (
<NSKeyValueObservance 0x1a000850: Observer: 0xa117890, Key path: coordinate, Options: <New: NO, Old: NO, Prior: YES> Context: 0x0, Property: 0x1a0008d0>
)
What i wonder is: How can i add these annotations relative to my map, with long/latitude obtained from JSON (and not relative to the view i currently see).

Resources