Source: visualize/FitsHeaderUtil.js

/*
 * License information at https://github.com/Caltech-IPAC/firefly/blob/master/License.txt
 */

import {get} from 'lodash';
import {isImage,isPlot} from './WebPlot.js';


/**
 * @global
 * @public
 * @typedef {Array.<{}>} FitsHeader
 *
 * @prop <Array
 * @prop {number} height
 *
 */





export const HdrConst= {

    // Common FITS Headers
    CTYPE1   : 'CTYPE1',
    CTYPE2   : 'CTYPE2',
    CTYPE3   : 'CTYPE3',
    BITPIX   : 'BITPIX',
    CRPIX1   : 'CRPIX1',
    CRPIX2   : 'CRPIX2',
    CRVAL1   : 'CRVAL1',
    CRVAL2   : 'CRVAL2',
    CDELT1   : 'CDELT1',
    CDELT2   : 'CDELT2',
    CROTA1   : 'CROTA1',
    CROTA2   : 'CROTA2',
    DATAMAX  : 'DATAMAX',
    DATAMIN  : 'DATAMIN',
    EXTNAME  : 'EXTNAME',
    NAXIS1   : 'NAXIS1',
    NAXIS2   : 'NAXIS2',
    NAXIS3   : 'NAXIS3',

    // FITS Headers that are added by firefly
    SPOT_OFF : 'SPOT_OFF', // Extension Offset (added by Firefly)
    SPOT_EXT : 'SPOT_EXT', // Extension Number (added by Firefly)
    SPOT_HS  : 'SPOT_HS',  // Header block size on disk (added by Firefly)
    SPOT_BP  : 'SPOT_BP',  // Original Bitpix value (added by Firefly)
    SPOT_PL  : 'SPOT_PL',  // Plane of FITS cube (added by Firefly)
};


const alphabetAry= 'ABCDEFGHIJKLMNOPQRSTUVWZYZ'.split('');

/**
 * return an array of all the alt projections in this file.
 * @param header
 * @return {string[]}
 */
export const geAtlProjectionIDs= (header) => alphabetAry.filter( (c) => header[HdrConst.CTYPE1+c]);


export function makeHeaderParse(header, altWcs='') {
    return {
        header,
        getIntValue: (key, def= 0) => parseInt(getNumberHeader(header,key,def)),

        getIntOneOfValue: (keyAry, def=0) => {
            const foundKey= keyAry.find( (k) => !isNaN(getNumberHeader(header,k,NaN)) );
            return foundKey ? getNumberHeader(header, foundKey,def) : def;
        },
        getValue: (key, def= '') => getHeader(header, key, def),
        getDoubleValue: (key, def) => getNumberHeader(header,key,def),
        getDoubleOneOfValue: (keyAry, def=0) => {
            const foundKey= keyAry.find( (k) => !isNaN(getNumberHeader(header,k,NaN)) );
            return foundKey ? getNumberHeader(header, foundKey,def) : def;
        },
        isDefinedHeaderList(list) {
            const key= list.find( (i) =>  header[i+altWcs]);
            return Boolean(key);
        },

        getDoubleAry(keyRoot,altWcs, startIdx,endIdx,def=undefined) {
            if (startIdx>endIdx) return def;
            const retAry= [];
            let i= 0;
            for (let headerIdx = startIdx; headerIdx <= endIdx; headerIdx++) {
                retAry[i]= getNumberHeader(header, `${keyRoot}${headerIdx}${altWcs}`,def);
                i++;
            }
            return retAry;
        },
        hasKeyStartWith(startKeys){
          let key;
          for (key in header) {
            if (key.startsWith(startKeys)) {
              return true;
            }
          }
          return false;
        }
    };
}

export function makeDoubleHeaderParse(header,zeroHeader,altWcs) {
    const hp= makeHeaderParse(header, altWcs);
    const zhp= zeroHeader && makeHeaderParse(zeroHeader, altWcs);
    return {
        header,
        zeroHeader,
        getIntValue(key, def = 0) {
            const v1 = hp.getIntValue(key, NaN);
            if (!isNaN(v1)) return v1;
            return zhp ? zhp.getIntValue(key, def) : def;
        },
        getIntOneOfValue: (keyAry, def=0) => {
            const v1= hp.getIntOneOfValue(keyAry,NaN);
            if (!isNaN(v1)) return v1;
            return zhp ? zhp.getIntOneOfValue(keyAry, def) : def;
        },
        getValue: (key, def = '') => {
            const v1 = hp.getValue(key, 'TEST--EMPTY');
            if (v1!=='TEST--EMPTY') return v1;
            return zhp ? zhp.getValue(key, def) : def;
        },
        getDoubleValue(key, def) {
            const v1 = hp.getDoubleValue(key, NaN);
            if (!isNaN(v1)) return v1;
            return zhp ? zhp.getDoubleValue(key, def) : def;
        },
        getDoubleOneOfValue(keyAry, def) {
            const v1= hp.getDoubleOneOfValue(keyAry,NaN);
            if (!isNaN(v1)) return v1;
            return zhp ? zhp.getDoubleOneOfValue(keyAry, def) : def;
        },
        getDoubleAry(keyRoot,altWcs, startIdx,endIdx,def=undefined) {
            if (startIdx>endIdx) return def;
            const retAry= hp.getDoubleAry(keyRoot,altWcs,startIdx,endIdx, def);
            let validValueArr = retAry.filter( (v)=> v!==def );
            if (validValueArr.length>0) return retAry;
            return zhp ? zhp.getDoubleAry(keyRoot,altWcs, startIdx,endIdx,def) : def;
        },
        hasKeyStartWith(startKeys){
           if (hp.hasKeyStartWith(startKeys)){
               return true;
           }
           else {
             return zhp ? zhp.hasKeyStartWith(startKeys):false;
           }
        },
        isDefinedHeaderList: (list) => hp.isDefinedHeaderList(list) || (zhp && zhp.isDefinedHeaderList(list))
    };
}


//=============================================================
//=============================================================
//---------- Header functions
//=============================================================
//=============================================================




/**
 *
 * @param {WebPlot|Object} plotOrHeader image WebPlot or Header obj
 * @param headerKey
 * @return {{value:string,comment:string,idx:number}}
 */
function getHeaderObj(plotOrHeader,headerKey) {
    if (!plotOrHeader) return {};
    if (isPlot(plotOrHeader)) {
        const plot= plotOrHeader;
        if (!isImage(plot) ) return {};
        return get(plot,['header',headerKey],{});
    }
    else {
        return plotOrHeader[headerKey] || {};
    }
}

/**
 * return a header description given a header key
 * @param {WebPlot|Object} plotOrHeader image WebPlot or Header obj
 * @param {string} headerKey key
 * @return {string} the description of the header if it exist otherwise an empty string
 */
export function getHeaderDesc(plotOrHeader,headerKey) {
    return getHeaderObj(plotOrHeader, headerKey)['comment'] || '';
}

/**
 * return a header value given a header key
 * @param {WebPlot|Object} plotOrHeader image WebPlot or Header obj
 * @param {string} headerKey key
 * @param {string} [defVal] the default value
 * @return {string} the value of the header if it exist, otherwise the default value
 */
export function getHeader(plotOrHeader,headerKey, defVal= undefined) {
    return getHeaderObj(plotOrHeader, headerKey)['value'] || defVal;
}

/**
 * return a header number value given a header key
 * @param {WebPlot|Object} plotOrHeader image WebPlot or Header obj
 * @param {string} headerKey key
 * @param {number} [defVal] the default value
 * @return {number} the number value of the header if it exist and can be converted to a number, otherwise the default value
 */
export function getNumberHeader(plotOrHeader, headerKey, defVal= NaN) {
    const v= getHeader(plotOrHeader,headerKey,'');
    if (v==='') return defVal;
    const n= Number(v);
    return isNaN(n) ? defVal : n;
}