Source: public/javascript/modules/olqv_axes.js

import { ShapesFactory } from "./olqv_shapes.js";
import { FITS_HEADER } from "./fitsheader.js";
import {DecDeg2DMS, DecDeg2HMS} from "./utils.js";

/**
 * 
 * @typedef {Object} AxesFactory
 * @extends ShapesFactory
 * 
 * @property {number} numeridians number of visible meridians (int)
 * @property {number} nparallels number of visible parallels (int)
 * @property {number} cdelt1 cdelt1 in radians (header is in degrees) (float)
 * @property {number} cdelt2 cdelt2 in radians (header is in degrees) (float)
 * @property {object} axisStyle styling options for axis
 */
class AxesFactory extends ShapesFactory {

    static enter(what) {
        console.group(this.name + "." + what);
    }

    static exit() {
        console.groupEnd();
    }

    /**
     * 
     * @param {Viewer} viewer 
     */
    constructor(viewer) {
        super(viewer, 'axes');
        this.numeridians = 4;
        this.nparallels = 4;
        // number of decimals for ra/dec values on axis
        //this._displayedDecimals = Math.abs(FITS_HEADER.cdelt1).toExponential().split("-")[1] - 1;
        // cdelt1 in radians
        this.cdelt1 = FITS_HEADER.cdelt1 * Math.PI / 180;
        //cdelt2 in radians
        this.cdelt2 = FITS_HEADER.cdelt2 * Math.PI / 180;
        this.axisStyle = new ol.style.Style({
            stroke: new ol.style.Stroke({
                color: 'green',
                width: 2,
            }),
        });

    }

    /**
     * Traces axes on image
     */
    build() {
        let axes = this.getAxes();
        let lineString = new ol.geom.MultiLineString(axes);
        let f = new ol.Feature({ geometry: lineString.simplify(1) });
        f.setStyle(this.axisStyle);

        this.layer.setZIndex(10);
        this.layer.getSource().addFeature(f);

        for (let i = 0; i < axes.length; i++) {
            this.addExtremityPoint(axes[i][0]);
            this.addExtremityPoint(axes[i][axes[i].length - 1]);
        }

        this.layer.set('axes', "axes");
        this.layer.changed();
    }

    /**
     * Shows coordinates at both axis extremity
     * @param {array} ra/dec coordinates
     */
    addExtremityPoint(coords) {
        let radec = this.projection.iRaiDecToRaDec(coords[0], coords[1]);
        let point = new ol.geom.Point(coords);
        let f2 = new ol.Feature({ geometry: point });

        
        f2.setStyle(new ol.style.Style({
            stroke: new ol.style.Stroke({
                color: 'green',
                width: 10,
            }),
            text: new ol.style.Text({
                font: '12px Calibri,sans-serif',
                fill: new ol.style.Fill({ color: '#000' }),
                stroke: new ol.style.Stroke({
                    color: '#fff',
                    width: 2
                }),
                // get the text from the feature - `this` is ol.Feature
                // and show only under certain resolution
                text: "RA : " + DecDeg2HMS(radec['ra']) + ", DEC : " + DecDeg2DMS(radec['dec'])
                //text: "RA : " + radec['ra'].toFixed(this._displayedDecimals) + ", DEC : " + radec['dec'].toFixed(this._displayedDecimals)
            })
        }));
        this.layer.getSource().addFeature(f2);
    }

    /**
     * Returns the points defining the meridians curves. The result curves will be smoothed before display.
     * @param {array} ra/dec limit values
     * @returns {array} an array of x, y arrays with all coordinates of the meridians
     */
    getMeridians(limits) {
        // Plot the meridians, 
        const dra = (limits["RA_max"] - limits["RA_min"]) / (this.numeridians - 1);
        let decs = [];
        for (let i = 0; i < FITS_HEADER.naxis2; i++) {
            decs.push(limits["DEC_min"] + i * (limits["DEC_max"] - limits["DEC_min"]) / (FITS_HEADER.naxis2 - 1));
        }

        let coordinates_list = [];
        for (let i = 0; i < this.numeridians; i++) {
            let coordinates = [];
            let ra_ = limits["RA_min"] + i * dra;
            if(i==0){
                console.log("ra_" + ra_);
            }
            for (let j = 0; j < FITS_HEADER.naxis2; j++) {
                let xy = this.projection.absToRel([ra_], [decs[j]]);
                const i_xp = Math.round(FITS_HEADER.crpix1 + (xy['x'][0]) / (this.cdelt1) - 1);
                const i_yp = Math.round(FITS_HEADER.crpix2 + (xy['y'][0]) / (this.cdelt2) - 1);
                if (this.viewer.isInExtent(i_xp, i_yp)) {
                    coordinates.push([i_xp, i_yp]);
                }
            }

            if (coordinates.length > 1)
                coordinates_list.push(coordinates);
        }
        return coordinates_list;
    }

    /**
     * Returns the points defining the parallels curves. The result curves will be smoothed before display.
     * @param {object} ra/dec limit values
     * @returns {array} an array of x, y arrays with all coordinates of the parallels
     */
    getParallels(limits) {
        const ddec = (limits["DEC_max"] - limits["DEC_min"]) / (this.nparallels);
        let ras = [];

        for (let i = 0; i < FITS_HEADER.naxis1; i++) {
            ras.push(limits["RA_min"] + i * (limits["RA_max"] - limits["RA_min"]) / (FITS_HEADER.naxis1 - 1))
        }

        let coordinates_list = [];
        for (let i = 0; i < this.nparallels; i++) {
            let coordinates = [];
            let ixpix = [];
            let iypix = [];
            let dec_ = limits["DEC_min"] + i * ddec;
            if(i==0){
                console.log("dec_" + dec_);
            }

            for (let j = 0; j < FITS_HEADER.naxis1; j++) {
                let xy = this.projection.absToRel([ras[j]], [dec_]);
                const i_xp = Math.round(FITS_HEADER.crpix1 + (xy['x'][0]) / (this.cdelt1) - 1);
                const i_yp = Math.round(FITS_HEADER.crpix2 + (xy['y'][0]) / (this.cdelt2) - 1);
                ixpix.push(i_xp);
                iypix.push(i_yp);
                if (this.viewer.isInExtent(i_xp, i_yp)) {
                    coordinates.push([i_xp, i_yp]);
                }
                if (coordinates.length > 1)
                    coordinates_list.push(coordinates);
            }
        }
        return coordinates_list;
    }

    /**
     * Returns the boundaries of ra/dec values
     * @returns {object}
     */
    getLimits() {
        let result = { "RA_min": 0, "RA_max": 0, "DEC_min": 0, "DEC_max": 0 };

        let x = new Array(FITS_HEADER.naxis1).fill(0);        
        let y = new Array(FITS_HEADER.naxis2).fill(0); 
        // coordinates after projection
        let raDec;


        for (let i = 0; i < FITS_HEADER.naxis1; i++) {
            x[i] = (i - FITS_HEADER.crpix1) * this.cdelt1;
        }

        for (let j = 0; j < FITS_HEADER.naxis2; j++) {
            y[j] = (j - FITS_HEADER.crpix2) * this.cdelt2;
        }      

        raDec = this.projection.relToAbs([x[0]], [y[0]]);
        result = {  "RA_min": raDec['ra'][0], 
                    "RA_max": raDec['ra'][0], 
                    "DEC_min": raDec['dec'][0], 
                    "DEC_max": raDec['dec'][0] };

        let ra = new Array(x.length);
        let dec = new Array(x.length);
    
        for (var i = 0; i < x.length; i++){
            ra[i] = new Array(y.length);
            dec[i] = new Array(y.length);
        }

        for (let i = 0; i < x.length; i++) {
            for (let j = 0; j < y.length; j++) {
                raDec = this.projection.relToAbs([x[i]], [y[j]]);
                        
                ra[i][j]=raDec['ra'][0];
                dec[i][j]=raDec['dec'][0];

                if (ra[i][j] < result["RA_min"]) {
                    result["RA_min"] = ra[i][j];
                }
                if (ra[i][j] > result["RA_max"]) {
                    result["RA_max"] = ra[i][j];
                }
                if (dec[i][j] < result["DEC_min"]) {
                    result["DEC_min"] = dec[i][j];
                }
                if (dec[i][j] > result["DEC_max"]) {
                    result["DEC_max"] = dec[i][j];
                }
            }
        }        

        return result;
    }

    /**
     * Returns a list of corrdinates of point defining parallels and meridians
     * @returns {array}
     */
    getAxes() {
        let limits = this.getLimits();

        // Plot the meridians
        let coordinates_list = this.getMeridians(limits);
        let parallels = this.getParallels(limits);

        coordinates_list.push.apply(coordinates_list, parallels);

        //return multipoints;
        return coordinates_list;
    }
}


export {
    AxesFactory
}