Join FlipAndroid.COM Telegram Group: https://t.me/joinchat/F_aqThGkhwcLzmI49vKAiw


Android Mapview: Fusionar marcadores superpuestos en un nuevo marcador

Así que tengo un MapView con una gran cantidad de marcadores, la mayoría de los cuales se concentran en millones de ancho racimos. Cuando se hace zoom, los marcadores se superponen y parecen ser sólo uno. Lo que quiero lograr es a un cierto nivel de zoom reemplazar los marcadores de superposición con un marcador de grupo que mostrará la densidad de los marcadores y onClick se zoom para mostrar todos los marcadores en el interior. Sé que puedo hacer esto con medidas de la distancia de la fuerza bruta pero debe haber una manera más eficiente. Alguien tiene alguna solución o algoritmos inteligentes sobre cómo puedo lograr esto?

  • Actualización de un marcador de mapa en android
  • Cómo agregar marcador de Android en la pantalla de inicio desde la página web?
  • Mejores prácticas al agrupar gran cantidad de marcadores en Google Maps Android V2
  • Android Historial de navegación dejando fuera sólo algunos sitios
  • Rotar marcador según la dirección del usuario en Google Maps V2 Android
  • 7 Solutions collect form web for “Android Mapview: Fusionar marcadores superpuestos en un nuevo marcador”

    Um … suponiendo que los marcadores no están agrupados, en capas o nada: ¿por qué – antes de mostrarlos – no se crea una cuadrícula de cierta densidad y simplemente colocar los marcadores en las celdas de su cuadrícula?

    Si luego cuenta que varios marcadores caen en el mismo bin (grid cell) – puede agruparlos. Si necesita un agrupamiento ligeramente más inteligente, también puede comprobar las celdas vecinas.

    Tal vez suena un poco primitivo, pero:

    • No hay algoritmos n ^ 2
    • No hay suposición sobre el pedido de la entrada
    • No es necesario procesar adicionalmente marcadores que no se muestren

    El código para la cuadrícula:

    Nota – Vengo del mundo de C ++ (conseguido aquí a través de la etiqueta [del algoritmo]) así que me pegaré al pseudo-C + +. No conozco la API del mapa. Pero me sorprendería si esto no pudiera ser traducido eficientemente a cualquier idioma / biblioteca que esté usando.

    Entrada: – lista de marcadores – la ventana de visualización del rectángulo en coordenadas del mundo (sección del mundo que estamos viendo actualmente)

    En la forma más simple, se vería algo como esto:

    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.xv.x1)/(v.x2-v.x1)); binIdx.y=floor(densityY*(m.coord.yv.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() } } } 

    El problema que puede aparecer es que una división de cuadrícula no deseada puede aparecer dentro de un grupo agrupado, formando dos GroupMarkers. Para contrarrestar esto, es posible que desee considerar no sólo una celda de cuadrícula, sino también sus vecinos en la sección "\ drawing" y, si está agrupada, marcar las celdas vecinas como visitadas.

    La siguiente solución pragmática basada en la distancia de píxeles realmente funcionó mejor para mí:

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

    He convertido la respuesta de Cygnus X1 a Java. Ponga este método en su Overlay personalizado y modifique drawSingle () y drawGroup () para satisfacer sus necesidades. También mejora el rendimiento, como convertir las ArrayLists a arrays primitivos.

      @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)px / (double)mapView.getWidth()); binX = (int) (Math.floor(densityX * fractionX)); double fractionY = ((double)py / (double)mapView.getHeight()); binY = (int) (Math .floor(densityX * fractionY)); // Log.w("PointClusterer absolute", p.x+ ", "+py); // 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 (px > 0 & px < mapView.getWidth() & py > 0 & py < mapView .getHeight()); } } 

    Suponiendo que sus marcadores se agrupan en un ItemizedOverlay puede crear un método que se llamó cuando se amplió el mapa. Esto compararía las coordenadas de píxeles de cada marcador para ver si se superponen y establecen una bandera. Luego, en el método de dibujo se puede dibujar el marcador agrupado o los individuos;

    Algo como:

      //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=px; y=py; continue; } deltaX=Math.abs(px-x); deltaY=Math.abs(py-y); //if the co-ordinates are too far apart dont draw grouped if(deltaX>tolerance || deltaY>tolerance){ drawGrouped=false; return; } x=px; y=py; } //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); } 

    Lo que usted está buscando es usualmente llamado clustering. Hay técnicas comunes para hacer esto, puede referirse, por ejemplo, a esta pregunta SO , que conduce a este post .

    La idea básica es dividir el mapa en cuadrados basado en el nivel de zoom actual (puede calcular la caché en función del nivel de zoom para evitar el recálculo cuando el usuario inicia el zoom) y agruparlos en base a qué cuadrado pertenecen. Así que terminas teniendo algún tipo de agrupación basada en el nivel de zoom, es decir, para el nivel 1-5 sólo dibujar los marcadores, para el nivel 5-8 agruparlos en cuadrados de 20 millas, para 9-10 en cuadrados de 50 millas, y así en.

    Aquí está otra pregunta relevante sobre SO que puede que desee echar un vistazo, no está seguro sobre el rendimiento de esto sin embargo: Android Maps Point Clustering

    Si sus marcadores están agrupados, tendrá una buena idea en qué nivel de zoom debe mostrar los marcadores individuales o el marcador de grupo, por ejemplo, el nivel de zoom> 17 y luego mostrar los marcadores individuales, de lo contrario mostrar el marcador de grupo. He utilizado código algo como esto en mi ItemizedOverlay para cambiar mis marcadores:

     @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 eg route lines. super.draw(canvas, mapv, false); } private void setMarkersForZoomLevel19() { for (JourneyOverlayItem item : mOverlays) { item.setMarker(areaPointIcon48); } } 

    Si es posible tener los marcadores individuales en una colección, fácilmente podría obtener la mayor y la menor latitud y longitud y la diferencia entre ellos le dará la latitud y la longitud (esto podría ser utilizado para hacer zoom en el intervalo para mostrar el Grupo de marcadores). Divide los tramos por 2 y debe tener el punto central para colocar el marcador de grupo.

    Este es el enfoque que usé. Sin embargo, es O (n ^ 2).

    Las clavijas se deben clasificar en función de las prominentes.

    Escoja el perno con el más alto prominente. Mire todos los pernos a su alrededor. Absorba los pasadores cerca de ese pasador.

    A continuación, pasar al siguiente alfiler más prominente. Hacer lo mismo. Repetir.

    Sencillo.

    Las cosas se complican si mueve el mapa, ampliando, alejando, y desea asegurarse de que los nuevos pines no están siendo redibujados. Por lo tanto, comprueba cada clúster si tiene que dividirlo durante el acercamiento y, a continuación, comprueba cada clúster si tiene que combinar durante el alejamiento. A continuación, quitar los pasadores que se ha ido y añadir nuevos pines. Para cada pin que agregue, verifique si deben unirse a un clúster o formar su propio clúster.

    FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.