Google maps API V3 - несколько маркеров на одном месте - PullRequest
109 голосов
/ 23 августа 2010

Бит застрял на этом. Я получаю список гео-координат через JSON и выкладываю их на карту Google. Все работает хорошо, за исключением случая, когда у меня есть два или более маркеров на одном и том же месте. API отображает только 1 маркер - самый верхний. Полагаю, это достаточно справедливо, но я хотел бы найти способ как-то отобразить их все.

Я искал в Google и нашел несколько решений, но они в основном, похоже, для V2 API или просто не так уж и хороши. В идеале я хотел бы получить решение, в котором вы щелкаете по какому-то групповому маркеру, и тогда в нем отображаются маркеры, сгруппированные вокруг места, где они все находятся.

Кто-нибудь имел эту проблему или подобное и хотел бы поделиться решением?

Ответы [ 17 ]

111 голосов
/ 12 апреля 2012

Взгляните на OverlappingMarkerSpiderfier .
Есть демонстрационная страница, но они не показывают маркеры, которые находятся точно в одном месте, только некоторые, которые очень близко друг к другу.

Но реальный пример с маркерами на одном и том же месте можно увидеть на http://www.ejw.de/ejw-vor-ort/ (прокрутите карту вниз и нажмите несколько маркеров, чтобы увидеть эффект паука).

Кажется, это идеальное решение для вашей проблемы.

32 голосов
/ 27 мая 2011

Смещение маркеров не является реальным решением, если они расположены в одном здании.Возможно, вы захотите изменить markerclusterer.js следующим образом:

  1. Добавьте метод прототипа click в классе MarkerClusterer, например, - мы переопределим это позже в инициализации карты () функция:

    MarkerClusterer.prototype.onClick = function() { 
        return true; 
    };
    
  2. В классе ClusterIcon добавьте следующий код ПОСЛЕ триггера кластерного клика:

    // 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. Затем вВаша функция initialize (), в которой вы инициализируете карту и объявляете свой объект MarkerClusterer:

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

    Где multiChoice () - это ВАША (еще не написанная) функция для всплывающего окна InfoWindow со списком параметров, которые можно выбрать,Обратите внимание, что объект markerClusterer передается в вашу функцию, потому что он понадобится вам для определения количества маркеров в этом кластере.Например:

    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;
    }
    
15 голосов
/ 02 сентября 2014

Я использовал это вместе с jQuery, и он делает свою работу:

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);
14 голосов
/ 04 февраля 2012

Расширяя Ответ Чаули , я реализовал функцию, которая, учитывая список местоположений (объекты со свойствами lng и lat), чьи координаты в точности совпадают, отодвигает их от исходногорасположение немного (изменение объектов на месте).Затем они образуют хороший круг вокруг центральной точки.

Я обнаружил, что для моей широты (52 градуса северной широты) лучше всего подходит 0,0003 градуса радиуса круга, и что вы должны компенсировать разницу между широтой иградусы долготы при пересчете на километры.Вы можете найти приблизительные преобразования для вашей широты здесь .

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;
    }
};
9 голосов
/ 20 января 2012

@ Отличный ответ Игнатия, обновленный для работы с v2.0.7 MarkerClustererPlus.

  1. Добавьте метод прототипа click в классе MarkerClusterer, вот так - мы переопределим это позже вфункция инициализации карты ():

    // BEGIN MODIFICATION (around line 715)
    MarkerClusterer.prototype.onClick = function() { 
        return true; 
    };
    // END MODIFICATION
    
  2. В классе ClusterIcon добавьте следующий код ПОСЛЕ триггера 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. Затем в вашей функции initialize (), где вы инициализируете карту и объявляете свой объект MarkerClusterer:

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

    Где multiChoice () - это ВАША (еще не написанная) функция для всплывающего окна InfoWindow ссписок опций на выбор.Обратите внимание, что объект markerClusterer передается в вашу функцию, потому что он понадобится вам для определения количества маркеров в этом кластере.Например:

    function multiChoice(clickedCluster) {
      if (clickedCluster.getMarkers().length > 1)
      {
        // var markers = clickedCluster.getMarkers();
        // do something creative!
        return false;
      }
      return true;
    };
    
4 голосов
/ 04 мая 2016

Ответы выше более элегантны, но я нашел быстрый и грязный способ, который на самом деле работает действительно очень хорошо. Вы можете увидеть это в действии на www.buildinglit.com

Все, что я сделал, это добавил случайное смещение широты и долготы к моей странице genxml.php, чтобы она возвращала немного разные результаты каждый раз со смещением при каждом создании карты с маркерами. Это звучит как хак, но на самом деле вам нужны маркеры только для того, чтобы слегка подтолкнуть их в случайном направлении, чтобы они могли нажиматься на карте, если они перекрывают друг друга. Это на самом деле работает очень хорошо, я бы сказал, лучше, чем метод паука, потому что, кто хочет справиться с этой сложностью и заставить их прыгать везде. Вы просто хотите иметь возможность выбрать маркер. Выдавливание случайно работает отлично.

Вот пример создания узла итерации оператора while в моем 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);

Обратите внимание, что под lat и long указывается + смещение. из 2 переменных выше. Мне пришлось делить случайное число на 0,1000 на 10000000, чтобы получить десятичную дробь, которая была достаточно мала, чтобы едва перемещать маркеры. Не стесняйтесь возиться с этой переменной, чтобы получить более точную для ваших нужд.

3 голосов
/ 25 августа 2010

Для ситуаций, когда в одном здании несколько служб, вы можете немного сместить маркеры (скажем, на 0,001 градуса) в радиусе от фактической точки.Это также должно дать хороший визуальный эффект.

2 голосов
/ 23 августа 2010

Check Marker Clusterer для V3 - эта библиотека группирует соседние точки в групповой маркерКарта увеличивается при щелчке по кластерам.Я думаю, что при увеличении масштаба у вас все еще будет та же проблема с маркерами на том же месте.

1 голос
/ 05 июня 2014

Обновлено для работы с 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
1 голос
/ 20 сентября 2017

Это скорее «быстрое и грязное» решение с временным ограничением, похожее на предложенное Мэтью Фоксом, на этот раз с использованием JavaScript.

В JavaScript вы можете просто сместить широту и длину всех ваших местоположений, добавив небольшое случайное смещение к обоим например,

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

(я обнаружил, что деление на 25000 дает достаточное разделение, но не приводит к значительному перемещению маркера от точного местоположения, например, от определенного адреса)

Это делает довольно хорошую работу по смещению их друг от друга, но только после того, как вы приблизитесь. При уменьшении все равно будет неясно, есть ли несколько вариантов местоположения.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...