LCOV - code coverage report
Current view: directory - frmts/grib - gribdataset.cpp (source / functions) Found Hit Coverage
Test: gdal_filtered.info Lines: 330 233 70.6 %
Date: 2013-03-30 Functions: 24 15 62.5 %

       1                 : /******************************************************************************
       2                 :  * $Id: gribdataset.cpp 25627 2013-02-10 10:17:19Z rouault $
       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 25627 2013-02-10 10:17:19Z rouault $");
      44                 : 
      45                 : CPL_C_START
      46                 : void  GDALRegister_GRIB(void);
      47                 : CPL_C_END
      48                 : 
      49                 : static void *hGRIBMutex = NULL;
      50                 : 
      51                 : /************************************************************************/
      52                 : /* ==================================================================== */
      53                 : /*        GRIBDataset       */
      54                 : /* ==================================================================== */
      55                 : /************************************************************************/
      56                 : 
      57                 : class GRIBRasterBand;
      58                 : 
      59                 : class GRIBDataset : public GDALPamDataset
      60                 : {
      61                 :     friend class GRIBRasterBand;
      62                 : 
      63                 :   public:
      64                 :     GRIBDataset();
      65                 :     ~GRIBDataset();
      66                 :     
      67                 :     static GDALDataset *Open( GDALOpenInfo * );
      68                 :     static int          Identify( GDALOpenInfo * );
      69                 : 
      70                 :     CPLErr  GetGeoTransform( double * padfTransform );
      71                 :     const char *GetProjectionRef();
      72                 :     
      73                 :   private:
      74                 :     void SetGribMetaData(grib_MetaData* meta);
      75                 :     VSILFILE  *fp;
      76                 :     char  *pszProjection;
      77                 :     OGRCoordinateTransformation *poTransform;
      78                 :     double adfGeoTransform[6]; // Calculate and store once as GetGeoTransform may be called multiple times
      79                 : 
      80                 :     GIntBig  nCachedBytes;
      81                 :     GIntBig  nCachedBytesThreshold;
      82                 :     int      bCacheOnlyOneBand;
      83                 :     GRIBRasterBand* poLastUsedBand;
      84                 : };
      85                 : 
      86                 : /************************************************************************/
      87                 : /* ==================================================================== */
      88                 : /*                            GRIBRasterBand                             */
      89                 : /* ==================================================================== */
      90                 : /************************************************************************/
      91                 : 
      92                 : class GRIBRasterBand : public GDALPamRasterBand
      93                 : {
      94                 :     friend class GRIBDataset;
      95                 :     
      96                 : public:
      97                 :     GRIBRasterBand( GRIBDataset*, int, inventoryType* );
      98                 :     virtual ~GRIBRasterBand();
      99                 :     virtual CPLErr IReadBlock( int, int, void * );
     100                 :     virtual const char *GetDescription() const;
     101                 : 
     102                 :     virtual double GetNoDataValue( int *pbSuccess = NULL );
     103                 : 
     104                 :     void    FindPDSTemplate();
     105                 : 
     106                 :     void    UncacheData();
     107                 : 
     108                 : private:
     109                 : 
     110                 :     CPLErr       LoadData();
     111                 : 
     112                 :     static void ReadGribData( DataSource &, sInt4, int, double**, grib_MetaData**);
     113                 :     sInt4 start;
     114                 :     int subgNum;
     115                 :     char *longFstLevel;
     116                 : 
     117                 :     double * m_Grib_Data;
     118                 :     grib_MetaData* m_Grib_MetaData;
     119                 : 
     120                 :     int      nGribDataXSize;
     121                 :     int      nGribDataYSize;
     122                 : };
     123                 : 
     124                 : 
     125                 : /************************************************************************/
     126                 : /*                           GRIBRasterBand()                            */
     127                 : /************************************************************************/
     128                 : 
     129              20 : GRIBRasterBand::GRIBRasterBand( GRIBDataset *poDS, int nBand, 
     130                 :                                 inventoryType *psInv )
     131              20 :   : m_Grib_Data(NULL), m_Grib_MetaData(NULL)
     132                 : {
     133              20 :     this->poDS = poDS;
     134              20 :     this->nBand = nBand;
     135              20 :     this->start = psInv->start;
     136              20 :     this->subgNum = psInv->subgNum;
     137              20 :     this->longFstLevel = CPLStrdup(psInv->longFstLevel);
     138                 : 
     139              20 :     eDataType = GDT_Float64; // let user do -ot Float32 if needed for saving space, GRIB contains Float64 (though not fully utilized most of the time)
     140                 : 
     141              20 :     nBlockXSize = poDS->nRasterXSize;
     142              20 :     nBlockYSize = 1;
     143                 : 
     144              20 :     nGribDataXSize = poDS->nRasterXSize;
     145              20 :     nGribDataYSize = poDS->nRasterYSize;
     146                 : 
     147              20 :     SetMetadataItem( "GRIB_UNIT", psInv->unitName );
     148              20 :     SetMetadataItem( "GRIB_COMMENT", psInv->comment );
     149              20 :     SetMetadataItem( "GRIB_ELEMENT", psInv->element );
     150              20 :     SetMetadataItem( "GRIB_SHORT_NAME", psInv->shortFstLevel );
     151                 :     SetMetadataItem( "GRIB_REF_TIME", 
     152              20 :                      CPLString().Printf("%12.0f sec UTC", psInv->refTime ) );
     153                 :     SetMetadataItem( "GRIB_VALID_TIME", 
     154              20 :                      CPLString().Printf("%12.0f sec UTC", psInv->validTime ) );
     155                 :     SetMetadataItem( "GRIB_FORECAST_SECONDS", 
     156              20 :                      CPLString().Printf("%.0f sec", psInv->foreSec ) );
     157              20 : }
     158                 : 
     159                 : /************************************************************************/
     160                 : /*                          FindPDSTemplate()                           */
     161                 : /*                                                                      */
     162                 : /*      Scan the file for the PDS template info and represent it as     */
     163                 : /*      metadata.                                                       */
     164                 : /************************************************************************/
     165                 : 
     166               2 : void GRIBRasterBand::FindPDSTemplate()
     167                 : 
     168                 : {
     169               2 :     GRIBDataset *poGDS = (GRIBDataset *) poDS;
     170                 : 
     171                 : /* -------------------------------------------------------------------- */
     172                 : /*      Collect section 4 octet information ... we read the file        */
     173                 : /*      ourselves since the GRIB API does not appear to preserve all    */
     174                 : /*      this for us.                                                    */
     175                 : /* -------------------------------------------------------------------- */
     176               2 :     GIntBig nOffset = VSIFTellL( poGDS->fp );
     177                 :     GByte abyHead[5];
     178                 :     GUInt32 nSectSize;
     179                 : 
     180               2 :     VSIFSeekL( poGDS->fp, start+16, SEEK_SET );
     181               2 :     VSIFReadL( abyHead, 5, 1, poGDS->fp );
     182                 : 
     183               2 :     while( abyHead[4] != 4 )
     184                 :     {
     185               2 :         memcpy( &nSectSize, abyHead, 4 );
     186               2 :         CPL_MSBPTR32( &nSectSize );
     187                 : 
     188               2 :         if( VSIFSeekL( poGDS->fp, nSectSize-5, SEEK_CUR ) != 0
     189                 :             || VSIFReadL( abyHead, 5, 1, poGDS->fp ) != 1 )
     190               2 :             break;
     191                 :     }
     192                 :         
     193               2 :     if( abyHead[4] == 4 )
     194                 :     {
     195                 :         GUInt16 nCoordCount;
     196                 :         GUInt16 nPDTN;
     197               0 :         CPLString osOctet;
     198                 :         int i;
     199                 :         GByte *pabyBody;
     200                 : 
     201               0 :         memcpy( &nSectSize, abyHead, 4 );
     202               0 :         CPL_MSBPTR32( &nSectSize );
     203                 : 
     204               0 :         pabyBody = (GByte *) CPLMalloc(nSectSize-5);
     205               0 :         VSIFReadL( pabyBody, 1, nSectSize-5, poGDS->fp );
     206                 : 
     207               0 :         memcpy( &nCoordCount, pabyBody + 5 - 5, 2 );
     208               0 :         CPL_MSBPTR16( &nCoordCount );
     209                 : 
     210               0 :         memcpy( &nPDTN, pabyBody + 7 - 5, 2 );
     211               0 :         CPL_MSBPTR16( &nPDTN );
     212                 : 
     213                 :         SetMetadataItem( "GRIB_PDS_PDTN",
     214               0 :                          CPLString().Printf( "%d", nPDTN ) );
     215                 : 
     216               0 :         for( i = 9; i < (int) nSectSize; i++ )
     217                 :         {
     218                 :             char szByte[10];
     219                 : 
     220               0 :             if( i == 9 )
     221               0 :                 sprintf( szByte, "%d", pabyBody[i-5] );
     222                 :             else
     223               0 :                 sprintf( szByte, " %d", pabyBody[i-5] );
     224               0 :             osOctet += szByte;
     225                 :         }
     226                 :         
     227               0 :         SetMetadataItem( "GRIB_PDS_TEMPLATE_NUMBERS", osOctet );
     228                 : 
     229               0 :         CPLFree( pabyBody );
     230                 :     }
     231                 : 
     232               2 :     VSIFSeekL( poGDS->fp, nOffset, SEEK_SET );
     233               2 : }
     234                 : 
     235                 : /************************************************************************/
     236                 : /*                         GetDescription()                             */
     237                 : /************************************************************************/
     238                 : 
     239               0 : const char * GRIBRasterBand::GetDescription() const
     240                 : {
     241               0 :     if( longFstLevel == NULL )
     242               0 :         return GDALPamRasterBand::GetDescription();
     243                 :     else
     244               0 :         return longFstLevel;
     245                 : }
     246                 :  
     247                 : /************************************************************************/
     248                 : /*                             LoadData()                               */
     249                 : /************************************************************************/
     250                 : 
     251             282 : CPLErr GRIBRasterBand::LoadData()
     252                 : 
     253                 : {
     254             282 :     if( !m_Grib_Data )
     255                 :     {
     256               3 :         GRIBDataset *poGDS = (GRIBDataset *) poDS;
     257                 : 
     258               3 :         if (poGDS->bCacheOnlyOneBand)
     259                 :         {
     260                 :             /* In "one-band-at-a-time" strategy, if the last recently used */
     261                 :             /* band is not that one, uncache it. We could use a smarter strategy */
     262                 :             /* based on a LRU, but that's a bit overkill for now. */
     263               0 :             poGDS->poLastUsedBand->UncacheData();
     264               0 :             poGDS->nCachedBytes = 0;
     265                 :         }
     266                 :         else
     267                 :         {
     268                 :             /* Once we have cached more than nCachedBytesThreshold bytes, we will switch */
     269                 :             /* to "one-band-at-a-time" strategy, instead of caching all bands that have */
     270                 :             /* been accessed */
     271               3 :             if (poGDS->nCachedBytes > poGDS->nCachedBytesThreshold)
     272                 :             {
     273                 :                 CPLDebug("GRIB", "Maximum band cache size reached for this dataset. "
     274               0 :                          "Caching only one band at a time from now");
     275               0 :                 for(int i=0;i<poGDS->nBands;i++)
     276                 :                 {
     277               0 :                     ((GRIBRasterBand*) poGDS->GetRasterBand(i+1))->UncacheData();
     278                 :                 }
     279               0 :                 poGDS->nCachedBytes = 0;
     280               0 :                 poGDS->bCacheOnlyOneBand = TRUE;
     281                 :             }
     282                 :         }
     283                 : 
     284               3 :         FileDataSource grib_fp (poGDS->fp);
     285                 : 
     286                 :         // we don't seem to have any way to detect errors in this!
     287               3 :         ReadGribData(grib_fp, start, subgNum, &m_Grib_Data, &m_Grib_MetaData);
     288               3 :         if( !m_Grib_Data )
     289                 :         {
     290               0 :             CPLError( CE_Failure, CPLE_AppDefined, "Out of memory." );
     291               0 :             return CE_Failure;
     292                 :         }
     293                 : 
     294                 : /* -------------------------------------------------------------------- */
     295                 : /*      Check that this band matches the dataset as a whole, size       */
     296                 : /*      wise. (#3246)                                                   */
     297                 : /* -------------------------------------------------------------------- */
     298               3 :         nGribDataXSize = m_Grib_MetaData->gds.Nx;
     299               3 :         nGribDataYSize = m_Grib_MetaData->gds.Ny;
     300                 : 
     301               3 :         poGDS->nCachedBytes += nGribDataXSize * nGribDataYSize * sizeof(double);
     302               3 :         poGDS->poLastUsedBand = this;
     303                 : 
     304               3 :         if( nGribDataXSize != nRasterXSize 
     305                 :             || nGribDataYSize != nRasterYSize )
     306                 :         {
     307                 :             CPLError( CE_Warning, CPLE_AppDefined,
     308                 :                       "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.", 
     309                 :                       nBand, 
     310                 :                       nGribDataXSize, nGribDataYSize, 
     311                 :                       nRasterXSize, nRasterYSize, 
     312               1 :                       nBand );
     313               0 :         }
     314                 :     }
     315                 : 
     316             282 :     return CE_None;
     317                 : }
     318                 : 
     319                 : /************************************************************************/
     320                 : /*                             IReadBlock()                             */
     321                 : /************************************************************************/
     322                 : 
     323             281 : CPLErr GRIBRasterBand::IReadBlock( int nBlockXOff, int nBlockYOff,
     324                 :                                    void * pImage )
     325                 : 
     326                 : {
     327             281 :     CPLErr eErr = LoadData();
     328             281 :     if (eErr != CE_None)
     329               0 :         return eErr;
     330                 : 
     331                 : /* -------------------------------------------------------------------- */
     332                 : /*      The image as read is always upside down to our normal           */
     333                 : /*      orientation so we need to effectively flip it at this           */
     334                 : /*      point.  We also need to deal with bands that are a different    */
     335                 : /*      size than the dataset as a whole.                               */
     336                 : /* -------------------------------------------------------------------- */
     337             281 :     if( nGribDataXSize == nRasterXSize
     338                 :         && nGribDataYSize == nRasterYSize )
     339                 :     {
     340                 :         // Simple 1:1 case.
     341                 :         memcpy(pImage, 
     342                 :                m_Grib_Data + nRasterXSize * (nRasterYSize - nBlockYOff - 1), 
     343             203 :                nRasterXSize * sizeof(double));
     344                 :         
     345             203 :         return CE_None;
     346                 :     }
     347                 :     else
     348                 :     {
     349              78 :         memset( pImage, 0, sizeof(double)*nRasterXSize );
     350                 : 
     351              78 :         if( nBlockYOff >= nGribDataYSize ) // off image?
     352              57 :             return CE_None;
     353                 : 
     354              21 :         int nCopyWords = MIN(nRasterXSize,nGribDataXSize);
     355                 : 
     356                 :         memcpy( pImage, 
     357                 :                 m_Grib_Data + nGribDataXSize*(nGribDataYSize-nBlockYOff-1),
     358              21 :                 nCopyWords * sizeof(double) );
     359                 :         
     360              21 :         return CE_None;
     361                 :     }
     362                 : }
     363                 : 
     364                 : /************************************************************************/
     365                 : /*                           GetNoDataValue()                           */
     366                 : /************************************************************************/
     367                 : 
     368               1 : double GRIBRasterBand::GetNoDataValue( int *pbSuccess )
     369                 : {
     370               1 :     CPLErr eErr = LoadData();
     371               1 :     if (eErr != CE_None ||
     372                 :         m_Grib_MetaData == NULL ||
     373                 :         m_Grib_MetaData->gridAttrib.f_miss == 0)
     374                 :     {
     375               0 :         if (pbSuccess)
     376               0 :             *pbSuccess = FALSE;
     377               0 :         return 0;
     378                 :     }
     379                 : 
     380               1 :     if (m_Grib_MetaData->gridAttrib.f_miss == 2)
     381                 :     {
     382                 :         /* what TODO ? */
     383                 :         CPLDebug("GRIB", "Secondary missing value also set for band %d : %f",
     384               0 :                  nBand, m_Grib_MetaData->gridAttrib.missSec);
     385                 :     }
     386                 : 
     387               1 :     if (pbSuccess)
     388               1 :         *pbSuccess = TRUE;
     389               1 :     return m_Grib_MetaData->gridAttrib.missPri;
     390                 : }
     391                 : 
     392                 : /************************************************************************/
     393                 : /*                            ReadGribData()                            */
     394                 : /************************************************************************/
     395                 : 
     396               7 : void GRIBRasterBand::ReadGribData( DataSource & fp, sInt4 start, int subgNum, double** data, grib_MetaData** metaData)
     397                 : {
     398                 :     /* Initialisation, for calling the ReadGrib2Record function */
     399               7 :     sInt4 f_endMsg = 1;  /* 1 if we read the last grid in a GRIB message, or we haven't read any messages. */
     400                 :     // int subgNum = 0;     /* The subgrid in the message that we are interested in. */
     401               7 :     sChar f_unit = 2;        /* None = 0, English = 1, Metric = 2 */
     402               7 :     double majEarth = 0;     /* -radEarth if < 6000 ignore, otherwise use this to
     403                 :                               * override the radEarth in the GRIB1 or GRIB2
     404                 :                               * message.  Needed because NCEP uses 6371.2 but GRIB1 could only state 6367.47. */
     405               7 :     double minEarth = 0;     /* -minEarth if < 6000 ignore, otherwise use this to
     406                 :                               * override the minEarth in the GRIB1 or GRIB2 message. */
     407               7 :     sChar f_SimpleVer = 4;   /* Which version of the simple NDFD Weather table to
     408                 :                               * use. (1 is 6/2003) (2 is 1/2004) (3 is 2/2004)
     409                 :                               * (4 is 11/2004) (default 4) */
     410                 :     LatLon lwlf;         /* lower left corner (cookie slicing) -lwlf */
     411                 :     LatLon uprt;         /* upper right corner (cookie slicing) -uprt */
     412                 :     IS_dataType is;      /* Un-parsed meta data for this GRIB2 message. As well as some memory used by the unpacker. */
     413                 : 
     414               7 :     lwlf.lat = -100; // lat == -100 instructs the GRIB decoder that we don't want a subgrid
     415                 : 
     416               7 :     IS_Init (&is);
     417                 : 
     418               7 :     const char* pszGribNormalizeUnits = CPLGetConfigOption("GRIB_NORMALIZE_UNITS", NULL);
     419               7 :     if ( pszGribNormalizeUnits != NULL && ( STRCASECMP(pszGribNormalizeUnits,"NO")==0 ) )
     420               0 :         f_unit = 0; /* do not normalize units to metric */
     421                 : 
     422                 :     /* Read GRIB message from file position "start". */
     423               7 :     fp.DataSourceFseek(start, SEEK_SET);
     424               7 :     uInt4 grib_DataLen = 0;  /* Size of Grib_Data. */
     425               7 :     *metaData = new grib_MetaData();
     426               7 :     MetaInit (*metaData);
     427                 :     ReadGrib2Record (fp, f_unit, data, &grib_DataLen, *metaData, &is, subgNum,
     428               7 :                      majEarth, minEarth, f_SimpleVer, &f_endMsg, &lwlf, &uprt);
     429                 : 
     430               7 :     char * errMsg = errSprintf(NULL); // no intention to show errors, just swallow it and free the memory
     431               7 :     if( errMsg != NULL )
     432               0 :         CPLDebug( "GRIB", "%s", errMsg );
     433               7 :     free(errMsg);
     434               7 :     IS_Free(&is);
     435               7 : }
     436                 : 
     437                 : /************************************************************************/
     438                 : /*                            UncacheData()                             */
     439                 : /************************************************************************/
     440                 : 
     441              20 : void GRIBRasterBand::UncacheData()
     442                 : {
     443              20 :     if (m_Grib_Data)
     444               7 :         free (m_Grib_Data);
     445              20 :     m_Grib_Data = NULL;
     446              20 :     if (m_Grib_MetaData)
     447                 :     {
     448               7 :         MetaFree( m_Grib_MetaData );
     449               7 :         delete m_Grib_MetaData;
     450                 :     }
     451              20 :     m_Grib_MetaData = NULL;
     452              20 : }
     453                 : 
     454                 : /************************************************************************/
     455                 : /*                           ~GRIBRasterBand()                          */
     456                 : /************************************************************************/
     457                 : 
     458              20 : GRIBRasterBand::~GRIBRasterBand()
     459                 : {
     460              20 :     CPLFree(longFstLevel);
     461              20 :     UncacheData();
     462              20 : }
     463                 : 
     464                 : /************************************************************************/
     465                 : /* ==================================================================== */
     466                 : /*        GRIBDataset       */
     467                 : /* ==================================================================== */
     468                 : /************************************************************************/
     469                 : 
     470               4 : GRIBDataset::GRIBDataset()
     471                 : 
     472                 : {
     473               4 :   poTransform = NULL;
     474               4 :   pszProjection = CPLStrdup("");
     475               4 :   adfGeoTransform[0] = 0.0;
     476               4 :   adfGeoTransform[1] = 1.0;
     477               4 :   adfGeoTransform[2] = 0.0;
     478               4 :   adfGeoTransform[3] = 0.0;
     479               4 :   adfGeoTransform[4] = 0.0;
     480               4 :   adfGeoTransform[5] = 1.0;
     481                 : 
     482               4 :   nCachedBytes = 0;
     483                 :   /* Switch caching strategy once 100 MB threshold is reached */
     484                 :   /* Why 100 MB ? --> why not ! */
     485               4 :   nCachedBytesThreshold = ((GIntBig)atoi(CPLGetConfigOption("GRIB_CACHEMAX", "100"))) * 1024 * 1024;
     486               4 :   bCacheOnlyOneBand = FALSE;
     487               4 :   poLastUsedBand = NULL;
     488               4 : }
     489                 : 
     490                 : /************************************************************************/
     491                 : /*                            ~GRIBDataset()                             */
     492                 : /************************************************************************/
     493                 : 
     494               4 : GRIBDataset::~GRIBDataset()
     495                 : 
     496                 : {
     497               4 :     FlushCache();
     498               4 :     if( fp != NULL )
     499               4 :         VSIFCloseL( fp );
     500                 :     
     501               4 :     CPLFree( pszProjection );
     502               4 : }
     503                 : 
     504                 : /************************************************************************/
     505                 : /*                          GetGeoTransform()                           */
     506                 : /************************************************************************/
     507                 : 
     508               0 : CPLErr GRIBDataset::GetGeoTransform( double * padfTransform )
     509                 : 
     510                 : {
     511               0 :     memcpy( padfTransform,  adfGeoTransform, sizeof(double) * 6 );
     512               0 :     return CE_None;
     513                 : }
     514                 : 
     515                 : /************************************************************************/
     516                 : /*                          GetProjectionRef()                          */
     517                 : /************************************************************************/
     518                 : 
     519               0 : const char *GRIBDataset::GetProjectionRef()
     520                 : 
     521                 : {
     522               0 :     return pszProjection;
     523                 : }
     524                 : 
     525                 : /************************************************************************/
     526                 : /*                            Identify()                                */
     527                 : /************************************************************************/
     528                 : 
     529           12680 : int GRIBDataset::Identify( GDALOpenInfo * poOpenInfo )
     530                 : {
     531           12680 :     if (poOpenInfo->nHeaderBytes < 8)
     532           11582 :         return FALSE;
     533                 :         
     534                 : /* -------------------------------------------------------------------- */
     535                 : /*      Does a part of what ReadSECT0() but in a thread-safe way.       */
     536                 : /* -------------------------------------------------------------------- */
     537                 :     int i;
     538          876159 :     for(i=0;i<poOpenInfo->nHeaderBytes-3;i++)
     539                 :     {
     540          875065 :         if (EQUALN((const char*)poOpenInfo->pabyHeader + i, "GRIB", 4) ||
     541                 :             EQUALN((const char*)poOpenInfo->pabyHeader + i, "TDLP", 4))
     542               4 :             return TRUE;
     543                 :     }
     544                 :     
     545            1094 :     return FALSE;
     546                 : }
     547                 : 
     548                 : /************************************************************************/
     549                 : /*                                Open()                                */
     550                 : /************************************************************************/
     551                 : 
     552            2549 : GDALDataset *GRIBDataset::Open( GDALOpenInfo * poOpenInfo )
     553                 : 
     554                 : {
     555            2549 :     if( !Identify(poOpenInfo) )
     556            2545 :         return NULL;
     557                 :         
     558                 : /* -------------------------------------------------------------------- */
     559                 : /*      A fast "probe" on the header that is partially read in memory.  */
     560                 : /* -------------------------------------------------------------------- */
     561               4 :     char *buff = NULL;
     562               4 :     uInt4 buffLen = 0;
     563                 :     sInt4 sect0[SECT0LEN_WORD];
     564                 :     uInt4 gribLen;
     565                 :     int version;
     566                 : // grib is not thread safe, make sure not to cause problems
     567                 : // for other thread safe formats
     568                 : 
     569               4 :     CPLMutexHolderD(&hGRIBMutex);
     570               4 :     MemoryDataSource mds (poOpenInfo->pabyHeader, poOpenInfo->nHeaderBytes);
     571               4 :     if (ReadSECT0 (mds, &buff, &buffLen, -1, sect0, &gribLen, &version) < 0) {
     572               0 :         free (buff);
     573               0 :         char * errMsg = errSprintf(NULL);
     574               0 :         if( errMsg != NULL && strstr(errMsg,"Ran out of file") == NULL )
     575               0 :             CPLDebug( "GRIB", "%s", errMsg );
     576               0 :         free(errMsg);
     577               0 :         return NULL;
     578                 :     }
     579               4 :     free(buff);
     580                 :     
     581                 : /* -------------------------------------------------------------------- */
     582                 : /*      Confirm the requested access is supported.                      */
     583                 : /* -------------------------------------------------------------------- */
     584               4 :     if( poOpenInfo->eAccess == GA_Update )
     585                 :     {
     586                 :         CPLError( CE_Failure, CPLE_NotSupported, 
     587                 :                   "The GRIB driver does not support update access to existing"
     588               0 :                   " datasets.\n" );
     589               0 :         return NULL;
     590                 :     }
     591                 : /* -------------------------------------------------------------------- */
     592                 : /*      Create a corresponding GDALDataset.                             */
     593                 : /* -------------------------------------------------------------------- */
     594                 :     GRIBDataset   *poDS;
     595                 : 
     596               4 :     poDS = new GRIBDataset();
     597                 : 
     598               8 :     poDS->fp = VSIFOpenL( poOpenInfo->pszFilename, "r" );
     599                 : 
     600                 :   /* Check the return values */    
     601               4 :   if (!poDS->fp) {
     602                 :         // we have no FP, so we don't have anywhere to read from
     603               0 :         char * errMsg = errSprintf(NULL);
     604               0 :         if( errMsg != NULL )
     605               0 :             CPLDebug( "GRIB", "%s", errMsg );
     606               0 :         free(errMsg);
     607                 :     
     608               0 :     CPLError( CE_Failure, CPLE_OpenFailed, "Error (%d) opening file %s", errno, poOpenInfo->pszFilename);
     609               0 :         CPLReleaseMutex(hGRIBMutex); // Release hGRIBMutex otherwise we'll deadlock with GDALDataset own hGRIBMutex
     610               0 :         delete poDS;
     611               0 :         CPLAcquireMutex(hGRIBMutex, 1000.0);
     612               0 :         return NULL;
     613                 :   }
     614                 :     
     615                 : /* -------------------------------------------------------------------- */
     616                 : /*      Read the header.                                                */
     617                 : /* -------------------------------------------------------------------- */
     618                 : 
     619                 : /* -------------------------------------------------------------------- */
     620                 : /*      Make an inventory of the GRIB file.                             */
     621                 : /* The inventory does not contain all the information needed for        */
     622                 : /* creating the RasterBands (especially the x and y size), therefore    */
     623                 : /* the first GRIB band is also read for some additional metadata.       */
     624                 : /* The band-data that is read is stored into the first RasterBand,      */
     625                 : /* simply so that the same portion of the file is not read twice.       */
     626                 : /* -------------------------------------------------------------------- */
     627                 :     
     628               4 :     VSIFSeekL( poDS->fp, 0, SEEK_SET );
     629                 : 
     630               4 :     FileDataSource grib_fp (poDS->fp);
     631                 : 
     632               4 :     inventoryType *Inv = NULL;  /* Contains an GRIB2 message inventory of the file */
     633               4 :     uInt4 LenInv = 0;        /* size of Inv (also # of GRIB2 messages) */
     634               4 :     int msgNum =0;          /* The messageNumber during the inventory. */
     635                 : 
     636               4 :     if (GRIB2Inventory (grib_fp, &Inv, &LenInv, 0, &msgNum) <= 0 )
     637                 :     {
     638               0 :         char * errMsg = errSprintf(NULL);
     639               0 :         if( errMsg != NULL )
     640               0 :             CPLDebug( "GRIB", "%s", errMsg );
     641               0 :         free(errMsg);
     642                 : 
     643                 :         CPLError( CE_Failure, CPLE_OpenFailed, 
     644                 :                   "%s is a grib file, but no raster dataset was successfully identified.",
     645               0 :                   poOpenInfo->pszFilename );
     646               0 :         CPLReleaseMutex(hGRIBMutex); // Release hGRIBMutex otherwise we'll deadlock with GDALDataset own hGRIBMutex
     647               0 :         delete poDS;
     648               0 :         CPLAcquireMutex(hGRIBMutex, 1000.0);
     649               0 :         return NULL;
     650                 :     }
     651                 : 
     652                 : /* -------------------------------------------------------------------- */
     653                 : /*      Create band objects.                                            */
     654                 : /* -------------------------------------------------------------------- */
     655              24 :     for (uInt4 i = 0; i < LenInv; ++i)
     656                 :     {
     657              20 :         uInt4 bandNr = i+1;
     658              20 :         if (bandNr == 1)
     659                 :         {
     660                 :             // important: set DataSet extents before creating first RasterBand in it
     661               4 :             double * data = NULL;
     662                 :             grib_MetaData* metaData;
     663               4 :             GRIBRasterBand::ReadGribData(grib_fp, 0, Inv[i].subgNum, &data, &metaData);
     664               4 :             if (data == 0 || metaData->gds.Nx < 1 || metaData->gds.Ny < 1)
     665                 :             {
     666                 :                 CPLError( CE_Failure, CPLE_OpenFailed, 
     667                 :                           "%s is a grib file, but no raster dataset was successfully identified.",
     668               0 :                           poOpenInfo->pszFilename );
     669               0 :                 CPLReleaseMutex(hGRIBMutex); // Release hGRIBMutex otherwise we'll deadlock with GDALDataset own hGRIBMutex
     670               0 :                 delete poDS;
     671               0 :                 CPLAcquireMutex(hGRIBMutex, 1000.0);
     672               0 :                 return NULL;
     673                 :             }
     674                 : 
     675               4 :             poDS->SetGribMetaData(metaData); // set the DataSet's x,y size, georeference and projection from the first GRIB band
     676               4 :             GRIBRasterBand* gribBand = new GRIBRasterBand( poDS, bandNr, Inv+i);
     677                 : 
     678               4 :             if( Inv->GribVersion == 2 )
     679               2 :                 gribBand->FindPDSTemplate();
     680                 : 
     681               4 :             gribBand->m_Grib_Data = data;
     682               4 :             gribBand->m_Grib_MetaData = metaData;
     683               4 :             poDS->SetBand( bandNr, gribBand);
     684                 :         }
     685                 :         else
     686              16 :             poDS->SetBand( bandNr, new GRIBRasterBand( poDS, bandNr, Inv+i ));
     687              20 :         GRIB2InventoryFree (Inv + i);
     688                 :     }
     689               4 :     free (Inv);
     690                 : 
     691                 : /* -------------------------------------------------------------------- */
     692                 : /*      Initialize any PAM information.                                 */
     693                 : /* -------------------------------------------------------------------- */
     694               4 :     poDS->SetDescription( poOpenInfo->pszFilename );
     695                 :     
     696               4 :     CPLReleaseMutex(hGRIBMutex); // Release hGRIBMutex otherwise we'll deadlock with GDALDataset own hGRIBMutex
     697               4 :     poDS->TryLoadXML();
     698                 : 
     699                 : /* -------------------------------------------------------------------- */
     700                 : /*      Check for external overviews.                                   */
     701                 : /* -------------------------------------------------------------------- */
     702               4 :     poDS->oOvManager.Initialize( poDS, poOpenInfo->pszFilename, poOpenInfo->papszSiblingFiles );
     703               4 :     CPLAcquireMutex(hGRIBMutex, 1000.0);
     704                 : 
     705               4 :     return( poDS );
     706                 : }
     707                 : 
     708                 : /************************************************************************/
     709                 : /*                            SetMetadata()                             */
     710                 : /************************************************************************/
     711                 : 
     712               4 : void GRIBDataset::SetGribMetaData(grib_MetaData* meta)
     713                 : {
     714               4 :     nRasterXSize = meta->gds.Nx;
     715               4 :     nRasterYSize = meta->gds.Ny;
     716                 : 
     717                 : /* -------------------------------------------------------------------- */
     718                 : /*      Image projection.                                               */
     719                 : /* -------------------------------------------------------------------- */
     720               4 :     OGRSpatialReference oSRS;
     721                 : 
     722               4 :     switch(meta->gds.projType)
     723                 :     {
     724                 :       case GS3_LATLON:
     725                 :       case GS3_GAUSSIAN_LATLON:
     726                 :           // No projection, only latlon system (geographic)
     727               2 :           break;
     728                 :       case GS3_MERCATOR:
     729                 :         oSRS.SetMercator(meta->gds.meshLat, meta->gds.orientLon,
     730               2 :                          1.0, 0.0, 0.0);
     731               2 :         break;
     732                 :       case GS3_POLAR:
     733                 :         oSRS.SetPS(meta->gds.meshLat, meta->gds.orientLon,
     734                 :                    meta->gds.scaleLat1,
     735               0 :                    0.0, 0.0);
     736               0 :         break;
     737                 :       case GS3_LAMBERT:
     738                 :         oSRS.SetLCC(meta->gds.scaleLat1, meta->gds.scaleLat2,
     739                 :                     meta->gds.meshLat, meta->gds.orientLon,
     740               0 :                     0.0, 0.0); // set projection
     741               0 :         break;
     742                 :       
     743                 : 
     744                 :       case GS3_ORTHOGRAPHIC:
     745                 : 
     746                 :         //oSRS.SetOrthographic(0.0, meta->gds.orientLon,
     747                 :         //                      meta->gds.lon2, meta->gds.lat2);
     748                 :         //oSRS.SetGEOS(meta->gds.orientLon, meta->gds.stretchFactor, meta->gds.lon2, meta->gds.lat2);
     749               0 :         oSRS.SetGEOS(  0, 35785831, 0, 0 ); // hardcoded for now, I don't know yet how to parse the meta->gds section
     750                 :         break;
     751                 :       case GS3_EQUATOR_EQUIDIST:
     752                 :         break;
     753                 :       case GS3_AZIMUTH_RANGE:
     754                 :         break;
     755                 :     }
     756                 : 
     757                 : /* -------------------------------------------------------------------- */
     758                 : /*      Earth model                                                     */
     759                 : /* -------------------------------------------------------------------- */
     760               4 :     double a = meta->gds.majEarth * 1000.0; // in meters
     761               4 :     double b = meta->gds.minEarth * 1000.0;
     762               4 :     if( a == 0 && b == 0 )
     763                 :     {
     764               0 :         a = 6377563.396;
     765               0 :         b = 6356256.910;
     766                 :     }
     767                 : 
     768               4 :     if (meta->gds.f_sphere)
     769                 :     {
     770                 :         oSRS.SetGeogCS( "Coordinate System imported from GRIB file",
     771                 :                         NULL,
     772                 :                         "Sphere",
     773               4 :                         a, 0.0 );
     774                 :     }
     775                 :     else
     776                 :     {
     777               0 :         double fInv = a/(a-b);
     778                 :         oSRS.SetGeogCS( "Coordinate System imported from GRIB file",
     779                 :                         NULL,
     780                 :                         "Spheroid imported from GRIB file",
     781               0 :                         a, fInv );
     782                 :     }
     783                 : 
     784               4 :     OGRSpatialReference oLL; // construct the "geographic" part of oSRS
     785               4 :     oLL.CopyGeogCSFrom( &oSRS );
     786                 : 
     787                 :     double rMinX;
     788                 :     double rMaxY;
     789                 :     double rPixelSizeX;
     790                 :     double rPixelSizeY;
     791               4 :     if (meta->gds.projType == GS3_ORTHOGRAPHIC)
     792                 :     {
     793                 :         //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
     794                 :         //rMaxY = meta->gds.Dy * (meta->gds.Ny / 2);
     795               0 :         const double geosExtentInMeters = 11137496.552; // hardcoded for now, assumption: GEOS projection, full disc (like MSG)
     796               0 :         rMinX = -(geosExtentInMeters / 2);
     797               0 :         rMaxY = geosExtentInMeters / 2;
     798               0 :         rPixelSizeX = geosExtentInMeters / meta->gds.Nx;
     799               0 :         rPixelSizeY = geosExtentInMeters / meta->gds.Ny;
     800                 :     }
     801               4 :     else if( oSRS.IsProjected() )
     802                 :     {
     803               2 :         rMinX = meta->gds.lon1; // longitude in degrees, to be transformed to meters (or degrees in case of latlon)
     804               2 :         rMaxY = meta->gds.lat1; // latitude in degrees, to be transformed to meters 
     805               2 :         OGRCoordinateTransformation *poTransformLLtoSRS = OGRCreateCoordinateTransformation( &(oLL), &(oSRS) );
     806               2 :         if ((poTransformLLtoSRS != NULL) && poTransformLLtoSRS->Transform( 1, &rMinX, &rMaxY )) // transform it to meters
     807                 :         {
     808               2 :             if (meta->gds.scan == GRIB2BIT_2) // Y is minY, GDAL wants maxY
     809               2 :                 rMaxY += (meta->gds.Ny - 1) * meta->gds.Dy; // -1 because we GDAL needs the coordinates of the centre of the pixel
     810               2 :             rPixelSizeX = meta->gds.Dx;
     811               2 :             rPixelSizeY = meta->gds.Dy;
     812                 :         }
     813                 :         else
     814                 :         {
     815               0 :             rMinX = 0.0;
     816               0 :             rMaxY = 0.0;
     817                 :             
     818               0 :             rPixelSizeX = 1.0;
     819               0 :             rPixelSizeY = -1.0;
     820                 :             
     821               0 :             oSRS.Clear();
     822                 : 
     823                 :             CPLError( CE_Warning, CPLE_AppDefined,
     824                 :                       "Unable to perform coordinate transformations, so the correct\n"
     825                 :                       "projected geotransform could not be deduced from the lat/long\n"
     826               0 :                       "control points.  Defaulting to ungeoreferenced." );
     827                 :         }
     828               2 :         delete poTransformLLtoSRS;
     829                 :     }
     830                 :     else
     831                 :     {
     832               2 :         rMinX = meta->gds.lon1; // longitude in degrees, to be transformed to meters (or degrees in case of latlon)
     833               2 :         rMaxY = meta->gds.lat1; // latitude in degrees, to be transformed to meters 
     834                 : 
     835               2 :         double rMinY = meta->gds.lat2;
     836               2 :         if (meta->gds.lat2 > rMaxY)
     837                 :         {
     838               2 :           rMaxY = meta->gds.lat2;
     839               2 :           rMinY = meta->gds.lat1;
     840                 :         }
     841                 : 
     842               2 :         if (meta->gds.lon1 > meta->gds.lon2)
     843               0 :           rPixelSizeX = (360.0 - (meta->gds.lon1 - meta->gds.lon2)) / (meta->gds.Nx - 1);
     844                 :         else
     845               2 :           rPixelSizeX = (meta->gds.lon2 - meta->gds.lon1) / (meta->gds.Nx - 1);
     846                 : 
     847               2 :         rPixelSizeY = (rMaxY - rMinY) / (meta->gds.Ny - 1);
     848                 : 
     849                 :         // Do some sanity checks for cases that can't be handled by the above
     850                 :         // pixel size corrections. GRIB1 has a minimum precision of 0.001
     851                 :         // for latitudes and longitudes, so we'll allow a bit higher than that.
     852               2 :         if (rPixelSizeX < 0 || fabs(rPixelSizeX - meta->gds.Dx) > 0.002)
     853               0 :           rPixelSizeX = meta->gds.Dx;
     854                 : 
     855               2 :         if (rPixelSizeY < 0 || fabs(rPixelSizeY - meta->gds.Dy) > 0.002)
     856               0 :           rPixelSizeY = meta->gds.Dy;
     857                 :     }
     858                 : 
     859                 :     // http://gdal.org/gdal_datamodel.html :
     860                 :     //   we need the top left corner of the top left pixel.
     861                 :     //   At the moment we have the center of the pixel.
     862               4 :     rMinX-=rPixelSizeX/2;
     863               4 :     rMaxY+=rPixelSizeY/2;
     864                 : 
     865               4 :     adfGeoTransform[0] = rMinX;
     866               4 :     adfGeoTransform[3] = rMaxY;
     867               4 :     adfGeoTransform[1] = rPixelSizeX;
     868               4 :     adfGeoTransform[5] = -rPixelSizeY;
     869                 : 
     870               4 :     CPLFree( pszProjection );
     871               4 :     pszProjection = NULL;
     872               4 :     oSRS.exportToWkt( &(pszProjection) );
     873               4 : }
     874                 : 
     875                 : /************************************************************************/
     876                 : /*                       GDALDeregister_GRIB()                          */
     877                 : /************************************************************************/
     878                 : 
     879             550 : static void GDALDeregister_GRIB(GDALDriver* )
     880                 : {
     881             550 :     if( hGRIBMutex != NULL )
     882                 :     {
     883               1 :         CPLDestroyMutex(hGRIBMutex);
     884               1 :         hGRIBMutex = NULL;
     885                 :     }
     886             550 : }
     887                 : 
     888                 : /************************************************************************/
     889                 : /*                         GDALRegister_GRIB()                          */
     890                 : /************************************************************************/
     891                 : 
     892             610 : void GDALRegister_GRIB()
     893                 : 
     894                 : {
     895                 :     GDALDriver  *poDriver;
     896                 : 
     897             610 :     if( GDALGetDriverByName( "GRIB" ) == NULL )
     898                 :     {
     899             588 :         poDriver = new GDALDriver();
     900                 :         
     901             588 :         poDriver->SetDescription( "GRIB" );
     902                 :         poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, 
     903             588 :                                    "GRIdded Binary (.grb)" );
     904                 :         poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC, 
     905             588 :                                    "frmt_grib.html" );
     906             588 :         poDriver->SetMetadataItem( GDAL_DMD_EXTENSION, "grb" );
     907             588 :         poDriver->SetMetadataItem( GDAL_DCAP_VIRTUALIO, "YES" );
     908                 : 
     909             588 :         poDriver->pfnOpen = GRIBDataset::Open;
     910             588 :         poDriver->pfnIdentify = GRIBDataset::Identify;
     911             588 :         poDriver->pfnUnloadDriver = GDALDeregister_GRIB;
     912                 : 
     913             588 :         GetGDALDriverManager()->RegisterDriver( poDriver );
     914                 :     }
     915             610 : }

Generated by: LCOV version 1.7