GammaLib  1.7.0.dev
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
GFitsTable.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  * GFitsTable.cpp - FITS table base class *
3  * ----------------------------------------------------------------------- *
4  * copyright (C) 2008-2018 by Juergen Knoedlseder *
5  * ----------------------------------------------------------------------- *
6  * *
7  * This program is free software: you can redistribute it and/or modify *
8  * it under the terms of the GNU General Public License as published by *
9  * the Free Software Foundation, either version 3 of the License, or *
10  * (at your option) any later version. *
11  * *
12  * This program is distributed in the hope that it will be useful, *
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of *
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
15  * GNU General Public License for more details. *
16  * *
17  * You should have received a copy of the GNU General Public License *
18  * along with this program. If not, see <http://www.gnu.org/licenses/>. *
19  * *
20  ***************************************************************************/
21 /**
22  * @file GFitsTable.cpp
23  * @brief FITS table abstract base class implementation
24  * @author Juergen Knoedlseder
25  */
26 
27 /* __ Includes ___________________________________________________________ */
28 #ifdef HAVE_CONFIG_H
29 #include <config.h>
30 #endif
31 #include <cstring>
32 #include <iostream>
33 #include <vector>
34 #include <cstdio>
35 #include <cstdlib>
36 #include "GException.hpp"
37 #include "GTools.hpp"
38 #include "GFitsCfitsio.hpp"
39 #include "GFits.hpp"
40 #include "GFitsTable.hpp"
41 #include "GFitsTableBitCol.hpp"
42 #include "GFitsTableByteCol.hpp"
43 #include "GFitsTableBoolCol.hpp"
44 #include "GFitsTableStringCol.hpp"
45 #include "GFitsTableUShortCol.hpp"
46 #include "GFitsTableShortCol.hpp"
47 #include "GFitsTableULongCol.hpp"
48 #include "GFitsTableLongCol.hpp"
50 #include "GFitsTableFloatCol.hpp"
51 #include "GFitsTableDoubleCol.hpp"
52 #include "GFitsTableCFloatCol.hpp"
53 #include "GFitsTableCDoubleCol.hpp"
54 
55 /* __ Method name definitions ____________________________________________ */
56 #define G_ACCESS1 "GFitsTable::operator[](int&)"
57 #define G_ACCESS2 "GFitsTable::operator[](std::string&)"
58 #define G_SET1 "GFitsTable::set(int&, GFitsTableCol&)"
59 #define G_SET2 "GFitsTable::set(std::string&, GFitsTableCol&)"
60 #define G_INSERT1 "GFitsTable::insert(int, GFitsTableCol&)"
61 #define G_INSERT2 "GFitsTable::insert(std::string&, GFitsTableCol&)"
62 #define G_REMOVE1 "GFitsTable::remove(int&)"
63 #define G_REMOVE2 "GFitsTable::remove(std::string&)"
64 #define G_INSERT_ROWS "GFitsTable::insert_rows(int&, int&)"
65 #define G_REMOVE_ROWS "GFitsTable::remove_rows(int&, int&)"
66 #define G_DATA_OPEN "GFitsTable::data_open(void*)"
67 #define G_DATA_SAVE "GFitsTable::data_save()"
68 #define G_GET_TFORM "GFitsTable::get_tform(int&)"
69 
70 /* __ Macros _____________________________________________________________ */
71 
72 /* __ Coding definitions _________________________________________________ */
73 
74 /* __ Debug definitions __________________________________________________ */
75 //#define G_DEBUG_SAVE //!< Debug data_save() method
76 
77 
78 /*==========================================================================
79  = =
80  = Constructors/destructors =
81  = =
82  ==========================================================================*/
83 
84 /***********************************************************************//**
85  * @brief Void constructor
86  *
87  * Construct empty FITS table.
88  ***************************************************************************/
90 {
91  // Initialise class members for clean destruction
92  init_members();
93 
94  // Return
95  return;
96 }
97 
98 
99 /***********************************************************************//**
100  * @brief Table constructor
101  *
102  * @param[in] nrows Number of rows in table.
103  *
104  * Construct FITS table with @p nrows table rows.
105  ***************************************************************************/
106 GFitsTable::GFitsTable(const int& nrows) : GFitsHDU()
107 {
108  // Initialise class members for clean destruction
109  init_members();
110 
111  // Store numnber of rows
112  m_rows = nrows;
113 
114  // Return
115  return;
116 }
117 
118 
119 /***********************************************************************//**
120  * @brief Copy constructor
121  *
122  * @param[in] table FITS table.
123  ***************************************************************************/
125 {
126  // Initialise class members for clean destruction
127  init_members();
128 
129  // Copy members
130  copy_members(table);
131 
132  // Return
133  return;
134 }
135 
136 
137 /***********************************************************************//**
138  * @brief Destructor
139  ***************************************************************************/
141 {
142  // Free members
143  free_members();
144 
145  // Return
146  return;
147 }
148 
149 
150 /*==========================================================================
151  = =
152  = Operators =
153  = =
154  ==========================================================================*/
155 
156 /***********************************************************************//**
157  * @brief Assignment operator
158  *
159  * @param[in] table FITS table.
160  * @return FITS table.
161  ***************************************************************************/
163 {
164  // Execute only if object is not identical
165  if (this != &table) {
166 
167  // Copy base class members
168  this->GFitsHDU::operator=(table);
169 
170  // Free members
171  free_members();
172 
173  // Initialise private members for clean destruction
174  init_members();
175 
176  // Copy members
177  copy_members(table);
178 
179  } // endif: object was not identical
180 
181  // Return this object
182  return *this;
183 }
184 
185 
186 /***********************************************************************//**
187  * @brief Returns pointer to table column
188  *
189  * @param[in] colnum Column number [0,...,ncols()-1].
190  * @return Table column pointer.
191  *
192  * @exception GException::fits_no_data
193  * No data found in table.
194  * @exception GException::out_of_range
195  * Column number is out of range.
196  ***************************************************************************/
198 {
199  // If there is no data then throw an exception
200  if (m_columns == NULL) {
201  throw GException::fits_no_data(G_ACCESS1, "No columns in table.");
202  }
203 
204  // Compile option: raise exception if column number is out of range
205  #if defined(G_RANGE_CHECK)
206  if (colnum < 0 || colnum >= m_cols) {
207  throw GException::out_of_range(G_ACCESS1, colnum, 0, m_cols-1);
208  }
209  #endif
210 
211  // Get column pointer
213  if (ptr == NULL) {
214  throw GException::fits_no_data(G_ACCESS1, "No data for this column.");
215  }
216 
217  // Return pointer
218  return ptr;
219 }
220 
221 
222 /***********************************************************************//**
223  * @brief Returns pointer to table column (const version)
224  *
225  * @param[in] colnum Column number [0,...,ncols()-1].
226  * @return Table column pointer.
227  *
228  * @exception GException::fits_no_data
229  * No data found in table.
230  * @exception GException::out_of_range
231  * Column number is out of range.
232  ***************************************************************************/
233 const GFitsTableCol* GFitsTable::operator[](const int& colnum) const
234 {
235  // If there is no data then throw an exception
236  if (m_columns == NULL) {
237  throw GException::fits_no_data(G_ACCESS1, "No columns in table.");
238  }
239 
240  // Compile option: raise exception if column number is out of range
241  #if defined(G_RANGE_CHECK)
242  if (colnum < 0 || colnum >= m_cols) {
243  throw GException::out_of_range(G_ACCESS1, colnum, 0, m_cols-1);
244  }
245  #endif
246 
247  // Get column pointer
248  const GFitsTableCol* ptr = m_columns[colnum];
249  if (ptr == NULL) {
250  throw GException::fits_no_data(G_ACCESS1, "No data for this column.");
251  }
252 
253  // Return pointer
254  return ptr;
255 }
256 
257 
258 /***********************************************************************//**
259  * @brief Returns pointer to table column
260  *
261  * @param[in] colname Column name.
262  * @return Table column pointer.
263  *
264  * @exception GException::fits_no_data
265  * No data found in table.
266  * @exception GException::fits_column_not_found
267  * Column name not found.
268  ***************************************************************************/
269 GFitsTableCol* GFitsTable::operator[](const std::string& colname)
270 {
271  // If there is no data then throw an exception
272  if (m_columns == NULL) {
273  throw GException::fits_no_data(G_ACCESS2, "No columns in table.");
274  }
275 
276  // Get column pointer
277  GFitsTableCol* ptr = ptr_column(colname);
278 
279  // If column has not been found throw an exception
280  if (ptr == NULL) {
282  }
283 
284  // Return pointer
285  return ptr;
286 }
287 
288 
289 /***********************************************************************//**
290  * @brief Returns pointer to table column (const version)
291  *
292  * @param[in] colname Column name.
293  * @return Table column pointer.
294  *
295  * @exception GException::fits_no_data
296  * No data found in table.
297  * @exception GException::fits_column_not_found
298  * Column name not found.
299  ***************************************************************************/
300 const GFitsTableCol* GFitsTable::operator[](const std::string& colname) const
301 {
302  // If there is no data then throw an exception
303  if (m_columns == NULL) {
304  throw GException::fits_no_data(G_ACCESS2, "No columns in table.");
305  }
306 
307  // Get column pointer
308  GFitsTableCol* ptr = ptr_column(colname);
309 
310  // If column has not been found throw an exception
311  if (ptr == NULL) {
313  }
314 
315  // Return pointer
316  return ptr;
317 }
318 
319 
320 /*==========================================================================
321  = =
322  = Public methods =
323  = =
324  ==========================================================================*/
325 
326 /***********************************************************************//**
327  * @brief Set column
328  *
329  * @param[in] colnum Column number [0,...,ncols()-1].
330  * @param[in] column Table column.
331  * @return Pointer to table column that has been set.
332  *
333  * Sets the column of a table by making a deep copy of the @p column
334  * provided.
335  ***************************************************************************/
336 GFitsTableCol* GFitsTable::set(const int& colnum, const GFitsTableCol& column)
337 {
338  // Check if column number is valid
339  if (colnum < 0 || colnum >= ncols()) {
340  throw GException::out_of_range(G_SET1, "Column number", colnum, ncols());
341  }
342 
343  // Free existing column only if it differs from current column. This
344  // prevents unintential deallocation of the argument
345  if ((m_columns[colnum] != NULL) && (m_columns[colnum] != &column)) {
346  delete m_columns[colnum];
347  }
348 
349  // Clone column
350  m_columns[colnum] = column.clone();
351 
352  // Update header
353  update_header();
354 
355  // Return pointer to column
356  return m_columns[colnum];
357 }
358 
359 
360 /***********************************************************************//**
361  * @brief Set column
362  *
363  * @param[in] colname Column name.
364  * @param[in] column Table column.
365  * @return Pointer to table column that has been set.
366  *
367  * Sets the column of a table by making a deep copy of the @p column
368  * provided.
369  ***************************************************************************/
370 GFitsTableCol* GFitsTable::set(const std::string& colname,
371  const GFitsTableCol& column)
372 {
373  // Get column number
374  int colnum = this->colnum(colname);
375 
376  // If column has not been found throw an exception
377  if (colnum < 0) {
379  }
380 
381  // Set column and return pointer to column
382  return (set(colnum, column));
383 }
384 
385 
386 
387 /***********************************************************************//**
388  * @brief Insert column into the table
389  *
390  * @param[in] colnum Column number [0,...,ncols()].
391  * @param[in] column Table column.
392  * @return Pointer to table column that has been appended.
393  *
394  * @exception GException::fits_bad_col_length
395  * The length of the column is incompatible with the number of
396  * rows in the table.
397  *
398  * A column will be inserted at position 'colnum' of the table. If the
399  * position is beyond the end of the table the column will be appended.
400  *
401  * If the table is empty and has 0 rows, the number of rows will be set to
402  * the length of the column.
403  *
404  * The length of the column to be inserted has to be identical to the number
405  * of rows in the table.
406  ***************************************************************************/
407 GFitsTableCol* GFitsTable::insert(int colnum, const GFitsTableCol& column)
408 {
409  // Check if column number is valid
410  if (colnum < 0 || colnum > ncols()) {
411  throw GException::out_of_range(G_INSERT1, "Column number", colnum, ncols()+1);
412  }
413 
414  // If the table is empty and has 0 rows then set the number of rows in
415  // the table to the length of the column
416  if (m_columns == NULL && m_rows == 0) {
417  m_rows = column.nrows();
418  }
419 
420  // Throw exception if the column length is incompatible with number of
421  // rows in the table
422  if (m_rows != column.nrows()) {
424  column.nrows(), m_rows);
425  }
426 
427  // If no column data exist then allocate them now
428  if (m_columns == NULL) {
429  m_columns = new GFitsTableCol*[1];
430  m_cols = 1;
431  m_columns[0] = NULL;
432  colnum = 0; // We insert at the first place
433  }
434 
435  // ... otherwise make space to insert column
436  else {
437 
438  // Allocate fresh memory
439  GFitsTableCol** tmp = new GFitsTableCol*[m_cols+1];
440 
441  // Copy over old column pointers. Leave some space at the position
442  // where we want to insert the column
443  for (int src = 0, dst = 0; dst < m_cols+1; ++dst) {
444  if (dst == colnum) {
445  tmp[dst] = NULL;
446  }
447  else {
448  tmp[dst] = m_columns[src];
449  src++;
450  }
451  }
452 
453  // Free old column pointer array. Do not free the columns since they
454  // are still alive in the new column pointer array.
455  delete [] m_columns;
456 
457  // Connect new memory
458  m_columns = tmp;
459 
460  // Increment column counter
461  m_cols++;
462 
463  } // endelse: we made space to insert a column
464 
465  // Copy column by cloning it
466  m_columns[colnum] = column.clone();
467 
468  // Reset column number since column does not already exist in FITS
469  // file
470  m_columns[colnum]->colnum(0);
471 
472  // Update header
473  update_header();
474 
475  // Return column pointer
476  return (m_columns[colnum]);
477 }
478 
479 
480 /***********************************************************************//**
481  * @brief Insert column into the table
482  *
483  * @param[in] colname Column name.
484  * @param[in] column Table column.
485  * @return Pointer to table column that has been appended
486  *
487  * Inserts the column at the position given by the specified column name.
488  ***************************************************************************/
489 GFitsTableCol* GFitsTable::insert(const std::string& colname,
490  const GFitsTableCol& column)
491 {
492  // Get column number
493  int colnum = this->colnum(colname);
494 
495  // If column has not been found throw an exception
496  if (colnum < 0) {
498  }
499 
500  // Insert column and return pointer to column
501  return (insert(colnum, column));
502 }
503 
504 
505 /***********************************************************************//**
506  * @brief Remove column from the table
507  *
508  * @param[in] colnum Column number [0,...,ncols()-1].
509  *
510  * @exception GException::fits_bad_col_length
511  * The length of the column is incompatible with the number of
512  * rows in the table.
513  *
514  * Remove the column at position @p colnum from the table.
515  ***************************************************************************/
516 void GFitsTable::remove(const int& colnum)
517 {
518  // Check if column number is valid
519  if (colnum < 0 || colnum >= ncols()) {
520  throw GException::out_of_range(G_REMOVE1, "Column number", colnum, ncols());
521  }
522 
523  // At this point we should have at least one column in the table and we
524  // can now reduce the table by removing the column. If there is just a
525  // single column in the table then delete all columns and set the number
526  // of columns to zero ...
527  if (m_cols == 1) {
528  free_columns();
529  m_cols = 0;
530  }
531 
532  // ... otherwise remove the specified column
533  else {
534 
535  // Allocate fresh memory
536  GFitsTableCol** tmp = new GFitsTableCol*[m_cols-1];
537 
538  // Copy over old column pointers, skipping the column that should be
539  // removed
540  for (int src = 0, dst = 0; src < m_cols; ++src) {
541  if (src != colnum) {
542  tmp[dst] = m_columns[src];
543  tmp[dst]->colnum(dst);
544  dst++;
545  }
546  }
547 
548  // Free deleted column and old column pointer array. Do not free the
549  // other columns since they are still alive in the new column pointer
550  // array.
551  if (m_columns[colnum] != NULL) {
552  delete m_columns[colnum];
553  }
554  delete [] m_columns;
555 
556  // Connect new memory
557  m_columns = tmp;
558 
559  // Decerement column counter
560  m_cols--;
561 
562  } // endelse: we made space to insert a column
563 
564  // Update header
565  update_header();
566 
567  // Return
568  return;
569 }
570 
571 
572 /***********************************************************************//**
573  * @brief Remove column from the table
574  *
575  * @param[in] colname Column name.
576  *
577  * Remove the column with name @p colname from the table.
578  ***************************************************************************/
579 void GFitsTable::remove(const std::string& colname)
580 {
581  // Get column number
582  int colnum = this->colnum(colname);
583 
584  // If column has not been found throw an exception
585  if (colnum < 0) {
587  }
588 
589  // Remove column
590  remove(colnum);
591 
592  // Return
593  return;
594 }
595 
596 
597 /***********************************************************************//**
598  * @brief Append rows to the table
599  *
600  * @param[in] nrows Number of rows to be appended.
601  *
602  * Appends @p nrows rows at the end of the FITS table. The method calls the
603  * insert() method to do the job. Note that a call of this method will lead
604  * to loading all table data into memory.
605  ***************************************************************************/
606 void GFitsTable::append_rows(const int& nrows)
607 {
608  // Set row number for insertion to end of the file
609  int rownum = this->nrows();
610 
611  // Insert rows
612  insert_rows(rownum, nrows);
613 
614  // Return
615  return;
616 }
617 
618 
619 /***********************************************************************//**
620  * @brief Insert rows into the table
621  *
622  * @param[in] row Row at which rows are inserted [0,...,nrows()].
623  * @param[in] nrows Number of rows to be inserted.
624  *
625  * @exception GException::fits_invalid_row
626  * Specified rownum is invalid.
627  *
628  * Inserts @p nrows table rows at the specified @p row into the table. If the
629  * @p row index is set to the number of existing rows, nrows(), @p nrows
630  * will be appened at the end of the table.
631  *
632  * Note that this method will load all table data into memory.
633  ***************************************************************************/
634 void GFitsTable::insert_rows(const int& row, const int& nrows)
635 {
636  // Make sure that row is valid
637  if (row < 0 || row > m_rows) {
638  throw GException::fits_invalid_row(G_INSERT_ROWS, row, m_rows);
639  }
640 
641  // Continue only if there are rows to be inserted
642  if (nrows > 0) {
643 
644  // Insert rows for all columns
645  for (int icol = 0; icol < m_cols; ++icol) {
646  m_columns[icol]->insert(row, nrows);
647  }
648 
649  // Increment number of rows in table
650  m_rows += nrows;
651 
652  } // endfor: there were rows to be inserted
653 
654  // Update header
655  update_header();
656 
657  // Return
658  return;
659 }
660 
661 
662 /***********************************************************************//**
663  * @brief Remove rows from the table
664  *
665  * @param[in] row Row from which on rows are removed [0,...,nrows()-1].
666  * @param[in] nrows Number of rows to be removed.
667  *
668  * @exception GException::fits_invalid_row
669  * Specified rownum is invalid.
670  * @exception GException::fits_invalid_nrows
671  * Invalid number of rows specified.
672  *
673  * Removes @p nrows table rows from the specified @p row on from the table.
674  *
675  * Note that this method will load all column data into memory.
676  ***************************************************************************/
677 void GFitsTable::remove_rows(const int& row, const int& nrows)
678 {
679  // Make sure that row is valid
680  if (row < 0 || row >= m_rows) {
682  }
683 
684  // Make sure that we don't remove beyond the limit
685  if (nrows < 0 || nrows > m_rows-row) {
687  }
688 
689  // Continue only if there are rows to be removed
690  if (nrows > 0) {
691 
692  // Remove rows for all columns
693  for (int icol = 0; icol < m_cols; ++icol) {
694  m_columns[icol]->remove(row, nrows);
695  }
696 
697  // Decrement number of rows in table
698  m_rows -= nrows;
699 
700  } // endfor: there were rows to be removed
701 
702  // Update header
703  update_header();
704 
705  // Return
706  return;
707 }
708 
709 
710 /***********************************************************************//**
711  * @brief Checks the presence of a column in table
712  *
713  * @param[in] colname Column name.
714  * @return True if the column exists, false otherwise.
715  ***************************************************************************/
716 bool GFitsTable::contains(const std::string& colname) const
717 {
718  // Get pointer in column
719  GFitsTableCol* ptr = ptr_column(colname);
720 
721  // Return state
722  return (ptr != NULL);
723 }
724 
725 
726 /***********************************************************************//**
727  * @brief Print table information
728  *
729  * @param[in] chatter Chattiness.
730  * @return String containing table information.
731  ***************************************************************************/
732 std::string GFitsTable::print(const GChatter& chatter) const
733 {
734  // Initialise result string
735  std::string result;
736 
737  // Continue only if chatter is not silent
738  if (chatter != SILENT) {
739 
740  // Append header
741  result.append("=== GFitsTable ===");
742 
743  // Append HDU information
744  result.append("\n"+print_hdu(chatter));
745 
746  // Append table type
747  result.append("\n"+gammalib::parformat("Table type"));
748  switch (m_type) {
750  result.append("ASCII table");
751  break;
753  result.append("Binary table");
754  break;
755  default:
756  result.append("Unknown");
757  break;
758  }
759 
760  // Append table dimensions
761  result.append("\n"+gammalib::parformat("Number of rows"));
762  result.append(gammalib::str(m_rows));
763  result.append("\n"+gammalib::parformat("Number of columns"));
764  result.append(gammalib::str(m_cols));
765 
766  // NORMAL: Append header information
767  if (chatter >= NORMAL) {
768  result.append("\n"+m_header.print(gammalib::reduce(chatter)));
769  }
770 
771  // NORMAL: Append table columns
772  if (chatter >= VERBOSE) {
773  if (m_columns != NULL) {
774  for (int i = 0; i < m_cols; ++i) {
775  result.append("\n");
776  if (m_columns[i] != NULL) {
777  result.append(m_columns[i]->print(gammalib::reduce(chatter)));
778  }
779  else {
780  result.append(" Column "+gammalib::str(i)+" undefined");
781  }
782  }
783  }
784  else {
785  result.append("\n Table columns undefined");
786  }
787  }
788 
789  } // endif: chatter was not silent
790 
791  // Return result
792  return result;
793 }
794 
795 
796 /*==========================================================================
797  = =
798  = Protected methods =
799  = =
800  ==========================================================================*/
801 
802 /***********************************************************************//**
803  * @brief Initialise class members
804  ***************************************************************************/
806 {
807  // Initialise members
808  m_type = -1;
809  m_rows = 0;
810  m_cols = 0;
811  m_columns = NULL;
812 
813  // Return
814  return;
815 }
816 
817 
818 /***********************************************************************//**
819  * @brief Copy class members
820  *
821  * @param[in] table FITS Table.
822  *
823  * Copies FITS table members from @p table.
824  ***************************************************************************/
826 {
827  // Copy attributes
828  m_type = table.m_type;
829  m_rows = table.m_rows;
830  m_cols = table.m_cols;
831 
832  // Copy column definition
833  if (table.m_columns != NULL && m_cols > 0) {
835  for (int i = 0; i < m_cols; ++i) {
836  if (table.m_columns[i] != NULL) {
837  m_columns[i] = table.m_columns[i]->clone();
838  }
839  else {
840  m_columns[i] = NULL;
841  }
842  }
843  }
844 
845  // Return
846  return;
847 }
848 
849 
850 /***********************************************************************//**
851  * @brief Free class members
852  *
853  * Free all class members.
854  ***************************************************************************/
856 {
857  // Free columns
858  free_columns();
859 
860  // Return
861  return;
862 }
863 
864 
865 /***********************************************************************//**
866  * @brief Free column pointers
867  *
868  * De-allocates all column pointers.
869  ***************************************************************************/
871 {
872  // Free memory
873  if (m_columns != NULL) {
874  for (int i = 0; i < m_cols; ++i) {
875  if (m_columns[i] != NULL) delete m_columns[i];
876  }
877  delete [] m_columns;
878  }
879 
880  // Mark memory as freed
881  m_columns = NULL;
882 
883  // Return
884  return;
885 }
886 
887 
888 /***********************************************************************//**
889  * @brief Update header after row or column manipulations
890  *
891  * Updates the header after row or column manipulations so that the header
892  * is kept up-to-date with the current table structure.
893  *
894  * The following keywords are updated:
895  * - TFIELDS
896  * - NAXIS1
897  * - NAXIS2
898  * - TTYPEi
899  * - TFORMi
900  * - TUNITi
901  * - TZEROi (only for unsigned integer columns)
902  * - TSCALi (only for unsigned integer columns)
903  * - TDIMi (only for columns with dimensions)
904  *
905  * This method should be called after adding or removing table rows or
906  * columns.
907  ***************************************************************************/
909 {
910  // Get non-const reference to header for manipulations
911  GFitsHeader& hdr = const_cast<GFitsHeader&>(header());
912 
913  // Initialise TFIELDS, NAXIS1 and NAXIS2 keywords
914  int tfields = 0;
915  int naxis1 = 0;
916  int naxis2 = 0;
917 
918  // Compute NAXIS1 keyword value
919  for (int i = 0; i < m_cols; ++i) {
920  if (m_columns[i] != NULL) {
921  naxis1 += m_columns[i]->number() * m_columns[i]->width();
922  }
923  }
924 
925  // Set TFIELDS, NAXIS1 and NAXIS2 keywords. In case that there are no
926  // rows, columns or the table has a zero width, all keywords will be
927  // set to zero for compliance with cfitsio.
928  if ((m_cols > 0) && (m_rows > 0) && (naxis1 > 0)) {
929  tfields = m_cols;
930  naxis2 = m_rows;
931  }
932  else {
933  tfields = 0;
934  naxis1 = 0;
935  naxis2 = 0;
936  }
937 
938  // Update TFIELDS, NAXIS1 and NAXIS2 keywords
939  card("TFIELDS", tfields, "number of table fields");
940  card("NAXIS1", naxis1, "width of table in bytes");
941  card("NAXIS2", naxis2, "number of rows in table");
942 
943  // Loop over all columns
944  for (int i = 0; i < m_cols; ++i) {
945 
946  // Setup keywords
947  std::string key_ttype = "TTYPE" + gammalib::str(i+1);
948  std::string key_tform = "TFORM" + gammalib::str(i+1);
949  std::string key_tunit = "TUNIT" + gammalib::str(i+1);
950  std::string key_tzero = "TZERO" + gammalib::str(i+1);
951  std::string key_tscal = "TSCAL" + gammalib::str(i+1);
952  std::string key_tdim = "TDIM" + gammalib::str(i+1);
953 
954  // Continue only if the column is valid
955  if (m_columns[i] != NULL) {
956 
957  // Get values
958  char* ttype = get_ttype(i);
959  char* tform = get_tform(i);
960  char* tunit = get_tunit(i);
961 
962  // Convert into strings
963  std::string val_ttype(ttype);
964  std::string val_tform(tform);
965  std::string val_tunit(tunit);
966 
967  // Replace "U" by "I" and "V" by "J" in TFORM keyword. This is
968  // needed due to a cfitsio internal translation of these
969  // characters.
970  val_tform = gammalib::replace_segment(val_tform, "U", "I");
971  val_tform = gammalib::replace_segment(val_tform, "V", "J");
972 
973  // Delete values
974  if (ttype != NULL) delete [] ttype;
975  if (tform != NULL) delete [] tform;
976  if (tunit != NULL) delete [] tunit;
977 
978  // Build TDIM value
979  std::vector<int> tdim = m_columns[i]->dim();
980  std::string val_tdim = "";
981  if (tdim.size() > 0) {
982  val_tdim.append("("+gammalib::str(tdim[0]));
983  for (int k = 1; k < tdim.size(); ++k) {
984  val_tdim.append(","+gammalib::str(tdim[k]));
985  }
986  val_tdim.append(")");
987  }
988 
989  // Setup comments
990  std::string com_ttype = "label for field " + gammalib::str(i+1);
991  std::string com_tform = "data format of field " + gammalib::str(i+1);
992  std::string com_tunit = "physical unit of field" + gammalib::str(i+1);
993  std::string com_tdim = "dimensions of field " + gammalib::str(i+1);
994 
995  // Set keywords
996  card(key_ttype, val_ttype, com_ttype);
997  card(key_tform, val_tform, com_tform);
998  card(key_tunit, val_tunit, com_tunit);
999  if (!val_tdim.empty()) {
1000  card(key_tdim, val_tdim, com_tdim);
1001  }
1002  else {
1003  if (hdr.contains(key_tdim)) {
1004  hdr.remove(key_tdim);
1005  }
1006  }
1007 
1008  // Set TZERO and TSCAL keywords for unsigned columns
1009  if (std::abs(m_columns[i]->type()) == __TUSHORT) {
1010  card(key_tzero, 0, "offset for unsigned integers");
1011  card(key_tscal, 0, "data are not scaled");
1012  hdr[key_tzero].value((unsigned long)(32768)); // Use appropriate value method
1013  hdr[key_tscal].value(1);
1014  }
1015  else if ((std::abs(m_columns[i]->type()) == __TULONG) ||
1016  (std::abs(m_columns[i]->type()) == __TUINT)) {
1017  card(key_tzero, 0, "offset for unsigned integers");
1018  card(key_tscal, 0, "data are not scaled");
1019  hdr[key_tzero].value((unsigned long)(2147483648)); // Use appropriate value method
1020  hdr[key_tscal].value(1);
1021  }
1022 
1023  } // endif: column was valid
1024 
1025  // ... otherwise remove keywords
1026  else {
1027  if (hdr.contains(key_ttype)) hdr.remove(key_ttype);
1028  if (hdr.contains(key_tform)) hdr.remove(key_tform);
1029  if (hdr.contains(key_tunit)) hdr.remove(key_tunit);
1030  if (hdr.contains(key_tzero)) hdr.remove(key_tzero);
1031  if (hdr.contains(key_tscal)) hdr.remove(key_tscal);
1032  if (hdr.contains(key_tdim)) hdr.remove(key_tdim);
1033  }
1034 
1035  } // endif: loop over all columns
1036 
1037  // Loop over possible columns remaining in header
1038  for (int i = m_cols; i < 100; ++i) {
1039 
1040  // Setup keywords
1041  std::string key_ttype = "TTYPE" + gammalib::str(i+1);
1042  std::string key_tform = "TFORM" + gammalib::str(i+1);
1043  std::string key_tunit = "TUNIT" + gammalib::str(i+1);
1044  std::string key_tzero = "TZERO" + gammalib::str(i+1);
1045  std::string key_tscal = "TSCAL" + gammalib::str(i+1);
1046  std::string key_tdim = "TDIM" + gammalib::str(i+1);
1047 
1048  // Remove keywords
1049  if (hdr.contains(key_ttype)) hdr.remove(key_ttype);
1050  if (hdr.contains(key_tform)) hdr.remove(key_tform);
1051  if (hdr.contains(key_tunit)) hdr.remove(key_tunit);
1052  if (hdr.contains(key_tzero)) hdr.remove(key_tzero);
1053  if (hdr.contains(key_tscal)) hdr.remove(key_tscal);
1054  if (hdr.contains(key_tdim)) hdr.remove(key_tdim);
1055 
1056  } // endif: loop over possible columns remaining in header
1057 
1058  // Return
1059  return;
1060 }
1061 
1062 
1063 /***********************************************************************//**
1064  * @brief Open Table
1065  *
1066  * @param[in] vptr FITS file pointer.
1067  *
1068  * @exception GException::fits_hdu_not_found
1069  * Specified HDU not found in FITS file.
1070  * @exception GException::fits_error
1071  * A CFITSIO error occured during loading the table.
1072  * @exception GException::fits_unknown_coltype
1073  * FITS column of unsupported type has been found in the FITS file.
1074  * @exception GException::fits_inconsistent_tdim
1075  * The TDIM information provided in the header is inconsistent
1076  * with the size of the column.
1077  *
1078  * This method loads a description of the table into memory. Column data are
1079  * not loaded at this point to save memory. They will be loaded on request
1080  * later.
1081  ***************************************************************************/
1082 void GFitsTable::data_open(void* vptr)
1083 {
1084  // Move to HDU
1086 
1087  // Save FITS file pointer
1088  FPTR_COPY(m_fitsfile, vptr);
1089 
1090  // Determine number of rows in table
1091  int status = 0;
1092  long nrows = 0;
1093  status = __ffgnrw(FPTR(m_fitsfile), &nrows, &status);
1094  if (status != 0) {
1095  throw GException::fits_error(G_DATA_OPEN, status);
1096  }
1097  else {
1098  m_rows = (int)nrows;
1099  }
1100 
1101  // Determine number of columns in table
1102  status = __ffgncl(FPTR(m_fitsfile), &m_cols, &status);
1103  if (status != 0) {
1104  throw GException::fits_error(G_DATA_OPEN, status);
1105  }
1106 
1107  // Allocate and initialise memory for column pointers. Note that this
1108  // initialisation is needed to allow for a clean free_members() call
1109  // in case of any exception.
1110  free_columns();
1111  m_columns = new GFitsTableCol*[m_cols];
1112  for (int i = 0; i < m_cols; ++i) {
1113  m_columns[i] = NULL;
1114  }
1115 
1116  // Get table column information
1117  int typecode = 0;
1118  long repeat = 0;
1119  long width = 0;
1120  for (int i = 0; i < m_cols; ++i) {
1121 
1122  // Get column name
1123  char keyname[10];
1124  char value[80];
1125  sprintf(keyname, "TTYPE%d", i+1);
1126  status = __ffgkey(FPTR(m_fitsfile), keyname, value, NULL, &status);
1127  if (status != 0) {
1128  throw GException::fits_error(G_DATA_OPEN, status);
1129  }
1130  value[strlen(value)-1] = '\0';
1131 
1132  // Get column definition
1133  status = __ffgtcl(FPTR(m_fitsfile), i+1, &typecode, &repeat, &width,
1134  &status);
1135  if (status != 0) {
1136  throw GException::fits_error(G_DATA_OPEN, status);
1137  }
1138 
1139  // Check for unsigned columns
1140  unsigned long offset = 0;
1141  sprintf(keyname, "TZERO%d", i+1);
1142  status = __ffgky(FPTR(m_fitsfile), __TULONG, keyname, &offset, NULL, &status);
1143  if (status == 0) {
1144  if (typecode == __TSHORT && offset == 32768u) {
1145  typecode = __TUSHORT;
1146  }
1147  else if (typecode == -__TSHORT && offset == 32768u) {
1148  typecode = -__TUSHORT;
1149  }
1150  else if (typecode == __TLONG && offset == 2147483648u) {
1151  typecode = __TULONG;
1152  }
1153  else if (typecode == -__TLONG && offset == 2147483648u) {
1154  typecode = -__TULONG;
1155  }
1156  else if (typecode == __TINT && offset == 2147483648u) {
1157  typecode = __TUINT;
1158  }
1159  else if (typecode == -__TINT && offset == 2147483648u) {
1160  typecode = -__TUINT;
1161  }
1162  else {
1163  std::string msg = "But column '"+std::string(value)+"' has"
1164  " typecode "+gammalib::str(typecode)+" and"
1165  " unexpected associated TZERO="+
1166  gammalib::str(offset)+".";
1167  throw GException::fits_error(G_DATA_OPEN, 0, msg);
1168  }
1169  }
1170  else {
1171  status = 0;
1172  }
1173 
1174  // Get column unit (optional, leave blank if not found)
1175  char unit[80];
1176  sprintf(keyname, "TUNIT%d", i+1);
1177  status = __ffgkey(FPTR(m_fitsfile), keyname, unit, NULL, &status);
1178  if (status != 0) {
1179  status = 0;
1180  unit[0] = '\0';
1181  unit[1] = '\0';
1182  }
1183  else {
1184  unit[strlen(unit)-1] = '\0';
1185  }
1186 
1187  // Get column dimension (optional, leave blank if not found)
1188  char dim[80];
1189  sprintf(keyname, "TDIM%d", i+1);
1190  status = __ffgkey(FPTR(m_fitsfile), keyname, dim, NULL, &status);
1191  if (status != 0) {
1192  status = 0;
1193  dim[0] = '\0';
1194  dim[1] = '\0';
1195  }
1196  else {
1197  dim[strlen(dim)-1] = '\0';
1198  }
1199 
1200  // If found, extract column dimension into vector array of integers
1201  std::vector<int> vdim;
1202  std::string sdim = gammalib::strip_chars(gammalib::strip_whitespace(&(dim[1])),"()");
1203  if (sdim.length() > 0) {
1204  std::vector<std::string> elements = gammalib::split(sdim, ",");
1205  for (int k = 0; k < elements.size(); ++k) {
1206  vdim.push_back(gammalib::toint(elements[k]));
1207  }
1208  }
1209 
1210  // Allocate column
1211  m_columns[i] = alloc_column(typecode);
1212  if (m_columns[i] == NULL) {
1213  std::ostringstream colname;
1214  colname << value;
1215  throw GException::fits_unknown_coltype(G_DATA_OPEN, colname.str(), typecode);
1216  }
1217 
1218  // Store column definition
1219  m_columns[i]->name(gammalib::strip_whitespace(&(value[1])));
1220  m_columns[i]->unit(gammalib::strip_whitespace(&(unit[1])));
1221  m_columns[i]->dim(vdim);
1222  m_columns[i]->colnum(i+1);
1223  m_columns[i]->type(typecode);
1224  m_columns[i]->repeat(repeat);
1225  m_columns[i]->width(width);
1226  m_columns[i]->nrows(m_rows);
1227  m_columns[i]->is_variable(typecode < 0);
1229 
1230  // Extract column vector size
1231  if (m_columns[i]->repeat() == 1) { // ASCII tables
1232  m_columns[i]->number(1);
1233  }
1234  else { // Binary tables
1235  if (typecode == __TSTRING) {
1236  m_columns[i]->number(m_columns[i]->repeat() /
1237  m_columns[i]->width());
1238  }
1239  else {
1240  m_columns[i]->number(m_columns[i]->repeat());
1241  }
1242  }
1243 
1244  // If TDIM information was set then check its consistency
1245  if (!vdim.empty()) {
1246 
1247  // Compute expectation
1248  int num = vdim[0];
1249  for (int k = 1; k < vdim.size(); ++k) {
1250  num *= vdim[k];
1251  }
1252 
1253  // Compare with real size
1254  if (num != m_columns[i]->number()) {
1256  vdim,
1257  m_columns[i]->number());
1258  }
1259 
1260  } // endif: Valid TDIM information was found
1261 
1262  } // endfor: looped over all columns
1263 
1264  // Return
1265  return;
1266 }
1267 
1268 
1269 /***********************************************************************//**
1270  * @brief Save table into FITS file
1271  *
1272  * @exception GException::fits_error
1273  * A CFITSIO error occured in this method.
1274  * @exception GException::fits_bad_col_length
1275  * Table columns have inconsistent lengths.
1276  *
1277  * Saves the FITS table into the FITS file.
1278  *
1279  * In case that no table HDU exists so far in the FITS file, a new table
1280  * will be appended to the FITS file. In case that a different HDU type
1281  * exists at the requested extension number, the existing HDU will be
1282  * deleted and be replaced by the requested table type.
1283  *
1284  * The method also verifies the consistency of all table columns. Table
1285  * columns need to have identical lengths to be saved into a FITS table.
1286  * All columns with a length of zero will be excluded from saving, and if
1287  * they exist in the FITS file, they will be removed from the file.
1288  *
1289  * @todo This method should also update the header. Even easier, this method
1290  * should save the header into the file using the m_header.save()
1291  * method.
1292  * Only this assures coherence between the files !!!! Once this has
1293  * been implemented (also in the GFitsImage method) we should delete
1294  * the m_header.save() call in GFitsHDU::save.
1295  ***************************************************************************/
1297 {
1298  // Debug definition: Dump method entry
1299  #if defined(G_DEBUG_SAVE)
1300  std::cout << "GFitsTable::save: entry" << std::endl;
1301  for (int i = 0; i < m_cols; ++i) {
1302  std::cout << m_columns[i]->print() << std::endl;
1303  }
1304  #endif
1305 
1306  // Make sure that column lengths are consistent with table length.
1307  // Columns with zero length will not be considered (why?)
1308  for (int i = 0; i < m_cols; ++i) {
1309  if (m_columns[i] != NULL && m_columns[i]->nrows() > 0) {
1310  if (m_columns[i]->nrows() != m_rows) {
1312  m_columns[i]->nrows(),
1313  m_rows);
1314  }
1315  }
1316  }
1317 
1318  // Move to HDU
1319  int status = 0;
1320  int type = 0;
1321  status = __ffmahd(FPTR(m_fitsfile), m_hdunum+1, &type, &status);
1322 
1323  // If move was successful but HDU type in file differs from HDU type
1324  // of object then replace the HDU in the file
1325  bool replace = (status == 0 && type != m_type);
1326 
1327  // If HDU does not exist in file or should be replaced then create or
1328  // replace it now
1329  if (status == 107 || replace) {
1330 
1331  // Reset status
1332  status = 0;
1333 
1334  // Initialise number of fields
1335  int tfields = 0;
1336 
1337  // Setup cfitsio column definition arrays
1338  char** ttype = NULL;
1339  char** tform = NULL;
1340  char** tunit = NULL;
1341  if (m_cols > 0) {
1342  ttype = new char*[m_cols];
1343  tform = new char*[m_cols];
1344  tunit = new char*[m_cols];
1345  for (int i = 0; i < m_cols; ++i) {
1346  ttype[i] = NULL;
1347  tform[i] = NULL;
1348  tunit[i] = NULL;
1349  }
1350  for (int i = 0; i < m_cols; ++i) {
1351  ttype[tfields] = get_ttype(i);
1352  tform[tfields] = get_tform(i);
1353  tunit[tfields] = get_tunit(i);
1354  if (ttype[tfields] != NULL && tform[tfields] != NULL &&
1355  tunit[tfields] != NULL)
1356  tfields++;
1357  }
1358  }
1359 
1360  // Replace FITS HDU by table
1361  if (replace) {
1362 
1363  // Delete current FITS HDU
1364  status = __ffdhdu(FPTR(m_fitsfile), NULL, &status);
1365  if (status != 0) {
1366  throw GException::fits_error(G_DATA_SAVE, status);
1367  }
1368 
1369  // Insert either ASCII or Binary table at current HDU position
1370  if (exttype() == GFitsHDU::HT_ASCII_TABLE) {
1371  long tbcol = 0;
1372  long rowlen = 0;
1373  status = __ffgabc(tfields, tform, 1, &rowlen, &tbcol, &status);
1374  status = __ffitab(FPTR(m_fitsfile), rowlen, m_rows, tfields, ttype,
1375  &tbcol, tform, tunit, NULL, &status);
1376  }
1377  else {
1378  status = __ffibin(FPTR(m_fitsfile), m_rows, tfields, ttype, tform,
1379  tunit, NULL, 0, &status);
1380  }
1381  if (status != 0) {
1382  throw GException::fits_error(G_DATA_SAVE, status);
1383  }
1384 
1385  }
1386 
1387  // ... otherwise create FITS table
1388  else {
1389  status = __ffcrtb(FPTR(m_fitsfile), m_type, m_rows, tfields,
1390  ttype, tform, tunit, NULL, &status);
1391  if (status != 0) {
1392  throw GException::fits_error(G_DATA_SAVE, status);
1393  }
1394  }
1395 
1396  // De-allocate column definition arrays
1397  if (m_cols > 0) {
1398  for (int i = 0; i < m_cols; ++i) {
1399  if (ttype[i] != NULL) delete [] ttype[i];
1400  if (tform[i] != NULL) delete [] tform[i];
1401  if (tunit[i] != NULL) delete [] tunit[i];
1402  }
1403  if (ttype != NULL) delete [] ttype;
1404  if (ttype != NULL) delete [] tform;
1405  if (ttype != NULL) delete [] tunit;
1406  }
1407 
1408  // Connect all existing columns to FITS table
1409  if (m_columns != NULL) {
1410  for (int i = 0; i < m_cols; ++i) {
1411  if (m_columns[i] != NULL) {
1413  m_columns[i]->colnum(i+1);
1414  }
1415  }
1416  }
1417 
1418  // Debug option: Signal table creation
1419  #if defined(G_DEBUG_SAVE)
1420  std::cout << "GFitsTable::save: created new table" << std::endl;
1421  #endif
1422 
1423  }
1424 
1425  // ... otherwise we signal a FITS error
1426  else if (status != 0) {
1427  throw GException::fits_error(G_DATA_SAVE, status);
1428  }
1429 
1430  // Determine number of columns in table
1431  int num_cols = 0;
1432  status = __ffgncl(FPTR(m_fitsfile), &num_cols, &status);
1433  if (status != 0) {
1434  throw GException::fits_error(G_DATA_SAVE, status);
1435  }
1436 
1437  // Debug option: Log number of columns in FITS table
1438  #if defined(G_DEBUG_SAVE)
1439  std::cout << "GFitsTable::save: FITS table contains ";
1440  std::cout << num_cols << " columns." << std::endl;
1441  #endif
1442 
1443  // If we have no columns in the table then delete all columns from
1444  // FITS table
1445  if (m_columns == NULL && num_cols > 0) {
1446  for (int i = 0; i < num_cols; ++i) {
1447  status = __ffdcol(FPTR(m_fitsfile), i, &status);
1448  if (status != 0) {
1449  throw GException::fits_error(G_DATA_SAVE, status);
1450  }
1451  }
1452  }
1453 
1454  // ... otherwise update the FITS table
1455  else {
1456 
1457  // Determine number of rows in table
1458  long num_rows = 0;
1459  status = __ffgnrw(FPTR(m_fitsfile), &num_rows, &status);
1460  if (status != 0) {
1461  throw GException::fits_error(G_DATA_SAVE, status);
1462  }
1463 
1464  // Debug option: Log number of rows in FITS table
1465  #if defined(G_DEBUG_SAVE)
1466  std::cout << "GFitsTable::save: FITS table contains ";
1467  std::cout << num_rows << " rows." << std::endl;
1468  #endif
1469 
1470  // If the table length differs from number of rows in the FITS file
1471  // then readjust FITS table length. We do this by adding or
1472  // deleting rows at the end of the table as we anyways rewrite the
1473  // entire table later
1474  if (m_rows > num_rows) {
1475 
1476  // Insert rows at the end of the table
1477  long long firstrow = num_rows;
1478  long long nrows = m_rows - num_rows;
1479  status = __ffirow(FPTR(m_fitsfile), firstrow, nrows, &status);
1480  if (status != 0) {
1481  throw GException::fits_error(G_DATA_SAVE, status);
1482  }
1483 
1484  // Debug option: Log row adding
1485  #if defined(G_DEBUG_SAVE)
1486  std::cout << "GFitsTable::save: Added " << nrows;
1487  std::cout << " rows to FITS table." << std::endl;
1488  #endif
1489 
1490  }
1491  else if (m_rows < num_rows) {
1492 
1493  // Delete rows at the end of the table
1494  long long firstrow = num_rows;
1495  long long nrows = num_rows - m_rows;
1496  status = __ffdrow(FPTR(m_fitsfile), firstrow, nrows, &status);
1497  if (status != 0) {
1498  throw GException::fits_error(G_DATA_SAVE, status);
1499  }
1500 
1501  // Debug option: Log row adding
1502  #if defined(G_DEBUG_SAVE)
1503  std::cout << "GFitsTable::save: Deleted " << nrows;
1504  std::cout << " rows from FITS table." << std::endl;
1505  #endif
1506 
1507  }
1508 
1509  // Debug option: Show where we are
1510  #if defined(G_DEBUG_SAVE)
1511  std::cout << "GFitsTable::save: Now update all columns." << std::endl;
1512  #endif
1513 
1514  // Update all columns. The 'm_colnum' field specifies where in the
1515  // FITS file the column resides. If 'm_colnum=0' then we have a new
1516  // column that does not yet exist. In this case we append a new column
1517  // to the FITS file.
1518  for (int i = 0; i < m_cols; ++i) {
1519 
1520  // Only consider valid columns
1521  if (m_columns[i] != NULL) {
1522 
1523  // If column has no correspondance than add new column in
1524  // FITS table and link column to table.
1525  if (m_columns[i]->colnum() == 0) {
1526 
1527  // Increment number of columns in FITS file
1528  num_cols++;
1529 
1530  // Append column to FITS file
1531  status = __fficol(FPTR(m_fitsfile), num_cols, get_ttype(i),
1532  get_tform(i), &status);
1533  if (status != 0) {
1534  throw GException::fits_error(G_DATA_SAVE, status);
1535  }
1536 
1537  // Connect all column to FITS table by copying over the
1538  // FITS file pointer.
1540  m_columns[i]->colnum(num_cols);
1541 
1542  } // endif: column appended to FITS file
1543 
1544  // Now write column into FITS file (only if length is positive)
1545  if (m_columns[i]->nrows() > 0) {
1546  // Debug option: Show which column we're going to write
1547  #if defined(G_DEBUG_SAVE)
1548  std::cout << "GFitsTable::save: Write column " << i;
1549  std::cout << "." << std::endl;
1550  #endif
1551  m_columns[i]->save();
1552  }
1553 
1554  } // endif: column was valid
1555  } // endfor: looped over all table columns
1556 
1557  // Debug option: Show where we are
1558  #if defined(G_DEBUG_SAVE)
1559  std::cout << "GFitsTable::save: Now delete all obsolete columns.";
1560  std::cout << std::endl;
1561  #endif
1562 
1563  // Delete all obsolete columns from FITS file. We do this from last to
1564  // first so that the column numbers remain valid. Also note that
1565  // FITS column counting starts from 1.
1566  for (int colnum = num_cols; colnum > 0; --colnum) {
1567 
1568  // Get column name from FITS file
1569  char keyname[10];
1570  char value[80];
1571  sprintf(keyname, "TTYPE%d", colnum);
1572  status = __ffgkey(FPTR(m_fitsfile), keyname, value, NULL, &status);
1573  if (status != 0) {
1574  throw GException::fits_error(G_DATA_SAVE, status);
1575  }
1576  value[strlen(value)-1] = '\0';
1577  std::string colname = gammalib::strip_whitespace(&(value[1]));
1578 
1579  // Check if this column is actually in our list of columns
1580  bool used = false;
1581  for (int i = 0; i < m_cols; ++i) {
1582  if (m_columns[i] != NULL &&
1583  m_columns[i]->nrows() > 0 &&
1584  m_columns[i]->name() == colname) {
1585  used = true;
1586  break;
1587  }
1588  }
1589 
1590  // If column is not used then delete it now from FITS table
1591  if (!used) {
1592 
1593  // Delete column
1594  status = __ffdcol(FPTR(m_fitsfile), colnum, &status);
1595  if (status != 0) {
1596  throw GException::fits_error(G_DATA_SAVE, status);
1597  }
1598 
1599  // Debug option: Log column deletion
1600  #if defined(G_DEBUG_SAVE)
1601  std::cout << "GFitsTable::save: Deleted obsolete column ";
1602  std::cout << value << " from FITS table." << std::endl;
1603  #endif
1604 
1605  } // endif: deleted column
1606 
1607  } // endfor: Looped over all FITS columns
1608 
1609  } // endelse: FITS table has been updated
1610 
1611  // Debug option: Show where we are
1612  #if defined(G_DEBUG_SAVE)
1613  std::cout << "GFitsTable::save: Now update the FITS header for all columns.";
1614  std::cout << std::endl;
1615  #endif
1616 
1617  // Now update the header (IS THIS REALLY NEEDED HERE???)
1618  update_header();
1619 
1620  // Debug definition: Dump method exit
1621  #if defined(G_DEBUG_SAVE)
1622  std::cout << "GFitsTable::save: exit" << std::endl;
1623  #endif
1624 
1625  // Return
1626  return;
1627 }
1628 
1629 
1630 /***********************************************************************//**
1631  * @brief Close table
1632  ***************************************************************************/
1634 {
1635  // Free members
1636  free_members();
1637 
1638  // Initialise members
1639  init_members();
1640 
1641  // Return
1642  return;
1643 }
1644 
1645 
1646 /***********************************************************************//**
1647  * @brief Connect table data to FITS file
1648  *
1649  * @param[in] vptr FITS file pointer.
1650  *
1651  * Connects the table columns to the file specified by the FITS file pointer.
1652  * This method does nothing if the file pointer in not valid.
1653  ***************************************************************************/
1655 {
1656  // Continue only if file pointer is valid
1657  if (vptr != NULL) {
1658 
1659  // Connect all columns
1660  if (m_columns != NULL) {
1661  for (int i = 0; i < m_cols; ++i) {
1662  if (m_columns[i] != NULL) m_columns[i]->connect(vptr);
1663  }
1664  }
1665 
1666  } // endif: file pointer was valid
1667 
1668  // Return
1669  return;
1670 }
1671 
1672 
1673 /***********************************************************************//**
1674  * @brief Returns pointer to column type
1675  *
1676  * @param[in] colnum Column number for which type is to be returned
1677  *
1678  * This methods allocates memory for the character string that holds the
1679  * column type. The client has to de-allocate this memory after usage.
1680  * In case that the column does not exist a NULL pointer is returned.
1681  ***************************************************************************/
1682 char* GFitsTable::get_ttype(const int& colnum) const
1683 {
1684  // Initialise result with NULL pointer
1685  char* ptr = NULL;
1686 
1687  // Get type only if column exists
1688  if (m_columns != NULL && colnum >=0 && colnum < m_cols &&
1689  m_columns[colnum] != NULL) {
1690  int size = m_columns[colnum]->name().length();
1691  ptr = new char[size+1];
1692  std::strncpy(ptr, m_columns[colnum]->name().c_str(), size);
1693  ptr[size] = '\0';
1694  }
1695 
1696  // Return result
1697  return ptr;
1698 }
1699 
1700 
1701 /***********************************************************************//**
1702  * @brief Returns pointer to column format
1703  *
1704  * @param[in] colnum Column number (starting from 0).
1705  *
1706  * @exception GException::fits_unknown_tabtype
1707  * Table is neither ASCII nor Binary.
1708  *
1709  * This methods allocates memory for the character string that holds the
1710  * column format. The client has to de-allocate this memory after usage.
1711  * In case that the column does not exist a NULL pointer is returned.
1712  ***************************************************************************/
1713 char* GFitsTable::get_tform(const int& colnum) const
1714 {
1715  // Initialise result with NULL pointer
1716  char* ptr = NULL;
1717 
1718  // Get type only if column exists
1719  if (m_columns != NULL && colnum >=0 && colnum < m_cols &&
1720  m_columns[colnum] != NULL) {
1721 
1722  // Get table type specific format
1723  int size;
1724  switch (m_type) {
1726  size = m_columns[colnum]->ascii_format().length();
1727  ptr = new char[size+1];
1728  std::strncpy(ptr, m_columns[colnum]->ascii_format().c_str(), size);
1729  break;
1731  size = m_columns[colnum]->tform_binary().length();
1732  ptr = new char[size+1];
1733  std::strncpy(ptr, m_columns[colnum]->tform_binary().c_str(), size);
1734  break;
1735  default:
1737  break;
1738  }
1739  ptr[size] = '\0';
1740  }
1741 
1742  // Return result
1743  return ptr;
1744 }
1745 
1746 
1747 /***********************************************************************//**
1748  * @brief Returns pointer to column unit
1749  *
1750  * @param[in] colnum Column number (starting from 0).
1751  *
1752  * This methods allocates memory for the character string that holds the
1753  * column unit. The client has to de-allocate this memory after usage.
1754  * In case that the column does not exist a NULL pointer is returned.
1755  ***************************************************************************/
1756 char* GFitsTable::get_tunit(const int& colnum) const
1757 {
1758  // Initialise result with NULL pointer
1759  char* ptr = NULL;
1760 
1761  // Get type only if column exists
1762  if (m_columns != NULL && colnum >=0 && colnum < m_cols &&
1763  m_columns[colnum] != NULL) {
1764  int size = m_columns[colnum]->unit().length();
1765  ptr = new char[size+1];
1766  std::strncpy(ptr, m_columns[colnum]->unit().c_str(), size);
1767  ptr[size] = '\0';
1768  }
1769 
1770  // Return result
1771  return ptr;
1772 }
1773 
1774 
1775 /*==========================================================================
1776  = =
1777  = Private methods =
1778  = =
1779  ==========================================================================*/
1780 
1781 /***********************************************************************//**
1782  * @brief Allocates column
1783  *
1784  * @param[in] typecode cfitsio type code
1785  *
1786  * Allocates a table column depending on the cfitsio type code. If the type
1787  * code is not found then return a NULL pointer.
1788  ***************************************************************************/
1790 {
1791  // Initialise table column pointer
1792  GFitsTableCol* ptr = NULL;
1793 
1794  // Allocate column
1795  switch (std::abs(typecode)) {
1796  case __TBIT:
1797  ptr = new GFitsTableBitCol;
1798  break;
1799  case __TBYTE:
1800  ptr = new GFitsTableByteCol;
1801  break;
1802  case __TLOGICAL:
1803  ptr = new GFitsTableBoolCol;
1804  break;
1805  case __TSTRING:
1806  ptr = new GFitsTableStringCol;
1807  break;
1808  case __TUSHORT:
1809  ptr = new GFitsTableUShortCol;
1810  break;
1811  case __TSHORT:
1812  ptr = new GFitsTableShortCol;
1813  break;
1814  case __TULONG:
1815  ptr = new GFitsTableULongCol;
1816  break;
1817  case __TLONG:
1818  ptr = new GFitsTableLongCol;
1819  break;
1820  case __TFLOAT:
1821  ptr = new GFitsTableFloatCol;
1822  break;
1823  case __TLONGLONG:
1824  ptr = new GFitsTableLongLongCol;
1825  break;
1826  case __TDOUBLE:
1827  ptr = new GFitsTableDoubleCol;
1828  break;
1829  case __TCOMPLEX:
1830  ptr = new GFitsTableCFloatCol;
1831  break;
1832  case __TDBLCOMPLEX:
1833  ptr = new GFitsTableCDoubleCol;
1834  break;
1835  default:
1836  break;
1837  }
1838 
1839  // Return
1840  return ptr;
1841 }
1842 
1843 
1844 /***********************************************************************//**
1845  * @brief Returns pointer of column with given name
1846  *
1847  * @param[in] colname Name of column.
1848  * @return Pointer to table column (NULL if column has not been found).
1849  *
1850  * Returns a pointer to the column with the specified name. If more columns
1851  * with the same name exist, a pointer to the first of these columns is
1852  * returned. If no column with the specified name is found, a NULL pointer
1853  * will be returned.
1854  ***************************************************************************/
1855 GFitsTableCol* GFitsTable::ptr_column(const std::string& colname) const
1856 {
1857  // Initialise pointer
1858  GFitsTableCol* ptr = NULL;
1859 
1860  // If there are columns then search for the specified name
1861  if (m_columns != NULL) {
1862  for (int i = 0; i < m_cols; ++i) {
1863  if (m_columns[i]->name() == colname) {
1864  ptr = m_columns[i];
1865  break;
1866  }
1867  }
1868  }
1869 
1870  // Return pointer
1871  return ptr;
1872 }
1873 
1874 
1875 /***********************************************************************//**
1876  * @brief Returns column number of a given column name
1877  *
1878  * @param[in] colname Name of column.
1879  * @return Column number (-1 if column has not been found).
1880  *
1881  * Returns the column number of the column with the specified name. If more
1882  * columns with the same name exist, the first of these columns is returned.
1883  ***************************************************************************/
1884 int GFitsTable::colnum(const std::string& colname) const
1885 {
1886  // Initialise column number
1887  int colnum = -1;
1888 
1889  // If there are columns then search for the specified name
1890  if (m_columns != NULL) {
1891  for (int i = 0; i < m_cols; ++i) {
1892  if (m_columns[i]->name() == colname) {
1893  colnum = i;
1894  break;
1895  }
1896  }
1897  }
1898 
1899  // Return column number
1900  return colnum;
1901 }
#define __TINT
FITS table Boolean column class interface definition.
void unit(const std::string &unit)
Set column unit.
#define G_SET1
Definition: GFitsTable.cpp:58
const int & ncols(void) const
Return number of columns in table.
Definition: GFitsTable.hpp:134
#define __TUSHORT
FITS table double column class interface definition.
void number(const int &number)
Set number of elements in column.
virtual GFitsTableCol * clone(void) const =0
Clones object.
#define __ffdhdu(A, B, C)
std::string replace_segment(const std::string &arg, const std::string &segment, const std::string &replacement)
Replace string segment in string.
Definition: GTools.cpp:161
#define __TULONG
int m_rows
Number of rows in table.
Definition: GFitsTable.hpp:99
#define __TLOGICAL
std::string number(const std::string &noun, const int &number)
Convert singular noun into number noun.
Definition: GTools.cpp:1046
void connect(void *vptr)
Connect table column to FITS file.
GFitsTable(void)
Void constructor.
Definition: GFitsTable.cpp:89
#define FPTR(A)
FITS table unsigned long integer column class interface definition.
#define __ffgtcl(A, B, C, D, E, F)
char * get_ttype(const int &colnum) const
Returns pointer to column type.
GFitsTableCol * set(const int &colnum, const GFitsTableCol &column)
Set column.
Definition: GFitsTable.cpp:336
std::string print_hdu(const GChatter &chatter=NORMAL) const
Print basic HDU information.
Definition: GFitsHDU.cpp:371
void insert_rows(const int &row, const int &nrows)
Insert rows into the table.
Definition: GFitsTable.cpp:634
GVector abs(const GVector &vector)
Computes absolute of vector elements.
Definition: GVector.cpp:1163
Abstract FITS extension base class.
Definition: GFitsHDU.hpp:51
int m_type
Table type (1=ASCII, 2=Binary)
Definition: GFitsTable.hpp:98
#define G_ACCESS2
Definition: GFitsTable.cpp:57
GFitsTableCol * alloc_column(int typecode) const
Allocates column.
FITS table double complex column.
bool contains(const std::string &colname) const
Checks the presence of a column in table.
Definition: GFitsTable.cpp:716
FITS table long long integer column class interface definition.
virtual HDUType exttype(void) const =0
void nrows(const int &nrows)
Set number of rows in column.
FITS table Byte column.
void append_rows(const int &nrows)
Append rows to the table.
Definition: GFitsTable.cpp:606
std::string strip_chars(const std::string &arg, const std::string &chars)
Strip leading and trailing character from string.
Definition: GTools.cpp:87
std::vector< std::string > split(const std::string &s, const std::string &sep)
Split string.
Definition: GTools.cpp:862
void * m_fitsfile
FITS file pointer pointing on actual HDU.
Definition: GFitsHDU.hpp:133
#define __TDOUBLE
GFitsTableCol ** m_columns
Array of table columns.
Definition: GFitsTable.hpp:101
Gammalib tools definition.
FITS table float column class interface definition.
char * get_tform(const int &colnum) const
Returns pointer to column format.
const GFitsHeader & header(void) const
Return extension header.
Definition: GFitsHDU.hpp:205
FITS file class interface definition.
#define __ffgkey(A, B, C, D, E)
std::string strip_whitespace(const std::string &arg)
Strip leading and trailing whitespace from string.
Definition: GTools.cpp:73
#define __TDBLCOMPLEX
virtual void insert(const int &row, const int &nrows)=0
GFitsHDU & operator=(const GFitsHDU &hdu)
Assignment operator.
Definition: GFitsHDU.cpp:124
#define __TBYTE
virtual void remove(const int &row, const int &nrows)=0
FITS table string column.
GFitsTableCol * operator[](const int &colnum)
Returns pointer to table column.
Definition: GFitsTable.cpp:197
#define __ffgncl(A, B, C)
Interface for FITS header class.
Definition: GFitsHeader.hpp:49
std::string print(const GChatter &chatter=NORMAL) const
Print column information.
#define __ffmahd(A, B, C, D)
#define __TBIT
#define __TCOMPLEX
void data_save(void)
Save table into FITS file.
#define G_INSERT_ROWS
Definition: GFitsTable.cpp:64
char * get_tunit(const int &colnum) const
Returns pointer to column unit.
FITS table Bit column.
#define __TSHORT
void update_header(void)
Update header after row or column manipulations.
Definition: GFitsTable.cpp:908
CFITSIO interface header.
Abstract interface for FITS table column.
#define __ffibin(A, B, C, D, E, F, G, H, I)
FITS table Boolean column.
void data_open(void *vptr)
Open Table.
void copy_members(const GFitsTable &table)
Copy class members.
Definition: GFitsTable.cpp:825
#define G_DATA_OPEN
Definition: GFitsTable.cpp:66
void width(const int &width)
Set width in Bytes of one column element.
FITS table unsigned long integer column.
FITS table string column class interface definition.
FITS table unsigned short integer column class interface definition.
virtual ~GFitsTable(void)
Destructor.
Definition: GFitsTable.cpp:140
#define __TUINT
#define __ffdrow(A, B, C, D)
Abstract interface for FITS table.
Definition: GFitsTable.hpp:44
void type(const int &type)
Set type code.
FITS table bit column class interface definition.
#define __ffgky(A, B, C, D, E, F)
std::string print(const GChatter &chatter=NORMAL) const
Print FITS header information.
GChatter
Definition: GTypemaps.hpp:33
void dim(const std::vector< int > &dim)
Set column dimension.
#define __ffitab(A, B, C, D, E, F, G, H, I, J)
#define G_REMOVE1
Definition: GFitsTable.cpp:62
void remove(const int &cardno)
Remove card from header.
#define __TLONG
GFitsHeader m_header
HDU header.
Definition: GFitsHDU.hpp:136
void is_variable(const bool &variable)
Set variable-length flag.
#define G_GET_TFORM
Definition: GFitsTable.cpp:68
FITS table double complex column class interface definition.
#define __ffirow(A, B, C, D)
#define __TFLOAT
#define FPTR_COPY(A, B)
virtual void save(void)
Save table column into FITS file.
FITS table long long integer column.
FITS table long integer column.
void init_members(void)
Initialise class members.
Definition: GFitsTable.cpp:805
const int & nrows(void) const
Return number of rows in table.
Definition: GFitsTable.hpp:119
#define G_DATA_SAVE
Definition: GFitsTable.cpp:67
#define G_REMOVE2
Definition: GFitsTable.cpp:63
void colnum(const int &colnum)
Set column number.
int colnum(const std::string &colname) const
Returns column number of a given column name.
#define __ffgnrw(A, B, C)
#define G_INSERT2
Definition: GFitsTable.cpp:61
std::string typecode(int type) const
Return typecode as string.
Definition: GFitsHDU.cpp:396
#define __TLONGLONG
int m_cols
Number of columns in table.
Definition: GFitsTable.hpp:100
void free_members(void)
Free class members.
Definition: GFitsTable.cpp:855
#define G_REMOVE_ROWS
Definition: GFitsTable.cpp:65
#define G_ACCESS1
Definition: GFitsTable.cpp:56
#define G_INSERT1
Definition: GFitsTable.cpp:60
#define __ffcrtb(A, B, C, D, E, F, G, H, I)
GChatter reduce(const GChatter &chatter)
Reduce chattiness by one level.
Definition: GTypemaps.hpp:65
FITS table unsigned short integer column.
void remove(const int &colnum)
Remove column from the table.
Definition: GFitsTable.cpp:516
bool contains(const int &cardno) const
Check if card is present in header.
Exception handler interface definition.
FITS table long integer column class interface definition.
#define G_SET2
Definition: GFitsTable.cpp:59
void data_close(void)
Close table.
FITS table short integer column class interface definition.
FITS table float column.
void repeat(const int &repeat)
Set repeat value.
int toint(const std::string &arg)
Convert string into integer value.
Definition: GTools.cpp:700
FITS table short integer column.
#define __ffdcol(A, B, C)
void data_connect(void *vptr)
Connect table data to FITS file.
FITS table Byte column class interface definition.
int fits_move_to_hdu(const std::string &caller, void *vptr, const int &hdunum=0)
Move to FITS extension.
Definition: GFits.cpp:1745
void free_columns(void)
Free column pointers.
Definition: GFitsTable.cpp:870
std::string parformat(const std::string &s, const int &indent=0)
Convert string in parameter format.
Definition: GTools.cpp:1022
#define __fficol(A, B, C, D, E)
virtual std::string ascii_format(void) const =0
FITS table float complex column.
#define __ffgabc(A, B, C, D, E, F)
GFitsHeaderCard & card(const int &cardno)
Return header card.
Definition: GFitsHDU.hpp:259
GFitsTableCol * insert(int colnum, const GFitsTableCol &column)
Insert column into the table.
Definition: GFitsTable.cpp:407
void name(const std::string &name)
Set column name.
GFitsTableCol * ptr_column(const std::string &colname) const
Returns pointer of column with given name.
GFitsTable & operator=(const GFitsTable &table)
Assignment operator.
Definition: GFitsTable.cpp:162
void remove_rows(const int &row, const int &nrows)
Remove rows from the table.
Definition: GFitsTable.cpp:677
FITS table double column.
#define __TSTRING
int m_hdunum
HDU number (starting from 0)
Definition: GFitsHDU.hpp:134
FITS table float complex column class interface definition.
std::string print(const GChatter &chatter=NORMAL) const
Print table information.
Definition: GFitsTable.cpp:732
std::string tform_binary(void) const
Returns TFORM code for binary table column.
std::string str(const unsigned short int &value)
Convert unsigned short integer value into string.
Definition: GTools.cpp:413
FITS table abstract base class interface definition.