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
206 lines (205 loc) • 7.7 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.Relation = void 0;
const osm_object_1 = require("./osm-object");
const way_1 = require("./way");
const node_1 = require("./node");
const way_collection_1 = require("./way-collection");
const late_binder_1 = require("./late-binder");
const utils_1 = require("./utils");
class Relation extends osm_object_1.OsmObject {
constructor(id, refElems) {
super('relation', id, refElems);
this.relations = [];
this.nodes = [];
this.bounds = undefined;
this.ways = [];
this.roles = [];
}
setBounds(bounds) {
this.bounds = bounds;
}
addMember(member) {
switch (member.type) {
// super relation, need to do combination
case 'relation':
let binder = new late_binder_1.LateBinder(this.relations, (id) => {
const relation = this.refElems.get(`relation/${id}`);
if (relation) {
relation.refCount++;
return relation;
}
}, this, [member.ref]);
this.relations.push(binder);
this.refElems.addBinder(binder);
break;
case 'way':
if (!member.role) {
member.role = '';
}
if (member.geometry) {
const way = new way_1.Way(member.ref, this.refElems);
way.setLatLngArray(member.geometry);
way.refCount++;
this.ways.push(way);
this.roles.push(member.role);
}
else if (member.nodes) {
const way = new way_1.Way(member.ref, this.refElems);
for (const nid of member.nodes) {
way.addNodeRef(nid);
}
way.refCount++;
this.ways.push(way);
this.roles.push(member.role);
}
else {
let binder = new late_binder_1.LateBinder(this.ways, (nid) => {
const way = this.refElems.get(`way/${nid}`);
if (way) {
way.refCount++;
return way;
}
}, this, [member.ref]);
this.ways.push(binder);
this.roles.push(member.role);
this.refElems.addBinder(binder);
}
break;
case 'node':
let node = null;
if (member.lat && member.lon) {
node = new node_1.Node(member.ref, this.refElems);
node.setLatLng({ lon: member.lon, lat: member.lat });
if (member.tags) {
node.addTags(member.tags);
}
for (const [k, v] of Object.entries(member)) {
if (['id', 'type', 'lat', 'lon'].indexOf(k) < 0) {
node.addProp(k, v);
}
}
node.refCount++;
this.nodes.push(node);
}
else {
let binder = new late_binder_1.LateBinder(this.nodes, (id) => {
const nn = this.refElems.get(`node/${id}`);
if (nn) {
nn.refCount++;
return nn;
}
}, this, [member.ref]);
this.nodes.push(binder);
this.refElems.addBinder(binder);
}
break;
}
}
constructStringGeometry(ws) {
const strings = ws ? ws.mergeWays() : [];
if (strings.length === 0) {
return null;
}
return {
type: 'MultiLineString',
coordinates: strings,
};
}
constructPolygonGeometry(ows, iws) {
const outerRings = ows ? ows.toRings('counterclockwise') : [];
const innerRings = iws ? iws.toRings('clockwise') : [];
if (outerRings.length > 0) {
const compositPolyons = [];
let ring;
for (ring of outerRings) {
compositPolyons.push([ring]);
}
// link inner polygons to outer containers
ring = innerRings.shift();
while (ring) {
for (const idx in outerRings) {
if ((0, utils_1.pointInsidePolygon)((0, utils_1.first)(ring), outerRings[idx])) {
compositPolyons[idx].push(ring);
break;
}
}
ring = innerRings.shift();
}
// construct the Polygon/MultiPolygon geometry
if (compositPolyons.length === 1) {
return {
type: 'Polygon',
coordinates: compositPolyons[0],
};
}
return {
type: 'MultiPolygon',
coordinates: compositPolyons,
};
}
return null;
}
toFeatureArray() {
const polygonFeatures = [];
const stringFeatures = [];
let pointFeatures = [];
for (const relation of this.relations) {
if (!relation) {
continue;
}
for (let i = 0; i < relation.ways.length; i++) {
const way = relation.ways[i];
this.ways.push(way);
this.roles.push(relation.roles[i]);
}
}
let templateFeature = {
type: 'Feature',
id: this.getCompositeId(),
bbox: this.bounds,
properties: this.getProps(),
geometry: null
};
if (!this.bounds) {
delete templateFeature.bbox;
}
if (this.roles.some((r) => r === 'outer')) {
const outerWayCollection = new way_collection_1.WayCollection();
const innerWayCollection = new way_collection_1.WayCollection();
for (let i = 0; i < this.ways.length; i++) {
const way = this.ways[i];
const role = this.roles[i];
if (role === 'outer') {
outerWayCollection.addWay(way);
}
else if (role === 'inner') {
innerWayCollection.addWay(way);
}
}
let feature = Object.assign({}, templateFeature);
let geometry = this.constructPolygonGeometry(outerWayCollection, innerWayCollection);
if (geometry) {
feature.geometry = geometry;
polygonFeatures.push(feature);
}
}
else {
const wayCollection = new way_collection_1.WayCollection();
for (let way of this.ways) {
wayCollection.addWay(way);
}
let geometry = this.constructStringGeometry(wayCollection);
if (geometry) {
let feature = Object.assign({}, templateFeature);
feature.geometry = geometry;
stringFeatures.push(feature);
}
}
for (let node of this.nodes) {
pointFeatures = pointFeatures.concat(node.toFeatureArray());
}
return [...polygonFeatures, ...stringFeatures, ...pointFeatures];
}
}
exports.Relation = Relation;