Google Maps API V3-多个标记完全相同


115

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

我已经搜索过google并找到了一些解决方案,但是它们似乎主要用于API的V2或不是那么好。理想情况下,我希望找到一种解决方案,在其中单击某种组标记,然后显示聚集在所有标记周围的标记。

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

Answers:


116

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

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

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


这很棒,可以完全满足群集库中剩下的需求,该库不能处理具有相同精确纬度/经度的标记。
贾斯汀

如果OverlappingMarkerSpiderfierMarkerClusterer结合使用,一个不错的选择是maxZoom在集群构造函数上设置该选项。在指定的缩放级别之后,这会“取消群集”标记。
Diederik 2015年

@Tad我不知道您是否是该网站的开发人员,但我想告诉您ejw.de上的地图已损坏。我收到JS错误。
亚历克斯

我检查了建议的演示,但似乎已死。经过进一步的研究,我找到了有关如何整合标记簇和Spiderifier的示例。没有现场演示,只是克隆并遵循自述文件。 github.com/yagoferrer/markerclusterer-plus-spiderfier-example
Sax

34

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

  1. 在MarkerClusterer类中添加原型click方法,如下所示-我们稍后将在地图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. 然后,在您的initialize()函数中,您将在其中初始化地图并声明您的MarkerClusterer对象:

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

17

我将它与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);

Thx非常适合我:)正是我几个小时以来一直在搜索的内容:p
Jicao

美丽而优雅的解决方案。谢谢!
Concept211

完美的解决方案可以抵消一点点!有可能,我们将鼠标悬停在标记上,然后显示多个标记
Intekhab Khan

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

1
DzinX,我花了最后4个小时来弄清楚如何将您的代码实施到我的代码中而无济于事。我得出的结论是,我对此很满意,非常感谢您的帮助。这是我尝试插入代码的位置:pastebin.com/5qYbvybm-任何帮助将不胜感激!谢谢!
WebMW

WebMW:从XML提取标记后,必须首先将它们分组为列表,以便每个列表都包含指向完全相同位置的标记。然后,在包含多个元素的每个列表上,运行correctLocList()。只有这样做之后,才能创建google.maps.Marker对象。
DzinX 2013年

它不起作用...。它只是将我的经度从更改为 37.68625400000000000,-75.7166980000000000037.686254,-75.716698这对于所有值都相同... M期望像... 37.68625400000000000,-75.7166980000000000037.6862540000001234,-75.7166980000001234 37.6862540000005678,-75.7166980000005678..etc ..我也尝试更改角度。
2013年

Hummm,让我们进行一点挖掘,希望@DzinX仍在该区域中。您可以交配(或其他任何人)解释如何获得所有这些数字吗?Lng_radius等?多谢。:)
Vae

1
@Vae:半径是您希望圆的大小。您希望圆以像素为单位看起来是圆的,因此您需要知道在您的位置中1度和1度之间的比例(在地图上以像素为单位)。您可以在某些网站上找到它,也可以尝试直到获得正确的数字。角度是第一个针脚从顶部到右侧的数量。
DzinX

11

@Ignatius最佳答案,已更新为可与MarkerClustererPlus v2.0.7一起使用。

  1. 在MarkerClusterer类中添加原型click方法,如下所示-我们稍后将在地图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. 然后,在您的initialize()函数中,您将在其中初始化地图并声明您的MarkerClusterer对象:

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

1
谢谢,这很完美!至于您的maxZoom问题,我认为只有在未设置maxZoom或群集的maxZoom大于地图的缩放比例的情况下,这才是问题。我已用此代码段替换了您的硬编码,它似乎运行良好:var maxZoom = mc.getMaxZoom(); if (null === maxZoom || maxZoom > mc.getMap().maxZoom) { maxZoom = mc.getMap().maxZoom; if (null === maxZoom) { maxZoom = 15; } }
Pascal

我知道这确实很老,但是我正在尝试使用此解决方案。我可以使用它,但是在显示带有集群数据的信息窗口的情况下...如何获得首先单击的集群的标记位置,以便显示信息窗口?标记是我的数据数组...但是如何在集群图标/标记上显示信息窗口?
756659

在multiChoice中,@ user756659选择通过以下方式可获得经纬度: clickedCluster.center_.lat() /clickedCluster.center_.lng()
Jens Kohl

1
@Pascal并稍加混淆,我们最终得到了结果,它可以工作,但是正如python的tao所说。“可读性很重要”。var maxZoom = Math.min(mc.getMaxZoom()|| 15,mc.getMap()。maxZoom || 15);
南德

6

上面的答案更加优雅,但是我发现一种快速而肮脏的方式实际上确实非常好用。您可以在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);

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


凉!这是我发现非常有用的肮脏技巧,无需弄乱Google Maps api代码
CuongDC

3

对于同一建筑物中有多个服务的情况,您可以将标记与实际点的半径稍微偏移一点(例如,0.001度)。这也应产生良好的视觉效果。


3

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

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

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

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

这样可以很好地抵消彼此之间的偏移,但前提是您必须仔细放大。缩小时,仍然不清楚该位置有多个选项。


1
我喜欢这个。如果您不需要准确的代表性标记,请将25000降低到250或25。简单快速的解决方案。
Fid

2

退房标记聚类器的V3 -景点这个库簇成团标记。单击群集时,地图会放大。我可以想象一下,当放大时,在同一位置上的标记仍然会遇到相同的问题。


是的,看了一下那个方便的小图书馆,但它似乎仍然无法克服这个问题。我正在为一家公司提供服务定位器,该公司有时在同一办公区域中有多个服务,因此地理坐标也完全相同。我可以在一个信息窗口中显示不同的服务,但这似乎并不是最优雅的解决方案……
Louzoid 2010年

我刚刚发现,@ Louzoid Marker Clusterer无法解决该问题。我遇到了同样的问题,我在一栋建筑物内有几家公司,因此只显示1个标记。
罗布

1

更新为可与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

这种方法要求用户单击群集图标,并且不会涵盖使用缩放滑块或鼠标滚动进行导航的用例。任何想法如何解决这些问题?
Remi Sture 2014年

1

我喜欢简单的解决方案,所以这是我的。而不是修改lib,这将使它更难维护。您可以像这样简单地观看事件

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

那你就可以管理

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

i,即使用引导程序显示带有列表的面板。与在“拥挤”的地方进行蜘蛛侠攻击相比,我发现它更加舒适和实用。(如果您使用的是群集器,那么一旦爬网,最终将导致碰撞)。您也可以在那里查看缩放。

顺便说一句 我刚刚发现了传单,它似乎工作得更好,集群和Spiderfy的运作非常流畅http://leaflet.github.io/Leaflet.markercluster/example/marker-clustering-realworld.10000.html ,它是开源的。



0

扩展上面给出的答案,只需确保在初始化地图对象时设置maxZoom选项即可。


0

当用户放大到最大时,赋予偏移量会使标记变远。因此,我找到了一种方法来实现这一目标。这可能不是正确的方法,但效果很好。

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

设置标记的zIndex,以便您将在顶部看到要显示的标记图标,否则将为标记设置动画,例如自动交换。当用户点击标记柄时,使用zIndex Swap将zIndex置于顶部。


0

如何摆脱它.. [迅速]

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

即将创建一个新的标记?检查clusterArray并处理它的偏移量

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


    }

结果

在此处输入图片说明


0

除了Matthew Fox狡猾的天才答案外,我在设置标记对象时为每个经纬度添加了一个小的随机偏移量。例如:

new LatLng(getLat()+getMarkerOffset(), getLng()+getMarkerOffset()),

private static double getMarkerOffset(){
    //add tiny random offset to keep markers from dropping on top of themselves
    double offset =Math.random()/4000;
    boolean isEven = ((int)(offset *400000)) %2 ==0;
    if (isEven) return  offset;
    else        return -offset;
}

-2

扩展上面的答案,当您加入字符串而不是加减位置(例如“ 37.12340-0.00069”)时,请将原始纬度/经度转换为浮点数,例如使用parseFloat(),然后添加或减去更正。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.