Setting up the MKReverseGeocoderDelegate? - iphone

I am a little confused about setting up the MKReverseGeocoderDelegate, I noted from Apples example that they are assigning the MKReverseGeocoder to a property iVar, in the end I decided to alloc the MKReverseGeocoder when a position is found and release the MKReverseGeocoders as they either report back success or failure, does this seem right?
// ------------------------------------------------------------------- **
// DELEGATE: CLLocationManager
// ------------------------------------------------------------------- **
- (void)locationManager:(CLLocationManager *)manager
didUpdateToLocation:(CLLocation *)newLocation
fromLocation:(CLLocation *)oldLocation {
MKReverseGeocoder *myGeocoder = [[MKReverseGeocoder alloc] initWithCoordinate:[newLocation coordinate]];
[myGeocoder setDelegate:self];
[myGeocoder start];
}
// ------------------------------------------------------------------- **
// DELEGATE: MKReverseGeocoderDelegate
// ------------------------------------------------------------------- **
- (void)reverseGeocoder:(MKReverseGeocoder *)geocoder didFindPlacemark:(MKPlacemark *)placemark {
NSLog(#"%#", [placemark locality]);
[geocoder release];
}
- (void)reverseGeocoder:(MKReverseGeocoder *)geocoder didFailWithError:(NSError *)error {
[geocoder release];
}
#end

You're fine in terms of memory management. If you run this through the leaks tool in Instruments, it'll probably complain about a possible leak in your locationManager:didUpdate method, but it's wrong about that, it just can't see where you're releasing the object.
I would do a couple things to decrease the impact on the geocoder, though. I'd look and see that the location you just didUpdate to has an accuracy that's worth talking about. When Core Location first comes to life, you're likely to get some garbage hits before the hardware's really up and receiving. And then the accuracy will narrow in on the real location (the Google Maps app shows pretty nicely what is happening there, the blue circle zeroing in on your real location). There's probably no point in geocoding until you've got an accuracy within 100 meters or so.
EDIT: Try this
- (void)locationManager:(CLLocationManager *)manager
didUpdateToLocation:(CLLocation *)newLocation
fromLocation:(CLLocation *)oldLocation {
if((newLocation.coordinate.latitude != oldLocation.coordinate.latitude) &&
(newLocation.coordinate.longitude != oldLocation.coordinate.longitude) &&
([newLocation horizontalAccuracy] < 100 && [newLocation horizontalAccuracy] > 0))
{
MKReverseGeocoder *myGeocoder = [[MKReverseGeocoder alloc] initWithCoordinate:[newLocation coordinate]];
[myGeocoder setDelegate:self];
[myGeocoder start];
}
}
That should ignore updates that are the same as the last update (which might be fairly frequent) and those that have invalid accuracy (you'll get negative numbers there occasionally, when the location hardware is first fired up) or are over 100 meters of horizontal accuracy.
Also, if you just want to do this once, go ahead and have your CLLocationManager stop updating once you get a valid and sufficiently accurate position.

In the MKReverseGeocoder Class Reference, Apple says that
Each Map Kit application has a limited amount of reverse geocoding capacity, so it is to your advantage to use reverse geocode requests sparingly. Here are some rules of thumb for using this class most effectively:
Send at most one reverse-geocoding request for any one user action.
(...)
In your case, If there is a slow network connection (i.e. position changes are faster then location lookups), multiple requests can be instantiated.
Your code will probably work fine in most cases, however it is not the suggested way to implement it. And implementing anything that only works "in most cases" is not a goot idea at all.

Related

Can I get colocation data in underground area (like as subway, parking lot, etc..)?

I set up cllocation manage by following code. It work in the open area. However, my phone can't received any datas (all datas was 0) after I went into subway or underground area.
What should I do if I want to make app work in subway as well? Is it necessary to integrate startMonitoringSignificantLocationChanges/startMonitoringForRegion method with standard location update method together? If so, how do I make it?
In init method
locationManager=[[CLLocationManager alloc]init];
locationManager.desiredAccuracy=kCLLocationAccuracyBest;
locationManager.distanceFilter=kCLDistanceFilterNone;
locationManager.delegate=self;
[locationManager startUpdatingLocation];
In cllocation delegate method
-(void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation{
NSLog(#"%#",newLocation);
NSDate*eventDate=newLocation.timestamp;
NSTimeInterval howRecent=abs([eventDate timeIntervalSinceNow]);
if ((newLocation.coordinate.latitude != oldLocation.coordinate.latitude)&&(newLocation.coordinate.longitude != oldLocation.coordinate.longitude)) {
locationChanged=YES;
}else{
locationChanged=NO;
}
if ((howRecent <5.0)&&
((newLocation.horizontalAccuracy<(oldLocation.horizontalAccuracy -10.0))||
(newLocation.horizontalAccuracy<50.0)||
((newLocation.horizontalAccuracy<=120)&& locationChanged))) {
self.myLocation=[newLocation copy];
// NSLog(#"mylocation==%#",[self.myLocation description]);
}
//location info draw from plist
CLLocation *targetLocation = [[CLLocation alloc] initWithLatitude:Target_LAT longitude:Target_LONG];
double distance = [self.myLocation distanceFromLocation:targetLocation]/1000;
NSString*distString=[NSString stringWithFormat:#"%0.2f km",distance];
[delegate getLocationUpdate:self.myLocation distanceUpdate:distString];
//In this testing app, I use delegate to take cllocation data. Then those datas display in view controller.
}
You can get location data for a mobile device In a Couple of ways. Device based (GPS, AGPS, WIFI etc when you are in GPS line of sight ) or CELLID based (off device - mobile number based whether you are in GPS line of sight or not as this is cellID based)..as you know your app can also get location information underground(out of line of sight) if it is using cellID Keep in mind that in underground situations if there is no wifi then you are out of luck.
I would love to say that you should use geoloqal.com (me) to locate mobile devices via cellID or GPS - however, unfortunately, in the subway we still will not be able to help you.

CoreLocation startMonitoringRegion not firing didenterregion /didexitregion delegates

i cant seem to get any of the didenterregion/didexitregion delegates firing , been stuck for 2 days on it
im using xcode4.2 with ios5 sdk and location simulation,
has anyone any luck getting this to work? is a 100m radius too small?(ive tried 1000 doesnt work too) or is there something wrong with my code
checked also with normal location updating and user location indeed enters region. could not find an answer anywhere as well
MainViewController(also CLLocationManagerDelegate)'s ViewDidLoad method:
mapView = [[MKMapView alloc] initWithFrame:self.view.bounds];
mapView.showsUserLocation = YES;
mapView.mapType = MKMapTypeStandard;
mapView.delegate = self;
[self.view addSubview:mapView];
MKCoordinateRegion region;// = {{0.0,0.0},{0.0,0.0}};
MKCoordinateSpan span;
span.latitudeDelta = 0.02;
span.longitudeDelta = 0.02;
CLLocationCoordinate2D coordinates =CLLocationCoordinate2DMake(1.34537, 103.84515);
region.span = span;
region.center = coordinates;
[mapView setRegion:region animated:TRUE];
[mapView regionThatFits:region];
if (locationManager==nil){
locationManager = [[CLLocationManager alloc] init];
}
[self.locationManager setDesiredAccuracy:kCLLocationAccuracyBest];
[self.locationManager setDistanceFilter:kCLDistanceFilterNone];
self.locationManager.delegate =self;
if ([CLLocationManager regionMonitoringAvailable]){
NSLog(#"regionMonitoring available");
CLRegion* region3 = [[CLRegion alloc] initCircularRegionWithCenter:coordinates radius:100 identifier:#"region3"];
[self.locationManager startMonitoringForRegion:region3 desiredAccuracy:1.0];
NSLog(#"region = %#",region3);
NSLog(#"loc max = %f",self.locationManager.maximumRegionMonitoringDistance);
NSLog(#"location manger monitored regions = %#",self.locationManager.monitoredRegions);
also implemented all delegate methods neccessary
- (void)locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error
{
NSLog(#"didfailwitherror");
}
- (void)locationManager:(CLLocationManager *)manager
didUpdateToLocation:(CLLocation *)newLocation
fromLocation:(CLLocation *)oldLocation
{
NSLog(#"latitude %+.10f, longitude %+.10f\n",newLocation.coordinate.latitude,newLocation.coordinate.longitude);
}
-(void)locationManager:(CLLocationManager *)manager didEnterRegion:(CLRegion *)region{
NSLog(#"didenterregion");
}
-(void)locationManager:(CLLocationManager *)manager didExitRegion:(CLRegion *)region{
NSLog(#"didexitregion");
}
- (void)locationManager:(CLLocationManager *)manager monitoringDidFailForRegion:(CLRegion *)region withError:(NSError *)error
{NSLog(#"monitoringDidFailForRegion");}
thanks for any help
How have you been testing the location? The regions are a little tricky, but once they are set up, they usually work great. What I usually do to test the region is just use the custom location in the Simulator. Use the same coordinates you are adding your region. 1000M is pretty large. I've tested with regions as small as 15-20M. If you have the right coordinates, it should be fine. Try switching back and forth from Apple HQ and your custom coordinates. You should be able to see the didEnter and didExit methods trip almost instantly if you have them logged out.
The one area you may have issues with is that the callbacks might not be getting to your method. None of my regions worked until I set up my AppDelegate as the CLLocationManagerDelegate. The app delegate finally got the updates and processed them correctly. I would try adding these callbacks there first, then work your way back to your mainviewcontroller. Hope this helps.
UPDATE
Since answering this question, I have learned a few things more about region monitoring. Regions get added with a minimum region size despite what you add for default. I was told by an Apple engineer that the minimum region size is 100M. I still feel they are very accurate, but it might help to understand why it will check you in. If you need precision better than 100M, you may need to look at other options.

I can't get the gps coordinates for a persistent period of time

I have implemented the standard method of retrieving the coordinates from the gps using - (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation.
The problem is that this function is called only on initialization and not throughout the life of the program. Is this normal?
In android you would implement a listener and you would get data instantly.
Is this not the wright way how i'm doing it? If it is, what could be the problem? (btw i've checked, i don't stopUpdatingLocation)
I have a CLLocationManager inherited class named testing and initialize it
testing* cllm = [[testing alloc] init];
cllm.delegate = self;
i later start the updating
[cllm startUpdatingLocation];
self.locationManagerDelegate = delegate;
and later is called
- (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation
but after that it isn't called anymore. I need it to be called frequently so that i may calculate the distance to a certain point X from where i am.
Agreed with #Matt, without more code the best solution I can offer is this to tell it to update every time the device is moved with:
[self.locationManager setDistanceFiler:kCLDistanceFilterNone]
Update
I went through past projects and found the code I believe you are looking for assuming your location manager subclass is working properly
- (void)viewDidLoad {
[super viewDidLoad];
//Location
// create new location manager
self.locationManager = [[CLLocationManager alloc] init];
self.locationManager.delegate = self;
self.locationManager.desiredAccuracy = kCLLocationAccuracyBest;
self.locationManager.distanceFilter = kCLDistanceFilterNone;
// start location manager
[self.locationManager startUpdatingLocation];
}
-(void) distanceBetweenUserandPin {
CLLocation *currentUserLocation = [[CLLocation alloc] initWithLatitude:_currentLocation.latitude longitude:_currentLocation.longitude];
CLLocation *currentPinLocation = [[CLLocation alloc] initWithLatitude:_pinLocation.latitude longitude:_pinLocation.longitude];
CLLocationDistance distanceBetweenUserAndPinMeters = [currentUserLocation distanceFromLocation:currentPinLocation];
}
- (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation{
//This successfully saves Lat, Long Data to a point location
CLLocationCoordinate2D location = CLLocationCoordinate2DMake(newLocation.coordinate.latitude, newLocation.coordinate.longitude);
NSLog(#"%f, %f", location.latitude, location.longitude);
//This assigns the value of location to the ivar _currentLocation
_currentLocation = CLLocationCoordinate2DMake(location.latitude, location.longitude);
NSLog(#"%f, %f", _currentLocation.latitude, _currentLocation.longitude);
}
First, it seems strange to me that you would use a subclass of CLLocationManager, since I'm not sure what benefit that provides you. Assuming that's not the problem, however...
From the CLLocationManager documentation:
This method returns immediately. Calling this method causes the
location manager to obtain an initial location fix (which may take
several seconds) and notify your delegate by calling its
locationManager:didUpdateToLocation:fromLocation: method. After that,
the receiver generates update events primarily when the value in the
distanceFilter property is exceeded. Updates may be delivered in other
situations though. For example, the receiver may send another
notification if the hardware gathers a more accurate location reading.
What's happening is that it is being called once for the initial position fix, but it isn't calling again because other conditions haven't changed. If a user doesn't move anywhere, then new location data won't be provided since it will be the same as last time (with a few exceptions as mentioned in the docs).
When you're testing your app, make sure that you try moving around and changing your location to produce an update. If that doesn't work, try experimenting with the desiredAccuracy and distanceFilter properties:
You start standard location services by calling the
startUpdatingLocation method. This service is most appropriate for
applications that need more fine-grained control over the delivery of
location events. Specifically, it takes into account the values in the
desiredAccuracy and distanceFilter property to determine when to
deliver new events.
Other than that, I'd guess it might have to do with how you're subclassing CLLocationManager. Providing some of that code might help.

Getting an accurate location with CLLocationManager

I'm beginning to mess around with the CLLocationManager and I am trying to use the callback method below. I went outside to test it on my iPhone, and with the first few updates, I seem to be getting gibberish values, like all of a sudden my distance is 39meters even though I haven't moved anywhere. Or sometimes it will start at 5, and then jump to 20 meters, again without me moving anywhere. I went outside and walked, and the updates from the initial starting point seemed 'ok,' and then when I walked back, I got back to the original 39 meters starting point. I was wondering if what I am doing below is correct. I also included my viewDidLoad method where I initialize my CLLocationManager object.
Is there a way to ensure that my first values are accurate? Thanks in advance.
- (void)viewDidLoad
{
[super viewDidLoad];
locationManager = [[CLLocationManager alloc] init];
locationManager.delegate = self;
locationManager.distanceFilter = kCLDistanceFilterNone;
locationManager.desiredAccuracy = kCLLocationAccuracyBest;
}
- (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation
{
if (startingPoint == nil) {
NSLog(#"NIL starting point");
self.startingPoint = newLocation;
}
CLLocationDistance distance = [newLocation distanceFromLocation:startingPoint];
NSString *distanceString = [[NSString alloc] initWithFormat:#"%g m", distance];
distanceLabel.text = distanceString;
[distanceString release];
numberOfUpdates++;
NSString *countString = [[NSString alloc] initWithFormat:#"%d", numberOfUpdates];
countLabel.text = countString;
[countString release];
}
GPS is an imperfect technology. Atmospheric conditions, sattelite availability (and position), sky visibility, signals bouncing off nearby objects (buildings) all contribute to it's inherent inaccuracy. Though there is even an "accuracy" value (which is usually pretty good) - even this is not completely reliable.
Airplanes are not allowed to use GPS for precision approaches - even their receivers aren't accurate enough, and they require other technologies (which have their own issues).
Try running the standard "Maps" application and use it as a comparison. I think your code is good - it's jut GPS.
Of course I am saying this because I am working on my own maritime navigation application, an running into all these issues myself.
Though this is an old question I still like to answer. The first call to "didUpdateToLocation" usually is some old value and you should always check the timestamp. From the Apple documentation:
NSDate* eventDate = newLocation.timestamp;
NSTimeInterval howRecent = [eventDate timeIntervalSinceNow];
if (abs(howRecent) < 15.0)
{
// use the value
}
In your didUpdate method, you can test newLocation's .horizontalAccuracy property for an acceptable value and toss any values that are impossible (or even unlikely!).

Small Location Change Detection

I'm need to detect small location change for the iphone , I tried the sample that's called Locate Me, but it doesn't recognize the small change in the location. Is there any way for doing this?
Thanks in Advance.
Best regards
John
How small is the change that you expected?
Maybe you should set the accuracy to it's maximum and calculate the displacement on the delegate method.
Configure the location manager to give you it's best.
[locationManager setDesiredAccuracy:kCLLocationAccuracyBest];
[locationManager setDistanceFilter:0.0];
And then on the delegate method:
- (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation {
// Calculate the distance between the two locations
CGFloat distance = [newLocation distanceFromLocation:oldLocation];
if (distance >= MIN_DISPLACEMENT) {
// Do something
} else {
// Do something
}
}
To get more accurate data from the location manager, set the proper keys for UIRequiredDeviceCapabilities (location-services, gps) on the Info.plist.