github.com/astrogo/fitsio@v0.3.0/file.go (about)

     1  // Copyright 2015 The astrogo Authors.  All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package fitsio
     6  
     7  import (
     8  	"fmt"
     9  	"io"
    10  	"os"
    11  )
    12  
    13  // Mode defines a FITS file access mode (r/w)
    14  type Mode int
    15  
    16  const (
    17  	ReadOnly  Mode = Mode(os.O_RDONLY) // open the file read-only
    18  	WriteOnly      = Mode(os.O_WRONLY) // open the file write-only
    19  	ReadWrite      = Mode(os.O_RDWR)   // open the file read-write
    20  )
    21  
    22  // File represents a FITS file.
    23  type File struct {
    24  	dec  Decoder
    25  	enc  Encoder
    26  	name string
    27  	mode Mode
    28  	hdus []HDU
    29  }
    30  
    31  // Open opens a FITS file in read-only mode.
    32  func Open(r io.Reader) (*File, error) {
    33  	var err error
    34  
    35  	type namer interface {
    36  		Name() string
    37  	}
    38  	name := ""
    39  	if r, ok := r.(namer); ok {
    40  		name = r.Name()
    41  	}
    42  
    43  	f := &File{
    44  		dec:  NewDecoder(r),
    45  		name: name,
    46  		mode: ReadOnly,
    47  		hdus: make([]HDU, 0, 1),
    48  	}
    49  
    50  	for {
    51  		var hdu HDU
    52  		hdu, err = f.dec.DecodeHDU()
    53  		if err != nil {
    54  			if err != io.EOF {
    55  				return nil, err
    56  			}
    57  			err = nil
    58  			break
    59  		}
    60  		f.hdus = append(f.hdus, hdu)
    61  	}
    62  
    63  	return f, err
    64  }
    65  
    66  // Create creates a new FITS file in write-only mode
    67  func Create(w io.Writer) (*File, error) {
    68  	var err error
    69  	type namer interface {
    70  		Name() string
    71  	}
    72  	name := ""
    73  	if w, ok := w.(namer); ok {
    74  		name = w.Name()
    75  	}
    76  
    77  	f := &File{
    78  		enc:  NewEncoder(w),
    79  		name: name,
    80  		mode: WriteOnly,
    81  		hdus: make([]HDU, 0, 1),
    82  	}
    83  
    84  	return f, err
    85  }
    86  
    87  // Close releases resources held by a FITS file.
    88  //
    89  // It does not close the underlying io.Reader or io.Writer.
    90  func (f *File) Close() error {
    91  	f.enc = nil
    92  	f.dec = nil
    93  	f.hdus = nil
    94  	return nil
    95  }
    96  
    97  // Mode returns the access-mode of this FITS file
    98  func (f *File) Mode() Mode {
    99  	return f.mode
   100  }
   101  
   102  // Name returns the name of the FITS file
   103  func (f *File) Name() string {
   104  	return f.name
   105  }
   106  
   107  // HDUs returns the list of all Header-Data Unit blocks in the file
   108  func (f *File) HDUs() []HDU {
   109  	return f.hdus
   110  }
   111  
   112  // HDU returns the i-th HDU
   113  func (f *File) HDU(i int) HDU {
   114  	return f.hdus[i]
   115  }
   116  
   117  // Get returns the HDU with name `name` or nil
   118  func (f *File) Get(name string) HDU {
   119  	i, hdu := f.gethdu(name)
   120  	if i < 0 {
   121  		return nil
   122  	}
   123  	return hdu
   124  }
   125  
   126  // Has returns whether the File has a HDU with name `name`.
   127  func (f *File) Has(name string) bool {
   128  	i, _ := f.gethdu(name)
   129  	if i < 0 {
   130  		return false
   131  	}
   132  	return true
   133  }
   134  
   135  // get returns the index and HDU of HDU with name `name`.
   136  func (f *File) gethdu(name string) (int, HDU) {
   137  	for i, hdu := range f.hdus {
   138  		if hdu.Name() == name {
   139  			return i, hdu
   140  		}
   141  	}
   142  	return -1, nil
   143  }
   144  
   145  // Write writes a HDU to file
   146  func (f *File) Write(hdu HDU) error {
   147  	var err error
   148  	if f.mode != WriteOnly && f.mode != ReadWrite {
   149  		return fmt.Errorf("fitsio: file not open for write")
   150  	}
   151  
   152  	if len(f.hdus) == 0 {
   153  		if hdu.Type() != IMAGE_HDU {
   154  			return fmt.Errorf("fitsio: file has no primary header. create one first")
   155  		}
   156  
   157  		hdr := hdu.Header()
   158  		if hdr.Get("SIMPLE") == nil {
   159  			err = hdr.prepend(Card{
   160  				Name:    "SIMPLE",
   161  				Value:   true,
   162  				Comment: "primary HDU",
   163  			})
   164  			if err != nil {
   165  				return err
   166  			}
   167  		}
   168  	} else {
   169  		switch hdu.Type() {
   170  		case IMAGE_HDU:
   171  			img := hdu.(Image)
   172  			err = img.freeze()
   173  			if err != nil {
   174  				return err
   175  			}
   176  
   177  		case ASCII_TBL, BINARY_TBL:
   178  			tbl := hdu.(*Table)
   179  			err = tbl.freeze()
   180  			if err != nil {
   181  				return err
   182  			}
   183  		}
   184  	}
   185  
   186  	err = f.enc.EncodeHDU(hdu)
   187  	if err != nil {
   188  		return err
   189  	}
   190  
   191  	err = f.append(hdu)
   192  	if err != nil {
   193  		return err
   194  	}
   195  
   196  	return err
   197  }
   198  
   199  // append appends an HDU to the list of Header-Data Unit blocks.
   200  func (f *File) append(hdu HDU) error {
   201  	var err error
   202  	if f.mode != WriteOnly && f.mode != ReadWrite {
   203  		return fmt.Errorf("fitsio: file not open for write")
   204  	}
   205  
   206  	// mare sure there is only one primary-hdu
   207  	if _, ok := hdu.(*primaryHDU); ok && len(f.hdus) != 0 {
   208  		return fmt.Errorf("fitsio: file has already a Primary HDU")
   209  	}
   210  
   211  	f.hdus = append(f.hdus, hdu)
   212  	return err
   213  }