UNPKG

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

188 lines (187 loc) 9.01 kB
import { parse } from "txml"; import { purgeProps } from "./utils.js"; import { Node } from "./node.js"; import { Output } from "./output.js"; import { Way } from "./way.js"; import { Relation } from "./relation.js"; function setTagsFromXML(elNode, obj) { for (const elChild of elNode.children) { if (elChild.tagName === "tag") { obj.addTag(elChild.attributes.k, elChild.attributes.v); } } } export default function analyzeFeaturesFromXml(osm, refElements) { const parsed = parse(osm, { noChildNodes: [] }); for (const rootNode of parsed) { if (rootNode.tagName !== "osm") continue; for (const elNode of rootNode.children) { // Check children for evidence this is a derived element and process if so if (elNode.children.find((c) => ["point", "vertex", "linestring", "group"].includes(c.tagName))) { // TODO: other derived output geoms? const obj = new Output(elNode.tagName, elNode.attributes.id, refElements); obj.addMetas(purgeProps(elNode.attributes, [ "id", "type", "tags", "geometry", ])); setTagsFromXML(elNode, obj); const coordinates = []; const geometries = []; for (const elChild of elNode.children) { switch (elChild.tagName) { case "point": obj.setGeometry({ type: "Point", coordinates: [ parseFloat(elChild.attributes.lon), parseFloat(elChild.attributes.lat), ], }); break; case "vertex": coordinates.push([ parseFloat(elChild.attributes.lon), parseFloat(elChild.attributes.lat), ]); break; case "linestring": const ring = []; for (const vertex of elChild.children) { ring.push([ parseFloat(vertex.attributes.lon), parseFloat(vertex.attributes.lat), ]); } obj.setGeometry({ type: "Polygon", coordinates: [ring] }); break; case "group": const groupCoords = []; for (const groupChild of elChild.children) { switch (groupChild.tagName) { case "point": geometries.push({ type: "Point", coordinates: [ parseFloat(groupChild.attributes.lon), parseFloat(groupChild.attributes.lat), ], }); break; case "vertex": groupCoords.push([ parseFloat(groupChild.attributes.lon), parseFloat(groupChild.attributes.lat), ]); break; } } if (groupCoords.length > 0) { geometries.push({ type: "LineString", coordinates: groupCoords, }); } break; } } if (coordinates.length > 0) { obj.setGeometry({ type: "LineString", coordinates }); } else if (geometries.length > 0) { obj.setGeometry({ type: "GeometryCollection", geometries }); } continue; } // handle nodes/ways/relations switch (elNode.tagName) { case "node": const node = new Node(elNode.attributes.id, refElements); setTagsFromXML(elNode, node); node.addMetas(purgeProps(elNode.attributes, [ "id", "lon", "lat", ])); node.setLatLng(elNode.attributes); break; case "way": const way = new Way(elNode.attributes.id, refElements); setTagsFromXML(elNode, way); way.addMetas(purgeProps(elNode.attributes, [ "id", "type", ])); for (const elChild of elNode.children) { switch (elChild.tagName) { case "center": way.setCenter(elChild.attributes); break; case "nd": if (elChild.attributes.lon && elChild.attributes.lat) { way.addLatLng(elChild.attributes); } else { way.addNodeRef(elChild.attributes.ref); } break; } } break; case "relation": const rel = new Relation(elNode.attributes.id, refElements); setTagsFromXML(elNode, rel); for (const elChild of elNode.children) { switch (elChild.tagName) { case "center": rel.setCenter(elChild.attributes); break; case "member": const member = { type: elChild.attributes.type, role: elChild.attributes.role || "", ref: elChild.attributes.ref, }; if (elChild.attributes.type === "node" && elChild.attributes.lon && elChild.attributes.lat) { member.lon = elChild.attributes.lon; member.lat = elChild.attributes.lat; } else { const geometry = []; const nodes = []; for (const memChild of elChild.children) { if (memChild.attributes.lon && memChild.attributes.lat) { geometry.push(memChild.attributes); } else if (memChild.attributes.ref) { nodes.push(memChild.attributes.ref); } } if (geometry.length > 0) { member.geometry = geometry; } else if (nodes.length > 0) { member.nodes = nodes; } } rel.addMember(member); break; case "bounds": rel.setBounds([ parseFloat(elChild.attributes.minlon), parseFloat(elChild.attributes.minlat), parseFloat(elChild.attributes.maxlon), parseFloat(elChild.attributes.maxlat), ]); break; } } break; } } } }