mirror of
https://github.com/cds-astro/aladin-lite.git
synced 2025-12-12 07:40:26 -08:00
make the HiPS browser more robust and implement a very proto interface for querying the HiPS on the fly generation from cube service: https://alasky.cds.unistra.fr/onthefly-cube-hips/
This commit is contained in:
committed by
Matthieu Baumann
parent
cd363ca4b9
commit
219761c512
@@ -25,6 +25,7 @@ Always prefer using the latest version. If you want the new features without min
|
||||
## API documentation
|
||||
|
||||
There is a new in progress API documentation at [this link](https://cds-astro.github.io/aladin-lite).
|
||||
Editable examples showing the API can also be found [here](https://aladin.cds.unistra.fr/AladinLite/doc/API/examples/).
|
||||
|
||||
## Embed it into your projects
|
||||
|
||||
|
||||
88
examples/al-onthefly.html
Normal file
88
examples/al-onthefly.html
Normal file
@@ -0,0 +1,88 @@
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div id="aladin-lite-div" style="width: 500px; height: 400px"></div>
|
||||
|
||||
<script type="module">
|
||||
import A from '../src/js/A.js';
|
||||
|
||||
let aladin;
|
||||
A.init.then(() => {
|
||||
aladin = A.aladin('#aladin-lite-div', {
|
||||
fullScreen: true,
|
||||
target: "ngc 1436",
|
||||
fov: 2,
|
||||
projection: 'TAN',
|
||||
showContextMenu: true,
|
||||
showSimbadPointerControl: true,
|
||||
expandLayersControl: true,
|
||||
hipsList: [
|
||||
// High energy (Gamma and X-rays)
|
||||
'CDS/P/HGPS/Flux',
|
||||
'CDS/P/Fermi/5',
|
||||
'CDS/P/Fermi/4',
|
||||
'CDS/P/Fermi/3',
|
||||
'ov-gso/P/Fermi/Band2',
|
||||
'ov-gso/P/BAT/150-195keV',
|
||||
'ov-gso/P/BAT/35-50keV',
|
||||
'ov-gso/P/BAT/14-20keV',
|
||||
'erosita/dr1/rate/023',
|
||||
'erosita/dr1/rate/024',
|
||||
// Uv/Optical/Infrared
|
||||
'CDS/P/GALEXGR6_7/FUV',
|
||||
'CDS/P/GALEXGR6_7/NUV',
|
||||
'CDS/P/DSS2/color',
|
||||
'CDS/P/PanSTARRS/DR1/g',
|
||||
'CDS/P/PanSTARRS/DR1/r',
|
||||
'CDS/P/Finkbeiner',
|
||||
'CDS/P/PanSTARRS/DR1/i',
|
||||
'CDS/P/PanSTARRS/DR1/color-i-r-g',
|
||||
'CDS/P/PanSTARRS/DR1/z',
|
||||
'CDS/P/PanSTARRS/DR1/y',
|
||||
'CDS/P/DES-DR2/ColorIRG',
|
||||
'CDS/P/2MASS/color',
|
||||
'ov-gso/P/GLIMPSE/irac1',
|
||||
'ov-gso/P/GLIMPSE/irac2',
|
||||
'CDS/P/unWISE/color-W2-W1W2-W1',
|
||||
'ov-gso/P/GLIMPSE/irac3',
|
||||
'ov-gso/P/GLIMPSE/irac4',
|
||||
'CDS/P/IRIS/color',
|
||||
'ESAVO/P/AKARI/N60',
|
||||
'ESAVO/P/AKARI/WideL',
|
||||
'ESAVO/P/HERSCHEL/SPIRE-250',
|
||||
'ESAVO/P/HERSCHEL/SPIRE-350',
|
||||
'ESAVO/P/HERSCHEL/SPIRE-500',
|
||||
// sub-mm/mm/radio
|
||||
'CDS/P/PLANCK/R3/HFI/color',
|
||||
'CDS/P/ACT_Planck/DR5/f220',
|
||||
'CDS/P/CO',
|
||||
'CDS/P/PLANCK/R3/HFI100',
|
||||
'CDS/P/PLANCK/R3/LFI30',
|
||||
'CDS/P/NVSS',
|
||||
'CSIRO/P/RACS/mid/I',
|
||||
'ov-gso/P/CGPS/VGPS',
|
||||
'CDS/C/HI4PI/HI',
|
||||
'CDS/P/MeerKAT/Galactic-Centre-1284MHz-StokesI',
|
||||
'CSIRO/P/RACS/low/I',
|
||||
'astron.nl/P/tgssadr',
|
||||
'ov-gso/P/GLEAM/170-231',
|
||||
'ov-gso/P/GLEAM/139-170',
|
||||
'astron.nl/P/lotss_dr2_high',
|
||||
'ov-gso/P/GLEAM/103-134',
|
||||
'ov-gso/P/GLEAM/072-103'
|
||||
]
|
||||
});
|
||||
|
||||
aladin.addCatalog(
|
||||
A.catalogFromSKAORucio("ngc 1436", 15, {
|
||||
onClick: 'showTable',
|
||||
hoverColor: "yellow",
|
||||
})
|
||||
);
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -12,7 +12,7 @@
|
||||
aladin = A.aladin("#aladin-lite-div", {
|
||||
fullScreen: true,
|
||||
target: "m51",
|
||||
fov: 90,
|
||||
fov: 15,
|
||||
projection: "AIT",
|
||||
showContextMenu: true,
|
||||
showShareControl: true,
|
||||
@@ -76,8 +76,8 @@ aladin = A.aladin("#aladin-lite-div", {
|
||||
aladin.setImageLayer(A.imageHiPS("P/DSS2/color"));
|
||||
|
||||
aladin.addCatalog(
|
||||
A.catalogFromSKAORucio("m51", 10, {
|
||||
onClick: "showTable",
|
||||
A.catalogFromSKAORucio("m51", 15, {
|
||||
onClick: 'showTable',
|
||||
hoverColor: "yellow",
|
||||
})
|
||||
);
|
||||
|
||||
@@ -18,13 +18,12 @@
|
||||
console.log("zoomChanged")
|
||||
})
|
||||
aladin.on("positionChanged", ({ra, dec}) => {
|
||||
console.log('call to aladin', aladin.pix2world(0, 0))
|
||||
console.log('call to aladin', aladin.pix2world(300, 300))
|
||||
console.log('positionChanged in icrs', ra, dec)
|
||||
})
|
||||
|
||||
aladin.gotoRaDec(0, 20);
|
||||
|
||||
|
||||
aladin.on('rightClickMove', (x, y) => {
|
||||
console.log("right click move", x, y)
|
||||
})
|
||||
|
||||
@@ -44,7 +44,9 @@ impl ImageFormat for RGB8U {
|
||||
|
||||
fn decode(raw_bytes: &[u8]) -> Result<Bytes<'_>, &'static str> {
|
||||
let mut decoder = jpeg::Decoder::new(raw_bytes);
|
||||
let bytes = decoder.decode().map_err(|_| "Cannot decoder jpeg. This image may not be compressed.")?;
|
||||
let bytes = decoder
|
||||
.decode()
|
||||
.map_err(|_| "Cannot decoder jpeg. This image may not be compressed.")?;
|
||||
|
||||
Ok(Bytes::Owned(bytes))
|
||||
}
|
||||
@@ -70,7 +72,9 @@ impl ImageFormat for RGBA8U {
|
||||
|
||||
fn decode(raw_bytes: &[u8]) -> Result<Bytes<'_>, &'static str> {
|
||||
let mut decoder = jpeg::Decoder::new(raw_bytes);
|
||||
let bytes = decoder.decode().map_err(|_| "Cannot decoder png. This image may not be compressed.")?;
|
||||
let bytes = decoder
|
||||
.decode()
|
||||
.map_err(|_| "Cannot decoder png. This image may not be compressed.")?;
|
||||
|
||||
Ok(Bytes::Owned(bytes))
|
||||
}
|
||||
@@ -93,7 +97,9 @@ impl ImageFormat for RGBA8U {
|
||||
|
||||
fn decode(raw_bytes: &[u8]) -> Result<Bytes<'_>, &'static str> {
|
||||
let mut decoder = jpeg::Decoder::new(raw_bytes);
|
||||
let bytes = decoder.decode().map_err(|_| "Cannot decoder png. This image may not be compressed.")?;
|
||||
let bytes = decoder
|
||||
.decode()
|
||||
.map_err(|_| "Cannot decoder png. This image may not be compressed.")?;
|
||||
|
||||
Ok(Bytes::Owned(bytes))
|
||||
}
|
||||
@@ -188,7 +194,6 @@ impl ImageFormat for R32F {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[cfg(feature = "webgl2")]
|
||||
#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq)]
|
||||
pub struct R64F;
|
||||
@@ -310,6 +315,8 @@ pub enum ChannelType {
|
||||
R32I,
|
||||
}
|
||||
|
||||
pub const NUM_CHANNELS: usize = 9;
|
||||
|
||||
#[derive(Debug, Clone, Copy, Hash, Eq, PartialEq)]
|
||||
pub struct ImageFormatType {
|
||||
pub ext: ImageExt,
|
||||
@@ -327,8 +334,11 @@ impl ImageFormatType {
|
||||
|
||||
pub fn is_colored(&self) -> bool {
|
||||
match self.channel {
|
||||
ChannelType::RGBA32F | ChannelType::RGB32F | ChannelType::RGBA8U | ChannelType::RGB8U => true,
|
||||
_ => false
|
||||
ChannelType::RGBA32F
|
||||
| ChannelType::RGB32F
|
||||
| ChannelType::RGBA8U
|
||||
| ChannelType::RGB8U => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ use al_core::image::Image;
|
||||
use al_core::log::console_log;
|
||||
use al_core::shader::Shader;
|
||||
use al_core::webgl_ctx::GlWrapper;
|
||||
use al_core::Texture2DArray;
|
||||
use al_core::VecData;
|
||||
use al_core::VertexArrayObject;
|
||||
use al_core::WebGlContext;
|
||||
@@ -43,6 +44,7 @@ use uv::{TileCorner, TileUVW};
|
||||
|
||||
use cgmath::{Matrix, Matrix4};
|
||||
use std::fmt::Debug;
|
||||
use std::rc::Rc;
|
||||
use wasm_bindgen::JsValue;
|
||||
use web_sys::WebGl2RenderingContext;
|
||||
|
||||
@@ -381,11 +383,7 @@ pub struct HiPS {
|
||||
}
|
||||
|
||||
impl HiPS {
|
||||
pub fn new(
|
||||
config: HiPSConfig,
|
||||
gl: &WebGlContext,
|
||||
_camera: &CameraViewPort,
|
||||
) -> Result<Self, JsValue> {
|
||||
pub fn new(config: HiPSConfig, gl: &WebGlContext) -> Result<Self, JsValue> {
|
||||
let mut vao = VertexArrayObject::new(gl);
|
||||
|
||||
// layout (location = 0) in vec2 lonlat;
|
||||
@@ -497,8 +495,6 @@ impl HiPS {
|
||||
.unbind();
|
||||
|
||||
let num_idx = 0;
|
||||
//let min_depth_tile = config.get_min_depth_tile();
|
||||
|
||||
let textures = ImageSurveyTextures::new(gl, config)?;
|
||||
|
||||
let gl = gl.clone();
|
||||
|
||||
@@ -10,6 +10,7 @@ pub mod utils;
|
||||
use crate::renderable::image::Image;
|
||||
|
||||
use al_core::image::format::ChannelType;
|
||||
use al_core::Texture2DArray;
|
||||
pub use hips::HiPS;
|
||||
|
||||
pub use catalog::Manager;
|
||||
@@ -20,6 +21,7 @@ use al_api::hips::ImageMetadata;
|
||||
use al_api::image::ImageParams;
|
||||
|
||||
use al_core::colormap::Colormaps;
|
||||
use al_core::image::format::NUM_CHANNELS;
|
||||
use al_core::shader::Shader;
|
||||
use al_core::SliceData;
|
||||
use al_core::VertexArrayObject;
|
||||
@@ -38,6 +40,7 @@ use hips::raytracing::RayTracer;
|
||||
|
||||
use std::borrow::Cow;
|
||||
use std::collections::HashMap;
|
||||
use std::rc::Rc;
|
||||
use wasm_bindgen::JsValue;
|
||||
use web_sys::WebGl2RenderingContext;
|
||||
|
||||
@@ -481,7 +484,7 @@ impl Layers {
|
||||
}*/
|
||||
camera.register_view_frame(cfg.get_frame(), proj);
|
||||
|
||||
let hips = HiPS::new(cfg, gl, camera)?;
|
||||
let hips = HiPS::new(cfg, gl)?;
|
||||
// add the frame to the camera
|
||||
|
||||
self.surveys.insert(creator_did.clone(), hips);
|
||||
@@ -613,16 +616,6 @@ impl Layers {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/*pub fn is_ready(&self) -> bool {
|
||||
let ready = self
|
||||
.surveys
|
||||
.iter()
|
||||
.map(|(_, survey)| survey.is_ready())
|
||||
.fold(true, |acc, x| acc & x);
|
||||
|
||||
ready
|
||||
}*/
|
||||
|
||||
// Accessors
|
||||
// HiPSes getters
|
||||
pub fn get_hips_from_layer(&self, layer: &str) -> Option<&HiPS> {
|
||||
|
||||
@@ -138,7 +138,7 @@ pub struct ImageSurveyTextures {
|
||||
//pub cutoff_values_tile: Rc<RefCell<HashMap<HEALPixCell, (f32, f32)>>>,
|
||||
|
||||
// Array of 2D textures
|
||||
texture_2d_array: Texture2DArray,
|
||||
pub texture_2d_array: Texture2DArray,
|
||||
|
||||
// A boolean ensuring the root textures
|
||||
// have already been loaded
|
||||
@@ -168,11 +168,7 @@ fn create_texture_array<F: ImageFormat>(
|
||||
}
|
||||
|
||||
impl ImageSurveyTextures {
|
||||
pub fn new(
|
||||
gl: &WebGlContext,
|
||||
config: HiPSConfig,
|
||||
//exec: Rc<RefCell<TaskExecutor>>,
|
||||
) -> Result<ImageSurveyTextures, JsValue> {
|
||||
pub fn new(gl: &WebGlContext, config: HiPSConfig) -> Result<ImageSurveyTextures, JsValue> {
|
||||
let size = config.num_textures();
|
||||
// Ensures there is at least space for the 12
|
||||
// root textures
|
||||
@@ -212,7 +208,6 @@ impl ImageSurveyTextures {
|
||||
#[cfg(feature = "webgl2")]
|
||||
ChannelType::R64F => create_texture_array::<R64F>(gl, &config)?,
|
||||
};
|
||||
|
||||
// The root textures have not been loaded
|
||||
//let ready = false;
|
||||
//let num_root_textures_available = 0;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
use al_api::hips::ImageExt;
|
||||
use al_core::log::console_log;
|
||||
use al_core::{image::format::ImageFormat, image::raw::ImageBuffer};
|
||||
|
||||
#[derive(Debug)]
|
||||
|
||||
@@ -96,6 +96,7 @@
|
||||
.aladin-measurement-div.aladin-dark-theme table thead {
|
||||
background-color: #000;
|
||||
color: white;
|
||||
|
||||
}
|
||||
|
||||
.aladin-measurement-div table th {
|
||||
@@ -107,7 +108,7 @@
|
||||
}
|
||||
|
||||
.aladin-measurement-div table tr td {
|
||||
padding: 0.5rem 0;
|
||||
padding: 0.5rem;
|
||||
}
|
||||
|
||||
.aladin-measurement-div table tr td, .aladin-measurement-div table tr td a {
|
||||
@@ -343,6 +344,13 @@ canvas {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
|
||||
}
|
||||
|
||||
.aladin-icon img {
|
||||
vertical-align:middle;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.aladin-icon.aladin-dark-theme {
|
||||
@@ -387,7 +395,7 @@ canvas {
|
||||
height: 1.7rem;
|
||||
}
|
||||
|
||||
.aladin-input-text.aladin-dark-theme:focus {
|
||||
.aladin-input-text.aladin-dark-theme:focus, .aladin-input-number.aladin-dark-theme:focus {
|
||||
border-color: dodgerblue;
|
||||
}
|
||||
|
||||
@@ -458,7 +466,7 @@ canvas {
|
||||
align-items: flex-end;
|
||||
}
|
||||
|
||||
.aladin-vertical-list > *:first-of-type {
|
||||
.aladin-vertical-list > *:first-child {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
@@ -477,11 +485,14 @@ canvas {
|
||||
margin-right: 0.2rem;
|
||||
}
|
||||
|
||||
.aladin-horizontal-list > *::last-of-type {
|
||||
vertical-align: middle;
|
||||
.aladin-horizontal-list > *:last-child {
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
.aladin-form {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.aladin-form .aladin-form-input {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
@@ -489,6 +500,10 @@ canvas {
|
||||
margin-bottom: 0.4rem;
|
||||
}
|
||||
|
||||
.aladin-form .aladin-form-input:last-of-type {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.aladin-form .aladin-form-group {
|
||||
margin-bottom: 1rem;
|
||||
width: 100%;
|
||||
@@ -870,12 +885,17 @@ canvas {
|
||||
/********** Range Input Styles **********/
|
||||
/*Range Reset*/
|
||||
.aladin-input-range {
|
||||
-webkit-appearance: none;
|
||||
appearance: none;
|
||||
background: transparent;
|
||||
cursor: pointer;
|
||||
width: 5em;
|
||||
height: 1.5rem;
|
||||
background: transparent;
|
||||
cursor: pointer;
|
||||
width: 5em;
|
||||
height: 0.1rem;
|
||||
margin:0;
|
||||
margin: 1rem 0;
|
||||
}
|
||||
|
||||
|
||||
.aladin-input-range.aladin-reversed {
|
||||
direction: rtl;
|
||||
}
|
||||
|
||||
/* Removes default focus */
|
||||
@@ -884,47 +904,43 @@ canvas {
|
||||
}
|
||||
|
||||
/***** Chrome, Safari, Opera and Edge Chromium styles *****/
|
||||
/* slider track */
|
||||
.aladin-input-range::-webkit-slider-runnable-track {
|
||||
background-color: #bababa;
|
||||
border-radius: 0.1rem;
|
||||
height: 0.1rem;
|
||||
|
||||
.aladin-input-range::-webkit-slider-container {
|
||||
background: white;
|
||||
height: 0.1rem;
|
||||
min-height: 0.1rem;
|
||||
}
|
||||
|
||||
/* slider thumb */
|
||||
.aladin-input-range::-webkit-slider-thumb {
|
||||
-webkit-appearance: none; /* Override default look */
|
||||
/*
|
||||
.aladin-input-range-datalist {
|
||||
-webkit-appearance: none;
|
||||
appearance: none;
|
||||
margin-top: -7px; /* Centers thumb on the track */
|
||||
|
||||
/*custom styles*/
|
||||
background-color: #bababa;
|
||||
height: 1rem;
|
||||
width: 1rem;
|
||||
display: none;
|
||||
display: flex;
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
padding:0;
|
||||
margin:0;
|
||||
height: 0.1rem;
|
||||
top: 0rem;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
border-radius: 0.5rem;
|
||||
}
|
||||
|
||||
/******** Firefox styles ********/
|
||||
/* slider track */
|
||||
.aladin-input-range::-moz-range-track {
|
||||
background-color: #bababa;
|
||||
|
||||
.aladin-input-range-datalist option {
|
||||
-webkit-appearance: none;
|
||||
appearance: none;
|
||||
position: absolute;
|
||||
transform: translate(-50%, 0);
|
||||
justify-content: center;
|
||||
text-align: center;
|
||||
width: 0.1rem;
|
||||
border-radius: 0.1rem;
|
||||
height: 0.1rem;
|
||||
}
|
||||
|
||||
/* slider thumb */
|
||||
.aladin-input-range::-moz-range-thumb {
|
||||
border: none; /*Removes extra border that FF applies*/
|
||||
border-radius: 0; /*Removes default border-radius that FF applies*/
|
||||
|
||||
/*custom styles*/
|
||||
background-color: #bababa;
|
||||
height: 1rem;
|
||||
width: 1rem;
|
||||
|
||||
border-radius: 0.5rem;
|
||||
}
|
||||
height: 0.1rem;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
background: #D3D3D3;
|
||||
}*/
|
||||
|
||||
.aladin-dark-theme {
|
||||
color: white;
|
||||
@@ -1090,8 +1106,12 @@ canvas {
|
||||
width: 17rem;
|
||||
}
|
||||
|
||||
.aladin-HiPS-filter-box {
|
||||
border: 1px solid white;
|
||||
}
|
||||
|
||||
.aladin-HiPS-browser-box .aladin-input-text {
|
||||
width: 30vw;
|
||||
width: 300px;
|
||||
padding: 0.5rem;
|
||||
}
|
||||
|
||||
|
||||
@@ -106,7 +106,7 @@ A.aladin = function (divSelector, options) {
|
||||
* @function
|
||||
* @name A.imageHiPS
|
||||
* @memberof A
|
||||
* @param {string} id - Can be an `url` that refers to a HiPS.
|
||||
* @param {string} id - Can be either 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 {ImageHiPSOptions} [options] - Options describing the survey
|
||||
* @returns {ImageHiPS} - A HiPS image object
|
||||
|
||||
@@ -1574,6 +1574,20 @@ export let Aladin = (function () {
|
||||
*/
|
||||
Aladin.createImageSurvey = Aladin.prototype.createImageSurvey;
|
||||
|
||||
/**
|
||||
* Creates a FITS image object
|
||||
* @deprecated prefer use {@link A.imageFITS}
|
||||
*
|
||||
* @function createImageFITS
|
||||
* @memberof Aladin
|
||||
* @static
|
||||
* @param {string} url - The url of the fits.
|
||||
* @param {string} [name] - A human readable name for that fits.
|
||||
* @param {ImageFITSOptions} [options] - Options for rendering the image
|
||||
* @param {function} [success] - A success callback
|
||||
* @param {function} [error] - A success callback
|
||||
* @returns {ImageFITS} A FITS image object.
|
||||
*/
|
||||
Aladin.prototype.createImageFITS = function (
|
||||
url,
|
||||
name,
|
||||
@@ -1592,7 +1606,7 @@ export let Aladin = (function () {
|
||||
url = url.toString();
|
||||
|
||||
// Do not use proxy with CORS headers until we solve that: https://github.com/MattiasBuelens/wasm-streams/issues/20
|
||||
//url = Utils.handleCORSNotSameOrigin(url);
|
||||
//url = Utils.handleCORSNotSameOrigin(url).href;
|
||||
|
||||
let image = HiPSCache.get(url);
|
||||
if (!image) {
|
||||
@@ -1605,6 +1619,7 @@ export let Aladin = (function () {
|
||||
};
|
||||
|
||||
/**
|
||||
* @deprecated prefer use {@link A.imageFITS} instead
|
||||
* Creates a FITS image object
|
||||
*
|
||||
* @function createImageFITS
|
||||
|
||||
@@ -64,7 +64,6 @@ export let ImageFITS = (function () {
|
||||
// Name of the layer
|
||||
this.layer = null;
|
||||
this.added = false;
|
||||
this.subtype = "fits";
|
||||
// Set it to a default value
|
||||
this.url = url;
|
||||
this.id = url;
|
||||
|
||||
@@ -125,15 +125,6 @@ PropertyParser.bitpix = function (properties) {
|
||||
return bitpix;
|
||||
};
|
||||
|
||||
PropertyParser.dataproductSubtype = function (properties) {
|
||||
let dataproductSubtype =
|
||||
(properties && properties.dataproduct_subtype) || "color";
|
||||
dataproductSubtype = dataproductSubtype
|
||||
.split(" ")
|
||||
.map((subtype) => subtype.toLowerCase());
|
||||
return dataproductSubtype;
|
||||
};
|
||||
|
||||
PropertyParser.isPlanetaryBody = function (properties) {
|
||||
return properties && properties.hips_body !== undefined;
|
||||
};
|
||||
@@ -182,12 +173,12 @@ export let ImageHiPS = (function () {
|
||||
// Unique identifier for a survey
|
||||
this.id = id;
|
||||
this.name = (options && options.name) || undefined;
|
||||
this.subtype = "survey";
|
||||
this.url = url;
|
||||
this.maxOrder = options.maxOrder;
|
||||
this.minOrder = options.minOrder || 0;
|
||||
this.cooFrame = options.cooFrame;
|
||||
this.tileSize = options.tileSize;
|
||||
this.skyFraction = options.skyFraction;
|
||||
this.longitudeReversed =
|
||||
options.longitudeReversed === undefined
|
||||
? false
|
||||
@@ -472,6 +463,7 @@ export let ImageHiPS = (function () {
|
||||
creatorDid: self.creatorDid,
|
||||
name: self.name,
|
||||
url: self.url,
|
||||
skyFraction: self.skyFraction,
|
||||
cooFrame: self.cooFrame,
|
||||
maxOrder: self.maxOrder,
|
||||
tileSize: self.tileSize,
|
||||
|
||||
@@ -126,11 +126,15 @@ export let View = (function () {
|
||||
if (typeof posChangedFn === 'function') {
|
||||
var pos = this.aladin.pix2world(this.width / 2, this.height / 2, 'icrs');
|
||||
if (pos !== undefined) {
|
||||
posChangedFn({
|
||||
ra: pos[0],
|
||||
dec: pos[1],
|
||||
dragging: true
|
||||
});
|
||||
try {
|
||||
posChangedFn({
|
||||
ra: pos[0],
|
||||
dec: pos[1],
|
||||
dragging: true
|
||||
});
|
||||
} catch(e) {
|
||||
console.error(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -1930,7 +1934,10 @@ export let View = (function () {
|
||||
|
||||
if (layer.type == 'catalog' || layer.type == 'progressivecat') {
|
||||
indexToDelete = this.catalogs.indexOf(layer);
|
||||
|
||||
this.catalogs.splice(indexToDelete, 1);
|
||||
|
||||
this.unselectObjects();
|
||||
}
|
||||
else if (layer.type == 'moc') {
|
||||
indexToDelete = this.mocs.indexOf(layer);
|
||||
|
||||
@@ -29,6 +29,8 @@ import { Layout } from "../Layout.js";
|
||||
import { HiPSFilterBox } from "./HiPSFilterBox.js";
|
||||
import A from "../../A.js";
|
||||
import { Utils } from "../../Utils.ts";
|
||||
import { ActionButton } from "../Widgets/ActionButton.js";
|
||||
import infoIconUrl from "../../../../assets/icons/info.svg"
|
||||
|
||||
/******************************************************************************
|
||||
* Aladin Lite project
|
||||
@@ -83,7 +85,7 @@ export class HiPSBrowserBox extends Box {
|
||||
|
||||
let searchDropdown = new Dropdown(aladin, {
|
||||
name: "HiPS browser",
|
||||
placeholder: "Browser a HiPS by an URL, ID or keywords",
|
||||
placeholder: "Browse a HiPS by an URL, ID or keywords",
|
||||
tooltip: {
|
||||
global: true,
|
||||
aladin,
|
||||
@@ -103,6 +105,10 @@ export class HiPSBrowserBox extends Box {
|
||||
}
|
||||
},
|
||||
input(e) {
|
||||
self.infoCurrentHiPSBtn.update({
|
||||
disable: true,
|
||||
})
|
||||
|
||||
searchDropdown.removeClass('aladin-valid')
|
||||
searchDropdown.removeClass('aladin-not-valid')
|
||||
},
|
||||
@@ -117,6 +123,7 @@ export class HiPSBrowserBox extends Box {
|
||||
checked: false,
|
||||
tooltip: {
|
||||
content: "Filter off",
|
||||
position: {direction: 'left'},
|
||||
},
|
||||
click(e) {
|
||||
let on = e.target.checked;
|
||||
@@ -139,14 +146,29 @@ export class HiPSBrowserBox extends Box {
|
||||
filterEnabler.update({
|
||||
tooltip: {
|
||||
content: on
|
||||
? "Filtering on"
|
||||
: "Filtering off",
|
||||
? "Filter on"
|
||||
: "Filter off",
|
||||
position: {direction: 'left'},
|
||||
},
|
||||
checked: on,
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
let infoCurrentHiPSBtn = new ActionButton({
|
||||
disable: true,
|
||||
icon: {
|
||||
size: 'medium',
|
||||
monochrome: true,
|
||||
url: infoIconUrl,
|
||||
},
|
||||
tooltip: {
|
||||
global: true,
|
||||
aladin,
|
||||
content: "More about that survey?"
|
||||
}
|
||||
});
|
||||
|
||||
let filterBtn = new TogglerActionButton({
|
||||
icon: {
|
||||
url: filterOffUrl,
|
||||
@@ -185,8 +207,8 @@ export class HiPSBrowserBox extends Box {
|
||||
},
|
||||
classList: ['aladin-HiPS-browser-box'],
|
||||
content: Layout.vertical([
|
||||
Layout.horizontal(["Filter:", filterEnabler, filterBtn]),
|
||||
Layout.horizontal(["Search:", searchDropdown]),
|
||||
Layout.horizontal(["Search:", searchDropdown, infoCurrentHiPSBtn]),
|
||||
Layout.horizontal(["Filter:", Layout.horizontal([filterEnabler, filterBtn])]),
|
||||
]),
|
||||
...options,
|
||||
},
|
||||
@@ -204,6 +226,8 @@ export class HiPSBrowserBox extends Box {
|
||||
this.filterBtn = filterBtn;
|
||||
this.aladin = aladin;
|
||||
|
||||
this.infoCurrentHiPSBtn = infoCurrentHiPSBtn;
|
||||
|
||||
self = this;
|
||||
|
||||
this.filterCallback = (HiPS, params) => {
|
||||
@@ -241,6 +265,14 @@ export class HiPSBrowserBox extends Box {
|
||||
successCallback: (hips) => {
|
||||
self.searchDropdown.removeClass('aladin-not-valid');
|
||||
self.searchDropdown.addClass('aladin-valid');
|
||||
|
||||
|
||||
self.infoCurrentHiPSBtn.update({
|
||||
disable: false,
|
||||
action(e) {
|
||||
window.open(hips.url);
|
||||
}
|
||||
})
|
||||
},
|
||||
errorCallback: (e) => {
|
||||
self.searchDropdown.removeClass('aladin-valid');
|
||||
@@ -252,6 +284,7 @@ export class HiPSBrowserBox extends Box {
|
||||
|
||||
// This method is executed only if the filter is enabled
|
||||
_filterHiPSList(params) {
|
||||
console.log("update dropdown")
|
||||
let self = this;
|
||||
let HiPSIDs = [];
|
||||
|
||||
|
||||
@@ -25,6 +25,8 @@ import { Layout } from "../Layout.js";
|
||||
import { Angle } from "../../libs/astro/angle.js";
|
||||
import { ALEvent } from "../../events/ALEvent.js";
|
||||
import { Utils } from "../../Utils.ts";
|
||||
import { AladinUtils } from "../../AladinUtils.js";
|
||||
import { Input } from "../Widgets/Input.js";
|
||||
/******************************************************************************
|
||||
* Aladin Lite project
|
||||
*
|
||||
@@ -41,7 +43,7 @@ export class HiPSFilterBox extends Box {
|
||||
|
||||
let regimeBtn = new TogglerActionButton({
|
||||
content: 'Regime',
|
||||
tooltip: {content: 'Observation regime'},
|
||||
tooltip: {content: 'Observation regime', position: {direction: 'bottom'}},
|
||||
toggled: true,
|
||||
actionOn: () => {
|
||||
self._triggerFilteringCallback();
|
||||
@@ -51,19 +53,19 @@ export class HiPSFilterBox extends Box {
|
||||
}
|
||||
});
|
||||
let spatialBtn = new TogglerActionButton({
|
||||
content: 'Spatial',
|
||||
tooltip: {content: 'Check for HiPS having observation in the view!'},
|
||||
content: 'Inside view',
|
||||
tooltip: {content: 'Check for HiPS having observation in the view!', position: {direction: 'bottom'}},
|
||||
toggled: false,
|
||||
actionOn: () => {
|
||||
self._triggerFilteringCallback();
|
||||
self._requestMOCServer();
|
||||
},
|
||||
actionOff: () => {
|
||||
self._triggerFilteringCallback();
|
||||
}
|
||||
});
|
||||
let resolutionBtn = new TogglerActionButton({
|
||||
content: 'Resolution',
|
||||
tooltip: {content: 'Check for HiPS with a specific pixel resolution.'},
|
||||
content: 'Pixel res',
|
||||
tooltip: {content: 'Check for HiPS with a specific pixel resolution.', position: {direction: 'bottom'}},
|
||||
toggled: false,
|
||||
actionOn: () => {
|
||||
self._triggerFilteringCallback();
|
||||
@@ -73,13 +75,37 @@ export class HiPSFilterBox extends Box {
|
||||
}
|
||||
});
|
||||
|
||||
let logSlider = new Input({
|
||||
label: "Max res [°/px]:",
|
||||
name: "res",
|
||||
value: 0.1,
|
||||
type: 'range',
|
||||
cssStyle: {
|
||||
width: '100%'
|
||||
},
|
||||
tooltip: {content: AladinUtils.degreesToString(0.1), position: {direction: 'bottom'}},
|
||||
ticks: [0.1 / 3600, 1 / 3600, 1 / 60, 0.1],
|
||||
stretch: "log",
|
||||
min: 0.1 / 3600,
|
||||
max: 0.1,
|
||||
reversed: true,
|
||||
change: (e, slider, deg) => {
|
||||
slider.update({value: e.target.value, tooltip: {content: AladinUtils.degreesToString(deg), position:{direction:'bottom'}}});
|
||||
|
||||
let resolution = new Angle(deg);
|
||||
self.params["resolution"] = resolution.degrees();
|
||||
|
||||
self._triggerFilteringCallback();
|
||||
},
|
||||
});
|
||||
super(
|
||||
{
|
||||
classList: ['aladin-HiPS-filter-box'],
|
||||
close: false,
|
||||
content: Layout.vertical([
|
||||
'<h3>Filters:</h3>',
|
||||
'<b>Filter by:</b>',
|
||||
Layout.horizontal([regimeBtn, spatialBtn, resolutionBtn]),
|
||||
'<h3>Parameters:</h3>',
|
||||
'<b>Details:</b>',
|
||||
new Form({
|
||||
subInputs: [
|
||||
{
|
||||
@@ -91,10 +117,12 @@ export class HiPSFilterBox extends Box {
|
||||
value: "Optical",
|
||||
type: 'select',
|
||||
options: [
|
||||
"Optical",
|
||||
"UV",
|
||||
"Radio",
|
||||
"Infrared",
|
||||
"Millimeter",
|
||||
"Optical",
|
||||
"UV",
|
||||
"EUV",
|
||||
"X-ray",
|
||||
"Gamma-ray",
|
||||
],
|
||||
@@ -111,39 +139,7 @@ export class HiPSFilterBox extends Box {
|
||||
position: { direction: "right" },
|
||||
},
|
||||
},
|
||||
{
|
||||
label: "Angular res/px:",
|
||||
name: "res",
|
||||
value: "1°",
|
||||
type: 'text',
|
||||
classList: ['aladin-valid'],
|
||||
autocomplete: 'off',
|
||||
actions: {
|
||||
input: (e) => {
|
||||
e.target.classList.remove('aladin-not-valid');
|
||||
e.target.classList.remove('aladin-valid');
|
||||
|
||||
let value = e.target.value;
|
||||
let resolution = new Angle();
|
||||
if (resolution.parse(value)) {
|
||||
// The angle has been parsed
|
||||
console.log(resolution.degrees())
|
||||
self.params["resolution"] = resolution.degrees();
|
||||
|
||||
e.target.classList.add('aladin-valid');
|
||||
//resolutionBtn.update({content: '<=' + value});
|
||||
|
||||
self._triggerFilteringCallback();
|
||||
} else {
|
||||
e.target.classList.add('aladin-not-valid');
|
||||
}
|
||||
},
|
||||
change: (e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
}
|
||||
}
|
||||
},
|
||||
logSlider
|
||||
],
|
||||
},
|
||||
],
|
||||
|
||||
@@ -74,8 +74,6 @@ export class ServiceQueryBox extends Box {
|
||||
// Tackle CORS problems
|
||||
Utils.loadFromUrls([url, Utils.handleCORSNotSameOrigin(url)], {timeout: 30000, dataType: 'blob'})
|
||||
.then((blob) => {
|
||||
console.log("azer", blob)
|
||||
|
||||
const url = URL.createObjectURL(blob);
|
||||
|
||||
try {
|
||||
@@ -86,7 +84,6 @@ export class ServiceQueryBox extends Box {
|
||||
}
|
||||
})
|
||||
.catch((e) => {
|
||||
console.log("jjaaz")
|
||||
window.alert(e)
|
||||
})
|
||||
},
|
||||
@@ -153,8 +150,8 @@ export class ServiceQueryBox extends Box {
|
||||
|
||||
let fov = new Angle(radius, 1).degrees();
|
||||
//selectorBtn.update({tooltip: {content: 'center: ' + ra.toFixed(2) + ', ' + dec.toFixed(2) + '<br\>radius: ' + radius.toFixed(2), position: {direction: 'left'}}})
|
||||
self.form.set('ra', +lon)
|
||||
self.form.set('dec', +lat)
|
||||
self.form.set('ra', lon)
|
||||
self.form.set('dec', lat)
|
||||
self.form.set('rad', fov)
|
||||
} catch (e) {
|
||||
alert(e, 'Cone search out of projection')
|
||||
|
||||
@@ -52,6 +52,7 @@ import { Input } from "../Widgets/Input.js";
|
||||
import { ImageFITS } from "../../ImageFITS.js";
|
||||
import { HiPSCache } from "../../DefaultHiPSCache.js";
|
||||
import { HiPSBrowserBox } from "./HiPSBrowserBox.js";
|
||||
import { ImageHiPS } from "../../ImageHiPS.js";
|
||||
|
||||
export class OverlayStackBox extends Box {
|
||||
/*static previewImagesUrl = {
|
||||
@@ -1053,7 +1054,7 @@ export class OverlayStackBox extends Box {
|
||||
|
||||
let btns = [showBtn, settingsBtn];
|
||||
|
||||
if (layer.subtype !== "fits") {
|
||||
if (!(layer instanceof ImageFITS)) {
|
||||
btns.push(loadMOCBtn);
|
||||
}
|
||||
btns.push(deleteBtn);
|
||||
|
||||
@@ -1,172 +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.
|
||||
//
|
||||
|
||||
import { Box } from "../Widgets/Box.js";
|
||||
import { Layout } from "../Layout.js";
|
||||
import { ActionButton } from "../Widgets/ActionButton.js";
|
||||
import { ALEvent } from "../../events/ALEvent.js";
|
||||
import { Icon } from "../Widgets/Icon.js";
|
||||
import infoIconUrl from '../../../../assets/icons/info.svg';
|
||||
|
||||
/******************************************************************************
|
||||
* Aladin Lite project
|
||||
*
|
||||
* File gui/HiPSSelector.js
|
||||
*
|
||||
*
|
||||
* Author: Thomas Boch, Matthieu Baumann[CDS]
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
// 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 Location.js
|
||||
*
|
||||
* Author: Thomas Boch[CDS]
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
import { Input } from "./../Widgets/Input.js";
|
||||
import { Utils } from "../../Utils.ts";
|
||||
|
||||
export class HiPSSearch extends Input {
|
||||
static HiPSList = {};
|
||||
|
||||
// constructor
|
||||
constructor(aladin, options) {
|
||||
let self;
|
||||
let layer = options && options.layer;
|
||||
|
||||
aladin.view.catalogCanvas.addEventListener('click', (e) => {
|
||||
self.el.blur();
|
||||
});
|
||||
|
||||
let prevKey = layer.name;
|
||||
let hips = HiPSSearch.HiPSList[layer.name];
|
||||
|
||||
let content = [new Icon({
|
||||
size: 'medium',
|
||||
monochrome: true,
|
||||
url: infoIconUrl,
|
||||
cssStyle: {
|
||||
cursor: "help",
|
||||
},
|
||||
})];
|
||||
let link;
|
||||
if (layer.subtype === "fits") {
|
||||
link = 'Download file...'
|
||||
} else {
|
||||
link = 'See more...'
|
||||
}
|
||||
content.push('<a style="color: white;" href="' + layer.url + '" target="_blank">' + link + '</a>')
|
||||
let tooltip = {
|
||||
content: new Layout({layout: content, orientation: 'horizontal'}),
|
||||
hoverable: true,
|
||||
position: {
|
||||
direction: 'bottom',
|
||||
}
|
||||
}
|
||||
super({
|
||||
name: 'HiPS search',
|
||||
type: 'text',
|
||||
name: 'survey' + Utils.uuidv4(),
|
||||
tooltip,
|
||||
placeholder: "Survey keywords or url",
|
||||
autocomplete: {options: Object.keys(HiPSSearch.HiPSList)},
|
||||
title: layer.name,
|
||||
actions: {
|
||||
change(e) {
|
||||
const key = e.target.value;
|
||||
if (!key) {
|
||||
self.update({value: prevKey, title: prevKey});
|
||||
return;
|
||||
}
|
||||
|
||||
let image, hips;
|
||||
// A user can put an url
|
||||
try {
|
||||
image = new URL(key).href;
|
||||
} catch(e) {
|
||||
// Or he can select a HiPS from the list given
|
||||
hips = HiPSSearch.HiPSList[key]
|
||||
|
||||
if (hips) {
|
||||
image = hips.id || hips.url || undefined;
|
||||
} else {
|
||||
// Finally if not found, interpret the input text value as the HiPS (e.g. ID)
|
||||
image = key;
|
||||
}
|
||||
}
|
||||
|
||||
self.el.blur();
|
||||
if (image) {
|
||||
prevKey = image;
|
||||
// set the layer to the new value
|
||||
self.layer = aladin.setOverlayImageLayer(image, layer.layer);
|
||||
}
|
||||
}
|
||||
},
|
||||
value: layer.name,
|
||||
...options
|
||||
})
|
||||
this.el.classList.add('aladin-HiPS-search', 'search')
|
||||
|
||||
self = this;
|
||||
this.layer = layer;
|
||||
|
||||
this._addEventListeners(aladin);
|
||||
}
|
||||
|
||||
setAutocompletionList(options) {
|
||||
this.update({autocomplete: {options}})
|
||||
}
|
||||
|
||||
_addEventListeners(aladin) {
|
||||
let self = this;
|
||||
ALEvent.HIPS_LAYER_ADDED.listenedBy(aladin.aladinDiv, (e) => {
|
||||
const layer = e.detail.layer;
|
||||
if (layer.layer === self.layer.layer) {
|
||||
let value = layer.name
|
||||
self.update({value, title: value})
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -50,11 +50,10 @@ export class Box extends DOMElement {
|
||||
//el.style.display = "initial";
|
||||
|
||||
super(el, options);
|
||||
this._show();
|
||||
|
||||
this.addClass('aladin-dark-theme')
|
||||
|
||||
this.attachTo(target, position);
|
||||
this._show();
|
||||
this.addClass('aladin-dark-theme')
|
||||
}
|
||||
|
||||
_show(options) {
|
||||
|
||||
@@ -59,6 +59,7 @@ export class Form extends DOMElement {
|
||||
el.className = "aladin-form";
|
||||
|
||||
super(el, options);
|
||||
|
||||
this.attachTo(target, position)
|
||||
|
||||
this._show()
|
||||
@@ -70,7 +71,7 @@ export class Form extends DOMElement {
|
||||
let layout = [];
|
||||
if (this.options && this.options.subInputs) {
|
||||
this.options.subInputs.forEach(subInput => {
|
||||
layout.push(Form._createInput(subInput))
|
||||
layout.push(this._createInput(subInput))
|
||||
});
|
||||
}
|
||||
|
||||
@@ -94,17 +95,23 @@ export class Form extends DOMElement {
|
||||
super._show();
|
||||
}
|
||||
|
||||
static _createInput(layout) {
|
||||
if (!layout.subInputs) {
|
||||
let input = new Input(layout);
|
||||
|
||||
_createInput(layout) {
|
||||
if (layout instanceof DOMElement || !layout.subInputs) {
|
||||
let input;
|
||||
let label = document.createElement('label');
|
||||
if (layout.labelContent) {
|
||||
DOMElement.appendTo(layout.labelContent, label);
|
||||
if (layout instanceof DOMElement) {
|
||||
input = layout;
|
||||
label.textContent = input.options.label;
|
||||
} else {
|
||||
label.textContent = layout.label;
|
||||
input = new Input(layout);
|
||||
|
||||
if (layout.labelContent) {
|
||||
DOMElement.appendTo(layout.labelContent, label);
|
||||
} else {
|
||||
label.textContent = layout.label;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
label.for = input.el.id;
|
||||
|
||||
let item = new Layout([label, input]);
|
||||
@@ -118,7 +125,7 @@ export class Form extends DOMElement {
|
||||
}
|
||||
|
||||
layout.subInputs.map((subInput) => {
|
||||
let input = Form._createInput(subInput)
|
||||
let input = this._createInput(subInput)
|
||||
groupLayout.push(input)
|
||||
});
|
||||
|
||||
|
||||
@@ -103,9 +103,6 @@ export class Icon extends DOMElement {
|
||||
if (this.options.url) {
|
||||
let img = document.createElement('img');
|
||||
img.src = this.options.url;
|
||||
img.style.objectFit = 'contain';
|
||||
img.style.verticalAlign = 'middle';
|
||||
img.style.width = '100%';
|
||||
|
||||
this.el.appendChild(img);
|
||||
}
|
||||
@@ -156,6 +153,9 @@ export class Icon extends DOMElement {
|
||||
elt.querySelector('svg').setAttribute('width', size);
|
||||
elt.querySelector('svg').setAttribute('height', size);
|
||||
|
||||
elt.style.width = size;
|
||||
elt.style.height = size;
|
||||
|
||||
return elt.innerHTML;
|
||||
};
|
||||
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
import { DOMElement } from "./Widget";
|
||||
import { Tooltip } from "./Tooltip";
|
||||
import { Utils } from "../../Utils";
|
||||
|
||||
/******************************************************************************
|
||||
* Aladin Lite project
|
||||
*
|
||||
@@ -59,9 +60,9 @@ export class Input extends DOMElement {
|
||||
|
||||
super(el, options);
|
||||
|
||||
this._show()
|
||||
|
||||
this.attachTo(target, position)
|
||||
this.target = target;
|
||||
this._show()
|
||||
}
|
||||
|
||||
_show() {
|
||||
@@ -105,7 +106,95 @@ export class Input extends DOMElement {
|
||||
this.el.step = "any";
|
||||
}
|
||||
|
||||
let self = this;
|
||||
|
||||
|
||||
function logPositionMinMax(value, minp, maxp) {
|
||||
// position will be between 1 / 3600 and 1.0
|
||||
var minv = Math.log(minp);
|
||||
var maxv = Math.log(maxp);
|
||||
|
||||
// calculate adjustment factor
|
||||
var scale = (maxv-minv) / (maxp-minp);
|
||||
console.log('value', value)
|
||||
|
||||
return (Math.log(value)-minv) / scale + minp;
|
||||
}
|
||||
|
||||
function logPosition(value) {
|
||||
// position will be between 1 / 3600 and 1.0
|
||||
var minp = self.options.min; // 1 arcsec
|
||||
var maxp = self.options.max; // 1 deg
|
||||
|
||||
var minv = Math.log(self.options.min);
|
||||
var maxv = Math.log(self.options.max);
|
||||
|
||||
// calculate adjustment factor
|
||||
var scale = (maxv-minv) / (maxp-minp);
|
||||
console.log(minv, maxv)
|
||||
|
||||
return (Math.log(value)-minv) / scale + minp;
|
||||
}
|
||||
|
||||
function logSliderMinMax(position, minp, maxp) {
|
||||
|
||||
var minv = Math.log(minp);
|
||||
var maxv = Math.log(maxp);
|
||||
|
||||
// calculate adjustment factor
|
||||
var scale = (maxv-minv) / (maxp-minp);
|
||||
|
||||
return Math.exp(minv + scale*(position-minp));
|
||||
}
|
||||
|
||||
function logSlider(position) {
|
||||
// position will be between 1 / 3600 and 1.0
|
||||
var minp = self.options.min; // 1 arcsec
|
||||
var maxp = self.options.max; // 1 deg
|
||||
|
||||
var minv = Math.log(self.options.min);
|
||||
var maxv = Math.log(self.options.max);
|
||||
|
||||
// calculate adjustment factor
|
||||
var scale = (maxv-minv) / (maxp-minp);
|
||||
|
||||
return Math.exp(minv + scale*(position-minp));
|
||||
}
|
||||
|
||||
if (this.options.type === "range") {
|
||||
if (this.options.reversed === true) {
|
||||
this.addClass('aladin-reversed');
|
||||
}
|
||||
|
||||
if (this.options.stretch) {
|
||||
let stretch = this.options.stretch || 'linear';
|
||||
if (stretch === 'log') {
|
||||
// Refers to this StackOverflow post: https://stackoverflow.com/questions/846221/logarithmic-slider
|
||||
|
||||
|
||||
|
||||
if (this.options.ticks) {
|
||||
this.options.ticks = this.options.ticks.map((t) => logPosition(t));
|
||||
}
|
||||
|
||||
|
||||
|
||||
if (this.options.change) {
|
||||
let change = this.options.change;
|
||||
this.options.change = (e) => {
|
||||
const value = logSlider(e.target.value)
|
||||
/*let p = 100 * (e.target.value - this.options.min) / (this.options.max - this.options.min);
|
||||
if (this.options.reversed === true) {
|
||||
p = 100 - p;
|
||||
}
|
||||
this.el.style.background = 'linear-gradient(to right, lightgreen ' + p + '%, #bababa ' + p + '%)';
|
||||
*/
|
||||
change(e, this, value);
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (this.options.ticks) {
|
||||
this.options.autocomplete = {options: this.options.ticks};
|
||||
delete this.options.ticks;
|
||||
@@ -119,22 +208,55 @@ export class Input extends DOMElement {
|
||||
if (this.options.autocomplete) {
|
||||
const autocomplete = this.options.autocomplete
|
||||
if (autocomplete instanceof Object && autocomplete !== null) {
|
||||
let datalist = document.createElement('datalist');
|
||||
let datalist = null;
|
||||
if (this.el.parentNode) {
|
||||
datalist = this.el.parentNode.querySelector('#' + 'ticks-' + this.options.name);
|
||||
}
|
||||
|
||||
if (datalist) {
|
||||
// it has been found, remove what's inside it
|
||||
datalist.innerHTML = "";
|
||||
} else {
|
||||
// it has not been found, then create it
|
||||
if (this.options.type === 'range') {
|
||||
datalist = document.createElement('datalist');
|
||||
} else {
|
||||
datalist = document.createElement('datalist');
|
||||
}
|
||||
datalist.id = 'ticks-' + this.options.name;
|
||||
if (this.options.type === "range")
|
||||
datalist.classList.add('aladin-input-range-datalist')
|
||||
|
||||
// and insert it into the dom
|
||||
this.el.appendChild(datalist);
|
||||
|
||||
this.el.setAttribute('list', datalist.id);
|
||||
}
|
||||
|
||||
autocomplete.options.forEach((o) => {
|
||||
let option = document.createElement('option');
|
||||
option.value = o;
|
||||
datalist.appendChild(option);
|
||||
})
|
||||
|
||||
let optionEl;
|
||||
if (this.options.type === 'range') {
|
||||
optionEl = document.createElement('option');
|
||||
let p = (o - this.options.min) / (this.options.max - this.options.min);
|
||||
optionEl.value = o;
|
||||
|
||||
datalist.id = 'ticks-' + this.options.name;
|
||||
this.el.setAttribute('list', datalist.id);
|
||||
|
||||
//console.log(this.el, this.el.querySelector('#' + datalist.id), datalist)
|
||||
/*if (this.el.querySelector('#' + datalist.id)) {
|
||||
this.el.querySelector('#' + datalist.id).remove()
|
||||
}*/
|
||||
this.el.appendChild(datalist);
|
||||
const lerp = (x, min, max) => {
|
||||
return x * max + (1 - x) * min;
|
||||
};
|
||||
|
||||
if (this.options.reversed) {
|
||||
p = 1 - p;
|
||||
}
|
||||
|
||||
optionEl.style.left = 'calc(' + 100.0 * p + '% + ' + lerp(p, 0.5, -0.5) + 'rem)';
|
||||
} else {
|
||||
optionEl = document.createElement('option');
|
||||
optionEl.value = o;
|
||||
}
|
||||
|
||||
datalist.appendChild(optionEl);
|
||||
})
|
||||
|
||||
this.el.autocomplete = 'off';
|
||||
} else {
|
||||
@@ -164,13 +286,17 @@ export class Input extends DOMElement {
|
||||
if (this.options.change) {
|
||||
if (this.options.type === 'color' || this.options.type === 'range' || this.options.type === "text") {
|
||||
this.el.removeEventListener('input', this.action);
|
||||
|
||||
this.action = (e) => {
|
||||
this.options.change(e, this);
|
||||
};
|
||||
|
||||
this.el.addEventListener('input', this.action);
|
||||
} else {
|
||||
this.el.removeEventListener('change', this.action);
|
||||
|
||||
this.action = this.options.change;
|
||||
|
||||
this.el.addEventListener('change', this.action);
|
||||
}
|
||||
}
|
||||
@@ -228,9 +354,16 @@ export class Input extends DOMElement {
|
||||
super._show()
|
||||
}
|
||||
|
||||
/*setPlaceholder(placeholder) {
|
||||
this.el.placeholder = placeholder;
|
||||
}*/
|
||||
attachTo(target, position = 'beforeend') {
|
||||
super.attachTo(target, position);
|
||||
|
||||
if (this.options.type === "range") {
|
||||
// Dirty workaround for plotting the slider ticks
|
||||
// The input slider must have a parent so that
|
||||
// its datalist can be put into the DOM
|
||||
this._show()
|
||||
}
|
||||
}
|
||||
|
||||
update(options) {
|
||||
// if no options given, use the previous one set
|
||||
|
||||
@@ -7,12 +7,16 @@ import { Format } from "./coo";
|
||||
* Creates an angle of the Aladin interactive sky atlas.
|
||||
* @class
|
||||
* @constructs Angle
|
||||
* @param {number} angle - precision in degrees
|
||||
* @param {number} angle - angle in degrees
|
||||
* @param {number} prec - precision
|
||||
* (8: 1/1000th sec, 7: 1/100th sec, 6: 1/10th sec, 5: sec, 4: 1/10th min, 3: min, 2: 1/10th deg, 1: deg
|
||||
*/
|
||||
export let Angle = function(angle, prec) {
|
||||
this.angle = angle;
|
||||
|
||||
if (prec === undefined || prec === null) {
|
||||
prec = 0;
|
||||
}
|
||||
this.prec = prec;
|
||||
};
|
||||
|
||||
|
||||
@@ -34,6 +34,10 @@ import { Utils } from './../Utils';
|
||||
import { ActionButton } from "../gui/Widgets/ActionButton.js";
|
||||
import { Catalog } from "../Catalog.js";
|
||||
import { ServiceQueryBox } from "../gui/Box/ServiceQueryBox.js";
|
||||
import { Input } from "../gui/Widgets/Input.js";
|
||||
import { Layout } from "../gui/Layout.js";
|
||||
import { HiPSProperties } from "../HiPSProperties.js";
|
||||
import A from "../A.js";
|
||||
|
||||
export let Datalink = (function() {
|
||||
|
||||
@@ -68,11 +72,25 @@ export let Datalink = (function() {
|
||||
})
|
||||
let self = this;
|
||||
let datalinkTable = {
|
||||
'name': 'Datalink:' + url,
|
||||
'color': 'purple',
|
||||
'rows': measures,
|
||||
'fields': fields,
|
||||
'showCallback': {
|
||||
name: 'Datalink:' + url,
|
||||
color: 'purple',
|
||||
rows: measures,
|
||||
fields,
|
||||
showCallback: {
|
||||
content_type: (data) => {
|
||||
let contentType = data['content_type'];
|
||||
|
||||
if (contentType && contentType.includes('datalink')) {
|
||||
return new ActionButton({
|
||||
size: 'small',
|
||||
content: '🔗',
|
||||
tooltip: {content: 'Datalink VOTable', aladin: aladinInstance, global: true},
|
||||
action(e) {}
|
||||
}).element();
|
||||
} else {
|
||||
return contentType;
|
||||
}
|
||||
},
|
||||
'service_def': (data) => {
|
||||
const serviceName = data['service_def'];
|
||||
|
||||
@@ -80,7 +98,7 @@ export let Datalink = (function() {
|
||||
return new ActionButton({
|
||||
size: 'small',
|
||||
content: '📡',
|
||||
tooltip: {content: 'Open the cutout service form', position: {direction: 'top'}},
|
||||
tooltip: {global: true, aladin: aladinInstance, content: 'Open the cutout service form'},
|
||||
action(e) {
|
||||
if (self.serviceQueryBox) {
|
||||
self.serviceQueryBox.remove()
|
||||
@@ -137,8 +155,90 @@ export let Datalink = (function() {
|
||||
break;
|
||||
case 'application/hips':
|
||||
// Clic on a HiPS
|
||||
let survey = aladinInstance.newImageSurvey(url);
|
||||
aladinInstance.setOverlayImageLayer(survey, Utils.uuidv4())
|
||||
let layer = Utils.uuidv4();
|
||||
if (contentQualifier === "cube") {
|
||||
let cubeOnTheFlyUrl = url + '/';
|
||||
|
||||
HiPSProperties.fetchFromUrl(cubeOnTheFlyUrl)
|
||||
.then(properties => {
|
||||
let numSlices = +properties.hips_cube_depth;
|
||||
let idxSlice = +properties.hips_cube_firstframe;
|
||||
|
||||
let updateSlice = () => {
|
||||
let colorCfg = aladinInstance.getOverlayImageLayer(layer).getColorCfg();
|
||||
let hips = aladinInstance.setOverlayImageLayer(cubeOnTheFlyUrl + idxSlice, layer)
|
||||
hips.setColorCfg(colorCfg)
|
||||
|
||||
slicer.update({
|
||||
value: idxSlice,
|
||||
tooltip: {content: (idxSlice + 1) + '/' + numSlices, position: {direction: 'bottom'}},
|
||||
})
|
||||
};
|
||||
|
||||
let slicer = Input.slider({
|
||||
label: "Slice",
|
||||
name: "cube slicer",
|
||||
ticks: [idxSlice],
|
||||
tooltip: {content: (idxSlice + 1) + '/' + numSlices , position: {direction: 'bottom'}},
|
||||
min: 0,
|
||||
max: numSlices - 1,
|
||||
value: idxSlice,
|
||||
actions: {
|
||||
change: (e) => {
|
||||
idxSlice = Math.round(e.target.value);
|
||||
|
||||
updateSlice();
|
||||
},
|
||||
input: (e) => {
|
||||
idxSlice = Math.round(e.target.value);
|
||||
|
||||
slicer.update({
|
||||
value: idxSlice,
|
||||
tooltip: {content: (idxSlice + 1) + '/' + numSlices, position: {direction: 'bottom'}},
|
||||
})
|
||||
}
|
||||
},
|
||||
cssStyle: {
|
||||
width: '300px'
|
||||
}
|
||||
});
|
||||
|
||||
let prevBtn = A.button({
|
||||
size: 'small',
|
||||
content: '<',
|
||||
action(o) {
|
||||
idxSlice = Math.max(idxSlice - 1, 0);
|
||||
updateSlice()
|
||||
}
|
||||
})
|
||||
|
||||
let nextBtn = A.button({
|
||||
size: 'small',
|
||||
content: '>',
|
||||
action(o) {
|
||||
idxSlice = Math.min(idxSlice + 1, numSlices - 1);
|
||||
updateSlice()
|
||||
}
|
||||
})
|
||||
|
||||
let cubeDisplayer = A.box({
|
||||
close: true,
|
||||
header: {
|
||||
title: 'HiPS cube player',
|
||||
draggable: true,
|
||||
},
|
||||
content: Layout.horizontal([prevBtn, nextBtn, slicer]),
|
||||
position: {anchor: 'center top'},
|
||||
});
|
||||
aladinInstance.addUI(cubeDisplayer)
|
||||
|
||||
aladinInstance.setOverlayImageLayer(cubeOnTheFlyUrl + idxSlice, layer)
|
||||
})
|
||||
} else {
|
||||
let survey = aladinInstance.newImageSurvey(url);
|
||||
aladinInstance.setOverlayImageLayer(survey, layer)
|
||||
}
|
||||
|
||||
break;
|
||||
// Any generic FITS file
|
||||
case 'application/fits':
|
||||
@@ -159,7 +259,7 @@ export let Datalink = (function() {
|
||||
break;
|
||||
default:
|
||||
// When all has been done, download what's under the link
|
||||
//Utils.download(url);
|
||||
Utils.openNewTab(url);
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
@@ -238,21 +238,20 @@
|
||||
|
||||
return accessUrlEl;
|
||||
},
|
||||
/*'access_format': (data) => {
|
||||
'access_format': (data) => {
|
||||
let accessFormat = data['access_format'];
|
||||
|
||||
if (accessFormat && accessFormat.includes('datalink')) {
|
||||
return new ActionButton({
|
||||
size: 'small',
|
||||
content: '🔗',
|
||||
tooltip: {content: accessFormat, position: {direction: 'right'}},
|
||||
tooltip: {content: 'Datalink VOTable', aladin: aladinInstance, global: true},
|
||||
action(e) {}
|
||||
}).element();
|
||||
return accessFormat;
|
||||
} else {
|
||||
return accessFormat;
|
||||
}
|
||||
}*/
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -458,19 +458,19 @@ export class VOTable {
|
||||
subInputs: [{
|
||||
name: 'ra',
|
||||
label: 'ra[' + unit + ']',
|
||||
type: 'number',
|
||||
type: 'text',
|
||||
value: values && values[0],
|
||||
},
|
||||
{
|
||||
name: 'dec',
|
||||
label: 'dec[' + unit + ']',
|
||||
type: 'number',
|
||||
type: 'text',
|
||||
value: values && values[1],
|
||||
},
|
||||
{
|
||||
name: 'rad',
|
||||
label: 'rad[' + unit + ']',
|
||||
type: 'number',
|
||||
type: 'text',
|
||||
value: values && values[2],
|
||||
}]
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user