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