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

Last change on this file since 14362 was 12849, checked in by tbretz, 13 years ago
Allow 'A' as format.
File size: 13.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//#include "Time.h"
19//#include "Converter.h"
20//#include "MessageImp.h"
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 WriteKey("TIMESYS", "UTC", "Time system");
44 WriteKey("TIMEUNIT", "d", "Time given in days w.r.t. to MJDREF");
45 WriteKey("MJDREF", 40587, "Store times in UNIX time (for convenience, seconds since 1970/1/1)");
46
47 //WriteKey("CONTACT", PACKAGE_BUGREPORT, "Current package maintainer");
48 //WriteKey("URL", PACKAGE_URL, "Current repositiory location");
49 }
50 catch (const CCfits::FitsException &e)
51 {
52 Error("CCfits::Table::addKey failed for '"+fTable->name()+"' in '"+fFile->name()+"': "+e.message());
53 return false;
54 }
55
56 return true;
57}
58
59// --------------------------------------------------------------------------
60//
61//! Add a new column to the vectors storing the column data.
62//! @param names the vector of string storing the columns names
63//! @param types the vector of string storing the FITS data format
64//! @param numElems the number of elements in this column
65//! @param type the char describing the FITS data format
66//! @param name the name of the particular column to be added.
67//
68void FitsFile::AddColumn(char type, const string &name, int numElems, const string &unit)
69{
70 fColNames.push_back(name);
71 fColUnits.push_back(unit);
72
73 ostringstream str;
74 if (numElems != 1)
75 str << numElems;
76
77 switch (toupper(type))
78 {
79 case 'B': str << 'L'; break; // logical
80 case 'C': str << 'B'; break; // byte
81 case 'S': str << 'I'; break; // short
82 case 'I': str << 'J'; break; // int
83 case 'X': str << 'K'; break; // long long
84 case 'F': str << 'E'; break; // float
85 case 'D': str << 'D'; break; // double
86 }
87
88 fColTypes.push_back(str.str());
89}
90
91void FitsFile::AddColumn(const string &name, const string &format, const string &unit)
92{
93 fColNames.push_back(name);
94 fColUnits.push_back(unit);
95 fColTypes.push_back(format);
96}
97
98bool FitsFile::OpenFile(const string &filename, bool allow_open)
99{
100 if (fFile || fTable)
101 {
102 Error("FitsFile::OpenFile - File already open.");
103 return false;
104 }
105
106 // fFileName = fileName;
107 if (!allow_open && access(filename.c_str(), F_OK)==0)
108 {
109 Error("File '"+filename+"' already existing.");
110 return false;
111 }
112
113 //create the FITS object
114 try
115 {
116 fFile = new CCfits::FITS(filename, CCfits::RWmode::Write);
117 }
118 catch (CCfits::FitsException e)
119 {
120 Error("CCfits::FITS failed for '"+filename+"': "+e.message());
121 return false;
122 }
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 map<string,CCfits::Column*> &cMap = table->column();
305
306 for (map<string,CCfits::Column*>::const_iterator cMapIt = cMap.begin();
307 cMapIt != cMap.end(); cMapIt++)
308 {
309 if (!cMapIt->second->isRead())
310 {
311 Error("Reading column '"+cMapIt->first+"' back from '"+fFile->name()+"' failed.");
312 return false;
313 }
314 }
315 }
316
317 // Set this as last - we use it for IsOpen()
318 fTable = table;
319
320 return true;
321 }
322
323 ostringstream str;
324 str << "FitsFile::OpenNewTable failed - more than " << maxtry << " tables tried." << endl;
325 Error(str);
326
327 return false;
328}
329
330bool FitsFile::AddRow()
331{
332 if (!fFile || !fTable)
333 {
334 Error("FitsFile::AddRow - No table open.");
335 return false;
336 }
337
338 //insert a new row (1==number of rows to insert)
339 int status(0);
340 fits_insert_rows(fFile->fitsPointer(), fNumRows, 1, &status);
341
342 // Status is also directly returned, but we need to give the
343 // pointer anyway
344 if (status)
345 {
346 char text[30];//max length of cfitsio error strings (from doc)
347 fits_get_errstatus(status, text);
348
349 ostringstream str;
350 str << "Inserting row " << fNumRows << " failed in '"+fFile->name()+"': " << text << " (fits_insert_rows,rc=" << status << ")";
351 Error(str);
352
353 return false;
354 }
355
356 fNumRows++;
357 fCursor = 1;
358
359 return true;
360}
361
362bool FitsFile::WriteData(size_t &start, const void *ptr, size_t size)
363{
364 if (!fFile || !fTable)
365 {
366 Error("FitsFile::AddRow - No table open.");
367 return false;
368 }
369
370 int status = 0;
371 fits_write_tblbytes(fFile->fitsPointer(), fNumRows, start, size,
372 (unsigned char*)ptr, &status);
373
374 // Status is also directly returned, but we need to give the
375 // pointer anyway
376 if (status)
377 {
378 char text[30];//max length of cfitsio error strings (from doc)
379 fits_get_errstatus(status, text);
380
381 ostringstream str;
382 str << "Writing row " << fNumRows << " failed in '"+fFile->name()+"': " << text << " (file_write_tblbytes,rc=" << status << ")";
383 Error(str);
384 }
385
386 start += size;
387 return status==0;
388}
389
390void FitsFile::Close()
391{
392 if (!fFile)
393 return;
394
395 if (fIsOwner)
396 {
397 const string name = fFile->name();
398 delete fFile;
399 }
400
401 //WARNING: do NOT delete the table as it gets deleted by the
402 // fFile object
403 fFile = NULL;
404 fTable = NULL;
405}
406
407void FitsFile::Flush()
408{
409 if (!fFile)
410 return;
411
412 int status = 0;
413 fits_flush_file(fFile->fitsPointer(), &status);
414
415 if (status)
416 {
417 char text[30];
418 fits_get_errstatus(status, text);
419
420 ostringstream str;
421 str << "Flushing file " << fFile->name() << " failed: " << text << " (fits_flush_file, rc=" << status << ")";
422 Error(str);
423 }
424}
425size_t FitsFile::GetDataSize() const
426{
427 size_t size = 0;
428
429 for (vector<string>::const_iterator it=fColTypes.begin();
430 it!=fColTypes.end(); it++)
431 {
432 size_t id=0;
433
434 int n=1;
435 try { n = stoi(*it, &id); }
436 catch (const exception&) { }
437
438 if (n==0)
439 continue;
440
441 switch ((*it)[id])
442 {
443 case 'L':
444 case 'A': size += n*1; break; // ascii
445 case 'B': size += n*1; break; // logical/byte
446 case 'I': size += n*2; break; // short
447 case 'J': size += n*4; break; // int
448 case 'K': size += n*8; break; // long long
449 case 'E': size += n*4; break; // float
450 case 'D': size += n*8; break; // double
451 default:
452 throw runtime_error("FitsFile::GetDataSize - id not known.");
453 }
454 }
455
456 return size;
457}
Note: See TracBrowser for help on using the repository browser.