谷歌地图 API V3 - 同一地点的多个标记

IT技术 javascript google-maps google-maps-api-3
2021-01-15 22:40:32

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

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

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

6个回答

看看OverlappingMarkerSpiderfier
有一个演示页面,但它们没有显示完全在同一位置的标记,只有一些非常靠近。

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

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

如果OverlappingMarkerSpiderfierMarkerClusterer结合使用一个不错的选择是maxZoom在集群构造函数上设置选项。这将在指定的缩放级别之后“分离”标记。
2021-03-18 22:40:32
OverlappingMarkerSpiderfier 代码略显陈旧和死机,问题跟踪器中有许多未解决的问题。它工作正常,但似乎有很多错误(其中一些对我来说很重要),所以在投入时间之前要小心。
2021-03-24 22:40:32
我检查了提议的演示,但它似乎已经死了。经过进一步研究,我发现了这个关于如何集成标记集群和 Spiderifier 的示例。没有现场演示,只是克隆并按照自述文件进行操作。 github.com/yagoferrer/markerclusterer-plus-spiderfier-example
2021-03-29 22:40:32
这很棒,并且完美地满足了集群库留下的需求,它不处理具有相同精确纬度/经度的标记。
2021-04-06 22:40:32
@Tad 我不知道您是否是该网站的开发者,但我想让您知道 ejw.de 上的地图已损坏。我收到一个 JS 错误。
2021-04-12 22:40:32

如果标记位于同一建筑物中,则偏移标记并不是真正的解决方案。您可能想要做的是像这样修改markerclusterer.js:

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

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

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

    multiChoice() 是您的(尚未编写)函数,用于弹出一个带有可供选择的选项列表的信息窗口。请注意,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;
    }
    

我将它与 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);
美丽而优雅的解决方案。谢谢!
2021-03-17 22:40:32
完美的解决方案抵消了一点点!是否有可能,我们将鼠标悬停在标记上,然后显示多个标记
2021-03-29 22:40:32
Thx 非常适合我:) 正是我几个小时以来一直在搜索的内容:p
2021-04-07 22:40:32

扩展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;
    }
};
@Vae:半径是您希望圆的大小。您希望圆圈以像素为单位环视,因此您需要知道,在您所在的位置,1 度纬度与 1 度经度的比例是多少(以地图上的像素为单位)。你可以在一些网站上找到这个,或者只是尝试直到你得到正确的数字。角度是第一个引脚从顶部向右的角度。
2021-03-19 22:40:32
WebMW:从 XML 中提取标记后,您必须首先将它们分组到列表中,以便每个列表都包含指向完全相同位置的标记。然后,在包含多个元素的每个列表上,运行correctLocList()。只有在你这样做之后,才能创建 google.maps.Marker 对象。
2021-03-26 22:40:32
DzinX,我花了 4 个小时试图弄清楚如何将您的代码实现到我的代码中,但无济于事。我得出的结论是我不擅长这个,非常感谢你的帮助。这是我尝试插入代码的地方:pastebin.com/5qYbvybm——任何帮助将不胜感激!谢谢!
2021-04-04 22:40:32
它不起作用......它只是改变了我的经纬度 37.68625400000000000,-75.7166980000000000037.686254,-75.716698这对于所有值都是相同的......期待像......37.68625400000000000,-75.71669800000000000......等......37.6862540000001234,-75.7166980000001234 37.6862540000005678,-75.7166980000005678我也尝试改变我的角度。
2021-04-04 22:40:32
嗯,让我们深入挖掘一下,希望@DzinX 仍在该地区。你能(或其他任何人)解释你是如何获得所有这些数字的吗?Lng_radius 等?多谢。:)
2021-04-10 22:40:32

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

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

    // 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. 然后,在您初始化地图并声明您的 MarkerClusterer 对象的 initialize() 函数中:

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

    multiChoice() 是您的(尚未编写)函数,用于弹出一个带有可供选择的选项列表的信息窗口。请注意,markerClusterer 对象会传递给您的函数,因为您将需要它来确定该集群中有多少个标记。例如:

    function multiChoice(clickedCluster) {
      if (clickedCluster.getMarkers().length > 1)
      {
        // var markers = clickedCluster.getMarkers();
        // do something creative!
        return false;
      }
      return true;
    };
    
谢谢,这完美地工作!至于你的maxZoom问题,我认为这只是一个问题,如果没有设置 maxZoom 或者集群的 maxZoom 大于地图的缩放。我已经用这个片段替换了你的硬编码,它似乎工作得很好:var maxZoom = mc.getMaxZoom(); if (null === maxZoom || maxZoom > mc.getMap().maxZoom) { maxZoom = mc.getMap().maxZoom; if (null === maxZoom) { maxZoom = 15; } }
2021-03-21 22:40:32
@Pascal 加上一点点混淆,我们最终得到了这个,这可以工作,但正如 python 的 tao 所说。“可读性很重要”。var maxZoom = Math.min(mc.getMaxZoom() || 15, mc.getMap().maxZoom || 15);
2021-03-21 22:40:32
@user756659 在 multiChoice 中,您可以通过以下方式获得 lat/lng:clickedCluster.center_.lat()/clickedCluster.center_.lng()
2021-03-28 22:40:32
嗨@nathancolgate 你能帮我推荐最新版本的更新吗?我知道这篇文章很旧,但我完全被困住了,正在寻找在同一地点/建筑物上显示多个标记的方法。这将非常有帮助。提前致谢
2021-03-29 22:40:32
我知道这真的很旧,但我正在尝试使用此解决方案。我有它的工作,但是在显示带有集群数据的信息窗口的情况下......我如何获得首先单击的集群的标记位置以便我可以显示信息窗口?标记是我的数据数组...但是如何在群集图标/标记上显示信息窗口?
2021-04-02 22:40:32