mxlib
c++ tools for analyzing astronomical data and other tasks by Jared R. Males. [git repo]
Loading...
Searching...
No Matches
fitsHeader.hpp
Go to the documentation of this file.
1/** \file fitsHeader.hpp
2 * \brief Declares and defines a class to work with a FITS header
3 * \ingroup fits_processing_files
4 *
5 */
6
7//***********************************************************************//
8// Copyright 2015-2025 Jared R. Males (jaredmales@gmail.com)
9//
10// This file is part of mxlib.
11//
12// mxlib is free software: you can redistribute it and/or modify
13// it under the terms of the GNU General Public License as published by
14// the Free Software Foundation, either version 3 of the License, or
15// (at your option) any later version.
16//
17// mxlib is distributed in the hope that it will be useful,
18// but WITHOUT ANY WARRANTY; without even the implied warranty of
19// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20// GNU General Public License for more details.
21//
22// You should have received a copy of the GNU General Public License
23// along with mxlib. If not, see <http://www.gnu.org/licenses/>.
24//***********************************************************************//
25
26#ifndef ioutils_fits__fitsHeader_hpp
27#define ioutils_fits__fitsHeader_hpp
28
29#include <list>
30#include <unordered_map>
31#include <iostream>
32#include <vector>
33#include <optional>
34
35#include "fitsHeaderCard.hpp"
36
37namespace mx
38{
39namespace fits
40{
41
42/// Class to manage a FITS file metadata header and provide fast access to the cards by keyword
43/** Manages tasks such as insertion (avoiding duplicates), and keyword lookup.
44 *
45 * The \ref fitsHeaderCard "cards" are stored in a `std::list` to preserve order, but
46 * a `std::unordered_multimap` is used to provide fast keyword lookup into the list.
47 *
48 * \tparam verboseT sets the error reporting \ref mx::verbose "verbosity"
49 *
50 * \ingroup fits_processing
51 */
52template <class verboseT = verbose::d>
54{
55 public:
56 /// The list type
57 /** We use a list, rather than forward_list, so that append (insert at end) is constant time.
58 *
59 */
60 typedef std::list<fitsHeaderCard<verboseT>> cardListT;
61
62 /// The iterator type for the cards list
63 typedef cardListT::iterator headerIteratorT;
64
65 /// The map type
66 /** Use unordered_multimap to allow multiple HISTORY and COMMENT properly, but be as efficient as possible.
67 */
68 typedef std::unordered_multimap<std::string, headerIteratorT> cardMapT;
69
70 /// The value type for the card map
71 typedef cardMapT::value_type cardMapValueT;
72
73 /// The iterator type for the card map
74 typedef cardMapT::iterator mapIteratorT;
75
76 protected:
77 /// The storage for the FITS header cards
79
80 /// This multimap allows for fast lookup by keyword.
82
83 /// Card with empty keyword used as an error sentinel.
85
86 public:
87 /// Default c'tor
88 fitsHeader();
89
90 /// Copy constructor
91 fitsHeader( const fitsHeader &head /**< The fitsHeader to copy */ );
92
93 /// Destructor
95
96 /// Assignment operator
97 fitsHeader &operator=( const fitsHeader &head /**< The fitsHeader to copy */ );
98
99 /// Get iterator to the beginning of the cards list
101
102 /// Get iterator to the end of the cards list
104
105 /// Get iterator pointing to a specific element
106 headerIteratorT iterator( const std::string &keyword /**< The keyword to look up*/ );
107
108 /// Test whether the header is empty.
109 bool empty();
110
111 /// Get number of cards currently stored in the header.
112 size_t size();
113
114 /// Clear all cards from the header
115 error_t clear();
116
117 /// Get number of cards with a given keyword
118 /** Returns the result of the count() method of the header map.
119 *
120 * \retval the number of cards with keyword.
121 */
122 size_t count( const std::string &keyword /**< [in] the keyword to look up*/ );
123
124 /// Erase card by keyword
125 /** This can not be used to erase COMMENT or HISTORY cards.
126 *
127 */
128 error_t erase( const std::string &keyword /**< [in] the keyword of the card to delete*/ );
129
130 /// Erase card by iterator
131 /** This handles COMMENT and HISTORY cards, deleting only the one pointed to by it
132 * using all the contents of the card (not just the keyword)
133 */
134 error_t erase( headerIteratorT it /**< [in] iterator pointing to the card to delete. */ );
135
136 /// Erase the standard entries at the top of the header
137 /** Erases each entry down to BSCALE. This is useful for appending
138 * a header from a previous image to a newly created file. Also erases boilerplate comments,
139 * such as for long string.
140 */
142
143 /// Append a fitsHeaderCard to the end of the header
144 /**
145 */
146 error_t append( const fitsHeaderCard<verboseT> &card /**< [in] the card to append*/ );
147
148 /// Append a string card to the end of the header, from the three components of a card.
149 /**
150 */
151 error_t append( const std::string &k, /**< [in] the keyword of the new card*/
152 const char *v, /**< [in] the value of the new card*/
153 const std::string &c /**< [in] the comment of the new card*/
154 );
155
156 /// Append a card to the end of the header, from the three components of a card.
157 /**
158 * \tparam typeT is the data type of the value
159 *
160 */
161 template <typename typeT>
162 error_t append( const std::string &k, /**< [in] the keyword of the new card*/
163 const typeT &v, /**< [in] the value of the new card*/
164 const std::string &c /**< [in] the comment of the new card*/
165 );
166
167 /// Append a card to the end of the header, from the components of a card with no comment.
168 /**
169 * \tparam typeT is the data type of the value
170 *
171 */
172 template <typename typeT>
173 error_t append( const std::string &k, /**< [in] the keyword of the new card*/
174 const typeT &v /**< [in] the value of the new card*/
175 );
176
177 /// Append a card to the end of the header, with just a keyword.
178 /** Appends a headerCard with unknownType
179 *
180 */
181 error_t append( const std::string &k /**< [in] the keyword of the new card*/ );
182
183 /// Append a fitsHeader to the end of the header
184 /**
185 */
186 error_t append( fitsHeader &head /**< [in] the fitsHeader to append*/ );
187
188 /// Insert a card before another card.
189 /**
190 */
191 error_t insert_before( headerIteratorT it, /**< [in] iterator pointing to the
192 element before which to insert*/
193 fitsHeaderCard<verboseT> card /**< [in] the card to insert*/
194 );
195
196 /// Insert a card before another card, specifying the card by its components.
197 /**
198 * \tparam typeT is the type of the value, which is converted to string for insertion
199 *
200 */
201 template <typename typeT>
202 error_t insert_before( headerIteratorT it, /**< [in] iterator pointing to the element before which to insert*/
203 const std::string &k, /**< [in] the keyword of the new card*/
204 typeT v, /**< [in] the value of the new card*/
205 const std::string &c /**< [in] the comment of the new card*/
206 );
207
208 /// Insert a card before another card, specifying the card by its components.
209 /**
210 * \tparam typeT is the type of the value, which is converted to string for insertion
211 *
212 */
213 template <typename typeT>
214 error_t insert_before( headerIteratorT it, /**< [in] iterator pointing to the element before which to insert*/
215 const std::string &k, /**< [in] the keyword of the new card*/
216 typeT v /**< [in] the value of the new card*/
217 );
218
219 /// Insert a card after another card.
220 /**
221 */
222 error_t insert_after( headerIteratorT it, /**< [in] iterator pointing to the element
223 after which to insert*/
224 fitsHeaderCard<verboseT> card /**< [in] the card to insert*/
225 );
226
227 /// Insert a card after another card, specifying the card by its components.
228 /**
229 * \tparam typeT is the type of the value, which is converted to string for insertion
230 *
231 */
232 template <typename typeT>
233 error_t insert_after( headerIteratorT it, /**< [in] iterator pointing to the element after which to insert*/
234 const std::string &k, /**< [in] the keyword of the new card*/
235 typeT v, /**< [in] the value of the new card*/
236 const std::string &c /**< [in] the comment of the new card*/
237 );
238
239 /// Insert a card after another card, specifying the card by its components.
240 /**
241 * \tparam typeT is the type of the value, which is converted to string for insertion
242 *
243 */
244 template <typename typeT>
245 error_t insert_after( headerIteratorT it, /**< [in] interator pointing to the element after which to insert*/
246 const std::string &k, /**< [in] the keyword of the new card*/
247 typeT v /**< [in] the value of the new card*/
248 );
249
250 /// Card access by keyword operator
251 /** Looks up the card by its keyword, and returns a reference to it.
252 *
253 * \returns on success: fitsHeaderCard& reference to the \ref fitsHeaderCard
254 * \returns on error: a fitsHeaderCard& to a card with an empty keyword
255 */
256 fitsHeaderCard<verboseT> &operator[]( const std::string &keyword /**< [in] the header keyword to look up*/ );
257
258 /// Card access by keyword operator (const version)
259 /** Looks up the card by its keyword, and returns a reference to it.
260 *
261 * \returns on success: fitsHeaderCard& reference to the \ref fitsHeaderCard
262 * \returns on error: a fitsHeaderCard& to a card with an empty keyword
263 */
265 operator[]( const std::string &keyword /**< [in] the header keyword to look up*/ ) const;
266
267}; // fitsHeader
268
269template <class verboseT>
273
274template <class verboseT>
276{
277 operator=( head );
278}
279
280template <class verboseT>
282{
283 clear();
284}
285
286template <class verboseT>
288{
289 m_cardList = head.m_cardList;
290
291 headerIteratorT it = m_cardList.begin();
292
293 m_cardMap.clear();
294 while( it != m_cardList.end() )
295 {
296 m_cardMap.insert( cardMapValueT( it->keyword(), it ) );
297 ++it;
298 }
299
300 return *this;
301}
302
303template <class verboseT>
308
309template <class verboseT>
314
315template <class verboseT>
317{
318 return m_cardMap.find( keyword )->second;
319}
320
321template <class verboseT>
323{
324 return m_cardList.empty();
325}
326
327template <class verboseT>
329{
330 return m_cardList.size();
331}
332
333template <class verboseT>
335{
336 m_cardList.clear();
337 m_cardMap.clear();
338
339 return error_t::noerror;
340}
341
342template <class verboseT>
343size_t fitsHeader<verboseT>::count( const std::string &keyword )
344{
345 return m_cardMap.count( keyword );
346}
347
348template <class verboseT>
349error_t fitsHeader<verboseT>::erase( const std::string &keyword )
350{
351 if( keyword == "COMMENT" )
352 {
353 return internal::mxlib_error_report<verboseT>( error_t::invalidarg, "can't erase COMMENT by keyword" );
354 }
355
356 if( keyword == "HISTORY" )
357 {
358 return internal::mxlib_error_report<verboseT>( error_t::invalidarg, "can't erase HISTORY by keyword" );
359 }
360
361 auto mit = m_cardMap.find( keyword );
362 if( mit == m_cardMap.end() )
363 {
364 return internal::mxlib_error_report<verboseT>( error_t::notfound, "keyword not found in map:" + keyword );
365 }
366
367 headerIteratorT it = mit->second;
368
369 size_t nrmv;
370 try
371 {
372 nrmv = m_cardMap.erase( keyword );
373 }
374 catch( const std::exception &e )
375 {
376 internal::mxlib_error_report<verboseT>( error_t::std_exception,
377 "exception thrown erasing " + keyword + ": " + e.what() );
378 // clang-format off
379 #if defined( MXLIB_CATCH_ALL_EXCEPTIONS ) || defined( MXLIB_CATCH_NONALLOC_EXCEPTIONS )
381 #else
382 throw;
383 #endif
384 // clang-format on
385 }
386
387 if( nrmv != 1 )
388 {
389 return internal::mxlib_error_report<verboseT>( error_t::notfound, "keyword not found:" + keyword );
390 }
391
392 if( it == m_cardList.end() )
393 {
394 return internal::mxlib_error_report<verboseT>( error_t::notfound, "keyword not found in list:" + keyword );
395 }
396
397 try
398 {
399 m_cardList.erase( it ); // this doesn't seem to actually throw but just in case
400 // unclear how to actually test for an error though...
401 }
402 catch( const std::exception &e )
403 {
404 internal::mxlib_error_report<verboseT>( error_t::std_exception,
405 "exception thrown erasing " + keyword + ": " + e.what() );
406 // clang-format off
407 #if defined( MXLIB_CATCH_ALL_EXCEPTIONS ) || defined( MXLIB_CATCH_NONALLOC_EXCEPTIONS )
409 #else
410 throw;
411 #endif
412 // clang-format on
413 }
414
415 return error_t::noerror;
416}
417
418template <class verboseT>
420{
421 if( it == m_cardList.end() )
422 {
423 return internal::mxlib_error_report<verboseT>( error_t::invalidarg, "invalid list iterator" );
424 }
425
426 std::string keyword = it->keyword();
427
428 mapIteratorT mit = m_cardMap.find( keyword );
429
430 if( mit == m_cardMap.end() )
431 {
432 return internal::mxlib_error_report<verboseT>( error_t::notfound, "keyword not found in map:" + keyword );
433 }
434
435 if( keyword == "COMMENT" || keyword == "HISTORY" )
436 {
437 while( mit->second->keyword() == keyword && it->comment() != mit->second->comment() )
438 {
439 ++mit;
440 }
441
442 if( mit == m_cardMap.end() )
443 {
444 return internal::mxlib_error_report<verboseT>( error_t::notfound,
445 keyword + " with matching comment not found" );
446 }
447 }
448
449 m_cardMap.erase( mit ); // does not throw
450
451 try
452 {
453 m_cardList.erase( it ); // this doesn't seem to actually throw but just in case
454 // unclear how to actually test for an error though...
455 }
456 catch( const std::exception &e )
457 {
458 internal::mxlib_error_report<verboseT>( error_t::std_exception,
459 "exception thrown erasing " + keyword + ": " + e.what() );
460 // clang-format off
461 #if defined( MXLIB_CATCH_ALL_EXCEPTIONS ) || defined( MXLIB_CATCH_NONALLOC_EXCEPTIONS )
463 #else
464 throw;
465 #endif
466 // clang-format on
467 }
468
469 return error_t::noerror;
470}
471
472template <class verboseT>
474{
475
476 headerIteratorT it = begin(), nit;
477
478 int n = 0;
479 while( it != end() )
480 {
481 nit = it;
482 ++nit;
483 if( it->keyword() == "SIMPLE" || it->keyword() == "BITPIX" || it->keyword() == "NAXIS" ||
484 it->keyword() == "NAXIS1" || it->keyword() == "NAXIS2" || it->keyword() == "NAXIS3" ||
485 it->keyword() == "EXTEND" || it->keyword() == "BZERO" || it->keyword() == "BSCALE" ||
486 it->keyword() == "LONGSTRN" )
487 {
488 mxlib_error_check( erase( it ) );
489 }
490
491 if( it->keyword() == "COMMENT" )
492 {
493 if( it->comment().find( "FITS (Flexible Image" ) != std::string::npos )
494 {
495 mxlib_error_check( erase( it ) );
496 }
497 else if( it->comment().find( "and Astrophysics'" ) != std::string::npos )
498 {
499 mxlib_error_check( erase( it ) );
500 }
501 }
502
503 if( nit == end() )
504 {
505 break;
506 }
507 it = nit;
508 ++n;
509 }
510
511 return error_t::noerror;
512}
513
514template <class verboseT>
516{
517 if( card.keyword() == "CONTINUE" )
518 {
519 headerIteratorT backIt = m_cardList.end();
520 --backIt;
521 return backIt->appendContinue( card );
522 }
523
524 // First check if duplicate key
525 if( m_cardMap.count( card.keyword() ) > 0 )
526 {
527 if( card.type() != fitsType<fitsCommentType>() && card.type() != fitsType<fitsHistoryType>() )
528 {
529 return internal::mxlib_error_report<verboseT>( error_t::invalidarg,
530 "attempt to duplicate keyword " + card.keyword() );
531 }
532 }
533
534 headerIteratorT insertedIt;
535
536 // Now insert in list
537 try
538 {
539 m_cardList.push_back( card );
540 insertedIt = m_cardList.end();
541 --insertedIt;
542 }
543 catch( const std::bad_alloc &e )
544 {
545 internal::mxlib_error_report<verboseT>( error_t::std_bad_alloc,
546 "inserting " + card.keyword() + " :" + e.what() );
547 // clang-format off
548 #if defined( MXLIB_CATCH_ALL_EXCEPTIONS )
550 #else
551 throw;
552 #endif
553 // clang-format on
554 }
555 catch( const std::exception &e )
556 {
557 internal::mxlib_error_report<verboseT>( error_t::std_exception,
558 "inserting " + card.keyword() + " :" + e.what() );
559 // clang-format off
560 #if defined( MXLIB_CATCH_ALL_EXCEPTIONS ) || defined( MXLIB_CATCH_NONALLOC_EXCEPTIONS )
562 #else
563 throw;
564 #endif
565 // clang-format on
566 }
567
568 // Then add to the Map.
569 try
570 {
571 auto insres = m_cardMap.insert( cardMapValueT( card.keyword(), insertedIt ) );
572
573 if( insres == m_cardMap.end() )
574 {
575 internal::mxlib_error_report<verboseT>( error_t::error, "inserting " + card.keyword() + " failed" );
576 }
577 }
578 catch( const std::bad_alloc &e )
579 {
580 internal::mxlib_error_report<verboseT>( error_t::std_bad_alloc,
581 "inserting " + card.keyword() + " :" + e.what() );
582 // clang-format off
583 #if defined( MXLIB_CATCH_ALL_EXCEPTIONS )
585 #else
586 throw;
587 #endif
588 // clang-format on
589 }
590 catch( const std::exception &e )
591 {
592 internal::mxlib_error_report<verboseT>( error_t::std_exception,
593 "inserting " + card.keyword() + " :" + e.what() );
594 // clang-format off
595 #if defined( MXLIB_CATCH_ALL_EXCEPTIONS ) || defined( MXLIB_CATCH_NONALLOC_EXCEPTIONS )
597 #else
598 throw;
599 #endif
600 // clang-format on
601 }
602
603 return error_t::noerror;
604}
605
606template <class verboseT>
608{
610
611 for( it = head.begin(); it != head.end(); ++it )
612 {
613 mxlib_error_check( append( *it ) );
614 }
615
616 return error_t::noerror;
617}
618
619template <class verboseT>
621{
623}
624
625template <class verboseT>
626error_t fitsHeader<verboseT>::append( const std::string &k, const char *v, const std::string &c )
627{
628 mxlib_error_return( append( fitsHeaderCard<verboseT>( k, v, c ) ) );
629}
630
631template <class verboseT>
632template <typename typeT>
633error_t fitsHeader<verboseT>::append( const std::string &k, const typeT &v, const std::string &c )
634{
635 mxlib_error_return( append( fitsHeaderCard<verboseT>( k, v, c ) ) );
636}
637
638template <class verboseT>
639template <typename typeT>
640error_t fitsHeader<verboseT>::append( const std::string &k, const typeT &v )
641{
642 mxlib_error_return( append( fitsHeaderCard<verboseT>( k, v ) ) );
643}
644
645template <class verboseT>
647{
648 // First check if duplicate key
649 if( m_cardMap.count( card.keyword() ) > 0 )
650 {
651 if( card.type() != fitsType<fitsCommentType>() && card.type() != fitsType<fitsHistoryType>() )
652 {
653 return internal::mxlib_error_report<verboseT>( error_t::invalidarg,
654 "duplicate keyword: " + card.keyword() );
655 }
656 }
657
658 // Now insert in list
659 headerIteratorT insertedIt;
660
661 try
662 {
663 insertedIt = m_cardList.insert( it, card );
664 }
665 catch( const std::bad_alloc &e )
666 {
667 internal::mxlib_error_report<verboseT>( error_t::std_bad_alloc,
668 "inserting " + card.keyword() + " :" + e.what() );
669 // clang-format off
670 #if defined( MXLIB_CATCH_ALL_EXCEPTIONS )
672 #else
673 throw;
674 #endif
675 // clang-format on
676 }
677 catch( const std::exception &e )
678 {
679 internal::mxlib_error_report<verboseT>( error_t::std_exception,
680 "inserting " + card.keyword() + " :" + e.what() );
681 // clang-format off
682 #if defined( MXLIB_CATCH_ALL_EXCEPTIONS ) || defined( MXLIB_CATCH_NONALLOC_EXCEPTIONS )
684 #else
685 throw;
686 #endif
687 // clang-format on
688 }
689
690 // Then add to the Map.
691 try
692 {
693 m_cardMap.insert( cardMapValueT( card.keyword(), insertedIt ) );
694 }
695 catch( const std::bad_alloc &e )
696 {
697 internal::mxlib_error_report<verboseT>( error_t::std_bad_alloc,
698 "inserting " + card.keyword() + " :" + e.what() );
699 // clang-format off
700 #if defined( MXLIB_CATCH_ALL_EXCEPTIONS )
702 #else
703 throw;
704 #endif
705 // clang-format on
706 }
707 catch( const std::exception &e )
708 {
709 internal::mxlib_error_report<verboseT>( error_t::std_exception,
710 "inserting " + card.keyword() + " :" + e.what() );
711 // clang-format off
712 #if defined( MXLIB_CATCH_ALL_EXCEPTIONS ) || defined( MXLIB_CATCH_NONALLOC_EXCEPTIONS )
714 #else
715 throw;
716 #endif
717 // clang-format on
718 }
719
720 return error_t::noerror;
721}
722
723template <class verboseT>
724template <typename typeT>
725error_t fitsHeader<verboseT>::insert_before( headerIteratorT it, const std::string &k, typeT v, const std::string &c )
726{
727 mxlib_error_return( insert_before( it, fitsHeaderCard<verboseT>( k, v, c ) ) );
728}
729
730template <class verboseT>
731template <typename typeT>
733{
734 mxlib_error_return( insert_before( it, fitsHeaderCard<verboseT>( k, v ) ) );
735}
736
737template <class verboseT>
738template <typename typeT>
739error_t fitsHeader<verboseT>::insert_after( headerIteratorT it, const std::string &k, typeT v, const std::string &c )
740{
741 mxlib_error_return( insert_after( it, fitsHeaderCard<verboseT>( k, v, c ) ) );
742}
743
744template <class verboseT>
746{
747 // First check if duplicate key
748 if( m_cardMap.count( card.keyword() ) > 0 )
749 {
750 if( card.type() != fitsType<fitsCommentType>() && card.type() != fitsType<fitsHistoryType>() )
751 {
752 return internal::mxlib_error_report<verboseT>( error_t::invalidarg,
753 "duplicate keyword: " + card.keyword() );
754 }
755 }
756
757 // Now insert in list
758 headerIteratorT insertedIt;
759
760 try
761 {
762 insertedIt = m_cardList.insert( ++it, card );
763 }
764 catch( const std::bad_alloc &e )
765 {
766 internal::mxlib_error_report<verboseT>( error_t::std_bad_alloc,
767 "inserting " + card.keyword() + " :" + e.what() );
768 // clang-format off
769 #if defined( MXLIB_CATCH_ALL_EXCEPTIONS )
771 #else
772 throw;
773 #endif
774 // clang-format on
775 }
776 catch( const std::exception &e )
777 {
778 internal::mxlib_error_report<verboseT>( error_t::std_exception,
779 "inserting " + card.keyword() + " :" + e.what() );
780 // clang-format off
781 #if defined( MXLIB_CATCH_ALL_EXCEPTIONS ) || defined( MXLIB_CATCH_NONALLOC_EXCEPTIONS )
783 #else
784 throw;
785 #endif
786 // clang-format on
787 }
788
789 // Then add to the Map.
790 try
791 {
792 m_cardMap.insert( cardMapValueT( card.keyword(), insertedIt ) );
793 }
794 catch( const std::bad_alloc &e )
795 {
796 internal::mxlib_error_report<verboseT>( error_t::std_bad_alloc,
797 "inserting " + card.keyword() + " :" + e.what() );
798 // clang-format off
799 #if defined( MXLIB_CATCH_ALL_EXCEPTIONS )
801 #else
802 throw;
803 #endif
804 // clang-format on
805 }
806 catch( const std::exception &e )
807 {
808 internal::mxlib_error_report<verboseT>( error_t::std_exception,
809 "inserting " + card.keyword() + " :" + e.what() );
810 // clang-format off
811 #if defined( MXLIB_CATCH_ALL_EXCEPTIONS ) || defined( MXLIB_CATCH_NONALLOC_EXCEPTIONS )
813 #else
814 throw;
815 #endif
816 // clang-format on
817 }
818
819 return error_t::noerror;
820}
821
822template <class verboseT>
823template <typename typeT>
825{
826 mxlib_error_return( insert_after( it, fitsHeaderCard<verboseT>( k, v ) ) );
827}
828
829template <class verboseT>
831{
833
834 auto mit = m_cardMap.find( keyword );
835
836 // If not found, append it.
837 if( mit == m_cardMap.end() )
838 {
839 error_t errc = append( keyword );
840 if( errc != error_t::noerror )
841 {
842 internal::mxlib_error_report<verboseT>( errc, "appending " + keyword );
843 m_emptyCard.keyword( "" ); // we have to reset this every time b/c someone could have changed it
844 return m_emptyCard;
845 }
846
847 // have to do new search
848 mit = m_cardMap.find( keyword );
849 if( mit == m_cardMap.end() )
850 {
851 internal::mxlib_error_report<verboseT>( error_t::notfound, "after appending " + keyword );
852 return m_emptyCard;
853 }
854 }
855
856 it = mit->second;
857
858 return *it;
859}
860
861template <class verboseT>
862const fitsHeaderCard<verboseT> &fitsHeader<verboseT>::operator[]( const std::string &keyword ) const
863{
864 auto mit = m_cardMap.find( keyword );
865 if( mit == m_cardMap.end() )
866 {
867 internal::mxlib_error_report<verboseT>( error_t::notfound, keyword + " not found" );
868 m_emptyCard.keyword( "" ); // we have to reset this every time b/c someone could have changed it
869 return m_emptyCard;
870 }
871
872 headerIteratorT it = mit->second;
873
874 return *it;
875}
876
877/** \addtogroup fits_utils
878 * @{
879 */
880
881/// Convert the values in a std::vector of \ref fitsHeader "fits headers" into a std::vector of values.
882/** Resizes the vector of the appropriate type.
883 *
884 * \returns error_t::noerror on success. \p bad will be empty.
885 * \returns error_t::error if the keyword fails to convert for any header. \p bad will contain the indices of
886 * \p heads for which the extraction of a value for \p keyw failed
887 *
888 * \tparam dataT is the type of the header value
889 * \tparam fitsHeaderT is the fitsHeader type
890 *
891 */
892template <typename dataT, class fitsHeaderT>
893error_t headersToValues( std::vector<dataT> &v, /**< [out] will contain the converted values*/
894 std::vector<size_t> &bad, /**<[out] will contain the indices of any
895 headers that failed conversion */
896 std::vector<fitsHeaderT> &heads, /**< [in] contains the headers */
897 const std::string &keyw /**< [in] contains the keyword designating which
898 value to convert*/
899)
900{
901 v.resize( heads.size() );
902 bad.clear();
903
904 for( size_t i = 0; i < heads.size(); ++i )
905 {
906 error_t errc;
907 v[i] = heads[i][keyw].template value<dataT>( &errc ); // convertFromString<dataT>(heads[i][keyw].value);
908
909 if( errc != error_t::noerror )
910 {
911 bad.push_back( i );
912 v[i] = std::numeric_limits<dataT>::max();
913 }
914 }
915
916 if( bad.size() > 0 )
917 {
918 return error_t::error;
919 }
920 else
921 {
922 return error_t::noerror;
923 }
924}
925
926/// Write the status of a Git repository to HISTORY in a FITS header.
927/**
928 * \param [in,out] head the HISTORY cards will be appended to this header
929 * \param [in] repoName the name of the repository
930 * \param [in] sha1 is the SHA-1 hash string of the repository
931 * \param [in] modified whether or not the repository has been modified after the
932 * commit referred to by sha1
933 */
934template <class fitsHeaderT>
935void fitsHeaderGitStatus( fitsHeaderT &head, const std::string &repoName, const char *sha1, int modified )
936{
937 std::string hist = "Git status for " + repoName + ":";
938 head.append( "", fitsHistoryType(), hist );
939
940 hist = " sha1=";
941 hist += sha1;
942 if( modified )
943 hist += ", modified";
944
945 head.append( "", fitsHistoryType(), hist );
946}
947
948///@}
949
950extern template class fitsHeader<verbose::d>;
951
952} // namespace fits
953} // namespace mx
954
955#endif // ioutils_fits__fitsHeader_hpp
Class to manage the three components of a FITS header card.
int type() const
Get the type.
const std::string & keyword() const
Get the keyword.
Class to manage a FITS file metadata header and provide fast access to the cards by keyword.
error_t append(const fitsHeaderCard< verboseT > &card)
Append a fitsHeaderCard to the end of the header.
size_t count(const std::string &keyword)
Get number of cards with a given keyword.
cardMapT::value_type cardMapValueT
The value type for the card map.
error_t insert_before(headerIteratorT it, fitsHeaderCard< verboseT > card)
Insert a card before another card.
fitsHeaderCard< verboseT > m_emptyCard
Card with empty keyword used as an error sentinel.
cardMapT m_cardMap
This multimap allows for fast lookup by keyword.
fitsHeaderCard< verboseT > & operator[](const std::string &keyword)
Card access by keyword operator.
std::unordered_multimap< std::string, headerIteratorT > cardMapT
The map type.
cardListT::iterator headerIteratorT
The iterator type for the cards list.
headerIteratorT iterator(const std::string &keyword)
Get iterator pointing to a specific element.
fitsHeader & operator=(const fitsHeader &head)
Assignment operator.
cardMapT::iterator mapIteratorT
The iterator type for the card map.
error_t erase(const std::string &keyword)
Erase card by keyword.
fitsHeader()
Default c'tor.
size_t size()
Get number of cards currently stored in the header.
error_t eraseStandardTop()
Erase the standard entries at the top of the header.
bool empty()
Test whether the header is empty.
error_t clear()
Clear all cards from the header.
cardListT m_cardList
The storage for the FITS header cards.
headerIteratorT end()
Get iterator to the end of the cards list.
~fitsHeader()
Destructor.
headerIteratorT begin()
Get iterator to the beginning of the cards list.
std::list< fitsHeaderCard< verboseT > > cardListT
The list type.
error_t insert_after(headerIteratorT it, fitsHeaderCard< verboseT > card)
Insert a card after another card.
A class to work with a FITS header card.
error_t
The mxlib error codes.
Definition error_t.hpp:26
@ noerror
No error has occurred.
@ std_exception
An exception was thrown.
@ std_bad_alloc
A bad allocation exception was thrown.
@ invalidarg
An argument was invalid.
@ notfound
An item was not found.
@ error
A general error has occurred.
#define mxlib_error_check(fxn)
Perform an error check, if an error occurs report it and return the error. Does not return on no erro...
Definition error.hpp:405
#define mxlib_error_return(fxn)
Perform an error check, if an error occurs report it, and return the error code even if no error.
Definition error.hpp:424
void fitsHeaderGitStatus(fitsHeaderT &head, const std::string &repoName, const char *sha1, int modified)
Write the status of a Git repository to HISTORY in a FITS header.
error_t headersToValues(std::vector< dataT > &v, std::vector< size_t > &bad, std::vector< fitsHeaderT > &heads, const std::string &keyw)
Convert the values in a std::vector of fits headers into a std::vector of values.
The mxlib c++ namespace.
Definition mxlib.hpp:37