Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 48 additions & 10 deletions src/building.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,8 @@ class Building {
const nodelist = Building.buildNodeList(xmlData);
const extents = Building.getExtents(id, xmlData, nodelist);
const innerData = await Building.getInnerData(...extents);
return new Building(id, innerData);
const [augmentedNodelist, augmentedWays] = await Building.buildAugmentedData(innerData);
return new Building(id, innerData, augmentedNodelist, augmentedWays);
}

/**
Expand All @@ -52,9 +53,11 @@ class Building {
* @param {string} id - the unique XML id of the object.
* @param {string} FullXmlData - XML data.
*/
constructor(id, FullXmlData) {
constructor(id, FullXmlData, augmentedNodelist, augmentedWays) {
this.id = id;
this.fullXmlData = new window.DOMParser().parseFromString(FullXmlData, 'text/xml');
this.augmentedNodelist = augmentedNodelist;
this.augmentedWays = augmentedWays;
const outerElementXml = this.fullXmlData.getElementById(id);
if (outerElementXml.tagName.toLowerCase() === 'way') {
this.type = 'way';
Expand All @@ -68,17 +71,17 @@ class Building {
this.setHome();
this.repositionNodes();
if (this.type === 'way') {
this.outerElement = new BuildingPart(id, this.fullXmlData, this.nodelist);
this.outerElement = new BuildingPart(id, this.fullXmlData, this.nodelist, this.augmentedNodelist, this.augmentedWays);
} else if (this.type === 'multipolygon') {
this.outerElement = new MultiBuildingPart(id, this.fullXmlData, this.nodelist);
this.outerElement = new MultiBuildingPart(id, this.fullXmlData, this.nodelist, this.augmentedNodelist, this.augmentedWays);
} else {
const outlineRef = outerElementXml.querySelector('member[role="outline"]').getAttribute('ref');
const outline = this.fullXmlData.getElementById(outlineRef);
const outlineType = outline.tagName.toLowerCase();
if (outlineType === 'way') {
this.outerElement = new BuildingPart(id, this.fullXmlData, this.nodelist);
this.outerElement = new BuildingPart(id, this.fullXmlData, this.nodelist, this.augmentedNodelist, this.augmentedWays);
} else {
this.outerElement = new MultiBuildingPart(outlineRef, this.fullXmlData, this.nodelist);
this.outerElement = new MultiBuildingPart(outlineRef, this.fullXmlData, this.nodelist, this.augmentedNodelist, this.augmentedWays);
}
}
this.addParts();
Expand Down Expand Up @@ -121,6 +124,34 @@ class Building {
return nodeList;
}


/**
* @param {DOM.Element} fullXmlData - OSM XML with nodes
* @return {Promise<({}|*)[]>}
*/
static async buildAugmentedData(fullXmlData) {
const xmlData = new DOMParser().parseFromString(fullXmlData, 'text/xml');
const completedWays = new Set(Array.from(xmlData.getElementsByTagName('way')).map(i => i.getAttribute('id')));
const memberWays = xmlData.querySelectorAll('member[type="way"]');
const nodeList = {};
const waysList = {};
await Promise.all(Array.from(memberWays).map(async currentWay => {
const wayID = currentWay.getAttribute('ref');
if (completedWays.has(wayID)) {
return;
}
window.printError('Additional downloading way ' + wayID);
const wayData = new DOMParser().parseFromString(await Building.getWayData(wayID), 'text/xml');
window.printError(`Way ${wayID} was downloaded`);
waysList[wayID] = wayData.querySelector('way');
wayData.querySelectorAll('node').forEach(i => {
nodeList[i.getAttribute('id')] = [i.getAttribute('lon'), i.getAttribute('lat')];
});
}));
return [nodeList, waysList];
}


/**
* convert all the longitude latitude values
* to meters from the home point.
Expand All @@ -129,6 +160,9 @@ class Building {
for (const key in this.nodelist) {
this.nodelist[key] = BuildingShapeUtils.repositionPoint(this.nodelist[key], this.home);
}
for (const key in this.augmentedNodelist) {
this.augmentedNodelist[key] = BuildingShapeUtils.repositionPoint(this.augmentedNodelist[key], this.home);
}
}

/**
Expand Down Expand Up @@ -158,9 +192,9 @@ class Building {
const ref = parts[i].getAttribute('ref');
const part = this.fullXmlData.getElementById(ref);
if (part.tagName.toLowerCase() === 'way') {
this.parts.push(new BuildingPart(ref, this.fullXmlData, this.nodelist, this.outerElement.options));
this.parts.push(new BuildingPart(ref, this.fullXmlData, this.nodelist, this.augmentedNodelist, this.augmentedWays, this.outerElement.options));
} else {
this.parts.push(new MultiBuildingPart(ref, this.fullXmlData, this.nodelist, this.outerElement.options));
this.parts.push(new MultiBuildingPart(ref, this.fullXmlData, this.nodelist, this.augmentedNodelist, this.augmentedWays, this.outerElement.options));
}
}
} else {
Expand All @@ -169,15 +203,19 @@ class Building {
for (let j = 0; j < parts.length; j++) {
if (parts[j].querySelector('[k="building:part"]')) {
const id = parts[j].getAttribute('id');
this.parts.push(new BuildingPart(id, this.fullXmlData, this.nodelist, this.outerElement.options));
this.parts.push(new BuildingPart(id, this.fullXmlData, this.nodelist, this.augmentedNodelist, this.augmentedWays, this.outerElement.options));
}
}
// Filter all relations
parts = this.fullXmlData.getElementsByTagName('relation');
for (let i = 0; i < parts.length; i++) {
if (parts[i].querySelector('[k="building:part"]')) {
const id = parts[i].getAttribute('id');
this.parts.push(new MultiBuildingPart(id, this.fullXmlData, this.nodelist, this.outerElement.options));
try {
this.parts.push(new MultiBuildingPart(id, this.fullXmlData, this.nodelist, this.augmentedNodelist, this.augmentedWays, this.outerElement.options));
} catch (e) {
window.printError(e);
}
}
}
}
Expand Down
10 changes: 7 additions & 3 deletions src/buildingpart.js
Original file line number Diff line number Diff line change
Expand Up @@ -66,9 +66,11 @@ class BuildingPart {
* @param {number} id - the OSM id of the way or multipolygon.
* @param {XMLDocument} fullXmlData - XML for the region.
* @param {[[number, number],...]} nodelist - Cartesian coordinates of each node keyed by node refID
* @param {object} options - default values for the building part.
* @param augmentedNodelist - list of nodes outside bbox
* @param augmentedWays - list of ways outside bbox
* @param {object} defaultOptions - default values for the building part.
*/
constructor(id, fullXmlData, nodelist, defaultOptions = {}) {
constructor(id, fullXmlData, nodelist, augmentedNodelist = {}, augmentedWays = {}, defaultOptions = {}) {
this.options = this.blankOptions;
if (Object.keys(defaultOptions).length === 0) {
defaultOptions = this.blankOptions;
Expand All @@ -78,6 +80,8 @@ class BuildingPart {
this.id = id;
this.way = fullXmlData.getElementById(id);
this.nodelist = nodelist;
this.augmentedNodelist = augmentedNodelist;
this.augmentedWays = augmentedWays;
this.shape = this.buildShape();
this.setOptions();
}
Expand All @@ -89,7 +93,7 @@ class BuildingPart {
*/
buildShape() {
this.type = 'way';
return BuildingShapeUtils.createShape(this.way, this.nodelist);
return BuildingShapeUtils.createShape(this.way, this.nodelist, this.augmentedNodelist);
}

/**
Expand Down
5 changes: 3 additions & 2 deletions src/extras/BuildingShapeUtils.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,11 @@ class BuildingShapeUtils extends ShapeUtils {
*
* @param {DOM.Element} way - OSM XML way element.
* @param {[number, number]} nodelist - list of all nodes
* @param augmentedNodelist - list of nodes outside bbox
*
* @return {THREE.Shape} shape - the shape
*/
static createShape(way, nodelist) {
static createShape(way, nodelist, augmentedNodelist = {}) {
// Initialize objects
const shape = new Shape();
var ref;
Expand All @@ -25,7 +26,7 @@ class BuildingShapeUtils extends ShapeUtils {
// Get the coordinates of all the nodes and add them to the shape outline.
for (let i = 0; i < elements.length; i++) {
ref = elements[i].getAttribute('ref');
node = nodelist[ref];
node = nodelist[ref] ?? augmentedNodelist[ref];
// The first node requires a differnet function call.
if (i === 0) {
shape.moveTo(parseFloat(node[0]), parseFloat(node[1]));
Expand Down
28 changes: 20 additions & 8 deletions src/multibuildingpart.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,22 +14,34 @@ class MultiBuildingPart extends BuildingPart {
*/
buildShape() {
this.type = 'multipolygon';
const innerMembers = this.way.querySelectorAll('member[role="inner"]');
const outerMembers = this.way.querySelectorAll('member[role="outer"]');
const innerMembers = this.way.querySelectorAll('member[role="inner"][type="way"]');
const outerMembers = this.way.querySelectorAll('member[role="outer"][type="way"]');
const innerShapes = [];
var shapes = [];
const shapes = [];
for (let i = 0; i < innerMembers.length; i++) {
const way = this.fullXmlData.getElementById(innerMembers[i].getAttribute('ref'));
innerShapes.push(BuildingShapeUtils.createShape(way, this.nodelist));
const wayID = innerMembers[i].getAttribute('ref');
const way = this.fullXmlData.getElementById(wayID);
if (way) {
innerShapes.push(BuildingShapeUtils.createShape(way, this.nodelist, this.augmentedNodelist));
} else {
window.printError(`Missing way ${wayID} for relation ${this.id} for inner members`);
innerShapes.push(BuildingShapeUtils.createShape(this.augmentedWays[wayID], this.nodelist, this.augmentedNodelist));
}
}
const ways = [];
for (let j = 0; j < outerMembers.length; j++) {
const way = this.fullXmlData.getElementById(outerMembers[j].getAttribute('ref'));
ways.push(way);
const wayID = outerMembers[j].getAttribute('ref');
const way = this.fullXmlData.getElementById(wayID);
if (way) {
ways.push(way);
} else {
window.printError(`Missing way ${wayID} for relation ${this.id}`);
ways.push(this.augmentedWays[wayID]);
}
}
const closedWays = BuildingShapeUtils.combineWays(ways);
for (let k = 0; k < closedWays.length; k++) {
const shape = BuildingShapeUtils.createShape(closedWays[k], this.nodelist);
const shape = BuildingShapeUtils.createShape(closedWays[k], this.nodelist, this.augmentedNodelist);
shape.holes.push(...innerShapes);
shapes.push(shape);
}
Expand Down
2 changes: 1 addition & 1 deletion test/multipolygon.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ const data = `
<node id="6" lat="4.001" lon="4.001"/>
<node id="7" lat="4.001" lon="4"/>
<relation id="4">
<member ref="1" role="outer"/>
<member ref="1" role="outer" type="way"/>
<tag k="type" v="multipolygon"/>
<tag k="building" v="yes"/>
<tag k="roof:shape" v="skillion"/>
Expand Down
6 changes: 3 additions & 3 deletions test/split_way_multipolygon.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,9 @@ const data = `
<node id="9" lat="4.00025" lon="4.00075"/>
<node id="10" lat="4.00075" lon="4.0005"/>
<relation id="5">
<member ref="1" role="outer"/>
<member ref="3" role="outer"/>
<member ref="2" role="inner"/>
<member ref="1" role="outer" type="way"/>
<member ref="3" role="outer" type="way"/>
<member ref="2" role="inner" type="way"/>
<tag k="type" v="multipolygon"/>
<tag k="building" v="yes"/>
<tag k="roof:shape" v="skillion"/>
Expand Down
6 changes: 3 additions & 3 deletions test/split_way_multipolygon_reverse.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,9 @@ const data = `
<node id="9" lat="4.00025" lon="4.00075"/>
<node id="10" lat="4.00075" lon="4.0005"/>
<relation id="5">
<member ref="1" role="outer"/>
<member ref="3" role="outer"/>
<member ref="2" role="inner"/>
<member ref="1" role="outer" type="way"/>
<member ref="3" role="outer" type="way"/>
<member ref="2" role="inner" type="way"/>
<tag k="type" v="multipolygon"/>
<tag k="building" v="yes"/>
<tag k="roof:shape" v="skillion"/>
Expand Down