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

Last change on this file since 10836 was 10797, checked in by tbretz, 14 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.