github.com/sbinet/go-cfitsio@v0.0.0-20140625105338-0307f985659e/table.go (about) 1 package cfitsio 2 3 // #include "go-cfitsio.h" 4 // #include "go-cfitsio-utils.h" 5 import "C" 6 import ( 7 "fmt" 8 "reflect" 9 "strconv" 10 "strings" 11 "unsafe" 12 ) 13 14 type TypeCode int 15 16 const ( 17 TBIT TypeCode = C.TBIT /* codes for FITS table data types */ 18 TBYTE TypeCode = C.TBYTE 19 TSBYTE TypeCode = C.TSBYTE 20 TLOGICAL TypeCode = C.TLOGICAL 21 TSTRING TypeCode = C.TSTRING 22 TUSHORT TypeCode = C.TUSHORT 23 TSHORT TypeCode = C.TSHORT 24 TUINT TypeCode = C.TUINT 25 TINT TypeCode = C.TINT 26 TULONG TypeCode = C.TULONG 27 TLONG TypeCode = C.TLONG 28 TINT32BIT TypeCode = C.TINT32BIT /* used when returning datatype of a column */ 29 TFLOAT TypeCode = C.TFLOAT 30 TLONGLONG TypeCode = C.TLONGLONG 31 TDOUBLE TypeCode = C.TDOUBLE 32 TCOMPLEX TypeCode = C.TCOMPLEX 33 TDBLCOMPLEX TypeCode = C.TDBLCOMPLEX 34 35 // variable length arrays 36 TVLABIT TypeCode = -C.TBIT /* codes for FITS table data types */ 37 TVLABYTE TypeCode = -C.TBYTE 38 TVLASBYTE TypeCode = -C.TSBYTE 39 TVLALOGICAL TypeCode = -C.TLOGICAL 40 TVLASTRING TypeCode = -C.TSTRING 41 TVLAUSHORT TypeCode = -C.TUSHORT 42 TVLASHORT TypeCode = -C.TSHORT 43 TVLAUINT TypeCode = -C.TUINT 44 TVLAINT TypeCode = -C.TINT 45 TVLAULONG TypeCode = -C.TULONG 46 TVLALONG TypeCode = -C.TLONG 47 TVLAINT32BIT TypeCode = -C.TINT32BIT /* used when returning datatype of a column */ 48 TVLAFLOAT TypeCode = -C.TFLOAT 49 TVLALONGLONG TypeCode = -C.TLONGLONG 50 TVLADOUBLE TypeCode = -C.TDOUBLE 51 TVLACOMPLEX TypeCode = -C.TCOMPLEX 52 TVLADBLCOMPLEX TypeCode = -C.TDBLCOMPLEX 53 ) 54 55 func govalue_from_typecode(t TypeCode) Value { 56 var v Value 57 switch t { 58 case TBIT, TBYTE: 59 var vv byte 60 v = vv 61 62 case TSBYTE: 63 var vv int8 64 v = vv 65 66 case TLOGICAL: 67 var vv bool 68 v = vv 69 70 case TSTRING: 71 var vv string 72 v = vv 73 74 case TUSHORT: 75 var vv uint16 76 v = vv 77 78 case TSHORT: 79 var vv int16 80 v = vv 81 82 case TUINT: 83 var vv uint32 84 v = vv 85 86 case TINT: 87 var vv int32 88 v = vv 89 90 case TULONG: 91 var vv uint64 92 v = vv 93 94 case TLONG: 95 var vv int64 96 v = vv 97 98 case TFLOAT: 99 var vv float32 100 v = vv 101 102 case TLONGLONG: 103 var vv int64 104 v = vv 105 106 case TDOUBLE: 107 var vv float64 108 v = vv 109 110 case TCOMPLEX: 111 var vv complex64 112 v = vv 113 114 case TDBLCOMPLEX: 115 var vv complex128 116 v = vv 117 118 case TVLABIT, TVLABYTE: 119 var vv = make([]byte, 0) 120 v = vv 121 122 case TVLASBYTE: 123 var vv = make([]int8, 0) 124 v = vv 125 126 case TVLALOGICAL: 127 var vv = make([]bool, 0) 128 v = vv 129 130 case TVLASTRING: 131 var vv = make([]string, 0) 132 v = vv 133 134 case TVLAUSHORT: 135 var vv = make([]uint16, 0) 136 v = vv 137 138 case TVLASHORT: 139 var vv = make([]int16, 0) 140 v = vv 141 142 case TVLAUINT: 143 var vv = make([]uint32, 0) 144 v = vv 145 146 case TVLAINT: 147 var vv = make([]int32, 0) 148 v = vv 149 150 case TVLAULONG: 151 var vv = make([]uint64, 0) 152 v = vv 153 154 case TVLALONG: 155 var vv = make([]int64, 0) 156 v = vv 157 158 case TVLAFLOAT: 159 var vv = make([]float32, 0) 160 v = vv 161 162 case TVLALONGLONG: 163 var vv = make([]int64, 0) 164 v = vv 165 166 case TVLADOUBLE: 167 var vv = make([]float64, 0) 168 v = vv 169 170 case TVLACOMPLEX: 171 var vv = make([]complex64, 0) 172 v = vv 173 174 case TVLADBLCOMPLEX: 175 var vv = make([]complex128, 0) 176 v = vv 177 178 default: 179 panic(fmt.Errorf("cfitsio: invalid TypeCode value [%v]", int(t))) 180 } 181 return v 182 } 183 184 type Table struct { 185 f *File 186 id C.int 187 header Header 188 nrows int64 189 cols []Column 190 data interface{} 191 } 192 193 func (hdu *Table) Close() error { 194 hdu.f = nil 195 return nil 196 } 197 198 func (hdu *Table) Header() Header { 199 return hdu.header 200 } 201 202 func (hdu *Table) Type() HDUType { 203 return hdu.header.htype 204 } 205 206 func (hdu *Table) Name() string { 207 card := hdu.header.Get("EXTNAME") 208 if card == nil { 209 return "" 210 } 211 return card.Value.(string) 212 } 213 214 func (hdu *Table) Version() int { 215 card := hdu.header.Get("EXTVER") 216 if card == nil { 217 return 1 218 } 219 return card.Value.(int) 220 } 221 222 func (hdu *Table) Data(interface{}) error { 223 var err error 224 if hdu.data == nil { 225 err = hdu.load() 226 } 227 return err 228 } 229 230 func (hdu *Table) load() error { 231 return nil 232 } 233 234 func (hdu *Table) NumRows() int64 { 235 return hdu.nrows 236 } 237 238 func (hdu *Table) NumCols() int { 239 return len(hdu.cols) 240 } 241 242 func (hdu *Table) Cols() []Column { 243 return hdu.cols 244 } 245 246 func (hdu *Table) Col(i int) *Column { 247 return &hdu.cols[i] 248 } 249 250 // Index returns the index of the first column with name `n` or -1 251 func (hdu *Table) Index(n string) int { 252 for i := range hdu.cols { 253 col := &hdu.cols[i] 254 if col.Name == n { 255 return i 256 } 257 } 258 return -1 259 } 260 261 func (hdu *Table) readRow(irow int64) error { 262 err := hdu.seekHDU() 263 if err != nil { 264 return err 265 } 266 for icol := range hdu.cols { 267 col := &hdu.cols[icol] 268 err = col.read(hdu.f, icol, irow, &col.Value) 269 if err != nil { 270 return err 271 } 272 } 273 return err 274 } 275 276 // ReadRange reads rows over the range [beg, end) and returns the corresponding iterator. 277 // if end > maxrows, the iteration will stop at maxrows 278 // ReadRange has the same semantics than a `for i=0; i < max; i+=inc {...}` loop 279 func (hdu *Table) ReadRange(beg, end, inc int64) (*Rows, error) { 280 var rows *Rows 281 err := hdu.seekHDU() 282 if err != nil { 283 return rows, err 284 } 285 286 maxrows := hdu.NumRows() 287 if end > maxrows { 288 end = maxrows 289 } 290 291 if beg < 0 { 292 beg = 0 293 } 294 295 cols := make([]int, len(hdu.cols)) 296 for i := range hdu.cols { 297 cols[i] = i 298 } 299 300 rows = &Rows{ 301 table: hdu, 302 cols: cols, 303 i: beg, 304 n: end, 305 inc: inc, 306 cur: beg - inc, 307 err: nil, 308 } 309 return rows, err 310 } 311 312 // Read reads rows over the range [beg, end) and returns the corresponding iterator. 313 // if end > maxrows, the iteration will stop at maxrows 314 // ReadRange has the same semantics than a `for i=0; i < max; i++ {...}` loop 315 func (hdu *Table) Read(beg, end int64) (*Rows, error) { 316 return hdu.ReadRange(beg, end, 1) 317 } 318 319 func (hdu *Table) seekHDU() error { 320 c_status := C.int(0) 321 c_htype := C.int(0) 322 C.fits_movabs_hdu(hdu.f.c, hdu.id, &c_htype, &c_status) 323 if c_status > 0 { 324 return to_err(c_status) 325 } 326 return nil 327 } 328 329 func newTable(f *File, hdr Header, i int) (hdu HDU, err error) { 330 c_status := C.int(0) 331 c_id := C.int(0) 332 C.fits_get_hdu_num(f.c, &c_id) 333 if c_status > 0 { 334 return nil, to_err(c_status) 335 } 336 337 c_nrows := C.long(0) 338 C.fits_get_num_rows(f.c, &c_nrows, &c_status) 339 if c_status > 0 { 340 return nil, to_err(c_status) 341 } 342 343 c_ncols := C.int(0) 344 C.fits_get_num_cols(f.c, &c_ncols, &c_status) 345 if c_status > 0 { 346 return nil, to_err(c_status) 347 } 348 349 ncols := int(c_ncols) 350 cols := make([]Column, ncols) 351 352 get := func(str string, ii int) *Card { 353 return hdr.Get(fmt.Sprintf(str+"%d", ii+1)) 354 } 355 for ii := 0; ii < ncols; ii++ { 356 col := &cols[ii] 357 // column name 358 { 359 c_status := C.int(0) 360 c_tmpl := C.CString(fmt.Sprintf("%d", ii+1)) 361 defer C.free(unsafe.Pointer(c_tmpl)) 362 c_name := C.CStringN(C.FLEN_CARD) 363 defer C.free(unsafe.Pointer(c_name)) 364 c_colnum := C.int(0) 365 C.fits_get_colname(f.c, C.CASESEN, c_tmpl, c_name, &c_colnum, &c_status) 366 if c_status > 0 { 367 return nil, to_err(c_status) 368 } 369 col.Name = C.GoString(c_name) 370 } 371 372 card := get("TFORM", ii) 373 if card != nil { 374 col.Format = card.Value.(string) 375 } 376 377 card = get("TUNIT", ii) 378 if card != nil { 379 col.Unit = card.Value.(string) 380 } 381 382 card = get("TNULL", ii) 383 if card != nil { 384 col.Null = card.Value.(string) 385 } 386 387 card = get("TSCAL", ii) 388 if card != nil { 389 switch vv := card.Value.(type) { 390 case float64: 391 col.Bscale = vv 392 case int64: 393 col.Bscale = float64(vv) 394 default: 395 panic(fmt.Errorf("unhandled type [%T]", vv)) 396 } 397 //col.Bscale = card.Value.(float64) 398 } else { 399 col.Bscale = 1.0 400 } 401 402 card = get("TZERO", ii) 403 if card != nil { 404 switch vv := card.Value.(type) { 405 case float64: 406 col.Bzero = vv 407 case int64: 408 col.Bzero = float64(vv) 409 default: 410 panic(fmt.Errorf("unhandled type [%T]", vv)) 411 } 412 //col.Bzero = card.Value.(float64) 413 } else { 414 col.Bzero = 0.0 415 } 416 417 card = get("TDISP", ii) 418 if card != nil { 419 col.Display = card.Value.(string) 420 } 421 422 { 423 // int fits_read_tdimll / ffgtdmll 424 //(fitsfile *fptr, int colnum, int maxdim, > int *naxis, 425 //LONGLONG *naxes, int *status) 426 427 } 428 card = get("TDIM", ii) 429 if card != nil { 430 dims := card.Value.(string) 431 dims = strings.Replace(dims, "(", "", -1) 432 dims = strings.Replace(dims, ")", "", -1) 433 toks := make([]string, 0) 434 for _, tok := range strings.Split(dims, ",") { 435 tok = strings.Trim(tok, " \t\n") 436 if tok == "" { 437 continue 438 } 439 toks = append(toks, tok) 440 } 441 col.Dim = make([]int64, 0, len(toks)) 442 for _, tok := range toks { 443 dim, err := strconv.ParseInt(tok, 10, 64) 444 if err != nil { 445 return nil, err 446 } 447 col.Dim = append(col.Dim, dim) 448 } 449 } 450 451 card = get("TBCOL", ii) 452 if card != nil { 453 col.Start = card.Value.(int64) 454 } 455 456 { 457 c_type := C.int(0) 458 c_repeat := C.long(0) 459 c_width := C.long(0) 460 c_status := C.int(0) 461 c_col := C.int(ii + 1) // 1-based index 462 C.fits_get_coltype(f.c, c_col, &c_type, &c_repeat, &c_width, &c_status) 463 if c_status > 0 { 464 return nil, to_err(c_status) 465 } 466 col.Value = govalue_from_typecode(TypeCode(c_type)) 467 } 468 } 469 470 hdu = &Table{ 471 f: f, 472 id: c_id, 473 header: hdr, 474 nrows: int64(c_nrows), 475 cols: cols, 476 data: nil, 477 } 478 return hdu, err 479 } 480 481 // NewTable creates a new table in the given FITS file 482 func NewTable(f *File, name string, cols []Column, hdutype HDUType) (*Table, error) { 483 var err error 484 var table *Table 485 mode, err := f.Mode() 486 if err != nil { 487 return table, err 488 } 489 if mode == ReadOnly { 490 return table, READONLY_FILE 491 } 492 493 nhdus := len(f.hdus) 494 495 if len(cols) <= 0 { 496 return table, fmt.Errorf("cfitsio.NewTable: invalid number of columns (%v)", len(cols)) 497 } 498 499 c_status := C.int(0) 500 c_sz := C.int(len(cols)) 501 c_types := C.char_array_new(c_sz) 502 defer C.free(unsafe.Pointer(c_types)) 503 c_forms := C.char_array_new(c_sz) 504 defer C.free(unsafe.Pointer(c_forms)) 505 c_units := C.char_array_new(c_sz) 506 defer C.free(unsafe.Pointer(c_units)) 507 c_hduname := C.CString(name) 508 defer C.free(unsafe.Pointer(c_hduname)) 509 510 for i := 0; i < len(cols); i++ { 511 c_idx := C.int(i) 512 col := &cols[i] 513 c_name := C.CString(col.Name) 514 defer C.free(unsafe.Pointer(c_name)) 515 C.char_array_set(c_types, c_idx, c_name) 516 517 err = col.inferFormat(hdutype) 518 if err != nil { 519 return table, err 520 } 521 c_form := C.CString(col.Format) 522 defer C.free(unsafe.Pointer(c_form)) 523 C.char_array_set(c_forms, c_idx, c_form) 524 525 c_unit := C.CString(col.Unit) 526 defer C.free(unsafe.Pointer(c_unit)) 527 C.char_array_set(c_units, c_idx, c_unit) 528 } 529 530 C.fits_create_tbl(f.c, C.int(hdutype), 0, C.int(len(cols)), c_types, c_forms, c_units, c_hduname, &c_status) 531 if c_status > 0 { 532 return table, to_err(c_status) 533 } 534 535 hdu, err := f.readHDU(nhdus) 536 if err != nil { 537 return table, err 538 } 539 f.hdus = append(f.hdus, hdu) 540 table = hdu.(*Table) 541 542 return table, err 543 } 544 545 // Write writes a row to the table 546 func (hdu *Table) Write(args ...interface{}) error { 547 548 err := hdu.seekHDU() 549 if err != nil { 550 return err 551 } 552 553 irow := hdu.NumRows() 554 555 defer func() { 556 // update nrows 557 c_nrows := C.long(0) 558 c_status := C.int(0) 559 C.fits_get_num_rows(hdu.f.c, &c_nrows, &c_status) 560 if c_status > 0 { 561 return 562 } 563 hdu.nrows = int64(c_nrows) 564 }() 565 566 switch len(args) { 567 case 0: 568 return fmt.Errorf("cfitsio: Table.Write needs at least one argument") 569 case 1: 570 // maybe special case: map? struct? 571 rt := reflect.TypeOf(args[0]).Elem() 572 switch rt.Kind() { 573 case reflect.Map: 574 return hdu.writeMap(irow, *args[0].(*map[string]interface{})) 575 case reflect.Struct: 576 return hdu.writeStruct(irow, args[0]) 577 } 578 } 579 580 return hdu.write(irow, args...) 581 } 582 583 func (hdu *Table) writeMap(irow int64, data map[string]interface{}) error { 584 var err error 585 586 for k, v := range data { 587 icol := hdu.Index(k) 588 if icol < 0 { 589 continue 590 } 591 col := &hdu.cols[icol] 592 col.Value = v 593 err = col.write(hdu.f, icol, irow, col.Value) 594 if err != nil { 595 return err 596 } 597 } 598 599 return err 600 } 601 602 func (hdu *Table) writeStruct(irow int64, data interface{}) error { 603 var err error 604 rt := reflect.TypeOf(data).Elem() 605 rv := reflect.ValueOf(data).Elem() 606 icols := make([][2]int, 0, rt.NumField()) 607 for i := 0; i < rt.NumField(); i++ { 608 f := rt.Field(i) 609 n := f.Tag.Get("fits") 610 if n == "" { 611 n = f.Name 612 } 613 icol := hdu.Index(n) 614 if icol >= 0 { 615 icols = append(icols, [2]int{i, icol}) 616 } 617 } 618 619 for _, icol := range icols { 620 vv := rv.Field(icol[0]) 621 col := &hdu.cols[icol[1]] 622 col.Value = vv.Interface() 623 err = col.write(hdu.f, icol[1], irow, col.Value) 624 if err != nil { 625 return err 626 } 627 } 628 return err 629 } 630 631 func (hdu *Table) write(irow int64, args ...interface{}) error { 632 var err error 633 634 nargs := len(args) 635 if nargs > len(hdu.cols) { 636 nargs = len(hdu.cols) 637 } 638 639 for i := 0; i < nargs; i++ { 640 col := &hdu.cols[i] 641 rv := reflect.ValueOf(args[i]).Elem() 642 vv := reflect.ValueOf(&col.Value).Elem() 643 vv.Set(rv) 644 err = col.write(hdu.f, i, irow, col.Value) 645 if err != nil { 646 return err 647 } 648 } 649 return err 650 } 651 652 // CopyTable copies all the rows from src into dst. 653 func CopyTable(dst, src *Table) error { 654 return CopyTableRange(dst, src, 0, src.NumRows()) 655 } 656 657 // CopyTableRange copies the rows interval [beg,end) from src into dst 658 func CopyTableRange(dst, src *Table, beg, end int64) error { 659 var err error 660 if dst == nil { 661 return fmt.Errorf("cfitsio: dst pointer is nil") 662 } 663 if src == nil { 664 return fmt.Errorf("cfitsio: src pointer is nil") 665 } 666 667 defer func() { 668 // update nrows 669 c_nrows := C.long(0) 670 c_status := C.int(0) 671 C.fits_get_num_rows(dst.f.c, &c_nrows, &c_status) 672 if c_status > 0 { 673 return 674 } 675 dst.nrows = int64(c_nrows) 676 }() 677 678 err = dst.seekHDU() 679 if err != nil { 680 return err 681 } 682 err = src.seekHDU() 683 if err != nil { 684 return err 685 } 686 687 hdr := src.Header() 688 buf := make([]byte, int(hdr.Axes()[0])) 689 slice := (*reflect.SliceHeader)((unsafe.Pointer(&buf))) 690 c_ptr := (*C.uchar)(unsafe.Pointer(slice.Data)) 691 c_len := C.LONGLONG(len(buf)) 692 c_orow := C.LONGLONG(dst.nrows) 693 for irow := beg; irow < end; irow++ { 694 c_status := C.int(0) 695 c_row := C.LONGLONG(irow) + 1 // from 0-based to 1-based index 696 C.fits_read_tblbytes(src.f.c, c_row, 1, c_len, c_ptr, &c_status) 697 if c_status > 0 { 698 return to_err(c_status) 699 } 700 701 fmt.Printf(">>> [%d] %v\n", irow, buf) 702 C.fits_write_tblbytes(dst.f.c, c_orow+c_row, 1, c_len, c_ptr, &c_status) 703 if c_status > 0 { 704 return to_err(c_status) 705 } 706 } 707 708 return err 709 } 710 711 func init() { 712 g_hdus[ASCII_TBL] = newTable 713 g_hdus[BINARY_TBL] = newTable 714 } 715 716 // EOF