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

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