Búsqueda de sitios web

javascript - API V3 de Google Maps - múltiples marcadores en el mismo lugar exacto


Un poco atascado con este. Estoy recuperando una lista de coordenadas geográficas a través de JSON y colocándolas en un mapa de Google. Todo funciona bien excepto en el caso cuando tengo dos o más marcadores en el mismo lugar exacto. La API solo muestra 1 marcador, el superior. Supongo que esto es justo, pero me gustaría encontrar una manera de mostrarlos todos de alguna forma.

He buscado en Google y encontré algunas soluciones, pero en su mayoría parecen ser para la V2 de la API o simplemente no son tan buenas. Idealmente, me gustaría una solución en la que hagas clic en algún tipo de marcador de grupo y eso luego muestre los marcadores agrupados alrededor del lugar donde todos están.

¿Alguien ha tenido este problema o algo similar y le gustaría compartir una solución?

Respuesta #1

Echa 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 cerca unos de otros.

Pero se puede ver un ejemplo de la vida real con marcadores en el mismo lugar exacto en http://www.ejw.de/ejw-vor-ort/ (desplázate hacia abajo para ver el mapa y haz clic en algunos marcadores para ver el efecto de araña).

Eso parece ser la solución perfecta para tu problema.

Respuesta #2

Desplazar los marcadores no es una solución real si se encuentran en el mismo edificio. Lo que podrías querer hacer es modificar el markerclusterer.js de la siguiente manera:

  1. Agrega un método click al prototipo en la clase MarkerClusterer, de la siguiente manera: lo sobrescribiremos más tarde en la función initialize() del mapa:

    MarkerClusterer.prototype.onClick = function() { 
        return true; 
    };
    
  2. En la clase ClusterIcon, agregue el siguiente código DESPUÉS del disparador clusterclick:

    // 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);
    }
    
  3. Luego, en tu función initialize() donde inicializas el mapa y declaras tu objeto MarkerClusterer:

    markerCluster = new MarkerClusterer(map, markers);
    // onClickZoom OVERRIDE
    markerCluster.onClickZoom = function() { return multiChoice(markerCluster); }
    

    Donde multiChoice() es TU función (aún por escribir) para mostrar una ventana de información con una lista de opciones para seleccionar. Ten en cuenta que el objeto markerClusterer se pasa a tu función, porque lo necesitarás 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;
    }
    

Respuesta #3

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

Respuesta #4

Ampliando la respuesta de Chaoley, implementé una función que, dada una lista de ubicaciones (objetos con propiedades lng y lat) cuyas coordenadas son exactamente las mismas, las mueve un poco lejos de su ubicación original (modificando los objetos en el lugar). Luego forman un círculo bonito alrededor del punto central.

Descubrí que, para mi latitud (52 grados norte), 0,0003 grados de radio de círculo funcionan mejor, y que tienes que compensar la diferencia entre los grados de latitud y longitud cuando se convierten a kilómetros. Puedes encontrar conversiones aproximadas para tu latitud aquí.

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

Respuesta #5

@Ignatius excelente respuesta, actualizada para funcionar con la v2.0.7 de MarkerClustererPlus.

  1. Agrega un método click de prototipo en la clase MarkerClusterer, así: lo sobrescribiremos más tarde en la función initialize() del mapa:

    // BEGIN MODIFICATION (around line 715)
    MarkerClusterer.prototype.onClick = function() { 
        return true; 
    };
    // END MODIFICATION
    
  2. En la clase ClusterIcon, añade 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
    
  3. Luego, en tu función initialize() donde inicializas el mapa y declaras tu objeto MarkerClusterer:

    markerCluster = new MarkerClusterer(map, markers);
    // onClick OVERRIDE
    markerCluster.onClick = function(clickedClusterIcon) { 
      return multiChoice(clickedClusterIcon.cluster_); 
    }
    

    Donde multiChoice() es TU función (aún por escribir) para mostrar una ventana de información con una lista de opciones para seleccionar. Ten en cuenta que el objeto markerClusterer se pasa a tu función, porque lo necesitarás 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;
    };
    

Respuesta #6

Esta es más una solución provisional 'rápida y sucia' similar a la que sugiere Matthew Fox, esta vez usando JavaScript.

En JavaScript, puedes simplemente desplazar la latitud y longitud de todas tus ubicaciones agregando un pequeño desplazamiento aleatorio a ambos p. ej.

myLocation[i].Latitude+ = (Math.random() / 25000)

(Descubrí que dividir por 25000 da suficiente separación pero no mueve el marcador significativamente del lugar exacto, por ejemplo, una dirección específica)

Esto hace un trabajo razonablemente bueno al separarlos entre sí, pero solo después de que hayas hecho un zoom de cerca. Cuando está alejado, todavía no será claro que hay múltiples opciones para la ubicación.

Artículos relacionados