diff --git a/src/core/Cargo.toml b/src/core/Cargo.toml index 43a2c0ef..5be66618 100644 --- a/src/core/Cargo.toml +++ b/src/core/Cargo.toml @@ -25,6 +25,7 @@ js-sys = "0.3.47" wasm-bindgen-futures = "0.4.20" cgmath = "*" cdshealpix = { package = "cdshealpix", git = 'https://github.com/cds-astro/cds-healpix-rust', branch = 'master' } +moclib = { package = "moc", version = "0.8.0" } serde = { version = "^1.0.59", features = ["derive"] } serde_json = "1.0" num = "*" diff --git a/src/core/src/app.rs b/src/core/src/app.rs index 35860685..5bee2567 100644 --- a/src/core/src/app.rs +++ b/src/core/src/app.rs @@ -597,6 +597,9 @@ where cfg.scale = metadata.value.scale; } } + Resource::MOC(moc) => { + // todo!() + } } } diff --git a/src/core/src/camera/viewport.rs b/src/core/src/camera/viewport.rs index dd7bc749..f26a9a0e 100644 --- a/src/core/src/camera/viewport.rs +++ b/src/core/src/camera/viewport.rs @@ -191,17 +191,17 @@ impl CameraViewPort { let mut tr_s = crate::math::projection::clip_to_screen_space(&tr_c, self); let mut br_s = crate::math::projection::clip_to_screen_space(&br_c, self); - tl_s.x *= (self.dpi as f64); - tl_s.y *= (self.dpi as f64); + tl_s.x *= self.dpi as f64; + tl_s.y *= self.dpi as f64; - tr_s.x *= (self.dpi as f64); - tr_s.y *= (self.dpi as f64); + tr_s.x *= self.dpi as f64; + tr_s.y *= self.dpi as f64; - br_s.x *= (self.dpi as f64); - br_s.y *= (self.dpi as f64); + br_s.x *= self.dpi as f64; + br_s.y *= self.dpi as f64; - let w = (tr_s.x - tl_s.x).min((self.width as f64)); - let h = (br_s.y - tr_s.y).min((self.height as f64)); + let w = (tr_s.x - tl_s.x).min(self.width as f64); + let h = (br_s.y - tr_s.y).min(self.height as f64); self.gl.scissor((tl_s.x as i32).max(0), (tl_s.y as i32).max(0), w as i32, h as i32); } diff --git a/src/core/src/downloader/mod.rs b/src/core/src/downloader/mod.rs index d1343c13..dbd13d7e 100644 --- a/src/core/src/downloader/mod.rs +++ b/src/core/src/downloader/mod.rs @@ -1,447 +1,5 @@ pub mod query; pub mod request; -/* -use super::{CompressedImageRequest, FitsImageRequest, ResolvedStatus, TileRequest}; -use crate::buffer::HiPSConfig; - -// A power of two maximum simultaneous tile requests -const NUM_EVENT_LISTENERS: usize = 32; - -struct RequestSystem { - reqs: Box<[Option; NUM_EVENT_LISTENERS]>, - //start_fits_req_idx: usize, - free_slots_idx: Vec, -} -use super::image::ImageRequestType; -use crate::buffer::image::ImageRequest; -use al_core::format::{R32F, RGB8U, RGBA8U}; - -#[cfg(feature = "webgl2")] -use al_core::format::{R16I, R32I, R8UI}; -use js_sys::Uint16Array; - -struct Tile { - time_req: Time, - cell: HEALPixCell, - - image: ImageResolved, -} - -impl RequestSystem { - fn new() -> Self { - let mut reqs: Vec> = Vec::with_capacity(NUM_EVENT_LISTENERS); - for _ in 0..NUM_EVENT_LISTENERS { - reqs.push(None); - } - - let reqs = reqs.into_boxed_slice(); - let reqs = unsafe { - Box::from_raw(Box::into_raw(reqs) as *mut [Option; NUM_EVENT_LISTENERS]) - }; - - let free_slots_idx = (0..NUM_EVENT_LISTENERS).into_iter().collect(); - - RequestSystem { - reqs, - free_slots_idx, - } - } - - fn check_send(&mut self, format: ImageFormatType) -> Option<&mut TileRequest> { - if self.free_slots_idx.is_empty() { - return None; - } - - let free_idx = self.free_slots_idx.pop().unwrap(); - self.reqs[free_idx] = Some(TileRequest::new(ImageRequestType::new(format))); - self.reqs[free_idx].as_mut() - } - - fn handle_received_tiles(&mut self) -> Vec<> { - /*let mut tiles_received = Vec::new(); - - for (idx, request) in self.reqs.iter_mut().enumerate() { - if let Some(request) = request.take() { - let TileRequest { - tile, - time_req, - status, - .. - } = request; - - /*let tile = resp.get_tile(); - let time_req = resp.get_time_request(); - let status = resp.resolve_status();*/ - let response = match status { - ResolvedStatus::Missing => { - Some(TileResolved::Missing { time_req }) - }, - ResolvedStatus::Found => { - let response = if let Some(survey) = surveys.get(&tile.root_url) { - let cfg = survey.get_config(); - if let Ok(image) = resp.get_image(cfg.get_tile_size()) { - TileResolved::Found { image, time_req } - } else { - TileResolved::Missing { time_req } - } - } else { - TileResolved::Missing { time_req } - }; - - Some(response) - }, - ResolvedStatus::NotResolved => None, - }; - - if let Some(resp) = response { - // Signals that the tile has been handled (copied for the GPU) - self.add_resolved_tile(tile, resp, surveys); - tiles_received.push(tile.clone()); - - // Free the request to be used to download a new tile - self.free_slots_idx.push(idx); - *req = None; - - break; // handle one tile per frame - } - } - } - - tiles_received*/ - let resolved = - for request in self.reqs.iter_mut() { - if let Some(request) = request { - let response = match request.get_status() { - ResolvedStatus::Missing => { - Some(ImageResolved::Missing) - }, - ResolvedStatus::Found => { - let response = if let Ok(image) = request.get_image() { - ImageResolved::Found { image } - } else { - ImageResolved::Missing - }; - - Some(response) - }, - ResolvedStatus::NotResolved => None, - }; - - if let Some(resp) = response { - - - // Free the request to be used to download a new tile - self.free_slots_idx.push(idx); - *request = None; - } - } - } - - if let Some(request) = request.take() { - - - /*let tile = resp.get_tile(); - let time_req = resp.get_time_request(); - let status = resp.resolve_status();*/ - - - if let Some(resp) = response { - // Signals that the tile has been handled (copied for the GPU) - self.add_resolved_tile(tile, resp, surveys); - tiles_received.push(tile.clone()); - - // Free the request to be used to download a new tile - self.free_slots_idx.push(idx); - *req = None; - - break; // handle one tile per frame - } - } - } - } - - fn add_resolved_tile(&mut self, tile: &Tile, status: TileResolved, surveys: &'mut ImageSurveys) { - if let Some(survey) = surveys.get_mut(&tile.root_url) { - match status { - TileResolved::Missing { time_req } => { - let missing = true; - - let cfg = survey.get_config(); - match cfg.get_format() { - ImageFormatType::RGBA8U { config } => { - let missing_tile_image = config.get_default_tile(); - survey.add_tile::>>( - &tile.cell, - missing_tile_image, - time_req, - missing, - ); - } - ImageFormatType::RGB8U { config } => { - let missing_tile_image = config.get_default_tile(); - survey.add_tile::>>( - &tile.cell, - missing_tile_image, - time_req, - missing, - ); - } - ImageFormatType::R32F { config } => { - let missing_tile_image = config.get_default_tile(); - survey.add_tile::>>( - &tile.cell, - missing_tile_image, - time_req, - missing, - ); - } - #[cfg(feature = "webgl2")] - ImageFormatType::R8UI { config } => { - let missing_tile_image = config.get_default_tile(); - survey.add_tile::>>( - &tile.cell, - missing_tile_image, - time_req, - missing, - ); - } - #[cfg(feature = "webgl2")] - ImageFormatType::R16I { config } => { - let missing_tile_image = config.get_default_tile(); - survey.add_tile::>>( - &tile.cell, - missing_tile_image, - time_req, - missing, - ); - } - #[cfg(feature = "webgl2")] - ImageFormatType::R32I { config } => { - let missing_tile_image = config.get_default_tile(); - survey.add_tile::>>( - &tile.cell, - missing_tile_image, - time_req, - missing, - ); - } - } - }, - TileResolved::Found { image, time_req } => { - let missing = false; - let cfg = survey.get_config_mut(); - match image { - RetrievedImageType::FitsImageR32f { image } => { - // update the metadata - cfg.set_fits_metadata( - image.bscale, - image.bzero, - image.blank, - ); - survey.add_tile::>(&tile.cell, image, time_req, missing); - } - #[cfg(feature = "webgl2")] - RetrievedImageType::FitsImageR32i { image } => { - cfg.set_fits_metadata( - image.bscale, - image.bzero, - image.blank, - ); - survey.add_tile::>(&tile.cell, image, time_req, missing); - } - #[cfg(feature = "webgl2")] - RetrievedImageType::FitsImageR16i { image } => { - cfg.set_fits_metadata( - image.bscale, - image.bzero, - image.blank, - ); - survey.add_tile::>(&tile.cell, image, time_req, missing); - } - #[cfg(feature = "webgl2")] - RetrievedImageType::FitsImageR8ui { image } => { - cfg.set_fits_metadata( - image.bscale, - image.bzero, - image.blank, - ); - survey.add_tile::>(&tile.cell, image, time_req, missing); - } - RetrievedImageType::PngImageRgba8u { image } => { - survey.add_tile::>(&tile.cell, image, time_req, missing); - } - RetrievedImageType::JpgImageRgb8u { image } => { - survey.add_tile::>(&tile.cell, image, time_req, missing); - } - } - } - } - } - } - - fn clear(&mut self) { - for req in self.reqs.iter_mut() { - *req = None; - } - self.free_slots_idx = (0..NUM_EVENT_LISTENERS).into_iter().collect(); - } -} - -use std::collections::{HashSet}; -pub struct TileDownloader { - // Waiting cells to be loaded - tiles_to_req: Vec, - base_tiles_to_req: Vec, - - requests: RequestSystem, - requested_tiles: HashSet, -} - -use al_core::format::ImageFormatType; - -use super::image::RetrievedImageType; -use crate::time::Time; -#[derive(Debug)] -pub enum ImageResolved { - Missing, - Found { - image: RetrievedImageType, - }, -} - -use std::collections::HashMap; -pub type ResolvedTiles = HashMap; - -use crate::ImageSurveys; -use al_core::log::*; -use wasm_bindgen::JsValue; -use crate::request::Request; -use wasm_bindgen::JsCast; -use crate::buffer::ImageBitmap; -use super::tile::Tile; -use crate::healpix_cell::HEALPixCell; -use web_sys::window; -impl TileDownloader { - pub fn new() -> TileDownloader { - let requests = RequestSystem::new(); - let tiles_to_req = Vec::new(); - let requested_tiles = HashSet::with_capacity(64); - let base_tiles_to_req = vec![]; - - Self { - tiles_to_req, - base_tiles_to_req, - - requests, - requested_tiles, - } - } - - pub fn clear_requests(&mut self) { - self.tiles_to_req.clear(); - self.base_tiles_to_req.clear(); - - self.requests.clear(); - self.requested_tiles.clear(); - } - - // Register further tile requests to launch - pub fn request_tiles(&mut self, tiles: Vec) { - // Remove the ancient requests - self.tiles_to_req.clear(); - - for tile in tiles.into_iter() { - self.request_tile(tile); - } - } - - fn request_tile(&mut self, tile: Tile /*, max_num_requested_tiles: usize*/) { - let already_requested = self.requested_tiles.contains(&tile); - // The cell is not already requested - if !already_requested { - if tile.is_root() { - self.base_tiles_to_req.push(tile); - } else { - self.tiles_to_req.push(tile); - } - } - } - - // Retrieve the tiles that have been resolved: - // Two possibilities: - // * The image have been found and retrieved - // * The image is missing - pub fn get_resolved_tiles( - &mut self, - surveys: &mut ImageSurveys, - ) -> bool { - let resolved_tiles = self.requests.handle_received_tiles(surveys); - for tile in resolved_tiles.iter() { - self.requested_tiles.remove(tile); - } - - !resolved_tiles.is_empty() - } - - pub fn try_sending_tile_requests(&mut self, surveys: &ImageSurveys) -> Result<(), JsValue> { - // Try sending the fits tile requests - let mut is_remaining_req = - !self.tiles_to_req.is_empty() || !self.base_tiles_to_req.is_empty(); - - let mut downloader_overloaded = false; - - while is_remaining_req && !downloader_overloaded { - let mut base_tile_requested = false; - let tile = if let Some(base_tile) = self.base_tiles_to_req.last() { - base_tile_requested = true; - base_tile - } else { - self.tiles_to_req.last().unwrap() - }; - //let tile = self.tiles_to_req.last(); - - if let Some(available_req) = self.requests.check_send(tile.format.clone()) { - let tile = if base_tile_requested { - // Send in priority requests to get the base tiles - self.base_tiles_to_req.pop().unwrap() - } else { - // Otherwise requests the other tiles - self.tiles_to_req.pop().unwrap() - }; - //let tile = self.tiles_to_req.pop().unwrap(); - - is_remaining_req = - !self.tiles_to_req.is_empty() || !self.base_tiles_to_req.is_empty(); - //is_remaining_req = !self.tiles_to_req.is_empty(); - self.requested_tiles.insert(tile.clone()); - - available_req.fetch(&tile, surveys)?; - } else { - // We have to wait for more requests - // to be available - downloader_overloaded = true; - } - } - - Ok(()) - } - - /*pub fn request_base_tiles(&mut self, config: &HiPSConfig) { - // Request base tiles - for idx in 0..12 { - let texture_cell = HEALPixCell(0, idx); - for cell in texture_cell.get_tile_cells(config) { - let tile = Tile { - root_url: config.root_url.clone(), - format: config.format(), - cell, - }; - - self.request_tile(tile); - } - } - }*/ -}*/ use crate::survey::Url; use std::collections::HashSet; diff --git a/src/core/src/downloader/query.rs b/src/core/src/downloader/query.rs index 3243632e..6ba72af9 100644 --- a/src/core/src/downloader/query.rs +++ b/src/core/src/downloader/query.rs @@ -128,3 +128,31 @@ impl Query for PixelMetadata { } } +/* ---------------------------------- */ +pub struct MOC { + // The root url of the HiPS + pub hips_url: Url, + // The total url of the query + pub url: Url, +} + +impl MOC { + pub fn new(cfg: &HiPSConfig) -> Self { + let hips_url = cfg.get_root_url().to_string(); + let url = format!("{}/Moc.fits", hips_url); + + MOC { + hips_url, + url, + } + } +} + +use super::request::moc::MOCRequest; +impl Query for MOC { + type Request = MOCRequest; + + fn url(&self) -> &Url { + &self.url + } +} \ No newline at end of file diff --git a/src/core/src/downloader/request/moc.rs b/src/core/src/downloader/request/moc.rs new file mode 100644 index 00000000..b015f030 --- /dev/null +++ b/src/core/src/downloader/request/moc.rs @@ -0,0 +1,130 @@ +use crate::{healpix::cell::HEALPixCell}; +use crate::downloader::query; + +use super::{Request, RequestType}; +use moclib::qty::Hpx; +use moclib::moc::range::RangeMOC; +use moclib::deser::fits::MocType; +pub struct MOCRequest { + pub hips_url: Url, + pub url: Url, + + request: Request>>, +} + +impl From for RequestType { + fn from(request: MOCRequest) -> Self { + RequestType::MOC(request) + } +} +use al_core::image::bitmap::Bitmap; +use al_core::image::html::HTMLImage; +use num::iter::Range; +use crate::survey::Url; +use wasm_bindgen_futures::JsFuture; +use web_sys::{Blob, RequestInit, RequestMode, Response}; +use wasm_bindgen::JsCast; +use moclib::deser::fits; + +use moclib::moc::range::op::convert::convert_to_u64; + +/// Convenient type for Space-MOCs +type SMOC = RangeMOC>; +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 std::io::Cursor; +use moclib::idx::Idx; +use moclib::moc::{RangeMOCIterator, CellMOCIntoIterator, CellMOCIterator}; +use moclib::deser::fits::MocIdxType; +use moclib::deser::fits::MocQtyType; +use wasm_bindgen::JsValue; +impl From for MOCRequest { + // Create a tile request associated to a HiPS + fn from(query: query::MOC) -> Self { + let query::MOC { + url, + hips_url, + } = query; + + let url_clone = url.clone(); + + let window = web_sys::window().unwrap(); + let request = Request::new(async move { + let mut opts = RequestInit::new(); + opts.method("GET"); + opts.mode(RequestMode::Cors); + + let request = web_sys::Request::new_with_str_and_init(&url_clone, &opts).unwrap(); + let resp_value = JsFuture::from(window.fetch_with_request(&request)).await?; + // `resp_value` is a `Response` object. + debug_assert!(resp_value.is_instance_of::()); + let resp: Response = resp_value.dyn_into()?; + let array_buffer = JsFuture::from(resp.array_buffer()?).await?; + + let bytes = js_sys::Uint8Array::new(&array_buffer).to_vec(); + let smoc = match fits::from_fits_ivoa(Cursor::new(&bytes[..])).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(smoc) + }); + + Self { + hips_url, + url, + request, + } + } +} + +use crate::time::Time; +use std::sync::{Arc, Mutex}; +pub struct MOC { + pub moc: Arc>>>>, + hips_url: Url, + url: Url, +} + +impl MOC { + pub fn get_hips_url(&self) -> &Url { + &self.hips_url + } + + pub fn get_url(&self) -> &Url { + &self.url + } +} + +impl<'a> From<&'a MOCRequest> for Option { + fn from(request: &'a MOCRequest) -> Self { + let MOCRequest { + request, + hips_url, + url, + } = request; + if request.is_resolved() { + let Request::>> { + data, .. + } = request; + Some(MOC { + // This is a clone on a Arc, it is supposed to be fast + moc: data.clone(), + hips_url: hips_url.clone(), + url: url.clone(), + }) + } else { + None + } + } +} diff --git a/src/core/src/downloader/request/mod.rs b/src/core/src/downloader/request/mod.rs index 84f3e73d..907a471c 100644 --- a/src/core/src/downloader/request/mod.rs +++ b/src/core/src/downloader/request/mod.rs @@ -3,9 +3,11 @@ pub mod allsky; pub mod tile; pub mod blank; +pub mod moc; /* ------------------------------------- */ +use crate::healpix::coverage::HEALPixCoverage; use crate::{time::Time}; use std::cell::Cell; use std::rc::Rc; @@ -79,10 +81,12 @@ where use allsky::AllskyRequest; use tile::TileRequest; use blank::PixelMetadataRequest; +use moc::MOCRequest; pub enum RequestType { Tile(TileRequest), Allsky(AllskyRequest), PixelMetadata(PixelMetadataRequest), + MOC(MOCRequest) //.. } @@ -93,6 +97,7 @@ impl RequestType { RequestType::Tile(request) => &request.url, RequestType::Allsky(request) => &request.url, RequestType::PixelMetadata(request) => &request.url, + RequestType::MOC(request) => &request.url, } } } @@ -109,6 +114,9 @@ impl<'a> From<&'a RequestType> for Option { RequestType::PixelMetadata(request) => { Option::::from(request).map(|metadata| Resource::PixelMetadata(metadata)) } + RequestType::MOC(request) => { + Option::::from(request).map(|moc| Resource::MOC(moc)) + } } } } @@ -116,11 +124,12 @@ impl<'a> From<&'a RequestType> for Option { use allsky::Allsky; use tile::Tile; use blank::PixelMetadata; - +use moc::MOC; pub enum Resource { Tile(Tile), Allsky(Allsky), - PixelMetadata(PixelMetadata) + PixelMetadata(PixelMetadata), + MOC(MOC) } impl Resource { @@ -129,6 +138,7 @@ impl Resource { Resource::Tile(tile) => tile.get_url(), Resource::Allsky(allsky) => allsky.get_url(), Resource::PixelMetadata(PixelMetadata { url, ..}) => url, + Resource::MOC(moc) => moc.get_url(), } } }