From efe2a137d2194c2f474d3b0d5aeff988e76c6174 Mon Sep 17 00:00:00 2001 From: Shehzan Mohammed Date: Mon, 14 Jan 2019 14:41:49 -0500 Subject: [PATCH 01/11] Add EllipsoidRhumb class similar to EllipsoidGeodesic --- Source/Core/EllipsoidRhumb.js | 431 +++++++++++++++++++++++ Specs/Core/EllipsoidRhumbSpec.js | 581 +++++++++++++++++++++++++++++++ 2 files changed, 1012 insertions(+) create mode 100644 Source/Core/EllipsoidRhumb.js create mode 100644 Specs/Core/EllipsoidRhumbSpec.js diff --git a/Source/Core/EllipsoidRhumb.js b/Source/Core/EllipsoidRhumb.js new file mode 100644 index 000000000000..c95da8267758 --- /dev/null +++ b/Source/Core/EllipsoidRhumb.js @@ -0,0 +1,431 @@ +define([ + './Cartesian3', + './Cartographic', + './Check', + './defaultValue', + './defined', + './defineProperties', + './Ellipsoid', + './Math' + ], function( + Cartesian3, + Cartographic, + Check, + defaultValue, + defined, + defineProperties, + Ellipsoid, + CesiumMath) { + 'use strict'; + + function calculateM(ellipticity, major, latitude) { + if (ellipticity === 0.0) { // sphere + return major * latitude; + } + + var e2 = ellipticity * ellipticity; + var e4 = e2 * e2; + var e6 = e4 * e2; + var e8 = e6 * e2; + var e10 = e8 * e2; + var e12 = e10 * e2; + var phi = latitude; + var sin2Phi = Math.sin(2 * phi); + var sin4Phi = Math.sin(4 * phi); + var sin6Phi = Math.sin(6 * phi); + var sin8Phi = Math.sin(8 * phi); + var sin10Phi = Math.sin(10 * phi); + var sin12Phi = Math.sin(12 * phi); + + return major * ((1 - e2 / 4 - 3 * e4 / 64 - 5 * e6 / 256 - 175 * e8 / 16384 - 441 * e10 / 65536 - 4851 * e12 / 1048576) * phi + - (3 * e2 / 8 + 3 * e4 / 32 + 45 * e6 / 1024 + 105 * e8 / 4096 + 2205 * e10 / 131072 + 6237 * e12 / 524288) * sin2Phi + + (15 * e4 / 256 + 45 * e6 / 1024 + 525 * e8 / 16384 + 1575 * e10 / 65536 + 155925 * e12 / 8388608) * sin4Phi + - (35 * e6 / 3072 + 175 * e8 / 12288 + 3675 * e10 / 262144 + 13475 * e12 / 1048576) * sin6Phi + + (315 * e8 / 131072 + 2205 * e10 / 524288 + 43659 * e12 / 8388608) * sin8Phi + - (693 * e10 / 1310720 + 6237 * e12 / 5242880) * sin10Phi + + 1001 * e12 / 8388608 * sin12Phi); + } + + function calculateInverseM(M, ellipticity, major) { + var d = M / major; + + if (ellipticity === 0.0) { // sphere + return d; + } + + var d2 = d * d; + var d3 = d2 * d; + var d4 = d3 * d; + var e = ellipticity; + var e2 = e * e; + var e4 = e2 * e2; + var e6 = e4 * e2; + var e8 = e6 * e2; + var e10 = e8 * e2; + var e12 = e10 * e2; + var sin2D = Math.sin(2 * d); + var cos2D = Math.cos(2 * d); + var sin4D = Math.sin(4 * d); + var cos4D = Math.cos(4 * d); + var sin6D = Math.sin(6 * d); + var cos6D = Math.cos(6 * d); + var sin8D = Math.sin(8 * d); + var cos8D = Math.cos(8 * d); + var sin10D = Math.sin(10 * d); + var cos10D = Math.cos(10 * d); + var sin12D = Math.sin(12 * d); + + return d + d * e2 / 4 + 7 * d * e4 / 64 + 15 * d * e6 / 256 + 579 * d * e8 / 16384 + 1515 * d * e10 / 65536 + 16837 * d * e12 / 1048576 + + (3 * d * e4 / 16 + 45 * d * e6 / 256 - d * (32 * d2 - 561) * e8 / 4096 - d * (232 * d2 - 1677) * e10 / 16384 + d * (399985 - 90560 * d2 + 512 * d4) * e12 / 5242880) * cos2D + + (21 * d * e6 / 256 + 483 * d * e8 / 4096 - d * (224 * d2 - 1969) * e10 / 16384 - d * (33152 * d2 - 112599) * e12 / 1048576) * cos4D + + (151 * d * e8 / 4096 + 4681 * d * e10 / 65536 + 1479 * d * e12 / 16384 - 453 * d3 * e12 / 32768) * cos6D + + (1097 * d * e10 / 65536 + 42783 * d * e12 / 1048576) * cos8D + + 8011 * d * e12 / 1048576 * cos10D + + (3 * e2 / 8 + 3 * e4 / 16 + 213 * e6 / 2048 - 3 * d2 * e6 / 64 + 255 * e8 / 4096 - 33 * d2 * e8 / 512 + 20861 * e10 / 524288 - 33 * d2 * e10 / 512 + d4 * e10 / 1024 + 28273 * e12 / 1048576 - 471 * d2 * e12 / 8192 + 9 * d4 * e12 / 4096) * sin2D + + (21 * e4 / 256 + 21 * e6 / 256 + 533 * e8 / 8192 - 21 * d2 * e8 / 512 + 197 * e10 / 4096 - 315 * d2 * e10 / 4096 + 584039 * e12 / 16777216 - 12517 * d2 * e12 / 131072 + 7 * d4 * e12 / 2048) * sin4D + + (151 * e6 / 6144 + 151 * e8 / 4096 + 5019 * e10 / 131072 - 453 * d2 * e10 / 16384 + 26965 * e12 / 786432 - 8607 * d2 * e12 / 131072) * sin6D + + (1097 * e8 / 131072 + 1097 * e10 / 65536 + 225797 * e12 / 10485760 - 1097 * d2 * e12 / 65536) * sin8D + + (8011 * e10 / 2621440 + 8011 * e12 / 1048576) * sin10D + + 293393 * e12 / 251658240 * sin12D; + } + + function calculateSigma(ellipticity, latitude) { + if (ellipticity === 0.0) { // sphere + return Math.log(Math.tan(0.5 * (CesiumMath.PI_OVER_TWO + latitude))); + } + + var eSinL = ellipticity * Math.sin(latitude); + return Math.log(Math.tan(0.5 * (CesiumMath.PI_OVER_TWO + latitude))) - (ellipticity / 2.0 * Math.log((1 + eSinL) / (1 - eSinL))); + } + + function calculateHeading(ellipsoidRhumb, major, minor, firstLongitude, firstLatitude, secondLongitude, secondLatitude) { + var sigma1 = calculateSigma(ellipsoidRhumb._ellipticity, firstLatitude); + var sigma2 = calculateSigma(ellipsoidRhumb._ellipticity, secondLatitude); + return Math.atan2(CesiumMath.negativePiToPi(secondLongitude - firstLongitude), sigma2 - sigma1); + } + + function calculateArcLength(ellipsoidRhumb, major, minor, firstLongitude, firstLatitude, secondLongitude, secondLatitude) { + //>>includeStart('debug', pragmas.debug); + Check.defined('heading', ellipsoidRhumb._heading); + //>>includeEnd('debug'); + + var heading = ellipsoidRhumb._heading; + var deltaLongitude = secondLongitude - firstLongitude; + + var distance = 0.0; + + //Check to see if the rhumb line has constant latitude + //This equation will diverge if heading gets close to 90 degrees + if (CesiumMath.equalsEpsilon(Math.abs(heading), CesiumMath.PI_OVER_TWO, CesiumMath.EPSILON8)) { //If heading is close to 90 degrees + if (major === minor) { + distance = major * Math.cos(firstLatitude) * CesiumMath.negativePiToPi(deltaLongitude); + } else { + var sinPhi = Math.sin(firstLatitude); + distance = major * Math.cos(firstLatitude) * CesiumMath.negativePiToPi(deltaLongitude) / Math.sqrt(1 - ellipsoidRhumb._ellipticitySquared * sinPhi * sinPhi); + } + } else { + var M1 = calculateM(ellipsoidRhumb._ellipticity, major, firstLatitude); + var M2 = calculateM(ellipsoidRhumb._ellipticity, major, secondLatitude); + + distance = (M2 - M1) / Math.cos(heading); + } + return Math.abs(distance); + } + + var scratchCart1 = new Cartesian3(); + var scratchCart2 = new Cartesian3(); + + function initiailize(ellipsoidRhumb, start, end, ellipsoid) { + var major = ellipsoid.maximumRadius; + var minor = ellipsoid.minimumRadius; + var majorSquared = major * major; + var minorSquared = minor * minor; + ellipsoidRhumb._ellipticitySquared = (majorSquared - minorSquared) / majorSquared; + ellipsoidRhumb._ellipticity = Math.sqrt(ellipsoidRhumb._ellipticitySquared); + + if (defined(start)) { + ellipsoidRhumb._start = Cartographic.clone(start, ellipsoidRhumb._start); + ellipsoidRhumb._start.height = 0; + } + if (defined(end)) { + ellipsoidRhumb._end = Cartographic.clone(end, ellipsoidRhumb._end); + ellipsoidRhumb._end.height = 0; + } + } + + function computeProperties(ellipsoidRhumb, start, end, ellipsoid) { + var firstCartesian = Cartesian3.normalize(ellipsoid.cartographicToCartesian(start, scratchCart2), scratchCart1); + var lastCartesian = Cartesian3.normalize(ellipsoid.cartographicToCartesian(end, scratchCart2), scratchCart2); + + //>>includeStart('debug', pragmas.debug); + Check.typeOf.number.greaterThanOrEquals('value', Math.abs(Math.abs(Cartesian3.angleBetween(firstCartesian, lastCartesian)) - Math.PI), 0.0125); + //>>includeEnd('debug'); + + initiailize(ellipsoidRhumb, start, end, ellipsoid); + + ellipsoidRhumb._heading = calculateHeading(ellipsoidRhumb, ellipsoid.maximumRadius, ellipsoid.minimumRadius, + start.longitude, start.latitude, end.longitude, end.latitude); + ellipsoidRhumb._distance = calculateArcLength(ellipsoidRhumb, ellipsoid.maximumRadius, ellipsoid.minimumRadius, + start.longitude, start.latitude, end.longitude, end.latitude); + } + + /** + * Initializes a rhumb line on the ellipsoid connecting the two provided planetodetic points. + * + * @alias EllipsoidRhumb + * @constructor + * + * @param {Cartographic} [start] The initial planetodetic point on the path. + * @param {Cartographic} [end] The final planetodetic point on the path. + * @param {Ellipsoid} [ellipsoid=Ellipsoid.WGS84] The ellipsoid on which the rhumb line lies. + */ + function EllipsoidRhumb(start, end, ellipsoid) { + var e = defaultValue(ellipsoid, Ellipsoid.WGS84); + this._ellipsoid = e; + this._start = new Cartographic(); + this._end = new Cartographic(); + + this._heading = undefined; + this._distance = undefined; + this._ellipticity = undefined; + this._ellipticitySquared = undefined; + + if (defined(start) && defined(end)) { + computeProperties(this, start, end, e); + } + } + + defineProperties(EllipsoidRhumb.prototype, { + /** + * Gets the ellipsoid. + * @memberof EllipsoidRhumb.prototype + * @type {Ellipsoid} + * @readonly + */ + ellipsoid : { + get : function() { + return this._ellipsoid; + } + }, + + /** + * Gets the surface distance between the start and end point + * @memberof EllipsoidRhumb.prototype + * @type {Number} + * @readonly + */ + surfaceDistance : { + get : function() { + //>>includeStart('debug', pragmas.debug); + Check.defined('distance', this._distance); + //>>includeEnd('debug'); + + return this._distance; + } + }, + + /** + * Gets the initial planetodetic point on the path. + * @memberof EllipsoidRhumb.prototype + * @type {Cartographic} + * @readonly + */ + start : { + get : function() { + return this._start; + } + }, + + /** + * Gets the final planetodetic point on the path. + * @memberof EllipsoidRhumb.prototype + * @type {Cartographic} + * @readonly + */ + end : { + get : function() { + return this._end; + } + }, + + /** + * Gets the heading from the start point to the end point. + * @memberof EllipsoidRhumb.prototype + * @type {Number} + * @readonly + */ + heading : { + get : function() { + //>>includeStart('debug', pragmas.debug); + Check.defined('distance', this._distance); + //>>includeEnd('debug'); + + return this._heading; + } + } + }); + + /** + * Create a rhumb line using an initial and final position. + * + * @param {Cartographic} start The initial planetodetic point on the path. + * @param {Cartographic} end The final planetodetic point on the path. + * @param {Ellipsoid} [ellipsoid=Ellipsoid.WGS84] The ellipsoid on which the rhumb line lies. + * @param {EllipsoidRhumb} [result] The object in whice to store the result. + * @returns {EllipsoidRhumb} The EllipsoidRhumb object. + */ + EllipsoidRhumb.fromStartAndEnd = function(start, end, ellipsoid, result) { + //>>includeStart('debug', pragmas.debug); + Check.defined('start', start); + Check.defined('end', end); + //>>includeEnd('debug'); + + var e = defaultValue(ellipsoid, Ellipsoid.WGS84); + + if (defined(result)) { + result._ellipsoid = e; + result.setEndPoints(start, end); + return result; + } + return new EllipsoidRhumb(start, end, e); + }; + + /** + * Create a rhumb line using an initial position with a heading and distance. + * + * @param {Cartographic} start The initial planetodetic point on the path. + * @param {Number} heading The heading in radians. + * @param {Number} distance The rhumb line distance between the start and end point. + * @param {Ellipsoid} [ellipsoid=Ellipsoid.WGS84] The ellipsoid on which the rhumb line lies. + * @param {EllipsoidRhumb} [result] The object in whice to store the result. + * @returns {EllipsoidRhumb} The EllipsoidRhumb object. + */ + EllipsoidRhumb.fromStartHeadingDistance = function(start, heading, distance, ellipsoid, result) { + //>>includeStart('debug', pragmas.debug); + Check.defined('start', start); + Check.defined('heading', heading); + Check.defined('distance', distance); + //>>includeEnd('debug'); + + var e = defaultValue(ellipsoid, Ellipsoid.WGS84); + + if (defined(result)) { + result._ellipsoid = e; + initiailize(result, start, undefined, e); + result._heading = CesiumMath.negativePiToPi(heading); + result._distance = distance; + + result._end = result.interpolateUsingSurfaceDistance(distance, new Cartographic()); + return result; + } + + var rhumb = new EllipsoidRhumb(undefined, undefined, e); + initiailize(rhumb, start, undefined, e); + rhumb._heading = CesiumMath.negativePiToPi(heading); + rhumb._distance = distance; + rhumb._end = rhumb.interpolateUsingSurfaceDistance(distance, new Cartographic()); + + return rhumb; + }; + + /** + * Sets the start and end points of the geodesic + * + * @param {Cartographic} start The initial planetodetic point on the path. + * @param {Cartographic} end The final planetodetic point on the path. + */ + EllipsoidRhumb.prototype.setEndPoints = function(start, end) { + //>>includeStart('debug', pragmas.debug); + Check.defined('start', start); + Check.defined('end', end); + //>>includeEnd('debug'); + + computeProperties(this, start, end, this._ellipsoid); + }; + + /** + * Provides the location of a point at the indicated portion along the rhumb line. + * + * @param {Number} fraction The portion of the distance between the initial and final points. + * @param {Cartographic} result The object in which to store the result. + * @returns {Cartographic} The location of the point along the rhumb line. + */ + EllipsoidRhumb.prototype.interpolateUsingFraction = function(fraction, result) { + //>>includeStart('debug', pragmas.debug); + Check.typeOf.number('distance', this._distance); + //>>includeEnd('debug'); + + return this.interpolateUsingSurfaceDistance(fraction * this._distance, result); + }; + + /** + * Provides the location of a point at the indicated distance along the rhumb line. + * + * @param {Number} distance The distance from the inital point to the point of interest along the rhumb. + * @param {Cartographic} result The object in which to store the result. + * @returns {Cartographic} The location of the point along the geodesic. + * + * @exception {DeveloperError} start and end must be set before calling function interpolateUsingSurfaceDistance + */ + EllipsoidRhumb.prototype.interpolateUsingSurfaceDistance = function(distance, result) { + //>>includeStart('debug', pragmas.debug); + Check.defined('distance', this._distance); + //>>includeEnd('debug'); + + var ellipsoid = this._ellipsoid; + var major = ellipsoid.maximumRadius; + var ellipticity = this._ellipticity; + var ellipticitySquared = this._ellipticitySquared; + var heading = this._heading; + var start = this._start; + + var longitude; + var latitude; + var deltaLongitude; + + //Check to see if the rhumb line has constant latitude + //This won't converge if heading is close to 90 degrees + if (Math.abs(CesiumMath.PI_OVER_TWO - Math.abs(heading)) > CesiumMath.EPSILON8) { + //Calculate latitude of the second point + var M1 = calculateM(ellipticity, major, start.latitude); + var deltaM = distance * Math.cos(heading); + var M2 = M1 + deltaM; + latitude = calculateInverseM(M2, ellipticity, major); + + //Now find the longitude of the second point + var sigma1 = calculateSigma(ellipticity, start.latitude); + var sigma2 = calculateSigma(ellipticity, latitude); + deltaLongitude = Math.tan(heading) * (sigma2 - sigma1); + longitude = CesiumMath.negativePiToPi(start.longitude + deltaLongitude); + } else { //If heading is close to 90 degrees + latitude = start.latitude; + var localRad; + + if (ellipticity === 0.0) { // sphere + localRad = major * Math.cos(start.latitude); + } else { + var sinPhi = Math.sin(start.latitude); + localRad = major * Math.cos(start.latitude) / Math.sqrt(1 - ellipticitySquared * sinPhi * sinPhi); + } + + deltaLongitude = distance / localRad; + if (heading > 0.0) { + longitude = CesiumMath.negativePiToPi(start.longitude + deltaLongitude); + } else { + longitude = CesiumMath.negativePiToPi(start.longitude - deltaLongitude); + } + } + + if (defined(result)) { + result.longitude = longitude; + result.latitude = latitude; + result.height = 0; + + return result; + } + + return new Cartographic(longitude, latitude, 0); + }; + + return EllipsoidRhumb; +}); diff --git a/Specs/Core/EllipsoidRhumbSpec.js b/Specs/Core/EllipsoidRhumbSpec.js new file mode 100644 index 000000000000..b9d74fee5b91 --- /dev/null +++ b/Specs/Core/EllipsoidRhumbSpec.js @@ -0,0 +1,581 @@ +defineSuite([ + 'Core/EllipsoidRhumb', + 'Core/Cartographic', + 'Core/Ellipsoid', + 'Core/EllipsoidGeodesic', + 'Core/Math' + ], function( + EllipsoidRhumb, + Cartographic, + Ellipsoid, + EllipsoidGeodesic, + CesiumMath) { + 'use strict'; + + it('throws without start', function() { + expect(function() { + var rhumb = new EllipsoidRhumb(); + return rhumb.interpolateUsingSurfaceDistance(0); + }).toThrowDeveloperError(); + }); + + it('throws without end', function() { + expect(function() { + var rhumb = new EllipsoidRhumb(new Cartographic(Math.PI, Math.PI)); + return rhumb.interpolateUsingSurfaceDistance(0); + }).toThrowDeveloperError(); + }); + + it('throws without unique position', function() { + expect(function() { + var rhumb = new EllipsoidRhumb(new Cartographic(Math.PI, Math.PI), new Cartographic(0, Math.PI)); + return rhumb.interpolateUsingSurfaceDistance(0); + }).toThrowDeveloperError(); + }); + + it('setEndPoints throws without start', function() { + expect(function() { + var rhumb = new EllipsoidRhumb(); + rhumb.setEndPoints(); + }).toThrowDeveloperError(); + }); + + it('setEndPoints throws without end', function() { + expect(function() { + var start = new Cartographic(CesiumMath.PI_OVER_TWO, 0); + var rhumb = new EllipsoidRhumb(); + rhumb.setEndPoints(start); + return rhumb.interpolateUsingSurfaceDistance(0); + }).toThrowDeveloperError(); + }); + + it('can create using fromStartAndEnd function', function() { + var fifteenDegrees = Math.PI / 12; + var start = new Cartographic(fifteenDegrees, fifteenDegrees); + var thirtyDegrees = Math.PI / 6; + var end = new Cartographic(thirtyDegrees, thirtyDegrees); + + var rhumb = EllipsoidRhumb.fromStartAndEnd(start, end); + expect(start).toEqual(rhumb.start); + expect(end).toEqual(rhumb.end); + }); + + it('can create using fromStartAndEnd function with result', function() { + var scratch = new EllipsoidRhumb(); + + var ellipsoid = new Ellipsoid(6, 6, 3); + var fifteenDegrees = Math.PI / 12; + var start = new Cartographic(fifteenDegrees, fifteenDegrees); + var thirtyDegrees = Math.PI / 6; + var end = new Cartographic(thirtyDegrees, thirtyDegrees); + + var rhumb = EllipsoidRhumb.fromStartAndEnd(start, end, ellipsoid, scratch); + expect(rhumb).toBe(scratch); + expect(rhumb.ellipsoid).toBe(ellipsoid); + expect(start).toEqual(rhumb.start); + expect(end).toEqual(rhumb.end); + }); + + it('can create using fromStartHeadingDistance function', function() { + var ellipsoid = new Ellipsoid(6, 6, 3); + var fifteenDegrees = Math.PI / 12; + var start = new Cartographic(fifteenDegrees, fifteenDegrees); + var heading = fifteenDegrees; + var distance = 6 * fifteenDegrees; + + var rhumb = EllipsoidRhumb.fromStartHeadingDistance(start, heading, distance, ellipsoid); + expect(start).toEqual(rhumb.start); + expect(distance).toEqualEpsilon(rhumb.surfaceDistance, CesiumMath.EPSILON12); + expect(heading).toEqualEpsilon(rhumb.heading, CesiumMath.EPSILON12); + }); + + it('can create using fromStartHeadingDistance function with result', function() { + var scratch = new EllipsoidRhumb(); + + var ellipsoid = new Ellipsoid(6, 6, 3); + var fifteenDegrees = Math.PI / 12; + var start = new Cartographic(fifteenDegrees, fifteenDegrees); + var heading = fifteenDegrees; + var distance = 6 * fifteenDegrees; + + var rhumb = EllipsoidRhumb.fromStartHeadingDistance(start, heading, distance, ellipsoid, scratch); + expect(rhumb).toBe(scratch); + expect(rhumb.ellipsoid).toBe(ellipsoid); + expect(start).toEqual(rhumb.start); + expect(distance).toEqualEpsilon(rhumb.surfaceDistance, CesiumMath.EPSILON12); + expect(heading).toEqualEpsilon(rhumb.heading, CesiumMath.EPSILON12); + }); + + it('getSurfaceDistance throws if start or end never defined', function() { + expect(function() { + var rhumb = new EllipsoidRhumb(); + return rhumb.surfaceDistance; + }).toThrowDeveloperError(); + }); + + it('getHeading throws if start or end never defined', function() { + expect(function() { + var rhumb = new EllipsoidRhumb(); + return rhumb.heading; + }).toThrowDeveloperError(); + }); + + it('works with two points', function() { + var fifteenDegrees = Math.PI / 12; + var start = new Cartographic(fifteenDegrees, fifteenDegrees); + var thirtyDegrees = Math.PI / 6; + var end = new Cartographic(thirtyDegrees, thirtyDegrees); + + var rhumb = new EllipsoidRhumb(start, end); + expect(start).toEqual(rhumb.start); + expect(end).toEqual(rhumb.end); + }); + + it('sets end points', function() { + var start = new Cartographic(CesiumMath.PI_OVER_TWO, 0); + var end = new Cartographic(CesiumMath.PI_OVER_TWO, CesiumMath.PI_OVER_TWO); + var rhumb = new EllipsoidRhumb(); + rhumb.setEndPoints(start, end); + expect(start).toEqual(rhumb.start); + expect(end).toEqual(rhumb.end); + }); + + it('gets heading', function() { + var ellipsoid = new Ellipsoid(6, 6, 3); + var start = new Cartographic(CesiumMath.PI_OVER_TWO, 0); + var end = new Cartographic(Math.PI, 0); + + var rhumb = new EllipsoidRhumb(start, end, ellipsoid); + expect(CesiumMath.PI_OVER_TWO).toEqualEpsilon(rhumb.heading, CesiumMath.EPSILON12); + }); + + it('computes heading not going over the pole', function() { + var ellipsoid = Ellipsoid.WGS84; + var start = new Cartographic(0, 1.2); + var end = new Cartographic(Math.PI, 1.5); + + var rhumb = new EllipsoidRhumb(start, end, ellipsoid); + + expect(0.0).not.toEqual(rhumb.heading); + }); + + it('computes heading going over the pole', function() { + var ellipsoid = Ellipsoid.WGS84; + var start = new Cartographic(1.3, CesiumMath.PI_OVER_TWO); + var end = new Cartographic(0.0, CesiumMath.PI / 2.4); + + var rhumb = new EllipsoidRhumb(start, end, ellipsoid); + + expect(0.0).not.toEqual(rhumb.heading); + }); + + it('heading works when going around the world at constant latitude', function() { + var ellipsoid = new Ellipsoid(6, 6, 6); + var start = new Cartographic(0.0, 0.3); + var end = new Cartographic(CesiumMath.PI_OVER_TWO, 0.3); + + var rhumb = new EllipsoidRhumb(start, end, ellipsoid); + + expect(CesiumMath.PI_OVER_TWO).toEqualEpsilon(rhumb.heading, CesiumMath.EPSILON12); + + start = new Cartographic(3 * CesiumMath.PI_OVER_TWO, 0.3); + end = new Cartographic(CesiumMath.PI, 0.3); + var rhumb2 = new EllipsoidRhumb(start, end, ellipsoid); + expect(-CesiumMath.PI_OVER_TWO).toEqualEpsilon(rhumb2.heading, CesiumMath.EPSILON12); + }); + + it('computes heading for vertical lines', function() { + var ellipsoid = Ellipsoid.WGS84; + var start = new Cartographic(0.0, 1.2); + var end = new Cartographic(0.0, 1.5); + + var rhumb = new EllipsoidRhumb(start, end, ellipsoid); + expect(0.0).toEqualEpsilon(rhumb.heading, CesiumMath.EPSILON12); + + var rhumb2 = new EllipsoidRhumb(end, start, ellipsoid); + expect(CesiumMath.PI).toEqualEpsilon(rhumb2.heading, CesiumMath.EPSILON12); + }); + + it('computes no distance', function() { + var ellipsoid = new Ellipsoid(6, 6, 3); + var start = new Cartographic(CesiumMath.PI_OVER_TWO, 0.0); + var end = new Cartographic(CesiumMath.PI_OVER_TWO, 0.0); + + var rhumb = new EllipsoidRhumb(start, end, ellipsoid); + expect(0).toEqualEpsilon(rhumb.surfaceDistance, CesiumMath.EPSILON12); + expect(0).toEqualEpsilon(rhumb.heading, CesiumMath.EPSILON12); + }); + + it('computes distance at equator', function() { + var ellipsoid = new Ellipsoid(6, 6, 3); + var start = new Cartographic(-CesiumMath.PI_OVER_FOUR, 0.0); + var end = new Cartographic(CesiumMath.PI_OVER_FOUR, 0.0); + + var rhumb = new EllipsoidRhumb(start, end, ellipsoid); + expect(CesiumMath.PI_OVER_TWO * 6).toEqualEpsilon(rhumb.surfaceDistance, CesiumMath.EPSILON12); + }); + + it('computes distance at meridian', function() { + var ellipsoid = new Ellipsoid(6, 6, 6); + var fifteenDegrees = Math.PI / 12; + var start = new Cartographic(CesiumMath.PI_OVER_TWO, fifteenDegrees); + var fortyfiveDegrees = Math.PI / 4; + var end = new Cartographic(CesiumMath.PI_OVER_TWO, fortyfiveDegrees); + + var rhumb = new EllipsoidRhumb(start, end, ellipsoid); + var thirtyDegrees = Math.PI / 6; + expect(thirtyDegrees * 6).toEqualEpsilon(rhumb.surfaceDistance, CesiumMath.EPSILON12); + }); + + it('computes distance for sphere 90 degrees along meridian and equator and check equality', function() { + var ellipsoid = new Ellipsoid(6, 6, 6); + var fortyFiveSouth = new Cartographic(0.0, -CesiumMath.PI_OVER_FOUR); + var fortyFiveNorth = new Cartographic(0.0, CesiumMath.PI_OVER_FOUR); + var fortyFiveWest = new Cartographic(-CesiumMath.PI_OVER_FOUR, 0.0); + var fortyFiveEast = new Cartographic(CesiumMath.PI_OVER_FOUR, 0.0); + + var westEastRhumb = new EllipsoidRhumb(fortyFiveWest, fortyFiveEast, ellipsoid); + var southNorthRhumb = new EllipsoidRhumb(fortyFiveSouth, fortyFiveNorth, ellipsoid); + var eastWestRhumb = new EllipsoidRhumb(fortyFiveEast, fortyFiveWest, ellipsoid); + var northSouthRhumb = new EllipsoidRhumb(fortyFiveNorth, fortyFiveSouth, ellipsoid); + expect(CesiumMath.PI_OVER_TWO * 6).toEqualEpsilon(westEastRhumb.surfaceDistance, CesiumMath.EPSILON12); + expect(CesiumMath.PI_OVER_TWO * 6).toEqualEpsilon(southNorthRhumb.surfaceDistance, CesiumMath.EPSILON12); + expect(westEastRhumb.surfaceDistance).toEqualEpsilon(southNorthRhumb.surfaceDistance, CesiumMath.EPSILON12); + + expect(CesiumMath.PI_OVER_TWO * 6).toEqualEpsilon(eastWestRhumb.surfaceDistance, CesiumMath.EPSILON12); + expect(CesiumMath.PI_OVER_TWO * 6).toEqualEpsilon(northSouthRhumb.surfaceDistance, CesiumMath.EPSILON12); + expect(eastWestRhumb.surfaceDistance).toEqualEpsilon(northSouthRhumb.surfaceDistance, CesiumMath.EPSILON12); + }); + + it('computes distance at same latitude', function() { + var ellipsoid = new Ellipsoid(6, 6, 6); + var fortyfiveDegrees = Math.PI / 4; + var start = new Cartographic(0, -fortyfiveDegrees); + var end = new Cartographic(CesiumMath.PI_OVER_TWO, -fortyfiveDegrees); + + var rhumb = new EllipsoidRhumb(start, end, ellipsoid); + var distance = Math.cos(fortyfiveDegrees) * CesiumMath.PI_OVER_TWO * 6; + expect(distance).toEqualEpsilon(rhumb.surfaceDistance, CesiumMath.EPSILON12); + }); + + it('tests sphere', function() { + var radius = 6378137.0; + var ellipsoid = new Ellipsoid(radius, radius, radius); + var fifteenDegrees = Math.PI / 12; + var initial = new Cartographic(fifteenDegrees, fifteenDegrees); + var distance = radius * fifteenDegrees; + + var rhumb1 = EllipsoidRhumb.fromStartHeadingDistance(initial, fifteenDegrees, distance, ellipsoid); + var rhumb2 = new EllipsoidRhumb(initial, rhumb1.end, ellipsoid); + + expect(fifteenDegrees).toEqualEpsilon(rhumb2.heading, CesiumMath.EPSILON12); + expect(distance).toEqualEpsilon(rhumb2.surfaceDistance, CesiumMath.EPSILON6); + }); + + it('tests sphereoid', function() { + var ellipsoid = Ellipsoid.WGS84; + var fifteenDegrees = Math.PI / 12; + var initial = new Cartographic(fifteenDegrees, fifteenDegrees); + var distance = ellipsoid.maximumRadius * fifteenDegrees; + + var rhumb1 = EllipsoidRhumb.fromStartHeadingDistance(initial, fifteenDegrees, distance, ellipsoid); + var rhumb2 = new EllipsoidRhumb(initial, rhumb1.end, ellipsoid); + + expect(fifteenDegrees).toEqualEpsilon(rhumb2.heading, CesiumMath.EPSILON12); + expect(distance).toEqualEpsilon(rhumb2.surfaceDistance, CesiumMath.EPSILON6); + }); + + it('tests sphere close to 90 degrees', function() { + var radius = 6378137.0; + var ellipsoid = new Ellipsoid(radius, radius, radius); + var fifteenDegrees = Math.PI / 12; + var initial = new Cartographic(fifteenDegrees, fifteenDegrees); + var distance = radius * fifteenDegrees; + + var oneDegree = CesiumMath.RADIANS_PER_DEGREE; + var eightyNineDegrees = 89 * oneDegree; + var eightyNinePointNineDegrees = 89.9 * oneDegree; + var ninetyDegrees = 90 * oneDegree; + var ninetyPointOneDegrees = 90.1 * oneDegree; + var ninetyPointZeroTwoDegrees = 90.02 * oneDegree; + + var rhumb1 = EllipsoidRhumb.fromStartHeadingDistance(initial, eightyNineDegrees, distance, ellipsoid); + var rhumb2 = new EllipsoidRhumb(initial, rhumb1.end, ellipsoid); + expect(rhumb1.heading).toEqualEpsilon(rhumb2.heading, CesiumMath.EPSILON12); + expect(rhumb1.surfaceDistance).toEqualEpsilon(rhumb2.surfaceDistance, CesiumMath.EPSILON6); + + rhumb1 = EllipsoidRhumb.fromStartHeadingDistance(initial, eightyNinePointNineDegrees, distance, ellipsoid); + rhumb2 = new EllipsoidRhumb(initial, rhumb1.end, ellipsoid); + expect(rhumb1.heading).toEqualEpsilon(rhumb2.heading, CesiumMath.EPSILON12); + expect(rhumb1.surfaceDistance).toEqualEpsilon(rhumb2.surfaceDistance, CesiumMath.EPSILON6); + + rhumb1 = EllipsoidRhumb.fromStartHeadingDistance(initial, ninetyDegrees, distance, ellipsoid); + rhumb2 = new EllipsoidRhumb(initial, rhumb1.end, ellipsoid); + expect(rhumb1.heading).toEqualEpsilon(rhumb2.heading, CesiumMath.EPSILON12); + expect(rhumb1.surfaceDistance).toEqualEpsilon(rhumb2.surfaceDistance, CesiumMath.EPSILON6); + + rhumb1 = EllipsoidRhumb.fromStartHeadingDistance(initial, ninetyPointOneDegrees, distance, ellipsoid); + rhumb2 = new EllipsoidRhumb(initial, rhumb1.end, ellipsoid); + expect(rhumb1.heading).toEqualEpsilon(rhumb2.heading, CesiumMath.EPSILON12); + expect(rhumb1.surfaceDistance).toEqualEpsilon(rhumb2.surfaceDistance, CesiumMath.EPSILON6); + + rhumb1 = EllipsoidRhumb.fromStartHeadingDistance(initial, ninetyPointZeroTwoDegrees, distance, ellipsoid); + rhumb2 = new EllipsoidRhumb(initial, rhumb1.end, ellipsoid); + expect(rhumb1.heading).toEqualEpsilon(rhumb2.heading, CesiumMath.EPSILON12); + expect(rhumb1.surfaceDistance).toEqualEpsilon(rhumb2.surfaceDistance, CesiumMath.EPSILON6); + }); + + it('tests spheroid close to 90 degrees', function() { + var ellipsoid = Ellipsoid.WGS84; + var fifteenDegrees = Math.PI / 12; + var initial = new Cartographic(fifteenDegrees, fifteenDegrees); + var distance = ellipsoid.maximumRadius * fifteenDegrees; + + var oneDegree = CesiumMath.RADIANS_PER_DEGREE; + var eightyNineDegrees = 89 * oneDegree; + var eightyNinePointNineDegrees = 89.9 * oneDegree; + var ninetyDegrees = 90 * oneDegree; + var ninetyPointOneDegrees = 90.1 * oneDegree; + var ninetyPointZeroTwoDegrees = 90.02 * oneDegree; + + var rhumb1 = EllipsoidRhumb.fromStartHeadingDistance(initial, eightyNineDegrees, distance, ellipsoid); + var rhumb2 = new EllipsoidRhumb(initial, rhumb1.end, ellipsoid); + expect(rhumb1.heading).toEqualEpsilon(rhumb2.heading, CesiumMath.EPSILON12); + expect(rhumb1.surfaceDistance).toEqualEpsilon(rhumb2.surfaceDistance, CesiumMath.EPSILON3); + + rhumb1 = EllipsoidRhumb.fromStartHeadingDistance(initial, eightyNinePointNineDegrees, distance, ellipsoid); + rhumb2 = new EllipsoidRhumb(initial, rhumb1.end, ellipsoid); + expect(rhumb1.heading).toEqualEpsilon(rhumb2.heading, CesiumMath.EPSILON12); + expect(rhumb1.surfaceDistance).toEqualEpsilon(rhumb2.surfaceDistance, CesiumMath.EPSILON3); + + rhumb1 = EllipsoidRhumb.fromStartHeadingDistance(initial, ninetyDegrees, distance, ellipsoid); + rhumb2 = new EllipsoidRhumb(initial, rhumb1.end, ellipsoid); + expect(rhumb1.heading).toEqualEpsilon(rhumb2.heading, CesiumMath.EPSILON12); + expect(rhumb1.surfaceDistance).toEqualEpsilon(rhumb2.surfaceDistance, CesiumMath.EPSILON3); + + rhumb1 = EllipsoidRhumb.fromStartHeadingDistance(initial, ninetyPointOneDegrees, distance, ellipsoid); + rhumb2 = new EllipsoidRhumb(initial, rhumb1.end, ellipsoid); + expect(rhumb1.heading).toEqualEpsilon(rhumb2.heading, CesiumMath.EPSILON12); + expect(rhumb1.surfaceDistance).toEqualEpsilon(rhumb2.surfaceDistance, CesiumMath.EPSILON3); + + rhumb1 = EllipsoidRhumb.fromStartHeadingDistance(initial, ninetyPointZeroTwoDegrees, distance, ellipsoid); + rhumb2 = new EllipsoidRhumb(initial, rhumb1.end, ellipsoid); + expect(rhumb1.heading).toEqualEpsilon(rhumb2.heading, CesiumMath.EPSILON12); + expect(rhumb1.surfaceDistance).toEqualEpsilon(rhumb2.surfaceDistance, CesiumMath.EPSILON3); + }); + + it('test sphereoid across meridian', function() { + var ellipsoid = Ellipsoid.WGS84; + var fifteenDegrees = Math.PI / 12; + var initial = new Cartographic(-fifteenDegrees, 0.0); + var final = new Cartographic(fifteenDegrees, 0.0); + var distance = ellipsoid.maximumRadius * 2 * fifteenDegrees; + + var rhumb1 = new EllipsoidRhumb(initial, final, ellipsoid); + var rhumb2 = EllipsoidRhumb.fromStartHeadingDistance(initial, CesiumMath.PI_OVER_TWO, distance, ellipsoid); + + expect(rhumb1.heading).toEqualEpsilon(rhumb2.heading, CesiumMath.EPSILON12); + expect(rhumb1.surfaceDistance).toEqualEpsilon(rhumb2.surfaceDistance, CesiumMath.EPSILON6); + }); + + it('test across IDL with -PI to PI range of longitude', function() { + var ellipsoid = Ellipsoid.WGS84; + var fifteenDegrees = Math.PI / 12.0; + var initial = new Cartographic(-CesiumMath.PI + fifteenDegrees, 0.0); + var final = new Cartographic(CesiumMath.PI - fifteenDegrees, 0.0); + + var distance = ellipsoid.maximumRadius * 2 * fifteenDegrees; + + var rhumb1 = new EllipsoidRhumb(initial, final, ellipsoid); + var rhumb2 = new EllipsoidRhumb.fromStartHeadingDistance(initial, 3.0 * CesiumMath.PI_OVER_TWO, distance, ellipsoid); + + expect(-CesiumMath.PI_OVER_TWO).toEqualEpsilon(rhumb1.heading, CesiumMath.EPSILON12); + expect(CesiumMath.PI / 6 * ellipsoid.maximumRadius).toEqualEpsilon(rhumb1.surfaceDistance, CesiumMath.EPSILON6); + expect(rhumb1.heading).toEqualEpsilon(rhumb2.heading, CesiumMath.EPSILON12); + expect(rhumb1.surfaceDistance).toEqualEpsilon(rhumb2.surfaceDistance, CesiumMath.EPSILON6); + + var rhumb3 = new EllipsoidRhumb(final, initial, ellipsoid); + var rhumb4 = new EllipsoidRhumb.fromStartHeadingDistance(final, CesiumMath.PI_OVER_TWO, distance, ellipsoid); + expect(CesiumMath.PI_OVER_TWO).toEqualEpsilon(rhumb3.heading, CesiumMath.EPSILON12); + expect(CesiumMath.PI / 6 * ellipsoid.maximumRadius).toEqualEpsilon(rhumb3.surfaceDistance, CesiumMath.EPSILON6); + expect(rhumb3.heading).toEqualEpsilon(rhumb4.heading, CesiumMath.EPSILON12); + expect(rhumb3.surfaceDistance).toEqualEpsilon(rhumb4.surfaceDistance, CesiumMath.EPSILON6); + }); + + it('test across equator', function() { + var ellipsoid = Ellipsoid.WGS84; + var fifteenDegrees = Math.PI / 12.0; + var oneDegree = Math.PI / 180.0; + var initial = new Cartographic(fifteenDegrees, -oneDegree); + var final = new Cartographic(fifteenDegrees, oneDegree); + + //A rhumb line with heading = 0 should be almost the same as a geodesic + var rhumb = new EllipsoidRhumb(initial, final, ellipsoid); + var geodesic = new EllipsoidGeodesic(initial, final, ellipsoid); + expect(0.0).toEqualEpsilon(rhumb.heading, CesiumMath.EPSILON12); + expect(geodesic.startHeading).toEqualEpsilon(rhumb.heading, CesiumMath.EPSILON12); + }); + + it('test on equator', function() { + var ellipsoid = Ellipsoid.WGS84; + var initial = new Cartographic(0.0, 0.0); + var final = new Cartographic(CesiumMath.PI - 1, 0.0); + + //A rhumb line on the equator should be the same as a geodesic + var rhumb = new EllipsoidRhumb(initial, final, ellipsoid); + var geodesic = new EllipsoidGeodesic(initial, final, ellipsoid); + expect(CesiumMath.PI_OVER_TWO).toEqualEpsilon(rhumb.heading, CesiumMath.EPSILON12); + expect(geodesic.startHeading).toEqualEpsilon(rhumb.heading, CesiumMath.EPSILON12); + expect(geodesic.surfaceDistance).toEqualEpsilon(rhumb.surfaceDistance, CesiumMath.EPSILON4); + }); + + it('test close to poles', function() { + var ellipsoid = Ellipsoid.WGS84; + var fiveDegrees = CesiumMath.PI / 36.0; + var fifteenDegrees = 3 * fiveDegrees; + var eightyDegrees = 16 * fiveDegrees; + + var distance = fifteenDegrees * ellipsoid.maximumRadius; + + var initial = new Cartographic(0.0, eightyDegrees); + + var rhumb1 = EllipsoidRhumb.fromStartHeadingDistance(initial, eightyDegrees, distance, ellipsoid); + var rhumb2 = new EllipsoidRhumb(initial, rhumb1.end, ellipsoid); + + expect(rhumb1.heading).toEqualEpsilon(rhumb2.heading, CesiumMath.EPSILON12); + expect(rhumb1.surfaceDistance).toEqualEpsilon(rhumb2.surfaceDistance, CesiumMath.EPSILON6); + }); + + it('test interpolate fraction', function() { + var ellipsoid = Ellipsoid.WGS84; + var initial = new Cartographic(0.0, 0.0); + var final = new Cartographic(CesiumMath.PI_OVER_TWO, 0.0); + var halfway = new Cartographic(CesiumMath.PI_OVER_FOUR, 0.0); + + var rhumb = new EllipsoidRhumb(initial, final, ellipsoid); + var interpolatedPoint = rhumb.interpolateUsingFraction(0.5); + + expect(halfway.longitude).toEqualEpsilon(interpolatedPoint.longitude, CesiumMath.EPSILON12); + expect(halfway.latitude).toEqualEpsilon(interpolatedPoint.latitude, CesiumMath.EPSILON12); + }); + + it('test interpolate distance', function() { + var ellipsoid = Ellipsoid.WGS84; + var initial = new Cartographic(0.0, 0.0); + var final = new Cartographic(CesiumMath.PI_OVER_TWO, 0.0); + var halfway = new Cartographic(CesiumMath.PI_OVER_FOUR, 0.0); + + var distance = ellipsoid.maximumRadius * CesiumMath.PI_OVER_FOUR; + + var rhumb = new EllipsoidRhumb(initial, final, ellipsoid); + var interpolatedPoint = rhumb.interpolateUsingSurfaceDistance(distance); + + expect(halfway.longitude).toEqualEpsilon(interpolatedPoint.longitude, CesiumMath.EPSILON12); + expect(halfway.latitude).toEqualEpsilon(interpolatedPoint.latitude, CesiumMath.EPSILON12); + }); + + it('interpolates start and end points', function() { + var fifteenDegrees = Math.PI / 12; + var start = new Cartographic(fifteenDegrees, fifteenDegrees); + var thirtyDegrees = Math.PI / 6; + var end = new Cartographic(thirtyDegrees, thirtyDegrees); + + var rhumb = new EllipsoidRhumb(start, end); + var distance = rhumb.surfaceDistance; + + var first = rhumb.interpolateUsingSurfaceDistance(0.0); + var last = rhumb.interpolateUsingSurfaceDistance(distance); + + expect(start.longitude).toEqualEpsilon(first.longitude, CesiumMath.EPSILON12); + expect(start.latitude).toEqualEpsilon(first.latitude, CesiumMath.EPSILON12); + expect(end.longitude).toEqualEpsilon(last.longitude, CesiumMath.EPSILON12); + expect(end.latitude).toEqualEpsilon(last.latitude, CesiumMath.EPSILON12); + }); + + it('interpolates midpoint', function() { + var fifteenDegrees = Math.PI / 12; + var start = new Cartographic(fifteenDegrees, 0.0); + var fortyfiveDegrees = Math.PI / 4; + var end = new Cartographic(fortyfiveDegrees, 0.0); + var thirtyDegrees = Math.PI / 6; + var expectedMid = new Cartographic(thirtyDegrees, 0.0); + + var rhumb = new EllipsoidRhumb(start, end); + var distance = Ellipsoid.WGS84.radii.x * fifteenDegrees; + + var midpoint = rhumb.interpolateUsingSurfaceDistance(distance); + + expect(expectedMid.longitude).toEqualEpsilon(midpoint.longitude, CesiumMath.EPSILON12); + expect(expectedMid.latitude).toEqualEpsilon(midpoint.latitude, CesiumMath.EPSILON12); + }); + + it('interpolates start and end points using fraction', function() { + var fifteenDegrees = Math.PI / 12; + var start = new Cartographic(fifteenDegrees, fifteenDegrees); + var thirtyDegrees = Math.PI / 6; + var end = new Cartographic(thirtyDegrees, thirtyDegrees); + + var rhumb = new EllipsoidRhumb(start, end); + + var first = rhumb.interpolateUsingFraction(0); + var last = rhumb.interpolateUsingFraction(1); + + expect(start.longitude).toEqualEpsilon(first.longitude, CesiumMath.EPSILON12); + expect(start.latitude).toEqualEpsilon(first.latitude, CesiumMath.EPSILON12); + expect(end.longitude).toEqualEpsilon(last.longitude, CesiumMath.EPSILON12); + expect(end.latitude).toEqualEpsilon(last.latitude, CesiumMath.EPSILON12); + }); + + it('interpolates midpoint using fraction', function() { + var fifteenDegrees = Math.PI / 12; + var start = new Cartographic(fifteenDegrees, 0.0); + var fortyfiveDegrees = Math.PI / 4; + var end = new Cartographic(fortyfiveDegrees, 0.0); + var thirtyDegrees = Math.PI / 6; + var expectedMid = new Cartographic(thirtyDegrees, 0.0); + + var rhumb = new EllipsoidRhumb(start, end); + + var midpoint = rhumb.interpolateUsingFraction(0.5); + + expect(expectedMid.longitude).toEqualEpsilon(midpoint.longitude, CesiumMath.EPSILON12); + expect(expectedMid.latitude).toEqualEpsilon(midpoint.latitude, CesiumMath.EPSILON12); + }); + + it('interpolates midpoint fraction using result parameter', function() { + var fifteenDegrees = Math.PI / 12; + var start = new Cartographic(fifteenDegrees, 0.0); + var fortyfiveDegrees = Math.PI / 4; + var end = new Cartographic(fortyfiveDegrees, 0.0); + var thirtyDegrees = Math.PI / 6; + var expectedMid = new Cartographic(thirtyDegrees, 0.0); + + var rhumb = new EllipsoidRhumb(start, end); + var result = new Cartographic(); + var midpoint = rhumb.interpolateUsingFraction(0.5, result); + expect(result).toBe(midpoint); + + expect(expectedMid.longitude).toEqualEpsilon(result.longitude, CesiumMath.EPSILON12); + expect(expectedMid.latitude).toEqualEpsilon(result.latitude, CesiumMath.EPSILON12); + }); + + it('interpolates midpoint using result parameter', function() { + var fifteenDegrees = Math.PI / 12; + var start = new Cartographic(fifteenDegrees, 0.0); + var fortyfiveDegrees = Math.PI / 4; + var end = new Cartographic(fortyfiveDegrees, 0.0); + var thirtyDegrees = Math.PI / 6; + var expectedMid = new Cartographic(thirtyDegrees, 0.0); + + var rhumb = new EllipsoidRhumb(start, end); + var distance = Ellipsoid.WGS84.radii.x * fifteenDegrees; + + var result = new Cartographic(); + var midpoint = rhumb.interpolateUsingSurfaceDistance(distance, result); + + expect(result).toBe(midpoint); + + expect(expectedMid.longitude).toEqualEpsilon(result.longitude, CesiumMath.EPSILON12); + expect(expectedMid.latitude).toEqualEpsilon(result.latitude, CesiumMath.EPSILON12); + }); +}); From 1722c44903fb6f0589b129e19b4c81317c09b11a Mon Sep 17 00:00:00 2001 From: Shehzan Mohammed Date: Mon, 14 Jan 2019 14:48:59 -0500 Subject: [PATCH 02/11] Add EllipsoidRhumb to CHANGES.md --- CHANGES.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGES.md b/CHANGES.md index d83761531d1e..7d1e941acbfd 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -15,6 +15,7 @@ Change Log * Added support for polylines on 3D Tiles. [#7437](https://github.com/AnalyticalGraphicsInc/cesium/pull/7437) * Added `classificationType` property to `PolylineGraphics` and `GroundPolylinePrimitive` which specifies whether a polyline clamped to ground should be clamped to terrain, 3D Tiles, or both. [#7437](https://github.com/AnalyticalGraphicsInc/cesium/pull/7437) * Added the ability to specify the width of the intersection volume for `Scene.sampleHeight`, `Scene.clampToHeight`, `Scene.sampleHeightMostDetailed`, and `Scene.clampToHeightMostDetailed`. [#7287](https://github.com/AnalyticalGraphicsInc/cesium/pull/7287) +* Added `EllipsoidRhumb` class as a rhumb line counterpart to `EllipsoidGeodesic`. [#7484](https://github.com/AnalyticalGraphicsInc/cesium/pull/7484) ##### Fixes :wrench: * Fixed an issue where classification primitives with the `CESIUM_3D_TILE` classification type would render on terrain. [#7422](https://github.com/AnalyticalGraphicsInc/cesium/pull/7422) From 94e1ecae750d9166be7a21afe7f490ef83024167 Mon Sep 17 00:00:00 2001 From: Shehzan Mohammed Date: Tue, 15 Jan 2019 12:22:29 -0500 Subject: [PATCH 03/11] EllipsoidRhumb -> EllipsoidRhumbLine --- CHANGES.md | 2 +- ...llipsoidRhumb.js => EllipsoidRhumbLine.js} | 100 ++++++------ ...RhumbSpec.js => EllipsoidRhumbLineSpec.js} | 148 +++++++++--------- 3 files changed, 125 insertions(+), 125 deletions(-) rename Source/Core/{EllipsoidRhumb.js => EllipsoidRhumbLine.js} (79%) rename Specs/Core/{EllipsoidRhumbSpec.js => EllipsoidRhumbLineSpec.js} (79%) diff --git a/CHANGES.md b/CHANGES.md index 7d1e941acbfd..56575a901a57 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -15,7 +15,7 @@ Change Log * Added support for polylines on 3D Tiles. [#7437](https://github.com/AnalyticalGraphicsInc/cesium/pull/7437) * Added `classificationType` property to `PolylineGraphics` and `GroundPolylinePrimitive` which specifies whether a polyline clamped to ground should be clamped to terrain, 3D Tiles, or both. [#7437](https://github.com/AnalyticalGraphicsInc/cesium/pull/7437) * Added the ability to specify the width of the intersection volume for `Scene.sampleHeight`, `Scene.clampToHeight`, `Scene.sampleHeightMostDetailed`, and `Scene.clampToHeightMostDetailed`. [#7287](https://github.com/AnalyticalGraphicsInc/cesium/pull/7287) -* Added `EllipsoidRhumb` class as a rhumb line counterpart to `EllipsoidGeodesic`. [#7484](https://github.com/AnalyticalGraphicsInc/cesium/pull/7484) +* Added `EllipsoidRhumbLine` class as a rhumb line counterpart to `EllipsoidGeodesic`. [#7484](https://github.com/AnalyticalGraphicsInc/cesium/pull/7484) ##### Fixes :wrench: * Fixed an issue where classification primitives with the `CESIUM_3D_TILE` classification type would render on terrain. [#7422](https://github.com/AnalyticalGraphicsInc/cesium/pull/7422) diff --git a/Source/Core/EllipsoidRhumb.js b/Source/Core/EllipsoidRhumbLine.js similarity index 79% rename from Source/Core/EllipsoidRhumb.js rename to Source/Core/EllipsoidRhumbLine.js index c95da8267758..fd6546120249 100644 --- a/Source/Core/EllipsoidRhumb.js +++ b/Source/Core/EllipsoidRhumbLine.js @@ -98,18 +98,18 @@ define([ return Math.log(Math.tan(0.5 * (CesiumMath.PI_OVER_TWO + latitude))) - (ellipticity / 2.0 * Math.log((1 + eSinL) / (1 - eSinL))); } - function calculateHeading(ellipsoidRhumb, major, minor, firstLongitude, firstLatitude, secondLongitude, secondLatitude) { - var sigma1 = calculateSigma(ellipsoidRhumb._ellipticity, firstLatitude); - var sigma2 = calculateSigma(ellipsoidRhumb._ellipticity, secondLatitude); + function calculateHeading(ellipsoidRhumbLine, major, minor, firstLongitude, firstLatitude, secondLongitude, secondLatitude) { + var sigma1 = calculateSigma(ellipsoidRhumbLine._ellipticity, firstLatitude); + var sigma2 = calculateSigma(ellipsoidRhumbLine._ellipticity, secondLatitude); return Math.atan2(CesiumMath.negativePiToPi(secondLongitude - firstLongitude), sigma2 - sigma1); } - function calculateArcLength(ellipsoidRhumb, major, minor, firstLongitude, firstLatitude, secondLongitude, secondLatitude) { + function calculateArcLength(ellipsoidRhumbLine, major, minor, firstLongitude, firstLatitude, secondLongitude, secondLatitude) { //>>includeStart('debug', pragmas.debug); - Check.defined('heading', ellipsoidRhumb._heading); + Check.defined('heading', ellipsoidRhumbLine._heading); //>>includeEnd('debug'); - var heading = ellipsoidRhumb._heading; + var heading = ellipsoidRhumbLine._heading; var deltaLongitude = secondLongitude - firstLongitude; var distance = 0.0; @@ -121,11 +121,11 @@ define([ distance = major * Math.cos(firstLatitude) * CesiumMath.negativePiToPi(deltaLongitude); } else { var sinPhi = Math.sin(firstLatitude); - distance = major * Math.cos(firstLatitude) * CesiumMath.negativePiToPi(deltaLongitude) / Math.sqrt(1 - ellipsoidRhumb._ellipticitySquared * sinPhi * sinPhi); + distance = major * Math.cos(firstLatitude) * CesiumMath.negativePiToPi(deltaLongitude) / Math.sqrt(1 - ellipsoidRhumbLine._ellipticitySquared * sinPhi * sinPhi); } } else { - var M1 = calculateM(ellipsoidRhumb._ellipticity, major, firstLatitude); - var M2 = calculateM(ellipsoidRhumb._ellipticity, major, secondLatitude); + var M1 = calculateM(ellipsoidRhumbLine._ellipticity, major, firstLatitude); + var M2 = calculateM(ellipsoidRhumbLine._ellipticity, major, secondLatitude); distance = (M2 - M1) / Math.cos(heading); } @@ -135,25 +135,25 @@ define([ var scratchCart1 = new Cartesian3(); var scratchCart2 = new Cartesian3(); - function initiailize(ellipsoidRhumb, start, end, ellipsoid) { + function initiailize(ellipsoidRhumbLine, start, end, ellipsoid) { var major = ellipsoid.maximumRadius; var minor = ellipsoid.minimumRadius; var majorSquared = major * major; var minorSquared = minor * minor; - ellipsoidRhumb._ellipticitySquared = (majorSquared - minorSquared) / majorSquared; - ellipsoidRhumb._ellipticity = Math.sqrt(ellipsoidRhumb._ellipticitySquared); + ellipsoidRhumbLine._ellipticitySquared = (majorSquared - minorSquared) / majorSquared; + ellipsoidRhumbLine._ellipticity = Math.sqrt(ellipsoidRhumbLine._ellipticitySquared); if (defined(start)) { - ellipsoidRhumb._start = Cartographic.clone(start, ellipsoidRhumb._start); - ellipsoidRhumb._start.height = 0; + ellipsoidRhumbLine._start = Cartographic.clone(start, ellipsoidRhumbLine._start); + ellipsoidRhumbLine._start.height = 0; } if (defined(end)) { - ellipsoidRhumb._end = Cartographic.clone(end, ellipsoidRhumb._end); - ellipsoidRhumb._end.height = 0; + ellipsoidRhumbLine._end = Cartographic.clone(end, ellipsoidRhumbLine._end); + ellipsoidRhumbLine._end.height = 0; } } - function computeProperties(ellipsoidRhumb, start, end, ellipsoid) { + function computeProperties(ellipsoidRhumbLine, start, end, ellipsoid) { var firstCartesian = Cartesian3.normalize(ellipsoid.cartographicToCartesian(start, scratchCart2), scratchCart1); var lastCartesian = Cartesian3.normalize(ellipsoid.cartographicToCartesian(end, scratchCart2), scratchCart2); @@ -161,25 +161,25 @@ define([ Check.typeOf.number.greaterThanOrEquals('value', Math.abs(Math.abs(Cartesian3.angleBetween(firstCartesian, lastCartesian)) - Math.PI), 0.0125); //>>includeEnd('debug'); - initiailize(ellipsoidRhumb, start, end, ellipsoid); + initiailize(ellipsoidRhumbLine, start, end, ellipsoid); - ellipsoidRhumb._heading = calculateHeading(ellipsoidRhumb, ellipsoid.maximumRadius, ellipsoid.minimumRadius, - start.longitude, start.latitude, end.longitude, end.latitude); - ellipsoidRhumb._distance = calculateArcLength(ellipsoidRhumb, ellipsoid.maximumRadius, ellipsoid.minimumRadius, - start.longitude, start.latitude, end.longitude, end.latitude); + ellipsoidRhumbLine._heading = calculateHeading(ellipsoidRhumbLine, ellipsoid.maximumRadius, ellipsoid.minimumRadius, + start.longitude, start.latitude, end.longitude, end.latitude); + ellipsoidRhumbLine._distance = calculateArcLength(ellipsoidRhumbLine, ellipsoid.maximumRadius, ellipsoid.minimumRadius, + start.longitude, start.latitude, end.longitude, end.latitude); } /** * Initializes a rhumb line on the ellipsoid connecting the two provided planetodetic points. * - * @alias EllipsoidRhumb + * @alias EllipsoidRhumbLine * @constructor * * @param {Cartographic} [start] The initial planetodetic point on the path. * @param {Cartographic} [end] The final planetodetic point on the path. * @param {Ellipsoid} [ellipsoid=Ellipsoid.WGS84] The ellipsoid on which the rhumb line lies. */ - function EllipsoidRhumb(start, end, ellipsoid) { + function EllipsoidRhumbLine(start, end, ellipsoid) { var e = defaultValue(ellipsoid, Ellipsoid.WGS84); this._ellipsoid = e; this._start = new Cartographic(); @@ -195,10 +195,10 @@ define([ } } - defineProperties(EllipsoidRhumb.prototype, { + defineProperties(EllipsoidRhumbLine.prototype, { /** * Gets the ellipsoid. - * @memberof EllipsoidRhumb.prototype + * @memberof EllipsoidRhumbLine.prototype * @type {Ellipsoid} * @readonly */ @@ -210,7 +210,7 @@ define([ /** * Gets the surface distance between the start and end point - * @memberof EllipsoidRhumb.prototype + * @memberof EllipsoidRhumbLine.prototype * @type {Number} * @readonly */ @@ -226,7 +226,7 @@ define([ /** * Gets the initial planetodetic point on the path. - * @memberof EllipsoidRhumb.prototype + * @memberof EllipsoidRhumbLine.prototype * @type {Cartographic} * @readonly */ @@ -238,7 +238,7 @@ define([ /** * Gets the final planetodetic point on the path. - * @memberof EllipsoidRhumb.prototype + * @memberof EllipsoidRhumbLine.prototype * @type {Cartographic} * @readonly */ @@ -250,7 +250,7 @@ define([ /** * Gets the heading from the start point to the end point. - * @memberof EllipsoidRhumb.prototype + * @memberof EllipsoidRhumbLine.prototype * @type {Number} * @readonly */ @@ -271,10 +271,10 @@ define([ * @param {Cartographic} start The initial planetodetic point on the path. * @param {Cartographic} end The final planetodetic point on the path. * @param {Ellipsoid} [ellipsoid=Ellipsoid.WGS84] The ellipsoid on which the rhumb line lies. - * @param {EllipsoidRhumb} [result] The object in whice to store the result. - * @returns {EllipsoidRhumb} The EllipsoidRhumb object. + * @param {EllipsoidRhumbLine} [result] The object in whice to store the result. + * @returns {EllipsoidRhumbLine} The EllipsoidRhumbLine object. */ - EllipsoidRhumb.fromStartAndEnd = function(start, end, ellipsoid, result) { + EllipsoidRhumbLine.fromStartAndEnd = function(start, end, ellipsoid, result) { //>>includeStart('debug', pragmas.debug); Check.defined('start', start); Check.defined('end', end); @@ -287,7 +287,7 @@ define([ result.setEndPoints(start, end); return result; } - return new EllipsoidRhumb(start, end, e); + return new EllipsoidRhumbLine(start, end, e); }; /** @@ -297,10 +297,10 @@ define([ * @param {Number} heading The heading in radians. * @param {Number} distance The rhumb line distance between the start and end point. * @param {Ellipsoid} [ellipsoid=Ellipsoid.WGS84] The ellipsoid on which the rhumb line lies. - * @param {EllipsoidRhumb} [result] The object in whice to store the result. - * @returns {EllipsoidRhumb} The EllipsoidRhumb object. + * @param {EllipsoidRhumbLine} [result] The object in which to store the result. + * @returns {EllipsoidRhumbLine} The EllipsoidRhumbLine object. */ - EllipsoidRhumb.fromStartHeadingDistance = function(start, heading, distance, ellipsoid, result) { + EllipsoidRhumbLine.fromStartHeadingDistance = function(start, heading, distance, ellipsoid, result) { //>>includeStart('debug', pragmas.debug); Check.defined('start', start); Check.defined('heading', heading); @@ -319,22 +319,22 @@ define([ return result; } - var rhumb = new EllipsoidRhumb(undefined, undefined, e); - initiailize(rhumb, start, undefined, e); - rhumb._heading = CesiumMath.negativePiToPi(heading); - rhumb._distance = distance; - rhumb._end = rhumb.interpolateUsingSurfaceDistance(distance, new Cartographic()); + var rhumbLine = new EllipsoidRhumbLine(undefined, undefined, e); + initiailize(rhumbLine, start, undefined, e); + rhumbLine._heading = CesiumMath.negativePiToPi(heading); + rhumbLine._distance = distance; + rhumbLine._end = rhumbLine.interpolateUsingSurfaceDistance(distance, new Cartographic()); - return rhumb; + return rhumbLine; }; /** - * Sets the start and end points of the geodesic + * Sets the start and end points of the rhumb line. * * @param {Cartographic} start The initial planetodetic point on the path. * @param {Cartographic} end The final planetodetic point on the path. */ - EllipsoidRhumb.prototype.setEndPoints = function(start, end) { + EllipsoidRhumbLine.prototype.setEndPoints = function(start, end) { //>>includeStart('debug', pragmas.debug); Check.defined('start', start); Check.defined('end', end); @@ -350,7 +350,7 @@ define([ * @param {Cartographic} result The object in which to store the result. * @returns {Cartographic} The location of the point along the rhumb line. */ - EllipsoidRhumb.prototype.interpolateUsingFraction = function(fraction, result) { + EllipsoidRhumbLine.prototype.interpolateUsingFraction = function(fraction, result) { //>>includeStart('debug', pragmas.debug); Check.typeOf.number('distance', this._distance); //>>includeEnd('debug'); @@ -361,13 +361,13 @@ define([ /** * Provides the location of a point at the indicated distance along the rhumb line. * - * @param {Number} distance The distance from the inital point to the point of interest along the rhumb. + * @param {Number} distance The distance from the inital point to the point of interest along the rhumbLine. * @param {Cartographic} result The object in which to store the result. - * @returns {Cartographic} The location of the point along the geodesic. + * @returns {Cartographic} The location of the point along the rhumb line. * * @exception {DeveloperError} start and end must be set before calling function interpolateUsingSurfaceDistance */ - EllipsoidRhumb.prototype.interpolateUsingSurfaceDistance = function(distance, result) { + EllipsoidRhumbLine.prototype.interpolateUsingSurfaceDistance = function(distance, result) { //>>includeStart('debug', pragmas.debug); Check.defined('distance', this._distance); //>>includeEnd('debug'); @@ -427,5 +427,5 @@ define([ return new Cartographic(longitude, latitude, 0); }; - return EllipsoidRhumb; + return EllipsoidRhumbLine; }); diff --git a/Specs/Core/EllipsoidRhumbSpec.js b/Specs/Core/EllipsoidRhumbLineSpec.js similarity index 79% rename from Specs/Core/EllipsoidRhumbSpec.js rename to Specs/Core/EllipsoidRhumbLineSpec.js index b9d74fee5b91..cf44cc81a5a3 100644 --- a/Specs/Core/EllipsoidRhumbSpec.js +++ b/Specs/Core/EllipsoidRhumbLineSpec.js @@ -1,11 +1,11 @@ defineSuite([ - 'Core/EllipsoidRhumb', + 'Core/EllipsoidRhumbLine', 'Core/Cartographic', 'Core/Ellipsoid', 'Core/EllipsoidGeodesic', 'Core/Math' ], function( - EllipsoidRhumb, + EllipsoidRhumbLine, Cartographic, Ellipsoid, EllipsoidGeodesic, @@ -14,28 +14,28 @@ defineSuite([ it('throws without start', function() { expect(function() { - var rhumb = new EllipsoidRhumb(); + var rhumb = new EllipsoidRhumbLine(); return rhumb.interpolateUsingSurfaceDistance(0); }).toThrowDeveloperError(); }); it('throws without end', function() { expect(function() { - var rhumb = new EllipsoidRhumb(new Cartographic(Math.PI, Math.PI)); + var rhumb = new EllipsoidRhumbLine(new Cartographic(Math.PI, Math.PI)); return rhumb.interpolateUsingSurfaceDistance(0); }).toThrowDeveloperError(); }); it('throws without unique position', function() { expect(function() { - var rhumb = new EllipsoidRhumb(new Cartographic(Math.PI, Math.PI), new Cartographic(0, Math.PI)); + var rhumb = new EllipsoidRhumbLine(new Cartographic(Math.PI, Math.PI), new Cartographic(0, Math.PI)); return rhumb.interpolateUsingSurfaceDistance(0); }).toThrowDeveloperError(); }); it('setEndPoints throws without start', function() { expect(function() { - var rhumb = new EllipsoidRhumb(); + var rhumb = new EllipsoidRhumbLine(); rhumb.setEndPoints(); }).toThrowDeveloperError(); }); @@ -43,7 +43,7 @@ defineSuite([ it('setEndPoints throws without end', function() { expect(function() { var start = new Cartographic(CesiumMath.PI_OVER_TWO, 0); - var rhumb = new EllipsoidRhumb(); + var rhumb = new EllipsoidRhumbLine(); rhumb.setEndPoints(start); return rhumb.interpolateUsingSurfaceDistance(0); }).toThrowDeveloperError(); @@ -55,13 +55,13 @@ defineSuite([ var thirtyDegrees = Math.PI / 6; var end = new Cartographic(thirtyDegrees, thirtyDegrees); - var rhumb = EllipsoidRhumb.fromStartAndEnd(start, end); + var rhumb = EllipsoidRhumbLine.fromStartAndEnd(start, end); expect(start).toEqual(rhumb.start); expect(end).toEqual(rhumb.end); }); it('can create using fromStartAndEnd function with result', function() { - var scratch = new EllipsoidRhumb(); + var scratch = new EllipsoidRhumbLine(); var ellipsoid = new Ellipsoid(6, 6, 3); var fifteenDegrees = Math.PI / 12; @@ -69,7 +69,7 @@ defineSuite([ var thirtyDegrees = Math.PI / 6; var end = new Cartographic(thirtyDegrees, thirtyDegrees); - var rhumb = EllipsoidRhumb.fromStartAndEnd(start, end, ellipsoid, scratch); + var rhumb = EllipsoidRhumbLine.fromStartAndEnd(start, end, ellipsoid, scratch); expect(rhumb).toBe(scratch); expect(rhumb.ellipsoid).toBe(ellipsoid); expect(start).toEqual(rhumb.start); @@ -83,14 +83,14 @@ defineSuite([ var heading = fifteenDegrees; var distance = 6 * fifteenDegrees; - var rhumb = EllipsoidRhumb.fromStartHeadingDistance(start, heading, distance, ellipsoid); + var rhumb = EllipsoidRhumbLine.fromStartHeadingDistance(start, heading, distance, ellipsoid); expect(start).toEqual(rhumb.start); expect(distance).toEqualEpsilon(rhumb.surfaceDistance, CesiumMath.EPSILON12); expect(heading).toEqualEpsilon(rhumb.heading, CesiumMath.EPSILON12); }); it('can create using fromStartHeadingDistance function with result', function() { - var scratch = new EllipsoidRhumb(); + var scratch = new EllipsoidRhumbLine(); var ellipsoid = new Ellipsoid(6, 6, 3); var fifteenDegrees = Math.PI / 12; @@ -98,7 +98,7 @@ defineSuite([ var heading = fifteenDegrees; var distance = 6 * fifteenDegrees; - var rhumb = EllipsoidRhumb.fromStartHeadingDistance(start, heading, distance, ellipsoid, scratch); + var rhumb = EllipsoidRhumbLine.fromStartHeadingDistance(start, heading, distance, ellipsoid, scratch); expect(rhumb).toBe(scratch); expect(rhumb.ellipsoid).toBe(ellipsoid); expect(start).toEqual(rhumb.start); @@ -108,14 +108,14 @@ defineSuite([ it('getSurfaceDistance throws if start or end never defined', function() { expect(function() { - var rhumb = new EllipsoidRhumb(); + var rhumb = new EllipsoidRhumbLine(); return rhumb.surfaceDistance; }).toThrowDeveloperError(); }); it('getHeading throws if start or end never defined', function() { expect(function() { - var rhumb = new EllipsoidRhumb(); + var rhumb = new EllipsoidRhumbLine(); return rhumb.heading; }).toThrowDeveloperError(); }); @@ -126,7 +126,7 @@ defineSuite([ var thirtyDegrees = Math.PI / 6; var end = new Cartographic(thirtyDegrees, thirtyDegrees); - var rhumb = new EllipsoidRhumb(start, end); + var rhumb = new EllipsoidRhumbLine(start, end); expect(start).toEqual(rhumb.start); expect(end).toEqual(rhumb.end); }); @@ -134,7 +134,7 @@ defineSuite([ it('sets end points', function() { var start = new Cartographic(CesiumMath.PI_OVER_TWO, 0); var end = new Cartographic(CesiumMath.PI_OVER_TWO, CesiumMath.PI_OVER_TWO); - var rhumb = new EllipsoidRhumb(); + var rhumb = new EllipsoidRhumbLine(); rhumb.setEndPoints(start, end); expect(start).toEqual(rhumb.start); expect(end).toEqual(rhumb.end); @@ -145,7 +145,7 @@ defineSuite([ var start = new Cartographic(CesiumMath.PI_OVER_TWO, 0); var end = new Cartographic(Math.PI, 0); - var rhumb = new EllipsoidRhumb(start, end, ellipsoid); + var rhumb = new EllipsoidRhumbLine(start, end, ellipsoid); expect(CesiumMath.PI_OVER_TWO).toEqualEpsilon(rhumb.heading, CesiumMath.EPSILON12); }); @@ -154,7 +154,7 @@ defineSuite([ var start = new Cartographic(0, 1.2); var end = new Cartographic(Math.PI, 1.5); - var rhumb = new EllipsoidRhumb(start, end, ellipsoid); + var rhumb = new EllipsoidRhumbLine(start, end, ellipsoid); expect(0.0).not.toEqual(rhumb.heading); }); @@ -164,7 +164,7 @@ defineSuite([ var start = new Cartographic(1.3, CesiumMath.PI_OVER_TWO); var end = new Cartographic(0.0, CesiumMath.PI / 2.4); - var rhumb = new EllipsoidRhumb(start, end, ellipsoid); + var rhumb = new EllipsoidRhumbLine(start, end, ellipsoid); expect(0.0).not.toEqual(rhumb.heading); }); @@ -174,13 +174,13 @@ defineSuite([ var start = new Cartographic(0.0, 0.3); var end = new Cartographic(CesiumMath.PI_OVER_TWO, 0.3); - var rhumb = new EllipsoidRhumb(start, end, ellipsoid); + var rhumb = new EllipsoidRhumbLine(start, end, ellipsoid); expect(CesiumMath.PI_OVER_TWO).toEqualEpsilon(rhumb.heading, CesiumMath.EPSILON12); start = new Cartographic(3 * CesiumMath.PI_OVER_TWO, 0.3); end = new Cartographic(CesiumMath.PI, 0.3); - var rhumb2 = new EllipsoidRhumb(start, end, ellipsoid); + var rhumb2 = new EllipsoidRhumbLine(start, end, ellipsoid); expect(-CesiumMath.PI_OVER_TWO).toEqualEpsilon(rhumb2.heading, CesiumMath.EPSILON12); }); @@ -189,10 +189,10 @@ defineSuite([ var start = new Cartographic(0.0, 1.2); var end = new Cartographic(0.0, 1.5); - var rhumb = new EllipsoidRhumb(start, end, ellipsoid); + var rhumb = new EllipsoidRhumbLine(start, end, ellipsoid); expect(0.0).toEqualEpsilon(rhumb.heading, CesiumMath.EPSILON12); - var rhumb2 = new EllipsoidRhumb(end, start, ellipsoid); + var rhumb2 = new EllipsoidRhumbLine(end, start, ellipsoid); expect(CesiumMath.PI).toEqualEpsilon(rhumb2.heading, CesiumMath.EPSILON12); }); @@ -201,7 +201,7 @@ defineSuite([ var start = new Cartographic(CesiumMath.PI_OVER_TWO, 0.0); var end = new Cartographic(CesiumMath.PI_OVER_TWO, 0.0); - var rhumb = new EllipsoidRhumb(start, end, ellipsoid); + var rhumb = new EllipsoidRhumbLine(start, end, ellipsoid); expect(0).toEqualEpsilon(rhumb.surfaceDistance, CesiumMath.EPSILON12); expect(0).toEqualEpsilon(rhumb.heading, CesiumMath.EPSILON12); }); @@ -211,7 +211,7 @@ defineSuite([ var start = new Cartographic(-CesiumMath.PI_OVER_FOUR, 0.0); var end = new Cartographic(CesiumMath.PI_OVER_FOUR, 0.0); - var rhumb = new EllipsoidRhumb(start, end, ellipsoid); + var rhumb = new EllipsoidRhumbLine(start, end, ellipsoid); expect(CesiumMath.PI_OVER_TWO * 6).toEqualEpsilon(rhumb.surfaceDistance, CesiumMath.EPSILON12); }); @@ -222,7 +222,7 @@ defineSuite([ var fortyfiveDegrees = Math.PI / 4; var end = new Cartographic(CesiumMath.PI_OVER_TWO, fortyfiveDegrees); - var rhumb = new EllipsoidRhumb(start, end, ellipsoid); + var rhumb = new EllipsoidRhumbLine(start, end, ellipsoid); var thirtyDegrees = Math.PI / 6; expect(thirtyDegrees * 6).toEqualEpsilon(rhumb.surfaceDistance, CesiumMath.EPSILON12); }); @@ -234,10 +234,10 @@ defineSuite([ var fortyFiveWest = new Cartographic(-CesiumMath.PI_OVER_FOUR, 0.0); var fortyFiveEast = new Cartographic(CesiumMath.PI_OVER_FOUR, 0.0); - var westEastRhumb = new EllipsoidRhumb(fortyFiveWest, fortyFiveEast, ellipsoid); - var southNorthRhumb = new EllipsoidRhumb(fortyFiveSouth, fortyFiveNorth, ellipsoid); - var eastWestRhumb = new EllipsoidRhumb(fortyFiveEast, fortyFiveWest, ellipsoid); - var northSouthRhumb = new EllipsoidRhumb(fortyFiveNorth, fortyFiveSouth, ellipsoid); + var westEastRhumb = new EllipsoidRhumbLine(fortyFiveWest, fortyFiveEast, ellipsoid); + var southNorthRhumb = new EllipsoidRhumbLine(fortyFiveSouth, fortyFiveNorth, ellipsoid); + var eastWestRhumb = new EllipsoidRhumbLine(fortyFiveEast, fortyFiveWest, ellipsoid); + var northSouthRhumb = new EllipsoidRhumbLine(fortyFiveNorth, fortyFiveSouth, ellipsoid); expect(CesiumMath.PI_OVER_TWO * 6).toEqualEpsilon(westEastRhumb.surfaceDistance, CesiumMath.EPSILON12); expect(CesiumMath.PI_OVER_TWO * 6).toEqualEpsilon(southNorthRhumb.surfaceDistance, CesiumMath.EPSILON12); expect(westEastRhumb.surfaceDistance).toEqualEpsilon(southNorthRhumb.surfaceDistance, CesiumMath.EPSILON12); @@ -253,7 +253,7 @@ defineSuite([ var start = new Cartographic(0, -fortyfiveDegrees); var end = new Cartographic(CesiumMath.PI_OVER_TWO, -fortyfiveDegrees); - var rhumb = new EllipsoidRhumb(start, end, ellipsoid); + var rhumb = new EllipsoidRhumbLine(start, end, ellipsoid); var distance = Math.cos(fortyfiveDegrees) * CesiumMath.PI_OVER_TWO * 6; expect(distance).toEqualEpsilon(rhumb.surfaceDistance, CesiumMath.EPSILON12); }); @@ -265,8 +265,8 @@ defineSuite([ var initial = new Cartographic(fifteenDegrees, fifteenDegrees); var distance = radius * fifteenDegrees; - var rhumb1 = EllipsoidRhumb.fromStartHeadingDistance(initial, fifteenDegrees, distance, ellipsoid); - var rhumb2 = new EllipsoidRhumb(initial, rhumb1.end, ellipsoid); + var rhumb1 = EllipsoidRhumbLine.fromStartHeadingDistance(initial, fifteenDegrees, distance, ellipsoid); + var rhumb2 = new EllipsoidRhumbLine(initial, rhumb1.end, ellipsoid); expect(fifteenDegrees).toEqualEpsilon(rhumb2.heading, CesiumMath.EPSILON12); expect(distance).toEqualEpsilon(rhumb2.surfaceDistance, CesiumMath.EPSILON6); @@ -278,8 +278,8 @@ defineSuite([ var initial = new Cartographic(fifteenDegrees, fifteenDegrees); var distance = ellipsoid.maximumRadius * fifteenDegrees; - var rhumb1 = EllipsoidRhumb.fromStartHeadingDistance(initial, fifteenDegrees, distance, ellipsoid); - var rhumb2 = new EllipsoidRhumb(initial, rhumb1.end, ellipsoid); + var rhumb1 = EllipsoidRhumbLine.fromStartHeadingDistance(initial, fifteenDegrees, distance, ellipsoid); + var rhumb2 = new EllipsoidRhumbLine(initial, rhumb1.end, ellipsoid); expect(fifteenDegrees).toEqualEpsilon(rhumb2.heading, CesiumMath.EPSILON12); expect(distance).toEqualEpsilon(rhumb2.surfaceDistance, CesiumMath.EPSILON6); @@ -299,28 +299,28 @@ defineSuite([ var ninetyPointOneDegrees = 90.1 * oneDegree; var ninetyPointZeroTwoDegrees = 90.02 * oneDegree; - var rhumb1 = EllipsoidRhumb.fromStartHeadingDistance(initial, eightyNineDegrees, distance, ellipsoid); - var rhumb2 = new EllipsoidRhumb(initial, rhumb1.end, ellipsoid); + var rhumb1 = EllipsoidRhumbLine.fromStartHeadingDistance(initial, eightyNineDegrees, distance, ellipsoid); + var rhumb2 = new EllipsoidRhumbLine(initial, rhumb1.end, ellipsoid); expect(rhumb1.heading).toEqualEpsilon(rhumb2.heading, CesiumMath.EPSILON12); expect(rhumb1.surfaceDistance).toEqualEpsilon(rhumb2.surfaceDistance, CesiumMath.EPSILON6); - rhumb1 = EllipsoidRhumb.fromStartHeadingDistance(initial, eightyNinePointNineDegrees, distance, ellipsoid); - rhumb2 = new EllipsoidRhumb(initial, rhumb1.end, ellipsoid); + rhumb1 = EllipsoidRhumbLine.fromStartHeadingDistance(initial, eightyNinePointNineDegrees, distance, ellipsoid); + rhumb2 = new EllipsoidRhumbLine(initial, rhumb1.end, ellipsoid); expect(rhumb1.heading).toEqualEpsilon(rhumb2.heading, CesiumMath.EPSILON12); expect(rhumb1.surfaceDistance).toEqualEpsilon(rhumb2.surfaceDistance, CesiumMath.EPSILON6); - rhumb1 = EllipsoidRhumb.fromStartHeadingDistance(initial, ninetyDegrees, distance, ellipsoid); - rhumb2 = new EllipsoidRhumb(initial, rhumb1.end, ellipsoid); + rhumb1 = EllipsoidRhumbLine.fromStartHeadingDistance(initial, ninetyDegrees, distance, ellipsoid); + rhumb2 = new EllipsoidRhumbLine(initial, rhumb1.end, ellipsoid); expect(rhumb1.heading).toEqualEpsilon(rhumb2.heading, CesiumMath.EPSILON12); expect(rhumb1.surfaceDistance).toEqualEpsilon(rhumb2.surfaceDistance, CesiumMath.EPSILON6); - rhumb1 = EllipsoidRhumb.fromStartHeadingDistance(initial, ninetyPointOneDegrees, distance, ellipsoid); - rhumb2 = new EllipsoidRhumb(initial, rhumb1.end, ellipsoid); + rhumb1 = EllipsoidRhumbLine.fromStartHeadingDistance(initial, ninetyPointOneDegrees, distance, ellipsoid); + rhumb2 = new EllipsoidRhumbLine(initial, rhumb1.end, ellipsoid); expect(rhumb1.heading).toEqualEpsilon(rhumb2.heading, CesiumMath.EPSILON12); expect(rhumb1.surfaceDistance).toEqualEpsilon(rhumb2.surfaceDistance, CesiumMath.EPSILON6); - rhumb1 = EllipsoidRhumb.fromStartHeadingDistance(initial, ninetyPointZeroTwoDegrees, distance, ellipsoid); - rhumb2 = new EllipsoidRhumb(initial, rhumb1.end, ellipsoid); + rhumb1 = EllipsoidRhumbLine.fromStartHeadingDistance(initial, ninetyPointZeroTwoDegrees, distance, ellipsoid); + rhumb2 = new EllipsoidRhumbLine(initial, rhumb1.end, ellipsoid); expect(rhumb1.heading).toEqualEpsilon(rhumb2.heading, CesiumMath.EPSILON12); expect(rhumb1.surfaceDistance).toEqualEpsilon(rhumb2.surfaceDistance, CesiumMath.EPSILON6); }); @@ -338,28 +338,28 @@ defineSuite([ var ninetyPointOneDegrees = 90.1 * oneDegree; var ninetyPointZeroTwoDegrees = 90.02 * oneDegree; - var rhumb1 = EllipsoidRhumb.fromStartHeadingDistance(initial, eightyNineDegrees, distance, ellipsoid); - var rhumb2 = new EllipsoidRhumb(initial, rhumb1.end, ellipsoid); + var rhumb1 = EllipsoidRhumbLine.fromStartHeadingDistance(initial, eightyNineDegrees, distance, ellipsoid); + var rhumb2 = new EllipsoidRhumbLine(initial, rhumb1.end, ellipsoid); expect(rhumb1.heading).toEqualEpsilon(rhumb2.heading, CesiumMath.EPSILON12); expect(rhumb1.surfaceDistance).toEqualEpsilon(rhumb2.surfaceDistance, CesiumMath.EPSILON3); - rhumb1 = EllipsoidRhumb.fromStartHeadingDistance(initial, eightyNinePointNineDegrees, distance, ellipsoid); - rhumb2 = new EllipsoidRhumb(initial, rhumb1.end, ellipsoid); + rhumb1 = EllipsoidRhumbLine.fromStartHeadingDistance(initial, eightyNinePointNineDegrees, distance, ellipsoid); + rhumb2 = new EllipsoidRhumbLine(initial, rhumb1.end, ellipsoid); expect(rhumb1.heading).toEqualEpsilon(rhumb2.heading, CesiumMath.EPSILON12); expect(rhumb1.surfaceDistance).toEqualEpsilon(rhumb2.surfaceDistance, CesiumMath.EPSILON3); - rhumb1 = EllipsoidRhumb.fromStartHeadingDistance(initial, ninetyDegrees, distance, ellipsoid); - rhumb2 = new EllipsoidRhumb(initial, rhumb1.end, ellipsoid); + rhumb1 = EllipsoidRhumbLine.fromStartHeadingDistance(initial, ninetyDegrees, distance, ellipsoid); + rhumb2 = new EllipsoidRhumbLine(initial, rhumb1.end, ellipsoid); expect(rhumb1.heading).toEqualEpsilon(rhumb2.heading, CesiumMath.EPSILON12); expect(rhumb1.surfaceDistance).toEqualEpsilon(rhumb2.surfaceDistance, CesiumMath.EPSILON3); - rhumb1 = EllipsoidRhumb.fromStartHeadingDistance(initial, ninetyPointOneDegrees, distance, ellipsoid); - rhumb2 = new EllipsoidRhumb(initial, rhumb1.end, ellipsoid); + rhumb1 = EllipsoidRhumbLine.fromStartHeadingDistance(initial, ninetyPointOneDegrees, distance, ellipsoid); + rhumb2 = new EllipsoidRhumbLine(initial, rhumb1.end, ellipsoid); expect(rhumb1.heading).toEqualEpsilon(rhumb2.heading, CesiumMath.EPSILON12); expect(rhumb1.surfaceDistance).toEqualEpsilon(rhumb2.surfaceDistance, CesiumMath.EPSILON3); - rhumb1 = EllipsoidRhumb.fromStartHeadingDistance(initial, ninetyPointZeroTwoDegrees, distance, ellipsoid); - rhumb2 = new EllipsoidRhumb(initial, rhumb1.end, ellipsoid); + rhumb1 = EllipsoidRhumbLine.fromStartHeadingDistance(initial, ninetyPointZeroTwoDegrees, distance, ellipsoid); + rhumb2 = new EllipsoidRhumbLine(initial, rhumb1.end, ellipsoid); expect(rhumb1.heading).toEqualEpsilon(rhumb2.heading, CesiumMath.EPSILON12); expect(rhumb1.surfaceDistance).toEqualEpsilon(rhumb2.surfaceDistance, CesiumMath.EPSILON3); }); @@ -371,8 +371,8 @@ defineSuite([ var final = new Cartographic(fifteenDegrees, 0.0); var distance = ellipsoid.maximumRadius * 2 * fifteenDegrees; - var rhumb1 = new EllipsoidRhumb(initial, final, ellipsoid); - var rhumb2 = EllipsoidRhumb.fromStartHeadingDistance(initial, CesiumMath.PI_OVER_TWO, distance, ellipsoid); + var rhumb1 = new EllipsoidRhumbLine(initial, final, ellipsoid); + var rhumb2 = EllipsoidRhumbLine.fromStartHeadingDistance(initial, CesiumMath.PI_OVER_TWO, distance, ellipsoid); expect(rhumb1.heading).toEqualEpsilon(rhumb2.heading, CesiumMath.EPSILON12); expect(rhumb1.surfaceDistance).toEqualEpsilon(rhumb2.surfaceDistance, CesiumMath.EPSILON6); @@ -386,16 +386,16 @@ defineSuite([ var distance = ellipsoid.maximumRadius * 2 * fifteenDegrees; - var rhumb1 = new EllipsoidRhumb(initial, final, ellipsoid); - var rhumb2 = new EllipsoidRhumb.fromStartHeadingDistance(initial, 3.0 * CesiumMath.PI_OVER_TWO, distance, ellipsoid); + var rhumb1 = new EllipsoidRhumbLine(initial, final, ellipsoid); + var rhumb2 = new EllipsoidRhumbLine.fromStartHeadingDistance(initial, 3.0 * CesiumMath.PI_OVER_TWO, distance, ellipsoid); expect(-CesiumMath.PI_OVER_TWO).toEqualEpsilon(rhumb1.heading, CesiumMath.EPSILON12); expect(CesiumMath.PI / 6 * ellipsoid.maximumRadius).toEqualEpsilon(rhumb1.surfaceDistance, CesiumMath.EPSILON6); expect(rhumb1.heading).toEqualEpsilon(rhumb2.heading, CesiumMath.EPSILON12); expect(rhumb1.surfaceDistance).toEqualEpsilon(rhumb2.surfaceDistance, CesiumMath.EPSILON6); - var rhumb3 = new EllipsoidRhumb(final, initial, ellipsoid); - var rhumb4 = new EllipsoidRhumb.fromStartHeadingDistance(final, CesiumMath.PI_OVER_TWO, distance, ellipsoid); + var rhumb3 = new EllipsoidRhumbLine(final, initial, ellipsoid); + var rhumb4 = new EllipsoidRhumbLine.fromStartHeadingDistance(final, CesiumMath.PI_OVER_TWO, distance, ellipsoid); expect(CesiumMath.PI_OVER_TWO).toEqualEpsilon(rhumb3.heading, CesiumMath.EPSILON12); expect(CesiumMath.PI / 6 * ellipsoid.maximumRadius).toEqualEpsilon(rhumb3.surfaceDistance, CesiumMath.EPSILON6); expect(rhumb3.heading).toEqualEpsilon(rhumb4.heading, CesiumMath.EPSILON12); @@ -410,7 +410,7 @@ defineSuite([ var final = new Cartographic(fifteenDegrees, oneDegree); //A rhumb line with heading = 0 should be almost the same as a geodesic - var rhumb = new EllipsoidRhumb(initial, final, ellipsoid); + var rhumb = new EllipsoidRhumbLine(initial, final, ellipsoid); var geodesic = new EllipsoidGeodesic(initial, final, ellipsoid); expect(0.0).toEqualEpsilon(rhumb.heading, CesiumMath.EPSILON12); expect(geodesic.startHeading).toEqualEpsilon(rhumb.heading, CesiumMath.EPSILON12); @@ -422,7 +422,7 @@ defineSuite([ var final = new Cartographic(CesiumMath.PI - 1, 0.0); //A rhumb line on the equator should be the same as a geodesic - var rhumb = new EllipsoidRhumb(initial, final, ellipsoid); + var rhumb = new EllipsoidRhumbLine(initial, final, ellipsoid); var geodesic = new EllipsoidGeodesic(initial, final, ellipsoid); expect(CesiumMath.PI_OVER_TWO).toEqualEpsilon(rhumb.heading, CesiumMath.EPSILON12); expect(geodesic.startHeading).toEqualEpsilon(rhumb.heading, CesiumMath.EPSILON12); @@ -439,8 +439,8 @@ defineSuite([ var initial = new Cartographic(0.0, eightyDegrees); - var rhumb1 = EllipsoidRhumb.fromStartHeadingDistance(initial, eightyDegrees, distance, ellipsoid); - var rhumb2 = new EllipsoidRhumb(initial, rhumb1.end, ellipsoid); + var rhumb1 = EllipsoidRhumbLine.fromStartHeadingDistance(initial, eightyDegrees, distance, ellipsoid); + var rhumb2 = new EllipsoidRhumbLine(initial, rhumb1.end, ellipsoid); expect(rhumb1.heading).toEqualEpsilon(rhumb2.heading, CesiumMath.EPSILON12); expect(rhumb1.surfaceDistance).toEqualEpsilon(rhumb2.surfaceDistance, CesiumMath.EPSILON6); @@ -452,7 +452,7 @@ defineSuite([ var final = new Cartographic(CesiumMath.PI_OVER_TWO, 0.0); var halfway = new Cartographic(CesiumMath.PI_OVER_FOUR, 0.0); - var rhumb = new EllipsoidRhumb(initial, final, ellipsoid); + var rhumb = new EllipsoidRhumbLine(initial, final, ellipsoid); var interpolatedPoint = rhumb.interpolateUsingFraction(0.5); expect(halfway.longitude).toEqualEpsilon(interpolatedPoint.longitude, CesiumMath.EPSILON12); @@ -467,7 +467,7 @@ defineSuite([ var distance = ellipsoid.maximumRadius * CesiumMath.PI_OVER_FOUR; - var rhumb = new EllipsoidRhumb(initial, final, ellipsoid); + var rhumb = new EllipsoidRhumbLine(initial, final, ellipsoid); var interpolatedPoint = rhumb.interpolateUsingSurfaceDistance(distance); expect(halfway.longitude).toEqualEpsilon(interpolatedPoint.longitude, CesiumMath.EPSILON12); @@ -480,7 +480,7 @@ defineSuite([ var thirtyDegrees = Math.PI / 6; var end = new Cartographic(thirtyDegrees, thirtyDegrees); - var rhumb = new EllipsoidRhumb(start, end); + var rhumb = new EllipsoidRhumbLine(start, end); var distance = rhumb.surfaceDistance; var first = rhumb.interpolateUsingSurfaceDistance(0.0); @@ -500,7 +500,7 @@ defineSuite([ var thirtyDegrees = Math.PI / 6; var expectedMid = new Cartographic(thirtyDegrees, 0.0); - var rhumb = new EllipsoidRhumb(start, end); + var rhumb = new EllipsoidRhumbLine(start, end); var distance = Ellipsoid.WGS84.radii.x * fifteenDegrees; var midpoint = rhumb.interpolateUsingSurfaceDistance(distance); @@ -515,7 +515,7 @@ defineSuite([ var thirtyDegrees = Math.PI / 6; var end = new Cartographic(thirtyDegrees, thirtyDegrees); - var rhumb = new EllipsoidRhumb(start, end); + var rhumb = new EllipsoidRhumbLine(start, end); var first = rhumb.interpolateUsingFraction(0); var last = rhumb.interpolateUsingFraction(1); @@ -534,7 +534,7 @@ defineSuite([ var thirtyDegrees = Math.PI / 6; var expectedMid = new Cartographic(thirtyDegrees, 0.0); - var rhumb = new EllipsoidRhumb(start, end); + var rhumb = new EllipsoidRhumbLine(start, end); var midpoint = rhumb.interpolateUsingFraction(0.5); @@ -550,7 +550,7 @@ defineSuite([ var thirtyDegrees = Math.PI / 6; var expectedMid = new Cartographic(thirtyDegrees, 0.0); - var rhumb = new EllipsoidRhumb(start, end); + var rhumb = new EllipsoidRhumbLine(start, end); var result = new Cartographic(); var midpoint = rhumb.interpolateUsingFraction(0.5, result); expect(result).toBe(midpoint); @@ -567,7 +567,7 @@ defineSuite([ var thirtyDegrees = Math.PI / 6; var expectedMid = new Cartographic(thirtyDegrees, 0.0); - var rhumb = new EllipsoidRhumb(start, end); + var rhumb = new EllipsoidRhumbLine(start, end); var distance = Ellipsoid.WGS84.radii.x * fifteenDegrees; var result = new Cartographic(); From e64e98851e950d43f0ab929bffcd2a4bc9ffc45a Mon Sep 17 00:00:00 2001 From: Shehzan Mohammed Date: Tue, 15 Jan 2019 13:22:39 -0500 Subject: [PATCH 04/11] Fixes from PR comments --- Source/Core/EllipsoidRhumbLine.js | 47 ++++++++++----------------- Specs/Core/EllipsoidRhumbLineSpec.js | 48 ++++------------------------ 2 files changed, 23 insertions(+), 72 deletions(-) diff --git a/Source/Core/EllipsoidRhumbLine.js b/Source/Core/EllipsoidRhumbLine.js index fd6546120249..71682b0e1a93 100644 --- a/Source/Core/EllipsoidRhumbLine.js +++ b/Source/Core/EllipsoidRhumbLine.js @@ -98,17 +98,13 @@ define([ return Math.log(Math.tan(0.5 * (CesiumMath.PI_OVER_TWO + latitude))) - (ellipticity / 2.0 * Math.log((1 + eSinL) / (1 - eSinL))); } - function calculateHeading(ellipsoidRhumbLine, major, minor, firstLongitude, firstLatitude, secondLongitude, secondLatitude) { + function calculateHeading(ellipsoidRhumbLine, firstLongitude, firstLatitude, secondLongitude, secondLatitude) { var sigma1 = calculateSigma(ellipsoidRhumbLine._ellipticity, firstLatitude); var sigma2 = calculateSigma(ellipsoidRhumbLine._ellipticity, secondLatitude); return Math.atan2(CesiumMath.negativePiToPi(secondLongitude - firstLongitude), sigma2 - sigma1); } function calculateArcLength(ellipsoidRhumbLine, major, minor, firstLongitude, firstLatitude, secondLongitude, secondLatitude) { - //>>includeStart('debug', pragmas.debug); - Check.defined('heading', ellipsoidRhumbLine._heading); - //>>includeEnd('debug'); - var heading = ellipsoidRhumbLine._heading; var deltaLongitude = secondLongitude - firstLongitude; @@ -135,7 +131,7 @@ define([ var scratchCart1 = new Cartesian3(); var scratchCart2 = new Cartesian3(); - function initiailize(ellipsoidRhumbLine, start, end, ellipsoid) { + function initialize(ellipsoidRhumbLine, start, end, ellipsoid) { var major = ellipsoid.maximumRadius; var minor = ellipsoid.minimumRadius; var majorSquared = major * major; @@ -161,10 +157,9 @@ define([ Check.typeOf.number.greaterThanOrEquals('value', Math.abs(Math.abs(Cartesian3.angleBetween(firstCartesian, lastCartesian)) - Math.PI), 0.0125); //>>includeEnd('debug'); - initiailize(ellipsoidRhumbLine, start, end, ellipsoid); + initialize(ellipsoidRhumbLine, start, end, ellipsoid); - ellipsoidRhumbLine._heading = calculateHeading(ellipsoidRhumbLine, ellipsoid.maximumRadius, ellipsoid.minimumRadius, - start.longitude, start.latitude, end.longitude, end.latitude); + ellipsoidRhumbLine._heading = calculateHeading(ellipsoidRhumbLine, start.longitude, start.latitude, end.longitude, end.latitude); ellipsoidRhumbLine._distance = calculateArcLength(ellipsoidRhumbLine, ellipsoid.maximumRadius, ellipsoid.minimumRadius, start.longitude, start.latitude, end.longitude, end.latitude); } @@ -271,7 +266,7 @@ define([ * @param {Cartographic} start The initial planetodetic point on the path. * @param {Cartographic} end The final planetodetic point on the path. * @param {Ellipsoid} [ellipsoid=Ellipsoid.WGS84] The ellipsoid on which the rhumb line lies. - * @param {EllipsoidRhumbLine} [result] The object in whice to store the result. + * @param {EllipsoidRhumbLine} [result] The object in which to store the result. * @returns {EllipsoidRhumbLine} The EllipsoidRhumbLine object. */ EllipsoidRhumbLine.fromStartAndEnd = function(start, end, ellipsoid, result) { @@ -309,23 +304,16 @@ define([ var e = defaultValue(ellipsoid, Ellipsoid.WGS84); - if (defined(result)) { - result._ellipsoid = e; - initiailize(result, start, undefined, e); - result._heading = CesiumMath.negativePiToPi(heading); - result._distance = distance; - - result._end = result.interpolateUsingSurfaceDistance(distance, new Cartographic()); - return result; + if (!defined(result)) { + result = new EllipsoidRhumbLine(undefined, undefined, e); } - var rhumbLine = new EllipsoidRhumbLine(undefined, undefined, e); - initiailize(rhumbLine, start, undefined, e); - rhumbLine._heading = CesiumMath.negativePiToPi(heading); - rhumbLine._distance = distance; - rhumbLine._end = rhumbLine.interpolateUsingSurfaceDistance(distance, new Cartographic()); + initialize(result, start, undefined, e); + result._heading = CesiumMath.negativePiToPi(heading); + result._distance = distance; - return rhumbLine; + result._end = result.interpolateUsingSurfaceDistance(distance, new Cartographic()); + return result; }; /** @@ -347,14 +335,10 @@ define([ * Provides the location of a point at the indicated portion along the rhumb line. * * @param {Number} fraction The portion of the distance between the initial and final points. - * @param {Cartographic} result The object in which to store the result. + * @param {Cartographic} [result] The object in which to store the result. * @returns {Cartographic} The location of the point along the rhumb line. */ EllipsoidRhumbLine.prototype.interpolateUsingFraction = function(fraction, result) { - //>>includeStart('debug', pragmas.debug); - Check.typeOf.number('distance', this._distance); - //>>includeEnd('debug'); - return this.interpolateUsingSurfaceDistance(fraction * this._distance, result); }; @@ -362,14 +346,15 @@ define([ * Provides the location of a point at the indicated distance along the rhumb line. * * @param {Number} distance The distance from the inital point to the point of interest along the rhumbLine. - * @param {Cartographic} result The object in which to store the result. + * @param {Cartographic} [result] The object in which to store the result. * @returns {Cartographic} The location of the point along the rhumb line. * * @exception {DeveloperError} start and end must be set before calling function interpolateUsingSurfaceDistance */ EllipsoidRhumbLine.prototype.interpolateUsingSurfaceDistance = function(distance, result) { //>>includeStart('debug', pragmas.debug); - Check.defined('distance', this._distance); + Check.defined('distance', distance); + Check.defined('this._distance', this._distance); //>>includeEnd('debug'); var ellipsoid = this._ellipsoid; diff --git a/Specs/Core/EllipsoidRhumbLineSpec.js b/Specs/Core/EllipsoidRhumbLineSpec.js index cf44cc81a5a3..03d2b6011fdb 100644 --- a/Specs/Core/EllipsoidRhumbLineSpec.js +++ b/Specs/Core/EllipsoidRhumbLineSpec.js @@ -12,6 +12,11 @@ defineSuite([ CesiumMath) { 'use strict'; + var oneDegree = CesiumMath.RADIANS_PER_DEGREE; + var fifteenDegrees = Math.PI / 12; + var thirtyDegrees = Math.PI / 6; + var fortyfiveDegrees = Math.PI / 4; + it('throws without start', function() { expect(function() { var rhumb = new EllipsoidRhumbLine(); @@ -50,9 +55,7 @@ defineSuite([ }); it('can create using fromStartAndEnd function', function() { - var fifteenDegrees = Math.PI / 12; var start = new Cartographic(fifteenDegrees, fifteenDegrees); - var thirtyDegrees = Math.PI / 6; var end = new Cartographic(thirtyDegrees, thirtyDegrees); var rhumb = EllipsoidRhumbLine.fromStartAndEnd(start, end); @@ -64,9 +67,7 @@ defineSuite([ var scratch = new EllipsoidRhumbLine(); var ellipsoid = new Ellipsoid(6, 6, 3); - var fifteenDegrees = Math.PI / 12; var start = new Cartographic(fifteenDegrees, fifteenDegrees); - var thirtyDegrees = Math.PI / 6; var end = new Cartographic(thirtyDegrees, thirtyDegrees); var rhumb = EllipsoidRhumbLine.fromStartAndEnd(start, end, ellipsoid, scratch); @@ -78,7 +79,6 @@ defineSuite([ it('can create using fromStartHeadingDistance function', function() { var ellipsoid = new Ellipsoid(6, 6, 3); - var fifteenDegrees = Math.PI / 12; var start = new Cartographic(fifteenDegrees, fifteenDegrees); var heading = fifteenDegrees; var distance = 6 * fifteenDegrees; @@ -90,10 +90,9 @@ defineSuite([ }); it('can create using fromStartHeadingDistance function with result', function() { - var scratch = new EllipsoidRhumbLine(); - var ellipsoid = new Ellipsoid(6, 6, 3); - var fifteenDegrees = Math.PI / 12; + var scratch = new EllipsoidRhumbLine(undefined, undefined, ellipsoid); + var start = new Cartographic(fifteenDegrees, fifteenDegrees); var heading = fifteenDegrees; var distance = 6 * fifteenDegrees; @@ -121,9 +120,7 @@ defineSuite([ }); it('works with two points', function() { - var fifteenDegrees = Math.PI / 12; var start = new Cartographic(fifteenDegrees, fifteenDegrees); - var thirtyDegrees = Math.PI / 6; var end = new Cartographic(thirtyDegrees, thirtyDegrees); var rhumb = new EllipsoidRhumbLine(start, end); @@ -217,13 +214,10 @@ defineSuite([ it('computes distance at meridian', function() { var ellipsoid = new Ellipsoid(6, 6, 6); - var fifteenDegrees = Math.PI / 12; var start = new Cartographic(CesiumMath.PI_OVER_TWO, fifteenDegrees); - var fortyfiveDegrees = Math.PI / 4; var end = new Cartographic(CesiumMath.PI_OVER_TWO, fortyfiveDegrees); var rhumb = new EllipsoidRhumbLine(start, end, ellipsoid); - var thirtyDegrees = Math.PI / 6; expect(thirtyDegrees * 6).toEqualEpsilon(rhumb.surfaceDistance, CesiumMath.EPSILON12); }); @@ -249,7 +243,6 @@ defineSuite([ it('computes distance at same latitude', function() { var ellipsoid = new Ellipsoid(6, 6, 6); - var fortyfiveDegrees = Math.PI / 4; var start = new Cartographic(0, -fortyfiveDegrees); var end = new Cartographic(CesiumMath.PI_OVER_TWO, -fortyfiveDegrees); @@ -261,7 +254,6 @@ defineSuite([ it('tests sphere', function() { var radius = 6378137.0; var ellipsoid = new Ellipsoid(radius, radius, radius); - var fifteenDegrees = Math.PI / 12; var initial = new Cartographic(fifteenDegrees, fifteenDegrees); var distance = radius * fifteenDegrees; @@ -274,7 +266,6 @@ defineSuite([ it('tests sphereoid', function() { var ellipsoid = Ellipsoid.WGS84; - var fifteenDegrees = Math.PI / 12; var initial = new Cartographic(fifteenDegrees, fifteenDegrees); var distance = ellipsoid.maximumRadius * fifteenDegrees; @@ -288,11 +279,9 @@ defineSuite([ it('tests sphere close to 90 degrees', function() { var radius = 6378137.0; var ellipsoid = new Ellipsoid(radius, radius, radius); - var fifteenDegrees = Math.PI / 12; var initial = new Cartographic(fifteenDegrees, fifteenDegrees); var distance = radius * fifteenDegrees; - var oneDegree = CesiumMath.RADIANS_PER_DEGREE; var eightyNineDegrees = 89 * oneDegree; var eightyNinePointNineDegrees = 89.9 * oneDegree; var ninetyDegrees = 90 * oneDegree; @@ -327,11 +316,9 @@ defineSuite([ it('tests spheroid close to 90 degrees', function() { var ellipsoid = Ellipsoid.WGS84; - var fifteenDegrees = Math.PI / 12; var initial = new Cartographic(fifteenDegrees, fifteenDegrees); var distance = ellipsoid.maximumRadius * fifteenDegrees; - var oneDegree = CesiumMath.RADIANS_PER_DEGREE; var eightyNineDegrees = 89 * oneDegree; var eightyNinePointNineDegrees = 89.9 * oneDegree; var ninetyDegrees = 90 * oneDegree; @@ -366,7 +353,6 @@ defineSuite([ it('test sphereoid across meridian', function() { var ellipsoid = Ellipsoid.WGS84; - var fifteenDegrees = Math.PI / 12; var initial = new Cartographic(-fifteenDegrees, 0.0); var final = new Cartographic(fifteenDegrees, 0.0); var distance = ellipsoid.maximumRadius * 2 * fifteenDegrees; @@ -380,7 +366,6 @@ defineSuite([ it('test across IDL with -PI to PI range of longitude', function() { var ellipsoid = Ellipsoid.WGS84; - var fifteenDegrees = Math.PI / 12.0; var initial = new Cartographic(-CesiumMath.PI + fifteenDegrees, 0.0); var final = new Cartographic(CesiumMath.PI - fifteenDegrees, 0.0); @@ -404,8 +389,6 @@ defineSuite([ it('test across equator', function() { var ellipsoid = Ellipsoid.WGS84; - var fifteenDegrees = Math.PI / 12.0; - var oneDegree = Math.PI / 180.0; var initial = new Cartographic(fifteenDegrees, -oneDegree); var final = new Cartographic(fifteenDegrees, oneDegree); @@ -432,7 +415,6 @@ defineSuite([ it('test close to poles', function() { var ellipsoid = Ellipsoid.WGS84; var fiveDegrees = CesiumMath.PI / 36.0; - var fifteenDegrees = 3 * fiveDegrees; var eightyDegrees = 16 * fiveDegrees; var distance = fifteenDegrees * ellipsoid.maximumRadius; @@ -475,9 +457,7 @@ defineSuite([ }); it('interpolates start and end points', function() { - var fifteenDegrees = Math.PI / 12; var start = new Cartographic(fifteenDegrees, fifteenDegrees); - var thirtyDegrees = Math.PI / 6; var end = new Cartographic(thirtyDegrees, thirtyDegrees); var rhumb = new EllipsoidRhumbLine(start, end); @@ -493,11 +473,8 @@ defineSuite([ }); it('interpolates midpoint', function() { - var fifteenDegrees = Math.PI / 12; var start = new Cartographic(fifteenDegrees, 0.0); - var fortyfiveDegrees = Math.PI / 4; var end = new Cartographic(fortyfiveDegrees, 0.0); - var thirtyDegrees = Math.PI / 6; var expectedMid = new Cartographic(thirtyDegrees, 0.0); var rhumb = new EllipsoidRhumbLine(start, end); @@ -510,9 +487,7 @@ defineSuite([ }); it('interpolates start and end points using fraction', function() { - var fifteenDegrees = Math.PI / 12; var start = new Cartographic(fifteenDegrees, fifteenDegrees); - var thirtyDegrees = Math.PI / 6; var end = new Cartographic(thirtyDegrees, thirtyDegrees); var rhumb = new EllipsoidRhumbLine(start, end); @@ -527,11 +502,8 @@ defineSuite([ }); it('interpolates midpoint using fraction', function() { - var fifteenDegrees = Math.PI / 12; var start = new Cartographic(fifteenDegrees, 0.0); - var fortyfiveDegrees = Math.PI / 4; var end = new Cartographic(fortyfiveDegrees, 0.0); - var thirtyDegrees = Math.PI / 6; var expectedMid = new Cartographic(thirtyDegrees, 0.0); var rhumb = new EllipsoidRhumbLine(start, end); @@ -543,11 +515,8 @@ defineSuite([ }); it('interpolates midpoint fraction using result parameter', function() { - var fifteenDegrees = Math.PI / 12; var start = new Cartographic(fifteenDegrees, 0.0); - var fortyfiveDegrees = Math.PI / 4; var end = new Cartographic(fortyfiveDegrees, 0.0); - var thirtyDegrees = Math.PI / 6; var expectedMid = new Cartographic(thirtyDegrees, 0.0); var rhumb = new EllipsoidRhumbLine(start, end); @@ -560,11 +529,8 @@ defineSuite([ }); it('interpolates midpoint using result parameter', function() { - var fifteenDegrees = Math.PI / 12; var start = new Cartographic(fifteenDegrees, 0.0); - var fortyfiveDegrees = Math.PI / 4; var end = new Cartographic(fortyfiveDegrees, 0.0); - var thirtyDegrees = Math.PI / 6; var expectedMid = new Cartographic(thirtyDegrees, 0.0); var rhumb = new EllipsoidRhumbLine(start, end); From 86c23bc64ae8a4caba2ee04cfa8dd51c9c687061 Mon Sep 17 00:00:00 2001 From: Shehzan Mohammed Date: Tue, 15 Jan 2019 13:31:39 -0500 Subject: [PATCH 05/11] Check correctly for distance=0 in EllipsoidRhumbLine, other PR fixes --- Source/Core/EllipsoidRhumbLine.js | 3 +- Specs/Core/EllipsoidRhumbLineSpec.js | 44 +++++++++++++++------------- 2 files changed, 25 insertions(+), 22 deletions(-) diff --git a/Source/Core/EllipsoidRhumbLine.js b/Source/Core/EllipsoidRhumbLine.js index 71682b0e1a93..51b9e51cfdc3 100644 --- a/Source/Core/EllipsoidRhumbLine.js +++ b/Source/Core/EllipsoidRhumbLine.js @@ -300,6 +300,7 @@ define([ Check.defined('start', start); Check.defined('heading', heading); Check.defined('distance', distance); + Check.typeOf.number.greaterThan('distance', distance, 0.0); //>>includeEnd('debug'); var e = defaultValue(ellipsoid, Ellipsoid.WGS84); @@ -354,7 +355,7 @@ define([ EllipsoidRhumbLine.prototype.interpolateUsingSurfaceDistance = function(distance, result) { //>>includeStart('debug', pragmas.debug); Check.defined('distance', distance); - Check.defined('this._distance', this._distance); + Check.typeOf.number.greaterThan('this._distance', this._distance, CesiumMath.EPSILON12); //>>includeEnd('debug'); var ellipsoid = this._ellipsoid; diff --git a/Specs/Core/EllipsoidRhumbLineSpec.js b/Specs/Core/EllipsoidRhumbLineSpec.js index 03d2b6011fdb..9b7e0c659cf2 100644 --- a/Specs/Core/EllipsoidRhumbLineSpec.js +++ b/Specs/Core/EllipsoidRhumbLineSpec.js @@ -193,16 +193,6 @@ defineSuite([ expect(CesiumMath.PI).toEqualEpsilon(rhumb2.heading, CesiumMath.EPSILON12); }); - it('computes no distance', function() { - var ellipsoid = new Ellipsoid(6, 6, 3); - var start = new Cartographic(CesiumMath.PI_OVER_TWO, 0.0); - var end = new Cartographic(CesiumMath.PI_OVER_TWO, 0.0); - - var rhumb = new EllipsoidRhumbLine(start, end, ellipsoid); - expect(0).toEqualEpsilon(rhumb.surfaceDistance, CesiumMath.EPSILON12); - expect(0).toEqualEpsilon(rhumb.heading, CesiumMath.EPSILON12); - }); - it('computes distance at equator', function() { var ellipsoid = new Ellipsoid(6, 6, 3); var start = new Cartographic(-CesiumMath.PI_OVER_FOUR, 0.0); @@ -221,7 +211,7 @@ defineSuite([ expect(thirtyDegrees * 6).toEqualEpsilon(rhumb.surfaceDistance, CesiumMath.EPSILON12); }); - it('computes distance for sphere 90 degrees along meridian and equator and check equality', function() { + it('computes equal distance on sphere for 90 degrees arcs along meridian and equator', function() { var ellipsoid = new Ellipsoid(6, 6, 6); var fortyFiveSouth = new Cartographic(0.0, -CesiumMath.PI_OVER_FOUR); var fortyFiveNorth = new Cartographic(0.0, CesiumMath.PI_OVER_FOUR); @@ -251,7 +241,18 @@ defineSuite([ expect(distance).toEqualEpsilon(rhumb.surfaceDistance, CesiumMath.EPSILON12); }); - it('tests sphere', function() { + it('fromStartHeadingDistance throws if distance is 0', function() { + var radius = 6378137.0; + var ellipsoid = new Ellipsoid(radius, radius, radius); + var initial = new Cartographic(fifteenDegrees, fifteenDegrees); + + expect(function() { + var rhumb = EllipsoidRhumbLine.fromStartHeadingDistance(initial, fifteenDegrees, 0.0, ellipsoid); + return rhumb.interpolateUsingSurfaceDistance(0); + }).toThrowDeveloperError(); + }); + + it('computes heading and distance given endpoints on sphere ', function() { var radius = 6378137.0; var ellipsoid = new Ellipsoid(radius, radius, radius); var initial = new Cartographic(fifteenDegrees, fifteenDegrees); @@ -264,7 +265,7 @@ defineSuite([ expect(distance).toEqualEpsilon(rhumb2.surfaceDistance, CesiumMath.EPSILON6); }); - it('tests sphereoid', function() { + it('computes heading and distance given endpoints on sphereoid', function() { var ellipsoid = Ellipsoid.WGS84; var initial = new Cartographic(fifteenDegrees, fifteenDegrees); var distance = ellipsoid.maximumRadius * fifteenDegrees; @@ -328,27 +329,27 @@ defineSuite([ var rhumb1 = EllipsoidRhumbLine.fromStartHeadingDistance(initial, eightyNineDegrees, distance, ellipsoid); var rhumb2 = new EllipsoidRhumbLine(initial, rhumb1.end, ellipsoid); expect(rhumb1.heading).toEqualEpsilon(rhumb2.heading, CesiumMath.EPSILON12); - expect(rhumb1.surfaceDistance).toEqualEpsilon(rhumb2.surfaceDistance, CesiumMath.EPSILON3); + expect(rhumb1.surfaceDistance).toEqualEpsilon(rhumb2.surfaceDistance, CesiumMath.EPSILON6); rhumb1 = EllipsoidRhumbLine.fromStartHeadingDistance(initial, eightyNinePointNineDegrees, distance, ellipsoid); rhumb2 = new EllipsoidRhumbLine(initial, rhumb1.end, ellipsoid); expect(rhumb1.heading).toEqualEpsilon(rhumb2.heading, CesiumMath.EPSILON12); - expect(rhumb1.surfaceDistance).toEqualEpsilon(rhumb2.surfaceDistance, CesiumMath.EPSILON3); + expect(rhumb1.surfaceDistance).toEqualEpsilon(rhumb2.surfaceDistance, CesiumMath.EPSILON6); rhumb1 = EllipsoidRhumbLine.fromStartHeadingDistance(initial, ninetyDegrees, distance, ellipsoid); rhumb2 = new EllipsoidRhumbLine(initial, rhumb1.end, ellipsoid); expect(rhumb1.heading).toEqualEpsilon(rhumb2.heading, CesiumMath.EPSILON12); - expect(rhumb1.surfaceDistance).toEqualEpsilon(rhumb2.surfaceDistance, CesiumMath.EPSILON3); + expect(rhumb1.surfaceDistance).toEqualEpsilon(rhumb2.surfaceDistance, CesiumMath.EPSILON6); rhumb1 = EllipsoidRhumbLine.fromStartHeadingDistance(initial, ninetyPointOneDegrees, distance, ellipsoid); rhumb2 = new EllipsoidRhumbLine(initial, rhumb1.end, ellipsoid); expect(rhumb1.heading).toEqualEpsilon(rhumb2.heading, CesiumMath.EPSILON12); - expect(rhumb1.surfaceDistance).toEqualEpsilon(rhumb2.surfaceDistance, CesiumMath.EPSILON3); + expect(rhumb1.surfaceDistance).toEqualEpsilon(rhumb2.surfaceDistance, CesiumMath.EPSILON6); rhumb1 = EllipsoidRhumbLine.fromStartHeadingDistance(initial, ninetyPointZeroTwoDegrees, distance, ellipsoid); rhumb2 = new EllipsoidRhumbLine(initial, rhumb1.end, ellipsoid); expect(rhumb1.heading).toEqualEpsilon(rhumb2.heading, CesiumMath.EPSILON12); - expect(rhumb1.surfaceDistance).toEqualEpsilon(rhumb2.surfaceDistance, CesiumMath.EPSILON3); + expect(rhumb1.surfaceDistance).toEqualEpsilon(rhumb2.surfaceDistance, CesiumMath.EPSILON6); }); it('test sphereoid across meridian', function() { @@ -375,14 +376,14 @@ defineSuite([ var rhumb2 = new EllipsoidRhumbLine.fromStartHeadingDistance(initial, 3.0 * CesiumMath.PI_OVER_TWO, distance, ellipsoid); expect(-CesiumMath.PI_OVER_TWO).toEqualEpsilon(rhumb1.heading, CesiumMath.EPSILON12); - expect(CesiumMath.PI / 6 * ellipsoid.maximumRadius).toEqualEpsilon(rhumb1.surfaceDistance, CesiumMath.EPSILON6); + expect(distance).toEqualEpsilon(rhumb1.surfaceDistance, CesiumMath.EPSILON6); expect(rhumb1.heading).toEqualEpsilon(rhumb2.heading, CesiumMath.EPSILON12); expect(rhumb1.surfaceDistance).toEqualEpsilon(rhumb2.surfaceDistance, CesiumMath.EPSILON6); var rhumb3 = new EllipsoidRhumbLine(final, initial, ellipsoid); var rhumb4 = new EllipsoidRhumbLine.fromStartHeadingDistance(final, CesiumMath.PI_OVER_TWO, distance, ellipsoid); expect(CesiumMath.PI_OVER_TWO).toEqualEpsilon(rhumb3.heading, CesiumMath.EPSILON12); - expect(CesiumMath.PI / 6 * ellipsoid.maximumRadius).toEqualEpsilon(rhumb3.surfaceDistance, CesiumMath.EPSILON6); + expect(distance).toEqualEpsilon(rhumb3.surfaceDistance, CesiumMath.EPSILON6); expect(rhumb3.heading).toEqualEpsilon(rhumb4.heading, CesiumMath.EPSILON12); expect(rhumb3.surfaceDistance).toEqualEpsilon(rhumb4.surfaceDistance, CesiumMath.EPSILON6); }); @@ -397,6 +398,7 @@ defineSuite([ var geodesic = new EllipsoidGeodesic(initial, final, ellipsoid); expect(0.0).toEqualEpsilon(rhumb.heading, CesiumMath.EPSILON12); expect(geodesic.startHeading).toEqualEpsilon(rhumb.heading, CesiumMath.EPSILON12); + expect(geodesic.surfaceDistance).toEqualEpsilon(rhumb.surfaceDistance, CesiumMath.EPSILON6); }); it('test on equator', function() { @@ -409,7 +411,7 @@ defineSuite([ var geodesic = new EllipsoidGeodesic(initial, final, ellipsoid); expect(CesiumMath.PI_OVER_TWO).toEqualEpsilon(rhumb.heading, CesiumMath.EPSILON12); expect(geodesic.startHeading).toEqualEpsilon(rhumb.heading, CesiumMath.EPSILON12); - expect(geodesic.surfaceDistance).toEqualEpsilon(rhumb.surfaceDistance, CesiumMath.EPSILON4); + expect(geodesic.surfaceDistance).toEqualEpsilon(rhumb.surfaceDistance, CesiumMath.EPSILON4); // Due to computational difference, slightly larger tolerance }); it('test close to poles', function() { From 0e12d1cc49092ec18fd9bc8fe39fac10fac95911 Mon Sep 17 00:00:00 2001 From: Shehzan Mohammed Date: Tue, 15 Jan 2019 14:43:23 -0500 Subject: [PATCH 06/11] Add intersectionWithLatitude and intersectionWithLongitude functions to EllipsoidRhumbLine --- Source/Core/EllipsoidRhumbLine.js | 106 +++++++++++++++++++++++++ Specs/Core/EllipsoidRhumbLineSpec.js | 113 +++++++++++++++++++++++++++ 2 files changed, 219 insertions(+) diff --git a/Source/Core/EllipsoidRhumbLine.js b/Source/Core/EllipsoidRhumbLine.js index 51b9e51cfdc3..112c13fe778a 100644 --- a/Source/Core/EllipsoidRhumbLine.js +++ b/Source/Core/EllipsoidRhumbLine.js @@ -413,5 +413,111 @@ define([ return new Cartographic(longitude, latitude, 0); }; + /** + * Provides the location of a point at the indicated longitude along the rhumb line. + * + * @param {Number} intersectionLongitude The longitude, in radians, at which to find the intersection point from the starting point using the heading. + * @param {Cartographic} [result] The object in which to store the result. + * @returns {Cartographic} The location of the intersection point along the rhumb line. + * + * @exception {DeveloperError} start and end must be set before calling function findIntersectionWithLongitude. + */ + EllipsoidRhumbLine.prototype.findIntersectionWithLongitude = function(intersectionLongitude, result) { + //>>includeStart('debug', pragmas.debug); + Check.defined('intersectionLongitude', intersectionLongitude); + Check.defined('distance', this._distance); + //>>includeEnd('debug'); + + var ellipticity = this._ellipticity; + var heading = this._heading; + var absHeading = Math.abs(heading); + var start = this._start; + + intersectionLongitude = CesiumMath.negativePiToPi(intersectionLongitude); + + if (!defined(result)) { + result = new Cartographic(); + } + + // If heading is -PI/2 or PI/2, this is an E-W rhumb line + // If heading is 0 or PI, this is an N-S rhumb line + if (Math.abs(CesiumMath.PI_OVER_TWO - absHeading) <= CesiumMath.EPSILON8) { + result.longitude = intersectionLongitude; + result.latitude = start.latitude; + result.height = 0; + return result; + } else if (CesiumMath.equalsEpsilon(Math.abs(CesiumMath.PI_OVER_TWO - absHeading), CesiumMath.PI_OVER_TWO, CesiumMath.EPSILON8)) { + if (CesiumMath.equalsEpsilon(intersectionLongitude, start.longitude, CesiumMath.EPSILON12)) { + return undefined; + } + + result.longitude = intersectionLongitude; + result.latitude = CesiumMath.PI_OVER_TWO * Math.sign(heading); + result.height = 0; + return result; + } + + // Use iterative solver from Equation 9 from http://edwilliams.org/ellipsoid/ellipsoid.pdf + var phi1 = start.latitude; + var eSinPhi1 = ellipticity * Math.sin(phi1); + var leftComponent = Math.tan(0.5 * (CesiumMath.PI_OVER_TWO + phi1)) * Math.exp((intersectionLongitude - start.longitude) / Math.tan(heading)); + var denominator = (1 + eSinPhi1) / (1 - eSinPhi1); + + var newPhi = start.latitude; + var phi; + do { + phi = newPhi; + var eSinPhi = ellipticity * Math.sin(phi); + var numerator = (1 + eSinPhi) / (1 - eSinPhi); + newPhi = 2 * Math.atan(leftComponent * Math.pow(numerator / denominator, ellipticity / 2)) - CesiumMath.PI_OVER_TWO; + } while (!CesiumMath.equalsEpsilon(newPhi, phi, CesiumMath.EPSILON12)); + + result.longitude = intersectionLongitude; + result.latitude = phi; + result.height = 0; + return result; + }; + + /** + * Provides the location of a point at the indicated latitude along the rhumb line. + * + * @param {Number} intersectionLatitude The latitude, in radians, at which to find the intersection point from the starting point using the heading. + * @param {Cartographic} [result] The object in which to store the result. + * @returns {Cartographic} The location of the intersection point along the rhumb line, undefined if there is no intersection, infinite intersections or if intersection point is not between start and end. + * + * @exception {DeveloperError} start and end must be set before calling function findIntersectionWithLongitude. + */ + EllipsoidRhumbLine.prototype.findIntersectionWithLatitude = function(intersectionLatitude, result) { + //>>includeStart('debug', pragmas.debug); + Check.defined('intersectionLatitude', intersectionLatitude); + Check.defined('distance', this._distance); + //>>includeEnd('debug'); + + var ellipticity = this._ellipticity; + var heading = this._heading; + var start = this._start; + + // If start and end have same latitude, return undefined since it's either no intersection or infinite intersections + if (CesiumMath.equalsEpsilon(Math.abs(heading), CesiumMath.PI_OVER_TWO, CesiumMath.EPSILON8)) { + return; + } + + // Can be solved using the same equations from interpolateUsingSurfaceDistance + var sigma1 = calculateSigma(ellipticity, start.latitude); + var sigma2 = calculateSigma(ellipticity, intersectionLatitude); + var deltaLongitude = Math.tan(heading) * (sigma2 - sigma1); + var longitude = CesiumMath.negativePiToPi(start.longitude + deltaLongitude); + + if (defined(result)) { + result.longitude = longitude; + result.latitude = intersectionLatitude; + result.height = 0; + + return result; + } + + return new Cartographic(longitude, intersectionLatitude, 0); + }; + return EllipsoidRhumbLine; }); diff --git a/Specs/Core/EllipsoidRhumbLineSpec.js b/Specs/Core/EllipsoidRhumbLineSpec.js index 9b7e0c659cf2..e97e691df240 100644 --- a/Specs/Core/EllipsoidRhumbLineSpec.js +++ b/Specs/Core/EllipsoidRhumbLineSpec.js @@ -546,4 +546,117 @@ defineSuite([ expect(expectedMid.longitude).toEqualEpsilon(result.longitude, CesiumMath.EPSILON12); expect(expectedMid.latitude).toEqualEpsilon(result.latitude, CesiumMath.EPSILON12); }); + + it('finds midpoint and other points using intersection with longitude', function() { + var start = new Cartographic(fifteenDegrees, 0.0); + var end = new Cartographic(fortyfiveDegrees, thirtyDegrees); + + var rhumb = new EllipsoidRhumbLine(start, end); + + var midpointUsingInterpolation = rhumb.interpolateUsingFraction(0.5); + var midpointUsingIntersection = rhumb.findIntersectionWithLongitude(midpointUsingInterpolation.longitude); + + expect(midpointUsingInterpolation.longitude).toEqualEpsilon(midpointUsingIntersection.longitude, CesiumMath.EPSILON12); + expect(midpointUsingInterpolation.latitude).toEqualEpsilon(midpointUsingIntersection.latitude, CesiumMath.EPSILON12); + + var pointUsingInterpolation = rhumb.interpolateUsingFraction(0.1); + var pointUsingIntersection = rhumb.findIntersectionWithLongitude(pointUsingInterpolation.longitude); + + expect(pointUsingInterpolation.longitude).toEqualEpsilon(pointUsingIntersection.longitude, CesiumMath.EPSILON12); + expect(pointUsingInterpolation.latitude).toEqualEpsilon(pointUsingIntersection.latitude, CesiumMath.EPSILON12); + + pointUsingInterpolation = rhumb.interpolateUsingFraction(0.75); + pointUsingIntersection = rhumb.findIntersectionWithLongitude(pointUsingInterpolation.longitude); + + expect(pointUsingInterpolation.longitude).toEqualEpsilon(pointUsingIntersection.longitude, CesiumMath.EPSILON12); + expect(pointUsingInterpolation.latitude).toEqualEpsilon(pointUsingIntersection.latitude, CesiumMath.EPSILON12); + + pointUsingInterpolation = rhumb.interpolateUsingFraction(1.1); + pointUsingIntersection = rhumb.findIntersectionWithLongitude(pointUsingInterpolation.longitude); + + expect(pointUsingInterpolation.longitude).toEqualEpsilon(pointUsingIntersection.longitude, CesiumMath.EPSILON12); + expect(pointUsingInterpolation.latitude).toEqualEpsilon(pointUsingIntersection.latitude, CesiumMath.EPSILON12); + }); + + it('intersection with longitude handles E-W lines', function() { + var start = new Cartographic(fifteenDegrees, 0.0); + var end = new Cartographic(fortyfiveDegrees, 0.0); + + var rhumb = new EllipsoidRhumbLine(start, end); + + var midpointUsingInterpolation = rhumb.interpolateUsingFraction(0.5); + var midpointUsingIntersection = rhumb.findIntersectionWithLongitude(midpointUsingInterpolation.longitude); + + expect(midpointUsingInterpolation.longitude).toEqualEpsilon(midpointUsingIntersection.longitude, CesiumMath.EPSILON12); + expect(midpointUsingInterpolation.latitude).toEqualEpsilon(midpointUsingIntersection.latitude, CesiumMath.EPSILON12); + }); + + it('intersection with longitude handles N-S lines', function() { + var start = new Cartographic(fifteenDegrees, 0.0); + var end = new Cartographic(fifteenDegrees, thirtyDegrees); + + var rhumb = new EllipsoidRhumbLine(start, end); + + var midpointUsingInterpolation = rhumb.interpolateUsingFraction(0.5); + var midpointUsingIntersection = rhumb.findIntersectionWithLongitude(midpointUsingInterpolation.longitude); + + expect(midpointUsingIntersection).not.toBeDefined(); + }); + + it('finds midpoint and other points using intersection with latitude', function() { + var start = new Cartographic(fifteenDegrees, 0.0); + var end = new Cartographic(fortyfiveDegrees, thirtyDegrees); + + var rhumb = new EllipsoidRhumbLine(start, end); + + var midpointUsingInterpolation = rhumb.interpolateUsingFraction(0.5); + var midpointUsingIntersection = rhumb.findIntersectionWithLatitude(midpointUsingInterpolation.latitude); + + expect(midpointUsingInterpolation.longitude).toEqualEpsilon(midpointUsingIntersection.longitude, CesiumMath.EPSILON12); + expect(midpointUsingInterpolation.latitude).toEqualEpsilon(midpointUsingIntersection.latitude, CesiumMath.EPSILON12); + + var pointUsingInterpolation = rhumb.interpolateUsingFraction(0.1); + var pointUsingIntersection = rhumb.findIntersectionWithLatitude(pointUsingInterpolation.latitude); + + expect(pointUsingInterpolation.longitude).toEqualEpsilon(pointUsingIntersection.longitude, CesiumMath.EPSILON12); + expect(pointUsingInterpolation.latitude).toEqualEpsilon(pointUsingIntersection.latitude, CesiumMath.EPSILON12); + + pointUsingInterpolation = rhumb.interpolateUsingFraction(0.75); + pointUsingIntersection = rhumb.findIntersectionWithLatitude(pointUsingInterpolation.latitude); + + expect(pointUsingInterpolation.longitude).toEqualEpsilon(pointUsingIntersection.longitude, CesiumMath.EPSILON12); + expect(pointUsingInterpolation.latitude).toEqualEpsilon(pointUsingIntersection.latitude, CesiumMath.EPSILON12); + + pointUsingInterpolation = rhumb.interpolateUsingFraction(1.1); + pointUsingIntersection = rhumb.findIntersectionWithLatitude(pointUsingInterpolation.latitude); + + expect(pointUsingInterpolation.longitude).toEqualEpsilon(pointUsingIntersection.longitude, CesiumMath.EPSILON12); + expect(pointUsingInterpolation.latitude).toEqualEpsilon(pointUsingIntersection.latitude, CesiumMath.EPSILON12); + }); + + it('intersection with latitude handles E-W lines', function() { + var start = new Cartographic(fifteenDegrees, 0.0); + var end = new Cartographic(fortyfiveDegrees, 0.0); + + var rhumb = new EllipsoidRhumbLine(start, end); + + var midpointUsingInterpolation = rhumb.interpolateUsingFraction(0.5); + var midpointUsingIntersection = rhumb.findIntersectionWithLatitude(midpointUsingInterpolation.latitude); + + expect(midpointUsingIntersection).not.toBeDefined(); + }); + + it('intersection with latitude handles N-S lines', function() { + var start = new Cartographic(fifteenDegrees, 0.0); + var end = new Cartographic(fifteenDegrees, thirtyDegrees); + + var rhumb = new EllipsoidRhumbLine(start, end); + + var midpointUsingInterpolation = rhumb.interpolateUsingFraction(0.5); + var midpointUsingIntersection = rhumb.findIntersectionWithLatitude(midpointUsingInterpolation.latitude); + + expect(midpointUsingInterpolation.longitude).toEqualEpsilon(midpointUsingIntersection.longitude, CesiumMath.EPSILON12); + expect(midpointUsingInterpolation.latitude).toEqualEpsilon(midpointUsingIntersection.latitude, CesiumMath.EPSILON12); + }); + }); From 882b01ae503ab7d0b9d7284da1bdf4fb8706f1d0 Mon Sep 17 00:00:00 2001 From: Shehzan Mohammed Date: Tue, 15 Jan 2019 17:56:56 -0500 Subject: [PATCH 07/11] Fixes from PR comments --- Source/Core/EllipsoidRhumbLine.js | 26 +++++++++----- Specs/Core/EllipsoidRhumbLineSpec.js | 54 ++++++++++++---------------- 2 files changed, 39 insertions(+), 41 deletions(-) diff --git a/Source/Core/EllipsoidRhumbLine.js b/Source/Core/EllipsoidRhumbLine.js index 112c13fe778a..7ad54525da5b 100644 --- a/Source/Core/EllipsoidRhumbLine.js +++ b/Source/Core/EllipsoidRhumbLine.js @@ -5,6 +5,7 @@ define([ './defaultValue', './defined', './defineProperties', + './DeveloperError', './Ellipsoid', './Math' ], function( @@ -14,6 +15,7 @@ define([ defaultValue, defined, defineProperties, + DeveloperError, Ellipsoid, CesiumMath) { 'use strict'; @@ -354,8 +356,10 @@ define([ */ EllipsoidRhumbLine.prototype.interpolateUsingSurfaceDistance = function(distance, result) { //>>includeStart('debug', pragmas.debug); - Check.defined('distance', distance); - Check.typeOf.number.greaterThan('this._distance', this._distance, CesiumMath.EPSILON12); + Check.typeOf.number('distance', distance); + if (!defined(this._distance) || this._distance === 0.0) { + throw new DeveloperError('EllipsoidRhumbLine must have distinct start and end set.'); + } //>>includeEnd('debug'); var ellipsoid = this._ellipsoid; @@ -418,14 +422,16 @@ define([ * * @param {Number} intersectionLongitude The longitude, in radians, at which to find the intersection point from the starting point using the heading. * @param {Cartographic} [result] The object in which to store the result. - * @returns {Cartographic} The location of the intersection point along the rhumb line. + * @returns {Cartographic} The location of the intersection point along the rhumb line, undefined if there is no intersection or infinite intersections. * * @exception {DeveloperError} start and end must be set before calling function findIntersectionWithLongitude. */ EllipsoidRhumbLine.prototype.findIntersectionWithLongitude = function(intersectionLongitude, result) { //>>includeStart('debug', pragmas.debug); - Check.defined('intersectionLongitude', intersectionLongitude); - Check.defined('distance', this._distance); + Check.typeOf.number('intersectionLongitude', intersectionLongitude); + if (!defined(this._distance) || this._distance === 0.0) { + throw new DeveloperError('EllipsoidRhumbLine must have distinct start and end set.'); + } //>>includeEnd('debug'); var ellipticity = this._ellipticity; @@ -452,7 +458,7 @@ define([ } result.longitude = intersectionLongitude; - result.latitude = CesiumMath.PI_OVER_TWO * Math.sign(heading); + result.latitude = CesiumMath.PI_OVER_TWO * Math.sign(CesiumMath.PI_OVER_TWO - heading); result.height = 0; return result; } @@ -483,14 +489,16 @@ define([ * * @param {Number} intersectionLatitude The latitude, in radians, at which to find the intersection point from the starting point using the heading. * @param {Cartographic} [result] The object in which to store the result. - * @returns {Cartographic} The location of the intersection point along the rhumb line, undefined if there is no intersection, infinite intersections or if intersection point is not between start and end. + * @returns {Cartographic} The location of the intersection point along the rhumb line, undefined if there is no intersection or infinite intersections. * * @exception {DeveloperError} start and end must be set before calling function findIntersectionWithLongitude. */ EllipsoidRhumbLine.prototype.findIntersectionWithLatitude = function(intersectionLatitude, result) { //>>includeStart('debug', pragmas.debug); - Check.defined('intersectionLatitude', intersectionLatitude); - Check.defined('distance', this._distance); + Check.typeOf.number('intersectionLatitude', intersectionLatitude); + if (!defined(this._distance) || this._distance === 0.0) { + throw new DeveloperError('EllipsoidRhumbLine must have distinct start and end set.'); + } //>>includeEnd('debug'); var ellipticity = this._ellipticity; diff --git a/Specs/Core/EllipsoidRhumbLineSpec.js b/Specs/Core/EllipsoidRhumbLineSpec.js index e97e691df240..2e9b7d971283 100644 --- a/Specs/Core/EllipsoidRhumbLineSpec.js +++ b/Specs/Core/EllipsoidRhumbLineSpec.js @@ -241,7 +241,7 @@ defineSuite([ expect(distance).toEqualEpsilon(rhumb.surfaceDistance, CesiumMath.EPSILON12); }); - it('fromStartHeadingDistance throws if distance is 0', function() { + it('throws when interpolating rhumb line of zero length', function() { var radius = 6378137.0; var ellipsoid = new Ellipsoid(radius, radius, radius); var initial = new Cartographic(fifteenDegrees, fifteenDegrees); @@ -555,27 +555,19 @@ defineSuite([ var midpointUsingInterpolation = rhumb.interpolateUsingFraction(0.5); var midpointUsingIntersection = rhumb.findIntersectionWithLongitude(midpointUsingInterpolation.longitude); - - expect(midpointUsingInterpolation.longitude).toEqualEpsilon(midpointUsingIntersection.longitude, CesiumMath.EPSILON12); - expect(midpointUsingInterpolation.latitude).toEqualEpsilon(midpointUsingIntersection.latitude, CesiumMath.EPSILON12); + expect(Cartographic.equalsEpsilon(midpointUsingInterpolation, midpointUsingIntersection, CesiumMath.EPSILON12)).toBe(true); var pointUsingInterpolation = rhumb.interpolateUsingFraction(0.1); var pointUsingIntersection = rhumb.findIntersectionWithLongitude(pointUsingInterpolation.longitude); - - expect(pointUsingInterpolation.longitude).toEqualEpsilon(pointUsingIntersection.longitude, CesiumMath.EPSILON12); - expect(pointUsingInterpolation.latitude).toEqualEpsilon(pointUsingIntersection.latitude, CesiumMath.EPSILON12); + expect(Cartographic.equalsEpsilon(pointUsingInterpolation, pointUsingIntersection, CesiumMath.EPSILON12)).toBe(true); pointUsingInterpolation = rhumb.interpolateUsingFraction(0.75); pointUsingIntersection = rhumb.findIntersectionWithLongitude(pointUsingInterpolation.longitude); - - expect(pointUsingInterpolation.longitude).toEqualEpsilon(pointUsingIntersection.longitude, CesiumMath.EPSILON12); - expect(pointUsingInterpolation.latitude).toEqualEpsilon(pointUsingIntersection.latitude, CesiumMath.EPSILON12); + expect(Cartographic.equalsEpsilon(pointUsingInterpolation, pointUsingIntersection, CesiumMath.EPSILON12)).toBe(true); pointUsingInterpolation = rhumb.interpolateUsingFraction(1.1); pointUsingIntersection = rhumb.findIntersectionWithLongitude(pointUsingInterpolation.longitude); - - expect(pointUsingInterpolation.longitude).toEqualEpsilon(pointUsingIntersection.longitude, CesiumMath.EPSILON12); - expect(pointUsingInterpolation.latitude).toEqualEpsilon(pointUsingIntersection.latitude, CesiumMath.EPSILON12); + expect(Cartographic.equalsEpsilon(pointUsingInterpolation, pointUsingIntersection, CesiumMath.EPSILON12)).toBe(true); }); it('intersection with longitude handles E-W lines', function() { @@ -586,9 +578,7 @@ defineSuite([ var midpointUsingInterpolation = rhumb.interpolateUsingFraction(0.5); var midpointUsingIntersection = rhumb.findIntersectionWithLongitude(midpointUsingInterpolation.longitude); - - expect(midpointUsingInterpolation.longitude).toEqualEpsilon(midpointUsingIntersection.longitude, CesiumMath.EPSILON12); - expect(midpointUsingInterpolation.latitude).toEqualEpsilon(midpointUsingIntersection.latitude, CesiumMath.EPSILON12); + expect(Cartographic.equalsEpsilon(midpointUsingInterpolation, midpointUsingIntersection, CesiumMath.EPSILON12)).toBe(true); }); it('intersection with longitude handles N-S lines', function() { @@ -603,6 +593,17 @@ defineSuite([ expect(midpointUsingIntersection).not.toBeDefined(); }); + it('intersection with longitude handles N-S lines with different longitude', function() { + var start = new Cartographic(fifteenDegrees, 0.0); + var end = new Cartographic(fifteenDegrees, thirtyDegrees); + + var rhumb = new EllipsoidRhumbLine(start, end); + + var midpointUsingIntersection = rhumb.findIntersectionWithLongitude(thirtyDegrees); + + expect(midpointUsingIntersection.latitude).toEqualEpsilon(CesiumMath.PI_OVER_TWO, CesiumMath.EPSILON12); + }); + it('finds midpoint and other points using intersection with latitude', function() { var start = new Cartographic(fifteenDegrees, 0.0); var end = new Cartographic(fortyfiveDegrees, thirtyDegrees); @@ -611,27 +612,19 @@ defineSuite([ var midpointUsingInterpolation = rhumb.interpolateUsingFraction(0.5); var midpointUsingIntersection = rhumb.findIntersectionWithLatitude(midpointUsingInterpolation.latitude); - - expect(midpointUsingInterpolation.longitude).toEqualEpsilon(midpointUsingIntersection.longitude, CesiumMath.EPSILON12); - expect(midpointUsingInterpolation.latitude).toEqualEpsilon(midpointUsingIntersection.latitude, CesiumMath.EPSILON12); + expect(Cartographic.equalsEpsilon(midpointUsingInterpolation, midpointUsingIntersection, CesiumMath.EPSILON12)).toBe(true); var pointUsingInterpolation = rhumb.interpolateUsingFraction(0.1); var pointUsingIntersection = rhumb.findIntersectionWithLatitude(pointUsingInterpolation.latitude); - - expect(pointUsingInterpolation.longitude).toEqualEpsilon(pointUsingIntersection.longitude, CesiumMath.EPSILON12); - expect(pointUsingInterpolation.latitude).toEqualEpsilon(pointUsingIntersection.latitude, CesiumMath.EPSILON12); + expect(Cartographic.equalsEpsilon(pointUsingInterpolation, pointUsingIntersection, CesiumMath.EPSILON12)).toBe(true); pointUsingInterpolation = rhumb.interpolateUsingFraction(0.75); pointUsingIntersection = rhumb.findIntersectionWithLatitude(pointUsingInterpolation.latitude); - - expect(pointUsingInterpolation.longitude).toEqualEpsilon(pointUsingIntersection.longitude, CesiumMath.EPSILON12); - expect(pointUsingInterpolation.latitude).toEqualEpsilon(pointUsingIntersection.latitude, CesiumMath.EPSILON12); + expect(Cartographic.equalsEpsilon(pointUsingInterpolation, pointUsingIntersection, CesiumMath.EPSILON12)).toBe(true); pointUsingInterpolation = rhumb.interpolateUsingFraction(1.1); pointUsingIntersection = rhumb.findIntersectionWithLatitude(pointUsingInterpolation.latitude); - - expect(pointUsingInterpolation.longitude).toEqualEpsilon(pointUsingIntersection.longitude, CesiumMath.EPSILON12); - expect(pointUsingInterpolation.latitude).toEqualEpsilon(pointUsingIntersection.latitude, CesiumMath.EPSILON12); + expect(Cartographic.equalsEpsilon(pointUsingInterpolation, pointUsingIntersection, CesiumMath.EPSILON12)).toBe(true); }); it('intersection with latitude handles E-W lines', function() { @@ -654,9 +647,6 @@ defineSuite([ var midpointUsingInterpolation = rhumb.interpolateUsingFraction(0.5); var midpointUsingIntersection = rhumb.findIntersectionWithLatitude(midpointUsingInterpolation.latitude); - - expect(midpointUsingInterpolation.longitude).toEqualEpsilon(midpointUsingIntersection.longitude, CesiumMath.EPSILON12); - expect(midpointUsingInterpolation.latitude).toEqualEpsilon(midpointUsingIntersection.latitude, CesiumMath.EPSILON12); + expect(Cartographic.equalsEpsilon(midpointUsingInterpolation, midpointUsingIntersection, CesiumMath.EPSILON12)).toBe(true); }); - }); From c323623a52b7b7b318421ffbb09738a4d9727d5e Mon Sep 17 00:00:00 2001 From: Shehzan Mohammed Date: Wed, 16 Jan 2019 11:45:25 -0500 Subject: [PATCH 08/11] Add comment about rhumb lines being spirals for rhumb line intersection --- Source/Core/EllipsoidRhumbLine.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Source/Core/EllipsoidRhumbLine.js b/Source/Core/EllipsoidRhumbLine.js index 7ad54525da5b..de4f9a787900 100644 --- a/Source/Core/EllipsoidRhumbLine.js +++ b/Source/Core/EllipsoidRhumbLine.js @@ -419,6 +419,7 @@ define([ /** * Provides the location of a point at the indicated longitude along the rhumb line. + * If the longitude is outside the range of start and end points, the first intersection with the longitude from the start point in the direction of the heading is returned. This follows the spiral property of a rhumb line. * * @param {Number} intersectionLongitude The longitude, in radians, at which to find the intersection point from the starting point using the heading. * @param {Cartographic} [result] The object in which to store the result. @@ -486,6 +487,7 @@ define([ /** * Provides the location of a point at the indicated latitude along the rhumb line. + * If the latitude is outside the range of start and end points, the first intersection with the latitude from that start point in the direction of the heading is returned. This follows the spiral property of a rhumb line. * * @param {Number} intersectionLatitude The latitude, in radians, at which to find the intersection point from the starting point using the heading. * @param {Cartographic} [result] The object in which to store the result. From 12bc94d0935f5ac00ded6e5ffad1658a7f128079 Mon Sep 17 00:00:00 2001 From: Shehzan Mohammed Date: Thu, 17 Jan 2019 11:01:18 -0500 Subject: [PATCH 09/11] Remove EllipsoidRhumbLine.fromStartAndEnd function --- Source/Core/EllipsoidRhumbLine.js | 25 ------------------------- Specs/Core/EllipsoidRhumbLineSpec.js | 23 ----------------------- 2 files changed, 48 deletions(-) diff --git a/Source/Core/EllipsoidRhumbLine.js b/Source/Core/EllipsoidRhumbLine.js index de4f9a787900..a820040b68d2 100644 --- a/Source/Core/EllipsoidRhumbLine.js +++ b/Source/Core/EllipsoidRhumbLine.js @@ -262,31 +262,6 @@ define([ } }); - /** - * Create a rhumb line using an initial and final position. - * - * @param {Cartographic} start The initial planetodetic point on the path. - * @param {Cartographic} end The final planetodetic point on the path. - * @param {Ellipsoid} [ellipsoid=Ellipsoid.WGS84] The ellipsoid on which the rhumb line lies. - * @param {EllipsoidRhumbLine} [result] The object in which to store the result. - * @returns {EllipsoidRhumbLine} The EllipsoidRhumbLine object. - */ - EllipsoidRhumbLine.fromStartAndEnd = function(start, end, ellipsoid, result) { - //>>includeStart('debug', pragmas.debug); - Check.defined('start', start); - Check.defined('end', end); - //>>includeEnd('debug'); - - var e = defaultValue(ellipsoid, Ellipsoid.WGS84); - - if (defined(result)) { - result._ellipsoid = e; - result.setEndPoints(start, end); - return result; - } - return new EllipsoidRhumbLine(start, end, e); - }; - /** * Create a rhumb line using an initial position with a heading and distance. * diff --git a/Specs/Core/EllipsoidRhumbLineSpec.js b/Specs/Core/EllipsoidRhumbLineSpec.js index 2e9b7d971283..9bc1e7e65363 100644 --- a/Specs/Core/EllipsoidRhumbLineSpec.js +++ b/Specs/Core/EllipsoidRhumbLineSpec.js @@ -54,29 +54,6 @@ defineSuite([ }).toThrowDeveloperError(); }); - it('can create using fromStartAndEnd function', function() { - var start = new Cartographic(fifteenDegrees, fifteenDegrees); - var end = new Cartographic(thirtyDegrees, thirtyDegrees); - - var rhumb = EllipsoidRhumbLine.fromStartAndEnd(start, end); - expect(start).toEqual(rhumb.start); - expect(end).toEqual(rhumb.end); - }); - - it('can create using fromStartAndEnd function with result', function() { - var scratch = new EllipsoidRhumbLine(); - - var ellipsoid = new Ellipsoid(6, 6, 3); - var start = new Cartographic(fifteenDegrees, fifteenDegrees); - var end = new Cartographic(thirtyDegrees, thirtyDegrees); - - var rhumb = EllipsoidRhumbLine.fromStartAndEnd(start, end, ellipsoid, scratch); - expect(rhumb).toBe(scratch); - expect(rhumb.ellipsoid).toBe(ellipsoid); - expect(start).toEqual(rhumb.start); - expect(end).toEqual(rhumb.end); - }); - it('can create using fromStartHeadingDistance function', function() { var ellipsoid = new Ellipsoid(6, 6, 3); var start = new Cartographic(fifteenDegrees, fifteenDegrees); From 3bf2528ffbdb5ad46885624c73399a5ca26abb6d Mon Sep 17 00:00:00 2001 From: Shehzan Mohammed Date: Thu, 17 Jan 2019 11:18:26 -0500 Subject: [PATCH 10/11] Restructure EllipsoidRhumbLine.fromStartHeadingDistance --- Source/Core/EllipsoidRhumbLine.js | 155 +++++++++++++-------------- Specs/Core/EllipsoidRhumbLineSpec.js | 12 +-- 2 files changed, 82 insertions(+), 85 deletions(-) diff --git a/Source/Core/EllipsoidRhumbLine.js b/Source/Core/EllipsoidRhumbLine.js index a820040b68d2..a5d61b7390d2 100644 --- a/Source/Core/EllipsoidRhumbLine.js +++ b/Source/Core/EllipsoidRhumbLine.js @@ -133,24 +133,6 @@ define([ var scratchCart1 = new Cartesian3(); var scratchCart2 = new Cartesian3(); - function initialize(ellipsoidRhumbLine, start, end, ellipsoid) { - var major = ellipsoid.maximumRadius; - var minor = ellipsoid.minimumRadius; - var majorSquared = major * major; - var minorSquared = minor * minor; - ellipsoidRhumbLine._ellipticitySquared = (majorSquared - minorSquared) / majorSquared; - ellipsoidRhumbLine._ellipticity = Math.sqrt(ellipsoidRhumbLine._ellipticitySquared); - - if (defined(start)) { - ellipsoidRhumbLine._start = Cartographic.clone(start, ellipsoidRhumbLine._start); - ellipsoidRhumbLine._start.height = 0; - } - if (defined(end)) { - ellipsoidRhumbLine._end = Cartographic.clone(end, ellipsoidRhumbLine._end); - ellipsoidRhumbLine._end.height = 0; - } - } - function computeProperties(ellipsoidRhumbLine, start, end, ellipsoid) { var firstCartesian = Cartesian3.normalize(ellipsoid.cartographicToCartesian(start, scratchCart2), scratchCart1); var lastCartesian = Cartesian3.normalize(ellipsoid.cartographicToCartesian(end, scratchCart2), scratchCart2); @@ -159,13 +141,76 @@ define([ Check.typeOf.number.greaterThanOrEquals('value', Math.abs(Math.abs(Cartesian3.angleBetween(firstCartesian, lastCartesian)) - Math.PI), 0.0125); //>>includeEnd('debug'); - initialize(ellipsoidRhumbLine, start, end, ellipsoid); + var major = ellipsoid.maximumRadius; + var minor = ellipsoid.minimumRadius; + var majorSquared = major * major; + var minorSquared = minor * minor; + ellipsoidRhumbLine._ellipticitySquared = (majorSquared - minorSquared) / majorSquared; + ellipsoidRhumbLine._ellipticity = Math.sqrt(ellipsoidRhumbLine._ellipticitySquared); + + ellipsoidRhumbLine._start = Cartographic.clone(start, ellipsoidRhumbLine._start); + ellipsoidRhumbLine._start.height = 0; + + ellipsoidRhumbLine._end = Cartographic.clone(end, ellipsoidRhumbLine._end); + ellipsoidRhumbLine._end.height = 0; ellipsoidRhumbLine._heading = calculateHeading(ellipsoidRhumbLine, start.longitude, start.latitude, end.longitude, end.latitude); ellipsoidRhumbLine._distance = calculateArcLength(ellipsoidRhumbLine, ellipsoid.maximumRadius, ellipsoid.minimumRadius, start.longitude, start.latitude, end.longitude, end.latitude); } + function interpolateUsingSurfaceDistance(start, heading, distance, major, ellipticity, result) + { + var ellipticitySquared = ellipticity * ellipticity; + + var longitude; + var latitude; + var deltaLongitude; + + //Check to see if the rhumb line has constant latitude + //This won't converge if heading is close to 90 degrees + if (Math.abs(CesiumMath.PI_OVER_TWO - Math.abs(heading)) > CesiumMath.EPSILON8) { + //Calculate latitude of the second point + var M1 = calculateM(ellipticity, major, start.latitude); + var deltaM = distance * Math.cos(heading); + var M2 = M1 + deltaM; + latitude = calculateInverseM(M2, ellipticity, major); + + //Now find the longitude of the second point + var sigma1 = calculateSigma(ellipticity, start.latitude); + var sigma2 = calculateSigma(ellipticity, latitude); + deltaLongitude = Math.tan(heading) * (sigma2 - sigma1); + longitude = CesiumMath.negativePiToPi(start.longitude + deltaLongitude); + } else { //If heading is close to 90 degrees + latitude = start.latitude; + var localRad; + + if (ellipticity === 0.0) { // sphere + localRad = major * Math.cos(start.latitude); + } else { + var sinPhi = Math.sin(start.latitude); + localRad = major * Math.cos(start.latitude) / Math.sqrt(1 - ellipticitySquared * sinPhi * sinPhi); + } + + deltaLongitude = distance / localRad; + if (heading > 0.0) { + longitude = CesiumMath.negativePiToPi(start.longitude + deltaLongitude); + } else { + longitude = CesiumMath.negativePiToPi(start.longitude - deltaLongitude); + } + } + + if (defined(result)) { + result.longitude = longitude; + result.latitude = latitude; + result.height = 0; + + return result; + } + + return new Cartographic(longitude, latitude, 0); + } + /** * Initializes a rhumb line on the ellipsoid connecting the two provided planetodetic points. * @@ -281,16 +326,20 @@ define([ //>>includeEnd('debug'); var e = defaultValue(ellipsoid, Ellipsoid.WGS84); + var major = e.maximumRadius; + var minor = e.minimumRadius; + var majorSquared = major * major; + var minorSquared = minor * minor; + var ellipticity = Math.sqrt((majorSquared - minorSquared) / majorSquared); - if (!defined(result)) { - result = new EllipsoidRhumbLine(undefined, undefined, e); - } + heading = CesiumMath.negativePiToPi(heading); + var end = interpolateUsingSurfaceDistance(start, heading, distance, e.maximumRadius, ellipticity); - initialize(result, start, undefined, e); - result._heading = CesiumMath.negativePiToPi(heading); - result._distance = distance; + if (!defined(result) || (defined(ellipsoid) && !ellipsoid.equals(result.ellipsoid))) { + return new EllipsoidRhumbLine(start, end, e); + } - result._end = result.interpolateUsingSurfaceDistance(distance, new Cartographic()); + result.setEndPoints(start, end); return result; }; @@ -337,59 +386,7 @@ define([ } //>>includeEnd('debug'); - var ellipsoid = this._ellipsoid; - var major = ellipsoid.maximumRadius; - var ellipticity = this._ellipticity; - var ellipticitySquared = this._ellipticitySquared; - var heading = this._heading; - var start = this._start; - - var longitude; - var latitude; - var deltaLongitude; - - //Check to see if the rhumb line has constant latitude - //This won't converge if heading is close to 90 degrees - if (Math.abs(CesiumMath.PI_OVER_TWO - Math.abs(heading)) > CesiumMath.EPSILON8) { - //Calculate latitude of the second point - var M1 = calculateM(ellipticity, major, start.latitude); - var deltaM = distance * Math.cos(heading); - var M2 = M1 + deltaM; - latitude = calculateInverseM(M2, ellipticity, major); - - //Now find the longitude of the second point - var sigma1 = calculateSigma(ellipticity, start.latitude); - var sigma2 = calculateSigma(ellipticity, latitude); - deltaLongitude = Math.tan(heading) * (sigma2 - sigma1); - longitude = CesiumMath.negativePiToPi(start.longitude + deltaLongitude); - } else { //If heading is close to 90 degrees - latitude = start.latitude; - var localRad; - - if (ellipticity === 0.0) { // sphere - localRad = major * Math.cos(start.latitude); - } else { - var sinPhi = Math.sin(start.latitude); - localRad = major * Math.cos(start.latitude) / Math.sqrt(1 - ellipticitySquared * sinPhi * sinPhi); - } - - deltaLongitude = distance / localRad; - if (heading > 0.0) { - longitude = CesiumMath.negativePiToPi(start.longitude + deltaLongitude); - } else { - longitude = CesiumMath.negativePiToPi(start.longitude - deltaLongitude); - } - } - - if (defined(result)) { - result.longitude = longitude; - result.latitude = latitude; - result.height = 0; - - return result; - } - - return new Cartographic(longitude, latitude, 0); + return interpolateUsingSurfaceDistance(this._start, this._heading, distance, this._ellipsoid.maximumRadius, this._ellipticity, result); }; /** diff --git a/Specs/Core/EllipsoidRhumbLineSpec.js b/Specs/Core/EllipsoidRhumbLineSpec.js index 9bc1e7e65363..364a56402c5c 100644 --- a/Specs/Core/EllipsoidRhumbLineSpec.js +++ b/Specs/Core/EllipsoidRhumbLineSpec.js @@ -55,30 +55,30 @@ defineSuite([ }); it('can create using fromStartHeadingDistance function', function() { - var ellipsoid = new Ellipsoid(6, 6, 3); + var ellipsoid = Ellipsoid.WGS84; var start = new Cartographic(fifteenDegrees, fifteenDegrees); var heading = fifteenDegrees; - var distance = 6 * fifteenDegrees; + var distance = fifteenDegrees * ellipsoid.maximumRadius; var rhumb = EllipsoidRhumbLine.fromStartHeadingDistance(start, heading, distance, ellipsoid); expect(start).toEqual(rhumb.start); - expect(distance).toEqualEpsilon(rhumb.surfaceDistance, CesiumMath.EPSILON12); + expect(distance).toEqualEpsilon(rhumb.surfaceDistance, CesiumMath.EPSILON6); expect(heading).toEqualEpsilon(rhumb.heading, CesiumMath.EPSILON12); }); it('can create using fromStartHeadingDistance function with result', function() { - var ellipsoid = new Ellipsoid(6, 6, 3); + var ellipsoid = Ellipsoid.WGS84; var scratch = new EllipsoidRhumbLine(undefined, undefined, ellipsoid); var start = new Cartographic(fifteenDegrees, fifteenDegrees); var heading = fifteenDegrees; - var distance = 6 * fifteenDegrees; + var distance = fifteenDegrees * ellipsoid.maximumRadius; var rhumb = EllipsoidRhumbLine.fromStartHeadingDistance(start, heading, distance, ellipsoid, scratch); expect(rhumb).toBe(scratch); expect(rhumb.ellipsoid).toBe(ellipsoid); expect(start).toEqual(rhumb.start); - expect(distance).toEqualEpsilon(rhumb.surfaceDistance, CesiumMath.EPSILON12); + expect(distance).toEqualEpsilon(rhumb.surfaceDistance, CesiumMath.EPSILON6); expect(heading).toEqualEpsilon(rhumb.heading, CesiumMath.EPSILON12); }); From f687d2a0f5490d4f49941e3d0f8407303fa22313 Mon Sep 17 00:00:00 2001 From: Shehzan Mohammed Date: Fri, 18 Jan 2019 10:00:00 -0500 Subject: [PATCH 11/11] Document developer error for angle between start and end when using EllipsoidRhumbLine --- Source/Core/EllipsoidRhumbLine.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Source/Core/EllipsoidRhumbLine.js b/Source/Core/EllipsoidRhumbLine.js index a5d61b7390d2..9c1273f90b31 100644 --- a/Source/Core/EllipsoidRhumbLine.js +++ b/Source/Core/EllipsoidRhumbLine.js @@ -220,6 +220,8 @@ define([ * @param {Cartographic} [start] The initial planetodetic point on the path. * @param {Cartographic} [end] The final planetodetic point on the path. * @param {Ellipsoid} [ellipsoid=Ellipsoid.WGS84] The ellipsoid on which the rhumb line lies. + * + * @exception {DeveloperError} angle between start and end must be at least 0.0125 radians. */ function EllipsoidRhumbLine(start, end, ellipsoid) { var e = defaultValue(ellipsoid, Ellipsoid.WGS84);