原文链接及内容

运行界面

OSM XML矢量数据使用bbox策略从Overpass API动态加载。注意,平移和缩放最终会导致来自Overpass API的“Too many requests”错误。

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
import Map from 'ol/Map.js';
import OSMXML from 'ol/format/OSMXML.js';
import VectorSource from 'ol/source/Vector.js';
import View from 'ol/View.js';
import XYZ from 'ol/source/XYZ.js';
import {Circle as CircleStyle, Fill, Stroke, Style} from 'ol/style.js';
import {Tile as TileLayer, Vector as VectorLayer} from 'ol/layer.js';
import {bbox as bboxStrategy} from 'ol/loadingstrategy.js';
import {transformExtent} from 'ol/proj.js';

let map = null;

const styles = {
'amenity': {
'parking': new Style({
stroke: new Stroke({
color: 'rgba(170, 170, 170, 1.0)',
width: 1,
}),
fill: new Fill({
color: 'rgba(170, 170, 170, 0.3)',
}),
}),
},
'building': {
'.*': new Style({
zIndex: 100,
stroke: new Stroke({
color: 'rgba(246, 99, 79, 1.0)',
width: 1,
}),
fill: new Fill({
color: 'rgba(246, 99, 79, 0.3)',
}),
}),
},
'highway': {
'service': new Style({
stroke: new Stroke({
color: 'rgba(255, 255, 255, 1.0)',
width: 2,
}),
}),
'.*': new Style({
stroke: new Stroke({
color: 'rgba(255, 255, 255, 1.0)',
width: 3,
}),
}),
},
'landuse': {
'forest|grass|allotments': new Style({
stroke: new Stroke({
color: 'rgba(140, 208, 95, 1.0)',
width: 1,
}),
fill: new Fill({
color: 'rgba(140, 208, 95, 0.3)',
}),
}),
},
'natural': {
'tree': new Style({
image: new CircleStyle({
radius: 2,
fill: new Fill({
color: 'rgba(140, 208, 95, 1.0)',
}),
stroke: null,
}),
}),
},
};

const vectorSource = new VectorSource({
format: new OSMXML(),
loader: function (extent, resolution, projection, success, failure) {
const epsg4326Extent = transformExtent(extent, projection, 'EPSG:4326');
const client = new XMLHttpRequest();
client.open('POST', 'https://overpass-api.de/api/interpreter');
client.addEventListener('load', function () {
const features = new OSMXML().readFeatures(client.responseText, {
featureProjection: map.getView().getProjection(),
});
vectorSource.addFeatures(features);
success(features);
});
client.addEventListener('error', failure);
const query =
'(node(' +
epsg4326Extent[1] +
',' +
Math.max(epsg4326Extent[0], -180) +
',' +
epsg4326Extent[3] +
',' +
Math.min(epsg4326Extent[2], 180) +
');rel(bn)->.foo;way(bn);node(w)->.foo;rel(bw););out meta;';
client.send(query);
},
strategy: bboxStrategy,
});

const vector = new VectorLayer({
source: vectorSource,
style: function (feature) {
for (const key in styles) {
const value = feature.get(key);
if (value !== undefined) {
for (const regexp in styles[key]) {
if (new RegExp(regexp).test(value)) {
return styles[key][regexp];
}
}
}
}
return null;
},
});

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 raster = new TileLayer({
source: new XYZ({
attributions: attributions,
url: 'https://api.maptiler.com/tiles/satellite/{z}/{x}/{y}.jpg?key=' + key,
maxZoom: 20,
}),
});

map = new Map({
layers: [raster, vector],
target: document.getElementById('map'),
view: new View({
center: [739218, 5906096],
maxZoom: 19,
zoom: 17,
}),
});

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>OSM XML</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>
<!-- 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>