Skip to content

Commit

Permalink
feat: support reading boolean columns (#342)
Browse files Browse the repository at this point in the history
Closes #339
  • Loading branch information
simonrw authored Jul 26, 2024
1 parent b467e4f commit 661c273
Show file tree
Hide file tree
Showing 4 changed files with 425 additions and 4 deletions.
24 changes: 20 additions & 4 deletions fitsio/src/longnam.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@

pub(crate) use crate::sys::{
ffclos, ffcopy, ffcrim, ffcrtb, ffdcol, ffdhdu, ffflmd, ffgbcl, ffgcdw, ffgcno, ffgcvd, ffgcve,
ffgcvi, ffgcvj, ffgcvjj, ffgcvk, ffgcvs, ffgcvui, ffgcvuj, ffgcvujj, ffgcvuk, ffghdn, ffghdt,
ffgidm, ffgiet, ffgisz, ffgkyd, ffgkye, ffgkyj, ffgkyjj, ffgkyl, ffgkys, ffgncl, ffgnrw, ffgpv,
ffgsv, fficol, ffinit, ffmahd, ffmnhd, ffopen, ffpcl, ffpcls, ffphps, ffpky, ffpkyd, ffpkye,
ffpkys, ffppr, ffpss, ffrsim, ffthdu, fitsfile, LONGLONG,
ffgcvi, ffgcvj, ffgcvjj, ffgcvk, ffgcvl, ffgcvs, ffgcvui, ffgcvuj, ffgcvujj, ffgcvuk, ffghdn,
ffghdt, ffgidm, ffgiet, ffgisz, ffgkyd, ffgkye, ffgkyj, ffgkyjj, ffgkyl, ffgkys, ffgncl,
ffgnrw, ffgpv, ffgsv, fficol, ffinit, ffmahd, ffmnhd, ffopen, ffpcl, ffpcls, ffphps, ffpky,
ffpkyd, ffpkye, ffpkys, ffppr, ffpss, ffrsim, ffthdu, fitsfile, LONGLONG,
};
pub use libc::{
c_char, c_double, c_float, c_int, c_long, c_short, c_uint, c_ulong, c_ulonglong, c_ushort,
Expand Down Expand Up @@ -132,6 +132,22 @@ pub(crate) unsafe fn fits_read_col_str(
)
}

pub(crate) unsafe fn fits_read_col_log(
fptr: *mut fitsfile,
colnum: c_int,
firstrow: LONGLONG,
firstelem: LONGLONG,
nelem: LONGLONG,
nulval: c_char,
array: *mut c_char,
anynul: *mut c_int,
status: *mut c_int,
) -> c_int {
ffgcvl(
fptr, colnum, firstrow, firstelem, nelem, nulval, array, anynul, status,
)
}

pub(crate) unsafe fn fits_read_col_sht(
fptr: *mut fitsfile,
colnum: c_int,
Expand Down
103 changes: 103 additions & 0 deletions fitsio/src/tables.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ use std::ops::Range;
use std::ptr;
use std::str::FromStr;

const BOOL_NULL: c_char = 127;

/// Trait for reading a fits column
pub trait ReadsCol {
#[doc(hidden)]
Expand Down Expand Up @@ -145,6 +147,107 @@ macro_rules! reads_col_impl {
};
}

impl ReadsCol for bool {
fn read_col_range<T: Into<String>>(
fits_file: &mut FitsFile,
name: T,
range: &Range<usize>,
) -> Result<Vec<Self>> {
match fits_file.fetch_hdu_info() {
Ok(HduInfo::TableInfo {
column_descriptions,
..
}) => {
let num_output_rows = range.end - range.start;
let mut out = vec![BOOL_NULL; num_output_rows];
let test_name = name.into();
let column_number = column_descriptions
.iter()
.position(|desc| desc.name == test_name)
.ok_or(Error::Message(format!(
"Cannot find column {:?}",
test_name
)))?;
let mut status = 0;
unsafe {
fits_read_col_log(
fits_file.fptr.as_mut() as *mut _,
(column_number + 1) as i32,
(range.start + 1) as i64,
1,
num_output_rows as _,
BOOL_NULL,
out.as_mut_ptr(),
ptr::null_mut(),
&mut status,
);
}

match status {
// TODO: this does not correctly account for nyll values,
// instead treat them as falsy for now
0 => Ok(out.into_iter().map(|v| v != BOOL_NULL && v > 0).collect()),
307 => Err(IndexError {
message: "given indices out of range".to_string(),
given: range.clone(),
}
.into()),
e => Err(FitsError {
status: e,
message: status_to_string(e).unwrap().unwrap(),
}
.into()),
}
}
Err(e) => Err(e),
_ => panic!("Unknown error occurred"),
}
}

fn read_cell_value<T>(fits_file: &mut FitsFile, name: T, idx: usize) -> Result<Self>
where
T: Into<String>,
Self: Sized,
{
match fits_file.fetch_hdu_info() {
Ok(HduInfo::TableInfo {
column_descriptions,
..
}) => {
let mut out = BOOL_NULL;
let test_name = name.into();
let column_number = column_descriptions
.iter()
.position(|desc| desc.name == test_name)
.ok_or(Error::Message(format!(
"Cannot find column {:?}",
test_name
)))?;
let mut status = 0;

unsafe {
fits_read_col_log(
fits_file.fptr.as_mut() as *mut _,
(column_number + 1) as i32,
(idx + 1) as i64,
1,
1,
BOOL_NULL,
&mut out,
ptr::null_mut(),
&mut status,
);
}
// TODO: this does not correctly account for nyll values,
// instead treat them as falsy for now
check_status(status).map(|_| out != BOOL_NULL && out > 0)
}
Err(e) => Err(e),
_ => panic!("Unknown error occurred"),
}
}
}

reads_col_impl!(i16, fits_read_col_sht, 0);
reads_col_impl!(u16, fits_read_col_usht, 0);
reads_col_impl!(i32, fits_read_col_int, 0);
Expand Down
31 changes: 31 additions & 0 deletions fitsio/tests/test_boolean_columns.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
use fitsio::FitsFile;

// read from astropy
static EXPECTED: &[bool] = &[
true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true,
true, true, true, true, true, true, false, false, true, true, true, true, true, true, true,
true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true,
true, true, true, true, true, true, true, true, true, true, true, false, false, true, true,
true, true, true, true, false, false, false, false, false, false, false, false, true, true,
true, true, true, true, true, true, true, true, false, false, false, false, false, false, true,
true, false, false, true, true, true, true, true, true, true, true, true, true, true, true,
true, true, true, true, true, true, true, true, true, true, true, true, true, true, false,
false, true, true, true, true, true, true, true, true, true, true, true, true, true, true,
true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true,
true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true,
true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true,
true, true, true, true, true, true, true, true, true, true, false, false, false, false, false,
false, true, true, true, true, true, true, true, true, true, true, true, true, true, true,
true, true, false, false, true, true, false, false, true, true, true, true, true, true, true,
true, true, true, true, true, true, true, true, true, true, true, true, true, false, false,
true, true, false, false, false, false, true, true, true, true, true, true,
];

#[test]
fn reading_binary_columns() -> Result<(), Box<dyn std::error::Error>> {
let mut fitsfile = FitsFile::open("../testdata/boolean_columns.fits")?;
let ant_hdu = fitsfile.hdu(1)?;
let col = ant_hdu.read_col::<bool>(&mut fitsfile, "Whitening_Filter")?;
assert_eq!(col, EXPECTED);
Ok(())
}
Loading

0 comments on commit 661c273

Please sign in to comment.