I am Using MapBox to draw direction route for source and destination point.
When I use the sample URL provided by the Website then I am able to draw route line.
But when I use my own co-ordinates then I am not able to draw any route.
I am not getting error message or any logs.
#import "ViewController.h"
#interface ViewController ()
{
NSMutableData *_responseData;
NSDictionary *_directionApiResponseDict;
NSArray *_coordinatesArray;
BOOL isControllerPoped;
}
#property (nonatomic) MGLMapView *mapView;
#end
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// initialize the map view
// NSURL *styleURL = [NSURL URLWithString:#"asset://styles/dark-v8.json"];
self.mapView = [[MGLMapView alloc] initWithFrame:self.view.bounds];
self.mapView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
self.mapView.delegate = self;
//set the map's center coordinate
[self.mapView setCenterCoordinate:CLLocationCoordinate2DMake(28.57,77.32)
zoomLevel:15
animated:NO];
[self.view addSubview:self.mapView];
// MGLPointAnnotation *point = [[MGLPointAnnotation alloc] init];
// point.coordinate = CLLocationCoordinate2DMake(-122.42,37.78);
// point.title = #"Hello world!";
// point.subtitle = #"Welcome to The Ellipse.";
//
// // Add annotation `point` to the map
// [self.mapView addAnnotation:point];
NSString *urlString = [NSString stringWithFormat:#"https://api.mapbox.com/v4/directions/mapbox.driving/28.57,77.32;28.11,77.22.json?access_token=pk.eyJ1IjoicmFodWx2cyIsImEiOiJjaWdjN2w1c3YycWNxdmptM3YzNzR4dWR5In0.jU9egzoUg46b4fLUQlL-Bg"];
//https://api.mapbox.com/v4/directions/mapbox.driving/-122.42,37.78;-77.03,38.91.json?alternatives=false&instructions=html&geometry=polyline&steps=false&&access_token=<your%20access%20token> these co-ordinates works fine
NSLog(#"URRRL=%#",urlString);
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:urlString]];
// Create url connection and fire request
NSURLConnection *conn = [[NSURLConnection alloc] initWithRequest:request delegate:self];
}
- (BOOL)mapView:(MGLMapView *)mapView annotationCanShowCallout:(id <MGLAnnotation>)annotation {
return YES;
}
#pragma mark NSURLConnection Delegate Methods
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
_responseData = [[NSMutableData alloc] init];
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
[_responseData appendData:data];
}
- (NSCachedURLResponse *)connection:(NSURLConnection *)connection
willCacheResponse:(NSCachedURLResponse*)cachedResponse {
return nil;
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
_directionApiResponseDict = [NSJSONSerialization JSONObjectWithData:_responseData options:0 error:nil];
NSArray *rootsDictArray = [_directionApiResponseDict valueForKey:#"routes"];
if(rootsDictArray && rootsDictArray.count > 0)
{
NSDictionary *rootDict = [rootsDictArray objectAtIndex:0];
if(rootDict && [rootDict valueForKey:#"geometry"])
{
NSDictionary *geoDict = [rootDict valueForKey:#"geometry"];
if(geoDict && [geoDict isKindOfClass:[NSDictionary class]])
{
_coordinatesArray = [geoDict valueForKey:#"coordinates"];
if(_coordinatesArray)
{
[self drawPolyline];
}
}
}
}
}
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
}
- (void)drawPolyline
{
// Perform GeoJSON parsing on a background thread
dispatch_queue_t backgroundQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(backgroundQueue, ^(void)
{
// Get the raw array of coordinates for our line
NSArray *rawCoordinates = _coordinatesArray;
NSUInteger coordinatesCount = rawCoordinates.count;
// Create a coordinates array, sized to fit all of the coordinates in the line.
// This array will hold the properly formatted coordinates for our MGLPolyline.
CLLocationCoordinate2D coordinates[coordinatesCount];
int validCoordinates=0;
// Iterate over `rawCoordinates` once for each coordinate on the line
for (NSUInteger index = 0; index < coordinatesCount; index++)
{
// Get the individual coordinate for this index
NSArray *point = [rawCoordinates objectAtIndex:index];
// GeoJSON is "longitude, latitude" order, but we need the opposite
if([point count]>1){
CLLocationDegrees lat = [[point objectAtIndex:1] doubleValue];
CLLocationDegrees lng = [[point objectAtIndex:0] doubleValue];
CLLocationCoordinate2D coordinate = CLLocationCoordinate2DMake(lat, lng);
// Add this formatted coordinate to the final coordinates array at the same index
coordinates[validCoordinates] = coordinate;
validCoordinates++;
}
}
// Create our polyline with the formatted coordinates array
MGLPolyline *polyline = [MGLPolyline polylineWithCoordinates:coordinates count:validCoordinates];
// Optionally set the title of the polyline, which can be used for:
// - Callout view
// - Object identification
// In this case, set it to the name included in the GeoJSON
// Add the polyline to the map, back on the main thread
// Use weak reference to self to prevent retain cycle
__weak typeof(self) weakSelf = self;
dispatch_async(dispatch_get_main_queue(), ^(void)
{
[weakSelf.mapView addAnnotation:polyline];
});
});
}
- (CGFloat)mapView:(MGLMapView *)mapView alphaForShapeAnnotation:(MGLShape *)annotation
{
// Set the alpha for all shape annotations to 1 (full opacity)
return 1.0f;
}
- (CGFloat)mapView:(MGLMapView *)mapView lineWidthForPolylineAnnotation:(MGLPolyline *)annotation
{
// Set the line width for polyline annotations
return 2.0f;
}
- (UIColor *)mapView:(MGLMapView *)mapView strokeColorForShapeAnnotation:(MGLShape *)annotation
{
// Set the stroke color for shape annotations
// ... but give our polyline a unique color by checking for its `title` property
if ([annotation.title isEqualToString:#"Crema to Council Crest"])
{
// Mapbox cyan
return [UIColor colorWithRed:59.0f/255.0f green:178.0f/255.0f blue:208.0f/255.0f alpha:1.0f];
}
else
{
return [UIColor redColor];
}
}
#pragma mark - Map Delegate methods
- (MGLAnnotationImage *)mapView:(MGLMapView *)mapView imageForAnnotation:(id <MGLAnnotation>)annotation {
return nil;
//
// MGLAnnotationImage *annotationImage = [mapView dequeueReusableAnnotationImageWithIdentifier:kImageIdentifire];
//
// if ( ! annotationImage)
// {
// // Leaning Tower of Pisa by Stefan Spieler from the Noun Project
// UIImage *image = [UIImage imageNamed:#"annotation.png"];
// annotationImage = [MGLAnnotationImage annotationImageWithImage:image reuseIdentifier:kImageIdentifire];
// }
//
// return annotationImage;
}
#end
There is an issue with your Mapbox URL call.
From Mapbox API Documentation:
a semicolon-separated list of {longitude},{latitude} coordinate pairs
to visit in order, containing at least two elements (origin and
destination). There can be anywhere between 2 and 25 coordinate pairs
in the list.
But you used the longitude value as latitude and the latitude value as longitude. Based on your CLLocationCoordinate2DMake method call that has these parameters:
CLLocationCoordinate2DMake(CLLocationDegrees latitude,
CLLocationDegrees longitude)
As you can see, in the Mapbox call URL you should put first the longitude and then the latitude.
Inverse the latitude and longitude in your url and everything will be OK. I tested with your link in the browser and I got directions.
You might be interested in the Mapbox Swift toolkit for directions:
https://github.com/mapbox/MapboxDirections.swift
It's meant to work like Apple's MKDirections. You can use it in Objective-C code by importing the automatically-generated <TargetName>-Swift.h into your code once the Swift code is also in the target.
Related
I am developing an app using Google Map iOS SDK (Objective C).
Done so far:
Loaded markers from the server in map view
Generate random clusters only.
When I run the project it looks like this.
Screen shot of map view
It shows both markers and clusters both in the map view at zoom level 10. But I wanted to show clusters first then when I zoomed in it should show the real markers. Not the randomly generated markers that I created because I don't know a way to show the clusters in the map.
Here is the full code with fake URL link:
#import "ViewController.h"
//#import "CSMarker.h"
#import <GoogleMaps/GoogleMaps.h>
#import <Google-Maps-iOS-Utils/GMUMarkerClustering.h>
//importing POI Item object - points of interest
#interface POIItem : NSObject<GMUClusterItem>
#property(nonatomic, readonly) CLLocationCoordinate2D position;
#property(nonatomic, readonly) NSString *name;
- (instancetype)initWithPosition:(CLLocationCoordinate2D)position name:(NSString *)name;
#end
#implementation POIItem
- (instancetype)initWithPosition:(CLLocationCoordinate2D)position name:(NSString *)name {
if ((self = [super init])) {
_position = position;
_name = [name copy];
}
return self;
}
#end
//implementation start - map view controller
static const NSUInteger kClusterItemCount = 60;
static const double kCameraLatitude = 25.277683999999997;
static const double kCameraLongitude = 55.309802999999995;
#interface ViewController ()<GMUClusterManagerDelegate, GMSMapViewDelegate>
{
NSMutableArray *waypoints_;
NSMutableArray *waypointStrings_;
GMSMapView *_mapView;
GMUClusterManager *_clusterManager;
}
#property(strong, nonatomic) NSURLSession *markerSession;
#property(strong, nonatomic) GMSMapView *mapView;
#property(copy, nonatomic) NSSet *markers;
#property(nonatomic, strong) NSMutableArray *markersArray;
#end
#implementation ViewController
#synthesize gs;
- (void)viewDidLoad {
[super viewDidLoad];
GMSCameraPosition *camera = [GMSCameraPosition cameraWithLatitude:kCameraLatitude
longitude:kCameraLongitude
zoom:10];
self.mapView =
[GMSMapView mapWithFrame:self.view.bounds camera:camera];
[self.view addSubview:self.mapView];
self.mapView.settings.compassButton = YES;
self.mapView.settings.myLocationButton = YES;
//setup the cluster manager
NSURLSessionConfiguration *config =
[NSURLSessionConfiguration defaultSessionConfiguration];
config.URLCache = [[NSURLCache alloc] initWithMemoryCapacity:2 * 1024 * 1024
diskCapacity:10 * 1024 * 1024
diskPath:#"MarkerData"];
self.markerSession = [NSURLSession sessionWithConfiguration:config];
[self downloadMarkerData];
//cluster load setup
id<GMUClusterAlgorithm> algorithm = [[GMUNonHierarchicalDistanceBasedAlgorithm alloc] init];
id<GMUClusterIconGenerator> iconGenerator = [[GMUDefaultClusterIconGenerator alloc] init];
id<GMUClusterRenderer> renderer =
[[GMUDefaultClusterRenderer alloc] initWithMapView:self.mapView
clusterIconGenerator:iconGenerator];
_clusterManager =
[[GMUClusterManager alloc] initWithMap:self.mapView algorithm:algorithm renderer:renderer];
// Generate and add random items to the cluster manager.
[self generateClusterItems];
// Call cluster() after items have been added to perform the clustering and rendering on map.
[_clusterManager cluster];
// Register self to listen to both GMUClusterManagerDelegate and GMSMapViewDelegate events.
[_clusterManager setDelegate:self mapDelegate:self];
// Do any additional setup after loading the view, typically from a nib.
}
- (NSMutableArray *)markersArray
{
if (!_markersArray) {
_markersArray = [NSMutableArray array];
}
return _markersArray;
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
//downloading marker data
- (void)downloadMarkerData {
NSURL *lakesURL =
[NSURL URLWithString:#"http://myscrap.com/xxx.php/webservice/xxx/xxxx"];
NSURLSessionDataTask *task = [self.markerSession dataTaskWithURL:lakesURL
completionHandler:^(NSData *data, NSURLResponse *response, NSError *e)
{
NSArray *json = [NSJSONSerialization JSONObjectWithData:data
options:0
error:nil];
NSLog(#"json: %#",json);
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
[self createMarkerObjectsWithJson:json];
}];
}];
[task resume];
}
-(void)drawMarkers
{
for(GMSMarker *marker in self.markers) {
if(marker.map == nil) {
marker.map = self.mapView;
}
}
}
-(void)createMarkerObjectsWithJson:(NSArray *)json
{
NSMutableSet *mutableSet = [[NSMutableSet alloc] initWithSet:self.markers];
for (NSDictionary *markerData in json) {
GMSMarker *newMarker = [[GMSMarker alloc] init];
// newMarker.appearAnimation = [markerData[#"id"] integerValue];
newMarker.position = CLLocationCoordinate2DMake([markerData[#"latitud"] doubleValue],
[markerData[#"longitude"] doubleValue]);
newMarker.title = markerData[#"name"];
newMarker.snippet = markerData[#"adress"];
// [mutableSet addObject:newMarker];
newMarker.map=self.mapView;
}
self.markers =[mutableSet copy];
[self drawMarkers];
}
#pragma mark GMUClusterManagerDelegate
- (void)clusterManager:(GMUClusterManager *)clusterManager didTapCluster:(id<GMUCluster>)cluster {
GMSCameraPosition *newCamera =
[GMSCameraPosition cameraWithTarget:cluster.position zoom:_mapView.camera.zoom + 1];
GMSCameraUpdate *update = [GMSCameraUpdate setCamera:newCamera];
[_mapView moveCamera:update];
}
#pragma mark GMSMapViewDelegate
- (BOOL)mapView:(GMSMapView *)mapView didTapMarker:(GMSMarker *)marker {
POIItem *poiItem = marker.userData;
if (poiItem != nil) {
NSLog(#"Did tap marker for cluster item %#", poiItem.name);
} else {
NSLog(#"Did tap a normal marker");
}
return NO;
}
#pragma mark Private
// Randomly generates cluster items within some extent of the camera and adds them to the
// cluster manager.
- (void)generateClusterItems {
const double extent = 0.2;
for (int index = 1; index <= kClusterItemCount; ++index) {
double lat = kCameraLatitude + extent * [self randomScale];
double lng = kCameraLongitude + extent * [self randomScale];
NSString *name = [NSString stringWithFormat:#"Item %d", index];
id<GMUClusterItem> item =
[[POIItem alloc] initWithPosition:CLLocationCoordinate2DMake(lat, lng) name:name];
[_clusterManager addItem:item];
}
}
// Returns a random value between -1.0 and 1.0.
- (double)randomScale {
return (double)arc4random() / UINT32_MAX * 2.0 - 1.0;
}
try changing in view did load
GMSCameraPosition *camera = [GMSCameraPosition cameraWithLatitude:kCameraLatitude longitude:kCameraLongitude zoom:4];
the zoom to a 5 or 4. It then should show the clusters and not the markers. When you zoom in, it will show the "real" markers.
i am newbie in iOS Development and new in map kit i want to know about Latitude and Longitude of MapView Center Every time Like as When User Scrollfrom one location to Another then i want to Show Center Latitude and Longitude of my GMSMapView how it Possible like as Here i Attach Image for it.
Here my Current Location is Mumbai Then It Shows My mapView like as
and i want to know Center Location Latitude and Longitude like as when user Scroll Mapview then i want to Know Latitude and Longitude of My Image Indicator Point. Here image Show as i want to Know Latitude and Longitude of this Point Here My Image indicated is in Center Position of my MapView.
To get the position whenever the map is moved, you'd want to handle mapView:didChangeCameraPosition:.
Then inside that method, you can access mapView.camera.target to get the current centre of the map.
- (void) mapView: (GMSMapView *)mapView
didChangeCameraPosition: (GMSCameraPosition *)position
{
double latitude = mapView.camera.target.latitude;
double longitude = mapView.camera.target.longitude;
// now do something with latitude and longitude
}
Note that to handle the delegate method, you'll need to implement the GMSMapViewDelegate protocol. When setting up the map, you'd need to set the map's delegate to the class which handles the protocol (usually self), eg:
mapView.delegate = self;
Use below code for getting center of mapview latitude and longitude .
When ever you scroll mapview in any direction automatically regionWillChangeAnimated: method will call.It's one of the optinal delegate method.
- (void)mapView:(MKMapView *)mapView regionWillChangeAnimated:(BOOL)animated
{
CLLocationCoordinate2D coord = CLLocationCoordinate2DMake(mkmapView.centerCoordinate.latitude, mkmapView.centerCoordinate.longitude);
CGFloat txtLatitude = coord.latitude;
CGFloat txtLongitude = coord.longitude;
NSLog(#"Latitude is===>>>%f %f",txtLongitude);
NSLog(#"Longitude is===>>>%f %f",txtLongitude);
}
If you tap the map view,you need that Latitude and Longitude use below code.
Here _mkMapView is outlet of Mapview.
- (void)viewDidLoad
{
UITapGestureRecognizer *tapGesture= [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(backgroundTapped:)];
[self.view addGestureRecognizer:tapGesture];
}
- (void)backgroundTapped:(UITapGestureRecognizer *)gesture
{
CGPoint touchPoint = [gesture locationInView:_mkMapView];
CLLocationCoordinate2D location =[_mkMapView convertPoint:touchPoint toCoordinateFromView:_mkMapView];
}
may be it will helpful for you.
- (void)mapView:(GMSMapView *)pMapView didChangeCameraPosition:(GMSCameraPosition *)position {
mapViewRef.delegate = self;
double latitude = mapViewRef.camera.target.latitude;
double longitude = mapViewRef.camera.target.longitude;
CLLocationCoordinate2D addressCoordinates = CLLocationCoordinate2DMake(latitude,longitude);
GMSGeocoder* coder = [[GMSGeocoder alloc] init];
[coder reverseGeocodeCoordinate:addressCoordinates completionHandler:^(GMSReverseGeocodeResponse *results, NSError *error) {
if (error) {
// NSLog(#"Error %#", error.description);
} else {
GMSAddress* address = [results firstResult];
NSArray *arr = [address valueForKey:#"lines"];
NSString *str1 = [NSString stringWithFormat:#"%lu",(unsigned long)[arr count]];
if ([str1 isEqualToString:#"0"]) {
self.txtPlaceSearch.text = #"";
}
else if ([str1 isEqualToString:#"1"]) {
NSString *str2 = [arr objectAtIndex:0];
self.txtPlaceSearch.text = str2;
}
else if ([str1 isEqualToString:#"2"]) {
NSString *str2 = [arr objectAtIndex:0];
NSString *str3 = [arr objectAtIndex:1];
self.txtPlaceSearch.text = [NSString stringWithFormat:#"%#,%#",str2,str3];
}
}
}];
}
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];
I've been through several different tutorials trying to get this working, but they seem to gloss over some crucial steps that a beginner might not know.
I have a JSON file at a URL with name, latitude, and longitude listed. How can I import that to an array or dictionary (I don't know the difference), and then iterate over it and create a new annotation with each iteration.
IOS6, Storyboards
_ Added Code _
ViewController.h
#import <UIKit/UIKit.h>
#import <MapKit/MapKit.h>
#interface ViewController : UIViewController {}
#property (weak, nonatomic) IBOutlet MKMapView *mapView;
#property (nonatomic, strong) NSMutableData *downloadData;
#end
ViewController.m
#import "ViewController.h"
#import "MapViewAnnotation.h"
#interface ViewController ()
#end
#implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
_downloadData = [NSMutableData new];
NSURL *requestURL = [NSURL URLWithString:#"OMITTED/apptest/locations.json"];
NSURLRequest *request = [NSURLRequest requestWithURL:requestURL];
NSURLConnection *connection = [NSURLConnection connectionWithRequest:request delegate:self];
[connection start];
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
[_downloadData appendData:data];
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
id parsed = [NSJSONSerialization JSONObjectWithData:_downloadData options:kNilOptions error:nil];
for (NSDictionary *pointInfo in parsed)
{
NSLog([parsed objectForKey:#"name"]);
double xCoord = [(NSNumber*)[parsed objectForKey:#"lat"] doubleValue];
double yCoord = [(NSNumber*)[parsed objectForKey:#"lon"] doubleValue];
CLLocationCoordinate2D coords = CLLocationCoordinate2DMake(xCoord, yCoord);
MKPointAnnotation *point = [MKPointAnnotation new];
point.coordinate = coords;
point.title = [parsed objectForKey:#"name"];
[self.mapView addAnnotation:point]; // or whatever your map view's variable name is
}
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
-(void)viewDidUnload {
[super viewDidUnload];
// ARC Problem --- [_mapView release];
self.mapView = nil;
}
#end
With iOS 5 there's a JSONSerializer class that can convert the raw JSON data from your URL into an array or dictionary as appropriate.
You'll need to download the data from the server:
NSURL *requestURL = [NSURL URLWithString:#"<your url here>"];
NSURLRequest *request = [NSURLRequest requestWithURL:requestURL];
NSURLConnection *connection = [NSURLConnection connectionWithRequest:request delegate:self];
[connection start];
Then you'll add these delegate methods:
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
[_downloadData appendData:data];
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
id parsed = [NSJSONSerialization JSONObjectWithData:_downloadData options:kNilOptions error:nil];
}
_downloadData is an instance variable or property of your class of type NSMutableData.
That parsed variable will contain your data from the server. It's probably an array if it's a list of points, so you can iterate through it using fast enumeration:
for (NSDictionary *pointInfo in parsed) {
double xCoord = [(NSNumber*)[parsed objectForKey:#"<key for lat coord>"] doubleValue];
double yCoord = [(NSNumber*)[parsed objectForKey:#"<key for long coord>"] doubleValue];
CLLocationCoordinate2D coords = CLLocationCoordinate2DMake(xCoord, yCoord);
MKPointAnnotation *point = [[MKPointAnnotation new] autorelease];
point.coordinate = coords;
point.title = [parsed objectForKey:#"<key for title>"];
[self.mapView addAnnotation:point]; // or whatever your map view's variable name is
}
I have an open source project on GitHub that uses the serializer with the NSCoding protocol so you can automatically create instances right from the JSON stream.
It's here.
G'day All,
I am trying to change the colours of the pins in MKPointAnnotations. Most of the code examples I have tried to follow are too complex for me to follow. I have come up with this:
//
// ViewController.m
// PlayingWithMaps
//
// Created by custom on 22/09/12.
// Copyright (c) 2012 __MyCompanyName__. All rights reserved.
//
#import "ViewController.h"
#interface ViewController ()
#end
#implementation ViewController
-(MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id<MKAnnotation>)annotationPoint
{
static NSString *annotationIdentifier = #"annotationIdentifier";
MKPinAnnotationView *pinView = [[MKPinAnnotationView alloc]initWithAnnotation:annotationPoint reuseIdentifier:annotationIdentifier];
pinView.pinColor = MKPinAnnotationColorPurple;
return pinView;
}
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
// First, choose what type of map
//mapView.mapType = MKMapTypeHybrid;
mapView.mapType = MKMapTypeStandard;
// Second, where is the centre of the map
CLLocationCoordinate2D centreCoord = {.latitude = -37.123456, .longitude = 145.123456};
MKCoordinateSpan mapSpan = {.latitudeDelta = 0.001, .longitudeDelta = 0.001};
MKCoordinateRegion region = {centreCoord, mapSpan};
[mapView setRegion:region];
// Now lets make a single annotation.
CLLocationCoordinate2D annotationCoordinate;
annotationCoordinate.latitude = -37.781650;
annotationCoordinate.longitude = 145.076116;
MKPointAnnotation *annotationPoint = [[MKPointAnnotation alloc]init];
annotationPoint.coordinate = annotationCoordinate;
annotationPoint.title = #"My point of interest";
annotationPoint.subtitle = #"Location";
[mapView addAnnotation:annotationPoint];
// Read a set of points from files into arrays
NSString *fileString = [NSString stringWithContentsOfFile:[[NSBundle mainBundle]pathForResource:#"latitudes" ofType:#"txt"] encoding:NSUTF8StringEncoding error:nil];
NSMutableArray *latitudeStringsArray = [NSMutableArray arrayWithArray:[fileString componentsSeparatedByString:#"\n"]];
fileString = [NSString stringWithContentsOfFile:[[NSBundle mainBundle]pathForResource:#"longitudes" ofType:#"txt"] encoding:NSUTF8StringEncoding error:nil];
NSMutableArray *longitudeStringsArray = [NSMutableArray arrayWithArray:[fileString componentsSeparatedByString:#"\n"]];
// Now lets put them onto the map
int i; // Loop control
for (i=0; i<25; i++)
{
MKPointAnnotation *annotationPoint = [[MKPointAnnotation alloc]init];
annotationCoordinate.latitude = [[latitudeStringsArray objectAtIndex:i]floatValue];
annotationCoordinate.longitude = [[longitudeStringsArray objectAtIndex:i]floatValue];
annotationPoint.coordinate = annotationCoordinate;
annotationPoint.title = [NSString stringWithFormat:#"Point %d",i];
annotationPoint.subtitle = [NSString stringWithFormat:#"%f, %f",[[latitudeStringsArray objectAtIndex:i]floatValue],[[longitudeStringsArray objectAtIndex:i]floatValue]];
[mapView addAnnotation:annotationPoint];
}
}
- (void)viewDidUnload
{
[super viewDidUnload];
// Release any retained subviews of the main view.
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
return YES;
}
#end
The points appear as in the right places but they are the default red and not purple as I was trying to achieve. My understanding from the reading I have done is that the (MKAnnotationView) method should be called whenever an annotation point is created but it's not happening.
I know it's something fundamental that I'm missing but I have no idea what.
All assistance greatly appreciated.
Cheers,
If it's not called then you haven't set your ViewController as delegate for map view.