Skip to content

Commit 59d46f8

Browse files
authored
Increased coverage and added isSelfIntersecting (#90)
1 parent 6e46da8 commit 59d46f8

File tree

4 files changed

+140
-95
lines changed

4 files changed

+140
-95
lines changed

src/extras/BuildingShapeUtils.js

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ class BuildingShapeUtils extends ShapeUtils {
3737
}
3838

3939
/**
40-
* Check if a way is a cloased shape.
40+
* Check if a way is a closed shape.
4141
*
4242
* @param {DOM.Element} way - OSM XML way element.
4343
*
@@ -49,6 +49,29 @@ class BuildingShapeUtils extends ShapeUtils {
4949
return elements[0].getAttribute('ref') === elements[elements.length - 1].getAttribute('ref');
5050
}
5151

52+
/**
53+
* Check if a way is self-intersecting.
54+
*
55+
* @param {DOM.Element} way - OSM XML way element.
56+
*
57+
* @return {boolean}
58+
*/
59+
static isSelfIntersecting(way) {
60+
const nodes = Array.from(way.getElementsByTagName('nd'));
61+
if (BuildingShapeUtils.isClosed(way)){
62+
nodes.pop();
63+
}
64+
const refs = new Set();
65+
for (const node of nodes) {
66+
const ref = node.getAttribute('ref');
67+
if (refs.has(ref)){
68+
return true;
69+
}
70+
refs.add(ref);
71+
}
72+
return false;
73+
}
74+
5275
/**
5376
* Walk through an array and seperate any closed ways.
5477
* Attempt to find matching open ways to enclose them.
@@ -92,8 +115,8 @@ class BuildingShapeUtils extends ShapeUtils {
92115
i++;
93116
changed = true;
94117
} else if (way1[0].getAttribute('ref') === way2[0].getAttribute('ref')) {
95-
const tempway = BuildingShapeUtils.reverseWay(ways[i]);
96-
const result = BuildingShapeUtils.joinWays(tempway, ways[i + 1]);
118+
const tempway = BuildingShapeUtils.reverseWay(ways[i+1]);
119+
const result = BuildingShapeUtils.joinWays(tempway, ways[i]);
97120
openWays.push(result);
98121
i++;
99122
changed = true;

test/combine_ways.js

Lines changed: 0 additions & 92 deletions
This file was deleted.

test/combine_ways.test.js

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
/**
2+
* @jest-environment jsdom
3+
*/
4+
5+
import {toBeDeepCloseTo} from 'jest-matcher-deep-close-to';
6+
expect.extend({toBeDeepCloseTo});
7+
8+
import { TextEncoder } from 'node:util';
9+
global.TextEncoder = TextEncoder;
10+
11+
import { Shape } from 'three';
12+
13+
import { BuildingShapeUtils } from '../src/extras/BuildingShapeUtils.js';
14+
// import { JSDOM } from 'jsdom';
15+
16+
test('Test no combining necessary. one open way', () => {
17+
var way1 = '<way id="1"><nd ref="1"/><nd ref="2"/><nd ref="3"/><nd ref="4"/></way>';
18+
let parser = new window.DOMParser();
19+
let xml1 = parser.parseFromString(way1, 'text/xml').getElementsByTagName('way')[0];
20+
let result = BuildingShapeUtils.combineWays([xml1]);
21+
expect(result.length).toBe(0);
22+
});
23+
24+
test('Test combining 2 ways 1->2', () => {
25+
var way1 = '<way id="1"><nd ref="1"/><nd ref="2"/></way>';
26+
var way2 = '<way id="2"><nd ref="2"/><nd ref="3"/></way>';
27+
var way3 = '<way id="3"><nd ref="3"/><nd ref="1"/></way>';
28+
let parser = new window.DOMParser();
29+
let xml1 = parser.parseFromString(way1, 'text/xml').getElementsByTagName('way')[0];
30+
let xml2 = parser.parseFromString(way2, 'text/xml').getElementsByTagName('way')[0];
31+
let xml3 = parser.parseFromString(way3, 'text/xml').getElementsByTagName('way')[0];
32+
let result = BuildingShapeUtils.combineWays([xml1, xml2, xml3]);
33+
// Expect one closed way with 3 unique nodes.
34+
expect(result.length).toBe(1);
35+
expect(BuildingShapeUtils.isClosed(result[0])).toBe(true);
36+
expect(result[0].getElementsByTagName('nd').length).toBe(4);
37+
expect(BuildingShapeUtils.isSelfIntersecting(result[0])).toBe(false);
38+
});
39+
40+
test('Test combining 3 ways 2->1->3', () => {
41+
var way1 = '<way id="1"><nd ref="1"/><nd ref="2"/></way>';
42+
var way2 = '<way id="2"><nd ref="3"/><nd ref="1"/></way>';
43+
var way3 = '<way id="3"><nd ref="2"/><nd ref="3"/></way>';
44+
let parser = new window.DOMParser();
45+
let xml1 = parser.parseFromString(way1, 'text/xml').getElementsByTagName('way')[0];
46+
let xml2 = parser.parseFromString(way2, 'text/xml').getElementsByTagName('way')[0];
47+
let xml3 = parser.parseFromString(way3, 'text/xml').getElementsByTagName('way')[0];
48+
let result = BuildingShapeUtils.combineWays([xml1, xml2, xml3]);
49+
expect(result.length).toBe(1);
50+
expect(BuildingShapeUtils.isClosed(result[0])).toBe(true);
51+
expect(BuildingShapeUtils.isSelfIntersecting(result[0])).toBe(false);
52+
expect(result[0].getElementsByTagName('nd').length).toBe(4);
53+
});
54+
55+
test('Test combining 2 unaligned ways', () => {
56+
var way1 = '<way id="1"><nd ref="1"/><nd ref="2"/></way>';
57+
var way2 = '<way id="2"><nd ref="3"/><nd ref="2"/></way>';
58+
var way3 = '<way id="3"><nd ref="3"/><nd ref="1"/></way>';
59+
let parser = new window.DOMParser();
60+
let xml1 = parser.parseFromString(way1, 'text/xml').getElementsByTagName('way')[0];
61+
let xml2 = parser.parseFromString(way2, 'text/xml').getElementsByTagName('way')[0];
62+
let xml3 = parser.parseFromString(way3, 'text/xml').getElementsByTagName('way')[0];
63+
let result = BuildingShapeUtils.combineWays([xml1, xml2, xml3]);
64+
expect(result.length).toBe(1);
65+
expect(BuildingShapeUtils.isClosed(result[0])).toBe(true);
66+
expect(BuildingShapeUtils.isSelfIntersecting(result[0])).toBe(false);
67+
expect(result[0].getElementsByTagName('nd').length).toBe(4);
68+
});
69+
70+
test('Test combining 3 ways 1->2->3', () => {
71+
var way1 = '<way id="1"><nd ref="1"/><nd ref="2"/></way>';
72+
var way2 = '<way id="2"><nd ref="1"/><nd ref="3"/></way>';
73+
var way3 = '<way id="3"><nd ref="2"/><nd ref="3"/></way>';
74+
var way4 = '<way id="2"><nd ref="3"/><nd ref="1"/><nd ref="2"/><nd ref="3"/></way>';
75+
let parser = new window.DOMParser();
76+
let xml1 = parser.parseFromString(way1, 'text/xml').getElementsByTagName('way')[0];
77+
let xml2 = parser.parseFromString(way2, 'text/xml').getElementsByTagName('way')[0];
78+
let xml3 = parser.parseFromString(way3, 'text/xml').getElementsByTagName('way')[0];
79+
let result = BuildingShapeUtils.combineWays([xml1, xml2, xml3]);
80+
expect(result.length).toBe(1);
81+
expect(BuildingShapeUtils.isClosed(result[0])).toBe(true);
82+
expect(BuildingShapeUtils.isSelfIntersecting(result[0])).toBe(false);
83+
expect(result[0].getElementsByTagName('nd').length).toBe(4);
84+
});
85+
86+
test('Test combining 4 ways', () => {
87+
var way1 = '<way id="1"><nd ref="1"/><nd ref="2"/></way>';
88+
var way2 = '<way id="2"><nd ref="3"/><nd ref="4"/></way>';
89+
var way3 = '<way id="3"><nd ref="4"/><nd ref="1"/></way>';
90+
var way4 = '<way id="4"><nd ref="2"/><nd ref="3"/></way>';
91+
let parser = new window.DOMParser();
92+
let xml1 = parser.parseFromString(way1, 'text/xml').getElementsByTagName('way')[0];
93+
let xml2 = parser.parseFromString(way2, 'text/xml').getElementsByTagName('way')[0];
94+
let xml3 = parser.parseFromString(way3, 'text/xml').getElementsByTagName('way')[0];
95+
let xml4 = parser.parseFromString(way4, 'text/xml').getElementsByTagName('way')[0];
96+
let result = BuildingShapeUtils.combineWays([xml1, xml2, xml3, xml4]);
97+
expect(result.length).toBe(1);
98+
expect(BuildingShapeUtils.isClosed(result[0]));
99+
expect(BuildingShapeUtils.isSelfIntersecting(result[0])).toBe(false);
100+
expect(result[0].getElementsByTagName('nd').length).toBe(5);
101+
});

test/utils.test.js

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,3 +75,16 @@ test('Longest side angle', () => {
7575
expect(rightTriangle.extractPoints().shape.length).toBe(3);
7676
expect(BuildingShapeUtils.longestSideAngle(rightTriangle)).toBe(-Math.PI / 4);
7777
});
78+
79+
describe('isSelfIntersecting', () => {
80+
test.each([
81+
['<way id="1"><nd ref="1"/><nd ref="2"/></way>', false, 'open non-intersecting'],
82+
['<way id="1"><nd ref="1"/><nd ref="2"/><nd ref="3"/><nd ref="1"/></way>', false, 'closed non-intersecting'],
83+
['<way id="1"><nd ref="1"/><nd ref="2"/><nd ref="3"/><nd ref="2"/></way>', true, 'open intersecting'],
84+
['<way id="1"><nd ref="1"/><nd ref="2"/><nd ref="3"/><nd ref="4"/><nd ref="3"/><nd ref="1"/></way>', true, 'closed intersecting'],
85+
])('${description}', (way, expected, description) => {
86+
let parser = new window.DOMParser();
87+
let xml = parser.parseFromString(way, 'text/xml').getElementsByTagName('way')[0];
88+
expect(BuildingShapeUtils.isSelfIntersecting(xml)).toBe(expected);
89+
});
90+
});

0 commit comments

Comments
 (0)