diff options
Diffstat (limited to 'fitsy/hpx.C')
-rw-r--r-- | fitsy/hpx.C | 589 |
1 files changed, 589 insertions, 0 deletions
diff --git a/fitsy/hpx.C b/fitsy/hpx.C new file mode 100644 index 0000000..cbd3548 --- /dev/null +++ b/fitsy/hpx.C @@ -0,0 +1,589 @@ +// Copyright (C) 1999-2018 +// Smithsonian Astrophysical Observatory, Cambridge, MA, USA +// For conditions of distribution and use, see copyright notice in "copyright" + +// This source has been modified from the original authored by +// Dr. Mark Calabretta as distributed with WCSLIBS under GNU GPL version 3 +// WCSLIB 4.7 - an implementation of the FITS WCS standard. +// Copyright (C) 1995-2011, Mark Calabretta + +#include <string.h> +#include <ctype.h> + +#include <iostream> +#include <sstream> +#include <iomanip> +using namespace std; + +#include "hpx.h" +#include "util.h" + +#include "fitsy.h" + +FitsHPX::FitsHPX(FitsFile* fits, Order oo, CoordSys ss, Layout ll, + int cc, int qq) + : order_(oo), coord_(ss), layout_(ll), quad_(qq) +{ + FitsHead* head = fits->head(); + FitsTableHDU* hdu = (FitsTableHDU*)(head->hdu()); + col_ = (FitsBinColumn*)hdu->find(cc); + if (!col_) + return; + + int nrow = hdu->rows(); + int nelem = col_->repeat(); + + nside_ = head->getInteger("NSIDE",0); + long firstpix = head->getInteger("FIRSTPIX",-1); + long lastpix = head->getInteger("LASTPIX",-1); + + if (!nside_) { + // Deduce NSIDE + if (lastpix >= 0) { + // If LASTPIX is present without NSIDE we can only assume it's npix. + nside_ = (int)(sqrt((double)((lastpix+1) / 12)) + 0.5); + } + else if (nrow) + nside_ = (int)(sqrt((double)((nrow * nelem) / 12)) + 0.5); + } + + long npix = 12*nside_*nside_; + + if (firstpix < 0) + firstpix = 0; + if (lastpix < 0) + lastpix = npix - 1; + + build(fits); + + if (byteswap_) + swap(); + + valid_ = 1; +} + +FitsHPX::~FitsHPX() +{ + if (data_) + delete [] (float*)data_; +} + +void FitsHPX::build(FitsFile* fits) +{ + // Number of facets on a side of each layout + const int NFACET[] = {5, 4, 4}; + + // Arrays that define the facet location and rotation for each recognised + // layout. Bear in mind that these appear to be upside-down, i.e. the top + // line contains facet numbers for the bottom row of the output image. + // Facets numbered -1 are blank. + + // Equatorial (diagonal) facet layout. + const int FACETS[][5][5] = {{{ 6, 9, -1, -1, -1}, + { 1, 5, 8, -1, -1}, + {-1, 0, 4, 11, -1}, + {-1, -1, 3, 7, 10}, + {-1, -1, -1, 2, 6}}, + // North polar (X) facet layout. + {{ 8, 4, 4, 11, -1}, + { 5, 0, 3, 7, -1}, + { 5, 1, 2, 7, -1}, + { 9, 6, 6, 10, -1}, + {-1, -1, -1, -1, -1}}, + // South polar (X) facet layout. + {{ 1, 6, 6, 2, -1}, + { 5, 9, 10, 7, -1}, + { 5, 8, 11, 7, -1}, + { 0, 4, 4, 3, -1}, + {-1, -1, -1, -1, -1}}}; + + // All facets of the equatorial layout are rotated by +45 degrees with + // respect to the normal orientation, i.e. that with the equator running + // horizontally. The rotation recorded for the polar facets is the number + // of additional positive (anti-clockwise) 90 degree turns with respect to + // the equatorial layout. + + // Equatorial (diagonal), no facet rotation. + const int FROTAT[][5][5] = {{{ 0, 0, 0, 0, 0}, + { 0, 0, 0, 0, 0}, + { 0, 0, 0, 0, 0}, + { 0, 0, 0, 0, 0}, + { 0, 0, 0, 0, 0}}, + // North polar (X) facet rotation. + {{ 3, 3, 0, 0, 0}, + { 3, 3, 0, 0, 0}, + { 2, 2, 1, 1, 0}, + { 2, 2, 1, 1, 0}, + { 0, 0, 0, 0, 0}}, + // South polar (X) facet rotation. + {{ 1, 1, 2, 2, 0}, + { 1, 1, 2, 2, 0}, + { 0, 0, 3, 3, 0}, + { 0, 0, 3, 3, 0}, + { 0, 0, 0, 0, 0}}}; + + // Facet halving codes. 0: the facet is whole (or wholly blank), + // 1: blanked bottom-right, 2: top-right, 3: top-left, 4: bottom-left. + // Positive values mean that the diagonal is included, otherwise not. + + // Equatorial (diagonal), no facet halving. + const int FHALVE[][5][5] = {{{ 0, 0, 0, 0, 0}, + { 0, 0, 0, 0, 0}, + { 0, 0, 0, 0, 0}, + { 0, 0, 0, 0, 0}, + { 0, 0, 0, 0, 0}}, + // North polar (X) facet halving. + {{ 0, 1, -4, 0, 0}, + {-3, 0, 0, 2, 0}, + { 4, 0, 0, -1, 0}, + { 0, -2, 3, 0, 0}, + { 0, 0, 0, 0, 0}}, + // South polar (X) facet halving. + {{ 0, 1, -4, 0, 0}, + {-3, 0, 0, 2, 0}, + { 4, 0, 0, -1, 0}, + { 0, -2, 3, 0, 0}, + { 0, 0, 0, 0, 0}}}; + + FitsHead* head = fits->head(); + FitsTableHDU* hdu = (FitsTableHDU*)(head->hdu()); + int rowlen = hdu->width(); + int nrow = hdu->rows(); + int repeat = col_->repeat(); + char* data = (char*)fits->data(); + + int nside = nside_; + int layout = layout_; + int nfacet = NFACET[layout]; + + pWidth_ = nfacet*nside; + pHeight_ = pWidth_; + + // create image space + size_t pSize = (size_t)pWidth_*pHeight_; + float* dest = new float[pSize]; + for (longlong ii=0; ii<pSize; ii++) + dest[ii] = NAN; + + // Write WCS keyrecords + initHeader(fits); + + // Allocate arrays + long healidx[nside]; + float row[nside]; + + // Loop vertically facet-by-facet. + longlong fpixel = 1; + // longlong group = 0; + longlong nelem = (longlong)nside; + for (int jfacet = 0; jfacet<nfacet; jfacet++) { + // Loop row-by-row. + for (int jj = 0; jj<nside; jj++) { + // Loop horizontally facet-by-facet + for (int ifacet = 0; ifacet<nfacet; ifacet++) { + int facet = FACETS[layout][jfacet][ifacet]; + int rotn = FROTAT[layout][jfacet][ifacet]; + int halve = FHALVE[layout][jfacet][ifacet]; + + // Recentre longitude? + if (quad_ && facet >= 0) { + if (facet <= 3) { + facet += quad_; + if (facet > 3) facet -= 4; + } + else if (facet <= 7) { + facet += quad_; + if (facet > 7) facet -= 4; + } + else { + facet += quad_; + if (facet > 11) facet -= 4; + } + } + + // Write out the data + if (facet < 0) + ; + else { + switch (order_) { + case NESTED: + NESTidx(nside, facet, rotn, jj, healidx); + break; + case RING: + RINGidx(nside, facet, rotn, jj, healidx); + break; + } + + // Gather data into the output vector. + /* + long* healp = healidx; + for (float* rowp = row; rowp < row+nside; rowp++) + *rowp = col_->value(data+*(healp++),0); + */ + for (int ii=0; ii<nside_; ii++) { + int aa = healidx[ii]/repeat; + int bb = healidx[ii] - (aa*repeat); + if (aa<nrow) + row[ii] = col_->value(data+aa*rowlen,bb); + else + row[ii] = 0; + } + + // Apply blanking to halved facets. + if (halve) { + int i1; + int i2; + if (abs(halve) == 1) { + // Blank bottom-right. + i1 = jj; + i2 = nside; + if (halve > 0) + i1++; + } else if (abs(halve) == 2) { + // Blank top-right. + i1 = nside - jj; + i2 = nside; + if (halve < 0) + i1--; + } else if (abs(halve) == 3) { + // Blank top-left. + i1 = 0; + i2 = jj; + if (halve < 0) + i2++; + } else { + // Blank bottom-left. + i1 = 0; + i2 = nside - jj; + if (halve > 0) + i2--; + } + + for (float* rowp = row+i1; rowp < row+i2; rowp++) + *rowp = NAN; + } + + // Write out this facet's contribution to this row of the map. + memcpy(dest+fpixel-1, row, nside*sizeof(float)); + } + + fpixel += nelem; + } + } + } + + data_ = dest; + + dataSize_ = pSize; + dataSkip_ = 0; +} + +// (imap,jmap) are 0-relative pixel coordinates in the output map with origin +// at the bottom-left corner of the specified facet which is rotated by +// (45 + rotn * 90) degrees from its natural orientation; imap increases to +// the right and jmap upwards. + +void FitsHPX::NESTidx(int nside, int facet, int rotn, int jmap, long *healidx) +{ + // Nested index (0-relative) of the first pixel in this facet. + int hh = facet*nside*nside; + + int nside1 = nside - 1; + long* hp = healidx; + for (int imap = 0; imap < nside; imap++, hp++) { + // (ii,jj) are 0-relative pixel coordinates with origin in the southern + // corner of the facet; i increases to the north-east and j to the + // north-west. + int ii =0; + int jj =0; + if (rotn == 0) { + ii = nside1 - imap; + jj = jmap; + } + else if (rotn == 1) { + ii = nside1 - jmap; + jj = nside1 - imap; + } + else if (rotn == 2) { + ii = imap; + jj = nside1 - jmap; + } + else if (rotn == 3) { + ii = jmap; + jj = imap; + } + + *hp = 0; + int bit = 1; + while (ii || jj) { + if (ii & 1) *hp |= bit; + bit <<= 1; + if (jj & 1) *hp |= bit; + bit <<= 1; + ii >>= 1; + jj >>= 1; + } + + *hp += hh; + } +} + +// (imap,jmap) pixel coordinates are as described above for NESTidx(). This +// function computes the double-pixelisation index then converts it to the +// regular ring index. + +void FitsHPX::RINGidx(int nside, int facet, int rotn, int jmap, long *healidx) +{ + const int I0[] = { 1, 3, -3, -1, 0, 2, 4, -2, 1, 3, -3, -1}; + const int J0[] = { 1, 1, 1, 1, 0, 0, 0, 0, -1, -1, -1, -1}; + + int n2side = 2 * nside; + int n8side = 8 * nside; + + // Double-pixelisation index of the last pixel in the north polar cap. */ + int npole = (n2side - 1) * (n2side - 1) - 1; + + // Double-pixelisation pixel coordinates of the centre of the facet. */ + int i0 = nside * I0[facet]; + int j0 = nside * J0[facet]; + + int nside1 = nside - 1; + long* hp = healidx; + for (int imap = 0; imap < nside; imap++, hp++) { + // (ii,jj) are 0-relative, double-pixelisation pixel coordinates. The + // origin is at the intersection of the equator and prime meridian, + // i increases to the east (N.B.) and j to the north. + int ii =0; + int jj =0; + if (rotn == 0) { + ii = i0 + nside1 - (jmap + imap); + jj = j0 + jmap - imap; + } + else if (rotn == 1) { + ii = i0 + imap - jmap; + jj = j0 + nside1 - (imap + jmap); + } + else if (rotn == 2) { + ii = i0 + (imap + jmap) - nside1; + jj = j0 + imap - jmap; + } + else if (rotn == 3) { + ii = i0 + jmap - imap; + jj = j0 + jmap + imap - nside1; + } + + // Convert i for counting pixels + if (ii < 0) + ii += n8side; + ii++; + + if (jj > nside) { + // North polar regime. + if (jj == n2side) + *hp = 0; + else { + // Number of pixels in a polar facet with this value of jj. + int npj = 2 * (n2side - jj); + + // Index of the last pixel in the row above this. + *hp = (npj - 1) * (npj - 1) - 1; + + // Number of pixels in this row in the polar facets before this. + *hp += npj * (ii/n2side); + + // Pixel number in this polar facet. + *hp += ii%n2side - (jj - nside) - 1; + } + } + else if (jj >= -nside) { + // Equatorial regime. + *hp = npole + n8side * (nside - jj) + ii; + } + else { + // South polar regime. + *hp = 24 * nside * nside + 1; + + if (jj > -n2side) { + // Number of pixels in a polar facet with this value of jj. + int npj = 2 * (jj + n2side); + + // Total number of pixels in this row or below it. + *hp -= (npj + 1) * (npj + 1); + + // Number of pixels in this row in the polar facets before this. + *hp += npj * (ii/n2side); + + // Pixel number in this polar facet. + *hp += ii%n2side + (nside + jj) - 1; + } + } + + // Convert double-pixelisation index to regular. + *hp -= 1; + *hp /= 2; + } +} + +void FitsHPX::initHeader(FitsFile* fits) +{ + FitsHead* src = fits->head(); + + // create header + head_ = new FitsHead(pWidth_, pHeight_, 1, -32); + + // OBJECT + char* object = src->getString("OBJECT"); + if (object) + head_->appendString("OBJECT", object, NULL); + + // CRPIX1/2 + float crpix1; + switch (layout_) { + case EQUATOR: + crpix1 = (5 * nside_ + 1) / 2.; + break; + case NORTH: + case SOUTH: + crpix1 = (4 * nside_ + 1) / 2.; + break; + } + float crpix2 = crpix1; + head_->appendReal("CRPIX1", crpix1, 8, "Coordinate reference pixel"); + head_->appendReal("CRPIX2", crpix2, 8, "Coordinate reference pixel"); + + // PCx_y + float cos45 = sqrt(2.0) / 2.0; + if (layout_ == EQUATOR) { + head_->appendReal("PC1_1", cos45, 8, "Transformation matrix element"); + head_->appendReal("PC1_2", cos45, 8, "Transformation matrix element"); + head_->appendReal("PC2_1", -cos45, 8, "Transformation matrix element"); + head_->appendReal("PC2_2", cos45, 8, "Transformation matrix element"); + } + + // CDELT1/2 + float cdelt1 = -90.0 / nside_ / sqrt(2.); + float cdelt2 = -cdelt1; + head_->appendReal("CDELT1", cdelt1, 8, "[deg] Coordinate increment"); + head_->appendReal("CDELT2", cdelt2, 8, "[deg] Coordinate increment"); + + // CTYPE1/2 + const char* pcode; + switch (layout_) { + case EQUATOR: + pcode = "HPX"; + break; + case NORTH: + case SOUTH: + pcode = "XPH"; + break; + } + const char* ctype1; + const char* ctype2; + const char* descr1; + const char* descr2; + switch (coord_) { + case EQU: + ctype1 = "RA--"; + ctype2 = "DEC-"; + descr1 = "Right ascension"; + descr2 = "Declination"; + break; + case GAL: + ctype1 = "GLON"; + ctype2 = "GLAT"; + descr1 = "Galactic longitude"; + descr2 = "Galactic latitude"; + break; + case ECL: + ctype1 = "ELON"; + ctype2 = "ELAT"; + descr1 = "Ecliptic longitude"; + descr2 = "Ecliptic latitude"; + break; + case UNKNOWN: + ctype1 = "XLON"; + ctype2 = "XLAT"; + descr1 = "Longitude"; + descr2 = " Latitude"; + } + { + ostringstream cval; + cval << ctype1 << '-' << pcode << ends; + ostringstream comm; + comm << descr1 << " in an " << pcode << " projection" << ends; + head_->appendString("CTYPE1", cval.str().c_str(), comm.str().c_str()); + } + { + ostringstream cval; + cval << ctype2 << '-' << pcode << ends; + ostringstream comm; + comm << descr2 << " in an " << pcode << " projection" << ends; + head_->appendString("CTYPE2", cval.str().c_str(), comm.str().c_str()); + } + + // CRVAL1/CRVAL2 + float crval1 = 0. + 90.*quad_; + float crval2; + switch (layout_) { + case EQUATOR: + crval2 = 0.; + break; + case NORTH: + crval1 += 180.; + crval2 = 90.; + break; + case SOUTH: + crval1 += 180.; + crval2 = -90.; + break; + } + if (360. < crval1) + crval1 -= 360.; + + { + ostringstream comm; + comm << "[deg] " << descr1 << " at the reference point" << ends; + head_->appendReal("CRVAL1", crval1, 8, comm.str().c_str()); + } + { + ostringstream comm; + comm << "[deg] " << descr2 << " at the reference point" << ends; + head_->appendReal("CRVAL2", crval2, 8, comm.str().c_str()); + } + + // PV2_1/2 + switch (layout_) { + case EQUATOR: + head_->appendInteger("PV2_1", 4, "HPX H parameter (longitude)"); + head_->appendInteger("PV2_2", 3, "HPX K parameter (latitude)"); + break; + case NORTH: + case SOUTH: + head_->appendReal("LONPOLE", 180., 8, "[deg] Native longitude of the celestial pole"); + break; + } + + // we added cards + head_->updateHDU(); +} + +void FitsHPX::swap() +{ + if (!data_) + return; + + // we now need to byteswap back to native form + float* dest = (float*)data_; + for (int ii=0; ii<dataSize_; ii++) { + const char* p = (char*)(dest+ii); + union { + char c[4]; + float f; + } u; + u.c[3] = *p++; + u.c[2] = *p++; + u.c[1] = *p++; + u.c[0] = *p; + dest[ii] = u.f; + } +} |