Skip to content

Commit f4c805d

Browse files
authored
Multipolygon part must be inside (#139)
1 parent 07fa9c8 commit f4c805d

File tree

4 files changed

+83
-12
lines changed

4 files changed

+83
-12
lines changed

src/building.js

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -219,11 +219,14 @@ class Building {
219219
}
220220
// Filter all relations
221221
parts = this.fullXmlData.getElementsByTagName('relation');
222-
for (let i = 0; i < parts.length; i++) {
223-
if (parts[i].querySelector('[k="building:part"]')) {
224-
const id = parts[i].getAttribute('id');
222+
for (const xmlPart of parts) {
223+
if (xmlPart.querySelector('[k="building:part"]')) {
224+
const id = xmlPart.getAttribute('id');
225225
try {
226-
this.parts.push(new MultiBuildingPart(id, this.fullXmlData, this.nodelist, this.outerElement.options));
226+
const part = new MultiBuildingPart(id, this.fullXmlData, this.nodelist, this.outerElement.options);
227+
if (this.partIsInside(part)) {
228+
this.parts.push(part);
229+
}
227230
} catch (e) {
228231
window.printError(e);
229232
}
@@ -416,7 +419,7 @@ class Building {
416419

417420
/**
418421
* Check if any point in a part is within this building's outline.
419-
* It only checknof points are inside, not if crossing events occur, or
422+
* It only checks if points are inside, not if crossing events occur, or
420423
* if the part completly surrounds the building.
421424
* @param {BuildingPart} part - the part to be tested
422425
* @returns {bool} is it?
@@ -428,6 +431,8 @@ class Building {
428431
return true;
429432
}
430433
}
434+
// @todo
435+
// return BuildingShapeUtils.surrounds(this.outerElement.shape, part.center);
431436
return false;
432437
}
433438
}

src/buildingpart.js

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,13 +22,13 @@ class BuildingPart {
2222
// DOM of the building part way
2323
way;
2424

25-
// THREE.Shape of the outline.
25+
/** @type {THREE.Shape} the outline. */
2626
shape;
2727

28-
// THREE.Mesh of the roof
28+
/** @type {THREE.Mesh} the roof */
2929
roof;
3030

31-
// array of Cartesian coordinates of every node.
31+
/** @type {Object.<string, [number, number]>} Cartesian coordinates of every node keyed by refId. */
3232
nodelist = [];
3333

3434
// Metadata of the building part.
@@ -66,7 +66,7 @@ class BuildingPart {
6666
/**
6767
* @param {number} id - the OSM id of the way or multipolygon.
6868
* @param {XMLDocument} fullXmlData - XML for the region.
69-
* @param {[[number, number],...]} nodelist - Cartesian coordinates of each node keyed by node refID
69+
* @param {Object.<string, [number, number]>} nodelist - Cartesian coordinates of each node keyed by node refID
7070
* @param {object} options - default values for the building part.
7171
*/
7272
constructor(id, fullXmlData, nodelist, defaultOptions = {}) {

test/building.test.js

Lines changed: 67 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -315,8 +315,7 @@ test('Test downloading type=building with multipolygon outline and multiple inne
315315
expect(global.fetch.mock.calls[2][0]).toBe(urlBase + 'map?bbox=30.4980057,59.9380365,30.4993839,59.9385087');
316316
});
317317

318-
test('Part must be within outline', () => {
319-
const data = `<?xml version="1.0" encoding="UTF-8"?>
318+
const nonIntersectingWays = `<?xml version="1.0" encoding="UTF-8"?>
320319
<osm>
321320
<node id="1" lat="0.001" lon="0.001"/>
322321
<node id="2" lat="0.001" lon="0"/>
@@ -342,8 +341,73 @@ test('Part must be within outline', () => {
342341
</way>
343342
</osm>
344343
`;
345-
expect(new Building('11', data).parts.length).toBe(0);
344+
345+
const nonIntersectingWayAndMulti = `<?xml version="1.0" encoding="UTF-8"?>
346+
<osm>
347+
<node id="1" lat="0.001" lon="0.001"/>
348+
<node id="2" lat="0.001" lon="0"/>
349+
<node id="3" lat="0" lon="0"/>
350+
<node id="4" lat="0" lon=".0005"/>
351+
<node id="5" lat="0" lon=".001"/>
352+
<node id="6" lat=".0001" lon=".001"/>
353+
<node id="7" lat=".0001" lon="0.005"/>
354+
<way id="11">
355+
<nd ref="1"/>
356+
<nd ref="2"/>
357+
<nd ref="3"/>
358+
<nd ref="1"/>
359+
<tag k="building" v="apartments"/>
360+
</way>
361+
<way id="22">
362+
<nd ref="4"/>
363+
<nd ref="5"/>
364+
<nd ref="6"/>
365+
<nd ref="7"/>
366+
<nd ref="4"/>
367+
</way>
368+
<relation id="40">
369+
<member type="way" ref="22" role="outer"/>
370+
<tag k="building:part" v="yes"/>
371+
</relation>
372+
</osm>
373+
`;
374+
375+
describe.each([
376+
[nonIntersectingWays, 0, 'ways non-intersecting'],
377+
[nonIntersectingWayAndMulti, 0, 'multipolygon non-intersecting'],
378+
])('Part must be within outline', (data, expected, description) => {
379+
test(`${description}`, () => {
380+
expect(new Building('11', data).parts.length).toBe(expected);
381+
});
382+
});
383+
384+
/** Test partIsInside
385+
class BldgMock extends Building {
386+
387+
constructor() {
388+
this.shape = new Shape();
389+
}
390+
}
391+
392+
class PartMock {
393+
constructor(shape) {
394+
this.shape = shape;
395+
}
396+
}
397+
398+
describe.each([
399+
[[], false, 'Two Separate Ways'],
400+
[[], true, 'Two Ways share edge'],
401+
[[], false, 'multipolygon outside'],
402+
])('partIsInside', (shapePoints, expected, description) => {
403+
test(`${description}`, () => {
404+
const shape = new Shape(shapePoints.map((point) => new Vector2(...point)));
405+
const part = new PartMock(shape);
406+
const bldg = new BldgMock();
407+
expect(bldg.partIsInside(part)).toBe(expected);
408+
});
346409
});
410+
*/
347411

348412
window.printError = printError;
349413

test/multipolygon.test.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,8 @@ test('Test Simple Multipolygon', () => {
4646
expect(errors.length).toBe(0);
4747
});
4848

49+
// @todo Test a multipolygon with multiple closed outer ways.
50+
4951
window.printError = printError;
5052

5153
var errors = [];

0 commit comments

Comments
 (0)