How can I match the place with the result of CLGeocoder? - iphone

I can get the result (like locality, ISOcountryCode, etc) by CLGeocoder's reverseGeocodeLocation:completionHandler: method successfully.
But how can I match the place with the result?
e.g.: If the city (locality) of result is Hangzhou City, I can match it simply by using
if ([placemark.locality isEqualToString:#"Hangzhou City"]) {...}
But as you know, there're millions of cities, it's impossible to get the city name one by one and hard code into my app.
So, is there any way to solve this problem? Or does there any framework exist? Or just several files contain countries & cities' name that match the CLGeocoder's result? Even fuzzy coordinate matching solution is okay (I mean, a city has its own region, and I can determine the city just by coordinate, but I still need to get every city's region area at the moment).
Deployment Target iOS5.0

Well there is a easier way, you can use the reverse GeocodeLocation to get the information of the place. You have to know this won't work in every city thought.
For more information check Apple's CLGeocoder Class Reference and Geocoding Location Data documentation.
So you can create and object that handle the service
#import <Foundation/Foundation.h>
#import <CoreLocation/CoreLocation.h>
#interface locationUtility : NSObject<CLLocationManagerDelegate>{
CLLocationManager *locationManager;
CLPlacemark *myPlacemark;
CLGeocoder * geoCoder;
}
#property (nonatomic,retain) CLLocationManager *locationManager;
#end
and the implementation
#import "locationUtility.h"
#implementation locationUtility
#synthesize locationManager;
#pragma mark - Init
-(id)init {
NSLog(#"locationUtility - init");
self=[super init];
locationManager = [[CLLocationManager alloc] init];
locationManager.delegate = self;
locationManager.desiredAccuracy = kCLLocationAccuracyBest;
locationManager.distanceFilter = kCLDistanceFilterNone;
[locationManager startMonitoringSignificantLocationChanges];
geoCoder= [[CLGeocoder alloc] init];
return self;
}
- (void) locationManager:(CLLocationManager *) manager didUpdateToLocation:(CLLocation *) newLocation
fromLocation:(CLLocation *) oldLocation {
[geoCoder reverseGeocodeLocation:newLocation completionHandler:^(NSArray *placemarks, NSError *error) {
CLPlacemark *placemark = [placemarks objectAtIndex:0];
myPlacemark=placemark;
// Here you get the information you need
// placemark.country;
// placemark.administrativeArea;
// placemark.subAdministrativeArea;
// placemark.postalCode];
}];
}
-(void) locationManager:(CLLocationManager *) manager didFailWithError:(NSError *) error {
NSLog(#"locationManager didFailWithError: %#", error.description);
}
#end

Related

Posting current location to rails from iPhone

I am building a rails-backed iphone app that uses AFNetworking to create posts at specific locations. So the post model has lat/lng parameters that should be filled in with the client's current location.
At this point, the posts can be made, but the lat/lng comes up as null.
In my (save:) method I pass a conditional to see if a location was found- this is what is failing i.e. "No Location" is logged.
- (void)save:(id)sender {
[self getLocation];
NSArray *locations;
CLLocation *location = [locations objectAtIndex:0];
Post *post = [[Post alloc] init];
post.content = self.contentTextView.text;
post.photoData = UIImagePNGRepresentation(self.imageView.image);
[self.view endEditing:YES];
ProgressView *progressView = [ProgressView presentInWindow:self.view.window];
if (location) {
[post savePostAtLocation:location withBlock:^(CGFloat progress) {
[progressView setProgress:progress];
} completion:^(BOOL success, NSError *error) {
[progressView dismiss];
if (success) {
[self.navigationController popViewControllerAnimated:YES];
} else {
NSLog(#"ERROR: %#", error);
}
}];
} else {
NSLog(#"No Location");
}
}
I have also attempted to implement a locationManager like so
-(void)locationManager:(CLLocationManager *)manager
didUpdateLocations:(NSArray *)locations {
[self getLocation];
}
-(CLLocation *) getLocation{
CLLocationManager * locationManager = [[CLLocationManager alloc] init];
locationManager.delegate = self;
self.locationManager.desiredAccuracy = kCLLocationAccuracyHundredMeters;
self.locationManager.distanceFilter = 80.0f;
[locationManager startUpdatingLocation];
CLLocation * location = [locationManager location];
return location;
}
I think ideally I would implement the savePostAtlocation in the CLLocationManagerDelegate where I could pass in the locations array like this:
- (void)locationManager:(CLLocationManager *)manager
didUpdateLocations:(NSArray *)locations
{
CLLocation *location = [locations objectAtIndex:0 /* firstObject */];
if (location) {
[Post createPostAtLocation:location...
But I want to have the post created onSave so I am trying to identify the location but running into some problems..
How do I properly get the current location and pass it into the dictionary?
Any advice on this would be greatly appreciated. Thanks!
Looking at your code, I think you have a slight misunderstanding about how CLLocationManager is designed to work. It looks like you are trying to call [self getLocation] from inside locationManager:didUpdateLocations. This is not correct. Try something like this, inside your save method that is called when you press your button (I would remove the code that's currently in there while testing):
CLLocationManager * locationManager = [[CLLocationManager alloc] init];
locationManager.delegate = self;
self.locationManager.desiredAccuracy = kCLLocationAccuracyHundredMeters;
self.locationManager.distanceFilter = 80.0f;
[locationManager startUpdatingLocation];
Then it will start generating location data. When that happens, the phone will automatically call locationManager:didUpdateLocations very rapidly. Then, in locationManager:didUpdateLocations you could use:
CLLocation * location = [manager location];
NSLog(#"%#", location);
To see your location data in the console.
What I have written here should get the phone generating location data for you. What you say about createPostAtLocation: in locationManager:didUpdateLocations is probably the correct way to go. When you get the location data, call [manager stopUpdatingLocation] to make the phone stop, then post the location data you got back to your server.

How to get current Location info using wifi or cellular without using GPS in ios

Currently I am working in a project , its requirement is to get the current location information specially latitude and longitude value in every 200m interval using wifi network or Cellular network without using gps as it is consuming more battery life.
Is this possible in ios latest version .
If any one having any idea ,please share with me ,
Thank you.
Have a look in to CLLocationManager, That will be able to tell you where the user is located.
.h
#import <CoreLocation/CoreLocation.h>
#property(nonatomic,retain) CLLocationManager *locationManager;
.m
- (void)viewDidLoad
{
locationManager = [[CLLocationManager alloc] init];
locationManager.delegate = self;
//let the user know the purpose
locationManager.purpose = #"Enable location services";
locationManager.distanceFilter = kCLDistanceFilterNone;
locationManager.desiredAccuracy = kCLLocationAccuracyHundredMeters; // 100 m
NSLog(#"User latitude: %f",locationManager.location.coordinate.latitude);
NSLog(#"User longitude: %f",locationManager.location.coordinate.longitude);
[locationManager startUpdatingLocation];
}
It only way to get your location info in every 200m that is CLLocationManager's startUpdatingLocation. But it is comsuming a lot of battery.
But there is a little different way to get your location when it is changed.
CLLocationManager's startMonitoringSignificantLocationChanges.
Here is a Link
The location manager protocol reference
https://developer.apple.com/library/mac/#documentation/CoreLocation/Reference/CLLocationManagerDelegate_Protocol/CLLocationManagerDelegate/CLLocationManagerDelegate.html
1.In Appdelegate
#import <CoreLocation/CoreLocation.h>
In #interface file
CLLocationManager *locationManager;
#property (nonatomic, retain) CLLocationManager *locationManager;
and add protocol CLLocationManagerDelegate protocol.
2.Impliment these functions in .m.
#synthesize locationManager;
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.locationManager = [[[CLLocationManager alloc] init] autorelease];
self.locationManager.delegate = self;
self.locationManager.desiredAccuracy = kCLLocationAccuracyBest;
self.locationManager.distanceFilter = 1.0;
[self.locationManager startUpdatingLocation];
}
- (void)locationManager:(CLLocationManager *)manager didEnterRegion:(CLRegion *)region
{
// Show an alert or otherwise notify the user
}
- (void)locationManager:(CLLocationManager *)manager didExitRegion:(CLRegion *)region
{
}
- (void)locationManager:(CLLocationManager *)manager monitoringDidFailForRegion:(CLRegion *)region withError:(NSError *)error
{
}
Note:If you want to debug first set current location in simulator
At Debug--->Location--->Custom Location.
its requirement is to get the current location information specially latitude and longitude value in every 200m interval using wifi network or Cellular network without using gps as it is consuming more battery life
The documentation for CLLocationManager has this to say about distance and the GPS hardware:
... setting the desired accuracy for location events to one kilometer gives the location manager the flexibility to turn off GPS hardware and rely solely on the WiFi or cell radios.
For less than 200 meters you'll probably need to roll-your-own solution here.
- (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation {
[locationManager stopUpdatingLocation];
[locationManager stopMonitoringSignificantLocationChanges];
[self performSelector:#selector(stopUpadateLocation)];
CLLocation *location = [locationManager location];
CLLocationCoordinate2D coord;
coord=[location coordinate];
NSLog(#"coord %f %f", coord.latitude, coord.longitude);
NSString *urlString = [NSString stringWithFormat:#"http://maps.google.com/maps/geo?q=%f,%f&output=json", newLocation.coordinate.latitude, newLocation.coordinate.longitude];
NSURL *url = [NSURL URLWithString:urlString];
NSString *locationString = [NSString stringWithContentsOfURL:url encoding:NSUTF8StringEncoding error:nil];
NSDictionary *dic=[locationString JSONValue];
NSLog(#"locationString:%#",locationString );
[strAddr setString:[AppUtility removeNull:[NSString stringWithFormat:#"%#",[[[dic valueForKey:#"Placemark"] objectAtIndex:0] valueForKey:#"address"]]]];
[txtNear setText:strAddr];
}
- (void)startUpdateLocation{
[locationManager startUpdatingLocation];
}
- (void)stopUpadateLocation{
[locationManager stopUpdatingLocation];
[locationManager stopMonitoringSignificantLocationChanges];
}
You have to use the Core Location method startMonitoringSignificantLocationChanges which uses only the wifi or cellular networks!

Calculating exact distance in iPhone

I am trying to calculate distance from start using Core Location framework, but when i put the Application on an iPhone device, the data is not correct. Distance keeps on fluctuating and showing random data. Kindly help me out. Also, Altitude is showing zero.
- (void)locationManager:(CLLocationManager *)manager
didUpdateToLocation:(CLLocation *)newLocation
fromLocation:(CLLocation *)oldLocation
{
//Altitude
if(startingPoint==nil)
self.startingPoint=newLocation;
NSString *currentAltitude = [[NSString alloc] initWithFormat:#"%g",
newLocation.altitude];
heightMesurement.text=currentAltitude;
[currentAltitude release];
//Distance
if(startingPoint==nil)
self.startingPoint=newLocation;
//if(newLocation.horizontalAccuracy <= 100.0f) {
// [locationManager stopUpdatingLocation];
//}
//test start.......................................................
//startlocation
NSString *sp = [[NSString alloc]
initWithFormat:#"%f",startingPoint];
NSLog(#"\nStarting point=%#",startingPoint);
ssp.text=sp;
//endlocation
NSString *ep = [[NSString alloc]
initWithFormat:#"%f",newLocation];
NSLog(#"\nStarting point=%#",newLocation);
eep.text=ep;
//test end............................................................
CLLocationDistance mydistance=[newLocation distanceFromLocation:startingPoint];
NSString *tripString = [[NSString alloc]
initWithFormat:#"%f",mydistance];
distLabel.text=tripString;
[tripString release];
//test........................
[sp release];
[ep release];
}//Location Manager ends..
//Time interval of 3 sec....
-(void)locationUpdate:(NSTimer*)timer{
if(timer != nil) [timer invalidate];
[self.locationManager startUpdatingLocation];
}
As to why your altitude may be zero, please see this answer to a similar question.
This may just be a problem with your NSLog statements, but both the starting and ending points are printed out with NSLog statements that say
NSLog(#"\nStarting point=%#",
The way you seem to have scheduled a 3-second timer is not really the way iOS wants you to use CLLocationManager. The preferred way is to tell CLLocationManager what your location criterion are, and then just start it updating. You don't actually need to keep telling it to start updating every 3 seconds. You can just do it once, and then if you ever decide you don't need any more updates, then call
[locationManager stopUpdatingLocation];
If the OS has no new location information, it probably doesn't make sense to keep asking. It'll tell you when it has new location information, via locationManager:didUpdateToLocation:fromLocation. So, I would recommend starting the process more like this:
CLLocationManager* locationManager = [[CLLocationManager alloc] init];
if ([locationManager locationServicesEnabled]) {
Reachability* netStatus = [Reachability sharedReachability];
if (([netStatus internetConnectionStatus] != NotReachable) || ([netStatus localWiFiConnectionStatus] != NotReachable)) {
locationManager.delegate = self;
locationManager.desiredAccuracy = kCLLocationAccuracyBestForNavigation;
locationManager.distanceFilter = 100.0; // 100 m, or whatever you want
[locationManager startUpdatingLocation];
} else {
// TODO: error handling
}
}
self.locMgr = locationManager;
[locationManager release];
The location manager will often deliver you multiple results, with increasing accuracy as it hones in on your location. If you're continually restarting it, I'm wondering if that's causing it problems.

How to make forced update of user location?

I need to get current user location but I don't know if it has changed or not. Can I request a forced location update from CLLocationManager? Or is there any other way to do that?
Stopping and restarting the LocationManager should force the device to re-acquire an initial position.
[locationManager stopUpdatingLocation];
[locationmanager startUpdatingLocation];
I have had the same problem in one of my apps. I actually had to change the app structure.
This is what I have done:
This class has a public method. -(void)locateMe; an abstract class needs to instantiate this class and run the locateMe then when userIsLocated a notification will be broadcasted. and another method can get the result coordinates from (CLLocation *)currentLocation;
#import <Foundation/Foundation.h>
#import <MapKit/MapKit.h>
#interface ManageUserLocation : NSOperation <CLLocationManagerDelegate> {
CLLocationManager *locationManager;
CLLocation *currentLocation;
}
#property (nonatomic, retain) CLLocationManager *locationManager;
#property (nonatomic, retain) CLLocation *currentLocation;
-(void) locateMe;
#end
in the .m
#import "ManageUserLocation.h"
#implementation ManageUserLocation
#synthesize locationManager;
#synthesize currentLocation; // Other classes use this to get the coordination even better you can make another method that even dont get the direct access to currentLocation. It is up to you.
- (id)init
{
self = [super init];
if (self) {
//[self locateMe]; // Just a hook if you need to run it
}
return self;
}
-(void) locateMe {
self.locationManager = nil;
self.locationManager = [[CLLocationManager alloc] init];
self.locationManager.desiredAccuracy=kCLLocationAccuracyBest;
self.locationManager.delegate = self;
[self.locationManager startUpdatingLocation];
}
- (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation {
// User location has been found/updated, load map data now.
[self.locationManager stopUpdatingLocation];
currentLocation = [newLocation copy];
// WooHoo Tell everyone that you found the userLocation
[[NSNotificationCenter defaultCenter] postNotificationName:#"userLocationIsFound" object:nil];
}
- (void)locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error{
// Failed to find the user's location. This error occurs when the user declines the location request or has location servives turned off.
NSString * errorString = #"Unable to determine your current location.";
UIAlertView * errorAlert = [[UIAlertView alloc] initWithTitle:#"Error Locating" message:errorString delegate:self cancelButtonTitle:#"OK" otherButtonTitles:nil];
[errorAlert show];
[errorAlert release];
[locationManager stopUpdatingLocation];
}
- (void)dealloc { #warning dont forget this :) }
#end
Hope this help.
I think is contrary to Apple Guidelines to force updates.
Apple says geo-update will occur automatically, but at un unknown time.

IPhone - MKReverseGeocoder - how to make sure I have the result back

what i'm trying to reach is to display annotation with the city name.
So I have a class MapPoint :
#interface MapPoint : NSObject<MKAnnotation,MKReverseGeocoderDelegate> {
NSString* title;
NSString* cityName;
CLLocationCoordinate2D coordinate;
MKReverseGeocoder* reverseGeo;
}
#property (nonatomic,readonly) CLLocationCoordinate2D coordinate;
#property (nonatomic,copy) NSString* title;
#property (nonatomic,copy) NSString* cityName;
-(id) initWithCoordinate:(CLLocationCoordinate2D)c tilte:(NSString*)t;
#end
I implemented it like this :
#implementation MapPoint
#synthesize title,coordinate,cityName;
-(id) initWithCoordinate:(CLLocationCoordinate2D)c tilte:(NSString*)t
{
[super init];
coordinate = c;
reverseGeo = [[MKReverseGeocoder alloc] initWithCoordinate:c];
reverseGeo.delegate = self;
[reverseGeo start];
[self setTitle:t];
return self;
}
- (void)reverseGeocoder:(MKReverseGeocoder *)geocoder didFindPlacemark:(MKPlacemark *)placemark
{
NSString* city = [placemark.addressDictionary objectForKey:(NSString*)kABPersonAddressCityKey];
NSString* newString = [NSString stringWithFormat:#"city-> %#",city];
[self setTitle:[title stringByAppendingString:newString]];
}
-(void)reverseGeocoder:(MKReverseGeocoder *)geocoder didFailWithError:(NSError *)error{
NSLog(#"error fetching the placemark");
}
-(void)dealloc
{
[reverseGeo release];
[cityName release];
[title release];
[super dealloc];
}
#end
Then, in my CoreLocation delegate I use MapPoint like that:
-(void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation
{
MapPoint* mp = [[MapPoint alloc] initWithCoordinate:[newLocation coordinate] tilte:[locationTitleField text]];
[mapView addAnnotation:mp];
[mp release];
}
Now, I have 2 issues I'm not sure of :
Is it correct to put reverseGeo as a data member , or a better option would be to just
alloc it inside the initializer and release it inside the didFindPlacemark/didFailWithError delegates(is it even possible to release it there) ?
How can I make sure then when my annotation get displayed I know for sure that the reverseGeo came back with an answer (placemark or error - whatever it is).
Maybe it's just wrong to wait for network response and I should leave it like that - I'm just not sure then when/if network response will arrive it will update the annotationView within the MapView accordingly.
Please elaborate as much as you can.
Thanks
It's fine to store it as a data member.
It looks like you're leaving a trail of annotations for the user's current location? If you're supplementing a regular user's-current-location annotation with a "bread crumb trail" showing where the user has been, then you need to wait to add the point to the map until you get the annotation back (if that's the behavior you want). I would either do that by making the class that manages your map be the MKReverseGeocoder delegate (and have it set the title property and then add the annotation to the map in reverseGeocoder:didFindPlacemark) or add a map reference to your MapPoint class and have it add itself to the map in the same callback.
By the way, the documentation for MKReverseGeocoder includes the following text:
When you want to update the location automatically (such as when the user is moving), reissue the reverse-geocoding request only when the user's location has moved a significant distance and after a reasonable amount of time has passed. For example, in a typical situation, you should not send more than one reverse-geocode request per minute.