咬住了这个。我正在通过JSON检索地理坐标列表并将它们弹出到谷歌地图上。除了在完全相同的位置上有两个或更多标记的情况下,一切都运行良好。 API仅显示1个标记 - 顶部标记。我觉得这很公平,但我想找到一种方法以某种方式显示它们。
我搜索过google并找到了一些解决方案,但它们似乎主要用于API的V2或者不是那么好。理想情况下,我想要一个解决方案,您可以点击某种组标记,然后显示聚集在它们所在位置周围的标记。
任何人都有这个问题或者类似的问题并愿意分享解决方案吗?
答案 0 :(得分:110)
看看OverlappingMarkerSpiderfier 有一个演示页面,但它们不会显示完全位于同一位置的标记,只有一些非常接近。
但是在http://www.ejw.de/ejw-vor-ort/上可以看到在完全相同的位置上有标记的真实例子(向下滚动地图并点击几个标记以查看蜘蛛效果)。
这似乎是您问题的完美解决方案。
答案 1 :(得分:32)
如果标记位于同一建筑物中,则偏移标记不是真正的解决方案。您可能想要做的是修改markerclusterer.js,如下所示:
在MarkerClusterer类中添加原型click方法,就像这样 - 我们稍后会在map 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()是您的(尚未编写)函数,用于弹出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;
}
答案 2 :(得分:15)
我和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);
答案 3 :(得分:14)
扩展Chaoley's answer,我实现了一个函数,给定一个位置列表(具有lng
和lat
属性的对象),其坐标完全相同,将它们从它们移开原始位置一点点(修改对象)。然后他们围绕中心点形成一个漂亮的圆圈。
我发现,对于我的纬度(北纬52度),0.0003度的圆半径效果最好,并且在转换为千米时必须弥补纬度和经度之间的差异。您可以找到纬度here的近似转换。
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;
}
};
答案 4 :(得分:9)
@Ignatius最优秀的答案,更新后与MarkerClustererPlus的v2.0.7一起使用。
在MarkerClusterer类中添加原型click方法,就像这样 - 我们稍后会在map 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()是您的(尚未编写)函数,用于弹出InfoWindow,其中包含可供选择的选项列表。请注意,markerClusterer对象将传递给您的函数,因为您需要此对象来确定该集群中有多少个标记。例如:
function multiChoice(clickedCluster) {
if (clickedCluster.getMarkers().length > 1)
{
// var markers = clickedCluster.getMarkers();
// do something creative!
return false;
}
return true;
};
答案 5 :(得分:4)
上面的答案更优雅,但我找到了一种快速而肮脏的方式,实际上真的非常好用。您可以在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下有+偏移量。从上面的2个变量。我不得不将随机除以0,1000乘以10000000,以获得一个随机小的十进制小数,几乎不会移动标记。您可以随意修改该变量,以获得更符合您需求的变量。
答案 6 :(得分:3)
对于同一建筑物中有多个服务的情况,您可以在距实际点的半径范围内稍微偏移标记(例如.001度)。这也应该产生一个很好的视觉效果。
答案 7 :(得分:2)
查看Marker Clusterer for V3 - 此库将附近的点聚类为组标记。单击群集时,地图会放大。我想当你在放大的时候,你仍然会在同一个地方出现同样的标记问题。
答案 8 :(得分: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
答案 9 :(得分:1)
我喜欢简单的解决方案,所以这里是我的。 而不是修改lib,这将使得难以保持。你可以简单地观看这样的活动
google.maps.event.addListener(mc, "clusterclick", onClusterClick);
然后你可以在
上进行管理function onClusterClick(cluster){
var ms = cluster.getMarkers();
i,即使用bootstrap显示带有列表的面板。我发现它比蜘蛛网更加舒适和可用,拥挤的#34;地方。 (如果你使用的是聚类器,那么一旦你发生了蜘蛛侠,你最终会遇到碰撞)。 你也可以在那里查看缩放。
顺便说一句。我刚刚发现传单,它似乎工作得更好,群集和蜘蛛网非常流畅http://leaflet.github.io/Leaflet.markercluster/example/marker-clustering-realworld.10000.html 它是开源的。
答案 10 :(得分:1)
这更像是一个快速而又肮脏的权宜之计。类似于马修福克斯建议的解决方案,这次是使用JavaScript。
在JavaScript中,您可以通过向添加一个小的随机偏移来偏移所有位置的纬度和长度。
myLocation[i].Latitude+ = (Math.random() / 25000)
(我发现除以 25000 可以提供足够的分离但不会将标记从确切位置(例如特定地址)显着移动
这可以很好地抵消彼此之间的差异,但只有在你紧密放大之后才能相互抵消。缩小时,仍然不清楚该位置有多个选项。
答案 11 :(得分:0)
扩展上面给出的答案,只需确保在初始化地图对象时设置maxZoom选项。
答案 12 :(得分:0)
请检查:https://github.com/plank/MarkerClusterer
当您在同一位置有多个标记时,这是MarkerCluster被修改为在群集标记中具有infoWindow。
您可以在此处查看其工作原理:http://culturedays.ca/en/2013-activities
答案 13 :(得分: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使用zIndex Swap将标记置于顶部。
答案 14 :(得分: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)
}
}
结果
答案 15 :(得分: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;
}
答案 16 :(得分:0)
除了上述答案外,还提供了php和wordpress中的替代快速解决方案。对于此示例,我将通过ACF存储location字段,并遍历帖子以获取该数据。
我发现将lat / lng存储在一个数组中,并检查该数组的值以查看循环是否匹配,然后我们可以用要移动点数的量更新该数组中的值。
//This is the value to shift the pips by. I found this worked best with markerClusterer
$add_to_latlng = 0.00003;
while ($query->have_posts()) {
$query->the_post();
$meta = get_post_meta(get_the_ID(), "postcode", true); //uses an acf field to store location
$lat = $meta["lat"];
$lng = $meta["lng"];
if(in_array($meta["lat"],$lat_checker)){ //check if this meta matches
//if matches then update the array to a new value (current value + shift value)
// This is using the lng value for a horizontal line of pips, use lat for vertical, or both for a diagonal
if(isset($latlng_storer[$meta["lng"]])){
$latlng_storer[$meta["lng"]] = $latlng_storer[$meta["lng"]] + $add_to_latlng;
$lng = $latlng_storer[$meta["lng"]];
} else {
$latlng_storer[$meta["lng"]] = $meta["lng"];
$lng = $latlng_storer[$meta["lng"]];
}
} else {
$lat_checker[] = $meta["lat"]; //just for simple checking of data
$latlng_storer[$meta["lat"]] = floatval($meta["lat"]);
$latlng_storer[$meta["lng"]] = floatval($meta["lng"]);
}
$entry[] = [
"lat" => $lat,
"lng" => $lng,
//...Add all the other post data here and use this array for the pips
];
} // end query
一旦我抓住了这些位置,我就会对$ entry变量进行json编码,并在我的JS中使用它。
let locations = <?=json_encode($entry)?>;
我知道这是一个相当具体的情况,但我希望这能对某人有所帮助!
答案 17 :(得分:0)
我使用了markerclustererplus,对我来说这是有效的:
//Code
google.maps.event.addListener(cMarkerClusterer, "clusterclick", function (c) {
var markers = c.getMarkers();
//Check Markers array for duplicate position and offset a little
if (markers .length > 1) {
//Check if all markers are in the same position (with 4 significant digits)
if (markers .every((val, index, arr) => (val.getPosition().lat().toFixed(4) == arr[0].getPosition().lat().toFixed(4)) && (val.getPosition().lng().toFixed(4) == arr[0].getPosition().lng().toFixed(4)))) { /
//Don't modify first element
for (i = 1; i < markers.length; i++) {
var existingMarker = markers[i];
var pos = existingMarker.getPosition();
var quot = 360.0 / markers.length;
var newLat = pos.lat() + -.00008 * Math.cos(+quot * i); //+ -.00008 * Math.cos((+quot * i) / 180 * Math.PI); //x
var newLng = pos.lng() + -.00008 * Math.sin(+quot * i); //+ -.0008 * Math.sin((+quot * i) / 180 * Math.PI); //Y
existingMarker.setPosition(new google.maps.LatLng(newLat, newLng));
}
let cZoom = map.getZoom();
map.setZoom(cZoom-1);
map.setZoom(cZoom+1);
}
}
});
答案 18 :(得分:-2)
扩展上面的答案,当你加入字符串,而不是添加/减去位置(例如&#34; 37.12340-0.00069&#34;)时,将你原来的纬度/经度转换成浮点数,例如:使用parseFloat(),然后添加或减去更正。