UNPKG

wcslight

Version:
352 lines 14.8 kB
/** * Summary. (bla bla bla) * * Description. (bla bla bla) * * @link github https://github.com/fab77/wcslight * @author Fabrizio Giordano <fabriziogiordano77@gmail.com> */ import { Pointing } from "healpixjs"; import { Hploc } from "healpixjs"; import { radToDeg } from '../model/Utils.js'; // TODO change package import { CoordsType } from "../model/CoordsType.js"; import { Point } from "../model/Point.js"; import { NumberType } from '../model/NumberType.js'; export class HiPSHelper { /** * Table 1 - ref paper HEALPix — a Framework for High Resolution Discretization, * and Fast Analysis of Data Distributed on the Sphere * K. M. G´orski1,2, E. Hivon3,4, A. J. Banday5, B. D. Wandelt6,7, F. K. Hansen8, M. * Reinecke5, M. Bartelman9 */ /** * * @param {decimal degrees} pxsize * @returns {int} nside */ static computeHiPSOrder(pxsize, pxXtile) { /** * with same order k (table 1), HIPS angular resolution is higher of order of 512 (2^9) pixels than * the HEALPix. This is because each tile in a HiPS is represented by default by 512x512 pixels.\ * Angular resolution of different HEALPix orders in respect to the order 0, can be calculated this * way: * * L(k) = L(0) / 2^k = 58.6 / 2^k * * Therefore, in the case of HiPS we need to take into account the extra resolution given by the * 512x512 (2^9) tiles. In this case the above becomes: * * L(k) = L(0) / (2^k * 2^9) * * Though, in order to compute the required order starting from the pxsize desired (in input) we * need to perform these steps: * * pxsize = L(k) = L(0) / (2^k * 2^9) * 2^k = L(0) / (pxsize * 2^9) * k * Log2 2 = Log2 L(0) - Log2 (pxsize * 2^9) * k = Log2 L(0) - Log2 (pxsize * 2^9) * */ let k = Math.log2((HiPSHelper.RES_ORDER_0 / pxXtile) / pxsize); // let k = Math.log2(HiPSHelper.RES_ORDER_0 / (pxXtile * pxsize)); k = Math.round(k); // let theta0px = HiPSHelper.RES_ORDER_0; // let k = Math.log2(theta0px) - Math.log2(pxsize * 2**9); // k = Match.round(k); // let nside = 2**k; // return { // "nside" : nside, // "norder" : k // }; return k; } static computeHiPSOrder2(pxsize, pxXtile) { const k = Math.log2(Math.sqrt(Math.PI / 3) / (pxsize * pxXtile)); const order = Math.round(k); console.warn(k); return order; } // based on "HiPS – Hierarchical Progressive Survey" IVOA recomandation (formula on table 5) static computeOrder(pxAngSizeDeg, pxTileWidth) { console.log(`Computing HiPS order having pixel angular size of ${pxAngSizeDeg} in degrees`); const deg2rad = Math.PI / 180; const pxAngSizeRad = pxAngSizeDeg * deg2rad; console.log(`pixel angular res in radians ${pxAngSizeRad}`); const computedOrder = 0.5 * Math.log2(Math.PI / (3 * pxAngSizeRad * pxAngSizeRad * pxTileWidth * pxTileWidth)); console.log(`Order ${computedOrder}`); if (computedOrder < 0) { return 0; } return Math.floor(computedOrder); } // based on "HiPS – Hierarchical Progressive Survey" IVOA recomandation (formula on table 5) static computePxAngularSize(pxTileWidth, order) { const computedPxAngSizeRadiant = Math.sqrt(4 * Math.PI / (12 * Math.pow((pxTileWidth * (Math.pow(2, order))), 2))); console.log(`Computing Pixel size with tile of ${pxTileWidth} pixels and order ${order}`); const rad2deg = 180 / Math.PI; const deg = computedPxAngSizeRadiant * rad2deg; const arcmin = computedPxAngSizeRadiant * rad2deg * 60; const arcsec = computedPxAngSizeRadiant * rad2deg * 3600; console.log("Pixel size in radiant:" + computedPxAngSizeRadiant); console.log("Pixel size in degrees:" + deg); console.log("Pixel size in arcmin:" + arcmin); console.log("Pixel size in arcsec:" + arcsec); return { "rad": computedPxAngSizeRadiant, "deg": deg, "arcmin": arcmin, "arcsec": arcsec }; } /** * Reference: HiPS – Hierarchical Progressive Survey page 11 * pxsize =~ sqrt[4 * PI / (12 * (512 * 2^order)^2)] * @param {*} order */ static computePxSize(order, pxXtile) { // TODO CHECK IT // let pxsize = 1 / (512 * 2 ** order) * Math.sqrt(Math.PI / 3); let pxsize = 1 / (pxXtile * Math.pow(2, order)) * Math.sqrt(Math.PI / 3); return pxsize; } // /** // * // * @param {Object {ra, dec}} point decimal degrees // * @returns {Object {phi_rad, theta_rad}} in radians // */ // static convert2PhiTheta (point: Point) { // let phitheta_rad = {}; // let phiTheta_deg = HiPSHelper.astroDegToSpherical(point.ra, point.dec); // phitheta_rad.phi_rad = HiPSHelper.degToRad(phiTheta_deg.phi); // phitheta_rad.theta_rad = HiPSHelper.degToRad(phiTheta_deg.theta); // return phitheta_rad; // } // static astroDegToSphericalRad(raDeg: number, decDeg: number) { // let phiThetaDeg = HiPSHelper.astroDegToSpherical(raDeg, decDeg); // let phiThetaRad = { // phi_rad: HiPSHelper.degToRad(phiThetaDeg.phi), // theta_rad: HiPSHelper.degToRad(phiThetaDeg.theta) // } // return phiThetaRad; // } // static degToRad(degrees: number): number { // return (degrees / 180 ) * Math.PI ; // } // static radToDeg(rad: number): number { // return (rad / Math.PI ) * 180 ; // } // static astroDegToSpherical(raDeg: number, decDeg: number): Point{ // let phiDeg: number; // let thetaDeg: number; // phiDeg = raDeg; // if (phiDeg < 0){ // phiDeg += 360; // } // thetaDeg = 90 - decDeg; // return { // phi: phiDeg, // theta: thetaDeg // }; // } /** * * @param {Object {phi_rad, theta_rad}} phiTheta_rad Center of the circle in radians * @param {decimal} r Radius of the circle in radians * @returns */ static computeBbox(point, r) { let bbox = []; bbox.push(new Pointing(null, false, point.spherical.thetaRad - r, point.spherical.phiRad - r)); bbox.push(new Pointing(null, false, point.spherical.thetaRad - r, point.spherical.phiRad + r)); bbox.push(new Pointing(null, false, point.spherical.thetaRad + r, point.spherical.phiRad + r)); bbox.push(new Pointing(null, false, point.spherical.thetaRad - r, point.spherical.phiRad - r)); return bbox; } static setupByTile(tileno, hp) { let xyGridProj = { "min_y": NaN, "max_y": NaN, "min_x": NaN, "max_x": NaN, "gridPointsDeg": [] }; let cornersVec3 = hp.getBoundariesWithStep(tileno, 1); let pointings = []; for (let i = 0; i < cornersVec3.length; i++) { pointings[i] = new Pointing(cornersVec3[i]); if (i >= 1) { let a = pointings[i - 1].phi; let b = pointings[i].phi; // case when RA is just crossing the origin (e.g. 357deg - 3deg) if (Math.abs(a - b) > Math.PI) { if (pointings[i - 1].phi < pointings[i].phi) { pointings[i - 1].phi += 2 * Math.PI; } else { pointings[i].phi += 2 * Math.PI; } } } } for (let j = 0; j < pointings.length; j++) { let coThetaRad = pointings[j].theta; // HEALPix works with colatitude (0 North Pole, 180 South Pole) // converting the colatitude in latitude (dec) let decRad = Math.PI / 2 - coThetaRad; let raRad = pointings[j].phi; // projection on healpix grid let p = new Point(CoordsType.ASTRO, NumberType.RADIANS, raRad, decRad); let xyDeg = HiPSHelper.world2intermediate(p.astro); xyGridProj.gridPointsDeg[j * 2] = xyDeg[0]; xyGridProj.gridPointsDeg[j * 2 + 1] = xyDeg[1]; if (isNaN(xyGridProj.max_y) || xyDeg[1] > xyGridProj.max_y) { xyGridProj.max_y = xyDeg[1]; } if (isNaN(xyGridProj.min_y) || xyDeg[1] < xyGridProj.min_y) { xyGridProj.min_y = xyDeg[1]; } if (isNaN(xyGridProj.max_x) || xyDeg[0] > xyGridProj.max_x) { xyGridProj.max_x = xyDeg[0]; } if (isNaN(xyGridProj.min_x) || xyDeg[0] < xyGridProj.min_x) { xyGridProj.min_x = xyDeg[0]; } } return xyGridProj; } static world2intermediate(ac) { let x_grid; let y_grid; if (Math.abs(ac.decRad) <= HiPSHelper.THETAX) { // equatorial belts x_grid = ac.raDeg; y_grid = Hploc.sin(ac.decRad) * HiPSHelper.K * 90 / HiPSHelper.H; } else if (Math.abs(ac.decRad) > HiPSHelper.THETAX) { // polar zones let raDeg = ac.raDeg; let w = 0; // omega if (HiPSHelper.K % 2 !== 0 || ac.decRad > 0) { // K odd or thetax > 0 w = 1; } let sigma = Math.sqrt(HiPSHelper.K * (1 - Math.abs(Hploc.sin(ac.decRad)))); let phi_c = -180 + (2 * Math.floor(((ac.raDeg + 180) * HiPSHelper.H / 360) + ((1 - w) / 2)) + w) * (180 / HiPSHelper.H); x_grid = phi_c + (raDeg - phi_c) * sigma; y_grid = (180 / HiPSHelper.H) * (((HiPSHelper.K + 1) / 2) - sigma); if (ac.decRad < 0) { y_grid *= -1; } } return [x_grid, y_grid]; } // static world2intermediate(sc: SphericalCoords): [number, number] { // let x_grid: number; // let y_grid: number; // if ( Math.abs(sc.thetaRad) <= HiPSHelper.THETAX) { // equatorial belts // x_grid = sc.phiDeg; // y_grid = Hploc.sin(sc.thetaRad) * HiPSHelper.K * 90 / HiPSHelper.H; // } else if ( Math.abs(sc.thetaRad) > HiPSHelper.THETAX) { // polar zones // let phiDeg = sc.phiDeg; // let w = 0; // omega // if (HiPSHelper.K % 2 !== 0 || sc.thetaRad > 0) { // K odd or thetax > 0 // w = 1; // } // let sigma = Math.sqrt( HiPSHelper.K * (1 - Math.abs(Hploc.sin(sc.thetaRad)) ) ); // let phi_c = - 180 + ( 2 * Math.floor( ((sc.phiRad + 180) * HiPSHelper.H/360) + ((1 - w)/2) ) + w ) * ( 180 / HiPSHelper.H ); // x_grid = phi_c + (phiDeg - phi_c) * sigma; // y_grid = (180 / HiPSHelper.H) * ( ((HiPSHelper.K + 1)/2) - sigma); // if (sc.thetaRad < 0) { // y_grid *= -1; // } // } // return [x_grid, y_grid]; // } static intermediate2pix(x, y, xyGridProj, pxXtile) { let xInterval = Math.abs(xyGridProj.max_x - xyGridProj.min_x); let yInterval = Math.abs(xyGridProj.max_y - xyGridProj.min_y); let i_norm; let j_norm; if ((xyGridProj.min_x > 360 || xyGridProj.max_x > 360) && x < xyGridProj.min_x) { i_norm = (x + 360 - xyGridProj.min_x) / xInterval; } else { i_norm = (x - xyGridProj.min_x) / xInterval; } j_norm = (y - xyGridProj.min_y) / yInterval; let i = 0.5 - (i_norm - j_norm); let j = (i_norm + j_norm) - 0.5; // TODO CHECK THE FOLLOWING. BEFORE IT WAS i = Math.floor(i * HiPSHelper.pxXtile); pxXtile; // i = Math.floor(i * HiPSHelper.DEFAULT_Naxis1_2); // j = Math.floor(j * HiPSHelper.DEFAULT_Naxis1_2); // return [i, HiPSHelper.DEFAULT_Naxis1_2 - j - 1]; i = Math.floor(i * pxXtile); j = Math.floor(j * pxXtile); return [i, pxXtile - j - 1]; } static pix2intermediate(i, j, xyGridProj, naxis1, naxis2) { /** * (i_norm,w_pixel) = (0,0) correspond to the lower-left corner of the facet in the image * (i_norm,w_pixel) = (1,1) is the upper right corner * dimamond in figure 1 from "Mapping on the HEalpix grid" paper * (0,0) leftmost corner * (1,0) upper corner * (0,1) lowest corner * (1,1) rightmost corner * Thanks YAGO! :p */ // let cnaxis1 = HiPSHelper.pxXtile; // let cnaxis2 = HiPSHelper.pxXtile; let cnaxis1 = naxis1; let cnaxis2 = naxis2; if (naxis1) { cnaxis1 = naxis1; } if (naxis2) { cnaxis2 = naxis2; } let i_norm = (i + 0.5) / cnaxis1; let j_norm = (j + 0.5) / cnaxis2; let xInterval = Math.abs(xyGridProj.max_x - xyGridProj.min_x) / 2.0; let yInterval = Math.abs(xyGridProj.max_y - xyGridProj.min_y) / 2.0; let yMean = (xyGridProj.max_y + xyGridProj.min_y) / 2.0; // bi-linear interpolation let x = xyGridProj.max_x - xInterval * (i_norm + j_norm); let y = yMean - yInterval * (j_norm - i_norm); return [x, y]; } static intermediate2world(x, y) { let phiDeg; let thetaDeg; let Yx = 90 * (HiPSHelper.K - 1) / HiPSHelper.H; if (Math.abs(y) <= Yx) { // equatorial belts phiDeg = x; thetaDeg = radToDeg(Math.asin((y * HiPSHelper.H) / (90 * HiPSHelper.K))); } else if (Math.abs(y) > Yx) { // polar regions let sigma = (HiPSHelper.K + 1) / 2 - Math.abs(y * HiPSHelper.H) / 180; let thetaRad = Hploc.asin(1 - (sigma * sigma) / HiPSHelper.K); let w = 0; // omega if (HiPSHelper.K % 2 !== 0 || thetaRad > 0) { // K odd or thetax > 0 w = 1; } let x_c = -180 + (2 * Math.floor((x + 180) * HiPSHelper.H / 360 + (1 - w) / 2) + w) * (180 / HiPSHelper.H); phiDeg = x_c + (x - x_c) / sigma; thetaDeg = radToDeg(thetaRad); if (y <= 0) { thetaDeg *= -1; } } // return [phiDeg, thetaDeg]; // TODO CHECK THIS! // let p = new Point(CoordsType.SPHERICAL, NumberType.DEGREES, phiDeg, thetaDeg); let p = new Point(CoordsType.ASTRO, NumberType.DEGREES, phiDeg, thetaDeg); return p; } } // static pxXtile: number = 512; // TODO in some cases it is different HiPSHelper.DEFAULT_Naxis1_2 = 512; // static RES_ORDER_0: number = 58.6 / HiPSHelper.pxXtile; HiPSHelper.RES_ORDER_0 = 58.6; HiPSHelper.H = 4; HiPSHelper.K = 3; HiPSHelper.THETAX = Hploc.asin((HiPSHelper.K - 1) / HiPSHelper.K); //# sourceMappingURL=HiPSHelper.js.map