use std::{
collections::BTreeMap,
error::Error,
fs::File,
io::{BufReader, BufWriter, Read, Seek, Write},
ops::{Add, AddAssign, Deref, RangeInclusive},
path::Path,
};
use colorous::Gradient;
use log::warn;
use num_traits::{ToBytes, Zero};
use mapproj::CanonicalProjection;
#[cfg(not(target_arch = "wasm32"))]
use super::img::show_with_default_app;
use crate::nested::{
map::{
fits::{
error::FitsError,
read::from_fits_skymap,
write::{write_explicit_skymap_fits, write_implicit_skymap_fits},
},
img::{to_skymap_png, ColorMapFunctionType, PosConversion},
mom::{CountMom, DensMom},
skymap::{
explicit::{ExplicitCountMap, ExplicitDensityMap, ExplicitSkyMapBTree},
implicit::{
ImplicitCountMap, ImplicitCountMapU32, ImplicitDensityMap, ImplicitDensityMapU32,
ImplicitSkyMapArray,
},
},
HHash,
},
n_hash,
};
pub mod explicit;
pub mod implicit;
pub trait SkyMapValue: ToBytes + Add + AddAssign + Clone + Zero + PartialEq {
const FITS_NAXIS1: u8 = size_of::<Self>() as u8;
const FITS_TFORM: &'static str;
const FITS_DATATYPE: &'static str;
}
impl SkyMapValue for u8 {
const FITS_TFORM: &'static str = "B";
const FITS_DATATYPE: &'static str = "u8";
}
impl SkyMapValue for i16 {
const FITS_TFORM: &'static str = "B";
const FITS_DATATYPE: &'static str = "i16";
}
impl SkyMapValue for i32 {
const FITS_TFORM: &'static str = "J";
const FITS_DATATYPE: &'static str = "i32";
}
impl SkyMapValue for i64 {
const FITS_TFORM: &'static str = "K";
const FITS_DATATYPE: &'static str = "i64";
}
impl SkyMapValue for u32 {
const FITS_TFORM: &'static str = "J";
const FITS_DATATYPE: &'static str = "u32";
}
impl SkyMapValue for u64 {
const FITS_TFORM: &'static str = "K";
const FITS_DATATYPE: &'static str = "u64";
}
impl SkyMapValue for f32 {
const FITS_TFORM: &'static str = "E";
const FITS_DATATYPE: &'static str = "f32";
}
impl SkyMapValue for f64 {
const FITS_TFORM: &'static str = "D";
const FITS_DATATYPE: &'static str = "f64";
}
#[allow(clippy::len_without_is_empty)]
pub trait SkyMap<'a> {
type HashType: HHash;
type ValueType: 'a + SkyMapValue;
type ValuesIt: Iterator<Item = &'a Self::ValueType>;
type EntriesIt: Iterator<Item = (Self::HashType, &'a Self::ValueType)>;
type OwnedEntriesIt: Iterator<Item = (Self::HashType, Self::ValueType)>;
fn depth(&self) -> u8;
fn is_implicit(&self) -> bool;
fn implicit_byte_size(&self) -> usize {
size_of::<Self::ValueType>()
* if self.is_implicit() {
self.len()
} else {
n_hash(self.depth()) as usize
}
}
fn explicit_byte_size(&'a self, null_value: Self::ValueType) -> usize {
(size_of::<Self::HashType>() + size_of::<Self::ValueType>())
* if self.is_implicit() {
let mut n = 0;
for v in self.values() {
if null_value.ne(v) {
n += 1;
}
}
n
} else {
self.len()
}
}
fn is_implicit_the_best_representation(
&'a self,
null_value: Self::ValueType,
impl_over_expl_limit_ratio: f64,
) -> bool {
(self.implicit_byte_size() as f64)
< self.explicit_byte_size(null_value) as f64 * impl_over_expl_limit_ratio
}
fn len(&self) -> usize;
fn get(&self, hash: Self::HashType) -> &Self::ValueType;
fn values(&'a self) -> Self::ValuesIt;
fn entries(&'a self) -> Self::EntriesIt;
fn owned_entries(self) -> Self::OwnedEntriesIt;
}
pub trait DegradableBySumming {
type Degraded: Sized;
fn degrade_sum(self, new_depth: u8) -> Self::Degraded;
}
#[derive(Default)]
pub struct NullValueProvider {
null_value_u8: u8,
null_value_i16: i16,
null_value_i32: i32,
null_value_i64: i64,
null_value_f32: f32,
null_value_f64: f64,
}
impl NullValueProvider {
pub fn set_null_value_u8(mut self, null_value_u8: u8) -> Self {
self.null_value_u8 = null_value_u8;
self
}
pub fn set_null_value_i16(mut self, null_value_i16: i16) -> Self {
self.null_value_i16 = null_value_i16;
self
}
pub fn set_null_value_i32(mut self, null_value_i32: i32) -> Self {
self.null_value_i32 = null_value_i32;
self
}
pub fn set_null_value_i64(mut self, null_value_i64: i64) -> Self {
self.null_value_i64 = null_value_i64;
self
}
pub fn set_null_value_f32(mut self, null_value_f32: f32) -> Self {
self.null_value_f32 = null_value_f32;
self
}
pub fn set_null_value_f64(mut self, null_value_f64: f64) -> Self {
self.null_value_f64 = null_value_f64;
self
}
pub fn null_value_u8(&self) -> u8 {
self.null_value_u8
}
pub fn null_value_i16(&self) -> i16 {
self.null_value_i16
}
pub fn null_value_i32(&self) -> i32 {
self.null_value_i32
}
pub fn null_value_i64(&self) -> i64 {
self.null_value_i64
}
pub fn null_value_f32(&self) -> f32 {
self.null_value_f32
}
pub fn null_value_f64(&self) -> f64 {
self.null_value_f64
}
}
#[derive(Debug)]
pub enum SkyMapEnum {
ImplicitU64U8(ImplicitSkyMapArray<u64, u8>),
ImplicitU64I16(ImplicitSkyMapArray<u64, i16>),
ImplicitU64I32(ImplicitSkyMapArray<u64, i32>),
ImplicitU64I64(ImplicitSkyMapArray<u64, i64>),
ImplicitU64F32(ImplicitSkyMapArray<u64, f32>),
ImplicitU64F64(ImplicitSkyMapArray<u64, f64>),
ExplicitU32U8(ExplicitSkyMapBTree<u32, u8>),
ExplicitU32I16(ExplicitSkyMapBTree<u32, i16>),
ExplicitU32I32(ExplicitSkyMapBTree<u32, i32>),
ExplicitU32I64(ExplicitSkyMapBTree<u32, i64>),
ExplicitU32F32(ExplicitSkyMapBTree<u32, f32>),
ExplicitU32F64(ExplicitSkyMapBTree<u32, f64>),
ExplicitU64U8(ExplicitSkyMapBTree<u64, u8>),
ExplicitU64I16(ExplicitSkyMapBTree<u64, i16>),
ExplicitU64I32(ExplicitSkyMapBTree<u64, i32>),
ExplicitU64I64(ExplicitSkyMapBTree<u64, i64>),
ExplicitU64F32(ExplicitSkyMapBTree<u64, f32>),
ExplicitU64F64(ExplicitSkyMapBTree<u64, f64>),
}
impl SkyMapEnum {
#[cfg(not(target_arch = "wasm32"))]
pub fn from_fits_file<P: AsRef<Path>>(path: P) -> Result<Self, FitsError> {
File::open(path)
.map_err(FitsError::Io)
.map(BufReader::new)
.and_then(SkyMapEnum::from_fits)
}
pub fn from_fits<R: Read + Seek>(reader: BufReader<R>) -> Result<Self, FitsError> {
from_fits_skymap(reader)
}
pub fn is_implicit_the_best_representation(
&self,
null_value_provided: NullValueProvider,
impl_over_expl_limit_ratio: f64,
) -> bool {
match &self {
Self::ImplicitU64U8(e) => e.is_implicit_the_best_representation(
null_value_provided.null_value_u8,
impl_over_expl_limit_ratio,
),
Self::ImplicitU64I16(e) => e.is_implicit_the_best_representation(
null_value_provided.null_value_i16,
impl_over_expl_limit_ratio,
),
Self::ImplicitU64I32(e) => e.is_implicit_the_best_representation(
null_value_provided.null_value_i32,
impl_over_expl_limit_ratio,
),
Self::ImplicitU64I64(e) => e.is_implicit_the_best_representation(
null_value_provided.null_value_i64,
impl_over_expl_limit_ratio,
),
Self::ImplicitU64F32(e) => e.is_implicit_the_best_representation(
null_value_provided.null_value_f32,
impl_over_expl_limit_ratio,
),
Self::ImplicitU64F64(e) => e.is_implicit_the_best_representation(
null_value_provided.null_value_f64,
impl_over_expl_limit_ratio,
),
Self::ExplicitU32U8(e) => e.is_implicit_the_best_representation(
null_value_provided.null_value_u8,
impl_over_expl_limit_ratio,
),
Self::ExplicitU32I16(e) => e.is_implicit_the_best_representation(
null_value_provided.null_value_i16,
impl_over_expl_limit_ratio,
),
Self::ExplicitU32I32(e) => e.is_implicit_the_best_representation(
null_value_provided.null_value_i32,
impl_over_expl_limit_ratio,
),
Self::ExplicitU32I64(e) => e.is_implicit_the_best_representation(
null_value_provided.null_value_i64,
impl_over_expl_limit_ratio,
),
Self::ExplicitU32F32(e) => e.is_implicit_the_best_representation(
null_value_provided.null_value_f32,
impl_over_expl_limit_ratio,
),
Self::ExplicitU32F64(e) => e.is_implicit_the_best_representation(
null_value_provided.null_value_f64,
impl_over_expl_limit_ratio,
),
Self::ExplicitU64U8(e) => e.is_implicit_the_best_representation(
null_value_provided.null_value_u8,
impl_over_expl_limit_ratio,
),
Self::ExplicitU64I16(e) => e.is_implicit_the_best_representation(
null_value_provided.null_value_i16,
impl_over_expl_limit_ratio,
),
Self::ExplicitU64I32(e) => e.is_implicit_the_best_representation(
null_value_provided.null_value_i32,
impl_over_expl_limit_ratio,
),
Self::ExplicitU64I64(e) => e.is_implicit_the_best_representation(
null_value_provided.null_value_i64,
impl_over_expl_limit_ratio,
),
Self::ExplicitU64F32(e) => e.is_implicit_the_best_representation(
null_value_provided.null_value_f32,
impl_over_expl_limit_ratio,
),
Self::ExplicitU64F64(e) => e.is_implicit_the_best_representation(
null_value_provided.null_value_f64,
impl_over_expl_limit_ratio,
),
}
}
pub fn to_implicit(self, null_value_provided: NullValueProvider) -> Self {
match self {
Self::ImplicitU64U8(_) => self,
Self::ImplicitU64I16(_) => self,
Self::ImplicitU64I32(_) => self,
Self::ImplicitU64I64(_) => self,
Self::ImplicitU64F32(_) => self,
Self::ImplicitU64F64(_) => self,
Self::ExplicitU32U8(e) => Self::ImplicitU64U8(
e.into_implicit_map(null_value_provided.null_value_u8)
.into(),
),
Self::ExplicitU32I16(e) => Self::ImplicitU64I16(
e.into_implicit_map(null_value_provided.null_value_i16)
.into(),
),
Self::ExplicitU32I32(e) => Self::ImplicitU64I32(
e.into_implicit_map(null_value_provided.null_value_i32)
.into(),
),
Self::ExplicitU32I64(e) => Self::ImplicitU64I64(
e.into_implicit_map(null_value_provided.null_value_i64)
.into(),
),
Self::ExplicitU32F32(e) => Self::ImplicitU64F32(
e.into_implicit_map(null_value_provided.null_value_f32)
.into(),
),
Self::ExplicitU32F64(e) => Self::ImplicitU64F64(
e.into_implicit_map(null_value_provided.null_value_f64)
.into(),
),
Self::ExplicitU64U8(e) => {
Self::ImplicitU64U8(e.into_implicit_map(null_value_provided.null_value_u8))
}
Self::ExplicitU64I16(e) => {
Self::ImplicitU64I16(e.into_implicit_map(null_value_provided.null_value_i16))
}
Self::ExplicitU64I32(e) => {
Self::ImplicitU64I32(e.into_implicit_map(null_value_provided.null_value_i32))
}
Self::ExplicitU64I64(e) => {
Self::ImplicitU64I64(e.into_implicit_map(null_value_provided.null_value_i64))
}
Self::ExplicitU64F32(e) => {
Self::ImplicitU64F32(e.into_implicit_map(null_value_provided.null_value_f32))
}
Self::ExplicitU64F64(e) => {
Self::ImplicitU64F64(e.into_implicit_map(null_value_provided.null_value_f64))
}
}
}
pub fn to_explicit(self, null_value_provided: NullValueProvider) -> Self {
match self {
Self::ImplicitU64U8(e) => {
Self::ExplicitU64U8(e.into_explicit_map(null_value_provided.null_value_u8))
}
Self::ImplicitU64I16(e) => {
Self::ExplicitU64I16(e.into_explicit_map(null_value_provided.null_value_i16))
}
Self::ImplicitU64I32(e) => {
Self::ExplicitU64I32(e.into_explicit_map(null_value_provided.null_value_i32))
}
Self::ImplicitU64I64(e) => {
Self::ExplicitU64I64(e.into_explicit_map(null_value_provided.null_value_i64))
}
Self::ImplicitU64F32(e) => {
Self::ExplicitU64F32(e.into_explicit_map(null_value_provided.null_value_f32))
}
Self::ImplicitU64F64(e) => {
Self::ExplicitU64F64(e.into_explicit_map(null_value_provided.null_value_f64))
}
Self::ExplicitU32U8(_) => self,
Self::ExplicitU32I16(_) => self,
Self::ExplicitU32I32(_) => self,
Self::ExplicitU32I64(_) => self,
Self::ExplicitU32F32(_) => self,
Self::ExplicitU32F64(_) => self,
Self::ExplicitU64U8(_) => self,
Self::ExplicitU64I16(_) => self,
Self::ExplicitU64I32(_) => self,
Self::ExplicitU64I64(_) => self,
Self::ExplicitU64F32(_) => self,
Self::ExplicitU64F64(_) => self,
}
}
pub fn to_count_map(self) -> Result<CountMap, String> {
self.try_into()
}
pub fn to_dens_map(self) -> Result<ImplicitDensityMap, String> {
match self {
Self::ImplicitU64F64(skymap) => {
Ok(ImplicitSkyMapArray::new(skymap.depth, skymap.values).into())
}
_ => Err(String::from("Unable to convert to count map.")),
}
}
pub fn par_add(
self,
rhs: Self,
null_value_provider: NullValueProvider,
) -> Result<Self, FitsError> {
match (self, rhs) {
(Self::ImplicitU64I32(l), Self::ImplicitU64I32(r)) => Ok(Self::ImplicitU64I32(l.par_add(r))),
(Self::ImplicitU64F32(l), Self::ImplicitU64F32(r)) => Ok(Self::ImplicitU64F32(l.par_add(r))),
(Self::ImplicitU64F64(l), Self::ImplicitU64F64(r)) => Ok(Self::ImplicitU64F64(l.par_add(r))),
(Self::ImplicitU64I32(l), Self::ExplicitU32I32(r))
| (Self::ExplicitU32I32(r), Self::ImplicitU64I32(l)) => Ok(Self::ImplicitU64I32(
l.par_add(
r.into_u64_hash()
.into_implicit_map(null_value_provider.null_value_i32),
),
)),
(Self::ImplicitU64F32(l), Self::ExplicitU32F32(r))
| (Self::ExplicitU32F32(r), Self::ImplicitU64F32(l)) => Ok(Self::ImplicitU64F32(
l.par_add(
r.into_u64_hash()
.into_implicit_map(null_value_provider.null_value_f32),
),
)),
(Self::ImplicitU64F64(l), Self::ExplicitU32F64(r))
| (Self::ExplicitU32F64(r), Self::ImplicitU64F64(l)) => Ok(Self::ImplicitU64F64(
l.par_add(
r.into_u64_hash()
.into_implicit_map(null_value_provider.null_value_f64),
),
)),
(Self::ImplicitU64I32(l), Self::ExplicitU64I32(r))
| (Self::ExplicitU64I32(r), Self::ImplicitU64I32(l)) => Ok(Self::ImplicitU64I32(
l.par_add(r.into_implicit_map(null_value_provider.null_value_i32)),
)),
(Self::ImplicitU64F32(l), Self::ExplicitU64F32(r))
| (Self::ExplicitU64F32(r), Self::ImplicitU64F32(l)) => Ok(Self::ImplicitU64F32(
l.par_add(r.into_implicit_map(null_value_provider.null_value_f32)),
)),
(Self::ImplicitU64F64(l), Self::ExplicitU64F64(r))
| (Self::ExplicitU64F64(r), Self::ImplicitU64F64(l)) => Ok(Self::ImplicitU64F64(
l.par_add(r.into_implicit_map(null_value_provider.null_value_f64)),
)),
(Self::ExplicitU32I32(l), Self::ExplicitU32I32(r)) => Ok(Self::ExplicitU32I32(l.par_add(r))),
(Self::ExplicitU32F32(l), Self::ExplicitU32F32(r)) => Ok(Self::ExplicitU32F32(l.par_add(r))),
(Self::ExplicitU32F64(l), Self::ExplicitU32F64(r)) => Ok(Self::ExplicitU32F64(l.par_add(r))),
(Self::ExplicitU64I32(l), Self::ExplicitU64I32(r)) => Ok(Self::ExplicitU64I32(l.par_add(r))),
(Self::ExplicitU64F32(l), Self::ExplicitU64F32(r)) => Ok(Self::ExplicitU64F32(l.par_add(r))),
(Self::ExplicitU64F64(l), Self::ExplicitU64F64(r)) => Ok(Self::ExplicitU64F64(l.par_add(r))),
(Self::ExplicitU64I32(l), Self::ExplicitU32I32(r))
| (Self::ExplicitU32I32(r), Self::ExplicitU64I32(l)) => {
Ok(Self::ExplicitU64I32(l.par_add(r.into_u64_hash())))
}
(Self::ExplicitU64F32(l), Self::ExplicitU32F32(r))
| (Self::ExplicitU32F32(r), Self::ExplicitU64F32(l)) => {
Ok(Self::ExplicitU64F32(l.par_add(r.into_u64_hash())))
}
(Self::ExplicitU64F64(l), Self::ExplicitU32F64(r))
| (Self::ExplicitU32F64(r), Self::ExplicitU64F64(l)) => {
Ok(Self::ExplicitU64F64(l.par_add(r.into_u64_hash())))
}
_ => Err(FitsError::Custom {
msg: format!("Skymap type not supported or not the same in 'add' operation"),
}),
}
}
pub fn degraded_sum(self, depth: u8) -> Self {
match self {
Self::ImplicitU64U8(s) => Self::ImplicitU64U8(s.degrade_sum(depth)),
Self::ImplicitU64I16(s) => Self::ImplicitU64I16(s.degrade_sum(depth)),
Self::ImplicitU64I32(s) => Self::ImplicitU64I32(s.degrade_sum(depth)),
Self::ImplicitU64I64(s) => Self::ImplicitU64I64(s.degrade_sum(depth)),
Self::ImplicitU64F32(s) => Self::ImplicitU64F32(s.degrade_sum(depth)),
Self::ImplicitU64F64(s) => Self::ImplicitU64F64(s.degrade_sum(depth)),
Self::ExplicitU32U8(s) => Self::ExplicitU32U8(s.degrade_sum(depth)),
Self::ExplicitU32I16(s) => Self::ExplicitU32I16(s.degrade_sum(depth)),
Self::ExplicitU32I32(s) => Self::ExplicitU32I32(s.degrade_sum(depth)),
Self::ExplicitU32I64(s) => Self::ExplicitU32I64(s.degrade_sum(depth)),
Self::ExplicitU32F32(s) => Self::ExplicitU32F32(s.degrade_sum(depth)),
Self::ExplicitU32F64(s) => Self::ExplicitU32F64(s.degrade_sum(depth)),
Self::ExplicitU64U8(s) => Self::ExplicitU64U8(s.degrade_sum(depth)),
Self::ExplicitU64I16(s) => Self::ExplicitU64I16(s.degrade_sum(depth)),
Self::ExplicitU64I32(s) => Self::ExplicitU64I32(s.degrade_sum(depth)),
Self::ExplicitU64I64(s) => Self::ExplicitU64I64(s.degrade_sum(depth)),
Self::ExplicitU64F32(s) => Self::ExplicitU64F32(s.degrade_sum(depth)),
Self::ExplicitU64F64(s) => Self::ExplicitU64F64(s.degrade_sum(depth)),
}
}
pub fn depth(&self) -> u8 {
match &self {
Self::ImplicitU64U8(s) => s.depth(),
Self::ImplicitU64I16(s) => s.depth(),
Self::ImplicitU64I32(s) => s.depth(),
Self::ImplicitU64I64(s) => s.depth(),
Self::ImplicitU64F32(s) => s.depth(),
Self::ImplicitU64F64(s) => s.depth(),
Self::ExplicitU32U8(s) => s.depth(),
Self::ExplicitU32I16(s) => s.depth(),
Self::ExplicitU32I32(s) => s.depth(),
Self::ExplicitU32I64(s) => s.depth(),
Self::ExplicitU32F32(s) => s.depth(),
Self::ExplicitU32F64(s) => s.depth(),
Self::ExplicitU64U8(s) => s.depth(),
Self::ExplicitU64I16(s) => s.depth(),
Self::ExplicitU64I32(s) => s.depth(),
Self::ExplicitU64I64(s) => s.depth(),
Self::ExplicitU64F32(s) => s.depth(),
Self::ExplicitU64F64(s) => s.depth(),
}
}
pub fn to_fits<W: Write>(&self, writer: W) -> Result<(), FitsError> {
match &self {
Self::ImplicitU64U8(s) => write_implicit_skymap_fits(writer, s.values.deref()),
Self::ImplicitU64I16(s) => write_implicit_skymap_fits(writer, s.values.deref()),
Self::ImplicitU64I32(s) => write_implicit_skymap_fits(writer, s.values.deref()),
Self::ImplicitU64I64(s) => write_implicit_skymap_fits(writer, s.values.deref()),
Self::ImplicitU64F32(s) => write_implicit_skymap_fits(writer, s.values.deref()),
Self::ImplicitU64F64(s) => write_implicit_skymap_fits(writer, s.values.deref()),
Self::ExplicitU32U8(s) => write_explicit_skymap_fits(writer, s),
Self::ExplicitU32I16(s) => write_explicit_skymap_fits(writer, s),
Self::ExplicitU32I32(s) => write_explicit_skymap_fits(writer, s),
Self::ExplicitU32I64(s) => write_explicit_skymap_fits(writer, s),
Self::ExplicitU32F32(s) => write_explicit_skymap_fits(writer, s),
Self::ExplicitU32F64(s) => write_explicit_skymap_fits(writer, s),
Self::ExplicitU64U8(s) => write_explicit_skymap_fits(writer, s),
Self::ExplicitU64I16(s) => write_explicit_skymap_fits(writer, s),
Self::ExplicitU64I32(s) => write_explicit_skymap_fits(writer, s),
Self::ExplicitU64I64(s) => write_explicit_skymap_fits(writer, s),
Self::ExplicitU64F32(s) => write_explicit_skymap_fits(writer, s),
Self::ExplicitU64F64(s) => write_explicit_skymap_fits(writer, s),
}
}
pub fn to_fits_file<P: AsRef<Path>>(&self, path: P) -> Result<(), FitsError> {
File::create(path)
.map_err(FitsError::Io)
.and_then(|file| self.to_fits(BufWriter::new(file)))
}
#[cfg(not(target_arch = "wasm32"))]
pub fn to_skymap_png_file<P: CanonicalProjection, W: AsRef<Path>>(
&self,
img_size: (u16, u16),
proj: Option<P>,
proj_center: Option<(f64, f64)>,
proj_bounds: Option<(RangeInclusive<f64>, RangeInclusive<f64>)>,
pos_convert: Option<PosConversion>,
color_map: Option<Gradient>,
color_map_func_type: Option<ColorMapFunctionType>,
path: W,
view: bool,
) -> Result<(), Box<dyn Error>> {
File::create(path.as_ref())
.map_err(|e| e.into())
.map(BufWriter::new)
.and_then(|mut writer| {
self.to_skymap_png(
img_size,
proj,
proj_center,
proj_bounds,
pos_convert,
color_map,
color_map_func_type,
&mut writer,
)
})
.and_then(|()| {
if view {
show_with_default_app(path.as_ref().to_string_lossy().as_ref()).map_err(|e| e.into())
} else {
Ok(())
}
})
}
pub fn to_skymap_png<P: CanonicalProjection, W: Write>(
&self,
img_size: (u16, u16),
proj: Option<P>,
proj_center: Option<(f64, f64)>,
proj_bounds: Option<(RangeInclusive<f64>, RangeInclusive<f64>)>,
pos_convert: Option<PosConversion>,
color_map: Option<Gradient>,
color_map_func_type: Option<ColorMapFunctionType>,
writer: W,
) -> Result<(), Box<dyn Error>> {
match &self {
Self::ImplicitU64U8(s) => to_skymap_png(
s,
img_size,
proj,
proj_center,
proj_bounds,
pos_convert,
color_map,
color_map_func_type,
writer,
),
Self::ImplicitU64I16(s) => to_skymap_png(
s,
img_size,
proj,
proj_center,
proj_bounds,
pos_convert,
color_map,
color_map_func_type,
writer,
),
Self::ImplicitU64I32(s) => to_skymap_png(
s,
img_size,
proj,
proj_center,
proj_bounds,
pos_convert,
color_map,
color_map_func_type,
writer,
),
Self::ImplicitU64I64(s) => to_skymap_png(
s,
img_size,
proj,
proj_center,
proj_bounds,
pos_convert,
color_map,
color_map_func_type,
writer,
),
Self::ImplicitU64F32(s) => to_skymap_png(
s,
img_size,
proj,
proj_center,
proj_bounds,
pos_convert,
color_map,
color_map_func_type,
writer,
),
Self::ImplicitU64F64(s) => to_skymap_png(
s,
img_size,
proj,
proj_center,
proj_bounds,
pos_convert,
color_map,
color_map_func_type,
writer,
),
Self::ExplicitU32U8(s) => to_skymap_png(
s,
img_size,
proj,
proj_center,
proj_bounds,
pos_convert,
color_map,
color_map_func_type,
writer,
),
Self::ExplicitU32I16(s) => to_skymap_png(
s,
img_size,
proj,
proj_center,
proj_bounds,
pos_convert,
color_map,
color_map_func_type,
writer,
),
Self::ExplicitU32I32(s) => to_skymap_png(
s,
img_size,
proj,
proj_center,
proj_bounds,
pos_convert,
color_map,
color_map_func_type,
writer,
),
Self::ExplicitU32I64(s) => to_skymap_png(
s,
img_size,
proj,
proj_center,
proj_bounds,
pos_convert,
color_map,
color_map_func_type,
writer,
),
Self::ExplicitU32F32(s) => to_skymap_png(
s,
img_size,
proj,
proj_center,
proj_bounds,
pos_convert,
color_map,
color_map_func_type,
writer,
),
Self::ExplicitU32F64(s) => to_skymap_png(
s,
img_size,
proj,
proj_center,
proj_bounds,
pos_convert,
color_map,
color_map_func_type,
writer,
),
Self::ExplicitU64U8(s) => to_skymap_png(
s,
img_size,
proj,
proj_center,
proj_bounds,
pos_convert,
color_map,
color_map_func_type,
writer,
),
Self::ExplicitU64I16(s) => to_skymap_png(
s,
img_size,
proj,
proj_center,
proj_bounds,
pos_convert,
color_map,
color_map_func_type,
writer,
),
Self::ExplicitU64I32(s) => to_skymap_png(
s,
img_size,
proj,
proj_center,
proj_bounds,
pos_convert,
color_map,
color_map_func_type,
writer,
),
Self::ExplicitU64I64(s) => to_skymap_png(
s,
img_size,
proj,
proj_center,
proj_bounds,
pos_convert,
color_map,
color_map_func_type,
writer,
),
Self::ExplicitU64F32(s) => to_skymap_png(
s,
img_size,
proj,
proj_center,
proj_bounds,
pos_convert,
color_map,
color_map_func_type,
writer,
),
Self::ExplicitU64F64(s) => to_skymap_png(
s,
img_size,
proj,
proj_center,
proj_bounds,
pos_convert,
color_map,
color_map_func_type,
writer,
),
}
}
}
pub enum CountMap {
ImplicitU32(ImplicitCountMapU32),
Implicit(ImplicitCountMap),
ExplicitU32(ExplicitCountMap<u32>),
Explicit(ExplicitCountMap<u64>),
}
impl CountMap {
pub fn to_dens_map(&self) -> DensMap {
match self {
Self::ImplicitU32(cmap) => cmap.to_dens_map().into(),
Self::Implicit(cmap) => cmap.to_dens_map().into(),
Self::ExplicitU32(cmap) => cmap.to_dens_map().into(),
Self::Explicit(cmap) => cmap.to_dens_map().into(),
}
}
pub fn to_dens_map_par(&self) -> DensMap {
match self {
Self::ImplicitU32(cmap) => cmap.to_dens_map_par().into(),
Self::Implicit(cmap) => cmap.to_dens_map_par().into(),
Self::ExplicitU32(cmap) => cmap.to_dens_map_par().into(),
Self::Explicit(cmap) => cmap.to_dens_map_par().into(),
}
}
pub fn to_upper_count_threshold_mom(self, upper_count_threshold: u32) -> CountMom {
match self {
Self::ImplicitU32(m) => m.to_upper_count_threshold_mom(upper_count_threshold).into(),
Self::Implicit(m) => m.to_upper_count_threshold_mom(upper_count_threshold).into(),
Self::ExplicitU32(m) => m
.into_implicit_skymap(0_u32)
.to_upper_count_threshold_mom(upper_count_threshold)
.into(),
Self::Explicit(m) => m
.into_implicit_skymap(0_u32)
.to_upper_count_threshold_mom(upper_count_threshold)
.into(),
}
}
pub fn to_chi2_mom(self, chi2_of_3dof_threshold: f64, depth_threshold: Option<u8>) -> CountMom {
match self {
Self::ImplicitU32(m) => m
.to_chi2_count_mom(chi2_of_3dof_threshold, depth_threshold)
.into(),
Self::Implicit(m) => m
.to_chi2_count_mom(chi2_of_3dof_threshold, depth_threshold)
.into(),
Self::ExplicitU32(m) => m
.into_implicit_skymap(0_u32)
.to_chi2_count_mom(chi2_of_3dof_threshold, depth_threshold)
.into(),
Self::Explicit(m) => m
.into_implicit_skymap(0_u32)
.to_chi2_count_mom(chi2_of_3dof_threshold, depth_threshold)
.into(),
}
}
pub fn to_chi2_dens_mom(
self,
chi2_of_3dof_threshold: f64,
depth_threshold: Option<u8>,
) -> DensMom {
match self {
Self::ImplicitU32(m) => m
.to_chi2_dens_mom(chi2_of_3dof_threshold, depth_threshold)
.into(),
Self::Implicit(m) => m
.to_chi2_dens_mom(chi2_of_3dof_threshold, depth_threshold)
.into(),
Self::ExplicitU32(m) => m
.into_implicit_skymap(0_u32)
.to_chi2_dens_mom(chi2_of_3dof_threshold, depth_threshold)
.into(),
Self::Explicit(m) => m
.into_implicit_skymap(0_u32)
.to_chi2_dens_mom(chi2_of_3dof_threshold, depth_threshold)
.into(),
}
}
pub fn to_fits<W: Write>(&self, writer: W) -> Result<(), FitsError> {
match self {
Self::ImplicitU32(m) => m.to_fits(writer),
Self::Implicit(m) => m.to_fits(writer),
Self::ExplicitU32(m) => m.to_fits(writer),
Self::Explicit(m) => m.to_fits(writer),
}
}
pub fn to_fits_file<P: AsRef<Path>>(&self, path: P) -> Result<(), FitsError> {
match self {
Self::ImplicitU32(m) => m.to_fits_file(path),
Self::Implicit(m) => m.to_fits_file(path),
Self::ExplicitU32(m) => m.to_fits_file(path),
Self::Explicit(m) => m.to_fits_file(path),
}
}
}
impl From<ImplicitSkyMapArray<u64, u32>> for CountMap {
fn from(value: ImplicitSkyMapArray<u64, u32>) -> Self {
Self::Implicit(value.into())
}
}
impl From<ImplicitSkyMapArray<u32, u32>> for CountMap {
fn from(value: ImplicitSkyMapArray<u32, u32>) -> Self {
Self::ImplicitU32(value.into())
}
}
impl From<ExplicitSkyMapBTree<u64, u32>> for CountMap {
fn from(value: ExplicitSkyMapBTree<u64, u32>) -> Self {
Self::Explicit(value.into())
}
}
impl From<ExplicitSkyMapBTree<u32, u32>> for CountMap {
fn from(value: ExplicitSkyMapBTree<u32, u32>) -> Self {
Self::ExplicitU32(value.into())
}
}
impl TryFrom<SkyMapEnum> for CountMap {
type Error = String;
fn try_from(value: SkyMapEnum) -> Result<Self, Self::Error> {
match value {
SkyMapEnum::ImplicitU64I32(skymap) => {
let values = unsafe { std::mem::transmute::<Box<[i32]>, Box<[u32]>>(skymap.values) };
Ok(if skymap.depth <= 13 {
ImplicitSkyMapArray::<u32, u32>::new(skymap.depth, values).into()
} else {
ImplicitSkyMapArray::<u64, u32>::new(skymap.depth, values).into()
})
}
SkyMapEnum::ImplicitU64I64(skymap) => {
warn!("Transforms i64 skymap into u32 skymap without checking possible overflow!");
let values = skymap.values.iter().map(|&v| v as u32).collect();
Ok(if skymap.depth <= 13 {
ImplicitSkyMapArray::<u32, u32>::new(skymap.depth, values).into()
} else {
ImplicitSkyMapArray::<u64, u32>::new(skymap.depth, values).into()
})
}
,
SkyMapEnum::ExplicitU64I32(skymap) => {
let depth = skymap.depth();
let btree = unsafe { std::mem::transmute::<_, BTreeMap<u64, u32>>(skymap.entries) };
Ok(ExplicitSkyMapBTree::new(depth, btree).into())
}
SkyMapEnum::ExplicitU64I64(skymap) => {
warn!("Transforms i64 skymap into u32 skymap without checking possible overflow!");
let depth = skymap.depth();
let btree = skymap
.owned_entries()
.map(|(k, v)| (k, v as u32))
.collect::<BTreeMap<u64, u32>>();
Ok(ExplicitSkyMapBTree::new(depth, btree).into())
}
SkyMapEnum::ExplicitU32I32(skymap) => {
let depth = skymap.depth();
let btree = unsafe { std::mem::transmute::<_, BTreeMap<u32, u32>>(skymap.entries) };
Ok(ExplicitSkyMapBTree::new(depth, btree).into())
}
SkyMapEnum::ExplicitU32I64(skymap) => {
warn!("Transforms i64 skymap into u32 skymap without checking possible overflow!");
let depth = skymap.depth();
let btree = skymap
.owned_entries()
.map(|(k, v)| (k, v as u32))
.collect::<BTreeMap<u32, u32>>();
Ok(ExplicitSkyMapBTree::new(depth, btree).into())
}
_ => Err(String::from(
"Unable to convert skymap to countmap (incompatible types).",
)),
}
}
}
pub enum DensMap {
ImplicitU32(ImplicitDensityMapU32),
Implicit(ImplicitDensityMap),
ExplicitU32(ExplicitDensityMap<u32>),
Explicit(ExplicitDensityMap<u64>),
}
impl From<ImplicitDensityMapU32> for DensMap {
fn from(value: ImplicitDensityMapU32) -> Self {
Self::ImplicitU32(value)
}
}
impl From<ImplicitDensityMap> for DensMap {
fn from(value: ImplicitDensityMap) -> Self {
Self::Implicit(value)
}
}
impl From<ExplicitDensityMap<u32>> for DensMap {
fn from(value: ExplicitDensityMap<u32>) -> Self {
Self::ExplicitU32(value)
}
}
impl From<ExplicitDensityMap<u64>> for DensMap {
fn from(value: ExplicitDensityMap<u64>) -> Self {
Self::Explicit(value)
}
}
impl From<ImplicitSkyMapArray<u64, f64>> for DensMap {
fn from(value: ImplicitSkyMapArray<u64, f64>) -> Self {
Self::Implicit(value.into())
}
}
impl From<ImplicitSkyMapArray<u32, f64>> for DensMap {
fn from(value: ImplicitSkyMapArray<u32, f64>) -> Self {
Self::ImplicitU32(value.into())
}
}
impl From<ExplicitSkyMapBTree<u64, f64>> for DensMap {
fn from(value: ExplicitSkyMapBTree<u64, f64>) -> Self {
Self::Explicit(value.into())
}
}
impl From<ExplicitSkyMapBTree<u32, f64>> for DensMap {
fn from(value: ExplicitSkyMapBTree<u32, f64>) -> Self {
Self::ExplicitU32(value.into())
}
}
impl DensMap {
pub fn to_fits<W: Write>(&self, writer: W) -> Result<(), FitsError> {
match self {
Self::ImplicitU32(m) => m.to_fits(writer),
Self::Implicit(m) => m.to_fits(writer),
Self::ExplicitU32(m) => m.to_fits(writer),
Self::Explicit(m) => m.to_fits(writer),
}
}
pub fn to_fits_file<P: AsRef<Path>>(&self, path: P) -> Result<(), FitsError> {
match self {
Self::ImplicitU32(m) => m.to_fits_file(path),
Self::Implicit(m) => m.to_fits_file(path),
Self::ExplicitU32(m) => m.to_fits_file(path),
Self::Explicit(m) => m.to_fits_file(path),
}
}
}
#[cfg(test)]
mod tests {
fn init_logger() {
let log_level = log::LevelFilter::max();
let _ = env_logger::builder()
.filter_level(log_level)
.is_test(true)
.try_init();
}
}