Issue forward-geocoding multiple addresses - objective-c

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

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.

kindly advice me to display x number of annotation points in mkmap

Good morning, I am very new to objective-c now I am developing my first app. It's a vehicle tracking app. I received the x-number of lat & long from the json service. Now I displayed a single annotation but I need to display all the annotation points which i received from the service here I search for a whole day to find this but am fails to find displaying x-num of annotations. So kindly advice me by using some sample codes. Thanks in Advance... My code is..
- (void)viewDidLoad
{
[super viewDidLoad];
//MAP VIEW WebService
NSString *urlMapString=[NSString stringWithFormat:#"http://www.logix.com/logix_webservice/map.php?format=json&truckno=%#",nam2];
NSURL *urlMap=[NSURL URLWithString:urlMapString];
NSData *dataMap=[NSData dataWithContentsOfURL:urlMap];
NSError *errorMap;
NSDictionary *jsonMap = [NSJSONSerialization JSONObjectWithData:dataMap options:kNilOptions error:&errorMap]; NSArray *resultsMap = [jsonMap valueForKey:#"posts"];
NSArray *resMap = [resultsMap valueForKey:#"post"];
NSArray *latitudeString=[resMap valueForKey:#"latitude"];
if([resMap count]){
NSString *latOrgstring = [latitudeString objectAtIndex:0];
double latitude=[latOrgstring doubleValue];
NSArray *longitudeString=[resMap valueForKey:#"longitude"];
NSString *longOrgstring = [longitudeString objectAtIndex:0];
double longitude=[longOrgstring doubleValue];
NSString *ignation=[[resMap valueForKey:#"ignition"]objectAtIndex:0];
i=[ignation intValue];
//MAP VIEW Point
MKCoordinateRegion myRegion;
//Center
CLLocationCoordinate2D center;
center.latitude=latitude;
center.longitude=longitude;
//Span
MKCoordinateSpan span;
span.latitudeDelta=0.01f;
span.longitudeDelta=0.01f;
myRegion.center=center;
myRegion.span=span;
//Set our mapView
[MapViewC setRegion:myRegion animated:YES];
//Annotation
//1.create coordinate for use with the annotation
CLLocationCoordinate2D wimbLocation;
wimbLocation.latitude=latitude;
wimbLocation.longitude=longitude;
Annotation * myAnnotation= [Annotation alloc];
CLLocation *someLocation=[[CLLocation alloc]initWithLatitude:latitude longitude:longitude];
CLGeocoder *geocoder = [[CLGeocoder alloc] init];
[geocoder reverseGeocodeLocation:someLocation completionHandler:^(NSArray *placemarks, NSError *error) {
NSDictionary *dictionary = [[placemarks objectAtIndex:0] addressDictionary];
addressOutlet=[dictionary valueForKey:#"Street"];
City=[dictionary valueForKey:#"City"];
State=[dictionary valueForKey:#"State"];
myAnnotation.coordinate=wimbLocation;
if (addressOutlet!=NULL&&City!=NULL)
{
myAnnotation.title=addressOutlet;
myAnnotation.subtitle=[NSString stringWithFormat:#"%#,%#", City, State];
}
else if (addressOutlet==NULL&&City!=NULL)
{
myAnnotation.title=City;
myAnnotation.subtitle=[NSString stringWithFormat:#"%#,%#", City, State];
}
else if (addressOutlet!=NULL&&City==NULL)
{
myAnnotation.title=addressOutlet;
myAnnotation.subtitle=[NSString stringWithFormat:#"%#", State];
}
else if(addressOutlet==NULL&&City==NULL)
{
myAnnotation.title=State;
myAnnotation.subtitle=[NSString stringWithFormat:#"%#",State];
}
[self.MapViewC addAnnotation:myAnnotation];
}];
}
}
Please make an individual class and use by importing .
Now use
-(id)initWithCoordinate:(CLLocationCoordinate2D)c;
and its .m file
-(id)initWithCoordinate:(CLLocationCoordinate2D)c
{
coordinate=c;
}
Now call this class anywhere and call this method and send your location coordinates.
and then add its objects into NSArray.
Now call [YourMapView addAnnotations:arrayOfAnnotations];
You will get it what you want.It works in my case I hope you will find it supportive.
Now add annotations on mapView:
-(MKAnnotationView *) mapView:(MKMapView *)mapV
viewForAnnotation:(id<MKAnnotation>)annotation
{
if ([annotation isKindOfClass:[pinAnnotation class]])
{
static NSString *defaultPinID = #"com.ABC.pin";//Your unique identifier anything
MKAnnotationView *pinView = nil;
if(!pinView)
{
pinView = (MKAnnotationView *)
[self.mapView dequeueReusableAnnotationViewWithIdentifier:defaultPinID];
pinView = [[MKAnnotationView alloc] initWithAnnotation:
annotation reuseIdentifier:defaultPinID] ;
return pinView;
}
static NSString *AnnotationViewID = #"annotationViewID";
MKAnnotationView* pin =
(MKAnnotationView*) [mapV dequeueReusableAnnotationViewWithIdentifier:
AnnotationViewID];
if ( pin == nil ) {
pin = [(MKAnnotationView*) [MKAnnotationView alloc]
initWithAnnotation:annotation reuseIdentifier: AnnotationViewID] ;
pin.canShowCallout = YES;
}
else
{
[pin setAnnotation: annotation];
}
((MKUserLocation *)annotation).title = #"You are here";
return pin;
}

Get State from GPS Coordinates

I have used CLLocationManager to get the current GPS coordinates of a user.
How would I use CLGeoCoder to convert these coordinates into just the current State the user is in?
Thanks.
Use the -reverseGeocodeLocation:completionHandler method, then inspect the administrativeArea property of the returned placemark.
In code:
/* Get a CLLocation into `location` */
CLGeocoder *gc = [[CLGeocoder alloc] init];
[gc reverseGeocodeLocation:location completionHandler:^(NSArray *placemark, NSError *error)
{
if(!placemark)
{
/* handle error */
}
else
{
CLPlacemark *pm = placemark[0];
NSLog(#"%#", pm.administrativeArea);
}
}

Programmatically getting directions using Apple Maps

I'm a bit confused by the documentation. After some research and experimentation, this is what I have.
if ([self canUseMKMapItem]) {
[self iosTheMap];
} else {
[self googleTheMap];
}
Using this to detect whether we can use the IOS6 mapping features:
- (BOOL) canUseMKMapItem {
Class itemClass = [MKMapItem class];
return (itemClass && [itemClass respondsToSelector:#selector(openMapsWithItems:launchOptions:)]);
}
This for IOS5, using Google Maps. It automatically takes us to a screen with a list of directions from the current address (if the user allows) to the destination.
- (void)googleTheMap
{
NSNumber *userLat = [[NSNumber alloc]initWithDouble:mapView.userLocation.coordinate.latitude];
NSNumber *userLong = [[NSNumber alloc]initWithDouble:mapView.userLocation.coordinate.longitude];
NSMutableString *queryString = [NSMutableString stringWithFormat:#"http://maps.google.com/?saddr=%#,%#&daddr=",userLat,userLong];
NSString *address = [partnerObject valueForKey:ATTRIBUTE_ADDRESS];
[queryString appendString:address];
NSString *escaped = [queryString stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:escaped]];
}
Here's the tricky part--this is what I'm trying to do to use Apple Maps
- (void)iosTheMap {
NSNumber * latitude = [partnerObject valueForKey:ATTRIBUTE_LATITUDE];
NSNumber * longitude = [partnerObject valueForKey:ATTRIBUTE_LONGITUDE];
CLLocationCoordinate2D coordinate;
coordinate.latitude = latitude.doubleValue;
coordinate.longitude = longitude.doubleValue;
NSMutableDictionary *addressDictionary = [[NSMutableDictionary alloc] init];
[addressDictionary setValue:[partnerObject valueForKey:ATTRIBUTE_ADDRESS] forKey:kABPersonAddressStreetKey];
MKPlacemark *placemark = [[MKPlacemark alloc] initWithCoordinate:coordinate addressDictionary:addressDictionary];
MKMapItem *mapItem = [[MKMapItem alloc] initWithPlacemark:placemark];
[mapItem openInMapsWithLaunchOptions:nil];
}
This "works," sort of. It takes the user to a map screen with a pin showing the address. The user can tap that and get directions. However, I have a few reservations about this approach:
I have a compiler warning where I set kABPersonAddressStreetKey: "Incompatible pointer types sending 'const CFStringRef' (aka 'const struct __CFString *const') to parameter of type 'NSString *'"
The string value I am using is the full address. I use that value for the street address, although the address values are meant to be more atomic--street, city, state. It seems to work, but I'm concerned this isn't the right way to do it.
It would be nice if Apple Maps showed the name of the business/destination, not just the address.
It would be great to have Apple Maps automatically show the directions, instead of a map with the destination point, saving the user a few taps.
Any suggestions for how I can improve my approach? Although it seems to work, I suspect that it is not the right way.
You are pretty close. You need to specify the Launch Options dictionary for the openInMapsWithLaunchOptions function using the MKLaunchOptionsDirectionsModeDriving, MKLaunchOptionsDirectionsModeKey value and key.
Class itemClass = [MKMapItem class];
if (itemClass && [itemClass respondsToSelector:#selector(openMapsWithItems:launchOptions:)]) {
// Use iOS 6 maps
CLLocationCoordinate2D coordinate = [((LocationAnnotation*)[map.annotations objectAtIndex:0]) coordinate];
MKPlacemark *placemark = [[MKPlacemark alloc] initWithCoordinate:coordinate addressDictionary:nil];
MKMapItem *mapItem = [[MKMapItem alloc] initWithPlacemark:placemark];
[mapItem openInMapsWithLaunchOptions:[NSDictionary dictionaryWithObjectsAndKeys:MKLaunchOptionsDirectionsModeDriving, MKLaunchOptionsDirectionsModeKey, nil]];
} else {
//Fall back on google
...
}