Answers:
看看OverlappingMarkerSpiderfier。
有一个演示页面,但它们没有显示完全位于同一位置的标记,而仅显示非常靠近的标记。
但是可以在http://www.ejw.de/ejw-vor-ort/上看到一个标记完全相同的真实示例(向下滚动查看地图,然后单击一些标记以查看蜘蛛效果)。
这似乎是解决您问题的完美解决方案。
OverlappingMarkerSpiderfier
与MarkerClusterer结合使用,一个不错的选择是maxZoom
在集群构造函数上设置该选项。在指定的缩放级别之后,这会“取消群集”标记。
如果标记位于同一建筑物中,则不是真正的解决方案。您可能想要做的就是像这样修改markerclusterer.js:
在MarkerClusterer类中添加原型click方法,如下所示-我们稍后将在地图initialize()函数中重写此方法:
MarkerClusterer.prototype.onClick = function() {
return true;
};
在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);
}
然后,在您的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;
}
我将它与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);
扩展对Chaoley的答案,我实现了一个函数,该函数给出了坐标完全相同的位置(带有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;
}
};
37.68625400000000000,-75.71669800000000000
, 37.686254,-75.716698
这对于所有值都相同... M期望像... 37.68625400000000000,-75.71669800000000000
到 37.6862540000001234,-75.7166980000001234
37.6862540000005678,-75.7166980000005678
..etc ..我也尝试更改角度。
@Ignatius最佳答案,已更新为可与MarkerClustererPlus v2.0.7一起使用。
在MarkerClusterer类中添加原型click方法,如下所示-我们稍后将在地图initialize()函数中重写此方法:
// BEGIN MODIFICATION (around line 715)
MarkerClusterer.prototype.onClick = function() {
return true;
};
// END MODIFICATION
在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
然后,在您的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;
};
maxZoom
问题,我认为只有在未设置maxZoom或群集的maxZoom大于地图的缩放比例的情况下,这才是问题。我已用此代码段替换了您的硬编码,它似乎运行良好:var maxZoom = mc.getMaxZoom(); if (null === maxZoom || maxZoom > mc.getMap().maxZoom) { maxZoom = mc.getMap().maxZoom; if (null === maxZoom) { maxZoom = 15; } }
clickedCluster.center_.lat()
/clickedCluster.center_.lng()
上面的答案更加优雅,但是我发现一种快速而肮脏的方式实际上确实非常好用。您可以在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,才能得到一个十进制的小数,它足够小,几乎不能移动标记。随意修改该变量,以获得更精确地满足您的需求的变量。
这更多是权宜之计的“快速而肮脏”的解决方案,类似于Matthew Fox建议的解决方案,这次是使用JavaScript。
在JavaScript中,您可以通过向两个位置都添加一个小的随机偏移量来偏移所有位置的经纬度
myLocation[i].Latitude+ = (Math.random() / 25000)
(我发现除以25000可以得到足够的分隔,但不会使标记从确切位置(例如特定地址)显着移动)
这样可以很好地抵消彼此之间的偏移,但前提是您必须仔细放大。缩小时,仍然不清楚该位置有多个选项。
退房标记聚类器的V3 -景点这个库簇成团标记。单击群集时,地图会放大。我可以想象一下,当放大时,在同一位置上的标记仍然会遇到相同的问题。
更新为可与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
我喜欢简单的解决方案,所以这是我的。而不是修改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 ,它是开源的。
检查一下:https : //github.com/plank/MarkerClusterer
当您在同一位置有多个标记时,这是对MarkerCluster的修改,在群集标记中具有一个infoWindow。
您可以在此处查看其工作方式:http://culturedays.ca/en/2013-activities
当用户放大到最大时,赋予偏移量会使标记变远。因此,我找到了一种方法来实现这一目标。这可能不是正确的方法,但效果很好。
// 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置于顶部。
如何摆脱它.. [迅速]
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)
}
}
结果
除了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;
}