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

位卡在这一个。 我正在通过JSON检索地理坐标列表,并将其popup到谷歌地图上。 除了在同一地点有两个或多个标记的情况之外,所有的工作都很好。 API只显示1个标记 – 最上面的一个。 我想这是公平的,但想find一种方法来显示它们。

我search谷歌,并find了一些解决scheme,但他们似乎主要是V2的API或只是不那么好。 理想情况下,我想要一个解决scheme,你点击某种组标记,然后显示聚集在他们所在地点的标记。

任何人有这个问题或类似的问题,并会分享解决scheme吗?

看看OverlappingMarkerSpiderfier 。
有一个演示页面,但它们不显示完全在同一个点上的标记,只有一些非常接近。

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

这似乎是你的问题的完美解决scheme。

如果标记位于同一build筑物内,则标记不是真正的解决scheme。 你可能想要做的是像这样修改markerclusterer.js:

  1. 在MarkerClusterer类中添加一个原型点击方法,如下所示 – 稍后我们将在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. 然后,在初始化map的initialize()函数中声明MarkerClusterer对象:

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

    其中multiChoice()是您的(尚未被写入)函数来popup信息窗口与select列表的选项。 请注意,markerClusterer对象被传递给你的函数,因为你需要这个来确定这个集群有多less个标记。 例如:

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

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

我发现,对于我的纬度(北纬52度)来说,0.0003度的圆半径效果最好,你必须弥补纬度和经度转换成千米之差。 你可以在这里find你的纬度的近似转换。

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

@Ignatius最出色的答案,更新与MarkerClustererPlus v2.0.7一起工作。

  1. 在MarkerClusterer类中添加一个原型点击方法,如下所示 – 稍后我们将在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. 然后,在初始化map的initialize()函数中声明MarkerClusterer对象:

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

    其中multiChoice()是您的(尚未被写入)函数来popup信息窗口与select列表的选项。 请注意,markerClusterer对象被传递给你的函数,因为你需要这个来确定这个集群有多less个标记。 例如:

     function multiChoice(clickedCluster) { if (clickedCluster.getMarkers().length > 1) { // var markers = clickedCluster.getMarkers(); // do something creative! 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); 

对于同一build筑物中有多个服务的情况,您可以在实际点的半径内稍微偏移标记(比如.001度)。 这也应该产生一个很好的视觉效果。

上面的答案更加优雅,但我发现了一个真正非常好的实际工作的快速和肮脏的方式。 你可以在www.buildinglit.com看到它的行动

我所做的只是为我的genxml.php页面添加一个随机偏移量,以便每次使用偏移量创build地图时,每次都会返回略有不同的结果。 这听起来像是一个黑客,但实际上你只需要标记在随机的方向轻轻移动一下,如果它们重叠的话就可以在地图上点击。 它实际上工作得很好,我会说比蜘蛛法更好,因为谁想要处理这种复杂性,并让它们到处都是spring。 你只是想能够select标记。 随机推动它完美。

这是php_genxml.php中创buildwhile语句迭代节点的一个例子

 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个variables。 为了得到一个十进制的小数点,我必须随机地用1000000000除以这个十进制小数,这个小数点是随机小到只是几乎不移动标记。 随意修改该variables,以获得一个更精确的您的需求。

查看V3的Marker Clusterer – 这个库将附近的点聚类成组标记。 地图放大时点击群集。 我想象一下,当你放大的时候,在同一个点上的标记仍然会有同样的问题。

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

检查这个: https : //github.com/plank/MarkerClusterer

这是MarkerCluster被修改为在簇标记中有一个infoWindow,当你有几个标记在同一个位置。

你可以看看它是如何工作的: http : //culturedays.ca/en/2013-activities

扩展上面的答案,只要确保在初始化地图对象时设置了maxZoom选项。

当用户放大到最大值时,偏移将使标记变得更远。 所以我find了一个方法来实现这一点。 这可能不是一个正确的方法,但它工作得很好。

 // 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,以便您可以看到您想要在顶部看到的标记图标,否则会为自动交换等标记添加animation。 当用户点击标记手柄时,zIndex使用zIndex Swap将标记置于顶部。

我喜欢简单的解决scheme,所以这是我的。 而不是修改lib,这将使得它更难以维持。 你可以简单地观看这样的事件

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

那么你可以pipe理它

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

我,即,使用引导来显示一个列表面板。 在“拥挤”的地方,我发现比spiderfying更加舒适和可用。 (如果你正在使用一个聚类器,那么一旦你发现了蜘蛛,你将会碰到碰撞)。 你也可以在那里查看缩放。

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

这更像是马修·福克斯(Matthew Fox)build议的类似于“快速和肮脏”的解决scheme,这次使用的是JavaScript。

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

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

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

这使得他们彼此抵消,但只有在你紧密放大之后才能做得相当好。 缩小时,仍然不清楚该位置有多个选项。

上面扩展的答案,当你joinstring,而不是加/减位置(例如“37.12340-0.00069”),将原始经纬度转换为浮点数,例如使用parseFloat(),然后增加或减less更正。

一个真正的IT专业人员不会操纵这样的数据。

我已经看到了带有组标记的地图,单击组标记打开一个信息窗口,其中包含此地理编码的成员列表,每个信息窗口都可以点击到该特定成员的新信息窗口。

我所掌握的最好的例子是在Google方面处理数据,而不是他们自己的数据。