Google maps API V3 - 完全相同位置的多个标记

Lou*_*oid 111 javascript google-maps google-maps-api-3

有点卡在这一个.我正在通过JSON检索地理坐标列表并将它们弹出到谷歌地图上.除了在完全相同的位置上有两个或更多标记的情况下,一切都运行良好.API仅显示1个标记 - 顶部标记.我认为这是公平的,但我想找到一种方法以某种方式显示它们.

我搜索过google并找到了一些解决方案,但它们似乎主要用于API的V2或者不是那么好.理想情况下,我想要一个解决方案,您可以单击某种组标记,然后显示聚集在它们所在位置周围的标记.

任何人都有这个问题或类似问题并愿意分享解决方案?

小智 113

看看OverlappingMarkerSpiderfier.
有一个演示页面,但它们不会显示完全位于同一位置的标记,只有一些非常接近.

但是在http://www.ejw.de/ejw-vor-ort/上可以看到在完全相同的位置上有标记的真实例子(向下滚动地图并点击几个标记来查看蜘蛛效应).

这似乎是您问题的完美解决方案.

  • OverlappingMarkerSpiderfier 代码稍显陈旧且死气沉沉,问题跟踪器中存在许多突出的问题。它工作正常,但似乎有很多错误(其中一些对我来说很重要),所以在投入时间之前要小心。 (3认同)

小智 33

如果它们位于同一建筑物中,则抵消标记不是真正的解决方案.您可能想要做的是修改markerclusterer.js,如下所示:

  1. 在MarkerClusterer类中添加原型click方法,就像这样 - 稍后我们将在map initialize()函数中覆盖它:

    MarkerClusterer.prototype.onClick = function() { 
        return true; 
    };
    
    Run Code Online (Sandbox Code Playgroud)
  2. 在ClusterIcon类中,在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);
    }
    
    Run Code Online (Sandbox Code Playgroud)
  3. 然后,在初始化地图并声明MarkerClusterer对象的initialize()函数中:

    markerCluster = new MarkerClusterer(map, markers);
    // onClickZoom OVERRIDE
    markerCluster.onClickZoom = function() { return multiChoice(markerCluster); }
    
    Run Code Online (Sandbox Code Playgroud)

    其中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;
    }
    
    Run Code Online (Sandbox Code Playgroud)

  • @DanishAdeel没有。该链接与任何内容均无关。 (5认同)

小智 16

我和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);
Run Code Online (Sandbox Code Playgroud)


Dzi*_*inX 14

扩展Chaoley的答案,我实现了一个函数,给定一个位置(具有lnglat属性的对象)的坐标完全相同的列表,将它们从原始位置稍微移开(修改对象).然后他们围绕中心点形成一个漂亮的圆圈.

我发现,对于我的纬度(北纬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;
    }
};
Run Code Online (Sandbox Code Playgroud)

  • DzinX,我花了过去 4 个小时试图弄清楚如何将您的代码实现到我的代码中,但没有成功。我得出的结论是我对此很糟糕,并且非常感谢您的帮助。这是我尝试插入代码的地方:http://pastebin.com/5qYbvybm - 任何帮助将不胜感激!谢谢! (2认同)

Nat*_*ate 9

@Ignatius最优秀的答案,更新后与MarkerClustererPlus的v2.0.7一起使用.

  1. 在MarkerClusterer类中添加原型click方法,就像这样 - 稍后我们将在map initialize()函数中覆盖它:

    // BEGIN MODIFICATION (around line 715)
    MarkerClusterer.prototype.onClick = function() { 
        return true; 
    };
    // END MODIFICATION
    
    Run Code Online (Sandbox Code Playgroud)
  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
    
    Run Code Online (Sandbox Code Playgroud)
  3. 然后,在初始化地图并声明MarkerClusterer对象的initialize()函数中:

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

    其中multiChoice()是您的(尚未编写)函数,用于弹出InfoWindow,其中包含可供选择的选项列表.请注意,markerClusterer对象将传递给您的函数,因为您需要此对象来确定该集群中有多少个标记.例如:

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


Chr*_*row 6

这更像是一种类似于 Matthew Fox 建议的权宜之计的“快速而肮脏”的解决方案,这次使用的是 JavaScript。

在 JavaScript 中,您可以通过向两者添加一个小的随机偏移量来抵消所有位置的经纬度,例如

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

(我发现除以25000 可以提供足够的间隔,但不会将标记从确切位置(例如特定地址)显着移动)

这可以很好地将它们相互抵消,但前提是您必须仔细放大。缩小后,仍然不清楚该位置有多个选项。

  • 我喜欢这个。如果您不需要准确的代表性标记,只需将 25000 降低到 250 或 25。简单快捷的解决方案。 (2认同)

小智 5

上面的答案更加优雅,但是我发现一种快速有效的方法实际上确实非常好。您可以在www.buildinglit.com上看到它的运行情况

我所做的只是在genxml.php页面的纬度和经度上添加了一个随机偏移,因此每次使用标记创建地图时,每次使用偏移都会返回略有不同的结果。这听起来像是骇客,但实际上,您只需要标记沿随机方向轻微移动即可使它们在重叠时在地图上点击。实际上,它确实工作得很好,我想说比蜘蛛方法更好,因为谁想要处理这种复杂性并使它们无处不在。您只希望能够选择标记。随机将其完美地工作。

这是我的php_genxml.php中while语句迭代节点创建的示例

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);
Run Code Online (Sandbox Code Playgroud)

注意在lat和long下有+ offset。从上面的2个变量。我必须将随机数除以0,1000除以10000000,才能得到一个十进制的小数,该小数足以使标记几乎不移动。随意修改该变量,以获取更符合您需求的变量。