Skip to content

Commit ef5365d

Browse files
committed
Rewrite ways combine, skip non-way members
1 parent ddde11f commit ef5365d

File tree

3 files changed

+92
-61
lines changed

3 files changed

+92
-61
lines changed

src/building.js

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,7 @@ 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
}
143143
printError('Additional downloading way ' + wayID);
144144
const wayData = new DOMParser().parseFromString(await Building.getWayData(wayID), 'text/xml');
@@ -147,7 +147,7 @@ class Building {
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+
printError(e);
218+
}
215219
}
216220
}
217221
}

src/extras/BuildingShapeUtils.js

Lines changed: 70 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -59,62 +59,82 @@ class BuildingShapeUtils extends ShapeUtils {
5959
*/
6060
static combineWays(ways) {
6161
const closedWays = [];
62-
let openWays = [];
63-
let changed = true;
64-
let watchDogCounter = 0
65-
while (changed) {
66-
if (watchDogCounter++ > 2_000_000) {
67-
throw 'Infinite loop in combineWays';
62+
const wayBegins = {};
63+
const wayEnds = {};
64+
65+
ways.forEach(w => {
66+
const firstNodeID = w.querySelector('nd').getAttribute('ref');
67+
if (wayBegins[firstNodeID]) {
68+
wayBegins[firstNodeID].push(w);
69+
} else {
70+
wayBegins[firstNodeID] = [w];
71+
}
72+
73+
const lastNodeID = w.querySelector('nd:last-of-type').getAttribute('ref');
74+
if (wayEnds[lastNodeID]) {
75+
wayEnds[lastNodeID].push(w);
76+
} else {
77+
wayEnds[lastNodeID] = [w];
6878
}
69-
changed = false;
70-
for (let i = 0; i < ways.length - 1; i++) {
71-
if (BuildingShapeUtils.isClosed(ways[i])) {
72-
closedWays.push(ways[i]);
73-
} else {
74-
// These are HTMLCollections of nodes, not ways.
75-
const way1 = ways[i].getElementsByTagName('nd');
76-
const way2 = ways[i + 1].getElementsByTagName('nd');
79+
});
7780

78-
// If the first node of way2 is the same as the last in way one, they can be combined
79-
// Or if the first node of way1 is the same as the last in way2
80-
// Need to extend this to tip-to-tip connections as well.
81-
// Need to add a "reverse way" function somewhere.
82-
if (way2[0].getAttribute('ref') === way1[way1.length - 1].getAttribute('ref')) {
83-
const result = BuildingShapeUtils.joinWays(ways[i], ways[i + 1]);
84-
openWays.push(result);
85-
i++;
86-
changed = true;
87-
} else if (way1[0].getAttribute('ref') === way2[way2.length - 1].getAttribute('ref')) {
88-
const result = BuildingShapeUtils.joinWays(ways[i + 1], ways[i]);
89-
openWays.push(result);
90-
i++;
91-
changed = true;
92-
} else if (way1[way1.length - 1].getAttribute('ref') === way2[way2.length - 1].getAttribute('ref')) {
93-
const tempway = BuildingShapeUtils.reverseWay(ways[i + 1]);
94-
const result = BuildingShapeUtils.joinWays(ways[i], tempway);
95-
openWays.push(result);
96-
i++;
97-
changed = true;
98-
} else if (way1[0].getAttribute('ref') === way2[0].getAttribute('ref')) {
99-
const tempway = BuildingShapeUtils.reverseWay(ways[i]);
100-
const result = BuildingShapeUtils.joinWays(tempway, ways[i + 1]);
101-
openWays.push(result);
102-
i++;
103-
changed = true;
104-
} else {
105-
openWays.push(ways[i]);
106-
}
81+
const usedWays = new Set();
82+
83+
function tryMakeRing(currentRingWays) {
84+
if (currentRingWays[0].querySelector('nd').getAttribute('ref') ===
85+
currentRingWays[currentRingWays.length - 1].querySelector('nd:last-of-type').getAttribute('ref')) {
86+
return currentRingWays;
87+
}
88+
89+
const lastWay = currentRingWays[currentRingWays.length - 1];
90+
const lastNodeID = lastWay.querySelector('nd:last-of-type').getAttribute('ref');
91+
for (let way of wayBegins[lastNodeID] ?? []) {
92+
const wayID = way.getAttribute('id');
93+
if (usedWays.has(wayID)) {
94+
continue;
95+
}
96+
usedWays.add(wayID);
97+
currentRingWays.push(way);
98+
if (tryMakeRing(currentRingWays).length) {
99+
return currentRingWays;
107100
}
101+
currentRingWays.pop();
102+
usedWays.delete(wayID);
108103
}
109-
const lastWay = ways[ways.length - 1];
110-
if (BuildingShapeUtils.isClosed(lastWay)) {
111-
closedWays.push(lastWay);
112-
} else {
113-
openWays.push(lastWay);
104+
105+
for (let way of wayEnds[lastNodeID] ?? []) {
106+
const wayID = way.getAttribute('id');
107+
if (usedWays.has(wayID)) {
108+
continue;
109+
}
110+
usedWays.add(wayID);
111+
currentRingWays.push(BuildingShapeUtils.reverseWay(way));
112+
if (tryMakeRing(currentRingWays).length) {
113+
return currentRingWays;
114+
}
115+
currentRingWays.pop();
116+
usedWays.delete(wayID);
114117
}
115-
ways = openWays;
116-
openWays = [];
118+
119+
return [];
117120
}
121+
122+
ways.forEach(w => {
123+
const wayID = w.getAttribute('ref');
124+
if (usedWays.has(wayID)){
125+
return;
126+
}
127+
usedWays.add(wayID);
128+
const result = tryMakeRing([w]);
129+
if (result.length) {
130+
let ring = result[0];
131+
result.slice(1).forEach(w => {
132+
ring = this.joinWays(ring, w);
133+
});
134+
closedWays.push(ring);
135+
}
136+
});
137+
118138
return closedWays;
119139
}
120140

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+
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+
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)