leaflet快速渲染聚合矢量瓦片(附源碼下載)

前言

leaflet 入門開發系列環境知識點了解:html

內容概覽

leaflet快速渲染聚合矢量瓦片
源代碼demo下載git

錄製演示視頻
效果圖以下:
github

具體實現思路:leaflet結合Web Worker技術,藉助supercluster插件,實現leaflet快速展現聚合效果。矢量瓦片請求數據源部分也放在Worker線程來處理,避免阻塞主線程UI交互部分響應。
測試數據:聚合點23.8w,矢量瓦片數據源15w左右。
引用關鍵技術點:
1.Web Worker:Web Worker的做用,就是爲JavaScript創造多線程環境,容許主線程建立Worker線程,將一些任務分配給後者運行。在主線程運行的同時,Worker線程在後臺運行,二者互不干擾。等到 Worker 線程完成計算任務,再把結果返回給主線程。這樣的好處是,一些計算密集型或高延遲的任務,被Worker線程負擔了,主線程(一般負責 UI 交互)就會很流暢,不會被阻塞或拖慢。
2.supercluster插件:supercluster插件json

  • 完整的見源碼demo下載,聚合效果實現部分思路:
    1.supercluster插件把聚合點數據源預處理,按照矢量瓦片思路提早把聚合點數據源切片處理好,每一個級別對應的聚合點數以及對應數據源(zoom,count,data);
    2.leaflet經過監聽地圖範圍變化事件,主線程跟worker子線程之間互相通訊交互,而後leaflet根據worker子線程返回來的數據源,動態刷新聚合數據。
主線程部分:
const markers = L.geoJson(null, {
pointToLayer: createClusterIcon
}).addTo(map);
var worker = new Worker('worker.js');
let ready = false;
worker.onmessage = function (e) {
if (e.data.ready) {
ready = true;
update();
} else if (e.data.expansionZoom) {
map.flyTo(e.data.center, e.data.expansionZoom);
} else {
markers.clearLayers();
markers.addData(e.data);
}
};

function update() {
if (!ready) return;
const bounds = map.getBounds();
worker.postMessage({
bbox: [bounds.getWest(), bounds.getSouth(), bounds.getEast(), bounds.getNorth()],
zoom: map.getZoom()
});
}
map.on('moveend', update);
function createClusterIcon(feature, latlng) {
if (!feature.properties.cluster) return L.marker(latlng);
const count = feature.properties.point_count;
const size =
count < 100 ? 'small' :
count < 1000 ? 'medium' : 'large';
const icon = L.divIcon({
html: `<div><span>${ feature.properties.point_count_abbreviated }</span></div>`,
className: `marker-cluster marker-cluster-${ size}`,
iconSize: L.point(40, 40)
});
return L.marker(latlng, {icon});
}
markers.on('click', (e) => {
if (e.layer.feature.properties.cluster_id) {
worker.postMessage({
getClusterExpansionZoom: e.layer.feature.properties.cluster_id,
center: e.latlng
});
}
});

worker線程部分:
importScripts('supercluster.min.js');
const now = Date.now();
let index;
//getJSON('../test/fixtures/places.json', (geojson) => {
getJSON('spotPoints.json', (geojson) => {
//console.log(`loaded ${ geojson.length } points JSON in ${ (Date.now() - now) / 1000 }s`);
console.log(`loaded ${ geojson.features.length } points JSON in ${ (Date.now() - now) / 1000 }s`);
index = new Supercluster({
log: true,
radius: 100,//60
extent: 256,
maxZoom: 17//17
}).load(geojson.features);
console.log(index.getTile(0, 0, 0));
postMessage({ready: true});
});
self.onmessage = function (e) {
if (e.data.getClusterExpansionZoom) {
postMessage({
expansionZoom: index.getClusterExpansionZoom(e.data.getClusterExpansionZoom),
center: e.data.center
});
} else if (e.data) {
postMessage(index.getClusters(e.data.bbox, e.data.zoom));
}
};
function getJSON(url, callback) {
const xhr = new XMLHttpRequest();
xhr.open('GET', url, true);
xhr.responseType = 'json';
xhr.setRequestHeader('Accept', 'application/json');
xhr.onload = function () {
if (xhr.readyState === 4 && xhr.status >= 200 && xhr.status < 300 && xhr.response) {
callback(xhr.response);
}
};
xhr.send();
}
  • 完整的見源碼demo下載,矢量瓦片實現部分思路:請求矢量數據源部分放在worker子線程處理,而後返回來leaflet主線程來矢量瓦片渲染可視化。
主線程:
//矢量瓦片
var vectorworker = new Worker('vectorGridworker.js');
vectorworker.onmessage = function (e) {
//vectorworker.terminate();
if (e.data.type == "spot") {//圖斑
vectorGrid = L.vectorGrid.slicer(e.data.geojson, geojsonTileOptions).addTo(map);
}
else {//項目紅線
vectorRedGrid = L.vectorGrid.slicer(e.data.geojson, geojsonRedTileOptions).addTo(map);
}
};
worker線程:
const now = Date.now();
let index;
//getJSON('../test/fixtures/places.json', (geojson) => {
getJSON('quanguoSpot.json', (geojson) => {
//console.log(`loaded ${ geojson.length } points JSON in ${ (Date.now() - now) / 1000 }s`);
console.log(`loaded ${ geojson.features.length } points JSON in ${ (Date.now() - now) / 1000 }s`);
postMessage({type:"spot",geojson:geojson});
});
getJSON('quanguoRedLine.json', (geojson) => {
//console.log(`loaded ${ geojson.length } points JSON in ${ (Date.now() - now) / 1000 }s`);
console.log(`loaded ${ geojson.features.length } points JSON in ${ (Date.now() - now) / 1000 }s`);
postMessage({type:"redline",geojson:geojson});
});
function getJSON(url, callback) {
const xhr = new XMLHttpRequest();
xhr.open('GET', url, true);
xhr.responseType = 'json';
xhr.setRequestHeader('Accept', 'application/json');
xhr.onload = function () {
if (xhr.readyState === 4 && xhr.status >= 200 && xhr.status < 300 && xhr.response) {
callback(xhr.response);
}
};
xhr.send();
}

完整demo源碼見小專欄文章尾部小專欄api

文章尾部提供源代碼下載,對本專欄感興趣的話,能夠關注一波多線程

相關文章
相關標籤/搜索