boreholeio 0.1.0

A library for interacting with borehole.io, a subsurface data management, delivery and visualisation platform
Documentation
use std::path::PathBuf;

use super::{utils::slice_view1, Base, ChannelAxis, Error, Numeric, NumericAxis};
use crate::{
    array_cache::{ArrayCache, UriResolvable},
    schema::{
        array_uri_reference::{AbsoluteUri, ArrayUriReference},
        PolynomialTransform,
    },
    strided_array_file::{ArrayProxy, Statistics, StridedArrayFile},
};
use ndarray::{Array1, Ix1, SliceInfoElem};
use serde::{Deserialize, Serialize};

#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct CoordinateAxis {
    pub name: String,
    pub units: Option<String>,
    pub bounds: [f64; 2],
    pub size: usize,
    pub transform: Option<PolynomialTransform>,
    pub uri: ArrayUriReference,
}

impl CoordinateAxis {
    /// Writes `self.coordinates()` to the specified `uri` and returns a copy
    /// of `Self` which points to the new `uri`.
    pub fn copy_to(&self, uri: &ArrayUriReference) -> Result<Self, Error> {
        let coordinates = self.coordinates()?;
        let proxy = ArrayProxy::from_view(coordinates.view().into_dyn())?;
        let path: PathBuf = uri.try_into()?;

        StridedArrayFile::write(&path, &[proxy])
            .map_err(|e| format!("Failed to write axis coordinates: {e}"))?;

        let mut copy = self.clone();
        copy.uri = uri.clone();

        Ok(copy)
    }
}

impl UriResolvable for CoordinateAxis {
    fn set_base_for_relative_uris(&mut self, base: &AbsoluteUri) {
        self.uri = self.uri.with_base_if_relative(base);
    }
}

impl Base for CoordinateAxis {
    fn size(&self) -> Result<usize, Error> {
        Ok(self.size)
    }

    fn slice(&self, slice_info: &SliceInfoElem) -> Result<ChannelAxis, Error> {
        if matches!(slice_info, SliceInfoElem::NewAxis) {
            return Err("SliceInfoElem::NewAxis variant is not supported".into());
        }

        let coordinates = self.coordinates()?;
        let slice = slice_view1(coordinates.view(), slice_info)?;

        let proxy = ArrayProxy::F64(slice.into_dyn());
        let uri = ArrayCache::insert(proxy.clone())?;

        Ok(Self {
            name: self.name.clone(),
            units: self.units.clone(),
            bounds: [
                proxy.calc_stats(Statistics::Min)?,
                proxy.calc_stats(Statistics::Max)?,
            ],
            size: proxy.shape()[0],
            transform: self.transform.clone(),
            uri,
        }
        .into())
    }
}

impl Numeric for CoordinateAxis {
    fn name(&self) -> &str {
        &self.name
    }

    fn units(&self) -> &Option<String> {
        &self.units
    }

    fn coordinates(&self) -> Result<Array1<f64>, Error> {
        if self.transform.is_some() {
            return Err("CoordinateAxis.transform is not yet supported".into());
        }
        ArrayCache::get(&self.uri)?
            .try_as::<f64>()?
            .to_owned()
            .into_dimensionality::<Ix1>()
            .map_err(|e| format!("Coordinates are not 1D: {e}"))
    }

    fn make_monotonic(&self) -> Result<Vec<NumericAxis>, Error> {
        todo!()
    }
}