github.com/astrogo/fitsio@v0.3.0/header.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  	"math/big"
    10  	"reflect"
    11  	"strconv"
    12  )
    13  
    14  // Header describes a Header-Data Unit of a FITS file
    15  type Header struct {
    16  	htype  HDUType // type of the HDU
    17  	bitpix int     // character information
    18  	axes   []int   // dimensions of image data array
    19  	cards  []Card  // content of the Header
    20  }
    21  
    22  // newHeader creates a new Header.
    23  // no test on the content of the cards is performed.
    24  func newHeader(cards []Card, htype HDUType, bitpix int, axes []int) *Header {
    25  	hdr := &Header{
    26  		htype:  htype,
    27  		bitpix: bitpix,
    28  		axes:   make([]int, len(axes)),
    29  		cards:  make([]Card, 0, len(cards)),
    30  	}
    31  	copy(hdr.axes, axes)
    32  	err := hdr.Append(cards...)
    33  	if err != nil {
    34  		panic(err)
    35  	}
    36  	return hdr
    37  }
    38  
    39  // NewHeader creates a new Header from a set of Cards, HDU Type, bitpix and axes.
    40  func NewHeader(cards []Card, htype HDUType, bitpix int, axes []int) *Header {
    41  	// short circuit: too many axes violates the FITS standard
    42  	if len(axes) > 999 {
    43  		panic(fmt.Errorf("fitsio: too many axes (got=%d > 999)", len(axes)))
    44  	}
    45  	hdr := newHeader(cards, htype, bitpix, axes)
    46  
    47  	// add (some) mandatory cards (BITPIX, NAXES, AXIS1, AXIS2)
    48  	keys := make(map[string]struct{}, len(cards))
    49  	for i := range hdr.cards {
    50  		card := &hdr.cards[i]
    51  		k := card.Name
    52  		keys[k] = struct{}{}
    53  	}
    54  
    55  	dcards := make([]Card, 0, 3)
    56  	if _, ok := keys["BITPIX"]; !ok {
    57  		dcards = append(dcards, Card{
    58  			Name:    "BITPIX",
    59  			Value:   hdr.Bitpix(),
    60  			Comment: "number of bits per data pixel",
    61  		})
    62  	}
    63  
    64  	if _, ok := keys["NAXIS"]; !ok {
    65  		dcards = append(dcards, Card{
    66  			Name:    "NAXIS",
    67  			Value:   len(hdr.Axes()),
    68  			Comment: "number of data axes",
    69  		})
    70  		nax := len(hdr.Axes())
    71  		for i := 0; i < nax; i++ {
    72  			axis := strconv.Itoa(i + 1) // index from 0, hdu starts at NAXIS1
    73  			key := "NAXIS" + axis
    74  			if _, ok := keys[key]; !ok {
    75  				dcards = append(dcards, Card{
    76  					Name:    key,
    77  					Value:   hdr.Axes()[i],
    78  					Comment: "length of data axis " + axis,
    79  				})
    80  			}
    81  		}
    82  	}
    83  
    84  	err := hdr.prepend(dcards...)
    85  	if err != nil {
    86  		panic(err)
    87  	}
    88  	return hdr
    89  }
    90  
    91  // NewDefaultHeader creates a Header with CFITSIO default Cards, of type IMAGE_HDU and
    92  // bitpix=8, no axes.
    93  func NewDefaultHeader() *Header {
    94  	return NewHeader(
    95  		[]Card{
    96  			{
    97  				Name:    "SIMPLE",
    98  				Value:   true,
    99  				Comment: "file does conform to FITS standard",
   100  			},
   101  			{
   102  				Name:    "BITPIX",
   103  				Value:   8,
   104  				Comment: "number of bits per data pixel",
   105  			},
   106  			{
   107  				Name:    "NAXIS",
   108  				Value:   0,
   109  				Comment: "number of data axes",
   110  			},
   111  			{
   112  				Name:    "NAXIS1",
   113  				Value:   0,
   114  				Comment: "length of data axis 1",
   115  			},
   116  			{
   117  				Name:    "NAXIS2",
   118  				Value:   0,
   119  				Comment: "length of data axis 2",
   120  			},
   121  		},
   122  		IMAGE_HDU,
   123  		8,
   124  		[]int{},
   125  	)
   126  }
   127  
   128  // Type returns the Type of this Header
   129  func (hdr *Header) Type() HDUType {
   130  	return hdr.htype
   131  }
   132  
   133  // Text returns the header's cards content as 80-byte lines
   134  func (hdr *Header) Text() string {
   135  	const kLINE = 80
   136  	buf := make([]byte, 0, kLINE*len(hdr.cards))
   137  	for i := range hdr.cards {
   138  		card := &hdr.cards[i]
   139  		line, err := makeHeaderLine(card)
   140  		if err != nil {
   141  			panic(fmt.Errorf("fitsio: %v", err))
   142  		}
   143  		buf = append(buf, line...)
   144  	}
   145  	return string(buf)
   146  }
   147  
   148  // Append appends a set of Cards to this Header
   149  func (hdr *Header) Append(cards ...Card) error {
   150  	var err error
   151  	keys := make(map[string]struct{}, len(hdr.cards))
   152  	for i := range hdr.cards {
   153  		card := &hdr.cards[i]
   154  		k := card.Name
   155  		keys[k] = struct{}{}
   156  	}
   157  
   158  	for _, card := range cards {
   159  		_, dup := keys[card.Name]
   160  		if dup {
   161  			switch card.Name {
   162  			case "COMMENT", "HISTORY", "":
   163  				hdr.cards = append(hdr.cards, card)
   164  				continue
   165  			case "END":
   166  				continue
   167  			default:
   168  				return fmt.Errorf("fitsio: duplicate Card [%s] (value=%v)", card.Name, card.Value)
   169  			}
   170  		}
   171  		rv := reflect.ValueOf(card.Value)
   172  		if rv.IsValid() {
   173  			switch rv.Type().Kind() {
   174  			case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
   175  				card.Value = int(rv.Int())
   176  			case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
   177  				card.Value = int(rv.Uint())
   178  			case reflect.Float32, reflect.Float64:
   179  				card.Value = rv.Float()
   180  			case reflect.Complex64, reflect.Complex128:
   181  				card.Value = rv.Complex()
   182  			case reflect.String:
   183  				card.Value = card.Value.(string)
   184  			case reflect.Bool:
   185  				card.Value = card.Value.(bool)
   186  			case reflect.Struct:
   187  				switch card.Value.(type) {
   188  				case big.Int:
   189  					// ok
   190  				default:
   191  					return fmt.Errorf(
   192  						"fitsio: invalid value type (%T) for card [%s] (kind=%v)",
   193  						card.Value, card.Name, rv.Type().Kind(),
   194  					)
   195  				}
   196  			default:
   197  				return fmt.Errorf(
   198  					"fitsio: invalid value type (%T) for card [%s] (kind=%v)",
   199  					card.Value, card.Name, rv.Type().Kind(),
   200  				)
   201  			}
   202  		}
   203  		hdr.cards = append(hdr.cards, card)
   204  	}
   205  	return err
   206  }
   207  
   208  // prepend prepends a (set of) cards to this Header
   209  func (hdr *Header) prepend(cards ...Card) error {
   210  	var err error
   211  	keys := make(map[string]struct{}, len(hdr.cards))
   212  	for i := range hdr.cards {
   213  		card := &hdr.cards[i]
   214  		k := card.Name
   215  		keys[k] = struct{}{}
   216  	}
   217  
   218  	hcards := make([]Card, 0, len(cards))
   219  	for _, card := range cards {
   220  		_, dup := keys[card.Name]
   221  		if dup {
   222  			switch card.Name {
   223  			case "COMMENT", "HISTORY", "":
   224  				hdr.cards = append(hdr.cards, card)
   225  				continue
   226  			case "END":
   227  				continue
   228  			default:
   229  				return fmt.Errorf("fitsio: duplicate Card [%s] (value=%v)", card.Name, card.Value)
   230  			}
   231  		}
   232  		rv := reflect.ValueOf(card.Value)
   233  		if rv.IsValid() {
   234  			switch rv.Type().Kind() {
   235  			case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
   236  				card.Value = int(rv.Int())
   237  			case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
   238  				card.Value = int(rv.Uint())
   239  			case reflect.Float32, reflect.Float64:
   240  				card.Value = rv.Float()
   241  			case reflect.Complex64, reflect.Complex128:
   242  				card.Value = rv.Complex()
   243  			case reflect.String:
   244  				card.Value = card.Value.(string)
   245  			case reflect.Bool:
   246  				card.Value = card.Value.(bool)
   247  			default:
   248  				return fmt.Errorf(
   249  					"fitsio: invalid value type (%T) for card [%s]",
   250  					card.Value, card.Name,
   251  				)
   252  			}
   253  		}
   254  		hcards = append(hcards, card)
   255  	}
   256  
   257  	hdr.cards = append(hcards, hdr.cards...)
   258  	return err
   259  }
   260  
   261  // Clear resets the Header to the default state.
   262  func (hdr *Header) Clear() {
   263  	hdr.cards = make([]Card, 0)
   264  	hdr.bitpix = 0
   265  	hdr.axes = make([]int, 0)
   266  }
   267  
   268  // get returns the Card (and its index) with name n if it exists.
   269  func (hdr *Header) get(n string) (int, *Card) {
   270  	for i := range hdr.cards {
   271  		c := &hdr.cards[i]
   272  		if n == c.Name {
   273  			return i, c
   274  		}
   275  	}
   276  	return -1, nil
   277  }
   278  
   279  // Get returns the Card with name n or nil if it doesn't exist.
   280  // If multiple cards with the same name exist, the first one is returned.
   281  func (hdr *Header) Get(n string) *Card {
   282  	_, card := hdr.get(n)
   283  	return card
   284  }
   285  
   286  // Card returns the i-th card.
   287  // Card panics if the index is out of range.
   288  func (hdr *Header) Card(i int) *Card {
   289  	return &hdr.cards[i]
   290  }
   291  
   292  // Comment returns the whole comment string for this Header.
   293  func (hdr *Header) Comment() string {
   294  	card := hdr.Get("COMMENT")
   295  	if card != nil {
   296  		return card.Value.(string)
   297  	}
   298  	return ""
   299  }
   300  
   301  // History returns the whole history string for this Header.
   302  func (hdr *Header) History() string {
   303  	card := hdr.Get("HISTORY")
   304  	if card != nil {
   305  		return card.Value.(string)
   306  	}
   307  	return ""
   308  }
   309  
   310  // Bitpix returns the bitpix value.
   311  func (hdr *Header) Bitpix() int {
   312  	return hdr.bitpix
   313  }
   314  
   315  // Axes returns the axes for this Header.
   316  func (hdr *Header) Axes() []int {
   317  	return hdr.axes
   318  }
   319  
   320  // Index returns the index of the Card with name n, or -1 if it doesn't exist
   321  func (hdr *Header) Index(n string) int {
   322  	idx, _ := hdr.get(n)
   323  	return idx
   324  }
   325  
   326  // Keys returns the name of all the Cards of this Header.
   327  func (hdr *Header) Keys() []string {
   328  	keys := make([]string, 0, len(hdr.cards))
   329  	for i := range hdr.cards {
   330  		key := hdr.cards[i].Name
   331  		switch key {
   332  		case "END", "COMMENT", "HISTORY", "":
   333  			continue
   334  		default:
   335  			keys = append(keys, key)
   336  		}
   337  	}
   338  	return keys
   339  }
   340  
   341  // Set modifies the value and comment of a Card with name n.
   342  func (hdr *Header) Set(n string, v interface{}, comment string) {
   343  	card := hdr.Get(n)
   344  	if card == nil {
   345  		hdr.Append(Card{
   346  			Name:    n,
   347  			Value:   v,
   348  			Comment: comment,
   349  		})
   350  	} else {
   351  		card.Value = v
   352  		card.Comment = comment
   353  	}
   354  }