source: trunk/FACT++/src/FitsFile.cc@ 20086

Last change on this file since 20086 was 19389, checked in by tbretz, 6 years ago
As NIGHT is bound to the observatory location, write a new keywork OBSERVAT which corresponds to PRESET_OBSERVATORY.
File size: 15.0 KB
Line 
1// **************************************************************************
2/** @class FitsFile
3
4@brief FITS writter for the FACT project.
5
6The FactFits class is able to open, manage and update FITS files.
7
8The file columns should be given to the class before the file is openned. Once
9a file has been created, the structure of its columns cannot be changed. Only
10row can be added.
11
12This class relies on the CCfits and CFitsIO packages.
13
14*/
15// **************************************************************************
16#include "FitsFile.h"
17
18#ifdef HAVE_NOVA
19#include "nova.h"
20#endif
21
22using namespace std;
23using namespace CCfits;
24
25bool FitsFile::WriteDefaultKeys(const string &prgname, float version)
26{
27 if (!fTable)
28 return false;
29
30 try
31 {
32 const Time now;
33 WriteKey("TELESCOP", "FACT", "Telescope that acquired this data");
34 WriteKey("PACKAGE", PACKAGE_NAME, "Package name");
35 WriteKey("VERSION", PACKAGE_VERSION, "Package description");
36 WriteKey("CREATOR", prgname, "Program that wrote this file");
37 WriteKey("EXTREL", version, "Release Number");
38 WriteKey("COMPILED", __DATE__ " " __TIME__, "Compile time");
39 WriteKey("REVISION", REVISION, "SVN revision");
40 WriteKey("ORIGIN", "FACT", "Institution that wrote the file");
41 WriteKey("DATE", now.Iso(), "File creation date");
42 WriteKey("NIGHT", now.NightAsInt(), "Night as int");
43#ifdef HAVE_NOVA
44 WriteKey("OBSERVAT", Nova::LnLatPosn::preset(), "Observatory name (see nova.h)");
45#endif
46 WriteKey("TIMESYS", "UTC", "Time system");
47 WriteKey("TIMEUNIT", "d", "Time given in days w.r.t. to MJDREF");
48 WriteKey("MJDREF", 40587, "Store times in UNIX time (for convenience, seconds since 1970/1/1)");
49
50 //WriteKey("CONTACT", PACKAGE_BUGREPORT, "Current package maintainer");
51 //WriteKey("URL", PACKAGE_URL, "Current repositiory location");
52 }
53 catch (const CCfits::FitsException &e)
54 {
55 Error("CCfits::Table::addKey failed for '"+fTable->name()+"' in '"+fFile->name()+"': "+e.message());
56 return false;
57 }
58
59 return true;
60}
61
62// --------------------------------------------------------------------------
63//
64//! Add a new column to the vectors storing the column data.
65//! @param names the vector of string storing the columns names
66//! @param types the vector of string storing the FITS data format
67//! @param numElems the number of elements in this column
68//! @param type the char describing the FITS data format
69//! @param name the name of the particular column to be added.
70//
71void FitsFile::AddColumn(char type, const string &name, int numElems, const string &unit)
72{
73 fColNames.push_back(name);
74 fColUnits.push_back(unit);
75
76 ostringstream str;
77 if (numElems != 1)
78 str << numElems;
79
80 switch (toupper(type))
81 {
82 case 'B': str << 'L'; break; // logical
83 case 'C': str << 'B'; break; // byte
84 case 'S': str << 'I'; break; // short
85 case 'I': str << 'J'; break; // int
86 case 'X': str << 'K'; break; // long long
87 case 'F': str << 'E'; break; // float
88 case 'D': str << 'D'; break; // double
89 }
90
91 fColTypes.push_back(str.str());
92}
93
94void FitsFile::AddColumn(const string &name, const string &format, const string &unit)
95{
96 fColNames.push_back(name);
97 fColUnits.push_back(unit);
98 fColTypes.push_back(format);
99}
100
101bool FitsFile::OpenFile(const string &filename, bool allow_open)
102{
103 if (fFile || fTable)
104 {
105 Error("FitsFile::OpenFile - File already open.");
106 return false;
107 }
108 // fFileName = fileName;
109 if (!allow_open && access(filename.c_str(), F_OK)==0)
110 {
111 Error("File '"+filename+"' already existing.");
112 return false;
113 }
114 //create the FITS object
115 try
116 {
117 fFile = new CCfits::FITS(filename, CCfits::RWmode::Write);
118 }
119 catch (CCfits::FitsException e)
120 {
121 Error("CCfits::FITS failed for '"+filename+"': "+e.message());
122 return false;
123 }
124 /*
125 "SIMPLE = T / file does conform to FITS standard "
126 "BITPIX = 8 / number of bits per data pixel "
127 "NAXIS = 0 / number of data axes "
128 "EXTEND = T / FITS dataset may contain extensions "
129 "COMMENT FITS (Flexible Image Transport System) format is defined in 'Astronomy"
130 "COMMENT and Astrophysics', volume 376, page 359; bibcode: 2001A&A...376..359H "
131 "END ";
132 */
133
134 fIsOwner = true;
135
136 return true;
137}
138
139void FitsFile::ResetColumns()
140{
141 fColNames.clear();
142 fColTypes.clear();
143 fColUnits.clear();
144}
145
146bool FitsFile::SetFile(CCfits::FITS *file)
147{
148 if (!file)
149 {
150 Error("Fits::SetFile failed: NULL argument.");
151 return false;
152 }
153
154 if (fFile)
155 {
156 Error("Fits::SetFile failed: File already set.");
157 return false;
158 }
159
160 fFile = file;
161 fIsOwner = false;
162
163 return true;
164}
165
166bool FitsFile::OpenTable(const string &tablename)
167{
168 if (!fFile)
169 {
170 Error("FitsFile::OpenTable - No file open.");
171 return false;
172 }
173 if (fTable)
174 {
175 Error("FitsFile::OpenTable - Table already open.");
176 return false;
177 }
178
179 //actually create the table
180 CCfits::Table *table = 0;
181 try
182 {
183 table = fFile->addTable(tablename, 0, fColNames, fColTypes, fColUnits);
184 }
185 catch (const CCfits::FitsException &e)
186 {
187 Error("CCfits::Table::addTable failed for '"+tablename+"' in '"+fFile->name()+"': "+e.message());
188 return false;
189 }
190
191 if (table->rows() != 0)
192 {
193 Error("FITS table '"+tablename+"' created in '"+fFile->name()+"' on the fly looks non-empty.");
194 return false;
195 }
196
197 // Set this as last - we use it for IsOpen()
198 fTable = table;
199 fNumRows = 0;
200
201 return true;
202}
203
204// --------------------------------------------------------------------------
205//
206//! This looks for a suitable table in the fits file, i.e. that corresponds to the name and column names. (no format check yet)
207//! @param tableName. the base table name to be obtained. If not suitable, numbers are appened to the name
208//! @param allNames. the name of all columns
209//! @param allDataTypes. the data types of all columns
210//! @param allUnits. the units of the columns
211//! @return a pointer to the newly retrieved/created table
212//
213bool FitsFile::OpenNewTable(const string &tableName, int maxtry)
214{
215 if (!fFile)
216 {
217 Error("FitsFile::OpenNewTable - No file open.");
218 return false;
219 }
220
221 if (fTable)
222 {
223 Error("FitsFile::OpenNewTable - Table already open.");
224 return false;
225 }
226
227 //first, let's check if the table already exist in the file
228 fFile->read(vector<string>(1, tableName));
229
230 // FIXME: Check for fFile and fTable
231 const multimap<string, CCfits::ExtHDU *> &extMap = fFile->extension();
232
233 for (int i=0; i<maxtry; i++)
234 {
235 //if (i==10)
236 // fMess->Warn("Already 10 different tables with different formats exist in this file. Please consider re-creating the file entirely (i.e. delete it please)");
237
238 ostringstream str;
239 str << tableName;
240 if (i != 0)
241 str << "-" << i;
242
243 const string tname = str.str();
244
245 const multimap<string,CCfits::ExtHDU*>::const_iterator it = extMap.find(tname);
246
247 //current table name does not exist yet. return its associated fits table newly created
248 if (it == extMap.end())
249 {
250 // What is this for?
251 //for (multimap<string, CCfits::ExtHDU*>::const_iterator it=extMap.begin();
252 // it!= extMap.end(); it++)
253 // fMess->Debug(it->first);
254
255 return OpenTable(tname);
256 }
257
258 CCfits::Table *table = dynamic_cast<CCfits::Table*>(it->second);
259
260 // something wrong happened while getting the table pointer
261 if (!table)
262 {
263 Error("HDU '"+tname+"' found in file, but it is not a proper CCfits::Table.");
264 return false;
265 }
266
267 //now check that the table columns are the same
268 //as the service columns
269 table->makeThisCurrent();
270
271 // FIXME: To be checked...
272 /*
273 const map<string, Column*> cMap = table->column();
274 for (vector<string>::const_iterator ii=fFile->fColNames;
275 ii!=fFile->fColNames.end(); ii++)
276 if (cMap.find(*ii) == cMap.end())
277 continue;
278 */
279
280 fNumRows = table->rows();
281
282 // ----------- This is just a simple sanity check ----------
283
284 // This is not necessary this is done already in
285 // findSuitableTable (either directly or indirectly through OpenTable)
286 // fFile->fTable->makeThisCurrent();
287
288 //If the file already existed, then we must load its data to memory before writing to it.
289 if (fNumRows>0)
290 {
291 CCfits::BinTable* bTable = dynamic_cast<CCfits::BinTable*>(table);
292 if (!bTable)
293 {
294 Error("Table '"+tableName+"' found in '"+fFile->name()+"' is not a binary table.");
295 return false;
296 }
297
298 //read the table binary data.
299 vector<string> colName;
300 bTable->readData(true, colName);
301
302 // double check that the data was indeed read from the disk.
303 // Go through the fTable instead as colName is empty (yes, it is !)
304 const auto &cMap = table->column();
305
306 //check that the existing columns are the same as the ones we want to write
307 for (map<string, CCfits::Column*>::const_iterator mapIt = cMap.begin(); mapIt != cMap.end(); mapIt++)
308 {
309 bool found = false;
310 for (unsigned int ii=0;ii<fColNames.size();ii++)
311 {
312 if (mapIt->first == fColNames[ii])
313 {
314 found = true;
315 if (mapIt->second->format() != fColTypes[ii])
316 {
317 Error("Column "+fColNames[ii]+" has wrong format ("+fColTypes[ii]+" vs "+mapIt->second->format()+" in file)");
318 return false;
319 }
320 }
321 }
322 if (!found)
323 {
324 Error("Column "+mapIt->first+" only exist in written file");
325 return false;
326 }
327 }
328 //now we know that all the file's columns are requested. Let's do it the other way around
329 for (unsigned int ii=0;ii<fColNames.size();ii++)
330 {
331 bool found = false;
332 for (map<string, CCfits::Column*>::const_iterator mapIt = cMap.begin(); mapIt != cMap.end(); mapIt++)
333 {
334 if (fColNames[ii] == mapIt->first)
335 {
336 found = true;
337 if (fColTypes[ii] != mapIt->second->format())
338 {
339 Error("Column "+fColNames[ii]+" has wrong format ("+fColTypes[ii]+" vs "+mapIt->second->format()+" in file)");
340 return false;
341 }
342 }
343 }
344 if (!found)
345 {
346 Error("Column "+fColNames[ii]+" only exist in requested description");
347 return false;
348 }
349 }
350
351 for (map<string,CCfits::Column*>::const_iterator cMapIt = cMap.begin();
352 cMapIt != cMap.end(); cMapIt++)
353 {
354 if (!cMapIt->second->isRead())
355 {
356 Error("Reading column '"+cMapIt->first+"' back from '"+fFile->name()+"' failed.");
357 return false;
358 }
359 }
360 }
361
362 // Set this as last - we use it for IsOpen()
363 fTable = table;
364
365 return true;
366 }
367
368 ostringstream str;
369 str << "FitsFile::OpenNewTable failed - more than " << maxtry << " tables tried." << endl;
370 Error(str);
371
372 return false;
373}
374
375bool FitsFile::AddRow()
376{
377 if (!fFile || !fTable)
378 {
379 Error("FitsFile::AddRow - No table open.");
380 return false;
381 }
382
383 //insert a new row (1==number of rows to insert)
384 int status(0);
385 fits_insert_rows(fFile->fitsPointer(), fNumRows, 1, &status);
386
387 // Status is also directly returned, but we need to give the
388 // pointer anyway
389 if (status)
390 {
391 char text[30];//max length of cfitsio error strings (from doc)
392 fits_get_errstatus(status, text);
393
394 ostringstream str;
395 str << "Inserting row " << fNumRows << " failed in '"+fFile->name()+"': " << text << " (fits_insert_rows,rc=" << status << ")";
396 Error(str);
397
398 return false;
399 }
400
401 fNumRows++;
402 fCursor = 1;
403
404 return true;
405}
406
407bool FitsFile::WriteData(size_t &start, const void *ptr, size_t size)
408{
409 if (!fFile || !fTable)
410 {
411 Error("FitsFile::AddRow - No table open.");
412 return false;
413 }
414
415 int status = 0;
416 fits_write_tblbytes(fFile->fitsPointer(), fNumRows, start, size,
417 (unsigned char*)ptr, &status);
418
419 // Status is also directly returned, but we need to give the
420 // pointer anyway
421 if (status)
422 {
423 char text[30];//max length of cfitsio error strings (from doc)
424 fits_get_errstatus(status, text);
425
426 ostringstream str;
427 str << "Writing row " << fNumRows << " failed in '"+fFile->name()+"': " << text << " (file_write_tblbytes,rc=" << status << ")";
428 Error(str);
429 }
430
431 start += size;
432 return status==0;
433}
434
435void FitsFile::Close()
436{
437 if (!fFile)
438 return;
439
440 if (fIsOwner)
441 {
442 const string name = fFile->name();
443 delete fFile;
444 }
445
446 //WARNING: do NOT delete the table as it gets deleted by the
447 // fFile object
448 fFile = NULL;
449 fTable = NULL;
450}
451
452void FitsFile::Flush()
453{
454 if (!fFile)
455 return;
456
457 int status = 0;
458 fits_flush_file(fFile->fitsPointer(), &status);
459
460 if (status)
461 {
462 char text[30];
463 fits_get_errstatus(status, text);
464
465 ostringstream str;
466 str << "Flushing file " << fFile->name() << " failed: " << text << " (fits_flush_file, rc=" << status << ")";
467 Error(str);
468 }
469}
470size_t FitsFile::GetDataSize() const
471{
472 size_t size = 0;
473
474 for (vector<string>::const_iterator it=fColTypes.begin();
475 it!=fColTypes.end(); it++)
476 {
477 size_t id=0;
478
479 int n=1;
480 try { n = stoi(*it, &id); }
481 catch (const exception&) { }
482
483 if (n==0)
484 continue;
485
486 switch ((*it)[id])
487 {
488 case 'L':
489 case 'A': size += n*1; break; // ascii
490 case 'B': size += n*1; break; // logical/byte
491 case 'I': size += n*2; break; // short
492 case 'J': size += n*4; break; // int
493 case 'K': size += n*8; break; // long long
494 case 'E': size += n*4; break; // float
495 case 'D': size += n*8; break; // double
496 default:
497 throw runtime_error("FitsFile::GetDataSize - id not known.");
498 }
499 }
500
501 return size;
502}
Note: See TracBrowser for help on using the repository browser.