Address/Zip Code as NSString to CLLocation - ios

Is there a way to convert a zip code a user enters into a textbox and convert it into a CLLocation? I am trying to compare distance between their current location and either an address or zip code and this would be easy if I can make a CLLocation out of the NSString.

the procedure called geocoding, and that is how it looks if you implement it:
NString *_address = // any address or postcode
CLGeocoder *_geocoder = [[CLGeocoder alloc] init];
[_geocoder geocodeAddressString:_address completionHandler:^(NSArray *placemarks, NSError *error) {
if (placemarks.count > 0) {
CLPlacemark *_placemark = [placemarks firstObject];
CLLocation *_location = _placemark.location;
// ... do whaterver you want to do with the location
}
}];
NOTE: the CoreLocation.framework has to be added to your project properly. you may need to handle errors in your final completion-block, I have not added such part to my code above.

Related

CLGeocoder returns null results for city and state when using current location data

Hi I am trying to get the name of city and state using CLGeocoder. However, placemark.addressDictionary is returning me :
{
FormattedAddressLines = (
"South Atlantic Ocean"
);
Name = "South Atlantic Ocean";
Ocean = "South Atlantic Ocean";
}
and placemark is:
South Atlantic Ocean, South Atlantic Ocean # <-42.60533670,-21.93128480> +/- 100.00m, region CLCircularRegion (identifier:'<-41.51023865,-31.60774370> radius 4958095.65', center:<-41.51023865,-31.60774370>, radius:4958095.65m)
Also, NSLog of [placemark locality] and [placemark administrativeArea] shows both nil.
Also tried adding ABAdressBookUI , ABAdressBook framework and get the data as:
NSString *addressDict = ABCreateStringWithAddressDictionary(placemark.addressDictionary, NO); which returns an object with empty description.
or tried:
NSString *state = (NSString *)[placemark.addressDictionary objectForKey:kABPersonAddressStateKey]; which throws warning of incompatible pointer types.
If there is any possible solution or if I am missing something please let me know.
My code when current location button is tapped is as follows:
CLLocation *loc;
loc = [[CLLocation alloc] initWithLatitude:[KLocationManager sharedManager]._locationManager.location.coordinate.latitude longitude:[KLocationManager sharedManager]._locationManager.location.coordinate.longitude];
CLGeocoder* geocoder = [[CLGeocoder alloc] init];
[geocoder reverseGeocodeLocation:loc completionHandler:^(NSArray *placemarks, NSError *error) {
CLPlacemark* placemark = [placemarks objectAtIndex:0];
if (placemark) {
NSDictionary* dic = placemark.addressDictionary;
NSString* locName = [NSString stringWithFormat:#"%#, %#", [dic objectForKey:#"City"],[dic objectForKey:#"State"]];
CLLocation* loc = placemark.location;
self.returnLocationDict = #{#"name":locName, #"location":loc};
}
}];
What am I missing here?
One way of replicating what you're experiencing is to use a CLLocation with latitude and longitude of 0,0
I ran your code by using latitude:37.3318 longitude:-122.0312 (Infinite Loop..) and it worked as expected.
Your location manager is probably not returning what you expect.

Insert Latitude and Longitude Programmatically for Reverse Geolocation

I have a Situation ,where i am pulling the Latitude and Longitude from a XML file ,Parsing and Displaying it .and i am successfully be able to Do it in Two Different UILabels .but now i need to the Location and Display it in a Label.Can someone suggest me how to achieve that , i have this code , but its only let me to select the Value of Coordinates from Simulator only .
- (IBAction)geoCodeLocation:(id)sender{
//Geocoding Block
[self.geoCoder reverseGeocodeLocation: locationManager.location completionHandler:
^(NSArray *placemarks, NSError *error) {
//Get nearby address
CLPlacemark *placemark = [placemarks objectAtIndex:0];
//String to hold address
NSString *locatedAt = [[placemark.addressDictionary valueForKey:#"FormattedAddressLines"] componentsJoinedByString:#", "];
//Print the location to console
NSLog(#"I am currently at %#",locatedAt);
//Set the label text to current location
[locationLabel setText:locatedAt];
}];
}
Suggestion : this Question is not about XML parsing .
Try This
CLLocation *location = [[CLLocation alloc] initWithLatitude:37.78583400 longitude:-122.40641700];
[self.geoCoder reverseGeocodeLocation: location completionHandler:
^(NSArray *placemarks, NSError *error) {
//Get nearby address
CLPlacemark *placemark = [placemarks objectAtIndex:0];
//String to hold address
locatedAtcountry = placemark.country;
locatedAtcity = placemark.locality;
locatedAtisocountry = placemark.ISOcountryCode;
//Print the location to console
NSLog(#"Estas en %#",locatedAtcountry);
NSLog(#"Estas en %#",locatedAtcity);
NSLog(#"Estas en %#",locatedAtisocountry);
[cityLabel setText:[NSString stringWithFormat:#"%#",locatedAtcity]];
[locationLabel setText:[NSString stringWithFormat:#"%#",locatedAtcountry]];
//Set the label text to current location
//[locationLabel setText:locatedAt];
}];

CLGeocoder ever return one placemark

I want to revive this and this question because the problem still persists for me, so I'm writing a new question.
This is my code:
- (SVGeocoder*)initWithParameters:(NSMutableDictionary*)parameters completion:(SVGeocoderCompletionHandler)block {
self = [super init];
self.operationCompletionBlock = block;
Class cl = NSClassFromString(#"CLGeocoder");
if (cl != nil)
{
if (self.geocoder_5_1 == nil) {
self.geocoder_5_1 = [[cl alloc] init];
}
NSString *address = [parameters objectForKey:kGeocoderAddress];
[self.geocoder_5_1 geocodeAddressString:address completionHandler:^(NSArray *placemarks, NSError *error) {
NSMutableArray *svplacemarks = [NSMutableArray arrayWithCapacity:1];
SVPlacemark *placemark;
NSLog(#"placemarks[count] = %i", [placemarks count]);
for (CLPlacemark *mark in placemarks) {
placemark = [[SVPlacemark alloc] initWithPlacemark:mark];
[svplacemarks addObject:placemark];
}
self.operationCompletionBlock([NSArray arrayWithArray:svplacemarks],nil,error);
}];
}
else
{
self.operationRequest = [[NSMutableURLRequest alloc] initWithURL:[NSURL URLWithString:#"http://maps.googleapis.com/maps/api/geocode/json"]];
[self.operationRequest setTimeoutInterval:kSVGeocoderTimeoutInterval];
[parameters setValue:#"true" forKey:kGeocoderSensor];
[parameters setValue:[[NSLocale currentLocale] objectForKey:NSLocaleLanguageCode] forKey:kGeocoderLanguage];
[self addParametersToRequest:parameters];
self.state = SVGeocoderStateReady;
}
return self;
}
It is my personal version (quite rough) of SVGeocoder using CLGeocoder for forward geocoding with retrocompatibility for iOS < 5.1
I use this solution because of the Google terms which prevent the use of the maps API without showing the result on a Google map.
The problem is the same one from the previously mentioned questions: CLGeocoder returns only one placemark and the log prints a nice
"placemarks[count] = 1".
My question is, does anyone know if there is another way to retrieve forward geocoding, or some other magic thing (the Apple map app shows multiple markers for the same query I do, "via roma", for example) ?
EDIT FOR ROB'S SOLUTION
Class mkLocalSearch = NSClassFromString(#"MKLocalSearch");
if (mkLocalSearch != nil)
{
NSString *address = [parameters objectForKey:kGeocoderAddress];
MKLocalSearchRequest *request = [[MKLocalSearchRequest alloc] init];
request.region = MKCoordinateRegionForMapRect(MKMapRectWorld);
request.naturalLanguageQuery = address;
MKLocalSearch *localsearch = [[MKLocalSearch alloc] initWithRequest:request];
[localsearch startWithCompletionHandler:^(MKLocalSearchResponse *response, NSError *error) {
NSMutableArray *svplacemarks = [NSMutableArray arrayWithCapacity:1];
SVPlacemark *placemark;
NSLog(#"response.mapItems[count] = %i", [response.mapItems count]);
for (MKMapItem *item in response.mapItems)
{
placemark = [[SVPlacemark alloc] initWithPlacemark:item.placemark];
[svplacemarks addObject:placemark];
}
self.operationCompletionBlock([NSArray arrayWithArray:svplacemarks],nil,error);
}];
}
This is an interesting solution that gives another point of view. Unfortunately, even if I set the region to worldwide, I still get a nice log
response.mapItems[count] = 1
The query was "via roma", which is a very common street name in Italy, so much so that I think we can find it in practically any Italian city.
Maybe I'm doing something wrong?
EDIT 2 - New Test:
convert World Rect to CLRegion, code from here
NSString *address = [parameters objectForKey:kGeocoderAddress];
// make a conversion from MKMapRectWorld to a regular CLRegion
MKMapRect mRect = MKMapRectWorld;
MKMapPoint neMapPoint = MKMapPointMake(MKMapRectGetMaxX(mRect), mRect.origin.y);
MKMapPoint swMapPoint = MKMapPointMake(mRect.origin.x, MKMapRectGetMaxY(mRect));
float ewDelta= neMapPoint.x - swMapPoint.x;
float nsDelta= swMapPoint.y - neMapPoint.y;
MKMapPoint cMapPoint = MKMapPointMake(ewDelta / 2 + swMapPoint.x, nsDelta / 2 + neMapPoint.y);
CLLocationCoordinate2D neCoord = MKCoordinateForMapPoint(neMapPoint);
CLLocationCoordinate2D swCoord = MKCoordinateForMapPoint(swMapPoint);
CLLocationCoordinate2D centerCoord = MKCoordinateForMapPoint(cMapPoint);
CLLocationDistance diameter = [self getDistanceFrom:neCoord to:swCoord];
// i don't have the map like showed in the example so i'm trying to center the search area to the hypothetical center of the world
CLRegion *clRegion = [[CLRegion alloc] initCircularRegionWithCenter:centerCoord radius:(diameter/2) identifier:#"worldwide"];
[self.geocoder_5_1 geocodeAddressString:address inRegion: clRegion completionHandler:^(NSArray *placemarks, NSError *error) {
NSMutableArray *svplacemarks = [NSMutableArray arrayWithCapacity:1];
SVPlacemark *placemark;
NSLog(#"placemarks[count] = %i", [placemarks count]);
for (CLPlacemark *mark in placemarks) {
placemark = [[SVPlacemark alloc] initWithPlacemark:mark];
[svplacemarks addObject:placemark];
}
self.operationCompletionBlock([NSArray arrayWithArray:svplacemarks],nil,error);
}];
... and I get the usual "placemark [count] = 1"
Obviously, CLGeocoder will return multiple placemarks if the address gets multiple hits (i.e. the region is large enough such that the simple street address is ambiguous), but frequently it will find just the one match if the region is small enough or if the supplied address is unique enough.
While it's not a general purpose solution, effective iOS 6.1, you have MKLocalSearch, which does a more general lookup (including names of businesses, etc.):
MKLocalSearchRequest *request = [[MKLocalSearchRequest alloc] init];
request.region = self.mapView.region;
request.naturalLanguageQuery = textField.text;
MKLocalSearch *localsearch = [[MKLocalSearch alloc] initWithRequest:request];
[localsearch startWithCompletionHandler:^(MKLocalSearchResponse *response, NSError *error) {
for (MKMapItem *item in response.mapItems)
{
Annotation *annotation = [[Annotation alloc] initWithPlacemark:item.placemark];
annotation.title = item.name;
annotation.phone = item.phoneNumber;
annotation.subtitle = item.placemark.addressDictionary[(NSString *)kABPersonAddressStreetKey];
[self.mapView addAnnotation:annotation];
}
}];
I guess it all depends upon what sort of multiple hits you're expecting to receive.
There are some addresses for which CLGeocoder does return multiple placemarks. One example I've found is "Herzel 13, Haifa, Israel". I use the geocodeAddressDictionary:completionHandler: method, and get the same 2 results for the address (it can be set either as street/city/country, or just as a street - the results are the same).
It's just pretty hard to find such examples, and they may change in the future of course. For some reason, the Apple maps app shows the "Did you mean..." dialog for many more addresses.

Get current city and country from CLGeocoder?

I've been all over the internet trying to find out how to get the city and country from CLGeocoder. I can get the longitude and latitude easily but I need the city and country information, and I keep running into deprecated methods and such, any ideas? It basically needs to get the location, then have an NSString for the country, and an NSString for the city, so I can use them to look up more info or put them on labels, etc.
You need to revise your terminology a bit - CLGeocoder (and most geocoders) won't give you a 'city' per-se - it uses terms such as 'Administrative Area', 'Subadministrative Area', etc. The CLGeocoder object will return an array of CLPlacemark objects which you can then query for the information you need. You init a CLGeocoder and call the reverseGeocodeLocation function with a location and a completion block. Here's an example:
if (osVersion() >= 5.0){
CLGeocoder *reverseGeocoder = [[CLGeocoder alloc] init];
[reverseGeocoder reverseGeocodeLocation:self.currentLocation completionHandler:^(NSArray *placemarks, NSError *error)
{
DDLogVerbose(#"reverseGeocodeLocation:completionHandler: Completion Handler called!");
if (error){
DDLogError(#"Geocode failed with error: %#", error);
return;
}
DDLogVerbose(#"Received placemarks: %#", placemarks);
CLPlacemark *myPlacemark = [placemarks objectAtIndex:0];
NSString *countryCode = myPlacemark.ISOcountryCode;
NSString *countryName = myPlacemark.country;
DDLogVerbose(#"My country code: %# and countryName: %#", countryCode, countryName);
}];
}
Now note that the CLPlacemark doesn't have a 'city' property. The full list of properties can be found here: CLPlacemark Class Reference
You can get city, country and iso country code using this (Swift 5):
private func getAddress(from coordinates: CLLocation) {
CLGeocoder().reverseGeocodeLocation(coordinates) { placemark, error in
guard error == nil,
let placemark = placemark
else
{
// TODO: Handle error
return
}
if placemark.count > 0 {
let place = placemark[0]
let city = place.locality
let country = place.country
let countryIsoCode = place.isoCountryCode
}
}
}

passing var into objective c block

I'm using the reverse geocoder and I want to save info into a SQLite database but I need the latitude and longitude set by the user but it's not being passed.
I've got two doubles var called latitude and longitude and when I execute this code below, it uses "random" doubles:
CLLocation *locate = [[CLLocation alloc] initWithLatitude:latitude longitude:longitude];
CLGeocoder *geoCoderCL = [[CLGeocoder alloc] init];
[geoCoderCL reverseGeocodeLocation: locate completionHandler:
^(NSArray *placemarks, NSError *error) {
CLPlacemark *placemark = [placemarks objectAtIndex:0];
Controller *mController = [[Controller alloc] init];
MyObject *newposition = [[Meeting alloc]init];
[newposition setLatitude:latitude];
[newposition setLongitude:longitude];
[newposition setMName:[NSString stringWithFormat:#"%# %#",placemark.thoroughfare,placemark.subThoroughfare]];
NSLog(#"%# %#",placemark.thoroughfare,placemark.subThoroughfare);
[mController createRecentPoint:newposition];
[mController release];
[newposition release];
}];
How is supposed to do this? Thanks.
Use the following to derive the latitude and longitude from a CLPlacemark object.
CLLocationDegrees latitude = placemark.location.coordinate.latitude;
CLLocationDegrees longitude = placemark.location.coordinate.longitude;
location is a CLLocation object. coordinate is a CLLocationCoordinate2D struct and latitude and longitude are CLLocationDegrees (which is just a typdef'd double).

Resources