Skip to content

Commit fd10245

Browse files
Beakerboydeevroman
andauthored
Parts inside (#3)
* Hipped roof (Beakerboy#128) * Update BuildingShapeUtils.js (Beakerboy#129) * Update BuildingShapeUtils.js (Beakerboy#130) * Update utils.test.js (Beakerboy#132) * Dev (Beakerboy#137) * Update BuildingShapeUtils.js * Update utils.test.js * Update utils.test.js * Update buildingpart.test.js * Update BuildingShapeUtils.js * Update buildingpart.test.js * Update from main (Beakerboy#133) * Split data downloading and building creating (Beakerboy#118) * Outer building visibility (Beakerboy#119) * Visibility (Beakerboy#120) * Update building.js (Beakerboy#121) * Tests for API (Beakerboy#122) * check HTTP code status and show alert with error * tests for API errors * Show validation errors (Beakerboy#123) * show validation errors + tests * fix typo * Skip incompleted ways, skip non-way members, prevent global modification of way object (Beakerboy#100) * skip non-way members, skip incompleted ways, prevent global modification of Document with way * add test * better colors for MeshPhysicalMaterial (Beakerboy#126) * Fix crash when processing type=building with outline being a multipolygon (Beakerboy#124) * Beakerboy#88 initial support type=building with multipolygon outline * support multiple ways in inner rings * add test * Hipped roof (Beakerboy#128) * Update BuildingShapeUtils.js (Beakerboy#129) * Update BuildingShapeUtils.js (Beakerboy#130) * Update utils.test.js (Beakerboy#132) --------- Co-authored-by: Roman Deev <roman.deev06@gmail.com> * Update building.test.js * Update buildingpart.js * Fixed direction bug (Beakerboy#136) * Update utils.test.js --------- Co-authored-by: Roman Deev <roman.deev06@gmail.com> * Update README.md * Update building.test.js * Update building.test.js * Update building.test.js * Update building.js * Update building.js * Update building.js * Update building.js * Update building.js * Update building.js --------- Co-authored-by: Roman Deev <roman.deev06@gmail.com>
1 parent c268a47 commit fd10245

File tree

7 files changed

+72
-19
lines changed

7 files changed

+72
-19
lines changed

README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,12 +33,13 @@ Roof Types:
3333
* Dome
3434
* Pyramidal
3535
* Gabled
36+
* Hipped
3637

3738
Examples:
3839
* Simple building with no parts - [Washington Monument](https://beakerboy.github.io/OSMBuilding/index.html?id=766761337)
3940
* Glass - [Petronas Towers](https://beakerboy.github.io/OSMBuilding/index.html?id=279944536)
4041
* Dome roof, Gabled roof, and Skillion ramp - [Jefferson Memorial](https://beakerboy.github.io/OSMBuilding/index.html?type=relation&id=3461570)
41-
* Dome, Gabled, and Pyramidal Roof - [US Capitol](https://beakerboy.github.io/OSMBuilding/index.html?type=relation&id=12286916)
42+
* Dome, Gabled, Hipped, and Pyramidal Roof - [US Capitol](https://beakerboy.github.io/OSMBuilding/index.html?type=relation&id=12286916)
4243
* [Chrysler Building](https://beakerboy.github.io/OSMBuilding/index.html?id=42500770)
4344
* Building Relation [Burj Khalifa](https://beakerboy.github.io/OSMBuilding/index.html?type=relation&id=7584462)
4445
* Multipolygon with no parts - [Freer Art Gallery](https://beakerboy.github.io/OSMBuilding/index.html?type=relation&id=1029355)

src/building.js

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -206,10 +206,13 @@ class Building {
206206
} else {
207207
// Filter to all ways
208208
var parts = this.fullXmlData.getElementsByTagName('way');
209-
for (let j = 0; j < parts.length; j++) {
210-
if (parts[j].querySelector('[k="building:part"]')) {
211-
const id = parts[j].getAttribute('id');
212-
this.parts.push(new BuildingPart(id, this.fullXmlData, this.nodelist, this.outerElement.options));
209+
for (const xmlPart of parts) {
210+
if (xmlPart.querySelector('[k="building:part"]')) {
211+
const id = xmlPart.getAttribute('id');
212+
const part = new BuildingPart(id, this.fullXmlData, this.nodelist, this.outerElement.options);
213+
if (this.partIsInside(part)) {
214+
this.parts.push(part);
215+
}
213216
}
214217
}
215218
// Filter all relations
@@ -408,5 +411,22 @@ class Building {
408411
}
409412
}
410413
}
414+
415+
/**
416+
* Check if any point in a part is within this building's outline.
417+
* It only checknof points are inside, not if crossing events occur, or
418+
* if the part completly surrounds the building.
419+
* @param {BuildingPart} part - the part to be tested
420+
* @returns {bool} is it?
421+
*/
422+
partIsInside(part) {
423+
const shape = part.shape;
424+
for (const vector of shape.extractPoints().shape) {
425+
if (BuildingShapeUtils.surrounds(this.outerElement.shape, [vector.x, vector.y])) {
426+
return true;
427+
}
428+
}
429+
return false;
430+
}
411431
}
412432
export {Building};

src/buildingpart.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -270,7 +270,7 @@ class BuildingPart {
270270
const options = {
271271
center: center,
272272
angle: angle / 180 * Math.PI,
273-
depth: this.options.roof.height,
273+
depth: this.options.roof.height ?? 3,
274274
};
275275
const geometry = new WedgeGeometry(this.shape, options);
276276

@@ -434,7 +434,7 @@ class BuildingPart {
434434

435435
/**
436436
* OSM compass degrees are 0-360 clockwise.
437-
*
437+
* 0 degrees is North.
438438
* @return {number} degrees
439439
*/
440440
static atanRadToCompassDeg(rad) {

src/extras/BuildingShapeUtils.js

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@ class BuildingShapeUtils extends ShapeUtils {
2424
const elements = way.getElementsByTagName('nd');
2525

2626
// Get the coordinates of all the nodes and add them to the shape outline.
27-
2827
for (const element of elements) {
2928
ref = element.getAttribute('ref');
3029
nodes.push(nodelist[ref]);
@@ -45,7 +44,6 @@ class BuildingShapeUtils extends ShapeUtils {
4544
}
4645
return shape;
4746
}
48-
4947
/**
5048
* Check if a way is a closed shape.
5149
*
@@ -288,7 +286,6 @@ class BuildingShapeUtils extends ShapeUtils {
288286
*/
289287
static combineCoordinates(shape) {
290288
const points = shape.extractPoints().shape;
291-
points.pop();
292289
var x = [];
293290
var y = [];
294291
var vec;
@@ -338,7 +335,6 @@ class BuildingShapeUtils extends ShapeUtils {
338335
*/
339336
static edgeLength(shape) {
340337
const points = shape.extractPoints().shape;
341-
points.pop();
342338
const lengths = [];
343339
var p1;
344340
var p2;
@@ -360,7 +356,6 @@ class BuildingShapeUtils extends ShapeUtils {
360356
*/
361357
static vertexAngle(shape) {
362358
const points = shape.extractPoints().shape;
363-
points.pop();
364359
const angles = [];
365360
var p0;
366361
var p1;
@@ -395,7 +390,6 @@ class BuildingShapeUtils extends ShapeUtils {
395390
*/
396391
static edgeDirection(shape) {
397392
const points = shape.extractPoints().shape;
398-
points.pop();
399393
const angles = [];
400394
var p1;
401395
var p2;
@@ -419,7 +413,6 @@ class BuildingShapeUtils extends ShapeUtils {
419413
static surrounds(shape, point) {
420414
var count = 0;
421415
const vecs = shape.extractPoints().shape;
422-
vecs.pop();
423416
var vec;
424417
var nextvec;
425418
for (let i = 0; i < vecs.length; i++) {
@@ -469,7 +462,7 @@ class BuildingShapeUtils extends ShapeUtils {
469462
* Return the angle of the longest side of a shape with 90° vertices.
470463
*
471464
* @param {THREE.Shape} shape - the shape
472-
* @return {number}
465+
* @return {number} in radians from Pi > x > -Pi
473466
*/
474467
static longestSideAngle(shape) {
475468
const lengths = BuildingShapeUtils.edgeLength(shape);

test/building.test.js

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -309,6 +309,40 @@ test('Test downloading type=building with multipolygon outline and multiple inne
309309
const building = new Building('42', innerData);
310310
expect(building.id).toBe('42');
311311
expect(building.outerElement.shape.holes.length).toBe(1);
312+
const urlBase = 'https://api.openstreetmap.org/api/0.6/';
313+
expect(global.fetch.mock.calls[0][0]).toBe(urlBase + 'relation/42/full');
314+
expect(global.fetch.mock.calls[1][0]).toBe(urlBase + 'relation/40/full');
315+
expect(global.fetch.mock.calls[2][0]).toBe(urlBase + 'map?bbox=30.4980057,59.9380365,30.4993839,59.9385087');
316+
});
317+
318+
test('Part must be within outline', () => {
319+
const data = `<?xml version="1.0" encoding="UTF-8"?>
320+
<osm>
321+
<node id="1" lat="0.001" lon="0.001"/>
322+
<node id="2" lat="0.001" lon="0"/>
323+
<node id="3" lat="0" lon="0"/>
324+
<node id="4" lat="0" lon=".0005"/>
325+
<node id="5" lat="0" lon=".001"/>
326+
<node id="6" lat=".0001" lon=".001"/>
327+
<node id="7" lat=".0001" lon="0.005"/>
328+
<way id="11">
329+
<nd ref="1"/>
330+
<nd ref="2"/>
331+
<nd ref="3"/>
332+
<nd ref="1"/>
333+
<tag k="building" v="apartments"/>
334+
</way>
335+
<way id="22">
336+
<nd ref="4"/>
337+
<nd ref="5"/>
338+
<nd ref="6"/>
339+
<nd ref="7"/>
340+
<nd ref="4"/>
341+
<tag k="building:part" v="yes"/>
342+
</way>
343+
</osm>
344+
`;
345+
expect(new Building('11', data).parts.length).toBe(0);
312346
});
313347

314348
window.printError = printError;

test/buildingpart.test.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,8 @@ test('Constructor', () => {
6161
expect(part.options.roof.orientation).toBe('along');
6262

6363
// toDo: Mock BuildingShapeUtils and test options
64+
expect(BuildingShapeUtils.edgeDirection(part.shape)).toStrictEqual([1.5707963267948966, 0, -1.5707963267948966, 3.141592653589793]);
65+
expect(BuildingShapeUtils.longestSideAngle(part.shape)).toBe(1.5707963267948966);
6466
expect(part.options.roof.direction).toBe(90);
6567
expect(errors.length).toBe(0);
6668
});

test/utils.test.js

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,12 +25,11 @@ test('', () => {
2525
3: [-1, 1],
2626
};
2727
const shape = BuildingShapeUtils.createShape(xmlData, nodelist);
28-
expect(shape.extractPoints().shape.length).toBe(4);
28+
expect(shape.extractPoints().shape.length).toBe(3);
2929
const points = shape.extractPoints().shape;
3030
expect([points[0].x, points[0].y]).toStrictEqual(nodelist[1]);
3131
expect([points[1].x, points[1].y]).toStrictEqual(nodelist[2]);
3232
expect([points[2].x, points[2].y]).toStrictEqual(nodelist[3]);
33-
expect([points[0].x, points[0].y]).toStrictEqual(nodelist[1]);
3433
});
3534

3635
/** Test isClosed */
@@ -116,13 +115,17 @@ const rightTriangle = new Shape();
116115
rightTriangle.moveTo(1, 1);
117116
rightTriangle.lineTo(1, -1);
118117
rightTriangle.lineTo(-1, 1);
119-
rightTriangle.lineTo(1, 1);
120118

121119
const rightTriangle2 = new Shape();
122120
rightTriangle2.moveTo(1, 1);
123121
rightTriangle2.lineTo(-1, 1);
124122
rightTriangle2.lineTo(1, -1);
125-
rightTriangle2.lineTo(1, 1);
123+
124+
const rectangle = new Shape();
125+
rectangle.moveTo(-4.332738077015795, -5.882209888874915);
126+
rectangle.lineTo(-4.332738077015795, 5.88221335051411);
127+
rectangle.lineTo(4.332747472106493, 5.88221335051411);
128+
rectangle.lineTo(4.332747472106493, -5.882209888874915);
126129

127130
const rectangle = new Shape();
128131
rectangle.moveTo(-4.332738077015795, -5.882209888874915);

0 commit comments

Comments
 (0)