/* * License information at https://github.com/Caltech-IPAC/firefly/blob/master/License.txt */ import {get} from 'lodash'; import {clone} from '../../util/WebUtil.js'; import {makeImagePt, makeProjectionPt, makeWorldPt} from '../Point.js'; import {computeDistance} from '../VisUtil.js'; import {CoordinateSys} from '../CoordSys.js'; import {AitoffProjection} from './AitoffProjection.js'; import {NCPProjection} from './NCPProjection.js'; import {ARCProjection} from './ARCProjection.js'; import {GnomonicProjection} from './GnomonicProjection.js'; import {SansonFlamsteedProjection } from './SansonFlamsteedProjection.js'; import {LinearProjection} from './LinearProjection.js'; import {CartesianProjection} from './CartesianProjection.js'; import {OrthographicProjection} from './OrthographicProjection.js'; import {CylindricalProjection} from './CylindricalProjection.js'; import {PlateProjection} from './PlateProjection.js'; import {TpvProjection} from './TpvProjection.js'; import {Projection as AladinProjection} from '../../externalSource/aladinProj/AladinProjections.js'; const unspecifiedProject= () => null; const unimplementedProject= () => null; export const GNOMONIC = 1001; // TESTED export const ORTHOGRAPHIC = 1002; // TESTED export const NCP = 1003; export const AITOFF = 1004; // TESTED export const CAR = 1005; export const LINEAR = 1006; export const PLATE = 1007; // TESTED export const ARC = 1008; export const SFL = 1009; // TESTED export const CEA = 1010; export const TPV = 1011; export const UNSPECIFIED = 1998; // TESTED export const UNRECOGNIZED = 1999; // TESTED export const ALADIN_SIN = 5; const projTypes= { [GNOMONIC] : { name: 'GNOMONIC', fwdProject: GnomonicProjection.fwdProject, revProject: GnomonicProjection.revProject, implemented : true, wrapping : false }, [TPV] : { name: 'TPV', fwdProject: TpvProjection.fwdProject, revProject: TpvProjection.revProject, implemented : true, wrapping : false }, [ORTHOGRAPHIC] : { name: 'ORTHOGRAPHIC', fwdProject: OrthographicProjection.fwdProject, revProject: OrthographicProjection.revProject, implemented : true, wrapping : false }, [NCP] : { name: 'NCP', fwdProject: NCPProjection.fwdProject, revProject: NCPProjection.revProject, implemented : true, wrapping : false }, [ARC] : { name: 'ARC', fwdProject: ARCProjection.fwdProject, revProject: ARCProjection.revProject, implemented : true, wrapping : false }, [AITOFF] : { name: 'AITOFF', fwdProject: AitoffProjection.fwdProject, revProject: AitoffProjection.revProject, implemented : true, wrapping : true }, [CAR] : { name: 'CAR', fwdProject: CartesianProjection.fwdProject, revProject: CartesianProjection.revProject, implemented : true, wrapping : false }, [CEA] : { name: 'CEA', fwdProject: CylindricalProjection.fwdProject, revProject: CylindricalProjection.revProject, implemented : true, wrapping : false }, [SFL] : { name: 'SFL', fwdProject: SansonFlamsteedProjection.fwdProject, revProject: SansonFlamsteedProjection.revProject, implemented : true, wrapping : true }, [LINEAR] : { name: 'LINEAR', fwdProject: LinearProjection.fwdProject, revProject: LinearProjection.revProject, implemented : true, wrapping : false }, [PLATE] : { name: 'PLATE', fwdProject: PlateProjection.fwdProject, revProject: PlateProjection.revProject, implemented : true, wrapping : false }, [UNSPECIFIED] : { name: 'UNSPECIFIED', fwdProject: unspecifiedProject, revProject: unspecifiedProject, implemented : false, wrapping : false }, [UNRECOGNIZED] : { name: 'UNRECOGNIZED', fwdProject: unimplementedProject, revProject: unimplementedProject, implemented : false, wrapping : false }, [ALADIN_SIN] : { name: 'ALADIN_SIN', fwdProject : fwdAladinSinProject, revProject : revAladinSinProject, implemented : true, wrapping : false, }, }; const translateProjectionName= (maptype) => get(projTypes, [maptype,'name'],'UNRECOGNIZED'); const isImplemented= (header) => get(projTypes, [header.maptype, 'implemented'],false); const isWrappingProjection= (header) => get(projTypes, [header.maptype, 'wrapping'],false); /** * Convert 'ProjectionPt' coordinates to World coordinates * 'ProjectionPt' coordinates have 0,0 in center of lower left pixel * (same as 'Skyview Screen' coordinates) * @param x double precision in 'ProjectionPt' coordinates * @param y double precision in 'ProjectionPt' coordinates * @param header the header info * @param coordSys used for the worldPt */ function getWorldCoordsInternal(x, y, header, coordSys) { if (!projTypes[header.maptype]) return null; const pt = projTypes[header.maptype].fwdProject( x, y, header); return pt ? makeWorldPt(pt.x, pt.y, coordSys) : null; } /** * Convert World coordinates to 'Skyview Screen' coordinates * 'Skyview Screen' coordinates have 0,0 in center of lower left pixel * @param ra double precision * @param dec double precision * @param header the header info * @return ImagePt with X,Y in 'Skyview Screen' coordinates */ function getImageCoordsInternal(ra, dec, header) { if (!projTypes[header.maptype]) return null; const image_pt = projTypes[header.maptype].revProject( ra, dec, header); return image_pt; } export class Projection { /** * @summary Contains data about the state of a plot. * This object is never created directly if is always instantiated from the json sent from the server. * @param {Object} header data from the fits file * @param {CoordinateSys} coordSys * Note - constructor should never be call directly * * * @prop {object} header * @prop {number} scale1 * @prop {number} scale2 * @prop {number} pixelScaleArcSec * @prop {CoordinateSys} coordSys * @public */ constructor(header, coordSys) { this.header= clone(header); this.coordSys= coordSys; const {crpix1,crpix2, cdelt1}= header; if (!cdelt1 && crpix1 && crpix2) { const projCenter = this.getWorldCoords(crpix1 - 1, crpix2 - 1); const oneToRight = this.getWorldCoords(crpix1, crpix2 - 1); const oneUp = this.getWorldCoords(crpix1 - 1, crpix2); if (oneToRight && oneUp) { this.header.cdelt1 = - computeDistance( projCenter, oneToRight); this.header.cdelt2 = computeDistance( projCenter, oneUp); } } this.pixelScaleDeg = Math.abs(this.header.cdelt1); this.pixelScaleArcSec = this.pixelScaleDeg * 3600.0; } /** * * @return {number} * @public */ getPixelScaleDegree() { return this.pixelScaleDeg; } /** * @summary the scale of an image pixel in arcsec * @return {number} * @public */ getPixelScaleArcSec() { return this.pixelScaleArcSec; } /** * @summary convert from a world point to a image point * @param ra * @param dec * @return {ImagePt} * @public */ getImageCoords(ra, dec) { return getImageCoordsInternal(ra, dec, this.header, false); } /** * @summary convert from a image point to a world point * @param x * @param y * @return {WorldPt} * @public */ getWorldCoords( x, y) { return getWorldCoordsInternal(x, y, this.header, this.coordSys, false); } /** * @return {boolean} true, if this projection is implemented * @public */ isImplemented() { return isImplemented(this.header); } /** * @return {boolean} true, if this projection is specified * @public */ isSpecified() { return this.header.maptype!==UNSPECIFIED; } /** * @return {boolean} true, if this projection is wrapping, e.g. AITOFF * @public */ isWrappingProjection() { return isWrappingProjection(this.header); } /** * @return true, if this projection is wrapping, e.g. AITOFF * @public */ getProjectionName() { return translateProjectionName(this.header.maptype); } } export function makeProjection(projJSON) { return new Projection(projJSON.header, CoordinateSys.parse(projJSON.coorindateSys)); } export function makeProjectionNew(header, csys) { return new Projection(header, csys); } function fwdAladinSinProject(x, y, header) { const widthHalf = header.crpix1; const heightHalf = header.crpix2; const yshift = 20; const height = header.crpix2 * 2; const yFlip = height - y; let pX = (x - widthHalf) / widthHalf; if (pX === -1) pX = -.99; else if (pX === 1) pX = .99; const pY = (yFlip - heightHalf) / (heightHalf + yshift); const p = new AladinProjection(header.crval1, header.crval2); p.setProjection(AladinProjection.PROJ_SIN); try { const pt = p.unproject(pX, pY); if (!pt) return null; const retPt = makeProjectionPt(pt.ra, pt.dec); return retPt; } catch (e) { return null; } } function revAladinSinProject(ra, dec, header) { const widthHalf= header.crpix1; const heightHalf= header.crpix2; const width= header.crpix1*2; const height= header.crpix2*2; const yshift= 20; const p= new AladinProjection(header.crval1, header.crval2); p.setProjection(AladinProjection.PROJ_SIN); const pt= p.project(ra,dec); if (!pt) return null; const x= pt.X; const y= pt.Y; const imX= x*widthHalf +widthHalf; const imY= y*(heightHalf+yshift) + heightHalf; return makeImagePt( imX, height - imY); }