import { ShapesFactory } from "./olqv_shapes.js";
import { FITS_HEADER } from "./fitsheader.js";
import {DecDeg2DMS, DecDeg2HMS, degToRad, radToDeg} from "./utils.js";
import {AxisButton} from "./olqv_olbuttons.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 = 6;
this.nparallels = 6;
//axes are hidden by default
this.isVisible = 0;
// 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 = degToRad(FITS_HEADER.cdelt1);
//cdelt2 in radians
this.cdelt2 = degToRad(FITS_HEADER.cdelt2);
this.axisStyle = new ol.style.Style({
stroke: new ol.style.Stroke({
// alpha = 0 to hide the axes
color: 'rgb(0, 0, 255, 0)',
width: 0,
}),
});
this._buttonObject = new AxisButton(viewer);
}
/**
* Utility. Returns the toolbox button that will activate this instance.
* @returns {button}
*/
getButtonObject() {
return this._buttonObject;
}
getButtonClick(){
this.toggleAxes();
if(this.isVisible){
this._buttonObject.getButton().classList.add("active");
}else{
this._buttonObject.getButton().classList.remove("active");
}
this.viewer.map.getView().changed();
}
/**
* Updates the alpha value of the axes and the coordinates text
* @param {number} alpha rgba alpha value
*/
updateAxesStyle(alpha){
this.axisStyle.getStroke().setColor('rgb(0, 0, 255, '+alpha+')');
let features = this.layer.getSource().getFeatures();
for(let f of features){
if(f.getStyle().getText() !== null){
f.getStyle().getText().getStroke().setColor('rgb(255, 255, 255, '+alpha+')');
f.getStyle().getText().getFill().setColor('rgb(0, 0, 0, '+alpha+')');
}
}
}
/**
* Makes the axes and coordinates visible
*/
showAxes(){
this.updateAxesStyle(1);
this.layer.changed();
this.isVisible = 1;
}
/**
* Hides the axes and coordinates
*/
hideAxes(){
this.updateAxesStyle(0);
this.layer.changed();
this.isVisible = 0;
}
/**
* Toggles visibility of axes and coordinates
*/
toggleAxes(){
if(this.isVisible){
this.hideAxes();
}else{
this.showAxes();
}
}
/**
* Traces axes on image
*/
build() {
let axes = this.getAxes();
let lineString = new ol.geom.MultiLineString(axes.parallels.concat(axes.meridians));
let f = new ol.Feature({ geometry: lineString.simplify(1) });
f.setStyle(this.axisStyle);
this.layer.setZIndex(10);
this.layer.getSource().addFeature(f);
// show coordinates at meridians top extremity
for (let i = 0; i < axes.meridians.length; i++) {
let coords = axes.meridians[i][axes.meridians[i].length - 1];
let radec = this.projection.iRaiDecToRaDec(coords[0], coords[1]);
this.addExtremityPoint(coords, DecDeg2HMS(radec.ra));
}
// show coordinates at parallels left extremity
for (let i = 0; i < axes.parallels.length; i++) {
let coords = axes.parallels[i][0];
let radec = this.projection.iRaiDecToRaDec(coords[0], coords[1]);
this.addExtremityPoint(coords, DecDeg2DMS(radec.dec));
}
this.layer.set('axes', "axes");
this.layer.changed();
}
/**
* Shows coordinates at both axis extremity
* @param {array} ra/dec coordinates
*/
addExtremityPoint(coords, label) {
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: 'rgb(0, 0, 0, 0)' }),
stroke: new ol.style.Stroke({
color: 'rgb(255, 255, 255, 0)',
width: 2
}),
// get the text from the feature - `this` is ol.Feature
// and show only under certain resolution
text: label
})
}));
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 x = FITS_HEADER.naxis1 * Math.abs(FITS_HEADER.cdelt1);
const stepx = x / this.numeridians;
const y = FITS_HEADER.naxis2 * Math.abs(FITS_HEADER.cdelt2);
const stepy = y / this.numeridians;
let ras = [];
let decs = [];
const radDegInterval = radToDeg(limits.RA_max) - radToDeg(limits.RA_min);
const decDegInterval = radToDeg(limits.DEC_max) - radToDeg(limits.DEC_min);
const coeff = 0.5;
for(let i = radToDeg(limits.RA_min) - coeff*radDegInterval;
i <= radToDeg(limits.RA_max)+ coeff*radDegInterval; i = i+stepx){
ras.push(i);
}
for(let i = radToDeg(limits.DEC_min) - coeff*decDegInterval;
i <= radToDeg(limits.DEC_max) + coeff*decDegInterval; i = i+stepy/20){
decs.push(i);
}
let coordinates_list = [];
for(let i = 0; i < ras.length; i=i+2){
let coordinates = [];
for(let j = 0; j < decs.length; j=j+2){
let xy = this.projection.absToRel([ras[i]*(Math.PI/180)], [decs[j]*(Math.PI/180)]);
const i_xp = Math.round(FITS_HEADER.crpix1 + ((xy.x) / (this.cdelt1)) - 1);
const i_yp = Math.round(FITS_HEADER.crpix2 + ((xy.y) / (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) {
// Plot the meridians,
const x = FITS_HEADER.naxis1 * Math.abs(FITS_HEADER.cdelt1);
const stepx = x / this.nparallels;
const y = FITS_HEADER.naxis2 * Math.abs(FITS_HEADER.cdelt2);
const stepy = y / this.nparallels;
let ras = [];
let decs = [];
const radDegInterval = radToDeg(limits.RA_max) - radToDeg(limits.RA_min);
const decDegInterval = radToDeg(limits.DEC_max) - radToDeg(limits.DEC_min);
const coeff = 0.5;
for(let i = radToDeg(limits.RA_min) - coeff*radDegInterval;
i <= radToDeg(limits.RA_max)+ coeff*radDegInterval; i = i+stepx/20){
ras.push(i);
}
for(let i = radToDeg(limits.DEC_min) - coeff*decDegInterval;
i <= radToDeg(limits.DEC_max) + coeff*decDegInterval; i = i+stepy){
decs.push(i);
}
let coordinates_list = [];
for(let i = 0; i < decs.length; i=i+2){
let coordinates = [];
for(let j = 0; j < ras.length; j=j+2){
let xy = this.projection.absToRel([ras[j]*(Math.PI/180)], [decs[i]*(Math.PI/180)]);
const i_xp = Math.round(FITS_HEADER.crpix1 + (xy.x) / (this.cdelt1) - 1);
const i_yp = Math.round(FITS_HEADER.crpix2 + (xy.y) / (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;
}
_updateMaxValues(x, y, result){
let raDec = this.projection.relToAbs([x], [y]);
let ra=raDec.ra[0];
let dec=raDec.dec[0];
if (ra < result.RA_min) {
result.RA_min = ra;
}
if (ra > result.RA_max) {
result.RA_max = ra;
}
if (dec < result.DEC_min) {
result.DEC_min = dec
}
if (dec > result.DEC_max) {
result.DEC_max = dec;
}
return result;
}
/**
* 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] };
// get min/max values of ra/dec after projection
// search only on the edges of the image
let i=0;
for (let j = 0; j < y.length; j=j+1) {
result = this._updateMaxValues(x[i], y[j], result);
}
i=x.length-1;
for (let j = 0; j < y.length; j=j+1) {
result = this._updateMaxValues(x[i], y[j], result);
}
let j=0;
for (let i = 0; i < x.length; i=i+1) {
result = this._updateMaxValues(x[i], y[j], result);
}
j=y.length-1;
for (let i = 0; i < x.length; i=i+1) {
result = this._updateMaxValues(x[i], y[j], result);
}
return result;
}
/**
* Returns a list of corrdinates of point defining parallels and meridians
* @returns {array}
*/
getAxes() {
let limits = this.getLimits();
let coordinates_list = {
meridians : this.getMeridians(limits),
parallels : this.getParallels(limits)
};
//return multipoints;
return coordinates_list;
}
}
export {
AxesFactory
};