2009-07-04 4 views
17

Aktualnie korzystam z CoreLocation + CLLocationManager, aby porównać bieżącą lokalizację użytkowników z N ilością innych lokalizacji. Problem, z którym się borykam, mam do czynienia z obszarami miejskimi, w których lokalizacje są blisko siebie, więc muszę wskazać lokalizację użytkownika tak dokładnie, jak pozwala na to urządzenie, bez poświęcania zbyt wiele czasu. Moje obliczenia są dokładne, problem polega na tym, że pobranie 10 próbek zajmuje zbyt dużo czasu. Wkleje moje filtry/dokładność odpowiedniego kodu poniżej. Jeśli ktokolwiek może skomentować, jak mogę to przyspieszyć, byłoby świetnie. Dopóki nie przyspieszę, jest to raczej bezużyteczne w mojej aplikacji, ponieważ gromadzenie informacji trwa około 3-4 minut, a czas, który nie jest akceptowany przez moją docelową grupę demograficzną.Optymalizacja CLLocationManager/CoreLocation w celu szybszego pobierania punktów danych na iPhonie

Kod wygląda mniej więcej tak:

[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]; 
} 

Dzięki za wszelkie sztuczki optymalizacji aby ten proces przyspieszyć.

Odpowiedz

48

Mam również wiele problemów z CLLocationManager na iPhone również. Z różnych Aplikacji oraz prób i błędów to właśnie wymyśliłem;

1) Użyj klasę singleton dla locationManager, tylko jedną instancję, wykonaj następujące przykłady w: przykład

Apple LocateMe

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

myślę może masz tylko jedno Menedżer lokalizacji działa, w iPhonie jest tylko jeden układ GPS, więc jeśli uruchomisz wiele obiektów menedżera lokalizacji, będą one w konflikcie, a ty otrzymasz błędy od menedżera lokalizacji GPS, który twierdzi, że nie może go zaktualizować.

2) Bądź bardzo ostrożny w jaki sposób zaktualizować locationManager.desiredAccuracy i locationManager.distanceFilter

Śledź przykład w kodzie Apple'a za to w FlipsideViewController:

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

Podejście to działa i nie powoduje żadnych błędów.Po zaktualizowaniu desiredAccuracy lub distanceFilter w pętli głównej Delegat:

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

Rdzeń Lokalizacja najprawdopodobniej skarżą się, że nie można zaktualizować wartości i daje błędy. W ten sposób należy używać tylko stałych dla desiredAccuracy dostarczonych przez Apple, a nie innych, tym samym;

kCLLocationAccuracyBest, kCLLocationAccuracyNearestTenMeters,
kCLLocationAccuracyHundredMeters, kCLLocationAccuracyKilometer,
kCLLocationAccuracyThreeKilometers

użycie innych wartości może dać błędy z locationManager. Dla distanceFilter możesz użyć dowolnej wartości, ale bądź świadomy, że ludzie orzekli, że ma problemy, główną wartością domyślną jest: -1 = Najlepsza, użyłem 10.0 i wygląda na to, że jest OK.

3) Aby wymusić nową aktualizację, wywołaj metodę startUpdates, tak jak w Tweetero, wymusza to locationManager, aby to zrobić, i powinien spróbować uzyskać nową wartość.

4) Podstawowa procedura delegowania lokalizacji jest trudna do uzyskania dokładnych aktualizacji. Po pierwszej inicjalizacji aplikacji możesz wyłączyć się z dokładnością do 1000 metrów lub więcej, a więc jedna próba nie będzie tego robić. Trzy próby po inicjalizacji zwykle sprawiają, że jesteś w odległości 47 lub więcej metrów, co jest dobre. Pamiętaj, że każda kolejna próba trwa dłużej i dłużej, aby poprawić swoją dokładność, przez co szybko staje się droga. Oto, co musisz zrobić: Podejmij nową wartość tylko, jeśli jest ostatnia ORAZ Jeśli nowa lokalizacja ma nieco lepszą dokładność, wybierz nową lokalizację LUB Jeśli nowa lokalizacja ma dokładność < 50 metrów podejmie nową lokalizację LUB Jeśli liczba prób przekroczyła 3 lub 5, a dokładność: < 150 metrów ORAZ lokalizacja zmieniła się, to jest to nowe położenie, a urządzenie zostało przeniesione, więc zmień tę wartość na , nawet jeśli jej dokładność jest gorsza. Oto mój kod Położenie to zrobić:

- (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]; 
} 

Jeśli masz iPhone 3GS aktualizowanie Nagłówek jest dużo łatwiejsze, oto kod w tym samym module jak wyżej:

//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]; 

    } 
} 

Hope this helps ludzie!

+0

Wielka kod. Ale, jak zaznaczono w poniższej odpowiedzi, metoda ta mogła zająć dużo czasu: doupdateToLocation: do ognia. Próbowałem i próbowałem i niezależnie od tego, widzę raczej powolne aktualizacje, gdy jestem w środku - czasami zajmuje to minutę lub dłużej. Jeśli jednak przejdę na Maps.app, natychmiast znajdzie moją lokalizację. Szkoda, że ​​nie możemy uzyskać takiej samej natychmiastowej dokładności za pośrednictwem interfejsu API, co w przypadku MapKit. – runmad

+0

Zastanawiam się, dlaczego sprawdza "UseLocations" w domyślnych ustawieniach użytkownika? Czy ta wartość jest automatycznie ustawiana przez system operacyjny? – nonamelive

+1

Świetny kod. Po ustawieniu locationChanged = YES, instrukcja if powinna używać || zamiast &&. Jeśli zmienił się lat lub lon, zmieniła się lokalizacja. –

4

didUpdateToLokalizacja aktualizuje się tylko, gdy użytkownik przenosi lokalizację. Może to być sporadyczne, ponieważ jestem pewien, że zauważyłeś. Menedżer CCLocationManager nie wysyła regularnych aktualizacji, ale zamiast tego wysyła aktualizacje po przeniesieniu użytkownika lub zwiększa jego dokładność. Kiedy jestem stacjonarny i uruchamiam aplikację, zwykle otrzymuję około trzech aktualizacji, a potem przez chwilę nic. Google Maps robi podobne rzeczy, pokazując pierścień kontra punkt, gdy zwiększa się dokładność. Proponuję czekać, aż osiągniesz wymaganą dokładność (< 120.0f), a następnie wykonasz swoje działanie lub obliczenia.

Jeśli jesteś przyzwyczajony do "normalnego" GPS, gdzie aktualizacje są wysyłane, i nadal udoskonalasz jego dokładność, obawiam się, że iPhone nie jest zaprojektowany do tego.

Można również skorzystać z interfejsu API MapKit i pokazać użytkownikowi mapę z lokalizacją oraz zezwolić na "akceptację", gdy uznają, że lokalizacja jest wystarczająco dokładna. Zauważam, że czasami dostaję punkt, a 30 sekund później porusza mnie to może 50 metrów bliżej miejsca, w którym faktycznie jestem.

0

pisałem repo GIT na ten temat, które mogą swobodnie korzystać https://github.com/xelvenone/M6GPSLocationManager

  • Jeśli dokładność wynik jest lepszy niż acceptableAccuracy, skończymy
  • Jeśli otrzymamy aktualizację occuracy czekamy maximumWaitTimeForBetterResult, aby uzyskać lepszy, - Jeśli tak się nie stanie, skończymy i otrzymamy najlepszą. Jeśli ciągle otrzymujemy aktualizacje, które przekraczają maximumAttempts, to mamy najlepszą (prawdopodobnie i tak się ruszamy).
  • Jeśli nie otrzymamy y inna zmiana w ciągu 30 sekund, mamy zrobić (nie będzie prawdopodobnie jakiejkolwiek innej aktualizacji)

Kod

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