USGS

Isis 3.0 Object Programmers' Reference

Home

ProcessImportFits.cpp
Go to the documentation of this file.
1 
23 #include "ProcessImportFits.h"
24 
25 #include <iostream>
26 #include <math.h>
27 
28 #include <QDebug>
29 #include <QString>
30 #include <sstream>
31 
32 #include "IException.h"
33 #include "IString.h"
34 #include "LineManager.h"
35 #include "Preference.h"
36 #include "Pvl.h"
37 #include "PvlGroup.h"
38 #include "PixelType.h"
39 #include "SpecialPixel.h"
40 #include "UserInterface.h"
41 
42 namespace Isis {
43 
48  m_fitsLabels = NULL;
49  m_headerSizes = NULL;
50  m_dataStarts = NULL;
51  }
52 
53 
58  delete m_fitsLabels;
59  delete m_headerSizes;
60  delete m_dataStarts;
61  m_file.close();
62  }
63 
64 
71 
75 
76  // Process each FITS label area. Storing each in its own PvlGroup
77  char readBuf[81];
78  IString line = "";
79  unsigned int place;
80 
81  // The main FITS label starts at the beginning of the file
82  // FITS extension labels start after the previous data and on a 2080 byte boundry
83  // Each FITS keyword in all lables is store in 80 bytes (space padded to 80 if necessary)
84 
85  // Start at the beginning of the file for the main FITS label
86  m_file.seekg(0, std::ios_base::beg);
87 
88  // Read the first label line (80 chars)
89  // We are assuming the file pointer is set to the beginning of the first/next label
90  while (m_file.read(readBuf, 80) && m_file.gcount() == 80) {
91 
92  PvlGroup *fitsLabel = new PvlGroup("FitsLabels");
93 
94  readBuf[80] = '\0';
95  line = readBuf;
96  place = 80;
97 
98  // Process each fits label record (80 bytes) and place keyword, value pairs into PvlKeywords
99  // with any associated comments
100  while (line.substr(0, 3) != "END") {
101 
102  // Check for blank lines
103  if (line.substr(0, 1) != " " && line.substr(0, 1) != "/") {
104  // Name of keyword
105  PvlKeyword label(line.Token(" =").ToQt()); // Stop on spaces OR equal sign
106  // Remove up to beginning of data
107  line.TrimHead(" =");
108  line.TrimTail(" ");
109  if (label.name() == "COMMENT" || label.name() == "HISTORY") {
110  label += line.ToQt();
111  }
112  else {
113  // Check for a quoted value
114  if (line.substr(0,1) == "'") {
115  line.TrimHead("'");
116  label += line.Token("'").TrimHead(" ").TrimTail(" ").ToQt();
117  line.TrimHead(" '");
118  }
119  else {
120  // Access any remaining data without the trailing comment if there is one
121  IString value = line.Token("/");
122  // Clear to end of data
123  value.TrimTail(" ");
124  label += value.ToQt();
125  line.TrimHead(" ");
126  }
127  // If the line still has anything in it, treat it is as a comment.
128  if (line.size() > 0) {
129  line.TrimHead(" /");
130  label.addComment(line.ToQt());
131  // A possible format for units, other possiblites exist.
132  if (line != line.Token("[")) {
133  label.setUnits(line.Token("[").Token("]").ToQt());
134  }
135  }
136  }
137  fitsLabel->addKeyword(label);
138  }
139 
140  // Read the next label line
141  m_file.read(readBuf, 80);
142  readBuf[80] = '\0';
143  line = readBuf;
144  place += 80;
145  }
146 
147  // Save off the PvlGroup and the number of records read from this label
148  m_fitsLabels->push_back(fitsLabel);
149  m_headerSizes->push_back((int)ceil(place / 2880.0));
150 
151  // The file pointer should be pointing at the end of the record that contained "END"
152  // Move the file pointer past the padding after the "END" (i.e., points to start of data)
153  std::streamoff jump;
154  jump = m_headerSizes->last() * 2880 - place;
155  m_file.seekg(jump, std::ios_base::cur);
156 
157  m_dataStarts->push_back(m_file.tellg());
158 
159  // NOTE: For now we only handle image data (i.e., keywords BITPIX & NAXIS & NAXISx must exist)
160  // Does this look like a label for a FITS image? Stop after the first label that does not
161  // because we don't know how to move the file pointer past a non-image data extension.
162  if (fitsLabel->hasKeyword("BITPIX") && fitsLabel->hasKeyword("NAXIS") &&
163  fitsLabel->hasKeyword("NAXIS1")) {
164 
165  int bytesPerPixel = 0;
166  bytesPerPixel = (int)((*fitsLabel)["BITPIX"]);
167  bytesPerPixel = fabs(bytesPerPixel);
168  bytesPerPixel /= 8;
169 
170  unsigned int axis1 = 1;
171  axis1 = toInt((*fitsLabel)["NAXIS1"]);
172 
173  unsigned int axis2 = 1;
174  if (fitsLabel->hasKeyword("NAXIS2")) {
175  axis2 = toInt((*fitsLabel)["NAXIS2"]);
176  }
177 
178  unsigned int axis3 = 1;
179  if (fitsLabel->hasKeyword("NAXIS3")) {
180  axis3 = toInt((*fitsLabel)["NAXIS3"]);
181  }
182 
183  jump = (int)(ceil(bytesPerPixel * axis1 * axis2 * axis3 / 2880.0) * 2880.0);
184  m_file.seekg(jump, std::ios_base::cur);
185  }
186  // Do we have at least on header that looks like it has image data? If so, we can continue,
187  // but ignore the rest of the file because we don't know how to skip over a non-image data.
188  else if (m_fitsLabels->size() > 1) {
189  m_fitsLabels->pop_back();
190  m_headerSizes->pop_back();
191  m_dataStarts->pop_back();
192  break;
193  }
194  else {
195  QString msg = QObject::tr("The FITS file does not contain a section header that looks "
196  "like it describes an image [%1]").arg(m_name.toString());
198  }
199  }
200  }
201 
202 
209  PvlGroup ProcessImportFits::fitsLabel(int labelNumber) const {
210 
211  if (labelNumber >= m_fitsLabels->size()) {
212  QString msg = QObject::tr("The requested label number [%1], from file [%2] is "
213  "past the last image in this FITS file. Image count is [%3]").arg(labelNumber).
214  arg(m_name.expanded()).arg(m_fitsLabels->size()-1);
216  }
217 
218  if (!m_fitsLabels) {
219  QString msg = QObject::tr("The FITS label has not been initialized, call setFitsFile first");
221  }
222  else if (m_fitsLabels->size() < labelNumber) {
223  QString msg = QObject::tr("The requested FITS label number does not exist from file [%1]").arg(m_name.toString());
225  }
226 
227  return *(*m_fitsLabels)[labelNumber];
228  }
229 
230 
238 
239  // NOTE: This needs to be changed over to use translation files
240 
241  // Attempt to extract the standard instrument group keywords
242  PvlGroup inst("Instrument");
243  if (fitsLabel.hasKeyword("DATE-OBS")) {
244  inst += PvlKeyword("StartTime", fitsLabel["DATE-OBS"][0]);
245  }
246  if (fitsLabel.hasKeyword("OBJECT")) {
247  inst += PvlKeyword("Target", fitsLabel["OBJECT"][0]);
248  }
249  if (fitsLabel.hasKeyword("INSTRUME")) {
250  inst += PvlKeyword("InstrumentId", fitsLabel["INSTRUME"][0]);
251  }
252  if (fitsLabel.hasKeyword("OBSERVER")) {
253  inst += PvlKeyword("SpacecraftName", fitsLabel["OBSERVER"][0]);
254  }
255 
256  return inst;
257  }
258 
259 
266  m_name = fitsFile;
267 
268  SetInputFile(fitsFile.toString()); // Make sure the file exists
269 
270  m_file.open(fitsFile.expanded().toLocal8Bit().constData(), std::ios::in | std::ios::binary);
271 
272  if (!m_file.is_open()) {
273  QString msg = QObject::tr("Unable to open FITS formatted file [%1]").arg(fitsFile.toString());
275  }
276 
277  // Get the FITS labels internalized
279 
280  // Check to make sure it is a FITS file we can handle
281  PvlGroup label = fitsLabel(0);
282  if (label["SIMPLE"][0] == "F") {
283  QString msg = QObject::tr("The file [%1] can not be processed. It is an unsupported format.").
284  arg(fitsFile.toString());
286  }
287 
288  m_file.close();
289  }
290 
291 
302 
303  if (labelNumber >= m_fitsLabels->size()) {
304  QString msg = QObject::tr("The requested label number [%1], from file [%2] is "
305  "past the last image in this FITS file [%3]").arg(labelNumber).
306  arg(InputFile()).arg(m_fitsLabels->size()-1);
308  }
309 
310  PvlGroup label = *(*m_fitsLabels)[labelNumber];
311 
312  // Set the ProcessImport to skip over all the previous images and their labels and the label for
313  // this image. Don't save this info (think memory)
314  SetFileHeaderBytes((*m_dataStarts)[labelNumber]);
315  //SaveFileHeader();
316 
317  // Find pixel type. NOTE: There are several unsupported possiblites
318  Isis::PixelType type;
319  QString msg = "";
320  switch (toInt(label["BITPIX"][0])) {
321  case 8:
322  type = Isis::UnsignedByte;
323  break;
324  case 16:
325  type = Isis::SignedWord;
326  break;
327  case 32:
328  msg = "Signed 32 bit integer (int) pixel type is not supported at this time";
330  break;
331  case -32:
332  type = Isis::Real;
333  break;
334  case 64:
335  msg = "Signed 64 bit integer (long) pixel type is not supported at this time";
337  break;
338  case -64:
339  type = Isis::Double;
340  break;
341  default:
342  msg = "Unknown pixel type [" + label["BITPIX"][0] + "] is not supported for imported";
344  break;
345  }
346 
347  SetPixelType(type);
348 
349  // It is possible to have a NAXIS value of 0 meaning no data, the file could include
350  // xtensions with data, however, those aren't supported because we need the code to know
351  // how to skip over them.
352  // NOTE: FITS files, at least the ones seen till now, do not specify a line prefix or suffix
353  // data byte count. Some FITS files do have them and ISIS needs to remove them so it is not
354  // considered part of the DNs. So, use the parent class' prefix/suffix byte count to reduce
355  // the number of samples.
356  if (Organization() == BSQ) {
357  if (toInt(label["NAXIS"][0]) == 2) {
358  SetDimensions(toInt(label["NAXIS1"][0]) - (DataPrefixBytes()+DataSuffixBytes())/SizeOf(type),
359  toInt(label["NAXIS2"][0]), 1);
360  }
361  else if (toInt(label["NAXIS"][0]) == 3) {
362  SetDimensions(toInt(label["NAXIS1"][0]) - (DataPrefixBytes()+DataSuffixBytes())/SizeOf(type),
363  toInt(label["NAXIS2"][0]), toInt(label["NAXIS3"][0]));
364  }
365  else {
366  QString msg = "NAXIS count of [" + label["NAXIS"][0] + "] is not supported at this time";
368  }
369  }
370 
371  if (Organization() == BIL) {
372  if (toInt(label["NAXIS"][0]) == 2) {
373  SetDimensions(toInt(label["NAXIS1"][0]) - (DataPrefixBytes()+DataSuffixBytes())/SizeOf(type),
374  1, toInt(label["NAXIS2"][0]));
375  }
376  else if (toInt(label["NAXIS"][0]) == 3) {
377  SetDimensions(toInt(label["NAXIS1"][0]) - (DataPrefixBytes()+DataSuffixBytes())/SizeOf(type),
378  toInt(label["NAXIS3"][0]), toInt(label["NAXIS2"][0]));
379  }
380  else {
381  QString msg = "NAXIS count of [" + label["NAXIS"][0] + "] is not supported at this time";
383  }
384  }
385 
386  // Base and multiplier
387  if (label.hasKeyword("BZERO")) {
388  SetBase(toDouble(label["BZERO"][0]));
389  }
390  else {
391  SetBase(0.0);
392  }
393  if (label.hasKeyword("BSCALE")) {
394  SetMultiplier(toDouble(label["BSCALE"][0]));
395  }
396  else {
397  SetMultiplier(1.0);
398  }
399 
400  // Byte order
401  SetByteOrder(Isis::Msb);
402 
403  }
404 
405 } // end namespace Isis
406