GammaLib 2.0.0
Loading...
Searching...
No Matches
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"
48#include "GFitsTableLongCol.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
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 ***************************************************************************/
106GFitsTable::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)
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 ***************************************************************************/
241const 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)
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 ***************************************************************************/
281GFitsTableCol* 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 ***************************************************************************/
316const 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 ***************************************************************************/
356GFitsTableCol* GFitsTable::set(const int& colnum, const GFitsTableCol& column)
357{
358 // Check if column number is valid
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
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 ***************************************************************************/
394GFitsTableCol* 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 ***************************************************************************/
434{
435 // Check if column number is valid
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
502
503 // 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 ***************************************************************************/
523GFitsTableCol* 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 ***************************************************************************/
551void GFitsTable::remove(const int& colnum)
552{
553 // Check if column number is valid
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
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 ***************************************************************************/
618void 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 ***************************************************************************/
647void 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 ***************************************************************************/
675void 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
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 ***************************************************************************/
718void 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
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
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 ***************************************************************************/
759bool 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 ***************************************************************************/
775std::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 ***************************************************************************/
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();
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) {
1186 }
1187 else if (typecode == -__TSHORT && offset == 32768u) {
1189 }
1190 else if (typecode == __TLONG && offset == 2147483648u) {
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
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 "+
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);
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
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 ***************************************************************************/
1768char* 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 ***************************************************************************/
1799char* 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 ***************************************************************************/
1843char* 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 ***************************************************************************/
1942GFitsTableCol* 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 ***************************************************************************/
1971int 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 G_REMOVE1
#define G_INSERT2
#define G_INSERT1
#define G_REMOVE2
Exception handler interface definition.
CFITSIO interface header.
#define __TLONG
#define __TLOGICAL
#define __TLONGLONG
#define __ffgtcl(A, B, C, D, E, F)
#define __TSHORT
#define FPTR(A)
#define __ffgnrw(A, B, C)
#define __TDBLCOMPLEX
#define __ffgncl(A, B, C)
#define __TBYTE
#define __TULONG
#define __ffmahd(A, B, C, D)
#define FPTR_COPY(A, B)
#define __ffgabc(A, B, C, D, E, F)
#define __fficol(A, B, C, D, E)
#define __TSTRING
#define __TFLOAT
#define __TDOUBLE
#define __TINT
#define __ffgkyd(A, B, C, D, E)
#define __TUSHORT
#define __ffdhdu(A, B, C)
#define __ffirow(A, B, C, D)
#define __ffgky(A, B, C, D, E, F)
#define __ffdrow(A, B, C, D)
#define __ffgkey(A, B, C, D, E)
#define __TCOMPLEX
#define __ffitab(A, B, C, D, E, F, G, H, I, J)
#define __ffdcol(A, B, C)
#define __ffibin(A, B, C, D, E, F, G, H, I)
#define __TUINT
#define __TBIT
#define __ffcrtb(A, B, C, D, E, F, G, H, I)
FITS table bit column class interface definition.
FITS table Boolean column class interface definition.
FITS table Byte column class interface definition.
FITS table double complex column class interface definition.
FITS table float complex column class interface definition.
FITS table double column class interface definition.
FITS table float column class interface definition.
FITS table long integer column class interface definition.
FITS table long long integer column class interface definition.
FITS table short integer column class interface definition.
FITS table string column class interface definition.
FITS table unsigned long integer column class interface definition.
FITS table unsigned short integer column class interface definition.
#define G_ACCESS2
#define G_GET_TFORM
#define G_INSERT_ROWS
#define G_ACCESS1
#define G_DATA_OPEN
#define G_REMOVE_ROWS
#define G_DATA_SAVE
FITS table abstract base class interface definition.
#define G_SET1
Definition GFits.cpp:57
#define G_SET2
Definition GFits.cpp:58
FITS file class interface definition.
Gammalib tools definition.
GChatter
Definition GTypemaps.hpp:33
@ NORMAL
Definition GTypemaps.hpp:36
@ SILENT
Definition GTypemaps.hpp:34
@ VERBOSE
Definition GTypemaps.hpp:38
Abstract FITS extension base class.
Definition GFitsHDU.hpp:51
const GFitsHeader & header(void) const
Return extension header.
Definition GFitsHDU.hpp:205
GFitsHeader m_header
HDU header.
Definition GFitsHDU.hpp:136
GFitsHDU & operator=(const GFitsHDU &hdu)
Assignment operator.
Definition GFitsHDU.cpp:124
void * m_fitsfile
FITS file pointer pointing on actual HDU.
Definition GFitsHDU.hpp:133
int m_hdunum
HDU number (starting from 0)
Definition GFitsHDU.hpp:134
@ HT_BIN_TABLE
Definition GFitsHDU.hpp:69
@ HT_ASCII_TABLE
Definition GFitsHDU.hpp:68
std::string typecode(int type) const
Return typecode as string.
Definition GFitsHDU.cpp:398
GFitsHeaderCard & card(const int &cardno)
Return header card.
Definition GFitsHDU.hpp:259
std::string print_hdu(const GChatter &chatter=NORMAL) const
Print basic HDU information.
Definition GFitsHDU.cpp:373
Interface for FITS header class.
bool contains(const int &cardno) const
Check if card is present in header.
void remove(const int &cardno)
Remove card from header.
std::string print(const GChatter &chatter=NORMAL) const
Print FITS header information.
FITS table Bit column.
FITS table Boolean column.
FITS table Byte column.
FITS table double complex column.
FITS table float complex column.
Abstract interface for FITS table column.
void connect(void *vptr)
Connect table column to FITS file.
void type(const int &type)
Set type code.
void dim(const std::vector< int > &dim)
Set column dimension.
virtual void remove(const int &row, const int &nrows)=0
void tscale(const double &tscale)
Set TSCALE value.
std::string print(const GChatter &chatter=NORMAL) const
Print column information.
void name(const std::string &name)
Set column name.
void number(const int &number)
Set number of elements in column.
void width(const int &width)
Set width in Bytes of one column element.
void colnum(const int &colnum)
Set column number.
virtual void insert(const int &row, const int &nrows)=0
void unit(const std::string &unit)
Set column unit.
void is_variable(const bool &variable)
Set variable-length flag.
void nrows(const int &nrows)
Set number of rows in column.
virtual std::string ascii_format(void) const =0
std::string tform_binary(void) const
Returns TFORM code for binary table column.
virtual void save(void)
Save table column into FITS file.
virtual GFitsTableCol * clone(void) const =0
Clones object.
void repeat(const int &repeat)
Set repeat value.
FITS table double column.
FITS table float column.
FITS table long integer column.
FITS table long long integer column.
FITS table short integer column.
FITS table string column.
FITS table unsigned long integer column.
FITS table unsigned short integer column.
Abstract interface for FITS table.
void data_save(void)
Save table into FITS file.
virtual HDUType exttype(void) const =0
void data_open(void *vptr)
Open Table.
void remove(const int &colnum)
Remove column from the table.
void update_header(void)
Update header after row or column manipulations.
char * get_tunit(const int &colnum) const
Returns pointer to column unit.
int m_type
Table type (1=ASCII, 2=Binary)
void data_close(void)
Close table.
bool contains(const std::string &colname) const
Checks the presence of a column in table.
GFitsTableCol * ptr_column(const std::string &colname) const
Returns pointer of column with given name.
void data_connect(void *vptr)
Connect table data to FITS file.
void free_members(void)
Free class members.
virtual ~GFitsTable(void)
Destructor.
GFitsTableCol * set(const int &colnum, const GFitsTableCol &column)
Set column.
void init_members(void)
Initialise class members.
int colnum(const std::string &colname) const
Returns column number of a given column name.
void free_columns(void)
Free column pointers.
GFitsTableCol * insert(int colnum, const GFitsTableCol &column)
Insert column into the table.
void append_rows(const int &nrows)
Append rows to the table.
GFitsTableCol ** m_columns
Array of table columns.
GFitsTableCol * alloc_column(int typecode) const
Allocates column.
char * get_tform(const int &colnum) const
Returns pointer to column format.
void insert_rows(const int &row, const int &nrows)
Insert rows into the table.
const int & nrows(void) const
Return number of rows in table.
GFitsTable & operator=(const GFitsTable &table)
Assignment operator.
GFitsTableCol * operator[](const int &colnum)
Returns pointer to table column.
int m_rows
Number of rows in table.
void remove_rows(const int &row, const int &nrows)
Remove rows from the table.
void copy_members(const GFitsTable &table)
Copy class members.
std::string print(const GChatter &chatter=NORMAL) const
Print table information.
const int & ncols(void) const
Return number of columns in table.
int m_cols
Number of columns in table.
char * get_ttype(const int &colnum) const
Returns pointer to column type.
GFitsTable(void)
Void constructor.
std::string parformat(const std::string &s, const int &indent=0)
Convert string in parameter format.
Definition GTools.cpp:1143
std::string str(const unsigned short int &value)
Convert unsigned short integer value into string.
Definition GTools.cpp:489
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
int toint(const std::string &arg)
Convert string into integer value.
Definition GTools.cpp:821
std::string strip_whitespace(const std::string &arg)
Strip leading and trailing whitespace from string.
Definition GTools.cpp:80
int fits_move_to_hdu(const std::string &caller, void *vptr, const int &hdunum=0)
Move to FITS extension.
Definition GFits.cpp:1774
GChatter reduce(const GChatter &chatter)
Reduce chattiness by one level.
Definition GTypemaps.hpp:65
std::vector< std::string > split(const std::string &s, const std::string &sep)
Split string.
Definition GTools.cpp:983
std::string strip_chars(const std::string &arg, const std::string &chars)
Strip leading and trailing character from string.
Definition GTools.cpp:94