使用 WebGL 过滤大量图形要素
这个例子展示了如何使用ol/layer/WebGLPoints的文字样式来动态地过滤大量的几何点。
上面的地图是基于美国国家航空航天局的数据集,包含45k记录的陨石着陆点。
每颗陨石在地图上都有一个圆圈标记(圆圈越大,物体越重)。增加了一个脉冲效应,它被影响的年份略微抵消了。
本示例演示使用 ol/layer/WebGLPoints
的文字样式动态的过滤大量的点图形。
上边的地图渲染了 NASA 的数据集,包含45k记录的陨石着陆点。
每颗陨石在地图上都有一个圆圈标记(圆圈越大,物体越重)。增加了一个脉冲效应,它被影响的年份略微抵消了。
每个陨石在地图上以圆圈来标记显示(圆圈越大,陨石越重)。
并且添加了脉冲效应,这个脉冲效应被影响的年份略微抵消了(没太明白啥意思)。
调整滑块会导致日期范围之外的对象被过滤出地图。
通过调整滑块,可以改变日期范围,并且将日期之外的图形对象过滤到地图之外。
此功能是通过改变提供给WebGL层的样式( style
)对象中的变量来实现的。
除此之外,还要注意的是,为了确保地图每一帧都刷新,最后一段代码是必须的。
import Feature from 'ol/Feature.js';
import Map from 'ol/Map.js';
import Point from 'ol/geom/Point.js';
import Stamen from 'ol/source/Stamen.js';
import TileLayer from 'ol/layer/Tile.js';
import View from 'ol/View.js';
import WebGLPointsLayer from 'ol/layer/WebGLPoints.js';
import {Vector} from 'ol/source.js';
import {fromLonLat} from 'ol/proj.js';
const vectorSource = new Vector({
attributions: 'NASA',
});
const oldColor = 'rgba(242,56,22,0.61)';
const newColor = '#ffe52c';
const period = 12; // animation period in seconds
const animRatio = [
'^',
[
'/',
[
'%',
[
'+',
['time'],
['interpolate', ['linear'], ['get', 'year'], 1850, 0, 2015, period],
],
period,
],
period,
],
0.5,
];
const style = {
variables: {
minYear: 1850,
maxYear: 2015,
},
filter: ['between', ['get', 'year'], ['var', 'minYear'], ['var', 'maxYear']],
symbol: {
symbolType: 'circle',
size: [
'*',
['interpolate', ['linear'], ['get', 'mass'], 0, 8, 200000, 26],
['-', 1.75, ['*', animRatio, 0.75]],
],
color: ['interpolate', ['linear'], animRatio, 0, newColor, 1, oldColor],
opacity: ['-', 1.0, ['*', animRatio, 0.75]],
},
};
// handle input values & events
const minYearInput = document.getElementById('min-year');
const maxYearInput = document.getElementById('max-year');
function updateStatusText() {
const div = document.getElementById('status');
div.querySelector('span.min-year').textContent = minYearInput.value;
div.querySelector('span.max-year').textContent = maxYearInput.value;
}
minYearInput.addEventListener('input', function () {
style.variables.minYear = parseInt(minYearInput.value);
updateStatusText();
});
maxYearInput.addEventListener('input', function () {
style.variables.maxYear = parseInt(maxYearInput.value);
updateStatusText();
});
updateStatusText();
// load data;
const client = new XMLHttpRequest();
client.open('GET', 'data/csv/meteorite_landings.csv');
client.onload = function () {
const csv = client.responseText;
const features = [];
let prevIndex = csv.indexOf('\n') + 1; // scan past the header line
let curIndex;
while ((curIndex = csv.indexOf('\n', prevIndex)) != -1) {
const line = csv.substr(prevIndex, curIndex - prevIndex).split(',');
prevIndex = curIndex + 1;
const coords = fromLonLat([parseFloat(line[4]), parseFloat(line[3])]);
if (isNaN(coords[0]) || isNaN(coords[1])) {
// guard against bad data
continue;
}
features.push(
new Feature({
mass: parseFloat(line[1]) || 0,
year: parseInt(line[2]) || 0,
geometry: new Point(coords),
})
);
}
vectorSource.addFeatures(features);
};
client.send();
const map = new Map({
layers: [
new TileLayer({
source: new Stamen({
layer: 'toner',
}),
}),
new WebGLPointsLayer({
style: style,
source: vectorSource,
disableHitDetection: true,
}),
],
target: document.getElementById('map'),
view: new View({
center: [0, 0],
zoom: 2,
}),
});
// animate the map
function animate() {
map.render();
window.requestAnimationFrame(animate);
}
animate();
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Filtering features with WebGL</title>
<link rel="stylesheet" href="node_modules/ol/ol.css">
<style>
.map {
width: 100%;
height: 400px;
}
</style>
</head>
<body>
<div id="map" class="map"></div>
<form>
<div id="status">Show impacts between <span class="min-year"></span> and <span class="max-year"></span></div>
<label for="min-year">Minimum year:</label>
<input id="min-year" type="range" min="1850" max="2015" step="1" value="1850"/>
<label for="max-year">Maximum year:</label>
<input id="max-year" type="range" min="1850" max="2015" step="1" value="2015"/>
</form>
<!-- Pointer events polyfill for old browsers, see https://caniuse.com/#feat=pointer -->
<script src="./resources/elm-pep.js"></script>
<script type="module" src="main.js"></script>
</body>
</html>
{
"name": "filter-points-webgl",
"dependencies": {
"ol": "7.3.0"
},
"devDependencies": {
"vite": "^3.2.3"
},
"scripts": {
"start": "vite",
"build": "vite build"
}
}