原文链接及内容

运行界面

: 本例使用的功能不是稳定API的一部分,可能会在不同版本之间发生变化。请参考API文档以了解最新版本中支持的内容。

这个例子展示了如何使用ol/layer/WebGLPoints来渲染大量的雪碧图图标。上面的地图是基于国家UFO报告中心的数据集:每个图标会根据其(目击者)报告的形状(圆盘、光、火球……)来标记一个UFO目击事件。看到的时间越长,图标就越红。

一个非常简单的雪碧图集合,它以PNG格式的文件来使用,在它的网格上包含所有的图标。然后,给ol/layer/WebGLPoints构造函数配置一个样式对象,以实现图层根据目击形状来指定要使用雪碧图上的哪一个图标。

该数据集包含大约80k个点,可以在这里找到:https://www.kaggle.com/NUFORC/ufo-sightings

main.js代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
import Feature from 'ol/Feature.js';
import Map from 'ol/Map.js';
import Point from 'ol/geom/Point.js';
import TileLayer from 'ol/layer/WebGLTile.js';
import VectorSource from 'ol/source/Vector.js';
import View from 'ol/View.js';
import WebGLPointsLayer from 'ol/layer/WebGLPoints.js';
import XYZ from 'ol/source/XYZ.js';
import {fromLonLat} from 'ol/proj.js';

const key = 'Get your own API key at https://www.maptiler.com/cloud/';
const attributions =
'<a href="https://www.maptiler.com/copyright/" target="_blank">&copy; MapTiler</a> ' +
'<a href="https://www.openstreetmap.org/copyright" target="_blank">&copy; OpenStreetMap contributors</a>';

const map = new Map({
layers: [
new TileLayer({
source: new XYZ({
attributions: attributions,
url:
'https://api.maptiler.com/tiles/satellite/{z}/{x}/{y}.jpg?key=' + key,
tileSize: 512,
}),
}),
],
target: document.getElementById('map'),
view: new View({
center: [0, 4000000],
zoom: 2,
}),
});

const oldColor = [255, 160, 110];
const newColor = [180, 255, 200];
const size = 16;

const style = {
variables: {
filterShape: 'all',
},
filter: [
'any',
['==', ['var', 'filterShape'], 'all'],
['==', ['var', 'filterShape'], ['get', 'shape']],
],
symbol: {
symbolType: 'image',
src: 'data/ufo_shapes.png',
size: size,
color: [
'interpolate',
['linear'],
['get', 'year'],
1950,
oldColor,
2013,
newColor,
],
rotateWithView: false,
offset: [0, 0],
textureCoord: [
'match',
['get', 'shape'],
'light',
[0, 0, 0.25, 0.5],
'sphere',
[0.25, 0, 0.5, 0.5],
'circle',
[0.25, 0, 0.5, 0.5],
'disc',
[0.5, 0, 0.75, 0.5],
'oval',
[0.5, 0, 0.75, 0.5],
'triangle',
[0.75, 0, 1, 0.5],
'fireball',
[0, 0.5, 0.25, 1],
[0.75, 0.5, 1, 1],
],
},
};

const shapeSelect = document.getElementById('shape-filter');
shapeSelect.addEventListener('input', function () {
style.variables.filterShape = shapeSelect.value;
map.render();
});
function fillShapeSelect(shapeTypes) {
Object.keys(shapeTypes)
.sort(function (a, b) {
return shapeTypes[b] - shapeTypes[a];
})
.forEach(function (shape) {
const option = document.createElement('option');
const sightings = shapeTypes[shape];
option.text = `${shape} (${sightings} sighting${
sightings === 1 ? '' : 's'
})`;
option.value = shape;
shapeSelect.appendChild(option);
});
}

const client = new XMLHttpRequest();
client.open('GET', 'data/csv/ufo_sighting_data.csv');
client.addEventListener('load', function () {
const csv = client.responseText;
// key is shape name, value is sightings count
const shapeTypes = {};
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.substring(prevIndex, curIndex).split(',');
prevIndex = curIndex + 1;

const coords = [parseFloat(line[5]), parseFloat(line[4])];
const shape = line[2];
shapeTypes[shape] = (shapeTypes[shape] || 0) + 1;

features.push(
new Feature({
datetime: line[0],
year: parseInt(/[0-9]{4}/.exec(line[0])[0], 10), // extract the year as int
shape: shape,
duration: line[3],
geometry: new Point(fromLonLat(coords)),
})
);
}
shapeTypes['all'] = features.length;
map.addLayer(
new WebGLPointsLayer({
source: new VectorSource({
features: features,
attributions: 'National UFO Reporting Center',
}),
style: style,
})
);
fillShapeSelect(shapeTypes);
});
client.send();

const info = document.getElementById('info');
map.on('pointermove', function (evt) {
if (map.getView().getInteracting() || map.getView().getAnimating()) {
return;
}
const text = map.forEachFeatureAtPixel(evt.pixel, function (feature) {
const datetime = feature.get('datetime');
const duration = feature.get('duration');
const shape = feature.get('shape');
return `On ${datetime}, lasted ${duration} seconds and had a "${shape}" shape.`;
});
info.innerText = text || '';
});

界面布局文件index.html代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Icon Sprites 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>
<div>Current sighting: <span id="info"></span></div>
<div>
<label for="shape-filter">Filter by UFO shape:</label>
<select id="shape-filter"></select>
</div>
<!-- Pointer events polyfill for old browsers, see https://caniuse.com/#feat=pointer -->
<script src="https://cdn.jsdelivr.net/npm/elm-pep@1.0.6/dist/elm-pep.js"></script>
<script type="module" src="main.js"></script>
</body>
</html>