当地图上点太多的时候你就会想到要用点的聚合,这个点改如何聚合呢?
聚合原理:在地图上画网格,然后有点落在这个网格里就标注,没有就不标注。如果有多个点落在这个网格,仍然只标注一个点。
网上介绍的大体上都有这样一个弊端:当你移动地图的时候,之前聚合的点有可能散开,多个散点有可能聚合。
那么怎样才能实现拖动地图的时候之前标注的点不变动呢?
解决办法:
网格一:在整个地图上画网格
网格二:在当前视窗上画网格
当然这两个网格的小格子边长要一样
想办法把网格一和网格二重叠,即网格二中的格子在网格一中一定有一个与之重叠的格子。然后在网格二上面marker即可
----------- 2016-02-16 --------
犹豫了好久,今天决定补上源码。
聚合从毕业实习到如今,从不会js到现在陆陆续续写了3个版本,三个版本代码逻辑都不一样,优化程度也不一样,它们见证了我js学习的提升,同时也有运行效率上的深度提升。
第一个版本,虽然说js是完全面向对象的,但是我写的时候完全没有面向对象的觉悟(因为还在蹒跚学步)
第二个版本,如果说第一个版本是因为工作需要,那么第二个版本完全是处于个人爱好了,因为我想要把聚合写成面向对象的,同时提高聚合的效率。
第三个版本,也就是现在这个版本(高德地图版本,前两版是PGIS版的),代码更加精简,逻辑更加清楚。
由于前两版本已经用于工作,故无法贴出源码,第三个版本是我前天写的,跟以前的版本逻辑完全不一样,因此奉上代码,请各位看官指正。
高德地图自带了聚合的API,我这里只是借用高德地图的平台写了自己的聚合。
精华部分请看marksCluster.js
【目录结构】
【marksCluster.html】
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta name="viewport" content="initial-scale=1.0, user-scalable=no" >
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>聚合与离散</title>
<style type="text/css">
html { height:100% }
body { height:100%; margin:0px; padding:0px }
#container { height:100% }
</style>
<script type="text/javascript"
src="http://webapi.amap.com/maps?v=1.3&key=<请填上你自己的高德key>" charset="utf-8">
</script>
<script type="text/javascript" src="js/jquery.js" charset="gb2312"></script>
<script type="text/javascript" src="js/marksCluster.js" charset="utf-8"></script>
<script type="text/javascript">
// 用来聚合的点
var allMarkArr = new Array();
// 地图对象
var mapObj = null;
$(document).ready(function (){
// 在id为"mapContainer"的div层上创建一个地图容器
mapObj = new AMap.Map("mapContainer"); // 创建地图实例
// 加载工具
loadTools( mapObj );
});
/**
* 加载工具
* @param mapObj 地图对象
*/
function loadTools( mapObj ) {
mapObj.plugin(["AMap.ToolBar","AMap.OverView,AMap.Scale"], function() {
// 加载工具条
var tool = new AMap.ToolBar({
direction: false, // 隐藏方向导航
ruler: false, // 隐藏视野级别控制尺
autoPosition: false // 禁止自动定位
});
mapObj.addControl(tool);
// 加载鹰眼
var view = new AMap.OverView();
mapObj.addControl(view);
// 加载比例尺
var scale = new AMap.Scale();
mapObj.addControl(scale);
});
}
/**
* 添加点
*/
function addMarkers() {
randomCreateMark(1000);
}
/**
* 清除所有点
*/
function clearMarkers() {
//mapObj.clearOverlays();
mapObj.clearMap();
allMarkArr.length = 0;
}
// 随机生成num个mark
function randomCreateMark(num, isCluster) {
var curMapBound = mapObj.getBounds(); // 当前显示边界
var latDet = curMapBound.northeast.lat - curMapBound.southwest.lat;
var lngDet = curMapBound.northeast.lng - curMapBound.southwest.lng;
for (var i = 0; i < num; i++) {
var lat = Math.random() * latDet + curMapBound.southwest.lat;
var lng = Math.random() * lngDet + curMapBound.southwest.lng;
var marker = new AMap.Marker({
id:"m"+i,
position: new AMap.LngLat(lng, lat),
offset: new AMap.Pixel(-8,-34),
icon: "http://api.amap.com/webapi/static/Images/0.png"
});// 自定义构造AMap.Marker对象
// mapObj.addOverlays(marker); //加载覆盖物
marker.setMap(mapObj);
allMarkArr.push(marker);
}
}
/**
* 开启聚合
*/
function clusterMarkersOn() {
// mapObj.clearOverlays();
mapObj.clearMap();
var cluster = mapObj.$MarkersCluster();
cluster.initMarkers(allMarkArr);
cluster.freshCluster();
}
</script>
</head>
<body>
<div id="header">
<span id="title">Marks Cluster Example</span>
<span id="shortdesc">
本示例旨在聚合地图中的标注信息.
</span></div>
<table style="margin-left:100px;margin-top:10px;">
<tr id="contenttable">
<td id="mapCon" style="width:855px;">
<div id="mapContainer" style="width:500px; height:500px;">
</div>
</td>
</tr>
<tr>
<td id="" style="width:855px;">
<input type="button" value="清除所有Markers" οnclick="clearMarkers();"/>
</td>
</tr>
<tr>
<td id="" style="width:855px;">
<input type="button" value="随机添加Markers" οnclick="addMarkers();"/>
</td>
</tr>
<tr>
<td id="" style="width:855px;">
<input type="button" value="聚合Markers" οnclick="clusterMarkersOn();"/>
</td>
</tr>
</table>
</body>
</html>
【marksCluster.js】
/**
* 聚合(把聚合的方法加载到地图对象上)
*/
AMap.Map.prototype.$MarkersCluster = function() {
// ========= 私有属性 ================== //
/**
* 需要聚合的所有点
*/
var marks = null
/**
* 地图当前视窗网格
*/
var curMarksGrid = { length : 0 };
/**
* 网格步长(单位像素)
*/
var gridStepPx = 40;
/**
* 网格步长(单位经度)
*/
var gridStepLng = 0.1;
/**
* 网格步长(单位纬度)
*/
var gridStepLat = 0.1;
// ========== 公有属性 ================== //
/**
* 地图对象
*/
var map = this;
// ========= 私有方法 ================== //
/**
* 刷新步长
*/
function freshStep() {
var bounds = map.getBounds(); // 当前视窗
var size = map.getSize();
gridStepLng = Math.abs( bounds.northeast.lng - bounds.southwest.lng ) / Math.ceil( size.width / gridStepPx ); // 经度步长
gridStepLat = Math.abs( bounds.northeast.lat - bounds.southwest.lat ) / Math.ceil( size.width / gridStepPx ); // 纬度步长
window.console.log("刷新步长");
window.console.log("步长(px):"+gridStepPx);
window.console.log("步长(经度):"+gridStepLng);
window.console.log("步长(纬度):"+gridStepLat);
}
/**
* 画网格
*/
function drawGrid() {
var bounds = map.getBounds(); // 当前视窗
var posX = null;
var posY = null;
var pos = null;
var marker = null;
var position = null;
for(var i = 0; i < marks.length; i++) {
marker = marks[i];
position = marker.getPosition();
// 过滤非当前视窗的点
if(bounds.southwest.lng < position.lng
&& position.lng < bounds.northeast.lng
&& bounds.southwest.lat < position.lat
&& position.lat < bounds.northeast.lat) {
posX = Math.ceil( position.lng / gridStepLng );
posY = Math.ceil( position.lat / gridStepLat );
pos = posX + "_" + posY;
if(!curMarksGrid[ pos ]) {
curMarksGrid[ pos ] = new Array();
curMarksGrid.length += 1;
}
curMarksGrid[ pos ].push( marker );
}
}
}
/**
* 在地图上显示点
*/
function showMarker( marker ){
// map.addOverlays(marker); //加载覆盖物
marker.setMap(map);
}
/**
* 清除聚合的点
*/
function clearCluster() {
var clusterMarker = null;
for(var key in curMarksGrid) {
clusterMarker = curMarksGrid[key];
if(clusterMarker instanceof Array && clusterMarker[0] instanceof AMap.Marker) {
clusterMarker[0].setMap(null);
}
curMarksGrid[key] = undefined;
}
}
// ========== 公有方法 ================= //
/**
* 初始化需要聚合的点
*/
var initMarkers = function( allMarkArr ) {
marks = allMarkArr;
// 绑定事件
map.off("zoomend", freshCluster);
map.off("dragend", freshClusterWithoutStep);
map.off("resize", freshCluster);
map.on("zoomend", freshCluster);
map.on("dragend", freshClusterWithoutStep);
map.on("resize", freshCluster);
};
/**
* 聚合
*/
var cluster = function() {
var clusterMarker = null;
for(var key in curMarksGrid) {
clusterMarker = curMarksGrid[key];
if(clusterMarker instanceof Array && clusterMarker[0] instanceof AMap.Marker) {
showMarker( clusterMarker[0] );
}
}
};
var freshClusterWithStep = function() {
// 清除聚合的点
clearCluster();
curMarksGrid.length = 0;
// 刷新步长后在画网格
freshStep();
// 画网格
drawGrid();
cluster();
}
/**
* 重新聚合
*/
var freshCluster = function() {
freshClusterWithStep();
}
/**
* 重新聚合
*/
var freshClusterWithoutStep = function() {
// 清除聚合的点
clearCluster();
curMarksGrid.length = 0;
// 画网格
drawGrid();
cluster();
}
// 返回公有方法
return {
map : map,
initMarkers : initMarkers,
freshCluster : freshCluster
}
}