關於地圖marker的聚合問題

當地圖上點太多的時候你就會想到要用點的聚合,這個點改如何聚合呢?

聚合原理:在地圖上畫網格,然後有點落在這個網格里就標註,沒有就不標註。如果有多個點落在這個網格,仍然只標註一個點。

 

網上介紹的大體上都有這樣一個弊端:當你移動地圖的時候,之前聚合的點有可能散開,多個散點有可能聚合。

 

那麼怎樣才能實現拖動地圖的時候之前標註的點不變動呢?

 

解決辦法:

       網格一:在整個地圖上畫網格

       網格二:在當前視窗上畫網格

當然這兩個網格的小格子邊長要一樣

       想辦法把網格一和網格二重疊,即網格二中的格子在網格一中一定有一個與之重疊的格子。然後在網格二上面marker即可


----------- 2016-02-16 --------

猶豫了好久,今天決定補上源碼。

聚合從畢業實習到如今,從不會js到現在陸陸續續寫了3個版本,三個版本代碼邏輯都不一樣,優化程度也不一樣,它們見證了我js學習的提升,同時也有運行效率上的深度提升。

第一個版本,雖然說js是完全面向對象的,但是我寫的時候完全沒有面向對象的覺悟(因爲還在蹣跚學步)

第二個版本,如果說第一個版本是因爲工作需要,那麼第二個版本完全是處於個人愛好了,因爲我想要把聚合寫成面向對象的,同時提高聚合的效率。

第三個版本,也就是現在這個版本(高德地圖版本,前兩版是PGIS版的),代碼更加精簡,邏輯更加清楚。

由於前兩版本已經用於工作,故無法貼出源碼,第三個版本是我前天寫的,跟以前的版本邏輯完全不一樣,因此奉上代碼,請各位看官指正。

高德地圖自帶了聚合的API,我這裏只是借用高德地圖的平臺寫了自己的聚合。

精華部分請看marksCluster.js

【目錄結構】

marksCluster.html
└ ─ js
      └ ─ marksCluster.js
      └ ─ jquery.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
	}
}




發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章