LCOV - code coverage report
Current view: directory - frmts/rmf - rmfdataset.cpp (source / functions) Found Hit Coverage
Test: gdal_filtered.info Lines: 750 578 77.1 %
Date: 2012-12-26 Functions: 28 20 71.4 %

       1                 : /******************************************************************************
       2                 :  * $Id: rmfdataset.cpp 24731 2012-08-03 17:02:56Z rouault $
       3                 :  *
       4                 :  * Project:  Raster Matrix Format
       5                 :  * Purpose:  Read/write raster files used in GIS "Integratsia"
       6                 :  *           (also known as "Panorama" GIS).
       7                 :  * Author:   Andrey Kiselev, dron@ak4719.spb.edu
       8                 :  *
       9                 :  ******************************************************************************
      10                 :  * Copyright (c) 2005, Andrey Kiselev <dron@ak4719.spb.edu>
      11                 :  *
      12                 :  * Permission is hereby granted, free of charge, to any person obtaining a
      13                 :  * copy of this software and associated documentation files (the "Software"),
      14                 :  * to deal in the Software without restriction, including without limitation
      15                 :  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
      16                 :  * and/or sell copies of the Software, and to permit persons to whom the
      17                 :  * Software is furnished to do so, subject to the following conditions:
      18                 :  *
      19                 :  * The above copyright notice and this permission notice shall be included
      20                 :  * in all copies or substantial portions of the Software.
      21                 :  *
      22                 :  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
      23                 :  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
      24                 :  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
      25                 :  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
      26                 :  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
      27                 :  * DEALINGS IN THE SOFTWARE.
      28                 :  ****************************************************************************/
      29                 : 
      30                 : #include "ogr_spatialref.h"
      31                 : #include "cpl_string.h"
      32                 : 
      33                 : #include "rmfdataset.h"
      34                 : 
      35                 : CPL_CVSID("$Id: rmfdataset.cpp 24731 2012-08-03 17:02:56Z rouault $");
      36                 : 
      37                 : CPL_C_START
      38                 : void    GDALRegister_RMF(void);
      39                 : CPL_C_END
      40                 : 
      41                 : #define RMF_DEFAULT_BLOCKXSIZE 256
      42                 : #define RMF_DEFAULT_BLOCKYSIZE 256
      43                 : 
      44                 : static const char RMF_SigRSW[] = { 'R', 'S', 'W', '\0' };
      45                 : static const char RMF_SigRSW_BE[] = { '\0', 'W', 'S', 'R' };
      46                 : static const char RMF_SigMTW[] = { 'M', 'T', 'W', '\0' };
      47                 : 
      48                 : static const char RMF_UnitsEmpty[] = "";
      49                 : static const char RMF_UnitsM[] = "m";
      50                 : static const char RMF_UnitsCM[] = "cm";
      51                 : static const char RMF_UnitsDM[] = "dm";
      52                 : static const char RMF_UnitsMM[] = "mm";
      53                 : 
      54                 : /************************************************************************/
      55                 : /* ==================================================================== */
      56                 : /*                            RMFRasterBand                             */
      57                 : /* ==================================================================== */
      58                 : /************************************************************************/
      59                 : 
      60                 : /************************************************************************/
      61                 : /*                           RMFRasterBand()                            */
      62                 : /************************************************************************/
      63                 : 
      64              41 : RMFRasterBand::RMFRasterBand( RMFDataset *poDS, int nBand,
      65              41 :                               GDALDataType eType )
      66                 : {
      67              41 :     this->poDS = poDS;
      68              41 :     this->nBand = nBand;
      69                 : 
      70              41 :     eDataType = eType;
      71              41 :     nBytesPerPixel = poDS->sHeader.nBitDepth / 8;
      72              41 :     nDataSize = GDALGetDataTypeSize( eDataType ) / 8;
      73              41 :     nBlockXSize = poDS->sHeader.nTileWidth;
      74              41 :     nBlockYSize = poDS->sHeader.nTileHeight;
      75              41 :     nBlockSize = nBlockXSize * nBlockYSize;
      76              41 :     nBlockBytes = nBlockSize * nDataSize;
      77                 :     nLastTileXBytes =
      78              41 :         (poDS->GetRasterXSize() % poDS->sHeader.nTileWidth) * nDataSize;
      79              41 :     nLastTileHeight = poDS->GetRasterYSize() % poDS->sHeader.nTileHeight;
      80                 : 
      81                 : #ifdef DEBUG
      82                 :     CPLDebug( "RMF",
      83                 :               "Band %d: tile width is %d, tile height is %d, "
      84                 :               " last tile width %d, last tile height %d, "
      85                 :               "bytes per pixel is %d, data type size is %d",
      86                 :               nBand, nBlockXSize, nBlockYSize,
      87                 :               poDS->sHeader.nLastTileWidth, poDS->sHeader.nLastTileHeight,
      88              41 :               nBytesPerPixel, nDataSize );
      89                 : #endif
      90              41 : }
      91                 : 
      92                 : /************************************************************************/
      93                 : /*                           ~RMFRasterBand()                           */
      94                 : /************************************************************************/
      95                 : 
      96              41 : RMFRasterBand::~RMFRasterBand()
      97                 : {
      98              41 : }
      99                 : 
     100                 : /************************************************************************/
     101                 : /*                              ReadBuffer()                            */
     102                 : /*                                                                      */
     103                 : /* Helper fucntion to read specified amount of bytes from the input     */
     104                 : /* file stream.                                                         */
     105                 : /************************************************************************/
     106                 : 
     107              13 : CPLErr RMFRasterBand::ReadBuffer( GByte *pabyBuf, GUInt32 nBytes ) const
     108                 : {
     109              13 :     RMFDataset  *poGDS = (RMFDataset *) poDS;
     110                 : 
     111              13 :     CPLAssert( pabyBuf != NULL && poGDS->fp != 0 );
     112                 : 
     113              13 :     vsi_l_offset nOffset = VSIFTellL( poGDS->fp );
     114                 : 
     115              13 :     if ( VSIFReadL( pabyBuf, 1, nBytes, poGDS->fp ) < nBytes )
     116                 :     {
     117                 :         // XXX
     118               0 :         if( poGDS->eAccess == GA_Update )
     119                 :         {
     120               0 :             return CE_Failure;
     121                 :         }
     122                 :         else
     123                 :         {
     124                 :             CPLError( CE_Failure, CPLE_FileIO,
     125                 :                       "Can't read at offset %ld from input file.\n%s\n",
     126               0 :                       (long)nOffset, VSIStrerror( errno ) );
     127               0 :             return CE_Failure;
     128                 :         }
     129                 :     }
     130                 : 
     131                 : #ifdef CPL_MSB
     132                 :     if ( poGDS->eRMFType == RMFT_MTW )
     133                 :     {
     134                 :         GUInt32     i;
     135                 : 
     136                 :         if ( poGDS->sHeader.nBitDepth == 16 )
     137                 :         {
     138                 :             for ( i = 0; i < nBytes; i += 2 )
     139                 :                 CPL_SWAP16PTR( pabyBuf + i );
     140                 :         }
     141                 : 
     142                 :         else if ( poGDS->sHeader.nBitDepth == 32 )
     143                 :         {
     144                 :             for ( i = 0; i < nBytes; i += 4 )
     145                 :                 CPL_SWAP32PTR( pabyBuf + i );
     146                 :         }
     147                 : 
     148                 :         else if ( poGDS->sHeader.nBitDepth == 64 )
     149                 :         {
     150                 :             for ( i = 0; i < nBytes; i += 8 )
     151                 :                 CPL_SWAPDOUBLE( pabyBuf + i );
     152                 :         }
     153                 :     }
     154                 : #endif
     155                 : 
     156              13 :     return CE_None;
     157                 : }
     158                 : 
     159                 : /************************************************************************/
     160                 : /*                             IReadBlock()                             */
     161                 : /************************************************************************/
     162                 : 
     163              13 : CPLErr RMFRasterBand::IReadBlock( int nBlockXOff, int nBlockYOff,
     164                 :                                   void * pImage )
     165                 : {
     166              13 :     RMFDataset  *poGDS = (RMFDataset *) poDS;
     167              13 :     GUInt32     nTile = nBlockYOff * poGDS->nXTiles + nBlockXOff;
     168                 :     GUInt32     nTileBytes;
     169                 :     GUInt32     nCurBlockYSize;
     170                 : 
     171                 :     CPLAssert( poGDS != NULL
     172                 :                && nBlockXOff >= 0
     173                 :                && nBlockYOff >= 0
     174              13 :                && pImage != NULL );
     175                 : 
     176              13 :     memset( pImage, 0, nBlockBytes );
     177                 : 
     178              13 :     if (2 * nTile + 1 >= poGDS->sHeader.nTileTblSize / sizeof(GUInt32))
     179                 :     {
     180               0 :         return CE_Failure;
     181                 :     }
     182                 : 
     183              13 :     nTileBytes = poGDS->paiTiles[2 * nTile + 1];
     184                 : 
     185              26 :     if ( poGDS->sHeader.nLastTileHeight
     186                 :          && (GUInt32) nBlockYOff == poGDS->nYTiles - 1 )
     187              13 :         nCurBlockYSize = poGDS->sHeader.nLastTileHeight;
     188                 :     else
     189               0 :         nCurBlockYSize = nBlockYSize;
     190                 : 
     191              13 :     if ( VSIFSeekL( poGDS->fp, poGDS->paiTiles[2 * nTile], SEEK_SET ) < 0 )
     192                 :     {
     193                 :         // XXX: We will not report error here, because file just may be
     194                 :   // in update state and data for this block will be available later
     195               0 :         if( poGDS->eAccess == GA_Update )
     196               0 :             return CE_None;
     197                 :         else
     198                 :         {
     199                 :             CPLError( CE_Failure, CPLE_FileIO,
     200                 :                 "Can't seek to offset %ld in input file to read data.\n%s\n",
     201               0 :                 (long) poGDS->paiTiles[2 * nTile], VSIStrerror( errno ) );
     202               0 :             return CE_Failure;
     203                 :         }
     204                 :     }
     205                 : 
     206              18 :     if ( poGDS->nBands == 1 &&
     207                 :          ( poGDS->sHeader.nBitDepth == 8
     208                 :            || poGDS->sHeader.nBitDepth == 16
     209                 :            || poGDS->sHeader.nBitDepth == 32
     210                 :            || poGDS->sHeader.nBitDepth == 64 ) )
     211                 :     {
     212               5 :         if ( nTileBytes > nBlockBytes )
     213               0 :             nTileBytes = nBlockBytes;
     214                 : 
     215                 : /* -------------------------------------------------------------------- */
     216                 : /*  Decompress buffer, if needed.                                       */
     217                 : /* -------------------------------------------------------------------- */
     218               5 :         if ( poGDS->Decompress )
     219                 :         {
     220                 :             GUInt32 nRawBytes;
     221                 : 
     222               1 :             if ( nLastTileXBytes && (GUInt32)nBlockXOff == poGDS->nXTiles - 1 )
     223               0 :                 nRawBytes = nLastTileXBytes;
     224                 :             else
     225               1 :                 nRawBytes = poGDS->nBands * nBlockXSize * nDataSize;
     226                 : 
     227               1 :             if ( nLastTileHeight && (GUInt32)nBlockYOff == poGDS->nYTiles - 1 )
     228               0 :                 nRawBytes *= nLastTileHeight;
     229                 :             else
     230               1 :                 nRawBytes *= nBlockYSize;
     231                 : 
     232               1 :             if ( nRawBytes > nTileBytes )
     233                 :             {
     234               0 :                 GByte   *pabyTile = (GByte *) VSIMalloc( nTileBytes );
     235                 : 
     236               0 :                 if ( !pabyTile )
     237                 :                 {
     238                 :                     CPLError( CE_Failure, CPLE_FileIO,
     239                 :                             "Can't allocate tile block of size %lu.\n%s\n",
     240               0 :                             (unsigned long) nTileBytes, VSIStrerror( errno ) );
     241               0 :                     return CE_Failure;
     242                 :                 }
     243                 : 
     244               0 :                 if ( ReadBuffer( pabyTile, nTileBytes ) == CE_Failure )
     245                 :                 {
     246                 :                     // XXX: Do not fail here, just return empty block
     247                 :                     // and continue reading.
     248               0 :                     CPLFree( pabyTile );
     249               0 :                     return CE_None;
     250                 :                 }
     251                 : 
     252                 :                 (*poGDS->Decompress)( pabyTile, nTileBytes,
     253               0 :                                       (GByte*)pImage, nRawBytes );
     254               0 :                 CPLFree( pabyTile );
     255               0 :                 nTileBytes = nRawBytes;
     256                 :             }
     257                 :             else
     258                 :             {
     259               1 :                 if ( ReadBuffer( (GByte *)pImage, nTileBytes ) == CE_Failure )
     260                 :                 {
     261                 :                     // XXX: Do not fail here, just return empty block
     262                 :                     // and continue reading.
     263               0 :                     return CE_None;
     264                 :                 }
     265                 :             }
     266                 :         }
     267                 : 
     268                 :         else
     269                 :         {
     270                 : 
     271               4 :             if ( ReadBuffer( (GByte *)pImage, nTileBytes ) == CE_Failure )
     272                 :             {
     273                 :                 // XXX: Do not fail here, just return empty block
     274                 :                 // and continue reading.
     275               0 :                 return CE_None;
     276                 :             }
     277                 : 
     278                 :         }
     279                 : 
     280                 :     }
     281                 : 
     282               8 :     else if ( poGDS->eRMFType == RMFT_RSW )
     283                 :     {
     284               8 :         GByte   *pabyTile = (GByte *) VSIMalloc( nTileBytes );
     285                 : 
     286               8 :         if ( !pabyTile )
     287                 :         {
     288                 :             CPLError( CE_Failure, CPLE_FileIO,
     289                 :                       "Can't allocate tile block of size %lu.\n%s\n",
     290               0 :                       (unsigned long) nTileBytes, VSIStrerror( errno ) );
     291               0 :             return CE_Failure;
     292                 :         }
     293                 : 
     294                 : 
     295               8 :         if ( ReadBuffer( pabyTile, nTileBytes ) == CE_Failure )
     296                 :         {
     297                 :             // XXX: Do not fail here, just return empty block
     298                 :             // and continue reading.
     299               0 :             CPLFree( pabyTile );
     300               0 :             return CE_None;
     301                 :         }
     302                 : 
     303                 : /* -------------------------------------------------------------------- */
     304                 : /*  If buffer was compressed, decompress it first.                      */
     305                 : /* -------------------------------------------------------------------- */
     306               8 :         if ( poGDS->Decompress )
     307                 :         {
     308                 :             GUInt32 nRawBytes;
     309                 : 
     310               1 :             if ( nLastTileXBytes && (GUInt32)nBlockXOff == poGDS->nXTiles - 1 )
     311               0 :                 nRawBytes = nLastTileXBytes;
     312                 :             else
     313               1 :                 nRawBytes = poGDS->nBands * nBlockXSize * nDataSize;
     314                 : 
     315               1 :             if ( nLastTileHeight && (GUInt32)nBlockYOff == poGDS->nYTiles - 1 )
     316               0 :                 nRawBytes *= nLastTileHeight;
     317                 :             else
     318               1 :                 nRawBytes *= nBlockYSize;
     319                 : 
     320               1 :             if ( nRawBytes > nTileBytes )
     321                 :             {
     322               0 :                 GByte *pszRawBuf = (GByte *)VSIMalloc( nRawBytes );
     323               0 :                 if (pszRawBuf == NULL)
     324                 :                 {
     325                 :                     CPLError( CE_Failure, CPLE_FileIO,
     326                 :                       "Can't allocate a buffer for raw data of size %lu.\n%s\n",
     327               0 :                       (unsigned long) nRawBytes, VSIStrerror( errno ) );
     328                 : 
     329               0 :                     VSIFree( pabyTile );
     330               0 :                     return CE_Failure;
     331                 :                 }
     332                 : 
     333                 :                 (*poGDS->Decompress)( pabyTile, nTileBytes,
     334               0 :                                       pszRawBuf, nRawBytes );
     335               0 :                 CPLFree( pabyTile );
     336               0 :                 pabyTile = pszRawBuf;
     337               0 :                 nTileBytes = nRawBytes;
     338                 :             }
     339                 :         }
     340                 : 
     341                 : /* -------------------------------------------------------------------- */
     342                 : /*  Deinterleave pixels from input buffer.                              */
     343                 : /* -------------------------------------------------------------------- */
     344                 :         GUInt32     i;
     345                 : 
     346              16 :         if ( poGDS->sHeader.nBitDepth == 24 || poGDS->sHeader.nBitDepth == 32 )
     347                 :         {
     348               8 :             GUInt32 nTileSize = nTileBytes / nBytesPerPixel;
     349                 : 
     350               8 :             if ( nTileSize > nBlockSize )
     351               0 :                 nTileSize = nBlockSize;
     352                 : 
     353           27680 :             for ( i = 0; i < nTileSize; i++ )
     354                 :             {
     355                 :                 // Colour triplets in RMF file organized in reverse order:
     356                 :                 // blue, green, red. When we have 32-bit RMF the forth byte
     357                 :                 // in quadriplet should be discarded as it has no meaning.
     358                 :                 // That is why we always use 3 byte count in the following
     359                 :                 // pabyTemp index.
     360           27672 :                 ((GByte *) pImage)[i] =
     361           27672 :                     pabyTile[i * nBytesPerPixel + 3 - nBand];
     362                 :             }
     363                 :         }
     364                 : 
     365               0 :         else if ( poGDS->sHeader.nBitDepth == 16 )
     366                 :         {
     367               0 :             GUInt32 nTileSize = nTileBytes / nBytesPerPixel;
     368                 : 
     369               0 :             if ( nTileSize > nBlockSize )
     370               0 :                 nTileSize = nBlockSize;
     371                 : 
     372               0 :             for ( i = 0; i < nTileSize; i++ )
     373                 :             {
     374               0 :                 switch ( nBand )
     375                 :                 {
     376                 :                     case 1:
     377               0 :                         ((GByte *) pImage)[i] =
     378               0 :                             (GByte)((((GUInt16*)pabyTile)[i] & 0x7c00) >> 7);
     379               0 :                         break;
     380                 :                     case 2:
     381               0 :                         ((GByte *) pImage)[i] =
     382               0 :                             (GByte)((((GUInt16*)pabyTile)[i] & 0x03e0) >> 2);
     383               0 :                         break;
     384                 :                     case 3:
     385               0 :                         ((GByte *) pImage)[i] =
     386               0 :                             (GByte)(((GUInt16*)pabyTile)[i] & 0x1F) << 3;
     387                 :                         break;
     388                 :                     default:
     389                 :                         break;
     390                 :                 }
     391                 :             }
     392                 :         }
     393                 : 
     394               0 :         else if ( poGDS->sHeader.nBitDepth == 4 )
     395                 :         {
     396               0 :             GByte *pabyTemp = pabyTile;
     397                 : 
     398               0 :             for ( i = 0; i < nBlockSize; i++ )
     399                 :             {
     400                 :                 // Most significant part of the byte represents leftmost pixel
     401               0 :                 if ( i & 0x01 )
     402               0 :                     ((GByte *) pImage)[i] = *pabyTemp++ & 0x0F;
     403                 :                 else
     404               0 :                     ((GByte *) pImage)[i] = (*pabyTemp & 0xF0) >> 4;
     405                 :             }
     406                 :         }
     407                 : 
     408               0 :         else if ( poGDS->sHeader.nBitDepth == 1 )
     409                 :         {
     410               0 :             GByte *pabyTemp = pabyTile;
     411                 : 
     412               0 :             for ( i = 0; i < nBlockSize; i++ )
     413                 :             {
     414               0 :                 switch ( i & 0x7 )
     415                 :                 {
     416                 :                     case 0:
     417               0 :                         ((GByte *) pImage)[i] = (*pabyTemp & 0x80) >> 7;
     418               0 :                         break;
     419                 :                     case 1:
     420               0 :                         ((GByte *) pImage)[i] = (*pabyTemp & 0x40) >> 6;
     421               0 :                         break;
     422                 :                     case 2:
     423               0 :                         ((GByte *) pImage)[i] = (*pabyTemp & 0x20) >> 5;
     424               0 :                         break;
     425                 :                     case 3:
     426               0 :                         ((GByte *) pImage)[i] = (*pabyTemp & 0x10) >> 4;
     427               0 :                         break;
     428                 :                     case 4:
     429               0 :                         ((GByte *) pImage)[i] = (*pabyTemp & 0x08) >> 3;
     430               0 :                         break;
     431                 :                     case 5:
     432               0 :                         ((GByte *) pImage)[i] = (*pabyTemp & 0x04) >> 2;
     433               0 :                         break;
     434                 :                     case 6:
     435               0 :                         ((GByte *) pImage)[i] = (*pabyTemp & 0x02) >> 1;
     436               0 :                         break;
     437                 :                     case 7:
     438               0 :                         ((GByte *) pImage)[i] = *pabyTemp++ & 0x01;
     439                 :                         break;
     440                 :                     default:
     441                 :                         break;
     442                 :                 }
     443                 :             }
     444                 :         }
     445                 : 
     446               8 :         CPLFree( pabyTile );
     447                 :     }
     448                 : 
     449              13 :     if ( nLastTileXBytes
     450                 :          && (GUInt32) nBlockXOff == poGDS->nXTiles - 1 )
     451                 :     {
     452                 :         GUInt32 iRow;
     453                 : 
     454              96 :         for ( iRow = nCurBlockYSize - 1; iRow > 0; iRow-- )
     455                 :         {
     456                 :             memmove( (GByte *)pImage + nBlockXSize * iRow * nDataSize,
     457                 :                      (GByte *)pImage + iRow * nLastTileXBytes,
     458              95 :                      nLastTileXBytes );
     459                 :         }
     460                 : 
     461                 :     }
     462                 : 
     463              13 :     return CE_None;
     464                 : }
     465                 : 
     466                 : /************************************************************************/
     467                 : /*                            IWriteBlock()                             */
     468                 : /************************************************************************/
     469                 : 
     470               7 : CPLErr RMFRasterBand::IWriteBlock( int nBlockXOff, int nBlockYOff,
     471                 :                                    void * pImage )
     472                 : {
     473               7 :     RMFDataset  *poGDS = (RMFDataset *)poDS;
     474               7 :     GUInt32     nTile = nBlockYOff * poGDS->nXTiles + nBlockXOff;
     475               7 :     GUInt32     nTileBytes = nDataSize * poGDS->nBands;
     476                 :     GUInt32     iInPixel, iOutPixel, nCurBlockYSize;
     477                 :     GByte       *pabyTile;
     478                 : 
     479                 :     CPLAssert( poGDS != NULL
     480                 :                && nBlockXOff >= 0
     481                 :                && nBlockYOff >= 0
     482               7 :                && pImage != NULL );
     483                 : 
     484               7 :     if ( poGDS->paiTiles[2 * nTile] )
     485                 :     {
     486               4 :         if ( VSIFSeekL( poGDS->fp, poGDS->paiTiles[2 * nTile], SEEK_SET ) < 0 )
     487                 :         {
     488                 :             CPLError( CE_Failure, CPLE_FileIO,
     489                 :                 "Can't seek to offset %ld in output file to write data.\n%s",
     490               0 :                       (long) poGDS->paiTiles[2 * nTile],
     491               0 :                       VSIStrerror( errno ) );
     492               0 :             return CE_Failure;
     493                 :         }
     494                 :     }
     495                 :     else
     496                 :     {
     497               3 :         if ( VSIFSeekL( poGDS->fp, 0, SEEK_END ) < 0 )
     498                 :         {
     499                 :             CPLError( CE_Failure, CPLE_FileIO,
     500                 :                 "Can't seek to offset %ld in output file to write data.\n%s",
     501               0 :                       (long) poGDS->paiTiles[2 * nTile],
     502               0 :                       VSIStrerror( errno ) );
     503               0 :             return CE_Failure;
     504                 :         }
     505               3 :         poGDS->paiTiles[2 * nTile] = (GUInt32) VSIFTellL( poGDS->fp );
     506                 : 
     507               3 :         poGDS->bHeaderDirty = TRUE;
     508                 :     }
     509                 : 
     510               7 :     if ( nLastTileXBytes
     511                 :          && (GUInt32) nBlockXOff == poGDS->nXTiles - 1 )
     512               0 :         nTileBytes *= poGDS->sHeader.nLastTileWidth;
     513                 :     else
     514               7 :         nTileBytes *= nBlockXSize;
     515                 : 
     516              14 :     if ( poGDS->sHeader.nLastTileHeight
     517                 :          && (GUInt32) nBlockYOff == poGDS->nYTiles - 1 )
     518               7 :         nCurBlockYSize = poGDS->sHeader.nLastTileHeight;
     519                 :     else
     520               0 :         nCurBlockYSize = nBlockYSize;
     521                 : 
     522               7 :     nTileBytes *= nCurBlockYSize;
     523                 : 
     524               7 :     pabyTile = (GByte *) VSICalloc( nTileBytes, 1 );
     525               7 :     if ( !pabyTile )
     526                 :     {
     527                 :         CPLError( CE_Failure, CPLE_FileIO,
     528                 :                   "Can't allocate space for the tile blocak of size %lu.\n%s",
     529               0 :                  (unsigned long) nTileBytes,  VSIStrerror( errno ) );
     530               0 :         return CE_Failure;
     531                 :     }
     532                 : 
     533               7 :     if ( nLastTileXBytes
     534                 :          && (GUInt32) nBlockXOff == poGDS->nXTiles - 1 )
     535                 :     {
     536                 :         GUInt32 iRow;
     537                 : 
     538               0 :         if ( poGDS->nBands == 1 )
     539                 :         {
     540               0 :             for ( iRow = 0; iRow < nCurBlockYSize; iRow++ )
     541                 :             {
     542                 :                 memcpy( pabyTile + iRow * nLastTileXBytes,
     543                 :                          (GByte*)pImage + nBlockXSize * iRow * nDataSize,
     544               0 :                          nLastTileXBytes );
     545                 :             }
     546                 :         }
     547                 :         else
     548                 :         {
     549               0 :             if ( poGDS->paiTiles[2 * nTile + 1] )
     550                 :             {
     551               0 :                 VSIFReadL( pabyTile, 1, nTileBytes, poGDS->fp );
     552               0 :                 VSIFSeekL( poGDS->fp, poGDS->paiTiles[2 * nTile], SEEK_SET );
     553                 :             }
     554                 : 
     555               0 :             for ( iRow = 0; iRow < nCurBlockYSize; iRow++ )
     556                 :             {
     557               0 :                 for ( iInPixel = 0, iOutPixel = nBytesPerPixel - nBand;
     558                 :                       iOutPixel < nLastTileXBytes * poGDS->nBands;
     559                 :                       iInPixel++, iOutPixel += poGDS->nBands )
     560               0 :                     (pabyTile + iRow * nLastTileXBytes * poGDS->nBands)[iOutPixel] =
     561               0 :                         ((GByte *) pImage + nBlockXSize * iRow * nDataSize)[iInPixel];
     562                 :             }
     563                 :         }
     564                 :     }
     565                 :     else
     566                 :     {
     567               7 :         if ( poGDS->nBands == 1 )
     568               1 :             memcpy( pabyTile, pImage, nTileBytes );
     569                 :         else
     570                 :         {
     571               6 :             if ( poGDS->paiTiles[2 * nTile + 1] )
     572                 :             {
     573               4 :                 VSIFReadL( pabyTile, 1, nTileBytes, poGDS->fp );
     574               4 :                 VSIFSeekL( poGDS->fp, poGDS->paiTiles[2 * nTile], SEEK_SET );
     575                 :             }
     576                 : 
     577            7806 :             for ( iInPixel = 0, iOutPixel = nBytesPerPixel - nBand;
     578                 :                   iOutPixel < nTileBytes;
     579                 :                   iInPixel++, iOutPixel += poGDS->nBands )
     580            7800 :                 pabyTile[iOutPixel] = ((GByte *) pImage)[iInPixel];
     581                 : 
     582                 :         }
     583                 :     }
     584                 : 
     585                 : #ifdef CPL_MSB
     586                 :     if ( poGDS->eRMFType == RMFT_MTW )
     587                 :     {
     588                 :         GUInt32 i;
     589                 : 
     590                 :         if ( poGDS->sHeader.nBitDepth == 16 )
     591                 :         {
     592                 :             for ( i = 0; i < nTileBytes; i += 2 )
     593                 :                 CPL_SWAP16PTR( pabyTile + i );
     594                 :         }
     595                 : 
     596                 :         else if ( poGDS->sHeader.nBitDepth == 32 )
     597                 :         {
     598                 :             for ( i = 0; i < nTileBytes; i += 4 )
     599                 :                 CPL_SWAP32PTR( pabyTile + i );
     600                 :         }
     601                 : 
     602                 :         else if ( poGDS->sHeader.nBitDepth == 64 )
     603                 :         {
     604                 :             for ( i = 0; i < nTileBytes; i += 8 )
     605                 :                 CPL_SWAPDOUBLE( pabyTile + i );
     606                 :         }
     607                 :     }
     608                 : #endif
     609                 : 
     610               7 :     if ( VSIFWriteL( pabyTile, 1, nTileBytes, poGDS->fp ) < nTileBytes )
     611                 :     {
     612                 :         CPLError( CE_Failure, CPLE_FileIO,
     613                 :                   "Can't write block with X offset %d and Y offset %d.\n%s",
     614               0 :                   nBlockXOff, nBlockYOff, VSIStrerror( errno ) );
     615               0 :         VSIFree( pabyTile );
     616               0 :         return CE_Failure;
     617                 :     }
     618                 : 
     619               7 :     poGDS->paiTiles[2 * nTile + 1] = nTileBytes;
     620               7 :     VSIFree( pabyTile );
     621                 : 
     622               7 :     poGDS->bHeaderDirty = TRUE;
     623                 : 
     624               7 :     return CE_None;
     625                 : }
     626                 : 
     627                 : /************************************************************************/
     628                 : /*                            GetUnitType()                             */
     629                 : /************************************************************************/
     630                 : 
     631               0 : const char *RMFRasterBand::GetUnitType()
     632                 : 
     633                 : {
     634               0 :     RMFDataset   *poGDS = (RMFDataset *) poDS;
     635                 : 
     636               0 :     return (const char *)poGDS->pszUnitType;
     637                 : }
     638                 : 
     639                 : /************************************************************************/
     640                 : /*                            SetUnitType()                             */
     641                 : /************************************************************************/
     642                 : 
     643               0 : CPLErr RMFRasterBand::SetUnitType( const char *pszNewValue )
     644                 : 
     645                 : {
     646               0 :     RMFDataset   *poGDS = (RMFDataset *) poDS;
     647                 : 
     648               0 :     CPLFree(poGDS->pszUnitType);
     649               0 :     poGDS->pszUnitType = CPLStrdup( pszNewValue );
     650                 : 
     651               0 :     return CE_None;
     652                 : }
     653                 : 
     654                 : /************************************************************************/
     655                 : /*                           GetColorTable()                            */
     656                 : /************************************************************************/
     657                 : 
     658               4 : GDALColorTable *RMFRasterBand::GetColorTable()
     659                 : {
     660               4 :     RMFDataset   *poGDS = (RMFDataset *) poDS;
     661                 : 
     662               4 :     return poGDS->poColorTable;
     663                 : }
     664                 : 
     665                 : /************************************************************************/
     666                 : /*                           SetColorTable()                            */
     667                 : /************************************************************************/
     668                 : 
     669               1 : CPLErr RMFRasterBand::SetColorTable( GDALColorTable *poColorTable )
     670                 : {
     671               1 :     RMFDataset  *poGDS = (RMFDataset *) poDS;
     672                 : 
     673               1 :     if ( poColorTable )
     674                 :     {
     675               1 :         if ( poGDS->eRMFType == RMFT_RSW && poGDS->nBands == 1 )
     676                 :         {
     677                 :             GDALColorEntry  oEntry;
     678                 :             GUInt32         i;
     679                 : 
     680               1 :             if ( !poGDS->pabyColorTable )
     681               0 :                 return CE_Failure;
     682                 : 
     683             257 :             for( i = 0; i < poGDS->nColorTableSize; i++ )
     684                 :             {
     685             256 :                 poColorTable->GetColorEntryAsRGB( i, &oEntry );
     686             256 :                 poGDS->pabyColorTable[i * 4] = (GByte) oEntry.c1;     // Red
     687             256 :                 poGDS->pabyColorTable[i * 4 + 1] = (GByte) oEntry.c2; // Green
     688             256 :                 poGDS->pabyColorTable[i * 4 + 2] = (GByte) oEntry.c3; // Blue
     689             256 :                 poGDS->pabyColorTable[i * 4 + 3] = 0;
     690                 :             }
     691                 : 
     692               1 :             poGDS->bHeaderDirty = TRUE;
     693                 :         }
     694                 :     }
     695                 :     else
     696               0 :         return CE_Failure;
     697                 : 
     698               1 :     return CE_None;
     699                 : }
     700                 : 
     701                 : /************************************************************************/
     702                 : /*                       GetColorInterpretation()                       */
     703                 : /************************************************************************/
     704                 : 
     705              16 : GDALColorInterp RMFRasterBand::GetColorInterpretation()
     706                 : {
     707              16 :     RMFDataset      *poGDS = (RMFDataset *) poDS;
     708                 : 
     709              16 :     if( poGDS->nBands == 3 )
     710                 :     {
     711              12 :         if( nBand == 1 )
     712               4 :             return GCI_RedBand;
     713               8 :         else if( nBand == 2 )
     714               4 :             return GCI_GreenBand;
     715               4 :         else if( nBand == 3 )
     716               4 :             return GCI_BlueBand;
     717                 :         else
     718               0 :             return GCI_Undefined;
     719                 :     }
     720                 :     else
     721                 :     {
     722               4 :         if ( poGDS->eRMFType == RMFT_RSW )
     723               4 :             return GCI_PaletteIndex;
     724                 :         else
     725               0 :             return GCI_Undefined;
     726                 :     }
     727                 : }
     728                 : 
     729                 : /************************************************************************/
     730                 : /* ==================================================================== */
     731                 : /*                              RMFDataset                              */
     732                 : /* ==================================================================== */
     733                 : /************************************************************************/
     734                 : 
     735                 : /************************************************************************/
     736                 : /*                           RMFDataset()                               */
     737                 : /************************************************************************/
     738                 : 
     739              30 : RMFDataset::RMFDataset()
     740                 : {
     741              30 :     pszFilename = NULL;
     742              30 :     fp = NULL;
     743              30 :     nBands = 0;
     744              30 :     nXTiles = 0;
     745              30 :     nYTiles = 0;
     746              30 :     paiTiles = NULL;
     747              30 :     pszProjection = CPLStrdup( "" );
     748              30 :     pszUnitType = CPLStrdup( RMF_UnitsEmpty );
     749              30 :     adfGeoTransform[0] = 0.0;
     750              30 :     adfGeoTransform[1] = 1.0;
     751              30 :     adfGeoTransform[2] = 0.0;
     752              30 :     adfGeoTransform[3] = 0.0;
     753              30 :     adfGeoTransform[4] = 0.0;
     754              30 :     adfGeoTransform[5] = 1.0;
     755              30 :     pabyColorTable = NULL;
     756              30 :     poColorTable = NULL;
     757              30 :     eRMFType = RMFT_RSW;
     758              30 :     memset( &sHeader, 0, sizeof(sHeader) );
     759              30 :     memset( &sExtHeader, 0, sizeof(sExtHeader) );
     760                 : 
     761              30 :     Decompress = NULL;
     762                 : 
     763              30 :     bBigEndian = FALSE;
     764              30 :     bHeaderDirty = FALSE;
     765              30 : }
     766                 : 
     767                 : /************************************************************************/
     768                 : /*                            ~RMFDataset()                             */
     769                 : /************************************************************************/
     770                 : 
     771              30 : RMFDataset::~RMFDataset()
     772                 : {
     773              30 :     FlushCache();
     774                 : 
     775              30 :     if ( paiTiles )
     776              21 :         CPLFree( paiTiles );
     777              30 :     if ( pszProjection )
     778              30 :         CPLFree( pszProjection );
     779              30 :     if ( pszUnitType )
     780              30 :         CPLFree( pszUnitType );
     781              30 :     if ( pabyColorTable )
     782              10 :         CPLFree( pabyColorTable );
     783              30 :     if ( poColorTable != NULL )
     784               7 :         delete poColorTable;
     785              30 :     if( fp != NULL )
     786              27 :         VSIFCloseL( fp );
     787              30 : }
     788                 : 
     789                 : /************************************************************************/
     790                 : /*                          GetGeoTransform()                           */
     791                 : /************************************************************************/
     792                 : 
     793              13 : CPLErr RMFDataset::GetGeoTransform( double * padfTransform )
     794                 : {
     795              13 :     memcpy( padfTransform, adfGeoTransform, sizeof(adfGeoTransform[0]) * 6 );
     796                 : 
     797              13 :     if( sHeader.iGeorefFlag )
     798              13 :         return CE_None;
     799                 :     else
     800               0 :         return CE_Failure;
     801                 : }
     802                 : 
     803                 : /************************************************************************/
     804                 : /*                          SetGeoTransform()                           */
     805                 : /************************************************************************/
     806                 : 
     807               6 : CPLErr RMFDataset::SetGeoTransform( double * padfTransform )
     808                 : {
     809               6 :     memcpy( adfGeoTransform, padfTransform, sizeof(double) * 6 );
     810               6 :     sHeader.dfPixelSize = adfGeoTransform[1];
     811               6 :     if ( sHeader.dfPixelSize != 0.0 )
     812               6 :         sHeader.dfResolution = sHeader.dfScale / sHeader.dfPixelSize;
     813               6 :     sHeader.dfLLX = adfGeoTransform[0];
     814               6 :     sHeader.dfLLY = adfGeoTransform[3] - nRasterYSize * sHeader.dfPixelSize;
     815               6 :     sHeader.iGeorefFlag = 1;
     816                 : 
     817               6 :     bHeaderDirty = TRUE;
     818                 : 
     819               6 :     return CE_None;
     820                 : }
     821                 : 
     822                 : /************************************************************************/
     823                 : /*                          GetProjectionRef()                          */
     824                 : /************************************************************************/
     825                 : 
     826              10 : const char *RMFDataset::GetProjectionRef()
     827                 : {
     828              10 :     if( pszProjection )
     829              10 :         return pszProjection;
     830                 :     else
     831               0 :         return "";
     832                 : }
     833                 : 
     834                 : /************************************************************************/
     835                 : /*                           SetProjection()                            */
     836                 : /************************************************************************/
     837                 : 
     838               6 : CPLErr RMFDataset::SetProjection( const char * pszNewProjection )
     839                 : 
     840                 : {
     841               6 :     if ( pszProjection )
     842               6 :         CPLFree( pszProjection );
     843               6 :     pszProjection = CPLStrdup( (pszNewProjection) ? pszNewProjection : "" );
     844                 : 
     845               6 :     bHeaderDirty = TRUE;
     846                 : 
     847               6 :     return CE_None;
     848                 : }
     849                 : 
     850                 : /************************************************************************/
     851                 : /*                           WriteHeader()                              */
     852                 : /************************************************************************/
     853                 : 
     854              12 : CPLErr RMFDataset::WriteHeader()
     855                 : {
     856                 : /* -------------------------------------------------------------------- */
     857                 : /*  Setup projection.                                                   */
     858                 : /* -------------------------------------------------------------------- */
     859              12 :     if( pszProjection && !EQUAL( pszProjection, "" ) )
     860                 :     {
     861               6 :         OGRSpatialReference oSRS;
     862                 :         long            iProjection, iDatum, iEllips, iZone;
     863               6 :         char            *pszProj =  pszProjection;
     864                 : 
     865               6 :         if ( oSRS.importFromWkt( &pszProj ) == OGRERR_NONE )
     866                 :         {
     867                 :             double  adfPrjParams[7];
     868                 : 
     869                 :             oSRS.exportToPanorama( &iProjection, &iDatum, &iEllips, &iZone,
     870               6 :                                    adfPrjParams );
     871               6 :             sHeader.iProjection = iProjection;
     872               6 :             sHeader.dfStdP1 = adfPrjParams[0];
     873               6 :             sHeader.dfStdP2 = adfPrjParams[1];
     874               6 :             sHeader.dfCenterLat = adfPrjParams[2];
     875               6 :             sHeader.dfCenterLong = adfPrjParams[3];
     876                 : 
     877               6 :             sExtHeader.nEllipsoid = iEllips;
     878               6 :             sExtHeader.nDatum = iDatum;
     879               6 :             sExtHeader.nZone = iZone;
     880               6 :         }
     881                 :     }
     882                 : 
     883                 : #define RMF_WRITE_LONG( ptr, value, offset )            \
     884                 : do {                                                    \
     885                 :     GInt32  iLong = CPL_LSBWORD32( value );             \
     886                 :     memcpy( (ptr) + (offset), &iLong, 4 );              \
     887                 : } while(0);
     888                 : 
     889                 : #define RMF_WRITE_ULONG( ptr,value, offset )            \
     890                 : do {                                                    \
     891                 :     GUInt32 iULong = CPL_LSBWORD32( value );            \
     892                 :     memcpy( (ptr) + (offset), &iULong, 4 );             \
     893                 : } while(0);
     894                 : 
     895                 : #define RMF_WRITE_DOUBLE( ptr,value, offset )           \
     896                 : do {                                                    \
     897                 :     double  dfDouble = (value);                         \
     898                 :     CPL_LSBPTR64( &dfDouble );                          \
     899                 :     memcpy( (ptr) + (offset), &dfDouble, 8 );           \
     900                 : } while(0);
     901                 : 
     902                 : /* -------------------------------------------------------------------- */
     903                 : /*  Write out the main header.                                          */
     904                 : /* -------------------------------------------------------------------- */
     905                 :     {
     906                 :         GByte   abyHeader[RMF_HEADER_SIZE];
     907                 : 
     908              12 :         memset( abyHeader, 0, sizeof(abyHeader) );
     909                 : 
     910              12 :         memcpy( abyHeader, sHeader.bySignature, RMF_SIGNATURE_SIZE );
     911              12 :         RMF_WRITE_ULONG( abyHeader, sHeader.iVersion, 4 );
     912                 :         //
     913              12 :         RMF_WRITE_ULONG( abyHeader, sHeader.nOvrOffset, 12 );
     914              12 :         RMF_WRITE_ULONG( abyHeader, sHeader.iUserID, 16 );
     915              12 :         memcpy( abyHeader + 20, sHeader.byName, RMF_NAME_SIZE );
     916              12 :         RMF_WRITE_ULONG( abyHeader, sHeader.nBitDepth, 52 );
     917              12 :         RMF_WRITE_ULONG( abyHeader, sHeader.nHeight, 56 );
     918              12 :         RMF_WRITE_ULONG( abyHeader, sHeader.nWidth, 60 );
     919              12 :         RMF_WRITE_ULONG( abyHeader, sHeader.nXTiles, 64 );
     920              12 :         RMF_WRITE_ULONG( abyHeader, sHeader.nYTiles, 68 );
     921              12 :         RMF_WRITE_ULONG( abyHeader, sHeader.nTileHeight, 72 );
     922              12 :         RMF_WRITE_ULONG( abyHeader, sHeader.nTileWidth, 76 );
     923              12 :         RMF_WRITE_ULONG( abyHeader, sHeader.nLastTileHeight, 80 );
     924              12 :         RMF_WRITE_ULONG( abyHeader, sHeader.nLastTileWidth, 84 );
     925              12 :         RMF_WRITE_ULONG( abyHeader, sHeader.nROIOffset, 88 );
     926              12 :         RMF_WRITE_ULONG( abyHeader, sHeader.nROISize, 92 );
     927              12 :         RMF_WRITE_ULONG( abyHeader, sHeader.nClrTblOffset, 96 );
     928              12 :         RMF_WRITE_ULONG( abyHeader, sHeader.nClrTblSize, 100 );
     929              12 :         RMF_WRITE_ULONG( abyHeader, sHeader.nTileTblOffset, 104 );
     930              12 :         RMF_WRITE_ULONG( abyHeader, sHeader.nTileTblSize, 108 );
     931              12 :         RMF_WRITE_LONG( abyHeader, sHeader.iMapType, 124 );
     932              12 :         RMF_WRITE_LONG( abyHeader, sHeader.iProjection, 128 );
     933              12 :         RMF_WRITE_DOUBLE( abyHeader, sHeader.dfScale, 136 );
     934              12 :         RMF_WRITE_DOUBLE( abyHeader, sHeader.dfResolution, 144 );
     935              12 :         RMF_WRITE_DOUBLE( abyHeader, sHeader.dfPixelSize, 152 );
     936              12 :         RMF_WRITE_DOUBLE( abyHeader, sHeader.dfLLY, 160 );
     937              12 :         RMF_WRITE_DOUBLE( abyHeader, sHeader.dfLLX, 168 );
     938              12 :         RMF_WRITE_DOUBLE( abyHeader, sHeader.dfStdP1, 176 );
     939              12 :         RMF_WRITE_DOUBLE( abyHeader, sHeader.dfStdP2, 184 );
     940              12 :         RMF_WRITE_DOUBLE( abyHeader, sHeader.dfCenterLong, 192 );
     941              12 :         RMF_WRITE_DOUBLE( abyHeader, sHeader.dfCenterLat, 200 );
     942              12 :         *(abyHeader + 208) = sHeader.iCompression;
     943              12 :         *(abyHeader + 209) = sHeader.iMaskType;
     944              12 :         *(abyHeader + 210) = sHeader.iMaskStep;
     945              12 :         *(abyHeader + 211) = sHeader.iFrameFlag;
     946              12 :         RMF_WRITE_ULONG( abyHeader, sHeader.nFlagsTblOffset, 212 );
     947              12 :         RMF_WRITE_ULONG( abyHeader, sHeader.nFlagsTblSize, 216 );
     948              12 :         RMF_WRITE_ULONG( abyHeader, sHeader.nFileSize0, 220 );
     949              12 :         RMF_WRITE_ULONG( abyHeader, sHeader.nFileSize1, 224 );
     950              12 :         *(abyHeader + 228) = sHeader.iUnknown;
     951              12 :         *(abyHeader + 244) = sHeader.iGeorefFlag;
     952              12 :         *(abyHeader + 245) = sHeader.iInverse;
     953                 :         memcpy( abyHeader + 248, sHeader.abyInvisibleColors,
     954              12 :                 sizeof(sHeader.abyInvisibleColors) );
     955              12 :         RMF_WRITE_DOUBLE( abyHeader, sHeader.adfElevMinMax[0], 280 );
     956              12 :         RMF_WRITE_DOUBLE( abyHeader, sHeader.adfElevMinMax[1], 288 );
     957              12 :         RMF_WRITE_DOUBLE( abyHeader, sHeader.dfNoData, 296 );
     958              12 :         RMF_WRITE_ULONG( abyHeader, sHeader.iElevationUnit, 304 );
     959              12 :         *(abyHeader + 308) = sHeader.iElevationType;
     960              12 :         RMF_WRITE_ULONG( abyHeader, sHeader.nExtHdrOffset, 312 );
     961              12 :         RMF_WRITE_ULONG( abyHeader, sHeader.nExtHdrSize, 316 );
     962                 : 
     963              12 :         VSIFSeekL( fp, 0, SEEK_SET );
     964              12 :         VSIFWriteL( abyHeader, 1, sizeof(abyHeader), fp );
     965                 :     }
     966                 : 
     967                 : /* -------------------------------------------------------------------- */
     968                 : /*  Write out the extended header.                                      */
     969                 : /* -------------------------------------------------------------------- */
     970                 : 
     971              12 :     if ( sHeader.nExtHdrOffset && sHeader.nExtHdrSize )
     972                 :     {
     973              12 :         GByte   *pabyExtHeader = (GByte *)CPLCalloc( sHeader.nExtHdrSize, 1 );
     974                 : 
     975              12 :         RMF_WRITE_LONG( pabyExtHeader, sExtHeader.nEllipsoid, 24 );
     976              12 :         RMF_WRITE_LONG( pabyExtHeader, sExtHeader.nDatum, 32 );
     977              12 :         RMF_WRITE_LONG( pabyExtHeader, sExtHeader.nZone, 36 );
     978                 : 
     979              12 :         VSIFSeekL( fp, sHeader.nExtHdrOffset, SEEK_SET );
     980              12 :         VSIFWriteL( pabyExtHeader, 1, sHeader.nExtHdrSize, fp );
     981                 : 
     982              12 :         CPLFree( pabyExtHeader );
     983                 :     }
     984                 : 
     985                 : #undef RMF_WRITE_DOUBLE
     986                 : #undef RMF_WRITE_ULONG
     987                 : #undef RMF_WRITE_LONG
     988                 : 
     989                 : /* -------------------------------------------------------------------- */
     990                 : /*  Write out the color table.                                          */
     991                 : /* -------------------------------------------------------------------- */
     992                 : 
     993              12 :     if ( sHeader.nClrTblOffset && sHeader.nClrTblSize )
     994                 :     {
     995               6 :         VSIFSeekL( fp, sHeader.nClrTblOffset, SEEK_SET );
     996               6 :         VSIFWriteL( pabyColorTable, 1, sHeader.nClrTblSize, fp );
     997                 :     }
     998                 : 
     999                 : /* -------------------------------------------------------------------- */
    1000                 : /*  Write out the block table, swap if needed.                          */
    1001                 : /* -------------------------------------------------------------------- */
    1002                 : 
    1003              12 :     VSIFSeekL( fp, sHeader.nTileTblOffset, SEEK_SET );
    1004                 : 
    1005                 : #ifdef CPL_MSB
    1006                 :     GUInt32 i;
    1007                 :     GUInt32 *paiTilesSwapped = (GUInt32 *)CPLMalloc( sHeader.nTileTblSize );
    1008                 : 
    1009                 :     if ( !paiTilesSwapped )
    1010                 :         return CE_Failure;
    1011                 : 
    1012                 :     memcpy( paiTilesSwapped, paiTiles, sHeader.nTileTblSize );
    1013                 :     for ( i = 0; i < sHeader.nTileTblSize / sizeof(GUInt32); i++ )
    1014                 :         CPL_SWAP32PTR( paiTilesSwapped + i );
    1015                 :     VSIFWriteL( paiTilesSwapped, 1, sHeader.nTileTblSize, fp );
    1016                 : 
    1017                 :     CPLFree( paiTilesSwapped );
    1018                 : #else
    1019              12 :     VSIFWriteL( paiTiles, 1, sHeader.nTileTblSize, fp );
    1020                 : #endif
    1021                 : 
    1022              12 :     bHeaderDirty = FALSE;
    1023                 : 
    1024              12 :     return CE_None;
    1025                 : }
    1026                 : 
    1027                 : /************************************************************************/
    1028                 : /*                             FlushCache()                             */
    1029                 : /************************************************************************/
    1030                 : 
    1031              30 : void RMFDataset::FlushCache()
    1032                 : 
    1033                 : {
    1034              30 :     GDALDataset::FlushCache();
    1035                 : 
    1036              30 :     if ( !bHeaderDirty )
    1037              24 :         return;
    1038                 : 
    1039               6 :     if ( eRMFType == RMFT_MTW )
    1040                 :     {
    1041               0 :         GDALRasterBand *poBand = GetRasterBand(1);
    1042                 : 
    1043               0 :         if ( poBand )
    1044                 :         {
    1045               0 :             poBand->ComputeRasterMinMax( FALSE, sHeader.adfElevMinMax );
    1046               0 :             bHeaderDirty = TRUE;
    1047                 :         }
    1048                 :     }
    1049               6 :     WriteHeader();
    1050                 : }
    1051                 : 
    1052                 : /************************************************************************/
    1053                 : /*                              Identify()                              */
    1054                 : /************************************************************************/
    1055                 : 
    1056           12616 : int RMFDataset::Identify( GDALOpenInfo *poOpenInfo )
    1057                 : 
    1058                 : {
    1059           12616 :     if( poOpenInfo->pabyHeader == NULL)
    1060           11310 :         return FALSE;
    1061                 : 
    1062            1306 :     if( memcmp(poOpenInfo->pabyHeader, RMF_SigRSW, sizeof(RMF_SigRSW)) != 0
    1063                 :         && memcmp(poOpenInfo->pabyHeader, RMF_SigRSW_BE, sizeof(RMF_SigRSW_BE)) != 0
    1064                 :         && memcmp(poOpenInfo->pabyHeader, RMF_SigMTW, sizeof(RMF_SigMTW)) != 0 )
    1065            1291 :         return FALSE;
    1066                 : 
    1067              15 :     return TRUE;
    1068                 : }
    1069                 : 
    1070                 : /************************************************************************/
    1071                 : /*                                Open()                                */
    1072                 : /************************************************************************/
    1073                 : 
    1074            2523 : GDALDataset *RMFDataset::Open( GDALOpenInfo * poOpenInfo )
    1075                 : {
    1076            2523 :     if ( !Identify(poOpenInfo) )
    1077            2508 :         return NULL;
    1078                 : 
    1079                 : /* -------------------------------------------------------------------- */
    1080                 : /*  Create a corresponding GDALDataset.                                 */
    1081                 : /* -------------------------------------------------------------------- */
    1082                 :     RMFDataset      *poDS;
    1083                 : 
    1084              15 :     poDS = new RMFDataset();
    1085                 : 
    1086              15 :     if( poOpenInfo->eAccess == GA_ReadOnly )
    1087              15 :         poDS->fp = VSIFOpenL( poOpenInfo->pszFilename, "rb" );
    1088                 :     else
    1089               0 :         poDS->fp = VSIFOpenL( poOpenInfo->pszFilename, "r+b" );
    1090              15 :     if ( !poDS->fp )
    1091                 :     {
    1092               0 :         delete poDS;
    1093               0 :         return NULL;
    1094                 :     }
    1095                 : 
    1096                 : #define RMF_READ_ULONG(ptr, value, offset)                              \
    1097                 : do {                                                                    \
    1098                 :     if ( poDS->bBigEndian )                                             \
    1099                 :     {                                                                   \
    1100                 :         (value) = CPL_MSBWORD32(*(GUInt32*)((ptr) + (offset)));         \
    1101                 :     }                                                                   \
    1102                 :     else                                                                \
    1103                 :     {                                                                   \
    1104                 :         (value) = CPL_LSBWORD32(*(GUInt32*)((ptr) + (offset)));         \
    1105                 :     }                                                                   \
    1106                 : } while(0);
    1107                 : 
    1108                 : #define RMF_READ_LONG(ptr, value, offset)                               \
    1109                 : do {                                                                    \
    1110                 :     if ( poDS->bBigEndian )                                             \
    1111                 :     {                                                                   \
    1112                 :         (value) = CPL_MSBWORD32(*(GInt32*)((ptr) + (offset)));          \
    1113                 :     }                                                                   \
    1114                 :     else                                                                \
    1115                 :     {                                                                   \
    1116                 :         (value) = CPL_LSBWORD32(*(GInt32*)((ptr) + (offset)));          \
    1117                 :     }                                                                   \
    1118                 : } while(0);
    1119                 : 
    1120                 : #define RMF_READ_DOUBLE(ptr, value, offset)                             \
    1121                 : do {                                                                    \
    1122                 :     (value) = *(double*)((ptr) + (offset));                             \
    1123                 :     if ( poDS->bBigEndian )                                             \
    1124                 :     {                                                                   \
    1125                 :         CPL_MSBPTR64(&(value));                                         \
    1126                 :     }                                                                   \
    1127                 :     else                                                                \
    1128                 :     {                                                                   \
    1129                 :         CPL_LSBPTR64(&(value));                                         \
    1130                 :     }                                                                   \
    1131                 : } while(0);
    1132                 : 
    1133                 : /* -------------------------------------------------------------------- */
    1134                 : /*  Read the main header.                                               */
    1135                 : /* -------------------------------------------------------------------- */
    1136                 : 
    1137                 :     {
    1138                 :         GByte   abyHeader[RMF_HEADER_SIZE];
    1139                 : 
    1140              15 :         VSIFSeekL( poDS->fp, 0, SEEK_SET );
    1141              15 :         VSIFReadL( abyHeader, 1, sizeof(abyHeader), poDS->fp );
    1142                 : 
    1143              15 :         if ( memcmp(abyHeader, RMF_SigMTW, sizeof(RMF_SigMTW)) == 0 )
    1144               1 :             poDS->eRMFType = RMFT_MTW;
    1145              14 :         else if ( memcmp(abyHeader, RMF_SigRSW_BE, sizeof(RMF_SigRSW_BE)) == 0 )
    1146                 :         {
    1147               1 :             poDS->eRMFType = RMFT_RSW;
    1148               1 :             poDS->bBigEndian = TRUE;
    1149                 :         }
    1150                 :         else
    1151              13 :             poDS->eRMFType = RMFT_RSW;
    1152                 : 
    1153              15 :         memcpy( poDS->sHeader.bySignature, abyHeader, RMF_SIGNATURE_SIZE );
    1154              15 :         RMF_READ_ULONG( abyHeader, poDS->sHeader.iVersion, 4 );
    1155              15 :         RMF_READ_ULONG( abyHeader, poDS->sHeader.nSize, 8 );
    1156              15 :         RMF_READ_ULONG( abyHeader, poDS->sHeader.nOvrOffset, 12 );
    1157              15 :         RMF_READ_ULONG( abyHeader, poDS->sHeader.iUserID, 16 );
    1158                 :         memcpy( poDS->sHeader.byName, abyHeader + 20,
    1159              15 :                 sizeof(poDS->sHeader.byName) );
    1160              15 :         poDS->sHeader.byName[sizeof(poDS->sHeader.byName) - 1] = '\0';
    1161              15 :         RMF_READ_ULONG( abyHeader, poDS->sHeader.nBitDepth, 52 );
    1162              15 :         RMF_READ_ULONG( abyHeader, poDS->sHeader.nHeight, 56 );
    1163              15 :         RMF_READ_ULONG( abyHeader, poDS->sHeader.nWidth, 60 );
    1164              15 :         RMF_READ_ULONG( abyHeader, poDS->sHeader.nXTiles, 64 );
    1165              15 :         RMF_READ_ULONG( abyHeader, poDS->sHeader.nYTiles, 68 );
    1166              15 :         RMF_READ_ULONG( abyHeader, poDS->sHeader.nTileHeight, 72 );
    1167              15 :         RMF_READ_ULONG( abyHeader, poDS->sHeader.nTileWidth, 76 );
    1168              15 :         RMF_READ_ULONG( abyHeader, poDS->sHeader.nLastTileHeight, 80 );
    1169              15 :         RMF_READ_ULONG( abyHeader, poDS->sHeader.nLastTileWidth, 84 );
    1170              15 :         RMF_READ_ULONG( abyHeader, poDS->sHeader.nROIOffset, 88 );
    1171              15 :         RMF_READ_ULONG( abyHeader, poDS->sHeader.nROISize, 92 );
    1172              15 :         RMF_READ_ULONG( abyHeader, poDS->sHeader.nClrTblOffset, 96 );
    1173              15 :         RMF_READ_ULONG( abyHeader, poDS->sHeader.nClrTblSize, 100 );
    1174              15 :         RMF_READ_ULONG( abyHeader, poDS->sHeader.nTileTblOffset, 104 );
    1175              15 :         RMF_READ_ULONG( abyHeader, poDS->sHeader.nTileTblSize, 108 );
    1176              15 :         RMF_READ_LONG( abyHeader, poDS->sHeader.iMapType, 124 );
    1177              15 :         RMF_READ_LONG( abyHeader, poDS->sHeader.iProjection, 128 );
    1178              15 :         RMF_READ_DOUBLE( abyHeader, poDS->sHeader.dfScale, 136 );
    1179              15 :         RMF_READ_DOUBLE( abyHeader, poDS->sHeader.dfResolution, 144 );
    1180              15 :         RMF_READ_DOUBLE( abyHeader, poDS->sHeader.dfPixelSize, 152 );
    1181              15 :         RMF_READ_DOUBLE( abyHeader, poDS->sHeader.dfLLY, 160 );
    1182              15 :         RMF_READ_DOUBLE( abyHeader, poDS->sHeader.dfLLX, 168 );
    1183              15 :         RMF_READ_DOUBLE( abyHeader, poDS->sHeader.dfStdP1, 176 );
    1184              15 :         RMF_READ_DOUBLE( abyHeader, poDS->sHeader.dfStdP2, 184 );
    1185              15 :         RMF_READ_DOUBLE( abyHeader, poDS->sHeader.dfCenterLong, 192 );
    1186              15 :         RMF_READ_DOUBLE( abyHeader, poDS->sHeader.dfCenterLat, 200 );
    1187              15 :         poDS->sHeader.iCompression = *(abyHeader + 208);
    1188              15 :         poDS->sHeader.iMaskType = *(abyHeader + 209);
    1189              15 :         poDS->sHeader.iMaskStep = *(abyHeader + 210);
    1190              15 :         poDS->sHeader.iFrameFlag = *(abyHeader + 211);
    1191              15 :         RMF_READ_ULONG( abyHeader, poDS->sHeader.nFlagsTblOffset, 212 );
    1192              15 :         RMF_READ_ULONG( abyHeader, poDS->sHeader.nFlagsTblSize, 216 );
    1193              15 :         RMF_READ_ULONG( abyHeader, poDS->sHeader.nFileSize0, 220 );
    1194              15 :         RMF_READ_ULONG( abyHeader, poDS->sHeader.nFileSize1, 224 );
    1195              15 :         poDS->sHeader.iUnknown = *(abyHeader + 228);
    1196              15 :         poDS->sHeader.iGeorefFlag = *(abyHeader + 244);
    1197              15 :         poDS->sHeader.iInverse = *(abyHeader + 245);
    1198                 :         memcpy( poDS->sHeader.abyInvisibleColors,
    1199              15 :                 abyHeader + 248, sizeof(poDS->sHeader.abyInvisibleColors) );
    1200              15 :         RMF_READ_DOUBLE( abyHeader, poDS->sHeader.adfElevMinMax[0], 280 );
    1201              15 :         RMF_READ_DOUBLE( abyHeader, poDS->sHeader.adfElevMinMax[1], 288 );
    1202              15 :         RMF_READ_DOUBLE( abyHeader, poDS->sHeader.dfNoData, 296 );
    1203              15 :         RMF_READ_ULONG( abyHeader, poDS->sHeader.iElevationUnit, 304 );
    1204              15 :         poDS->sHeader.iElevationType = *(abyHeader + 308);
    1205              15 :         RMF_READ_ULONG( abyHeader, poDS->sHeader.nExtHdrOffset, 312 );
    1206              15 :         RMF_READ_ULONG( abyHeader, poDS->sHeader.nExtHdrSize, 316 );
    1207                 :     }
    1208                 : 
    1209                 : /* -------------------------------------------------------------------- */
    1210                 : /*  Read the extended header.                                           */
    1211                 : /* -------------------------------------------------------------------- */
    1212                 : 
    1213              15 :     if ( poDS->sHeader.nExtHdrOffset && poDS->sHeader.nExtHdrSize )
    1214                 :     {
    1215                 :         GByte *pabyExtHeader =
    1216              14 :             (GByte *)VSICalloc( poDS->sHeader.nExtHdrSize, 1 );
    1217              14 :         if (pabyExtHeader == NULL)
    1218                 :         {
    1219               0 :             delete poDS;
    1220               0 :             return NULL;
    1221                 :         }
    1222                 : 
    1223              14 :         VSIFSeekL( poDS->fp, poDS->sHeader.nExtHdrOffset, SEEK_SET );
    1224              14 :         VSIFReadL( pabyExtHeader, 1, poDS->sHeader.nExtHdrSize, poDS->fp );
    1225                 : 
    1226              14 :         RMF_READ_LONG( pabyExtHeader, poDS->sExtHeader.nEllipsoid, 24 );
    1227              14 :         RMF_READ_LONG( pabyExtHeader, poDS->sExtHeader.nDatum, 32 );
    1228              14 :         RMF_READ_LONG( pabyExtHeader, poDS->sExtHeader.nZone, 36 );
    1229                 : 
    1230              14 :         CPLFree( pabyExtHeader );
    1231                 :     }
    1232                 : 
    1233                 : #undef RMF_READ_DOUBLE
    1234                 : #undef RMF_READ_LONG
    1235                 : #undef RMF_READ_ULONG
    1236                 : 
    1237                 : #ifdef DEBUG
    1238                 : 
    1239                 :     CPLDebug( "RMF", "%s image has width %d, height %d, bit depth %d, "
    1240                 :               "compression scheme %d, %s, nodata %f",
    1241                 :               (poDS->eRMFType == RMFT_MTW) ? "MTW" : "RSW",
    1242                 :               poDS->sHeader.nWidth, poDS->sHeader.nHeight,
    1243                 :               poDS->sHeader.nBitDepth, poDS->sHeader.iCompression,
    1244                 :               poDS->bBigEndian ? "big endian" : "little endian",
    1245              15 :               poDS->sHeader.dfNoData );
    1246                 :     CPLDebug( "RMF", "Size %d, offset to overview %#lx, user ID %d, "
    1247                 :               "ROI offset %#lx, ROI size %d",
    1248                 :               poDS->sHeader.nSize, (unsigned long)poDS->sHeader.nOvrOffset,
    1249                 :               poDS->sHeader.iUserID, (unsigned long)poDS->sHeader.nROIOffset,
    1250              15 :               poDS->sHeader.nROISize );
    1251                 :     CPLDebug( "RMF", "Map type %d, projection %d, scale %f, resolution %f, ",
    1252                 :               poDS->sHeader.iMapType, poDS->sHeader.iProjection,
    1253              15 :               poDS->sHeader.dfScale, poDS->sHeader.dfResolution );
    1254                 :     CPLDebug( "RMF", "Georeferencing: pixel size %f, LLX %f, LLY %f",
    1255                 :               poDS->sHeader.dfPixelSize,
    1256              15 :               poDS->sHeader.dfLLX, poDS->sHeader.dfLLY );
    1257              15 :     if ( poDS->sHeader.nROIOffset && poDS->sHeader.nROISize )
    1258                 :     {
    1259                 :         GUInt32     i;
    1260                 :         GInt32     nValue;
    1261                 : 
    1262               1 :         CPLDebug( "RMF", "ROI coordinates:" );
    1263             569 :         for ( i = 0; i < poDS->sHeader.nROISize; i += sizeof(nValue) )
    1264                 :         {
    1265                 :             GUInt32  nValue;
    1266                 : 
    1267             568 :             VSIFSeekL( poDS->fp, poDS->sHeader.nROIOffset + i, SEEK_SET );
    1268             568 :             VSIFReadL( &nValue, 1, sizeof(nValue), poDS->fp );
    1269                 : 
    1270             568 :             CPLDebug( "RMF", "%d", nValue );
    1271                 :         }
    1272                 :     }
    1273                 : #endif
    1274                 : 
    1275                 : /* -------------------------------------------------------------------- */
    1276                 : /*  Read array of blocks offsets/sizes.                                 */
    1277                 : /* -------------------------------------------------------------------- */
    1278                 :     GUInt32 i;
    1279                 : 
    1280              15 :     if ( VSIFSeekL( poDS->fp, poDS->sHeader.nTileTblOffset, SEEK_SET ) < 0)
    1281                 :     {
    1282               0 :         delete poDS;
    1283               0 :         return NULL;
    1284                 :     }
    1285                 : 
    1286              15 :     poDS->paiTiles = (GUInt32 *)VSIMalloc( poDS->sHeader.nTileTblSize );
    1287              15 :     if ( !poDS->paiTiles )
    1288                 :     {
    1289               0 :         delete poDS;
    1290               0 :         return NULL;
    1291                 :     }
    1292                 : 
    1293              15 :     if ( VSIFReadL( poDS->paiTiles, 1, poDS->sHeader.nTileTblSize,
    1294                 :                     poDS->fp ) < poDS->sHeader.nTileTblSize )
    1295                 :     {
    1296               0 :         CPLDebug( "RMF", "Can't read tiles offsets/sizes table." );
    1297               0 :         delete poDS;
    1298               0 :         return NULL;
    1299                 :     }
    1300                 : 
    1301                 : #ifdef CPL_MSB
    1302                 :     if ( !poDS->bBigEndian )
    1303                 :     {
    1304                 :         for ( i = 0; i < poDS->sHeader.nTileTblSize / sizeof(GUInt32); i++ )
    1305                 :             CPL_SWAP32PTR( poDS->paiTiles + i );
    1306                 :     }
    1307                 : #else
    1308              15 :     if ( poDS->bBigEndian )
    1309                 :     {
    1310               5 :         for ( i = 0; i < poDS->sHeader.nTileTblSize / sizeof(GUInt32); i++ )
    1311               4 :             CPL_SWAP32PTR( poDS->paiTiles + i );
    1312                 :     }
    1313                 : #endif
    1314                 : 
    1315                 : #ifdef DEBUG
    1316              15 :     CPLDebug( "RMF", "List of block offsets/sizes:" );
    1317                 : 
    1318              31 :     for ( i = 0; i < poDS->sHeader.nTileTblSize / sizeof(GUInt32); i += 2 )
    1319                 :     {
    1320                 :         CPLDebug( "RMF", "    %d / %d",
    1321              16 :                   poDS->paiTiles[i], poDS->paiTiles[i + 1] );
    1322                 :     }
    1323                 : #endif
    1324                 : 
    1325                 : /* -------------------------------------------------------------------- */
    1326                 : /*  Set up essential image parameters.                                  */
    1327                 : /* -------------------------------------------------------------------- */
    1328              15 :     GDALDataType eType = GDT_Byte;
    1329                 : 
    1330              15 :     poDS->nRasterXSize = poDS->sHeader.nWidth;
    1331              15 :     poDS->nRasterYSize = poDS->sHeader.nHeight;
    1332                 : 
    1333              15 :     if (!GDALCheckDatasetDimensions(poDS->nRasterXSize, poDS->nRasterYSize))
    1334                 :     {
    1335               0 :         delete poDS;
    1336               0 :         return NULL;
    1337                 :     }
    1338                 : 
    1339              15 :     if ( poDS->eRMFType == RMFT_RSW )
    1340                 :     {
    1341              14 :         switch ( poDS->sHeader.nBitDepth )
    1342                 :         {
    1343                 :             case 32:
    1344                 :             case 24:
    1345                 :             case 16:
    1346               7 :                 poDS->nBands = 3;
    1347               7 :                 break;
    1348                 :             case 1:
    1349                 :             case 4:
    1350                 :             case 8:
    1351                 :                 {
    1352                 :                     // Allocate memory for colour table and read it
    1353               7 :                     poDS->nColorTableSize = 1 << poDS->sHeader.nBitDepth;
    1354               7 :                     if ( poDS->nColorTableSize * 4 > poDS->sHeader.nClrTblSize )
    1355                 :                     {
    1356                 :                         CPLDebug( "RMF",
    1357                 :                                   "Wrong color table size. Expected %d, got %d.",
    1358                 :                                   poDS->nColorTableSize * 4,
    1359               0 :                                   poDS->sHeader.nClrTblSize );
    1360               0 :                         delete poDS;
    1361               0 :                         return NULL;
    1362                 :                     }
    1363                 :                     poDS->pabyColorTable =
    1364               7 :                         (GByte *)VSIMalloc( poDS->sHeader.nClrTblSize );
    1365               7 :                     if (poDS->pabyColorTable == NULL)
    1366                 :                     {
    1367               0 :                         CPLDebug( "RMF", "Can't allocate color table." );
    1368               0 :                         delete poDS;
    1369               0 :                         return NULL;
    1370                 :                     }
    1371               7 :                     if ( VSIFSeekL( poDS->fp, poDS->sHeader.nClrTblOffset,
    1372                 :                                     SEEK_SET ) < 0 )
    1373                 :                     {
    1374               0 :                         CPLDebug( "RMF", "Can't seek to color table location." );
    1375               0 :                         delete poDS;
    1376               0 :                         return NULL;
    1377                 :                     }
    1378               7 :                     if ( VSIFReadL( poDS->pabyColorTable, 1,
    1379                 :                                     poDS->sHeader.nClrTblSize, poDS->fp )
    1380                 :                          < poDS->sHeader.nClrTblSize )
    1381                 :                     {
    1382               0 :                         CPLDebug( "RMF", "Can't read color table." );
    1383               0 :                         delete poDS;
    1384               0 :                         return NULL;
    1385                 :                     }
    1386                 : 
    1387                 :                     GDALColorEntry oEntry;
    1388               7 :                     poDS->poColorTable = new GDALColorTable();
    1389            1799 :                     for( i = 0; i < poDS->nColorTableSize; i++ )
    1390                 :                     {
    1391            1792 :                         oEntry.c1 = poDS->pabyColorTable[i * 4];     // Red
    1392            1792 :                         oEntry.c2 = poDS->pabyColorTable[i * 4 + 1]; // Green
    1393            1792 :                         oEntry.c3 = poDS->pabyColorTable[i * 4 + 2]; // Blue
    1394            1792 :                         oEntry.c4 = 255;                             // Alpha
    1395                 : 
    1396            1792 :                         poDS->poColorTable->SetColorEntry( i, &oEntry );
    1397                 :                     }
    1398                 :                 }
    1399               7 :                 poDS->nBands = 1;
    1400                 :                 break;
    1401                 :             default:
    1402                 :                 break;
    1403                 :         }
    1404              14 :         eType = GDT_Byte;
    1405                 :     }
    1406                 :     else
    1407                 :     {
    1408               1 :         poDS->nBands = 1;
    1409               1 :         if ( poDS->sHeader.nBitDepth == 8 )
    1410               0 :             eType = GDT_Byte;
    1411               1 :         else if ( poDS->sHeader.nBitDepth == 16 )
    1412               0 :             eType = GDT_Int16;
    1413               1 :         else if ( poDS->sHeader.nBitDepth == 32 )
    1414               0 :             eType = GDT_Int32;
    1415               1 :         else if ( poDS->sHeader.nBitDepth == 64 )
    1416               1 :             eType = GDT_Float64;
    1417                 :     }
    1418                 : 
    1419              15 :     if (poDS->sHeader.nTileWidth == 0 ||
    1420                 :         poDS->sHeader.nTileHeight == 0)
    1421                 :     {
    1422                 :         CPLDebug ("RMF", "Invalid tile dimension : %d x %d",
    1423               0 :                   poDS->sHeader.nTileWidth, poDS->sHeader.nTileHeight);
    1424               0 :         delete poDS;
    1425               0 :         return NULL;
    1426                 :     }
    1427                 : 
    1428                 :     poDS->nXTiles = ( poDS->nRasterXSize + poDS->sHeader.nTileWidth - 1 ) /
    1429              15 :         poDS->sHeader.nTileWidth;
    1430                 :     poDS->nYTiles = ( poDS->nRasterYSize + poDS->sHeader.nTileHeight - 1 ) /
    1431              15 :         poDS->sHeader.nTileHeight;
    1432                 : 
    1433                 : #ifdef DEBUG
    1434                 :     CPLDebug( "RMF", "Image is %d tiles wide, %d tiles long",
    1435              15 :               poDS->nXTiles, poDS->nYTiles );
    1436                 : #endif
    1437                 : 
    1438                 : /* -------------------------------------------------------------------- */
    1439                 : /*  Choose compression scheme.                                          */
    1440                 : /*  XXX: The DEM compression method seems to be only applicable         */
    1441                 : /*  to Int32 data.                                                      */
    1442                 : /* -------------------------------------------------------------------- */
    1443              15 :     if ( poDS->sHeader.iCompression == RMF_COMPRESSION_LZW )
    1444               2 :         poDS->Decompress = &LZWDecompress;
    1445              13 :     else if ( poDS->sHeader.iCompression == RMF_COMPRESSION_DEM
    1446                 :               && eType == GDT_Int32 )
    1447               0 :         poDS->Decompress = &DEMDecompress;
    1448                 :     else    // No compression
    1449              13 :         poDS->Decompress = NULL;
    1450                 : 
    1451                 : /* -------------------------------------------------------------------- */
    1452                 : /*  Create band information objects.                                    */
    1453                 : /* -------------------------------------------------------------------- */
    1454                 :     int iBand;
    1455                 : 
    1456              88 :     for( iBand = 1; iBand <= poDS->nBands; iBand++ )
    1457              29 :         poDS->SetBand( iBand, new RMFRasterBand( poDS, iBand, eType ) );
    1458                 : 
    1459                 : /* -------------------------------------------------------------------- */
    1460                 : /*  Set up projection.                                                  */
    1461                 : /*                                                                      */
    1462                 : /*  XXX: If projection value is not specified, but image still have     */
    1463                 : /*  georeferencing information, assume Gauss-Kruger projection.         */
    1464                 : /* -------------------------------------------------------------------- */
    1465              15 :     if( poDS->sHeader.iProjection > 0 ||
    1466                 :         (poDS->sHeader.dfPixelSize != 0.0 &&
    1467                 :          poDS->sHeader.dfLLX != 0.0 &&
    1468                 :          poDS->sHeader.dfLLY != 0.0) )
    1469                 :     {
    1470              15 :         OGRSpatialReference oSRS;
    1471                 :         GInt32  nProj =
    1472              15 :             (poDS->sHeader.iProjection) ? poDS->sHeader.iProjection : 1L;
    1473                 :         double  padfPrjParams[8];
    1474                 : 
    1475              15 :         padfPrjParams[0] = poDS->sHeader.dfStdP1;
    1476              15 :         padfPrjParams[1] = poDS->sHeader.dfStdP2;
    1477              15 :         padfPrjParams[2] = poDS->sHeader.dfCenterLat;
    1478              15 :         padfPrjParams[3] = poDS->sHeader.dfCenterLong;
    1479              15 :         padfPrjParams[4] = 1.0;
    1480              15 :         padfPrjParams[5] = 0.0;
    1481              15 :         padfPrjParams[6] = 0.0;
    1482                 : 
    1483                 :         // XXX: Compute zone number for Gauss-Kruger (Transverse Mercator)
    1484                 :         // projection if it is not specified.
    1485              18 :         if ( nProj == 1L && poDS->sHeader.dfCenterLong == 0.0 )
    1486                 :         {
    1487               3 :             if ( poDS->sExtHeader.nZone == 0 )
    1488                 :             {
    1489                 :                 double centerXCoord = poDS->sHeader.dfLLX +
    1490               2 :                     (poDS->nRasterXSize * poDS->sHeader.dfPixelSize / 2.0);
    1491                 :                 padfPrjParams[7] =
    1492               2 :                     floor((centerXCoord - 500000.0 ) / 1000000.0);
    1493                 :             }
    1494                 :             else
    1495               1 :                 padfPrjParams[7] = poDS->sExtHeader.nZone;
    1496                 :         }
    1497                 :         else
    1498              12 :             padfPrjParams[7] = 0.0;
    1499                 : 
    1500                 :         oSRS.importFromPanorama( nProj, poDS->sExtHeader.nDatum,
    1501              15 :                                  poDS->sExtHeader.nEllipsoid, padfPrjParams );
    1502              15 :         if ( poDS->pszProjection )
    1503              15 :             CPLFree( poDS->pszProjection );
    1504              15 :         oSRS.exportToWkt( &poDS->pszProjection );
    1505                 :     }
    1506                 : 
    1507                 : /* -------------------------------------------------------------------- */
    1508                 : /*  Set up georeferencing.                                              */
    1509                 : /* -------------------------------------------------------------------- */
    1510              15 :     if ( (poDS->eRMFType == RMFT_RSW && poDS->sHeader.iGeorefFlag) ||
    1511                 :          (poDS->eRMFType == RMFT_MTW && poDS->sHeader.dfPixelSize != 0.0) )
    1512                 :     {
    1513              15 :         poDS->adfGeoTransform[0] = poDS->sHeader.dfLLX;
    1514                 :         poDS->adfGeoTransform[3] = poDS->sHeader.dfLLY
    1515              15 :             + poDS->nRasterYSize * poDS->sHeader.dfPixelSize;
    1516              15 :         poDS->adfGeoTransform[1] = poDS->sHeader.dfPixelSize;
    1517              15 :         poDS->adfGeoTransform[5] = - poDS->sHeader.dfPixelSize;
    1518              15 :         poDS->adfGeoTransform[2] = 0.0;
    1519              15 :         poDS->adfGeoTransform[4] = 0.0;
    1520                 :     }
    1521                 : 
    1522                 : /* -------------------------------------------------------------------- */
    1523                 : /*  Set units.                                                          */
    1524                 : /* -------------------------------------------------------------------- */
    1525                 : 
    1526              15 :     if ( poDS->eRMFType == RMFT_MTW )
    1527                 :     {
    1528               1 :         CPLFree(poDS->pszUnitType);
    1529               1 :         switch ( poDS->sHeader.iElevationUnit )
    1530                 :         {
    1531                 :             case 0:
    1532               1 :                 poDS->pszUnitType = CPLStrdup( RMF_UnitsM );
    1533               1 :                 break;
    1534                 :             case 1:
    1535               0 :                 poDS->pszUnitType = CPLStrdup( RMF_UnitsCM );
    1536               0 :                 break;
    1537                 :             case 2:
    1538               0 :                 poDS->pszUnitType = CPLStrdup( RMF_UnitsDM );
    1539               0 :                 break;
    1540                 :             case 3:
    1541               0 :                 poDS->pszUnitType = CPLStrdup( RMF_UnitsMM );
    1542               0 :                 break;
    1543                 :             default:
    1544               0 :                 poDS->pszUnitType = CPLStrdup( RMF_UnitsEmpty );
    1545                 :                 break;
    1546                 :         }
    1547                 :     }
    1548                 : 
    1549                 : /* -------------------------------------------------------------------- */
    1550                 : /*  Report some other dataset related information.                      */
    1551                 : /* -------------------------------------------------------------------- */
    1552                 : 
    1553              15 :     if ( poDS->eRMFType == RMFT_MTW )
    1554                 :     {
    1555                 :         char        szTemp[256];
    1556                 : 
    1557               1 :         snprintf( szTemp, sizeof(szTemp), "%g", poDS->sHeader.adfElevMinMax[0] );
    1558               1 :         poDS->SetMetadataItem( "ELEVATION_MINIMUM", szTemp );
    1559                 : 
    1560               1 :         snprintf( szTemp, sizeof(szTemp), "%g", poDS->sHeader.adfElevMinMax[1] );
    1561               1 :         poDS->SetMetadataItem( "ELEVATION_MAXIMUM", szTemp );
    1562                 : 
    1563               1 :         poDS->SetMetadataItem( "ELEVATION_UNITS", poDS->pszUnitType );
    1564                 : 
    1565               1 :         snprintf( szTemp, sizeof(szTemp), "%d", poDS->sHeader.iElevationType );
    1566               1 :         poDS->SetMetadataItem( "ELEVATION_TYPE", szTemp );
    1567                 :     }
    1568                 : 
    1569                 : /* -------------------------------------------------------------------- */
    1570                 : /*      Check for overviews.                                            */
    1571                 : /* -------------------------------------------------------------------- */
    1572              15 :     poDS->oOvManager.Initialize( poDS, poOpenInfo->pszFilename );
    1573                 : 
    1574              15 :     return( poDS );
    1575                 : }
    1576                 : 
    1577                 : /************************************************************************/
    1578                 : /*                               Create()                               */
    1579                 : /************************************************************************/
    1580                 : 
    1581              46 : GDALDataset *RMFDataset::Create( const char * pszFilename,
    1582                 :                                  int nXSize, int nYSize, int nBands,
    1583                 :                                  GDALDataType eType, char **papszParmList )
    1584                 : 
    1585                 : {
    1586              46 :     if ( nBands != 1 && nBands != 3 )
    1587                 :     {
    1588                 :         CPLError( CE_Failure, CPLE_NotSupported,
    1589                 :                   "RMF driver doesn't support %d bands. Must be 1 or 3.\n",
    1590               7 :                   nBands );
    1591                 : 
    1592               7 :         return NULL;
    1593                 :     }
    1594                 : 
    1595              39 :     if ( nBands == 1
    1596                 :          && eType != GDT_Byte
    1597                 :          && eType != GDT_Int16
    1598                 :          && eType != GDT_Int32
    1599                 :          && eType != GDT_Float64 )
    1600                 :     {
    1601                 :          CPLError( CE_Failure, CPLE_AppDefined,
    1602                 :               "Attempt to create RMF dataset with an illegal data type (%s),\n"
    1603                 :               "only Byte, Int16, Int32 and Float64 types supported "
    1604                 :               "by the format for single-band images.\n",
    1605              14 :               GDALGetDataTypeName(eType) );
    1606                 : 
    1607              14 :         return NULL;
    1608                 :     }
    1609                 : 
    1610              25 :     if ( nBands == 3 && eType != GDT_Byte )
    1611                 :     {
    1612                 :          CPLError( CE_Failure, CPLE_AppDefined,
    1613                 :               "Attempt to create RMF dataset with an illegal data type (%s),\n"
    1614                 :               "only Byte type supported by the format for three-band images.\n",
    1615              10 :               GDALGetDataTypeName(eType) );
    1616                 : 
    1617              10 :         return NULL;
    1618                 :     }
    1619                 : 
    1620                 : /* -------------------------------------------------------------------- */
    1621                 : /*  Create the dataset.                                                 */
    1622                 : /* -------------------------------------------------------------------- */
    1623                 :     RMFDataset      *poDS;
    1624                 : 
    1625              15 :     poDS = new RMFDataset();
    1626                 : 
    1627              15 :     poDS->fp = VSIFOpenL( pszFilename, "w+b" );
    1628              15 :     if( poDS->fp == NULL )
    1629                 :     {
    1630                 :         CPLError( CE_Failure, CPLE_OpenFailed, "Unable to create file %s.\n",
    1631               3 :                   pszFilename );
    1632               3 :         delete poDS;
    1633               3 :         return NULL;
    1634                 :     }
    1635                 : 
    1636              12 :     poDS->pszFilename = pszFilename;
    1637                 : 
    1638                 : /* -------------------------------------------------------------------- */
    1639                 : /*  Fill the RMFHeader                                                  */
    1640                 : /* -------------------------------------------------------------------- */
    1641              12 :     GUInt32     nTileSize, nCurPtr = 0;
    1642                 :     GUInt32     nBlockXSize =
    1643              12 :         ( nXSize < RMF_DEFAULT_BLOCKXSIZE ) ? nXSize : RMF_DEFAULT_BLOCKXSIZE;
    1644                 :     GUInt32     nBlockYSize =
    1645              12 :         ( nYSize < RMF_DEFAULT_BLOCKYSIZE ) ? nYSize : RMF_DEFAULT_BLOCKYSIZE;
    1646                 :     const char  *pszValue;
    1647                 : 
    1648              12 :     if ( CSLFetchBoolean( papszParmList, "MTW", FALSE) )
    1649               0 :         poDS->eRMFType = RMFT_MTW;
    1650                 :     else
    1651              12 :         poDS->eRMFType = RMFT_RSW;
    1652              12 :     if ( poDS->eRMFType == RMFT_MTW )
    1653               0 :         memcpy( poDS->sHeader.bySignature, RMF_SigMTW, RMF_SIGNATURE_SIZE );
    1654                 :     else
    1655              12 :         memcpy( poDS->sHeader.bySignature, RMF_SigRSW, RMF_SIGNATURE_SIZE );
    1656              12 :     poDS->sHeader.iVersion = 0x0200;
    1657              12 :     poDS->sHeader.nOvrOffset = 0x00;
    1658              12 :     poDS->sHeader.iUserID = 0x00;
    1659              12 :     memset( poDS->sHeader.byName, 0, sizeof(poDS->sHeader.byName) );
    1660              12 :     poDS->sHeader.nBitDepth = GDALGetDataTypeSize( eType ) * nBands;
    1661              12 :     poDS->sHeader.nHeight = nYSize;
    1662              12 :     poDS->sHeader.nWidth = nXSize;
    1663                 : 
    1664              12 :     pszValue = CSLFetchNameValue(papszParmList,"BLOCKXSIZE");
    1665              12 :     if( pszValue != NULL )
    1666               0 :         nBlockXSize = atoi( pszValue );
    1667                 : 
    1668              12 :     pszValue = CSLFetchNameValue(papszParmList,"BLOCKYSIZE");
    1669              12 :     if( pszValue != NULL )
    1670               0 :         nBlockYSize = atoi( pszValue );
    1671                 : 
    1672              12 :     poDS->sHeader.nTileWidth = nBlockXSize;
    1673              12 :     poDS->sHeader.nTileHeight = nBlockYSize;
    1674                 : 
    1675                 :     poDS->nXTiles = poDS->sHeader.nXTiles =
    1676              12 :         ( nXSize + poDS->sHeader.nTileWidth - 1 ) / poDS->sHeader.nTileWidth;
    1677                 :     poDS->nYTiles = poDS->sHeader.nYTiles =
    1678              12 :         ( nYSize + poDS->sHeader.nTileHeight - 1 ) / poDS->sHeader.nTileHeight;
    1679              12 :     poDS->sHeader.nLastTileHeight = nYSize % poDS->sHeader.nTileHeight;
    1680              12 :     if ( !poDS->sHeader.nLastTileHeight )
    1681              12 :         poDS->sHeader.nLastTileHeight = poDS->sHeader.nTileHeight;
    1682              12 :     poDS->sHeader.nLastTileWidth = nXSize % poDS->sHeader.nTileWidth;
    1683              12 :     if ( !poDS->sHeader.nLastTileWidth )
    1684              12 :         poDS->sHeader.nLastTileWidth = poDS->sHeader.nTileWidth;
    1685                 : 
    1686              12 :     poDS->sHeader.nROIOffset = 0x00;
    1687              12 :     poDS->sHeader.nROISize = 0x00;
    1688                 : 
    1689              12 :     nCurPtr += RMF_HEADER_SIZE;
    1690                 : 
    1691                 :     // Extended header
    1692              12 :     poDS->sHeader.nExtHdrOffset = nCurPtr;
    1693              12 :     poDS->sHeader.nExtHdrSize = RMF_EXT_HEADER_SIZE;
    1694              12 :     nCurPtr += poDS->sHeader.nExtHdrSize;
    1695                 : 
    1696                 :     // Color table
    1697              15 :     if ( poDS->eRMFType == RMFT_RSW && nBands == 1 )
    1698                 :     {
    1699                 :         GUInt32 i;
    1700                 : 
    1701               9 :         if ( poDS->sHeader.nBitDepth > 8 )
    1702                 :         {
    1703                 :             CPLError( CE_Failure, CPLE_AppDefined,
    1704                 :                       "Cannot create color table of RSW with nBitDepth = %d. Retry with MTW ?",
    1705               6 :                       poDS->sHeader.nBitDepth );
    1706               6 :             delete poDS;
    1707               6 :             return NULL;
    1708                 :         }
    1709                 : 
    1710               3 :         poDS->sHeader.nClrTblOffset = nCurPtr;
    1711               3 :         poDS->nColorTableSize = 1 << poDS->sHeader.nBitDepth;
    1712               3 :         poDS->sHeader.nClrTblSize = poDS->nColorTableSize * 4;
    1713               3 :         poDS->pabyColorTable = (GByte *) VSIMalloc( poDS->sHeader.nClrTblSize );
    1714               3 :         if (poDS->pabyColorTable == NULL)
    1715                 :         {
    1716               0 :             CPLError( CE_Failure, CPLE_OutOfMemory, "Out of memory");
    1717               0 :             delete poDS;
    1718               0 :             return NULL;
    1719                 :         }
    1720             771 :         for ( i = 0; i < poDS->nColorTableSize; i++ )
    1721                 :         {
    1722             768 :             poDS->pabyColorTable[i * 4] =
    1723            1536 :                 poDS->pabyColorTable[i * 4 + 1] =
    1724            1536 :                 poDS->pabyColorTable[i * 4 + 2] = (GByte) i;
    1725             768 :                 poDS->pabyColorTable[i * 4 + 3] = 0;
    1726                 :         }
    1727               3 :         nCurPtr += poDS->sHeader.nClrTblSize;
    1728                 :     }
    1729                 :     else
    1730                 :     {
    1731               3 :         poDS->sHeader.nClrTblOffset = 0x00;
    1732               3 :         poDS->sHeader.nClrTblSize = 0x00;
    1733                 :     }
    1734                 : 
    1735                 :     // Blocks table
    1736               6 :     poDS->sHeader.nTileTblOffset = nCurPtr;
    1737                 :     poDS->sHeader.nTileTblSize =
    1738               6 :         poDS->sHeader.nXTiles * poDS->sHeader.nYTiles * 4 * 2;
    1739               6 :     poDS->paiTiles = (GUInt32 *)CPLCalloc( poDS->sHeader.nTileTblSize, 1 );
    1740               6 :     nCurPtr += poDS->sHeader.nTileTblSize;
    1741                 :     nTileSize = poDS->sHeader.nTileWidth * poDS->sHeader.nTileHeight
    1742               6 :         * GDALGetDataTypeSize( eType ) / 8;
    1743                 :     poDS->sHeader.nSize =
    1744               6 :         poDS->paiTiles[poDS->sHeader.nTileTblSize / 4 - 2] + nTileSize;
    1745                 : 
    1746                 :     // Elevation units
    1747               6 :     if ( EQUAL(poDS->pszUnitType, RMF_UnitsM) )
    1748               0 :         poDS->sHeader.iElevationUnit = 0;
    1749               6 :     else if ( EQUAL(poDS->pszUnitType, RMF_UnitsCM) )
    1750               0 :         poDS->sHeader.iElevationUnit = 1;
    1751               6 :     else if ( EQUAL(poDS->pszUnitType, RMF_UnitsDM) )
    1752               0 :         poDS->sHeader.iElevationUnit = 2;
    1753               6 :     else if ( EQUAL(poDS->pszUnitType, RMF_UnitsMM) )
    1754               0 :         poDS->sHeader.iElevationUnit = 3;
    1755                 :     else
    1756               6 :         poDS->sHeader.iElevationUnit = 0;
    1757                 : 
    1758               6 :     poDS->sHeader.iMapType = -1;
    1759               6 :     poDS->sHeader.iProjection = -1;
    1760               6 :     poDS->sHeader.dfScale = 10000.0;
    1761               6 :     poDS->sHeader.dfResolution = 100.0;
    1762               6 :     poDS->sHeader.iCompression = 0;
    1763               6 :     poDS->sHeader.iMaskType = 0;
    1764               6 :     poDS->sHeader.iMaskStep = 0;
    1765               6 :     poDS->sHeader.iFrameFlag = 0;
    1766               6 :     poDS->sHeader.nFlagsTblOffset = 0x00;
    1767               6 :     poDS->sHeader.nFlagsTblSize = 0x00;
    1768               6 :     poDS->sHeader.nFileSize0 = 0x00;
    1769               6 :     poDS->sHeader.nFileSize1 = 0x00;
    1770               6 :     poDS->sHeader.iUnknown = 0;
    1771               6 :     poDS->sHeader.iGeorefFlag = 0;
    1772               6 :     poDS->sHeader.iInverse = 0;
    1773                 :     memset( poDS->sHeader.abyInvisibleColors, 0,
    1774               6 :             sizeof(poDS->sHeader.abyInvisibleColors) );
    1775               6 :     poDS->sHeader.adfElevMinMax[0] = 0.0;
    1776               6 :     poDS->sHeader.adfElevMinMax[1] = 0.0;
    1777               6 :     poDS->sHeader.dfNoData = 0.0;
    1778               6 :     poDS->sHeader.iElevationType = 0;
    1779                 : 
    1780               6 :     poDS->nRasterXSize = nXSize;
    1781               6 :     poDS->nRasterYSize = nYSize;
    1782               6 :     poDS->eAccess = GA_Update;
    1783               6 :     poDS->nBands = nBands;
    1784                 : 
    1785               6 :     poDS->WriteHeader();
    1786                 : 
    1787                 : /* -------------------------------------------------------------------- */
    1788                 : /*      Create band information objects.                                */
    1789                 : /* -------------------------------------------------------------------- */
    1790                 :     int         iBand;
    1791                 : 
    1792              36 :     for( iBand = 1; iBand <= poDS->nBands; iBand++ )
    1793              12 :         poDS->SetBand( iBand, new RMFRasterBand( poDS, iBand, eType ) );
    1794                 : 
    1795               6 :     return (GDALDataset *) poDS;
    1796                 : }
    1797                 : 
    1798                 : /************************************************************************/
    1799                 : /*                        GDALRegister_RMF()                            */
    1800                 : /************************************************************************/
    1801                 : 
    1802             582 : void GDALRegister_RMF()
    1803                 : 
    1804                 : {
    1805                 :     GDALDriver  *poDriver;
    1806                 : 
    1807             582 :     if( GDALGetDriverByName( "RMF" ) == NULL )
    1808                 :     {
    1809             561 :         poDriver = new GDALDriver();
    1810                 : 
    1811             561 :         poDriver->SetDescription( "RMF" );
    1812                 :         poDriver->SetMetadataItem( GDAL_DMD_LONGNAME,
    1813             561 :                                    "Raster Matrix Format" );
    1814                 :         poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC,
    1815             561 :                                    "frmt_rmf.html" );
    1816             561 :         poDriver->SetMetadataItem( GDAL_DMD_EXTENSION, "rsw" );
    1817                 :         poDriver->SetMetadataItem( GDAL_DMD_CREATIONDATATYPES,
    1818             561 :                                    "Byte Int16 Int32 Float64" );
    1819                 :         poDriver->SetMetadataItem( GDAL_DMD_CREATIONOPTIONLIST,
    1820                 : "<CreationOptionList>"
    1821                 : "   <Option name='MTW' type='boolean' description='Create MTW DEM matrix'/>"
    1822                 : "   <Option name='BLOCKXSIZE' type='int' description='Tile Width'/>"
    1823                 : "   <Option name='BLOCKYSIZE' type='int' description='Tile Height'/>"
    1824             561 : "</CreationOptionList>" );
    1825             561 :         poDriver->SetMetadataItem( GDAL_DCAP_VIRTUALIO, "YES" );
    1826                 : 
    1827             561 :         poDriver->pfnIdentify = RMFDataset::Identify;
    1828             561 :         poDriver->pfnOpen = RMFDataset::Open;
    1829             561 :         poDriver->pfnCreate = RMFDataset::Create;
    1830                 : 
    1831             561 :         GetGDALDriverManager()->RegisterDriver( poDriver );
    1832                 :     }
    1833             582 : }
    1834                 : 

Generated by: LCOV version 1.7