Isis 3 Programmer Reference
ProcessImportFits.cpp
Go to the documentation of this file.
1 
23 #include "ProcessImportFits.h"
24 
25 #include <QDebug>
26 #include <QString>
27 
28 #include <iostream>
29 #include <math.h>
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_fitsImageLabels = NULL;
49  m_extraFitsLabels = NULL;
50  m_headerSizes = NULL;
51  m_dataStarts = NULL;
52  }
53 
54 
59  delete m_fitsImageLabels;
60  delete m_extraFitsLabels;
61  delete m_headerSizes;
62  delete m_dataStarts;
63  m_file.close();
64  }
65 
66 
73 
78 
79  // Process each FITS label area, storing each in its own PvlGroup.
80  char readBuf[81];
81  IString line = "";
82  unsigned int place;
83 
84  // The main FITS label starts at the beginning of the file.
85  // FITS extension labels start after the previous data and on a 2880 byte boundry
86  // Each FITS keyword in all lables is stored in 80 bytes (space padded to 80 if
87  // necessary).
88 
89  // Start at the beginning of the file for the main FITS label
90  m_file.seekg(0, std::ios_base::beg);
91 
92  // Read the first label line (80 chars)
93  // We are assuming the file pointer is set to the beginning of the first/next label
94  while (m_file.read(readBuf, 80) && m_file.gcount() == 80) {
95 
96  PvlGroup *fitsLabel = new PvlGroup("FitsLabels");
97 
98  readBuf[80] = '\0';
99  line = readBuf;
100  place = 80;
101 
102  // Process each fits label record (80 bytes) and place keyword, value pairs into PvlKeywords
103  // with any associated comments.
104  while (line.substr(0, 3) != "END") {
105 
106  // Check for blank lines
107  if (line.substr(0, 1) != " " && line.substr(0, 1) != "/") {
108  // Name of keyword
109  PvlKeyword label(line.Token(" =").ToQt()); // Stop on spaces OR equal sign
110  if (QString::compare(label.name(), "OBJECT", Qt::CaseInsensitive) == 0) {
111  label.setName("TARGET");
112  label.addComment("NOTE: This keyword name was changed from 'OBJECT' in the original "
113  "fit header file.");
114  }
115  // Remove up to beginning of data
116  line.TrimHead(" =");
117  line.TrimTail(" ");
118  if (label.name() == "COMMENT" || label.name() == "HISTORY") {
119  label += line.ToQt();
120  }
121  else {
122  // Check for a quoted value
123  if (line.substr(0,1) == "'") {
124  line.TrimHead("'");
125  label += line.Token("'").TrimHead(" ").TrimTail(" ").ToQt();
126  line.TrimHead(" '");
127  }
128  else {
129  // Access any remaining data without the trailing comment if there is one
130  IString value = line.Token("/");
131  // Clear to end of data
132  value.TrimTail(" ");
133  label += value.ToQt();
134  line.TrimHead(" ");
135  }
136  // If the line still has anything in it, treat it is as a comment.
137  if (line.size() > 0) {
138  line.TrimHead(" /");
139  label.addComment(line.ToQt());
140  // A possible format for units, other possiblites exist.
141  if (line != line.Token("[")) {
142  label.setUnits(line.Token("[").Token("]").ToQt());
143  }
144  }
145  }
146  fitsLabel->addKeyword(label);
147  }
148 
149  // Read the next label line
150  m_file.read(readBuf, 80);
151  readBuf[80] = '\0';
152  line = readBuf;
153  place += 80;
154  }
155 
156  // Save off the PvlGroup and the number of records read from this label
157  m_fitsImageLabels->append(fitsLabel);
158  m_headerSizes->append((int)ceil(place / 2880.0));
159 
160  // The file pointer should be pointing at the end of the record that contained "END"
161  // Move the file pointer past the padding after the "END" (i.e., points to start of data)
162  std::streamoff jump;
163  jump = m_headerSizes->last() * 2880 - place;
164  m_file.seekg(jump, std::ios_base::cur);
165 
166  m_dataStarts->append(m_file.tellg());
167 
168  // Does this look like a label for a FITS image? Stop after the first label that does not
169  // because we don't know how to move the file pointer past a non-image data extension.
170  if (fitsLabel->hasKeyword("BITPIX") && fitsLabel->hasKeyword("NAXIS")) {
171 
172  // This section can only handle image data
173  // (i.e., keywords BITPIX & NAXIS & NAXISx must exist).
174  if((int)fitsLabel->findKeyword("NAXIS") > 0) {
175  int bytesPerPixel = 0;
176  bytesPerPixel = (int)((*fitsLabel)["BITPIX"]);
177  bytesPerPixel = std::abs(bytesPerPixel);
178  bytesPerPixel /= 8;
179 
180  unsigned int axis1 = 1;
181  axis1 = toInt((*fitsLabel)["NAXIS1"]);
182 
183  unsigned int axis2 = 1;
184  if (fitsLabel->hasKeyword("NAXIS2")) {
185  axis2 = toInt((*fitsLabel)["NAXIS2"]);
186  }
187 
188  unsigned int axis3 = 1;
189  if (fitsLabel->hasKeyword("NAXIS3")) {
190  axis3 = toInt((*fitsLabel)["NAXIS3"]);
191  }
192 
193  jump = (int)(ceil(bytesPerPixel * axis1 * axis2 * axis3 / 2880.0) * 2880.0);
194  m_file.seekg(jump, std::ios_base::cur);
195  }
196  else {
197  // Note: this will allow us to read extra label sections that have 0 axes,
198  // but has image-related info (so BITPIX and NAXIS keywords exist). This
199  // includes informational labels, as seen at the beginning of hayabusa2
200  // images in this case, there is NO DATA, so no jump should be needed to
201  // get to the next section.
202  PvlGroup *extraLabelGroup = m_fitsImageLabels->last();
203  extraLabelGroup->setName("FitsExtras");
204  m_extraFitsLabels->append(extraLabelGroup);
205 
206  m_fitsImageLabels->removeLast();
207  m_headerSizes->removeLast();
208  m_dataStarts->removeLast();
209  }
210  }
211  // Do we have at least on header that looks like it has image data? If so, we can continue,
212  // but ignore the rest of the file because we don't know how to skip over a non-image data.
213  else if (m_fitsImageLabels->size() > 1) {
214  m_fitsImageLabels->removeLast();
215  m_headerSizes->removeLast();
216  m_dataStarts->removeLast();
217  break;
218  }
219 
220  else {
221  QString msg = QObject::tr("The FITS file does not contain a section header that appears "
222  "to describe an image [%1].").arg(m_name.toString());
224  }
225  }
226  }
227 
228 
241  if (labelNumber >= m_extraFitsLabels->size()) {
242  QString msg = QObject::tr("The requested label number [%1], from file [%2] is "
243  "past the last extra group found in this FITS file. "
244  "Extra label count is [%3]").arg(labelNumber).
245  arg(m_name.expanded()).arg(m_extraFitsLabels->size()-1);
247  }
248 
249  if (!m_extraFitsLabels) {
250  QString msg = QObject::tr("The FITS label has not been initialized, "
251  "call setFitsFile() first.");
253  }
254  else if (m_extraFitsLabels->size() < labelNumber) {
255  QString msg = QObject::tr("The requested FITS label number "
256  "was not found in file [%1].").arg(m_name.toString());
258  }
259 
260  return *(*m_extraFitsLabels)[labelNumber];
261  }
262 
263 
276 
277  if (labelNumber >= m_fitsImageLabels->size()) {
278  QString msg = QObject::tr("The requested label number [%1], from file [%2] is "
279  "past the last image group found in this FITS file. "
280  "Image label count is [%3]").arg(labelNumber).
281  arg(m_name.expanded()).arg(m_fitsImageLabels->size()-1);
283  }
284 
285  if (!m_fitsImageLabels) {
286  QString msg = QObject::tr("The FITS label has not been initialized, "
287  "call setFitsFile first.");
289  }
290  else if (m_fitsImageLabels->size() < labelNumber) {
291  QString msg = QObject::tr("The requested FITS label number "
292  "was not found in file [%1].").arg(m_name.toString());
294  }
295 
296  return *(*m_fitsImageLabels)[labelNumber];
297  }
298 
299 
312 
313  // NOTE: This needs to be changed over to use translation files
314 
315  // Attempt to extract the standard instrument group keywords
316  PvlGroup inst("Instrument");
317  if (fitsLabel.hasKeyword("DATE-OBS")) {
318  inst += PvlKeyword("StartTime", fitsLabel["DATE-OBS"][0]);
319  }
320  if (fitsLabel.hasKeyword("TARGET")) {
321  inst += PvlKeyword("Target", fitsLabel["TARGET"][0]);
322  }
323  if (fitsLabel.hasKeyword("INSTRUME")) {
324  inst += PvlKeyword("InstrumentId", fitsLabel["INSTRUME"][0]);
325  }
326  if (fitsLabel.hasKeyword("OBSERVER")) {
327  inst += PvlKeyword("SpacecraftName", fitsLabel["OBSERVER"][0]);
328  }
329 
330  return inst;
331  }
332 
333 
341  m_name = fitsFile;
342 
343  SetInputFile(fitsFile.toString()); // Make sure the file exists
344 
345  m_file.open(fitsFile.expanded().toLocal8Bit().constData(), std::ios::in | std::ios::binary);
346 
347  if (!m_file.is_open()) {
348  QString msg = QObject::tr("Unable to open FITS formatted file [%1].")
349  .arg(fitsFile.toString());
351  }
352 
353  // Get the FITS labels internalized
355 
356  // Check to make sure it is a FITS file we can handle
357  PvlGroup label = fitsImageLabel(0);
358  if (label.hasKeyword("SIMPLE") && label["SIMPLE"][0] == "F") {
359  QString msg = QObject::tr("The file [%1] cannot be processed. "
360  "It is an unsupported format.").
361  arg(fitsFile.toString());
363  }
364 
365  m_file.close();
366  }
367 
368 
380 
381  if (labelNumber >= m_fitsImageLabels->size()) {
382  QString msg = QObject::tr("The requested label number [%1], from file [%2] is "
383  "past the last image in this FITS file [%3].").arg(labelNumber).
384  arg(InputFile()).arg(m_fitsImageLabels->size()-1);
386  }
387 
388  PvlGroup label = *(*m_fitsImageLabels)[labelNumber];
389 
390  // Set the ProcessImport to skip over all the previous images and their labels and the label for
391  // this image. Don't save this info (think memory)
392  SetFileHeaderBytes((*m_dataStarts)[labelNumber]);
393 
394  // Find pixel type. NOTE: There are several unsupported possiblites
395  Isis::PixelType type;
396  QString msg = "";
397  switch (toInt(label["BITPIX"][0])) {
398  case 8:
399  type = Isis::UnsignedByte;
400  break;
401  case 16:
402  type = Isis::SignedWord;
403  break;
404  case 32:
405  type = Isis::SignedInteger;
406  break;
407  case -32:
408  type = Isis::Real;
409  break;
410  case 64:
411  msg = "Signed 64-bit integer (long) pixel type is not supported for FITS imports.";
413  break;
414  case -64:
415  type = Isis::Double;
416  break;
417  default:
418  msg = "Unknown pixel type [" + label["BITPIX"][0] + "] is not supported for FITS imports.";
420  break;
421  }
422 
423  SetPixelType(type);
424 
425  // It is possible to have a NAXIS value of 0 meaning no data, the file could include
426  // xtensions with data, however, those aren't supported because we need the code to know
427  // how to skip over them.
428  // NOTE: FITS files, at least the ones seen till now, do not specify a line prefix or suffix
429  // data byte count. Some FITS files do have them and ISIS needs to remove them so it is not
430  // considered part of the DNs. So, use the parent class' prefix/suffix byte count to reduce
431  // the number of samples.
432  if (Organization() == BSQ) {
433  if (toInt(label["NAXIS"][0]) == 2) {
434  SetDimensions(toInt(label["NAXIS1"][0])
435  - (DataPrefixBytes()+DataSuffixBytes())/SizeOf(type),
436  toInt(label["NAXIS2"][0]), 1);
437  }
438  else if (toInt(label["NAXIS"][0]) == 3) {
439  SetDimensions(toInt(label["NAXIS1"][0])
440  - (DataPrefixBytes()+DataSuffixBytes())/SizeOf(type),
441  toInt(label["NAXIS2"][0]), toInt(label["NAXIS3"][0]));
442  }
443  else {
444  QString msg = "NAXIS count of ["
445  + label["NAXIS"][0]
446  + "] is not supported for FITS imports.";
448  }
449  }
450  else if (Organization() == BIL) {
451  if (toInt(label["NAXIS"][0]) == 2) {
452  SetDimensions(toInt(label["NAXIS1"][0])
453  - (DataPrefixBytes()+DataSuffixBytes())/SizeOf(type),
454  1, toInt(label["NAXIS2"][0]));
455  }
456  else if (toInt(label["NAXIS"][0]) == 3) {
457  SetDimensions(toInt(label["NAXIS1"][0])
458  - (DataPrefixBytes()+DataSuffixBytes())/SizeOf(type),
459  toInt(label["NAXIS3"][0]), toInt(label["NAXIS2"][0]));
460  }
461  else {
462  QString msg = "NAXIS count of ["
463  + label["NAXIS"][0]
464  + "] is not supported for FITS imports.";
466  }
467  }
468  else if (Organization() == BIP) {
469  QString msg = "BIP (Band Interleaved by Pixel) "
470  "organization is not supported for FITS imports.";
472  }
473  else {
474  QString msg = "Unknown organization is not supported for FITS imports.";
476  }
477 
478  // Base and multiplier
479  if (label.hasKeyword("BZERO")) {
480  SetBase(toDouble(label["BZERO"][0]));
481  }
482  else {
483  SetBase(0.0);
484  }
485  if (label.hasKeyword("BSCALE")) {
486  SetMultiplier(toDouble(label["BSCALE"][0]));
487  }
488  else {
489  SetMultiplier(1.0);
490  }
491 
492  // Byte order
493  SetByteOrder(Isis::Msb);
494 
495  }
496 
497 } // end namespace Isis
bool hasKeyword(const QString &name) const
Check to see if a keyword exists.
QList< PvlGroup * > * m_fitsImageLabels
Holds the PvlGroups with the converted FITS image labels from the main and all extensions.
ProcessImportFits()
Constructor for ProcessImportFits.
File name manipulation and expansion.
Definition: FileName.h:116
int SizeOf(Isis::PixelType pixelType)
Returns the number of bytes of the specified PixelType.
Definition: PixelType.h:62
int DataSuffixBytes() const
This method returns the number of data duffix bytes.
int toInt(const QString &string)
Global function to convert from a string to an integer.
Definition: IString.cpp:108
void setName(QString name)
Sets the keyword name.
Definition: PvlKeyword.cpp:136
IString TrimHead(const std::string &chars)
Trims The input characters from the beginning of the object IString.
Definition: IString.cpp:573
void addKeyword(const PvlKeyword &keyword, const InsertMode mode=Append)
Add a keyword to the container.
void setProcessFileStructure(int labelNumber)
Sets the Process file structure parameters based on the given image label index.
$Revision: $ $Date:
double toDouble(const QString &string)
Global function to convert from a string to a double.
Definition: IString.cpp:164
This error is for when a programmer made an API call that was illegal.
Definition: IException.h:162
void setName(const QString &name)
Set the name of the container.
Definition: PvlContainer.h:70
FileName m_name
The name of the input FITS file.
QString InputFile()
Sets the name of the input file to be read in the import StartProcess method and verifies its existan...
void SetDimensions(const int ns, const int nl, const int nb)
Sets the physical size of the input cube.
PixelType
Enumerations for Isis Pixel Types.
Definition: PixelType.h:43
IString Token(const IString &separator)
Returns the first token in the IString.
Definition: IString.cpp:912
QList< int > * m_headerSizes
The number, or count, of 2880 byte header records for each image header section.
Interleave Organization() const
Gets the organization of the input cube.
Contains multiple PvlContainers.
Definition: PvlGroup.h:57
#define _FILEINFO_
Macro for the filename and line number.
Definition: IException.h:40
A type of error that could only have occurred due to a mistake on the user&#39;s part (e...
Definition: IException.h:142
A single keyword-value pair.
Definition: PvlKeyword.h:98
QString ToQt() const
Retuns the object string as a QString.
Definition: IString.cpp:884
QString expanded() const
Returns a QString of the full file name including the file path, excluding the attributes.
Definition: FileName.cpp:212
PvlGroup fitsImageLabel(int labelNumber) const
Supplies the FITS image label corresponding to the given index.
void SetPixelType(const Isis::PixelType type)
Sets the pixel type of the input file.
QList< int > * m_dataStarts
The starting byte of the data for each image.
void extractFitsLabels()
Extract all the FITS labels from the file.
PvlKeyword & findKeyword(const QString &name)
Find a keyword with a specified name.
Band Sequential Format (i.e.
Band Interleaved By Pixel Format (i.e.
PvlGroup extraFitsLabel(int labelNumber) const
Supplies the extra FITS label corresponding to the given index.
QString toString() const
Returns a QString of the full file name including the file path, excluding the attributes with any Is...
Definition: FileName.cpp:531
int DataPrefixBytes() const
This method returns the number of data prefix bytes.
void setFitsFile(FileName fitsFile)
Opens a FITS image file with header and reads the FITS labels.
Isis exception class.
Definition: IException.h:107
Adds specific functionality to C++ strings.
Definition: IString.h:181
Namespace for ISIS/Bullet specific routines.
Definition: Apollo.h:31
void SetMultiplier(const double mult)
Sets the core multiplier of the input cube.
QList< PvlGroup * > * m_extraFitsLabels
Holds the PvlGroups with the converted extra FITS labels from the main and all extensions.
virtual ~ProcessImportFits()
Destructor for ProcessImportFits.
void SetFileHeaderBytes(const int bytes)
This method sets the number of bytes in the header of a file.
void SetBase(const double base)
Sets the core base of the input cube.
void SetInputFile(const QString &file)
Sets the name of the input file to be read in the import StartProcess method and verifies its existan...
PvlGroup standardInstrumentGroup(PvlGroup fitsLabel) const
Return a PVL instrument group populated with expected default values.
std::ifstream m_file
The stream used to read the FITS file.
IString TrimTail(const std::string &chars)
Trims the input characters from the end of the object IString.
Definition: IString.cpp:602
void SetByteOrder(const Isis::ByteOrder order)
Sets the byte order of the input file.
Band Interleaved By Line Format (i.e.