Skip to content

Commit bc46f55

Browse files
committed
Rewrite ways combine, skip non-way members
1 parent a9278bb commit bc46f55

File tree

4 files changed

+100
-66
lines changed

4 files changed

+100
-66
lines changed

src/building.js

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

@@ -138,16 +138,16 @@ class Building {
138138
await Promise.all(Array.from(memberWays).map(async currentWay => {
139139
const wayID = currentWay.getAttribute('ref');
140140
if (completedWays.has(wayID)) {
141-
return
141+
return;
142142
}
143-
printError('Additional downloading way ' + wayID);
143+
window.printError('Additional downloading way ' + wayID);
144144
const wayData = new DOMParser().parseFromString(await Building.getWayData(wayID), 'text/xml');
145-
printError(`Way ${wayID} was downloaded`);
145+
window.printError(`Way ${wayID} was downloaded`);
146146
waysList[wayID] = wayData.querySelector('way');
147147
wayData.querySelectorAll('node').forEach(i => {
148148
nodeList[i.getAttribute('id')] = [i.getAttribute('lon'), i.getAttribute('lat')];
149149
});
150-
}))
150+
}));
151151
return [nodeList, waysList];
152152
}
153153

@@ -211,7 +211,11 @@ class Building {
211211
for (let i = 0; i < parts.length; i++) {
212212
if (parts[i].querySelector('[k="building:part"]')) {
213213
const id = parts[i].getAttribute('id');
214-
this.parts.push(new MultiBuildingPart(id, this.fullXmlData, this.nodelist, this.augmentedNodelist, this.augmentedWays, this.outerElement.options));
214+
try {
215+
this.parts.push(new MultiBuildingPart(id, this.fullXmlData, this.nodelist, this.augmentedNodelist, this.augmentedWays, this.outerElement.options));
216+
} catch (e) {
217+
window.printError(e);
218+
}
215219
}
216220
}
217221
}

src/buildingpart.js

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -66,9 +66,11 @@ class BuildingPart {
6666
* @param {number} id - the OSM id of the way or multipolygon.
6767
* @param {XMLDocument} fullXmlData - XML for the region.
6868
* @param {[[number, number],...]} nodelist - Cartesian coordinates of each node keyed by node refID
69-
* @param {object} options - default values for the building part.
69+
* @param augmentedNodelist - list of nodes outside bbox
70+
* @param augmentedWays - list of ways outside bbox
71+
* @param {object} defaultOptions - default values for the building part.
7072
*/
71-
constructor(id, fullXmlData, nodelist, augmentedNodelist, augmentedWays, defaultOptions = {}) {
73+
constructor(id, fullXmlData, nodelist, augmentedNodelist = {}, augmentedWays = {}, defaultOptions = {}) {
7274
this.options = this.blankOptions;
7375
if (Object.keys(defaultOptions).length === 0) {
7476
defaultOptions = this.blankOptions;

src/extras/BuildingShapeUtils.js

Lines changed: 71 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ class BuildingShapeUtils extends ShapeUtils {
1010
*
1111
* @param {DOM.Element} way - OSM XML way element.
1212
* @param {[number, number]} nodelist - list of all nodes
13+
* @param augmentedNodelist - list of nodes outside bbox
1314
*
1415
* @return {THREE.Shape} shape - the shape
1516
*/
@@ -82,62 +83,82 @@ class BuildingShapeUtils extends ShapeUtils {
8283
*/
8384
static combineWays(ways) {
8485
const closedWays = [];
85-
let openWays = [];
86-
let changed = true;
87-
let watchDogCounter = 0
88-
while (changed) {
89-
if (watchDogCounter++ > 2_000_000) {
90-
throw 'Infinite loop in combineWays';
86+
const wayBegins = {};
87+
const wayEnds = {};
88+
89+
ways.forEach(w => {
90+
const firstNodeID = w.querySelector('nd').getAttribute('ref');
91+
if (wayBegins[firstNodeID]) {
92+
wayBegins[firstNodeID].push(w);
93+
} else {
94+
wayBegins[firstNodeID] = [w];
95+
}
96+
97+
const lastNodeID = w.querySelector('nd:last-of-type').getAttribute('ref');
98+
if (wayEnds[lastNodeID]) {
99+
wayEnds[lastNodeID].push(w);
100+
} else {
101+
wayEnds[lastNodeID] = [w];
91102
}
92-
changed = false;
93-
for (let i = 0; i < ways.length - 1; i++) {
94-
if (BuildingShapeUtils.isClosed(ways[i])) {
95-
closedWays.push(ways[i]);
96-
} else {
97-
// These are HTMLCollections of nodes, not ways.
98-
const way1 = ways[i].getElementsByTagName('nd');
99-
const way2 = ways[i + 1].getElementsByTagName('nd');
103+
});
100104

101-
// If the first node of way2 is the same as the last in way one, they can be combined
102-
// Or if the first node of way1 is the same as the last in way2
103-
// Need to extend this to tip-to-tip connections as well.
104-
// Need to add a "reverse way" function somewhere.
105-
if (way2[0].getAttribute('ref') === way1[way1.length - 1].getAttribute('ref')) {
106-
const result = BuildingShapeUtils.joinWays(ways[i], ways[i + 1]);
107-
openWays.push(result);
108-
i++;
109-
changed = true;
110-
} else if (way1[0].getAttribute('ref') === way2[way2.length - 1].getAttribute('ref')) {
111-
const result = BuildingShapeUtils.joinWays(ways[i + 1], ways[i]);
112-
openWays.push(result);
113-
i++;
114-
changed = true;
115-
} else if (way1[way1.length - 1].getAttribute('ref') === way2[way2.length - 1].getAttribute('ref')) {
116-
const tempway = BuildingShapeUtils.reverseWay(ways[i + 1]);
117-
const result = BuildingShapeUtils.joinWays(ways[i], tempway);
118-
openWays.push(result);
119-
i++;
120-
changed = true;
121-
} else if (way1[0].getAttribute('ref') === way2[0].getAttribute('ref')) {
122-
const tempway = BuildingShapeUtils.reverseWay(ways[i+1]);
123-
const result = BuildingShapeUtils.joinWays(tempway, ways[i]);
124-
openWays.push(result);
125-
i++;
126-
changed = true;
127-
} else {
128-
openWays.push(ways[i]);
129-
}
105+
const usedWays = new Set();
106+
107+
function tryMakeRing(currentRingWays) {
108+
if (currentRingWays[0].querySelector('nd').getAttribute('ref') ===
109+
currentRingWays[currentRingWays.length - 1].querySelector('nd:last-of-type').getAttribute('ref')) {
110+
return currentRingWays;
111+
}
112+
113+
const lastWay = currentRingWays[currentRingWays.length - 1];
114+
const lastNodeID = lastWay.querySelector('nd:last-of-type').getAttribute('ref');
115+
for (let way of wayBegins[lastNodeID] ?? []) {
116+
const wayID = way.getAttribute('id');
117+
if (usedWays.has(wayID)) {
118+
continue;
119+
}
120+
usedWays.add(wayID);
121+
currentRingWays.push(way);
122+
if (tryMakeRing(currentRingWays).length) {
123+
return currentRingWays;
130124
}
125+
currentRingWays.pop();
126+
usedWays.delete(wayID);
131127
}
132-
const lastWay = ways[ways.length - 1];
133-
if (BuildingShapeUtils.isClosed(lastWay)) {
134-
closedWays.push(lastWay);
135-
} else {
136-
openWays.push(lastWay);
128+
129+
for (let way of wayEnds[lastNodeID] ?? []) {
130+
const wayID = way.getAttribute('id');
131+
if (usedWays.has(wayID)) {
132+
continue;
133+
}
134+
usedWays.add(wayID);
135+
currentRingWays.push(BuildingShapeUtils.reverseWay(way));
136+
if (tryMakeRing(currentRingWays).length) {
137+
return currentRingWays;
138+
}
139+
currentRingWays.pop();
140+
usedWays.delete(wayID);
137141
}
138-
ways = openWays;
139-
openWays = [];
142+
143+
return [];
140144
}
145+
146+
ways.forEach(w => {
147+
const wayID = w.getAttribute('ref');
148+
if (usedWays.has(wayID)){
149+
return;
150+
}
151+
usedWays.add(wayID);
152+
const result = tryMakeRing([w]);
153+
if (result.length) {
154+
let ring = result[0];
155+
result.slice(1).forEach(w => {
156+
ring = this.joinWays(ring, w);
157+
});
158+
closedWays.push(ring);
159+
}
160+
});
161+
141162
return closedWays;
142163
}
143164

src/multibuildingpart.js

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -14,22 +14,29 @@ class MultiBuildingPart extends BuildingPart {
1414
*/
1515
buildShape() {
1616
this.type = 'multipolygon';
17-
const innerMembers = this.way.querySelectorAll('member[role="inner"]');
18-
const outerMembers = this.way.querySelectorAll('member[role="outer"]');
17+
const innerMembers = this.way.querySelectorAll('member[role="inner"][type="way"]');
18+
const outerMembers = this.way.querySelectorAll('member[role="outer"][type="way"]');
1919
const innerShapes = [];
20-
let shapes = [];
20+
const shapes = [];
2121
for (let i = 0; i < innerMembers.length; i++) {
22-
const way = this.fullXmlData.getElementById(innerMembers[i].getAttribute('ref'));
23-
innerShapes.push(BuildingShapeUtils.createShape(way, this.nodelist, this.augmentedNodelist));
22+
const wayID = innerMembers[i].getAttribute('ref');
23+
const way = this.fullXmlData.getElementById(wayID);
24+
if (way) {
25+
innerShapes.push(BuildingShapeUtils.createShape(way, this.nodelist, this.augmentedNodelist));
26+
} else {
27+
window.printError(`Missing way ${wayID} for relation ${this.id} for inner members`);
28+
innerShapes.push(BuildingShapeUtils.createShape(this.augmentedWays[wayID], this.nodelist, this.augmentedNodelist));
29+
}
2430
}
2531
const ways = [];
2632
for (let j = 0; j < outerMembers.length; j++) {
27-
const way = this.fullXmlData.getElementById(outerMembers[j].getAttribute('ref'));
33+
const wayID = outerMembers[j].getAttribute('ref');
34+
const way = this.fullXmlData.getElementById(wayID);
2835
if (way) {
2936
ways.push(way);
3037
} else {
31-
printError(`Missing way ${outerMembers[j].getAttribute('ref')} for relation ${this.id}`);
32-
ways.push(this.augmentedWays[outerMembers[j].getAttribute('ref')])
38+
window.printError(`Missing way ${wayID} for relation ${this.id}`);
39+
ways.push(this.augmentedWays[wayID]);
3340
}
3441
}
3542
const closedWays = BuildingShapeUtils.combineWays(ways);

0 commit comments

Comments
 (0)