source: fact/tools/pyscripts/pyfact/pyfits.h@ 13397

Last change on this file since 13397 was 13355, checked in by neise, 13 years ago
supports table columns type 'A'
  • Property svn:executable set to *
File size: 24.2 KB
Line 
1#ifndef MARS_fits
2#define MARS_fits
3
4#include <stdint.h>
5
6#include <map>
7#include <string>
8#include <sstream>
9#include <algorithm>
10
11#ifdef __EXCEPTIONS
12#include <stdexcept>
13#endif
14
15#ifdef __CINT__
16#define off_t size_t
17#endif
18
19#ifndef __MARS__
20#include <vector>
21#include <iomanip>
22#include <iostream>
23#define gLog cerr
24#define ___err___ ""
25#define ___all___ ""
26#else
27#include "MLog.h"
28#include "MLogManip.h"
29#define ___err___ err
30#define ___all___ all
31#endif
32
33#define HAVE_ZLIB
34#ifdef HAVE_ZLIB
35#include "izstream.h"
36#else
37#include <fstream>
38#define izstream ifstream
39#warning Support for zipped FITS files disabled.
40#endif
41
42namespace std
43{
44
45class fits : public izstream
46{
47public:
48 struct Entry
49 {
50 char type;
51 string value;
52 string comment;
53
54 template<typename T>
55 T Get() const
56 {
57 T t;
58
59 istringstream str(value);
60 str >> t;
61
62 return t;
63 }
64 };
65
66 struct Table
67 {
68 off_t offset;
69
70 string name;
71 size_t bytes_per_row;
72 size_t num_rows;
73 size_t num_cols;
74
75 struct Column
76 {
77 size_t offset;
78 size_t num;
79 size_t size;
80 char type;
81 string unit;
82 };
83
84 typedef map<string, Entry> Keys;
85 typedef map<string, Column> Columns;
86
87 Columns cols;
88 Keys keys;
89
90 //------------ vectors containing the same info as cols and keys------
91 // They are filled at the end of the contstuctor
92 // They are used py python, for retrieving the contents of the maps
93 // I did not find out how to return a map of <string, struct> via ROOT to python
94 // So I use this ugly hack. I guess its no problem, its just ugly
95 //
96 // btw: The way these variables are named, is imho as ugly as possible,
97 // but I will stick to it.
98 // usually a std::map contains key-value pairs
99 // that means the e.g. map<string, Entry> contains keys of type string
100 // and values of type Entry.
101 // but now the map itself was called 'keys', which means one should say:
102 // the keys of 'keys' are of type string
103 // and
104 // the value of 'keys' are of type Entry.
105 // not to forget that:
106 // one field of a value of 'keys' is named 'value'
107
108 // vectors for Keys keys;
109 vector<string> Py_KeyKeys;
110 vector<string> Py_KeyValues;
111 vector<string> Py_KeyComments;
112 vector<char> Py_KeyTypes;
113
114 // vectors for Columns cols;
115 vector<string> Py_ColumnKeys;
116 vector<size_t> Py_ColumnOffsets;
117 vector<size_t> Py_ColumnNums;
118 vector<size_t> Py_ColumnSizes;
119 vector<char> Py_ColumnTypes;
120 vector<string> Py_ColumnUnits;
121 //-end of----- vectors containing the same info as cols and keys------
122
123 string Trim(const string &str, char c=' ') const
124 {
125 // Trim Both leading and trailing spaces
126 const size_t pstart = str.find_first_not_of(c); // Find the first character position after excluding leading blank spaces
127 const size_t pend = str.find_last_not_of(c); // Find the first character position from reverse af
128
129 // if all spaces or empty return an empty string
130 if (string::npos==pstart || string::npos==pend)
131 return string();
132
133 return str.substr(pstart, pend-pstart+1);
134 }
135
136 bool Check(const string &key, char type, const string &value="") const
137 {
138 const Keys::const_iterator it = keys.find(key);
139 if (it==keys.end())
140 {
141 ostringstream str;
142 str << "Key '" << key << "' not found.";
143#ifdef __EXCEPTIONS
144 throw runtime_error(str.str());
145#else
146 gLog << ___err___ << "ERROR - " << str.str() << endl;
147 return false;
148#endif
149 }
150
151 if (it->second.type!=type)
152 {
153 ostringstream str;
154 str << "Wrong type for key '" << key << "': expected " << type << ", found " << it->second.type << ".";
155#ifdef __EXCEPTIONS
156 throw runtime_error(str.str());
157#else
158 gLog << ___err___ << "ERROR - " << str.str() << endl;
159 return false;
160#endif
161 }
162
163 if (!value.empty() && it->second.value!=value)
164 {
165 ostringstream str;
166 str << "Wrong value for key '" << key << "': expected " << value << ", found " << it->second.value << ".";
167#ifdef __EXCEPTIONS
168 throw runtime_error(str.str());
169#else
170 gLog << ___err___ << "ERROR - " << str.str() << endl;
171 return false;
172#endif
173 }
174
175 return true;
176 }
177
178 Keys ParseBlock(const vector<string> &vec) const
179 {
180 map<string,Entry> rc;
181
182 for (unsigned int i=0; i<vec.size(); i++)
183 {
184 const string key = Trim(vec[i].substr(0,8));
185 // Keywords without a value, like COMMENT / HISTORY
186 if (vec[i].substr(8,2)!="= ")
187 continue;
188
189 char type = 0;
190
191 string com;
192 string val = Trim(vec[i].substr(10));
193 if (val[0]=='\'')
194 {
195 // First skip all '' in the string
196 size_t p = 1;
197 while (1)
198 {
199 const size_t pp = val.find_first_of('\'', p);
200 if (pp==string::npos)
201 break;
202
203 p = val[pp+1]=='\'' ? pp+2 : pp+1;
204 }
205
206 // Now find the comment
207 const size_t ppp = val.find_first_of('/', p);
208
209 // Set value, comment and type
210 com = ppp==string::npos ? "" : Trim(val.substr(ppp+1));
211 val = Trim(val.substr(1, p-2));
212 type = 'T';
213 }
214 else
215 {
216 const size_t p = val.find_first_of('/');
217
218 com = Trim(val.substr(p+2));
219 val = Trim(val.substr(0, p));
220
221 if (val.empty() || val.find_first_of('T')!=string::npos || val.find_first_of('F')!=string::npos)
222 type = 'B';
223 else
224 type = val.find_last_of('.')==string::npos ? 'I' : 'F';
225 }
226
227 const Entry e = { type, val, com };
228 rc[key] = e;
229 }
230
231 return rc;
232 }
233
234 Table() : offset(0) { }
235 Table(const vector<string> &vec, off_t off) :
236 offset(off), keys(ParseBlock(vec))
237 {
238 if (!Check("XTENSION", 'T', "BINTABLE") ||
239 !Check("NAXIS", 'I', "2") ||
240 !Check("BITPIX", 'I', "8") ||
241 !Check("PCOUNT", 'I', "0") ||
242 !Check("GCOUNT", 'I', "1") ||
243 !Check("EXTNAME", 'T') ||
244 !Check("NAXIS1", 'I') ||
245 !Check("NAXIS2", 'I') ||
246 !Check("TFIELDS", 'I'))
247 return;
248
249 bytes_per_row = Get<size_t>("NAXIS1");
250 num_rows = Get<size_t>("NAXIS2");
251 num_cols = Get<size_t>("TFIELDS");
252
253 size_t bytes = 0;
254 for (size_t i=1; i<=num_cols; i++)
255 {
256 ostringstream num;
257 num << i;
258
259 if (!Check("TTYPE"+num.str(), 'T') ||
260 !Check("TFORM"+num.str(), 'T'))
261 return;
262
263 const string id = Get<string>("TTYPE"+num.str());
264 const string fmt = Get<string>("TFORM"+num.str());
265 const string unit = Get<string>("TUNIT"+num.str(), "");
266
267 istringstream sin(fmt);
268 int n = 0;
269 sin >> n;
270 if (!sin)
271 n = 1;
272
273 const char type = fmt[fmt.length()-1];
274
275 size_t size = 0;
276 switch (type)
277 {
278 // We could use negative values to mark floats
279 // otheriwse we could just cast them to int64_t?
280 case 'L': size = 1; break; // logical
281 // case 'X': size = n; break; // bits (n=number of bytes needed to contain all bits)
282 case 'A':
283 case 'B': size = 1; break; // byte
284 case 'I': size = 2; break; // short
285 case 'J': size = 4; break; // int
286 case 'K': size = 8; break; // long long
287 case 'E': size = 4; break; // float
288 case 'D': size = 8; break; // double
289 // case 'C': size = 8; break; // complex float
290 // case 'M': size = 16; break; // complex double
291 // case 'P': size = 8; break; // array descriptor (32bit)
292 // case 'Q': size = 16; break; // array descriptor (64bit)
293 default:
294 {
295 ostringstream str;
296 str << "FITS format TFORM='" << fmt << "' not yet supported.";
297#ifdef __EXCEPTIONS
298 throw runtime_error(str.str());
299#else
300 gLog << ___err___ << "ERROR - " << str.str() << endl;
301 return;
302#endif
303 }
304 }
305
306 const Table::Column col = { bytes, n, size, type, unit };
307
308 cols[id] = col;
309 bytes += n*size;
310 }
311
312 if (bytes!=bytes_per_row)
313 {
314#ifdef __EXCEPTIONS
315 throw runtime_error("Column size mismatch");
316#else
317 gLog << ___err___ << "ERROR - Column size mismatch" << endl;
318 return;
319#endif
320 }
321
322 name = Get<string>("EXTNAME");
323
324
325 Fill_Py_Vectors();
326 }
327
328 void Fill_Py_Vectors(void)
329 {
330 // Fill the Py_ vectors see line: 90ff
331 // vectors for Keys keys;
332 for (Keys::const_iterator it=keys.begin(); it!=keys.end(); it++)
333 {
334 Py_KeyKeys.push_back(it->first);
335 Py_KeyValues.push_back(it->second.value);
336 Py_KeyComments.push_back(it->second.comment);
337 Py_KeyTypes.push_back(it->second.type);
338 }
339
340 // vectors for Columns cols;
341 for (Columns::const_iterator it=cols.begin(); it!=cols.end(); it++)
342 {
343 Py_ColumnKeys.push_back(it->first);
344 Py_ColumnTypes.push_back(it->second.type);
345 Py_ColumnOffsets.push_back(it->second.offset);
346 Py_ColumnNums.push_back(it->second.num);
347 Py_ColumnSizes.push_back(it->second.size);
348 Py_ColumnUnits.push_back(it->second.unit);
349 }
350 }
351
352
353 void PrintKeys(bool display_all=false) const
354 {
355 for (Keys::const_iterator it=keys.begin(); it!=keys.end(); it++)
356 {
357 if (!display_all &&
358 (it->first.substr(0, 6)=="TTYPE" ||
359 it->first.substr(0, 6)=="TFORM" ||
360 it->first.substr(0, 6)=="TUNIT" ||
361 it->first=="TFIELDS" ||
362 it->first=="XTENSION" ||
363 it->first=="NAXIS" ||
364 it->first=="BITPIX" ||
365 it->first=="PCOUNT" ||
366 it->first=="GCOUNT")
367 )
368 continue;
369
370 gLog << ___all___ << setw(2) << it->second.type << '|' << it->first << '=' << it->second.value << '/' << it->second.comment << '|' << endl;
371 }
372 }
373
374 void PrintColumns() const
375 {
376 typedef map<pair<size_t, string>, Column> Sorted;
377
378 Sorted sorted;
379
380 for (Columns::const_iterator it=cols.begin(); it!=cols.end(); it++)
381 sorted[make_pair(it->second.offset, it->first)] = it->second;
382
383 for (Sorted::const_iterator it=sorted.begin(); it!=sorted.end(); it++)
384 {
385 gLog << ___all___ << setw(6) << it->second.offset << "| ";
386 gLog << it->second.num << 'x';
387 switch (it->second.type)
388 {
389 case 'L': gLog << "bool(8)"; break;
390 case 'B': gLog << "byte(8)"; break;
391 case 'I': gLog << "short(16)"; break;
392 case 'J': gLog << "int(32)"; break;
393 case 'K': gLog << "int(64)"; break;
394 case 'E': gLog << "float(32)"; break;
395 case 'D': gLog << "double(64)"; break;
396 }
397 gLog << ": " << it->first.second << " [" << it->second.unit << "]" << endl;
398 }
399 }
400
401 operator bool() const { return !name.empty(); }
402
403 bool HasKey(const string &key) const
404 {
405 return keys.find(key)!=keys.end();
406 }
407
408 // Values of keys are always signed
409 template<typename T>
410 T Get(const string &key) const
411 {
412 const map<string,Entry>::const_iterator it = keys.find(key);
413 if (it==keys.end())
414 {
415 ostringstream str;
416 str << "Key '" << key << "' not found." << endl;
417#ifdef __EXCEPTIONS
418 throw runtime_error(str.str());
419#else
420 gLog << ___err___ << "ERROR - " << str.str() << endl;
421 return 0;
422#endif
423 }
424 return it->second.Get<T>();
425 }
426
427 // Values of keys are always signed
428 template<typename T>
429 T Get(const string &key, const string &deflt) const
430 {
431 const map<string,Entry>::const_iterator it = keys.find(key);
432 return it==keys.end() ? deflt :it->second.Get<T>();
433 }
434
435 size_t GetN(const string &key) const
436 {
437 const Columns::const_iterator it = cols.find(key);
438 if (it==cols.end()){
439 ostringstream str;
440 str << "Key '" << key << "' not found." << endl;
441 str << "Possible keys are:" << endl;
442 for ( Columns::const_iterator it=cols.begin() ; it != cols.end(); ++it){
443 str << it->first << endl;
444 }
445#ifdef __EXCEPTIONS
446 throw runtime_error(str.str());
447#else
448 gLog << ___err___ << "ERROR - " << str.str() << endl;
449 return 0;
450#endif
451 }
452 return it==cols.end() ? 0 : it->second.num;
453 }
454 };
455
456private:
457 Table fTable;
458
459 typedef pair<void*, Table::Column> Address;
460 typedef vector<Address> Addresses;
461 //map<void*, Table::Column> fAddresses;
462 Addresses fAddresses;
463
464 vector<char> fBufferRow;
465 vector<char> fBufferDat;
466
467 size_t fRow;
468
469 vector<string> ReadBlock(vector<string> &vec)
470 {
471 bool endtag = false;
472 for (int i=0; i<36; i++)
473 {
474 char c[81];
475 c[80] = 0;
476 read(c, 80);
477 if (!good())
478 break;
479
480 if (c[0]==0)
481 return vector<string>();
482
483 string str(c);
484
485// if (!str.empty())
486// cout << setw(2) << i << "|" << str << "|" << (endtag?'-':'+') << endl;
487
488 if (str=="END ")
489 {
490 endtag = true;
491
492 // Make sure that no empty vector is returned
493 if (vec.size()%36==0)
494 vec.push_back(string("END = '' / "));
495 }
496
497 if (endtag)
498 continue;
499
500 vec.push_back(str);
501 }
502
503 return vec;
504 }
505
506 string Compile(const string &key, int16_t i=-1)
507 {
508 if (i<0)
509 return key;
510
511 ostringstream str;
512 str << key << i;
513 return str.str();
514 }
515
516public:
517 fits(const string &fname) : izstream(fname.c_str())
518 {
519 char simple[10];
520 read(simple, 10);
521 if (!good())
522 return;
523
524 if (memcmp(simple, "SIMPLE = ", 10))
525 {
526 clear(rdstate()|ios::badbit);
527#ifdef __EXCEPTIONS
528 throw runtime_error("File is not a FITS file.");
529#else
530 gLog << ___err___ << "ERROR - File is not a FITS file." << endl;
531 return;
532#endif
533 }
534
535 seekg(0);
536
537 while (good())
538 {
539 vector<string> block;
540 while (1)
541 {
542 // FIXME: Set limit on memory consumption
543 ReadBlock(block);
544 if (!good())
545 {
546 clear(rdstate()|ios::badbit);
547#ifdef __EXCEPTIONS
548 throw runtime_error("FITS file corrupted.");
549#else
550 gLog << ___err___ << "ERROR - FITS file corrupted." << endl;
551 return;
552#endif
553 }
554
555 if (block.size()%36)
556 break;
557 }
558
559 if (block.size()==0)
560 break;
561
562 if (block[0].substr(0, 9)=="SIMPLE =")
563 continue;
564
565 if (block[0].substr(0, 9)=="XTENSION=")
566 {
567 // FIXME: Check for table name
568
569 fTable = Table(block, tellg());
570 fRow = (size_t)-1;
571
572 //fTable.PrintKeys();
573
574 if (!fTable)
575 {
576 clear(rdstate()|ios::badbit);
577 return;
578 }
579
580 fBufferRow.resize(fTable.bytes_per_row);
581 fBufferDat.resize(fTable.bytes_per_row);
582
583 /*
584 // Next table should start at:
585 const size_t size = fTable.bytes_per_row*fTable.num_rows;
586 const size_t blks = size/(36*80);
587 const size_t rest = size%(36*80);
588
589 seekg((blks+(rest>0?1:0))*(36*80), ios::cur);
590 if (!good())
591 gLog << ___err___ << "File seems to be incomplete (less data than expected from header)." << endl;
592
593 fRow = fTable.num_rows;
594 */
595
596 break;
597 }
598 }
599 }
600
601 void ReadRow(size_t row)
602 {
603 // if (row!=fRow+1) // Fast seeking is ensured by izstream
604 seekg(fTable.offset+row*fTable.bytes_per_row);
605
606 fRow = row;
607
608 read(fBufferRow.data(), fBufferRow.size());
609 //fin.clear(fin.rdstate()&~ios::eofbit);
610 }
611
612 template<size_t N>
613 void revcpy(char *dest, const char *src, int num)
614 {
615 const char *pend = src + num*N;
616 for (const char *ptr = src; ptr<pend; ptr+=N, dest+=N)
617 reverse_copy(ptr, ptr+N, dest);
618 }
619
620 bool GetRow(size_t row)
621 {
622 ReadRow(row);
623 if (!good())
624 return good();
625
626 for (Addresses::const_iterator it=fAddresses.begin(); it!=fAddresses.end(); it++)
627 {
628 const Table::Column &c = it->second;
629
630 const char *src = fBufferRow.data() + c.offset;
631 char *dest = reinterpret_cast<char*>(it->first);
632
633 // Let the compiler do some optimization by
634 // knowing the we only have 1, 2, 4 and 8
635 switch (c.size)
636 {
637 case 1: memcpy (dest, src, c.num*c.size); break;
638 case 2: revcpy<2>(dest, src, c.num); break;
639 case 4: revcpy<4>(dest, src, c.num); break;
640 case 8: revcpy<8>(dest, src, c.num); break;
641 }
642 }
643
644 return good();
645 }
646
647 bool GetNextRow()
648 {
649 return GetRow(fRow+1);
650 }
651
652 bool SkipNextRow()
653 {
654 seekg(fTable.offset+(++fRow)*fTable.bytes_per_row);
655 return good();
656 }
657
658 static bool Compare(const Address &p1, const Address &p2)
659 {
660 return p1.first>p2.first;
661 }
662
663 template<typename T>
664 bool SetPtrAddress(const string &name, T *ptr, size_t cnt)
665 {
666 if (fTable.cols.count(name)==0)
667 {
668 ostringstream str;
669 str <<"SetPtrAddress('" << name << "') - Column not found." << endl;
670#ifdef __EXCEPTIONS
671 throw runtime_error(str.str());
672#else
673 gLog << ___err___ << "ERROR - " << str.str() << endl;
674 return false;
675#endif
676 }
677
678 if (sizeof(T)!=fTable.cols[name].size)
679 {
680 ostringstream str;
681 str << "SetPtrAddress('" << name << "') - Element size mismatch: expected "
682 << fTable.cols[name].size << " from header, got " << sizeof(T) << endl;
683#ifdef __EXCEPTIONS
684 throw runtime_error(str.str());
685#else
686 gLog << ___err___ << "ERROR - " << str.str() << endl;
687 return false;
688#endif
689 }
690
691 if (cnt!=fTable.cols[name].num)
692 {
693 ostringstream str;
694 str << "SetPtrAddress('" << name << "') - Element count mismatch: expected "
695 << fTable.cols[name].num << " from header, got " << cnt << endl;
696#ifdef __EXCEPTIONS
697 throw runtime_error(str.str());
698#else
699 gLog << ___err___ << "ERROR - " << str.str() << endl;
700 return false;
701#endif
702 }
703
704 // if (fAddresses.count(ptr)>0)
705 // gLog << warn << "SetPtrAddress('" << name << "') - Pointer " << ptr << " already assigned." << endl;
706
707 //fAddresses[ptr] = fTable.cols[name];
708 fAddresses.push_back(make_pair(ptr, fTable.cols[name]));
709 sort(fAddresses.begin(), fAddresses.end(), Compare);
710 return true;
711 }
712
713 template<class T>
714 bool SetRefAddress(const string &name, T &ptr)
715 {
716 return SetPtrAddress(name, &ptr, sizeof(ptr)/sizeof(T));
717 }
718
719 template<typename T>
720 bool SetVecAddress(const string &name, vector<T> &vec)
721 {
722 return SetPtrAddress(name, vec.data(), vec.size());
723 }
724
725 template<typename T>
726 T Get(const string &key) const
727 {
728 return fTable.Get<T>(key);
729 }
730
731 template<typename T>
732 T Get(const string &key, const string &deflt) const
733 {
734 return fTable.Get<T>(key, deflt);
735 }
736
737 bool SetPtrAddress(const string &name, void *ptr)
738 {
739 if (fTable.cols.count(name)==0)
740 {
741 ostringstream str;
742 str <<"SetPtrAddress('" << name << "') - Column not found." << endl;
743#ifdef __EXCEPTIONS
744 throw runtime_error(str.str());
745#else
746 gLog << ___err___ << "ERROR - " << str.str() << endl;
747 return false;
748#endif
749 }
750
751 // if (fAddresses.count(ptr)>0)
752 // gLog << warn << "SetPtrAddress('" << name << "') - Pointer " << ptr << " already assigned." << endl;
753
754 //fAddresses[ptr] = fTable.cols[name];
755 fAddresses.push_back(make_pair(ptr, fTable.cols[name]));
756 sort(fAddresses.begin(), fAddresses.end(), Compare);
757 return true;
758 }
759
760 bool HasKey(const string &key) const { return fTable.HasKey(key); }
761 int64_t GetInt(const string &key) const { return fTable.Get<int64_t>(key); }
762 uint64_t GetUInt(const string &key) const { return fTable.Get<uint64_t>(key); }
763 double GetFloat(const string &key) const { return fTable.Get<double>(key); }
764 string GetStr(const string &key) const { return fTable.Get<string>(key); }
765
766 size_t GetN(const string &key) const
767 {
768 return fTable.GetN(key);
769 }
770
771 size_t GetNumRows() const { return fTable.num_rows; }
772 size_t GetRow() const { return fRow; }
773
774 operator bool() const { return fTable && fTable.offset!=0; }
775
776 void PrintKeys() const { fTable.PrintKeys(); }
777 void PrintColumns() const { fTable.PrintColumns(); }
778
779 // 'Wrappers' for the Table's Getters of the Py Vectors
780 vector<string> GetPy_KeyKeys() {return fTable.Py_KeyKeys; }
781 vector<string> GetPy_KeyValues() {return fTable.Py_KeyValues; }
782 vector<string> GetPy_KeyComments() {return fTable.Py_KeyComments; }
783 vector<char> GetPy_KeyTypes() {return fTable.Py_KeyTypes; }
784
785 vector<string> GetPy_ColumnKeys() {return fTable.Py_ColumnKeys; }
786 vector<size_t> GetPy_ColumnOffsets() {return fTable.Py_ColumnOffsets; }
787 vector<size_t> GetPy_ColumnNums() {return fTable.Py_ColumnNums; }
788 vector<size_t> GetPy_ColumnSizes() {return fTable.Py_ColumnSizes;}
789 vector<char> GetPy_ColumnTypes() {return fTable.Py_ColumnTypes;}
790 vector<string> GetPy_ColumnUnits() {return fTable.Py_ColumnUnits;}
791
792};
793
794};
795#endif
Note: See TracBrowser for help on using the repository browser.