Transforms.js 39 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132
  1. import Cartesian2 from "./Cartesian2.js";
  2. import Cartesian3 from "./Cartesian3.js";
  3. import Cartesian4 from "./Cartesian4.js";
  4. import Cartographic from "./Cartographic.js";
  5. import Check from "./Check.js";
  6. import defaultValue from "./defaultValue.js";
  7. import defined from "./defined.js";
  8. import DeveloperError from "./DeveloperError.js";
  9. import EarthOrientationParameters from "./EarthOrientationParameters.js";
  10. import EarthOrientationParametersSample from "./EarthOrientationParametersSample.js";
  11. import Ellipsoid from "./Ellipsoid.js";
  12. import HeadingPitchRoll from "./HeadingPitchRoll.js";
  13. import Iau2006XysData from "./Iau2006XysData.js";
  14. import Iau2006XysSample from "./Iau2006XysSample.js";
  15. import JulianDate from "./JulianDate.js";
  16. import CesiumMath from "./Math.js";
  17. import Matrix3 from "./Matrix3.js";
  18. import Matrix4 from "./Matrix4.js";
  19. import Quaternion from "./Quaternion.js";
  20. import TimeConstants from "./TimeConstants.js";
  21. /**
  22. * Contains functions for transforming positions to various reference frames.
  23. *
  24. * @namespace Transforms
  25. */
  26. const Transforms = {};
  27. const vectorProductLocalFrame = {
  28. up: {
  29. south: "east",
  30. north: "west",
  31. west: "south",
  32. east: "north",
  33. },
  34. down: {
  35. south: "west",
  36. north: "east",
  37. west: "north",
  38. east: "south",
  39. },
  40. south: {
  41. up: "west",
  42. down: "east",
  43. west: "down",
  44. east: "up",
  45. },
  46. north: {
  47. up: "east",
  48. down: "west",
  49. west: "up",
  50. east: "down",
  51. },
  52. west: {
  53. up: "north",
  54. down: "south",
  55. north: "down",
  56. south: "up",
  57. },
  58. east: {
  59. up: "south",
  60. down: "north",
  61. north: "up",
  62. south: "down",
  63. },
  64. };
  65. const degeneratePositionLocalFrame = {
  66. north: [-1, 0, 0],
  67. east: [0, 1, 0],
  68. up: [0, 0, 1],
  69. south: [1, 0, 0],
  70. west: [0, -1, 0],
  71. down: [0, 0, -1],
  72. };
  73. const localFrameToFixedFrameCache = {};
  74. const scratchCalculateCartesian = {
  75. east: new Cartesian3(),
  76. north: new Cartesian3(),
  77. up: new Cartesian3(),
  78. west: new Cartesian3(),
  79. south: new Cartesian3(),
  80. down: new Cartesian3(),
  81. };
  82. let scratchFirstCartesian = new Cartesian3();
  83. let scratchSecondCartesian = new Cartesian3();
  84. let scratchThirdCartesian = new Cartesian3();
  85. /**
  86. * Generates a function that computes a 4x4 transformation matrix from a reference frame
  87. * centered at the provided origin to the provided ellipsoid's fixed reference frame.
  88. * @param {String} firstAxis name of the first axis of the local reference frame. Must be
  89. * 'east', 'north', 'up', 'west', 'south' or 'down'.
  90. * @param {String} secondAxis name of the second axis of the local reference frame. Must be
  91. * 'east', 'north', 'up', 'west', 'south' or 'down'.
  92. * @return {Transforms.LocalFrameToFixedFrame} The function that will computes a
  93. * 4x4 transformation matrix from a reference frame, with first axis and second axis compliant with the parameters,
  94. */
  95. Transforms.localFrameToFixedFrameGenerator = function (firstAxis, secondAxis) {
  96. if (
  97. !vectorProductLocalFrame.hasOwnProperty(firstAxis) ||
  98. !vectorProductLocalFrame[firstAxis].hasOwnProperty(secondAxis)
  99. ) {
  100. throw new DeveloperError(
  101. "firstAxis and secondAxis must be east, north, up, west, south or down."
  102. );
  103. }
  104. const thirdAxis = vectorProductLocalFrame[firstAxis][secondAxis];
  105. /**
  106. * Computes a 4x4 transformation matrix from a reference frame
  107. * centered at the provided origin to the provided ellipsoid's fixed reference frame.
  108. * @callback Transforms.LocalFrameToFixedFrame
  109. * @param {Cartesian3} origin The center point of the local reference frame.
  110. * @param {Ellipsoid} [ellipsoid=Ellipsoid.WGS84] The ellipsoid whose fixed frame is used in the transformation.
  111. * @param {Matrix4} [result] The object onto which to store the result.
  112. * @returns {Matrix4} The modified result parameter or a new Matrix4 instance if none was provided.
  113. */
  114. let resultat;
  115. const hashAxis = firstAxis + secondAxis;
  116. if (defined(localFrameToFixedFrameCache[hashAxis])) {
  117. resultat = localFrameToFixedFrameCache[hashAxis];
  118. } else {
  119. resultat = function (origin, ellipsoid, result) {
  120. //>>includeStart('debug', pragmas.debug);
  121. if (!defined(origin)) {
  122. throw new DeveloperError("origin is required.");
  123. }
  124. //>>includeEnd('debug');
  125. if (!defined(result)) {
  126. result = new Matrix4();
  127. }
  128. if (
  129. Cartesian3.equalsEpsilon(origin, Cartesian3.ZERO, CesiumMath.EPSILON14)
  130. ) {
  131. // If x, y, and z are zero, use the degenerate local frame, which is a special case
  132. Cartesian3.unpack(
  133. degeneratePositionLocalFrame[firstAxis],
  134. 0,
  135. scratchFirstCartesian
  136. );
  137. Cartesian3.unpack(
  138. degeneratePositionLocalFrame[secondAxis],
  139. 0,
  140. scratchSecondCartesian
  141. );
  142. Cartesian3.unpack(
  143. degeneratePositionLocalFrame[thirdAxis],
  144. 0,
  145. scratchThirdCartesian
  146. );
  147. } else if (
  148. CesiumMath.equalsEpsilon(origin.x, 0.0, CesiumMath.EPSILON14) &&
  149. CesiumMath.equalsEpsilon(origin.y, 0.0, CesiumMath.EPSILON14)
  150. ) {
  151. // If x and y are zero, assume origin is at a pole, which is a special case.
  152. const sign = CesiumMath.sign(origin.z);
  153. Cartesian3.unpack(
  154. degeneratePositionLocalFrame[firstAxis],
  155. 0,
  156. scratchFirstCartesian
  157. );
  158. if (firstAxis !== "east" && firstAxis !== "west") {
  159. Cartesian3.multiplyByScalar(
  160. scratchFirstCartesian,
  161. sign,
  162. scratchFirstCartesian
  163. );
  164. }
  165. Cartesian3.unpack(
  166. degeneratePositionLocalFrame[secondAxis],
  167. 0,
  168. scratchSecondCartesian
  169. );
  170. if (secondAxis !== "east" && secondAxis !== "west") {
  171. Cartesian3.multiplyByScalar(
  172. scratchSecondCartesian,
  173. sign,
  174. scratchSecondCartesian
  175. );
  176. }
  177. Cartesian3.unpack(
  178. degeneratePositionLocalFrame[thirdAxis],
  179. 0,
  180. scratchThirdCartesian
  181. );
  182. if (thirdAxis !== "east" && thirdAxis !== "west") {
  183. Cartesian3.multiplyByScalar(
  184. scratchThirdCartesian,
  185. sign,
  186. scratchThirdCartesian
  187. );
  188. }
  189. } else {
  190. ellipsoid = defaultValue(ellipsoid, Ellipsoid.WGS84);
  191. ellipsoid.geodeticSurfaceNormal(origin, scratchCalculateCartesian.up);
  192. const up = scratchCalculateCartesian.up;
  193. const east = scratchCalculateCartesian.east;
  194. east.x = -origin.y;
  195. east.y = origin.x;
  196. east.z = 0.0;
  197. Cartesian3.normalize(east, scratchCalculateCartesian.east);
  198. Cartesian3.cross(up, east, scratchCalculateCartesian.north);
  199. Cartesian3.multiplyByScalar(
  200. scratchCalculateCartesian.up,
  201. -1,
  202. scratchCalculateCartesian.down
  203. );
  204. Cartesian3.multiplyByScalar(
  205. scratchCalculateCartesian.east,
  206. -1,
  207. scratchCalculateCartesian.west
  208. );
  209. Cartesian3.multiplyByScalar(
  210. scratchCalculateCartesian.north,
  211. -1,
  212. scratchCalculateCartesian.south
  213. );
  214. scratchFirstCartesian = scratchCalculateCartesian[firstAxis];
  215. scratchSecondCartesian = scratchCalculateCartesian[secondAxis];
  216. scratchThirdCartesian = scratchCalculateCartesian[thirdAxis];
  217. }
  218. result[0] = scratchFirstCartesian.x;
  219. result[1] = scratchFirstCartesian.y;
  220. result[2] = scratchFirstCartesian.z;
  221. result[3] = 0.0;
  222. result[4] = scratchSecondCartesian.x;
  223. result[5] = scratchSecondCartesian.y;
  224. result[6] = scratchSecondCartesian.z;
  225. result[7] = 0.0;
  226. result[8] = scratchThirdCartesian.x;
  227. result[9] = scratchThirdCartesian.y;
  228. result[10] = scratchThirdCartesian.z;
  229. result[11] = 0.0;
  230. result[12] = origin.x;
  231. result[13] = origin.y;
  232. result[14] = origin.z;
  233. result[15] = 1.0;
  234. return result;
  235. };
  236. localFrameToFixedFrameCache[hashAxis] = resultat;
  237. }
  238. return resultat;
  239. };
  240. /**
  241. * Computes a 4x4 transformation matrix from a reference frame with an east-north-up axes
  242. * centered at the provided origin to the provided ellipsoid's fixed reference frame.
  243. * The local axes are defined as:
  244. * <ul>
  245. * <li>The <code>x</code> axis points in the local east direction.</li>
  246. * <li>The <code>y</code> axis points in the local north direction.</li>
  247. * <li>The <code>z</code> axis points in the direction of the ellipsoid surface normal which passes through the position.</li>
  248. * </ul>
  249. *
  250. * @function
  251. * @param {Cartesian3} origin The center point of the local reference frame.
  252. * @param {Ellipsoid} [ellipsoid=Ellipsoid.WGS84] The ellipsoid whose fixed frame is used in the transformation.
  253. * @param {Matrix4} [result] The object onto which to store the result.
  254. * @returns {Matrix4} The modified result parameter or a new Matrix4 instance if none was provided.
  255. *
  256. * @example
  257. * // Get the transform from local east-north-up at cartographic (0.0, 0.0) to Earth's fixed frame.
  258. * const center = Cesium.Cartesian3.fromDegrees(0.0, 0.0);
  259. * const transform = Cesium.Transforms.eastNorthUpToFixedFrame(center);
  260. */
  261. Transforms.eastNorthUpToFixedFrame = Transforms.localFrameToFixedFrameGenerator(
  262. "east",
  263. "north"
  264. );
  265. /**
  266. * Computes a 4x4 transformation matrix from a reference frame with an north-east-down axes
  267. * centered at the provided origin to the provided ellipsoid's fixed reference frame.
  268. * The local axes are defined as:
  269. * <ul>
  270. * <li>The <code>x</code> axis points in the local north direction.</li>
  271. * <li>The <code>y</code> axis points in the local east direction.</li>
  272. * <li>The <code>z</code> axis points in the opposite direction of the ellipsoid surface normal which passes through the position.</li>
  273. * </ul>
  274. *
  275. * @function
  276. * @param {Cartesian3} origin The center point of the local reference frame.
  277. * @param {Ellipsoid} [ellipsoid=Ellipsoid.WGS84] The ellipsoid whose fixed frame is used in the transformation.
  278. * @param {Matrix4} [result] The object onto which to store the result.
  279. * @returns {Matrix4} The modified result parameter or a new Matrix4 instance if none was provided.
  280. *
  281. * @example
  282. * // Get the transform from local north-east-down at cartographic (0.0, 0.0) to Earth's fixed frame.
  283. * const center = Cesium.Cartesian3.fromDegrees(0.0, 0.0);
  284. * const transform = Cesium.Transforms.northEastDownToFixedFrame(center);
  285. */
  286. Transforms.northEastDownToFixedFrame = Transforms.localFrameToFixedFrameGenerator(
  287. "north",
  288. "east"
  289. );
  290. /**
  291. * Computes a 4x4 transformation matrix from a reference frame with an north-up-east axes
  292. * centered at the provided origin to the provided ellipsoid's fixed reference frame.
  293. * The local axes are defined as:
  294. * <ul>
  295. * <li>The <code>x</code> axis points in the local north direction.</li>
  296. * <li>The <code>y</code> axis points in the direction of the ellipsoid surface normal which passes through the position.</li>
  297. * <li>The <code>z</code> axis points in the local east direction.</li>
  298. * </ul>
  299. *
  300. * @function
  301. * @param {Cartesian3} origin The center point of the local reference frame.
  302. * @param {Ellipsoid} [ellipsoid=Ellipsoid.WGS84] The ellipsoid whose fixed frame is used in the transformation.
  303. * @param {Matrix4} [result] The object onto which to store the result.
  304. * @returns {Matrix4} The modified result parameter or a new Matrix4 instance if none was provided.
  305. *
  306. * @example
  307. * // Get the transform from local north-up-east at cartographic (0.0, 0.0) to Earth's fixed frame.
  308. * const center = Cesium.Cartesian3.fromDegrees(0.0, 0.0);
  309. * const transform = Cesium.Transforms.northUpEastToFixedFrame(center);
  310. */
  311. Transforms.northUpEastToFixedFrame = Transforms.localFrameToFixedFrameGenerator(
  312. "north",
  313. "up"
  314. );
  315. /**
  316. * Computes a 4x4 transformation matrix from a reference frame with an north-west-up axes
  317. * centered at the provided origin to the provided ellipsoid's fixed reference frame.
  318. * The local axes are defined as:
  319. * <ul>
  320. * <li>The <code>x</code> axis points in the local north direction.</li>
  321. * <li>The <code>y</code> axis points in the local west direction.</li>
  322. * <li>The <code>z</code> axis points in the direction of the ellipsoid surface normal which passes through the position.</li>
  323. * </ul>
  324. *
  325. * @function
  326. * @param {Cartesian3} origin The center point of the local reference frame.
  327. * @param {Ellipsoid} [ellipsoid=Ellipsoid.WGS84] The ellipsoid whose fixed frame is used in the transformation.
  328. * @param {Matrix4} [result] The object onto which to store the result.
  329. * @returns {Matrix4} The modified result parameter or a new Matrix4 instance if none was provided.
  330. *
  331. * @example
  332. * // Get the transform from local north-West-Up at cartographic (0.0, 0.0) to Earth's fixed frame.
  333. * const center = Cesium.Cartesian3.fromDegrees(0.0, 0.0);
  334. * const transform = Cesium.Transforms.northWestUpToFixedFrame(center);
  335. */
  336. Transforms.northWestUpToFixedFrame = Transforms.localFrameToFixedFrameGenerator(
  337. "north",
  338. "west"
  339. );
  340. const scratchHPRQuaternion = new Quaternion();
  341. const scratchScale = new Cartesian3(1.0, 1.0, 1.0);
  342. const scratchHPRMatrix4 = new Matrix4();
  343. /**
  344. * Computes a 4x4 transformation matrix from a reference frame with axes computed from the heading-pitch-roll angles
  345. * centered at the provided origin to the provided ellipsoid's fixed reference frame. Heading is the rotation from the local north
  346. * direction where a positive angle is increasing eastward. Pitch is the rotation from the local east-north plane. Positive pitch angles
  347. * are above the plane. Negative pitch angles are below the plane. Roll is the first rotation applied about the local east axis.
  348. *
  349. * @param {Cartesian3} origin The center point of the local reference frame.
  350. * @param {HeadingPitchRoll} headingPitchRoll The heading, pitch, and roll.
  351. * @param {Ellipsoid} [ellipsoid=Ellipsoid.WGS84] The ellipsoid whose fixed frame is used in the transformation.
  352. * @param {Transforms.LocalFrameToFixedFrame} [fixedFrameTransform=Transforms.eastNorthUpToFixedFrame] A 4x4 transformation
  353. * matrix from a reference frame to the provided ellipsoid's fixed reference frame
  354. * @param {Matrix4} [result] The object onto which to store the result.
  355. * @returns {Matrix4} The modified result parameter or a new Matrix4 instance if none was provided.
  356. *
  357. * @example
  358. * // Get the transform from local heading-pitch-roll at cartographic (0.0, 0.0) to Earth's fixed frame.
  359. * const center = Cesium.Cartesian3.fromDegrees(0.0, 0.0);
  360. * const heading = -Cesium.Math.PI_OVER_TWO;
  361. * const pitch = Cesium.Math.PI_OVER_FOUR;
  362. * const roll = 0.0;
  363. * const hpr = new Cesium.HeadingPitchRoll(heading, pitch, roll);
  364. * const transform = Cesium.Transforms.headingPitchRollToFixedFrame(center, hpr);
  365. */
  366. Transforms.headingPitchRollToFixedFrame = function (
  367. origin,
  368. headingPitchRoll,
  369. ellipsoid,
  370. fixedFrameTransform,
  371. result
  372. ) {
  373. //>>includeStart('debug', pragmas.debug);
  374. Check.typeOf.object("HeadingPitchRoll", headingPitchRoll);
  375. //>>includeEnd('debug');
  376. fixedFrameTransform = defaultValue(
  377. fixedFrameTransform,
  378. Transforms.eastNorthUpToFixedFrame
  379. );
  380. const hprQuaternion = Quaternion.fromHeadingPitchRoll(
  381. headingPitchRoll,
  382. scratchHPRQuaternion
  383. );
  384. const hprMatrix = Matrix4.fromTranslationQuaternionRotationScale(
  385. Cartesian3.ZERO,
  386. hprQuaternion,
  387. scratchScale,
  388. scratchHPRMatrix4
  389. );
  390. result = fixedFrameTransform(origin, ellipsoid, result);
  391. return Matrix4.multiply(result, hprMatrix, result);
  392. };
  393. const scratchENUMatrix4 = new Matrix4();
  394. const scratchHPRMatrix3 = new Matrix3();
  395. /**
  396. * Computes a quaternion from a reference frame with axes computed from the heading-pitch-roll angles
  397. * centered at the provided origin. Heading is the rotation from the local north
  398. * direction where a positive angle is increasing eastward. Pitch is the rotation from the local east-north plane. Positive pitch angles
  399. * are above the plane. Negative pitch angles are below the plane. Roll is the first rotation applied about the local east axis.
  400. *
  401. * @param {Cartesian3} origin The center point of the local reference frame.
  402. * @param {HeadingPitchRoll} headingPitchRoll The heading, pitch, and roll.
  403. * @param {Ellipsoid} [ellipsoid=Ellipsoid.WGS84] The ellipsoid whose fixed frame is used in the transformation.
  404. * @param {Transforms.LocalFrameToFixedFrame} [fixedFrameTransform=Transforms.eastNorthUpToFixedFrame] A 4x4 transformation
  405. * matrix from a reference frame to the provided ellipsoid's fixed reference frame
  406. * @param {Quaternion} [result] The object onto which to store the result.
  407. * @returns {Quaternion} The modified result parameter or a new Quaternion instance if none was provided.
  408. *
  409. * @example
  410. * // Get the quaternion from local heading-pitch-roll at cartographic (0.0, 0.0) to Earth's fixed frame.
  411. * const center = Cesium.Cartesian3.fromDegrees(0.0, 0.0);
  412. * const heading = -Cesium.Math.PI_OVER_TWO;
  413. * const pitch = Cesium.Math.PI_OVER_FOUR;
  414. * const roll = 0.0;
  415. * const hpr = new HeadingPitchRoll(heading, pitch, roll);
  416. * const quaternion = Cesium.Transforms.headingPitchRollQuaternion(center, hpr);
  417. */
  418. Transforms.headingPitchRollQuaternion = function (
  419. origin,
  420. headingPitchRoll,
  421. ellipsoid,
  422. fixedFrameTransform,
  423. result
  424. ) {
  425. //>>includeStart('debug', pragmas.debug);
  426. Check.typeOf.object("HeadingPitchRoll", headingPitchRoll);
  427. //>>includeEnd('debug');
  428. const transform = Transforms.headingPitchRollToFixedFrame(
  429. origin,
  430. headingPitchRoll,
  431. ellipsoid,
  432. fixedFrameTransform,
  433. scratchENUMatrix4
  434. );
  435. const rotation = Matrix4.getMatrix3(transform, scratchHPRMatrix3);
  436. return Quaternion.fromRotationMatrix(rotation, result);
  437. };
  438. const noScale = new Cartesian3(1.0, 1.0, 1.0);
  439. const hprCenterScratch = new Cartesian3();
  440. const ffScratch = new Matrix4();
  441. const hprTransformScratch = new Matrix4();
  442. const hprRotationScratch = new Matrix3();
  443. const hprQuaternionScratch = new Quaternion();
  444. /**
  445. * Computes heading-pitch-roll angles from a transform in a particular reference frame. Heading is the rotation from the local north
  446. * direction where a positive angle is increasing eastward. Pitch is the rotation from the local east-north plane. Positive pitch angles
  447. * are above the plane. Negative pitch angles are below the plane. Roll is the first rotation applied about the local east axis.
  448. *
  449. * @param {Matrix4} transform The transform
  450. * @param {Ellipsoid} [ellipsoid=Ellipsoid.WGS84] The ellipsoid whose fixed frame is used in the transformation.
  451. * @param {Transforms.LocalFrameToFixedFrame} [fixedFrameTransform=Transforms.eastNorthUpToFixedFrame] A 4x4 transformation
  452. * matrix from a reference frame to the provided ellipsoid's fixed reference frame
  453. * @param {HeadingPitchRoll} [result] The object onto which to store the result.
  454. * @returns {HeadingPitchRoll} The modified result parameter or a new HeadingPitchRoll instance if none was provided.
  455. */
  456. Transforms.fixedFrameToHeadingPitchRoll = function (
  457. transform,
  458. ellipsoid,
  459. fixedFrameTransform,
  460. result
  461. ) {
  462. //>>includeStart('debug', pragmas.debug);
  463. Check.defined("transform", transform);
  464. //>>includeEnd('debug');
  465. ellipsoid = defaultValue(ellipsoid, Ellipsoid.WGS84);
  466. fixedFrameTransform = defaultValue(
  467. fixedFrameTransform,
  468. Transforms.eastNorthUpToFixedFrame
  469. );
  470. if (!defined(result)) {
  471. result = new HeadingPitchRoll();
  472. }
  473. const center = Matrix4.getTranslation(transform, hprCenterScratch);
  474. if (Cartesian3.equals(center, Cartesian3.ZERO)) {
  475. result.heading = 0;
  476. result.pitch = 0;
  477. result.roll = 0;
  478. return result;
  479. }
  480. let toFixedFrame = Matrix4.inverseTransformation(
  481. fixedFrameTransform(center, ellipsoid, ffScratch),
  482. ffScratch
  483. );
  484. let transformCopy = Matrix4.setScale(transform, noScale, hprTransformScratch);
  485. transformCopy = Matrix4.setTranslation(
  486. transformCopy,
  487. Cartesian3.ZERO,
  488. transformCopy
  489. );
  490. toFixedFrame = Matrix4.multiply(toFixedFrame, transformCopy, toFixedFrame);
  491. let quaternionRotation = Quaternion.fromRotationMatrix(
  492. Matrix4.getMatrix3(toFixedFrame, hprRotationScratch),
  493. hprQuaternionScratch
  494. );
  495. quaternionRotation = Quaternion.normalize(
  496. quaternionRotation,
  497. quaternionRotation
  498. );
  499. return HeadingPitchRoll.fromQuaternion(quaternionRotation, result);
  500. };
  501. const gmstConstant0 = 6 * 3600 + 41 * 60 + 50.54841;
  502. const gmstConstant1 = 8640184.812866;
  503. const gmstConstant2 = 0.093104;
  504. const gmstConstant3 = -6.2e-6;
  505. const rateCoef = 1.1772758384668e-19;
  506. const wgs84WRPrecessing = 7.2921158553e-5;
  507. const twoPiOverSecondsInDay = CesiumMath.TWO_PI / 86400.0;
  508. let dateInUtc = new JulianDate();
  509. /**
  510. * Computes a rotation matrix to transform a point or vector from True Equator Mean Equinox (TEME) axes to the
  511. * pseudo-fixed axes at a given time. This method treats the UT1 time standard as equivalent to UTC.
  512. *
  513. * @param {JulianDate} date The time at which to compute the rotation matrix.
  514. * @param {Matrix3} [result] The object onto which to store the result.
  515. * @returns {Matrix3} The modified result parameter or a new Matrix3 instance if none was provided.
  516. *
  517. * @example
  518. * //Set the view to the inertial frame.
  519. * scene.postUpdate.addEventListener(function(scene, time) {
  520. * const now = Cesium.JulianDate.now();
  521. * const offset = Cesium.Matrix4.multiplyByPoint(camera.transform, camera.position, new Cesium.Cartesian3());
  522. * const transform = Cesium.Matrix4.fromRotationTranslation(Cesium.Transforms.computeTemeToPseudoFixedMatrix(now));
  523. * const inverseTransform = Cesium.Matrix4.inverseTransformation(transform, new Cesium.Matrix4());
  524. * Cesium.Matrix4.multiplyByPoint(inverseTransform, offset, offset);
  525. * camera.lookAtTransform(transform, offset);
  526. * });
  527. */
  528. Transforms.computeTemeToPseudoFixedMatrix = function (date, result) {
  529. //>>includeStart('debug', pragmas.debug);
  530. if (!defined(date)) {
  531. throw new DeveloperError("date is required.");
  532. }
  533. //>>includeEnd('debug');
  534. // GMST is actually computed using UT1. We're using UTC as an approximation of UT1.
  535. // We do not want to use the function like convertTaiToUtc in JulianDate because
  536. // we explicitly do not want to fail when inside the leap second.
  537. dateInUtc = JulianDate.addSeconds(
  538. date,
  539. -JulianDate.computeTaiMinusUtc(date),
  540. dateInUtc
  541. );
  542. const utcDayNumber = dateInUtc.dayNumber;
  543. const utcSecondsIntoDay = dateInUtc.secondsOfDay;
  544. let t;
  545. const diffDays = utcDayNumber - 2451545;
  546. if (utcSecondsIntoDay >= 43200.0) {
  547. t = (diffDays + 0.5) / TimeConstants.DAYS_PER_JULIAN_CENTURY;
  548. } else {
  549. t = (diffDays - 0.5) / TimeConstants.DAYS_PER_JULIAN_CENTURY;
  550. }
  551. const gmst0 =
  552. gmstConstant0 +
  553. t * (gmstConstant1 + t * (gmstConstant2 + t * gmstConstant3));
  554. const angle = (gmst0 * twoPiOverSecondsInDay) % CesiumMath.TWO_PI;
  555. const ratio = wgs84WRPrecessing + rateCoef * (utcDayNumber - 2451545.5);
  556. const secondsSinceMidnight =
  557. (utcSecondsIntoDay + TimeConstants.SECONDS_PER_DAY * 0.5) %
  558. TimeConstants.SECONDS_PER_DAY;
  559. const gha = angle + ratio * secondsSinceMidnight;
  560. const cosGha = Math.cos(gha);
  561. const sinGha = Math.sin(gha);
  562. if (!defined(result)) {
  563. return new Matrix3(
  564. cosGha,
  565. sinGha,
  566. 0.0,
  567. -sinGha,
  568. cosGha,
  569. 0.0,
  570. 0.0,
  571. 0.0,
  572. 1.0
  573. );
  574. }
  575. result[0] = cosGha;
  576. result[1] = -sinGha;
  577. result[2] = 0.0;
  578. result[3] = sinGha;
  579. result[4] = cosGha;
  580. result[5] = 0.0;
  581. result[6] = 0.0;
  582. result[7] = 0.0;
  583. result[8] = 1.0;
  584. return result;
  585. };
  586. /**
  587. * The source of IAU 2006 XYS data, used for computing the transformation between the
  588. * Fixed and ICRF axes.
  589. * @type {Iau2006XysData}
  590. *
  591. * @see Transforms.computeIcrfToFixedMatrix
  592. * @see Transforms.computeFixedToIcrfMatrix
  593. *
  594. * @private
  595. */
  596. Transforms.iau2006XysData = new Iau2006XysData();
  597. /**
  598. * The source of Earth Orientation Parameters (EOP) data, used for computing the transformation
  599. * between the Fixed and ICRF axes. By default, zero values are used for all EOP values,
  600. * yielding a reasonable but not completely accurate representation of the ICRF axes.
  601. * @type {EarthOrientationParameters}
  602. *
  603. * @see Transforms.computeIcrfToFixedMatrix
  604. * @see Transforms.computeFixedToIcrfMatrix
  605. *
  606. * @private
  607. */
  608. Transforms.earthOrientationParameters = EarthOrientationParameters.NONE;
  609. const ttMinusTai = 32.184;
  610. const j2000ttDays = 2451545.0;
  611. /**
  612. * Preloads the data necessary to transform between the ICRF and Fixed axes, in either
  613. * direction, over a given interval. This function returns a promise that, when resolved,
  614. * indicates that the preload has completed.
  615. *
  616. * @param {TimeInterval} timeInterval The interval to preload.
  617. * @returns {Promise<void>} A promise that, when resolved, indicates that the preload has completed
  618. * and evaluation of the transformation between the fixed and ICRF axes will
  619. * no longer return undefined for a time inside the interval.
  620. *
  621. *
  622. * @example
  623. * const interval = new Cesium.TimeInterval(...);
  624. * Promise.resolve(Cesium.Transforms.preloadIcrfFixed(interval)).then(function() {
  625. * // the data is now loaded
  626. * });
  627. *
  628. * @see Transforms.computeIcrfToFixedMatrix
  629. * @see Transforms.computeFixedToIcrfMatrix
  630. */
  631. Transforms.preloadIcrfFixed = function (timeInterval) {
  632. const startDayTT = timeInterval.start.dayNumber;
  633. const startSecondTT = timeInterval.start.secondsOfDay + ttMinusTai;
  634. const stopDayTT = timeInterval.stop.dayNumber;
  635. const stopSecondTT = timeInterval.stop.secondsOfDay + ttMinusTai;
  636. const xysPromise = Transforms.iau2006XysData.preload(
  637. startDayTT,
  638. startSecondTT,
  639. stopDayTT,
  640. stopSecondTT
  641. );
  642. const eopPromise = Transforms.earthOrientationParameters.getPromiseToLoad();
  643. return Promise.all([xysPromise, eopPromise]);
  644. };
  645. /**
  646. * Computes a rotation matrix to transform a point or vector from the International Celestial
  647. * Reference Frame (GCRF/ICRF) inertial frame axes to the Earth-Fixed frame axes (ITRF)
  648. * at a given time. This function may return undefined if the data necessary to
  649. * do the transformation is not yet loaded.
  650. *
  651. * @param {JulianDate} date The time at which to compute the rotation matrix.
  652. * @param {Matrix3} [result] The object onto which to store the result. If this parameter is
  653. * not specified, a new instance is created and returned.
  654. * @returns {Matrix3} The rotation matrix, or undefined if the data necessary to do the
  655. * transformation is not yet loaded.
  656. *
  657. *
  658. * @example
  659. * scene.postUpdate.addEventListener(function(scene, time) {
  660. * // View in ICRF.
  661. * const icrfToFixed = Cesium.Transforms.computeIcrfToFixedMatrix(time);
  662. * if (Cesium.defined(icrfToFixed)) {
  663. * const offset = Cesium.Cartesian3.clone(camera.position);
  664. * const transform = Cesium.Matrix4.fromRotationTranslation(icrfToFixed);
  665. * camera.lookAtTransform(transform, offset);
  666. * }
  667. * });
  668. *
  669. * @see Transforms.preloadIcrfFixed
  670. */
  671. Transforms.computeIcrfToFixedMatrix = function (date, result) {
  672. //>>includeStart('debug', pragmas.debug);
  673. if (!defined(date)) {
  674. throw new DeveloperError("date is required.");
  675. }
  676. //>>includeEnd('debug');
  677. if (!defined(result)) {
  678. result = new Matrix3();
  679. }
  680. const fixedToIcrfMtx = Transforms.computeFixedToIcrfMatrix(date, result);
  681. if (!defined(fixedToIcrfMtx)) {
  682. return undefined;
  683. }
  684. return Matrix3.transpose(fixedToIcrfMtx, result);
  685. };
  686. const xysScratch = new Iau2006XysSample(0.0, 0.0, 0.0);
  687. const eopScratch = new EarthOrientationParametersSample(
  688. 0.0,
  689. 0.0,
  690. 0.0,
  691. 0.0,
  692. 0.0,
  693. 0.0
  694. );
  695. const rotation1Scratch = new Matrix3();
  696. const rotation2Scratch = new Matrix3();
  697. /**
  698. * Computes a rotation matrix to transform a point or vector from the Earth-Fixed frame axes (ITRF)
  699. * to the International Celestial Reference Frame (GCRF/ICRF) inertial frame axes
  700. * at a given time. This function may return undefined if the data necessary to
  701. * do the transformation is not yet loaded.
  702. *
  703. * @param {JulianDate} date The time at which to compute the rotation matrix.
  704. * @param {Matrix3} [result] The object onto which to store the result. If this parameter is
  705. * not specified, a new instance is created and returned.
  706. * @returns {Matrix3} The rotation matrix, or undefined if the data necessary to do the
  707. * transformation is not yet loaded.
  708. *
  709. *
  710. * @example
  711. * // Transform a point from the ICRF axes to the Fixed axes.
  712. * const now = Cesium.JulianDate.now();
  713. * const pointInFixed = Cesium.Cartesian3.fromDegrees(0.0, 0.0);
  714. * const fixedToIcrf = Cesium.Transforms.computeIcrfToFixedMatrix(now);
  715. * let pointInInertial = new Cesium.Cartesian3();
  716. * if (Cesium.defined(fixedToIcrf)) {
  717. * pointInInertial = Cesium.Matrix3.multiplyByVector(fixedToIcrf, pointInFixed, pointInInertial);
  718. * }
  719. *
  720. * @see Transforms.preloadIcrfFixed
  721. */
  722. Transforms.computeFixedToIcrfMatrix = function (date, result) {
  723. //>>includeStart('debug', pragmas.debug);
  724. if (!defined(date)) {
  725. throw new DeveloperError("date is required.");
  726. }
  727. //>>includeEnd('debug');
  728. if (!defined(result)) {
  729. result = new Matrix3();
  730. }
  731. // Compute pole wander
  732. const eop = Transforms.earthOrientationParameters.compute(date, eopScratch);
  733. if (!defined(eop)) {
  734. return undefined;
  735. }
  736. // There is no external conversion to Terrestrial Time (TT).
  737. // So use International Atomic Time (TAI) and convert using offsets.
  738. // Here we are assuming that dayTT and secondTT are positive
  739. const dayTT = date.dayNumber;
  740. // It's possible here that secondTT could roll over 86400
  741. // This does not seem to affect the precision (unit tests check for this)
  742. const secondTT = date.secondsOfDay + ttMinusTai;
  743. const xys = Transforms.iau2006XysData.computeXysRadians(
  744. dayTT,
  745. secondTT,
  746. xysScratch
  747. );
  748. if (!defined(xys)) {
  749. return undefined;
  750. }
  751. const x = xys.x + eop.xPoleOffset;
  752. const y = xys.y + eop.yPoleOffset;
  753. // Compute XYS rotation
  754. const a = 1.0 / (1.0 + Math.sqrt(1.0 - x * x - y * y));
  755. const rotation1 = rotation1Scratch;
  756. rotation1[0] = 1.0 - a * x * x;
  757. rotation1[3] = -a * x * y;
  758. rotation1[6] = x;
  759. rotation1[1] = -a * x * y;
  760. rotation1[4] = 1 - a * y * y;
  761. rotation1[7] = y;
  762. rotation1[2] = -x;
  763. rotation1[5] = -y;
  764. rotation1[8] = 1 - a * (x * x + y * y);
  765. const rotation2 = Matrix3.fromRotationZ(-xys.s, rotation2Scratch);
  766. const matrixQ = Matrix3.multiply(rotation1, rotation2, rotation1Scratch);
  767. // Similar to TT conversions above
  768. // It's possible here that secondTT could roll over 86400
  769. // This does not seem to affect the precision (unit tests check for this)
  770. const dateUt1day = date.dayNumber;
  771. const dateUt1sec =
  772. date.secondsOfDay - JulianDate.computeTaiMinusUtc(date) + eop.ut1MinusUtc;
  773. // Compute Earth rotation angle
  774. // The IERS standard for era is
  775. // era = 0.7790572732640 + 1.00273781191135448 * Tu
  776. // where
  777. // Tu = JulianDateInUt1 - 2451545.0
  778. // However, you get much more precision if you make the following simplification
  779. // era = a + (1 + b) * (JulianDayNumber + FractionOfDay - 2451545)
  780. // era = a + (JulianDayNumber - 2451545) + FractionOfDay + b (JulianDayNumber - 2451545 + FractionOfDay)
  781. // era = a + FractionOfDay + b (JulianDayNumber - 2451545 + FractionOfDay)
  782. // since (JulianDayNumber - 2451545) represents an integer number of revolutions which will be discarded anyway.
  783. const daysSinceJ2000 = dateUt1day - 2451545;
  784. const fractionOfDay = dateUt1sec / TimeConstants.SECONDS_PER_DAY;
  785. let era =
  786. 0.779057273264 +
  787. fractionOfDay +
  788. 0.00273781191135448 * (daysSinceJ2000 + fractionOfDay);
  789. era = (era % 1.0) * CesiumMath.TWO_PI;
  790. const earthRotation = Matrix3.fromRotationZ(era, rotation2Scratch);
  791. // pseudoFixed to ICRF
  792. const pfToIcrf = Matrix3.multiply(matrixQ, earthRotation, rotation1Scratch);
  793. // Compute pole wander matrix
  794. const cosxp = Math.cos(eop.xPoleWander);
  795. const cosyp = Math.cos(eop.yPoleWander);
  796. const sinxp = Math.sin(eop.xPoleWander);
  797. const sinyp = Math.sin(eop.yPoleWander);
  798. let ttt = dayTT - j2000ttDays + secondTT / TimeConstants.SECONDS_PER_DAY;
  799. ttt /= 36525.0;
  800. // approximate sp value in rad
  801. const sp = (-47.0e-6 * ttt * CesiumMath.RADIANS_PER_DEGREE) / 3600.0;
  802. const cossp = Math.cos(sp);
  803. const sinsp = Math.sin(sp);
  804. const fToPfMtx = rotation2Scratch;
  805. fToPfMtx[0] = cosxp * cossp;
  806. fToPfMtx[1] = cosxp * sinsp;
  807. fToPfMtx[2] = sinxp;
  808. fToPfMtx[3] = -cosyp * sinsp + sinyp * sinxp * cossp;
  809. fToPfMtx[4] = cosyp * cossp + sinyp * sinxp * sinsp;
  810. fToPfMtx[5] = -sinyp * cosxp;
  811. fToPfMtx[6] = -sinyp * sinsp - cosyp * sinxp * cossp;
  812. fToPfMtx[7] = sinyp * cossp - cosyp * sinxp * sinsp;
  813. fToPfMtx[8] = cosyp * cosxp;
  814. return Matrix3.multiply(pfToIcrf, fToPfMtx, result);
  815. };
  816. const pointToWindowCoordinatesTemp = new Cartesian4();
  817. /**
  818. * Transform a point from model coordinates to window coordinates.
  819. *
  820. * @param {Matrix4} modelViewProjectionMatrix The 4x4 model-view-projection matrix.
  821. * @param {Matrix4} viewportTransformation The 4x4 viewport transformation.
  822. * @param {Cartesian3} point The point to transform.
  823. * @param {Cartesian2} [result] The object onto which to store the result.
  824. * @returns {Cartesian2} The modified result parameter or a new Cartesian2 instance if none was provided.
  825. */
  826. Transforms.pointToWindowCoordinates = function (
  827. modelViewProjectionMatrix,
  828. viewportTransformation,
  829. point,
  830. result
  831. ) {
  832. result = Transforms.pointToGLWindowCoordinates(
  833. modelViewProjectionMatrix,
  834. viewportTransformation,
  835. point,
  836. result
  837. );
  838. result.y = 2.0 * viewportTransformation[5] - result.y;
  839. return result;
  840. };
  841. /**
  842. * @private
  843. */
  844. Transforms.pointToGLWindowCoordinates = function (
  845. modelViewProjectionMatrix,
  846. viewportTransformation,
  847. point,
  848. result
  849. ) {
  850. //>>includeStart('debug', pragmas.debug);
  851. if (!defined(modelViewProjectionMatrix)) {
  852. throw new DeveloperError("modelViewProjectionMatrix is required.");
  853. }
  854. if (!defined(viewportTransformation)) {
  855. throw new DeveloperError("viewportTransformation is required.");
  856. }
  857. if (!defined(point)) {
  858. throw new DeveloperError("point is required.");
  859. }
  860. //>>includeEnd('debug');
  861. if (!defined(result)) {
  862. result = new Cartesian2();
  863. }
  864. const tmp = pointToWindowCoordinatesTemp;
  865. Matrix4.multiplyByVector(
  866. modelViewProjectionMatrix,
  867. Cartesian4.fromElements(point.x, point.y, point.z, 1, tmp),
  868. tmp
  869. );
  870. Cartesian4.multiplyByScalar(tmp, 1.0 / tmp.w, tmp);
  871. Matrix4.multiplyByVector(viewportTransformation, tmp, tmp);
  872. return Cartesian2.fromCartesian4(tmp, result);
  873. };
  874. const normalScratch = new Cartesian3();
  875. const rightScratch = new Cartesian3();
  876. const upScratch = new Cartesian3();
  877. /**
  878. * Transform a position and velocity to a rotation matrix.
  879. *
  880. * @param {Cartesian3} position The position to transform.
  881. * @param {Cartesian3} velocity The velocity vector to transform.
  882. * @param {Ellipsoid} [ellipsoid=Ellipsoid.WGS84] The ellipsoid whose fixed frame is used in the transformation.
  883. * @param {Matrix3} [result] The object onto which to store the result.
  884. * @returns {Matrix3} The modified result parameter or a new Matrix3 instance if none was provided.
  885. */
  886. Transforms.rotationMatrixFromPositionVelocity = function (
  887. position,
  888. velocity,
  889. ellipsoid,
  890. result
  891. ) {
  892. //>>includeStart('debug', pragmas.debug);
  893. if (!defined(position)) {
  894. throw new DeveloperError("position is required.");
  895. }
  896. if (!defined(velocity)) {
  897. throw new DeveloperError("velocity is required.");
  898. }
  899. //>>includeEnd('debug');
  900. const normal = defaultValue(ellipsoid, Ellipsoid.WGS84).geodeticSurfaceNormal(
  901. position,
  902. normalScratch
  903. );
  904. let right = Cartesian3.cross(velocity, normal, rightScratch);
  905. if (Cartesian3.equalsEpsilon(right, Cartesian3.ZERO, CesiumMath.EPSILON6)) {
  906. right = Cartesian3.clone(Cartesian3.UNIT_X, right);
  907. }
  908. const up = Cartesian3.cross(right, velocity, upScratch);
  909. Cartesian3.normalize(up, up);
  910. Cartesian3.cross(velocity, up, right);
  911. Cartesian3.negate(right, right);
  912. Cartesian3.normalize(right, right);
  913. if (!defined(result)) {
  914. result = new Matrix3();
  915. }
  916. result[0] = velocity.x;
  917. result[1] = velocity.y;
  918. result[2] = velocity.z;
  919. result[3] = right.x;
  920. result[4] = right.y;
  921. result[5] = right.z;
  922. result[6] = up.x;
  923. result[7] = up.y;
  924. result[8] = up.z;
  925. return result;
  926. };
  927. const swizzleMatrix = new Matrix4(
  928. 0.0,
  929. 0.0,
  930. 1.0,
  931. 0.0,
  932. 1.0,
  933. 0.0,
  934. 0.0,
  935. 0.0,
  936. 0.0,
  937. 1.0,
  938. 0.0,
  939. 0.0,
  940. 0.0,
  941. 0.0,
  942. 0.0,
  943. 1.0
  944. );
  945. const scratchCartographic = new Cartographic();
  946. const scratchCartesian3Projection = new Cartesian3();
  947. const scratchCenter = new Cartesian3();
  948. const scratchRotation = new Matrix3();
  949. const scratchFromENU = new Matrix4();
  950. const scratchToENU = new Matrix4();
  951. /**
  952. * @private
  953. */
  954. Transforms.basisTo2D = function (projection, matrix, result) {
  955. //>>includeStart('debug', pragmas.debug);
  956. if (!defined(projection)) {
  957. throw new DeveloperError("projection is required.");
  958. }
  959. if (!defined(matrix)) {
  960. throw new DeveloperError("matrix is required.");
  961. }
  962. if (!defined(result)) {
  963. throw new DeveloperError("result is required.");
  964. }
  965. //>>includeEnd('debug');
  966. const rtcCenter = Matrix4.getTranslation(matrix, scratchCenter);
  967. const ellipsoid = projection.ellipsoid;
  968. // Get the 2D Center
  969. const cartographic = ellipsoid.cartesianToCartographic(
  970. rtcCenter,
  971. scratchCartographic
  972. );
  973. const projectedPosition = projection.project(
  974. cartographic,
  975. scratchCartesian3Projection
  976. );
  977. Cartesian3.fromElements(
  978. projectedPosition.z,
  979. projectedPosition.x,
  980. projectedPosition.y,
  981. projectedPosition
  982. );
  983. // Assuming the instance are positioned in WGS84, invert the WGS84 transform to get the local transform and then convert to 2D
  984. const fromENU = Transforms.eastNorthUpToFixedFrame(
  985. rtcCenter,
  986. ellipsoid,
  987. scratchFromENU
  988. );
  989. const toENU = Matrix4.inverseTransformation(fromENU, scratchToENU);
  990. const rotation = Matrix4.getMatrix3(matrix, scratchRotation);
  991. const local = Matrix4.multiplyByMatrix3(toENU, rotation, result);
  992. Matrix4.multiply(swizzleMatrix, local, result); // Swap x, y, z for 2D
  993. Matrix4.setTranslation(result, projectedPosition, result); // Use the projected center
  994. return result;
  995. };
  996. /**
  997. * @private
  998. */
  999. Transforms.wgs84To2DModelMatrix = function (projection, center, result) {
  1000. //>>includeStart('debug', pragmas.debug);
  1001. if (!defined(projection)) {
  1002. throw new DeveloperError("projection is required.");
  1003. }
  1004. if (!defined(center)) {
  1005. throw new DeveloperError("center is required.");
  1006. }
  1007. if (!defined(result)) {
  1008. throw new DeveloperError("result is required.");
  1009. }
  1010. //>>includeEnd('debug');
  1011. const ellipsoid = projection.ellipsoid;
  1012. const fromENU = Transforms.eastNorthUpToFixedFrame(
  1013. center,
  1014. ellipsoid,
  1015. scratchFromENU
  1016. );
  1017. const toENU = Matrix4.inverseTransformation(fromENU, scratchToENU);
  1018. const cartographic = ellipsoid.cartesianToCartographic(
  1019. center,
  1020. scratchCartographic
  1021. );
  1022. const projectedPosition = projection.project(
  1023. cartographic,
  1024. scratchCartesian3Projection
  1025. );
  1026. Cartesian3.fromElements(
  1027. projectedPosition.z,
  1028. projectedPosition.x,
  1029. projectedPosition.y,
  1030. projectedPosition
  1031. );
  1032. const translation = Matrix4.fromTranslation(
  1033. projectedPosition,
  1034. scratchFromENU
  1035. );
  1036. Matrix4.multiply(swizzleMatrix, toENU, result);
  1037. Matrix4.multiply(translation, result, result);
  1038. return result;
  1039. };
  1040. export default Transforms;