Tengo una vista de mapa con muchos marcadores, la mayoría de los cuales están concentrados en grupos de una milla de ancho. Cuando se amplía, los marcadores se superponen y parecen ser solo uno. Lo que quiero lograr es a un cierto nivel de zoom reemplazar los marcadores superpuestos con un marcador de grupo que mostrará la densidad de los marcadores y onClick hará un zoom para mostrar todos los marcadores dentro. Sé que puedo hacer esto con mediciones de distancia de fuerza bruta, pero debe haber una manera más eficiente. ¿Alguien tiene alguna solución o algoritmos inteligentes sobre cómo puedo lograr esto?Android Mapview: fusión de marcadores superpuestos en un nuevo marcador
Respuesta
Um ... suponiendo que los marcadores no están agrupados, en capas o algo por el estilo: por qué - antes de mostrarlos - ¿no creas una cuadrícula de cierta densidad y simplemente colocas los marcadores en las celdas de tu cuadrícula?
Si cuenta que varios marcadores caen en el mismo contenedor (celda de cuadrícula), puede agruparlos. Si necesita una agrupación ligeramente más inteligente, también puede verificar las celdas vecinas.
Tal vez que suena un poco primitivo, pero:
- No n^2 algoritmos
- No suposición acerca de la adquisición de la entrada
- No hay necesidad de, además, marcadores de proceso que no van a ser mostrado
el código para la cuadrícula:
Nota: Vengo del mundo de C++ (aquí me tocó mediante la etiqueta [algorithm]), así que me quedaré con el pseudo-C++. No sé la API de la vista de mapa. Pero me sorprendería que esto no se pueda traducir eficientemente a cualquier idioma/biblioteca que esté utilizando.
de entrada: - lista de marcadores - el rectángulo ventana en coordenadas mundiales de visualización (sección del mundo que están estudiando)
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.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()
}
}
}
El problema que puede aparecer es que una cuadrícula indeseada puede aparecer dentro de un grupo agrupado, formando dos GroupMarkers. Para contrarrestar eso, es posible que desee considerar no solo una celda de cuadrícula, sino también sus vecinos en la sección "\ drawing", y, si está agrupada, marque las celdas vecinas como visitó.
Asumiendo que sus marcadores estén agrupados en una Objetorevelado, puede crear un método que se invocó 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 sorteo, puede dibujar el marcador agrupado o los individuos;
Algo así 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=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);
}
Gracias, voy a intentar esto. Podría ver que se vuelve un poco lento si hay miles de marcadores, pero supongo que no hay mejor manera. Esperaba que hubiera algo de soporte de API que he visto – NSjonas
Si se agrupan los marcadores, usted tendrá una idea clara a nivel de zoom que lo que debe mostrar los marcadores individuales o del grupo de marcación por ejemplo, nivel de zoom> 17 luego muestra marcadores individuales; de lo contrario, muestra el marcador de grupo. Solía código de algo como esto en mi ItemizedOverlay 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 e.g. route lines.
super.draw(canvas, mapv, false);
}
private void setMarkersForZoomLevel19()
{
for (JourneyOverlayItem item : mOverlays)
{
item.setMarker(areaPointIcon48);
}
}
Si su posible tener los marcadores individuales en una colección, usted puede fácilmente obtener el mayor y el menor latitud y longitud y la diferencia entre ellos se le da el rango de latitud y longitud (esto podría usarse para acercarse al tramo para mostrar el grupo de marcadores). Divida los tramos por 2 y debe tener el punto central para colocar el marcador de grupo.
¿Qué quieres decir con agrupado? Mis marcadores están todos en el mismo ItemizedOverlay. – NSjonas
Sí, pero podría tener los geopoints de su marcador en algunas colecciones, p. Ej.si tiene muchos marcadores en el cuadrante superior izquierdo del mapa, estos pueden estar en 1 colección y luego es más fácil identificar un punto central. Alternativamente, divida su mapa en sectores y tenga una colección para cada sector. –
Lo que está buscando generalmente se llama clustering. Hay técnicas comunes para hacer esto, puede referirse, por ejemplo, a este SO question, esto lleva a post.
La idea básica consiste en dividir el mapa en las plazas en función del nivel de zoom actual (se puede almacenar en caché los cálculos basados en el nivel de zoom para evitar un nuevo cálculo cuando el usuario inicia el zoom), y agruparlos basan en qué cuadrado al que pertenecen . Así que terminas teniendo algún tipo de agrupamiento basado en el nivel de zoom, es decir, para el nivel 1-5 simplemente dibuja los marcadores, para el nivel 5-8 agrupalos en cuadrados de 20 millas, para 9-10 en cuadrados de 50 millas, y así en.
Aquí es otra cuestión relevante encendido de modo que es posible que desee echar un vistazo, no estoy seguro sobre el rendimiento de esto, sin embargo: Android Maps Point Clustering
gracias, muy buena información. Tuve que darle la recompensa a CygnusX1 porque ya se había tomado la molestia de escribir esta solución, ya que le pregunté – NSjonas
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. Coloque este método en su Overlay personalizado y modifique drawSingle() y drawGroup() para adaptarlo a sus necesidades. También mejora el rendimiento, como convertir ArrayLists en matrices primitivas.
@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());
}
}
¿qué es mOverlays? ¿Qué necesito declarar como? – Shrikant
Son los resúmenes que obtiene a través de su MapView, por ejemplo, MapView.getOverlays(). Tenga en cuenta que este código es realmente descuidado, y debe convertir los ArrayLists en matrices primitivas para obtener un impulso de rendimiento masivo. – Maarten
Este es el enfoque que he utilizado. Sin embargo, es O (n^2).
Las clavijas se deben clasificar según su prominencia.
Pin de selección con el más alto prominente. Mire todos los alfileres que lo rodean. Absorbe los pernos cerca de ese pin.
Luego avance al siguiente pin más prominente. Hacer lo mismo. Repetir.
Simple.
Las cosas se complican si mueve el mapa, acerca y aleja la imagen, y quiere asegurarse de que las nuevas patillas no se vuelven a dibujar. Entonces, debe verificar cada grupo si tienen que dividirse durante el acercamiento, y luego verificar cada grupo si tienen que fusionarse durante el alejamiento. Luego quita los alfileres que se han ido y agrega nuevos alfileres. Por cada pin que agregue, compruebe si deben unirse a un clúster o formar su propio clúster.
- 1. MapView agregar marcadores al toque
- 2. android mapview marker size
- 3. marcador Clustering - Fusión Tabla de capa - Google Maps v3
- 4. agregar marcador múltiple en el mapa de google en android
- 5. Rotar MapView en Android
- 6. Moviendo iconos en un Android MapView
- 7. ¿Cómo detectar el movimiento de Google MapView en Android?
- 8. Mostrar el marcador emergente del mapa en MapView
- 9. Android MapView giratorio
- 10. Está fusionando una fusión de nuevo un problema en subversión?
- 11. android mapview tile personalizado
- 12. Cómo animar marcador cuando se agrega al mapa en Android?
- 13. Google Maps: marcador de renderizado sobre marcador de marcador
- 14. Fusionando archivos pdf con marcadores
- 15. Eventos de zoom que funcionan en un android Mapview
- 16. Diferentes marcadores con nombre en Google Android Map
- 17. Fundamentos de Git - Fusión de nuevo en el maestro
- 18. Android draw route en un Mapview con dos POI-s
- 19. Android - MapView contenida dentro de una Listview
- 20. MapView IOS MapView resize
- 21. Cómo rotar MapView?
- 22. Android Ícono de marcador personalizado
- 23. fusión de sensores implementada en Android?
- 24. ¿Cómo especificar un marcador de trazado particular de la lista automática de marcadores de Mathematica?
- 25. Añadir una imagen para Android MapView
- 26. ¿Cómo se puede hacer que múltiples marcadores superpuestos en Google Maps abran su propia ventana de información cuando se hace clic en
- 27. Reemplazo para Android Mapview para usar un servicio WMS
- 28. Marcadores de reinicio Jpeg
- 29. Intervalos personalizados de marcadores en Mathematica PlotMarkers
- 30. Android Agregar un nuevo calendario
¿Podría darme algún código de muestra sobre cómo crearía la red de manera eficiente? – NSjonas
¿Este código funciona correctamente cuando se acerca el mapa? Porque supongo que los cuadros de cuadrícula deben ser más grandes cuando el mapa se aleja. – adrianTNT
El tamaño de la cuadrícula depende directamente de las coordenadas del mundo del rectángulo de vista 'v' en las líneas donde se calculan los valores de' binIdx'. En consecuencia, se adaptará a su nivel de acercamiento. La "densidad fija" es la densidad de la cuadrícula en el espacio de la pantalla, no en el espacio del mundo. – CygnusX1