原文链接及内容

运行界面

居住地数据集有一个pop_max字段,用于渲染每个地点的样式。interpolate(插值)运算符用于缩放圆的半径。对于人口低于50万的地点,绘制半径为3的点;对于人口1000万或更多的地点,绘制半径为10的点。相同的插值运算符用于基于pop_max字段为地点生成颜色表。

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
import GeoJSON from 'ol/format/GeoJSON.js';
import Link from 'ol/interaction/Link.js';
import Map from 'ol/Map.js';
import TileLayer from 'ol/layer/Tile.js';
import VectorLayer from 'ol/layer/Vector.js';
import VectorSource from 'ol/source/Vector.js';
import View from 'ol/View.js';
import XYZ from 'ol/source/XYZ.js';

const populatedPlaces = new VectorLayer({
source: new VectorSource({
url: 'https://openlayers.org/data/vector/populated-places.json',
format: new GeoJSON(),
}),
style: {
'circle-stroke-color': 'hsl(0 100% 100% / 0.9)',
'circle-stroke-width': 0.75,
'circle-radius': [
'interpolate',
['linear'],
['get', 'pop_max'],
500_000,
3,
10_000_000,
10,
],
'circle-fill-color': [
'interpolate',
['linear'],
['get', 'pop_max'],
1_000_000,
'hsl(210 100% 40% / 0.9)',
10_000_000,
'hsl(0 80% 60% / 0.9)',
],
},
});

const key = 'Get your own API key at https://www.maptiler.com/cloud/';

const map = new Map({
layers: [
new TileLayer({
source: new XYZ({
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>',
url:
'https://api.maptiler.com/tiles/satellite/{z}/{x}/{y}.jpg?key=' + key,
maxZoom: 20,
}),
}),
populatedPlaces,
],
target: 'map',
view: new View({
center: [0, 0],
zoom: 1,
}),
});

map.addInteraction(new Link());

const info = document.getElementById('info');

let currentFeature = null;
function displayFeatureInfo(pixel, width) {
const feature = map.getFeaturesAtPixel(pixel)[0];
if (feature) {
const featurePixel = map.getPixelFromCoordinate(
feature.getGeometry().getCoordinates()
);
if (featurePixel[0] > width) {
featurePixel[0] = featurePixel[0] % width;
} else if (featurePixel[1] < width) {
featurePixel[0] = width + (featurePixel[0] % width);
}
info.style.top = featurePixel[1] + 'px';
if (featurePixel[0] < width / 2) {
info.style.left = featurePixel[0] + 'px';
info.style.right = 'auto';
} else {
info.style.right = width - featurePixel[0] + 'px';
info.style.left = 'auto';
}
if (feature !== currentFeature) {
info.style.visibility = 'visible';
info.innerHTML =
feature.get('name') + '<br>' + feature.get('pop_max').toLocaleString();
}
} else if (currentFeature) {
info.style.visibility = 'hidden';
}
currentFeature = feature;
}

map.on('pointermove', function (evt) {
if (evt.dragging) {
info.style.visibility = 'hidden';
currentFeature = undefined;
return;
}
const pixel = map.getEventPixel(evt.originalEvent);
displayFeatureInfo(pixel, evt.frameState.size[0]);
});

map.on('click', function (evt) {
displayFeatureInfo(evt.pixel, evt.frameState.size[0]);
});

map.getTargetElement().addEventListener('pointerleave', function () {
currentFeature = undefined;
info.style.visibility = 'hidden';
});

界面布局文件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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Populated Places</title>
<link rel="stylesheet" href="node_modules/ol/ol.css">
<style>
.map {
width: 100%;
height: 400px;
}
#map {
position: relative;
}

#info {
position: absolute;
display: inline-block;
height: auto;
width: auto;
z-index: 100;
background-color: #333;
color: #fff;
text-align: center;
border-radius: 4px;
padding: 5px;
left: 50%;
transform: translateX(3%);
visibility: hidden;
pointer-events: none;
}
</style>
</head>
<body>
<div id="map" class="map">
<div id="info">&nbsp;</div>
</div>

<script type="module" src="main.js"></script>
</body>
</html>