import {
unitRescale,
} from "./utils.js";
import { withSAMP, dataPaths, testMode } from './init.js';
import { ServerApi } from './serverApi.js'
import { FITS_HEADER } from './fitsheader.js';
import { Constants } from "./constants.js";
import { setOnHubAvailability } from "./samp_utils.js";
import { getProjection } from "./olqv_projections.js";
import { DOMAccessor } from "./domelements.js";
import { EventFactory } from './customevents.js';
import { LastClickMarker, LastClickMarkerSummed } from './lastclickmarker.js';
import {fireChartSelectionEvent, SingleSpectrumViewer, SingleSpectrumViewer1D, SummedPixelsSpectrumViewer} from './spectrum/olqv_spectrum.js';
import {Slice, SingleSlice, SummedSlice, getSliceUpdateEvent} from './olqv_slice.js';
/**
* Class creating link between slices and spectra
* @typedef {Object} ViewLinker
*
* @property {SpectrumViewer} spectrumViewer
* @property {SummedPixelsSpectrumViewer} summedPixelsSpectrumViewer
* @property {array} extent
* @property {RaLabelFormatter} raLabelFormatter
* @property {SpectruDecLabelFormattermViewer} decLabelFormatter
* @property {SliceViewer} singleSliceImage
* @property {SummedSliceViewer} summedSlicesImage
* @property {Projection} coordsProjection
* @property {SpectroscopyUI} spectroUI
*
*/
class ViewLinker {
constructor(paths, width, height,
RADECRangeInDegrees, divSlice, divSummedSlices, spectroUI) {
// public attributes
this.spectrumViewer = null;
this.summedPixelsSpectrumViewer = null;
this.spectroUI = spectroUI;
// a flag used to prevent display refresh if values are not set correctly
// (i.e valocity/redshift change has not been validated)
this.isRefreshable = true;
this.extent = [0, 0, width - 1, height - 1];
this.singleSliceImage = new SingleSlice(this, divSlice, "hidden-" + divSlice, RADECRangeInDegrees, width, height);
this.summedSlicesImage = new SummedSlice(this, divSummedSlices, "hidden-" + divSummedSlices, RADECRangeInDegrees, width, height);
this.singleSliceImage.addGridEventListener(this.summedSlicesImage);
this.summedSlicesImage.addGridEventListener(this.singleSliceImage);
// projection object for coordinates calculation
try {
this.coordsProjection = getProjection(FITS_HEADER.projectionType);
} catch (e) {
alert(e);
}
this._currentDensity = null;
// private attributes
this._relFITSFilePath = paths.relFITSFilePath;
this._lastClickMarker = new LastClickMarker(this.singleSliceImage._map, 'popup-single');
this._lastClickMarkerSummed = new LastClickMarkerSummed(this.summedSlicesImage._map, 'popup-summed');
//events
this.singleSliceImage._map.getView().on('change:resolution', (event) => {
this.updateView(event, this.summedSlicesImage._map.getView());
});
this.singleSliceImage._map.getView().on('change:center', (event) => {
this.updateView(event, this.summedSlicesImage._map.getView());
});
this.summedSlicesImage._map.getView().on('change:resolution', (event) => {
this.updateView(event, this.singleSliceImage._map.getView());
});
this.summedSlicesImage._map.getView().on('change:center', (event) => {
this.updateView(event, this.singleSliceImage._map.getView());
});
}
get relFITSFilePath(){
return this._relFITSFilePath;
}
getSliceIndex() {
return this.singleSliceImage.sliceIndex;
}
updateSummedSlicesFreqIndexes(iFreq0, iFreq1) {
this.summedSlicesImage.regionOfInterest.iFREQ0 = Math.min(iFreq0, iFreq1);
this.summedSlicesImage.regionOfInterest.iFREQ1 = Math.max(iFreq0, iFreq1);
this.summedSlicesImage.onclick(EventFactory.getEvent(EventFactory.EVENT_TYPES.refreshFrequency, {}));
}
/**
* Sets spectrumViewer
* @param {SpectrumViewer} spectrumViewer
*/
setSpectrumViewer(spectrumViewer) {
this.spectrumViewer = spectrumViewer
}
/**
* Sets summedPixelsSpectrumViewer
* @param {SummedPixelsSpectrumViewer} summedPixelsSpectrumViewer
*/
setSummedPixelsSpectrumViewer(summedPixelsSpectrumViewer) {
this.summedPixelsSpectrumViewer = summedPixelsSpectrumViewer
}
/**
* Executes a POST request and updates singleSlice.
* Request parameters are :
* -for slice retrieval : selected slice index, fits file path
* -for image configuration : ittName, lutName, vmName
*
* @param {number} sliceIndex index of searched slice (int)
*/
getAndPlotSingleSlice(sliceIndex) {
console.log('getAndPlotSingleSlice: entering');
let self = this;
let config = DOMAccessor.getConfiguration();
this.singleSliceImage.setSliceIndex(sliceIndex);
let apiQuery = new ServerApi();
apiQuery.getSingleSlice(
this.singleSliceImage.sliceIndex, this._relFITSFilePath,
config.ittName, config.lutName, config.vmName,
(resp)=>{
self.singleSliceImage.updateSlice(resp["result"]);
self.singleSliceImage.setRms(parseFloat(resp["result"]["statistics"]["stdev"]), FITS_HEADER.bunit);
self.singleSliceImage.setMean(parseFloat(resp["result"]["statistics"]["mean"]), FITS_HEADER.bunit);
DOMAccessor.setSliceChannel("Chan#" + sliceIndex);
if (withSAMP) {
dataPaths.relSlicePNG = resp["result"]["path_to_png"];
}
if(testMode){
self.singleSliceImage._executeSliceLoadedListener(
getSliceUpdateEvent(self.singleSliceImage, sliceIndex, "single",resp["result"]));
}
}
);
}
/**
* Executes a POST request and updates summedSlice
* Request parameters are :
* -for slice retrieval : start index (on averaged spectrum), end index (on averaged spectrum), fits file path
* -for image configuration : ittName, lutName, vmName
*
* @param {number} sliceIndex0 start index (int)
* @param {number} sliceIndex1 end index (int)
*/
getAndPlotSummedSlices(sliceIndex0, sliceIndex1, callback) {
let self = this;
let config = DOMAccessor.getConfiguration();
this.summedSlicesImage.sliceIndex0 = sliceIndex0;
this.summedSlicesImage.sliceIndex1 = sliceIndex1;
let apiQuery = new ServerApi();
apiQuery.getSummedSlice(
self.summedSlicesImage.sliceIndex0, self.summedSlicesImage.sliceIndex1,
self._relFITSFilePath, config.ittName, config.lutName, config.vmName,
(resp)=>{
self.summedSlicesImage.updateSlice(resp["result"]);
self.summedSlicesImage.setRms(parseFloat(resp["result"]["statistics"]["stdev"]), FITS_HEADER.bunit + "*km/s");
self.summedSlicesImage.setMean(parseFloat(resp["result"]["statistics"]["mean"]), FITS_HEADER.bunit + "*km/s");
if (callback !== undefined) {
callback();
}
if (withSAMP) {
dataPaths.relSummedSlicesPNG = resp["result"]["path_to_png"];
}
if(testMode){
self.summedSlicesImage._executeSliceLoadedListener(
getSliceUpdateEvent(self.summedSlicesImage, self.getSliceIndex(), "summed", resp["result"]));
}
});
console.log("_updateSummedSlicesWithPOST : exiting");
}
/**
* Refreshes both slices display from current parameters
*/
refresh() {
this.getAndPlotSingleSlice(this.singleSliceImage.sliceIndex);
this.getAndPlotSummedSlices(this.summedSlicesImage.sliceIndex0, this.summedSlicesImage.sliceIndex1);
}
/**
* Notifies to the view that an action occured on an image (image has been moved or zoomed in/out)
* @param {*} event type of action
* @param {*} viewRef modified view
*/
updateView(event, viewRef) {
let newValue = event.target.get(event.key);
viewRef.set(event.key, newValue);
}
/**
* Removes the currently selected box on the summedSlicesViewer
*/
forgetSelectedBox() {
this.summedSlicesImage.forgetSelectedBox();
}
/**
* Displays a popup at the position clicked in the slice. It shows the coordinates of the click in the image,
* the chanel index, ra/dec values and flux density. Flux density is always set as "To Be Determined" because a query to the server
* it necessary to get the value. "t.b.d." will be replaced once the value has been obtained.
* @param {Slice} target the slice where the click occured
* @param {array} coordinate a 2 elements array containing the x,y coordinates
*/
_markLastClickInSlice(target, coordinate) {
let raDec = this.coordsProjection.iRaiDecToHMSDMS(coordinate[0], coordinate[1]);
target.setPositions(coordinate[0], coordinate[1], raDec["ra"], raDec["dec"]);
target.setChanIndex(this.singleSliceImage.sliceIndex);
target.setFluxDensity("t.b.d", "");
target.updateLastClickInfos();
};
/**
* Calls _markLastClickInSlice in both single and summed slices
* @param {array} coordinate a 2 elements array containing the x,y coordinates
*/
markLastClickInSlices(coordinate) {
this._markLastClickInSlice(this._lastClickMarker, coordinate);
this._markLastClickInSlice(this._lastClickMarkerSummed, coordinate);
};
/**
* Sets the value of flux density in a popup in the single slice
* @param {number} density density value (float)
* @param {string} source provenance of the call (spectrum or data slice)
*/
setFluxDensityInPopup(density, source) {
// marker is shown if click on a slice or click on a spectrum if it is already visible
if(source === Slice.objectType ||
(source === SpectrumViewer.objectType && this._lastClickMarker.isVisible)){
this._currentDensity = density;
this._lastClickMarker.setFluxDensity(density * unitRescale(FITS_HEADER.bunit), FITS_HEADER.bunit);
this._lastClickMarker.setChanIndex(this.singleSliceImage.sliceIndex);
this._lastClickMarker.updateLastClickInfos();
}
}
/**
* Sets the value of flux density in a popup in the averaged slice
* density is already known from _currentDensity attribute
* if isRefresh is true, the popup area will not be displayed if it is currently
* hidden
* @param {number} density density value (float)
* @param {boolean} isRefresh only ask for a refresh of the displayed value
*/
setFluxDensityInSummedPopup(value, isRefresh) {
this._lastClickMarkerSummed.setFluxDensity(value, FITS_HEADER.bunit + "*km/s");
this._lastClickMarkerSummed.updateLastClickInfos(isRefresh);
}
}
/**
* Returns a ViewLinker object that provides an interface to manipulate Spectrums and Slices
*
* @param {array} radecRange RA/DEC ranges corresponding to opened fits file
* @param {SpectroUI} spectroUI spectroscopy interface control object
* @param {SourceTable} sourceTable NED interface object
* @param {MarkerList} markerList list of markers
* @returns {ViewLinker}
*/
function getViewLinker(radecRange, spectroUI, sourceTable, markerList) {
let RADECRangeInDegrees = radecRange;
console.log("Data of '" + dataPaths.relFITSFilePath + "' are contained in " + JSON.stringify(radecRange));
let viewLinker = new ViewLinker(dataPaths, FITS_HEADER.width, FITS_HEADER.height, RADECRangeInDegrees, "slice",
"summed-slices", spectroUI);
// naxis3 is slice number, central channel by default
viewLinker.getAndPlotSingleSlice(Math.round(FITS_HEADER.naxis3 / 2));
// upper spectrum
let spectrumViewer = new SingleSpectrumViewer(dataPaths, 'spectrum',
Constants.PLOT_WIDTH_3D_LARGE,
Constants.PLOT_HEIGHT_RATIO_3D_LARGE, spectroUI);
spectrumViewer.setViewLinker(viewLinker);
//spectrumViewer.setSpectroUI(spectroUI);
viewLinker.setSpectrumViewer(spectrumViewer);
// show centered channel
const initialPixel = FITS_HEADER.getCentralPixelPosition();
spectrumViewer.plot(initialPixel[0], initialPixel[1]);
// summed spectrum
let summedPixelsSpectrumViewer = new SummedPixelsSpectrumViewer(dataPaths, 'summed-pixels-spectrum',
Constants.PLOT_WIDTH_3D_LARGE,
Constants.PLOT_HEIGHT_RATIO_3D_LARGE, spectroUI);
summedPixelsSpectrumViewer.setViewLinker(viewLinker);
//summedPixelsSpectrumViewer.setSpectroUI(spectroUI);
viewLinker.setSummedPixelsSpectrumViewer(summedPixelsSpectrumViewer);
// summed slices image with default selection box
viewLinker.getAndPlotSummedSlices(summedPixelsSpectrumViewer.defaultIndexMin,
summedPixelsSpectrumViewer.defaultIndexMax,
() => {
viewLinker.summedSlicesImage.drawInitialBox();
});
//refresh lines display when redshift selection changes
sourceTable.addListener(summedPixelsSpectrumViewer);
sourceTable.addListener(spectrumViewer);
sourceTable.addListener(viewLinker.summedSlicesImage);
sourceTable.addListener(viewLinker.singleSliceImage);
markerList.addListener(viewLinker.summedSlicesImage);
markerList.addListener(viewLinker.singleSliceImage);
viewLinker.singleSliceImage.enableSelectorMode();
viewLinker.summedSlicesImage.enableSelectorMode();
/*if (withSAMP) {
// all implement setSampButtonVisible
setOnHubAvailability([spectrumViewer,
summedPixelsSpectrumViewer,
viewLinker.singleSliceImage,
viewLinker.summedSlicesImage]);
}*/
return viewLinker;
}
export {getViewLinker}