Skip to content

Commit e5c52da

Browse files
authored
Beakerboy patch 1 (#115)
1 parent bd19793 commit e5c52da

File tree

6 files changed

+152
-55
lines changed

6 files changed

+152
-55
lines changed

src/building.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,10 @@ class Building {
151151
return mesh;
152152
}
153153

154+
/**
155+
* Inspect XML data for building parts and add them to the array.
156+
*
157+
*/
154158
addParts() {
155159
if (this.type === 'relation') {
156160
let parts = this.fullXmlData.getElementById(this.id).querySelectorAll('member[role="part"]');

src/buildingpart.js

Lines changed: 14 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -230,18 +230,7 @@ class BuildingPart {
230230
var way = this.way;
231231
var material;
232232
var roof;
233-
if (this.options.roof.shape === 'flat') {
234-
let extrusionHeight = this.options.roof.height ?? 0;
235-
let extrudeSettings = {
236-
bevelEnabled: false,
237-
depth: extrusionHeight,
238-
};
239-
var geometry = new ExtrudeGeometry(this.shape, extrudeSettings);
240-
// Create the mesh.
241-
roof = new Mesh(geometry, [BuildingPart.getRoofMaterial(this.way), BuildingPart.getMaterial(this.way)]);
242-
roof.rotation.x = -Math.PI / 2;
243-
roof.position.set(0, this.options.building.height - this.options.roof.height, 0);
244-
} else if (this.options.roof.shape === 'dome' || this.options.roof.shape === 'onion') {
233+
if (this.options.roof.shape === 'dome' || this.options.roof.shape === 'onion') {
245234
// find largest circle within the way
246235
// R, x, y
247236
var thetaStart = Math.PI / 2;
@@ -303,7 +292,19 @@ class BuildingPart {
303292
roof.rotation.x = -Math.PI / 2;
304293
roof.position.set( 0, this.options.building.height - this.options.roof.height, 0);
305294
} else {
306-
return;
295+
let extrusionHeight = this.options.roof.height ?? 0;
296+
let extrudeSettings = {
297+
bevelEnabled: false,
298+
depth: extrusionHeight,
299+
};
300+
var geometry = new ExtrudeGeometry(this.shape, extrudeSettings);
301+
// Create the mesh.
302+
roof = new Mesh(geometry, [BuildingPart.getRoofMaterial(this.way), BuildingPart.getMaterial(this.way)]);
303+
roof.rotation.x = -Math.PI / 2;
304+
roof.position.set(0, this.options.building.height - this.options.roof.height, 0);
305+
if (this.options.roof.shape !== 'flat') {
306+
window.printError('Unknown roof shape on '+ this.id + ': '+ this.options.roof.shape);
307+
}
307308
}
308309
roof.name = 'r' + this.id;
309310
this.roof = roof;

src/extras/BuildingShapeUtils.js

Lines changed: 29 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -280,6 +280,7 @@ class BuildingShapeUtils extends ShapeUtils {
280280
*/
281281
static combineCoordinates(shape) {
282282
const points = shape.extractPoints().shape;
283+
points.pop();
283284
var x = [];
284285
var y = [];
285286
var vec;
@@ -329,6 +330,7 @@ class BuildingShapeUtils extends ShapeUtils {
329330
*/
330331
static edgeLength(shape) {
331332
const points = shape.extractPoints().shape;
333+
points.pop();
332334
const lengths = [];
333335
var p1;
334336
var p2;
@@ -350,6 +352,7 @@ class BuildingShapeUtils extends ShapeUtils {
350352
*/
351353
static vertexAngle(shape) {
352354
const points = shape.extractPoints().shape;
355+
points.pop();
353356
const angles = [];
354357
var p0;
355358
var p1;
@@ -384,18 +387,14 @@ class BuildingShapeUtils extends ShapeUtils {
384387
*/
385388
static edgeDirection(shape) {
386389
const points = shape.extractPoints().shape;
390+
points.pop();
387391
const angles = [];
388392
var p1;
389393
var p2;
390-
for (const i in points) {
394+
for (let i = 0; i < points.length; i++) {
391395
p1 = points[i];
392396
p2 = points[(i + 1) % points.length];
393397
let angle = Math.atan2((p2.y - p1.y), (p2.x - p1.x));
394-
if (angle >= Math.PI) {
395-
angle -= 2 * Math.PI;
396-
} else if (angle < -Math.PI) {
397-
angle += 2* Math.PI;
398-
}
399398
angles.push(angle);
400399
}
401400
return angles;
@@ -412,21 +411,36 @@ class BuildingShapeUtils extends ShapeUtils {
412411
static surrounds(shape, point) {
413412
var count = 0;
414413
const vecs = shape.extractPoints().shape;
414+
vecs.pop();
415415
var vec;
416416
var nextvec;
417-
for (let i = 0; i < vecs.length - 1; i++) {
417+
for (let i = 0; i < vecs.length; i++) {
418418
vec = vecs[i];
419-
nextvec = vecs[i+1];
419+
nextvec = vecs[(i + 1) % vecs.length];
420420
if (vec.x === point[0] && vec.y === point[1]) {
421421
return true;
422422
}
423-
const slope = (nextvec.y - vec.y) / (nextvec.x - vec.x);
424-
const intercept = vec.y / slope / vec.x;
425-
const intersection = (point[1] - intercept) / slope;
426-
if (intersection > point[0]) {
427-
count++;
428-
} else if (intersection === point[0]) {
429-
return true;
423+
if (nextvec.x === vec.x) {
424+
// vertical line
425+
if (vec.x === point[0]) {
426+
return true;
427+
}
428+
if (vec.x > point[0] && (vec.y > point[1] || nextvec.y > point[1]) && !(vec.y > point[1] && nextvec.y > point[1])){
429+
count++;
430+
}
431+
} else if (nextvec.y === vec.y) {
432+
if (vec.y === point[1] && (vec.x > point[0] || nextvec.x > point[0]) && !(vec.x > point[0] && nextvec.x > point[0])){
433+
return true;
434+
}
435+
} else {
436+
const slope = (nextvec.y - vec.y) / (nextvec.x - vec.x);
437+
const intercept = vec.y - slope * vec.x;
438+
const intersection = (point[1] - intercept) / slope;
439+
if (intersection > point[0] && intersection < Math.max(nextvec.x, vec.x) && intersection > Math.min(nextvec.x, vec.x)) {
440+
count++;
441+
} else if (intersection === point[0]) {
442+
return true;
443+
}
430444
}
431445
}
432446
return count % 2 === 1;

test/building.test.js

Lines changed: 27 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -31,26 +31,27 @@ import fetchMock from 'jest-fetch-mock';
3131
fetchMock.enableMocks();
3232

3333
const data = `
34-
<osm>
35-
<node id="3" lat="4" lon="4"/>
36-
<node id="5" lat="4" lon="4.001"/>
37-
<node id="6" lat="4.001" lon="4.001"/>
38-
<node id="7" lat="4.001" lon="4"/>
39-
<relation id="4">
40-
<member ref="1" role="outer"/>
41-
<tag k="type" v="multipolygon"/>
42-
<tag k="building" v="yes"/>
43-
<tag k="roof:shape" v="skillion"/>
44-
<tag k="roof:direction" v="0"/>
45-
<tag k="roof:angle" v="45"/>
46-
</relation>
47-
<way id="1">
48-
<nd ref="3"/>
49-
<nd ref="5"/>
50-
<nd ref="6"/>
51-
<nd ref="7"/>
52-
<nd ref="3"/>
53-
</way>
34+
<osm version="0.6" generator="openstreetmap-cgimap 2.0.1 (3529586 spike-06.openstreetmap.org)" copyright="OpenStreetMap and contributors" attribution="http://www.openstreetmap.org/copyright" license="http://opendatacommons.org/licenses/odbl/1-0/">
35+
<node id="349300285" visible="true" version="2" changeset="16924847" timestamp="2013-07-12T11:32:52Z" user="Oberaffe" uid="56597" lat="49.5833130" lon="11.0155721"/>
36+
<node id="349300289" visible="true" version="2" changeset="16924847" timestamp="2013-07-12T11:32:52Z" user="Oberaffe" uid="56597" lat="49.5834188" lon="11.0154519"/>
37+
<node id="349300292" visible="true" version="2" changeset="16924847" timestamp="2013-07-12T11:32:52Z" user="Oberaffe" uid="56597" lat="49.5833130" lon="11.0154519"/>
38+
<node id="349300295" visible="true" version="2" changeset="16924847" timestamp="2013-07-12T11:32:52Z" user="Oberaffe" uid="56597" lat="49.5834188" lon="11.0155721"/>
39+
<way id="31361386" visible="true" version="7" changeset="103461964" timestamp="2021-04-23T08:01:35Z" user="hans007" uid="376477">
40+
<nd ref="349300292"/>
41+
<nd ref="349300289"/>
42+
<nd ref="349300295"/>
43+
<nd ref="349300285"/>
44+
<nd ref="349300292"/>
45+
<tag k="addr:city" v="Erlangen"/>
46+
<tag k="addr:country" v="DE"/>
47+
<tag k="addr:housenumber" v="30"/>
48+
<tag k="addr:postcode" v="91052"/>
49+
<tag k="addr:street" v="Badstraße"/>
50+
<tag k="building" v="detached"/>
51+
<tag k="building:levels" v="1"/>
52+
<tag k="roof:levels" v="2"/>
53+
<tag k="roof:shape" v="gabled"/>
54+
</way>
5455
</osm>`;
5556

5657
beforeEach(() => {
@@ -59,17 +60,20 @@ beforeEach(() => {
5960
});
6061

6162
test('Test Constructor', async() => {
62-
const bldg = new Building('4', data);
63-
expect(bldg.home).toBeDeepCloseTo([4.0005, 4.0005], 10);
63+
const bldg = new Building('31361386', data);
64+
expect(bldg.home).toBeDeepCloseTo([11.015512, 49.5833659], 10);
6465
expect(bldg.parts.length).toBe(0);
66+
expect(bldg.nodelist['349300285']).toStrictEqual([4.332747472106493, -5.882209888874915]);
67+
expect(bldg.nodelist['349300289']).toStrictEqual([-4.332738077015795, 5.88221335051411]);
6568
expect(errors.length).toBe(0);
6669
});
6770

6871
test('Create Nodelist', () => {
6972
let xmlData = new window.DOMParser().parseFromString(data, 'text/xml');
7073
const list = Building.buildNodeList(xmlData);
7174
expect(Object.keys(list).length).toBe(4);
72-
expect(list['3']).toStrictEqual(['4', '4']);
75+
// Long / Lat
76+
expect(list['349300285']).toStrictEqual(['11.0155721', '49.5833130']);
7377
expect(errors.length).toBe(0);
7478
});
7579

test/buildingpart.test.js

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,37 @@
1+
/**
2+
* @jest-environment jsdom
3+
*/
14
import { BuildingPart } from '../src/buildingpart.js';
5+
import { BuildingShapeUtils } from '../src/extras/BuildingShapeUtils.js';
6+
import { TextEncoder } from 'node:util';
7+
8+
const data = `
9+
<osm version="0.6" generator="openstreetmap-cgimap 2.0.1 (3529586 spike-06.openstreetmap.org)" copyright="OpenStreetMap and contributors" attribution="http://www.openstreetmap.org/copyright" license="http://opendatacommons.org/licenses/odbl/1-0/">
10+
<node id="349300285" visible="true" version="2" changeset="16924847" timestamp="2013-07-12T11:32:52Z" user="Oberaffe" uid="56597" lat="49.5833130" lon="11.0155721"/>
11+
<node id="349300289" visible="true" version="2" changeset="16924847" timestamp="2013-07-12T11:32:52Z" user="Oberaffe" uid="56597" lat="49.5834188" lon="11.0154519"/>
12+
<node id="349300292" visible="true" version="2" changeset="16924847" timestamp="2013-07-12T11:32:52Z" user="Oberaffe" uid="56597" lat="49.5833130" lon="11.0154519"/>
13+
<node id="349300295" visible="true" version="2" changeset="16924847" timestamp="2013-07-12T11:32:52Z" user="Oberaffe" uid="56597" lat="49.5834188" lon="11.0155721"/>
14+
<way id="31361386" visible="true" version="7" changeset="103461964" timestamp="2021-04-23T08:01:35Z" user="hans007" uid="376477">
15+
<nd ref="349300292"/>
16+
<nd ref="349300289"/>
17+
<nd ref="349300295"/>
18+
<nd ref="349300285"/>
19+
<nd ref="349300292"/>
20+
<tag k="addr:city" v="Erlangen"/>
21+
<tag k="addr:country" v="DE"/>
22+
<tag k="addr:housenumber" v="30"/>
23+
<tag k="addr:postcode" v="91052"/>
24+
<tag k="addr:street" v="Badstraße"/>
25+
<tag k="building" v="detached"/>
26+
<tag k="building:levels" v="1"/>
27+
<tag k="roof:levels" v="2"/>
28+
<tag k="roof:shape" v="gabled"/>
29+
</way>
30+
</osm>`;
31+
32+
beforeEach(() => {
33+
errors = [];
34+
});
235

336
test('Test Cardinal to Degree', () => {
437
expect(BuildingPart.cardinalToDegree('N')).toBe(0);
@@ -10,3 +43,41 @@ test('radToDeg', () => {
1043
expect (BuildingPart.atanRadToCompassDeg(0)).toBe(90);
1144
expect (BuildingPart.atanRadToCompassDeg(Math.PI / 2)).toBe(0);
1245
});
46+
47+
test('Constructor', () => {
48+
let xmlData = new window.DOMParser().parseFromString(data, 'text/xml');
49+
const nodes = {
50+
'349300285': [4.332747472106493, -5.882209888874915],
51+
'349300289': [-4.332738077015795, 5.88221335051411],
52+
'349300292': [-4.332738077015795, -5.882209888874915],
53+
'349300295': [4.332747472106493, 5.88221335051411],
54+
};
55+
const part = new BuildingPart('31361386', xmlData, nodes);
56+
expect(part.options.building.levels).toBe(1);
57+
expect(part.options.roof.levels).toBe(2);
58+
expect(part.options.roof.shape).toBe('gabled');
59+
60+
// Gabled with unspecified orientation shal be 'along'
61+
expect(part.options.roof.orientation).toBe('along');
62+
63+
// Troubleshoot Bug
64+
const shape = part.shape.extractPoints().shape;
65+
let value = [shape[0].x, shape[0].y];
66+
expect(value).toStrictEqual([-4.332738077015795, -5.882209888874915]);
67+
value = [shape[1].x, shape[1].y];
68+
expect(value).toStrictEqual([-4.332738077015795, 5.88221335051411]);
69+
value = [shape[2].x, shape[2].y];
70+
expect(value).toStrictEqual([4.332747472106493, 5.88221335051411]);
71+
expect(BuildingShapeUtils.edgeDirection(part.shape)).toStrictEqual([Math.PI / 2, 0, -Math.PI / 2, Math.PI]);
72+
expect(BuildingShapeUtils.longestSideAngle(part.shape)).toBe(Math.PI / 2);
73+
expect(part.options.roof.direction).toBe(0);
74+
expect(errors.length).toBe(0);
75+
});
76+
77+
window.printError = printError;
78+
79+
var errors = [];
80+
81+
function printError(txt) {
82+
errors.push[txt];
83+
}

test/utils.test.js

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -116,11 +116,13 @@ const rightTriangle = new Shape();
116116
rightTriangle.moveTo(1, 1);
117117
rightTriangle.lineTo(1, -1);
118118
rightTriangle.lineTo(-1, 1);
119+
rightTriangle.lineTo(1, 1);
119120

120121
const rightTriangle2 = new Shape();
121122
rightTriangle2.moveTo(1, 1);
122123
rightTriangle2.lineTo(-1, 1);
123124
rightTriangle2.lineTo(1, -1);
125+
rightTriangle2.lineTo(1, 1);
124126

125127
test('Extents no Rotation', () => {
126128
expect(BuildingShapeUtils.extents(rightTriangle)).toStrictEqual([-1, -1, 1, 1]);
@@ -149,7 +151,7 @@ test('Vertex Angles counterclockwise', () => {
149151
/** Test edgeDirection */
150152
describe.each([
151153
[rightTriangle, [-Math.PI / 2, 3 * Math.PI / 4, 0], 'CW'],
152-
[rightTriangle2, [-Math.PI, -Math.PI / 4, Math.PI / 2], 'CCW'],
154+
[rightTriangle2, [Math.PI, -Math.PI / 4, Math.PI / 2], 'CCW'],
153155
])('Edge Direction', (shape, expected, description) =>{
154156
test(`${description}`, () => {
155157
expect(BuildingShapeUtils.edgeDirection(shape)).toBeDeepCloseTo(expected);
@@ -158,7 +160,8 @@ describe.each([
158160

159161
/** Test surrounds */
160162
describe.each([
161-
[[-1, -1], false, 'Outside'],
163+
[[-.5, -.5], false, 'Outside but crossing'],
164+
[[-1.5, -1.5], false, 'Outside no crossings'],
162165
[[1, 1], true, 'Share Node'],
163166
[[.5, .5], true, 'Inside'],
164167
[[0, 0], true, 'Border'],
@@ -180,8 +183,8 @@ test('Longest side angle', () => {
180183

181184
/** Test repositionPoint */
182185
test('Reposition Point', () => {
183-
const point = [11.0155721, 49.583313];
186+
const point = [11.0154519, 49.5834188];
184187
const home = [11.015512, 49.5833659];
185-
const expected = [4.332747472234555, -5.882209888874915];
188+
const expected = [-4.3327380768877335, 5.88221335051411];
186189
expect(BuildingShapeUtils.repositionPoint(point, home)).toStrictEqual(expected);
187190
});

0 commit comments

Comments
 (0)