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