Google Maps API V3: più marker nello stesso identico punto

Bit bloccato su questo. Sto recuperando una lista di geo coords tramite JSON e inserendoli su una mappa di google. Tutto funziona bene, tranne nel caso in cui ho due o più marcatori sullo stesso identico punto. L’API visualizza solo 1 marcatore, quello superiore. Questo è abbastanza giusto, suppongo, ma vorrei trovare un modo per visualizzarli tutti in qualche modo.

Ho cercato su google e ho trovato alcune soluzioni, ma per lo più sembrano essere per la V2 dell’API o semplicemente non così grandiose. Idealmente mi piacerebbe una soluzione in cui si fa clic su una sorta di marcatore di gruppo e che poi mostra i marcatori raggruppati attorno al punto in cui si trovano tutti.

Qualcuno ha avuto questo problema o simili e avrebbe voluto condividere una soluzione?

Dai un’occhiata a OverlappingMarkerSpiderfier .
C’è una pagina demo, ma non mostrano marcatori che si trovano esattamente nello stesso punto, solo alcuni che sono molto vicini tra loro.

Ma un vero esempio di vita con marcatori nello stesso identico punto può essere visto su http://www.ejw.de/ejw-vor-ort/ (scorrere verso il basso per la mappa e fare clic su alcuni indicatori per vedere l’effetto ragno ).

Questa sembra essere la soluzione perfetta per il tuo problema.

La compensazione dei marcatori non è una soluzione reale se si trovano nello stesso edificio. Quello che potresti voler fare è modificare markerclusterer.js in questo modo:

  1. Aggiungi un metodo di clic prototipo nella class MarkerClusterer, in questo modo: lo sovrascriveremo più avanti nella funzione initialize () della mappa:

     MarkerClusterer.prototype.onClick = function() { return true; }; 
  2. Nella class ClusterIcon, aggiungere il seguente codice AFTER al trigger 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. Quindi, nella funzione initialize () in cui si inizializza la mappa e si dichiara l’object MarkerClusterer:

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

    Dove multiChoice () è la funzione TUO (ancora da scrivere) per visualizzare una finestra InfoWindow con un elenco di opzioni tra cui scegliere. Nota che l’object markerClusterer viene passato alla tua funzione, poiché avrai bisogno di questo per determinare quanti marker ci sono in quel cluster. Per esempio:

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

Espandendo la risposta di Chaoley , ho implementato una funzione che, dato un elenco di posizioni (oggetti con proprietà lng e lat ) le cui coordinate sono esattamente le stesse, le allontana leggermente dalla loro posizione originale (modificando gli oggetti sul posto). Quindi formano un bel cerchio attorno al punto centrale.

Ho scoperto che, per la mia latitudine (52 gradi nord), 0,0003 gradi di raggio del cerchio funzionano meglio e che devi compensare la differenza tra gradi di latitudine e longitudine quando convertiti in chilometri. Qui puoi trovare le conversioni approssimative per la tua latitudine.

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

L’ho usato insieme a jQuery e fa il lavoro:

 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 = "

"+val['TITLE']+"

"+ "Address: "+val['ADDRESS']+", "+val['SUBURB']+", "+val['STATE']+", "+val['POSTCODE']+"
"+ "
"; // 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);

@Ignatius risposta più eccellente, aggiornata per lavorare con v2.0.7 di MarkerClustererPlus.

  1. Aggiungi un metodo di clic prototipo nella class MarkerClusterer, in questo modo: lo sovrascriveremo più avanti nella funzione initialize () della mappa:

     // BEGIN MODIFICATION (around line 715) MarkerClusterer.prototype.onClick = function() { return true; }; // END MODIFICATION 
  2. Nella class ClusterIcon, aggiungi il codice seguente DOPO il trigger 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. Quindi, nella funzione initialize () in cui si inizializza la mappa e si dichiara l’object MarkerClusterer:

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

    Dove multiChoice () è la funzione TUO (ancora da scrivere) per visualizzare una finestra InfoWindow con un elenco di opzioni tra cui scegliere. Nota che l’object markerClusterer viene passato alla tua funzione, poiché avrai bisogno di questo per determinare quanti marker ci sono in quel cluster. Per esempio:

     function multiChoice(clickedCluster) { if (clickedCluster.getMarkers().length > 1) { // var markers = clickedCluster.getMarkers(); // do something creative! return false; } return true; }; 

Per le situazioni in cui vi sono più servizi nello stesso edificio, è ansible compensare leggermente i marcatori (ad esempio di 0,001 gradi), in un raggio dal punto reale. Questo dovrebbe anche produrre un bell’effetto visivo.

Le risposte sopra sono più eleganti, ma ho trovato un modo veloce e sporco che funziona davvero davvero incredibilmente bene. Puoi vederlo in azione su http://www.buildinglit.com

Tutto ciò che ho fatto è stato aggiungere un offset casuale alla latitudine e longitudine alla mia pagina genxml.php in modo che restituisca risultati leggermente diversi ogni volta con offset ogni volta che la mappa viene creata con marcatori. Questo suona come un hack, ma in realtà hai bisogno solo dei marcatori per spostare leggermente la spinta in una direzione casuale affinché siano cliccabili sulla mappa se si sovrappongono. Funziona davvero molto bene, direi migliore del metodo spider perché chi vuole affrontare questa complessità e li molla ovunque. Vuoi solo essere in grado di selezionare il marcatore. Spingendola a caso funziona perfettamente.

Ecco un esempio della creazione del nodo iterazione dell’istruzione while nel mio 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); 

Si noti sotto lat e long c’è l’offset +. dalle 2 variabili sopra. Ho dovuto dividere casualmente per 0,1000 per 10000000 al fine di ottenere un decimale che fosse sufficientemente piccolo abbastanza da spostare a mala pena i marcatori. Sentiti libero di armeggiare con quella variabile per ottenere quella che è più precisa per le tue esigenze.

Scopri Marker Clusterer per V3: questa libreria raggruppa i punti vicini in un marcatore di gruppo. La mappa ingrandisce quando si fa clic sui cluster. Immagino che quando avvii lo zoom, avresti comunque lo stesso problema con i marcatori nello stesso punto.

Aggiornato per funzionare 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 

Mi piacciono le soluzioni semplici quindi ecco la mia. Invece di modificare la lib, che renderebbe più difficile la manutenzione. puoi semplicemente guardare l’evento in questo modo

 google.maps.event.addListener(mc, "clusterclick", onClusterClick); 

allora puoi gestirlo

 function onClusterClick(cluster){ var ms = cluster.getMarkers(); 

io, cioè, ho usato bootstrap per mostrare un pannello con un elenco. che trovo molto più confortevole e utilizzabile di quanto lo sia lo spiderfying in luoghi “affollati”. (Se stai usando un clusterer è probabile che finirai con le collisioni una volta che sarai diventato spiderfy). puoi controllare anche lo zoom.

btw. Ho appena trovato un volantino e sembra funzionare molto meglio, il cluster AND spiderfy funziona in modo molto fluido http://leaflet.github.io/Leaflet.markercluster/example/marker-clustering-realworld.10000.html ed è open-source.

Controlla questo: https://github.com/plank/MarkerClusterer

Questo è il MarkerCluster modificato per avere un infoWindow in un marcatore cluster, quando si hanno diversi marcatori nella stessa posizione.

Puoi vedere come funziona qui: http://culturedays.ca/en/2013-activities

Espandendo le risposte fornite sopra, assicurati di impostare l’opzione maxZoom durante l’inizializzazione dell’object mappa.

Dare offset renderà i marker lontani quando l’utente ingrandirà al massimo. Quindi ho trovato un modo per farlo. questo potrebbe non essere un modo corretto ma ha funzionato molto bene.

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

imposta zIndex del marcatore in modo che tu possa vedere l’icona del marcatore che vuoi vedere in alto, altrimenti animerà i marcatori come lo scambio automatico. quando l’utente tocca l’indicatore, mantieni zIndex per portare l’indicatore in alto usando zIndex Swap.

Questa è più di una soluzione “rapida e sporca” di ripiego simile a quella suggerita da Matthew Fox, stavolta usando JavaScript.

In JavaScript puoi semplicemente compensare lat e long di tutte le tue posizioni aggiungendo un piccolo offset casuale ad entrambi

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

(Ho scoperto che la divisione per 25000 fornisce una separazione sufficiente ma non sposta il marcatore in modo significativo dalla posizione esatta, ad esempio un indirizzo specifico)

Questo rende ragionevolmente un buon lavoro nel compensarli l’uno dall’altro, ma solo dopo aver ingrandito da vicino. Quando si rimpicciolisce, non sarà ancora chiaro se ci sono più opzioni per la posizione.

Come farla franca .. [Swift]

  var clusterArray = [String]() var pinOffSet : Double = 0 var pinLat = yourLat var pinLong = yourLong var location = pinLat + pinLong 

Un nuovo marker sta per essere creato? controlla clusterArray e manipola il suo 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) } } 

risultato

inserisci la descrizione dell'immagine qui

Estendendo le risposte sopra, quando hai unito le stringhe, non hai aggiunto / sottratto la posizione (es. “37.12340-0.00069”), converti il ​​lat / longitudine originale in float, ad esempio usando parseFloat (), quindi aggiungi o sottrai le correzioni.