first commit tree

This commit is contained in:
Matthieu Baumann
2025-12-08 14:02:05 +01:00
parent 8d244596ba
commit af89535a91
21 changed files with 8534 additions and 454 deletions

4
assets/icons/folder.svg Normal file
View File

@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?><!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
<svg width="800px" height="800px" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M0 1H5L8 3H13V5H3.7457L2.03141 11H4.11144L5.2543 7H16L14 14H0V1Z" fill="#000000"/>
</svg>

After

Width:  |  Height:  |  Size: 322 B

View File

@@ -8,8 +8,8 @@
"dateModified": "2023-01-31",
"issueTracker": "https://github.com/cds-astro/aladin-lite/issues",
"name": "Aladin Lite",
"version": "3.7.2-beta",
"softwareVersion": "3.7.2-beta",
"version": "3.7.3-beta",
"softwareVersion": "3.7.3-beta",
"description": "An astronomical HiPS visualizer in the browser.",
"identifier": "10.5281/zenodo.7638833",
"applicationCategory": "Astronomy, Visualization",

View File

@@ -37,7 +37,10 @@
colorPicker.value = cat.color;
colorPicker.addEventListener('input', function (e) {
// Change the color of the catalog
cat.updateShape({color: this.value});
console.log(this.value)
cat.updateShape({color: () => {
return '#00ff00'
}});
})
// Define the box

View File

@@ -10,7 +10,7 @@
<script type="text/javascript">
var aladin;
A.init.then(() => {
aladin = A.aladin('#aladin-lite-div', {fullScreen: true, cooFrame: "ICRSd", showSimbadPointerControl: true, showShareControl: true, showShareControl: true, survey: 'https://jafar.astro.unistra.fr/data/jafar_hips/Surveys/CFIS/IC0750/asinh_0_1/', fov: 180, showContextMenu: true});
aladin = A.aladin('#aladin-lite-div', {fullScreen: true, cooFrame: "ICRSd", showSimbadPointerControl: true, showShareControl: true, showShareControl: true, fov: 180, showContextMenu: true});
// manage URL parameters
const searchParams = new URL(document.location).searchParams;
if (searchParams.has('baseImageLayer')) {

View File

@@ -553,6 +553,7 @@ impl App {
/*let is_there_new_available_tiles = self
.downloader
.get_resolved_tiles(/*&available_tiles, */&mut self.hipss);*/
//self.tile_fetcher.clear();
if self.request_for_new_tiles
&& Time::now() - self.last_time_request_for_new_tiles > DeltaTime::from(500.0)
@@ -564,18 +565,18 @@ impl App {
}
// Tiles are fetched if:
let fetch_tiles =
// * the user is not panning the view
// * or the user is but did not move for at least 100ms
//(Time::now() - self.camera.get_time_of_last_move() >= DeltaTime(100.0) || !self.dragging) &&
// * no inertia action is in progress
//self.inertia.is_none() &&
// * the user is not zooming
!self.camera.has_zoomed();
//let fetch_tiles =
// * the user is not panning the view
// * or the user is but did not move for at least 100ms
//(Time::now() - self.camera.get_time_of_last_move() >= DeltaTime(100.0) || !self.dragging) &&
// * no inertia action is in progress
//self.inertia.is_none() &&
// * the user is not zooming
// !self.camera.has_zoomed();
if fetch_tiles {
self.tile_fetcher.notify(self.downloader.clone(), None);
}
//if fetch_tiles {
self.tile_fetcher.notify(self.downloader.clone(), None);
//}
}
let rscs_received = self.downloader.borrow_mut().get_received_resources();

View File

@@ -390,7 +390,15 @@ impl Layers {
"Layer {second_layer:?} not found, so cannot be removed.",
)))?;
self.layers.swap(id_first_layer, id_second_layer);
if let (Some(k1), Some(k2)) = (self.ids.get(first_layer), self.ids.get(second_layer)) {
if let (Some(v1), Some(v2)) = (self.hipses.remove(k1), self.hipses.remove(k2)) {
self.hipses.insert(k2.to_string(), v1);
self.hipses.insert(k1.to_string(), v2);
} else if let (Some(v1), Some(v2)) = (self.images.remove(k1), self.images.remove(k2)) {
self.images.insert(k2.to_string(), v1);
self.images.insert(k1.to_string(), v2);
}
}
Ok(())
}

File diff suppressed because it is too large Load Diff

View File

@@ -19,6 +19,44 @@
--aladin-color-border: #fff;
}
.aladin-tree {
width: 100%;
border-top: 1px solid white;
border-bottom: 2px solid white;
}
.aladin-tree .aladin-directory-path {
padding-top: 0.5rem;
padding-bottom: 0.5rem;
width: 100%;
border-bottom: 2px solid white;
}
.aladin-tree ul {
padding:0;
margin:0;
overflow-y: scroll;
height: 300px;
}
.aladin-link {
list-style-type: none;
padding: 0.5em 0px;
cursor:pointer;
margin: 0;
}
.aladin-tree li:hover {
color: yellowgreen;
}
.aladin-tree li:last-child {
border-bottom: none;
}
.aladin-tree li {
border-bottom: 1px solid white;
}
.aladin-dark-theme {
background-color: black;
color: white;
@@ -239,18 +277,15 @@
font-family: monospace;
background: #fff;
line-height: 1.3;
color: #222;
/*box-shadow: 0 0 6px rgba(0,0,0,0.2);*/
/* Allow scrolling but disable scroll bar */
-ms-overflow-style: none; /* for Internet Explorer, Edge */
scrollbar-width: none; /* for Firefox */
/*overflow-y: auto;*/
overflow-y: none;
max-height: 500px;
/*max-width: fit-content;*/
height: fit-content;
height: min-content;
width: min-content;
border: 1px solid white;
}
.aladin-container canvas {
@@ -319,7 +354,7 @@
.aladin-box-separator {
height: 0;
border-top: 1px solid #d2d2d2;
margin: 5px 0px 5px -4px;
padding-bottom: 5px;
}
.aladin-restore {
@@ -509,11 +544,11 @@
}
.aladin-vertical-list > *:first-child {
margin-top: 0;
padding-top: 0;
}
.aladin-vertical-list > * {
margin-top: 0.5rem;
padding-top: 0.5rem;
}
.aladin-horizontal-list {
@@ -1124,6 +1159,7 @@ otherwise it fits its content options. If those are too big the select can go ou
.aladin-link:hover {
color: greenyellow;
text-decoration: underline;
}
.aladin-simbadPointer-control {
@@ -1160,9 +1196,13 @@ otherwise it fits its content options. If those are too big the select can go ou
.aladin-stack-box {
width: 17rem;
}
.aladin-item-selected {
border: 1px solid orange;
}
.aladin-HiPS-filter-box {
margin-top: 0.4rem;
/*width: 250px;*/
}
.aladin-HiPS-filter-box .aladin-horizontal-list {

View File

@@ -2004,7 +2004,7 @@ export let Aladin = (function () {
* </ul>
*/
Aladin.prototype.setBaseImageLayer = function (urlOrHiPSOrFITS) {
return this.setOverlayImageLayer(urlOrHiPSOrFITS, "base");
return this.setOverlayImageLayer(urlOrHiPSOrFITS, this.overlayLayers[0] || Utils.uuidv4());
};
/**

View File

@@ -51,7 +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",
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",
};

View File

@@ -384,7 +384,6 @@ Utils.fetch = function(params) {
// localhost url
url = params.url;
}
let request = new Request(url, {
method: params.method || 'GET',

View File

@@ -1927,12 +1927,12 @@ export let View = (function () {
this.wasm.swapLayers(firstLayer, secondLayer);
// Swap in overlaylayers
const idxFirstLayer = this.overlayLayers.findIndex(overlayLayer => overlayLayer == firstLayer);
const idxSecondLayer = this.overlayLayers.findIndex(overlayLayer => overlayLayer == secondLayer);
/*const idxFirstLayer = this.overlayLayers.indexOf(firstLayer);
const idxSecondLayer = this.overlayLayers.indexOf(secondLayer);
const tmp = this.overlayLayers[idxFirstLayer];
this.overlayLayers[idxFirstLayer] = this.overlayLayers[idxSecondLayer];
this.overlayLayers[idxSecondLayer] = tmp;
this.overlayLayers[idxSecondLayer] = tmp;*/
// Tell the layer hierarchy has changed
ALEvent.HIPS_LAYER_SWAP.dispatchedTo(this.aladinDiv, { firstLayer: firstLayer, secondLayer: secondLayer });

View File

@@ -33,6 +33,8 @@ 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";
/******************************************************************************
* Aladin Lite project
@@ -44,32 +46,109 @@ import { Icon } from "../Widgets/Icon.js";
*
*****************************************************************************/
function fillHiPSHierarchy(name, hips, path, hierarchy) {
let folders = path.split('/')
let curFolder = folders.shift()
// 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 HiPSBrowserBox extends Box {
static HiPSList = {};
constructor(aladin, options) {
let self;
let filter = (item, params) => {
if (params.regime) {
if (!item.obs_regime)
return false;
if (params.regime.toLowerCase() !== item.obs_regime.toLowerCase()) {
return false;
}
}
if (params.resolution) {
if (!item.hips_tile_width || !item.hips_order) {
return false;
}
let pixelHEALPixOrder = Math.log2(item.hips_tile_width) + (+item.hips_order);
let resPixel = Math.sqrt(Math.PI / (3*Math.pow(4, pixelHEALPixOrder)));
if (resPixel > params.resolution)
return false;
}
if (params.title) {
if (!item.obs_title)
return false;
if (!item.obs_title.toLowerCase().includes(params.title.toLowerCase())) {
return false;
}
}
return true;
};
// Search tree
let searchTree = new Tree({
// a JS object describing the tree to show
label: (item) => {
let name = item.obs_title.replace(/:|\'/g, '');
return name;
},
// a callback called when the user selects a leaf item of the tree
click: (item) => {
let image = item.ID || item.hips_service_url;
let name = item.obs_title || item.ID;
self._addHiPS(image, name)
},
// a callback called for filtering
filter,
});
MocServer.getAllHiPSes().then((HiPSes) => {
HiPSBrowserBox.HiPSList = {}
self.HiPSTree = {};
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;
self.HiPSTree[h.obs_regime] = self.HiPSTree[h.obs_regime] || {};
if (self.HiPSTree[h.obs_regime]) {
self.HiPSTree[h.obs_regime][name] = h
if (h.client_category) {
let path = h.client_category.toLowerCase()
fillHiPSHierarchy(name, h, path, hipsHierarchy)
}
});
console.log("jkjk", self.HiPSTree)
self.searchTree.setHierarchy(hipsHierarchy)
// Initialize the autocompletion without any filtering
self._filterHiPSList({})
@@ -98,7 +177,6 @@ export class HiPSBrowserBox extends Box {
if (image) {
self._addHiPS(image, name)
self.searchDropdown.update({title: value});
}
};
@@ -128,14 +206,11 @@ export class HiPSBrowserBox extends Box {
disable: true,
})
self.searchTree.triggerFilter({title: e.target.value});
searchDropdown.removeClass('aladin-valid')
searchDropdown.removeClass('aladin-not-valid')
},
change(e) {
e.stopPropagation();
e.preventDefault()
_parseHiPS(e)
}
},
});
@@ -193,13 +268,9 @@ export class HiPSBrowserBox extends Box {
},
toggled: false,
actionOn: (e) => {
self.filterBox._show({
position: {
nextTo: filterBtn,
direction: "bottom",
aladin,
},
});
self.filterBox._show({position: {
anchor: 'right center'
}});
},
actionOff: (e) => {
self.filterBox._hide();
@@ -216,15 +287,17 @@ export class HiPSBrowserBox extends Box {
size: 'medium',
url: hipsIconUrl,
monochrome: true,
}), "HiPS browser"])
},
onDragged: () => {
if (self.filterBtn.toggled) {
self.filterBtn.toggle();
}
}), "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])]),
]),
@@ -233,6 +306,9 @@ export class HiPSBrowserBox extends Box {
aladin.aladinDiv
);
self = this;
this.searchTree = searchTree;
this.filterBox = new HiPSFilterBox(aladin, {
callback: (params) => {
self._filterHiPSList(params);
@@ -247,48 +323,42 @@ export class HiPSBrowserBox extends Box {
this.infoCurrentHiPSBtn = infoCurrentHiPSBtn;
self = this;
this.filterCallback = (HiPS, params) => {
if (params.regime) {
if (!HiPS.obs_regime)
return false;
if (params.regime.toLowerCase() !== HiPS.obs_regime.toLowerCase()) {
return false;
}
}
if (params.spatial) {
if (!HiPS.ID)
return false;
if (Array.isArray(params.spatial) && !(params.spatial.includes(HiPS.ID))) {
return false;
}
}
if (params.resolution) {
if (!HiPS.hips_tile_width || !HiPS.hips_order) {
return false;
}
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;
};
this.filter = filter;
filterEnabler.action({target: {checked: true}});
this._addListeners();
this._requestMOCServer();
}
_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.isHidden && this.searchTree) {
return;
}
let self = this;
MocServer.getAllHiPSesInsideView(this.aladin)
.then((HiPSes) => {
let HiPSIDs = HiPSes.map((x) => x.ID);
self.searchTree.highlightNodes(HiPSIDs)
})
}
_addHiPS(id, name) {
let self = this;
self.searchDropdown.update({value: name, title: name});
let hips = A.imageHiPS(id, {
name,
successCallback: (hips) => {
@@ -414,8 +484,8 @@ export class HiPSBrowserBox extends Box {
let HiPS = HiPSBrowserBox.HiPSList[key];
// apply filtering
if (
self.filterCallback &&
self.filterCallback(HiPS, params)
self.filter &&
self.filter(HiPS, params)
) {
// search with the name or id
let name = HiPS.obs_title;
@@ -425,14 +495,15 @@ export class HiPSBrowserBox extends Box {
}
}
if (self.searchTree) {
self.searchTree.triggerFilter(params);
}
self.searchDropdown.update({ options: HiPSIDs });
self.filterNumberElt.innerHTML = HiPSIDs.length + "/" + Object.keys(HiPSBrowserBox.HiPSList).length;
}
_hide() {
if (this.filterBox)
this.filterBox.signalBrowserStatus(true)
if (this.filterBtn && this.filterBtn.toggled) {
this.filterBtn.toggle();
}
@@ -443,10 +514,6 @@ export class HiPSBrowserBox extends Box {
_show(options) {
// Regenerate a new layer name
this.layer = (options && options.layer) || Utils.uuidv4();
if (this.filterBox)
this.filterBox.signalBrowserStatus(false)
super._show(options)
}
}

View File

@@ -19,17 +19,11 @@
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";
import freqIconUrl from '../../../../assets/icons/freq.svg';
import inViewIconUrl from '../../../../assets/icons/inside.svg';
import targetIconUrl from '../../../../assets/icons/target.svg';
/******************************************************************************
* Aladin Lite project
@@ -45,120 +39,96 @@ export class HiPSFilterBox extends Box {
constructor(aladin, options) {
let self;
let regimeBtn = new TogglerActionButton({
content: 'Freq',
icon: {
monochrome: true,
size: 'medium',
url: freqIconUrl,
},
tooltip: {content: 'Observation bandwidth', position: {direction: 'bottom'}},
toggled: false,
actionOn: () => {
self._triggerFilteringCallback();
},
actionOff: () => {
let regimeBtn = Input.checkbox({
name: 'Freq',
tooltip: {content: 'Observation bandwidth', position: {direction: 'left'}},
type: 'checkbox',
checked: false,
click(e) {
self._triggerFilteringCallback();
}
});
let spatialBtn = new TogglerActionButton({
content: 'In view',
icon: {
monochrome: true,
size: 'medium',
url: inViewIconUrl,
},
tooltip: {content: 'Survey in view only!', position: {direction: 'bottom'}},
toggled: false,
actionOn: () => {
self._requestMOCServer();
},
actionOff: () => {
self._triggerFilteringCallback();
}
});
let resolutionBtn = new TogglerActionButton({
content: 'Resolution',
icon: {
monochrome: true,
size: 'medium',
url: targetIconUrl,
},
tooltip: {content: 'Check for HiPS with a specific pixel resolution.', position: {direction: 'bottom'}},
toggled: false,
actionOn: () => {
self._triggerFilteringCallback();
},
actionOff: () => {
let resolutionBtn = Input.checkbox({
name: 'Resolution',
tooltip: {content: 'Check for HiPS with a specific pixel resolution.', position: {direction: 'left'}},
type: 'checkbox',
checked: false,
click(e) {
self._triggerFilteringCallback();
}
});
let logSlider = new Input({
let regimeOption = Layout.horizontal({
tooltip: {
content: "Observation regime",
position: { direction: "right" },
},
label: 'Freq: ',
layout: [Input.select({
value: "Optical",
options: [
"Radio",
"Infrared",
"Millimeter",
"Optical",
"UV",
"EUV",
"X-ray",
"Gamma-ray",
],
change: (e) => {
let regime = e.target.value;
self.params["regime"] = regime;
self._triggerFilteringCallback();
},
}), regimeBtn]
});
let resolutionOption = Layout.horizontal({
label: "Max resolution [°/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'}}});
layout: [
new Input({
name: "res",
value: 0.1,
type: 'range',
cssStyle: {
width: '200px'
},
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();
let resolution = new Angle(deg);
self.params["resolution"] = resolution.degrees();
self._triggerFilteringCallback();
},
self._triggerFilteringCallback();
},
}),
resolutionBtn,
]
});
super(
{
classList: ['aladin-HiPS-filter-box'],
header: {
title: 'Filter tags',
draggable: false,
},
close: false,
classList: ['aladin-HiPS-filter-box'],
content: Layout.vertical([
'<b>Filter by:</b>',
Layout.horizontal([regimeBtn, spatialBtn, resolutionBtn]),
'<b>Details:</b>',
new Form({
subInputs: [
{
type: "group",
subInputs: [
{
label: "Freq:",
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
regimeOption,
resolutionOption
],
},
],
@@ -170,60 +140,28 @@ export class HiPSFilterBox extends Box {
self = this;
this.browserClosed = false;
this.callback = options.callback;
this.regimeBtn = regimeBtn;
this.spatialBtn = spatialBtn;
this.resolutionBtn = resolutionBtn;
this.params = {
regime: "Optical",
spatial: true,
highlight: 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) {
if (this.regimeBtn.checked) {
filterParams['regime'] = this.params['regime']
}
if (this.spatialBtn.toggled) {
filterParams['spatial'] = this.params['spatial']
}
if (this.resolutionBtn.toggled) {
if (this.resolutionBtn.checked) {
filterParams['resolution'] = this.params['resolution']
}
@@ -232,21 +170,18 @@ export class HiPSFilterBox extends Box {
}
}
signalBrowserStatus(closed) {
/*signalBrowserStatus(closed) {
this.browserClosed = closed;
// open
if (!closed) {
this._requestMOCServer()
}
}
}*/
enable(enable) {
this.on = enable;
if (this.on)
this._requestMOCServer();
this._triggerFilteringCallback();
}
}

View File

@@ -1115,6 +1115,7 @@ export class OverlayStackBox extends Box {
let item = Layout.horizontal({
layout: [HiPSSelector, Layout.horizontal(btns)],
cssStyle: {'padding': '10px 0px'}
});
layout.push(item);
@@ -1129,7 +1130,18 @@ export class OverlayStackBox extends Box {
}
}
return layout;
return Layout.vertical({
layout,
draggable: (a, b) => {
let i1 = layout.findIndex((x) => x.element() === a);
let i2 = layout.findIndex((x) => x.element() === b);
let h1 = layers[i1];
let h2 = layers[i2];
self.aladin.view.swapLayers(h1.layer, h2.layer);
}
});
}
_addOverlayIcon(overlay) {

View File

@@ -79,7 +79,7 @@ export class Dropdown extends Input {
super({
type: 'text',
autocomplete: {options: options.options},
//autocomplete: {options: options.options},
...options
})
this.el.classList.add('search')
@@ -91,11 +91,11 @@ export class Dropdown extends Input {
update(options) {
let newOptions = {};
if (options && options.options) {
/*if (options && options.options) {
newOptions['autocomplete'] = {options: options.options};
delete options.options;
}
}*/
// add the other input text options
newOptions = {...newOptions, ...options};

View File

@@ -67,6 +67,47 @@ export class Layout extends DOMElement {
this.appendContent(item)
}
}
if (options.draggable) {
// retrieve the children and add the drag listeners
let draggableFn = options.draggable;
let firstSelected = null;
this.el.childNodes.forEach(div => {
div.addEventListener("click", () => {
// If nothing selected yet → select this one
if (!firstSelected) {
firstSelected = div;
div.classList.add("aladin-item-selected");
return;
}
// If clicking the same one again → unselect
if (firstSelected === div) {
div.classList.remove("aladin-item-selected");
firstSelected = null;
return;
}
// Otherwise: swap the two elements
let a = firstSelected;
let b = div;
let temp = document.createElement("div");
a.parentNode.insertBefore(temp, a);
b.parentNode.insertBefore(a, b);
temp.parentNode.insertBefore(b, temp);
temp.remove();
// Exec callback
draggableFn(a, b)
// Clear selection
a.classList.remove("aladin-item-selected");
firstSelected = null;
});
});
}
}
// The tooltip has to be set once the element
@@ -182,9 +223,5 @@ export class Layout extends DOMElement {
if (this.options.position) {
this.setPosition(this.options.position)
}
//super._show()
// attach to the DOM again
//this.attachTo(this.target);
}
}
}

View File

@@ -137,11 +137,7 @@ export class Box extends DOMElement {
if (this.options.content) {
let content = this.options.content
//if (Array.isArray(content)) {
this.appendContent(content);
//} else {
// this.appendContent(content);
//}
this.appendContent(content);
}
if (this.options.position) {

View File

@@ -74,12 +74,17 @@ export class Input extends DOMElement {
this.el.type = this.type;
this.el.checked = this.options.checked;
this.checked = this.options.checked;
// for checkbox widgets, we authorize calling the callback name click or change
let action = this.options.click || this.options.change;
if (action) {
this.el.removeEventListener('click', this.action);
this.action = action;
this.action = (e) => {
this.checked = this.el.checked;
action(e);
};
this.el.addEventListener('click', this.action);
}
} else if (this.type === "select") {
@@ -374,6 +379,7 @@ export class Input extends DOMElement {
set(value) {
if (this.el.type === "checkbox") {
this.el.checked = value;
this.checked = value;
} else {
this.el.value = value;
}

313
src/js/gui/Widgets/Tree.js Normal file
View File

@@ -0,0 +1,313 @@
// Copyright 2023 - UDS/CNRS
// The Aladin Lite program is distributed under the terms
// of the GNU General Public License version 3.
//
// This file is part of Aladin Lite.
//
// Aladin Lite is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, version 3 of the License.
//
// Aladin Lite is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// The GNU General Public License is available in COPYING file
// along with Aladin Lite.
//
import { DOMElement } from "./Widget";
import { Icon } from "./Icon";
import folderIconUrl from "../../../../assets/icons/folder.svg";
/******************************************************************************
* Aladin Lite project
*
* File gui/Tree.js
*
* A tree
*
* Author: Matthieu Baumann[CDS]
*
*****************************************************************************
*/
export class Tree extends DOMElement {
constructor(options, target, position = "beforeend") {
let el = document.createElement("div");
el.classList.add('aladin-tree');
super(el, options);
this.click = options && options.click;
let rootNode = options && options.root || {};
this.params = null;
this.filter = options && options.filter;
this.label = options && options.label;
this._setRoot(rootNode);
this.attachTo(target, position);
this._show();
this.addClass('aladin-dark-theme')
}
_setRoot(root) {
if (root) {
root.label = new Icon({
size: "small",
url: folderIconUrl,
monochrome: true,
cssStyle: {'display': 'inline-block'}
}).element().outerHTML;
}
this.root = root;
this._createDOM(root);
}
_createDOM(node) {
if (!node) {
return;
}
this.el.innerHTML = "";
this.curNode = node;
// create the parent directory
let curNode = this.curNode;
let directoryLinks = []
var levelsOfParenty = 0;
while (curNode) {
let parentLinkEl = document.createElement('a');
parentLinkEl.innerHTML = curNode.label;
let curLevel = levelsOfParenty;
parentLinkEl.addEventListener('click', () => {
this.navigate(curLevel)
})
levelsOfParenty += 1;
parentLinkEl.classList.add("aladin-link");
directoryLinks.push(parentLinkEl);
curNode = curNode.parent;
}
directoryLinks.reverse()
let directoryListEl = document.createElement('div');
directoryListEl.classList.add('aladin-directory-path');
directoryListEl.style.display = "inline-block"
for (var link of directoryLinks) {
directoryListEl.appendChild(link);
// root node
let spanSplitEl = document.createElement('span')
spanSplitEl.innerText = ' \/ '
directoryListEl.appendChild(spanSplitEl)
}
this.el.appendChild(directoryListEl)
let listElt = document.createElement('ul');
for (const label of Object.keys(node).sort()) {
if (label !== 'parent' && label !== "label") {
let elt = document.createElement('li');
// points towards the parent node
let child = node[label];
let isLeaf = typeof child === "object" && 'ID' in child;
if (isLeaf) {
if(this.params && this.filter && !this.filter(child, this.params)) {
elt.style.display = "none";
} else {
elt.style.display = "block";
}
elt.innerHTML = this.label(child);
} else {
// we see a parent, we must determine:
// * its color: he has at least 1 child inside the FoV => green
// * the number of children matching the filter params
let numFilteringMatching = this.numChildMatchingFilter(child, true);
let numTotal = this.numChildMatchingFilter(child, false);
elt.innerHTML = label + ` (${numFilteringMatching}/${numTotal})`
}
elt.style.color = this.hasChildLocatedInFov(child) ? 'green' : 'orange';
child.label = label;
child.parent = node;
elt.for = label;
elt.classList.add("aladin-link");
elt.addEventListener('click', (e) => {
if (isLeaf) {
this.click(child)
} else {
// not leaf
this._createDOM(child);
}
})
listElt.appendChild(elt)
}
}
this.el.appendChild(listElt);
}
setHierarchy(root) {
this._setRoot(root)
}
navigate(numOfLevels) {
let curNode = this.curNode;
while (curNode && curNode.parent && numOfLevels >= 1) {
numOfLevels -= 1;
curNode = curNode.parent;
}
this._createDOM(curNode)
}
highlightNodes(highlight) {
this.highlight = highlight
let elts = this.el.querySelectorAll("li");
let i = 0;
for (const label of Object.keys(this.curNode).sort()) {
if (label !== 'parent' && label !== "label") {
let elt = elts[i];
i += 1;
// points towards the parent node
let child = this.curNode[label];
let isLeaf = typeof child === "object" && 'ID' in child;
if (isLeaf) {
// Check if its ID is found in the view
if (this.highlight) {
elt.style.color = this.highlight.includes(child.ID) ? 'green' : 'orange';
}
} else {
// we see a parent, we must determine:
// * its color: he has at least 1 child inside the FoV => green
// * the number of children matching the filter params
elt.style.color = this.hasChildLocatedInFov(child) ? 'green' : 'orange';
// we see a parent, we must determine:
// * its color: he has at least 1 child inside the FoV => green
// * the number of children matching the filter params
let numFilteringMatching = this.numChildMatchingFilter(child, true);
let numTotal = this.numChildMatchingFilter(child, false);
let name = elt.innerText.split('(');
elt.innerHTML = name[0] + ` (${numFilteringMatching}/${numTotal})`
}
}
}
}
// Set params to null, undefined or {} to disable the filtering
triggerFilter(params) {
if (params && params.title) {
params.title = params.title.toLowerCase()
}
this.params = params;
let elts = this.el.querySelectorAll("li");
let i = 0;
for (const label of Object.keys(this.curNode).sort()) {
if (label !== 'parent' && label !== "label") {
let elt = elts[i];
i += 1;
// points towards the parent node
let child = this.curNode[label];
let isLeaf = typeof child === "object" && 'ID' in child;
if (isLeaf) {
if(this.params && this.filter && !this.filter(child, this.params)) {
elt.style.display = "none"
} else {
elt.style.display = "block"
}
} else {
// we see a parent, we must determine:
// * its color: he has at least 1 child inside the FoV => green
// * the number of children matching the filter params
let numFilteringMatching = this.numChildMatchingFilter(child, true);
let numTotal = this.numChildMatchingFilter(child, false);
let name = elt.innerText.split('(');
elt.innerHTML = name[0] + ` (${numFilteringMatching}/${numTotal})`
}
}
}
}
hasChildLocatedInFov(node) {
if (!this.highlight) {
return false;
}
if (typeof node !== "object") {
return false;
}
let isLeaf = typeof node === "object" && 'ID' in node;
if (isLeaf) {
if (this.highlight.includes(node.ID)) {
return true;
}
} else {
for (const label of Object.keys(node).sort()) {
if (label === "parent")
continue;
let child = node[label];
if (child && this.hasChildLocatedInFov(child)) {
return true;
}
}
}
return false;
}
numChildMatchingFilter(node, filtering) {
if (typeof node !== "object") {
return 0;
}
let isLeaf = typeof node === "object" && 'ID' in node;
if (isLeaf) {
if (!filtering || (this.params && this.filter && this.filter(node, this.params))) {
return 1;
} else {
return 0;
}
} else {
let num = 0;
for (const label of Object.keys(node).sort()) {
if (label === "parent")
continue;
let child = node[label];
if (child) {
num += this.numChildMatchingFilter(child, filtering);
}
}
return num;
}
}
}

View File

@@ -80,7 +80,7 @@ export class SAMPConnector {
let params = message["samp.params"];
const {url, name} = params;
const image = aladin.createImageFITS(url, {name}, (e) => window.alert(e));
const image = aladin.createImageFITS(url, {name}, undefined, (e) => window.alert(e));
aladin.setOverlayImageLayer(image, name);
};
@@ -92,6 +92,8 @@ export class SAMPConnector {
let url = params['url'];
let name = params['name'] || id;
console.log(id, url, name)
A.catalogFromURL(
url,
{name, onClick: 'showTable'},
@@ -99,7 +101,7 @@ export class SAMPConnector {
(catalog) => {
aladin.addCatalog(catalog)
},
(e) => window.alert(e)
(e) => window.alert(e),
);
};