When trying to use CLGeocoder, I got this example from an online tutorial:
https://www.appcoda.com/how-to-get-current-location-iphone-user/
- (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation
{
NSLog(#"didUpdateToLocation: %#", newLocation);
CLLocation *currentLocation = newLocation;
if (currentLocation != nil) {
longitudeLabel.text = [NSString stringWithFormat:#"%.8f", currentLocation.coordinate.longitude];
latitudeLabel.text = [NSString stringWithFormat:#"%.8f", currentLocation.coordinate.latitude];
}
// 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];
addressLabel.text = [NSString stringWithFormat:#"%# %#\n%# %#\n%#\n%#",
placemark.subThoroughfare, placemark.thoroughfare,
placemark.postalCode, placemark.locality,
placemark.administrativeArea,
placemark.country];
} else {
NSLog(#"%#", error.debugDescription);
}
} ];
}
However, I don't quite understand why it takes an array of "placemarks" in the completion handler part and why we should use the last object of "placemarks" (I also see people using the first object?).
I've read through the apple document but didn't find any good explanation in terms of use:
https://developer.apple.com/library/ios/documentation/CoreLocation/Reference/CLGeocoder_class/
You are reverse-geocoding a lat/long. It might yield more than one address. If you get more than one and don't care which one you use, it's just as valid to extract the first or last placemark object in the array.
(You'll probably only get one match most of the time, in which case the first and last element are the same thing.)
Related
I want to get the true postal address from the Latitude & Longitude, however is not working well. Maybe I am note using the right APIs or etc.
For example I have the following code
var location = CLLocation(latitude:currentLocation.coordinate.latitude, longitude: currentLocation.coordinate.longitude)
CLGeocoder().reverseGeocodeLocation(location, completionHandler: {(placemarks, error) -> Void in
print(location)
if error != nil {
print("Reverse geocoder failed with error" + error!.localizedDescription)
return
}
if placemarks!.count > 0 {
let pm = placemarks![0] as! CLPlacemark
street = String(pm.thoroughfare!)
city = String(pm.locality!)
state = String(pm.administrativeArea!)
print(pm.locality)
print(pm.administrativeArea)
print(pm.thoroughfare)
self.utility.setMyLocation(dName, longitude: long, latitude: lat, street: street, city: city, state: state)
locManager.stopUpdatingLocation()
}
else {
print("Problem with the data received from geocoder")
}
})
However I am not getting the true address. For example let say if that long and lat address is
123 East Street
I get something like only East Street (with no number) or near by road.
What do I need to do inorder to get the real address?
Im not sure if this is what your looking for but its what i currently use to get a nice readable address. I also send these values to my Application delegate for referencing later if i need them. I don't have it written in Swift however just in Objective C. :/ But i think the key is reverseGeocodeLocation
#pragma mark - location Manager update and FUNCTIONS
- (void)locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error{
NSLog(#"%s Failed with Error: %#", __PRETTY_FUNCTION__, error);
}
- (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation {
NSLog(#"%s Update to Location: %#", __PRETTY_FUNCTION__, newLocation);
AppDelegate *appDelegate = [[UIApplication sharedApplication] delegate];
CLLocation *currentLocation = newLocation;
if (currentLocation != nil) {
NSString *longitude = [NSString stringWithFormat:#"%.8f", currentLocation.coordinate.longitude];
NSString *latitude = [NSString stringWithFormat:#"%.8f", currentLocation.coordinate.latitude];
NSLog(#"Longitude: %#", longitude);
NSLog(#"Latitude: %#", latitude);
// Set current values to the AppDelegate for refrencing
appDelegate.locationDetailLon = longitude;
appDelegate.locationDetailLat = latitude;
}
// Stop Location Manager to save power
[locationManager stopUpdatingLocation];
// Reverse Geocoding
NSLog(#"Translating and getting 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];
NSString *address = [NSString stringWithFormat:#"%# %#\n%# %#\n%#\n%#",
placemark.subThoroughfare, placemark.thoroughfare,
placemark.postalCode, placemark.locality,
placemark.administrativeArea,
placemark.country];
NSLog(#"Address:\n%#", address);
// Set current values to the AppDelegate for refrencing
appDelegate.locationDetailState = placemark.administrativeArea;
appDelegate.locationDetailCountry = placemark.country;
appDelegate.locationDetailCity = placemark.locality;
appDelegate.locationDetailPostCode = placemark.postalCode;
} else {
NSLog(#"%s ERROR: %#", __PRETTY_FUNCTION__, error.debugDescription);
}
} ];
}
I am trying to make my app find a user's location in an address-like form (located after the "Reverse Geocoding" comment). Below is my code:
- (IBAction)getuserlocation:(id) sender{
//Getting Location
locationManager.delegate = self;
locationManager.desiredAccuracy = kCLLocationAccuracyBest;
[locationManager startUpdatingLocation];
NSLog(address);
}
#pragma mark - CLLocationManagerDelegate
- (void)locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error
{
NSLog(#"didFailWithError: %#", error);
UIAlertView *errorAlert = [[UIAlertView alloc]
initWithTitle:#"Error" message:#"Failed to Get Your Location" delegate:nil cancelButtonTitle:#"OK" otherButtonTitles:nil];
[errorAlert show];
}
- (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];
}
// Stop Location Manager
[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];
address = [NSString stringWithFormat:#"%# %#\n%# %#\n%#\n%#",
placemark.subThoroughfare, placemark.thoroughfare,
placemark.postalCode, placemark.locality,
placemark.administrativeArea,
placemark.country];
} else {
NSLog(#"%#", error.debugDescription);
}
} ];
}
My issue is that the compiler only prints out "(null)". (The line where I do this is located in the "getuserlocation" IBAction.) I'm not sure why this is. I've tested the app on an actual iPhone 6 Plus, which unlike the simulator, has access to location services.
If anyone would be able to point out where the error/problem in my code is, causing it to print out null, I would greatly appreciate it. Thanks in advance to all who reply.
***BTW: The following lines are in my viewDidLoad section:
locationManager = [[CLLocationManager alloc] init];
geocoder = [[CLGeocoder alloc] init];
You are logging address long before you get a location and convert it to an address.
Move the NSLog(address); to just after where you actually assign a value to address.
BTW - it should be more like: NSLog(#"Address: %#, address);.
I am trying to get the user's updated location by using core location framework.It is working in simulator, but not in ipad/iphone.I am using the following code to get it. In simulator, i am using freeway drive.
-(void)locationManager:(CLLocationManager *)manager didUpdateToLocation:
(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation
{
CLLocation *currentLocation=newLocation;
NSLog(#"location : %#",currentLocation);
if(currentLocation !=nil)
{
self.latitudelabel.text = [NSString
stringWithFormat:#"%.8f",currentLocation.coordinate.latitude];
self.longitudelabel.text = [NSString
stringWithFormat:#"%.8f",currentLocation.coordinate.longitude];
}
CLLocationDistance distance = [newLocation distanceFromLocation:oldLocation];
NSLog(#"Calc.distance%f",distance);
[geocoder reverseGeocodeLocation:currentLocation completionHandler:^
(NSArray *placemarks, NSError *error)
{
if(error == nil && [placemarks count]>0)
{
placemark = [placemarks lastObject];
NSLog(#"Country %#",placemark.country);
NSLog(#"Area %#",placemark.administrativeArea);
NSLog(#"City %#",placemark.locality);
NSLog(#"Code %#",placemark.postalCode);
NSLog(#"Road %#",placemark.thoroughfare);
NSLog(#"Number %#",placemark.subThoroughfare);
self.addresslabel.text = [NSString stringWithFormat:#"%# %#
\n %# %# \n %# \n %#",
placemark.subThoroughfare, placemark.thoroughfare,
placemark.postalCode, placemark.locality,
placemark.administrativeArea,
placemark.country];
}
else
{
NSLog(#"tktk%#",error.debugDescription);
}
}];
}
Go in settings > Privacy > Location services > Your app > Always.
You can also check other alternatives posted in the following question Core Location not working in iOS 8
HiallI am using Corelocation and updating basic long, lat etc to a view as many time per second as possible, this is what I want, but I now have brought in reverseGeocodeLocation which I only want that to run / geocode the location once per minute. It too at the moment is updating as much as possible which Apple says is bad and only wants it to update once per minute.
Can someone please show me how I do this? (keep my main corelocation updates coming in as much as possible, but have my reverseGeocodeLocation run once per minute.)
I have tried multiple differen ways to limit the amount reverseGeocodeLocation runlike with a timer, if statements and dispatch_after without success, these other ways I have tried are below, I'm not sure if I need to do something like pause the code somehow while it does the Geocode or ??? etc. My corelocation code is pretty stock standard Apple example code.
Any help would be great!
//code below not showing all code but related parts etc
.h
#interface ViewController : UIViewController <CLLocationManagerDelegate>
//I set other standard property outlets for location, locationManager, labels etc
#property (nonatomic, strong) NSDate *reverseGeoLastUpdate; //my 60second reversegeoupdates
.m
#implementation ViewController {
NSTimer *timer;
}
- (void)viewDidLoad {
[super viewDidLoad];
[self startStandardUpdates];
}
- (void)startStandardUpdates {
if (nil == _locationManager)
_locationManager = [[CLLocationManager alloc] init];
geocoder = [[CLGeocoder alloc] init];
_locationManager.delegate = self;
_locationManager.desiredAccuracy = kCLLocationAccuracyBestForNavigation;
if ([self.locationManager respondsToSelector:#selector(requestWhenInUseAuthorization)]) {
[_locationManager requestAlwaysAuthorization];
}
[self.locationManager startUpdatingLocation];
}
- (void)locationManager:(CLLocationManager *)manager
didUpdateLocations:(NSArray *)locations {
CLLocation* location = [locations lastObject];
NSDate* eventDate = location.timestamp;
NSTimeInterval howRecent = [eventDate timeIntervalSinceNow];
NSLog(#"eventDate timeIntervalSinceNow = %f", [eventDate timeIntervalSinceNow]);
if (abs(howRecent) < 15.0) {
self.coordinateLat.text = [NSString stringWithFormat:#"%f", location.coordinate.latitude];
//my timer code for reverseGeocodeLocation, trying to get to to run once at the start then every 60 seconds after
//this fires once at the start because self.reverseGeoLastUpdate == nil with no errors but my views onscreen label never actually gets updated with the data from the geoserver, and also the loop does not repeat when [self.reverseGeoLastUpdate timeIntervalSinceNow] > 60???????
if( [self.reverseGeoLastUpdate timeIntervalSinceNow] > 60 || self.reverseGeoLastUpdate == nil ){
NSLog(#"Resolving the Address: Loop run %d", _amountofruns++);
[geocoder reverseGeocodeLocation:_location completionHandler:^(NSArray *placemarks, NSError *error) {
NSLog(#"Found placemarks: %#, error: %#", placemarks, error);
if (error == nil && [placemarks count] > 0) {
placemark = [placemarks lastObject];
_address1.text = [NSString stringWithFormat:#"%# %#\n%# %#\n%#\n%#\n",
placemark.subThoroughfare, placemark.thoroughfare,
//other 4 place marker calls here
];
} else {
NSLog(#"%#", error.debugDescription);
}
}];
self.reverseGeoLastUpdate = [NSDate date];
}
}
When I tried a NSTimer in place of where the "//my timer" code is in the method above but get
"Error Domain=kCLErrorDomain Code=8 "The operation couldn’t be completed".
NSTimer scheduledTimerWithTimeInterval: 60.0
target: self
selector:#selector(onTick:)
userInfo: nil
repeats:YES];
-(void)onTick:(NSTimer *)timer {
[geocoder reverseGeocodeLocation:_location completionHandler:^(NSArray *placemarks, NSError *error) {
NSLog(#"Found placemarks: %#, error: %#", placemarks, error);
if (error == nil && [placemarks count] > 0) {
placemark = [placemarks lastObject];
_address1.text = [NSString stringWithFormat:#"%# %#\n%# %#\n%#\n%#\n",
placemark.subThoroughfare, placemark.thoroughfare,
//other 4 place marker calls here
];
} else {
NSLog(#"%#", error.debugDescription);
}
}];
When I tried
dispatch_after way it fires and label gets updated but once it fires it continuously fires every frame, and couldn't work out how to get it to go once per 60 seconds.
double delayInSeconds = 60.0;
dispatch_time_t delayTime = dispatch_time(DISPATCH_TIME_NOW, delayInSeconds * NSEC_PER_SEC);
dispatch_after(delayTime, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0),
^(void){
[geocoder reverseGeocodeLocation:location completionHandler:^(NSArray *placemarks, NSError *error) {
NSLog(#"Found placemarks: %#, error: %#", placemarks, error);
if (error == nil && [placemarks count] > 0) {
placemark = [placemarks lastObject];
_address1.text = [NSString stringWithFormat:#"%# %#\n%# %#\n%#\n%#\n",
placemark.subThoroughfare, placemark.thoroughfare,
//other 4 placemarks
];
} else {
NSLog(#"%#", error.debugDescription);
}
}];
});
Any help with figuring out how to do this properly would be great
Your first code is almost correct, but you need to account for the fact that the reverse geocode will complete on a background thread and that you should only update the UI on the main queue.
if(self.reverseGeoLastUpdate == nil || [self.reverseGeoLastUpdate timeIntervalSinceNow] > 59 ) {
self.reverseGeoLastUpdate = [NSDate date];
[geocoder reverseGeocodeLocation:location completionHandler:^(NSArray *placemarks, NSError *error) {
NSLog(#"Found placemarks: %#, error: %#", placemarks, error);
if (error == nil && [placemarks count] > 0) {
placemark = [placemarks lastObject];
dispatch_async(dispatch_get_main_queue(), ^{
self.address1.text = [NSString stringWithFormat:#"%# %#\n%# %#\n%#\n%#\n",
placemark.subThoroughfare, placemark.thoroughfare,
//other 4 place marker calls here
];
});
} else {
NSLog(#"%#", error.debugDescription);
}
}];
}
}
Also, try to get out the habit of using _ instead of self to access properties unless you deliberately want to bypass a setter/getter
It shows me (null) (null),Ahmedabad Gujarat (null) (null) when following code executes.
- (void)locationManager:(CLLocationManager *)manager
didUpdateToLocation:(CLLocation *)newLocation
fromLocation:(CLLocation *)oldLocation
{
_CurrentLocation = newLocation.coordinate;
CLGeocoder * geoCoder = [[CLGeocoder alloc] init];
[geoCoder reverseGeocodeLocation:newLocation completionHandler:^(NSArray *placemarks, NSError *error)
{
for (CLPlacemark * placemark in placemarks)
{
NSString *addressTxt = [NSString stringWithFormat:#"%# %#,%# %# %# %#",
[placemark subThoroughfare],[placemark thoroughfare],
[placemark locality], [placemark administrativeArea], [placemark subLocality],[placemark subAdministrativeArea]];
NSLog(#" >> %#",addressTxt);
self.strLocation = [placemark locality];
}
}];
}
How to get other values ? Is location service will not provide these data in my area ?
And if not then any alternative to get this data ?