ImportDescriptor* Nitf::NitfImporterShell::getImportDescriptor(const string& filename, ossim_uint32 imageSegment,
                                                               const Nitf::OssimFileResource& pFile,
                                                               const ossimNitfFileHeaderV2_X* pFileHeader,
                                                               const ossimNitfImageHeaderV2_X* pImageSubheader)
{
   if (pImageSubheader == NULL)
   {
      return NULL;
   }

   EncodingType dataType = ossimImageHeaderToEncodingType(pImageSubheader);
   if (dataType.isValid() == false)
   {
      return NULL;
   }

   stringstream imageNameStream;
   imageNameStream << "I" << imageSegment + 1;
   string imageName = imageNameStream.str();

   ImportDescriptorResource pImportDescriptor(filename + "-" + imageName,
      TypeConverter::toString<RasterElement>(), NULL);
   VERIFYRV(pImportDescriptor.get() != NULL, NULL);
   pImportDescriptor->setImported(pImageSubheader->getRepresentation() != "NODISPLY");

   RasterDataDescriptor* pDescriptor = dynamic_cast<RasterDataDescriptor*>(pImportDescriptor->getDataDescriptor());
   VERIFYRV(pDescriptor != NULL, NULL);

   vector<DimensionDescriptor> bands =  RasterUtilities::generateDimensionVector(pImageSubheader->getNumberOfBands(),
      true, false, true);
   pDescriptor->setBands(bands);

   vector<DimensionDescriptor> rows = RasterUtilities::generateDimensionVector(pImageSubheader->getNumberOfRows(),
      true, false, true);
   pDescriptor->setRows(rows);

   vector<DimensionDescriptor> cols = RasterUtilities::generateDimensionVector(pImageSubheader->getNumberOfCols(),
      true, false, true);
   pDescriptor->setColumns(cols);

   if (pImageSubheader->getIMode() == "P")
   {
      pDescriptor->setInterleaveFormat(BIP);
   }
   else if (pImageSubheader->getIMode() == "R")
   {
      pDescriptor->setInterleaveFormat(BIL);
   }
   else
   {
      pDescriptor->setInterleaveFormat(BSQ);
   }

   pDescriptor->setDataType(dataType);
   pDescriptor->setValidDataTypes(vector<EncodingType>(1, dataType));
   pDescriptor->setProcessingLocation(IN_MEMORY);

   map<string, TrePlugInResource> parsers;
   string errorMessage;

   // Set the file descriptor
   RasterFileDescriptor* pFileDescriptor = dynamic_cast<RasterFileDescriptor*>(
      RasterUtilities::generateAndSetFileDescriptor(pDescriptor, filename, imageName, LITTLE_ENDIAN_ORDER));
   if (pFileDescriptor == NULL)
   {
      return NULL;
   }

   // Set the bits per element, which may be different than the data type in the data descriptor,
   // using NBPP instead of ABPP as is done in ossimNitfTileSource.cpp.
   unsigned int bitsPerPixel = static_cast<unsigned int>(pImageSubheader->getBitsPerPixelPerBand());
   pFileDescriptor->setBitsPerElement(bitsPerPixel);

   // Populate the metadata and set applicable values in the data descriptor
   if (Nitf::importMetadata(imageSegment + 1, pFile, pFileHeader, pImageSubheader, pDescriptor, parsers,
      errorMessage) == true)
   {
      // Populate specific fields in the data descriptor or file descriptor from the TREs
      const DynamicObject* pMetadata = pDescriptor->getMetadata();
      VERIFYRV(pMetadata, NULL);

      // Pixel size - This info is contained in multiple TREs, but there is no documentation on which
      // TRE contains the more precise value if multiple TREs containing the info are present.  Choosing
      // the order ACFTA, BANDSA, ACFTB, and BANDSB where the later "B" TREs will overwrite the values
      // contained in the earlier "A" TREs.  The BANDSB TRE contains GSD values for each band, which is
      // currently not supported, so only set the pixel size if the values in all bands are the same.
      double xGsd = 1.0;
      double yGsd = 1.0;

      const string acrftaPath[] =
      {
         Nitf::NITF_METADATA,
         Nitf::TRE_METADATA,
         "ACFTA",
         "0",
         END_METADATA_NAME
      };

      const DynamicObject* pAcrftA = dv_cast<DynamicObject>(&pMetadata->getAttributeByPath(acrftaPath));
      if (pAcrftA != NULL)
      {
         // The ACFTA spec calls out specific spacing units for "SAR" and "EO-IR" data, but does not indicate how
         // this is determined.  It seems to be related to the ACFTB SENSOR_ID_TYPE field, but that field is not
         // present in the ACFTA TRE.  So just check for "SAR" data from the ICAT field in the image subheader
         // and assume every other data type is "EO-IR" data.
         const string imageCategory = pImageSubheader->getCategory().trim();

         const DataVariant& rowSpacing = pAcrftA->getAttribute(Nitf::TRE::ACFTA::ROW_SPACING);
         if (rowSpacing.isValid() == true)
         {
            if (imageCategory == "SAR")
            {
               yGsd = getGsd(rowSpacing, "f");     // Feet
            }
            else
            {
               yGsd = getGsd(rowSpacing, "r");     // Micro-radians
            }
         }

         const DataVariant& columnSpacing = pAcrftA->getAttribute(Nitf::TRE::ACFTA::COL_SPACING);
         if (columnSpacing.isValid() == true)
         {
            if (imageCategory == "SAR")
            {
               xGsd = getGsd(columnSpacing, "f");  // Feet
            }
            else
            {
               xGsd = getGsd(columnSpacing, "r");  // Micro-radians
            }
         }
      }

      const string bandsaPath[] =
      {
         Nitf::NITF_METADATA,
         Nitf::TRE_METADATA,
         "BANDSA",
         "0",
         END_METADATA_NAME
      };

      const DynamicObject* pBandsA = dv_cast<DynamicObject>(&pMetadata->getAttributeByPath(bandsaPath));
      if (pBandsA != NULL)
      {
         const DataVariant& rowSpacing = pBandsA->getAttribute(Nitf::TRE::BANDSA::ROW_SPACING);
         if (rowSpacing.isValid() == true)
         {
            const DataVariant& rowSpacingUnits = pBandsA->getAttribute(Nitf::TRE::BANDSA::ROW_SPACING_UNITS);
            if (rowSpacingUnits.isValid() == true)
            {
               yGsd = getGsd(rowSpacing, rowSpacingUnits.toXmlString());
            }
         }

         const DataVariant& columnSpacing = pBandsA->getAttribute(Nitf::TRE::BANDSA::COL_SPACING);
         if (columnSpacing.isValid() == true)
         {
            const DataVariant& columnSpacingUnits = pBandsA->getAttribute(Nitf::TRE::BANDSA::COL_SPACING_UNITS);
            if (columnSpacingUnits.isValid() == true)
            {
               xGsd = getGsd(columnSpacing, columnSpacingUnits.toXmlString());
            }
         }
      }

      const string acrftbPath[] =
      {
         Nitf::NITF_METADATA,
         Nitf::TRE_METADATA,
         "ACFTB",
         "0",
         END_METADATA_NAME
      };

      const DynamicObject* pAcrftB = dv_cast<DynamicObject>(&pMetadata->getAttributeByPath(acrftbPath));
      if (pAcrftB != NULL)
      {
         const DataVariant& rowSpacing = pAcrftB->getAttribute(Nitf::TRE::ACFTB::ROW_SPACING);
         if (rowSpacing.isValid() == true)
         {
            const DataVariant& rowSpacingUnits = pAcrftB->getAttribute(Nitf::TRE::ACFTB::ROW_SPACING_UNITS);
            if (rowSpacingUnits.isValid() == true)
            {
               yGsd = getGsd(rowSpacing, rowSpacingUnits.toXmlString());
            }
         }

         const DataVariant& columnSpacing = pAcrftB->getAttribute(Nitf::TRE::ACFTB::COL_SPACING);
         if (columnSpacing.isValid() == true)
         {
            const DataVariant& columnSpacingUnits = pAcrftB->getAttribute(Nitf::TRE::ACFTB::COL_SPACING_UNITS);
            if (columnSpacingUnits.isValid() == true)
            {
               xGsd = getGsd(columnSpacing, columnSpacingUnits.toXmlString());
            }
         }
      }

      const string bandsbPath[] =
      {
         Nitf::NITF_METADATA,
         Nitf::TRE_METADATA,
         "BANDSB",
         "0",
         END_METADATA_NAME
      };

      const DynamicObject* pBandsB = dv_cast<DynamicObject>(&pMetadata->getAttributeByPath(bandsbPath));
      if (pBandsB != NULL)
      {
         bool validRowGsd = false;

         const DataVariant& rowGsd = pBandsB->getAttribute(Nitf::TRE::BANDSB::ROW_GSD);
         if (rowGsd.isValid() == true)
         {
            const DataVariant& rowGsdUnits = pBandsB->getAttribute(Nitf::TRE::BANDSB::ROW_GSD_UNIT);
            if (rowGsdUnits.isValid() == true)
            {
               yGsd = getGsd(rowGsd, rowGsdUnits.toXmlString());
               validRowGsd = true;
            }
         }

         if (validRowGsd == false)
         {
            if (pBandsB->getAttribute(Nitf::TRE::BANDSB::ROW_GSD + "#0").isValid())
            {
               double commonYGsd = -1.0;

               unsigned int numBands = pDescriptor->getBandCount();
               for (unsigned int i = 0; i < numBands; ++i)
               {
                  double bandYGsd = -1.0;
                  string bandPostfix = "#" + StringUtilities::toDisplayString(i);

                  const DataVariant& bandRowGsd = pBandsB->getAttribute(Nitf::TRE::BANDSB::ROW_GSD + bandPostfix);
                  if (bandRowGsd.isValid() == true)
                  {
                     const DataVariant& bandRowGsdUnits = pBandsB->getAttribute(Nitf::TRE::BANDSB::ROW_GSD_UNIT +
                        bandPostfix);
                     if (bandRowGsdUnits.isValid() == true)
                     {
                        bandYGsd = getGsd(bandRowGsd, bandRowGsdUnits.toXmlString());
                     }
                  }

                  if (bandYGsd == commonYGsd)
                  {
                     continue;
                  }

                  if (commonYGsd != -1.0)
                  {
                     commonYGsd = -1.0;
                     break;
                  }

                  commonYGsd = bandYGsd;
               }

               if (commonYGsd != 1.0)
               {
                  yGsd = commonYGsd;
               }
            }
         }

         bool validColumnGsd = false;

         const DataVariant& columnGsd = pBandsB->getAttribute(Nitf::TRE::BANDSB::COL_GSD);
         if (columnGsd.isValid() == true)
         {
            const DataVariant& columnGsdUnits = pBandsB->getAttribute(Nitf::TRE::BANDSB::COL_GSD_UNITS);
            if (columnGsdUnits.isValid() == true)
            {
               xGsd = getGsd(columnGsd, columnGsdUnits.toXmlString());
               validColumnGsd = true;
            }
         }

         if (validColumnGsd == false)
         {
            if (pBandsB->getAttribute(Nitf::TRE::BANDSB::COL_GSD + "#0").isValid())
            {
               double commonXGsd = -1.0;

               unsigned int numBands = pDescriptor->getBandCount();
               for (unsigned int i = 0; i < numBands; ++i)
               {
                  double bandXGsd = -1.0;
                  string bandPostfix = "#" + StringUtilities::toDisplayString(i);

                  const DataVariant& bandRowGsd = pBandsB->getAttribute(Nitf::TRE::BANDSB::COL_GSD + bandPostfix);
                  if (bandRowGsd.isValid() == true)
                  {
                     const DataVariant& bandRowGsdUnits = pBandsB->getAttribute(Nitf::TRE::BANDSB::COL_GSD_UNIT +
                        bandPostfix);
                     if (bandRowGsdUnits.isValid() == true)
                     {
                        bandXGsd = getGsd(bandRowGsd, bandRowGsdUnits.toXmlString());
                     }
                  }

                  if (bandXGsd == commonXGsd)
                  {
                     continue;
                  }

                  if (commonXGsd != -1.0)
                  {
                     commonXGsd = -1.0;
                     break;
                  }

                  commonXGsd = bandXGsd;
               }

               if (commonXGsd != 1.0)
               {
                  xGsd = commonXGsd;
               }
            }
         }
      }

      double magFactor = 1.0;
      ossimString imag = pImageSubheader->getImageMagnification().trim();
      if (imag.empty() == false)
      {
         // Need to multiply the GSD values by the image magnification (IMAG) value in the image subheader
         if (imag[0] == '/')
         {
            ossimString reciprocal = imag.substr(1);
            magFactor = 1.0 / reciprocal.toDouble();
         }
         else
         {
            magFactor = imag.toDouble();
         }

         xGsd *= magFactor;
         yGsd *= magFactor;
      }

      pDescriptor->setXPixelSize(xGsd);
      pDescriptor->setYPixelSize(yGsd);

      // Higher precision GCPs
      const string blockaPath[] =
      {
         Nitf::NITF_METADATA,
         Nitf::TRE_METADATA,
         "BLOCKA",
         "0",
         END_METADATA_NAME
      };

      const DynamicObject* pBlockA = dv_cast<DynamicObject>(&pMetadata->getAttributeByPath(blockaPath));
      if (pBlockA != NULL)
      {
         const DataVariant& blockLines = pBlockA->getAttribute(Nitf::TRE::BLOCKA::L_LINES);
         if (blockLines.isValid() == true)
         {
            unsigned int numBlockRows = 0;
            if (blockLines.getValue<unsigned int>(numBlockRows) == true)
            {
               // Need to multiply the number of rows by the image magnification (IMAG) value in the image subheader
               numBlockRows = static_cast<unsigned int>(static_cast<double>(numBlockRows) * magFactor);
               if (numBlockRows == pFileDescriptor->getRowCount())
               {
                  list<GcpPoint> updatedGcps;

                  list<GcpPoint> gcps = pFileDescriptor->getGcps();
                  for (list<GcpPoint>::iterator iter = gcps.begin(); iter != gcps.end(); ++iter)
                  {
                     GcpPoint gcp = *iter;
                     string coordinateText;

                     list<GcpPoint>::size_type index = updatedGcps.size();
                     if (index == 0)
                     {
                        const DataVariant& gcp1 = pBlockA->getAttribute(Nitf::TRE::BLOCKA::FRFC_LOC);
                        if (gcp1.isValid() == true)
                        {
                           coordinateText = gcp1.toXmlString();
                        }
                     }
                     else if (index == 1)
                     {
                        const DataVariant& gcp2 = pBlockA->getAttribute(Nitf::TRE::BLOCKA::FRLC_LOC);
                        if (gcp2.isValid() == true)
                        {
                           coordinateText = gcp2.toXmlString();
                        }
                     }
                     else if (index == 2)
                     {
                        const DataVariant& gcp3 = pBlockA->getAttribute(Nitf::TRE::BLOCKA::LRLC_LOC);
                        if (gcp3.isValid() == true)
                        {
                           coordinateText = gcp3.toXmlString();
                        }
                     }
                     else if (index == 3)
                     {
                        const DataVariant& gcp4 = pBlockA->getAttribute(Nitf::TRE::BLOCKA::LRFC_LOC);
                        if (gcp4.isValid() == true)
                        {
                           coordinateText = gcp4.toXmlString();
                        }
                     }

                     if (StringUtilities::isAllBlank(coordinateText) == false)
                     {
                        coordinateText.insert(10, ", ");
                        LatLonPoint latLon(coordinateText);
                        gcp.mCoordinate = latLon.getCoordinates();
                     }

                     updatedGcps.push_back(gcp);
                  }

                  pFileDescriptor->setGcps(updatedGcps);
               }
            }
         }
      }

      // This only checks the first BANDSB. It is possible to have multiple BANDSB TREs.
      // If someone runs across real data where the bad band info is in another BANDSB TRE
      // this code will need to be modified.
      if (pBandsB != NULL && pBandsB->getAttribute(Nitf::TRE::BANDSB::BAD_BAND + "#0").isValid())
      {
         const vector<DimensionDescriptor>& curBands = pDescriptor->getBands();
         vector<DimensionDescriptor> newBands;
         for (size_t idx = 0; idx < curBands.size(); ++idx)
         {
            const int* pVal = dv_cast<int>(&pBandsB->getAttribute(
               Nitf::TRE::BANDSB::BAD_BAND + "#" + StringUtilities::toDisplayString(idx)));
            if (pVal == NULL || *pVal == 1) // 0 == invalid or suspect band, 1 = valid band
            {
               newBands.push_back(curBands[idx]);
            }
         }
         pDescriptor->setBands(newBands);
      }

      // Bad values
      if (pImageSubheader->hasTransparentCode() == true)
      {
         vector<int> badValues;
         badValues.push_back(static_cast<int>(pImageSubheader->getTransparentCode()));
         pDescriptor->setBadValues(badValues);
      }

      // If red, green, OR blue bands are valid, set the display mode to RGB.
      if (pDescriptor->getDisplayBand(RED).isValid() == true ||
         pDescriptor->getDisplayBand(GREEN).isValid() == true ||
         pDescriptor->getDisplayBand(BLUE).isValid() == true)
      {
         pDescriptor->setDisplayMode(RGB_MODE);
      }
      // Otherwise, if the gray band is valid, set the display mode to GRAYSCALE.
      else if (pDescriptor->getDisplayBand(GRAY).isValid() == true)
      {
         pDescriptor->setDisplayMode(GRAYSCALE_MODE);
      }
      // Otherwise, if at least 3 bands are available, set the display mode to RGB,
      // and set the first three bands to red, green, and blue respectively.
      else if (bands.size() >= 3)
      {
         pDescriptor->setDisplayBand(RED, bands[0]);
         pDescriptor->setDisplayBand(GREEN, bands[1]);
         pDescriptor->setDisplayBand(BLUE, bands[2]);
         pDescriptor->setDisplayMode(RGB_MODE);
      }
      // Otherwise, if at least 1 band is available, set the display mode to GRAYSCALE,
      // and set the first band to GRAY.
      else if (bands.empty() == false)
      {
         pDescriptor->setDisplayBand(GRAY, bands[0]);
         pDescriptor->setDisplayMode(GRAYSCALE_MODE);
      }
      else
      {
         return NULL;
      }

      // Special initialization for J2K compressed image segments
      const string compressionPath[] =
      {
         Nitf::NITF_METADATA,
         Nitf::IMAGE_SUBHEADER,
         Nitf::ImageSubheaderFieldNames::COMPRESSION,
         END_METADATA_NAME
      };

      string imageCompression = pMetadata->getAttributeByPath(compressionPath).toDisplayString();
      if ((imageCompression == Nitf::ImageSubheaderFieldValues::IC_C8) ||
         (imageCompression == Nitf::ImageSubheaderFieldValues::IC_M8))
      {
         // Per Section 8.1 of the BIIF Profile for JPEG 2000 Version 01.10 (BPJ2K01.10),
         // if the values in the J2K data differ from the values in the image subheader,
         // the J2K values are given precedence.
         opj_image_t* pImage = getImageInfo(filename, imageSegment, OPJ_CODEC_J2K);
         if (pImage == NULL)
         {
            pImage = getImageInfo(filename, imageSegment, OPJ_CODEC_JP2);
         }

         if (pImage != NULL)
         {
            // Bits per element
            unsigned int bitsPerElement = pImage->comps->prec;
            if (bitsPerElement != pFileDescriptor->getBitsPerElement())
            {
               pFileDescriptor->setBitsPerElement(bitsPerElement);
            }

            // Data type
            EncodingType dataType = INT1UBYTE;
            if (bitsPerElement <= 8)
            {
               if (pImage->comps->sgnd)
               {
                  dataType = INT1SBYTE;
               }
               else
               {
                  dataType = INT1UBYTE;
               }
            }
            else if (bitsPerElement <= 16)
            {
               if (pImage->comps->sgnd)
               {
                  dataType = INT2SBYTES;
               }
               else
               {
                  dataType = INT2UBYTES;
               }
            }
            else if (bitsPerElement <= 32)
            {
               if (pImage->comps->sgnd)
               {
                  dataType = INT4SBYTES;
               }
               else
               {
                  dataType = INT4UBYTES;
               }
            }
            else if (bitsPerElement <= 64)
            {
               dataType = FLT8BYTES;
            }

            if (dataType != pDescriptor->getDataType())
            {
               pDescriptor->setDataType(dataType);
            }

            // Rows
            unsigned int numRows = pImage->comps->h;
            if (numRows != pFileDescriptor->getRowCount())
            {
               vector<DimensionDescriptor> rows = RasterUtilities::generateDimensionVector(numRows, true, false, true);
               pDescriptor->setRows(rows);
               pFileDescriptor->setRows(rows);
            }

            // Columns
            unsigned int numColumns = pImage->comps->w;
            if (numColumns != pFileDescriptor->getColumnCount())
            {
               vector<DimensionDescriptor> columns = RasterUtilities::generateDimensionVector(numColumns, true, false,
                  true);
               pDescriptor->setColumns(columns);
               pFileDescriptor->setColumns(columns);
            }

            // Bands
            unsigned int numBands = pImage->numcomps;
            if (numBands != pFileDescriptor->getBandCount())
            {
               vector<DimensionDescriptor> bands = RasterUtilities::generateDimensionVector(numBands, true, false,
                  true);
               pDescriptor->setBands(bands);
               pFileDescriptor->setBands(bands);
            }

            // Cleanup
            opj_image_destroy(pImage);
         }

         // Set the interleave format as BIP, which is the interleave format for J2K data
         pDescriptor->setInterleaveFormat(BIP);
         pFileDescriptor->setInterleaveFormat(BIP);
      }

      mParseMessages[imageSegment] = errorMessage;
   }

   return pImportDescriptor.release();
}
void FileDescriptorWidget::initialize()
{
   mpTreeWidget->clear();
   mpGcpTree->clear();
   mpGcpGroup->hide();

   if (mpFileDescriptor == NULL)
   {
      return;
   }

   // Filename
   QTreeWidgetItem* pFilenameItem = new QTreeWidgetItem(mpTreeWidget);
   if (pFilenameItem != NULL)
   {
      string filename = mpFileDescriptor->getFilename();

      pFilenameItem->setText(0, "Filename");
      pFilenameItem->setText(1, QString::fromStdString(filename));
   }

   // Data set location
   QTreeWidgetItem* pDatasetLocationItem = new QTreeWidgetItem(mpTreeWidget);
   if (pDatasetLocationItem != NULL)
   {
      string datasetLocation = mpFileDescriptor->getDatasetLocation();

      pDatasetLocationItem->setText(0, "Data Set Location");
      pDatasetLocationItem->setText(1, QString::fromStdString(datasetLocation));
   }

   // Endian
   QTreeWidgetItem* pEndianItem = new QTreeWidgetItem(mpTreeWidget);
   if (pEndianItem != NULL)
   {
      EndianType endian = mpFileDescriptor->getEndian();
      string endianText = StringUtilities::toDisplayString(endian);

      pEndianItem->setText(0, "Endian");
      pEndianItem->setText(1, QString::fromStdString(endianText));

      if (mReadOnly == false)
      {
         QComboBox* pEndianCombo = new QComboBox(mpTreeWidget);
         pEndianCombo->setEditable(false);
         pEndianCombo->addItem(QString::fromStdString(StringUtilities::toDisplayString(LITTLE_ENDIAN_ORDER)));
         pEndianCombo->addItem(QString::fromStdString(StringUtilities::toDisplayString(BIG_ENDIAN_ORDER)));
         pEndianCombo->hide();

         mpTreeWidget->setCellWidgetType(pEndianItem, 1, CustomTreeWidget::COMBO_BOX);
         mpTreeWidget->setComboBox(pEndianItem, 1, pEndianCombo);
      }
   }

   // Raster element file descriptor items
   RasterFileDescriptor* pRasterDescriptor = dynamic_cast<RasterFileDescriptor*>(mpFileDescriptor);
   if (pRasterDescriptor == NULL)
   {
      return;
   }

   mpGcpGroup->show();

   // Rows
   QTreeWidgetItem* pRowsItem = new QTreeWidgetItem();
   if (pRowsItem != NULL)
   {
      unsigned int rows = pRasterDescriptor->getRowCount();

      pRowsItem->setText(0, "Rows");
      pRowsItem->setText(1, QString::number(rows));

      mpTreeWidget->insertTopLevelItem(2, pRowsItem);
      if (mReadOnly == false)
      {
         mpTreeWidget->setCellWidgetType(pRowsItem, 1, CustomTreeWidget::LINE_EDIT);
      }
   }

   // Columns
   QTreeWidgetItem* pColumnsItem = new QTreeWidgetItem();
   if (pColumnsItem != NULL)
   {
      unsigned int columns = pRasterDescriptor->getColumnCount();

      pColumnsItem->setText(0, "Columns");
      pColumnsItem->setText(1, QString::number(columns));

      mpTreeWidget->insertTopLevelItem(3, pColumnsItem);
      if (mReadOnly == false)
      {
         mpTreeWidget->setCellWidgetType(pColumnsItem, 1, CustomTreeWidget::LINE_EDIT);
      }
   }

   // Bits per element
   QTreeWidgetItem* pBitsPerElementItem = new QTreeWidgetItem();
   if (pBitsPerElementItem != NULL)
   {
      unsigned int bitsPerElement = pRasterDescriptor->getBitsPerElement();

      pBitsPerElementItem->setText(0, "Bits Per Element");
      pBitsPerElementItem->setText(1, QString::number(bitsPerElement));

      mpTreeWidget->insertTopLevelItem(4, pBitsPerElementItem);
      if (mReadOnly == false)
      {
         mpTreeWidget->setCellWidgetType(pBitsPerElementItem, 1, CustomTreeWidget::LINE_EDIT);
      }
   }

   // Header bytes
   QTreeWidgetItem* pHeaderBytesItem = new QTreeWidgetItem(mpTreeWidget);
   if (pHeaderBytesItem != NULL)
   {
      unsigned int headerBytes = pRasterDescriptor->getHeaderBytes();

      pHeaderBytesItem->setText(0, "Header Bytes");
      pHeaderBytesItem->setText(1, QString::number(headerBytes));

      if (mReadOnly == false)
      {
         mpTreeWidget->setCellWidgetType(pHeaderBytesItem, 1, CustomTreeWidget::LINE_EDIT);
      }
   }

   // Trailer bytes
   QTreeWidgetItem* pTrailerBytesItem = new QTreeWidgetItem(mpTreeWidget);
   if (pTrailerBytesItem != NULL)
   {
      unsigned int trailerBytes = pRasterDescriptor->getTrailerBytes();

      pTrailerBytesItem->setText(0, "Trailer Bytes");
      pTrailerBytesItem->setText(1, QString::number(trailerBytes));

      if (mReadOnly == false)
      {
         mpTreeWidget->setCellWidgetType(pTrailerBytesItem, 1, CustomTreeWidget::LINE_EDIT);
      }
   }

   // Preline bytes
   QTreeWidgetItem* pPrelineBytesItem = new QTreeWidgetItem(mpTreeWidget);
   if (pPrelineBytesItem != NULL)
   {
      unsigned int prelineBytes = pRasterDescriptor->getPrelineBytes();

      pPrelineBytesItem->setText(0, "Preline Bytes");
      pPrelineBytesItem->setText(1, QString::number(prelineBytes));

      if (mReadOnly == false)
      {
         mpTreeWidget->setCellWidgetType(pPrelineBytesItem, 1, CustomTreeWidget::LINE_EDIT);
      }
   }

   // Postline bytes
   QTreeWidgetItem* pPostlineBytesItem = new QTreeWidgetItem(mpTreeWidget);
   if (pPostlineBytesItem != NULL)
   {
      unsigned int postlineBytes = pRasterDescriptor->getPostlineBytes();

      pPostlineBytesItem->setText(0, "Postline Bytes");
      pPostlineBytesItem->setText(1, QString::number(postlineBytes));

      if (mReadOnly == false)
      {
         mpTreeWidget->setCellWidgetType(pPostlineBytesItem, 1, CustomTreeWidget::LINE_EDIT);
      }
   }

   // Pixel size
   QTreeWidgetItem* pXPixelSizeItem = new QTreeWidgetItem(mpTreeWidget);
   if (pXPixelSizeItem != NULL)
   {
      double pixelSize = pRasterDescriptor->getXPixelSize();

      pXPixelSizeItem->setText(0, "X Pixel Size");
      pXPixelSizeItem->setText(1, QString::number(pixelSize));

      if (mReadOnly == false)
      {
         mpTreeWidget->setCellWidgetType(pXPixelSizeItem, 1, CustomTreeWidget::LINE_EDIT);
      }
   }

   QTreeWidgetItem* pYPixelSizeItem = new QTreeWidgetItem(mpTreeWidget);
   if (pYPixelSizeItem != NULL)
   {
      double pixelSize = pRasterDescriptor->getYPixelSize();

      pYPixelSizeItem->setText(0, "Y Pixel Size");
      pYPixelSizeItem->setText(1, QString::number(pixelSize));

      if (mReadOnly == false)
      {
         mpTreeWidget->setCellWidgetType(pYPixelSizeItem, 1, CustomTreeWidget::LINE_EDIT);
      }
   }

   // Units
   const Units* pUnits = pRasterDescriptor->getUnits();
   if (pUnits != NULL)
   {
      QTreeWidgetItem* pUnitsItem = new QTreeWidgetItem(mpTreeWidget);
      if (pUnitsItem != NULL)
      {
         pUnitsItem->setText(0, "Units");
         pUnitsItem->setBackgroundColor(1, Qt::lightGray);

         // Name
         QTreeWidgetItem* pNameItem = new QTreeWidgetItem(pUnitsItem);
         if (pNameItem != NULL)
         {
            string unitsName = pUnits->getUnitName();

            pNameItem->setText(0, "Name");
            pNameItem->setText(1, QString::fromStdString(unitsName));

            if (mReadOnly == false)
            {
               mpTreeWidget->setCellWidgetType(pNameItem, 1, CustomTreeWidget::LINE_EDIT);
            }
         }

         // Type
         QTreeWidgetItem* pTypeItem = new QTreeWidgetItem(pUnitsItem);
         if (pTypeItem != NULL)
         {
            UnitType unitType = pUnits->getUnitType();
            string unitTypeText = StringUtilities::toDisplayString(unitType);

            pTypeItem->setText(0, "Type");
            pTypeItem->setText(1, QString::fromStdString(unitTypeText));

            if (mReadOnly == false)
            {
               QComboBox* pUnitTypeCombo = new QComboBox(mpTreeWidget);
               pUnitTypeCombo->setEditable(false);
               pUnitTypeCombo->addItem(QString::fromStdString(StringUtilities::toDisplayString(ABSORBANCE)));
               pUnitTypeCombo->addItem(QString::fromStdString(StringUtilities::toDisplayString(ABSORPTANCE)));
               pUnitTypeCombo->addItem(QString::fromStdString(StringUtilities::toDisplayString(DIGITAL_NO)));
               pUnitTypeCombo->addItem(QString::fromStdString(StringUtilities::toDisplayString(DISTANCE)));
               pUnitTypeCombo->addItem(QString::fromStdString(StringUtilities::toDisplayString(EMISSIVITY)));
               pUnitTypeCombo->addItem(QString::fromStdString(StringUtilities::toDisplayString(RADIANCE)));
               pUnitTypeCombo->addItem(QString::fromStdString(StringUtilities::toDisplayString(REFLECTANCE)));
               pUnitTypeCombo->addItem(QString::fromStdString(StringUtilities::toDisplayString(REFLECTANCE_FACTOR)));
               pUnitTypeCombo->addItem(QString::fromStdString(StringUtilities::toDisplayString(TRANSMITTANCE)));
               pUnitTypeCombo->addItem(QString::fromStdString(StringUtilities::toDisplayString(CUSTOM_UNIT)));
               pUnitTypeCombo->hide();

               mpTreeWidget->setCellWidgetType(pTypeItem, 1, CustomTreeWidget::COMBO_BOX);
               mpTreeWidget->setComboBox(pTypeItem, 1, pUnitTypeCombo);
            }
         }

         // Scale
         QTreeWidgetItem* pScaleItem = new QTreeWidgetItem(pUnitsItem);
         if (pScaleItem != NULL)
         {
            double dScale = pUnits->getScaleFromStandard();

            pScaleItem->setText(0, "Scale");
            pScaleItem->setText(1, QString::number(dScale));

            if (mReadOnly == false)
            {
               mpTreeWidget->setCellWidgetType(pScaleItem, 1, CustomTreeWidget::LINE_EDIT);
            }
         }

         // Range minimum
         QTreeWidgetItem* pMinimumItem = new QTreeWidgetItem(pUnitsItem);
         if (pMinimumItem != NULL)
         {
            double dMinimum = pUnits->getRangeMin();

            pMinimumItem->setText(0, "Range Minimum");
            pMinimumItem->setText(1, QString::number(dMinimum));

            if (mReadOnly == false)
            {
               mpTreeWidget->setCellWidgetType(pMinimumItem, 1, CustomTreeWidget::LINE_EDIT);
            }
         }

         // Range maximum
         QTreeWidgetItem* pMaximumItem = new QTreeWidgetItem(pUnitsItem);
         if (pMaximumItem != NULL)
         {
            double dMaximum = pUnits->getRangeMax();

            pMaximumItem->setText(0, "Range Maximum");
            pMaximumItem->setText(1, QString::number(dMaximum));

            if (mReadOnly == false)
            {
               mpTreeWidget->setCellWidgetType(pMaximumItem, 1, CustomTreeWidget::LINE_EDIT);
            }
         }
      }
   }

   // GCPs
   if (mpGcpTree != NULL)
   {
      const list<GcpPoint>& gcps = pRasterDescriptor->getGcps();
      if (gcps.empty() == false)
      {
         list<GcpPoint>::const_iterator iter;
         unsigned int i = 0;

         for (iter = gcps.begin(), i = 0; iter != gcps.end(); ++iter, ++i)
         {
            GcpPoint gcp = *iter;

            QTreeWidgetItem* pGcpItem = new QTreeWidgetItem(mpGcpTree);
            if (pGcpItem != NULL)
            {
               QString strLatitude;
               QString strLongitude;
               LatLonPoint latLonPoint(gcp.mCoordinate);

               string latText = latLonPoint.getLatitudeText();
               if (latText.empty() == false)
               {
                  strLatitude = QString::fromStdString(latText);
               }

               string longText = latLonPoint.getLongitudeText();
               if (longText.empty() == false)
               {
                  strLongitude = QString::fromStdString(longText);
               }

               pGcpItem->setText(0, QString("GCP ") + QString::number(i + 1));
               pGcpItem->setText(1, QString::number(gcp.mPixel.mX + 1.0));
               pGcpItem->setText(2, QString::number(gcp.mPixel.mY + 1.0));
               pGcpItem->setText(3, strLatitude);
               pGcpItem->setText(4, strLongitude);
            }
         }

         mpGcpTree->setEnabled(true);
      }
      else
      {
         mpGcpTree->setEnabled(false);
      }
   }

   // Bands
   QTreeWidgetItem* pBandsItem = new QTreeWidgetItem();
   if (pBandsItem != NULL)
   {
      unsigned int bands = pRasterDescriptor->getBandCount();

      pBandsItem->setText(0, "Bands");
      pBandsItem->setText(1, QString::number(bands));

      mpTreeWidget->insertTopLevelItem(4, pBandsItem);
      if (mReadOnly == false)
      {
         mpTreeWidget->setCellWidgetType(pBandsItem, 1, CustomTreeWidget::LINE_EDIT);
      }
   }

   // Interleave format
   InterleaveFormatType interleave = pRasterDescriptor->getInterleaveFormat();

   QTreeWidgetItem* pInterleaveItem = new QTreeWidgetItem();
   if (pInterleaveItem != NULL)
   {
      string interleaveText = StringUtilities::toDisplayString(interleave);
      if (interleave == BSQ)
      {
         const vector<const Filename*>& bandFiles = pRasterDescriptor->getBandFiles();
         if (bandFiles.size() > 0)
         {
            interleaveText += BSQ_MULTI_SUFFIX;
         }
         else
         {
            interleaveText += BSQ_SINGLE_SUFFIX;
         }
      }
      pInterleaveItem->setText(0, "Interleave Format");
      pInterleaveItem->setText(1, QString::fromStdString(interleaveText));

      mpTreeWidget->insertTopLevelItem(7, pInterleaveItem);
      if (mReadOnly == false)
      {
         QComboBox* pInterleaveCombo = new QComboBox(mpTreeWidget);
         pInterleaveCombo->setEditable(false);
         pInterleaveCombo->addItem(QString::fromStdString(StringUtilities::toDisplayString(BIL)));
         pInterleaveCombo->addItem(QString::fromStdString(StringUtilities::toDisplayString(BIP)));
         pInterleaveCombo->addItem(QString::fromStdString(StringUtilities::toDisplayString(BSQ) + BSQ_SINGLE_SUFFIX));
         pInterleaveCombo->addItem(QString::fromStdString(StringUtilities::toDisplayString(BSQ) + BSQ_MULTI_SUFFIX));
         pInterleaveCombo->hide();

         mpTreeWidget->setCellWidgetType(pInterleaveItem, 1, CustomTreeWidget::COMBO_BOX);
         mpTreeWidget->setComboBox(pInterleaveItem, 1, pInterleaveCombo);
      }
   }

   // Band files
   const vector<const Filename*>& bandFiles = pRasterDescriptor->getBandFiles();
   for (unsigned int i = 0; i < bandFiles.size(); ++i)
   {
      string bandFilename = bandFiles[i]->getFullPathAndName();
      if (bandFilename.empty() == false)
      {
         QTreeWidgetItem* pBandItem = new QTreeWidgetItem(pFilenameItem);
         if (pBandItem != NULL)
         {
            pBandItem->setText(0, QString::number(i + 1));
            pBandItem->setText(1, QString::fromStdString(bandFilename));

            if (mReadOnly == false)
            {
               mpTreeWidget->setCellWidgetType(pBandItem, 1, CustomTreeWidget::BROWSE_FILE_EDIT);
               mpTreeWidget->setFileBrowser(pBandItem, 1, mpFileBrowser);
            }
         }
      }
   }

   // Preband bytes
   QTreeWidgetItem* pPrebandBytesItem = new QTreeWidgetItem();
   if (pPrebandBytesItem != NULL)
   {
      QString strPrebandBytes;
      QColor cellColor = Qt::lightGray;

      if (interleave == BSQ)
      {
         strPrebandBytes = QString::number(pRasterDescriptor->getPrebandBytes());
         cellColor = Qt::white;
      }

      pPrebandBytesItem->setText(0, "Preband Bytes");
      pPrebandBytesItem->setText(1, strPrebandBytes);
      pPrebandBytesItem->setBackgroundColor(1, cellColor);

      mpTreeWidget->insertTopLevelItem(12, pPrebandBytesItem);
      if ((mReadOnly == false) && (interleave == BSQ))
      {
         mpTreeWidget->setCellWidgetType(pPrebandBytesItem, 1, CustomTreeWidget::LINE_EDIT);
      }
   }

   // Postband bytes
   QTreeWidgetItem* pPostbandBytesItem = new QTreeWidgetItem();
   if (pPostbandBytesItem != NULL)
   {
      QString strPostbandBytes;
      QColor cellColor = Qt::lightGray;

      if (interleave == BSQ)
      {
         strPostbandBytes = QString::number(pRasterDescriptor->getPostbandBytes());
         cellColor = Qt::white;
      }

      pPostbandBytesItem->setText(0, "Postband Bytes");
      pPostbandBytesItem->setText(1, strPostbandBytes);
      pPostbandBytesItem->setBackgroundColor(1, cellColor);

      mpTreeWidget->insertTopLevelItem(13, pPostbandBytesItem);
      if ((mReadOnly == false) && (interleave == BSQ))
      {
         mpTreeWidget->setCellWidgetType(pPostbandBytesItem, 1, CustomTreeWidget::LINE_EDIT);
      }
   }
}