osm2geojson-ultra
Version:
a faster & more complete OSM & Overpass (either in xml or in json formats) to geojson convertor - 4x faster than xmldom + osmtogeojson in most situations - implemented in TypeScript with txml for XML parsing
105 lines (104 loc) • 4.01 kB
JavaScript
import { addToMap, coordsToKey, first, getFirstFromMap, isRing, last, removeFromMap, ringDirection, strArrayArrayToFloat, } from "./utils.js";
export class WayCollection extends Array {
constructor() {
super();
this.firstMap = {};
this.lastMap = {};
}
addWay(way) {
const w = way.toCoordsArray();
if (w.length > 0) {
this.push(w);
addToMap(this.firstMap, coordsToKey(first(w)), w);
addToMap(this.lastMap, coordsToKey(last(w)), w);
}
}
mergeWays() {
const strings = [];
let way = this.shift();
while (way) {
removeFromMap(this.firstMap, coordsToKey(first(way)), way);
removeFromMap(this.lastMap, coordsToKey(last(way)), way);
let current = way;
let next;
do {
let nextWay = this.getNextWay(current);
next = nextWay.next;
let mergeType = nextWay.mergeType;
if (!next) {
continue;
}
this.splice(this.indexOf(next), 1);
removeFromMap(this.firstMap, coordsToKey(first(next)), next);
removeFromMap(this.lastMap, coordsToKey(last(next)), next);
switch (mergeType) {
case 0 /* MergeType.EndStart */:
current = current.concat(next.slice(1));
break;
case 1 /* MergeType.EndEnd */:
next.reverse();
current = current.concat(next.slice(1));
break;
case 2 /* MergeType.StartStart */:
current.reverse();
current = current.concat(next.slice(1));
break;
case 3 /* MergeType.StartEnd */:
current = next.concat(current.slice(1));
break;
}
} while (next);
strings.push(strArrayArrayToFloat(current));
way = this.shift();
}
return strings;
}
/**
* Try to find the next way to add to the current way.
* It first tries the next way in the array, and if this doesn't work, try any other way.
*/
getNextWay(current) {
const lastKey = coordsToKey(last(current));
const firstKey = coordsToKey(first(current));
// Step 1: Prefer the next way in the array if it connects
let next = this.length > 0 ? this[0] : null;
if (next) {
const nextFirstKey = coordsToKey(first(next));
const nextLastKey = coordsToKey(last(next));
if (lastKey === nextFirstKey) {
return { next, mergeType: 0 /* MergeType.EndStart */ };
}
if (lastKey === nextLastKey) {
return { next, mergeType: 1 /* MergeType.EndEnd */ };
}
if (firstKey === nextFirstKey) {
return { next, mergeType: 2 /* MergeType.StartStart */ };
}
if (firstKey === nextLastKey) {
return { next, mergeType: 3 /* MergeType.StartEnd */ };
}
}
// Step 2: Fallback to map-based lookup if no sequential match
next = getFirstFromMap(this.firstMap, lastKey);
if (next) {
return { next, mergeType: 0 /* MergeType.EndStart */ };
}
next = getFirstFromMap(this.lastMap, lastKey);
return { next, mergeType: 1 /* MergeType.EndEnd */ };
}
toRings(direction) {
const strings = this.mergeWays();
const rings = [];
let str = strings.shift();
while (str) {
if (isRing(str)) {
if (ringDirection(str) !== direction) {
str.reverse();
}
rings.push(str);
}
str = strings.shift();
}
return rings;
}
}