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
161 lines (160 loc) • 5.1 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
function conditioned(evt) {
return evt.match(/^(.+?)\[(.+?)\]>$/g) !== null;
}
function parseEvent(evt) {
const match = /^(.+?)\[(.+?)\]>$/g.exec(evt);
if (match) {
return { evt: match[1] + '>', exp: match[2] };
}
return { evt };
}
function genConditionFunc(cond) {
const body = 'return ' + cond.replace(/(\$.+?)(?=[=!.])/g, 'node.$&') + ';';
return new Function('node', body);
}
class default_1 {
constructor(opts) {
this.queryParent = false;
this.progressive = false;
this.parentMap = new WeakMap();
if (opts) {
this.queryParent = opts.queryParent ? true : false;
this.progressive = opts.progressive;
if (this.queryParent) {
this.parentMap = new WeakMap();
}
}
this.evtListeners = {};
}
parse(xml, parent, dir) {
dir = dir ? dir + '.' : '';
const nodeRegEx = /<([^ >\/]+)(.*?)>/mg;
const nodes = [];
let nodeMatch = nodeRegEx.exec(xml);
while (nodeMatch) {
const tag = nodeMatch[1];
const node = { $tag: tag };
const fullTag = dir + tag;
const attrText = nodeMatch[2].trim();
let closed = false;
if (attrText.endsWith('/') || tag.startsWith('?') || tag.startsWith('!')) {
closed = true;
}
const attRegEx1 = /([^ ]+?)="(.+?)"/g;
const attRegEx2 = /([^ ]+?)='(.+?)'/g;
let attMatch = attRegEx1.exec(attrText);
let hasAttrs = false;
while (attMatch) {
hasAttrs = true;
node[attMatch[1]] = attMatch[2];
attMatch = attRegEx1.exec(attrText);
}
if (!hasAttrs) {
attMatch = attRegEx2.exec(attrText);
while (attMatch) {
hasAttrs = true;
node[attMatch[1]] = attMatch[2];
attMatch = attRegEx2.exec(attrText);
}
}
if (!hasAttrs && attrText !== '') {
node.text = attrText;
}
if (this.progressive) {
this.emit(`<${fullTag}>`, node, parent);
}
if (!closed) {
const innerRegEx = new RegExp(`([^]+?)<\/${tag}>`, 'g');
innerRegEx.lastIndex = nodeRegEx.lastIndex;
const innerMatch = innerRegEx.exec(xml);
if (innerMatch && innerMatch[1]) {
nodeRegEx.lastIndex = innerRegEx.lastIndex;
const innerNodes = this.parse(innerMatch[1], node, fullTag);
if (innerNodes.length > 0) {
node.$innerNodes = innerNodes;
}
else {
node.$innerText = innerMatch[1];
}
}
}
if (this.queryParent && parent) {
this.parentMap.set(node, parent);
}
if (this.progressive) {
this.emit(`</${fullTag}>`, node, parent);
}
nodes.push(node);
nodeMatch = nodeRegEx.exec(xml);
}
return nodes;
}
getParent(node) {
if (this.queryParent) {
return this.parentMap.get(node);
}
return null;
}
// support javascript condition for the last tag
addListener(evt, func) {
if (conditioned(evt)) {
// func.prototype = evt;
const ev = parseEvent(evt);
if (ev.exp) {
func.condition = genConditionFunc(ev.exp);
}
evt = ev.evt;
}
this.$addListener(evt, func);
}
removeListener(evt, func) {
if (conditioned(evt)) {
const ev = parseEvent(evt);
evt = ev.evt;
}
this.$removeListener(evt, func);
}
on(evt, func) {
this.addListener(evt, func);
}
off(evt, func) {
this.removeListener(evt, func);
}
$addListener(evt, func) {
const funcs = this.evtListeners[evt];
if (funcs) {
funcs.push(func);
}
else {
this.evtListeners[evt] = [func];
}
}
$removeListener(evt, func) {
const funcs = this.evtListeners[evt];
let idx = -1;
if (funcs) {
idx = funcs.indexOf(func);
}
if (idx >= 0) {
funcs.splice(idx, 1);
}
}
emit(evt, ...args) {
const funcs = this.evtListeners[evt];
if (funcs) {
for (const func of funcs) {
if (func.condition) {
if (func.condition.apply(null, args) === true) {
func.apply(null, args);
}
}
else {
func.apply(null, args);
}
}
}
}
}
exports.default = default_1;