Optimizando CLLocationManager / CoreLocation para recuperar puntos de datos más rápido en el iPhone

Actualmente estoy usando CoreLocation + CLLocationManager para comparar la ubicación actual de un usuario con N cantidad de otras ubicaciones. El problema al que me enfrento es que estoy lidiando con áreas urbanas donde las ubicaciones están cerca unas de otras, por lo que debo señalar la ubicación del usuario con la precisión que permite el dispositivo sin sacrificar demasiado tiempo. Mis cálculos son muy precisos, el problema es que lleva demasiado tiempo recolectar 10 muestras. Pegaré mis filtros / código de precisión respectivo a continuación. Si alguien puede comentar sobre cómo puedo acelerar esto, sería genial. Hasta que lo acelere, es bastante inútil en mi aplicación, ya que actualmente toma alrededor de 3-4 minutos recostackr información, una duración que no es aceptada por mi objective demográfico.

El código se ve así:

[self.locationManager setDistanceFilter:kCLDistanceFilterNone]; [self.locationManager setDesiredAccuracy:kCLLocationAccuracyBest]; - (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation { if (newLocation.horizontalAccuracy > 0.0f && newLocation.horizontalAccuracy < 120.0f) { // roughly an accuracy of 120 meters, we can adjust this. [myLocs addObject:newLocation]; } 

Gracias por cualquier trucos de optimización para acelerar esto.

También he tenido muchos problemas con CLLocationManager en el iPhone. Desde diferentes aplicaciones, y prueba y error esto es lo que he descubierto;

1) Use una clase singleton para locationManager, SOLO UNA instancia, siga los ejemplos en:

Ejemplo de LocateMe de Apple

Tweetero: http://tweetero.googlecode.com/svn/trunk

Estoy pensando que solo puedes tener un administrador de ubicación funcionando, solo hay un chip GPS en el iPhone, por lo tanto, si lanzas múltiples objetos de administrador de ubicación, entrarán en conflicto y obtendrás errores del administrador de ubicación GPS indicando que no se puede actualizar .

2) Tenga mucho cuidado al actualizar la locationManager.desiredAccuracy y locationManager.distanceFilter

Siga el ejemplo en el código de Apple para esto en FlipsideViewController:

 [MyCLController sharedInstance].locationManager.desiredAccuracy = accuracyToSet; [MyCLController sharedInstance].locationManager.distanceFilter = filterToSet; 

Este enfoque funciona y no causa errores. Si actualiza la precisión o el filtro de distancia deseado en el bucle delegado principal:

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

Core Location probablemente se quejará de que no puede actualizar los valores y le dará errores. Además, solo use las constantes para la desiredAccuracy provista por Apple, y ninguna otra, por lo tanto;

kCLLocationAccuracyBest, kCLLocationAccuracyNearestTenMeters,
kCLLocationAccuracyHundredMeters, kCLLocationAccuracyKilometer,
kCLLocationAccuracyThreeKilometers

El uso de otros valores puede generar errores de locationManager. Para distanceFilter puede usar cualquier valor, pero tenga en cuenta que la gente ha declarado que tiene problemas, el principal valor por defecto es: -1 = Mejor, he usado 10.0 y parece estar bien.

3) Para forzar una nueva actualización, llame al método startUpdates como en Tweetero, esto fuerza a locationManager a hacerlo, y debería intentar obtener un nuevo valor.

4) La rutina de delegado de ubicación central es difícil de obtener actualizaciones precisas. Cuando su aplicación se inicializa por primera vez, puede tener una precisión de 1000 metros o más, por lo tanto, un bash no lo hará. Tres bashs después de la inicialización generalmente te llevan dentro de 47 o más metros, lo que es bueno. Recuerde que cada bash sucesivo tomará más y más tiempo para mejorar su precisión, por lo que se vuelve costoso rápidamente. Esto es lo que debe hacer: tomar un nuevo valor solo si es reciente Y si la nueva ubicación tiene una precisión ligeramente mejor tomar la nueva ubicación O si la nueva ubicación tiene una precisión <50 metros tomar la nueva ubicación O si el número de los intentos han excedido 3 o 5 Y la precisión <150 metros Y la ubicación ha CAMBIADO, entonces esta es una nueva ubicación y el dispositivo se movió, así que tome este valor incluso si su precisión es peor. Aquí está mi código de ubicación para hacer esto:

 - (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation  *)newLocation  fromLocation:(CLLocation *)oldLocation { locationDenied = NO; if(![[NSUserDefaults standardUserDefaults] boolForKey:@"UseLocations"]) { [self stopAllUpdates]; return; } NSDate* eventDate = newLocation.timestamp; NSTimeInterval howRecent = abs([eventDate timeIntervalSinceNow]); attempts++; if((newLocation.coordinate.latitude != oldLocation.coordinate.latitude) && (newLocation.coordinate.longitude != oldLocation.coordinate.longitude)) locationChanged = YES; else locationChanged = NO; #ifdef __i386__ //Do this for the simulator since location always returns Cupertino if (howRecent < 5.0) #else // Here's the theory of operation // If the value is recent AND // If the new location has slightly better accuracy take the new location OR // If the new location has an accuracy < 50 meters take the new location OR // If the attempts is maxed (3 -5) AND the accuracy < 150 AND the location has changed, then this must be a new location and the device moved // so take this new value even though it's accuracy might be worse if ((howRecent < 5.0) && ( (newLocation.horizontalAccuracy < (oldLocation.horizontalAccuracy - 10.0)) || (newLocation.horizontalAccuracy < 50.0) || ((attempts >= attempt) && (newLocation.horizontalAccuracy <= 150.0) && locationChanged))) #endif { attempts = 0; latitude = newLocation.coordinate.latitude; longitude = newLocation.coordinate.longitude; [currentLocation release]; currentLocation = [newLocation retain]; goodLocation = YES; [[NSNotificationCenter defaultCenter] postNotificationName: @"UpdateLocationNotification" object: nil]; if (oldLocation != nil) { distanceMoved = [newLocation getDistanceFrom:oldLocation]; currentSpeed = newLocation.speed; distanceTimeDelta = [newLocation.timestamp timeIntervalSinceDate:oldLocation.timestamp]; } } // Send the update to our delegate [self.delegate newLocationUpdate:currentLocation]; } 

Si tiene un iPhone 3GS recibiendo actualizaciones de encabezado es mucho más fácil, aquí está el código en el mismo módulo que el anterior:

 //This is the Heading or Compass values - (void)locationManager:(CLLocationManager *)manager didUpdateHeading:(CLHeading *)newHeading { if (newHeading.headingAccuracy > 0) { [newHeading retain]; // headingType = YES for True North or NO for Magnetic North if(headingType) { currentHeading = newHeading.trueHeading; } else { currentHeading = newHeading.magneticHeading; } goodHeading = YES; [[NSNotificationCenter defaultCenter] postNotificationName: @"UpdateHeadingNotification" object: nil]; } } 

Espero que esto ayude a la gente!

didUpdateToLocation solo se actualiza cuando el usuario mueve la ubicación. Esto puede ser esporádico, como estoy seguro que has notado. CCLocationManager no envía actualizaciones periódicas, sino que envía actualizaciones cuando el usuario se ha mudado, o aumenta su precisión. Cuando estoy parado y empiezo una aplicación, suelo recibir aproximadamente tres actualizaciones, y luego nada durante un tiempo. Google Maps hace un tipo similar de cosas, mostrando un anillo vs punto a medida que aumenta la precisión. Sugeriría esperar hasta que hayas alcanzado la precisión requerida (<120.0f) y luego realizar tu acción o cálculo.

Si estás acostumbrado a un GPS “normal”, donde se envían las actualizaciones y continúa perfeccionando su precisión, me temo que el iPhone no está diseñado para hacerlo.

Alternativamente, puede usar la API de MapKit y mostrar al usuario el mapa con su ubicación, y permitir que ‘acepten’ una vez que sientan que la ubicación es lo suficientemente precisa. Noté que a veces obtengo un punto exacto, y luego, 30 segundos después, me mueve unos 50 metros más cerca de donde estoy en realidad.

Escribí un informe de GIT sobre esto, que puede usar de forma gratuita https://github.com/xelvenone/M6GPSLocationManager

  • Si la precisión del resultado es mejor que la precisión aceptable, hemos terminado
  • Si obtenemos una actualización sobre la oclusión, esperamos maximumWaitTimeForBetterResult para obtener una mejor, – Si esto no sucede, hemos terminado y tomamos el mejor
  • Si constantemente recibimos actualizaciones, que superan los Máximos Apetitosos, tomamos el mejor (probablemente nos mudemos de todos modos)
  • Si no recibimos ninguna otra actualización en 30 segundos, hemos terminado (probablemente no haya otra actualización)

Código

 - (void)scopeToCurrentLocationWithAcceptableAccuracy:(CLLocationAccuracy)acceptableAccuracy maximumWaitTimeForBetterResult:(NSTimeInterval)maximumWaitTimeForBetterResult maximumAttempts:(NSInteger)maximumAttempts onCompletion:(M6GPSLocationManagerCompletion)completion;