mirror of
https://github.com/cds-astro/aladin-lite.git
synced 2025-12-25 04:16:55 -08:00
Compare commits
5 Commits
customize-
...
browse_hip
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6fd09ce10e | ||
|
|
51ee3c0516 | ||
|
|
fa1bd8353a | ||
|
|
1d00b58351 | ||
|
|
a0fc0306e7 |
@@ -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
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
import A from '../src/js/A.js';
|
||||
A.init.then(() => {
|
||||
var aladin = A.aladin('#aladin-lite-div', {target: '05 37 58 +08 17 35', fov: 12, backgroundColor: 'rgb(120, 0, 0)'});
|
||||
var cat = A.catalog({sourceSize: 20});
|
||||
var cat = A.catalog({sourceSize: 20, onClick: 'showTable'});
|
||||
aladin.addCatalog(cat);
|
||||
cat.addSources([A.source(83.784490, 9.934156, {name: 'Meissa'}), A.source(88.792939, 7.407064, {name: 'Betelgeuse'}), A.source(81.282764, 6.349703, {name: 'Bellatrix'})]);
|
||||
var msg;
|
||||
@@ -51,6 +51,8 @@
|
||||
}
|
||||
$('#infoDiv').html(msg);
|
||||
});
|
||||
|
||||
cat.sources[0].actionClicked();
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
showCooGrid: false, // set the grid
|
||||
fullScreen: true,
|
||||
inertia: false,
|
||||
showStatusBar: false,
|
||||
showStatusBar: true,
|
||||
showShareControl: true,
|
||||
showSettingsControl: true,
|
||||
showLayersControl: true,
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
fov: 360,
|
||||
target: '0 0',
|
||||
fullScreen: true,
|
||||
survey: ["fsdfsd", "jfjfj", "jfj", "P/allWISE/color"],
|
||||
survey: ["azef", "jfjfj", "jfj", "P/allWISE/color"],
|
||||
showCooGrid: true,
|
||||
});
|
||||
});
|
||||
|
||||
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>
|
||||
@@ -14,7 +14,7 @@
|
||||
fullScreen: true,
|
||||
target: "centaurus A",
|
||||
fov: 10,
|
||||
projection: 'AIT',
|
||||
projection: 'TAN',
|
||||
showContextMenu: true,
|
||||
showSimbadPointerControl: true,
|
||||
expandLayersControl: true,
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
aladin = A.aladin("#aladin-lite-div", {
|
||||
fullScreen: true,
|
||||
target: "m51",
|
||||
fov: 90,
|
||||
fov: 15,
|
||||
projection: "AIT",
|
||||
showContextMenu: true,
|
||||
showShareControl: true,
|
||||
@@ -73,11 +73,11 @@ aladin = A.aladin("#aladin-lite-div", {
|
||||
],
|
||||
});
|
||||
|
||||
aladin.setImageLayer(A.imageHiPS("astron.nl/P/lotss_dr2_high"));
|
||||
aladin.setImageLayer(A.imageHiPS("P/DSS2/color"));
|
||||
|
||||
aladin.addCatalog(
|
||||
A.catalogFromSKAORucio("m51", 10, {
|
||||
onClick: "showTable",
|
||||
A.catalogFromSKAORucio("m51", 15, {
|
||||
onClick: 'showTable',
|
||||
hoverColor: "yellow",
|
||||
})
|
||||
);
|
||||
|
||||
@@ -17,14 +17,13 @@
|
||||
aladin.on("zoomChanged", () => {
|
||||
console.log("zoomChanged")
|
||||
})
|
||||
|
||||
aladin.on("positionChanged", ({ra, dec}) => {
|
||||
|
||||
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)
|
||||
})
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"homepage": "https://aladin.u-strasbg.fr/",
|
||||
"name": "aladin-lite",
|
||||
"type": "module",
|
||||
"version": "3.4.0-beta",
|
||||
"version": "3.4.1-beta",
|
||||
"description": "An astronomical HiPS visualizer in the browser",
|
||||
"author": "Thomas Boch and Matthieu Baumann",
|
||||
"license": "GPL-3",
|
||||
|
||||
@@ -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)]
|
||||
|
||||
@@ -5,6 +5,8 @@
|
||||
/* disable x swipe on chrome, firefox */
|
||||
/* see. https://stackoverflow.com/questions/30636930/disable-web-page-navigation-on-swipeback-and-forward */
|
||||
overscroll-behavior-x: none;
|
||||
/* Hide the draggable boxes that goes out of the view */
|
||||
overflow: hidden;
|
||||
/* media query on the aladin lite container. not supported everywhere.
|
||||
There can be a more supported alternative here: https://caniuse.com/?search=grid-template-columns */
|
||||
/*container-type: inline-size;*/
|
||||
@@ -94,56 +96,30 @@
|
||||
.aladin-measurement-div.aladin-dark-theme table thead {
|
||||
background-color: #000;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.aladin-measurement-div table td.aladin-href-td-container a:hover {
|
||||
overflow: visible;
|
||||
display: inline-block;
|
||||
animation: leftright 5s infinite normal linear;
|
||||
}
|
||||
|
||||
@keyframes leftright {
|
||||
0%,
|
||||
20% {
|
||||
transform: translateX(0%);
|
||||
left: 0%;
|
||||
}
|
||||
80%,
|
||||
100% {
|
||||
/* the max width is 150px and a padding of 0.8em is added for href link */
|
||||
transform: translateX(-100%);
|
||||
left: -100%;
|
||||
}
|
||||
}
|
||||
|
||||
.aladin-measurement-div table th {
|
||||
padding: 0.3em 0.5em;
|
||||
}
|
||||
|
||||
.aladin-measurement-div table td.aladin-href-td-container {
|
||||
border: 1px solid #d2d2d2;
|
||||
.aladin-measurement-div table tr td a {
|
||||
display: block;
|
||||
}
|
||||
|
||||
border-radius: 3px;
|
||||
.aladin-measurement-div table tr td {
|
||||
padding: 0.5rem;
|
||||
}
|
||||
|
||||
.aladin-measurement-div table tr td, .aladin-measurement-div table tr td a {
|
||||
max-width: 10rem;
|
||||
text-overflow: ellipsis;
|
||||
|
||||
padding: 0.5em;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-align: center;
|
||||
/*max-width: 150px;
|
||||
text-overflow: ellipsis;*/
|
||||
}
|
||||
|
||||
.aladin-measurement-div table td.aladin-text-td-container {
|
||||
padding: 0.5em;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
|
||||
/*max-width: 150px;
|
||||
text-overflow: ellipsis;*/
|
||||
}
|
||||
|
||||
.aladin-measurement-div table td.aladin-href-td-container:hover {
|
||||
background-color: #fff;
|
||||
word-wrap:break-word;
|
||||
}
|
||||
|
||||
.aladin-marker-measurement {
|
||||
@@ -159,10 +135,6 @@
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.aladin-marker-measurement table tr td{
|
||||
word-wrap:break-word;
|
||||
}
|
||||
|
||||
.aladin-marker-measurement tr:nth-child(even) {
|
||||
background-color: #dddddd;
|
||||
}
|
||||
@@ -353,16 +325,15 @@ canvas {
|
||||
.aladin-btn.aladin-dark-theme.toggled {
|
||||
border-color: greenyellow;
|
||||
}
|
||||
.aladin-btn.aladin-dark-theme:hover, .aladin-input-select.aladin-dark-theme:hover {
|
||||
border-color: red;
|
||||
}
|
||||
|
||||
.aladin-btn.disabled {
|
||||
cursor: not-allowed;
|
||||
filter: brightness(70%);
|
||||
}
|
||||
|
||||
.aladin-btn:not(.disabled):hover {
|
||||
filter: brightness(105%);
|
||||
}
|
||||
|
||||
.aladin-btn.svg-icon {
|
||||
background-repeat: no-repeat;
|
||||
background-position:center center;
|
||||
@@ -373,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 {
|
||||
@@ -417,16 +395,19 @@ canvas {
|
||||
height: 1.7rem;
|
||||
}
|
||||
|
||||
.aladin-input-text.aladin-dark-theme:focus, .aladin-input-number.aladin-dark-theme:focus {
|
||||
border-color: dodgerblue;
|
||||
}
|
||||
|
||||
.aladin-input-text.aladin-dark-theme.search {
|
||||
width: 15rem;
|
||||
text-shadow: 0px 0px 2px #000;
|
||||
}
|
||||
|
||||
.aladin-input-text.aladin-dark-theme.search:focus, .aladin-input-text.aladin-dark-theme.search:hover {
|
||||
.aladin-input-text.search:focus, .aladin-input-text.search:hover {
|
||||
background-image: url(../../assets/icons/search-white.svg);
|
||||
background-size: 1.8rem;
|
||||
background-repeat: no-repeat;
|
||||
text-indent: 1.8rem;
|
||||
padding-left: 1.8rem;
|
||||
}
|
||||
|
||||
.aladin-input-text.search {
|
||||
@@ -436,14 +417,20 @@ canvas {
|
||||
line-height: 1.2rem;
|
||||
}
|
||||
|
||||
.aladin-input-text.search.aladin-unknownObject {
|
||||
.aladin-input-text.search.aladin-not-valid {
|
||||
-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;
|
||||
}
|
||||
|
||||
.aladin-input-text.aladin-dark-theme.aladin-not-valid {
|
||||
border: 1px solid red;
|
||||
}
|
||||
|
||||
.aladin-input-text.aladin-dark-theme.aladin-valid {
|
||||
border: 1px solid yellowgreen;
|
||||
}
|
||||
|
||||
.aladin-cancelBtn {
|
||||
background-color: #ca4242;
|
||||
border-color: #bd3935;
|
||||
@@ -479,12 +466,12 @@ canvas {
|
||||
align-items: flex-end;
|
||||
}
|
||||
|
||||
.aladin-vertical-list > *:first-of-type {
|
||||
.aladin-vertical-list > *:first-child {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.aladin-vertical-list > * {
|
||||
margin-top: 0.2rem;
|
||||
margin-top: 0.5rem;
|
||||
}
|
||||
|
||||
.aladin-horizontal-list {
|
||||
@@ -498,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;
|
||||
@@ -510,8 +500,17 @@ 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%;
|
||||
}
|
||||
|
||||
.aladin-form .aladin-form-group:last-of-type {
|
||||
margin-bottom: 0rem;
|
||||
}
|
||||
|
||||
.aladin-form .aladin-form-input select {
|
||||
@@ -886,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 */
|
||||
@@ -900,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;
|
||||
@@ -1096,17 +1096,23 @@ canvas {
|
||||
height: 1.7rem;
|
||||
}
|
||||
|
||||
/*
|
||||
.aladin-input-text.aladin-dark-theme.search.aladin-HiPS-search {
|
||||
width: 100%;
|
||||
|
||||
}
|
||||
}*/
|
||||
|
||||
.aladin-stack-box {
|
||||
width: 17rem;
|
||||
}
|
||||
|
||||
.aladin-stack-box .content > * {
|
||||
margin-bottom: 0.5rem;
|
||||
.aladin-HiPS-filter-box {
|
||||
border: 1px solid white;
|
||||
}
|
||||
|
||||
.aladin-HiPS-browser-box .aladin-input-text {
|
||||
width: 300px;
|
||||
padding: 0.5rem;
|
||||
}
|
||||
|
||||
.aladin-stack-box .aladin-input-select {
|
||||
@@ -1126,6 +1132,10 @@ canvas {
|
||||
height: 1.7rem;
|
||||
}
|
||||
|
||||
.aladin-location .aladin-input-text {
|
||||
width: 15rem;
|
||||
}
|
||||
|
||||
.aladin-fov {
|
||||
position: absolute;
|
||||
bottom: 0.2rem;
|
||||
|
||||
@@ -106,13 +106,13 @@ A.aladin = function (divSelector, options) {
|
||||
* @function
|
||||
* @name A.imageHiPS
|
||||
* @memberof A
|
||||
* @param {string} id - Mandatory unique identifier for the survey.
|
||||
* @param {string} url - 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
|
||||
*/
|
||||
A.imageHiPS = function (id, url, options) {
|
||||
A.imageHiPS = function (id, options) {
|
||||
let url = id;
|
||||
return Aladin.createImageSurvey(
|
||||
id,
|
||||
options && options.name,
|
||||
|
||||
1327
src/js/Aladin.js
1327
src/js/Aladin.js
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -33,13 +33,13 @@ export let HiPSCache = (function () {
|
||||
HiPSCache.append = function (key, image) {
|
||||
HiPSCache.cache[key] = image;
|
||||
|
||||
ALEvent.HIPS_LIST_UPDATED.dispatchedTo(document.body);
|
||||
ALEvent.HIPS_CACHE_UPDATED.dispatchedTo(document.body);
|
||||
};
|
||||
|
||||
HiPSCache.delete = function (key) {
|
||||
delete HiPSCache.cache[key];
|
||||
|
||||
ALEvent.HIPS_LIST_UPDATED.dispatchedTo(document.body);
|
||||
ALEvent.HIPS_CACHE_UPDATED.dispatchedTo(document.body);
|
||||
};
|
||||
|
||||
HiPSCache.get = function (key) {
|
||||
|
||||
@@ -59,7 +59,7 @@ HiPSProperties.fetchFromID = async function(ID) {
|
||||
result = matching;
|
||||
} else {
|
||||
result = metadata[0];
|
||||
console.warn("Multiple surveys are matching, please choose one. The chosen one is: " + result);
|
||||
console.warn("Multiple surveys are matching, please choose one. The chosen one is: " + result.ID);
|
||||
}
|
||||
} else {
|
||||
// Exactly one matching
|
||||
|
||||
@@ -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;
|
||||
@@ -88,13 +87,6 @@ export let ImageFITS = (function () {
|
||||
}
|
||||
|
||||
ImageFITS.prototype._saveInCache = function () {
|
||||
/*ImageHiPS.cache[this.id] = this;
|
||||
|
||||
// Tell that the HiPS List has been updated
|
||||
if (this.view) {
|
||||
ALEvent.HIPS_LIST_UPDATED.dispatchedTo(this.view.aladin.aladinDiv);
|
||||
}*/
|
||||
|
||||
HiPSCache.append(this.id, this);
|
||||
};
|
||||
|
||||
@@ -261,10 +253,6 @@ export let ImageFITS = (function () {
|
||||
return self;
|
||||
})
|
||||
.catch((e) => {
|
||||
if (self.errorCallback) {
|
||||
self.errorCallback();
|
||||
}
|
||||
|
||||
// This error result from a promise
|
||||
// If I throw it, it will not be catched because
|
||||
// it is run async
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
@@ -142,6 +133,8 @@ PropertyParser.isPlanetaryBody = function (properties) {
|
||||
* @typedef {Object} ImageHiPSOptions
|
||||
*
|
||||
* @property {string} [name] - The name of the survey to be displayed in the UI
|
||||
* @property {Function} [successCallback] - A callback executed when the HiPS has been loaded
|
||||
* @property {Function} [errorCallback] - A callback executed when the HiPS could not be loaded
|
||||
* @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.
|
||||
@@ -180,13 +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
|
||||
@@ -194,6 +186,8 @@ export let ImageHiPS = (function () {
|
||||
this.imgFormat = options.imgFormat;
|
||||
this.numBitsPerPixel = options.numBitsPerPixel;
|
||||
this.creatorDid = options.creatorDid;
|
||||
this.errorCallback = options.errorCallback;
|
||||
this.successCallback = options.successCallback;
|
||||
|
||||
this.colorCfg = new ColorCfg(options);
|
||||
}
|
||||
@@ -296,12 +290,8 @@ export let ImageHiPS = (function () {
|
||||
}
|
||||
})
|
||||
.catch((e) => {
|
||||
//alert(e);
|
||||
console.error(self);
|
||||
console.error(e);
|
||||
// the survey has been added so we remove it from the stack
|
||||
//self.view.removeImageLayer(self.layer)
|
||||
//throw e;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -361,6 +351,7 @@ export let ImageHiPS = (function () {
|
||||
}
|
||||
|
||||
self.name = self.name || self.id || self.url;
|
||||
self.name = self.name.replace(/ +/g, ' ');
|
||||
|
||||
self.creatorDid = self.creatorDid || self.id || self.url;
|
||||
|
||||
@@ -460,7 +451,7 @@ export let ImageHiPS = (function () {
|
||||
self._saveInCache();
|
||||
|
||||
return self;
|
||||
})();
|
||||
})()
|
||||
};
|
||||
|
||||
ImageHiPS.prototype._saveInCache = function () {
|
||||
@@ -472,10 +463,13 @@ 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,
|
||||
imgFormat: self.imgFormat,
|
||||
successCallback: self.successCallback,
|
||||
errorCallback: self.errorCallback,
|
||||
...colorOpt,
|
||||
};
|
||||
|
||||
@@ -490,21 +484,6 @@ export let ImageHiPS = (function () {
|
||||
// append new important infos from the properties queried
|
||||
...surveyOpt,
|
||||
});
|
||||
|
||||
/*ImageHiPS.cache[self.id] = {
|
||||
// Erase by the cache already put values which is considered
|
||||
// as the ground truth
|
||||
...ImageHiPS.cache[self.id],
|
||||
// append new important infos from the properties queried
|
||||
...surveyOpt,
|
||||
}*/
|
||||
|
||||
//console.log('new CACHE', ImageHiPS.cache, self.id, surveyOpt, ImageHiPS.cache[self.id], ImageHiPS.cache["CSIRO/P/RACS/mid/I"])
|
||||
|
||||
// Tell that the HiPS List has been updated
|
||||
/*if (this.view) {
|
||||
ALEvent.HIPS_LIST_UPDATED.dispatchedTo(this.view.aladin.aladinDiv);
|
||||
}*/
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -831,7 +810,14 @@ export let ImageHiPS = (function () {
|
||||
},
|
||||
});
|
||||
|
||||
return Promise.resolve(this);
|
||||
return Promise.resolve(this)
|
||||
.then((hips) => {
|
||||
if (hips.successCallback) {
|
||||
hips.successCallback(hips)
|
||||
}
|
||||
|
||||
return hips
|
||||
});
|
||||
};
|
||||
|
||||
// @api
|
||||
|
||||
@@ -51,19 +51,46 @@ export class MocServer {
|
||||
expr: "dataproduct_type=image",
|
||||
get: "record",
|
||||
fmt: "json",
|
||||
fields: "ID,hips_creator,hips_copyright,hips_frame,hips_tile_format,obs_title,obs_description,obs_copyright,obs_regime",
|
||||
fields: "ID,hips_creator,hips_copyright,hips_order,hips_tile_width,hips_frame,hips_tile_format,obs_title,obs_description,obs_copyright,obs_regime",
|
||||
//fields: "ID,hips_initial_fov,hips_initial_ra,hips_initial_dec,hips_pixel_bitpix,hips_creator,hips_copyright,hips_frame,hips_order,hips_order_min,hips_tile_width,hips_tile_format,hips_pixel_cut,obs_title,obs_description,obs_copyright,obs_regime,hips_data_range,hips_service_url",
|
||||
};
|
||||
|
||||
this._allHiPSes = Utils.loadFromUrls(MocServer.MIRRORS_HTTPS, {
|
||||
data: params,
|
||||
dataType: 'json'
|
||||
dataType: 'json',
|
||||
desc: 'MOCServer query to get all the HiPS metadata'
|
||||
})
|
||||
}
|
||||
|
||||
return this._allHiPSes;
|
||||
}
|
||||
|
||||
static getAllHiPSesInsideView(aladin) {
|
||||
let params = {
|
||||
//expr: "dataproduct_type=image||dataproduct_type=cube",
|
||||
expr: "dataproduct_type=image",
|
||||
get: "record",
|
||||
fmt: "json",
|
||||
fields: "ID",
|
||||
};
|
||||
|
||||
try {
|
||||
const corners = aladin.getFoVCorners(1, 'icrs');
|
||||
let stc = 'Polygon '
|
||||
for (var radec of corners) {
|
||||
stc += radec[0] + ' ' + radec[1] + ' ';
|
||||
}
|
||||
|
||||
params['stc'] = stc;
|
||||
} catch (e) {}
|
||||
|
||||
return Utils.loadFromUrls(MocServer.MIRRORS_HTTPS, {
|
||||
data: params,
|
||||
dataType: 'json',
|
||||
desc: 'MOCServer: Retrieve HiPS inside FoV'
|
||||
})
|
||||
}
|
||||
|
||||
static getHiPSesFromIDs(ids) {
|
||||
const params = {
|
||||
//expr: "dataproduct_type=image||dataproduct_type=cube",
|
||||
|
||||
@@ -117,7 +117,12 @@ export let Source = (function() {
|
||||
}
|
||||
}
|
||||
|
||||
// function called when a source is clicked. Called by the View object
|
||||
/**
|
||||
* Simulates a click on the source
|
||||
*
|
||||
* @memberof Source
|
||||
* @param {Footprint|Source} [obj] - If not given, the source is taken as the object to be selected
|
||||
*/
|
||||
Source.prototype.actionClicked = function(obj) {
|
||||
if (this.catalog && this.catalog.onClick) {
|
||||
var view = this.catalog.view;
|
||||
|
||||
@@ -265,7 +265,7 @@ Utils.loadFromUrls = function (urls, options) {
|
||||
const mode = options && options.mode || 'cors';
|
||||
const contentType = options && options.contentType || undefined;
|
||||
const dataType = (options && options.dataType) || 'text';
|
||||
|
||||
const desc = options && options.desc;
|
||||
// A controller that can abort the query when a timeout is reached
|
||||
const controller = new AbortController()
|
||||
|
||||
@@ -289,7 +289,9 @@ Utils.loadFromUrls = function (urls, options) {
|
||||
cache: 'default',
|
||||
// Abort the request when a timeout exceeded
|
||||
signal: controller.signal,
|
||||
dataType
|
||||
dataType,
|
||||
// message description
|
||||
desc
|
||||
}
|
||||
|
||||
if (contentType) {
|
||||
@@ -387,13 +389,15 @@ Utils.fetch = function(params) {
|
||||
if (params.success) {
|
||||
return params.success(data)
|
||||
}
|
||||
|
||||
return Promise.resolve(data);
|
||||
})
|
||||
.catch(e => {
|
||||
if (params.error) {
|
||||
params.error(e)
|
||||
} else {
|
||||
alert(e)
|
||||
return params.error(e)
|
||||
}
|
||||
|
||||
return Promise.reject(e);
|
||||
})
|
||||
.finally(() => {
|
||||
ALEvent.RESOURCE_FETCHED.dispatchedTo(document, {task});
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -258,14 +262,9 @@ export let View = (function () {
|
||||
self.fixLayoutDimensions();
|
||||
})
|
||||
} else {*/
|
||||
let resizeLayout = () => {
|
||||
self.fixLayoutDimensions();
|
||||
}
|
||||
|
||||
this.resizeObserver = new ResizeObserver(() => {
|
||||
//clearTimeout(doit);
|
||||
//doit = setTimeout(resizeLayout, 100);
|
||||
resizeLayout();
|
||||
self.fixLayoutDimensions();
|
||||
});
|
||||
|
||||
self.resizeObserver.observe(this.aladinDiv)
|
||||
@@ -1646,6 +1645,10 @@ export let View = (function () {
|
||||
// remove it from the cache
|
||||
HiPSCache.delete(imageLayer.id)
|
||||
|
||||
if (imageLayer.errorCallback) {
|
||||
imageLayer.errorCallback(e);
|
||||
}
|
||||
|
||||
throw e;
|
||||
})
|
||||
.finally(() => {
|
||||
@@ -1742,7 +1745,7 @@ export let View = (function () {
|
||||
if (this.overlayLayers.length === 0) {
|
||||
this.empty = true;
|
||||
} else if (this.selectedLayer === layer) {
|
||||
// If the layer removed was selected then we select the base layer
|
||||
// If the layer removed was selected then we select the last layer
|
||||
this.selectLayer(this.overlayLayers[this.overlayLayers.length - 1]);
|
||||
}
|
||||
|
||||
@@ -1931,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);
|
||||
|
||||
@@ -53,7 +53,7 @@ export class ALEvent {
|
||||
static HIPS_LAYER_RENAMED = new ALEvent("AL:HiPSLayer.renamed");
|
||||
static HIPS_LAYER_SWAP = new ALEvent("AL:HiPSLayer.swap");
|
||||
|
||||
static HIPS_LIST_UPDATED = new ALEvent("AL:HiPSList.updated");
|
||||
static HIPS_CACHE_UPDATED = new ALEvent("AL:HiPSCache.updated");
|
||||
|
||||
static HIPS_LAYER_CHANGED = new ALEvent("AL:HiPSLayer.changed");
|
||||
|
||||
|
||||
327
src/js/gui/Box/HiPSBrowserBox.js
Normal file
327
src/js/gui/Box/HiPSBrowserBox.js
Normal file
@@ -0,0 +1,327 @@
|
||||
// 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 { MocServer } from "../../MocServer.js";
|
||||
|
||||
import { Box } from "../Widgets/Box.js";
|
||||
import { Dropdown } from "../Input/Dropdown.js";
|
||||
import filterOnUrl from "../../../../assets/icons/filter-on.svg";
|
||||
import filterOffUrl from "../../../../assets/icons/filter-off.svg";
|
||||
import { Input } from "../Widgets/Input.js";
|
||||
import { TogglerActionButton } from "../Button/Toggler.js";
|
||||
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
|
||||
*
|
||||
* File gui/HiPSBrowserBox.js
|
||||
*
|
||||
*
|
||||
* Author: Matthieu Baumann[CDS]
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
export class HiPSBrowserBox extends Box {
|
||||
static HiPSList = {};
|
||||
|
||||
constructor(aladin, options) {
|
||||
let self;
|
||||
|
||||
MocServer.getAllHiPSes().then((HiPSes) => {
|
||||
// Fill the HiPSList from the MOCServer
|
||||
HiPSes.forEach((h) => {
|
||||
HiPSBrowserBox.HiPSList[h.obs_title] = h;
|
||||
});
|
||||
|
||||
// Initialize the autocompletion without any filtering
|
||||
self._filterHiPSList({})
|
||||
});
|
||||
|
||||
const _parseHiPS = (e) => {
|
||||
const value = e.target.value;
|
||||
|
||||
let image;
|
||||
// A user can put an url
|
||||
try {
|
||||
image = new URL(value).href;
|
||||
} catch (e) {
|
||||
// Or he can select a HiPS from the list given
|
||||
const hips = HiPSBrowserBox.HiPSList[value];
|
||||
|
||||
if (hips) {
|
||||
image = hips.ID || hips.hips_service_url;
|
||||
} else {
|
||||
// Finally if not found, interpret the input text value as the HiPS (e.g. ID)
|
||||
image = value;
|
||||
}
|
||||
}
|
||||
|
||||
if (image) {
|
||||
self._addHiPS(image)
|
||||
self.searchDropdown.update({title: value});
|
||||
}
|
||||
};
|
||||
|
||||
let searchDropdown = new Dropdown(aladin, {
|
||||
name: "HiPS browser",
|
||||
placeholder: "Browse a HiPS by an URL, ID or keywords",
|
||||
tooltip: {
|
||||
global: true,
|
||||
aladin,
|
||||
content: 'HiPS url, ID or keyword accepted',
|
||||
},
|
||||
actions: {
|
||||
focus(e) {
|
||||
searchDropdown.removeClass('aladin-valid')
|
||||
searchDropdown.removeClass('aladin-not-valid')
|
||||
},
|
||||
keydown(e) {
|
||||
e.stopPropagation();
|
||||
|
||||
if (e.key === 'Enter') {
|
||||
e.preventDefault()
|
||||
_parseHiPS(e)
|
||||
}
|
||||
},
|
||||
input(e) {
|
||||
self.infoCurrentHiPSBtn.update({
|
||||
disable: true,
|
||||
})
|
||||
|
||||
searchDropdown.removeClass('aladin-valid')
|
||||
searchDropdown.removeClass('aladin-not-valid')
|
||||
},
|
||||
change(e) {
|
||||
_parseHiPS(e)
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
let filterEnabler = Input.checkbox({
|
||||
name: "filter-enabler",
|
||||
checked: false,
|
||||
tooltip: {
|
||||
content: "Filter off",
|
||||
position: {direction: 'left'},
|
||||
},
|
||||
click(e) {
|
||||
let on = e.target.checked;
|
||||
self.filterBox.enable(on);
|
||||
|
||||
if (!on) {
|
||||
// if the filter has been disabled we also need to update
|
||||
// the autocompletion list of the search dropdown
|
||||
// We give no filter params
|
||||
self._filterHiPSList({});
|
||||
}
|
||||
|
||||
filterBtn.update({
|
||||
icon: {
|
||||
url: on ? filterOnUrl : filterOffUrl,
|
||||
monochrome: true,
|
||||
},
|
||||
});
|
||||
|
||||
filterEnabler.update({
|
||||
tooltip: {
|
||||
content: on
|
||||
? "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,
|
||||
monochrome: true,
|
||||
},
|
||||
size: "small",
|
||||
tooltip: {
|
||||
content: "Want to filter HiPS surveys by criteria ?",
|
||||
position: { direction: "top" },
|
||||
},
|
||||
toggled: false,
|
||||
actionOn: (e) => {
|
||||
self.filterBox._show({
|
||||
position: {
|
||||
nextTo: filterBtn,
|
||||
direction: "right",
|
||||
aladin,
|
||||
},
|
||||
});
|
||||
},
|
||||
actionOff: (e) => {
|
||||
self.filterBox._hide();
|
||||
},
|
||||
});
|
||||
|
||||
super(
|
||||
{
|
||||
close: true,
|
||||
header: {
|
||||
title: "HiPS browser",
|
||||
},
|
||||
onDragged: () => {
|
||||
if (self.filterBtn.toggled) {
|
||||
self.filterBtn.toggle();
|
||||
}
|
||||
},
|
||||
classList: ['aladin-HiPS-browser-box'],
|
||||
content: Layout.vertical([
|
||||
Layout.horizontal(["Search:", searchDropdown, infoCurrentHiPSBtn]),
|
||||
Layout.horizontal(["Filter:", Layout.horizontal([filterEnabler, filterBtn])]),
|
||||
]),
|
||||
...options,
|
||||
},
|
||||
aladin.aladinDiv
|
||||
);
|
||||
|
||||
this.filterBox = new HiPSFilterBox(aladin, {
|
||||
callback: (params) => {
|
||||
self._filterHiPSList(params);
|
||||
},
|
||||
})
|
||||
this.filterBox._hide();
|
||||
|
||||
this.searchDropdown = searchDropdown;
|
||||
this.filterBtn = filterBtn;
|
||||
this.aladin = aladin;
|
||||
|
||||
this.infoCurrentHiPSBtn = infoCurrentHiPSBtn;
|
||||
|
||||
self = this;
|
||||
|
||||
this.filterCallback = (HiPS, params) => {
|
||||
if (!HiPS.obs_regime || (
|
||||
params.regime &&
|
||||
HiPS.obs_regime &&
|
||||
params.regime.toLowerCase() !==
|
||||
HiPS.obs_regime.toLowerCase()
|
||||
)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (Array.isArray(params.spatial) && HiPS.ID && !(params.spatial.includes(HiPS.ID))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!HiPS.hips_tile_width || !HiPS.hips_order)
|
||||
return false;
|
||||
|
||||
if (params.resolution) {
|
||||
let pixelHEALPixOrder = Math.log2(HiPS.hips_tile_width) + HiPS.hips_order;
|
||||
let resPixel = Math.sqrt(Math.PI / (3*Math.pow(4, pixelHEALPixOrder)));
|
||||
|
||||
if (resPixel > params.resolution)
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
}
|
||||
|
||||
_addHiPS(id) {
|
||||
let self = this;
|
||||
let hips = A.imageHiPS(id, {
|
||||
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');
|
||||
self.searchDropdown.addClass('aladin-not-valid');
|
||||
}
|
||||
});
|
||||
this.aladin.setOverlayImageLayer(hips, self.layer);
|
||||
}
|
||||
|
||||
// This method is executed only if the filter is enabled
|
||||
_filterHiPSList(params) {
|
||||
console.log("update dropdown")
|
||||
let self = this;
|
||||
let HiPSIDs = [];
|
||||
|
||||
for (var key in HiPSBrowserBox.HiPSList) {
|
||||
let HiPS = HiPSBrowserBox.HiPSList[key];
|
||||
|
||||
// apply filtering
|
||||
if (
|
||||
self.filterCallback &&
|
||||
self.filterCallback(HiPS, params)
|
||||
) {
|
||||
// search with the name or id
|
||||
HiPSIDs.push(HiPS.obs_title);
|
||||
}
|
||||
}
|
||||
|
||||
self.searchDropdown.update({ options: HiPSIDs });
|
||||
}
|
||||
|
||||
_hide() {
|
||||
if (this.filterBox)
|
||||
this.filterBox.signalBrowserStatus(true)
|
||||
|
||||
if (this.filterBtn && this.filterBtn.toggled) {
|
||||
this.filterBtn.toggle();
|
||||
}
|
||||
|
||||
super._hide()
|
||||
}
|
||||
|
||||
_show(options) {
|
||||
// Regenerate a new layer name
|
||||
this.layer = Utils.uuidv4()
|
||||
|
||||
if (this.filterBox)
|
||||
this.filterBox.signalBrowserStatus(false)
|
||||
|
||||
super._show(options)
|
||||
}
|
||||
}
|
||||
230
src/js/gui/Box/HiPSFilterBox.js
Normal file
230
src/js/gui/Box/HiPSFilterBox.js
Normal file
@@ -0,0 +1,230 @@
|
||||
// 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 { Form } from "../Widgets/Form.js";
|
||||
import { MocServer } from "../../MocServer.js";
|
||||
import { TogglerActionButton } from "../Button/Toggler.js";
|
||||
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
|
||||
*
|
||||
* File gui/HiPSBrowserBox.js
|
||||
*
|
||||
*
|
||||
* Author: Matthieu Baumann[CDS]
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
export class HiPSFilterBox extends Box {
|
||||
constructor(aladin, options) {
|
||||
let self;
|
||||
|
||||
let regimeBtn = new TogglerActionButton({
|
||||
content: 'Regime',
|
||||
tooltip: {content: 'Observation regime', position: {direction: 'bottom'}},
|
||||
toggled: true,
|
||||
actionOn: () => {
|
||||
self._triggerFilteringCallback();
|
||||
},
|
||||
actionOff: () => {
|
||||
self._triggerFilteringCallback();
|
||||
}
|
||||
});
|
||||
let spatialBtn = new TogglerActionButton({
|
||||
content: 'Inside view',
|
||||
tooltip: {content: 'Check for HiPS having observation in the view!', position: {direction: 'bottom'}},
|
||||
toggled: false,
|
||||
actionOn: () => {
|
||||
self._requestMOCServer();
|
||||
},
|
||||
actionOff: () => {
|
||||
self._triggerFilteringCallback();
|
||||
}
|
||||
});
|
||||
let resolutionBtn = new TogglerActionButton({
|
||||
content: 'Pixel res',
|
||||
tooltip: {content: 'Check for HiPS with a specific pixel resolution.', position: {direction: 'bottom'}},
|
||||
toggled: false,
|
||||
actionOn: () => {
|
||||
self._triggerFilteringCallback();
|
||||
},
|
||||
actionOff: () => {
|
||||
self._triggerFilteringCallback();
|
||||
}
|
||||
});
|
||||
|
||||
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([
|
||||
'<b>Filter by:</b>',
|
||||
Layout.horizontal([regimeBtn, spatialBtn, resolutionBtn]),
|
||||
'<b>Details:</b>',
|
||||
new Form({
|
||||
subInputs: [
|
||||
{
|
||||
type: "group",
|
||||
subInputs: [
|
||||
{
|
||||
label: "Regime:",
|
||||
name: "regime",
|
||||
value: "Optical",
|
||||
type: 'select',
|
||||
options: [
|
||||
"Radio",
|
||||
"Infrared",
|
||||
"Millimeter",
|
||||
"Optical",
|
||||
"UV",
|
||||
"EUV",
|
||||
"X-ray",
|
||||
"Gamma-ray",
|
||||
],
|
||||
change: (e) => {
|
||||
let regime = e.target.value;
|
||||
self.params["regime"] = regime;
|
||||
|
||||
//regimeBtn.update({content: regime});
|
||||
|
||||
self._triggerFilteringCallback();
|
||||
},
|
||||
tooltip: {
|
||||
content: "Observation regime",
|
||||
position: { direction: "right" },
|
||||
},
|
||||
},
|
||||
logSlider
|
||||
],
|
||||
},
|
||||
],
|
||||
}),
|
||||
])
|
||||
},
|
||||
aladin.aladinDiv
|
||||
);
|
||||
|
||||
self = this;
|
||||
|
||||
this.browserClosed = false;
|
||||
|
||||
this.callback = options.callback;
|
||||
|
||||
this.regimeBtn = regimeBtn;
|
||||
this.spatialBtn = spatialBtn;
|
||||
this.resolutionBtn = resolutionBtn;
|
||||
|
||||
this.params = {
|
||||
regime: "Optical",
|
||||
spatial: true,
|
||||
resolution: 1, // 1°/pixel
|
||||
};
|
||||
this.on = false;
|
||||
this.aladin = aladin;
|
||||
this._addListeners();
|
||||
}
|
||||
|
||||
_addListeners() {
|
||||
const requestMOCServerDebounced = Utils.debounce(() => {
|
||||
this._requestMOCServer()
|
||||
}, 500);
|
||||
|
||||
ALEvent.POSITION_CHANGED.listenedBy(this.aladin.aladinDiv, requestMOCServerDebounced);
|
||||
ALEvent.ZOOM_CHANGED.listenedBy(this.aladin.aladinDiv, requestMOCServerDebounced);
|
||||
}
|
||||
|
||||
_requestMOCServer() {
|
||||
if (!this.spatialBtn.toggled || !this.on || this.browserClosed) {
|
||||
return;
|
||||
}
|
||||
|
||||
let self = this;
|
||||
MocServer.getAllHiPSesInsideView(this.aladin)
|
||||
.then((HiPSes) => {
|
||||
let HiPSIDs = HiPSes.map((x) => x.ID);
|
||||
self.params["spatial"] = HiPSIDs;
|
||||
|
||||
self._triggerFilteringCallback();
|
||||
})
|
||||
}
|
||||
|
||||
_triggerFilteringCallback() {
|
||||
let filterParams = {};
|
||||
|
||||
if (this.regimeBtn.toggled) {
|
||||
filterParams['regime'] = this.params['regime']
|
||||
}
|
||||
|
||||
if (this.spatialBtn.toggled) {
|
||||
filterParams['spatial'] = this.params['spatial']
|
||||
}
|
||||
|
||||
if (this.resolutionBtn.toggled) {
|
||||
filterParams['resolution'] = this.params['resolution']
|
||||
}
|
||||
|
||||
if (this.on && this.callback) {
|
||||
this.callback(filterParams);
|
||||
}
|
||||
}
|
||||
|
||||
signalBrowserStatus(closed) {
|
||||
this.browserClosed = closed;
|
||||
|
||||
// open
|
||||
if (!closed) {
|
||||
this._requestMOCServer()
|
||||
}
|
||||
}
|
||||
|
||||
enable(enable) {
|
||||
this.on = enable;
|
||||
|
||||
this._triggerFilteringCallback();
|
||||
}
|
||||
}
|
||||
@@ -75,6 +75,7 @@ export class ServiceQueryBox extends Box {
|
||||
Utils.loadFromUrls([url, Utils.handleCORSNotSameOrigin(url)], {timeout: 30000, dataType: 'blob'})
|
||||
.then((blob) => {
|
||||
const url = URL.createObjectURL(blob);
|
||||
|
||||
try {
|
||||
let image = self.aladin.createImageFITS(url, name);
|
||||
self.aladin.setOverlayImageLayer(image, Utils.uuidv4())
|
||||
@@ -149,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')
|
||||
|
||||
@@ -41,18 +41,18 @@ import addIconUrl from "../../../../assets/icons/plus.svg";
|
||||
import hideIconUrl from "../../../../assets/icons/hide.svg";
|
||||
import removeIconUrl from "../../../../assets/icons/remove.svg";
|
||||
import settingsIconUrl from "../../../../assets/icons/settings.svg";
|
||||
import filterOnUrl from "../../../../assets/icons/filter-on.svg";
|
||||
import filterOffUrl from "../../../../assets/icons/filter-off.svg";
|
||||
|
||||
import searchIconImg from "../../../../assets/icons/search.svg";
|
||||
|
||||
import { TogglerActionButton } from "../Button/Toggler.js";
|
||||
import { Icon } from "../Widgets/Icon.js";
|
||||
import { ImageHiPS } from "../../ImageHiPS.js";
|
||||
import { Box } from "../Widgets/Box.js";
|
||||
import { CtxMenuActionButtonOpener } from "../Button/CtxMenuOpener.js";
|
||||
import { Input } from "../Widgets/Input.js";
|
||||
import { ImageFITS } from "../../ImageFITS.js";
|
||||
import { HiPSCache } from "../../DefaultHiPSCache.js";
|
||||
import { HiPSBrowserBox } from "./HiPSBrowserBox.js";
|
||||
import { ImageHiPS } from "../../ImageHiPS.js";
|
||||
|
||||
export class OverlayStackBox extends Box {
|
||||
/*static previewImagesUrl = {
|
||||
@@ -138,24 +138,6 @@ export class OverlayStackBox extends Box {
|
||||
|
||||
this.aladin = aladin;
|
||||
|
||||
this.filterHiPSOn = false;
|
||||
this.filterCallback = (HiPS) => {
|
||||
if (!HiPS.regime) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (
|
||||
this.filterParams.regime &&
|
||||
HiPS.regime &&
|
||||
this.filterParams.regime.toLowerCase() !==
|
||||
HiPS.regime.toLowerCase()
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
this.mode = "stack";
|
||||
|
||||
this._addListeners();
|
||||
@@ -526,10 +508,10 @@ export class OverlayStackBox extends Box {
|
||||
{
|
||||
label: {
|
||||
icon: {
|
||||
url: searchIconUrl,
|
||||
url: addIconUrl,
|
||||
monochrome: true,
|
||||
tooltip: {
|
||||
content: "From our database...",
|
||||
content: "Add a new layer",
|
||||
position: { direction: "right" },
|
||||
},
|
||||
cssStyle: {
|
||||
@@ -544,23 +526,50 @@ export class OverlayStackBox extends Box {
|
||||
|
||||
/*self._hide();
|
||||
|
||||
self.hipsSelectorBox = new HiPSSelectorBox(self.aladin);
|
||||
// attach a callback
|
||||
self.hipsSelectorBox.attach(
|
||||
(HiPSId) => {
|
||||
let name = Utils.uuidv4()
|
||||
self.aladin.setOverlayImageLayer(HiPSId, name)
|
||||
self.hipsSelectorBox = new HiPSSelectorBox(self.aladin);
|
||||
// attach a callback
|
||||
self.hipsSelectorBox.attach(
|
||||
(HiPSId) => {
|
||||
let name = Utils.uuidv4()
|
||||
self.aladin.setOverlayImageLayer(HiPSId, name)
|
||||
|
||||
self.show();
|
||||
}
|
||||
);
|
||||
self.show();
|
||||
}
|
||||
);
|
||||
|
||||
self.hipsSelectorBox._show({
|
||||
position: self.position,
|
||||
});*/
|
||||
self.hipsSelectorBox._show({
|
||||
position: self.position,
|
||||
});*/
|
||||
self.aladin.addNewImageLayer();
|
||||
},
|
||||
},
|
||||
{
|
||||
label: {
|
||||
icon: {
|
||||
url: searchIconUrl,
|
||||
monochrome: true,
|
||||
tooltip: {
|
||||
content: "From our database...",
|
||||
position: { direction: "right" },
|
||||
},
|
||||
cssStyle: {
|
||||
cursor: "help",
|
||||
},
|
||||
},
|
||||
content: "Browse HiPS",
|
||||
},
|
||||
action: (e) => {
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
|
||||
if (!self.hipsBrowser)
|
||||
self.hipsBrowser = new HiPSBrowserBox(aladin);
|
||||
|
||||
self.hipsBrowser._show({position: {
|
||||
anchor: 'center center'
|
||||
}});
|
||||
},
|
||||
},
|
||||
ContextMenu.fileLoaderItem({
|
||||
label: "FITS image file",
|
||||
accept: ".fits",
|
||||
@@ -595,97 +604,6 @@ export class OverlayStackBox extends Box {
|
||||
this.aladin
|
||||
);
|
||||
|
||||
this.filterParams = {
|
||||
regime: "Optical",
|
||||
};
|
||||
this.filterBox = new Box(
|
||||
{
|
||||
close: false,
|
||||
content: [
|
||||
Input.select({
|
||||
name: "regime",
|
||||
value: "Optical",
|
||||
options: [
|
||||
"Optical",
|
||||
"UV",
|
||||
"Radio",
|
||||
"Infrared",
|
||||
"X-ray",
|
||||
"Gamma-ray",
|
||||
],
|
||||
change() {
|
||||
self.filterParams["regime"] = this.value;
|
||||
|
||||
if (self.filterHiPSOn) {
|
||||
ALEvent.HIPS_LIST_UPDATED.dispatchedTo(
|
||||
self.aladin.aladinDiv
|
||||
);
|
||||
}
|
||||
},
|
||||
tooltip: {
|
||||
content: "Observation regime",
|
||||
position: { direction: "right" },
|
||||
},
|
||||
}),
|
||||
],
|
||||
},
|
||||
self.aladin.aladinDiv
|
||||
);
|
||||
this.filterBox._hide();
|
||||
|
||||
this.filterEnabler = Input.checkbox({
|
||||
name: "filter-enabler",
|
||||
checked: this.filterHiPSOn,
|
||||
tooltip: {
|
||||
content: self.filterHiPSOn ? "Filtering on" : "Filtering off",
|
||||
},
|
||||
click(e) {
|
||||
self.filterHiPSOn = e.target.checked;
|
||||
self.filterBtn.update({
|
||||
icon: {
|
||||
url: self.filterHiPSOn ? filterOnUrl : filterOffUrl,
|
||||
monochrome: true,
|
||||
},
|
||||
});
|
||||
|
||||
self.filterEnabler.update({
|
||||
tooltip: {
|
||||
content: self.filterHiPSOn
|
||||
? "Filtering on"
|
||||
: "Filtering off",
|
||||
},
|
||||
checked: self.filterHiPSOn,
|
||||
});
|
||||
|
||||
ALEvent.HIPS_LIST_UPDATED.dispatchedTo(self.aladin.aladinDiv);
|
||||
},
|
||||
});
|
||||
|
||||
this.filterBtn = new TogglerActionButton({
|
||||
icon: {
|
||||
url: this.filterHiPSOn ? filterOnUrl : filterOffUrl,
|
||||
monochrome: true,
|
||||
},
|
||||
size: "small",
|
||||
tooltip: {
|
||||
content: "Want to filter HiPS surveys by criteria ?",
|
||||
position: { direction: "top" },
|
||||
},
|
||||
toggled: false,
|
||||
actionOn: (e) => {
|
||||
self.filterBox._show({
|
||||
position: {
|
||||
nextTo: self.filterBtn,
|
||||
direction: "right",
|
||||
aladin: self.aladin,
|
||||
},
|
||||
});
|
||||
},
|
||||
actionOff: (e) => {
|
||||
self.filterBox._hide();
|
||||
},
|
||||
});
|
||||
|
||||
this.update({ content: this.createLayout() });
|
||||
}
|
||||
|
||||
@@ -784,14 +702,15 @@ export class OverlayStackBox extends Box {
|
||||
updateOverlayList();
|
||||
|
||||
// Add a listener for HiPS list changes
|
||||
ALEvent.HIPS_LIST_UPDATED.listenedBy(document.body, () => {
|
||||
ALEvent.HIPS_CACHE_UPDATED.listenedBy(document.body, () => {
|
||||
self.cachedHiPS = {};
|
||||
|
||||
for (var key in HiPSCache.cache) {
|
||||
|
||||
let HiPS = HiPSCache.cache[key];
|
||||
|
||||
self.cachedHiPS[HiPS.name] = HiPS;
|
||||
if (HiPS.name) {
|
||||
self.cachedHiPS[HiPS.name.toString()] = HiPS;
|
||||
}
|
||||
}
|
||||
|
||||
// Update the options of the selector
|
||||
@@ -800,36 +719,9 @@ export class OverlayStackBox extends Box {
|
||||
|
||||
for (var key in self.HiPSui) {
|
||||
let hips = self.HiPSui[key];
|
||||
hips.HiPSSelector.update({options});
|
||||
hips.HiPSSelector.update({value: hips.HiPSSelector.options.value, options});
|
||||
}
|
||||
});
|
||||
|
||||
/*ALEvent.HIPS_LIST_UPDATED.listenedBy(this.aladin.aladinDiv, () => {
|
||||
// Recompute the autocompletion as the cache has changed
|
||||
HiPSSearch.HiPSList = {};
|
||||
for (var key in ImageHiPS.cache) {
|
||||
let HiPS = ImageHiPS.cache[key];
|
||||
|
||||
// apply filtering
|
||||
if (
|
||||
!self.filterHiPSOn ||
|
||||
(self.filterHiPSOn && !self.filterCallback) ||
|
||||
(self.filterHiPSOn &&
|
||||
self.filterCallback &&
|
||||
self.filterCallback(HiPS))
|
||||
) {
|
||||
// search with the name or id
|
||||
HiPSSearch.HiPSList[HiPS.name] = HiPS;
|
||||
}
|
||||
}
|
||||
|
||||
let keys = Object.keys(HiPSSearch.HiPSList);
|
||||
// Change the autocomplete of all the search input text
|
||||
for (var key in this.HiPSui) {
|
||||
let hips = this.HiPSui[key];
|
||||
hips.searchInput.setAutocompletionList(keys);
|
||||
}
|
||||
});*/
|
||||
}
|
||||
|
||||
_hide() {
|
||||
@@ -841,14 +733,14 @@ export class OverlayStackBox extends Box {
|
||||
}
|
||||
}
|
||||
|
||||
/*if (this.hipsBrowser) {
|
||||
this.hipsBrowser._hide();
|
||||
}*/
|
||||
|
||||
if (this.catBox) {
|
||||
this.catBox._hide();
|
||||
}
|
||||
|
||||
if (this.filterBtn && this.filterBtn.toggled) {
|
||||
this.filterBtn.toggle();
|
||||
}
|
||||
|
||||
if (this.addOverlayBtn) this.addOverlayBtn.hideMenu();
|
||||
|
||||
if (this.addHiPSBtn) this.addHiPSBtn.hideMenu();
|
||||
@@ -875,7 +767,7 @@ export class OverlayStackBox extends Box {
|
||||
);
|
||||
layout = layout.concat(this._createSurveysList());
|
||||
|
||||
return new Layout({ layout, classList: ["content"] });
|
||||
return Layout.vertical({ layout, classList: ["content"] });
|
||||
}
|
||||
|
||||
_createOverlaysList() {
|
||||
@@ -984,7 +876,7 @@ export class OverlayStackBox extends Box {
|
||||
_createSurveysList() {
|
||||
let self = this;
|
||||
|
||||
const layers = Array.from(self.aladin.getImageOverlays())
|
||||
const layers = Array.from(self.aladin.getStackLayers())
|
||||
.reverse()
|
||||
.map((name) => {
|
||||
let overlay = self.aladin.getOverlayImageLayer(name);
|
||||
@@ -1103,8 +995,8 @@ export class OverlayStackBox extends Box {
|
||||
);
|
||||
return found !== undefined;
|
||||
})(),
|
||||
action: (e, btn) => {
|
||||
if (!btn.options.toggled) {
|
||||
action: (e) => {
|
||||
if (!loadMOCBtn.options.toggled) {
|
||||
// load the moc
|
||||
let moc = A.MOCFromURL(
|
||||
layer.url + "/Moc.fits",
|
||||
@@ -1123,7 +1015,7 @@ export class OverlayStackBox extends Box {
|
||||
});
|
||||
}
|
||||
|
||||
btn.update({
|
||||
loadMOCBtn.update({
|
||||
toggled: true,
|
||||
tooltip: {
|
||||
content: "Remove coverage",
|
||||
@@ -1149,7 +1041,7 @@ export class OverlayStackBox extends Box {
|
||||
});
|
||||
}
|
||||
|
||||
btn.update({
|
||||
loadMOCBtn.update({
|
||||
toggled: false,
|
||||
tooltip: {
|
||||
content: "Add coverage",
|
||||
@@ -1162,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);
|
||||
|
||||
107
src/js/gui/Input/Dropdown.js
Normal file
107
src/js/gui/Input/Dropdown.js
Normal file
@@ -0,0 +1,107 @@
|
||||
// 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";
|
||||
|
||||
export class Dropdown extends Input {
|
||||
|
||||
// constructor
|
||||
constructor(aladin, options) {
|
||||
let self;
|
||||
aladin.view.catalogCanvas.addEventListener('click', (e) => {
|
||||
self.el.blur();
|
||||
});
|
||||
|
||||
options.options = options.options || [];
|
||||
|
||||
super({
|
||||
type: 'text',
|
||||
autocomplete: {options: options.options},
|
||||
...options
|
||||
})
|
||||
this.el.classList.add('search')
|
||||
|
||||
self = this;
|
||||
this._addListeners(aladin);
|
||||
}
|
||||
|
||||
update(options) {
|
||||
let newOptions = {};
|
||||
|
||||
if (options && options.options) {
|
||||
newOptions['autocomplete'] = {options: options.options};
|
||||
|
||||
delete options.options;
|
||||
}
|
||||
|
||||
// add the other input text options
|
||||
newOptions = {...newOptions, ...options};
|
||||
super.update(newOptions)
|
||||
}
|
||||
|
||||
_addListeners(aladin) {
|
||||
}
|
||||
};
|
||||
@@ -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})
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -76,10 +76,11 @@ export class Location extends DOMElement {
|
||||
keydown: (e) => {
|
||||
e.stopPropagation();
|
||||
|
||||
field.removeClass('aladin-unknownObject'); // remove red border
|
||||
field.removeClass('aladin-not-valid'); // remove red border
|
||||
field.removeClass('aladin-valid'); // remove red border
|
||||
|
||||
if (e.key === 'Enter') {
|
||||
field.el.blur();
|
||||
//field.el.blur();
|
||||
|
||||
let object = field.get();
|
||||
|
||||
@@ -90,12 +91,14 @@ export class Location extends DOMElement {
|
||||
object,
|
||||
{
|
||||
error: function () {
|
||||
field.addClass('aladin-unknownObject');
|
||||
field.addClass('aladin-not-valid');
|
||||
field.update({placeholder: object + ' not found...'})
|
||||
field.set('');
|
||||
field.el.focus();
|
||||
},
|
||||
success: function() {
|
||||
field.addClass('aladin-valid');
|
||||
|
||||
field.update({placeholder:'Search for an object...', value: object});
|
||||
}
|
||||
}
|
||||
@@ -154,7 +157,7 @@ export class Location extends DOMElement {
|
||||
}
|
||||
|
||||
let [lon, lat] = lonlat;
|
||||
|
||||
self.field.el.blur()
|
||||
self.update({
|
||||
lon, lat,
|
||||
frame: aladin.view.cooFrame,
|
||||
@@ -162,6 +165,10 @@ export class Location extends DOMElement {
|
||||
}, aladin);
|
||||
}
|
||||
|
||||
if(param.state.dragging) {
|
||||
self.field.el.blur()
|
||||
}
|
||||
|
||||
if (param.type === 'mousemove' && param.state.dragging === false) {
|
||||
if (focused) {
|
||||
return;
|
||||
@@ -177,6 +184,7 @@ export class Location extends DOMElement {
|
||||
});
|
||||
|
||||
ALEvent.POSITION_CHANGED.listenedBy(aladin.aladinDiv, function (e) {
|
||||
|
||||
self.update({
|
||||
lon: e.detail.lon,
|
||||
lat: e.detail.lat,
|
||||
@@ -220,7 +228,8 @@ export class Location extends DOMElement {
|
||||
else {
|
||||
self.field.set(coo.format('d/'))
|
||||
}
|
||||
self.field.removeClass('aladin-unknownObject');
|
||||
self.field.removeClass('aladin-not-valid');
|
||||
self.field.removeClass('aladin-valid');
|
||||
|
||||
self.field.element().style.color = options.isViewCenter ? aladin.getReticle().getColor() : 'white';
|
||||
//self.field.el.blur()
|
||||
|
||||
@@ -167,6 +167,10 @@ export class ActionButton extends DOMElement {
|
||||
super._show();
|
||||
}
|
||||
|
||||
click() {
|
||||
this.el.click()
|
||||
}
|
||||
|
||||
static createIconBtn(opt, target, position = 'beforeend') {
|
||||
let btn = new ActionButton({...opt, size: 'medium'}, target, position);
|
||||
|
||||
|
||||
@@ -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) {
|
||||
@@ -67,7 +66,8 @@ export class Box extends DOMElement {
|
||||
|
||||
let self = this;
|
||||
|
||||
let close = this.options.close === false ? false : true;
|
||||
let close = this.options.close === false ? false : true;
|
||||
let draggable = false;
|
||||
if (close) {
|
||||
new ActionButton({
|
||||
size: 'small',
|
||||
@@ -86,6 +86,10 @@ export class Box extends DOMElement {
|
||||
}, this.el);
|
||||
}
|
||||
|
||||
if (this.options.onDragged) {
|
||||
draggable = true;
|
||||
}
|
||||
|
||||
// Check for the title
|
||||
if (this.options.header) {
|
||||
let header = this.options.header;
|
||||
@@ -99,6 +103,10 @@ export class Box extends DOMElement {
|
||||
|
||||
let draggableEl;
|
||||
if (header.draggable) {
|
||||
draggable = true;
|
||||
}
|
||||
|
||||
if (draggable) {
|
||||
draggableEl = new ActionButton({
|
||||
icon: {
|
||||
url: moveIconImg,
|
||||
@@ -111,13 +119,13 @@ export class Box extends DOMElement {
|
||||
},
|
||||
action(e) {}
|
||||
});
|
||||
|
||||
dragElement(draggableEl.element(), this.el)
|
||||
dragElement(titleEl, this.el)
|
||||
titleEl.style.cursor = 'move'
|
||||
}
|
||||
|
||||
Layout.horizontal([draggableEl, titleEl], this.el);
|
||||
let headerEl = Layout.horizontal([draggableEl, titleEl], this.el);
|
||||
if (draggable) {
|
||||
dragElement(headerEl.element(), this.el, this.options.onDragged);
|
||||
headerEl.element().style.cursor = 'move';
|
||||
}
|
||||
|
||||
let separatorEl = document.createElement('div')
|
||||
separatorEl.classList.add("aladin-box-separator");
|
||||
@@ -130,12 +138,11 @@ export class Box extends DOMElement {
|
||||
|
||||
if (this.options.content) {
|
||||
let content = this.options.content
|
||||
|
||||
if (Array.isArray(content)) {
|
||||
this.appendContent(new Layout({layout: content}));
|
||||
} else {
|
||||
//if (Array.isArray(content)) {
|
||||
this.appendContent(content);
|
||||
}
|
||||
//} else {
|
||||
// this.appendContent(content);
|
||||
//}
|
||||
}
|
||||
|
||||
if (this.options.position) {
|
||||
@@ -151,10 +158,10 @@ export class Box extends DOMElement {
|
||||
}
|
||||
|
||||
// Heavily inspired from https://www.w3schools.com/howto/howto_js_draggable.asp
|
||||
function dragElement(triggerElt, elmnt) {
|
||||
function dragElement(triggerElt, elmnt, onDragged) {
|
||||
var pos1 = 0, pos2 = 0, pos3 = 0, pos4 = 0;
|
||||
// otherwise, move the DIV from anywhere inside the DIV:
|
||||
|
||||
var t, l;
|
||||
triggerElt.onmousedown = dragMouseDown;
|
||||
|
||||
function dragMouseDown(e) {
|
||||
@@ -166,6 +173,10 @@ function dragElement(triggerElt, elmnt) {
|
||||
document.onmouseup = closeDragElement;
|
||||
// call a function whenever the cursor moves:
|
||||
document.onmousemove = elementDrag;
|
||||
|
||||
if (onDragged) {
|
||||
onDragged();
|
||||
}
|
||||
}
|
||||
|
||||
function elementDrag(e) {
|
||||
@@ -176,14 +187,37 @@ function dragElement(triggerElt, elmnt) {
|
||||
pos2 = pos4 - e.clientY;
|
||||
pos3 = e.clientX;
|
||||
pos4 = e.clientY;
|
||||
|
||||
// set the element's new position:
|
||||
elmnt.style.top = (elmnt.offsetTop - pos2) + "px";
|
||||
elmnt.style.left = (elmnt.offsetLeft - pos1) + "px";
|
||||
t = elmnt.offsetTop - pos2
|
||||
l = elmnt.offsetLeft - pos1
|
||||
elmnt.style.top = t + "px";
|
||||
elmnt.style.left = l + "px";
|
||||
}
|
||||
|
||||
function closeDragElement() {
|
||||
// stop moving when mouse button is released:
|
||||
document.onmouseup = null;
|
||||
document.onmousemove = null;
|
||||
|
||||
var r = elmnt.getBoundingClientRect();
|
||||
|
||||
if (t < r.height / 2) {
|
||||
elmnt.style.top = r.height / 2 + "px";
|
||||
}
|
||||
|
||||
if (l < r.width / 2) {
|
||||
elmnt.style.left = r.width / 2 + "px";
|
||||
}
|
||||
|
||||
const aladinDiv = elmnt.closest('.aladin-container');
|
||||
|
||||
if (l + r.width / 2 > aladinDiv.offsetWidth) {
|
||||
elmnt.style.left = (aladinDiv.offsetWidth - r.width / 2) + "px";
|
||||
}
|
||||
|
||||
if (t + r.height / 2 > aladinDiv.offsetHeight) {
|
||||
elmnt.style.top = (aladinDiv.offsetHeight - r.height / 2) + "px";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -53,9 +53,13 @@ Exemple of layout object
|
||||
export class Form extends DOMElement {
|
||||
constructor(options, target, position = "beforeend") {
|
||||
let el = document.createElement('form');
|
||||
el.onsubmit = (e) => {
|
||||
e.preventDefault();
|
||||
};
|
||||
el.className = "aladin-form";
|
||||
|
||||
super(el, options);
|
||||
|
||||
this.attachTo(target, position)
|
||||
|
||||
this._show()
|
||||
@@ -67,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))
|
||||
});
|
||||
}
|
||||
|
||||
@@ -87,21 +91,27 @@ export class Form extends DOMElement {
|
||||
);
|
||||
}
|
||||
|
||||
this.appendContent(new Layout(layout))
|
||||
this.appendContent(Layout.vertical(layout))
|
||||
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]);
|
||||
@@ -115,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
|
||||
|
||||
@@ -81,6 +81,7 @@ export class Table extends DOMElement {
|
||||
let val = row.data[key] || '--';
|
||||
tdEl.innerHTML = val;
|
||||
tdEl.classList.add("aladin-text-td-container");
|
||||
tdEl.title = val;
|
||||
}
|
||||
|
||||
trEl.appendChild(tdEl);
|
||||
|
||||
@@ -2,14 +2,21 @@
|
||||
// Class Coo
|
||||
//=================================
|
||||
import { Format } from "./coo";
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
* @param angle angle (precision in degrees)
|
||||
* @param prec precision
|
||||
* Creates an angle of the Aladin interactive sky atlas.
|
||||
* @class
|
||||
* @constructs Angle
|
||||
* @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;
|
||||
};
|
||||
|
||||
@@ -43,28 +50,66 @@ Angle.prototype = {
|
||||
return Format.toDecimal(fov, this.prec) + suffix;
|
||||
},
|
||||
|
||||
/**
|
||||
* @memberof Angle
|
||||
*
|
||||
* @param {string} str - A string in the form [<deg>°<minutes>'<seconds>"]. [hms] form is not supported
|
||||
* @returns {boolean} - Whether the string has been successfully parsed
|
||||
*/
|
||||
|
||||
parse: function(str) {
|
||||
// check for degrees
|
||||
let idxUnit;
|
||||
idxUnit = str.indexOf('°');
|
||||
if (idxUnit > 0) {
|
||||
this.angle = +str.substring(0, idxUnit)
|
||||
return true;
|
||||
let idx = str.indexOf('°');
|
||||
|
||||
let angleDeg = NaN;
|
||||
if (idx > 0) {
|
||||
const deg = parseFloat(str.substring(0, idx));
|
||||
if (!Number.isFinite(deg)) {
|
||||
return false
|
||||
}
|
||||
|
||||
angleDeg = deg;
|
||||
|
||||
str = str.substring(idx + 1)
|
||||
}
|
||||
|
||||
idxUnit = str.indexOf('\'');
|
||||
if (idxUnit > 0) {
|
||||
this.angle = (+str.substring(0, idxUnit)) / 60.0
|
||||
return true;
|
||||
idx = str.indexOf('\'');
|
||||
if (idx > 0) {
|
||||
const minutes = parseFloat(str.substring(0, idx))
|
||||
|
||||
if (!Number.isFinite(minutes)) {
|
||||
return false
|
||||
}
|
||||
|
||||
if (!Number.isFinite(angleDeg)) {
|
||||
angleDeg = 0;
|
||||
}
|
||||
angleDeg += minutes / 60.0
|
||||
|
||||
str = str.substring(idx + 1);
|
||||
}
|
||||
|
||||
idxUnit = str.indexOf('"');
|
||||
if (idxUnit > 0) {
|
||||
this.angle = (+str.substring(0, idxUnit)) / 3600.0
|
||||
return true;
|
||||
idx = str.indexOf('"');
|
||||
if (idx > 0) {
|
||||
const seconds = parseFloat(str.substring(0, idx))
|
||||
|
||||
if (!Number.isFinite(seconds)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!Number.isFinite(angleDeg)) {
|
||||
angleDeg = 0;
|
||||
}
|
||||
|
||||
angleDeg += seconds / 3600.0
|
||||
}
|
||||
|
||||
return false
|
||||
if (Number.isFinite(angleDeg)) {
|
||||
this.angle = angleDeg;
|
||||
return true;
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
},
|
||||
|
||||
degrees: function() {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
});
|
||||
|
||||
@@ -191,7 +191,7 @@
|
||||
let accessUrlEl = document.createElement('div');
|
||||
try {
|
||||
let _ = new URL(url);
|
||||
accessUrlEl.classList.add('aladin-href-td-container');
|
||||
accessUrlEl.classList.add('aladin-href-link');
|
||||
|
||||
accessUrlEl.innerHTML = '<a href=' + url + ' target="_blank">' + url + '</a>';
|
||||
|
||||
@@ -245,7 +245,7 @@
|
||||
return new ActionButton({
|
||||
size: 'small',
|
||||
content: '🔗',
|
||||
tooltip: {content: accessFormat, position: {direction: 'left'}},
|
||||
tooltip: {content: 'Datalink VOTable', aladin: aladinInstance, global: true},
|
||||
action(e) {}
|
||||
}).element();
|
||||
} else {
|
||||
|
||||
@@ -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