2012-01-08 17 views
16

Estoy intentando crear una aplicación iOS que muestre la distancia total recorrida al correr o caminar. He leído y releído toda la documentación que puedo encontrar, pero tengo problemas para encontrar algo que me brinde una distancia total precisa.Tiene problemas para calcular la distancia total precisa para caminar/correr usando CLLocationManager

En comparación con Nike + GPS o RunKeeper, mi aplicación informa consistentemente una distancia más corta. Inicialmente reportarán lo mismo, pero a medida que sigo avanzando, los valores de mi aplicación frente a otras aplicaciones en ejecución gradualmente van a la deriva.

Por ejemplo, si ando .3 kilómetros (verificado por el cuentakilómetros de mi coche), Nike + GPS y RunKeeper ambos informan ~ .3 kilómetros cada vez, pero mi aplicación informará ~ .13 kilómetros. newLocation.horizontalAccuracy es consistentemente 5.0 o 10.0.

Aquí está el código que estoy usando. ¿Me estoy perdiendo algo obvio? ¿Alguna idea sobre cómo podría mejorar esto para obtener una lectura más precisa?

#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; 
     } 
    } 
} 

Respuesta

11

Como resultado, el código que publiqué arriba funciona muy bien. El problema sucedió en una parte diferente de mi aplicación. Estaba convirtiendo accidentalmente la distancia de metros a millas, en lugar de metros a kilómetros. Oops!

De todos modos, espero que mi publicación todavía tenga algún mérito, ya que creo que es un ejemplo bastante sólido de cómo rastrear la distancia de un usuario con Core Location.

+0

No olvides aceptar esta como respuesta para evitar que se muestre como una pregunta abierta. Buen ejemplo por cierto. –

+0

Hola Daniel, estoy obteniendo el mismo problema desde hace más de una semana y todavía no obtengo ninguna solución. ¿Entonces puedes ayudarme? –

+0

@Daniel .... Ayúdame por favor. –

0

Probablemente ha configurado kRequiredHorizontalAccuracy demasiado bajo. Si no hay ninguna ubicación en el historial que tenga exactitud < kAspecificación horizontal exigida, ignore todos esos puntos y agregue 0 a la distancia.

+0

Lamentablemente, no creo que eso lo haga. La primera razón es que estoy registrando la precisión horizontal, y se mantiene de manera bastante consistente en 10.0 o 5.0. Además, aunque se ignoran los puntos> kRequiredHorizontalAccuracy, tan pronto como obtengo una ubicación válida, la comparo con la última ubicación válida para obtener una distancia, así que no estoy desperdiciando datos de distancia. – Daniel

+0

@Daniel ... y la última ubicación válida (lastRecordedLocation) es newLocation en caso de que no haya una buena precisión en la lista. Lo que significa que restablece el lastRecordedLocation sin agregar distancia. Por cierto, 10 y 5 son bastante bajos. Obtuve una precisión de 100-150 con bastante regularidad en mis pruebas. – fishinear

+0

Gracias por echar un vistazo. lastRecordedLocation solo debe ser nil si aún no se ha registrado una ubicación válida, ya que establecí self.lastRecordedLocation = bestLocation en la parte inferior. Gracias también por la sugerencia de una precisión de 100-150. Haré algunas pruebas más ... puede que necesite subir mi kRequiredHorizontalAccuracy de 40. – Daniel

Cuestiones relacionadas