UNPKG

osm2geojson-lite

Version:

a lightweight yet faster osm (either in xml or in json formats) to geojson convertor - 4x faster than xmldom + osmtogeojson in most situations - implemented in pure JavaScript without any 3rd party dependency

223 lines (222 loc) 7.53 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.WayCollection = exports.RefElements = exports.LateBinder = exports.strToFloat = exports.ptInsidePolygon = exports.ringDirection = exports.isRing = exports.coordsToKey = exports.last = exports.first = void 0; exports.purgeProps = purgeProps; exports.mergeProps = mergeProps; exports.addPropToFeature = addPropToFeature; exports.addPropToFeatures = addPropToFeatures; exports.addToMap = addToMap; exports.removeFromMap = removeFromMap; exports.getFirstFromMap = getFirstFromMap; function purgeProps(obj, blacklist) { if (obj) { const rs = Object.assign({}, obj); if (blacklist) { for (const prop of blacklist) { delete rs[prop]; } } return rs; } return {}; } function mergeProps(obj1, obj2) { obj1 = obj1 ? obj1 : {}; obj2 = obj2 ? obj2 : {}; return Object.assign(obj1, obj2); } function addPropToFeature(f, k, v) { if (f.properties && k && v) { f.properties[k] = v; } } function addPropToFeatures(fs, k, v) { for (const f of fs) { addPropToFeature(f, k, v); } } const first = (a) => a[0]; exports.first = first; const last = (a) => a[a.length - 1]; exports.last = last; const coordsToKey = (a) => a.join(','); exports.coordsToKey = coordsToKey; function addToMap(m, k, v) { const a = m[k]; if (a) { a.push(v); } else { m[k] = [v]; } } function removeFromMap(m, k, v) { const a = m[k]; let idx = -1; if (a) { idx = a.indexOf(v); } if (idx >= 0) { a.splice(idx, 1); } } function getFirstFromMap(m, k) { const a = m[k]; if (a && a.length > 0) { return a[0]; } return null; } // need 3+ different points to form a ring, here using > 3 is 'coz a the first and the last points are actually the same const isRing = (a) => a.length > 3 && (0, exports.coordsToKey)((0, exports.first)(a)) === (0, exports.coordsToKey)((0, exports.last)(a)); exports.isRing = isRing; const ringDirection = (a, xIdx, yIdx) => { xIdx = xIdx || 0, yIdx = yIdx || 1; // get the index of the point which has the maximum x value const m = a.reduce((maxxIdx, v, idx) => a[maxxIdx][xIdx || 0] > v[xIdx || 0] ? maxxIdx : idx, 0); // 'coz the first point is virtually the same one as the last point, // we need to skip a.length - 1 for left when m = 0, // and skip 0 for right when m = a.length - 1; const l = m <= 0 ? a.length - 2 : m - 1; const r = m >= a.length - 1 ? 1 : m + 1; const xa = a[l][xIdx]; const xb = a[m][xIdx]; const xc = a[r][xIdx]; const ya = a[l][yIdx]; const yb = a[m][yIdx]; const yc = a[r][yIdx]; const det = (xb - xa) * (yc - ya) - (xc - xa) * (yb - ya); return det < 0 ? 'clockwise' : 'counterclockwise'; }; exports.ringDirection = ringDirection; const ptInsidePolygon = (pt, polygon, xIdx, yIdx) => { xIdx = xIdx || 0, yIdx = yIdx || 1; let result = false; 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; } } return result; }; exports.ptInsidePolygon = ptInsidePolygon; const strToFloat = (el) => el instanceof Array ? el.map(exports.strToFloat) : parseFloat(el); exports.strToFloat = strToFloat; class LateBinder { constructor(container, valueFunc, ctx, args) { this.container = container; this.valueFunc = valueFunc; this.ctx = ctx; this.args = args; } bind() { const v = this.valueFunc.apply(this.ctx, this.args); if (this.container instanceof Array) { const idx = this.container.indexOf(this); if (idx >= 0) { const args = [idx, 1]; if (v) { args.push(v); } Array.prototype.splice.apply(this.container, args); } } else if (typeof this.container === 'object' && !Array.isArray(this.container)) { const container = this.container; const k = Object.keys(container).find((nv) => container[nv] === this); if (k) { if (v) { container[k] = v; } else { delete container[k]; } } } } } exports.LateBinder = LateBinder; class RefElements extends Map { constructor() { super(); this.binders = []; } add(k, v) { this.set(k, v); } addBinder(binder) { this.binders.push(binder); } bindAll() { this.binders.forEach((binder) => binder.bind()); } } exports.RefElements = RefElements; 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, (0, exports.coordsToKey)((0, exports.first)(w)), w); addToMap(this.lastMap, (0, exports.coordsToKey)((0, exports.last)(w)), w); } } toStrings() { const strings = []; let way = this.shift(); while (way) { removeFromMap(this.firstMap, (0, exports.coordsToKey)((0, exports.first)(way)), way); removeFromMap(this.lastMap, (0, exports.coordsToKey)((0, exports.last)(way)), way); let current = way; let next; do { const key = (0, exports.coordsToKey)((0, exports.last)(current)); let shouldReverse = false; next = getFirstFromMap(this.firstMap, key); if (!next) { next = getFirstFromMap(this.lastMap, key); shouldReverse = true; } if (next) { this.splice(this.indexOf(next), 1); removeFromMap(this.firstMap, (0, exports.coordsToKey)((0, exports.first)(next)), next); removeFromMap(this.lastMap, (0, exports.coordsToKey)((0, exports.last)(next)), next); if (shouldReverse) { // always reverse shorter one to save time if (next.length > current.length) { [current, next] = [next, current]; } next.reverse(); } current = current.concat(next.slice(1)); } } while (next); strings.push((0, exports.strToFloat)(current)); way = this.shift(); } return strings; } toRings(direction) { const strings = this.toStrings(); const rings = []; let str = strings.shift(); while (str) { if ((0, exports.isRing)(str)) { if ((0, exports.ringDirection)(str) !== direction) { str.reverse(); } rings.push(str); } str = strings.shift(); } return rings; } } exports.WayCollection = WayCollection;