text: /******************************************************************************
   * $Id: grcdataset.cpp 24355 2012-05-01 10:52:54Z rouault $
   *
   * Project:  GRC Reader
   * Purpose:  GDAL driver for Northwood Classified Format
   * Author:   Perry Casson
   *
   ******************************************************************************
   * Copyright (c) 2007, Waypoint Information Technology
   *
   * Permission is hereby granted, free of charge, to any person obtaining a
   * copy of this software and associated documentation files (the "Software"),
   * to deal in the Software without restriction, including without limitation
   * the rights to use, copy, modify, merge, publish, distribute, sublicense,
   * and/or sell copies of the Software, and to permit persons to whom the
   * Software is furnished to do so, subject to the following conditions:
   *
   * The above copyright notice and this permission notice shall be included
   * in all copies or substantial portions of the Software.
   *
   * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
   * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
   * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
   * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
   * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
   * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
   * DEALINGS IN THE SOFTWARE.
   ****************************************************************************/
  
  #include "gdal_pam.h"
  #include "northwood.h"
  
  #ifdef OGR_ENABLED
  #ifdef MSVC
  #include "..\..\ogr\ogrsf_frmts\mitab\mitab.h"
  #else
  #include "../../ogr/ogrsf_frmts/mitab/mitab.h"
  #endif
  #endif
  
  
  CPL_C_START void GDALRegister_NWT_GRC( void );
  CPL_C_END
  /************************************************************************/
  /* ==================================================================== */
  /*                             NWT_GRCDataset                           */
  /* ==================================================================== */
  /************************************************************************/
  class NWT_GRCRasterBand;
  
  class NWT_GRCDataset : public GDALPamDataset
  {
    friend class NWT_GRCRasterBand;
  
    private:
      VSILFILE * fp;
      GByte abyHeader[1024];
      NWT_GRID *pGrd;
      char **papszCategories;
      char *pszProjection;
  
    protected:
      GDALColorTable * poColorTable;
  
    public:
      NWT_GRCDataset();
      ~NWT_GRCDataset();
  
      static GDALDataset *Open( GDALOpenInfo * );
      static int Identify( GDALOpenInfo * poOpenInfo );
  
      CPLErr GetGeoTransform( double *padfTransform );
      const char *GetProjectionRef();
  };
  
  /************************************************************************/
  /* ==================================================================== */
  /*                            NWT_GRCRasterBand                         */
  /* ==================================================================== */
  /************************************************************************/
  
  class NWT_GRCRasterBand : public GDALPamRasterBand
  {
    friend class NWT_GRCDataset;
      int bHaveOffsetScale;
      double dfOffset;
      double dfScale;
  
    public:
  
      NWT_GRCRasterBand( NWT_GRCDataset *, int );
      virtual ~NWT_GRCRasterBand();
  
      virtual CPLErr IReadBlock( int, int, void * );
      virtual double GetNoDataValue( int *pbSuccess );
  
      virtual double GetOffset( int *pbSuccess = NULL );
      virtual CPLErr SetOffset( double dfNewValue );
      virtual double GetScale( int *pbSuccess = NULL );
      virtual CPLErr SetScale( double dfNewValue );
  
      virtual GDALColorInterp GetColorInterpretation();
      virtual char **GetCategoryNames();
      virtual GDALColorTable *GetColorTable();
  };
  
  
  /************************************************************************/
  /*                           NWT_GRCRasterBand()                        */
  /************************************************************************/
  
  NWT_GRCRasterBand::NWT_GRCRasterBand( NWT_GRCDataset * poDS, int nBand )
  {
      this->poDS = poDS;
      this->nBand = nBand;
      NWT_GRCDataset *poGDS =( NWT_GRCDataset * ) poDS;
  
      bHaveOffsetScale = FALSE;
      dfOffset = 0;
      dfScale = 1.0;
      if( poGDS->pGrd->nBitsPerPixel == 8 )
          eDataType = GDT_Byte;
      else if( poGDS->pGrd->nBitsPerPixel == 16 )
          eDataType = GDT_UInt16;
      else if( poGDS->pGrd->nBitsPerPixel == 32 )
          eDataType = GDT_UInt32;        // this would be funny
  
      nBlockXSize = poDS->GetRasterXSize();
      nBlockYSize = 1;
  
      // load the color table and might as well to the ClassNames
      poGDS->poColorTable = new GDALColorTable();
  
      GDALColorEntry oEntry;
      // null value = 0 is transparent
      oEntry.c1 = 255;
      oEntry.c2 = 255;
      oEntry.c3 = 255;
      oEntry.c4 = 255;                // alpha 255 = transparent
  
      poGDS->poColorTable->SetColorEntry( 0, &oEntry );
  
      int i;
      for( i=0; i < (int) poGDS->pGrd->stClassDict->nNumClassifiedItems; i++ )
      {
          oEntry.c1 = poGDS->pGrd->stClassDict->stClassifedItem[i]->r;
          oEntry.c2 = poGDS->pGrd->stClassDict->stClassifedItem[i]->g;
          oEntry.c3 = poGDS->pGrd->stClassDict->stClassifedItem[i]->b;
          oEntry.c4 = 0;            // alpha 0 = solid
  
          poGDS->poColorTable->SetColorEntry( poDS->pGrd->
                                            stClassDict->stClassifedItem[i]->
                                            usPixVal, &oEntry );
      }
  
      // find the max value used in the grc
      int maxValue = 0;
      for( i=0; i < (int) poDS->pGrd->stClassDict->nNumClassifiedItems; i++ )
      {
          if( poDS->pGrd->stClassDict->stClassifedItem[i]->usPixVal > maxValue )
              maxValue = poDS->pGrd->stClassDict->stClassifedItem[i]->usPixVal;
      }
  
      // load a value for the null value
      poGDS->papszCategories = CSLAddString( poGDS->papszCategories, "No Data" );
  
      // for the class names we need to load nulls string for all classes that
      // are not defined
      for( int val = 1; val <= maxValue; val++ )
      {
          int i;
          // loop throught the GRC dictionary to see if the value is defined
          for( i=0; i < (int) poDS->pGrd->stClassDict->nNumClassifiedItems; i++ )
          {
              if( (int) poDS->pGrd->stClassDict->stClassifedItem[i]->usPixVal ==
                  val )
              {
                  poGDS->papszCategories =
                      CSLAddString( poGDS->papszCategories,
                                      poDS->pGrd->stClassDict->
                                      stClassifedItem[i]->szClassName );
                  break;
              }
          }
          if( i >= (int) poDS->pGrd->stClassDict->nNumClassifiedItems )
              poGDS->papszCategories = CSLAddString( poGDS->papszCategories, "" );
  
      }
  }
  
  NWT_GRCRasterBand::~NWT_GRCRasterBand()
  {
  }
  
  double NWT_GRCRasterBand::GetNoDataValue( int *pbSuccess )
  {
      if( pbSuccess != NULL )
          *pbSuccess = TRUE;
  
      return 0;                        //Northwood grid 0 is always null
  }
  
  // return an array of null terminated strings for the class names
  char **NWT_GRCRasterBand::GetCategoryNames()
  {
      NWT_GRCDataset *poGDS = (NWT_GRCDataset *) poDS;
  
      return poGDS->papszCategories;
  }
  
  // return the color table
  GDALColorTable *NWT_GRCRasterBand::GetColorTable()
  {
      NWT_GRCDataset *poGDS = (NWT_GRCDataset *) poDS;
  
      return poGDS->poColorTable;
  }
  
  GDALColorInterp NWT_GRCRasterBand::GetColorInterpretation()
  {
      if( nBand == 1 )
          return GCI_PaletteIndex;
      else
          return GCI_Undefined;
  }
  
  /************************************************************************/
  /*                             IReadBlock()                             */
  /************************************************************************/
  CPLErr NWT_GRCRasterBand::IReadBlock( int nBlockXOff, int nBlockYOff,
                                          void *pImage )
  {
      NWT_GRCDataset *poGDS =(NWT_GRCDataset *) poDS;
      int nRecordSize = nBlockXSize *( poGDS->pGrd->nBitsPerPixel / 8 );
  
      if( nBand == 1 )
      {                            //grc's are just one band of indices
          VSIFSeekL( poGDS->fp, 1024 + nRecordSize * (vsi_l_offset)nBlockYOff, SEEK_SET );
          VSIFReadL( pImage, 1, nRecordSize, poGDS->fp );
      }
      else
      {
          CPLError( CE_Failure, CPLE_IllegalArg,
                    "No band number %d",
                    nBand );
          return CE_Failure;
      }
      return CE_None;
  }
  
  
  /************************************************************************/
  /*                             GetOffset()                              */
  /************************************************************************/
  double NWT_GRCRasterBand::GetOffset( int *pbSuccess )
  {
      if( pbSuccess )
          *pbSuccess = bHaveOffsetScale;
      return dfOffset;
  }
  
  /************************************************************************/
  /*                             SetOffset()                              */
  /************************************************************************/
  CPLErr NWT_GRCRasterBand::SetOffset( double dfNewValue )
  {
      //poGDS->bMetadataChanged = TRUE;
  
      bHaveOffsetScale = TRUE;
      dfOffset = dfNewValue;
      return CE_None;
  }
  
  /************************************************************************/
  /*                              GetScale()                              */
  /************************************************************************/
  double NWT_GRCRasterBand::GetScale( int *pbSuccess )
  {
      if( pbSuccess )
          *pbSuccess = bHaveOffsetScale;
      return dfScale;
  }
  
  /************************************************************************/
  /*                              SetScale()                              */
  /************************************************************************/
  CPLErr NWT_GRCRasterBand::SetScale( double dfNewValue )
  {
      bHaveOffsetScale = TRUE;
      dfScale = dfNewValue;
      return CE_None;
  }
  
  /************************************************************************/
  /* ==================================================================== */
  /*                          NWT_GRCDataset                              */
  /* ==================================================================== */
  /************************************************************************/
  NWT_GRCDataset::NWT_GRCDataset()
  {
      poColorTable = NULL;
      papszCategories = NULL;
      pszProjection = NULL;
  }
  
  
  /************************************************************************/
  /*                            ~NWT_GRCDataset()                         */
  /************************************************************************/
  NWT_GRCDataset::~NWT_GRCDataset()
  {
      delete poColorTable;
      CSLDestroy( papszCategories );
  
      FlushCache();
      pGrd->fp = NULL;       // this prevents nwtCloseGrid from closing the fp
      nwtCloseGrid( pGrd );
  
      if( fp != NULL )
          VSIFCloseL( fp );
  
      CPLFree( pszProjection );
  }
  
  /************************************************************************/
  /*                          GetGeoTransform()                           */
  /************************************************************************/
  CPLErr NWT_GRCDataset::GetGeoTransform( double *padfTransform )
  {
      padfTransform[0] = pGrd->dfMinX - ( pGrd->dfStepSize * 0.5 );
      padfTransform[3] = pGrd->dfMaxY + ( pGrd->dfStepSize * 0.5 );
      padfTransform[1] = pGrd->dfStepSize;
      padfTransform[2] = 0.0;
  
      padfTransform[4] = 0.0;
      padfTransform[5] = -1 * pGrd->dfStepSize;
  
      return CE_None;
  }
  
  /************************************************************************/
  /*                          GetProjectionRef()                          */
  /************************************************************************/
  const char *NWT_GRCDataset::GetProjectionRef()
  {
  #ifdef OGR_ENABLED
      if (pszProjection == NULL)
      {
          OGRSpatialReference *poSpatialRef;
          poSpatialRef = MITABCoordSys2SpatialRef( pGrd->cMICoordSys );
          if (poSpatialRef)
          {
              poSpatialRef->exportToWkt( &pszProjection );
              poSpatialRef->Release();
          }
      }
  #endif
      return ( (const char *) pszProjection );
  }
  
  /************************************************************************/
  /*                              Identify()                              */
  /************************************************************************/
  
  int NWT_GRCDataset::Identify( GDALOpenInfo * poOpenInfo )
  {
  /* -------------------------------------------------------------------- */
  /*  Look for the header                                                 */
  /* -------------------------------------------------------------------- */
      if( poOpenInfo->nHeaderBytes < 50 )
          return FALSE;
  
      if( poOpenInfo->pabyHeader[0] != 'H' ||
          poOpenInfo->pabyHeader[1] != 'G' ||
          poOpenInfo->pabyHeader[2] != 'P' ||
          poOpenInfo->pabyHeader[3] != 'C' ||
          poOpenInfo->pabyHeader[4] != '8' )
          return FALSE;
  
      return TRUE;
  }
  
  /************************************************************************/
  /*                                Open()                                */
  /************************************************************************/
  
  GDALDataset *NWT_GRCDataset::Open( GDALOpenInfo * poOpenInfo )
  {
      if( !Identify(poOpenInfo) )
          return NULL;
  
  /* -------------------------------------------------------------------- */
  /*      Create a corresponding GDALDataset.                             */
  /* -------------------------------------------------------------------- */
      NWT_GRCDataset *poDS;
  
      poDS = new NWT_GRCDataset();
  
      poDS->fp = VSIFOpenL(poOpenInfo->pszFilename, "rb");
      if (poDS->fp == NULL)
      {
          delete poDS;
          return NULL;
      }
  
  /* -------------------------------------------------------------------- */
  /*      Read the header.                                                */
  /* -------------------------------------------------------------------- */
      VSIFSeekL( poDS->fp, 0, SEEK_SET );
      VSIFReadL( poDS->abyHeader, 1, 1024, poDS->fp );
      poDS->pGrd = (NWT_GRID *) malloc( sizeof (NWT_GRID) );
  
      poDS->pGrd->fp = poDS->fp;
  
      if (!nwt_ParseHeader( poDS->pGrd, (char *) poDS->abyHeader ) ||
          !GDALCheckDatasetDimensions(poDS->pGrd->nXSide, poDS->pGrd->nYSide) ||
          poDS->pGrd->stClassDict == NULL)
      {
          delete poDS;
          return NULL;
      }
  
      poDS->nRasterXSize = poDS->pGrd->nXSide;
      poDS->nRasterYSize = poDS->pGrd->nYSide;
  
  /* -------------------------------------------------------------------- */
  /*      Create band information objects.                                */
  /* -------------------------------------------------------------------- */
      poDS->SetBand( 1, new NWT_GRCRasterBand( poDS, 1) );    //Class Indexes
  
  /* -------------------------------------------------------------------- */
  /*      Initialize any PAM information.                                 */
  /* -------------------------------------------------------------------- */
      poDS->SetDescription( poOpenInfo->pszFilename );
      poDS->TryLoadXML();
  
  /* -------------------------------------------------------------------- */
  /*      Check for external overviews.                                   */
  /* -------------------------------------------------------------------- */
      poDS->oOvManager.Initialize( poDS, poOpenInfo->pszFilename, poOpenInfo->papszSiblingFiles );
  
      return (poDS);
  }
  
  
  /************************************************************************/
  /*                          GDALRegister_GRC()                          */
  /************************************************************************/
  
  void
  GDALRegister_NWT_GRC()
  {
      GDALDriver *poDriver;
  
      if( GDALGetDriverByName( "NWT_GRC" ) == NULL )
      {
          poDriver = new GDALDriver();
  
          poDriver->SetDescription( "NWT_GRC" );
          poDriver->SetMetadataItem( GDAL_DMD_LONGNAME,
                                   "Northwood Classified Grid Format .grc/.tab");
          poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC,
                                   "frmt_various.html#northwood_grc" );
          poDriver->SetMetadataItem( GDAL_DMD_EXTENSION, "grc" );
          poDriver->SetMetadataItem( GDAL_DCAP_VIRTUALIO, "YES" );
  
          poDriver->pfnOpen = NWT_GRCDataset::Open;
          poDriver->pfnIdentify = NWT_GRCDataset::Identify;
  
          GetGDALDriverManager()->RegisterDriver( poDriver );
      }
  }
  