use Moc.fits when loading a local HiPS + add readme example vignettes

This commit is contained in:
Matthieu Baumann
2024-09-24 14:29:32 +02:00
parent 2395cb9425
commit 61f9af1415
17 changed files with 188 additions and 119 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 962 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 MiB

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -201,27 +201,38 @@ export let HiPS = (function () {
this.startUrl = options.startUrl;
if (location instanceof FileList) {
let files = {};
let localFiles = {};
for (var file of location) {
let path = file.webkitRelativePath;
if (path.includes("Norder") && path.includes("Npix")) {
const order = +path.substring(path.indexOf("Norder") + 6).split("/")[0];
if (!files[order]) {
files[order] = {}
if (!localFiles[order]) {
localFiles[order] = {}
}
const ipix = +path.substring(path.indexOf("Npix") + 4).split(".")[0];
files[order][ipix] = file;
let tile = path.substring(path.indexOf("Npix") + 4).split(".");
const ipix = +tile[0];
const fmt = tile[1];
if (!localFiles[order][ipix]) {
localFiles[order][ipix] = {}
}
localFiles[order][ipix][fmt] = file;
}
if (path.includes("properties")) {
files['properties'] = file;
localFiles['properties'] = file;
}
if (path.includes("Moc")) {
localFiles['moc'] = file;
}
}
this.files = files;
this.localFiles = localFiles;
} else if (location instanceof Object) {
this.files = location;
this.localFiles = location;
}
this.url = location;
@@ -448,17 +459,17 @@ export let HiPS = (function () {
}
this.view = view;
if (this.files) {
if (this.localFiles) {
// Fetch the properties file
self.query = (async () => {
// look for the properties file
await HiPSProperties.fetchFromFile(self.files["properties"])
await HiPSProperties.fetchFromFile(self.localFiles["properties"])
.then((p) => {
self._parseProperties(p);
self.url = "local";
delete self.files["properties"]
delete self.localFiles["properties"]
})
return self;
@@ -887,12 +898,27 @@ export let HiPS = (function () {
};
let localFiles;
if (this.files) {
localFiles = new Aladin.wasmLibs.core.HiPSLocalFiles();
for (var order in this.files) {
for (var ipix in this.files[order]) {
const file = this.files[order][ipix];
localFiles.insert(+order, BigInt(+ipix), file)
if (this.localFiles) {
localFiles = new Aladin.wasmLibs.core.HiPSLocalFiles(this.localFiles["moc"]);
let fmt;
for (var order in this.localFiles) {
if (order === "moc")
continue;
for (var ipix in this.localFiles[order]) {
for (var f in this.localFiles[order][ipix]) {
if (f === "png") {
fmt = Aladin.wasmLibs.core.ImageExt.Png;
} else if (f === "fits") {
fmt = Aladin.wasmLibs.core.ImageExt.Fits;
} else {
fmt = Aladin.wasmLibs.core.ImageExt.Jpeg;
}
const tileFile = this.localFiles[order][+ipix][f];
localFiles.insert(+order, BigInt(+ipix), fmt, tileFile)
}
}
}
}

View File

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