Change starting location when opening Apple Maps from within app - objective-c

By default when opening Apple Maps through a MKMapItem it uses the device's location as the starting point but i'm trying to allow a user to add their own starting point by entering in the proper fields (address, city, state, zipcode) and passing the address string to
CLGeocoder *geocoder = [[CLGeocoder alloc] init];
NSString *addressString = [NSString stringWithFormat:#"%# %# %# %#",
self.address.text,
self.city.text,
self.state.text,
self.zip.text];
[geocoder geocodeAddressString:addressString
completionHandler:^(NSArray *placemarks, NSError *error)
{
CLLocationCoordinate2D addressCoordinates;
if (error.code == 2)
{
UIAlertView *networkConnectionAlert = [[UIAlertView alloc] initWithTitle:#"Network Error" message:#"The internet connections appears to be offline. Ensure that data is enabled or that your device is connected to a wireless connection." delegate:self cancelButtonTitle:#"OK" otherButtonTitles:nil];
[networkConnectionAlert show];
NSLog(#"Geocode failed with error: %#", error);
return;
}
if (placemarks && placemarks.count > 0)
{
CLPlacemark *placemark = placemarks[0];
// Location of address passed in
CLLocation *location = placemark.location;
// Coordinates of address passed in
addressCoordinates = location.coordinate;
}
// Destination address dictionary values
NSDictionary *address = #{
(NSString *)kABPersonAddressStreetKey: self.address.text,
(NSString *)kABPersonAddressCityKey: self.city.text,
(NSString *)kABPersonAddressStateKey: self.state.text,
(NSString *)kABPersonAddressZIPKey: self.zip.text
};
MKPlacemark *place = [[MKPlacemark alloc] initWithCoordinate:addressCoordinates
addressDictionary:address];
MKMapItem *mapItem = [[MKMapItem alloc]initWithPlacemark:place];
NSDictionary *options = #{MKLaunchOptionsDirectionsModeKey:MKLaunchOptionsDirectionsModeDriving};
[mapItem openInMapsWithLaunchOptions:options];
}];
Of course every time I run the app it uses the device's location as the starting point and the destination point as the address entered which is understandable given the code above but is there not a property that can be called to change the starting point? I know that you can change the starting address/location when you open the actual Apple Maps so I assume that there has to be a way and that I'm just doing something wrong or missing something. Any help on this would be greatly appreciated, thanks.

Related

Why I am not able to get Latitude and longitude on real device in Objective C

I am new in iOS and I am facing problem to get current Latitude and Longitude on the real device where as I am able to get latitude and Longitude on simulator.
My code is like this
if (nil == locationManager)
locationManager = [[CLLocationManager alloc] init];
locationManager.delegate = self;
//Configure Accuracy depending on your needs, default is kCLLocationAccuracyBest
locationManager.desiredAccuracy = kCLLocationAccuracyKilometer;
// Set a movement threshold for new events.
locationManager.distanceFilter = kCLDistanceFilterNone; // meters
[locationManager startUpdatingLocation];
Currentlatitude = locationManager.location.coordinate.latitude;
Currentlongitude = locationManager.location.coordinate.longitude;
NSLog(#"Latitude =%f",Currentlatitude);
NSLog(#"Longitude =%f",Currentlongitude);
CheckString=#"2";
// Your location from latitude and longitude
CLLocation *location = [[CLLocation alloc] initWithLatitude:Currentlatitude longitude:Currentlongitude];
// Call the method to find the address
[self getAddressFromLocation:location completionHandler:^(NSMutableDictionary *d) {
NSLog(#"address informations : %#", d);
// NSLog(#"formatted address : %#", [placemark.addressDictionary valueForKey:#"FormattedAddressLines"]);
NSLog(#"Street : %#", [d valueForKey:#"Street"]);
NSLog(#"ZIP code : %#", [d valueForKey:#"ZIP"]);
NSLog(#"City : %#", [d valueForKey:#"City"]);
CityNameCurrent=[d valueForKey:#"City"];
// etc.
} failureHandler:^(NSError *error) {
NSLog(#"Error : %#", error);
}];
- (void)getAddressFromLocation:(CLLocation *)location completionHandler:(void (^)(NSMutableDictionary *placemark))completionHandler failureHandler:(void (^)(NSError *error))failureHandler
{
NSMutableDictionary *d = [NSMutableDictionary new];
CLGeocoder *geocoder = [CLGeocoder new];
[geocoder reverseGeocodeLocation:location completionHandler:^(NSArray *placemarks, NSError *error) {
if (failureHandler && (error || placemarks.count == 0)) {
failureHandler(error);
} else {
CLPlacemark *placemark = [placemarks objectAtIndex:0];
if(completionHandler) {
completionHandler(placemark.addressDictionary);
}
}
}];
}
In ViewDidLod()
mapViewMap.delegate=self;
mapViewMap.myLocationEnabled=YES;
locationtbl.hidden=YES;
if (nil == locationManager)
locationManager = [[CLLocationManager alloc] init];
locationManager.delegate = self;
//Configure Accuracy depending on your needs, default is kCLLocationAccuracyBest
locationManager.desiredAccuracy = kCLLocationAccuracyKilometer;
// Set a movement threshold for new events.
locationManager.distanceFilter = kCLDistanceFilterNone; // meters
[locationManager startUpdatingLocation];
Currentlatitude = locationManager.location.coordinate.latitude;
Currentlongitude = locationManager.location.coordinate.longitude;
NSLog(#"Latitude =%f",Currentlatitude);
NSLog(#"Longitude =%f",Currentlongitude);
mapViewMap.settings.myLocationButton = YES;
mapViewMap.settings.scrollGestures = YES;
mapViewMap.settings.zoomGestures = YES;
mapViewMap.settings.tiltGestures=YES;
mapViewMap.settings.rotateGestures=NO;
mapViewMap.settings.compassButton = YES;
I am not getting what I am doing wrong.Please help.Thanks in Advance!
The simulator doesn't have to wait for a fix before generating a location. The device does. You have to implement a delegate for the location manager and wait for the delegate to be called with an update.
You need to implement CLLocationManager delegate method didUpdateLocations, it is where you get location updates.
You can do it like this.
- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations{
// latest location
self.currentLocation = [locations lastObject];
float longitude = self.currentLocation.coordinate.longitude;
float latitude = self.currentLocation.coordinate.latitude;
// Here you can perform operations with your location coordinates
}
You also need to add following entries in your .plist file based on your location need.
NSLocationWhenInUseUsageDescription
or
NSLocationAlwaysUsageDescription
And request the permission for location usage by calling
[locationManager requestWhenInUseAuthorization];
or
[locationManager requestAlwaysAuthorization]
Documentation link Location and Maps Programming Guide
You also need to add one (or both) of the following keys to you plist:
NSLocationWhenInUseUsageDescription
or
NSLocationAlwaysUsageDescription
and call correspondingly either
[self.locationManager requestWhenInUseAuthorization]
or
[self.locationManager requestAlwaysAuthorization]

Objective-C - Search for streets based on user-entered query

I want to allow the users to search for a street name and have the results displayed in a UITableView.
For the moment the region is not important, it can be from any region.
I could not find any relevant example in my searches and I don't know if I should use CLLocation or MKLocalSearch.
Based on docs, I should use MKLocalSearch:
Although local search and geocoding are similar, they support
different use cases. Use geocoding when you want to convert between
map coordinates and a structured address, such as an Address Book
address. Use local search when you want to find a set of locations
that match the user’s input.
But I have tried both methods and it gives me only 1 result (even-though there is an NSArray returned.
This is the CLGeocoder approach:
CLGeocoder *geocoding = [[CLGeocoder alloc] init];
[geocoding geocodeAddressString:theTextField.text completionHandler:^(NSArray *placemarks, NSError *error) {
if (error) {
NSLog(#"%#", error);
} else {
NSLog(#"%i", [placemarks count]);
for(CLPlacemark *myStr in placemarks) {
NSLog(#"%#", myStr);
}
}
}];
And this is my MKLocalSearch try:
MKLocalSearchRequest *request = [[MKLocalSearchRequest alloc] init];
request.naturalLanguageQuery = theTextField.text;
request.region = self.region;
localSearch = [[MKLocalSearch alloc] initWithRequest:request];
[localSearch startWithCompletionHandler:^(MKLocalSearchResponse *response, NSError *error){
if (error != nil) {
[[[UIAlertView alloc] initWithTitle:NSLocalizedString(#"Map Error",nil)
message:[error localizedDescription]
delegate:nil
cancelButtonTitle:NSLocalizedString(#"OK",nil) otherButtonTitles:nil] show];
return;
}
if ([response.mapItems count] == 0) {
[[[UIAlertView alloc] initWithTitle:NSLocalizedString(#"No Results",nil)
message:nil
delegate:nil
cancelButtonTitle:NSLocalizedString(#"OK",nil) otherButtonTitles:nil] show];
return;
}
self.streets = response;
[self.streetsTableView reloadData];
}];
MKLocalSearch seems to return more than 1 response in some cases, but these are related to places not street names searches.
Thanks in advance.
This is the closest I could get. This involves using google places API Web Service.
Note: You could probably use their Google Maps API, etc. I am sure there are other ways to get this information from the various Google APIs.
NSURL *googlePlacesURL = [NSURL URLWithString:[NSString stringWithFormat:#"https://maps.googleapis.com/maps/api/place/autocomplete/json?input=%#&location=%f,%f&sensor=true&key=API_KEY", formattedSearchText, location.coordinate.latitude,
location.coordinate.longitude]];
The response is a JSON object. Convert it to a dictionary.
NSDictionary *response = [NSJSONSerialization JSONObjectWithData:_googlePlacesResponse
options:NSJSONReadingMutableContainers error:&error];
if([[response objectForKey:#"status"] isEqualToString:#"OK"])
{
NSArray *predictions = [response objectForKey:#"predictions"];
for(NSDictionary *prediction in predictions)
{
NSArray *addressTypes = [prediction objectForKey:#"types"];
if([addressTypes containsObject:#"route"])
{
//This search result contains a street name.
//Now get the street name.
NSArray *terms = [prediction objectForKey:#"terms"];
NSDictionary *streetNameKeyValuePair = [terms objectAtIndex:0];
NSLog(#"%#",[streetNameKeyValuePair objectForKey#"value"]);
}
}
}
The possible types seem to be
Route -> Street name
Locality -> City/location name
Political -> State, etc
Geocode -> lat/long available
You could populate the table view with those predictions that ONLY contain route as an address type. This could work.
CLGeocoder simply returns address format.
Slap this in your code and play with the contents of mapitems
MKLocalSearchRequest *request = [MKLocalSearchRequest new];
request.naturalLanguageQuery = #"Pizza";
request.region = MKCoordinateRegionMake(location.coordinate, MKCoordinateSpanMake(.01, .01));
MKLocalSearch *search = [[MKLocalSearch alloc]initWithRequest:request];
[search startWithCompletionHandler:^(MKLocalSearchResponse *response, NSError *error) {
NSArray *mapItems = response.mapItems;
for (MKMapItem *mapItem in mapItems) {
MKPointAnnotation *point = [MKPointAnnotation new];
point.coordinate = mapItem.placemark.coordinate;`
}
}];
The array returned contains mapItems, you can iterate over the array to pull out all the mapItems like this:
myMatchingItems = [[NSMutableArray alloc] init];
for (MKMapItem *item in response.mapItems){
[myMatchingItems addObject:item];
}
Each mapItem.placemark.thoroughfare contains the Street of the location that was found.

How do you use Core Location without User sharing/granting permission?

I have implemented core location in my app.
Everything works perfect when user allows sharing the location at start of the app.
But when user does not share his location "South atlantic" is shown in the location field in the app.
What should be done to avoid this and what should be displayed when the user does not share his location.
I am using this code for getting the location:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
// Override point for customization after application launch.
self.viewController = [[ViewController alloc] initWithNibName:#"ViewController" bundle:nil];
[self.window makeKeyAndVisible];
locationManager = [[CLLocationManager alloc] init];
locationManager.distanceFilter = kCLDistanceFilterNone; // whenever we move
locationManager.desiredAccuracy = kCLLocationAccuracyBest;
[locationManager startUpdatingLocation];
CLGeocoder *ceo = [[CLGeocoder alloc]init];
CLLocation *loc = [[CLLocation alloc]initWithLatitude:locationManager.location.coordinate.latitude longitude:locationManager.location.coordinate.longitude]; //insert your coordinates
[ceo reverseGeocodeLocation: loc completionHandler:
^(NSArray *placemarks, NSError *error)
{
CLPlacemark *placemark = [placemarks objectAtIndex:0];
NSLog(#"placemark %#",placemark);
//String to hold address
locatedAt = [[placemark.addressDictionary valueForKey:#"FormattedAddressLines"] componentsJoinedByString:#", "];
NSLog(#"addressDictionary %#", placemark.addressDictionary);
NSLog(#"placemark %#",placemark.region);
NSLog(#"placemark %#",placemark.country); // Give Country Name
NSLog(#"placemark %#",placemark.locality); // Extract the city name
NSLog(#"location %#",placemark.name);
NSLog(#"location %#",placemark.ocean);
NSLog(#"location %#",placemark.postalCode);
NSLog(#"location %#",placemark.subLocality);
NSLog(#"location %#",placemark.location);
//Print the location to console
NSLog(#"I am currently at %#",locatedAt);
}];
return YES;
}
- (NSString *)deviceLocation
{
NSString *theLocation = [NSString stringWithFormat:#"latitude: %f longitude: %f", locationManager.location.coordinate.latitude, locationManager.location.coordinate.longitude];
return theLocation;
}
You can check [CLLocationManager authorizationStatus] to verify that the user has allowed location services using something like
int result = [CLLocationManager authorizationStatus];
It has return values in
typedef enum {
kCLAuthorizationStatusNotDetermined = 0,
kCLAuthorizationStatusRestricted,
kCLAuthorizationStatusDenied,
kCLAuthorizationStatusAuthorized
} CLAuthorizationStatus;
See the Location Awareness Programming Guide for information on how to get the user's location. Bottom line, you have to create the CLLocationManager location manager (like you are), but then start a location service (either the low-power significant-change location service or the standard location service) and wait for the CLLocationManagerDelegate method didUpdateLocations to return a location (or for didFailWithError to report an error, either because the app failed authorization, or because the device couldn't satisfy the location request for some reason). You can't, though, just grab the location from the CLLocationManager. You have to wait for the service to call didUpdateLocations before you reliably start using longitudes and latitudes.
The Location Awareness Programming Guide will walk you through all of this, including some very helpful code samples.

GPS routing (Shortest path) in iPhone using Mapkit and Google map

I am developing on Xcode 4.3.3, iOS 5 and using Mapkit library.
The app should show current location on Google map, get the destination address and finally draw the shortest path between those two points.
I used this tutorial for implementing the app and I have current location now:
http://blog.objectgraph.com/index.php/2009/04/02/iphone-sdk-30-playing-with-map-kit/
I looked for routing but I haven't found any resource.
Please guide me how I can draw the shortest path between the current location and the destination.
Thanks!
You can try this will only work for ios 6.
Class itemClass = [MKMapItem class];
if (itemClass && [itemClass respondsToSelector:#selector(openMapsWithItems:launchOptions:)]) {
CLGeocoder *geocoder = [[CLGeocoder alloc] init];
CLLocation *newLocation = [[CLLocation alloc]initWithLatitude:getLatitude
longitude:getLongitude];
[geocoder reverseGeocodeLocation:newLocation
completionHandler:^(NSArray *placemarks, NSError *error) {
MKPlacemark *placeMark = [[MKPlacemark alloc] initWithPlacemark:[placemarks objectAtIndex:0]];
MKMapItem *mapItem = [[MKMapItem alloc]initWithPlacemark:placeMark];
MKMapItem *mapItem2 = [MKMapItem mapItemForCurrentLocation];
NSArray *mapItems = #[mapItem, mapItem2];
NSDictionary *options = #{
MKLaunchOptionsDirectionsModeKey:MKLaunchOptionsDirectionsModeDriving,
MKLaunchOptionsMapTypeKey:
[NSNumber numberWithInteger:MKMapTypeStandard],
MKLaunchOptionsShowsTrafficKey:#YES
};
[MKMapItem openMapsWithItems:mapItems launchOptions:options];
} ];
}
else
{
UIAlertView *errorAlert = [[UIAlertView alloc] initWithTitle:#"Error" message:#"Failed to Get Your Location"delegate:nil cancelButtonTitle:#"OK" otherButtonTitles:nil];
[errorAlert show];
}
}
Did you google for map kit and route?
You'd find this: http://inlight.com.au/posts/mapkit which explains how to use Google Maps and decode the response to extract points to get a route.
There's also a commercial kit: http://mtdirectionsk.it/
iOS MapKit does not provide routing information for Apps as a service. Instead, your choices now are either to get routing information from a third-party mechanism, or to build the routes yourself (which, if you don't have the underlying street data is basically impossible).
You might look at CloudMade as an alternative map system if you need to provide routing information in your own App, or consider just calling Maps to route to a location if you want to take advantage of Apple's Map for navigation.
If you want to call out to the Map App for navigation, you basically need to open a URL with the source and destination location. Here's an example:
CLLocationManager *manager = [[[CLLocationManager alloc] init] autorelease];
CLLocationCoordinate2D currentLocation = [manager location].coordinate;
NSString *from = [NSString stringWithFormat: #"%f,%f",
currentLocation.latitude, currentLocation.longitude];
// used to be able to (3.2 iPad) "Current+Location", now we have to send: lat,long
NSString *encodedAddress = [address stringByAddingPercentEscapesUsingEncoding:
NSUTF8StringEncoding];
NSString *url = [NSString stringWithFormat:
#"http://maps.google.com/maps?saddr=%#&daddr=%#", from, encodedAddress];
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:url]];
Below ans work for all ios:
NSString *deviceVersion = [[UIDevice currentDevice] systemVersion];
NSLog(#"My Device version is :%# ",deviceVersion);
//********* For ios 6 supporting devices *********
if ([deviceVersion isEqualToString:#"6.0"])
{
Class itemClass = [MKMapItem class];
if (itemClass && [itemClass respondsToSelector:#selector(openMapsWithItems:launchOptions:)]) {
CLGeocoder *geocoder = [[CLGeocoder alloc] init];
CLLocation *newLocation = [[CLLocation alloc]initWithLatitude:getLatitude
longitude:getLongitude];
[geocoder reverseGeocodeLocation:newLocation
completionHandler:^(NSArray *placemarks, NSError *error) {
MKPlacemark *placeMark = [[MKPlacemark alloc] initWithPlacemark:[placemarks objectAtIndex:0]];
MKMapItem *mapItem = [[MKMapItem alloc]initWithPlacemark:placeMark];
MKMapItem *mapItem2 = [MKMapItem mapItemForCurrentLocation];
NSArray *mapItems = #[mapItem, mapItem2];
NSDictionary *options = #{
MKLaunchOptionsDirectionsModeKey:MKLaunchOptionsDirectionsModeDriving,
MKLaunchOptionsMapTypeKey:
[NSNumber numberWithInteger:MKMapTypeStandard],
MKLaunchOptionsShowsTrafficKey:#YES
};
[MKMapItem openMapsWithItems:mapItems launchOptions:options];
} ];
}
else
{
UIAlertView *errorAlert = [[UIAlertView alloc] initWithTitle:#"Error" message:#"Failed to Get Your Location"delegate:nil cancelButtonTitle:#"OK" otherButtonTitles:nil];
[errorAlert show];
}
}
//********* For other ios supporting devices *********
else {
MKCoordinateRegion region = { {0.0, 0.0 }, { 0.0, 0.0 } };
region.center.latitude = getLatitude;
region.center.longitude = getLongitude;
MKCoordinateRegion currentRegion = { {0.0, 0.0 }, { 0.0, 0.0 } };
currentRegion.center.latitude = currentLatitude;
currentRegion.center.longitude = currentLongitude;
region.span.longitudeDelta = 4.0f;
region.span.latitudeDelta = 4.0f;
currentRegion.span.longitudeDelta = 4.0f;
currentRegion.span.latitudeDelta = 4.0f;
CLLocationCoordinate2D start = { currentRegion.center.latitude, currentRegion.center.longitude };
CLLocationCoordinate2D destination = { region.center.latitude, region.center.longitude };
NSString *googleMapsURLString = [NSString stringWithFormat:#"http://maps.google.com/?saddr=%1.6f,%1.6f&daddr=%1.6f,%1.6f",start.latitude, start.longitude, destination.latitude, destination.longitude];
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:googleMapsURLString]];
}
}

Issue forward-geocoding multiple addresses

I am connecting to a remote web service which basically returns an XML back. I am then parsing that XML into a Property object (think real state sort of thing)
But now, the web service returns a postal code for each property alone. It does not provide a coordinate which is what I need to place an annotation in the map. I am able to geocode an address provided a postal code. However, my problem is it is not allowing me to do multiple requests
Here's my code
- (void)processProperties:(Property *)property {
[geocoder geocodeAddressString:property.postalCode
completionHandler:^(NSArray* placemarks, NSError* error){
placemark = [placemarks lastObject];
for (CLPlacemark* aPlacemark in placemarks)
{
[sublet setLatitude:aPlacemark.location.coordinate.latitude];
[sublet setLongitude:aPlacemark.location.coordinate.longitude];
}
}];
}
- (void)addAnnotations:(NSArray *)objects {
CLLocationDegrees lat;
CLLocationDegrees longitude;
CLLocationCoordinate2D mCoords;
NSString *fullAddress;
// Add the annotations found nearby
for (Property *property in objects) {
[self processProperties:property];
lat = property.latitude;
longitude = property.longitude;
fullAddress = [NSString stringWithFormat:#"%# %# %#", property.houseNumber, #" ", property.streetName];
[self createAnnotationWithCoords:mCoords :fullAddress :[NSString stringWithFormat:#"$%.2f", property.rent]];
}
zoomLevel = 0.1;
mCoords = CLLocationCoordinate2DMake(lat,longitude);
MKCoordinateRegion region = MKCoordinateRegionMake(mCoords,MKCoordinateSpanMake(zoomLevel,zoomLevel));
[self.mapView setRegion:region animated:YES];
}
For some reason it's just geocoding 1 property. Is not going through the loop accordingly.
Any ideas folks?
Use this on your Forward Geo Function. geocoder needs to be release and initialized again to start a new address, hope this helps.
- (void)processProperties:(Property *)property {
CLGeocoder *geocoder = [[CLGeocoder alloc] init];
[geocoder geocodeAddressString:property.postalCode
completionHandler:^(NSArray* placemarks, NSError* error){
placemark = [placemarks lastObject];
for (CLPlacemark* aPlacemark in placemarks)
{
[sublet setLatitude:aPlacemark.location.coordinate.latitude];
[sublet setLongitude:aPlacemark.location.coordinate.longitude];
}
[geocoder release];
}];
}