Skip to content

Commit ddde11f

Browse files
committed
#80 make data preprocessing more error-tolerant. Download incompleted ways
1 parent 6e46da8 commit ddde11f

File tree

4 files changed

+66
-21
lines changed

4 files changed

+66
-21
lines changed

src/building.js

Lines changed: 44 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,8 @@ class Building {
4343
const nodelist = Building.buildNodeList(xmlData);
4444
const extents = Building.getExtents(id, xmlData, nodelist);
4545
const innerData = await Building.getInnerData(...extents);
46-
return new Building(id, innerData);
46+
const [augmentedNodelist, augmentedWays] = await Building.buildAugmentedData(innerData)
47+
return new Building(id, innerData, augmentedNodelist, augmentedWays);
4748
}
4849

4950
/**
@@ -52,9 +53,11 @@ class Building {
5253
* @param {string} id - the unique XML id of the object.
5354
* @param {string} FullXmlData - XML data.
5455
*/
55-
constructor(id, FullXmlData) {
56+
constructor(id, FullXmlData, augmentedNodelist, augmentedWays) {
5657
this.id = id;
5758
this.fullXmlData = new window.DOMParser().parseFromString(FullXmlData, 'text/xml');
59+
this.augmentedNodelist = augmentedNodelist;
60+
this.augmentedWays = augmentedWays;
5861
const outerElementXml = this.fullXmlData.getElementById(id);
5962
if (outerElementXml.tagName.toLowerCase() === 'way') {
6063
this.type = 'way';
@@ -68,17 +71,17 @@ class Building {
6871
this.setHome();
6972
this.repositionNodes();
7073
if (this.type === 'way') {
71-
this.outerElement = new BuildingPart(id, this.fullXmlData, this.nodelist);
74+
this.outerElement = new BuildingPart(id, this.fullXmlData, this.nodelist, this.augmentedNodelist, this.augmentedWays);
7275
} else if (this.type === 'multipolygon') {
73-
this.outerElement = new MultiBuildingPart(id, this.fullXmlData, this.nodelist);
76+
this.outerElement = new MultiBuildingPart(id, this.fullXmlData, this.nodelist, this.augmentedNodelist, this.augmentedWays);
7477
} else {
7578
const outlineRef = outerElementXml.querySelector('member[role="outline"]').getAttribute('ref');
7679
const outline = this.fullXmlData.getElementById(outlineRef);
7780
const outlineType = outline.tagName.toLowerCase();
7881
if (outlineType === 'way') {
79-
this.outerElement = new BuildingPart(id, this.fullXmlData, this.nodelist);
82+
this.outerElement = new BuildingPart(id, this.fullXmlData, this.nodelist, this.augmentedNodelist, this.augmentedWays);
8083
} else {
81-
this.outerElement = new MultiBuildingPart(outlineRef, this.fullXmlData, this.nodelist);
84+
this.outerElement = new MultiBuildingPart(outlineRef, this.fullXmlData, this.nodelist, this.augmentedNodelist, this.augmentedWays);
8285
}
8386
}
8487
this.addParts();
@@ -121,6 +124,34 @@ class Building {
121124
return nodeList;
122125
}
123126

127+
128+
/**
129+
* @param {DOM.Element} fullXmlData - OSM XML with nodes
130+
* @return {Promise<({}|*)[]>}
131+
*/
132+
static async buildAugmentedData(fullXmlData) {
133+
const xmlData = new DOMParser().parseFromString(fullXmlData, 'text/xml');
134+
const completedWays = new Set(Array.from(xmlData.getElementsByTagName('way')).map(i => i.getAttribute('id')));
135+
const memberWays = xmlData.querySelectorAll('member[type="way"]');
136+
const nodeList = {};
137+
const waysList = {};
138+
await Promise.all(Array.from(memberWays).map(async currentWay => {
139+
const wayID = currentWay.getAttribute('ref');
140+
if (completedWays.has(wayID)) {
141+
return
142+
}
143+
printError('Additional downloading way ' + wayID);
144+
const wayData = new DOMParser().parseFromString(await Building.getWayData(wayID), 'text/xml');
145+
printError(`Way ${wayID} was downloaded`);
146+
waysList[wayID] = wayData.querySelector('way');
147+
wayData.querySelectorAll('node').forEach(i => {
148+
nodeList[i.getAttribute('id')] = [i.getAttribute('lon'), i.getAttribute('lat')];
149+
});
150+
}))
151+
return [nodeList, waysList];
152+
}
153+
154+
124155
/**
125156
* convert all the longitude latitude values
126157
* to meters from the home point.
@@ -129,6 +160,9 @@ class Building {
129160
for (const key in this.nodelist) {
130161
this.nodelist[key] = BuildingShapeUtils.repositionPoint(this.nodelist[key], this.home);
131162
}
163+
for (const key in this.augmentedNodelist) {
164+
this.augmentedNodelist[key] = BuildingShapeUtils.repositionPoint(this.augmentedNodelist[key], this.home);
165+
}
132166
}
133167

134168
/**
@@ -158,9 +192,9 @@ class Building {
158192
const ref = parts[i].getAttribute('ref');
159193
const part = this.fullXmlData.getElementById(ref);
160194
if (part.tagName.toLowerCase() === 'way') {
161-
this.parts.push(new BuildingPart(ref, this.fullXmlData, this.nodelist, this.outerElement.options));
195+
this.parts.push(new BuildingPart(ref, this.fullXmlData, this.nodelist, this.augmentedNodelist, this.augmentedWays, this.outerElement.options));
162196
} else {
163-
this.parts.push(new MultiBuildingPart(ref, this.fullXmlData, this.nodelist, this.outerElement.options));
197+
this.parts.push(new MultiBuildingPart(ref, this.fullXmlData, this.nodelist, this.augmentedNodelist, this.augmentedWays, this.outerElement.options));
164198
}
165199
}
166200
} else {
@@ -169,15 +203,15 @@ class Building {
169203
for (let j = 0; j < parts.length; j++) {
170204
if (parts[j].querySelector('[k="building:part"]')) {
171205
const id = parts[j].getAttribute('id');
172-
this.parts.push(new BuildingPart(id, this.fullXmlData, this.nodelist, this.outerElement.options));
206+
this.parts.push(new BuildingPart(id, this.fullXmlData, this.nodelist, this.augmentedNodelist, this.augmentedWays, this.outerElement.options));
173207
}
174208
}
175209
// Filter all relations
176210
parts = this.fullXmlData.getElementsByTagName('relation');
177211
for (let i = 0; i < parts.length; i++) {
178212
if (parts[i].querySelector('[k="building:part"]')) {
179213
const id = parts[i].getAttribute('id');
180-
this.parts.push(new MultiBuildingPart(id, this.fullXmlData, this.nodelist, this.outerElement.options));
214+
this.parts.push(new MultiBuildingPart(id, this.fullXmlData, this.nodelist, this.augmentedNodelist, this.augmentedWays, this.outerElement.options));
181215
}
182216
}
183217
}

src/buildingpart.js

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ class BuildingPart {
6868
* @param {[[number, number],...]} nodelist - Cartesian coordinates of each node keyed by node refID
6969
* @param {object} options - default values for the building part.
7070
*/
71-
constructor(id, fullXmlData, nodelist, defaultOptions = {}) {
71+
constructor(id, fullXmlData, nodelist, augmentedNodelist, augmentedWays, defaultOptions = {}) {
7272
this.options = this.blankOptions;
7373
if (Object.keys(defaultOptions).length === 0) {
7474
defaultOptions = this.blankOptions;
@@ -78,6 +78,8 @@ class BuildingPart {
7878
this.id = id;
7979
this.way = fullXmlData.getElementById(id);
8080
this.nodelist = nodelist;
81+
this.augmentedNodelist = augmentedNodelist;
82+
this.augmentedWays = augmentedWays;
8183
this.shape = this.buildShape();
8284
this.setOptions();
8385
}
@@ -89,7 +91,7 @@ class BuildingPart {
8991
*/
9092
buildShape() {
9193
this.type = 'way';
92-
return BuildingShapeUtils.createShape(this.way, this.nodelist);
94+
return BuildingShapeUtils.createShape(this.way, this.nodelist, this.augmentedNodelist);
9395
}
9496

9597
/**

src/extras/BuildingShapeUtils.js

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ class BuildingShapeUtils extends ShapeUtils {
1313
*
1414
* @return {THREE.Shape} shape - the shape
1515
*/
16-
static createShape(way, nodelist) {
16+
static createShape(way, nodelist, augmentedNodelist = {}) {
1717
// Initialize objects
1818
const shape = new Shape();
1919
var ref;
@@ -25,7 +25,7 @@ class BuildingShapeUtils extends ShapeUtils {
2525
// Get the coordinates of all the nodes and add them to the shape outline.
2626
for (let i = 0; i < elements.length; i++) {
2727
ref = elements[i].getAttribute('ref');
28-
node = nodelist[ref];
28+
node = nodelist[ref] ?? augmentedNodelist[ref];
2929
// The first node requires a differnet function call.
3030
if (i === 0) {
3131
shape.moveTo(parseFloat(node[0]), parseFloat(node[1]));
@@ -58,10 +58,14 @@ class BuildingShapeUtils extends ShapeUtils {
5858
* @return {DOM.Element}
5959
*/
6060
static combineWays(ways) {
61-
var closedWays = [];
62-
var openWays = [];
63-
var changed = true;
61+
const closedWays = [];
62+
let openWays = [];
63+
let changed = true;
64+
let watchDogCounter = 0
6465
while (changed) {
66+
if (watchDogCounter++ > 2_000_000) {
67+
throw 'Infinite loop in combineWays';
68+
}
6569
changed = false;
6670
for (let i = 0; i < ways.length - 1; i++) {
6771
if (BuildingShapeUtils.isClosed(ways[i])) {

src/multibuildingpart.js

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,19 +17,24 @@ class MultiBuildingPart extends BuildingPart {
1717
const innerMembers = this.way.querySelectorAll('member[role="inner"]');
1818
const outerMembers = this.way.querySelectorAll('member[role="outer"]');
1919
const innerShapes = [];
20-
var shapes = [];
20+
let shapes = [];
2121
for (let i = 0; i < innerMembers.length; i++) {
2222
const way = this.fullXmlData.getElementById(innerMembers[i].getAttribute('ref'));
23-
innerShapes.push(BuildingShapeUtils.createShape(way, this.nodelist));
23+
innerShapes.push(BuildingShapeUtils.createShape(way, this.nodelist, this.augmentedNodelist));
2424
}
2525
const ways = [];
2626
for (let j = 0; j < outerMembers.length; j++) {
2727
const way = this.fullXmlData.getElementById(outerMembers[j].getAttribute('ref'));
28-
ways.push(way);
28+
if (way) {
29+
ways.push(way);
30+
} else {
31+
printError(`Missing way ${outerMembers[j].getAttribute('ref')} for relation ${this.id}`);
32+
ways.push(this.augmentedWays[outerMembers[j].getAttribute('ref')])
33+
}
2934
}
3035
const closedWays = BuildingShapeUtils.combineWays(ways);
3136
for (let k = 0; k < closedWays.length; k++) {
32-
const shape = BuildingShapeUtils.createShape(closedWays[k], this.nodelist);
37+
const shape = BuildingShapeUtils.createShape(closedWays[k], this.nodelist, this.augmentedNodelist);
3338
shape.holes.push(...innerShapes);
3439
shapes.push(shape);
3540
}

0 commit comments

Comments
 (0)