fitsio/lib.rs
1/*!
2`fitsio` - a thin wrapper around the [`cfitsio`][cfitsio] C library.
3
4* [File access](#file-access)
5 * [Pretty printing](#pretty-printing)
6* [HDU access](#hdu-access)
7* [Creating new HDUs](#creating-new-hdus)
8 * [Creating a new image](#creating-a-new-image)
9 * [Creating a new table](#creating-a-new-table)
10 * [Column descriptions](#column-descriptions)
11 * [Copying HDUs to another file](#copying-hdus-to-another-file)
12 * [Deleting a HDU](#deleting-a-hdu)
13 * [Iterating over the HDUs in a file](#iterating-over-the-hdus-in-a-file)
14 * [General calling behaviour](#general-calling-behaviour)
15* [Header keys](#header-keys)
16* [Reading file data](#reading-file-data)
17 * [Reading images](#reading-images)
18 * [`ndarray` support](#ndarray-support)
19 * [Reading tables](#reading-tables)
20 * [Reading cell values](#reading-cell-values)
21 * [Reading rows](#reading-rows)
22 * [Iterating over columns](#iterating-over-columns)
23* [Writing file data](#writing-file-data)
24 * [Writing images](#writing-images)
25 * [Resizing an image](#resizing-an-image)
26 * [Writing tables](#writing-tables)
27 * [Writing table data](#writing-table-data)
28 * [Inserting columns](#inserting-columns)
29 * [Deleting columns](#deleting-columns)
30* [Raw fits file access](#raw-fits-file-access)
31* [Threadsafe access](#threadsafe-access)
32
33This library wraps the low level `cfitsio` bindings: [`fitsio-sys`][fitsio-sys] and provides a more
34native experience for rust users.
35
36The main interface to a fits file is [`FitsFile`][fits-file]. All file manipulation
37and reading starts with this class.
38
39# File access
40
41To open an existing file, use the [open][fitsfile-open] method.
42
43```rust
44use fitsio::FitsFile;
45# use std::error::Error;
46
47# fn try_main() -> Result<(), Box<dyn Error>> {
48# let filename = "../testdata/full_example.fits";
49// let filename = ...;
50let fptr = FitsFile::open(filename)?;
51# Ok(())
52# }
53# fn main() {
54# try_main().unwrap();
55# }
56```
57
58Alternatively a new file can be created on disk with the companion method
59[`create`][fits-file-create]:
60
61```rust
62# fn try_main() -> Result<(), Box<std::error::Error>> {
63# let tdir = tempfile::Builder::new().prefix("fitsio-").tempdir().unwrap();
64# let tdir_path = tdir.path();
65# let filename = tdir_path.join("test.fits");
66use fitsio::FitsFile;
67
68// let filename = ...;
69let fptr = FitsFile::create(filename).open()?;
70# Ok(())
71# }
72# fn main() {
73# try_main().unwrap();
74# }
75```
76
77The [`create`][fits-file-create] method returns a [`NewFitsFile`][new-fits-file], which is an
78internal representation of a temporary fits file on disk, before the file is fully created.
79
80This representation has two methods: [`open`][new-fits-file-open] and
81[`with_custom_primary`][new-fits-file-with-custom-primary]. The [`open`][new-fits-file-open]
82method actually creates the file on disk, but before calling this method, the
83[`with_custom_primary`][new-fits-file-with-custom-primary] method can be used to add a custom
84primary HDU. This is mostly useful for images. Otherwise, a default primary HDU is created. An
85example of not adding a custom primary HDU is shown above. Below we see an example of
86[`with_custom_primary`][new-fits-file-with-custom-primary]:
87
88```rust
89# fn try_main() -> Result<(), Box<std::error::Error>> {
90# let tdir = tempfile::Builder::new().prefix("fitsio-").tempdir().unwrap();
91# let tdir_path = tdir.path();
92# let filename = tdir_path.join("test.fits");
93use fitsio::FitsFile;
94use fitsio::images::{ImageType, ImageDescription};
95
96// let filename = ...;
97let description = ImageDescription {
98 data_type: ImageType::Double,
99 dimensions: &[52, 103],
100};
101
102let fptr = FitsFile::create(filename)
103 .with_custom_primary(&description)
104 .open()?;
105# Ok(())
106# }
107# fn main() { try_main().unwrap(); }
108```
109
110From this point, the current HDU can be queried and changed, or fits header cards can be read
111or file contents can be read.
112
113To open a fits file in read/write mode (to allow changes to the file), the
114[`edit`][fits-file-edit] must be used. This opens a file which already exists
115on disk for editing.
116
117```rust
118# fn try_main() -> Result<(), Box<std::error::Error>> {
119# let filename = "../testdata/full_example.fits";
120use fitsio::FitsFile;
121
122// let filename = ...;
123let fptr = FitsFile::edit(filename)?;
124# Ok(())
125# }
126# fn main() { try_main().unwrap(); }
127```
128
129## Pretty printing
130
131Fits files can be pretty-printed with [`pretty_print`][pretty-print], or its more powerful
132cousin [`pretty_write`][pretty-write].
133
134```rust
135# fn try_main() -> Result<(), Box<std::error::Error>> {
136# let filename = "../testdata/full_example.fits";
137# use std::io;
138use fitsio::FitsFile;
139
140let mut fptr = FitsFile::open(filename)?;
141fptr.pretty_print()?;
142// or
143fptr.pretty_write(&mut io::stdout())?;
144# Ok(())
145# }
146# fn main() { try_main().unwrap(); }
147```
148
149In the continuing tradition of releasing fits summary programs with each fits library, this
150create contains a binary program [`fitssummary`] which can be installed with `cargo install`. This
151takes fits files on the command line and prints their summaries to stdout.
152
153```sh
154$ fitssummary ../testdata/full_example.fits
155
156 file: ../testdata/full_example.fits
157 mode: READONLY
158 extnum hdutype hduname details
159 0 IMAGE_HDU dimensions: [100, 100], type: Long
160 1 BINARY_TBL TESTEXT num_cols: 4, num_rows: 50
161```
162
163# HDU access
164
165HDU information belongs to the [`FitsHdu`][fits-hdu] object. HDUs can be fetched by
166`String`/`str` or integer (0-indexed), with the [`hdu`][fitsfile-hdu] method. The `HduInfo`
167object contains information about the current HDU:
168
169```rust
170# use fitsio::{sys, FitsFile};
171#
172# fn try_main() -> Result<(), Box<std::error::Error>> {
173# let filename = "../testdata/full_example.fits";
174# let mut fptr = FitsFile::open(filename)?;
175use fitsio::hdu::HduInfo;
176
177let hdu = fptr.hdu(0)?;
178// image HDU
179if let HduInfo::ImageInfo { shape, .. } = hdu.info {
180 println!("Image is {}-dimensional", shape.len());
181 println!("Found image with shape {:?}", shape);
182}
183# let hdu = fptr.hdu("TESTEXT")?;
184
185// tables
186if let HduInfo::TableInfo { column_descriptions, num_rows, .. } = hdu.info {
187 println!("Table contains {} rows", num_rows);
188 println!("Table has {} columns", column_descriptions.len());
189}
190# Ok(())
191# }
192# fn main() { try_main().unwrap(); }
193```
194
195The number of HDUs in a file can be queried with the [`num_hdus`][fits-file-num-hdus] method:
196```rust
197use fitsio::FitsFile;
198
199# fn main() -> Result<(), Box<dyn std::error::Error>> {
200let mut fptr = FitsFile::open("../testdata/full_example.fits")?;
201
202let num_hdus = fptr.num_hdus()?;
203println!("Number of HDUs: {}", num_hdus);
204# Ok(())
205# }
206```
207
208The primary HDU can always be accessed with the `FitsFile::primary_hdu` method.
209
210# Creating new HDUs
211
212## Creating a new image
213
214New fits images are created with the [`create_image`][fits-file-create-image]
215method. This method requires the extension name, and an
216[`ImageDescription`][image-description] object, which defines the shape and type of
217the desired image:
218
219```rust
220# fn try_main() -> Result<(), Box<std::error::Error>> {
221# let tdir = tempfile::Builder::new().prefix("fitsio-").tempdir().unwrap();
222# let tdir_path = tdir.path();
223# let filename = tdir_path.join("test.fits");
224# let mut fptr = fitsio::FitsFile::create(filename).open()?;
225use fitsio::images::{ImageDescription, ImageType};
226
227let image_description = ImageDescription {
228 data_type: ImageType::Float,
229 dimensions: &[100, 100],
230};
231let hdu = fptr.create_image("EXTNAME".to_string(), &image_description)?;
232# Ok(())
233# }
234# fn main() { try_main().unwrap(); }
235```
236
237_Unlike cfitsio, the order of the dimensions of `new_size` follows the C convention, i.e.
238[row-major order](https://en.wikipedia.org/wiki/Row-_and_column-major_order)._
239
240## Creating a new table
241
242Similar to creating new images, new tables are created with the
243[`create_table`][fits-file-create-table] method. This requires an extension
244name, and a slice of [`ColumnDescription`][column-description]s:
245
246```rust
247# fn try_main() -> Result<(), Box<std::error::Error>> {
248# let tdir = tempfile::Builder::new().prefix("fitsio-").tempdir().unwrap();
249# let tdir_path = tdir.path();
250# let filename = tdir_path.join("test.fits");
251# let mut fptr = fitsio::FitsFile::create(filename).open()?;
252use fitsio::tables::{ColumnDescription, ColumnDataType};
253
254let first_description = ColumnDescription::new("A")
255 .with_type(ColumnDataType::Int)
256 .create()?;
257let second_description = ColumnDescription::new("B")
258 .with_type(ColumnDataType::Long)
259 .create()?;
260let descriptions = [first_description, second_description];
261let hdu = fptr.create_table("EXTNAME".to_string(), &descriptions)?;
262# Ok(())
263# }
264# fn main() { try_main().unwrap(); }
265```
266
267### Column descriptions
268
269Columns are described with the
270[`ColumnDescription`][column-description] struct. This
271encapsulates: the name of the column, and the data format.
272
273The fits specification allows scalar or vector columns, and the data format is described the
274[`ColumnDataDescription`][column-data-description] struct, which in
275turn encapsulates the number of elements per row element (typically 1), the width of the
276column (for strings), and the data type, which is one of the
277[`ColumnDataType`][column-data-type] members
278
279For the common case of a scalar column, a `ColumnDataDescription` object can be constructed
280with the `scalar` method:
281
282```rust
283# fn main() {
284use fitsio::tables::{ColumnDescription, ColumnDataDescription, ColumnDataType};
285
286let desc = ColumnDataDescription::scalar(ColumnDataType::Int);
287assert_eq!(desc.repeat, 1);
288assert_eq!(desc.width, 1);
289# }
290```
291
292Vector columns can be constructed with the `vector` method:
293
294```rust
295# fn main() {
296use fitsio::tables::{ColumnDataDescription, ColumnDescription, ColumnDataType};
297
298let desc = ColumnDataDescription::vector(ColumnDataType::Int, 100);
299assert_eq!(desc.repeat, 100);
300assert_eq!(desc.width, 1);
301# }
302```
303
304These impl `From<...> for String` such that the traditional fits column description string can
305be obtained:
306
307```rust
308# fn main() {
309use fitsio::tables::{ColumnDataDescription, ColumnDescription, ColumnDataType};
310
311let desc = ColumnDataDescription::scalar(ColumnDataType::Int);
312assert_eq!(String::from(desc), "1J".to_string());
313# }
314```
315
316## Copying HDUs to another file
317
318A HDU can be copied to another open file with the [`copy_to`][fits-hdu-copy-to] method. This
319requires another open [`FitsFile`][fits-file] object to copy to:
320
321```rust
322# fn try_main() -> Result<(), Box<std::error::Error>> {
323# let filename = "../testdata/full_example.fits";
324# let mut src_fptr = fitsio::FitsFile::open(filename)?;
325#
326# let tdir = tempfile::Builder::new().prefix("fitsio-").tempdir().unwrap();
327# let tdir_path = tdir.path();
328# let filename = tdir_path.join("test.fits");
329# let mut dest_fptr = fitsio::FitsFile::create(filename).open()?;
330#
331# let hdu = src_fptr.hdu(1)?;
332hdu.copy_to(&mut src_fptr, &mut dest_fptr)?;
333# Ok(())
334# }
335# fn main() { try_main().unwrap(); }
336```
337
338## Deleting a HDU
339
340The current HDU can be deleted using the [`delete`][fits-hdu-delete] method. Note: this method
341takes ownership of `self`, and as such the [`FitsHdu`][fits-hdu] object cannot be used after
342this is called.
343
344```rust
345# use fitsio::images::{ImageType, ImageDescription};
346# fn try_main() -> Result<(), Box<std::error::Error>> {
347# let tdir = tempfile::Builder::new().prefix("fitsio-").tempdir().unwrap();
348# let tdir_path = tdir.path();
349# let filename = tdir_path.join("test.fits");
350# let mut fptr = fitsio::FitsFile::create(filename).open()?;
351# let image_description = ImageDescription {
352# data_type: ImageType::Float,
353# dimensions: &[100, 100],
354# };
355# let hdu = fptr.create_image("EXTNAME".to_string(), &image_description)?;
356// let fptr = FitsFile::open(...)?;
357// let hdu = fptr.hdu(0)?;
358hdu.delete(&mut fptr)?;
359// Cannot use hdu after this
360# Ok(())
361# }
362# fn main() { try_main().unwrap(); }
363```
364
365## Iterating over the HDUs in a file
366
367The [`iter`][fits-hdu-iter] method allows for iteration over the HDUs of a fits file.
368
369```rust
370# fn try_main() -> Result<(), Box<std::error::Error>> {
371# let mut fptr = fitsio::FitsFile::open("../testdata/full_example.fits")?;
372for hdu in fptr.iter() {
373 // Do something with hdu
374}
375# Ok(())
376# }
377# fn main() { try_main().unwrap(); }
378```
379
380## General calling behaviour
381
382All subsequent data acess is performed through the [`FitsHdu`][fits-hdu] object. Most methods
383take the currently open [`FitsFile`][fits-file] as the first parameter.
384
385# Header keys
386
387Header keys are read through the [`read_key`][fits-hdu-read-key] function,
388and is generic over types that implement the [`ReadsKey`][reads-key] trait:
389
390```rust
391# fn try_main() -> Result<(), Box<dyn std::error::Error>> {
392# let filename = "../testdata/full_example.fits";
393# let mut fptr = fitsio::FitsFile::open(filename)?;
394# {
395let int_value: i64 = fptr.hdu(0)?.read_key(&mut fptr, "INTTEST")?;
396# }
397
398// Alternatively
399# {
400let int_value = fptr.hdu(0)?.read_key::<i64>(&mut fptr, "INTTEST")?;
401# }
402
403
404// Or let the compiler infer the types (if possible)
405# Ok(())
406# }
407# fn main() { try_main().unwrap(); }
408```
409
410[`HeaderValue`] also implements the [`ReadsKey`][reads-key] trait, and allows the reading of comments:
411
412```rust
413# fn try_main() -> Result<(), Box<dyn std::error::Error>> {
414# use fitsio::HeaderValue;
415# let filename = "../testdata/full_example.fits";
416# let mut fptr = fitsio::FitsFile::open(filename)?;
417# {
418let int_value_with_comment: HeaderValue<i64> = fptr.hdu(0)?.read_key(&mut fptr, "INTTEST")?;
419let HeaderValue { value, comment } = int_value_with_comment;
420# }
421# Ok(())
422# }
423```
424
425
426Header cards can be written through the method [`write_key`][fits-hdu-write-key].
427It takes a key name and value, or a key name and value-comment tuple.
428See the [`WritesKey`][writes-key] trait for supported data types.
429
430```rust
431# fn try_main() -> Result<(), Box<dyn std::error::Error>> {
432# let tdir = tempfile::Builder::new().prefix("fitsio-").tempdir().unwrap();
433# let tdir_path = tdir.path();
434# let filename = tdir_path.join("test.fits");
435# {
436# let mut fptr = fitsio::FitsFile::create(filename).open()?;
437fptr.hdu(0)?.write_key(&mut fptr, "foo", 1i64)?;
438assert_eq!(fptr.hdu(0)?.read_key::<i64>(&mut fptr, "foo")?, 1i64);
439
440// with comments
441# use fitsio::HeaderValue;
442fptr.hdu(0)?.write_key(&mut fptr, "bar", (1i64, "bar comment"))?;
443
444let HeaderValue { value, comment } = fptr.hdu(0)?.read_key::<HeaderValue<i64>>(&mut fptr, "bar")?;
445assert_eq!(value, 1i64);
446assert_eq!(comment, Some("bar comment".to_string()));
447# Ok(())
448# }
449# }
450# fn main() { try_main().unwrap(); }
451```
452
453# Reading file data
454
455Methods taking ranges are exclusive of the upper range value, reflecting the nature of Rust's
456range type.
457
458## Reading images
459
460Image data can be read through either
461[`read_section`][fits-hdu-read-section] which reads contiguous pixels
462between a start index and end index, or
463[`read_region`][fits-hdu-read-region] which reads rectangular chunks from
464the image.
465
466```rust
467# fn try_main() -> Result<(), Box<std::error::Error>> {
468# let filename = "../testdata/full_example.fits";
469# let mut fptr = fitsio::FitsFile::open(filename)?;
470# let hdu = fptr.hdu(0)?;
471// Read the first 100 pixels
472let first_row: Vec<i32> = hdu.read_section(&mut fptr, 0, 100)?;
473
474// Read a square section of the image
475let xcoord = 0..10;
476let ycoord = 0..10;
477let chunk: Vec<i32> = hdu.read_region(&mut fptr, &[&ycoord, &xcoord])?;
478# Ok(())
479# }
480# fn main() { try_main().unwrap(); }
481```
482
483_Unlike cfitsio, the order of the the section ranges follows the C convention, i.e.
484[row-major order](https://en.wikipedia.org/wiki/Row-_and_column-major_order)._
485
486Some convenience methods are available for reading rows of the image. This is
487typically useful as it's an efficient access method:
488
489```rust
490# fn try_main() -> Result<(), Box<std::error::Error>> {
491# let filename = "../testdata/full_example.fits";
492# let mut fptr = fitsio::FitsFile::open(filename)?;
493# let hdu = fptr.hdu(0)?;
494let start_row = 0;
495let num_rows = 10;
496let first_few_rows: Vec<f32> = hdu.read_rows(&mut fptr, start_row, num_rows)?;
497
498// 10 rows of 100 columns
499assert_eq!(first_few_rows.len(), 1000);
500# Ok(())
501# }
502# fn main() { try_main().unwrap(); }
503```
504
505The whole image can also be read into memory:
506
507```rust
508# fn try_main() -> Result<(), Box<std::error::Error>> {
509# let filename = "../testdata/full_example.fits";
510# let mut fptr = fitsio::FitsFile::open(filename)?;
511# let hdu = fptr.hdu(0)?;
512let image_data: Vec<f32> = hdu.read_image(&mut fptr, )?;
513
514// 100 rows of 100 columns
515assert_eq!(image_data.len(), 10_000);
516# Ok(())
517# }
518# fn main() { try_main().unwrap(); }
519```
520
521### [`ndarray`][ndarray] support
522
523When `fitsio` is compiled with the `array` feature, images can be read into
524the [`ndarray::ArrayD`][arrayd] type:
525
526```rust
527# #[cfg(feature = "array")]
528# fn main() {
529use fitsio::FitsFile;
530# #[cfg(feature = "array")]
531use ndarray::ArrayD;
532
533let mut f = FitsFile::open("../testdata/full_example.fits").unwrap();
534let hdu = f.primary_hdu().unwrap();
535
536let data: ArrayD<u32> = hdu.read_image(&mut f).unwrap();
537let dim = data.dim();
538assert_eq!(dim[0], 100);
539assert_eq!(dim[1], 100);
540assert_eq!(data[[20, 5]], 152);
541# }
542#
543# #[cfg(not(feature = "array"))]
544# fn main() {}
545```
546
547For more details, see the [`ndarray_compat`](ndarray_compat/index.html) documentation (only
548available if compiled with `array` feature).
549
550## Reading tables
551
552Columns can be read using the [`read_col`][fits-hdu-read-col] function,
553which can convert data types on the fly. See the [`ReadsCol`][reads-col] trait for
554supported data types.
555
556```rust
557# fn try_main() -> Result<(), Box<std::error::Error>> {
558# let filename = "../testdata/full_example.fits";
559# let mut fptr = fitsio::FitsFile::open(filename)?;
560# let hdu = fptr.hdu(1);
561let integer_data: Vec<i32> = hdu.and_then(|hdu| hdu.read_col(&mut fptr, "intcol"))?;
562# Ok(())
563# }
564# fn main() { try_main().unwrap(); }
565```
566
567### Reading cell values
568
569Individual cell values can be read from FITS tables:
570
571```rust
572# fn try_main() -> Result<(), Box<std::error::Error>> {
573# let filename = "../testdata/full_example.fits[TESTEXT]";
574# let mut f = fitsio::FitsFile::open(filename)?;
575# let tbl_hdu = f.hdu("TESTEXT")?;
576let result: i64 = tbl_hdu.read_cell_value(&mut f, "intcol", 4)?;
577assert_eq!(result, 16);
578
579let result: String = tbl_hdu.read_cell_value(&mut f, "strcol", 4)?;
580assert_eq!(result, "value4".to_string());
581# Ok(())
582# }
583# fn main() { try_main().unwrap(); }
584```
585
586### Reading rows
587
588Single rows can be read from a fits table with the [`row`][fits-hdu-row] method. This requires
589use of the [`fitsio-derive`][fitsio-derive] crate.
590
591```rust
592use fitsio::tables::FitsRow;
593use fitsio_derive::FitsRow;
594
595#[derive(Default, FitsRow)]
596struct Row {
597 #[fitsio(colname = "intcol")]
598 intfoo: i32,
599 #[fitsio(colname = "strcol")]
600 foobar: String,
601}
602#
603# fn try_main() -> Result<(), Box<std::error::Error>> {
604# let filename = "../testdata/full_example.fits[TESTEXT]";
605# let mut f = fitsio::FitsFile::open(filename)?;
606# let hdu = f.hdu("TESTEXT")?;
607
608// Pick the 4th row
609let row: Row = hdu.row(&mut f, 4)?;
610assert_eq!(row.intfoo, 16);
611assert_eq!(row.foobar, "value4");
612# Ok(())
613# }
614# fn main() { try_main().unwrap(); }
615```
616
617## Iterating over columns
618
619Iterate over the columns with [`columns`][fits-hdu-columns].
620
621```rust
622# fn try_main() -> Result<(), Box<std::error::Error>> {
623# let filename = "../testdata/full_example.fits";
624# let mut fptr = fitsio::FitsFile::open(filename)?;
625# let hdu = fptr.hdu("TESTEXT")?;
626for column in hdu.columns(&mut fptr) {
627 // Do something with column
628}
629# Ok(())
630# }
631# fn main() { try_main().unwrap(); }
632```
633
634# Writing file data
635
636Methods taking ranges are exclusive of the upper range value, reflecting the nature of Rust's
637range type.
638
639## Writing images
640
641Image data is written through three methods on the HDU object:
642[`write_section`][fits-hdu-write-section], [`write_region`][fits-hdu-write-region], and
643[`write_image`][fits-hdu-write-image].
644
645[`write_section`][fits-hdu-write-section] requires a start index and
646end index and data to write. The data parameter needs to be a slice, meaning any contiguous
647memory storage method (e.g. `Vec`) can be passed.
648
649```rust
650# use fitsio::images::{ImageType, ImageDescription};
651#
652# fn try_main() -> Result<(), Box<std::error::Error>> {
653# let tdir = tempfile::Builder::new().prefix("fitsio-").tempdir().unwrap();
654# let tdir_path = tdir.path();
655# let filename = tdir_path.join("test.fits");
656# let mut fptr = fitsio::FitsFile::create(filename).open()?;
657# let desc = ImageDescription {
658# data_type: ImageType::Float,
659# dimensions: &[100, 100],
660# };
661# let hdu = fptr.create_image("".to_string(), &desc)?;
662let data_to_write: Vec<f64> = vec![1.0, 2.0, 3.0];
663hdu.write_section(&mut fptr, 0, data_to_write.len(), &data_to_write)?;
664# Ok(())
665# }
666# fn main() { try_main().unwrap(); }
667```
668
669[`write_region`][fits-hdu-write-region] takes a slice of ranges with which
670the data is to be written, and the data to write.
671
672```rust
673# use fitsio::images::{ImageType, ImageDescription};
674#
675# fn try_main() -> Result<(), Box<std::error::Error>> {
676# let tdir = tempfile::Builder::new().prefix("fitsio-").tempdir().unwrap();
677# let tdir_path = tdir.path();
678# let filename = tdir_path.join("test.fits");
679# let mut fptr = fitsio::FitsFile::create(filename).open()?;
680# let desc = ImageDescription {
681# data_type: ImageType::Float,
682# dimensions: &[100, 100],
683# };
684# let hdu = fptr.create_image("".to_string(), &desc)?;
685let data_to_write: Vec<f64> = vec![1.0, 2.0, 3.0, 4.0];
686let ranges = [&(0..1), &(0..1)];
687hdu.write_region(&mut fptr, &ranges, &data_to_write)?;
688# Ok(())
689# }
690# fn main() { try_main().unwrap(); }
691```
692
693_Unlike cfitsio, the order of the ranges follows the C convention, i.e.
694[row-major order](https://en.wikipedia.org/wiki/Row-_and_column-major_order)._
695
696[`write_image`][fits-hdu-write-image] writes all of the data passed (if possible) into the
697image. If more data is passed than pixels in the image, the method returns with an error.
698
699```rust
700# use fitsio::images::{ImageType, ImageDescription};
701#
702# fn try_main() -> Result<(), Box<std::error::Error>> {
703# let tdir = tempfile::Builder::new().prefix("fitsio-").tempdir().unwrap();
704# let tdir_path = tdir.path();
705# let filename = tdir_path.join("test.fits");
706# let mut fptr = fitsio::FitsFile::create(filename).open()?;
707# let desc = ImageDescription {
708# data_type: ImageType::Float,
709# dimensions: &[3, 1],
710# };
711# let hdu = fptr.create_image("".to_string(), &desc)?;
712// Image is 3x1
713assert!(hdu.write_image(&mut fptr, &[1.0, 2.0, 3.0]).is_ok());
714assert!(hdu.write_image(&mut fptr, &[1.0, 2.0, 3.0, 4.0]).is_err());
715# Ok(())
716# }
717# fn main() { try_main().unwrap(); }
718```
719
720### Resizing an image
721
722Images can be resized to a new shape using the [`resize`][fits-hdu-resize] method.
723
724The method takes the open [`FitsFile`][fits-file], and an slice of `usize` values. Note:
725currently `fitsio` only supports slices with length 2, i.e. a 2D image.
726[`resize`][fits-hdu-resize] takes ownership `self` to force the user to fetch the HDU object
727again. This ensures the image changes are reflected in the hew HDU object.
728
729```rust
730# use std::fs::copy;
731# fn try_main() -> Result<(), Box<std::error::Error>> {
732# let tdir = tempfile::Builder::new().prefix("fitsio-").tempdir().unwrap();
733# let tdir_path = tdir.path();
734# let filename = tdir_path.join("test.fits");
735# copy("../testdata/full_example.fits", &filename)?;
736# let filename = filename.to_str().unwrap();
737# let mut fptr = fitsio::FitsFile::edit(filename)?;
738# let hdu = fptr.hdu(0)?;
739use fitsio::hdu::HduInfo;
740
741hdu.resize(&mut fptr, &[1024, 1024])?;
742
743// Have to get the HDU again, to reflect the latest changes
744let hdu = fptr.hdu(0)?;
745match hdu.info {
746 HduInfo::ImageInfo { shape, .. } => {
747 assert_eq!(shape, [1024, 1024]);
748 }
749 _ => panic!("Unexpected hdu type"),
750}
751# Ok(())
752# }
753# fn main() { try_main().unwrap(); }
754```
755
756_Unlike cfitsio, the order of the dimensions of `new_size` follows the C convention, i.e.
757[row-major order](https://en.wikipedia.org/wiki/Row-_and_column-major_order)._
758
759## Writing tables
760
761### Writing table data
762
763Tablular data can either be written with [`write_col`][fits-hdu-write-col] or
764[`write_col_range`][fits-hdu-write-col-range].
765
766[`write_col`][fits-hdu-write-col] writes an entire column's worth of data to the file. It does
767not check how many rows are in the file, but extends the table if the length of data is longer
768than the table length.
769
770```rust
771# use std::fs::copy;
772# use fitsio::hdu::HduInfo;
773# use fitsio::tables::{ColumnDescription, ColumnDataType};
774# fn try_main() -> Result<(), Box<std::error::Error>> {
775# let tdir = tempfile::Builder::new().prefix("fitsio-").tempdir().unwrap();
776# let tdir_path = tdir.path();
777# let filename = tdir_path.join("test.fits");
778# let mut fptr = fitsio::FitsFile::create(filename).open()?;
779# let table_description = vec![
780# ColumnDescription::new("bar")
781# .with_type(ColumnDataType::Int)
782# .create()
783# ?,
784# ];
785# let hdu = fptr.create_table("foo".to_string(), &table_description)
786# ?;
787let data_to_write: Vec<i32> = vec![10101; 5];
788hdu.write_col(&mut fptr, "bar", &data_to_write)?;
789let data: Vec<i32> = hdu.read_col(&mut fptr, "bar")?;
790assert_eq!(data, vec![10101, 10101, 10101, 10101, 10101]);
791# Ok(())
792# }
793# fn main() { try_main().unwrap(); }
794```
795
796[`write_col_range`][fits-hdu-write-col-range] writes data to a range of rows in a table. The
797range is inclusive of both the upper and lower bounds, so `0..4` writes 5 elements.
798
799```rust
800# use std::fs::copy;
801# use fitsio::hdu::HduInfo;
802# use fitsio::tables::{ColumnDescription, ColumnDataType};
803# fn try_main() -> Result<(), Box<std::error::Error>> {
804# let tdir = tempfile::Builder::new().prefix("fitsio-").tempdir().unwrap();
805# let tdir_path = tdir.path();
806# let filename = tdir_path.join("test.fits");
807# let mut fptr = fitsio::FitsFile::create(filename).open()?;
808# let table_description = vec![
809# ColumnDescription::new("bar")
810# .with_type(ColumnDataType::Int)
811# .create()?,
812# ];
813# let hdu = fptr.create_table("foo".to_string(), &table_description)?;
814let data_to_write: Vec<i32> = vec![10101; 10];
815hdu.write_col_range(&mut fptr, "bar", &data_to_write, &(0..5))?;
816let data: Vec<i32> = hdu.read_col(&mut fptr, "bar")?;
817assert_eq!(data, vec![10101, 10101, 10101, 10101, 10101]);
818# Ok(())
819# }
820# fn main() { try_main().unwrap(); }
821```
822
823### Inserting columns
824
825Two methods on the HDU object allow for adding new columns:
826[`append_column`][fits-hdu-append-column]
827and [`insert_column`][fits-hdu-insert-column].
828[`append_column`][fits-hdu-append-column] adds a new column as the last column member, and is
829generally
830preferred as it does not require shifting of data within the file.
831
832```rust
833use fitsio::tables::{ColumnDescription, ColumnDataType};
834
835# fn try_main() -> Result<(), Box<std::error::Error>> {
836# let tdir = tempfile::Builder::new().prefix("fitsio-").tempdir().unwrap();
837# let tdir_path = tdir.path();
838# let filename = tdir_path.join("test.fits");
839# let mut fptr = fitsio::FitsFile::create(filename).open()?;
840# let table_description = &[
841# ColumnDescription::new("bar")
842# .with_type(ColumnDataType::Int)
843# .create()?,
844# ];
845# let hdu = fptr.create_table("foo".to_string(), table_description)?;
846let column_description = ColumnDescription::new("abcdefg")
847 .with_type(ColumnDataType::Int)
848 .create()?;
849hdu.append_column(&mut fptr, &column_description)?;
850# Ok(())
851# }
852# fn main() { try_main().unwrap(); }
853```
854
855### Deleting columns
856
857The HDU object has the method [`delete_column`][fits-hdu-delete-column] which removes a column.
858The column can either be accessed by integer or name
859
860```rust
861# use fitsio::tables::{ColumnDescription, ColumnDataType};
862# fn try_main() -> Result<(), Box<std::error::Error>> {
863# {
864# let tdir = tempfile::Builder::new().prefix("fitsio-").tempdir().unwrap();
865# let tdir_path = tdir.path();
866# let filename = tdir_path.join("test.fits");
867# let mut fptr = fitsio::FitsFile::create(filename).open()?;
868# let table_description = &[
869# ColumnDescription::new("bar")
870# .with_type(ColumnDataType::Int)
871# .create()?,
872# ];
873# let hdu = fptr.create_table("foo".to_string(), table_description)?;
874let newhdu = hdu.delete_column(&mut fptr, "bar")?;
875# }
876# {
877# let tdir = tempfile::Builder::new().prefix("fitsio-").tempdir().unwrap();
878# let tdir_path = tdir.path();
879# let filename = tdir_path.join("test.fits");
880# let mut fptr = fitsio::FitsFile::create(filename).open()?;
881# let table_description = &[
882# ColumnDescription::new("bar")
883# .with_type(ColumnDataType::Int)
884# .create()?,
885# ];
886# let hdu = fptr.create_table("foo".to_string(), table_description)?;
887// or
888let newhdu = hdu.delete_column(&mut fptr, 0)?;
889# }
890# Ok(())
891# }
892# fn main() { try_main().unwrap(); }
893```
894
895# Raw fits file access
896
897## Converting a `FitsFile` to a raw `fitsio_sys::fitsfile` pointer
898
899If this library does not support the particular use case that is needed, the raw `fitsfile`
900pointer can be accessed:
901
902```rust
903# use fitsio::sys as sys;
904use fitsio::FitsFile;
905
906# fn try_main() -> Result<(), Box<dyn std::error::Error>> {
907# let filename = "../testdata/full_example.fits";
908let mut fptr = FitsFile::open(filename)?;
909
910/* Find out the number of HDUs in the file */
911let mut num_hdus = 0;
912let mut status = 0;
913
914unsafe {
915 let fitsfile = fptr.as_raw();
916
917 /* Use the unsafe fitsio-sys low level library to call a function that is possibly not
918 implemented in this crate */
919 fitsio_sys::ffthdu(fitsfile, &mut num_hdus, &mut status);
920}
921assert_eq!(num_hdus, 2);
922# Ok(())
923# }
924# fn main() { try_main().unwrap(); }
925```
926
927This (unsafe) pointer can then be used with the underlying [`fitsio-sys`][fitsio-sys] library directly.
928
929## Creating a `FitsFile` from a raw `fitsio_sys::fitsfile` pointer
930
931The inverse of the process described above can be performed. Note: calling this [`FitsFile`]
932constructor is _unsafe_ => it is up to the caller to guarantee that the pointer given was
933
9341. created by `cfitsio` (or [`fitsio_sys`]), and
9352. it represents a valid FITS file.
936
937Given these two things, a [`FitsFile`] can be created.
938
939```rust
940use fitsio::{sys::ffopen, FileOpenMode, FitsFile};
941
942# fn main() -> Result<(), Box<dyn std::error::Error>> {
943let filename = "../testdata/full_example.fits";
944let mut fptr = std::ptr::null_mut();
945let mut status = 0;
946let c_filename = std::ffi::CString::new(filename).expect("filename is not a valid C-string");
947
948unsafe {
949 ffopen(
950 &mut fptr as *mut *mut _,
951 c_filename.as_ptr(),
952 0, // readonly
953 &mut status,
954 );
955}
956assert_eq!(status, 0);
957
958let mut f = unsafe { FitsFile::from_raw(fptr, FileOpenMode::READONLY) }.unwrap();
959# Ok(())
960# }
961```
962
963# Threadsafe access
964
965Access to a [`FitsFile`][fits-file] is not threadsafe. Behind the scenes, fetching a
966[`FitsHdu`][fits-hdu] changes internal state, and `fitsio` does not provide any concurrent access
967gauruntees. Therefore, a [`FitsFile`][fits-file] does not implement `Send` or `Sync`.
968
969In order to allow for threadsafe access, the [`FitsFile`][fits-file] struct has a
970[`threadsafe`][fits-file-threadsafe] method, which returns a threadsafe
971[`ThreadsafeFitsFile`][threadsafe-fits-file] struct (a tuple-type wrapper around
972`Arc<Mutex<FitsFile>>`) which can be shared between threads safely.
973
974The same concerns with `Arc<Mutex<T>>` data should be applied here. Additionally, the library is
975subject to OS level limits, such as the maximum number of open files.
976
977## Example
978
979```rust
980# use fitsio::FitsFile;
981# use std::thread;
982# let fptr = FitsFile::open("../testdata/full_example.fits").unwrap();
983let f = fptr.threadsafe();
984
985/* Spawn loads of threads... */
986# let mut handles = Vec::new();
987for i in 0..100 {
988let mut f1 = f.clone();
989
990/* Send the cloned ThreadsafeFitsFile to another thread */
991let handle = thread::spawn(move || {
992/* Get the underlyng fits file back */
993let mut t = f1.lock().unwrap();
994
995/* Fetch a different HDU per thread */
996let hdu_num = i % 2;
997let _hdu = t.hdu(hdu_num).unwrap();
998});
999# handles.push(handle);
1000}
1001#
1002# /* Wait for all of the threads to finish */
1003# for handle in handles {
1004# handle.join().unwrap();
1005# }
1006```
1007
1008[cfitsio]: http://heasarc.gsfc.nasa.gov/fitsio/fitsio.html
1009[fitsio-sys]: https://crates.io/crates/fitsio-sys
1010[column-data-description]: tables/struct.ColumnDataDescription.html
1011[column-data-type]: tables/enum.ColumnDataType.html
1012[column-description]: tables/struct.ColumnDescription.html
1013[fits-file-num-hdus]: fitsfile/struct.FitsFile.html#method.num_hdus
1014[fits-file-create-image]: fitsfile/struct.FitsFile.html#method.create_image
1015[fits-file-create-table]: fitsfile/struct.FitsFile.html#method.create_table
1016[fits-file-create]: fitsfile/struct.FitsFile.html#method.create
1017[fits-file-edit]: fitsfile/struct.FitsFile.html#method.edit
1018[fits-file-threadsafe]: fitsfile/struct.FitsFile.html#method.threadsafe
1019[fits-file]: fitsfile/struct.FitsFile.html
1020[fits-hdu]: hdu/struct.FitsHdu.html
1021[fits-hdu-append-column]: hdu/struct.FitsHdu.html#method.append_column
1022[fits-hdu-columns]: hdu/struct.FitsHdu.html#method.columns
1023[fits-hdu-delete-column]: hdu/struct.FitsHdu.html#method.delete_column
1024[fits-hdu-insert-column]: hdu/struct.FitsHdu.html#method.insert_column
1025[fits-hdu-read-col]: hdu/struct.FitsHdu.html#method.read_col
1026[fits-hdu-read-key]: hdu/struct.FitsHdu.html#method.read_key
1027[fits-hdu-read-region]: hdu/struct.FitsHdu.html#method.read_region
1028[fits-hdu-read-section]: hdu/struct.FitsHdu.html#method.read_section
1029[fits-hdu-write-key]: hdu/struct.FitsHdu.html#method.write_key
1030[fits-hdu-write-col]: hdu/struct.FitsHdu.html#method.write_col
1031[fits-hdu-write-col-range]: hdu/struct.FitsHdu.html#method.write_col_range
1032[fits-hdu-write-region]: hdu/struct.FitsHdu.html#method.write_region
1033[fits-hdu-write-image]: hdu/struct.FitsHdu.html#method.write_image
1034[fits-hdu-write-section]: hdu/struct.FitsHdu.html#method.write_section
1035[fits-hdu-iter]: hdu/struct.FitsHdu.html#method.iter
1036[fits-hdu-copy-to]: hdu/struct.FitsHdu.html#method.copy_to
1037[fits-hdu-delete]: hdu/struct.FitsHdu.html#method.copy_to
1038[fits-hdu-resize]: hdu/struct.FitsHdu.html#method.resize
1039[fits-hdu-row]: hdu/struct.FitsHdu.html#method.row
1040[image-description]: images/struct.ImageDescription.html
1041[reads-col]: tables/trait.ReadsCol.html
1042[reads-key]: headers/trait.ReadsKey.html
1043[writes-key]: headers/trait.WritesKey.html
1044[new-fits-file]: fitsfile/struct.NewFitsFile.html
1045[new-fits-file-open]: fitsfile/struct.NewFitsFile.html#method.open
1046[new-fits-file-with-custom-primary]: fitsfile/struct.NewFitsFile.html#method.with_custom_primary
1047[pretty-print]: fitsfile/struct.FitsFile.html#method.pretty_print
1048[pretty-write]: fitsfile/struct.FitsFile.html#method.pretty_write
1049[fitsio-derive]: https://crates.io/crates/fitsio-derive
1050[ndarray]: https://crates.io/crates/ndarray
1051[arrayd]: https://docs.rs/ndarray/0.11.2/ndarray/type.ArrayD.html
1052[fitsfile-open]: fitsfile/struct.FitsFile.html#method.open
1053[`fitssummary`]: ../fitssummary/index.html
1054[fitsfile-hdu]: fitsfile/struct.FitsFile.html#method.hdu
1055[threadsafe-fits-file]: threadsafe_fitsfile/struct.ThreadsafeFitsFile.html
1056*/
1057
1058#![doc(html_root_url = "https://docs.rs/fitsio/0.21.9")]
1059#![deny(missing_docs)]
1060#![cfg_attr(feature = "clippy", feature(plugin))]
1061#![cfg_attr(feature = "clippy", plugin(clippy))]
1062#![allow(clippy::uninlined_format_args)]
1063
1064pub use fitsio_sys as sys;
1065
1066// re-export version information
1067pub use sys::{cfitsio_version, CfitsioVersion};
1068
1069#[macro_use]
1070mod macros;
1071mod fitsfile;
1072mod longnam;
1073#[cfg(feature = "array")]
1074mod ndarray_compat;
1075mod stringutils;
1076#[cfg(test)]
1077mod testhelpers;
1078mod types;
1079
1080// Public mods
1081pub mod hdu;
1082pub mod headers;
1083pub mod images;
1084pub mod tables;
1085pub mod threadsafe_fitsfile;
1086
1087pub mod errors;
1088
1089// Re-exports
1090pub use crate::fitsfile::{FileOpenMode, FitsFile};
1091pub use crate::headers::HeaderValue;
1092
1093// For custom derive purposes
1094// pub use tables::FitsRow;