mirror of
https://github.com/cds-astro/aladin-lite.git
synced 2026-01-11 12:46:05 -08:00
Compare commits
4 Commits
ui-discove
...
composite
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c69aa990b6 | ||
|
|
42209b166e | ||
|
|
2ef5e47c27 | ||
|
|
337618d6ef |
@@ -6,6 +6,13 @@
|
||||
|
||||
## Released
|
||||
|
||||
### 3.8.0
|
||||
|
||||
* [fix] horizontal/vertical overlay lines appearing correctly <https://github.com/cds-astro/aladin-lite/issues/334>
|
||||
* [fix] layer opacity restored when switching from not visible to visible <https://github.com/cds-astro/aladin-lite/issues/332>
|
||||
* [feat] dark/light mode for the interface
|
||||
* [fix] polylines shapes size not consistent w.r.t to div size <https://github.com/cds-astro/aladin-lite/issues/331>
|
||||
|
||||
### 3.7.0-beta
|
||||
|
||||
#### What's Changed
|
||||
|
||||
6
assets/icons/tree.svg
Normal file
6
assets/icons/tree.svg
Normal file
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?><!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
|
||||
<svg fill="#000000" width="800px" height="800px" viewBox="0 0 36 36" version="1.1" preserveAspectRatio="xMidYMid meet" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<title>tree-view-line</title>
|
||||
<path d="M15,32H11a1,1,0,0,1-1-1V27a1,1,0,0,1,1-1h4a1,1,0,0,1,1,1v4A1,1,0,0,1,15,32Zm-3-2h2V28H12Z" class="clr-i-outline clr-i-outline-path-1"></path><path d="M15,16H11a1,1,0,0,0-1,1v1.2H5.8V12H7a1,1,0,0,0,1-1V7A1,1,0,0,0,7,6H3A1,1,0,0,0,2,7v4a1,1,0,0,0,1,1H4.2V29.8h6.36a.8.8,0,0,0,0-1.6H5.8V19.8H10V21a1,1,0,0,0,1,1h4a1,1,0,0,0,1-1V17A1,1,0,0,0,15,16ZM4,8H6v2H4ZM14,20H12V18h2Z" class="clr-i-outline clr-i-outline-path-2"></path><path d="M34,9a1,1,0,0,0-1-1H10v2H33A1,1,0,0,0,34,9Z" class="clr-i-outline clr-i-outline-path-3"></path><path d="M33,18H18v2H33a1,1,0,0,0,0-2Z" class="clr-i-outline clr-i-outline-path-4"></path><path d="M33,28H18v2H33a1,1,0,0,0,0-2Z" class="clr-i-outline clr-i-outline-path-5"></path>
|
||||
<rect x="0" y="0" width="36" height="36" fill-opacity="0"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.1 KiB |
@@ -910,7 +910,6 @@ impl App {
|
||||
hips.add_allsky(allsky)?;
|
||||
// Once received ask for redraw
|
||||
self.request_redraw = true;
|
||||
al_core::log("request redraw");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1072,7 +1071,6 @@ impl App {
|
||||
gl.clear(WebGl2RenderingContext::COLOR_BUFFER_BIT);
|
||||
|
||||
// set the blending options
|
||||
al_core::log("draw");
|
||||
layers.draw(camera, shaders, colormaps, projection)?;
|
||||
|
||||
// Draw the catalog
|
||||
|
||||
@@ -183,8 +183,6 @@ impl From<query::Allsky> for AllskyRequest {
|
||||
.collect())
|
||||
}
|
||||
Bitpix::F32 => {
|
||||
al_core::log("allsky fits parsed");
|
||||
|
||||
Ok(handle_allsky_fits(raw_bytes, tile_size, allsky_tile_size)?
|
||||
.map(|image| ImageType::RawRgba8u { image })
|
||||
.collect())
|
||||
@@ -211,7 +209,6 @@ impl From<query::Allsky> for AllskyRequest {
|
||||
}
|
||||
}
|
||||
});
|
||||
al_core::log("allsky parsed");
|
||||
|
||||
Self {
|
||||
id,
|
||||
|
||||
@@ -99,10 +99,6 @@ impl FreqSpaceMoc {
|
||||
let f_hash_0 = f_hash << (Frequency::<u64>::MAX_DEPTH - f_depth);
|
||||
let f_hash_1 = (f_hash + 1) << (Frequency::<u64>::MAX_DEPTH - f_depth);
|
||||
|
||||
//let f0 = Frequency::<u64>::hash2freq(5171582628058365952);
|
||||
//let f1 = Frequency::<u64>::hash2freq(5171590187200806912);
|
||||
//al_core::log(&format!("F1: {f0}"));
|
||||
|
||||
let hpx_ranges_2d = HpxRanges2D::create_from_freq_ranges_positions(
|
||||
vec![f_hash_0..f_hash_1; 1],
|
||||
vec![hpx.idx()],
|
||||
|
||||
@@ -206,10 +206,7 @@ impl HiPS2DBuffer {
|
||||
}
|
||||
|
||||
let is_allsky_tile = tile_size == self.config.allsky_tile_size() as u32;
|
||||
al_core::log("cell is root");
|
||||
if is_allsky_tile {
|
||||
al_core::log("copied to gpu");
|
||||
|
||||
texture.copy_to_gpu(
|
||||
cell, // The tile cell
|
||||
&image,
|
||||
@@ -365,7 +362,6 @@ impl HpxTileBuffer for HiPS2DBuffer {
|
||||
];
|
||||
|
||||
let channel = config.get_format().get_pixel_format();
|
||||
al_core::log(&format!("channel: {:?}", channel));
|
||||
let tile_size = config.get_tile_size();
|
||||
let tile_pixels = create_hpx_texture_storage(gl, channel, 128, tile_size)?;
|
||||
|
||||
@@ -398,7 +394,6 @@ impl HpxTileBuffer for HiPS2DBuffer {
|
||||
self.config.set_image_ext(ext)?;
|
||||
|
||||
let channel = self.config.get_format().get_pixel_format();
|
||||
al_core::log(&format!("set channel: {:?}", channel));
|
||||
|
||||
let tile_size = self.config.get_tile_size();
|
||||
self.tile_pixels = create_hpx_texture_storage(gl, channel, 128, tile_size)?;
|
||||
@@ -482,7 +477,6 @@ impl SendUniforms for HiPS2DBuffer {
|
||||
let shader = shader.attach_uniforms_from(&self.config);
|
||||
|
||||
if self.allsky_rendering {
|
||||
al_core::log("allsky is rendering");
|
||||
for idx in 0..NUM_HPX_TILES_DEPTH_ZERO {
|
||||
let cell = HEALPixCell(0, idx as u64);
|
||||
|
||||
|
||||
10312
src/core/src/shaders.rs
10312
src/core/src/shaders.rs
File diff suppressed because it is too large
Load Diff
@@ -37,14 +37,13 @@
|
||||
--hover-color: green;
|
||||
--toggle-color: dodgerblue;
|
||||
--border-size: 2px;
|
||||
--valid-color: greenyellow;
|
||||
--valid-color: green;
|
||||
--error-color: red;
|
||||
}
|
||||
|
||||
.aladin-tree {
|
||||
width: 100%;
|
||||
border-top: 1px solid var(--border-color);
|
||||
border-bottom: 2px solid var(--border-color);
|
||||
border-bottom: var(--border-size) solid var(--border-color);
|
||||
}
|
||||
|
||||
.aladin-tree .aladin-directory-path {
|
||||
@@ -62,7 +61,6 @@
|
||||
}
|
||||
.aladin-link {
|
||||
list-style-type: none;
|
||||
padding: 0.5em 0px;
|
||||
cursor:pointer;
|
||||
margin: 0;
|
||||
}
|
||||
@@ -75,8 +73,48 @@
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.aladin-tree li {
|
||||
border-bottom: 1px solid var(--border-color);
|
||||
.aladin-tree li > * {
|
||||
border-bottom: var(--border-size) solid var(--border-color);
|
||||
padding: 0.5em 0px;
|
||||
}
|
||||
|
||||
.aladin-tree li:last-of-type > * {
|
||||
border-bottom: 0px;
|
||||
}
|
||||
|
||||
#aladin-tooltip-mouse {
|
||||
display: none;
|
||||
position: fixed;
|
||||
pointer-events: none; /* tooltip won't block mouse */
|
||||
z-index: 1000;
|
||||
padding: 0;
|
||||
max-width: 100%;
|
||||
}
|
||||
#aladin-tooltip-mouse * {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.aladin-fig {
|
||||
display: inline-block; /* shrink-wraps content */
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.aladin-fig img {
|
||||
display: block;
|
||||
max-width: 128px;
|
||||
max-height: 128px;
|
||||
}
|
||||
|
||||
.aladin-fig figcaption {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
|
||||
color: white;
|
||||
font-size: 11px;
|
||||
text-align: center;
|
||||
background: black;
|
||||
}
|
||||
|
||||
.aladin-lite-spectra-displayer .aladin-spectra-unit-selector {
|
||||
@@ -286,7 +324,7 @@
|
||||
}
|
||||
|
||||
.aladin-box {
|
||||
padding: 0.2rem;
|
||||
padding: 0.3rem;
|
||||
background: whitesmoke;
|
||||
position: absolute;
|
||||
font-size: inherit;
|
||||
@@ -353,7 +391,7 @@
|
||||
margin-top: 0 0 2px 0;
|
||||
cursor:pointer;
|
||||
color: #605F61;
|
||||
border: var(--border-size) solid #AEAEAE;
|
||||
border: var(--border-size) solid var(--border-color);
|
||||
border-radius: 3px;
|
||||
background: #fff;
|
||||
font-size: 1.0rem;
|
||||
@@ -368,8 +406,10 @@
|
||||
|
||||
.aladin-box-separator {
|
||||
height: 0;
|
||||
border-top: 1px solid #d2d2d2;
|
||||
padding-bottom: 5px;
|
||||
border-top: var(--border-size) solid var(--border-color);
|
||||
margin-top: 5px;
|
||||
margin-bottom: 5px;
|
||||
|
||||
}
|
||||
|
||||
.aladin-restore {
|
||||
@@ -493,6 +533,7 @@
|
||||
|
||||
.aladin-input-text:focus, .aladin-input-number:focus {
|
||||
border-color: var(--toggle-color);
|
||||
background-color: var(--bg-color);
|
||||
}
|
||||
|
||||
[data-theme="light"] .aladin-input-text.search:focus {
|
||||
@@ -529,12 +570,6 @@
|
||||
line-height: 1.2rem;
|
||||
}
|
||||
|
||||
.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-not-valid {
|
||||
border: var(--border-size) solid var(--error-color);
|
||||
}
|
||||
@@ -882,7 +917,7 @@
|
||||
-moz-appearance: none;
|
||||
-webkit-appearance: none;
|
||||
outline-style: none;
|
||||
padding: 0.2rem;
|
||||
padding: 0.5rem;
|
||||
border-radius: 0.2rem;
|
||||
font-family: monospace;
|
||||
box-sizing: border-box;
|
||||
@@ -965,7 +1000,6 @@ otherwise it fits its content options. If those are too big the select can go ou
|
||||
/********** Range Input Styles **********/
|
||||
/*Range Reset*/
|
||||
.aladin-input-range {
|
||||
background: transparent;
|
||||
cursor: pointer;
|
||||
margin: 0.8rem 0;
|
||||
box-sizing: border-box;
|
||||
@@ -981,11 +1015,20 @@ otherwise it fits its content options. If those are too big the select can go ou
|
||||
.aladin-input-range:focus {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.aladin-input-text:-internal-autofill-selected,
|
||||
.aladin-input-text:-internal-autofill-selected:hover,
|
||||
.aladin-input-text:-internal-autofill-selected:focus,
|
||||
.aladin-input-text:-internal-autofill-selected:active {
|
||||
-webkit-box-shadow: 0 0 0 1000px var(--bg-color) inset !important;
|
||||
box-shadow: 0 0 0 1000px var(--bg-color) inset !important;
|
||||
-webkit-text-fill-color: var(--text-color) !important;
|
||||
padding-left: 0.5rem !important;
|
||||
}
|
||||
|
||||
/***** Chrome, Safari, Opera and Edge Chromium styles *****/
|
||||
|
||||
.aladin-input-range::-webkit-slider-container {
|
||||
background: var(--text-color);
|
||||
height: 0.1rem;
|
||||
min-height: 0.1rem;
|
||||
}
|
||||
@@ -999,8 +1042,7 @@ otherwise it fits its content options. If those are too big the select can go ou
|
||||
|
||||
font-family: monospace;
|
||||
line-height: 1rem;
|
||||
|
||||
float: left;
|
||||
|
||||
}
|
||||
|
||||
.aladin-tooltip-container .aladin-tooltip {
|
||||
@@ -1184,14 +1226,12 @@ otherwise it fits its content options. If those are too big the select can go ou
|
||||
.aladin-HiPS-browser-box .aladin-input-text {
|
||||
width: 100%;
|
||||
min-width: 300px;
|
||||
padding: 0.5rem;
|
||||
|
||||
}
|
||||
|
||||
.aladin-cat-browser-box .aladin-input-text.search {
|
||||
width: 100%;
|
||||
min-width: 300px;
|
||||
padding-top: 0.5rem;
|
||||
padding-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.aladin-location {
|
||||
@@ -1213,6 +1253,7 @@ otherwise it fits its content options. If those are too big the select can go ou
|
||||
border-radius: 0 0.2rem 0.2rem 0;
|
||||
box-sizing: content-box;
|
||||
width: 12.5rem;
|
||||
padding: 0.2rem;
|
||||
}
|
||||
|
||||
.aladin-location .aladin-location-copy {
|
||||
|
||||
@@ -358,6 +358,11 @@ export let Aladin = (function () {
|
||||
|
||||
this.reticle = new Reticle(this.options, this);
|
||||
this.popup = new Popup(this.aladinDiv, this.view);
|
||||
this.tooltip = document.createElement('div')
|
||||
this.tooltip.id = 'aladin-tooltip-mouse';
|
||||
this.tooltip.classList.add("aladin-box")
|
||||
|
||||
this.aladinDiv.appendChild(this.tooltip)
|
||||
|
||||
this.ui = [];
|
||||
|
||||
|
||||
@@ -77,7 +77,7 @@ export let MeasurementTable = (function() {
|
||||
tooltip: {
|
||||
global: true,
|
||||
aladin: this.aladin,
|
||||
content: 'Press shift + mouse wheel for scrolling'
|
||||
content: 'Scroll to see more...'
|
||||
},
|
||||
aladin: this.aladin,
|
||||
layout,
|
||||
|
||||
@@ -51,8 +51,7 @@ export class MocServer {
|
||||
//expr: "dataproduct_type=image",
|
||||
get: "record",
|
||||
fmt: "json",
|
||||
fields: "ID,hips_creator,hips_copyright,hips_order,hips_tile_width,hips_frame,hips_tile_format,obs_title,obs_description,obs_copyright,obs_regime,client_category",
|
||||
//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",
|
||||
fields: "ID,hips_creator,hips_copyright,hips_order,hips_tile_width,hips_frame,hips_tile_format,obs_title,obs_description,obs_copyright,obs_regime,client_category,dataproduct_subtype,hips_service_url,hips_initial_ra,hips_initial_dec,hips_initial_fov",
|
||||
};
|
||||
|
||||
this._allHiPSes = Utils.loadFromUrls(MocServer.MIRRORS_HTTPS, {
|
||||
@@ -97,8 +96,7 @@ export class MocServer {
|
||||
expr: "dataproduct_type=image&&ID=" + ids.join(','),
|
||||
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_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",
|
||||
fields: "ID,hips_creator,hips_copyright,hips_frame,hips_tile_format,obs_title,obs_description,obs_copyright,obs_regime,dataproduct_subtype,hips_service_url,hips_initial_ra,hips_initial_dec,hips_initial_fov",
|
||||
};
|
||||
|
||||
return Utils.loadFromUrls(MocServer.MIRRORS_HTTPS, {
|
||||
@@ -114,7 +112,6 @@ export class MocServer {
|
||||
get: "record",
|
||||
fmt: "json",
|
||||
fields: "ID,hips_copyright,obs_title,obs_description,obs_copyright,cs_service_url,hips_service_url",
|
||||
//fields: "ID,hips_copyright,hips_order,hips_order_min,obs_title,obs_description,obs_copyright,obs_regime,cs_service_url,hips_service_url",
|
||||
};
|
||||
|
||||
this._allCatalogHiPSes = Utils.loadFromUrls(MocServer.MIRRORS_HTTPS, {data: params, dataType: 'json'})
|
||||
|
||||
@@ -22,7 +22,7 @@ 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 hipsIconUrl from "../../../../assets/icons/hips.svg";
|
||||
import treeIconUrl from "../../../../assets/icons/tree.svg";
|
||||
import filterOffUrl from "../../../../assets/icons/filter-off.svg";
|
||||
import { Input } from "../Widgets/Input.js";
|
||||
import { TogglerActionButton } from "../Button/Toggler.js";
|
||||
@@ -31,7 +31,6 @@ 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"
|
||||
import { Icon } from "../Widgets/Icon.js";
|
||||
import { Tree } from "../Widgets/Tree.js";
|
||||
import { ALEvent } from "../../events/ALEvent.js";
|
||||
@@ -47,6 +46,10 @@ import { ALEvent } from "../../events/ALEvent.js";
|
||||
*****************************************************************************/
|
||||
|
||||
function fillHiPSHierarchy(name, hips, path, hierarchy) {
|
||||
if (path[path.length - 1] === '/') {
|
||||
path = path.substring(0, path.length - 1);
|
||||
}
|
||||
|
||||
let folders = path.split('/')
|
||||
let curFolder = folders.shift()
|
||||
|
||||
@@ -131,6 +134,7 @@ export class HiPSBrowserBox extends Box {
|
||||
},
|
||||
// a callback called for filtering
|
||||
filter,
|
||||
aladin,
|
||||
});
|
||||
|
||||
MocServer.getAllHiPSes().then((HiPSes) => {
|
||||
@@ -185,6 +189,7 @@ export class HiPSBrowserBox extends Box {
|
||||
}
|
||||
};
|
||||
|
||||
let typedRecently = false;
|
||||
let searchDropdown = new Dropdown(aladin, {
|
||||
name: "HiPS browser",
|
||||
placeholder: "Browse a HiPS by an URL, ID or keywords",
|
||||
@@ -201,20 +206,34 @@ export class HiPSBrowserBox extends Box {
|
||||
keydown(e) {
|
||||
e.stopPropagation();
|
||||
|
||||
// ignore navigation keys
|
||||
if (e.key.length === 1 || e.key === "Backspace" || e.key === "Delete") {
|
||||
typedRecently = true;
|
||||
}
|
||||
|
||||
if (e.key === 'Enter') {
|
||||
e.preventDefault()
|
||||
_parseHiPS(e)
|
||||
}
|
||||
},
|
||||
input(e) {
|
||||
setTimeout(() => (typedRecently = false), 0);
|
||||
|
||||
let value = e.target.value;
|
||||
self.infoCurrentHiPSBtn.update({
|
||||
disable: true,
|
||||
})
|
||||
|
||||
self.searchTree.triggerFilter({title: e.target.value});
|
||||
self.searchTree.triggerFilter({title: value});
|
||||
|
||||
searchDropdown.removeClass('aladin-valid')
|
||||
searchDropdown.removeClass('aladin-not-valid')
|
||||
|
||||
if (searchDropdown.options && !typedRecently) {
|
||||
let HiPSIDs = searchDropdown.options.options;
|
||||
if (HiPSIDs.includes(value)) {
|
||||
_parseHiPS(e)
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
});
|
||||
@@ -247,19 +266,8 @@ export class HiPSBrowserBox extends Box {
|
||||
},
|
||||
});
|
||||
|
||||
let infoCurrentHiPSBtn = new ActionButton({
|
||||
disable: true,
|
||||
icon: {
|
||||
size: 'medium',
|
||||
monochrome: true,
|
||||
url: infoIconUrl,
|
||||
},
|
||||
tooltip: {
|
||||
global: true,
|
||||
aladin,
|
||||
content: "More about that survey?"
|
||||
}
|
||||
});
|
||||
let infoCurrentHiPSBtn = ActionButton.BUTTONS(aladin)
|
||||
.infoHiPS({disable: true})
|
||||
|
||||
let filterBtn = new TogglerActionButton({
|
||||
icon: {
|
||||
@@ -290,21 +298,16 @@ export class HiPSBrowserBox extends Box {
|
||||
header: {
|
||||
title: Layout.horizontal([new Icon({
|
||||
size: 'medium',
|
||||
url: hipsIconUrl,
|
||||
url: treeIconUrl,
|
||||
monochrome: true,
|
||||
}), "HiPS browser"]),
|
||||
draggable: true,
|
||||
},
|
||||
//onDragged: () => {
|
||||
//if (self.filterBtn.toggled) {
|
||||
//self.filterBtn.toggle();
|
||||
//}
|
||||
//},
|
||||
classList: ['aladin-HiPS-browser-box'],
|
||||
content: Layout.vertical([
|
||||
searchTree,
|
||||
Layout.horizontal(["Search:", searchDropdown, infoCurrentHiPSBtn]),
|
||||
Layout.horizontal(["Filter:", Layout.horizontal([filterEnabler, filterBtn, filterNumberElt])]),
|
||||
Layout.horizontal([Layout.horizontal([filterEnabler, filterBtn, filterNumberElt])]),
|
||||
]),
|
||||
...options,
|
||||
},
|
||||
|
||||
331
src/js/gui/Box/HiPSCompositeBox.js
Normal file
331
src/js/gui/Box/HiPSCompositeBox.js
Normal file
@@ -0,0 +1,331 @@
|
||||
// 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 hipsIconUrl from "../../../../assets/icons/hips.svg";
|
||||
import { Input } from "../Widgets/Input.js";
|
||||
import { Layout } from "../Layout.js";
|
||||
import A from "../../A.js";
|
||||
import { Utils } from "../../Utils.ts";
|
||||
import { ActionButton } from "../Widgets/ActionButton.js";
|
||||
import infoIconUrl from "../../../../assets/icons/info.svg"
|
||||
import { Icon } from "../Widgets/Icon.js";
|
||||
|
||||
/******************************************************************************
|
||||
* Aladin Lite project
|
||||
*
|
||||
* File gui/Box/HiPSCompositeBox.js
|
||||
*
|
||||
* The code source of the interface for creating a new composite HiPS survey from multiple surveys
|
||||
*
|
||||
* Author: Matthieu Baumann[CDS]
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
function fillHiPSHierarchy(name, hips, path, hierarchy) {
|
||||
let folders = path.split('/')
|
||||
let curFolder = folders.shift()
|
||||
|
||||
if(curFolder === 'Image') {
|
||||
let newPath = folders.join('/')
|
||||
fillHiPSHierarchy(name, hips, newPath, hierarchy);
|
||||
} else {
|
||||
// Some exceptions because the MOCServer client_category field may contain some typos
|
||||
if (['X', 'X-ray', 'Xray'].includes(curFolder)) {
|
||||
curFolder = 'X-ray'
|
||||
}
|
||||
|
||||
if (['Radion', 'Radio'].includes(curFolder)) {
|
||||
curFolder = 'Radio'
|
||||
}
|
||||
|
||||
if (curFolder === "Deprecated")
|
||||
return;
|
||||
|
||||
hierarchy[curFolder] = hierarchy[curFolder] || {};
|
||||
if (folders.length == 0) {
|
||||
hierarchy[curFolder][name] = hips
|
||||
} else {
|
||||
let newPath = folders.join('/')
|
||||
fillHiPSHierarchy(name, hips, newPath, hierarchy[curFolder])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class HiPSCompositeBox extends Box {
|
||||
static HiPSList = {};
|
||||
|
||||
constructor(aladin, options) {
|
||||
let self;
|
||||
|
||||
// Search tree
|
||||
MocServer.getAllHiPSes().then((HiPSes) => {
|
||||
HiPSBrowserBox.HiPSList = {}
|
||||
|
||||
let hipsHierarchy = {};
|
||||
// Fill the HiPSList from the MOCServer
|
||||
|
||||
// Build a hierarchy w.r.t sorted by regime
|
||||
HiPSes.forEach((h) => {
|
||||
let name = h.obs_title;
|
||||
name = name.replace(/:|\'/g, '');
|
||||
|
||||
HiPSBrowserBox.HiPSList[name] = h;
|
||||
|
||||
if (h.client_category) {
|
||||
let path = h.client_category
|
||||
|
||||
fillHiPSHierarchy(name, h, path, hipsHierarchy)
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
const _parseHiPS = (e) => {
|
||||
const value = e.target.value;
|
||||
|
||||
let image, name;
|
||||
// A user can put an url
|
||||
try {
|
||||
image = new URL(value).href;
|
||||
name = image;
|
||||
} 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;
|
||||
name = hips.obs_title || hips.ID;
|
||||
} else {
|
||||
// Finally if not found, interpret the input text value as the HiPS (e.g. ID)
|
||||
image = value;
|
||||
name = value;
|
||||
}
|
||||
}
|
||||
|
||||
if (image) {
|
||||
self._addHiPS(image, name)
|
||||
}
|
||||
};
|
||||
|
||||
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')
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
let infoCurrentHiPSBtn = new ActionButton({
|
||||
disable: true,
|
||||
icon: {
|
||||
size: 'medium',
|
||||
monochrome: true,
|
||||
url: infoIconUrl,
|
||||
},
|
||||
tooltip: {
|
||||
global: true,
|
||||
aladin,
|
||||
content: "More about that survey?"
|
||||
}
|
||||
});
|
||||
|
||||
super(
|
||||
{
|
||||
close: true,
|
||||
header: {
|
||||
title: Layout.horizontal([new Icon({
|
||||
size: 'medium',
|
||||
url: hipsIconUrl,
|
||||
monochrome: true,
|
||||
}), "HiPS Compositor"]),
|
||||
draggable: true,
|
||||
},
|
||||
content: Layout.vertical([
|
||||
Layout.horizontal([searchDropdown, infoCurrentHiPSBtn]),
|
||||
]),
|
||||
...options,
|
||||
},
|
||||
aladin.aladinDiv
|
||||
);
|
||||
|
||||
self = this;
|
||||
|
||||
this.searchDropdown = searchDropdown;
|
||||
this.aladin = aladin;
|
||||
|
||||
this.infoCurrentHiPSBtn = infoCurrentHiPSBtn;
|
||||
|
||||
this._addListeners();
|
||||
}
|
||||
|
||||
_addListeners() {}
|
||||
|
||||
_addHiPS(id, name) {
|
||||
let self = this;
|
||||
|
||||
self.searchDropdown.update({value: name, title: name});
|
||||
|
||||
let hips = A.imageHiPS(id, {
|
||||
name,
|
||||
successCallback: (hips) => {
|
||||
self.searchDropdown.removeClass('aladin-not-valid');
|
||||
self.searchDropdown.addClass('aladin-valid');
|
||||
|
||||
self.infoCurrentHiPSBtn.update({
|
||||
disable: false,
|
||||
action(e) {
|
||||
window.open(hips.url);
|
||||
}
|
||||
})
|
||||
|
||||
self.aladin.removeUIByName("cube_displayer" + hips.layer)
|
||||
|
||||
if (!hips.cubeDepth)
|
||||
return;
|
||||
|
||||
let numSlices = hips.cubeDepth;
|
||||
let idxSlice = hips.cubeFirstFrame;
|
||||
|
||||
hips.setSliceNumber(idxSlice)
|
||||
|
||||
let toStr = (n, paddingBegin = false) => {
|
||||
let s = n.toString();
|
||||
let maxNumDigits = numSlices.toString().length;
|
||||
|
||||
if (s.length < maxNumDigits) {
|
||||
let r = ' '.repeat(maxNumDigits - s.length)
|
||||
if (paddingBegin) {
|
||||
s = r + s
|
||||
} else {
|
||||
s += r
|
||||
}
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
let updateSlice = () => {
|
||||
slicer.update({
|
||||
value: idxSlice,
|
||||
tooltip: {content: (idxSlice + 1) + '/' + numSlices, position: {direction: 'bottom'}},
|
||||
})
|
||||
|
||||
hips.setSliceNumber(idxSlice)
|
||||
cubeDisplayer.update({position: cubeDisplayer.position, content: Layout.horizontal([prevBtn, nextBtn, slicer, toStr(idxSlice + 1, true) + '/' + toStr(numSlices, false)])})
|
||||
};
|
||||
|
||||
let slicer = Input.slider({
|
||||
label: "Slice",
|
||||
name: "cube_slicer" + hips.layer,
|
||||
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,
|
||||
name: "cube_displayer" + hips.layer,
|
||||
header: {
|
||||
title: 'Player for: ' + hips.name,
|
||||
draggable: true,
|
||||
},
|
||||
content: Layout.horizontal([prevBtn, nextBtn, slicer, toStr(idxSlice + 1, true) + '/' + toStr(numSlices, false)]),
|
||||
position: {anchor: 'center top'},
|
||||
});
|
||||
|
||||
self.aladin.addUI(cubeDisplayer)
|
||||
},
|
||||
errorCallback: (e) => {
|
||||
self.searchDropdown.removeClass('aladin-valid');
|
||||
self.searchDropdown.addClass('aladin-not-valid');
|
||||
}
|
||||
});
|
||||
this.aladin.setOverlayImageLayer(hips, self.layer);
|
||||
}
|
||||
|
||||
_show(options) {
|
||||
// Regenerate a new layer name
|
||||
this.layer = (options && options.layer) || Utils.uuidv4();
|
||||
super._show(options)
|
||||
}
|
||||
}
|
||||
@@ -41,7 +41,7 @@ export class HiPSFilterBox extends Box {
|
||||
|
||||
let regimeBtn = Input.checkbox({
|
||||
name: 'Freq',
|
||||
tooltip: {content: 'Observation bandwidth', position: {direction: 'left'}},
|
||||
tooltip: {content: 'enable/disable', position: {direction: 'left'}},
|
||||
type: 'checkbox',
|
||||
checked: false,
|
||||
click(e) {
|
||||
@@ -50,7 +50,7 @@ export class HiPSFilterBox extends Box {
|
||||
});
|
||||
let resolutionBtn = Input.checkbox({
|
||||
name: 'Resolution',
|
||||
tooltip: {content: 'Check for HiPS with a specific pixel resolution.', position: {direction: 'left'}},
|
||||
tooltip: {content: 'enable/disable', position: {direction: 'left'}},
|
||||
type: 'checkbox',
|
||||
checked: false,
|
||||
click(e) {
|
||||
@@ -59,12 +59,12 @@ export class HiPSFilterBox extends Box {
|
||||
});
|
||||
|
||||
let regimeOption = Layout.horizontal({
|
||||
tooltip: {
|
||||
content: "Observation regime",
|
||||
position: { direction: "right" },
|
||||
},
|
||||
label: 'Freq: ',
|
||||
label: 'Freq:',
|
||||
layout: [Input.select({
|
||||
tooltip: {
|
||||
content: "Observation regime",
|
||||
position: { direction: "left" },
|
||||
},
|
||||
value: "Optical",
|
||||
options: [
|
||||
"Radio",
|
||||
@@ -170,15 +170,6 @@ export class HiPSFilterBox extends Box {
|
||||
}
|
||||
}
|
||||
|
||||
/*signalBrowserStatus(closed) {
|
||||
this.browserClosed = closed;
|
||||
|
||||
// open
|
||||
if (!closed) {
|
||||
this._requestMOCServer()
|
||||
}
|
||||
}*/
|
||||
|
||||
enable(enable) {
|
||||
this.on = enable;
|
||||
|
||||
|
||||
@@ -36,6 +36,7 @@ import { Utils } from "../../Utils";
|
||||
import { View } from "../../View.js";
|
||||
import { HiPSSettingsBox } from "./HiPSSettingsBox.js";
|
||||
import hipsIconUrl from "../../../../assets/icons/hips.svg";
|
||||
import treeIconUrl from "../../../../assets/icons/tree.svg";
|
||||
import showIconUrl from "../../../../assets/icons/show.svg";
|
||||
import addIconUrl from "../../../../assets/icons/plus.svg";
|
||||
import hideIconUrl from "../../../../assets/icons/hide.svg";
|
||||
@@ -51,6 +52,7 @@ import { CtxMenuActionButtonOpener } from "../Button/CtxMenuOpener.js";
|
||||
import { Input } from "../Widgets/Input.js";
|
||||
import { Image } from "../../Image.js";
|
||||
import { HiPSBrowserBox } from "./HiPSBrowserBox.js";
|
||||
import { HiPSCompositeBox } from "./HiPSCompositeBox.js"
|
||||
|
||||
export class OverlayStackBox extends Box {
|
||||
/*static previewImagesUrl = {
|
||||
@@ -141,8 +143,6 @@ export class OverlayStackBox extends Box {
|
||||
|
||||
this._addListeners();
|
||||
|
||||
this.mocHiPSUrls = {};
|
||||
|
||||
this.HiPSui = {};
|
||||
let self = this;
|
||||
// Add overlay button
|
||||
@@ -501,28 +501,12 @@ export class OverlayStackBox extends Box {
|
||||
cursor: "help",
|
||||
},
|
||||
},
|
||||
content: "Add new survey",
|
||||
content: "Add a new HiPS",
|
||||
},
|
||||
action: (e) => {
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
|
||||
/*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.show();
|
||||
}
|
||||
);
|
||||
|
||||
self.hipsSelectorBox._show({
|
||||
position: self.position,
|
||||
});*/
|
||||
self.aladin.addNewImageLayer(
|
||||
'P/DSS2/color'
|
||||
);
|
||||
@@ -531,7 +515,7 @@ export class OverlayStackBox extends Box {
|
||||
{
|
||||
label: {
|
||||
icon: {
|
||||
url: hipsIconUrl,
|
||||
url: treeIconUrl,
|
||||
monochrome: true,
|
||||
tooltip: {
|
||||
content: "From our database...",
|
||||
@@ -555,6 +539,33 @@ export class OverlayStackBox extends Box {
|
||||
}});
|
||||
},
|
||||
},
|
||||
{
|
||||
label: {
|
||||
icon: {
|
||||
url: hipsIconUrl,
|
||||
monochrome: true,
|
||||
tooltip: {
|
||||
content: "Combine different surveys into a color one!",
|
||||
position: { direction: "right" },
|
||||
},
|
||||
cssStyle: {
|
||||
cursor: "help",
|
||||
},
|
||||
},
|
||||
content: "Add a composite HiPS",
|
||||
},
|
||||
action: (e) => {
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
|
||||
if (!self.hipsCompositeBox)
|
||||
self.hipsCompositeBox = new HiPSCompositeBox(aladin);
|
||||
|
||||
self.hipsCompositeBox._show({position: {
|
||||
anchor: 'center center'
|
||||
}});
|
||||
},
|
||||
},
|
||||
ContextMenu.fileLoaderItem({
|
||||
label: "FITS image file",
|
||||
accept: ".fits",
|
||||
@@ -1034,81 +1045,11 @@ export class OverlayStackBox extends Box {
|
||||
},
|
||||
});
|
||||
|
||||
let loadMOCBtn = new ActionButton({
|
||||
size: "small",
|
||||
|
||||
icon: {
|
||||
url: Icon.dataURLFromSVG({ svg: Icon.SVG_ICONS.MOC }),
|
||||
size: "small",
|
||||
monochrome: true,
|
||||
},
|
||||
tooltip: {
|
||||
content: "Add coverage",
|
||||
position: { direction: "top" },
|
||||
},
|
||||
toggled: (() => {
|
||||
let overlays = self.aladin.getOverlays();
|
||||
let found = overlays.find(
|
||||
(o) => o.type === "moc" && o.name === layer.name
|
||||
);
|
||||
return found !== undefined;
|
||||
})(),
|
||||
action: (e) => {
|
||||
if (!loadMOCBtn.options.toggled) {
|
||||
// load the moc
|
||||
let moc = A.MOCFromURL(
|
||||
layer.url + "/Moc.fits",
|
||||
{ name: layer.name },
|
||||
() => {
|
||||
self.mocHiPSUrls[layer.url] = moc;
|
||||
|
||||
if (self.aladin.statusBar) {
|
||||
self.aladin.statusBar.appendMessage({
|
||||
message:
|
||||
"Coverage of " +
|
||||
layer.name +
|
||||
" loaded",
|
||||
duration: 2000,
|
||||
type: "info",
|
||||
});
|
||||
}
|
||||
|
||||
loadMOCBtn.update({
|
||||
toggled: true,
|
||||
tooltip: {
|
||||
content: "Remove coverage",
|
||||
position: { direction: "top" },
|
||||
},
|
||||
});
|
||||
}
|
||||
);
|
||||
self.aladin.addMOC(moc);
|
||||
} else {
|
||||
// unload the moc
|
||||
let moc = self.mocHiPSUrls[layer.url];
|
||||
self.aladin.removeLayer(moc);
|
||||
|
||||
delete self.mocHiPSUrls[layer.url];
|
||||
|
||||
if (self.aladin.statusBar) {
|
||||
self.aladin.statusBar.appendMessage({
|
||||
message:
|
||||
"Coverage of " + layer.name + " removed",
|
||||
duration: 2000,
|
||||
type: "info",
|
||||
});
|
||||
}
|
||||
|
||||
loadMOCBtn.update({
|
||||
toggled: false,
|
||||
tooltip: {
|
||||
content: "Add coverage",
|
||||
position: { direction: "top" },
|
||||
},
|
||||
});
|
||||
}
|
||||
},
|
||||
});
|
||||
let loadMOCBtn = ActionButton.BUTTONS(aladin)
|
||||
.addMOC({
|
||||
name: layer.name,
|
||||
url: layer.url + '/Moc.fits'
|
||||
});
|
||||
|
||||
self.layer2swap = null;
|
||||
let swapBtn = new ActionButton({
|
||||
|
||||
@@ -67,7 +67,6 @@ import infoIconUrl from '../../../../assets/icons/info.svg';
|
||||
import { Input } from "../Widgets/Input.js";
|
||||
|
||||
export class Dropdown extends Input {
|
||||
|
||||
// constructor
|
||||
constructor(aladin, options) {
|
||||
let self;
|
||||
@@ -91,12 +90,6 @@ export class Dropdown extends Input {
|
||||
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)
|
||||
|
||||
@@ -21,6 +21,10 @@ import { DOMElement } from "./Widget";
|
||||
import { Tooltip } from "./Tooltip";
|
||||
import { Icon } from "./Icon";
|
||||
import { Layout } from "../Layout";
|
||||
|
||||
import infoIconUrl from "../../../../assets/icons/info.svg"
|
||||
import targetIconUrl from '../../../../assets/icons/target.svg';
|
||||
import A from "../../A";
|
||||
/******************************************************************************
|
||||
* Aladin Lite project
|
||||
*
|
||||
@@ -188,4 +192,136 @@ export class ActionButton extends DOMElement {
|
||||
|
||||
return new ActionButton(opt, target, position);
|
||||
}
|
||||
|
||||
static mocs = {};
|
||||
static BUTTONS(aladin) {
|
||||
return {
|
||||
infoHiPS: (options) => {
|
||||
return new ActionButton({
|
||||
icon: {
|
||||
size: 'small',
|
||||
monochrome: true,
|
||||
url: infoIconUrl,
|
||||
},
|
||||
tooltip: {
|
||||
position: {direction: "top"},
|
||||
content: "More about that survey?"
|
||||
},
|
||||
action(e) {
|
||||
window.open(options && options.url);
|
||||
},
|
||||
...options
|
||||
})
|
||||
},
|
||||
targetHiPSLocation: (options) => {
|
||||
let ra = options && options.ra;
|
||||
let dec = options && options.dec;
|
||||
let fov = options && options.fov;
|
||||
return new ActionButton({
|
||||
icon: {
|
||||
size: 'small',
|
||||
monochrome: true,
|
||||
url: targetIconUrl,
|
||||
},
|
||||
disable: ra === undefined || dec === undefined || fov === undefined,
|
||||
tooltip: {
|
||||
content: "Target interesting sky location",
|
||||
},
|
||||
action(e) {
|
||||
if (fov !== undefined && ra !== undefined && dec !== undefined) {
|
||||
aladin.setFoV(+fov)
|
||||
aladin.gotoObject(ra + ' ' + dec);
|
||||
}
|
||||
|
||||
},
|
||||
...options
|
||||
})
|
||||
},
|
||||
addMOC: (options) => {
|
||||
let name = options && options.name;
|
||||
let url = options && options.url;
|
||||
|
||||
let button = new ActionButton({
|
||||
size: "small",
|
||||
icon: {
|
||||
url: Icon.dataURLFromSVG({ svg: Icon.SVG_ICONS.MOC }),
|
||||
size: "small",
|
||||
monochrome: true,
|
||||
},
|
||||
tooltip: {
|
||||
content: "Add coverage",
|
||||
position: { direction: "top" },
|
||||
},
|
||||
toggled: (() => {
|
||||
let overlays = aladin.getOverlays();
|
||||
let found = overlays.find(
|
||||
(o) => o.type === "moc" && o.name === name
|
||||
);
|
||||
return found !== undefined;
|
||||
})(),
|
||||
action: (e) => {
|
||||
if (!button.options.toggled) {
|
||||
// load the moc
|
||||
let moc = A.MOCFromURL(
|
||||
url,
|
||||
{ name },
|
||||
() => {
|
||||
if (aladin.statusBar) {
|
||||
aladin.statusBar.appendMessage({
|
||||
message:
|
||||
"Coverage of " +
|
||||
name +
|
||||
" loaded",
|
||||
duration: 2000,
|
||||
type: "info",
|
||||
});
|
||||
}
|
||||
|
||||
button.update({
|
||||
toggled: true,
|
||||
tooltip: {
|
||||
content: "Remove coverage",
|
||||
position: { direction: "top" },
|
||||
},
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
aladin.addMOC(moc);
|
||||
} else {
|
||||
// unload the moc
|
||||
let overlays = aladin.getOverlays();
|
||||
let moc = overlays.find(
|
||||
(o) => {
|
||||
console.log(o.name)
|
||||
o.type === "moc" && o.name === name
|
||||
}
|
||||
);
|
||||
aladin.removeLayer(moc);
|
||||
|
||||
if (aladin.statusBar) {
|
||||
aladin.statusBar.appendMessage({
|
||||
message:
|
||||
"Coverage of " + name + " removed",
|
||||
duration: 2000,
|
||||
type: "info",
|
||||
});
|
||||
}
|
||||
|
||||
button.update({
|
||||
toggled: false,
|
||||
tooltip: {
|
||||
content: "Add coverage",
|
||||
position: { direction: "top" },
|
||||
},
|
||||
});
|
||||
}
|
||||
},
|
||||
...options
|
||||
})
|
||||
|
||||
return button;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -141,7 +141,8 @@ export class Icon extends DOMElement {
|
||||
static SVG_ICONS = {
|
||||
CATALOG: '<svg xmlns="http://www.w3.org/2000/svg"><polygon points="1,0,5,0,5,3,1,3" fill="FILLCOLOR" /><polygon points="7,0,9,0,9,3,7,3" fill="FILLCOLOR" /><polygon points="10,0,12,0,12,3,10,3" fill="FILLCOLOR" /><polygon points="13,0,15,0,15,3,13,3" fill="FILLCOLOR" /><polyline points="1,5,5,9" stroke="FILLCOLOR" /><polyline points="1,9,5,5" stroke="FILLCOLOR" /><line x1="7" y1="7" x2="15" y2="7" stroke="FILLCOLOR" stroke-width="2" /><polyline points="1,11,5,15" stroke="FILLCOLOR" /><polyline points="1,15,5,11" stroke="FILLCOLOR" /><line x1="7" y1="13" x2="15" y2="13" stroke="FILLCOLOR" stroke-width="2" /></svg>',
|
||||
MOC: '<svg xmlns="http://www.w3.org/2000/svg"><polyline points="0.5,7,2.5,7,2.5,5,7,5,7,3,10,3,10,5,13,5,13,7,15,7,15,9,13,9,13,12,10,12,10,14,7,14,7,12,2.5,12,2.5,10,0.5,10,0.5,7" stroke-width="1" stroke="FILLCOLOR" fill="transparent" /><line x1="1" y1="10" x2="6" y2="5" stroke="FILLCOLOR" stroke-width="0.5" /><line x1="2" y1="12" x2="10" y2="4" stroke="FILLCOLOR" stroke-width="0.5" /><line x1="5" y1="12" x2="12" y2="5" stroke="FILLCOLOR" stroke-width="0.5" /><line x1="7" y1="13" x2="13" y2="7" stroke="FILLCOLOR" stroke-width="0.5" /><line x1="10" y1="13" x2="13" y2="10" stroke="FILLCOLOR" stroke-width="0.5" /></svg>',
|
||||
OVERLAY: '<svg xmlns="http://www.w3.org/2000/svg"><polygon points="10,5,10,1,14,1,14,14,2,14,2,9,6,9,6,5" fill="transparent" stroke="FILLCOLOR" stroke-width="2"/></svg>'
|
||||
OVERLAY: '<svg xmlns="http://www.w3.org/2000/svg"><polygon points="10,5,10,1,14,1,14,14,2,14,2,9,6,9,6,5" fill="transparent" stroke="FILLCOLOR" stroke-width="2"/></svg>',
|
||||
COLOR: '<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 122.88 112.75" style="enable-background:new 0 0 122.88 112.75" xml:space="preserve"><style type="text/css">.st0{fill-rule:evenodd;clip-rule:evenodd;fill:#6BBE66;} .st1{fill-rule:evenodd;clip-rule:evenodd;fill:#FF4141;} .st2{fill-rule:evenodd;clip-rule:evenodd;fill:#00A1F1;}</style><g><path class="st0" d="M56.66,105.35c-5.94,4.6-13.4,7.34-21.5,7.34C15.74,112.69,0,96.95,0,77.53c0-13.47,7.58-25.17,18.7-31.07 c1.96,6.96,5.68,13.19,10.65,18.16c4.64,4.64,10.38,8.2,16.79,10.24c-0.06,0.9-0.09,1.81-0.09,2.73 C46.06,88.25,50.07,97.98,56.66,105.35L56.66,105.35z"/><path class="st1" d="M122.88,77.59c0,19.42-15.74,35.16-35.16,35.16S52.56,97,52.56,77.59c0-0.41,0.01-0.82,0.02-1.23 c2.03,0.3,4.11,0.46,6.23,0.46c11.5,0,21.92-4.66,29.46-12.2c5.45-5.45,9.4-12.41,11.17-20.19 C113.1,49.26,122.88,62.28,122.88,77.59L122.88,77.59z"/><path class="st2" d="M93.97,35.16c0,19.42-15.74,35.16-35.16,35.16S23.65,54.58,23.65,35.16S39.39,0,58.81,0 S93.97,15.74,93.97,35.16L93.97,35.16z"/></g></svg>'
|
||||
}
|
||||
|
||||
static dataURLFromSVG(icon) {
|
||||
@@ -150,9 +151,6 @@ export class Icon extends DOMElement {
|
||||
let elt = document.createElement('div');
|
||||
elt.innerHTML = str;
|
||||
|
||||
//elt.querySelector('svg').setAttribute('width', size);
|
||||
//elt.querySelector('svg').setAttribute('height', size);
|
||||
|
||||
elt.style.width = size;
|
||||
elt.style.height = size;
|
||||
|
||||
|
||||
@@ -50,8 +50,7 @@ export class Tooltip extends DOMElement {
|
||||
}
|
||||
options.position.anchor = target;
|
||||
|
||||
|
||||
if (!options.delayShowUpTime) {
|
||||
if (options.delayShowUpTime === undefined) {
|
||||
options.delayShowUpTime = 500;
|
||||
}
|
||||
|
||||
@@ -176,6 +175,8 @@ export class Tooltip extends DOMElement {
|
||||
return this.el.querySelector('.aladin-tooltip');
|
||||
}
|
||||
|
||||
static hoveredEl = null;
|
||||
|
||||
static add(options, target) {
|
||||
if (target) {
|
||||
if (target.tooltip) {
|
||||
@@ -188,13 +189,42 @@ export class Tooltip extends DOMElement {
|
||||
|
||||
let targetEl = target.element()
|
||||
|
||||
if (options.mouse) {
|
||||
let tooltip = options.aladin && options.aladin.tooltip;
|
||||
|
||||
Utils.on(targetEl, 'mousemove', (e) => {
|
||||
tooltip.style.left = e.clientX + 12 + 'px';
|
||||
tooltip.style.top = e.clientY + 12 + 'px';
|
||||
});
|
||||
|
||||
Utils.on(targetEl, 'mouseover', (e) => {
|
||||
if (Tooltip.hoveredEl && Tooltip.hoveredEl.contains(targetEl))
|
||||
return;
|
||||
|
||||
Tooltip.hoveredEl = targetEl;
|
||||
// Change the content to match
|
||||
tooltip.innerHTML = options.content;
|
||||
tooltip.style.display = 'block';
|
||||
});
|
||||
|
||||
Utils.on(targetEl, 'mouseleave', (e) => {
|
||||
if (Tooltip.hoveredEl && Tooltip.hoveredEl.contains(targetEl) && Tooltip.hoveredEl !== targetEl) {
|
||||
return;
|
||||
}
|
||||
|
||||
tooltip.style.display = 'none';
|
||||
Tooltip.hoveredEl = null;
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (options.global) {
|
||||
let statusBar = options.aladin && options.aladin.statusBar;
|
||||
if (!statusBar) {
|
||||
return;
|
||||
}
|
||||
|
||||
// handle global tooltip div display
|
||||
Utils.on(targetEl, 'mouseover', (e) => {
|
||||
statusBar.removeMessage('tooltip')
|
||||
statusBar.appendMessage({
|
||||
|
||||
@@ -21,6 +21,8 @@ import { DOMElement } from "./Widget";
|
||||
import { Icon } from "./Icon";
|
||||
import folderIconUrl from "../../../../assets/icons/folder.svg";
|
||||
|
||||
import { Layout } from "../Layout";
|
||||
import { ActionButton } from "./ActionButton";
|
||||
/******************************************************************************
|
||||
* Aladin Lite project
|
||||
*
|
||||
@@ -40,6 +42,7 @@ export class Tree extends DOMElement {
|
||||
super(el, options);
|
||||
|
||||
this.click = options && options.click;
|
||||
this.aladin = options && options.aladin;
|
||||
|
||||
let rootNode = options && options.root || {};
|
||||
this.params = null;
|
||||
@@ -116,7 +119,22 @@ export class Tree extends DOMElement {
|
||||
|
||||
let listElt = document.createElement('ul');
|
||||
|
||||
for (const label of Object.keys(node).sort()) {
|
||||
let labels = Object.keys(node).sort((la, lb) => {
|
||||
let na = node[la];
|
||||
let nb = node[lb];
|
||||
|
||||
let aIsLeaf = typeof na === "object" && 'ID' in na;
|
||||
let bIsLeaf = typeof nb === "object" && 'ID' in nb;
|
||||
|
||||
if (aIsLeaf !== bIsLeaf) {
|
||||
return aIsLeaf - bIsLeaf;
|
||||
} else if (la < lb) {
|
||||
return -1
|
||||
} else {
|
||||
return 1;
|
||||
}
|
||||
});
|
||||
for (const label of labels) {
|
||||
if (label !== 'parent' && label !== "label") {
|
||||
let elt = document.createElement('li');
|
||||
// points towards the parent node
|
||||
@@ -129,7 +147,69 @@ export class Tree extends DOMElement {
|
||||
elt.style.display = "block";
|
||||
}
|
||||
|
||||
elt.innerHTML = this.label(child);
|
||||
let label = this.label(child);
|
||||
|
||||
let layout = [label];
|
||||
|
||||
if (child.dataproduct_subtype === "color") {
|
||||
layout.push(new Icon({
|
||||
size: "small",
|
||||
url: Icon.dataURLFromSVG({ svg: Icon.SVG_ICONS.COLOR }),
|
||||
}))
|
||||
}
|
||||
|
||||
layout.push(ActionButton.BUTTONS(this.aladin)
|
||||
.infoHiPS({
|
||||
url: child.hips_service_url,
|
||||
tooltip: {
|
||||
aladin: this.aladin,
|
||||
global: true,
|
||||
content: "More info on the survey ?",
|
||||
},
|
||||
}).element()
|
||||
)
|
||||
|
||||
layout = layout.concat([
|
||||
ActionButton.BUTTONS(this.aladin)
|
||||
.targetHiPSLocation({
|
||||
ra: child.hips_initial_ra,
|
||||
dec: child.hips_initial_dec,
|
||||
fov: child.hips_initial_fov,
|
||||
tooltip: {
|
||||
aladin: this.aladin,
|
||||
global: true,
|
||||
content: "Move to an interesting location",
|
||||
},
|
||||
})
|
||||
.element(),
|
||||
ActionButton.BUTTONS(this.aladin)
|
||||
.addMOC({
|
||||
name: label,
|
||||
url: child.hips_service_url + '/Moc.fits',
|
||||
tooltip: {
|
||||
aladin: this.aladin,
|
||||
global: true,
|
||||
content: "Click to add its coverage",
|
||||
},
|
||||
})
|
||||
.element(),
|
||||
])
|
||||
|
||||
let config = {
|
||||
layout,
|
||||
tooltip: {
|
||||
content: '<figure class="aladin-fig"><img ' +
|
||||
`src="${child.hips_service_url + "/preview.jpg"}"` +
|
||||
`alt="${label}" />` +
|
||||
`<figcaption>${label}</figcaption>` +
|
||||
'</figure>',
|
||||
delayShowUpTime: "100ms",
|
||||
mouse: true,
|
||||
aladin: this.aladin,
|
||||
}
|
||||
};
|
||||
|
||||
elt.appendChild(Layout.horizontal(config).element());
|
||||
} else {
|
||||
// we see a parent, we must determine:
|
||||
// * its color: he has at least 1 child inside the FoV => green
|
||||
@@ -138,7 +218,14 @@ export class Tree extends DOMElement {
|
||||
let numTotal = this.numChildMatchingFilter(child, false);
|
||||
|
||||
let name = label;
|
||||
elt.innerHTML = name + ` (${numFilteringMatching}/${numTotal})`
|
||||
elt.appendChild(Layout.horizontal([
|
||||
new Icon({
|
||||
size: "small",
|
||||
monochrome: true,
|
||||
url: folderIconUrl,
|
||||
}),
|
||||
name + ` (${numFilteringMatching}/${numTotal})`
|
||||
]).element())
|
||||
|
||||
if (numFilteringMatching == 0) {
|
||||
elt.style.display = "none";
|
||||
@@ -191,7 +278,23 @@ export class Tree extends DOMElement {
|
||||
let elts = this.el.querySelectorAll("li");
|
||||
let i = 0;
|
||||
|
||||
for (const label of Object.keys(this.curNode).sort()) {
|
||||
let labels = Object.keys(this.curNode).sort((la, lb) => {
|
||||
let na = this.curNode[la];
|
||||
let nb = this.curNode[lb];
|
||||
|
||||
let aIsLeaf = typeof na === "object" && 'ID' in na;
|
||||
let bIsLeaf = typeof nb === "object" && 'ID' in nb;
|
||||
|
||||
if (aIsLeaf !== bIsLeaf) {
|
||||
return aIsLeaf - bIsLeaf;
|
||||
} else if (la < lb) {
|
||||
return -1
|
||||
} else {
|
||||
return 1;
|
||||
}
|
||||
});
|
||||
|
||||
for (const label of labels) {
|
||||
if (label !== 'parent' && label !== "label") {
|
||||
let elt = elts[i];
|
||||
i += 1;
|
||||
@@ -216,7 +319,15 @@ export class Tree extends DOMElement {
|
||||
let numTotal = this.numChildMatchingFilter(child, false);
|
||||
|
||||
let name = elt.innerText.split('(');
|
||||
elt.innerHTML = name[0] + ` (${numFilteringMatching}/${numTotal})`
|
||||
|
||||
elt.innerHTML = Layout.horizontal([
|
||||
new Icon({
|
||||
size: "small",
|
||||
monochrome: true,
|
||||
url: folderIconUrl,
|
||||
}),
|
||||
name[0] + ` (${numFilteringMatching}/${numTotal})`
|
||||
]).element().outerHTML
|
||||
|
||||
if (numFilteringMatching == 0) {
|
||||
elt.style.display = "none";
|
||||
@@ -239,7 +350,22 @@ export class Tree extends DOMElement {
|
||||
let elts = this.el.querySelectorAll("li");
|
||||
let i = 0;
|
||||
|
||||
for (const label of Object.keys(this.curNode).sort()) {
|
||||
let labels = Object.keys(this.curNode).sort((la, lb) => {
|
||||
let na = this.curNode[la];
|
||||
let nb = this.curNode[lb];
|
||||
|
||||
let aIsLeaf = typeof na === "object" && 'ID' in na;
|
||||
let bIsLeaf = typeof nb === "object" && 'ID' in nb;
|
||||
|
||||
if (aIsLeaf !== bIsLeaf) {
|
||||
return aIsLeaf - bIsLeaf;
|
||||
} else if (la < lb) {
|
||||
return -1
|
||||
} else {
|
||||
return 1;
|
||||
}
|
||||
});
|
||||
for (const label of labels) {
|
||||
if (label !== 'parent' && label !== "label") {
|
||||
let elt = elts[i];
|
||||
i += 1;
|
||||
@@ -260,7 +386,15 @@ export class Tree extends DOMElement {
|
||||
let numTotal = this.numChildMatchingFilter(child, false);
|
||||
|
||||
let name = elt.innerText.split('(');
|
||||
elt.innerHTML = name[0] + ` (${numFilteringMatching}/${numTotal})`
|
||||
elt.innerHTML = Layout.horizontal([
|
||||
new Icon({
|
||||
size: "small",
|
||||
monochrome: true,
|
||||
url: folderIconUrl,
|
||||
}),
|
||||
name[0] + ` (${numFilteringMatching}/${numTotal})`
|
||||
]).element().outerHTML;
|
||||
|
||||
if (numFilteringMatching == 0) {
|
||||
elt.style.display = "none";
|
||||
} else {
|
||||
@@ -286,7 +420,22 @@ export class Tree extends DOMElement {
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
for (const label of Object.keys(node).sort()) {
|
||||
let labels = Object.keys(node).sort((la, lb) => {
|
||||
let na = node[la];
|
||||
let nb = node[lb];
|
||||
|
||||
let aIsLeaf = typeof na === "object" && 'ID' in na;
|
||||
let bIsLeaf = typeof nb === "object" && 'ID' in nb;
|
||||
|
||||
if (aIsLeaf !== bIsLeaf) {
|
||||
return aIsLeaf - bIsLeaf;
|
||||
} else if (la < lb) {
|
||||
return -1
|
||||
} else {
|
||||
return 1;
|
||||
}
|
||||
});
|
||||
for (const label of labels) {
|
||||
if (label === "parent")
|
||||
continue;
|
||||
|
||||
@@ -314,7 +463,22 @@ export class Tree extends DOMElement {
|
||||
}
|
||||
} else {
|
||||
let num = 0;
|
||||
for (const label of Object.keys(node).sort()) {
|
||||
let labels = Object.keys(node).sort((la, lb) => {
|
||||
let na = node[la];
|
||||
let nb = node[lb];
|
||||
|
||||
let aIsLeaf = typeof na === "object" && 'ID' in na;
|
||||
let bIsLeaf = typeof nb === "object" && 'ID' in nb;
|
||||
|
||||
if (aIsLeaf !== bIsLeaf) {
|
||||
return aIsLeaf - bIsLeaf;
|
||||
} else if (la < lb) {
|
||||
return -1
|
||||
} else {
|
||||
return 1;
|
||||
}
|
||||
});
|
||||
for (const label of labels) {
|
||||
if (label === "parent")
|
||||
continue;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user