2012-01-08 10 views
16

Sto provando a creare un'app iOS che visualizzi la distanza totale percorsa durante la corsa o la marcia. Ho letto e riletto tutta la documentazione che riesco a trovare, ma ho difficoltà a trovare qualcosa che mi dia una distanza totale precisa.Problemi nel calcolare la distanza totale di camminata/corsa totale con CLLocationManager

Rispetto a Nike + GPS o RunKeeper, la mia app segnala costantemente una distanza più breve. In un primo momento, riferiranno lo stesso, ma man mano che procedo, i valori della mia app e di altre app in esecuzione vanno alla deriva.

Ad esempio, se percorro3,3 chilometri (verificati dal contachilometri della mia auto), Nike + GPS e RunKeeper segnalano ogni volta ~ .3 chilometri, ma la mia app riporterà ~ 0,1 chilometri. newLocation.horizontalAccuracy è costantemente 5.0 o 10.0.

Ecco il codice che sto utilizzando. Mi manca qualcosa di ovvio? Qualche idea su come potrei migliorare questo per ottenere una lettura più accurata?

#define kDistanceCalculationInterval 10 // the interval (seconds) at which we calculate the user's distance 
#define kNumLocationHistoriesToKeep 5 // the number of locations to store in history so that we can look back at them and determine which is most accurate 
#define kValidLocationHistoryDeltaInterval 3 // the maximum valid age in seconds of a location stored in the location history 
#define kMinLocationsNeededToUpdateDistance 3 // the number of locations needed in history before we will even update the current distance 
#define kRequiredHorizontalAccuracy 40.0f // the required accuracy in meters for a location. anything above this number will be discarded 

- (id)init { 
    if ((self = [super init])) { 
     if ([CLLocationManager locationServicesEnabled]) { 
      self.locationManager = [[CLLocationManager alloc] init]; 
      self.locationManager.delegate = self; 
      self.locationManager.desiredAccuracy = kCLLocationAccuracyBestForNavigation; 
      self.locationManager.distanceFilter = 5; // specified in meters 
     } 

     self.locationHistory = [NSMutableArray arrayWithCapacity:kNumLocationHistoriesToKeep]; 
    } 

    return self; 
} 

- (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation { 
    // since the oldLocation might be from some previous use of core location, we need to make sure we're getting data from this run 
    if (oldLocation == nil) return; 
    BOOL isStaleLocation = [oldLocation.timestamp compare:self.startTimestamp] == NSOrderedAscending; 

    [self.delegate locationManagerDebugText:[NSString stringWithFormat:@"accuracy: %.2f", newLocation.horizontalAccuracy]]; 

    if (!isStaleLocation && newLocation.horizontalAccuracy >= 0.0f && newLocation.horizontalAccuracy < kRequiredHorizontalAccuracy) { 

     [self.locationHistory addObject:newLocation]; 
     if ([self.locationHistory count] > kNumLocationHistoriesToKeep) { 
      [self.locationHistory removeObjectAtIndex:0]; 
     } 

     BOOL canUpdateDistance = NO; 
     if ([self.locationHistory count] >= kMinLocationsNeededToUpdateDistance) { 
      canUpdateDistance = YES; 
     } 

     if ([NSDate timeIntervalSinceReferenceDate] - self.lastDistanceCalculation > kDistanceCalculationInterval) { 
      self.lastDistanceCalculation = [NSDate timeIntervalSinceReferenceDate]; 

      CLLocation *lastLocation = (self.lastRecordedLocation != nil) ? self.lastRecordedLocation : oldLocation; 

      CLLocation *bestLocation = nil; 
      CGFloat bestAccuracy = kRequiredHorizontalAccuracy; 
      for (CLLocation *location in self.locationHistory) { 
       if ([NSDate timeIntervalSinceReferenceDate] - [location.timestamp timeIntervalSinceReferenceDate] <= kValidLocationHistoryDeltaInterval) { 
        if (location.horizontalAccuracy < bestAccuracy && location != lastLocation) { 
         bestAccuracy = location.horizontalAccuracy; 
         bestLocation = location; 
        } 
       } 
      } 
      if (bestLocation == nil) bestLocation = newLocation; 

      CLLocationDistance distance = [bestLocation distanceFromLocation:lastLocation]; 
      if (canUpdateDistance) self.totalDistance += distance; 
      self.lastRecordedLocation = bestLocation; 
     } 
    } 
} 

risposta

11

Come risulta, il codice che ho postato sopra funziona alla grande. Il problema si è verificato in una parte diversa della mia app. Stavo accidentalmente convertendo la distanza da metri a miglia, anziché da metri a chilometri. Oops!

In ogni caso, si spera che il mio post abbia ancora qualche merito, dal momento che ritengo sia un esempio abbastanza solido di come monitorare la distanza di un utente con Core Location.

+0

Non dimenticare di accettarlo come risposta per non farlo apparire come una domanda aperta. Bel esempio, a proposito. –

+0

Ciao Daniel, sto recuperando lo stesso problema da più di una settimana e ancora non ho trovato alcuna soluzione. Quindi, puoi aiutarmi? –

+0

@Daniel .... Aiutatemi, per favore. –

0

Probabilmente hai impostato kRequiredHorizontalAccuracy troppo basso. Se nella cronologia non è presente alcuna posizione con precisione < kRequiredHorizontalAccuracy, ignorare tutti questi punti e aggiungere 0 alla distanza.

+0

Purtroppo non penso che lo farà. La prima ragione è che sto registrando l'orizzontaleAccuracy, e rimane abbastanza uniformemente a 10.0 o 5.0. Inoltre, anche se i punti> kRequiredHorizontalAccuracy vengono ignorati, non appena avrò una posizione valida, la confronterò con l'ultima posizione valida per ottenere una distanza, quindi non sto gettando via i dati sulla distanza. – Daniel

+0

@Daniel ... e l'ultima posizione valida (lastRecordedLocation) è nuovaLocazione nel caso in cui non vi sia una buona precisione nell'elenco. Il che significa che reimpostate l'ultima posizione registrata senza aggiungere distanza. A proposito, 10 e 5 sono piuttosto bassi. Ottengo accuratezza 100-150 abbastanza regolarmente nei miei test. – fishinear

+0

Grazie per dare un'occhiata. lastRecordedLocation dovrebbe essere solo zero se una posizione valida non è stata ancora registrata, poiché ho impostato self.lastRecordedLocation = bestLocation nella parte inferiore. Grazie anche per il suggerimento circa l'accuratezza 100-150. Farò qualche altro test ... potrebbe essere necessario il mio kRequiredHorizontalAccuracy da 40. – Daniel