Compare commits
11 Commits
gui-add-la
...
select-imp
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0c9c4cf9a9 | ||
|
|
ea8af8acb3 | ||
|
|
fac6c045f4 | ||
|
|
b778ce380a | ||
|
|
d3e0bb4fbc | ||
|
|
81e1eaddee | ||
|
|
51af4fa2f4 | ||
|
|
ef86dbd06d | ||
|
|
45f77feeb1 | ||
|
|
b8820d6f19 | ||
|
|
98d877b937 |
@@ -47,8 +47,6 @@ Editable examples showing the API can also be found [here](https://aladin.cds.un
|
||||
|
||||
## Embed it into your projects
|
||||
|
||||
**Terms of use**: you are welcome to integrate Aladin Lite in your web pages and to customize its GUI to your needs, but **please leave the Aladin logo and link intact** at the bottom right of the view.
|
||||
|
||||
You can embed Aladin Lite it into your webpages in two ways
|
||||
|
||||
### The vanilla way
|
||||
|
||||
|
Before Width: | Height: | Size: 60 KiB After Width: | Height: | Size: 1.4 MiB |
|
Before Width: | Height: | Size: 62 KiB After Width: | Height: | Size: 1.2 MiB |
|
Before Width: | Height: | Size: 88 KiB After Width: | Height: | Size: 1.9 MiB |
|
Before Width: | Height: | Size: 94 KiB After Width: | Height: | Size: 1.3 MiB |
|
Before Width: | Height: | Size: 44 KiB After Width: | Height: | Size: 962 KiB |
|
Before Width: | Height: | Size: 69 KiB After Width: | Height: | Size: 1.6 MiB |
@@ -1,8 +0,0 @@
|
||||
nav > h2 {
|
||||
color: blue;
|
||||
font-size: medium;
|
||||
}
|
||||
|
||||
nav > ul > li > a {
|
||||
font-size: medium;
|
||||
}
|
||||
57
jsdoc.json
@@ -10,64 +10,13 @@
|
||||
"allowUnknownTags": true,
|
||||
"dictionaries": ["jsdoc","closure"]
|
||||
},
|
||||
"docdash": {
|
||||
"scripts": [
|
||||
"jsdoc-custom-style.css"
|
||||
],
|
||||
"sectionOrder": [
|
||||
"Namespaces",
|
||||
"Classes",
|
||||
"Modules",
|
||||
"Externals",
|
||||
"Events",
|
||||
"Mixins",
|
||||
"Tutorials",
|
||||
"Interfaces"
|
||||
],
|
||||
"openGraph": {
|
||||
"title": "Aladin Lite API documentation",
|
||||
"type": "website",
|
||||
"image": "https://cds-astro.github.io/aladin-lite/aladin-logo.png",
|
||||
"site_name": "Aladin Lite API documentation",
|
||||
"url": "https://cds-astro.github.io/aladin-lite/"
|
||||
},
|
||||
"meta": {
|
||||
"title": "Aladin Lite API documentation",
|
||||
"description": "Aladin Lite API documentation",
|
||||
"keyword": "astronomy"
|
||||
},
|
||||
"search": true,
|
||||
"menu": {
|
||||
"Project Website": {
|
||||
"href":"https://aladin.cds.unistra.fr/AladinLite/doc/",
|
||||
"target":"_blank",
|
||||
"class":"menu-item",
|
||||
"id":"website_link"
|
||||
},
|
||||
"Forum": {
|
||||
"href":"https://github.com/cds-astro/aladin-lite/issues",
|
||||
"target":"_blank",
|
||||
"class":"menu-item",
|
||||
"id":"forum_link"
|
||||
}
|
||||
}
|
||||
},
|
||||
"templates": {
|
||||
"cleverLinks": true,
|
||||
"monospaceLinks": true,
|
||||
"default": {
|
||||
"staticFiles": {
|
||||
"include": [
|
||||
"./jsdoc-custom-style.css"
|
||||
]
|
||||
}
|
||||
}
|
||||
"monospaceLinks": true
|
||||
},
|
||||
"opts": {
|
||||
"readme": "./README.md",
|
||||
"destination": "./docs/",
|
||||
"template": "node_modules/docdash",
|
||||
"encoding": "utf8",
|
||||
"verbose": true
|
||||
"tutorials": "./tutorials"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -44,12 +44,11 @@
|
||||
"test:build": "cd src/core && cargo test --release --features webgl2",
|
||||
"test:playwright": "npx playwright test",
|
||||
"test:update-snapshots": "npx playwright test --update-snapshots",
|
||||
"doc": "jsdoc -c jsdoc.json src/js src/js/shapes src/js/libs/astro && cp aladin-logo.png docs/ && cp jsdoc-custom-style.css docs/",
|
||||
"doc": "jsdoc -c jsdoc.json src/js src/js/shapes src/js/libs/astro && cp aladin-logo.png docs/",
|
||||
"doc:dev": "npm run doc && open docs/index.html"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@playwright/test": "^1.47.0",
|
||||
"docdash": "^2.0.2",
|
||||
"jsdoc": "^4.0.2",
|
||||
"vite": "^4.3.8",
|
||||
"vite-plugin-glsl": "^1.1.2",
|
||||
|
||||
@@ -146,6 +146,7 @@ import { Polyline } from "./shapes/Polyline";
|
||||
* @property {boolean} [samp=false] - Whether to enable SAMP (Simple Application Messaging Protocol).
|
||||
* @property {boolean} [realFullscreen=false] - Whether to use real fullscreen mode.
|
||||
* @property {boolean} [pixelateCanvas=true] - Whether to pixelate the canvas.
|
||||
* @property {boolean} [manualSelection=false] - When set to true, no selection will be performed, only events will be generated.
|
||||
* @example
|
||||
* let aladin = A.aladin({
|
||||
target: 'galactic center',
|
||||
@@ -713,6 +714,7 @@ export let Aladin = (function () {
|
||||
samp: false,
|
||||
realFullscreen: false,
|
||||
pixelateCanvas: true,
|
||||
manualSelection: false
|
||||
};
|
||||
|
||||
// realFullscreen: AL div expands not only to the size of its parent, but takes the whole available screen estate
|
||||
|
||||
@@ -182,21 +182,20 @@ export let AladinUtils = {
|
||||
* @param {number} ra - Right Ascension (RA) coordinate in degrees.
|
||||
* @param {number} dec - Declination (Dec) coordinate in degrees.
|
||||
* @param {Aladin} aladin - Aladin Lite object containing the WebAssembly API.
|
||||
* @returns {number[]} A 2 elements array representing the screen coordinates [X, Y] in pixels.
|
||||
* @returns {number[]} xy - A 2 elements array representing the screen coordinates [X, Y] in pixels.
|
||||
*/
|
||||
radecToViewXy: function(ra, dec, aladin) {
|
||||
return aladin.world2pix(ra, dec);
|
||||
},
|
||||
|
||||
/**
|
||||
* Convert a number in degrees into a string<br>
|
||||
*
|
||||
* @function
|
||||
* @memberof AladinUtils
|
||||
* @name degreesToString
|
||||
*
|
||||
* Convert a number in degrees into a string<br>
|
||||
*
|
||||
* @param numberDegrees number in degrees (integer or decimal)
|
||||
* @return the formatted string
|
||||
* @return a formattes string
|
||||
*
|
||||
* @example <caption> Result in degrees </caption>
|
||||
* // returns "1°"
|
||||
|
||||
@@ -206,6 +206,12 @@ export let DefaultActionsForContextMenu = (function () {
|
||||
action(o) {
|
||||
a.select('rect', selectObjects)
|
||||
}
|
||||
},
|
||||
{
|
||||
label: 'Polygon',
|
||||
action(o) {
|
||||
a.select('poly', selectObjects)
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
@@ -57,8 +57,9 @@ export class CircleSelect extends FSM {
|
||||
let ctx = view.catalogCtx;
|
||||
|
||||
// draw the selection
|
||||
ctx.fillStyle = options.color + '7f';
|
||||
ctx.strokeStyle = options.color;
|
||||
let colorValue = (typeof options.color === 'function') ? options.color(this.startCoo, this.coo) : options.color;
|
||||
ctx.fillStyle = colorValue;
|
||||
ctx.strokeStyle = colorValue;
|
||||
ctx.lineWidth = options.lineWidth;
|
||||
|
||||
var r2 = (this.coo.x - this.startCoo.x) * (this.coo.x - this.startCoo.x) + (this.coo.y - this.startCoo.y) * (this.coo.y - this.startCoo.y);
|
||||
@@ -112,7 +113,7 @@ export class CircleSelect extends FSM {
|
||||
}
|
||||
|
||||
// execute selection callback only
|
||||
(typeof this.callback === 'function') && this.callback(s);
|
||||
(typeof this.callback === 'function') && this.callback(s, Selector.getObjects(s, view));
|
||||
|
||||
// TODO: remove these modes in the future
|
||||
view.aladin.showReticle(true)
|
||||
|
||||
@@ -22,6 +22,7 @@ import { ActionButton } from "../gui/Widgets/ActionButton";
|
||||
import { View } from "../View";
|
||||
import finishIconUrl from '../../../assets/icons/finish.svg';
|
||||
import { Utils } from "../Utils";
|
||||
import { Selector } from "../Selector";
|
||||
|
||||
/******************************************************************************
|
||||
* Aladin Lite project
|
||||
@@ -118,11 +119,12 @@ export class PolySelect extends FSM {
|
||||
|
||||
let draw = () => {
|
||||
let ctx = view.catalogCtx;
|
||||
|
||||
|
||||
// draw the selection
|
||||
ctx.save();
|
||||
ctx.fillStyle = options.color + '7f';
|
||||
ctx.strokeStyle = options.color;
|
||||
let colorValue = (typeof options.color === 'function') ? options.color() : options.color;
|
||||
ctx.fillStyle = colorValue;
|
||||
ctx.strokeStyle = colorValue;
|
||||
ctx.lineWidth = options.lineWidth;
|
||||
|
||||
ctx.beginPath();
|
||||
@@ -144,6 +146,11 @@ export class PolySelect extends FSM {
|
||||
|
||||
let finish = () => {
|
||||
// finish the selection
|
||||
const numCoo = this.coos.length;
|
||||
if (numCoo <= 1) {
|
||||
this.dispatch('off');
|
||||
return;
|
||||
}
|
||||
let xMin = this.coos[0].x
|
||||
let yMin = this.coos[0].y
|
||||
let xMax = this.coos[0].x
|
||||
@@ -163,17 +170,32 @@ export class PolySelect extends FSM {
|
||||
let s = {
|
||||
vertices: this.coos,
|
||||
label: 'polygon',
|
||||
contains(s) {
|
||||
let x = s.x;
|
||||
let y = s.y;
|
||||
|
||||
let inside = false;
|
||||
for (let i = 0, j = this.vertices.length - 1; i < this.vertices.length; j = i++) {
|
||||
let xi = this.vertices[i].x, yi = this.vertices[i].y;
|
||||
let xj = this.vertices[j].x, yj = this.vertices[j].y;
|
||||
|
||||
let intersect = ((yi > y) !== (yj > y)) && (x < (xj - xi) * (y - yi) / (yj - yi) + xi);
|
||||
if (intersect) inside = !inside;
|
||||
}
|
||||
return inside;
|
||||
},
|
||||
bbox() {
|
||||
return {x, y, w, h}
|
||||
}
|
||||
};
|
||||
(typeof this.callback === 'function') && this.callback(s);
|
||||
(typeof this.callback === 'function') && this.callback(s, Selector.getObjects(s, view));
|
||||
|
||||
// execute general callback
|
||||
if (view.aladin.callbacksByEventName) {
|
||||
var callback = view.aladin.callbacksByEventName['objectsSelected'] || view.aladin.callbacksByEventName['select'];
|
||||
if (typeof callback === "function") {
|
||||
console.warn('polygon selection is not fully implemented, PolySelect.contains is needed for finding sources inside a polygon')
|
||||
let objList = Selector.getObjects(s, view);
|
||||
callback(objList);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -202,7 +224,6 @@ export class PolySelect extends FSM {
|
||||
mousedown
|
||||
},
|
||||
mousedown: {
|
||||
//mouseout,
|
||||
mousemove,
|
||||
draw,
|
||||
},
|
||||
@@ -246,6 +267,7 @@ export class PolySelect extends FSM {
|
||||
//mouseout,
|
||||
mousemove,
|
||||
draw,
|
||||
finish,
|
||||
},
|
||||
mouseout: {
|
||||
start,
|
||||
|
||||
@@ -58,8 +58,10 @@ export class RectSelect extends FSM {
|
||||
let ctx = view.catalogCtx;
|
||||
|
||||
// draw the selection
|
||||
ctx.fillStyle = options.color + '7f';
|
||||
ctx.strokeStyle = options.color;
|
||||
let colorValue = (typeof options.color === 'function') ? options.color(this.startCoo, this.coo) : options.color;
|
||||
|
||||
ctx.fillStyle = colorValue;
|
||||
ctx.strokeStyle = colorValue;
|
||||
ctx.lineWidth = options.lineWidth;
|
||||
|
||||
var w = this.coo.x - this.startCoo.x;
|
||||
@@ -99,7 +101,7 @@ export class RectSelect extends FSM {
|
||||
}
|
||||
};
|
||||
|
||||
(typeof this.callback === 'function') && this.callback(s);
|
||||
(typeof this.callback === 'function') && this.callback(s, Selector.getObjects(s, view));
|
||||
|
||||
// TODO: remove these modes in the future
|
||||
view.aladin.showReticle(true)
|
||||
|
||||
@@ -128,8 +128,8 @@ PropertyParser.isPlanetaryBody = function (properties) {
|
||||
};
|
||||
|
||||
/**
|
||||
* HiPS options
|
||||
* @typedef {Object} HiPSOptions
|
||||
*
|
||||
* @property {string} [name] - The name of the survey to be displayed in the UI
|
||||
* @property {Function} [successCallback] - A callback executed when the HiPS has been loaded
|
||||
* @property {Function} [errorCallback] - A callback executed when the HiPS could not be loaded
|
||||
@@ -155,16 +155,19 @@ PropertyParser.isPlanetaryBody = function (properties) {
|
||||
*/
|
||||
|
||||
/**
|
||||
* JS {@link https://developer.mozilla.org/fr/docs/Web/API/FileList| FileList} API type
|
||||
*
|
||||
* @typedef {Object} FileList
|
||||
*
|
||||
* JS {@link https://developer.mozilla.org/fr/docs/Web/API/FileList| FileList} API type
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {Object} HiPSLocalFiles
|
||||
*
|
||||
* @property {File} properties - The local properties file of the HiPS
|
||||
*
|
||||
* @description
|
||||
* Tiles are accessed like so: HIPSLocalFiles[norder][ipix] = {@link File};<br/>
|
||||
* The properties file is accessed with: HIPSLocalFiles["properties"]
|
||||
* @typedef {Object} HiPSLocalFiles
|
||||
* @property {File} properties - The local properties file of the HiPS
|
||||
*/
|
||||
|
||||
|
||||
|
||||
@@ -222,8 +222,8 @@ export let MOC = (function() {
|
||||
/**
|
||||
* Serialize a MOC into different format
|
||||
*
|
||||
* @memberof MOC
|
||||
* @param {string} [format='json'] - The output format. Only `json` is currently supported but `fits` could be added.
|
||||
* @memberof Aladin
|
||||
* @param {string} [format='json'] - The output format. Only json is currently supported but 'fits' could be added.
|
||||
*/
|
||||
MOC.prototype.serialize = function(format) {
|
||||
if (!this.ready) {
|
||||
|
||||
@@ -94,7 +94,7 @@ export class Selector {
|
||||
|
||||
var objList = [];
|
||||
var cat, sources, s;
|
||||
var footprints, f;
|
||||
var overlayItems, f;
|
||||
var objListPerCatalog = [];
|
||||
if (view.catalogs) {
|
||||
for (var k = 0; k < view.catalogs.length; k++) {
|
||||
@@ -114,11 +114,11 @@ export class Selector {
|
||||
}
|
||||
}
|
||||
// footprints
|
||||
footprints = cat.getFootprints();
|
||||
if (footprints) {
|
||||
overlayItems = cat.getFootprints();
|
||||
if (overlayItems) {
|
||||
const {x, y, w, h} = selection.bbox();
|
||||
for (var l = 0; l < footprints.length; l++) {
|
||||
f = footprints[l];
|
||||
for (var l = 0; l < overlayItems.length; l++) {
|
||||
f = overlayItems[l];
|
||||
if (f.intersectsBBox(x, y, w, h, view)) {
|
||||
objListPerCatalog.push(f);
|
||||
}
|
||||
@@ -132,6 +132,27 @@ export class Selector {
|
||||
}
|
||||
}
|
||||
|
||||
if (view.overlays) {
|
||||
const {x, y, w, h} = selection.bbox();
|
||||
for (var k = 0; k < view.overlays.length; k++) {
|
||||
let overlay = view.overlays[k];
|
||||
if (!overlay.isShowing) {
|
||||
continue;
|
||||
}
|
||||
var overlayItems = overlay.overlayItems;
|
||||
for (var l = 0; l < overlayItems.length; l++) {
|
||||
let o = overlayItems[l];
|
||||
if (!o.isShowing) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (o.intersectsBBox && o.intersectsBBox(x, y, w, h, view)) {
|
||||
objList.push([o]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return objList;
|
||||
}
|
||||
}
|
||||
@@ -205,7 +205,8 @@ export let View = (function () {
|
||||
const cooFrame = CooFrameEnum.fromString(this.options.cooFrame, CooFrameEnum.J2000);
|
||||
this.changeFrame(cooFrame);
|
||||
|
||||
this.selector = new Selector(this);
|
||||
this.selector = new Selector(this, this.options.selector);
|
||||
this.manualSelection = (this.options && this.options.manualSelection) || false;
|
||||
|
||||
// current reference image survey displayed
|
||||
this.imageLayers = new Map();
|
||||
@@ -575,7 +576,7 @@ export let View = (function () {
|
||||
const xymouse = Utils.relMouseCoords(e);
|
||||
|
||||
// deselect all the selected sources with Select panel
|
||||
view.unselectObjects()
|
||||
view.unselectObjects();
|
||||
|
||||
try {
|
||||
const [lon, lat] = view.aladin.pix2world(xymouse.x, xymouse.y, 'icrs');
|
||||
@@ -618,7 +619,7 @@ export let View = (function () {
|
||||
var handleSelect = function(xy, tolerance) {
|
||||
tolerance = tolerance || 5;
|
||||
var objs = view.closestObjects(xy.x, xy.y, tolerance);
|
||||
// Deselect objects if any
|
||||
|
||||
view.unselectObjects();
|
||||
|
||||
if (objs) {
|
||||
@@ -640,7 +641,7 @@ export let View = (function () {
|
||||
(typeof objClickedFunction === 'function') && objClickedFunction(o, xy);
|
||||
|
||||
if (o.isFootprint()) {
|
||||
if (typeof footprintClickedFunction === 'function' && (!view.lastClickedObject || !view.lastClickedObject.includes(o))) {
|
||||
if (typeof footprintClickedFunction === 'function') {
|
||||
footprintClickedFunction(o, xy);
|
||||
}
|
||||
}
|
||||
@@ -650,6 +651,7 @@ export let View = (function () {
|
||||
objs = Array.from(Object.values(objsByCats));
|
||||
view.selectObjects(objs);
|
||||
view.lastClickedObject = objs;
|
||||
|
||||
} else {
|
||||
// If there is a past clicked object
|
||||
if (view.lastClickedObject) {
|
||||
@@ -1430,6 +1432,10 @@ export let View = (function () {
|
||||
};
|
||||
|
||||
View.prototype.unselectObjects = function() {
|
||||
if (this.manualSelection) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.aladin.measurementTable.hide();
|
||||
|
||||
if (this.selection) {
|
||||
@@ -1449,9 +1455,13 @@ export let View = (function () {
|
||||
}
|
||||
|
||||
View.prototype.selectObjects = function(selection) {
|
||||
if (this.manualSelection) {
|
||||
return;
|
||||
}
|
||||
|
||||
// unselect the previous selection
|
||||
this.unselectObjects();
|
||||
|
||||
|
||||
if (Array.isArray(selection)) {
|
||||
this.selection = selection;
|
||||
} else {
|
||||
|
||||
@@ -288,8 +288,8 @@ export let Circle = (function() {
|
||||
// From StackOverflow: https://stackoverflow.com/questions/401847/circle-rectangle-collision-detection-intersection
|
||||
Circle.prototype.intersectsBBox = function(x, y, w, h) {
|
||||
const circleDistance = {
|
||||
x: abs(this.center.x - x),
|
||||
y: abs(this.center.y - y)
|
||||
x: Math.abs(this.center.x - x),
|
||||
y: Math.abs(this.center.y - y)
|
||||
};
|
||||
|
||||
if (circleDistance.x > (w/2 + this.radius)) { return false; }
|
||||
|
||||
@@ -464,8 +464,51 @@ export let Polyline = (function() {
|
||||
return false;
|
||||
};
|
||||
|
||||
Polyline.prototype.intersectsBBox = function(x, y, w, h) {
|
||||
// todo
|
||||
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];
|
||||
|
||||
let xy1 = view.aladin.world2pix(p1[0], p1[1]);
|
||||
let xy2 = view.aladin.world2pix(p2[0], p2[1]);
|
||||
|
||||
if (!xy1 || !xy2) {
|
||||
return false;
|
||||
}
|
||||
|
||||
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)) {
|
||||
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
|
||||
|
||||
// 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)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
// static methods
|
||||
|
||||