概述
先描述一下大致场景:以0.05为单元格大小生成网格,并在地图上绘制,绘制的时候需要区分海陆。本文以此需求为契机,简单描述一下该需求的实现以及如何来优化。
效果
实现
优化前
var source = new ol.source.Vector({
features: []
});
var vector = new ol.layer.Vector({
source: source,
zIndex: 1,
opacity: 0.65,
style: styleFunction
});
map.addLayer(vector);
var landData = format.readFeatures(chinaZone, options);
landGeom = landData[0].getGeometry();
function styleFunction(feat) {
var i = feat.get("i"),
j = feat.get("j"),
isLand = feat.get("land");
if(isLand) {
var val = data[i][j];
var color;
if (val > 0.33 && val <= 0.66) {
color = "orange";
} else if (val > 0.66 && val <= 1) {
color = "red";
} else {
color = 'yellow';
}
return new ol.style.Style({
fill: new ol.style.Fill({
color: color,
})
})
}
}
function generateGrid() {
var bound = $("#bound").val(),
size = Number($("#size").val());
var bounds = bound.split(",").map(Number);
console.time('Time Test');
createGrid(bounds, size);
console.timeEnd('Time Test')
}
function isOnLand(coord) {
var is = false;
for(var i = 0;i<landData.length;i++){
var geom = landData[i].getGeometry();
if (geom.intersectsCoordinate(coord)) {
is = true;
break;
}
}
return is;
}
function createGrid(bound, size) {
var gridData = {
type: "FeatureCollection",
features: []
};
var deltaLon = bound[2] - bound[0],
deltaLat = bound[3] - bound[1];
var numLon, numLat;
numLon = Math.ceil(deltaLon / size);
numLat = Math.ceil(deltaLat/ size);
var minLon = bound[0],
maxLat = bound[3];
for(var i = 0; i < numLat; i++) {
var lat1 = maxLat - i * size,
lat2 = maxLat - (i + 1) * size;
var latC = (lat1 + lat2) / 2;
data[i] = [];
for(var j = 0; j < numLon; j++) {
data[i][j] = Math.random();
var lon1 = minLon + j * size,
lon2 = minLon + (j + 1) * size;
var lonC = (lon1 + lon2) / 2;
var coord = ol.proj.fromLonLat([lonC, latC]);
var prop = {
i: i,
j: j,
land: isOnLand(coord)
};
// 网格面
var featG = {
"type":"Feature",
"properties": prop,
"geometry":{
"type":"Polygon",
"coordinates":[[
[lon1, lat1],
[lon2, lat1],
[lon2, lat2],
[lon1, lat2],
[lon1, lat1]
]]
}
};
gridData.features.push(featG);
}
}
var gridFeatures = format.readFeatures(gridData, options);
source.addFeatures(gridFeatures);
}
优化后
var source = new ol.source.Vector({
features: []
});
var vector = new ol.layer.Vector({
source: source,
zIndex: 1,
opacity: 0.65,
style: styleFunction,
renderMode: 'image'
});
map.addLayer(vector);
var landData = format.readFeatures(chinaZone, options);
landGeom = landData[0].getGeometry();
function styleFunction(feat) {
// i为lat,j为lon
var i = feat.get("i"),
j = feat.get("j"),
land = feat.get("land");
if(land) {
var val = data[i][j];
var color;
if (val > 0.33 && val <= 0.66) {
color = "orange";
} else if (val > 0.66 && val <= 1) {
color = "red";
} else {
color = 'yellow';
}
return new ol.style.Style({
// stroke: new ol.style.Stroke({
// color: 'grey',
// width: 1
// }),
fill: new ol.style.Fill({
color: color,
})
})
}
}
function generateGrid() {
var bound = $("#bound").val(),
size = Number($("#size").val());
var bounds = bound.split(",").map(Number);
console.time('Time Test');
createGrid(bounds, size);
console.timeEnd('Time Test')
}
function isOnLand(coord) {
return landGeom.intersectsCoordinate(coord);
}
/**
* 创建网格
* @param bound
* @param size
* @returns {{grid: {features: Array, type: string}, center: {features: Array, type: string}}}
*/
function createGrid(bound, size) {
var deltaLon = bound[2] - bound[0],
deltaLat = bound[3] - bound[1];
var numLat;
numLat = Math.ceil(deltaLat / size);
for (var i = 0; i < numLat; i++) {
data[i] = [];
getFeatures(bound, i, deltaLon, size)
.then(res => {
var json = {
"type": "FeatureCollection",
features: res
};
var features = format.readFeatures(json, options);
source.addFeatures(features);
})
}
}
function getFeatures(bound, i, deltaLon, size) {
var promise = new Promise(function(resolve, reject) {
window.setTimeout(function() {
var features = [];
var minLon = bound[0],
maxLat = bound[3],
numLon = Math.ceil(deltaLon / size);
var lat1 = maxLat - i * size,
lat2 = maxLat - (i + 1) * size;
var latC = (lat1 + lat2) / 2;
for(var j = 0; j < numLon; j++) {
var lon1 = minLon + j * size,
lon2 = minLon + (j + 1) * size;
var lonC = (lon1 + lon2) / 2;
var coord = ol.proj.fromLonLat([lonC, latC]);
var prop = {
i: i,
j: j,
land: isOnLand(coord)
};
data[i][j] = Math.random();
// 网格面
var featG = {
"type":"Feature",
"properties": prop,
"geometry":{
"type":"Polygon",
"coordinates":[[
[lon1, lat1],
[lon2, lat1],
[lon2, lat2],
[lon1, lat2],
[lon1, lat1]
]]
}
};
features.push(featG);
}
resolve(features);
});
});
return promise;
}
思路分析
通过前面的两张图可以明显看出,优化前后效率上有了质的变化,这说明我们的优化思路是正确的。下面说一下我在做这部分优化的时候的思路:
1.找到原因
从本案例来看,能影响效率的有可能有两点:1、js的for循环比较慢;2、渲染到地图上的时候比较慢。于是就做了一下测试,发现原因其实是1,而不是2.
2.思考解决
既然找到了是1影响了效率,那就考虑如何优化1。在本案例中,第一层循环有200,第二层循环有140,由于js的执行是单线程顺序执行的,所以我思考把这个循环拆开,拆成若干个循环,异步执行,这样就能避免同步执行慢的问题。因此,在优化的时候用了setTimeout
和promise
来实现循环的异步执行。
3. 其他
此外,在创建vectorLayer的时候,加入了renderMode: 'image'
参数,提高渲染层面的效率。
思考优化
本案例其实还可以做进一步的优化,优化主要在渲染上,优化思路类似于地图切片,将展示数据建立索引,并将展示结果进行分块,以达到优化展示。
技术博客
CSDN:http://blog.csdn.NET/gisshixisheng
在线教程
https://edu.csdn.net/course/detail/799
https://edu.csdn.net/course/detail/7471
联系方式
类型 | 内容 |
---|---|
1004740957 | |
公众号 | lzugis15 |
[email protected] | |
webgis群 | 452117357 |
Android群 | 337469080 |
GIS数据可视化群 | 458292378 |