2011-08-08 13 views
11

में ओवरलैपिंग मार्करों को विलय करना तो मेरे पास बहुत सारे मार्करों के साथ एक नक्शा दृश्य है, जिनमें से अधिकांश मील चौड़े क्लस्टर में केंद्रित हैं। जब मार्कर ओवरलैप ज़ूम करते हैं और केवल एक ही दिखाई देते हैं। मैं जो हासिल करना चाहता हूं वह एक निश्चित ज़ूम स्तर पर एक समूह मार्कर के साथ ओवरलैपिंग मार्करों को प्रतिस्थापित करता है जो मार्करों की घनत्व प्रदर्शित करेगा और ऑनक्लिक सभी मार्करों को अंदर प्रदर्शित करने के लिए ज़ूम करेगा। मुझे पता है कि मैं इसे बलपूर्वक बल दूरी माप के साथ कर सकता हूं लेकिन एक और अधिक प्रभावी तरीका होना चाहिए। किसी के पास कोई समाधान या स्मार्ट एल्गोरिदम है कि मैं इसे कैसे प्राप्त कर सकता हूं?एंड्रॉइड मैपव्यू: एक नए मार्कर

उत्तर

11

उम ... मानते हैं कि मार्कर समूहबद्ध, स्तरित या कुछ भी नहीं हैं: क्यों - उन्हें दिखाने से पहले - क्या आप कुछ घनत्व का ग्रिड नहीं बनाते हैं और बस अपने ग्रिड की कोशिकाओं में मार्कर को बिन करते हैं?

यदि आप मानते हैं कि कई मार्कर एक ही बिन (ग्रिड सेल) में आते हैं - तो आप उन्हें समूहित कर सकते हैं। यदि आपको थोड़ी अधिक चतुर समूह की आवश्यकता है, तो आप पड़ोसी कोशिकाओं की भी जांच कर सकते हैं।

हो सकता है कि यह थोड़ा आदिम लेकिन लगता है:

  • नहीं n^2 एल्गोरिदम
  • इनपुट
  • कोई ज़रूरत नहीं के आदेश के बारे में कोई धारणा अतिरिक्त प्रक्रिया मार्करों जो नहीं जा रहे हैं
  • दिखाए जाने के लिए

ग्रिड के लिए कोड:

नोट - मैं सी ++ दुनिया से आया हूं (यहां [एल्गोरिदम] टैग के माध्यम से मिला है) इसलिए मैं छद्म-सी ++ पर चिपके रहूंगा। मैं नक्शादृश्य के एपीआई नहीं जानता। लेकिन मुझे आश्चर्य होगा कि अगर आप जिस भाषा/पुस्तकालय का उपयोग कर रहे हैं उसमें इसका कुशलतापूर्वक अनुवाद नहीं किया जा सकता है।

इनपुट:

void draw(MarkerList mlist, View v) { 

    //binning: 

    list<Marker> grid[densityX][densityY]; //2D array with some configurable, fixed density 
    foreach(Marker m in mlist) { 
     if (m.within(v)) { 
      int2 binIdx; 
      binIdx.x=floor(densityX*(m.coord.x-v.x1)/(v.x2-v.x1)); 
      binIdx.y=floor(densityY*(m.coord.y-v.y1)/(v.y2-v.y1)); 
      grid[binIdx.x][binIdx.y].push(m); //just push the reference 
     } 

    //drawing: 

    for (int i=0; i<densityX; ++i) 
    for (int j=0; j<densityY; ++j) { 
     if (grid[i][j].size()>N) { 
      GroupMarker g; 
      g.add(grid[i][j]); //process the list of markers belonging to this cell 
      g.draw(); 
     } else { 
      foreach (Marker m in grid[i][j]) 
       m.draw() 
     } 
    } 

} 
: - - मार्करों के सूची आयत दुनिया निर्देशांक में खिड़की देखने वाले लोग (दुनिया की धारा हम वर्तमान में देख रहे हैं)

सरलतम रूप में, यह कुछ इस तरह दिखेगा

समस्या जो दिखाई दे सकती है वह यह है कि कुछ क्लस्टर समूह के भीतर एक अवांछित ग्रिड विभाजन दिखाई दे सकता है, जिसमें दो ग्रुपमार्कर बनते हैं। इसका मुकाबला करने के लिए, आप केवल एक ग्रिड सेल पर विचार नहीं करना चाहेंगे, बल्कि "पड़ोसी" खंड में पड़ोसियों को भी विचार करना चाहेंगे, और - यदि समूहबद्ध हो - पड़ोसी कोशिकाओं को विज़िट के रूप में चिह्नित करें।

+0

क्या आप मुझे कुछ नमूना कोड दे सकते हैं कि आप ग्रिड को कुशलतापूर्वक कैसे बनाएंगे? – NSjonas

+0

क्या आप मानचित्र ज़ूम करते समय यह कोड ठीक से काम करते हैं? क्योंकि मुझे लगता है कि मानचित्र ज़ूम आउट होने पर ग्रिड बक्से बड़े होने की आवश्यकता है। – adrianTNT

+0

ग्रिड का आकार सीधे उन आयतों में दृश्य आयताकार 'v' के विश्व निर्देशांक पर निर्भर करता है जहां आप' binIdx' मानों की गणना करते हैं। नतीजतन, यह आपके ज़ूमिंग स्तर के अनुकूल होगा। "निश्चित घनत्व" स्क्रीन स्पेस में ग्रिड घनत्व है, न कि विश्व की जगह। – CygnusX1

2

मान लें कि आपके मार्करों को एक आइटमीकृत ओवरले में एक साथ समूहीकृत किया गया है, आप एक विधि बना सकते हैं जिसे मानचित्र ज़ूम किया गया था। यह प्रत्येक मार्कर के पिक्सेल समन्वय की तुलना करने के लिए यह देखने के लिए तुलना करेगा कि क्या वे ओवरलैप करते हैं और ध्वज सेट करते हैं। फिर ड्रॉ विधि में आप समूहबद्ध मार्कर या व्यक्तियों को आकर्षित कर सकते हैं;

कुछ की तरह:

//this would need to be wired to be called when the mapview is zoomed 
    //it sets the drawgrouped flag if co-ordinates are close together 
    Boolean drawGrouped=false; 
    public void onMapZoom(MapView mapView){ 
     //loop thru overlay items 
     Integer i,l=this.size(); 
     OverlayItem item; 
     Integer deltaX=null,deltaY=null; 
     Projection proj = mapView.getProjection(); 
     Point p=new Point(); 
     Integer x=null,y=null; 
     Integer tolerance = 10; //if co-ordinates less than this draw grouped icon 
     for(i=0;i<l;i++){ 
     //get the item 
     item=this.getItem(i); 
     //convert the overlays position to pixels 
     proj.toPixels(item.getPoint(), p); 
     proj.toPixels(item.getPoint(), p); 
     //compare co-ordinates 
     if(i==0){ 
      x=p.x; 
      y=p.y; 
      continue; 
     } 
     deltaX=Math.abs(p.x-x); 
     deltaY=Math.abs(p.y-y); 

     //if the co-ordinates are too far apart dont draw grouped 
     if(deltaX>tolerance || deltaY>tolerance){ 
      drawGrouped=false; 
      return; 
     } 
     x=p.x; 
     y=p.y; 
     } 
     //all co-ords are within the tolerance 
     drawGrouped=true; 
    } 

    public void draw(android.graphics.Canvas canvas, MapView mapView, boolean shadow){ 
     if(drawGrouped==true){ 
      //draw the grouped icon *needs to be optimised to only do it once 
      drawGrouped(canvas,mapView,shadow); 
      return; 
     } 
     //not grouped do regular drawing 
     super.draw(canvas, mapView, shadow); 
    } 
+0

धन्यवाद मैं इसे आज़मा दूंगा। अगर हजारों मार्कर हैं तो मैं थोड़ी धीमी गति से देख सकता हूं, लेकिन मुझे लगता है कि कोई बेहतर तरीका नहीं है। मैं उम्मीद कर रहा था कि कुछ एपीआई समर्थन था जिसे मैंने देखा है – NSjonas

1

अपने मार्कर वर्गीकृत किया है, तो आप क्या ज़ूम स्तर पर एक उचित विचार होगा आप अलग-अलग मार्करों या समूह मार्कर उदा प्रदर्शित किया जाना चाहिए ज़ूम स्तर> 17 फिर व्यक्तिगत मार्कर प्रदर्शित करें, अन्यथा समूह मार्कर प्रदर्शित करें। मैं अपने मार्कर को बदलने के लिए मेरी ItemizedOverlay में इस तरह कोड कुछ प्रयोग किया है:

@Override 
public void draw(Canvas canvas, MapView mapv, boolean shadow) 
{  
    int zoom = mapv.getZoomLevel(); 

    switch(zoom) 
    { 
     case 19: 
      setMarkersForZoomLevel19(); 
      break; 
     case 18: 
      setMarkersForZoomLevel18(); 
      break; 
     case 17: 
      setMarkersForZoomLevel17(); 
      break; 
     case 16: 
      setMarkersForZoomLevel16(); 
      break; 
     default: 
      // Hide the markers or remove the overlay from the map view.     
      mapv.getOverlays().clear(); 
    }  

    area.drawArea(canvas, mapv); 

    // Putting this call here rather than at the beginning, ensures that 
    // the Overlay items are drawn over the top of canvas stuff e.g. route lines. 
    super.draw(canvas, mapv, false);   

} 


private void setMarkersForZoomLevel19() 
{  
    for (JourneyOverlayItem item : mOverlays) 
    {    
     item.setMarker(areaPointIcon48);    
    } 
} 

तो इसके संभावित एक संग्रह में अलग-अलग मार्कर के लिए, आप आसानी से सबसे बड़ा और सबसे छोटा अक्षांश और देशांतर और उनके बीच के अंतर हो सकता है होगा आपको अक्षांश और देशांतर अवधि प्रदान करें (इसका उपयोग मार्करों के समूह को दिखाने के लिए अवधि में ज़ूम करने के लिए किया जा सकता है)। स्पैन को 2 से विभाजित करें और आपके पास समूह मार्कर रखने के लिए केंद्र बिंदु होना चाहिए।

+0

समूहबद्ध द्वारा आपका क्या मतलब है। मेरे मार्कर सभी एक ही itemizedOverlay पर हैं। – NSjonas

+0

हां, लेकिन आप अपने संग्रहक geopoints कुछ संग्रहों में निहित हो सकता है उदा।यदि आपके पास मानचित्र के ऊपरी बाएं चतुर्भुज में बहुत सारे मार्कर हैं, तो यह 1 संग्रह में हो सकता है और फिर केंद्रीय बिंदु की पहचान करना आसान हो जाता है। वैकल्पिक रूप से, अपने मानचित्र को क्षेत्रों में विभाजित करें और प्रत्येक क्षेत्र के लिए संग्रह रखें। –

2

जो आप खोज रहे हैं उसे आमतौर पर क्लस्टरिंग कहा जाता है। ऐसा करने के लिए सामान्य तकनीकें हैं, उदाहरण के लिए, आप इस SO question पर संदर्भित कर सकते हैं, यह post की ओर जाता है।

मूल विचार वर्तमान ज़ूम स्तर के आधार पर वर्गों पर नक्शा को विभाजित करना है (उपयोगकर्ता ज़ूमिंग शुरू करते समय पुनर्मूल्यांकन से बचने के लिए ज़ूम स्तर के आधार पर गणना कैश कर सकते हैं), और उन्हें समूहबद्ध करने के लिए वे किस वर्ग के हैं । तो आप ज़ूम स्तर के आधार पर कुछ प्रकार के समूहिंग को समाप्त कर देते हैं, यानी स्तर 1-5 के लिए केवल मार्करों को आकर्षित करें, स्तर 5-8 के लिए उन्हें 20 मील के वर्गों में समूहित करें, 50 मील के वर्गों में 9-10 के लिए, और इसलिए पर। Android Maps Point Clustering

+0

धन्यवाद, वास्तव में अच्छी जानकारी। मुझे सिग्नसएक्स 1 को बक्षीस देना पड़ा क्योंकि वह पहले से ही इस समाधान को लिखने की परेशानी से गुजर चुका था क्योंकि मैंने – NSjonas

3

पिक्सेल दूरी के आधार पर व्यावहारिक समाधान निम्नलिखित वास्तव में मेरे लिए सबसे अच्छा काम किया:

यहाँ आप एक नज़र, यकीन है कि यह हालांकि के प्रदर्शन के बारे में नहीं ले जाना चाहते हो सकता है कि इतने पर किसी अन्य प्रासंगिक सवाल यह है कि

http://www.appelsiini.net/2008/11/introduction-to-marker-clustering-with-google-maps

3

मैंने जावा के सिग्नस एक्स 1 के जवाब को परिवर्तित किया। इस विधि को अपने कस्टम ओवरले में रखें और अपनी आवश्यकताओं के अनुसार drawSingle() और drawGroup() को संशोधित करें। आप प्रदर्शन को भी बेहतर बनाते हैं, जैसे ArrayLists को आदिम सरणी में परिवर्तित करना।

@Override 
    public void draw(Canvas canvas, MapView mapView, boolean shadow) { 
     // binning: 
     int densityX = 10; 
     int densityY = 10; 
     // 2D array with some configurable, fixed density 
     List<List<List<OverlayItem>>> grid = new ArrayList<List<List<OverlayItem>>>(
       densityX); 

     for(int i = 0; i<densityX; i++){ 
      ArrayList<List<OverlayItem>> column = new ArrayList<List<OverlayItem>>(densityY); 
      for(int j = 0; j < densityY; j++){ 
       column.add(new ArrayList<OverlayItem>()); 
      } 
      grid.add(column); 
     } 

     for (OverlayItem m : mOverlays) { 
       int binX; 
       int binY; 

       Projection proj = mapView.getProjection(); 
       Point p = proj.toPixels(m.getPoint(), null); 

      if (isWithin(p, mapView)) { 
       double fractionX = ((double)p.x/(double)mapView.getWidth()); 
       binX = (int) (Math.floor(densityX * fractionX)); 
       double fractionY = ((double)p.y/(double)mapView.getHeight()); 
       binY = (int) (Math 
         .floor(densityX * fractionY)); 
//    Log.w("PointClusterer absolute", p.x+ ", "+p.y); 
//    Log.w("PointClusterer relative", fractionX+ ", "+fractionY); 
//    Log.w("PointClusterer portion", "Marker is in portion: " + binX 
//      + ", " + binY); 
       grid.get(binX).get(binY).add(m); // just push the reference 
      } 
     } 

     // drawing: 

     for (int i = 0; i < densityX; i++) { 
      for (int j = 0; j < densityY; j++) { 
       List<OverlayItem> markerList = grid.get(i).get(j); 
       if (markerList.size() > 1) { 
        drawGroup(canvas, mapView, markerList); 
       } else { 
        // draw single marker 
        drawSingle(canvas, mapView, markerList); 
       } 
      } 
     } 
    } 

    private void drawGroup(Canvas canvas, MapView mapView, 
      List<OverlayItem> markerList) { 
     GeoPoint point = markerList.get(0).getPoint(); 
     Point ptScreenCoord = new Point(); 
     mapView.getProjection().toPixels(point, ptScreenCoord); 
     Paint paint = new Paint(); 
     paint.setTextAlign(Paint.Align.CENTER); 
     paint.setTextSize(30); 
     paint.setAntiAlias(true); 
     paint.setARGB(150, 0, 0, 0); 
     // show text to the right of the icon 
     canvas.drawText("GROUP", ptScreenCoord.x, ptScreenCoord.y + 30, paint); 
    } 

    private void drawSingle(Canvas canvas, MapView mapView, 
      List<OverlayItem> markerList) { 
     for (OverlayItem item : markerList) { 
      GeoPoint point = item.getPoint(); 
      Point ptScreenCoord = new Point(); 
      mapView.getProjection().toPixels(point, ptScreenCoord); 
      Paint paint = new Paint(); 
      paint.setTextAlign(Paint.Align.CENTER); 
      paint.setTextSize(30); 
      paint.setAntiAlias(true); 
      paint.setARGB(150, 0, 0, 0); 
      // show text to the right of the icon 
      canvas.drawText("SINGLE", ptScreenCoord.x, ptScreenCoord.y + 30, 
        paint); 
     } 
    } 

    public static boolean isWithin(Point p, MapView mapView) { 
     return (p.x > 0 & p.x < mapView.getWidth() & p.y > 0 & p.y < mapView 
       .getHeight()); 
    } 
} 
+0

से पूछा कि मोवरले क्या है? मुझे इसे घोषित करने की क्या ज़रूरत है? – Shrikant

+0

यह अवलोकन है कि आप अपने MapView के माध्यम से प्राप्त करते हैं, उदाहरण के लिए, MapView.getOverlays()। ध्यान दें कि यह कोड असली मैला है, और आपको भारी प्रदर्शन बढ़ाने के लिए ArrayLists को आदिम सरणी में परिवर्तित करना चाहिए। – Maarten

0

यह वह दृष्टिकोण है जिसका मैंने उपयोग किया था। हालांकि, यह ओ (एन^2) है।

पिनों को प्रमुख के आधार पर क्रमबद्ध किया जाना चाहिए।

उच्चतम प्रमुख के साथ पिन चुनें। इसके चारों ओर सभी पिन देखो। उस पिन के पास Absorb पिन।

फिर अगले उच्चतम प्रमुख पिन पर जाएं। ऐसा ही करने। दोहराएँ।

सरल।

चीजें जटिल हो जाती हैं यदि आप मानचित्र को चारों ओर ले जाते हैं, ज़ूम इन करते हैं, ज़ूम आउट करते हैं, और आप यह सुनिश्चित करना चाहते हैं कि नए पिन को फिर से खींचा नहीं जा रहा है। इसलिए यदि आप ज़ूम इन के दौरान विभाजित करना चाहते हैं तो प्रत्येक क्लस्टर को जांचें, और फिर यदि आप ज़ूम आउट के दौरान विलय करना चाहते हैं तो आप प्रत्येक क्लस्टर को चेक करें। फिर आप चले गए पिन हटाते हैं और नए पिन जोड़ते हैं। आपके द्वारा जोड़े गए प्रत्येक पिन के लिए, आप जांचते हैं कि उन्हें क्लस्टर में शामिल होना चाहिए या अपना स्वयं का क्लस्टर बनाना चाहिए।

संबंधित मुद्दे