Geocoder to fill UITextField - ios

I have an app that fills in the user location into a textfield, if the user allows location sharing. Everything works but my question is how do I get the results to be split up into multiple text fields.
Currently the "address" text field displays everything. All I want in that field is the street address, and another one for City, State, Zip, etc. Any suggestions would be appreciated! I've looked at the documentation for example for zip code I've tried
self.zip.text=
NSString *postalCode = [NSString stringWithFormat:#"%#",placemark.postalCode];
but that doesn't work
-(void)locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error
{
if(error.code == kCLErrorDenied)
{
self.address.text = #"Location information denied";
}
}
- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations
{
// Make sure this is a recent location event
CLLocation *newLocation = [locations lastObject];
NSTimeInterval eventInterval = [newLocation.timestamp timeIntervalSinceNow];
if(abs(eventInterval) < 30.0)
{
// Make sure the event is valid
if (newLocation.horizontalAccuracy < 0)
return;
// Instantiate _geoCoder if it has not been already
if (_geocoder == nil)
_geocoder = [[CLGeocoder alloc] init];
//Only one geocoding instance per action
//so stop any previous geocoding actions before starting this one
if([_geocoder isGeocoding])
[_geocoder cancelGeocode];
[_geocoder reverseGeocodeLocation: newLocation
completionHandler: ^(NSArray* placemarks, NSError* error)
{
if([placemarks count] > 0)
{
CLPlacemark *foundPlacemark = [placemarks objectAtIndex:0];
self.address.text =
[NSString stringWithFormat:#"%#",
foundPlacemark.description];
}
else if (error.code == kCLErrorGeocodeCanceled)
{
NSLog(#"Geocoding cancelled");
}
else if (error.code == kCLErrorGeocodeFoundNoResult)
{
self.address.text=#"No geocode result found";
}
else if (error.code == kCLErrorGeocodeFoundPartialResult)
{
self.address.text=#"Partial geocode result";
}
else
{
self.address.text =
[NSString stringWithFormat:#"Unknown error: %#",
error.description];
}
}
];
//Stop updating location until they click the button again
[manager stopUpdatingLocation];
}
}

You should check the reference page for CLPlacemark class.
There you will find the properties you need: postal code, locality ...

CLPlacemark *foundPlacemark = [placemarks lastObject];
NSString *subThoroughfare = [NSString stringWithFormat:#"%#",placemark.subThoroughfare];
NSString *thoroughfare = [NSString stringWithFormat:#"%#",placemark.thoroughfare];
NSString *postalCode = [NSString stringWithFormat:#"%#",placemark.postalCode];
NSString *country = [NSString stringWithFormat:#"%#",placemark.country];
NSString *locality = [NSString stringWithFormat:#"%#",placemark.locality];
NSString *administrativeArea = [NSString stringWithFormat:#"%#",placemark.administrativeArea];

Related

Geocoding to UITextfield not displaying

I am using reverse geocoding which works but I am trying to separate the attributes. For example I want to have zip code displayed in it's own text field.
This is what I've tried but I get a breakpoint at if([placemarks count] >0)
- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations
{
// Make sure this is a recent location event
CLLocation *newLocation = [locations lastObject];
NSTimeInterval eventInterval = [newLocation.timestamp timeIntervalSinceNow];
if(abs(eventInterval) < 30.0)
{
// Make sure the event is valid
if (newLocation.horizontalAccuracy < 0)
return;
// Instantiate _geoCoder if it has not been already
if (_geocoder == nil)
_geocoder = [[CLGeocoder alloc] init];
//Only one geocoding instance per action
//so stop any previous geocoding actions before starting this one
if([_geocoder isGeocoding])
[_geocoder cancelGeocode];
[_geocoder reverseGeocodeLocation: newLocation
completionHandler: ^(NSArray* placemarks, NSError* error)
{
if([placemarks count] > 0)
{
CLPlacemark *foundPlacemark = [placemarks objectAtIndex:0];
self.address.text =
[NSString stringWithFormat:#"%#",
foundPlacemark.description];
self.zip.text =
[NSString stringWithFormat:#"%#",
foundPlacemark.postalCode];
}
else if (error.code == kCLErrorGeocodeCanceled)
{
NSLog(#"Geocoding cancelled");
}
else if (error.code == kCLErrorGeocodeFoundNoResult)
{
self.address.text=#"No geocode result found";
}
else if (error.code == kCLErrorGeocodeFoundPartialResult)
{
self.address.text=#"Partial geocode result";
}
else
{
self.address.text =
[NSString stringWithFormat:#"Unknown error: %#",
error.description];
}
}
];
Try to handle error first by checking if its nil:
if (error) {
// handle here
NSLog(#"error: %#", error);
return; // exit statement
}
// handle code here if there was no error
and avoid strong reference cycles create a weak object of your self, when calling a object use this instnace.
__weak typeof(self) weakSelf = self;
so self.address.text=#"Partial geocode result"; will turn to weakSelf.address.text=#"some text here...";

Get latitude and longitude based on Address using Geocoder class in iOS

I got the current location based on the Longitude and Latitude values and then I also got multiple places on google map using Annotation. Now I want to get the longitude and latitude values based on the Address( i.e street,city and county).Need some guidance on how this can be achieved.
Till now, this is what I have tried:-
#import "ViewController.h"
#interface ViewController ()
#end
#implementation ViewController
#synthesize streetField = _streetField, cityField = _cityField, countryField = _countryField, fetchCoordinatesButton = _fetchCoordinatesButton, nameLabel = _nameLabel, coordinatesLabel = _coordinatesLabel;
#synthesize geocoder = _geocoder;
- (void)viewDidLoad
{
[super viewDidLoad];
_streetField.delegate=self;
_cityField.delegate=self;
_countryField.delegate=self;
// Do any additional setup after loading the view, typically from a nib.
}
- (IBAction)fetchCoordinates:(id)sender {
NSLog(#"Fetch Coordinates");
if (!self.geocoder) {
NSLog(#"Geocdoing");
self.geocoder = [[CLGeocoder alloc] init];
}
NSString *address = [NSString stringWithFormat:#"%# %# %#", self.streetField.text, self.cityField.text, self.countryField.text];
NSLog(#"GET Addres%#",address);
self.fetchCoordinatesButton.enabled = NO;
[self.geocoder geocodeAddressString:address completionHandler:^(NSArray *placemarks, NSError *error) {
NSLog(#"Fetch Gecodingaddress");
if ([placemarks count] > 0) {
CLPlacemark *placemark = [placemarks objectAtIndex:0];
NSLog(#"GET placemark%#",placemark);
CLLocation *location = placemark.location;
NSLog(#"GET location%#",location);
CLLocationCoordinate2D coordinate = location.coordinate;
self.coordinatesLabel.text = [NSString stringWithFormat:#"%f, %f", coordinate.latitude, coordinate.longitude];
NSLog(#"CoordinatesLabel%#",self.coordinatesLabel.text);
if ([placemark.areasOfInterest count] > 0) {
NSString *areaOfInterest = [placemark.areasOfInterest objectAtIndex:0];
self.nameLabel.text = areaOfInterest;
NSLog(#"NameLabe%#",self.nameLabel.text);
}
}
self.fetchCoordinatesButton.enabled = YES;
}];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
-(BOOL)textFieldShouldReturn:(UITextField *)textField
{
[textField resignFirstResponder];
return YES;
}
#end
Above code is not working to give me the latitude and longitude.Need some help on what am I doing wrong here or if I am missing on something.
Thanks in Advance.
This was very old answer, Kindly check with new Updates
EDIT:
Before using this check with iOS8 updation
NSLocationAlwaysUsageDescription
NSLocationWhenInUseUsageDescription
This is for getting lat and long based user area like street name,state name,country.
-(CLLocationCoordinate2D) getLocationFromAddressString: (NSString*) addressStr {
double latitude = 0, longitude = 0;
NSString *esc_addr = [addressStr stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
NSString *req = [NSString stringWithFormat:#"http://maps.google.com/maps/api/geocode/json?sensor=false&address=%#", esc_addr];
NSString *result = [NSString stringWithContentsOfURL:[NSURL URLWithString:req] encoding:NSUTF8StringEncoding error:NULL];
if (result) {
NSScanner *scanner = [NSScanner scannerWithString:result];
if ([scanner scanUpToString:#"\"lat\" :" intoString:nil] && [scanner scanString:#"\"lat\" :" intoString:nil]) {
[scanner scanDouble:&latitude];
if ([scanner scanUpToString:#"\"lng\" :" intoString:nil] && [scanner scanString:#"\"lng\" :" intoString:nil]) {
[scanner scanDouble:&longitude];
}
}
}
CLLocationCoordinate2D center;
center.latitude=latitude;
center.longitude = longitude;
NSLog(#"View Controller get Location Logitute : %f",center.latitude);
NSLog(#"View Controller get Location Latitute : %f",center.longitude);
return center;
}
call the method like this in viewdidload method or somewhere according to your project
[self getLocationFromAddressString:#"chennai"];
just pass this in your browser
http://maps.google.com/maps/api/geocode/json?sensor=false&address=chennai
and you will have json format with lat and lon
http://maps.google.com/maps/api/geocode/json?sensor=false&address=#"your city name here"
NSString *address = [NSString stringWithFormat:#"http://maps.google.com/maps/api/geocode/json?sensor=false&address=%# %# %#", self.streetField.text, self.cityField.text, self.countryField.text];
the usage of this method....
CLLocationCoordinate2D center;
center=[self getLocationFromAddressString:#"uthangarai"];
double latFrom=&center.latitude;
double lonFrom=&center.longitude;
NSLog(#"View Controller get Location Logitute : %f",latFrom);
NSLog(#"View Controller get Location Latitute : %f",lonFrom);
Someone looking for Swift 2.0 solution can use below :
let address = "1 Infinite Loop, CA, USA"
let geocoder = CLGeocoder()
geocoder.geocodeAddressString(address, completionHandler: {(placemarks, error) -> Void in
if((error) != nil){
print("Error", error)
}
if let placemark = placemarks?.first {
let coordinates:CLLocationCoordinate2D = placemark.location!.coordinate
coordinates.latitude
coordinates.longitude
print("lat", coordinates.latitude)
print("long", coordinates.longitude)
}
})
Try this,
NSString *address = [NSString stringWithFormat:#"%#,%#,%#", self.streetField.text, self.cityField.text,self.countryField.text];
[self.geocoder geocodeAddressString:address completionHandler:^(NSArray *placemarks, NSError *error)
{
if(!error)
{
CLPlacemark *placemark = [placemarks objectAtIndex:0];
NSLog(#"%f",placemark.location.coordinate.latitude);
NSLog(#"%f",placemark.location.coordinate.longitude);
NSLog(#"%#",[NSString stringWithFormat:#"%#",[placemark description]]);
}
else
{
NSLog(#"There was a forward geocoding error\n%#",[error localizedDescription]);
}
}
];
Swift
public func getLocationFromAddress(address : String) -> CLLocationCoordinate2D {
var lat : Double = 0.0
var lon : Double = 0.0
do {
let url = String(format: "https://maps.google.com/maps/api/geocode/json?sensor=false&address=%#", (address.addingPercentEncoding(withAllowedCharacters: .urlPathAllowed)!))
let result = try Data(contentsOf: URL(string: url)!)
let json = JSON(data: result)
lat = json["results"][0]["geometry"]["location"]["lat"].doubleValue
lon = json["results"][0]["geometry"]["location"]["lng"].doubleValue
}
catch let error{
print(error)
}
return CLLocationCoordinate2D(latitude: lat, longitude: lon)
}
I used SwiftyJSON but you can parse the JSON response however you want
- (IBAction)forwardButton:(id)sender
{
if([self.address.text length])
{
NSString *place = self.address.text;
CLGeocoder *geocoder = [[CLGeocoder alloc] init];
__unsafe_unretained RTGeoCoderViewController *weakSelf = self;
[geocoder geocodeAddressString:place completionHandler:^(NSArray* placemarks, NSError* error)
{
NSLog(#"completed");
if ( error )
{
NSLog(#"error = %#", error );
dispatch_async(dispatch_get_main_queue(),
^{
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Error" message:[self errorMessage:error.code] delegate:nil cancelButtonTitle:nil otherButtonTitles:#"OK", nil];
[alert show];
});
}
else
{
NSLog(#"%#",placemarks);
}
}];
}
}
Try this
- (void)getAddressFromAdrress:(NSString *)address withCompletationHandle:(void (^)(NSDictionary *))completationHandler {
CLGeocoder *geoCoder = [[CLGeocoder alloc] init];
//Get the address through geoCoder
[geoCoder geocodeAddressString:address completionHandler:^(NSArray *placemarks, NSError *error) {
if ([placemarks count] > 0 && !error) {
//get the address from placemark
CLPlacemark *placemark = [placemarks objectAtIndex:0];
NSString *locatedAt = [[placemark.addressDictionary valueForKey:#"FormattedAddressLines"] componentsJoinedByString:#", "];
CLLocation *location = placemark.location;
CLLocationCoordinate2D coordinate = location.coordinate;
_latitudeUserLocation = coordinate.latitude;
_longitudeUserLocation = coordinate.longitude;
NSString *postalCode = placemark.addressDictionary[(NSString*)kABPersonAddressZIPKey];
if (postalCode == nil) postalCode = #"";
if (locatedAt == nil) locatedAt = #"";
NSDictionary *dictionary = [NSDictionary dictionaryWithObjectsAndKeys:
postalCode ,kPostalCode,
locatedAt ,kFullAddress,
nil];
completationHandler(dictionary);
} else {
completationHandler(nil);
}
}];
}

Reverse geocoding appending twice - possible threading issue

So I'm doing this -
- (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation
{
if (newLocation != nil) {
currentLocation = newLocation;
}
currentLocationString = [[NSMutableString alloc] initWithString:#""];
geocoder = [[CLGeocoder alloc] init];
[geocoder reverseGeocodeLocation:currentLocation completionHandler:^(NSArray *placemarks, NSError *error) {
if (error == nil && [placemarks count] > 0) {
CLPlacemark* currentLocPlacemark = [placemarks lastObject];
NSLog(#"FORMATTED ADDR DICT : %#", currentLocPlacemark.addressDictionary);
[currentLocationString appendString: currentLocPlacemark.addressDictionary[#"Street"]];
[currentLocationString appendString: #" "];
[currentLocationString appendString: currentLocPlacemark.addressDictionary[#"City"]];
NSLog(#"%#", currentLocationString);
[currentLocationString appendString: #" "];
[currentLocationString appendString: currentLocPlacemark.addressDictionary[#"Country"]];
NSLog(#"CURRENTLOCATION STRING : %#", currentLocationString);
} else {
NSLog(#"%#", error.debugDescription);
}
} ];
[locationManager stopUpdatingLocation];
}
Sometimes the currentLocationString has two copies of the same string appended, and sometimes it does not. This seems like a threading issue - what's going on? Is there a synchronized keyword in objective C, or some way of getting around this through cocoa-touch?
It happens when reverseGeocodeLocation has not finished its execution and you receive new location update. So, in the completionHandler, you will append the string in the same variable.
To avoid it, you should create the copy of currentLocationString inside the completionHandler.

didUpdateToLocation is never called

I want to pass information about the location of the user to another function which uses it. The delegate class MyLocation is a singleton which stores this information.
But in my code the didUpdateToLocation never gets called . Should n't it be called at least once whenever startUpdatingLocation is called even if the device is stationary?
I did check if locationmanager.location.coordinate.latitude and locationmanager.location.coordinate.longitude have the right values and they do. The location services are enabled and the user permission to access location services is also granted. I am still building for iOS 5. None of the previously given solutions seem to work for me!
Can someone please give me some idea as to why it is not working?
The code is as follows:
CLLocationManager *locationManager;
CLGeocoder *geocoder;
CLPlacemark *placemark;
#implementation MyLocation {
}
+ (id)getInstance {
static MyLocation *sharedMyLocation = nil;
static int i=0;
if(i==0){
sharedMyLocation = [[MyLocation alloc] init];
i=1;
}
return sharedMyLocation;
}
- (id)init {
if (self = [super init]) {
latitude = [[NSString alloc] init];
longitude = [[NSString alloc] init];
country = [[NSString alloc] init];
admin_area = [[NSString alloc] init];
postal_code = [[NSString alloc] init];
locality = [[NSString alloc] init];
subtfare = [[NSString alloc] init];
tfare = [[NSString alloc] init];
}
return self;
}
- (void) startupdate{
if(locationManager == nil)
{
locationManager = [[CLLocationManager alloc] init];
locationManager.delegate = self;
locationManager.desiredAccuracy = kCLLocationAccuracyBest;
}
if(geocoder == nil)
geocoder = [[CLGeocoder alloc] init];
[locationManager startUpdatingLocation];
}
- (void)locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error
{
NSLog(#"didFailWithError: %#", error);
}
- (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation
{
NSLog(#"didUpdateToLocation: %#", newLocation);
CLLocation *currentLocation = newLocation;
if(currentLocation != nil){
longitude = [NSString stringWithFormat:#"%.8f", currentLocation.coordinate.longitude];
latitude = [NSString stringWithFormat:#"%.8f", currentLocation.coordinate.latitude];
}
[locationManager stopUpdatingLocation];
// Reverse Geocoding
NSLog(#"Resolving the Address");
[geocoder reverseGeocodeLocation:currentLocation completionHandler:^(NSArray *placemarks, NSError *error) {
NSLog(#"Found placemarks: %#, error: %#", placemarks, error);
if (error == nil && [placemarks count] > 0) {
placemark = [placemarks lastObject];
country = [NSString stringWithFormat:#"%#",placemark.country];
admin_area = [NSString stringWithFormat:#"%#",placemark.administrativeArea];
postal_code = [NSString stringWithFormat:#"%#",placemark.postalCode];
locality = [NSString stringWithFormat:#"%#",placemark.locality];
subtfare = [NSString stringWithFormat:#"%#",placemark.subThoroughfare];
tfare = [NSString stringWithFormat:#"%#",placemark.thoroughfare];
} else {
NSLog(#"%#", error.debugDescription);
}
} ];
}
- (NSString*) getLatitude
{
return latitude;
}
- (NSString*) getLongitude
{
return longitude;
}
- (NSString*) getCountry
{
return country;
}
- (NSString*) getAdminArea
{
return admin_area;
}
- (NSString*) getPostalCode
{
return postal_code;
}
- (NSString*) getLocality
{
return locality;
}
- (NSString*) getSubTFare
{
return subtfare;
}
- (NSString*) getTFare
{
return tfare;
}
error GPS_INTERFACE::getLatitude(char* buffer , unsigned int len)
{
id temp = [MyLocation getInstance];
[temp startupdate];
NSString* tempbuf = [temp getLatitude];
NSString *l = [NSString stringWithFormat:#"%#",tempbuf];
const char* lati= [l UTF8String];
if(strlen(lati) < len)
std::strncpy(buffer,lati,[l length]);
else
return Buffer_Insufficent;
return No_Error;
}
/* other similar getter functions! */

locationManager avoid (null) string in a Label

I have some code that find the user location and return it into a Label. In some case, the subAdministrativeArea is not available or the thoroughfare and I want to modify my string so it doesn't show (null). I tried with some if to check this but there is a problem if the (null) are more than one. Anyone has an idea about this?
Here is my current code that needs to be modified to detect if a placemark object is equal to null:
- (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation {
CLLocation *currentLocation = newLocation;
[location stopUpdatingLocation];
[geocoder reverseGeocodeLocation:currentLocation completionHandler:^(NSArray *placemarks, NSError *error) {
if (error == nil && [placemarks count] > 0) {
placemark = [placemarks lastObject];
countryDetected = placemark.ISOcountryCode;
placeLabel.text = [NSString stringWithFormat:#"%#, %#, %#, %#, %# %#", placemark.country, placemark.subAdministrativeArea, placemark.subLocality, placemark.postalCode, placemark.thoroughfare, placemark.subThoroughfare];
userPlace = [NSString stringWithFormat:#"%#, %#, %#, %#, %# %#", placemark.country, placemark.subAdministrativeArea, placemark.subLocality, placemark.postalCode, placemark.thoroughfare, placemark.subThoroughfare];
} else {
NSLog(#"%#", error.debugDescription);
}
} ];
}
try this one.. if placemark.subAdministrativeArea is nil then you can write your own string in if condition otherwise set the value of placemark.subAdministrativeArea to the string variable and assign that to UILable ...
UPDATE:
NSMutableString *strLblTexts = [[NSMutableString alloc] init];
if (placemark.country != nil) {
[strLblTexts appendString:placemark.country];
}
if (placemark.subAdministrativeArea != nil) {
if ([strLblTexts isEqualToString:#""]||[strLblTexts isEqual:nil]) {
[strLblTexts appendString:placemark.subAdministrativeArea];
}
else{
NSString *strtemp=[NSString stringWithFormat:#",%#",placemark.subAdministrativeArea];
NSLog(#">>>>>>>>>>>>> str temp :%#", strtemp);
[strLblTexts appendString:strtemp];
}
}
if (placemark.subLocality != nil) {
if ([strLblTexts isEqualToString:#""]||[strLblTexts isEqual:nil]) {
[strLblTexts appendString:placemark.subLocality];
}
else{
NSString *strtemp=[NSString stringWithFormat:#",%#",placemark.subLocality];
NSLog(#">>>>>>>>>>>>> str temp :%#", strtemp);
[strLblTexts appendString:strtemp];
}
}
Do same like another 3 field and at last set that value to the UILable like bellow...
placeLabel.text = strLblTexts;
Two options:
1) Use an NSMutableString and only append the non-nil values
2) Update your current solution so each parameter is like:
placemark.country ? placemark.country : #"", placemark.subAdministrativeArea ? placemark.subAdministrativeArea : #"", ...
Update: I didn't notice the commas in the original question. Due to those being there, your best option is option 1:
NSMutableString *label = [NSMutableString string];
if (placemark.country) {
[label appendString:placemark.country];
}
if (placemark.subAdministrativeArea) {
if (label.length) {
[label appendString:#", "];
}
[label appendString:placemark.subAdministrativeArea];
}
// and the rest

Resources