From 389654ba396a218ec30bc50f3ee074ea8d701016 Mon Sep 17 00:00:00 2001 From: Matthieu Baumann Date: Thu, 14 Mar 2024 19:52:28 +0100 Subject: [PATCH] Refac and fix UI and cooFrame problems, add API doc too --- CHANGELOG.md | 8 + README.md | 1 + examples/al-badbaselayer.html | 4 +- examples/al-coronelli.html | 10 +- examples/al-customize-button.html | 7 +- examples/al-displayFITS.html | 1 + examples/al-easy-access-simbad-ned.html | 2 +- examples/al-hips-order-list.html | 6 +- examples/al-init-custom-options.html | 4 +- examples/al-multiple-surveys.html | 5 +- examples/al-no-properties.html | 27 + src/core/al-api/src/hips.rs | 10 +- src/core/src/renderable/hips/mod.rs | 10 +- src/core/src/survey/config.rs | 18 +- src/css/aladin.css | 31 +- src/js/A.js | 40 ++ src/js/Aladin.js | 215 ++++---- src/js/Catalog.js | 1 - src/js/ColorCfg.js | 26 +- src/js/ColorMap.js | 275 ---------- src/js/FiniteStateMachine/CircleSelect.js | 28 +- src/js/FiniteStateMachine/PolySelect.js | 14 +- src/js/FiniteStateMachine/RectSelect.js | 18 +- src/js/GenericPointer.js | 4 +- src/js/HiPSProperties.js | 3 +- src/js/ImageFITS.js | 84 ++- src/js/ImageLayer.js | 218 -------- src/js/ImageSurvey.js | 625 +++++++++++++++------- src/js/Selector.js | 5 +- src/js/View.js | 63 +-- src/js/gui/Box/CatalogQueryBox.js | 5 +- src/js/gui/Box/ConeSearchBox.js | 8 +- src/js/gui/Button/ConeSearch.js | 4 +- src/js/gui/CtxMenu/OverlayStack.js | 72 +-- src/js/gui/Input/CooFrame.js | 3 +- src/js/gui/Widgets/Input.js | 8 +- src/js/gui/Widgets/Selector.js | 6 +- src/js/gui/Widgets/Toolbar.js | 182 ------- src/js/gui/Widgets/Widget.js | 9 +- 39 files changed, 869 insertions(+), 1191 deletions(-) create mode 100644 examples/al-no-properties.html delete mode 100644 src/js/ColorMap.js delete mode 100644 src/js/ImageLayer.js delete mode 100644 src/js/gui/Widgets/Toolbar.js diff --git a/CHANGELOG.md b/CHANGELOG.md index 49d2b351..f9b50bee 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,14 @@ ## 3.3.0 +* [fixed] A.on('select') (debugged from ipyaladin) +* [fixed] Simbad pointer in galactical frame, cone search of simbad/vizier cats/other cone search services in galactical frame and MOC creation from selection in galactical frame => there is now a new `frame` optional param to Aladin.pix2world. If not given, the coo returned are in the frame of the view. +* [doc] Add doc for image survey definition +* [deprecation] A.createImageSurvey/A.newImageSurvey are now deprecated (but still in the API). Please use `A.imageHiPS` instead by providing a valid url or CDS ID conformed to +* [refac] Simplify the instanciation of an imageHiPS/ imageFITS. Add a `A.imageHiPS` method for defining a HiPS object +* [fixed] At initialisation, giving a fov > 180 was clamped back to 180 even if we specify allsky projection (i.e. accepting fov > 180). This is now fixed. +* [fixed] MeasurementTable now display the full cell values (no ellipsis anymore) +* [fixed] aladin.on('select') has been implemented. Callback is triggered on a circle and rect selections for not on polygonal selection. * [fixed] the cooFrame UI selector is updated if the user calls `aladin.setFrame` * [fixed] `reticleColor` and `reticleSize` options in the public API * Restore setFoVRange diff --git a/README.md b/README.md index e3b0ba99..73441b00 100644 --- a/README.md +++ b/README.md @@ -11,6 +11,7 @@ Aladin Lite is built to be easily embeddable in any web page. It powers astronom More details on [Aladin Lite documentation page](http://aladin.u-strasbg.fr/AladinLite/doc/). [![Run tests](https://github.com/cds-astro/aladin-lite/actions/workflows/test.yml/badge.svg)](https://github.com/cds-astro/aladin-lite/actions/workflows/test.yml) +[![API Documentation](https://img.shields.io/badge/docs-latest-blue.svg)](https://cds-astro.github.io/aladin-lite) # How to test it ? diff --git a/examples/al-badbaselayer.html b/examples/al-badbaselayer.html index 0913cedd..15bf2c13 100644 --- a/examples/al-badbaselayer.html +++ b/examples/al-badbaselayer.html @@ -9,8 +9,8 @@ A.init.then(() => { let aladin = A.aladin('#aladin-lite-div', {fov: 70,projection: "AIT"}); - //let hsc = aladin.newImageSurvey("P/HSC/DR2/deep/g", {colormap:"Purples", imgFormat: "fits"}); - //aladin.setBaseImageLayer(hsc); + let hsc = aladin.newImageSurvey("P/HSC/DR2/deep/g", {colormap:"Purples", imgFormat: "fits"}); + aladin.setBaseImageLayer(hsc); }); diff --git a/examples/al-coronelli.html b/examples/al-coronelli.html index 97decd87..69593a90 100644 --- a/examples/al-coronelli.html +++ b/examples/al-coronelli.html @@ -252,13 +252,13 @@ aladin.addCatalog(hipsCats['constellations-boundaries']); aladin.addCatalog(hipsCats['gaia']); - var coronelliStars = { - 'coronelli-stars-white': A.catalogFromURL("http://cdsweb.u-strasbg.fr/~derriere/coronelli/white.xml", {name: 'Coronelli white', color: '#ffffff', shape: 'rhomb', sourceSize: 10}), - 'coronelli-stars-yellow': A.catalogFromURL("http://cdsweb.u-strasbg.fr/~derriere/coronelli/yellow.xml", {name: 'Coronelli yellow', color: '#f6f874', shape: 'rhomb', sourceSize: 10}), - 'coronelli-stars-red': A.catalogFromURL("http://cdsweb.u-strasbg.fr/~derriere/coronelli/red.xml", {name: 'Coronelli red', color: '#ff5555', shape: 'rhomb', sourceSize: 10}), - 'coronelli-stars-blue': A.catalogFromURL("http://cdsweb.u-strasbg.fr/~derriere/coronelli/blue.xml", {name: 'Coronelli blue', color: '#1ca5ec', shape: 'rhomb', sourceSize: 10}) + 'coronelli-stars-white': A.catalogFromURL("http://cdsweb.u-strasbg.fr/~derriere/coronelli/white.xml", {name: 'Coronelli white', color: '#ffffff', shape: 'rhomb', sourceSize: 10}), + 'coronelli-stars-yellow': A.catalogFromURL("http://cdsweb.u-strasbg.fr/~derriere/coronelli/yellow.xml", {name: 'Coronelli yellow', color: '#f6f874', shape: 'rhomb', sourceSize: 10}), + 'coronelli-stars-red': A.catalogFromURL("http://cdsweb.u-strasbg.fr/~derriere/coronelli/red.xml", {name: 'Coronelli red', color: '#ff5555', shape: 'rhomb', sourceSize: 10}), + 'coronelli-stars-blue': A.catalogFromURL("http://cdsweb.u-strasbg.fr/~derriere/coronelli/blue.xml", {name: 'Coronelli blue', color: '#1ca5ec', shape: 'rhomb', sourceSize: 20}) }; + coronelliStars['coronelli-stars-white'].hide(); coronelliStars['coronelli-stars-yellow'].hide(); coronelliStars['coronelli-stars-red'].hide(); diff --git a/examples/al-customize-button.html b/examples/al-customize-button.html index 5117fdc5..53212403 100644 --- a/examples/al-customize-button.html +++ b/examples/al-customize-button.html @@ -70,7 +70,7 @@ aladin.addStatusBarMessage({ duration: 10000, type: 'info', - message: 'Aladin Lite v3.3 is out. New features available:' + message: 'Aladin Lite v3.3 is out. New features available:' }) }); @@ -94,6 +94,11 @@ background-color: pink; } + + .aladin-cooFrame { + position: absolute; + top: 10rem; + } diff --git a/examples/al-displayFITS.html b/examples/al-displayFITS.html index 321f18a3..ba06799a 100644 --- a/examples/al-displayFITS.html +++ b/examples/al-displayFITS.html @@ -26,6 +26,7 @@ }, // no optional params (ra, dec, fov, image) => { // ra, dec and fov are centered around the fits image + console.log("jjj", image) image.setColormap("magma", {stretch: "asinh"}); aladin.gotoRaDec(ra, dec); diff --git a/examples/al-easy-access-simbad-ned.html b/examples/al-easy-access-simbad-ned.html index 1098aa97..44330271 100644 --- a/examples/al-easy-access-simbad-ned.html +++ b/examples/al-easy-access-simbad-ned.html @@ -16,8 +16,8 @@ showContextMenu: true, fullScreen: true, showSimbadPointerControl: true, - showSimbadPointerControl: false, showShareControl: true, + showSettingsControl: true, showStackLayerControl: true, samp: true, }); diff --git a/examples/al-hips-order-list.html b/examples/al-hips-order-list.html index 9d5f78a3..34c5430b 100644 --- a/examples/al-hips-order-list.html +++ b/examples/al-hips-order-list.html @@ -13,9 +13,9 @@ aladin = A.aladin('#aladin-lite-div', {survey: 'http://alasky.cds.unistra.fr/ancillary/GaiaDR2/hips-density-map/', showProjectionControl: true, showContextMenu: true, showStatusBar: true, fullScreen: true, target: 'galactic center'}); const fluxMap = aladin.createImageSurvey('gdr3-color-flux-map', 'Gaia DR3 flux map', 'https://alasky.u-strasbg.fr/ancillary/GaiaEDR3/color-Rp-G-Bp-flux-map', 'equatorial', 7); - const densityMap = aladin.createImageSurvey('gdr3-density-map', 'Gaia DR3 density map', 'sdfsg', 'equatorial', 7, {imgFormat: 'fits'}); - const decaps = aladin.createImageSurvey("decaps", "DECaPS DR1", "http://alasky.u-strasbg.fr/DECaPS/DR1/color/", "equatorial", 11, {imgFormat: 'png'}); - const panstarrs = aladin.createImageSurvey("panstarrs", "PanSTARRS", "http://alasky.u-strasbg.fr/Pan-STARRS/DR1/color-i-r-g/", "equatorial", 11, {imgFormat: 'jpg'}); + const densityMap = aladin.createImageSurvey('gdr3-density-map', 'Gaia DR3 density map', 'sdfsg', 'equatorial', 7, {formats: ['fits']}); + const decaps = aladin.createImageSurvey("decaps", "DECaPS DR1", "http://alasky.u-strasbg.fr/DECaPS/DR1/color/", "equatorial", 11, {formats: ['png'], tileSize: 512}); + const panstarrs = aladin.createImageSurvey("panstarrs", "PanSTARRS", "http://alasky.u-strasbg.fr/Pan-STARRS/DR1/color-i-r-g/", "equatorial", 11, {formats: ['jpg']}); aladin.setOverlayImageLayer(fluxMap) aladin.setOverlayImageLayer(densityMap, "density") diff --git a/examples/al-init-custom-options.html b/examples/al-init-custom-options.html index fab092ef..7a22f4b7 100644 --- a/examples/al-init-custom-options.html +++ b/examples/al-init-custom-options.html @@ -9,6 +9,8 @@ diff --git a/examples/al-no-properties.html b/examples/al-no-properties.html new file mode 100644 index 00000000..eb9a15da --- /dev/null +++ b/examples/al-no-properties.html @@ -0,0 +1,27 @@ + + + + + +
+ + + \ No newline at end of file diff --git a/src/core/al-api/src/hips.rs b/src/core/al-api/src/hips.rs index 3e5699ee..db681c45 100644 --- a/src/core/al-api/src/hips.rs +++ b/src/core/al-api/src/hips.rs @@ -43,10 +43,9 @@ pub struct HiPSProperties { // Associated with the HiPS url: String, max_order: u8, - frame: CooSystem, + coo_frame: CooSystem, tile_size: i32, formats: Vec, - dataproduct_subtype: Option>, is_planetary_body: Option, @@ -103,7 +102,7 @@ impl HiPSProperties { #[inline(always)] pub fn get_frame(&self) -> CooSystem { - self.frame + self.coo_frame } #[inline(always)] @@ -125,11 +124,6 @@ impl HiPSProperties { pub fn get_initial_dec(&self) -> Option { self.hips_initial_dec } - - #[inline(always)] - pub fn get_dataproduct_subtype(&self) -> &Option> { - &self.dataproduct_subtype - } } #[derive(Deserialize, Debug, Clone, Copy, PartialEq, Eq, Hash)] diff --git a/src/core/src/renderable/hips/mod.rs b/src/core/src/renderable/hips/mod.rs index 5c1b2e3e..48056407 100644 --- a/src/core/src/renderable/hips/mod.rs +++ b/src/core/src/renderable/hips/mod.rs @@ -4,10 +4,10 @@ pub mod uv; use al_api::hips::ImageExt; use al_api::hips::ImageMetadata; - use al_core::colormap::Colormap; use al_core::colormap::Colormaps; use al_core::image::format::ChannelType; +use al_core::image::format::ImageFormatType; use al_core::image::Image; use al_core::log::console_log; use al_core::shader::Shader; @@ -282,9 +282,7 @@ pub fn get_raster_shader<'a>( shaders: &'a mut ShaderManager, config: &HiPSConfig, ) -> Result<&'a Shader, JsValue> { - let colored_hips = config.is_colored(); - - if colored_hips && cmap.label() == "native" { + if config.get_format().is_colored() && cmap.label() == "native" { crate::shader::get_shader(gl, shaders, "RasterizerVS", "RasterizerColorFS") } else { if config.tex_storing_unsigned_int { @@ -318,8 +316,8 @@ pub fn get_raytracer_shader<'a>( shaders: &'a mut ShaderManager, config: &HiPSConfig, ) -> Result<&'a Shader, JsValue> { - let colored_hips = config.is_colored(); - if colored_hips && cmap.label() == "native" { + //let colored_hips = config.is_colored(); + if config.get_format().is_colored() && cmap.label() == "native" { crate::shader::get_shader(gl, shaders, "RayTracerVS", "RayTracerColorFS") } else { if config.tex_storing_unsigned_int { diff --git a/src/core/src/survey/config.rs b/src/core/src/survey/config.rs index 18aef6c9..8becf983 100644 --- a/src/core/src/survey/config.rs +++ b/src/core/src/survey/config.rs @@ -168,8 +168,8 @@ pub struct HiPSConfig { pub frame: CooSystem, pub bitpix: Option, format: ImageFormatType, - dataproduct_subtype: Option>, - colored: bool, + //dataproduct_subtype: Option>, + //colored: bool, pub creator_did: String, } @@ -272,7 +272,7 @@ impl HiPSConfig { }), }?; - let dataproduct_subtype = properties.get_dataproduct_subtype().clone(); + /*let dataproduct_subtype = properties.get_dataproduct_subtype().clone(); let colored = if tex_storing_fits { false } else { @@ -281,7 +281,7 @@ impl HiPSConfig { } else { false } - }; + };*/ let empty_image = EmptyTileImage::new(tile_size, format.get_channel()); @@ -341,8 +341,8 @@ impl HiPSConfig { bitpix, format, tile_size, - dataproduct_subtype, - colored, + //dataproduct_subtype, + //colored, }; Ok(hips_config) @@ -421,7 +421,7 @@ impl HiPSConfig { self.empty_image = EmptyTileImage::new(self.tile_size, self.format.get_channel()); // Recompute if the survey will be colored or not - self.colored = if self.tex_storing_fits { + /*self.colored = if self.tex_storing_fits { false } else { if let Some(subtypes) = &self.dataproduct_subtype { @@ -429,7 +429,7 @@ impl HiPSConfig { } else { false } - }; + };*/ Ok(()) } @@ -528,7 +528,7 @@ impl HiPSConfig { #[inline(always)] pub fn is_colored(&self) -> bool { - self.colored + self.format.is_colored() } #[inline(always)] diff --git a/src/css/aladin.css b/src/css/aladin.css index 3841bd7a..3e55eb3a 100644 --- a/src/css/aladin.css +++ b/src/css/aladin.css @@ -80,7 +80,7 @@ body { overscroll-behavior: contain; } display: block; - max-height: 33vh; + max-height: 30vh; max-width: 100%; -ms-overflow-style: none; overscroll-behavior-x: none; @@ -150,8 +150,8 @@ body { overscroll-behavior: contain; } white-space: nowrap; overflow: hidden; text-align: center; - max-width: 150px; - text-overflow: ellipsis; + /*max-width: 150px; + text-overflow: ellipsis;*/ } .aladin-measurement-div table td.aladin-text-td-container { @@ -159,8 +159,8 @@ body { overscroll-behavior: contain; } white-space: nowrap; overflow: hidden; - max-width: 150px; - text-overflow: ellipsis; + /*max-width: 150px; + text-overflow: ellipsis;*/ } .aladin-measurement-div table td.aladin-href-td-container:hover { @@ -464,10 +464,12 @@ canvas { } .aladin-input-text.search.aladin-unknownObject { - -webkit-box-shadow:inset 0px 0px 0px 3px #f00; - -moz-box-shadow:inset 0px 0px 0px 3px #f00; - box-shadow:inset 0px 0px 0px 3px #f00; -} + -webkit-box-shadow:inset 0px 0px 0px 1px #f00; + -moz-box-shadow:inset 0px 0px 0px 1px #f00; + box-shadow:inset 0px 0px 0px 1px #f00; + + border: 1px solid red; +} .aladin-dark-theme { color: white; @@ -654,10 +656,14 @@ canvas { .aladin-status-bar-message { display: block; white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; - max-width: 300px; + text-wrap: nowrap; + -ms-overflow-style: none; + overscroll-behavior-x: none; + overflow-y: scroll; + scrollbar-width: none; + + max-width: 30rem; font-size: 1rem; } @@ -988,6 +994,7 @@ canvas { border-radius: 2px; top:0%; + left:0%; z-index: 100; diff --git a/src/js/A.js b/src/js/A.js index cfd97fc0..a188f072 100644 --- a/src/js/A.js +++ b/src/js/A.js @@ -87,6 +87,7 @@ let A = {}; */ A.aladin = function (divSelector, options) { let divElement; + if (!(divSelector instanceof HTMLElement)) { divElement = document.querySelector(divSelector) } else { @@ -95,6 +96,45 @@ A.aladin = function (divSelector, options) { return new Aladin(divElement, options); }; +/** + * Creates a HiPS image object + * + * @function + * @name A.imageHiPS + * @memberof A + * @param {string} id - Mandatory unique identifier for the layer. + * @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 + */ +A.imageHiPS = function (id, url, options) { + return Aladin.createImageSurvey( + id, + options && options.name, + url, + options && options.cooFrame, + options && options.maxOrder, + options + ); +} + +/** + * Creates a celestial source object with the given coordinates. + * + * @function + * @name A.imageFITS + * @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 + * @example + * const sourceObj = A.source(180.0, 30.0, data, options); + */ +A.imageFITS = function (url, options) { + return Aladin.createImageFITS(url, options.name, options, options.successCallback, options.errorCallback); +} + /** * Creates a celestial source object with the given coordinates. * diff --git a/src/js/Aladin.js b/src/js/Aladin.js index 086bac3e..1a34019f 100644 --- a/src/js/Aladin.js +++ b/src/js/Aladin.js @@ -56,14 +56,11 @@ import { Location } from "./gui/Location.js"; import { FoV } from "./gui/FoV.js"; import { ShareActionButton } from "./gui/Button/ShareView.js"; import { ContextMenu } from "./gui/Widgets/ContextMenu.js"; -import { Input } from "./gui/Widgets/Input.js"; import { Popup } from "./Popup.js"; import A from "./A.js"; import { StatusBarBox } from "./gui/Box/StatusBarBox.js"; import { FullScreenActionButton } from "./gui/Button/FullScreen.js"; import { ProjectionActionButton } from "./gui/Button/Projection.js"; -import { Toolbar } from './gui/Widgets/Toolbar'; -import { ImageLayer } from './ImageLayer'; // features import { SettingsButton } from "./gui/Button/Settings"; @@ -80,7 +77,7 @@ import { CooFrame } from './gui/Input/CooFrame'; * @property {string[]} [surveyUrl=["https://alaskybis.unistra.fr/DSS/DSSColor", "https://alasky.unistra.fr/DSS/DSSColor"]] * Array of URLs for the survey images. This replaces the survey parameter. * @property {string} [target="0 +0"] - Target coordinates for the initial view. - * @property {string} [cooFrame="J2000"] - Coordinate frame. + * @property {CooFrame} [cooFrame="J2000"] - Coordinate frame. * @property {number} [fov=60] - Field of view in degrees. * @property {string} [backgroundColor="rgb(60, 60, 60)"] - Background color in RGB format. * @@ -130,13 +127,18 @@ import { CooFrame } from './gui/Input/CooFrame'; * @property {boolean} [gridOptions.showLabels=true] - Whether the grid has labels. * @property {number} [gridOptions.labelSize=15] - The font size of the labels. * - * @property {string} [projection="SIN"] - Projection type. + * @property {string} [projection="SIN"] - Projection type. Can be 'SIN' for orthographic, 'MOL' for mollweide, 'AIT' for hammer-aitoff, 'ZEA' for zenital equal-area or 'MER' for mercator * @property {boolean} [log=true] - Whether to log events. * @property {boolean} [samp=false] - Whether to enable SAMP (Simple Application Messaging Protocol). * @property {boolean} [realFullscreen=false] - Whether to use real fullscreen mode. * @property {boolean} [pixelateCanvas=true] - Whether to pixelate the canvas. */ +/** + * @typedef {string} CooFrame + * String with possible values: 'equatorial', 'ICRS', 'ICRSd', 'j2000', 'gal, 'galactic' + */ + export let Aladin = (function () { /** * Creates an instance of the Aladin interactive sky atlas. @@ -214,11 +216,9 @@ export let Aladin = (function () { // parent div aladinDiv.classList.add("aladin-container"); - // measurement table + // Init the measurement table this.measurementTable = new MeasurementTable(this); - //var location = new Location(locationDiv.find('.aladin-location-text')); - // set different options // Reticle this.view = new View(this); @@ -229,7 +229,6 @@ export let Aladin = (function () { this.reticle = new Reticle(this.options, this); this.popup = new Popup(this.aladinDiv, this.view); - this.cacheSurveys = new Map(); this.ui = []; // Background color @@ -249,10 +248,6 @@ export let Aladin = (function () { } this.setCooGrid(gridOptions); - // Set the projection - let projection = (options && options.projection) || 'SIN'; - this.setProjection(projection) - this.gotoObject(options.target, undefined); if (options.log) { @@ -279,12 +274,14 @@ export let Aladin = (function () { i++; }); } else if (options.survey === ImageSurvey.DEFAULT_SURVEY_ID) { - const survey = ImageSurvey.fromLayerOptions(this, ImageLayer.DEFAULT_SURVEY); + // DSS is cached inside ImageSurvey class, no need to provide any further information + const survey = this.createImageSurvey(ImageSurvey.DEFAULT_SURVEY_ID); + this.setBaseImageLayer(survey); } else { this.setBaseImageLayer(options.survey) } - } else if (options.surveyUrl) { + } else { // Add the image layers // For that we check the survey key of options // It can be given as a single string or an array of strings @@ -301,9 +298,6 @@ export let Aladin = (function () { } this.setBaseImageLayer(url); - } else { - // This case should not happen because if there is no survey given - // then the surveyUrl pointing to the DSS is given. } this.view.showCatalog(options.showCatalog); @@ -348,11 +342,7 @@ export let Aladin = (function () { // Status bar if (options.showStatusBar) { - let statusBarOptions = {}; - if (typeof options.showStatusBar === "object") { - statusBarOptions = options.showStatusBar; - } - this.statusBar = new StatusBarBox(this, statusBarOptions); + this.statusBar = new StatusBarBox(this); this.addUI(this.statusBar) } @@ -853,7 +843,7 @@ export let Aladin = (function () { } // planetary case else { - const body = baseImageLayer.properties.hipsBody; + const body = baseImageLayer.hipsBody; PlanetaryFeaturesNameResolver.resolve(targetName, body, function (data) { // success callback self.view.pointTo(data.lon, data.lat); @@ -1179,29 +1169,51 @@ export let Aladin = (function () { this.view.removeLayer(layer); }; - // @oldAPI - Aladin.prototype.createImageSurvey = function(id, name, rootUrl, cooFrame, maxOrder, options = {}) { - let cfg = this.cacheSurveys.get(id); - if (!cfg) { - // Add the cooFrame and maxOrder given by the user - // to the list of options passed to the ImageSurvey constructor - if (cooFrame) { - options.cooFrame = cooFrame; - } + /** + * @deprecated + * Creates and return an image survey (HiPS) object + * + * @memberof Aladin + * @param {string} id - Mandatory unique identifier for the layer. + * @param {string} [name] - A convinient name for the survey, optional + * @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 {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. + */ + Aladin.prototype.createImageSurvey = function(id, name, url, cooFrame, maxOrder, options) { + let surveyOptions = ImageSurvey.cache[id]; - if (maxOrder) { - options.maxOrder = maxOrder; - } - - cfg = {id, name, rootUrl, options}; - this.cacheSurveys.set(id, cfg); - } else { - cfg = Utils.clone(cfg) + if (!surveyOptions) { + surveyOptions = {url, name, maxOrder, cooFrame, ...options}; + ImageSurvey.cache[id] = surveyOptions; } - return new ImageSurvey(cfg.id, cfg.name, cfg.rootUrl, cfg.options, this.view); + + return new ImageSurvey(id, surveyOptions.url, surveyOptions); }; - Aladin.prototype.createImageFITS = function(url, name, options = {}, successCallback = undefined, errorCallback = undefined) { + /** + * @deprecated + * Creates and return an image survey (HiPS) object + * + * @function createImageSurvey + * @memberof Aladin + * @static + * @param {string} id - Mandatory unique identifier for the layer. + * @param {string} [name] - A convinient name for the survey, optional + * @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 {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. + */ + Aladin.createImageSurvey = Aladin.prototype.createImageSurvey; + + + Aladin.prototype.createImageFITS = function(url, name, options, successCallback, errorCallback) { try { url = new URL(url); } catch(e) { @@ -1213,30 +1225,53 @@ 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 cfg = this.cacheSurveys.get(url); - if (!cfg) { - cfg = {url, name, options, successCallback, errorCallback} - this.cacheSurveys.set(url, cfg); - } else { - cfg = Utils.clone(cfg) + let image = ImageFITS.cache[url]; + if (!image) { + image = new ImageFITS(url, name, options, successCallback, errorCallback) + ImageFITS.cache[url] = image; } - return new ImageFITS(cfg.url, cfg.name, this.view, cfg.options, cfg.successCallback, cfg.errorCallback); + return image; }; - Aladin.prototype.newImageSurvey = function(rootUrlOrId, options) { - const idOrUrl = rootUrlOrId; - // Check if the survey has already been added - // Create a new ImageSurvey - const name = idOrUrl; + /** + * Creates a FITS image object + * + * @function createImageFITS + * @memberof Aladin + * @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 {function} [success] - A success callback + * @param {function} [error] - A success callback + * @returns {ImageSurvey} A FITS image object. + */ + Aladin.createImageFITS = Aladin.prototype.createImageFITS; - return this.createImageSurvey(idOrUrl, name, idOrUrl, null, null, options); + + /** + * @deprecated + * Create a new layer from an url or CDS ID. + * + * @memberof Aladin + * @static + * @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 for rendering the image + * @param {function} [success] - A success callback + * @param {function} [error] - A success callback + * @returns {ImageSurvey} A FITS image object. + */ + Aladin.prototype.newImageSurvey = function(url, options) { + const id = url; + return A.imageHiPS(id, url, options); } Aladin.prototype.addNewImageLayer = function() { let layerName = Utils.uuidv4(); // A HIPS_LAYER_ADDED will be called after the hips is added to the view - this.setOverlayImageLayer('CDS/P/DSS2/color', layerName); + this.setOverlayImageLayer(ImageSurvey.DEFAULT_SURVEY_ID, layerName); } // @param imageSurvey : ImageSurvey object or image survey identifier @@ -1273,8 +1308,8 @@ export let Aladin = (function () { }; - Aladin.prototype.setBaseImageLayer = function(idOrSurvey) { - return this.setOverlayImageLayer(idOrSurvey, "base"); + Aladin.prototype.setBaseImageLayer = function(idOrUrlOrImageLayer) { + return this.setOverlayImageLayer(idOrUrlOrImageLayer, "base"); }; // @api @@ -1288,26 +1323,9 @@ export let Aladin = (function () { // 1. User gives an ID if (typeof idOrUrlOrImageLayer === "string") { const idOrUrl = idOrUrlOrImageLayer; - // Check if the survey has already been added - // Create a new ImageSurvey - /*let isUrl = false; - if (idOrUrl.includes("http")) { - isUrl = true; - } - const name = idOrUrl; - if (isUrl) { - const url = idOrUrl; - const id = url; - // Url - imageLayer = this.createImageSurvey(idOrUrl, name, idOrUrl, null, null); - } else { - const id = idOrUrl; - // ID - imageLayer = this.createImageSurvey(idOrUrl, name, idOrUrl, null, null); - }*/ - const name = idOrUrl; - imageLayer = this.createImageSurvey(idOrUrl, name, idOrUrl, null, null); + imageLayer = A.imageHiPS(idOrUrl, idOrUrl); + // 2. User gives a non resolved promise } else { imageLayer = idOrUrlOrImageLayer; @@ -1457,11 +1475,6 @@ export let Aladin = (function () { Aladin.prototype.select = async function (mode = 'rect', callback) { await this.reticle.loaded; - // Default callback selects objects - callback = callback || ((selection) => { - this.view.selectObjects(selection); - }); - this.fire('selectstart', {mode, callback}); }; @@ -1635,7 +1648,7 @@ export let Aladin = (function () { let radecsys; if (this.getBaseImageLayer().isPlanetaryBody()) { - const body = this.getBaseImageLayer().properties.hipsBody + const body = this.getBaseImageLayer().hipsBody if (body in solarSystemObjects) { cooType1 = `${solarSystemObjects[body]}LN-`; cooType2 = `${solarSystemObjects[body]}LT-`; @@ -1715,13 +1728,29 @@ export let Aladin = (function () { * @memberof Aladin * @param {number} x - The x-coordinate in pixel coordinates. * @param {number} y - The y-coordinate in pixel coordinates. + * @param {CooFrame} [frame] - The frame in which we want to retrieve the coordinates. + * If not given, the frame chosen is the one from the view * - * @returns {number[]} - An array representing the [Right Ascension, Declination] coordinates in degrees. + * @returns {number[]} - An array representing the [Right Ascension, Declination] coordinates in degrees in the `frame`. + * If not specified, returns the coo in the frame of the current view. * * @throws {Error} Throws an error if an issue occurs during the transformation. */ - Aladin.prototype.pix2world = function (x, y) { - const [ra, dec] = this.view.wasm.screenToWorld(x, y); + Aladin.prototype.pix2world = function (x, y, frame) { + let radec = this.view.wasm.screenToWorld(x, y); + + frame = frame || this.view.cooFrame.label; + frame = CooFrameEnum.fromString(frame, CooFrameEnum.J2000); + + if (frame !== this.view.cooFrame) { + if (frame.label === 'Galactic') { + console.warn('Conversion from icrs to galactic not yet impl') + } else { + radec = this.view.wasm.viewToICRSCooSys(radec[0], radec[1]); + } + } + + let [ra, dec] = radec; if (ra < 0) { return [ra + 360.0, dec]; @@ -1734,8 +1763,8 @@ export let Aladin = (function () { * Transform world coordinates to pixel coordinates in the view. * * @memberof Aladin - * @param {number} ra - The Right Ascension (RA) coordinate in degrees. - * @param {number} dec - The Declination (Dec) coordinate in degrees. + * @param {number} ra - The Right Ascension (RA) coordinate in degrees. Frame considered is the current view frame + * @param {number} dec - The Declination (Dec) coordinate in degrees. Frame considered is the current view frame * * @returns {number[]} - An array representing the [x, y] coordinates in pixel coordinates in the view. * @@ -1753,14 +1782,16 @@ export let Aladin = (function () { * @param {number} y1 - The y-coordinate of the first pixel coordinates. * @param {number} x2 - The x-coordinate of the second pixel coordinates. * @param {number} y2 - The y-coordinate of the second pixel coordinates. + * @param {CooFrame} [frame] - The frame in which we want to retrieve the coordinates. + * If not given, the frame chosen is the one from the view * * @returns {number} - The angular distance between the two pixel coordinates in degrees * * @throws {Error} Throws an error if an issue occurs during the transformation. */ - Aladin.prototype.angularDist = function (x1, y1, x2, y2) { - const [ra1, dec1] = this.pix2world(x1, y1); - const [ra2, dec2] = this.pix2world(x2, y2); + Aladin.prototype.angularDist = function (x1, y1, x2, y2, frame) { + const [ra1, dec1] = this.pix2world(x1, y1, frame); + const [ra2, dec2] = this.pix2world(x2, y2, frame); return this.wasm.angularDist(ra1, dec1, ra2, dec2); }; diff --git a/src/js/Catalog.js b/src/js/Catalog.js index 91f6b5ca..abf9bf5d 100644 --- a/src/js/Catalog.js +++ b/src/js/Catalog.js @@ -55,7 +55,6 @@ export let Catalog = (function() { * @param {string} [options.name="catalog"] - The name of the catalog. * @param {string} [options.color] - The color associated with the catalog. * @param {number} [options.sourceSize=8] - The size of the sources in the catalog. - * @param {number} [options.markerSize=12] - The size of the markers associated with sources. * @param {string} [options.shape="square"] - The shape of the sources (can be, "square", "circle", "plus", "cross", "rhomb", "triangle"). * @param {number} [options.limit] - The maximum number of sources to display. * @param {function} [options.onClick] - The callback function to execute on a source click. diff --git a/src/js/ColorCfg.js b/src/js/ColorCfg.js index 964ff554..5f967de2 100644 --- a/src/js/ColorCfg.js +++ b/src/js/ColorCfg.js @@ -222,7 +222,31 @@ return [this.minCut, this.maxCut]; }; - ColorCfg.COLORMAPS = []; + ColorCfg.COLORMAPS = [ + "blues", + "cividis", + "cubehelix", + "eosb", + "grayscale", + "inferno", + "magma", + "native", + "parula", + "plasma", + "rainbow", + "rdbu", + "rdylbu", + "redtemperature", + "sinebow", + "spectral", + "summer", + "viridis", + "ylgnbu", + "ylorbr", + "red", + "green", + "blue" + ]; return ColorCfg; })(); diff --git a/src/js/ColorMap.js b/src/js/ColorMap.js deleted file mode 100644 index f90a7e8c..00000000 --- a/src/js/ColorMap.js +++ /dev/null @@ -1,275 +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 ColorMap.js - * - * Author: Thomas Boch[CDS] - * - *****************************************************************************/ - -import { AladinUtils } from "./AladinUtils.js"; - -/** - * @deprecated since version 3.0 - */ -export let ColorMap = (function() { - - - // constructor - let ColorMap = function(view) { - this.view = view; - this.reversed = false; - this.mapName = 'native'; - this.sig = this.signature(); - }; - -ColorMap.MAPS = {}; - - ColorMap.MAPS['eosb'] = { - name: 'Eos B', - r: [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,9,18,27,36,45,49,57,72,81,91,100,109,118,127, - 136,131,139,163,173,182,191,200,209,218,227,213,221,255,255,255,255,255, - 255,255,255,229,229,255,255,255,255,255,255,255,255,229,229,255,255,255, - 255,255,255,255,255,229,229,255,255,255,255,255,255,255,255,229,229,255, - 255,255,255,255,255,255,255,229,229,255,255,255,255,255,255,255,255,229, - 229,255,255,255,255,255,255,255,255,229,229,255,255,255,255,255,255,255, - 255,229,229,255,255,255,255,255,255,255,255,229,229,255,253,251,249,247, - 245,243,241,215,214,235,234,232,230,228,226,224,222,198,196,216,215,213, - 211,209,207,205,203,181,179,197,196,194,192,190,188,186,184,164,162,178, - 176,175,173,171,169,167,165,147,145,159,157,156,154,152,150,148,146,130, - 128,140,138,137,135,133,131,129,127,113,111,121,119,117,117], - g: [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,15,23,31,39,47,55,57,64,79,87,95, - 103,111,119,127,135,129,136,159,167,175,183,191,199,207,215,200,207,239, - 247,255,255,255,255,255,255,229,229,255,255,255,255,255,255,255,255,229, - 229,255,255,255,255,255,255,255,255,229,229,255,250,246,242,238,233,229, - 225,198,195,212,208,204,199,195,191,187,182,160,156,169,165,161,157,153, - 148,144,140,122,118,127,125,123,121,119,116,114,112,99,97,106,104,102, - 99,97,95,93,91,80,78,84,82,80,78,76,74,72,70,61,59,63,61,59,57,55,53,50, - 48,42,40,42,40,38,36,33,31,29,27,22,21,21,19,16,14,12,13,8,6,3,1,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], - b: [116,121,127,131,136,140,144,148,153, - 157,145,149,170,174,178,182,187,191,195,199,183,187,212,216,221,225,229, - 233,238,242,221,225,255,247,239,231,223,215,207,199,172,164,175,167,159, - 151,143,135,127,119,100,93,95,87,79,71,63,55,47,39,28,21,15,7,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0] - }; - ColorMap.MAPS['rainbow'] = { - name: 'Rainbow', - r: [0,4,9,13,18,22,27,31,36,40,45,50,54, - 58,61,64,68,69,72,74,77,79,80,82,83,85,84,86,87,88,86,87,87,87,85,84,84, - 84,83,79,78,77,76,71,70,68,66,60,58,55,53,46,43,40,36,33,25,21,16,12,4,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,8,12,21,25,29,33,42, - 46,51,55,63,67,72,76,80,89,93,97,101,110,114,119,123,131,135,140,144,153, - 157,161,165,169,178,182,187,191,199,203,208,212,221,225,229,233,242,246, - 250,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, - 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, - 255,255,255,255,255,255,255,255,255,255,255,255,255,255], - g: [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,4,8,16,21,25,29,38,42,46,51,55,63,67,72,76,84,89,93,97, - 106,110,114,119,127,131,135,140,144,152,157,161,165,174,178,182,187,195, - 199,203,208,216,220,225,229,233,242,246,250,255,255,255,255,255,255,255, - 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, - 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, - 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, - 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, - 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, - 255,250,242,238,233,229,221,216,212,208,199,195,191,187,178,174,170,165, - 161,153,148,144,140,131,127,123,119,110,106,102,97,89,85,80,76,72,63,59, - 55,51,42,38,34,29,21,17,12,8,0], - b: [0,3,7,10,14,19,23,28,32,38,43,48,53, - 59,63,68,72,77,81,86,91,95,100,104,109,113,118,122,127,132,136,141,145, - 150,154,159,163,168,173,177,182,186,191,195,200,204,209,214,218,223,227, - 232,236,241,245,250,255,255,255,255,255,255,255,255,255,255,255,255,255, - 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, - 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, - 255,255,255,255,255,255,246,242,238,233,225,220,216,212,203,199,195,191, - 187,178,174,170,165,157,152,148,144,135,131,127,123,114,110,106,102,97, - 89,84,80,76,67,63,59,55,46,42,38,34,25,21,16,12,8,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0] - }; - ColorMap.MAPS['cubehelix'] = { - name: 'Cubehelix', - r: [0,1,3,4,6,8,9,10,12,13,14,15,17,18, - 19,20,20,21,22,23,23,24,24,25,25,25,26,26,26,26,26,26,26,26,26,26,26,25, - 25,25,25,24,24,24,23,23,23,23,22,22,22,21,21,21,21,21,21,20,20,20,21,21, - 21,21,21,22,22,22,23,23,24,25,26,27,27,28,30,31,32,33,35,36,38,39,41,43, - 45,47,49,51,53,55,57,60,62,65,67,70,72,75,78,81,83,86,89,92,95,98,101,104, - 107,110,113,116,120,123,126,129,132,135,138,141,144,147,150,153,155,158, - 161,164,166,169,171,174,176,178,181,183,185,187,189,191,193,194,196,198, - 199,201,202,203,204,205,206,207,208,209,209,210,211,211,211,212,212,212, - 212,212,212,212,212,211,211,211,210,210,210,209,208,208,207,207,206,205, - 205,204,203,203,202,201,201,200,199,199,198,197,197,196,196,195,195,194, - 194,194,193,193,193,193,193,193,193,193,193,193,194,194,195,195,196,196, - 197,198,199,200,200,202,203,204,205,206,208,209,210,212,213,215,217,218, - 220,222,223,225,227,229,231,232,234,236,238,240,242,244,245,247,249,251, - 253,255], - g: [0,0,1,1,2,2,3,4,4,5,6,6,7,8,9,10, - 11,11,12,13,14,15,17,18,19,20,21,22,24,25,26,28,29,31,32,34,35,37,38,40, - 41,43,45,46,48,50,52,53,55,57,58,60,62,64,66,67,69,71,73,74,76,78,79,81, - 83,84,86,88,89,91,92,94,95,97,98,99,101,102,103,104,106,107,108,109,110, - 111,112,113,114,114,115,116,116,117,118,118,119,119,120,120,120,121,121, - 121,121,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,121, - 121,121,121,121,121,121,121,121,120,120,120,120,120,120,120,120,120,120, - 121,121,121,121,121,122,122,122,123,123,124,124,125,125,126,127,127,128, - 129,130,131,131,132,133,135,136,137,138,139,140,142,143,144,146,147,149, - 150,152,154,155,157,158,160,162,164,165,167,169,171,172,174,176,178,180, - 182,183,185,187,189,191,193,194,196,198,200,202,203,205,207,208,210,212, - 213,215,216,218,219,221,222,224,225,226,228,229,230,231,232,233,235,236, - 237,238,239,240,240,241,242,243,244,244,245,246,247,247,248,248,249,250, - 250,251,251,252,252,253,253,254,255], - b: [0,1,3,4,6,8,9,11,13,15,17,19,21,23, - 25,27,29,31,33,35,37,39,41,43,45,47,48,50,52,54,56,57,59,60,62,63,65,66, - 67,69,70,71,72,73,74,74,75,76,76,77,77,77,78,78,78,78,78,78,78,77,77,77, - 76,76,75,75,74,73,73,72,71,70,69,68,67,66,66,65,64,63,61,60,59,58,58,57, - 56,55,54,53,52,51,51,50,49,49,48,48,47,47,47,46,46,46,46,46,47,47,47,48, - 48,49,50,50,51,52,53,55,56,57,59,60,62,64,65,67,69,71,74,76,78,81,83,86, - 88,91,94,96,99,102,105,108,111,114,117,120,124,127,130,133,136,140,143, - 146,149,153,156,159,162,165,169,172,175,178,181,184,186,189,192,195,197, - 200,203,205,207,210,212,214,216,218,220,222,224,226,227,229,230,231,233, - 234,235,236,237,238,239,239,240,241,241,242,242,242,243,243,243,243,243, - 243,243,243,243,243,242,242,242,242,241,241,241,241,240,240,240,239,239, - 239,239,239,238,238,238,238,238,238,238,238,239,239,239,240,240,240,241, - 242,242,243,244,245,246,247,248,249,250,252,253,255] - }; - - - - ColorMap.MAPS_CUSTOM = ['cubehelix', 'eosb', 'rainbow']; - ColorMap.MAPS_NAMES = ['native', 'grayscale'].concat(ColorMap.MAPS_CUSTOM); - - ColorMap.prototype.reverse = function(val) { - if (val) { - this.reversed = val; - } - else { - this.reversed = ! this.reversed; - } - this.sig = this.signature(); - this.view.requestRedraw(); - }; - - - ColorMap.prototype.signature = function() { - var s = this.mapName; - - if (this.reversed) { - s += ' reversed'; - } - - return s; - }; - - ColorMap.prototype.update = function(mapName) { - this.mapName = mapName; - this.sig = this.signature(); - this.view.requestRedraw(); - }; - - ColorMap.prototype.apply = function(img) { - if ( this.sig=='native' ) { - return img; - } - - if (img.cmSig==this.sig) { - return img.cmImg; // return cached pixels - } - - var canvas = document.createElement("canvas"); - canvas.width = img.width; - canvas.height = img.height; - var ctx = canvas.getContext("2d"); - ctx.drawImage(img, 0, 0); - - var imageData = ctx.getImageData(0, 0, canvas.width, canvas.height); - var pixelData = imageData.data; - var length = pixelData.length; - var a, b, c; - var switchCase = 3; - if (this.mapName=='grayscale') { - switchCase = 1; - } - else if (ColorMap.MAPS_CUSTOM.indexOf(this.mapName)>=0) { - switchCase = 2; - } - for (var i = 0; i < length; i+= 4) { - switch(switchCase) { - case 1: - a = b = c = AladinUtils.myRound((pixelData[i]+pixelData[i+1]+pixelData[i+2])/3); - break; - case 2: - if (this.reversed) { - a = ColorMap.MAPS[this.mapName].r[255-pixelData[i]]; - b = ColorMap.MAPS[this.mapName].g[255-pixelData[i+1]]; - c = ColorMap.MAPS[this.mapName].b[255-pixelData[i+2]]; - } - else { - a = ColorMap.MAPS[this.mapName].r[pixelData[i]]; - b = ColorMap.MAPS[this.mapName].g[pixelData[i+1]]; - c = ColorMap.MAPS[this.mapName].b[pixelData[i+2]]; - } - break; - default: - a = pixelData[i]; - b = pixelData[i + 1]; - c = pixelData[i + 2]; - - } - if (switchCase!=2 && this.reversed) { - a = 255-a; - b = 255-b; - c = 255-c; - - } - pixelData[i] = a; - pixelData[i + 1] = b; - pixelData[i + 2] = c; - - } - //imageData.data = pixelData; // not needed, and create an error in strict mode ! - ctx.putImageData(imageData, 0, 0); - - // cache image with color map applied - img.cmSig = this.sig; - img.cmImg = canvas; - - return img.cmImg; - }; - - return ColorMap; -})(); - diff --git a/src/js/FiniteStateMachine/CircleSelect.js b/src/js/FiniteStateMachine/CircleSelect.js index fdd93005..13b2e7f4 100644 --- a/src/js/FiniteStateMachine/CircleSelect.js +++ b/src/js/FiniteStateMachine/CircleSelect.js @@ -19,6 +19,7 @@ import { FSM } from "../FiniteStateMachine"; import { View } from "../View"; +import { Selector } from "../Selector"; /****************************************************************************** * Aladin Lite project * @@ -83,7 +84,8 @@ export class CircleSelect extends FSM { x = this.startCoo.x; y = this.startCoo.y; - (typeof this.callback === 'function') && this.callback({ + + let s = { x, y, r, label: 'circle', contains(s) { @@ -100,21 +102,25 @@ export class CircleSelect extends FSM { h: 2*r } } - }); + }; + + // execute general callback + if (view.aladin.callbacksByEventName) { + var callback = view.aladin.callbacksByEventName['select']; + if (typeof callback === "function") { + let objList = Selector.getObjects(s, view); + callback(objList); + } + } + + // execute selection callback only + (typeof this.callback === 'function') && this.callback(s); // TODO: remove these modes in the future view.aladin.showReticle(true) view.setCursor('default'); + console.log("end select", view) - // execute general callback - if (view.callbacksByEventName) { - var callback = view.callbacksByEventName['select']; - if (typeof callback === "function") { - // !todo - let selectedObjects = view.selectObjects(this); - callback(selectedObjects); - } - } view.setMode(View.PAN) view.requestRedraw(); }; diff --git a/src/js/FiniteStateMachine/PolySelect.js b/src/js/FiniteStateMachine/PolySelect.js index 2102d867..080941d3 100644 --- a/src/js/FiniteStateMachine/PolySelect.js +++ b/src/js/FiniteStateMachine/PolySelect.js @@ -163,13 +163,23 @@ export class PolySelect extends FSM { let y = yMin; let w = xMax - xMin; let h = yMax - yMin; - (typeof this.callback === 'function') && this.callback({ + + let s = { vertices: this.coos, label: 'polygon', bbox() { return {x, y, w, h} } - }); + }; + (typeof this.callback === 'function') && this.callback(s); + + // execute general callback + if (view.aladin.callbacksByEventName) { + var callback = view.aladin.callbacksByEventName['select']; + if (typeof callback === "function") { + console.warn('polygon selection is not fully implemented, PolySelect.contains is needed for finding sources inside a polygon') + } + } this.coos = []; diff --git a/src/js/FiniteStateMachine/RectSelect.js b/src/js/FiniteStateMachine/RectSelect.js index f1de0aa8..78b065a0 100644 --- a/src/js/FiniteStateMachine/RectSelect.js +++ b/src/js/FiniteStateMachine/RectSelect.js @@ -19,6 +19,7 @@ import { FSM } from "../FiniteStateMachine"; import { View } from "../View"; +import { Selector } from "../Selector"; /****************************************************************************** * Aladin Lite project * @@ -91,7 +92,7 @@ export class RectSelect extends FSM { h = -h; } - (typeof this.callback === 'function') && this.callback({ + let s = { x, y, w, h, label: 'rect', contains(s) { @@ -100,21 +101,20 @@ export class RectSelect extends FSM { bbox() { return {x, y, w, h} } - }); + }; + + (typeof this.callback === 'function') && this.callback(s); // TODO: remove these modes in the future view.aladin.showReticle(true) view.setCursor('default'); // execute general callback - if (view.callbacksByEventName) { - var callback = view.callbacksByEventName['select']; + if (view.aladin.callbacksByEventName) { + var callback = view.aladin.callbacksByEventName['select']; if (typeof callback === "function") { - // !todo - let selectedObjects = view.selectObjects(this); - console.log(selectedObjects) - - callback(selectedObjects); + let objList = Selector.getObjects(s, view); + callback(objList); } } view.setMode(View.PAN) diff --git a/src/js/GenericPointer.js b/src/js/GenericPointer.js index 49a493db..182ee498 100644 --- a/src/js/GenericPointer.js +++ b/src/js/GenericPointer.js @@ -13,7 +13,7 @@ import { Utils } from './Utils'; export let GenericPointer = function (view, e) { const xymouse = Utils.relMouseCoords(e); - let radec = view.aladin.pix2world(xymouse.x, xymouse.y); + let radec = view.aladin.pix2world(xymouse.x, xymouse.y, 'icrs'); if (radec) { // sky case if (view.aladin.getBaseImageLayer().isPlanetaryBody() === false) { @@ -23,7 +23,7 @@ export let GenericPointer = function (view, e) { // planetary body case else { // TODO: replace with actual value - const body = view.aladin.getBaseImageLayer().properties.hipsBody; + const body = view.aladin.getBaseImageLayer().hipsBody; PlanetaryFeaturesPointer.query(radec[0], radec[1], Math.min(80, view.fov / 20.0), body, view.aladin); } } else { diff --git a/src/js/HiPSProperties.js b/src/js/HiPSProperties.js index 3c29bb05..75c4c992 100644 --- a/src/js/HiPSProperties.js +++ b/src/js/HiPSProperties.js @@ -65,7 +65,6 @@ HiPSProperties.fetchFromID = async function(ID) { // Exactly one matching result = metadata[0]; } - return result; } } @@ -74,7 +73,7 @@ HiPSProperties.fetchFromUrl = async function(urlOrId) { try { urlOrId = new URL(urlOrId); } catch (e) { - // Relative path + // Relative path test try { urlOrId = Utils.getAbsoluteURL(urlOrId) diff --git a/src/js/ImageFITS.js b/src/js/ImageFITS.js index ba0ca390..e9a1bd18 100644 --- a/src/js/ImageFITS.js +++ b/src/js/ImageFITS.js @@ -29,15 +29,11 @@ *****************************************************************************/ import { ALEvent } from "./events/ALEvent.js"; import { ColorCfg } from "./ColorCfg.js"; -import { ImageLayer } from "./ImageLayer.js"; import { Utils } from "./Utils"; export let ImageFITS = (function () { - function ImageFITS(url, name, view, options, successCallback = undefined, errorCallback = undefined) { - this.view = view; - this.wasm = view.wasm; - + function ImageFITS(url, name, options, successCallback = undefined, errorCallback = undefined) { // Name of the layer this.layer = null; this.added = false; @@ -46,110 +42,102 @@ export let ImageFITS = (function () { this.url = url.toString(); this.id = url.toString(); - this.name = name; + this.name = name || this.url; this.imgFormat = "fits"; - this.properties = { - formats: ["fits"] - } + this.formats = ["fits"] // callbacks this.successCallback = successCallback; this.errorCallback = errorCallback; // initialize the color meta data here // set a asinh stretch by default if there is none - if (options) { + /*if (options) { options.stretch = options.stretch || "asinh"; - } + }*/ this.colorCfg = new ColorCfg(options); let self = this; - updateMetadata(self); - ImageLayer.update(self); - this.query = Promise.resolve(self); } + + // A cache storing directly the images to not query for the properties each time + ImageFITS.cache = {}; + + ImageFITS.prototype.setView = function(view) { + this.view = view; + } // @api ImageFITS.prototype.setOpacity = function (opacity) { let self = this; - updateMetadata(self, () => { + this._updateMetadata(() => { self.colorCfg.setOpacity(opacity); }); }; // @api ImageFITS.prototype.setBlendingConfig = function (additive = false) { - updateMetadata(this, () => { + this._updateMetadata(() => { this.colorCfg.setBlendingConfig(additive); }); }; // @api ImageFITS.prototype.setColormap = function (colormap, options) { - updateMetadata(this, () => { + this._updateMetadata(() => { this.colorCfg.setColormap(colormap, options); }); } // @api ImageFITS.prototype.setCuts = function (lowCut, highCut) { - updateMetadata(this, () => { + this._updateMetadata(() => { this.colorCfg.setCuts(lowCut, highCut); }); }; // @api ImageFITS.prototype.setGamma = function (gamma) { - updateMetadata(this, () => { + this._updateMetadata(() => { this.colorCfg.setGamma(gamma); }); }; - ImageFITS.prototype.getAvailableFormats = function() { - return this.properties.formats; - } - // @api ImageFITS.prototype.setSaturation = function (saturation) { - updateMetadata(this, () => { + this._updateMetadata(() => { this.colorCfg.setSaturation(saturation); }); }; ImageFITS.prototype.setBrightness = function (brightness) { - updateMetadata(this, () => { + this._updateMetadata(() => { this.colorCfg.setBrightness(brightness); }); }; ImageFITS.prototype.setContrast = function (contrast) { - updateMetadata(this, () => { + this._updateMetadata(() => { this.colorCfg.setContrast(contrast); }); }; - ImageFITS.prototype.metadata = function () { - return { - ...this.colorCfg.get(), - longitudeReversed: false, - imgFormat: this.imgFormat - }; - } - // Private method for updating the view with the new meta - var updateMetadata = function (self, callback = undefined) { + ImageFITS.prototype._updateMetadata = function (callback) { if (callback) { callback(); } // Tell the view its meta have changed try { - if (self.added) { - const metadata = self.metadata(); - self.wasm.setImageMetadata(self.layer, metadata); - - ALEvent.HIPS_LAYER_CHANGED.dispatchedTo(self.view.aladinDiv, { layer: self }); + if (this.added) { + this.view.wasm.setImageMetadata(this.layer, { + ...this.colorCfg.get(), + longitudeReversed: false, + imgFormat: this.imgFormat + }); + ALEvent.HIPS_LAYER_CHANGED.dispatchedTo(this.view.aladinDiv, { layer: this }); } } catch (e) { // Display the error message @@ -161,13 +149,19 @@ export let ImageFITS = (function () { this.layer = layer; let self = this; - const promise = self.wasm.addImageFITS({ + + const promise = self.view.wasm.addImageFITS({ layer: self.layer, url: self.url, - meta: self.metadata() + meta: { + ...this.colorCfg.get(), + longitudeReversed: false, + imgFormat: this.imgFormat + } }).then((imagesParams) => { // There is at least one entry in imageParams self.added = true; + self.children = []; let hduIdx = 0; @@ -176,7 +170,6 @@ export let ImageFITS = (function () { let image = new ImageFITS( imageParams.url, self.name + "_ext_" + hduIdx.toString(), - self.view, null, null, null @@ -185,6 +178,7 @@ export let ImageFITS = (function () { // 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 @@ -259,7 +253,7 @@ export let ImageFITS = (function () { ImageFITS.prototype.setAlpha = ImageFITS.prototype.setOpacity; ImageFITS.prototype.setColorCfg = function (colorCfg) { - updateMetadata(this, () => { + this._updateMetadata(() => { this.colorCfg = colorCfg; }); }; @@ -283,7 +277,7 @@ export let ImageFITS = (function () { // @api ImageFITS.prototype.readPixel = function (x, y) { - return this.wasm.readPixel(x, y, this.layer); + return this.view.wasm.readPixel(x, y, this.layer); }; return ImageFITS; diff --git a/src/js/ImageLayer.js b/src/js/ImageLayer.js deleted file mode 100644 index 7273b346..00000000 --- a/src/js/ImageLayer.js +++ /dev/null @@ -1,218 +0,0 @@ -export let ImageLayer = {}; - -ImageLayer.update = function (layer) { - const foundLayer = ImageLayer.contains(layer.id) - - const options = layer.metadata; - // The survey has not been found among the ones cached - if (foundLayer) { - foundLayer.options = options; - } else { - ImageLayer.LAYERS.push({ - id: layer.id, - name: layer.name, - url: layer.url, - options, - subtype: layer.subtype, - }); - } -} - -ImageLayer.contains = function(id) { - return ImageLayer.LAYERS.find((layer) => layer.id.endsWith(id)); -} - -ImageLayer.DEFAULT_SURVEY = { - id: "P/DSS2/color", - name: "DSS colored", - url: "https://alasky.cds.unistra.fr/DSS/DSSColor", - maxOrder: 9, - subtype: "survey", - tileSize: 512, - formats: ['jpeg'], - creatorDid: "ivo://CDS/P/DSS2/color", - dataproductSubtype: ['color'], - frame: "ICRS" -} - -ImageLayer.LAYERS = [ - ImageLayer.DEFAULT_SURVEY, - { - id: "P/2MASS/color", - name: "2MASS colored", - url: "https://alasky.cds.unistra.fr/2MASS/Color", - maxOrder: 9, - subtype: "survey", - }, - { - id: "P/DSS2/red", - name: "DSS2 Red (F+R)", - url: "https://alasky.cds.unistra.fr/DSS/DSS2Merged", - maxOrder: 9, - subtype: "survey", - // options - options: { - minCut: 1000.0, - maxCut: 10000.0, - colormap: "magma", - stretch: 'Linear', - imgFormat: "fits" - } - }, - { - id: "P/DM/I/350/gaiaedr3", - name: "Density map for Gaia EDR3 (I/350/gaiaedr3)", - url: "https://alasky.cds.unistra.fr/ancillary/GaiaEDR3/density-map", - maxOrder: 7, - subtype: "survey", - // options - options: { - minCut: 0, - maxCut: 12000, - stretch: 'asinh', - colormap: "rdylbu", - imgFormat: "fits", - } - }, - { - id: "P/PanSTARRS/DR1/g", - name: "PanSTARRS DR1 g", - url: "https://alasky.cds.unistra.fr/Pan-STARRS/DR1/g", - maxOrder: 11, - subtype: "survey", - // options - options: { - minCut: -34, - maxCut: 7000, - stretch: 'asinh', - colormap: "redtemperature", - imgFormat: "fits", - } - }, - { - id: "P/PanSTARRS/DR1/color-z-zg-g", - name: "PanSTARRS DR1 color", - url: "https://alasky.cds.unistra.fr/Pan-STARRS/DR1/color-z-zg-g", - maxOrder: 11, - subtype: "survey", - }, - { - id: "P/DECaPS/DR1/color", - name: "DECaPS DR1 color", - url: "https://alasky.cds.unistra.fr/DECaPS/DR1/color", - maxOrder: 11, - subtype: "survey", - }, - { - id: "P/Fermi/color", - name: "Fermi color", - url: "https://alasky.cds.unistra.fr/Fermi/Color", - maxOrder: 3, - subtype: "survey", - }, - { - id: "P/Finkbeiner", - name: "Halpha", - url: "https://alasky.cds.unistra.fr/FinkbeinerHalpha", - maxOrder: 3, - subtype: "survey", - // options - options: { - minCut: -10, - maxCut: 800, - colormap: "rdbu", - imgFormat: "fits", - } - }, - { - id: "P/GALEXGR6_7/NUV", - name: "GALEXGR6_7 NUV", - url: "http://alasky.cds.unistra.fr/GALEX/GALEXGR6_7_NUV/", - maxOrder: 8, - subtype: "survey", - }, - { - id: "P/IRIS/color", - name: "IRIS colored", - url: "https://alasky.cds.unistra.fr/IRISColor", - maxOrder: 3, - subtype: "survey", - }, - { - id: "P/Mellinger/color", - name: "Mellinger colored", - url: "https://alasky.cds.unistra.fr/MellingerRGB", - maxOrder: 4, - subtype: "survey", - }, - { - id: "P/SDSS9/color", - name: "SDSS9 colored", - url: "https://alasky.cds.unistra.fr/SDSS/DR9/color", - maxOrder: 10, - subtype: "survey", - }, - { - id: "P/SDSS9/g", - name: "SDSS9 band-g", - url: "https://alasky.cds.unistra.fr/SDSS/DR9/band-g", - maxOrder: 10, - subtype: "survey", - options: { - stretch: 'asinh', - colormap: "redtemperature", - imgFormat: 'fits' - } - }, - { - id: "P/SPITZER/color", - name: "IRAC color I1,I2,I4 - (GLIMPSE, SAGE, SAGE-SMC, SINGS)", - url: "http://alasky.cds.unistra.fr/Spitzer/SpitzerI1I2I4color/", - maxOrder: 9, - subtype: "survey", - }, - { - id: "P/VTSS/Ha", - name: "VTSS-Ha", - url: "https://alasky.cds.unistra.fr/VTSS/Ha", - maxOrder: 3, - subtype: "survey", - options: { - minCut: -10.0, - maxCut: 100.0, - colormap: "grayscale", - imgFormat: "fits" - } - }, - { - id: "xcatdb/P/XMM/PN/color", - name: "XMM PN colored", - url: "https://alasky.cds.unistra.fr/cgi/JSONProxy?url=https://saada.unistra.fr/PNColor", - maxOrder: 7, - subtype: "survey", - }, - { - id: "P/allWISE/color", - name: "AllWISE color", - url: "https://alasky.cds.unistra.fr/AllWISE/RGB-W4-W2-W1/", - maxOrder: 8, - subtype: "survey", - }, - { - id: "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", - subtype: "survey", - options: { - maxOrder: 9, - imgFormat: "jpg", - minOrder: 3, - } - }, -]; - -ImageLayer.getAvailableLayers = function () { - return ImageLayer.LAYERS; -}; diff --git a/src/js/ImageSurvey.js b/src/js/ImageSurvey.js index ac777c20..0084d30a 100644 --- a/src/js/ImageSurvey.js +++ b/src/js/ImageSurvey.js @@ -30,14 +30,13 @@ import { Utils } from "./Utils"; import { ALEvent } from "./events/ALEvent.js"; import { ColorCfg } from "./ColorCfg.js"; -import { ImageLayer } from "./ImageLayer.js"; import { HiPSProperties } from "./HiPSProperties.js"; let PropertyParser = {}; // Utilitary functions for parsing the properties and giving default values /// Mandatory tileSize property -PropertyParser.tileSize = function(options, properties = {}) { - let tileSize = (options && options.tileSize) || (properties.hips_tile_width && (+properties.hips_tile_width)) || 512; +PropertyParser.tileSize = function(properties) { + let tileSize = (properties && properties.hips_tile_width && (+properties.hips_tile_width)) || 512; // Check if the tile width size is a power of 2 if (tileSize & (tileSize - 1) !== 0) { @@ -48,35 +47,25 @@ PropertyParser.tileSize = function(options, properties = {}) { } /// Mandatory frame property -PropertyParser.frame = function(options, properties = {}) { - let frame = (options && options.cooFrame) || (properties.hips_body && "ICRSd") || properties.hips_frame; - - if (frame == "ICRS" || frame == "ICRSd" || frame == "equatorial" || frame == "j2000") { - frame = "ICRS"; - } else if (frame == "galactic") { - frame = "GAL"; - } else { - frame = "ICRS"; - console.warn('Invalid cooframe given: ' + cooFrame + '. Coordinate systems supported: "ICRS", "ICRSd", "j2000" or "galactic". ICRS is chosen by default'); - } - - return frame; +PropertyParser.cooFrame = function(properties) { + let cooFrame = (properties && properties.hips_body && "ICRSd") || (properties && properties.hips_frame) || "ICRS"; + return cooFrame; } /// Mandatory maxOrder property -PropertyParser.maxOrder = function(options, properties = {}) { - let maxOrder = (options && options.maxOrder) || (properties.hips_order && (+properties.hips_order)); +PropertyParser.maxOrder = function(properties) { + let maxOrder = properties && properties.hips_order && (+properties.hips_order); return maxOrder; } /// Mandatory minOrder property -PropertyParser.minOrder = function(options, properties = {}) { - const minOrder = (options && options.minOrder) || (properties.hips_order_min && (+properties.hips_order_min)) || 0; +PropertyParser.minOrder = function(properties) { + const minOrder = (properties && properties.hips_order_min && (+properties.hips_order_min)) || 0; return minOrder; } -PropertyParser.formats = function(options, properties = {}) { - let formats = properties.hips_tile_format || "jpeg"; +PropertyParser.formats = function(properties) { + let formats = properties && properties.hips_tile_format || "jpeg"; formats = formats.split(' ') .map((fmt) => fmt.toLowerCase()); @@ -84,8 +73,8 @@ PropertyParser.formats = function(options, properties = {}) { return formats; } -PropertyParser.initialFov = function(options, properties = {}) { - let initialFov = properties.hips_initial_fov && +properties.hips_initial_fov; +PropertyParser.initialFov = function(properties) { + let initialFov = properties && properties.hips_initial_fov && (+properties.hips_initial_fov); if (initialFov && initialFov < 0.00002777777) { initialFov = 360; @@ -94,13 +83,13 @@ PropertyParser.initialFov = function(options, properties = {}) { return initialFov; } -PropertyParser.skyFraction = function(options, properties = {}) { - const skyFraction = (properties.moc_sky_fraction && (+properties.moc_sky_fraction)) || 0.0; +PropertyParser.skyFraction = function(properties) { + const skyFraction = (properties && properties.moc_sky_fraction && (+properties.moc_sky_fraction)) || 0.0; return skyFraction; } -PropertyParser.cutouts = function(options, properties = {}) { - let cuts = properties.hips_pixel_cut && properties.hips_pixel_cut.split(" "); +PropertyParser.cutouts = function(properties) { + let cuts = properties && properties.hips_pixel_cut && properties.hips_pixel_cut.split(" "); const minCutout = cuts && parseFloat(cuts[0]); const maxCutout = cuts && parseFloat(cuts[1]); @@ -108,25 +97,32 @@ PropertyParser.cutouts = function(options, properties = {}) { return [minCutout, maxCutout]; } -PropertyParser.bitpix = function(options, properties = {}) { - const bitpix = properties.hips_pixel_bitpix && (+properties.hips_pixel_bitpix); +PropertyParser.bitpix = function(properties) { + const bitpix = properties && properties.hips_pixel_bitpix && (+properties.hips_pixel_bitpix); return bitpix; } -PropertyParser.dataproductSubtype = function(options, properties = {}) { - let dataproductSubtype = properties.dataproduct_subtype || "color"; +PropertyParser.dataproductSubtype = function(properties) { + let dataproductSubtype = (properties && properties.dataproduct_subtype) || "color"; dataproductSubtype = dataproductSubtype.split(" ") .map((subtype) => subtype.toLowerCase()); return dataproductSubtype; } -PropertyParser.isPlanetaryBody = function(options, properties = {}) { - return properties.hips_body !== undefined; +PropertyParser.isPlanetaryBody = function(properties) { + return properties && properties.hips_body !== undefined; } /** * @typedef {Object} ImageSurveyOptions * + * @property {string} [name] - The name of the survey to be displayed in the UI + * @property {string} [imgFormat] - Formats accepted 'webp', 'png', 'jpeg' or 'fits'. Will raise an error if the HiPS does not contain tiles in this format + * @property {CooFrame} [cooFrame="J2000"] - Coordinate frame of the survey tiles + * @property {number} [maxOrder] - The maximum HEALPix order of the HiPS, i.e the HEALPix order of the most refined tile images of the HiPS. + * @property {number} [numBitsPerPixel] - useful if you want to display the FITS tiles of a HiPS. It specifies the number of bits per pixel. Possible values are: + * -64: double, -32: float, 8: unsigned byte, 16: short, 32: integer 32 bits, 64: integer 64 bits + * @property {number} [tileSize] - The width of the HEALPix tile images. Mostly 512 pixels for can be 256, 128, 64, 32 * @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. @@ -138,12 +134,9 @@ PropertyParser.isPlanetaryBody = function(options, properties = {}) { * @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. - * @property {number} [maxOrder] - If not given, retrieved from the properties of the survey. * @property {number} [minOrder] - If not given, retrieved from the properties of the survey. * @property {boolean} [longitudeReversed=false] - Set it to True for planetary survey visualization - * @property {string} [imgFormat] - If not given, look into the properties to see the accepted format. The format is chosen from WEBP > PNG > JPEG > FITS (in this importance order). - * @property {string} [cooFrame] - If not given, look into the properties. If it is a planet, then ICRS is chosen, otherwise its hips_frame key is read. If no value is found in the properties, ICRS is chosen by default. - */ +*/ export let ImageSurvey = (function () { /** * The object describing an image survey @@ -151,36 +144,57 @@ export let ImageSurvey = (function () { * @class * @constructs ImageSurvey * - * @param {string} [id] - Optional, a uniq id for the survey. See {@link https://aladin.cds.unistra.fr/hips/list|here} for the list of IDs. - * Keep in mind that it is better to directly provide an url as it will not request our mocserver first to get final survey tiles retrieval url. - * @param {string} [name] - The name of the survey to be displayed in the UI - * @param {string} url - The url where the survey is located. Check the hips list {@link https://aladin.cds.unistra.fr/hips/list|here} for the valid survey urls to display. + * @param {string} id - Mandatory unique identifier for the layer. + * Can be an arbitrary name + * @param {string} url - Can be an url to the survey or 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] - The option for the survey * - * @description Prefer provide an url better than an id. If both are given, the url will be requested first for the survey data. + * @description For the `url` param, prefer provide an url better than an CDS ID. Giving a CDS ID will do a query to the mocserver more but will also check for the presence of faster HiPS nodes. */ - function ImageSurvey(id, name, url, options, view) { - this.view = view; + function ImageSurvey(id, url, options) { this.added = false; + // Unique identifier for a survey this.id = id; - this.name = name; + this.name = options.name || id || url; this.subtype = "survey"; - this.properties = options.properties || {}; + this.url = url; + this.maxOrder = options.maxOrder; + this.minOrder = options.minOrder || 0; + this.cooFrame = options.cooFrame; + this.tileSize = options.tileSize; + this.longitudeReversed = options.longitudeReversed === undefined ? false : options.longitudeReversed; + this.imgFormat = options.imgFormat; + this.numBitsPerPixel = options.numBitsPerPixel; + this.creatorDid = options.creatorDid; + this.colorCfg = new ColorCfg(options); + }; + ImageSurvey.prototype.setView = function(view) { let self = this; - self.query = (async () => { - if (!options.properties) { - let obsTitle, creatorDid, maxOrder, frame, tileSize, formats, minCutout, maxCutout, bitpix, skyFraction, minOrder, initialFov, initialRa, initialDec, hipsBody, isPlanetaryBody, dataproductSubtype; + self.view = view; + + let isMOCServerToBeQueried = true; + if (this.imgFormat === 'fits') { + // a fits is given + isMOCServerToBeQueried = !(this.maxOrder && this.url && this.imgFormat && this.tileSize && this.cooFrame && this.numBitsPerPixel) + } else { + isMOCServerToBeQueried = !(this.maxOrder && this.url && this.imgFormat && this.tileSize && this.cooFrame); + } + + self.query = (async () => { + if (isMOCServerToBeQueried) { let properties; + let isCDSId = false; try { - properties = await HiPSProperties.fetchFromUrl(url) + properties = await HiPSProperties.fetchFromUrl(self.url) .catch(async (e) => { // url not valid so we try with the id try { - return await HiPSProperties.fetchFromID(id); + isCDSId = true; + return await HiPSProperties.fetchFromID(self.url); } catch(e) { throw e; } @@ -189,200 +203,201 @@ export let ImageSurvey = (function () { throw e; } - obsTitle = properties.obs_title; - creatorDid = properties.creator_did; - // Set it to a default value - if (!properties.hips_service_url) { - throw 'no valid service URL for retrieving the tiles' + if (isCDSId) { + self.url = properties.hips_service_url; } - url = Utils.fixURLForHTTPS(properties.hips_service_url); + //obsTitle = properties.obs_title; + self.creatorDid = properties.creator_did || self.creatorDid; + // url - // Request all the properties to see which mirror is the fastest - HiPSProperties.getFasterMirrorUrl(properties) - .then((url) => { - self._setUrl(url); - }) - .catch(e => { - //alert(e); - console.error(e); - // the survey has been added so we remove it from the stack - self.view.removeImageLayer(self.layer) - //throw e; - }) + if (!self.url) { + self.url = properties.hips_service_url + if (!self.url) { + throw 'no valid service URL for retrieving the tiles' + } + + self.url = Utils.fixURLForHTTPS(self.url); + + // Request all the properties to see which mirror is the fastest + HiPSProperties.getFasterMirrorUrl(properties) + .then((url) => { + if (self.url !== url) { + console.info("Change url of ", self.id, " from ", self.url, " to ", url) + + // If added to the backend, then we need to tell it the url has changed + if (self.added) { + self.view.wasm.setHiPSUrl(self.url, url); + } + + self.url = url; + + // save the new url to the cache + ImageSurvey.cache[self.id].url = self.url; + } + }) + /*.catch(e => { + //alert(e); + console.error(e); + // the survey has been added so we remove it from the stack + self.view.removeImageLayer(self.layer) + //throw e; + })*/ + } // Max order - maxOrder = PropertyParser.maxOrder(options, properties); + self.maxOrder = PropertyParser.maxOrder(properties) || self.maxOrder; // Tile size - tileSize = PropertyParser.tileSize(options, properties); + self.tileSize = PropertyParser.tileSize(properties) || self.tileSize; // Tile formats - formats = PropertyParser.formats(options, properties); + self.formats = PropertyParser.formats(properties) || self.formats; // min order - minOrder = PropertyParser.minOrder(options, properties); + self.minOrder = PropertyParser.minOrder(properties) || self.minOrder; // Frame - frame = PropertyParser.frame(options, properties); + self.cooFrame = PropertyParser.cooFrame(properties) || self.cooFrame; // sky fraction - skyFraction = PropertyParser.skyFraction(options, properties); + self.skyFraction = PropertyParser.skyFraction(properties); // Initial fov/ra/dec - initialFov = PropertyParser.initialFov(options, properties); - initialRa = +properties.hips_initial_ra; - initialDec = +properties.hips_initial_dec; + self.initialFov = PropertyParser.initialFov(properties); + self.initialRa = properties && properties.hips_initial_ra && (+properties.hips_initial_ra); + self.initialDec = properties && properties.hips_initial_dec && (+properties.hips_initial_dec); // Cutouts - [minCutout, maxCutout] = PropertyParser.cutouts(options, properties); + const cutoutFromProperties = PropertyParser.cutouts(properties); + self.minCut = cutoutFromProperties[0]; + self.maxCut = cutoutFromProperties[1]; // Bitpix - bitpix = PropertyParser.bitpix(options, properties); - - // Dataproduct subtype - dataproductSubtype = PropertyParser.dataproductSubtype(options, properties); + self.numBitsPerPixel = PropertyParser.bitpix(properties) || self.numBitsPerPixel; // HiPS body - isPlanetaryBody = PropertyParser.isPlanetaryBody(options, properties); if (properties.hips_body) { - hipsBody = properties.hips_body; + self.hipsBody = properties.hips_body; + // Use the property to define and check some user given infos + // Longitude reversed + self.longitudeReversed = true; } - self.properties = { - creatorDid, - obsTitle, - url, - maxOrder, - frame, - tileSize, - formats, - minCutout, - maxCutout, - bitpix, - skyFraction, - minOrder, - hipsInitialFov: initialFov, - hipsInitialRa: initialRa, - hipsInitialDec: initialDec, - dataproductSubtype, - isPlanetaryBody, - hipsBody - }; + // Give a better name if we have the HiPS metadata + self.name = self.name || properties.obsTitle; + + self.cooFrame = properties.cooFrame || self.cooFrame; } - // Give a better name if we have the HiPS metadata - self.name = self.name || self.properties.obsTitle; - - // Use the property to define and check some user given infos - // Longitude reversed - let longitudeReversed = false; - if (options && options.longitudeReversed === true) { - longitudeReversed = true; - } - - if (self.properties.hipsBody) { - longitudeReversed = true; - } - - self.longitudeReversed = longitudeReversed; + self.creatorDid = self.creatorDid || self.id || self.url; // Image format - let imgFormat = options && options.imgFormat; - if (imgFormat) { + if (self.imgFormat) { // transform to lower case - imgFormat = imgFormat.toLowerCase(); + self.imgFormat = self.imgFormat.toLowerCase(); // convert JPG -> JPEG - if (imgFormat === "jpg") { - imgFormat = "jpeg"; + if (self.imgFormat === "jpg") { + self.imgFormat = "jpeg"; } // user wants a fits but the properties tells this format is not available - if (imgFormat === "fits" && self.properties.formats && self.properties.formats.indexOf('fits') < 0) { + if (self.imgFormat === "fits" && self.formats && self.formats.indexOf('fits') < 0) { throw self.name + " does not provide fits tiles"; } - if (imgFormat === "webp" && self.properties.formats && self.properties.formats.indexOf('webp') < 0) { + if (self.imgFormat === "webp" && self.formats && self.formats.indexOf('webp') < 0) { throw self.name + " does not provide webp tiles"; } - if (imgFormat === "png" && self.properties.formats && self.properties.formats.indexOf('png') < 0) { + if (self.imgFormat === "png" && self.formats && self.formats.indexOf('png') < 0) { throw self.name + " does not provide png tiles"; } - if (imgFormat === "jpeg" && self.properties.formats && self.properties.formats.indexOf('jpeg') < 0) { + if (self.imgFormat === "jpeg" && self.formats && self.formats.indexOf('jpeg') < 0) { throw self.name + " does not provide jpeg tiles"; } } else { // user wants nothing then we choose one from the properties - if (self.properties.formats.indexOf('webp') >= 0) { - imgFormat = "webp"; - } else if (self.properties.formats.indexOf('png') >= 0) { - imgFormat = "png"; - } else if (self.properties.formats.indexOf('jpeg') >= 0) { - imgFormat = "jpeg"; - } else if (self.properties.formats.indexOf('fits') >= 0) { - imgFormat = "fits"; + if (self.formats.indexOf('webp') >= 0) { + self.imgFormat = "webp"; + } else if (self.formats.indexOf('png') >= 0) { + self.imgFormat = "png"; + } else if (self.formats.indexOf('jpeg') >= 0) { + self.imgFormat = "jpeg"; + } else if (self.formats.indexOf('fits') >= 0) { + self.imgFormat = "fits"; } else { - throw "Unsupported format(s) found in the properties: " + self.properties.formats; + throw "Unsupported format(s) found in the properties: " + self.formats; } } - self.imgFormat = imgFormat; - - // Initialize the color meta data here + // Cutouts let minCut, maxCut; - if (imgFormat === "fits") { + if (self.imgFormat === "fits") { // Take into account the default cuts given by the property file (this is true especially for FITS HiPSes) - minCut = self.colorCfg.minCut || (options && options.minCut) || self.properties.minCutout || 0.0; - maxCut = self.colorCfg.maxCut || (options && options.maxCut) || self.properties.maxCutout || 1.0; + minCut = self.colorCfg.minCut || self.minCut || 0.0; + maxCut = self.colorCfg.maxCut || self.maxCut || 1.0; } else { - minCut = self.colorCfg.minCut || (options && options.minCut) || 0.0; - maxCut = self.colorCfg.maxCut || (options && options.maxCut) || 1.0; + minCut = self.colorCfg.minCut || 0.0; + maxCut = self.colorCfg.maxCut || 1.0; } self.colorCfg.setCuts(minCut, maxCut); - ImageLayer.update(self); + // Coo frame + if (self.cooFrame == "ICRS" || self.cooFrame == "ICRSd" || self.cooFrame == "equatorial" || self.cooFrame == "j2000") { + self.cooFrame = "ICRS"; + } else if (self.cooFrame == "galactic") { + self.cooFrame = "GAL"; + } else { + self.cooFrame = "ICRS"; + console.warn('Invalid cooframe given: ' + self.cooFrame + '. Coordinate systems supported: "ICRS", "ICRSd", "j2000" or "galactic". ICRS is chosen by default'); + } + + self.formats = self.formats || [self.imgFormat]; + + self._save(); return self; })(); - }; - - ImageSurvey.fromLayerOptions = function(aladin, options) { - return new ImageSurvey( - options.id, - options.name, - options.url, - { - properties: options, - ...options.options - }, - aladin.view - ); } - ImageSurvey.prototype._setUrl = function (url) { - if (this.properties.url !== url) { - console.info("Change url of ", this.id, " from ", this.properties.url, " to ", url) + ImageSurvey.prototype._save = function() { + let self = this; - // If added to the backend, then we need to tell it the url has changed - if (this.added) { - this.view.wasm.setHiPSUrl(this.properties.url, url); - } - - this.properties.url = url; + let surveyOpt = { + creatorDid: self.creatorDid, + name: self.name, + url: self.url, + cooFrame: self.cooFrame, + maxOrder: self.maxOrder, + tileSize: self.tileSize, + imgFormat: self.imgFormat, } - } + + if (self.numBitsPerPixel) { + surveyOpt.numBitsPerPixel = self.numBitsPerPixel; + } + + ImageSurvey.cache[self.id] = { + // Erase by the cache already put values which is considered + // as the ground truth + ...ImageSurvey.cache[self.id], + // append new important infos from the properties queried + ...surveyOpt, + } + } + /** - * Checks if the ImageSurvey represents a planetary body. - * - * This method returns a boolean indicating whether the ImageSurvey corresponds to a planetary body, e.g. the earth or a celestial body. - * - * @memberof ImageSurvey - * - * @returns {boolean} Returns true if the ImageSurvey represents a planetary body; otherwise, returns false. - */ + * Checks if the ImageSurvey represents a planetary body. + * + * This method returns a boolean indicating whether the ImageSurvey corresponds to a planetary body, e.g. the earth or a celestial body. + * + * @memberof ImageSurvey + * + * @returns {boolean} Returns true if the ImageSurvey represents a planetary body; otherwise, returns false. + */ ImageSurvey.prototype.isPlanetaryBody = function() { - return this.properties.isPlanetaryBody; + return self.hipsBody !== undefined;; } /** @@ -419,7 +434,7 @@ export let ImageSurvey = (function () { // Check the properties to see if the given format is available among the list // If the properties have not been retrieved yet, it will be tested afterwards - const availableFormats = self.properties.formats; + const availableFormats = self.formats; // user wants a fits but the metadata tells this format is not available if (imgFormat === "fits" && availableFormats.indexOf('fits') < 0) { throw self.id + " does not provide fits tiles"; @@ -439,8 +454,8 @@ export let ImageSurvey = (function () { // Switch from png/webp/jpeg to fits if ((self.imgFormat === 'png' || self.imgFormat === "webp" || self.imgFormat === "jpeg") && imgFormat === 'fits') { - if (self.properties.minCutout && self.properties.maxCutout) { - self.setCuts(self.properties.minCutout, self.properties.maxCutout) + if (self.minCut && self.maxCut) { + self.setCuts(self.minCut, self.maxCut) } // Switch from fits to png/webp/jpeg } else if (self.imgFormat === "fits") { @@ -453,7 +468,7 @@ export let ImageSurvey = (function () { }) }; - /** + /** * Sets the opacity factor when rendering the ImageSurvey * * @memberof ImageSurvey @@ -461,7 +476,7 @@ export let ImageSurvey = (function () { * @returns {string[]} Returns the formats accepted for the survey, i.e. the formats of tiles that are availables. Could be PNG, WEBP, JPG and FITS. */ ImageSurvey.prototype.getAvailableFormats = function() { - return this.properties.formats; + return this.formats; } /** @@ -624,14 +639,12 @@ export let ImageSurvey = (function () { // Tell the view its meta have changed try { - this.metadata = { - ...this.colorCfg.get(), - longitudeReversed: this.longitudeReversed, - imgFormat: this.imgFormat - }; - if (this.added) { - this.view.wasm.setImageMetadata(this.layer, this.metadata); + this.view.wasm.setImageMetadata(this.layer, { + ...this.colorCfg.get(), + longitudeReversed: this.longitudeReversed, + imgFormat: this.imgFormat + }); // once the meta have been well parsed, we can set the meta ALEvent.HIPS_LAYER_CHANGED.dispatchedTo(this.view.aladinDiv, { layer: this }); } @@ -643,17 +656,31 @@ export let ImageSurvey = (function () { ImageSurvey.prototype.add = function (layer) { this.layer = layer; - - this.metadata = { - ...this.colorCfg.get(), - longitudeReversed: this.longitudeReversed, - imgFormat: this.imgFormat - }; + let self = this; this.view.wasm.addImageSurvey({ layer, - properties: this.properties, - meta: this.metadata, + properties: { + creatorDid: self.creatorDid, + url: self.url, + maxOrder: self.maxOrder, + cooFrame: self.cooFrame, + tileSize: self.tileSize, + formats: self.formats, + bitpix: self.numBitsPerPixel, + skyFraction: self.skyFraction, + minOrder: self.minOrder, + hipsInitialFov: self.initialFov, + hipsInitialRa: self.initialRa, + hipsInitialDec: self.initialDec, + isPlanetaryBody: self.isPlanetaryBody(), + hipsBody: self.hipsBody + }, + meta: { + ...this.colorCfg.get(), + longitudeReversed: this.longitudeReversed, + imgFormat: this.imgFormat + }, }); this.added = true; @@ -696,7 +723,203 @@ export let ImageSurvey = (function () { return this.view.wasm.readPixel(x, y, this.layer); }; - ImageSurvey.DEFAULT_SURVEY_ID = "P/DSS2/color"; + ImageSurvey.DEFAULT_SURVEY_ID = "DSS2_color"; + + // A cache storing directly surveys important information to not query for the properties each time + ImageSurvey.cache = { + DSS2_color: { + creatorDid: "ivo://CDS/P/DSS2/color", + name: "DSS colored", + url: "https://alasky.cds.unistra.fr/DSS/DSSColor", + maxOrder: 9, + tileSize: 512, + imgFormat: 'jpeg', + cooFrame: "ICRS" + }, + MASS2_color: { + creatorDid: "ivo://CDS/P/2MASS/color", + name: "2MASS colored", + url: "https://alasky.cds.unistra.fr/2MASS/Color", + maxOrder: 9, + tileSize: 512, + imgFormat: 'jpeg', + cooFrame: "ICRS" + }, + DSS2_red: { + creatorDid: "ivo://CDS/P/DSS2/red", + name: "DSS2 Red (F+R)", + url: "https://alasky.cds.unistra.fr/DSS/DSS2Merged", + maxOrder: 9, + tileSize: 512, + imgFormat: 'fits', + cooFrame: "ICRS", + numBitsPerPixel: 16, + // options + minCut: 1000.0, + maxCut: 10000.0, + colormap: "magma", + stretch: 'Linear', + imgFormat: "fits" + }, + GAIA_EDR3: { + creatorDid: "ivo://CDS/P/DM/I/350/gaiaedr3", + name: "Density map for Gaia EDR3 (I/350/gaiaedr3)", + url: "https://alasky.cds.unistra.fr/ancillary/GaiaEDR3/density-map", + maxOrder: 7, + tileSize: 512, + numBitsPerPixel: -32, + cooFrame: "ICRS", + minCut: 0, + maxCut: 12000, + stretch: 'asinh', + colormap: "rdylbu", + imgFormat: "fits", + }, + PanSTARRS_DR1_g: { + creatorDid: "ivo://CDS/P/PanSTARRS/DR1/g", + name: "PanSTARRS DR1 g", + url: "https://alasky.cds.unistra.fr/Pan-STARRS/DR1/g", + maxOrder: 11, + tileSize: 512, + imgFormat: 'fits', + cooFrame: "ICRS", + numBitsPerPixel: -32, + // options + minCut: -34, + maxCut: 7000, + stretch: 'asinh', + colormap: "redtemperature", + }, + PanSTARRS_DR1_color: { + creatorDid: "ivo://CDS/P/PanSTARRS/DR1/color-z-zg-g", + name: "PanSTARRS DR1 color", + url: "https://alasky.cds.unistra.fr/Pan-STARRS/DR1/color-z-zg-g", + maxOrder: 11, + tileSize: 512, + imgFormat: 'jpeg', + cooFrame: "ICRS", + }, + DECaPS_DR2_color: { + creatorDid: "ivo://CDS/P/DECaPS/DR2/color", + name: "DECaPS DR2 color", + url: "https://alasky.cds.unistra.fr/DECaPS/DR2/CDS_P_DECaPS_DR2_color/", + maxOrder: 11, + cooFrame: "equatorial", + tileSize: 512, + imgFormat: 'png', + }, + Fermi_color: { + creatorDid: "ivo://CDS/P/Fermi/color", + name: "Fermi color", + url: "https://alasky.cds.unistra.fr/Fermi/Color", + maxOrder: 3, + imgFormat: 'jpeg', + tileSize: 512, + cooFrame: 'equatorial' + }, + /* + { + id: "P/Finkbeiner", + name: "Halpha", + url: "https://alasky.cds.unistra.fr/FinkbeinerHalpha", + maxOrder: 3, + subtype: "survey", + // options + options: { + minCut: -10, + maxCut: 800, + colormap: "rdbu", + imgFormat: "fits", + } + }, + { + id: "P/GALEXGR6_7/NUV", + name: "GALEXGR6_7 NUV", + url: "http://alasky.cds.unistra.fr/GALEX/GALEXGR6_7_NUV/", + maxOrder: 8, + subtype: "survey", + }, + { + id: "P/IRIS/color", + name: "IRIS colored", + url: "https://alasky.cds.unistra.fr/IRISColor", + maxOrder: 3, + subtype: "survey", + }, + { + id: "P/Mellinger/color", + name: "Mellinger colored", + url: "https://alasky.cds.unistra.fr/MellingerRGB", + maxOrder: 4, + subtype: "survey", + }, + { + id: "P/SDSS9/color", + name: "SDSS9 colored", + url: "https://alasky.cds.unistra.fr/SDSS/DR9/color", + maxOrder: 10, + subtype: "survey", + }, + { + id: "P/SDSS9/g", + name: "SDSS9 band-g", + url: "https://alasky.cds.unistra.fr/SDSS/DR9/band-g", + maxOrder: 10, + subtype: "survey", + options: { + stretch: 'asinh', + colormap: "redtemperature", + imgFormat: 'fits' + } + }, + { + id: "P/SPITZER/color", + name: "IRAC color I1,I2,I4 - (GLIMPSE, SAGE, SAGE-SMC, SINGS)", + url: "http://alasky.cds.unistra.fr/Spitzer/SpitzerI1I2I4color/", + maxOrder: 9, + subtype: "survey", + }, + { + id: "P/VTSS/Ha", + name: "VTSS-Ha", + url: "https://alasky.cds.unistra.fr/VTSS/Ha", + maxOrder: 3, + subtype: "survey", + options: { + minCut: -10.0, + maxCut: 100.0, + colormap: "grayscale", + imgFormat: "fits" + } + }, + { + id: "xcatdb/P/XMM/PN/color", + name: "XMM PN colored", + url: "https://alasky.cds.unistra.fr/cgi/JSONProxy?url=https://saada.unistra.fr/PNColor", + maxOrder: 7, + subtype: "survey", + }, + { + id: "P/allWISE/color", + name: "AllWISE color", + url: "https://alasky.cds.unistra.fr/AllWISE/RGB-W4-W2-W1/", + maxOrder: 8, + subtype: "survey", + }, + { + id: "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", + subtype: "survey", + options: { + maxOrder: 9, + imgFormat: "jpg", + minOrder: 3, + } + },*/ + }; return ImageSurvey; })(); diff --git a/src/js/Selector.js b/src/js/Selector.js index 50ba5c8d..c834403b 100644 --- a/src/js/Selector.js +++ b/src/js/Selector.js @@ -92,12 +92,10 @@ export class Selector { return; } - var objList = []; var cat, sources, s; var footprints, f; var objListPerCatalog = []; - if (view.catalogs) { for (var k = 0; k < view.catalogs.length; k++) { cat = view.catalogs[k]; @@ -133,6 +131,9 @@ export class Selector { } } + console.log(objList) + + return objList; } } \ No newline at end of file diff --git a/src/js/View.js b/src/js/View.js index c677d7a6..9c2681ce 100644 --- a/src/js/View.js +++ b/src/js/View.js @@ -50,10 +50,8 @@ 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 { Icon } from "./gui/Widgets/Icon.js"; -import openerIconUrl from '../../assets/icons/loading.svg' import { ImageSurvey } from "./ImageSurvey.js"; -import { ImageLayer } from "./ImageLayer.js"; +import { ImageFITS } from "./ImageFITS.js"; export let View = (function () { @@ -84,9 +82,6 @@ export let View = (function () { callback(self.wasm); }); - - // Retrieve all the possible colormaps - ColorCfg.COLORMAPS = this.wasm.getAvailableColormapList(); } catch (e) { // For browsers not supporting WebGL2: // 1. Print the original exception message in the console @@ -128,7 +123,6 @@ export let View = (function () { this.aladinDiv.ondragover = Utils.dragOverHandler; - //this.location = location; this.mustClearCatalog = true; this.mode = View.PAN; @@ -139,18 +133,8 @@ export let View = (function () { var lon, lat; lon = lat = 0; - this.projection = ProjectionEnum.SIN; - this.viewCenter = { lon: lon, lat: lat }; // position of center of view - - this.cooFrame = CooFrameEnum.fromString(this.options.cooFrame, CooFrameEnum.J2000); - - // Frame setting - this.changeFrame(this.cooFrame); - - this.selector = new Selector(this); - - // Zoom starting setting + // FoV init settings const si = 500000.0; const alpha = 40.0; let initialFov = this.options.fov || 180.0; @@ -160,7 +144,23 @@ export let View = (function () { initialDistance: undefined, initialAccDelta: Math.pow(si / initialFov, 1.0 / alpha) }; + + // Projection definition + const projName = (this.options && this.options.projection) || "SIN"; + this.setProjection(projName) + + // Then set the zoom properly once the projection is defined this.setZoom(initialFov); + + // Target position settings + this.viewCenter = { lon, lat }; // position of center of view + + // Coo frame setting + const cooFrame = CooFrameEnum.fromString(this.options.cooFrame, CooFrameEnum.J2000); + this.changeFrame(cooFrame); + + this.selector = new Selector(this); + // current reference image survey displayed this.imageLayers = new Map(); @@ -631,13 +631,9 @@ export let View = (function () { // Take as start cut values what is inside the properties // If the cuts are not defined in the metadata of the survey // then we take what has been defined by the user - if (imageLayer.imgFormat === "fits") { - cutMinInit = imageLayer.properties.minCutout || imageLayer.getColorCfg().minCut || 0.0; - cutMaxInit = imageLayer.properties.maxCutout || imageLayer.getColorCfg().maxCut || 1.0; - } else { - cutMinInit = imageLayer.getColorCfg().minCut || 0.0; - cutMaxInit = imageLayer.getColorCfg().maxCut || 1.0; - } + cutMinInit = imageLayer.getColorCfg().minCut || 0.0; + cutMaxInit = imageLayer.getColorCfg().maxCut || 1.0; + } } @@ -1353,6 +1349,7 @@ export let View = (function () { this.selection = Selector.getObjects(selection, this); } + if (this.selection.length > 0) { this.selection.forEach((objListPerCatalog) => { objListPerCatalog.forEach((obj) => obj.select()) @@ -1535,6 +1532,10 @@ export let View = (function () { }; View.prototype.setOverlayImageLayer = function (imageLayer, layer = "overlay") { + // set the view to the image layer object + // do the properties query if needed + imageLayer.setView(this); + // register its promise this.imageLayersBeingQueried.set(layer, imageLayer); @@ -1604,13 +1605,17 @@ export let View = (function () { } // change the view frame in case we have a planetary hips loaded - if (imageLayer.properties.hipsBody) { + if (imageLayer.hipsBody) { if (this.options.showFrame) { this.aladin.setFrame('J2000d'); } } }) .catch((e) => { + // remove it from the cache + delete ImageSurvey.cache[imageLayer.id] + delete ImageFITS.cache[imageLayer.id] + throw e; }) .finally(() => { @@ -1629,9 +1634,7 @@ export let View = (function () { if (noMoreLayersToWaitFor) { if (self.empty) { // no promises to launch! - const dssLayerOptions = ImageLayer.DEFAULT_SURVEY - - self.aladin.setBaseImageLayer(ImageSurvey.fromLayerOptions(self, dssLayerOptions)); + //self.aladin.setBaseImageLayer(self.aladin.createImageSurvey(ImageSurvey.DEFAULT_SURVEY_ID)); } else { // there is surveys that have been queried // rename the first overlay layer to "base" @@ -1758,7 +1761,7 @@ export let View = (function () { projName = 'SIN' } - if (this.projection.id === ProjectionEnum[projName].id) { + if (this.projection && this.projection.id === ProjectionEnum[projName].id) { return; } diff --git a/src/js/gui/Box/CatalogQueryBox.js b/src/js/gui/Box/CatalogQueryBox.js index 03ff7ec3..99ddb21d 100644 --- a/src/js/gui/Box/CatalogQueryBox.js +++ b/src/js/gui/Box/CatalogQueryBox.js @@ -248,9 +248,12 @@ import { CtxMenuActionButtonOpener } from "../Button/CtxMenuOpener.js"; if (self.box) { self.box.remove(); } - self.box = new ConeSearchBox(aladin); + // output the resulting cone search in the icrs frame + self.box = new ConeSearchBox(aladin, {frame: 'icrs'}); self.box.attach({ callback: (cs) => { + // the cone search services are asking for + self.fnIdSelected('coneSearch', { baseURL: self.selectedItem.cs_service_url, id: self.selectedItem.ID, diff --git a/src/js/gui/Box/ConeSearchBox.js b/src/js/gui/Box/ConeSearchBox.js index 6da3d491..a8f3adc0 100644 --- a/src/js/gui/Box/ConeSearchBox.js +++ b/src/js/gui/Box/ConeSearchBox.js @@ -33,7 +33,7 @@ import { Angle } from "../../libs/astro/angle.js"; *****************************************************************************/ export class ConeSearchBox extends Box { - constructor(aladin) { + constructor(aladin, options) { let self; let selectorBtn = new ConeSearchActionButton({ tooltip: {content: 'Select the area to query the catalogue with', position: {direction: 'left'}}, @@ -43,8 +43,8 @@ import { Angle } from "../../libs/astro/angle.js"; action(circle) { // convert to ra, dec and radius in deg try { - let [ra, dec] = aladin.pix2world(circle.x, circle.y); - let radius = aladin.angularDist(circle.x, circle.y, circle.x + circle.r, circle.y); + let [ra, dec] = aladin.pix2world(circle.x, circle.y, options.frame); + let radius = aladin.angularDist(circle.x, circle.y, circle.x + circle.r, circle.y, options.frame); //var hlon = this.lon/15.0; //var strlon = Numbers.toSexagesimal(hlon, this.prec+1, false); @@ -160,8 +160,6 @@ import { Angle } from "../../libs/astro/angle.js"; aladin.aladinDiv ) - // hide by default - //console.log("hide cone search") this._hide(); self = this; diff --git a/src/js/gui/Button/ConeSearch.js b/src/js/gui/Button/ConeSearch.js index f7de009d..139dca8f 100644 --- a/src/js/gui/Button/ConeSearch.js +++ b/src/js/gui/Button/ConeSearch.js @@ -43,8 +43,8 @@ export class ConeSearchActionButton extends ActionButton { // Constructor constructor(options, aladin) { super({ + size: 'medium', icon: { - size: 'medium', monochrome: true, url: targetIconUrl }, @@ -65,7 +65,5 @@ export class ConeSearchActionButton extends ActionButton { }) } }) - - this.addClass('medium-sized-icon') } } \ No newline at end of file diff --git a/src/js/gui/CtxMenu/OverlayStack.js b/src/js/gui/CtxMenu/OverlayStack.js index 2d192a3e..463ff5f5 100644 --- a/src/js/gui/CtxMenu/OverlayStack.js +++ b/src/js/gui/CtxMenu/OverlayStack.js @@ -44,10 +44,10 @@ import hideIconUrl from '../../../../assets/icons/hide.svg'; import removeIconUrl from '../../../../assets/icons/remove.svg'; import editIconUrl from '../../../../assets/icons/edit.svg'; import { ImageFITS } from "../../ImageFITS.js"; -import { ImageLayer } from "../../ImageLayer.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 = { @@ -293,10 +293,11 @@ export class OverlayStack extends ContextMenu { self._hide(); self.aladin.select('circle', c => { - let [ra, dec] = self.aladin.pix2world(c.x, c.y); + 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); if (ra && dec && radius) { + // the moc needs a let moc = A.MOCFromCircle( {ra, dec, radius}, {name: 'cone', lineWidth: 3.0}, @@ -321,10 +322,10 @@ export class OverlayStack extends ContextMenu { self.aladin.select('rect', r => { try { - let [ra1, dec1] = self.aladin.pix2world(r.x, r.y); - let [ra2, dec2] = self.aladin.pix2world(r.x + r.w, r.y); - let [ra3, dec3] = self.aladin.pix2world(r.x + r.w, r.y + r.h); - let [ra4, dec4] = self.aladin.pix2world(r.x, r.y + r.h); + 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( { @@ -356,7 +357,7 @@ export class OverlayStack extends ContextMenu { let ra = [] let dec = [] for (const v of p.vertices) { - let [lon, lat] = self.aladin.pix2world(v.x, v.y); + let [lon, lat] = self.aladin.pix2world(v.x, v.y, 'j2000'); ra.push(lon) dec.push(lat) } @@ -528,15 +529,19 @@ export class OverlayStack extends ContextMenu { return; }*/ - const defaultLayers = ImageLayer.LAYERS.sort(function (a, b) { + 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.properties.url + '/preview.jpg'; + let backgroundUrl = layer.url + '/preview.jpg'; let cssStyle = { height: 'fit-content', }; @@ -597,7 +602,6 @@ export class OverlayStack extends ContextMenu { self._hide(); - console.log("kjkj") //self.aladin.selectLayer(layer.layer); //self.attach() @@ -618,35 +622,35 @@ export class OverlayStack extends ContextMenu { tooltip: {content: 'Add coverage', position: {direction: 'bottom'}}, toggled: (() => { let overlays = self.aladin.getOverlays(); - let found = overlays.find((o) => o.type === "moc" && o.name === layer.properties.obsTitle); + let found = overlays.find((o) => o.type === "moc" && o.name === layer.name); return found !== undefined; })(), actionOn: (e) => { - let moc = A.MOCFromURL(layer.properties.url + '/Moc.fits', {lineWidth: 3, name: layer.properties.obsTitle}); + let moc = A.MOCFromURL(layer.url + '/Moc.fits', {lineWidth: 3, name: layer.name}); self.aladin.addMOC(moc); - self.mocHiPSUrls[layer.properties.url] = 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.properties.obsTitle + ' loaded', + message: 'Coverage of ' + layer.name + ' loaded', duration: 2000, type: 'info' }) } }, actionOff: (e) => { - let moc = self.mocHiPSUrls[layer.properties.url]; + let moc = self.mocHiPSUrls[layer.url]; self.aladin.removeLayer(moc) - delete self.mocHiPSUrls[layer.properties.url]; + 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.properties.obsTitle + ' removed', + message: 'Coverage of ' + layer.name + ' removed', duration: 2000, type: 'info' }) @@ -697,7 +701,7 @@ export class OverlayStack extends ContextMenu { l.subMenu = []; - for(let ll of defaultLayers) { + for(const [id, ll] of defaultLayers) { backgroundUrl = OverlayStack.previewImagesUrl[ll.name]; if (!backgroundUrl) { backgroundUrl = ll.url + '/preview.jpg' @@ -718,31 +722,7 @@ export class OverlayStack extends ContextMenu { label: '
' + ll.name + '
', cssStyle, action(e) { - let cfg = ImageLayer.LAYERS.find((l) => l.name === ll.name); - let newLayer; - - // Max order is specific for surveys - if (cfg.subtype === "fits") { - // FITS - newLayer = self.aladin.createImageFITS( - cfg.url, - cfg.name, - cfg.options, - ); - } else { - // HiPS - newLayer = self.aladin.createImageSurvey( - cfg.id, - cfg.name, - cfg.url, - undefined, - cfg.maxOrder, - cfg.options - ); - } - - self.aladin.setOverlayImageLayer(newLayer, layer.layer); - //self._hide(); + self.aladin.setOverlayImageLayer(id, layer.layer); }, hover(e, item) { item.style.filter = 'brightness(1.5)'; @@ -806,11 +786,11 @@ export class OverlayStack extends ContextMenu { return; } - if (!layer.properties || !layer.properties.creatorDid) { + if (!layer.creatorDid) { return; } - const creatorDid = layer.properties.creatorDid; + const creatorDid = layer.creatorDid; for (const key in Stack.previewImagesUrl) { if (creatorDid.includes(key)) { @@ -818,7 +798,7 @@ export class OverlayStack extends ContextMenu { } } // if not found - return layer.properties.url + '/preview.jpg' + return layer.url + '/preview.jpg' } _addOverlayIcon(overlay) { diff --git a/src/js/gui/Input/CooFrame.js b/src/js/gui/Input/CooFrame.js index 25fe5363..9f7dd435 100644 --- a/src/js/gui/Input/CooFrame.js +++ b/src/js/gui/Input/CooFrame.js @@ -46,7 +46,7 @@ value: cooFrame.label, options: [CooFrameEnum.J2000.label, CooFrameEnum.J2000d.label, CooFrameEnum.GAL.label], change(e) { - self.setFrame(e.target.value) + aladin.setFrame(e.target.value) }, classList: ['aladin-cooFrame'], tooltip: { @@ -67,7 +67,6 @@ let self = this; ALEvent.FRAME_CHANGED.listenedBy(aladin.aladinDiv, function (e) { let frame = e.detail.cooFrame; - console.log(frame, e) self.update({ value: frame.label diff --git a/src/js/gui/Widgets/Input.js b/src/js/gui/Widgets/Input.js index 2c37bc66..a6c70d1e 100644 --- a/src/js/gui/Widgets/Input.js +++ b/src/js/gui/Widgets/Input.js @@ -67,10 +67,6 @@ export class Input extends DOMElement { _show() { this.el.innerHTML = ''; - if (this.options.classList) { - this.addClass(this.options.classList) - } - if (this.options.type === "checkbox") { this.el.type = this.options.type; @@ -214,6 +210,10 @@ export class Input extends DOMElement { Tooltip.add(this.options.tooltip, this) } + if (this.options.classList) { + this.element().classList.add(this.options.classList) + } + /*// Add padding for inputs except color ones if (Utils.hasTouchScreen() && this.options.type !== "color") { // Add a little padding diff --git a/src/js/gui/Widgets/Selector.js b/src/js/gui/Widgets/Selector.js index abc356f5..c81541a1 100644 --- a/src/js/gui/Widgets/Selector.js +++ b/src/js/gui/Widgets/Selector.js @@ -75,8 +75,9 @@ export class SelectorButton extends DOMElement { if (id === 'selected' || this.options.selected === id || id === 'tooltip') { continue; } - let optSelect = this.options[id]; + console.log(optSelect) + menuOptions.push({ label: new ActionButton(optSelect), action(e) { @@ -85,6 +86,8 @@ export class SelectorButton extends DOMElement { } self.update({selected: id}); + self._show(); + self.fsm.dispatch('closeCtxMenu') } }) @@ -133,6 +136,7 @@ export class SelectorButton extends DOMElement { // remove from the DOM tree const selectedId = this.options.selected; + let {target, position} = this.remove(); this.el = new ActionButton({ ...this.options[selectedId], diff --git a/src/js/gui/Widgets/Toolbar.js b/src/js/gui/Widgets/Toolbar.js deleted file mode 100644 index d1bcc33d..00000000 --- a/src/js/gui/Widgets/Toolbar.js +++ /dev/null @@ -1,182 +0,0 @@ -// Copyright 2023 - 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. -// -import { Layout } from "../Layout"; -import { Utils } from "../../Utils"; -/****************************************************************************** - * Aladin Lite project - * - * File gui/Widgets/layout/Horizontal.js - * - * A layout grouping widgets horizontaly - * - * - * Author: Matthieu Baumann[CDS] - * - *****************************************************************************/ - -/*{ - direction: 'vertical' | 'horizontal', - cssStyle: {...} - position: { - top, - left - } \ { - container: NodeElement - anchor: 'left top' | - 'left center' | - 'left bottom' | - 'right top' | - 'right center' | - 'right bottom' | - 'center top' | - 'center center' | - 'center bottom' - } - } -}*/ -export class Toolbar extends Layout { - /** - * Create a layout - * @param {layout: Array., cssStyle: Object} options - Represents the structure of the Tabs - * @param {DOMElement} target - The parent element. - * @param {String} position - The position of the tabs layout relative to the target. - * For the list of possibilities, see https://developer.mozilla.org/en-US/docs/Web/API/Element/insertAdjacentHTML - */ - constructor(options) { - options.orientation = options.orientation || 'horizontal'; - options.position = options.position || {anchor: 'left top'}; - - if (!!options.direction === false) { - if (options.orientation === 'horizontal') { - // top or bottom - if (options.position.anchor && options.position.anchor.includes('top')) { - options.direction = 'bottom' - } else { - options.direction = 'top' - } - } else { - // left or right - if (options.position.anchor && options.position.anchor.includes('left')) { - options.direction = 'right' - } else { - options.direction = 'left' - } - } - } - - options.layout = options.layout || []; - - super(options) - - this.addClass(options.direction); - - this.tools = {}; - } - - update(options) { - if (options.direction) { - this.removeClass('left'); - this.removeClass('right'); - this.removeClass('top'); - this.removeClass('bottom'); - - this.addClass(options.direction) - - // search for a tooltip - /*this.el.querySelectorAll(".aladin-tooltip-container") - .forEach((t) => { - t.classList.remove('left'); - t.classList.remove('right'); - t.classList.remove('top'); - t.classList.remove('bottom'); - - if (options.direction === 'left') { - t.classList.add('bottom') - } else if (options.direction === 'right') { - t.classList.add('bottom', 'left') - } else if (options.direction === 'top') { - t.classList.add('right') - } else { - t.classList.add('left') - } - })*/ - } - - super.update(options); - } - - add(tool, name, position = 'after') { - if (Array.isArray(tool)) { - let tools = tool; - tools.forEach(t => { - this.appendAtLast(t) - }); - } else { - if (position === 'begin') { - this.appendAtIndex(tool, name, 0) - } else { - this.appendAtLast(tool, name) - } - } - } - - remove(name) { - let tool = this.tools[name]; - - this.removeItem(tool) - delete this.tools[name]; - - return tool; - } - - appendAtLast(tool, name) { - if (!name) { - name = Utils.uuidv4() - } - this.tools[name] = tool; - this.appendLast(tool); - } - - appendAtIndex(tool, name, index) { - this.tools[name] = tool; - this.insertItemAtIndex(tool, index); - } - - /* Show a tool */ - show(name) { - if (name && this.tools[name]) { - this.tools[name]._show() - } - } - - isShown(name) { - return this.tools[name] && !this.tools[name].isHidden - } - - /* Hide a tool */ - hide(name) { - if (name && this.tools[name]) { - this.tools[name]._hide() - } - } - - contains(name) { - return this.tools[name] !== undefined; - } -} diff --git a/src/js/gui/Widgets/Widget.js b/src/js/gui/Widgets/Widget.js index 976b76dd..76bd2626 100644 --- a/src/js/gui/Widgets/Widget.js +++ b/src/js/gui/Widgets/Widget.js @@ -295,17 +295,12 @@ export class DOMElement { _show() { this.el.style.display = "" - //this.el.style.display = 'block'; this.isHidden = false; } _hide() { this.isHidden = true; this.el.style.display = 'none'; - - if (this.options && this.options.onHidden) { - this.options.onHidden(); - } } attachTo(target, position = 'beforeend') { @@ -324,6 +319,10 @@ export class DOMElement { this.options = {...this.options, ...options}; } + if (this.isHidden) { + return; + } + this._show(); } };