objective-c - corelocation - ios location
Tener problemas para calcular la distancia total para caminar/correr con precisiĆ³n usando CLLocationManager (2)
Estoy tratando de construir 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 proporcione una distancia total precisa.
Cuando se compara con Nike + GPS o RunKeeper, mi aplicación siempre informa una distancia más corta. Informarán de la misma manera al principio, pero a medida que me muevo, los valores de mi aplicación frente a otras aplicaciones en ejecución van disminuyendo gradualmente.
Por ejemplo, si camino .3 kilómetros (verificado por el odómetro de mi auto), Nike + GPS y RunKeeper reportan ~ .3 kilómetros cada vez, pero mi aplicación reportará ~ .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;
}
}
}
Como resultado, el código que publiqué arriba funciona muy bien. El problema pasó a estar en una parte diferente de mi aplicación. Accidentalmente estaba convirtiendo la distancia de metros a millas, en lugar de metros a kilómetros. ¡Uy!
De todos modos, espero que mi publicación aún 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.
Probablemente haya establecido kRequiredHorizontalAccuracy demasiado bajo. Si no hay una ubicación en el historial que tenga una precisión <kRequiredHorizontalAccuracy, entonces ignora todos esos puntos y agrega 0 a la distancia.