From ed583d97d93a405b787fc612d0cb26b41dd328d7 Mon Sep 17 00:00:00 2001 From: gchoqueux Date: Thu, 18 Nov 2021 10:22:37 +0100 Subject: [PATCH] feature(Extent): add methods to calculate extent dimensions. * planarDimensions * geodesicDimensions * earthEuclideanDimensions --- src/Core/Geographic/Extent.js | 69 ++++++++++++++++++++++++++++++++--- test/unit/extent.js | 37 +++++++++++++++++-- 2 files changed, 97 insertions(+), 9 deletions(-) diff --git a/src/Core/Geographic/Extent.js b/src/Core/Geographic/Extent.js index 553fd9087c..d424e99b60 100644 --- a/src/Core/Geographic/Extent.js +++ b/src/Core/Geographic/Extent.js @@ -16,7 +16,9 @@ const dimensionTile = new THREE.Vector2(); const defaultScheme = new THREE.Vector2(2, 2); const r = { row: 0, col: 0, invDiff: 0 }; -const coord = new Coordinates('EPSG:4326', 0, 0, 0); +const cNorthWest = new Coordinates('EPSG:4326', 0, 0, 0); +const cSouthWest = new Coordinates('EPSG:4326', 0, 0, 0); +const cNorthEast = new Coordinates('EPSG:4326', 0, 0, 0); const southWest = new THREE.Vector3(); const northEast = new THREE.Vector3(); @@ -256,11 +258,67 @@ class Extent { * @return {THREE.Vector2} */ dimensions(target = new THREE.Vector2()) { + console.warn('Extent.dimensions is deprecated, use planarDimensions, geodesicDimensions or geodesicChordDimensions'); target.x = Math.abs(this.east - this.west); target.y = Math.abs(this.north - this.south); return target; } + /** + * Planar dimensions are two planar distances west/east and south/north. + * Planar distance straight-line Euclidean distance calculated in a 2D Cartesian coordinate system. + * + * @param {THREE.Vector2} [target=new THREE.Vector2()] The target + * @return {THREE.Vector2} Planar dimensions + */ + planarDimensions(target = new THREE.Vector2()) { + // Calculte the dimensions for x and y + return target.set(Math.abs(this.east - this.west), Math.abs(this.north - this.south)); + } + + /** + * Geodesic dimensions are two planar distances west/east and south/north. + * Geodesic distance is calculated in an ellispoid space as the distance + * across the curved surface of the world. + * + * @param {THREE.Vector2} [target=new THREE.Vector2()] The target + * @return {THREE.Vector2} Planar dimensions + */ + geodesicDimensions(target = new THREE.Vector2()) { + // set 3 corners extent + cNorthWest.crs = this.crs; + cSouthWest.crs = this.crs; + cNorthEast.crs = this.crs; + + cNorthWest.setFromValues(this.west, this.north, 0); + cSouthWest.setFromValues(this.west, this.south, 0); + cNorthEast.setFromValues(this.east, this.north, 0); + + // calcul geodesic distance northWest/northEast and northWest/southWest + return target.set(cNorthWest.geodesicDistanceTo(cNorthEast), cNorthWest.geodesicDistanceTo(cSouthWest)); + } + + /** + * Earth euclidean dimensions are two earth euclidean distances between west/east and south/north. + * Earth euclidean distance chord is calculated in a ellispoid space. + * + * @param {THREE.Vector2} [target=new THREE.Vector2()] The target + * @return {THREE.Vector2} Earth euclidean dimensions + */ + earthEuclideanDimensions(target = new THREE.Vector2()) { + // set 3 corners extent + cNorthWest.crs = this.crs; + cSouthWest.crs = this.crs; + cNorthEast.crs = this.crs; + + cNorthWest.setFromValues(this.west, this.north, 0); + cSouthWest.setFromValues(this.west, this.south, 0); + cNorthEast.setFromValues(this.east, this.north, 0); + + // calcul chord distance northWest/northEast and northWest/southWest + return target.set(cNorthWest.earthEuclideanDistanceTo(cNorthEast), cNorthWest.earthEuclideanDistanceTo(cSouthWest)); + } + /** * Return true if `coord` is inside the bounding box. * @@ -546,10 +604,11 @@ class Extent { // if geocentric reproject box on 'EPSG:4326' crs = 'EPSG:4326'; box = _box.copy(box); - coord.crs = crs; - coord.setFromVector3(box.min).as(crs, coord).toVector3(box.min); - coord.crs = crs; - coord.setFromVector3(box.max).as(crs, coord).toVector3(box.max); + + cSouthWest.crs = crs; + cSouthWest.setFromVector3(box.min).as(crs, cSouthWest).toVector3(box.min); + cNorthEast.crs = crs; + cNorthEast.setFromVector3(box.max).as(crs, cNorthEast).toVector3(box.max); } return new Extent(crs, { diff --git a/test/unit/extent.js b/test/unit/extent.js index 10bde54a48..2468cc032f 100644 --- a/test/unit/extent.js +++ b/test/unit/extent.js @@ -1,8 +1,11 @@ import assert from 'assert'; -import { Box3, Vector3, Matrix4, Quaternion } from 'three'; +import { Box3, Vector3, Vector2, Matrix4, Quaternion } from 'three'; import Coordinates from 'Core/Geographic/Coordinates'; import Extent from 'Core/Geographic/Extent'; import CRS from 'Core/Geographic/Crs'; +import proj4 from 'proj4'; + +proj4.defs('EPSG:2154', '+proj=lcc +lat_1=49 +lat_2=44 +lat_0=46.5 +lon_0=3 +x_0=700000 +y_0=6600000 +ellps=GRS80 +towgs84=0,0,0,0,0,0,0 +units=m +no_defs'); describe('Extent', function () { const minX = 0; @@ -95,9 +98,9 @@ describe('Extent', function () { assert.strictEqual(subdivided[3].north, 0); }); - it('should return the correct dimension of the extent', function () { + it('should return the correct planar dimensions', function () { const extent = new Extent('EPSG:4326', -15, 10, -10, 10); - const dimensions = extent.dimensions(); + const dimensions = extent.planarDimensions(); // Width assert.equal(dimensions.x, 25); @@ -105,6 +108,32 @@ describe('Extent', function () { assert.equal(dimensions.y, 20); }); + it('should return the same planar dimensions with deprecated dimensions method', function () { + const extent = new Extent('EPSG:4326', -15, 10, -10, 10); + const dimensions = extent.planarDimensions(); + const dimensions_2 = extent.dimensions(); + assert.equal(dimensions.x, dimensions_2.x); + assert.equal(dimensions.y, dimensions_2.y); + }); + + it('should return the correct earth euclidean dimensions', function () { + const extent = new Extent('EPSG:4326', 3, 3.01, 46, 46.01); + const dimensions = new Vector2(); + + extent.earthEuclideanDimensions(dimensions); + assert.equal(dimensions.x, 774.4934293643765); + assert.equal(dimensions.y, 1111.5141604285038); + }); + + it('should return the correct geodesic dimensions', function () { + const extent = new Extent('EPSG:4326', 3, 3.01, 46, 46.01); + const dimensions = new Vector2(); + + extent.geodesicDimensions(dimensions); + assert.equal(dimensions.x, 773.2375602074535); + assert.equal(dimensions.y, 1113.3197697640906); + }); + it('should clone extent like expected', function () { const withValues = new Extent('EPSG:4326', [minX, maxX, minY, maxY]); const clonedExtent = withValues.clone(); @@ -168,7 +197,7 @@ describe('Extent', function () { }); it('should return dimensions of extent expected', function () { const withValues = new Extent('EPSG:4326', [minX, maxX, minY, maxY]); - const dimensions = withValues.dimensions(); + const dimensions = withValues.planarDimensions(); assert.equal(10, dimensions.x); assert.equal(4, dimensions.y); });