fix polygonal selection #339

This commit is contained in:
Matthieu Baumann
2026-01-22 11:29:50 +01:00
committed by Matthieu Baumann
parent 1894f908f4
commit c50fdb7430
16 changed files with 5663 additions and 5710 deletions
+33 -16
View File
@@ -19,6 +19,7 @@
<meta name="viewport" content="viewport-fit=cover, width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no">
</head>
<body>
<script type="text/javascript" src="https://aladin.cds.unistra.fr/AladinLite/api/v3/latest/aladin.js" charset="utf-8"></script>
<script src="https://code.jquery.com/jquery-1.10.1.min.js"></script>
<script type="text/javascript">
@@ -38,8 +39,8 @@
<!-- fin temporaire gestion cercle -->
<b>Orientation</b><br>
<button id="hips-coronelli" class="pure-button" name="ref-hips" onclick="longitudeReversed = false; aladin.reverseLongitude(longitudeReversed)">Normal</button><br>
<button id="hips-illenoroc" class="pure-button" name="ref-hips" onclick="longitudeReversed = true; aladin.reverseLongitude(longitudeReversed)">Inversé</button>
<button id="hips-coronelli" class="pure-button" name="ref-hips" onclick="aladin.setImageSurvey('Coronelli');">Normal</button><br>
<button id="hips-illenoroc" class="pure-button" name="ref-hips" onclick="aladin.setImageSurvey('illenoroC');">Inversé</button>
<br><br>
<b>Constellations</b>
@@ -75,6 +76,13 @@
<a href="#"><img id="coronelli-stars-red" class="catcoro coro-star" src="star_red.png"></a>
<a href="#"><img id="coronelli-stars-blue" class="catcoro coro-star" src="star_blue.png"></a>
</div>
<div>Coronelli2<br>
<!--a id="coronelli-stars" class="pure-button catcoro" href="#">Coronelli</a-->
<a href="#"><img id="coronelli-stars-white" class="catcoro coro-star" src="star_white.png"></a>
<a href="#"><img id="coronelli-stars-yellow" class="catcoro coro-star" src="star_yellow.png"></a><br>
<a href="#"><img id="coronelli-stars-red" class="catcoro coro-star" src="star_red.png"></a>
<a href="#"><img id="coronelli-stars-blue" class="catcoro coro-star" src="star_blue.png"></a>
</div>
<br><br>
<b>Navigation</b>&nbsp;&nbsp;<br><button id="stop">Stop</button>
@@ -99,6 +107,14 @@
<a class="pure-button nav-button nav-goto" href="#">Halley</a><br>
&nbsp;&nbsp;<a class="pure-button nav-button nav-flyto" href="#">Move</a>
</div>
<div id="coo_halley">
<a class="pure-button nav-button nav-goto" href="#">Halley</a><br>
&nbsp;&nbsp;<a class="pure-button nav-button nav-flyto" href="#">Move</a>
</div>
<div id="coo_halley">
<a class="pure-button nav-button nav-goto" href="#">Halley</a><br>
&nbsp;&nbsp;<a class="pure-button nav-button nav-flyto" href="#">Move</a>
</div>
</div>
<style type="text/css"> .aladin-reticleColor { color: rgb(178, 50, 178); font-weight:bold;} </style>
@@ -226,15 +242,16 @@
}
</style>
<script type="module">
import A from '../src/js/A.js';
import {Utils} from '../src/js/Utils';
<script type="text/javascript">
//import A from '../src/js/A.js';
//import {Utils} from '../src/js/Utils';
A.init.then(() => {
var hipsDir="http://alasky.u-strasbg.fr/CDS_P_Coronelli";
aladin = A.aladin("#aladin-lite-div", {showSimbadPointerControl: true, expandLayersControl: true, realFullscreen: true, fov: 100, allowFullZoomout: true, showReticle: false });
aladin.createImageSurvey('Coronelli', 'Coronelli', hipsDir, 'equatorial', 4, {imgFormat: 'jpg'});
aladin.setImageSurvey('Coronelli');
var hipsDir="http://alasky.cds.unistra.fr/CDS_P_Coronelli";
aladin = A.aladin("#aladin-lite-div", {showSimbadPointerControl: true, lockNorthUp: true, realFullscreen: true, fov: 100, allowFullZoomout: true, showReticle: false });
aladin.createImageSurvey('Coronelli', 'Coronelli', hipsDir, 'equatorial', 4, {imgFormat: 'jpg', longitudeReversed: true, minOrder: 3});
aladin.createImageSurvey('illenoroC', 'illenoroC', hipsDir, 'equatorial', 4, {imgFormat: 'jpg', longitudeReversed: false, minOrder: 3});
aladin.setImageSurvey('illenoroC');
$('#layersControlLeft').show();
$('#layersCL2').show();
@@ -344,14 +361,14 @@
// listen click on navigation buttons
$('.nav-button').click(function() {
var cooTarget = $(this).parent().attr('id');
var cooTarget = $(this).parent().attr('id');
if ($(this).hasClass("nav-goto")) {
aladin.gotoRaDec(cooNav[cooTarget].ra, cooNav[cooTarget].dec);
}
else if ($(this).hasClass("nav-flyto")) {
aladin.animateToRaDec(cooNav[cooTarget].ra, cooNav[cooTarget].dec, cooNav[cooTarget].time);
}
if ($(this).hasClass("nav-goto")) {
aladin.gotoRaDec(cooNav[cooTarget].ra, cooNav[cooTarget].dec);
}
else if ($(this).hasClass("nav-flyto")) {
aladin.animateToRaDec(cooNav[cooTarget].ra, cooNav[cooTarget].dec, cooNav[cooTarget].time);
}
});
// stop animations
+5180 -5180
View File
File diff suppressed because it is too large Load Diff
+16 -7
View File
@@ -580,6 +580,10 @@
color: var(--error-color);
}
.aladin-not-found {
color: darkorange;
}
.aladin-button:hover {
color: #ffffff;
background-color: #3276b1;
@@ -962,7 +966,7 @@ otherwise it fits its content options. If those are too big the select can go ou
-webkit-appearance: none;
outline-style: none;
padding: 0;
border: none;
border: var(--border-size) solid var(--border-color);
border-radius: 0.4rem;
width: 2rem;
height: 1.5rem;
@@ -971,13 +975,11 @@ otherwise it fits its content options. If those are too big the select can go ou
.aladin-input-color::-webkit-color-swatch {
border: none;
border-radius: 0.4rem;
padding: 0;
}
.aladin-input-color::-webkit-color-swatch-wrapper {
border: none;
border-radius: 0.4rem;
padding: 0;
}
@@ -1159,8 +1161,8 @@ otherwise it fits its content options. If those are too big the select can go ou
vertical-align: middle;
}
.aladin-indicator.aladin-not-valid {
background-color: var(--error-color);
.aladin-indicator.aladin-not-found {
background-color: darkorange;
}
.aladin-indicator.aladin-valid {
@@ -1204,6 +1206,7 @@ otherwise it fits its content options. If those are too big the select can go ou
.aladin-stack-box {
min-width: 17rem;
max-width: 300px;
}
.aladin-item-selected {
border: var(--border-size) solid orange;
@@ -1221,11 +1224,11 @@ otherwise it fits its content options. If those are too big the select can go ou
.aladin-HiPS-browser-box {
height: 500px;
min-width: 300px;
min-width: 400px;
}
.aladin-cat-browser-box {
width: 300px;
width: 400px;
}
.aladin-HiPS-browser-box .aladin-input-text {
@@ -1306,6 +1309,12 @@ otherwise it fits its content options. If those are too big the select can go ou
bottom: 0;
left: 50%;
transform: translate(-50%, 0);
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
max-width: 50vw;
}
.aladin-status-bar{
-29
View File
@@ -209,35 +209,6 @@ export let Footprint= (function() {
};
Footprint.prototype.intersectsBBox = function(x, y, w, h, view) {
/*if(this.source) {
let s = this.source;
if (!s.isShowing) {
return false;
}
let c = null;
if (s.x && s.y) {
c = {
x: s.x,
y: s.y,
};
} else {
var xy = view.aladin.world2pix(s.ra, s.dec);
if (!xy) {
return false;
}
c = {
x: xy[0],
y: xy[1],
};
}
if (c.x >= x && c.x <= x + w && c.y >= y && c.y <= y + h) {
return true;
}
}*/
return this.shapes.some((shape) => shape.intersectsBBox(x, y, w, h, view));
};
+2 -2
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,client_category,dataproduct_subtype,hips_service_url,hips_initial_ra,hips_initial_dec,hips_initial_fov",
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,em_min,em_max",
};
this._allHiPSes = Utils.loadFromUrls(MocServer.MIRRORS_HTTPS, {
@@ -96,7 +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,dataproduct_subtype,hips_service_url,hips_initial_ra,hips_initial_dec,hips_initial_fov",
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,em_min,em_max",
};
return Utils.loadFromUrls(MocServer.MIRRORS_HTTPS, {
+4 -1
View File
@@ -93,7 +93,7 @@ export let View = (function () {
// 1. Print the original exception message in the console
//console.error(e)
// 2. Add a more explicite message to the end user
console.error("Problem initializing Aladin Lite. Please contact the support by contacting Matthieu Baumann (baumannmatthieu0@gmail.com) or Thomas Boch (thomas.boch@astro.unistra.fr). You can also open an issue on the Aladin Lite github repository here: https://github.com/cds-astro/aladin-lite. Message error:" + e)
console.error("Problem initializing Aladin Lite. Please contact the support by contacting Matthieu Baumann (matthieu.baumann@astro.unistra.fr) or Thomas Boch (thomas.boch@astro.unistra.fr). You can also open an issue on the Aladin Lite github repository here: https://github.com/cds-astro/aladin-lite. Message error:" + e)
}
this._defineProperties();
@@ -595,6 +595,9 @@ export let View = (function () {
return;
}
if (this.spectraDisplayer)
this.spectraDisplayer.hide();
if (imageLayer.dataproductType === "spectral-cube") {
if (!this.spectraDisplayer) {
this.spectraDisplayer = new SpectraDisplayer(this, {width: 800, height: 300});
+18 -7
View File
@@ -110,10 +110,13 @@ export class HiPSBrowserBox extends Box {
}
if (params.title) {
if (!item.obs_title)
if (!item.obs_title && !item.ID)
return false;
if (!item.obs_title.toLowerCase().includes(params.title.toLowerCase())) {
let obsTitleDoesNotMatch = !item.obs_title.toLowerCase().includes(params.title.toLowerCase());
let creatorDidDoesNotMatch = !item.ID.toLowerCase().includes(params.title.toLowerCase());
if (obsTitleDoesNotMatch && creatorDidDoesNotMatch) {
return false;
}
}
@@ -134,6 +137,13 @@ export class HiPSBrowserBox extends Box {
let name = item.obs_title || item.ID;
self._addHiPS(image, name)
},
dblclick: (item) => {
let image = item.ID || item.hips_service_url;
let name = item.obs_title || item.ID;
self._addHiPS(image, name)
self.close();
},
// a callback called for filtering
filter,
aladin,
@@ -168,7 +178,6 @@ export class HiPSBrowserBox extends Box {
});
self.searchDropdown.update({ options: HiPSIDs });
self.searchTree.setHierarchy(hipsHierarchy)
// Initialize the autocompletion without any filtering
@@ -281,7 +290,7 @@ export class HiPSBrowserBox extends Box {
tooltip: {
global: true,
aladin,
content: 'red: out of the view, green: in view'
content: 'orange: out of the view, green: in view'
},
header: {
title: [
@@ -296,7 +305,7 @@ export class HiPSBrowserBox extends Box {
url: helpIconUrl,
monochrome: true,
tooltip: {
content: 'HiPS:<br/><span class="aladin-indicator aladin-not-valid"></span> out of the view<br /><span class="aladin-indicator aladin-valid"></span> in view',
content: 'HiPS:<br/><span class="aladin-indicator aladin-not-found"></span> out of the view<br /><span class="aladin-indicator aladin-valid"></span> in view',
mouse: true,
aladin
},
@@ -485,7 +494,8 @@ export class HiPSBrowserBox extends Box {
self.searchDropdown.addClass('aladin-not-valid');
}
});
this.aladin.setOverlayImageLayer(hips, self.layer);
self.selected(hips)
}
// This method is executed only if the filter is enabled
@@ -525,7 +535,8 @@ export class HiPSBrowserBox extends Box {
_show(options) {
// Regenerate a new layer name
this.layer = (options && options.layer) || Utils.uuidv4();
this.selected = options && options.selected;
super._show(options)
this._requestMOCServer();
+174 -241
View File
@@ -17,19 +17,17 @@
// 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 addIconUrl from "../../../../assets/icons/plus.svg";
import settingsIconUrl from "../../../../assets/icons/settings.svg";
import { Icon } from "../Widgets/Icon.js";
import { HiPSSelector } from "../Input/HiPSSelector.js";
import { HiPSBrowserBox } from "./HiPSBrowserBox.js";
import { Input } from "../Widgets/Input.js";
import { ActionButton } from "../Widgets/ActionButton.js";
import { Form } from "../Widgets/Form.js";
import { TogglerActionButton } from "../Button/Toggler.js";
/******************************************************************************
* Aladin Lite project
*
@@ -41,273 +39,208 @@ import { Icon } from "../Widgets/Icon.js";
*
*****************************************************************************/
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",
let nameInput = Input.text({
tooltip: {
global: true,
aladin,
content: 'HiPS url, ID or keyword accepted',
content: 'What name for your composite survey?'
},
placeholder: "What name?...",
autocomplete: 'off',
autofocus: true,
actions: {
dblclick: (_) => {
nameInput.set('')
},
keydown: (e) => {
e.stopPropagation();
//
}
},
action: (e) => {}
});
let infoCurrentHiPSBtn = new ActionButton({
disable: true,
icon: {
size: 'medium',
monochrome: true,
url: infoIconUrl,
},
tooltip: {
global: true,
aladin,
content: "More about that survey?"
}
});
let content = [[
new ActionButton({
icon: {
url: addIconUrl,
size: "small",
monochrome: true,
},
tooltip: {
content: "Add a new layer",
position: { direction: "top" },
},
toggled: false,
action(_) {
self.content.push(self._addNewHiPS());
self.update({content: self.content})
}
}),
nameInput
]];
super(
{
close: true,
header: {
title: Layout.horizontal([new Icon({
size: 'medium',
url: hipsIconUrl,
monochrome: true,
}), "HiPS Compositor"]),
title: [
new Icon({
size: 'medium',
url: hipsIconUrl,
monochrome: true,
}),
"HiPS Compositor"
],
draggable: true,
},
content: Layout.vertical([
[searchDropdown, infoCurrentHiPSBtn],
]),
content,
...options,
},
aladin.aladinDiv
);
self = this;
this.searchDropdown = searchDropdown;
this.aladin = aladin;
this.infoCurrentHiPSBtn = infoCurrentHiPSBtn;
self = this;
this._addListeners();
this.numHiPSLayers = 0;
this.content = content.concat([this._addNewHiPS()])
this.update({content: this.content})
this.openSettings = null;
}
_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 = '&nbsp;'.repeat(maxNumDigits - s.length)
if (paddingBegin) {
s = r + s
} else {
s += r
_addNewHiPS() {
let newLayerLayout = [
new HiPSSelector({
change(e) {
let name = e.target.value;
if (name === "More...") {
if (!aladin.hipsBrowser) {
aladin.hipsBrowser = new HiPSBrowserBox(aladin);
}
}
return s;
aladin.hipsBrowser._show({
selected: (hips) => {
console.log(hips)
},
position: { anchor: "center center" }
});
return;
}
}
}),
this._createLayerSettingsBox(),
ActionButton.BUTTONS(aladin)
.remove(
(e) => {
let node = e.target.parentElement.parentElement.parentElement;
let idLayer = [...node.parentElement.children].indexOf(node);
this.content.splice(idLayer, 1)
this.update({content: this.content})
this.numHiPSLayers = this.content.length - 1;
}
)
];
this.numHiPSLayers += 1;
return newLayerLayout;
}
_createLayerSettingsBox() {
let self = this;
let layerSettingsBox = new Box({
close: false,
content: new Form({
subInputs: [
{
type: 'color',
label: "Color",
value: 'red',
name: 'color',
change(e) {
let hex = e.target.value;
}
},
{
label: 'Stretch',
type: "select",
name: 'stretch',
value: 'linear',
options: ['sqrt', 'linear', 'asinh', 'pow2', 'log'],
change(e) {},
},
{
type: 'number',
label: "Min cut",
name: 'mincut',
value: 0.0,
change: (e) => {
let minCut = +e.target.value
}
},
{
label: 'Max cut',
type: "number",
name: 'maxcut',
value: 0.0,
change: (e) => {
let maxCut = +e.target.value
}
},
]
}),
}, this.aladin.aladinDiv);
layerSettingsBox._hide()
// catalog settings
let layerSettingsBtn = new TogglerActionButton({
icon: { url: settingsIconUrl, monochrome: true },
size: "small",
tooltip: {
content: "Settings",
position: { direction: "top" },
},
toggled: false,
actionOn: (_) => {
layerSettingsBox._show({
position: {
nextTo: layerSettingsBtn,
direction: "right",
aladin: self.aladin,
},
});
if (self.openSettings) {
self.openSettings.close();
}
let updateSlice = () => {
slicer.update({
value: idxSlice,
tooltip: {content: (idxSlice + 1) + '/' + numSlices, position: {direction: 'bottom'}},
})
hips.setSliceNumber(idxSlice)
cubeDisplayer.update({
position: cubeDisplayer.position,
content: [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)
self.openSettings = layerSettingsBtn;
},
actionOff: (_) => {
layerSettingsBox._hide();
if (self.openSettings === layerSettingsBtn) {
self.openSettings = null;
}
},
errorCallback: (e) => {
self.searchDropdown.removeClass('aladin-valid');
self.searchDropdown.addClass('aladin-not-valid');
}
});
this.aladin.setOverlayImageLayer(hips, self.layer);
return layerSettingsBtn
}
_show(options) {
// Regenerate a new layer name
this.layer = (options && options.layer) || Utils.uuidv4();
super._show(options)
_hide() {
if (this.openSettings)
this.openSettings.close();
super._hide()
}
}
-6
View File
@@ -209,9 +209,6 @@ import { TogglerActionButton } from "../Button/Toggler.js";
{
label: 'min cut:',
type: 'number',
cssStyle: {
width: '6rem',
},
tooltip: {content: 'Min cut', position: {direction: 'bottom'}},
name: 'mincut',
value: 0.0,
@@ -223,9 +220,6 @@ import { TogglerActionButton } from "../Button/Toggler.js";
{
type: 'number',
label: 'max cut:',
cssStyle: {
width: '6rem',
},
tooltip: {content: 'Max cut', position: {direction: 'bottom'}},
name: 'maxcut',
value: 1.0,
+72 -117
View File
@@ -49,13 +49,14 @@ import { TogglerActionButton } from "../Button/Toggler.js";
import { Icon } from "../Widgets/Icon.js";
import { Box } from "../Widgets/Box.js";
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"
import { Catalog } from "../../Catalog.js";
import { ProgressiveCat } from "../../ProgressiveCat.js";
import { Form } from "../Widgets/Form.js";
import { HiPSSelector } from "./../Input/HiPSSelector.js";
import { HiPS } from "../../HiPS.js";
export class OverlayStackBox extends Box {
/*static previewImagesUrl = {
@@ -191,8 +192,6 @@ export class OverlayStackBox extends Box {
o.stopPropagation();
o.preventDefault();
//self._hide();
const simbadHiPS = A.catalogHiPS(
OverlayStackBox.predefinedCats.simbad
.url,
@@ -208,8 +207,6 @@ export class OverlayStackBox extends Box {
o.stopPropagation();
o.preventDefault();
//self._hide();
const simbadHiPS = A.catalogHiPS(
OverlayStackBox.predefinedCats.gaia.url,
OverlayStackBox.predefinedCats.gaia
@@ -535,12 +532,19 @@ export class OverlayStackBox extends Box {
e.stopPropagation();
e.preventDefault();
if (!self.hipsBrowser)
self.hipsBrowser = new HiPSBrowserBox(aladin);
if (!aladin.hipsBrowser)
aladin.hipsBrowser = new HiPSBrowserBox(aladin);
self.hipsBrowser._show({position: {
anchor: 'center center'
}});
let newLayer = Utils.uuidv4();
aladin.hipsBrowser._show({
selected: (hips) => {
aladin.setOverlayImageLayer(hips, newLayer);
},
position: {
anchor: 'center center'
}
});
},
},
{
@@ -661,13 +665,6 @@ export class OverlayStackBox extends Box {
}
);
ALEvent.GRAPHIC_OVERLAY_LAYER_CHANGED.listenedBy(
this.aladin.aladinDiv,
function (e) {
updateOverlayList();
}
);
ALEvent.HIPS_LAYER_ADDED.listenedBy(
this.aladin.aladinDiv,
function (e) {
@@ -717,42 +714,6 @@ export class OverlayStackBox extends Box {
);
updateOverlayList();
// Add a listener for HiPS list changes
ALEvent.FAVORITE_HIPS_LIST_UPDATED.listenedBy(document.body, (event) => {
let favoritesHips = event.detail;
self.cachedHiPS = {};
for (var hips of favoritesHips) {
let key = hips.name || hips.id || hips.url;
self.cachedHiPS[key] = hips;
}
// Update the options of the selector
const favorites = Object.keys(self.cachedHiPS);
for (var key in self.ui) {
let ui = self.ui[key];
// refers to an HiPS image survey
if (ui.HiPSSelector) {
let currentHiPS = ui.HiPSSelector.options.value
let favoritesCopy = [...favorites];
// add the current hips to the selector as well, even if it has been manually
// removed from the HiPSList
if (favoritesCopy.indexOf(currentHiPS) < 0) {
favoritesCopy.push(currentHiPS)
}
// one must add the current HiPS too!
favoritesCopy.sort();
favoritesCopy.push("More...")
ui.HiPSSelector.update({value: currentHiPS, options: favoritesCopy});
}
}
});
}
_hide() {
@@ -780,7 +741,6 @@ export class OverlayStackBox extends Box {
let layout = [[this.addOverlayBtn, "Overlays"]];
layout = layout.concat(this._createOverlaysList());
layout.push(
[
this.addHiPSBtn,
@@ -790,7 +750,14 @@ export class OverlayStackBox extends Box {
],
);
layout = layout.concat(this._createSurveysList());
return Layout.vertical(layout);
return Layout.vertical(layout,
{
cssStyle: {
overflowWrap: "anywhere",
wordBreak: "break-word",
}
}
);
}
_createOverlaysList() {
@@ -955,20 +922,11 @@ export class OverlayStackBox extends Box {
}
}
optBtn.push(new ActionButton({
icon: {
url: removeIconUrl,
monochrome: true,
},
size: "small",
tooltip: {
content: "Remove",
position: { direction: "top" },
},
action(e) {
optBtn.push(ActionButton.BUTTONS(self.aladin).remove(
(e) => {
self.aladin.removeLayer(overlay);
},
}))
}
));
layout.push(
{
@@ -984,98 +942,95 @@ export class OverlayStackBox extends Box {
_createSurveysList() {
let self = this;
const layers = Array.from(self.aladin.getStackLayers())
let aladin = self.aladin;
const layers = Array.from(aladin.getStackLayers())
.reverse()
.map((name) => {
let overlay = self.aladin.getOverlayImageLayer(name);
let overlay = aladin.getOverlayImageLayer(name);
return overlay;
});
// survey list
let layout = [];
let hipsOptions = Object.keys(self.cachedHiPS);
hipsOptions.sort()
for (const layer of layers) {
if (!layer) {
for (const hips of layers) {
if (!hips) {
continue;
}
let options = Array.from([...hipsOptions])
let value = layer.name || layer.id
if (options.indexOf(value) < 0) {
options.push(value)
}
options.push("More...")
let HiPSSelector = Input.select({
value,
options,
title: layer.name,
change: (e) => {
let HiPSselect = new HiPSSelector({
layer: hips,
change(e) {
let name = e.target.value;
if (name === "More...") {
if (!self.hipsBrowser)
self.hipsBrowser = new HiPSBrowserBox(self.aladin);
if (!aladin.hipsBrowser) {
aladin.hipsBrowser = new HiPSBrowserBox(aladin);
}
self.hipsBrowser._show({ layer: layer.layer, position: { anchor: "center center" } });
aladin.hipsBrowser._show({
selected: (hips) => {
self.aladin.setOverlayImageLayer(hips, hips.layer);
},
position: { anchor: "center center" }
});
return;
}
// search for the
let overlayLayer;
if (name in self.cachedHiPS) {
// it is an hips
let HiPSOptions = self.cachedHiPS[name];
let overlayLayer;
if (name in HiPSSelector.cachedHiPS) {
// it is an hips
let HiPSOptions = HiPSSelector.cachedHiPS[name];
overlayLayer = A.HiPS(HiPSOptions.id || HiPSOptions.url, HiPSOptions);
} else {
overlayLayer = layer
overlayLayer = hips
}
self.aladin.setOverlayImageLayer(overlayLayer, layer.layer);
aladin.setOverlayImageLayer(overlayLayer, hips.layer);
}
});
let deleteBtn = ActionButton.createSmallSizedIconBtn({
icon: { url: removeIconUrl, monochrome: true },
//disable: layer.layer === "base",
tooltip: { content: "Remove", position: { direction: "top" } },
action(e) {
self.aladin.removeImageLayer(layer.layer);
aladin.removeImageLayer(hips.layer);
// remove HiPS cube player if any
self.aladin.removeUIByName("cube_displayer" + layer.layer)
aladin.removeUIByName("cube_displayer" + hips.layer)
if (hips instanceof HiPS && hips === aladin.view.spectraDisplayer.hips) {
aladin.view.spectraDisplayer.hide()
}
},
});
let prevOpacity = null;
let showBtn = ActionButton.createSmallSizedIconBtn({
icon: {
url: layer.getOpacity() === 0.0 ? hideIconUrl : showIconUrl,
url: hips.getOpacity() === 0.0 ? hideIconUrl : showIconUrl,
monochrome: true,
},
tooltip: {
content: layer.getOpacity() === 0.0 ? "Show" : "Hide",
content: hips.getOpacity() === 0.0 ? "Show" : "Hide",
position: { direction: "top" },
},
action(e, btn) {
e.preventDefault();
e.stopPropagation();
let opacity = layer.getOpacity();
let opacity = hips.getOpacity();
if (opacity === 0.0) {
let newOpacity = prevOpacity || 1.0;
prevOpacity = null;
layer.setOpacity(newOpacity);
hips.setOpacity(newOpacity);
btn.update({
icon: { monochrome: true, url: showIconUrl },
tooltip: { content: "Hide" },
});
} else {
prevOpacity = opacity;
layer.setOpacity(0.0);
hips.setOpacity(0.0);
btn.update({
icon: { monochrome: true, url: hideIconUrl },
tooltip: { content: "Show" },
@@ -1085,7 +1040,7 @@ export class OverlayStackBox extends Box {
});
let settingsBox = new HiPSSettingsBox(self.aladin);
settingsBox.update({ layer });
settingsBox.update({ layer: hips });
settingsBox._hide();
let settingsBtn = new TogglerActionButton({
@@ -1101,7 +1056,7 @@ export class OverlayStackBox extends Box {
for (var l in self.ui) {
let ui = self.ui[l]
if (l != layer.layer) {
if (l != hips.layer) {
ui.settingsBtn.close();
}
}
@@ -1121,8 +1076,8 @@ export class OverlayStackBox extends Box {
let loadMOCBtn = ActionButton.BUTTONS(self.aladin)
.addMOC({
name: layer.name,
url: layer.url + '/Moc.fits'
name: hips.name,
url: hips.url + '/Moc.fits'
});
self.layer2swap = null;
@@ -1142,9 +1097,9 @@ export class OverlayStackBox extends Box {
let toggled = swapBtn.options.toggled;
if (!toggled) {
if (!self.layer2swap) {
self.layer2swap = layer;
self.layer2swap = hips;
} else {
self.aladin.view.swapLayers(self.layer2swap.layer, layer.layer);
self.aladin.view.swapLayers(self.layer2swap.layer, hips.layer);
}
} else {
if (self.layer2swap) {
@@ -1160,17 +1115,17 @@ export class OverlayStackBox extends Box {
let btns = [showBtn, settingsBtn];
if (!(layer instanceof Image)) {
if (!(hips instanceof Image)) {
btns.push(loadMOCBtn);
}
btns = btns.concat([swapBtn, deleteBtn]);
let item = Layout.horizontal([HiPSSelector, Layout.horizontal(btns)]);
let item = Layout.horizontal([HiPSselect, Layout.horizontal(btns)]);
layout.push(item);
if (!(layer.layer in self.ui)) {
self.ui[layer.layer] = {
HiPSSelector,
if (!(hips.layer in self.ui)) {
self.ui[hips.layer] = {
HiPSSelector: HiPSselect,
settingsBox,
settingsBtn,
showBtn,
+1 -17
View File
@@ -117,23 +117,7 @@ export class StatusBarBox extends Box {
this.el.title = task.message;
// create message div
let message = Layout.horizontal(task.message,
{
tooltip: {
content: task.message,
position: {
direction: "top",
},
hoverable: true,
delayShowUpTime: '500ms',
cssStyle: {
fontSize: 'x-small',
maxWidth: "200px",
"overflow-wrap": "break-word",
}
},
}
);
let message = Layout.horizontal(task.message);
message.addClass("aladin-status-bar-message")
+15 -1
View File
@@ -23,7 +23,8 @@ import { Icon } from "./Icon";
import { Layout } from "../Layout";
import infoIconUrl from "../../../../assets/icons/info.svg"
import targetIconUrl from '../../../../assets/icons/target.svg';
import targetIconUrl from "../../../../assets/icons/target.svg";
import removeIconUrl from "../../../../assets/icons/remove.svg";
import A from "../../A";
/******************************************************************************
* Aladin Lite project
@@ -276,6 +277,19 @@ export class ActionButton extends DOMElement {
})
return button;
},
remove: (action) => {
return new ActionButton({
icon: {
url: removeIconUrl,
monochrome: true,
},
size: "small",
tooltip: {
content: "Remove",
},
action
})
}
}
}
+5 -1
View File
@@ -45,6 +45,10 @@ export class Box extends DOMElement {
this._show();
}
close() {
this._hide()
}
_show(options) {
this.options = {
...this.options,
@@ -143,7 +147,7 @@ export class Box extends DOMElement {
size: "small",
monochrome: true,
},
tooltip: {content: 'Enlarge the window', global: true, aladin},
tooltip: {content: 'Enlarge the window', global: true, aladin: this.aladin},
cssStyle: {
cursor: 'move',
position: 'absolute',
+50 -40
View File
@@ -23,6 +23,7 @@ import folderIconUrl from "../../../../assets/icons/folder.svg";
import { Layout } from "../Layout";
import { ActionButton } from "./ActionButton";
/******************************************************************************
* Aladin Lite project
*
@@ -42,6 +43,7 @@ export class Tree extends DOMElement {
super(el, options);
this.click = options && options.click;
this.dblclick = options && options.dblclick;
this.aladin = options && options.aladin;
let rootNode = options && options.root || {};
@@ -118,6 +120,31 @@ export class Tree extends DOMElement {
let listElt = document.createElement('ul');
let seekMinEmInNode = (n) => {
const isLeaf = typeof n === 'object' && 'ID' in n;
if (isLeaf) {
return (+n.em_min)
} else if (typeof n === 'object') {
let min = Number.POSITIVE_INFINITY;
for (var [key, child] of Object.entries(n)) {
if (key === "parent" || key === "label") {
continue;
}
let val = seekMinEmInNode(child);
if (Number.isFinite(val)) {
min = Math.min(val, min);
}
}
return min;
} else {
return Number.POSITIVE_INFINITY;
}
};
let labels = Object.keys(node).sort((la, lb) => {
let na = node[la];
let nb = node[lb];
@@ -127,10 +154,19 @@ export class Tree extends DOMElement {
if (aIsLeaf !== bIsLeaf) {
return aIsLeaf - bIsLeaf;
} else if (typeof na === "object" && typeof nb === "object") {
let emNa = seekMinEmInNode(na)
let emNb = seekMinEmInNode(nb)
if (emNa > emNb) {
return -1;
} else {
return 1;
}
} else if (la < lb) {
return -1
} else {
return 1;
return 1
}
});
@@ -196,7 +232,6 @@ export class Tree extends DOMElement {
.element(),
])
console.log(layout)
let childElt = new Layout(
layout,
{
@@ -207,18 +242,18 @@ export class Tree extends DOMElement {
`alt="${label}" />` +
`<figcaption>${label}</figcaption>` +
'</figure>',
delayShowUpTime: "100ms",
mouse: true,
aladin: this.aladin,
}
}).element();
if (this.highlight) {
if(this.highlight.includes(child.ID)) {
childElt.classList.add("aladin-valid");
childElt.classList.remove("aladin-not-valid");
childElt.classList.remove("aladin-not-found");
} else {
childElt.classList.remove("aladin-valid");
childElt.classList.add("aladin-not-valid");
childElt.classList.add("aladin-not-found");
}
}
elt.appendChild(childElt);
@@ -249,10 +284,10 @@ export class Tree extends DOMElement {
if(this.hasChildLocatedInFov(child)) {
elt.classList.add("aladin-valid");
elt.classList.remove("aladin-not-valid");
elt.classList.remove("aladin-not-found");
} else {
elt.classList.remove("aladin-valid");
elt.classList.add("aladin-not-valid");
elt.classList.add("aladin-not-found");
}
child.label = label;
@@ -269,6 +304,11 @@ export class Tree extends DOMElement {
this._createDOM(child);
}
})
elt.addEventListener('dblclick', (e) => {
if (isLeaf) {
this.dblclick(child)
}
})
listElt.appendChild(elt)
}
@@ -327,23 +367,8 @@ export class Tree extends DOMElement {
return true;
}
} else {
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")
for (const label of Object.keys(node)) {
if (label === "parent" || label === "label")
continue;
let child = node[label];
@@ -370,22 +395,7 @@ export class Tree extends DOMElement {
}
} else {
let num = 0;
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) {
for (const label of Object.keys(node)) {
if (label === "parent")
continue;
+92 -44
View File
@@ -456,51 +456,111 @@ export let Polyline = (function() {
return false;
};
Polyline.prototype.intersectsBBox = function(x, y, w, h, view) {
for (let i = 0; i < this.raDecArray.length - 1; i++) {
let p1 = this.raDecArray[i];
let p2 = this.raDecArray[i + 1];
Polyline.prototype.intersectsBBox = function (x, y, w, h, view) {
let n = this.raDecArray.length;
if (n < 2) return false;
let xy1 = view.aladin.world2pix(p1[0], p1[1]);
let xy2 = view.aladin.world2pix(p2[0], p2[1]);
// 2️⃣ Edge intersection test
let i = this.closed ? n - 1 : 0;
let j = this.closed ? 0 : 1;
let poly = [];
while (j < n) {
const p1 = this.raDecArray[i];
const p2 = this.raDecArray[j];
const xy1 = view.aladin.world2pix(p1[0], p1[1]);
const xy2 = view.aladin.world2pix(p2[0], p2[1]);
// Skip invalid segments, do NOT abort
if (!xy1 || !xy2) {
return false;
i = j++;
continue;
}
xy1 = {x: xy1[0], y: xy1[1]};
xy2 = {x: xy2[0], y: xy2[1]};
// Check if line segment intersects with the bounding box
if (this.lineIntersectsBox(xy1, xy2, x, y, w, h)) {
const a = { x: xy1[0], y: xy1[1] };
const b = { x: xy2[0], y: xy2[1] };
poly.push(a);
if (Polyline.segmentIntersectsBox(a, b, x, y, w, h)) {
return true;
}
i = j++;
}
if (this.closed && poly.length === this.raDecArray.length) {
console.log("closed poly")
const corners = [
{ x, y },
{ x: x + w, y },
{ x: x + w, y: y + h },
{ x, y: y + h }
];
for (const c of corners) {
if (Polyline.pointInPolygon(c, poly)) {
return true;
}
}
}
return false;
};
Polyline.prototype.lineIntersectsBox = function(p1, p2, x, y, w, h) {
// Check if line segment is completely outside the box
if ((p1.x < x && p2.x < x) ||
(p1.y < y && p2.y < y) ||
(p1.x > x + w && p2.x > x + w) ||
(p1.y > y + h && p2.y > y + h)) {
return false;
}
let m = (p2.y - p1.y) / (p2.x - p1.x); // Slope of the line
let c = p1.y - m * p1.x; // y-intercept of the line
Polyline.segmentIntersectsBox = function (p1, p2, x, y, w, h) {
const x2 = x + w;
const y2 = y + h;
// Check if line intersects with the sides of the box
if ((p1.y >= y && p1.y <= y + h) ||
(p2.y >= y && p2.y <= y + h) ||
(m * x + c >= y && m * x + c <= y + h) ||
(m * (x + w) + c >= y && m * (x + w) + c <= y + h)) {
// 1️⃣ Endpoint inside box
if (Polyline.pointInBox(p1, x, y, x2, y2) ||
Polyline.pointInBox(p2, x, y, x2, y2)) {
return true;
}
return false;
// 2️⃣ Check intersection with 4 box edges
return (
Polyline.segmentsIntersect(p1, p2, { x, y }, { x: x2, y }) || // top
Polyline.segmentsIntersect(p1, p2, { x: x2, y }, { x: x2, y: y2 }) || // right
Polyline.segmentsIntersect(p1, p2, { x: x2, y: y2 }, { x, y: y2 }) || // bottom
Polyline.segmentsIntersect(p1, p2, { x, y: y2 }, { x, y }) // left
);
};
Polyline.pointInBox = function (p, x1, y1, x2, y2) {
return p.x >= x1 && p.x <= x2 && p.y >= y1 && p.y <= y2;
};
Polyline.pointInPolygon = function (xy, poly) {
let inside = false;
for (let i = 0, j = poly.length - 1; i < poly.length; j = i++) {
const xi = poly[i].x, yi = poly[i].y;
const xj = poly[j].x, yj = poly[j].y;
const intersect =
((yi > xy.y) !== (yj > xy.y)) &&
(xy.x < (xj - xi) * (xy.y - yi) / (yj - yi) + xi);
if (intersect) inside = !inside;
}
return inside;
};
Polyline.segmentsIntersect = function (p1, p2, p3, p4) {
function orient(a, b, c) {
return (b.x - a.x) * (c.y - a.y) - (b.y - a.y) * (c.x - a.x);
}
const o1 = orient(p1, p2, p3);
const o2 = orient(p1, p2, p4);
const o3 = orient(p3, p4, p1);
const o4 = orient(p3, p4, p2);
return o1 * o2 < 0 && o3 * o4 < 0;
};
// static methods
@@ -516,10 +576,10 @@ export let Polyline = (function() {
// check if the line has hit any of the rectangle's sides
// uses the Line/Line function below
let left = Polyline._intersectLine(x1, y1, x2, y2, 0, 0, 0, rh);
let right = Polyline._intersectLine(x1, y1, x2, y2, rw, 0, rw, rh);
let top = Polyline._intersectLine(x1, y1, x2, y2, 0, 0, rw, 0);
let bottom = Polyline._intersectLine(x1, y1, x2, y2, 0, rh, rw, rh);
let left = Polyline.segmentsIntersect({x: x1, y: y1}, {x: x2, y: y2}, {x: 0, y: 0}, {x: 0, y: rh});
let right = Polyline.segmentsIntersect({x: x1, y: y1}, {x: x2, y: y2}, {x: rw, y: 0}, {x: rw, y: rh});
let top = Polyline.segmentsIntersect({x: x1, y: y1}, {x: x2, y: y2}, {x: 0, y: 0}, {x: rw, y: 0});
let bottom = Polyline.segmentsIntersect({x: x1, y: y1}, {x: x2, y: y2}, {x: 0, y: rh}, {x: rw, y: rh});
// if ANY of the above are true, the line
// has hit the rectangle
@@ -530,17 +590,5 @@ export let Polyline = (function() {
return false;
};
Polyline._intersectLine = function(x1, y1, x2, y2, x3, y3, x4, y4) {
// Calculate the direction of the lines
let uA = ((x4-x3)*(y1-y3) - (y4-y3)*(x1-x3)) / ((y4-y3)*(x2-x1) - (x4-x3)*(y2-y1));
let uB = ((x2-x1)*(y1-y3) - (y2-y1)*(x1-x3)) / ((y4-y3)*(x2-x1) - (x4-x3)*(y2-y1));
// If uA and uB are between 0-1, lines are colliding
if (uA >= 0 && uA <= 1 && uB >= 0 && uB <= 1) {
return true;
}
return false;
};
return Polyline;
})();
+1 -1
View File
@@ -193,7 +193,7 @@ export let Vector = (function() {
xy2 = {x: xy2[0], y: xy2[1]};
// Check if line segment intersects with the bounding box
if (this.lineIntersectsBox(xy1, xy2, x, y, w, h)) {
if (Polyline.segmentIntersectsBox(xy1, xy2, x, y, w, h)) {
return true;
}