欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

基于百度地图的数据可视化,包括大量数据的标绘以及热力图的插入

程序员文章站 2022-06-18 19:16:51
在日常的开发工作中经常可以遇到地图的相关应用,但是大部分的问题我们都可以通过查询相关的地图api接口就可以得到我们想要的结果,无非会在数据处理以及一些基础的函数的问题,但是如果想在地图上实现一些大量数据的标绘制的时候,问题就出现了。经过测试,百度地图(本文中的地图均指百度地图)在实现基于图片的标绘物品的时候,在200个点属于完美性能(PC,移动端未测试)一旦超过2000个标绘物的时候就会出现明显的卡顿,而超过5000的时候则会出现浏览器直接奔溃的情况。经过多方查找且改进,我找到了基于百度地图canvas的额...

在日常的开发工作中经常可以遇到地图的相关应用,但是大部分的问题我们都可以通过查询相关的地图api接口就可以得到我们想要的结果,无非会在数据处理以及一些基础的函数的问题,但是如果想在地图上实现一些大量数据的标绘制的时候,问题就出现了。经过测试,百度地图(本文中的地图均指百度地图)在实现基于图片的标绘物品的时候,在200个点属于完美性能(PC,移动端未测试)一旦超过2000个标绘物的时候就会出现明显的卡顿,而超过5000的时候则会出现浏览器直接奔溃的情况。经过多方查找且改进,我找到了基于百度地图canvas的额外图层绘制。经过测试,可以实现10000个点基本完美性能。但是再往上我也不太会了,因此希望诸位大佬给出改进意见。

html

<!DOCTYPE html>
<html>

<head>
    <title>baidu map pointline</title>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, user-scalable=no">
    <link rel="stylesheet" href="./style.css">
	<!-- <link rel="stylesheet" type="text/css" href="../../css/map.css" />
	<script type="text/javascript" src="../../jquery.js"></script>
	<script type="text/javascript" src="../../map_load.js"></script>
	<script type="text/javascript" src="../../map.js"></script>
	<script src="../../layer/layer.js" type="text/javascript" charset="utf-8"></script> -->
	 <script type="text/javascript" src="https://api.map.baidu.com/api?v=2.0&ak=nuWah68S1WieW2AEwiT8T3Ro&s=1"></script>
    <!-- <script type="text/javascript" src="mapstyle/gray.js"></script> -->
    <script type="text/javascript" src="js/jquery.min.js"></script>
	<script src="./data/point.js" type="text/javascript" charset="utf-8"></script>
</head>

<body>
    <div id="map"></div>
	<img src="images/red.png" >
    <script type="text/javascript" src="./baidu-map-pointLine.js"></script>
    <script type="text/javascript">
        var map = new BMap.Map('map', {
            minZoom: 5
        });
        map.centerAndZoom(new BMap.Point(112.954699, 27.936651192), 7);
        map.enableScrollWheelZoom(true);
		var arr=[]
		for(var i=0;i<10000;i++){
			var obj={
				lng:Math.random()*10+100,
				lat:Math.random()*10+30,
				message:"标绘物"+i
			}
			arr.push(obj)
			
		}
            var pointLine = new PointLine(map, {
                //数据源
                data: arr,
                //事件
                methods: {
                    click: function (e, msg) {
                        console.log('你当前点击的是' + msg);
                    },
                    // mousemove: function (e, name) {
                    //     console.log('你当前点击的是' + name);
                    // }
                }
            });
    </script>
</body>

js

在这儿需要感谢一位大哥完成了代码里最麻烦的部分,就是百度地图和canvas的经纬度坐标和canvas的屏幕坐标转换的部分,而他也完成了多条地铁线路的地图标会,而我在他的基础上加入了海量图片的标绘以及热力图的嵌入+点聚合的优化方法,这是那哥们的github地址:https://github.com/chengquan223

(function(global, factory) {
	typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
		typeof define === 'function' && define.amd ? define(factory) :
		(global.PointLine = factory());
}(this, (function() {
	'use strict';

	/**
	 * @author https://github.com/chengquan223
	 * @Date 2017-02-27
	 * */
	function CanvasLayer(options) {
		this.options = options || {};
		this.paneName = this.options.paneName || 'labelPane';
		this.zIndex = this.options.zIndex || 0;
		this._map = options.map;
		this._lastDrawTime = null;
		this.show();
	}

	CanvasLayer.prototype = new BMap.Overlay();

	CanvasLayer.prototype.initialize = function(map) {
		this._map = map;
		var canvas = this.canvas = document.createElement('canvas');
		var ctx = this.ctx = this.canvas.getContext('2d');
		canvas.style.cssText = 'position:absolute;' + 'left:0;' + 'top:0;' + 'z-index:' + this.zIndex + ';';
		this.adjustSize();
		this.adjustRatio(ctx);
		map.getPanes()[this.paneName].appendChild(canvas);
		var that = this;
		map.addEventListener('resize', function() {
			that.adjustSize();
			that._draw();
		});
		return this.canvas;
	};

	CanvasLayer.prototype.adjustSize = function() {
		var size = this._map.getSize();
		var canvas = this.canvas;
		canvas.width = size.width;
		canvas.height = size.height;
		canvas.style.width = canvas.width + 'px';
		canvas.style.height = canvas.height + 'px';
	};

	CanvasLayer.prototype.adjustRatio = function(ctx) {
		var backingStore = ctx.backingStorePixelRatio || ctx.webkitBackingStorePixelRatio || ctx.mozBackingStorePixelRatio ||
			ctx.msBackingStorePixelRatio || ctx.oBackingStorePixelRatio || ctx.backingStorePixelRatio || 1;
		var pixelRatio = (window.devicePixelRatio || 1) / backingStore;
		var canvasWidth = ctx.canvas.width;
		var canvasHeight = ctx.canvas.height;
		ctx.canvas.width = canvasWidth * pixelRatio;
		ctx.canvas.height = canvasHeight * pixelRatio;
		ctx.canvas.style.width = canvasWidth + 'px';
		ctx.canvas.style.height = canvasHeight + 'px';
		// console.log(ctx.canvas.height, canvasHeight);
		ctx.scale(pixelRatio, pixelRatio);
	};

	CanvasLayer.prototype.draw = function() {
		var self = this;
		var args = arguments;

		clearTimeout(self.timeoutID);
		self.timeoutID = setTimeout(function() {
			self._draw();
		}, 15);
	};

	CanvasLayer.prototype._draw = function() {
		var map = this._map;
		var size = map.getSize();
		var center = map.getCenter();
		if (center) {
			var pixel = map.pointToOverlayPixel(center);
			this.canvas.style.left = pixel.x - size.width / 2 + 'px';
			this.canvas.style.top = pixel.y - size.height / 2 + 'px';
			this.dispatchEvent('draw');
			this.options.update && this.options.update.call(this);
		}
	};

	CanvasLayer.prototype.getContainer = function() {
		return this.canvas;
	};

	CanvasLayer.prototype.show = function() {
		if (!this.canvas) {
			this._map.addOverlay(this);
		}
		this.canvas.style.display = 'block';
	};

	CanvasLayer.prototype.hide = function() {
		this.canvas.style.display = 'none';
		//this._map.removeOverlay(this);
	};

	CanvasLayer.prototype.setZIndex = function(zIndex) {
		this.canvas.style.zIndex = zIndex;
	};

	CanvasLayer.prototype.getZIndex = function() {
		return this.zIndex;
	};

	var tool = {
		merge: function merge(settings, defaults) {
			Object.keys(settings).forEach(function(key) {
				defaults[key] = settings[key];
			});
		},
		//计算两点间距离
		getDistance: function getDistance(p1, p2) {
			return Math.sqrt((p1[0] - p2[0]) * (p1[0] - p2[0]) + (p1[1] - p2[1]) * (p1[1] - p2[1]));
		},
		//判断点是否在线段上
		containStroke: function containStroke(x0, y0, x1, y1, lineWidth, x, y) {
			if (lineWidth === 0) {
				return false;
			}
			var _l = lineWidth;
			var _a = 0;
			var _b = x0;
			// Quick reject
			if (y > y0 + _l && y > y1 + _l || y < y0 - _l && y < y1 - _l || x > x0 + _l && x > x1 + _l || x < x0 - _l && x <
				x1 - _l) {
				return false;
			}

			if (x0 !== x1) {
				_a = (y0 - y1) / (x0 - x1);
				_b = (x0 * y1 - x1 * y0) / (x0 - x1);
			} else {
				return Math.abs(x - x0) <= _l / 2;
			}
			var tmp = _a * x - y + _b;
			var _s = tmp * tmp / (_a * _a + 1);
			return _s <= _l / 2 * _l / 2;
		},
		// 判断点击图片的位置
		containStrokeImage: function(clickPoint, markerPoint) {
			var markerWidth = 32,
				markerHeight = 32,
				self = this;
			var marker = self.isimageContent(markerPoint);
			var arr = [];
			if (marker.leftTopX <= clickPoint.x && clickPoint.x <= marker.rightBottomX && clickPoint.y < marker.rightBottomY &&
				marker.leftTopY < clickPoint.y) {
				return true;
			} else {
				return false;
			}
			// ctx.clearRech(0,0,ctx.width,ctx.height);
			/**
			 * 1.点击的位置
			 * 2.计算出四个边角的位置
			 * 3. 筛选出是否在四边之内
			 * 
			 * 
			 * 
			 * 
			 * **/
		},
		isimageContent: function(markerPoint) {
			return {
				leftTopX: markerPoint.x,
				leftTopY: markerPoint.y,
				rightBottomX: markerPoint.x + 32,
				rightBottomY: markerPoint.y + 32
			}

		},
		//点击的区域重合的时候
		selectZjFunc: function(markerarr, e) {
			var thatSelectjl = 0;
			var thatIndex = 0
			var self = this;
			markerarr.forEach((item, index) => {
				var markerpix = item.marker.pixel;
				var thisjl = self.selectjsFunc(markerpix, e)
				if (thisjl > thatSelectjl) {
					thatSelectjl = thisjl
					thatIndex = index
				}
			})
			return thatIndex;
		},
		// 计算点击的位置距离部署点位的距离
		selectjsFunc: function(marker, e) {
			return Math.sqrt((marker.x - e.x) * (marker.x - e.x) + (e.y - marker.y) * (e.y - marker.y))
		},
		//点聚合算法的实现1. 实现拆分屏幕
		//				2. 数据判断
		//				3. 实现绘制(仅限热力图,如果有后续需要做出标绘的点聚合,也可以)
		cfpmFunc: function(width, height, cfWidth) {
			var self = this;
			//横向
			var widthLength = parseInt(width / cfWidth) + 1;
			var widthOver = width % cfWidth
			var widthHeight = parseInt(height / cfWidth) + 1;
			var heightOver = height % cfWidth
			var pointarr = [];
			for (var i = 1; i <= widthLength; i++) {
				for (var j = 1; j <= widthHeight; j++) {
					var obj = {}
					obj.leftTopX = (i - 1) * cfWidth;
					obj.leftTopY = (j - 1) * cfWidth;
					obj.rightBottomX = (i - 1) * cfWidth;
					obj.rightBottomY = (j - 1) * cfWidth;
					if (i == widthLength) {

						if (j == widthHeight) {
							obj.rightBottomX = (i - 1) * cfWidth + widthOver;
							obj.rightBottomY = (j - 1) * cfWidth + heightOver;
						} else {
							obj.rightBottomX = (i - 1) * cfWidth + widthOver;
							obj.rightBottomY = j * cfWidth;
						}
					} else {
						obj.rightBottomX = i * cfWidth;
						obj.rightBottomY = (j - 1) * cfWidth + heightOver;
					}
					pointarr.push(obj)
				}
			}

			return pointarr;
		},
		// 当前方框内是否存在数据
		jhFunc: function(clickPoint, marker, width, height) {
			var self = this;
			if (marker.leftTopX <= clickPoint.x && clickPoint.x <= marker.rightBottomX && clickPoint.y < marker.rightBottomY &&
				marker.leftTopY < clickPoint.y) {
				return true;
			} else {
				return false;
			}
		},


	};
	//所有图片存储的位置
	var base64Image = {
		tzred:"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAYAAACM/rhtAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAA3NpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuNS1jMDE0IDc5LjE1MTQ4MSwgMjAxMy8wMy8xMy0xMjowOToxNSAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtcE1NOk9yaWdpbmFsRG9jdW1lbnRJRD0ieG1wLmRpZDozMmMyNjgwOS1mOTMwLTQyYTEtYjBjZi1kNjYyNjY3MjFhYzEiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6MDREODQ2QTAxMTgyMTFFNDk0QTg5MjZDRjBEOTA3RDkiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6MDREODQ2OUYxMTgyMTFFNDk0QTg5MjZDRjBEOTA3RDkiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENDIChNYWNpbnRvc2gpIj4gPHhtcE1NOkRlcml2ZWRGcm9tIHN0UmVmOmluc3RhbmNlSUQ9InhtcC5paWQ6NjY2N2Q2MDAtM2RkMS00ZWZhLTgyNTktZTA0MWUyYTkwOTZjIiBzdFJlZjpkb2N1bWVudElEPSJ4bXAuZGlkOjMyYzI2ODA5LWY5MzAtNDJhMS1iMGNmLWQ2NjI2NjcyMWFjMSIvPiA8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"
	}

	var PointLine = function PointLine(map, userOptions) {
		var self = this;

		self.map = map;
		self.lines = [];
		self.pixelList = [];

		//默认参数
		//需要的数据
		/**
		 * 经度,纬度,标题头 添加的点击事件 
		 * 
		 * **/
		var options = {

		};

		//全局变量
		var baseLayer = null,
			//获取当前所需要的canvas的宽高
			width = map.getSize().width,
			height = map.getSize().height;

		function Line(opts) {
			// this.message = opts.message;//装备信息
			// this.path = opts.path;//装备的位置
			this.tzArr = opts.tzArr;
		}
		//绘制军标
		Line.prototype.draw = function() {
			var self = this;
			var ctx = self.ctx;
			var img = new Image();
			//等待处理
			// img.setAttribute("crossOrigin","")
			img.onload = function() {
				for (let i = 0; i < self.pixelList.length; i++) {
					const item = self.pixelList[i]
					ctx.drawImage(img, item.pixel.x, item.pixel.y, 32, 32);
				}
				// 存储到这个的全局变量
			}
			img.src = base64Image.tzred;
		}
		//热力图绘制
		Line.prototype.drawHot = function() {
			var self = this;
			var ctx = self.ctx;

			var radius = 50;
			for (let i = 0; i < self.jhArr.length; i++) {
				
				const item = self.jhArr[i]
				if(!item){
					continue
				}
				ctx.beginPath()
				ctx.arc(item.pixel.x, item.pixel.y, radius, 0, 2 * Math.PI);
				ctx.closePath()
				let radialGradient = ctx.createRadialGradient(item.pixel.x, item.pixel.y, 0, item.pixel.x, item.pixel.y,
					radius);
				radialGradient.addColorStop(0.0, "rgba(0,0,0,1)")
				radialGradient.addColorStop(1.0, "rgba(0,0,0,0)")
				ctx.fillStyle = radialGradient;
				//权重对比算法,替换算法
				// let globalAlpha = (value - min) / (max - min);
				// ctx.globalAlpha = Math.max(Math.min(globalAlpha, 1), 0)
				ctx.fill()
			}

			//自定义调色板
			var paientCanvas = document.createElement('canvas')
			var paientctx = paientCanvas.getContext('2d');
			let gradentConfig = {
				'0.2': 'rgba(0,0,255,0.2)',
				'0.3': 'rgba(43,111,231,0.3)',
				'0.4': 'rgba(2,192,241,0.4)',
				'0.6': 'rgba(44,222,148,0.6)',
				'0.8': 'rgba(254,237,83,0.8)',
				'0.9': 'rgba(255,118,55,0.9)',
				'1.0': 'rgba(255,64,28,1)',
			}
			paientCanvas.width = 256;
			paientCanvas.height = 1;
			var gradient = paientctx.createLinearGradient(0, 0, 256, 1)
			for (var key in gradentConfig) {
				gradient.addColorStop(key, gradentConfig[key])
			}
			paientctx.fillStyle = gradient;
			paientctx.fillRect(0, 0, 256, 1)

			//获取到需要映射的imagedata
			var imgdata = paientctx.getImageData(0, 0, 256, 1).data;
			var img = ctx.getImageData(0, 0, ctx.canvas.width, ctx.canvas.height)
			var ctxImagedata = img.data
			for (var i = 3; i < ctxImagedata.length; i += 4) {
				var alpha = ctxImagedata[i]
				var offset = alpha * 4;
				if (!offset) {
					continue
				}
				ctxImagedata[i - 3] = imgdata[offset]
				ctxImagedata[i - 2] = imgdata[offset + 1]
				ctxImagedata[i - 1] = imgdata[offset + 2]
			}
			//卡顿,需要点聚合的算法进行相应处理
			ctx.putImageData(img, 0, 0, 0, 0, ctx.canvas.width, ctx.canvas.height)
		}

		//底层canvas添加
		var brush = function brush() {
			// 创建一个新的canvas
			var baseCtx = baseLayer.canvas.getContext('2d');
			if (!baseCtx) {
				return;
			}
			//添加军标

			addLine();
			baseCtx.clearRect(0, 0, width, height);
			//获取当前地图层级
			var zoom = map.getZoom();
			self.lines.pixelList = [];
			self.lines.tzArr.forEach((item) => {
				self.lines.pixelList.push({
					message: item.message,
					pixel: map.pointToPixel(item.H)
				})
			})
			self.lines.ctx = baseCtx
			if (zoom > 12) {
				self.lines.draw();
			} else {
				//将屏幕分为50*50的网格,并且确定左上角和右下角的点的位置
				var pmWg = tool.cfpmFunc(baseCtx.canvas.width, baseCtx.canvas.height, 50)
				//将网格内存在的点做进一步的筛选
				// console.log(pmWg)
				var jhArr = []
				self.lines.tzArr.forEach((item) => {
					var thisPixel = map.pointToPixel(item.H);
					//筛选掉超出屏幕边界的点
					if (thisPixel.x < 0 || thisPixel.y < 0 || thisPixel.x > baseCtx.canvas.width || thisPixel.y > baseCtx.canvas
						.height) {
						return
					}
					//进一步筛选点
					for (let k in pmWg) {
						var itemWg = pmWg[k]
						if (tool.jhFunc(thisPixel, itemWg, 50, 50)) {
							if (jhArr[k]) {
								continue;
							} else {
								jhArr[k] = {
									message: item.message,
									pixel: thisPixel
								}
							}

						}
					}

				})
				self.lines.jhArr = jhArr;
				self.lines.drawHot();
			}



		};

		var addLine = function addLine() {
			// if (self.lines!=""&&!self.lines) return;
			var dataset = options.data;
			dataset.forEach((item, index) => {
				item.H = new BMap.Point(item.lng, item.lat)
			})
			var line = new Line({
				tzArr: dataset
			})
			self.lines = line;
		};

		self.init(userOptions, options);

		baseLayer = new CanvasLayer({
			map: map,
			update: brush
		});

		this.clickEvent = this.clickEvent.bind(this);

		this.bindEvent();
	};

	PointLine.prototype.init = function(settings, defaults) {
		//合并参数
		tool.merge(settings, defaults);

		this.options = defaults;
	};

	PointLine.prototype.bindEvent = function(e) {
		var map = this.map;
		if (this.options.methods) {
			if (this.options.methods.click) {
				map.setDefaultCursor("default");
				map.addEventListener('click', this.clickEvent);
			}
			if (this.options.methods.mousemove) {
				map.setDefaultCursor("default");
				map.addEventListener('mousemove', this.moveEvent);
			}
		}
	};

	PointLine.prototype.clickEvent = function(e) {
		var self = this,

			lines = self.lines.pixelList;
		var curPt = e.pixel;
		var arr = []
		if (lines.length > 0) {
			lines.forEach(function(marker, i) {
				var markerPixel = marker.pixel;
				var isOnMarker = tool.containStrokeImage(curPt, markerPixel)
				if (isOnMarker) {
					arr.push({
						index: i,
						marker: marker
					})
				}

			});
		}
		if (arr.length > 1) {
			var lastGod = tool.selectZjFunc(arr, curPt)
			self.options.methods.click(e, arr[lastGod].marker.message);
			return;
		} else if (arr.length == 1) {
			var lastGod = arr[0].marker.message
			self.options.methods.click(e, arr[0].marker.message);
			return;
		} else {
			return;
		}
	};
	return PointLine;

})));

实现的结果如下图所示:

基于百度地图的数据可视化,包括大量数据的标绘以及热力图的插入
基于百度地图的数据可视化,包括大量数据的标绘以及热力图的插入
基于百度地图的数据可视化,包括大量数据的标绘以及热力图的插入
下面是我的源码地址,可以直接运行,具体的点聚合算法以及屏幕切割算法、点击重合标绘物的判断方法等等我都有详细的注释,目前10万左右的数据会在最小层级上卡顿,还有就是图片需要用到base64的方法,因为做测试老是出现图片跨域的方法,我也懒得专门弄一个服务端的环境了。具体的解决方法自行百度。。。

github地址

本文地址:https://blog.csdn.net/wusu0wei/article/details/112547540