From a1fd2ef17f2864e177441411edd8db7feb00d507 Mon Sep 17 00:00:00 2001 From: ErixenCruz Date: Tue, 23 Jun 2020 11:30:00 -0400 Subject: [PATCH 1/5] Ellipsoid zero check and surface area. --- Source/Core/Ellipsoid.js | 120 ++++++++++++++++++++++++++++++++++++ Specs/Core/EllipsoidSpec.js | 87 ++++++++++++++++++++++++++ 2 files changed, 207 insertions(+) diff --git a/Source/Core/Ellipsoid.js b/Source/Core/Ellipsoid.js index d78c468c9a9b..143a84b0f0bd 100644 --- a/Source/Core/Ellipsoid.js +++ b/Source/Core/Ellipsoid.js @@ -362,6 +362,11 @@ Ellipsoid.prototype.geodeticSurfaceNormalCartographic = function ( * @returns {Cartesian3} The modified result parameter or a new Cartesian3 instance if none was provided. */ Ellipsoid.prototype.geodeticSurfaceNormal = function (cartesian, result) { + if ( + Cartesian3.equalsEpsilon(cartesian, Cartesian3.ZERO, CesiumMath.EPSILON14) + ) { + return undefined; + } if (!defined(result)) { result = new Cartesian3(); } @@ -688,4 +693,119 @@ Ellipsoid.prototype.getSurfaceNormalIntersectionWithZAxis = function ( return result; }; + +var abscissas = [ + 0.14887433898163, + 0.43339539412925, + 0.67940956829902, + 0.86506336668898, + 0.97390652851717, + 0.0, +]; +var weights = [ + 0.29552422471475, + 0.26926671930999, + 0.21908636251598, + 0.14945134915058, + 0.066671344308684, + 0.0, +]; + +/** + * Compute the 10th order Gauss-Legendre Quadrature of the given definite integral. + * + * @param {Number} a The lower bound for the integration. + * @param {Number} b The upper bound for the integration. + * @param {Ellipsoid~RealValuedScalarFunction} func The function to integrate. + * @returns {Number} The value of the integral of the given function over the given domain. + * + * @private + */ +function gaussLegendreQuadrature(a, b, func) { + //>>includeStart('debug', pragmas.debug); + Check.typeOf.number("a", a); + Check.typeOf.number("b", b); + Check.typeOf.func("func", func); + //>>includeEnd('debug'); + + // The range is half of the normal range since the five weights add to one (ten weights add to two). + // The values of the abscissas are multiplied by two to account for this. + var xMean = 0.5 * (b + a); + var xRange = 0.5 * (b - a); + + var sum = 0.0; + for (var i = 0; i < 5; i++) { + var dx = xRange * abscissas[i]; + sum += weights[i] * (func(xMean + dx) + func(xMean - dx)); + } + + // Scale the sum to the range of x. + sum *= xRange; + return sum; +} + +/** + * A real valued scalar function. + * @callback Ellipsoid~RealValuedScalarFunction + * + * @param {Number} x The value used to evaluate the function. + * @returns {Number} The value of the function at x. + * + * @private + */ + +/** + * Computes an approximation of the surface area of a given section of the surface of an ellipsoid using + * Gauss-Legendre 10th order quadrature. + * + * @param {Number} minLongitude The west most longitude which bounds the region. + * @param {Number} minLatitude The south most latitude which bounds the region. + * @param {Number} maxLongitude The east most longitude which bounds the region. + * @param {Number} maxLatitude The north most latitude which bounds the region. + * @returns {Number} The approximate area of the section on the surface of this ellipsoid. + */ +Ellipsoid.prototype.surfaceArea = function ( + minLongitude, + minLatitude, + maxLongitude, + maxLatitude +) { + //>>includeStart('debug', pragmas.debug); + Check.typeOf.number("minLongitude", minLongitude); + Check.typeOf.number("minLatitude", minLatitude); + Check.typeOf.number("maxLongitude", maxLongitude); + Check.typeOf.number("maxLatitude", maxLatitude); + //>>includeEnd('debug'); + + while (maxLongitude < minLongitude) { + maxLongitude += CesiumMath.TWO_PI; + } + + var radiiSquared = this._radiiSquared; + var a2 = radiiSquared.x; + var b2 = radiiSquared.y; + var c2 = radiiSquared.z; + var a2b2 = a2 * b2; + return gaussLegendreQuadrature(minLatitude, maxLatitude, function (lat) { + // phi represents the angle measured from the north pole + // sin(phi) = sin(pi / 2 - lat) = cos(lat), cos(phi) is similar + var sinPhi = Math.cos(lat); + var cosPhi = Math.sin(lat); + return ( + Math.cos(lat) * + gaussLegendreQuadrature(minLongitude, maxLongitude, function (lon) { + var cosTheta = Math.cos(lon); + var sinTheta = Math.sin(lon); + return Math.sqrt( + a2b2 * cosPhi * cosPhi + + c2 * + (b2 * cosTheta * cosTheta + a2 * sinTheta * sinTheta) * + sinPhi * + sinPhi + ); + }) + ); + }); +}; + export default Ellipsoid; diff --git a/Specs/Core/EllipsoidSpec.js b/Specs/Core/EllipsoidSpec.js index 657ba272c4d1..d453e5d005f5 100644 --- a/Specs/Core/EllipsoidSpec.js +++ b/Specs/Core/EllipsoidSpec.js @@ -137,6 +137,12 @@ describe("Core/Ellipsoid", function () { ); }); + it("geodeticSurfaceNormal returns undefined when given the origin", function () { + var ellipsoid = Ellipsoid.WGS84; + var returnedResult = ellipsoid.geodeticSurfaceNormal(Cartesian3.ZERO); + expect(returnedResult).toBeUndefined(); + }); + it("geodeticSurfaceNormal works with a result parameter", function () { var ellipsoid = Ellipsoid.WGS84; var result = new Cartesian3(); @@ -708,6 +714,87 @@ describe("Core/Ellipsoid", function () { expect(ellipsoid._squaredXOverSquaredZ).toEqual(squaredXOverSquaredZ); }); + it("surfaceArea throws without minLongitude", function () { + expect(function () { + return Ellipsoid.WGS84.surfaceArea( + undefined, + -CesiumMath.PI_OVER_TWO, + CesiumMath.PI, + CesiumMath.PI_OVER_TWO + ); + }).toThrowDeveloperError(); + }); + + it("surfaceArea throws without minLatitude", function () { + expect(function () { + return Ellipsoid.WGS84.surfaceArea( + -CesiumMath.PI, + undefined, + CesiumMath.PI, + CesiumMath.PI_OVER_TWO + ); + }).toThrowDeveloperError(); + }); + + it("surfaceArea throws without maxLongitude", function () { + expect(function () { + return Ellipsoid.WGS84.surfaceArea( + -CesiumMath.PI, + -CesiumMath.PI_OVER_TWO, + undefined, + CesiumMath.PI_OVER_TWO + ); + }).toThrowDeveloperError(); + }); + + it("surfaceArea throws without maxLatitude", function () { + expect(function () { + return Ellipsoid.WGS84.surfaceArea( + -CesiumMath.PI, + -CesiumMath.PI_OVER_TWO, + CesiumMath.PI, + undefined + ); + }).toThrowDeveloperError(); + }); + + it("computes surfaceArea", function () { + // area of an oblate spheroid + var ellipsoid = new Ellipsoid(4, 4, 3); + var a2 = ellipsoid.radiiSquared.x; + var c2 = ellipsoid.radiiSquared.z; + var e = Math.sqrt(1.0 - c2 / a2); + var area = + CesiumMath.TWO_PI * a2 + + CesiumMath.PI * (c2 / e) * Math.log((1.0 + e) / (1.0 - e)); + expect( + ellipsoid.surfaceArea( + -CesiumMath.PI, + -CesiumMath.PI_OVER_TWO, + CesiumMath.PI, + CesiumMath.PI_OVER_TWO + ) + ).toEqualEpsilon(area, CesiumMath.EPSILON3); + + // area of a prolate spheroid + ellipsoid = new Ellipsoid(3, 3, 4); + a2 = ellipsoid.radiiSquared.x; + c2 = ellipsoid.radiiSquared.z; + e = Math.sqrt(1.0 - a2 / c2); + var a = ellipsoid.radii.x; + var c = ellipsoid.radii.z; + area = + CesiumMath.TWO_PI * a2 + CesiumMath.TWO_PI * ((a * c) / e) * Math.asin(e); + expect( + ellipsoid.surfaceArea( + -CesiumMath.PI, + -CesiumMath.PI_OVER_TWO, + CesiumMath.PI, + CesiumMath.PI_OVER_TWO + ) + ).toEqualEpsilon(area, CesiumMath.EPSILON3); + }); + createPackableSpecs(Ellipsoid, Ellipsoid.WGS84, [ Ellipsoid.WGS84.radii.x, Ellipsoid.WGS84.radii.y, From 7c7a1d16b2d9ea6b5ada8695ec9797f0f9e3f56d Mon Sep 17 00:00:00 2001 From: ErixenCruz Date: Tue, 23 Jun 2020 11:48:27 -0400 Subject: [PATCH 2/5] Change Ellipsoid.surfaceArea to take rectangle. --- CHANGES.md | 2 ++ Source/Core/Ellipsoid.js | 21 +++++-------- Specs/Core/EllipsoidSpec.js | 63 +++++++++---------------------------- 3 files changed, 24 insertions(+), 62 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 098711f0a489..73a7f0d207bf 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -8,10 +8,12 @@ ##### Additions :tada: +- Added `Ellipsoid.surfaceArea` for computing the surface area under given rectangle. - Added support for PolylineVolume in CZML [#8841](https://github.com/CesiumGS/cesium/pull/8841) ##### Fixes :wrench: +- Fixed `Ellipsoid.geodeticSurfaceNormal` dividing by zero when given origin as input. - Fixed error with `WallGeoemtry` when there were adjacent positions with very close values [#8952](https://github.com/CesiumGS/cesium/pull/8952) - Fixed artifact for skinned model when log depth is enabled. [#6447](https://github.com/CesiumGS/cesium/issues/6447) - Fixed a bug where certain rhumb arc polylines would lead to a crash. [#8787](https://github.com/CesiumGS/cesium/pull/8787) diff --git a/Source/Core/Ellipsoid.js b/Source/Core/Ellipsoid.js index 143a84b0f0bd..0527f23bf417 100644 --- a/Source/Core/Ellipsoid.js +++ b/Source/Core/Ellipsoid.js @@ -758,24 +758,17 @@ function gaussLegendreQuadrature(a, b, func) { * Computes an approximation of the surface area of a given section of the surface of an ellipsoid using * Gauss-Legendre 10th order quadrature. * - * @param {Number} minLongitude The west most longitude which bounds the region. - * @param {Number} minLatitude The south most latitude which bounds the region. - * @param {Number} maxLongitude The east most longitude which bounds the region. - * @param {Number} maxLatitude The north most latitude which bounds the region. + * @param {Rectangle} rectangle The rectangle to find the surface area of. * @returns {Number} The approximate area of the section on the surface of this ellipsoid. */ -Ellipsoid.prototype.surfaceArea = function ( - minLongitude, - minLatitude, - maxLongitude, - maxLatitude -) { +Ellipsoid.prototype.surfaceArea = function (rectangle) { //>>includeStart('debug', pragmas.debug); - Check.typeOf.number("minLongitude", minLongitude); - Check.typeOf.number("minLatitude", minLatitude); - Check.typeOf.number("maxLongitude", maxLongitude); - Check.typeOf.number("maxLatitude", maxLatitude); + Check.typeOf.object("rectangle", rectangle); //>>includeEnd('debug'); + var minLongitude = rectangle.west; + var maxLongitude = rectangle.east; + var minLatitude = rectangle.south; + var maxLatitude = rectangle.north; while (maxLongitude < minLongitude) { maxLongitude += CesiumMath.TWO_PI; diff --git a/Specs/Core/EllipsoidSpec.js b/Specs/Core/EllipsoidSpec.js index d453e5d005f5..1e7fea4107b3 100644 --- a/Specs/Core/EllipsoidSpec.js +++ b/Specs/Core/EllipsoidSpec.js @@ -3,6 +3,7 @@ import { Cartographic } from "../../Source/Cesium.js"; import { Ellipsoid } from "../../Source/Cesium.js"; import { Math as CesiumMath } from "../../Source/Cesium.js"; import createPackableSpecs from "../createPackableSpecs.js"; +import { Rectangle } from "../../Source/Cesium.js"; describe("Core/Ellipsoid", function () { var radii = new Cartesian3(1.0, 2.0, 3.0); @@ -714,47 +715,9 @@ describe("Core/Ellipsoid", function () { expect(ellipsoid._squaredXOverSquaredZ).toEqual(squaredXOverSquaredZ); }); - it("surfaceArea throws without minLongitude", function () { + it("surfaceArea throws without rectangle", function () { expect(function () { - return Ellipsoid.WGS84.surfaceArea( - undefined, - -CesiumMath.PI_OVER_TWO, - CesiumMath.PI, - CesiumMath.PI_OVER_TWO - ); - }).toThrowDeveloperError(); - }); - - it("surfaceArea throws without minLatitude", function () { - expect(function () { - return Ellipsoid.WGS84.surfaceArea( - -CesiumMath.PI, - undefined, - CesiumMath.PI, - CesiumMath.PI_OVER_TWO - ); - }).toThrowDeveloperError(); - }); - - it("surfaceArea throws without maxLongitude", function () { - expect(function () { - return Ellipsoid.WGS84.surfaceArea( - -CesiumMath.PI, - -CesiumMath.PI_OVER_TWO, - undefined, - CesiumMath.PI_OVER_TWO - ); - }).toThrowDeveloperError(); - }); - - it("surfaceArea throws without maxLatitude", function () { - expect(function () { - return Ellipsoid.WGS84.surfaceArea( - -CesiumMath.PI, - -CesiumMath.PI_OVER_TWO, - CesiumMath.PI, - undefined - ); + return Ellipsoid.WGS84.surfaceArea(undefined); }).toThrowDeveloperError(); }); @@ -769,10 +732,12 @@ describe("Core/Ellipsoid", function () { CesiumMath.PI * (c2 / e) * Math.log((1.0 + e) / (1.0 - e)); expect( ellipsoid.surfaceArea( - -CesiumMath.PI, - -CesiumMath.PI_OVER_TWO, - CesiumMath.PI, - CesiumMath.PI_OVER_TWO + new Rectangle( + -CesiumMath.PI, + -CesiumMath.PI_OVER_TWO, + CesiumMath.PI, + CesiumMath.PI_OVER_TWO + ) ) ).toEqualEpsilon(area, CesiumMath.EPSILON3); @@ -787,10 +752,12 @@ describe("Core/Ellipsoid", function () { CesiumMath.TWO_PI * a2 + CesiumMath.TWO_PI * ((a * c) / e) * Math.asin(e); expect( ellipsoid.surfaceArea( - -CesiumMath.PI, - -CesiumMath.PI_OVER_TWO, - CesiumMath.PI, - CesiumMath.PI_OVER_TWO + new Rectangle( + -CesiumMath.PI, + -CesiumMath.PI_OVER_TWO, + CesiumMath.PI, + CesiumMath.PI_OVER_TWO + ) ) ).toEqualEpsilon(area, CesiumMath.EPSILON3); }); From 8dfcf3a1767e6013e7596c3f9239bc3068f72a1e Mon Sep 17 00:00:00 2001 From: ErixenCruz Date: Wed, 24 Jun 2020 09:22:32 -0400 Subject: [PATCH 3/5] Better wording for documentation and move import statement up a few lines. --- Source/Core/Ellipsoid.js | 6 +++--- Specs/Core/EllipsoidSpec.js | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Source/Core/Ellipsoid.js b/Source/Core/Ellipsoid.js index 0527f23bf417..2b565cbef675 100644 --- a/Source/Core/Ellipsoid.js +++ b/Source/Core/Ellipsoid.js @@ -755,11 +755,11 @@ function gaussLegendreQuadrature(a, b, func) { */ /** - * Computes an approximation of the surface area of a given section of the surface of an ellipsoid using + * Computes an approximation of the surface area of a rectangle on the surface of an ellipsoid using * Gauss-Legendre 10th order quadrature. * - * @param {Rectangle} rectangle The rectangle to find the surface area of. - * @returns {Number} The approximate area of the section on the surface of this ellipsoid. + * @param {Rectangle} rectangle The rectangle used for computing the surface area. + * @returns {Number} The approximate area of the rectangle on the surface of this ellipsoid. */ Ellipsoid.prototype.surfaceArea = function (rectangle) { //>>includeStart('debug', pragmas.debug); diff --git a/Specs/Core/EllipsoidSpec.js b/Specs/Core/EllipsoidSpec.js index 1e7fea4107b3..61401ab63c9d 100644 --- a/Specs/Core/EllipsoidSpec.js +++ b/Specs/Core/EllipsoidSpec.js @@ -2,8 +2,8 @@ import { Cartesian3 } from "../../Source/Cesium.js"; import { Cartographic } from "../../Source/Cesium.js"; import { Ellipsoid } from "../../Source/Cesium.js"; import { Math as CesiumMath } from "../../Source/Cesium.js"; -import createPackableSpecs from "../createPackableSpecs.js"; import { Rectangle } from "../../Source/Cesium.js"; +import createPackableSpecs from "../createPackableSpecs.js"; describe("Core/Ellipsoid", function () { var radii = new Cartesian3(1.0, 2.0, 3.0); From 5255bb6158a1773fa8ada389b90fe941ee474ea7 Mon Sep 17 00:00:00 2001 From: ErixenCruz Date: Wed, 24 Jun 2020 09:36:54 -0400 Subject: [PATCH 4/5] Better wording in CHANGES.md --- CHANGES.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 73a7f0d207bf..7d8311deb6c0 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -8,12 +8,12 @@ ##### Additions :tada: -- Added `Ellipsoid.surfaceArea` for computing the surface area under given rectangle. +- Added `Ellipsoid.surfaceArea` for computing the approximate surface area of a rectangle on the surface of an ellipsoid. - Added support for PolylineVolume in CZML [#8841](https://github.com/CesiumGS/cesium/pull/8841) ##### Fixes :wrench: -- Fixed `Ellipsoid.geodeticSurfaceNormal` dividing by zero when given origin as input. +- Fixed a divide-by-zero bug in `Ellipsoid.geodeticSurfaceNormal` when given the origin as input. `undefined` is returned instead. [#8986](https://github.com/CesiumGS/cesium/pull/8986) - Fixed error with `WallGeoemtry` when there were adjacent positions with very close values [#8952](https://github.com/CesiumGS/cesium/pull/8952) - Fixed artifact for skinned model when log depth is enabled. [#6447](https://github.com/CesiumGS/cesium/issues/6447) - Fixed a bug where certain rhumb arc polylines would lead to a crash. [#8787](https://github.com/CesiumGS/cesium/pull/8787) From 7ba009b67c8477b12d37731fe37ac407782de76b Mon Sep 17 00:00:00 2001 From: Sean Lilley Date: Wed, 24 Jun 2020 16:42:09 -0400 Subject: [PATCH 5/5] Update CHANGES.md [skip ci] --- CHANGES.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 7d8311deb6c0..ac746ed05a92 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -8,8 +8,8 @@ ##### Additions :tada: -- Added `Ellipsoid.surfaceArea` for computing the approximate surface area of a rectangle on the surface of an ellipsoid. -- Added support for PolylineVolume in CZML [#8841](https://github.com/CesiumGS/cesium/pull/8841) +- Added `Ellipsoid.surfaceArea` for computing the approximate surface area of a rectangle on the surface of an ellipsoid. [#8986](https://github.com/CesiumGS/cesium/pull/8986) +- Added support for PolylineVolume in CZML. [#8841](https://github.com/CesiumGS/cesium/pull/8841) ##### Fixes :wrench: