text: //*******************************************************************
  //
  // See top level LICENSE.txt file.
  //
  // Author: Ken Melero
  // Contributor: David A. Horner (DAH) - http://dave.thehorners.com
  // 
  // Description: This class give the capability to access tiles from an
  //              ADRG file.
  //
  //********************************************************************
  // $Id: ossimAdrgTileSource.cpp 21631 2012-09-06 18:10:55Z dburken $
  
  #include <ossim/imaging/ossimAdrgTileSource.h>
  #include <ossim/imaging/ossimAdrgHeader.h>
  #include <ossim/base/ossimConstants.h>
  #include <ossim/base/ossimCommon.h>
  #include <ossim/base/ossimKeywordNames.h>
  #include <ossim/base/ossimTrace.h>
  #include <ossim/base/ossimKeywordlist.h>
  #include <ossim/base/ossimEllipsoid.h>
  #include <ossim/base/ossimDatum.h>
  #include <ossim/base/ossimUnitTypeLut.h>
  #include <ossim/base/ossimStringProperty.h>
  #include <ossim/base/ossimContainerProperty.h>
  #include <ossim/imaging/ossimImageDataFactory.h>
  #include <ossim/imaging/ossimImageGeometry.h>
  #include <ossim/imaging/ossimTiffTileSource.h>
  #include <ossim/imaging/ossimU8ImageData.h>
  #include <ossim/projection/ossimMapProjection.h>
  #include <ossim/projection/ossimEquDistCylProjection.h>
  #include <ossim/projection/ossimProjectionFactoryRegistry.h>
  #include <iostream>
  
  RTTI_DEF1(ossimAdrgTileSource, "ossimAdrgTileSource", ossimImageHandler)
  
  //***
  // Static trace for debugging
  //***
  static ossimTrace traceDebug("ossimAdrgTileSource:debug");
  
  //***
  // NOTE:  Currently this tilesource is hard-coded to access tiles
  //        from the ZDR image file. This will change as I wrote support
  //        data classes for all the ADRG image files: ZDR, overview,
  //        and legend support data.  Next step in development will
  //        be to put in flags to access overview and legend image files.
  //***
  
  using namespace std;
  
  //*******************************************************************
  // Public Constructor:
  //*******************************************************************
  ossimAdrgTileSource::ossimAdrgTileSource()
     :
        ossimImageHandler(),
        m_Tile(0),
        m_TileBuffer(0),
        m_FileStr(),
        m_AdrgHeader(0)
  {
     // Construction not complete.  Users should call "open" method.
  }
  
  ossimAdrgTileSource::~ossimAdrgTileSource()
  {
     if(m_AdrgHeader)
     {
        delete m_AdrgHeader;
        m_AdrgHeader = 0;
     }
     if (m_TileBuffer)
     {
        delete [] m_TileBuffer;
        m_TileBuffer = 0;
     }
  
     close();
  }
  
  ossimRefPtr<ossimImageData> ossimAdrgTileSource::getTile(
     const ossimIrect& rect,
     ossim_uint32 resLevel)
  {
     if (m_Tile.valid())
     {
        // Image rectangle must be set prior to calling getTile.
        m_Tile->setImageRectangle(rect);
        
        if ( getTile( m_Tile.get(), resLevel ) == false )
        {
           if (m_Tile->getDataObjectStatus() != OSSIM_NULL)
           {
              m_Tile->makeBlank();
           }
        }
     }
     
     return m_Tile;
  }
  
  bool ossimAdrgTileSource::getTile(ossimImageData* result,
                                    ossim_uint32 resLevel)
  {
     bool status = false;
  
     //---
     // Not open, this tile source bypassed, or invalid res level,
     // return a blank tile.
     //---
     if( isOpen() && isSourceEnabled() && isValidRLevel(resLevel) &&
         result && (result->getNumberOfBands() == getNumberOfOutputBands()) )
     {
        result->ref();  // Increment ref count.
  
        //---
        // Check for overview tile.  Some overviews can contain r0 so always
        // call even if resLevel is 0.  Method returns true on success, false
        // on error.
        //---
        status = getOverviewTile(resLevel, result);
        
        if (!status) // Did not get an overview tile.
        {
           status = true;
           
           ossimIrect tile_rect = result->getImageRectangle();
           
           ossimIrect image_rect = getImageRectangle(resLevel);
           
           result->makeBlank();
           
           //---
           // See if any point of the requested tile is in the image.
           //---
           if ( tile_rect.intersects(image_rect) )
           {
              ossimIrect clip_rect = tile_rect.clipToRect(image_rect);
              
              // Load the tile buffer with data from the adrg.
              status = fillBuffer(tile_rect, clip_rect, result);
  
              if (status)
              {
                 result->validate();
              }
           }
        }
  
        result->unref();  // Decrement ref count.
     }
  
     return status;
  }
  
  //*******************************************************************
  // Private Method:
  //*******************************************************************
  bool ossimAdrgTileSource::fillBuffer(const ossimIrect& /* tile_rect */,
                                       const ossimIrect& clip_rect,
                                       ossimImageData* tile)
  {
     //***
     // Shift the upper left corner of the "clip_rect" to the an even chunk
     // boundry.
     //***
     ossimIpt tileOrigin = clip_rect.ul();
     adjustToStartOfTile(tileOrigin);
  
     //***
     // Calculate the number of tiles needed in the line/sample directions.
     //***
     ossim_int32 size_in_x = clip_rect.lr().x - tileOrigin.x + 1;
     ossim_int32 size_in_y = clip_rect.lr().y - tileOrigin.y + 1;
     
     ossim_int32 tiles_in_x_dir = size_in_x / ADRG_TILE_WIDTH  +
        (size_in_x % ADRG_TILE_WIDTH  ? 1 : 0);
     ossim_int32 tiles_in_y_dir = size_in_y / ADRG_TILE_HEIGHT +
        (size_in_y % ADRG_TILE_HEIGHT ? 1 : 0);
  
  
     ossimIpt ulTilePt = tileOrigin;
     
     // Chunk loop in line direction.
     for (ossim_int32 y=0; y<tiles_in_y_dir; y++)
     {
        ulTilePt.x = tileOrigin.x;
  
        // Tile loop in sample direction.
        for (ossim_int32 x=0; x<tiles_in_x_dir; x++)
        {
           ossimIrect adrg_tile_rect(ulTilePt.x,
                                     ulTilePt.y,
                                     ulTilePt.x + ADRG_TILE_WIDTH- 1,
                                     ulTilePt.y + ADRG_TILE_HEIGHT - 1);
  
           if (adrg_tile_rect.intersects(clip_rect))
           {
              ossimIrect tile_clip_rect = clip_rect.clipToRect(adrg_tile_rect);
              
              //---
              // Some point in the chip intersect the tile so grab the
              // data.
              //---
              ossim_int32 row = (ossim_int32) ulTilePt.y / ADRG_TILE_HEIGHT; 
              ossim_int32 col = (ossim_int32) ulTilePt.x / ADRG_TILE_WIDTH;
              ossim_int32 tileOffset = m_AdrgHeader->tim(row, col);
  
              if(tileOffset != 0)
              {
                 // Get the data.
                 int seek_position = (tileOffset - 1) * 49152 + 2048;
                 int band;
  
                 // seek to start of chip
                 m_FileStr.seekg(seek_position, ios::beg);
                 for (band=0; band<3; band++)
                 {
                    //***
                    // Read the chip from the ccf file into the chunk buffer.
                    // This will get all the bands.  Bands are interleaved by
                    // chip.
                    //***
                    if (!m_FileStr.read((char*)m_TileBuffer,
                                         ADRG_TILE_SIZE))
                    {
                       theErrorStatus = ossimErrorCodes::OSSIM_ERROR;
                       return false;
                    }
  
                    tile->loadBand(m_TileBuffer,
                                   adrg_tile_rect,
                                   tile_clip_rect,
                                   band);
  
                 } // End of band loop.
                 
              } // End of if (tileOffset != 0)
              
           } // End of if (adrg_tile_rect.intersects(clip_rect))
           
           ulTilePt.x += ADRG_TILE_WIDTH;
           
        }  // End of tile loop in the sample direction.
  
        ulTilePt.y += ADRG_TILE_HEIGHT;
        
     }  // End of tile loop in the line direction.
  
     return true;
  }
  
  ossim_uint32 ossimAdrgTileSource::getNumberOfOutputBands()const
  {
     return getNumberOfInputBands();
  }
  
  //*******************************************************************
  // Public Method:
  //*******************************************************************
  ossimIrect
  ossimAdrgTileSource::getImageRectangle(ossim_uint32 reduced_res_level) const
  {
     return ossimIrect(0,                         // upper left x
                       0,                         // upper left y
                       getNumberOfSamples(reduced_res_level) - 1,  // lower right x
                       getNumberOfLines(reduced_res_level)   - 1); // lower right y
  }
  
  void ossimAdrgTileSource::close()
  {
     if(m_AdrgHeader)
     {
        delete m_AdrgHeader;
        m_AdrgHeader = 0;
     }
     if(m_FileStr.is_open())
     {
        m_FileStr.close();
     }
     m_Tile = 0;
  }
  
  //*******************************************************************
  // Public method:
  //*******************************************************************
  bool ossimAdrgTileSource::open()
  {
     static const char MODULE[] = "ossimAdrgTileSource::open";
  
     if(traceDebug())
     {
        ossimNotify(ossimNotifyLevel_DEBUG)
           << MODULE << "Entered..."<<std::endl;
     }
     if(isOpen())
     {
        close();
     }
     if(m_AdrgHeader)
     {
        delete m_AdrgHeader;
        m_AdrgHeader = 0;
     }
     // Instantiate support data class to parse header file.
     m_AdrgHeader = new ossimAdrgHeader(theImageFile);
  
     // Check for errors.
     if (m_AdrgHeader->errorStatus() == ossimErrorCodes::OSSIM_ERROR)
     {
        if (traceDebug())
        {
           ossimNotify(ossimNotifyLevel_WARN)
              << MODULE << " Error in ossimAdrg header detected." << std::endl;
        }
  
        close();
        return false;
     }
  
     m_FileStr.open(m_AdrgHeader->imageFile().c_str(),
                     ios::in | ios::binary);
  
     // Check the file pointer.
     if(!m_FileStr)
     {
        theErrorStatus = ossimErrorCodes::OSSIM_ERROR;
        if (traceDebug())
        {
           ossimNotify(ossimNotifyLevel_WARN) << MODULE << "\nCannot open:  "
                << m_AdrgHeader->imageFile().c_str() << std::endl;
        }
        close();
        
        return false;
     }
     
     if(traceDebug())
     {
        ossimNotify(ossimNotifyLevel_DEBUG)
           << MODULE
           << "File is opened -> "<< m_AdrgHeader->imageFile()<<std::endl;
     }
  
     // allow the base handler to check for other overrides
     completeOpen();
     // Allocate memory.
     m_Tile      = ossimImageDataFactory::instance()->create(this, this);
     m_Tile->initialize();
     m_TileBuffer  = new ossim_uint8[ADRG_TILE_SIZE];
  
     if (traceDebug())
     {
        ossimNotify(ossimNotifyLevel_DEBUG)
           << "\nminLon:   " << m_AdrgHeader->minLon()
           << "\nminLond:  " << m_AdrgHeader->minLongitude() 
           << "\nminLat:   " << m_AdrgHeader->minLat()
           << "\nminLatd:  " << m_AdrgHeader->minLatitude()
           << "\nmaxLon:   " << m_AdrgHeader->maxLon()
           << "\nmaxLond:  " << m_AdrgHeader->maxLongitude()
           << "\nmaxLat:   " << m_AdrgHeader->maxLat()
           << "\nmaxLatd:  " << m_AdrgHeader->maxLatitude()
           << std::endl;
     }
     
     return true;
  }
  
  //*******************************************************************
  // Public method:
  //*******************************************************************
  bool ossimAdrgTileSource::saveState(ossimKeywordlist& kwl,
                                      const char* prefix) const
  {
     // Currently nothing to do here.
     return ossimImageHandler::saveState(kwl, prefix);
  }
  
  //*******************************************************************
  // Public method:
  //*******************************************************************
  bool ossimAdrgTileSource::loadState(const ossimKeywordlist& kwl,
                                      const char* prefix)
  {
     if (ossimImageHandler::loadState(kwl, prefix))
     {
        if (open())
        {
           return true;
        }
     }
  
     return false;
  }
  
  //*******************************************************************
  // Public method:
  //*******************************************************************
  ossimRefPtr<ossimImageGeometry> ossimAdrgTileSource::getImageGeometry()
  {
     if ( !theGeometry )
     {
        // Check for external geom:
        theGeometry = getExternalImageGeometry();
        
        if ( !theGeometry )
        {
           // origin of latitude
           ossim_float64 originLatitude = (m_AdrgHeader->maxLatitude() +
                                           m_AdrgHeader->minLatitude()) / 2.0;
           
           // central meridian.
           ossim_float64 centralMeridian = (m_AdrgHeader->maxLongitude() +
                                            m_AdrgHeader->minLongitude()) / 2.0;
           
           //---
           // Compute the pixel size in latitude and longitude direction.  This will
           // be full image extents divided by full image lines and samples.
           //---
           
           // Samples in full image (used to compute degPerPixelX).
           ossim_float64 samples = m_AdrgHeader->samples();
           
           // Lines in full image (used to compute degPerPixelX).
           ossim_float64 lines = m_AdrgHeader->lines();
           
           // Degrees in latitude direction of the full image.
           ossim_float64 degrees_in_lat_dir = m_AdrgHeader->maxLatitude() -
              m_AdrgHeader->minLatitude();
           
           // Degrees in longitude direction of the full image.
           ossim_float64 degrees_in_lon_dir = m_AdrgHeader->maxLongitude() -
              m_AdrgHeader->minLongitude();
           
           ossim_float64 degPerPixelY = degrees_in_lat_dir / lines;
           ossim_float64 degPerPixelX = degrees_in_lon_dir / samples;
           
           //---
           // The tie is determined with the following assumptions that need to be
           // verified:
           // 1) Rows and columns start at 1.
           // 2) The min / max latitudes longitudes go to the edge of the pixel.
           // 3) Latitude decreases by degPerPixelY with each line.
           // 4) Longitude increases by degPerPixelX with each sample.
           //---
           ossim_float64 ul_lat = (m_AdrgHeader->maxLatitude() - 
                                   ( (m_AdrgHeader->startRow() - 1) *
                                     degPerPixelY ) - ( degPerPixelY * 0.5 ) );
           ossim_float64 ul_lon = (m_AdrgHeader->minLongitude() +
                                   ( (m_AdrgHeader->startCol() -1) *
                                     degPerPixelX ) +  ( degPerPixelX * 0.5 ) );
           
           // projection type
           ossimKeywordlist kwl;
           const char* prefix = 0;
           kwl.add(prefix,
                   ossimKeywordNames::TYPE_KW,
                   "ossimEquDistCylProjection",
                   true);
           
           // datum.
           kwl.add(prefix,
                   ossimKeywordNames::DATUM_KW,
                   "WGE",
                   true);
           
           // origin latitude
           kwl.add(prefix,
                   ossimKeywordNames::ORIGIN_LATITUDE_KW,
                   originLatitude,
                   true);
  
           // central meridin
           kwl.add(prefix,
                   ossimKeywordNames::CENTRAL_MERIDIAN_KW,
                   centralMeridian,
                   true);
  
           // Save the tie point.
           kwl.add(prefix,
                   ossimKeywordNames::TIE_POINT_XY_KW,
                   ossimDpt(ul_lon, ul_lat).toString().c_str(),
                   true);
           kwl.add(prefix,
                   ossimKeywordNames::TIE_POINT_UNITS_KW,
                   ossimUnitTypeLut::instance()->getEntryString(OSSIM_DEGREES),
                   true);
  
           // Save the scale.
           kwl.add(prefix,
                   ossimKeywordNames::TIE_POINT_LAT_KW,
                   ul_lat,
                   true);
     
           kwl.add(prefix,
                   ossimKeywordNames::TIE_POINT_LON_KW,
                   ul_lon,
                   true);
  
           // Save the scale.
           kwl.add(prefix,
                   ossimKeywordNames::PIXEL_SCALE_XY_KW,
                   ossimDpt(degPerPixelX, degPerPixelY).toString().c_str(),
                   true);
           kwl.add(prefix,
                   ossimKeywordNames::PIXEL_SCALE_UNITS_KW,
                   ossimUnitTypeLut::instance()->getEntryString(OSSIM_DEGREES),
                   true);  
  
           // lines
           kwl.add(prefix,
                   ossimKeywordNames::NUMBER_LINES_KW,
                   getNumberOfLines());
  
           // samples
           kwl.add(prefix,
                   ossimKeywordNames::NUMBER_SAMPLES_KW,
                   getNumberOfSamples());
  
           // res sets
           kwl.add(prefix,
                   ossimKeywordNames::NUMBER_REDUCED_RES_SETS_KW,
                   getNumberOfDecimationLevels());
  
           // bands
           kwl.add(prefix,
                   ossimKeywordNames::NUMBER_INPUT_BANDS_KW,
                   getNumberOfInputBands());
  
           // bands
           kwl.add(prefix,
                   ossimKeywordNames::NUMBER_OUTPUT_BANDS_KW,
                   getNumberOfOutputBands());
     
           if (traceDebug())
           {
              ossimNotify(ossimNotifyLevel_DEBUG)
                 << "\nminLon:             " << m_AdrgHeader->minLon()
                 << "\nminLond:            " << m_AdrgHeader->minLongitude() 
                 << "\nminLat:             " << m_AdrgHeader->minLat()
                 << "\nminLatd:            " << m_AdrgHeader->minLatitude()
                 << "\nmaxLon:             " << m_AdrgHeader->maxLon()
                 << "\nmaxLond:            " << m_AdrgHeader->maxLongitude()
                 << "\nmaxLat:             " << m_AdrgHeader->maxLat()
                 << "\nmaxLatd:            " << m_AdrgHeader->maxLatitude()
                 << "\nstartRow:           " << m_AdrgHeader->startRow()
                 << "\nstartCol:           " << m_AdrgHeader->startCol()
                 << "\nstopRow:            " << m_AdrgHeader->stopRow()
                 << "\nstopCol:            " << m_AdrgHeader->stopCol()
                 << "\nfull image lines:   " << lines
                 << "\nfull image samples: " << samples
                 << "\nkwl:\n"               << kwl
                 << std::endl;
           }
  
           ossimProjection* new_proj = ossimProjectionFactoryRegistry::instance()->createProjection(kwl);
           theGeometry = new ossimImageGeometry;
           theGeometry->setProjection(new_proj);  // assumes management of projection instance
           
        } // matches (after getExternalImageGeometry()):  if ( !theGeometry ) 
        
        // Set image things the geometry object should know about.
        initImageParameters( theGeometry.get() );
        
     } // matches: if ( !theGeometry )
  
     return theGeometry;
  }
  
  //*******************************************************************
  // Public method:
  //*******************************************************************
  ossimScalarType ossimAdrgTileSource::getOutputScalarType() const
  {
    return OSSIM_UCHAR;
  }
  
  //*******************************************************************
  // Public method:
  //*******************************************************************
  ossim_uint32 ossimAdrgTileSource::getTileWidth() const
  {
     return ( m_Tile.valid() ? m_Tile->getWidth() : 0 );
  }
  
  //*******************************************************************
  // Public method:
  //*******************************************************************
  ossim_uint32 ossimAdrgTileSource::getTileHeight() const
  {
     return ( m_Tile.valid() ? m_Tile->getHeight() : 0 );
  }
  
  //*******************************************************************
  // Public method:
  //*******************************************************************
  ossim_uint32 ossimAdrgTileSource::getNumberOfLines(ossim_uint32 reduced_res_level) const
  {
     if ( (reduced_res_level == 0) && m_AdrgHeader )
     {
        return (m_AdrgHeader->stopRow() - m_AdrgHeader->startRow()) + 1;
     }
     else if (theOverview.valid())
     {
        return theOverview->getNumberOfLines(reduced_res_level);
     }
  
     return 0;
  }
  
  //*******************************************************************
  // Public method:
  //*******************************************************************
  ossim_uint32 ossimAdrgTileSource::getNumberOfSamples(ossim_uint32 reduced_res_level) const
  {
     if ( (reduced_res_level == 0) && m_AdrgHeader )
     {
        return (m_AdrgHeader->stopCol() - m_AdrgHeader->startCol()) + 1;
     }
     else if (theOverview.valid())
     {
        return theOverview->getNumberOfSamples(reduced_res_level);
     }
  
     return 0;
  }
  
  ossim_uint32 ossimAdrgTileSource::getImageTileWidth() const
  {
     return ADRG_TILE_WIDTH;
  }
  
  ossim_uint32 ossimAdrgTileSource::getImageTileHeight() const
  {
     return ADRG_TILE_HEIGHT;
  }
  
  ossimRefPtr<ossimProperty> ossimAdrgTileSource::getProperty(const ossimString& name)const
  {
     if(name == "file_type")
     {
        return new ossimStringProperty(name, "ADRG");
     }
     
     return ossimImageHandler::getProperty(name);
  }
  
  void ossimAdrgTileSource::getPropertyNames(std::vector<ossimString>& propertyNames)const
  {
  	ossimImageHandler::getPropertyNames(propertyNames);
  	propertyNames.push_back("file_type");
  }
  
  ossimString ossimAdrgTileSource::getShortName()const
  {
     return ossimString("adrg");
  }
  
  ossimString ossimAdrgTileSource::getLongName()const
  {
     return ossimString("adrg reader");
  }
  
  ossim_uint32 ossimAdrgTileSource::getNumberOfInputBands() const
  {
     ossim_uint32 bands = 1;
     if ( m_AdrgHeader )
     {
        bands = m_AdrgHeader->numberOfBands();
     }
     return bands;
  }
  
  bool ossimAdrgTileSource::isOpen()const
  {
     return (m_AdrgHeader!=0);
  }
  
  void ossimAdrgTileSource::adjustToStartOfTile(ossimIpt& pt) const
  {
     pt.x &= 0xffffff80;
     pt.y &= 0xffffff80;
  }
  