mod image_data;
mod measurement_array_view;
mod strided_array_data;
mod utils;
use super::{
schema::{
axes::{Base, ChannelAxis},
Channel, Label,
},
strided_array_file::ArrayProxy,
};
use image_data::ImageData;
use measurement_array_view::MeasurementArrayView;
use ndarray::SliceInfoElem;
use std::{fmt::Debug, path::Path, sync::Arc};
use strided_array_data::StridedArrayData;
use utils::{calculate_bounds, update_labelled_axis};
pub use utils::{slice_coordinate_system, Error};
pub trait MeasurementArray<'a>: Debug {
fn data(&self) -> ArrayProxy<'a>;
fn axes(&self) -> &Vec<ChannelAxis>;
fn label(&self) -> &Option<Label>;
fn slice(
&self,
slice_info: Vec<SliceInfoElem>,
) -> Result<Arc<dyn MeasurementArray<'a> + 'a>, Error> {
let data = self.data().slice(slice_info.clone())?;
let label = update_label(self.label(), &data)?;
let mut axes = slice_coordinate_system(self.axes(), &slice_info)?;
update_coordinate_system(&mut axes, &data)?;
Ok(Arc::new(MeasurementArrayView::<'a>::new(axes, label, data)))
}
fn data_type(&self) -> ChannelDataType {
if self.axes().len() == 1 {
ChannelDataType::Curve
} else {
ChannelDataType::GridData
}
}
fn is_decomposed(&self) -> bool {
for axis in self.axes() {
match axis {
ChannelAxis::Composite(_) => return false,
_ => continue,
}
}
true
}
fn decompose(&self) -> Result<Vec<Arc<dyn MeasurementArray<'a> + 'a>>, Error> {
let axes_sets = decompose_coordinate_system(self.axes().clone());
let mut decomposed = vec![];
for axes in axes_sets {
decomposed.push(create_data_view(self.data(), axes, self.label().clone())?);
}
Ok(decomposed)
}
}
#[derive(Clone, Copy, Debug)]
pub enum ChannelDataType {
Curve,
GridData,
}
pub fn get_data_from_path<'a>(
metadata_path: &Path,
) -> Result<Arc<dyn MeasurementArray<'a> + 'a>, Error> {
let metadata = Channel::from_json_file(metadata_path)?.data;
if metadata.format.starts_with("image/") {
ImageData::from_uri(&metadata.uri, metadata.axes, metadata.label)
} else if metadata.format == "application/vnd.alt.strided-array" {
StridedArrayData::from_uri(&metadata.uri, metadata.axes, metadata.label)
} else {
Err(format!("Unsupported format: {}", metadata.format))
}
}
pub fn create_data_view<'a>(
data: ArrayProxy<'a>,
axes: Vec<ChannelAxis>,
label: Option<Label>,
) -> Result<Arc<dyn MeasurementArray<'a> + 'a>, Error> {
if data.dimensions() != axes.len() {
return Err("Inconsistent data and axes dimensionality".to_string());
}
for (data_size, axis) in data.shape().iter().zip(&axes) {
if *data_size != axis.size()? {
return Err("Inconsistent data and axis size".to_string());
}
}
Ok(Arc::new(MeasurementArrayView::new(axes, label, data)))
}
pub fn update_label(label: &Option<Label>, data: &ArrayProxy<'_>) -> Result<Option<Label>, Error> {
if let Some(mut label) = label.clone() {
label.bounds = calculate_bounds(data)?;
Ok(Some(label))
} else {
Ok(None)
}
}
fn update_coordinate_system(axes: &mut [ChannelAxis], data: &ArrayProxy<'_>) -> Result<(), Error> {
for (axis_index, axis) in axes.iter_mut().enumerate() {
match axis {
ChannelAxis::Labelled(labelled) => update_labelled_axis(labelled, axis_index, data)?,
_ => continue,
}
}
Ok(())
}
fn decompose_coordinate_system(mut composite_axes: Vec<ChannelAxis>) -> Vec<Vec<ChannelAxis>> {
let mut result_prev: Vec<Vec<ChannelAxis>> = vec![vec![]];
while !composite_axes.is_empty() {
let mut result_next: Vec<Vec<ChannelAxis>> = vec![];
for component in decompose_axis(composite_axes.remove(0)) {
for axes_set_prev in &result_prev {
let mut axes_set_next = axes_set_prev.clone();
axes_set_next.push(component.clone());
result_next.push(axes_set_next);
}
}
result_prev = result_next;
}
result_prev
}
fn decompose_axis(axis: ChannelAxis) -> Vec<ChannelAxis> {
match axis {
ChannelAxis::Composite(composite) => composite
.descriptors
.into_iter()
.map(|numeric_axis| numeric_axis.into())
.collect(),
non_composite => vec![non_composite],
}
}