Skip to content

Commit 9fc5414

Browse files
authored
Prevent self intersecting rings from being created
1 parent c07bc96 commit 9fc5414

File tree

2 files changed

+47
-14
lines changed

2 files changed

+47
-14
lines changed

src/extras/BuildingShapeUtils.js

Lines changed: 41 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -76,20 +76,21 @@ class BuildingShapeUtils extends ShapeUtils {
7676
* Walk through an array and seperate any closed ways.
7777
* Attempt to find matching open ways to enclose them.
7878
*
79-
* @param {[DOM.Element]} array - list of OSM XML way elements.
79+
* @param {[DOM.Element]} ways - array of OSM XML way elements.
8080
*
8181
* @return {[DOM.Element]} array of closed ways.
8282
*/
8383
static combineWays(ways) {
8484
const validWays = [];
8585

86+
// Check if the provided array contains any self-intersecting ways.
87+
// Remove them and notify the user.
8688
for (const way of ways) {
8789
if (BuildingShapeUtils.isSelfIntersecting(way)) {
8890
const id = way.getAttribute('id');
8991
const msg = 'Way ' + id + ' is self-intersecting';
9092
window.printError(msg);
9193
} else {
92-
const i = 3 + 'q';
9394
validWays.push(way);
9495
}
9596
}
@@ -98,6 +99,7 @@ class BuildingShapeUtils extends ShapeUtils {
9899
const wayBegins = {};
99100
const wayEnds = {};
100101

102+
// Create lists of the first and last nodes in each way.
101103
validWays.forEach(w => {
102104
const firstNodeID = w.querySelector('nd').getAttribute('ref');
103105
if (wayBegins[firstNodeID]) {
@@ -116,14 +118,26 @@ class BuildingShapeUtils extends ShapeUtils {
116118

117119
const usedWays = new Set();
118120

121+
/**
122+
* Use recursion to attempt to build a ring from ways.
123+
*
124+
* @param {[DOM.Element]} currentRingWays - array of OSM XML way elements.
125+
*/
119126
function tryMakeRing(currentRingWays) {
127+
128+
// Check if the array contains ways which will together form a ring. Return the array if it does.
120129
if (currentRingWays[0].querySelector('nd').getAttribute('ref') ===
121130
currentRingWays[currentRingWays.length - 1].querySelector('nd:last-of-type').getAttribute('ref')) {
131+
if (BuildingShapeUtils.isSelfIntersecting(BuildingShapeUtils.joinAllWays(currentRingWays))) {
132+
return [];
133+
}
122134
return currentRingWays;
123135
}
124136

125137
const lastWay = currentRingWays[currentRingWays.length - 1];
126138
const lastNodeID = lastWay.querySelector('nd:last-of-type').getAttribute('ref');
139+
140+
// Check if any of the unused ways can complete a ring as the are.
127141
for (let way of wayBegins[lastNodeID] ?? []) {
128142
const wayID = way.getAttribute('id');
129143
if (usedWays.has(wayID)) {
@@ -138,6 +152,7 @@ class BuildingShapeUtils extends ShapeUtils {
138152
usedWays.delete(wayID);
139153
}
140154

155+
// Check if any of the unused ways can complete a ring if reversed.
141156
for (let way of wayEnds[lastNodeID] ?? []) {
142157
const wayID = way.getAttribute('id');
143158
if (usedWays.has(wayID)) {
@@ -163,14 +178,15 @@ class BuildingShapeUtils extends ShapeUtils {
163178
usedWays.add(wayID);
164179
const result = tryMakeRing([w]);
165180
if (result.length) {
166-
let ring = result[0];
167-
result.slice(1).forEach(w => {
168-
ring = this.joinWays(ring, w);
169-
});
181+
const ring = this.joinAllWays(result);
170182
closedWays.push(ring);
171183
}
172184
});
173185

186+
// Notify the user if there are unused ways.
187+
// if (validWays.length !== usedWays.length) {
188+
// window.printError('Unused ways in relation')
189+
// }
174190
return closedWays;
175191
}
176192

@@ -184,11 +200,28 @@ class BuildingShapeUtils extends ShapeUtils {
184200
*/
185201
static joinWays(way1, way2) {
186202
const elements = way2.getElementsByTagName('nd');
203+
const newWay = way1.cloneNode(true);
187204
for (let i = 1; i < elements.length; i++) {
188205
let elem = elements[i].cloneNode();
189-
way1.appendChild(elem);
206+
newWay.appendChild(elem);
190207
}
191-
return way1;
208+
return newWay;
209+
}
210+
211+
/**
212+
* Append the nodes from one way into another.
213+
*
214+
* @param {DOM.Element} way1 - an open, non self-intersecring way
215+
* @param {DOM.Element} way2
216+
*
217+
* @return {DOM.Element} way
218+
*/
219+
static joinAllWays(ways) {
220+
let way = ways[0];
221+
ways.slice(1).forEach(w => {
222+
way = this.joinWays(way, w);
223+
});
224+
return way;
192225
}
193226

194227
/**

test/combine_ways.test.js

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -86,12 +86,12 @@ describe.each([
8686
'<way id="3"><nd ref="3"/><nd ref="4"/><nd ref="1"/></way>',
8787
], 1, 5, [], 'Dealing with amiguity. Only make one closed way',
8888
],
89-
//[
90-
// [
91-
// '<way id="1"><nd ref="1"/><nd ref="2"/><nd ref="3"/></way>',
92-
// '<way id="2"><nd ref="3"/><nd ref="4"/><nd ref="2"/><nd ref="5"/><nd ref="1"/></way>',
93-
// ], 0, 0, [], 'Closed way is self intersecting',
94-
//],
89+
[
90+
[
91+
'<way id="1"><nd ref="1"/><nd ref="2"/><nd ref="3"/></way>',
92+
'<way id="2"><nd ref="3"/><nd ref="4"/><nd ref="2"/><nd ref="5"/><nd ref="1"/></way>',
93+
], 0, 0, [], 'Closed way is self intersecting',
94+
],
9595
[
9696
[
9797
'<way id="1"><nd ref="1"/><nd ref="2"/></way>',

0 commit comments

Comments
 (0)