Compare commits
51 Commits
feature/pl
...
select-imp
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0c9c4cf9a9 | ||
|
|
ea8af8acb3 | ||
|
|
fac6c045f4 | ||
|
|
b778ce380a | ||
|
|
d3e0bb4fbc | ||
|
|
81e1eaddee | ||
|
|
51af4fa2f4 | ||
|
|
ef86dbd06d | ||
|
|
ea174b2182 | ||
|
|
1ad6c54538 | ||
|
|
7d3eb6e80a | ||
|
|
61f9af1415 | ||
|
|
2395cb9425 | ||
|
|
f5da11c337 | ||
|
|
d61b56c0ce | ||
|
|
9c9ebb0ed1 | ||
|
|
64d013618f | ||
|
|
504cab42bb | ||
|
|
bf726088ad | ||
|
|
4fe70f09bb | ||
|
|
aa8c31d246 | ||
|
|
1271e5f0ac | ||
|
|
afca8f8be9 | ||
|
|
f5ac0dff61 | ||
|
|
959c99fb19 | ||
|
|
8ee161481e | ||
|
|
3c23b6dbe9 | ||
|
|
b4f905e745 | ||
|
|
4c48d9a2a1 | ||
|
|
7379e81277 | ||
|
|
3f1702b34c | ||
|
|
aacf79007e | ||
|
|
180b63db37 | ||
|
|
46cf0c2939 | ||
|
|
69b2254542 | ||
|
|
c9b1fc5f7f | ||
|
|
0024032861 | ||
|
|
b9fb0b7e53 | ||
|
|
38d14e2239 | ||
|
|
b0bf7da8b1 | ||
|
|
3dd2c19c4c | ||
|
|
f0d9ccaf0f | ||
|
|
6021514ab7 | ||
|
|
e6acb80f4d | ||
|
|
bd1b165971 | ||
|
|
4eb2dece2c | ||
|
|
c89156211b | ||
|
|
e0726590ab | ||
|
|
45f77feeb1 | ||
|
|
b8820d6f19 | ||
|
|
98d877b937 |
14
CHANGELOG.md
@@ -1,15 +1,25 @@
|
||||
# Changelogs
|
||||
|
||||
## [Unreleased]
|
||||
## 3.5.1-beta
|
||||
|
||||
* [feat] Add support for name removing in `removeOverlay` method
|
||||
* [test] Add support of playwright. Instructions in the readme for running the test matching snapshots [PR #176]
|
||||
* [fix] Order of overlays in the stack now matches the addMOC/addCatalog/addOverlay calls ordering
|
||||
* [doc] Expose the API of Coo class
|
||||
* [fix] Insert aladin css inside the aladin lite so that it should be compliant with the use of shadow DOMs [cds-astro/ipyaladin#113], [marimo-team/marimo#2106]
|
||||
* [feat] Add possibility of giving a local JS FileList to load a locally-stored HiPS without starting an HTTP server [cds-astro/aladin-lite#103]
|
||||
* [fix] removeOverlayByName
|
||||
|
||||
## 3.5.0-beta
|
||||
|
||||
* [enhancement] add `options.colnames` to A.catalogFromVizieR to tell VizieR we want absolutely want to retrieve specific columns
|
||||
* [feat] provide a new drawAxes option to A.ellipse. This is useful for plotting error ellipsis.
|
||||
|
||||
## 3.4.5-beta
|
||||
|
||||
* [feat] add `layerChanged` event when a layer is added or removed
|
||||
* [deprecate] of `select` event, use `objectsSelected` event instead
|
||||
* [ui] add the ability to switch the tile format to download
|
||||
* [ui] add the ability to switch the tile format to download
|
||||
|
||||
## 3.4.3-beta
|
||||
|
||||
|
||||
169
README.md
@@ -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,20 +12,37 @@ 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.
|
||||
|
||||
[](https://github.com/cds-astro/aladin-lite/actions/workflows/test.yml)
|
||||
[](https://cds-astro.github.io/aladin-lite)
|
||||
[](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
|
||||
@@ -36,7 +53,7 @@ You can embed Aladin Lite it into your webpages in two ways
|
||||
|
||||
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 +76,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 +113,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 +124,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 +140,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
|
||||
```
|
||||
|
||||
|
After Width: | Height: | Size: 1.4 MiB |
BIN
assets/vignettes/Basic Aladin Lite instanciation.png
Normal file
|
After Width: | Height: | Size: 1.2 MiB |
BIN
assets/vignettes/Display proper motions.png
Normal file
|
After Width: | Height: | Size: 1.9 MiB |
BIN
assets/vignettes/Load SIMBAD & NED catalogs data.png
Normal file
|
After Width: | Height: | Size: 1.3 MiB |
BIN
assets/vignettes/Load a FITS image.png
Normal file
|
After Width: | Height: | Size: 962 KiB |
BIN
assets/vignettes/Visualization of Mars.png
Normal file
|
After Width: | Height: | Size: 1.6 MiB |
@@ -8,8 +8,8 @@
|
||||
"dateModified": "2023-01-31",
|
||||
"issueTracker": "https://github.com/cds-astro/aladin-lite/issues",
|
||||
"name": "Aladin Lite",
|
||||
"version": "3.4.5",
|
||||
"softwareVersion": "3.4.5",
|
||||
"version": "3.5.1-beta",
|
||||
"softwareVersion": "3.5.1-beta",
|
||||
"description": "An astronomical HiPS visualizer in the browser.",
|
||||
"identifier": "10.5281/zenodo.7638833",
|
||||
"applicationCategory": "Astronomy, Visualization",
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
let aladin;
|
||||
A.init.then(() => {
|
||||
aladin = A.aladin('#aladin-lite-div', { fov:0.15, target: 'Arp 240', showReticle: false, fullScreen: true });
|
||||
aladin.setBaseImageLayer(aladin.newImageSurvey('P/SDSS9/g', {colormap: "rainbow", stretch: "Linear"}));
|
||||
aladin.setBaseImageLayer(aladin.newImageSurvey('P/SDSS9/g', {colormap: "eosb", stretch: "linear"}));
|
||||
|
||||
var simbad = A.catalog({name: 'Simbad', sourceSize: 16, color: '#4050F0'});
|
||||
aladin.addCatalog(simbad);
|
||||
|
||||
@@ -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-i-r-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"});
|
||||
|
||||
|
||||
@@ -14,11 +14,11 @@ A.init.then(() => {
|
||||
hscGreenSurvey.setColormap("green", { stretch: "asinh" });
|
||||
hscGreenSurvey.setCuts(-0.2186, 5.30322);
|
||||
|
||||
const HSCRedSurvey = aladin.newImageSurvey('CDS/P/HSC/DR2/deep/r', {imgFormat: 'fits', colormap: "red", minCut: 0.34228, maxCut: 2.75785, additive: true, stretch: "asinh"});
|
||||
const HSCBlueSurvey = aladin.newImageSurvey('CDS/P/HSC/DR2/deep/z', {imgFormat: 'fits', colormap: "blue", minCut: -0.01218, maxCut: 2.27397, additive: true, stretch: "asinh"});
|
||||
const HSCRedSurvey = aladin.newImageSurvey('P/HSC/DR2/deep/r', {imgFormat: 'fits', colormap: "red", minCut: 0.34228, maxCut: 2.75785, additive: true, stretch: "asinh"});
|
||||
const HSCBlueSurvey = aladin.newImageSurvey('P/HSC/DR2/deep/z', {imgFormat: 'fits', colormap: "blue", minCut: -0.01218, maxCut: 2.27397, additive: true, stretch: "asinh"});
|
||||
|
||||
aladin.setOverlayImageLayer('CDS/P/HSC/DR2/deep/r', 'hsc red layer');
|
||||
aladin.setOverlayImageLayer('CDS/P/HSC/DR2/deep/z', 'hsc blue layer');
|
||||
aladin.setOverlayImageLayer('P/HSC/DR2/deep/r', 'hsc red layer');
|
||||
aladin.setOverlayImageLayer('P/HSC/DR2/deep/z', 'hsc blue layer');
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div id="aladin-lite-div" style="width: 1024px; height: 100%;"></div>
|
||||
<div id="aladin-lite-div" style="width: 1024px; height: 256px;"></div>
|
||||
|
||||
<script type="module">
|
||||
import A from '../src/js/A.js';
|
||||
|
||||
@@ -4,13 +4,13 @@
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div id="aladin-lite-div" style="width: 1024px; height: 768px"></div>
|
||||
<div id="aladin-lite-div" style="width: 512px; height: 512px"></div>
|
||||
|
||||
<script type="module">
|
||||
import A from '../src/js/A.js';
|
||||
let aladin;
|
||||
A.init.then(() => {
|
||||
aladin = A.aladin('#aladin-lite-div', {survey: "data/hips/PanSTARRS_DR1_color-z-zg-g", fov:1.5, target: "NGC 7318B", showSettingsControl: true});
|
||||
aladin = A.aladin('#aladin-lite-div', {survey: "data/hips/PanSTARRS_DR1_color-z-zg-g", fov:2.0, target: "22 35 58.39 +33 57 57.8", showSettingsControl: true, log: false});
|
||||
aladin.setProjection('AIT');
|
||||
let cfht = aladin.createImageSurvey("CFHT", "CFHT MegaCam u+g+r", "./data/hips/CFHT", "equatorial", 10, {imgFormat: 'png'});
|
||||
let jwst1 = aladin.createImageSurvey("CDS/P/JWST/Stephans-Quintet/NIRCam+MIRI", "JWST NIRCam+MIRI", "data/hips/JWST_NIRCam_MIRI", null, null, {imgFormat: 'png'});
|
||||
|
||||
@@ -5,13 +5,13 @@
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div id="aladin-lite-div" style="width:440px;height:300px"></div>
|
||||
<div id="aladin-lite-div" style="width: 512px; height: 512px"></div>
|
||||
|
||||
<script type="module">
|
||||
import A from '../src/js/A.js';
|
||||
let aladin;
|
||||
A.init.then(() => {
|
||||
aladin = A.aladin('#aladin-lite-div', {cooFrame: "icrs", fullScreen: true});
|
||||
aladin = A.aladin('#aladin-lite-div', {cooFrame: "icrs", log: false});
|
||||
|
||||
aladin.displayFITS(
|
||||
//'https://fits.gsfc.nasa.gov/samples/FOCx38i0101t_c0f.fits', // url of the fits file
|
||||
|
||||
38
examples/al-error-ellipse.html
Normal file
@@ -0,0 +1,38 @@
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div id="aladin-lite-div" style="width: 500px; height: 400px"></div>
|
||||
<link rel="stylesheet" href="https://cdn.datatables.net/1.13.4/css/jquery.dataTables.min.css">
|
||||
|
||||
<script type="module">
|
||||
import A from '../src/js/A.js';
|
||||
var aladin;
|
||||
A.init.then(() => {
|
||||
aladin = A.aladin('#aladin-lite-div', {survey: 'https://alasky.cds.unistra.fr/DSS/DSSColor/', target: 'm1', fov: 5, showContextMenu: true, showSettingsControl:true, samp:true});
|
||||
|
||||
var customImg = new Image();
|
||||
customImg.onload = function() {
|
||||
const cat = A.catalogFromVizieR('II/246/out', 'm1', 0.1, {onClick: 'showTable', hoverColor: 'purple', limit: 1000, colnames: ["errMin", "errMaj", "errPA"], shape: (s) => {
|
||||
if (+s.data['Jmag'] > 15) {
|
||||
return;
|
||||
} else {
|
||||
let a = +s.data['errMaj']/36;
|
||||
let b = +s.data['errMin']/36;
|
||||
|
||||
let theta = +s.data['errPA'];
|
||||
|
||||
return A.ellipse(s.ra, s.dec, a, b, theta, {fillColor: 'rgba(255, 0, 255, 0.2)', drawAxes: true})
|
||||
}
|
||||
}});
|
||||
aladin.addCatalog(cat);
|
||||
};
|
||||
customImg.src = 'https://aladin.u-strasbg.fr/AladinLite/doc/API/examples/img/star.png';
|
||||
|
||||
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -4,14 +4,13 @@
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<script src="https://code.jquery.com/jquery-1.10.1.min.js"></script>
|
||||
<div id='infoDiv'> </div>
|
||||
<div id="aladin-lite-div" style="width: 500px; height: 400px"></div>
|
||||
<script type="module">
|
||||
import A from '../src/js/A.js';
|
||||
A.init.then(() => {
|
||||
var aladin = A.aladin('#aladin-lite-div', {target: '05 37 58 +08 17 35', fov: 12, backgroundColor: 'rgb(120, 0, 0)'});
|
||||
var cat = A.catalog({sourceSize: 20, onClick: 'showTable'});
|
||||
var aladin = A.aladin('#aladin-lite-div', {showContextMenu: true, target: '05 37 58 +08 17 35', fov: 12, backgroundColor: 'rgb(120, 0, 0)'});
|
||||
var cat = A.catalog({sourceSize: 20, onClick: (s) => {console.log("kjk", s)}});
|
||||
aladin.addCatalog(cat);
|
||||
cat.addSources([A.source(83.784490, 9.934156, {name: 'Meissa'}), A.source(88.792939, 7.407064, {name: 'Betelgeuse'}), A.source(81.282764, 6.349703, {name: 'Bellatrix'})]);
|
||||
var msg;
|
||||
@@ -21,6 +20,7 @@
|
||||
console.log(e)
|
||||
});
|
||||
|
||||
let infoDiv = document.querySelector("#infoDiv");
|
||||
aladin.on('objectHovered', function(object, xyMouseCoords) {
|
||||
if (object) {
|
||||
msg = 'You hovered object ' + object.data.name + ' located at ' + object.ra + ', ' + object.dec + '; mouse coords - x: '
|
||||
@@ -29,7 +29,7 @@
|
||||
else {
|
||||
msg = 'No object hovered';
|
||||
}
|
||||
$('#infoDiv').html(msg);
|
||||
infoDiv.innerText = msg;
|
||||
});
|
||||
|
||||
aladin.on('objectHoveredStop', function(object, xyMouseCoords) {
|
||||
@@ -37,7 +37,7 @@
|
||||
msg = 'You stopped hove object ' + object.data.name + ' located at ' + object.ra + ', ' + object.dec + '; mouse coords - x: '
|
||||
+ xyMouseCoords.x + ', y: ' + xyMouseCoords.y;
|
||||
}
|
||||
$('#infoDiv').html(msg);
|
||||
infoDiv.innerText = msg;
|
||||
});
|
||||
|
||||
// define function triggered when an object is clicked
|
||||
@@ -53,7 +53,7 @@
|
||||
objClicked.deselect();
|
||||
msg = 'You clicked in void';
|
||||
}
|
||||
$('#infoDiv').html(msg);
|
||||
infoDiv.innerText = msg;
|
||||
});
|
||||
|
||||
aladin.on('resizeChanged', function() {
|
||||
|
||||
@@ -12,9 +12,9 @@
|
||||
import A from '../src/js/A.js';
|
||||
//let aladin;
|
||||
A.init.then(() => {
|
||||
aladin = A.aladin('#aladin-lite-div', {showReticle: true, showSurveyStackControl: true, showOverlayStackControl: false, projection: "TAN", target: '15 16 57.636 -60 55 7.49', showProjectionControl: true, realFullscreen: true, showZoomControl: true, showSimbadPointerControl: true, showShareControl: true, showContextMenu: true, showCooGridControl: true, fullScreen: true, showCooGrid: true, fov: 90});
|
||||
|
||||
var moc_0_99 = A.MOCFromURL("./data//gw/gw_0.9.fits",{ name: "GW 90%", color: "#ff0000", opacity: 0.0, lineWidth: 10, fill: false, perimeter: true});
|
||||
aladin = A.aladin('#aladin-lite-div', {survey: "data/hips/CDS_P_DSS2_color", showReticle: true, showSurveyStackControl: true, showOverlayStackControl: false, projection: "TAN", target: '15 16 57.636 -60 55 7.49', showProjectionControl: true, realFullscreen: true, showZoomControl: true, showSimbadPointerControl: true, showShareControl: true, showContextMenu: true, showCooGridControl: true, fullScreen: true, showCooGrid: true, fov: 180, log: false});
|
||||
|
||||
var moc_0_99 = A.MOCFromURL("./data/gw/gw_0.9.fits",{ name: "GW 90%", color: "#ff0000", opacity: 0.0, lineWidth: 10, fill: false, perimeter: true});
|
||||
var moc_0_95 = A.MOCFromURL("./data/gw/gw_0.6.fits",{ name: "GW 60%", color: "#00ff00", opacity: 0.5, lineWidth: 3, fill: true, perimeter: true});
|
||||
var moc_0_5 = A.MOCFromURL("./data/gw/gw_0.3.fits",{ name: "GW 30%", color: "#00ffff", opacity: 0.5, lineWidth: 3, fill: true, perimeter: true});
|
||||
var moc_0_2 = A.MOCFromURL("./data/gw/gw_0.1.fits",{ name: "GW 10%", color: "#ff00ff", opacity: 0.5, lineWidth: 3, fill: true, perimeter: true});
|
||||
|
||||
@@ -4,17 +4,14 @@
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div id="aladin-lite-div" style="width: 1024px; height: 768px"></div>
|
||||
<div id="aladin-lite-div" style="width: 512px; height: 512px"></div>
|
||||
|
||||
<script type="module">
|
||||
import A from '../src/js/A.js';
|
||||
|
||||
let aladin;
|
||||
A.init.then(() => {
|
||||
aladin = A.aladin('#aladin-lite-div', {target: "05 40 59.12 -02 27 04.1", fov: 2});
|
||||
|
||||
let survey = aladin.createImageSurvey('DSS2 local', "local hips", "data/hips/CDS_P_DSS2_color");
|
||||
aladin.setBaseImageLayer(survey);
|
||||
aladin = A.aladin('#aladin-lite-div', {survey: "data/hips/CDS_P_DSS2_color", target: "05 40 59.12 -02 27 04.1", fov: 2, log: false});
|
||||
});
|
||||
</script>
|
||||
|
||||
|
||||
32
examples/al-in-shadow-dom.html
Normal file
@@ -0,0 +1,32 @@
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="shadow-host"></div>
|
||||
|
||||
<script type="text/javascript" src="./../dist/aladin.umd.cjs" charset="utf-8"></script>
|
||||
<script type="text/javascript">
|
||||
const shadowEl = document.querySelector(".shadow-host");
|
||||
const shadow = shadowEl.attachShadow({mode: 'open'});
|
||||
|
||||
let aladinLiteEl = document.createElement('div');
|
||||
aladinLiteEl.id = "aladin-lite-div"
|
||||
aladinLiteEl.style = "width: 768px; height: 512px";
|
||||
shadow.appendChild(aladinLiteEl)
|
||||
|
||||
A.init.then(() => {
|
||||
let aladin = A.aladin(
|
||||
aladinLiteEl,
|
||||
{
|
||||
survey: 'P/allWISE/color', // set initial image survey
|
||||
projection: 'AIT', // set a projection
|
||||
fov: 1.5, // initial field of view in degrees
|
||||
target: 'orion', // initial target
|
||||
}
|
||||
);
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -13,10 +13,11 @@
|
||||
aladin = A.aladin(
|
||||
'#aladin-lite-div',
|
||||
{
|
||||
showSimbadPointerControl: true,
|
||||
survey: 'P/allWISE/color', // set initial image survey
|
||||
projection: 'AIT', // set a projection
|
||||
fov: 1.5, // initial field of view in degrees
|
||||
target: 'NGC 2175', // initial target
|
||||
target: 'orion', // initial target
|
||||
cooFrame: 'icrs', // set galactic frame
|
||||
reticleColor: '#ff89ff', // change reticle color
|
||||
reticleSize: 64, // change reticle size
|
||||
@@ -26,13 +27,24 @@
|
||||
showSettingsControl:true,
|
||||
showCooGrid: true,
|
||||
fullScreen: true,
|
||||
samp: true,
|
||||
}
|
||||
);
|
||||
|
||||
aladin.removeHiPSFromFavorites('CDS/P/allWISE/color')
|
||||
/*let id;
|
||||
aladin.on("zoomChanged", () => {
|
||||
if (id)
|
||||
clearTimeout(id);
|
||||
id = setTimeout(() => {
|
||||
console.log("wheel stopped, new cone search here")
|
||||
}, 500);
|
||||
})*/
|
||||
});
|
||||
</script>
|
||||
<style>
|
||||
.aladin-cat-browser-box {
|
||||
width: 600px;
|
||||
}
|
||||
</style>
|
||||
</body>
|
||||
</html>
|
||||
@@ -44,6 +44,12 @@
|
||||
landingSites.addSources([A.marker(-225.71, 47.64, {popupTitle: 'Viking 2', popupDesc: 'Landing date: September 3, 1976 22:37:50'})]);
|
||||
landingSites.addSources([A.marker(175.472636, -14.5684, {popupTitle: 'Spirit', popupDesc: 'Landing date: January 4, 2004, 04:35 UTC '})]);
|
||||
landingSites.addSources([A.marker(137.4417, -4.5895, {popupTitle: 'Curiosity', popupDesc: 'Landing date: August 6, 2012, 05:17 UTC '})])
|
||||
|
||||
var cat = A.catalog({name: "Temp", sourceSize: 18, onClick: 'showPopup'});
|
||||
aladin.addCatalog(cat);
|
||||
cat.addSources(A.source(105.69239256, -8.45235969, {a: 1, b: 2, c:3}));
|
||||
cat.addSources(A.source(105.70779763, -8.31350997, {a: 4, b: 5, c:6}));
|
||||
cat.addSources(A.source(105.74242906, -8.34776709, {a: 7, b: 8, c:9}));
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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>
|
||||
|
||||
|
||||
@@ -3,12 +3,12 @@
|
||||
<head>
|
||||
</head>
|
||||
<body>
|
||||
<div id="aladin-lite-div" style="width: 500px; height: 400px"></div>
|
||||
<div id="aladin-lite-div" style="width: 512px; height: 512px"></div>
|
||||
<script type="module">
|
||||
import A from '../src/js/A.js';
|
||||
let aladin;
|
||||
A.init.then(() => {
|
||||
aladin = A.aladin('#aladin-lite-div', {survey: 'data/hips/CDS_P_DSS2_color', target: 'M1', fov: 0.3});
|
||||
aladin = A.aladin('#aladin-lite-div', {survey: 'data/hips/CDS_P_DSS2_color', target: '05 34 30.89 +22 00 52.9', fov: 0.3, log: false, showSettingsControl: true});
|
||||
var cat = A.catalogFromURL('data/votable/labels.vot', {sourceSize:12, color: '#cc99bb', displayLabel: true, labelColumn: 'main_id', labelColor: '#ae4', labelFont: '9px sans-serif'});
|
||||
aladin.addCatalog(cat);
|
||||
});
|
||||
|
||||
@@ -16,10 +16,6 @@
|
||||
var overlay = A.graphicOverlay({lineWidth: 2});
|
||||
aladin.addOverlay(overlay);
|
||||
overlay.add(A.polyline([ [2.29452158, 59.14978110], [10.12683778, 56.53733116], [14.1772154, 60.7167403], [21.45396446, 60.23528403], [28.59885697, 63.67010079] ], {color: 'green'}));
|
||||
|
||||
aladin.select('rect', (s) => {
|
||||
console.log(s)
|
||||
})
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
|
||||
@@ -25,7 +25,10 @@
|
||||
|
||||
aladin.setImageLayer(survey2);
|
||||
|
||||
aladin.removeHiPSFromFavorites(survey3);
|
||||
/*setTimeout(() => {
|
||||
aladin.removeHiPSFromFavorites(survey3)
|
||||
}, 10000);*/
|
||||
|
||||
|
||||
aladin.addColormap('mycmap', ["lightblue", "red", "violet", "lightgreen"])
|
||||
});
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<meta name="viewport" content="width=device-width, height=device-height, maximum-scale=1.0, initial-scale=1.0, user-scalable=no">
|
||||
</head>
|
||||
<body>
|
||||
<div id="aladin-lite-div" style="width: 500px; height: 500px"></div>
|
||||
<div id="aladin-lite-div" style="width: 512px; height: 512px"></div>
|
||||
|
||||
<script type="module">
|
||||
import A from '../src/js/A.js';
|
||||
@@ -13,9 +13,8 @@
|
||||
A.init.then(() => {
|
||||
aladin = A.aladin('#aladin-lite-div',
|
||||
{
|
||||
fullScreen: true,
|
||||
cooFrame: "ICRSd",
|
||||
survey: "../examples/hips/CDS_P_DSS2_color",
|
||||
survey: "data/hips/CDS_P_DSS2_color",
|
||||
target: "05 40 59.12 -02 27 04.1",
|
||||
fov: 2,
|
||||
showSimbadPointerControl: false,
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<meta name="viewport" content="width=device-width, height=device-height, maximum-scale=1.0, initial-scale=1.0, user-scalable=no">
|
||||
</head>
|
||||
<body>
|
||||
<div id="aladin-lite-div" style="width: 500px; height: 500px"></div>
|
||||
<div id="aladin-lite-div" style="width: 512px; height: 512px"></div>
|
||||
|
||||
<script type="module">
|
||||
import A from '../src/js/A.js';
|
||||
@@ -13,9 +13,8 @@
|
||||
A.init.then(() => {
|
||||
aladin = A.aladin('#aladin-lite-div',
|
||||
{
|
||||
fullScreen: true,
|
||||
cooFrame: "ICRSd",
|
||||
survey: "../examples/hips/CDS_P_DSS2_color",
|
||||
survey: "data/hips/CDS_P_DSS2_color",
|
||||
target: "05 40 59.12 -02 27 04.1",
|
||||
fov: 2,
|
||||
showSimbadPointerControl: true,
|
||||
|
||||
34
examples/al-v2VSv3.html
Normal file
@@ -0,0 +1,34 @@
|
||||
<html>
|
||||
<head>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Trifid interactive map</h1>
|
||||
|
||||
<!-- Aladin Lite container at requested dimensions -->
|
||||
<div id="aladin-lite-div" style="width:700px;height:400px;"></div>
|
||||
|
||||
<!-- our script needs jQuery -->
|
||||
<script type="text/javascript" src="https://code.jquery.com/jquery-1.9.1.min.js" charset="utf-8"></script>
|
||||
<!-- V2-->
|
||||
<div id="aladin-lite-div-old" style="width:700px;height:400px;"></div>
|
||||
|
||||
<!-- Aladin Lite V2 JS code -->
|
||||
<script type="text/javascript" src="https://aladin.u-strasbg.fr/AladinLite/api/v2/latest/aladin.min.js" charset="utf-8"></script>
|
||||
<script type="text/javascript">
|
||||
var aladinold = A.aladin('#aladin-lite-div-old', {survey: "P/allWISE/color", fov:1.5, target: "14 03 12.583 +54 20 55.5"});
|
||||
</script>
|
||||
|
||||
<script type="text/javascript" src="./../dist/aladin.umd.cjs" charset="utf-8"></script>
|
||||
|
||||
<!-- Aladin Lite V3 JS code -->
|
||||
<!-- Creation of Aladin Lite instance with initial parameters -->
|
||||
<script type="text/javascript">
|
||||
//import A from '../src/js/A.js';
|
||||
|
||||
let aladin;
|
||||
A.init.then(() => {
|
||||
aladin = A.aladin('#aladin-lite-div', {survey: "P/allWISE/color", fov:1.5, target: "14 03 12.583 +54 20 55.5", });
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -11,10 +11,17 @@
|
||||
|
||||
A.init.then(() => {
|
||||
let vertices = A.Utils.HEALPix.vertices(Math.pow(2, 3), BigInt(276))
|
||||
//let lonlat = A.Utils.HEALPix.pix2ang(8, 0n)
|
||||
//let ipix = A.Utils.HEALPix.ang2pix(8, 0.1, 0.4)
|
||||
//console.log("vertices", vertices, lonlat, ipix)
|
||||
console.log(vertices)
|
||||
let lonlat = A.Utils.HEALPix.pix2ang(8, 0n)
|
||||
let ipix = A.Utils.HEALPix.ang2pix(8, 0.1, 0.4)
|
||||
console.log("vertices", vertices, lonlat, ipix)
|
||||
|
||||
A.Utils.Sesame.resolveAstronomicalName("M101", (o) => {
|
||||
console.log("object found", o)
|
||||
},
|
||||
(err) => {
|
||||
console.error("errr", err)
|
||||
}
|
||||
)
|
||||
})
|
||||
|
||||
</script>
|
||||
|
||||
BIN
examples/data/hips/CDS_P_DSS2_color/Norder0/Dir0/Npix0.jpg
Normal file
|
After Width: | Height: | Size: 47 KiB |
BIN
examples/data/hips/CDS_P_DSS2_color/Norder0/Dir0/Npix1.jpg
Normal file
|
After Width: | Height: | Size: 41 KiB |
BIN
examples/data/hips/CDS_P_DSS2_color/Norder0/Dir0/Npix10.jpg
Normal file
|
After Width: | Height: | Size: 58 KiB |
BIN
examples/data/hips/CDS_P_DSS2_color/Norder0/Dir0/Npix11.jpg
Normal file
|
After Width: | Height: | Size: 44 KiB |
BIN
examples/data/hips/CDS_P_DSS2_color/Norder0/Dir0/Npix2.jpg
Normal file
|
After Width: | Height: | Size: 43 KiB |
BIN
examples/data/hips/CDS_P_DSS2_color/Norder0/Dir0/Npix3.jpg
Normal file
|
After Width: | Height: | Size: 53 KiB |
BIN
examples/data/hips/CDS_P_DSS2_color/Norder0/Dir0/Npix4.jpg
Normal file
|
After Width: | Height: | Size: 41 KiB |
|
Before Width: | Height: | Size: 5.8 KiB After Width: | Height: | Size: 5.8 KiB |
BIN
examples/data/hips/CDS_P_DSS2_color/Norder0/Dir0/Npix6.jpg
Normal file
|
After Width: | Height: | Size: 40 KiB |
BIN
examples/data/hips/CDS_P_DSS2_color/Norder0/Dir0/Npix7.jpg
Normal file
|
After Width: | Height: | Size: 55 KiB |
BIN
examples/data/hips/CDS_P_DSS2_color/Norder0/Dir0/Npix8.jpg
Normal file
|
After Width: | Height: | Size: 42 KiB |
BIN
examples/data/hips/CDS_P_DSS2_color/Norder0/Dir0/Npix9.jpg
Normal file
|
After Width: | Height: | Size: 54 KiB |
@@ -18,7 +18,6 @@ hips_tile_width = 512
|
||||
hips_tile_format = jpeg
|
||||
dataproduct_type = image
|
||||
client_application = AladinLite
|
||||
#hips_service_url = http://alasky.u-strasbg.fr/DSS/DSSColor
|
||||
hips_status = public partial unclonable
|
||||
hips_rgb_red = DSS2Merged [1488.0 8488.8125 14666.0 Linear]
|
||||
hips_rgb_blue = DSS2-blue-XJ-S [4286.0 12122.5 19959.0 Linear]
|
||||
@@ -27,22 +26,20 @@ hips_pixel_scale = 2.236E-4
|
||||
hips_initial_ra = 085.30251
|
||||
hips_initial_dec = -02.25468
|
||||
hips_initial_fov = 2
|
||||
moc_sky_fraction = 2.034E-5
|
||||
moc_sky_fraction = 0.001302
|
||||
hips_copyright = CNRS/Unistra
|
||||
obs_ack = The Digitized Sky Surveys were produced at the Space Telescope Science Institute under U.S. Government grant NAG W-2166. The images of these surveys are based on photographic data obtained using the Oschin Schmidt Telescope on Palomar Mountain and the UK Schmidt Telescope. The plates were processed into the present compressed digital form with the permission of these institutions. The National Geographic Society - Palomar Observatory Sky Atlas (POSS-I) was made by the California Institute of Technology with grants from the National Geographic Society. The Second Palomar Observatory Sky Survey (POSS-II) was made by the California Institute of Technology with funds from the National Science Foundation, the National Geographic Society, the Sloan Foundation, the Samuel Oschin Foundation, and the Eastman Kodak Corporation. The Oschin Schmidt Telescope is operated by the California Institute of Technology and Palomar Observatory. The UK Schmidt Telescope was operated by the Royal Observatory Edinburgh, with funding from the UK Science and Engineering Research Council (later the UK Particle Physics and Astronomy Research Council), until 1988 June, and thereafter by the Anglo-Australian Observatory. The blue plates of the southern Sky Atlas and its Equatorial Extension (together known as the SERC-J), as well as the Equatorial Red (ER), and the Second Epoch [red] Survey (SES) were all taken with the UK Schmidt. Supplemental funding for sky-survey work at the ST ScI is provided by the European Southern Observatory.
|
||||
prov_progenitor = STScI
|
||||
bib_reference = 1996ASPC..101...88L
|
||||
bib_reference_url = http://cdsads.u-strasbg.fr/abs/1996ASPC..101...88L
|
||||
# 1975-1999
|
||||
t_min = 42413
|
||||
t_max = 51179
|
||||
obs_regime = Optical
|
||||
em_min = 4e-7
|
||||
em_max = 6e-7
|
||||
#hips_master_url = ex: http://yourHipsServer/null
|
||||
hips_order_min = 0
|
||||
dataproduct_subtype = color
|
||||
hipsgen_date = 2019-05-07T10:55Z
|
||||
hipsgen_params = out=/asd-volumes/sc1-asd-volume8/DSS/DSSColor UPDATE
|
||||
hipsgen_date_1 = 2024-09-10T14:49Z
|
||||
hipsgen_params_1 = in=https://alasky.cds.unistra.fr/DSS/DSSColor out=./hips/CDS_P_DSS2_color region=6/24185 order=7 MIRROR
|
||||
hipsgen_date_1 = 2024-09-10T16:50Z
|
||||
hipsgen_params_1 = in=https://alasky.cds.unistra.fr/DSS/DSSColor/ out=./CDS_P_DSS2_color region=3/357 order=5 MIRROR
|
||||
|
||||
@@ -1,25 +1,14 @@
|
||||
hips_initial_fov = 1.17162
|
||||
hips_initial_ra = 339.12169
|
||||
hips_initial_dec = +34.2324
|
||||
hips_initial_dec = 34.2324
|
||||
creator_did = ivo://CDS/P/CDS_EPO/2022_Duc
|
||||
hips_overlay = mean
|
||||
hips_hierarchy = median
|
||||
hips_creator = Sebastien Derriere, CDS
|
||||
#hips_copyright = Copyright mention of the HiPS
|
||||
obs_title = CFHT deep view of NGC7331 and Stephan's quintet u+g+r
|
||||
#obs_collection = Dataset collection name
|
||||
obs_description = CFHT MegaCam u+g+r color composition enhancing low surface brightness stellar components
|
||||
obs_ack = Image created by P.-A. Duc
|
||||
obs_title = CFHT deep view of NGC7331 and Stephans quintet ugr
|
||||
obs_description = CFHT MegaCam ugr color composition enhancing low surface brightness stellar components
|
||||
prov_progenitor = CFHT MegaCam observations
|
||||
#bib_reference = Bibcode for bibliographic reference
|
||||
bib_reference_url = https://www.nature.com/articles/s41586-022-05206-x
|
||||
#obs_copyright = Copyright mention of the original data
|
||||
#obs_copyright_url = URL to copyright page of the original data
|
||||
#t_min = Start time in MJD ( =(Unixtime/86400)+40587 or https://heasarc.gsfc.nasa.gov/cgi-bin/Tools/xTime/xTime.pl)
|
||||
#t_max = Stop time in MJD
|
||||
#obs_regime = Waveband keyword (Radio Infrared Optical UV X-ray Gamma-ray)
|
||||
#em_min = Start in spectral coordinates in meters ( =2.998E8/freq in Hz, or =1.2398841929E-12*energy in MeV )
|
||||
#em_max = Stop in spectral coordinates in meters
|
||||
hips_builder = Aladin/HipsGen v12.001
|
||||
hips_version = 1.4
|
||||
hips_release_date = 2022-10-19T15:00Z
|
||||
@@ -33,7 +22,7 @@ hips_pixel_scale = 1.118E-4
|
||||
s_pixel_scale = 1.558E-4
|
||||
dataproduct_type = image
|
||||
hipsgen_date = 2022-10-18T15:43Z
|
||||
hipsgen_params = in=NGC7331_u+g+r-max out=NGC7331_u+g+r-maxHiPS creator_did=ivo://CDS
|
||||
hipsgen_params = in=NGC7331_ugr-max out=NGC7331_ugr-maxHiPS creator_did=ivo://CDS
|
||||
hips_creation_date = 2022-10-18T15:43Z
|
||||
dataproduct_subtype = color
|
||||
hipsgen_date_1 = 2024-09-10T15:15Z
|
||||
|
||||
13
package.json
@@ -2,16 +2,14 @@
|
||||
"homepage": "https://aladin.u-strasbg.fr/",
|
||||
"name": "aladin-lite",
|
||||
"type": "module",
|
||||
"version": "3.4.6-beta",
|
||||
"version": "3.5.1-beta",
|
||||
"description": "An astronomical HiPS visualizer in the browser",
|
||||
"author": "Thomas Boch and Matthieu Baumann",
|
||||
"license": "GPL-3",
|
||||
"module": "./dist/aladin.js",
|
||||
"main": "./dist/aladin.js",
|
||||
"files": [
|
||||
"dist/aladin.js",
|
||||
"dist/aladin.umd.cjs",
|
||||
"dist/index.html"
|
||||
"dist/aladin.js"
|
||||
],
|
||||
"exports": {
|
||||
".": {
|
||||
@@ -36,8 +34,8 @@
|
||||
"wasm:dbg": "wasm-pack build --dev ./src/core --target web --out-name core -- --features=webgl2,dbg -Z build-std=panic_abort,std -Z build-std-features=panic_immediate_abort ",
|
||||
"predeploy": "npm run build && rm -rf aladin-lite*.tgz && npm pack",
|
||||
"deploy": "python3 deploy/deploy.py",
|
||||
"build": "npm run wasm && vite build && cp examples/index.html dist/index.html",
|
||||
"build:dbg": "npm run wasm:dbg && vite build && cp examples/index.html dist/index.html",
|
||||
"build": "npm run wasm && vite build",
|
||||
"build:dbg": "npm run wasm:dbg && vite build",
|
||||
"dev": "npm run build && vite",
|
||||
"dev:dbg": "npm run build:dbg && vite",
|
||||
"serve": "npm run dev",
|
||||
@@ -46,14 +44,13 @@
|
||||
"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 && cp aladin-logo.png 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",
|
||||
"jsdoc": "^4.0.2",
|
||||
"vite": "^4.3.8",
|
||||
"vite-plugin-css-injected-by-js": "^3.1.1",
|
||||
"vite-plugin-glsl": "^1.1.2",
|
||||
"vite-plugin-top-level-await": "^1.4.1",
|
||||
"vite-plugin-wasm": "^3.2.2",
|
||||
|
||||
@@ -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.4.5"
|
||||
version = "3.5.1-beta"
|
||||
authors = [ "baumannmatthieu0@gmail.com", "matthieu.baumann@astro.unistra.fr",]
|
||||
edition = "2018"
|
||||
|
||||
@@ -70,7 +70,7 @@ path = "./al-api"
|
||||
|
||||
[dependencies.web-sys]
|
||||
version = "*"
|
||||
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",]
|
||||
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]
|
||||
package = "image"
|
||||
|
||||
@@ -61,6 +61,8 @@ features = [
|
||||
'PerformanceTiming',
|
||||
'Performance',
|
||||
'Url',
|
||||
'File',
|
||||
'FileList'
|
||||
]
|
||||
|
||||
[profile.dev]
|
||||
|
||||
@@ -5,7 +5,7 @@ use serde::Deserialize;
|
||||
pub struct Resources(HashMap<String, String>);
|
||||
|
||||
impl Resources {
|
||||
pub fn get_filename<'a>(&'a self, name: &str) -> Option<&String> {
|
||||
pub fn get_filename(&self, name: &str) -> Option<&String> {
|
||||
self.0.get(name)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -73,6 +73,8 @@ features = [
|
||||
'PerformanceTiming',
|
||||
'Performance',
|
||||
'Url',
|
||||
'File',
|
||||
'FileList'
|
||||
]
|
||||
|
||||
[profile.dev]
|
||||
|
||||
@@ -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};
|
||||
|
||||
615
src/core/al-core/src/texture/3d.rs
Normal file
@@ -0,0 +1,615 @@
|
||||
use crate::webgl_ctx::WebGlContext;
|
||||
use crate::webgl_ctx::WebGlRenderingCtx;
|
||||
use wasm_bindgen::prelude::*;
|
||||
use wasm_bindgen::JsCast;
|
||||
use web_sys::HtmlCanvasElement;
|
||||
use web_sys::HtmlImageElement;
|
||||
|
||||
/// Some GPU texture relative:
|
||||
// * Usual texture parameters when defining a texture
|
||||
pub const TEX_PARAMS: &'static [(u32, u32); 4] = &[
|
||||
(
|
||||
WebGlRenderingCtx::TEXTURE_MIN_FILTER,
|
||||
WebGlRenderingCtx::NEAREST,
|
||||
),
|
||||
(
|
||||
WebGlRenderingCtx::TEXTURE_MAG_FILTER,
|
||||
WebGlRenderingCtx::NEAREST,
|
||||
),
|
||||
// Prevents s-coordinate wrapping (repeating)
|
||||
(
|
||||
WebGlRenderingCtx::TEXTURE_WRAP_S,
|
||||
WebGlRenderingCtx::CLAMP_TO_EDGE,
|
||||
),
|
||||
// Prevents t-coordinate wrapping (repeating)
|
||||
(
|
||||
WebGlRenderingCtx::TEXTURE_WRAP_T,
|
||||
WebGlRenderingCtx::CLAMP_TO_EDGE,
|
||||
),
|
||||
];
|
||||
|
||||
pub static mut CUR_IDX_TEX_UNIT: u8 = 0;
|
||||
|
||||
#[derive(Clone)]
|
||||
#[allow(dead_code)]
|
||||
struct Texture3DMeta {
|
||||
pub format: u32,
|
||||
pub internal_format: i32,
|
||||
pub type_: u32,
|
||||
|
||||
pub width: u32,
|
||||
pub height: u32,
|
||||
}
|
||||
|
||||
use web_sys::WebGlTexture;
|
||||
#[derive(Clone)]
|
||||
pub struct Texture3D {
|
||||
pub texture: Option<WebGlTexture>,
|
||||
|
||||
gl: WebGlContext,
|
||||
|
||||
metadata: Option<Rc<RefCell<Texture3DMeta>>>,
|
||||
}
|
||||
|
||||
pub enum SamplerType {
|
||||
Float,
|
||||
Integer,
|
||||
Unsigned,
|
||||
}
|
||||
|
||||
use crate::image::format::ImageFormat;
|
||||
//use super::pixel::PixelType;
|
||||
use std::cell::RefCell;
|
||||
use std::path::Path;
|
||||
use std::rc::Rc;
|
||||
impl Texture3D {
|
||||
/*pub fn create_from_path<P: AsRef<Path>, F: ImageFormat>(
|
||||
gl: &WebGlContext,
|
||||
name: &'static str,
|
||||
src: &P,
|
||||
tex_params: &'static [(u32, u32)],
|
||||
) -> Result<Texture3D, JsValue> {
|
||||
let image = HtmlImageElement::new().unwrap_abort();
|
||||
|
||||
let texture = gl.create_texture();
|
||||
let onerror = {
|
||||
Closure::wrap(Box::new(move || {
|
||||
println!("Cannot load texture located at: {:?}", name);
|
||||
}) as Box<dyn Fn()>)
|
||||
};
|
||||
|
||||
let width = image.width();
|
||||
let height = image.height();
|
||||
|
||||
let metadata = Rc::new(RefCell::new(Texture3DMeta {
|
||||
width: width,
|
||||
height: height,
|
||||
internal_format: F::INTERNAL_FORMAT,
|
||||
format: F::FORMAT,
|
||||
type_: F::TYPE,
|
||||
}));
|
||||
|
||||
let onload = {
|
||||
let image = image.clone();
|
||||
let gl = gl.clone();
|
||||
let texture = texture.clone();
|
||||
let metadata = metadata.clone();
|
||||
|
||||
Closure::wrap(Box::new(move || {
|
||||
gl.bind_texture(WebGlRenderingCtx::TEXTURE_2D, texture.as_ref());
|
||||
|
||||
for (pname, param) in tex_params.iter() {
|
||||
gl.tex_parameteri(WebGlRenderingCtx::TEXTURE_2D, *pname, *param as i32);
|
||||
}
|
||||
|
||||
#[cfg(feature = "webgl2")]
|
||||
gl.tex_image_2d_with_u32_and_u32_and_html_image_element(
|
||||
WebGlRenderingCtx::TEXTURE_2D,
|
||||
0,
|
||||
F::INTERNAL_FORMAT,
|
||||
F::FORMAT,
|
||||
F::TYPE,
|
||||
&image,
|
||||
)
|
||||
.expect("Texture 2D");
|
||||
|
||||
metadata.borrow_mut().width = image.width();
|
||||
metadata.borrow_mut().height = image.height();
|
||||
|
||||
//gl.generate_mipmap(WebGl2RenderingContext::TEXTURE_2D);
|
||||
}) as Box<dyn Fn()>)
|
||||
};
|
||||
|
||||
image.set_onload(Some(onload.as_ref().unchecked_ref()));
|
||||
image.set_onerror(Some(onerror.as_ref().unchecked_ref()));
|
||||
|
||||
image.set_cross_origin(Some(""));
|
||||
image.set_src(src.as_ref().to_str().unwrap_abort());
|
||||
|
||||
onload.forget();
|
||||
onerror.forget();
|
||||
|
||||
let gl = gl.clone();
|
||||
Ok(Texture3D {
|
||||
texture,
|
||||
|
||||
gl,
|
||||
|
||||
metadata: Some(metadata),
|
||||
})
|
||||
}*/
|
||||
|
||||
pub fn create_from_raw_pixels<F: ImageFormat>(
|
||||
gl: &WebGlContext,
|
||||
width: i32,
|
||||
height: i32,
|
||||
tex_params: &'static [(u32, u32)],
|
||||
data: Option<&[<F::P as Pixel>::Item]>,
|
||||
) -> Result<Texture3D, JsValue> {
|
||||
let texture = Texture3D::create_empty_with_format::<F>(gl, width, height, tex_params)?;
|
||||
|
||||
if let Some(data) = data {
|
||||
let buf_data = unsafe { F::view(data) };
|
||||
texture
|
||||
.bind()
|
||||
.tex_sub_image_2d_with_i32_and_i32_and_u32_and_type_and_opt_array_buffer_view(
|
||||
0,
|
||||
0,
|
||||
width,
|
||||
height,
|
||||
Some(buf_data.as_ref()),
|
||||
);
|
||||
}
|
||||
|
||||
Ok(texture)
|
||||
}
|
||||
|
||||
pub fn create_from_raw_bytes<F: ImageFormat>(
|
||||
gl: &WebGlContext,
|
||||
width: i32,
|
||||
height: i32,
|
||||
tex_params: &'static [(u32, u32)],
|
||||
bytes: Option<&[u8]>,
|
||||
) -> Result<Texture3D, JsValue> {
|
||||
let texture = gl.create_texture();
|
||||
|
||||
gl.bind_texture(WebGlRenderingCtx::TEXTURE_2D, texture.as_ref());
|
||||
|
||||
for (pname, param) in tex_params.iter() {
|
||||
gl.tex_parameteri(WebGlRenderingCtx::TEXTURE_2D, *pname, *param as i32);
|
||||
}
|
||||
|
||||
gl.tex_image_2d_with_i32_and_i32_and_i32_and_format_and_type_and_opt_u8_array(
|
||||
WebGlRenderingCtx::TEXTURE_2D,
|
||||
0,
|
||||
F::INTERNAL_FORMAT,
|
||||
width,
|
||||
height,
|
||||
0,
|
||||
F::FORMAT,
|
||||
F::TYPE,
|
||||
bytes,
|
||||
)
|
||||
.expect("Texture 2D");
|
||||
|
||||
let gl = gl.clone();
|
||||
let metadata = Some(Rc::new(RefCell::new(Texture3DMeta {
|
||||
width: width as u32,
|
||||
height: height as u32,
|
||||
internal_format: F::INTERNAL_FORMAT,
|
||||
format: F::FORMAT,
|
||||
type_: F::TYPE,
|
||||
})));
|
||||
|
||||
Ok(Texture3D {
|
||||
texture,
|
||||
|
||||
gl,
|
||||
|
||||
metadata,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn create_empty_unsized(
|
||||
gl: &WebGlContext,
|
||||
tex_params: &'static [(u32, u32)],
|
||||
) -> Result<Texture3D, JsValue> {
|
||||
let texture = gl.create_texture();
|
||||
|
||||
gl.bind_texture(WebGlRenderingCtx::TEXTURE_2D, texture.as_ref());
|
||||
|
||||
for (pname, param) in tex_params.iter() {
|
||||
gl.tex_parameteri(WebGlRenderingCtx::TEXTURE_2D, *pname, *param as i32);
|
||||
}
|
||||
|
||||
let gl = gl.clone();
|
||||
|
||||
let metadata = None;
|
||||
Ok(Texture3D {
|
||||
texture,
|
||||
|
||||
gl,
|
||||
|
||||
metadata,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn create_empty_with_format<F: ImageFormat>(
|
||||
gl: &WebGlContext,
|
||||
width: i32,
|
||||
height: i32,
|
||||
tex_params: &'static [(u32, u32)],
|
||||
) -> Result<Texture3D, JsValue> {
|
||||
let texture = gl.create_texture();
|
||||
|
||||
gl.bind_texture(WebGlRenderingCtx::TEXTURE_2D, texture.as_ref());
|
||||
|
||||
for (pname, param) in tex_params.iter() {
|
||||
gl.tex_parameteri(WebGlRenderingCtx::TEXTURE_2D, *pname, *param as i32);
|
||||
}
|
||||
|
||||
gl.tex_image_2d_with_i32_and_i32_and_i32_and_format_and_type_and_opt_u8_array(
|
||||
WebGlRenderingCtx::TEXTURE_2D,
|
||||
0,
|
||||
F::INTERNAL_FORMAT,
|
||||
width,
|
||||
height,
|
||||
0,
|
||||
F::FORMAT,
|
||||
F::TYPE,
|
||||
None,
|
||||
)
|
||||
.expect("Texture 2D");
|
||||
|
||||
let gl = gl.clone();
|
||||
let metadata = Some(Rc::new(RefCell::new(Texture3DMeta {
|
||||
width: width as u32,
|
||||
height: height as u32,
|
||||
internal_format: F::INTERNAL_FORMAT,
|
||||
format: F::FORMAT,
|
||||
type_: F::TYPE,
|
||||
})));
|
||||
Ok(Texture3D {
|
||||
texture,
|
||||
|
||||
gl,
|
||||
|
||||
metadata,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn attach_to_framebuffer(&self) {
|
||||
self.gl.framebuffer_texture_2d(
|
||||
WebGlRenderingCtx::FRAMEBUFFER,
|
||||
WebGlRenderingCtx::COLOR_ATTACHMENT0,
|
||||
WebGlRenderingCtx::TEXTURE_2D,
|
||||
self.texture.as_ref(),
|
||||
0,
|
||||
);
|
||||
}
|
||||
|
||||
pub fn get_size(&self) -> (u32, u32) {
|
||||
(
|
||||
self.metadata.as_ref().unwrap_abort().borrow().width,
|
||||
self.metadata.as_ref().unwrap_abort().borrow().height,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn width(&self) -> u32 {
|
||||
self.metadata.as_ref().unwrap_abort().borrow().width
|
||||
}
|
||||
|
||||
pub fn height(&self) -> u32 {
|
||||
self.metadata.as_ref().unwrap_abort().borrow().height
|
||||
}
|
||||
|
||||
pub fn active_texture(&self, idx_tex_unit: u8) -> &Self {
|
||||
self.gl
|
||||
.active_texture(WebGlRenderingCtx::TEXTURE0 + idx_tex_unit as u32);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn bind(&self) -> Texture3DBound {
|
||||
self.gl
|
||||
.bind_texture(WebGlRenderingCtx::TEXTURE_2D, self.texture.as_ref());
|
||||
|
||||
Texture3DBound { texture: self }
|
||||
}
|
||||
|
||||
pub fn bind_mut(&mut self) -> Texture3DBoundMut {
|
||||
self.gl
|
||||
.bind_texture(WebGlRenderingCtx::TEXTURE_2D, self.texture.as_ref());
|
||||
|
||||
Texture3DBoundMut { texture: self }
|
||||
}
|
||||
|
||||
pub fn read_pixel(&self, x: i32, y: i32) -> Result<JsValue, JsValue> {
|
||||
// Create and bind the framebuffer
|
||||
let reader = self.gl.create_framebuffer();
|
||||
self.gl
|
||||
.bind_framebuffer(WebGlRenderingCtx::FRAMEBUFFER, reader.as_ref());
|
||||
|
||||
// Attach the texture as the first color attachment
|
||||
//self.attach_to_framebuffer();
|
||||
self.gl.framebuffer_texture_2d(
|
||||
WebGlRenderingCtx::READ_FRAMEBUFFER,
|
||||
WebGlRenderingCtx::COLOR_ATTACHMENT0,
|
||||
WebGlRenderingCtx::TEXTURE_2D,
|
||||
self.texture.as_ref(),
|
||||
0,
|
||||
);
|
||||
|
||||
let status = self
|
||||
.gl
|
||||
.check_framebuffer_status(WebGlRenderingCtx::FRAMEBUFFER);
|
||||
if status != WebGlRenderingCtx::FRAMEBUFFER_COMPLETE {
|
||||
// Unbind the framebuffer
|
||||
self.gl
|
||||
.bind_framebuffer(WebGlRenderingCtx::FRAMEBUFFER, None);
|
||||
// Delete the framebuffer
|
||||
self.gl.delete_framebuffer(reader.as_ref());
|
||||
|
||||
Err(JsValue::from_str("incomplete framebuffer"))
|
||||
} else {
|
||||
// set the viewport as the FBO won't be the same dimension as the screen
|
||||
let metadata = self.metadata.as_ref().unwrap_abort().borrow();
|
||||
self.gl
|
||||
.viewport(x, y, metadata.width as i32, metadata.height as i32);
|
||||
#[cfg(feature = "webgl2")]
|
||||
let value = match (metadata.format, metadata.type_) {
|
||||
(WebGlRenderingCtx::RED_INTEGER, WebGlRenderingCtx::UNSIGNED_BYTE) => {
|
||||
let p = <[u8; 1]>::read_pixel(&self.gl, x, y)?;
|
||||
Ok(serde_wasm_bindgen::to_value(&p[0])?)
|
||||
}
|
||||
(WebGlRenderingCtx::RED_INTEGER, WebGlRenderingCtx::SHORT) => {
|
||||
let p = <[i16; 1]>::read_pixel(&self.gl, x, y)?;
|
||||
Ok(serde_wasm_bindgen::to_value(&p[0])?)
|
||||
}
|
||||
(WebGlRenderingCtx::RED_INTEGER, WebGlRenderingCtx::INT) => {
|
||||
let p = <[i32; 1]>::read_pixel(&self.gl, x, y)?;
|
||||
Ok(serde_wasm_bindgen::to_value(&p[0])?)
|
||||
}
|
||||
(WebGlRenderingCtx::RED, WebGlRenderingCtx::FLOAT) => {
|
||||
let p = <[f32; 1]>::read_pixel(&self.gl, x, y)?;
|
||||
Ok(serde_wasm_bindgen::to_value(&p[0])?)
|
||||
}
|
||||
(WebGlRenderingCtx::RGB, WebGlRenderingCtx::UNSIGNED_BYTE) => {
|
||||
let p = <[u8; 3]>::read_pixel(&self.gl, x, y)?;
|
||||
Ok(serde_wasm_bindgen::to_value(&p)?)
|
||||
}
|
||||
(WebGlRenderingCtx::RGBA, WebGlRenderingCtx::UNSIGNED_BYTE) => {
|
||||
let p = <[u8; 4]>::read_pixel(&self.gl, x, y)?;
|
||||
Ok(serde_wasm_bindgen::to_value(&p)?)
|
||||
}
|
||||
_ => Err(JsValue::from_str(
|
||||
"Pixel retrieval not implemented for that texture format.",
|
||||
)),
|
||||
};
|
||||
|
||||
// Unbind the framebuffer
|
||||
self.gl
|
||||
.bind_framebuffer(WebGlRenderingCtx::FRAMEBUFFER, None);
|
||||
// Delete the framebuffer
|
||||
self.gl.delete_framebuffer(reader.as_ref());
|
||||
|
||||
// set the viewport as the FBO won't be the same dimension as the screen
|
||||
let canvas = self
|
||||
.gl
|
||||
.canvas()
|
||||
.unwrap_abort()
|
||||
.dyn_into::<web_sys::HtmlCanvasElement>()
|
||||
.unwrap_abort();
|
||||
self.gl
|
||||
.viewport(0, 0, canvas.width() as i32, canvas.height() as i32);
|
||||
|
||||
value
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Texture3D {
|
||||
fn drop(&mut self) {
|
||||
self.gl.delete_texture(self.texture.as_ref());
|
||||
|
||||
// free the texture unit
|
||||
/*let i = (self.idx_texture_unit - WebGl2RenderingContext::TEXTURE0) as usize;
|
||||
unsafe {
|
||||
AVAILABLE_TEX_UNITS[i] = Some(self.idx_texture_unit);
|
||||
}*/
|
||||
}
|
||||
}
|
||||
use crate::Abort;
|
||||
|
||||
pub struct Texture3DBound<'a> {
|
||||
texture: &'a Texture3D,
|
||||
}
|
||||
|
||||
impl<'a> Texture3DBound<'a> {
|
||||
pub fn tex_sub_image_2d_with_u32_and_u32_and_html_image_element(
|
||||
&self,
|
||||
dx: i32,
|
||||
dy: i32,
|
||||
image: &HtmlImageElement,
|
||||
) {
|
||||
let metadata = self.texture.metadata.as_ref().unwrap_abort().borrow();
|
||||
|
||||
#[cfg(feature = "webgl2")]
|
||||
self.texture
|
||||
.gl
|
||||
.tex_sub_image_2d_with_u32_and_u32_and_html_image_element(
|
||||
WebGlRenderingCtx::TEXTURE_2D,
|
||||
0,
|
||||
dx,
|
||||
dy,
|
||||
metadata.format,
|
||||
metadata.type_,
|
||||
image,
|
||||
)
|
||||
.expect("Sub texture 2d");
|
||||
}
|
||||
|
||||
pub fn tex_sub_image_2d_with_u32_and_u32_and_html_canvas_element(
|
||||
&self,
|
||||
dx: i32,
|
||||
dy: i32,
|
||||
canvas: &HtmlCanvasElement,
|
||||
) {
|
||||
let metadata = self.texture.metadata.as_ref().unwrap_abort().borrow();
|
||||
|
||||
#[cfg(feature = "webgl2")]
|
||||
self.texture
|
||||
.gl
|
||||
.tex_sub_image_2d_with_u32_and_u32_and_html_canvas_element(
|
||||
WebGlRenderingCtx::TEXTURE_2D,
|
||||
0,
|
||||
dx,
|
||||
dy,
|
||||
metadata.format,
|
||||
metadata.type_,
|
||||
canvas,
|
||||
)
|
||||
.expect("Sub texture 2d");
|
||||
}
|
||||
|
||||
pub fn tex_sub_image_2d_with_u32_and_u32_and_image_bitmap(
|
||||
&self,
|
||||
dx: i32,
|
||||
dy: i32,
|
||||
image: &web_sys::ImageBitmap,
|
||||
) {
|
||||
let metadata = self.texture.metadata.as_ref().unwrap_abort().borrow();
|
||||
|
||||
#[cfg(feature = "webgl2")]
|
||||
self.texture
|
||||
.gl
|
||||
.tex_sub_image_2d_with_u32_and_u32_and_image_bitmap(
|
||||
WebGlRenderingCtx::TEXTURE_2D,
|
||||
0,
|
||||
dx,
|
||||
dy,
|
||||
metadata.format,
|
||||
metadata.type_,
|
||||
image,
|
||||
)
|
||||
.expect("Sub texture 2d");
|
||||
}
|
||||
|
||||
pub fn tex_sub_image_2d_with_i32_and_i32_and_u32_and_type_and_opt_array_buffer_view(
|
||||
&self,
|
||||
dx: i32,
|
||||
dy: i32,
|
||||
width: i32, // Width of the image
|
||||
height: i32, // Height of the image
|
||||
image: Option<&js_sys::Object>,
|
||||
) {
|
||||
let metadata = self.texture.metadata.as_ref().unwrap_abort().borrow();
|
||||
|
||||
self.texture
|
||||
.gl
|
||||
.tex_sub_image_2d_with_i32_and_i32_and_u32_and_type_and_opt_array_buffer_view(
|
||||
WebGlRenderingCtx::TEXTURE_2D,
|
||||
0,
|
||||
dx,
|
||||
dy,
|
||||
width,
|
||||
height,
|
||||
metadata.format,
|
||||
metadata.type_,
|
||||
image,
|
||||
)
|
||||
.expect("Sub texture 2d");
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn tex_sub_image_2d_with_i32_and_i32_and_u32_and_type_and_opt_u8_array(
|
||||
&self,
|
||||
dx: i32,
|
||||
dy: i32,
|
||||
width: i32, // Width of the image
|
||||
height: i32, // Height of the image
|
||||
pixels: Option<&[u8]>,
|
||||
) {
|
||||
let metadata = self.texture.metadata.as_ref().unwrap_abort().borrow();
|
||||
self.texture
|
||||
.gl
|
||||
.tex_sub_image_2d_with_i32_and_i32_and_u32_and_type_and_opt_u8_array(
|
||||
WebGlRenderingCtx::TEXTURE_2D,
|
||||
0,
|
||||
dx,
|
||||
dy,
|
||||
width,
|
||||
height,
|
||||
metadata.format,
|
||||
metadata.type_,
|
||||
pixels,
|
||||
)
|
||||
.expect("Sub texture 2d");
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Texture3DBoundMut<'a> {
|
||||
texture &'a mut Texture3D,
|
||||
}
|
||||
|
||||
impl<'a> Texture3DBoundMut<'a> {
|
||||
#[allow(dead_code)]
|
||||
pub fn tex_image_2d_with_i32_and_i32_and_i32_and_format_and_type_and_opt_u8_array(
|
||||
&mut self,
|
||||
width: i32, // Width of the image
|
||||
height: i32, // Height of the image
|
||||
internal_format: i32,
|
||||
src_format: u32,
|
||||
src_type: u32,
|
||||
pixels: Option<&[u8]>,
|
||||
) {
|
||||
self.texture
|
||||
.gl
|
||||
.tex_image_2d_with_i32_and_i32_and_i32_and_format_and_type_and_opt_u8_array(
|
||||
WebGlRenderingCtx::TEXTURE_2D,
|
||||
0,
|
||||
internal_format as i32,
|
||||
width as i32,
|
||||
height as i32,
|
||||
0,
|
||||
src_format,
|
||||
src_type,
|
||||
pixels,
|
||||
)
|
||||
.expect("Sub texture 2d");
|
||||
//self.texture.gl.generate_mipmap(WebGlRenderingCtx::TEXTURE_2D);
|
||||
|
||||
self.texture.metadata = Some(Rc::new(RefCell::new(Texture3DMeta {
|
||||
format: src_format,
|
||||
internal_format,
|
||||
type_: src_type,
|
||||
width: width as u32,
|
||||
height: height as u32,
|
||||
})));
|
||||
}
|
||||
|
||||
pub fn tex_sub_image_2d_with_i32_and_i32_and_u32_and_type_and_opt_array_buffer_view(
|
||||
&self,
|
||||
dx: i32,
|
||||
dy: i32,
|
||||
width: i32, // Width of the image
|
||||
height: i32, // Height of the image
|
||||
image: Option<&js_sys::Object>,
|
||||
) {
|
||||
let metadata = self.texture.metadata.as_ref().unwrap_abort().borrow();
|
||||
|
||||
self.texture
|
||||
.gl
|
||||
.tex_sub_image_2d_with_i32_and_i32_and_u32_and_type_and_opt_array_buffer_view(
|
||||
WebGlRenderingCtx::TEXTURE_2D,
|
||||
0,
|
||||
dx,
|
||||
dy,
|
||||
width,
|
||||
height,
|
||||
metadata.format,
|
||||
metadata.type_,
|
||||
image,
|
||||
)
|
||||
.expect("Sub texture 2d");
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
use crate::renderable::ImageLayer;
|
||||
use crate::tile_fetcher::HiPSLocalFiles;
|
||||
use crate::{
|
||||
//async_task::{BuildCatalogIndex, ParseTableTask, TaskExecutor, TaskResult, TaskType},
|
||||
camera::CameraViewPort,
|
||||
@@ -53,7 +54,7 @@ pub struct App {
|
||||
shaders: ShaderManager,
|
||||
pub camera: CameraViewPort,
|
||||
|
||||
downloader: Downloader,
|
||||
downloader: Rc<RefCell<Downloader>>,
|
||||
tile_fetcher: TileFetcherQueue,
|
||||
layers: Layers,
|
||||
|
||||
@@ -146,7 +147,7 @@ impl App {
|
||||
//gl.enable(WebGl2RenderingContext::CULL_FACE);
|
||||
|
||||
// The tile buffer responsible for the tile requests
|
||||
let downloader = Downloader::new();
|
||||
let downloader = Rc::new(RefCell::new(Downloader::new()));
|
||||
|
||||
let camera = CameraViewPort::new(&gl, CooSystem::ICRS, &projection);
|
||||
let screen_size = &camera.get_screen_size();
|
||||
@@ -262,12 +263,11 @@ impl App {
|
||||
// Move the views of the different active surveys
|
||||
self.tile_fetcher.clear();
|
||||
// Loop over the surveys
|
||||
let _raytracer = self.layers.get_raytracer();
|
||||
|
||||
for survey in self.layers.values_mut_hips() {
|
||||
if self.camera.get_texture_depth() == 0
|
||||
&& self
|
||||
.downloader
|
||||
.borrow()
|
||||
.is_queried(&query::Allsky::new(survey.get_config()).id)
|
||||
{
|
||||
// do not ask for tiles if we download the allsky
|
||||
@@ -287,34 +287,33 @@ impl App {
|
||||
if let Some(tiles_iter) = survey.look_for_new_tiles(&mut self.camera, &self.projection)
|
||||
{
|
||||
for tile_cell in tiles_iter.into_iter() {
|
||||
self.tile_fetcher.append(
|
||||
query::Tile::new(&tile_cell, creator_did.clone(), root_url.clone(), format),
|
||||
&mut self.downloader,
|
||||
);
|
||||
self.tile_fetcher.append(query::Tile::new(
|
||||
&tile_cell,
|
||||
creator_did.clone(),
|
||||
root_url.clone(),
|
||||
format,
|
||||
));
|
||||
|
||||
if tile_cell.depth() >= min_tile_depth + 3 {
|
||||
let ancestor_tile_cell = tile_cell.ancestor(3);
|
||||
ancestors.insert(ancestor_tile_cell);
|
||||
// check if we are starting aladin lite or not.
|
||||
// If so we want to retrieve only the tiles in the view and access them
|
||||
// directly i.e. without blending them with less precised tiles
|
||||
if self.tile_fetcher.get_num_tile_fetched() > 0 {
|
||||
if tile_cell.depth() >= min_tile_depth + 3 {
|
||||
let ancestor_tile_cell = tile_cell.ancestor(3);
|
||||
ancestors.insert(ancestor_tile_cell);
|
||||
}
|
||||
}
|
||||
//let ancestor_next_tile_cell = next_tile_cell.ancestor(3);
|
||||
//if !survey.contains_tile(&ancestor_tile_cell) {
|
||||
//self.tile_fetcher.append(
|
||||
// query::Tile::new(&ancestor_tile_cell, hips_url.clone(), format),
|
||||
// &mut self.downloader,
|
||||
//);
|
||||
//}
|
||||
//if ancestor_tile_cell != ancestor_next_tile_cell {
|
||||
|
||||
//}
|
||||
}
|
||||
}
|
||||
// Request for ancestor
|
||||
for ancestor in ancestors {
|
||||
if !survey.update_priority_tile(&ancestor) {
|
||||
self.tile_fetcher.append(
|
||||
query::Tile::new(&ancestor, creator_did.clone(), root_url.clone(), format),
|
||||
&mut self.downloader,
|
||||
);
|
||||
self.tile_fetcher.append(query::Tile::new(
|
||||
&ancestor,
|
||||
creator_did.clone(),
|
||||
root_url.clone(),
|
||||
format,
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -486,12 +485,6 @@ impl App {
|
||||
self.catalog_loaded
|
||||
}
|
||||
|
||||
/*pub(crate) fn is_ready(&self) -> Result<bool, JsValue> {
|
||||
let res = self.layers.is_ready();
|
||||
|
||||
Ok(res)
|
||||
}*/
|
||||
|
||||
pub(crate) fn get_moc(&self, cfg: &al_api::moc::MOC) -> Option<&HEALPixCoverage> {
|
||||
self.moc.get_hpx_coverage(cfg)
|
||||
}
|
||||
@@ -591,124 +584,125 @@ impl App {
|
||||
/*let is_there_new_available_tiles = self
|
||||
.downloader
|
||||
.get_resolved_tiles(/*&available_tiles, */&mut self.surveys);*/
|
||||
let rscs_received = self.downloader.get_received_resources();
|
||||
let rscs_received = self.downloader.borrow_mut().get_received_resources();
|
||||
|
||||
let _num_tile_handled = 0;
|
||||
let _tile_copied = false;
|
||||
for rsc in rscs_received {
|
||||
match rsc {
|
||||
Resource::Tile(tile) => {
|
||||
if !_has_camera_zoomed {
|
||||
if let Some(survey) =
|
||||
self.layers.get_mut_hips_from_cdid(&tile.get_hips_cdid())
|
||||
{
|
||||
let cfg = survey.get_config_mut();
|
||||
//if !_has_camera_zoomed {
|
||||
if let Some(survey) =
|
||||
self.layers.get_mut_hips_from_cdid(&tile.get_hips_cdid())
|
||||
{
|
||||
let cfg = survey.get_config_mut();
|
||||
|
||||
if cfg.get_format() == tile.format {
|
||||
let delta_depth = cfg.delta_depth();
|
||||
let fov_coverage = self.camera.get_cov(cfg.get_frame());
|
||||
let included_or_near_coverage = tile
|
||||
.cell()
|
||||
.get_texture_cell(delta_depth)
|
||||
.get_tile_cells(delta_depth)
|
||||
.any(|neighbor_tile_cell| {
|
||||
fov_coverage.intersects_cell(&neighbor_tile_cell)
|
||||
});
|
||||
if cfg.get_format() == tile.format {
|
||||
let delta_depth = cfg.delta_depth();
|
||||
let fov_coverage = self.camera.get_cov(cfg.get_frame());
|
||||
let included_or_near_coverage = tile
|
||||
.cell()
|
||||
.get_texture_cell(delta_depth)
|
||||
.get_tile_cells(delta_depth)
|
||||
.any(|neighbor_tile_cell| {
|
||||
fov_coverage.intersects_cell(&neighbor_tile_cell)
|
||||
});
|
||||
|
||||
//let is_tile_root = tile.cell().depth() == delta_depth;
|
||||
//let _depth = tile.cell().depth();
|
||||
// do not perform tex_sub costly GPU calls while the camera is zooming
|
||||
if included_or_near_coverage {
|
||||
let is_missing = tile.missing();
|
||||
/*self.tile_fetcher.notify_tile(
|
||||
&tile,
|
||||
true,
|
||||
false,
|
||||
&mut self.downloader,
|
||||
);*/
|
||||
let Tile {
|
||||
cell,
|
||||
image,
|
||||
time_req,
|
||||
..
|
||||
} = tile;
|
||||
//let is_tile_root = tile.cell().depth() == delta_depth;
|
||||
//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();
|
||||
/*self.tile_fetcher.notify_tile(
|
||||
&tile,
|
||||
true,
|
||||
false,
|
||||
&mut self.downloader,
|
||||
);*/
|
||||
let Tile {
|
||||
cell,
|
||||
image,
|
||||
time_req,
|
||||
..
|
||||
} = tile;
|
||||
|
||||
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[..]);
|
||||
/*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[..]);
|
||||
|
||||
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 => (),
|
||||
};
|
||||
}
|
||||
}
|
||||
} else {
|
||||
self.downloader.delay_rsc(Resource::Tile(tile));
|
||||
}
|
||||
/*} else {
|
||||
self.downloader.delay_rsc(Resource::Tile(tile));
|
||||
}*/
|
||||
}
|
||||
Resource::Allsky(allsky) => {
|
||||
let hips_cdid = allsky.get_hips_cdid();
|
||||
@@ -719,7 +713,6 @@ impl App {
|
||||
// The allsky image is missing so we donwload all the tiles contained into
|
||||
// the 0's cell
|
||||
let cfg = survey.get_config();
|
||||
let _delta_depth = cfg.delta_depth();
|
||||
for texture_cell in crate::healpix::cell::ALLSKY_HPX_CELLS_D0 {
|
||||
for cell in texture_cell.get_tile_cells(cfg.delta_depth()) {
|
||||
let query = query::Tile::new(
|
||||
@@ -728,8 +721,7 @@ impl App {
|
||||
cfg.get_root_url().to_string(),
|
||||
cfg.get_format(),
|
||||
);
|
||||
self.tile_fetcher
|
||||
.append_base_tile(query, &mut self.downloader);
|
||||
self.tile_fetcher.append_base_tile(query);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@@ -770,26 +762,20 @@ impl App {
|
||||
}
|
||||
}
|
||||
|
||||
// We fetch when we does not move
|
||||
/*let has_not_moved_recently =
|
||||
(Time::now() - self.camera.get_time_of_last_move()) > DeltaTime(100.0);
|
||||
if has_not_moved_recently && self.inertia.is_none() {
|
||||
// Triggers the fetching of new queued tiles
|
||||
self.tile_fetcher.notify(&mut self.downloader);
|
||||
}*/
|
||||
// Tiles are fetched if:
|
||||
let fetch_tiles = self.inertia.is_none() &&
|
||||
// * the user is not zooming
|
||||
!self.camera.has_zoomed() &&
|
||||
// * no inertia action is in progress
|
||||
(
|
||||
// * the user is not panning the view
|
||||
!self.dragging ||
|
||||
// * or the user is but did not move for at least 100ms
|
||||
(self.dragging && Time::now() - self.camera.get_time_of_last_move() >= DeltaTime(100.0))
|
||||
);
|
||||
|
||||
// If there is inertia, we do not fetch any new tiles
|
||||
if self.inertia.is_none() {
|
||||
let has_not_moved_recently =
|
||||
(Time::now() - self.camera.get_time_of_last_move()) > DeltaTime(100.0);
|
||||
|
||||
let dt = if has_not_moved_recently {
|
||||
None
|
||||
} else {
|
||||
Some(DeltaTime::from_millis(700.0))
|
||||
};
|
||||
|
||||
self.tile_fetcher.notify(&mut self.downloader, dt);
|
||||
if fetch_tiles {
|
||||
self.tile_fetcher.notify(self.downloader.clone(), None);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -797,7 +783,7 @@ impl App {
|
||||
//self.layers.update(&mut self.camera, &self.projection);
|
||||
|
||||
if self.request_for_new_tiles
|
||||
&& Time::now() - self.last_time_request_for_new_tiles > DeltaTime::from(200.0)
|
||||
//&& Time::now() - self.last_time_request_for_new_tiles > DeltaTime::from(200.0)
|
||||
{
|
||||
self.look_for_new_tiles()?;
|
||||
|
||||
@@ -808,27 +794,16 @@ impl App {
|
||||
// - there is at least one tile in its blending phase
|
||||
let blending_anim_occuring =
|
||||
(Time::now() - self.time_start_blending) < BLENDING_ANIM_DURATION;
|
||||
/*let start_fading = self.layers.values_hips().any(|hips| {
|
||||
if let Some(start_time) = hips.get_ready_time() {
|
||||
Time::now() - *start_time < BLENDING_ANIM_DURATION
|
||||
} else {
|
||||
false
|
||||
}
|
||||
});*/
|
||||
|
||||
// Finally update the camera that reset the flag camera changed
|
||||
//if has_camera_moved {
|
||||
// Catalogues update
|
||||
/*if let Some(view) = self.layers.get_view() {
|
||||
self.manager.update(&self.camera, view);
|
||||
}*/
|
||||
//}
|
||||
|
||||
// Check for async retrieval
|
||||
if let Ok(img) = self.img_recv.try_recv() {
|
||||
let params = img.get_params();
|
||||
self.layers
|
||||
.add_image(img, &mut self.camera, &self.projection)?;
|
||||
self.layers.add_image(
|
||||
img,
|
||||
&mut self.camera,
|
||||
&self.projection,
|
||||
&mut self.tile_fetcher,
|
||||
)?;
|
||||
self.request_redraw = true;
|
||||
|
||||
// Send the ack to the js promise so that she finished
|
||||
@@ -980,8 +955,12 @@ impl App {
|
||||
}
|
||||
|
||||
pub(crate) fn remove_layer(&mut self, layer: &str) -> Result<(), JsValue> {
|
||||
self.layers
|
||||
.remove_layer(layer, &mut self.camera, &self.projection)?;
|
||||
self.layers.remove_layer(
|
||||
layer,
|
||||
&mut self.camera,
|
||||
&self.projection,
|
||||
&mut self.tile_fetcher,
|
||||
)?;
|
||||
|
||||
self.request_redraw = true;
|
||||
|
||||
@@ -1004,17 +983,31 @@ impl App {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn add_image_hips(&mut self, hips_cfg: HiPSCfg) -> Result<(), JsValue> {
|
||||
let hips =
|
||||
self.layers
|
||||
.add_image_hips(&self.gl, hips_cfg, &mut self.camera, &self.projection)?;
|
||||
pub(crate) fn add_image_hips(
|
||||
&mut self,
|
||||
hips_cfg: HiPSCfg,
|
||||
local_files: Option<HiPSLocalFiles>,
|
||||
) -> Result<(), JsValue> {
|
||||
let cdid = hips_cfg.properties.get_creator_did().to_string();
|
||||
|
||||
let hips = self.layers.add_image_hips(
|
||||
&self.gl,
|
||||
hips_cfg,
|
||||
&mut self.camera,
|
||||
&self.projection,
|
||||
&mut self.tile_fetcher,
|
||||
)?;
|
||||
|
||||
if let Some(local_files) = local_files {
|
||||
self.tile_fetcher.insert_hips_local_files(cdid, local_files);
|
||||
}
|
||||
|
||||
self.tile_fetcher
|
||||
.launch_starting_hips_requests(hips, &mut self.downloader);
|
||||
.launch_starting_hips_requests(hips, self.downloader.clone());
|
||||
|
||||
// Once its added, request the tiles in the view (unless the viewer is at depth 0)
|
||||
self.request_for_new_tiles = true;
|
||||
self.request_redraw = true;
|
||||
//self.grid.update(&self.camera, &self.projection);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -1315,7 +1308,7 @@ impl App {
|
||||
|
||||
// Relaunch the base tiles for the survey to be ready with the new url
|
||||
self.tile_fetcher
|
||||
.launch_starting_hips_requests(hips, &mut self.downloader);
|
||||
.launch_starting_hips_requests(hips, self.downloader.clone());
|
||||
|
||||
// Once its added, request the tiles in the view (unless the viewer is at depth 0)
|
||||
self.request_for_new_tiles = true;
|
||||
@@ -1504,7 +1497,7 @@ impl App {
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn press_left_button_mouse(&mut self, _sx: f32, _sy: f32) {
|
||||
pub(crate) fn press_left_button_mouse(&mut self) {
|
||||
self.dist_dragging = 0.0;
|
||||
self.time_start_dragging = Time::now();
|
||||
self.dragging = true;
|
||||
@@ -1514,11 +1507,10 @@ impl App {
|
||||
self.out_of_fov = false;
|
||||
}
|
||||
|
||||
pub(crate) fn release_left_button_mouse(&mut self, sx: f32, sy: f32) {
|
||||
pub(crate) fn release_left_button_mouse(&mut self) {
|
||||
self.request_for_new_tiles = true;
|
||||
|
||||
self.dragging = false;
|
||||
let _cur_mouse_pos = [sx, sy];
|
||||
|
||||
// Check whether the center has moved
|
||||
// between the pressing and releasing
|
||||
@@ -1629,9 +1621,6 @@ impl App {
|
||||
self.prev_cam_position = self.camera.get_center().truncate();
|
||||
self.camera.apply_rotation(&(-axis), d, &self.projection);
|
||||
|
||||
/* 2. Or just set the center to the current position */
|
||||
//self.set_center(&cur_pos.lonlat());
|
||||
|
||||
self.request_for_new_tiles = true;
|
||||
}
|
||||
} else {
|
||||
|
||||
@@ -432,7 +432,7 @@ impl CameraViewPort {
|
||||
};*/
|
||||
|
||||
let w_screen_px = self.width as f64;
|
||||
let smallest_cell_size_px = 1.0;
|
||||
let smallest_cell_size_px = self.dpi as f64;
|
||||
let mut depth_pixel = 29 as usize;
|
||||
|
||||
let hpx_cell_size_rad =
|
||||
@@ -445,6 +445,7 @@ impl CameraViewPort {
|
||||
|
||||
depth_pixel = depth_pixel - 1;
|
||||
}
|
||||
depth_pixel += 1;
|
||||
const DEPTH_OFFSET_TEXTURE: usize = 9;
|
||||
self.texture_depth = if DEPTH_OFFSET_TEXTURE > depth_pixel {
|
||||
0_u8
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
pub mod query;
|
||||
pub mod request;
|
||||
|
||||
|
||||
use std::collections::HashSet;
|
||||
|
||||
use query::QueryId;
|
||||
@@ -98,13 +97,7 @@ impl Downloader {
|
||||
self.queried_list.contains(id)
|
||||
}
|
||||
|
||||
//pub fn get_cached_resources(&mut self) -> Vec<Resource> {}
|
||||
|
||||
/*pub fn cache_rsc(&mut self, rsc: Resource) {
|
||||
self.cache.insert(rsc.url().clone(), rsc);
|
||||
}*/
|
||||
|
||||
pub fn delay_rsc(&mut self, rsc: Resource) {
|
||||
pub fn _delay_rsc(&mut self, rsc: Resource) {
|
||||
self.queried_cached_ids.push(rsc.id().clone());
|
||||
self.cache.insert(rsc.id().clone(), rsc);
|
||||
}
|
||||
|
||||
@@ -128,9 +128,9 @@ impl From<query::Allsky> for AllskyRequest {
|
||||
Ok(allsky_tiles)
|
||||
}
|
||||
_ => {
|
||||
let mut opts = RequestInit::new();
|
||||
opts.method("GET");
|
||||
opts.mode(RequestMode::Cors);
|
||||
let opts = RequestInit::new();
|
||||
opts.set_method("GET");
|
||||
opts.set_mode(RequestMode::Cors);
|
||||
let window = web_sys::window().unwrap_abort();
|
||||
|
||||
let request = web_sys::Request::new_with_str_and_init(&url_clone, &opts)?;
|
||||
|
||||
@@ -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 mut opts = RequestInit::new();
|
||||
opts.method("GET");
|
||||
opts.mode(RequestMode::Cors);
|
||||
let opts = RequestInit::new();
|
||||
opts.set_method("GET");
|
||||
opts.set_mode(RequestMode::Cors);
|
||||
|
||||
let request =
|
||||
web_sys::Request::new_with_str_and_init(&url_clone, &opts).unwrap_abort();
|
||||
|
||||
@@ -60,9 +60,9 @@ impl From<query::Moc> for MOCRequest {
|
||||
|
||||
let window = web_sys::window().unwrap_abort();
|
||||
let request = Request::new(async move {
|
||||
let mut opts = RequestInit::new();
|
||||
opts.method("GET");
|
||||
opts.mode(RequestMode::Cors);
|
||||
let opts = RequestInit::new();
|
||||
opts.set_method("GET");
|
||||
opts.set_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?;
|
||||
|
||||
@@ -56,9 +56,11 @@ where
|
||||
} else {
|
||||
resolved_cloned.set(ResolvedStatus::Failed);
|
||||
}
|
||||
|
||||
Ok(JsValue::from_bool(true))
|
||||
};
|
||||
|
||||
wasm_bindgen_futures::spawn_local(fut);
|
||||
let _ = wasm_bindgen_futures::future_to_promise(fut);
|
||||
}
|
||||
|
||||
Self {
|
||||
|
||||
@@ -26,11 +26,11 @@ impl From<TileRequest> for RequestType {
|
||||
}
|
||||
}
|
||||
|
||||
async fn query_html_image(url: &str) -> Result<HtmlImageElement, JsValue> {
|
||||
async fn query_html_image(url: &str) -> Result<web_sys::HtmlImageElement, JsValue> {
|
||||
let image = web_sys::HtmlImageElement::new().unwrap_abort();
|
||||
let image_cloned = image.clone();
|
||||
|
||||
let html_img_elt_promise = js_sys::Promise::new(
|
||||
let promise = js_sys::Promise::new(
|
||||
&mut (Box::new(move |resolve, reject| {
|
||||
// Ask for CORS permissions
|
||||
image_cloned.set_cross_origin(Some(""));
|
||||
@@ -40,7 +40,7 @@ async fn query_html_image(url: &str) -> Result<HtmlImageElement, JsValue> {
|
||||
}) as Box<dyn FnMut(js_sys::Function, js_sys::Function)>),
|
||||
);
|
||||
|
||||
let _ = JsFuture::from(html_img_elt_promise).await?;
|
||||
let _ = JsFuture::from(promise).await?;
|
||||
|
||||
Ok(image)
|
||||
}
|
||||
@@ -49,7 +49,7 @@ use al_core::image::html::HTMLImage;
|
||||
use wasm_bindgen::JsCast;
|
||||
use wasm_bindgen::JsValue;
|
||||
use wasm_bindgen_futures::JsFuture;
|
||||
use web_sys::{HtmlImageElement, RequestInit, RequestMode, Response};
|
||||
use web_sys::{RequestInit, RequestMode, Response};
|
||||
impl From<query::Tile> for TileRequest {
|
||||
// Create a tile request associated to a HiPS
|
||||
fn from(query: query::Tile) -> Self {
|
||||
@@ -141,9 +141,9 @@ impl From<query::Tile> for TileRequest {
|
||||
| ChannelType::R32I
|
||||
| ChannelType::R16I
|
||||
| ChannelType::R8UI => Request::new(async move {
|
||||
let mut opts = RequestInit::new();
|
||||
opts.method("GET");
|
||||
opts.mode(RequestMode::Cors);
|
||||
let opts = RequestInit::new();
|
||||
opts.set_method("GET");
|
||||
opts.set_mode(RequestMode::Cors);
|
||||
|
||||
let request =
|
||||
web_sys::Request::new_with_str_and_init(&url_clone, &opts).unwrap_abort();
|
||||
|
||||
@@ -86,7 +86,7 @@ impl HEALPixCell {
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn is_root(&self, _delta_depth_to_texture: u8) -> bool {
|
||||
pub fn is_root(&self) -> bool {
|
||||
self.depth() == 0
|
||||
}
|
||||
|
||||
|
||||
@@ -87,6 +87,7 @@ use math::projection::*;
|
||||
|
||||
use moclib::moc::RangeMOCIntoIterator;
|
||||
//use votable::votable::VOTableWrapper;
|
||||
use crate::tile_fetcher::HiPSLocalFiles;
|
||||
use wasm_bindgen::prelude::*;
|
||||
use web_sys::HtmlElement;
|
||||
|
||||
@@ -160,11 +161,7 @@ impl WebClient {
|
||||
/// * `shaders` - The list of shader objects containing the GLSL code source
|
||||
/// * `resources` - Image resource files
|
||||
#[wasm_bindgen(constructor)]
|
||||
pub fn new(
|
||||
aladin_div: &HtmlElement,
|
||||
//_shaders: JsValue,
|
||||
resources: JsValue,
|
||||
) -> Result<WebClient, JsValue> {
|
||||
pub fn new(aladin_div: &HtmlElement, resources: JsValue) -> Result<WebClient, JsValue> {
|
||||
#[cfg(feature = "dbg")]
|
||||
panic::set_hook(Box::new(console_error_panic_hook::hook));
|
||||
|
||||
@@ -370,11 +367,15 @@ impl WebClient {
|
||||
/// * If the number of surveys is greater than 4. For the moment, due to the limitations
|
||||
/// of WebGL2 texture units on some architectures, the total number of surveys rendered is
|
||||
/// limited to 4.
|
||||
#[wasm_bindgen(js_name = addImageHiPS)]
|
||||
pub fn add_image_hips(&mut self, hips: JsValue) -> Result<(), JsValue> {
|
||||
#[wasm_bindgen(js_name = addHiPS)]
|
||||
pub fn add_image_hips(
|
||||
&mut self,
|
||||
hips: JsValue,
|
||||
files: Option<HiPSLocalFiles>,
|
||||
) -> Result<(), JsValue> {
|
||||
// Deserialize the survey objects that compose the survey
|
||||
let hips = serde_wasm_bindgen::from_value(hips)?;
|
||||
self.app.add_image_hips(hips)?;
|
||||
self.app.add_image_hips(hips, files)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -404,7 +405,8 @@ impl WebClient {
|
||||
let wcs_params: WCSParams = serde_wasm_bindgen::from_value(wcs)?;
|
||||
let wcs = WCS::new(&wcs_params).map_err(|e| JsValue::from_str(&format!("{:?}", e)))?;
|
||||
|
||||
self.app.add_image_from_blob_and_wcs(layer, stream, wcs, cfg)
|
||||
self.app
|
||||
.add_image_from_blob_and_wcs(layer, stream, wcs, cfg)
|
||||
}
|
||||
|
||||
#[wasm_bindgen(js_name = removeLayer)]
|
||||
@@ -773,16 +775,16 @@ impl WebClient {
|
||||
///
|
||||
/// This is useful for beginning inerting.
|
||||
#[wasm_bindgen(js_name = releaseLeftButtonMouse)]
|
||||
pub fn release_left_button_mouse(&mut self, sx: f32, sy: f32) -> Result<(), JsValue> {
|
||||
self.app.release_left_button_mouse(sx, sy);
|
||||
pub fn release_left_button_mouse(&mut self) -> Result<(), JsValue> {
|
||||
self.app.release_left_button_mouse();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Signal the backend when the left mouse button has been pressed.
|
||||
#[wasm_bindgen(js_name = pressLeftMouseButton)]
|
||||
pub fn press_left_button_mouse(&mut self, sx: f32, sy: f32) -> Result<(), JsValue> {
|
||||
self.app.press_left_button_mouse(sx, sy);
|
||||
pub fn press_left_button_mouse(&mut self) -> Result<(), JsValue> {
|
||||
self.app.press_left_button_mouse();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -1031,7 +1033,7 @@ impl WebClient {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[wasm_bindgen(js_name = addFITSMoc)]
|
||||
#[wasm_bindgen(js_name = addFITSMOC)]
|
||||
pub fn add_fits_moc(&mut self, params: &al_api::moc::MOC, data: &[u8]) -> Result<(), JsValue> {
|
||||
//let bytes = js_sys::Uint8Array::new(array_buffer).to_vec();
|
||||
let moc = match fits::from_fits_ivoa_custom(Cursor::new(&data[..]), false)
|
||||
|
||||
@@ -81,7 +81,10 @@ fn is_too_large(cell: &HEALPixCell, camera: &CameraViewPort, projection: &Projec
|
||||
|
||||
fn num_subdivision(cell: &HEALPixCell, camera: &CameraViewPort, projection: &ProjectionType) -> u8 {
|
||||
let d = cell.depth();
|
||||
let mut num_sub = 0;
|
||||
// Subdivide all cells at least one time.
|
||||
// TODO: use a single subdivision number computed from the current cells inside the view
|
||||
// i.e. subdivide all cells in the view with the cell that has to be the most subdivided
|
||||
let mut num_sub = 1;
|
||||
if d < 2 {
|
||||
num_sub = 2 - d;
|
||||
}
|
||||
@@ -124,158 +127,6 @@ impl<'a, 'b> TextureToDraw<'a, 'b> {
|
||||
}
|
||||
}
|
||||
}
|
||||
/*
|
||||
pub trait RecomputeRasterizer {
|
||||
// Returns:
|
||||
// * The UV of the starting tile in the global 4096x4096 texture
|
||||
// * The UV of the ending tile in the global 4096x4096 texture
|
||||
// * the blending factor between the two tiles in the texture
|
||||
fn get_textures_from_survey<'a, 'b>(
|
||||
view: &'b HEALPixCellsInView,
|
||||
// The survey from which we get the textures to plot
|
||||
// Usually it is the most refined survey
|
||||
survey: &'a ImageSurveyTextures,
|
||||
) -> Vec<TextureToDraw<'a, 'b>>;
|
||||
}
|
||||
|
||||
pub struct Move;
|
||||
pub struct Zoom;
|
||||
pub struct UnZoom;
|
||||
|
||||
impl RecomputeRasterizer for Move {
|
||||
// Returns:
|
||||
// * The UV of the starting tile in the global 4096x4096 texture
|
||||
// * The UV of the ending tile in the global 4096x4096 texture
|
||||
// * the blending factor between the two tiles in the texture
|
||||
fn get_textures_from_survey<'a, 'b>(
|
||||
view: &'b HEALPixCellsInView,
|
||||
survey: &'a ImageSurveyTextures,
|
||||
) -> Vec<TextureToDraw<'a, 'b>> {
|
||||
let cells_to_draw = view.get_cells();
|
||||
let mut textures = Vec::with_capacity(view.num_of_cells());
|
||||
|
||||
for cell in cells_to_draw {
|
||||
if survey.contains(cell) {
|
||||
let parent_cell = survey.get_nearest_parent(cell);
|
||||
|
||||
if let Some(ending_cell_in_tex) = survey.get(cell) {
|
||||
if let Some(starting_cell_in_tex) = survey.get(&parent_cell) {
|
||||
textures.push(TextureToDraw::new(
|
||||
starting_cell_in_tex,
|
||||
ending_cell_in_tex,
|
||||
cell,
|
||||
));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
let parent_cell = survey.get_nearest_parent(cell);
|
||||
let grand_parent_cell = survey.get_nearest_parent(&parent_cell);
|
||||
|
||||
if let Some(ending_cell_in_tex) = survey.get(&parent_cell) {
|
||||
if let Some(starting_cell_in_tex) = survey.get(&grand_parent_cell) {
|
||||
textures.push(TextureToDraw::new(
|
||||
starting_cell_in_tex,
|
||||
ending_cell_in_tex,
|
||||
cell,
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
textures
|
||||
}
|
||||
}
|
||||
|
||||
impl RecomputeRasterizer for Zoom {
|
||||
// Returns:
|
||||
// * The UV of the starting tile in the global 4096x4096 texture
|
||||
// * The UV of the ending tile in the global 4096x4096 texture
|
||||
// * the blending factor between the two tiles in the texture
|
||||
fn get_textures_from_survey<'a, 'b>(
|
||||
view: &'b HEALPixCellsInView,
|
||||
survey: &'a ImageSurveyTextures,
|
||||
) -> Vec<TextureToDraw<'a, 'b>> {
|
||||
let cells_to_draw = view.get_cells();
|
||||
let mut textures = Vec::with_capacity(view.num_of_cells());
|
||||
|
||||
for cell in cells_to_draw {
|
||||
if survey.contains(cell) {
|
||||
let parent_cell = survey.get_nearest_parent(cell);
|
||||
|
||||
if let Some(ending_cell_in_tex) = survey.get(cell) {
|
||||
if let Some(starting_cell_in_tex) = survey.get(&parent_cell) {
|
||||
textures.push(TextureToDraw::new(
|
||||
starting_cell_in_tex,
|
||||
ending_cell_in_tex,
|
||||
cell,
|
||||
));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
let parent_cell = survey.get_nearest_parent(cell);
|
||||
let grand_parent_cell = survey.get_nearest_parent(&parent_cell);
|
||||
|
||||
if let Some(ending_cell_in_tex) = survey.get(&parent_cell) {
|
||||
if let Some(starting_cell_in_tex) = survey.get(&grand_parent_cell) {
|
||||
textures.push(TextureToDraw::new(
|
||||
starting_cell_in_tex,
|
||||
ending_cell_in_tex,
|
||||
cell,
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
textures
|
||||
}
|
||||
}
|
||||
|
||||
impl RecomputeRasterizer for UnZoom {
|
||||
// Returns:
|
||||
// * The UV of the starting tile in the global 4096x4096 texture
|
||||
// * The UV of the ending tile in the global 4096x4096 texture
|
||||
// * the blending factor between the two tiles in the texture
|
||||
fn get_textures_from_survey<'a, 'b>(
|
||||
view: &'b HEALPixCellsInView,
|
||||
survey: &'a ImageSurveyTextures,
|
||||
) -> Vec<TextureToDraw<'a, 'b>> {
|
||||
let _depth = view.get_depth();
|
||||
let _max_depth = survey.config().get_max_depth();
|
||||
|
||||
// We do not draw the parent cells if the depth has not decreased by at least one
|
||||
let cells_to_draw = view.get_cells();
|
||||
|
||||
let mut textures = Vec::with_capacity(view.num_of_cells());
|
||||
|
||||
for cell in cells_to_draw {
|
||||
if survey.contains(cell) {
|
||||
if let Some(starting_cell_in_tex) = survey.get(cell) {
|
||||
textures.push(TextureToDraw::new(
|
||||
starting_cell_in_tex,
|
||||
starting_cell_in_tex,
|
||||
cell,
|
||||
));
|
||||
}
|
||||
} else {
|
||||
let parent_cell = survey.get_nearest_parent(cell);
|
||||
|
||||
if let Some(ending_cell_in_tex) = survey.get(&parent_cell) {
|
||||
textures.push(TextureToDraw::new(
|
||||
ending_cell_in_tex,
|
||||
ending_cell_in_tex,
|
||||
cell,
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
textures
|
||||
}
|
||||
}
|
||||
|
||||
*/
|
||||
|
||||
pub fn get_raster_shader<'a>(
|
||||
cmap: &Colormap,
|
||||
@@ -675,24 +526,10 @@ impl HiPS {
|
||||
self.textures.set_format(&self.gl, ext)
|
||||
}
|
||||
|
||||
/*pub fn get_fading_factor(&self) -> f32 {
|
||||
self.textures
|
||||
.start_time
|
||||
.map(|start_time| {
|
||||
let fading = (Time::now().0 - start_time.0) / crate::app::BLENDING_ANIM_DURATION.0;
|
||||
fading.clamp(0.0, 1.0)
|
||||
})
|
||||
.unwrap_or(0.0)
|
||||
}*/
|
||||
|
||||
pub fn is_allsky(&self) -> bool {
|
||||
self.textures.config().is_allsky
|
||||
}
|
||||
|
||||
/*pub fn reset_frame(&mut self) {
|
||||
self.view.reset_frame();
|
||||
}*/
|
||||
|
||||
// Position given is in the camera space
|
||||
pub fn read_pixel(
|
||||
&self,
|
||||
@@ -825,7 +662,7 @@ impl HiPS {
|
||||
))
|
||||
}
|
||||
} else {
|
||||
None
|
||||
unreachable!();
|
||||
}
|
||||
} else {
|
||||
None
|
||||
@@ -842,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);
|
||||
|
||||
@@ -966,24 +803,6 @@ impl HiPS {
|
||||
);
|
||||
}
|
||||
|
||||
/*pub fn (&mut self, camera: &CameraViewPort, proj: &ProjectionType) {
|
||||
let cfg = self.textures.config();
|
||||
let max_tile_depth = cfg.get_max_tile_depth();
|
||||
//let delta_depth = cfg.delta_depth();
|
||||
|
||||
//let hips_frame = cfg.get_frame();
|
||||
// Compute that depth
|
||||
let camera_tile_depth = camera.get_tile_depth();
|
||||
self.depth_tile = camera_tile_depth.min(max_tile_depth);
|
||||
|
||||
// Set the depth of the HiPS textures
|
||||
/*self.depth = if self.depth_tile > delta_depth {
|
||||
self.depth_tile - delta_depth
|
||||
} else {
|
||||
0
|
||||
};*/
|
||||
}*/
|
||||
|
||||
// Return a boolean to signal if the tile is present or not in the survey
|
||||
pub fn update_priority_tile(&mut self, cell: &HEALPixCell) -> bool {
|
||||
if self.textures.contains_tile(cell) {
|
||||
@@ -998,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)
|
||||
@@ -1019,21 +838,6 @@ impl HiPS {
|
||||
self.textures.config_mut()
|
||||
}
|
||||
|
||||
/*#[inline]
|
||||
pub fn get_view(&self) -> &HEALPixCellsInView {
|
||||
&self.view
|
||||
}*/
|
||||
|
||||
/*#[inline]
|
||||
pub fn get_min_depth_tile(&self) -> u8 {
|
||||
self.min_depth_tile
|
||||
}*/
|
||||
|
||||
/*#[inline]
|
||||
pub fn is_ready(&self) -> bool {
|
||||
self.textures.is_ready()
|
||||
}*/
|
||||
|
||||
#[inline]
|
||||
pub fn get_ready_time(&self) -> &Option<Time> {
|
||||
&self.textures.start_time
|
||||
|
||||
@@ -1,10 +1,7 @@
|
||||
use crate::domain::sdf::ProjDefType;
|
||||
|
||||
use crate::{math::projection::Projection};
|
||||
|
||||
use crate::math::projection::Projection;
|
||||
|
||||
use al_core::VecData;
|
||||
use al_core::{shader::ShaderBound, Texture2D, VertexArrayObject, WebGlContext};
|
||||
use al_core::{shader::ShaderBound, VertexArrayObject, WebGlContext};
|
||||
|
||||
pub use super::triangulation::Triangulation;
|
||||
|
||||
@@ -12,15 +9,23 @@ pub trait RayTracingProjection {
|
||||
fn get_raytracer_vertex_array_object(raytracer: &RayTracer) -> &VertexArrayObject;
|
||||
}
|
||||
|
||||
fn create_vertices_array(proj_area: &ProjDefType) -> (Vec<f32>, Vec<u16>) {
|
||||
let Triangulation { vertices, idx } = Triangulation::build(proj_area);
|
||||
fn create_vertices_array(proj: &ProjectionType) -> (Vec<f32>, Vec<u16>) {
|
||||
let Triangulation { vertices, idx } = Triangulation::build(proj.get_area());
|
||||
|
||||
let vertices = vertices
|
||||
.into_iter().flat_map(|pos_clip_space| {
|
||||
.into_iter()
|
||||
.flat_map(|pos_clip_space| {
|
||||
// Cast all the double into float
|
||||
// simple precision because this buffer
|
||||
// is sent to the GPU
|
||||
vec![pos_clip_space.x as f32, pos_clip_space.y as f32/*, pos_world_space.x as f32, pos_world_space.y as f32, pos_world_space.z as f32*/]
|
||||
let pos_world_space = proj.clip_to_world_space(&(pos_clip_space * 0.99)).unwrap();
|
||||
[
|
||||
pos_clip_space.x as f32,
|
||||
pos_clip_space.y as f32,
|
||||
pos_world_space.x as f32,
|
||||
pos_world_space.y as f32,
|
||||
pos_world_space.z as f32,
|
||||
]
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
@@ -31,13 +36,9 @@ use web_sys::WebGl2RenderingContext;
|
||||
|
||||
pub struct RayTracer {
|
||||
vao: VertexArrayObject,
|
||||
position_tex: Texture2D,
|
||||
#[cfg(feature = "webgl1")]
|
||||
ang2pix_tex: Texture2D,
|
||||
}
|
||||
use cgmath::{InnerSpace, Vector2};
|
||||
|
||||
const SIZE_POSITION_TEX: usize = 512;
|
||||
/*const SIZE_POSITION_TEX: usize = 512;
|
||||
fn generate_xyz_position(projection: &ProjectionType) -> Vec<f32> {
|
||||
let (w, h) = (SIZE_POSITION_TEX, SIZE_POSITION_TEX);
|
||||
let mut data = vec![0.0; SIZE_POSITION_TEX * SIZE_POSITION_TEX * 3];
|
||||
@@ -93,38 +94,13 @@ fn generate_xyz_position(projection: &ProjectionType) -> Vec<f32> {
|
||||
}
|
||||
|
||||
data
|
||||
}
|
||||
}*/
|
||||
|
||||
#[cfg(feature = "webgl1")]
|
||||
use cgmath::Rad;
|
||||
#[cfg(feature = "webgl1")]
|
||||
fn generate_hash_dxdy<P: Projection>(depth: u8) -> Vec<f32> {
|
||||
let (w, h) = (SIZE_POSITION_TEX as f64, SIZE_POSITION_TEX as f64);
|
||||
let mut data = vec![];
|
||||
for y in 0..(h as u32) {
|
||||
for x in 0..(w as u32) {
|
||||
let xy = Vector2::new(x, y);
|
||||
let lonlat = LonLatT::new(
|
||||
Rad(((xy.x as f64) / (w as f64)) * std::f64::consts::PI * 2.0
|
||||
+ std::f64::consts::PI)
|
||||
.into(),
|
||||
Rad((2.0 * ((xy.y as f64) / (h as f64)) - 1.0) * std::f64::consts::FRAC_PI_2)
|
||||
.into(),
|
||||
);
|
||||
let (idx, dx, dy) =
|
||||
cdshealpix::nested::hash_with_dxdy(depth, lonlat.lon().0, lonlat.lat().0);
|
||||
data.extend(&[(idx as f32), dx as f32, dy as f32]);
|
||||
}
|
||||
}
|
||||
|
||||
data
|
||||
}
|
||||
use crate::ProjectionType;
|
||||
use wasm_bindgen::JsValue;
|
||||
impl RayTracer {
|
||||
pub fn new(gl: &WebGlContext, proj: &ProjectionType) -> Result<RayTracer, JsValue> {
|
||||
let proj_area = proj.get_area();
|
||||
let (vertices, idx) = create_vertices_array(proj_area);
|
||||
let (vertices, idx) = create_vertices_array(proj);
|
||||
|
||||
let mut vao = VertexArrayObject::new(gl);
|
||||
// layout (location = 0) in vec2 pos_clip_space;
|
||||
@@ -132,9 +108,9 @@ impl RayTracer {
|
||||
vao.bind_for_update()
|
||||
.add_array_buffer(
|
||||
"vertices",
|
||||
2 * std::mem::size_of::<f32>(),
|
||||
&[2],
|
||||
&[0],
|
||||
5 * std::mem::size_of::<f32>(),
|
||||
&[2, 3],
|
||||
&[0, 2 * std::mem::size_of::<f32>()],
|
||||
WebGl2RenderingContext::STATIC_DRAW,
|
||||
VecData::<f32>(&vertices),
|
||||
)
|
||||
@@ -149,20 +125,9 @@ impl RayTracer {
|
||||
.add_element_buffer(WebGl2RenderingContext::STATIC_DRAW, VecData::<u16>(&idx))
|
||||
// Unbind the buffer
|
||||
.unbind();
|
||||
#[cfg(feature = "webgl1")]
|
||||
vao.bind_for_update()
|
||||
.add_array_buffer(
|
||||
2,
|
||||
"pos_clip_space",
|
||||
WebGl2RenderingContext::STATIC_DRAW,
|
||||
VecData::<f32>(&vertices),
|
||||
)
|
||||
// Set the element buffer
|
||||
.add_element_buffer(WebGl2RenderingContext::STATIC_DRAW, VecData::<u16>(&idx))
|
||||
// Unbind the buffer
|
||||
.unbind();
|
||||
|
||||
// create position data
|
||||
let data = generate_xyz_position(proj);
|
||||
/*let data = generate_xyz_position(proj);
|
||||
let position_tex = Texture2D::create_from_raw_pixels::<al_core::image::format::RGB32F>(
|
||||
gl,
|
||||
SIZE_POSITION_TEX as i32,
|
||||
@@ -188,28 +153,9 @@ impl RayTracer {
|
||||
),
|
||||
],
|
||||
Some(&data),
|
||||
)?;
|
||||
)?;*/
|
||||
|
||||
// create ang2pix texture for webgl1 app
|
||||
#[cfg(feature = "webgl1")]
|
||||
let ang2pix_tex = {
|
||||
let data = generate_hash_dxdy(0, proj);
|
||||
create_f32_texture_from_raw(
|
||||
&gl,
|
||||
SIZE_POSITION_TEX as i32,
|
||||
SIZE_POSITION_TEX as i32,
|
||||
&data,
|
||||
)
|
||||
};
|
||||
|
||||
Ok(RayTracer {
|
||||
vao,
|
||||
|
||||
position_tex,
|
||||
|
||||
#[cfg(feature = "webgl1")]
|
||||
ang2pix_tex,
|
||||
})
|
||||
Ok(RayTracer { vao })
|
||||
}
|
||||
|
||||
pub fn get_vao(&self) -> &VertexArrayObject {
|
||||
@@ -217,20 +163,8 @@ impl RayTracer {
|
||||
}
|
||||
|
||||
pub fn draw<'a>(&self, shader: &ShaderBound<'a>) {
|
||||
#[cfg(feature = "webgl1")]
|
||||
shader
|
||||
.attach_uniform("position_tex", &self.position_tex)
|
||||
.attach_uniform("u_ang2pixd", &self.ang2pix_tex)
|
||||
.bind_vertex_array_object_ref(&self.vao)
|
||||
.draw_elements_with_i32(
|
||||
WebGl2RenderingContext::TRIANGLES,
|
||||
None,
|
||||
WebGl2RenderingContext::UNSIGNED_SHORT,
|
||||
0,
|
||||
);
|
||||
#[cfg(feature = "webgl2")]
|
||||
shader
|
||||
.attach_uniform("position_tex", &self.position_tex)
|
||||
.bind_vertex_array_object_ref(&self.vao)
|
||||
.draw_elements_with_i32(
|
||||
WebGl2RenderingContext::TRIANGLES,
|
||||
|
||||
@@ -1,293 +0,0 @@
|
||||
//use moclib::moc::range::CellAndNeighs;
|
||||
|
||||
/*use crate::renderable::coverage::HEALPixCell;
|
||||
|
||||
use healpix::compass_point::Ordinal;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(super) struct EdgeNeigs {
|
||||
// Indices of the neighbors in the stack
|
||||
pub neig_idx: Vec<usize>,
|
||||
// Smallest depth from the neighbor cells
|
||||
pub max_depth_neig: u8,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(super) struct NodeEdgeNeigs {
|
||||
pub cell: HEALPixCell,
|
||||
pub edge_neigs: [Option<EdgeNeigs>; 4],
|
||||
}
|
||||
|
||||
impl PartialEq for NodeEdgeNeigs {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.cell == other.cell
|
||||
}
|
||||
}
|
||||
|
||||
impl NodeEdgeNeigs {
|
||||
pub(super) fn add_neig(&mut self, org: Ordinal, neig_idx: usize, neig_cell_depth: u8) {
|
||||
let org_idx = org as u8 as usize;
|
||||
if let Some(neigs) = &mut self.edge_neigs[org_idx] {
|
||||
neigs.neig_idx.push(neig_idx);
|
||||
neigs.max_depth_neig = neigs.max_depth_neig.max(neig_cell_depth);
|
||||
} else {
|
||||
self.edge_neigs[org_idx] = Some(EdgeNeigs {
|
||||
neig_idx: vec![neig_idx],
|
||||
max_depth_neig: neig_cell_depth,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn compute_n_seg(&self, side: Ordinal) -> u32 {
|
||||
let mut delta_depth =
|
||||
if let Some(edge_neigs) = self.edge_neigs[side as u8 as usize].as_ref() {
|
||||
edge_neigs.max_depth_neig.max(self.cell.depth()) - self.cell.depth()
|
||||
} else {
|
||||
0
|
||||
};
|
||||
|
||||
if self.cell.depth() + delta_depth < 3 {
|
||||
delta_depth = 3 - self.cell.depth();
|
||||
}
|
||||
|
||||
if self.cell.depth() >= 6 {
|
||||
delta_depth = 0
|
||||
}
|
||||
|
||||
1 << delta_depth
|
||||
}
|
||||
|
||||
pub(super) fn compute_n_seg_with_neig_info(
|
||||
&self,
|
||||
neig: &Self,
|
||||
side: Ordinal,
|
||||
side_neig: Ordinal,
|
||||
) -> u32 {
|
||||
let mut delta_depth = if self.cell.depth() > 6 {
|
||||
0
|
||||
} else {
|
||||
if let (Some(edge_neigs), Some(edge_self)) = (
|
||||
self.edge_neigs[side as u8 as usize].as_ref(),
|
||||
neig.edge_neigs[side_neig as u8 as usize].as_ref(),
|
||||
) {
|
||||
edge_neigs
|
||||
.max_depth_neig
|
||||
.max(edge_self.max_depth_neig)
|
||||
.max(self.cell.depth())
|
||||
- self.cell.depth()
|
||||
} else {
|
||||
0
|
||||
}
|
||||
};
|
||||
|
||||
if self.cell.depth() + delta_depth < 3 {
|
||||
delta_depth = 3 - self.cell.depth();
|
||||
}
|
||||
|
||||
1 << delta_depth
|
||||
}
|
||||
}
|
||||
*/
|
||||
/*pub(super) struct G {
|
||||
nodes: Vec<NodeEdgeNeigs>,
|
||||
}
|
||||
use crate::renderable::coverage::mode::Node;
|
||||
impl G {
|
||||
pub(super) fn new(moc: &HEALPixCoverage) -> Self {
|
||||
let mut nodes: Vec<_> = (&moc.0)
|
||||
.into_range_moc_iter()
|
||||
.cells()
|
||||
.map(|cell| {
|
||||
let cell = HEALPixCell(cell.depth, cell.idx);
|
||||
|
||||
NodeEdgeNeigs {
|
||||
cell,
|
||||
edge_neigs: [None, None, None, None],
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
|
||||
let find_cell_node_idx = |nodes: &[NodeEdgeNeigs], cell: &Cell<u64>| -> usize {
|
||||
let hpx_cell = HEALPixCell(cell.depth, cell.idx);
|
||||
let result = nodes.binary_search_by(|n| n.cell.cmp(&hpx_cell));
|
||||
match result {
|
||||
Ok(i) => i,
|
||||
Err(_) => unreachable!(),
|
||||
}
|
||||
};
|
||||
|
||||
// 1. Build the MOC graph structure
|
||||
for cell_and_neig in moc.0.all_cells_with_unidirectional_neigs() {
|
||||
let CellAndNeighs { cell, neigs } = cell_and_neig;
|
||||
// cells are given by uniq order so big cells at first and smaller cells after
|
||||
// neighbor information are also given for small cells towards bigger cells
|
||||
// Thus we are sure we have already processed the neighbor before as it is a bigger cell or equal order
|
||||
// cell having an idx inferior
|
||||
|
||||
let small_node_idx = find_cell_node_idx(&nodes, &cell);
|
||||
|
||||
if let Some(&nw_neig_cell_idx) = neigs.get(Ordinal::NW) {
|
||||
//al_core::log("nw neig");
|
||||
let nw_neig_cell_d = nodes[nw_neig_cell_idx].cell.depth();
|
||||
debug_assert!(nw_neig_cell_d <= cell.depth);
|
||||
|
||||
if let Some(dir) =
|
||||
find_neig_dir(nodes[nw_neig_cell_idx].cell, nodes[small_node_idx].cell)
|
||||
{
|
||||
nodes[nw_neig_cell_idx].add_neig(dir, small_node_idx, cell.depth);
|
||||
}
|
||||
// Add the neig info from the big to the small node
|
||||
//nodes[nw_neig_cell_idx].add_neig(Ordinal::SE, small_node_idx, cell.depth);
|
||||
// Add the neig info from the small to the big node
|
||||
nodes[small_node_idx].add_neig(Ordinal::NW, nw_neig_cell_idx, nw_neig_cell_d);
|
||||
}
|
||||
|
||||
if let Some(&ne_neig_cell_idx) = neigs.get(Ordinal::NE) {
|
||||
//al_core::log("ne neig");
|
||||
let ne_neig_cell_d = nodes[ne_neig_cell_idx].cell.depth();
|
||||
debug_assert!(ne_neig_cell_d <= cell.depth);
|
||||
|
||||
if let Some(dir) =
|
||||
find_neig_dir(nodes[ne_neig_cell_idx].cell, nodes[small_node_idx].cell)
|
||||
{
|
||||
nodes[ne_neig_cell_idx].add_neig(dir, small_node_idx, cell.depth);
|
||||
}
|
||||
|
||||
// Add the neig info from the big to the small node
|
||||
//nodes[ne_neig_cell_idx].add_neig(Ordinal::SW, small_node_idx, cell.depth);
|
||||
// Add the neig info from the small to the big node
|
||||
nodes[small_node_idx].add_neig(Ordinal::NE, ne_neig_cell_idx, ne_neig_cell_d);
|
||||
}
|
||||
|
||||
if let Some(&se_neig_cell_idx) = neigs.get(Ordinal::SE) {
|
||||
//al_core::log("se neig");
|
||||
let se_neig_cell_d = nodes[se_neig_cell_idx].cell.depth();
|
||||
debug_assert!(se_neig_cell_d <= cell.depth);
|
||||
|
||||
if let Some(dir) =
|
||||
find_neig_dir(nodes[se_neig_cell_idx].cell, nodes[small_node_idx].cell)
|
||||
{
|
||||
nodes[se_neig_cell_idx].add_neig(dir, small_node_idx, cell.depth);
|
||||
}
|
||||
|
||||
// Add the neig info from the big to the small node
|
||||
//nodes[se_neig_cell_idx].add_neig(Ordinal::NW, small_node_idx, cell.depth);
|
||||
// Add the neig info from the small to the big node
|
||||
nodes[small_node_idx].add_neig(Ordinal::SE, se_neig_cell_idx, se_neig_cell_d);
|
||||
}
|
||||
|
||||
if let Some(&sw_neig_cell_idx) = neigs.get(Ordinal::SW) {
|
||||
//al_core::log("sw neig");
|
||||
let sw_neig_cell_d = nodes[sw_neig_cell_idx].cell.depth();
|
||||
debug_assert!(sw_neig_cell_d <= cell.depth);
|
||||
|
||||
if let Some(dir) =
|
||||
find_neig_dir(nodes[sw_neig_cell_idx].cell, nodes[small_node_idx].cell)
|
||||
{
|
||||
nodes[sw_neig_cell_idx].add_neig(dir, small_node_idx, cell.depth);
|
||||
}
|
||||
|
||||
// Add the neig info from the big to the small node
|
||||
//nodes[sw_neig_cell_idx].add_neig(Ordinal::NE, small_node_idx, cell.depth);
|
||||
// Add the neig info from the small to the big node
|
||||
nodes[small_node_idx].add_neig(Ordinal::SW, sw_neig_cell_idx, sw_neig_cell_d);
|
||||
}
|
||||
}
|
||||
|
||||
Self { nodes }
|
||||
}
|
||||
|
||||
pub(super) fn get_neigs(
|
||||
&self,
|
||||
node: &NodeEdgeNeigs,
|
||||
dir: Ordinal,
|
||||
) -> Option<Vec<&NodeEdgeNeigs>> {
|
||||
node.edge_neigs[dir as u8 as usize]
|
||||
.as_ref()
|
||||
.map(|edge| {
|
||||
if !edge.neig_idx.is_empty() {
|
||||
Some(
|
||||
edge.neig_idx
|
||||
.iter()
|
||||
.map(|idx| &self.nodes[*idx])
|
||||
.collect::<Vec<_>>(),
|
||||
)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.flatten()
|
||||
}
|
||||
|
||||
pub(super) fn get_neig_dir(
|
||||
&self,
|
||||
node: &NodeEdgeNeigs,
|
||||
neig: &NodeEdgeNeigs,
|
||||
) -> Option<Ordinal> {
|
||||
if let Some(neigs) = self.get_neigs(node, Ordinal::NW) {
|
||||
if let Some(_) = neigs.iter().find(|&&n| n == neig) {
|
||||
return Some(Ordinal::NW);
|
||||
}
|
||||
}
|
||||
if let Some(neigs) = self.get_neigs(node, Ordinal::SW) {
|
||||
if let Some(_) = neigs.iter().find(|&&n| n == neig) {
|
||||
return Some(Ordinal::SW);
|
||||
}
|
||||
}
|
||||
if let Some(neigs) = self.get_neigs(node, Ordinal::SE) {
|
||||
if let Some(_) = neigs.iter().find(|&&n| n == neig) {
|
||||
return Some(Ordinal::SE);
|
||||
}
|
||||
}
|
||||
if let Some(neigs) = self.get_neigs(node, Ordinal::NE) {
|
||||
if let Some(_) = neigs.iter().find(|&&n| n == neig) {
|
||||
return Some(Ordinal::NE);
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
pub(super) fn nodes_iter<'a>(&'a self) -> impl Iterator<Item = &'a NodeEdgeNeigs> {
|
||||
self.nodes.iter()
|
||||
}
|
||||
|
||||
pub(super) fn nodes(&self) -> &[NodeEdgeNeigs] {
|
||||
&self.nodes[..]
|
||||
}
|
||||
}
|
||||
|
||||
fn find_neig_dir(mut cell: HEALPixCell, mut neig: HEALPixCell) -> Option<Ordinal> {
|
||||
if cell.depth() > neig.depth() {
|
||||
cell = cell.ancestor(cell.depth() - neig.depth());
|
||||
} else if cell.depth() < neig.depth() {
|
||||
neig = neig.ancestor(neig.depth() - cell.depth());
|
||||
}
|
||||
|
||||
if let Some(nw) = cell.neighbor(MainWind::NW) {
|
||||
if nw == neig {
|
||||
return Some(Ordinal::NW);
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(ne) = cell.neighbor(MainWind::NE) {
|
||||
if ne == neig {
|
||||
return Some(Ordinal::NE);
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(sw) = cell.neighbor(MainWind::SW) {
|
||||
if sw == neig {
|
||||
return Some(Ordinal::SW);
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(se) = cell.neighbor(MainWind::SE) {
|
||||
if se == neig {
|
||||
return Some(Ordinal::SE);
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
*/
|
||||
@@ -1,6 +1,3 @@
|
||||
mod graph;
|
||||
mod mode;
|
||||
|
||||
pub mod hierarchy;
|
||||
pub mod renderer;
|
||||
pub use renderer::MOCRenderer;
|
||||
|
||||
@@ -1,192 +0,0 @@
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
|
||||
pub struct Edge;
|
||||
|
||||
impl RenderMode for Edge {
|
||||
fn build(moc: &HEALPixCoverage) -> Vec<Node> {
|
||||
let g = graph::G::new(moc);
|
||||
|
||||
// 2. Precompute the vertices from the graph structure
|
||||
g.nodes_iter()
|
||||
.flat_map(|n| {
|
||||
let mut edges = OrdinalMap::new();
|
||||
let cell = n.cell;
|
||||
|
||||
if let Some(edge_neigs) = &n.edge_neigs[Ordinal::NW as u8 as usize] {
|
||||
// if the smallest neig for this edge is smaller than self
|
||||
let _smallest_neig_depth = edge_neigs.max_depth_neig;
|
||||
|
||||
let first_neig_idx = edge_neigs.neig_idx[0];
|
||||
let neig_cell = &g.nodes()[first_neig_idx].cell;
|
||||
|
||||
let draw_side =
|
||||
// the current node has several (smaller) neig
|
||||
edge_neigs.neig_idx.len() > 1
|
||||
// or it has only one neig and if so
|
||||
// we draw the side either if the node's idx is < to the neig's idx
|
||||
|| (edge_neigs.neig_idx.len() == 1
|
||||
&& cell.depth() == neig_cell.depth()
|
||||
&& neig_cell.idx() > cell.idx())
|
||||
// or we draw the side if the neig is smaller than the node
|
||||
|| (edge_neigs.neig_idx.len() == 1
|
||||
&& cell.depth() < neig_cell.depth());
|
||||
|
||||
if draw_side {
|
||||
debug_assert!(edge_neigs.max_depth_neig >= cell.depth());
|
||||
|
||||
// draw the NW edge
|
||||
edges.put(Ordinal::NW, n.compute_n_seg(Ordinal::NW));
|
||||
}
|
||||
} else {
|
||||
// draw the NW edge because there are no neig along that edge
|
||||
edges.put(Ordinal::NW, n.compute_n_seg(Ordinal::NW));
|
||||
}
|
||||
|
||||
if let Some(edge_neigs) = &n.edge_neigs[Ordinal::SW as u8 as usize] {
|
||||
// if the smallest neig for this edge is smaller than self
|
||||
let _smallest_neig_depth = edge_neigs.max_depth_neig;
|
||||
|
||||
let first_neig_idx = edge_neigs.neig_idx[0];
|
||||
let neig_cell = &g.nodes()[first_neig_idx].cell;
|
||||
|
||||
let draw_side =
|
||||
// the current node has several (smaller) neig
|
||||
edge_neigs.neig_idx.len() > 1
|
||||
// or it has only one neig and if so
|
||||
// we draw the side either if the node's idx is < to the neig's idx
|
||||
|| (edge_neigs.neig_idx.len() == 1
|
||||
&& cell.depth() == neig_cell.depth()
|
||||
&& neig_cell.idx() > cell.idx())
|
||||
// or we draw the side if the neig is smaller than the node
|
||||
|| (edge_neigs.neig_idx.len() == 1
|
||||
&& cell.depth() < neig_cell.depth());
|
||||
|
||||
if draw_side {
|
||||
debug_assert!(edge_neigs.max_depth_neig >= cell.depth());
|
||||
|
||||
// draw the NW edge
|
||||
edges.put(Ordinal::SW, n.compute_n_seg(Ordinal::SW));
|
||||
}
|
||||
} else {
|
||||
// draw the NW edge because there are no neig along that edge
|
||||
edges.put(Ordinal::SW, n.compute_n_seg(Ordinal::SW));
|
||||
}
|
||||
|
||||
if let Some(edge_neigs) = &n.edge_neigs[Ordinal::SE as u8 as usize] {
|
||||
// if the smallest neig for this edge is smaller than self
|
||||
let _smallest_neig_depth = edge_neigs.max_depth_neig;
|
||||
|
||||
let first_neig_idx = edge_neigs.neig_idx[0];
|
||||
let neig_cell = &g.nodes()[first_neig_idx].cell;
|
||||
|
||||
let draw_side =
|
||||
// the current node has several (smaller) neig
|
||||
edge_neigs.neig_idx.len() > 1
|
||||
// or it has only one neig and if so
|
||||
// we draw the side either if the node's idx is < to the neig's idx
|
||||
|| (edge_neigs.neig_idx.len() == 1
|
||||
&& cell.depth() == neig_cell.depth()
|
||||
&& neig_cell.idx() > cell.idx())
|
||||
// or we draw the side if the neig is smaller than the node
|
||||
|| (edge_neigs.neig_idx.len() == 1
|
||||
&& cell.depth() < neig_cell.depth());
|
||||
|
||||
if draw_side {
|
||||
debug_assert!(edge_neigs.max_depth_neig >= cell.depth());
|
||||
|
||||
edges.put(Ordinal::SE, n.compute_n_seg(Ordinal::SE));
|
||||
}
|
||||
} else {
|
||||
// draw the NW edge because there are no neig along that edge
|
||||
edges.put(Ordinal::SE, n.compute_n_seg(Ordinal::SE));
|
||||
}
|
||||
|
||||
if let Some(edge_neigs) = &n.edge_neigs[Ordinal::NE as u8 as usize] {
|
||||
// if the smallest neig for this edge is smaller than self
|
||||
let _smallest_neig_depth = edge_neigs.max_depth_neig;
|
||||
|
||||
let first_neig_idx = edge_neigs.neig_idx[0];
|
||||
let neig_cell = &g.nodes()[first_neig_idx].cell;
|
||||
|
||||
let draw_side =
|
||||
// the current node has several (smaller) neig
|
||||
edge_neigs.neig_idx.len() > 1
|
||||
// or it has only one neig and if so
|
||||
// we draw the side either if the node's idx is < to the neig's idx
|
||||
|| (edge_neigs.neig_idx.len() == 1
|
||||
&& cell.depth() == neig_cell.depth()
|
||||
&& neig_cell.idx() > cell.idx())
|
||||
// or we draw the side if the neig is smaller than the node
|
||||
|| (edge_neigs.neig_idx.len() == 1
|
||||
&& cell.depth() < neig_cell.depth());
|
||||
|
||||
if draw_side {
|
||||
debug_assert!(edge_neigs.max_depth_neig >= cell.depth());
|
||||
|
||||
// draw the NW edge
|
||||
edges.put(Ordinal::NE, n.compute_n_seg(Ordinal::NE));
|
||||
}
|
||||
} else {
|
||||
// draw the NE edge because there are no neig along that edge
|
||||
edges.put(Ordinal::NE, n.compute_n_seg(Ordinal::NE));
|
||||
}
|
||||
|
||||
/*let delta_depth = (3 - (cell.depth() as usize)).max(0) as u8;
|
||||
|
||||
cell.get_children_cells(delta_depth).map(move |child_cell| {
|
||||
let mut edges = OrdinalMap::new();
|
||||
edges.put(Ordinal::NW, 1);
|
||||
edges.put(Ordinal::SW, 1);
|
||||
edges.put(Ordinal::SE, 1);
|
||||
edges.put(Ordinal::NE, 1);
|
||||
Node {
|
||||
vertices: child_cell.path_along_sides(&edges),
|
||||
cell: child_cell,
|
||||
}
|
||||
})*/
|
||||
|
||||
if cell.depth() < 2 {
|
||||
/*let max_depth = crate::math::utils::log_2_unchecked(n_seg_nw)
|
||||
.max(crate::math::utils::log_2_unchecked(n_seg_se))
|
||||
.max(crate::math::utils::log_2_unchecked(n_seg_ne))
|
||||
.max(crate::math::utils::log_2_unchecked(n_seg_sw))
|
||||
+ cell.depth() as u32;
|
||||
let n_seg = if max_depth > 3 {
|
||||
1 << (max_depth - 3)
|
||||
} else {
|
||||
1
|
||||
};*/
|
||||
|
||||
cell.get_children_cells(2 - cell.depth())
|
||||
.map(|child_cell| {
|
||||
let mut edges = OrdinalMap::new();
|
||||
edges.put(Ordinal::NW, 1);
|
||||
edges.put(Ordinal::NE, 1);
|
||||
edges.put(Ordinal::SW, 1);
|
||||
edges.put(Ordinal::SE, 1);
|
||||
|
||||
Node {
|
||||
vertices: child_cell.path_along_sides(&edges),
|
||||
cell: child_cell,
|
||||
}
|
||||
})
|
||||
.collect()
|
||||
} else {
|
||||
vec![Node {
|
||||
vertices: cell.path_along_sides(&edges),
|
||||
cell,
|
||||
}]
|
||||
}
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
*/
|
||||
@@ -1,193 +0,0 @@
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
use super::super::graph::G;
|
||||
|
||||
pub struct Fill;
|
||||
|
||||
impl RenderMode for Fill {
|
||||
fn build(moc: &HEALPixCoverage) -> Vec<Node> {
|
||||
let g = G::new(moc);
|
||||
|
||||
let n_seg_from_dir = |n: &NodeEdgeNeigs, dir: Ordinal| -> u32 {
|
||||
if let Some(neigs) = g.get_neigs(n, dir) {
|
||||
if let Some(neig_side) = g.get_neig_dir(neigs[0], n) {
|
||||
n.compute_n_seg_with_neig_info(neigs[0], dir, neig_side)
|
||||
} else {
|
||||
1
|
||||
}
|
||||
} else {
|
||||
1
|
||||
}
|
||||
};
|
||||
|
||||
g.nodes_iter()
|
||||
.flat_map(|n| {
|
||||
let cell = n.cell;
|
||||
|
||||
// Draw all of the node's edges
|
||||
let n_seg_nw = n_seg_from_dir(n, Ordinal::NW);
|
||||
let n_seg_ne = n_seg_from_dir(n, Ordinal::NE);
|
||||
let n_seg_sw = n_seg_from_dir(n, Ordinal::SW);
|
||||
let n_seg_se = n_seg_from_dir(n, Ordinal::SE);
|
||||
|
||||
match cell.depth() {
|
||||
0 => {
|
||||
let n_seg_sw = (n_seg_sw >> 2).max(1);
|
||||
let n_seg_se = (n_seg_se >> 2).max(1);
|
||||
let n_seg_nw = (n_seg_nw >> 2).max(1);
|
||||
let n_seg_ne = (n_seg_ne >> 2).max(1);
|
||||
|
||||
cell.get_children_cells(2)
|
||||
.map(|child_cell| {
|
||||
let mut edges = OrdinalMap::new();
|
||||
|
||||
let off = child_cell.idx() - (cell.idx() << 4);
|
||||
|
||||
match off {
|
||||
// S
|
||||
0 => {
|
||||
edges.put(Ordinal::NW, 1);
|
||||
edges.put(Ordinal::NE, 1);
|
||||
edges.put(Ordinal::SW, n_seg_sw);
|
||||
edges.put(Ordinal::SE, n_seg_se);
|
||||
}
|
||||
// W
|
||||
10 => {
|
||||
edges.put(Ordinal::NW, n_seg_nw);
|
||||
edges.put(Ordinal::NE, 1);
|
||||
edges.put(Ordinal::SW, n_seg_sw);
|
||||
edges.put(Ordinal::SE, 1);
|
||||
}
|
||||
// E
|
||||
5 => {
|
||||
edges.put(Ordinal::NW, 1);
|
||||
edges.put(Ordinal::NE, n_seg_ne);
|
||||
edges.put(Ordinal::SW, 1);
|
||||
edges.put(Ordinal::SE, n_seg_se);
|
||||
}
|
||||
// N
|
||||
15 => {
|
||||
edges.put(Ordinal::NW, n_seg_nw);
|
||||
edges.put(Ordinal::NE, n_seg_ne);
|
||||
edges.put(Ordinal::SW, 1);
|
||||
edges.put(Ordinal::SE, 1);
|
||||
}
|
||||
// SE
|
||||
1 | 4 => {
|
||||
edges.put(Ordinal::NW, 1);
|
||||
edges.put(Ordinal::NE, 1);
|
||||
edges.put(Ordinal::SW, 1);
|
||||
edges.put(Ordinal::SE, n_seg_se);
|
||||
}
|
||||
// SW
|
||||
2 | 8 => {
|
||||
edges.put(Ordinal::NW, 1);
|
||||
edges.put(Ordinal::NE, 1);
|
||||
edges.put(Ordinal::SW, n_seg_sw);
|
||||
edges.put(Ordinal::SE, 1);
|
||||
}
|
||||
// NW
|
||||
11 | 14 => {
|
||||
edges.put(Ordinal::NW, n_seg_nw);
|
||||
edges.put(Ordinal::NE, 1);
|
||||
edges.put(Ordinal::SW, 1);
|
||||
edges.put(Ordinal::SE, 1);
|
||||
}
|
||||
// NE
|
||||
7 | 13 => {
|
||||
edges.put(Ordinal::NW, 1);
|
||||
edges.put(Ordinal::NE, n_seg_ne);
|
||||
edges.put(Ordinal::SW, 1);
|
||||
edges.put(Ordinal::SE, 1);
|
||||
}
|
||||
_ => {
|
||||
edges.put(Ordinal::NW, 1);
|
||||
edges.put(Ordinal::NE, 1);
|
||||
edges.put(Ordinal::SW, 1);
|
||||
edges.put(Ordinal::SE, 1);
|
||||
}
|
||||
}
|
||||
|
||||
Node {
|
||||
vertices: child_cell.path_along_sides(&edges),
|
||||
cell: child_cell,
|
||||
}
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
1 => {
|
||||
let n_seg_sw = (n_seg_sw >> 1).max(1);
|
||||
let n_seg_se = (n_seg_se >> 1).max(1);
|
||||
let n_seg_nw = (n_seg_nw >> 1).max(1);
|
||||
let n_seg_ne = (n_seg_ne >> 1).max(1);
|
||||
|
||||
cell.get_children_cells(1)
|
||||
.map(|child_cell| {
|
||||
let mut edges = OrdinalMap::new();
|
||||
|
||||
let off = child_cell.idx() - (cell.idx() << 2);
|
||||
match off {
|
||||
// S
|
||||
0 => {
|
||||
edges.put(Ordinal::NW, 1);
|
||||
edges.put(Ordinal::NE, 1);
|
||||
edges.put(Ordinal::SW, n_seg_sw);
|
||||
edges.put(Ordinal::SE, n_seg_se);
|
||||
}
|
||||
// W
|
||||
2 => {
|
||||
edges.put(Ordinal::NW, n_seg_nw);
|
||||
edges.put(Ordinal::NE, 1);
|
||||
edges.put(Ordinal::SW, n_seg_sw);
|
||||
edges.put(Ordinal::SE, 1);
|
||||
}
|
||||
// E
|
||||
1 => {
|
||||
edges.put(Ordinal::NW, 1);
|
||||
edges.put(Ordinal::NE, n_seg_ne);
|
||||
edges.put(Ordinal::SW, 1);
|
||||
edges.put(Ordinal::SE, n_seg_se);
|
||||
}
|
||||
// N
|
||||
3 => {
|
||||
edges.put(Ordinal::NW, n_seg_nw);
|
||||
edges.put(Ordinal::NE, n_seg_ne);
|
||||
edges.put(Ordinal::SW, 1);
|
||||
edges.put(Ordinal::SE, 1);
|
||||
}
|
||||
_ => {
|
||||
unimplemented!();
|
||||
}
|
||||
}
|
||||
|
||||
Node {
|
||||
vertices: child_cell.path_along_sides(&edges),
|
||||
cell: child_cell,
|
||||
}
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
_ => {
|
||||
let mut edges = OrdinalMap::new();
|
||||
|
||||
edges.put(Ordinal::NW, n_seg_nw);
|
||||
edges.put(Ordinal::NE, n_seg_ne);
|
||||
edges.put(Ordinal::SW, n_seg_sw);
|
||||
edges.put(Ordinal::SE, n_seg_se);
|
||||
|
||||
vec![Node {
|
||||
vertices: cell.path_along_sides(&edges),
|
||||
cell,
|
||||
}]
|
||||
}
|
||||
}
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
*/
|
||||
@@ -1,2 +0,0 @@
|
||||
pub mod edge;
|
||||
pub mod filled;
|
||||
@@ -1,39 +0,0 @@
|
||||
use super::Node;
|
||||
use super::RenderMode;
|
||||
use crate::healpix::cell::HEALPixCell;
|
||||
use healpix::compass_point::{Ordinal, OrdinalMap};
|
||||
use moclib::elem::cell::Cell;
|
||||
|
||||
use crate::HEALPixCoverage;
|
||||
use moclib::moc::range::CellAndEdges;
|
||||
|
||||
pub struct Perimeter;
|
||||
|
||||
impl RenderMode for Perimeter {
|
||||
fn build(moc: &HEALPixCoverage) -> impl Iterator<Item = Node> {
|
||||
moc.0
|
||||
.border_elementary_edges()
|
||||
.map(|CellAndEdges { uniq, edges }| {
|
||||
let c = Cell::from_uniq_hpx(uniq);
|
||||
let cell = HEALPixCell(c.depth, c.idx);
|
||||
|
||||
let mut map = OrdinalMap::new();
|
||||
if edges.get(moclib::moc::range::Ordinal::SE) {
|
||||
map.put(Ordinal::SE, 1);
|
||||
}
|
||||
if edges.get(moclib::moc::range::Ordinal::SW) {
|
||||
map.put(Ordinal::SW, 1);
|
||||
}
|
||||
if edges.get(moclib::moc::range::Ordinal::NE) {
|
||||
map.put(Ordinal::NE, 1);
|
||||
}
|
||||
if edges.get(moclib::moc::range::Ordinal::NW) {
|
||||
map.put(Ordinal::NW, 1);
|
||||
}
|
||||
|
||||
let vertices = cell.path_along_sides(&map);
|
||||
|
||||
Node { cell, vertices }
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -10,6 +10,7 @@ pub mod text;
|
||||
pub mod utils;
|
||||
|
||||
use crate::renderable::image::Image;
|
||||
use crate::tile_fetcher::TileFetcherQueue;
|
||||
|
||||
use al_core::image::format::ChannelType;
|
||||
|
||||
@@ -316,6 +317,7 @@ impl Layers {
|
||||
layer: &str,
|
||||
camera: &mut CameraViewPort,
|
||||
proj: &ProjectionType,
|
||||
tile_fetcher: &mut TileFetcherQueue,
|
||||
) -> Result<usize, JsValue> {
|
||||
let err_layer_not_found = JsValue::from_str(&format!(
|
||||
"Layer {:?} not found, so cannot be removed.",
|
||||
@@ -351,6 +353,9 @@ impl Layers {
|
||||
// remove the frame
|
||||
camera.unregister_view_frame(hips_frame, proj);
|
||||
|
||||
// remove the local files access from the tile fetcher
|
||||
tile_fetcher.delete_hips_local_files(s.get_config().get_creator_did());
|
||||
|
||||
Ok(id_layer)
|
||||
} else if let Some(_) = self.images.remove(&id) {
|
||||
// A FITS image has been found and removed
|
||||
@@ -418,6 +423,7 @@ impl Layers {
|
||||
hips: HiPSCfg,
|
||||
camera: &mut CameraViewPort,
|
||||
proj: &ProjectionType,
|
||||
tile_fetcher: &mut TileFetcherQueue,
|
||||
) -> Result<&HiPS, JsValue> {
|
||||
let HiPSCfg {
|
||||
layer,
|
||||
@@ -431,7 +437,7 @@ impl Layers {
|
||||
let layer_already_found = self.layers.iter().any(|l| l == &layer);
|
||||
|
||||
let idx = if layer_already_found {
|
||||
let idx = self.remove_layer(&layer, camera, proj)?;
|
||||
let idx = self.remove_layer(&layer, camera, proj, tile_fetcher)?;
|
||||
idx
|
||||
} else {
|
||||
self.layers.len()
|
||||
@@ -492,6 +498,7 @@ impl Layers {
|
||||
image: ImageLayer,
|
||||
camera: &mut CameraViewPort,
|
||||
proj: &ProjectionType,
|
||||
tile_fetcher: &mut TileFetcherQueue,
|
||||
) -> Result<&[Image], JsValue> {
|
||||
let ImageLayer {
|
||||
layer,
|
||||
@@ -504,7 +511,7 @@ impl Layers {
|
||||
let layer_already_found = self.layers.iter().any(|s| s == &layer);
|
||||
|
||||
let idx = if layer_already_found {
|
||||
let idx = self.remove_layer(&layer, camera, proj)?;
|
||||
let idx = self.remove_layer(&layer, camera, proj, tile_fetcher)?;
|
||||
idx
|
||||
} else {
|
||||
self.layers.len()
|
||||
|
||||
40
src/core/src/survey/bitvector.rs
Normal file
@@ -0,0 +1,40 @@
|
||||
struct BitVector<const N: usize>([u64; N]);
|
||||
|
||||
impl<const N: usize> Default for BitVector<N> {
|
||||
fn default() -> Self {
|
||||
Self([0_u64; N])
|
||||
}
|
||||
}
|
||||
|
||||
impl<const N: usize> BitVector<N> {
|
||||
pub fn new_empty() -> Self {
|
||||
Self::default()
|
||||
}
|
||||
pub fn set(&mut self, i: usize) {
|
||||
debug_assert!(i < (N << 6));
|
||||
let j = i >> 6;
|
||||
let k = i & 0x3f;
|
||||
|
||||
self.0[j] |= 1 << k;
|
||||
}
|
||||
|
||||
pub fn get(&self, i: usize) -> bool {
|
||||
debug_assert!(i < (N << 6));
|
||||
let j = i >> 6;
|
||||
let k = i & 0x3f;
|
||||
|
||||
(self.0[j] >> k) & 0x1 == 1
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::BitVector;
|
||||
|
||||
#[test]
|
||||
fn test_bitvector_basic_op() {
|
||||
let mut bv: BitVector<32> = BitVector::new_empty();
|
||||
bv.set(64);
|
||||
assert!(bv.get(64));
|
||||
}
|
||||
}
|
||||
@@ -36,8 +36,8 @@ pub struct TextureCellItem {
|
||||
}
|
||||
|
||||
impl TextureCellItem {
|
||||
fn is_root(&self, delta_depth: u8) -> bool {
|
||||
self.cell.is_root(delta_depth)
|
||||
fn is_root(&self) -> bool {
|
||||
self.cell.is_root()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
@@ -318,7 +318,7 @@ impl ImageSurveyTextures {
|
||||
// Get the texture cell in which the tile has to be
|
||||
let tex_cell = cell.get_texture_cell(dd);
|
||||
|
||||
let tex_cell_is_root = tex_cell.is_root(dd);
|
||||
let tex_cell_is_root = tex_cell.is_root();
|
||||
if !tex_cell_is_root && !self.textures.contains_key(&tex_cell) {
|
||||
// The texture is not among the essential ones
|
||||
// (i.e. is not a root texture)
|
||||
@@ -326,7 +326,7 @@ impl ImageSurveyTextures {
|
||||
// Pop the oldest requested texture
|
||||
let oldest_texture = self.heap.pop().unwrap_abort();
|
||||
// Ensure this is not a base texture
|
||||
debug_assert!(!oldest_texture.is_root(self.config.delta_depth()));
|
||||
debug_assert!(!oldest_texture.is_root());
|
||||
|
||||
// Remove it from the textures HashMap
|
||||
let mut texture = self.textures.remove(&oldest_texture.cell).expect(
|
||||
@@ -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;
|
||||
@@ -448,9 +448,10 @@ impl ImageSurveyTextures {
|
||||
// texture ancestor exists and then, it it contains the tile
|
||||
pub fn contains_tile(&self, cell: &HEALPixCell) -> bool {
|
||||
let dd = self.config.delta_depth();
|
||||
|
||||
let texture_cell = cell.get_texture_cell(dd);
|
||||
|
||||
let tex_cell_is_root = texture_cell.is_root(dd);
|
||||
let tex_cell_is_root = texture_cell.is_root();
|
||||
if tex_cell_is_root {
|
||||
let HEALPixCell(_, idx) = texture_cell;
|
||||
self.base_textures[idx as usize].contains(cell)
|
||||
@@ -471,10 +472,11 @@ impl ImageSurveyTextures {
|
||||
pub fn update_priority(&mut self, cell: &HEALPixCell /*, new_fov_cell: bool*/) {
|
||||
debug_assert!(self.contains_tile(cell));
|
||||
|
||||
// Get the texture cell in which the tile has to be
|
||||
let dd = self.config.delta_depth();
|
||||
|
||||
// Get the texture cell in which the tile has to be
|
||||
let texture_cell = cell.get_texture_cell(dd);
|
||||
if texture_cell.is_root(dd) {
|
||||
if texture_cell.is_root() {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -556,7 +558,7 @@ impl ImageSurveyTextures {
|
||||
|
||||
/// Accessors
|
||||
pub fn get(&self, texture_cell: &HEALPixCell) -> Option<&Texture> {
|
||||
if texture_cell.is_root(self.config().delta_depth()) {
|
||||
if texture_cell.is_root() {
|
||||
let HEALPixCell(_, idx) = texture_cell;
|
||||
Some(&self.base_textures[*idx as usize])
|
||||
} else {
|
||||
@@ -566,14 +568,13 @@ impl ImageSurveyTextures {
|
||||
|
||||
// Get the nearest parent tile found in the CPU buffer
|
||||
pub fn get_nearest_parent(&self, cell: &HEALPixCell) -> Option<HEALPixCell> {
|
||||
let dd = self.config.delta_depth();
|
||||
if cell.is_root(dd) {
|
||||
if cell.is_root() {
|
||||
// Root cells are in the buffer by definition
|
||||
Some(*cell)
|
||||
} else {
|
||||
let mut parent_cell = cell.parent();
|
||||
|
||||
while !self.contains(&parent_cell) && !parent_cell.is_root(dd) {
|
||||
while !self.contains(&parent_cell) && !parent_cell.is_root() {
|
||||
parent_cell = parent_cell.parent();
|
||||
}
|
||||
|
||||
@@ -628,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> {
|
||||
@@ -662,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 {
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
pub mod bitvector;
|
||||
pub mod buffer;
|
||||
pub mod config;
|
||||
pub mod texture;
|
||||
|
||||
@@ -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"),
|
||||
|
||||
@@ -3,9 +3,11 @@ use crate::renderable::HiPS;
|
||||
use crate::time::{DeltaTime, Time};
|
||||
use crate::Abort;
|
||||
|
||||
use std::collections::VecDeque;
|
||||
use std::cell::RefCell;
|
||||
use std::collections::{HashMap, VecDeque};
|
||||
use std::rc::Rc;
|
||||
|
||||
const MAX_NUM_TILE_FETCHING: isize = 8;
|
||||
const MAX_NUM_TILE_FETCHING: usize = 8;
|
||||
const MAX_QUERY_QUEUE_LENGTH: usize = 100;
|
||||
|
||||
pub struct TileFetcherQueue {
|
||||
@@ -13,26 +15,102 @@ pub struct TileFetcherQueue {
|
||||
queries: VecDeque<query::Tile>,
|
||||
base_tile_queries: Vec<query::Tile>,
|
||||
tiles_fetched_time: Time,
|
||||
num_tiles_fetched: usize,
|
||||
|
||||
hips_local_files: HashMap<CreatorDid, HiPSLocalFiles>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[wasm_bindgen]
|
||||
pub struct HiPSLocalFiles {
|
||||
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(moc: web_sys::File) -> Self {
|
||||
let tiles_per_fmt = vec![HashMap::new(); 30].into_boxed_slice();
|
||||
|
||||
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, 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_tile(&self, cell: &HEALPixCell, ext: ImageExt) -> Option<&web_sys::File> {
|
||||
let d = cell.depth() as usize;
|
||||
let i = cell.idx();
|
||||
|
||||
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 {
|
||||
let queries = VecDeque::new();
|
||||
let base_tile_queries = Vec::new();
|
||||
let tiles_fetched_time = Time::now();
|
||||
let num_tiles_fetched = 0;
|
||||
|
||||
Self {
|
||||
queries,
|
||||
base_tile_queries,
|
||||
tiles_fetched_time,
|
||||
num_tiles_fetched,
|
||||
hips_local_files: HashMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn insert_hips_local_files(&mut self, id: CreatorDid, local_files: HiPSLocalFiles) {
|
||||
self.hips_local_files.insert(id, local_files);
|
||||
}
|
||||
|
||||
pub fn delete_hips_local_files(&mut self, id: &str) {
|
||||
self.hips_local_files.remove(id);
|
||||
}
|
||||
|
||||
pub fn clear(&mut self) {
|
||||
self.queries.clear();
|
||||
//self.query_set.clear();
|
||||
}
|
||||
|
||||
pub fn append(&mut self, query: query::Tile, _downloader: &mut Downloader) {
|
||||
pub fn append(&mut self, query: query::Tile) {
|
||||
// Check if the query has already been done
|
||||
//if !self.query_set.contains(&query) {
|
||||
// discard too old tile queries
|
||||
@@ -44,11 +122,11 @@ impl TileFetcherQueue {
|
||||
}
|
||||
|
||||
// fetch the base tile
|
||||
pub fn append_base_tile(&mut self, query: query::Tile, _downloader: &mut Downloader) {
|
||||
pub fn append_base_tile(&mut self, query: query::Tile) {
|
||||
self.base_tile_queries.push(query);
|
||||
}
|
||||
|
||||
pub fn notify(&mut self, downloader: &mut Downloader, dt: Option<DeltaTime>) {
|
||||
pub fn notify(&mut self, downloader: Rc<RefCell<Downloader>>, dt: Option<DeltaTime>) {
|
||||
// notify all the x ms
|
||||
let now = Time::now();
|
||||
|
||||
@@ -63,35 +141,78 @@ impl TileFetcherQueue {
|
||||
}
|
||||
}
|
||||
|
||||
fn fetch(&mut self, downloader: &mut Downloader) {
|
||||
pub fn get_num_tile_fetched(&self) -> usize {
|
||||
self.num_tiles_fetched
|
||||
}
|
||||
|
||||
fn check_in_file_list(&self, mut query: Tile) -> Result<Tile, JsValue> {
|
||||
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)
|
||||
} else {
|
||||
Err(JsValue::from_str("could not create an url from the tile"))
|
||||
}
|
||||
} else {
|
||||
Ok(query)
|
||||
}
|
||||
} else {
|
||||
Ok(query)
|
||||
}
|
||||
}
|
||||
|
||||
fn fetch(&mut self, downloader: Rc<RefCell<Downloader>>) {
|
||||
// Fetch the base tiles with higher priority
|
||||
while let Some(query) = self.base_tile_queries.pop() {
|
||||
//if downloader.fetch(query) {
|
||||
// The fetch has succeded
|
||||
//self.num_tiles_fetched += 1;
|
||||
//}
|
||||
downloader.fetch(query);
|
||||
if let Ok(query) = self.check_in_file_list(query) {
|
||||
downloader.borrow_mut().fetch(query);
|
||||
}
|
||||
}
|
||||
|
||||
let mut num_fetched_tile = 0;
|
||||
while num_fetched_tile < MAX_NUM_TILE_FETCHING && !self.queries.is_empty() {
|
||||
let query = self.queries.pop_back().unwrap_abort();
|
||||
|
||||
if downloader.fetch(query) {
|
||||
// The fetch has succeded
|
||||
num_fetched_tile += 1;
|
||||
if let Ok(query) = self.check_in_file_list(query) {
|
||||
if downloader.borrow_mut().fetch(query) {
|
||||
// The fetch has succeded
|
||||
num_fetched_tile += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self.num_tiles_fetched += num_fetched_tile;
|
||||
}
|
||||
|
||||
pub fn launch_starting_hips_requests(&mut self, hips: &HiPS, downloader: &mut Downloader) {
|
||||
pub fn launch_starting_hips_requests(
|
||||
&mut self,
|
||||
hips: &HiPS,
|
||||
downloader: Rc<RefCell<Downloader>>,
|
||||
) {
|
||||
let cfg = hips.get_config();
|
||||
// Request for the allsky first
|
||||
// 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
|
||||
downloader.fetch(query::Moc::new(
|
||||
format!("{}/Moc.fits", cfg.get_root_url()),
|
||||
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(
|
||||
moc_url,
|
||||
cfg.get_creator_did().to_string(),
|
||||
al_api::moc::MOC::default(),
|
||||
));
|
||||
@@ -100,17 +221,33 @@ impl TileFetcherQueue {
|
||||
//Request the allsky for the small tile size or if base tiles are not available
|
||||
if tile_size <= 128 || cfg.get_min_depth_tile() > 0 {
|
||||
// Request the allsky
|
||||
downloader.fetch(query::Allsky::new(cfg));
|
||||
}
|
||||
/*else {
|
||||
for texture_cell in crate::healpix::cell::ALLSKY_HPX_CELLS_D0 {
|
||||
for cell in texture_cell.get_tile_cells(cfg.delta_depth()) {
|
||||
let hips_url = cfg.get_root_url();
|
||||
let format = cfg.get_format();
|
||||
let query = query::Tile::new(&cell, hips_url.to_string(), format);
|
||||
self.append_base_tile(query, downloader);
|
||||
downloader.borrow_mut().fetch(query::Allsky::new(cfg));
|
||||
} else if cfg.get_min_depth_tile() == 0 {
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
{
|
||||
let hips_cdid = cfg.get_creator_did().to_string();
|
||||
let hips_url = cfg.get_root_url().to_string();
|
||||
let hips_fmt = cfg.get_format();
|
||||
let min_order = cfg.get_min_depth_texture();
|
||||
|
||||
for tile_cell in crate::healpix::cell::ALLSKY_HPX_CELLS_D0 {
|
||||
if let Ok(query) = self.check_in_file_list(query::Tile::new(
|
||||
tile_cell,
|
||||
hips_cdid.clone(),
|
||||
hips_url.clone(),
|
||||
hips_fmt,
|
||||
)) {
|
||||
let dl = downloader.clone();
|
||||
|
||||
crate::utils::set_timeout(
|
||||
move || {
|
||||
dl.borrow_mut().fetch(query);
|
||||
},
|
||||
2_000,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}*/
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -76,6 +76,14 @@ impl Add<DeltaTime> for Time {
|
||||
}
|
||||
}
|
||||
|
||||
impl Sub<DeltaTime> for Time {
|
||||
type Output = Self;
|
||||
|
||||
fn sub(self, duration: DeltaTime) -> Self {
|
||||
Time(self.0 - duration.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl Mul<f32> for DeltaTime {
|
||||
type Output = Self;
|
||||
|
||||
|
||||
@@ -95,3 +95,40 @@ pub(super) fn merge_overlapping_intervals(mut intervals: Vec<Range<usize>>) -> V
|
||||
|
||||
intervals
|
||||
}
|
||||
|
||||
/*
|
||||
Execute a closure after some delay. This mimics the javascript built-in setTimeout procedure.
|
||||
*/
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
pub(crate) fn set_timeout<F>(f: F, delay: i32)
|
||||
where
|
||||
F: 'static + FnOnce() -> (),
|
||||
{
|
||||
use std::cell::Cell;
|
||||
use std::rc::Rc;
|
||||
use wasm_bindgen::closure::Closure;
|
||||
use wasm_bindgen::JsCast;
|
||||
|
||||
let timeout_id = Rc::new(Cell::new(0));
|
||||
let t_id = timeout_id.clone();
|
||||
let cb = Closure::once_into_js(move || {
|
||||
f();
|
||||
|
||||
web_sys::window()
|
||||
.unwrap()
|
||||
.clear_timeout_with_handle(t_id.get());
|
||||
});
|
||||
|
||||
let window = web_sys::window().unwrap();
|
||||
timeout_id.set(
|
||||
window
|
||||
.set_timeout_with_callback_and_timeout_and_arguments_0(
|
||||
// Note this method call, which uses `as_ref()` to get a `JsValue`
|
||||
// from our `Closure` which is then converted to a `&Function`
|
||||
// using the `JsCast::unchecked_ref` function.
|
||||
cb.unchecked_ref(),
|
||||
delay,
|
||||
)
|
||||
.unwrap(),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -210,7 +210,6 @@
|
||||
background: whitesmoke;
|
||||
border-radius: 2px;
|
||||
position: absolute;
|
||||
max-width: 15em;
|
||||
font-size: inherit;
|
||||
|
||||
font-family: monospace;
|
||||
@@ -226,7 +225,7 @@
|
||||
/*overflow-y: auto;*/
|
||||
overflow-y: none;
|
||||
max-height: 500px;
|
||||
max-width: fit-content;
|
||||
/*max-width: fit-content;*/
|
||||
height: fit-content;
|
||||
}
|
||||
|
||||
@@ -556,7 +555,7 @@ canvas {
|
||||
}
|
||||
|
||||
.aladin-popup-container {
|
||||
|
||||
color: black;
|
||||
position: absolute;
|
||||
width: 200px;
|
||||
display: none;
|
||||
@@ -1117,28 +1116,23 @@ canvas {
|
||||
width: 4.5rem;
|
||||
}
|
||||
|
||||
/*
|
||||
.aladin-input-text.aladin-dark-theme.search.aladin-HiPS-search {
|
||||
width: 100%;
|
||||
|
||||
}*/
|
||||
|
||||
.aladin-stack-box {
|
||||
width: 17rem;
|
||||
}
|
||||
|
||||
.aladin-HiPS-browser-box .aladin-input-text {
|
||||
width: 300px;
|
||||
width: 100%;
|
||||
min-width: 300px;
|
||||
padding: 0.5rem;
|
||||
}
|
||||
|
||||
.aladin-cat-browser-box .aladin-input-text.search {
|
||||
width: 300px;
|
||||
width: 100%;
|
||||
min-width: 300px;
|
||||
padding-top: 0.5rem;
|
||||
padding-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
|
||||
.aladin-location {
|
||||
position: absolute;
|
||||
top: 0.2rem;
|
||||
|
||||
@@ -3,6 +3,8 @@ precision highp float;
|
||||
precision mediump int;
|
||||
|
||||
layout (location = 0) in vec2 pos_clip_space;
|
||||
layout (location = 1) in vec3 pos_world_space;
|
||||
|
||||
out vec2 out_clip_pos;
|
||||
out vec3 frag_pos;
|
||||
|
||||
@@ -10,14 +12,11 @@ uniform vec2 ndc_to_clip;
|
||||
uniform float czf;
|
||||
uniform mat4 model;
|
||||
|
||||
uniform sampler2D position_tex;
|
||||
|
||||
void main() {
|
||||
vec2 uv = pos_clip_space * 0.5 + 0.5;
|
||||
vec3 world_pos = texture(position_tex, uv).rgb;
|
||||
//world_pos = check_inversed_longitude(world_pos);
|
||||
|
||||
frag_pos = vec3(model * vec4(world_pos, 1.0));
|
||||
frag_pos = vec3(model * vec4(pos_world_space, 1.0));
|
||||
|
||||
gl_Position = vec4(pos_clip_space / (ndc_to_clip * czf), 0.0, 1.0);
|
||||
out_clip_pos = pos_clip_space;
|
||||
|
||||
60
src/js/A.js
@@ -40,7 +40,6 @@ import { ProgressiveCat } from "./ProgressiveCat.js";
|
||||
import { Source } from "./Source.js";
|
||||
import { Coo } from "./libs/astro/coo.js";
|
||||
import { URLBuilder } from "./URLBuilder.js";
|
||||
import { ColorCfg } from './ColorCfg.js';
|
||||
import { Footprint } from './Footprint.js';
|
||||
import { Aladin } from "./Aladin.js";
|
||||
import { ActionButton } from "./gui/Widgets/ActionButton.js";
|
||||
@@ -52,8 +51,7 @@ import { Sesame } from "./Sesame.js";
|
||||
import init, * as module from './../core/pkg';
|
||||
|
||||
// Import aladin css inside the project
|
||||
import './../css/aladin.css';
|
||||
|
||||
import aladinCSS from './../css/aladin.css?inline';
|
||||
|
||||
///////////////////////////////
|
||||
/////// Aladin Lite API ///////
|
||||
@@ -98,6 +96,13 @@ A.aladin = function (divSelector, options) {
|
||||
} else {
|
||||
divElement = divSelector;
|
||||
}
|
||||
|
||||
// Associate the CSS inside the div
|
||||
var cssStyleSheet = document.createElement('style')
|
||||
cssStyleSheet.classList.add("aladin-css");
|
||||
cssStyleSheet.innerHTML = aladinCSS;
|
||||
divElement.appendChild(cssStyleSheet)
|
||||
|
||||
return new Aladin(divElement, options);
|
||||
};
|
||||
|
||||
@@ -105,14 +110,18 @@ A.aladin = function (divSelector, options) {
|
||||
* Creates a HiPS image object
|
||||
*
|
||||
* @function
|
||||
* @name A.imageHiPS
|
||||
* @name A.HiPS
|
||||
* @memberof A
|
||||
* @param {string} id - Can be either an `url` that refers to a HiPS.
|
||||
* Or it can be a "CDS ID" pointing towards a HiPS. One can found the list of IDs {@link https://aladin.cds.unistra.fr/hips/list| here}.
|
||||
* @param {ImageHiPSOptions} [options] - Options describing the survey
|
||||
* @returns {ImageHiPS} - A HiPS image object
|
||||
* @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
|
||||
*/
|
||||
A.imageHiPS = function (id, options) {
|
||||
A.HiPS = function (id, options) {
|
||||
let url = id;
|
||||
return Aladin.createImageSurvey(
|
||||
id,
|
||||
@@ -124,6 +133,15 @@ A.imageHiPS = function (id, options) {
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @function
|
||||
* @name A.imageHiPS
|
||||
* @memberof A
|
||||
* @deprecated
|
||||
* Old method name, use {@link A.HiPS} instead.
|
||||
*/
|
||||
A.imageHiPS = A.HiPS;
|
||||
|
||||
/**
|
||||
* Creates a celestial source object with the given coordinates.
|
||||
*
|
||||
@@ -376,8 +394,9 @@ A.coo = function (longitude, latitude, prec) {
|
||||
* @memberof A
|
||||
* @name footprint
|
||||
*
|
||||
* @param {A.polygon[]} shapes - an array of A.polygon objects
|
||||
* @param {A.source} source - a A.source object
|
||||
* @param {Circle[]|Polyline[]|Ellipse[]|Vector[]} shapes - an array of A.polygon objects
|
||||
* @param {Source} [source] - a A.source object associated with the footprint
|
||||
*
|
||||
* @returns {Footprint} Returns a new Footprint object
|
||||
*/
|
||||
A.footprint = function(shapes, source) {
|
||||
@@ -434,17 +453,19 @@ A.MOCFromURL = function (url, options, successCallback, errorCallback) {
|
||||
* @returns {MOC} Returns a new MOC object
|
||||
*
|
||||
* @example
|
||||
* var json = {"3":[517],
|
||||
* "4":[2065,2066,2067,2112,2344,2346,2432],
|
||||
* "5":[8221,8257,8258,8259,8293,8304,8305,8307,8308,8452,8456,9346,9352,9354,9736],
|
||||
* "6":[32861,32862,32863,32881,32882,32883,32892,32893,33025,33026,33027,33157,33168,33169,33171,
|
||||
* var json = {
|
||||
* "3": [517],
|
||||
* "4": [2065,2066,2067,2112,2344,2346,2432],
|
||||
* "5": [8221,8257,8258,8259,8293,8304,8305,8307,8308,8452,8456,9346,9352,9354,9736],
|
||||
* "6": [32861,32862,32863,32881,32882,32883,32892,32893,33025,33026,33027,33157,33168,33169,33171,
|
||||
* 33181,33224,33225,33227,33236,33240,33812,33816,33828,33832,37377,37378,37379,37382,37388,
|
||||
* 37390,37412,37414,37420,37422,37562,38928,38930,38936,38948,38952],
|
||||
* "7":[131423,131439,131443,131523,131556,131557,131580,131581,132099,132612,132613,132624,132625,132627,132637,
|
||||
* "7": [131423,131439,131443,131523,131556,131557,131580,131581,132099,132612,132613,132624,132625,132627,132637,
|
||||
* 132680,132681,132683,132709,132720,132721,132904,132905,132948,132952,132964,132968,133008,133009,133012,135252,135256,135268,135316,135320,135332,135336,148143,148152,148154,149507,149520
|
||||
* ,149522,149523,149652,149654,149660,149662,149684,149686,149692,149694,149695,150120,150122,150208,150210,150216,150218,150240,150242,150243,155748,155752,155796,155800,155812,155816]};
|
||||
* ,149522,149523,149652,149654,149660,149662,149684,149686,149692,149694,149695,150120,150122,150208,150210,150216,150218,150240,150242,150243,155748,155752,155796,155800,155812,155816]
|
||||
* };
|
||||
* var moc = A.MOCFromJSON(json, {opacity: 0.25, color: 'magenta', lineWidth: 3});
|
||||
* aladin.addMOC(moc);
|
||||
* aladin.addMOC(moc);
|
||||
*/
|
||||
A.MOCFromJSON = function (jsonMOC, options, successCallback, errorCallback) {
|
||||
var moc = new MOC(options);
|
||||
@@ -803,6 +824,7 @@ A.catalogFromSKAORucio = function (target, radiusDegrees, options, successCallba
|
||||
*
|
||||
* @example
|
||||
* const cat = A.catalogFromVizieR('I/311/hip2', 'M 45', 5, {onClick: 'showTable'});
|
||||
* const cat2 = A.catalogFromVizieR('I/311/hip2', '12 +9', 5, {onClick: 'showTable'});
|
||||
*/
|
||||
A.catalogFromVizieR = function (vizCatId, target, radius, options, successCallback, errorCallback) {
|
||||
options = options || {};
|
||||
@@ -970,7 +992,7 @@ A.box = function(options) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns utils object
|
||||
* Returns Utils object.
|
||||
*
|
||||
* This contains utilitary methods such as HEALPix basic or projection methods.
|
||||
*
|
||||
|
||||
333
src/js/Aladin.js
@@ -37,7 +37,7 @@ import { Sesame } from "./Sesame.js";
|
||||
import { PlanetaryFeaturesNameResolver } from "./PlanetaryFeaturesNameResolver.js";
|
||||
import { CooFrameEnum } from "./CooFrameEnum.js";
|
||||
import { MeasurementTable } from "./MeasurementTable.js";
|
||||
import { ImageHiPS } from "./ImageHiPS.js";
|
||||
import { HiPS } from "./HiPS.js";
|
||||
import { Coo } from "./libs/astro/coo.js";
|
||||
import { CooConversion } from "./CooConversion.js";
|
||||
import { HiPSCache } from "./HiPSCache.js";
|
||||
@@ -79,7 +79,7 @@ import { Polyline } from "./shapes/Polyline";
|
||||
* @typedef {Object} AladinOptions
|
||||
* @description Options for configuring the Aladin Lite instance.
|
||||
*
|
||||
* @property {string} [survey="CDS/P/DSS2/color"] URL or ID of the survey to use
|
||||
* @property {string} [survey="P/DSS2/color"] URL or ID of the survey to use
|
||||
* @property {string[]} [surveyUrl]
|
||||
* Array of URLs for the survey images. This replaces the survey parameter.
|
||||
* @property {Object[]|string[]} [hipsList] A list of predefined HiPS for the Aladin instance.
|
||||
@@ -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',
|
||||
@@ -160,7 +161,7 @@ import { Polyline } from "./shapes/Polyline";
|
||||
name: 'DESI Legacy Surveys color (g, r, i, z)',
|
||||
id: 'CDS/P/DESI-Legacy-Surveys/DR10/color',
|
||||
},
|
||||
// HiPS with options. Fields accepted are those described in {@link A.imageHiPSOptions}.
|
||||
// HiPS with options. Fields accepted are those described in {@link A.hiPSOptions}.
|
||||
{
|
||||
name: "SDSS9 band-g",
|
||||
id: "P/SDSS9/g",
|
||||
@@ -262,7 +263,6 @@ export let Aladin = (function () {
|
||||
this.callbacksByEventName = {}; // we store the callback functions (on 'zoomChanged', 'positionChanged', ...) here
|
||||
this.hipsCache = new HiPSCache();
|
||||
|
||||
console.log(this.hipsCache)
|
||||
// check that aladinDiv exists, stop immediately otherwise
|
||||
if (!aladinDiv) {
|
||||
console.error(
|
||||
@@ -380,46 +380,6 @@ export let Aladin = (function () {
|
||||
}
|
||||
}
|
||||
|
||||
if (options.survey) {
|
||||
if (Array.isArray(options.survey)) {
|
||||
let i = 0;
|
||||
options.survey.forEach((rootURLOrId) => {
|
||||
if (i == 0) {
|
||||
this.setBaseImageLayer(rootURLOrId);
|
||||
} else {
|
||||
this.setOverlayImageLayer(rootURLOrId, Utils.uuidv4());
|
||||
}
|
||||
i++;
|
||||
});
|
||||
} else if (options.survey === ImageHiPS.DEFAULT_SURVEY_ID) {
|
||||
// DSS is cached inside ImageHiPS class, no need to provide any further information
|
||||
const survey = this.createImageSurvey(
|
||||
ImageHiPS.DEFAULT_SURVEY_ID
|
||||
);
|
||||
|
||||
this.setBaseImageLayer(survey);
|
||||
} else {
|
||||
this.setBaseImageLayer(options.survey);
|
||||
}
|
||||
} else {
|
||||
// Add the image layers
|
||||
// For that we check the survey key of options
|
||||
// It can be given as a single string or an array of strings
|
||||
// for multiple blending surveys
|
||||
// take in priority the surveyUrl parameter
|
||||
let url;
|
||||
if (Array.isArray(options.surveyUrl)) {
|
||||
// mirrors given, take randomly one
|
||||
let numMirrors = options.surveyUrl.length;
|
||||
let id = Math.floor(Math.random() * numMirrors);
|
||||
url = options.surveyUrl[id];
|
||||
} else {
|
||||
url = options.surveyUrl;
|
||||
}
|
||||
|
||||
this.setBaseImageLayer(url);
|
||||
}
|
||||
|
||||
let hipsList = [].concat(options.hipsList);
|
||||
|
||||
const fillHiPSCache = () => {
|
||||
@@ -445,15 +405,6 @@ export let Aladin = (function () {
|
||||
|
||||
name = survey.name || survey.id || survey.url;
|
||||
|
||||
if (id && url) {
|
||||
console.warn(
|
||||
'Both "CDS ID" and url are given for ',
|
||||
survey,
|
||||
". ID is chosen."
|
||||
);
|
||||
url = null;
|
||||
}
|
||||
|
||||
cachedSurvey = { ...cachedSurvey, ...survey };
|
||||
} else {
|
||||
console.warn(
|
||||
@@ -478,19 +429,54 @@ export let Aladin = (function () {
|
||||
|
||||
// Merge what is already in the cache for that HiPS with new properties
|
||||
// coming from the MOCServer
|
||||
|
||||
/*HiPSCache.append(key, {
|
||||
...HiPSCache.get(key),
|
||||
...cachedSurvey,
|
||||
});*/
|
||||
let hips = new ImageHiPS(key, cachedSurvey.url, cachedSurvey)
|
||||
let hips = new HiPS(key, key, cachedSurvey)
|
||||
self.hipsCache.append(key, hips);
|
||||
}
|
||||
};
|
||||
this._setupUI(options);
|
||||
|
||||
|
||||
fillHiPSCache();
|
||||
|
||||
if (options.survey) {
|
||||
if (Array.isArray(options.survey)) {
|
||||
let i = 0;
|
||||
options.survey.forEach((rootURLOrId) => {
|
||||
if (i == 0) {
|
||||
this.setBaseImageLayer(rootURLOrId);
|
||||
} else {
|
||||
this.setOverlayImageLayer(rootURLOrId, Utils.uuidv4());
|
||||
}
|
||||
i++;
|
||||
});
|
||||
} else if (options.survey === HiPS.DEFAULT_SURVEY_ID) {
|
||||
// DSS is cached inside HiPS class, no need to provide any further information
|
||||
const survey = this.createImageSurvey(
|
||||
HiPS.DEFAULT_SURVEY_ID
|
||||
);
|
||||
|
||||
this.setBaseImageLayer(survey);
|
||||
} else {
|
||||
this.setBaseImageLayer(options.survey);
|
||||
}
|
||||
} else {
|
||||
// Add the image layers
|
||||
// For that we check the survey key of options
|
||||
// It can be given as a single string or an array of strings
|
||||
// for multiple blending surveys
|
||||
// take in priority the surveyUrl parameter
|
||||
let url;
|
||||
if (Array.isArray(options.surveyUrl)) {
|
||||
// mirrors given, take randomly one
|
||||
let numMirrors = options.surveyUrl.length;
|
||||
let id = Math.floor(Math.random() * numMirrors);
|
||||
url = options.surveyUrl[id];
|
||||
} else {
|
||||
url = options.surveyUrl;
|
||||
}
|
||||
|
||||
this.setBaseImageLayer(url);
|
||||
}
|
||||
|
||||
this.view.showCatalog(options.showCatalog);
|
||||
|
||||
// FullScreen toolbar icon
|
||||
@@ -544,9 +530,6 @@ export let Aladin = (function () {
|
||||
if (options.northPoleOrientation) {
|
||||
this.setViewCenter2NorthPoleAngle(options.northPoleOrientation);
|
||||
}
|
||||
|
||||
this._setupUI(options);
|
||||
|
||||
};
|
||||
|
||||
Aladin.prototype._setupUI = function (options) {
|
||||
@@ -680,7 +663,7 @@ export let Aladin = (function () {
|
||||
// access to WASM libraries
|
||||
Aladin.wasmLibs = {};
|
||||
Aladin.DEFAULT_OPTIONS = {
|
||||
survey: ImageHiPS.DEFAULT_SURVEY_ID,
|
||||
survey: HiPS.DEFAULT_SURVEY_ID,
|
||||
// surveys suggestion list
|
||||
hipsList: HiPSList.DEFAULT,
|
||||
//surveyUrl: ["https://alaskybis.unistra.fr/DSS/DSSColor", "https://alasky.unistra.fr/DSS/DSSColor"],
|
||||
@@ -731,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
|
||||
@@ -815,7 +799,7 @@ export let Aladin = (function () {
|
||||
var requestedSurveyId = Utils.urlParam("survey");
|
||||
if (
|
||||
requestedSurveyId &&
|
||||
ImageHiPS.getSurveyInfoFromId(requestedSurveyId)
|
||||
HiPS.getSurveyInfoFromId(requestedSurveyId)
|
||||
) {
|
||||
options.survey = requestedSurveyId;
|
||||
}
|
||||
@@ -1444,16 +1428,22 @@ export let Aladin = (function () {
|
||||
|
||||
Aladin.prototype.addMOC = function (moc) {
|
||||
this.view.addMOC(moc);
|
||||
|
||||
// see MOC.setView for sending it to outside the UI
|
||||
};
|
||||
|
||||
Aladin.prototype.addUI = function (ui) {
|
||||
this.ui.push(ui);
|
||||
ui.attachTo(this.aladinDiv);
|
||||
ui = [].concat(ui);
|
||||
|
||||
// as the ui is pushed to the dom, setting position may need the aladin instance to work
|
||||
// so we recompute it
|
||||
if (ui.options) {
|
||||
ui.update({ position: { ...ui.options.position, aladin: this } });
|
||||
for (var ui of ui) {
|
||||
this.ui.push(ui);
|
||||
ui.attachTo(this.aladinDiv);
|
||||
|
||||
// as the ui is pushed to the dom, setting position may need the aladin instance to work
|
||||
// so we recompute it
|
||||
if (ui.options) {
|
||||
ui.update({ position: { ...ui.options.position, aladin: this } });
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1474,7 +1464,7 @@ export let Aladin = (function () {
|
||||
* @memberof Aladin
|
||||
*/
|
||||
Aladin.prototype.removeOverlays = function () {
|
||||
this.view.removeLayers();
|
||||
this.view.removeOverlays();
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -1483,10 +1473,10 @@ export let Aladin = (function () {
|
||||
* @memberof Aladin
|
||||
*/
|
||||
Aladin.prototype.removeLayers = Aladin.prototype.removeOverlays;
|
||||
/**
|
||||
* @typedef {MOC|Catalog|ProgressiveCat|GraphicOverlay} Overlay
|
||||
* @description Possible overlays
|
||||
*/
|
||||
/**
|
||||
* @typedef {MOC|Catalog|ProgressiveCat|GraphicOverlay} Overlay
|
||||
* @description Possible overlays
|
||||
*/
|
||||
/**
|
||||
* Remove an overlay by its layer name
|
||||
*
|
||||
@@ -1494,10 +1484,11 @@ export let Aladin = (function () {
|
||||
* @param {string|Overlay} overlay - The name of the overlay to remove or the overlay object itself
|
||||
*/
|
||||
Aladin.prototype.removeOverlay = function (overlay) {
|
||||
if(overlay instanceof String)
|
||||
if(typeof overlay === 'string' || overlay instanceof String) {
|
||||
this.view.removeOverlayByName(overlay);
|
||||
else
|
||||
} else {
|
||||
this.view.removeOverlay(overlay);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -1508,22 +1499,21 @@ export let Aladin = (function () {
|
||||
Aladin.prototype.removeLayer = Aladin.prototype.removeOverlay;
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
* Creates and return an image survey (HiPS) object
|
||||
* Please use {@link A.imageHiPS} instead for creating a new survey image
|
||||
*
|
||||
* @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.
|
||||
* @param {ImageHiPSOptions} [options] - Options describing the survey
|
||||
* @returns {ImageHiPS} A HiPS image object.
|
||||
* @param {HiPSOptions} [options] - Options describing the survey
|
||||
* @returns {HiPS} A HiPS image object.
|
||||
*/
|
||||
Aladin.prototype.createImageSurvey = function (
|
||||
id,
|
||||
@@ -1533,48 +1523,34 @@ export let Aladin = (function () {
|
||||
maxOrder,
|
||||
options
|
||||
) {
|
||||
/*let surveyOptions = HiPSCache.get(id);
|
||||
|
||||
if (!surveyOptions) {
|
||||
surveyOptions = { name, maxOrder, cooFrame, ...options };
|
||||
surveyOptions.url = url;
|
||||
} else {
|
||||
// update the cached survey
|
||||
surveyOptions = {...surveyOptions, ...options};
|
||||
}*/
|
||||
|
||||
/*let hips = this.hipsCache.get(id);
|
||||
if (!hips) {
|
||||
options = { name, maxOrder, url, cooFrame, ...options };
|
||||
hips = new ImageHiPS(id, options.url, options)
|
||||
this.hipsCache.append(id, hips);
|
||||
} */
|
||||
|
||||
let hips = new ImageHiPS(id, url, { name, maxOrder, url, cooFrame, ...options })
|
||||
let hips = new HiPS(id, url || id, { name, maxOrder, url, cooFrame, ...options })
|
||||
|
||||
if (this instanceof Aladin && !this.hipsCache.contains(id)) {
|
||||
// Add it to the cache as soon as possible if we have a reference to the aladin object
|
||||
this.hipsCache.append(hips.id, hips)
|
||||
}
|
||||
|
||||
return hips;
|
||||
};
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
* Creates and return an image survey (HiPS) object.
|
||||
* Please use {@link A.imageHiPS} instead for creating a new survey image
|
||||
*
|
||||
* @function createImageSurvey
|
||||
* @memberof Aladin
|
||||
* @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.
|
||||
* @param {ImageHiPSOptions} [options] - Options describing the survey
|
||||
* @returns {ImageHiPS} A HiPS image object.
|
||||
* @param {HiPSOptions} [options] - Options describing the survey
|
||||
* @returns {HiPS} A HiPS image object.
|
||||
*/
|
||||
Aladin.createImageSurvey = Aladin.prototype.createImageSurvey;
|
||||
|
||||
@@ -1584,11 +1560,11 @@ export let Aladin = (function () {
|
||||
* @throws A warning when the asset is currently present in the view
|
||||
*
|
||||
* @memberof Aladin
|
||||
* @param {string|ImageHiPS|Image} urlOrHiPSOrFITS - Can be:
|
||||
* @param {string|HiPS|Image} urlOrHiPSOrFITS - Can be:
|
||||
* <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 ImageHiPS} HiPS object created from {@link A.imageHiPS}</li>
|
||||
* <li>3. A {@link HiPS} HiPS object created from {@link A.HiPS}</li>
|
||||
* <li>4. A {@link Image} FITS image object</li>
|
||||
* </ul>
|
||||
*/
|
||||
@@ -1609,11 +1585,11 @@ export let Aladin = (function () {
|
||||
* Check whether a survey is currently in the view
|
||||
*
|
||||
* @memberof Aladin
|
||||
* @param {string|ImageHiPS|Image} urlOrHiPSOrFITS - Can be:
|
||||
* @param {string|HiPS|Image} urlOrHiPSOrFITS - Can be:
|
||||
* <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 ImageHiPS} HiPS object created from {@link A.imageHiPS}</li>
|
||||
* <li>3. A {@link HiPS} HiPS object</li>
|
||||
* <li>4. A {@link Image} Image object</li>
|
||||
* </ul>
|
||||
*/
|
||||
@@ -1653,12 +1629,6 @@ export let Aladin = (function () {
|
||||
// Do not use proxy with CORS headers until we solve that: https://github.com/MattiasBuelens/wasm-streams/issues/20
|
||||
//url = Utils.handleCORSNotSameOrigin(url).href;
|
||||
|
||||
/*let image = this.hipsCache.get(url);
|
||||
if (!image) {
|
||||
options = { successCallback, errorCallback, ...options };
|
||||
image = new Image(url, options);
|
||||
this.hipsCache.append(url, image);
|
||||
}*/
|
||||
let image = new Image(url, {...options, successCallback, errorCallback});
|
||||
|
||||
return image;
|
||||
@@ -1682,37 +1652,45 @@ export let Aladin = (function () {
|
||||
/**
|
||||
* @deprecated
|
||||
* Create a new layer from an url or CDS ID.
|
||||
* Please use {@link A.imageHiPS} instead for creating a new survey image
|
||||
* Please use {@link A.hiPS} instead for creating a new survey image
|
||||
*
|
||||
* @memberof Aladin
|
||||
* @static
|
||||
* @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 {ImageHiPSOptions} [options] - Options for rendering the image
|
||||
* @param {HiPSOptions} [options] - Options for rendering the image
|
||||
* @param {function} [success] - A success callback
|
||||
* @param {function} [error] - A success callback
|
||||
* @returns {ImageHiPS} A FITS image object.
|
||||
* @returns {HiPS} A FITS image object.
|
||||
*/
|
||||
Aladin.prototype.newImageSurvey = function (id, options) {
|
||||
return A.imageHiPS(id, options);
|
||||
// a wrapper on createImageSurvey that aggregates all params in an options object
|
||||
return this.createImageSurvey(
|
||||
id,
|
||||
options && options.name,
|
||||
id,
|
||||
options && options.cooFrame,
|
||||
options && options.maxOrder,
|
||||
options
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Add a new HiPS layer to the view on top of the others
|
||||
*
|
||||
* @memberof Aladin
|
||||
* @param {string|ImageHiPS|Image} [survey="CDS/P/DSS2/color"] - Can be:
|
||||
* @param {string|HiPS|Image} [survey="P/DSS2/color"] - Can be:
|
||||
* <ul>
|
||||
* <li>1. An url that refers to a HiPS.</li>
|
||||
* <li>2. 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>3. It can also be an {@link A.ImageHiPS} HiPS object created from {@link A.imageHiPS}</li>
|
||||
* <li>2. 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>3. It can also be an {@link A.HiPS} HiPS object created from {@link A.HiPS}</li>
|
||||
* </ul>
|
||||
* By default, the {@link https://alasky.cds.unistra.fr/DSS/DSSColor/|Digital Sky Survey 2} survey will be displayed
|
||||
*/
|
||||
Aladin.prototype.addNewImageLayer = function (survey = "CDS/P/DSS2/color") {
|
||||
Aladin.prototype.addNewImageLayer = function (survey = "P/DSS2/color") {
|
||||
let layerName = Utils.uuidv4();
|
||||
return this.setOverlayImageLayer(survey, layerName);
|
||||
};
|
||||
@@ -1720,14 +1698,14 @@ export let Aladin = (function () {
|
||||
/**
|
||||
* Change the base layer of the view
|
||||
*
|
||||
* It internally calls {@link Aladin#setBaseImageLayer|Aladin.setBaseImageLayer} with the url/{@link ImageHiPS}/{@link Image} given
|
||||
* It internally calls {@link Aladin#setBaseImageLayer|Aladin.setBaseImageLayer} with the url/{@link HiPS}/{@link Image} given
|
||||
*
|
||||
* @memberof Aladin
|
||||
* @param {string|ImageHiPS|Image} urlOrHiPSOrFITS - Can be:
|
||||
* @param {string|HiPS|Image} urlOrHiPSOrFITS - Can be:
|
||||
* <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 ImageHiPS} HiPS object created from {@link A.imageHiPS}</li>
|
||||
* <li>3. A {@link HiPS} HiPS object created from {@link A.HiPS}</li>
|
||||
* <li>4. A {@link Image} FITS image object</li>
|
||||
* </ul>
|
||||
*/
|
||||
@@ -1738,14 +1716,14 @@ export let Aladin = (function () {
|
||||
/**
|
||||
* Change the base layer of the view
|
||||
*
|
||||
* It internally calls {@link Aladin#setBaseImageLayer|Aladin.setBaseImageLayer} with the url/{@link ImageHiPS}/{@link Image} given
|
||||
* It internally calls {@link Aladin#setBaseImageLayer|Aladin.setBaseImageLayer} with the url/{@link HiPS}/{@link Image} given
|
||||
*
|
||||
* @memberof Aladin
|
||||
* @param {string|ImageHiPS|Image} urlOrHiPSOrFITS - Can be:
|
||||
* @param {string|HiPS|Image} urlOrHiPSOrFITS - Can be:
|
||||
* <ul>
|
||||
* <li>1. An url that refers to a HiPS.</li>
|
||||
* <li>2. 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>3. A {@link ImageHiPS} HiPS object created from {@link A.imageHiPS}</li>
|
||||
* <li>3. A {@link HiPS} HiPS object created from {@link A.HiPS}</li>
|
||||
* <li>4. A {@link Image} FITS image object</li>
|
||||
* </ul>
|
||||
*/
|
||||
@@ -1772,11 +1750,29 @@ export let Aladin = (function () {
|
||||
return this.backgroundColor;
|
||||
};
|
||||
|
||||
/**
|
||||
* Remove an image layer/overlay from the instance
|
||||
*
|
||||
* @memberof Aladin
|
||||
* @param {string|Overlay} item - the overlay object or image layer name to remove
|
||||
*/
|
||||
Aladin.prototype.remove = function (item) {
|
||||
const layers = this.getStackLayers()
|
||||
let idxToDelete = layers.findIndex(l => l === item);
|
||||
if (idxToDelete >= 0) {
|
||||
this.view.removeImageLayer(item);
|
||||
return;
|
||||
}
|
||||
|
||||
// must be an overlay
|
||||
this.view.removeOverlay(item)
|
||||
};
|
||||
|
||||
/**
|
||||
* Remove a specific layer
|
||||
*
|
||||
* @memberof Aladin
|
||||
* @param {string} layer - The name of the layer to remove
|
||||
* @param {string} layer - The name of the layer to remove or the HiPS/Image object
|
||||
*/
|
||||
Aladin.prototype.removeImageLayer = function (layer) {
|
||||
this.view.removeImageLayer(layer);
|
||||
@@ -1786,11 +1782,11 @@ export let Aladin = (function () {
|
||||
* Change the base layer of the view
|
||||
*
|
||||
* @memberof Aladin
|
||||
* @param {string|ImageHiPS|Image} urlOrHiPSOrFITS - Can be:
|
||||
* @param {string|HiPS|Image} urlOrHiPSOrFITS - Can be:
|
||||
* <ul>
|
||||
* <li>1. An url that refers to a HiPS.</li>
|
||||
* <li>2. 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>3. A {@link ImageHiPS} HiPS object created from {@link A.imageHiPS}</li>
|
||||
* <li>3. A {@link HiPS} HiPS object created from {@link A.HiPS}</li>
|
||||
* <li>4. A {@link Image} FITS image object</li>
|
||||
* </ul>
|
||||
*/
|
||||
@@ -1802,7 +1798,7 @@ export let Aladin = (function () {
|
||||
* Get the base image layer object
|
||||
*
|
||||
* @memberof Aladin
|
||||
* @returns {ImageHiPS|Image} - Returns the image layer corresponding to the base layer
|
||||
* @returns {HiPS|Image} - Returns the image layer corresponding to the base layer
|
||||
*/
|
||||
Aladin.prototype.getBaseImageLayer = function () {
|
||||
return this.view.getImageLayer("base");
|
||||
@@ -1812,12 +1808,12 @@ export let Aladin = (function () {
|
||||
* Add a new HiPS/FITS image layer in the view
|
||||
*
|
||||
* @memberof Aladin
|
||||
* @param {string|ImageHiPS|Image} urlOrHiPSOrFITS - Can be:
|
||||
* @param {string|HiPS|Image} urlOrHiPSOrFITS - Can be:
|
||||
* <ul>
|
||||
* <li>1. An url that refers to a HiPS.</li>
|
||||
* <li>2. 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>3. A {@link ImageHiPS} HiPS object created from {@link A.imageHiPS}</li>
|
||||
* <li>4. A {@link Image} FITS/jped/png image</li>
|
||||
* <li>3. A {@link HiPS} HiPS object created from {@link A.HiPS}</li>
|
||||
* <li>4. A {@link Image} FITS/jpeg/png image</li>
|
||||
* </ul>
|
||||
* @param {string} [layer="overlay"] - A layer name. By default 'overlay' is chosen and it is destined to be plot
|
||||
* on top the 'base' layer. If the layer is already present in the view, it will be replaced by the new HiPS/FITS image given here.
|
||||
@@ -1827,24 +1823,35 @@ export let Aladin = (function () {
|
||||
layer = "overlay"
|
||||
) {
|
||||
let imageLayer;
|
||||
|
||||
let hipsCache = this.hipsCache;
|
||||
// 1. User gives an ID
|
||||
if (typeof urlOrHiPSOrFITS === "string") {
|
||||
const idOrUrl = urlOrHiPSOrFITS;
|
||||
|
||||
imageLayer = A.imageHiPS(idOrUrl);
|
||||
|
||||
// 2. User gives a non resolved promise
|
||||
// many cases here
|
||||
// 1/ It has been already added to the cache
|
||||
let cachedLayer = hipsCache.get(idOrUrl)
|
||||
if (cachedLayer) {
|
||||
imageLayer = cachedLayer
|
||||
} else {
|
||||
// 2/ Not in the cache, then we create the hips from this url/id and
|
||||
// go to the case 3
|
||||
imageLayer = A.HiPS(idOrUrl);
|
||||
return this.setOverlayImageLayer(imageLayer, layer);
|
||||
}
|
||||
} else {
|
||||
// 3/ It is an image survey.
|
||||
imageLayer = urlOrHiPSOrFITS;
|
||||
}
|
||||
|
||||
|
||||
let hipsCache = this.hipsCache;
|
||||
let cachedLayer = hipsCache.get(imageLayer.id)
|
||||
if (cachedLayer && !cachedLayer.added) {
|
||||
imageLayer = cachedLayer
|
||||
} else {
|
||||
this.hipsCache.append(imageLayer.id, imageLayer);
|
||||
let cachedLayer = hipsCache.get(imageLayer.id)
|
||||
if (!cachedLayer) {
|
||||
hipsCache.append(imageLayer.id, imageLayer)
|
||||
} else {
|
||||
// first set the options of the cached layer to the one of the user
|
||||
cachedLayer.setOptions(imageLayer.options)
|
||||
// if it is in the cache we get it from the cache
|
||||
imageLayer = cachedLayer
|
||||
}
|
||||
}
|
||||
|
||||
return this.view.setOverlayImageLayer(imageLayer, layer);
|
||||
@@ -1856,7 +1863,7 @@ export let Aladin = (function () {
|
||||
* @memberof Aladin
|
||||
* @param {string} [layer="overlay"] - The name of the layer
|
||||
|
||||
* @returns {ImageHiPS|Image} - The requested image layer.
|
||||
* @returns {HiPS|Image} - The requested image layer.
|
||||
*/
|
||||
Aladin.prototype.getOverlayImageLayer = function (layer = "overlay") {
|
||||
const survey = this.view.getImageLayer(layer);
|
||||
@@ -1909,7 +1916,7 @@ export let Aladin = (function () {
|
||||
* Get list of overlays layers
|
||||
*
|
||||
* @memberof Aladin
|
||||
* @returns {MOC[]|Catalog[]|ProgressiveCat[]|GraphicOverlay[]} - Returns the ordered list of image layers. Items can be {@link ImageHiPS} or {@link Image} objects.
|
||||
* @returns {MOC[]|Catalog[]|ProgressiveCat[]|GraphicOverlay[]} - Returns the ordered list of image layers. Items can be {@link HiPS} or {@link Image} objects.
|
||||
*/
|
||||
Aladin.prototype.getOverlays = function () {
|
||||
return this.view.allOverlayLayers;
|
||||
@@ -1919,7 +1926,7 @@ export let Aladin = (function () {
|
||||
* Get list of layers
|
||||
*
|
||||
* @memberof Aladin
|
||||
* @returns {ImageHiPS[]|Image[]} - Returns the ordered list of image layers. Items can be {@link ImageHiPS} or {@link Image} objects.
|
||||
* @returns {HiPS[]|Image[]} - Returns the ordered list of image layers. Items can be {@link HiPS} or {@link Image} objects.
|
||||
*/
|
||||
Aladin.prototype.getStackLayers = function () {
|
||||
return this.view.overlayLayers;
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
*
|
||||
*****************************************************************************/
|
||||
import { Aladin } from "./Aladin";
|
||||
import { Sesame } from "./Sesame";
|
||||
|
||||
/**
|
||||
* @namespace AladinUtils
|
||||
@@ -145,6 +146,28 @@ export let AladinUtils = {
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* @namespace Sesame
|
||||
* @memberof AladinUtils
|
||||
* @description Namespace for Sesame related service.
|
||||
*/
|
||||
Sesame: {
|
||||
/**
|
||||
* 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
|
||||
* @memberof AladinUtils.Sesame
|
||||
* @name resolveAstronomicalName
|
||||
*
|
||||
* @param {string} target - object name or position
|
||||
* @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
|
||||
},
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
*
|
||||
|
||||
@@ -47,7 +47,7 @@ import { Footprint } from "./Footprint.js";
|
||||
* @property {string} [name="catalog"] - The name of the catalog.
|
||||
* @property {string} [color] - The color associated with the catalog.
|
||||
* @property {number} [sourceSize=8] - The size of the sources in the catalog.
|
||||
* @property {string|function|Image|HTMLCanvasElement} [shape="square"] - The shape of the sources (can be, "square", "circle", "plus", "cross", "rhomb", "triangle").
|
||||
* @property {string|Function|Image|HTMLCanvasElement|HTMLImageElement} [shape="square"] - The shape of the sources (can be, "square", "circle", "plus", "cross", "rhomb", "triangle").
|
||||
* @property {number} [limit] - The maximum number of sources to display.
|
||||
* @property {string|Function} [onClick] - Whether the source data appears as a table row or a in popup. Can be 'showTable' string, 'showPopup' string or a custom user defined function that handles the click.
|
||||
* @property {boolean} [readOnly=false] - Whether the catalog is read-only.
|
||||
@@ -79,7 +79,9 @@ export let Catalog = (function () {
|
||||
* markerSize: 15,
|
||||
* shape: "circle",
|
||||
* limit: 1000,
|
||||
* onClick: (source) => { /* handle source click * },
|
||||
* onClick: (source) => {
|
||||
* // handle sources
|
||||
* },
|
||||
* readOnly: true,
|
||||
* raField: "ra",
|
||||
* decField: "dec",
|
||||
@@ -479,7 +481,17 @@ export let Catalog = (function () {
|
||||
);
|
||||
};
|
||||
|
||||
// API
|
||||
/**
|
||||
* Set the shape of the sources
|
||||
*
|
||||
* @memberof Catalog
|
||||
*
|
||||
* @param {Object} [options] - shape options
|
||||
* @param {string} [options.color] - the color of the shape
|
||||
* @param {number} [options.sourceSize] - size of the shape
|
||||
* @param {string|Function|HTMLImageCanvas|HTMLImageElement} [options.shape="square"] - the type of the shape. Can be square, rhomb, plus, cross, triangle, circle.
|
||||
* A callback function can also be called that return an HTMLImageElement in function of the source object. A canvas or an image can also be given.
|
||||
*/
|
||||
Catalog.prototype.updateShape = function (options) {
|
||||
options = options || {};
|
||||
this.color = options.color || this.color || Color.getNextColor();
|
||||
@@ -524,7 +536,13 @@ export let Catalog = (function () {
|
||||
this.reportChange();
|
||||
};
|
||||
|
||||
// API
|
||||
/**
|
||||
* Add sources to the catalog
|
||||
*
|
||||
* @memberof Catalog
|
||||
*
|
||||
* @param {Source[]} sources - An array of sources or only one source to add
|
||||
*/
|
||||
Catalog.prototype.addSources = function (sources) {
|
||||
// make sure we have an array and not an individual source
|
||||
sources = [].concat(sources);
|
||||
@@ -559,45 +577,38 @@ export let Catalog = (function () {
|
||||
this.reportChange();
|
||||
};
|
||||
|
||||
/*Catalog.prototype.addFootprints = function(footprintsToAdd) {
|
||||
footprintsToAdd = [].concat(footprintsToAdd); // make sure we have an array and not an individual footprints
|
||||
this.footprints = this.footprints.concat(footprintsToAdd);
|
||||
|
||||
footprintsToAdd.forEach(f => {
|
||||
f.setCatalog(this);
|
||||
})
|
||||
|
||||
this.reportChange();
|
||||
};*/
|
||||
|
||||
Catalog.prototype.computeFootprints = function (sources) {
|
||||
let footprints = [];
|
||||
|
||||
if (this._shapeIsFunction) {
|
||||
for (const source of sources) {
|
||||
try {
|
||||
let shape = this.shape(source);
|
||||
let shapes = this.shape(source);
|
||||
if (shapes) {
|
||||
shapes = [].concat(shapes);
|
||||
|
||||
// convert simple shapes to footprints
|
||||
if (
|
||||
shape instanceof Circle ||
|
||||
shape instanceof Polyline ||
|
||||
shape instanceof Ellipse ||
|
||||
shape instanceof Vector
|
||||
) {
|
||||
shape = new Footprint(shape, source);
|
||||
}
|
||||
// 1. return of the shape func is an image
|
||||
if (shapes.length == 1 && (shapes[0] instanceof Image || shapes[0] instanceof HTMLCanvasElement)) {
|
||||
source.setImage(shapes[0]);
|
||||
// 2. return of the shape is a set of shapes or a footprint
|
||||
} else {
|
||||
let footprint;
|
||||
if (shapes.length == 1 && shapes[0] instanceof Footprint) {
|
||||
footprint = shapes[0];
|
||||
} else {
|
||||
footprint = new Footprint(shapes, source);
|
||||
}
|
||||
|
||||
if (shape instanceof Footprint) {
|
||||
let footprint = shape;
|
||||
this._shapeIsFootprintFunction = true;
|
||||
footprint.setCatalog(this);
|
||||
this._shapeIsFootprintFunction = true;
|
||||
footprint.setCatalog(this);
|
||||
|
||||
// store the footprints
|
||||
footprints.push(footprint);
|
||||
// store the footprints
|
||||
footprints.push(footprint);
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
// do not create the footprint
|
||||
console.warn("Return of shape function could not be interpreted as a footprint");
|
||||
continue;
|
||||
}
|
||||
}
|
||||
@@ -615,12 +626,14 @@ export let Catalog = (function () {
|
||||
this.showFieldCallback[field] = callback;
|
||||
};
|
||||
|
||||
// API
|
||||
//
|
||||
// create sources from a 2d array and add them to the catalog
|
||||
//
|
||||
// @param columnNames: array with names of the columns
|
||||
// @array: 2D-array, each item being a 1d-array with the same number of items as columnNames
|
||||
/**
|
||||
* Create sources from a 2d array and add them to the catalog
|
||||
*
|
||||
* @memberof Catalog
|
||||
*
|
||||
* @param {String[]} columnNames - array with names of the columns
|
||||
* @param {String[][]|number[][]} array - 2D-array, each item being a 1d-array with the same number of items as columnNames
|
||||
*/
|
||||
Catalog.prototype.addSourcesAsArray = function (columnNames, array) {
|
||||
var fields = [];
|
||||
for (var colIdx = 0; colIdx < columnNames.length; colIdx++) {
|
||||
@@ -662,7 +675,13 @@ export let Catalog = (function () {
|
||||
this.addSources(newSources);
|
||||
};
|
||||
|
||||
// return the current list of Source objects
|
||||
/**
|
||||
* Get all the sources
|
||||
*
|
||||
* @memberof Catalog
|
||||
*
|
||||
* @returns {Source[]} - an array of all the sources in the catalog object
|
||||
*/
|
||||
Catalog.prototype.getSources = function () {
|
||||
return this.sources;
|
||||
};
|
||||
@@ -671,7 +690,11 @@ export let Catalog = (function () {
|
||||
return this.footprints;
|
||||
};
|
||||
|
||||
// TODO : fonction générique traversant la liste des sources
|
||||
/**
|
||||
* Select all the source catalog
|
||||
*
|
||||
* @memberof Catalog
|
||||
*/
|
||||
Catalog.prototype.selectAll = function () {
|
||||
if (!this.sources) {
|
||||
return;
|
||||
@@ -682,6 +705,11 @@ export let Catalog = (function () {
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Unselect all the source of the catalog
|
||||
*
|
||||
* @memberof Catalog
|
||||
*/
|
||||
Catalog.prototype.deselectAll = function () {
|
||||
if (!this.sources) {
|
||||
return;
|
||||
@@ -692,7 +720,15 @@ export let Catalog = (function () {
|
||||
}
|
||||
};
|
||||
|
||||
// return a source by index
|
||||
/**
|
||||
* Get one source by its index in the catalog
|
||||
*
|
||||
* @memberof Catalog
|
||||
*
|
||||
* @param {number} idx - the index of the source in the catalog sources
|
||||
*
|
||||
* @returns {Source} - the source at the index
|
||||
*/
|
||||
Catalog.prototype.getSource = function (idx) {
|
||||
if (idx < this.sources.length) {
|
||||
return this.sources[idx];
|
||||
@@ -701,42 +737,95 @@ export let Catalog = (function () {
|
||||
}
|
||||
};
|
||||
|
||||
Catalog.prototype.setView = function (view) {
|
||||
Catalog.prototype.setView = function (view, idx) {
|
||||
this.view = view;
|
||||
|
||||
this.view.catalogs.push(this);
|
||||
this.view.insertOverlay(this, idx);
|
||||
|
||||
this.reportChange();
|
||||
};
|
||||
|
||||
/**
|
||||
* Set the color of the catalog
|
||||
*
|
||||
* @memberof Catalog
|
||||
*
|
||||
* @param {String} - the new color
|
||||
*/
|
||||
Catalog.prototype.setColor = function (color) {
|
||||
this.color = color;
|
||||
this.updateShape();
|
||||
};
|
||||
|
||||
/**
|
||||
* Set the color of selected sources
|
||||
*
|
||||
* @memberof Catalog
|
||||
*
|
||||
* @param {String} - the new color
|
||||
*/
|
||||
Catalog.prototype.setSelectionColor = function (color) {
|
||||
this.selectionColor = color;
|
||||
this.updateShape();
|
||||
};
|
||||
|
||||
/**
|
||||
* Set the color of hovered sources
|
||||
*
|
||||
* @memberof Catalog
|
||||
*
|
||||
* @param {String} - the new color
|
||||
*/
|
||||
Catalog.prototype.setHoverColor = function (color) {
|
||||
this.hoverColor = color;
|
||||
this.updateShape();
|
||||
};
|
||||
|
||||
/**
|
||||
* Set the size of the catalog sources
|
||||
*
|
||||
* @memberof Catalog
|
||||
*
|
||||
* @param {number} - the new size
|
||||
*/
|
||||
Catalog.prototype.setSourceSize = function (sourceSize) {
|
||||
// will be discarded in updateShape if the shape is an Image
|
||||
this.sourceSize = sourceSize;
|
||||
this.updateShape();
|
||||
};
|
||||
|
||||
/**
|
||||
* Set the color of the catalog
|
||||
*
|
||||
* @memberof Catalog
|
||||
*
|
||||
* @param {string|Function|HTMLImageCanvas|HTMLImageElement} [shape="square"] - the type of the shape. Can be square, rhomb, plus, cross, triangle, circle.
|
||||
* A callback function can also be called that return an HTMLImageElement in function of the source object. A canvas or an image can also be given.
|
||||
*/
|
||||
Catalog.prototype.setShape = function (shape) {
|
||||
this.shape = shape;
|
||||
this.updateShape();
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the size of the catalog sources
|
||||
*
|
||||
* @memberof Catalog
|
||||
*
|
||||
* @returns {number} - the size of the sources
|
||||
*/
|
||||
Catalog.prototype.getSourceSize = function () {
|
||||
return this.sourceSize;
|
||||
};
|
||||
|
||||
// remove a source
|
||||
/**
|
||||
* Remove a specific source from the catalog
|
||||
*
|
||||
* @memberof Catalog
|
||||
*
|
||||
* @param {Source} - the source to remove
|
||||
*/
|
||||
Catalog.prototype.remove = function (source) {
|
||||
var idx = this.sources.indexOf(source);
|
||||
if (idx < 0) {
|
||||
@@ -754,12 +843,19 @@ export let Catalog = (function () {
|
||||
this.reportChange();
|
||||
};
|
||||
|
||||
/**
|
||||
* Clear all the sources from the catalog
|
||||
*
|
||||
* @memberof Catalog
|
||||
*/
|
||||
Catalog.prototype.removeAll = Catalog.prototype.clear = function () {
|
||||
// TODO : RAZ de l'index
|
||||
this.sources = [];
|
||||
this.ra = [];
|
||||
this.dec = [];
|
||||
this.footprints = [];
|
||||
|
||||
this.reportChange();
|
||||
};
|
||||
|
||||
Catalog.prototype.draw = function (ctx, width, height) {
|
||||
@@ -829,6 +925,12 @@ export let Catalog = (function () {
|
||||
if (s.x <= width && s.x >= 0 && s.y <= height && s.y >= 0) {
|
||||
if (this._shapeIsFunction && !this._shapeIsFootprintFunction) {
|
||||
this.shape(s, ctx, this.view.getViewParams());
|
||||
} else if (s.image) {
|
||||
ctx.drawImage(
|
||||
s.image,
|
||||
s.x - s.image.width / 2,
|
||||
s.y - s.image.height / 2
|
||||
);
|
||||
} else if (s.marker && s.useMarkerDefaultIcon) {
|
||||
ctx.drawImage(
|
||||
this.cacheMarkerCanvas,
|
||||
@@ -891,8 +993,6 @@ export let Catalog = (function () {
|
||||
|
||||
f.draw(ctx, this.view);
|
||||
f.source.tooSmallFootprint = f.isTooSmall();
|
||||
// propagate the info that the footprint is too small
|
||||
//f.source.tooSmallFootprint = f.isTooSmall
|
||||
}
|
||||
};
|
||||
|
||||
@@ -901,6 +1001,11 @@ export let Catalog = (function () {
|
||||
this.view && this.view.requestRedraw();
|
||||
};
|
||||
|
||||
/**
|
||||
* Show the catalog
|
||||
*
|
||||
* @memberof Catalog
|
||||
*/
|
||||
Catalog.prototype.show = function () {
|
||||
if (this.isShowing) {
|
||||
return;
|
||||
@@ -914,6 +1019,11 @@ export let Catalog = (function () {
|
||||
this.reportChange();
|
||||
};
|
||||
|
||||
/**
|
||||
* Hide the catalog
|
||||
*
|
||||
* @memberof Catalog
|
||||
*/
|
||||
Catalog.prototype.hide = function () {
|
||||
if (!this.isShowing) {
|
||||
return;
|
||||
|
||||
@@ -100,9 +100,29 @@
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
ColorCfg.prototype.setOptions = function(options) {
|
||||
if (options.colormap)
|
||||
this.setColormap(options.colormap, options)
|
||||
|
||||
this.setCuts(options.minCut, options.maxCut)
|
||||
|
||||
this.setBrightness(options.brightness)
|
||||
this.setSaturation(options.saturation)
|
||||
this.setContrast(options.contrast)
|
||||
|
||||
this.setGamma(options.gamma)
|
||||
|
||||
this.setOpacity(options.opacity)
|
||||
|
||||
this.setBlendingConfig(options.additive)
|
||||
}
|
||||
|
||||
// @api
|
||||
ColorCfg.prototype.setBrightness = function(kBrightness) {
|
||||
if (kBrightness == null || kBrightness == undefined)
|
||||
return;
|
||||
|
||||
kBrightness = +kBrightness || 0.0; // coerce to number
|
||||
this.kBrightness = Math.max(-1, Math.min(kBrightness, 1));
|
||||
};
|
||||
@@ -114,6 +134,9 @@
|
||||
|
||||
// @api
|
||||
ColorCfg.prototype.setContrast = function(kContrast) {
|
||||
if (kContrast == null || kContrast == undefined)
|
||||
return;
|
||||
|
||||
kContrast = +kContrast || 0.0; // coerce to number
|
||||
this.kContrast = Math.max(-1, Math.min(kContrast, 1));
|
||||
};
|
||||
@@ -125,6 +148,9 @@
|
||||
|
||||
// @api
|
||||
ColorCfg.prototype.setSaturation = function(kSaturation) {
|
||||
if (kSaturation == null || kSaturation == undefined)
|
||||
return;
|
||||
|
||||
kSaturation = +kSaturation || 0.0; // coerce to number
|
||||
|
||||
this.kSaturation = Math.max(-1, Math.min(kSaturation, 1));
|
||||
@@ -137,6 +163,9 @@
|
||||
|
||||
// @api
|
||||
ColorCfg.prototype.setGamma = function(gamma) {
|
||||
if (gamma == null || gamma == undefined)
|
||||
return;
|
||||
|
||||
gamma = +gamma; // coerce to number
|
||||
this.kGamma = Math.max(0.1, Math.min(gamma, 10));
|
||||
};
|
||||
@@ -148,6 +177,9 @@
|
||||
|
||||
// @api
|
||||
ColorCfg.prototype.setOpacity = function(opacity) {
|
||||
if (opacity == null || opacity == undefined)
|
||||
return;
|
||||
|
||||
opacity = +opacity; // coerce to number
|
||||
this.opacity = Math.max(0, Math.min(opacity, 1));
|
||||
};
|
||||
@@ -164,7 +196,10 @@
|
||||
ColorCfg.prototype.getAlpha = ColorCfg.prototype.getOpacity;
|
||||
|
||||
// @api
|
||||
ColorCfg.prototype.setBlendingConfig = function(additive = false) {
|
||||
ColorCfg.prototype.setBlendingConfig = function(additive) {
|
||||
if (additive === null || additive === undefined)
|
||||
return;
|
||||
|
||||
this.additiveBlending = additive;
|
||||
};
|
||||
|
||||
@@ -189,6 +224,9 @@
|
||||
// @api
|
||||
// Optional arguments,
|
||||
ColorCfg.prototype.setColormap = function(colormap = "native", options) {
|
||||
if (colormap == null || colormap == undefined)
|
||||
return;
|
||||
|
||||
/// colormap
|
||||
// Make it case insensitive
|
||||
let cmap = formatColormap(colormap);
|
||||
@@ -218,40 +256,18 @@
|
||||
};
|
||||
|
||||
// @api
|
||||
ColorCfg.prototype.setCuts = function(lowCut, highCut) {
|
||||
this.minCut = lowCut;
|
||||
this.maxCut = highCut;
|
||||
ColorCfg.prototype.setCuts = function(minCut, maxCut) {
|
||||
if (minCut === null || minCut === undefined || maxCut === null || maxCut === undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.minCut = minCut;
|
||||
this.maxCut = maxCut;
|
||||
};
|
||||
|
||||
ColorCfg.prototype.getCuts = function() {
|
||||
return [this.minCut, this.maxCut];
|
||||
};
|
||||
|
||||
/*ColorCfg.COLORMAPS = [
|
||||
"blues",
|
||||
"cividis",
|
||||
"cubehelix",
|
||||
"eosb",
|
||||
"grayscale",
|
||||
"inferno",
|
||||
"magma",
|
||||
"native",
|
||||
"parula",
|
||||
"plasma",
|
||||
"rainbow",
|
||||
"rdbu",
|
||||
"rdylbu",
|
||||
"redtemperature",
|
||||
"sinebow",
|
||||
"spectral",
|
||||
"summer",
|
||||
"viridis",
|
||||
"ylgnbu",
|
||||
"ylorbr",
|
||||
"red",
|
||||
"green",
|
||||
"blue"
|
||||
];*/
|
||||
|
||||
return ColorCfg;
|
||||
})();
|
||||
|
||||
@@ -206,6 +206,12 @@ export let DefaultActionsForContextMenu = (function () {
|
||||
action(o) {
|
||||
a.select('rect', selectObjects)
|
||||
}
|
||||
},
|
||||
{
|
||||
label: 'Polygon',
|
||||
action(o) {
|
||||
a.select('poly', selectObjects)
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
@@ -5,25 +5,27 @@ export let HiPSList = (function () {
|
||||
{
|
||||
creatorDid: "ivo://CDS/P/DSS2/color",
|
||||
name: "DSS colored",
|
||||
id: "CDS/P/DSS2/color",
|
||||
id: "P/DSS2/color",
|
||||
maxOrder: 9,
|
||||
tileSize: 512,
|
||||
imgFormat: "jpeg",
|
||||
cooFrame: "ICRS",
|
||||
startUrl: "https://alasky.cds.unistra.fr/DSS/DSSColor",
|
||||
},
|
||||
{
|
||||
creatorDid: "ivo://CDS/P/2MASS/color",
|
||||
name: "2MASS colored",
|
||||
id: "CDS/P/2MASS/color",
|
||||
id: "P/2MASS/color",
|
||||
maxOrder: 9,
|
||||
tileSize: 512,
|
||||
imgFormat: "jpeg",
|
||||
cooFrame: "ICRS",
|
||||
startUrl: "https://alaskybis.cds.unistra.fr/2MASS/Color",
|
||||
},
|
||||
{
|
||||
creatorDid: "ivo://CDS/P/DSS2/red",
|
||||
name: "DSS2 Red (F+R)",
|
||||
id: "CDS/P/DSS2/red",
|
||||
id: "P/DSS2/red",
|
||||
maxOrder: 9,
|
||||
tileSize: 512,
|
||||
imgFormat: "fits",
|
||||
@@ -35,11 +37,12 @@ export let HiPSList = (function () {
|
||||
colormap: "magma",
|
||||
stretch: "Linear",
|
||||
imgFormat: "fits",
|
||||
startUrl: "https://alaskybis.cds.unistra.fr/DSS/DSS2Merged",
|
||||
},
|
||||
{
|
||||
creatorDid: "ivo://CDS/P/DM/I/350/gaiaedr3",
|
||||
name: "Density map for Gaia EDR3 (I/350/gaiaedr3)",
|
||||
id: "CDS/P/DM/I/350/gaiaedr3",
|
||||
id: "P/DM/I/350/gaiaedr3",
|
||||
maxOrder: 7,
|
||||
tileSize: 512,
|
||||
numBitsPerPixel: -32,
|
||||
@@ -49,11 +52,12 @@ export let HiPSList = (function () {
|
||||
stretch: "asinh",
|
||||
colormap: "rdylbu",
|
||||
imgFormat: "fits",
|
||||
startUrl: "https://alaskybis.cds.unistra.fr/ancillary/GaiaEDR3/density-map",
|
||||
},
|
||||
{
|
||||
creatorDid: "ivo://CDS/P/PanSTARRS/DR1/g",
|
||||
name: "PanSTARRS DR1 g",
|
||||
id: "CDS/P/PanSTARRS/DR1/g",
|
||||
id: "P/PanSTARRS/DR1/g",
|
||||
maxOrder: 11,
|
||||
tileSize: 512,
|
||||
imgFormat: "fits",
|
||||
@@ -64,33 +68,37 @@ export let HiPSList = (function () {
|
||||
maxCut: 7000,
|
||||
stretch: "asinh",
|
||||
colormap: "redtemperature",
|
||||
startUrl: "https://alasky.cds.unistra.fr/Pan-STARRS/DR1/g"
|
||||
},
|
||||
{
|
||||
creatorDid: "ivo://CDS/P/PanSTARRS/DR1/color-z-zg-g",
|
||||
name: "PanSTARRS DR1 color",
|
||||
id: "CDS/P/PanSTARRS/DR1/color-z-zg-g",
|
||||
id: "P/PanSTARRS/DR1/color-z-zg-g",
|
||||
maxOrder: 11,
|
||||
tileSize: 512,
|
||||
imgFormat: "jpeg",
|
||||
cooFrame: "ICRS",
|
||||
startUrl: "https://alasky.cds.unistra.fr/Pan-STARRS/DR1/color-z-zg-g",
|
||||
},
|
||||
{
|
||||
creatorDid: "ivo://CDS/P/DECaPS/DR2/color",
|
||||
name: "DECaPS DR2 color",
|
||||
id: "CDS/P/DECaPS/DR2/color",
|
||||
id: "P/DECaPS/DR2/color",
|
||||
maxOrder: 11,
|
||||
cooFrame: "equatorial",
|
||||
tileSize: 512,
|
||||
imgFormat: "png",
|
||||
startUrl: "https://alasky.cds.unistra.fr/DECaPS/DR2/CDS_P_DECaPS_DR2_color"
|
||||
},
|
||||
{
|
||||
creatorDid: "ivo://CDS/P/Fermi/color",
|
||||
name: "Fermi color",
|
||||
id: "CDS/P/Fermi/color",
|
||||
id: "P/Fermi/color",
|
||||
maxOrder: 3,
|
||||
imgFormat: "jpeg",
|
||||
tileSize: 512,
|
||||
cooFrame: "equatorial",
|
||||
startUrl: "https://alasky.cds.unistra.fr/Fermi/Color",
|
||||
},
|
||||
{
|
||||
creatorDid: "ivo://CDS/P/GALEXGR6_7/NUV",
|
||||
@@ -100,55 +108,61 @@ export let HiPSList = (function () {
|
||||
imgFormat: "png",
|
||||
tileSize: 512,
|
||||
cooFrame: "equatorial",
|
||||
startUrl: "https://alasky.cds.unistra.fr/GALEX/GALEXGR6_7_NUV"
|
||||
},
|
||||
{
|
||||
creatorDid: "ivo://CDS/P/IRIS/color",
|
||||
id: "CDS/P/IRIS/color",
|
||||
id: "P/IRIS/color",
|
||||
name: "IRIS colored",
|
||||
maxOrder: 3,
|
||||
tileSize: 256,
|
||||
imgFormat: "jpeg",
|
||||
cooFrame: "galactic",
|
||||
startUrl: "https://alasky.cds.unistra.fr/IRISColor",
|
||||
},
|
||||
{
|
||||
creatorDid: "ivo://CDS/P/Mellinger/color",
|
||||
id: "CDS/P/Mellinger/color",
|
||||
id: "P/Mellinger/color",
|
||||
name: "Mellinger colored",
|
||||
maxOrder: 4,
|
||||
tileSize: 512,
|
||||
imgFormat: "jpeg",
|
||||
cooFrame: "galactic",
|
||||
startUrl: "https://alasky.cds.unistra.fr/MellingerRGB"
|
||||
},
|
||||
{
|
||||
creatorDid: "ivo://CDS/P/SDSS9/color",
|
||||
id: "CDS/P/SDSS9/color",
|
||||
id: "P/SDSS9/color",
|
||||
name: "SDSS9 colored",
|
||||
maxOrder: 10,
|
||||
tileSize: 512,
|
||||
imgFormat: "jpeg",
|
||||
cooFrame: "equatorial",
|
||||
startUrl: "https://alasky.cds.unistra.fr/SDSS/DR9/color"
|
||||
},
|
||||
{
|
||||
creatorDid: "ivo://CDS/P/SPITZER/color",
|
||||
id: "CDS/P/SPITZER/color",
|
||||
id: "P/SPITZER/color",
|
||||
name: "IRAC color I1,I2,I4 - (GLIMPSE, SAGE, SAGE-SMC, SINGS)",
|
||||
maxOrder: 9,
|
||||
tileSize: 512,
|
||||
imgFormat: "jpeg",
|
||||
cooFrame: "galactic",
|
||||
startUrl: "https://alasky.cds.unistra.fr/Spitzer/SpitzerI1I2I4color"
|
||||
},
|
||||
{
|
||||
creatorDid: "ivo://CDS/P/allWISE/color",
|
||||
id: "CDS/P/allWISE/color",
|
||||
id: "P/allWISE/color",
|
||||
name: "AllWISE color",
|
||||
maxOrder: 8,
|
||||
tileSize: 512,
|
||||
imgFormat: "jpeg",
|
||||
cooFrame: "equatorial",
|
||||
startUrl: "https://alaskybis.cds.unistra.fr/AllWISE/RGB-W4-W2-W1"
|
||||
},
|
||||
{
|
||||
creatorDid: "ivo://CDS/P/SDSS9/g",
|
||||
id: "CDS/P/SDSS9/g",
|
||||
id: "P/SDSS9/g",
|
||||
name: "SDSS9 band-g",
|
||||
maxOrder: 10,
|
||||
tileSize: 512,
|
||||
@@ -159,45 +173,36 @@ export let HiPSList = (function () {
|
||||
maxCut: 1.8,
|
||||
stretch: "linear",
|
||||
colormap: "redtemperature",
|
||||
startUrl: "https://alasky.cds.unistra.fr/SDSS/DR9/band-g"
|
||||
},
|
||||
{
|
||||
id: "CDS/P/Finkbeiner",
|
||||
id: "P/Finkbeiner",
|
||||
name: "Halpha",
|
||||
maxOrder: 3,
|
||||
minCut: -10,
|
||||
maxCut: 800,
|
||||
colormap: "rdbu",
|
||||
imgFormat: "fits",
|
||||
startUrl: "https://alasky.cds.unistra.fr/FinkbeinerHalpha"
|
||||
},
|
||||
{
|
||||
id: "CDS/P/VTSS/Ha",
|
||||
id: "P/VTSS/Ha",
|
||||
name: "VTSS-Ha",
|
||||
maxOrder: 3,
|
||||
minCut: -10.0,
|
||||
maxCut: 100.0,
|
||||
colormap: "grayscale",
|
||||
imgFormat: "fits",
|
||||
startUrl: "https://alasky.cds.unistra.fr/VTSS/Ha"
|
||||
},
|
||||
{
|
||||
id: "xcatdb/P/XMM/PN/color",
|
||||
name: "XMM PN colored",
|
||||
maxOrder: 7,
|
||||
},
|
||||
{
|
||||
id: "CDS/P/allWISE/color",
|
||||
name: "AllWISE color",
|
||||
maxOrder: 8,
|
||||
},
|
||||
/*{
|
||||
id: "CDS/P/GLIMPSE360",
|
||||
id: "P/GLIMPSE360",
|
||||
name: "GLIMPSE360",
|
||||
// This domain is not giving the CORS headers
|
||||
// We need to query by with a proxy equipped with CORS header.
|
||||
//url: "https://alasky.cds.unistra.fr/cgi/JSONProxy?url=https://www.spitzer.caltech.edu/glimpse360/aladin/data",
|
||||
maxOrder: 9,
|
||||
imgFormat: "jpeg",
|
||||
minOrder: 3,
|
||||
}*/
|
||||
startUrl: "https://alasky.cds.unistra.fr/IPAC/IPAC_P_GLIMPSE360",
|
||||
}
|
||||
];
|
||||
|
||||
return HiPSList;
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -54,6 +54,18 @@ export let Footprint= (function() {
|
||||
this.overlay = null;
|
||||
};
|
||||
|
||||
Footprint.prototype.setSource = function(source) {
|
||||
if (this.source) {
|
||||
this.source.hasFootprint = false;
|
||||
}
|
||||
|
||||
this.source = source;
|
||||
|
||||
if (this.source) {
|
||||
this.source.hasFootprint = true;
|
||||
}
|
||||
}
|
||||
|
||||
Footprint.prototype.setCatalog = function(catalog) {
|
||||
if (this.source) {
|
||||
this.source.setCatalog(catalog);
|
||||
@@ -170,7 +182,12 @@ export let Footprint= (function() {
|
||||
}
|
||||
|
||||
Footprint.prototype.draw = function(ctx, view, noStroke) {
|
||||
return this.shapes.some((shape) => {return shape.draw(ctx, view, noStroke)})
|
||||
let hasBeenDrawn = false;
|
||||
for (let shape of this.shapes) {
|
||||
hasBeenDrawn |= shape.draw(ctx, view, noStroke)
|
||||
}
|
||||
|
||||
return hasBeenDrawn;
|
||||
};
|
||||
|
||||
Footprint.prototype.actionClicked = function() {
|
||||
@@ -192,7 +209,7 @@ export let Footprint= (function() {
|
||||
return this.shapes.some((shape) => shape.isInStroke(ctx, view, x, y));
|
||||
};
|
||||
|
||||
Footprint.prototype.isTooSmall = function(view) {
|
||||
Footprint.prototype.isTooSmall = function() {
|
||||
return this.shapes.every((shape) => shape.isTooSmall);
|
||||
};
|
||||
|
||||
|
||||
981
src/js/HiPS.js
Normal file
@@ -0,0 +1,981 @@
|
||||
// Copyright 2013 - UDS/CNRS
|
||||
// The Aladin Lite program is distributed under the terms
|
||||
// of the GNU General Public License version 3.
|
||||
//
|
||||
// This file is part of Aladin Lite.
|
||||
//
|
||||
// Aladin Lite is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, version 3 of the License.
|
||||
//
|
||||
// Aladin Lite is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// The GNU General Public License is available in COPYING file
|
||||
// along with Aladin Lite.
|
||||
//
|
||||
|
||||
/******************************************************************************
|
||||
* Aladin Lite project
|
||||
*
|
||||
* File HiPS
|
||||
*
|
||||
* Authors: Thomas Boch & Matthieu Baumann [CDS]
|
||||
*
|
||||
*****************************************************************************/
|
||||
import { ALEvent } from "./events/ALEvent.js";
|
||||
import { ColorCfg } from "./ColorCfg.js";
|
||||
import { HiPSProperties } from "./HiPSProperties.js";
|
||||
import { Aladin } from "./Aladin.js";
|
||||
let PropertyParser = {};
|
||||
// Utilitary functions for parsing the properties and giving default values
|
||||
/// Mandatory tileSize property
|
||||
PropertyParser.tileSize = function (properties) {
|
||||
let tileSize =
|
||||
(properties &&
|
||||
properties.hips_tile_width &&
|
||||
+properties.hips_tile_width) ||
|
||||
512;
|
||||
|
||||
// Check if the tile width size is a power of 2
|
||||
if (tileSize & (tileSize - 1 !== 0)) {
|
||||
tileSize = 512;
|
||||
}
|
||||
|
||||
return tileSize;
|
||||
};
|
||||
|
||||
/// Mandatory frame property
|
||||
PropertyParser.cooFrame = function (properties) {
|
||||
let cooFrame =
|
||||
(properties && properties.hips_body && "ICRSd") ||
|
||||
(properties && properties.hips_frame) ||
|
||||
"ICRS";
|
||||
return cooFrame;
|
||||
};
|
||||
|
||||
/// Mandatory maxOrder property
|
||||
PropertyParser.maxOrder = function (properties) {
|
||||
let maxOrder =
|
||||
properties && properties.hips_order && +properties.hips_order;
|
||||
return maxOrder;
|
||||
};
|
||||
|
||||
/// Mandatory minOrder property
|
||||
PropertyParser.minOrder = function (properties) {
|
||||
const minOrder =
|
||||
(properties &&
|
||||
properties.hips_order_min &&
|
||||
+properties.hips_order_min) ||
|
||||
0;
|
||||
return minOrder;
|
||||
};
|
||||
|
||||
PropertyParser.formats = function (properties) {
|
||||
let formats = (properties && properties.hips_tile_format) || "jpeg";
|
||||
|
||||
formats = formats.split(" ").map((fmt) => fmt.toLowerCase());
|
||||
|
||||
return formats;
|
||||
};
|
||||
|
||||
PropertyParser.initialFov = function (properties) {
|
||||
let initialFov =
|
||||
properties &&
|
||||
properties.hips_initial_fov &&
|
||||
+properties.hips_initial_fov;
|
||||
|
||||
if (initialFov && initialFov < 0.00002777777) {
|
||||
initialFov = 360;
|
||||
}
|
||||
|
||||
return initialFov;
|
||||
};
|
||||
|
||||
PropertyParser.skyFraction = function (properties) {
|
||||
const skyFraction =
|
||||
(properties &&
|
||||
properties.moc_sky_fraction &&
|
||||
+properties.moc_sky_fraction) ||
|
||||
0.0;
|
||||
return skyFraction;
|
||||
};
|
||||
|
||||
PropertyParser.cutouts = function (properties) {
|
||||
let cuts =
|
||||
properties &&
|
||||
properties.hips_pixel_cut &&
|
||||
properties.hips_pixel_cut.split(" ");
|
||||
|
||||
const minCutout = cuts && parseFloat(cuts[0]);
|
||||
const maxCutout = cuts && parseFloat(cuts[1]);
|
||||
|
||||
return [minCutout, maxCutout];
|
||||
};
|
||||
|
||||
PropertyParser.bitpix = function (properties) {
|
||||
const bitpix =
|
||||
properties &&
|
||||
properties.hips_pixel_bitpix &&
|
||||
+properties.hips_pixel_bitpix;
|
||||
return bitpix;
|
||||
};
|
||||
|
||||
PropertyParser.isPlanetaryBody = function (properties) {
|
||||
return properties && properties.hips_body !== undefined;
|
||||
};
|
||||
|
||||
/**
|
||||
* @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
|
||||
* @property {string} [imgFormat] - Formats accepted 'webp', 'png', 'jpeg' or 'fits'. Will raise an error if the HiPS does not contain tiles in this format
|
||||
* @property {CooFrame} [cooFrame="J2000"] - Coordinate frame of the survey tiles
|
||||
* @property {number} [maxOrder] - The maximum HEALPix order of the HiPS, i.e the HEALPix order of the most refined tile images of the HiPS.
|
||||
* @property {number} [numBitsPerPixel] - Useful if you want to display the FITS tiles of a HiPS. It specifies the number of bits per pixel. Possible values are:
|
||||
* -64: double, -32: float, 8: unsigned byte, 16: short, 32: integer 32 bits, 64: integer 64 bits
|
||||
* @property {number} [tileSize] - The width of the HEALPix tile images. Mostly 512 pixels but can be 256, 128, 64, 32
|
||||
* @property {number} [minOrder] - If not given, retrieved from the properties of the survey.
|
||||
* @property {boolean} [longitudeReversed=false] - Set it to True for planetary survey visualization
|
||||
* @property {number} [opacity=1.0] - Opacity of the survey or image (value between 0 and 1).
|
||||
* @property {string} [colormap="native"] - The colormap configuration for the survey or image.
|
||||
* @property {string} [stretch="linear"] - The stretch configuration for the survey or image.
|
||||
* @property {boolean} [reversed=false] - If true, the colormap is reversed; otherwise, it is not reversed.
|
||||
* @property {number} [minCut] - The minimum cut value for the color configuration. If not given, 0.0 for JPEG/PNG surveys, the value of the property file for FITS surveys
|
||||
* @property {number} [maxCut] - The maximum cut value for the color configuration. If not given, 1.0 for JPEG/PNG surveys, the value of the property file for FITS surveys
|
||||
* @property {boolean} [additive=false] - If true, additive blending is applied; otherwise, it is not applied.
|
||||
* @property {number} [gamma=1.0] - The gamma correction value for the color configuration.
|
||||
* @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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @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"]
|
||||
*/
|
||||
|
||||
|
||||
export let HiPS = (function () {
|
||||
/**
|
||||
* The object describing an image survey
|
||||
*
|
||||
* @class
|
||||
* @constructs HiPS
|
||||
*
|
||||
* @param {string} id - Mandatory unique identifier for the layer. Can be an arbitrary name
|
||||
* @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.
|
||||
*/
|
||||
function HiPS(id, location, options) {
|
||||
this.added = false;
|
||||
// Unique identifier for a survey
|
||||
this.id = id;
|
||||
|
||||
this.options = options;
|
||||
this.name = (options && options.name) || undefined;
|
||||
this.startUrl = options.startUrl;
|
||||
|
||||
if (location instanceof FileList) {
|
||||
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 (!localFiles[order]) {
|
||||
localFiles[order] = {}
|
||||
}
|
||||
|
||||
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")) {
|
||||
localFiles['properties'] = file;
|
||||
}
|
||||
|
||||
if (path.includes("Moc")) {
|
||||
localFiles['moc'] = file;
|
||||
}
|
||||
}
|
||||
|
||||
this.localFiles = localFiles;
|
||||
} else if (location instanceof Object) {
|
||||
this.localFiles = location;
|
||||
}
|
||||
|
||||
this.url = location;
|
||||
this.maxOrder = options.maxOrder;
|
||||
this.minOrder = options.minOrder || 0;
|
||||
this.cooFrame = options.cooFrame;
|
||||
this.tileSize = options.tileSize;
|
||||
this.skyFraction = options.skyFraction;
|
||||
this.longitudeReversed =
|
||||
options.longitudeReversed === undefined
|
||||
? false
|
||||
: options.longitudeReversed;
|
||||
this.imgFormat = options.imgFormat;
|
||||
this.formats = options.formats;
|
||||
this.defaultFitsMinCut = options.defaultFitsMinCut;
|
||||
this.defaultFitsMaxCut = options.defaultFitsMaxCut;
|
||||
this.numBitsPerPixel = options.numBitsPerPixel;
|
||||
this.creatorDid = options.creatorDid;
|
||||
this.errorCallback = options.errorCallback;
|
||||
this.successCallback = options.successCallback;
|
||||
|
||||
this.colorCfg = new ColorCfg(options);
|
||||
};
|
||||
|
||||
HiPS.prototype._fetchFasterUrlFromProperties = function(properties) {
|
||||
let self = this;
|
||||
|
||||
HiPSProperties.getFasterMirrorUrl(properties)
|
||||
.then((url) => {
|
||||
if (self.url !== url) {
|
||||
console.info(
|
||||
"Change url of ",
|
||||
self.id,
|
||||
" to ",
|
||||
url
|
||||
);
|
||||
|
||||
self.url = url;
|
||||
|
||||
// If added to the backend, then we need to tell it the url has changed
|
||||
if (self.added) {
|
||||
self.view.wasm.setHiPSUrl(
|
||||
self.creatorDid,
|
||||
url
|
||||
);
|
||||
}
|
||||
}
|
||||
})
|
||||
.catch((e) => {
|
||||
console.error(self);
|
||||
console.error(e);
|
||||
});
|
||||
}
|
||||
|
||||
HiPS.prototype._parseProperties = function(properties) {
|
||||
let self = this;
|
||||
self.creatorDid = properties.creator_did || self.creatorDid;
|
||||
// url
|
||||
|
||||
// Max order
|
||||
self.maxOrder =
|
||||
PropertyParser.maxOrder(properties) || self.maxOrder;
|
||||
|
||||
// Tile size
|
||||
self.tileSize =
|
||||
PropertyParser.tileSize(properties) || self.tileSize;
|
||||
|
||||
// Tile formats
|
||||
self.formats =
|
||||
PropertyParser.formats(properties) || self.formats;
|
||||
|
||||
// min order
|
||||
self.minOrder =
|
||||
PropertyParser.minOrder(properties) || self.minOrder;
|
||||
|
||||
// Frame
|
||||
self.cooFrame =
|
||||
PropertyParser.cooFrame(properties) || self.cooFrame;
|
||||
|
||||
// sky fraction
|
||||
self.skyFraction = PropertyParser.skyFraction(properties);
|
||||
|
||||
// Initial fov/ra/dec
|
||||
self.initialFov = PropertyParser.initialFov(properties);
|
||||
self.initialRa =
|
||||
properties &&
|
||||
properties.hips_initial_ra &&
|
||||
+properties.hips_initial_ra;
|
||||
self.initialDec =
|
||||
properties &&
|
||||
properties.hips_initial_dec &&
|
||||
+properties.hips_initial_dec;
|
||||
|
||||
// Cutouts
|
||||
const cutoutFromProperties = PropertyParser.cutouts(properties);
|
||||
self.defaultFitsMinCut = cutoutFromProperties[0];
|
||||
self.defaultFitsMaxCut = cutoutFromProperties[1];
|
||||
|
||||
// Bitpix
|
||||
self.numBitsPerPixel =
|
||||
PropertyParser.bitpix(properties) || self.numBitsPerPixel;
|
||||
|
||||
// HiPS body
|
||||
if (properties.hips_body) {
|
||||
self.hipsBody = properties.hips_body;
|
||||
// Use the property to define and check some user given infos
|
||||
// Longitude reversed
|
||||
self.longitudeReversed = true;
|
||||
}
|
||||
|
||||
// Give a better name if we have the HiPS metadata
|
||||
self.name = self.name || properties.obs_title;
|
||||
|
||||
self.name = self.name || self.id || self.url;
|
||||
self.name = self.name.replace(/ +/g, ' ');
|
||||
|
||||
self.creatorDid = self.creatorDid || self.id || self.url;
|
||||
|
||||
// Image format
|
||||
if (self.imgFormat) {
|
||||
// transform to lower case
|
||||
self.imgFormat = self.imgFormat.toLowerCase();
|
||||
// convert JPG -> JPEG
|
||||
if (self.imgFormat === "jpg") {
|
||||
self.imgFormat = "jpeg";
|
||||
}
|
||||
|
||||
// user wants a fits but the properties tells this format is not available
|
||||
if (
|
||||
self.imgFormat === "fits" &&
|
||||
self.formats &&
|
||||
self.formats.indexOf("fits") < 0
|
||||
) {
|
||||
throw self.name + " does not provide fits tiles";
|
||||
}
|
||||
|
||||
if (
|
||||
self.imgFormat === "webp" &&
|
||||
self.formats &&
|
||||
self.formats.indexOf("webp") < 0
|
||||
) {
|
||||
throw self.name + " does not provide webp tiles";
|
||||
}
|
||||
|
||||
if (
|
||||
self.imgFormat === "png" &&
|
||||
self.formats &&
|
||||
self.formats.indexOf("png") < 0
|
||||
) {
|
||||
throw self.name + " does not provide png tiles";
|
||||
}
|
||||
|
||||
if (
|
||||
self.imgFormat === "jpeg" &&
|
||||
self.formats &&
|
||||
self.formats.indexOf("jpeg") < 0
|
||||
) {
|
||||
throw self.name + " does not provide jpeg tiles";
|
||||
}
|
||||
} else {
|
||||
// user wants nothing then we choose one from the properties
|
||||
if (self.formats.indexOf("webp") >= 0) {
|
||||
self.imgFormat = "webp";
|
||||
} else if (self.formats.indexOf("png") >= 0) {
|
||||
self.imgFormat = "png";
|
||||
} else if (self.formats.indexOf("jpeg") >= 0) {
|
||||
self.imgFormat = "jpeg";
|
||||
} else if (self.formats.indexOf("fits") >= 0) {
|
||||
self.imgFormat = "fits";
|
||||
} else {
|
||||
throw (
|
||||
"Unsupported format(s) found in the properties: " +
|
||||
self.formats
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Cutouts
|
||||
let minCut, maxCut;
|
||||
if (self.imgFormat === "fits") {
|
||||
// Take into account the default cuts given by the property file (this is true especially for FITS HiPSes)
|
||||
minCut = self.colorCfg.minCut || self.defaultFitsMinCut || 0.0;
|
||||
maxCut = self.colorCfg.maxCut || self.defaultFitsMaxCut || 1.0;
|
||||
} else {
|
||||
minCut = self.colorCfg.minCut || 0.0;
|
||||
maxCut = self.colorCfg.maxCut || 1.0;
|
||||
}
|
||||
|
||||
self.setOptions({minCut, maxCut});
|
||||
|
||||
// Coo frame
|
||||
if (
|
||||
self.cooFrame == "ICRS" ||
|
||||
self.cooFrame == "ICRSd" ||
|
||||
self.cooFrame == "equatorial" ||
|
||||
self.cooFrame == "j2000"
|
||||
) {
|
||||
self.cooFrame = "ICRS";
|
||||
} else if (self.cooFrame == "galactic" || self.cooFrame == "GAL") {
|
||||
self.cooFrame = "GAL";
|
||||
} else {
|
||||
console.warn(
|
||||
"Invalid cooframe given: " +
|
||||
self.cooFrame +
|
||||
'. Coordinate systems supported: "ICRS", "ICRSd", "j2000" or "galactic". ICRS is chosen by default'
|
||||
);
|
||||
self.cooFrame = "ICRS";
|
||||
|
||||
}
|
||||
|
||||
self.formats = self.formats || [self.imgFormat];
|
||||
|
||||
self._saveInCache();
|
||||
}
|
||||
|
||||
HiPS.prototype.setView = function (view) {
|
||||
let self = this;
|
||||
|
||||
// do not allow to call setView multiple times otherwise
|
||||
// the querying to the properties and the search to the best
|
||||
// HiPS node will be done again for the same hiPS
|
||||
if (this.view) {
|
||||
return;
|
||||
}
|
||||
this.view = view;
|
||||
|
||||
if (this.localFiles) {
|
||||
// Fetch the properties file
|
||||
self.query = (async () => {
|
||||
// look for the properties file
|
||||
await HiPSProperties.fetchFromFile(self.localFiles["properties"])
|
||||
.then((p) => {
|
||||
self._parseProperties(p);
|
||||
|
||||
self.url = "local";
|
||||
|
||||
delete self.localFiles["properties"]
|
||||
})
|
||||
|
||||
return self;
|
||||
})();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
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/")
|
||||
|
||||
if (this.imgFormat === "fits") {
|
||||
// a fits is given
|
||||
isIncompleteOptions = !(
|
||||
this.maxOrder &&
|
||||
(!isID && this.url) &&
|
||||
this.imgFormat &&
|
||||
this.tileSize &&
|
||||
this.cooFrame &&
|
||||
this.numBitsPerPixel
|
||||
);
|
||||
} else {
|
||||
isIncompleteOptions = !(
|
||||
this.maxOrder &&
|
||||
(!isID && this.url) &&
|
||||
this.imgFormat &&
|
||||
this.tileSize &&
|
||||
this.cooFrame
|
||||
);
|
||||
}
|
||||
|
||||
self.query = (async () => {
|
||||
if (isIncompleteOptions) {
|
||||
// ID typed url
|
||||
if (self.startUrl && isID) {
|
||||
// First download the properties from the start url
|
||||
await HiPSProperties.fetchFromUrl(self.startUrl)
|
||||
.then((p) => {
|
||||
self._parseProperties(p);
|
||||
})
|
||||
|
||||
try {
|
||||
// the url stores a "CDS ID" we take it prioritaly
|
||||
// if the url is null, take the id, this is for some tests
|
||||
// to pass because some users might just give null as url param and a "CDS ID" as id param
|
||||
let id = self.url || self.id;
|
||||
|
||||
self.url = self.startUrl;
|
||||
|
||||
setTimeout(
|
||||
() => {
|
||||
if (!self.added)
|
||||
return;
|
||||
|
||||
HiPSProperties.fetchFromID(id)
|
||||
.then((p) => {
|
||||
//self.url = self.startUrl;
|
||||
self._fetchFasterUrlFromProperties(p);
|
||||
})
|
||||
},
|
||||
1000
|
||||
);
|
||||
} catch (e) {
|
||||
throw e;
|
||||
}
|
||||
} else if (!this.startUrl && isID) {
|
||||
try {
|
||||
// the url stores a "CDS ID" we take it prioritaly
|
||||
// if the url is null, take the id, this is for some tests
|
||||
// to pass because some users might just give null as url param and a "CDS ID" as id param
|
||||
let id = self.url || self.id;
|
||||
|
||||
await HiPSProperties.fetchFromID(id)
|
||||
.then((p) => {
|
||||
self.url = p.hips_service_url;
|
||||
|
||||
self._parseProperties(p);
|
||||
self._fetchFasterUrlFromProperties(p);
|
||||
})
|
||||
} catch (e) {
|
||||
throw e;
|
||||
}
|
||||
} else {
|
||||
await HiPSProperties.fetchFromUrl(self.url)
|
||||
.then((p) => {
|
||||
self._parseProperties(p);
|
||||
})
|
||||
}
|
||||
} else {
|
||||
self._parseProperties({
|
||||
hips_order: this.maxOrder,
|
||||
hips_service_url: this.url,
|
||||
hips_tile_width: this.tileSize,
|
||||
hips_frame: this.cooFrame
|
||||
})
|
||||
}
|
||||
|
||||
return self;
|
||||
})()
|
||||
};
|
||||
|
||||
/* Precondition: view is attached */
|
||||
HiPS.prototype._saveInCache = function () {
|
||||
let self = this;
|
||||
let hipsCache = this.view.aladin.hipsCache;
|
||||
|
||||
if (hipsCache.contains(self.id)) {
|
||||
hipsCache.append(self.id, this)
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Checks if the HiPS represents a planetary body.
|
||||
*
|
||||
* This method returns a boolean indicating whether the HiPS corresponds to a planetary body, e.g. the earth or a celestial body.
|
||||
*
|
||||
* @memberof HiPS
|
||||
*
|
||||
* @returns {boolean} Returns true if the HiPS represents a planetary body; otherwise, returns false.
|
||||
*/
|
||||
HiPS.prototype.isPlanetaryBody = function () {
|
||||
return this.hipsBody !== undefined;
|
||||
};
|
||||
|
||||
/**
|
||||
* Sets the image format for the HiPS.
|
||||
*
|
||||
* This method updates the image format of the HiPS, performs format validation, and triggers the update of metadata.
|
||||
*
|
||||
* @memberof HiPS
|
||||
*
|
||||
* @param {string} format - The desired image format. Should be one of ["fits", "png", "jpg", "webp"].
|
||||
*
|
||||
* @throws {string} Throws an error if the provided format is not one of the supported formats or if the format is not available for the specific HiPS.
|
||||
*/
|
||||
HiPS.prototype.setImageFormat = function (format) {
|
||||
let self = this;
|
||||
self.query.then(() => {
|
||||
let imgFormat = format.toLowerCase();
|
||||
|
||||
if (
|
||||
imgFormat !== "fits" &&
|
||||
imgFormat !== "png" &&
|
||||
imgFormat !== "jpg" &&
|
||||
imgFormat !== "jpeg" &&
|
||||
imgFormat !== "webp"
|
||||
) {
|
||||
throw 'Formats must lie in ["fits", "png", "jpg", "webp"]';
|
||||
}
|
||||
|
||||
if (imgFormat === "jpg") {
|
||||
imgFormat = "jpeg";
|
||||
}
|
||||
|
||||
// Passed the check, we erase the image format with the new one
|
||||
// We do nothing if the imgFormat is the same
|
||||
if (self.imgFormat === imgFormat) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Check the properties to see if the given format is available among the list
|
||||
// If the properties have not been retrieved yet, it will be tested afterwards
|
||||
const availableFormats = self.formats;
|
||||
// user wants a fits but the metadata tells this format is not available
|
||||
if (
|
||||
imgFormat === "fits" &&
|
||||
availableFormats.indexOf("fits") < 0
|
||||
) {
|
||||
throw self.id + " does not provide fits tiles";
|
||||
}
|
||||
|
||||
if (
|
||||
imgFormat === "webp" &&
|
||||
availableFormats.indexOf("webp") < 0
|
||||
) {
|
||||
throw self.id + " does not provide webp tiles";
|
||||
}
|
||||
|
||||
if (
|
||||
imgFormat === "png" &&
|
||||
availableFormats.indexOf("png") < 0
|
||||
) {
|
||||
throw self.id + " does not provide png tiles";
|
||||
}
|
||||
|
||||
if (
|
||||
imgFormat === "jpeg" &&
|
||||
availableFormats.indexOf("jpeg") < 0
|
||||
) {
|
||||
throw self.id + " does not provide jpeg tiles";
|
||||
}
|
||||
|
||||
// Switch from png/webp/jpeg to fits
|
||||
if (
|
||||
(self.imgFormat === "png" ||
|
||||
self.imgFormat === "webp" ||
|
||||
self.imgFormat === "jpeg") &&
|
||||
imgFormat === "fits"
|
||||
) {
|
||||
if (Number.isFinite(self.defaultFitsMinCut) && Number.isFinite(self.defaultFitsMaxCut)) {
|
||||
// reset cuts to those given from the properties
|
||||
self.setCuts(self.defaultFitsMinCut, self.defaultFitsMaxCut);
|
||||
}
|
||||
// Switch from fits to png/webp/jpeg
|
||||
} else if (self.imgFormat === "fits") {
|
||||
self.setCuts(0.0, 1.0);
|
||||
}
|
||||
|
||||
// Check if it is a fits
|
||||
self.imgFormat = imgFormat;
|
||||
|
||||
self._updateMetadata();
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Sets the opacity factor when rendering the HiPS
|
||||
*
|
||||
* @memberof HiPS
|
||||
*
|
||||
* @returns {string[]} Returns the formats accepted for the survey, i.e. the formats of tiles that are availables. Could be PNG, WEBP, JPG and FITS.
|
||||
*/
|
||||
HiPS.prototype.getAvailableFormats = function () {
|
||||
return this.formats;
|
||||
};
|
||||
|
||||
/**
|
||||
* Sets the opacity factor when rendering the HiPS
|
||||
*
|
||||
* @memberof HiPS
|
||||
*
|
||||
* @param {number} opacity - Opacity of the survey to set. Between 0 and 1
|
||||
*/
|
||||
HiPS.prototype.setOpacity = function (opacity) {
|
||||
this.setOptions({opacity})
|
||||
};
|
||||
|
||||
/**
|
||||
* Sets the blending mode when rendering the HiPS
|
||||
*
|
||||
* @memberof HiPS
|
||||
*
|
||||
* @param {boolean} [additive=false] -
|
||||
*
|
||||
* @description Two rendering modes are availables i.e. the default one and the additive one.
|
||||
* When rendering this survey on top of the already rendered ones, the final color of the screen is computed like:
|
||||
* <br>
|
||||
* <br>opacity * this_survey_color + (1 - opacity) * already_rendered_color for the default mode
|
||||
* <br>opacity * this_survey_color + already_rendered_color for the additive mode
|
||||
* <br>
|
||||
* <br>
|
||||
* Additive mode allows you to do linear survey color combination i.e. let's define 3 surveys named s1, s2, s3. Each could be associated to one color channel, i.e. s1 with red, s2 with green and s3 with the blue color channel.
|
||||
* If the additive blending mode is enabled, then the final pixel color of your screen will be: rgb = [s1_opacity * s1_color; s2_opacity * s2_color; s3_opacity * s3_color]
|
||||
*/
|
||||
HiPS.prototype.setBlendingConfig = function (additive = false) {
|
||||
this.setOptions({additive});
|
||||
};
|
||||
|
||||
/**
|
||||
* Sets the colormap when rendering the HiPS.
|
||||
*
|
||||
* @memberof HiPS
|
||||
*
|
||||
* @param {string} [colormap="grayscale"] - The colormap label to use. See {@link https://matplotlib.org/stable/users/explain/colors/colormaps.html|here} for more info about colormaps.
|
||||
* Possible values are:
|
||||
* <br>"blues"
|
||||
* <br>"cividis"
|
||||
* <br>"cubehelix"
|
||||
* <br>"eosb"
|
||||
* <br>"grayscale"
|
||||
* <br>"inferno"
|
||||
* <br>"magma"
|
||||
* <br>"native"
|
||||
* <br>"parula"
|
||||
* <br>"plasma"
|
||||
* <br>"rainbow"
|
||||
* <br>"rdbu"
|
||||
* <br>"rdylbu"
|
||||
* <br>"redtemperature"
|
||||
* <br>"sinebow"
|
||||
* <br>"spectral"
|
||||
* <br>"summer"
|
||||
* <br>"viridis"
|
||||
* <br>"ylgnbu"
|
||||
* <br>"ylorbr"
|
||||
* <br>"red"
|
||||
* <br>"green"
|
||||
* <br>"blue"
|
||||
* @param {Object} [options] - Options for the colormap
|
||||
* @param {string} [options.stretch] - Stretching function of the colormap. Possible values are 'linear', 'asinh', 'log', 'sqrt', 'pow'. If no given, will not change it.
|
||||
* @param {boolean} [options.reversed=false] - Reverse the colormap axis.
|
||||
*/
|
||||
HiPS.prototype.setColormap = function (colormap, options) {
|
||||
this.setOptions({colormap, ...options})
|
||||
};
|
||||
|
||||
/**
|
||||
* Sets the gamma correction factor for the HiPS.
|
||||
*
|
||||
* This method updates the gamma of the HiPS.
|
||||
*
|
||||
* @memberof HiPS
|
||||
*
|
||||
* @param {number} minCut - The low cut value to set for the HiPS.
|
||||
* @param {number} maxCut - The high cut value to set for the HiPS.
|
||||
*/
|
||||
HiPS.prototype.setCuts = function (minCut, maxCut) {
|
||||
this.setOptions({minCut, maxCut})
|
||||
};
|
||||
|
||||
HiPS.prototype.getCuts = function () {
|
||||
return this.colorCfg.getCuts();
|
||||
};
|
||||
|
||||
/**
|
||||
* Sets the gamma correction factor for the HiPS.
|
||||
*
|
||||
* This method updates the gamma of the HiPS.
|
||||
*
|
||||
* @memberof HiPS
|
||||
*
|
||||
* @param {number} gamma - The saturation value to set for the HiPS. Between 0.1 and 10
|
||||
*/
|
||||
HiPS.prototype.setGamma = function (gamma) {
|
||||
this.setOptions({gamma})
|
||||
};
|
||||
|
||||
/**
|
||||
* Sets the saturation for the HiPS.
|
||||
*
|
||||
* This method updates the saturation of the HiPS.
|
||||
*
|
||||
* @memberof HiPS
|
||||
*
|
||||
* @param {number} saturation - The saturation value to set for the HiPS. Between 0 and 1
|
||||
*/
|
||||
HiPS.prototype.setSaturation = function (saturation) {
|
||||
this.setOptions({saturation})
|
||||
};
|
||||
|
||||
/**
|
||||
* Sets the brightness for the HiPS.
|
||||
*
|
||||
* This method updates the brightness of the HiPS.
|
||||
*
|
||||
* @memberof HiPS
|
||||
*
|
||||
* @param {number} brightness - The brightness value to set for the HiPS. Between 0 and 1
|
||||
*/
|
||||
HiPS.prototype.setBrightness = function (brightness) {
|
||||
this.setOptions({brightness})
|
||||
};
|
||||
|
||||
/**
|
||||
* Sets the contrast for the HiPS.
|
||||
*
|
||||
* This method updates the contrast of the HiPS and triggers the update of metadata.
|
||||
*
|
||||
* @memberof HiPS
|
||||
*
|
||||
* @param {number} contrast - The contrast value to set for the HiPS. Between 0 and 1
|
||||
*/
|
||||
HiPS.prototype.setContrast = function (contrast) {
|
||||
this.setOptions({contrast})
|
||||
};
|
||||
|
||||
// Private method for updating the backend with the new meta
|
||||
HiPS.prototype._updateMetadata = function () {
|
||||
try {
|
||||
if (this.added) {
|
||||
this.view.wasm.setImageMetadata(this.layer, {
|
||||
...this.colorCfg.get(),
|
||||
longitudeReversed: this.longitudeReversed,
|
||||
imgFormat: this.imgFormat,
|
||||
});
|
||||
// once the meta have been well parsed, we can set the meta
|
||||
ALEvent.HIPS_LAYER_CHANGED.dispatchedTo(this.view.aladinDiv, {
|
||||
layer: this,
|
||||
});
|
||||
|
||||
// Save it in the JS HiPS cache
|
||||
this._saveInCache();
|
||||
}
|
||||
} catch (e) {
|
||||
// Display the error message
|
||||
console.error(e);
|
||||
}
|
||||
};
|
||||
|
||||
HiPS.prototype.setOptions = function(options) {
|
||||
this.colorCfg.setOptions(options);
|
||||
this.options = {...this.options, ...options};
|
||||
|
||||
this._updateMetadata();
|
||||
};
|
||||
|
||||
HiPS.prototype.add = function (layer) {
|
||||
this.layer = layer;
|
||||
let self = this;
|
||||
|
||||
const config = {
|
||||
layer,
|
||||
properties: {
|
||||
creatorDid: self.creatorDid,
|
||||
url: self.url,
|
||||
maxOrder: self.maxOrder,
|
||||
cooFrame: self.cooFrame,
|
||||
tileSize: self.tileSize,
|
||||
formats: self.formats,
|
||||
bitpix: self.numBitsPerPixel,
|
||||
skyFraction: self.skyFraction,
|
||||
minOrder: self.minOrder,
|
||||
hipsInitialFov: self.initialFov,
|
||||
hipsInitialRa: self.initialRa,
|
||||
hipsInitialDec: self.initialDec,
|
||||
isPlanetaryBody: self.isPlanetaryBody(),
|
||||
hipsBody: self.hipsBody,
|
||||
},
|
||||
meta: {
|
||||
...this.colorCfg.get(),
|
||||
longitudeReversed: this.longitudeReversed,
|
||||
imgFormat: this.imgFormat,
|
||||
}
|
||||
};
|
||||
|
||||
let localFiles;
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.view.wasm.addHiPS(
|
||||
config,
|
||||
localFiles
|
||||
);
|
||||
|
||||
return Promise.resolve(this)
|
||||
.then((hips) => {
|
||||
if (hips.successCallback) {
|
||||
hips.successCallback(hips)
|
||||
}
|
||||
|
||||
return hips
|
||||
});
|
||||
};
|
||||
|
||||
// @api
|
||||
HiPS.prototype.toggle = function () {
|
||||
const opacity = this.getOpacity()
|
||||
if (opacity != 0.0) {
|
||||
this.prevOpacity = opacity;
|
||||
this.setOpacity(0.0);
|
||||
} else {
|
||||
this.setOpacity(this.prevOpacity);
|
||||
}
|
||||
};
|
||||
|
||||
// @oldapi
|
||||
HiPS.prototype.setAlpha = HiPS.prototype.setOpacity;
|
||||
|
||||
HiPS.prototype.setColorCfg = function (colorCfg) {
|
||||
this.colorCfg = colorCfg;
|
||||
|
||||
this._updateMetadata();
|
||||
};
|
||||
|
||||
// @api
|
||||
HiPS.prototype.getColorCfg = function () {
|
||||
return this.colorCfg;
|
||||
};
|
||||
|
||||
// @api
|
||||
HiPS.prototype.getOpacity = function () {
|
||||
return this.colorCfg.getOpacity();
|
||||
};
|
||||
|
||||
HiPS.prototype.getAlpha = HiPS.prototype.getOpacity;
|
||||
|
||||
// @api
|
||||
HiPS.prototype.readPixel = function (x, y) {
|
||||
return this.view.wasm.readPixel(x, y, this.layer);
|
||||
};
|
||||
|
||||
HiPS.DEFAULT_SURVEY_ID = "P/DSS2/color";
|
||||
|
||||
return HiPS;
|
||||
})();
|
||||
@@ -20,7 +20,7 @@
|
||||
/******************************************************************************
|
||||
* Aladin Lite project
|
||||
*
|
||||
* File ImageHiPS
|
||||
* File HiPS
|
||||
*
|
||||
* Authors: Thomas Boch & Matthieu Baumann [CDS]
|
||||
*
|
||||
|
||||
@@ -70,14 +70,14 @@ HiPSProperties.fetchFromID = async function(ID) {
|
||||
}
|
||||
|
||||
HiPSProperties.fetchFromUrl = async function(urlOrId) {
|
||||
let url;
|
||||
|
||||
try {
|
||||
urlOrId = new URL(urlOrId);
|
||||
} catch (e) {
|
||||
// Relative path test
|
||||
try {
|
||||
urlOrId = Utils.getAbsoluteURL(urlOrId)
|
||||
console.log(urlOrId)
|
||||
|
||||
urlOrId = new URL(urlOrId);
|
||||
} catch(e) {
|
||||
throw e;
|
||||
@@ -87,7 +87,7 @@ HiPSProperties.fetchFromUrl = async function(urlOrId) {
|
||||
// Fetch the properties of the survey
|
||||
const HiPSServiceUrl = urlOrId.toString();
|
||||
|
||||
let url = HiPSServiceUrl;
|
||||
url = HiPSServiceUrl;
|
||||
// Use the url for retrieving the HiPS properties
|
||||
// remove final slash
|
||||
if (url.slice(-1) === '/') {
|
||||
@@ -100,6 +100,7 @@ HiPSProperties.fetchFromUrl = async function(urlOrId) {
|
||||
// fix for HTTPS support --> will work for all HiPS served by CDS
|
||||
url = Utils.fixURLForHTTPS(url)
|
||||
|
||||
|
||||
let init = {};
|
||||
if (Utils.requestCORSIfNotSameOrigin(url)) {
|
||||
init = { mode: 'cors' };
|
||||
@@ -123,7 +124,9 @@ HiPSProperties.fetchFromUrl = async function(urlOrId) {
|
||||
if (!metadata.hips_frame || !metadata.hips_order) {
|
||||
reject('Bad properties: do not contain the mandatory frame or order info')
|
||||
} else {
|
||||
metadata.hips_service_url = HiPSServiceUrl;
|
||||
if (!("hips_service_url" in metadata)) {
|
||||
metadata.hips_service_url = HiPSServiceUrl;
|
||||
}
|
||||
resolve(metadata);
|
||||
}
|
||||
} else {
|
||||
@@ -135,7 +138,28 @@ HiPSProperties.fetchFromUrl = async function(urlOrId) {
|
||||
return result;
|
||||
}
|
||||
|
||||
HiPSProperties.getFasterMirrorUrl = function (metadata, currUrl) {
|
||||
HiPSProperties.fetchFromFile = function(file) {
|
||||
let url = URL.createObjectURL(file);
|
||||
return fetch(url)
|
||||
.then((response) => response.text())
|
||||
.then(
|
||||
(response) => new Promise((resolve, reject) => {
|
||||
// We get the property here
|
||||
let metadata = HiPSDefinition.parseHiPSProperties(response);
|
||||
|
||||
URL.revokeObjectURL(url)
|
||||
|
||||
// 1. Ensure there is exactly one survey matching
|
||||
if (metadata && Object.keys(metadata).length > 0) {
|
||||
resolve(metadata)
|
||||
} else {
|
||||
reject('No surveys matching at this url: ' + rootURL);
|
||||
}
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
HiPSProperties.getFasterMirrorUrl = function (metadata) {
|
||||
const pingHiPSServiceUrl = async (baseUrl) => {
|
||||
baseUrl = Utils.fixURLForHTTPS(baseUrl);
|
||||
|
||||
@@ -163,7 +187,6 @@ HiPSProperties.getFasterMirrorUrl = function (metadata, currUrl) {
|
||||
});
|
||||
const duration = performance.now() - startRequestTime;//the time needed to do the request
|
||||
|
||||
|
||||
return {duration, validRequest, baseUrl};
|
||||
};
|
||||
|
||||
@@ -184,6 +207,10 @@ HiPSProperties.getFasterMirrorUrl = function (metadata, currUrl) {
|
||||
urls.push(curUrl)
|
||||
}
|
||||
|
||||
if (numHiPSServiceURL === 1) {
|
||||
return Promise.resolve(urls[0]);
|
||||
}
|
||||
|
||||
return Promise.all(promises)
|
||||
.then((responses) => {
|
||||
// filter the ones that failed to not choose them
|
||||
@@ -200,7 +227,6 @@ HiPSProperties.getFasterMirrorUrl = function (metadata, currUrl) {
|
||||
return r1.duration - r2.duration;
|
||||
});
|
||||
|
||||
//console.log(validResponses)
|
||||
let newUrlResp;
|
||||
|
||||
if (validResponses.length >= 2) {
|
||||
@@ -217,9 +243,10 @@ HiPSProperties.getFasterMirrorUrl = function (metadata, currUrl) {
|
||||
// no valid response => we return an error
|
||||
return Promise.reject('All mirrors urls have been tested:' + urls)
|
||||
}
|
||||
|
||||
/*
|
||||
// check if there is a big difference from the current one
|
||||
let currUrlResp = validResponses.find((r) => r.baseUrl === currUrl)
|
||||
|
||||
// it may happen that the url requested by the user is too slow hence discarded
|
||||
// for these cases, we automatically switch to the new fastest url.
|
||||
let urlChosen;
|
||||
@@ -231,9 +258,10 @@ HiPSProperties.getFasterMirrorUrl = function (metadata, currUrl) {
|
||||
urlChosen = newUrlResp.baseUrl;
|
||||
}
|
||||
|
||||
//console.log('curr url', currUrlResp, ', new ', newUrlResp)
|
||||
|
||||
urlChosen = Utils.fixURLForHTTPS(urlChosen)
|
||||
*/
|
||||
let urlChosen = newUrlResp.baseUrl;
|
||||
return urlChosen;
|
||||
})
|
||||
}
|
||||
|
||||
617
src/js/Image.js
@@ -25,11 +25,41 @@
|
||||
* Authors: Matthieu Baumann [CDS]
|
||||
*
|
||||
*****************************************************************************/
|
||||
import { ALEvent } from "./events/ALEvent.js";
|
||||
import { ColorCfg } from "./ColorCfg.js";
|
||||
import { Aladin } from "./Aladin.js";
|
||||
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 { AVM } from "./libs/avm.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.
|
||||
*
|
||||
@@ -97,7 +127,7 @@ export let Image = (function () {
|
||||
* @param {ImageOptions} [options] - The option for the survey
|
||||
*
|
||||
*/
|
||||
function Image(url, options) {
|
||||
let Image = function(url, options) {
|
||||
// Name of the layer
|
||||
this.layer = null;
|
||||
this.added = false;
|
||||
@@ -110,11 +140,8 @@ export let Image = (function () {
|
||||
// callbacks
|
||||
this.successCallback = options && options.successCallback;
|
||||
this.errorCallback = options && options.errorCallback;
|
||||
// initialize the color meta data here
|
||||
// set a asinh stretch by default if there is none
|
||||
/*if (options) {
|
||||
options.stretch = options.stretch || "asinh";
|
||||
}*/
|
||||
|
||||
this.longitudeReversed = false;
|
||||
|
||||
this.colorCfg = new ColorCfg(options);
|
||||
this.options = options || {};
|
||||
@@ -124,374 +151,296 @@ export let Image = (function () {
|
||||
this.query = Promise.resolve(self);
|
||||
}
|
||||
|
||||
/* Precondition: view is already attached */
|
||||
Image.prototype._saveInCache = function () {
|
||||
let hipsCache = this.view.aladin.hipsCache;
|
||||
if (hipsCache.contains(self.id)) {
|
||||
hipsCache.append(this.id, this);
|
||||
}
|
||||
};
|
||||
Image.prototype = {
|
||||
/* Precondition: view is already attached */
|
||||
_saveInCache: HiPS.prototype._saveInCache,
|
||||
|
||||
Image.prototype.setView = function (view) {
|
||||
this.view = view;
|
||||
// @api
|
||||
getCuts: HiPS.prototype.getCuts,
|
||||
|
||||
this._saveInCache();
|
||||
};
|
||||
// @api
|
||||
setOpacity: HiPS.prototype.setOpacity,
|
||||
|
||||
// @api
|
||||
Image.prototype.setOpacity = function (opacity) {
|
||||
let self = this;
|
||||
this._updateMetadata(() => {
|
||||
self.colorCfg.setOpacity(opacity);
|
||||
});
|
||||
};
|
||||
|
||||
// @api
|
||||
Image.prototype.setBlendingConfig = function (additive = false) {
|
||||
this._updateMetadata(() => {
|
||||
this.colorCfg.setBlendingConfig(additive);
|
||||
});
|
||||
};
|
||||
// @api
|
||||
setOptions: HiPS.prototype.setOptions,
|
||||
// @api
|
||||
setBlendingConfig: HiPS.prototype.setBlendingConfig,
|
||||
|
||||
// @api
|
||||
Image.prototype.setColormap = function (colormap, options) {
|
||||
this._updateMetadata(() => {
|
||||
this.colorCfg.setColormap(colormap, options);
|
||||
});
|
||||
};
|
||||
// @api
|
||||
setColormap: HiPS.prototype.setColormap,
|
||||
|
||||
// @api
|
||||
Image.prototype.setCuts = function (lowCut, highCut) {
|
||||
this._updateMetadata(() => {
|
||||
this.colorCfg.setCuts(lowCut, highCut);
|
||||
});
|
||||
};
|
||||
// @api
|
||||
setCuts: HiPS.prototype.setCuts,
|
||||
|
||||
// @api
|
||||
Image.prototype.setGamma = function (gamma) {
|
||||
this._updateMetadata(() => {
|
||||
this.colorCfg.setGamma(gamma);
|
||||
});
|
||||
};
|
||||
// @api
|
||||
setGamma: HiPS.prototype.setGamma,
|
||||
|
||||
// @api
|
||||
Image.prototype.setSaturation = function (saturation) {
|
||||
this._updateMetadata(() => {
|
||||
this.colorCfg.setSaturation(saturation);
|
||||
});
|
||||
};
|
||||
// @api
|
||||
setSaturation: HiPS.prototype.setSaturation,
|
||||
|
||||
Image.prototype.setBrightness = function (brightness) {
|
||||
this._updateMetadata(() => {
|
||||
this.colorCfg.setBrightness(brightness);
|
||||
});
|
||||
};
|
||||
setBrightness: HiPS.prototype.setBrightness,
|
||||
|
||||
Image.prototype.setContrast = function (contrast) {
|
||||
this._updateMetadata(() => {
|
||||
this.colorCfg.setContrast(contrast);
|
||||
});
|
||||
};
|
||||
setContrast: HiPS.prototype.setContrast,
|
||||
|
||||
// Private method for updating the view with the new meta
|
||||
Image.prototype._updateMetadata = function (callback) {
|
||||
if (callback) {
|
||||
callback();
|
||||
}
|
||||
// @api
|
||||
toggle: HiPS.prototype.toggle,
|
||||
// @oldapi
|
||||
setAlpha: HiPS.prototype.setOpacity,
|
||||
|
||||
setColorCfg: HiPS.prototype.setColorCfg,
|
||||
|
||||
// @api
|
||||
getColorCfg: HiPS.prototype.getColorCfg,
|
||||
|
||||
// @api
|
||||
getOpacity: HiPS.prototype.getOpacity,
|
||||
getAlpha: HiPS.prototype.getOpacity,
|
||||
|
||||
// @api
|
||||
readPixel: HiPS.prototype.readPixel,
|
||||
|
||||
// Tell the view its meta have changed
|
||||
try {
|
||||
if (this.added) {
|
||||
this.view.wasm.setImageMetadata(this.layer, {
|
||||
...this.colorCfg.get(),
|
||||
longitudeReversed: false,
|
||||
imgFormat: this.imgFormat,
|
||||
});
|
||||
ALEvent.HIPS_LAYER_CHANGED.dispatchedTo(this.view.aladinDiv, {
|
||||
layer: this,
|
||||
});
|
||||
}
|
||||
// Private method for updating the view with the new meta
|
||||
_updateMetadata: HiPS.prototype._updateMetadata,
|
||||
|
||||
// save it in the JS HiPS cache
|
||||
setView: function (view) {
|
||||
this.view = view;
|
||||
this._saveInCache();
|
||||
} catch (e) {
|
||||
// Display the error message
|
||||
console.error(e);
|
||||
}
|
||||
};
|
||||
},
|
||||
|
||||
Image.prototype._addFITS = function(layer) {
|
||||
let self = this;
|
||||
_addFITS: function(layer) {
|
||||
let self = this;
|
||||
|
||||
return Utils.fetch({
|
||||
url: this.url,
|
||||
dataType: 'readableStream',
|
||||
success: (stream) => {
|
||||
return self.view.wasm.addImageFITS(
|
||||
stream,
|
||||
{
|
||||
...self.colorCfg.get(),
|
||||
longitudeReversed: false,
|
||||
imgFormat: 'fits',
|
||||
},
|
||||
layer
|
||||
)
|
||||
},
|
||||
error: (e) => {
|
||||
// try as cors
|
||||
const url = Aladin.JSONP_PROXY + '?url=' + self.url;
|
||||
return Utils.fetch({
|
||||
url: this.url,
|
||||
dataType: 'readableStream',
|
||||
success: (stream) => {
|
||||
return self.view.wasm.addImageFITS(
|
||||
stream,
|
||||
{
|
||||
...self.colorCfg.get(),
|
||||
longitudeReversed: this.longitudeReversed,
|
||||
imgFormat: 'fits',
|
||||
},
|
||||
layer
|
||||
)
|
||||
},
|
||||
error: (e) => {
|
||||
// try as cors
|
||||
const url = Aladin.JSONP_PROXY + '?url=' + self.url;
|
||||
|
||||
return Utils.fetch({
|
||||
url: url,
|
||||
dataType: 'readableStream',
|
||||
success: (stream) => {
|
||||
return self.view.wasm.addImageFITS(
|
||||
stream,
|
||||
{
|
||||
...self.colorCfg.get(),
|
||||
longitudeReversed: false,
|
||||
imgFormat: 'fits',
|
||||
},
|
||||
layer
|
||||
)
|
||||
},
|
||||
});
|
||||
}
|
||||
})
|
||||
.then((imageParams) => {
|
||||
self.imgFormat = 'fits';
|
||||
|
||||
return Promise.resolve(imageParams);
|
||||
})
|
||||
}
|
||||
|
||||
Image.prototype._addJPGOrPNG = function(layer) {
|
||||
let self = this;
|
||||
let img = document.createElement('img');
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
img.src = this.url;
|
||||
img.crossOrigin = "Anonymous";
|
||||
img.onload = () => {
|
||||
const img2Blob = () => {
|
||||
var canvas = document.createElement("canvas");
|
||||
|
||||
canvas.width = img.width;
|
||||
canvas.height = img.height;
|
||||
|
||||
// Copy the image contents to the canvas
|
||||
var ctx = canvas.getContext("2d");
|
||||
ctx.drawImage(img, 0, 0, img.width, img.height);
|
||||
|
||||
const imageData = ctx.getImageData(0, 0, img.width, img.height);
|
||||
|
||||
const blob = new Blob([imageData.data]);
|
||||
const stream = blob.stream(1024);
|
||||
|
||||
resolve(stream)
|
||||
};
|
||||
|
||||
if (!self.options.wcs) {
|
||||
/* look for avm tags if no wcs is given */
|
||||
let avm = new AVM(img);
|
||||
|
||||
avm.load((obj) => {
|
||||
// obj contains the following information:
|
||||
// obj.id (string) = The ID provided for the image
|
||||
// obj.img (object) = The image object
|
||||
// obj.xmp (string) = The raw XMP header
|
||||
// obj.wcsdata (Boolean) = If WCS have been loaded
|
||||
// obj.tags (object) = An array containing all the loaded tags e.g. obj.tags['Headline']
|
||||
// obj.wcs (object) = The wcs parsed from the image
|
||||
if (obj.wcsdata) {
|
||||
if (img.width !== obj.wcs.NAXIS1) {
|
||||
obj.wcs.NAXIS1 = img.width;
|
||||
}
|
||||
|
||||
if (img.height !== obj.wcs.NAXIS2) {
|
||||
obj.wcs.NAXIS2 = img.height;
|
||||
}
|
||||
|
||||
self.options.wcs = obj.wcs;
|
||||
|
||||
img2Blob()
|
||||
} else {
|
||||
// no tags found
|
||||
reject('No WCS have been found in the image')
|
||||
return;
|
||||
}
|
||||
})
|
||||
} else {
|
||||
img2Blob()
|
||||
return Utils.fetch({
|
||||
url: url,
|
||||
dataType: 'readableStream',
|
||||
success: (stream) => {
|
||||
return self.view.wasm.addImageFITS(
|
||||
stream,
|
||||
{
|
||||
...self.colorCfg.get(),
|
||||
longitudeReversed: this.longitudeReversed,
|
||||
imgFormat: 'fits',
|
||||
},
|
||||
layer
|
||||
)
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
})
|
||||
.then((imageParams) => {
|
||||
self.imgFormat = 'fits';
|
||||
|
||||
let proxyUsed = false;
|
||||
img.onerror = (e) => {
|
||||
// use proxy
|
||||
if (proxyUsed) {
|
||||
console.error(e);
|
||||
return Promise.resolve(imageParams);
|
||||
})
|
||||
},
|
||||
|
||||
reject('Error parsing image located at: ' + self.url)
|
||||
return;
|
||||
}
|
||||
_addJPGOrPNG: function(layer) {
|
||||
let self = this;
|
||||
let img = document.createElement('img');
|
||||
|
||||
proxyUsed = true;
|
||||
img.src = Aladin.JSONP_PROXY + '?url=' + self.url;
|
||||
}
|
||||
})
|
||||
.then((readableStream) => {
|
||||
let wcs = self.options && self.options.wcs;
|
||||
wcs.NAXIS1 = wcs.NAXIS1 || img.width;
|
||||
wcs.NAXIS2 = wcs.NAXIS2 || img.height;
|
||||
return new Promise((resolve, reject) => {
|
||||
img.src = this.url;
|
||||
img.crossOrigin = "Anonymous";
|
||||
img.onload = () => {
|
||||
const img2Blob = () => {
|
||||
var canvas = document.createElement("canvas");
|
||||
|
||||
return self.view.wasm
|
||||
.addImageWithWCS(
|
||||
readableStream,
|
||||
wcs,
|
||||
{
|
||||
...self.colorCfg.get(),
|
||||
longitudeReversed: false,
|
||||
imgFormat: 'jpeg',
|
||||
},
|
||||
layer
|
||||
)
|
||||
})
|
||||
.then((imageParams) => {
|
||||
self.imgFormat = 'jpeg';
|
||||
return Promise.resolve(imageParams);
|
||||
})
|
||||
.finally(() => {
|
||||
img.remove();
|
||||
});
|
||||
}
|
||||
canvas.width = img.width;
|
||||
canvas.height = img.height;
|
||||
|
||||
// Copy the image contents to the canvas
|
||||
var ctx = canvas.getContext("2d");
|
||||
ctx.drawImage(img, 0, 0, img.width, img.height);
|
||||
|
||||
const imageData = ctx.getImageData(0, 0, img.width, img.height);
|
||||
|
||||
const blob = new Blob([imageData.data]);
|
||||
const stream = blob.stream(1024);
|
||||
|
||||
resolve(stream)
|
||||
};
|
||||
|
||||
Image.prototype.add = function (layer) {
|
||||
this.layer = layer;
|
||||
if (!self.options.wcs) {
|
||||
/* look for avm tags if no wcs is given */
|
||||
let avm = new AVM(img);
|
||||
|
||||
let self = this;
|
||||
let promise;
|
||||
avm.load((obj) => {
|
||||
// obj contains the following information:
|
||||
// obj.id (string) = The ID provided for the image
|
||||
// obj.img (object) = The image object
|
||||
// obj.xmp (string) = The raw XMP header
|
||||
// obj.wcsdata (Boolean) = If WCS have been loaded
|
||||
// obj.tags (object) = An array containing all the loaded tags e.g. obj.tags['Headline']
|
||||
// obj.wcs (object) = The wcs parsed from the image
|
||||
if (obj.wcsdata) {
|
||||
if (img.width !== obj.wcs.NAXIS1) {
|
||||
obj.wcs.NAXIS1 = img.width;
|
||||
}
|
||||
|
||||
if (this.imgFormat === 'fits') {
|
||||
promise = this._addFITS(layer)
|
||||
.catch(e => {
|
||||
console.error(`Image located at ${this.url} could not be parsed as fits file. Is the imgFormat specified correct?`)
|
||||
return Promise.reject(e)
|
||||
})
|
||||
} else if (this.imgFormat === 'jpeg' || this.imgFormat === 'png') {
|
||||
promise = this._addJPGOrPNG(layer)
|
||||
.catch(e => {
|
||||
console.error(`Image located at ${this.url} could not be parsed as a ${this.imgFormat} file. Is the imgFormat specified correct?`);
|
||||
return Promise.reject(e)
|
||||
})
|
||||
} else {
|
||||
// imgformat not defined we will try first supposing it is a fits file and then use the jpg heuristic
|
||||
promise = self._addFITS(layer)
|
||||
.catch(e => {
|
||||
return self._addJPGOrPNG(layer)
|
||||
.catch(e => {
|
||||
console.error(`Image located at ${self.url} could not be parsed as jpg/png/tif image file. Aborting...`)
|
||||
return Promise.reject(e);
|
||||
if (img.height !== obj.wcs.NAXIS2) {
|
||||
obj.wcs.NAXIS2 = img.height;
|
||||
}
|
||||
|
||||
self.options.wcs = obj.wcs;
|
||||
|
||||
img2Blob()
|
||||
} else {
|
||||
// no tags found
|
||||
reject('No WCS have been found in the image')
|
||||
return;
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
} else {
|
||||
img2Blob()
|
||||
}
|
||||
}
|
||||
|
||||
promise = promise.then((imageParams) => {
|
||||
self.formats = [self.imgFormat];
|
||||
let proxyUsed = false;
|
||||
img.onerror = (e) => {
|
||||
// use proxy
|
||||
if (proxyUsed) {
|
||||
console.error(e);
|
||||
|
||||
// There is at least one entry in imageParams
|
||||
self.added = true;
|
||||
self.setView(self.view);
|
||||
reject('Error parsing image located at: ' + self.url)
|
||||
return;
|
||||
}
|
||||
|
||||
// Set the automatic computed cuts
|
||||
let [minCut, maxCut] = self.getCuts();
|
||||
minCut = minCut || imageParams.min_cut;
|
||||
maxCut = maxCut || imageParams.max_cut;
|
||||
self.setCuts(
|
||||
minCut,
|
||||
maxCut
|
||||
);
|
||||
proxyUsed = true;
|
||||
img.src = Aladin.JSONP_PROXY + '?url=' + self.url;
|
||||
}
|
||||
})
|
||||
.then((readableStream) => {
|
||||
let wcs = self.options && self.options.wcs;
|
||||
wcs.NAXIS1 = wcs.NAXIS1 || img.width;
|
||||
wcs.NAXIS2 = wcs.NAXIS2 || img.height;
|
||||
|
||||
self.ra = imageParams.centered_fov.ra;
|
||||
self.dec = imageParams.centered_fov.dec;
|
||||
self.fov = imageParams.centered_fov.fov;
|
||||
return self.view.wasm
|
||||
.addImageWithWCS(
|
||||
readableStream,
|
||||
wcs,
|
||||
{
|
||||
...self.colorCfg.get(),
|
||||
longitudeReversed: this.longitudeReversed,
|
||||
imgFormat: 'jpeg',
|
||||
},
|
||||
layer
|
||||
)
|
||||
})
|
||||
.then((imageParams) => {
|
||||
self.imgFormat = 'jpeg';
|
||||
return Promise.resolve(imageParams);
|
||||
})
|
||||
.finally(() => {
|
||||
img.remove();
|
||||
});
|
||||
},
|
||||
|
||||
// Call the success callback on the first HDU image parsed
|
||||
if (self.successCallback) {
|
||||
self.successCallback(
|
||||
self.ra,
|
||||
self.dec,
|
||||
self.fov,
|
||||
self
|
||||
);
|
||||
add: function (layer) {
|
||||
this.layer = layer;
|
||||
|
||||
let self = this;
|
||||
let promise;
|
||||
|
||||
if (this.imgFormat === 'fits') {
|
||||
promise = this._addFITS(layer)
|
||||
.catch(e => {
|
||||
console.error(`Image located at ${this.url} could not be parsed as fits file. Is the imgFormat specified correct?`)
|
||||
return Promise.reject(e)
|
||||
})
|
||||
} else if (this.imgFormat === 'jpeg' || this.imgFormat === 'png') {
|
||||
promise = this._addJPGOrPNG(layer)
|
||||
.catch(e => {
|
||||
console.error(`Image located at ${this.url} could not be parsed as a ${this.imgFormat} file. Is the imgFormat specified correct?`);
|
||||
return Promise.reject(e)
|
||||
})
|
||||
} else {
|
||||
// imgformat not defined we will try first supposing it is a fits file and then use the jpg heuristic
|
||||
promise = self._addFITS(layer)
|
||||
.catch(e => {
|
||||
return self._addJPGOrPNG(layer)
|
||||
.catch(e => {
|
||||
console.error(`Image located at ${self.url} could not be parsed as jpg/png/tif image file. Aborting...`)
|
||||
return Promise.reject(e);
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
return self;
|
||||
})
|
||||
.catch((e) => {
|
||||
// This error result from a promise
|
||||
// If I throw it, it will not be catched because
|
||||
// it is run async
|
||||
self.view.removeImageLayer(layer);
|
||||
promise = promise.then((imageParams) => {
|
||||
self.formats = [self.imgFormat];
|
||||
|
||||
return Promise.reject(e);
|
||||
});
|
||||
// There is at least one entry in imageParams
|
||||
self.added = true;
|
||||
self.setView(self.view);
|
||||
|
||||
return promise;
|
||||
};
|
||||
// Set the automatic computed cuts
|
||||
let [minCut, maxCut] = self.getCuts();
|
||||
minCut = minCut || imageParams.min_cut;
|
||||
maxCut = maxCut || imageParams.max_cut;
|
||||
self.setCuts(
|
||||
minCut,
|
||||
maxCut
|
||||
);
|
||||
|
||||
// @api
|
||||
Image.prototype.toggle = function () {
|
||||
if (this.colorCfg.getOpacity() != 0.0) {
|
||||
this.colorCfg.setOpacity(0.0);
|
||||
} else {
|
||||
this.colorCfg.setOpacity(this.prevOpacity);
|
||||
}
|
||||
};
|
||||
self.ra = imageParams.centered_fov.ra;
|
||||
self.dec = imageParams.centered_fov.dec;
|
||||
self.fov = imageParams.centered_fov.fov;
|
||||
|
||||
// FITS images does not mean to be used for storing planetary data
|
||||
Image.prototype.isPlanetaryBody = function () {
|
||||
return false;
|
||||
};
|
||||
// Call the success callback on the first HDU image parsed
|
||||
if (self.successCallback) {
|
||||
self.successCallback(
|
||||
self.ra,
|
||||
self.dec,
|
||||
self.fov,
|
||||
self
|
||||
);
|
||||
}
|
||||
|
||||
// @api
|
||||
Image.prototype.focusOn = function () {
|
||||
// ensure the fits have been parsed
|
||||
if (this.added) {
|
||||
this.view.aladin.gotoRaDec(this.ra, this.dec);
|
||||
this.view.aladin.setFoV(this.fov);
|
||||
}
|
||||
};
|
||||
return self;
|
||||
})
|
||||
.catch((e) => {
|
||||
// This error result from a promise
|
||||
// If I throw it, it will not be catched because
|
||||
// it is run async
|
||||
self.view.removeImageLayer(layer);
|
||||
|
||||
// @oldapi
|
||||
Image.prototype.setAlpha = Image.prototype.setOpacity;
|
||||
return Promise.reject(e);
|
||||
});
|
||||
|
||||
Image.prototype.setColorCfg = function (colorCfg) {
|
||||
this._updateMetadata(() => {
|
||||
this.colorCfg = colorCfg;
|
||||
});
|
||||
};
|
||||
return promise;
|
||||
},
|
||||
|
||||
// @api
|
||||
Image.prototype.getColorCfg = function () {
|
||||
return this.colorCfg;
|
||||
};
|
||||
// FITS images does not mean to be used for storing planetary data
|
||||
isPlanetaryBody: function () {
|
||||
return false;
|
||||
},
|
||||
|
||||
// @api
|
||||
Image.prototype.getCuts = function () {
|
||||
return this.colorCfg.getCuts();
|
||||
};
|
||||
|
||||
// @api
|
||||
Image.prototype.getOpacity = function () {
|
||||
return this.colorCfg.getOpacity();
|
||||
};
|
||||
|
||||
Image.prototype.getAlpha = Image.prototype.getOpacity;
|
||||
|
||||
// @api
|
||||
Image.prototype.readPixel = function (x, y) {
|
||||
return this.view.wasm.readPixel(x, y, this.layer);
|
||||
// @api
|
||||
focusOn: function () {
|
||||
// ensure the fits have been parsed
|
||||
if (this.added) {
|
||||
this.view.aladin.gotoRaDec(this.ra, this.dec);
|
||||
this.view.aladin.setFoV(this.fov);
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
return Image;
|
||||
|
||||
@@ -1,872 +0,0 @@
|
||||
// Copyright 2013 - UDS/CNRS
|
||||
// The Aladin Lite program is distributed under the terms
|
||||
// of the GNU General Public License version 3.
|
||||
//
|
||||
// This file is part of Aladin Lite.
|
||||
//
|
||||
// Aladin Lite is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, version 3 of the License.
|
||||
//
|
||||
// Aladin Lite is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// The GNU General Public License is available in COPYING file
|
||||
// along with Aladin Lite.
|
||||
//
|
||||
|
||||
/******************************************************************************
|
||||
* Aladin Lite project
|
||||
*
|
||||
* File ImageHiPS
|
||||
*
|
||||
* Authors: Thomas Boch & Matthieu Baumann [CDS]
|
||||
*
|
||||
*****************************************************************************/
|
||||
import { Utils } from "./Utils";
|
||||
import { ALEvent } from "./events/ALEvent.js";
|
||||
import { ColorCfg } from "./ColorCfg.js";
|
||||
import { HiPSProperties } from "./HiPSProperties.js";
|
||||
|
||||
let PropertyParser = {};
|
||||
// Utilitary functions for parsing the properties and giving default values
|
||||
/// Mandatory tileSize property
|
||||
PropertyParser.tileSize = function (properties) {
|
||||
let tileSize =
|
||||
(properties &&
|
||||
properties.hips_tile_width &&
|
||||
+properties.hips_tile_width) ||
|
||||
512;
|
||||
|
||||
// Check if the tile width size is a power of 2
|
||||
if (tileSize & (tileSize - 1 !== 0)) {
|
||||
tileSize = 512;
|
||||
}
|
||||
|
||||
return tileSize;
|
||||
};
|
||||
|
||||
/// Mandatory frame property
|
||||
PropertyParser.cooFrame = function (properties) {
|
||||
let cooFrame =
|
||||
(properties && properties.hips_body && "ICRSd") ||
|
||||
(properties && properties.hips_frame) ||
|
||||
"ICRS";
|
||||
return cooFrame;
|
||||
};
|
||||
|
||||
/// Mandatory maxOrder property
|
||||
PropertyParser.maxOrder = function (properties) {
|
||||
let maxOrder =
|
||||
properties && properties.hips_order && +properties.hips_order;
|
||||
return maxOrder;
|
||||
};
|
||||
|
||||
/// Mandatory minOrder property
|
||||
PropertyParser.minOrder = function (properties) {
|
||||
const minOrder =
|
||||
(properties &&
|
||||
properties.hips_order_min &&
|
||||
+properties.hips_order_min) ||
|
||||
0;
|
||||
return minOrder;
|
||||
};
|
||||
|
||||
PropertyParser.formats = function (properties) {
|
||||
let formats = (properties && properties.hips_tile_format) || "jpeg";
|
||||
|
||||
formats = formats.split(" ").map((fmt) => fmt.toLowerCase());
|
||||
|
||||
return formats;
|
||||
};
|
||||
|
||||
PropertyParser.initialFov = function (properties) {
|
||||
let initialFov =
|
||||
properties &&
|
||||
properties.hips_initial_fov &&
|
||||
+properties.hips_initial_fov;
|
||||
|
||||
if (initialFov && initialFov < 0.00002777777) {
|
||||
initialFov = 360;
|
||||
}
|
||||
|
||||
return initialFov;
|
||||
};
|
||||
|
||||
PropertyParser.skyFraction = function (properties) {
|
||||
const skyFraction =
|
||||
(properties &&
|
||||
properties.moc_sky_fraction &&
|
||||
+properties.moc_sky_fraction) ||
|
||||
0.0;
|
||||
return skyFraction;
|
||||
};
|
||||
|
||||
PropertyParser.cutouts = function (properties) {
|
||||
let cuts =
|
||||
properties &&
|
||||
properties.hips_pixel_cut &&
|
||||
properties.hips_pixel_cut.split(" ");
|
||||
|
||||
const minCutout = cuts && parseFloat(cuts[0]);
|
||||
const maxCutout = cuts && parseFloat(cuts[1]);
|
||||
|
||||
return [minCutout, maxCutout];
|
||||
};
|
||||
|
||||
PropertyParser.bitpix = function (properties) {
|
||||
const bitpix =
|
||||
properties &&
|
||||
properties.hips_pixel_bitpix &&
|
||||
+properties.hips_pixel_bitpix;
|
||||
return bitpix;
|
||||
};
|
||||
|
||||
PropertyParser.isPlanetaryBody = function (properties) {
|
||||
return properties && properties.hips_body !== undefined;
|
||||
};
|
||||
|
||||
/**
|
||||
* @typedef {Object} ImageHiPSOptions
|
||||
*
|
||||
* @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
|
||||
* @property {string} [imgFormat] - Formats accepted 'webp', 'png', 'jpeg' or 'fits'. Will raise an error if the HiPS does not contain tiles in this format
|
||||
* @property {CooFrame} [cooFrame="J2000"] - Coordinate frame of the survey tiles
|
||||
* @property {number} [maxOrder] - The maximum HEALPix order of the HiPS, i.e the HEALPix order of the most refined tile images of the HiPS.
|
||||
* @property {number} [numBitsPerPixel] - Useful if you want to display the FITS tiles of a HiPS. It specifies the number of bits per pixel. Possible values are:
|
||||
* -64: double, -32: float, 8: unsigned byte, 16: short, 32: integer 32 bits, 64: integer 64 bits
|
||||
* @property {number} [tileSize] - The width of the HEALPix tile images. Mostly 512 pixels but can be 256, 128, 64, 32
|
||||
* @property {number} [minOrder] - If not given, retrieved from the properties of the survey.
|
||||
* @property {boolean} [longitudeReversed=false] - Set it to True for planetary survey visualization
|
||||
* @property {number} [opacity=1.0] - Opacity of the survey or image (value between 0 and 1).
|
||||
* @property {string} [colormap="native"] - The colormap configuration for the survey or image.
|
||||
* @property {string} [stretch="linear"] - The stretch configuration for the survey or image.
|
||||
* @property {boolean} [reversed=false] - If true, the colormap is reversed; otherwise, it is not reversed.
|
||||
* @property {number} [minCut] - The minimum cut value for the color configuration. If not given, 0.0 for JPEG/PNG surveys, the value of the property file for FITS surveys
|
||||
* @property {number} [maxCut] - The maximum cut value for the color configuration. If not given, 1.0 for JPEG/PNG surveys, the value of the property file for FITS surveys
|
||||
* @property {boolean} [additive=false] - If true, additive blending is applied; otherwise, it is not applied.
|
||||
* @property {number} [gamma=1.0] - The gamma correction value for the color configuration.
|
||||
* @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.
|
||||
*/
|
||||
export let ImageHiPS = (function () {
|
||||
/**
|
||||
* The object describing an image survey
|
||||
*
|
||||
* @class
|
||||
* @constructs ImageHiPS
|
||||
*
|
||||
* @param {string} id - Mandatory unique identifier for the layer. Can be an arbitrary name
|
||||
* @param {string} url - Can be an url to the survey or a "CDS" ID pointing towards a HiPS. One can found the list of IDs {@link https://aladin.cds.unistra.fr/hips/list| here}
|
||||
* @param {ImageHiPSOptions} [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.
|
||||
*/
|
||||
function ImageHiPS(id, url, options) {
|
||||
this.added = false;
|
||||
// Unique identifier for a survey
|
||||
this.id = id;
|
||||
this.name = (options && options.name) || undefined;
|
||||
this.url = url;
|
||||
this.maxOrder = options.maxOrder;
|
||||
this.minOrder = options.minOrder || 0;
|
||||
this.cooFrame = options.cooFrame;
|
||||
this.tileSize = options.tileSize;
|
||||
this.skyFraction = options.skyFraction;
|
||||
this.longitudeReversed =
|
||||
options.longitudeReversed === undefined
|
||||
? false
|
||||
: options.longitudeReversed;
|
||||
this.imgFormat = options.imgFormat;
|
||||
this.formats = options.formats;
|
||||
this.defaultFitsMinCut = options.defaultFitsMinCut;
|
||||
this.defaultFitsMaxCut = options.defaultFitsMaxCut;
|
||||
this.numBitsPerPixel = options.numBitsPerPixel;
|
||||
this.creatorDid = options.creatorDid;
|
||||
this.errorCallback = options.errorCallback;
|
||||
this.successCallback = options.successCallback;
|
||||
|
||||
this.colorCfg = new ColorCfg(options);
|
||||
}
|
||||
|
||||
ImageHiPS.prototype.setView = function (view) {
|
||||
let self = this;
|
||||
|
||||
// do not allow to call setView multiple times otherwise
|
||||
// the querying to the properties and the search to the best
|
||||
// HiPS node will be done again for the same imageHiPS
|
||||
if (this.view) {
|
||||
return;
|
||||
}
|
||||
this.view = view;
|
||||
|
||||
let isMOCServerToBeQueried = true;
|
||||
if (this.imgFormat === "fits") {
|
||||
// a fits is given
|
||||
isMOCServerToBeQueried = !(
|
||||
this.maxOrder &&
|
||||
this.url &&
|
||||
this.imgFormat &&
|
||||
this.tileSize &&
|
||||
this.cooFrame &&
|
||||
this.numBitsPerPixel
|
||||
);
|
||||
} else {
|
||||
isMOCServerToBeQueried = !(
|
||||
this.maxOrder &&
|
||||
this.url &&
|
||||
this.imgFormat &&
|
||||
this.tileSize &&
|
||||
this.cooFrame
|
||||
);
|
||||
}
|
||||
|
||||
self.query = (async () => {
|
||||
if (isMOCServerToBeQueried) {
|
||||
let isCDSId = false;
|
||||
|
||||
let properties = await HiPSProperties.fetchFromUrl(self.url)
|
||||
/*.catch((e) => {
|
||||
// try with the proxy
|
||||
url = Utils.handleCORSNotSameOrigin(url).href;
|
||||
|
||||
return HiPSProperties.fetchFromUrl(url);
|
||||
})*/
|
||||
.catch(async (e) => {
|
||||
// url not valid so we try with the id
|
||||
try {
|
||||
isCDSId = true;
|
||||
// the url stores a "CDS ID" we take it prioritaly
|
||||
// if the url is null, take the id, this is for some tests
|
||||
// to pass because some users might just give null as url param and a "CDS ID" as id param
|
||||
let id = self.url || self.id;
|
||||
return await HiPSProperties.fetchFromID(id);
|
||||
} catch (e) {
|
||||
throw e;
|
||||
}
|
||||
});
|
||||
|
||||
//obsTitle = properties.obs_title;
|
||||
self.creatorDid = properties.creator_did || self.creatorDid;
|
||||
// url
|
||||
|
||||
if (isCDSId) {
|
||||
self.url = properties.hips_service_url;
|
||||
if (!self.url) {
|
||||
throw "no valid service URL for retrieving the tiles";
|
||||
}
|
||||
|
||||
self.url = Utils.fixURLForHTTPS(self.url);
|
||||
|
||||
// Request all the properties to see which mirror is the fastest
|
||||
HiPSProperties.getFasterMirrorUrl(properties, self.url)
|
||||
.then((url) => {
|
||||
if (self.url !== url) {
|
||||
console.info(
|
||||
"Change url of ",
|
||||
self.id,
|
||||
" from ",
|
||||
self.url,
|
||||
" to ",
|
||||
url
|
||||
);
|
||||
|
||||
self.url = url;
|
||||
|
||||
// save the new url to the cache
|
||||
self._saveInCache();
|
||||
|
||||
// If added to the backend, then we need to tell it the url has changed
|
||||
if (self.added) {
|
||||
self.view.wasm.setHiPSUrl(
|
||||
self.creatorDid,
|
||||
url
|
||||
);
|
||||
}
|
||||
}
|
||||
})
|
||||
.catch((e) => {
|
||||
console.error(self);
|
||||
console.error(e);
|
||||
});
|
||||
}
|
||||
|
||||
// Max order
|
||||
self.maxOrder =
|
||||
PropertyParser.maxOrder(properties) || self.maxOrder;
|
||||
|
||||
// Tile size
|
||||
self.tileSize =
|
||||
PropertyParser.tileSize(properties) || self.tileSize;
|
||||
|
||||
// Tile formats
|
||||
self.formats =
|
||||
PropertyParser.formats(properties) || self.formats;
|
||||
|
||||
// min order
|
||||
self.minOrder =
|
||||
PropertyParser.minOrder(properties) || self.minOrder;
|
||||
|
||||
// Frame
|
||||
self.cooFrame =
|
||||
PropertyParser.cooFrame(properties) || self.cooFrame;
|
||||
|
||||
// sky fraction
|
||||
self.skyFraction = PropertyParser.skyFraction(properties);
|
||||
|
||||
// Initial fov/ra/dec
|
||||
self.initialFov = PropertyParser.initialFov(properties);
|
||||
self.initialRa =
|
||||
properties &&
|
||||
properties.hips_initial_ra &&
|
||||
+properties.hips_initial_ra;
|
||||
self.initialDec =
|
||||
properties &&
|
||||
properties.hips_initial_dec &&
|
||||
+properties.hips_initial_dec;
|
||||
|
||||
// Cutouts
|
||||
const cutoutFromProperties = PropertyParser.cutouts(properties);
|
||||
self.defaultFitsMinCut = cutoutFromProperties[0];
|
||||
self.defaultFitsMaxCut = cutoutFromProperties[1];
|
||||
|
||||
// Bitpix
|
||||
self.numBitsPerPixel =
|
||||
PropertyParser.bitpix(properties) || self.numBitsPerPixel;
|
||||
|
||||
// HiPS body
|
||||
if (properties.hips_body) {
|
||||
self.hipsBody = properties.hips_body;
|
||||
// Use the property to define and check some user given infos
|
||||
// Longitude reversed
|
||||
self.longitudeReversed = true;
|
||||
}
|
||||
|
||||
// Give a better name if we have the HiPS metadata
|
||||
self.name = self.name || properties.obs_title;
|
||||
}
|
||||
|
||||
self.name = self.name || self.id || self.url;
|
||||
self.name = self.name.replace(/ +/g, ' ');
|
||||
|
||||
self.creatorDid = self.creatorDid || self.id || self.url;
|
||||
|
||||
// Image format
|
||||
if (self.imgFormat) {
|
||||
// transform to lower case
|
||||
self.imgFormat = self.imgFormat.toLowerCase();
|
||||
// convert JPG -> JPEG
|
||||
if (self.imgFormat === "jpg") {
|
||||
self.imgFormat = "jpeg";
|
||||
}
|
||||
|
||||
// user wants a fits but the properties tells this format is not available
|
||||
if (
|
||||
self.imgFormat === "fits" &&
|
||||
self.formats &&
|
||||
self.formats.indexOf("fits") < 0
|
||||
) {
|
||||
throw self.name + " does not provide fits tiles";
|
||||
}
|
||||
|
||||
if (
|
||||
self.imgFormat === "webp" &&
|
||||
self.formats &&
|
||||
self.formats.indexOf("webp") < 0
|
||||
) {
|
||||
throw self.name + " does not provide webp tiles";
|
||||
}
|
||||
|
||||
if (
|
||||
self.imgFormat === "png" &&
|
||||
self.formats &&
|
||||
self.formats.indexOf("png") < 0
|
||||
) {
|
||||
throw self.name + " does not provide png tiles";
|
||||
}
|
||||
|
||||
if (
|
||||
self.imgFormat === "jpeg" &&
|
||||
self.formats &&
|
||||
self.formats.indexOf("jpeg") < 0
|
||||
) {
|
||||
throw self.name + " does not provide jpeg tiles";
|
||||
}
|
||||
} else {
|
||||
// user wants nothing then we choose one from the properties
|
||||
if (self.formats.indexOf("webp") >= 0) {
|
||||
self.imgFormat = "webp";
|
||||
} else if (self.formats.indexOf("png") >= 0) {
|
||||
self.imgFormat = "png";
|
||||
} else if (self.formats.indexOf("jpeg") >= 0) {
|
||||
self.imgFormat = "jpeg";
|
||||
} else if (self.formats.indexOf("fits") >= 0) {
|
||||
self.imgFormat = "fits";
|
||||
} else {
|
||||
throw (
|
||||
"Unsupported format(s) found in the properties: " +
|
||||
self.formats
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Cutouts
|
||||
let minCut, maxCut;
|
||||
if (self.imgFormat === "fits") {
|
||||
// Take into account the default cuts given by the property file (this is true especially for FITS HiPSes)
|
||||
minCut = self.colorCfg.minCut || self.defaultFitsMinCut || 0.0;
|
||||
maxCut = self.colorCfg.maxCut || self.defaultFitsMaxCut || 1.0;
|
||||
} else {
|
||||
minCut = self.colorCfg.minCut || 0.0;
|
||||
maxCut = self.colorCfg.maxCut || 1.0;
|
||||
}
|
||||
|
||||
self.colorCfg.setCuts(minCut, maxCut);
|
||||
|
||||
// Coo frame
|
||||
if (
|
||||
self.cooFrame == "ICRS" ||
|
||||
self.cooFrame == "ICRSd" ||
|
||||
self.cooFrame == "equatorial" ||
|
||||
self.cooFrame == "j2000"
|
||||
) {
|
||||
self.cooFrame = "ICRS";
|
||||
} else if (self.cooFrame == "galactic" || self.cooFrame == "GAL") {
|
||||
self.cooFrame = "GAL";
|
||||
} else {
|
||||
console.warn(
|
||||
"Invalid cooframe given: " +
|
||||
self.cooFrame +
|
||||
'. Coordinate systems supported: "ICRS", "ICRSd", "j2000" or "galactic". ICRS is chosen by default'
|
||||
);
|
||||
self.cooFrame = "ICRS";
|
||||
|
||||
}
|
||||
|
||||
self.formats = self.formats || [self.imgFormat];
|
||||
|
||||
self._saveInCache();
|
||||
|
||||
return self;
|
||||
})()
|
||||
};
|
||||
|
||||
/* Precondition: view is attached */
|
||||
ImageHiPS.prototype._saveInCache = function () {
|
||||
let self = this;
|
||||
/*let colorOpt = Object.fromEntries(Object.entries(this.colorCfg));
|
||||
let surveyOpt = {
|
||||
id: self.id,
|
||||
creatorDid: self.creatorDid,
|
||||
name: self.name,
|
||||
url: self.url,
|
||||
skyFraction: self.skyFraction,
|
||||
cooFrame: self.cooFrame,
|
||||
maxOrder: self.maxOrder,
|
||||
tileSize: self.tileSize,
|
||||
formats: self.formats,
|
||||
imgFormat: self.imgFormat,
|
||||
successCallback: self.successCallback,
|
||||
errorCallback: self.errorCallback,
|
||||
defaultFitsMinCut: self.defaultFitsMinCut,
|
||||
defaultFitsMaxCut: self.defaultFitsMaxCut,
|
||||
...colorOpt,
|
||||
};
|
||||
|
||||
if (self.numBitsPerPixel) {
|
||||
surveyOpt.numBitsPerPixel = self.numBitsPerPixel;
|
||||
}*/
|
||||
|
||||
let hipsCache = this.view.aladin.hipsCache;
|
||||
|
||||
|
||||
if (hipsCache.contains(self.id)) {
|
||||
/*HiPSCache.append(self.id, {
|
||||
// Erase by the cache already put values which is considered
|
||||
// as the ground truth
|
||||
...HiPSCache.get[self.id],
|
||||
// append new important infos from the properties queried
|
||||
...surveyOpt,
|
||||
});*/
|
||||
hipsCache.append(self.id, this)
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Checks if the ImageHiPS represents a planetary body.
|
||||
*
|
||||
* This method returns a boolean indicating whether the ImageHiPS corresponds to a planetary body, e.g. the earth or a celestial body.
|
||||
*
|
||||
* @memberof ImageHiPS
|
||||
*
|
||||
* @returns {boolean} Returns true if the ImageHiPS represents a planetary body; otherwise, returns false.
|
||||
*/
|
||||
ImageHiPS.prototype.isPlanetaryBody = function () {
|
||||
return this.hipsBody !== undefined;
|
||||
};
|
||||
|
||||
/**
|
||||
* Sets the image format for the ImageHiPS.
|
||||
*
|
||||
* This method updates the image format of the ImageHiPS, performs format validation, and triggers the update of metadata.
|
||||
*
|
||||
* @memberof ImageHiPS
|
||||
*
|
||||
* @param {string} format - The desired image format. Should be one of ["fits", "png", "jpg", "webp"].
|
||||
*
|
||||
* @throws {string} Throws an error if the provided format is not one of the supported formats or if the format is not available for the specific ImageHiPS.
|
||||
*/
|
||||
ImageHiPS.prototype.setImageFormat = function (format) {
|
||||
let self = this;
|
||||
self.query.then(() => {
|
||||
self._updateMetadata(() => {
|
||||
let imgFormat = format.toLowerCase();
|
||||
|
||||
if (
|
||||
imgFormat !== "fits" &&
|
||||
imgFormat !== "png" &&
|
||||
imgFormat !== "jpg" &&
|
||||
imgFormat !== "jpeg" &&
|
||||
imgFormat !== "webp"
|
||||
) {
|
||||
throw 'Formats must lie in ["fits", "png", "jpg", "webp"]';
|
||||
}
|
||||
|
||||
if (imgFormat === "jpg") {
|
||||
imgFormat = "jpeg";
|
||||
}
|
||||
|
||||
// Passed the check, we erase the image format with the new one
|
||||
// We do nothing if the imgFormat is the same
|
||||
if (self.imgFormat === imgFormat) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Check the properties to see if the given format is available among the list
|
||||
// If the properties have not been retrieved yet, it will be tested afterwards
|
||||
const availableFormats = self.formats;
|
||||
// user wants a fits but the metadata tells this format is not available
|
||||
if (
|
||||
imgFormat === "fits" &&
|
||||
availableFormats.indexOf("fits") < 0
|
||||
) {
|
||||
throw self.id + " does not provide fits tiles";
|
||||
}
|
||||
|
||||
if (
|
||||
imgFormat === "webp" &&
|
||||
availableFormats.indexOf("webp") < 0
|
||||
) {
|
||||
throw self.id + " does not provide webp tiles";
|
||||
}
|
||||
|
||||
if (
|
||||
imgFormat === "png" &&
|
||||
availableFormats.indexOf("png") < 0
|
||||
) {
|
||||
throw self.id + " does not provide png tiles";
|
||||
}
|
||||
|
||||
if (
|
||||
imgFormat === "jpeg" &&
|
||||
availableFormats.indexOf("jpeg") < 0
|
||||
) {
|
||||
throw self.id + " does not provide jpeg tiles";
|
||||
}
|
||||
|
||||
// Switch from png/webp/jpeg to fits
|
||||
if (
|
||||
(self.imgFormat === "png" ||
|
||||
self.imgFormat === "webp" ||
|
||||
self.imgFormat === "jpeg") &&
|
||||
imgFormat === "fits"
|
||||
) {
|
||||
if (Number.isFinite(self.defaultFitsMinCut) && Number.isFinite(self.defaultFitsMaxCut)) {
|
||||
// reset cuts to those given from the properties
|
||||
self.setCuts(self.defaultFitsMinCut, self.defaultFitsMaxCut);
|
||||
}
|
||||
// Switch from fits to png/webp/jpeg
|
||||
} else if (self.imgFormat === "fits") {
|
||||
self.setCuts(0.0, 1.0);
|
||||
}
|
||||
|
||||
// Check if it is a fits
|
||||
self.imgFormat = imgFormat;
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Sets the opacity factor when rendering the ImageHiPS
|
||||
*
|
||||
* @memberof ImageHiPS
|
||||
*
|
||||
* @returns {string[]} Returns the formats accepted for the survey, i.e. the formats of tiles that are availables. Could be PNG, WEBP, JPG and FITS.
|
||||
*/
|
||||
ImageHiPS.prototype.getAvailableFormats = function () {
|
||||
return this.formats;
|
||||
};
|
||||
|
||||
/**
|
||||
* Sets the opacity factor when rendering the ImageHiPS
|
||||
*
|
||||
* @memberof ImageHiPS
|
||||
*
|
||||
* @param {number} opacity - Opacity of the survey to set. Between 0 and 1
|
||||
*/
|
||||
ImageHiPS.prototype.setOpacity = function (opacity) {
|
||||
this._updateMetadata(() => {
|
||||
this.colorCfg.setOpacity(opacity);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Sets the blending mode when rendering the ImageHiPS
|
||||
*
|
||||
* @memberof ImageHiPS
|
||||
*
|
||||
* @param {boolean} [additive=false] -
|
||||
*
|
||||
* @description Two rendering modes are availables i.e. the default one and the additive one.
|
||||
* When rendering this survey on top of the already rendered ones, the final color of the screen is computed like:
|
||||
* <br>
|
||||
* <br>opacity * this_survey_color + (1 - opacity) * already_rendered_color for the default mode
|
||||
* <br>opacity * this_survey_color + already_rendered_color for the additive mode
|
||||
* <br>
|
||||
* <br>
|
||||
* Additive mode allows you to do linear survey color combination i.e. let's define 3 surveys named s1, s2, s3. Each could be associated to one color channel, i.e. s1 with red, s2 with green and s3 with the blue color channel.
|
||||
* If the additive blending mode is enabled, then the final pixel color of your screen will be: rgb = [s1_opacity * s1_color; s2_opacity * s2_color; s3_opacity * s3_color]
|
||||
*/
|
||||
ImageHiPS.prototype.setBlendingConfig = function (additive = false) {
|
||||
this._updateMetadata(() => {
|
||||
this.colorCfg.setBlendingConfig(additive);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Sets the colormap when rendering the ImageHiPS.
|
||||
*
|
||||
* @memberof ImageHiPS
|
||||
*
|
||||
* @param {string} [colormap="grayscale"] - The colormap label to use. See {@link https://matplotlib.org/stable/users/explain/colors/colormaps.html|here} for more info about colormaps.
|
||||
* Possible values are:
|
||||
* <br>"blues"
|
||||
* <br>"cividis"
|
||||
* <br>"cubehelix"
|
||||
* <br>"eosb"
|
||||
* <br>"grayscale"
|
||||
* <br>"inferno"
|
||||
* <br>"magma"
|
||||
* <br>"native"
|
||||
* <br>"parula"
|
||||
* <br>"plasma"
|
||||
* <br>"rainbow"
|
||||
* <br>"rdbu"
|
||||
* <br>"rdylbu"
|
||||
* <br>"redtemperature"
|
||||
* <br>"sinebow"
|
||||
* <br>"spectral"
|
||||
* <br>"summer"
|
||||
* <br>"viridis"
|
||||
* <br>"ylgnbu"
|
||||
* <br>"ylorbr"
|
||||
* <br>"red"
|
||||
* <br>"green"
|
||||
* <br>"blue"
|
||||
* @param {Object} [options] - Options for the colormap
|
||||
* @param {string} [options.stretch] - Stretching function of the colormap. Possible values are 'linear', 'asinh', 'log', 'sqrt', 'pow'. If no given, will not change it.
|
||||
* @param {boolean} [options.reversed=false] - Reverse the colormap axis.
|
||||
*/
|
||||
ImageHiPS.prototype.setColormap = function (colormap, options) {
|
||||
this._updateMetadata(() => {
|
||||
this.colorCfg.setColormap(colormap, options);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Sets the gamma correction factor for the ImageHiPS.
|
||||
*
|
||||
* This method updates the gamma of the ImageHiPS.
|
||||
*
|
||||
* @memberof ImageHiPS
|
||||
*
|
||||
* @param {number} lowCut - The low cut value to set for the ImageHiPS.
|
||||
* @param {number} highCut - The high cut value to set for the ImageHiPS.
|
||||
*/
|
||||
ImageHiPS.prototype.setCuts = function (lowCut, highCut) {
|
||||
this._updateMetadata(() => {
|
||||
this.colorCfg.setCuts(lowCut, highCut);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Sets the gamma correction factor for the ImageHiPS.
|
||||
*
|
||||
* This method updates the gamma of the ImageHiPS.
|
||||
*
|
||||
* @memberof ImageHiPS
|
||||
*
|
||||
* @param {number} gamma - The saturation value to set for the ImageHiPS. Between 0.1 and 10
|
||||
*/
|
||||
ImageHiPS.prototype.setGamma = function (gamma) {
|
||||
this._updateMetadata(() => {
|
||||
this.colorCfg.setGamma(gamma);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Sets the saturation for the ImageHiPS.
|
||||
*
|
||||
* This method updates the saturation of the ImageHiPS.
|
||||
*
|
||||
* @memberof ImageHiPS
|
||||
*
|
||||
* @param {number} saturation - The saturation value to set for the ImageHiPS. Between 0 and 1
|
||||
*/
|
||||
ImageHiPS.prototype.setSaturation = function (saturation) {
|
||||
this._updateMetadata(() => {
|
||||
this.colorCfg.setSaturation(saturation);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Sets the brightness for the ImageHiPS.
|
||||
*
|
||||
* This method updates the brightness of the ImageHiPS.
|
||||
*
|
||||
* @memberof ImageHiPS
|
||||
*
|
||||
* @param {number} brightness - The brightness value to set for the ImageHiPS. Between 0 and 1
|
||||
*/
|
||||
ImageHiPS.prototype.setBrightness = function (brightness) {
|
||||
this._updateMetadata(() => {
|
||||
this.colorCfg.setBrightness(brightness);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Sets the contrast for the ImageHiPS.
|
||||
*
|
||||
* This method updates the contrast of the ImageHiPS and triggers the update of metadata.
|
||||
*
|
||||
* @memberof ImageHiPS
|
||||
*
|
||||
* @param {number} contrast - The contrast value to set for the ImageHiPS. Between 0 and 1
|
||||
*/
|
||||
ImageHiPS.prototype.setContrast = function (contrast) {
|
||||
this._updateMetadata(() => {
|
||||
this.colorCfg.setContrast(contrast);
|
||||
});
|
||||
};
|
||||
|
||||
// Private method for updating the backend with the new meta
|
||||
ImageHiPS.prototype._updateMetadata = function (callback) {
|
||||
if (callback) {
|
||||
callback();
|
||||
}
|
||||
|
||||
// Tell the view its meta have changed
|
||||
try {
|
||||
if (this.added) {
|
||||
this.view.wasm.setImageMetadata(this.layer, {
|
||||
...this.colorCfg.get(),
|
||||
longitudeReversed: this.longitudeReversed,
|
||||
imgFormat: this.imgFormat,
|
||||
});
|
||||
// once the meta have been well parsed, we can set the meta
|
||||
ALEvent.HIPS_LAYER_CHANGED.dispatchedTo(this.view.aladinDiv, {
|
||||
layer: this,
|
||||
});
|
||||
}
|
||||
|
||||
// save it in the JS HiPS cache
|
||||
this._saveInCache();
|
||||
} catch (e) {
|
||||
// Display the error message
|
||||
console.error(e);
|
||||
}
|
||||
};
|
||||
|
||||
ImageHiPS.prototype.add = function (layer) {
|
||||
this.layer = layer;
|
||||
let self = this;
|
||||
|
||||
this.view.wasm.addImageHiPS({
|
||||
layer,
|
||||
properties: {
|
||||
creatorDid: self.creatorDid,
|
||||
url: self.url,
|
||||
maxOrder: self.maxOrder,
|
||||
cooFrame: self.cooFrame,
|
||||
tileSize: self.tileSize,
|
||||
formats: self.formats,
|
||||
bitpix: self.numBitsPerPixel,
|
||||
skyFraction: self.skyFraction,
|
||||
minOrder: self.minOrder,
|
||||
hipsInitialFov: self.initialFov,
|
||||
hipsInitialRa: self.initialRa,
|
||||
hipsInitialDec: self.initialDec,
|
||||
isPlanetaryBody: self.isPlanetaryBody(),
|
||||
hipsBody: self.hipsBody,
|
||||
},
|
||||
meta: {
|
||||
...this.colorCfg.get(),
|
||||
longitudeReversed: this.longitudeReversed,
|
||||
imgFormat: this.imgFormat,
|
||||
},
|
||||
});
|
||||
|
||||
return Promise.resolve(this)
|
||||
.then((hips) => {
|
||||
if (hips.successCallback) {
|
||||
hips.successCallback(hips)
|
||||
}
|
||||
|
||||
return hips
|
||||
});
|
||||
};
|
||||
|
||||
// @api
|
||||
ImageHiPS.prototype.toggle = function () {
|
||||
if (this.colorCfg.getOpacity() != 0.0) {
|
||||
this.colorCfg.setOpacity(0.0);
|
||||
} else {
|
||||
this.colorCfg.setOpacity(this.prevOpacity);
|
||||
}
|
||||
};
|
||||
|
||||
// @oldapi
|
||||
ImageHiPS.prototype.setAlpha = ImageHiPS.prototype.setOpacity;
|
||||
|
||||
ImageHiPS.prototype.setColorCfg = function (colorCfg) {
|
||||
this._updateMetadata(() => {
|
||||
this.colorCfg = colorCfg;
|
||||
});
|
||||
};
|
||||
|
||||
// @api
|
||||
ImageHiPS.prototype.getColorCfg = function () {
|
||||
return this.colorCfg;
|
||||
};
|
||||
|
||||
// @api
|
||||
ImageHiPS.prototype.getOpacity = function () {
|
||||
return this.colorCfg.getOpacity();
|
||||
};
|
||||
|
||||
ImageHiPS.prototype.getAlpha = ImageHiPS.prototype.getOpacity;
|
||||
|
||||
// @api
|
||||
ImageHiPS.prototype.readPixel = function (x, y) {
|
||||
return this.view.wasm.readPixel(x, y, this.layer);
|
||||
};
|
||||
|
||||
ImageHiPS.DEFAULT_SURVEY_ID = "CDS/P/DSS2/color";
|
||||
|
||||
return ImageHiPS;
|
||||
})();
|
||||
@@ -123,7 +123,7 @@ export let MOC = (function() {
|
||||
this.errorCallback = errorCallback;
|
||||
};
|
||||
|
||||
MOC.prototype.setView = function(view) {
|
||||
MOC.prototype.setView = function(view, idx) {
|
||||
let self = this;
|
||||
|
||||
this.view = view;
|
||||
@@ -134,7 +134,7 @@ export let MOC = (function() {
|
||||
if (data instanceof ArrayBuffer) {
|
||||
// from an url
|
||||
const buf = data;
|
||||
self.view.wasm.addFITSMoc(self.mocParams, new Uint8Array(buf));
|
||||
self.view.wasm.addFITSMOC(self.mocParams, new Uint8Array(buf));
|
||||
} else if(data.ra && data.dec && data.radius) {
|
||||
// circle
|
||||
const c = data;
|
||||
@@ -160,7 +160,7 @@ export let MOC = (function() {
|
||||
|
||||
// Add it to the view
|
||||
self.view.mocs.push(self);
|
||||
self.view.allOverlayLayers.push(self);
|
||||
self.view.insertOverlay(self, idx);
|
||||
|
||||
// Tell the MOC has been fully loaded and can be sent as an event
|
||||
ALEvent.GRAPHIC_OVERLAY_LAYER_ADDED.dispatchedTo(self.view.aladinDiv, {layer: self});
|
||||
|
||||
@@ -226,8 +226,12 @@ export let GraphicOverlay = (function() {
|
||||
}
|
||||
};
|
||||
|
||||
GraphicOverlay.prototype.setView = function(view) {
|
||||
GraphicOverlay.prototype.setView = function(view, idx) {
|
||||
console.trace()
|
||||
this.view = view;
|
||||
|
||||
this.view.overlays.push(this);
|
||||
this.view.insertOverlay(this, idx);
|
||||
};
|
||||
|
||||
GraphicOverlay.prototype.removeAll = function() {
|
||||
|
||||
@@ -216,10 +216,13 @@ export let ProgressiveCat = (function() {
|
||||
|
||||
ProgressiveCat.prototype = {
|
||||
|
||||
init: function(view) {
|
||||
setView: function(view, idx) {
|
||||
var self = this;
|
||||
this.view = view;
|
||||
|
||||
this.view.catalogs.push(this);
|
||||
this.view.insertOverlay(this, idx);
|
||||
|
||||
if (this.maxOrder && this.frameStr) {
|
||||
this._loadMetadata();
|
||||
}
|
||||
|
||||