Isis 3 Programmer Reference
ProcessImportFits.cpp
1 
6 /* SPDX-License-Identifier: CC0-1.0 */
7 #include "ProcessImportFits.h"
8 
9 #include <QDebug>
10 #include <QString>
11 
12 #include <iostream>
13 #include <math.h>
14 #include <sstream>
15 
16 #include "IException.h"
17 #include "IString.h"
18 #include "LineManager.h"
19 #include "Preference.h"
20 #include "Pvl.h"
21 #include "PvlGroup.h"
22 #include "PixelType.h"
23 #include "SpecialPixel.h"
24 #include "UserInterface.h"
25 
26 namespace Isis {
27 
32  m_fitsImageLabels = NULL;
33  m_extraFitsLabels = NULL;
34  m_headerSizes = NULL;
35  m_dataStarts = NULL;
36  }
37 
38 
43  delete m_fitsImageLabels;
44  delete m_extraFitsLabels;
45  delete m_headerSizes;
46  delete m_dataStarts;
47  m_file.close();
48  }
49 
50 
57 
62 
63  // Process each FITS label area, storing each in its own PvlGroup.
64  char readBuf[81];
65  IString line = "";
66  unsigned int place;
67 
68  // The main FITS label starts at the beginning of the file.
69  // FITS extension labels start after the previous data and on a 2880 byte boundry
70  // Each FITS keyword in all lables is stored in 80 bytes (space padded to 80 if
71  // necessary).
72 
73  // Start at the beginning of the file for the main FITS label
74  m_file.seekg(0, std::ios_base::beg);
75 
76  // Read the first label line (80 chars)
77  // We are assuming the file pointer is set to the beginning of the first/next label
78  while (m_file.read(readBuf, 80) && m_file.gcount() == 80) {
79 
80  PvlGroup *fitsLabel = new PvlGroup("FitsLabels");
81 
82  readBuf[80] = '\0';
83  line = readBuf;
84  place = 80;
85 
86  // Process each fits label record (80 bytes) and place keyword, value pairs into PvlKeywords
87  // with any associated comments.
88  while (line.substr(0, 3) != "END") {
89 
90  // Check for blank lines
91  if (line.substr(0, 1) != " " && line.substr(0, 1) != "/") {
92  // Name of keyword
93  PvlKeyword label(line.Token(" =").ToQt()); // Stop on spaces OR equal sign
94  if (QString::compare(label.name(), "OBJECT", Qt::CaseInsensitive) == 0) {
95  label.setName("TARGET");
96  label.addComment("NOTE: This keyword name was changed from 'OBJECT' in the original "
97  "fit header file.");
98  }
99  // Remove up to beginning of data
100  line.TrimHead(" =");
101  line.TrimTail(" ");
102  if (label.name() == "COMMENT" || label.name() == "HISTORY") {
103  label += line.ToQt();
104  }
105  else {
106  // Check for a quoted value
107  if (line.substr(0,1) == "'") {
108  line.TrimHead("'");
109  label += line.Token("'").TrimHead(" ").TrimTail(" ").ToQt();
110  line.TrimHead(" '");
111  }
112  else {
113  // Access any remaining data without the trailing comment if there is one
114  IString value = line.Token("/");
115  // Clear to end of data
116  value.TrimTail(" ");
117  label += value.ToQt();
118  line.TrimHead(" ");
119  }
120  // If the line still has anything in it, treat it is as a comment.
121  if (line.size() > 0) {
122  line.TrimHead(" /");
123  label.addComment(line.ToQt());
124  // A possible format for units, other possiblites exist.
125  if (line != line.Token("[")) {
126  label.setUnits(line.Token("[").Token("]").ToQt());
127  }
128  }
129  }
130  fitsLabel->addKeyword(label);
131  }
132 
133  // Read the next label line
134  m_file.read(readBuf, 80);
135  readBuf[80] = '\0';
136  line = readBuf;
137  place += 80;
138  }
139 
140  // Save off the PvlGroup and the number of records read from this label
141  m_fitsImageLabels->append(fitsLabel);
142  m_headerSizes->append((int)ceil(place / 2880.0));
143 
144  // The file pointer should be pointing at the end of the record that contained "END"
145  // Move the file pointer past the padding after the "END" (i.e., points to start of data)
146  std::streamoff jump;
147  jump = m_headerSizes->last() * 2880 - place;
148  m_file.seekg(jump, std::ios_base::cur);
149 
150  m_dataStarts->append(m_file.tellg());
151 
152  // Does this look like a label for a FITS image? Stop after the first label that does not
153  // because we don't know how to move the file pointer past a non-image data extension.
154  if (fitsLabel->hasKeyword("BITPIX") && fitsLabel->hasKeyword("NAXIS")) {
155 
156  // This section can only handle image data
157  // (i.e., keywords BITPIX & NAXIS & NAXISx must exist).
158  if((int)fitsLabel->findKeyword("NAXIS") > 0) {
159  int bytesPerPixel = 0;
160  bytesPerPixel = (int)((*fitsLabel)["BITPIX"]);
161  bytesPerPixel = std::abs(bytesPerPixel);
162  bytesPerPixel /= 8;
163 
164  unsigned int axis1 = 1;
165  axis1 = toInt((*fitsLabel)["NAXIS1"]);
166 
167  unsigned int axis2 = 1;
168  if (fitsLabel->hasKeyword("NAXIS2")) {
169  axis2 = toInt((*fitsLabel)["NAXIS2"]);
170  }
171 
172  unsigned int axis3 = 1;
173  if (fitsLabel->hasKeyword("NAXIS3")) {
174  axis3 = toInt((*fitsLabel)["NAXIS3"]);
175  }
176 
177  jump = (int)(ceil(bytesPerPixel * axis1 * axis2 * axis3 / 2880.0) * 2880.0);
178  m_file.seekg(jump, std::ios_base::cur);
179  }
180  else {
181  // Note: this will allow us to read extra label sections that have 0 axes,
182  // but has image-related info (so BITPIX and NAXIS keywords exist). This
183  // includes informational labels, as seen at the beginning of hayabusa2
184  // images in this case, there is NO DATA, so no jump should be needed to
185  // get to the next section.
186  PvlGroup *extraLabelGroup = m_fitsImageLabels->last();
187  extraLabelGroup->setName("FitsExtras");
188  m_extraFitsLabels->append(extraLabelGroup);
189 
190  m_fitsImageLabels->removeLast();
191  m_headerSizes->removeLast();
192  m_dataStarts->removeLast();
193  }
194  }
195  // Do we have at least on header that looks like it has image data? If so, we can continue,
196  // but ignore the rest of the file because we don't know how to skip over a non-image data.
197  else if (m_fitsImageLabels->size() > 1) {
198  m_fitsImageLabels->removeLast();
199  m_headerSizes->removeLast();
200  m_dataStarts->removeLast();
201  break;
202  }
203 
204  else {
205  QString msg = QObject::tr("The FITS file does not contain a section header that appears "
206  "to describe an image [%1].").arg(m_name.toString());
207  throw IException(IException::User, msg, _FILEINFO_);
208  }
209  }
210  }
211 
212 
225  if (labelNumber >= m_extraFitsLabels->size()) {
226  QString msg = QObject::tr("The requested label number [%1], from file [%2] is "
227  "past the last extra group found in this FITS file. "
228  "Extra label count is [%3]").arg(labelNumber).
229  arg(m_name.expanded()).arg(m_extraFitsLabels->size()-1);
230  throw IException(IException::User, msg, _FILEINFO_);
231  }
232 
233  if (!m_extraFitsLabels) {
234  QString msg = QObject::tr("The FITS label has not been initialized, "
235  "call setFitsFile() first.");
236  throw IException(IException::Programmer, msg, _FILEINFO_);
237  }
238  else if (m_extraFitsLabels->size() < labelNumber) {
239  QString msg = QObject::tr("The requested FITS label number "
240  "was not found in file [%1].").arg(m_name.toString());
241  throw IException(IException::Programmer, msg, _FILEINFO_);
242  }
243 
244  return *(*m_extraFitsLabels)[labelNumber];
245  }
246 
247 
260 
261  if (labelNumber >= m_fitsImageLabels->size()) {
262  QString msg = QObject::tr("The requested label number [%1], from file [%2] is "
263  "past the last image group found in this FITS file. "
264  "Image label count is [%3]").arg(labelNumber).
265  arg(m_name.expanded()).arg(m_fitsImageLabels->size()-1);
266  throw IException(IException::User, msg, _FILEINFO_);
267  }
268 
269  if (!m_fitsImageLabels) {
270  QString msg = QObject::tr("The FITS label has not been initialized, "
271  "call setFitsFile first.");
272  throw IException(IException::Programmer, msg, _FILEINFO_);
273  }
274  else if (m_fitsImageLabels->size() < labelNumber) {
275  QString msg = QObject::tr("The requested FITS label number "
276  "was not found in file [%1].").arg(m_name.toString());
277  throw IException(IException::Programmer, msg, _FILEINFO_);
278  }
279 
280  return *(*m_fitsImageLabels)[labelNumber];
281  }
282 
283 
296 
297  // NOTE: This needs to be changed over to use translation files
298 
299  // Attempt to extract the standard instrument group keywords
300  PvlGroup inst("Instrument");
301  if (fitsLabel.hasKeyword("DATE-OBS")) {
302  inst += PvlKeyword("StartTime", fitsLabel["DATE-OBS"][0]);
303  }
304  if (fitsLabel.hasKeyword("TARGET")) {
305  inst += PvlKeyword("Target", fitsLabel["TARGET"][0]);
306  }
307  if (fitsLabel.hasKeyword("INSTRUME")) {
308  inst += PvlKeyword("InstrumentId", fitsLabel["INSTRUME"][0]);
309  }
310  if (fitsLabel.hasKeyword("OBSERVER")) {
311  inst += PvlKeyword("SpacecraftName", fitsLabel["OBSERVER"][0]);
312  }
313 
314  return inst;
315  }
316 
317 
325  m_name = fitsFile;
326 
327  SetInputFile(fitsFile.toString()); // Make sure the file exists
328 
329  m_file.open(fitsFile.expanded().toLocal8Bit().constData(), std::ios::in | std::ios::binary);
330 
331  if (!m_file.is_open()) {
332  QString msg = QObject::tr("Unable to open FITS formatted file [%1].")
333  .arg(fitsFile.toString());
334  throw IException(IException::User, msg, _FILEINFO_);
335  }
336 
337  // Get the FITS labels internalized
339 
340  // Check to make sure it is a FITS file we can handle
341  PvlGroup label = fitsImageLabel(0);
342  if (label.hasKeyword("SIMPLE") && label["SIMPLE"][0] == "F") {
343  QString msg = QObject::tr("The file [%1] cannot be processed. "
344  "It is an unsupported format.").
345  arg(fitsFile.toString());
346  throw IException(IException::User, msg, _FILEINFO_);
347  }
348 
349  m_file.close();
350  }
351 
352 
364 
365  if (labelNumber >= m_fitsImageLabels->size()) {
366  QString msg = QObject::tr("The requested label number [%1], from file [%2] is "
367  "past the last image in this FITS file [%3].").arg(labelNumber).
368  arg(InputFile()).arg(m_fitsImageLabels->size()-1);
369  throw IException(IException::User, msg, _FILEINFO_);
370  }
371 
372  PvlGroup label = *(*m_fitsImageLabels)[labelNumber];
373 
374  // Set the ProcessImport to skip over all the previous images and their labels and the label for
375  // this image. Don't save this info (think memory)
376  SetFileHeaderBytes((*m_dataStarts)[labelNumber]);
377 
378  // Find pixel type. NOTE: There are several unsupported possiblites
379  Isis::PixelType type;
380  QString msg = "";
381  switch (toInt(label["BITPIX"][0])) {
382  case 8:
383  type = Isis::UnsignedByte;
384  break;
385  case 16:
386  type = Isis::SignedWord;
387  break;
388  case 32:
389  type = Isis::SignedInteger;
390  break;
391  case -32:
392  type = Isis::Real;
393  break;
394  case 64:
395  msg = "Signed 64-bit integer (long) pixel type is not supported for FITS imports.";
396  throw IException(IException::User, msg, _FILEINFO_);
397  break;
398  case -64:
399  type = Isis::Double;
400  break;
401  default:
402  msg = "Unknown pixel type [" + label["BITPIX"][0] + "] is not supported for FITS imports.";
403  throw IException(IException::User, msg, _FILEINFO_);
404  break;
405  }
406 
407  SetPixelType(type);
408 
409  // It is possible to have a NAXIS value of 0 meaning no data, the file could include
410  // xtensions with data, however, those aren't supported because we need the code to know
411  // how to skip over them.
412  // NOTE: FITS files, at least the ones seen till now, do not specify a line prefix or suffix
413  // data byte count. Some FITS files do have them and ISIS needs to remove them so it is not
414  // considered part of the DNs. So, use the parent class' prefix/suffix byte count to reduce
415  // the number of samples.
416  if (Organization() == BSQ) {
417  if (toInt(label["NAXIS"][0]) == 2) {
418  SetDimensions(toInt(label["NAXIS1"][0])
419  - (DataPrefixBytes()+DataSuffixBytes())/SizeOf(type),
420  toInt(label["NAXIS2"][0]), 1);
421  }
422  else if (toInt(label["NAXIS"][0]) == 3) {
423  SetDimensions(toInt(label["NAXIS1"][0])
424  - (DataPrefixBytes()+DataSuffixBytes())/SizeOf(type),
425  toInt(label["NAXIS2"][0]), toInt(label["NAXIS3"][0]));
426  }
427  else {
428  QString msg = "NAXIS count of ["
429  + label["NAXIS"][0]
430  + "] is not supported for FITS imports.";
431  throw IException(IException::User, msg, _FILEINFO_);
432  }
433  }
434  else if (Organization() == BIL) {
435  if (toInt(label["NAXIS"][0]) == 2) {
436  SetDimensions(toInt(label["NAXIS1"][0])
437  - (DataPrefixBytes()+DataSuffixBytes())/SizeOf(type),
438  1, toInt(label["NAXIS2"][0]));
439  }
440  else if (toInt(label["NAXIS"][0]) == 3) {
441  SetDimensions(toInt(label["NAXIS1"][0])
442  - (DataPrefixBytes()+DataSuffixBytes())/SizeOf(type),
443  toInt(label["NAXIS3"][0]), toInt(label["NAXIS2"][0]));
444  }
445  else {
446  QString msg = "NAXIS count of ["
447  + label["NAXIS"][0]
448  + "] is not supported for FITS imports.";
449  throw IException(IException::User, msg, _FILEINFO_);
450  }
451  }
452  else if (Organization() == BIP) {
453  QString msg = "BIP (Band Interleaved by Pixel) "
454  "organization is not supported for FITS imports.";
455  throw IException(IException::User, msg, _FILEINFO_);
456  }
457  else {
458  QString msg = "Unknown organization is not supported for FITS imports.";
459  throw IException(IException::User, msg, _FILEINFO_);
460  }
461 
462  // Base and multiplier
463  if (label.hasKeyword("BZERO")) {
464  SetBase(toDouble(label["BZERO"][0]));
465  }
466  else {
467  SetBase(0.0);
468  }
469  if (label.hasKeyword("BSCALE")) {
470  SetMultiplier(toDouble(label["BSCALE"][0]));
471  }
472  else {
473  SetMultiplier(1.0);
474  }
475 
476  // Byte order
477  SetByteOrder(Isis::Msb);
478 
479  }
480 
481 } // end namespace Isis
Isis::SizeOf
int SizeOf(Isis::PixelType pixelType)
Returns the number of bytes of the specified PixelType.
Definition: PixelType.h:46
Isis::ProcessImportFits::ProcessImportFits
ProcessImportFits()
Constructor for ProcessImportFits.
Definition: ProcessImportFits.cpp:31
Isis::PvlKeyword::name
QString name() const
Returns the keyword name.
Definition: PvlKeyword.h:98
Isis::ProcessImportFits::m_fitsImageLabels
QList< PvlGroup * > * m_fitsImageLabels
Holds the PvlGroups with the converted FITS image labels from the main and all extensions.
Definition: ProcessImportFits.h:55
Isis::PvlKeyword
A single keyword-value pair.
Definition: PvlKeyword.h:82
QList
This is free and unencumbered software released into the public domain.
Definition: BoxcarCachingAlgorithm.h:13
Isis::IString::TrimTail
IString TrimTail(const std::string &chars)
Trims the input characters from the end of the object IString.
Definition: IString.cpp:587
Isis::ProcessImport::SetBase
void SetBase(const double base)
Sets the core base of the input cube.
Definition: ProcessImport.cpp:938
Isis::ProcessImport::InputFile
QString InputFile()
Sets the name of the input file to be read in the import StartProcess method and verifies its existan...
Definition: ProcessImport.cpp:2319
Isis::ProcessImport::DataSuffixBytes
int DataSuffixBytes() const
This method returns the number of data duffix bytes.
Definition: ProcessImport.cpp:729
Isis::PvlContainer::setName
void setName(const QString &name)
Set the name of the container.
Definition: PvlContainer.h:56
Isis::PvlContainer::addKeyword
void addKeyword(const PvlKeyword &keyword, const InsertMode mode=Append)
Add a keyword to the container.
Definition: PvlContainer.cpp:202
Isis::FileName
File name manipulation and expansion.
Definition: FileName.h:100
Isis::ProcessImport::BIL
@ BIL
Band Interleaved By Line Format (i.e.
Definition: ProcessImport.h:284
Isis::ProcessImportFits::setProcessFileStructure
void setProcessFileStructure(int labelNumber)
Sets the Process file structure parameters based on the given image label index.
Definition: ProcessImportFits.cpp:363
Isis::PvlContainer::hasKeyword
bool hasKeyword(const QString &name) const
Check to see if a keyword exists.
Definition: PvlContainer.cpp:159
Isis::PvlKeyword::setUnits
void setUnits(QString units)
Sets the unit of measure for all current values if any exist.
Definition: PvlKeyword.cpp:166
Isis::ProcessImportFits::m_name
FileName m_name
The name of the input FITS file.
Definition: ProcessImportFits.h:61
Isis::PvlKeyword::setName
void setName(QString name)
Sets the keyword name.
Definition: PvlKeyword.cpp:120
Isis::ProcessImport::SetDimensions
void SetDimensions(const int ns, const int nl, const int nb)
Sets the physical size of the input cube.
Definition: ProcessImport.cpp:304
Isis::FileName::expanded
QString expanded() const
Returns a QString of the full file name including the file path, excluding the attributes.
Definition: FileName.cpp:196
Isis::PvlGroup
Contains multiple PvlContainers.
Definition: PvlGroup.h:41
Isis::ProcessImport::DataPrefixBytes
int DataPrefixBytes() const
This method returns the number of data prefix bytes.
Definition: ProcessImport.cpp:721
Isis::toInt
int toInt(const QString &string)
Global function to convert from a string to an integer.
Definition: IString.cpp:93
Isis::ProcessImportFits::fitsImageLabel
PvlGroup fitsImageLabel(int labelNumber) const
Supplies the FITS image label corresponding to the given index.
Definition: ProcessImportFits.cpp:259
Isis::ProcessImport::SetInputFile
void SetInputFile(const QString &file)
Sets the name of the input file to be read in the import StartProcess method and verifies its existan...
Definition: ProcessImport.cpp:2302
Isis::ProcessImportFits::m_headerSizes
QList< int > * m_headerSizes
The number, or count, of 2880 byte header records for each image header section.
Definition: ProcessImportFits.h:63
Isis::ProcessImport::Organization
Interleave Organization() const
Gets the organization of the input cube.
Definition: ProcessImport.cpp:928
Isis::PvlKeyword::addComment
void addComment(QString comment)
Add a comment to the PvlKeyword.
Definition: PvlKeyword.cpp:376
Isis::ProcessImportFits::m_dataStarts
QList< int > * m_dataStarts
The starting byte of the data for each image.
Definition: ProcessImportFits.h:65
Isis::ProcessImport::SetPixelType
void SetPixelType(const Isis::PixelType type)
Sets the pixel type of the input file.
Definition: ProcessImport.cpp:264
Isis::IException
Isis exception class.
Definition: IException.h:91
Isis::FileName::toString
QString toString() const
Returns a QString of the full file name including the file path, excluding the attributes with any Is...
Definition: FileName.cpp:515
Isis::ProcessImportFits::extraFitsLabel
PvlGroup extraFitsLabel(int labelNumber) const
Supplies the extra FITS label corresponding to the given index.
Definition: ProcessImportFits.cpp:224
Isis::ProcessImportFits::extractFitsLabels
void extractFitsLabels()
Extract all the FITS labels from the file.
Definition: ProcessImportFits.cpp:56
Isis::PixelType
PixelType
Enumerations for Isis Pixel Types.
Definition: PixelType.h:27
Isis::toDouble
double toDouble(const QString &string)
Global function to convert from a string to a double.
Definition: IString.cpp:149
Isis::IException::Programmer
@ Programmer
This error is for when a programmer made an API call that was illegal.
Definition: IException.h:146
Isis::ProcessImport::SetMultiplier
void SetMultiplier(const double mult)
Sets the core multiplier of the input cube.
Definition: ProcessImport.cpp:959
Isis::ProcessImportFits::~ProcessImportFits
virtual ~ProcessImportFits()
Destructor for ProcessImportFits.
Definition: ProcessImportFits.cpp:42
Isis::IString::Token
IString Token(const IString &separator)
Returns the first token in the IString.
Definition: IString.cpp:897
Isis::ProcessImport::SetByteOrder
void SetByteOrder(const Isis::ByteOrder order)
Sets the byte order of the input file.
Definition: ProcessImport.cpp:324
Isis::ProcessImport::BSQ
@ BSQ
Band Sequential Format (i.e.
Definition: ProcessImport.h:281
Isis::ProcessImportFits::setFitsFile
void setFitsFile(FileName fitsFile)
Opens a FITS image file with header and reads the FITS labels.
Definition: ProcessImportFits.cpp:324
Isis::ProcessImport::SetFileHeaderBytes
void SetFileHeaderBytes(const int bytes)
This method sets the number of bytes in the header of a file.
Definition: ProcessImport.cpp:349
Isis::IString
Adds specific functionality to C++ strings.
Definition: IString.h:165
Isis::PvlContainer::findKeyword
PvlKeyword & findKeyword(const QString &name)
Find a keyword with a specified name.
Definition: PvlContainer.cpp:62
Isis::IString::TrimHead
IString TrimHead(const std::string &chars)
Trims The input characters from the beginning of the object IString.
Definition: IString.cpp:558
Isis::ProcessImportFits::m_extraFitsLabels
QList< PvlGroup * > * m_extraFitsLabels
Holds the PvlGroups with the converted extra FITS labels from the main and all extensions.
Definition: ProcessImportFits.h:57
Isis::ProcessImportFits::m_file
std::ifstream m_file
The stream used to read the FITS file.
Definition: ProcessImportFits.h:62
Isis::ProcessImportFits::standardInstrumentGroup
PvlGroup standardInstrumentGroup(PvlGroup fitsLabel) const
Return a PVL instrument group populated with expected default values.
Definition: ProcessImportFits.cpp:295
Isis
This is free and unencumbered software released into the public domain.
Definition: Apollo.h:16
Isis::ProcessImport::BIP
@ BIP
Band Interleaved By Pixel Format (i.e.
Definition: ProcessImport.h:288
Isis::IString::ToQt
QString ToQt() const
Retuns the object string as a QString.
Definition: IString.cpp:869
Isis::IException::User
@ User
A type of error that could only have occurred due to a mistake on the user's part (e....
Definition: IException.h:126