當地圖上點太多的時候你就會想到要用點的聚合,這個點改如何聚合呢?
聚合原理:在地圖上畫網格,然後有點落在這個網格里就標註,沒有就不標註。如果有多個點落在這個網格,仍然只標註一個點。
網上介紹的大體上都有這樣一個弊端:當你移動地圖的時候,之前聚合的點有可能散開,多個散點有可能聚合。
那麼怎樣才能實現拖動地圖的時候之前標註的點不變動呢?
解決辦法:
網格一:在整個地圖上畫網格
網格二:在當前視窗上畫網格
當然這兩個網格的小格子邊長要一樣
想辦法把網格一和網格二重疊,即網格二中的格子在網格一中一定有一個與之重疊的格子。然後在網格二上面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
}
}