关于地图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
	}
}




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