2009-10-19 15 views
13

Istnieje wiele złych sposobów, aby poradzić sobie z tym, co chcę zrobić, ale wydaje się, że jeden z tych przypadków "musi być lepszy sposób".Skonsoliduj adnotacje na powiększonym MKMapView

Używam MKMapView w aplikacji na iPhone'a wyświetlającej wiele adnotacji. Udawaj, że każda miejscowość w amerykańskim stanie ma adnotację, więc na ekranie jest dość gęsta kupka adnotacji. Gdy użytkownik pomniejsza mapę, te adnotacje zaczynają się wzajemnie zaciskać, dopóki nie nakładają się na siebie i stają się trudne do oddzielenia.

Chciałbym, aby w przypadku gęstości adnotacji (np. Gdy adnotacje nakładają się), skonsoliduj te adnotacje w jedną adnotację, która wskazuje, że zawiera wiele sub-adnotacji (niektóre wskaźniki wizualne do powiedzenia , "powiększ i zobaczysz więcej adnotacji").

Mogę wywołać CGRectIntersectsRect w widokach adnotacji, ale używając tego problemu wydaje się być N^2 - musiałbym iterować nad każdą adnotacją dla każdej adnotacji. Rozważmy to Pseudokod:

 
FOR firstAnnotationView IN allAnnotationViews 
    FOR secondAnnotationView in allAnnotationViews 
     IF CGRectIntersectsRect(firstAnnotationView.frame, secondAnnotationView.frame) 
      // found two overlapping annotations, consolidate them 
     ENDIF 
    ENDFOR 
ENDFOR

Można zrozumieć, dlaczego to byłoby powolne, a to trzeba uruchomić za każdym razem, gdy mapa została powiększać lub na zewnątrz!

Jak można wykryć nakładające się adnotacje na mapie i inteligentnie je konsolidować?

+0

Zastanów się przestrzeń poszukiwań. Czy konieczne jest uwzględnienie wszystkich adnotacji w zewnętrznej pętli? A co z prostą oceną aktualnie oglądanych adnotacji? –

Odpowiedz

1

Załadowałbym twoje adnotacje na podstawie długości/szerokości geograficznej, a następnie skonsolidowałem je przy użyciu tych binów. Podstawową ideą będzie wyglądać mniej więcej tak:

#include <vector> 

float minLongitude = 180.0f; 
float maxLongitude = -180.0f; 
float longitudeBinSize = 0.1; // Degrees 
float minLatitude = -90.0f; 
float maxLatitude = 90.0f; 
float latitudeBinSize = 0.1; // Degrees 
int numBinColumns = int((maxLongitude - minLongitude)/longitudeBinSize); 
int numBinRows = int((maxLatitude - minLatitude)/latitudeBinSize); 

void calcBinCoords(float longitude, float latitude, int &column, int &row) { 
    column = int((latitude - minLatitude)/latitudeBinSize); 
    row = int((longitude - minLongitude)/longitudeBinSize); 
} 

typedef std::vector<AnnotationView *> AnnotationViews; 

void binAnnotations(NSArray *annotationViews, std::vector<AnnotationViews> &binnedAnnotations) { 
    binnedAnnotations.clear(); 
    binnedAnnotations.resize(numBinColumns * numBinRows); 
    for (AnnotationView *annotationView in annotationViews) { 
     int column, row; 
     calcBinCoords(annotationView.longitude, annotationView.latitude, column, row); 
     binnedAnnotations[row * numBinColumns + column].push_back(annotationView); 
    } 
} 

wartości dla longitudeBinSize i latitudeBinSize byłaby maksymalna odległość, które zamierzają szukać po konsolidacji. Gdy wszystko znajdzie się w pojemnikach, problem z wyszukiwaniem polega tylko na wyszukaniu listy wartości w przyległych pojemnikach dla kandydatów. Ponadto, ponieważ będziesz skanował tablicę podczas konsolidacji, naprawdę musisz tylko sprawdzić trzy sąsiadujące pojemniki dla każdego przetwarzanego bin - bin (kolumna + 1, wiersz), bin (kolumna, wiersz + 1), a bin na (kolumna + 1, wiersz + 1).

Możesz użyć NSMutableArrays zamiast std :: vector dla binów, ale brzmi to jak masz dużą liczbę przedmiotów do przetworzenia i podejrzewam, że std :: vector będzie szybszy. To tylko moje preferencje, ale może to nie wystarczyć, by się tym przejmować. Jeśli użyjesz ObjC zamiast ObjC++, nie możesz oczywiście użyć std :: vector.

0

Możesz użyć Geohash, aby podzielić swoje adnotacje. Spowoduje to zmniejszenie przestrzeni wyszukiwania przy próbie "skonsolidowania" swoich adnotacji.