wcslight
Version:
WCS Javascript library.
292 lines • 15.1 kB
JavaScript
/**
* Summary. (bla bla bla)
*
* Description. (bla bla bla)
*
* @link github https://github.com/fab77/wcslight
* @author Fabrizio Giordano <fabriziogiordano77@gmail.com>
*/
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
import { FITSParser, FITSHeader, FITSHeaderItem, ParseUtils } from 'jsfitsio';
import { AbstractProjection } from './AbstractProjection.js';
import { ImagePixel } from '../model/ImagePixel.js';
import { Point } from '../model/Point.js';
import { CoordsType } from '../model/CoordsType.js';
import { NumberType } from '../model/NumberType.js';
import { exit } from 'process';
export class MercatorProjection extends AbstractProjection {
constructor() {
super("'RA---CAR'", "'DEC--CAR'");
this._wcsname = "MER"; // TODO check WCS standard and create ENUM
this._pxvalues = new Map();
this._fitsheader = new Array();
}
initFromFile(infile) {
const _super = Object.create(null, {
naxis1: { get: () => super.naxis1, set: v => super.naxis1 = v },
naxis2: { get: () => super.naxis2, set: v => super.naxis2 = v },
pxsize: { get: () => super.pxsize, set: v => super.pxsize = v }
});
return __awaiter(this, void 0, void 0, function* () {
let fp = new FITSParser(infile);
this._infile = infile;
this._fitsUsed.push(infile);
let promise = fp.loadFITS().then(fits => {
// console.log(fits.header);
this._pxvalues.set(0, fits.data);
this._fitsheader[0] = fits.header;
_super.naxis1 = fits.header.get("NAXIS1");
_super.naxis2 = fits.header.get("NAXIS2");
// this._naxis1 = fits.header.get("NAXIS1");
// this._naxis2 = fits.header.get("NAXIS2");
this._craDeg = fits.header.getItemListOf("CRVAL1")[0].value;
this._cdecDeg = fits.header.getItemListOf("CRVAL2")[0].value;
// TODO CDELT could not be present. In this is the case,
// there should be CDi_ja, but I am not handling them atm
// [Ref. Representation of celestial coordinates in FITS - equation (1)]
// this._pxsize1 = this._fitsheader[0].getItemListOf("CDELT1")[0].value as number;
// this._pxsize2 = this._fitsheader[0].getItemListOf("CDELT2")[0].value as number;
const pxsize1 = this._fitsheader[0].getItemListOf("CDELT1")[0].value;
const pxsize2 = this._fitsheader[0].getItemListOf("CDELT2")[0].value;
if (pxsize1 !== pxsize2 || pxsize1 === undefined || pxsize2 === undefined) {
throw new Error("pxsize1 is not equal to pxsize2");
exit;
}
_super.pxsize = pxsize1;
// this._minra = this._craDeg - this._pxsize1 * this._naxis1 / 2;
this._minra = this._craDeg - _super.pxsize * _super.naxis1 / 2;
if (this._minra < 0) {
this._minra += 360;
}
// this._mindec = this._cdecDeg - this._pxsize2 * this._naxis2 / 2;
this._mindec = this._cdecDeg - _super.pxsize * _super.naxis2 / 2;
return fits;
});
yield promise;
return promise;
});
}
extractPhysicalValues(fits) {
let bzero = fits.header.get("BZERO");
let bscale = fits.header.get("BSCALE");
let naxis1 = fits.header.get("NAXIS1");
let naxis2 = fits.header.get("NAXIS2");
let bitpix = fits.header.get("BITPIX");
let bytesXelem = Math.abs(bitpix / 8);
let blankBytes = ParseUtils.convertBlankToBytes(fits.header.get("BLANK"), bytesXelem); // TODO => ??????? Im not using it. it should be used!
// let physicalvalues = new Array[naxis2][naxis1];
let physicalvalues = new Array(naxis2);
for (let n2 = 0; n2 < naxis2; n2++) {
physicalvalues[n2] = new Array(naxis1);
for (let n1 = 0; n1 < naxis1; n1++) {
let pixval = ParseUtils.extractPixelValue(0, fits.data[n2].slice(n1 * bytesXelem, (n1 + 1) * bytesXelem), bitpix);
let physicalVal = bzero + bscale * pixval;
physicalvalues[n2][n1] = physicalVal;
}
}
return physicalvalues;
}
prepareFITSHeader(fitsHeaderParams) {
this._fitsheader[0] = new FITSHeader();
this._fitsheader[0].addItemAtTheBeginning(new FITSHeaderItem("NAXIS1", super.naxis1));
this._fitsheader[0].addItemAtTheBeginning(new FITSHeaderItem("NAXIS2", super.naxis2));
this._fitsheader[0].addItemAtTheBeginning(new FITSHeaderItem("NAXIS", 2));
this._fitsheader[0].addItemAtTheBeginning(new FITSHeaderItem("BITPIX", fitsHeaderParams.get("BITPIX")));
this._fitsheader[0].addItemAtTheBeginning(new FITSHeaderItem("SIMPLE", fitsHeaderParams.get("SIMPLE")));
if (fitsHeaderParams.get("BLANK") !== undefined) {
this._fitsheader[0].addItem(new FITSHeaderItem("BLANK", fitsHeaderParams.get("BLANK")));
}
let bscale = 1.0;
if (fitsHeaderParams.get("BSCALE") !== undefined) {
bscale = fitsHeaderParams.get("BSCALE");
}
this._fitsheader[0].addItem(new FITSHeaderItem("BSCALE", bscale));
let bzero = 0.0;
if (fitsHeaderParams.get("BZERO") !== undefined) {
bzero = fitsHeaderParams.get("BZERO");
}
this._fitsheader[0].addItem(new FITSHeaderItem("BZERO", bzero));
this._fitsheader[0].addItem(new FITSHeaderItem("CTYPE1", super.ctype1));
this._fitsheader[0].addItem(new FITSHeaderItem("CTYPE2", super.ctype2));
this._fitsheader[0].addItem(new FITSHeaderItem("CDELT1", super.pxsize)); // ??? Pixel spacing along axis 1 ???
this._fitsheader[0].addItem(new FITSHeaderItem("CDELT2", super.pxsize)); // ??? Pixel spacing along axis 2 ???
this._fitsheader[0].addItem(new FITSHeaderItem("CRPIX1", super.naxis1 / 2)); // central/reference pixel i along naxis1
this._fitsheader[0].addItem(new FITSHeaderItem("CRPIX2", super.naxis2 / 2)); // central/reference pixel j along naxis2
this._fitsheader[0].addItem(new FITSHeaderItem("CRVAL1", this._craDeg)); // central/reference pixel RA
this._fitsheader[0].addItem(new FITSHeaderItem("CRVAL2", this._cdecDeg)); // central/reference pixel Dec
let min = bzero + bscale * this._minphysicalval;
let max = bzero + bscale * this._maxphysicalval;
this._fitsheader[0].addItem(new FITSHeaderItem("DATAMIN", min)); // min data value
this._fitsheader[0].addItem(new FITSHeaderItem("DATAMAX", max)); // max data value
this._fitsheader[0].addItem(new FITSHeaderItem("ORIGIN", "'WCSLight v.0.x'"));
this._fitsheader[0].addItem(new FITSHeaderItem("COMMENT", "'WCSLight v0.x developed by F.Giordano and Y.Ascasibar'"));
this._fitsheader[0].addItem(new FITSHeaderItem("END"));
return this._fitsheader;
}
getFITSHeader() {
return this._fitsheader;
}
getCommonFitsHeaderParams() {
let header = new FITSHeader();
for (const [key, value] of this._fitsheader[0]) {
// I could add a list of used NPIXs to be included in the comment of the output FITS
if (["SIMPLE", "BITPIX", "BSCALE", "BZERO", "BLANK", "ORDER",].includes(key)) {
header.addItem(new FITSHeaderItem(key, value));
}
}
return header;
}
get fitsUsed() {
return this._fitsUsed;
}
getPixValues(inputPixelsList) {
const _super = Object.create(null, {
naxis2: { get: () => super.naxis2 },
naxis1: { get: () => super.naxis1 }
});
return __awaiter(this, void 0, void 0, function* () {
let promise = new Promise((resolve, reject) => {
try {
let bytesXelem = Math.abs(this._fitsheader[0].get("BITPIX") / 8);
let blankBytes = ParseUtils.convertBlankToBytes(this._fitsheader[0].get("BLANK"), bytesXelem);
let pixcount = inputPixelsList.length;
let values = new Uint8Array(pixcount * bytesXelem);
for (let p = 0; p < pixcount; p++) {
let imgpx = inputPixelsList[p];
// TODO check when input is undefined. atm it puts 0 bur it should be BLANK
// TODO why I am getting negative i and j? check world2pix!!!
if ((imgpx._j) < 0 || (imgpx._j) >= _super.naxis2 ||
(imgpx._i) < 0 || (imgpx._i) >= _super.naxis1) {
for (let b = 0; b < bytesXelem; b++) {
values[p * bytesXelem + b] = blankBytes[b];
}
}
else {
let pv = this._pxvalues.get(0);
if (pv !== undefined) {
for (let b = 0; b < bytesXelem; b++) {
values[p * bytesXelem + b] = pv[imgpx._j][(imgpx._i) * bytesXelem + b];
}
}
}
}
resolve(values);
}
catch (err) {
reject("[MercatorProjection] ERROR: " + err);
}
});
return promise;
});
}
setPxsValue(values, fitsHeaderParams) {
let bytesXelem = Math.abs(fitsHeaderParams.get("BITPIX") / 8);
let minpixb = ParseUtils.extractPixelValue(0, values.slice(0, bytesXelem), fitsHeaderParams.get("BITPIX"));
let maxpixb = minpixb;
let bscale = (fitsHeaderParams.get("BSCALE") !== undefined) ? fitsHeaderParams.get("BSCALE") : 1.0;
let bzero = (fitsHeaderParams.get("BZERO") !== undefined) ? fitsHeaderParams.get("BZERO") : 0.0;
this._minphysicalval = bzero + bscale * minpixb;
this._maxphysicalval = bzero + bscale * maxpixb;
// this._pxvalues = new Array(this._naxis2);
// for (let r = 0; r < this._naxis2; r++) {
// this._pxvalues[r] = new Uint8Array(this._naxis1 * bytesXelem);
// }
// this._pxvalues.set(0, new Uint8Array[this._naxis2][this._naxis1 * bytesXelem]);
this._pxvalues.set(0, new Array(super.naxis2));
let pv = this._pxvalues.get(0);
if (pv !== undefined) {
for (let r = 0; r < super.naxis2; r++) {
pv[r] = new Uint8Array(super.naxis1 * bytesXelem);
}
let r;
let c;
let b;
for (let p = 0; (p * bytesXelem) < values.length; p++) {
// console.log("processing "+p + " of "+ (values.length / bytesXelem));
try {
r = Math.floor(p / super.naxis1);
c = (p - r * super.naxis1) * bytesXelem;
for (b = 0; b < bytesXelem; b++) {
pv[r][c + b] = values[p * bytesXelem + b];
}
let valpixb = ParseUtils.extractPixelValue(0, values.slice(p * bytesXelem, (p * bytesXelem) + bytesXelem), fitsHeaderParams.get("BITPIX"));
let valphysical = bzero + bscale * valpixb;
if (valphysical < this._minphysicalval || isNaN(this._minphysicalval)) {
this._minphysicalval = valphysical;
}
else if (valphysical > this._maxphysicalval || isNaN(this._maxphysicalval)) {
this._maxphysicalval = valphysical;
}
}
catch (err) {
console.log(err);
console.log("p " + p);
console.log("r %, c %, b %" + r, c, b);
console.log("this._pxvalues[r][c + b] " + pv[r][c + b]);
console.log("values[p * bytesXelem + b] " + values[p * bytesXelem + b]);
}
}
}
this.prepareFITSHeader(fitsHeaderParams);
return this._pxvalues;
}
getImageRADecList(center, radius, pxsize) {
super.computeSquaredNaxes(2 * radius, pxsize); // compute naxis[1, 2]
super.pxsize = pxsize;
this._minra = center.astro.raDeg - radius;
if (this._minra < 0) {
this._minra += 360;
}
this._mindec = center.astro.decDeg - radius;
let radeclist = new Array();
for (let d = 0; d < super.naxis2; d++) {
for (let r = 0; r < super.naxis1; r++) {
radeclist.push([this._minra + (r * super.pxsize), this._mindec + (d * super.pxsize)]);
}
}
let cidx = (super.naxis2 / 2) * super.naxis1 + super.naxis1 / 2;
if (super.naxis1 % 2 != 0) {
cidx = Math.floor(radeclist.length / 2);
}
// let cidx2 = (this._naxis2 / 2 - 1) * this._naxis1 + this._naxis1 / 2;
// let cidx = Math.ceil(radeclist.length / 2);
// let cidx = Math.floor(radeclist.length / 2);
this._craDeg = radeclist[cidx][0];
this._cdecDeg = radeclist[cidx][1];
return radeclist;
}
/** TODO !!! check and handle RA passing through 360-0 */
pix2world(i, j) {
let ra;
let dec;
// ra = i * this._stepra + this._minra;
// dec = j * this._stepdec + this._mindec;
ra = i * super.pxsize + this._minra;
dec = j * super.pxsize + this._mindec;
let p = new Point(CoordsType.ASTRO, NumberType.DEGREES, ra, dec);
return p;
// return [ra, dec];
}
world2pix(radeclist) {
let imgpxlist = [];
for (let radecItem of radeclist) {
let ra = radecItem[0];
let dec = radecItem[1];
// let i = Math.floor((ra - this._minra) / this._pxsize1);
// let j = Math.floor((dec - this._mindec) / this._pxsize2);
let i = Math.floor((ra - this._minra) / super.pxsize);
let j = Math.floor((dec - this._mindec) / super.pxsize);
imgpxlist.push(new ImagePixel(i, j));
}
return imgpxlist;
}
}
//# sourceMappingURL=MercatorProjection.js.map