USGS

Isis 3.0 Object Programmers' Reference

Home

ImportPdsTable.cpp
Go to the documentation of this file.
1 
22 #include "ImportPdsTable.h"
23 
24 #include <cctype>
25 #include <iomanip>
26 #include <iostream>
27 #include <sstream>
28 
29 #include <QDebug>
30 #include <QString>
31 
32 #include "EndianSwapper.h"
33 #include "FileName.h"
34 #include "IString.h"
35 #include "Pvl.h"
36 #include "PvlObject.h"
37 #include "Table.h"
38 #include "TableField.h"
39 #include "TableRecord.h"
40 #include "TextFile.h"
41 
42 
43 using namespace std;
44 
45 namespace Isis {
46 
47 
57  ImportPdsTable::ImportPdsTable() {
58  // just inititalize the member variables
59  init();
60  m_tableName = "TABLE";
61  }
62 
82  ImportPdsTable::ImportPdsTable(const QString &pdsLabFile,
83  const QString &pdsTableFile,
84  const QString &pdsTableName) {
85  m_tableName = pdsTableName;
86  load(pdsLabFile, pdsTableFile);
87  }
88 
92  ImportPdsTable::~ImportPdsTable() {
93  }
94 
115  void ImportPdsTable::load(const QString &pdsLabFile,
116  const QString &pdsTableFile) {
117 
118  init();
119  QString tempTblFile;
120  loadLabel(pdsLabFile, tempTblFile);
121  if (!pdsTableFile.isEmpty()) tempTblFile = pdsTableFile;
122  // Vet the table filename. Many PDS files record the filename in
123  // uppercase and in practice, the filename is in lowercase. Do this
124  // check here.
125  FileName tableFile(tempTblFile);
126  try {
127  int tableStartRecord = toInt(tableFile.baseName());
128  tempTblFile = pdsLabFile;
129  m_pdsTableStart = tableStartRecord;
130  }
131  catch (IException &e) {
132  // if we are unable to cast the table file value to an integer, it must be a
133  // file name, not a location in the label file.
134  if (!tableFile.fileExists()) {
135  // if the table file name doesn't exist, try lowercased version...
136  FileName tableFileLowercase(tableFile.path() + "/"
137  + tableFile.name().toLower());
138  if (!tableFileLowercase.fileExists()) {
139  IString msg = "Unable to import PDS table. Neither of the following "
140  "possible table files were found: ["
141  + tableFile.expanded() + "] or ["
142  + tableFileLowercase.expanded() + "]";
143  throw IException(e, IException::Unknown, msg, _FILEINFO_);
144  }
145  tableFile = tableFileLowercase.expanded();
146  tempTblFile = tableFile.expanded();
147  }
148  m_pdsTableStart = 1;
149  }
150  if (m_pdsTableType == "ASCII") {
151  loadTable(tempTblFile);
152  }
153  m_pdsTableFile = tempTblFile;
154  return;
155  }
156 
167  bool ImportPdsTable::hasColumn(const QString &colName) const {
168  return (findColumn(colName) != 0);
169  }
170 
171 
189  QString ImportPdsTable::getColumnName(const unsigned int &index,
190  const bool &formatted) const {
191  if ((int) index >= columns() - 1) {
192  QString msg = "Unable to import the binary PDS table [" + m_tableName
193  + "] into Isis. The requested column index ["
194  + toString((int) index) + "] exceeds the last column index ["
195  + toString(columns() - 1) + "]";
196  throw IException(IException::Programmer, msg.toStdString(), _FILEINFO_);
197  }
198  QString name = m_coldesc[index].m_name;
199  if (formatted) name = getFormattedName(name);
200  return (name);
201  }
202 
219  QStringList ImportPdsTable::getColumnNames(const bool &formatted)
220  const {
221  QStringList colnames;
222  for (int i = 0 ; i < columns() ; i++) {
223  QString name = m_coldesc[i].m_name;
224  if (formatted) name = getFormattedName(name);
225  colnames.push_back(name);
226  }
227  return (colnames);
228  }
229 
245  QString ImportPdsTable::getType(const QString &colName) const {
246  const ColumnDescr *column = findColumn(colName);
247  QString dtype("");
248  if (column != 0) {
249  dtype = column->m_dataType;
250  }
251  return (dtype);
252  }
253 
270  bool ImportPdsTable::setType(const QString &colName,
271  const QString &dataType) {
272  ColumnDescr *column = findColumn(colName);
273  if (column != 0) {
274  column->m_dataType = dataType.toUpper();
275  }
276  return (column != 0);
277  }
278 
290  Table ImportPdsTable::importTable(const QString &isisTableName) {
291  try {
292  TableRecord record = makeRecord(m_coldesc);
293  Table table(isisTableName, record);
294  fillTable(table, m_coldesc, record);
295  return (table);
296  }
297  catch (IException &e) {
298  QString msg = "Unable to import the PDS table [" + m_tableName
299  + "] from the PDS file [" + m_pdsTableFile + "] into Isis.";
300  throw IException(e, IException::Unknown, msg.toStdString(), _FILEINFO_);
301 
302  }
303  }
304 
305 
322  Table ImportPdsTable::importTable(const QString &colnames,
323  const QString &isisTableName) {
324  return (importTable(colnames.split(","), isisTableName));
325  }
326 
341  Table ImportPdsTable::importTable(const QStringList &colnames,
342  const QString &isisTableName) {
343  ColumnTypes ctypes;
344  for (int i = 0 ; i < colnames.size() ; i++) {
345  const ColumnDescr *descr = findColumn(colnames[i]);
346  if (!descr) {
347  QString msg = "Unable to import the PDS table [" + m_tableName
348  + "] into Isis. The requested column name ["
349  + colnames[i] + "] does not "
350  "exist in table.";
351  throw IException(IException::Programmer, msg.toStdString(), _FILEINFO_);
352  }
353  ctypes.push_back(*descr);
354  }
355 
356  // Create and populate the table
357  TableRecord record = makeRecord(ctypes);
358  Table table(isisTableName, record);
359  fillTable(table, ctypes, record);
360  return (table);
361  }
362 
368  void ImportPdsTable::init() {
369 
370  m_byteOrder = "";
371  m_trows = 0;
372  m_pdsTableStart = 0;
373  m_coldesc.clear();
374  m_rows.clear();
375  m_pdsTableType = "";
376  m_pdsTableFile = "";
377  return;
378  }
379 
393  void ImportPdsTable::loadLabel(const QString &pdsLabFile,
394  QString &pdsTableFile) {
395 
396  Isis::Pvl label(pdsLabFile);
397 
398  if (!label.hasObject(m_tableName)) {
399  QString msg = "The PDS file " + pdsLabFile +
400  " does not have the required TABLE object, ["
401  + m_tableName +"]. The PDS label file is probably invalid";
402  throw IException(IException::Unknown, msg.toStdString(), _FILEINFO_);
403  }
404  // Get some pertinent information from the label
405  PvlObject &tabObj = label.findObject(m_tableName);
406  if (tabObj.hasKeyword("RECORD_BYTES")) {
407  m_recordBytes = (int) tabObj.findKeyword("RECORD_BYTES");
408  }
409  else {
410  m_recordBytes = (int) label.findKeyword("RECORD_BYTES");
411  }
412 
413  QString trueTableName;
414  PvlObject *tableDetails = &tabObj;
415  if (label.hasKeyword("^" + m_tableName)) {
416  trueTableName = m_tableName;
417  pdsTableFile = FileName(pdsLabFile).path() + "/"
418  + label["^" + m_tableName][0];
419  }
420  else if (tabObj.objects() == 1) {
421  trueTableName = tabObj.object(0).name();
422  tableDetails = &tabObj.object(0);
423  pdsTableFile = FileName(pdsLabFile).path() + "/"
424  + tabObj["^" + trueTableName][0];
425  }
426  m_trows = (int) tableDetails->findKeyword("ROWS");
427  int ncols = (int) tableDetails->findKeyword("COLUMNS");
428  m_pdsTableType = QString(tableDetails->findKeyword("INTERCHANGE_FORMAT"));
429  if (m_pdsTableType != "ASCII" && m_pdsTableType.toUpper() != "BINARY") {
430  QString msg = "Unable to import the PDS table [" + m_tableName
431  + "] from the PDS file ["
432  + pdsTableFile + "] into Isis. "
433  "The PDS INTERCHANGE_FORMAT [" + m_pdsTableType
434  + "] is not supported. Valid values are ASCII or BINARY.";
435  throw IException(IException::User, msg.toStdString(), _FILEINFO_);
436  }
437  m_rowBytes = tableDetails->findKeyword("ROW_BYTES");
438 
439  m_coldesc.clear();
440  PvlObject::PvlObjectIterator colobj = tableDetails->beginObject();
441  int icol(0);
442  while (colobj != tableDetails->endObject()) {
443  if (colobj->isNamed("COLUMN")) {
444  m_coldesc.push_back(getColumnDescription(*colobj, icol));
445  icol++;
446  }
447  ++colobj;
448  }
449 
450  // Test to ensure columns match the number listed in the label
451  if (ncols != columns()) {
452  ostringstream msg;
453  msg << "Number of columns in the COLUMNS label keyword (" << ncols
454  << ") does not match number of COLUMN objects found ("
455  << columns() << ")";
456  #if 0
457  throw iException::Message(iException::Programmer, msg.str(), _FILEINFO_);
458  #else
459  cout << msg.str() << endl;
460  #endif
461  }
462  return;
463  }
464 
481  void ImportPdsTable::loadTable(const QString &pdsTableFile) {
482 
483  // Vet the filename. Many PDS files record the filename in uppercase and
484  // in practice, the filename is in lowercase. Do this check here.
485  QString tempTblFile(pdsTableFile);
486  TextFile tfile(tempTblFile);
487  QString tline;
488  m_rows.clear();
489  int irow(0);
490  while (tfile.GetLine(tline, false)) {
491  if (irow >= m_trows) break;
492 
493  Columns cols;
494  for (int i = 0 ; i < columns() ; i++) {
495  cols.push_back(getColumnValue(tline, m_coldesc[i]));
496  }
497  m_rows.push_back(cols);
498  irow++;
499  }
500  return;
501  }
502 
520  ImportPdsTable::ColumnDescr ImportPdsTable::getColumnDescription(
521  PvlObject &colobj, int nth) const {
522  ColumnDescr cd;
523  cd.m_name = colobj["NAME"][0];
524  cd.m_colnum = nth;
525  if (m_pdsTableType == "ASCII") {
526  cd.m_dataType = getGenericType(colobj["DATA_TYPE"][0]).toUpper();
527  }
528  else {
529  cd.m_dataType = colobj["DATA_TYPE"][0].toUpper();
530  //cd.m_numBytes = colobj["ITEM_BYTES"];
531  }
532  cd.m_startByte = ((int) colobj["START_BYTE"]) - 1; // 0-based indexing
533  cd.m_numBytes = (int) colobj["BYTES"];
534  return (cd);
535  }
536 
551  ImportPdsTable::ColumnDescr *ImportPdsTable::findColumn(
552  const QString &colName) {
553  QString cName = getFormattedName(colName);
554  ColumnTypes::iterator col = m_coldesc.begin();
555  while (col != m_coldesc.end()) {
556  QString c = getFormattedName(col->m_name);
557  if (c.toUpper() == cName.toUpper()) { return (&(*col)); }
558  col++;
559  }
560  return (0);
561  }
562 
577  const ImportPdsTable::ColumnDescr *ImportPdsTable::findColumn(
578  const QString &colName) const {
579  QString cName = getFormattedName(colName);
580  ColumnTypes::const_iterator col = m_coldesc.begin();
581  while (col != m_coldesc.end()) {
582  QString c = getFormattedName(col->m_name);
583  if (c.toUpper() == cName.toUpper()) { return (&(*col)); }
584  col++;
585  }
586  return (0);
587  }
588 
589 
600  QString ImportPdsTable::getColumnValue(const QString &tline,
601  const ColumnDescr &cdesc) const {
602  return (tline.mid(cdesc.m_startByte, cdesc.m_numBytes));
603  }
604 
624  QString ImportPdsTable::getFormattedName(
625  const QString &colname) const {
626  QString cname = QString(colname).replace(QRegExp("[(),]"), " ").simplified();
627 
628  bool uppercase = true;
629  QString oString;
630  for (int i = 0 ; i < cname.size() ; i++) {
631  if (uppercase) {
632  oString.push_back(cname[i].toUpper());
633  uppercase = false;
634  }
635  else if ( (cname[i] == ' ') || (cname[i] == '_') ) {
636  uppercase = true;
637  }
638  else {
639  oString.push_back(cname[i].toLower());
640  }
641  }
642 
643  return (oString);
644  }
645 
646 
664  QString ImportPdsTable::getGenericType(const QString &ttype) const {
665  return ttype.split("_").last();
666  }
667 
668 
686  TableField ImportPdsTable::makeField(const ColumnDescr &cdesc) {
687  QString dtype = cdesc.m_dataType;
688  QString name = getFormattedName(cdesc.m_name);
689  if (m_pdsTableType == "ASCII") {
690  if ( dtype == "INTEGER" ) {
691  return (TableField(name, TableField::Integer));
692  }
693  else if ( ((dtype == "DOUBLE" )
694  || (dtype == "REAL")
695  || (dtype == "FLOAT")) ) {
696  return (TableField(name, TableField::Double));
697  }
698  else {
699  return (TableField(name, TableField::Text, cdesc.m_numBytes));
700  }
701  }
702  else {
703  return makeFieldFromBinaryTable(cdesc);
704  }
705  }
706 
721  TableRecord ImportPdsTable::makeRecord(const ColumnTypes &ctypes) {
722  TableRecord rec;
723  for (unsigned int i = 0 ; i < ctypes.size() ; i++) {
724  TableField field = makeField(ctypes[i]);
725  rec += field;
726  }
727  return (rec);
728  }
729 
730 
745  TableField &ImportPdsTable::extract(const Columns &cols,
746  const ColumnDescr &cdesc,
747  TableField &tfield) const {
748  int ith = cdesc.m_colnum;
749  try {
750  IString data(cols[ith]);
751  if (tfield.isInteger()) {
752  data.Trim(" \t\r\n");
753  tfield = data.ToInteger();
754  }
755  else if (tfield.isDouble()) {
756  data.Trim(" \t\r\n");
757  tfield = data.ToDouble();
758  }
759  else { // Its a text field
760  QString str(tfield.size(), ' ');
761  str.insert(0, data.Trim(" \t\r\n").ToQt());
762  tfield = str;
763  }
764  }
765  catch (IException &e) {
766  QString msg = "Conversion failure of column " + cdesc.m_name;
767  throw IException(e, IException::Programmer, msg, _FILEINFO_);
768  }
769 
770  return (tfield);
771  }
772 
787  TableRecord &ImportPdsTable::extract(const Columns &cols,
788  const ColumnTypes &ctypes,
789  TableRecord &record) const {
790  for (unsigned int i = 0 ; i < ctypes.size() ; i++) {
791  extract(cols, ctypes[i], record[i]);
792  }
793  return (record);
794  }
795 
808  void ImportPdsTable::fillTable(Table &table, const ColumnTypes &cols,
809  TableRecord &record) const {
810  if (m_pdsTableType == "ASCII") {
811  for (unsigned int i = 0 ; i < m_rows.size() ; i++) {
812  try {
813  table += extract(m_rows[i], cols, record);
814  }
815  catch (IException &e) {
816  QString msg = "Failed to convert data in row [" + toString((int) i) + "]";
817  throw IException(e, IException::Programmer, msg, _FILEINFO_);
818  }
819  }
820  }
821  else {
822  QString tempTblFile = m_pdsTableFile;
823  ifstream pdsFileStream(tempTblFile.toAscii().data(), ifstream::binary);
824  if (!pdsFileStream) {
825  IString msg = "Unable to open file containing PDS table ["
826  + tempTblFile + "].";
827  throw IException(IException::Unknown, msg, _FILEINFO_);
828  }
829  // read and discard the rows above the table data
830  char *rowBuffer = new char[m_recordBytes];
831  for (int i = 1; i < m_pdsTableStart; i++) {
832  pdsFileStream.read(rowBuffer, m_recordBytes);
833  }
834  delete [] rowBuffer;
835  rowBuffer = NULL;
836  // now, import and add the records to the table
837  for (int rowIndex = 0; rowIndex < m_trows; rowIndex++) {
838  rowBuffer = new char[m_recordBytes];
839  pdsFileStream.read(rowBuffer, m_recordBytes);
840  TableRecord rec = extractBinary(rowBuffer, record);
841  table += rec;
842  // TableRecord rec = extractBinary(pdsFileStream, record);
843  delete [] rowBuffer;
844  rowBuffer = NULL;
845  }
846  }
847  return;
848  }
849 
857  int ImportPdsTable::columns() const {
858  return (m_coldesc.size());
859  }
860 
868  int ImportPdsTable::rows() const {
869  return (m_rows.size());
870  }
871 
887  TableRecord ImportPdsTable::extractBinary(char *rowBuffer, TableRecord &record) const {
888  // for each record loop through the columns to get field values
889  for (int colIndex = 0; colIndex < columns(); colIndex++) {
890  QString columnName = m_coldesc[colIndex].m_name;
891  for (int fieldIndex = 0 ; fieldIndex < record.Fields() ; fieldIndex++) {
892  QString fieldName = record[fieldIndex].name();
893  if (fieldName == columnName) {
894  int startByte = m_coldesc[colIndex].m_startByte;
895  int numBytes = m_coldesc[colIndex].m_numBytes;
896  if (record[fieldIndex].isInteger()) {
897  int columnValue;
898  memmove(&columnValue, &rowBuffer[startByte], numBytes);
899  EndianSwapper endianSwap(m_byteOrder);
900  int fieldValue = endianSwap.Int(&columnValue);
901  record[fieldIndex] = fieldValue;
902  }
903  else if (record[fieldIndex].isDouble()) {
904  EndianSwapper endianSwap(m_byteOrder);
905  double columnValue;
906  memmove(&columnValue, &rowBuffer[startByte], numBytes);
907  double fieldValue = endianSwap.Double(&columnValue);
908  record[fieldIndex] = fieldValue;
909  }
910  else if (record[fieldIndex].isReal()) {
911  EndianSwapper endianSwap(m_byteOrder);
912  float columnValue;
913  memmove(&columnValue, &rowBuffer[startByte], numBytes);
914  float fieldValue = endianSwap.Float(&columnValue);
915  record[fieldIndex] = fieldValue;
916  }
917  else if (record[fieldIndex].isText()) {
918  QString fieldValue(numBytes, '\0');
919  for (int byte = 0; byte < numBytes; byte++) {
920  fieldValue[byte] = rowBuffer[startByte + byte];
921  }
922  record[fieldIndex] = fieldValue;
923  }
924  }
925  }
926 
927  }
928  return record;
929  }
930 
944  TableField ImportPdsTable::makeFieldFromBinaryTable(const ColumnDescr &cdesc){
945  QString dataType = cdesc.m_dataType;
946  // For binary tables, we will not reformat the name of the column
947  QString name = cdesc.m_name;
948  if (dataType == "MSB_INTEGER" || dataType == "INTEGER"
949  || dataType == "SUN_INTEGER" || dataType == "MAC_INTEGER") {
950  if (cdesc.m_numBytes != 4) {
951  QString msg = "Only 4 byte integer values are supported in Isis. "
952  "PDS Column [" + cdesc.m_name
953  + "] has an integer DATA_TYPE with [BYTES = "
954  + toString(cdesc.m_numBytes) + "].";
955  throw IException(IException::Unknown, msg, _FILEINFO_);
956  }
957  setPdsByteOrder("MSB");
958  return TableField(name, TableField::Integer);
959  }
960  else if (dataType == "LSB_INTEGER" || dataType == "VAX_INTEGER"
961  || dataType == "PC_INTEGER" ) {
962  if (cdesc.m_numBytes != 4) {
963  QString msg = "Only 4 byte integer values are supported in Isis. "
964  "PDS Column [" + cdesc.m_name
965  + "] has an integer DATA_TYPE with [BYTES = "
966  + toString(cdesc.m_numBytes) + "].";
967  throw IException(IException::Unknown, msg, _FILEINFO_);
968  }
969  setPdsByteOrder("LSB");
970  return TableField(name, TableField::Integer);
971  }
972  else if (dataType == "FLOAT" //IEEE_REAL alias (MSB)
973  || dataType == "REAL" //IEEE_REAL alias (MSB)
974  || dataType == "SUN_REAL" //IEEE_REAL alias (MSB)
975  || dataType == "MAC_REAL" //IEEE_REAL alias (MSB)
976  || dataType == "IEEE_REAL" ) {
977  setPdsByteOrder("MSB");
978  if (cdesc.m_numBytes == 8) {
979  return TableField(name, TableField::Double);
980  }
981  else if (cdesc.m_numBytes == 4) {
982  return TableField(name, TableField::Real);
983  }
984  else {
985  IString msg = "Only 4 byte or 8 byte real values are supported in Isis. "
986  "PDS Column [" + cdesc.m_name
987  + "] has a real DATA_TYPE with [BYTES = "
988  + toString(cdesc.m_numBytes) + "].";
989  throw IException(IException::Unknown, msg, _FILEINFO_);
990  }
991  }
992  else if (dataType == "PC_REAL") {
993  setPdsByteOrder("LSB");
994  if (cdesc.m_numBytes == 8) {
995  return TableField(name, TableField::Double);
996  }
997  else if (cdesc.m_numBytes == 4) {
998  return TableField(name, TableField::Real);
999  }
1000  else {
1001  QString msg = "Only 4 byte or 8 byte real values are supported in Isis. "
1002  "PDS Column [" + cdesc.m_name
1003  + "] has a real DATA_TYPE with [BYTES = "
1004  + toString(cdesc.m_numBytes) + "].";
1005  throw IException(IException::Unknown, msg, _FILEINFO_);
1006  }
1007  }
1008  else if (dataType.contains("CHARACTER")
1009  || dataType.contains("ASCII")
1010  || dataType == "DATE" || dataType == "TIME" ) {
1011  return TableField(name, TableField::Text, cdesc.m_numBytes);
1012  }
1013  // Isis3 tables currently don't support any of the following PDS DATA_TYPE:
1014  // BIT_STRING, COMPLEX, N/A, BOOLEAN, UNSIGNED_INTEGER, IBM types, some VAX types
1015  IString msg = "PDS Column [" + cdesc.m_name
1016  + "] has an unsupported DATA_TYPE ["
1017  + dataType + "].";
1018  throw IException(IException::Unknown, msg, _FILEINFO_);
1019  }
1020 
1029  void ImportPdsTable::setPdsByteOrder(QString byteOrder) {
1030  if (!m_byteOrder.isEmpty() && m_byteOrder != byteOrder) {
1031  QString msg = "Unable import the binary PDS table [" + m_tableName
1032  + "]. The column DATA_TYPE values indicate differing byte "
1033  "orders. ";
1034  throw IException(IException::Unknown, msg.toStdString(), _FILEINFO_);
1035  }
1036  m_byteOrder = byteOrder;
1037  }
1038 
1039 
1040 } // namespace Isis
1041