Transforms.js 41 KB

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