source: fact/tools/pyscripts/pyfact/fits.h@ 16926

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