From 66d3b8dcce39b07b8b383499b7b2301e58ed2739 Mon Sep 17 00:00:00 2001 From: Matthieu Baumann Date: Tue, 1 Jul 2025 20:07:14 +0200 Subject: [PATCH] first HiPS3D commit --- examples/al-displayFITS.html | 1 + src/core/Cargo.toml | 2 +- src/core/al-api/src/hips.rs | 30 ++++ src/core/src/app.rs | 66 +++---- src/core/src/camera/mod.rs | 10 +- src/core/src/camera/view_hpx_cells.rs | 16 +- src/core/src/camera/viewport.rs | 4 +- src/core/src/downloader/query.rs | 38 ++++- src/core/src/downloader/request/moc.rs | 61 +++---- src/core/src/downloader/request/mod.rs | 7 +- src/core/src/downloader/request/tile.rs | 8 +- src/core/src/healpix/moc/freq_space.rs | 161 ++++++++++++++++++ src/core/src/healpix/moc/mod.rs | 10 ++ .../src/healpix/{coverage.rs => moc/space.rs} | 86 ++++++++-- src/core/src/healpix/mod.rs | 2 +- src/core/src/lib.rs | 47 +---- src/core/src/math/mod.rs | 2 + src/core/src/math/spectra.rs | 62 +++++++ src/core/src/renderable/hips/config.rs | 35 +++- src/core/src/renderable/hips/d2/mod.rs | 27 ++- .../renderable/hips/d3/{buffer.rs => cube.rs} | 10 +- src/core/src/renderable/hips/d3/mod.rs | 99 +++++------ src/core/src/renderable/hips/mod.rs | 8 +- src/core/src/renderable/moc/hierarchy.rs | 14 +- src/core/src/renderable/moc/mod.rs | 12 +- src/core/src/renderable/moc/renderer.rs | 6 +- src/core/src/renderable/mod.rs | 11 +- src/core/src/tile_fetcher.rs | 53 +++--- src/js/HiPS.js | 16 +- 29 files changed, 603 insertions(+), 301 deletions(-) create mode 100644 src/core/src/healpix/moc/freq_space.rs create mode 100644 src/core/src/healpix/moc/mod.rs rename src/core/src/healpix/{coverage.rs => moc/space.rs} (54%) create mode 100644 src/core/src/math/spectra.rs rename src/core/src/renderable/hips/d3/{buffer.rs => cube.rs} (96%) diff --git a/examples/al-displayFITS.html b/examples/al-displayFITS.html index 06cac6f6..09c7a235 100644 --- a/examples/al-displayFITS.html +++ b/examples/al-displayFITS.html @@ -16,6 +16,7 @@ aladin.displayFITS( //'https://fits.gsfc.nasa.gov/samples/FOCx38i0101t_c0f.fits', // url of the fits file 'data/fits/panstarrs-g-m61.fits', + //'https://almascience.eso.org/dataPortal/member.uid___A001_X88f_X297.calibrated_final_cont_Sgr_B1off.pbcor.fits', { name: 'm61', colormap: 'viridis' diff --git a/src/core/Cargo.toml b/src/core/Cargo.toml index 237e5420..578bd89a 100644 --- a/src/core/Cargo.toml +++ b/src/core/Cargo.toml @@ -50,7 +50,7 @@ version = "0.7.3" [dependencies.moclib] package = "moc" -version = "0.17.0" +version = "0.18.0" [dependencies.serde] version = "^1.0.183" diff --git a/src/core/al-api/src/hips.rs b/src/core/al-api/src/hips.rs index e5eceb86..3c3b9148 100644 --- a/src/core/al-api/src/hips.rs +++ b/src/core/al-api/src/hips.rs @@ -48,14 +48,21 @@ pub struct HiPSProperties { hips_initial_fov: Option, hips_initial_ra: Option, hips_initial_dec: Option, + // HiPS cube hips_cube_depth: Option, + // HiPS 3D keywords + hips_order_freq: Option, + hips_tile_depth: Option, + // Parametrable by the user #[allow(unused)] min_cutout: Option, #[allow(unused)] max_cutout: Option, + dataproduct_type: Option, + creator_did: String, request_credentials: String, @@ -63,6 +70,20 @@ pub struct HiPSProperties { } impl HiPSProperties { + #[inline(always)] + pub fn get_hips_order_freq(&self) -> Option { + self.hips_order_freq + } + #[inline(always)] + pub fn get_hips_tile_depth(&self) -> Option { + self.hips_tile_depth + } + + #[inline(always)] + pub fn get_dataproduct_type(&self) -> Option { + self.dataproduct_type + } + #[inline(always)] pub fn get_url(&self) -> &str { &self.url @@ -149,6 +170,15 @@ pub enum ImageExt { Webp, } +#[derive(Deserialize, Debug, Clone, Copy, PartialEq, Eq, Hash)] +#[wasm_bindgen] +#[serde(rename_all = "camelCase")] +pub enum DataproductType { + SpectralCube, + Image, + Cube, +} + impl std::fmt::Display for ImageExt { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { match self { diff --git a/src/core/src/app.rs b/src/core/src/app.rs index 8d14d582..8a9f88e4 100644 --- a/src/core/src/app.rs +++ b/src/core/src/app.rs @@ -1,17 +1,13 @@ +use crate::math::angle::ToAngle; +use crate::math::spectra::Freq; +use crate::renderable::hips::HiPS; use crate::renderable::image::Image; use crate::renderable::ImageLayer; use crate::tile_fetcher::HiPSLocalFiles; -use al_core::image::fits::FitsImage; -use al_core::image::ImageType; -use fitsrs::WCS; -use std::io::Cursor; - -use crate::math::angle::ToAngle; -use crate::renderable::hips::HiPS; use crate::{ camera::CameraViewPort, downloader::Downloader, - healpix::coverage::HEALPixCoverage, + healpix::moc::SpaceMoc, inertia::Inertia, math::{ self, @@ -26,6 +22,10 @@ use crate::{ time::DeltaTime, }; use al_api::moc::MOCOptions; +use al_core::image::fits::FitsImage; +use al_core::image::ImageType; +use fitsrs::WCS; +use std::io::Cursor; use wasm_bindgen::prelude::*; @@ -39,6 +39,8 @@ use al_api::{ hips::{HiPSCfg, ImageMetadata}, }; +use crate::healpix::moc::Moc; + use web_sys::{HtmlElement, WebGl2RenderingContext}; use std::cell::RefCell; @@ -261,13 +263,16 @@ impl App { // Loop over the hipss for hips in self.layers.get_mut_hipses() { if self.camera.get_tile_depth() == 0 { - let allsky_query = match hips { - HiPS::D2(h) => query::Allsky::new(h.get_config(), None), - HiPS::D3(h) => query::Allsky::new(h.get_config(), Some(h.get_slice() as u32)), - }; - if self.downloader.borrow().is_queried(&allsky_query.id) { - // do not ask for tiles if we download the allsky - continue; + match hips { + HiPS::D2(h) => { + let query = query::Allsky::new(h.get_config(), None); + if self.downloader.borrow().is_queried(&query.id) { + // do not ask for tiles if we download the allsky + continue; + } + } + // no Allsky generated for HiPS3D + HiPS::D3(h) => (), } } @@ -301,9 +306,9 @@ impl App { } } HiPS::D3(hips) => { - let slice = hips.get_slice(); + let freq = hips.get_freq(); for ancestor in ancestors { - if !hips.contains_tile(&ancestor, slice) { + if !hips.contains_tile(&ancestor, freq) { self.tile_fetcher.append(hips.get_tile_query(&ancestor)); } } @@ -475,15 +480,11 @@ impl App { self.catalog_loaded } - pub(crate) fn get_moc(&self, moc_uuid: &str) -> Option<&HEALPixCoverage> { + pub(crate) fn get_moc(&self, moc_uuid: &str) -> Option<&SpaceMoc> { self.moc.get_hpx_coverage(moc_uuid) } - pub(crate) fn add_moc( - &mut self, - moc: HEALPixCoverage, - options: MOCOptions, - ) -> Result<(), JsValue> { + pub(crate) fn add_moc(&mut self, moc: SpaceMoc, options: MOCOptions) -> Result<(), JsValue> { self.moc .push_back(moc, options, &mut self.camera, &self.projection); self.request_redraw = true; @@ -678,14 +679,21 @@ impl App { } } } - Resource::Moc(moc) => { - let moc_hips_cdid = moc.get_hips_cdid(); + Resource::Moc(fetched_moc) => { + let moc_hips_cdid = fetched_moc.get_hips_cdid(); //let url = &moc_url[..moc_url.find("/Moc.fits").unwrap_abort()]; if let Some(hips) = self.layers.get_mut_hips_from_cdid(moc_hips_cdid) { - let request::moc::Moc { moc, .. } = moc; - + let request::moc::FetchedMoc { moc, .. } = fetched_moc; if let Some(moc) = &*moc.borrow() { - hips.set_moc(moc.clone()); + match (hips, moc) { + (HiPS::D2(hips), Moc::Space(moc)) => { + hips.set_moc(moc.clone()); + } + (HiPS::D3(hips), Moc::FreqSpace(moc)) => { + hips.set_moc(moc.clone()); + } + _ => (), + } self.request_for_new_tiles = true; self.request_redraw = true; @@ -1053,7 +1061,7 @@ impl App { match hips { HiPS::D2(_) => Err(JsValue::from_str("layer do not refers to a cube")), HiPS::D3(hips) => { - hips.set_slice(slice as u16); + hips.set_freq(Freq(slice as f64)); Ok(()) } diff --git a/src/core/src/camera/mod.rs b/src/core/src/camera/mod.rs index c8a800a4..41c35dbb 100644 --- a/src/core/src/camera/mod.rs +++ b/src/core/src/camera/mod.rs @@ -8,8 +8,8 @@ pub use fov::FieldOfView; pub mod view_hpx_cells; use crate::CooSystem; -use crate::HEALPixCoverage; use crate::ProjectionType; +use crate::SpaceMoc; pub fn build_fov_coverage( depth: u8, @@ -18,7 +18,7 @@ pub fn build_fov_coverage( camera_frame: CooSystem, frame: CooSystem, proj: &ProjectionType, -) -> HEALPixCoverage { +) -> SpaceMoc { if let Some(vertices) = fov.get_vertices() { // The vertices coming from the camera are in a specific coo sys // but cdshealpix accepts them to be given in ICRS coo sys @@ -44,20 +44,20 @@ pub fn build_fov_coverage( ::healpix::nested::hash(depth, lon.to_radians(), lat.to_radians()) }); - HEALPixCoverage::from_fixed_hpx_cells(depth, hpx_idxs_iter, Some(vertices.len())) + SpaceMoc::from_fixed_hpx_cells(depth, hpx_idxs_iter, Some(vertices.len())) } else { // The polygon is not too small for the depth asked let inside_vertex = crate::coosys::apply_coo_system(camera_frame, frame, camera_center); // Prefer to query from_polygon with depth >= 2 - HEALPixCoverage::from_3d_coos(depth, vertices_iter, &inside_vertex) + SpaceMoc::from_3d_coos(depth, vertices_iter, &inside_vertex) } } else { let center_xyz = crate::coosys::apply_coo_system(camera_frame, frame, camera_center); let biggest_fov_rad = proj.aperture_start().to_radians(); let lonlat = center_xyz.lonlat(); - HEALPixCoverage::from_cone(&lonlat, biggest_fov_rad * 0.5, depth) + SpaceMoc::from_cone(&lonlat, biggest_fov_rad * 0.5, depth) } } diff --git a/src/core/src/camera/view_hpx_cells.rs b/src/core/src/camera/view_hpx_cells.rs index 7bf546f7..0b587dbc 100644 --- a/src/core/src/camera/view_hpx_cells.rs +++ b/src/core/src/camera/view_hpx_cells.rs @@ -3,7 +3,7 @@ use crate::healpix::cell::HEALPixCell; use crate::math::projection::*; -use crate::HEALPixCoverage; +use crate::SpaceMoc; use moclib::moc::{range::op::degrade::degrade, RangeMOCIterator}; @@ -84,7 +84,7 @@ impl ViewHpxCells { self.hpx_cells[frame as usize].get_cells(depth) } - pub(super) fn get_cov(&self, frame: CooSystem) -> &HEALPixCoverage { + pub(super) fn get_cov(&self, frame: CooSystem) -> &SpaceMoc { self.hpx_cells[frame as usize].get_cov() } @@ -109,7 +109,7 @@ pub struct HpxCells { // An index vector referring to the indices of each depth cells //idx_rng: [Option>; MAX_HPX_DEPTH as usize + 1], // Coverage created in the frame - cov: HEALPixCoverage, + cov: SpaceMoc, // boolean refering to if the cells in the view has changed //new_cells: bool, } @@ -127,7 +127,7 @@ use super::FieldOfView; impl HpxCells { pub fn new(frame: CooSystem) -> Self { //let cells = Vec::new(); - let cov = HEALPixCoverage::empty(29); + let cov = SpaceMoc::empty(29); //let idx_rng = Default::default(); @@ -203,7 +203,7 @@ impl HpxCells { if depth == cov_depth { self.cov .flatten_to_fixed_depth_cells() - .map(move |idx| HEALPixCell(depth, idx)) + .map(|idx| HEALPixCell(depth, idx)) .collect() } else if depth > self.cov.depth_max() { let cov_d = self.cov.depth_max(); @@ -212,7 +212,7 @@ impl HpxCells { self.cov .flatten_to_fixed_depth_cells() - .flat_map(move |idx| { + .flat_map(|idx| { // idx is at depth_max HEALPixCell(cov_d, idx).get_children_cells(dd) }) @@ -221,7 +221,7 @@ impl HpxCells { // compute the cells from the coverage degrade((&self.cov.0).into_range_moc_iter(), depth) .flatten_to_fixed_depth_cells() - .map(move |idx| HEALPixCell(depth, idx)) + .map(|idx| HEALPixCell(depth, idx)) .collect() } } @@ -257,7 +257,7 @@ impl HpxCells { }*/ #[inline(always)] - pub fn get_cov(&self) -> &HEALPixCoverage { + pub fn get_cov(&self) -> &SpaceMoc { &self.cov } diff --git a/src/core/src/camera/viewport.rs b/src/core/src/camera/viewport.rs index 34887068..95134637 100644 --- a/src/core/src/camera/viewport.rs +++ b/src/core/src/camera/viewport.rs @@ -12,7 +12,7 @@ const ID_R: &Matrix3 = &Matrix3::new(-1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0. use super::{fov::FieldOfView, view_hpx_cells::ViewHpxCells}; use crate::healpix::cell::HEALPixCell; -use crate::healpix::coverage::HEALPixCoverage; +use crate::healpix::moc::SpaceMoc; use crate::math::angle::ToAngle; use crate::math::{projection::coo_space::XYZModel, projection::domain::sdf::ProjDef}; use cgmath::{InnerSpace, Vector3}; @@ -216,7 +216,7 @@ impl CameraViewPort { self.view_hpx_cells.has_changed() }*/ - pub fn get_cov(&self, frame: CooSystem) -> &HEALPixCoverage { + pub fn get_cov(&self, frame: CooSystem) -> &SpaceMoc { self.view_hpx_cells.get_cov(frame) } diff --git a/src/core/src/downloader/query.rs b/src/core/src/downloader/query.rs index badef43b..79cbffb0 100644 --- a/src/core/src/downloader/query.rs +++ b/src/core/src/downloader/query.rs @@ -9,6 +9,7 @@ pub trait Query: Sized { pub type QueryId = String; +use al_api::hips::DataproductType; use al_core::image::format::ImageFormatType; #[derive(Eq, PartialEq, Clone)] @@ -26,9 +27,14 @@ pub struct Tile { pub channel: Option, } +/*pub enum { + +}*/ + use crate::healpix::cell::HEALPixCell; use crate::renderable::hips::config::HiPSConfig; use crate::renderable::CreatorDid; +use crate::tile_fetcher::HiPSLocalFiles; use web_sys::{RequestCredentials, RequestMode}; impl Tile { pub fn new(cell: &HEALPixCell, channel: Option, cfg: &HiPSConfig) -> Self { @@ -65,7 +71,7 @@ impl Tile { ext ); - let size = cfg.get_tile_size(); + let size = cfg.get_tile_size() as u32; Tile { hips_cdid: hips_cdid.to_string(), url, @@ -75,7 +81,7 @@ impl Tile { mode, id, channel, - size: size as u32, + size, } } } @@ -168,21 +174,41 @@ pub struct Moc { pub credentials: RequestCredentials, pub params: MOCOptions, pub hips_cdid: CreatorDid, + pub dataproduct_type: DataproductType, } +use std::collections::HashMap; impl Moc { pub fn new( - url: String, - mode: RequestMode, - credentials: RequestCredentials, - hips_cdid: CreatorDid, + cfg: &HiPSConfig, + hips_local_files: &HashMap, params: MOCOptions, ) -> Self { + // Try to fetch the MOC + let hips_cdid = cfg.get_creator_did(); + let url = if let Some(local_hips) = 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()) + }; + + let mode = cfg.get_request_mode(); + let credentials = cfg.get_request_credentials(); + let hips_cdid = cfg.get_creator_did().to_string(); + let dataproduct_type = cfg.dataproduct_type; + Moc { url, params, hips_cdid, mode, credentials, + dataproduct_type, } } } diff --git a/src/core/src/downloader/request/moc.rs b/src/core/src/downloader/request/moc.rs index c5deb4b4..76247d2b 100644 --- a/src/core/src/downloader/request/moc.rs +++ b/src/core/src/downloader/request/moc.rs @@ -3,7 +3,9 @@ use crate::renderable::CreatorDid; use super::{Request, RequestType}; -use crate::healpix::coverage::Smoc; +use crate::healpix::moc::Moc; +use crate::healpix::moc::{FreqSpaceMoc, SpaceMoc}; +use al_api::hips::DataproductType; use moclib::deser::fits::MocType; use moclib::qty::Hpx; @@ -11,7 +13,7 @@ pub struct MOCRequest { //pub id: QueryId, pub hips_cdid: CreatorDid, pub params: MOCOptions, - request: Request, + request: Request, } impl From for RequestType { @@ -21,31 +23,13 @@ impl From for RequestType { } use super::Url; -use moclib::deser::fits; use wasm_bindgen::JsCast; use wasm_bindgen_futures::JsFuture; use web_sys::{RequestInit, Response}; -use moclib::moc::range::op::convert::convert_to_u64; - -/// Convenient type for Space-MOCs -pub fn from_fits_hpx(moc: MocType, Cursor<&[u8]>>) -> Smoc { - match moc { - MocType::Ranges(moc) => convert_to_u64::, _, Hpx>(moc).into_range_moc(), - MocType::Cells(moc) => { - convert_to_u64::, _, Hpx>(moc.into_cell_moc_iter().ranges()) - .into_range_moc() - } - } -} - -use crate::healpix::coverage::HEALPixCoverage; use crate::Abort; use al_api::moc::MOCOptions; -use moclib::deser::fits::MocIdxType; -use moclib::deser::fits::MocQtyType; -use moclib::idx::Idx; -use moclib::moc::{CellMOCIntoIterator, CellMOCIterator, RangeMOCIterator}; + use std::io::Cursor; use wasm_bindgen::JsValue; impl From for MOCRequest { @@ -57,6 +41,7 @@ impl From for MOCRequest { hips_cdid, credentials, mode, + dataproduct_type, } = query; let url_clone = url.clone(); @@ -75,22 +60,16 @@ impl From for MOCRequest { let resp: Response = resp_value.dyn_into()?; let array_buffer = JsFuture::from(resp.array_buffer()?).await?; - let bytes_buf = js_sys::Uint8Array::new(&array_buffer); - let num_bytes = bytes_buf.length() as usize; - let mut bytes = vec![0; num_bytes]; - bytes_buf.copy_to(&mut bytes[..]); + let buf = js_sys::Uint8Array::new(&array_buffer); + let bytes = buf.to_vec(); // Coosys is permissive because we load a moc - let smoc = match fits::from_fits_ivoa_custom(Cursor::new(&bytes[..]), true) - .map_err(|e| JsValue::from_str(&e.to_string()))? - { - MocIdxType::U16(MocQtyType::::Hpx(moc)) => Ok(from_fits_hpx(moc)), - MocIdxType::U32(MocQtyType::::Hpx(moc)) => Ok(from_fits_hpx(moc)), - MocIdxType::U64(MocQtyType::::Hpx(moc)) => Ok(from_fits_hpx(moc)), - _ => Err(JsValue::from_str("MOC not supported. Must be a HPX MOC")), - }?; - - Ok(HEALPixCoverage(smoc)) + Ok(match dataproduct_type { + DataproductType::SpectralCube => { + Moc::FreqSpace(FreqSpaceMoc::from_fits_raw_bytes(&bytes)?) + } + _ => Moc::Space(SpaceMoc::from_fits_raw_bytes(&bytes)?), + }) }); Self { @@ -105,19 +84,19 @@ impl From for MOCRequest { use std::cell::RefCell; use std::rc::Rc; -pub struct Moc { - pub moc: Rc>>, +pub struct FetchedMoc { + pub moc: Rc>>, pub params: MOCOptions, pub hips_cdid: Url, } -impl Moc { +impl FetchedMoc { pub fn get_hips_cdid(&self) -> &Url { &self.hips_cdid } } -impl<'a> From<&'a MOCRequest> for Option { +impl<'a> From<&'a MOCRequest> for Option { fn from(request: &'a MOCRequest) -> Self { let MOCRequest { request, @@ -126,8 +105,8 @@ impl<'a> From<&'a MOCRequest> for Option { .. } = request; if request.is_resolved() { - let Request:: { data, .. } = request; - Some(Moc { + let Request:: { data, .. } = request; + Some(FetchedMoc { // This is a clone on a Arc, it is supposed to be fast moc: data.clone(), hips_cdid: hips_cdid.clone(), diff --git a/src/core/src/downloader/request/mod.rs b/src/core/src/downloader/request/mod.rs index 1f0d83ae..39dddfc6 100644 --- a/src/core/src/downloader/request/mod.rs +++ b/src/core/src/downloader/request/mod.rs @@ -102,22 +102,23 @@ impl<'a> From<&'a RequestType> for Option { match request { RequestType::Tile(request) => Option::::from(request).map(Resource::Tile), RequestType::Allsky(request) => Option::::from(request).map(Resource::Allsky), - RequestType::Moc(request) => Option::::from(request).map(Resource::Moc), + RequestType::Moc(request) => Option::::from(request).map(Resource::Moc), } } } use crate::Abort; use allsky::Allsky; -use moc::Moc; use tile::Tile; pub enum Resource { Tile(Tile), Allsky(Allsky), - Moc(Moc), + Moc(FetchedMoc), } use web_sys::RequestCredentials; + +use self::moc::FetchedMoc; async fn query_html_image( url: &str, credentials: RequestCredentials, diff --git a/src/core/src/downloader/request/tile.rs b/src/core/src/downloader/request/tile.rs index 95c40575..ccbc4648 100644 --- a/src/core/src/downloader/request/tile.rs +++ b/src/core/src/downloader/request/tile.rs @@ -43,15 +43,15 @@ impl From for TileRequest { credentials, mode, id, - channel: slice, + channel, size, } = query; let url_clone = url.clone(); - let channel = format.get_pixel_format(); + let pixel_format = format.get_pixel_format(); let window = web_sys::window().unwrap_abort(); - let request = match channel { + let request = match pixel_format { PixelType::RGB8U => Request::new(async move { // HTMLImageElement let image = query_html_image(&url_clone, credentials).await?; @@ -113,7 +113,7 @@ impl From for TileRequest { hips_cdid, url, request, - channel: slice, + channel, } } } diff --git a/src/core/src/healpix/moc/freq_space.rs b/src/core/src/healpix/moc/freq_space.rs new file mode 100644 index 00000000..dca6ae49 --- /dev/null +++ b/src/core/src/healpix/moc/freq_space.rs @@ -0,0 +1,161 @@ +use crate::math::lonlat::LonLatT; +use crate::math::PI; +use crate::math::{self, lonlat::LonLat}; + +use cgmath::Vector3; +use moclib::elemset::range::uniq::HpxUniqRanges; +use moclib::hpxranges2d::HpxRanges2D; +use moclib::moc::RangeMOCIntoIterator; +use moclib::moc2d::{HasTwoMaxDepth, RangeMOC2Iterator}; +use moclib::ranges::ranges2d::Ranges2D; +use moclib::{ + moc::range::{CellSelection, RangeMOC}, + moc2d::range::RangeMOC2, + qty::Hpx, + ranges::SNORanges, +}; + +use moclib::qty::Frequency; + +use crate::healpix::cell::HEALPixCell; +#[derive(Debug)] +pub struct FreqSpaceMoc(pub moclib::hpxranges2d::FreqSpaceMoc); + +impl Clone for FreqSpaceMoc { + fn clone(&self) -> Self { + let HpxRanges2D(Moc2DRanges { + ranges2d: Ranges2D { x, y }, + .. + }) = &**self; + + Self(HpxRanges2D(Moc2DRanges::new(x.clone(), y.clone()))) + } +} + +use wasm_bindgen::JsValue; + +use moclib::deser::fits; +use moclib::deser::fits::MocIdxType; +use moclib::deser::fits::MocQtyType; +use moclib::deser::fits::RangeMoc2DIterFromFits; +use moclib::idx::Idx; +use moclib::moc::range::op::convert::convert_to_u64; +use moclib::moc::{CellMOCIntoIterator, CellMOCIterator, RangeMOCIterator}; +use moclib::mocranges2d::Moc2DRanges; + +use moclib::deser::fits::MocType; +use std::io::Cursor; + +use crate::math::spectra::Freq; +use crate::math::spectra::SpectralUnit; +impl FreqSpaceMoc { + pub fn from_fits_raw_bytes(bytes: &[u8]) -> Result { + let sfmoc = match fits::from_fits_ivoa_custom(Cursor::new(bytes), true) + .map_err(|e| JsValue::from_str(&e.to_string()))? + { + //MocIdxType::U16(MocQtyType::::FreqHpx(moc)) => Ok(from_fits_hpx(moc)), + //MocIdxType::U32(MocQtyType::::FreqHpx(moc)) => Ok(from_fits_hpx(moc)), + MocIdxType::U64(MocQtyType::::FreqHpx(ranges_iter)) => { + let moc_2d_ranges = Moc2DRanges::from_ranges_it(ranges_iter); + let inner = moclib::hpxranges2d::HpxRanges2D(moc_2d_ranges); + Ok(inner) + } + _ => Err(JsValue::from_str( + "MOC not supported. Must be a FREQ|HPX 2DMOC coded on U64 only", + )), + }?; + + Ok(Self(sfmoc)) + } + + /// This methods builds a SFMOC made of: + /// * the cells in the spatial viewport at a specific frequency f + /// * the +/- f_window cells containing inside lonlat on the frequency axis + /// This is the method to use when looking for new cube HiPS3D tiles + pub fn from_coos_freq, F: SpectralUnit>( + // The depth of the smallest HEALPix cells contained in it + depth: u8, + // The vertices of the polygon delimiting the coverage + vertices_iter: impl Iterator, + // A vertex being inside the coverage, + // typically the center of projection + inside: &L, + // The freq at which we want to compute the sfmoc + f: F, + // Frequency window i.e. the number of cells around f to query + f_window: u8, + ) -> Self { + let freq: Freq = f.into(); + + todo!(); + + /*let lonlat = vertices_iter + .map(|vertex| { + let LonLatT(lon, lat) = vertex.lonlat(); + (lon.to_radians(), lat.to_radians()) + }) + .collect::>(); + + let LonLatT(in_lon, in_lat) = inside.lonlat(); + let moc = RangeMOC2::from_freqranges_in_hz_and_coos( + &lonlat[..], + (in_lon.to_radians(), in_lat.to_radians()), + depth, + CellSelection::All, + ); + SpaceFreqMoc(moc)*/ + } + + /*pub fn from_fixed_hpx_cells( + depth: u8, + hpx_idx: impl Iterator, + cap: Option, + ) -> Self { + let moc = RangeMOC::from_fixed_depth_cells(depth, hpx_idx, cap); + SpaceMoc(moc) + } + + pub fn from_hpx_cells<'a>( + depth: u8, + hpx_cell_it: impl Iterator, + cap: Option, + ) -> Self { + let cells_it = hpx_cell_it.map(|HEALPixCell(depth, idx)| (*depth, *idx)); + + let moc = RangeMOC::from_cells(depth, cells_it, cap); + SpaceMoc(moc) + }*/ + + pub fn f_max_depth(&self) -> u8 { + self.0.compute_min_depth().0 + } + + pub fn s_max_depth(&self) -> u8 { + self.0.compute_min_depth().1 + } + + pub fn sky_fraction(&self) -> f64 { + todo!() + } + + pub fn intersects_cell(&self, hpx_cell: &HEALPixCell, f: Freq) -> bool { + let z29_rng = hpx_cell.z_29_rng(); + let f_hash = f.hash(); + + self.0.contains(f_hash, &z29_rng) + } + + /*/// provide the list of (hash hpx, hash freq) of the cells contained in the sfmoc + pub fn cells(&self) -> impl Iterator { + todo!() + }*/ +} + +use core::ops::Deref; +impl Deref for FreqSpaceMoc { + type Target = moclib::hpxranges2d::FreqSpaceMoc; + + fn deref(&'_ self) -> &'_ Self::Target { + &self.0 + } +} diff --git a/src/core/src/healpix/moc/mod.rs b/src/core/src/healpix/moc/mod.rs new file mode 100644 index 00000000..09b01524 --- /dev/null +++ b/src/core/src/healpix/moc/mod.rs @@ -0,0 +1,10 @@ +mod freq_space; +mod space; + +pub use freq_space::FreqSpaceMoc; +pub use space::SpaceMoc; + +pub enum Moc { + FreqSpace(FreqSpaceMoc), + Space(SpaceMoc), +} diff --git a/src/core/src/healpix/coverage.rs b/src/core/src/healpix/moc/space.rs similarity index 54% rename from src/core/src/healpix/coverage.rs rename to src/core/src/healpix/moc/space.rs index 91f28ee0..3497ef1e 100644 --- a/src/core/src/healpix/coverage.rs +++ b/src/core/src/healpix/moc/space.rs @@ -3,6 +3,7 @@ use crate::math::PI; use crate::math::{self, lonlat::LonLat}; use cgmath::Vector3; +use moclib::moc::RangeMOCIntoIterator; use moclib::{ moc::range::{CellSelection, RangeMOC}, qty::Hpx, @@ -12,9 +13,65 @@ pub type Smoc = RangeMOC>; use crate::healpix::cell::HEALPixCell; #[derive(Clone, Debug)] -pub struct HEALPixCoverage(pub Smoc); +pub struct SpaceMoc(pub Smoc); + +use wasm_bindgen::JsValue; + +use moclib::deser::fits; +use moclib::deser::fits::MocIdxType; +use moclib::deser::fits::MocQtyType; +use moclib::idx::Idx; +use moclib::moc::range::op::convert::convert_to_u64; +use moclib::moc::{CellMOCIntoIterator, CellMOCIterator, RangeMOCIterator}; +/// Convenient type for Space-MOCs +pub fn from_fits_hpx(moc: MocType, Cursor<&[u8]>>) -> Smoc { + match moc { + MocType::Ranges(moc) => convert_to_u64::, _, Hpx>(moc).into_range_moc(), + MocType::Cells(moc) => { + convert_to_u64::, _, Hpx>(moc.into_cell_moc_iter().ranges()) + .into_range_moc() + } + } +} + +use moclib::deser::fits::MocType; +use std::io::Cursor; +impl SpaceMoc { + pub fn from_fits_raw_bytes(bytes: &[u8]) -> Result { + let smoc = match fits::from_fits_ivoa_custom(Cursor::new(bytes), true) + .map_err(|e| JsValue::from_str(&e.to_string()))? + { + MocIdxType::U16(MocQtyType::::Hpx(moc)) => Ok(from_fits_hpx(moc)), + MocIdxType::U32(MocQtyType::::Hpx(moc)) => Ok(from_fits_hpx(moc)), + MocIdxType::U64(MocQtyType::::Hpx(moc)) => Ok(from_fits_hpx(moc)), + _ => Err(JsValue::from_str("MOC not supported. Must be a HPX MOC")), + }?; + + Ok(Self(smoc)) + } + + pub fn from_json(s: &str) -> Result { + let moc = moclib::deser::json::from_json_aladin::>(s) + .map_err(|e| JsValue::from(js_sys::Error::new(&e.to_string())))? + .into_cell_moc_iter() + .ranges() + .into_range_moc(); + + Ok(Self(moc)) + } + + pub fn serialize_to_json(&self) -> Result { + let mut buf: Vec = Default::default(); + let json = (&self.0) + .into_range_moc_iter() + .cells() + .to_json_aladin(None, &mut buf) + .map(|()| unsafe { String::from_utf8_unchecked(buf) }) + .map_err(|err| JsValue::from_str(&format!("{err:?}"))); + + json + } -impl HEALPixCoverage { pub fn from_3d_coos>( // The depth of the smallest HEALPix cells contained in it depth: u8, @@ -38,7 +95,7 @@ impl HEALPixCoverage { depth, CellSelection::All, ); - HEALPixCoverage(moc) + SpaceMoc(moc) } pub fn from_fixed_hpx_cells( @@ -47,7 +104,7 @@ impl HEALPixCoverage { cap: Option, ) -> Self { let moc = RangeMOC::from_fixed_depth_cells(depth, hpx_idx, cap); - HEALPixCoverage(moc) + SpaceMoc(moc) } pub fn from_hpx_cells<'a>( @@ -58,14 +115,14 @@ impl HEALPixCoverage { let cells_it = hpx_cell_it.map(|HEALPixCell(depth, idx)| (*depth, *idx)); let moc = RangeMOC::from_cells(depth, cells_it, cap); - HEALPixCoverage(moc) + SpaceMoc(moc) } pub fn from_cone(lonlat: &LonLatT, rad: f64, depth: u8) -> Self { if rad >= PI { Self::allsky(depth) } else { - HEALPixCoverage(RangeMOC::from_cone( + SpaceMoc(RangeMOC::from_cone( lonlat.lon().to_radians(), lonlat.lat().to_radians(), rad, @@ -78,12 +135,7 @@ impl HEALPixCoverage { pub fn allsky(depth_max: u8) -> Self { let moc = RangeMOC::new_full_domain(depth_max); - HEALPixCoverage(moc) - } - - pub fn contains_coo(&self, coo: &Vector3) -> bool { - let (lon, lat) = math::lonlat::xyz_to_radec(coo); - self.0.is_in(lon.to_radians(), lat.to_radians()) + SpaceMoc(moc) } pub fn contains_lonlat(&self, lonlat: &LonLatT) -> bool { @@ -98,9 +150,9 @@ impl HEALPixCoverage { self.0.moc_ranges().intersects_range(&z29_rng) } - pub fn is_intersecting(&self, other: &Self) -> bool { + /*pub fn is_intersecting(&self, other: &Self) -> bool { !self.0.intersection(&other.0).is_empty() - } + }*/ pub fn depth(&self) -> u8 { self.0.depth_max() @@ -111,16 +163,16 @@ impl HEALPixCoverage { } pub fn not(&self) -> Self { - HEALPixCoverage(self.0.not()) + SpaceMoc(self.0.not()) } pub fn empty(depth: u8) -> Self { - HEALPixCoverage(RangeMOC::new_empty(depth)) + SpaceMoc(RangeMOC::new_empty(depth)) } } use core::ops::Deref; -impl Deref for HEALPixCoverage { +impl Deref for SpaceMoc { type Target = Smoc; fn deref(&'_ self) -> &'_ Self::Target { diff --git a/src/core/src/healpix/mod.rs b/src/core/src/healpix/mod.rs index da712288..a9179298 100644 --- a/src/core/src/healpix/mod.rs +++ b/src/core/src/healpix/mod.rs @@ -1,4 +1,4 @@ pub mod cell; -pub mod coverage; pub mod index_vector; +pub mod moc; pub mod utils; diff --git a/src/core/src/lib.rs b/src/core/src/lib.rs index f435d56a..1aa483e9 100644 --- a/src/core/src/lib.rs +++ b/src/core/src/lib.rs @@ -112,14 +112,10 @@ mod shader; mod tile_fetcher; mod time; -use crate::downloader::request::moc::from_fits_hpx; use crate::{ - camera::CameraViewPort, healpix::coverage::HEALPixCoverage, math::lonlat::LonLatT, - shader::ShaderManager, time::DeltaTime, + camera::CameraViewPort, healpix::moc::SpaceMoc, math::lonlat::LonLatT, shader::ShaderManager, + time::DeltaTime, }; -use moclib::deser::fits; -use moclib::deser::fits::MocIdxType; -use moclib::deser::fits::MocQtyType; use std::io::Cursor; @@ -136,10 +132,6 @@ use cgmath::{Vector2, Vector3}; use crate::healpix::cell::HEALPixCell; use math::angle::ArcDeg; -use moclib::{ - moc::{CellMOCIntoIterator, CellMOCIterator, RangeMOCIterator}, - qty::Hpx, -}; #[wasm_bindgen] pub struct WebClient { @@ -1062,13 +1054,8 @@ impl WebClient { pub fn add_json_moc(&mut self, options: MOCOptions, data: &JsValue) -> Result<(), JsValue> { let str: String = js_sys::JSON::stringify(data)?.into(); - let moc = moclib::deser::json::from_json_aladin::>(&str) - .map_err(|e| JsValue::from(js_sys::Error::new(&e.to_string())))? - .into_cell_moc_iter() - .ranges() - .into_range_moc(); - - self.app.add_moc(HEALPixCoverage(moc), options)?; + let smoc = SpaceMoc::from_json(&str)?; + self.app.add_moc(smoc, options)?; Ok(()) } @@ -1076,18 +1063,8 @@ impl WebClient { #[wasm_bindgen(js_name = addFITSMOC)] pub fn add_fits_moc(&mut self, options: MOCOptions, 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) - .map_err(|e| JsValue::from_str(&e.to_string()))? - { - MocIdxType::U16(MocQtyType::::Hpx(moc)) => { - Ok(crate::downloader::request::moc::from_fits_hpx(moc)) - } - MocIdxType::U32(MocQtyType::::Hpx(moc)) => Ok(from_fits_hpx(moc)), - MocIdxType::U64(MocQtyType::::Hpx(moc)) => Ok(from_fits_hpx(moc)), - _ => Err(JsValue::from_str("MOC not supported. Must be a HPX MOC")), - }?; - - self.app.add_moc(HEALPixCoverage(moc), options)?; + let smoc = SpaceMoc::from_fits_raw_bytes(data)?; + self.app.add_moc(smoc, options)?; Ok(()) } @@ -1102,7 +1079,7 @@ impl WebClient { ) -> Result<(), JsValue> { let tile_d = self.app.get_norder(); let pixel_d = tile_d + 9; - let moc = HEALPixCoverage::from_cone( + let moc = SpaceMoc::from_cone( &LonLatT::new( ra_deg.to_radians().to_angle(), dec_deg.to_radians().to_angle(), @@ -1136,7 +1113,7 @@ impl WebClient { let v_in = &Vector3::new(1.0, 0.0, 0.0); - let mut moc = HEALPixCoverage::from_3d_coos(pixel_d as u8 - 1, vertex_it, v_in); + let mut moc = SpaceMoc::from_3d_coos(pixel_d as u8 - 1, vertex_it, v_in); if moc.sky_fraction() > 0.5 { moc = moc.not(); } @@ -1182,13 +1159,7 @@ impl WebClient { .get_moc(&moc_uuid) .ok_or_else(|| JsValue::from(js_sys::Error::new("MOC not found")))?; - let mut buf: Vec = Default::default(); - let json = (&moc.0) - .into_range_moc_iter() - .cells() - .to_json_aladin(None, &mut buf) - .map(|()| unsafe { String::from_utf8_unchecked(buf) }) - .map_err(|err| JsValue::from_str(&format!("{err:?}")))?; + let json = moc.serialize_to_json()?; serde_wasm_bindgen::to_value(&json).map_err(|err| JsValue::from_str(&format!("{err:?}"))) } diff --git a/src/core/src/math/mod.rs b/src/core/src/math/mod.rs index e4c30165..5087e256 100644 --- a/src/core/src/math/mod.rs +++ b/src/core/src/math/mod.rs @@ -9,6 +9,8 @@ pub const SQRT_TWO: f64 = std::f64::consts::SQRT_2; pub const ZERO: f64 = 0.0; +pub mod spectra; + pub mod angle; pub mod lonlat; pub mod projection; diff --git a/src/core/src/math/spectra.rs b/src/core/src/math/spectra.rs new file mode 100644 index 00000000..05e616ff --- /dev/null +++ b/src/core/src/math/spectra.rs @@ -0,0 +1,62 @@ +pub trait SpectralUnit: Into + Clone + Copy { + fn hash(&self) -> u64 { + let f: Freq = (*self).into(); + Frequency::freq2hash(f.0) + } +} + +use moclib::qty::Frequency; + +/// Frequency in Hz unit +#[derive(Clone, Copy)] +pub struct Freq(pub f64); + +impl Freq { + fn from_hash(hash: u64) -> Self { + let f = Frequency::hash2freq(hash); + + Freq(f) + } +} + +/// Wavelength in meter unit +#[derive(Clone, Copy)] +pub struct Wavelength(f64); + +/// Velocity in meter/sec unit +#[derive(Clone, Copy)] +pub struct Velocity { + /// A rest frequency to compute the velocity from + /// given by the obs_restfreq HiPS property + rest_freq: Freq, + /// The velocity in m/s + velocity: f64, +} + +const SPEED_OF_LIGHT: f64 = 299792458.0; + +impl From for Freq { + fn from(v: Velocity) -> Self { + let Velocity { + rest_freq, + velocity, + } = v; + + // v = c * (of - f) / of + // v * of = c * (of - f) + // c * f = c * of - v * of = of * (c - v) + // f = of * (c - v) / c = of * (1 - v / c) + + Freq(rest_freq.0 * (1.0 - velocity / SPEED_OF_LIGHT)) + } +} + +impl From for Freq { + fn from(lambda: Wavelength) -> Self { + Freq(SPEED_OF_LIGHT / lambda.0) + } +} + +impl SpectralUnit for Freq {} +impl SpectralUnit for Wavelength {} +impl SpectralUnit for Velocity {} diff --git a/src/core/src/renderable/hips/config.rs b/src/core/src/renderable/hips/config.rs index e120c06a..ec3d2dcc 100644 --- a/src/core/src/renderable/hips/config.rs +++ b/src/core/src/renderable/hips/config.rs @@ -1,4 +1,4 @@ -use al_api::hips::ImageExt; +use al_api::hips::{DataproductType, ImageExt}; use al_core::image::format::ImageFormatType; use al_core::texture::format::PixelType; @@ -10,23 +10,31 @@ pub struct HiPSConfig { // HiPS image format // TODO: Make that independant of the HiPS but of the ImageFormat - // The size of the texture images - tile_size: i32, + // Size of the tiles + pub tile_size: i32, - min_depth_tile: u8, - // the number of slices for cubes + // Number of slices for HiPS cubes cube_depth: Option, // Max depth of the current HiPS tiles max_depth_tile: u8, + // Min depth of the current HiPS tiles + min_depth_tile: u8, + // For HiPS3D + max_depth_freq: Option, + + // For HiPS3D + pub tile_depth: Option, pub is_allsky: bool, pub frame: CooSystem, + // For FITS HiPSes pub bitpix: Option, format: ImageFormatType, - //dataproduct_subtype: Option>, - //colored: bool, + + pub dataproduct_type: DataproductType, + pub creator_did: String, pub request_credentials: RequestCredentials, @@ -116,6 +124,12 @@ impl HiPSConfig { _ => RequestMode::Cors, }; + let dataproduct_type = properties.get_dataproduct_type().ok_or(JsValue::from_str( + "dataproduct_type keyword is required in the HiPS properties file", + ))?; + let max_depth_freq = properties.get_hips_order_freq(); + let tile_depth = properties.get_hips_tile_depth(); + let hips_config = HiPSConfig { creator_did, // HiPS name @@ -125,15 +139,20 @@ impl HiPSConfig { is_allsky, - // the number of slices in a cube + // HiPSCube cube_depth, + // HiPS3D + tile_depth, + max_depth_freq, + frame, bitpix, format, tile_size, request_credentials, request_mode, + dataproduct_type, }; Ok(hips_config) diff --git a/src/core/src/renderable/hips/d2/mod.rs b/src/core/src/renderable/hips/d2/mod.rs index e42c83f4..0c7b67b0 100644 --- a/src/core/src/renderable/hips/d2/mod.rs +++ b/src/core/src/renderable/hips/d2/mod.rs @@ -33,7 +33,7 @@ use crate::shader::ShaderManager; use crate::utils; use crate::downloader::request::allsky::Allsky; -use crate::healpix::{cell::HEALPixCell, coverage::HEALPixCoverage}; +use crate::healpix::{cell::HEALPixCell, moc::SpaceMoc}; use crate::time::Time; use super::config::HiPSConfig; @@ -226,7 +226,7 @@ pub struct HiPS2D { vao: VertexArrayObject, gl: WebGlContext, - footprint_moc: Option, + moc: Option, // A buffer storing the cells in the view hpx_cells_in_view: Vec, @@ -292,7 +292,7 @@ impl HiPS2D { let buffer = HiPS2DBuffer::new(gl, config)?; let gl = gl.clone(); - let footprint_moc = None; + let moc = None; let hpx_cells_in_view = vec![]; // request the allsky texture Ok(Self { @@ -313,7 +313,7 @@ impl HiPS2D { idx_vertices, - footprint_moc, + moc, hpx_cells_in_view, }) } @@ -330,19 +330,12 @@ impl HiPS2D { .max(cfg.get_min_depth_tile()); let survey_frame = cfg.get_frame(); - let mut already_considered_tiles = HashSet::new(); let tile_cells_iter = camera .get_hpx_cells(depth_tile, survey_frame) .into_iter() .filter(move |tile_cell| { - if already_considered_tiles.contains(tile_cell) { - return false; - } - - already_considered_tiles.insert(*tile_cell); - - if let Some(moc) = self.footprint_moc.as_ref() { + if let Some(moc) = self.moc.as_ref() { moc.intersects_cell(tile_cell) && !self.update_priority_tile(tile_cell) } else { !self.update_priority_tile(tile_cell) @@ -401,13 +394,13 @@ impl HiPS2D { } #[inline] - pub fn set_moc(&mut self, moc: HEALPixCoverage) { - self.footprint_moc = Some(moc); + pub fn set_moc(&mut self, moc: SpaceMoc) { + self.moc = Some(moc); } #[inline] - pub fn get_moc(&self) -> Option<&HEALPixCoverage> { - self.footprint_moc.as_ref() + pub fn get_moc(&self) -> Option<&SpaceMoc> { + self.moc.as_ref() } pub fn set_image_ext(&mut self, ext: ImageExt) -> Result<(), JsValue> { @@ -479,7 +472,7 @@ impl HiPS2D { // super::subdivide::num_hpx_subdivision(&self.hpx_cells_in_view[0], camera, projection); for cell in &self.hpx_cells_in_view { // filter textures that are not in the moc - let cell_in_cov = if let Some(moc) = self.footprint_moc.as_ref() { + let cell_in_cov = if let Some(moc) = self.moc.as_ref() { if moc.intersects_cell(cell) { // Rasterizer does not render tiles that are not in the MOC // This is not a problem for transparency rendered HiPses (FITS or PNG) diff --git a/src/core/src/renderable/hips/d3/buffer.rs b/src/core/src/renderable/hips/d3/cube.rs similarity index 96% rename from src/core/src/renderable/hips/d3/buffer.rs rename to src/core/src/renderable/hips/d3/cube.rs index a4ef48ab..5a97da5a 100644 --- a/src/core/src/renderable/hips/d3/buffer.rs +++ b/src/core/src/renderable/hips/d3/cube.rs @@ -13,7 +13,7 @@ use crate::Abort; use crate::JsValue; use al_api::hips::ImageExt; // Fixed sized binary heap -pub struct HiPS3DBuffer { +pub struct HiPSCubeBuffer { // Some information about the HiPS textures: HashMap, @@ -24,7 +24,7 @@ pub struct HiPS3DBuffer { gl: WebGlContext, } -impl HiPS3DBuffer { +impl HiPSCubeBuffer { pub fn new(gl: &WebGlContext, config: HiPSConfig) -> Result { let textures = HashMap::new(); @@ -124,7 +124,7 @@ impl HiPS3DBuffer { } } -impl HpxTileBuffer for HiPS3DBuffer { +impl HpxTileBuffer for HiPSCubeBuffer { type T = HpxTexture3D; fn new(gl: &WebGlContext, config: HiPSConfig) -> Result { @@ -182,14 +182,14 @@ impl HpxTileBuffer for HiPS3DBuffer { use al_core::shader::SendUniforms; use al_core::shader::ShaderBound; -impl SendUniforms for HiPS3DBuffer { +impl SendUniforms for HiPSCubeBuffer { // Send only the allsky textures fn attach_uniforms<'a>(&self, shader: &'a ShaderBound<'a>) -> &'a ShaderBound<'a> { shader.attach_uniforms_from(&self.config) } } -impl Drop for HiPS3DBuffer { +impl Drop for HiPSCubeBuffer { fn drop(&mut self) { // drop all the 3D block textures self.textures.clear(); diff --git a/src/core/src/renderable/hips/d3/mod.rs b/src/core/src/renderable/hips/d3/mod.rs index e37eb938..502bed53 100644 --- a/src/core/src/renderable/hips/d3/mod.rs +++ b/src/core/src/renderable/hips/d3/mod.rs @@ -1,7 +1,10 @@ -pub mod buffer; +pub mod cube; pub mod texture; +use crate::healpix::moc::FreqSpaceMoc; +use crate::math::spectra::SpectralUnit; use crate::renderable::hips::HpxTile; +use al_api::hips::DataproductType; use al_api::hips::ImageExt; use al_api::hips::ImageMetadata; use al_core::colormap::Colormap; @@ -26,7 +29,7 @@ use crate::downloader::query; use crate::shader::ShaderManager; use crate::downloader::request::allsky::Allsky; -use crate::healpix::{cell::HEALPixCell, coverage::HEALPixCoverage}; +use crate::healpix::cell::HEALPixCell; use crate::time::Time; use super::config::HiPSConfig; @@ -36,7 +39,7 @@ use std::collections::HashSet; // Recursively compute the number of subdivision needed for a cell // to not be too much skewed -use buffer::HiPS3DBuffer; +use cube::HiPSCubeBuffer; use super::uv::{TileCorner, TileUVW}; @@ -83,7 +86,7 @@ pub fn get_raster_shader<'a>( pub struct HiPS3D { //color: Color, // The image survey texture buffer - buffer: HiPS3DBuffer, + buffer: HiPSCubeBuffer, // The projected vertices data // For WebGL2 wasm, the data are interleaved @@ -100,7 +103,7 @@ pub struct HiPS3D { vao: VertexArrayObject, gl: WebGlContext, - footprint_moc: Option, + moc: Option, // A buffer storing the cells in the view hpx_cells_in_view: Vec, @@ -108,23 +111,22 @@ pub struct HiPS3D { pub(crate) fits_params: Option, // The current slice index - slice: u16, + freq: Freq, num_indices: Vec, - slice_indices: Vec, cells: Vec, } use super::HpxTileBuffer; +use crate::math::spectra::Freq; impl HiPS3D { pub fn new(config: HiPSConfig, gl: &WebGlContext) -> Result { let mut vao = VertexArrayObject::new(gl); - let slice = 0; + let freq = Freq(0.0); let num_indices = vec![]; - let slice_indices = vec![]; // layout (location = 0) in vec2 lonlat; // layout (location = 1) in vec3 position; // layout (location = 2) in vec3 uv_start; @@ -159,12 +161,12 @@ impl HiPS3D { ) .unbind(); - let buffer = HiPS3DBuffer::new(gl, config)?; + let buffer = HiPSCubeBuffer::new(gl, config)?; let cells = vec![]; let gl = gl.clone(); - let footprint_moc = None; + let moc = None; let hpx_cells_in_view = vec![]; // request the allsky texture Ok(Self { @@ -181,13 +183,12 @@ impl HiPS3D { fits_params: None, - footprint_moc, + moc, hpx_cells_in_view, - slice, + freq, cells, num_indices, - slice_indices, }) } @@ -204,8 +205,6 @@ impl HiPS3D { .max(cfg.get_min_depth_tile()); let survey_frame = cfg.get_frame(); - let mut already_considered_tiles = HashSet::new(); - // raytracer is rendering and the shader only renders HPX texture cells of depth 0 /*if camera.is_raytracing(proj) { depth_tile = 0; @@ -215,14 +214,8 @@ impl HiPS3D { .get_hpx_cells(depth_tile, survey_frame) .into_iter() .filter(move |tile_cell| { - if already_considered_tiles.contains(tile_cell) { - return false; - } - - already_considered_tiles.insert(*tile_cell); - - if let Some(moc) = self.footprint_moc.as_ref() { - moc.intersects_cell(tile_cell) + if let Some(moc) = self.moc.as_ref() { + moc.intersects_cell(tile_cell, self.freq) } else { true } @@ -231,17 +224,21 @@ impl HiPS3D { Some(tile_cells_iter) } - pub fn set_slice(&mut self, slice: u16) { - self.slice = slice; + pub fn set_freq(&mut self, f: Freq) { + self.freq = f; } pub fn get_tile_query(&self, cell: &HEALPixCell) -> query::Tile { let cfg = self.get_config(); - query::Tile::new(cell, Some(self.get_slice() as u32), cfg) + match cfg.dataproduct_type { + DataproductType::Cube => query::Tile::new(cell, Some(self.freq.0 as u32), cfg), + DataproductType::SpectralCube => todo!(), + _ => unreachable!(), + } } - pub fn contains_tile(&self, cell: &HEALPixCell, slice: u16) -> bool { - self.buffer.contains_tile(cell, slice) + pub fn contains_tile(&self, cell: &HEALPixCell, freq: Freq) -> bool { + self.buffer.contains_tile(cell, freq.0 as u16) } pub fn draw( @@ -270,9 +267,12 @@ impl HiPS3D { //} } + pub fn get_freq(&self) -> Freq { + self.freq + } + fn recompute_vertices(&mut self, camera: &CameraViewPort, proj: &ProjectionType) { self.cells.clear(); - self.slice_indices.clear(); self.position.clear(); self.uv.clear(); @@ -298,8 +298,8 @@ impl HiPS3D { for cell in &self.hpx_cells_in_view { // filter textures that are not in the moc - let cell = if let Some(moc) = self.footprint_moc.as_ref() { - if moc.intersects_cell(cell) { + let cell = if let Some(moc) = self.moc.as_ref() { + if moc.intersects_cell(cell, self.freq) { Some(&cell) } else if channel == PixelType::RGB8U { // Rasterizer does not render tiles that are not in the MOC @@ -314,13 +314,11 @@ impl HiPS3D { Some(&cell) }; - let mut slice_contained = 0; - if let Some(cell) = cell { - let hpx_cell_texture = if self.buffer.contains_tile(cell, self.slice) { - slice_contained = self.slice; + let hpx_cell_texture = if self.contains_tile(cell, self.freq) { self.buffer.get(cell) - } else if let Some(next_slice) = self.buffer.find_nearest_slice(cell, self.slice) { + // if the freq is not found we just draw nothing + /*} else if let Some(next_slice) = self.buffer.find_nearest_slice(cell, self.slice) { slice_contained = next_slice; self.buffer.get(cell) } else if let Some(parent_cell) = self.buffer.get_nearest_parent(cell) { @@ -330,15 +328,17 @@ impl HiPS3D { .find_nearest_slice(&parent_cell, self.slice) .unwrap(); self.buffer.get(&parent_cell) + */ } else { None }; if let Some(texture) = hpx_cell_texture { - self.slice_indices.push(slice_contained as usize); self.cells.push(*texture.cell()); // The slice is sure to be contained so we can unwrap - let hpx_slice_tex = texture.extract_2d_slice_texture(slice_contained).unwrap(); + let hpx_slice_tex = texture + .extract_2d_slice_texture(self.freq.0 as u16) + .unwrap(); let uv_1 = TileUVW::new(cell, &hpx_slice_tex); let d01e = uv_1[TileCorner::BottomRight].x - uv_1[TileCorner::BottomLeft].x; @@ -461,8 +461,8 @@ impl HiPS3D { } #[inline] - pub fn set_moc(&mut self, moc: HEALPixCoverage) { - self.footprint_moc = Some(moc); + pub fn set_moc(&mut self, moc: FreqSpaceMoc) { + self.moc = Some(moc); } pub fn set_fits_params(&mut self, bscale: f32, bzero: f32, blank: Option) { @@ -474,8 +474,8 @@ impl HiPS3D { } #[inline] - pub fn get_moc(&self) -> Option<&HEALPixCoverage> { - self.footprint_moc.as_ref() + pub fn get_moc(&self) -> Option<&FreqSpaceMoc> { + self.moc.as_ref() } pub fn set_image_ext(&mut self, ext: ImageExt) -> Result<(), JsValue> { @@ -541,11 +541,7 @@ impl HiPS3D { let shader = get_raster_shader(cmap, &self.gl, shaders, hips_cfg)?; - for (slice_idx, (cell, num_indices)) in self - .slice_indices - .iter() - .zip(self.cells.iter().zip(self.num_indices.iter())) - { + for (cell, num_indices) in self.cells.iter().zip(self.num_indices.iter()) { blend_cfg.enable(&self.gl, || { // Bind the shader at each draw of a cell to not exceed the max number of tex image units bindable // to a shader. It is 32 in my case @@ -557,7 +553,7 @@ impl HiPS3D { self.buffer .get(cell) .unwrap() - .get_3d_block_from_slice(*slice_idx as u16) + .get_3d_block_from_slice(self.freq.0 as u16) .unwrap(), ) .attach_uniforms_from(&self.buffer) @@ -609,11 +605,6 @@ impl HiPS3D { self.buffer.push_allsky(allsky) } - #[inline] - pub fn get_slice(&self) -> u16 { - self.slice - } - /* Accessors */ #[inline] pub fn get_config(&self) -> &HiPSConfig { diff --git a/src/core/src/renderable/hips/mod.rs b/src/core/src/renderable/hips/mod.rs index f75a39f2..c2427209 100644 --- a/src/core/src/renderable/hips/mod.rs +++ b/src/core/src/renderable/hips/mod.rs @@ -13,7 +13,7 @@ use crate::renderable::HiPSConfig; use crate::time::Time; use crate::CameraViewPort; use crate::HEALPixCell; -use crate::HEALPixCoverage; +use crate::SpaceMoc; use crate::WebGlContext; use al_api::hips::ImageExt; use wasm_bindgen::JsValue; @@ -128,13 +128,13 @@ impl HiPS { } } - #[inline] - pub fn set_moc(&mut self, moc: HEALPixCoverage) { + /*#[inline] + pub fn set_moc(&mut self, moc: SpaceMoc) { match self { D2(hips) => hips.set_moc(moc), D3(hips) => hips.set_moc(moc), } - } + }*/ #[inline] pub fn get_tile_query(&self, cell: &HEALPixCell) -> query::Tile { diff --git a/src/core/src/renderable/moc/hierarchy.rs b/src/core/src/renderable/moc/hierarchy.rs index 94e8ac40..bdf539ad 100644 --- a/src/core/src/renderable/moc/hierarchy.rs +++ b/src/core/src/renderable/moc/hierarchy.rs @@ -1,5 +1,5 @@ use super::MOC; -use crate::{camera::CameraViewPort, HEALPixCoverage}; +use crate::{camera::CameraViewPort, SpaceMoc}; use al_api::moc::MOCOptions; pub struct MOCHierarchy { @@ -12,19 +12,13 @@ use al_core::WebGlContext; impl MOCHierarchy { pub fn from_full_res_moc( gl: WebGlContext, - full_res_moc: HEALPixCoverage, + full_res_moc: SpaceMoc, options: &MOCOptions, ) -> Self { let full_res_depth = full_res_moc.depth(); let mut mocs: Vec<_> = (0..full_res_depth) - .map(|d| { - MOC::new( - gl.clone(), - HEALPixCoverage(full_res_moc.degraded(d)), - options, - ) - }) + .map(|d| MOC::new(gl.clone(), SpaceMoc(full_res_moc.degraded(d)), options)) .collect(); mocs.push(MOC::new(gl.clone(), full_res_moc, options)); @@ -80,7 +74,7 @@ impl MOCHierarchy { &mut self.mocs[d] } - pub fn get_full_moc(&self) -> &HEALPixCoverage { + pub fn get_full_moc(&self) -> &SpaceMoc { &self.mocs.last().unwrap().moc } diff --git a/src/core/src/renderable/moc/mod.rs b/src/core/src/renderable/moc/mod.rs index 416e79f6..ae452444 100644 --- a/src/core/src/renderable/moc/mod.rs +++ b/src/core/src/renderable/moc/mod.rs @@ -3,7 +3,7 @@ pub mod renderer; pub use renderer::MOCRenderer; use crate::camera::CameraViewPort; -use crate::healpix::coverage::HEALPixCoverage; +use crate::healpix::moc::SpaceMoc; use crate::math::projection::ProjectionType; use crate::renderable::WebGl2RenderingContext; use crate::shader::ShaderManager; @@ -32,11 +32,11 @@ pub struct MOC { inner: [Option; 3], - pub moc: HEALPixCoverage, + pub moc: SpaceMoc, } impl MOC { - pub(super) fn new(gl: WebGlContext, moc: HEALPixCoverage, cfg: &MOCOptions) -> Self { + pub(super) fn new(gl: WebGlContext, moc: SpaceMoc, cfg: &MOCOptions) -> Self { let sky_fraction = moc.sky_fraction() as f32; let max_order = moc.depth_max(); @@ -228,7 +228,7 @@ impl MOCIntern { fn vertices_in_view<'a>( &self, - moc: &'a HEALPixCoverage, + moc: &'a SpaceMoc, camera: &'a mut CameraViewPort, ) -> impl Iterator + 'a { let view_moc = camera.get_cov(CooSystem::ICRS); @@ -250,7 +250,7 @@ impl MOCIntern { fn draw( &mut self, - moc: &HEALPixCoverage, + moc: &SpaceMoc, camera: &mut CameraViewPort, proj: &ProjectionType, shaders: &mut ShaderManager, @@ -457,7 +457,7 @@ impl MOCIntern { fn compute_edge_paths_iter<'a>( &self, - moc: &'a HEALPixCoverage, + moc: &'a SpaceMoc, camera: &'a mut CameraViewPort, ) -> impl Iterator + 'a { self.vertices_in_view(moc, camera).flat_map(|v| { diff --git a/src/core/src/renderable/moc/renderer.rs b/src/core/src/renderable/moc/renderer.rs index 3f949110..2e5ba3f5 100644 --- a/src/core/src/renderable/moc/renderer.rs +++ b/src/core/src/renderable/moc/renderer.rs @@ -1,4 +1,4 @@ -use crate::{healpix::coverage::HEALPixCoverage, CameraViewPort, ShaderManager}; +use crate::{healpix::moc::SpaceMoc, CameraViewPort, ShaderManager}; use al_core::WebGlContext; use wasm_bindgen::JsValue; @@ -67,7 +67,7 @@ impl MOCRenderer { pub fn push_back( &mut self, - moc: HEALPixCoverage, + moc: SpaceMoc, cfg: MOCOptions, camera: &mut CameraViewPort, proj: &ProjectionType, @@ -80,7 +80,7 @@ impl MOCRenderer { //self.layers.push(key); } - pub fn get_hpx_coverage(&self, moc_uuid: &str) -> Option<&HEALPixCoverage> { + pub fn get_hpx_coverage(&self, moc_uuid: &str) -> Option<&SpaceMoc> { if let Some(idx) = self.cfgs.iter().position(|cfg| cfg.get_uuid() == moc_uuid) { Some(self.mocs[idx].get_full_moc()) } else { diff --git a/src/core/src/renderable/mod.rs b/src/core/src/renderable/mod.rs index 4ab0d5e6..3ef4b11c 100644 --- a/src/core/src/renderable/mod.rs +++ b/src/core/src/renderable/mod.rs @@ -13,6 +13,7 @@ use crate::renderable::image::Image; use crate::tile_fetcher::TileFetcherQueue; use al_api::color::ColorRGB; +use al_api::hips::DataproductType; use al_api::hips::HiPSCfg; use al_api::hips::ImageMetadata; use al_api::image::ImageParams; @@ -435,11 +436,13 @@ impl Layers { }*/ camera.register_view_frame(cfg.get_frame(), proj); - let hips = if cfg.get_cube_depth().is_some() { + let hips = match &cfg.dataproduct_type { // HiPS cube - HiPS::D3(HiPS3D::new(cfg, gl)?) - } else { - HiPS::D2(HiPS2D::new(cfg, gl)?) + DataproductType::Cube => HiPS::D3(HiPS3D::new(cfg, gl)?), + // HiPS 3D + DataproductType::SpectralCube => HiPS::D3(HiPS3D::new(cfg, gl)?), + // Typical HiPS image + _ => HiPS::D2(HiPS2D::new(cfg, gl)?), }; // add the frame to the camera diff --git a/src/core/src/tile_fetcher.rs b/src/core/src/tile_fetcher.rs index 2121d0e2..88c113e0 100644 --- a/src/core/src/tile_fetcher.rs +++ b/src/core/src/tile_fetcher.rs @@ -72,8 +72,10 @@ impl HiPSLocalFiles { tiles_per_fmt[d].get(&i) } +} - fn get_moc(&self) -> &web_sys::File { +impl HiPSLocalFiles { + pub fn get_moc(&self) -> &web_sys::File { &self.moc } } @@ -189,28 +191,10 @@ impl TileFetcherQueue { downloader: Rc>, ) { 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 - 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_request_mode(), - cfg.get_request_credentials(), - cfg.get_creator_did().to_string(), + cfg, + &self.hips_local_files, MOCOptions::default(), )); @@ -219,20 +203,21 @@ impl TileFetcherQueue { // Request the allsky let dl = downloader.clone(); - let allsky_query = query::Allsky::new( - cfg, - match hips { - HiPS::D2(_) => None, - HiPS::D3(h) => Some(h.get_slice() as u32), - }, - ); + // Allsky query + match hips { + HiPS::D2(_) => { + let allsky_query = query::Allsky::new(cfg, None); - crate::utils::set_timeout( - move || { - dl.borrow_mut().fetch(allsky_query); - }, - 100, - ); + crate::utils::set_timeout( + move || { + dl.borrow_mut().fetch(allsky_query); + }, + 100, + ); + } + // Do not ask for allsky for HiPS3D + HiPS::D3(_) => (), + } if cfg.get_min_depth_tile() == 0 { for tile_cell in crate::healpix::cell::ALLSKY_HPX_CELLS_D0 { diff --git a/src/js/HiPS.js b/src/js/HiPS.js index 4a46c269..f8409d7e 100644 --- a/src/js/HiPS.js +++ b/src/js/HiPS.js @@ -469,16 +469,24 @@ export let HiPS = (function () { let self = this; self.creatorDid = properties.creator_did || self.creatorDid; - // Cube depth + // HiPS Cube special keywords self.cubeDepth = properties && properties.hips_cube_depth && +properties.hips_cube_depth; self.cubeFirstFrame = properties && properties.hips_cube_firstframe && +properties.hips_cube_firstframe; + // HiPS3D special keywords + self.hipsOrderFreq = properties && properties.hips_order_freq && +properties.hips_order_freq; + self.hipsTileDepth = properties && properties.hips_tile_depth && +properties.hips_tile_depth; + + // Max order const maxOrder = PropertyParser.maxOrder(properties) if (maxOrder !== undefined) { self.maxOrder = maxOrder; } + // dataproduct type + self.dataproductType = properties && properties.dataproduct_type; + // Tile size self.tileSize = PropertyParser.tileSize(properties) || self.tileSize; @@ -997,7 +1005,13 @@ export let HiPS = (function () { hipsInitialFov: self.initialFov, hipsInitialRa: self.initialRa, hipsInitialDec: self.initialDec, + // HiPS Cube hipsCubeDepth: self.cubeDepth, + // HiPS3D + hipsTileDepth: self.hipsTileDepth, + hipsOrderFreq: self.hipsOrderFreq, + // Dataproduct type + dataproductType: self.dataproductType, isPlanetaryBody: self.isPlanetaryBody(), hipsBody: self.hipsBody, requestCredentials: self.requestCredentials,