Compare commits

...

7 Commits

Author SHA1 Message Date
Matthieu Baumann
1bdbf49438 Fix selection of footprints not associated with catalog sources
Targets #274

* fix: handleSelect now call selectObjects with not only the list of catalog sources but also with the footprints
* fix: View.closestFootprints: if no lineWidth was given to a footprint then it could happen that this method set it to 1px, erasing its previous undefined value
* Circle and Ellipse now behaves like PolyLine and Vector, if no linewidth is given, the one from its GraphicOverlay is taken.
2025-04-01 14:31:57 +02:00
Matthieu Baumann
4a5d66768c 3.6.4 2025-04-01 11:10:17 +02:00
Matthieu Baumann
239ae2ce74 fix make it work for safari 16.1
Revert wasm-bindgen version to 0.2.92 to make it work for safari 16.1 (and maybe older?)
(This is very obscure)
2025-03-31 17:09:05 +02:00
Matthieu Baumann
9bf898c104 3.6.3 2025-03-26 18:06:12 +01:00
Matthieu Baumann
6f085429f5 background color set to (0.1, 0.1, 0.1)
jpeg HiPS that does not cover all the sky are plotted as black when data is missing. This allow to recognize the border of the projection
2025-03-26 17:18:23 +01:00
Matthieu Baumann
b49c763e07 Improved distant HiPS url detection
This targets #270

* Use js URL object to detect if the user gives an url
* There is important point: a user can give a path pointing towards a local HiPS. For those Aladin Lite will think the path is an ID but it is not. That is why after failing fetching the MocServer for its properties, we simply try to reconsider it as an URL so that a local HiPS can be load afterwards.
2025-03-26 17:10:13 +01:00
Matthieu Baumann
fcacda0c19 Several fixes:
* use Utils.copy2Clipboard in contextmenu and shareview
* check for a mousedown before computing distance from the position when the mouse has been clicked
* smartphone 2 fingers pinched rotation between lon pi and 2*pi seems to have been fixed. The bug seem to be there from a long time ago.
2025-03-26 15:47:32 +01:00
24 changed files with 118 additions and 91 deletions

View File

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

View File

@@ -13,7 +13,7 @@
// Start up Aladin Lite
aladin = A.aladin('#aladin-lite-div', {survey: "CDS/P/DSS2/color", target: 'M 1', fov: 0.2, showContextMenu: true, fullScreen: true});
var overlay = A.graphicOverlay({color: '#ee2345', lineWidth: 3, lineDash: [2, 2]});
/*aladin.addOverlay(overlay);
aladin.addOverlay(overlay);
overlay.addFootprints([
A.polygon([[83.64287, 22.01713], [83.59872, 22.01692], [83.59852, 21.97629], [83.64295, 21.97629]], {hoverColor: 'green'}),
A.polygon([[83.62807, 22.06330], [83.58397, 22.02280], [83.62792, 22.02258]]),
@@ -21,7 +21,7 @@
]);
overlay.add(A.circle(83.66067, 22.03081, 0.04, {color: 'cyan'})); // radius in degrees
overlay.add(A.vector(83.66067, 22.03081, 0.04, {color: 'cyan'})); // radius in degrees
*/
aladin.on("footprintClicked", (footprint, xyMouseCoords) => {
console.log("footprint clicked catched: ", footprint, "mouse coords xy: ", xyMouseCoords.x, xyMouseCoords.y);
})
@@ -35,8 +35,8 @@
console.log("Object hovered stopped: ", object, "mouse coords xy: ", xyMouseCoords.x, xyMouseCoords.y);
})
const cat = A.catalogFromVizieR('B/assocdata/obscore', 'M 1', 10, {onClick: 'showTable', selectionColor: "orange", hoverColor: 'red', limit: 10000});
aladin.addCatalog(cat);
//const cat = A.catalogFromVizieR('B/assocdata/obscore', 'M 1', 10, {onClick: 'showTable', selectionColor: "orange", hoverColor: 'red', limit: 10000});
//aladin.addCatalog(cat);
});
</script>
</body>

View File

@@ -2,7 +2,7 @@
"homepage": "https://aladin.u-strasbg.fr/",
"name": "aladin-lite",
"type": "module",
"version": "3.6.2",
"version": "3.6.4",
"description": "An astronomical HiPS visualizer in the browser",
"author": "Thomas Boch and Matthieu Baumann",
"license": "GPL-3",

View File

@@ -3,7 +3,7 @@ name = "aladin-lite"
description = "Aladin Lite v3 introduces a new graphical engine written in Rust with the use of WebGL"
license = "BSD-3-Clause"
repository = "https://github.com/cds-astro/aladin-lite"
version = "3.6.2"
version = "3.6.4"
authors = [ "baumannmatthieu0@gmail.com", "matthieu.baumann@astro.unistra.fr",]
edition = "2018"
@@ -22,7 +22,7 @@ url-lite = "0.1.0"
serde_json = "1.0.104"
serde-wasm-bindgen = "0.5"
enum_dispatch = "0.3.8"
wasm-bindgen = "0.2.100"
wasm-bindgen = "=0.2.92"
wasm-streams = "0.3.0"
async-channel = "1.8.0"
mapproj = "0.3.0"
@@ -64,7 +64,7 @@ path = "./al-core"
path = "./al-api"
[dependencies.web-sys]
version = "*"
version = "0.3.56"
features = [ "console", "CssStyleDeclaration", "Document", "Element", "HtmlCollection", "HtmlElement", "HtmlImageElement", "HtmlCanvasElement", "Blob", "ImageBitmap", "ImageData", "CanvasRenderingContext2d", "WebGlBuffer", "WebGlContextAttributes", "WebGlFramebuffer", "WebGlProgram", "WebGlShader", "WebGlUniformLocation", "WebGlTexture", "WebGlActiveInfo", "Headers", "Window", "Request", "RequestInit", "RequestMode", "Response", "XmlHttpRequest", "XmlHttpRequestResponseType", "PerformanceTiming", "Performance", "Url", "ReadableStream", "File", "FileList",]
[dev-dependencies.image-decoder]

View File

@@ -1,6 +1,6 @@
[package]
name = "al-api"
version = "0.1.0"
version = "3.6.4"
authors = ["baumannmatthieu0@gmail.com", "matthieu.baumann@astro.unistra.fr"]
edition = "2018"

View File

@@ -1,6 +1,6 @@
[package]
name = "al-core"
version = "0.1.0"
version = "3.6.4"
authors = ["baumannmatthieu0@gmail.com", "matthieu.baumann@astro.unistra.fr"]
edition = "2018"
@@ -17,9 +17,7 @@ serde-wasm-bindgen = "0.4"
wasm-streams = "0.3.0"
futures = "0.3.25"
colorgrad = "0.6.2"
[dependencies.wasm-bindgen]
version = "0.2.92"
wasm-bindgen = "0.2.92"
[dev-dependencies]
fontdue = "0.7.2"
@@ -38,7 +36,7 @@ webgl2 = [
]
[dependencies.web-sys]
version = "0.3.77"
version = "0.3.56"
features = [
'console',
'CssStyleDeclaration',

View File

@@ -1,6 +1,7 @@
use crate::renderable::ImageLayer;
use crate::tile_fetcher::HiPSLocalFiles;
use crate::math::angle::ToAngle;
use crate::renderable::hips::HiPS;
use crate::{
//async_task::{BuildCatalogIndex, ParseTableTask, TaskExecutor, TaskResult, TaskType},
@@ -21,7 +22,6 @@ use crate::{
time::DeltaTime,
};
use al_api::moc::MOCOptions;
use crate::math::angle::ToAngle;
use wcs::WCS;
use wasm_bindgen::prelude::*;
@@ -188,7 +188,7 @@ impl App {
let request_for_new_tiles = true;
let moc = MOCRenderer::new(&gl)?;
gl.clear_color(0.0, 0.0, 0.0, 1.0);
gl.clear_color(0.1, 0.1, 0.1, 1.0);
let (img_send, img_recv) = async_channel::unbounded::<ImageLayer>();
let (ack_img_send, ack_img_recv) = async_channel::unbounded::<ImageParams>();
@@ -714,8 +714,8 @@ impl App {
)?,
}
self.time_start_blending = Time::now();
},
_ => ()
}
_ => (),
};
}
}
@@ -1277,8 +1277,7 @@ impl App {
// Set the new meta
// keep the old meta data
let new_img_ext = meta.img_format;
self.layers
.set_layer_cfg(layer.clone(), meta)?;
self.layers.set_layer_cfg(layer.clone(), meta)?;
if old_meta.img_format != new_img_ext {
// The image format has been changed

View File

@@ -5,8 +5,8 @@ pub enum UserAction {
Moving = 3,
Starting = 4,
}
use web_sys::WebGl2RenderingContext;
use web_sys::WebGl2RenderingContext;
// Longitude reversed identity matrix
const ID_R: &Matrix4<f64> = &Matrix4::new(
-1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0,
@@ -488,11 +488,11 @@ impl CameraViewPort {
pub fn set_center(&mut self, lonlat: &LonLatT<f64>, proj: &ProjectionType) {
let icrs_pos: Vector4<_> = lonlat.vector();
let view_pos = CooSystem::ICRS.to(self.get_coo_system()) * icrs_pos;
let rot_to_center = Rotation::from_sky_position(&view_pos);
let center = (CooSystem::ICRS.to(self.get_coo_system()) * icrs_pos).truncate();
let rot_to_center = Rotation::from_sky_position(&center);
let phi = self.get_center_pos_angle();
let third_euler_rot = Rotation::from_axis_angle(&view_pos.truncate(), phi);
let third_euler_rot = Rotation::from_axis_angle(&center, phi);
let rot = third_euler_rot * rot_to_center;
@@ -502,8 +502,9 @@ impl CameraViewPort {
}
pub fn set_center_pos_angle(&mut self, phi: Angle<f64>, proj: &ProjectionType) {
let rot_to_center = Rotation::from_sky_position(&self.center);
let third_euler_rot = Rotation::from_axis_angle(&self.center.truncate(), phi);
let c = self.center.truncate();
let rot_to_center = Rotation::from_sky_position(&c);
let third_euler_rot = Rotation::from_axis_angle(&c, phi);
let total_rot = third_euler_rot * rot_to_center;
self.set_rotation(&total_rot, proj);
@@ -523,7 +524,7 @@ impl CameraViewPort {
// Compute the center position according to the new coordinate frame system
let new_center = coosys::apply_coo_system(self.coo_sys, new_coo_sys, &self.center);
// Create a rotation object from that position
let new_rotation = Rotation::from_sky_position(&new_center);
let new_rotation = Rotation::from_sky_position(&new_center.truncate());
// Apply it to the center of the view
self.set_rotation(&new_rotation, proj);

View File

@@ -130,9 +130,9 @@ impl From<query::Allsky> for AllskyRequest {
Ok(allsky_tiles)
}
_ => {
let opts = RequestInit::new();
opts.set_method("GET");
opts.set_mode(RequestMode::Cors);
let mut opts = RequestInit::new();
opts.method("GET");
opts.mode(RequestMode::Cors);
let window = web_sys::window().unwrap_abort();
let request = web_sys::Request::new_with_str_and_init(&url_clone, &opts)?;

View File

@@ -61,9 +61,9 @@ impl From<query::PixelMetadata> for PixelMetadataRequest {
let request = match channel {
ChannelType::R32F | ChannelType::R32I | ChannelType::R16I | ChannelType::R8UI => {
Request::new(async move {
let opts = RequestInit::new();
opts.set_method("GET");
opts.set_mode(RequestMode::Cors);
let mut opts = RequestInit::new();
opts.method("GET");
opts.mode(RequestMode::Cors);
let request =
web_sys::Request::new_with_str_and_init(&url_clone, &opts).unwrap_abort();

View File

@@ -61,9 +61,9 @@ impl From<query::Moc> for MOCRequest {
let window = web_sys::window().unwrap_abort();
let request = Request::new(async move {
let opts = RequestInit::new();
opts.set_method("GET");
opts.set_mode(RequestMode::Cors);
let mut opts = RequestInit::new();
opts.method("GET");
opts.mode(RequestMode::Cors);
let request = web_sys::Request::new_with_str_and_init(&url_clone, &opts).unwrap_abort();
let resp_value = JsFuture::from(window.fetch_with_request(&request)).await?;

View File

@@ -142,9 +142,9 @@ impl From<query::Tile> for TileRequest {
| ChannelType::R32I
| ChannelType::R16I
| ChannelType::R8UI => Request::new(async move {
let opts = RequestInit::new();
opts.set_method("GET");
opts.set_mode(RequestMode::Cors);
let mut opts = RequestInit::new();
opts.method("GET");
opts.mode(RequestMode::Cors);
let request =
web_sys::Request::new_with_str_and_init(&url_clone, &opts).unwrap_abort();

View File

@@ -523,7 +523,7 @@ impl WebClient {
/// Get if the longitude axis is reversed
#[wasm_bindgen(js_name = getLongitudeReversed)]
pub fn get_longitude_reversed(&mut self) -> bool {
pub fn get_longitude_reversed(&self) -> bool {
self.app.get_longitude_reversed()
}

View File

@@ -1,8 +1,8 @@
use crate::math;
use crate::math::angle::ToAngle;
use cgmath::{BaseFloat, InnerSpace};
use cgmath::{Euler, Quaternion};
use cgmath::{Vector3, Vector4};
use crate::math::angle::ToAngle;
#[derive(Clone, Copy, Debug)]
// Internal structure of a rotation, a quaternion
@@ -109,13 +109,13 @@ where
// Define a rotation from an axis and a angle
pub fn from_axis_angle(axis: &Vector3<S>, angle: Angle<S>) -> Rotation<S> {
let angle: Rad<S> = angle.into();
let mat4 = Matrix4::from_axis_angle(*axis, angle);
let mat4 = Matrix4::from_axis_angle(axis.normalize(), angle);
(&mat4).into()
}
// Define a rotation from a normalized vector
pub fn from_sky_position(pos: &Vector4<S>) -> Rotation<S> {
let (lon, lat) = math::lonlat::xyzw_to_radec(&pos.normalize());
pub fn from_sky_position(pos: &Vector3<S>) -> Rotation<S> {
let (lon, lat) = math::lonlat::xyz_to_radec(&pos);
let rot_y = Matrix4::from_angle_y(lon);
let rot_x = Matrix4::from_angle_x(-lat);

View File

@@ -95,7 +95,7 @@ impl Renderer for TextRenderManager {
// reset the font and color
self.ctx
.set_font(&format!("{}px verdana, sans-serif", self.font_size));
self.ctx.set_fill_style_str(&self.color);
self.ctx.set_fill_style(&JsValue::from_str(&self.color));
}
fn end(&mut self) {}

View File

@@ -1036,7 +1036,7 @@ A.init = (async () => {
.createElement('canvas')
.getContext('webgl2');
await init({});
await init();
// Check for webgl2 support
if (isWebGL2Supported) {
Aladin.wasmLibs.core = module;

View File

@@ -613,7 +613,6 @@ export let Catalog = (function () {
// Set the same color of the shape than the catalog.
// FIXME: the color/shape could be a parameter at the source level, allowing the user single catalogs handling different shapes
shape.setColor(this.color)
shape.setSelectionColor(this.selectionColor);
shape.setHoverColor(this.hoverColor);
}

View File

@@ -35,6 +35,7 @@ import cameraIconUrl from '../../assets/icons/camera.svg'
import targetIconUrl from '../../assets/icons/target.svg';
import uploadIconUrl from '../../assets/icons/upload.svg';
import selectIconUrl from '../../assets/icons/select.svg';
import { Utils } from "./Utils";
export let DefaultActionsForContextMenu = (function () {
@@ -55,7 +56,7 @@ export let DefaultActionsForContextMenu = (function () {
return false;
}
navigator.clipboard.writeText(text)
Utils.copy2Clipboard(text)
.then(() => {
msg = 'successful'
if (aladinInstance.statusBar) {

View File

@@ -30,6 +30,8 @@ import { ColorCfg } from "./ColorCfg.js";
import { HiPSProperties } from "./HiPSProperties.js";
import { Aladin } from "./Aladin.js";
import { CooFrameEnum } from "./CooFrameEnum.js";
import { Utils } from "./Utils"
let PropertyParser = {};
// Utilitary functions for parsing the properties and giving default values
/// Mandatory tileSize property
@@ -860,9 +862,8 @@ export let HiPS = (function () {
let isIncompleteOptions = true;
// This is very dirty but it allows me to differentiate the location from whether it is an ID or a plain url
let isID = this.url.includes("P/") || this.url.includes("C/")
let isID = Utils.isUrl(this.url) === undefined;
if (this.imgFormat === "fits") {
// a fits is given
isIncompleteOptions = !(
@@ -887,7 +888,6 @@ export let HiPS = (function () {
if (isIncompleteOptions) {
// ID typed url
if (self.startUrl && isID) {
// First download the properties from the start url
await HiPSProperties.fetchFromUrl(self.startUrl)
.then((p) => {
@@ -909,9 +909,16 @@ export let HiPS = (function () {
HiPSProperties.fetchFromID(id)
.then((p) => {
//self.url = self.startUrl;
self._fetchFasterUrlFromProperties(p);
})
.catch(() => {
// If no ID has been found then it may actually be a path
// url pointing to a local HiPS
return HiPSProperties.fetchFromUrl(id)
.then((p) => {
self._parseProperties(p);
})
})
},
1000
);
@@ -932,6 +939,14 @@ export let HiPS = (function () {
self._parseProperties(p);
self._fetchFasterUrlFromProperties(p);
})
.catch(() => {
// If no ID has been found then it may actually be a path
// url pointing to a local HiPS
return HiPSProperties.fetchFromUrl(id)
.then((p) => {
self._parseProperties(p);
})
})
} catch (e) {
throw e;
}

View File

@@ -38,7 +38,7 @@ HiPSProperties.fetchFromID = async function(ID) {
const params = {
get: "record",
fmt: "json",
ID: "*" + ID + "*",
ID: "*" + decodeURI(ID) + "*",
};
let metadata = await Utils.loadFromUrls(MocServer.MIRRORS_HTTPS, {

View File

@@ -630,6 +630,7 @@ export let View = (function () {
var footprintClickedFunction = view.aladin.callbacksByEventName['footprintClicked'];
let objsByCats = {};
let footprints = [];
for (let o of objs) {
// classify the different objects by catalog
let cat = o.getCatalog && o.getCatalog();
@@ -647,12 +648,22 @@ export let View = (function () {
if (typeof footprintClickedFunction === 'function') {
footprintClickedFunction(o, xy);
}
// If this footprint has a catalog then it will be selected from its source
// so we will not add it
if (!cat) {
footprints.push(o);
}
}
}
// rewrite objs
// Rewrite objs
objs = Array.from(Object.values(objsByCats));
// Add the external footprints (i.e. which are not associated with catalog sources e.g. those from GraphicOverlay)
if (footprints.length > 0) {
objs.push(footprints)
}
view.selectObjects(objs);
view.lastClickedObject = objs;
} else {
@@ -926,9 +937,10 @@ export let View = (function () {
xy: xymouse,
});
let dist = (() => {
return (xymouse.x - xystart.x)*(xymouse.x - xystart.x) + (xymouse.y - xystart.y)*(xymouse.y - xystart.y)
})();
let dist;
if (xystart) {
dist = (xymouse.x - xystart.x)*(xymouse.x - xystart.x) + (xymouse.y - xystart.y)*(xymouse.y - xystart.y);
}
if (e.type === 'touchmove' && xystart) {
if (longTouchTimer && dist > 100) {
@@ -2159,26 +2171,21 @@ export let View = (function () {
}
let closests = [];
const fLineWidth = (footprints && footprints[0] && footprints[0].getLineWidth()) || 1;
let lw = fLineWidth + 3;
//for (var lw = startLw + 1; lw <= startLw + 3; lw++) {
footprints.forEach((footprint) => {
if (!footprint.source || !footprint.source.tooSmallFootprint) {
// Hidden footprints are not considered
//let originLineWidth = footprint.getLineWidth();
footprint.setLineWidth(lw);
if (footprint.isShowing && footprint.isInStroke(ctx, this, x * window.devicePixelRatio, y * window.devicePixelRatio)) {
closests.push(footprint);
}
footprint.setLineWidth(fLineWidth);
}
})
/* if (closests.length > 0) {
break;
footprints.forEach((footprint) => {
if (!footprint.source || !footprint.source.tooSmallFootprint) {
const originLineWidth = footprint.getLineWidth();
let spreadedLineWidth = (originLineWidth || 1) + 3;
footprint.setLineWidth(spreadedLineWidth);
if (footprint.isShowing && footprint.isInStroke(ctx, this, x * window.devicePixelRatio, y * window.devicePixelRatio)) {
closests.push(footprint);
}
footprint.setLineWidth(originLineWidth);
}
}*/
})
return closests;
};
@@ -2190,13 +2197,11 @@ export let View = (function () {
var canvas = this.catalogCanvas;
var ctx = canvas.getContext("2d");
// this makes footprint selection easier as the catch-zone is larger
//let pastLineWidth = ctx.lineWidth;
let closests = [];
if (this.overlays) {
for (var k = 0; k < this.overlays.length; k++) {
overlay = this.overlays[k];
closests = closests.concat(this.closestFootprints(overlay.overlayItems, ctx, x, y));
}
}

View File

@@ -22,7 +22,7 @@ import shareIconUrl from '../../../../assets/icons/share.svg';
import cameraIconUrl from '../../../../assets/icons/camera.svg';
import linkIconUrl from '../../../../assets/icons/link.svg';
import jupyterIconUrl from '../../../../assets/icons/jupyter.svg';
import { Utils } from "../../Utils";
/******************************************************************************
* Aladin Lite project
*
@@ -62,15 +62,16 @@ import jupyterIconUrl from '../../../../assets/icons/jupyter.svg';
},
action(o) {
var url = aladin.getShareURL();
navigator.clipboard.writeText(url);
if (aladin.statusBar) {
aladin.statusBar.appendMessage({
message: 'View URL saved into your clipboard!',
duration: 2000,
type: 'info'
Utils.copy2Clipboard(url)
.then(() => {
if (aladin.statusBar) {
aladin.statusBar.appendMessage({
message: 'View URL saved into your clipboard!',
duration: 2000,
type: 'info'
})
}
})
}
}
},
{

View File

@@ -48,7 +48,7 @@ export let Circle = (function() {
this.color = options['color'] || undefined;
this.fillColor = options['fillColor'] || undefined;
this.lineWidth = options["lineWidth"] || 2;
this.lineWidth = options["lineWidth"] || undefined;
this.selectionColor = options["selectionColor"] || '#00ff00';
this.hoverColor = options["hoverColor"] || undefined;
this.opacity = options['opacity'] || 1;
@@ -297,6 +297,10 @@ export let Circle = (function() {
ctx.strokeStyle = baseColor;
}
if (!this.lineWidth) {
this.lineWidth = (this.overlay && this.overlay.lineWidth) || 2;
}
ctx.lineWidth = this.lineWidth;
ctx.globalAlpha = this.opacity;
ctx.beginPath();

View File

@@ -51,7 +51,7 @@ export let Ellipse = (function() {
this.color = options['color'] || undefined;
this.fillColor = options['fillColor'] || undefined;
this.lineWidth = options["lineWidth"] || 2;
this.lineWidth = options["lineWidth"] || undefined;
this.selectionColor = options["selectionColor"] || '#00ff00';
this.hoverColor = options["hoverColor"] || undefined;
this.opacity = options['opacity'] || 1;
@@ -283,6 +283,10 @@ export let Ellipse = (function() {
ctx.strokeStyle = baseColor;
}
if (!this.lineWidth) {
this.lineWidth = (this.overlay && this.overlay.lineWidth) || 2;
}
ctx.lineWidth = this.lineWidth;
ctx.globalAlpha = this.opacity;
ctx.beginPath();