UNPKG

itowns

Version:

A JS/WebGL framework for 3D geospatial data visualization

531 lines (460 loc) 24.5 kB
"use strict"; var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); var _typeof = require("@babel/runtime/helpers/typeof"); Object.defineProperty(exports, "__esModule", { value: true }); exports["default"] = void 0; var THREE = _interopRequireWildcard(require("three")); var _proj = _interopRequireDefault(require("proj4")); var _Coordinates = _interopRequireDefault(require("../Core/Geographic/Coordinates")); function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function _getRequireWildcardCache(nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); } function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || _typeof(obj) !== "object" && typeof obj !== "function") { return { "default": obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj["default"] = obj; if (cache) { cache.set(obj, newObj); } return newObj; } var DEG2RAD = THREE.MathUtils.DEG2RAD; var matrix = new THREE.Matrix4(); var north = new THREE.Vector3(); var east = new THREE.Vector3(); var axis = new THREE.Vector3().set(0, 0, 1); var coord = new _Coordinates["default"]('EPSG:4326', 0, 0, 0); var euler = new THREE.Euler(); var quat = new THREE.Quaternion(); function quaternionIdentity(coordinates) { var target = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : new THREE.Quaternion(); return coordinates ? target.set(0, 0, 0, 1) : quaternionIdentity; } /** * The OrientationUtils module provides methods to compute the quaternion that * models a rotation defined with various conventions, including between different * CRS. * The local <a href="https://en.wikipedia.org/wiki/Local_tangent_plane_coordinates#Local_east,_north,_up_(ENU)_coordinates"> * East/North/Up frame (ENU)</a> is used as a pivot frame when computing the rotation between two distinct CRS. * If the origin of the frame is undefined, CRS-related methods precompute and return a function * that can be applied efficiently to many points of origin. * Otherwise, the target quaternion is returned at the provided origin coordinates. * * @example * // Compute the rotation around the point of origin from a frame aligned with Lambert93 axes (epsg:2154), * // to the geocentric frame (epsg:4978) * quat_crs2crs = OrientationUtils.quaternionFromCRSToCRS("EPSG:2154", "EPSG:4978")(origin); * // Compute the rotation of a sensor platform defined by its attitude * quat_attitude = OrientationUtils.quaternionFromAttitude(attitude); * // Compute the rotation from the sensor platform frame to the geocentric frame * quat = quat_crs2crs.multiply(quat_attitude); * * @module OrientationUtils */ var _default = { /** * @typedef {Object} Attitude * Properties are either defined as (omega, phi, kappa) or as (roll, pitch, * heading) or all `undefined`. * * @property {number} omega - angle in degrees * @property {number} phi - angle in degrees * @property {number} kappa - angle in degrees * @property {number} roll - angle in degrees * @property {number} pitch - angle in degrees * @property {number} heading - angle in degrees */ /** * The transform from the platform frame to the local East, North, Up (ENU) * frame is `RotationZ(heading).RotationX(pitch).RotationY(roll)` * * @param {number} [roll=0] - angle in degrees * @param {number} [pitch=0] - angle in degrees * @param {number} [heading=0] - angle in degrees * @param {THREE.Quaternion} [target=new THREE.Quaternion()] - output Quaternion * * @return {THREE.Quaternion} target quaternion */ quaternionFromRollPitchHeading: function quaternionFromRollPitchHeading() { var roll = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 0; var pitch = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0; var heading = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 0; var target = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : new THREE.Quaternion(); roll *= DEG2RAD; pitch *= DEG2RAD; heading *= DEG2RAD; // return this.setFromEuler(euler.set(pitch, roll, heading , 'ZXY')).conjugate(); return target.setFromEuler(euler.set(-pitch, -roll, -heading, 'YXZ')); // optimized version of above }, /** * From * [DocMicMac](https://github.com/micmacIGN/Documentation/raw/master/DocMicMac.pdf), * the transform from the platform frame to the local East, North, Up (ENU) * frame is: * * ``` * RotationX(omega).RotationY(phi).RotationZ(kappa).RotationX(PI) * RotationX(PI) <=> Quaternion(1,0,0,0) : converts between the 2 conventions for the camera local frame: * X right, Y bottom, Z front : convention in photogrammetry and computer vision * X right, Y top, Z back : convention in webGL, threejs * ``` * * @param {number} [omega=0] - angle in degrees * @param {number} [phi=0] - angle in degrees * @param {number} [kappa=0] - angle in degrees * @param {THREE.Quaternion} [target=new THREE.Quaternion()] output Quaternion * * @return {THREE.Quaternion} target quaternion */ quaternionFromOmegaPhiKappa: function quaternionFromOmegaPhiKappa() { var omega = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 0; var phi = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0; var kappa = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 0; var target = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : new THREE.Quaternion(); omega *= DEG2RAD; phi *= DEG2RAD; kappa *= DEG2RAD; target.setFromEuler(euler.set(omega, phi, kappa, 'XYZ')); target.set(target.w, target.z, -target.y, -target.x); // <=> target.multiply(new THREE.Quaternion(1, 0, 0, 0)); return target; }, /** * Set the quaternion according to the rotation from the platform frame to * the local frame. * * @param {Attitude} attitude - Attitude * @param {THREE.Quaternion} [target=new THREE.Quaternion()] output Quaternion * * @return {THREE.Quaternion} target quaternion */ quaternionFromAttitude: function quaternionFromAttitude(attitude) { var target = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : new THREE.Quaternion(); if (attitude.roll !== undefined || attitude.pitch !== undefined || attitude.heading !== undefined) { return this.quaternionFromRollPitchHeading(attitude.roll, attitude.pitch, attitude.heading, target); } if (attitude.omega !== undefined || attitude.phi !== undefined || attitude.kappa !== undefined) { return this.quaternionFromOmegaPhiKappa(attitude.omega, attitude.phi, attitude.kappa, target); } return target.set(0, 0, 0, 1); }, /** * FunctionOrQuaternion is either a THREE.Quaternion or a function that accepts * arguments `(coordinates, target)` and returns the quaternion that models a rotation * around the point of origin. If target is not provided, a new quaternion is * created and returned instead. * * @typedef {function|THREE.Quaternion} FunctionOrQuaternion * * @property {Coordinates} coordinates the origin of the local East North Up * (ENU) frame * @property {THREE.Quaternion} [target=new THREE.Quaternion()] output Quaternion. */ /** * A Projection object models a Coordinate Reference System (CRS). * Such an object is usually created with proj4 using `proj4.defs(crs);` * * @typedef {Object} Projection * * @property {string} projName */ /** * Set the quaternion according to the rotation from the local East North Up (ENU) * frame to the geocentric frame. The up direction of the ENU frame is * provided by the normalized geodetic normal of the provided coordinates * (geodeticNormal property). * * @param {Coordinates} [coordinates] the origin of the local East North Up * (ENU) frame * @param {THREE.Quaternion} [target=new THREE.Quaternion()] output Quaternion * @return {FunctionOrQuaternion} The target quaternion if coordinates is defined, otherwise, a function to compute it from coordinates. */ quaternionFromEnuToGeocent: function quaternionFromEnuToGeocent(coordinates) { var target = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : new THREE.Quaternion(); if (coordinates) { return this.quaternionFromEnuToGeocent()(coordinates, target); } return function (coordinates) { var target = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : new THREE.Quaternion(); var up = coordinates.geodesicNormal; if (up.x == 0 && up.y == 0) { return target.set(0, 0, 0, 1); } // this is an optimized version of matrix.lookAt(up, new THREE.Vector3(0, 0, 0), new THREE.Vector3(0, 0, 1)); east.set(-up.y, up.x, 0).normalize(); north.crossVectors(up, east); matrix.makeBasis(east, north, up); return target.setFromRotationMatrix(matrix); }; }, /** * Set the quaternion according to the rotation from a geocentric frame * to the local East North Up (ENU) frame. The up direction of the ENU frame is * provided by the normalized geodetic normal of the provided coordinates * (geodeticNormal property). * * @param {Coordinates} [coordinates] the origin of the local East North Up * (ENU) frame * @param {THREE.Quaternion} [target=new THREE.Quaternion()] output Quaternion * @return {FunctionOrQuaternion} The target quaternion if coordinates is defined, otherwise, a function to compute it from coordinates. */ quaternionFromGeocentToEnu: function quaternionFromGeocentToEnu(coordinates) { var target = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : new THREE.Quaternion(); if (coordinates) { return this.quaternionFromGeocentToEnu()(coordinates, target); } var toGeocent = this.quaternionFromEnuToGeocent(); return function (coordinates) { var target = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : new THREE.Quaternion(); return toGeocent(coordinates, target).conjugate(); }; }, /** * Computes the rotation from a Lambert Conformal Conic (LCC) frame to the local East North Up (ENU) frame. * The quaternion accounts for the * <a href="https://geodesie.ign.fr/contenu/fichiers/documentation/algorithmes/alg0060.pdf">meridian convergence</a> * between the ENU and LCC frames. * This is a generally small rotation around Z. * * @param {Projection} proj the lcc projection (may be parsed using proj4) * @param {number} proj.lat0 - the latitude of origin * @param {number} proj.long0 - the longitude of the central meridian * @param {Coordinates} [coordinates] coordinates the origin of the local East North Up * (ENU) frame * @param {THREE.Quaternion} [target=new THREE.Quaternion()] output Quaternion * @return {FunctionOrQuaternion} The target quaternion if coordinates is defined, otherwise, a function to compute it from coordinates. */ quaternionFromLCCToEnu: function quaternionFromLCCToEnu(proj, coordinates) { var target = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : new THREE.Quaternion(); if (coordinates) { return this.quaternionFromLCCToEnu(proj)(coordinates, target); } var sinlat0 = Math.sin(proj.lat0); return function (coordinates) { var target = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : new THREE.Quaternion(); var _long = coordinates.as(coord.crs, coord).longitude * DEG2RAD; return target.setFromAxisAngle(axis, sinlat0 * (proj.long0 - _long)); }; }, /** * Computes the rotation from the local East North Up (ENU) frame to a Lambert Conformal Conic (LCC) frame. * The quaternion accounts for the * <a href="https://geodesie.ign.fr/contenu/fichiers/documentation/algorithmes/alg0060.pdf">meridian convergence</a> * between the ENU and LCC frames. * This is a generally small rotation around Z. * * @param {Projection} proj the lcc projection (may be parsed using proj4) * @param {number} proj.lat0 - the latitude of origin * @param {number} proj.long0 - the longitude of the central meridian * @param {Coordinates} [coordinates] coordinates the origin of the local East North Up * (ENU) frame * @param {THREE.Quaternion} [target=new THREE.Quaternion()] output Quaternion * @return {FunctionOrQuaternion} The target quaternion if coordinates is defined, otherwise, a function to compute it from coordinates. */ quaternionFromEnuToLCC: function quaternionFromEnuToLCC(proj, coordinates) { var target = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : new THREE.Quaternion(); if (coordinates) { return this.quaternionFromEnuToLCC(proj)(coordinates, target); } var fromLCC = this.quaternionFromLCCToEnu(proj); return function (coordinates) { var target = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : new THREE.Quaternion(); return fromLCC(coordinates, target).conjugate(); }; }, /** * Computes the rotation from a Transverse Mercator frame (TMerc) to the local East North Up (ENU) frame. * The quaternion accounts for the * <a href="https://geodesie.ign.fr/contenu/fichiers/documentation/algorithmes/alg0061.pdf">meridian convergence</a> * between the ENU and TMerc frames. * This is a generally small rotation around Z. * * @param {Projection} proj the tmerc projection (may be parsed using proj4) * @param {number} proj.e - the excentricity of the ellipsoid (supersedes {proj.a} and {proj.b}) * @param {number} proj.a - the semimajor radius of the ellipsoid axis * @param {number} proj.b - the semiminor radius of the ellipsoid axis * @param {number} proj.long0 - the longitude of the central meridian * * @param {Coordinates} [coordinates] coordinates the origin of the local East North Up * (ENU) frame * @param {THREE.Quaternion} [target=new THREE.Quaternion()] output Quaternion * @return {FunctionOrQuaternion} The target quaternion if coordinates is defined, otherwise, a function to compute it from coordinates. */ quaternionFromTMercToEnu: function quaternionFromTMercToEnu(proj, coordinates) { var target = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : new THREE.Quaternion(); if (coordinates) { return this.quaternionFromTMercToEnu(proj)(coordinates, target); } var a2 = proj.a * proj.a; var b2 = proj.b * proj.b; var e2 = proj.e * proj.e; var eta0 = proj.e ? e2 / (1 - e2) : a2 / b2 - 1; return function (coordinates) { var target = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : new THREE.Quaternion(); coordinates.as(coord.crs, coord); var _long2 = coord.longitude * DEG2RAD; var lat = coord.latitude * DEG2RAD; var dlong = proj.long0 - _long2; var coslat = Math.cos(lat); var sinlat = Math.sin(lat); var coslat2 = coslat * coslat; var dl2 = dlong * dlong * coslat2; var eta2 = eta0 * coslat2; return target.setFromAxisAngle(axis, dlong * sinlat * (1 + dl2 / 3 * (1 + 3 * eta2 + 2 * eta2 * eta2) + dl2 * dl2 * (2 - sinlat / coslat) / 15)); }; }, /** * Computes the rotation from the local East North Up (ENU) to a Transverse Mercator frame. * The quaternion accounts for the * <a href="https://geodesie.ign.fr/contenu/fichiers/documentation/algorithmes/alg0061.pdf">meridian convergence</a> * between the ENU and TMerc frames. * This is a generally small rotation around Z. * * @param {Projection} proj the tmerc projection (may be parsed using proj4) * @param {number} proj.e - the excentricity of the ellipsoid (supersedes * {proj.a} and {proj.b}) * @param {number} proj.a - the semimajor radius of the ellipsoid axis * @param {number} proj.b - the semiminor radius of the ellipsoid axis * @param {number} proj.long0 - the longitude of the central meridian * * @param {Coordinates} [coordinates] coordinates the origin of the local East North Up * (ENU) frame * @param {THREE.Quaternion} [target=new THREE.Quaternion()] output Quaternion * @return {FunctionOrQuaternion} The target quaternion if coordinates is defined, otherwise, a function to compute it from coordinates. */ quaternionFromEnuToTMerc: function quaternionFromEnuToTMerc(proj, coordinates) { var target = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : new THREE.Quaternion(); if (coordinates) { return this.quaternionFromEnuToTMerc(proj)(coordinates, target); } var fromTMerc = this.quaternionFromTMercToEnu(proj); return function (coordinates) { var target = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : new THREE.Quaternion(); return fromTMerc(coordinates, target).conjugate(); }; }, /** * Computes the rotation from a LongLat frame to the local East North Up (ENU) frame. * The identity quaternion (0,0,0,1) is returned, as longlat and ENU frame are assumed to be aligned. * * @param {Coordinates} [coordinates] coordinates the origin of the local East North Up * (ENU) frame * @param {THREE.Quaternion} [target=new THREE.Quaternion()] output Quaternion * @return {FunctionOrQuaternion} The target quaternion if coordinates is defined, otherwise, a function to compute it from coordinates. */ quaternionFromLongLatToEnu: function quaternionFromLongLatToEnu(coordinates) { var target = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : new THREE.Quaternion(); return quaternionIdentity(coordinates, target); }, /** * Computes the rotation from the local East North Up (ENU) frame to a LongLat frame. * The identity quaternion (0,0,0,1) is returned, as longlat and ENU frame are assumed to be aligned. * * @param {Coordinates} [coordinates] coordinates the origin of the local East North Up * (ENU) frame * @param {THREE.Quaternion} [target=new THREE.Quaternion()] output Quaternion * @return {FunctionOrQuaternion} The target quaternion if coordinates is defined, otherwise, a function to compute it from coordinates. */ quaternionFromEnuToLongLat: function quaternionFromEnuToLongLat(coordinates) { var target = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : new THREE.Quaternion(); return quaternionIdentity(coordinates, target); }, /** * Warns for an unimplemented projection, sets the quaternion to the * identity (0,0,0,1). * * @param {Projection} proj - the unimplemented projection (may be parsed * using proj4) * * @param {Coordinates} [coordinates] coordinates the origin of the local East North Up * (ENU) frame * @param {THREE.Quaternion} [target=new THREE.Quaternion()] output Quaternion * @return {FunctionOrQuaternion} The target quaternion if coordinates is defined, otherwise, a function to compute it from coordinates. */ quaternionUnimplemented: function quaternionUnimplemented(proj, coordinates) { var target = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : new THREE.Quaternion(); console.warn('This quaternion function is not implemented for projections of type', proj.projName); return quaternionIdentity(coordinates, target); }, /** * Compute the quaternion that models the rotation from the local East North * Up (ENU) frame to the frame of the given crs. * * @param {string|Projection} crsOrProj - the CRS of the target frame or its * proj4-compatible object. * * @param {Coordinates} [coordinates] coordinates the origin of the local East North Up * (ENU) frame * @param {THREE.Quaternion} [target=new THREE.Quaternion()] output Quaternion * @return {FunctionOrQuaternion} The target quaternion if coordinates is defined, otherwise, a function to compute it from coordinates. */ quaternionFromEnuToCRS: function quaternionFromEnuToCRS(crsOrProj, coordinates) { var target = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : new THREE.Quaternion(); if (coordinates) { return this.quaternionFromEnuToCRS(crsOrProj)(coordinates, target); } var proj = crsOrProj.projName ? crsOrProj : _proj["default"].defs(crsOrProj); switch (proj.projName) { case 'geocent': return this.quaternionFromEnuToGeocent(); case 'lcc': return this.quaternionFromEnuToLCC(proj); case 'tmerc': return this.quaternionFromEnuToTMerc(proj); case 'longlat': return this.quaternionFromEnuToLongLat(); default: return this.quaternionUnimplemented(proj); } }, /** * Compute the quaternion that models the rotation from the frame of the * given crs to the local East North Up (ENU) frame. * * @param {string|Projection} crsOrProj - the CRS of the source frame or its * proj4-compatible object. * * @param {Coordinates} [coordinates] coordinates the origin of the local East North Up * (ENU) frame * @param {THREE.Quaternion} [target=new THREE.Quaternion()] output Quaternion * @return {FunctionOrQuaternion} The target quaternion if coordinates is defined, otherwise, a function to compute it from coordinates. */ quaternionFromCRSToEnu: function quaternionFromCRSToEnu(crsOrProj, coordinates) { var target = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : new THREE.Quaternion(); if (coordinates) { return this.quaternionFromCRSToEnu(crsOrProj)(coordinates, target); } var proj = crsOrProj.projName ? crsOrProj : _proj["default"].defs(crsOrProj); switch (proj.projName) { case 'geocent': return this.quaternionFromGeocentToEnu(); case 'lcc': return this.quaternionFromLCCToEnu(proj); case 'tmerc': return this.quaternionFromTMercToEnu(proj); case 'longlat': return this.quaternionFromLongLatToEnu(); default: return this.quaternionUnimplemented(proj); } }, /** * Return the function that computes the quaternion that represents a * rotation of coordinates between two CRS frames. * * @param {string} crsIn - the CRS of the input frame. * @param {string} crsOut - the CRS of the output frame. * @param {Coordinates} [coordinates] coordinates - the origin of the local East North Up * (ENU) frame * @param {THREE.Quaternion} [target=new THREE.Quaternion()] output Quaternion * @return {FunctionOrQuaternion} The target quaternion if coordinates is defined, otherwise, a function to compute it from coordinates. */ quaternionFromCRSToCRS: function quaternionFromCRSToCRS(crsIn, crsOut, coordinates) { var target = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : new THREE.Quaternion(); if (coordinates) { return this.quaternionFromCRSToCRS(crsIn, crsOut)(coordinates, target); } if (crsIn == crsOut) { return function () { var target = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : new THREE.Quaternion(); return target.set(0, 0, 0, 1); }; } // get rotations from the local East/North/Up (ENU) frame to both CRS. var fromCrs = this.quaternionFromCRSToEnu(crsIn); var toCrs = this.quaternionFromEnuToCRS(crsOut); return function (origin) { var target = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : new THREE.Quaternion(); return toCrs(origin, target).multiply(fromCrs(origin, quat)); }; } }; exports["default"] = _default;