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
42namespace fitsHeaderDetail
43{
44
45/** \cond */
46/// Mutation stages exposed to deterministic failure tests.
47enum class mutation
48{
49 appendList,
50 appendMap,
51 insertBeforeList,
52 insertBeforeMap,
53 insertAfterList,
54 insertAfterMap
55};
56
57/// Signature of the resettable mutation hook used by fitsHeader.
58using mutationHookT = error_t ( * )( mutation );
59
60/// Access the process-wide fitsHeader mutation hook.
61mutationHookT &mutationHook();
62
63/// Restore the fitsHeader mutation hook to its production no-op.
64void resetMutationHook();
65/** \endcond */
66
67} // namespace fitsHeaderDetail
68
69/// Class to manage a FITS file metadata header and provide fast access to the cards by keyword
70/** Manages tasks such as insertion (avoiding duplicates), and keyword lookup.
71 *
72 * The \ref fitsHeaderCard "cards" are stored in a `std::list` to preserve order, but
73 * a `std::unordered_multimap` is used to provide fast keyword lookup into the list.
74 *
75 * \tparam verboseT sets the error reporting \ref mx::verbose "verbosity"
76 *
77 * \ingroup fits_processing
78 */
79template <class verboseT = verbose::d>
81{
82 public:
83 /// The list type
84 /** We use a list, rather than forward_list, so that append (insert at end) is constant time.
85 *
86 */
87 typedef std::list<fitsHeaderCard<verboseT>> cardListT;
88
89 /// The iterator type for the cards list
90 typedef cardListT::iterator headerIteratorT;
91
92 /// The map type
93 /** Use unordered_multimap to allow multiple HISTORY and COMMENT properly, but be as efficient as possible.
94 */
95 typedef std::unordered_multimap<std::string, headerIteratorT> cardMapT;
96
97 /// The value type for the card map
98 typedef cardMapT::value_type cardMapValueT;
99
100 /// The iterator type for the card map
101 typedef cardMapT::iterator mapIteratorT;
102
103 protected:
104 /// The storage for the FITS header cards
106
107 /// This multimap allows for fast lookup by keyword.
109
110 /// Card with empty keyword used as an error sentinel.
112
113 public:
114 /// Default c'tor
116
117 /// Copy constructor
118 fitsHeader( const fitsHeader &head /**< The fitsHeader to copy */ );
119
120 /// Destructor
122
123 /// Assignment operator
124 fitsHeader &operator=( const fitsHeader &head /**< The fitsHeader to copy */ );
125
126 /// Get iterator to the beginning of the cards list
128
129 /// Get iterator to the end of the cards list
131
132 /// Get iterator pointing to a specific element
133 headerIteratorT iterator( const std::string &keyword /**< The keyword to look up*/ );
134
135 /// Test whether the header is empty.
136 bool empty();
137
138 /// Get number of cards currently stored in the header.
139 size_t size();
140
141 /// Clear all cards from the header
143
144 /// Get number of cards with a given keyword
145 /** Returns the result of the count() method of the header map.
146 *
147 * \retval the number of cards with keyword.
148 */
149 size_t count( const std::string &keyword /**< [in] the keyword to look up*/ );
150
151 /// Erase card by keyword
152 /** This can not be used to erase COMMENT or HISTORY cards.
153 *
154 */
155 error_t erase( const std::string &keyword /**< [in] the keyword of the card to delete*/ );
156
157 /// Erase card by iterator
158 /** This handles COMMENT and HISTORY cards, deleting only the one pointed to by it
159 * using all the contents of the card (not just the keyword)
160 */
161 error_t erase( headerIteratorT it /**< [in] iterator pointing to the card to delete. */ );
162
163 /// Erase the standard entries at the top of the header
164 /** Erases each entry down to BSCALE. This is useful for appending
165 * a header from a previous image to a newly created file. Also erases boilerplate comments,
166 * such as for long string.
167 */
169
170 /// Append a fitsHeaderCard to the end of the header
171 /**
172 */
173 error_t append( const fitsHeaderCard<verboseT> &card /**< [in] the card to append*/ );
174
175 /// Append a string card to the end of the header, from the three components of a card.
176 /**
177 */
178 error_t append( const std::string &k, /**< [in] the keyword of the new card*/
179 const char *v, /**< [in] the value of the new card*/
180 const std::string &c /**< [in] the comment of the new card*/
181 );
182
183 /// Append a card to the end of the header, from the three components of a card.
184 /**
185 * \tparam typeT is the data type of the value
186 *
187 */
188 template <typename typeT>
189 error_t append( const std::string &k, /**< [in] the keyword of the new card*/
190 const typeT &v, /**< [in] the value of the new card*/
191 const std::string &c /**< [in] the comment of the new card*/
192 );
193
194 /// Append a card to the end of the header, from the components of a card with no comment.
195 /**
196 * \tparam typeT is the data type of the value
197 *
198 */
199 template <typename typeT>
200 error_t append( const std::string &k, /**< [in] the keyword of the new card*/
201 const typeT &v /**< [in] the value of the new card*/
202 );
203
204 /// Append a card to the end of the header, with just a keyword.
205 /** Appends a headerCard with unknownType
206 *
207 */
208 error_t append( const std::string &k /**< [in] the keyword of the new card*/ );
209
210 /// Append a fitsHeader to the end of the header
211 /**
212 */
213 error_t append( fitsHeader &head /**< [in] the fitsHeader to append*/ );
214
215 /// Insert a card before another card.
216 /**
217 */
218 error_t insert_before( headerIteratorT it, /**< [in] iterator pointing to the
219 element before which to insert*/
220 fitsHeaderCard<verboseT> card /**< [in] the card to insert*/
221 );
222
223 /// Insert a card before another card, specifying the card by its components.
224 /**
225 * \tparam typeT is the type of the value, which is converted to string for insertion
226 *
227 */
228 template <typename typeT>
229 error_t insert_before( headerIteratorT it, /**< [in] iterator pointing to the element before which to insert*/
230 const std::string &k, /**< [in] the keyword of the new card*/
231 typeT v, /**< [in] the value of the new card*/
232 const std::string &c /**< [in] the comment of the new card*/
233 );
234
235 /// Insert a card before another card, specifying the card by its components.
236 /**
237 * \tparam typeT is the type of the value, which is converted to string for insertion
238 *
239 */
240 template <typename typeT>
241 error_t insert_before( headerIteratorT it, /**< [in] iterator pointing to the element before which to insert*/
242 const std::string &k, /**< [in] the keyword of the new card*/
243 typeT v /**< [in] the value of the new card*/
244 );
245
246 /// Insert a card after another card.
247 /**
248 */
249 error_t insert_after( headerIteratorT it, /**< [in] iterator pointing to the element
250 after which to insert*/
251 fitsHeaderCard<verboseT> card /**< [in] the card to insert*/
252 );
253
254 /// Insert a card after another card, specifying the card by its components.
255 /**
256 * \tparam typeT is the type of the value, which is converted to string for insertion
257 *
258 */
259 template <typename typeT>
260 error_t insert_after( headerIteratorT it, /**< [in] iterator pointing to the element after which to insert*/
261 const std::string &k, /**< [in] the keyword of the new card*/
262 typeT v, /**< [in] the value of the new card*/
263 const std::string &c /**< [in] the comment of the new card*/
264 );
265
266 /// Insert a card after another card, specifying the card by its components.
267 /**
268 * \tparam typeT is the type of the value, which is converted to string for insertion
269 *
270 */
271 template <typename typeT>
272 error_t insert_after( headerIteratorT it, /**< [in] interator pointing to the element after which to insert*/
273 const std::string &k, /**< [in] the keyword of the new card*/
274 typeT v /**< [in] the value of the new card*/
275 );
276
277 /// Card access by keyword operator
278 /** Looks up the card by its keyword, and returns a reference to it.
279 *
280 * \returns on success: fitsHeaderCard& reference to the \ref fitsHeaderCard
281 * \returns on error: a fitsHeaderCard& to a card with an empty keyword
282 */
283 fitsHeaderCard<verboseT> &operator[]( const std::string &keyword /**< [in] the header keyword to look up*/ );
284
285 /// Card access by keyword operator (const version)
286 /** Looks up the card by its keyword, and returns a reference to it.
287 *
288 * \returns on success: fitsHeaderCard& reference to the \ref fitsHeaderCard
289 * \returns on error: a fitsHeaderCard& to a card with an empty keyword
290 */
292 operator[]( const std::string &keyword /**< [in] the header keyword to look up*/ ) const;
293
294}; // fitsHeader
295
296template <class verboseT>
300
301template <class verboseT>
303{
304 operator=( head );
305}
306
307template <class verboseT>
312
313template <class verboseT>
315{
316 m_cardList = head.m_cardList;
317
318 headerIteratorT it = m_cardList.begin();
319
320 m_cardMap.clear();
321 while( it != m_cardList.end() )
322 {
323 m_cardMap.insert( cardMapValueT( it->keyword(), it ) );
324 ++it;
325 }
326
327 return *this;
328}
329
330template <class verboseT>
335
336template <class verboseT>
341
342template <class verboseT>
344{
345 auto mit = m_cardMap.find( keyword );
346 if( mit == m_cardMap.end() )
347 {
348 return m_cardList.end();
349 }
350
351 return mit->second;
352}
353
354template <class verboseT>
356{
357 return m_cardList.empty();
358}
359
360template <class verboseT>
362{
363 return m_cardList.size();
364}
365
366template <class verboseT>
368{
369 m_cardList.clear();
370 m_cardMap.clear();
371
372 return error_t::noerror;
373}
374
375template <class verboseT>
376size_t fitsHeader<verboseT>::count( const std::string &keyword )
377{
378 return m_cardMap.count( keyword );
379}
380
381template <class verboseT>
382error_t fitsHeader<verboseT>::erase( const std::string &keyword )
383{
384 if( keyword == "COMMENT" )
385 {
386 return internal::mxlib_error_report<verboseT>( error_t::invalidarg, "can't erase COMMENT by keyword" );
387 }
388
389 if( keyword == "HISTORY" )
390 {
391 return internal::mxlib_error_report<verboseT>( error_t::invalidarg, "can't erase HISTORY by keyword" );
392 }
393
394 auto mit = m_cardMap.find( keyword );
395 if( mit == m_cardMap.end() )
396 {
397 return internal::mxlib_error_report<verboseT>( error_t::notfound, "keyword not found in map:" + keyword );
398 }
399
400 headerIteratorT it = mit->second;
401
402 m_cardMap.erase( mit );
403 m_cardList.erase( it );
404
405 return error_t::noerror;
406}
407
408template <class verboseT>
410{
411 if( it == m_cardList.end() )
412 {
413 return internal::mxlib_error_report<verboseT>( error_t::invalidarg, "invalid list iterator" );
414 }
415
416 std::string keyword = it->keyword();
417
418 auto range = m_cardMap.equal_range( keyword );
419 mapIteratorT mit;
420 for( mit = range.first; mit != range.second; ++mit )
421 {
422 if( mit->second == it )
423 {
424 break;
425 }
426 }
427
428 m_cardMap.erase( mit ); // does not throw
429 m_cardList.erase( it );
430
431 return error_t::noerror;
432}
433
434template <class verboseT>
436{
437
438 headerIteratorT it = begin(), nit;
439
440 int n = 0;
441 while( it != end() )
442 {
443 nit = it;
444 ++nit;
445 if( it->keyword() == "SIMPLE" || it->keyword() == "BITPIX" || it->keyword() == "NAXIS" ||
446 it->keyword() == "NAXIS1" || it->keyword() == "NAXIS2" || it->keyword() == "NAXIS3" ||
447 it->keyword() == "EXTEND" || it->keyword() == "BZERO" || it->keyword() == "BSCALE" ||
448 it->keyword() == "LONGSTRN" )
449 {
450 mxlib_error_check( erase( it ) );
451 }
452
453 if( it->keyword() == "COMMENT" )
454 {
455 if( it->comment().find( "FITS (Flexible Image" ) != std::string::npos )
456 {
457 mxlib_error_check( erase( it ) );
458 }
459 else if( it->comment().find( "and Astrophysics'" ) != std::string::npos )
460 {
461 mxlib_error_check( erase( it ) );
462 }
463 }
464
465 if( nit == end() )
466 {
467 break;
468 }
469 it = nit;
470 ++n;
471 }
472
473 return error_t::noerror;
474}
475
476template <class verboseT>
478{
479 if( card.keyword() == "CONTINUE" )
480 {
481 if( m_cardList.empty() )
482 {
484 "CONTINUE card requires a preceding card" );
485 }
486
487 headerIteratorT backIt = m_cardList.end();
488 --backIt;
489 return backIt->appendContinue( card );
490 }
491
492 // First check if duplicate key
493 if( m_cardMap.count( card.keyword() ) > 0 )
494 {
495 if( card.type() != fitsType<fitsCommentType>() && card.type() != fitsType<fitsHistoryType>() )
496 {
498 "attempt to duplicate keyword " + card.keyword() );
499 }
500 }
501
502 headerIteratorT insertedIt;
503
504 // Now insert in list
505 try
506 {
507 error_t errc = fitsHeaderDetail::mutationHook()( fitsHeaderDetail::mutation::appendList );
508 if( errc != error_t::noerror )
509 {
510 return internal::mxlib_error_report<verboseT>( errc, "inserting " + card.keyword() );
511 }
512
513 m_cardList.push_back( card );
514 insertedIt = m_cardList.end();
515 --insertedIt;
516 }
517 catch( const std::bad_alloc &e )
518 {
520 "inserting " + card.keyword() + " :" + e.what() );
521 // clang-format off
522 #if defined( MXLIB_CATCH_ALL_EXCEPTIONS )
524 #else
525 throw;
526 #endif
527 // clang-format on
528 }
529 catch( const std::exception &e )
530 {
532 "inserting " + card.keyword() + " :" + e.what() );
533 // clang-format off
534 #if defined( MXLIB_CATCH_ALL_EXCEPTIONS ) || defined( MXLIB_CATCH_NONALLOC_EXCEPTIONS )
536 #else
537 throw;
538 #endif
539 // clang-format on
540 }
541
542 // Then add to the Map.
543 try
544 {
545 error_t errc = fitsHeaderDetail::mutationHook()( fitsHeaderDetail::mutation::appendMap );
546 if( errc != error_t::noerror )
547 {
548 m_cardList.erase( insertedIt );
549 return internal::mxlib_error_report<verboseT>( errc, "inserting " + card.keyword() );
550 }
551
552 m_cardMap.insert( cardMapValueT( card.keyword(), insertedIt ) );
553 }
554 catch( const std::bad_alloc &e )
555 {
556 m_cardList.erase( insertedIt );
558 "inserting " + card.keyword() + " :" + e.what() );
559 // clang-format off
560 #if defined( MXLIB_CATCH_ALL_EXCEPTIONS )
562 #else
563 throw;
564 #endif
565 // clang-format on
566 }
567 catch( const std::exception &e )
568 {
569 m_cardList.erase( insertedIt );
571 "inserting " + card.keyword() + " :" + e.what() );
572 // clang-format off
573 #if defined( MXLIB_CATCH_ALL_EXCEPTIONS ) || defined( MXLIB_CATCH_NONALLOC_EXCEPTIONS )
575 #else
576 throw;
577 #endif
578 // clang-format on
579 }
580
581 return error_t::noerror;
582}
583
584template <class verboseT>
586{
587 if( this == &head )
588 {
589 fitsHeader copy( head );
590 return append( copy );
591 }
592
594
595 for( it = head.begin(); it != head.end(); ++it )
596 {
597 mxlib_error_check( append( *it ) );
598 }
599
600 return error_t::noerror;
601}
602
603template <class verboseT>
608
609template <class verboseT>
610error_t fitsHeader<verboseT>::append( const std::string &k, const char *v, const std::string &c )
611{
613}
614
615template <class verboseT>
616template <typename typeT>
617error_t fitsHeader<verboseT>::append( const std::string &k, const typeT &v, const std::string &c )
618{
620}
621
622template <class verboseT>
623template <typename typeT>
624error_t fitsHeader<verboseT>::append( const std::string &k, const typeT &v )
625{
627}
628
629template <class verboseT>
631{
632 // First check if duplicate key
633 if( m_cardMap.count( card.keyword() ) > 0 )
634 {
635 if( card.type() != fitsType<fitsCommentType>() && card.type() != fitsType<fitsHistoryType>() )
636 {
638 "duplicate keyword: " + card.keyword() );
639 }
640 }
641
642 // Now insert in list
643 headerIteratorT insertedIt;
644
645 try
646 {
647 error_t errc = fitsHeaderDetail::mutationHook()( fitsHeaderDetail::mutation::insertBeforeList );
648 if( errc != error_t::noerror )
649 {
650 return internal::mxlib_error_report<verboseT>( errc, "inserting " + card.keyword() );
651 }
652
653 insertedIt = m_cardList.insert( it, card );
654 }
655 catch( const std::bad_alloc &e )
656 {
658 "inserting " + card.keyword() + " :" + e.what() );
659 // clang-format off
660 #if defined( MXLIB_CATCH_ALL_EXCEPTIONS )
662 #else
663 throw;
664 #endif
665 // clang-format on
666 }
667 catch( const std::exception &e )
668 {
670 "inserting " + card.keyword() + " :" + e.what() );
671 // clang-format off
672 #if defined( MXLIB_CATCH_ALL_EXCEPTIONS ) || defined( MXLIB_CATCH_NONALLOC_EXCEPTIONS )
674 #else
675 throw;
676 #endif
677 // clang-format on
678 }
679
680 // Then add to the Map.
681 try
682 {
683 error_t errc = fitsHeaderDetail::mutationHook()( fitsHeaderDetail::mutation::insertBeforeMap );
684 if( errc != error_t::noerror )
685 {
686 m_cardList.erase( insertedIt );
687 return internal::mxlib_error_report<verboseT>( errc, "inserting " + card.keyword() );
688 }
689
690 m_cardMap.insert( cardMapValueT( card.keyword(), insertedIt ) );
691 }
692 catch( const std::bad_alloc &e )
693 {
694 m_cardList.erase( insertedIt );
696 "inserting " + card.keyword() + " :" + e.what() );
697 // clang-format off
698 #if defined( MXLIB_CATCH_ALL_EXCEPTIONS )
700 #else
701 throw;
702 #endif
703 // clang-format on
704 }
705 catch( const std::exception &e )
706 {
707 m_cardList.erase( insertedIt );
709 "inserting " + card.keyword() + " :" + e.what() );
710 // clang-format off
711 #if defined( MXLIB_CATCH_ALL_EXCEPTIONS ) || defined( MXLIB_CATCH_NONALLOC_EXCEPTIONS )
713 #else
714 throw;
715 #endif
716 // clang-format on
717 }
718
719 return error_t::noerror;
720}
721
722template <class verboseT>
723template <typename typeT>
724error_t fitsHeader<verboseT>::insert_before( headerIteratorT it, const std::string &k, typeT v, const std::string &c )
725{
727}
728
729template <class verboseT>
730template <typename typeT>
732{
734}
735
736template <class verboseT>
737template <typename typeT>
738error_t fitsHeader<verboseT>::insert_after( headerIteratorT it, const std::string &k, typeT v, const std::string &c )
739{
741}
742
743template <class verboseT>
745{
746 if( it == m_cardList.end() )
747 {
748 return internal::mxlib_error_report<verboseT>( error_t::invalidarg, "invalid list iterator" );
749 }
750
751 // First check if duplicate key
752 if( m_cardMap.count( card.keyword() ) > 0 )
753 {
754 if( card.type() != fitsType<fitsCommentType>() && card.type() != fitsType<fitsHistoryType>() )
755 {
757 "duplicate keyword: " + card.keyword() );
758 }
759 }
760
761 // Now insert in list
762 headerIteratorT insertedIt;
763
764 try
765 {
766 error_t errc = fitsHeaderDetail::mutationHook()( fitsHeaderDetail::mutation::insertAfterList );
767 if( errc != error_t::noerror )
768 {
769 return internal::mxlib_error_report<verboseT>( errc, "inserting " + card.keyword() );
770 }
771
772 insertedIt = m_cardList.insert( ++it, card );
773 }
774 catch( const std::bad_alloc &e )
775 {
777 "inserting " + card.keyword() + " :" + e.what() );
778 // clang-format off
779 #if defined( MXLIB_CATCH_ALL_EXCEPTIONS )
781 #else
782 throw;
783 #endif
784 // clang-format on
785 }
786 catch( const std::exception &e )
787 {
789 "inserting " + card.keyword() + " :" + e.what() );
790 // clang-format off
791 #if defined( MXLIB_CATCH_ALL_EXCEPTIONS ) || defined( MXLIB_CATCH_NONALLOC_EXCEPTIONS )
793 #else
794 throw;
795 #endif
796 // clang-format on
797 }
798
799 // Then add to the Map.
800 try
801 {
802 error_t errc = fitsHeaderDetail::mutationHook()( fitsHeaderDetail::mutation::insertAfterMap );
803 if( errc != error_t::noerror )
804 {
805 m_cardList.erase( insertedIt );
806 return internal::mxlib_error_report<verboseT>( errc, "inserting " + card.keyword() );
807 }
808
809 m_cardMap.insert( cardMapValueT( card.keyword(), insertedIt ) );
810 }
811 catch( const std::bad_alloc &e )
812 {
813 m_cardList.erase( insertedIt );
815 "inserting " + card.keyword() + " :" + e.what() );
816 // clang-format off
817 #if defined( MXLIB_CATCH_ALL_EXCEPTIONS )
819 #else
820 throw;
821 #endif
822 // clang-format on
823 }
824 catch( const std::exception &e )
825 {
826 m_cardList.erase( insertedIt );
828 "inserting " + card.keyword() + " :" + e.what() );
829 // clang-format off
830 #if defined( MXLIB_CATCH_ALL_EXCEPTIONS ) || defined( MXLIB_CATCH_NONALLOC_EXCEPTIONS )
832 #else
833 throw;
834 #endif
835 // clang-format on
836 }
837
838 return error_t::noerror;
839}
840
841template <class verboseT>
842template <typename typeT>
844{
846}
847
848template <class verboseT>
850{
852
853 auto mit = m_cardMap.find( keyword );
854
855 // If not found, append it.
856 if( mit == m_cardMap.end() )
857 {
858 error_t errc = append( keyword );
859 if( errc != error_t::noerror )
860 {
861 internal::mxlib_error_report<verboseT>( errc, "appending " + keyword );
862 m_emptyCard.keyword( "" ); // we have to reset this every time b/c someone could have changed it
863 return m_emptyCard;
864 }
865
866 // have to do new search
867 mit = m_cardMap.find( keyword );
868 }
869
870 it = mit->second;
871
872 return *it;
873}
874
875template <class verboseT>
876const fitsHeaderCard<verboseT> &fitsHeader<verboseT>::operator[]( const std::string &keyword ) const
877{
878 auto mit = m_cardMap.find( keyword );
879 if( mit == m_cardMap.end() )
880 {
882 m_emptyCard.keyword( "" ); // we have to reset this every time b/c someone could have changed it
883 return m_emptyCard;
884 }
885
886 headerIteratorT it = mit->second;
887
888 return *it;
889}
890
891/** \addtogroup fits_utils
892 * @{
893 */
894
895/// Convert the values in a std::vector of \ref fitsHeader "fits headers" into a std::vector of values.
896/** Resizes the vector of the appropriate type.
897 *
898 * \returns error_t::noerror on success. \p bad will be empty.
899 * \returns error_t::error if the keyword fails to convert for any header. \p bad will contain the indices of
900 * \p heads for which the extraction of a value for \p keyw failed
901 *
902 * \tparam dataT is the type of the header value
903 * \tparam fitsHeaderT is the fitsHeader type
904 *
905 */
906template <typename dataT, class fitsHeaderT>
907error_t headersToValues( std::vector<dataT> &v, /**< [out] will contain the converted values*/
908 std::vector<size_t> &bad, /**<[out] will contain the indices of any
909 headers that failed conversion */
910 std::vector<fitsHeaderT> &heads, /**< [in] contains the headers */
911 const std::string &keyw /**< [in] contains the keyword designating which
912 value to convert*/
913)
914{
915 v.resize( heads.size() );
916 bad.clear();
917
918 for( size_t i = 0; i < heads.size(); ++i )
919 {
920 error_t errc;
921 v[i] = heads[i][keyw].template value<dataT>( &errc ); // convertFromString<dataT>(heads[i][keyw].value);
922
923 if( errc != error_t::noerror )
924 {
925 bad.push_back( i );
926 v[i] = std::numeric_limits<dataT>::max();
927 }
928 }
929
930 if( bad.size() > 0 )
931 {
932 return error_t::error;
933 }
934 else
935 {
936 return error_t::noerror;
937 }
938}
939
940/// Write the status of a Git repository to HISTORY in a FITS header.
941/**
942 * \param [in,out] head the HISTORY cards will be appended to this header
943 * \param [in] repoName the name of the repository
944 * \param [in] sha1 is the SHA-1 hash string of the repository
945 * \param [in] modified whether or not the repository has been modified after the
946 * commit referred to by sha1
947 */
948template <class fitsHeaderT>
949void fitsHeaderGitStatus( fitsHeaderT &head, const std::string &repoName, const char *sha1, int modified )
950{
951 std::string hist = "Git status for " + repoName + ":";
952 head.append( "", fitsHistoryType(), hist );
953
954 hist = " sha1=";
955 hist += sha1;
956 if( modified )
957 hist += ", modified";
958
959 head.append( "", fitsHistoryType(), hist );
960}
961
962///@}
963
964extern template class fitsHeader<verbose::d>;
965
966} // namespace fits
967} // namespace mx
968
969#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.
error_t append(const fitsHeaderCard< verboseT > &card)
Append a fitsHeaderCard to the end of the header.
error_t append(const std::string &k, const typeT &v, const std::string &c)
Append a card to the end of the header, from the three components of a card.
size_t count(const std::string &keyword)
Get number of cards with a given keyword.
error_t insert_after(headerIteratorT it, const std::string &k, typeT v, const std::string &c)
Insert a card after another card, specifying the card by its components.
error_t append(const std::string &k)
Append a card to the end of the header, with just a keyword.
error_t insert_before(headerIteratorT it, const std::string &k, typeT v)
Insert a card before another card, specifying the card by its components.
cardMapT::value_type cardMapValueT
error_t insert_before(headerIteratorT it, fitsHeaderCard< verboseT > card)
Insert a card before another card.
fitsHeaderCard< verboseT > m_emptyCard
fitsHeaderCard< verboseT > & operator[](const std::string &keyword)
Card access by keyword operator.
error_t append(fitsHeader &head)
Append a fitsHeader to the end of the header.
error_t erase(headerIteratorT it)
Erase card by iterator.
std::unordered_multimap< std::string, headerIteratorT > cardMapT
cardListT::iterator headerIteratorT
const fitsHeaderCard< verboseT > & operator[](const std::string &keyword) const
Card access by keyword operator (const version).
headerIteratorT iterator(const std::string &keyword)
Get iterator pointing to a specific element.
fitsHeader & operator=(const fitsHeader &head)
Assignment operator.
cardMapT::iterator mapIteratorT
error_t erase(const std::string &keyword)
Erase card by keyword.
fitsHeader()
Default c'tor.
error_t insert_before(headerIteratorT it, const std::string &k, typeT v, const std::string &c)
Insert a card before another card, specifying the card by its components.
error_t append(const std::string &k, const typeT &v)
Append a card to the end of the header, from the components of a card with no comment.
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.
headerIteratorT end()
Get iterator to the end of the cards list.
~fitsHeader()
Destructor.
headerIteratorT begin()
Get iterator to the beginning of the cards list.
error_t insert_after(headerIteratorT it, const std::string &k, typeT v)
Insert a card after another card, specifying the card by its components.
fitsHeader(const fitsHeader &head)
Copy constructor.
error_t append(const std::string &k, const char *v, const std::string &c)
Append a string card to the end of the header, from the three components of a card.
std::list< fitsHeaderCard< verboseT > > cardListT
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.
Definition error_t.hpp:27
@ std_exception
An exception was thrown.
Definition error_t.hpp:52
@ std_bad_alloc
A bad allocation exception was thrown.
Definition error_t.hpp:53
@ invalidarg
An argument was invalid.
Definition error_t.hpp:29
@ notfound
An item was not found.
Definition error_t.hpp:34
@ error
A general error has occurred.
Definition error_t.hpp:28
error_t mxlib_error_report(const error_t &code, const std::string &expl, const std::source_location &loc=std::source_location::current())
Print a report to stderr given an mxlib error_t code and explanation and return the code.
Definition error.hpp:331
#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
constexpr int fitsType()
Return the cfitsio constant for a given data type.
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