Ellipsoid.js 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804
  1. import Cartesian3 from "./Cartesian3.js";
  2. import Cartographic from "./Cartographic.js";
  3. import Check from "./Check.js";
  4. import defaultValue from "./defaultValue.js";
  5. import defined from "./defined.js";
  6. import DeveloperError from "./DeveloperError.js";
  7. import CesiumMath from "./Math.js";
  8. import scaleToGeodeticSurface from "./scaleToGeodeticSurface.js";
  9. function initialize(ellipsoid, x, y, z) {
  10. x = defaultValue(x, 0.0);
  11. y = defaultValue(y, 0.0);
  12. z = defaultValue(z, 0.0);
  13. //>>includeStart('debug', pragmas.debug);
  14. Check.typeOf.number.greaterThanOrEquals("x", x, 0.0);
  15. Check.typeOf.number.greaterThanOrEquals("y", y, 0.0);
  16. Check.typeOf.number.greaterThanOrEquals("z", z, 0.0);
  17. //>>includeEnd('debug');
  18. ellipsoid._radii = new Cartesian3(x, y, z);
  19. ellipsoid._radiiSquared = new Cartesian3(x * x, y * y, z * z);
  20. ellipsoid._radiiToTheFourth = new Cartesian3(
  21. x * x * x * x,
  22. y * y * y * y,
  23. z * z * z * z
  24. );
  25. ellipsoid._oneOverRadii = new Cartesian3(
  26. x === 0.0 ? 0.0 : 1.0 / x,
  27. y === 0.0 ? 0.0 : 1.0 / y,
  28. z === 0.0 ? 0.0 : 1.0 / z
  29. );
  30. ellipsoid._oneOverRadiiSquared = new Cartesian3(
  31. x === 0.0 ? 0.0 : 1.0 / (x * x),
  32. y === 0.0 ? 0.0 : 1.0 / (y * y),
  33. z === 0.0 ? 0.0 : 1.0 / (z * z)
  34. );
  35. ellipsoid._minimumRadius = Math.min(x, y, z);
  36. ellipsoid._maximumRadius = Math.max(x, y, z);
  37. ellipsoid._centerToleranceSquared = CesiumMath.EPSILON1;
  38. if (ellipsoid._radiiSquared.z !== 0) {
  39. ellipsoid._squaredXOverSquaredZ =
  40. ellipsoid._radiiSquared.x / ellipsoid._radiiSquared.z;
  41. }
  42. }
  43. /**
  44. * A quadratic surface defined in Cartesian coordinates by the equation
  45. * <code>(x / a)^2 + (y / b)^2 + (z / c)^2 = 1</code>. Primarily used
  46. * by Cesium to represent the shape of planetary bodies.
  47. *
  48. * Rather than constructing this object directly, one of the provided
  49. * constants is normally used.
  50. * @alias Ellipsoid
  51. * @constructor
  52. *
  53. * @param {number} [x=0] The radius in the x direction.
  54. * @param {number} [y=0] The radius in the y direction.
  55. * @param {number} [z=0] The radius in the z direction.
  56. *
  57. * @exception {DeveloperError} All radii components must be greater than or equal to zero.
  58. *
  59. * @see Ellipsoid.fromCartesian3
  60. * @see Ellipsoid.WGS84
  61. * @see Ellipsoid.UNIT_SPHERE
  62. */
  63. function Ellipsoid(x, y, z) {
  64. this._radii = undefined;
  65. this._radiiSquared = undefined;
  66. this._radiiToTheFourth = undefined;
  67. this._oneOverRadii = undefined;
  68. this._oneOverRadiiSquared = undefined;
  69. this._minimumRadius = undefined;
  70. this._maximumRadius = undefined;
  71. this._centerToleranceSquared = undefined;
  72. this._squaredXOverSquaredZ = undefined;
  73. initialize(this, x, y, z);
  74. }
  75. Object.defineProperties(Ellipsoid.prototype, {
  76. /**
  77. * Gets the radii of the ellipsoid.
  78. * @memberof Ellipsoid.prototype
  79. * @type {Cartesian3}
  80. * @readonly
  81. */
  82. radii: {
  83. get: function () {
  84. return this._radii;
  85. },
  86. },
  87. /**
  88. * Gets the squared radii of the ellipsoid.
  89. * @memberof Ellipsoid.prototype
  90. * @type {Cartesian3}
  91. * @readonly
  92. */
  93. radiiSquared: {
  94. get: function () {
  95. return this._radiiSquared;
  96. },
  97. },
  98. /**
  99. * Gets the radii of the ellipsoid raise to the fourth power.
  100. * @memberof Ellipsoid.prototype
  101. * @type {Cartesian3}
  102. * @readonly
  103. */
  104. radiiToTheFourth: {
  105. get: function () {
  106. return this._radiiToTheFourth;
  107. },
  108. },
  109. /**
  110. * Gets one over the radii of the ellipsoid.
  111. * @memberof Ellipsoid.prototype
  112. * @type {Cartesian3}
  113. * @readonly
  114. */
  115. oneOverRadii: {
  116. get: function () {
  117. return this._oneOverRadii;
  118. },
  119. },
  120. /**
  121. * Gets one over the squared radii of the ellipsoid.
  122. * @memberof Ellipsoid.prototype
  123. * @type {Cartesian3}
  124. * @readonly
  125. */
  126. oneOverRadiiSquared: {
  127. get: function () {
  128. return this._oneOverRadiiSquared;
  129. },
  130. },
  131. /**
  132. * Gets the minimum radius of the ellipsoid.
  133. * @memberof Ellipsoid.prototype
  134. * @type {number}
  135. * @readonly
  136. */
  137. minimumRadius: {
  138. get: function () {
  139. return this._minimumRadius;
  140. },
  141. },
  142. /**
  143. * Gets the maximum radius of the ellipsoid.
  144. * @memberof Ellipsoid.prototype
  145. * @type {number}
  146. * @readonly
  147. */
  148. maximumRadius: {
  149. get: function () {
  150. return this._maximumRadius;
  151. },
  152. },
  153. });
  154. /**
  155. * Duplicates an Ellipsoid instance.
  156. *
  157. * @param {Ellipsoid} ellipsoid The ellipsoid to duplicate.
  158. * @param {Ellipsoid} [result] The object onto which to store the result, or undefined if a new
  159. * instance should be created.
  160. * @returns {Ellipsoid} The cloned Ellipsoid. (Returns undefined if ellipsoid is undefined)
  161. */
  162. Ellipsoid.clone = function (ellipsoid, result) {
  163. if (!defined(ellipsoid)) {
  164. return undefined;
  165. }
  166. const radii = ellipsoid._radii;
  167. if (!defined(result)) {
  168. return new Ellipsoid(radii.x, radii.y, radii.z);
  169. }
  170. Cartesian3.clone(radii, result._radii);
  171. Cartesian3.clone(ellipsoid._radiiSquared, result._radiiSquared);
  172. Cartesian3.clone(ellipsoid._radiiToTheFourth, result._radiiToTheFourth);
  173. Cartesian3.clone(ellipsoid._oneOverRadii, result._oneOverRadii);
  174. Cartesian3.clone(ellipsoid._oneOverRadiiSquared, result._oneOverRadiiSquared);
  175. result._minimumRadius = ellipsoid._minimumRadius;
  176. result._maximumRadius = ellipsoid._maximumRadius;
  177. result._centerToleranceSquared = ellipsoid._centerToleranceSquared;
  178. return result;
  179. };
  180. /**
  181. * Computes an Ellipsoid from a Cartesian specifying the radii in x, y, and z directions.
  182. *
  183. * @param {Cartesian3} [cartesian=Cartesian3.ZERO] The ellipsoid's radius in the x, y, and z directions.
  184. * @param {Ellipsoid} [result] The object onto which to store the result, or undefined if a new
  185. * instance should be created.
  186. * @returns {Ellipsoid} A new Ellipsoid instance.
  187. *
  188. * @exception {DeveloperError} All radii components must be greater than or equal to zero.
  189. *
  190. * @see Ellipsoid.WGS84
  191. * @see Ellipsoid.UNIT_SPHERE
  192. */
  193. Ellipsoid.fromCartesian3 = function (cartesian, result) {
  194. if (!defined(result)) {
  195. result = new Ellipsoid();
  196. }
  197. if (!defined(cartesian)) {
  198. return result;
  199. }
  200. initialize(result, cartesian.x, cartesian.y, cartesian.z);
  201. return result;
  202. };
  203. /**
  204. * An Ellipsoid instance initialized to the WGS84 standard.
  205. *
  206. * @type {Ellipsoid}
  207. * @constant
  208. */
  209. Ellipsoid.WGS84 = Object.freeze(
  210. new Ellipsoid(6378137.0, 6378137.0, 6356752.3142451793)
  211. );
  212. /**
  213. * An Ellipsoid instance initialized to radii of (1.0, 1.0, 1.0).
  214. *
  215. * @type {Ellipsoid}
  216. * @constant
  217. */
  218. Ellipsoid.UNIT_SPHERE = Object.freeze(new Ellipsoid(1.0, 1.0, 1.0));
  219. /**
  220. * An Ellipsoid instance initialized to a sphere with the lunar radius.
  221. *
  222. * @type {Ellipsoid}
  223. * @constant
  224. */
  225. Ellipsoid.MOON = Object.freeze(
  226. new Ellipsoid(
  227. CesiumMath.LUNAR_RADIUS,
  228. CesiumMath.LUNAR_RADIUS,
  229. CesiumMath.LUNAR_RADIUS
  230. )
  231. );
  232. /**
  233. * Duplicates an Ellipsoid instance.
  234. *
  235. * @param {Ellipsoid} [result] The object onto which to store the result, or undefined if a new
  236. * instance should be created.
  237. * @returns {Ellipsoid} The cloned Ellipsoid.
  238. */
  239. Ellipsoid.prototype.clone = function (result) {
  240. return Ellipsoid.clone(this, result);
  241. };
  242. /**
  243. * The number of elements used to pack the object into an array.
  244. * @type {number}
  245. */
  246. Ellipsoid.packedLength = Cartesian3.packedLength;
  247. /**
  248. * Stores the provided instance into the provided array.
  249. *
  250. * @param {Ellipsoid} value The value to pack.
  251. * @param {number[]} array The array to pack into.
  252. * @param {number} [startingIndex=0] The index into the array at which to start packing the elements.
  253. *
  254. * @returns {number[]} The array that was packed into
  255. */
  256. Ellipsoid.pack = function (value, array, startingIndex) {
  257. //>>includeStart('debug', pragmas.debug);
  258. Check.typeOf.object("value", value);
  259. Check.defined("array", array);
  260. //>>includeEnd('debug');
  261. startingIndex = defaultValue(startingIndex, 0);
  262. Cartesian3.pack(value._radii, array, startingIndex);
  263. return array;
  264. };
  265. /**
  266. * Retrieves an instance from a packed array.
  267. *
  268. * @param {number[]} array The packed array.
  269. * @param {number} [startingIndex=0] The starting index of the element to be unpacked.
  270. * @param {Ellipsoid} [result] The object into which to store the result.
  271. * @returns {Ellipsoid} The modified result parameter or a new Ellipsoid instance if one was not provided.
  272. */
  273. Ellipsoid.unpack = function (array, startingIndex, result) {
  274. //>>includeStart('debug', pragmas.debug);
  275. Check.defined("array", array);
  276. //>>includeEnd('debug');
  277. startingIndex = defaultValue(startingIndex, 0);
  278. const radii = Cartesian3.unpack(array, startingIndex);
  279. return Ellipsoid.fromCartesian3(radii, result);
  280. };
  281. /**
  282. * Computes the unit vector directed from the center of this ellipsoid toward the provided Cartesian position.
  283. * @function
  284. *
  285. * @param {Cartesian3} cartesian The Cartesian for which to to determine the geocentric normal.
  286. * @param {Cartesian3} [result] The object onto which to store the result.
  287. * @returns {Cartesian3} The modified result parameter or a new Cartesian3 instance if none was provided.
  288. */
  289. Ellipsoid.prototype.geocentricSurfaceNormal = Cartesian3.normalize;
  290. /**
  291. * Computes the normal of the plane tangent to the surface of the ellipsoid at the provided position.
  292. *
  293. * @param {Cartographic} cartographic The cartographic position for which to to determine the geodetic normal.
  294. * @param {Cartesian3} [result] The object onto which to store the result.
  295. * @returns {Cartesian3} The modified result parameter or a new Cartesian3 instance if none was provided.
  296. */
  297. Ellipsoid.prototype.geodeticSurfaceNormalCartographic = function (
  298. cartographic,
  299. result
  300. ) {
  301. //>>includeStart('debug', pragmas.debug);
  302. Check.typeOf.object("cartographic", cartographic);
  303. //>>includeEnd('debug');
  304. const longitude = cartographic.longitude;
  305. const latitude = cartographic.latitude;
  306. const cosLatitude = Math.cos(latitude);
  307. const x = cosLatitude * Math.cos(longitude);
  308. const y = cosLatitude * Math.sin(longitude);
  309. const z = Math.sin(latitude);
  310. if (!defined(result)) {
  311. result = new Cartesian3();
  312. }
  313. result.x = x;
  314. result.y = y;
  315. result.z = z;
  316. return Cartesian3.normalize(result, result);
  317. };
  318. /**
  319. * Computes the normal of the plane tangent to the surface of the ellipsoid at the provided position.
  320. *
  321. * @param {Cartesian3} cartesian The Cartesian position for which to to determine the surface normal.
  322. * @param {Cartesian3} [result] The object onto which to store the result.
  323. * @returns {Cartesian3} The modified result parameter or a new Cartesian3 instance if none was provided, or undefined if a normal cannot be found.
  324. */
  325. Ellipsoid.prototype.geodeticSurfaceNormal = function (cartesian, result) {
  326. if (
  327. Cartesian3.equalsEpsilon(cartesian, Cartesian3.ZERO, CesiumMath.EPSILON14)
  328. ) {
  329. return undefined;
  330. }
  331. if (!defined(result)) {
  332. result = new Cartesian3();
  333. }
  334. result = Cartesian3.multiplyComponents(
  335. cartesian,
  336. this._oneOverRadiiSquared,
  337. result
  338. );
  339. return Cartesian3.normalize(result, result);
  340. };
  341. const cartographicToCartesianNormal = new Cartesian3();
  342. const cartographicToCartesianK = new Cartesian3();
  343. /**
  344. * Converts the provided cartographic to Cartesian representation.
  345. *
  346. * @param {Cartographic} cartographic The cartographic position.
  347. * @param {Cartesian3} [result] The object onto which to store the result.
  348. * @returns {Cartesian3} The modified result parameter or a new Cartesian3 instance if none was provided.
  349. *
  350. * @example
  351. * //Create a Cartographic and determine it's Cartesian representation on a WGS84 ellipsoid.
  352. * const position = new Cesium.Cartographic(Cesium.Math.toRadians(21), Cesium.Math.toRadians(78), 5000);
  353. * const cartesianPosition = Cesium.Ellipsoid.WGS84.cartographicToCartesian(position);
  354. */
  355. Ellipsoid.prototype.cartographicToCartesian = function (cartographic, result) {
  356. //`cartographic is required` is thrown from geodeticSurfaceNormalCartographic.
  357. const n = cartographicToCartesianNormal;
  358. const k = cartographicToCartesianK;
  359. this.geodeticSurfaceNormalCartographic(cartographic, n);
  360. Cartesian3.multiplyComponents(this._radiiSquared, n, k);
  361. const gamma = Math.sqrt(Cartesian3.dot(n, k));
  362. Cartesian3.divideByScalar(k, gamma, k);
  363. Cartesian3.multiplyByScalar(n, cartographic.height, n);
  364. if (!defined(result)) {
  365. result = new Cartesian3();
  366. }
  367. return Cartesian3.add(k, n, result);
  368. };
  369. /**
  370. * Converts the provided array of cartographics to an array of Cartesians.
  371. *
  372. * @param {Cartographic[]} cartographics An array of cartographic positions.
  373. * @param {Cartesian3[]} [result] The object onto which to store the result.
  374. * @returns {Cartesian3[]} The modified result parameter or a new Array instance if none was provided.
  375. *
  376. * @example
  377. * //Convert an array of Cartographics and determine their Cartesian representation on a WGS84 ellipsoid.
  378. * const positions = [new Cesium.Cartographic(Cesium.Math.toRadians(21), Cesium.Math.toRadians(78), 0),
  379. * new Cesium.Cartographic(Cesium.Math.toRadians(21.321), Cesium.Math.toRadians(78.123), 100),
  380. * new Cesium.Cartographic(Cesium.Math.toRadians(21.645), Cesium.Math.toRadians(78.456), 250)];
  381. * const cartesianPositions = Cesium.Ellipsoid.WGS84.cartographicArrayToCartesianArray(positions);
  382. */
  383. Ellipsoid.prototype.cartographicArrayToCartesianArray = function (
  384. cartographics,
  385. result
  386. ) {
  387. //>>includeStart('debug', pragmas.debug);
  388. Check.defined("cartographics", cartographics);
  389. //>>includeEnd('debug')
  390. const length = cartographics.length;
  391. if (!defined(result)) {
  392. result = new Array(length);
  393. } else {
  394. result.length = length;
  395. }
  396. for (let i = 0; i < length; i++) {
  397. result[i] = this.cartographicToCartesian(cartographics[i], result[i]);
  398. }
  399. return result;
  400. };
  401. const cartesianToCartographicN = new Cartesian3();
  402. const cartesianToCartographicP = new Cartesian3();
  403. const cartesianToCartographicH = new Cartesian3();
  404. /**
  405. * Converts the provided cartesian to cartographic representation.
  406. * The cartesian is undefined at the center of the ellipsoid.
  407. *
  408. * @param {Cartesian3} cartesian The Cartesian position to convert to cartographic representation.
  409. * @param {Cartographic} [result] The object onto which to store the result.
  410. * @returns {Cartographic} The modified result parameter, new Cartographic instance if none was provided, or undefined if the cartesian is at the center of the ellipsoid.
  411. *
  412. * @example
  413. * //Create a Cartesian and determine it's Cartographic representation on a WGS84 ellipsoid.
  414. * const position = new Cesium.Cartesian3(17832.12, 83234.52, 952313.73);
  415. * const cartographicPosition = Cesium.Ellipsoid.WGS84.cartesianToCartographic(position);
  416. */
  417. Ellipsoid.prototype.cartesianToCartographic = function (cartesian, result) {
  418. //`cartesian is required.` is thrown from scaleToGeodeticSurface
  419. const p = this.scaleToGeodeticSurface(cartesian, cartesianToCartographicP);
  420. if (!defined(p)) {
  421. return undefined;
  422. }
  423. const n = this.geodeticSurfaceNormal(p, cartesianToCartographicN);
  424. const h = Cartesian3.subtract(cartesian, p, cartesianToCartographicH);
  425. const longitude = Math.atan2(n.y, n.x);
  426. const latitude = Math.asin(n.z);
  427. const height =
  428. CesiumMath.sign(Cartesian3.dot(h, cartesian)) * Cartesian3.magnitude(h);
  429. if (!defined(result)) {
  430. return new Cartographic(longitude, latitude, height);
  431. }
  432. result.longitude = longitude;
  433. result.latitude = latitude;
  434. result.height = height;
  435. return result;
  436. };
  437. /**
  438. * Converts the provided array of cartesians to an array of cartographics.
  439. *
  440. * @param {Cartesian3[]} cartesians An array of Cartesian positions.
  441. * @param {Cartographic[]} [result] The object onto which to store the result.
  442. * @returns {Cartographic[]} The modified result parameter or a new Array instance if none was provided.
  443. *
  444. * @example
  445. * //Create an array of Cartesians and determine their Cartographic representation on a WGS84 ellipsoid.
  446. * const positions = [new Cesium.Cartesian3(17832.12, 83234.52, 952313.73),
  447. * new Cesium.Cartesian3(17832.13, 83234.53, 952313.73),
  448. * new Cesium.Cartesian3(17832.14, 83234.54, 952313.73)]
  449. * const cartographicPositions = Cesium.Ellipsoid.WGS84.cartesianArrayToCartographicArray(positions);
  450. */
  451. Ellipsoid.prototype.cartesianArrayToCartographicArray = function (
  452. cartesians,
  453. result
  454. ) {
  455. //>>includeStart('debug', pragmas.debug);
  456. Check.defined("cartesians", cartesians);
  457. //>>includeEnd('debug');
  458. const length = cartesians.length;
  459. if (!defined(result)) {
  460. result = new Array(length);
  461. } else {
  462. result.length = length;
  463. }
  464. for (let i = 0; i < length; ++i) {
  465. result[i] = this.cartesianToCartographic(cartesians[i], result[i]);
  466. }
  467. return result;
  468. };
  469. /**
  470. * Scales the provided Cartesian position along the geodetic surface normal
  471. * so that it is on the surface of this ellipsoid. If the position is
  472. * at the center of the ellipsoid, this function returns undefined.
  473. *
  474. * @param {Cartesian3} cartesian The Cartesian position to scale.
  475. * @param {Cartesian3} [result] The object onto which to store the result.
  476. * @returns {Cartesian3} The modified result parameter, a new Cartesian3 instance if none was provided, or undefined if the position is at the center.
  477. */
  478. Ellipsoid.prototype.scaleToGeodeticSurface = function (cartesian, result) {
  479. return scaleToGeodeticSurface(
  480. cartesian,
  481. this._oneOverRadii,
  482. this._oneOverRadiiSquared,
  483. this._centerToleranceSquared,
  484. result
  485. );
  486. };
  487. /**
  488. * Scales the provided Cartesian position along the geocentric surface normal
  489. * so that it is on the surface of this ellipsoid.
  490. *
  491. * @param {Cartesian3} cartesian The Cartesian position to scale.
  492. * @param {Cartesian3} [result] The object onto which to store the result.
  493. * @returns {Cartesian3} The modified result parameter or a new Cartesian3 instance if none was provided.
  494. */
  495. Ellipsoid.prototype.scaleToGeocentricSurface = function (cartesian, result) {
  496. //>>includeStart('debug', pragmas.debug);
  497. Check.typeOf.object("cartesian", cartesian);
  498. //>>includeEnd('debug');
  499. if (!defined(result)) {
  500. result = new Cartesian3();
  501. }
  502. const positionX = cartesian.x;
  503. const positionY = cartesian.y;
  504. const positionZ = cartesian.z;
  505. const oneOverRadiiSquared = this._oneOverRadiiSquared;
  506. const beta =
  507. 1.0 /
  508. Math.sqrt(
  509. positionX * positionX * oneOverRadiiSquared.x +
  510. positionY * positionY * oneOverRadiiSquared.y +
  511. positionZ * positionZ * oneOverRadiiSquared.z
  512. );
  513. return Cartesian3.multiplyByScalar(cartesian, beta, result);
  514. };
  515. /**
  516. * Transforms a Cartesian X, Y, Z position to the ellipsoid-scaled space by multiplying
  517. * its components by the result of {@link Ellipsoid#oneOverRadii}.
  518. *
  519. * @param {Cartesian3} position The position to transform.
  520. * @param {Cartesian3} [result] The position to which to copy the result, or undefined to create and
  521. * return a new instance.
  522. * @returns {Cartesian3} The position expressed in the scaled space. The returned instance is the
  523. * one passed as the result parameter if it is not undefined, or a new instance of it is.
  524. */
  525. Ellipsoid.prototype.transformPositionToScaledSpace = function (
  526. position,
  527. result
  528. ) {
  529. if (!defined(result)) {
  530. result = new Cartesian3();
  531. }
  532. return Cartesian3.multiplyComponents(position, this._oneOverRadii, result);
  533. };
  534. /**
  535. * Transforms a Cartesian X, Y, Z position from the ellipsoid-scaled space by multiplying
  536. * its components by the result of {@link Ellipsoid#radii}.
  537. *
  538. * @param {Cartesian3} position The position to transform.
  539. * @param {Cartesian3} [result] The position to which to copy the result, or undefined to create and
  540. * return a new instance.
  541. * @returns {Cartesian3} The position expressed in the unscaled space. The returned instance is the
  542. * one passed as the result parameter if it is not undefined, or a new instance of it is.
  543. */
  544. Ellipsoid.prototype.transformPositionFromScaledSpace = function (
  545. position,
  546. result
  547. ) {
  548. if (!defined(result)) {
  549. result = new Cartesian3();
  550. }
  551. return Cartesian3.multiplyComponents(position, this._radii, result);
  552. };
  553. /**
  554. * Compares this Ellipsoid against the provided Ellipsoid componentwise and returns
  555. * <code>true</code> if they are equal, <code>false</code> otherwise.
  556. *
  557. * @param {Ellipsoid} [right] The other Ellipsoid.
  558. * @returns {boolean} <code>true</code> if they are equal, <code>false</code> otherwise.
  559. */
  560. Ellipsoid.prototype.equals = function (right) {
  561. return (
  562. this === right ||
  563. (defined(right) && Cartesian3.equals(this._radii, right._radii))
  564. );
  565. };
  566. /**
  567. * Creates a string representing this Ellipsoid in the format '(radii.x, radii.y, radii.z)'.
  568. *
  569. * @returns {string} A string representing this ellipsoid in the format '(radii.x, radii.y, radii.z)'.
  570. */
  571. Ellipsoid.prototype.toString = function () {
  572. return this._radii.toString();
  573. };
  574. /**
  575. * Computes a point which is the intersection of the surface normal with the z-axis.
  576. *
  577. * @param {Cartesian3} position the position. must be on the surface of the ellipsoid.
  578. * @param {number} [buffer = 0.0] A buffer to subtract from the ellipsoid size when checking if the point is inside the ellipsoid.
  579. * In earth case, with common earth datums, there is no need for this buffer since the intersection point is always (relatively) very close to the center.
  580. * In WGS84 datum, intersection point is at max z = +-42841.31151331382 (0.673% of z-axis).
  581. * Intersection point could be outside the ellipsoid if the ratio of MajorAxis / AxisOfRotation is bigger than the square root of 2
  582. * @param {Cartesian3} [result] The cartesian to which to copy the result, or undefined to create and
  583. * return a new instance.
  584. * @returns {Cartesian3 | undefined} the intersection point if it's inside the ellipsoid, undefined otherwise
  585. *
  586. * @exception {DeveloperError} position is required.
  587. * @exception {DeveloperError} Ellipsoid must be an ellipsoid of revolution (radii.x == radii.y).
  588. * @exception {DeveloperError} Ellipsoid.radii.z must be greater than 0.
  589. */
  590. Ellipsoid.prototype.getSurfaceNormalIntersectionWithZAxis = function (
  591. position,
  592. buffer,
  593. result
  594. ) {
  595. //>>includeStart('debug', pragmas.debug);
  596. Check.typeOf.object("position", position);
  597. if (
  598. !CesiumMath.equalsEpsilon(
  599. this._radii.x,
  600. this._radii.y,
  601. CesiumMath.EPSILON15
  602. )
  603. ) {
  604. throw new DeveloperError(
  605. "Ellipsoid must be an ellipsoid of revolution (radii.x == radii.y)"
  606. );
  607. }
  608. Check.typeOf.number.greaterThan("Ellipsoid.radii.z", this._radii.z, 0);
  609. //>>includeEnd('debug');
  610. buffer = defaultValue(buffer, 0.0);
  611. const squaredXOverSquaredZ = this._squaredXOverSquaredZ;
  612. if (!defined(result)) {
  613. result = new Cartesian3();
  614. }
  615. result.x = 0.0;
  616. result.y = 0.0;
  617. result.z = position.z * (1 - squaredXOverSquaredZ);
  618. if (Math.abs(result.z) >= this._radii.z - buffer) {
  619. return undefined;
  620. }
  621. return result;
  622. };
  623. const abscissas = [
  624. 0.14887433898163,
  625. 0.43339539412925,
  626. 0.67940956829902,
  627. 0.86506336668898,
  628. 0.97390652851717,
  629. 0.0,
  630. ];
  631. const weights = [
  632. 0.29552422471475,
  633. 0.26926671930999,
  634. 0.21908636251598,
  635. 0.14945134915058,
  636. 0.066671344308684,
  637. 0.0,
  638. ];
  639. /**
  640. * Compute the 10th order Gauss-Legendre Quadrature of the given definite integral.
  641. *
  642. * @param {number} a The lower bound for the integration.
  643. * @param {number} b The upper bound for the integration.
  644. * @param {Ellipsoid~RealValuedScalarFunction} func The function to integrate.
  645. * @returns {number} The value of the integral of the given function over the given domain.
  646. *
  647. * @private
  648. */
  649. function gaussLegendreQuadrature(a, b, func) {
  650. //>>includeStart('debug', pragmas.debug);
  651. Check.typeOf.number("a", a);
  652. Check.typeOf.number("b", b);
  653. Check.typeOf.func("func", func);
  654. //>>includeEnd('debug');
  655. // The range is half of the normal range since the five weights add to one (ten weights add to two).
  656. // The values of the abscissas are multiplied by two to account for this.
  657. const xMean = 0.5 * (b + a);
  658. const xRange = 0.5 * (b - a);
  659. let sum = 0.0;
  660. for (let i = 0; i < 5; i++) {
  661. const dx = xRange * abscissas[i];
  662. sum += weights[i] * (func(xMean + dx) + func(xMean - dx));
  663. }
  664. // Scale the sum to the range of x.
  665. sum *= xRange;
  666. return sum;
  667. }
  668. /**
  669. * A real valued scalar function.
  670. * @callback Ellipsoid~RealValuedScalarFunction
  671. *
  672. * @param {number} x The value used to evaluate the function.
  673. * @returns {number} The value of the function at x.
  674. *
  675. * @private
  676. */
  677. /**
  678. * Computes an approximation of the surface area of a rectangle on the surface of an ellipsoid using
  679. * Gauss-Legendre 10th order quadrature.
  680. *
  681. * @param {Rectangle} rectangle The rectangle used for computing the surface area.
  682. * @returns {number} The approximate area of the rectangle on the surface of this ellipsoid.
  683. */
  684. Ellipsoid.prototype.surfaceArea = function (rectangle) {
  685. //>>includeStart('debug', pragmas.debug);
  686. Check.typeOf.object("rectangle", rectangle);
  687. //>>includeEnd('debug');
  688. const minLongitude = rectangle.west;
  689. let maxLongitude = rectangle.east;
  690. const minLatitude = rectangle.south;
  691. const maxLatitude = rectangle.north;
  692. while (maxLongitude < minLongitude) {
  693. maxLongitude += CesiumMath.TWO_PI;
  694. }
  695. const radiiSquared = this._radiiSquared;
  696. const a2 = radiiSquared.x;
  697. const b2 = radiiSquared.y;
  698. const c2 = radiiSquared.z;
  699. const a2b2 = a2 * b2;
  700. return gaussLegendreQuadrature(minLatitude, maxLatitude, function (lat) {
  701. // phi represents the angle measured from the north pole
  702. // sin(phi) = sin(pi / 2 - lat) = cos(lat), cos(phi) is similar
  703. const sinPhi = Math.cos(lat);
  704. const cosPhi = Math.sin(lat);
  705. return (
  706. Math.cos(lat) *
  707. gaussLegendreQuadrature(minLongitude, maxLongitude, function (lon) {
  708. const cosTheta = Math.cos(lon);
  709. const sinTheta = Math.sin(lon);
  710. return Math.sqrt(
  711. a2b2 * cosPhi * cosPhi +
  712. c2 *
  713. (b2 * cosTheta * cosTheta + a2 * sinTheta * sinTheta) *
  714. sinPhi *
  715. sinPhi
  716. );
  717. })
  718. );
  719. });
  720. };
  721. export default Ellipsoid;