场景
需要对全国范围9万多个方形网格(shp格式文件)在地图上进行着色展示。直接使用leaflet操作geojson数据的话,地图加载慢,拖拽、缩放卡顿。
解决方案
解决这个问题的基本思路是:将geojson数据转化为pbf/png文件并发布成服务,leaflet通过加载pbf/png文件渲染地图,提高效率。
-
geojson转化为mbtiles
通过tippecanoe将geojson格式转换为mbtiles格式。tippecanoe安装:
git clone https://github.com/mapbox/tippecanoe.git
cd tippecanoe
make –j
make install
tippecanoe使用方法:
tippecanoe -o chred.mbtiles -Z4 -z12 -pC T3.JSON
# -o 输出文件
# -Z 最大缩放等级
# -z 最小缩放等级
# -pC 生成的文件可以被计算机识别
# --drop-densest-as-needed 抽稀
# chred.mbtiles 输出的mbtiles文件名称
# T3.JSON 输入的geojson文件名称
详细使用方法参见:Tippecanoe矢量切片的命令行参数解析。
-
mbtiles发布成pbf文件
mbtiles发布成pbf文件的方法很多,这里使用mbview实现。安装mbview需要安装nodejs等相关依赖。
yum install nodejs
yum update openssl -y
yum install gcc+ gcc-c++
yum install sqlite-devel
yum install zlib
yum install
zlib-devel
安装mbview:
git clone https://github.com/mapbox/mbview.git
cd mbview
npm install
npm install -g mbview
如果安装过程中,出现无法创建文件夹的错误,可以尝试:
npm install --unsafe-perm
npm install -g mbview --unsafe-perm
安装完成之后,在nodejs/lib/node_modules/mbview/views/ 文件夹下,存在vector.ejs等四个文件是地图前端生成时的模板。其中,vector.ejs文件中可以修改前端的访问地址,可以将localhost替换为服务器地址,实现远程访问。
map.on('load', function () {
<% Object.keys(sources).forEach(function (sid) { %>
map.addSource('<%= sid %>', {
type: 'vector',
tiles: [
'http://localhost:3000/<%= sid %>/{z}/{x}/{y}.pbf'
],
maxzoom: <%= sources[sid].maxzoom %>
});
<% include layers %>
<% }); %>
});
此后,可以通过mbview将mbtiles发布为pbf格式文件:
mbview chred.mbtiles
发布成功后,可以在浏览器中访问http://localhost:3000查看发布的地图服务:
此时,利用leaflet加载pbf文件会还会遇到跨域访问问题,通过修改nodejs/lib/node_modules/mbview/文件夹下的mbview.js文件,添加app.use段,实现跨域访问pbf文件。
app.get('/', function (req, res) {
if (format === 'pbf') {
res.render('vector', config);
} else {
res.render('raster', config);
}
});
app.use(function(req, res, next) {
// 服务端要支持跨域,否则会出现跨域问题
res.header("Access-Control-Allow-Origin", "*");
res.header("Access-Control-Allow-Headers",
"Origin, X-Requested-With, Content-Type, Accept");
next();
});
- leaflet加载pbf文件
pbf发布成功后,可以通过leaflet的插件Leaflet.VectorGrid加载,进一步渲染地图:
//pbf网格图层
var gridLayer = L.vectorGrid.protobuf(
// 加载pbf文件
'http://49.234.83.212:3000/chred.mbtiles/{z}/{x}/{y}.pbf',
{
rendererFactory: L.canvas.tile,
attribution: "© CHRED",
interactive: true,
getFeatureId: function(feature) {
return feature.properties.id;
},
vectorTileLayerStyles: {
T3JSON: function(feature) { //T3JSON 是转化mbtiles时layer的名称
return {
fill: true,
fillColor: getColor(feature.TT),
fillOpacity: 0.7,
stroke: false, //不显示网格边框线
weight: 0.5,
color: "#595959",
}
}
}
}
);
一个例子
数据来源于中国高空间分辨率网格数据(China High Resolution Emission Gridded Database,CHRED):
共98000多个网格,加载稍慢,拖拽与缩放无压力。
主要代码:
<body>
<div id="map"></div>
<script>
//根据数值大小对网格进行着色
function getColor(d) {
return d > 1000000 ? 'rgba(255,34,0,0.8)' :
d > 100000 ? 'rgba(255,132,0,0.8)' :
d > 10000 ? 'rgba(255,230,00.8)' :
d > 1000 ? 'rgba(161,214,0,0.8)' :
'rgba(57,156,0,0.8)';
}
//创建地图
var map = L.map('map').setView([40.9828,120.141], 5);
//加载基础图层
var baseLasyer = L.tileLayer('http://{s}.tile.osm.org/{z}/{x}/{y}.png', {
attribution: '© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
}).addTo(map);
//加载pbf网格图层
var gridLayer = L.vectorGrid.protobuf(
// 加载pbf文件,host:主机名,port:端口号,sid:mbtiles文件名
'http://host:port/sid/{z}/{x}/{y}.pbf',
{
rendererFactory: L.canvas.tile,
attribution: "© CHRED",
interactive: true,
getFeatureId: function(feature) {
return feature.properties.id;
},
vectorTileLayerStyles: {
T3JSON: function(feature) { //T3JSON 是转化mbtiles时layer的名称
return {
fill: true,
fillColor: getColor(feature.TT),
fillOpacity: 0.7,
stroke: false, //不显示网格边框线
weight: 0.5,
color: "#595959",
}
}
}
}
);
gridLayer.addTo(map);
//鼠标悬浮显示网格信息
var info = L.control();
info.onAdd = function (map) {
this._div = L.DomUtil.create('div', 'info');
this.update();
return this._div;
};
info.update = function (props) {
this._div.innerHTML = '<h4 style="color">CO2 indicators</h4>' + (props ?
'<b>'+'二氧化碳总排放: ' + props.TT.toFixed(0) + 't' + '</b><br />' :
'鼠标悬停网格区域显示相关信息');
};
info.addTo(map);
function highlightFeature(e) {
//更新信息
var props = e.layer.properties;
info.update(props);
}
function resetHighlight(e) {
//重置信息
info.update();
}
gridLayer.on({
mouseover: highlightFeature,
mouseout: resetHighlight,
});
//图层控件
var baseMap = {}
var overlayMaps = {
'二氧化碳总排放':gridLayer ,
}
L.control.layers(baseMap,overlayMaps).addTo(map);
//添加常见控件
L.control.scale({maxWidth:100,metric:true,imperial:false}).addTo(map); // 比例尺控件
L.control.locate({ // 定位控件
position: 'topleft',
strings: {
title: "Show me where I am, yo!"
}
}).addTo(map);
L.control.browserPrint().addTo(map); //打印控件
//添加图例控件
var gridLayerLegend = L.control({ position: "bottomright" });
gridLayerLegend.onAdd = function(map) {
var div = L.DomUtil.create("div", "legend"),
grades = [0,1000, 10000, 100000, 1000000];
div.innerHTML+='<p style = "font-weight:bold; height:0px;">二氧化碳排放量(吨/年)</p>' + '<br>';
for (var i = 0; i < grades.length; i++) {
div.innerHTML +=
'<i style="background:' +
getColor(grades[i] + 1) +
'"></i> ' +
grades[i] +
(grades[i + 1] ? "–" + grades[i + 1] + "<br>" : "+");
}
return div;
};
gridLayerLegend.addTo(map);
</script>
</body>