rename ImageSurvey in ImageHiPS, add a global cache for HiPS and FITS

This commit is contained in:
Matthieu Baumann
2024-04-30 09:59:08 +02:00
parent 9e0caa54c2
commit 5e8d0aca42
13 changed files with 803 additions and 1559 deletions

View File

@@ -1024,10 +1024,10 @@ impl App {
Ok(())
}
pub(crate) fn add_image_survey(&mut self, hips_cfg: HiPSCfg) -> Result<(), JsValue> {
pub(crate) fn add_image_hips(&mut self, hips_cfg: HiPSCfg) -> Result<(), JsValue> {
let hips =
self.layers
.add_image_survey(&self.gl, hips_cfg, &mut self.camera, &self.projection)?;
.add_image_hips(&self.gl, hips_cfg, &mut self.camera, &self.projection)?;
self.tile_fetcher
.launch_starting_hips_requests(hips, &mut self.downloader);

View File

@@ -364,11 +364,11 @@ impl WebClient {
/// * If the number of surveys is greater than 4. For the moment, due to the limitations
/// of WebGL2 texture units on some architectures, the total number of surveys rendered is
/// limited to 4.
#[wasm_bindgen(js_name = addImageSurvey)]
pub fn add_image_survey(&mut self, hips: JsValue) -> Result<(), JsValue> {
#[wasm_bindgen(js_name = addImageHiPS)]
pub fn add_image_hips(&mut self, hips: JsValue) -> Result<(), JsValue> {
// Deserialize the survey objects that compose the survey
let hips = serde_wasm_bindgen::from_value(hips)?;
self.app.add_image_survey(hips)?;
self.app.add_image_hips(hips)?;
Ok(())
}

View File

@@ -421,7 +421,7 @@ impl Layers {
Ok(())
}
pub fn add_image_survey(
pub fn add_image_hips(
&mut self,
gl: &WebGlContext,
hips: HiPSCfg,

View File

@@ -109,8 +109,8 @@ A.aladin = function (divSelector, options) {
* @param {string} id - Mandatory unique identifier for the survey.
* @param {string} url - Can be an `url` that refers to a HiPS.
* Or it can be a "CDS ID" pointing towards a HiPS. One can found the list of IDs {@link https://aladin.cds.unistra.fr/hips/list| here}.
* @param {ImageSurveyOptions} [options] - Options describing the survey
* @returns {ImageSurvey} - A HiPS image object
* @param {ImageHiPSOptions} [options] - Options describing the survey
* @returns {ImageHiPS} - A HiPS image object
*/
A.imageHiPS = function (id, url, options) {
return Aladin.createImageSurvey(
@@ -131,7 +131,7 @@ A.imageHiPS = function (id, url, options) {
* @memberof A
* @param {string} url - Options describing the fits file. An url is mandatory
* @param {ImageFITSOptions} [options] - Options describing the fits file. An url is mandatory
* @returns {ImageSurvey} - A HiPS image object
* @returns {ImageFITS} - A HiPS image object
* @example
* const sourceObj = A.source(180.0, 30.0, data, options);
*/

View File

@@ -37,10 +37,10 @@ import { Sesame } from "./Sesame.js";
import { PlanetaryFeaturesNameResolver } from "./PlanetaryFeaturesNameResolver.js";
import { CooFrameEnum } from "./CooFrameEnum.js";
import { MeasurementTable } from "./MeasurementTable.js";
import { ImageSurvey } from "./ImageSurvey.js";
import { ImageHiPS } from "./ImageHiPS.js";
import { Coo } from "./libs/astro/coo.js";
import { CooConversion } from "./CooConversion.js";
import { MocServer } from './MocServer';
import { HiPSCache } from './DefaultHiPSCache';
import { ProjectionEnum } from "./ProjectionEnum.js";
@@ -382,9 +382,9 @@ export let Aladin = (function () {
}
i++;
});
} else if (options.survey === ImageSurvey.DEFAULT_SURVEY_ID) {
// DSS is cached inside ImageSurvey class, no need to provide any further information
const survey = this.createImageSurvey(ImageSurvey.DEFAULT_SURVEY_ID);
} else if (options.survey === ImageHiPS.DEFAULT_SURVEY_ID) {
// DSS is cached inside ImageHiPS class, no need to provide any further information
const survey = this.createImageSurvey(ImageHiPS.DEFAULT_SURVEY_ID);
this.setBaseImageLayer(survey);
} else {
@@ -468,11 +468,9 @@ export let Aladin = (function () {
// at least id or url is defined
let key = id || url;
//if (!(key in ImageSurvey.cache)) {
// Merge what is already in the cache for that HiPS with new properties
// coming from the MOCServer
ImageSurvey.cache[key] = {...ImageSurvey.cache[key], ...cachedSurvey}
//}
HiPSCache.append(key, {...HiPSCache.get(key), ...cachedSurvey})
}
ALEvent.HIPS_LIST_UPDATED.dispatchedTo(this.aladinDiv);
@@ -664,9 +662,9 @@ export let Aladin = (function () {
// access to WASM libraries
Aladin.wasmLibs = {};
Aladin.DEFAULT_OPTIONS = {
survey: ImageSurvey.DEFAULT_SURVEY_ID,
survey: ImageHiPS.DEFAULT_SURVEY_ID,
// surveys suggestion list
hipsList: ImageSurvey.DEFAULT_HIPS_LIST,
hipsList: HiPSCache.DEFAULT_HIPS_LIST,
//surveyUrl: ["https://alaskybis.unistra.fr/DSS/DSSColor", "https://alasky.unistra.fr/DSS/DSSColor"],
target: "0 +0",
cooFrame: "J2000",
@@ -792,7 +790,7 @@ export let Aladin = (function () {
options.frame = requestedFrame;
}
var requestedSurveyId = Utils.urlParam('survey');
if (requestedSurveyId && ImageSurvey.getSurveyInfoFromId(requestedSurveyId)) {
if (requestedSurveyId && ImageHiPS.getSurveyInfoFromId(requestedSurveyId)) {
options.survey = requestedSurveyId;
}
var requestedZoom = Utils.urlParam('zoom');
@@ -1412,11 +1410,11 @@ export let Aladin = (function () {
* </ul>
* @param {string} [cooFrame] - Values accepted: 'equatorial', 'icrs', 'icrsd', 'j2000', 'gal', 'galactic'
* @param {number} [maxOrder] - The maximum HEALPix order of the HiPS, i.e the HEALPix order of the most refined tile images of the HiPS.
* @param {ImageSurveyOptions} [options] - Options describing the survey
* @returns {ImageSurvey} A HiPS image object.
* @param {ImageOptions} [options] - Options describing the survey
* @returns {ImageHiPS} A HiPS image object.
*/
Aladin.prototype.createImageSurvey = function(id, name, url, cooFrame, maxOrder, options) {
let surveyOptions = ImageSurvey.cache[id];
let surveyOptions = HiPSCache.get(id);
if (!surveyOptions) {
surveyOptions = {name, maxOrder, cooFrame, ...options};
@@ -1428,10 +1426,10 @@ export let Aladin = (function () {
surveyOptions.url = url;
}
ImageSurvey.cache[id] = surveyOptions;
HiPSCache.append(id, surveyOptions);
}
return new ImageSurvey(id, surveyOptions.url, surveyOptions);
return new ImageHiPS(id, surveyOptions.url, surveyOptions);
};
/**
@@ -1451,8 +1449,8 @@ export let Aladin = (function () {
* </ul>
* @param {string} [cooFrame] - Values accepted: 'equatorial', 'icrs', 'icrsd', 'j2000', 'gal', 'galactic'
* @param {number} [maxOrder] - The maximum HEALPix order of the HiPS, i.e the HEALPix order of the most refined tile images of the HiPS.
* @param {ImageSurveyOptions} [options] - Options describing the survey
* @returns {ImageSurvey} A HiPS image object.
* @param {ImageOptions} [options] - Options describing the survey
* @returns {ImageHiPS} A HiPS image object.
*/
Aladin.createImageSurvey = Aladin.prototype.createImageSurvey;
@@ -1471,10 +1469,11 @@ export let Aladin = (function () {
// Do not use proxy with CORS headers until we solve that: https://github.com/MattiasBuelens/wasm-streams/issues/20
//url = Utils.handleCORSNotSameOrigin(url);
let image = ImageSurvey.cache[url];
let image = HiPSCache.get(url);
if (!image) {
image = new ImageFITS(url, name, options, successCallback, errorCallback)
ImageSurvey.cache[url] = image;
options = { name, successCallback, errorCallback, ...options };
image = new ImageFITS(url, options);
HiPSCache.append(url, image);
}
return image;
@@ -1488,10 +1487,10 @@ export let Aladin = (function () {
* @static
* @param {string} url - The url of the fits.
* @param {string} [name] - The url of the fits.
* @param {ImageSurveyOptions} [options] - Options for rendering the image
* @param {ImageOptions} [options] - Options for rendering the image
* @param {function} [success] - A success callback
* @param {function} [error] - A success callback
* @returns {ImageSurvey} A FITS image object.
* @returns {ImageFITS} A FITS image object.
*/
Aladin.createImageFITS = Aladin.prototype.createImageFITS;
@@ -1508,10 +1507,10 @@ export let Aladin = (function () {
* <li>1. An url that refers to a HiPS.</li>
* <li>Or it can be a "CDS ID" that refers to a HiPS. One can found the list of IDs {@link https://aladin.cds.unistra.fr/hips/list| here}</li>
* </ul>
* @param {ImageSurveyOptions} [options] - Options for rendering the image
* @param {ImageHiPSOptions} [options] - Options for rendering the image
* @param {function} [success] - A success callback
* @param {function} [error] - A success callback
* @returns {ImageSurvey} A FITS image object.
* @returns {ImageHiPS} A FITS image object.
*/
Aladin.prototype.newImageSurvey = function(url, options) {
const id = url;
@@ -1523,30 +1522,30 @@ export let Aladin = (function () {
* Add a new HiPS layer to the view on top of the others
*
* @memberof Aladin
* @param {string|ImageSurvey} [survey="CDS/P/DSS2/color"] - Can be:
* @param {string|ImageHiPS} [survey="CDS/P/DSS2/color"] - Can be:
* <ul>
* <li>1. An url that refers to a HiPS.</li>
* <li>2. Or it can be a CDS ID that refers to a HiPS. One can found the list of IDs {@link https://aladin.cds.unistra.fr/hips/list| here}</li>
* <li>3. It can also be an {@link ImageSurvey} HiPS object created from {@link A.imageHiPS}</li>
* <li>3. It can also be an {@link ImageHiPS} HiPS object created from {@link A.imageHiPS}</li>
* </ul>
* By default, the {@link https://alasky.cds.unistra.fr/DSS/DSSColor/|Digital Sky Survey 2} survey will be displayed
*/
Aladin.prototype.addNewImageLayer = function(survey="CDS/P/DSS2/color") {
let layerName = Utils.uuidv4();
this.setOverlayImageLayer(survey, layerName);
return this.setOverlayImageLayer(survey, layerName);
}
/**
* Change the base layer of the view
*
* It internally calls {@link Aladin#setBaseImageLayer|Aladin.setBaseImageLayer} with the url/{@link ImageSurvey}/{@link ImageFITS} given
* It internally calls {@link Aladin#setBaseImageLayer|Aladin.setBaseImageLayer} with the url/{@link ImageHiPS}/{@link ImageFITS} given
*
* @memberof Aladin
* @param {string|ImageSurvey|ImageFITS} urlOrHiPSOrFITS - Can be:
* @param {string|ImageHiPS|ImageFITS} urlOrHiPSOrFITS - Can be:
* <ul>
* <li>1. An url that refers to a HiPS.</li>
* <li>2. Or it can be a CDS identifier that refers to a HiPS. One can found the list of IDs {@link https://aladin.cds.unistra.fr/hips/list| here}</li>
* <li>3. A {@link ImageSurvey} HiPS object created from {@link A.imageHiPS}</li>
* <li>3. A {@link ImageHiPS} HiPS object created from {@link A.imageHiPS}</li>
* <li>4. A {@link ImageFITS} FITS image object</li>
* </ul>
*/
@@ -1557,14 +1556,14 @@ export let Aladin = (function () {
/**
* Change the base layer of the view
*
* It internally calls {@link Aladin#setBaseImageLayer|Aladin.setBaseImageLayer} with the url/{@link ImageSurvey}/{@link ImageFITS} given
* It internally calls {@link Aladin#setBaseImageLayer|Aladin.setBaseImageLayer} with the url/{@link ImageHiPS}/{@link ImageFITS} given
*
* @memberof Aladin
* @param {string|ImageSurvey|ImageFITS} urlOrHiPSOrFITS - Can be:
* @param {string|ImageHiPS|ImageFITS} urlOrHiPSOrFITS - Can be:
* <ul>
* <li>1. An url that refers to a HiPS.</li>
* <li>2. Or it can be a CDS ID that refers to a HiPS. One can found the list of IDs {@link https://aladin.cds.unistra.fr/hips/list| here}</li>
* <li>3. A {@link ImageSurvey} HiPS object created from {@link A.imageHiPS}</li>
* <li>3. A {@link ImageHiPS} HiPS object created from {@link A.imageHiPS}</li>
* <li>4. A {@link ImageFITS} FITS image object</li>
* </ul>
*/
@@ -1598,11 +1597,11 @@ export let Aladin = (function () {
* Change the base layer of the view
*
* @memberof Aladin
* @param {string|ImageSurvey|ImageFITS} urlOrHiPSOrFITS - Can be:
* @param {string|ImageHiPS|ImageFITS} urlOrHiPSOrFITS - Can be:
* <ul>
* <li>1. An url that refers to a HiPS.</li>
* <li>2. Or it can be a CDS ID that refers to a HiPS. One can found the list of IDs {@link https://aladin.cds.unistra.fr/hips/list| here}</li>
* <li>3. A {@link ImageSurvey} HiPS object created from {@link A.imageHiPS}</li>
* <li>3. A {@link ImageHiPS} HiPS object created from {@link A.imageHiPS}</li>
* <li>4. A {@link ImageFITS} FITS image object</li>
* </ul>
*/
@@ -1619,11 +1618,11 @@ export let Aladin = (function () {
* Add a new HiPS/FITS image layer in the view
*
* @memberof Aladin
* @param {string|ImageSurvey|ImageFITS} urlOrHiPSOrFITS - Can be:
* @param {string|ImageHiPS|ImageFITS} urlOrHiPSOrFITS - Can be:
* <ul>
* <li>1. An url that refers to a HiPS.</li>
* <li>2. Or it can be a CDS ID that refers to a HiPS. One can found the list of IDs {@link https://aladin.cds.unistra.fr/hips/list| here}</li>
* <li>3. A {@link ImageSurvey} HiPS object created from {@link A.imageHiPS}</li>
* <li>3. A {@link ImageHiPS} HiPS object created from {@link A.imageHiPS}</li>
* <li>4. A {@link ImageFITS} FITS image object</li>
* </ul>
* @param {string} [layer="overlay"] - A layer name. By default 'overlay' is chosen and it is destined to be plot

252
src/js/DefaultHiPSCache.js Normal file
View File

@@ -0,0 +1,252 @@
// Copyright 2013 - UDS/CNRS
// The Aladin Lite program is distributed under the terms
// of the GNU General Public License version 3.
//
// This file is part of Aladin Lite.
//
// Aladin Lite is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, version 3 of the License.
//
// Aladin Lite is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// The GNU General Public License is available in COPYING file
// along with Aladin Lite.
//
/******************************************************************************
* Aladin Lite project
*
* File ImageHiPS
*
* Authors: Thomas Boch & Matthieu Baumann [CDS]
*
*****************************************************************************/
import { ALEvent } from "./events/ALEvent.js";
export let HiPSCache = (function () {
function HiPSCache() {}
HiPSCache.append = function (key, image) {
HiPSCache.cache[key] = image;
ALEvent.HIPS_LIST_UPDATED.dispatchedTo(document.body);
};
HiPSCache.delete = function (key) {
delete HiPSCache.cache[key];
ALEvent.HIPS_LIST_UPDATED.dispatchedTo(document.body);
};
HiPSCache.get = function (key) {
return HiPSCache.cache[key];
};
// A cache storing directly surveys important information to not query for the properties each time
HiPSCache.cache = {};
HiPSCache.DEFAULT_HIPS_LIST = [
{
creatorDid: "ivo://CDS/P/DSS2/color",
name: "DSS colored",
id: "CDS/P/DSS2/color",
maxOrder: 9,
tileSize: 512,
imgFormat: "jpeg",
cooFrame: "ICRS",
},
{
creatorDid: "ivo://CDS/P/2MASS/color",
name: "2MASS colored",
id: "CDS/P/2MASS/color",
maxOrder: 9,
tileSize: 512,
imgFormat: "jpeg",
cooFrame: "ICRS",
},
{
creatorDid: "ivo://CDS/P/DSS2/red",
name: "DSS2 Red (F+R)",
id: "CDS/P/DSS2/red",
maxOrder: 9,
tileSize: 512,
imgFormat: "fits",
cooFrame: "ICRS",
numBitsPerPixel: 16,
// options
minCut: 1000.0,
maxCut: 10000.0,
colormap: "magma",
stretch: "Linear",
imgFormat: "fits",
},
{
creatorDid: "ivo://CDS/P/DM/I/350/gaiaedr3",
name: "Density map for Gaia EDR3 (I/350/gaiaedr3)",
id: "CDS/P/DM/I/350/gaiaedr3",
maxOrder: 7,
tileSize: 512,
numBitsPerPixel: -32,
cooFrame: "ICRS",
minCut: 0,
maxCut: 12000,
stretch: "asinh",
colormap: "rdylbu",
imgFormat: "fits",
},
{
creatorDid: "ivo://CDS/P/PanSTARRS/DR1/g",
name: "PanSTARRS DR1 g",
id: "CDS/P/PanSTARRS/DR1/g",
maxOrder: 11,
tileSize: 512,
imgFormat: "fits",
cooFrame: "ICRS",
numBitsPerPixel: -32,
// options
minCut: -34,
maxCut: 7000,
stretch: "asinh",
colormap: "redtemperature",
},
{
creatorDid: "ivo://CDS/P/PanSTARRS/DR1/color-z-zg-g",
name: "PanSTARRS DR1 color",
id: "CDS/P/PanSTARRS/DR1/color-z-zg-g",
maxOrder: 11,
tileSize: 512,
imgFormat: "jpeg",
cooFrame: "ICRS",
},
{
creatorDid: "ivo://CDS/P/DECaPS/DR2/color",
name: "DECaPS DR2 color",
id: "CDS/P/DECaPS/DR2/color",
maxOrder: 11,
cooFrame: "equatorial",
tileSize: 512,
imgFormat: "png",
},
{
creatorDid: "ivo://CDS/P/Fermi/color",
name: "Fermi color",
id: "CDS/P/Fermi/color",
maxOrder: 3,
imgFormat: "jpeg",
tileSize: 512,
cooFrame: "equatorial",
},
{
creatorDid: "ivo://CDS/P/GALEXGR6_7/NUV",
id: "P/GALEXGR6_7/NUV",
name: "GALEXGR6_7 NUV",
maxOrder: 8,
imgFormat: "png",
tileSize: 512,
cooFrame: "equatorial",
},
{
creatorDid: "ivo://CDS/P/IRIS/color",
id: "CDS/P/IRIS/color",
name: "IRIS colored",
maxOrder: 3,
tileSize: 256,
imgFormat: "jpeg",
cooFrame: "galactic",
},
{
creatorDid: "ivo://CDS/P/Mellinger/color",
id: "CDS/P/Mellinger/color",
name: "Mellinger colored",
maxOrder: 4,
tileSize: 512,
imgFormat: "jpeg",
cooFrame: "galactic",
},
{
creatorDid: "ivo://CDS/P/SDSS9/color",
id: "CDS/P/SDSS9/color",
name: "SDSS9 colored",
maxOrder: 10,
tileSize: 512,
imgFormat: "jpeg",
cooFrame: "equatorial",
},
{
creatorDid: "ivo://CDS/P/SPITZER/color",
id: "CDS/P/SPITZER/color",
name: "IRAC color I1,I2,I4 - (GLIMPSE, SAGE, SAGE-SMC, SINGS)",
maxOrder: 9,
tileSize: 512,
imgFormat: "jpeg",
cooFrame: "galactic",
},
{
creatorDid: "ivo://CDS/P/allWISE/color",
id: "CDS/P/allWISE/color",
name: "AllWISE color",
maxOrder: 8,
tileSize: 512,
imgFormat: "jpeg",
cooFrame: "equatorial",
},
{
creatorDid: "ivo://CDS/P/SDSS9/g",
id: "CDS/P/SDSS9/g",
name: "SDSS9 band-g",
maxOrder: 10,
tileSize: 512,
numBitsPerPixel: 16,
imgFormat: "fits",
cooFrame: "equatorial",
minCut: 0,
maxCut: 1.8,
stretch: "linear",
colormap: "redtemperature",
},
{
id: "CDS/P/Finkbeiner",
name: "Halpha",
maxOrder: 3,
minCut: -10,
maxCut: 800,
colormap: "rdbu",
imgFormat: "fits",
},
{
id: "CDS/P/VTSS/Ha",
name: "VTSS-Ha",
maxOrder: 3,
minCut: -10.0,
maxCut: 100.0,
colormap: "grayscale",
imgFormat: "fits",
},
{
id: "xcatdb/P/XMM/PN/color",
name: "XMM PN colored",
maxOrder: 7,
},
{
id: "CDS/P/allWISE/color",
name: "AllWISE color",
maxOrder: 8,
},
/*{
id: "CDS/P/GLIMPSE360",
name: "GLIMPSE360",
// This domain is not giving the CORS headers
// We need to query by with a proxy equipped with CORS header.
//url: "https://alasky.cds.unistra.fr/cgi/JSONProxy?url=https://www.spitzer.caltech.edu/glimpse360/aladin/data",
maxOrder: 9,
imgFormat: "jpeg",
minOrder: 3,
}*/
];
return HiPSCache;
})();

View File

@@ -1,130 +0,0 @@
import A from "./A.js";
export let DiscoveryTree = (function () {
// Constructor
var DiscoveryTree = function (aladin) {
// activate Vue on the <div> that contains the component
new Vue({
el: '#ui',
methods: {
// Define the methods for the discovery-tree component
// to interact with the aladin viewer
getFovCorners() {
return aladin.getFovCorners();
},
getCenter() {
return aladin.getRaDec();
},
// Called when the user add a image survey
addImage(metadata) {
const order = (+metadata.hips_order);
const hipsTileFormat = metadata.hips_tile_format.split(' ');
let tileFormat;
let color;
if (hipsTileFormat.indexOf('fits') >= 0) {
tileFormat = {
FITSImage: {
bitpix: parseInt(metadata.hips_pixel_bitpix)
}
};
color = {
Grayscale2Color: {
color: [1.0, 1.0, 1.0],
k: 1.0,
transfer: "asinh"
}
};
} else {
color = "Color";
if (hipsTileFormat.indexOf('png') >= 0) {
tileFormat = {
Image: {
format: "png"
}
};
} else {
tileFormat = {
Image: {
format: "jpeg"
}
};
}
}
let cuts = [undefined, undefined];
if (metadata.hips_pixel_cut) {
cuts = metadata.hips_pixel_cut.split(" ");
}
let tileSize = 512;
// Verify the validity of the tile width
if (metadata.hips_tile_width) {
let hipsTileWidth = parseInt(metadata.hips_tile_width);
let isPowerOfTwo = hipsTileWidth && !(hipsTileWidth & (hipsTileWidth - 1));
if (isPowerOfTwo === true) {
tileSize = hipsTileWidth;
}
}
let url = metadata.hips_service_url;
if (url.startsWith('http://alasky')) {
// From alasky one can directly use the https access
url = url.replace('http', 'https');
} else {
// Pass by a proxy for extern http urls
url = 'https://alasky.u-strasbg.fr/cgi/JSONProxy?url=' + url;
}
let survey = {
properties: {
url: url,
maxOrder: parseInt(metadata.hips_order),
frame: {
label: "J2000",
system: "J2000"
},
tileSize: tileSize,
format: tileFormat,
minCutout: parseFloat(cuts[0]),
maxCutout: parseFloat(cuts[1]),
},
color: color
};
aladin.setImageSurveysLayer([survey], "base");
},
// Called when the user add a catalog survey
addCatalog(metadata, center, radius) {
if (metadata.hips_service_url) {
const hips = A.catalogHiPS(metadata.hips_service_url, {
onClick: 'showTable',
name: metadata.ID,
});
aladin.addCatalog(hips);
} else {
console.log(metadata.obs_id, "center, ", center, " radius, ", radius)
const catalog = A.catalogFromVizieR(
metadata.obs_id,
{
ra: center[0],
dec: center[1]
},
radius, {
onClick: 'showTable',
limit: 1000,
}
);
aladin.addCatalog(catalog);
}
},
// Called when the user add a HEALPix coverage
addCoverage(metadata) {
const moc = A.MOCFromURL(metadata.moc_access_url);
aladin.addMOC(moc);
},
},
});
}
return DiscoveryTree;
})();

View File

@@ -17,25 +17,50 @@
// along with Aladin Lite.
//
/******************************************************************************
* Aladin Lite project
*
*
* File ImageFITS
*
*
* Authors: Matthieu Baumann [CDS]
*
*
*****************************************************************************/
import { ALEvent } from "./events/ALEvent.js";
import { ColorCfg } from "./ColorCfg.js";
import { Utils } from "./Utils";
import { ImageSurvey } from "./ImageSurvey.js";
import { HiPSCache } from "./DefaultHiPSCache";
/**
* @typedef {Object} ImageFITSOptions
*
* @property {string} [name] - A human-readable name for the FITS image
* @property {Function} [successCallback] - A callback executed when the FITS has been loaded
* @property {Function} [errorCallback] - A callback executed when the FITS could not be loaded
* @property {number} [opacity=1.0] - Opacity of the survey or image (value between 0 and 1).
* @property {string} [colormap="native"] - The colormap configuration for the survey or image.
* @property {string} [stretch="linear"] - The stretch configuration for the survey or image.
* @property {boolean} [reversed=false] - If true, the colormap is reversed; otherwise, it is not reversed.
* @property {number} [minCut] - The minimum cut value for the color configuration. If not given, 0.0 for JPEG/PNG surveys, the value of the property file for FITS surveys
* @property {number} [maxCut] - The maximum cut value for the color configuration. If not given, 1.0 for JPEG/PNG surveys, the value of the property file for FITS surveys
* @property {boolean} [additive=false] - If true, additive blending is applied; otherwise, it is not applied.
* @property {number} [gamma=1.0] - The gamma correction value for the color configuration.
* @property {number} [saturation=0.0] - The saturation value for the color configuration.
* @property {number} [brightness=0.0] - The brightness value for the color configuration.
* @property {number} [contrast=0.0] - The contrast value for the color configuration.
*/
export let ImageFITS = (function () {
function ImageFITS(url, name, options, successCallback = undefined, errorCallback = undefined) {
/**
* The object describing a FITS image
*
* @class
* @constructs ImageFITS
*
* @param {string} url - Mandatory unique identifier for the layer. Can be an arbitrary name
* @param {ImageFITSOptions} [options] - The option for the survey
*
*/
function ImageFITS(url, options) {
// Name of the layer
this.layer = null;
this.added = false;
@@ -43,13 +68,13 @@ export let ImageFITS = (function () {
// Set it to a default value
this.url = url;
this.id = url;
this.name = name || this.url;
this.name = (options && options.name) || this.url;
this.imgFormat = "fits";
this.formats = ["fits"]
this.formats = ["fits"];
// callbacks
this.successCallback = successCallback;
this.errorCallback = errorCallback;
this.successCallback = options && options.successCallback;
this.errorCallback = options && options.errorCallback;
// initialize the color meta data here
// set a asinh stretch by default if there is none
/*if (options) {
@@ -60,37 +85,27 @@ export let ImageFITS = (function () {
let self = this;
this.query = Promise.resolve(self);
this._saveInCache();
}
ImageFITS.prototype._saveInCache = function() {
let self = this;
let colorOpt = Object.fromEntries(Object.entries(this.colorCfg));
let fitsOpt = {
id: self.id,
name: self.name,
url: self.url,
imgFormat: self.imgFormat,
...colorOpt
}
ImageSurvey.cache[self.id] = self;
//console.log('new CACHE', ImageSurvey.cache, self.id, surveyOpt, ImageSurvey.cache[self.id], ImageSurvey.cache["CSIRO/P/RACS/mid/I"])
ImageFITS.prototype._saveInCache = function () {
/*ImageHiPS.cache[this.id] = this;
// Tell that the HiPS List has been updated
if (this.view) {
ALEvent.HIPS_LIST_UPDATED.dispatchedTo(this.view.aladin.aladinDiv);
}
}
}*/
HiPSCache.append(this.id, this);
};
// A cache storing directly the images to not query for the properties each time
//ImageFITS.cache = {};
ImageFITS.prototype.setView = function(view) {
ImageFITS.prototype.setView = function (view) {
this.view = view;
}
this._saveInCache();
};
// @api
ImageFITS.prototype.setOpacity = function (opacity) {
@@ -112,7 +127,7 @@ export let ImageFITS = (function () {
this._updateMetadata(() => {
this.colorCfg.setColormap(colormap, options);
});
}
};
// @api
ImageFITS.prototype.setCuts = function (lowCut, highCut) {
@@ -159,95 +174,105 @@ export let ImageFITS = (function () {
this.view.wasm.setImageMetadata(this.layer, {
...this.colorCfg.get(),
longitudeReversed: false,
imgFormat: this.imgFormat
imgFormat: this.imgFormat,
});
ALEvent.HIPS_LAYER_CHANGED.dispatchedTo(this.view.aladinDiv, {
layer: this,
});
ALEvent.HIPS_LAYER_CHANGED.dispatchedTo(this.view.aladinDiv, { layer: this });
}
// save it in the JS HiPS cache
this._saveInCache()
this._saveInCache();
} catch (e) {
// Display the error message
console.error(e);
}
}
};
ImageFITS.prototype.add = function (layer) {
this.layer = layer;
let self = this;
const promise = self.view.wasm.addImageFITS({
layer: self.layer,
url: self.url,
meta: {
...this.colorCfg.get(),
longitudeReversed: false,
imgFormat: this.imgFormat
}
}).then((imagesParams) => {
// There is at least one entry in imageParams
self.added = true;
const promise = self.view.wasm
.addImageFITS({
layer: self.layer,
url: self.url,
meta: {
...this.colorCfg.get(),
longitudeReversed: false,
imgFormat: this.imgFormat,
},
})
.then((imagesParams) => {
// There is at least one entry in imageParams
self.added = true;
self.children = [];
self.children = [];
let hduIdx = 0;
imagesParams.forEach((imageParams) => {
// This fits has HDU extensions
let image = new ImageFITS(
imageParams.url,
self.name + "_ext_" + hduIdx.toString(),
null,
null,
null
);
let hduIdx = 0;
imagesParams.forEach((imageParams) => {
// This fits has HDU extensions
let image = new ImageFITS(imageParams.url, {
name: self.name + "_ext_" + hduIdx.toString(),
});
// Set the layer corresponding to the onein the backend
image.layer = imageParams.layer;
image.added = true;
image.setView(self.view);
// deep copy of the color object of self
image.colorCfg = Utils.deepCopy(self.colorCfg);
// Set the automatic computed cuts
image.setCuts(imageParams.automatic_min_cut, imageParams.automatic_max_cut);
// Set the layer corresponding to the onein the backend
image.layer = imageParams.layer;
image.added = true;
image.setView(self.view);
// deep copy of the color object of self
image.colorCfg = Utils.deepCopy(self.colorCfg);
// Set the automatic computed cuts
image.setCuts(
imageParams.automatic_min_cut,
imageParams.automatic_max_cut
);
image.ra = imageParams.centered_fov.ra;
image.dec = imageParams.centered_fov.dec;
image.fov = imageParams.centered_fov.fov;
image.ra = imageParams.centered_fov.ra;
image.dec = imageParams.centered_fov.dec;
image.fov = imageParams.centered_fov.fov;
if (!self.ra) { self.ra = image.ra; }
if (!self.dec) { self.dec = image.dec; }
if (!self.fov) { self.fov = image.fov; }
if (!self.ra) {
self.ra = image.ra;
}
if (!self.dec) {
self.dec = image.dec;
}
if (!self.fov) {
self.fov = image.fov;
}
self.children.push(image)
self.children.push(image);
hduIdx += 1;
hduIdx += 1;
});
// Call the success callback on the first HDU image parsed
if (self.successCallback) {
self.successCallback(
self.children[0].ra,
self.children[0].dec,
self.children[0].fov,
self.children[0]
);
}
return self;
})
.catch((e) => {
if (self.errorCallback) {
self.errorCallback();
}
// This error result from a promise
// If I throw it, it will not be catched because
// it is run async
self.view.removeImageLayer(layer);
return Promise.reject(e);
});
// Call the success callback on the first HDU image parsed
if (self.successCallback) {
self.successCallback(
self.children[0].ra,
self.children[0].dec,
self.children[0].fov,
self.children[0]
);
}
return self;
}).catch((e) => {
if (self.errorCallback) {
self.errorCallback()
}
// This error result from a promise
// If I throw it, it will not be catched because
// it is run async
self.view.removeImageLayer(layer)
return Promise.reject(e);
});
return promise;
};
@@ -261,9 +286,9 @@ export let ImageFITS = (function () {
};
// FITS images does not mean to be used for storing planetary data
ImageFITS.prototype.isPlanetaryBody = function() {
ImageFITS.prototype.isPlanetaryBody = function () {
return false;
}
};
// @api
ImageFITS.prototype.focusOn = function () {
@@ -307,4 +332,3 @@ export let ImageFITS = (function () {
return ImageFITS;
})();

File diff suppressed because it is too large Load Diff

View File

@@ -50,8 +50,7 @@ import { ObsCore } from "./vo/ObsCore.js";
import { DefaultActionsForContextMenu } from "./DefaultActionsForContextMenu.js";
import { Layout } from "./gui/Layout.js";
import { SAMPActionButton } from "./gui/Button/SAMP.js";
import { ImageSurvey } from "./ImageSurvey.js";
import { ImageFITS } from "./ImageFITS.js";
import { HiPSCache } from "./DefaultHiPSCache.js";
export let View = (function () {
@@ -1613,7 +1612,7 @@ export let View = (function () {
Promise.allSettled(this.promises)
.then(() => imageLayerPromise)
// The promise is resolved and we now have access
// to the image layer objet (whether it is an ImageSurvey or an ImageFITS)
// to the image layer objet (whether it is an ImageHiPS or an ImageFITS)
.then((imageLayer) => {
// Add to the backend
const promise = imageLayer.add(layer);
@@ -1642,8 +1641,7 @@ export let View = (function () {
})
.catch((e) => {
// remove it from the cache
delete ImageSurvey.cache[imageLayer.id]
ALEvent.HIPS_LIST_UPDATED.dispatchedTo(this.aladin.aladinDiv);
HiPSCache.delete(imageLayer.id)
throw e;
})
@@ -1662,7 +1660,7 @@ export let View = (function () {
if (noMoreLayersToWaitFor) {
if (self.empty) {
// no promises to launch!
//self.aladin.setBaseImageLayer(self.aladin.createImageSurvey(ImageSurvey.DEFAULT_SURVEY_ID));
//self.aladin.setBaseImageLayer(self.aladin.createImageSurvey(ImageHiPS.DEFAULT_SURVEY_ID));
} else {
// there is surveys that have been queried
// rename the first overlay layer to "base"

View File

@@ -47,11 +47,12 @@ import filterOffUrl from "../../../../assets/icons/filter-off.svg";
import searchIconImg from "../../../../assets/icons/search.svg";
import { TogglerActionButton } from "../Button/Toggler.js";
import { Icon } from "../Widgets/Icon.js";
import { ImageSurvey } from "../../ImageSurvey.js";
import { ImageHiPS } from "../../ImageHiPS.js";
import { Box } from "../Widgets/Box.js";
import { CtxMenuActionButtonOpener } from "../Button/CtxMenuOpener.js";
import { Input } from "../Widgets/Input.js";
import { ImageFITS } from "../../ImageFITS.js";
import { HiPSCache } from "../../DefaultHiPSCache.js";
export class OverlayStackBox extends Box {
/*static previewImagesUrl = {
@@ -783,11 +784,12 @@ export class OverlayStackBox extends Box {
updateOverlayList();
// Add a listener for HiPS list changes
ALEvent.HIPS_LIST_UPDATED.listenedBy(this.aladin.aladinDiv, () => {
ALEvent.HIPS_LIST_UPDATED.listenedBy(document.body, () => {
self.cachedHiPS = {};
for (var key in ImageSurvey.cache) {
let HiPS = ImageSurvey.cache[key];
for (var key in HiPSCache.cache) {
let HiPS = HiPSCache.cache[key];
self.cachedHiPS[HiPS.name] = HiPS;
}
@@ -805,8 +807,8 @@ export class OverlayStackBox extends Box {
/*ALEvent.HIPS_LIST_UPDATED.listenedBy(this.aladin.aladinDiv, () => {
// Recompute the autocompletion as the cache has changed
HiPSSearch.HiPSList = {};
for (var key in ImageSurvey.cache) {
let HiPS = ImageSurvey.cache[key];
for (var key in ImageHiPS.cache) {
let HiPS = ImageHiPS.cache[key];
// apply filtering
if (

View File

@@ -19,7 +19,6 @@
import { CtxMenuActionButtonOpener } from "./CtxMenuOpener";
import stackOverlayIconUrl from './../../../../assets/icons/stack.svg';
import { OverlayStack } from "../CtxMenu/OverlayStack";
import { OverlayStackBox } from "../Box/StackBox";
import { TogglerActionButton } from "./Toggler";
/******************************************************************************

View File

@@ -1,817 +0,0 @@
// Copyright 2013 - UDS/CNRS
// The Aladin Lite program is distributed under the terms
// of the GNU General Public License version 3.
//
// This file is part of Aladin Lite.
//
// Aladin Lite is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, version 3 of the License.
//
// Aladin Lite is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// The GNU General Public License is available in COPYING file
// along with Aladin Lite.
//
/******************************************************************************
* Aladin Lite project
*
* File gui/Stack/Menu.js
*
*
* Author: Matthieu Baumann [CDS, matthieu.baumann@astro.unistra.fr]
*
*****************************************************************************/
import { CatalogQueryBox } from "../Box/CatalogQueryBox.js";
import { ALEvent } from "../../events/ALEvent.js";
import { Layout } from "../Layout.js";
import { ContextMenu } from "../Widgets/ContextMenu.js";
import { ActionButton } from "../Widgets/ActionButton.js";
import A from "../../A.js";
import { Utils } from "../../../js/Utils";
import { View } from "../../View.js";
import { HiPSSettingsBox } from "../Box/HiPSSettingsBox.js";
import { HiPSSelectorBox } from "../Box/HiPSSelectorBox.js";
import searchIconUrl from '../../../../assets/icons/search.svg';
import showIconUrl from '../../../../assets/icons/show.svg';
import hideIconUrl from '../../../../assets/icons/hide.svg';
import removeIconUrl from '../../../../assets/icons/remove.svg';
import settingsIconUrl from '../../../../assets/icons/settings.svg';
import { ImageFITS } from "../../ImageFITS.js";
import searchIconImg from '../../../../assets/icons/search.svg';
import { TogglerActionButton } from "../Button/Toggler.js";
import { Icon } from "../Widgets/Icon.js";
import { ImageSurvey } from "../../ImageSurvey.js";
export class OverlayStack extends ContextMenu {
static previewImagesUrl = {
'AllWISE color': 'https://aladin.cds.unistra.fr/AladinLite/survey-previews/P_allWISE_color.jpg',
'DSS colored': 'https://aladin.cds.unistra.fr/AladinLite/survey-previews/P_DSS2_color.jpg',
'DSS2 Red (F+R)': 'https://aladin.cds.unistra.fr/AladinLite/survey-previews/P_DSS2_red.jpg',
'Fermi color': 'https://aladin.cds.unistra.fr/AladinLite/survey-previews/P_Fermi_color.jpg',
'GALEXGR6_7 NUV': 'https://aladin.cds.unistra.fr/AladinLite/survey-previews/P_GALEXGR6_7_color.jpg',
'GLIMPSE360': 'https://aladin.cds.unistra.fr/AladinLite/survey-previews/P_GLIMPSE360.jpg',
'Halpha': 'https://aladin.cds.unistra.fr/AladinLite/survey-previews/P_VTSS_Ha.jpg',
'IRAC color I1,I2,I4 - (GLIMPSE, SAGE, SAGE-SMC, SINGS)': 'https://aladin.cds.unistra.fr/AladinLite/survey-previews/P_SPITZER_color.jpg',
'IRIS colored': 'https://aladin.cds.unistra.fr/AladinLite/survey-previews/P_IRIS_color.jpg',
'Mellinger colored': 'https://aladin.cds.unistra.fr/AladinLite/survey-previews/P_Mellinger_color.jpg',
'PanSTARRS DR1 color': 'https://aladin.cds.unistra.fr/AladinLite/survey-previews/P_PanSTARRS_DR1_color-z-zg-g.jpg',
'2MASS colored': 'https://aladin.cds.unistra.fr/AladinLite/survey-previews/P_2MASS_color.jpg',
'AKARI colored': 'https://aladin.cds.unistra.fr/AladinLite/survey-previews/P_AKARI_FIS_Color.jpg',
'SWIFT': 'https://aladin.cds.unistra.fr/AladinLite/survey-previews/P_SWIFT_BAT_FLUX.jpg',
'VTSS-Ha': 'https://aladin.cds.unistra.fr/AladinLite/survey-previews/P_Finkbeiner.jpg',
'XMM PN colored': 'https://aladin.cds.unistra.fr/AladinLite/survey-previews/P_XMM_PN_color.jpg',
'SDSS9 colored': 'https://aladin.cds.unistra.fr/AladinLite/survey-previews/P_SDSS9_color.jpg',
};
static predefinedCats = {
simbad: {url: 'https://axel.u-strasbg.fr/HiPSCatService/SIMBAD', options: {id: 'simbad', name: 'SIMBAD', shape: 'circle', sourceSize: 8, color: '#318d80', onClick: 'showTable'}},
gaia: {url: 'https://axel.u-strasbg.fr/HiPSCatService/I/355/gaiadr3', options: {id: 'gaia-dr3', name: 'Gaia DR3', shape: 'square', sourceSize: 8, color: '#6baed6', onClick: 'showTable'}},
twomass: {url: 'https://axel.u-strasbg.fr/HiPSCatService/II/246/out', options: {id: '2mass', name: '2MASS', shape: 'plus', sourceSize: 8, color: '#dd2233', onClick: 'showTable'}}
};
// Constructor
constructor(aladin) {
let self;
super(aladin, {hideOnClick: (e) => {
// only hide the stack ctx menu but not the windows
super._hide();
}});
self = this;
this.aladin = aladin;
this.mode = 'stack';
this._addListeners();
this.mocHiPSUrls = {}
}
_addListeners() {
let self = this;
let updateOverlayList = () => {
// If it is shown, update it
if (!self.isHidden) {
// show will update the content of the stack
self.attach();
self.show();
}
};
ALEvent.GRAPHIC_OVERLAY_LAYER_ADDED.listenedBy(this.aladin.aladinDiv, function (e) {
updateOverlayList();
});
ALEvent.GRAPHIC_OVERLAY_LAYER_REMOVED.listenedBy(this.aladin.aladinDiv, function (e) {
updateOverlayList();
});
ALEvent.GRAPHIC_OVERLAY_LAYER_CHANGED.listenedBy(this.aladin.aladinDiv, function (e) {
updateOverlayList();
});
ALEvent.HIPS_LAYER_ADDED.listenedBy(this.aladin.aladinDiv, function (e) {
updateOverlayList();
});
ALEvent.HIPS_LAYER_RENAMED.listenedBy(this.aladin.aladinDiv, function (e) {
updateOverlayList();
});
ALEvent.HIPS_LAYER_SWAP.listenedBy(this.aladin.aladinDiv, function (e) {
updateOverlayList();
});
ALEvent.HIPS_LAYER_REMOVED.listenedBy(this.aladin.aladinDiv, function (e) {
updateOverlayList();
});
updateOverlayList();
}
attach() {
let self = this;
const overlays = Array.from(this.aladin.getOverlays()).reverse().map((overlay) => {
return overlay;
});
const layers = Array.from(self.aladin.getImageOverlays()).reverse().map((name) => {
let overlay = self.aladin.getOverlayImageLayer(name);
return overlay;
});
let layout = [{
label: 'Add overlay',
subMenu: [
{
label: 'Catalogue',
subMenu: [
{
label: {
icon: {
url: 'https://aladin.cds.unistra.fr/AladinLite/logos/SIMBAD.svg',
cssStyle: {
width: '3rem',
height: '3rem',
cursor: 'help',
},
action(o) {
window.open('https://simbad.cds.unistra.fr/simbad/')
}
},
content: 'database',
tooltip: {content: 'Click to go to the SIMBAD database', position: {direction: 'bottom'}},
},
action(o) {
o.stopPropagation();
o.preventDefault();
self._hide();
const simbadHiPS = A.catalogHiPS(OverlayStack.predefinedCats.simbad.url, OverlayStack.predefinedCats.simbad.options);
self.aladin.addCatalog(simbadHiPS);
self.mode = 'stack';
}
},
{
label: 'Gaia DR3',
action(o) {
o.stopPropagation();
o.preventDefault();
self._hide();
const simbadHiPS = A.catalogHiPS(OverlayStack.predefinedCats.gaia.url, OverlayStack.predefinedCats.gaia.options);
self.aladin.addCatalog(simbadHiPS);
self.mode = 'stack';
}
},
{
label: '2MASS',
action(o) {
o.stopPropagation();
o.preventDefault();
self._hide();
const simbadHiPS = A.catalogHiPS(OverlayStack.predefinedCats.twomass.url, OverlayStack.predefinedCats.twomass.options);
self.aladin.addCatalog(simbadHiPS);
self.mode = 'stack';
}
},
ContextMenu.fileLoaderItem({
label: 'From a VOTable File',
accept: '.xml,.vot',
action(file) {
let url = URL.createObjectURL(file);
A.catalogFromURL(
url,
{onClick: 'showTable'},
(catalog) => {
self.aladin.addCatalog(catalog)
},
e => alert(e)
);
}
}),
{
label: {
icon: {
url: searchIconImg,
monochrome: true,
tooltip: {content: 'Find a specific catalogue <br /> in our database...', position: { direction: 'top' }},
cssStyle: {
cursor: 'help',
},
},
content: 'More...'
},
action(o) {
o.stopPropagation();
o.preventDefault();
self._hide();
self.catBox = new CatalogQueryBox(self.aladin);
self.catBox._show({position: self.position});
self.mode = 'search';
}
},
]
},
{
label: {
icon: {
url: Icon.dataURLFromSVG({svg: Icon.SVG_ICONS.MOC}),
size: 'small',
tooltip: {content: 'Define a selection coverage', position: {direction: 'bottom'}},
monochrome: true,
cssStyle: {
cursor: 'pointer',
},
},
content: 'MOC'
},
subMenu: [
ContextMenu.fileLoaderItem({
label: 'FITS File',
accept: '.fits',
action(file) {
let url = URL.createObjectURL(file);
let moc = A.MOCFromURL(
url,
{name: file.name, lineWidth: 3.0},
);
self.aladin.addMOC(moc)
}
}),
{
label: 'From selection',
subMenu: [
{
label: '◌ Circle',
disabled: self.aladin.view.mode !== View.PAN ? {
reason: 'Exit your current mode<br/>(e.g. disable the SIMBAD pointer mode)'
} : false,
action(o) {
o.preventDefault();
o.stopPropagation();
self._hide();
self.aladin.select('circle', c => {
try {
let [ra, dec] = self.aladin.pix2world(c.x, c.y, 'j2000');
let radius = self.aladin.angularDist(c.x, c.y, c.x + c.r, c.y);
// the moc needs a
let moc = A.MOCFromCone(
{ra, dec, radius},
{name: 'cone', lineWidth: 3.0},
);
self.aladin.addMOC(moc)
} catch {
console.error('Circle out of projection. Selection canceled')
}
})
}
},
{
label: '⬚ Rect',
disabled: self.aladin.view.mode !== View.PAN ? {
reason: 'Exit your current mode<br/>(e.g. disable the SIMBAD pointer mode)'
} : false,
action(o) {
o.stopPropagation();
o.preventDefault();
self._hide();
self.aladin.select('rect', r => {
try {
let [ra1, dec1] = self.aladin.pix2world(r.x, r.y, 'j2000');
let [ra2, dec2] = self.aladin.pix2world(r.x + r.w, r.y, 'j2000');
let [ra3, dec3] = self.aladin.pix2world(r.x + r.w, r.y + r.h, 'j2000');
let [ra4, dec4] = self.aladin.pix2world(r.x, r.y + r.h, 'j2000');
let moc = A.MOCFromPolygon(
{
ra: [ra1, ra2, ra3, ra4],
dec: [dec1, dec2, dec3, dec4]
},
{name: 'rect', lineWidth: 3.0},
);
self.aladin.addMOC(moc)
} catch(_) {
alert('Selection covers a region out of the projection definition domain.');
}
})
}
},
{
label: '⛉ Polygon',
disabled: self.aladin.view.mode !== View.PAN ? {
reason: 'Exit your current mode<br/>(e.g. disable the SIMBAD pointer mode)'
} : false,
action(o) {
o.stopPropagation();
o.preventDefault();
self._hide();
self.aladin.select('poly', p => {
try {
let ra = []
let dec = []
for (const v of p.vertices) {
let [lon, lat] = self.aladin.pix2world(v.x, v.y, 'j2000');
ra.push(lon)
dec.push(lat)
}
let moc = A.MOCFromPolygon(
{ra, dec},
{name: 'poly', lineWidth: 3.0},
);
self.aladin.addMOC(moc)
} catch(_) {
alert('Selection covers a region out of the projection definition domain.');
}
})
}
},
]
}
]
}
]
}];
for(const overlay of overlays) {
const name = overlay.name;
let cssStyle = {
height: 'fit-content',
};
let showBtn = new ActionButton({
size: 'small',
icon: {
url: overlay.isShowing ? showIconUrl : hideIconUrl,
monochrome: true,
},
cssStyle: {
visibility: Utils.hasTouchScreen() ? 'visible' : 'hidden',
},
tooltip: {content: overlay.isShowing ? 'Hide' : 'Show', position: {direction: 'bottom'}},
action(e, btn) {
if (overlay.isShowing) {
overlay.hide()
btn.update({icon: {monochrome: true, url: hideIconUrl}, tooltip: {content: 'Show'}});
} else {
overlay.show()
btn.update({icon: {monochrome: true, url: showIconUrl}, tooltip: {content: 'Hide'}});
}
}
});
let deleteBtn = new ActionButton({
icon: {
url: removeIconUrl,
monochrome: true,
},
size: 'small',
cssStyle: {
visibility: Utils.hasTouchScreen() ? 'visible' : 'hidden',
},
tooltip: {
content: 'Remove',
position: {direction: 'bottom'}
},
action(e) {
self.aladin.removeLayer(overlay)
}
});
let item = Layout.horizontal({
layout: [
this._addOverlayIcon(overlay),
'<div style="background-color: rgba(0, 0, 0, 0.6); padding: 3px; border-radius: 3px; word-break: break-word;">' + name + '</div>',
Layout.horizontal({layout: [showBtn, deleteBtn]})
],
cssStyle: {
textAlign: 'center',
}
});
if(!Utils.hasTouchScreen()) {
layout.push({
label: item,
cssStyle,
hover(e) {
showBtn.el.style.visibility = 'visible'
deleteBtn.el.style.visibility = 'visible'
},
unhover(e) {
showBtn.el.style.visibility = 'hidden'
deleteBtn.el.style.visibility = 'hidden'
},
})
} else {
layout.push({
label: item,
cssStyle
})
}
}
// survey list
let selectedLayer = self.aladin.getSelectedLayer();
/*if (!layers) {
super.attach(layout);
return;
}*/
const defaultLayers = Object.entries(ImageSurvey.cache).sort(function (e1, e2) {
let a = e1[1]
let b = e2[1]
if (!a.order) {
return a.name > b.name ? 1 : -1;
}
return a.maxOrder && a.maxOrder > b.maxOrder ? 1 : -1;
});
for(const layer of layers) {
let backgroundUrl = layer.url + '/preview.jpg';
let cssStyle = {
height: 'fit-content',
};
if (backgroundUrl) {
cssStyle = {
backgroundSize: '100%',
backgroundImage: 'url(' + backgroundUrl + ')',
...cssStyle
}
}
let showBtn = ActionButton.createSmallSizedIconBtn({
icon: {
url: layer.getOpacity() === 0.0 ? hideIconUrl : showIconUrl,
monochrome: true,
},
cssStyle: {
visibility: Utils.hasTouchScreen() ? 'visible' : 'hidden',
},
tooltip: {content: layer.getOpacity() === 0.0 ? 'Show' : 'Hide', position: {direction: 'bottom'}},
action(e, btn) {
e.preventDefault();
e.stopPropagation();
let opacity = layer.getOpacity();
if (opacity === 0.0) {
layer.setOpacity(1.0);
btn.update({icon: {monochrome: true, url: showIconUrl}, tooltip: {content: 'Hide'}});
} else {
layer.setOpacity(0.0);
btn.update({icon: {monochrome: true, url: hideIconUrl}, tooltip: {content: 'Show'}});
}
}
});
let deleteBtn = ActionButton.createSmallSizedIconBtn({
icon: {url: removeIconUrl, monochrome: true},
cssStyle: {
visibility: Utils.hasTouchScreen() ? 'visible' : 'hidden',
},
disable: layer.layer === 'base',
tooltip: {content: 'Remove', position: {direction: 'bottom'}},
action(e) {
self.aladin.removeImageLayer(layer.layer);
}
});
let editBtn = ActionButton.createSmallSizedIconBtn({
icon: {url: settingsIconUrl, monochrome: true},
cssStyle: {
visibility: Utils.hasTouchScreen() ? 'visible' : 'hidden',
},
tooltip: {content: 'Settings', position: {direction: 'bottom'}},
action: (e) => {
e.stopPropagation();
e.preventDefault();
self._hide();
//self.aladin.selectLayer(layer.layer);
//self.attach()
self.editBox = new LayerEditBox(self.aladin);
self.editBox.update({layer})
self.editBox._show({position: self.position});
self.mode = 'edit';
}
});
let loadMOCBtn = new TogglerActionButton({
size: 'small',
cssStyle: {
visibility: Utils.hasTouchScreen() ? 'visible' : 'hidden',
},
icon: {url: Icon.dataURLFromSVG({svg: Icon.SVG_ICONS.MOC}), monochrome: true},
tooltip: {content: 'Add coverage', position: {direction: 'bottom'}},
toggled: (() => {
let overlays = self.aladin.getOverlays();
let found = overlays.find((o) => o.type === "moc" && o.name === layer.name);
return found !== undefined;
})(),
actionOn: (e) => {
let moc = A.MOCFromURL(layer.url + '/Moc.fits', {lineWidth: 3, name: layer.name});
self.aladin.addMOC(moc);
self.mocHiPSUrls[layer.url] = moc;
loadMOCBtn.update({tooltip: {content: 'Remove coverage', position: {direction: 'bottom'}}})
if (self.aladin.statusBar) {
self.aladin.statusBar.appendMessage({
message: 'Coverage of ' + layer.name + ' loaded',
duration: 2000,
type: 'info'
})
}
},
actionOff: (e) => {
let moc = self.mocHiPSUrls[layer.url];
self.aladin.removeLayer(moc)
delete self.mocHiPSUrls[layer.url];
loadMOCBtn.update({tooltip: {content: 'Add coverage', position: {direction: 'bottom'}}})
if (self.aladin.statusBar) {
self.aladin.statusBar.appendMessage({
message: 'Coverage of ' + layer.name + ' removed',
duration: 2000,
type: 'info'
})
}
}
});
let layerClassName = 'a' + layer.layer.replace(/[.\/ ]/g, '')
let btns = [showBtn, editBtn];
if (layer.subtype !== 'fits') {
btns.push(loadMOCBtn)
}
btns.push(deleteBtn)
let item = Layout.horizontal({
layout: [
'<div class="' + layerClassName + '" style="background-color: rgba(0, 0, 0, 0.6); line-height: 1rem; padding: 3px; border-radius: 3px; word-break: break-word;' + (selectedLayer === layer.layer ? 'border: 1px solid white;' : '') + '">' + (layer.name) + '</div>',
Layout.horizontal(btns)
],
/*cssStyle: {
display: 'flex',
alignItems: 'center',
listStyle: 'none',
justifyContent: 'space-between',
width: '100%',
}*/
});
let l = {
label: item,
classList: 'surveyItem',
cssStyle,
hover(e) {
showBtn.el.style.visibility = 'visible'
editBtn.el.style.visibility = 'visible'
deleteBtn.el.style.visibility = 'visible'
loadMOCBtn.el.style.visibility = 'visible'
},
unhover(e) {
showBtn.el.style.visibility = 'hidden'
editBtn.el.style.visibility = 'hidden'
deleteBtn.el.style.visibility = 'hidden'
loadMOCBtn.el.style.visibility = 'hidden'
}
};
l.subMenu = [];
l.subMenu.push({
label: {
icon: {
url: searchIconImg,
monochrome: true,
tooltip: {content: 'Find a specific survey <br /> in our database...', position: { direction: 'bottom' }},
cssStyle: {
cursor: 'help',
},
},
content: 'More...'
},
action(o) {
o.stopPropagation();
o.preventDefault();
self._hide();
self.hipsBox = new HiPSSelectorBox(self.aladin)
self.hipsBox.attach((HiPSId) => {
self.aladin.setOverlayImageLayer(HiPSId, layer.layer);
self.show();
});
self.hipsBox._show({
position: self.position,
})
self.mode = 'hips';
}
})
for(const [id, ll] of defaultLayers) {
backgroundUrl = OverlayStack.previewImagesUrl[ll.name];
if (!backgroundUrl) {
backgroundUrl = ll.url + '/preview.jpg'
}
let cssStyle = {
height: '2.5em',
};
if (backgroundUrl) {
cssStyle = {
backgroundSize: '100%',
backgroundImage: 'url(' + backgroundUrl + ')',
...cssStyle
}
}
l.subMenu.push({
//selected: layer.name === aladin.getBaseImageLayer().name,
label: '<div style="background-color: rgba(0, 0, 0, 0.6); line-height: 1rem; padding: 3px; border-radius: 3px">' + ll.name + '</div>',
cssStyle,
action(e) {
self.aladin.setOverlayImageLayer(id, layer.layer);
},
hover(e, item) {
item.style.filter = 'brightness(1.5)';
},
unhover(e, item) {
item.style.filter = 'brightness(1.0)';
}
})
}
l.action = (o) => {
let oldLayerClassName = 'a' + self.aladin.getSelectedLayer().replace(/[.\/ ]/g, '')
self.el.querySelector('.' + oldLayerClassName).style.removeProperty('border')
self.aladin.selectLayer(layer.layer);
self.el.querySelector('.' + layerClassName).style.border = '1px solid white';
}
layout.push(l);
}
super.attach(layout);
}
_findPreviewImageUrl(layer) {
if (layer instanceof ImageFITS) {
return;
}
if (!layer.creatorDid) {
return;
}
const creatorDid = layer.creatorDid;
for (const key in Stack.previewImagesUrl) {
if (creatorDid.includes(key)) {
return Stack.previewImagesUrl[key];
}
}
// if not found
return layer.url + '/preview.jpg'
}
_addOverlayIcon(overlay) {
var tooltipText;
var svg = '';
if (overlay.type == 'catalog' || overlay.type == 'progressivecat') {
var nbSources = overlay.getSources().length;
tooltipText = nbSources + ' source' + (nbSources > 1 ? 's' : '');
svg = Icon.SVG_ICONS.CATALOG;
}
else if (overlay.type == 'moc') {
tooltipText = 'Coverage: ' + (100 * overlay.skyFraction()).toFixed(2) + ' % of sky';
svg = Icon.SVG_ICONS.MOC;
}
else if (overlay.type == 'overlay') {
svg = Icon.SVG_ICONS.OVERLAY;
}
let tooltip;
if (tooltipText) {
tooltip = { content: tooltipText, position: {direction: 'bottom'} }
}
// retrieve SVG icon, and apply the layer color
return new Icon({
size: 'small',
url: Icon.dataURLFromSVG({svg, color: overlay.color}),
tooltip
});
}
show(options) {
if (this.mode !== 'stack') {
if(this.hipsSelectorBox) {
this.hipsSelectorBox.remove()
}
if(this.catBox) {
this.catBox.remove()
}
if(this.hipsBox) {
this.hipsBox.remove()
}
if(this.editBox) {
this.editBox.remove()
}
}
self.mode = 'stack';
this.attach();
this.position = (options && options.position) || this.position || { anchor: 'center center'};
this.position.aladin = this.aladin;
super.show({
...options,
...{position: this.position},
cssStyle: {
maxWidth: '17rem',
}
})
const innerHeight = this.aladin.aladinDiv.offsetHeight;
this.element().querySelectorAll(".surveyItem")
.forEach((surveyItem) => {
surveyItem.querySelectorAll(".aladin-context-sub-menu")
// skip the first menu
.forEach((subMenu) => {
subMenu.style.display = 'block'
let Y = innerHeight - (subMenu.getBoundingClientRect().y - this.aladin.aladinDiv.getBoundingClientRect().y);
subMenu.style.display = 'none'
subMenu.style.maxHeight = Y + 'px';
subMenu.style.overflowY = 'scroll';
})
})
}
}