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