Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix vertical problems in polygons triangulation #1729

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 32 additions & 1 deletion src/Converter/Feature2Mesh.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,11 @@ const _color = new THREE.Color();
const maxValueUint8 = Math.pow(2, 8) - 1;
const maxValueUint16 = Math.pow(2, 16) - 1;
const maxValueUint32 = Math.pow(2, 32) - 1;
const quaternion = new THREE.Quaternion();
const normal = new THREE.Vector3(0, 0, 0);
const v0 = new THREE.Vector3(0, 0, 0);
const v1 = new THREE.Vector3(0, 0, 0);
const vZ = new THREE.Vector3(0, 0, 1);

function toColor(color) {
if (color) {
Expand Down Expand Up @@ -274,8 +279,33 @@ function featureToPolygon(feature, options) {

const geomVertices = vertices.slice(start * 3, end * 3);
const holesOffsets = geometry.indices.map(i => i.offset - start).slice(1);
const triangles = Earcut(geomVertices, holesOffsets, 3);

const reprojection = options.withoutPlanReprojection ? !options.withoutPlanReprojection : true;
if (reprojection) {
// transform all vertices to XY plan
// calculate average plane from points
// http://www.les-mathematiques.net/phorum/read.php?13,728203,728209#msg-728209
const size = count * 3;
for (let i = 3; i < size; i += 3) {
v0.fromArray(geomVertices, i - 3);
v1.fromArray(geomVertices, i);
normal.x += (v0.y - v1.y) * (v0.z + v1.z);
normal.y += (v0.z - v1.z) * (v0.x + v1.x);
normal.z += (v0.x - v1.x) * (v0.y + v1.y);
}

// calculate normal plane
normal.normalize();
// calculate quaternion to transform the normal plane to z axis
quaternion.setFromUnitVectors(normal, vZ);

// apply transformation to vertices
for (let i = 0; i < size; i += 3) {
v1.fromArray(geomVertices, i).applyQuaternion(quaternion);
v1.toArray(geomVertices, i);
}
}
const triangles = Earcut(geomVertices, holesOffsets, 3);
const startIndice = indices.length;
indices.length += triangles.length;

Expand Down Expand Up @@ -442,6 +472,7 @@ export default {
* a THREE.Group.
*
* @param {Object} options - options controlling the conversion
* @param {Boolean} [options.withoutPlanReprojection] - to avoid useless plan reprojection for non-vertical meshes
* @param {function} [options.batchId] - optional function to create batchId attribute. It is passed the feature property and the feature index. As the batchId is using an unsigned int structure on 32 bits, the batchId could be between 0 and 4,294,967,295.
* @return {function}
* @example <caption>Example usage of batchId with featureId.</caption>
Expand Down
10 changes: 9 additions & 1 deletion src/Parser/GeoJsonParser.js
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,15 @@ const toFeature = {

// Then read contour and holes
for (let i = 0; i < coordsIn.length; i++) {
this.populateGeometry(crsIn, coordsIn[i], geometry, feature);
// GeoJson standard: The first and last positions are equivalent,
// and they MUST contain identical values; their representation SHOULD also be identical.
const ring = coordsIn[i];
if (ring.length > 1) {
if (ring[0].every((v, i) => v == ring[ring.length - 1][i])) {
ring.pop();
}
}
this.populateGeometry(crsIn, ring, geometry, feature);
}
feature.updateExtent(geometry);
},
Expand Down
40 changes: 31 additions & 9 deletions test/data/geojson/holes.geojson.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,19 +12,25 @@
0
],
[
0,
1
2,
0
],
[
2,
1
],
[
2,
0,
1
],
[
0,
0
]
]
]
},
"properties" : {
}
},
{
Expand All @@ -38,19 +44,25 @@
0.3
],
[
0.2,
0.7
1.4,
0.3
],
[
1.4,
0.7
],
[
1.4,
0.2,
0.7
],
[
0.2,
0.3
]
]
]
},
"properties" : {
}
},
{
Expand All @@ -64,15 +76,19 @@
0
],
[
0,
1
2,
0
],
[
2,
1
],
[
2,
0,
1
],
[
0,
0
]
],
Expand All @@ -92,9 +108,15 @@
[
1.4,
0.3
],
[
0.2,
0.3
]
]
]
},
"properties" : {
}
}
]
Expand Down
8 changes: 4 additions & 4 deletions test/data/geojson/simple.geojson.json
Original file line number Diff line number Diff line change
Expand Up @@ -42,16 +42,16 @@
43.715534726205114
],
[
0.9067382756620646,
43.72744458647463
0.36291503347456455,
43.15710884095329
],
[
1.1044921819120646,
43.37311218382002
],
[
0.36291503347456455,
43.15710884095329
0.9067382756620646,
43.72744458647463
],
[
0.30798339284956455,
Expand Down
42 changes: 42 additions & 0 deletions test/data/geojson/vertical.geojson.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
{
"type": "FeatureCollection",
"features": [
{
"type": "Feature",
"geometry": {
"type": "Polygon",
"coordinates": [
[
[
0,
0,
0
],
[
0,
0,
1
],
[
0,
1,
1
],
[
0,
1,
0
],
[
0,
0,
0
]
]
]
},
"properties": {
}
}
]
}
24 changes: 23 additions & 1 deletion test/unit/feature2mesh.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import Feature2Mesh from 'Converter/Feature2Mesh';

const geojson = require('../data/geojson/holes.geojson.json');
const geojson2 = require('../data/geojson/simple.geojson.json');
const geojson3 = require('../data/geojson/vertical.geojson.json');


proj4.defs('EPSG:3946',
'+proj=lcc +lat_1=45.25 +lat_2=46.75 +lat_0=46 +lon_0=3 +x_0=1700000 +y_0=5200000 +ellps=GRS80 +towgs84=0,0,0,0,0,0,0 +units=m +no_defs');
Expand All @@ -29,6 +31,7 @@ function computeAreaOfMesh(mesh) {
describe('Feature2Mesh', function () {
const parsed = GeoJsonParser.parse(geojson, { in: { crs: 'EPSG:3946' }, out: { crs: 'EPSG:3946', buildExtent: true, mergeFeatures: false, structure: '3d' } });
const parsed2 = GeoJsonParser.parse(geojson2, { in: { crs: 'EPSG:3946' }, out: { crs: 'EPSG:3946', buildExtent: true, mergeFeatures: false, structure: '3d' } });
const parsed3 = GeoJsonParser.parse(geojson3, { in: { crs: 'EPSG:3946' }, out: { crs: 'EPSG:3946', buildExtent: true, mergeFeatures: false, structure: '3d' } });

it('rect mesh area should match geometry extent', () =>
parsed.then((collection) => {
Expand All @@ -43,7 +46,6 @@ describe('Feature2Mesh', function () {
it('square mesh area should match geometry extent minus holes', () =>
parsed.then((collection) => {
const mesh = Feature2Mesh.convert()(collection);

const noHoleArea = computeAreaOfMesh(mesh.children[0]);
const holeArea = computeAreaOfMesh(mesh.children[1]);
const meshWithHoleArea = computeAreaOfMesh(mesh.children[2]);
Expand All @@ -53,6 +55,26 @@ describe('Feature2Mesh', function () {
meshWithHoleArea);
}));

it('vertical polygon triangulation', () =>
parsed3.then((collection) => {
const mesh = Feature2Mesh.convert()(collection);
const geom = mesh.children[0].geometry;
assert.equal(
geom.index.count,
6,
);
}));

it('vertical polygon triangulation without plan reprojection', () =>
parsed3.then((collection) => {
const mesh = Feature2Mesh.convert({ withoutPlanReprojection: 'toto' })(collection);
const geom = mesh.children[0].geometry;
assert.equal(
geom.index.count,
0,
);
}));

it('convert points, lines and mesh', () =>
parsed2.then((collection) => {
const mesh = Feature2Mesh.convert()(collection);
Expand Down
2 changes: 1 addition & 1 deletion test/unit/source.js
Original file line number Diff line number Diff line change
Expand Up @@ -241,7 +241,7 @@ describe('Sources', function () {
layer.whenReady.then(() => {
const promise = source.loadData([], layer);
promise.then((featureCollection) => {
assert.equal(featureCollection.features[0].vertices.length, 3536);
assert.equal(featureCollection.features[0].vertices.length, 3534);
done();
});
});
Expand Down