Skip to content

Commit 95b0b41

Browse files
authored
Reverse way (#78)
1 parent 76d553a commit 95b0b41

File tree

3 files changed

+142
-2
lines changed

3 files changed

+142
-2
lines changed

src/extras/BuildingShapeUtils.js

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,8 +67,14 @@ class BuildingShapeUtils extends ShapeUtils {
6767
if (BuildingShapeUtils.isClosed(ways[i])) {
6868
closedWays.push(ways[i]);
6969
} else {
70+
// These are HTMLCollections of nodes, not ways.
7071
const way1 = ways[i].getElementsByTagName('nd');
7172
const way2 = ways[i + 1].getElementsByTagName('nd');
73+
74+
// If the first node of way2 is the same as the last in way one, they can be combined
75+
// Or if the first node of way1 is the same as the last in way2
76+
// Need to extend this to tip-to-tip connections as well.
77+
// Need to add a "reverse way" function somewhere.
7278
if (way2[0].getAttribute('ref') === way1[way1.length - 1].getAttribute('ref')) {
7379
const result = BuildingShapeUtils.joinWays(ways[i], ways[i + 1]);
7480
openWays.push(result);
@@ -79,8 +85,20 @@ class BuildingShapeUtils extends ShapeUtils {
7985
openWays.push(result);
8086
i++;
8187
changed = true;
88+
} else if (way1[way1.length - 1].getAttribute('ref') === way2[way2.length - 1].getAttribute('ref')) {
89+
const tempway = BuildingShapeUtils.reverseWay(ways[i + 1]);
90+
const result = BuildingShapeUtils.joinWays(ways[i], tempway);
91+
openWays.push(result);
92+
i++;
93+
changed = true;
94+
} 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]);
97+
openWays.push(result);
98+
i++;
99+
changed = true;
82100
} else {
83-
openWays.push(way1);
101+
openWays.push(ways[i]);
84102
}
85103
}
86104
}
@@ -113,6 +131,24 @@ class BuildingShapeUtils extends ShapeUtils {
113131
return way1;
114132
}
115133

134+
/**
135+
* Reverse the order of nodes in a way.
136+
*
137+
* @param {DOM.Element} way - a way
138+
*
139+
* @return {DOM.Element} way
140+
*/
141+
static reverseWay(way) {
142+
const elements = way.getElementsByTagName('nd');
143+
const newWay = way.cloneNode(true);
144+
newWay.innerHTML = '';
145+
for (let i = 0; i < elements.length; i++) {
146+
let elem = elements[elements.length - 1 - i].cloneNode();
147+
newWay.appendChild(elem);
148+
}
149+
return newWay;
150+
}
151+
116152
/**
117153
* Find the center of a closed way
118154
*
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
/**
2+
* @jest-environment jsdom
3+
*/
4+
5+
import { Shape, Mesh } from 'three';
6+
import { TextEncoder } from 'node:util';
7+
global.TextEncoder = TextEncoder;
8+
9+
import { Building } from '../src/building.js';
10+
import { MultiBuildingPart } from '../src/multibuildingpart.js';
11+
// ways 1 and 3 are tip-to-tip.
12+
const data = `
13+
<osm>
14+
<node id="12" lat="4" lon="4"/>
15+
<node id="11" lat="4" lon="4.001"/>
16+
<node id="6" lat="4.001" lon="4.001"/>
17+
<node id="7" lat="4.001" lon="4"/>
18+
<node id="8" lat="4.00025" lon="4.00025"/>
19+
<node id="9" lat="4.00025" lon="4.00075"/>
20+
<node id="10" lat="4.00075" lon="4.0005"/>
21+
<relation id="5">
22+
<member ref="1" role="outer"/>
23+
<member ref="3" role="outer"/>
24+
<member ref="2" role="inner"/>
25+
<tag k="type" v="multipolygon"/>
26+
<tag k="building" v="yes"/>
27+
<tag k="roof:shape" v="skillion"/>
28+
<tag k="roof:direction" v="0"/>
29+
<tag k="roof:angle" v="45"/>
30+
</relation>
31+
<way id="1">
32+
<nd ref="12"/>
33+
<nd ref="11"/>
34+
<nd ref="6"/>
35+
</way>
36+
<way id="2">
37+
<nd ref="8"/>
38+
<nd ref="9"/>
39+
<nd ref="10"/>
40+
<nd ref="8"/>
41+
</way>
42+
<way id="3">
43+
<nd ref="12"/>
44+
<nd ref="7"/>
45+
<nd ref="6"/>
46+
</way>
47+
</osm>`;
48+
49+
beforeEach(() => {
50+
errors = [];
51+
});
52+
53+
test('Test Simple Multipolygon', () => {
54+
let xmlData = new window.DOMParser().parseFromString(data, 'text/xml');
55+
const nodelist = Building.buildNodeList(xmlData);
56+
const shape = new MultiBuildingPart('5', xmlData, nodelist);
57+
expect(shape.id).toBe('5');
58+
expect(shape.shape).toBeInstanceOf(Shape);
59+
// expect(shape.roof).toBeInstanceOf(Mesh);
60+
expect(errors.length).toBe(0);
61+
});
62+
63+
window.printError = printError;
64+
65+
var errors = [];
66+
67+
function printError(txt) {
68+
errors.push[txt];
69+
}

test/utils.test.js

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,15 @@ test('Test Closed Way', () => {
2020
expect(BuildingShapeUtils.isClosed(xmlData)).toBe(true);
2121
});
2222

23+
test('Reverse way', () => {
24+
var way1 = '<way id="1"><nd ref="1"/><nd ref="2"/><nd ref="3"/></way>';
25+
var way2 = '<way id="1"><nd ref="3"/><nd ref="2"/><nd ref="1"/></way>';
26+
let parser = new window.DOMParser();
27+
let xml1 = parser.parseFromString(way1, 'text/xml').getElementsByTagName('way')[0];
28+
let result = BuildingShapeUtils.reverseWay(xml1);
29+
expect(result.outerHTML).toBe(way2);
30+
});
31+
2332
test('Test Open Way', () => {
2433
var way = '<way id="1"><nd ref="2"/><nd ref="3"/><nd ref="4"/><nd ref="5"/><nd ref="6"/></way>';
2534
let parser = new window.DOMParser();
@@ -38,7 +47,7 @@ test('Test joining 2 ways', () => {
3847
expect(result.outerHTML).toBe(way3);
3948
});
4049

41-
test('Test combining 2 ways', () => {
50+
test('Test combining 2 ways 1->2', () => {
4251
var way1 = '<way id="1"><nd ref="1"/><nd ref="2"/><nd ref="3"/></way>';
4352
var way2 = '<way id="2"><nd ref="3"/><nd ref="4"/><nd ref="1"/></way>';
4453
var way3 = '<way id="1"><nd ref="1"/><nd ref="2"/><nd ref="3"/><nd ref="4"/><nd ref="1"/></way>';
@@ -51,6 +60,32 @@ test('Test combining 2 ways', () => {
5160
expect(result[0].outerHTML).toBe(way3);
5261
});
5362

63+
test('Test combining 2 ways 2->1', () => {
64+
var way2 = '<way id="1"><nd ref="1"/><nd ref="2"/><nd ref="3"/></way>';
65+
var way1 = '<way id="2"><nd ref="3"/><nd ref="4"/><nd ref="1"/></way>';
66+
var way3 = '<way id="2"><nd ref="3"/><nd ref="4"/><nd ref="1"/><nd ref="2"/><nd ref="3"/></way>';
67+
let parser = new window.DOMParser();
68+
let xml1 = parser.parseFromString(way1, 'text/xml').getElementsByTagName('way')[0];
69+
let xml2 = parser.parseFromString(way2, 'text/xml').getElementsByTagName('way')[0];
70+
let result = BuildingShapeUtils.combineWays([xml1, xml2]);
71+
expect(result.length).toBe(1);
72+
let expected = parser.parseFromString(way3, 'text/xml');
73+
expect(result[0].outerHTML).toBe(way3);
74+
});
75+
76+
test('Test combining 2 unaligned ways', () => {
77+
var way1 = '<way id="1"><nd ref="1"/><nd ref="2"/><nd ref="3"/></way>';
78+
var way2 = '<way id="2"><nd ref="1"/><nd ref="4"/><nd ref="3"/></way>';
79+
var way3 = '<way id="1"><nd ref="1"/><nd ref="2"/><nd ref="3"/><nd ref="4"/><nd ref="1"/></way>';
80+
let parser = new window.DOMParser();
81+
let xml1 = parser.parseFromString(way1, 'text/xml').getElementsByTagName('way')[0];
82+
let xml2 = parser.parseFromString(way2, 'text/xml').getElementsByTagName('way')[0];
83+
let result = BuildingShapeUtils.combineWays([xml1, xml2]);
84+
expect(result.length).toBe(1);
85+
let expected = parser.parseFromString(way3, 'text/xml');
86+
expect(result[0].outerHTML).toBe(way3);
87+
});
88+
5489
const rightTriangle = new Shape();
5590
rightTriangle.moveTo(1, 1);
5691
rightTriangle.lineTo(1, -1);

0 commit comments

Comments
 (0)