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

Preparation for PR reprojection 'a la volee' #2422

Open
wants to merge 9 commits into
base: master
Choose a base branch
from
9 changes: 6 additions & 3 deletions src/Core/Geographic/Crs.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ function formatToEPSG(crs) {
const UNIT = {
DEGREE: 1,
METER: 2,
FOOT: 3,
};

function is4326(crs) {
Expand All @@ -48,8 +49,10 @@ function isGeocentric(crs) {
function _unitFromProj4Unit(projunit) {
if (projunit === 'degrees') {
return UNIT.DEGREE;
} else if (projunit === 'm') {
} else if (projunit === 'm' || projunit === 'meter') {
return UNIT.METER;
} else if (projunit === 'foot') {
return UNIT.FOOT;
Desplandis marked this conversation as resolved.
Show resolved Hide resolved
} else {
return undefined;
}
Expand All @@ -61,7 +64,7 @@ function toUnit(crs) {
case 'EPSG:4326' : return UNIT.DEGREE;
case 'EPSG:4978' : return UNIT.METER;
default: {
const p = proj4.defs(formatToEPSG(crs));
const p = proj4.defs(crs);
if (!p) {
return undefined;
}
Expand Down Expand Up @@ -129,7 +132,7 @@ export default {
* Get the unit to use with the CRS.
*
* @param {string} crs - The CRS to get the unit from.
* @return {number} Either `UNIT.METER`, `UNIT.DEGREE` or `undefined`.
* @return {number} Either `UNIT.METER`, `UNIT.DEGREE`, `UNIT.FOOT` or `undefined`.
*/
toUnit,

Expand Down
2 changes: 1 addition & 1 deletion src/Layer/GeometryLayer.js
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ class GeometryLayer extends Layer {
configurable: true,
});

this.opacity = 1.0;
this.opacity = config.opacity ?? 1.0;
this.wireframe = false;

this.attachedLayers = [];
Expand Down
14 changes: 5 additions & 9 deletions src/Layer/PointCloudLayer.js
Original file line number Diff line number Diff line change
Expand Up @@ -282,10 +282,6 @@ class PointCloudLayer extends GeometryLayer {
redraw: true,
earlyDropFunction: cmd => !cmd.requester.visible || !this.visible,
}).then((pts) => {
if (this.onPointsCreated) {
this.onPointsCreated(layer, pts);
}

Desplandis marked this conversation as resolved.
Show resolved Hide resolved
elt.obj = pts;
// store tightbbox to avoid ping-pong (bbox = larger => visible, tight => invisible)
elt.tightbbox = pts.tightbbox;
Expand All @@ -294,12 +290,12 @@ class PointCloudLayer extends GeometryLayer {
// be added nor cleaned
this.group.add(elt.obj);
elt.obj.updateMatrixWorld(true);

elt.promise = null;
}, (err) => {
if (err.isCancelledCommandException) {
elt.promise = null;
}).catch((err) => {
if (!err.isCancelledCommandException) {
return err;
}
}).finally(() => {
elt.promise = null;
});
}
}
Expand Down
18 changes: 16 additions & 2 deletions src/Source/CopcSource.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import proj4 from 'proj4';
import { Binary, Info, Las } from 'copc';
import Extent from 'Core/Geographic/Extent';
import Fetcher from 'Provider/Fetcher';
Expand Down Expand Up @@ -102,8 +103,21 @@ class CopcSource extends Source {
this.header = metadata.header;
this.info = metadata.info;
this.eb = metadata.eb;
// TODO: use wkt definition in `metadata.wkt` to infer/define crs
this.crs = config.crs || 'EPSG:4326';

proj4.defs('unknown', metadata.wkt);
let projCS;

if (proj4.defs('unknown').type === 'COMPD_CS') {
console.warn('CopcSource: compound coordinate system is not yet supported.');
projCS = proj4.defs('unknown').PROJCS;
} else {
projCS = proj4.defs('unknown');
}

this.crs = projCS.title || projCS.name || 'EPSG:4326';
if (!(this.crs in proj4.defs)) {
proj4.defs(this.crs, projCS);
}

const bbox = new THREE.Box3();
bbox.min.fromArray(this.info.cube, 0);
Expand Down
19 changes: 14 additions & 5 deletions src/Source/EntwinePointTileSource.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,12 +40,20 @@ class EntwinePointTileSource extends Source {
this.parse = metadata.dataType === 'laszip' ? LASParser.parse : PotreeBinParser.parse;
this.extension = metadata.dataType === 'laszip' ? 'laz' : 'bin';

if (metadata.srs && metadata.srs.authority && metadata.srs.horizontal) {
this.crs = `${metadata.srs.authority}:${metadata.srs.horizontal}`;
if (!proj4.defs(this.crs)) {
proj4.defs(this.crs, metadata.srs.wkt);
if (metadata.srs) {
if (metadata.srs.authority && metadata.srs.horizontal) {
this.crs = `${metadata.srs.authority}:${metadata.srs.horizontal}`;
if (!proj4.defs(this.crs)) {
proj4.defs(this.crs, metadata.srs.wkt);
}
} else if (metadata.srs.wkt) {
proj4.defs('unknown', metadata.srs.wkt);
const projCS = proj4.defs('unknown');
this.crs = projCS.title || projCS.name;
if (!(this.crs in proj4.defs)) {
proj4.defs(this.crs, projCS);
}
}

if (metadata.srs.vertical && metadata.srs.vertical !== metadata.srs.horizontal) {
console.warn('EntwinePointTileSource: Vertical coordinates system code is not yet supported.');
}
Expand All @@ -58,6 +66,7 @@ class EntwinePointTileSource extends Source {
+ Math.abs(metadata.boundsConforming[4] - metadata.boundsConforming[1])) / (2 * metadata.span);

this.boundsConforming = metadata.boundsConforming;
this.bounds = metadata.bounds;
this.span = metadata.span;

return this;
Expand Down
31 changes: 31 additions & 0 deletions test/unit/copc.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import assert from 'assert';
import { HttpsProxyAgent } from 'https-proxy-agent';
import CopcSource from 'Source/CopcSource';

const copcUrl = 'https://s3.amazonaws.com/hobu-lidar/autzen-classified.copc.laz';

describe('COPC', function () {
let source;

describe('Copc Source', function () {
describe('retrieving crs from wkt information', function () {
it('wkt.srs.type is COMPD_CS', function (done) {
const networkOptions = process.env.HTTPS_PROXY ? { agent: new HttpsProxyAgent(process.env.HTTPS_PROXY) } : {};
source = new CopcSource({
url: copcUrl,
networkOptions,
});
source.whenReady
.then((headers) => {
assert.ok(headers.header.pointCount);
assert.ok(headers.info.spacing);
assert.ok(Array.isArray(headers.eb));
assert.equal(source.crs, 'NAD83 / Oregon GIC Lambert (ft)');
// when the proj4 PR will be merged we should change to :
// assert.equal(source.crs, 'EPSG:2992');
done();
}).catch(done);
}).timeout(5000);
});
});
});
57 changes: 41 additions & 16 deletions test/unit/entwine.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,23 @@ import sinon from 'sinon';
import Fetcher from 'Provider/Fetcher';
import Renderer from './bootstrap';

import ept from '../data/entwine/ept.json';
import eptHierarchy from '../data/entwine/ept-hierarchy/0-0-0-0.json';
import eptFile from '../data/entwine/ept.json';
import eptHierarchyFile from '../data/entwine/ept-hierarchy/0-0-0-0.json';

const baseurl = 'https://raw.githubusercontent.com/iTowns/iTowns2-sample-data/master/pointclouds';
const urlEpt = `${baseurl}/entwine/ept.json`;
const urlEptHierarchy = `${baseurl}/entwine/ept-hierarchy/0-0-0-0.json`;
// LASParser need to be mocked instead of calling it
LASParser.enableLazPerf('./examples/libs/laz-perf');

const baseurl = 'https://raw.githubusercontent.com/iTowns/iTowns2-sample-data/master/pointclouds/entwine';

const eptSsAuthority = JSON.parse(eptFile);
eptSsAuthority.srs = {
wkt: 'PROJCS["RGF93 v1 / Lambert-93",GEOGCS["RGF93 v1",DATUM["Reseau_Geodesique_Francais_1993_v1",SPHEROID["GRS 1980",6378137,298.257222101],TOWGS84[0,0,0,0,0,0,0]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.0174532925199433,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4171"]],PROJECTION["Lambert_Conformal_Conic_2SP"],PARAMETER["latitude_of_origin",46.5],PARAMETER["central_meridian",3],PARAMETER["standard_parallel_1",49],PARAMETER["standard_parallel_2",44],PARAMETER["false_easting",700000],PARAMETER["false_northing",6600000],UNIT["metre",1,AUTHORITY["EPSG","9001"]],AXIS["Easting",EAST],AXIS["Northing",NORTH],AUTHORITY["EPSG","2154"]]',
};

const resources = {
[urlEpt]: ept,
[urlEptHierarchy]: eptHierarchy,
[`${baseurl}/ept.json`]: JSON.parse(eptFile),
'withoutAutority/ept.json': eptSsAuthority,
[`${baseurl}/ept-hierarchy/0-0-0-0.json`]: JSON.parse(eptHierarchyFile),
};

describe('Entwine Point Tile', function () {
Expand All @@ -29,15 +36,12 @@ describe('Entwine Point Tile', function () {

before(function () {
stubFetcherJson = sinon.stub(Fetcher, 'json')
.callsFake(url => Promise.resolve(JSON.parse(resources[url])));
.callsFake(url => Promise.resolve(resources[url]));
stubFetcherArrayBuf = sinon.stub(Fetcher, 'arrayBuffer')
.callsFake(() => Promise.resolve(new ArrayBuffer()));
// currently no test on data fetched...

LASParser.enableLazPerf('./examples/libs/laz-perf');
source = new EntwinePointTileSource({
url: 'https://raw.githubusercontent.com/iTowns/iTowns2-sample-data/master/pointclouds/entwine',
});
});

after(async function () {
Expand All @@ -46,11 +50,32 @@ describe('Entwine Point Tile', function () {
await LASParser.terminate();
});

it('loads the EPT structure', (done) => {
source.whenReady
.then(() => {
done();
}).catch(done);
describe('Entwine Point Tile Source', function () {
describe('data type', function () {
// TO DO dataType in [laszip, binary, zstandard]
});
describe('retrieving crs from srs information', function () {
it('No srs authority', (done) => {
source = new EntwinePointTileSource({
url: 'withoutAutority',
});
source.whenReady
.then(() => {
assert.equal(source.crs, 'RGF93 v1 / Lambert-93');
done();
}).catch(done);
});
it('With srs authority', (done) => {
source = new EntwinePointTileSource({
url: 'https://raw.githubusercontent.com/iTowns/iTowns2-sample-data/master/pointclouds/entwine',
});
source.whenReady
.then(() => {
assert.equal(source.crs, 'EPSG:3857');
done();
}).catch(done);
});
});
});

describe('Layer', function () {
Expand Down
3 changes: 2 additions & 1 deletion test/unit/fetcher.js
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,8 @@ describe('Fetcher', function () {
describe('texture', function () {
// Fetcher.texture always send a texture even with a false url...
const url = 'https://data.geopf.fr/wmts?' +
'LAYER=ORTHOIMAGERY.ORTHOPHOTOS&FORMAT=image/jpeg&SERVICE=WMTS&VERSION=1.0.0&' +
'LAYER=ORTHOIMAGERY.ORTHOPHOTOS&FORMAT=image/jpeg' +
'&SERVICE=WMTS&VERSION=1.0.0&' +
'REQUEST=GetTile&STYLE=normal&' +
'TILEMATRIXSET=PM&TILEMATRIX=2&TILEROW=1&TILECOL=1';
it('should load a texture', (done) => {
Expand Down
69 changes: 63 additions & 6 deletions test/unit/lasparser.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,26 @@ const lasUrl = `${baseurl}/data_test.las`;
const url = 'https://github.com/connormanning/copc.js/raw/master/src/test/data';
const lazV14Url = `${url}/ellipsoid-1.4.laz`;

const copcUrl = `${url}/ellipsoid.copc.laz`;

describe('LASParser', function () {
let lasData;
let lazV14Data;
it('fetch binaries', async function () {
let copcData;
describe('fetch binaries', function () {
const networkOptions = process.env.HTTPS_PROXY ? { agent: new HttpsProxyAgent(process.env.HTTPS_PROXY) } : {};
lasData = await Fetcher.arrayBuffer(lasUrl, networkOptions);
lazV14Data = await Fetcher.arrayBuffer(lazV14Url, networkOptions);
}).timeout(4000);
it('fetch las data', async function () {
lasData = await Fetcher.arrayBuffer(lasUrl, networkOptions);
});
it('fetch laz data', async function () {
lazV14Data = await Fetcher.arrayBuffer(lazV14Url, networkOptions);
});
it('fetch copc data', async function _it() {
copcData = await Fetcher.arrayBuffer(copcUrl, networkOptions);
});
});

describe('unit tests', function () {
describe('unit tests', function _describe() {
const epsilon = 0.1;
LASParser.enableLazPerf('./examples/libs/laz-perf');

Expand All @@ -40,6 +50,7 @@ describe('LASParser', function () {
assert.ok(compareWithEpsilon(bufferGeometry.boundingBox.max.x + origin.x, header.max[0], epsilon));
assert.ok(compareWithEpsilon(bufferGeometry.boundingBox.max.y + origin.y, header.max[1], epsilon));
assert.ok(compareWithEpsilon(bufferGeometry.boundingBox.max.z + origin.z, header.max[2], epsilon));
await LASParser.terminate();
});

it('parses a laz file to a THREE.BufferGeometry', async function () {
Expand All @@ -59,9 +70,55 @@ describe('LASParser', function () {
assert.ok(compareWithEpsilon(bufferGeometry.boundingBox.max.x + origin.x, header.max[0], epsilon));
assert.ok(compareWithEpsilon(bufferGeometry.boundingBox.max.y + origin.y, header.max[1], epsilon));
assert.ok(compareWithEpsilon(bufferGeometry.boundingBox.max.z + origin.z, header.max[2], epsilon));
await LASParser.terminate();
});

afterEach(async function () {
it('parses a copc chunk to a THREE.BufferGeometry', async function _it() {
if (!copcData) { this.skip(); }
const header = {
fileSignature: 'LASF',
fileSourceId: 0,
globalEncoding: 16,
projectId: '00000000-0000-0000-0000000000000000',
majorVersion: 1,
minorVersion: 4,
systemIdentifier: '',
generatingSoftware: '',
fileCreationDayOfYear: 1,
fileCreationYear: 1,
headerLength: 375,
pointDataOffset: 1424,
vlrCount: 3,
pointDataRecordFormat: 7,
pointDataRecordLength: 36,
pointCount: 100000,
pointCountByReturn: [
50002, 49998, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0,
],
scale: [0.01, 0.01, 0.01],
offset: [-8242596, 4966606, 0],
min: [-8242746, 4966506, -50],
max: [-8242446, 4966706, 50],
waveformDataOffset: 0,
evlrOffset: 630520,
evlrCount: 1,
};
const options = {
in: {
pointCount: header.pointCount,
header,
},
// eb,
};
const bufferGeometry = await LASParser.parseChunk(copcData, options);

assert.strictEqual(bufferGeometry.attributes.position.count, header.pointCount);
assert.strictEqual(bufferGeometry.attributes.intensity.count, header.pointCount);
assert.strictEqual(bufferGeometry.attributes.classification.count, header.pointCount);
assert.strictEqual(bufferGeometry.attributes.color.count, header.pointCount);
await LASParser.terminate();
});
});
Expand Down
1 change: 0 additions & 1 deletion test/unit/potree.js
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,6 @@ describe('Potree', function () {
// Configure Point Cloud layer
potreeLayer = new PotreeLayer('lion_takanawa', {
source,
onPointsCreated: () => {},
crs: viewer.referenceCrs,
});

Expand Down
3 changes: 1 addition & 2 deletions test/unit/potree2.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@ describe('Potree2', function () {
url: 'https://raw.githubusercontent.com/iTowns/iTowns2-sample-data/master/pointclouds/potree2.0/lion',
networkOptions: process.env.HTTPS_PROXY ? { agent: new HttpsProxyAgent(process.env.HTTPS_PROXY) } : {},
}),
onPointsCreated: () => {},
crs: viewer.referenceCrs,
});

Expand Down Expand Up @@ -63,7 +62,7 @@ describe('Potree2', function () {
assert.equal(potreeLayer.group.children.length, 1);
done();
}).catch(done);
});
}).timeout(5000);

it('postUpdate potree2 layer', function () {
potreeLayer.postUpdate(context, potreeLayer);
Expand Down
Loading