概述
在ol4里面可以通过Vector Layer的方式进行点的渲染,但是当点的个数比较多的时候,会存在明显的操作不流畅。本文讲述如何利用ImageCanvas接口,对大量的点进行展示,并添加相应的交互。
实现效果
实现分析
1.效率差异如何得来?
ol的最终渲染也是通过canvas的方式来渲染的,但是为什么效率会差这么多呢?分析原因,是因为: 1)Vector的渲染方式会保留很多交互相关的操作; 2)ImageCanvas的方式其实是将数据渲染到一个新的画布上,再以图片的方式渲染到map的canvas上;
2.交互如何实现
地图上的渲染查看相关的接口实现起来比较简单,这里重点说一下渲染完后如何交互。在实现地图交互的时候,存在两个技术点: 1)如何判断鼠标经过的位置要触发交互的位置? 我们知道每一个点的大小是指的像素值,所以在判断位置的时候调用view的getResolution()接口,实时计算一个半径,通过鼠标当前点 半径可以创建一个圆,在判断落在圆内的点就为交互的点。代码实现如下:
代码语言:javascript复制map.on('pointermove', function (e) {
var coord = e.coordinate;
var res = map.getView().getResolution();
var _radius = res * r;
var _circle = new ol.geom.Circle(coord, _radius);
var lonlat = ol.proj.toLonLat(coord);
var index = getLonLatIndex(lonlat[0], lonlat[1]);
if(index !== -1) {
var data = dataCache[index].data;
for (var i = 0; i < data.length; i ) {
var d = data[i];
var _coord = ol.proj.fromLonLat([d.lon, d.lat]);
if (_circle.intersectsCoordinate(_coord)) {
map.getTargetElement().style.cursor = 'pointer';
document.getElementById('popup').innerText = d.n;
popup.setPosition(_coord);
break;
} else {
map.getTargetElement().style.cursor = 'default';
popup.setPosition(null);
}
}
}
});
2)大量数据的检索如何优化? 针对大量数据的检索,在处理的时候做了前端的分区缓存,在通过经纬度去查找对应的分区位置,再去找里面的数据。实现代码如下:
代码语言:javascript复制 // 1、核心函数,通过经纬度计算分区位置
function getLonLatIndex(lon, lat) {
var index = -1;
// 在边界内
if(ol.extent.containsCoordinate(chinaB, [lon, lat])) {
var idxX = (lon - chinaB[0]) / deltLon,
idxY = (lat - chinaB[1]) / deltLat;
index = Math.floor(idxY) * numLon Math.floor(idxX);
}
return index;
}
// 2、缓存数据
for(var i = 0;i<data.length;i ) {
var d = data[i];
var idx = getLonLatIndex(d.lon, d.lat);
if(idx !== -1) {
dataCache[idx].data.push(d);
}
}
实现源码
代码语言:javascript复制<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<link rel="stylesheet" href="https://openlayers.org/en/v4.6.5/css/ol.css" type="text/css">
<link rel="stylesheet" href="css/demo.css" type="text/css">
</head>
<body>
<div id="map"></div>
<div id="popup"></div>
<script src="http://libs.baidu.com/jquery/2.0.0/jquery.min.js"></script>
<script src="https://openlayers.org/en/v4.6.5/build/ol.js"></script>
<script src="js/demo.js"></script>
<script>
var vec_w = getWmsLayer("light");
var data = [];
var chinaB = [60, 0, 140, 55];
var deltLon = 8,
deltLat = 5.5;
var numLon = (chinaB[2] - chinaB[0]) / deltLon,
numLat = (chinaB[3] - chinaB[1]) / deltLat;
var dataCache = [];
// 数据从左下角开始
for(var j =0;j<numLat;j ) {
var maxLat = chinaB[1] (j 1)*deltLat,
minLat = chinaB[1] j*deltLat;
for(var i =0;i<numLon;i ) {
var maxLon = chinaB[0] (i 1)*numLon,
minLon = chinaB[0] i*numLon;
var dCache = {
xmin: minLon,
xmax: maxLon,
ymin: minLat,
yMax: maxLat,
data: []
};
dataCache.push(dCache);
}
}
var r = 3;
var map = new ol.Map({
controls: ol.control.defaults({
attribution: false
}),
target: 'map',
layers: [vec_w],
view: new ol.View({
center: ol.proj.fromLonLat([98.633, 31.607]),
zoom:4,
minZoom:0,
maxZoom:18
})
});
var popup = new ol.Overlay({
element: document.getElementById('popup'),
position: null,
positioning: 'center-left',
offset: [16, 0]
});
map.addOverlay(popup);
var imageLayer = new ol.layer.Image({
source: null,
opacity: 0.85
});
map.addLayer(imageLayer);
map.on('click', function (e) {
var coord = e.coordinate;
var res = map.getView().getResolution();
var _radius = res * r;
var _circle = new ol.geom.Circle(coord, _radius);
var lonlat = ol.proj.toLonLat(coord);
var index = getLonLatIndex(lonlat[0], lonlat[1]);
if(index !== -1) {
var data = dataCache[index].data;
for (var i = 0; i < data.length; i ) {
var d = data[i];
var _coord = ol.proj.fromLonLat([d.lon, d.lat]);
if (_circle.intersectsCoordinate(_coord)) {
console.log(d);
break;
}
}
}
});
map.on('pointermove', function (e) {
var coord = e.coordinate;
var res = map.getView().getResolution();
var _radius = res * r;
var _circle = new ol.geom.Circle(coord, _radius);
var lonlat = ol.proj.toLonLat(coord);
var index = getLonLatIndex(lonlat[0], lonlat[1]);
if(index !== -1) {
var data = dataCache[index].data;
for (var i = 0; i < data.length; i ) {
var d = data[i];
var _coord = ol.proj.fromLonLat([d.lon, d.lat]);
if (_circle.intersectsCoordinate(_coord)) {
map.getTargetElement().style.cursor = 'pointer';
document.getElementById('popup').innerText = d.n;
popup.setPosition(_coord);
break;
} else {
map.getTargetElement().style.cursor = 'default';
popup.setPosition(null);
}
}
}
});
function canvasFunction(extent, res, pixelRatio, size) {
var mapSize = map.getSize();
var zoom = map.getView().getZoom();
r = zoom * 0.6;
r = r<3 ? 3: r;
r = r > 10 ? 10: r;
var xOff = (size[0] - mapSize[0]) / 2,
yOff = (size[1] - mapSize[1]) / 2;
var canvas = document.createElement('canvas');
canvas.setAttribute('width', size[0]);
canvas.setAttribute('height', size[1]);
var ctx = canvas.getContext('2d');
for(var i = 0;i<data.length;i ) {
var d = data[i];
ctx.fillStyle = getValueColor(d["pre_1h"]);
ctx.strokeStyle = 'rgba(0,0,0,0.2)';
ctx.strokeWidth = 1;
ctx.beginPath();
var coord = ol.proj.fromLonLat([d["lon"], d["lat"]]);
var pixel = map.getPixelFromCoordinate(coord);
// x, y, r, start, end
ctx.arc(pixel[0] xOff, pixel[1] yOff, r, 0, 2*Math.PI);
ctx.fill();
ctx.stroke();
}
return canvas;
}
function getLonLatIndex(lon, lat) {
var index = -1;
// 在边界内
if(ol.extent.containsCoordinate(chinaB, [lon, lat])) {
var idxX = (lon - chinaB[0]) / deltLon,
idxY = (lat - chinaB[1]) / deltLat;
index = Math.floor(idxY) * numLon Math.floor(idxX);
}
return index;
}
$.get("datas/data.json", (res) => {
data = res.data;
// data = getRandomData();
var source = new ol.source.ImageCanvas({
canvasFunction: canvasFunction
});
imageLayer.setSource(source);
for(var i = 0;i<data.length;i ) {
var d = data[i];
var idx = getLonLatIndex(d.lon, d.lat);
if(idx !== -1) {
dataCache[idx].data.push(d);
}
}
})
</script>
</body>
</html>