source: trunk/FACT++/src/fitsdump.cc @ 10797

Last change on this file since 10797 was 10797, checked in by tbretz, 9 years ago
Simplified program options handling.
File size: 16.9 KB
Line 
1//****************************************************************
2/** @class FitsDumper
3
4  @brief Dumps contents of fits tables to stdout or a file
5
6 */
7 //****************************************************************
8#include "Configuration.h"
9
10#include <map>
11#include <fstream>
12
13#include <CCfits/CCfits>
14
15using namespace std;
16
17class FitsDumper
18{
19
20public:
21    FitsDumper();
22    ~FitsDumper();
23
24private:
25   
26    CCfits::FITS  *fFile;                 /// FITS pointer
27    CCfits::Table *fTable;                /// Table pointer
28    map<string, CCfits::Column*> fColMap; /// map between the column names and their CCfits objects
29
30    // Convert CCfits::ValueType into a human readable string
31    string ValueTypeToStr(CCfits::ValueType type) const;
32
33    // Convert CCfits::ValueType into a number of associated bytes
34    int    ValueTypeToSize(CCfits::ValueType type) const;
35
36    /// Calculate the buffer size required to read a row of the fits table, as well as the offsets to each column
37    vector<int> CalculateOffsets() const;
38
39    template<class T>
40        void Write(ostream &out, const unsigned char* &ptr) const;
41
42    /// Write a single row of the selected data
43    int  WriteRow(ostream &, const vector<string> &, const vector<int> &, unsigned char *) const;
44
45    bool OpenFile(const string &);        /// Open a file
46    bool OpenTable(const string &);       /// Open a table
47
48    /// Lists all columns of an open file
49    void List();                         
50    void ListHeader();
51    void ListKeywords(ostream &);
52
53    /// Perform the dumping, based on the current dump list
54    bool Dump(const string &, const vector<string> &list, int);
55
56
57public:
58    ///Configures the fitsLoader from the config file and/or command arguments.
59    int ExecConfig(Configuration& conf);
60};
61
62// --------------------------------------------------------------------------
63//
64//! Constructor
65//! @param out
66//!        the ostream where to redirect the outputs
67//
68FitsDumper::FitsDumper() : fFile(0), fTable(0)
69{
70}
71
72// --------------------------------------------------------------------------
73//
74//! Destructor
75//
76FitsDumper::~FitsDumper()
77{
78    if (fFile)
79        delete fFile;
80}
81
82
83string FitsDumper::ValueTypeToStr(CCfits::ValueType type) const
84{
85    switch (type)
86    {
87    case CCfits::Tbyte:     return "uint8_t";
88    case CCfits::Tushort:   return "uint16_t";
89    case CCfits::Tshort:    return "int16_t";
90    case CCfits::Tuint:     return "uint32_t";
91    case CCfits::Tint:      return "int32_t";
92    case CCfits::Tulong:    return "uint32_t";
93    case CCfits::Tlong:     return "int32_t";
94    case CCfits::Tlonglong: return "int64_t";
95    case CCfits::Tfloat:    return "float";
96    case CCfits::Tdouble:   return "double";
97
98    default:
99        return "unknwown";
100    }
101}
102
103int FitsDumper::ValueTypeToSize(CCfits::ValueType type) const
104{
105    switch (type)
106    {
107    case CCfits::Tbyte:     return sizeof(uint8_t);
108    case CCfits::Tushort:
109    case CCfits::Tshort:    return sizeof(uint16_t);
110    case CCfits::Tuint:
111    case CCfits::Tint:
112    case CCfits::Tulong:
113    case CCfits::Tlong:     return sizeof(uint32_t);
114    case CCfits::Tlonglong: return sizeof(uint64_t);
115    case CCfits::Tfloat:    return sizeof(float);
116    case CCfits::Tdouble:   return sizeof(double);
117
118    default:
119        return 0;
120    }
121}
122
123template<class T>
124void FitsDumper::Write(ostream &out, const unsigned char* &ptr) const
125{
126    T t;
127    reverse_copy(ptr, ptr+sizeof(T), reinterpret_cast<unsigned char*>(&t));
128    out << t;
129
130    ptr += sizeof(T);
131}
132
133
134// --------------------------------------------------------------------------
135//
136//! Writes a single row of the selected FITS data to the output file.
137//!
138//! Data type \b not yet implemented:
139//!   CCfits::Tnull, CCfits::Tbit, CCfits::Tlogical, CCfits::Tstring,
140//!   CCfits::Tcomplex, CCfits::Tdblcomplex, CCfits::VTbit,
141//!   CCfits::VTbyte, CCfits::VTlogical, CCfits::VTushort,
142//!   CCfits::VTshort, CCfits::VTuint, CCfits::VTint, CCfits::VTulong,
143//!   CCfits::VTlong, CCfits::VTlonglong, CCfits::VTfloat,
144//!   CCfits::VTdouble, CCfits::VTcomplex, CCfits::VTdblcomplex
145//!
146//! @param offsets
147//!         a vector containing the offsets to the columns (in bytes)
148//! @param targetFile
149//!         the ofstream where to write to
150//! @param fitsBuffer
151//!         the memory were the row has been loaded by cfitsio
152//
153int FitsDumper::WriteRow(ostream &out, const vector<string> &list, const vector<int> &offsets, unsigned char* fitsBuffer) const
154{
155    int cnt = 0;
156
157    for (vector<string>::const_iterator it=list.begin(); it!=list.end(); it++)
158    {
159        // Loop over all columns in our list of requested columns
160        const CCfits::Column *col = fColMap.find(*it)->second;
161
162        // CCfits starts counting at 1 not 0
163        const int offset = offsets[col->index()-1];
164
165        // Get the pointer to the array which we are supposed to print
166        const unsigned char *ptr = fitsBuffer + offset;
167
168        // Loop over all array entries
169        for (int width=0; width<col->width(); width++)
170        {
171            switch (col->type())
172            {
173            case CCfits::Tbyte:     Write<uint8_t> (out, ptr); break;
174            case CCfits::Tushort:   Write<uint16_t>(out, ptr); break;
175            case CCfits::Tuint:
176            case CCfits::Tulong:    Write<uint32_t>(out, ptr); break;
177            case CCfits::Tshort:    Write<int16_t> (out, ptr); break;
178            case CCfits::Tint:
179            case CCfits::Tlong:     Write<int32_t> (out, ptr); break;
180            case CCfits::Tlonglong: Write<int64_t> (out, ptr); break;
181            case CCfits::Tfloat:    Write<float>   (out, ptr); break;
182            case CCfits::Tdouble:   Write<double>  (out, ptr); break;
183
184            default:
185                cerr << "Data type not implemented yet." << endl;
186                return 0;
187            }
188
189            out << " ";
190            cnt++;
191
192            if (out.fail())
193            {
194                cerr << "ERROR - writing output: " << strerror(errno) << endl;
195                return 0;
196            }
197        }
198    }
199
200    if (cnt>0)
201        out << endl;
202
203    return cnt;
204}
205
206// --------------------------------------------------------------------------
207//
208//! Calculates the required buffer size for reading one row of the current
209//! table. Also calculates the offsets to all the columns
210//
211vector<int> FitsDumper::CalculateOffsets() const
212{
213    map<int,int> sizes;
214
215    for (map<string, CCfits::Column*>::const_iterator it=fColMap.begin();
216         it!=fColMap.end(); it++)
217    {
218        const int &width = it->second->width();
219        const int &idx   = it->second->index();
220
221        const int size = ValueTypeToSize(it->second->type());
222        if (size==0)
223        {
224            cerr << "Data type " << (int)it->second->type() << " not implemented yet." << endl;
225            return vector<int>();
226        }
227
228        sizes[idx] = size*width;
229    }
230
231    //calculate the offsets in the vector.
232    vector<int> result(1, 0);
233
234    int size = 0;
235    int idx  = 0;
236
237    for (map<int,int>::const_iterator it=sizes.begin(); it!=sizes.end(); it++)
238    {
239        size += it->second;
240        result.push_back(size);
241
242        if (it->first == ++idx)
243            continue;
244
245        cerr << "Expected index " << idx << ", but found " << it->first << endl;
246        return vector<int>();
247    }
248
249    return result;
250}
251
252// --------------------------------------------------------------------------
253//
254//! Loads the fits file based on the current parameters
255//
256bool FitsDumper::OpenFile(const string &filename)
257{
258    if (fFile)
259        delete fFile;
260
261    ostringstream str;
262    try
263    {
264        fFile = new CCfits::FITS(filename);
265    }
266    catch (CCfits::FitsException e)
267    {
268        cerr << "Could not open FITS file " << filename << " reason: " << e.message() << endl;
269        return false;
270    }
271
272    return true;
273}
274
275bool FitsDumper::OpenTable(const string &tablename)
276{
277    if (!fFile)
278    {
279        cerr << "No file open." << endl;
280        return false;
281    }
282
283    const multimap< string, CCfits::ExtHDU * > extMap = fFile->extension();
284    if (extMap.find(tablename) == extMap.end())
285    {
286        cerr << "Table '" << tablename << "' not found." << endl;
287        return false;
288    }
289
290    fTable  = dynamic_cast<CCfits::Table*>(extMap.find(tablename)->second);
291    if (!fTable)
292    {
293        cerr << "Object '" << tablename << "' returned not a CCfits::Table." << endl;
294        return false;
295    }
296
297    fColMap = fTable->column();
298
299    fTable->makeThisCurrent();
300
301    fTable->getComments();
302    fTable->getHistory();
303    fTable->readAllKeys();
304
305    return true;
306}
307
308class MyColumn : public CCfits::Column
309{
310public:
311    const string &comment() const { return CCfits::Column::comment(); }
312};
313
314void FitsDumper::List()
315{
316    if (!fFile)
317    {
318        cerr << "No file open." << endl;
319        return;
320    }
321
322    cout << "\nFile: " << fFile->name() << "\n";
323
324    const multimap< string, CCfits::ExtHDU * > extMap = fFile->extension();
325    for (std::multimap<string, CCfits::ExtHDU*>::const_iterator it=extMap.begin(); it != extMap.end(); it++)
326    {
327
328        CCfits::Table *table = dynamic_cast<CCfits::Table*>(extMap.find(it->first)->second);
329
330        cout << " " << it->first << " [" << table->rows() << "]\n";
331
332        const map<string, CCfits::Column*> &cols = table->column();
333
334        for (map<string, CCfits::Column*>::const_iterator ic=cols.begin();
335             ic != cols.end(); ic++)
336        {
337            const MyColumn *col = static_cast<MyColumn*>(ic->second);
338
339            cout << "   " << col->name() << "[" << col->width() << "] * " << col->scale() << " (" << col->unit() << ":" <<  ValueTypeToStr(col->type())<<  ") " << col->comment() << "\n";
340            /*
341             inline size_t Column::repeat () const
342             inline bool   Column::varLength () const
343             inline double Column::zero () const
344             inline const  String& Column::display () const
345             inline const  String& Column::dimen () const
346             inline const  String& Column::TBCOL ()
347             inline const  String& Column::TTYPE ()
348             inline const  String& Column::TFORM ()
349             inline const  String& Column::TDISP ()
350             inline const  String& Column::TZERO ()
351             inline const  String& Column::TDIM  ()
352             inline const  String& Column::TNULL ()
353             inline const  String& Column::TLMIN ()
354             inline const  String& Column::TLMAX ()
355             inline const  String& Column::TDMAX ()
356             inline const  String& Column::TDMIN ()
357            */
358        }
359        cout << '\n';
360    }
361    cout << flush;
362}
363
364class MyKeyword : public CCfits::Keyword
365{
366public:
367    CCfits::ValueType keytype() const { return CCfits::Keyword::keytype(); }
368};
369
370void FitsDumper::ListKeywords(ostream &out)
371{
372    map<string,CCfits::Keyword*> keys = fTable->keyWord();
373    for (map<string,CCfits::Keyword*>::const_iterator it=keys.begin();
374         it!=keys.end(); it++)
375    {
376        string str;
377        double d;
378
379        const MyKeyword *kw = static_cast<MyKeyword*>(it->second);
380        kw->keytype();
381        out << "## " << setw(8) << kw->name() << "='";
382        if (kw->keytype()==16)
383            out << kw->value(str);
384        if (kw->keytype()==82)
385            out << kw->value(d);
386        out << "' \t(" << kw->comment() << ")" << endl;
387    }
388}
389
390void FitsDumper::ListHeader()
391{
392    if (!fTable)
393    {
394        cerr << "No table open." << endl;
395        return;
396    }
397
398    cout << "\nTable: " << fTable->name() << " (rows=" << fTable->rows() << ")\n";
399    if (!fTable->comment().empty())
400        cout << "Comment: \t" << fTable->comment() << '\n';
401    if (!fTable->history().empty())
402        cout << "History: \t" << fTable->history() << '\n';
403
404    ListKeywords(cout);
405    cout << endl;
406}
407
408
409// --------------------------------------------------------------------------
410//
411//! Perform the actual dump, based on the current parameters
412//
413bool FitsDumper::Dump(const string &filename, const vector<string> &list, int precision)
414{
415    for (vector<string>::const_iterator it=list.begin(); it!=list.end(); it++)
416        if (fColMap.find(*it) == fColMap.end())
417        {
418            cerr << "WARNING - Column '" << *it << "' not found in table." << endl;
419            return false;
420        }
421
422
423    const vector<int> offsets = CalculateOffsets();
424    if (offsets.size()==0)
425        return false;
426
427    const int size = offsets[offsets.size()-1];
428
429    ofstream out(filename=="-"?"/dev/stdout":filename);
430    if (!out)
431    {
432        cerr << "Cannot open file " << filename << ": " << strerror(errno) << endl;
433        return false;
434    }
435
436    out.precision(precision);
437
438    out << "## --------------------------------------------------------------------------\n";
439    if (filename!="-")
440        out << "## File:    \t" << filename << '\n';
441    out << "## Table:   \t" << fTable->name() << '\n';
442    if (!fTable->comment().empty())
443        out << "## Comment: \t" << fTable->comment() << '\n';
444    if (!fTable->history().empty())
445        out << "## History: \t" << fTable->history() << '\n';
446    out << "## NumRows: \t" << fTable->rows() << '\n';
447    out << "## --------------------------------------------------------------------------\n";
448    ListKeywords(out);
449    out << "## --------------------------------------------------------------------------\n";
450    out << "#\n";
451    for (vector<string>::const_iterator it=list.begin(); it!=list.end(); it++)
452    {
453        const MyColumn *col = static_cast<MyColumn*>(fTable->column()[*it]);
454        out << "# " << col->name() << "[" << col->width() << "]: " << col->unit();
455        if (!col->comment().empty())
456            out << " (" <<col->comment() << ")";
457        out << '\n';
458    }
459    out << "#" << endl;
460
461    unsigned char* fitsBuffer = new unsigned char[size];
462
463    int status = 0;
464    for (int i=1; i<=fTable->rows(); i++)
465    {
466        fits_read_tblbytes(fFile->fitsPointer(), i, 1, size, fitsBuffer, &status);
467        if (status)
468        {
469            cerr << "An error occurred while reading fits row #" << i << " error code: " << status << endl;
470            break;
471        }
472        WriteRow(out, list, offsets, fitsBuffer);
473    }
474    delete[] fitsBuffer;
475
476    return status;
477}
478
479// --------------------------------------------------------------------------
480//
481//! Retrieves the configuration parameters
482//! @param conf
483//!             the configuration object
484//
485int FitsDumper::ExecConfig(Configuration& conf)
486{
487    if (conf.Has("fitsfile"))
488    {
489        if (!OpenFile(conf.Get<string>("fitsfile")))
490            return -1;
491    }
492
493    if (conf.Get<bool>("list"))
494        List();
495
496    if (conf.Has("tablename"))
497    {
498        if (!OpenTable(conf.Get<string>("tablename")))
499            return -1;
500    }
501
502    if (conf.Get<bool>("header"))
503        ListHeader();
504
505    if (conf.Get<bool>("header") || conf.Get<bool>("list"))
506        return 1;
507
508    if (conf.Has("outfile"))
509    {
510        if (!Dump(conf.Get<string>("outfile"),
511                  conf.Get<vector<string>>("col"),
512                  conf.Get<int>("precision")))
513            return -1;
514    }
515
516    return 0;
517}
518
519void PrintUsage()
520{
521    cout <<
522        "fitsdump is a tool to dump data from a FITS table as ascii.\n"
523        "\n"
524        "Usage: fitsdump [OPTIONS] fitsfile col col ... \n"
525        "  or:  fitsdump [OPTIONS]\n";
526    cout << endl;
527}
528
529void PrintHelp()
530{
531    //
532}
533
534void SetupConfiguration(Configuration& conf)
535{
536    po::options_description configs("Fitsdump options");
537    configs.add_options()
538        ("fitsfile,f",  var<string>()
539#if BOOST_VERSION >= 104200
540         ->required()
541#endif
542                                                  , "Name of FITS file")
543        ("tablename,t", var<string>("DATA")
544#if BOOST_VERSION >= 104200
545         ->required()
546#endif
547                                                  , "Name of input table")
548        ("col,c",       vars<string>(),             "List of columns to dump")
549        ("outfile,o",   var<string>("/dev/stdout"), "Name of output file (-:/dev/stdout)")
550        ("precision,p", var<int>(20),               "Precision of ofstream")
551        ("list,l",      po_switch(),                "List all tables and columns in file")
552        ("header,h",    po_switch(),                "Dump header of given table")
553        ;
554
555    po::positional_options_description p;
556    p.add("fitsfile",   1); // The first positional options
557    p.add("col",       -1); // All others
558
559    conf.AddOptions(configs);
560    conf.SetArgumentPositions(p);
561}
562
563int main(int argc, const char** argv)
564{
565    Configuration conf(argv[0]);
566    conf.SetPrintUsage(PrintUsage);
567    SetupConfiguration(conf);
568
569    po::variables_map vm;
570    try
571    {
572        vm = conf.Parse(argc, argv);
573    }
574#if BOOST_VERSION > 104000
575    catch (po::multiple_occurrences &e)
576    {
577        cerr << "Program options invalid due to: " << e.what() << " of option '" << e.get_option_name() << "'." << endl;
578        return -1;
579    }
580#endif
581    catch (exception& e)
582    {
583        cerr << "Program options invalid due to: " << e.what() << endl;
584        return -1;
585    }
586
587    if (conf.HasVersion() || conf.HasPrint())
588        return -1;
589
590    if (conf.HasHelp())
591    {
592        PrintHelp();
593        return -1;
594    }
595
596    FitsDumper loader;
597    return loader.ExecConfig(conf);
598}
Note: See TracBrowser for help on using the repository browser.