views:

95

answers:

1

Has anyone nailed this down yet? I've read a lot of forum postings and I still can't tell if this is a settled question...

Given that didUpdateToLocation() can return cached or inaccurate information how do you tell when you have an accurate fix?

When you set desired accuracy to kCLLocationAccuracyNearestTenMeters, HundredMeters, Kilometer, ThreeKilometers, it seems obvious that you can compare the horizontal accuracy of points returned against the desired accuracy to decide when to accept a point.

But the values of kCLLocationAccuracyBestForNavigation and kCLLocationAccuracyBest are -2 and -1 respectively so how do I know when I have the desired accuracy??

I currently check the age of the fix and reject any fix that is too "old" timewise. I then check to see if the horizontal accuracy is negative and reject that if it is. if I get a positive verticalAccuracy then I generally use that fix since the corresponding horizontal accuracy is also good.

But these checks were used when I has looking for accuracy in the 10-100s of meters... I don't know what if anything to check when I'm running ...AccuracyBestForNavigation or ...AccuracyBest?? Am I suppose to level the locationManager running constantly and never expect to shut it off and don't filter any results??

Anyone wiser than I with an answer??

Thanks

+2  A: 

It's not really a definitive, perfect algorithm (I doubt there is one for this task, because of external conditions, I mean, you can try get ur location on plain field or inside a tomb), it's an ad-hoc one, and it works for me.

I did a wrapper for LocationManager, like

@protocol LocationManagerWrapperDelegate <NSObject>

@required

- (void) locationUpdated: (CLLocation *) locationUpdate;
- (void) errorOccured: (NSError *) error;

@end

@interface LocationManagerWrapper : NSObject <CLLocationManagerDelegate>
{
    CLLocationManager *locationManager;
    id delegate;
    CLLocation *mostAccurateLocation;
    int updatesCounter;
    BOOL m_acceptableTimePeriodElapsed;
}

@property (nonatomic, retain) CLLocationManager *locationManager;
@property (nonatomic, retain) CLLocation *mostAccurateLocation;
@property (nonatomic, assign) id <LocationManagerWrapperDelegate> delegate;

- (void) startUpdatingLocation;

- (void) locationManager: (CLLocationManager *) manager
    didUpdateToLocation: (CLLocation *) newLocation
           fromLocation: (CLLocation *) oldLocation;

- (void) locationManager: (CLLocationManager *) manager
   didFailWithError: (NSError *) error;

+ (LocationManagerWrapper *) sharedInstance;

@end

Implementation

#define NUMBER_OF_TRIES 4   
#define ACCEPTABLE_TIME_PERIOD 15.0

- (void) startUpdatingLocation
{
NSAssert(self.delegate != nil, @"No delegate set to receive location update.");

updatesCounter = 0;
self.mostAccurateLocation = nil;
m_acceptableTimePeriodElapsed = NO;
[NSTimer scheduledTimerWithTimeInterval:ACCEPTABLE_TIME_PERIOD
                                 target:self
                               selector:@selector(acceptableTimePeriodElapsed:) 
                               userInfo:nil
                                repeats:NO];
[self.locationManager startUpdatingLocation];
}

- (void) acceptableTimePeriodElapsed: (NSTimer *) timer
{
@synchronized(self)
{
    m_acceptableTimePeriodElapsed = YES;
    // TODO: if period is set by user - check we have mostAccurateLocation at this point
    [self.delegate locationUpdated:self.mostAccurateLocation];
    [self.locationManager stopUpdatingLocation];
}
}

- (void) locationManager: (CLLocationManager *) manager
    didUpdateToLocation: (CLLocation *) newLocation
           fromLocation: (CLLocation *) oldLocation
{
@synchronized(self)
{
    if (m_acceptableTimePeriodElapsed) return;
    NSLog([NSString stringWithFormat:@"lat: %@, long: %@, acc: %@", 
           [ [NSNumber numberWithDouble:newLocation.coordinate.latitude] stringValue],
           [ [NSNumber numberWithDouble:newLocation.coordinate.longitude] stringValue],
           [ [NSNumber numberWithDouble:newLocation.horizontalAccuracy] stringValue]  ] );

    updatesCounter++;
    // ignore first returned value
    if (updatesCounter <= 1) return;
    if (self.mostAccurateLocation == nil ||
        self.mostAccurateLocation.horizontalAccuracy > newLocation.horizontalAccuracy)
    {
        self.mostAccurateLocation = newLocation;
    }
    if  (updatesCounter >= NUMBER_OF_TRIES)
    {
        [self.delegate locationUpdated:self.mostAccurateLocation];
        [self.locationManager stopUpdatingLocation];
    }
}
}

The code is not excellent (neither formatting is), but the idea, I think, is simple and clear, get first location, throw it out, it's a cached one, do 3 tries max for a the most accurate location. It can take a long time, and if user is waiting (as in my case), define a time limit. One more time, it works for my app, but feel free to tweak it or take another approach.

fspirit
ok this is more or less what I came up with as well. I don't have it wrapped in a class but it looks like I covered everything you implemented. Thanks for an answer.
morgman
@morgman you can vote it up then ;)
fspirit