LCOV - code coverage report
Current view: directory - frmts/grib - gribdataset.cpp (source / functions) Found Hit Coverage
Test: gdal_filtered.info Lines: 280 201 71.8 %
Date: 2011-12-18 Functions: 20 11 55.0 %

       1                 : /******************************************************************************
       2                 :  * $Id: gribdataset.cpp 23302 2011-11-02 16:07:04Z aboudreault $
       3                 :  *
       4                 :  * Project:  GRIB Driver
       5                 :  * Purpose:  GDALDataset driver for GRIB translator for read support
       6                 :  * Author:   Bas Retsios, retsios@itc.nl
       7                 :  *
       8                 :  ******************************************************************************
       9                 :  * Copyright (c) 2007, ITC
      10                 :  *
      11                 :  * Permission is hereby granted, free of charge, to any person obtaining a
      12                 :  * copy of this software and associated documentation files (the "Software"),
      13                 :  * to deal in the Software without restriction, including without limitation
      14                 :  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
      15                 :  * and/or sell copies of the Software, and to permit persons to whom the
      16                 :  * Software is furnished to do so, subject to the following conditions:
      17                 :  *
      18                 :  * The above copyright notice and this permission notice shall be included
      19                 :  * in all copies or substantial portions of the Software.
      20                 :  *
      21                 :  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
      22                 :  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
      23                 :  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
      24                 :  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
      25                 :  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
      26                 :  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
      27                 :  * DEALINGS IN THE SOFTWARE.
      28                 :  ******************************************************************************
      29                 :  * 
      30                 :  */
      31                 : 
      32                 : #include "gdal_pam.h"
      33                 : #include "cpl_multiproc.h"
      34                 : 
      35                 : #include "degrib18/degrib/degrib2.h"
      36                 : #include "degrib18/degrib/inventory.h"
      37                 : #include "degrib18/degrib/myerror.h"
      38                 : #include "degrib18/degrib/filedatasource.h"
      39                 : #include "degrib18/degrib/memorydatasource.h"
      40                 : 
      41                 : #include "ogr_spatialref.h"
      42                 : 
      43                 : CPL_CVSID("$Id: gribdataset.cpp 23302 2011-11-02 16:07:04Z aboudreault $");
      44                 : 
      45                 : CPL_C_START
      46                 : void  GDALRegister_GRIB(void);
      47                 : CPL_C_END
      48                 : 
      49                 : /************************************************************************/
      50                 : /* ==================================================================== */
      51                 : /*        GRIBDataset       */
      52                 : /* ==================================================================== */
      53                 : /************************************************************************/
      54                 : 
      55                 : class GRIBRasterBand;
      56                 : 
      57                 : class GRIBDataset : public GDALPamDataset
      58                 : {
      59                 :     friend class GRIBRasterBand;
      60                 : 
      61                 :   public:
      62                 :     GRIBDataset();
      63                 :     ~GRIBDataset();
      64                 :     
      65                 :     static GDALDataset *Open( GDALOpenInfo * );
      66                 :     static int          Identify( GDALOpenInfo * );
      67                 : 
      68                 :     CPLErr  GetGeoTransform( double * padfTransform );
      69                 :     const char *GetProjectionRef();
      70                 :   private:
      71                 :     void SetGribMetaData(grib_MetaData* meta);
      72                 :     VSILFILE  *fp;
      73                 :     char  *pszProjection;
      74                 :     char  *pszDescription;
      75                 :     OGRCoordinateTransformation *poTransform;
      76                 :     double adfGeoTransform[6]; // Calculate and store once as GetGeoTransform may be called multiple times
      77                 : };
      78                 : 
      79                 : /************************************************************************/
      80                 : /* ==================================================================== */
      81                 : /*                            GRIBRasterBand                             */
      82                 : /* ==================================================================== */
      83                 : /************************************************************************/
      84                 : 
      85                 : class GRIBRasterBand : public GDALPamRasterBand
      86                 : {
      87                 :     friend class GRIBDataset;
      88                 :     
      89                 : public:
      90                 :     GRIBRasterBand( GRIBDataset*, int, inventoryType* );
      91                 :     virtual ~GRIBRasterBand();
      92                 :     virtual CPLErr IReadBlock( int, int, void * );
      93                 :     virtual const char *GetDescription() const;
      94                 : 
      95                 :     void    FindPDSTemplate();
      96                 : 
      97                 : private:
      98                 :     static void ReadGribData( DataSource &, sInt4, int, double**, grib_MetaData**);
      99                 :     sInt4 start;
     100                 :     int subgNum;
     101                 :     char *longFstLevel;
     102                 : 
     103                 :     double * m_Grib_Data;
     104                 :     grib_MetaData* m_Grib_MetaData;
     105                 : 
     106                 :     int      nGribDataXSize;
     107                 :     int      nGribDataYSize;
     108                 : };
     109                 : 
     110                 : 
     111                 : /************************************************************************/
     112                 : /*                           GRIBRasterBand()                            */
     113                 : /************************************************************************/
     114                 : 
     115              18 : GRIBRasterBand::GRIBRasterBand( GRIBDataset *poDS, int nBand, 
     116                 :                                 inventoryType *psInv )
     117              18 :   : m_Grib_Data(NULL), m_Grib_MetaData(NULL)
     118                 : {
     119              18 :     this->poDS = poDS;
     120              18 :     this->nBand = nBand;
     121              18 :     this->start = psInv->start;
     122              18 :     this->subgNum = psInv->subgNum;
     123              18 :     this->longFstLevel = CPLStrdup(psInv->longFstLevel);
     124                 : 
     125              18 :     eDataType = GDT_Float64; // let user do -ot Float32 if needed for saving space, GRIB contains Float64 (though not fully utilized most of the time)
     126                 : 
     127              18 :     nBlockXSize = poDS->nRasterXSize;
     128              18 :     nBlockYSize = 1;
     129                 : 
     130              18 :     nGribDataXSize = poDS->nRasterXSize;
     131              18 :     nGribDataYSize = poDS->nRasterYSize;
     132                 : 
     133              18 :     SetMetadataItem( "GRIB_UNIT", psInv->unitName );
     134              18 :     SetMetadataItem( "GRIB_COMMENT", psInv->comment );
     135              18 :     SetMetadataItem( "GRIB_ELEMENT", psInv->element );
     136              18 :     SetMetadataItem( "GRIB_SHORT_NAME", psInv->shortFstLevel );
     137                 :     SetMetadataItem( "GRIB_REF_TIME", 
     138              18 :                      CPLString().Printf("%12.0f sec UTC", psInv->refTime ) );
     139                 :     SetMetadataItem( "GRIB_VALID_TIME", 
     140              18 :                      CPLString().Printf("%12.0f sec UTC", psInv->validTime ) );
     141                 :     SetMetadataItem( "GRIB_FORECAST_SECONDS", 
     142              18 :                      CPLString().Printf("%.0f sec", psInv->foreSec ) );
     143              18 : }
     144                 : 
     145                 : /************************************************************************/
     146                 : /*                          FindPDSTemplate()                           */
     147                 : /*                                                                      */
     148                 : /*      Scan the file for the PDS template info and represent it as     */
     149                 : /*      metadata.                                                       */
     150                 : /************************************************************************/
     151                 : 
     152               1 : void GRIBRasterBand::FindPDSTemplate()
     153                 : 
     154                 : {
     155               1 :     GRIBDataset *poGDS = (GRIBDataset *) poDS;
     156                 : 
     157                 : /* -------------------------------------------------------------------- */
     158                 : /*      Collect section 4 octet information ... we read the file        */
     159                 : /*      ourselves since the GRIB API does not appear to preserve all    */
     160                 : /*      this for us.                                                    */
     161                 : /* -------------------------------------------------------------------- */
     162               1 :     GIntBig nOffset = VSIFTellL( poGDS->fp );
     163                 :     GByte abyHead[5];
     164                 :     GUInt32 nSectSize;
     165                 : 
     166               1 :     VSIFSeekL( poGDS->fp, start+16, SEEK_SET );
     167               1 :     VSIFReadL( abyHead, 5, 1, poGDS->fp );
     168                 : 
     169               1 :     while( abyHead[4] != 4 )
     170                 :     {
     171               1 :         memcpy( &nSectSize, abyHead, 4 );
     172               1 :         CPL_MSBPTR32( &nSectSize );
     173                 : 
     174               1 :         if( VSIFSeekL( poGDS->fp, nSectSize-5, SEEK_CUR ) != 0
     175                 :             || VSIFReadL( abyHead, 5, 1, poGDS->fp ) != 1 )
     176               1 :             break;
     177                 :     }
     178                 :         
     179               1 :     if( abyHead[4] == 4 )
     180                 :     {
     181                 :         GUInt16 nCoordCount;
     182                 :         GUInt16 nPDTN;
     183               0 :         CPLString osOctet;
     184                 :         int i;
     185                 :         GByte *pabyBody;
     186                 : 
     187               0 :         memcpy( &nSectSize, abyHead, 4 );
     188               0 :         CPL_MSBPTR32( &nSectSize );
     189                 : 
     190               0 :         pabyBody = (GByte *) CPLMalloc(nSectSize-5);
     191               0 :         VSIFReadL( pabyBody, 1, nSectSize-5, poGDS->fp );
     192                 : 
     193               0 :         memcpy( &nCoordCount, pabyBody + 5 - 5, 2 );
     194               0 :         CPL_MSBPTR16( &nCoordCount );
     195                 : 
     196               0 :         memcpy( &nPDTN, pabyBody + 7 - 5, 2 );
     197               0 :         CPL_MSBPTR16( &nPDTN );
     198                 : 
     199                 :         SetMetadataItem( "GRIB_PDS_PDTN",
     200               0 :                          CPLString().Printf( "%d", nPDTN ) );
     201                 : 
     202               0 :         for( i = 9; i < (int) nSectSize; i++ )
     203                 :         {
     204                 :             char szByte[10];
     205                 : 
     206               0 :             if( i == 9 )
     207               0 :                 sprintf( szByte, "%d", pabyBody[i-5] );
     208                 :             else
     209               0 :                 sprintf( szByte, " %d", pabyBody[i-5] );
     210               0 :             osOctet += szByte;
     211                 :         }
     212                 :         
     213               0 :         SetMetadataItem( "GRIB_PDS_TEMPLATE_NUMBERS", osOctet );
     214                 : 
     215               0 :         CPLFree( pabyBody );
     216                 :     }
     217                 : 
     218               1 :     VSIFSeekL( poGDS->fp, nOffset, SEEK_SET );
     219               1 : }
     220                 : 
     221                 : /************************************************************************/
     222                 : /*                         GetDescription()                             */
     223                 : /************************************************************************/
     224                 : 
     225               0 : const char * GRIBRasterBand::GetDescription() const
     226                 : {
     227               0 :     if( longFstLevel == NULL )
     228               0 :         return GDALPamRasterBand::GetDescription();
     229                 :     else
     230               0 :         return longFstLevel;
     231                 : }
     232                 :  
     233                 : /************************************************************************/
     234                 : /*                             IReadBlock()                             */
     235                 : /************************************************************************/
     236                 : 
     237             281 : CPLErr GRIBRasterBand::IReadBlock( int nBlockXOff, int nBlockYOff,
     238                 :                                    void * pImage )
     239                 : 
     240                 : {
     241             281 :     if( !m_Grib_Data )
     242                 :     {
     243               3 :         GRIBDataset *poGDS = (GRIBDataset *) poDS;
     244                 : 
     245               3 :         FileDataSource grib_fp (poGDS->fp);
     246                 : 
     247                 :         // we don't seem to have any way to detect errors in this!
     248               3 :         ReadGribData(grib_fp, start, subgNum, &m_Grib_Data, &m_Grib_MetaData);
     249               3 :         if( !m_Grib_Data )
     250                 :         {
     251               0 :             CPLError( CE_Failure, CPLE_AppDefined, "Out of memory." );
     252               0 :             return CE_Failure;
     253                 :         }
     254                 : 
     255                 : /* -------------------------------------------------------------------- */
     256                 : /*      Check that this band matches the dataset as a whole, size       */
     257                 : /*      wise. (#3246)                                                   */
     258                 : /* -------------------------------------------------------------------- */
     259               3 :         nGribDataXSize = m_Grib_MetaData->gds.Nx;
     260               3 :         nGribDataYSize = m_Grib_MetaData->gds.Ny;
     261                 : 
     262               3 :         if( nGribDataXSize != nRasterXSize 
     263                 :             || nGribDataYSize != nRasterYSize )
     264                 :         {
     265                 :             CPLError( CE_Warning, CPLE_AppDefined,
     266                 :                       "Band %d of GRIB dataset is %dx%d, while the first band and dataset is %dx%d.  Georeferencing of band %d may be incorrect, and data access may be incomplete.", 
     267                 :                       nBand, 
     268                 :                       nGribDataXSize, nGribDataYSize, 
     269                 :                       nRasterXSize, nRasterYSize, 
     270               1 :                       nBand );
     271               0 :         }
     272                 :     }
     273                 : 
     274                 : /* -------------------------------------------------------------------- */
     275                 : /*      The image as read is always upside down to our normal           */
     276                 : /*      orientation so we need to effectively flip it at this           */
     277                 : /*      point.  We also need to deal with bands that are a different    */
     278                 : /*      size than the dataset as a whole.                               */
     279                 : /* -------------------------------------------------------------------- */
     280             281 :     if( nGribDataXSize == nRasterXSize
     281                 :         && nGribDataYSize == nRasterYSize )
     282                 :     {
     283                 :         // Simple 1:1 case.
     284                 :         memcpy(pImage, 
     285                 :                m_Grib_Data + nRasterXSize * (nRasterYSize - nBlockYOff - 1), 
     286             203 :                nRasterXSize * sizeof(double));
     287                 :         
     288             203 :         return CE_None;
     289                 :     }
     290                 :     else
     291                 :     {
     292              78 :         memset( pImage, 0, sizeof(double)*nRasterXSize );
     293                 : 
     294              78 :         if( nBlockYOff >= nGribDataYSize ) // off image?
     295              57 :             return CE_None;
     296                 : 
     297              21 :         int nCopyWords = MIN(nRasterXSize,nGribDataXSize);
     298                 : 
     299                 :         memcpy( pImage, 
     300                 :                 m_Grib_Data + nGribDataXSize*(nGribDataYSize-nBlockYOff-1),
     301              21 :                 nCopyWords * sizeof(double) );
     302                 :         
     303              21 :         return CE_None;
     304                 :     }
     305                 : }
     306                 : 
     307                 : /************************************************************************/
     308                 : /*                            ReadGribData()                            */
     309                 : /************************************************************************/
     310                 : 
     311               6 : void GRIBRasterBand::ReadGribData( DataSource & fp, sInt4 start, int subgNum, double** data, grib_MetaData** metaData)
     312                 : {
     313                 :     /* Initialisation, for calling the ReadGrib2Record function */
     314               6 :     sInt4 f_endMsg = 1;  /* 1 if we read the last grid in a GRIB message, or we haven't read any messages. */
     315                 :     // int subgNum = 0;     /* The subgrid in the message that we are interested in. */
     316               6 :     sChar f_unit = 2;        /* None = 0, English = 1, Metric = 2 */
     317               6 :     double majEarth = 0;     /* -radEarth if < 6000 ignore, otherwise use this to
     318                 :                               * override the radEarth in the GRIB1 or GRIB2
     319                 :                               * message.  Needed because NCEP uses 6371.2 but GRIB1 could only state 6367.47. */
     320               6 :     double minEarth = 0;     /* -minEarth if < 6000 ignore, otherwise use this to
     321                 :                               * override the minEarth in the GRIB1 or GRIB2 message. */
     322               6 :     sChar f_SimpleVer = 4;   /* Which version of the simple NDFD Weather table to
     323                 :                               * use. (1 is 6/2003) (2 is 1/2004) (3 is 2/2004)
     324                 :                               * (4 is 11/2004) (default 4) */
     325                 :     LatLon lwlf;         /* lower left corner (cookie slicing) -lwlf */
     326                 :     LatLon uprt;         /* upper right corner (cookie slicing) -uprt */
     327                 :     IS_dataType is;      /* Un-parsed meta data for this GRIB2 message. As well as some memory used by the unpacker. */
     328                 : 
     329               6 :     lwlf.lat = -100; // lat == -100 instructs the GRIB decoder that we don't want a subgrid
     330                 : 
     331               6 :     IS_Init (&is);
     332                 : 
     333               6 :     const char* pszGribNormalizeUnits = CPLGetConfigOption("GRIB_NORMALIZE_UNITS", NULL);
     334               6 :     if ( pszGribNormalizeUnits != NULL && ( STRCASECMP(pszGribNormalizeUnits,"NO")==0 ) )
     335               0 :         f_unit = 0; /* do not normalize units to metric */
     336                 : 
     337                 :     /* Read GRIB message from file position "start". */
     338               6 :     fp.DataSourceFseek(start, SEEK_SET);
     339               6 :     uInt4 grib_DataLen = 0;  /* Size of Grib_Data. */
     340               6 :     *metaData = new grib_MetaData();
     341               6 :     MetaInit (*metaData);
     342                 :     ReadGrib2Record (fp, f_unit, data, &grib_DataLen, *metaData, &is, subgNum,
     343               6 :                      majEarth, minEarth, f_SimpleVer, &f_endMsg, &lwlf, &uprt);
     344                 : 
     345               6 :     char * errMsg = errSprintf(NULL); // no intention to show errors, just swallow it and free the memory
     346               6 :     if( errMsg != NULL )
     347               0 :         CPLDebug( "GRIB", "%s", errMsg );
     348               6 :     free(errMsg);
     349               6 :     IS_Free(&is);
     350               6 : }
     351                 : 
     352                 : /************************************************************************/
     353                 : /*                           ~GRIBRasterBand()                          */
     354                 : /************************************************************************/
     355                 : 
     356              18 : GRIBRasterBand::~GRIBRasterBand()
     357                 : {
     358              18 :     CPLFree(longFstLevel);
     359              18 :     if (m_Grib_Data)
     360               6 :         free (m_Grib_Data);
     361              18 :     if (m_Grib_MetaData)
     362                 :     {
     363               6 :         MetaFree( m_Grib_MetaData );
     364               6 :         delete m_Grib_MetaData;
     365                 :     }
     366              18 : }
     367                 : 
     368                 : /************************************************************************/
     369                 : /* ==================================================================== */
     370                 : /*        GRIBDataset       */
     371                 : /* ==================================================================== */
     372                 : /************************************************************************/
     373                 : 
     374               3 : GRIBDataset::GRIBDataset()
     375                 : 
     376                 : {
     377               3 :   poTransform = NULL;
     378               3 :   pszProjection = CPLStrdup("");
     379               3 :   adfGeoTransform[0] = 0.0;
     380               3 :   adfGeoTransform[1] = 1.0;
     381               3 :   adfGeoTransform[2] = 0.0;
     382               3 :   adfGeoTransform[3] = 0.0;
     383               3 :   adfGeoTransform[4] = 0.0;
     384               3 :   adfGeoTransform[5] = 1.0;
     385               3 : }
     386                 : 
     387                 : /************************************************************************/
     388                 : /*                            ~GRIBDataset()                             */
     389                 : /************************************************************************/
     390                 : 
     391               3 : GRIBDataset::~GRIBDataset()
     392                 : 
     393                 : {
     394               3 :     FlushCache();
     395               3 :     if( fp != NULL )
     396               3 :         VSIFCloseL( fp );
     397                 :     
     398               3 :     CPLFree( pszProjection );
     399               3 : }
     400                 : 
     401                 : /************************************************************************/
     402                 : /*                          GetGeoTransform()                           */
     403                 : /************************************************************************/
     404                 : 
     405               0 : CPLErr GRIBDataset::GetGeoTransform( double * padfTransform )
     406                 : 
     407                 : {
     408               0 :     memcpy( padfTransform,  adfGeoTransform, sizeof(double) * 6 );
     409               0 :     return CE_None;
     410                 : }
     411                 : 
     412                 : /************************************************************************/
     413                 : /*                          GetProjectionRef()                          */
     414                 : /************************************************************************/
     415                 : 
     416               0 : const char *GRIBDataset::GetProjectionRef()
     417                 : 
     418                 : {
     419               0 :     return pszProjection;
     420                 : }
     421                 : 
     422                 : /************************************************************************/
     423                 : /*                            Identify()                                */
     424                 : /************************************************************************/
     425                 : 
     426           11403 : int GRIBDataset::Identify( GDALOpenInfo * poOpenInfo )
     427                 : {
     428           11403 :     if (poOpenInfo->nHeaderBytes < 8)
     429           10600 :         return FALSE;
     430                 :         
     431                 : /* -------------------------------------------------------------------- */
     432                 : /*      Does a part of what ReadSECT0() but in a thread-safe way.       */
     433                 : /* -------------------------------------------------------------------- */
     434                 :     int i;
     435          635286 :     for(i=0;i<poOpenInfo->nHeaderBytes-3;i++)
     436                 :     {
     437          634486 :         if (EQUALN((const char*)poOpenInfo->pabyHeader + i, "GRIB", 4) ||
     438                 :             EQUALN((const char*)poOpenInfo->pabyHeader + i, "TDLP", 4))
     439               3 :             return TRUE;
     440                 :     }
     441                 :     
     442             800 :     return FALSE;
     443                 : }
     444                 : 
     445                 : /************************************************************************/
     446                 : /*                                Open()                                */
     447                 : /************************************************************************/
     448                 : 
     449            2158 : GDALDataset *GRIBDataset::Open( GDALOpenInfo * poOpenInfo )
     450                 : 
     451                 : {
     452            2158 :     if( !Identify(poOpenInfo) )
     453            2155 :         return NULL;
     454                 :         
     455                 : /* -------------------------------------------------------------------- */
     456                 : /*      A fast "probe" on the header that is partially read in memory.  */
     457                 : /* -------------------------------------------------------------------- */
     458               3 :     char *buff = NULL;
     459               3 :     uInt4 buffLen = 0;
     460                 :     sInt4 sect0[SECT0LEN_WORD];
     461                 :     uInt4 gribLen;
     462                 :     int version;
     463                 : // grib is not thread safe, make sure not to cause problems
     464                 : // for other thread safe formats
     465                 :     static void *mutex = 0;
     466               3 :     CPLMutexHolderD(&mutex);
     467               3 :     MemoryDataSource mds (poOpenInfo->pabyHeader, poOpenInfo->nHeaderBytes);
     468               3 :     if (ReadSECT0 (mds, &buff, &buffLen, -1, sect0, &gribLen, &version) < 0) {
     469               0 :         free (buff);
     470               0 :         char * errMsg = errSprintf(NULL);
     471               0 :         if( errMsg != NULL && strstr(errMsg,"Ran out of file") == NULL )
     472               0 :             CPLDebug( "GRIB", "%s", errMsg );
     473               0 :         free(errMsg);
     474               0 :         return NULL;
     475                 :     }
     476               3 :     free(buff);
     477                 :     
     478                 : /* -------------------------------------------------------------------- */
     479                 : /*      Confirm the requested access is supported.                      */
     480                 : /* -------------------------------------------------------------------- */
     481               3 :     if( poOpenInfo->eAccess == GA_Update )
     482                 :     {
     483                 :         CPLError( CE_Failure, CPLE_NotSupported, 
     484                 :                   "The GRIB driver does not support update access to existing"
     485               0 :                   " datasets.\n" );
     486               0 :         return NULL;
     487                 :     }
     488                 : /* -------------------------------------------------------------------- */
     489                 : /*      Create a corresponding GDALDataset.                             */
     490                 : /* -------------------------------------------------------------------- */
     491                 :     GRIBDataset   *poDS;
     492                 : 
     493               3 :     poDS = new GRIBDataset();
     494                 : 
     495               6 :     poDS->fp = VSIFOpenL( poOpenInfo->pszFilename, "r" );
     496                 : 
     497                 :   /* Check the return values */    
     498               3 :   if (!poDS->fp) {
     499                 :         // we have no FP, so we don't have anywhere to read from
     500               0 :         char * errMsg = errSprintf(NULL);
     501               0 :         if( errMsg != NULL )
     502               0 :             CPLDebug( "GRIB", "%s", errMsg );
     503               0 :         free(errMsg);
     504                 :     
     505               0 :     CPLError( CE_Failure, CPLE_OpenFailed, "Error (%d) opening file %s", errno, poOpenInfo->pszFilename);
     506               0 :         delete poDS;
     507               0 :         return NULL;
     508                 :   }
     509                 :     
     510                 : /* -------------------------------------------------------------------- */
     511                 : /*      Read the header.                                                */
     512                 : /* -------------------------------------------------------------------- */
     513                 : 
     514                 : /* -------------------------------------------------------------------- */
     515                 : /*      Make an inventory of the GRIB file.                             */
     516                 : /* The inventory does not contain all the information needed for        */
     517                 : /* creating the RasterBands (especially the x and y size), therefore    */
     518                 : /* the first GRIB band is also read for some additional metadata.       */
     519                 : /* The band-data that is read is stored into the first RasterBand,      */
     520                 : /* simply so that the same portion of the file is not read twice.       */
     521                 : /* -------------------------------------------------------------------- */
     522                 :     
     523               3 :     VSIFSeekL( poDS->fp, 0, SEEK_SET );
     524                 : 
     525               3 :     FileDataSource grib_fp (poDS->fp);
     526                 : 
     527               3 :     inventoryType *Inv = NULL;  /* Contains an GRIB2 message inventory of the file */
     528               3 :     uInt4 LenInv = 0;        /* size of Inv (also # of GRIB2 messages) */
     529               3 :     int msgNum =0;          /* The messageNumber during the inventory. */
     530                 : 
     531               3 :     if (GRIB2Inventory (grib_fp, &Inv, &LenInv, 0, &msgNum) <= 0 )
     532                 :     {
     533               0 :         char * errMsg = errSprintf(NULL);
     534               0 :         if( errMsg != NULL )
     535               0 :             CPLDebug( "GRIB", "%s", errMsg );
     536               0 :         free(errMsg);
     537                 : 
     538                 :         CPLError( CE_Failure, CPLE_OpenFailed, 
     539                 :                   "%s is a grib file, but no raster dataset was successfully identified.",
     540               0 :                   poOpenInfo->pszFilename );
     541               0 :         delete poDS;
     542               0 :         return NULL;
     543                 :     }
     544                 : 
     545                 : /* -------------------------------------------------------------------- */
     546                 : /*      Create band objects.                                            */
     547                 : /* -------------------------------------------------------------------- */
     548              21 :     for (uInt4 i = 0; i < LenInv; ++i)
     549                 :     {
     550              18 :         uInt4 bandNr = i+1;
     551              18 :         if (bandNr == 1)
     552                 :         {
     553                 :             // important: set DataSet extents before creating first RasterBand in it
     554               3 :             double * data = NULL;
     555                 :             grib_MetaData* metaData;
     556               3 :             GRIBRasterBand::ReadGribData(grib_fp, 0, Inv[i].subgNum, &data, &metaData);
     557               3 :             if (data == 0 || metaData->gds.Nx < 1 || metaData->gds.Ny < 1)
     558                 :             {
     559                 :                 CPLError( CE_Failure, CPLE_OpenFailed, 
     560                 :                           "%s is a grib file, but no raster dataset was successfully identified.",
     561               0 :                           poOpenInfo->pszFilename );
     562               0 :                 delete poDS;
     563               0 :                 return NULL;
     564                 :             }
     565                 : 
     566               3 :             poDS->SetGribMetaData(metaData); // set the DataSet's x,y size, georeference and projection from the first GRIB band
     567               3 :             GRIBRasterBand* gribBand = new GRIBRasterBand( poDS, bandNr, Inv+i);
     568                 : 
     569               3 :             if( Inv->GribVersion == 2 )
     570               1 :                 gribBand->FindPDSTemplate();
     571                 : 
     572               3 :             gribBand->m_Grib_Data = data;
     573               3 :             gribBand->m_Grib_MetaData = metaData;
     574               3 :             poDS->SetBand( bandNr, gribBand);
     575                 :         }
     576                 :         else
     577              15 :             poDS->SetBand( bandNr, new GRIBRasterBand( poDS, bandNr, Inv+i ));
     578              18 :         GRIB2InventoryFree (Inv + i);
     579                 :     }
     580               3 :     free (Inv);
     581                 : 
     582                 : /* -------------------------------------------------------------------- */
     583                 : /*      Initialize any PAM information.                                 */
     584                 : /* -------------------------------------------------------------------- */
     585               3 :     poDS->SetDescription( poOpenInfo->pszFilename );
     586               3 :     poDS->TryLoadXML();
     587                 : 
     588                 : /* -------------------------------------------------------------------- */
     589                 : /*      Check for external overviews.                                   */
     590                 : /* -------------------------------------------------------------------- */
     591               3 :     poDS->oOvManager.Initialize( poDS, poOpenInfo->pszFilename, poOpenInfo->papszSiblingFiles );
     592                 : 
     593               3 :     return( poDS );
     594                 : }
     595                 : 
     596                 : /************************************************************************/
     597                 : /*                            SetMetadata()                             */
     598                 : /************************************************************************/
     599                 : 
     600               3 : void GRIBDataset::SetGribMetaData(grib_MetaData* meta)
     601                 : {
     602               3 :     nRasterXSize = meta->gds.Nx;
     603               3 :     nRasterYSize = meta->gds.Ny;
     604                 : 
     605                 : /* -------------------------------------------------------------------- */
     606                 : /*      Image projection.                                               */
     607                 : /* -------------------------------------------------------------------- */
     608               3 :     OGRSpatialReference oSRS;
     609                 : 
     610               3 :     switch(meta->gds.projType)
     611                 :     {
     612                 :       case GS3_LATLON:
     613                 :       case GS3_GAUSSIAN_LATLON:
     614                 :           // No projection, only latlon system (geographic)
     615               2 :           break;
     616                 :       case GS3_MERCATOR:
     617                 :         oSRS.SetMercator(meta->gds.meshLat, meta->gds.orientLon,
     618               1 :                          1.0, 0.0, 0.0);
     619               1 :         break;
     620                 :       case GS3_POLAR:
     621                 :         oSRS.SetPS(meta->gds.meshLat, meta->gds.orientLon,
     622                 :                    meta->gds.scaleLat1,
     623               0 :                    0.0, 0.0);
     624               0 :         break;
     625                 :       case GS3_LAMBERT:
     626                 :         oSRS.SetLCC(meta->gds.scaleLat1, meta->gds.scaleLat2,
     627                 :                     0.0, meta->gds.orientLon,
     628               0 :                     0.0, 0.0); // set projection
     629               0 :         break;
     630                 :       
     631                 : 
     632                 :       case GS3_ORTHOGRAPHIC:
     633                 : 
     634                 :         //oSRS.SetOrthographic(0.0, meta->gds.orientLon,
     635                 :         //                      meta->gds.lon2, meta->gds.lat2);
     636                 :         //oSRS.SetGEOS(meta->gds.orientLon, meta->gds.stretchFactor, meta->gds.lon2, meta->gds.lat2);
     637               0 :         oSRS.SetGEOS(  0, 35785831, 0, 0 ); // hardcoded for now, I don't know yet how to parse the meta->gds section
     638                 :         break;
     639                 :       case GS3_EQUATOR_EQUIDIST:
     640                 :         break;
     641                 :       case GS3_AZIMUTH_RANGE:
     642                 :         break;
     643                 :     }
     644                 : 
     645                 : /* -------------------------------------------------------------------- */
     646                 : /*      Earth model                                                     */
     647                 : /* -------------------------------------------------------------------- */
     648               3 :     double a = meta->gds.majEarth * 1000.0; // in meters
     649               3 :     double b = meta->gds.minEarth * 1000.0;
     650               3 :     if( a == 0 && b == 0 )
     651                 :     {
     652               0 :         a = 6377563.396;
     653               0 :         b = 6356256.910;
     654                 :     }
     655                 : 
     656               3 :     if (meta->gds.f_sphere)
     657                 :     {
     658                 :         oSRS.SetGeogCS( "Coordinate System imported from GRIB file",
     659                 :                         NULL,
     660                 :                         "Sphere",
     661               3 :                         a, 0.0 );
     662                 :     }
     663                 :     else
     664                 :     {
     665               0 :         double fInv = a/(a-b);
     666                 :         oSRS.SetGeogCS( "Coordinate System imported from GRIB file",
     667                 :                         NULL,
     668                 :                         "Spheroid imported from GRIB file",
     669               0 :                         a, fInv );
     670                 :     }
     671                 : 
     672               3 :     OGRSpatialReference oLL; // construct the "geographic" part of oSRS
     673               3 :     oLL.CopyGeogCSFrom( &oSRS );
     674                 : 
     675                 :     double rMinX;
     676                 :     double rMaxY;
     677                 :     double rPixelSizeX;
     678                 :     double rPixelSizeY;
     679               3 :     if (meta->gds.projType == GS3_ORTHOGRAPHIC)
     680                 :     {
     681                 :         //rMinX = -meta->gds.Dx * (meta->gds.Nx / 2); // This is what should work, but it doesn't .. Dx seems to have an inverse relation with pixel size
     682                 :         //rMaxY = meta->gds.Dy * (meta->gds.Ny / 2);
     683               0 :         const double geosExtentInMeters = 11137496.552; // hardcoded for now, assumption: GEOS projection, full disc (like MSG)
     684               0 :         rMinX = -(geosExtentInMeters / 2);
     685               0 :         rMaxY = geosExtentInMeters / 2;
     686               0 :         rPixelSizeX = geosExtentInMeters / meta->gds.Nx;
     687               0 :         rPixelSizeY = geosExtentInMeters / meta->gds.Ny;
     688                 :     }
     689               3 :     else if( oSRS.IsProjected() )
     690                 :     {
     691               1 :         rMinX = meta->gds.lon1; // longitude in degrees, to be transformed to meters (or degrees in case of latlon)
     692               1 :         rMaxY = meta->gds.lat1; // latitude in degrees, to be transformed to meters 
     693               1 :         OGRCoordinateTransformation *poTransformLLtoSRS = OGRCreateCoordinateTransformation( &(oLL), &(oSRS) );
     694               1 :         if ((poTransformLLtoSRS != NULL) && poTransformLLtoSRS->Transform( 1, &rMinX, &rMaxY )) // transform it to meters
     695                 :         {
     696               1 :             if (meta->gds.scan == GRIB2BIT_2) // Y is minY, GDAL wants maxY
     697               1 :                 rMaxY += (meta->gds.Ny - 1) * meta->gds.Dy; // -1 because we GDAL needs the coordinates of the centre of the pixel
     698               1 :             rPixelSizeX = meta->gds.Dx;
     699               1 :             rPixelSizeY = meta->gds.Dy;
     700                 :         }
     701                 :         else
     702                 :         {
     703               0 :             rMinX = 0.0;
     704               0 :             rMaxY = 0.0;
     705                 :             
     706               0 :             rPixelSizeX = 1.0;
     707               0 :             rPixelSizeY = -1.0;
     708                 :             
     709               0 :             oSRS.Clear();
     710                 : 
     711                 :             CPLError( CE_Warning, CPLE_AppDefined,
     712                 :                       "Unable to perform coordinate transformations, so the correct\n"
     713                 :                       "projected geotransform could not be deduced from the lat/long\n"
     714               0 :                       "control points.  Defaulting to ungeoreferenced." );
     715                 :         }
     716               1 :         delete poTransformLLtoSRS;
     717                 :     }
     718                 :     else
     719                 :     {
     720               2 :         rMinX = meta->gds.lon1; // longitude in degrees, to be transformed to meters (or degrees in case of latlon)
     721               2 :         rMaxY = meta->gds.lat1; // latitude in degrees, to be transformed to meters 
     722                 : 
     723               2 :         double rMinY = meta->gds.lat2;
     724               2 :         if (meta->gds.lat2 > rMaxY)
     725                 :         {
     726               2 :           rMaxY = meta->gds.lat2;
     727               2 :           rMinY = meta->gds.lat1;
     728                 :         }
     729                 : 
     730               2 :         if (meta->gds.lon1 > meta->gds.lon2)
     731               0 :           rPixelSizeX = (360.0 - (meta->gds.lon1 - meta->gds.lon2)) / (meta->gds.Nx - 1);
     732                 :         else
     733               2 :           rPixelSizeX = (meta->gds.lon2 - meta->gds.lon1) / (meta->gds.Nx - 1);
     734                 : 
     735               2 :         rPixelSizeY = (rMaxY - rMinY) / (meta->gds.Ny - 1);
     736                 : 
     737                 :         // Do some sanity checks for cases that can't be handled by the above
     738                 :         // pixel size corrections. GRIB1 has a minimum precision of 0.001
     739                 :         // for latitudes and longitudes, so we'll allow a bit higher than that.
     740               2 :         if (rPixelSizeX < 0 || fabs(rPixelSizeX - meta->gds.Dx) > 0.002)
     741               0 :           rPixelSizeX = meta->gds.Dx;
     742                 : 
     743               2 :         if (rPixelSizeY < 0 || fabs(rPixelSizeY - meta->gds.Dy) > 0.002)
     744               0 :           rPixelSizeY = meta->gds.Dy;
     745                 :     }
     746                 : 
     747                 :     // http://gdal.org/gdal_datamodel.html :
     748                 :     //   we need the top left corner of the top left pixel.
     749                 :     //   At the moment we have the center of the pixel.
     750               3 :     rMinX-=rPixelSizeX/2;
     751               3 :     rMaxY+=rPixelSizeY/2;
     752                 : 
     753               3 :     adfGeoTransform[0] = rMinX;
     754               3 :     adfGeoTransform[3] = rMaxY;
     755               3 :     adfGeoTransform[1] = rPixelSizeX;
     756               3 :     adfGeoTransform[5] = -rPixelSizeY;
     757                 : 
     758               3 :     CPLFree( pszProjection );
     759               3 :     pszProjection = NULL;
     760               3 :     oSRS.exportToWkt( &(pszProjection) );
     761               3 : }
     762                 : 
     763                 : /************************************************************************/
     764                 : /*                         GDALRegister_GRIB()                          */
     765                 : /************************************************************************/
     766                 : 
     767             558 : void GDALRegister_GRIB()
     768                 : 
     769                 : {
     770                 :     GDALDriver  *poDriver;
     771                 : 
     772             558 :     if( GDALGetDriverByName( "GRIB" ) == NULL )
     773                 :     {
     774             537 :         poDriver = new GDALDriver();
     775                 :         
     776             537 :         poDriver->SetDescription( "GRIB" );
     777                 :         poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, 
     778             537 :                                    "GRIdded Binary (.grb)" );
     779                 :         poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC, 
     780             537 :                                    "frmt_grib.html" );
     781             537 :         poDriver->SetMetadataItem( GDAL_DMD_EXTENSION, "grb" );
     782             537 :         poDriver->SetMetadataItem( GDAL_DCAP_VIRTUALIO, "YES" );
     783                 : 
     784             537 :         poDriver->pfnOpen = GRIBDataset::Open;
     785             537 :         poDriver->pfnIdentify = GRIBDataset::Identify;
     786                 : 
     787             537 :         GetGDALDriverManager()->RegisterDriver( poDriver );
     788                 :     }
     789             558 : }

Generated by: LCOV version 1.7