2009-10-19 18 views
13

Yapmak istediklerim hakkında bazı kötü yollar vardır, ancak bu "daha iyi bir yol olmalı" gibi durumlardan biri gibi görünüyor.Yakınlaştırılmış bir MKMapView üzerinde ek açıklamaları konsolide edin

Bir iPhone uygulamasında bir takım ek açıklamaları görüntüleyen bir MKMapView kullanıyorum. Bir ABD eyaletindeki her kasabanın bir ek açıklamasına sahip olduğu kavramsal tartışmayı varsayın, bu nedenle ekranda çok yoğun bir ek not yığını var. Kullanıcı haritayı yakınlaştırdıkça, bu açıklamaların üst üste gelmesine ve birbirini seçmeye zorlanıncaya kadar birbiri içine girmeye başlar.

Yapmak istediğim şey, ek açıklamaların belirli bir yoğunluğuna (herhangi bir ek açıklamaların çakıştığı zaman söyleyin), bu ek açıklamaları bir dizi alt ek açıklamalar içeren tek bir ek açıklamada birleştirin (bazı görsel göstergeler için söyleyin) , "yakınlaştırın ve daha fazla ek açıklama göreceksiniz").

ben açıklama görünümlerinde CGRectIntersectsRect diyebiliriz, ama bu kullanarak bir N^2 sorunu olarak görünecektir - Ben her ek açıklamadan için her ek açıklamadan üzerinde yineleme gerekir. Bu pseudocode düşünün:

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

Bunu yavaş olacağını neden görebilirsiniz ve bu harita veya dışarı yakınlaştırılmış her zaman çalıştırmak zorunda kalacak!

Haritadaki örtüşen ek açıklamaları nasıl algılayacaksınız ve performans konusunda bilinçli bir şekilde bunları akıllı bir şekilde birleştirin?

+0

arama alanını düşünün. Dış döngüdeki tüm ek açıklamaları dikkate almak gerekli mi? Şu anda incelenen ek açıklamaları değerlendirmek ne dersiniz? –

cevap

1

Ek açıklamalarınızı boylam/enlem bazında depolar ve daha sonra bu kutuları kullanarak birleştirir.

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

longitudeBinSize ve latitudeBinSize için değerleri Eğer konsolide ederken aramak niyetinde maksimum mesafe olacaktır: Temel fikir bu gibi bir şey olur. Her şey kutuda olduğunda, arama sorunlarınız yalnızca adaylar için bitişik kutulardaki değerlerin listesini aramayı gerektirir. Ayrıca, konsolidasyon sırasında diziyi tarayacağınız için, işlem yaptığınız her bölme için yalnızca bitişik kutuların üçünü denetlemeniz yeterlidir. (Sütun + 1, satır), çöp kutusu (sütun, satır + 1)) ve çöp kutusu (sütun + 1, satır + 1).

Kutular için std :: vector yerine NSMutableArrays kullanabilirsiniz, ancak işlemek için çok sayıda öğeye sahip olduğunuzu ve std :: vektörünün daha hızlı olacağından şüpheleniyorum. Bu sadece benim tercihim olsa da, umursamayacak kadar önemli olmayabilir. ObjC++ yerine ObjC kullanırsanız, std :: vektörünü elbette kullanamazsınız.

0

Ek açıklamalarınızı bölümlemek için Geohash kullanabilirsiniz. Bu, ek açıklamalarınızı "birleştirmeye" çalışırken arama alanını azaltacaktır.

İlgili konular