LCOV - code coverage report
Current view: directory - frmts/raw - ntv2dataset.cpp (source / functions) Found Hit Coverage
Test: gdal_filtered.info Lines: 329 292 88.8 %
Date: 2012-12-26 Functions: 15 12 80.0 %

       1                 : /******************************************************************************
       2                 :  * $Id: ntv2dataset.cpp 25283 2012-12-03 21:01:15Z rouault $
       3                 :  *
       4                 :  * Project:  Horizontal Datum Formats
       5                 :  * Purpose:  Implementation of NTv2 datum shift format used in Canada, France, 
       6                 :  *           Australia and elsewhere.
       7                 :  * Author:   Frank Warmerdam, warmerdam@pobox.com
       8                 :  * Financial Support: i-cubed (http://www.i-cubed.com)
       9                 :  *
      10                 :  ******************************************************************************
      11                 :  * Copyright (c) 2010, Frank Warmerdam
      12                 :  *
      13                 :  * Permission is hereby granted, free of charge, to any person obtaining a
      14                 :  * copy of this software and associated documentation files (the "Software"),
      15                 :  * to deal in the Software without restriction, including without limitation
      16                 :  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
      17                 :  * and/or sell copies of the Software, and to permit persons to whom the
      18                 :  * Software is furnished to do so, subject to the following conditions:
      19                 :  *
      20                 :  * The above copyright notice and this permission notice shall be included
      21                 :  * in all copies or substantial portions of the Software.
      22                 :  *
      23                 :  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
      24                 :  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
      25                 :  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
      26                 :  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
      27                 :  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
      28                 :  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
      29                 :  * DEALINGS IN THE SOFTWARE.
      30                 :  ****************************************************************************/
      31                 : 
      32                 : #include "rawdataset.h"
      33                 : #include "cpl_string.h"
      34                 : #include "ogr_srs_api.h"
      35                 : 
      36                 : CPL_CVSID("$Id: ntv2dataset.cpp 25283 2012-12-03 21:01:15Z rouault $");
      37                 : 
      38                 : /** 
      39                 :  * The header for the file, and each grid consists of 11 16byte records.
      40                 :  * The first half is an ASCII label, and the second half is the value
      41                 :  * often in a little endian int or float. 
      42                 :  *
      43                 :  * Example:
      44                 : 
      45                 : 00000000  4e 55 4d 5f 4f 52 45 43  0b 00 00 00 00 00 00 00  |NUM_OREC........|
      46                 : 00000010  4e 55 4d 5f 53 52 45 43  0b 00 00 00 00 00 00 00  |NUM_SREC........|
      47                 : 00000020  4e 55 4d 5f 46 49 4c 45  01 00 00 00 00 00 00 00  |NUM_FILE........|
      48                 : 00000030  47 53 5f 54 59 50 45 20  53 45 43 4f 4e 44 53 20  |GS_TYPE SECONDS |
      49                 : 00000040  56 45 52 53 49 4f 4e 20  49 47 4e 30 37 5f 30 31  |VERSION IGN07_01|
      50                 : 00000050  53 59 53 54 45 4d 5f 46  4e 54 46 20 20 20 20 20  |SYSTEM_FNTF     |
      51                 : 00000060  53 59 53 54 45 4d 5f 54  52 47 46 39 33 20 20 20  |SYSTEM_TRGF93   |
      52                 : 00000070  4d 41 4a 4f 52 5f 46 20  cd cc cc 4c c2 54 58 41  |MAJOR_F ...L.TXA|
      53                 : 00000080  4d 49 4e 4f 52 5f 46 20  00 00 00 c0 88 3f 58 41  |MINOR_F .....?XA|
      54                 : 00000090  4d 41 4a 4f 52 5f 54 20  00 00 00 40 a6 54 58 41  |MAJOR_T ...@.TXA|
      55                 : 000000a0  4d 49 4e 4f 52 5f 54 20  27 e0 1a 14 c4 3f 58 41  |MINOR_T '....?XA|
      56                 : 000000b0  53 55 42 5f 4e 41 4d 45  46 52 41 4e 43 45 20 20  |SUB_NAMEFRANCE  |
      57                 : 000000c0  50 41 52 45 4e 54 20 20  4e 4f 4e 45 20 20 20 20  |PARENT  NONE    |
      58                 : 000000d0  43 52 45 41 54 45 44 20  33 31 2f 31 30 2f 30 37  |CREATED 31/10/07|
      59                 : 000000e0  55 50 44 41 54 45 44 20  20 20 20 20 20 20 20 20  |UPDATED         |
      60                 : 000000f0  53 5f 4c 41 54 20 20 20  00 00 00 00 80 04 02 41  |S_LAT   .......A|
      61                 : 00000100  4e 5f 4c 41 54 20 20 20  00 00 00 00 00 da 06 41  |N_LAT   .......A|
      62                 : 00000110  45 5f 4c 4f 4e 47 20 20  00 00 00 00 00 94 e1 c0  |E_LONG  ........|
      63                 : 00000120  57 5f 4c 4f 4e 47 20 20  00 00 00 00 00 56 d3 40  |W_LONG  .....V.@|
      64                 : 00000130  4c 41 54 5f 49 4e 43 20  00 00 00 00 00 80 76 40  |LAT_INC ......v@|
      65                 : 00000140  4c 4f 4e 47 5f 49 4e 43  00 00 00 00 00 80 76 40  |LONG_INC......v@|
      66                 : 00000150  47 53 5f 43 4f 55 4e 54  a4 43 00 00 00 00 00 00  |GS_COUNT.C......|
      67                 : 00000160  94 f7 c1 3e 70 ee a3 3f  2a c7 84 3d ff 42 af 3d  |...>p..?*..=.B.=|
      68                 : 
      69                 : the actual grid data is a raster with 4 float32 bands (lat offset, long
      70                 : offset, lat error, long error).  The offset values are in arc seconds.
      71                 : The grid is flipped in the x and y axis from our usual GDAL orientation.
      72                 : That is, the first pixel is the south east corner with scanlines going
      73                 : east to west, and rows from south to north.  As a GDAL dataset we represent
      74                 : these both in the more conventional orientation.
      75                 :  */
      76                 : 
      77                 : /************************************************************************/
      78                 : /* ==================================================================== */
      79                 : /*        NTv2Dataset       */
      80                 : /* ==================================================================== */
      81                 : /************************************************************************/
      82                 : 
      83                 : class NTv2Dataset : public RawDataset
      84                 : {
      85                 :   public:
      86                 :     VSILFILE  *fpImage; // image data file.
      87                 : 
      88                 :     int         nRecordLength;
      89                 :     vsi_l_offset nGridOffset;
      90                 :     
      91                 :     double      adfGeoTransform[6];
      92                 : 
      93                 :     void        CaptureMetadataItem( char *pszItem );
      94                 : 
      95                 :     int         OpenGrid( char *pachGridHeader, vsi_l_offset nDataStart );
      96                 : 
      97                 :   public:
      98                 :         NTv2Dataset();
      99                 :               ~NTv2Dataset();
     100                 :     
     101                 :     virtual CPLErr SetGeoTransform( double * padfTransform );
     102                 :     virtual CPLErr GetGeoTransform( double * padfTransform );
     103                 :     virtual const char *GetProjectionRef();
     104                 :     virtual void   FlushCache(void);
     105                 : 
     106                 :     static GDALDataset *Open( GDALOpenInfo * );
     107                 :     static int          Identify( GDALOpenInfo * );
     108                 :     static GDALDataset *Create( const char * pszFilename,
     109                 :                                 int nXSize, int nYSize, int nBands,
     110                 :                                 GDALDataType eType, char ** papszOptions );
     111                 : };
     112                 : 
     113                 : /************************************************************************/
     114                 : /* ==================================================================== */
     115                 : /*        NTv2Dataset       */
     116                 : /* ==================================================================== */
     117                 : /************************************************************************/
     118                 : 
     119                 : /************************************************************************/
     120                 : /*                             NTv2Dataset()                          */
     121                 : /************************************************************************/
     122                 : 
     123              15 : NTv2Dataset::NTv2Dataset()
     124                 : {
     125              15 :     fpImage = NULL;
     126              15 : }
     127                 : 
     128                 : /************************************************************************/
     129                 : /*                            ~NTv2Dataset()                          */
     130                 : /************************************************************************/
     131                 : 
     132              15 : NTv2Dataset::~NTv2Dataset()
     133                 : 
     134                 : {
     135              15 :     FlushCache();
     136                 : 
     137              15 :     if( fpImage != NULL )
     138              15 :         VSIFCloseL( fpImage );
     139              15 : }
     140                 : 
     141                 : /************************************************************************/
     142                 : /*                             FlushCache()                             */
     143                 : /************************************************************************/
     144                 : 
     145              15 : void NTv2Dataset::FlushCache()
     146                 : 
     147                 : {
     148                 : /* -------------------------------------------------------------------- */
     149                 : /*      Nothing to do in readonly mode, or if nothing seems to have     */
     150                 : /*      changed metadata wise.                                          */
     151                 : /* -------------------------------------------------------------------- */
     152              15 :     if( eAccess != GA_Update || !(GetPamFlags() & GPF_DIRTY) )
     153                 :     {
     154              11 :         RawDataset::FlushCache();
     155              11 :         return;
     156                 :     }
     157                 : 
     158                 : /* -------------------------------------------------------------------- */
     159                 : /*      Load grid and file headers.                                     */
     160                 : /* -------------------------------------------------------------------- */
     161                 :     char achFileHeader[11*16];
     162                 :     char achGridHeader[11*16];
     163                 : 
     164               4 :     VSIFSeekL( fpImage, 0, SEEK_SET );
     165               4 :     VSIFReadL( achFileHeader, 11, 16, fpImage );
     166                 : 
     167               4 :     VSIFSeekL( fpImage, nGridOffset, SEEK_SET );
     168               4 :     VSIFReadL( achGridHeader, 11, 16, fpImage );
     169                 : 
     170                 : /* -------------------------------------------------------------------- */
     171                 : /*      Update the grid, and file headers with any available            */
     172                 : /*      metadata.  If all available metadata is recognised then mark    */
     173                 : /*      things "clean" from a PAM point of view.                        */
     174                 : /* -------------------------------------------------------------------- */
     175               4 :     char **papszMD = GetMetadata();
     176                 :     int i;
     177               4 :     int bSomeLeftOver = FALSE;
     178                 : 
     179              41 :     for( i = 0; papszMD != NULL && papszMD[i] != NULL; i++ )
     180                 :     {
     181              37 :         char *pszKey = NULL;
     182              37 :         const char *pszValue = CPLParseNameValue( papszMD[i], &pszKey );
     183              37 :         if( pszKey == NULL )
     184               0 :             continue;
     185                 : 
     186              37 :         if( EQUAL(pszKey,"GS_TYPE") )
     187                 :         {
     188               3 :             memcpy( achFileHeader + 3*16+8, "        ", 8 );
     189               3 :             memcpy( achFileHeader + 3*16+8, pszValue, MIN(8,strlen(pszValue)) );
     190                 :         }
     191              34 :         else if( EQUAL(pszKey,"VERSION") )
     192                 :         {
     193               3 :             memcpy( achFileHeader + 4*16+8, "        ", 8 );
     194               3 :             memcpy( achFileHeader + 4*16+8, pszValue, MIN(8,strlen(pszValue)) );
     195                 :         }
     196              31 :         else if( EQUAL(pszKey,"SYSTEM_F") )
     197                 :         {
     198               3 :             memcpy( achFileHeader + 5*16+8, "        ", 8 );
     199               3 :             memcpy( achFileHeader + 5*16+8, pszValue, MIN(8,strlen(pszValue)) );
     200                 :         }
     201              28 :         else if( EQUAL(pszKey,"SYSTEM_T") )
     202                 :         {
     203               3 :             memcpy( achFileHeader + 6*16+8, "        ", 8 );
     204               3 :             memcpy( achFileHeader + 6*16+8, pszValue, MIN(8,strlen(pszValue)) );
     205                 :         }
     206              25 :         else if( EQUAL(pszKey,"MAJOR_F") )
     207                 :         {
     208               3 :             double dfValue = atof(pszValue);
     209                 :             CPL_LSBPTR64( &dfValue );
     210               3 :             memcpy( achFileHeader + 7*16+8, &dfValue, 8 );
     211                 :         }
     212              22 :         else if( EQUAL(pszKey,"MINOR_F") )
     213                 :         {
     214               3 :             double dfValue = atof(pszValue);
     215                 :             CPL_LSBPTR64( &dfValue );
     216               3 :             memcpy( achFileHeader + 8*16+8, &dfValue, 8 );
     217                 :         }
     218              19 :         else if( EQUAL(pszKey,"MAJOR_T") )
     219                 :         {
     220               3 :             double dfValue = atof(pszValue);
     221                 :             CPL_LSBPTR64( &dfValue );
     222               3 :             memcpy( achFileHeader + 9*16+8, &dfValue, 8 );
     223                 :         }
     224              16 :         else if( EQUAL(pszKey,"MINOR_T") )
     225                 :         {
     226               3 :             double dfValue = atof(pszValue);
     227                 :             CPL_LSBPTR64( &dfValue );
     228               3 :             memcpy( achFileHeader + 10*16+8, &dfValue, 8 );
     229                 :         }
     230              13 :         else if( EQUAL(pszKey,"SUB_NAME") )
     231                 :         {
     232               3 :             memcpy( achGridHeader + 0*16+8, "        ", 8 );
     233               3 :             memcpy( achGridHeader + 0*16+8, pszValue, MIN(8,strlen(pszValue)) );
     234                 :         }
     235              10 :         else if( EQUAL(pszKey,"PARENT") )
     236                 :         {
     237               3 :             memcpy( achGridHeader + 1*16+8, "        ", 8 );
     238               3 :             memcpy( achGridHeader + 1*16+8, pszValue, MIN(8,strlen(pszValue)) );
     239                 :         }
     240               7 :         else if( EQUAL(pszKey,"CREATED") )
     241                 :         {
     242               3 :             memcpy( achGridHeader + 2*16+8, "        ", 8 );
     243               3 :             memcpy( achGridHeader + 2*16+8, pszValue, MIN(8,strlen(pszValue)) );
     244                 :         }
     245               4 :         else if( EQUAL(pszKey,"UPDATED") )
     246                 :         {
     247               3 :             memcpy( achGridHeader + 3*16+8, "        ", 8 );
     248               3 :             memcpy( achGridHeader + 3*16+8, pszValue, MIN(8,strlen(pszValue)) );
     249                 :         }
     250                 :         else
     251                 :         {
     252               1 :             bSomeLeftOver = TRUE;
     253                 :         }
     254                 :         
     255              37 :         CPLFree( pszKey );
     256                 :     }
     257                 : 
     258                 : /* -------------------------------------------------------------------- */
     259                 : /*      Load grid and file headers.                                     */
     260                 : /* -------------------------------------------------------------------- */
     261               4 :     VSIFSeekL( fpImage, 0, SEEK_SET );
     262               4 :     VSIFWriteL( achFileHeader, 11, 16, fpImage );
     263                 : 
     264               4 :     VSIFSeekL( fpImage, nGridOffset, SEEK_SET );
     265               4 :     VSIFWriteL( achGridHeader, 11, 16, fpImage );
     266                 : 
     267                 : /* -------------------------------------------------------------------- */
     268                 : /*      Clear flags if we got everything, then let pam and below do     */
     269                 : /*      their flushing.                                                 */
     270                 : /* -------------------------------------------------------------------- */
     271               4 :     if( !bSomeLeftOver )
     272               3 :         SetPamFlags( GetPamFlags() & (~GPF_DIRTY) );
     273                 : 
     274               4 :     RawDataset::FlushCache();
     275                 : }
     276                 : 
     277                 : /************************************************************************/
     278                 : /*                              Identify()                              */
     279                 : /************************************************************************/
     280                 : 
     281           11944 : int NTv2Dataset::Identify( GDALOpenInfo *poOpenInfo )
     282                 : 
     283                 : {
     284           11944 :     if( EQUALN(poOpenInfo->pszFilename,"NTv2:",5) )
     285               0 :         return TRUE;
     286                 :     
     287           11944 :     if( poOpenInfo->nHeaderBytes < 64 )
     288           11360 :         return FALSE;
     289                 : 
     290             584 :     if( !EQUALN((const char *)poOpenInfo->pabyHeader + 0, "NUM_OREC", 8 ) )
     291             569 :         return FALSE;
     292                 : 
     293              15 :     if( !EQUALN((const char *)poOpenInfo->pabyHeader +16, "NUM_SREC", 8 ) )
     294               0 :         return FALSE;
     295                 : 
     296              15 :     return TRUE;
     297                 : }
     298                 : 
     299                 : /************************************************************************/
     300                 : /*                                Open()                                */
     301                 : /************************************************************************/
     302                 : 
     303            1851 : GDALDataset *NTv2Dataset::Open( GDALOpenInfo * poOpenInfo )
     304                 : 
     305                 : {
     306            1851 :     if( !Identify( poOpenInfo ) )
     307            1836 :         return NULL;
     308                 :         
     309                 : /* -------------------------------------------------------------------- */
     310                 : /*      Are we targetting a particular grid?                            */
     311                 : /* -------------------------------------------------------------------- */
     312              15 :     CPLString osFilename;
     313              15 :     int iTargetGrid = -1;
     314                 : 
     315              15 :     if( EQUALN(poOpenInfo->pszFilename,"NTv2:",5) )
     316                 :     {
     317               0 :         const char *pszRest = poOpenInfo->pszFilename+5;
     318                 :         
     319               0 :         iTargetGrid = atoi(pszRest);
     320               0 :         while( *pszRest != '\0' && *pszRest != ':' )
     321               0 :             pszRest++;
     322                 :      
     323               0 :         if( *pszRest == ':' )
     324               0 :             pszRest++;
     325                 :         
     326               0 :         osFilename = pszRest;
     327                 :     }
     328                 :     else
     329              15 :         osFilename = poOpenInfo->pszFilename;
     330                 :     
     331                 : /* -------------------------------------------------------------------- */
     332                 : /*      Create a corresponding GDALDataset.                             */
     333                 : /* -------------------------------------------------------------------- */
     334                 :     NTv2Dataset   *poDS;
     335                 : 
     336              15 :     poDS = new NTv2Dataset();
     337              15 :     poDS->eAccess = poOpenInfo->eAccess;
     338                 : 
     339                 : /* -------------------------------------------------------------------- */
     340                 : /*      Open the file.                                                  */
     341                 : /* -------------------------------------------------------------------- */
     342              15 :     if( poOpenInfo->eAccess == GA_ReadOnly )
     343              10 :         poDS->fpImage = VSIFOpenL( osFilename, "rb" );
     344                 :     else
     345               5 :         poDS->fpImage = VSIFOpenL( osFilename, "rb+" );
     346                 : 
     347              15 :     if( poDS->fpImage == NULL )
     348                 :     {
     349               0 :         delete poDS;
     350               0 :         return NULL;
     351                 :     }
     352                 : 
     353                 : /* -------------------------------------------------------------------- */
     354                 : /*      Read the file header.                                           */
     355                 : /* -------------------------------------------------------------------- */
     356                 :     char  achHeader[11*16];
     357                 :     GInt32 nSubFileCount;
     358                 :     double dfValue;
     359              15 :     CPLString osFValue;
     360                 : 
     361              15 :     VSIFSeekL( poDS->fpImage, 0, SEEK_SET );
     362              15 :     VSIFReadL( achHeader, 11, 16, poDS->fpImage );
     363                 : 
     364                 :     CPL_LSBPTR32( achHeader + 2*16 + 8 );
     365              15 :     memcpy( &nSubFileCount, achHeader + 2*16 + 8, 4 );
     366              15 :     if (nSubFileCount <= 0 || nSubFileCount >= 1024)
     367                 :     {
     368                 :         CPLError(CE_Failure, CPLE_AppDefined,
     369               0 :                   "Invalid value for NUM_FILE : %d", nSubFileCount);
     370               0 :         delete poDS;
     371               0 :         return NULL;
     372                 :     }
     373                 : 
     374              15 :     poDS->CaptureMetadataItem( achHeader + 3*16 );
     375              15 :     poDS->CaptureMetadataItem( achHeader + 4*16 );
     376              15 :     poDS->CaptureMetadataItem( achHeader + 5*16 );
     377              15 :     poDS->CaptureMetadataItem( achHeader + 6*16 );
     378                 : 
     379              15 :     memcpy( &dfValue, achHeader + 7*16 + 8, 8 );
     380                 :     CPL_LSBPTR64( &dfValue );
     381              15 :     osFValue.Printf( "%.15g", dfValue );
     382              15 :     poDS->SetMetadataItem( "MAJOR_F", osFValue );
     383                 :     
     384              15 :     memcpy( &dfValue, achHeader + 8*16 + 8, 8 );
     385                 :     CPL_LSBPTR64( &dfValue );
     386              15 :     osFValue.Printf( "%.15g", dfValue );
     387              15 :     poDS->SetMetadataItem( "MINOR_F", osFValue );
     388                 :     
     389              15 :     memcpy( &dfValue, achHeader + 9*16 + 8, 8 );
     390                 :     CPL_LSBPTR64( &dfValue );
     391              15 :     osFValue.Printf( "%.15g", dfValue );
     392              15 :     poDS->SetMetadataItem( "MAJOR_T", osFValue );
     393                 :     
     394              15 :     memcpy( &dfValue, achHeader + 10*16 + 8, 8 );
     395                 :     CPL_LSBPTR64( &dfValue );
     396              15 :     osFValue.Printf( "%.15g", dfValue );
     397              15 :     poDS->SetMetadataItem( "MINOR_T", osFValue );
     398                 : 
     399                 : /* ==================================================================== */
     400                 : /*      Loop over grids.                                                */
     401                 : /* ==================================================================== */
     402                 :     int iGrid;
     403              15 :     vsi_l_offset nGridOffset = sizeof(achHeader);
     404                 : 
     405              15 :     for( iGrid = 0; iGrid < nSubFileCount; iGrid++ )
     406                 :     {
     407              15 :         CPLString  osSubName;
     408                 :         int i;
     409                 :         GUInt32 nGSCount;
     410                 : 
     411              15 :         VSIFSeekL( poDS->fpImage, nGridOffset, SEEK_SET );
     412              15 :         if (VSIFReadL( achHeader, 11, 16, poDS->fpImage ) != 16)
     413                 :         {
     414                 :             CPLError(CE_Failure, CPLE_AppDefined,
     415               0 :                      "Cannot read header for subfile %d", iGrid);
     416               0 :             delete poDS;
     417               0 :             return NULL;
     418                 :         }
     419                 : 
     420              15 :         for( i = 4; i <= 9; i++ )
     421                 :             CPL_LSBPTR64( achHeader + i*16 + 8 );
     422                 :         
     423                 :         CPL_LSBPTR32( achHeader + 10*16 + 8 );
     424                 :         
     425              15 :         memcpy( &nGSCount, achHeader + 10*16 + 8, 4 );
     426                 : 
     427              15 :         osSubName.assign( achHeader + 8, 8 );
     428              15 :         osSubName.Trim();
     429                 : 
     430                 :         // If this is our target grid, open it as a dataset.
     431              15 :         if( iTargetGrid == iGrid || (iTargetGrid == -1 && iGrid == 0) )
     432                 :         {
     433              15 :             if( !poDS->OpenGrid( achHeader, nGridOffset ) )
     434                 :             {
     435               0 :                 delete poDS;
     436               0 :                 return NULL;
     437                 :             }
     438                 :         }
     439                 : 
     440                 :         // If we are opening the file as a whole, list subdatasets.
     441              15 :         if( iTargetGrid == -1 )
     442                 :         {
     443              15 :             CPLString osKey, osValue;
     444                 : 
     445              15 :             osKey.Printf( "SUBDATASET_%d_NAME", iGrid );
     446              15 :             osValue.Printf( "NTv2:%d:%s", iGrid, osFilename.c_str() );
     447              15 :             poDS->SetMetadataItem( osKey, osValue, "SUBDATASETS" );
     448                 : 
     449              15 :             osKey.Printf( "SUBDATASET_%d_DESC", iGrid );
     450              15 :             osValue.Printf( "%s", osSubName.c_str() );
     451              15 :             poDS->SetMetadataItem( osKey, osValue, "SUBDATASETS" );
     452                 :         }
     453                 : 
     454              15 :         nGridOffset += (11+(vsi_l_offset)nGSCount) * 16;
     455                 :     }
     456                 : 
     457                 : /* -------------------------------------------------------------------- */
     458                 : /*      Initialize any PAM information.                                 */
     459                 : /* -------------------------------------------------------------------- */
     460              15 :     poDS->SetDescription( poOpenInfo->pszFilename );
     461              15 :     poDS->TryLoadXML();
     462                 : 
     463                 : /* -------------------------------------------------------------------- */
     464                 : /*      Check for overviews.                                            */
     465                 : /* -------------------------------------------------------------------- */
     466              15 :     poDS->oOvManager.Initialize( poDS, poOpenInfo->pszFilename );
     467                 : 
     468              15 :     return( poDS );
     469                 : }
     470                 : 
     471                 : /************************************************************************/
     472                 : /*                              OpenGrid()                              */
     473                 : /*                                                                      */
     474                 : /*      Note that the caller will already have byte swapped needed      */
     475                 : /*      portions of the header.                                         */
     476                 : /************************************************************************/
     477                 : 
     478              15 : int NTv2Dataset::OpenGrid( char *pachHeader, vsi_l_offset nGridOffset )
     479                 : 
     480                 : {
     481              15 :     this->nGridOffset = nGridOffset;
     482                 : 
     483                 : /* -------------------------------------------------------------------- */
     484                 : /*      Read the grid header.                                           */
     485                 : /* -------------------------------------------------------------------- */
     486                 :     double s_lat, n_lat, e_long, w_long, lat_inc, long_inc;
     487                 : 
     488              15 :     CaptureMetadataItem( pachHeader + 0*16 );
     489              15 :     CaptureMetadataItem( pachHeader + 1*16 );
     490              15 :     CaptureMetadataItem( pachHeader + 2*16 );
     491              15 :     CaptureMetadataItem( pachHeader + 3*16 );
     492                 : 
     493              15 :     memcpy( &s_lat,  pachHeader + 4*16 + 8, 8 );
     494              15 :     memcpy( &n_lat,  pachHeader + 5*16 + 8, 8 );
     495              15 :     memcpy( &e_long, pachHeader + 6*16 + 8, 8 );
     496              15 :     memcpy( &w_long, pachHeader + 7*16 + 8, 8 );
     497              15 :     memcpy( &lat_inc, pachHeader + 8*16 + 8, 8 );
     498              15 :     memcpy( &long_inc, pachHeader + 9*16 + 8, 8 );
     499                 : 
     500              15 :     e_long *= -1;
     501              15 :     w_long *= -1;
     502                 : 
     503              15 :     nRasterXSize = (int) floor((e_long - w_long) / long_inc + 1.5);
     504              15 :     nRasterYSize = (int) floor((n_lat - s_lat) / lat_inc + 1.5);
     505                 : 
     506              15 :     if (!GDALCheckDatasetDimensions(nRasterXSize, nRasterYSize))
     507               0 :         return FALSE;
     508                 : 
     509                 : /* -------------------------------------------------------------------- */
     510                 : /*      Create band information object.                                 */
     511                 : /*                                                                      */
     512                 : /*      We use unusual offsets to remap from bottom to top, to top      */
     513                 : /*      to bottom orientation, and also to remap east to west, to       */
     514                 : /*      west to east.                                                   */
     515                 : /* -------------------------------------------------------------------- */
     516                 :     int iBand;
     517                 :     
     518             150 :     for( iBand = 0; iBand < 4; iBand++ )
     519                 :     {
     520                 :         RawRasterBand *poBand = 
     521                 :             new RawRasterBand( this, iBand+1, fpImage, 
     522                 :                                nGridOffset + 4*iBand + 11*16
     523                 :                                + (nRasterXSize-1) * 16
     524                 :                                + (nRasterYSize-1) * 16 * nRasterXSize,
     525                 :                                -16, -16 * nRasterXSize,
     526              60 :                                GDT_Float32, CPL_IS_LSB, TRUE, FALSE );
     527              60 :         SetBand( iBand+1, poBand );
     528                 :     }
     529                 :     
     530              15 :     GetRasterBand(1)->SetDescription( "Latitude Offset (arc seconds)" );
     531              15 :     GetRasterBand(2)->SetDescription( "Longitude Offset (arc seconds)" );
     532              15 :     GetRasterBand(3)->SetDescription( "Latitude Error" );
     533              15 :     GetRasterBand(4)->SetDescription( "Longitude Error" );
     534                 :     
     535                 : /* -------------------------------------------------------------------- */
     536                 : /*      Setup georeferencing.                                           */
     537                 : /* -------------------------------------------------------------------- */
     538              15 :     adfGeoTransform[0] = (w_long - long_inc*0.5) / 3600.0;
     539              15 :     adfGeoTransform[1] = long_inc / 3600.0;
     540              15 :     adfGeoTransform[2] = 0.0;
     541              15 :     adfGeoTransform[3] = (n_lat + lat_inc*0.5) / 3600.0;
     542              15 :     adfGeoTransform[4] = 0.0;
     543              15 :     adfGeoTransform[5] = (-1 * lat_inc) / 3600.0;
     544                 : 
     545              15 :     return TRUE;
     546                 : }
     547                 : 
     548                 : /************************************************************************/
     549                 : /*                        CaptureMetadataItem()                         */
     550                 : /************************************************************************/
     551                 : 
     552             120 : void NTv2Dataset::CaptureMetadataItem( char *pszItem )
     553                 : 
     554                 : {
     555             120 :     CPLString osKey, osValue;
     556                 : 
     557             120 :     osKey.assign( pszItem, 8 );
     558             120 :     osValue.assign( pszItem+8, 8 );
     559                 : 
     560             120 :     SetMetadataItem( osKey.Trim(), osValue.Trim() );
     561             120 : }
     562                 : 
     563                 : /************************************************************************/
     564                 : /*                          GetGeoTransform()                           */
     565                 : /************************************************************************/
     566                 : 
     567               5 : CPLErr NTv2Dataset::GetGeoTransform( double * padfTransform )
     568                 : 
     569                 : {
     570               5 :     memcpy( padfTransform, adfGeoTransform, sizeof(double)*6 );
     571               5 :     return CE_None;
     572                 : }
     573                 : 
     574                 : /************************************************************************/
     575                 : /*                          SetGeoTransform()                           */
     576                 : /************************************************************************/
     577                 : 
     578               4 : CPLErr NTv2Dataset::SetGeoTransform( double * padfTransform )
     579                 : 
     580                 : {
     581               4 :     if( eAccess == GA_ReadOnly )
     582                 :     {
     583                 :         CPLError( CE_Failure, CPLE_NoWriteAccess,
     584               0 :                   "Unable to update geotransform on readonly file." ); 
     585               0 :         return CE_Failure;
     586                 :     }
     587                 : 
     588               4 :     if( padfTransform[2] != 0.0 || padfTransform[4] != 0.0 )
     589                 :     {
     590                 :         CPLError( CE_Failure, CPLE_AppDefined,
     591               0 :                   "Rotated and sheared geotransforms not supported for NTv2."); 
     592               0 :         return CE_Failure;
     593                 :     }
     594                 : 
     595               4 :     memcpy( adfGeoTransform, padfTransform, sizeof(double)*6 );
     596                 : 
     597                 : /* -------------------------------------------------------------------- */
     598                 : /*      Update grid header.                                             */
     599                 : /* -------------------------------------------------------------------- */
     600                 :     double dfValue;
     601                 :     char   achHeader[11*16];
     602                 : 
     603                 :     // read grid header
     604               4 :     VSIFSeekL( fpImage, nGridOffset, SEEK_SET );
     605               4 :     VSIFReadL( achHeader, 11, 16, fpImage );
     606                 : 
     607                 :     // S_LAT
     608               4 :     dfValue = 3600 * (adfGeoTransform[3] + (nRasterYSize-0.5) * adfGeoTransform[5]);
     609                 :     CPL_LSBPTR64( &dfValue );
     610               4 :     memcpy( achHeader +  4*16 + 8, &dfValue, 8 );
     611                 : 
     612                 :     // N_LAT
     613               4 :     dfValue = 3600 * (adfGeoTransform[3] + 0.5 * adfGeoTransform[5]);
     614                 :     CPL_LSBPTR64( &dfValue );
     615               4 :     memcpy( achHeader +  5*16 + 8, &dfValue, 8 );
     616                 : 
     617                 :     // E_LONG
     618               4 :     dfValue = -3600 * (adfGeoTransform[0] + (nRasterXSize-0.5)*adfGeoTransform[1]);
     619                 :     CPL_LSBPTR64( &dfValue );
     620               4 :     memcpy( achHeader +  6*16 + 8, &dfValue, 8 );
     621                 : 
     622                 :     // W_LONG
     623               4 :     dfValue = -3600 * (adfGeoTransform[0] + 0.5 * adfGeoTransform[1]);
     624                 :     CPL_LSBPTR64( &dfValue );
     625               4 :     memcpy( achHeader +  7*16 + 8, &dfValue, 8 );
     626                 : 
     627                 :     // LAT_INC
     628               4 :     dfValue = -3600 * adfGeoTransform[5];
     629                 :     CPL_LSBPTR64( &dfValue );
     630               4 :     memcpy( achHeader +  8*16 + 8, &dfValue, 8 );
     631                 :     
     632                 :     // LONG_INC
     633               4 :     dfValue = 3600 * adfGeoTransform[1];
     634                 :     CPL_LSBPTR64( &dfValue );
     635               4 :     memcpy( achHeader +  9*16 + 8, &dfValue, 8 );
     636                 :     
     637                 :     // write grid header.
     638               4 :     VSIFSeekL( fpImage, nGridOffset, SEEK_SET );
     639               4 :     VSIFWriteL( achHeader, 11, 16, fpImage );
     640                 : 
     641               4 :     return CE_None;
     642                 : }
     643                 : 
     644                 : 
     645                 : /************************************************************************/
     646                 : /*                          GetProjectionRef()                          */
     647                 : /************************************************************************/
     648                 : 
     649               5 : const char *NTv2Dataset::GetProjectionRef()
     650                 : 
     651                 : {
     652               5 :     return SRS_WKT_WGS84;
     653                 : }
     654                 : 
     655                 : /************************************************************************/
     656                 : /*                               Create()                               */
     657                 : /************************************************************************/
     658                 : 
     659              45 : GDALDataset *NTv2Dataset::Create( const char * pszFilename,
     660                 :                                   int nXSize, int nYSize, int nBands,
     661                 :                                   GDALDataType eType,
     662                 :                                   char ** papszOptions )
     663                 : 
     664                 : {
     665              45 :     if( eType != GDT_Float32 )
     666                 :     {
     667                 :         CPLError(CE_Failure, CPLE_AppDefined,
     668                 :                  "Attempt to create NTv2 file with unsupported data type '%s'.",
     669              38 :                  GDALGetDataTypeName( eType ) );
     670              38 :         return NULL;
     671                 :     }
     672                 : 
     673                 : /* -------------------------------------------------------------------- */
     674                 : /*      Are we extending an existing file?                              */
     675                 : /* -------------------------------------------------------------------- */
     676                 :     VSILFILE  *fp;
     677               7 :     GUInt32   nNumFile = 1;
     678                 : 
     679               7 :     int bAppend = CSLFetchBoolean(papszOptions,"APPEND_SUBDATASET",FALSE);
     680                 :     
     681                 : /* -------------------------------------------------------------------- */
     682                 : /*      Try to open or create file.                                     */
     683                 : /* -------------------------------------------------------------------- */
     684               7 :     if( bAppend )
     685               0 :         fp = VSIFOpenL( pszFilename, "rb+" );
     686                 :     else
     687               7 :         fp = VSIFOpenL( pszFilename, "wb" );
     688                 :     
     689               7 :     if( fp == NULL )
     690                 :     {
     691                 :         CPLError( CE_Failure, CPLE_OpenFailed,
     692                 :                   "Attempt to open/create file `%s' failed.\n",
     693               2 :                   pszFilename );
     694               2 :         return NULL;
     695                 :     }
     696                 : 
     697                 : /* -------------------------------------------------------------------- */
     698                 : /*      Create a file level header if we are creating new.              */
     699                 : /* -------------------------------------------------------------------- */
     700                 :     char achHeader[11*16];
     701                 :     const char *pszValue;
     702                 : 
     703               5 :     if( !bAppend )
     704                 :     {
     705               5 :         memset( achHeader, 0, sizeof(achHeader) );
     706                 :         
     707               5 :         memcpy( achHeader +  0*16, "NUM_OREC", 8 );
     708               5 :         achHeader[ 0*16 + 8] = 0xb;
     709                 : 
     710               5 :         memcpy( achHeader +  1*16, "NUM_SREC", 8 );
     711               5 :         achHeader[ 1*16 + 8] = 0xb;
     712                 : 
     713               5 :         memcpy( achHeader +  2*16, "NUM_FILE", 8 );
     714               5 :         achHeader[ 2*16 + 8] = 0x1;
     715                 : 
     716               5 :         memcpy( achHeader +  3*16, "GS_TYPE         ", 16 );
     717               5 :         pszValue = CSLFetchNameValueDef( papszOptions, "GS_TYPE", "SECONDS");
     718               5 :         memcpy( achHeader +  3*16+8, pszValue, MIN(16,strlen(pszValue)) );
     719                 : 
     720               5 :         memcpy( achHeader +  4*16, "VERSION         ", 16 );
     721               5 :         pszValue = CSLFetchNameValueDef( papszOptions, "VERSION", "" );
     722               5 :         memcpy( achHeader +  4*16+8, pszValue, MIN(16,strlen(pszValue)) );
     723                 : 
     724               5 :         memcpy( achHeader +  5*16, "SYSTEM_F        ", 16 );
     725               5 :         pszValue = CSLFetchNameValueDef( papszOptions, "SYSTEM_F", "" );
     726               5 :         memcpy( achHeader +  5*16+8, pszValue, MIN(16,strlen(pszValue)) );
     727                 : 
     728               5 :         memcpy( achHeader +  6*16, "SYSTEM_T        ", 16 );
     729               5 :         pszValue = CSLFetchNameValueDef( papszOptions, "SYSTEM_T", "" );
     730               5 :         memcpy( achHeader +  6*16+8, pszValue, MIN(16,strlen(pszValue)) );
     731                 : 
     732               5 :         memcpy( achHeader +  7*16, "MAJOR_F ", 8);
     733               5 :         memcpy( achHeader +  8*16, "MINOR_F ", 8 );
     734               5 :         memcpy( achHeader +  9*16, "MAJOR_T ", 8 );
     735               5 :         memcpy( achHeader + 10*16, "MINOR_T ", 8 );
     736                 : 
     737               5 :         VSIFWriteL( achHeader, 1, sizeof(achHeader), fp );
     738                 :     }
     739                 : 
     740                 : /* -------------------------------------------------------------------- */
     741                 : /*      Otherwise update the header with an increased subfile count,    */
     742                 : /*      and advanced to the last record of the file.                    */
     743                 : /* -------------------------------------------------------------------- */
     744                 :     else
     745                 :     {
     746               0 :         VSIFSeekL( fp, 2*16 + 8, SEEK_SET );
     747               0 :         VSIFReadL( &nNumFile, 1, 4, fp );
     748                 :         CPL_LSBPTR32( &nNumFile );
     749                 : 
     750               0 :         nNumFile++;
     751                 :         
     752                 :         CPL_LSBPTR32( &nNumFile );
     753               0 :         VSIFSeekL( fp, 2*16 + 8, SEEK_SET );
     754               0 :         VSIFWriteL( &nNumFile, 1, 4, fp );
     755                 : 
     756                 :         vsi_l_offset nEnd;
     757                 : 
     758               0 :         VSIFSeekL( fp, 0, SEEK_END );
     759               0 :         nEnd = VSIFTellL( fp );
     760               0 :         VSIFSeekL( fp, nEnd-16, SEEK_SET );
     761                 :     }
     762                 : 
     763                 : /* -------------------------------------------------------------------- */
     764                 : /*      Write the grid header.                                          */
     765                 : /* -------------------------------------------------------------------- */
     766               5 :     memset( achHeader, 0, sizeof(achHeader) );
     767                 : 
     768               5 :     memcpy( achHeader +  0*16, "SUB_NAME        ", 16 );
     769               5 :     pszValue = CSLFetchNameValueDef( papszOptions, "SUB_NAME", "" );
     770               5 :     memcpy( achHeader +  0*16+8, pszValue, MIN(16,strlen(pszValue)) );
     771                 :     
     772               5 :     memcpy( achHeader +  1*16, "PARENT          ", 16 );
     773               5 :     pszValue = CSLFetchNameValueDef( papszOptions, "PARENT", "NONE" );
     774               5 :     memcpy( achHeader +  1*16+8, pszValue, MIN(16,strlen(pszValue)) );
     775                 :     
     776               5 :     memcpy( achHeader +  2*16, "CREATED         ", 16 );
     777               5 :     pszValue = CSLFetchNameValueDef( papszOptions, "CREATED", "" );
     778               5 :     memcpy( achHeader +  2*16+8, pszValue, MIN(16,strlen(pszValue)) );
     779                 :     
     780               5 :     memcpy( achHeader +  3*16, "UPDATED         ", 16 );
     781               5 :     pszValue = CSLFetchNameValueDef( papszOptions, "UPDATED", "" );
     782               5 :     memcpy( achHeader +  3*16+8, pszValue, MIN(16,strlen(pszValue)) );
     783                 : 
     784                 :     double dfValue;
     785                 : 
     786               5 :     memcpy( achHeader +  4*16, "S_LAT   ", 8 );
     787               5 :     dfValue = 0;
     788                 :     CPL_LSBPTR64( &dfValue );
     789               5 :     memcpy( achHeader +  4*16 + 8, &dfValue, 8 );
     790                 : 
     791               5 :     memcpy( achHeader +  5*16, "N_LAT   ", 8 );
     792               5 :     dfValue = nYSize-1;
     793                 :     CPL_LSBPTR64( &dfValue );
     794               5 :     memcpy( achHeader +  5*16 + 8, &dfValue, 8 );
     795                 : 
     796               5 :     memcpy( achHeader +  6*16, "E_LONG  ", 8 );
     797               5 :     dfValue = -1*(nXSize-1);
     798                 :     CPL_LSBPTR64( &dfValue );
     799               5 :     memcpy( achHeader +  6*16 + 8, &dfValue, 8 );
     800                 : 
     801               5 :     memcpy( achHeader +  7*16, "W_LONG  ", 8 );
     802               5 :     dfValue = 0;
     803                 :     CPL_LSBPTR64( &dfValue );
     804               5 :     memcpy( achHeader +  7*16 + 8, &dfValue, 8 );
     805                 : 
     806               5 :     memcpy( achHeader +  8*16, "LAT_INC ", 8 );
     807               5 :     dfValue = 1;
     808                 :     CPL_LSBPTR64( &dfValue );
     809               5 :     memcpy( achHeader +  8*16 + 8, &dfValue, 8 );
     810                 :     
     811               5 :     memcpy( achHeader +  9*16, "LONG_INC", 8 );
     812               5 :     memcpy( achHeader +  9*16 + 8, &dfValue, 8 );
     813                 :     
     814               5 :     memcpy( achHeader + 10*16, "GS_COUNT", 8 );
     815               5 :     GUInt32 nGSCount = nXSize * nYSize;
     816                 :     CPL_LSBPTR32( &nGSCount );
     817               5 :     memcpy( achHeader + 10*16+8, &nGSCount, 4 );
     818                 :     
     819               5 :     VSIFWriteL( achHeader, 1, sizeof(achHeader), fp );
     820                 : 
     821                 : /* -------------------------------------------------------------------- */
     822                 : /*      Write zeroed grid data.                                         */
     823                 : /* -------------------------------------------------------------------- */
     824                 :     int i;
     825                 : 
     826               5 :     memset( achHeader, 0, 16 );
     827                 : 
     828                 :     // Use -1 (0x000080bf) as the default error value.
     829               5 :     memset( achHeader + 10, 0x80, 1 );
     830               5 :     memset( achHeader + 11, 0xbf, 1 );
     831               5 :     memset( achHeader + 14, 0x80, 1 );
     832               5 :     memset( achHeader + 15, 0xbf, 1 );
     833                 : 
     834           59867 :     for( i = 0; i < nXSize * nYSize; i++ )
     835           59862 :         VSIFWriteL( achHeader, 1, 16, fp );
     836                 :     
     837                 : /* -------------------------------------------------------------------- */
     838                 : /*      Write the end record.                                           */
     839                 : /* -------------------------------------------------------------------- */
     840               5 :     memset( achHeader, 0, 16 );
     841               5 :     memcpy( achHeader, "END     ", 8 );
     842               5 :     VSIFWriteL( achHeader, 1, 16, fp );
     843               5 :     VSIFCloseL( fp );
     844                 : 
     845               5 :     if( nNumFile == 1 )
     846               5 :         return (GDALDataset *) GDALOpen( pszFilename, GA_Update );
     847                 :     else
     848                 :     {
     849               0 :         CPLString osSubDSName;
     850               0 :         osSubDSName.Printf( "NTv2:%d:%s", nNumFile-1, pszFilename );
     851               0 :         return (GDALDataset *) GDALOpen( osSubDSName, GA_Update );
     852                 :     }
     853                 : }
     854                 : 
     855                 : /************************************************************************/
     856                 : /*                         GDALRegister_NTv2()                          */
     857                 : /************************************************************************/
     858                 : 
     859             582 : void GDALRegister_NTv2()
     860                 : 
     861                 : {
     862                 :     GDALDriver  *poDriver;
     863                 : 
     864             582 :     if( GDALGetDriverByName( "NTv2" ) == NULL )
     865                 :     {
     866             561 :         poDriver = new GDALDriver();
     867                 :         
     868             561 :         poDriver->SetDescription( "NTv2" );
     869                 :         poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, 
     870             561 :                                    "NTv2 Datum Grid Shift" );
     871             561 :         poDriver->SetMetadataItem( GDAL_DMD_EXTENSION, "gsb" );
     872             561 :         poDriver->SetMetadataItem( GDAL_DCAP_VIRTUALIO, "YES" );
     873                 : 
     874                 :         poDriver->SetMetadataItem( GDAL_DMD_CREATIONDATATYPES, 
     875             561 :                                    "Float32" );
     876                 : 
     877             561 :         poDriver->pfnOpen = NTv2Dataset::Open;
     878             561 :         poDriver->pfnIdentify = NTv2Dataset::Identify;
     879             561 :         poDriver->pfnCreate = NTv2Dataset::Create;
     880                 : 
     881             561 :         GetGDALDriverManager()->RegisterDriver( poDriver );
     882                 :     }
     883             582 : }
     884                 : 

Generated by: LCOV version 1.7