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 }