I'm able to retrieve the current location in my iPad application using,
CLLocation *location = [[CLLocation alloc] initWithLatitude:[latitude floatValue] longitude:[longitude floatValue]];
CLGeocoder *geocoder = [[CLGeocoder alloc] init];
[geocoder reverseGeocodeLocation:location completionHandler:^(NSArray *placemarks, NSError *error)
{
NSLog(#"-----------------Placemark is %#-----------------", placemarks);
locationLabel.text = placemarks;
}];
and the output is,
-----------------Placemark is ("South Atlantic Ocean, South Atlantic Ocean #<-42.60533670,-21.93128480> +/- 100.00m, region (identifier <-41.51023865,-31.60774370> radius 4954476.31) <-41.51023865,-31.60774370> radius 4954476.31m"
)-----------------
Can I use the same information to just get the city and the country name? instead of the long list of information?
also, the 'locationLabel.text = placemarks' gives a warning, "Incompatible pointer types assigning to 'NSString*' from 'NSArray*_strong', which I'm unable to resolve.
Yes you can.
But you doing it a little it wrong. First of all, placemarks is an array and not a string. That's why locationLabel.text = placemarks gives a warning.
Placemarks is an array of CLPlacemarks. This is because the geocoder could return multiple results for a coordinate. In the simplest condition the first item in it should be okay.
A CLPlacemark has the property addressDictionary which contains the data of this location.
You can access this data with the address property constans defined by the ABPerson header file.
For example:
Get the first placemark from the array:
CLPlacemark *place = [placemarks objectAtIndex:0];
then get the city from this placemark:
NSString *cityName = [place objectForKey: kABPersonAddressCityKey];
Don't forget to import the AVPerson header!
your can get all following place details
placeNameLabel.text = [placemarks[0] name];
addressNumberLabel.text = [placemarks[0] subThoroughfare];
addressLabel.text = [placemarks[0] thoroughfare];
neighborhoodLabel.text = [placemarks[0] subLocality];
cityLabel.text = [placemarks[0] locality];
countyLabel.text = [placemarks[0] subAdministrativeArea];
stateLabel.text = [placemarks[0] administrativeArea];
zipCodeLabel.text = [placemarks[0] postalCode];
countryLabel.text = [placemarks[0] country];
countryCodeLabel.text = [placemarks[0] ISOcountryCode];
inlandWaterLabel.text = [placemarks[0] inlandWater];
oceanLabel.text = [placemarks[0] ocean];
Related
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.
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];
}];
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.
I have this code to reverse geocoding and it work
- (void)reverseGeocoder:(MKReverseGeocoder *)geocoder didFindPlacemark:(MKPlacemark *)placemark
{
MKPlacemark * myPlacemark = placemark;
// with the placemark you can now retrieve the city name
NSString *region = [myPlacemark.addressDictionary objectForKey:(NSString*) kABPersonAddressStateKey];
NSString *city = [myPlacemark.addressDictionary objectForKey:(NSString*) kABPersonAddressCityKey];
NSString *address = [myPlacemark.addressDictionary objectForKey:(NSString*) kABPersonAddressStreetKey];
NSLog(#"region:%#", region);
NSLog(#"city:%#", city);
NSLog(#"address:%#", address);
}
It work fine but I'm not able to have "province"...what's the way to obtain province?
These are all the keys for the address dictionary:
const ABPropertyID kABPersonAddressProperty;
const CFStringRef kABPersonAddressStreetKey;
const CFStringRef kABPersonAddressCityKey;
const CFStringRef kABPersonAddressStateKey;
const CFStringRef kABPersonAddressZIPKey;
const CFStringRef kABPersonAddressCountryKey;
const CFStringRef kABPersonAddressCountryCodeKey;
Constants
kABPersonAddressProperty Identifier for the address multivalue property. Available in iOS 2.0 and later.
Declared in ABPerson.h.
kABPersonAddressStreetKey Street. Available in iOS 2.0 and later. Declared in ABPerson.h.
kABPersonAddressCityKey City. Available in iOS 2.0 and later. Declared in ABPerson.h.
kABPersonAddressStateKey State. Available in iOS 2.0 and later. Declared in ABPerson.h.
kABPersonAddressZIPKey Zip code. Available in iOS 2.0 and later. Declared in ABPerson.h.
kABPersonAddressCountryKey Country. Available in iOS 2.0 and later. Declared in ABPerson.h.
kABPersonAddressCountryCodeKey Country code. The supported values are listed in “Country Codes.” Available in iOS 2.0 and
later. Declared in ABPerson.h.
Unfortunately there isn't one for province.
In My code, I have following ways to get address and place names.
Province is State or administrativeArea
[geocoder reverseGeocodeLocation:newLocation completionHandler:^(NSArray *placemarks, NSError *error) {
if(placemarks.count){
NSDictionary *dictionary = [[placemarks objectAtIndex:0] addressDictionary];
addressLabel.Text = [dictionary valueForKey:#"Street"];
cityLabel.Text = [dictionary valueForKey:#"City"];
stateLabel.Text = [dictionary valueForKey:#"State"];
zipCodeLabel.Text = [dictionary valueForKey:#"ZIP"];
countryLabel.text = [dictionary valueForKey:#"Country"];
countryCodeLabel.text = [dictionary valueForKey:#"CountryCode"];
placeNameLabel.text = [placemarks[0] name];
addressNumberLabel.text = [placemarks[0] subThoroughfare];
addressLabel.text = [placemarks[0] thoroughfare];
neighborhoodLabel.text = [placemarks[0] subLocality];
cityLabel.text = [placemarks[0] locality];
countyLabel.text = [placemarks[0] subAdministrativeArea];
stateLabel.text = [placemarks[0] administrativeArea];
zipCodeLabel.text = [placemarks[0] postalCode];
countryLabel.text = [placemarks[0] country];
countryCodeLabel.text = [placemarks[0] ISOcountryCode];
inlandWaterLabel.text = [placemarks[0] inlandWater];
oceanLabel.text = [placemarks[0] ocean];
}
}];
You can try my open source project. It is easy to get place marks. Device-Details https://github.com/robert-yi-jones/Device-Details
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).