Giter Club home page Giter Club logo

astrors's Introduction

astrors

codecov

A package for astronomical image processing and analysis. We aim to provide a simple interface for common tasks like opening fits files, including images and tables.

To Do list


  • Read/Writing modifying header.
  • Read/Writing modifying image data.
  • [-] Documentation
  • Read/Writing bin table data.
  • Keep CARD comment
  • Support of multiple HDU, fits extensions (in progress, only the header is parsed)
  • Read / Write compressed images
  • WCS operations
  • General astronomy operations

Astrors Library Guide

Introduction

This guide provides an overview of the astrors library, a Rust-based tool for handling FITS files used in astronomical data. With astrors, you can read, write, and manipulate FITS files, enabling efficient processing and analysis of astronomical datasets. This guide covers common use cases, including reading FITS files, writing data back to FITS format, and manipulating image and table data within FITS files.

Prerequisites

Before you start, ensure you have Rust installed on your machine. You'll also need the astrors. This guide assumes basic familiarity with Rust programming.

Setup

To use astrors in your project, add it to your Cargo.toml file:

[dependencies]
astrors = ""

Reading FITS Files

Basic Reading of a FITS File

To read a FITS file and access its HDUs (Header/Data Units), you can use the following approach:

use astrors::fits;

let testfile = common::get_testdata_path("your_file.fits");
let mut hdu_list = fits::fromfile(testfile.to_str().unwrap()).unwrap();

println!("HDU List Length: {:?}", hdu_list.hdus.len());

This code snippet opens a FITS file, reads its contents into an HDUList structure, and prints the number of HDUs found in the file.

Writing FITS Files

Writing Modified HDUs Back to a FITS File

After reading and optionally modifying HDUs, you can write them back to a new FITS file:

let outfile = common::get_outtestdata_path("modified_file.fits");
hdu_list.write_to(outfile.to_str().unwrap()).unwrap();

Manipulating HDU Data

Reading and Modifying Primary HDU

To read the primary HDU, modify its data, and write it back to a file:

use astrors::io::hdus::primaryhdu::PrimaryHDU;
use std::fs::File;
use ndarray::ArrayD;
use astrors::io::hdulist::HDUList;
use astrors::io::hdus::image::ImageData;
use ndarray::IxDyn;

let testfile = common::get_testdata_path("your_primary_hdu_file.fits");
let mut f: File = File::open(testfile)?;
let mut primary_hdu = PrimaryHDU::read_from_file(&mut f)?;

// Modify the primary HDU's data
primary_hdu.data = ImageData::F32(ArrayD::from_elem(IxDyn(&[100, 100]), 1.0));

// Write the modified primary HDU to a new file
let outfile = common::get_outtestdata_path("modified_primary_hdu.fits");
let mut f_out: File = File::create(outfile)?;
let mut hdus = HDUList::new();
hdus.add_hdu(HDU::Primary(primary_hdu));
hdus.write_to(outfile.to_str().unwrap())?;

Integrating with Polars for Tabular Data

To create a binary table HDU from a DataFrame and add it to an HDUList:

use polars::prelude::*;
use astrors::io::hdus::bintable::bintablehdu::BinTableHDU;

let df = DataFrame::new(vec![
    Series::new("RA", vec![1, 2, 3, 4, 5]),
    Series::new("DEC", vec![1, 2, 3, 4, 5]),
    Series::new("MAG", vec![1, 2, 3, 4, 5]),
]).unwrap();

let mut bintable = BinTableHDU::new_data(df);
hdus.add_hdu(HDU::BinTable(bintable));

This snippet creates a DataFrame with astronomical data, converts it to a binary table HDU, and adds it to an HDUList for writing to a FITS file.

Contributing to Development

We welcome contributions from the community to help further develop and improve this library. Whether you're fixing bugs, adding new features, or improving documentation, your help is invaluable. Please feel free to submit pull requests or open issues on our GitHub repository. For major changes, please open an issue first to discuss what you would like to change.

Sponsorship

If you find this library useful and would like to support its development, consider sponsoring the project. Your sponsorship can help with the maintenance of the project, the development of new features, and the improvement of the existing ones. For more information on how to sponsor, please visit our GitHub repository or contact us directly.

License

This project is licensed under the BSD 3-Clause License - see the LICENSE file for details. The BSD 3-Clause License is a permissive license that allows for redistribution and use in source and binary forms, with or without modification, under certain conditions. This license is business-friendly and compatible with open source and commercial projects.

astrors's People

Contributors

schwarzam avatar vincas2539 avatar szabgab avatar

Stargazers

Harlan D Heilman avatar Pedro Cabral avatar Ricardo da Rocha avatar

Watchers

Pedro Cabral avatar  avatar

Forkers

szabgab

astrors's Issues

Can't Access some PrimaryHDU cards

  • version: "0.1.7"
  • Operating System: Windows 11

Description

I've been recently transitioning my current all python library into a rust + python mixed library, and have been searching for a solution to read and write FITS files. I am working with data collected from Synchrotron X-ray sources that use the FITS file format. I discovered that there are some header cards that are inaccessible using the astrors::io::Header.get_card() method.

What I Did

I attached a zip file.zip with a jupyter notebook outlining what I was playing around with and the fits file that I have been using.

There are two cards I need access to, but are somehow missing, "Beamline Current" and "Sample Theta". Using the pretty print method, I can display them

let mut testfile = File::open("~/testing/ZnPc82862-00001.fits")?;

let mut header = io::Header::new();
header.read_from_file(&mut testfile).unwrap();

header.pretty_print_advanced();

This prints out all 111 cards in the fits file and importantly shows the cards I am looking for

...
----------------------------------------
Keyword: Beam Current 
Value: 499.725799560547
Comment: Analog
----------------------------------------
...
----------------------------------------
Keyword: Sample Theta 
Value: 0
Comment: [Degrees] Motor
----------------------------------------
...

However, using either get_card() or the method under the hood of iter().find(|card| card.keyword == "Sample Theta"),

println!("━━━━━━━━━━━━━ Using `get_card()` ━━━━━━━━━━━━━");

println!("Using get_card(Beamline Current) {:?}", header.get_card("Beamline Current"));
println!("Using get_card(Sample Theta) {:?}", header.get_card("Sample Theta"));

println!("━━━━━━━━━━━━━ Using `iter().find()` ━━━━━━━━━━━━━");

println!("Using iter().find(Beamline Current) {:?}", header.iter().find(|card| card.keyword == "Beamline Current"));
println!("Using iter().find(Sample Theta) {:?}", header.iter().find(|card| card.keyword == "Sample Theta"));

I cannot find the cards I am looking for

━━━━━━━━━━━━━ Using `get_card()` ━━━━━━━━━━━━━
Using get_card(Beamline Current) None
Using get_card(Sample Theta) None
━━━━━━━━━━━━━ Using `iter().find()` ━━━━━━━━━━━━━
Using iter().find(Beamline Current) None
Using iter().find(Sample Theta) None

Not sure what is going on here or what the best fix would be. But your crate is so far wonderful aside from this annoying bug.... maybe it is not a bug, and I am showing how new I am at rust lol.

Find better way to implement loop on header

There should be a better way to parse the header instead of going through blocks of 2880 bytes.

There are two problems:

  • If the header does not have a END card it goes on the loop until the end of the buffer (than I think it raises an error).
  • When it reaches 35 on the counter, it adds the card to the header, if the card is a CONTINUE card, this will raise unknown problems.

Snippets with suggestions:

let mut buffer= [0; 2880];
let n = file.read(&mut buffer[..])?;
let mut last_card : Option<Card> = None;

let mut counter = 0; // This is not right!
for card in buffer.chunks(80) { // This for should be replaced by a loop. 
    let card_str = String::from_utf8_lossy(card).trim_end().to_string();
else {
    last_card = Card::parse_card(card_str);
    
    if (counter) == (2880 / 80) - 1 { // Remove this if block
        self.add_card(last_card);
        break;
    }
}
counter += 1; // Remove

Add Header Card on index or after other card.

Implement a way to insert after another card like after="SIMPLE", or at first or end of header card list.

Suggestion, modify function below:

pub fn add_card(&mut self, card: Card) {
        self.cards.push(card);
}

BSCALE and BZERO implementation needed??

What is the bscale and bzero from the header?

Imagine you have a FITS file with an image, and you read the header to find BSCALE = 1.5 and BZERO = 100.0. The stored pixel values are integers, but the actual physical values might be floating-point numbers.

When you read a pixel value from this file, say 400, the actual physical value represented by this pixel would be calculated as:

Physical Value = 1.5 × 400 + 100.0 = 700.0

Reading FITS Files in Practice
When reading FITS files programmatically (e.g., using Python with libraries like astropy.io.fits), these libraries typically handle the BZERO and BSCALE conversion for you. They read the pixel data, apply the scale and zero-point, and return the data in its physical form.

If you are handling the FITS file at a low level (i.e., reading raw bytes), you need to manually apply these transformations after reading the pixel values based on the BZERO and BSCALE values found in the file's header.

Card and Header setters and getters should be tamplated

Header and Card setters and getters should have a template type, because they may be one the types below and not only string.

pub enum TYPE{
    INT,
    FLOAT,
    STRING, 
    LOGICAL
}

fn check_type(s: &str) -> TYPE {
    if s.parse::<i64>().is_ok() {
        TYPE::INT
    } else if s.parse::<f64>().is_ok() {
        TYPE::FLOAT
    } else if s == "T" || s == "F" {
        TYPE::LOGICAL
    } else {
        TYPE::STRING
    }
}

This is for example wrong:

impl Card {
    // Function to set the value and take ownership of the passed variable
    pub fn set_value(&mut self, new_value: String) {
        self.value = Some(new_value);
    }

Also this on header (maybe this should be removed):

pub fn get_value(&self, keyword: &str) -> Result<&str, Error> {
        self.cards.iter().find(|card| card.keyword == keyword).map_or(
            Err(Error::new(ErrorKind::Other, format!("{} keyword not found", keyword))),
            |card| {
                match &card.value {
                    Some(value) => Ok(value),
                    None => Err(Error::new(ErrorKind::Other, format!("{} keyword has no value", keyword))),
                }
            }
        )
    }

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.