osm2geojson-lite 1.2.0 → 2.0.1

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/dist/index.js ADDED
@@ -0,0 +1,924 @@
1
+ //#region \0rolldown/runtime.js
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
6
+ function purgeProps(obj, blacklist) {
7
+ if (obj) {
8
+ const rs = Object.assign({}, obj);
9
+ if (blacklist) for (const prop of blacklist) delete rs[prop];
10
+ return rs;
11
+ }
12
+ return {};
13
+ }
14
+ function first(a) {
15
+ return a[0];
16
+ }
17
+ function last(a) {
18
+ return a[a.length - 1];
19
+ }
20
+ function coordsToKey(a) {
21
+ return a.join(",");
22
+ }
23
+ function addToMap(m, k, v) {
24
+ const a = m[k];
25
+ if (a) a.push(v);
26
+ else m[k] = [v];
27
+ }
28
+ function removeFromMap(m, k, v) {
29
+ const a = m[k];
30
+ let idx = -1;
31
+ if (a) idx = a.indexOf(v);
32
+ if (idx >= 0) a.splice(idx, 1);
33
+ }
34
+ function getFirstFromMap(m, k) {
35
+ const a = m[k];
36
+ if (a && a.length > 0) return a[0];
37
+ return null;
38
+ }
39
+ function isRing(a) {
40
+ return a.length > 3 && coordsToKey(first(a)) === coordsToKey(last(a));
41
+ }
42
+ function ringDirection(a, xIdx, yIdx) {
43
+ xIdx = xIdx || 0, yIdx = yIdx || 1;
44
+ const m = a.reduce((maxxIdx, v, idx) => a[maxxIdx][xIdx || 0] > v[xIdx || 0] ? maxxIdx : idx, 0);
45
+ const l = m <= 0 ? a.length - 2 : m - 1;
46
+ const r = m >= a.length - 1 ? 1 : m + 1;
47
+ const xa = a[l][xIdx];
48
+ const xb = a[m][xIdx];
49
+ const xc = a[r][xIdx];
50
+ const ya = a[l][yIdx];
51
+ const yb = a[m][yIdx];
52
+ const yc = a[r][yIdx];
53
+ return (xb - xa) * (yc - ya) - (xc - xa) * (yb - ya) < 0 ? "clockwise" : "counterclockwise";
54
+ }
55
+ function pointInsidePolygon(pt, polygon, xIdx, yIdx) {
56
+ xIdx = xIdx || 0, yIdx = yIdx || 1;
57
+ let result = false;
58
+ for (let i = 0, j = polygon.length - 1; i < polygon.length; j = i++) if ((polygon[i][xIdx] <= pt[xIdx] && pt[xIdx] < polygon[j][xIdx] || polygon[j][xIdx] <= pt[xIdx] && pt[xIdx] < polygon[i][xIdx]) && pt[yIdx] < (polygon[j][yIdx] - polygon[i][yIdx]) * (pt[xIdx] - polygon[i][xIdx]) / (polygon[j][xIdx] - polygon[i][xIdx]) + polygon[i][yIdx]) result = !result;
59
+ return result;
60
+ }
61
+ function strArrayArrayToFloat(el) {
62
+ return el.map(strArrayToFloat);
63
+ }
64
+ function strArrayToFloat(el) {
65
+ return el.map(parseFloat);
66
+ }
67
+ var init_utils = __esmMin((() => {}));
68
+ //#endregion
69
+ //#region src/xmlparser.ts
70
+ function conditioned(evt) {
71
+ return evt.match(/^(.+?)\[(.+?)\]>$/g) !== null;
72
+ }
73
+ function parseEvent(evt) {
74
+ const match = /^(.+?)\[(.+?)\]>$/g.exec(evt);
75
+ if (match) return {
76
+ evt: match[1] + ">",
77
+ exp: match[2]
78
+ };
79
+ return { evt };
80
+ }
81
+ function genConditionFunc(cond) {
82
+ const body = "return " + cond.replace(/(\$.+?)(?=[=!.])/g, "node.$&") + ";";
83
+ return new Function("node", body);
84
+ }
85
+ var XmlParser;
86
+ var init_xmlparser = __esmMin((() => {
87
+ XmlParser = class {
88
+ constructor(opts) {
89
+ this.queryParent = false;
90
+ this.progressive = false;
91
+ this.parentMap = /* @__PURE__ */ new WeakMap();
92
+ if (opts) {
93
+ this.queryParent = opts.queryParent ? true : false;
94
+ this.progressive = opts.progressive;
95
+ if (this.queryParent) this.parentMap = /* @__PURE__ */ new WeakMap();
96
+ }
97
+ this.evtListeners = {};
98
+ }
99
+ parse(xml, parent, dir) {
100
+ dir = dir ? dir + "." : "";
101
+ const nodeRegEx = /<([^ >\/]+)(.*?)>/gm;
102
+ const nodes = [];
103
+ let nodeMatch = nodeRegEx.exec(xml);
104
+ while (nodeMatch) {
105
+ const tag = nodeMatch[1];
106
+ const node = { $tag: tag };
107
+ const fullTag = dir + tag;
108
+ const attrText = nodeMatch[2].trim();
109
+ let closed = false;
110
+ if (attrText.endsWith("/") || tag.startsWith("?") || tag.startsWith("!")) closed = true;
111
+ const attRegEx1 = /([^ ]+?)="(.+?)"/g;
112
+ const attRegEx2 = /([^ ]+?)='(.+?)'/g;
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;
157
+ }
158
+ this.$addListener(evt, func);
159
+ }
160
+ removeListener(evt, func) {
161
+ if (conditioned(evt)) evt = parseEvent(evt).evt;
162
+ this.$removeListener(evt, func);
163
+ }
164
+ on(evt, func) {
165
+ this.addListener(evt, func);
166
+ }
167
+ off(evt, func) {
168
+ this.removeListener(evt, func);
169
+ }
170
+ $addListener(evt, func) {
171
+ const funcs = this.evtListeners[evt];
172
+ if (funcs) funcs.push(func);
173
+ else this.evtListeners[evt] = [func];
174
+ }
175
+ $removeListener(evt, func) {
176
+ const funcs = this.evtListeners[evt];
177
+ let idx = -1;
178
+ if (funcs) idx = funcs.indexOf(func);
179
+ if (idx >= 0) funcs.splice(idx, 1);
180
+ }
181
+ emit(evt, ...args) {
182
+ const funcs = this.evtListeners[evt];
183
+ if (funcs) for (const func of funcs) if (func.condition) {
184
+ if (func.condition.apply(null, args) === true) func.apply(null, args);
185
+ } else func.apply(null, args);
186
+ }
187
+ };
188
+ }));
189
+ //#endregion
190
+ //#region src/osm-object.ts
191
+ var OsmObject;
192
+ var init_osm_object = __esmMin((() => {
193
+ OsmObject = class {
194
+ constructor(type, id, refElems) {
195
+ this.type = type;
196
+ this.id = id;
197
+ this.refElems = refElems;
198
+ this.tags = {};
199
+ this.props = { id: this.getCompositeId() };
200
+ this.refCount = 0;
201
+ this.hasTag = false;
202
+ if (refElems) refElems.add(this.getCompositeId(), this);
203
+ }
204
+ addTags(tags) {
205
+ this.tags = Object.assign(this.tags, tags);
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": [
285
+ "services",
286
+ "rest_area",
287
+ "escape",
288
+ "elevator"
289
+ ] },
290
+ natural: { "blacklist": [
291
+ "coastline",
292
+ "cliff",
293
+ "ridge",
294
+ "arete",
295
+ "tree_row"
296
+ ] },
297
+ landuse: {},
298
+ waterway: { "whitelist": [
299
+ "riverbank",
300
+ "dock",
301
+ "boatyard",
302
+ "dam"
303
+ ] },
304
+ amenity: {},
305
+ leisure: {},
306
+ barrier: { "whitelist": [
307
+ "city_wall",
308
+ "ditch",
309
+ "hedge",
310
+ "retaining_wall",
311
+ "wall",
312
+ "spikes"
313
+ ] },
314
+ railway: { "whitelist": [
315
+ "station",
316
+ "turntable",
317
+ "roundhouse",
318
+ "platform"
319
+ ] },
320
+ area: {},
321
+ boundary: {},
322
+ man_made: { "blacklist": [
323
+ "cutline",
324
+ "embankment",
325
+ "pipeline"
326
+ ] },
327
+ power: { "whitelist": [
328
+ "plant",
329
+ "substation",
330
+ "generator",
331
+ "transformer"
332
+ ] },
333
+ place: {},
334
+ shop: {},
335
+ aeroway: { "blacklist": ["taxiway"] },
336
+ tourism: {},
337
+ historic: {},
338
+ public_transport: {},
339
+ office: {},
340
+ "building:part": {},
341
+ military: {},
342
+ ruins: {},
343
+ "area:highway": {},
344
+ craft: {},
345
+ golf: {},
346
+ indoor: {}
347
+ };
348
+ //#endregion
349
+ //#region src/way.ts
350
+ var Way;
351
+ var init_way = __esmMin((() => {
352
+ init_osm_object();
353
+ init_utils();
354
+ Way = class extends OsmObject {
355
+ constructor(id, refElems) {
356
+ super("way", id, refElems);
357
+ this.latLngArray = [];
358
+ this.isPolygon = false;
359
+ }
360
+ addLatLng(latLng) {
361
+ this.latLngArray.push(latLng);
362
+ }
363
+ setLatLngArray(latLngArray) {
364
+ this.latLngArray = latLngArray;
365
+ }
366
+ addNodeRef(ref) {
367
+ const binder = new LateBinder(this.latLngArray, (id) => {
368
+ const node = this.refElems.get(`node/${id}`);
369
+ if (node) {
370
+ node.refCount++;
371
+ return node.getLatLng();
372
+ }
373
+ }, this, [ref]);
374
+ this.latLngArray.push(binder);
375
+ this.refElems.addBinder(binder);
376
+ }
377
+ addTags(tags) {
378
+ super.addTags(tags);
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
+ }
400
+ };
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
+ }
420
+ }
421
+ };
422
+ }));
423
+ //#endregion
424
+ //#region src/way-collection.ts
425
+ var WayCollection;
426
+ var init_way_collection = __esmMin((() => {
427
+ init_utils();
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);
440
+ }
441
+ }
442
+ mergeWays() {
443
+ const strings = [];
444
+ let way = this.shift();
445
+ while (way) {
446
+ removeFromMap(this.firstMap, coordsToKey(first(way)), way);
447
+ removeFromMap(this.lastMap, coordsToKey(last(way)), way);
448
+ let current = way;
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();
478
+ }
479
+ return strings;
480
+ }
481
+ /**
482
+ * Try to find the next way to add to the current way.
483
+ * It first tries the next way in the array, and if this doesn't work, try any other way.
484
+ */
485
+ getNextWay(current) {
486
+ const lastKey = coordsToKey(last(current));
487
+ const firstKey = coordsToKey(first(current));
488
+ let next = this.length > 0 ? this[0] : null;
489
+ if (next) {
490
+ const nextFirstKey = coordsToKey(first(next));
491
+ const nextLastKey = coordsToKey(last(next));
492
+ if (lastKey === nextFirstKey) return {
493
+ next,
494
+ mergeType: 0
495
+ };
496
+ if (lastKey === nextLastKey) return {
497
+ next,
498
+ mergeType: 1
499
+ };
500
+ if (firstKey === nextFirstKey) return {
501
+ next,
502
+ mergeType: 2
503
+ };
504
+ if (firstKey === nextLastKey) return {
505
+ next,
506
+ mergeType: 3
507
+ };
508
+ }
509
+ next = getFirstFromMap(this.firstMap, lastKey);
510
+ if (next) return {
511
+ next,
512
+ mergeType: 0
513
+ };
514
+ next = getFirstFromMap(this.lastMap, lastKey);
515
+ return {
516
+ next,
517
+ mergeType: 1
518
+ };
519
+ }
520
+ toRings(direction) {
521
+ const strings = this.mergeWays();
522
+ const rings = [];
523
+ let str = strings.shift();
524
+ while (str) {
525
+ if (isRing(str)) {
526
+ if (ringDirection(str) !== direction) str.reverse();
527
+ rings.push(str);
528
+ }
529
+ str = strings.shift();
530
+ }
531
+ return rings;
532
+ }
533
+ };
534
+ }));
535
+ //#endregion
536
+ //#region src/relation.ts
537
+ var Relation;
538
+ var init_relation = __esmMin((() => {
539
+ init_osm_object();
540
+ init_way();
541
+ init_way_collection();
542
+ init_late_binder();
543
+ init_utils();
544
+ Relation = class extends OsmObject {
545
+ constructor(id, refElems) {
546
+ super("relation", id, refElems);
547
+ this.relations = [];
548
+ this.nodes = [];
549
+ this.bounds = void 0;
550
+ this.ways = [];
551
+ this.roles = [];
552
+ }
553
+ setBounds(bounds) {
554
+ this.bounds = bounds;
555
+ }
556
+ addMember(member) {
557
+ switch (member.type) {
558
+ case "relation":
559
+ let binder = new LateBinder(this.relations, (id) => {
560
+ const relation = this.refElems.get(`relation/${id}`);
561
+ if (relation) {
562
+ relation.refCount++;
563
+ return relation;
564
+ }
565
+ }, this, [member.ref]);
566
+ this.relations.push(binder);
567
+ this.refElems.addBinder(binder);
568
+ break;
569
+ case "way":
570
+ if (!member.role) member.role = "";
571
+ if (member.geometry) {
572
+ const way = new Way(member.ref, this.refElems);
573
+ way.setLatLngArray(member.geometry);
574
+ way.refCount++;
575
+ this.ways.push(way);
576
+ this.roles.push(member.role);
577
+ } else if (member.nodes) {
578
+ const way = new Way(member.ref, this.refElems);
579
+ for (const nid of member.nodes) way.addNodeRef(nid);
580
+ way.refCount++;
581
+ this.ways.push(way);
582
+ this.roles.push(member.role);
583
+ } else {
584
+ let binder = new LateBinder(this.ways, (nid) => {
585
+ const way = this.refElems.get(`way/${nid}`);
586
+ if (way) {
587
+ way.refCount++;
588
+ return way;
589
+ }
590
+ }, this, [member.ref]);
591
+ this.ways.push(binder);
592
+ this.roles.push(member.role);
593
+ this.refElems.addBinder(binder);
594
+ }
595
+ break;
596
+ case "node":
597
+ let node = null;
598
+ if (member.lat && member.lon) {
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
+ };
634
+ }
635
+ constructPolygonGeometry(ows, iws) {
636
+ const outerRings = ows ? ows.toRings("counterclockwise") : [];
637
+ const innerRings = iws ? iws.toRings("clockwise") : [];
638
+ if (outerRings.length > 0) {
639
+ const compositPolyons = [];
640
+ let ring;
641
+ for (ring of outerRings) compositPolyons.push([ring]);
642
+ ring = innerRings.shift();
643
+ while (ring) {
644
+ for (const idx in outerRings) if (pointInsidePolygon(first(ring), outerRings[idx])) {
645
+ compositPolyons[idx].push(ring);
646
+ break;
647
+ }
648
+ ring = innerRings.shift();
649
+ }
650
+ if (compositPolyons.length === 1) return {
651
+ type: "Polygon",
652
+ coordinates: compositPolyons[0]
653
+ };
654
+ return {
655
+ type: "MultiPolygon",
656
+ coordinates: compositPolyons
657
+ };
658
+ }
659
+ return null;
660
+ }
661
+ collectAllWaysForRelation(relation, relationToWaysMap) {
662
+ const ways = [...relation.ways];
663
+ const roles = [...relation.roles];
664
+ if (relation.relations.length === 0) {
665
+ relationToWaysMap.set(relation.id, {
666
+ ways,
667
+ roles
668
+ });
669
+ return {
670
+ ways,
671
+ roles
672
+ };
673
+ }
674
+ for (const subRelation of relation.relations) {
675
+ if (!subRelation) continue;
676
+ if (!relationToWaysMap.has(subRelation.id)) this.collectAllWaysForRelation(subRelation, relationToWaysMap);
677
+ const entry = relationToWaysMap.get(subRelation.id);
678
+ for (let i = 0; i < entry.ways.length; i++) {
679
+ ways.push(entry.ways[i]);
680
+ roles.push(entry.roles[i]);
681
+ }
682
+ }
683
+ relationToWaysMap.set(relation.id, {
684
+ ways,
685
+ roles
686
+ });
687
+ return {
688
+ ways,
689
+ roles
690
+ };
691
+ }
692
+ toFeatureArray() {
693
+ const polygonFeatures = [];
694
+ const stringFeatures = [];
695
+ let pointFeatures = [];
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
704
+ };
705
+ if (!this.bounds) delete templateFeature.bbox;
706
+ if (this.roles.some((r) => r === "outer")) {
707
+ const outerWayCollection = new WayCollection();
708
+ const innerWayCollection = new WayCollection();
709
+ for (let i = 0; i < waysAndRoles.ways.length; i++) {
710
+ const way = waysAndRoles.ways[i];
711
+ const role = waysAndRoles.roles[i];
712
+ if (role === "outer") outerWayCollection.addWay(way);
713
+ else if (role === "inner") innerWayCollection.addWay(way);
714
+ }
715
+ let feature = Object.assign({}, templateFeature);
716
+ let geometry = this.constructPolygonGeometry(outerWayCollection, innerWayCollection);
717
+ if (geometry) {
718
+ feature.geometry = geometry;
719
+ polygonFeatures.push(feature);
720
+ }
721
+ } else {
722
+ const wayCollection = new WayCollection();
723
+ for (let way of waysAndRoles.ways) wayCollection.addWay(way);
724
+ let geometry = this.constructStringGeometry(wayCollection);
725
+ if (geometry) {
726
+ let feature = Object.assign({}, templateFeature);
727
+ feature.geometry = geometry;
728
+ stringFeatures.push(feature);
729
+ }
730
+ }
731
+ for (let node of this.nodes) pointFeatures = pointFeatures.concat(node.toFeatureArray());
732
+ return [
733
+ ...polygonFeatures,
734
+ ...stringFeatures,
735
+ ...pointFeatures
736
+ ];
737
+ }
738
+ };
739
+ }));
740
+ //#endregion
741
+ //#region src/ref-elements.ts
742
+ var RefElements;
743
+ var init_ref_elements = __esmMin((() => {
744
+ RefElements = class extends Map {
745
+ constructor() {
746
+ super();
747
+ this.binders = [];
748
+ }
749
+ add(k, v) {
750
+ this.set(k, v);
751
+ }
752
+ addBinder(binder) {
753
+ this.binders.push(binder);
754
+ }
755
+ bindAll() {
756
+ this.binders.forEach((binder) => binder.bind());
757
+ }
758
+ };
759
+ }));
760
+ //#endregion
761
+ //#region src/index.ts
762
+ var require_src = /* @__PURE__ */ __commonJSMin(((exports, module) => {
763
+ init_utils();
764
+ init_node();
765
+ init_way();
766
+ init_relation();
767
+ init_ref_elements();
768
+ function parseOptions(options) {
769
+ if (!options) return {
770
+ completeFeature: false,
771
+ renderTagged: false,
772
+ excludeWay: true
773
+ };
774
+ let excludeWay = options.excludeWay === void 0 || options.excludeWay;
775
+ return {
776
+ completeFeature: options.completeFeature ? true : false,
777
+ renderTagged: options.renderTagged ? true : false,
778
+ excludeWay
779
+ };
780
+ }
781
+ function detectFormat(o) {
782
+ if (o.elements) return "json";
783
+ if (o.indexOf("<osm") >= 0) return "xml";
784
+ if (o.trim().startsWith("{")) return "json-raw";
785
+ return "invalid";
786
+ }
787
+ function analyzeFeaturesFromJson(osm, refElements) {
788
+ for (const elem of osm.elements) switch (elem.type) {
789
+ case "node":
790
+ const node = new Node(elem.id, refElements);
791
+ if (elem.tags) node.addTags(elem.tags);
792
+ node.addProps(purgeProps(elem, [
793
+ "id",
794
+ "type",
795
+ "tags",
796
+ "lat",
797
+ "lon"
798
+ ]));
799
+ node.setLatLng(elem);
800
+ break;
801
+ case "way":
802
+ const way = new Way(elem.id, refElements);
803
+ if (elem.tags) way.addTags(elem.tags);
804
+ way.addProps(purgeProps(elem, [
805
+ "id",
806
+ "type",
807
+ "tags",
808
+ "nodes",
809
+ "geometry"
810
+ ]));
811
+ if (elem.geometry) way.setLatLngArray(elem.geometry);
812
+ else if (elem.nodes) for (const n of elem.nodes) way.addNodeRef(n);
813
+ break;
814
+ case "relation":
815
+ const relation = new Relation(elem.id, refElements);
816
+ if (elem.bounds) relation.setBounds([
817
+ parseFloat(elem.bounds.minlon),
818
+ parseFloat(elem.bounds.minlat),
819
+ parseFloat(elem.bounds.maxlon),
820
+ parseFloat(elem.bounds.maxlat)
821
+ ]);
822
+ if (elem.tags) relation.addTags(elem.tags);
823
+ relation.addProps(purgeProps(elem, [
824
+ "id",
825
+ "type",
826
+ "tags",
827
+ "bounds",
828
+ "members"
829
+ ]));
830
+ if (elem.members) for (const member of elem.members) relation.addMember(member);
831
+ default: break;
832
+ }
833
+ }
834
+ function analyzeFeaturesFromXml(osm, refElements) {
835
+ const xmlParser = new XmlParser({ progressive: true });
836
+ xmlParser.on("</osm.node>", (node) => {
837
+ const nd = new Node(node.id, refElements);
838
+ for (const [k, v] of Object.entries(node)) if (!k.startsWith("$") && [
839
+ "id",
840
+ "lon",
841
+ "lat"
842
+ ].indexOf(k) < 0) nd.addProp(k, v);
843
+ nd.setLatLng(node);
844
+ if (node.$innerNodes) {
845
+ for (const ind of node.$innerNodes) if (ind.$tag === "tag") nd.addTag(ind.k, ind.v);
846
+ }
847
+ });
848
+ xmlParser.on("</osm.way>", (node) => {
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
+ };
920
+ }
921
+ module.exports = osm2geojson;
922
+ }));
923
+ //#endregion
924
+ export default require_src();