javascript - marcar - google maps marker size
Google maps API V3: marcadores múltiples en el mismo lugar exacto (16)
Poco atrapado en este. Estoy recuperando una lista de geo coords a través de JSON y apareciendo en un mapa de google. Todo funciona bien, excepto en el caso en que tengo dos o más marcadores exactamente en el mismo lugar. La API solo muestra 1 marcador: el más alto. Esto es bastante justo, supongo, pero me gustaría encontrar una manera de mostrarlos de alguna manera.
Busqué en Google y encontré algunas soluciones, pero en su mayoría parecen ser para V2 de la API o simplemente no tan bueno. Idealmente, me gustaría una solución donde haga clic en algún tipo de marcador de grupo y que luego muestre los marcadores agrupados alrededor del lugar donde están todos.
¿Alguien tuvo este problema o algo similar y le importaría compartir una solución?
@Ignatius es la respuesta más excelente, actualizada para trabajar con v2.0.7 de MarkerClustererPlus.
Agregue un método de clic de prototipo en la clase MarkerClusterer, como tal; lo anularemos más adelante en la función de inicialización de mapa ():
// BEGIN MODIFICATION (around line 715) MarkerClusterer.prototype.onClick = function() { return true; }; // END MODIFICATION
En la clase ClusterIcon, agregue el siguiente código DESPUÉS del disparador click / clusterclick:
// EXISTING CODE (around line 143) google.maps.event.trigger(mc, "click", cClusterIcon.cluster_); google.maps.event.trigger(mc, "clusterclick", cClusterIcon.cluster_); // deprecated name // BEGIN MODIFICATION var zoom = mc.getMap().getZoom(); // Trying to pull this dynamically made the more zoomed in clusters not render // when then kind of made this useless. -NNC @ BNB // var maxZoom = mc.getMaxZoom(); var maxZoom = 15; // if we have reached the maxZoom and there is more than 1 marker in this cluster // use our onClick method to popup a list of options if (zoom >= maxZoom && cClusterIcon.cluster_.markers_.length > 1) { return mc.onClick(cClusterIcon); } // END MODIFICATION
Luego, en su función initialize () donde inicializa el mapa y declara su objeto MarkerClusterer:
markerCluster = new MarkerClusterer(map, markers); // onClick OVERRIDE markerCluster.onClick = function(clickedClusterIcon) { return multiChoice(clickedClusterIcon.cluster_); }
Donde multiChoice () es SU función (aún por escribir) para abrir una Ventana de información con una lista de opciones para seleccionar. Tenga en cuenta que el objeto markerClusterer se pasa a su función porque lo necesitará para determinar cuántos marcadores hay en ese clúster. Por ejemplo:
function multiChoice(clickedCluster) { if (clickedCluster.getMarkers().length > 1) { // var markers = clickedCluster.getMarkers(); // do something creative! return false; } return true; };
Actualizado para trabajar con MarkerClustererPlus.
google.maps.event.trigger(mc, "click", cClusterIcon.cluster_);
google.maps.event.trigger(mc, "clusterclick", cClusterIcon.cluster_); // deprecated name
// BEGIN MODIFICATION
var zoom = mc.getMap().getZoom();
// Trying to pull this dynamically made the more zoomed in clusters not render
// when then kind of made this useless. -NNC @ BNB
// var maxZoom = mc.getMaxZoom();
var maxZoom = 15;
// if we have reached the maxZoom and there is more than 1 marker in this cluster
// use our onClick method to popup a list of options
if (zoom >= maxZoom && cClusterIcon.cluster_.markers_.length > 1) {
var markers = cClusterIcon.cluster_.markers_;
var a = 360.0 / markers.length;
for (var i=0; i < markers.length; i++)
{
var pos = markers[i].getPosition();
var newLat = pos.lat() + -.00004 * Math.cos((+a*i) / 180 * Math.PI); // x
var newLng = pos.lng() + -.00004 * Math.sin((+a*i) / 180 * Math.PI); // Y
var finalLatLng = new google.maps.LatLng(newLat,newLng);
markers[i].setPosition(finalLatLng);
markers[i].setMap(cClusterIcon.cluster_.map_);
}
cClusterIcon.hide();
return ;
}
// END MODIFICATION
Ampliando la respuesta de Chaoley , implementé una función que, dada una lista de ubicaciones (objetos con propiedades de lat
y lat
) cuyas coordenadas son exactamente las mismas, los aleja un poco de su ubicación original (modificando objetos en su lugar). Luego forman un bonito círculo alrededor del punto central.
Descubrí que, para mi latitud (52 grados norte), los 0.0003 grados de radio del círculo funcionan mejor, y que debe compensar la diferencia entre los grados de latitud y longitud cuando se convierten a kilómetros. here puede encontrar las conversiones aproximadas para su latitud.
var correctLocList = function (loclist) {
var lng_radius = 0.0003, // degrees of longitude separation
lat_to_lng = 111.23 / 71.7, // lat to long proportion in Warsaw
angle = 0.5, // starting angle, in radians
loclen = loclist.length,
step = 2 * Math.PI / loclen,
i,
loc,
lat_radius = lng_radius / lat_to_lng;
for (i = 0; i < loclen; ++i) {
loc = loclist[i];
loc.lng = loc.lng + (Math.cos(angle) * lng_radius);
loc.lat = loc.lat + (Math.sin(angle) * lat_radius);
angle += step;
}
};
Ampliando las respuestas dadas anteriormente, solo asegúrese de configurar la opción maxZoom al inicializar el objeto del mapa.
Cómo salirse con la suya .. [Swift]
var clusterArray = [String]()
var pinOffSet : Double = 0
var pinLat = yourLat
var pinLong = yourLong
var location = pinLat + pinLong
¿Está a punto de crearse un nuevo marcador? comprueba clusterArray
y manipularlo es offset
if(!clusterArray.contains(location)){
clusterArray.append(location)
} else {
pinOffSet += 1
let offWithIt = 0.00025 // reasonable offset with zoomLvl(14-16)
switch pinOffSet {
case 1 : pinLong = pinLong + offWithIt ; pinLat = pinLat + offWithIt
case 2 : pinLong = pinLong + offWithIt ; pinLat = pinLat - offWithIt
case 3 : pinLong = pinLong - offWithIt ; pinLat = pinLat - offWithIt
case 4 : pinLong = pinLong - offWithIt ; pinLat = pinLat + offWithIt
default : print(1)
}
}
resultado
Compensar los marcadores no es una solución real si están ubicados en el mismo edificio. Lo que podría querer hacer es modificar el marcadorclusterrerer.js de la siguiente manera:
Agregue un método de clic de prototipo en la clase MarkerClusterer, como tal; lo anularemos más adelante en la función de inicialización de mapa ():
MarkerClusterer.prototype.onClick = function() { return true; };
En la clase ClusterIcon, agregue el siguiente código DESPUÉS del activador de clúster de clúster:
// Trigger the clusterclick event. google.maps.event.trigger(markerClusterer, ''clusterclick'', this.cluster_); var zoom = this.map_.getZoom(); var maxZoom = markerClusterer.getMaxZoom(); // if we have reached the maxZoom and there is more than 1 marker in this cluster // use our onClick method to popup a list of options if (zoom >= maxZoom && this.cluster_.markers_.length > 1) { return markerClusterer.onClickZoom(this); }
Luego, en su función initialize () donde inicializa el mapa y declara su objeto MarkerClusterer:
markerCluster = new MarkerClusterer(map, markers); // onClickZoom OVERRIDE markerCluster.onClickZoom = function() { return multiChoice(markerCluster); }
Donde multiChoice () es SU función (aún por escribir) para abrir una Ventana de información con una lista de opciones para seleccionar. Tenga en cuenta que el objeto markerClusterer se pasa a su función porque lo necesitará para determinar cuántos marcadores hay en ese clúster. Por ejemplo:
function multiChoice(mc) { var cluster = mc.clusters_; // if more than 1 point shares the same lat/long // the size of the cluster array will be 1 AND // the number of markers in the cluster will be > 1 // REMEMBER: maxZoom was already reached and we can''t zoom in anymore if (cluster.length == 1 && cluster[0].markers_.length > 1) { var markers = cluster[0].markers_; for (var i=0; i < markers.length; i++) { // you''ll probably want to generate your list of options here... } return false; } return true; }
Dar compensación hará que los marcadores sean lejanos cuando el usuario haga zoom en un máximo de. Entonces encontré una manera de lograr eso. esto puede no ser una forma adecuada, pero funcionó muy bien.
// This code is in swift
for loop markers
{
//create marker
let mapMarker = GMSMarker()
mapMarker.groundAnchor = CGPosition(0.5, 0.5)
mapMarker.position = //set the CLLocation
//instead of setting marker.icon set the iconView
let image:UIIMage = UIIMage:init(named:"filename")
let imageView:UIImageView = UIImageView.init(frame:rect(0,0, ((image.width/2 * markerIndex) + image.width), image.height))
imageView.contentMode = .Right
imageView.image = image
mapMarker.iconView = imageView
mapMarker.map = mapView
}
Establezca el zIndex del marcador para que pueda ver el icono del marcador que desea ver en la parte superior, de lo contrario, animará los marcadores como el intercambio automático. cuando el usuario toca el marcador, maneje el índice z para poner el marcador en la parte superior utilizando zIndex Swap.
Echa un vistazo a Marker Clusterer para V3: esta biblioteca agrupa los puntos cercanos en un marcador de grupo. El mapa se acerca cuando se hace clic en los clústeres. Me imagino que cuando se acercó el zoom todavía tendría el mismo problema con los marcadores en el mismo lugar.
Eche un vistazo a OverlappingMarkerSpiderfier .
Hay una página de demostración, pero no muestran marcadores que están exactamente en el mismo lugar, solo algunos que están muy juntos.
Pero un ejemplo de la vida real con marcadores en el mismo lugar se puede ver en http://www.ejw.de/ejw-vor-ort/ (desplácese hacia abajo para ver el mapa y haga clic en algunos marcadores para ver el efecto de araña) )
Esa parece ser la solución perfecta para tu problema.
Extendiendo las respuestas de arriba, cuando se unieron cadenas, no se agregó / restó la posición (por ejemplo, "37.12340-0,00069"), convertir su latitud / longitud original en flotantes, por ejemplo, usando parseFloat (), luego agregar o restar correcciones.
Las respuestas anteriores son más elegantes, pero encontré una manera rápida y sucia que realmente funciona realmente increíblemente bien. Puedes verlo en acción en www.buildinglit.com
Todo lo que hice fue agregar un desplazamiento aleatorio a la latitud y longitude de mi página genxml.php, por lo que cada vez que se crea el mapa con marcadores, se obtienen resultados ligeramente diferentes con desplazamiento. Esto suena como un hack, pero en realidad solo necesitas los marcadores para mover un pequeño empujón en una dirección aleatoria para que puedan hacer clic en el mapa si se superponen. De hecho, funciona muy bien, diría mejor que el método de araña porque quiere lidiar con esa complejidad y hacer que broten en todas partes. Solo desea poder seleccionar el marcador. Empujarlo al azar funciona perfecto.
Aquí hay un ejemplo de la creación del nodo de iteración de sentencias while en mi php_genxml.php
while ($row = @mysql_fetch_assoc($result)){ $offset = rand(0,1000)/10000000;
$offset2 = rand(0, 1000)/10000000;
$node = $dom->createElement("marker");
$newnode = $parnode->appendChild($node);
$newnode->setAttribute("name", $row[''name'']);
$newnode->setAttribute("address", $row[''address'']);
$newnode->setAttribute("lat", $row[''lat''] + $offset);
$newnode->setAttribute("lng", $row[''lng''] + $offset2);
$newnode->setAttribute("distance", $row[''distance'']);
$newnode->setAttribute("type", $row[''type'']);
$newnode->setAttribute("date", $row[''date'']);
$newnode->setAttribute("service", $row[''service'']);
$newnode->setAttribute("cost", $row[''cost'']);
$newnode->setAttribute("company", $company);
Observe debajo de lat y long está el + desplazamiento. de las 2 variables anteriores. Tuve que dividir aleatoriamente entre 0,1000 por 10000000 para obtener un decimal que fuera lo suficientemente pequeño como para apenas mover los marcadores. Siéntase libre de jugar con esa variable para obtener una que sea más precisa para sus necesidades.
Me gustan las soluciones simples así que aquí está el mío. En lugar de modificar la lib, lo que haría más difícil mantener. simplemente puedes ver el evento como este
google.maps.event.addListener(mc, "clusterclick", onClusterClick);
entonces puedes administrarlo en
function onClusterClick(cluster){
var ms = cluster.getMarkers();
Yo, es decir, usé bootstrap para mostrar un panel con una lista. que me resulta mucho más cómodo y utilizable que arañar en lugares "llenos". (Si está utilizando un clusterer es probable que termine con colisiones una vez que spiderfy). puedes consultar el zoom allí también.
por cierto. Acabo de encontrar un folleto y parece funcionar mucho mejor, el cluster AND spiderfy funciona de forma muy fluida http://leaflet.github.io/Leaflet.markercluster/example/marker-clustering-realworld.10000.html y es de código abierto.
Para situaciones en las que hay servicios múltiples en el mismo edificio, podría compensar los marcadores un poco (por ejemplo, en un grado de 0,001) en un radio desde el punto real. Esto también debería producir un buen efecto visual.
Se trata más de una solución provisional ''rápida y sucia'' similar a la sugerida por Matthew Fox, esta vez usando JavaScript.
En JavaScript, puede compensar el lat y long de todas sus ubicaciones agregando un pequeño desplazamiento aleatorio a ambos, por ejemplo
myLocation[i].Latitude+ = (Math.random() / 25000)
(Encontré que dividir por 25000 da suficiente separación pero no mueve el marcador significativamente desde la ubicación exacta, por ejemplo, una dirección específica)
Esto hace un trabajo razonablemente bueno de compensarlos entre sí, pero solo después de haber acercado el zoom. Cuando se aleja, aún no está claro que hay múltiples opciones para la ubicación.
Usé esto junto con jQuery y hace el trabajo:
var map;
var markers = [];
var infoWindow;
function initialize() {
var center = new google.maps.LatLng(-29.6833300, 152.9333300);
var mapOptions = {
zoom: 5,
center: center,
panControl: false,
zoomControl: false,
mapTypeControl: false,
scaleControl: false,
streetViewControl: false,
overviewMapControl: false,
mapTypeId: google.maps.MapTypeId.ROADMAP
}
map = new google.maps.Map(document.getElementById(''map-canvas''), mapOptions);
$.getJSON(''jsonbackend.php'', function(data) {
infoWindow = new google.maps.InfoWindow();
$.each(data, function(key, val) {
if(val[''LATITUDE'']!='''' && val[''LONGITUDE'']!='''')
{
// Set the coordonates of the new point
var latLng = new google.maps.LatLng(val[''LATITUDE''],val[''LONGITUDE'']);
//Check Markers array for duplicate position and offset a little
if(markers.length != 0) {
for (i=0; i < markers.length; i++) {
var existingMarker = markers[i];
var pos = existingMarker.getPosition();
if (latLng.equals(pos)) {
var a = 360.0 / markers.length;
var newLat = pos.lat() + -.00004 * Math.cos((+a*i) / 180 * Math.PI); //x
var newLng = pos.lng() + -.00004 * Math.sin((+a*i) / 180 * Math.PI); //Y
var latLng = new google.maps.LatLng(newLat,newLng);
}
}
}
// Initialize the new marker
var marker = new google.maps.Marker({map: map, position: latLng, title: val[''TITLE'']});
// The HTML that is shown in the window of each item (when the icon it''s clicked)
var html = "<div id=''iwcontent''><h3>"+val[''TITLE'']+"</h3>"+
"<strong>Address: </strong>"+val[''ADDRESS'']+", "+val[''SUBURB'']+", "+val[''STATE'']+", "+val[''POSTCODE'']+"<br>"+
"</div>";
// Binds the infoWindow to the point
bindInfoWindow(marker, map, infoWindow, html);
// Add the marker to the array
markers.push(marker);
}
});
// Make a cluster with the markers from the array
var markerCluster = new MarkerClusterer(map, markers, { zoomOnClick: true, maxZoom: 15, gridSize: 20 });
});
}
function markerOpen(markerid) {
map.setZoom(22);
map.panTo(markers[markerid].getPosition());
google.maps.event.trigger(markers[markerid],''click'');
switchView(''map'');
}
google.maps.event.addDomListener(window, ''load'', initialize);
Verifique esto: https://github.com/plank/MarkerClusterer
Este es el MarkerCluster modificado para tener una ventana de información en un marcador de clúster, cuando tiene varios marcadores en la misma posición.
Puedes ver cómo funciona aquí: http://culturedays.ca/en/2013-activities