Leaflet.js上高性能渲染百万级GeoJson数据的解决方案(无需服务端)

1,081 阅读4分钟

1.背景介绍

GeoJSON是一种对各种地理数据结构进行编码的格式,是gis系统中渲染点线面等要素的标准格式。但是如果GeoJson数据过大(比如一个包含上百万经纬度信息的GeoJson数据文件大小可能会超过100M),这时浏览器渲染这个文件压力就会很大,地图卡顿严重甚至浏览器崩溃。一般针对这种数据可能常规操作就是把数据发布成矢量切片服务供前端调用。将数据发布成OGC服务供前端调用。比如使用geoserverArcGis等工具将数据发布成失量切片服务,这样可以实现大数据量上图展示。

但是不借助服务端就没办法实现百万级数据高性能渲染了吗?Leaflet.js的插件库中找到了解决方案!

geojson-vt:它是一个通过将 GeoJSON 数据转换为矢量切片的高效库。要设计用于在浏览器端(无需服务器)实现大型地理空间数据集的渲染和交互。支持如Mapbox GL JSLeafletOpenLayersd3,以及 Node.js 服务器应用程序。它的原理也很简单,就是基于瓦片分割矢量切片的思想。生成符合矢量切片规范的 JSON 等效项。为了使数据渲染和交互快速,对图块进行了简化,保留适合每个缩放级别的最低细节级别(简化形状、过滤掉微小的多边形和折线)。地图上会自动根据当前可视范围(xyz)加载不同细节程度的切片数据。

在leaflet插件库中还有一个依赖geojson-vt更快速简单使用的插件:leaflet-geojson-vt。下面我们利用这个插件来实现百万级GeoJson数据上图展示。

有一个方便的调试页面可以在不同的数据上测试geojson-vt 。只需将任何 GeoJSON 拖到页面上,观察控制台和页面即可。

2.效果展示

在github上有一个动态加载具有 540 万个点的(100Mb)美国邮政编码 GeoJSON,可以看到性能还是很高的。 image.png

这是在leaflet中通过geojson-vt渲染全球海上边界数据,相比于直接加载geojon性能要好太多。 20240509175626_rec_.gif

3.使用方法

leaflet-geojson-vt的使用比较简单,首先是安装leaflet以及leaflet-geojson-vt,leaflet的安装以及基本使用我就不再介绍,下面主要列出leaflet-geojson-vt使用方式。

npm install leaflet-geojson-vt

可以通过cdn方式加载资源

<script src="https://unpkg.com/geojson-vt@3.2.0/geojson-vt.js"></script>
<script src="[path to js]/leaflet-geojson-vt.js"></script>

在main.js中引入leaflet-geojson-vt包

import "leaflet-geojson-vt/src/leaflet-geojson-vt.js"

使用方式:

//获取geojson文件
let data = require('../../assets/data/ezz_geojson_low.json');
var options = {
    maxZoom: 16,  // max zoom to preserve detail on; can't be higher than 24
    tolerance: 3, // simplification tolerance (higher means simpler)
    extent: 4096, // tile extent (both width and height)
    buffer: 64,   // tile buffer on each side
    debug: 0,     // logging level (0 to disable, 1 or 2)
    lineMetrics: false, // whether to enable line metrics tracking for LineString/MultiLineString features
    promoteId: null,    // name of a feature property to promote to feature.id. Cannot be used with `generateId`
    generateId: false,  // whether to generate feature ids. Cannot be used with `promoteId`
    indexMaxZoom: 5,       // max zoom in the initial tile index
    indexMaxPoints: 100000 // max number of points per tile in the index
    style: {  //样式配置,参考L.path
        color: 'blue',
        fill: false,
        fillOpacity: 0.2,
    }
};

var geojsonTileLayer =L.geoJson.vt(data, options); //定义geojson矢量切片图层
var map = L.map('map').setView([51.505, -0.09], 13);
geojsonTileLayer.addTo(map); //上图展示

leaflet-geojson-vt还支持分级渲染不同类型的数据

let data = require('../../assets/data/ezz_geojson_low.json');
var options = {
  maxZoom: 16,
  tolerance: 3,
  debug: 0,
  style: (properties) => {
    if (properties.ADM1_PCODE == 'NP07') {
      return  {fillColor:"#0F0",color:"#F2FF00"};
    } else {
      return  {fillColor:"#1EB300",color:"#F2FF00"};
    }
  }
};
var geojsonTileLayer =L.geoJson.vt(data, options); //定义geojson矢量切片图层
var map = L.map('map').setView([51.505, -0.09], 13);
geojsonTileLayer.addTo(map); //上图展示

我们还可以通过getTile方式获取对应范围内的数据,通过控制台我们也可以看到它首先对数据进行切片,拿到全部的层级,然后通过矢量切片的方式去对不同的层级数据进行抽稀渲染。

var tileIndex = geojsonvt(geoJSON);

// 获取对应地图xyz下的要素
var features = tileIndex.getTile(z, x, y).features;
//通过打印输出可以看到它确实是通过切片的方式去对数据进行抽稀
console.log(tileIndex.tileCoords); // [{z: 0, x: 0, y: 0}, ...]

4.总结

geojson-vt在不依赖服务端的情况下解决了geojson大数据量展示的问题,并且性能也很高,但是也有不足,就是无法进行交互,比如鼠标点击高亮。leaflet-geojson-vt 并不支持直接的事件处理,如点击事件 click 等,因为它不是标准的 Leaflet 图层。它将个数据切分成许多小的、快速渲染的瓦片组成,这些瓦片本身并不是交互式的。所以只能通过点击屏幕位置获取对应位置的瓦片来实现。 相关资料: