osm2geojson-lite 2.0.1 → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +33 -16
- package/dist/cli.mjs +1474 -6
- package/dist/index.cjs +1469 -0
- package/dist/index.d.ts +28 -5
- package/dist/index.mjs +1274 -729
- package/dist/polytags.json +24 -45
- package/package.json +20 -10
- package/dist/index.js +0 -924
package/dist/index.mjs
CHANGED
|
@@ -1,8 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
var __esmMin = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
|
|
3
|
-
var __commonJSMin = (cb, mod) => () => (mod || (cb((mod = { exports: {} }).exports, mod), cb = null), mod.exports);
|
|
4
|
-
//#endregion
|
|
5
|
-
//#region src/utils.ts
|
|
1
|
+
// src/utils.ts
|
|
6
2
|
function purgeProps(obj, blacklist) {
|
|
7
3
|
if (obj) {
|
|
8
4
|
const rs = Object.assign({}, obj);
|
|
@@ -37,11 +33,56 @@ function getFirstFromMap(m, k) {
|
|
|
37
33
|
return null;
|
|
38
34
|
}
|
|
39
35
|
function isRing(a) {
|
|
40
|
-
|
|
36
|
+
if (a.length <= 3) return false;
|
|
37
|
+
const firstPoint = a[0];
|
|
38
|
+
const lastPoint = a[a.length - 1];
|
|
39
|
+
return firstPoint[0] === lastPoint[0] && firstPoint[1] === lastPoint[1];
|
|
41
40
|
}
|
|
41
|
+
/**
|
|
42
|
+
* Build the closed rectangle used by Overpass `out bb` responses when an
|
|
43
|
+
* element carries only a bounding box.
|
|
44
|
+
* @param bounds `[minlon, minlat, maxlon, maxlat]`
|
|
45
|
+
*/
|
|
46
|
+
function boundsToRing(bounds) {
|
|
47
|
+
const [minLon, minLat, maxLon, maxLat] = bounds;
|
|
48
|
+
return [
|
|
49
|
+
[minLon, minLat],
|
|
50
|
+
[maxLon, minLat],
|
|
51
|
+
[maxLon, maxLat],
|
|
52
|
+
[minLon, maxLat],
|
|
53
|
+
[minLon, minLat]
|
|
54
|
+
];
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Bounds produced by `ringDirection` while it already scans the ring. Polygon
|
|
58
|
+
* assembly needs both, so reusing that pass avoids a second full scan on
|
|
59
|
+
* relation-heavy data.
|
|
60
|
+
*/
|
|
61
|
+
const ringBoundsCache = /* @__PURE__ */ new WeakMap();
|
|
42
62
|
function ringDirection(a, xIdx, yIdx) {
|
|
43
63
|
xIdx = xIdx || 0, yIdx = yIdx || 1;
|
|
44
|
-
|
|
64
|
+
let m = 0;
|
|
65
|
+
let maxX = a[0][xIdx];
|
|
66
|
+
let minX = maxX;
|
|
67
|
+
let minY = a[0][yIdx];
|
|
68
|
+
let maxY = minY;
|
|
69
|
+
for (let idx = 1; idx < a.length; idx++) {
|
|
70
|
+
const point = a[idx];
|
|
71
|
+
const x = point[xIdx];
|
|
72
|
+
const y = point[yIdx];
|
|
73
|
+
if (x >= maxX) {
|
|
74
|
+
maxX = x;
|
|
75
|
+
m = idx;
|
|
76
|
+
} else if (x < minX) minX = x;
|
|
77
|
+
if (y < minY) minY = y;
|
|
78
|
+
else if (y > maxY) maxY = y;
|
|
79
|
+
}
|
|
80
|
+
if (xIdx === 0 && yIdx === 1) ringBoundsCache.set(a, {
|
|
81
|
+
minX,
|
|
82
|
+
maxX,
|
|
83
|
+
minY,
|
|
84
|
+
maxY
|
|
85
|
+
});
|
|
45
86
|
const l = m <= 0 ? a.length - 2 : m - 1;
|
|
46
87
|
const r = m >= a.length - 1 ? 1 : m + 1;
|
|
47
88
|
const xa = a[l][xIdx];
|
|
@@ -52,242 +93,152 @@ function ringDirection(a, xIdx, yIdx) {
|
|
|
52
93
|
const yc = a[r][yIdx];
|
|
53
94
|
return (xb - xa) * (yc - ya) - (xc - xa) * (yb - ya) < 0 ? "clockwise" : "counterclockwise";
|
|
54
95
|
}
|
|
96
|
+
function ringBounds(ring) {
|
|
97
|
+
const cached = ringBoundsCache.get(ring);
|
|
98
|
+
if (cached) return cached;
|
|
99
|
+
let minX = Infinity, maxX = -Infinity, minY = Infinity, maxY = -Infinity;
|
|
100
|
+
for (let i = 0; i < ring.length; i++) {
|
|
101
|
+
const point = ring[i];
|
|
102
|
+
const x = point[0];
|
|
103
|
+
const y = point[1];
|
|
104
|
+
if (x < minX) minX = x;
|
|
105
|
+
if (x > maxX) maxX = x;
|
|
106
|
+
if (y < minY) minY = y;
|
|
107
|
+
if (y > maxY) maxY = y;
|
|
108
|
+
}
|
|
109
|
+
const bounds = {
|
|
110
|
+
minX,
|
|
111
|
+
maxX,
|
|
112
|
+
minY,
|
|
113
|
+
maxY
|
|
114
|
+
};
|
|
115
|
+
ringBoundsCache.set(ring, bounds);
|
|
116
|
+
return bounds;
|
|
117
|
+
}
|
|
55
118
|
function pointInsidePolygon(pt, polygon, xIdx, yIdx) {
|
|
56
119
|
xIdx = xIdx || 0, yIdx = yIdx || 1;
|
|
120
|
+
const px = pt[xIdx];
|
|
121
|
+
const py = pt[yIdx];
|
|
57
122
|
let result = false;
|
|
58
|
-
|
|
123
|
+
let xj = polygon[polygon.length - 1][xIdx];
|
|
124
|
+
let yj = polygon[polygon.length - 1][yIdx];
|
|
125
|
+
for (let i = 0; i < polygon.length; i++) {
|
|
126
|
+
const point = polygon[i];
|
|
127
|
+
const xi = point[xIdx];
|
|
128
|
+
const yi = point[yIdx];
|
|
129
|
+
if ((xi <= px && px < xj || xj <= px && px < xi) && py < (yj - yi) * (px - xi) / (xj - xi) + yi) result = !result;
|
|
130
|
+
xj = xi;
|
|
131
|
+
yj = yi;
|
|
132
|
+
}
|
|
59
133
|
return result;
|
|
60
134
|
}
|
|
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
|
-
let attMatch = attRegEx1.exec(attrText);
|
|
114
|
-
let hasAttrs = false;
|
|
115
|
-
while (attMatch) {
|
|
116
|
-
hasAttrs = true;
|
|
117
|
-
node[attMatch[1]] = attMatch[2];
|
|
118
|
-
attMatch = attRegEx1.exec(attrText);
|
|
119
|
-
}
|
|
120
|
-
if (!hasAttrs) {
|
|
121
|
-
attMatch = attRegEx2.exec(attrText);
|
|
122
|
-
while (attMatch) {
|
|
123
|
-
hasAttrs = true;
|
|
124
|
-
node[attMatch[1]] = attMatch[2];
|
|
125
|
-
attMatch = attRegEx2.exec(attrText);
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
if (!hasAttrs && attrText !== "") node.text = attrText;
|
|
129
|
-
if (this.progressive) this.emit(`<${fullTag}>`, node, parent);
|
|
130
|
-
if (!closed) {
|
|
131
|
-
const innerRegEx = new RegExp(`([^]+?)<\/${tag}>`, "g");
|
|
132
|
-
innerRegEx.lastIndex = nodeRegEx.lastIndex;
|
|
133
|
-
const innerMatch = innerRegEx.exec(xml);
|
|
134
|
-
if (innerMatch && innerMatch[1]) {
|
|
135
|
-
nodeRegEx.lastIndex = innerRegEx.lastIndex;
|
|
136
|
-
const innerNodes = this.parse(innerMatch[1], node, fullTag);
|
|
137
|
-
if (innerNodes.length > 0) node.$innerNodes = innerNodes;
|
|
138
|
-
else node.$innerText = innerMatch[1];
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
if (this.queryParent && parent) this.parentMap.set(node, parent);
|
|
142
|
-
if (this.progressive) this.emit(`</${fullTag}>`, node, parent);
|
|
143
|
-
nodes.push(node);
|
|
144
|
-
nodeMatch = nodeRegEx.exec(xml);
|
|
145
|
-
}
|
|
146
|
-
return nodes;
|
|
147
|
-
}
|
|
148
|
-
getParent(node) {
|
|
149
|
-
if (this.queryParent) return this.parentMap.get(node);
|
|
150
|
-
return null;
|
|
151
|
-
}
|
|
152
|
-
addListener(evt, func) {
|
|
153
|
-
if (conditioned(evt)) {
|
|
154
|
-
const ev = parseEvent(evt);
|
|
155
|
-
if (ev.exp) func.condition = genConditionFunc(ev.exp);
|
|
156
|
-
evt = ev.evt;
|
|
135
|
+
// src/osm-object.ts
|
|
136
|
+
var OsmObject = class {
|
|
137
|
+
constructor(type, id, refElems, register = true) {
|
|
138
|
+
this.type = type;
|
|
139
|
+
this.id = id;
|
|
140
|
+
this.refElems = refElems;
|
|
141
|
+
this.tags = {};
|
|
142
|
+
this.props = { id: this.getCompositeId() };
|
|
143
|
+
this.refCount = 0;
|
|
144
|
+
this.hasTag = false;
|
|
145
|
+
this.tainted = false;
|
|
146
|
+
if (refElems && register) refElems.add(this.getCompositeId(), this);
|
|
147
|
+
}
|
|
148
|
+
addTags(tags) {
|
|
149
|
+
this.tags = Object.assign(this.tags, tags);
|
|
150
|
+
this.hasTag = true;
|
|
151
|
+
}
|
|
152
|
+
addTag(k, v) {
|
|
153
|
+
this.tags[k] = v;
|
|
154
|
+
this.hasTag = true;
|
|
155
|
+
}
|
|
156
|
+
addProp(k, v) {
|
|
157
|
+
this.props[k] = v;
|
|
158
|
+
}
|
|
159
|
+
addProps(props) {
|
|
160
|
+
this.props = Object.assign(this.props, props);
|
|
161
|
+
}
|
|
162
|
+
getCompositeId() {
|
|
163
|
+
return `${this.type}/${this.id}`;
|
|
164
|
+
}
|
|
165
|
+
getProps() {
|
|
166
|
+
const props = Object.assign({}, this.props, this.tags);
|
|
167
|
+
if (this.tainted && this.refElems && this.refElems.reportTainted) props["@tainted"] = true;
|
|
168
|
+
return props;
|
|
169
|
+
}
|
|
170
|
+
};
|
|
171
|
+
// src/node.ts
|
|
172
|
+
var Node = class extends OsmObject {
|
|
173
|
+
constructor(id, refElems, register = true) {
|
|
174
|
+
super("node", id, refElems, register);
|
|
175
|
+
}
|
|
176
|
+
setLatLng(latLng) {
|
|
177
|
+
this.latLng = latLng;
|
|
178
|
+
}
|
|
179
|
+
toFeatureArray() {
|
|
180
|
+
if (this.latLng) return [{
|
|
181
|
+
type: "Feature",
|
|
182
|
+
id: this.getCompositeId(),
|
|
183
|
+
properties: this.getProps(),
|
|
184
|
+
geometry: {
|
|
185
|
+
type: "Point",
|
|
186
|
+
coordinates: [+this.latLng.lon, +this.latLng.lat]
|
|
157
187
|
}
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
this.hasTag = true;
|
|
207
|
-
}
|
|
208
|
-
addTag(k, v) {
|
|
209
|
-
this.tags[k] = v;
|
|
210
|
-
this.hasTag = true;
|
|
211
|
-
}
|
|
212
|
-
addProp(k, v) {
|
|
213
|
-
this.props[k] = v;
|
|
214
|
-
}
|
|
215
|
-
addProps(props) {
|
|
216
|
-
this.props = Object.assign(this.props, props);
|
|
217
|
-
}
|
|
218
|
-
getCompositeId() {
|
|
219
|
-
return `${this.type}/${this.id}`;
|
|
220
|
-
}
|
|
221
|
-
getProps() {
|
|
222
|
-
return Object.assign(this.props, this.tags);
|
|
223
|
-
}
|
|
224
|
-
};
|
|
225
|
-
}));
|
|
226
|
-
//#endregion
|
|
227
|
-
//#region src/node.ts
|
|
228
|
-
var Node;
|
|
229
|
-
var init_node = __esmMin((() => {
|
|
230
|
-
init_osm_object();
|
|
231
|
-
init_utils();
|
|
232
|
-
Node = class extends OsmObject {
|
|
233
|
-
constructor(id, refElems) {
|
|
234
|
-
super("node", id, refElems);
|
|
235
|
-
}
|
|
236
|
-
setLatLng(latLng) {
|
|
237
|
-
this.latLng = latLng;
|
|
238
|
-
}
|
|
239
|
-
toFeatureArray() {
|
|
240
|
-
if (this.latLng) return [{
|
|
241
|
-
type: "Feature",
|
|
242
|
-
id: this.getCompositeId(),
|
|
243
|
-
properties: this.getProps(),
|
|
244
|
-
geometry: {
|
|
245
|
-
type: "Point",
|
|
246
|
-
coordinates: strArrayToFloat([this.latLng.lon, this.latLng.lat])
|
|
247
|
-
}
|
|
248
|
-
}];
|
|
249
|
-
return [];
|
|
250
|
-
}
|
|
251
|
-
getLatLng() {
|
|
252
|
-
return this.latLng;
|
|
253
|
-
}
|
|
254
|
-
};
|
|
255
|
-
}));
|
|
256
|
-
//#endregion
|
|
257
|
-
//#region src/late-binder.ts
|
|
258
|
-
var LateBinder;
|
|
259
|
-
var init_late_binder = __esmMin((() => {
|
|
260
|
-
LateBinder = class {
|
|
261
|
-
constructor(container, valueFunc, ctx, args) {
|
|
262
|
-
this.container = container;
|
|
263
|
-
this.valueFunc = valueFunc;
|
|
264
|
-
this.ctx = ctx;
|
|
265
|
-
this.args = args;
|
|
266
|
-
}
|
|
267
|
-
bind() {
|
|
268
|
-
const v = this.valueFunc.apply(this.ctx, this.args);
|
|
269
|
-
const idx = this.container.indexOf(this);
|
|
270
|
-
if (idx < 0) return;
|
|
271
|
-
const args = [idx, 1];
|
|
272
|
-
if (v) args.push(v);
|
|
273
|
-
Array.prototype.splice.apply(this.container, args);
|
|
274
|
-
}
|
|
275
|
-
};
|
|
276
|
-
}));
|
|
277
|
-
//#endregion
|
|
278
|
-
//#region src/polytags.json
|
|
279
|
-
init_xmlparser();
|
|
280
|
-
init_node();
|
|
281
|
-
init_late_binder();
|
|
282
|
-
var polytags_default = {
|
|
283
|
-
building: {},
|
|
284
|
-
highway: { "whitelist": [
|
|
188
|
+
}];
|
|
189
|
+
return [];
|
|
190
|
+
}
|
|
191
|
+
getLatLng() {
|
|
192
|
+
return this.latLng;
|
|
193
|
+
}
|
|
194
|
+
};
|
|
195
|
+
// src/output.ts
|
|
196
|
+
/**
|
|
197
|
+
* A pre-built geometry carried by Overpass derived responses (`out geom`,
|
|
198
|
+
* `convert`, `make`, `local`) or by equivalent XML output.
|
|
199
|
+
*/
|
|
200
|
+
var Output = class extends OsmObject {
|
|
201
|
+
constructor(type, id, refElems) {
|
|
202
|
+
super(type, id, refElems);
|
|
203
|
+
}
|
|
204
|
+
setGeometry(geometry) {
|
|
205
|
+
this.geometry = geometry;
|
|
206
|
+
}
|
|
207
|
+
toFeatureArray() {
|
|
208
|
+
if (!this.geometry) return [];
|
|
209
|
+
return [{
|
|
210
|
+
type: "Feature",
|
|
211
|
+
id: this.getCompositeId(),
|
|
212
|
+
properties: this.getProps(),
|
|
213
|
+
geometry: this.geometry
|
|
214
|
+
}];
|
|
215
|
+
}
|
|
216
|
+
};
|
|
217
|
+
// src/late-binder.ts
|
|
218
|
+
var LateBinder = class {
|
|
219
|
+
constructor(container, valueFunc, ctx, args) {
|
|
220
|
+
this.container = container;
|
|
221
|
+
this.valueFunc = valueFunc;
|
|
222
|
+
this.ctx = ctx;
|
|
223
|
+
this.args = args;
|
|
224
|
+
}
|
|
225
|
+
resolve() {
|
|
226
|
+
return this.valueFunc.apply(this.ctx, this.args);
|
|
227
|
+
}
|
|
228
|
+
getContainer() {
|
|
229
|
+
return this.container;
|
|
230
|
+
}
|
|
231
|
+
};
|
|
232
|
+
// src/way.ts
|
|
233
|
+
const defaultPolygonRules = {
|
|
234
|
+
building: { "ignore": ["no"] },
|
|
235
|
+
highway: { "include": [
|
|
285
236
|
"services",
|
|
286
237
|
"rest_area",
|
|
287
238
|
"escape",
|
|
288
239
|
"elevator"
|
|
289
240
|
] },
|
|
290
|
-
natural: { "
|
|
241
|
+
natural: { "exclude": [
|
|
291
242
|
"coastline",
|
|
292
243
|
"cliff",
|
|
293
244
|
"ridge",
|
|
@@ -295,7 +246,7 @@ var polytags_default = {
|
|
|
295
246
|
"tree_row"
|
|
296
247
|
] },
|
|
297
248
|
landuse: {},
|
|
298
|
-
waterway: { "
|
|
249
|
+
waterway: { "include": [
|
|
299
250
|
"riverbank",
|
|
300
251
|
"dock",
|
|
301
252
|
"boatyard",
|
|
@@ -303,7 +254,7 @@ var polytags_default = {
|
|
|
303
254
|
] },
|
|
304
255
|
amenity: {},
|
|
305
256
|
leisure: {},
|
|
306
|
-
barrier: { "
|
|
257
|
+
barrier: { "include": [
|
|
307
258
|
"city_wall",
|
|
308
259
|
"ditch",
|
|
309
260
|
"hedge",
|
|
@@ -311,481 +262,1124 @@ var polytags_default = {
|
|
|
311
262
|
"wall",
|
|
312
263
|
"spikes"
|
|
313
264
|
] },
|
|
314
|
-
railway: { "
|
|
265
|
+
railway: { "include": [
|
|
315
266
|
"station",
|
|
316
267
|
"turntable",
|
|
317
268
|
"roundhouse",
|
|
318
269
|
"platform"
|
|
319
270
|
] },
|
|
320
|
-
area: {},
|
|
321
271
|
boundary: {},
|
|
322
|
-
man_made: { "
|
|
272
|
+
man_made: { "exclude": [
|
|
323
273
|
"cutline",
|
|
324
274
|
"embankment",
|
|
325
275
|
"pipeline"
|
|
326
276
|
] },
|
|
327
|
-
power: { "
|
|
277
|
+
power: { "include": [
|
|
328
278
|
"plant",
|
|
329
279
|
"substation",
|
|
330
280
|
"generator",
|
|
331
281
|
"transformer"
|
|
332
282
|
] },
|
|
283
|
+
"piste:type": { "include": ["downhill"] },
|
|
333
284
|
place: {},
|
|
334
285
|
shop: {},
|
|
335
|
-
aeroway: { "
|
|
286
|
+
aeroway: { "exclude": ["taxiway"] },
|
|
336
287
|
tourism: {},
|
|
337
288
|
historic: {},
|
|
338
289
|
public_transport: {},
|
|
339
290
|
office: {},
|
|
340
|
-
"building:part": {},
|
|
291
|
+
"building:part": { "ignore": ["no"] },
|
|
341
292
|
military: {},
|
|
342
293
|
ruins: {},
|
|
343
294
|
"area:highway": {},
|
|
344
295
|
craft: {},
|
|
345
296
|
golf: {},
|
|
346
|
-
indoor: {}
|
|
297
|
+
indoor: { "ignore": ["yes", "no"] },
|
|
298
|
+
area: { "exclude": ["no"] }
|
|
347
299
|
};
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
300
|
+
var Way = class extends OsmObject {
|
|
301
|
+
constructor(id, refElems, register = true) {
|
|
302
|
+
super("way", id, refElems, register);
|
|
303
|
+
this.latLngArray = [];
|
|
304
|
+
this.center = null;
|
|
305
|
+
this.bounds = null;
|
|
306
|
+
}
|
|
307
|
+
addLatLng(latLng) {
|
|
308
|
+
this.latLngArray.push(latLng);
|
|
309
|
+
}
|
|
310
|
+
setLatLngArray(latLngArray) {
|
|
311
|
+
this.latLngArray = latLngArray;
|
|
312
|
+
}
|
|
313
|
+
setCenter(center) {
|
|
314
|
+
this.center = center;
|
|
315
|
+
}
|
|
316
|
+
setBounds(bounds) {
|
|
317
|
+
this.bounds = bounds;
|
|
318
|
+
}
|
|
319
|
+
getBounds() {
|
|
320
|
+
return this.bounds;
|
|
321
|
+
}
|
|
322
|
+
addNodeRef(ref) {
|
|
323
|
+
const binder = new LateBinder(this.latLngArray, (id) => {
|
|
324
|
+
const node = this.refElems.get(`node/${id}`);
|
|
325
|
+
if (node) {
|
|
326
|
+
node.refCount++;
|
|
327
|
+
return node.getLatLng();
|
|
328
|
+
}
|
|
329
|
+
this.tainted = true;
|
|
330
|
+
}, this, [ref]);
|
|
331
|
+
this.latLngArray.push(binder);
|
|
332
|
+
this.refElems.addBinder(binder);
|
|
333
|
+
}
|
|
334
|
+
/**
|
|
335
|
+
* Coordinates are normalised to numbers in a single pass here. Previously
|
|
336
|
+
* the strings were joined for map keys and parsed again during output, so
|
|
337
|
+
* every coordinate was converted twice and several intermediate arrays
|
|
338
|
+
* were allocated.
|
|
339
|
+
*/
|
|
340
|
+
toCoordsArray() {
|
|
341
|
+
const source = this.latLngArray;
|
|
342
|
+
const coords = new Array(source.length);
|
|
343
|
+
for (let i = 0; i < source.length; i++) {
|
|
344
|
+
const latLng = source[i];
|
|
345
|
+
coords[i] = [+latLng.lon, +latLng.lat];
|
|
346
|
+
}
|
|
347
|
+
return coords;
|
|
348
|
+
}
|
|
349
|
+
toFeatureArray() {
|
|
350
|
+
const coordsArray = this.toCoordsArray();
|
|
351
|
+
if (coordsArray.length > 1) {
|
|
352
|
+
const feature = {
|
|
353
|
+
type: "Feature",
|
|
354
|
+
id: this.getCompositeId(),
|
|
355
|
+
properties: this.getProps(),
|
|
356
|
+
geometry: {
|
|
357
|
+
type: "LineString",
|
|
358
|
+
coordinates: coordsArray
|
|
372
359
|
}
|
|
373
|
-
}
|
|
374
|
-
this.
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
for (const [k, v] of Object.entries(tags)) this.analyzeTag(k, v);
|
|
380
|
-
}
|
|
381
|
-
addTag(k, v) {
|
|
382
|
-
super.addTag(k, v);
|
|
383
|
-
this.analyzeTag(k, v);
|
|
384
|
-
}
|
|
385
|
-
toCoordsArray() {
|
|
386
|
-
return this.latLngArray.map((latLng) => [latLng.lon, latLng.lat]);
|
|
387
|
-
}
|
|
388
|
-
toFeatureArray() {
|
|
389
|
-
let coordsArrayString = this.toCoordsArray();
|
|
390
|
-
if (coordsArrayString.length > 1) {
|
|
391
|
-
const coordsArray = strArrayArrayToFloat(coordsArrayString);
|
|
392
|
-
const feature = {
|
|
393
|
-
type: "Feature",
|
|
394
|
-
id: this.getCompositeId(),
|
|
395
|
-
properties: this.getProps(),
|
|
396
|
-
geometry: {
|
|
397
|
-
type: "LineString",
|
|
398
|
-
coordinates: coordsArray
|
|
399
|
-
}
|
|
360
|
+
};
|
|
361
|
+
if (isRing(coordsArray) && this.isPolygon()) {
|
|
362
|
+
if (ringDirection(coordsArray) !== "counterclockwise") coordsArray.reverse();
|
|
363
|
+
feature.geometry = {
|
|
364
|
+
type: "Polygon",
|
|
365
|
+
coordinates: [coordsArray]
|
|
400
366
|
};
|
|
401
|
-
if (this.isPolygon && isRing(coordsArray)) {
|
|
402
|
-
if (ringDirection(coordsArray) !== "counterclockwise") coordsArray.reverse();
|
|
403
|
-
feature.geometry = {
|
|
404
|
-
type: "Polygon",
|
|
405
|
-
coordinates: [coordsArray]
|
|
406
|
-
};
|
|
407
|
-
return [feature];
|
|
408
|
-
}
|
|
409
|
-
return [feature];
|
|
410
|
-
}
|
|
411
|
-
return [];
|
|
412
|
-
}
|
|
413
|
-
analyzeTag(k, v) {
|
|
414
|
-
const o = polytags_default[k];
|
|
415
|
-
if (o) {
|
|
416
|
-
this.isPolygon = true;
|
|
417
|
-
if (o.whitelist) this.isPolygon = o.whitelist.indexOf(v) >= 0 ? true : false;
|
|
418
|
-
else if (o.blacklist) this.isPolygon = o.blacklist.indexOf(v) >= 0 ? false : true;
|
|
419
367
|
}
|
|
368
|
+
return [feature];
|
|
420
369
|
}
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
WayCollection = class extends Array {
|
|
429
|
-
constructor() {
|
|
430
|
-
super();
|
|
431
|
-
this.firstMap = {};
|
|
432
|
-
this.lastMap = {};
|
|
433
|
-
}
|
|
434
|
-
addWay(way) {
|
|
435
|
-
const w = way.toCoordsArray();
|
|
436
|
-
if (w.length > 0) {
|
|
437
|
-
this.push(w);
|
|
438
|
-
addToMap(this.firstMap, coordsToKey(first(w)), w);
|
|
439
|
-
addToMap(this.lastMap, coordsToKey(last(w)), w);
|
|
370
|
+
if (this.bounds) return [{
|
|
371
|
+
type: "Feature",
|
|
372
|
+
id: this.getCompositeId(),
|
|
373
|
+
properties: this.getProps(),
|
|
374
|
+
geometry: {
|
|
375
|
+
type: "Polygon",
|
|
376
|
+
coordinates: [boundsToRing(this.bounds)]
|
|
440
377
|
}
|
|
441
|
-
}
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
let next;
|
|
450
|
-
do {
|
|
451
|
-
let nextWay = this.getNextWay(current);
|
|
452
|
-
next = nextWay.next;
|
|
453
|
-
let mergeType = nextWay.mergeType;
|
|
454
|
-
if (!next) continue;
|
|
455
|
-
this.splice(this.indexOf(next), 1);
|
|
456
|
-
removeFromMap(this.firstMap, coordsToKey(first(next)), next);
|
|
457
|
-
removeFromMap(this.lastMap, coordsToKey(last(next)), next);
|
|
458
|
-
switch (mergeType) {
|
|
459
|
-
case 0:
|
|
460
|
-
current = current.concat(next.slice(1));
|
|
461
|
-
break;
|
|
462
|
-
case 1:
|
|
463
|
-
next.reverse();
|
|
464
|
-
current = current.concat(next.slice(1));
|
|
465
|
-
break;
|
|
466
|
-
case 2:
|
|
467
|
-
current.reverse();
|
|
468
|
-
current = current.concat(next.slice(1));
|
|
469
|
-
break;
|
|
470
|
-
case 3:
|
|
471
|
-
current = next.concat(current.slice(1));
|
|
472
|
-
current.reverse();
|
|
473
|
-
break;
|
|
474
|
-
}
|
|
475
|
-
} while (next);
|
|
476
|
-
strings.push(strArrayArrayToFloat(current));
|
|
477
|
-
way = this.shift();
|
|
378
|
+
}];
|
|
379
|
+
if (this.center) return [{
|
|
380
|
+
type: "Feature",
|
|
381
|
+
id: this.getCompositeId(),
|
|
382
|
+
properties: this.getProps(),
|
|
383
|
+
geometry: {
|
|
384
|
+
type: "Point",
|
|
385
|
+
coordinates: [+this.center.lon, +this.center.lat]
|
|
478
386
|
}
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
387
|
+
}];
|
|
388
|
+
return [];
|
|
389
|
+
}
|
|
390
|
+
/**
|
|
391
|
+
* Polygon detection is order-independent: a closed way is an area when at
|
|
392
|
+
* least one polygon tag applies, while tags such as `building=no` or
|
|
393
|
+
* `area=no` explicitly opt out.
|
|
394
|
+
*/
|
|
395
|
+
isPolygon() {
|
|
396
|
+
const polygonRules = this.refElems && this.refElems.polygonTags || defaultPolygonRules;
|
|
397
|
+
let area = false;
|
|
398
|
+
for (const key in polygonRules) {
|
|
399
|
+
const value = this.tags[key];
|
|
400
|
+
if (!value) continue;
|
|
401
|
+
const rule = polygonRules[key];
|
|
402
|
+
if (rule.ignore && rule.ignore.indexOf(value) >= 0) continue;
|
|
403
|
+
area = true;
|
|
404
|
+
if (rule.include) area = rule.include.indexOf(value) >= 0;
|
|
405
|
+
else if (rule.exclude) area = rule.exclude.indexOf(value) < 0;
|
|
406
|
+
else if (rule.whitelist) area = rule.whitelist.indexOf(value) >= 0;
|
|
407
|
+
else if (rule.blacklist) area = rule.blacklist.indexOf(value) < 0;
|
|
408
|
+
}
|
|
409
|
+
return area;
|
|
410
|
+
}
|
|
411
|
+
};
|
|
412
|
+
// src/way-collection.ts
|
|
413
|
+
/**
|
|
414
|
+
* A growable point sequence with O(1) reverse and amortised O(1) append.
|
|
415
|
+
*
|
|
416
|
+
* `left` is stored in reverse order, so the logical sequence is
|
|
417
|
+
* `reverse(left) + right`. Reversing therefore only swaps the two arrays and
|
|
418
|
+
* appending always targets `right`. This lets the line/ring stitcher grow a
|
|
419
|
+
* path without repeatedly copying the accumulated coordinates.
|
|
420
|
+
*/
|
|
421
|
+
var PointDeque = class {
|
|
422
|
+
constructor(first, firstKey, lastKey) {
|
|
423
|
+
this.left = [];
|
|
424
|
+
this.right = first.slice();
|
|
425
|
+
this.headKey = firstKey;
|
|
426
|
+
this.tailKey = lastKey;
|
|
427
|
+
}
|
|
428
|
+
reverse() {
|
|
429
|
+
const tmp = this.left;
|
|
430
|
+
this.left = this.right;
|
|
431
|
+
this.right = tmp;
|
|
432
|
+
const key = this.headKey;
|
|
433
|
+
this.headKey = this.tailKey;
|
|
434
|
+
this.tailKey = key;
|
|
435
|
+
}
|
|
436
|
+
/** Append a segment, dropping its first point (the shared joint). */
|
|
437
|
+
append(segment, segmentLastKey) {
|
|
438
|
+
for (let i = 1; i < segment.length; i++) this.right.push(segment[i]);
|
|
439
|
+
this.tailKey = segmentLastKey;
|
|
440
|
+
}
|
|
441
|
+
flatten() {
|
|
442
|
+
const result = new Array(this.left.length + this.right.length);
|
|
443
|
+
let at = 0;
|
|
444
|
+
for (let i = this.left.length - 1; i >= 0; i--) result[at++] = this.left[i];
|
|
445
|
+
for (let i = 0; i < this.right.length; i++) result[at++] = this.right[i];
|
|
446
|
+
return result;
|
|
447
|
+
}
|
|
448
|
+
};
|
|
449
|
+
/**
|
|
450
|
+
* A collection of way coordinate arrays that can be stitched back into lines
|
|
451
|
+
* or rings.
|
|
452
|
+
*
|
|
453
|
+
* The previous implementation extended `Array` and relied on `shift()` plus
|
|
454
|
+
* `splice(indexOf(...))` while merging, which is O(n^2) for relations made of
|
|
455
|
+
* many ways. This version keeps a cursor over a fixed array and marks consumed
|
|
456
|
+
* entries, so merging is linear apart from the small per-coordinate-end maps.
|
|
457
|
+
*/
|
|
458
|
+
var WayCollection = class {
|
|
459
|
+
constructor() {
|
|
460
|
+
this.ways = [];
|
|
461
|
+
this.firstKeys = [];
|
|
462
|
+
this.lastKeys = [];
|
|
463
|
+
this.firstMap = {};
|
|
464
|
+
this.lastMap = {};
|
|
465
|
+
this.used = [];
|
|
466
|
+
this.indexByWay = /* @__PURE__ */ new Map();
|
|
467
|
+
this.head = 0;
|
|
468
|
+
}
|
|
469
|
+
addWay(way) {
|
|
470
|
+
let w = way.toCoordsArray();
|
|
471
|
+
if (w.length === 0) {
|
|
472
|
+
const bounds = way.getBounds();
|
|
473
|
+
if (bounds) w = boundsToRing(bounds);
|
|
474
|
+
}
|
|
475
|
+
if (w.length > 0) {
|
|
476
|
+
const index = this.ways.length;
|
|
477
|
+
const firstKey = coordsToKey(first(w));
|
|
478
|
+
const lastKey = coordsToKey(last(w));
|
|
479
|
+
this.ways.push(w);
|
|
480
|
+
this.firstKeys.push(firstKey);
|
|
481
|
+
this.lastKeys.push(lastKey);
|
|
482
|
+
this.used.push(false);
|
|
483
|
+
this.indexByWay.set(w, index);
|
|
484
|
+
addToMap(this.firstMap, firstKey, w);
|
|
485
|
+
addToMap(this.lastMap, lastKey, w);
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
mergeWays() {
|
|
489
|
+
const strings = [];
|
|
490
|
+
let index = this.firstUnused();
|
|
491
|
+
while (index >= 0) {
|
|
492
|
+
const way = this.ways[index];
|
|
493
|
+
this.consume(index);
|
|
494
|
+
const deque = new PointDeque(way, this.firstKeys[index], this.lastKeys[index]);
|
|
495
|
+
for (;;) {
|
|
496
|
+
const found = this.getNextWay(deque.headKey, deque.tailKey);
|
|
497
|
+
const next = found.next;
|
|
498
|
+
if (!next) break;
|
|
499
|
+
const nextIndex = this.indexByWay.get(next);
|
|
500
|
+
if (nextIndex === void 0) break;
|
|
501
|
+
this.consume(nextIndex);
|
|
502
|
+
switch (found.mergeType) {
|
|
503
|
+
case 0:
|
|
504
|
+
deque.append(next, this.lastKeys[nextIndex]);
|
|
505
|
+
break;
|
|
506
|
+
case 1:
|
|
507
|
+
next.reverse();
|
|
508
|
+
deque.append(next, this.firstKeys[nextIndex]);
|
|
509
|
+
break;
|
|
510
|
+
case 2:
|
|
511
|
+
deque.reverse();
|
|
512
|
+
deque.append(next, this.lastKeys[nextIndex]);
|
|
513
|
+
break;
|
|
514
|
+
case 3:
|
|
515
|
+
next.reverse();
|
|
516
|
+
deque.reverse();
|
|
517
|
+
deque.append(next, this.firstKeys[nextIndex]);
|
|
518
|
+
}
|
|
508
519
|
}
|
|
509
|
-
|
|
510
|
-
|
|
520
|
+
strings.push(deque.flatten());
|
|
521
|
+
index = this.firstUnused();
|
|
522
|
+
}
|
|
523
|
+
return strings;
|
|
524
|
+
}
|
|
525
|
+
firstUnused() {
|
|
526
|
+
while (this.head < this.ways.length && this.used[this.head]) this.head++;
|
|
527
|
+
return this.head < this.ways.length ? this.head : -1;
|
|
528
|
+
}
|
|
529
|
+
consume(index) {
|
|
530
|
+
this.used[index] = true;
|
|
531
|
+
const way = this.ways[index];
|
|
532
|
+
removeFromMap(this.firstMap, this.firstKeys[index], way);
|
|
533
|
+
removeFromMap(this.lastMap, this.lastKeys[index], way);
|
|
534
|
+
}
|
|
535
|
+
/**
|
|
536
|
+
* Try to find the next way to add to the current way.
|
|
537
|
+
* It first tries the next unused way in the array, and if this doesn't work,
|
|
538
|
+
* tries any other way through the endpoint maps.
|
|
539
|
+
*/
|
|
540
|
+
getNextWay(headKey, tailKey) {
|
|
541
|
+
const candidateIndex = this.firstUnused();
|
|
542
|
+
const next = candidateIndex >= 0 ? this.ways[candidateIndex] : null;
|
|
543
|
+
if (next) {
|
|
544
|
+
const nextFirstKey = this.firstKeys[candidateIndex];
|
|
545
|
+
const nextLastKey = this.lastKeys[candidateIndex];
|
|
546
|
+
if (tailKey === nextFirstKey) return {
|
|
511
547
|
next,
|
|
512
548
|
mergeType: 0
|
|
513
549
|
};
|
|
514
|
-
|
|
515
|
-
return {
|
|
550
|
+
if (tailKey === nextLastKey) return {
|
|
516
551
|
next,
|
|
517
552
|
mergeType: 1
|
|
518
553
|
};
|
|
554
|
+
if (headKey === nextFirstKey) return {
|
|
555
|
+
next,
|
|
556
|
+
mergeType: 2
|
|
557
|
+
};
|
|
558
|
+
if (headKey === nextLastKey) return {
|
|
559
|
+
next,
|
|
560
|
+
mergeType: 3
|
|
561
|
+
};
|
|
519
562
|
}
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
563
|
+
const fromFirst = getFirstFromMap(this.firstMap, tailKey);
|
|
564
|
+
if (fromFirst) return {
|
|
565
|
+
next: fromFirst,
|
|
566
|
+
mergeType: 0
|
|
567
|
+
};
|
|
568
|
+
return {
|
|
569
|
+
next: getFirstFromMap(this.lastMap, tailKey),
|
|
570
|
+
mergeType: 1
|
|
571
|
+
};
|
|
572
|
+
}
|
|
573
|
+
toRings(direction) {
|
|
574
|
+
const strings = this.mergeWays();
|
|
575
|
+
const rings = [];
|
|
576
|
+
for (let i = 0; i < strings.length; i++) {
|
|
577
|
+
const str = strings[i];
|
|
578
|
+
if (isRing(str)) {
|
|
579
|
+
if (ringDirection(str) !== direction) str.reverse();
|
|
580
|
+
rings.push(str);
|
|
530
581
|
}
|
|
531
|
-
return rings;
|
|
532
582
|
}
|
|
533
|
-
|
|
534
|
-
}
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
var Relation
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
583
|
+
return rings;
|
|
584
|
+
}
|
|
585
|
+
};
|
|
586
|
+
// src/relation.ts
|
|
587
|
+
var Relation = class Relation extends OsmObject {
|
|
588
|
+
constructor(id, refElems, register = true) {
|
|
589
|
+
super("relation", id, refElems, register);
|
|
590
|
+
this.relations = [];
|
|
591
|
+
this.nodes = [];
|
|
592
|
+
this.bounds = void 0;
|
|
593
|
+
this.center = null;
|
|
594
|
+
this.ways = [];
|
|
595
|
+
this.roles = [];
|
|
596
|
+
}
|
|
597
|
+
setBounds(bounds) {
|
|
598
|
+
this.bounds = bounds;
|
|
599
|
+
}
|
|
600
|
+
setCenter(center) {
|
|
601
|
+
this.center = center;
|
|
602
|
+
}
|
|
603
|
+
addMember(member) {
|
|
604
|
+
switch (member.type) {
|
|
605
|
+
case "relation":
|
|
606
|
+
let binder = new LateBinder(this.relations, (id) => {
|
|
607
|
+
const relation = this.refElems.get(`relation/${id}`);
|
|
608
|
+
if (relation) {
|
|
609
|
+
relation.refCount++;
|
|
610
|
+
return relation;
|
|
611
|
+
}
|
|
612
|
+
this.tainted = true;
|
|
613
|
+
return new Relation(id, this.refElems, false);
|
|
614
|
+
}, this, [member.ref]);
|
|
615
|
+
this.relations.push(binder);
|
|
616
|
+
this.refElems.addBinder(binder);
|
|
617
|
+
break;
|
|
618
|
+
case "way":
|
|
619
|
+
if (!member.role) member.role = "";
|
|
620
|
+
if (member.geometry) {
|
|
621
|
+
const way = new Way(member.ref, this.refElems, false);
|
|
622
|
+
way.setLatLngArray(member.geometry);
|
|
623
|
+
way.refCount++;
|
|
624
|
+
this.ways.push(way);
|
|
625
|
+
this.roles.push(member.role);
|
|
626
|
+
} else if (member.nodes) {
|
|
627
|
+
const way = new Way(member.ref, this.refElems, false);
|
|
628
|
+
for (const nid of member.nodes) way.addNodeRef(nid);
|
|
629
|
+
way.refCount++;
|
|
630
|
+
this.ways.push(way);
|
|
631
|
+
this.roles.push(member.role);
|
|
632
|
+
} else {
|
|
633
|
+
let binder = new LateBinder(this.ways, (nid) => {
|
|
634
|
+
const way = this.refElems.get(`way/${nid}`);
|
|
635
|
+
if (way) {
|
|
636
|
+
way.refCount++;
|
|
637
|
+
return way;
|
|
564
638
|
}
|
|
639
|
+
this.tainted = true;
|
|
640
|
+
return new Way(nid, this.refElems, false);
|
|
565
641
|
}, this, [member.ref]);
|
|
566
|
-
this.
|
|
642
|
+
this.ways.push(binder);
|
|
643
|
+
this.roles.push(member.role);
|
|
567
644
|
this.refElems.addBinder(binder);
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
}
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
node = new Node(member.ref, this.refElems);
|
|
600
|
-
node.setLatLng({
|
|
601
|
-
lon: member.lon,
|
|
602
|
-
lat: member.lat
|
|
603
|
-
});
|
|
604
|
-
if (member.tags) node.addTags(member.tags);
|
|
605
|
-
for (const [k, v] of Object.entries(member)) if ([
|
|
606
|
-
"id",
|
|
607
|
-
"type",
|
|
608
|
-
"lat",
|
|
609
|
-
"lon"
|
|
610
|
-
].indexOf(k) < 0) node.addProp(k, v);
|
|
611
|
-
node.refCount++;
|
|
612
|
-
this.nodes.push(node);
|
|
613
|
-
} else {
|
|
614
|
-
let binder = new LateBinder(this.nodes, (id) => {
|
|
615
|
-
const nn = this.refElems.get(`node/${id}`);
|
|
616
|
-
if (nn) {
|
|
617
|
-
nn.refCount++;
|
|
618
|
-
return nn;
|
|
619
|
-
}
|
|
620
|
-
}, this, [member.ref]);
|
|
621
|
-
this.nodes.push(binder);
|
|
622
|
-
this.refElems.addBinder(binder);
|
|
623
|
-
}
|
|
624
|
-
break;
|
|
625
|
-
}
|
|
626
|
-
}
|
|
627
|
-
constructStringGeometry(ws) {
|
|
628
|
-
const strings = ws ? ws.mergeWays() : [];
|
|
629
|
-
if (strings.length === 0) return null;
|
|
630
|
-
return {
|
|
631
|
-
type: "MultiLineString",
|
|
632
|
-
coordinates: strings
|
|
633
|
-
};
|
|
645
|
+
}
|
|
646
|
+
break;
|
|
647
|
+
case "node":
|
|
648
|
+
let node = null;
|
|
649
|
+
if (member.lat !== void 0 && member.lon !== void 0) {
|
|
650
|
+
node = new Node(member.ref, this.refElems, false);
|
|
651
|
+
node.setLatLng({
|
|
652
|
+
lon: member.lon,
|
|
653
|
+
lat: member.lat
|
|
654
|
+
});
|
|
655
|
+
if (member.tags) node.addTags(member.tags);
|
|
656
|
+
for (const [k, v] of Object.entries(member)) if ([
|
|
657
|
+
"id",
|
|
658
|
+
"type",
|
|
659
|
+
"lat",
|
|
660
|
+
"lon"
|
|
661
|
+
].indexOf(k) < 0) node.addProp(k, v);
|
|
662
|
+
node.refCount++;
|
|
663
|
+
this.nodes.push(node);
|
|
664
|
+
} else {
|
|
665
|
+
let binder = new LateBinder(this.nodes, (id) => {
|
|
666
|
+
const nn = this.refElems.get(`node/${id}`);
|
|
667
|
+
if (nn) {
|
|
668
|
+
nn.refCount++;
|
|
669
|
+
return nn;
|
|
670
|
+
}
|
|
671
|
+
this.tainted = true;
|
|
672
|
+
}, this, [member.ref]);
|
|
673
|
+
this.nodes.push(binder);
|
|
674
|
+
this.refElems.addBinder(binder);
|
|
675
|
+
}
|
|
634
676
|
}
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
677
|
+
}
|
|
678
|
+
constructStringGeometry(ws) {
|
|
679
|
+
const strings = ws ? ws.mergeWays() : [];
|
|
680
|
+
if (strings.length === 0) return null;
|
|
681
|
+
return {
|
|
682
|
+
type: "MultiLineString",
|
|
683
|
+
coordinates: strings
|
|
684
|
+
};
|
|
685
|
+
}
|
|
686
|
+
constructPolygonGeometry(ows, iws) {
|
|
687
|
+
const outerRings = ows ? ows.toRings("counterclockwise") : [];
|
|
688
|
+
const innerRings = iws ? iws.toRings("clockwise") : [];
|
|
689
|
+
if (outerRings.length > 0) {
|
|
690
|
+
const compositPolyons = [];
|
|
691
|
+
const outerBounds = [];
|
|
692
|
+
for (const ring of outerRings) compositPolyons.push([ring]);
|
|
693
|
+
if (innerRings.length > 0) for (const ring of outerRings) outerBounds.push(ringBounds(ring));
|
|
694
|
+
for (let i = 0; i < innerRings.length; i++) {
|
|
695
|
+
const ring = innerRings[i];
|
|
696
|
+
const pt = first(ring);
|
|
697
|
+
for (let idx = 0; idx < outerRings.length; idx++) {
|
|
698
|
+
const b = outerBounds[idx];
|
|
699
|
+
if (pt[0] < b.minX || pt[0] > b.maxX || pt[1] < b.minY || pt[1] > b.maxY) continue;
|
|
700
|
+
if (pointInsidePolygon(pt, outerRings[idx])) {
|
|
645
701
|
compositPolyons[idx].push(ring);
|
|
646
702
|
break;
|
|
647
703
|
}
|
|
648
|
-
ring = innerRings.shift();
|
|
649
704
|
}
|
|
650
|
-
if (compositPolyons.length === 1) return {
|
|
651
|
-
type: "Polygon",
|
|
652
|
-
coordinates: compositPolyons[0]
|
|
653
|
-
};
|
|
654
|
-
return {
|
|
655
|
-
type: "MultiPolygon",
|
|
656
|
-
coordinates: compositPolyons
|
|
657
|
-
};
|
|
658
705
|
}
|
|
659
|
-
return
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
706
|
+
if (compositPolyons.length === 1) return {
|
|
707
|
+
type: "Polygon",
|
|
708
|
+
coordinates: compositPolyons[0]
|
|
709
|
+
};
|
|
710
|
+
return {
|
|
711
|
+
type: "MultiPolygon",
|
|
712
|
+
coordinates: compositPolyons
|
|
713
|
+
};
|
|
714
|
+
}
|
|
715
|
+
return null;
|
|
716
|
+
}
|
|
717
|
+
/**
|
|
718
|
+
* Flatten a relation and its sub-relations into a single way/role list.
|
|
719
|
+
* Duplicate way references (a common cause of duplicated rings) are removed
|
|
720
|
+
* while preserving the first occurrence and its role.
|
|
721
|
+
*/
|
|
722
|
+
collectAllWaysForRelation(relation, relationToWaysMap) {
|
|
723
|
+
const cached = relationToWaysMap.get(relation.id);
|
|
724
|
+
if (cached) return cached;
|
|
725
|
+
const ways = [];
|
|
726
|
+
const roles = [];
|
|
727
|
+
const seen = /* @__PURE__ */ new Set();
|
|
728
|
+
const addDirect = (way, role) => {
|
|
729
|
+
if (!way) {
|
|
730
|
+
relation.tainted = true;
|
|
731
|
+
return;
|
|
732
|
+
}
|
|
733
|
+
const key = way.getCompositeId() + "\n" + role;
|
|
734
|
+
if (seen.has(key)) return;
|
|
735
|
+
seen.add(key);
|
|
736
|
+
ways.push(way);
|
|
737
|
+
roles.push(role);
|
|
738
|
+
};
|
|
739
|
+
for (let i = 0; i < relation.ways.length; i++) addDirect(relation.ways[i], relation.roles[i]);
|
|
740
|
+
const seenRelations = /* @__PURE__ */ new Set();
|
|
741
|
+
for (const subRelation of relation.relations) {
|
|
742
|
+
if (!subRelation) {
|
|
743
|
+
relation.tainted = true;
|
|
744
|
+
continue;
|
|
745
|
+
}
|
|
746
|
+
const id = subRelation.getCompositeId();
|
|
747
|
+
if (seenRelations.has(id)) continue;
|
|
748
|
+
seenRelations.add(id);
|
|
749
|
+
const entry = this.collectAllWaysForRelation(subRelation, relationToWaysMap);
|
|
750
|
+
for (let i = 0; i < entry.ways.length; i++) {
|
|
751
|
+
ways.push(entry.ways[i]);
|
|
752
|
+
roles.push(entry.roles[i]);
|
|
753
|
+
}
|
|
754
|
+
}
|
|
755
|
+
const result = {
|
|
756
|
+
ways,
|
|
757
|
+
roles
|
|
758
|
+
};
|
|
759
|
+
relationToWaysMap.set(relation.id, result);
|
|
760
|
+
return result;
|
|
761
|
+
}
|
|
762
|
+
toFeatureArray() {
|
|
763
|
+
const polygonFeatures = [];
|
|
764
|
+
const stringFeatures = [];
|
|
765
|
+
let pointFeatures = [];
|
|
766
|
+
const relationToWaysMap = /* @__PURE__ */ new Map();
|
|
767
|
+
const waysAndRoles = this.collectAllWaysForRelation(this, relationToWaysMap);
|
|
768
|
+
let templateFeature = {
|
|
769
|
+
type: "Feature",
|
|
770
|
+
id: this.getCompositeId(),
|
|
771
|
+
bbox: this.bounds,
|
|
772
|
+
properties: this.getProps(),
|
|
773
|
+
geometry: null
|
|
774
|
+
};
|
|
775
|
+
if (!this.bounds) delete templateFeature.bbox;
|
|
776
|
+
let hasGeometry = false;
|
|
777
|
+
if (waysAndRoles.roles.some((r) => r === "outer")) {
|
|
778
|
+
const outerWayCollection = new WayCollection();
|
|
779
|
+
const innerWayCollection = new WayCollection();
|
|
780
|
+
for (let i = 0; i < waysAndRoles.ways.length; i++) {
|
|
781
|
+
const way = waysAndRoles.ways[i];
|
|
782
|
+
const role = waysAndRoles.roles[i];
|
|
783
|
+
if (role === "outer") outerWayCollection.addWay(way);
|
|
784
|
+
else if (role === "inner") innerWayCollection.addWay(way);
|
|
785
|
+
}
|
|
786
|
+
const geometry = this.constructPolygonGeometry(outerWayCollection, innerWayCollection);
|
|
787
|
+
if (geometry) {
|
|
788
|
+
const feature = Object.assign({}, templateFeature);
|
|
789
|
+
feature.geometry = geometry;
|
|
790
|
+
polygonFeatures.push(feature);
|
|
791
|
+
hasGeometry = true;
|
|
792
|
+
}
|
|
793
|
+
} else {
|
|
794
|
+
const wayCollection = new WayCollection();
|
|
795
|
+
for (const way of waysAndRoles.ways) wayCollection.addWay(way);
|
|
796
|
+
const geometry = this.constructStringGeometry(wayCollection);
|
|
797
|
+
if (geometry) {
|
|
798
|
+
const feature = Object.assign({}, templateFeature);
|
|
799
|
+
feature.geometry = geometry;
|
|
800
|
+
stringFeatures.push(feature);
|
|
801
|
+
hasGeometry = true;
|
|
802
|
+
}
|
|
803
|
+
}
|
|
804
|
+
if (!hasGeometry && this.bounds) {
|
|
805
|
+
const feature = Object.assign({}, templateFeature);
|
|
806
|
+
feature.geometry = {
|
|
807
|
+
type: "Polygon",
|
|
808
|
+
coordinates: [boundsToRing(this.bounds)]
|
|
809
|
+
};
|
|
810
|
+
polygonFeatures.push(feature);
|
|
811
|
+
hasGeometry = true;
|
|
812
|
+
}
|
|
813
|
+
if (!hasGeometry && this.center) {
|
|
814
|
+
const feature = Object.assign({}, templateFeature);
|
|
815
|
+
feature.geometry = {
|
|
816
|
+
type: "Point",
|
|
817
|
+
coordinates: [+this.center.lon, +this.center.lat]
|
|
818
|
+
};
|
|
819
|
+
pointFeatures.push(feature);
|
|
820
|
+
}
|
|
821
|
+
for (const node of this.nodes) pointFeatures = pointFeatures.concat(node.toFeatureArray());
|
|
822
|
+
return [
|
|
823
|
+
...polygonFeatures,
|
|
824
|
+
...stringFeatures,
|
|
825
|
+
...pointFeatures
|
|
826
|
+
];
|
|
827
|
+
}
|
|
828
|
+
};
|
|
829
|
+
// src/xmlparser.ts
|
|
830
|
+
const XML_ENTITY = /&(#x[0-9a-fA-F]+|#[0-9]+|amp|lt|gt|quot|apos);/g;
|
|
831
|
+
/**
|
|
832
|
+
* Decode the XML entities that occur in OSM data. The cheap `indexOf` check
|
|
833
|
+
* keeps the common case (no entities) allocation-free.
|
|
834
|
+
*/
|
|
835
|
+
function decodeEntities(value) {
|
|
836
|
+
if (value.indexOf("&") < 0) return value;
|
|
837
|
+
return value.replace(XML_ENTITY, (match, entity) => {
|
|
838
|
+
switch (entity) {
|
|
839
|
+
case "amp": return "&";
|
|
840
|
+
case "lt": return "<";
|
|
841
|
+
case "gt": return ">";
|
|
842
|
+
case "quot": return "\"";
|
|
843
|
+
case "apos": return "'";
|
|
844
|
+
}
|
|
845
|
+
if (entity.charCodeAt(0) === 35) {
|
|
846
|
+
const code = entity.charCodeAt(1) === 120 || entity.charCodeAt(1) === 88 ? parseInt(entity.slice(2), 16) : parseInt(entity.slice(1), 10);
|
|
847
|
+
if (Number.isFinite(code) && code >= 0 && code <= 1114111) return String.fromCodePoint(code);
|
|
848
|
+
}
|
|
849
|
+
return match;
|
|
850
|
+
});
|
|
851
|
+
}
|
|
852
|
+
// src/osm-xml.ts
|
|
853
|
+
const GT = 62;
|
|
854
|
+
const SLASH = 47;
|
|
855
|
+
const BANG = 33;
|
|
856
|
+
const QUESTION = 63;
|
|
857
|
+
const EQUALS = 61;
|
|
858
|
+
const DQUOTE = 34;
|
|
859
|
+
const SQUOTE = 39;
|
|
860
|
+
const SPACE = 32;
|
|
861
|
+
const TAB = 9;
|
|
862
|
+
const LF = 10;
|
|
863
|
+
const CR = 13;
|
|
864
|
+
function isSpace(c) {
|
|
865
|
+
return c === SPACE || c === TAB || c === LF || c === CR;
|
|
866
|
+
}
|
|
867
|
+
function newDerived() {
|
|
868
|
+
return {
|
|
869
|
+
lineCoords: [],
|
|
870
|
+
geometries: []
|
|
871
|
+
};
|
|
872
|
+
}
|
|
873
|
+
function buildDerivedGeometry(derived) {
|
|
874
|
+
if (derived.lineCoords.length > 0) return {
|
|
875
|
+
type: "LineString",
|
|
876
|
+
coordinates: derived.lineCoords
|
|
877
|
+
};
|
|
878
|
+
if (derived.geometries.length > 0) return {
|
|
879
|
+
type: "GeometryCollection",
|
|
880
|
+
geometries: derived.geometries
|
|
881
|
+
};
|
|
882
|
+
return derived.geometry;
|
|
883
|
+
}
|
|
884
|
+
function addNodeProps(node, attrs) {
|
|
885
|
+
for (const k in attrs) if (k !== "id" && k !== "lon" && k !== "lat") node.addProp(k, attrs[k]);
|
|
886
|
+
}
|
|
887
|
+
function addWayProps(way, attrs) {
|
|
888
|
+
for (const k in attrs) if (k !== "id") way.addProp(k, attrs[k]);
|
|
889
|
+
}
|
|
890
|
+
function addDerivedProps(output, attrs) {
|
|
891
|
+
for (const k in attrs) if (k !== "id" && k !== "type") output.addProp(k, attrs[k]);
|
|
892
|
+
}
|
|
893
|
+
function isRoot(kind) {
|
|
894
|
+
return kind === 0 || kind === 7;
|
|
895
|
+
}
|
|
896
|
+
function finish(frame, parent, refElements) {
|
|
897
|
+
switch (frame.kind) {
|
|
898
|
+
case 1: {
|
|
899
|
+
const attrs = frame.attrs;
|
|
900
|
+
if (frame.hasDerived) {
|
|
901
|
+
const output = new Output("node", frame.id, refElements);
|
|
902
|
+
addDerivedProps(output, attrs);
|
|
903
|
+
if (frame.tags) output.addTags(frame.tags);
|
|
904
|
+
const geometry = buildDerivedGeometry(frame.derived);
|
|
905
|
+
if (geometry) output.setGeometry(geometry);
|
|
906
|
+
} else {
|
|
907
|
+
const node = new Node(frame.id, refElements);
|
|
908
|
+
addNodeProps(node, attrs);
|
|
909
|
+
node.setLatLng({
|
|
910
|
+
lat: attrs.lat,
|
|
911
|
+
lon: attrs.lon
|
|
668
912
|
});
|
|
669
|
-
|
|
670
|
-
ways,
|
|
671
|
-
roles
|
|
672
|
-
};
|
|
913
|
+
if (frame.tags) node.addTags(frame.tags);
|
|
673
914
|
}
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
915
|
+
return;
|
|
916
|
+
}
|
|
917
|
+
case 2: {
|
|
918
|
+
const way = frame.way;
|
|
919
|
+
if (frame.hasDerived) {
|
|
920
|
+
const output = new Output("way", frame.id, refElements);
|
|
921
|
+
addDerivedProps(output, frame.attrs);
|
|
922
|
+
if (frame.tags) output.addTags(frame.tags);
|
|
923
|
+
const geometry = buildDerivedGeometry(frame.derived);
|
|
924
|
+
if (geometry) output.setGeometry(geometry);
|
|
925
|
+
} else {
|
|
926
|
+
refElements.add(way.getCompositeId(), way);
|
|
927
|
+
addWayProps(way, frame.attrs);
|
|
928
|
+
if (frame.tags) way.addTags(frame.tags);
|
|
929
|
+
if (frame.center) way.setCenter(frame.center);
|
|
930
|
+
if (frame.bounds && !frame.hasNd) way.setBounds(frame.bounds);
|
|
682
931
|
}
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
932
|
+
return;
|
|
933
|
+
}
|
|
934
|
+
case 3: {
|
|
935
|
+
const relation = frame.relation;
|
|
936
|
+
if (frame.hasDerived) {
|
|
937
|
+
const output = new Output("relation", frame.id, refElements);
|
|
938
|
+
addDerivedProps(output, frame.attrs);
|
|
939
|
+
if (frame.tags) output.addTags(frame.tags);
|
|
940
|
+
const geometry = buildDerivedGeometry(frame.derived);
|
|
941
|
+
if (geometry) output.setGeometry(geometry);
|
|
942
|
+
} else {
|
|
943
|
+
if (frame.tags) relation.addTags(frame.tags);
|
|
944
|
+
if (frame.center) relation.setCenter(frame.center);
|
|
945
|
+
}
|
|
946
|
+
return;
|
|
947
|
+
}
|
|
948
|
+
case 4: {
|
|
949
|
+
const attrs = frame.attrs;
|
|
950
|
+
const member = {
|
|
951
|
+
type: attrs.type,
|
|
952
|
+
role: attrs.role ? attrs.role : "",
|
|
953
|
+
ref: attrs.ref
|
|
690
954
|
};
|
|
955
|
+
if (attrs.lat && attrs.lon) {
|
|
956
|
+
member.lat = attrs.lat;
|
|
957
|
+
member.lon = attrs.lon;
|
|
958
|
+
member.tags = {};
|
|
959
|
+
for (const k in attrs) if (k !== "type" && k !== "lat" && k !== "lon") member[k] = attrs[k];
|
|
960
|
+
}
|
|
961
|
+
if (frame.member.geometry.length > 0) member.geometry = frame.member.geometry;
|
|
962
|
+
else if (frame.member.nodes.length > 0) member.nodes = frame.member.nodes;
|
|
963
|
+
if (parent && parent.kind === 3 && parent.relation) parent.relation.addMember(member);
|
|
964
|
+
return;
|
|
691
965
|
}
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
const relationToWaysMap = /* @__PURE__ */ new Map();
|
|
697
|
-
const waysAndRoles = this.collectAllWaysForRelation(this, relationToWaysMap);
|
|
698
|
-
let templateFeature = {
|
|
699
|
-
type: "Feature",
|
|
700
|
-
id: this.getCompositeId(),
|
|
701
|
-
bbox: this.bounds,
|
|
702
|
-
properties: this.getProps(),
|
|
703
|
-
geometry: null
|
|
966
|
+
case 5:
|
|
967
|
+
if (parent && (parent.kind === 1 || parent.kind === 2 || parent.kind === 3)) parent.derived.geometry = {
|
|
968
|
+
type: "Polygon",
|
|
969
|
+
coordinates: [frame.lineCoords]
|
|
704
970
|
};
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
const
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
971
|
+
return;
|
|
972
|
+
case 6:
|
|
973
|
+
if (parent && (parent.kind === 1 || parent.kind === 2 || parent.kind === 3)) {
|
|
974
|
+
const derived = parent.derived;
|
|
975
|
+
const points = frame.groupPoints;
|
|
976
|
+
for (let i = 0; i < points.length; i++) derived.geometries.push(points[i]);
|
|
977
|
+
const coords = frame.groupCoords;
|
|
978
|
+
if (coords.length > 0) derived.geometries.push({
|
|
979
|
+
type: "LineString",
|
|
980
|
+
coordinates: coords
|
|
981
|
+
});
|
|
982
|
+
}
|
|
983
|
+
return;
|
|
984
|
+
}
|
|
985
|
+
}
|
|
986
|
+
/**
|
|
987
|
+
* Parse OSM/Overpass XML directly into `RefElements`.
|
|
988
|
+
*/
|
|
989
|
+
function parseOsmXml(xml, refElements) {
|
|
990
|
+
const len = xml.length;
|
|
991
|
+
const stack = [];
|
|
992
|
+
let i = 0;
|
|
993
|
+
while (i < len) {
|
|
994
|
+
const lt = xml.indexOf("<", i);
|
|
995
|
+
if (lt < 0) break;
|
|
996
|
+
i = lt + 1;
|
|
997
|
+
const c = xml.charCodeAt(i);
|
|
998
|
+
if (c === SLASH) {
|
|
999
|
+
const gt = xml.indexOf(">", i);
|
|
1000
|
+
if (gt < 0) break;
|
|
1001
|
+
i = gt + 1;
|
|
1002
|
+
const frame = stack.pop();
|
|
1003
|
+
if (frame) finish(frame, stack.length ? stack[stack.length - 1] : void 0, refElements);
|
|
1004
|
+
continue;
|
|
1005
|
+
}
|
|
1006
|
+
if (c === QUESTION) {
|
|
1007
|
+
const gt = xml.indexOf(">", i);
|
|
1008
|
+
i = gt < 0 ? len : gt + 1;
|
|
1009
|
+
continue;
|
|
1010
|
+
}
|
|
1011
|
+
if (c === BANG) {
|
|
1012
|
+
if (xml.charCodeAt(i + 1) === 45 && xml.charCodeAt(i + 2) === 45) {
|
|
1013
|
+
const end = xml.indexOf("-->", i + 3);
|
|
1014
|
+
i = end < 0 ? len : end + 3;
|
|
1015
|
+
} else {
|
|
1016
|
+
const gt = xml.indexOf(">", i);
|
|
1017
|
+
i = gt < 0 ? len : gt + 1;
|
|
1018
|
+
}
|
|
1019
|
+
continue;
|
|
1020
|
+
}
|
|
1021
|
+
let j = i;
|
|
1022
|
+
while (j < len) {
|
|
1023
|
+
const ch = xml.charCodeAt(j);
|
|
1024
|
+
if (isSpace(ch) || ch === SLASH || ch === GT) break;
|
|
1025
|
+
j++;
|
|
1026
|
+
}
|
|
1027
|
+
const tag = xml.slice(i, j);
|
|
1028
|
+
const topKind = stack.length ? stack[stack.length - 1].kind : 0;
|
|
1029
|
+
let kind = 7;
|
|
1030
|
+
let attrMode = 0;
|
|
1031
|
+
switch (tag) {
|
|
1032
|
+
case "node":
|
|
1033
|
+
if (isRoot(topKind)) {
|
|
1034
|
+
kind = 1;
|
|
1035
|
+
attrMode = 1;
|
|
714
1036
|
}
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
if (
|
|
718
|
-
|
|
719
|
-
|
|
1037
|
+
break;
|
|
1038
|
+
case "way":
|
|
1039
|
+
if (isRoot(topKind)) {
|
|
1040
|
+
kind = 2;
|
|
1041
|
+
attrMode = 1;
|
|
720
1042
|
}
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
1043
|
+
break;
|
|
1044
|
+
case "relation":
|
|
1045
|
+
if (isRoot(topKind)) {
|
|
1046
|
+
kind = 3;
|
|
1047
|
+
attrMode = 1;
|
|
1048
|
+
}
|
|
1049
|
+
break;
|
|
1050
|
+
case "osm":
|
|
1051
|
+
kind = 0;
|
|
1052
|
+
break;
|
|
1053
|
+
case "member":
|
|
1054
|
+
if (topKind === 3) {
|
|
1055
|
+
kind = 4;
|
|
1056
|
+
attrMode = 4;
|
|
1057
|
+
}
|
|
1058
|
+
break;
|
|
1059
|
+
case "nd":
|
|
1060
|
+
if (topKind === 2 || topKind === 4) attrMode = 2;
|
|
1061
|
+
break;
|
|
1062
|
+
case "tag":
|
|
1063
|
+
if (topKind === 1 || topKind === 2 || topKind === 3) attrMode = 3;
|
|
1064
|
+
break;
|
|
1065
|
+
case "bounds":
|
|
1066
|
+
if (topKind === 3 || topKind === 2) attrMode = 5;
|
|
1067
|
+
break;
|
|
1068
|
+
case "center":
|
|
1069
|
+
if (topKind === 2 || topKind === 3) attrMode = 6;
|
|
1070
|
+
break;
|
|
1071
|
+
case "point":
|
|
1072
|
+
if (topKind === 1 || topKind === 2 || topKind === 3 || topKind === 6) attrMode = 7;
|
|
1073
|
+
break;
|
|
1074
|
+
case "vertex":
|
|
1075
|
+
if (topKind === 2 || topKind === 3 || topKind === 6 || topKind === 5) attrMode = 7;
|
|
1076
|
+
break;
|
|
1077
|
+
case "linestring":
|
|
1078
|
+
if (topKind === 1 || topKind === 2 || topKind === 3) kind = 5;
|
|
1079
|
+
break;
|
|
1080
|
+
case "group": if (topKind === 1 || topKind === 2 || topKind === 3) kind = 6;
|
|
1081
|
+
}
|
|
1082
|
+
const attrs = attrMode === 1 || attrMode === 4 ? {} : void 0;
|
|
1083
|
+
let ref = "", lat = "", lon = "", k = "", v = "";
|
|
1084
|
+
let minlat = "", minlon = "", maxlat = "", maxlon = "";
|
|
1085
|
+
let selfClose = false;
|
|
1086
|
+
while (j < len) {
|
|
1087
|
+
let ch = xml.charCodeAt(j);
|
|
1088
|
+
while (isSpace(ch)) ch = xml.charCodeAt(++j);
|
|
1089
|
+
if (ch === GT) {
|
|
1090
|
+
j++;
|
|
1091
|
+
break;
|
|
1092
|
+
}
|
|
1093
|
+
if (ch === SLASH) {
|
|
1094
|
+
selfClose = true;
|
|
1095
|
+
j++;
|
|
1096
|
+
continue;
|
|
1097
|
+
}
|
|
1098
|
+
const nameStart = j;
|
|
1099
|
+
while (j < len) {
|
|
1100
|
+
ch = xml.charCodeAt(j);
|
|
1101
|
+
if (ch === EQUALS || isSpace(ch) || ch === GT || ch === SLASH) break;
|
|
1102
|
+
j++;
|
|
1103
|
+
}
|
|
1104
|
+
const attrName = xml.slice(nameStart, j);
|
|
1105
|
+
while (isSpace(xml.charCodeAt(j))) j++;
|
|
1106
|
+
let value = "";
|
|
1107
|
+
if (xml.charCodeAt(j) === EQUALS) {
|
|
1108
|
+
j++;
|
|
1109
|
+
while (isSpace(xml.charCodeAt(j))) j++;
|
|
1110
|
+
const q = xml.charCodeAt(j);
|
|
1111
|
+
if (q === DQUOTE || q === SQUOTE) {
|
|
1112
|
+
j++;
|
|
1113
|
+
let valEnd = xml.indexOf(q === DQUOTE ? "\"" : "'", j);
|
|
1114
|
+
if (valEnd < 0) valEnd = len;
|
|
1115
|
+
value = xml.slice(j, valEnd);
|
|
1116
|
+
if (value.indexOf("&") >= 0) value = decodeEntities(value);
|
|
1117
|
+
j = valEnd + 1;
|
|
729
1118
|
}
|
|
730
1119
|
}
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
1120
|
+
switch (attrMode) {
|
|
1121
|
+
case 1:
|
|
1122
|
+
case 4:
|
|
1123
|
+
attrs[attrName] = value;
|
|
1124
|
+
break;
|
|
1125
|
+
case 2:
|
|
1126
|
+
if (attrName === "ref") ref = value;
|
|
1127
|
+
else if (attrName === "lat") lat = value;
|
|
1128
|
+
else if (attrName === "lon") lon = value;
|
|
1129
|
+
break;
|
|
1130
|
+
case 3:
|
|
1131
|
+
if (attrName === "k") k = value;
|
|
1132
|
+
else if (attrName === "v") v = value;
|
|
1133
|
+
break;
|
|
1134
|
+
case 5:
|
|
1135
|
+
if (attrName === "minlat") minlat = value;
|
|
1136
|
+
else if (attrName === "minlon") minlon = value;
|
|
1137
|
+
else if (attrName === "maxlat") maxlat = value;
|
|
1138
|
+
else if (attrName === "maxlon") maxlon = value;
|
|
1139
|
+
break;
|
|
1140
|
+
case 6:
|
|
1141
|
+
case 7: if (attrName === "lat") lat = value;
|
|
1142
|
+
else if (attrName === "lon") lon = value;
|
|
1143
|
+
}
|
|
737
1144
|
}
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
1145
|
+
i = j;
|
|
1146
|
+
const parent = stack.length ? stack[stack.length - 1] : void 0;
|
|
1147
|
+
if (attrMode === 2) {
|
|
1148
|
+
if (parent) {
|
|
1149
|
+
if (parent.kind === 2 && parent.way) {
|
|
1150
|
+
parent.hasNd = true;
|
|
1151
|
+
if (lat && lon) parent.way.addLatLng({
|
|
1152
|
+
lat,
|
|
1153
|
+
lon
|
|
1154
|
+
});
|
|
1155
|
+
else if (ref) parent.way.addNodeRef(ref);
|
|
1156
|
+
} else if (parent.kind === 4 && parent.member) {
|
|
1157
|
+
if (lat && lon) parent.member.geometry.push({
|
|
1158
|
+
lat,
|
|
1159
|
+
lon
|
|
1160
|
+
});
|
|
1161
|
+
else if (ref) parent.member.nodes.push(ref);
|
|
1162
|
+
}
|
|
1163
|
+
}
|
|
1164
|
+
if (!selfClose) stack.push({ kind: 7 });
|
|
1165
|
+
continue;
|
|
748
1166
|
}
|
|
749
|
-
|
|
750
|
-
|
|
1167
|
+
if (attrMode === 3) {
|
|
1168
|
+
if (parent && k) {
|
|
1169
|
+
const tags = parent.tags || (parent.tags = {});
|
|
1170
|
+
tags[k] = v;
|
|
1171
|
+
}
|
|
1172
|
+
if (!selfClose) stack.push({ kind: 7 });
|
|
1173
|
+
continue;
|
|
751
1174
|
}
|
|
752
|
-
|
|
753
|
-
|
|
1175
|
+
if (attrMode === 5) {
|
|
1176
|
+
if (parent) {
|
|
1177
|
+
const bounds = [
|
|
1178
|
+
parseFloat(minlon),
|
|
1179
|
+
parseFloat(minlat),
|
|
1180
|
+
parseFloat(maxlon),
|
|
1181
|
+
parseFloat(maxlat)
|
|
1182
|
+
];
|
|
1183
|
+
if (parent.kind === 3 && parent.relation) parent.relation.setBounds(bounds);
|
|
1184
|
+
else if (parent.kind === 2) parent.bounds = bounds;
|
|
1185
|
+
}
|
|
1186
|
+
if (!selfClose) stack.push({ kind: 7 });
|
|
1187
|
+
continue;
|
|
754
1188
|
}
|
|
755
|
-
|
|
756
|
-
|
|
1189
|
+
if (attrMode === 6) {
|
|
1190
|
+
if (parent) parent.center = {
|
|
1191
|
+
lat,
|
|
1192
|
+
lon
|
|
1193
|
+
};
|
|
1194
|
+
if (!selfClose) stack.push({ kind: 7 });
|
|
1195
|
+
continue;
|
|
757
1196
|
}
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
1197
|
+
if (attrMode === 7) {
|
|
1198
|
+
if (parent) {
|
|
1199
|
+
const x = +lon;
|
|
1200
|
+
const y = +lat;
|
|
1201
|
+
switch (parent.kind) {
|
|
1202
|
+
case 1:
|
|
1203
|
+
case 2:
|
|
1204
|
+
case 3:
|
|
1205
|
+
parent.hasDerived = true;
|
|
1206
|
+
if (tag === "point") parent.derived.geometry = {
|
|
1207
|
+
type: "Point",
|
|
1208
|
+
coordinates: [x, y]
|
|
1209
|
+
};
|
|
1210
|
+
else parent.derived.lineCoords.push([x, y]);
|
|
1211
|
+
break;
|
|
1212
|
+
case 6:
|
|
1213
|
+
if (tag === "point") parent.groupPoints.push({
|
|
1214
|
+
type: "Point",
|
|
1215
|
+
coordinates: [x, y]
|
|
1216
|
+
});
|
|
1217
|
+
else parent.groupCoords.push([x, y]);
|
|
1218
|
+
break;
|
|
1219
|
+
case 5: parent.lineCoords.push([x, y]);
|
|
1220
|
+
}
|
|
1221
|
+
}
|
|
1222
|
+
if (!selfClose) stack.push({ kind: 7 });
|
|
1223
|
+
continue;
|
|
1224
|
+
}
|
|
1225
|
+
let frame;
|
|
1226
|
+
switch (kind) {
|
|
1227
|
+
case 1:
|
|
1228
|
+
frame = {
|
|
1229
|
+
kind,
|
|
1230
|
+
id: attrs.id,
|
|
1231
|
+
attrs,
|
|
1232
|
+
derived: newDerived(),
|
|
1233
|
+
hasDerived: false
|
|
1234
|
+
};
|
|
1235
|
+
break;
|
|
1236
|
+
case 2:
|
|
1237
|
+
frame = {
|
|
1238
|
+
kind,
|
|
1239
|
+
id: attrs.id,
|
|
1240
|
+
attrs,
|
|
1241
|
+
way: new Way(attrs.id, refElements, false),
|
|
1242
|
+
derived: newDerived(),
|
|
1243
|
+
hasDerived: false
|
|
1244
|
+
};
|
|
1245
|
+
break;
|
|
1246
|
+
case 3:
|
|
1247
|
+
frame = {
|
|
1248
|
+
kind,
|
|
1249
|
+
id: attrs.id,
|
|
1250
|
+
attrs,
|
|
1251
|
+
relation: new Relation(attrs.id, refElements),
|
|
1252
|
+
derived: newDerived(),
|
|
1253
|
+
hasDerived: false
|
|
1254
|
+
};
|
|
1255
|
+
break;
|
|
1256
|
+
case 4:
|
|
1257
|
+
frame = {
|
|
1258
|
+
kind,
|
|
1259
|
+
attrs,
|
|
1260
|
+
member: {
|
|
1261
|
+
geometry: [],
|
|
1262
|
+
nodes: []
|
|
1263
|
+
}
|
|
1264
|
+
};
|
|
1265
|
+
break;
|
|
1266
|
+
case 5:
|
|
1267
|
+
if (parent && (parent.kind === 1 || parent.kind === 2 || parent.kind === 3)) parent.hasDerived = true;
|
|
1268
|
+
frame = {
|
|
1269
|
+
kind,
|
|
1270
|
+
lineCoords: []
|
|
1271
|
+
};
|
|
1272
|
+
break;
|
|
1273
|
+
case 6:
|
|
1274
|
+
if (parent && (parent.kind === 1 || parent.kind === 2 || parent.kind === 3)) parent.hasDerived = true;
|
|
1275
|
+
frame = {
|
|
1276
|
+
kind,
|
|
1277
|
+
groupPoints: [],
|
|
1278
|
+
groupCoords: []
|
|
1279
|
+
};
|
|
1280
|
+
break;
|
|
1281
|
+
case 0:
|
|
1282
|
+
case 7: frame = { kind };
|
|
1283
|
+
}
|
|
1284
|
+
if (selfClose) {
|
|
1285
|
+
if (frame) finish(frame, parent, refElements);
|
|
1286
|
+
} else if (frame) stack.push(frame);
|
|
780
1287
|
}
|
|
781
|
-
|
|
782
|
-
|
|
1288
|
+
}
|
|
1289
|
+
// src/ref-elements.ts
|
|
1290
|
+
var RefElements = class extends Map {
|
|
1291
|
+
constructor() {
|
|
1292
|
+
super();
|
|
1293
|
+
this.binders = [];
|
|
1294
|
+
this.reportTainted = false;
|
|
1295
|
+
this.polygonTags = void 0;
|
|
1296
|
+
}
|
|
1297
|
+
add(k, v) {
|
|
1298
|
+
this.set(k, v);
|
|
1299
|
+
}
|
|
1300
|
+
addBinder(binder) {
|
|
1301
|
+
this.binders.push(binder);
|
|
1302
|
+
}
|
|
1303
|
+
/**
|
|
1304
|
+
* Resolve every deferred reference in one pass per container.
|
|
1305
|
+
*
|
|
1306
|
+
* The previous implementation removed each binder with `indexOf` + `splice`,
|
|
1307
|
+
* which is O(n^2) for containers such as a way's node list. Grouping by
|
|
1308
|
+
* container and compacting once keeps insertion order and makes binding
|
|
1309
|
+
* linear in the number of binders.
|
|
1310
|
+
*/
|
|
1311
|
+
bindAll() {
|
|
1312
|
+
if (this.binders.length === 0) return;
|
|
1313
|
+
const groups = /* @__PURE__ */ new Map();
|
|
1314
|
+
for (const binder of this.binders) {
|
|
1315
|
+
const container = binder.getContainer();
|
|
1316
|
+
const list = groups.get(container);
|
|
1317
|
+
if (list) list.push(binder);
|
|
1318
|
+
else groups.set(container, [binder]);
|
|
1319
|
+
}
|
|
1320
|
+
for (const [container, binders] of groups) {
|
|
1321
|
+
const bindersInContainer = new Set(binders);
|
|
1322
|
+
const resolved = /* @__PURE__ */ new Map();
|
|
1323
|
+
for (const binder of binders) resolved.set(binder, binder.resolve());
|
|
1324
|
+
let write = 0;
|
|
1325
|
+
for (let read = 0; read < container.length; read++) {
|
|
1326
|
+
const item = container[read];
|
|
1327
|
+
if (bindersInContainer.has(item)) {
|
|
1328
|
+
const value = resolved.get(item);
|
|
1329
|
+
if (value !== void 0) container[write++] = value;
|
|
1330
|
+
} else container[write++] = item;
|
|
1331
|
+
}
|
|
1332
|
+
container.length = write;
|
|
1333
|
+
}
|
|
1334
|
+
this.binders = [];
|
|
1335
|
+
}
|
|
1336
|
+
};
|
|
1337
|
+
// src/index.ts
|
|
1338
|
+
function parseOptions(options) {
|
|
1339
|
+
if (!options) return {
|
|
1340
|
+
completeFeature: false,
|
|
1341
|
+
renderTagged: false,
|
|
1342
|
+
excludeWay: true,
|
|
1343
|
+
elementId: void 0,
|
|
1344
|
+
tainted: false,
|
|
1345
|
+
polygonTags: void 0
|
|
1346
|
+
};
|
|
1347
|
+
let excludeWay = options.excludeWay === void 0 || options.excludeWay;
|
|
1348
|
+
let completeFeature = options.completeFeature ? true : false;
|
|
1349
|
+
let renderTagged = options.renderTagged ? true : false;
|
|
1350
|
+
let tainted = options.tainted ? true : false;
|
|
1351
|
+
return {
|
|
1352
|
+
completeFeature,
|
|
1353
|
+
renderTagged,
|
|
1354
|
+
excludeWay,
|
|
1355
|
+
elementId: options.elementId,
|
|
1356
|
+
tainted,
|
|
1357
|
+
polygonTags: options.polygonTags
|
|
1358
|
+
};
|
|
1359
|
+
}
|
|
1360
|
+
function detectFormat(o) {
|
|
1361
|
+
if (o && typeof o === "object" && o.elements) return "json";
|
|
1362
|
+
if (typeof o === "string") {
|
|
783
1363
|
if (o.indexOf("<osm") >= 0) return "xml";
|
|
784
1364
|
if (o.trim().startsWith("{")) return "json-raw";
|
|
785
|
-
return "invalid";
|
|
786
1365
|
}
|
|
787
|
-
|
|
788
|
-
|
|
1366
|
+
return "invalid";
|
|
1367
|
+
}
|
|
1368
|
+
function analyzeFeaturesFromJson(osm, refElements) {
|
|
1369
|
+
for (const elem of osm.elements) {
|
|
1370
|
+
if (elem.geometry && typeof elem.geometry === "object" && elem.geometry.type) {
|
|
1371
|
+
const output = new Output(elem.type, elem.id, refElements);
|
|
1372
|
+
if (elem.tags) output.addTags(elem.tags);
|
|
1373
|
+
output.addProps(purgeProps(elem, [
|
|
1374
|
+
"id",
|
|
1375
|
+
"type",
|
|
1376
|
+
"tags",
|
|
1377
|
+
"geometry"
|
|
1378
|
+
]));
|
|
1379
|
+
output.setGeometry(elem.geometry);
|
|
1380
|
+
continue;
|
|
1381
|
+
}
|
|
1382
|
+
switch (elem.type) {
|
|
789
1383
|
case "node":
|
|
790
1384
|
const node = new Node(elem.id, refElements);
|
|
791
1385
|
if (elem.tags) node.addTags(elem.tags);
|
|
@@ -809,7 +1403,14 @@ var require_src = /* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
|
809
1403
|
"geometry"
|
|
810
1404
|
]));
|
|
811
1405
|
if (elem.geometry) way.setLatLngArray(elem.geometry);
|
|
1406
|
+
else if (elem.center) way.setCenter(elem.center);
|
|
812
1407
|
else if (elem.nodes) for (const n of elem.nodes) way.addNodeRef(n);
|
|
1408
|
+
else if (elem.bounds) way.setBounds([
|
|
1409
|
+
parseFloat(elem.bounds.minlon),
|
|
1410
|
+
parseFloat(elem.bounds.minlat),
|
|
1411
|
+
parseFloat(elem.bounds.maxlon),
|
|
1412
|
+
parseFloat(elem.bounds.maxlat)
|
|
1413
|
+
]);
|
|
813
1414
|
break;
|
|
814
1415
|
case "relation":
|
|
815
1416
|
const relation = new Relation(elem.id, refElements);
|
|
@@ -820,6 +1421,7 @@ var require_src = /* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
|
820
1421
|
parseFloat(elem.bounds.maxlat)
|
|
821
1422
|
]);
|
|
822
1423
|
if (elem.tags) relation.addTags(elem.tags);
|
|
1424
|
+
if (elem.center) relation.setCenter(elem.center);
|
|
823
1425
|
relation.addProps(purgeProps(elem, [
|
|
824
1426
|
"id",
|
|
825
1427
|
"type",
|
|
@@ -828,97 +1430,40 @@ var require_src = /* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
|
828
1430
|
"members"
|
|
829
1431
|
]));
|
|
830
1432
|
if (elem.members) for (const member of elem.members) relation.addMember(member);
|
|
831
|
-
default: break;
|
|
832
1433
|
}
|
|
833
1434
|
}
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
const way = new Way(node.id, refElements);
|
|
850
|
-
for (const [k, v] of Object.entries(node)) if (!k.startsWith("$") && ["id"].indexOf(k) < 0) way.addProp(k, v);
|
|
851
|
-
if (node.$innerNodes) {
|
|
852
|
-
for (const ind of node.$innerNodes) if (ind.$tag === "nd") {
|
|
853
|
-
if (ind.lon && ind.lat) way.addLatLng(ind);
|
|
854
|
-
else if (ind.ref) way.addNodeRef(ind.ref);
|
|
855
|
-
} else if (ind.$tag === "tag") way.addTag(ind.k, ind.v);
|
|
856
|
-
}
|
|
857
|
-
});
|
|
858
|
-
xmlParser.on("<osm.relation>", (node) => new Relation(node.id, refElements));
|
|
859
|
-
xmlParser.on("</osm.relation.member>", (node, parent) => {
|
|
860
|
-
const relation = refElements.get(`relation/${parent?.id}`);
|
|
861
|
-
const member = {
|
|
862
|
-
type: node.type,
|
|
863
|
-
role: node.role ? node.role : "",
|
|
864
|
-
ref: node.ref
|
|
865
|
-
};
|
|
866
|
-
if (node.lat && node.lon) {
|
|
867
|
-
member.lat = node.lat, member.lon = node.lon, member.tags = {};
|
|
868
|
-
for (const [k, v] of Object.entries(node)) if (!k.startsWith("$") && [
|
|
869
|
-
"type",
|
|
870
|
-
"lat",
|
|
871
|
-
"lon"
|
|
872
|
-
].indexOf(k) < 0) member[k] = v;
|
|
873
|
-
}
|
|
874
|
-
if (node.$innerNodes) {
|
|
875
|
-
const geometry = [];
|
|
876
|
-
const nodes = [];
|
|
877
|
-
for (const ind of node.$innerNodes) if (ind.lat && ind.lon) geometry.push(ind);
|
|
878
|
-
else if (ind.ref) nodes.push(ind.ref);
|
|
879
|
-
if (geometry.length > 0) member.geometry = geometry;
|
|
880
|
-
else if (nodes.length > 0) member.nodes = nodes;
|
|
881
|
-
}
|
|
882
|
-
relation.addMember(member);
|
|
883
|
-
});
|
|
884
|
-
xmlParser.on("</osm.relation.bounds>", (node, parent) => {
|
|
885
|
-
refElements.get(`relation/${parent?.id}`).setBounds([
|
|
886
|
-
parseFloat(node.minlon),
|
|
887
|
-
parseFloat(node.minlat),
|
|
888
|
-
parseFloat(node.maxlon),
|
|
889
|
-
parseFloat(node.maxlat)
|
|
890
|
-
]);
|
|
891
|
-
});
|
|
892
|
-
xmlParser.on("</osm.relation.tag>", (node, parent) => {
|
|
893
|
-
refElements.get(`relation/${parent?.id}`).addTag(node.k, node.v);
|
|
894
|
-
});
|
|
895
|
-
xmlParser.parse(osm);
|
|
896
|
-
}
|
|
897
|
-
function osm2geojson(osm, opts) {
|
|
898
|
-
let { completeFeature, renderTagged, excludeWay } = parseOptions(opts);
|
|
899
|
-
let format = detectFormat(osm);
|
|
900
|
-
const refElements = new RefElements();
|
|
901
|
-
let featureArray = [];
|
|
902
|
-
if (format === "json-raw") {
|
|
903
|
-
osm = JSON.parse(osm);
|
|
904
|
-
if (osm.elements) format = "json";
|
|
905
|
-
else format = "invalid";
|
|
906
|
-
}
|
|
907
|
-
if (format === "json") analyzeFeaturesFromJson(osm, refElements);
|
|
908
|
-
else if (format === "xml") analyzeFeaturesFromXml(osm, refElements);
|
|
909
|
-
refElements.bindAll();
|
|
910
|
-
for (const v of refElements.values()) {
|
|
911
|
-
if (v.refCount > 0 && (!v.hasTag || !renderTagged || v instanceof Way && excludeWay)) continue;
|
|
912
|
-
const features = v.toFeatureArray();
|
|
913
|
-
if (v instanceof Relation && !completeFeature && features.length > 0) return features[0].geometry;
|
|
914
|
-
featureArray = featureArray.concat(features);
|
|
915
|
-
}
|
|
916
|
-
return {
|
|
917
|
-
type: "FeatureCollection",
|
|
918
|
-
features: featureArray
|
|
919
|
-
};
|
|
1435
|
+
}
|
|
1436
|
+
function analyzeFeaturesFromXml(osm, refElements) {
|
|
1437
|
+
parseOsmXml(osm, refElements);
|
|
1438
|
+
}
|
|
1439
|
+
function osm2geojson(osm, opts) {
|
|
1440
|
+
let { completeFeature, renderTagged, excludeWay, elementId, tainted, polygonTags } = parseOptions(opts);
|
|
1441
|
+
let format = detectFormat(osm);
|
|
1442
|
+
const refElements = new RefElements();
|
|
1443
|
+
refElements.reportTainted = tainted;
|
|
1444
|
+
refElements.polygonTags = polygonTags;
|
|
1445
|
+
let featureArray = [];
|
|
1446
|
+
if (format === "json-raw") {
|
|
1447
|
+
osm = JSON.parse(osm);
|
|
1448
|
+
if (osm.elements) format = "json";
|
|
1449
|
+
else format = "invalid";
|
|
920
1450
|
}
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
1451
|
+
if (format === "json") analyzeFeaturesFromJson(osm, refElements);
|
|
1452
|
+
else if (format === "xml") analyzeFeaturesFromXml(osm, refElements);
|
|
1453
|
+
refElements.bindAll();
|
|
1454
|
+
if (elementId) {
|
|
1455
|
+
const element = refElements.get(elementId);
|
|
1456
|
+
return element ? element.toFeatureArray()[0] : void 0;
|
|
1457
|
+
}
|
|
1458
|
+
for (const v of refElements.values()) {
|
|
1459
|
+
if (v.refCount > 0 && (!v.hasTag || !renderTagged || v instanceof Way && excludeWay)) continue;
|
|
1460
|
+
const features = v.toFeatureArray();
|
|
1461
|
+
if (v instanceof Relation && !completeFeature && features.length > 0) return features[0].geometry;
|
|
1462
|
+
for (const feature of features) featureArray.push(feature);
|
|
1463
|
+
}
|
|
1464
|
+
return {
|
|
1465
|
+
type: "FeatureCollection",
|
|
1466
|
+
features: featureArray
|
|
1467
|
+
};
|
|
1468
|
+
}
|
|
1469
|
+
export { osm2geojson as default };
|