diff --git a/assets/vignettes/American Astronomical Society 225 demonstration.png b/assets/vignettes/American Astronomical Society 225 demonstration.png new file mode 100644 index 00000000..0254a017 Binary files /dev/null and b/assets/vignettes/American Astronomical Society 225 demonstration.png differ diff --git a/assets/vignettes/Basic Aladin Lite instanciation.png b/assets/vignettes/Basic Aladin Lite instanciation.png new file mode 100644 index 00000000..8a470e53 Binary files /dev/null and b/assets/vignettes/Basic Aladin Lite instanciation.png differ diff --git a/assets/vignettes/Display proper motions.png b/assets/vignettes/Display proper motions.png new file mode 100644 index 00000000..22950f1b Binary files /dev/null and b/assets/vignettes/Display proper motions.png differ diff --git a/assets/vignettes/Load SIMBAD & NED catalogs data.png b/assets/vignettes/Load SIMBAD & NED catalogs data.png new file mode 100644 index 00000000..91624fcf Binary files /dev/null and b/assets/vignettes/Load SIMBAD & NED catalogs data.png differ diff --git a/assets/vignettes/Load a FITS image.png b/assets/vignettes/Load a FITS image.png new file mode 100644 index 00000000..39b08574 Binary files /dev/null and b/assets/vignettes/Load a FITS image.png differ diff --git a/assets/vignettes/Visualization of Mars.png b/assets/vignettes/Visualization of Mars.png new file mode 100644 index 00000000..73647b3c Binary files /dev/null and b/assets/vignettes/Visualization of Mars.png differ diff --git a/examples/al-adass2022.html b/examples/al-adass2022.html index 47ee230b..50561fd9 100644 --- a/examples/al-adass2022.html +++ b/examples/al-adass2022.html @@ -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"}); diff --git a/examples/al-moc-sdss9.html b/examples/al-moc-sdss9.html index fda81b89..0c96d336 100644 --- a/examples/al-moc-sdss9.html +++ b/examples/al-moc-sdss9.html @@ -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)); diff --git a/examples/al-multiple-surveys.html b/examples/al-multiple-surveys.html index 08a1c3d7..a3e4d36f 100644 --- a/examples/al-multiple-surveys.html +++ b/examples/al-multiple-surveys.html @@ -8,7 +8,7 @@ diff --git a/src/core/al-core/src/image/mod.rs b/src/core/al-core/src/image/mod.rs index fd9a616b..d699149e 100644 --- a/src/core/al-core/src/image/mod.rs +++ b/src/core/al-core/src/image/mod.rs @@ -232,7 +232,7 @@ where use crate::Abort; use std::sync::{Arc, Mutex}; -impl Image for Arc>> +/*impl Image for Arc>> where I: Image, { @@ -249,7 +249,7 @@ where Ok(()) } -} +}*/ #[cfg(feature = "webgl2")] use crate::image::format::{R16I, R32I, R64F, R8UI}; diff --git a/src/core/src/app.rs b/src/core/src/app.rs index d7fce4d3..b2a5033f 100644 --- a/src/core/src/app.rs +++ b/src/core/src/app.rs @@ -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 => (), + }; } } } diff --git a/src/core/src/renderable/hips/mod.rs b/src/core/src/renderable/hips/mod.rs index 959634e0..a79a9338 100644 --- a/src/core/src/renderable/hips/mod.rs +++ b/src/core/src/renderable/hips/mod.rs @@ -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( &mut self, cell: &HEALPixCell, - image: Option, + image: I, time_request: Time, ) -> Result<(), JsValue> { self.textures.push(&cell, image, time_request) diff --git a/src/core/src/survey/buffer.rs b/src/core/src/survey/buffer.rs index 8c60ec7b..e140d945 100644 --- a/src/core/src/survey/buffer.rs +++ b/src/core/src/survey/buffer.rs @@ -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( &mut self, cell: &HEALPixCell, - image: Option, + 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( cell: &HEALPixCell, texture: &Texture, - image: Option, + image: I, texture_array: &Texture2DArray, cfg: &mut HiPSConfig, ) -> Result<(), JsValue> { @@ -663,12 +663,9 @@ fn send_to_gpu( 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 { diff --git a/src/core/src/survey/texture.rs b/src/core/src/survey/texture.rs index 4f99fee4..94ca026c 100644 --- a/src/core/src/survey/texture.rs +++ b/src/core/src/survey/texture.rs @@ -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"), diff --git a/src/core/src/tile_fetcher.rs b/src/core/src/tile_fetcher.rs index 1eb4fc47..7dd6912a 100644 --- a/src/core/src/tile_fetcher.rs +++ b/src/core/src/tile_fetcher.rs @@ -23,34 +23,64 @@ pub struct TileFetcherQueue { #[derive(Debug)] #[wasm_bindgen] pub struct HiPSLocalFiles { - paths: Box<[HashMap]>, + tiles: Box<[Box<[HashMap]>; 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 { - 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(), )); diff --git a/src/js/HiPS.js b/src/js/HiPS.js index 095e86dc..52d0d1c7 100644 --- a/src/js/HiPS.js +++ b/src/js/HiPS.js @@ -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) + } } } } diff --git a/src/js/View.js b/src/js/View.js index 76a460f2..d9539ca9 100644 --- a/src/js/View.js +++ b/src/js/View.js @@ -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); };