Compare commits

..

14 Commits

Author SHA1 Message Date
Thomas Boch
0d613a9b42 Merge pull request #221 from cds-astro/revert-220-fix-circle-opacity-not-working
Revert "Bug fix: opacity is now available for Circle overlays (issue #215)"
2025-01-16 10:03:44 +01:00
Thomas Boch
09f1302646 Revert "Bug fix: opacity is now available for Circle overlays (issue #215)" 2025-01-16 10:03:08 +01:00
Thomas Boch
c39045171e Merge pull request #220 from cds-astro/fix-circle-opacity-not-working
Bug fix: opacity is now available for Circle overlays (issue #215)
2025-01-16 10:00:15 +01:00
Thomas Boch
bbd5848bdb Bug fix: opacity is now available for Circle overlays (issue #215) 2025-01-16 09:58:17 +01:00
Thomas Boch
7062a3cd76 Update README.md with terms of use
Add Terms of use for Aladin Lite embedding.
2024-12-05 10:26:32 +01:00
Matthieu Baumann
f79d47d61e update jsdoc template to docdash 2024-09-24 23:36:19 +02:00
Matthieu Baumann
e6321f3950 resize vignettes 2024-09-24 18:27:42 +02:00
Matthieu Baumann
ea174b2182 Update README.md
contributing guidelines
2024-09-24 15:43:28 +02:00
Matthieu Baumann
1ad6c54538 README.md 2024-09-24 15:12:42 +02:00
Matthieu Baumann
7d3eb6e80a Update README.md
Add links to editable examples
2024-09-24 15:10:21 +02:00
Matthieu Baumann
61f9af1415 use Moc.fits when loading a local HiPS + add readme example vignettes 2024-09-24 14:29:32 +02:00
Matthieu Baumann
2395cb9425 wcs basic doc + filelist doc 2024-09-19 11:38:11 +02:00
Matthieu Baumann
f5da11c337 Merge branch 'develop' 2024-09-18 17:30:48 +02:00
Matthieu Baumann
e0726590ab Update README.md 2024-07-28 00:00:23 +02:00
26 changed files with 424 additions and 241 deletions

171
README.md
View File

@@ -1,4 +1,4 @@
# Aladin Lite v3
# [Aladin Lite](https://aladin.u-strasbg.fr/AladinLite)
**An astronomical HiPS visualizer in the browser** <img src="aladin-logo.png" alt="Aladin Lite logo" width="220">
@@ -12,31 +12,50 @@ More details on [Aladin Lite documentation page](http://aladin.u-strasbg.fr/Alad
A new [API technical documentation](https://cds-astro.github.io/aladin-lite/) is now available.
[![Run tests](https://github.com/cds-astro/aladin-lite/actions/workflows/test.yml/badge.svg)](https://github.com/cds-astro/aladin-lite/actions/workflows/test.yml)
[![API Documentation](https://img.shields.io/badge/docs-latest-blue.svg)](https://cds-astro.github.io/aladin-lite)
[![API Documentation](https://img.shields.io/badge/API-documentation-blue.svg)](https://cds-astro.github.io/aladin-lite)
# How to test it ?
Aladin Lite is available [at this link](https://aladin.u-strasbg.fr/AladinLite).
Aladin Lite v3 is out! Please play with [Aladin Lite v3 at this link](https://aladin.u-strasbg.fr/AladinLite).
## Running & editable JS examples
<!-- Examples -->
<table><tbody>
<tr><td>Basic Aladin Lite setup</td><td>Load SIMBAD & NED catalog data</td><td>Load a FITS image</td></tr>
<tr><td align="left"><a href="https://aladin.cds.unistra.fr/AladinLite/doc/API/examples/AL-in-responsive-div/"><img height="200" src="https://github.com/cds-astro/aladin-lite/blob/develop/assets/vignettes/Basic%20Aladin%20Lite%20instanciation.png?raw=true"></img></a></td>
<td align="center"><a href="https://aladin.cds.unistra.fr/AladinLite/doc/API/examples/easy-access-simbad-ned/"><img height="200" src="https://github.com/cds-astro/aladin-lite/blob/develop/assets/vignettes/Load%20SIMBAD%20&%20NED%20catalogs%20data.png?raw=true"></img></a></td>
<td align="right"><a href="https://aladin.cds.unistra.fr/AladinLite/doc/API/examples/load-FITS-image-URL/"><img height="200" src="https://github.com/cds-astro/aladin-lite/blob/develop/assets/vignettes/Load%20a%20FITS%20image.png?raw=true"></img></a></td></tr><tr>
<td>American Astronomical Society 225<br/>example</td><td>Display proper motion vectors</td><td>Visualization of Mars</td></tr><tr>
<td align="left"><a href="https://aladin.cds.unistra.fr/AladinLite/doc/API/examples/AAS225/"><img height="200" src="https://github.com/cds-astro/aladin-lite/blob/develop/assets/vignettes/American%20Astronomical%20Society%20225%20demonstration.png?raw=true"></img></a></td>
<td align="center"><a href="https://aladin.cds.unistra.fr/AladinLite/doc/API/examples/show-proper-motions/"><img height="200" src="https://github.com/cds-astro/aladin-lite/blob/develop/assets/vignettes/Display%20proper%20motions.png?raw=true"></img></a></td>
<td align="right"><a href="https://aladin.cds.unistra.fr/AladinLite/doc/API/examples/mars-visualisation/"><img height="200" src="https://github.com/cds-astro/aladin-lite/blob/develop/assets/vignettes/Visualization%20of%20Mars.png?raw=true"></img></a></td></tr></tbody></table>
<!-- Examples -->
## Releases
For integrating Aladin Lite into your personal website, please refer to this [release page](https://aladin.cds.unistra.fr/AladinLite/doc/release/).
Always prefer using the latest version. If you want the new features without minding about the bugs coming with it, then the beta is the good way to go.
A [release page](https://aladin.cds.unistra.fr/AladinLite/doc/release/) keeps track of all the current and previous builds.
A ``release`` and ``beta`` versions, regularly updated are available. The ``beta`` version is usually more advanced than the ``release`` one but features more error prone and not production-ready code.
## API documentation
> [!TIP]
> If you are working on a project that uses Aladin Lite, prefer working with a fixed version. Every build is tagged with a version number and accessible with:
>
> ```https://aladin.cds.unistra.fr/AladinLite/api/v3/<version>/aladin.js```
There is a new in progress API documentation at [this link](https://cds-astro.github.io/aladin-lite).
## Documentation
There is a new API documentation available [here](https://cds-astro.github.io/aladin-lite).
Editable examples showing the API can also be found [here](https://aladin.cds.unistra.fr/AladinLite/doc/API/examples/).
## 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
Please include [the javascript script of Aladin Lite v3](https://aladin.cds.unistra.fr/AladinLite/api/v3/latest/aladin.js) into your project. API differences from the v2 are minimal, here is a snippet of code you can use to embed it into your webpages:
```js
```html
<!doctype html>
<html>
<head>
@@ -59,60 +78,36 @@ Please include [the javascript script of Aladin Lite v3](https://aladin.cds.unis
</html>
```
### Using the aladin lite NPM package
### NPM deployment
First, install it with npm:
A [NPM package](https://www.npmjs.com/package/aladin-lite) is deployed and maintained. It is used by [ipyaladin](https://github.com/cds-astro/ipyaladin), a jupyter widget allowing to run aladin lite in a notebook.
```npm i aladin-lite```
Second, you can use it that way:
```js
<!doctype html>
<html>
<head>
<!-- Mandatory when setting up Aladin Lite v3 for a smartphones/tablet usage -->
<meta name="viewport" content="width=device-width, height=device-height, initial-scale=1.0, user-scalable=no">
</head>
<body>
<div id="aladin-lite-div" style="width: 500px; height: 400px"></div>
Aladin Lite can be imported with:
```html
<script type="module">
import A from 'aladin-lite';
A.init.then(() => {
let aladin = A.aladin('#aladin-lite-div', {fov: 360, projection: "AIT", cooFrame: 'equatorial', showCooGridControl: true, showSimbadPointerControl: true, showCooGrid: true});
});
</script>
</body>
</html>
```
## Goals of v3
## New features
- Rust/WebGL new core integration
- Remove jQuery dep
- UI dev, better support for smartphones
- FITS images support
- easy sharing of current « view »
- support of all VOTable serializations (using votable.js?)
- support of FITS tables?
- creating HiPS instance from an URL
- multiple mirrors handling for HiPS tile retrival
## Source code
Source code is available in the ``src`` directory.
* [X] Rust/WebGL2 new rendering engine
* [X] Remove jQuery dep
* [ ] UI dev, better support for smartphones
* [X] FITS images support
* [X] WCS parsing, displaying an (JPEG/PNG) image in aladin lite view
* [X] Display customized shapes (e.g. proper motions) from astronomical catalog data
* [X] AVM tags parsing support
* [X] Easy sharing of current « view »
* [ ] All VOTable serializations
* [ ] FITS tables
* [X] Creating HiPS instance from an URL
* [X] Local HiPS loading
* [X] Multiple mirrors handling for HiPS tile retrival
* [ ] HiPS cube
## Licence
@@ -120,7 +115,7 @@ Aladin Lite is currently licensed under GPL v3.0
If you think this license might prevent you from using Aladin Lite in your pages/application/portal, please open an issue or [contact us](mailto:cds-question@unistra.fr)
## Contributing
## Contribution guidelines
There are several ways to contribute to Aladin Lite:
@@ -131,12 +126,12 @@ There are several ways to contribute to Aladin Lite:
- **develop new features/provide code fixing bugs**. As open development is a new thing for us, we will in a first time only take into consideration code contribution (_i.e._ Pull Requests) from our close partners.
In any case, please get in touch before starting a major update or rewrite.
### Building the application steps
### Building steps
First you need to install the dependencies from the package.json
Please run:
```bash
```sh
npm install
```
@@ -147,63 +142,73 @@ You will also need [wasm-pack](https://rustwasm.github.io/wasm-pack/), a tool he
Once it's installed you will need to switch to the nightly rust version:
```bash
```sh
rustup default nightly
```
Then you can build the project:
```bash
```sh
npm run build
```
:warning: **If you are experimenting rust error compilations**:
> [!WARNING]
> **If you are experimenting Rust compiling issues:**
> - Make sure you have your **wasm-pack** version updated. To do so:
> ```sh
> cargo install wasm-pack --version ~0.12
> ```
> - Make sure you are using the rust **nightly** toolchain
> ```sh
> rustup default nightly
> ```
> - Remove your `src/core/Cargo.lock` file and `src/core/target` directory -- this ensures that you'd escape any bad compilation state:
> ```sh
> git clean -di
> ```
> - then recompile with `npm run build`.
- Make sure you have your **wasm-pack** version updated. To do so:
It will generate the aladin lite compiled code into a `dist/` directory located at the root of the repository. This directory contains two javascript files. `aladin.umd.cjs` follows the UMD module export convention and it is the one you need to use for your project.
```bash
cargo install wasm-pack --version ~0.12
```
### Testing guidelines
- Make sure you are using the rust **nightly** toolchain
- Remove your `src/core/Cargo.lock` file and `src/core/target` directory -- this ensures that you'd escape any bad compilation state:
#### Run the examples
```bash
git clean -di
```
- then recompile with `npm run build`.
A bunch of examples are located into the `examples` directory.
To run them, start a localhost server:
It will generate the aladin lite compiled code into a `dist/` directory located at the root of the repository. This directory contains two javascript files. `aladin.umd.cjs` follows the UMD module export convention and it is the one you can use for your project.
To run the examples, you can start a localhost server with the following command:
```bash
```sh
npm run serve
```
For just compiling the rust core, from the root location do:
#### Rust tests
```bash
These can be executed separately from the JS part:
* Compile the Rust code:
```sh
cd src/core
cargo check --features webgl2
```
and run the tests:
* Run the tests:
```bash
cd src/core
```sh
cargo test --features webgl2
```
For running the playwright test locally please first install playwright like so:
#### Snapshot comparisons
```bash
We use [playwright](https://playwright.dev/) for snapshot comparison testing. Only ground truth snapshots have been generated for MacOS/Darwin architecture.
First install playwright:
```sh
npx playwright install
```
After that you will be able to run them. These are generated snapshots that will be compared to ground truth snapshots:
Run the tests, advises are given for opening the UI mode or for generating your own ground truth snapshots.
```bash
```sh
npm run test:playwright
```

Binary file not shown.

After

Width:  |  Height:  |  Size: 60 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 62 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 88 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 94 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 69 KiB

View File

@@ -10,8 +10,8 @@
import A from '../src/js/A.js';
let aladin;
A.init.then(() => {
aladin = A.aladin('#aladin-lite-div', {survey: "P/PanSTARRS/DR1/color-z-zg-g", showReticle: false, projection: "AIT", cooFrame: 'icrs', target: "stephan's quintet", fov: 1000, showGotoControl: false, showFrame: false, fullScreen: true, showLayersControl: true, showCooGrid: true, showCooGridControl: false});
aladin = A.aladin('#aladin-lite-div', {showSettingsControl: true, survey: "P/PanSTARRS/DR1/color-z-zg-g", showReticle: false, projection: "AIT", cooFrame: 'icrs', target: "stephan's quintet", fov: 1000, showGotoControl: false, showFrame: false, fullScreen: true, showLayersControl: true, showCooGridControl: false});
aladin.showHealpixGrid(true);
const chft = aladin.createImageSurvey('CFHT', "CFHT deep view of NGC7331 and Stephan's quintet u+g+r", "https://cds.unistra.fr/~derriere/PR_HiPS/2022_Duc/", null, null, {imgFormat: 'png'});
const nircamJWST = aladin.createImageSurvey('Nircam', "Stephans Quintet NIRCam+MIRI", "http://alasky.cds.unistra.fr/JWST/CDS_P_JWST_Stephans-Quintet_NIRCam+MIRI/", null, null, {imgFormat: 'png', colormap: "viridis"});

View File

@@ -12,7 +12,7 @@
import A from '../src/js/A.js';
let aladin;
A.init.then(() => {
aladin = A.aladin('#aladin-lite-div', {target: '00 00 00 +07 00 00', fov: 130, survey: 'P/Mellinger/color', showContextMenu: true, fullScreen: true});
aladin = A.aladin('#aladin-lite-div', {inertia: false, target: '00 00 00 +07 00 00', fov: 130, survey: 'P/Mellinger/color', showContextMenu: true, fullScreen: true});
var moc11 = A.MOCFromURL('http://skies.esac.esa.int/HST/NICMOS/Moc.fits', {color: '#84f', lineWidth: 3}, (moc) => {
// moc is ready
console.log(moc.contains(205.9019247, +2.4492764));

View File

@@ -8,7 +8,7 @@
<script type="module">
import A from '../src/js/A.js';
A.init.then(() => {
aladin = A.aladin('#aladin-lite-div', {projection: 'MOL', fullScreen: true, fov: 360, survey: ['P/DM/vizMine', 'P/HST/GOODS/color', 'P/MATLAS/g'], target: '0 0', showProjectionControl: true, showSettingsControl: true, showCooGrid: true});
aladin = A.aladin('#aladin-lite-div', {projection: 'MOL', fullScreen: true, fov: 360, survey: ['P/DM/vizMine', 'P/HST/GOODS/color', 'P/MATLAS/g'], target: '0 0', showProjectionControl: false, showSettingsControl: false, showLayersControl: false, showCooGrid: false, showFrame: false, showCooLocation: false});
});
</script>

8
jsdoc-custom-style.css Normal file
View File

@@ -0,0 +1,8 @@
nav > h2 {
color: blue;
font-size: medium;
}
nav > ul > li > a {
font-size: medium;
}

View File

@@ -10,13 +10,64 @@
"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
"monospaceLinks": true,
"default": {
"staticFiles": {
"include": [
"./jsdoc-custom-style.css"
]
}
}
},
"opts": {
"readme": "./README.md",
"destination": "./docs/",
"tutorials": "./tutorials"
"template": "node_modules/docdash",
"encoding": "utf8",
"verbose": true
}
}
}

View File

@@ -44,11 +44,12 @@
"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/",
"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: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",

View File

@@ -232,7 +232,7 @@ where
use crate::Abort;
use std::sync::{Arc, Mutex};
impl<I> Image for Arc<Mutex<Option<I>>>
/*impl<I> Image for Arc<Mutex<Option<I>>>
where
I: Image,
{
@@ -249,7 +249,7 @@ where
Ok(())
}
}
}*/
#[cfg(feature = "webgl2")]
use crate::image::format::{R16I, R32I, R64F, R8UI};

View File

@@ -612,7 +612,7 @@ impl App {
//let _depth = tile.cell().depth();
// do not perform tex_sub costly GPU calls while the camera is zooming
if tile.cell().is_root() || included_or_near_coverage {
let is_missing = tile.missing();
//let is_missing = tile.missing();
/*self.tile_fetcher.notify_tile(
&tile,
true,
@@ -626,75 +626,77 @@ impl App {
..
} = tile;
let image = if is_missing {
/*let image = if is_missing {
// Otherwise we push nothing, it is probably the case where:
// - an request error occured on a valid tile
// - the tile is not present, e.g. chandra HiPS have not the 0, 1 and 2 order tiles
None
} else {
Some(image)
};
};*/
use al_core::image::ImageType;
use fitsrs::fits::Fits;
use std::io::Cursor;
if let Some(image) = image.as_ref() {
match &*image.lock().unwrap_abort() {
Some(ImageType::FitsImage {
raw_bytes: raw_bytes_buf,
}) => {
// check if the metadata has not been set
if !cfg.fits_metadata {
let num_bytes = raw_bytes_buf.length() as usize;
let mut raw_bytes = vec![0; num_bytes];
raw_bytes_buf.copy_to(&mut raw_bytes[..]);
//if let Some(image) = image.as_ref() {
match &*image.lock().unwrap_abort() {
Some(ImageType::FitsImage {
raw_bytes: raw_bytes_buf,
}) => {
// check if the metadata has not been set
if !cfg.fits_metadata {
let num_bytes = raw_bytes_buf.length() as usize;
let mut raw_bytes = vec![0; num_bytes];
raw_bytes_buf.copy_to(&mut raw_bytes[..]);
let mut bytes_reader =
Cursor::new(raw_bytes.as_slice());
let Fits { hdu } =
Fits::from_reader(&mut bytes_reader)
.map_err(|_| {
JsValue::from_str(
"Parsing fits error",
)
})?;
let mut bytes_reader =
Cursor::new(raw_bytes.as_slice());
let Fits { hdu } =
Fits::from_reader(&mut bytes_reader).map_err(
|_| JsValue::from_str("Parsing fits error"),
)?;
let header = hdu.get_header();
let bscale = if let Some(
fitsrs::card::Value::Float(bscale),
) = header.get(b"BSCALE ")
{
*bscale as f32
} else {
1.0
};
let bzero = if let Some(
fitsrs::card::Value::Float(bzero),
) = header.get(b"BZERO ")
{
*bzero as f32
} else {
0.0
};
let blank = if let Some(
fitsrs::card::Value::Float(blank),
) = header.get(b"BLANK ")
{
*blank as f32
} else {
std::f32::NAN
};
let header = hdu.get_header();
let bscale = if let Some(
fitsrs::card::Value::Float(bscale),
) = header.get(b"BSCALE ")
{
*bscale as f32
} else {
1.0
};
let bzero = if let Some(
fitsrs::card::Value::Float(bzero),
) = header.get(b"BZERO ")
{
*bzero as f32
} else {
0.0
};
let blank = if let Some(
fitsrs::card::Value::Float(blank),
) = header.get(b"BLANK ")
{
*blank as f32
} else {
std::f32::NAN
};
cfg.set_fits_metadata(bscale, bzero, blank);
}
cfg.set_fits_metadata(bscale, bzero, blank);
}
_ => (),
}
}
_ => (),
};
//}
survey.add_tile(&cell, image, time_req)?;
self.request_redraw = true;
match &*image.lock().unwrap_abort() {
Some(img) => {
survey.add_tile(&cell, img, time_req)?;
self.request_redraw = true;
self.time_start_blending = Time::now();
self.time_start_blending = Time::now();
}
None => (),
};
}
}
}

View File

@@ -662,7 +662,7 @@ impl HiPS {
))
}
} else {
None
unreachable!();
}
} else {
None
@@ -679,8 +679,8 @@ impl HiPS {
let uv_1 = TileUVW::new(cell, ending_texture, cfg);
let start_time = ending_texture.start_time().as_millis();
let miss_0 = (starting_texture.is_missing()) as i32 as f32;
let miss_1 = (ending_texture.is_missing()) as i32 as f32;
let miss_0 = (false) as i32 as f32;
let miss_1 = (false) as i32 as f32;
let num_subdivision = num_subdivision(cell, camera, projection);
@@ -817,7 +817,7 @@ impl HiPS {
pub fn add_tile<I: Image + Debug>(
&mut self,
cell: &HEALPixCell,
image: Option<I>,
image: I,
time_request: Time,
) -> Result<(), JsValue> {
self.textures.push(&cell, image, time_request)

View File

@@ -289,7 +289,7 @@ impl ImageSurveyTextures {
let mutex_locked = image.lock().unwrap_abort();
let images = mutex_locked.as_ref().unwrap_abort();
for (idx, image) in images.iter().enumerate() {
self.push(&HEALPixCell(depth_tile, idx as u64), Some(image), time_req)?;
self.push(&HEALPixCell(depth_tile, idx as u64), image, time_req)?;
}
}
@@ -310,7 +310,7 @@ impl ImageSurveyTextures {
pub fn push<I: Image + std::fmt::Debug>(
&mut self,
cell: &HEALPixCell,
image: Option<I>,
image: I,
time_request: Time,
) -> Result<(), JsValue> {
if !self.contains_tile(cell) {
@@ -381,7 +381,7 @@ impl ImageSurveyTextures {
&mut self.base_textures[idx as usize]
};
let missing = image.is_none();
//let missing = image.is_none();
send_to_gpu(
cell,
texture,
@@ -393,7 +393,7 @@ impl ImageSurveyTextures {
texture.append(
cell, // The tile cell
&self.config,
missing,
//missing,
);
self.available_tiles_during_frame = true;
@@ -629,7 +629,7 @@ impl ImageSurveyTextures {
fn send_to_gpu<I: Image>(
cell: &HEALPixCell,
texture: &Texture,
image: Option<I>,
image: I,
texture_array: &Texture2DArray,
cfg: &mut HiPSConfig,
) -> Result<(), JsValue> {
@@ -663,12 +663,9 @@ fn send_to_gpu<I: Image>(
idx_slice,
);
if let Some(image) = image {
image.tex_sub_image_3d(&texture_array, &offset)
} else {
cfg.get_default_image()
.tex_sub_image_3d(&texture_array, &offset)
}
image.tex_sub_image_3d(&texture_array, &offset)?;
Ok(())
}
impl SendUniforms for ImageSurveyTextures {

View File

@@ -31,7 +31,7 @@ pub struct Texture {
num_tiles_written: usize,
// Flag telling whether the texture is available
// for drawing
missing: bool,
//missing: bool,
}
use super::config::HiPSConfig;
@@ -44,7 +44,7 @@ impl Texture {
let full = false;
let texture_cell = *texture_cell;
let uniq = texture_cell.uniq();
let missing = true;
//let missing = true;
let num_tiles_written = 0;
Texture {
texture_cell,
@@ -55,19 +55,19 @@ impl Texture {
start_time,
full,
num_tiles_written,
missing,
//missing,
}
}
// Panic if cell is not contained in the texture
// Do nothing if the texture is full
// Return true if the tile is newly added
pub fn append(&mut self, cell: &HEALPixCell, cfg: &HiPSConfig, missing: bool) {
pub fn append(&mut self, cell: &HEALPixCell, cfg: &HiPSConfig /*, missing: bool */) {
let texture_cell = cell.get_texture_cell(cfg.delta_depth());
debug_assert!(texture_cell == self.texture_cell);
debug_assert!(!self.full);
self.missing &= missing;
//self.missing &= missing;
//self.start_time = Some(Time::now());
//self.full = true;
let num_tiles_per_texture = cfg.num_tiles_per_texture();
@@ -127,9 +127,9 @@ impl Texture {
self.idx
}
pub fn is_missing(&self) -> bool {
/*pub fn is_missing(&self) -> bool {
self.missing
}
}*/
// Setter
pub fn replace(&mut self, texture_cell: &HEALPixCell, time_request: Time) {
@@ -143,7 +143,7 @@ impl Texture {
self.start_time = None;
self.time_request = time_request;
self.tiles.clear();
self.missing = true;
//self.missing = true;
self.num_tiles_written = 0;
}
@@ -187,9 +187,7 @@ impl<'a> TextureUniforms<'a> {
}
}
use al_core::{
shader::{SendUniforms, ShaderBound},
};
use al_core::shader::{SendUniforms, ShaderBound};
impl<'a> SendUniforms for TextureUniforms<'a> {
fn attach_uniforms<'b>(&self, shader: &'b ShaderBound<'b>) -> &'b ShaderBound<'b> {
shader
@@ -200,7 +198,8 @@ impl<'a> SendUniforms for TextureUniforms<'a> {
)
.attach_uniform(
&format!("{}{}", self.name, "empty"),
&((self.texture.missing as u8) as f32),
//&((self.texture.full as u8) as f32),
&0.0,
)
.attach_uniform(
&format!("{}{}", self.name, "start_time"),

View File

@@ -23,34 +23,64 @@ pub struct TileFetcherQueue {
#[derive(Debug)]
#[wasm_bindgen]
pub struct HiPSLocalFiles {
paths: Box<[HashMap<u64, web_sys::File>]>,
tiles: Box<[Box<[HashMap<u64, web_sys::File>]>; 4]>,
moc: web_sys::File,
}
use crate::tile_fetcher::query::Tile;
use crate::HEALPixCell;
use al_api::hips::ImageExt;
use al_core::image::format::ImageFormatType;
use wasm_bindgen::prelude::wasm_bindgen;
use wasm_bindgen::JsValue;
#[wasm_bindgen]
impl HiPSLocalFiles {
#[wasm_bindgen(constructor)]
pub fn new() -> Self {
let paths = vec![HashMap::new(); 30].into_boxed_slice();
pub fn new(moc: web_sys::File) -> Self {
let tiles_per_fmt = vec![HashMap::new(); 30].into_boxed_slice();
Self { paths }
Self {
tiles: Box::new([
tiles_per_fmt.clone(),
tiles_per_fmt.clone(),
tiles_per_fmt.clone(),
tiles_per_fmt,
]),
moc,
}
}
pub fn insert(&mut self, depth: u8, ipix: u64, file: web_sys::File) {
self.paths[depth as usize].insert(ipix, file);
pub fn insert(&mut self, depth: u8, ipix: u64, ext: ImageExt, file: web_sys::File) {
let mut tiles_per_fmt = match ext {
ImageExt::Fits => &mut self.tiles[0],
ImageExt::Jpeg => &mut self.tiles[1],
ImageExt::Png => &mut self.tiles[2],
ImageExt::Webp => &mut self.tiles[3],
};
tiles_per_fmt[depth as usize].insert(ipix, file);
}
fn get(&self, cell: &HEALPixCell) -> Option<&web_sys::File> {
fn get_tile(&self, cell: &HEALPixCell, ext: ImageExt) -> Option<&web_sys::File> {
let d = cell.depth() as usize;
let i = cell.idx();
return self.paths[d].get(&i);
let tiles_per_fmt = match ext {
ImageExt::Fits => &self.tiles[0],
ImageExt::Jpeg => &self.tiles[1],
ImageExt::Png => &self.tiles[2],
ImageExt::Webp => &self.tiles[3],
};
return tiles_per_fmt[d].get(&i);
}
fn get_moc(&self) -> &web_sys::File {
&self.moc
}
}
use crate::renderable::CreatorDid;
impl TileFetcherQueue {
pub fn new() -> Self {
@@ -116,9 +146,11 @@ impl TileFetcherQueue {
}
fn check_in_file_list(&self, mut query: Tile) -> Result<Tile, JsValue> {
if let Some(files) = self.hips_local_files.get(&query.hips_cdid) {
if let Some(file) = files.get(&query.cell) {
if let Ok(url) = web_sys::Url::create_object_url_with_blob(file.as_ref()) {
if let Some(local_hips) = self.hips_local_files.get(&query.hips_cdid) {
if let Some(tile) =
local_hips.get_tile(&query.cell, query.format.get_ext_file().clone())
{
if let Ok(url) = web_sys::Url::create_object_url_with_blob(tile.as_ref()) {
// rewrite the url
query.url = url;
Ok(query)
@@ -166,8 +198,21 @@ impl TileFetcherQueue {
// The allsky is not mandatory present in a HiPS service but it is better to first try to search for it
//downloader.fetch(query::PixelMetadata::new(cfg));
// Try to fetch the MOC
let hips_cdid = cfg.get_creator_did();
let moc_url = if let Some(local_hips) = self.hips_local_files.get(hips_cdid) {
if let Ok(url) =
web_sys::Url::create_object_url_with_blob(local_hips.get_moc().as_ref())
{
url
} else {
format!("{}/Moc.fits", cfg.get_root_url())
}
} else {
format!("{}/Moc.fits", cfg.get_root_url())
};
downloader.borrow_mut().fetch(query::Moc::new(
format!("{}/Moc.fits", cfg.get_root_url()),
moc_url,
cfg.get_creator_did().to_string(),
al_api::moc::MOC::default(),
));

View File

@@ -112,12 +112,12 @@ A.aladin = function (divSelector, options) {
* @function
* @name A.HiPS
* @memberof A
* @param {string|FileList|Object} id - Can be: <br/>
* - an http url <br/>
* - a relative path to your HiPS <br/>
* - a special ID pointing towards a HiPS. One can found the list of IDs {@link https://aladin.cds.unistra.fr/hips/list| here} <br/>
* - a dict storing a local HiPS files. This object contains a tile file: hips[order][ipix] = File and refers to the properties file like so: hips["properties"] = File. <br/>
* A javascript FileList pointing to the opened webkit directory is also accepted.
* @param {string} id - Can be:
* <ul>
* <li>An http url towards a HiPS.</li>
* <li>A relative path to your HiPS</li>
* <li>A special ID pointing towards a HiPS. One can found the list of IDs {@link https://aladin.cds.unistra.fr/hips/list| here}</li>
* </ul>
* @param {HiPSOptions} [options] - Options describing the survey
* @returns {HiPS} - A HiPS image object
*/

View File

@@ -1500,10 +1500,13 @@ export let Aladin = (function () {
* @memberof Aladin
* @param {string} id - Mandatory unique identifier for the survey.
* @param {string} [name] - A convinient name for the survey, optional
* @param {string} url - Can be:
* @param {string|FileList|HiPSLocalFiles} url - Can be:
* <ul>
* <li>1. An url that refers to a HiPS.</li>
* <li>Or it can be a "CDS ID" that refers to a HiPS. One can found the list of IDs {@link https://aladin.cds.unistra.fr/hips/list| here}</li>
* <li>An http url towards a HiPS.</li>
* <li>A relative path to your HiPS</li>
* <li>A special ID pointing towards a HiPS. One can found the list of IDs {@link https://aladin.cds.unistra.fr/hips/list| here}</li>
* <li>A dict storing a local HiPS files. This object contains a tile file: hips[order][ipix] = File and refers to the properties file like so: hips["properties"] = File. </li>
* A javascript {@link FileList} pointing to the opened webkit directory is also accepted.
* </ul>
* @param {string} [cooFrame] - Values accepted: 'equatorial', 'icrs', 'icrsd', 'j2000', 'gal', 'galactic'
* @param {number} [maxOrder] - The maximum HEALPix order of the HiPS, i.e the HEALPix order of the most refined tile images of the HiPS.
@@ -1534,10 +1537,13 @@ export let Aladin = (function () {
* @static
* @param {string} id - Mandatory unique identifier for the survey.
* @param {string} [name] - A convinient name for the survey, optional
* @param {string} url - Can be:
* @param {string|FileList|HiPSLocalFiles} url - Can be:
* <ul>
* <li>1. An url that refers to a HiPS.</li>
* <li>Or it can be a "CDS ID" that refers to a HiPS. One can found the list of IDs {@link https://aladin.cds.unistra.fr/hips/list| here}</li>
* <li>An http url towards a HiPS.</li>
* <li>A relative path to your HiPS</li>
* <li>A special ID pointing towards a HiPS. One can found the list of IDs {@link https://aladin.cds.unistra.fr/hips/list| here}</li>
* <li>A dict storing a local HiPS files. This object contains a tile file: hips[order][ipix] = File and refers to the properties file like so: hips["properties"] = File. </li>
* A javascript {@link FileList} pointing to the opened webkit directory is also accepted.
* </ul>
* @param {string} [cooFrame] - Values accepted: 'equatorial', 'icrs', 'icrsd', 'j2000', 'gal', 'galactic'
* @param {number} [maxOrder] - The maximum HEALPix order of the HiPS, i.e the HEALPix order of the most refined tile images of the HiPS.
@@ -1581,7 +1587,7 @@ export let Aladin = (function () {
* <ul>
* <li>1. An url that refers to a HiPS</li>
* <li>2. Or it can be a CDS identifier that refers to a HiPS. One can found the list of IDs {@link https://aladin.cds.unistra.fr/hips/list| here}</li>
* <li>3. A {@link HiPS} HiPS object created from {@link A.HiPS}</li>
* <li>3. A {@link HiPS} HiPS object</li>
* <li>4. A {@link Image} Image object</li>
* </ul>
*/
@@ -1649,8 +1655,9 @@ export let Aladin = (function () {
* @memberof Aladin
* @param {string} id - Can be:
* <ul>
* <li>1. An url that refers to a HiPS.</li>
* <li>Or it can be a "CDS ID" that refers to a HiPS. One can found the list of IDs {@link https://aladin.cds.unistra.fr/hips/list| here}</li>
* <li>An http url towards a HiPS.</li>
* <li>A relative path to your HiPS</li>
* <li>A special ID pointing towards a HiPS. One can found the list of IDs {@link https://aladin.cds.unistra.fr/hips/list| here}</li>
* </ul>
* @param {HiPSOptions} [options] - Options for rendering the image
* @param {function} [success] - A success callback

View File

@@ -153,8 +153,8 @@ export let AladinUtils = {
*/
Sesame: {
/**
* find RA, DEC for any target (object name or position) <br/>
* if successful, callback is called with an object {ra: ra-value, dec: dec-value} <br/>
* Find RA, DEC for any target (object name or position) <br/>
* if successful, callback is called with an object {ra: {@link number}, dec: {@link number}} <br/>
* if not successful, errorCallback is called
*
* @function
@@ -162,8 +162,8 @@ export let AladinUtils = {
* @name resolveAstronomicalName
*
* @param {string} target - object name or position
* @param {Function} callback - if successful, callback is called with an object {ra: <ra-value>, dec: <dec-value>}
* @param {Function} errorCallback - if not successful, errorCallback is called
* @param {Function} callback - if successful, callback is called with an object {ra: {@link number}, dec: {@link number}}
* @param {Function} errorCallback - if not successful, errorCallback is called with the error
*/
resolveAstronomicalName: Sesame.getTargetRADec
},
@@ -182,20 +182,21 @@ 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[]} xy - A 2 elements array representing the screen coordinates [X, Y] in pixels.
* @returns {number[]} 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 a formattes string
* @return the formatted string
*
* @example <caption> Result in degrees </caption>
* // returns "1°"

View File

@@ -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,10 +155,19 @@ PropertyParser.isPlanetaryBody = function (properties) {
*/
/**
* @typedef {Object} FileList
*
* JS {@link https://developer.mozilla.org/fr/docs/Web/API/FileList| FileList} API type
*
* @typedef {Object} FileList
*/
/**
* 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
*/
export let HiPS = (function () {
/**
* The object describing an image survey
@@ -167,12 +176,14 @@ export let HiPS = (function () {
* @constructs HiPS
*
* @param {string} id - Mandatory unique identifier for the layer. Can be an arbitrary name
* @param {string|FileList|Object} location - Can be:
* - an http url <br/>
* - a relative path to your HiPS <br/>
* - a special ID pointing towards a HiPS. One can found the list of IDs {@link https://aladin.cds.unistra.fr/hips/list| here} <br/>
* - a dict storing a local HiPS. This object contains a tile file: hips[order][ipix] = File and refers to the properties file like so: hips["properties"] = File. <br/>
* A javascript FileList pointing to the opened webkit directory is also accepted.
* @param {string|FileList|HiPSLocalFiles} url - Can be:
* <ul>
* <li>An http url towards a HiPS.</li>
* <li>A relative path to your HiPS</li>
* <li>A special ID pointing towards a HiPS. One can found the list of IDs {@link https://aladin.cds.unistra.fr/hips/list| here}</li>
* <li>A dict storing a local HiPS files. This object contains a tile file: hips[order][ipix] = File and refers to the properties file like so: hips["properties"] = File. </li>
* A javascript {@link FileList} pointing to the opened webkit directory is also accepted.
* </ul>
* @param {HiPSOptions} [options] - The option for the survey
*
* @description Giving a CDS ID will do a query to the MOCServer first to retrieve metadata. Then it will also check for the presence of faster HiPS nodes to choose a faster url to query to tiles from.
@@ -187,27 +198,38 @@ export let HiPS = (function () {
this.startUrl = options.startUrl;
if (location instanceof FileList) {
let files = {};
let localFiles = {};
for (var file of location) {
let path = file.webkitRelativePath;
if (path.includes("Norder") && path.includes("Npix")) {
const order = +path.substring(path.indexOf("Norder") + 6).split("/")[0];
if (!files[order]) {
files[order] = {}
if (!localFiles[order]) {
localFiles[order] = {}
}
const ipix = +path.substring(path.indexOf("Npix") + 4).split(".")[0];
files[order][ipix] = file;
let tile = path.substring(path.indexOf("Npix") + 4).split(".");
const ipix = +tile[0];
const fmt = tile[1];
if (!localFiles[order][ipix]) {
localFiles[order][ipix] = {}
}
localFiles[order][ipix][fmt] = file;
}
if (path.includes("properties")) {
files['properties'] = file;
localFiles['properties'] = file;
}
if (path.includes("Moc")) {
localFiles['moc'] = file;
}
}
this.files = files;
this.localFiles = localFiles;
} else if (location instanceof Object) {
this.files = location;
this.localFiles = location;
}
this.url = location;
@@ -434,17 +456,17 @@ export let HiPS = (function () {
}
this.view = view;
if (this.files) {
if (this.localFiles) {
// Fetch the properties file
self.query = (async () => {
// look for the properties file
await HiPSProperties.fetchFromFile(self.files["properties"])
await HiPSProperties.fetchFromFile(self.localFiles["properties"])
.then((p) => {
self._parseProperties(p);
self.url = "local";
delete self.files["properties"]
delete self.localFiles["properties"]
})
return self;
@@ -873,12 +895,27 @@ export let HiPS = (function () {
};
let localFiles;
if (this.files) {
localFiles = new Aladin.wasmLibs.core.HiPSLocalFiles();
for (var order in this.files) {
for (var ipix in this.files[order]) {
const file = this.files[order][ipix];
localFiles.insert(+order, BigInt(+ipix), file)
if (this.localFiles) {
localFiles = new Aladin.wasmLibs.core.HiPSLocalFiles(this.localFiles["moc"]);
let fmt;
for (var order in this.localFiles) {
if (order === "moc")
continue;
for (var ipix in this.localFiles[order]) {
for (var f in this.localFiles[order][ipix]) {
if (f === "png") {
fmt = Aladin.wasmLibs.core.ImageExt.Png;
} else if (f === "fits") {
fmt = Aladin.wasmLibs.core.ImageExt.Fits;
} else {
fmt = Aladin.wasmLibs.core.ImageExt.Jpeg;
}
const tileFile = this.localFiles[order][+ipix][f];
localFiles.insert(+order, BigInt(+ipix), fmt, tileFile)
}
}
}
}

View File

@@ -31,6 +31,36 @@ import { Utils } from "./Utils";
import { AVM } from "./libs/avm.js";
import { HiPS } from "./HiPS.js";
/**
* @typedef {Object} WCS
*
* {@link https://ui.adsabs.harvard.edu/abs/2002A%26A...395.1077C/abstract|FITS (Paper II)}, Calabretta, M. R., and Greisen, E. W., Astronomy & Astrophysics, 395, 1077-1122, 2002
*
* @property {number} [NAXIS]
* @property {string} CTYPE1
* @property {string} [CTYPE2]
* @property {number} [LONPOLE]
* @property {number} [LATPOLE]
* @property {number} [CRVAL1]
* @property {number} [CRVAL2]
* @property {number} [CRPIX1]
* @property {number} [CRPIX2]
* @property {string} [CUNIT1] - e.g. 'deg'
* @property {string} [CUNIT2] - e.g. 'deg'
* @property {number} [CD1_1]
* @property {number} [CD1_2]
* @property {number} [CD2_1]
* @property {number} [CD2_2]
* @property {number} [PC1_1]
* @property {number} [PC1_2]
* @property {number} [PC2_1]
* @property {number} [PC2_2]
* @property {number} [CDELT1]
* @property {number} [CDELT2]
* @property {number} [NAXIS1]
* @property {number} [NAXIS2]
*/
/**
* @typedef {Object} ImageOptions
*
@@ -48,7 +78,7 @@ import { HiPS } from "./HiPS.js";
* @property {number} [saturation=0.0] - The saturation value for the color configuration.
* @property {number} [brightness=0.0] - The brightness value for the color configuration.
* @property {number} [contrast=0.0] - The contrast value for the color configuration.
* @property {Object} [wcs] - an object describing the WCS of the image. In case of a fits image
* @property {WCS} [wcs] - an object describing the WCS of the image. In case of a fits image
* this property will be ignored as the WCS taken will be the one present in the fits file.
* @property {string} [imgFormat] - Optional image format. Giving it will prevent the auto extension determination algorithm to be triggered. Possible values are 'jpeg', 'png' or 'fits'. tiff files are not supported. You can convert your tiff files to jpg ones by using the fantastic image magick suite.
*

View File

@@ -222,8 +222,8 @@ export let MOC = (function() {
/**
* Serialize a MOC into different format
*
* @memberof Aladin
* @param {string} [format='json'] - The output format. Only json is currently supported but 'fits' could be added.
* @memberof MOC
* @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) {

View File

@@ -1284,12 +1284,12 @@ export let View = (function () {
/**
* redraw the whole view
*/
View.prototype.redraw = function () {
View.prototype.redraw = function (timestamp) {
// request another frame
// Elapsed time since last loop
const now = performance.now();
const elapsedTime = now - this.then;
const elapsedTime = now - timestamp;
this.dt = elapsedTime;
this.moving = this.wasm.update(elapsedTime);
@@ -1300,6 +1300,7 @@ export let View = (function () {
this.throttledPositionChanged(false);
}
////// 2. Draw catalogues////////
const isViewRendering = this.wasm.isRendering();
if (isViewRendering || this.needRedraw) {
@@ -1307,7 +1308,6 @@ export let View = (function () {
}
this.needRedraw = false;
this.then = now;
//this.then = now % View.FPS_INTERVAL;
requestAnimFrame(this.redrawClbk);
};