import when from "../ThirdParty/when.js"; import Cartesian2 from "./Cartesian2.js"; import Cartesian3 from "./Cartesian3.js"; import Cartesian4 from "./Cartesian4.js"; import Cartographic from "./Cartographic.js"; import Check from "./Check.js"; import defaultValue from "./defaultValue.js"; import defined from "./defined.js"; import DeveloperError from "./DeveloperError.js"; import EarthOrientationParameters from "./EarthOrientationParameters.js"; import EarthOrientationParametersSample from "./EarthOrientationParametersSample.js"; import Ellipsoid from "./Ellipsoid.js"; import HeadingPitchRoll from "./HeadingPitchRoll.js"; import Iau2006XysData from "./Iau2006XysData.js"; import Iau2006XysSample from "./Iau2006XysSample.js"; import JulianDate from "./JulianDate.js"; import CesiumMath from "./Math.js"; import Matrix3 from "./Matrix3.js"; import Matrix4 from "./Matrix4.js"; import Quaternion from "./Quaternion.js"; import TimeConstants from "./TimeConstants.js"; /** * Contains functions for transforming positions to various reference frames. * * @namespace Transforms */ var Transforms = {}; var vectorProductLocalFrame = { up: { south: "east", north: "west", west: "south", east: "north", }, down: { south: "west", north: "east", west: "north", east: "south", }, south: { up: "west", down: "east", west: "down", east: "up", }, north: { up: "east", down: "west", west: "up", east: "down", }, west: { up: "north", down: "south", north: "down", south: "up", }, east: { up: "south", down: "north", north: "up", south: "down", }, }; var degeneratePositionLocalFrame = { north: [-1, 0, 0], east: [0, 1, 0], up: [0, 0, 1], south: [1, 0, 0], west: [0, -1, 0], down: [0, 0, -1], }; var localFrameToFixedFrameCache = {}; var scratchCalculateCartesian = { east: new Cartesian3(), north: new Cartesian3(), up: new Cartesian3(), west: new Cartesian3(), south: new Cartesian3(), down: new Cartesian3(), }; var scratchFirstCartesian = new Cartesian3(); var scratchSecondCartesian = new Cartesian3(); var scratchThirdCartesian = new Cartesian3(); /** * Generates a function that computes a 4x4 transformation matrix from a reference frame * centered at the provided origin to the provided ellipsoid's fixed reference frame. * @param {String} firstAxis name of the first axis of the local reference frame. Must be * 'east', 'north', 'up', 'west', 'south' or 'down'. * @param {String} secondAxis name of the second axis of the local reference frame. Must be * 'east', 'north', 'up', 'west', 'south' or 'down'. * @return {Transforms.LocalFrameToFixedFrame} The function that will computes a * 4x4 transformation matrix from a reference frame, with first axis and second axis compliant with the parameters, */ Transforms.localFrameToFixedFrameGenerator = function (firstAxis, secondAxis) { if ( !vectorProductLocalFrame.hasOwnProperty(firstAxis) || !vectorProductLocalFrame[firstAxis].hasOwnProperty(secondAxis) ) { throw new DeveloperError( "firstAxis and secondAxis must be east, north, up, west, south or down." ); } var thirdAxis = vectorProductLocalFrame[firstAxis][secondAxis]; /** * Computes a 4x4 transformation matrix from a reference frame * centered at the provided origin to the provided ellipsoid's fixed reference frame. * @callback Transforms.LocalFrameToFixedFrame * @param {Cartesian3} origin The center point of the local reference frame. * @param {Ellipsoid} [ellipsoid=Ellipsoid.WGS84] The ellipsoid whose fixed frame is used in the transformation. * @param {Matrix4} [result] The object onto which to store the result. * @returns {Matrix4} The modified result parameter or a new Matrix4 instance if none was provided. */ var resultat; var hashAxis = firstAxis + secondAxis; if (defined(localFrameToFixedFrameCache[hashAxis])) { resultat = localFrameToFixedFrameCache[hashAxis]; } else { resultat = function (origin, ellipsoid, result) { //>>includeStart('debug', pragmas.debug); if (!defined(origin)) { throw new DeveloperError("origin is required."); } //>>includeEnd('debug'); if (!defined(result)) { result = new Matrix4(); } if ( Cartesian3.equalsEpsilon(origin, Cartesian3.ZERO, CesiumMath.EPSILON14) ) { // If x, y, and z are zero, use the degenerate local frame, which is a special case Cartesian3.unpack( degeneratePositionLocalFrame[firstAxis], 0, scratchFirstCartesian ); Cartesian3.unpack( degeneratePositionLocalFrame[secondAxis], 0, scratchSecondCartesian ); Cartesian3.unpack( degeneratePositionLocalFrame[thirdAxis], 0, scratchThirdCartesian ); } else if ( CesiumMath.equalsEpsilon(origin.x, 0.0, CesiumMath.EPSILON14) && CesiumMath.equalsEpsilon(origin.y, 0.0, CesiumMath.EPSILON14) ) { // If x and y are zero, assume origin is at a pole, which is a special case. var sign = CesiumMath.sign(origin.z); Cartesian3.unpack( degeneratePositionLocalFrame[firstAxis], 0, scratchFirstCartesian ); if (firstAxis !== "east" && firstAxis !== "west") { Cartesian3.multiplyByScalar( scratchFirstCartesian, sign, scratchFirstCartesian ); } Cartesian3.unpack( degeneratePositionLocalFrame[secondAxis], 0, scratchSecondCartesian ); if (secondAxis !== "east" && secondAxis !== "west") { Cartesian3.multiplyByScalar( scratchSecondCartesian, sign, scratchSecondCartesian ); } Cartesian3.unpack( degeneratePositionLocalFrame[thirdAxis], 0, scratchThirdCartesian ); if (thirdAxis !== "east" && thirdAxis !== "west") { Cartesian3.multiplyByScalar( scratchThirdCartesian, sign, scratchThirdCartesian ); } } else { ellipsoid = defaultValue(ellipsoid, Ellipsoid.WGS84); ellipsoid.geodeticSurfaceNormal(origin, scratchCalculateCartesian.up); var up = scratchCalculateCartesian.up; var east = scratchCalculateCartesian.east; east.x = -origin.y; east.y = origin.x; east.z = 0.0; Cartesian3.normalize(east, scratchCalculateCartesian.east); Cartesian3.cross(up, east, scratchCalculateCartesian.north); Cartesian3.multiplyByScalar( scratchCalculateCartesian.up, -1, scratchCalculateCartesian.down ); Cartesian3.multiplyByScalar( scratchCalculateCartesian.east, -1, scratchCalculateCartesian.west ); Cartesian3.multiplyByScalar( scratchCalculateCartesian.north, -1, scratchCalculateCartesian.south ); scratchFirstCartesian = scratchCalculateCartesian[firstAxis]; scratchSecondCartesian = scratchCalculateCartesian[secondAxis]; scratchThirdCartesian = scratchCalculateCartesian[thirdAxis]; } result[0] = scratchFirstCartesian.x; result[1] = scratchFirstCartesian.y; result[2] = scratchFirstCartesian.z; result[3] = 0.0; result[4] = scratchSecondCartesian.x; result[5] = scratchSecondCartesian.y; result[6] = scratchSecondCartesian.z; result[7] = 0.0; result[8] = scratchThirdCartesian.x; result[9] = scratchThirdCartesian.y; result[10] = scratchThirdCartesian.z; result[11] = 0.0; result[12] = origin.x; result[13] = origin.y; result[14] = origin.z; result[15] = 1.0; return result; }; localFrameToFixedFrameCache[hashAxis] = resultat; } return resultat; }; /** * Computes a 4x4 transformation matrix from a reference frame with an east-north-up axes * centered at the provided origin to the provided ellipsoid's fixed reference frame. * The local axes are defined as: * * * @function * @param {Cartesian3} origin The center point of the local reference frame. * @param {Ellipsoid} [ellipsoid=Ellipsoid.WGS84] The ellipsoid whose fixed frame is used in the transformation. * @param {Matrix4} [result] The object onto which to store the result. * @returns {Matrix4} The modified result parameter or a new Matrix4 instance if none was provided. * * @example * // Get the transform from local east-north-up at cartographic (0.0, 0.0) to Earth's fixed frame. * var center = Cesium.Cartesian3.fromDegrees(0.0, 0.0); * var transform = Cesium.Transforms.eastNorthUpToFixedFrame(center); */ Transforms.eastNorthUpToFixedFrame = Transforms.localFrameToFixedFrameGenerator( "east", "north" ); /** * Computes a 4x4 transformation matrix from a reference frame with an north-east-down axes * centered at the provided origin to the provided ellipsoid's fixed reference frame. * The local axes are defined as: * * * @function * @param {Cartesian3} origin The center point of the local reference frame. * @param {Ellipsoid} [ellipsoid=Ellipsoid.WGS84] The ellipsoid whose fixed frame is used in the transformation. * @param {Matrix4} [result] The object onto which to store the result. * @returns {Matrix4} The modified result parameter or a new Matrix4 instance if none was provided. * * @example * // Get the transform from local north-east-down at cartographic (0.0, 0.0) to Earth's fixed frame. * var center = Cesium.Cartesian3.fromDegrees(0.0, 0.0); * var transform = Cesium.Transforms.northEastDownToFixedFrame(center); */ Transforms.northEastDownToFixedFrame = Transforms.localFrameToFixedFrameGenerator( "north", "east" ); /** * Computes a 4x4 transformation matrix from a reference frame with an north-up-east axes * centered at the provided origin to the provided ellipsoid's fixed reference frame. * The local axes are defined as: * * * @function * @param {Cartesian3} origin The center point of the local reference frame. * @param {Ellipsoid} [ellipsoid=Ellipsoid.WGS84] The ellipsoid whose fixed frame is used in the transformation. * @param {Matrix4} [result] The object onto which to store the result. * @returns {Matrix4} The modified result parameter or a new Matrix4 instance if none was provided. * * @example * // Get the transform from local north-up-east at cartographic (0.0, 0.0) to Earth's fixed frame. * var center = Cesium.Cartesian3.fromDegrees(0.0, 0.0); * var transform = Cesium.Transforms.northUpEastToFixedFrame(center); */ Transforms.northUpEastToFixedFrame = Transforms.localFrameToFixedFrameGenerator( "north", "up" ); /** * Computes a 4x4 transformation matrix from a reference frame with an north-west-up axes * centered at the provided origin to the provided ellipsoid's fixed reference frame. * The local axes are defined as: * * * @function * @param {Cartesian3} origin The center point of the local reference frame. * @param {Ellipsoid} [ellipsoid=Ellipsoid.WGS84] The ellipsoid whose fixed frame is used in the transformation. * @param {Matrix4} [result] The object onto which to store the result. * @returns {Matrix4} The modified result parameter or a new Matrix4 instance if none was provided. * * @example * // Get the transform from local north-West-Up at cartographic (0.0, 0.0) to Earth's fixed frame. * var center = Cesium.Cartesian3.fromDegrees(0.0, 0.0); * var transform = Cesium.Transforms.northWestUpToFixedFrame(center); */ Transforms.northWestUpToFixedFrame = Transforms.localFrameToFixedFrameGenerator( "north", "west" ); var scratchHPRQuaternion = new Quaternion(); var scratchScale = new Cartesian3(1.0, 1.0, 1.0); var scratchHPRMatrix4 = new Matrix4(); /** * Computes a 4x4 transformation matrix from a reference frame with axes computed from the heading-pitch-roll angles * centered at the provided origin to the provided ellipsoid's fixed reference frame. Heading is the rotation from the local north * direction where a positive angle is increasing eastward. Pitch is the rotation from the local east-north plane. Positive pitch angles * are above the plane. Negative pitch angles are below the plane. Roll is the first rotation applied about the local east axis. * * @param {Cartesian3} origin The center point of the local reference frame. * @param {HeadingPitchRoll} headingPitchRoll The heading, pitch, and roll. * @param {Ellipsoid} [ellipsoid=Ellipsoid.WGS84] The ellipsoid whose fixed frame is used in the transformation. * @param {Transforms.LocalFrameToFixedFrame} [fixedFrameTransform=Transforms.eastNorthUpToFixedFrame] A 4x4 transformation * matrix from a reference frame to the provided ellipsoid's fixed reference frame * @param {Matrix4} [result] The object onto which to store the result. * @returns {Matrix4} The modified result parameter or a new Matrix4 instance if none was provided. * * @example * // Get the transform from local heading-pitch-roll at cartographic (0.0, 0.0) to Earth's fixed frame. * var center = Cesium.Cartesian3.fromDegrees(0.0, 0.0); * var heading = -Cesium.Math.PI_OVER_TWO; * var pitch = Cesium.Math.PI_OVER_FOUR; * var roll = 0.0; * var hpr = new Cesium.HeadingPitchRoll(heading, pitch, roll); * var transform = Cesium.Transforms.headingPitchRollToFixedFrame(center, hpr); */ Transforms.headingPitchRollToFixedFrame = function ( origin, headingPitchRoll, ellipsoid, fixedFrameTransform, result ) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("HeadingPitchRoll", headingPitchRoll); //>>includeEnd('debug'); fixedFrameTransform = defaultValue( fixedFrameTransform, Transforms.eastNorthUpToFixedFrame ); var hprQuaternion = Quaternion.fromHeadingPitchRoll( headingPitchRoll, scratchHPRQuaternion ); var hprMatrix = Matrix4.fromTranslationQuaternionRotationScale( Cartesian3.ZERO, hprQuaternion, scratchScale, scratchHPRMatrix4 ); result = fixedFrameTransform(origin, ellipsoid, result); return Matrix4.multiply(result, hprMatrix, result); }; var scratchENUMatrix4 = new Matrix4(); var scratchHPRMatrix3 = new Matrix3(); /** * Computes a quaternion from a reference frame with axes computed from the heading-pitch-roll angles * centered at the provided origin. Heading is the rotation from the local north * direction where a positive angle is increasing eastward. Pitch is the rotation from the local east-north plane. Positive pitch angles * are above the plane. Negative pitch angles are below the plane. Roll is the first rotation applied about the local east axis. * * @param {Cartesian3} origin The center point of the local reference frame. * @param {HeadingPitchRoll} headingPitchRoll The heading, pitch, and roll. * @param {Ellipsoid} [ellipsoid=Ellipsoid.WGS84] The ellipsoid whose fixed frame is used in the transformation. * @param {Transforms.LocalFrameToFixedFrame} [fixedFrameTransform=Transforms.eastNorthUpToFixedFrame] A 4x4 transformation * matrix from a reference frame to the provided ellipsoid's fixed reference frame * @param {Quaternion} [result] The object onto which to store the result. * @returns {Quaternion} The modified result parameter or a new Quaternion instance if none was provided. * * @example * // Get the quaternion from local heading-pitch-roll at cartographic (0.0, 0.0) to Earth's fixed frame. * var center = Cesium.Cartesian3.fromDegrees(0.0, 0.0); * var heading = -Cesium.Math.PI_OVER_TWO; * var pitch = Cesium.Math.PI_OVER_FOUR; * var roll = 0.0; * var hpr = new HeadingPitchRoll(heading, pitch, roll); * var quaternion = Cesium.Transforms.headingPitchRollQuaternion(center, hpr); */ Transforms.headingPitchRollQuaternion = function ( origin, headingPitchRoll, ellipsoid, fixedFrameTransform, result ) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("HeadingPitchRoll", headingPitchRoll); //>>includeEnd('debug'); var transform = Transforms.headingPitchRollToFixedFrame( origin, headingPitchRoll, ellipsoid, fixedFrameTransform, scratchENUMatrix4 ); var rotation = Matrix4.getMatrix3(transform, scratchHPRMatrix3); return Quaternion.fromRotationMatrix(rotation, result); }; var noScale = new Cartesian3(1.0, 1.0, 1.0); var hprCenterScratch = new Cartesian3(); var ffScratch = new Matrix4(); var hprTransformScratch = new Matrix4(); var hprRotationScratch = new Matrix3(); var hprQuaternionScratch = new Quaternion(); /** * Computes heading-pitch-roll angles from a transform in a particular reference frame. Heading is the rotation from the local north * direction where a positive angle is increasing eastward. Pitch is the rotation from the local east-north plane. Positive pitch angles * are above the plane. Negative pitch angles are below the plane. Roll is the first rotation applied about the local east axis. * * @param {Matrix4} transform The transform * @param {Ellipsoid} [ellipsoid=Ellipsoid.WGS84] The ellipsoid whose fixed frame is used in the transformation. * @param {Transforms.LocalFrameToFixedFrame} [fixedFrameTransform=Transforms.eastNorthUpToFixedFrame] A 4x4 transformation * matrix from a reference frame to the provided ellipsoid's fixed reference frame * @param {HeadingPitchRoll} [result] The object onto which to store the result. * @returns {HeadingPitchRoll} The modified result parameter or a new HeadingPitchRoll instance if none was provided. */ Transforms.fixedFrameToHeadingPitchRoll = function ( transform, ellipsoid, fixedFrameTransform, result ) { //>>includeStart('debug', pragmas.debug); Check.defined("transform", transform); //>>includeEnd('debug'); ellipsoid = defaultValue(ellipsoid, Ellipsoid.WGS84); fixedFrameTransform = defaultValue( fixedFrameTransform, Transforms.eastNorthUpToFixedFrame ); if (!defined(result)) { result = new HeadingPitchRoll(); } var center = Matrix4.getTranslation(transform, hprCenterScratch); if (Cartesian3.equals(center, Cartesian3.ZERO)) { result.heading = 0; result.pitch = 0; result.roll = 0; return result; } var toFixedFrame = Matrix4.inverseTransformation( fixedFrameTransform(center, ellipsoid, ffScratch), ffScratch ); var transformCopy = Matrix4.setScale(transform, noScale, hprTransformScratch); transformCopy = Matrix4.setTranslation( transformCopy, Cartesian3.ZERO, transformCopy ); toFixedFrame = Matrix4.multiply(toFixedFrame, transformCopy, toFixedFrame); var quaternionRotation = Quaternion.fromRotationMatrix( Matrix4.getMatrix3(toFixedFrame, hprRotationScratch), hprQuaternionScratch ); quaternionRotation = Quaternion.normalize( quaternionRotation, quaternionRotation ); return HeadingPitchRoll.fromQuaternion(quaternionRotation, result); }; var gmstConstant0 = 6 * 3600 + 41 * 60 + 50.54841; var gmstConstant1 = 8640184.812866; var gmstConstant2 = 0.093104; var gmstConstant3 = -6.2e-6; var rateCoef = 1.1772758384668e-19; var wgs84WRPrecessing = 7.2921158553e-5; var twoPiOverSecondsInDay = CesiumMath.TWO_PI / 86400.0; var dateInUtc = new JulianDate(); /** * Computes a rotation matrix to transform a point or vector from True Equator Mean Equinox (TEME) axes to the * pseudo-fixed axes at a given time. This method treats the UT1 time standard as equivalent to UTC. * * @param {JulianDate} date The time at which to compute the rotation matrix. * @param {Matrix3} [result] The object onto which to store the result. * @returns {Matrix3} The modified result parameter or a new Matrix3 instance if none was provided. * * @example * //Set the view to the inertial frame. * scene.postUpdate.addEventListener(function(scene, time) { * var now = Cesium.JulianDate.now(); * var offset = Cesium.Matrix4.multiplyByPoint(camera.transform, camera.position, new Cesium.Cartesian3()); * var transform = Cesium.Matrix4.fromRotationTranslation(Cesium.Transforms.computeTemeToPseudoFixedMatrix(now)); * var inverseTransform = Cesium.Matrix4.inverseTransformation(transform, new Cesium.Matrix4()); * Cesium.Matrix4.multiplyByPoint(inverseTransform, offset, offset); * camera.lookAtTransform(transform, offset); * }); */ Transforms.computeTemeToPseudoFixedMatrix = function (date, result) { //>>includeStart('debug', pragmas.debug); if (!defined(date)) { throw new DeveloperError("date is required."); } //>>includeEnd('debug'); // GMST is actually computed using UT1. We're using UTC as an approximation of UT1. // We do not want to use the function like convertTaiToUtc in JulianDate because // we explicitly do not want to fail when inside the leap second. dateInUtc = JulianDate.addSeconds( date, -JulianDate.computeTaiMinusUtc(date), dateInUtc ); var utcDayNumber = dateInUtc.dayNumber; var utcSecondsIntoDay = dateInUtc.secondsOfDay; var t; var diffDays = utcDayNumber - 2451545; if (utcSecondsIntoDay >= 43200.0) { t = (diffDays + 0.5) / TimeConstants.DAYS_PER_JULIAN_CENTURY; } else { t = (diffDays - 0.5) / TimeConstants.DAYS_PER_JULIAN_CENTURY; } var gmst0 = gmstConstant0 + t * (gmstConstant1 + t * (gmstConstant2 + t * gmstConstant3)); var angle = (gmst0 * twoPiOverSecondsInDay) % CesiumMath.TWO_PI; var ratio = wgs84WRPrecessing + rateCoef * (utcDayNumber - 2451545.5); var secondsSinceMidnight = (utcSecondsIntoDay + TimeConstants.SECONDS_PER_DAY * 0.5) % TimeConstants.SECONDS_PER_DAY; var gha = angle + ratio * secondsSinceMidnight; var cosGha = Math.cos(gha); var sinGha = Math.sin(gha); if (!defined(result)) { return new Matrix3( cosGha, sinGha, 0.0, -sinGha, cosGha, 0.0, 0.0, 0.0, 1.0 ); } result[0] = cosGha; result[1] = -sinGha; result[2] = 0.0; result[3] = sinGha; result[4] = cosGha; result[5] = 0.0; result[6] = 0.0; result[7] = 0.0; result[8] = 1.0; return result; }; /** * The source of IAU 2006 XYS data, used for computing the transformation between the * Fixed and ICRF axes. * @type {Iau2006XysData} * * @see Transforms.computeIcrfToFixedMatrix * @see Transforms.computeFixedToIcrfMatrix * * @private */ Transforms.iau2006XysData = new Iau2006XysData(); /** * The source of Earth Orientation Parameters (EOP) data, used for computing the transformation * between the Fixed and ICRF axes. By default, zero values are used for all EOP values, * yielding a reasonable but not completely accurate representation of the ICRF axes. * @type {EarthOrientationParameters} * * @see Transforms.computeIcrfToFixedMatrix * @see Transforms.computeFixedToIcrfMatrix * * @private */ Transforms.earthOrientationParameters = EarthOrientationParameters.NONE; var ttMinusTai = 32.184; var j2000ttDays = 2451545.0; /** * Preloads the data necessary to transform between the ICRF and Fixed axes, in either * direction, over a given interval. This function returns a promise that, when resolved, * indicates that the preload has completed. * * @param {TimeInterval} timeInterval The interval to preload. * @returns {Promise} A promise that, when resolved, indicates that the preload has completed * and evaluation of the transformation between the fixed and ICRF axes will * no longer return undefined for a time inside the interval. * * * @example * var interval = new Cesium.TimeInterval(...); * when(Cesium.Transforms.preloadIcrfFixed(interval), function() { * // the data is now loaded * }); * * @see Transforms.computeIcrfToFixedMatrix * @see Transforms.computeFixedToIcrfMatrix * @see when */ Transforms.preloadIcrfFixed = function (timeInterval) { var startDayTT = timeInterval.start.dayNumber; var startSecondTT = timeInterval.start.secondsOfDay + ttMinusTai; var stopDayTT = timeInterval.stop.dayNumber; var stopSecondTT = timeInterval.stop.secondsOfDay + ttMinusTai; var xysPromise = Transforms.iau2006XysData.preload( startDayTT, startSecondTT, stopDayTT, stopSecondTT ); var eopPromise = Transforms.earthOrientationParameters.getPromiseToLoad(); return when.all([xysPromise, eopPromise]); }; /** * Computes a rotation matrix to transform a point or vector from the International Celestial * Reference Frame (GCRF/ICRF) inertial frame axes to the Earth-Fixed frame axes (ITRF) * at a given time. This function may return undefined if the data necessary to * do the transformation is not yet loaded. * * @param {JulianDate} date The time at which to compute the rotation matrix. * @param {Matrix3} [result] The object onto which to store the result. If this parameter is * not specified, a new instance is created and returned. * @returns {Matrix3} The rotation matrix, or undefined if the data necessary to do the * transformation is not yet loaded. * * * @example * scene.postUpdate.addEventListener(function(scene, time) { * // View in ICRF. * var icrfToFixed = Cesium.Transforms.computeIcrfToFixedMatrix(time); * if (Cesium.defined(icrfToFixed)) { * var offset = Cesium.Cartesian3.clone(camera.position); * var transform = Cesium.Matrix4.fromRotationTranslation(icrfToFixed); * camera.lookAtTransform(transform, offset); * } * }); * * @see Transforms.preloadIcrfFixed */ Transforms.computeIcrfToFixedMatrix = function (date, result) { //>>includeStart('debug', pragmas.debug); if (!defined(date)) { throw new DeveloperError("date is required."); } //>>includeEnd('debug'); if (!defined(result)) { result = new Matrix3(); } var fixedToIcrfMtx = Transforms.computeFixedToIcrfMatrix(date, result); if (!defined(fixedToIcrfMtx)) { return undefined; } return Matrix3.transpose(fixedToIcrfMtx, result); }; var xysScratch = new Iau2006XysSample(0.0, 0.0, 0.0); var eopScratch = new EarthOrientationParametersSample( 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 ); var rotation1Scratch = new Matrix3(); var rotation2Scratch = new Matrix3(); /** * Computes a rotation matrix to transform a point or vector from the Earth-Fixed frame axes (ITRF) * to the International Celestial Reference Frame (GCRF/ICRF) inertial frame axes * at a given time. This function may return undefined if the data necessary to * do the transformation is not yet loaded. * * @param {JulianDate} date The time at which to compute the rotation matrix. * @param {Matrix3} [result] The object onto which to store the result. If this parameter is * not specified, a new instance is created and returned. * @returns {Matrix3} The rotation matrix, or undefined if the data necessary to do the * transformation is not yet loaded. * * * @example * // Transform a point from the ICRF axes to the Fixed axes. * var now = Cesium.JulianDate.now(); * var pointInFixed = Cesium.Cartesian3.fromDegrees(0.0, 0.0); * var fixedToIcrf = Cesium.Transforms.computeIcrfToFixedMatrix(now); * var pointInInertial = new Cesium.Cartesian3(); * if (Cesium.defined(fixedToIcrf)) { * pointInInertial = Cesium.Matrix3.multiplyByVector(fixedToIcrf, pointInFixed, pointInInertial); * } * * @see Transforms.preloadIcrfFixed */ Transforms.computeFixedToIcrfMatrix = function (date, result) { //>>includeStart('debug', pragmas.debug); if (!defined(date)) { throw new DeveloperError("date is required."); } //>>includeEnd('debug'); if (!defined(result)) { result = new Matrix3(); } // Compute pole wander var eop = Transforms.earthOrientationParameters.compute(date, eopScratch); if (!defined(eop)) { return undefined; } // There is no external conversion to Terrestrial Time (TT). // So use International Atomic Time (TAI) and convert using offsets. // Here we are assuming that dayTT and secondTT are positive var dayTT = date.dayNumber; // It's possible here that secondTT could roll over 86400 // This does not seem to affect the precision (unit tests check for this) var secondTT = date.secondsOfDay + ttMinusTai; var xys = Transforms.iau2006XysData.computeXysRadians( dayTT, secondTT, xysScratch ); if (!defined(xys)) { return undefined; } var x = xys.x + eop.xPoleOffset; var y = xys.y + eop.yPoleOffset; // Compute XYS rotation var a = 1.0 / (1.0 + Math.sqrt(1.0 - x * x - y * y)); var rotation1 = rotation1Scratch; rotation1[0] = 1.0 - a * x * x; rotation1[3] = -a * x * y; rotation1[6] = x; rotation1[1] = -a * x * y; rotation1[4] = 1 - a * y * y; rotation1[7] = y; rotation1[2] = -x; rotation1[5] = -y; rotation1[8] = 1 - a * (x * x + y * y); var rotation2 = Matrix3.fromRotationZ(-xys.s, rotation2Scratch); var matrixQ = Matrix3.multiply(rotation1, rotation2, rotation1Scratch); // Similar to TT conversions above // It's possible here that secondTT could roll over 86400 // This does not seem to affect the precision (unit tests check for this) var dateUt1day = date.dayNumber; var dateUt1sec = date.secondsOfDay - JulianDate.computeTaiMinusUtc(date) + eop.ut1MinusUtc; // Compute Earth rotation angle // The IERS standard for era is // era = 0.7790572732640 + 1.00273781191135448 * Tu // where // Tu = JulianDateInUt1 - 2451545.0 // However, you get much more precision if you make the following simplification // era = a + (1 + b) * (JulianDayNumber + FractionOfDay - 2451545) // era = a + (JulianDayNumber - 2451545) + FractionOfDay + b (JulianDayNumber - 2451545 + FractionOfDay) // era = a + FractionOfDay + b (JulianDayNumber - 2451545 + FractionOfDay) // since (JulianDayNumber - 2451545) represents an integer number of revolutions which will be discarded anyway. var daysSinceJ2000 = dateUt1day - 2451545; var fractionOfDay = dateUt1sec / TimeConstants.SECONDS_PER_DAY; var era = 0.779057273264 + fractionOfDay + 0.00273781191135448 * (daysSinceJ2000 + fractionOfDay); era = (era % 1.0) * CesiumMath.TWO_PI; var earthRotation = Matrix3.fromRotationZ(era, rotation2Scratch); // pseudoFixed to ICRF var pfToIcrf = Matrix3.multiply(matrixQ, earthRotation, rotation1Scratch); // Compute pole wander matrix var cosxp = Math.cos(eop.xPoleWander); var cosyp = Math.cos(eop.yPoleWander); var sinxp = Math.sin(eop.xPoleWander); var sinyp = Math.sin(eop.yPoleWander); var ttt = dayTT - j2000ttDays + secondTT / TimeConstants.SECONDS_PER_DAY; ttt /= 36525.0; // approximate sp value in rad var sp = (-47.0e-6 * ttt * CesiumMath.RADIANS_PER_DEGREE) / 3600.0; var cossp = Math.cos(sp); var sinsp = Math.sin(sp); var fToPfMtx = rotation2Scratch; fToPfMtx[0] = cosxp * cossp; fToPfMtx[1] = cosxp * sinsp; fToPfMtx[2] = sinxp; fToPfMtx[3] = -cosyp * sinsp + sinyp * sinxp * cossp; fToPfMtx[4] = cosyp * cossp + sinyp * sinxp * sinsp; fToPfMtx[5] = -sinyp * cosxp; fToPfMtx[6] = -sinyp * sinsp - cosyp * sinxp * cossp; fToPfMtx[7] = sinyp * cossp - cosyp * sinxp * sinsp; fToPfMtx[8] = cosyp * cosxp; return Matrix3.multiply(pfToIcrf, fToPfMtx, result); }; var pointToWindowCoordinatesTemp = new Cartesian4(); /** * Transform a point from model coordinates to window coordinates. * * @param {Matrix4} modelViewProjectionMatrix The 4x4 model-view-projection matrix. * @param {Matrix4} viewportTransformation The 4x4 viewport transformation. * @param {Cartesian3} point The point to transform. * @param {Cartesian2} [result] The object onto which to store the result. * @returns {Cartesian2} The modified result parameter or a new Cartesian2 instance if none was provided. */ Transforms.pointToWindowCoordinates = function ( modelViewProjectionMatrix, viewportTransformation, point, result ) { result = Transforms.pointToGLWindowCoordinates( modelViewProjectionMatrix, viewportTransformation, point, result ); result.y = 2.0 * viewportTransformation[5] - result.y; return result; }; /** * @private */ Transforms.pointToGLWindowCoordinates = function ( modelViewProjectionMatrix, viewportTransformation, point, result ) { //>>includeStart('debug', pragmas.debug); if (!defined(modelViewProjectionMatrix)) { throw new DeveloperError("modelViewProjectionMatrix is required."); } if (!defined(viewportTransformation)) { throw new DeveloperError("viewportTransformation is required."); } if (!defined(point)) { throw new DeveloperError("point is required."); } //>>includeEnd('debug'); if (!defined(result)) { result = new Cartesian2(); } var tmp = pointToWindowCoordinatesTemp; Matrix4.multiplyByVector( modelViewProjectionMatrix, Cartesian4.fromElements(point.x, point.y, point.z, 1, tmp), tmp ); Cartesian4.multiplyByScalar(tmp, 1.0 / tmp.w, tmp); Matrix4.multiplyByVector(viewportTransformation, tmp, tmp); return Cartesian2.fromCartesian4(tmp, result); }; var normalScratch = new Cartesian3(); var rightScratch = new Cartesian3(); var upScratch = new Cartesian3(); /** * Transform a position and velocity to a rotation matrix. * * @param {Cartesian3} position The position to transform. * @param {Cartesian3} velocity The velocity vector to transform. * @param {Ellipsoid} [ellipsoid=Ellipsoid.WGS84] The ellipsoid whose fixed frame is used in the transformation. * @param {Matrix3} [result] The object onto which to store the result. * @returns {Matrix3} The modified result parameter or a new Matrix3 instance if none was provided. */ Transforms.rotationMatrixFromPositionVelocity = function ( position, velocity, ellipsoid, result ) { //>>includeStart('debug', pragmas.debug); if (!defined(position)) { throw new DeveloperError("position is required."); } if (!defined(velocity)) { throw new DeveloperError("velocity is required."); } //>>includeEnd('debug'); var normal = defaultValue(ellipsoid, Ellipsoid.WGS84).geodeticSurfaceNormal( position, normalScratch ); var right = Cartesian3.cross(velocity, normal, rightScratch); if (Cartesian3.equalsEpsilon(right, Cartesian3.ZERO, CesiumMath.EPSILON6)) { right = Cartesian3.clone(Cartesian3.UNIT_X, right); } var up = Cartesian3.cross(right, velocity, upScratch); Cartesian3.normalize(up, up); Cartesian3.cross(velocity, up, right); Cartesian3.negate(right, right); Cartesian3.normalize(right, right); if (!defined(result)) { result = new Matrix3(); } result[0] = velocity.x; result[1] = velocity.y; result[2] = velocity.z; result[3] = right.x; result[4] = right.y; result[5] = right.z; result[6] = up.x; result[7] = up.y; result[8] = up.z; return result; }; var swizzleMatrix = new Matrix4( 0.0, 0.0, 1.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0 ); var scratchCartographic = new Cartographic(); var scratchCartesian3Projection = new Cartesian3(); var scratchCenter = new Cartesian3(); var scratchRotation = new Matrix3(); var scratchFromENU = new Matrix4(); var scratchToENU = new Matrix4(); /** * @private */ Transforms.basisTo2D = function (projection, matrix, result) { //>>includeStart('debug', pragmas.debug); if (!defined(projection)) { throw new DeveloperError("projection is required."); } if (!defined(matrix)) { throw new DeveloperError("matrix is required."); } if (!defined(result)) { throw new DeveloperError("result is required."); } //>>includeEnd('debug'); var rtcCenter = Matrix4.getTranslation(matrix, scratchCenter); var ellipsoid = projection.ellipsoid; // Get the 2D Center var cartographic = ellipsoid.cartesianToCartographic( rtcCenter, scratchCartographic ); var projectedPosition = projection.project( cartographic, scratchCartesian3Projection ); Cartesian3.fromElements( projectedPosition.z, projectedPosition.x, projectedPosition.y, projectedPosition ); // Assuming the instance are positioned in WGS84, invert the WGS84 transform to get the local transform and then convert to 2D var fromENU = Transforms.eastNorthUpToFixedFrame( rtcCenter, ellipsoid, scratchFromENU ); var toENU = Matrix4.inverseTransformation(fromENU, scratchToENU); var rotation = Matrix4.getMatrix3(matrix, scratchRotation); var local = Matrix4.multiplyByMatrix3(toENU, rotation, result); Matrix4.multiply(swizzleMatrix, local, result); // Swap x, y, z for 2D Matrix4.setTranslation(result, projectedPosition, result); // Use the projected center return result; }; /** * @private */ Transforms.wgs84To2DModelMatrix = function (projection, center, result) { //>>includeStart('debug', pragmas.debug); if (!defined(projection)) { throw new DeveloperError("projection is required."); } if (!defined(center)) { throw new DeveloperError("center is required."); } if (!defined(result)) { throw new DeveloperError("result is required."); } //>>includeEnd('debug'); var ellipsoid = projection.ellipsoid; var fromENU = Transforms.eastNorthUpToFixedFrame( center, ellipsoid, scratchFromENU ); var toENU = Matrix4.inverseTransformation(fromENU, scratchToENU); var cartographic = ellipsoid.cartesianToCartographic( center, scratchCartographic ); var projectedPosition = projection.project( cartographic, scratchCartesian3Projection ); Cartesian3.fromElements( projectedPosition.z, projectedPosition.x, projectedPosition.y, projectedPosition ); var translation = Matrix4.fromTranslation(projectedPosition, scratchFromENU); Matrix4.multiply(swizzleMatrix, toENU, result); Matrix4.multiply(translation, result, result); return result; }; export default Transforms;