/*
* License information at https://github.com/Caltech-IPAC/firefly/blob/master/License.txt
*/
import {isEmpty, isArray} from 'lodash';
import {Band} from './Band.js';
import {BandState} from './BandState.js';
import CoordinateSys from './CoordSys.js';
import Enum from 'enum';
/**
* The type of rotation
* can be 'NORTH', 'ANGLE', 'UNROTATE'
* @public
* @global
* */
export const RotateType= new Enum(['NORTH', 'ANGLE', 'UNROTATE']);
/**
* can be 'ROTATE', 'CROP', 'FLIP_Y'
*
*/
export const Operation= new Enum(['ROTATE', 'CROP', 'FLIP_Y']);
export class PlotState {
/**
* @summary Contains data about the state of a plot.
* This object is never created directly if is always instantiated from the json sent from the server.
* @prop {number} zoomLevel - the zoomlevel of the image
* @prop {boolean} threeColor - is a three color plot
* @prop {number} colorTableId - the id of the color table in use
* @prop {boolean} flippedY - fliped on the y axis
* @prop {number} rotationAngle - if rotated by angle, then the angle of the rotation
* @prop {RotateType} rotationType the type of rotations
* @public
*/
constructor() {
this.bandStateAry= [null,null,null];
this.ctxStr=null;
this.zoomLevel= 1;
this.threeColor= false;
this.colorTableId= 0;
this.rotationType= RotateType.UNROTATE;
this.rotaNorthType= CoordinateSys.EQ_J2000;
this.flippedY= false;
this.rotationAngle= NaN;
this.ops= [];
}
//======================================================================
//----------------------- Public Methods -------------------------------
//======================================================================
/**
* @summary returns the first used band. It is possible that this method will return null. You should always check.
* @return {Band} the first name used.
* @public
*/
firstBand() {
const bandAry= this.getBands();
return bandAry && bandAry[0];
}
/**
* @summary Get an array of used band. It is possible that this routine will return a array of length 0
* @return {Array} the bands in use
* @public
*/
getBands() {
if (!this.usedBands) {
this.usedBands= [];
if (this.threeColor) {
if (this.get(Band.RED).hasRequest()) this.usedBands.push(Band.RED);
if (this.get(Band.GREEN).hasRequest()) this.usedBands.push(Band.GREEN);
if (this.get(Band.BLUE).hasRequest()) this.usedBands.push(Band.BLUE);
}
else {
this.usedBands.push(Band.NO_BAND);
}
}
return this.usedBands;
}
/**
* @param {String|Band} [band] the bad to test
* @return {boolean}
* @public
*/
isBandUsed(band) {
return this.getBands().indexOf(band)>-1;
}
/**
* Get the number of the color table
* @return {number}
*/
getColorTableId() { return this.colorTableId; }
/**
*
* @return {boolean}
*/
isThreeColor() { return this.threeColor; }
/**
*
* @return {number}
*/
getZoomLevel() {return this.zoomLevel; }
/**
*
* @return {boolean}
*/
isFlippedY() { return this.flippedY; }
/**
* @summary this method will make a copy of WebPlotRequest. Any changes to the WebPlotRequest object
* after the set will not be reflected here.
* @param {Band} [band] the band to get the request for, if not passed the used the primary band
* @return {WebPlotRequest} the WebPlotRequest
* @public
*/
getWebPlotRequest(band) { return this.get(band || this.firstBand()).getWebPlotRequest(); }
/**
* @summary if a cube, checkout how many images it contains
* @param {Band} [band] the band check for, if not passed the used the primary band
* @return {number} the WebPlotRequest
* @public
*/
getCubeCnt(band) { return this.get(band || this.firstBand()).getCubeCnt(); }
getCubePlaneNumber(band) {
return this.get(band || this.firstBand()).getCubePlaneNumber();
}
/**
* Get the range values for the plot.
* @param {band} [band] the band get range value for, parameter is unnecessary for non-three color plots
* @return {RangeValues}
*/
getRangeValues(band) { return this.get(band || this.firstBand()).getRangeValues(); }
/**
*
* @param band
* @return {ClientFitsHeader}
*/
getDirectFileAccessData(band) { return this.get(band).getDirectFileAccessData(); }
/**
* @param band
* @return {string}
*/
getWorkingFitsFileStr(band) { return band ? this.get(band).getWorkingFitsFileStr() : null; }
/**
* @param band
* @return {string}
*/
getOriginalFitsFileStr(band) { return band ? this.get(band).getOriginalFitsFileStr() : null; }
getUploadFileName(band) { return band ? this.get(band).getUploadedFileName() : null; }
/**
*
* @param {Operation} op
*/
hasOperation(op) {
let newOp;
if (op.key) {
newOp= op;
}
else if (op.toString === 'function') {
newOp= Operation.get(op.toString());
}
else if (typeof op === 'string') {
newOp= Operation.get(op);
}
return newOp ? this.ops.indexOf(newOp)>-1 : false;
}
equals(obj) {
return (obj instanceof PlotState) ? this.toJson()===obj.toJson() : false;
}
/**
* @param {object} band
* @return {BandState}
*/
get(band) {
let idx;
if (band.value) {
idx= band.value;
}
else if (typeof band === 'number') {
idx= band;
}
else {
const b= Band.get(band.toString());
idx= b ? b.value : Band.NO_BAND.value;
}
if (!this.bandStateAry[idx]) this.bandStateAry[idx]= BandState.makeBandState();
return this.bandStateAry[idx];
}
toJson(includeDirectAccessData= true) {
return JSON.stringify(PlotState.convertToJSON(this, includeDirectAccessData));
}
static makePlotState() {
return new PlotState();
}
static makePlotStateWithJson(psJson) {
if (!psJson) return null;
const state= PlotState.makePlotState();
if (isArray(psJson.bandStateAry)) {
state.bandStateAry= psJson.bandStateAry.map( (bJ) => BandState.makeBandStateWithJson(bJ));
}
else {
state.bandStateAry= [BandState.makeBandStateWithJson(psJson.bandStateAry)];
}
state.ctxStr=psJson.ctxStr;
state.zoomLevel= psJson.zoomLevel;
state.colorTableId= psJson.colorTableId || 0;
// if not include used defaulted values
state.multiImage= psJson.multiImage; // if multiImage is not default we don't care
state.rotationType= psJson.rotationType ? RotateType.get(psJson.rotationType) : RotateType.UNROTATE;
state.rotaNorthType= psJson.rotaNorthType ? CoordinateSys.parse(psJson.rotaNorthType) : CoordinateSys.EQ_J2000;
state.rotationAngle= psJson.rotationAngle ? psJson.rotationAngle : NaN;
state.flippedY= Boolean(psJson.flippedY);
state.threeColor= Boolean(psJson.threeColor);
state.ops= psJson.ops ? psJson.ops.map( (op) => Operation.get(op) ) :[];
return state;
}
/**
* @summary convert his PlotState to something can be used with JSON.stringify
* @param {PlotState} s
* @param {boolean} includeDirectAccessData include the includeDirectAccessData object
*/
static convertToJSON(s, includeDirectAccessData= true) {
if (!s) return null;
const json= {};
json.ctxStr=s.ctxStr;
json.zoomLevel= s.zoomLevel;
json.colorTableId= s.colorTableId;
// json.rotationType= s.rotationType.key;
// json.rotaNorthType= s.rotaNorthType.toString();
// json.ops= s.ops.map( (op) => op.key );
// json.threeColor= s.threeColor;
// json.flippedY= s.flippedY;
// json.rotationAngle= s.rotationAngle;
// if not defaulted values, don't include
if (s.multiImage) json.multiImage= s.multiImage;
if (s.rotationType!==RotateType.UNROTATE) json.rotationType= s.rotationType.key;
if (s.rotaNorthType!==CoordinateSys.EQ_J2000) json.rotaNorthType= s.rotaNorthType.toString();
if (!isEmpty(s.ops)) json.ops= s.ops.map( (op) => op.key );
if (s.threeColor) json.threeColor= true;
if (s.flippedY) json.flippedY= true;
if (!isNaN(s.rotationAngle)) json.rotationAngle= s.rotationAngle;
json.bandStateAry= s.bandStateAry.map( (bJ) => BandState.convertToJSON(bJ,includeDirectAccessData));
return json;
}
}
export default PlotState;