UNPKG

mapillary-js

Version:

WebGL JavaScript library for displaying street level imagery from mapillary.com

281 lines (242 loc) 9.53 kB
/** * @class GeoCoords * * @classdesc Converts coordinates between the geodetic (WGS84), * Earth-Centered, Earth-Fixed (ECEF) and local topocentric * East, North, Up (ENU) reference frames. * * The WGS84 has latitude (degrees), longitude (degrees) and * altitude (meters) values. * * The ECEF Z-axis pierces the north pole and the * XY-axis defines the equatorial plane. The X-axis extends * from the geocenter to the intersection of the Equator and * the Greenwich Meridian. All values in meters. * * The WGS84 parameters are: * * a = 6378137 * b = a * (1 - f) * f = 1 / 298.257223563 * e = Math.sqrt((a^2 - b^2) / a^2) * e' = Math.sqrt((a^2 - b^2) / b^2) * * The WGS84 to ECEF conversion is performed using the following: * * X = (N - h) * cos(phi) * cos(lambda) * Y = (N + h) * cos(phi) * sin(lambda) * Z = (b^2 * N / a^2 + h) * sin(phi) * * where * * phi = latitude * lambda = longitude * h = height above ellipsoid (altitude) * N = Radius of curvature (meters) * = a / Math.sqrt(1 - e^2 * sin(phi)^2) * * The ECEF to WGS84 conversion is performed using the following: * * phi = arctan((Z + e'^2 * b * sin(theta)^3) / (p - e^2 * a * cos(theta)^3)) * lambda = arctan(Y / X) * h = p / cos(phi) - N * * where * * p = Math.sqrt(X^2 + Y^2) * theta = arctan(Z * a / p * b) * * In the ENU reference frame the x-axis points to the * East, the y-axis to the North and the z-axis Up. All values * in meters. * * The ECEF to ENU conversion is performed using the following: * * | x | | -sin(lambda_r) cos(lambda_r) 0 | | X - X_r | * | y | = | -sin(phi_r) * cos(lambda_r) -sin(phi_r) * sin(lambda_r) cos(phi_r) | | Y - Y_r | * | z | | cos(phi_r) * cos(lambda_r) cos(phi_r) * sin(lambda_r) sin(phi_r) | | Z - Z_r | * * where * * phi_r = latitude of reference * lambda_r = longitude of reference * X_r, Y_r, Z_r = ECEF coordinates of reference * * The ENU to ECEF conversion is performed by solving the above equation for X, Y, Z. * * WGS84 to ENU and ENU to WGS84 are two step conversions with ECEF calculated in * the first step for both conversions. */ export class GeoCoords { private _wgs84a: number = 6378137.0; private _wgs84b: number = 6356752.31424518; /** * Convert coordinates from geodetic (WGS84) reference to local topocentric * (ENU) reference. * * @param {number} lat Latitude in degrees. * @param {number} lon Longitude in degrees. * @param {number} alt Altitude in meters. * @param {number} refLat Reference latitude in degrees. * @param {number} refLon Reference longitude in degrees. * @param {number} refAlt Reference altitude in meters. * @returns {Array<number>} The x, y, z local topocentric ENU coordinates. */ public geodeticToEnu( lat: number, lon: number, alt: number, refLat: number, refLon: number, refAlt: number): number[] { let ecef: number[] = this.geodeticToEcef(lat, lon, alt); return this.ecefToEnu(ecef[0], ecef[1], ecef[2], refLat, refLon, refAlt); } /** * Convert coordinates from local topocentric (ENU) reference to * geodetic (WGS84) reference. * * @param {number} x Topocentric ENU coordinate in East direction. * @param {number} y Topocentric ENU coordinate in North direction. * @param {number} z Topocentric ENU coordinate in Up direction. * @param {number} refLat Reference latitude in degrees. * @param {number} refLon Reference longitude in degrees. * @param {number} refAlt Reference altitude in meters. * @returns {Array<number>} The latitude and longitude in degrees * as well as altitude in meters. */ public enuToGeodetic( x: number, y: number, z: number, refLat: number, refLon: number, refAlt: number): number[] { let ecef: number[] = this.enuToEcef(x, y, z, refLat, refLon, refAlt); return this.ecefToGeodetic(ecef[0], ecef[1], ecef[2]); } /** * Convert coordinates from Earth-Centered, Earth-Fixed (ECEF) reference * to local topocentric (ENU) reference. * * @param {number} X ECEF X-value. * @param {number} Y ECEF Y-value. * @param {number} Z ECEF Z-value. * @param {number} refLat Reference latitude in degrees. * @param {number} refLon Reference longitude in degrees. * @param {number} refAlt Reference altitude in meters. * @returns {Array<number>} The x, y, z topocentric ENU coordinates in East, North * and Up directions respectively. */ public ecefToEnu( X: number, Y: number, Z: number, refLat: number, refLon: number, refAlt: number): number[] { let refEcef: number[] = this.geodeticToEcef(refLat, refLon, refAlt); let V: number[] = [X - refEcef[0], Y - refEcef[1], Z - refEcef[2]]; refLat = refLat * Math.PI / 180.0; refLon = refLon * Math.PI / 180.0; let cosLat: number = Math.cos(refLat); let sinLat: number = Math.sin(refLat); let cosLon: number = Math.cos(refLon); let sinLon: number = Math.sin(refLon); let x: number = -sinLon * V[0] + cosLon * V[1]; let y: number = -sinLat * cosLon * V[0] - sinLat * sinLon * V[1] + cosLat * V[2]; let z: number = cosLat * cosLon * V[0] + cosLat * sinLon * V[1] + sinLat * V[2]; return [x, y, z]; } /** * Convert coordinates from local topocentric (ENU) reference * to Earth-Centered, Earth-Fixed (ECEF) reference. * * @param {number} x Topocentric ENU coordinate in East direction. * @param {number} y Topocentric ENU coordinate in North direction. * @param {number} z Topocentric ENU coordinate in Up direction. * @param {number} refLat Reference latitude in degrees. * @param {number} refLon Reference longitude in degrees. * @param {number} refAlt Reference altitude in meters. * @returns {Array<number>} The X, Y, Z ECEF coordinates. */ public enuToEcef( x: number, y: number, z: number, refLat: number, refLon: number, refAlt: number): number[] { let refEcef: number[] = this.geodeticToEcef(refLat, refLon, refAlt); refLat = refLat * Math.PI / 180.0; refLon = refLon * Math.PI / 180.0; let cosLat: number = Math.cos(refLat); let sinLat: number = Math.sin(refLat); let cosLon: number = Math.cos(refLon); let sinLon: number = Math.sin(refLon); let X: number = -sinLon * x - sinLat * cosLon * y + cosLat * cosLon * z + refEcef[0]; let Y: number = cosLon * x - sinLat * sinLon * y + cosLat * sinLon * z + refEcef[1]; let Z: number = cosLat * y + sinLat * z + refEcef[2]; return [X, Y, Z]; } /** * Convert coordinates from geodetic reference (WGS84) to Earth-Centered, * Earth-Fixed (ECEF) reference. * * @param {number} lat Latitude in degrees. * @param {number} lon Longitude in degrees. * @param {number} alt Altitude in meters. * @returns {Array<number>} The X, Y, Z ECEF coordinates. */ public geodeticToEcef(lat: number, lon: number, alt: number): number[] { let a: number = this._wgs84a; let b: number = this._wgs84b; lat = lat * Math.PI / 180.0; lon = lon * Math.PI / 180.0; let cosLat: number = Math.cos(lat); let sinLat: number = Math.sin(lat); let cosLon: number = Math.cos(lon); let sinLon: number = Math.sin(lon); let a2: number = a * a; let b2: number = b * b; let L: number = 1.0 / Math.sqrt(a2 * cosLat * cosLat + b2 * sinLat * sinLat); let nhcl: number = (a2 * L + alt) * cosLat; let X: number = nhcl * cosLon; let Y: number = nhcl * sinLon; let Z: number = (b2 * L + alt) * sinLat; return [X, Y, Z]; } /** * Convert coordinates from Earth-Centered, Earth-Fixed (ECEF) reference * to geodetic reference (WGS84). * * @param {number} X ECEF X-value. * @param {number} Y ECEF Y-value. * @param {number} Z ECEF Z-value. * @returns {Array<number>} The latitude and longitude in degrees * as well as altitude in meters. */ public ecefToGeodetic(X: number, Y: number, Z: number): number[] { let a: number = this._wgs84a; let b: number = this._wgs84b; let a2: number = a * a; let b2: number = b * b; let a2mb2: number = a2 - b2; let ea: number = Math.sqrt(a2mb2 / a2); let eb: number = Math.sqrt(a2mb2 / b2); let p: number = Math.sqrt(X * X + Y * Y); let theta: number = Math.atan2(Z * a, p * b); let sinTheta: number = Math.sin(theta); let cosTheta: number = Math.cos(theta); let lon: number = Math.atan2(Y, X); let lat: number = Math.atan2(Z + eb * eb * b * sinTheta * sinTheta * sinTheta, p - ea * ea * a * cosTheta * cosTheta * cosTheta); let sinLat: number = Math.sin(lat); let cosLat: number = Math.cos(lat); let N: number = a / Math.sqrt(1 - ea * ea * sinLat * sinLat); let alt: number = p / cosLat - N; return [lat * 180.0 / Math.PI, lon * 180.0 / Math.PI, alt]; } } export default GeoCoords;