1 : /******************************************************************************
2 : * $Id: netcdfdataset.cpp 25683 2013-02-25 14:51:41Z rouault $
3 : *
4 : * Project: netCDF read/write Driver
5 : * Purpose: GDAL bindings over netCDF library.
6 : * Author: Frank Warmerdam, warmerdam@pobox.com
7 : *
8 : ******************************************************************************
9 : * Copyright (c) 2004, Frank Warmerdam
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 : #include "netcdfdataset.h"
31 : #include "cpl_error.h"
32 : #include "cpl_multiproc.h"
33 :
34 : CPL_CVSID("$Id: netcdfdataset.cpp 25683 2013-02-25 14:51:41Z rouault $");
35 :
36 : #include <map> //for NCDFWriteProjAttribs()
37 :
38 : /* Internal function declarations */
39 :
40 : int NCDFIsGDALVersionGTE(const char* pszVersion, int nTarget);
41 :
42 : void NCDFAddGDALHistory( int fpImage,
43 : const char * pszFilename, const char *pszOldHist,
44 : const char * pszFunctionName );
45 :
46 : void NCDFAddHistory(int fpImage, const char *pszAddHist, const char *pszOldHist);
47 :
48 : int NCDFIsCfProjection( const char* pszProjection );
49 :
50 : void NCDFWriteProjAttribs(const OGR_SRSNode *poPROJCS,
51 : const char* pszProjection,
52 : const int fpImage, const int NCDFVarID);
53 :
54 : CPLErr NCDFSafeStrcat(char** ppszDest, char* pszSrc, size_t* nDestSize);
55 :
56 : /* var / attribute helper functions */
57 : CPLErr NCDFGetAttr( int nCdfId, int nVarId, const char *pszAttrName,
58 : double *pdfValue );
59 : CPLErr NCDFGetAttr( int nCdfId, int nVarId, const char *pszAttrName,
60 : char **pszValue );
61 : CPLErr NCDFPutAttr( int nCdfId, int nVarId,
62 : const char *pszAttrName, const char *pszValue );
63 : CPLErr NCDFGet1DVar( int nCdfId, int nVarId, char **pszValue );//replace this where used
64 : CPLErr NCDFPut1DVar( int nCdfId, int nVarId, const char *pszValue );
65 :
66 : double NCDFGetDefaultNoDataValue( int nVarType );
67 :
68 : /* dimension check functions */
69 : int NCDFIsVarLongitude(int nCdfId, int nVarId=-1, const char * nVarName=NULL );
70 : int NCDFIsVarLatitude(int nCdfId, int nVarId=-1, const char * nVarName=NULL );
71 : int NCDFIsVarProjectionX( int nCdfId, int nVarId=-1, const char * pszVarName=NULL );
72 : int NCDFIsVarProjectionY( int nCdfId, int nVarId=-1, const char * pszVarName=NULL );
73 : int NCDFIsVarVerticalCoord(int nCdfId, int nVarId=-1, const char * nVarName=NULL );
74 : int NCDFIsVarTimeCoord(int nCdfId, int nVarId=-1, const char * nVarName=NULL );
75 :
76 : char **NCDFTokenizeArray( const char *pszValue );//replace this where used
77 : void CopyMetadata( void *poDS, int fpImage, int CDFVarID,
78 : const char *pszMatchPrefix=NULL, int bIsBand=TRUE );
79 :
80 :
81 : void *hNCMutex = NULL;
82 :
83 : /************************************************************************/
84 : /* ==================================================================== */
85 : /* netCDFRasterBand */
86 : /* ==================================================================== */
87 : /************************************************************************/
88 :
89 : class netCDFRasterBand : public GDALPamRasterBand
90 : {
91 : friend class netCDFDataset;
92 :
93 : nc_type nc_datatype;
94 : int cdfid;
95 : int nZId;
96 : int nZDim;
97 : int nLevel;
98 : int nBandXPos;
99 : int nBandYPos;
100 : int *panBandZPos;
101 : int *panBandZLev;
102 : int bNoDataSet;
103 : double dfNoDataValue;
104 : double adfValidRange[2];
105 : double dfScale;
106 : double dfOffset;
107 : int bSignedData;
108 : int status;
109 : int bCheckLongitude;
110 :
111 : CPLErr CreateBandMetadata( int *paDimIds );
112 : template <class T> void CheckValidData ( void * pImage,
113 : int bCheckIsNan=FALSE ) ;
114 :
115 : protected:
116 :
117 : CPLXMLNode *SerializeToXML( const char *pszVRTPath );
118 :
119 : public:
120 :
121 : netCDFRasterBand( netCDFDataset *poDS,
122 : int nZId,
123 : int nZDim,
124 : int nLevel,
125 : int *panBandZLen,
126 : int *panBandPos,
127 : int *paDimIds,
128 : int nBand );
129 : netCDFRasterBand( netCDFDataset *poDS,
130 : GDALDataType eType,
131 : int nBand,
132 : int bSigned=TRUE,
133 : char *pszBandName=NULL,
134 : char *pszLongName=NULL,
135 : int nZId=-1,
136 : int nZDim=2,
137 : int nLevel=0,
138 : int *panBandZLev=NULL,
139 : int *panBandZPos=NULL,
140 : int *paDimIds=NULL );
141 : ~netCDFRasterBand( );
142 :
143 : virtual double GetNoDataValue( int * );
144 : virtual CPLErr SetNoDataValue( double );
145 : virtual double GetOffset( int * );
146 : virtual CPLErr SetOffset( double );
147 : virtual double GetScale( int * );
148 : virtual CPLErr SetScale( double );
149 : virtual CPLErr IReadBlock( int, int, void * );
150 : virtual CPLErr IWriteBlock( int, int, void * );
151 :
152 : };
153 :
154 : /************************************************************************/
155 : /* netCDFRasterBand() */
156 : /************************************************************************/
157 :
158 266 : netCDFRasterBand::netCDFRasterBand( netCDFDataset *poNCDFDS,
159 : int nZId,
160 : int nZDim,
161 : int nLevel,
162 : int *panBandZLev,
163 : int *panBandZPos,
164 : int *paDimIds,
165 266 : int nBand )
166 :
167 : {
168 266 : double dfNoData = 0.0;
169 266 : int bGotNoData = FALSE;
170 266 : nc_type vartype=NC_NAT;
171 266 : nc_type atttype=NC_NAT;
172 : size_t attlen;
173 : char szNoValueName[NCDF_MAX_STR_LEN];
174 :
175 266 : this->poDS = poNCDFDS;
176 266 : this->panBandZPos = NULL;
177 266 : this->panBandZLev = NULL;
178 266 : this->nBand = nBand;
179 266 : this->nZId = nZId;
180 266 : this->nZDim = nZDim;
181 266 : this->nLevel = nLevel;
182 266 : this->nBandXPos = panBandZPos[0];
183 266 : this->nBandYPos = panBandZPos[1];
184 266 : this->bSignedData = TRUE; //default signed, except for Byte
185 266 : this->cdfid = poNCDFDS->GetCDFID();
186 266 : this->status = NC_NOERR;
187 266 : this->bCheckLongitude = FALSE;
188 :
189 : /* -------------------------------------------------------------------- */
190 : /* Take care of all other dimmensions */
191 : /* ------------------------------------------------------------------ */
192 266 : if( nZDim > 2 ) {
193 : this->panBandZPos =
194 105 : (int *) CPLCalloc( nZDim-1, sizeof( int ) );
195 : this->panBandZLev =
196 105 : (int *) CPLCalloc( nZDim-1, sizeof( int ) );
197 :
198 314 : for ( int i=0; i < nZDim - 2; i++ ){
199 209 : this->panBandZPos[i] = panBandZPos[i+2];
200 209 : this->panBandZLev[i] = panBandZLev[i];
201 : }
202 : }
203 :
204 266 : this->dfNoDataValue = 0.0;
205 266 : this->bNoDataSet = FALSE;
206 :
207 266 : nRasterXSize = poDS->GetRasterXSize( );
208 266 : nRasterYSize = poDS->GetRasterYSize( );
209 266 : nBlockXSize = poDS->GetRasterXSize( );
210 266 : nBlockYSize = 1;
211 :
212 : /* -------------------------------------------------------------------- */
213 : /* Get the type of the "z" variable, our target raster array. */
214 : /* -------------------------------------------------------------------- */
215 266 : if( nc_inq_var( cdfid, nZId, NULL, &nc_datatype, NULL, NULL,
216 : NULL ) != NC_NOERR ){
217 : CPLError( CE_Failure, CPLE_AppDefined,
218 0 : "Error in nc_var_inq() on 'z'." );
219 0 : return;
220 : }
221 :
222 266 : if( nc_datatype == NC_BYTE )
223 93 : eDataType = GDT_Byte;
224 : #ifdef NETCDF_HAS_NC4
225 : /* NC_UBYTE (unsigned byte) is only available for NC4 */
226 : else if( nc_datatype == NC_UBYTE )
227 : eDataType = GDT_Byte;
228 : #endif
229 173 : else if( nc_datatype == NC_CHAR )
230 0 : eDataType = GDT_Byte;
231 173 : else if( nc_datatype == NC_SHORT )
232 19 : eDataType = GDT_Int16;
233 154 : else if( nc_datatype == NC_INT )
234 83 : eDataType = GDT_Int32;
235 71 : else if( nc_datatype == NC_FLOAT )
236 46 : eDataType = GDT_Float32;
237 25 : else if( nc_datatype == NC_DOUBLE )
238 25 : eDataType = GDT_Float64;
239 : else
240 : {
241 0 : if( nBand == 1 )
242 : CPLError( CE_Warning, CPLE_AppDefined,
243 : "Unsupported netCDF datatype (%d), treat as Float32.",
244 0 : (int) nc_datatype );
245 0 : eDataType = GDT_Float32;
246 : }
247 :
248 : /* -------------------------------------------------------------------- */
249 : /* Find and set No Data for this variable */
250 : /* -------------------------------------------------------------------- */
251 :
252 : /* find attribute name, either _FillValue or missing_value */
253 : status = nc_inq_att( cdfid, nZId,
254 266 : _FillValue, &atttype, &attlen);
255 266 : if( status == NC_NOERR ) {
256 239 : strcpy(szNoValueName, _FillValue );
257 : }
258 : else {
259 : status = nc_inq_att( cdfid, nZId,
260 27 : "missing_value", &atttype, &attlen );
261 27 : if( status == NC_NOERR ) {
262 7 : strcpy( szNoValueName, "missing_value" );
263 : }
264 : }
265 :
266 : /* fetch missing value */
267 266 : if( status == NC_NOERR ) {
268 246 : if ( NCDFGetAttr( cdfid, nZId, szNoValueName,
269 : &dfNoData ) == CE_None )
270 246 : bGotNoData = TRUE;
271 : }
272 :
273 : /* if NoData was not found, use the default value */
274 266 : if ( ! bGotNoData ) {
275 20 : nc_inq_vartype( cdfid, nZId, &vartype );
276 20 : dfNoData = NCDFGetDefaultNoDataValue( vartype );
277 20 : bGotNoData = TRUE;
278 : CPLDebug( "GDAL_netCDF",
279 : "did not get nodata value for variable #%d, using default %f",
280 20 : nZId, dfNoData );
281 : }
282 :
283 : /* set value */
284 266 : SetNoDataValue( dfNoData );
285 :
286 : /* -------------------------------------------------------------------- */
287 : /* Look for valid_range or valid_min/valid_max */
288 : /* -------------------------------------------------------------------- */
289 : /* set valid_range to nodata, then check for actual values */
290 266 : adfValidRange[0] = dfNoData;
291 266 : adfValidRange[1] = dfNoData;
292 : /* first look for valid_range */
293 266 : int bGotValidRange = FALSE;
294 : status = nc_inq_att( cdfid, nZId,
295 266 : "valid_range", &atttype, &attlen);
296 266 : if( (status == NC_NOERR) && (attlen == 2)) {
297 : int vrange[2];
298 : int vmin, vmax;
299 : status = nc_get_att_int( cdfid, nZId,
300 91 : "valid_range", vrange );
301 91 : if( status == NC_NOERR ) {
302 91 : bGotValidRange = TRUE;
303 91 : adfValidRange[0] = vrange[0];
304 91 : adfValidRange[1] = vrange[1];
305 : }
306 : /* if not found look for valid_min and valid_max */
307 : else {
308 : status = nc_get_att_int( cdfid, nZId,
309 0 : "valid_min", &vmin );
310 0 : if( status == NC_NOERR ) {
311 0 : adfValidRange[0] = vmin;
312 : status = nc_get_att_int( cdfid, nZId,
313 0 : "valid_max", &vmax );
314 0 : if( status == NC_NOERR ) {
315 0 : adfValidRange[1] = vmax;
316 0 : bGotValidRange = TRUE;
317 : }
318 : }
319 : }
320 : }
321 :
322 : /* -------------------------------------------------------------------- */
323 : /* Special For Byte Bands: check for signed/unsigned byte */
324 : /* -------------------------------------------------------------------- */
325 266 : if ( nc_datatype == NC_BYTE ) {
326 :
327 : /* netcdf uses signed byte by default, but GDAL uses unsigned by default */
328 : /* This may cause unexpected results, but is needed for back-compat */
329 93 : if ( poNCDFDS->bIsGdalFile )
330 91 : this->bSignedData = FALSE;
331 : else
332 2 : this->bSignedData = TRUE;
333 :
334 : /* For NC4 format NC_BYTE is signed, NC_UBYTE is unsigned */
335 93 : if ( poNCDFDS->nFormat == NCDF_FORMAT_NC4 ) {
336 0 : this->bSignedData = TRUE;
337 : }
338 : else {
339 : /* if we got valid_range, test for signed/unsigned range */
340 : /* http://www.unidata.ucar.edu/software/netcdf/docs/netcdf/Attribute-Conventions.html */
341 93 : if ( bGotValidRange == TRUE ) {
342 : /* If we got valid_range={0,255}, treat as unsigned */
343 175 : if ( (adfValidRange[0] == 0) && (adfValidRange[1] == 255) ) {
344 84 : bSignedData = FALSE;
345 : /* reset valid_range */
346 84 : adfValidRange[0] = dfNoData;
347 84 : adfValidRange[1] = dfNoData;
348 : }
349 : /* If we got valid_range={-128,127}, treat as signed */
350 7 : else if ( (adfValidRange[0] == -128) && (adfValidRange[1] == 127) ) {
351 7 : bSignedData = TRUE;
352 : /* reset valid_range */
353 7 : adfValidRange[0] = dfNoData;
354 7 : adfValidRange[1] = dfNoData;
355 : }
356 : }
357 : /* else test for _Unsigned */
358 : /* http://www.unidata.ucar.edu/software/netcdf/docs/BestPractices.html */
359 : else {
360 2 : char *pszTemp = NULL;
361 2 : if ( NCDFGetAttr( cdfid, nZId, "_Unsigned", &pszTemp )
362 :
363 : == CE_None ) {
364 2 : if ( EQUAL(pszTemp,"true"))
365 2 : bSignedData = FALSE;
366 0 : else if ( EQUAL(pszTemp,"false"))
367 0 : bSignedData = TRUE;
368 2 : CPLFree( pszTemp );
369 : }
370 : }
371 : }
372 :
373 93 : if ( bSignedData )
374 : {
375 : /* set PIXELTYPE=SIGNEDBYTE */
376 : /* See http://trac.osgeo.org/gdal/wiki/rfc14_imagestructure */
377 7 : SetMetadataItem( "PIXELTYPE", "SIGNEDBYTE", "IMAGE_STRUCTURE" );
378 7 : CPLDebug( "GDAL_netCDF", "got signed Byte" );
379 : }
380 : else
381 86 : CPLDebug( "GDAL_netCDF", "got unsigned Byte" );
382 :
383 : }
384 :
385 : /* -------------------------------------------------------------------- */
386 : /* Create Band Metadata */
387 : /* -------------------------------------------------------------------- */
388 266 : CreateBandMetadata( paDimIds );
389 :
390 : /* -------------------------------------------------------------------- */
391 : /* Attempt to fetch the scale_factor and add_offset attributes for the */
392 : /* variable and set them. If these values are not available, set */
393 : /* offset to 0 and scale to 1 */
394 : /* -------------------------------------------------------------------- */
395 266 : double dfOff = 0.0;
396 266 : double dfScale = 1.0;
397 :
398 266 : if ( nc_inq_attid ( cdfid, nZId, CF_ADD_OFFSET, NULL) == NC_NOERR ) {
399 3 : status = nc_get_att_double( cdfid, nZId, CF_ADD_OFFSET, &dfOff );
400 3 : CPLDebug( "GDAL_netCDF", "got add_offset=%.16g, status=%d", dfOff, status );
401 : }
402 266 : if ( nc_inq_attid ( cdfid, nZId,
403 : CF_SCALE_FACTOR, NULL) == NC_NOERR ) {
404 3 : status = nc_get_att_double( cdfid, nZId, CF_SCALE_FACTOR, &dfScale );
405 3 : CPLDebug( "GDAL_netCDF", "got scale_factor=%.16g, status=%d", dfScale, status );
406 : }
407 266 : SetOffset( dfOff );
408 266 : SetScale( dfScale );
409 :
410 : /* should we check for longitude values > 360 ? */
411 : this->bCheckLongitude =
412 : CSLTestBoolean(CPLGetConfigOption("GDAL_NETCDF_CENTERLONG_180", "YES"))
413 266 : && NCDFIsVarLongitude( cdfid, nZId, NULL );
414 0 : }
415 :
416 : /* constructor in create mode */
417 : /* if nZId and following variables are not passed, the band will have 2 dimensions */
418 : /* TODO get metadata, missing val from band #1 if nZDim>2 */
419 154 : netCDFRasterBand::netCDFRasterBand( netCDFDataset *poNCDFDS,
420 : GDALDataType eType,
421 : int nBand,
422 : int bSigned,
423 : char *pszBandName,
424 : char *pszLongName,
425 : int nZId,
426 : int nZDim,
427 : int nLevel,
428 : int *panBandZLev,
429 : int *panBandZPos,
430 154 : int *paDimIds )
431 : {
432 : int status;
433 154 : double dfNoData = 0.0;
434 : char szTemp[NCDF_MAX_STR_LEN];
435 154 : int bDefineVar = FALSE;
436 :
437 154 : this->poDS = poNCDFDS;
438 154 : this->nBand = nBand;
439 154 : this->nZId = nZId;
440 154 : this->nZDim = nZDim;
441 154 : this->nLevel = nLevel;
442 154 : this->panBandZPos = NULL;
443 154 : this->panBandZLev = NULL;
444 154 : this->nBandXPos = 1;
445 154 : this->nBandYPos = 0;
446 154 : this->bSignedData = bSigned;
447 :
448 154 : this->status = NC_NOERR;
449 154 : this->cdfid = poNCDFDS->GetCDFID();
450 154 : this->bCheckLongitude = FALSE;
451 :
452 154 : this->bNoDataSet = FALSE;
453 154 : this->dfNoDataValue = 0.0;
454 :
455 154 : nRasterXSize = poDS->GetRasterXSize( );
456 154 : nRasterYSize = poDS->GetRasterYSize( );
457 154 : nBlockXSize = poDS->GetRasterXSize( );
458 154 : nBlockYSize = 1;
459 :
460 154 : if ( poDS->GetAccess() != GA_Update ) {
461 : CPLError( CE_Failure, CPLE_NotSupported,
462 0 : "Dataset is not in update mode, wrong netCDFRasterBand constructor" );
463 0 : return;
464 : }
465 :
466 : /* -------------------------------------------------------------------- */
467 : /* Take care of all other dimmensions */
468 : /* ------------------------------------------------------------------ */
469 154 : if ( nZDim > 2 && paDimIds != NULL ) {
470 :
471 19 : this->nBandXPos = panBandZPos[0];
472 19 : this->nBandYPos = panBandZPos[1];
473 :
474 : this->panBandZPos =
475 19 : (int *) CPLCalloc( nZDim-1, sizeof( int ) );
476 : this->panBandZLev =
477 19 : (int *) CPLCalloc( nZDim-1, sizeof( int ) );
478 :
479 54 : for ( int i=0; i < nZDim - 2; i++ ){
480 35 : this->panBandZPos[i] = panBandZPos[i+2];
481 35 : this->panBandZLev[i] = panBandZLev[i];
482 : }
483 : }
484 :
485 : /* -------------------------------------------------------------------- */
486 : /* Get the type of the "z" variable, our target raster array. */
487 : /* -------------------------------------------------------------------- */
488 154 : eDataType = eType;
489 :
490 154 : switch ( eDataType )
491 : {
492 : case GDT_Byte:
493 73 : nc_datatype = NC_BYTE;
494 : #ifdef NETCDF_HAS_NC4
495 : /* NC_UBYTE (unsigned byte) is only available for NC4 */
496 : if ( ! bSignedData && (poNCDFDS->nFormat == NCDF_FORMAT_NC4) )
497 : nc_datatype = NC_UBYTE;
498 : #endif
499 73 : break;
500 : case GDT_Int16:
501 10 : nc_datatype = NC_SHORT;
502 10 : break;
503 : case GDT_Int32:
504 24 : nc_datatype = NC_INT;
505 24 : break;
506 : case GDT_Float32:
507 13 : nc_datatype = NC_FLOAT;
508 13 : break;
509 : case GDT_Float64:
510 8 : nc_datatype = NC_DOUBLE;
511 8 : break;
512 : default:
513 26 : if( nBand == 1 )
514 : CPLError( CE_Warning, CPLE_AppDefined,
515 : "Unsupported GDAL datatype (%d), treat as NC_FLOAT.",
516 14 : (int) eDataType );
517 26 : nc_datatype = NC_FLOAT;
518 : break;
519 : }
520 :
521 : /* -------------------------------------------------------------------- */
522 : /* Define the variable if necessary (if nZId==-1) */
523 : /* -------------------------------------------------------------------- */
524 154 : if ( nZId == -1 ) {
525 :
526 140 : bDefineVar = TRUE;
527 :
528 : /* make sure we are in define mode */
529 140 : ( ( netCDFDataset * ) poDS )->SetDefineMode( TRUE );
530 :
531 259 : if ( !pszBandName || EQUAL(pszBandName,"") )
532 119 : sprintf( szTemp, "Band%d", nBand );
533 : else
534 21 : strcpy( szTemp, pszBandName );
535 :
536 145 : if ( nZDim > 2 && paDimIds != NULL ) {
537 : status = nc_def_var( cdfid, szTemp, nc_datatype,
538 5 : nZDim, paDimIds, &nZId );
539 : }
540 : else {
541 : int anBandDims[ 2 ];
542 135 : anBandDims[0] = poNCDFDS->nYDimID;
543 135 : anBandDims[1] = poNCDFDS->nXDimID;
544 : status = nc_def_var( cdfid, szTemp, nc_datatype,
545 135 : 2, anBandDims, &nZId );
546 : }
547 140 : NCDF_ERR(status);
548 : CPLDebug( "GDAL_netCDF", "nc_def_var(%d,%s,%d) id=%d",
549 140 : cdfid, szTemp, nc_datatype, nZId );
550 140 : this->nZId = nZId;
551 :
552 262 : if ( !pszLongName || EQUAL(pszLongName,"") )
553 122 : sprintf( szTemp, "GDAL Band Number %d", nBand );
554 : else
555 18 : strcpy( szTemp, pszLongName );
556 : status = nc_put_att_text( cdfid, nZId, CF_LNG_NAME,
557 140 : strlen( szTemp ), szTemp );
558 140 : NCDF_ERR(status);
559 :
560 140 : poNCDFDS->DefVarDeflate(nZId, TRUE);
561 :
562 : }
563 :
564 : /* for Byte data add signed/unsigned info */
565 154 : if ( eDataType == GDT_Byte ) {
566 :
567 73 : if ( bDefineVar ) { //only add attributes if creating variable
568 73 : CPLDebug( "GDAL_netCDF", "adding valid_range attributes for Byte Band" );
569 : /* For unsigned NC_BYTE (except NC4 format) */
570 : /* add valid_range and _Unsigned ( defined in CF-1 and NUG ) */
571 73 : if ( (nc_datatype == NC_BYTE) && (poNCDFDS->nFormat != NCDF_FORMAT_NC4) ) {
572 : short int adfValidRange[2];
573 73 : if ( bSignedData ) {
574 3 : adfValidRange[0] = -128;
575 3 : adfValidRange[1] = 127;
576 : status = nc_put_att_text( cdfid,nZId,
577 3 : "_Unsigned", 5, "false" );
578 : }
579 : else {
580 70 : adfValidRange[0] = 0;
581 70 : adfValidRange[1] = 255;
582 : status = nc_put_att_text( cdfid,nZId,
583 70 : "_Unsigned", 4, "true" );
584 : }
585 : status=nc_put_att_short( cdfid,nZId, "valid_range",
586 73 : NC_SHORT, 2, adfValidRange );
587 : }
588 : }
589 : /* for unsigned byte set PIXELTYPE=SIGNEDBYTE */
590 : /* See http://trac.osgeo.org/gdal/wiki/rfc14_imagestructure */
591 73 : if ( bSignedData )
592 3 : SetMetadataItem( "PIXELTYPE", "SIGNEDBYTE", "IMAGE_STRUCTURE" );
593 :
594 : }
595 :
596 : /* set default nodata */
597 154 : dfNoData = NCDFGetDefaultNoDataValue( nc_datatype );
598 154 : SetNoDataValue( dfNoData );
599 :
600 0 : }
601 :
602 : /************************************************************************/
603 : /* ~netCDFRasterBand() */
604 : /************************************************************************/
605 :
606 420 : netCDFRasterBand::~netCDFRasterBand()
607 : {
608 420 : FlushCache();
609 420 : if( panBandZPos )
610 124 : CPLFree( panBandZPos );
611 420 : if( panBandZLev )
612 124 : CPLFree( panBandZLev );
613 420 : }
614 :
615 : /************************************************************************/
616 : /* GetOffset() */
617 : /************************************************************************/
618 31 : double netCDFRasterBand::GetOffset( int *pbSuccess )
619 : {
620 31 : if( pbSuccess != NULL )
621 31 : *pbSuccess = TRUE;
622 :
623 31 : return dfOffset;
624 : }
625 :
626 : /************************************************************************/
627 : /* SetOffset() */
628 : /************************************************************************/
629 266 : CPLErr netCDFRasterBand::SetOffset( double dfNewOffset )
630 : {
631 266 : CPLMutexHolderD(&hNCMutex);
632 :
633 266 : dfOffset = dfNewOffset;
634 :
635 : /* write value if in update mode */
636 266 : if ( poDS->GetAccess() == GA_Update ) {
637 :
638 : /* make sure we are in define mode */
639 0 : ( ( netCDFDataset * ) poDS )->SetDefineMode( TRUE );
640 :
641 : status = nc_put_att_double( cdfid, nZId, CF_ADD_OFFSET,
642 0 : NC_DOUBLE, 1, &dfOffset );
643 :
644 0 : NCDF_ERR(status);
645 0 : if ( status == NC_NOERR )
646 0 : return CE_None;
647 : else
648 0 : return CE_Failure;
649 :
650 : }
651 :
652 266 : return CE_None;
653 : }
654 :
655 : /************************************************************************/
656 : /* GetScale() */
657 : /************************************************************************/
658 31 : double netCDFRasterBand::GetScale( int *pbSuccess )
659 : {
660 31 : if( pbSuccess != NULL )
661 31 : *pbSuccess = TRUE;
662 31 : return dfScale;
663 : }
664 :
665 : /************************************************************************/
666 : /* SetScale() */
667 : /************************************************************************/
668 266 : CPLErr netCDFRasterBand::SetScale( double dfNewScale )
669 : {
670 266 : CPLMutexHolderD(&hNCMutex);
671 :
672 266 : dfScale = dfNewScale;
673 :
674 : /* write value if in update mode */
675 266 : if ( poDS->GetAccess() == GA_Update ) {
676 :
677 : /* make sure we are in define mode */
678 0 : ( ( netCDFDataset * ) poDS )->SetDefineMode( TRUE );
679 :
680 : status = nc_put_att_double( cdfid, nZId, CF_SCALE_FACTOR,
681 0 : NC_DOUBLE, 1, &dfScale );
682 :
683 0 : NCDF_ERR(status);
684 0 : if ( status == NC_NOERR )
685 0 : return CE_None;
686 : else
687 0 : return CE_Failure;
688 :
689 : }
690 :
691 266 : return CE_None;
692 : }
693 :
694 : /************************************************************************/
695 : /* GetNoDataValue() */
696 : /************************************************************************/
697 :
698 88 : double netCDFRasterBand::GetNoDataValue( int * pbSuccess )
699 :
700 : {
701 88 : if( pbSuccess )
702 61 : *pbSuccess = bNoDataSet;
703 :
704 88 : if( bNoDataSet )
705 88 : return dfNoDataValue;
706 : else
707 0 : return GDALPamRasterBand::GetNoDataValue( pbSuccess );
708 : }
709 :
710 :
711 : /************************************************************************/
712 : /* SetNoDataValue() */
713 : /************************************************************************/
714 :
715 511 : CPLErr netCDFRasterBand::SetNoDataValue( double dfNoData )
716 :
717 : {
718 511 : CPLMutexHolderD(&hNCMutex);
719 :
720 : /* If already set to new value, don't do anything */
721 511 : if ( bNoDataSet && CPLIsEqual( dfNoData, dfNoDataValue ) )
722 11 : return CE_None;
723 :
724 : /* write value if in update mode */
725 500 : if ( poDS->GetAccess() == GA_Update ) {
726 :
727 : /* netcdf-4 does not allow to set _FillValue after leaving define mode */
728 : /* but it's ok if variable has not been written to, so only print debug */
729 : /* see bug #4484 */
730 234 : if ( bNoDataSet &&
731 : ( ((netCDFDataset *)poDS)->GetDefineMode() == FALSE ) ) {
732 : CPLDebug( "GDAL_netCDF",
733 : "Setting NoDataValue to %.18g (previously set to %.18g) "
734 : "but file is no longer in define mode (id #%d, band #%d)",
735 0 : dfNoData, dfNoDataValue, cdfid, nBand );
736 : }
737 : /* TODO add NCDFDebug function for verbose debugging */
738 : #ifdef NCDF_DEBUG
739 : else {
740 : CPLDebug( "GDAL_netCDF", "Setting NoDataValue to %.18g (id #%d, band #%d)",
741 : dfNoData, cdfid, nBand );
742 : }
743 : #endif
744 : /* make sure we are in define mode */
745 234 : ( ( netCDFDataset * ) poDS )->SetDefineMode( TRUE );
746 :
747 234 : if ( eDataType == GDT_Byte) {
748 116 : if ( bSignedData ) {
749 5 : signed char cNoDataValue = (signed char) dfNoData;
750 : status = nc_put_att_schar( cdfid, nZId, _FillValue,
751 5 : nc_datatype, 1, &cNoDataValue );
752 : }
753 : else {
754 111 : unsigned char ucNoDataValue = (unsigned char) dfNoData;
755 : status = nc_put_att_uchar( cdfid, nZId, _FillValue,
756 111 : nc_datatype, 1, &ucNoDataValue );
757 : }
758 : }
759 118 : else if ( eDataType == GDT_Int16 ) {
760 15 : short int nsNoDataValue = (short int) dfNoData;
761 : status = nc_put_att_short( cdfid, nZId, _FillValue,
762 15 : nc_datatype, 1, &nsNoDataValue );
763 : }
764 103 : else if ( eDataType == GDT_Int32) {
765 43 : int nNoDataValue = (int) dfNoData;
766 : status = nc_put_att_int( cdfid, nZId, _FillValue,
767 43 : nc_datatype, 1, &nNoDataValue );
768 : }
769 60 : else if ( eDataType == GDT_Float32) {
770 21 : float fNoDataValue = (float) dfNoData;
771 : status = nc_put_att_float( cdfid, nZId, _FillValue,
772 21 : nc_datatype, 1, &fNoDataValue );
773 : }
774 : else
775 : status = nc_put_att_double( cdfid, nZId, _FillValue,
776 39 : nc_datatype, 1, &dfNoData );
777 :
778 234 : NCDF_ERR(status);
779 :
780 : /* update status if write worked */
781 234 : if ( status == NC_NOERR ) {
782 234 : dfNoDataValue = dfNoData;
783 234 : bNoDataSet = TRUE;
784 234 : return CE_None;
785 : }
786 : else
787 0 : return CE_Failure;
788 :
789 : }
790 :
791 266 : dfNoDataValue = dfNoData;
792 266 : bNoDataSet = TRUE;
793 266 : return CE_None;
794 : }
795 :
796 : /************************************************************************/
797 : /* SerializeToXML() */
798 : /************************************************************************/
799 :
800 2 : CPLXMLNode *netCDFRasterBand::SerializeToXML( const char *pszUnused )
801 :
802 : {
803 : /* -------------------------------------------------------------------- */
804 : /* Overriden from GDALPamDataset to add only band histogram */
805 : /* and statistics. See bug #4244. */
806 : /* -------------------------------------------------------------------- */
807 :
808 2 : if( psPam == NULL )
809 0 : return NULL;
810 :
811 : /* -------------------------------------------------------------------- */
812 : /* Setup root node and attributes. */
813 : /* -------------------------------------------------------------------- */
814 2 : CPLString oFmt;
815 :
816 : CPLXMLNode *psTree;
817 :
818 2 : psTree = CPLCreateXMLNode( NULL, CXT_Element, "PAMRasterBand" );
819 :
820 2 : if( GetBand() > 0 )
821 2 : CPLSetXMLValue( psTree, "#band", oFmt.Printf( "%d", GetBand() ) );
822 :
823 : /* -------------------------------------------------------------------- */
824 : /* Histograms. */
825 : /* -------------------------------------------------------------------- */
826 2 : if( psPam->psSavedHistograms != NULL )
827 0 : CPLAddXMLChild( psTree, CPLCloneXMLTree( psPam->psSavedHistograms ) );
828 :
829 : /* -------------------------------------------------------------------- */
830 : /* Metadata (statistics only). */
831 : /* -------------------------------------------------------------------- */
832 : CPLXMLNode *psMD;
833 :
834 2 : GDALMultiDomainMetadata oMDMDStats;
835 : const char* papszMDStats[] = { "STATISTICS_MINIMUM", "STATISTICS_MAXIMUM",
836 : "STATISTICS_MEAN", "STATISTICS_STDDEV",
837 2 : NULL };
838 10 : for ( int i=0; i<CSLCount((char**)papszMDStats); i++ ) {
839 8 : if ( GetMetadataItem( papszMDStats[i] ) != NULL )
840 : oMDMDStats.SetMetadataItem( papszMDStats[i],
841 0 : GetMetadataItem(papszMDStats[i]) );
842 : }
843 2 : psMD = oMDMDStats.Serialize();
844 :
845 2 : if( psMD != NULL )
846 : {
847 0 : if( psMD->psChild == NULL )
848 0 : CPLDestroyXMLNode( psMD );
849 : else
850 0 : CPLAddXMLChild( psTree, psMD );
851 : }
852 :
853 : /* -------------------------------------------------------------------- */
854 : /* We don't want to return anything if we had no metadata to */
855 : /* attach. */
856 : /* -------------------------------------------------------------------- */
857 2 : if( psTree->psChild == NULL || psTree->psChild->psNext == NULL )
858 : {
859 2 : CPLDestroyXMLNode( psTree );
860 2 : psTree = NULL;
861 : }
862 :
863 2 : return psTree;
864 : }
865 :
866 : /************************************************************************/
867 : /* CreateBandMetadata() */
868 : /************************************************************************/
869 :
870 266 : CPLErr netCDFRasterBand::CreateBandMetadata( int *paDimIds )
871 :
872 : {
873 : char szVarName[NC_MAX_NAME];
874 : char szMetaName[NC_MAX_NAME];
875 : char szMetaTemp[NCDF_MAX_STR_LEN];
876 266 : char *pszMetaValue = NULL;
877 : char szTemp[NC_MAX_NAME];
878 :
879 : int nd;
880 : int i,j;
881 266 : int Sum = 1;
882 266 : int Taken = 0;
883 266 : int result = 0;
884 : int status;
885 266 : int nVarID = -1;
886 : int nDims;
887 : size_t start[1];
888 : size_t count[1];
889 266 : nc_type nVarType = NC_NAT;
890 266 : int nAtt=0;
891 :
892 266 : netCDFDataset *poDS = (netCDFDataset *) this->poDS;
893 :
894 : /* -------------------------------------------------------------------- */
895 : /* Compute all dimensions from Band number and save in Metadata */
896 : /* -------------------------------------------------------------------- */
897 266 : nc_inq_varname( cdfid, nZId, szVarName );
898 266 : nc_inq_varndims( cdfid, nZId, &nd );
899 : /* -------------------------------------------------------------------- */
900 : /* Compute multidimention band position */
901 : /* */
902 : /* BandPosition = (Total - sum(PastBandLevels) - 1)/sum(remainingLevels)*/
903 : /* if Data[2,3,4,x,y] */
904 : /* */
905 : /* BandPos0 = (nBand ) / (3*4) */
906 : /* BandPos1 = (nBand - BandPos0*(3*4) ) / (4) */
907 : /* BandPos2 = (nBand - BandPos0*(3*4) ) % (4) */
908 : /* -------------------------------------------------------------------- */
909 :
910 266 : sprintf( szMetaName,"NETCDF_VARNAME");
911 266 : sprintf( szMetaTemp,"%s",szVarName);
912 266 : SetMetadataItem( szMetaName, szMetaTemp );
913 266 : if( nd == 3 ) {
914 17 : Sum *= panBandZLev[0];
915 : }
916 :
917 : /* -------------------------------------------------------------------- */
918 : /* Loop over non-spatial dimensions */
919 : /* -------------------------------------------------------------------- */
920 475 : for( i=0; i < nd-2 ; i++ ) {
921 :
922 209 : if( i != nd - 2 -1 ) {
923 104 : Sum = 1;
924 224 : for( j=i+1; j < nd-2; j++ ) {
925 120 : Sum *= panBandZLev[j];
926 : }
927 104 : result = (int) ( ( nLevel-Taken) / Sum );
928 : }
929 : else {
930 105 : result = (int) ( ( nLevel-Taken) % Sum );
931 : }
932 :
933 : strcpy(szVarName,
934 209 : poDS->papszDimName[paDimIds[panBandZPos[i]]] );
935 :
936 209 : status=nc_inq_varid( cdfid, szVarName, &nVarID );
937 209 : if( status != NC_NOERR ) {
938 : /* Try to uppercase the first letter of the variable */
939 : /* Note: why is this needed? leaving for safety */
940 48 : szVarName[0]=(char) toupper(szVarName[0]);
941 48 : status=nc_inq_varid( cdfid, szVarName, &nVarID );
942 : }
943 :
944 209 : status = nc_inq_vartype( cdfid, nVarID, &nVarType );
945 :
946 209 : nDims = 0;
947 209 : status = nc_inq_varndims( cdfid, nVarID, &nDims );
948 :
949 209 : if( nDims == 1 ) {
950 161 : count[0]=1;
951 161 : start[0]=result;
952 161 : switch( nVarType ) {
953 : case NC_SHORT:
954 : short sData;
955 : status = nc_get_vara_short( cdfid, nVarID,
956 : start,
957 0 : count, &sData );
958 0 : sprintf( szMetaTemp,"%d", sData );
959 0 : break;
960 : case NC_INT:
961 : int nData;
962 : status = nc_get_vara_int( cdfid, nVarID,
963 : start,
964 72 : count, &nData );
965 72 : sprintf( szMetaTemp,"%d", nData );
966 72 : break;
967 : case NC_FLOAT:
968 : float fData;
969 : status = nc_get_vara_float( cdfid, nVarID,
970 : start,
971 0 : count, &fData );
972 0 : sprintf( szMetaTemp,"%.8g", fData );
973 0 : break;
974 : case NC_DOUBLE:
975 : double dfData;
976 : status = nc_get_vara_double( cdfid, nVarID,
977 : start,
978 89 : count, &dfData);
979 89 : sprintf( szMetaTemp,"%.16g", dfData );
980 89 : break;
981 : default:
982 : CPLDebug( "GDAL_netCDF", "invalid dim %s, type=%d",
983 0 : szMetaTemp, nVarType);
984 : break;
985 : }
986 : }
987 : else
988 48 : sprintf( szMetaTemp,"%d", result+1);
989 :
990 : /* -------------------------------------------------------------------- */
991 : /* Save dimension value */
992 : /* -------------------------------------------------------------------- */
993 : /* NOTE: removed #original_units as not part of CF-1 */
994 209 : sprintf( szMetaName,"NETCDF_DIM_%s", szVarName );
995 209 : SetMetadataItem( szMetaName, szMetaTemp );
996 :
997 209 : Taken += result * Sum;
998 :
999 : } // end loop non-spatial dimensions
1000 :
1001 : /* -------------------------------------------------------------------- */
1002 : /* Get all other metadata */
1003 : /* -------------------------------------------------------------------- */
1004 266 : nc_inq_varnatts( cdfid, nZId, &nAtt );
1005 :
1006 1211 : for( i=0; i < nAtt ; i++ ) {
1007 :
1008 945 : status = nc_inq_attname( cdfid, nZId, i, szTemp);
1009 : // if(strcmp(szTemp,_FillValue) ==0) continue;
1010 945 : sprintf( szMetaName,"%s",szTemp);
1011 :
1012 945 : if ( NCDFGetAttr( cdfid, nZId, szMetaName, &pszMetaValue)
1013 : == CE_None ) {
1014 945 : SetMetadataItem( szMetaName, pszMetaValue );
1015 : }
1016 : else {
1017 0 : CPLDebug( "GDAL_netCDF", "invalid Band metadata %s", szMetaName );
1018 : }
1019 :
1020 945 : if ( pszMetaValue ) {
1021 945 : CPLFree( pszMetaValue );
1022 945 : pszMetaValue = NULL;
1023 : }
1024 :
1025 : }
1026 :
1027 266 : return CE_None;
1028 : }
1029 :
1030 : /************************************************************************/
1031 : /* CheckValidData() */
1032 : /************************************************************************/
1033 : template <class T>
1034 2406 : void netCDFRasterBand::CheckValidData ( void * pImage, int bCheckIsNan )
1035 : {
1036 : int i;
1037 2406 : CPLAssert( pImage != NULL );
1038 :
1039 : /* check if needed or requested */
1040 2406 : if ( (adfValidRange[0] != dfNoDataValue) ||
1041 : (adfValidRange[1] != dfNoDataValue) ||
1042 : bCheckIsNan ) {
1043 45678 : for( i=0; i<nBlockXSize; i++ ) {
1044 : /* check for nodata and nan */
1045 44793 : if ( CPLIsEqual( (double) ((T *)pImage)[i], dfNoDataValue ) )
1046 0 : continue;
1047 44793 : if( bCheckIsNan && CPLIsNan( (double) (( (T *) pImage))[i] ) ) {
1048 225 : ( (T *)pImage )[i] = (T)dfNoDataValue;
1049 225 : continue;
1050 : }
1051 : /* check for valid_range */
1052 44568 : if ( ( ( adfValidRange[0] != dfNoDataValue ) &&
1053 : ( ((T *)pImage)[i] < (T)adfValidRange[0] ) )
1054 : ||
1055 : ( ( adfValidRange[1] != dfNoDataValue ) &&
1056 : ( ((T *)pImage)[i] > (T)adfValidRange[1] ) ) ) {
1057 0 : ( (T *)pImage )[i] = (T)dfNoDataValue;
1058 : }
1059 : }
1060 : }
1061 :
1062 : /* if mininum longitude is > 180, subtract 360 from all */
1063 : /* if not, disable checking for further calls (check just once) */
1064 : /* only check first and last block elements since lon must be monotonic */
1065 2406 : if ( bCheckLongitude &&
1066 : MIN( ((T *)pImage)[0], ((T *)pImage)[nBlockXSize-1] ) > 180.0 ) {
1067 0 : for( i=0; i<nBlockXSize; i++ ) {
1068 0 : ((T *)pImage )[i] -= 360.0;
1069 : }
1070 : }
1071 : else
1072 2406 : bCheckLongitude = FALSE;
1073 :
1074 2406 : }
1075 :
1076 : /************************************************************************/
1077 : /* IReadBlock() */
1078 : /************************************************************************/
1079 :
1080 2406 : CPLErr netCDFRasterBand::IReadBlock( int nBlockXOff, int nBlockYOff,
1081 : void * pImage )
1082 :
1083 : {
1084 : size_t start[ MAX_NC_DIMS ];
1085 : size_t edge[ MAX_NC_DIMS ];
1086 : char pszName[ NCDF_MAX_STR_LEN ];
1087 : int i,j;
1088 2406 : int Sum=-1;
1089 2406 : int Taken=-1;
1090 : int nd;
1091 :
1092 2406 : CPLMutexHolderD(&hNCMutex);
1093 :
1094 : #ifdef NCDF_DEBUG
1095 : if ( (nBlockYOff == 0) || (nBlockYOff == nRasterYSize-1) )
1096 : CPLDebug( "GDAL_netCDF", "netCDFRasterBand::IReadBlock( %d, %d, ... ) nBand=%d",
1097 : nBlockXOff, nBlockYOff, nBand );
1098 : #endif
1099 :
1100 2406 : *pszName='\0';
1101 2406 : memset( start, 0, sizeof( start ) );
1102 2406 : memset( edge, 0, sizeof( edge ) );
1103 2406 : nc_inq_varndims ( cdfid, nZId, &nd );
1104 :
1105 : /* -------------------------------------------------------------------- */
1106 : /* Locate X, Y and Z position in the array */
1107 : /* -------------------------------------------------------------------- */
1108 :
1109 2406 : start[nBandXPos] = 0; // x dim can move arround in array
1110 : // check y order
1111 2406 : if( ( ( netCDFDataset *) poDS )->bBottomUp ) {
1112 2162 : start[nBandYPos] = nRasterYSize - 1 - nBlockYOff;
1113 : } else {
1114 244 : start[nBandYPos] = nBlockYOff; // y
1115 : }
1116 :
1117 2406 : edge[nBandXPos] = nBlockXSize;
1118 2406 : edge[nBandYPos] = 1;
1119 :
1120 2406 : if( nd == 3 ) {
1121 400 : start[panBandZPos[0]] = nLevel; // z
1122 400 : edge [panBandZPos[0]] = 1;
1123 : }
1124 :
1125 : /* -------------------------------------------------------------------- */
1126 : /* Compute multidimention band position */
1127 : /* */
1128 : /* BandPosition = (Total - sum(PastBandLevels) - 1)/sum(remainingLevels)*/
1129 : /* if Data[2,3,4,x,y] */
1130 : /* */
1131 : /* BandPos0 = (nBand ) / (3*4) */
1132 : /* BandPos1 = (nBand - (3*4) ) / (4) */
1133 : /* BandPos2 = (nBand - (3*4) ) % (4) */
1134 : /* -------------------------------------------------------------------- */
1135 2406 : if (nd > 3)
1136 : {
1137 160 : Taken = 0;
1138 480 : for( i=0; i < nd-2 ; i++ )
1139 : {
1140 320 : if( i != nd - 2 -1 ) {
1141 160 : Sum = 1;
1142 320 : for( j=i+1; j < nd-2; j++ ) {
1143 160 : Sum *= panBandZLev[j];
1144 : }
1145 160 : start[panBandZPos[i]] = (int) ( ( nLevel-Taken) / Sum );
1146 160 : edge[panBandZPos[i]] = 1;
1147 : } else {
1148 160 : start[panBandZPos[i]] = (int) ( ( nLevel-Taken) % Sum );
1149 160 : edge[panBandZPos[i]] = 1;
1150 : }
1151 320 : Taken += start[panBandZPos[i]] * Sum;
1152 : }
1153 : }
1154 :
1155 : /* make sure we are in data mode */
1156 2406 : ( ( netCDFDataset * ) poDS )->SetDefineMode( FALSE );
1157 :
1158 : /* read data according to type */
1159 2406 : if( eDataType == GDT_Byte )
1160 : {
1161 1081 : if (this->bSignedData)
1162 : {
1163 : status = nc_get_vara_schar( cdfid, nZId, start, edge,
1164 60 : (signed char *) pImage );
1165 60 : if ( status == NC_NOERR ) CheckValidData<signed char>( pImage );
1166 : }
1167 : else {
1168 : status = nc_get_vara_uchar( cdfid, nZId, start, edge,
1169 1021 : (unsigned char *) pImage );
1170 1021 : if ( status == NC_NOERR ) CheckValidData<unsigned char>( pImage );
1171 : }
1172 : }
1173 1325 : else if( eDataType == GDT_Int16 )
1174 : {
1175 : status = nc_get_vara_short( cdfid, nZId, start, edge,
1176 220 : (short int *) pImage );
1177 220 : if ( status == NC_NOERR ) CheckValidData<short int>( pImage );
1178 : }
1179 1105 : else if( eDataType == GDT_Int32 )
1180 : {
1181 : if( sizeof(long) == 4 )
1182 : {
1183 : status = nc_get_vara_long( cdfid, nZId, start, edge,
1184 : (long *) pImage );
1185 : if ( status == NC_NOERR ) CheckValidData<long>( pImage );
1186 : }
1187 : else
1188 : {
1189 : status = nc_get_vara_int( cdfid, nZId, start, edge,
1190 220 : (int *) pImage );
1191 220 : if ( status == NC_NOERR ) CheckValidData<int>( pImage );
1192 : }
1193 : }
1194 885 : else if( eDataType == GDT_Float32 )
1195 : {
1196 : status = nc_get_vara_float( cdfid, nZId, start, edge,
1197 825 : (float *) pImage );
1198 825 : if ( status == NC_NOERR ) CheckValidData<float>( pImage, TRUE );
1199 : }
1200 60 : else if( eDataType == GDT_Float64 )
1201 : {
1202 : status = nc_get_vara_double( cdfid, nZId, start, edge,
1203 60 : (double *) pImage );
1204 60 : if ( status == NC_NOERR ) CheckValidData<double>( pImage, TRUE );
1205 : }
1206 : else
1207 0 : status = NC_EBADTYPE;
1208 :
1209 2406 : if( status != NC_NOERR )
1210 : {
1211 : CPLError( CE_Failure, CPLE_AppDefined,
1212 : "netCDF scanline fetch failed: %s",
1213 0 : nc_strerror( status ) );
1214 0 : return CE_Failure;
1215 : }
1216 : else
1217 2406 : return CE_None;
1218 : }
1219 :
1220 : /************************************************************************/
1221 : /* IWriteBlock() */
1222 : /************************************************************************/
1223 :
1224 3678 : CPLErr netCDFRasterBand::IWriteBlock( int nBlockXOff, int nBlockYOff,
1225 : void * pImage )
1226 :
1227 : {
1228 : size_t start[ MAX_NC_DIMS];
1229 : size_t edge[ MAX_NC_DIMS ];
1230 : char pszName[ NCDF_MAX_STR_LEN ];
1231 : int i,j;
1232 3678 : int Sum=-1;
1233 3678 : int Taken=-1;
1234 : int nd;
1235 :
1236 3678 : CPLMutexHolderD(&hNCMutex);
1237 :
1238 : #ifdef NCDF_DEBUG
1239 : if ( (nBlockYOff == 0) || (nBlockYOff == nRasterYSize-1) )
1240 : CPLDebug( "GDAL_netCDF", "netCDFRasterBand::IWriteBlock( %d, %d, ... ) nBand=%d",
1241 : nBlockXOff, nBlockYOff, nBand );
1242 : #endif
1243 :
1244 3678 : *pszName='\0';
1245 3678 : memset( start, 0, sizeof( start ) );
1246 3678 : memset( edge, 0, sizeof( edge ) );
1247 3678 : nc_inq_varndims ( cdfid, nZId, &nd );
1248 :
1249 : /* -------------------------------------------------------------------- */
1250 : /* Locate X, Y and Z position in the array */
1251 : /* -------------------------------------------------------------------- */
1252 :
1253 3678 : start[nBandXPos] = 0; // x dim can move arround in array
1254 : // check y order
1255 3678 : if( ( ( netCDFDataset *) poDS )->bBottomUp ) {
1256 3658 : start[nBandYPos] = nRasterYSize - 1 - nBlockYOff;
1257 : } else {
1258 20 : start[nBandYPos] = nBlockYOff; // y
1259 : }
1260 :
1261 3678 : edge[nBandXPos] = nBlockXSize;
1262 3678 : edge[nBandYPos] = 1;
1263 :
1264 3678 : if( nd == 3 ) {
1265 120 : start[panBandZPos[0]] = nLevel; // z
1266 120 : edge [panBandZPos[0]] = 1;
1267 : }
1268 :
1269 : /* -------------------------------------------------------------------- */
1270 : /* Compute multidimention band position */
1271 : /* */
1272 : /* BandPosition = (Total - sum(PastBandLevels) - 1)/sum(remainingLevels)*/
1273 : /* if Data[2,3,4,x,y] */
1274 : /* */
1275 : /* BandPos0 = (nBand ) / (3*4) */
1276 : /* BandPos1 = (nBand - (3*4) ) / (4) */
1277 : /* BandPos2 = (nBand - (3*4) ) % (4) */
1278 : /* -------------------------------------------------------------------- */
1279 3678 : if (nd > 3)
1280 : {
1281 160 : Taken = 0;
1282 480 : for( i=0; i < nd-2 ; i++ )
1283 : {
1284 320 : if( i != nd - 2 -1 ) {
1285 160 : Sum = 1;
1286 320 : for( j=i+1; j < nd-2; j++ ) {
1287 160 : Sum *= panBandZLev[j];
1288 : }
1289 160 : start[panBandZPos[i]] = (int) ( ( nLevel-Taken) / Sum );
1290 160 : edge[panBandZPos[i]] = 1;
1291 : } else {
1292 160 : start[panBandZPos[i]] = (int) ( ( nLevel-Taken) % Sum );
1293 160 : edge[panBandZPos[i]] = 1;
1294 : }
1295 320 : Taken += start[panBandZPos[i]] * Sum;
1296 : }
1297 : }
1298 :
1299 : /* make sure we are in data mode */
1300 3678 : ( ( netCDFDataset * ) poDS )->SetDefineMode( FALSE );
1301 :
1302 : /* copy data according to type */
1303 3678 : if( eDataType == GDT_Byte ) {
1304 3067 : if ( this->bSignedData )
1305 : status = nc_put_vara_schar( cdfid, nZId, start, edge,
1306 40 : (signed char*) pImage);
1307 : else
1308 : status = nc_put_vara_uchar( cdfid, nZId, start, edge,
1309 3027 : (unsigned char*) pImage);
1310 : }
1311 711 : else if( ( eDataType == GDT_UInt16 ) || ( eDataType == GDT_Int16 ) ) {
1312 : status = nc_put_vara_short( cdfid, nZId, start, edge,
1313 100 : (short int *) pImage);
1314 : }
1315 511 : else if( eDataType == GDT_Int32 ) {
1316 : status = nc_put_vara_int( cdfid, nZId, start, edge,
1317 210 : (int *) pImage);
1318 : }
1319 301 : else if( eDataType == GDT_Float32 ) {
1320 : status = nc_put_vara_float( cdfid, nZId, start, edge,
1321 250 : (float *) pImage);
1322 : }
1323 51 : else if( eDataType == GDT_Float64 ) {
1324 : status = nc_put_vara_double( cdfid, nZId, start, edge,
1325 50 : (double *) pImage);
1326 : }
1327 : else {
1328 : CPLError( CE_Failure, CPLE_NotSupported,
1329 : "The NetCDF driver does not support GDAL data type %d",
1330 1 : eDataType );
1331 1 : status = NC_EBADTYPE;
1332 : }
1333 3678 : NCDF_ERR(status);
1334 :
1335 3678 : if( status != NC_NOERR )
1336 : {
1337 : CPLError( CE_Failure, CPLE_AppDefined,
1338 : "netCDF scanline write failed: %s",
1339 1 : nc_strerror( status ) );
1340 1 : return CE_Failure;
1341 : }
1342 : else
1343 3677 : return CE_None;
1344 :
1345 : }
1346 :
1347 : /************************************************************************/
1348 : /* ==================================================================== */
1349 : /* netCDFDataset */
1350 : /* ==================================================================== */
1351 : /************************************************************************/
1352 :
1353 : /************************************************************************/
1354 : /* netCDFDataset() */
1355 : /************************************************************************/
1356 :
1357 322 : netCDFDataset::netCDFDataset()
1358 :
1359 : {
1360 : /* basic dataset vars */
1361 322 : cdfid = -1;
1362 322 : papszSubDatasets = NULL;
1363 322 : papszMetadata = NULL;
1364 322 : bBottomUp = TRUE;
1365 322 : nFormat = NCDF_FORMAT_NONE;
1366 322 : bIsGdalFile = FALSE;
1367 322 : bIsGdalCfFile = FALSE;
1368 :
1369 : /* projection/GT */
1370 322 : adfGeoTransform[0] = 0.0;
1371 322 : adfGeoTransform[1] = 1.0;
1372 322 : adfGeoTransform[2] = 0.0;
1373 322 : adfGeoTransform[3] = 0.0;
1374 322 : adfGeoTransform[4] = 0.0;
1375 322 : adfGeoTransform[5] = 1.0;
1376 322 : pszProjection = NULL;
1377 322 : nXDimID = -1;
1378 322 : nYDimID = -1;
1379 322 : bIsProjected = FALSE;
1380 322 : bIsGeographic = FALSE; /* can be not projected, and also not geographic */
1381 322 : pszCFProjection = NULL;
1382 322 : pszCFCoordinates = NULL;
1383 :
1384 : /* state vars */
1385 322 : status = NC_NOERR;
1386 322 : bDefineMode = TRUE;
1387 322 : bSetProjection = FALSE;
1388 322 : bSetGeoTransform = FALSE;
1389 322 : bAddedProjectionVars = FALSE;
1390 322 : bAddedGridMappingRef = FALSE;
1391 :
1392 : /* create vars */
1393 322 : papszCreationOptions = NULL;
1394 322 : nCompress = NCDF_COMPRESS_NONE;
1395 322 : nZLevel = NCDF_DEFLATE_LEVEL;
1396 322 : nCreateMode = NC_CLOBBER;
1397 322 : bSignedData = TRUE;
1398 322 : }
1399 :
1400 :
1401 : /************************************************************************/
1402 : /* ~netCDFDataset() */
1403 : /************************************************************************/
1404 :
1405 322 : netCDFDataset::~netCDFDataset()
1406 :
1407 : {
1408 322 : CPLMutexHolderD(&hNCMutex);
1409 :
1410 : #ifdef NCDF_DEBUG
1411 : CPLDebug( "GDAL_netCDF", "netCDFDataset::~netCDFDataset(), cdfid=%d",
1412 : cdfid );
1413 : #endif
1414 : /* make sure projection is written if GeoTransform OR Projection are missing */
1415 322 : if( (GetAccess() == GA_Update) && (! bAddedProjectionVars) ) {
1416 26 : if ( bSetProjection && ! bSetGeoTransform )
1417 0 : AddProjectionVars();
1418 26 : else if ( bSetGeoTransform && ! bSetProjection )
1419 1 : AddProjectionVars();
1420 : // CPLError( CE_Warning, CPLE_AppDefined,
1421 : // "netCDFDataset::~netCDFDataset() Projection was not defined, projection will be missing" );
1422 : }
1423 :
1424 322 : FlushCache();
1425 :
1426 : /* make sure projection variable is written to band variable */
1427 322 : if( (GetAccess() == GA_Update) && ! bAddedGridMappingRef )
1428 35 : AddGridMappingRef();
1429 :
1430 322 : CSLDestroy( papszMetadata );
1431 322 : CSLDestroy( papszSubDatasets );
1432 322 : CSLDestroy( papszCreationOptions );
1433 :
1434 322 : if( pszProjection )
1435 206 : CPLFree( pszProjection );
1436 322 : if( pszCFProjection )
1437 80 : CPLFree( pszCFProjection );
1438 322 : if( pszCFCoordinates )
1439 0 : CPLFree( pszCFCoordinates );
1440 :
1441 322 : if( cdfid ) {
1442 : #ifdef NCDF_DEBUG
1443 : CPLDebug( "GDAL_netCDF", "calling nc_close( %d )", cdfid );
1444 : #endif
1445 322 : status = nc_close( cdfid );
1446 322 : NCDF_ERR(status);
1447 322 : }
1448 :
1449 322 : }
1450 :
1451 : /************************************************************************/
1452 : /* SetDefineMode() */
1453 : /************************************************************************/
1454 6970 : int netCDFDataset::SetDefineMode( int bNewDefineMode )
1455 : {
1456 : /* do nothing if already in new define mode
1457 : or if dataset is in read-only mode */
1458 6970 : if ( ( bDefineMode == bNewDefineMode ) ||
1459 : ( GetAccess() == GA_ReadOnly ) )
1460 6757 : return CE_None;
1461 :
1462 : CPLDebug( "GDAL_netCDF", "SetDefineMode(%d) old=%d",
1463 213 : bNewDefineMode, bDefineMode );
1464 :
1465 213 : bDefineMode = bNewDefineMode;
1466 :
1467 213 : if ( bDefineMode == TRUE )
1468 59 : status = nc_redef( cdfid );
1469 : else
1470 154 : status = nc_enddef( cdfid );
1471 :
1472 213 : NCDF_ERR(status);
1473 213 : return status;
1474 : }
1475 :
1476 : /************************************************************************/
1477 : /* GetMetadata() */
1478 : /************************************************************************/
1479 137 : char **netCDFDataset::GetMetadata( const char *pszDomain )
1480 : {
1481 137 : if( pszDomain != NULL && EQUALN( pszDomain, "SUBDATASETS", 11 ) )
1482 2 : return papszSubDatasets;
1483 : else
1484 135 : return GDALDataset::GetMetadata( pszDomain );
1485 : }
1486 :
1487 : /************************************************************************/
1488 : /* GetProjectionRef() */
1489 : /************************************************************************/
1490 :
1491 83 : const char * netCDFDataset::GetProjectionRef()
1492 : {
1493 83 : if( bSetProjection )
1494 57 : return pszProjection;
1495 : else
1496 26 : return GDALPamDataset::GetProjectionRef();
1497 : }
1498 :
1499 : /************************************************************************/
1500 : /* SerializeToXML() */
1501 : /************************************************************************/
1502 :
1503 2 : CPLXMLNode *netCDFDataset::SerializeToXML( const char *pszUnused )
1504 :
1505 : {
1506 : /* -------------------------------------------------------------------- */
1507 : /* Overriden from GDALPamDataset to add only band histogram */
1508 : /* and statistics. See bug #4244. */
1509 : /* -------------------------------------------------------------------- */
1510 :
1511 2 : CPLString oFmt;
1512 :
1513 2 : if( psPam == NULL )
1514 0 : return NULL;
1515 :
1516 : /* -------------------------------------------------------------------- */
1517 : /* Setup root node and attributes. */
1518 : /* -------------------------------------------------------------------- */
1519 : CPLXMLNode *psDSTree;
1520 :
1521 2 : psDSTree = CPLCreateXMLNode( NULL, CXT_Element, "PAMDataset" );
1522 :
1523 : /* -------------------------------------------------------------------- */
1524 : /* Process bands. */
1525 : /* -------------------------------------------------------------------- */
1526 : int iBand;
1527 :
1528 4 : for( iBand = 0; iBand < GetRasterCount(); iBand++ )
1529 : {
1530 : CPLXMLNode *psBandTree;
1531 :
1532 : netCDFRasterBand *poBand = (netCDFRasterBand *)
1533 2 : GetRasterBand(iBand+1);
1534 :
1535 2 : if( poBand == NULL || !(poBand->GetMOFlags() & GMO_PAM_CLASS) )
1536 0 : continue;
1537 :
1538 2 : psBandTree = poBand->SerializeToXML( pszUnused );
1539 :
1540 2 : if( psBandTree != NULL )
1541 0 : CPLAddXMLChild( psDSTree, psBandTree );
1542 : }
1543 :
1544 : /* -------------------------------------------------------------------- */
1545 : /* We don't want to return anything if we had no metadata to */
1546 : /* attach. */
1547 : /* -------------------------------------------------------------------- */
1548 2 : if( psDSTree->psChild == NULL )
1549 : {
1550 2 : CPLDestroyXMLNode( psDSTree );
1551 2 : psDSTree = NULL;
1552 : }
1553 :
1554 2 : return psDSTree;
1555 : }
1556 :
1557 : /************************************************************************/
1558 : /* FetchCopyParm() */
1559 : /************************************************************************/
1560 :
1561 1012 : double netCDFDataset::FetchCopyParm( const char *pszGridMappingValue,
1562 : const char *pszParm, double dfDefault )
1563 :
1564 : {
1565 : char szTemp[ MAX_NC_NAME ];
1566 : const char *pszValue;
1567 :
1568 1012 : strcpy(szTemp,pszGridMappingValue);
1569 1012 : strcat( szTemp, "#" );
1570 1012 : strcat( szTemp, pszParm );
1571 1012 : pszValue = CSLFetchNameValue(papszMetadata, szTemp);
1572 :
1573 1012 : if( pszValue )
1574 : {
1575 730 : return CPLAtofM(pszValue);
1576 : }
1577 : else
1578 282 : return dfDefault;
1579 : }
1580 :
1581 : /************************************************************************/
1582 : /* FetchStandardParallels() */
1583 : /************************************************************************/
1584 :
1585 45 : char** netCDFDataset::FetchStandardParallels( const char *pszGridMappingValue )
1586 : {
1587 : char szTemp[ MAX_NC_NAME ];
1588 : const char *pszValue;
1589 45 : char **papszValues = NULL;
1590 : //cf-1.0 tags
1591 45 : strcpy( szTemp,pszGridMappingValue );
1592 45 : strcat( szTemp, "#" );
1593 45 : strcat( szTemp, CF_PP_STD_PARALLEL );
1594 45 : pszValue = CSLFetchNameValue( papszMetadata, szTemp );
1595 45 : if( pszValue != NULL ) {
1596 38 : papszValues = NCDFTokenizeArray( pszValue );
1597 : }
1598 : //try gdal tags
1599 : else
1600 : {
1601 7 : strcpy( szTemp, pszGridMappingValue );
1602 7 : strcat( szTemp, "#" );
1603 7 : strcat( szTemp, CF_PP_STD_PARALLEL_1 );
1604 :
1605 7 : pszValue = CSLFetchNameValue( papszMetadata, szTemp );
1606 :
1607 7 : if ( pszValue != NULL )
1608 0 : papszValues = CSLAddString( papszValues, pszValue );
1609 :
1610 7 : strcpy( szTemp,pszGridMappingValue );
1611 7 : strcat( szTemp, "#" );
1612 7 : strcat( szTemp, CF_PP_STD_PARALLEL_2 );
1613 :
1614 7 : pszValue = CSLFetchNameValue( papszMetadata, szTemp );
1615 :
1616 7 : if( pszValue != NULL )
1617 0 : papszValues = CSLAddString( papszValues, pszValue );
1618 : }
1619 :
1620 45 : return papszValues;
1621 : }
1622 :
1623 : /************************************************************************/
1624 : /* SetProjectionFromVar() */
1625 : /************************************************************************/
1626 189 : void netCDFDataset::SetProjectionFromVar( int nVarId )
1627 : {
1628 : size_t start[2], edge[2];
1629 189 : unsigned int i=0;
1630 189 : const char *pszValue = NULL;
1631 189 : int nVarProjectionID = -1;
1632 : char szVarName[ MAX_NC_NAME ];
1633 : char szTemp[ MAX_NC_NAME ];
1634 : char szGridMappingName[ MAX_NC_NAME ];
1635 : char szGridMappingValue[ MAX_NC_NAME ];
1636 :
1637 189 : double dfStdP1=0.0;
1638 189 : double dfStdP2=0.0;
1639 189 : double dfCenterLat=0.0;
1640 189 : double dfCenterLon=0.0;
1641 189 : double dfScale=1.0;
1642 189 : double dfFalseEasting=0.0;
1643 189 : double dfFalseNorthing=0.0;
1644 189 : double dfCentralMeridian=0.0;
1645 189 : double dfEarthRadius=0.0;
1646 189 : double dfInverseFlattening=0.0;
1647 189 : double dfLonPrimeMeridian=0.0;
1648 189 : const char *pszPMName=NULL;
1649 189 : double dfSemiMajorAxis=0.0;
1650 189 : double dfSemiMinorAxis=0.0;
1651 :
1652 189 : int bGotGeogCS = FALSE;
1653 189 : int bGotCfSRS = FALSE;
1654 189 : int bGotGdalSRS = FALSE;
1655 189 : int bGotCfGT = FALSE;
1656 189 : int bGotGdalGT = FALSE;
1657 189 : int bLookForWellKnownGCS = FALSE; //this could be a Config Option
1658 :
1659 : /* These values from CF metadata */
1660 189 : OGRSpatialReference oSRS;
1661 189 : int nVarDimXID = -1;
1662 189 : int nVarDimYID = -1;
1663 189 : double *pdfXCoord = NULL;
1664 189 : double *pdfYCoord = NULL;
1665 : char szDimNameX[ MAX_NC_NAME ];
1666 : char szDimNameY[ MAX_NC_NAME ];
1667 189 : int nSpacingBegin=0;
1668 189 : int nSpacingMiddle=0;
1669 189 : int nSpacingLast=0;
1670 189 : int bLatSpacingOK=FALSE;
1671 189 : int bLonSpacingOK=FALSE;
1672 189 : size_t xdim = nRasterXSize;
1673 189 : size_t ydim = nRasterYSize;
1674 :
1675 189 : const char *pszUnits = NULL;
1676 :
1677 : /* These values from GDAL metadata */
1678 189 : const char *pszWKT = NULL;
1679 189 : const char *pszGeoTransform = NULL;
1680 189 : char **papszGeoTransform = NULL;
1681 :
1682 189 : netCDFDataset * poDS = this; /* perhaps this should be removed for clarity */
1683 :
1684 : /* temp variables to use in SetGeoTransform() and SetProjection() */
1685 : double adfTempGeoTransform[6];
1686 : char *pszTempProjection;
1687 :
1688 189 : CPLDebug( "GDAL_netCDF", "\n=====\nSetProjectionFromVar( %d )\n", nVarId );
1689 :
1690 : /* -------------------------------------------------------------------- */
1691 : /* Get x/y range information. */
1692 : /* -------------------------------------------------------------------- */
1693 :
1694 189 : adfTempGeoTransform[0] = 0.0;
1695 189 : adfTempGeoTransform[1] = 1.0;
1696 189 : adfTempGeoTransform[2] = 0.0;
1697 189 : adfTempGeoTransform[3] = 0.0;
1698 189 : adfTempGeoTransform[4] = 0.0;
1699 189 : adfTempGeoTransform[5] = 1.0;
1700 189 : pszTempProjection = NULL;
1701 :
1702 189 : if ( xdim == 1 || ydim == 1 ) {
1703 : CPLError( CE_Warning, CPLE_AppDefined,
1704 : "1-pixel width/height files not supported, xdim: %ld ydim: %ld",
1705 0 : (long)xdim, (long)ydim );
1706 : return;
1707 : }
1708 :
1709 : /* -------------------------------------------------------------------- */
1710 : /* Look for grid_mapping metadata */
1711 : /* -------------------------------------------------------------------- */
1712 :
1713 189 : strcpy( szGridMappingValue, "" );
1714 189 : strcpy( szGridMappingName, "" );
1715 :
1716 189 : nc_inq_varname( cdfid, nVarId, szVarName );
1717 189 : strcpy(szTemp,szVarName);
1718 189 : strcat(szTemp,"#");
1719 189 : strcat(szTemp,CF_GRD_MAPPING);
1720 :
1721 189 : pszValue = CSLFetchNameValue(poDS->papszMetadata, szTemp);
1722 189 : if( pszValue ) {
1723 118 : strcpy(szGridMappingName,szTemp);
1724 118 : strcpy(szGridMappingValue,pszValue);
1725 : }
1726 :
1727 189 : if( !EQUAL( szGridMappingValue, "" ) ) {
1728 :
1729 : /* Read grid_mapping metadata */
1730 118 : nc_inq_varid( cdfid, szGridMappingValue, &nVarProjectionID );
1731 118 : poDS->ReadAttributes( cdfid, nVarProjectionID );
1732 :
1733 : /* -------------------------------------------------------------------- */
1734 : /* Look for GDAL spatial_ref and GeoTransform within grid_mapping */
1735 : /* -------------------------------------------------------------------- */
1736 118 : CPLDebug( "GDAL_netCDF", "got grid_mapping %s", szGridMappingValue );
1737 118 : strcpy( szTemp,szGridMappingValue);
1738 118 : strcat( szTemp, "#" );
1739 118 : strcat( szTemp, NCDF_SPATIAL_REF);
1740 :
1741 118 : pszWKT = CSLFetchNameValue(poDS->papszMetadata, szTemp);
1742 :
1743 118 : if( pszWKT != NULL ) {
1744 111 : strcpy( szTemp,szGridMappingValue);
1745 111 : strcat( szTemp, "#" );
1746 111 : strcat( szTemp, NCDF_GEOTRANSFORM);
1747 111 : pszGeoTransform = CSLFetchNameValue(poDS->papszMetadata, szTemp);
1748 : }
1749 : }
1750 :
1751 : /* -------------------------------------------------------------------- */
1752 : /* Get information about the file. */
1753 : /* -------------------------------------------------------------------- */
1754 : /* Was this file created by the GDAL netcdf driver? */
1755 : /* Was this file created by the newer (CF-conformant) driver? */
1756 : /* -------------------------------------------------------------------- */
1757 : /* 1) If GDAL netcdf metadata is set, and version >= 1.9, */
1758 : /* it was created with the new driver */
1759 : /* 2) Else, if spatial_ref and GeoTransform are present in the */
1760 : /* grid_mapping variable, it was created by the old driver */
1761 : /* -------------------------------------------------------------------- */
1762 189 : pszValue = CSLFetchNameValue(poDS->papszMetadata, "NC_GLOBAL#GDAL");
1763 :
1764 189 : if( pszValue && NCDFIsGDALVersionGTE(pszValue, 1900)) {
1765 159 : bIsGdalFile = TRUE;
1766 159 : bIsGdalCfFile = TRUE;
1767 : }
1768 30 : else if( pszWKT != NULL && pszGeoTransform != NULL ) {
1769 0 : bIsGdalFile = TRUE;
1770 0 : bIsGdalCfFile = FALSE;
1771 : }
1772 :
1773 : /* -------------------------------------------------------------------- */
1774 : /* Set default bottom-up default value */
1775 : /* Y axis dimension and absence of GT can modify this value */
1776 : /* Override with Config option GDAL_NETCDF_BOTTOMUP */
1777 : /* -------------------------------------------------------------------- */
1778 : /* new driver is bottom-up by default */
1779 189 : if ( bIsGdalFile && ! bIsGdalCfFile )
1780 0 : poDS->bBottomUp = FALSE;
1781 : else
1782 189 : poDS->bBottomUp = TRUE;
1783 :
1784 : CPLDebug( "GDAL_netCDF",
1785 : "bIsGdalFile=%d bIsGdalCfFile=%d bBottomUp=%d",
1786 189 : bIsGdalFile, bIsGdalCfFile, bBottomUp );
1787 :
1788 : /* -------------------------------------------------------------------- */
1789 : /* Look for dimension: lon */
1790 : /* -------------------------------------------------------------------- */
1791 :
1792 189 : memset( szDimNameX, '\0', sizeof( char ) * MAX_NC_NAME );
1793 189 : memset( szDimNameY, '\0', sizeof( char ) * MAX_NC_NAME );
1794 :
1795 486 : for( i = 0; (i < strlen( poDS->papszDimName[ poDS->nXDimID ] ) &&
1796 : i < 3 ); i++ ) {
1797 297 : szDimNameX[i]=(char)tolower( ( poDS->papszDimName[poDS->nXDimID] )[i] );
1798 : }
1799 189 : szDimNameX[3] = '\0';
1800 486 : for( i = 0; (i < strlen( poDS->papszDimName[ poDS->nYDimID ] ) &&
1801 : i < 3 ); i++ ) {
1802 297 : szDimNameY[i]=(char)tolower( ( poDS->papszDimName[poDS->nYDimID] )[i] );
1803 : }
1804 189 : szDimNameY[3] = '\0';
1805 :
1806 : /* -------------------------------------------------------------------- */
1807 : /* Read grid_mapping information and set projections */
1808 : /* -------------------------------------------------------------------- */
1809 :
1810 189 : if( !( EQUAL(szGridMappingName,"" ) ) ) {
1811 :
1812 118 : strcpy( szTemp, szGridMappingValue );
1813 118 : strcat( szTemp, "#" );
1814 118 : strcat( szTemp, CF_GRD_MAPPING_NAME );
1815 118 : pszValue = CSLFetchNameValue(poDS->papszMetadata, szTemp);
1816 :
1817 118 : if( pszValue != NULL ) {
1818 :
1819 : /* -------------------------------------------------------------------- */
1820 : /* Check for datum/spheroid information */
1821 : /* -------------------------------------------------------------------- */
1822 : dfEarthRadius =
1823 : poDS->FetchCopyParm( szGridMappingValue,
1824 : CF_PP_EARTH_RADIUS,
1825 118 : -1.0 );
1826 :
1827 : dfLonPrimeMeridian =
1828 : poDS->FetchCopyParm( szGridMappingValue,
1829 : CF_PP_LONG_PRIME_MERIDIAN,
1830 118 : 0.0 );
1831 : // should try to find PM name from its value if not Greenwich
1832 118 : if ( ! CPLIsEqual(dfLonPrimeMeridian,0.0) )
1833 0 : pszPMName = "unknown";
1834 :
1835 : dfInverseFlattening =
1836 : poDS->FetchCopyParm( szGridMappingValue,
1837 : CF_PP_INVERSE_FLATTENING,
1838 118 : -1.0 );
1839 :
1840 : dfSemiMajorAxis =
1841 : poDS->FetchCopyParm( szGridMappingValue,
1842 : CF_PP_SEMI_MAJOR_AXIS,
1843 118 : -1.0 );
1844 :
1845 : dfSemiMinorAxis =
1846 : poDS->FetchCopyParm( szGridMappingValue,
1847 : CF_PP_SEMI_MINOR_AXIS,
1848 118 : -1.0 );
1849 :
1850 : //see if semi-major exists if radius doesn't
1851 118 : if( dfEarthRadius < 0.0 )
1852 118 : dfEarthRadius = dfSemiMajorAxis;
1853 :
1854 : //if still no radius, check old tag
1855 118 : if( dfEarthRadius < 0.0 )
1856 : dfEarthRadius = poDS->FetchCopyParm( szGridMappingValue,
1857 : CF_PP_EARTH_RADIUS_OLD,
1858 7 : -1.0 );
1859 :
1860 : //has radius value
1861 118 : if( dfEarthRadius > 0.0 ) {
1862 : //check for inv_flat tag
1863 114 : if( dfInverseFlattening < 0.0 ) {
1864 : //no inv_flat tag, check for semi_minor
1865 2 : if( dfSemiMinorAxis < 0.0 ) {
1866 : //no way to get inv_flat, use sphere
1867 : oSRS.SetGeogCS( "unknown",
1868 : NULL,
1869 : "Sphere",
1870 : dfEarthRadius, 0.0,
1871 2 : pszPMName, dfLonPrimeMeridian );
1872 2 : bGotGeogCS = TRUE;
1873 : }
1874 : else {
1875 0 : if( dfSemiMajorAxis < 0.0 )
1876 0 : dfSemiMajorAxis = dfEarthRadius;
1877 : //set inv_flat using semi_minor/major
1878 : dfInverseFlattening =
1879 0 : 1.0 / ( dfSemiMajorAxis - dfSemiMinorAxis ) / dfSemiMajorAxis;
1880 : oSRS.SetGeogCS( "unknown",
1881 : NULL,
1882 : "Spheroid",
1883 : dfEarthRadius, dfInverseFlattening,
1884 0 : pszPMName, dfLonPrimeMeridian );
1885 0 : bGotGeogCS = TRUE;
1886 : }
1887 : }
1888 : else {
1889 : oSRS.SetGeogCS( "unknown",
1890 : NULL,
1891 : "Spheroid",
1892 : dfEarthRadius, dfInverseFlattening,
1893 112 : pszPMName, dfLonPrimeMeridian );
1894 112 : bGotGeogCS = TRUE;
1895 : }
1896 :
1897 114 : if ( bGotGeogCS )
1898 114 : CPLDebug( "GDAL_netCDF", "got spheroid from CF: (%f , %f)", dfEarthRadius, dfInverseFlattening );
1899 :
1900 : }
1901 : //no radius, set as wgs84 as default?
1902 : else {
1903 : // This would be too indiscrimant. But we should set
1904 : // it if we know the data is geographic.
1905 : //oSRS.SetWellKnownGeogCS( "WGS84" );
1906 : }
1907 :
1908 : /* -------------------------------------------------------------------- */
1909 : /* Transverse Mercator */
1910 : /* -------------------------------------------------------------------- */
1911 :
1912 118 : if( EQUAL( pszValue, CF_PT_TM ) ) {
1913 :
1914 : dfScale =
1915 : poDS->FetchCopyParm( szGridMappingValue,
1916 26 : CF_PP_SCALE_FACTOR_MERIDIAN, 1.0 );
1917 :
1918 : dfCenterLon =
1919 : poDS->FetchCopyParm( szGridMappingValue,
1920 26 : CF_PP_LONG_CENTRAL_MERIDIAN, 0.0 );
1921 :
1922 : dfCenterLat =
1923 : poDS->FetchCopyParm( szGridMappingValue,
1924 26 : CF_PP_LAT_PROJ_ORIGIN, 0.0 );
1925 :
1926 : dfFalseEasting =
1927 : poDS->FetchCopyParm( szGridMappingValue,
1928 26 : CF_PP_FALSE_EASTING, 0.0 );
1929 :
1930 : dfFalseNorthing =
1931 : poDS->FetchCopyParm( szGridMappingValue,
1932 26 : CF_PP_FALSE_NORTHING, 0.0 );
1933 :
1934 26 : bGotCfSRS = TRUE;
1935 : oSRS.SetTM( dfCenterLat,
1936 : dfCenterLon,
1937 : dfScale,
1938 : dfFalseEasting,
1939 26 : dfFalseNorthing );
1940 :
1941 26 : if( !bGotGeogCS )
1942 0 : oSRS.SetWellKnownGeogCS( "WGS84" );
1943 : }
1944 :
1945 : /* -------------------------------------------------------------------- */
1946 : /* Albers Equal Area */
1947 : /* -------------------------------------------------------------------- */
1948 :
1949 118 : if( EQUAL( pszValue, CF_PT_AEA ) ) {
1950 :
1951 7 : char **papszStdParallels = NULL;
1952 :
1953 : dfCenterLon =
1954 : poDS->FetchCopyParm( szGridMappingValue,
1955 7 : CF_PP_LONG_CENTRAL_MERIDIAN, 0.0 );
1956 :
1957 : dfCenterLat =
1958 : poDS->FetchCopyParm( szGridMappingValue,
1959 7 : CF_PP_LAT_PROJ_ORIGIN, 0.0 );
1960 :
1961 : dfFalseEasting =
1962 : poDS->FetchCopyParm( szGridMappingValue,
1963 7 : CF_PP_FALSE_EASTING, 0.0 );
1964 :
1965 : dfFalseNorthing =
1966 : poDS->FetchCopyParm( szGridMappingValue,
1967 7 : CF_PP_FALSE_NORTHING, 0.0 );
1968 :
1969 : papszStdParallels =
1970 7 : FetchStandardParallels( szGridMappingValue );
1971 :
1972 7 : if( papszStdParallels != NULL ) {
1973 :
1974 7 : if ( CSLCount( papszStdParallels ) == 1 ) {
1975 : /* TODO CF-1 standard says it allows AEA to be encoded with only 1 standard parallel */
1976 : /* how should this actually map to a 2StdP OGC WKT version? */
1977 : CPLError( CE_Warning, CPLE_NotSupported,
1978 0 : "NetCDF driver import of AEA-1SP is not tested, using identical std. parallels\n" );
1979 0 : dfStdP1 = CPLAtofM( papszStdParallels[0] );
1980 0 : dfStdP2 = dfStdP1;
1981 :
1982 : }
1983 :
1984 7 : else if( CSLCount( papszStdParallels ) == 2 ) {
1985 7 : dfStdP1 = CPLAtofM( papszStdParallels[0] );
1986 7 : dfStdP2 = CPLAtofM( papszStdParallels[1] );
1987 : }
1988 : }
1989 : //old default
1990 : else {
1991 : dfStdP1 =
1992 : poDS->FetchCopyParm( szGridMappingValue,
1993 0 : CF_PP_STD_PARALLEL_1, 0.0 );
1994 :
1995 : dfStdP2 =
1996 : poDS->FetchCopyParm( szGridMappingValue,
1997 0 : CF_PP_STD_PARALLEL_2, 0.0 );
1998 : }
1999 :
2000 : dfCenterLat =
2001 : poDS->FetchCopyParm( szGridMappingValue,
2002 7 : CF_PP_LAT_PROJ_ORIGIN, 0.0 );
2003 :
2004 7 : bGotCfSRS = TRUE;
2005 : oSRS.SetACEA( dfStdP1, dfStdP2, dfCenterLat, dfCenterLon,
2006 7 : dfFalseEasting, dfFalseNorthing );
2007 :
2008 7 : if( !bGotGeogCS )
2009 0 : oSRS.SetWellKnownGeogCS( "WGS84" );
2010 :
2011 7 : CSLDestroy( papszStdParallels );
2012 : }
2013 :
2014 : /* -------------------------------------------------------------------- */
2015 : /* Cylindrical Equal Area */
2016 : /* -------------------------------------------------------------------- */
2017 :
2018 117 : else if( EQUAL( pszValue, CF_PT_CEA ) || EQUAL( pszValue, CF_PT_LCEA ) ) {
2019 :
2020 6 : char **papszStdParallels = NULL;
2021 :
2022 : papszStdParallels =
2023 6 : FetchStandardParallels( szGridMappingValue );
2024 :
2025 6 : if( papszStdParallels != NULL ) {
2026 6 : dfStdP1 = CPLAtofM( papszStdParallels[0] );
2027 : }
2028 : else {
2029 : //TODO: add support for 'scale_factor_at_projection_origin' variant to standard parallel
2030 : //Probably then need to calc a std parallel equivalent
2031 : CPLError( CE_Failure, CPLE_NotSupported,
2032 : "NetCDF driver does not support import of CF-1 LCEA "
2033 0 : "'scale_factor_at_projection_origin' variant yet.\n" );
2034 : }
2035 :
2036 : dfCentralMeridian =
2037 : poDS->FetchCopyParm( szGridMappingValue,
2038 6 : CF_PP_LONG_CENTRAL_MERIDIAN, 0.0 );
2039 :
2040 : dfFalseEasting =
2041 : poDS->FetchCopyParm( szGridMappingValue,
2042 6 : CF_PP_FALSE_EASTING, 0.0 );
2043 :
2044 : dfFalseNorthing =
2045 : poDS->FetchCopyParm( szGridMappingValue,
2046 6 : CF_PP_FALSE_NORTHING, 0.0 );
2047 :
2048 6 : bGotCfSRS = TRUE;
2049 : oSRS.SetCEA( dfStdP1, dfCentralMeridian,
2050 6 : dfFalseEasting, dfFalseNorthing );
2051 :
2052 6 : if( !bGotGeogCS )
2053 0 : oSRS.SetWellKnownGeogCS( "WGS84" );
2054 :
2055 6 : CSLDestroy( papszStdParallels );
2056 : }
2057 :
2058 : /* -------------------------------------------------------------------- */
2059 : /* lambert_azimuthal_equal_area */
2060 : /* -------------------------------------------------------------------- */
2061 105 : else if( EQUAL( pszValue, CF_PT_LAEA ) ) {
2062 : dfCenterLon =
2063 : poDS->FetchCopyParm( szGridMappingValue,
2064 6 : CF_PP_LON_PROJ_ORIGIN, 0.0 );
2065 :
2066 : dfCenterLat =
2067 : poDS->FetchCopyParm( szGridMappingValue,
2068 6 : CF_PP_LAT_PROJ_ORIGIN, 0.0 );
2069 :
2070 : dfFalseEasting =
2071 : poDS->FetchCopyParm( szGridMappingValue,
2072 6 : CF_PP_FALSE_EASTING, 0.0 );
2073 :
2074 : dfFalseNorthing =
2075 : poDS->FetchCopyParm( szGridMappingValue,
2076 6 : CF_PP_FALSE_NORTHING, 0.0 );
2077 :
2078 6 : oSRS.SetProjCS( "LAEA (WGS84) " );
2079 :
2080 6 : bGotCfSRS = TRUE;
2081 : oSRS.SetLAEA( dfCenterLat, dfCenterLon,
2082 6 : dfFalseEasting, dfFalseNorthing );
2083 :
2084 6 : if( !bGotGeogCS )
2085 0 : oSRS.SetWellKnownGeogCS( "WGS84" );
2086 :
2087 : }
2088 :
2089 : /* -------------------------------------------------------------------- */
2090 : /* Azimuthal Equidistant */
2091 : /* -------------------------------------------------------------------- */
2092 99 : else if( EQUAL( pszValue, CF_PT_AE ) ) {
2093 : dfCenterLon =
2094 : poDS->FetchCopyParm( szGridMappingValue,
2095 6 : CF_PP_LON_PROJ_ORIGIN, 0.0 );
2096 :
2097 : dfCenterLat =
2098 : poDS->FetchCopyParm( szGridMappingValue,
2099 6 : CF_PP_LAT_PROJ_ORIGIN, 0.0 );
2100 :
2101 : dfFalseEasting =
2102 : poDS->FetchCopyParm( szGridMappingValue,
2103 6 : CF_PP_FALSE_EASTING, 0.0 );
2104 :
2105 : dfFalseNorthing =
2106 : poDS->FetchCopyParm( szGridMappingValue,
2107 6 : CF_PP_FALSE_NORTHING, 0.0 );
2108 :
2109 6 : bGotCfSRS = TRUE;
2110 : oSRS.SetAE( dfCenterLat, dfCenterLon,
2111 6 : dfFalseEasting, dfFalseNorthing );
2112 :
2113 6 : if( !bGotGeogCS )
2114 0 : oSRS.SetWellKnownGeogCS( "WGS84" );
2115 :
2116 : }
2117 :
2118 : /* -------------------------------------------------------------------- */
2119 : /* Lambert conformal conic */
2120 : /* -------------------------------------------------------------------- */
2121 93 : else if( EQUAL( pszValue, CF_PT_LCC ) ) {
2122 :
2123 12 : char **papszStdParallels = NULL;
2124 :
2125 : dfCenterLon =
2126 : poDS->FetchCopyParm( szGridMappingValue,
2127 12 : CF_PP_LONG_CENTRAL_MERIDIAN, 0.0 );
2128 :
2129 : dfCenterLat =
2130 : poDS->FetchCopyParm( szGridMappingValue,
2131 12 : CF_PP_LAT_PROJ_ORIGIN, 0.0 );
2132 :
2133 : dfFalseEasting =
2134 : poDS->FetchCopyParm( szGridMappingValue,
2135 12 : CF_PP_FALSE_EASTING, 0.0 );
2136 :
2137 : dfFalseNorthing =
2138 : poDS->FetchCopyParm( szGridMappingValue,
2139 12 : CF_PP_FALSE_NORTHING, 0.0 );
2140 :
2141 : papszStdParallels =
2142 12 : FetchStandardParallels( szGridMappingValue );
2143 :
2144 : /* 2SP variant */
2145 12 : if( CSLCount( papszStdParallels ) == 2 ) {
2146 11 : dfStdP1 = CPLAtofM( papszStdParallels[0] );
2147 11 : dfStdP2 = CPLAtofM( papszStdParallels[1] );
2148 : oSRS.SetLCC( dfStdP1, dfStdP2, dfCenterLat, dfCenterLon,
2149 11 : dfFalseEasting, dfFalseNorthing );
2150 : }
2151 : /* 1SP variant (with standard_parallel or center lon) */
2152 : /* See comments in netcdfdataset.h for this projection. */
2153 : else {
2154 :
2155 : dfScale =
2156 : poDS->FetchCopyParm( szGridMappingValue,
2157 1 : CF_PP_SCALE_FACTOR_ORIGIN, -1.0 );
2158 :
2159 : /* CF definition, without scale factor */
2160 1 : if( CPLIsEqual(dfScale, -1.0) ) {
2161 :
2162 : /* with standard_parallel */
2163 1 : if( CSLCount( papszStdParallels ) == 1 )
2164 1 : dfStdP1 = CPLAtofM( papszStdParallels[0] );
2165 : /* with center lon instead */
2166 : else
2167 0 : dfStdP1 = dfCenterLat;
2168 1 : dfStdP2 = dfStdP1;
2169 :
2170 : /* test if we should actually compute scale factor */
2171 1 : if ( ! CPLIsEqual( dfStdP1, dfCenterLat ) ) {
2172 : CPLError( CE_Warning, CPLE_NotSupported,
2173 : "NetCDF driver import of LCC-1SP with standard_parallel1 != latitude_of_projection_origin\n"
2174 0 : "(which forces a computation of scale_factor) is experimental (bug #3324)\n" );
2175 : /* use Snyder eq. 15-4 to compute dfScale from dfStdP1 and dfCenterLat */
2176 : /* only tested for dfStdP1=dfCenterLat and (25,26), needs more data for testing */
2177 : /* other option: use the 2SP variant - how to compute new standard parallels? */
2178 : dfScale = ( cos(dfStdP1) * pow( tan(NCDF_PI/4 + dfStdP1/2), sin(dfStdP1) ) ) /
2179 0 : ( cos(dfCenterLat) * pow( tan(NCDF_PI/4 + dfCenterLat/2), sin(dfCenterLat) ) );
2180 : }
2181 : /* default is 1.0 */
2182 : else
2183 1 : dfScale = 1.0;
2184 :
2185 : oSRS.SetLCC1SP( dfCenterLat, dfCenterLon, dfScale,
2186 1 : dfFalseEasting, dfFalseNorthing );
2187 : /* store dfStdP1 so we can output it to CF later */
2188 1 : oSRS.SetNormProjParm( SRS_PP_STANDARD_PARALLEL_1, dfStdP1 );
2189 : }
2190 : /* OGC/PROJ.4 definition with scale factor */
2191 : else {
2192 : oSRS.SetLCC1SP( dfCenterLat, dfCenterLon, dfScale,
2193 0 : dfFalseEasting, dfFalseNorthing );
2194 : }
2195 : }
2196 :
2197 :
2198 12 : bGotCfSRS = TRUE;
2199 12 : if( !bGotGeogCS )
2200 2 : oSRS.SetWellKnownGeogCS( "WGS84" );
2201 :
2202 12 : CSLDestroy( papszStdParallels );
2203 : }
2204 :
2205 : /* -------------------------------------------------------------------- */
2206 : /* Is this Latitude/Longitude Grid explicitly */
2207 : /* -------------------------------------------------------------------- */
2208 :
2209 81 : else if ( EQUAL ( pszValue, CF_PT_LATITUDE_LONGITUDE ) ) {
2210 23 : bGotCfSRS = TRUE;
2211 23 : if( !bGotGeogCS )
2212 0 : oSRS.SetWellKnownGeogCS( "WGS84" );
2213 : }
2214 : /* -------------------------------------------------------------------- */
2215 : /* Mercator */
2216 : /* -------------------------------------------------------------------- */
2217 :
2218 58 : else if ( EQUAL ( pszValue, CF_PT_MERCATOR ) ) {
2219 :
2220 12 : char **papszStdParallels = NULL;
2221 :
2222 : /* If there is a standard_parallel, know it is Mercator 2SP */
2223 : papszStdParallels =
2224 12 : FetchStandardParallels( szGridMappingValue );
2225 :
2226 12 : if (NULL != papszStdParallels) {
2227 : /* CF-1 Mercator 2SP always has lat centered at equator */
2228 6 : dfStdP1 = CPLAtofM( papszStdParallels[0] );
2229 :
2230 6 : dfCenterLat = 0.0;
2231 :
2232 : dfCenterLon =
2233 : poDS->FetchCopyParm( szGridMappingValue,
2234 6 : CF_PP_LON_PROJ_ORIGIN, 0.0 );
2235 :
2236 : dfFalseEasting =
2237 : poDS->FetchCopyParm( szGridMappingValue,
2238 6 : CF_PP_FALSE_EASTING, 0.0 );
2239 :
2240 : dfFalseNorthing =
2241 : poDS->FetchCopyParm( szGridMappingValue,
2242 6 : CF_PP_FALSE_NORTHING, 0.0 );
2243 :
2244 : oSRS.SetMercator2SP( dfStdP1, dfCenterLat, dfCenterLon,
2245 6 : dfFalseEasting, dfFalseNorthing );
2246 : }
2247 : else {
2248 : dfCenterLon =
2249 : poDS->FetchCopyParm( szGridMappingValue,
2250 6 : CF_PP_LON_PROJ_ORIGIN, 0.0 );
2251 :
2252 : dfCenterLat =
2253 : poDS->FetchCopyParm( szGridMappingValue,
2254 6 : CF_PP_LAT_PROJ_ORIGIN, 0.0 );
2255 :
2256 : dfScale =
2257 : poDS->FetchCopyParm( szGridMappingValue,
2258 : CF_PP_SCALE_FACTOR_ORIGIN,
2259 6 : 1.0 );
2260 :
2261 : dfFalseEasting =
2262 : poDS->FetchCopyParm( szGridMappingValue,
2263 6 : CF_PP_FALSE_EASTING, 0.0 );
2264 :
2265 : dfFalseNorthing =
2266 : poDS->FetchCopyParm( szGridMappingValue,
2267 6 : CF_PP_FALSE_NORTHING, 0.0 );
2268 :
2269 : oSRS.SetMercator( dfCenterLat, dfCenterLon, dfScale,
2270 6 : dfFalseEasting, dfFalseNorthing );
2271 : }
2272 :
2273 12 : bGotCfSRS = TRUE;
2274 :
2275 12 : if( !bGotGeogCS )
2276 0 : oSRS.SetWellKnownGeogCS( "WGS84" );
2277 :
2278 12 : CSLDestroy( papszStdParallels );
2279 : }
2280 :
2281 : /* -------------------------------------------------------------------- */
2282 : /* Orthographic */
2283 : /* -------------------------------------------------------------------- */
2284 :
2285 :
2286 46 : else if ( EQUAL ( pszValue, CF_PT_ORTHOGRAPHIC ) ) {
2287 : dfCenterLon =
2288 : poDS->FetchCopyParm( szGridMappingValue,
2289 6 : CF_PP_LON_PROJ_ORIGIN, 0.0 );
2290 :
2291 : dfCenterLat =
2292 : poDS->FetchCopyParm( szGridMappingValue,
2293 6 : CF_PP_LAT_PROJ_ORIGIN, 0.0 );
2294 :
2295 : dfFalseEasting =
2296 : poDS->FetchCopyParm( szGridMappingValue,
2297 6 : CF_PP_FALSE_EASTING, 0.0 );
2298 :
2299 : dfFalseNorthing =
2300 : poDS->FetchCopyParm( szGridMappingValue,
2301 6 : CF_PP_FALSE_NORTHING, 0.0 );
2302 :
2303 6 : bGotCfSRS = TRUE;
2304 :
2305 : oSRS.SetOrthographic( dfCenterLat, dfCenterLon,
2306 6 : dfFalseEasting, dfFalseNorthing );
2307 :
2308 6 : if( !bGotGeogCS )
2309 0 : oSRS.SetWellKnownGeogCS( "WGS84" );
2310 : }
2311 :
2312 : /* -------------------------------------------------------------------- */
2313 : /* Polar Stereographic */
2314 : /* -------------------------------------------------------------------- */
2315 :
2316 40 : else if ( EQUAL ( pszValue, CF_PT_POLAR_STEREO ) ) {
2317 :
2318 8 : char **papszStdParallels = NULL;
2319 :
2320 : dfScale =
2321 : poDS->FetchCopyParm( szGridMappingValue,
2322 : CF_PP_SCALE_FACTOR_ORIGIN,
2323 8 : -1.0 );
2324 :
2325 : papszStdParallels =
2326 8 : FetchStandardParallels( szGridMappingValue );
2327 :
2328 : /* CF allows the use of standard_parallel (lat_ts) OR scale_factor (k0),
2329 : make sure we have standard_parallel, using Snyder eq. 22-7
2330 : with k=1 and lat=standard_parallel */
2331 8 : if ( papszStdParallels != NULL ) {
2332 7 : dfStdP1 = CPLAtofM( papszStdParallels[0] );
2333 : /* compute scale_factor from standard_parallel */
2334 : /* this creates WKT that is inconsistent, don't write for now
2335 : also proj4 does not seem to use this parameter */
2336 : // dfScale = ( 1.0 + fabs( sin( dfStdP1 * NCDF_PI / 180.0 ) ) ) / 2.0;
2337 : }
2338 : else {
2339 1 : if ( ! CPLIsEqual(dfScale,-1.0) ) {
2340 : /* compute standard_parallel from scale_factor */
2341 1 : dfStdP1 = asin( 2*dfScale - 1 ) * 180.0 / NCDF_PI;
2342 :
2343 : /* fetch latitude_of_projection_origin (+90/-90)
2344 : used here for the sign of standard_parallel */
2345 : double dfLatProjOrigin =
2346 : poDS->FetchCopyParm( szGridMappingValue,
2347 : CF_PP_LAT_PROJ_ORIGIN,
2348 1 : 0.0 );
2349 1 : if ( ! CPLIsEqual(dfLatProjOrigin,90.0) &&
2350 : ! CPLIsEqual(dfLatProjOrigin,-90.0) ) {
2351 : CPLError( CE_Failure, CPLE_NotSupported,
2352 : "Polar Stereographic must have a %s parameter equal to +90 or -90\n.",
2353 0 : CF_PP_LAT_PROJ_ORIGIN );
2354 0 : dfLatProjOrigin = 90.0;
2355 : }
2356 1 : if ( CPLIsEqual(dfLatProjOrigin,-90.0) )
2357 0 : dfStdP1 = - dfStdP1;
2358 : }
2359 : else {
2360 0 : dfStdP1 = 0.0; //just to avoid warning at compilation
2361 : CPLError( CE_Failure, CPLE_NotSupported,
2362 : "The NetCDF driver does not support import of CF-1 Polar stereographic "
2363 0 : "without standard_parallel and scale_factor_at_projection_origin parameters.\n" );
2364 : }
2365 : }
2366 :
2367 : /* set scale to default value 1.0 if it was not set */
2368 8 : if ( CPLIsEqual(dfScale,-1.0) )
2369 7 : dfScale = 1.0;
2370 :
2371 : dfCenterLon =
2372 : poDS->FetchCopyParm( szGridMappingValue,
2373 8 : CF_PP_VERT_LONG_FROM_POLE, 0.0 );
2374 :
2375 : dfFalseEasting =
2376 : poDS->FetchCopyParm( szGridMappingValue,
2377 8 : CF_PP_FALSE_EASTING, 0.0 );
2378 :
2379 : dfFalseNorthing =
2380 : poDS->FetchCopyParm( szGridMappingValue,
2381 8 : CF_PP_FALSE_NORTHING, 0.0 );
2382 :
2383 8 : bGotCfSRS = TRUE;
2384 : /* map CF CF_PP_STD_PARALLEL_1 to WKT SRS_PP_LATITUDE_OF_ORIGIN */
2385 : oSRS.SetPS( dfStdP1, dfCenterLon, dfScale,
2386 8 : dfFalseEasting, dfFalseNorthing );
2387 :
2388 8 : if( !bGotGeogCS )
2389 2 : oSRS.SetWellKnownGeogCS( "WGS84" );
2390 :
2391 8 : CSLDestroy( papszStdParallels );
2392 : }
2393 :
2394 : /* -------------------------------------------------------------------- */
2395 : /* Stereographic */
2396 : /* -------------------------------------------------------------------- */
2397 :
2398 32 : else if ( EQUAL ( pszValue, CF_PT_STEREO ) ) {
2399 :
2400 : dfCenterLon =
2401 : poDS->FetchCopyParm( szGridMappingValue,
2402 6 : CF_PP_LON_PROJ_ORIGIN, 0.0 );
2403 :
2404 : dfCenterLat =
2405 : poDS->FetchCopyParm( szGridMappingValue,
2406 6 : CF_PP_LAT_PROJ_ORIGIN, 0.0 );
2407 :
2408 : dfScale =
2409 : poDS->FetchCopyParm( szGridMappingValue,
2410 : CF_PP_SCALE_FACTOR_ORIGIN,
2411 6 : 1.0 );
2412 :
2413 : dfFalseEasting =
2414 : poDS->FetchCopyParm( szGridMappingValue,
2415 6 : CF_PP_FALSE_EASTING, 0.0 );
2416 :
2417 : dfFalseNorthing =
2418 : poDS->FetchCopyParm( szGridMappingValue,
2419 6 : CF_PP_FALSE_NORTHING, 0.0 );
2420 :
2421 6 : bGotCfSRS = TRUE;
2422 : oSRS.SetStereographic( dfCenterLat, dfCenterLon, dfScale,
2423 6 : dfFalseEasting, dfFalseNorthing );
2424 :
2425 6 : if( !bGotGeogCS )
2426 0 : oSRS.SetWellKnownGeogCS( "WGS84" );
2427 : }
2428 :
2429 : /* -------------------------------------------------------------------- */
2430 : /* Is this Latitude/Longitude Grid, default */
2431 : /* -------------------------------------------------------------------- */
2432 :
2433 0 : } else if( EQUAL( szDimNameX,"lon" ) ) {
2434 0 : oSRS.SetWellKnownGeogCS( "WGS84" );
2435 :
2436 : } else {
2437 : // This would be too indiscrimant. But we should set
2438 : // it if we know the data is geographic.
2439 : //oSRS.SetWellKnownGeogCS( "WGS84" );
2440 : }
2441 : }
2442 : /* -------------------------------------------------------------------- */
2443 : /* Read projection coordinates */
2444 : /* -------------------------------------------------------------------- */
2445 :
2446 189 : nc_inq_varid( cdfid, poDS->papszDimName[nXDimID], &nVarDimXID );
2447 189 : nc_inq_varid( cdfid, poDS->papszDimName[nYDimID], &nVarDimYID );
2448 :
2449 189 : if( ( nVarDimXID != -1 ) && ( nVarDimYID != -1 ) ) {
2450 153 : pdfXCoord = (double *) CPLCalloc( xdim, sizeof(double) );
2451 153 : pdfYCoord = (double *) CPLCalloc( ydim, sizeof(double) );
2452 :
2453 153 : start[0] = 0;
2454 153 : edge[0] = xdim;
2455 : status = nc_get_vara_double( cdfid, nVarDimXID,
2456 153 : start, edge, pdfXCoord);
2457 :
2458 153 : edge[0] = ydim;
2459 : status = nc_get_vara_double( cdfid, nVarDimYID,
2460 153 : start, edge, pdfYCoord);
2461 :
2462 : /* -------------------------------------------------------------------- */
2463 : /* Check for bottom-up from the Y-axis order */
2464 : /* see bugs #4284 and #4251 */
2465 : /* -------------------------------------------------------------------- */
2466 :
2467 153 : if ( pdfYCoord[0] > pdfYCoord[1] )
2468 4 : poDS->bBottomUp = FALSE;
2469 : else
2470 149 : poDS->bBottomUp = TRUE;
2471 :
2472 153 : CPLDebug( "GDAL_netCDF", "set bBottomUp = %d from Y axis", poDS->bBottomUp );
2473 :
2474 : /* -------------------------------------------------------------------- */
2475 : /* convert ]180,360] longitude values to [-180,180] */
2476 : /* -------------------------------------------------------------------- */
2477 :
2478 153 : if ( NCDFIsVarLongitude( cdfid, nVarDimXID, NULL ) &&
2479 : CSLTestBoolean(CPLGetConfigOption("GDAL_NETCDF_CENTERLONG_180", "YES")) ) {
2480 : /* if mininum longitude is > 180, subtract 360 from all */
2481 54 : if ( MIN( pdfXCoord[0], pdfXCoord[xdim-1] ) > 180.0 ) {
2482 20 : for ( size_t i=0; i<xdim ; i++ )
2483 15 : pdfXCoord[i] -= 360;
2484 : }
2485 : }
2486 :
2487 : /* -------------------------------------------------------------------- */
2488 : /* Is pixel spacing uniform accross the map? */
2489 : /* -------------------------------------------------------------------- */
2490 :
2491 : /* -------------------------------------------------------------------- */
2492 : /* Check Longitude */
2493 : /* -------------------------------------------------------------------- */
2494 :
2495 153 : if( xdim == 2 ) {
2496 1 : bLonSpacingOK = TRUE;
2497 : }
2498 : else
2499 : {
2500 152 : nSpacingBegin = (int) poDS->rint((pdfXCoord[1] - pdfXCoord[0]) * 1000);
2501 :
2502 152 : nSpacingMiddle = (int) poDS->rint((pdfXCoord[xdim / 2] -
2503 152 : pdfXCoord[(xdim / 2) + 1]) * 1000);
2504 :
2505 152 : nSpacingLast = (int) poDS->rint((pdfXCoord[xdim - 2] -
2506 152 : pdfXCoord[xdim-1]) * 1000);
2507 :
2508 : CPLDebug("GDAL_netCDF",
2509 : "xdim: %ld nSpacingBegin: %d nSpacingMiddle: %d nSpacingLast: %d",
2510 152 : (long)xdim, nSpacingBegin, nSpacingMiddle, nSpacingLast );
2511 :
2512 152 : if( ( abs( nSpacingBegin ) == abs( nSpacingLast ) ) &&
2513 : ( abs( nSpacingBegin ) == abs( nSpacingMiddle ) ) &&
2514 : ( abs( nSpacingMiddle ) == abs( nSpacingLast ) ) ) {
2515 151 : bLonSpacingOK = TRUE;
2516 : }
2517 : }
2518 :
2519 153 : if ( bLonSpacingOK == FALSE ) {
2520 : CPLDebug( "GDAL_netCDF",
2521 1 : "Longitude is not equally spaced." );
2522 : }
2523 :
2524 : /* -------------------------------------------------------------------- */
2525 : /* Check Latitude */
2526 : /* -------------------------------------------------------------------- */
2527 153 : if( ydim == 2 ) {
2528 1 : bLatSpacingOK = TRUE;
2529 : }
2530 : else
2531 : {
2532 152 : nSpacingBegin = (int) poDS->rint((pdfYCoord[1] - pdfYCoord[0]) *
2533 152 : 1000);
2534 :
2535 152 : nSpacingMiddle = (int) poDS->rint((pdfYCoord[ydim / 2] -
2536 152 : pdfYCoord[(ydim / 2) + 1]) *
2537 152 : 1000);
2538 :
2539 152 : nSpacingLast = (int) poDS->rint((pdfYCoord[ydim - 2] -
2540 152 : pdfYCoord[ydim-1]) *
2541 152 : 1000);
2542 :
2543 : CPLDebug("GDAL_netCDF",
2544 : "ydim: %ld nSpacingBegin: %d nSpacingMiddle: %d nSpacingLast: %d",
2545 152 : (long)ydim, nSpacingBegin, nSpacingMiddle, nSpacingLast );
2546 :
2547 : /* -------------------------------------------------------------------- */
2548 : /* For Latitude we allow an error of 0.1 degrees for gaussian */
2549 : /* gridding */
2550 : /* -------------------------------------------------------------------- */
2551 :
2552 152 : if( (( abs( abs(nSpacingBegin) - abs(nSpacingLast) ) ) < 100 ) &&
2553 : (( abs( abs(nSpacingBegin) - abs(nSpacingMiddle) ) ) < 100 ) &&
2554 : (( abs( abs(nSpacingMiddle) - abs(nSpacingLast) ) ) < 100 ) ) {
2555 :
2556 152 : bLatSpacingOK = TRUE;
2557 :
2558 152 : if( ( abs( nSpacingBegin ) != abs( nSpacingLast ) ) ||
2559 : ( abs( nSpacingBegin ) != abs( nSpacingMiddle ) ) ||
2560 : ( abs( nSpacingMiddle ) != abs( nSpacingLast ) ) ) {
2561 :
2562 1 : CPLError(CE_Warning, 1,"Latitude grid not spaced evenly.\nSeting projection for grid spacing is within 0.1 degrees threshold.\n");
2563 :
2564 : CPLDebug("GDAL_netCDF",
2565 : "Latitude grid not spaced evenly, but within 0.1 degree threshold (probably a Gaussian grid).\n"
2566 1 : "Saving original latitude values in Y_VALUES geolocation metadata" );
2567 1 : Set1DGeolocation( nVarDimYID, "Y" );
2568 :
2569 : }
2570 : }
2571 : }
2572 :
2573 153 : if ( bLatSpacingOK == FALSE ) {
2574 : CPLDebug( "GDAL_netCDF",
2575 0 : "Latitude is not equally spaced." );
2576 : }
2577 :
2578 153 : if ( ( bLonSpacingOK == TRUE ) && ( bLatSpacingOK == TRUE ) ) {
2579 :
2580 : /* -------------------------------------------------------------------- */
2581 : /* We have gridded data so we can set the Gereferencing info. */
2582 : /* -------------------------------------------------------------------- */
2583 :
2584 : /* -------------------------------------------------------------------- */
2585 : /* Enable GeoTransform */
2586 : /* -------------------------------------------------------------------- */
2587 : /* ----------------------------------------------------------*/
2588 : /* In the following "actual_range" and "node_offset" */
2589 : /* are attributes used by netCDF files created by GMT. */
2590 : /* If we find them we know how to proceed. Else, use */
2591 : /* the original algorithm. */
2592 : /* --------------------------------------------------------- */
2593 : double dummy[2], xMinMax[2], yMinMax[2];
2594 152 : int node_offset = 0;
2595 :
2596 152 : bGotCfGT = TRUE;
2597 :
2598 152 : nc_get_att_int (cdfid, NC_GLOBAL, "node_offset", &node_offset);
2599 :
2600 152 : if (!nc_get_att_double (cdfid, nVarDimXID, "actual_range", dummy)) {
2601 5 : xMinMax[0] = dummy[0];
2602 5 : xMinMax[1] = dummy[1];
2603 : }
2604 : else {
2605 147 : xMinMax[0] = pdfXCoord[0];
2606 147 : xMinMax[1] = pdfXCoord[xdim-1];
2607 147 : node_offset = 0;
2608 : }
2609 :
2610 152 : if (!nc_get_att_double (cdfid, nVarDimYID, "actual_range", dummy)) {
2611 5 : yMinMax[0] = dummy[0];
2612 5 : yMinMax[1] = dummy[1];
2613 : }
2614 : else {
2615 147 : yMinMax[0] = pdfYCoord[0];
2616 147 : yMinMax[1] = pdfYCoord[ydim-1];
2617 147 : node_offset = 0;
2618 : }
2619 :
2620 : /* Check for reverse order of y-coordinate */
2621 152 : if ( yMinMax[0] > yMinMax[1] ) {
2622 3 : dummy[0] = yMinMax[1];
2623 3 : dummy[1] = yMinMax[0];
2624 3 : yMinMax[0] = dummy[0];
2625 3 : yMinMax[1] = dummy[1];
2626 : }
2627 :
2628 152 : adfTempGeoTransform[0] = xMinMax[0];
2629 152 : adfTempGeoTransform[2] = 0;
2630 152 : adfTempGeoTransform[3] = yMinMax[1];
2631 152 : adfTempGeoTransform[4] = 0;
2632 152 : adfTempGeoTransform[1] = ( xMinMax[1] - xMinMax[0] ) /
2633 152 : ( poDS->nRasterXSize + (node_offset - 1) );
2634 152 : adfTempGeoTransform[5] = ( yMinMax[0] - yMinMax[1] ) /
2635 152 : ( poDS->nRasterYSize + (node_offset - 1) );
2636 :
2637 : /* -------------------------------------------------------------------- */
2638 : /* Compute the center of the pixel */
2639 : /* -------------------------------------------------------------------- */
2640 152 : if ( !node_offset ) { // Otherwise its already the pixel center
2641 152 : adfTempGeoTransform[0] -= (adfTempGeoTransform[1] / 2);
2642 152 : adfTempGeoTransform[3] -= (adfTempGeoTransform[5] / 2);
2643 : }
2644 :
2645 : }
2646 :
2647 153 : CPLFree( pdfXCoord );
2648 153 : CPLFree( pdfYCoord );
2649 : }// end if (has dims)
2650 :
2651 : /* -------------------------------------------------------------------- */
2652 : /* Set Projection from CF */
2653 : /* -------------------------------------------------------------------- */
2654 189 : if ( bGotGeogCS || bGotCfSRS ) {
2655 : /* Set SRS Units */
2656 :
2657 : /* check units for x and y */
2658 118 : if( oSRS.IsProjected( ) ) {
2659 95 : const char *pszUnitsX = NULL;
2660 95 : const char *pszUnitsY = NULL;
2661 :
2662 95 : strcpy( szTemp, poDS->papszDimName[nXDimID] );
2663 95 : strcat( szTemp, "#units" );
2664 : pszValue = CSLFetchNameValue( poDS->papszMetadata,
2665 95 : szTemp );
2666 95 : if( pszValue != NULL )
2667 95 : pszUnitsX = pszValue;
2668 :
2669 95 : strcpy( szTemp, poDS->papszDimName[nYDimID] );
2670 95 : strcat( szTemp, "#units" );
2671 : pszValue = CSLFetchNameValue( poDS->papszMetadata,
2672 95 : szTemp );
2673 95 : if( pszValue != NULL )
2674 95 : pszUnitsY = pszValue;
2675 :
2676 : /* TODO: what to do if units are not equal in X and Y */
2677 95 : if ( (pszUnitsX != NULL) && (pszUnitsY != NULL) &&
2678 : EQUAL(pszUnitsX,pszUnitsY) )
2679 95 : pszUnits = pszUnitsX;
2680 :
2681 : /* add units to PROJCS */
2682 95 : if ( pszUnits != NULL && ! EQUAL(pszUnits,"") ) {
2683 : CPLDebug( "GDAL_netCDF",
2684 93 : "units=%s", pszUnits );
2685 93 : if ( EQUAL(pszUnits,"m") ) {
2686 88 : oSRS.SetLinearUnits( "metre", 1.0 );
2687 88 : oSRS.SetAuthority( "PROJCS|UNIT", "EPSG", 9001 );
2688 : }
2689 5 : else if ( EQUAL(pszUnits,"km") ) {
2690 5 : oSRS.SetLinearUnits( "kilometre", 1000.0 );
2691 5 : oSRS.SetAuthority( "PROJCS|UNIT", "EPSG", 9036 );
2692 : }
2693 : /* TODO check for other values */
2694 : // else
2695 : // oSRS.SetLinearUnits(pszUnits, 1.0);
2696 : }
2697 : }
2698 23 : else if ( oSRS.IsGeographic() ) {
2699 23 : oSRS.SetAngularUnits( CF_UNITS_D, CPLAtof(SRS_UA_DEGREE_CONV) );
2700 23 : oSRS.SetAuthority( "GEOGCS|UNIT", "EPSG", 9122 );
2701 : }
2702 :
2703 : /* Set Projection */
2704 118 : oSRS.exportToWkt( &(pszTempProjection) );
2705 118 : CPLDebug( "GDAL_netCDF", "setting WKT from CF" );
2706 118 : SetProjection( pszTempProjection );
2707 118 : CPLFree( pszTempProjection );
2708 :
2709 118 : if ( !bGotCfGT )
2710 0 : CPLDebug( "GDAL_netCDF", "got SRS but no geotransform from CF!");
2711 : }
2712 :
2713 : /* -------------------------------------------------------------------- */
2714 : /* Process custom GDAL values (spatial_ref, GeoTransform) */
2715 : /* -------------------------------------------------------------------- */
2716 189 : if( !EQUAL( szGridMappingValue, "" ) ) {
2717 :
2718 118 : if( pszWKT != NULL ) {
2719 :
2720 : /* -------------------------------------------------------------------- */
2721 : /* Compare SRS obtained from CF attributes and GDAL WKT */
2722 : /* If possible use the more complete GDAL WKT */
2723 : /* -------------------------------------------------------------------- */
2724 : /* Set the SRS to the one written by GDAL */
2725 111 : if ( ! bGotCfSRS || poDS->pszProjection == NULL || ! bIsGdalCfFile ) {
2726 0 : bGotGdalSRS = TRUE;
2727 0 : CPLDebug( "GDAL_netCDF", "setting WKT from GDAL" );
2728 0 : SetProjection( pszWKT );
2729 : }
2730 : else { /* use the SRS from GDAL if it doesn't conflict with the one from CF */
2731 111 : char *pszProjectionGDAL = (char*) pszWKT ;
2732 111 : OGRSpatialReference oSRSGDAL;
2733 111 : oSRSGDAL.importFromWkt( &pszProjectionGDAL );
2734 : /* set datum to unknown or else datums will not match, see bug #4281 */
2735 111 : if ( oSRSGDAL.GetAttrNode( "DATUM" ) )
2736 111 : oSRSGDAL.GetAttrNode( "DATUM" )->GetChild(0)->SetValue( "unknown" );
2737 : /* need this for setprojection autotest */
2738 111 : if ( oSRSGDAL.GetAttrNode( "PROJCS" ) )
2739 88 : oSRSGDAL.GetAttrNode( "PROJCS" )->GetChild(0)->SetValue( "unnamed" );
2740 111 : if ( oSRSGDAL.GetAttrNode( "GEOGCS" ) )
2741 111 : oSRSGDAL.GetAttrNode( "GEOGCS" )->GetChild(0)->SetValue( "unknown" );
2742 111 : oSRSGDAL.GetRoot()->StripNodes( "UNIT" );
2743 111 : if ( oSRS.IsSame(&oSRSGDAL) ) {
2744 : // printf("ARE SAME, using GDAL WKT\n");
2745 111 : bGotGdalSRS = TRUE;
2746 111 : CPLDebug( "GDAL_netCDF", "setting WKT from GDAL" );
2747 111 : SetProjection( pszWKT );
2748 : }
2749 : else {
2750 : CPLDebug( "GDAL_netCDF",
2751 : "got WKT from GDAL \n[%s]\nbut not using it because conflicts with CF\n[%s]\n",
2752 0 : pszWKT, poDS->pszProjection );
2753 111 : }
2754 : }
2755 :
2756 : /* -------------------------------------------------------------------- */
2757 : /* Look for GeoTransform Array, if not found in CF */
2758 : /* -------------------------------------------------------------------- */
2759 111 : if ( !bGotCfGT ) {
2760 :
2761 : /* TODO read the GT values and detect for conflict with CF */
2762 : /* this could resolve the GT precision loss issue */
2763 :
2764 0 : if( pszGeoTransform != NULL ) {
2765 :
2766 0 : bGotGdalGT = TRUE;
2767 :
2768 : papszGeoTransform = CSLTokenizeString2( pszGeoTransform,
2769 : " ",
2770 0 : CSLT_HONOURSTRINGS );
2771 0 : adfTempGeoTransform[0] = atof( papszGeoTransform[0] );
2772 0 : adfTempGeoTransform[1] = atof( papszGeoTransform[1] );
2773 0 : adfTempGeoTransform[2] = atof( papszGeoTransform[2] );
2774 0 : adfTempGeoTransform[3] = atof( papszGeoTransform[3] );
2775 0 : adfTempGeoTransform[4] = atof( papszGeoTransform[4] );
2776 0 : adfTempGeoTransform[5] = atof( papszGeoTransform[5] );
2777 :
2778 : /* -------------------------------------------------------------------- */
2779 : /* Look for corner array values */
2780 : /* -------------------------------------------------------------------- */
2781 : } else {
2782 0 : double dfNN=0.0, dfSN=0.0, dfEE=0.0, dfWE=0.0;
2783 0 : int bGotNN=FALSE, bGotSN=FALSE, bGotEE=FALSE, bGotWE=FALSE;
2784 : // CPLDebug( "GDAL_netCDF", "looking for geotransform corners\n" );
2785 :
2786 0 : strcpy(szTemp,szGridMappingValue);
2787 0 : strcat( szTemp, "#" );
2788 0 : strcat( szTemp, "Northernmost_Northing");
2789 0 : pszValue = CSLFetchNameValue(poDS->papszMetadata, szTemp);
2790 0 : if( pszValue != NULL ) {
2791 0 : dfNN = atof( pszValue );
2792 0 : bGotNN = TRUE;
2793 : }
2794 :
2795 0 : strcpy(szTemp,szGridMappingValue);
2796 0 : strcat( szTemp, "#" );
2797 0 : strcat( szTemp, "Southernmost_Northing");
2798 0 : pszValue = CSLFetchNameValue(poDS->papszMetadata, szTemp);
2799 0 : if( pszValue != NULL ) {
2800 0 : dfSN = atof( pszValue );
2801 0 : bGotSN = TRUE;
2802 : }
2803 :
2804 0 : strcpy(szTemp,szGridMappingValue);
2805 0 : strcat( szTemp, "#" );
2806 0 : strcat( szTemp, "Easternmost_Easting");
2807 0 : pszValue = CSLFetchNameValue(poDS->papszMetadata, szTemp);
2808 0 : if( pszValue != NULL ) {
2809 0 : dfEE = atof( pszValue );
2810 0 : bGotEE = TRUE;
2811 : }
2812 :
2813 0 : strcpy(szTemp,szGridMappingValue);
2814 0 : strcat( szTemp, "#" );
2815 0 : strcat( szTemp, "Westernmost_Easting");
2816 0 : pszValue = CSLFetchNameValue(poDS->papszMetadata, szTemp);
2817 0 : if( pszValue != NULL ) {
2818 0 : dfWE = atof( pszValue );
2819 0 : bGotWE = TRUE;
2820 : }
2821 :
2822 : /* Only set the GeoTransform if we got all the values */
2823 0 : if ( bGotNN && bGotSN && bGotEE && bGotWE ) {
2824 :
2825 0 : bGotGdalGT = TRUE;
2826 :
2827 0 : adfTempGeoTransform[0] = dfWE;
2828 : adfTempGeoTransform[1] = (dfEE - dfWE) /
2829 0 : ( poDS->GetRasterXSize() - 1 );
2830 0 : adfTempGeoTransform[2] = 0.0;
2831 0 : adfTempGeoTransform[3] = dfNN;
2832 0 : adfTempGeoTransform[4] = 0.0;
2833 : adfTempGeoTransform[5] = (dfSN - dfNN) /
2834 0 : ( poDS->GetRasterYSize() - 1 );
2835 : /* compute the center of the pixel */
2836 : adfTempGeoTransform[0] = dfWE
2837 0 : - (adfTempGeoTransform[1] / 2);
2838 : adfTempGeoTransform[3] = dfNN
2839 0 : - (adfTempGeoTransform[5] / 2);
2840 : }
2841 : } // (pszGeoTransform != NULL)
2842 0 : CSLDestroy( papszGeoTransform );
2843 :
2844 0 : if ( bGotGdalSRS && ! bGotGdalGT )
2845 0 : CPLDebug( "GDAL_netCDF", "got SRS but not geotransform from GDAL!");
2846 :
2847 : } // if ( !bGotCfGT )
2848 :
2849 : }
2850 : }
2851 :
2852 : /* Set GeoTransform if we got a complete one - after projection has been set */
2853 189 : if ( bGotCfGT || bGotGdalGT ) {
2854 152 : SetGeoTransform( adfTempGeoTransform );
2855 : }
2856 :
2857 : /* Process geolocation arrays from CF "coordinates" attribute */
2858 : /* perhaps we should only add if is not a (supported) CF projection (bIsCfProjection */
2859 189 : ProcessCFGeolocation( nVarId );
2860 :
2861 : /* debuging reports */
2862 : CPLDebug( "GDAL_netCDF",
2863 : "bGotGeogCS=%d bGotCfSRS=%d bGotCfGT=%d bGotGdalSRS=%d bGotGdalGT=%d",
2864 189 : bGotGeogCS, bGotCfSRS, bGotCfGT, bGotGdalSRS, bGotGdalGT );
2865 :
2866 189 : if ( !bGotCfGT && !bGotGdalGT )
2867 37 : CPLDebug( "GDAL_netCDF", "did not get geotransform from CF nor GDAL!");
2868 :
2869 189 : if ( !bGotGeogCS && !bGotCfSRS && !bGotGdalSRS && !bGotCfGT)
2870 37 : CPLDebug( "GDAL_netCDF", "did not get projection from CF nor GDAL!");
2871 :
2872 : /* -------------------------------------------------------------------- */
2873 : /* Search for Well-known GeogCS if got only CF WKT */
2874 : /* Disabled for now, as a named datum also include control points */
2875 : /* (see mailing list and bug#4281 */
2876 : /* For example, WGS84 vs. GDA94 (EPSG:3577) - AEA in netcdf_cf.py */
2877 : /* -------------------------------------------------------------------- */
2878 : /* disabled for now, but could be set in a config option */
2879 189 : bLookForWellKnownGCS = FALSE;
2880 189 : if ( bLookForWellKnownGCS && bGotCfSRS && ! bGotGdalSRS ) {
2881 : /* ET - could use a more exhaustive method by scanning all EPSG codes in data/gcs.csv */
2882 : /* as proposed by Even in the gdal-dev mailing list "help for comparing two WKT" */
2883 : /* this code could be contributed to a new function */
2884 : /* OGRSpatialReference * OGRSpatialReference::FindMatchingGeogCS( const OGRSpatialReference *poOther ) */
2885 0 : CPLDebug( "GDAL_netCDF", "Searching for Well-known GeogCS" );
2886 0 : const char *pszWKGCSList[] = { "WGS84", "WGS72", "NAD27", "NAD83" };
2887 0 : char *pszWKGCS = NULL;
2888 0 : oSRS.exportToPrettyWkt( &pszWKGCS );
2889 0 : for( size_t i=0; i<sizeof(pszWKGCSList)/8; i++ ) {
2890 0 : pszWKGCS = CPLStrdup( pszWKGCSList[i] );
2891 0 : OGRSpatialReference oSRSTmp;
2892 0 : oSRSTmp.SetWellKnownGeogCS( pszWKGCSList[i] );
2893 : /* set datum to unknown, bug #4281 */
2894 0 : if ( oSRSTmp.GetAttrNode( "DATUM" ) )
2895 0 : oSRSTmp.GetAttrNode( "DATUM" )->GetChild(0)->SetValue( "unknown" );
2896 : /* could use OGRSpatialReference::StripCTParms() but let's keep TOWGS84 */
2897 0 : oSRSTmp.GetRoot()->StripNodes( "AXIS" );
2898 0 : oSRSTmp.GetRoot()->StripNodes( "AUTHORITY" );
2899 0 : oSRSTmp.GetRoot()->StripNodes( "EXTENSION" );
2900 :
2901 0 : oSRSTmp.exportToPrettyWkt( &pszWKGCS );
2902 0 : if ( oSRS.IsSameGeogCS(&oSRSTmp) ) {
2903 0 : oSRS.SetWellKnownGeogCS( pszWKGCSList[i] );
2904 0 : oSRS.exportToWkt( &(pszTempProjection) );
2905 0 : SetProjection( pszTempProjection );
2906 0 : CPLFree( pszTempProjection );
2907 : }
2908 : }
2909 0 : }
2910 : }
2911 :
2912 :
2913 189 : int netCDFDataset::ProcessCFGeolocation( int nVarId )
2914 : {
2915 189 : int bAddGeoloc = FALSE;
2916 189 : char *pszTemp = NULL;
2917 189 : char **papszTokens = NULL;
2918 189 : CPLString osTMP;
2919 : char szGeolocXName[NC_MAX_NAME];
2920 : char szGeolocYName[NC_MAX_NAME];
2921 189 : szGeolocXName[0] = '\0';
2922 189 : szGeolocYName[0] = '\0';
2923 :
2924 189 : if ( NCDFGetAttr( cdfid, nVarId, "coordinates", &pszTemp ) == CE_None ) {
2925 : /* get X and Y geolocation names from coordinates attribute */
2926 6 : papszTokens = CSLTokenizeString2( pszTemp, " ", 0 );
2927 6 : if ( CSLCount(papszTokens) >= 2 ) {
2928 : /* test that each variable is longitude/latitude */
2929 23 : for ( int i=0; i<CSLCount(papszTokens); i++ ) {
2930 17 : if ( NCDFIsVarLongitude(cdfid, -1, papszTokens[i]) )
2931 1 : strcpy( szGeolocXName, papszTokens[i] );
2932 16 : else if ( NCDFIsVarLatitude(cdfid, -1, papszTokens[i]) )
2933 1 : strcpy( szGeolocYName, papszTokens[i] );
2934 : }
2935 : /* add GEOLOCATION metadata */
2936 7 : if ( !EQUAL(szGeolocXName,"") && !EQUAL(szGeolocYName,"") ) {
2937 1 : bAddGeoloc = TRUE;
2938 : CPLDebug( "GDAL_netCDF",
2939 : "using variables %s and %s for GEOLOCATION",
2940 1 : szGeolocXName, szGeolocYName );
2941 :
2942 1 : SetMetadataItem( "SRS", SRS_WKT_WGS84, "GEOLOCATION" );
2943 :
2944 : osTMP.Printf( "NETCDF:\"%s\":%s",
2945 1 : osFilename.c_str(), szGeolocXName );
2946 1 : SetMetadataItem( "X_DATASET", osTMP, "GEOLOCATION" );
2947 1 : SetMetadataItem( "X_BAND", "1" , "GEOLOCATION" );
2948 : osTMP.Printf( "NETCDF:\"%s\":%s",
2949 1 : osFilename.c_str(), szGeolocYName );
2950 1 : SetMetadataItem( "Y_DATASET", osTMP, "GEOLOCATION" );
2951 1 : SetMetadataItem( "Y_BAND", "1" , "GEOLOCATION" );
2952 :
2953 1 : SetMetadataItem( "PIXEL_OFFSET", "0", "GEOLOCATION" );
2954 1 : SetMetadataItem( "PIXEL_STEP", "1", "GEOLOCATION" );
2955 :
2956 1 : SetMetadataItem( "LINE_OFFSET", "0", "GEOLOCATION" );
2957 1 : SetMetadataItem( "LINE_STEP", "1", "GEOLOCATION" );
2958 : }
2959 : else {
2960 : CPLDebug( "GDAL_netCDF",
2961 : "coordinates attribute [%s] is unsupported",
2962 5 : pszTemp );
2963 : }
2964 : }
2965 : else {
2966 : CPLDebug( "GDAL_netCDF",
2967 : "coordinates attribute [%s] with %d element(s) is unsupported",
2968 0 : pszTemp, CSLCount(papszTokens) );
2969 : }
2970 6 : if (papszTokens) CSLDestroy(papszTokens);
2971 6 : CPLFree( pszTemp );
2972 : }
2973 :
2974 189 : return bAddGeoloc;
2975 : }
2976 :
2977 1 : CPLErr netCDFDataset::Set1DGeolocation( int nVarId, const char *szDimName )
2978 : {
2979 : char szTemp[ NCDF_MAX_STR_LEN ];
2980 1 : char *pszVarValues = NULL;
2981 : CPLErr eErr;
2982 :
2983 : /* get values */
2984 1 : eErr = NCDFGet1DVar( cdfid, nVarId, &pszVarValues );
2985 1 : if ( eErr != CE_None )
2986 0 : return eErr;
2987 :
2988 : /* write metadata */
2989 1 : sprintf( szTemp, "%s_VALUES", szDimName );
2990 1 : SetMetadataItem( szTemp, pszVarValues, "GEOLOCATION" );
2991 :
2992 1 : CPLFree( pszVarValues );
2993 :
2994 1 : return CE_None;
2995 : }
2996 :
2997 :
2998 0 : double *netCDFDataset::Get1DGeolocation( const char *szDimName, int &nVarLen )
2999 : {
3000 0 : char **papszValues = NULL;
3001 0 : char *pszTemp = NULL;
3002 0 : double *pdfVarValues = NULL;
3003 :
3004 0 : nVarLen = 0;
3005 :
3006 : /* get Y_VALUES as tokens */
3007 0 : papszValues = NCDFTokenizeArray( GetMetadataItem( "Y_VALUES", "GEOLOCATION" ) );
3008 0 : if ( papszValues == NULL )
3009 0 : return NULL;
3010 :
3011 : /* initialize and fill array */
3012 0 : nVarLen = CSLCount(papszValues);
3013 0 : pdfVarValues = (double *) CPLCalloc( nVarLen, sizeof( double ) );
3014 0 : for(int i=0, j=0; i < nVarLen; i++) {
3015 0 : if ( ! bBottomUp ) j=nVarLen - 1 - i;
3016 0 : else j=i; /* invert latitude values */
3017 0 : pdfVarValues[j] = strtod( papszValues[i], &pszTemp );
3018 : }
3019 0 : CSLDestroy( papszValues );
3020 :
3021 0 : return pdfVarValues;
3022 : }
3023 :
3024 :
3025 : /************************************************************************/
3026 : /* SetProjection() */
3027 : /************************************************************************/
3028 317 : CPLErr netCDFDataset::SetProjection( const char * pszNewProjection )
3029 : {
3030 317 : CPLMutexHolderD(&hNCMutex);
3031 :
3032 : /* TODO look if proj. already defined, like in geotiff */
3033 317 : if( pszNewProjection == NULL )
3034 : {
3035 0 : CPLError( CE_Failure, CPLE_AppDefined, "NULL projection." );
3036 0 : return CE_Failure;
3037 : }
3038 :
3039 317 : if( bSetProjection && (GetAccess() == GA_Update) )
3040 : {
3041 : CPLError( CE_Warning, CPLE_AppDefined,
3042 : "netCDFDataset::SetProjection() should only be called once "
3043 : "in update mode!\npszNewProjection=\n%s",
3044 0 : pszNewProjection );
3045 : }
3046 :
3047 317 : CPLDebug( "GDAL_netCDF", "SetProjection, WKT = %s", pszNewProjection );
3048 :
3049 317 : if( !EQUALN(pszNewProjection,"GEOGCS",6)
3050 : && !EQUALN(pszNewProjection,"PROJCS",6)
3051 : && !EQUAL(pszNewProjection,"") )
3052 : {
3053 : CPLError( CE_Failure, CPLE_AppDefined,
3054 : "Only OGC WKT GEOGCS and PROJCS Projections supported for writing to NetCDF.\n"
3055 : "%s not supported.",
3056 0 : pszNewProjection );
3057 :
3058 0 : return CE_Failure;
3059 : }
3060 :
3061 317 : CPLFree( pszProjection );
3062 317 : pszProjection = CPLStrdup( pszNewProjection );
3063 :
3064 317 : if( GetAccess() == GA_Update )
3065 : {
3066 88 : if ( bSetGeoTransform && ! bSetProjection ) {
3067 27 : bSetProjection = TRUE;
3068 27 : return AddProjectionVars();
3069 : }
3070 : }
3071 :
3072 290 : bSetProjection = TRUE;
3073 :
3074 290 : return CE_None;
3075 :
3076 : }
3077 :
3078 : /************************************************************************/
3079 : /* SetGeoTransform() */
3080 : /************************************************************************/
3081 :
3082 239 : CPLErr netCDFDataset::SetGeoTransform ( double * padfTransform )
3083 : {
3084 239 : CPLMutexHolderD(&hNCMutex);
3085 :
3086 239 : memcpy( adfGeoTransform, padfTransform, sizeof(double)*6 );
3087 : // bGeoTransformValid = TRUE;
3088 : // bGeoTIFFInfoChanged = TRUE;
3089 :
3090 : CPLDebug( "GDAL_netCDF",
3091 : "SetGeoTransform(%f,%f,%f,%f,%f,%f)",
3092 : padfTransform[0],padfTransform[1],padfTransform[2],
3093 239 : padfTransform[3],padfTransform[4],padfTransform[5]);
3094 :
3095 239 : if( GetAccess() == GA_Update )
3096 : {
3097 87 : if ( bSetProjection && ! bSetGeoTransform ) {
3098 0 : bSetGeoTransform = TRUE;
3099 0 : return AddProjectionVars();
3100 : }
3101 : }
3102 :
3103 239 : bSetGeoTransform = TRUE;
3104 :
3105 239 : return CE_None;
3106 :
3107 : }
3108 :
3109 : /************************************************************************/
3110 : /* AddProjectionVars() */
3111 : /************************************************************************/
3112 :
3113 89 : CPLErr netCDFDataset::AddProjectionVars( GDALProgressFunc pfnProgress,
3114 : void * pProgressData )
3115 : {
3116 89 : OGRSpatialReference oSRS;
3117 89 : int NCDFVarID = -1;
3118 89 : double dfTemp = 0.0;
3119 89 : const char *pszValue = NULL;
3120 : char szTemp[ NCDF_MAX_STR_LEN ];
3121 89 : CPLErr eErr = CE_None;
3122 :
3123 : char szGeoTransform[ NCDF_MAX_STR_LEN ];
3124 89 : *szGeoTransform = '\0';
3125 89 : char *pszWKT = NULL;
3126 89 : const char *pszUnits = NULL;
3127 : char szUnits[ NCDF_MAX_STR_LEN ];
3128 89 : szUnits[0]='\0';
3129 :
3130 89 : int bWriteGridMapping = FALSE;
3131 89 : int bWriteLonLat = FALSE;
3132 89 : int bHasGeoloc = FALSE;
3133 89 : int bWriteGDALTags = FALSE;
3134 89 : int bWriteGeoTransform = FALSE;
3135 :
3136 89 : nc_type eLonLatType = NC_NAT;
3137 89 : int nVarLonID=-1, nVarLatID=-1;
3138 89 : int nVarXID=-1, nVarYID=-1;
3139 :
3140 : /* For GEOLOCATION information */
3141 89 : char ** papszGeolocationInfo = NULL;
3142 89 : const char *pszDSName = NULL;
3143 89 : GDALDatasetH hDS_X = NULL;
3144 89 : GDALRasterBandH hBand_X = NULL;
3145 89 : GDALDatasetH hDS_Y = NULL;
3146 89 : GDALRasterBandH hBand_Y = NULL;
3147 : int nBand;
3148 :
3149 89 : bAddedProjectionVars = TRUE;
3150 :
3151 89 : pszWKT = (char *) pszProjection;
3152 89 : oSRS.importFromWkt( &pszWKT );
3153 :
3154 89 : if( oSRS.IsProjected() )
3155 41 : bIsProjected = TRUE;
3156 48 : else if( oSRS.IsGeographic() )
3157 39 : bIsGeographic = TRUE;
3158 :
3159 : CPLDebug( "GDAL_netCDF", "SetProjection, WKT now = [%s]\nprojected: %d geographic: %d",
3160 89 : pszProjection,bIsProjected,bIsGeographic );
3161 :
3162 89 : if ( ! bSetGeoTransform )
3163 : CPLDebug( "GDAL_netCDF", "netCDFDataset::AddProjectionVars() called, "
3164 2 : "but GeoTransform has not yet been defined!" );
3165 :
3166 89 : if ( ! bSetProjection )
3167 : CPLDebug( "GDAL_netCDF", "netCDFDataset::AddProjectionVars() called, "
3168 1 : "but Projection has not yet been defined!" );
3169 :
3170 : /* check GEOLOCATION information */
3171 89 : papszGeolocationInfo = GetMetadata("GEOLOCATION");
3172 89 : if ( papszGeolocationInfo != NULL ) {
3173 :
3174 : /* look for geolocation datasets */
3175 0 : pszDSName = CSLFetchNameValue( papszGeolocationInfo, "X_DATASET" );
3176 0 : if( pszDSName != NULL )
3177 0 : hDS_X = GDALOpenShared( pszDSName, GA_ReadOnly );
3178 0 : pszDSName = CSLFetchNameValue( papszGeolocationInfo, "Y_DATASET" );
3179 0 : if( pszDSName != NULL )
3180 0 : hDS_Y = GDALOpenShared( pszDSName, GA_ReadOnly );
3181 :
3182 0 : if ( hDS_X != NULL && hDS_Y != NULL ) {
3183 0 : nBand = MAX(1,atoi(CSLFetchNameValue( papszGeolocationInfo, "X_BAND" )));
3184 0 : hBand_X = GDALGetRasterBand( hDS_X, nBand );
3185 0 : nBand = MAX(1,atoi(CSLFetchNameValue( papszGeolocationInfo, "Y_BAND" )));
3186 0 : hBand_Y = GDALGetRasterBand( hDS_Y, nBand );
3187 :
3188 : /* if geoloc bands are found do basic vaidation based on their dimensions */
3189 0 : if ( hDS_X != NULL && hDS_Y != NULL ) {
3190 :
3191 0 : int nXSize_XBand = GDALGetRasterXSize( hDS_X );
3192 0 : int nYSize_XBand = GDALGetRasterYSize( hDS_X );
3193 0 : int nXSize_YBand = GDALGetRasterXSize( hDS_Y );
3194 0 : int nYSize_YBand = GDALGetRasterYSize( hDS_Y );
3195 :
3196 : /* TODO 1D geolocation arrays not implemented */
3197 0 : if ( (nYSize_XBand == 1) && (nYSize_YBand == 1) ) {
3198 0 : bHasGeoloc = FALSE;
3199 : CPLDebug( "GDAL_netCDF",
3200 0 : "1D GEOLOCATION arrays not supported yet" );
3201 : }
3202 : /* 2D bands must have same sizes as the raster bands */
3203 0 : else if ( (nXSize_XBand != nRasterXSize) ||
3204 : (nYSize_XBand != nRasterYSize) ||
3205 : (nXSize_YBand != nRasterXSize) ||
3206 : (nYSize_YBand != nRasterYSize) ) {
3207 0 : bHasGeoloc = FALSE;
3208 : CPLDebug( "GDAL_netCDF",
3209 : "GEOLOCATION array sizes (%dx%d %dx%d) differ from raster (%dx%d), not supported",
3210 : nXSize_XBand, nYSize_XBand, nXSize_YBand, nYSize_YBand,
3211 0 : nRasterXSize, nRasterYSize );
3212 : }
3213 : /* 2D bands are only supported for projected SRS (see CF 5.6) */
3214 0 : else if ( ! bIsProjected ) {
3215 0 : bHasGeoloc = FALSE;
3216 : CPLDebug( "GDAL_netCDF",
3217 0 : "2D GEOLOCATION arrays only supported for projected SRS" );
3218 : }
3219 : else {
3220 0 : bHasGeoloc = TRUE;
3221 : CPLDebug( "GDAL_netCDF",
3222 0 : "dataset has GEOLOCATION information, will try to write it" );
3223 : }
3224 : }
3225 : }
3226 : }
3227 :
3228 : /* process projection options */
3229 89 : if( bIsProjected )
3230 : {
3231 41 : int bIsCfProjection = NCDFIsCfProjection( oSRS.GetAttrValue( "PROJECTION" ) );
3232 41 : bWriteGridMapping = TRUE;
3233 41 : bWriteGDALTags = CSLFetchBoolean( papszCreationOptions, "WRITE_GDAL_TAGS", TRUE );
3234 : /* force WRITE_GDAL_TAGS if is not a CF projection */
3235 41 : if ( ! bWriteGDALTags && ! bIsCfProjection )
3236 0 : bWriteGDALTags = TRUE;
3237 41 : if ( bWriteGDALTags )
3238 41 : bWriteGeoTransform = TRUE;
3239 :
3240 : /* write lon/lat : default is NO, except if has geolocation */
3241 : /* with IF_NEEDED : write if has geoloc or is not CF projection */
3242 41 : pszValue = CSLFetchNameValue( papszCreationOptions,"WRITE_LONLAT" );
3243 41 : if ( pszValue ) {
3244 0 : if ( EQUAL( pszValue, "IF_NEEDED" ) ) {
3245 0 : if ( bHasGeoloc || ! bIsCfProjection )
3246 0 : bWriteLonLat = TRUE;
3247 : else
3248 0 : bWriteLonLat = FALSE;
3249 : }
3250 0 : else bWriteLonLat = CSLTestBoolean( pszValue );
3251 : }
3252 : else
3253 41 : bWriteLonLat = bHasGeoloc;
3254 :
3255 : /* save value of pszCFCoordinates for later */
3256 41 : if ( bWriteLonLat == TRUE ) {
3257 0 : pszCFCoordinates = CPLStrdup( NCDF_LONLAT );
3258 : }
3259 :
3260 41 : eLonLatType = NC_FLOAT;
3261 41 : pszValue = CSLFetchNameValueDef(papszCreationOptions,"TYPE_LONLAT", "FLOAT");
3262 41 : if ( EQUAL(pszValue, "DOUBLE" ) )
3263 0 : eLonLatType = NC_DOUBLE;
3264 : }
3265 : else
3266 : {
3267 : /* files without a Datum will not have a grid_mapping variable and geographic information */
3268 48 : if ( bIsGeographic ) bWriteGridMapping = TRUE;
3269 9 : else bWriteGridMapping = FALSE;
3270 48 : bWriteGDALTags = CSLFetchBoolean( papszCreationOptions, "WRITE_GDAL_TAGS", bWriteGridMapping );
3271 48 : if ( bWriteGDALTags )
3272 39 : bWriteGeoTransform = TRUE;
3273 :
3274 48 : pszValue = CSLFetchNameValueDef(papszCreationOptions,"WRITE_LONLAT", "YES");
3275 48 : if ( EQUAL( pszValue, "IF_NEEDED" ) )
3276 0 : bWriteLonLat = TRUE;
3277 48 : else bWriteLonLat = CSLTestBoolean( pszValue );
3278 : /* Don't write lon/lat if no source geotransform */
3279 48 : if ( ! bSetGeoTransform )
3280 2 : bWriteLonLat = FALSE;
3281 : /* If we don't write lon/lat, set dimnames to X/Y and write gdal tags*/
3282 48 : if ( ! bWriteLonLat ) {
3283 : CPLError( CE_Warning, CPLE_AppDefined,
3284 2 : "creating geographic file without lon/lat values!");
3285 2 : if ( bSetGeoTransform ) {
3286 0 : bWriteGDALTags = TRUE; //not desireable if no geotransform
3287 0 : bWriteGeoTransform = TRUE;
3288 : }
3289 : }
3290 :
3291 48 : eLonLatType = NC_DOUBLE;
3292 48 : pszValue = CSLFetchNameValueDef(papszCreationOptions,"TYPE_LONLAT", "DOUBLE");
3293 48 : if ( EQUAL(pszValue, "FLOAT" ) )
3294 0 : eLonLatType = NC_FLOAT;
3295 : }
3296 :
3297 : /* make sure we write grid_mapping if we need to write GDAL tags */
3298 89 : if ( bWriteGDALTags ) bWriteGridMapping = TRUE;
3299 :
3300 : /* bottom-up value: new driver is bottom-up by default */
3301 : /* override with WRITE_BOTTOMUP */
3302 89 : bBottomUp = CSLFetchBoolean( papszCreationOptions, "WRITE_BOTTOMUP", TRUE );
3303 :
3304 : CPLDebug( "GDAL_netCDF",
3305 : "bIsProjected=%d bIsGeographic=%d bWriteGridMapping=%d "
3306 : "bWriteGDALTags=%d bWriteLonLat=%d bBottomUp=%d bHasGeoloc=%d",
3307 : bIsProjected,bIsGeographic,bWriteGridMapping,
3308 89 : bWriteGDALTags,bWriteLonLat,bBottomUp,bHasGeoloc );
3309 :
3310 : /* exit if nothing to do */
3311 89 : if ( !bIsProjected && !bWriteLonLat )
3312 2 : return CE_None;
3313 :
3314 : /* -------------------------------------------------------------------- */
3315 : /* Define dimension names */
3316 : /* -------------------------------------------------------------------- */
3317 : /* make sure we are in define mode */
3318 87 : SetDefineMode( TRUE );
3319 :
3320 :
3321 : /* -------------------------------------------------------------------- */
3322 : /* Rename dimensions if lon/lat */
3323 : /* -------------------------------------------------------------------- */
3324 87 : if( ! bIsProjected )
3325 : {
3326 : /* rename dims to lat/lon */
3327 46 : papszDimName.Clear(); //if we add other dims one day, this has to change
3328 46 : papszDimName.AddString( NCDF_DIMNAME_LAT );
3329 46 : papszDimName.AddString( NCDF_DIMNAME_LON );
3330 :
3331 46 : status = nc_rename_dim(cdfid, nYDimID, NCDF_DIMNAME_LAT );
3332 46 : NCDF_ERR(status);
3333 46 : status = nc_rename_dim(cdfid, nXDimID, NCDF_DIMNAME_LON );
3334 46 : NCDF_ERR(status);
3335 : }
3336 :
3337 : /* -------------------------------------------------------------------- */
3338 : /* Write projection attributes */
3339 : /* -------------------------------------------------------------------- */
3340 87 : if( bWriteGridMapping == TRUE )
3341 : {
3342 :
3343 80 : if( bIsProjected )
3344 : {
3345 : /* -------------------------------------------------------------------- */
3346 : /* Write CF-1.5 compliant Projected attributes */
3347 : /* -------------------------------------------------------------------- */
3348 :
3349 41 : const OGR_SRSNode *poPROJCS = oSRS.GetAttrNode( "PROJCS" );
3350 : const char *pszProjName;
3351 41 : pszProjName = oSRS.GetAttrValue( "PROJECTION" );
3352 :
3353 : /* Basic Projection info (grid_mapping and datum) */
3354 823 : for( int i=0; poNetcdfSRS_PT[i].WKT_SRS != NULL; i++ ) {
3355 823 : if( EQUAL( poNetcdfSRS_PT[i].WKT_SRS, pszProjName ) ) {
3356 : CPLDebug( "GDAL_netCDF", "GDAL PROJECTION = %s , NCDF PROJECTION = %s",
3357 : poNetcdfSRS_PT[i].WKT_SRS,
3358 41 : poNetcdfSRS_PT[i].CF_SRS);
3359 41 : pszCFProjection = CPLStrdup( poNetcdfSRS_PT[i].CF_SRS );
3360 : CPLDebug( "GDAL_netCDF", "nc_def_var(%d,%s,%d)",
3361 41 : cdfid, poNetcdfSRS_PT[i].CF_SRS, NC_CHAR );
3362 : status = nc_def_var( cdfid,
3363 : poNetcdfSRS_PT[i].CF_SRS,
3364 : NC_CHAR,
3365 41 : 0, NULL, &NCDFVarID );
3366 41 : NCDF_ERR(status);
3367 41 : break;
3368 : }
3369 : }
3370 : status = nc_put_att_text( cdfid, NCDFVarID, CF_GRD_MAPPING_NAME,
3371 : strlen( pszCFProjection ),
3372 41 : pszCFProjection );
3373 41 : NCDF_ERR(status);
3374 :
3375 : /* Various projection attributes */
3376 : // PDS: keep in synch with SetProjection function
3377 41 : NCDFWriteProjAttribs(poPROJCS, pszProjName, cdfid, NCDFVarID);
3378 :
3379 : }
3380 : else
3381 : {
3382 : /* -------------------------------------------------------------------- */
3383 : /* Write CF-1.5 compliant Geographics attributes */
3384 : /* Note: WKT information will not be preserved (e.g. WGS84) */
3385 : /* -------------------------------------------------------------------- */
3386 :
3387 39 : pszCFProjection = CPLStrdup( "crs" );
3388 : CPLDebug( "GDAL_netCDF", "nc_def_var(%d,%s,%d)",
3389 39 : cdfid, pszCFProjection, NC_CHAR );
3390 : status = nc_def_var( cdfid, pszCFProjection, NC_CHAR,
3391 39 : 0, NULL, &NCDFVarID );
3392 39 : NCDF_ERR(status);
3393 : status = nc_put_att_text( cdfid, NCDFVarID, CF_GRD_MAPPING_NAME,
3394 : strlen(CF_PT_LATITUDE_LONGITUDE),
3395 39 : CF_PT_LATITUDE_LONGITUDE );
3396 39 : NCDF_ERR(status);
3397 : }
3398 :
3399 : /* -------------------------------------------------------------------- */
3400 : /* Write CF-1.5 compliant common attributes */
3401 : /* -------------------------------------------------------------------- */
3402 :
3403 : /* DATUM information */
3404 80 : dfTemp = oSRS.GetPrimeMeridian();
3405 : nc_put_att_double( cdfid, NCDFVarID, CF_PP_LONG_PRIME_MERIDIAN,
3406 80 : NC_DOUBLE, 1, &dfTemp );
3407 80 : dfTemp = oSRS.GetSemiMajor();
3408 : nc_put_att_double( cdfid, NCDFVarID, CF_PP_SEMI_MAJOR_AXIS,
3409 80 : NC_DOUBLE, 1, &dfTemp );
3410 80 : dfTemp = oSRS.GetInvFlattening();
3411 : nc_put_att_double( cdfid, NCDFVarID, CF_PP_INVERSE_FLATTENING,
3412 80 : NC_DOUBLE, 1, &dfTemp );
3413 :
3414 : /* Optional GDAL custom projection tags */
3415 80 : if ( bWriteGDALTags == TRUE ) {
3416 :
3417 80 : *szGeoTransform = '\0';
3418 560 : for( int i=0; i<6; i++ ) {
3419 : sprintf( szTemp, "%.16g ",
3420 480 : adfGeoTransform[i] );
3421 480 : strcat( szGeoTransform, szTemp );
3422 : }
3423 80 : CPLDebug( "GDAL_netCDF", "szGeoTranform = %s", szGeoTransform );
3424 :
3425 : // if ( strlen(pszProj4Defn) > 0 ) {
3426 : // nc_put_att_text( cdfid, NCDFVarID, "proj4",
3427 : // strlen( pszProj4Defn ), pszProj4Defn );
3428 : // }
3429 : nc_put_att_text( cdfid, NCDFVarID, NCDF_SPATIAL_REF,
3430 80 : strlen( pszProjection ), pszProjection );
3431 : /* for now write the geotransform for back-compat or else
3432 : the old (1.8.1) driver overrides the CF geotransform with
3433 : empty values from dfNN, dfSN, dfEE, dfWE; */
3434 : /* TODO: fix this in 1.8 branch, and then remove this here */
3435 80 : if ( bWriteGeoTransform && bSetGeoTransform ) {
3436 : nc_put_att_text( cdfid, NCDFVarID, NCDF_GEOTRANSFORM,
3437 : strlen( szGeoTransform ),
3438 80 : szGeoTransform );
3439 : }
3440 : }
3441 :
3442 : /* write projection variable to band variable */
3443 : /* need to call later if there are no bands */
3444 80 : AddGridMappingRef();
3445 :
3446 : } /* end if( bWriteGridMapping ) */
3447 :
3448 87 : pfnProgress( 0.10, NULL, pProgressData );
3449 :
3450 : /* -------------------------------------------------------------------- */
3451 : /* Write CF Projection vars */
3452 : /* -------------------------------------------------------------------- */
3453 :
3454 : /* -------------------------------------------------------------------- */
3455 : /* Write X/Y attributes */
3456 : /* -------------------------------------------------------------------- */
3457 87 : if( bIsProjected )
3458 : {
3459 41 : pszUnits = oSRS.GetAttrValue("PROJCS|UNIT",1);
3460 81 : if ( pszUnits == NULL || EQUAL(pszUnits,"1") )
3461 40 : strcpy(szUnits,"m");
3462 1 : else if ( EQUAL(pszUnits,"1000") )
3463 0 : strcpy(szUnits,"km");
3464 :
3465 : /* X */
3466 : int anXDims[1];
3467 41 : anXDims[0] = nXDimID;
3468 : CPLDebug( "GDAL_netCDF", "nc_def_var(%d,%s,%d)",
3469 41 : cdfid, NCDF_DIMNAME_X, NC_DOUBLE );
3470 : status = nc_def_var( cdfid, NCDF_DIMNAME_X, NC_DOUBLE,
3471 41 : 1, anXDims, &NCDFVarID );
3472 41 : NCDF_ERR(status);
3473 41 : nVarXID=NCDFVarID;
3474 : nc_put_att_text( cdfid, NCDFVarID, CF_STD_NAME,
3475 : strlen(CF_PROJ_X_COORD),
3476 41 : CF_PROJ_X_COORD );
3477 : nc_put_att_text( cdfid, NCDFVarID, CF_LNG_NAME,
3478 : strlen(CF_PROJ_X_COORD_LONG_NAME),
3479 41 : CF_PROJ_X_COORD_LONG_NAME );
3480 41 : nc_put_att_text( cdfid, NCDFVarID, CF_UNITS, strlen(szUnits), szUnits );
3481 :
3482 : /* Y */
3483 : int anYDims[1];
3484 41 : anYDims[0] = nYDimID;
3485 : CPLDebug( "GDAL_netCDF", "nc_def_var(%d,%s,%d)",
3486 41 : cdfid, NCDF_DIMNAME_Y, NC_DOUBLE );
3487 : status = nc_def_var( cdfid, NCDF_DIMNAME_Y, NC_DOUBLE,
3488 41 : 1, anYDims, &NCDFVarID );
3489 41 : NCDF_ERR(status);
3490 41 : nVarYID=NCDFVarID;
3491 : nc_put_att_text( cdfid, NCDFVarID, CF_STD_NAME,
3492 : strlen(CF_PROJ_Y_COORD),
3493 41 : CF_PROJ_Y_COORD );
3494 : nc_put_att_text( cdfid, NCDFVarID, CF_LNG_NAME,
3495 : strlen(CF_PROJ_Y_COORD_LONG_NAME),
3496 41 : CF_PROJ_Y_COORD_LONG_NAME );
3497 41 : nc_put_att_text( cdfid, NCDFVarID, CF_UNITS, strlen(szUnits), szUnits );
3498 : }
3499 :
3500 : /* -------------------------------------------------------------------- */
3501 : /* Write lat/lon attributes if needed */
3502 : /* -------------------------------------------------------------------- */
3503 87 : if ( bWriteLonLat ) {
3504 46 : int *panLatDims=NULL;
3505 46 : int *panLonDims=NULL;
3506 46 : int nLatDims=-1;
3507 46 : int nLonDims=-1;
3508 :
3509 : /* get information */
3510 46 : if ( bHasGeoloc ) { /* geoloc */
3511 0 : nLatDims = 2;
3512 0 : panLatDims = (int *) CPLCalloc( nLatDims, sizeof( int ) );
3513 0 : panLatDims[0] = nYDimID;
3514 0 : panLatDims[1] = nXDimID;
3515 0 : nLonDims = 2;
3516 0 : panLonDims = (int *) CPLCalloc( nLonDims, sizeof( int ) );
3517 0 : panLonDims[0] = nYDimID;
3518 0 : panLonDims[1] = nXDimID;
3519 : }
3520 46 : else if ( bIsProjected ) { /* projected */
3521 0 : nLatDims = 2;
3522 0 : panLatDims = (int *) CPLCalloc( nLatDims, sizeof( int ) );
3523 0 : panLatDims[0] = nYDimID;
3524 0 : panLatDims[1] = nXDimID;
3525 0 : nLonDims = 2;
3526 0 : panLonDims = (int *) CPLCalloc( nLonDims, sizeof( int ) );
3527 0 : panLonDims[0] = nYDimID;
3528 0 : panLonDims[1] = nXDimID;
3529 : }
3530 : else { /* geographic */
3531 46 : nLatDims = 1;
3532 46 : panLatDims = (int *) CPLCalloc( nLatDims, sizeof( int ) );
3533 46 : panLatDims[0] = nYDimID;
3534 46 : nLonDims = 1;
3535 46 : panLonDims = (int *) CPLCalloc( nLonDims, sizeof( int ) );
3536 46 : panLonDims[0] = nXDimID;
3537 : }
3538 :
3539 : /* def vars and attributes */
3540 : status = nc_def_var( cdfid, NCDF_DIMNAME_LAT, eLonLatType,
3541 46 : nLatDims, panLatDims, &NCDFVarID );
3542 : CPLDebug( "GDAL_netCDF", "nc_def_var(%d,%s,%d,%d,-,-) got id %d",
3543 46 : cdfid, NCDF_DIMNAME_LAT, eLonLatType, nLatDims, NCDFVarID );
3544 46 : NCDF_ERR(status);
3545 46 : DefVarDeflate( NCDFVarID );
3546 46 : nVarLatID = NCDFVarID;
3547 : nc_put_att_text( cdfid, NCDFVarID, CF_STD_NAME,
3548 46 : 8,"latitude" );
3549 : nc_put_att_text( cdfid, NCDFVarID, CF_LNG_NAME,
3550 46 : 8, "latitude" );
3551 : nc_put_att_text( cdfid, NCDFVarID, CF_UNITS,
3552 46 : 13, "degrees_north" );
3553 :
3554 : status = nc_def_var( cdfid, NCDF_DIMNAME_LON, eLonLatType,
3555 46 : nLonDims, panLonDims, &NCDFVarID );
3556 : CPLDebug( "GDAL_netCDF", "nc_def_var(%d,%s,%d,%d,-,-) got id %d",
3557 46 : cdfid, NCDF_DIMNAME_LON, eLonLatType, nLatDims, NCDFVarID );
3558 46 : NCDF_ERR(status);
3559 46 : DefVarDeflate( NCDFVarID );
3560 46 : nVarLonID = NCDFVarID;
3561 : nc_put_att_text( cdfid, NCDFVarID, CF_STD_NAME,
3562 46 : 9, "longitude" );
3563 : nc_put_att_text( cdfid, NCDFVarID, CF_LNG_NAME,
3564 46 : 9, "longitude" );
3565 : nc_put_att_text( cdfid, NCDFVarID, CF_UNITS,
3566 46 : 12, "degrees_east" );
3567 : /* free data */
3568 46 : CPLFree( panLatDims );
3569 46 : CPLFree( panLonDims );
3570 :
3571 : }
3572 :
3573 87 : pfnProgress( 0.50, NULL, pProgressData );
3574 :
3575 : /* -------------------------------------------------------------------- */
3576 : /* Get projection values */
3577 : /* -------------------------------------------------------------------- */
3578 :
3579 : double dfX0, dfDX, dfY0, dfDY;
3580 87 : dfX0=0.0, dfDX=0.0, dfY0=0.0, dfDY=0.0;
3581 87 : double *padLonVal = NULL;
3582 87 : double *padLatVal = NULL; /* should use float for projected, save space */
3583 :
3584 87 : if( bIsProjected )
3585 : {
3586 : // const char *pszProjection;
3587 41 : OGRSpatialReference oSRS;
3588 41 : OGRSpatialReference *poLatLonSRS = NULL;
3589 41 : OGRCoordinateTransformation *poTransform = NULL;
3590 :
3591 41 : char *pszWKT = (char *) pszProjection;
3592 41 : oSRS.importFromWkt( &pszWKT );
3593 :
3594 41 : double *padYVal = NULL;
3595 41 : double *padXVal = NULL;
3596 : size_t startX[1];
3597 : size_t countX[1];
3598 : size_t startY[1];
3599 : size_t countY[1];
3600 :
3601 41 : CPLDebug("GDAL_netCDF", "Getting (X,Y) values" );
3602 :
3603 41 : padXVal = (double *) CPLMalloc( nRasterXSize * sizeof( double ) );
3604 41 : padYVal = (double *) CPLMalloc( nRasterYSize * sizeof( double ) );
3605 :
3606 : /* -------------------------------------------------------------------- */
3607 : /* Get Y values */
3608 : /* -------------------------------------------------------------------- */
3609 41 : if ( ! bBottomUp )
3610 0 : dfY0 = adfGeoTransform[3];
3611 : else /* invert latitude values */
3612 41 : dfY0 = adfGeoTransform[3] + ( adfGeoTransform[5] * nRasterYSize );
3613 41 : dfDY = adfGeoTransform[5];
3614 :
3615 3015 : for( int j=0; j<nRasterYSize; j++ ) {
3616 : /* The data point is centered inside the pixel */
3617 2974 : if ( ! bBottomUp )
3618 0 : padYVal[j] = dfY0 + (j+0.5)*dfDY ;
3619 : else /* invert latitude values */
3620 2974 : padYVal[j] = dfY0 - (j+0.5)*dfDY ;
3621 : }
3622 41 : startX[0] = 0;
3623 41 : countX[0] = nRasterXSize;
3624 :
3625 : /* -------------------------------------------------------------------- */
3626 : /* Get X values */
3627 : /* -------------------------------------------------------------------- */
3628 41 : dfX0 = adfGeoTransform[0];
3629 41 : dfDX = adfGeoTransform[1];
3630 :
3631 6585 : for( int i=0; i<nRasterXSize; i++ ) {
3632 : /* The data point is centered inside the pixel */
3633 6544 : padXVal[i] = dfX0 + (i+0.5)*dfDX ;
3634 : }
3635 41 : startY[0] = 0;
3636 41 : countY[0] = nRasterYSize;
3637 :
3638 : /* -------------------------------------------------------------------- */
3639 : /* Write X/Y values */
3640 : /* -------------------------------------------------------------------- */
3641 : /* make sure we are in data mode */
3642 41 : SetDefineMode( FALSE );
3643 :
3644 41 : CPLDebug("GDAL_netCDF", "Writing X values" );
3645 : status = nc_put_vara_double( cdfid, nVarXID, startX,
3646 41 : countX, padXVal);
3647 41 : NCDF_ERR(status);
3648 :
3649 41 : CPLDebug("GDAL_netCDF", "Writing Y values" );
3650 : status = nc_put_vara_double( cdfid, nVarYID, startY,
3651 41 : countY, padYVal);
3652 41 : NCDF_ERR(status);
3653 :
3654 41 : pfnProgress( 0.20, NULL, pProgressData );
3655 :
3656 :
3657 : /* -------------------------------------------------------------------- */
3658 : /* Write lon/lat arrays (CF coordinates) if requested */
3659 : /* -------------------------------------------------------------------- */
3660 :
3661 : /* Get OGR transform if GEOLOCATION is not available */
3662 41 : if ( bWriteLonLat && !bHasGeoloc ) {
3663 0 : poLatLonSRS = oSRS.CloneGeogCS();
3664 0 : if ( poLatLonSRS != NULL )
3665 0 : poTransform = OGRCreateCoordinateTransformation( &oSRS, poLatLonSRS );
3666 : /* if no OGR transform, then don't write CF lon/lat */
3667 0 : if( poTransform == NULL ) {
3668 : CPLError( CE_Failure, CPLE_AppDefined,
3669 0 : "Unable to get Coordinate Transform" );
3670 0 : bWriteLonLat = FALSE;
3671 : }
3672 : }
3673 :
3674 41 : if ( bWriteLonLat ) {
3675 :
3676 0 : if ( ! bHasGeoloc )
3677 0 : CPLDebug("GDAL_netCDF", "Transforming (X,Y)->(lon,lat)" );
3678 : else
3679 0 : CPLDebug("GDAL_netCDF", "writing (lon,lat) from GEOLOCATION arrays" );
3680 :
3681 0 : int bOK = TRUE;
3682 0 : double dfProgress = 0.2;
3683 : int i,j;
3684 :
3685 0 : size_t start[]={ 0, 0 };
3686 0 : size_t count[]={ 1, nRasterXSize };
3687 0 : padLatVal = (double *) CPLMalloc( nRasterXSize * sizeof( double ) );
3688 0 : padLonVal = (double *) CPLMalloc( nRasterXSize * sizeof( double ) );
3689 :
3690 0 : for( j = 0; (j < nRasterYSize) && bOK && (status == NC_NOERR); j++ ) {
3691 :
3692 0 : start[0] = j;
3693 :
3694 : /* get values from geotransform */
3695 0 : if ( ! bHasGeoloc ) {
3696 : /* fill values to transform */
3697 0 : for( i=0; i<nRasterXSize; i++ ) {
3698 0 : padLatVal[i] = padYVal[j];
3699 0 : padLonVal[i] = padXVal[i];
3700 : }
3701 :
3702 : /* do the transform */
3703 : bOK = poTransform->Transform( nRasterXSize,
3704 0 : padLonVal, padLatVal, NULL );
3705 0 : if ( ! bOK ) {
3706 : CPLError( CE_Failure, CPLE_AppDefined,
3707 0 : "Unable to Transform (X,Y) to (lon,lat).\n" );
3708 : }
3709 : }
3710 : /* get values from geoloc arrays */
3711 : else {
3712 : eErr = GDALRasterIO( hBand_Y, GF_Read,
3713 : 0, j, nRasterXSize, 1,
3714 : padLatVal, nRasterXSize, 1,
3715 0 : GDT_Float64, 0, 0 );
3716 0 : if ( eErr == CE_None ) {
3717 : eErr = GDALRasterIO( hBand_X, GF_Read,
3718 : 0, j, nRasterXSize, 1,
3719 : padLonVal, nRasterXSize, 1,
3720 0 : GDT_Float64, 0, 0 );
3721 : }
3722 :
3723 0 : if ( eErr == CE_None )
3724 0 : bOK = TRUE;
3725 : else {
3726 0 : bOK = FALSE;
3727 : CPLError( CE_Failure, CPLE_AppDefined,
3728 0 : "Unable to get scanline %d\n",j );
3729 : }
3730 : }
3731 :
3732 : /* write data */
3733 0 : if ( bOK ) {
3734 : status = nc_put_vara_double( cdfid, nVarLatID, start,
3735 0 : count, padLatVal);
3736 0 : NCDF_ERR(status);
3737 : status = nc_put_vara_double( cdfid, nVarLonID, start,
3738 0 : count, padLonVal);
3739 0 : NCDF_ERR(status);
3740 : }
3741 :
3742 0 : if ( j % (nRasterYSize/10) == 0 ) {
3743 0 : dfProgress += 0.08;
3744 0 : pfnProgress( dfProgress , NULL, pProgressData );
3745 : }
3746 : }
3747 :
3748 : }
3749 :
3750 : /* Free the srs and transform objects */
3751 41 : if ( poLatLonSRS != NULL ) CPLFree( poLatLonSRS );
3752 41 : if ( poTransform != NULL ) CPLFree( poTransform );
3753 :
3754 : /* Free data */
3755 41 : CPLFree( padXVal );
3756 41 : CPLFree( padYVal );
3757 41 : CPLFree( padLonVal );
3758 41 : CPLFree( padLatVal);
3759 :
3760 : } // projected
3761 :
3762 : /* If not Projected assume Geographic to catch grids without Datum */
3763 46 : else if ( bWriteLonLat == TRUE ) {
3764 :
3765 : /* -------------------------------------------------------------------- */
3766 : /* Get latitude values */
3767 : /* -------------------------------------------------------------------- */
3768 46 : if ( ! bBottomUp )
3769 0 : dfY0 = adfGeoTransform[3];
3770 : else /* invert latitude values */
3771 46 : dfY0 = adfGeoTransform[3] + ( adfGeoTransform[5] * nRasterYSize );
3772 46 : dfDY = adfGeoTransform[5];
3773 :
3774 : /* override lat values with the ones in GEOLOCATION/Y_VALUES */
3775 46 : if ( GetMetadataItem( "Y_VALUES", "GEOLOCATION" ) != NULL ) {
3776 0 : int nTemp = 0;
3777 0 : padLatVal = Get1DGeolocation( "Y_VALUES", nTemp );
3778 : /* make sure we got the correct amount, if not fallback to GT */
3779 : /* could add test fabs( fabs(padLatVal[0]) - fabs(dfY0) ) <= 0.1 ) ) */
3780 0 : if ( nTemp == nRasterYSize ) {
3781 0 : CPLDebug("GDAL_netCDF", "Using Y_VALUES geolocation metadata for lat values" );
3782 : }
3783 : else {
3784 : CPLDebug("GDAL_netCDF",
3785 : "Got %d elements from Y_VALUES geolocation metadata, need %d",
3786 0 : nTemp, nRasterYSize );
3787 0 : if ( padLatVal ) {
3788 0 : CPLFree( padLatVal );
3789 0 : padLatVal = NULL;
3790 : }
3791 : }
3792 : }
3793 :
3794 46 : if ( padLatVal == NULL ) {
3795 46 : padLatVal = (double *) CPLMalloc( nRasterYSize * sizeof( double ) );
3796 2999 : for( int i=0; i<nRasterYSize; i++ ) {
3797 : /* The data point is centered inside the pixel */
3798 2953 : if ( ! bBottomUp )
3799 0 : padLatVal[i] = dfY0 + (i+0.5)*dfDY ;
3800 : else /* invert latitude values */
3801 2953 : padLatVal[i] = dfY0 - (i+0.5)*dfDY ;
3802 : }
3803 : }
3804 :
3805 : size_t startLat[1];
3806 : size_t countLat[1];
3807 46 : startLat[0] = 0;
3808 46 : countLat[0] = nRasterYSize;
3809 :
3810 : /* -------------------------------------------------------------------- */
3811 : /* Get longitude values */
3812 : /* -------------------------------------------------------------------- */
3813 46 : dfX0 = adfGeoTransform[0];
3814 46 : dfDX = adfGeoTransform[1];
3815 :
3816 46 : padLonVal = (double *) CPLMalloc( nRasterXSize * sizeof( double ) );
3817 2999 : for( int i=0; i<nRasterXSize; i++ ) {
3818 : /* The data point is centered inside the pixel */
3819 2953 : padLonVal[i] = dfX0 + (i+0.5)*dfDX ;
3820 : }
3821 :
3822 : size_t startLon[1];
3823 : size_t countLon[1];
3824 46 : startLon[0] = 0;
3825 46 : countLon[0] = nRasterXSize;
3826 :
3827 : /* -------------------------------------------------------------------- */
3828 : /* Write latitude and longitude values */
3829 : /* -------------------------------------------------------------------- */
3830 : /* make sure we are in data mode */
3831 46 : SetDefineMode( FALSE );
3832 :
3833 : /* write values */
3834 46 : CPLDebug("GDAL_netCDF", "Writing lat values" );
3835 :
3836 : status = nc_put_vara_double( cdfid, nVarLatID, startLat,
3837 46 : countLat, padLatVal);
3838 46 : NCDF_ERR(status);
3839 :
3840 46 : CPLDebug("GDAL_netCDF", "Writing lon values" );
3841 : status = nc_put_vara_double( cdfid, nVarLonID, startLon,
3842 46 : countLon, padLonVal);
3843 46 : NCDF_ERR(status);
3844 :
3845 : /* free values */
3846 46 : CPLFree( padLatVal );
3847 46 : CPLFree( padLonVal );
3848 :
3849 : }// not projected
3850 :
3851 : /* close geoloc datasets */
3852 87 : if ( bHasGeoloc ) {
3853 0 : GDALClose( hDS_X );
3854 0 : GDALClose( hDS_Y );
3855 : }
3856 :
3857 87 : pfnProgress( 1.00, NULL, pProgressData );
3858 :
3859 87 : return CE_None;
3860 : }
3861 :
3862 : /* Write Projection variable to band variable */
3863 : /* Moved from AddProjectionVars() for cases when bands are added after projection */
3864 176 : void netCDFDataset::AddGridMappingRef( )
3865 : {
3866 176 : int nVarId = -1;
3867 176 : int bOldDefineMode = bDefineMode;
3868 :
3869 176 : if( (GetAccess() == GA_Update) &&
3870 : (nBands >= 1) && (GetRasterBand( 1 )) &&
3871 : pszCFProjection != NULL && ! EQUAL( pszCFProjection, "" ) ) {
3872 :
3873 79 : nVarId = ( (netCDFRasterBand *) GetRasterBand( 1 ) )->nZId;
3874 79 : bAddedGridMappingRef = TRUE;
3875 :
3876 : /* make sure we are in define mode */
3877 79 : SetDefineMode( TRUE );
3878 : status = nc_put_att_text( cdfid, nVarId,
3879 : CF_GRD_MAPPING,
3880 : strlen( pszCFProjection ),
3881 79 : pszCFProjection );
3882 79 : NCDF_ERR(status);
3883 79 : if ( pszCFCoordinates != NULL && ! EQUAL( pszCFCoordinates, "" ) ) {
3884 : status = nc_put_att_text( cdfid, nVarId,
3885 : CF_COORDINATES,
3886 : strlen( pszCFCoordinates ),
3887 0 : pszCFCoordinates );
3888 0 : NCDF_ERR(status);
3889 : }
3890 :
3891 : /* go back to previous define mode */
3892 79 : SetDefineMode( bOldDefineMode );
3893 : }
3894 176 : }
3895 :
3896 :
3897 : /************************************************************************/
3898 : /* GetGeoTransform() */
3899 : /************************************************************************/
3900 :
3901 69 : CPLErr netCDFDataset::GetGeoTransform( double * padfTransform )
3902 :
3903 : {
3904 69 : memcpy( padfTransform, adfGeoTransform, sizeof(double) * 6 );
3905 69 : if( bSetGeoTransform )
3906 65 : return CE_None;
3907 : else
3908 4 : return GDALPamDataset::GetGeoTransform( padfTransform );
3909 : }
3910 :
3911 : /************************************************************************/
3912 : /* rint() */
3913 : /************************************************************************/
3914 :
3915 912 : double netCDFDataset::rint( double dfX)
3916 : {
3917 912 : if( dfX > 0 ) {
3918 308 : int nX = (int) (dfX+0.5);
3919 308 : if( nX % 2 ) {
3920 11 : double dfDiff = dfX - (double)nX;
3921 11 : if( dfDiff == -0.5 )
3922 0 : return double( nX-1 );
3923 : }
3924 308 : return double( nX );
3925 : } else {
3926 604 : int nX= (int) (dfX-0.5);
3927 604 : if( nX % 2 ) {
3928 22 : double dfDiff = dfX - (double)nX;
3929 22 : if( dfDiff == 0.5 )
3930 0 : return double(nX+1);
3931 : }
3932 604 : return double(nX);
3933 : }
3934 : }
3935 :
3936 : /************************************************************************/
3937 : /* ReadAttributes() */
3938 : /************************************************************************/
3939 858 : CPLErr netCDFDataset::ReadAttributes( int cdfid, int var)
3940 :
3941 : {
3942 : char szAttrName[ NC_MAX_NAME ];
3943 : char szVarName [ NC_MAX_NAME ];
3944 : char szMetaName[ NC_MAX_NAME * 2 ];
3945 858 : char *pszMetaTemp = NULL;
3946 : int nbAttr;
3947 :
3948 858 : nc_inq_varnatts( cdfid, var, &nbAttr );
3949 858 : if( var == NC_GLOBAL ) {
3950 208 : strcpy( szVarName,"NC_GLOBAL" );
3951 : }
3952 : else {
3953 650 : nc_inq_varname( cdfid, var, szVarName );
3954 : }
3955 :
3956 4720 : for( int l=0; l < nbAttr; l++) {
3957 :
3958 3862 : nc_inq_attname( cdfid, var, l, szAttrName);
3959 3862 : sprintf( szMetaName, "%s#%s", szVarName, szAttrName );
3960 :
3961 3862 : if ( NCDFGetAttr( cdfid, var, szAttrName, &pszMetaTemp )
3962 : == CE_None ) {
3963 : papszMetadata = CSLSetNameValue(papszMetadata,
3964 : szMetaName,
3965 3862 : pszMetaTemp);
3966 3862 : CPLFree(pszMetaTemp);
3967 3862 : pszMetaTemp = NULL;
3968 : }
3969 : else {
3970 0 : CPLDebug( "GDAL_netCDF", "invalid global metadata %s", szMetaName );
3971 : }
3972 :
3973 : }
3974 :
3975 858 : return CE_None;
3976 :
3977 : }
3978 :
3979 :
3980 : /************************************************************************/
3981 : /* netCDFDataset::CreateSubDatasetList() */
3982 : /************************************************************************/
3983 23 : void netCDFDataset::CreateSubDatasetList( )
3984 : {
3985 :
3986 : char szDim[ MAX_NC_NAME ];
3987 : char szTemp[ MAX_NC_NAME ];
3988 : char szType[ MAX_NC_NAME ];
3989 : char szName[ MAX_NC_NAME ];
3990 : char szVarStdName[ MAX_NC_NAME ];
3991 : int nDims;
3992 : int nVar;
3993 : int nVarCount;
3994 : int i;
3995 : nc_type nVarType;
3996 : int *ponDimIds;
3997 : size_t nDimLen;
3998 : int nSub;
3999 : nc_type nAttype;
4000 : size_t nAttlen;
4001 :
4002 : netCDFDataset *poDS;
4003 23 : poDS = this;
4004 :
4005 23 : nSub=1;
4006 23 : nc_inq_nvars ( cdfid, &nVarCount );
4007 :
4008 160 : for ( nVar = 0; nVar < nVarCount; nVar++ ) {
4009 :
4010 137 : nc_inq_varndims ( cdfid, nVar, &nDims );
4011 :
4012 137 : if( nDims >= 2 ) {
4013 63 : ponDimIds = (int *) CPLCalloc( nDims, sizeof( int ) );
4014 63 : nc_inq_vardimid ( cdfid, nVar, ponDimIds );
4015 :
4016 : /* -------------------------------------------------------------------- */
4017 : /* Create Sub dataset list */
4018 : /* -------------------------------------------------------------------- */
4019 63 : szDim[0]='\0';
4020 194 : for( i = 0; i < nDims; i++ ) {
4021 131 : nc_inq_dimlen ( cdfid, ponDimIds[i], &nDimLen );
4022 131 : sprintf(szTemp, "%d", (int) nDimLen);
4023 131 : strcat(szTemp, "x" );
4024 131 : strcat(szDim, szTemp);
4025 : }
4026 :
4027 63 : nc_inq_vartype( cdfid, nVar, &nVarType );
4028 : /* -------------------------------------------------------------------- */
4029 : /* Get rid of the last "x" character */
4030 : /* -------------------------------------------------------------------- */
4031 63 : szDim[strlen(szDim) - 1] = '\0';
4032 63 : switch( nVarType ) {
4033 :
4034 : case NC_BYTE:
4035 28 : strcpy(szType, "8-bit integer");
4036 28 : break;
4037 : case NC_CHAR:
4038 0 : strcpy(szType, "8-bit character");
4039 0 : break;
4040 : case NC_SHORT:
4041 3 : strcpy(szType, "16-bit integer");
4042 3 : break;
4043 : case NC_INT:
4044 3 : strcpy(szType, "32-bit integer");
4045 3 : break;
4046 : case NC_FLOAT:
4047 26 : strcpy(szType, "32-bit floating-point");
4048 26 : break;
4049 : case NC_DOUBLE:
4050 3 : strcpy(szType, "64-bit floating-point");
4051 : break;
4052 : #ifdef NETCDF_HAS_NC4
4053 : case NC_UBYTE:
4054 : strcpy(szType, "8-bit unsigned integer");
4055 : break;
4056 : case NC_USHORT:
4057 : strcpy(szType, "16-bit unsigned integer");
4058 : break;
4059 : case NC_UINT:
4060 : strcpy(szType, "32-bit unsigned integer");
4061 : break;
4062 : case NC_INT64:
4063 : strcpy(szType, "64-bit integer");
4064 : break;
4065 : case NC_UINT64:
4066 : strcpy(szType, "64-bit unsigned integer");
4067 : break;
4068 : #endif
4069 : default:
4070 : break;
4071 : }
4072 63 : nc_inq_varname( cdfid, nVar, szName);
4073 63 : nc_inq_att( cdfid, nVar, CF_STD_NAME, &nAttype, &nAttlen);
4074 63 : if( nc_get_att_text ( cdfid, nVar, CF_STD_NAME,
4075 : szVarStdName ) == NC_NOERR ) {
4076 0 : szVarStdName[nAttlen] = '\0';
4077 : }
4078 : else {
4079 63 : strcpy( szVarStdName, szName );
4080 : }
4081 :
4082 63 : sprintf( szTemp, "SUBDATASET_%d_NAME", nSub);
4083 :
4084 : poDS->papszSubDatasets =
4085 : CSLSetNameValue( poDS->papszSubDatasets, szTemp,
4086 : CPLSPrintf( "NETCDF:\"%s\":%s",
4087 : poDS->osFilename.c_str(),
4088 63 : szName) ) ;
4089 :
4090 63 : sprintf( szTemp, "SUBDATASET_%d_DESC", nSub++ );
4091 :
4092 : poDS->papszSubDatasets =
4093 : CSLSetNameValue( poDS->papszSubDatasets, szTemp,
4094 : CPLSPrintf( "[%s] %s (%s)",
4095 : szDim,
4096 : szVarStdName,
4097 63 : szType ) );
4098 :
4099 63 : CPLFree(ponDimIds);
4100 : }
4101 : }
4102 :
4103 23 : }
4104 :
4105 : /************************************************************************/
4106 : /* IdentifyFormat() */
4107 : /************************************************************************/
4108 :
4109 13559 : int netCDFDataset::IdentifyFormat( GDALOpenInfo * poOpenInfo, bool bCheckExt = TRUE )
4110 :
4111 : {
4112 : /* -------------------------------------------------------------------- */
4113 : /* Does this appear to be a netcdf file? If so, which format? */
4114 : /* http://www.unidata.ucar.edu/software/netcdf/docs/faq.html#fv1_5 */
4115 : /* -------------------------------------------------------------------- */
4116 :
4117 13559 : if( EQUALN(poOpenInfo->pszFilename,"NETCDF:",7) )
4118 0 : return NCDF_FORMAT_UNKNOWN;
4119 13559 : if ( poOpenInfo->nHeaderBytes < 4 )
4120 11832 : return NCDF_FORMAT_NONE;
4121 1727 : if ( EQUALN((char*)poOpenInfo->pabyHeader,"CDF\001",4) )
4122 207 : return NCDF_FORMAT_NC;
4123 1520 : else if ( EQUALN((char*)poOpenInfo->pabyHeader,"CDF\002",4) )
4124 1 : return NCDF_FORMAT_NC2;
4125 1519 : else if ( EQUALN((char*)poOpenInfo->pabyHeader,"\211HDF\r\n\032\n",8) ) {
4126 : /* Requires netCDF-4/HDF5 support in libnetcdf (not just libnetcdf-v4).
4127 : If HDF5 is not supported in GDAL, this driver will try to open the file
4128 : Else, make sure this driver does not try to open HDF5 files
4129 : If user really wants to open with this driver, use NETCDF:file.h5 format.
4130 : This check should be relaxed, but there is no clear way to make a difference.
4131 : */
4132 :
4133 : /* Check for HDF5 support in GDAL */
4134 : #ifdef HAVE_HDF5
4135 5 : if ( bCheckExt ) { /* Check by default */
4136 5 : const char* pszExtension = CPLGetExtension( poOpenInfo->pszFilename );
4137 5 : if ( ! ( EQUAL( pszExtension, "nc") || EQUAL( pszExtension, "cdf")
4138 : || EQUAL( pszExtension, "nc2") || EQUAL( pszExtension, "nc4") ) )
4139 5 : return NCDF_FORMAT_HDF5;
4140 : }
4141 : #endif
4142 :
4143 : /* Check for netcdf-4 support in libnetcdf */
4144 : #ifdef NETCDF_HAS_NC4
4145 : return NCDF_FORMAT_NC4;
4146 : #else
4147 0 : return NCDF_FORMAT_HDF5;
4148 : #endif
4149 :
4150 : }
4151 1514 : else if ( EQUALN((char*)poOpenInfo->pabyHeader,"\016\003\023\001",4) ) {
4152 : /* Requires HDF4 support in libnetcdf, but if HF4 is supported by GDAL don't try to open. */
4153 : /* If user really wants to open with this driver, use NETCDF:file.hdf syntax. */
4154 :
4155 : /* Check for HDF4 support in GDAL */
4156 : #ifdef HAVE_HDF4
4157 257 : if ( bCheckExt ) { /* Check by default */
4158 : /* Always treat as HDF4 file */
4159 257 : return NCDF_FORMAT_HDF4;
4160 : }
4161 : #endif
4162 :
4163 : /* Check for HDF4 support in libnetcdf */
4164 : #ifdef NETCDF_HAS_HDF4
4165 : return NCDF_FORMAT_NC4;
4166 : #else
4167 0 : return NCDF_FORMAT_HDF4;
4168 : #endif
4169 : }
4170 :
4171 1257 : return NCDF_FORMAT_NONE;
4172 : }
4173 :
4174 : /************************************************************************/
4175 : /* Identify() */
4176 : /************************************************************************/
4177 :
4178 10132 : int netCDFDataset::Identify( GDALOpenInfo * poOpenInfo )
4179 :
4180 : {
4181 10132 : if( EQUALN(poOpenInfo->pszFilename,"NETCDF:",7) ) {
4182 0 : return TRUE;
4183 : }
4184 10132 : int nTmpFormat = IdentifyFormat( poOpenInfo );
4185 10132 : if( NCDF_FORMAT_NC == nTmpFormat ||
4186 : NCDF_FORMAT_NC2 == nTmpFormat ||
4187 : NCDF_FORMAT_NC4 == nTmpFormat ||
4188 : NCDF_FORMAT_NC4C == nTmpFormat )
4189 0 : return TRUE;
4190 : else
4191 10132 : return FALSE;
4192 : }
4193 :
4194 : /************************************************************************/
4195 : /* Open() */
4196 : /************************************************************************/
4197 :
4198 3427 : GDALDataset *netCDFDataset::Open( GDALOpenInfo * poOpenInfo )
4199 :
4200 : {
4201 : int j;
4202 : unsigned int k;
4203 : int nd;
4204 : int cdfid, dim_count, var, var_count;
4205 3427 : int i = 0;
4206 : size_t lev_count;
4207 3427 : size_t nTotLevCount = 1;
4208 3427 : int nDim = 2;
4209 : int status;
4210 : int nDimID;
4211 : char szConventions[NC_MAX_NAME];
4212 : int ndims, nvars, ngatts, unlimdimid;
4213 3427 : int nCount=0;
4214 3427 : int nVarID=-1;
4215 :
4216 3427 : int nTmpFormat=NCDF_FORMAT_NONE;
4217 3427 : int *panBandDimPos=NULL; // X, Y, Z postion in array
4218 3427 : int *panBandZLev=NULL;
4219 3427 : int *paDimIds=NULL;
4220 : size_t xdim, ydim;
4221 : char szTemp[NC_MAX_NAME];
4222 :
4223 3427 : CPLString osSubdatasetName;
4224 : int bTreatAsSubdataset;
4225 :
4226 3427 : char **papszIgnoreVars = NULL;
4227 3427 : char *pszTemp = NULL;
4228 3427 : int nIgnoredVars = 0;
4229 :
4230 : char szDimName[NC_MAX_NAME];
4231 : char szExtraDimNames[NC_MAX_NAME];
4232 : char szExtraDimDef[NC_MAX_NAME];
4233 3427 : nc_type nType=NC_NAT;
4234 :
4235 : /* -------------------------------------------------------------------- */
4236 : /* Does this appear to be a netcdf file? */
4237 : /* -------------------------------------------------------------------- */
4238 3427 : if( ! EQUALN(poOpenInfo->pszFilename,"NETCDF:",7) ) {
4239 3420 : nTmpFormat = IdentifyFormat( poOpenInfo );
4240 : /* Note: not calling Identify() directly, because we want the file type */
4241 : /* Only support NCDF_FORMAT* formats */
4242 3420 : if( ! ( NCDF_FORMAT_NC == nTmpFormat ||
4243 : NCDF_FORMAT_NC2 == nTmpFormat ||
4244 : NCDF_FORMAT_NC4 == nTmpFormat ||
4245 : NCDF_FORMAT_NC4C == nTmpFormat ) )
4246 3219 : return NULL;
4247 : }
4248 :
4249 208 : CPLMutexHolderD(&hNCMutex);
4250 :
4251 : netCDFDataset *poDS;
4252 208 : CPLReleaseMutex(hNCMutex); // Release mutex otherwise we'll deadlock with GDALDataset own mutex
4253 208 : poDS = new netCDFDataset();
4254 208 : CPLAcquireMutex(hNCMutex, 1000.0);
4255 :
4256 208 : poDS->SetDescription( poOpenInfo->pszFilename );
4257 :
4258 : /* -------------------------------------------------------------------- */
4259 : /* Check if filename start with NETCDF: tag */
4260 : /* -------------------------------------------------------------------- */
4261 208 : if( EQUALN( poOpenInfo->pszFilename,"NETCDF:",7) )
4262 : {
4263 : char **papszName =
4264 : CSLTokenizeString2( poOpenInfo->pszFilename,
4265 7 : ":", CSLT_HONOURSTRINGS|CSLT_PRESERVEESCAPES );
4266 :
4267 : /* -------------------------------------------------------------------- */
4268 : /* Check for drive name in windows NETCDF:"D:\... */
4269 : /* -------------------------------------------------------------------- */
4270 7 : if ( CSLCount(papszName) == 4 &&
4271 0 : strlen(papszName[1]) == 1 &&
4272 0 : (papszName[2][0] == '/' || papszName[2][0] == '\\') )
4273 : {
4274 0 : poDS->osFilename = papszName[1];
4275 0 : poDS->osFilename += ':';
4276 0 : poDS->osFilename += papszName[2];
4277 0 : osSubdatasetName = papszName[3];
4278 0 : bTreatAsSubdataset = TRUE;
4279 0 : CSLDestroy( papszName );
4280 : }
4281 7 : else if( CSLCount(papszName) == 3 )
4282 : {
4283 7 : poDS->osFilename = papszName[1];
4284 7 : osSubdatasetName = papszName[2];
4285 7 : bTreatAsSubdataset = TRUE;
4286 7 : CSLDestroy( papszName );
4287 : }
4288 0 : else if( CSLCount(papszName) == 2 )
4289 : {
4290 0 : poDS->osFilename = papszName[1];
4291 0 : osSubdatasetName = "";
4292 0 : bTreatAsSubdataset = FALSE;
4293 0 : CSLDestroy( papszName );
4294 : }
4295 : else
4296 : {
4297 0 : CSLDestroy( papszName );
4298 0 : CPLReleaseMutex(hNCMutex); // Release mutex otherwise we'll deadlock with GDALDataset own mutex
4299 0 : delete poDS;
4300 0 : CPLAcquireMutex(hNCMutex, 1000.0);
4301 : CPLError( CE_Failure, CPLE_AppDefined,
4302 0 : "Failed to parse NETCDF: prefix string into expected 2, 3 or 4 fields." );
4303 0 : return NULL;
4304 : }
4305 : /* Identify Format from real file, with bCheckExt=FALSE */
4306 7 : GDALOpenInfo* poOpenInfo2 = new GDALOpenInfo(poDS->osFilename.c_str(), GA_ReadOnly );
4307 14 : poDS->nFormat = IdentifyFormat( poOpenInfo2, FALSE );
4308 7 : delete poOpenInfo2;
4309 7 : if( NCDF_FORMAT_NONE == poDS->nFormat ||
4310 : NCDF_FORMAT_UNKNOWN == poDS->nFormat ) {
4311 0 : CPLReleaseMutex(hNCMutex); // Release mutex otherwise we'll deadlock with GDALDataset own mutex
4312 0 : delete poDS;
4313 0 : CPLAcquireMutex(hNCMutex, 1000.0);
4314 0 : return NULL;
4315 : }
4316 : }
4317 : else
4318 : {
4319 201 : poDS->osFilename = poOpenInfo->pszFilename;
4320 201 : bTreatAsSubdataset = FALSE;
4321 201 : poDS->nFormat = nTmpFormat;
4322 : }
4323 :
4324 : /* -------------------------------------------------------------------- */
4325 : /* Try opening the dataset. */
4326 : /* -------------------------------------------------------------------- */
4327 208 : CPLDebug( "GDAL_netCDF", "\n=====\ncalling nc_open( %s )", poDS->osFilename.c_str() );
4328 208 : if( nc_open( poDS->osFilename, NC_NOWRITE, &cdfid ) != NC_NOERR ) {
4329 0 : CPLReleaseMutex(hNCMutex); // Release mutex otherwise we'll deadlock with GDALDataset own mutex
4330 0 : delete poDS;
4331 0 : CPLAcquireMutex(hNCMutex, 1000.0);
4332 0 : return NULL;
4333 : }
4334 208 : CPLDebug( "GDAL_netCDF", "got cdfid=%d\n", cdfid );
4335 :
4336 : /* -------------------------------------------------------------------- */
4337 : /* Is this a real netCDF file? */
4338 : /* -------------------------------------------------------------------- */
4339 208 : status = nc_inq(cdfid, &ndims, &nvars, &ngatts, &unlimdimid);
4340 208 : if( status != NC_NOERR ) {
4341 0 : CPLReleaseMutex(hNCMutex); // Release mutex otherwise we'll deadlock with GDALDataset own mutex
4342 0 : delete poDS;
4343 0 : CPLAcquireMutex(hNCMutex, 1000.0);
4344 0 : return NULL;
4345 : }
4346 :
4347 : /* -------------------------------------------------------------------- */
4348 : /* Get file type from netcdf */
4349 : /* -------------------------------------------------------------------- */
4350 208 : status = nc_inq_format (cdfid, &nTmpFormat);
4351 208 : if ( status != NC_NOERR ) {
4352 0 : NCDF_ERR(status);
4353 : }
4354 : else {
4355 : CPLDebug( "GDAL_netCDF",
4356 : "driver detected file type=%d, libnetcdf detected type=%d",
4357 208 : poDS->nFormat, nTmpFormat );
4358 208 : if ( nTmpFormat != poDS->nFormat ) {
4359 : /* warn if file detection conflicts with that from libnetcdf */
4360 : /* except for NC4C, which we have no way of detecting initially */
4361 0 : if ( nTmpFormat != NCDF_FORMAT_NC4C ) {
4362 : CPLError( CE_Warning, CPLE_AppDefined,
4363 : "NetCDF driver detected file type=%d, but libnetcdf detected type=%d",
4364 0 : poDS->nFormat, nTmpFormat );
4365 : }
4366 : CPLDebug( "GDAL_netCDF", "seting file type to %d, was %d",
4367 0 : nTmpFormat, poDS->nFormat );
4368 0 : poDS->nFormat = nTmpFormat;
4369 : }
4370 : }
4371 :
4372 : /* -------------------------------------------------------------------- */
4373 : /* Confirm the requested access is supported. */
4374 : /* -------------------------------------------------------------------- */
4375 208 : if( poOpenInfo->eAccess == GA_Update )
4376 : {
4377 : CPLError( CE_Failure, CPLE_NotSupported,
4378 : "The NETCDF driver does not support update access to existing"
4379 0 : " datasets.\n" );
4380 0 : nc_close( cdfid );
4381 0 : CPLReleaseMutex(hNCMutex); // Release mutex otherwise we'll deadlock with GDALDataset own mutex
4382 0 : delete poDS;
4383 0 : CPLAcquireMutex(hNCMutex, 1000.0);
4384 0 : return NULL;
4385 : }
4386 :
4387 : /* -------------------------------------------------------------------- */
4388 : /* Does the request variable exist? */
4389 : /* -------------------------------------------------------------------- */
4390 208 : if( bTreatAsSubdataset )
4391 : {
4392 7 : status = nc_inq_varid( cdfid, osSubdatasetName, &var);
4393 7 : if( status != NC_NOERR ) {
4394 : CPLError( CE_Warning, CPLE_AppDefined,
4395 : "%s is a netCDF file, but %s is not a variable.",
4396 : poOpenInfo->pszFilename,
4397 0 : osSubdatasetName.c_str() );
4398 :
4399 0 : nc_close( cdfid );
4400 0 : CPLReleaseMutex(hNCMutex); // Release mutex otherwise we'll deadlock with GDALDataset own mutex
4401 0 : delete poDS;
4402 0 : CPLAcquireMutex(hNCMutex, 1000.0);
4403 0 : return NULL;
4404 : }
4405 : }
4406 :
4407 208 : if( nc_inq_ndims( cdfid, &dim_count ) != NC_NOERR || dim_count < 2 )
4408 : {
4409 : CPLError( CE_Warning, CPLE_AppDefined,
4410 : "%s is a netCDF file, but not in GMT configuration.",
4411 0 : poOpenInfo->pszFilename );
4412 :
4413 0 : nc_close( cdfid );
4414 0 : CPLReleaseMutex(hNCMutex); // Release mutex otherwise we'll deadlock with GDALDataset own mutex
4415 0 : delete poDS;
4416 0 : CPLAcquireMutex(hNCMutex, 1000.0);
4417 0 : return NULL;
4418 : }
4419 :
4420 208 : CPLDebug( "GDAL_netCDF", "dim_count = %d", dim_count );
4421 :
4422 208 : szConventions[0] = '\0';
4423 208 : if( (status = nc_get_att_text( cdfid, NC_GLOBAL, "Conventions",
4424 : szConventions )) != NC_NOERR ) {
4425 : CPLError( CE_Warning, CPLE_AppDefined,
4426 5 : "No UNIDATA NC_GLOBAL:Conventions attribute");
4427 : /* note that 'Conventions' is always capital 'C' in CF spec*/
4428 : }
4429 :
4430 :
4431 : /* -------------------------------------------------------------------- */
4432 : /* Create band information objects. */
4433 : /* -------------------------------------------------------------------- */
4434 208 : if ( nc_inq_nvars ( cdfid, &var_count) != NC_NOERR )
4435 : {
4436 0 : CPLReleaseMutex(hNCMutex); // Release mutex otherwise we'll deadlock with GDALDataset own mutex
4437 0 : delete poDS;
4438 0 : CPLAcquireMutex(hNCMutex, 1000.0);
4439 0 : return NULL;
4440 : }
4441 :
4442 208 : CPLDebug( "GDAL_netCDF", "var_count = %d", var_count );
4443 :
4444 : /* -------------------------------------------------------------------- */
4445 : /* Create a corresponding GDALDataset. */
4446 : /* Create Netcdf Subdataset if filename as NETCDF tag */
4447 : /* -------------------------------------------------------------------- */
4448 208 : poDS->cdfid = cdfid;
4449 :
4450 208 : poDS->ReadAttributes( cdfid, NC_GLOBAL );
4451 :
4452 : /* -------------------------------------------------------------------- */
4453 : /* Identify variables that we should ignore as Raster Bands. */
4454 : /* Variables that are identified in other variable's "coordinate" and */
4455 : /* "bounds" attribute should not be treated as Raster Bands. */
4456 : /* See CF sections 5.2, 5.6 and 7.1 */
4457 : /* -------------------------------------------------------------------- */
4458 979 : for ( j = 0; j < nvars; j++ ) {
4459 771 : char **papszTokens = NULL;
4460 771 : if ( NCDFGetAttr( cdfid, j, "coordinates", &pszTemp ) == CE_None ) {
4461 6 : papszTokens = CSLTokenizeString2( pszTemp, " ", 0 );
4462 23 : for ( i=0; i<CSLCount(papszTokens); i++ ) {
4463 17 : papszIgnoreVars = CSLAddString( papszIgnoreVars, papszTokens[i] );
4464 : }
4465 6 : if ( papszTokens) CSLDestroy( papszTokens );
4466 6 : CPLFree( pszTemp );
4467 : }
4468 771 : if ( NCDFGetAttr( cdfid, j, "bounds", &pszTemp ) == CE_None ) {
4469 0 : if ( !EQUAL( pszTemp, "" ) )
4470 0 : papszIgnoreVars = CSLAddString( papszIgnoreVars, pszTemp );
4471 0 : CPLFree( pszTemp );
4472 : }
4473 : }
4474 :
4475 : /* -------------------------------------------------------------------- */
4476 : /* Filter variables (valid 2D raster bands) */
4477 : /* -------------------------------------------------------------------- */
4478 979 : for ( j = 0; j < nvars; j++ ) {
4479 771 : nc_inq_varndims ( cdfid, j, &ndims );
4480 : /* should we ignore this variable ? */
4481 771 : status = nc_inq_varname( cdfid, j, szTemp );
4482 771 : if ( status == NC_NOERR &&
4483 : ( CSLFindString( papszIgnoreVars, szTemp ) != -1 ) ) {
4484 17 : nIgnoredVars++;
4485 17 : CPLDebug( "GDAL_netCDF", "variable #%d [%s] was ignored",j, szTemp);
4486 : }
4487 : /* only accept 2+D vars */
4488 754 : else if( ndims >= 2 ) {
4489 251 : nVarID=j;
4490 251 : nCount++;
4491 : }
4492 : }
4493 :
4494 208 : if ( papszIgnoreVars )
4495 6 : CSLDestroy( papszIgnoreVars );
4496 :
4497 : /* -------------------------------------------------------------------- */
4498 : /* We have more than one variable with 2 dimensions in the */
4499 : /* file, then treat this as a subdataset container dataset. */
4500 : /* -------------------------------------------------------------------- */
4501 208 : if( (nCount > 1) && !bTreatAsSubdataset )
4502 : {
4503 18 : poDS->CreateSubDatasetList();
4504 18 : poDS->SetMetadata( poDS->papszMetadata );
4505 18 : CPLReleaseMutex(hNCMutex); // Release mutex otherwise we'll deadlock with GDALDataset own mutex
4506 18 : poDS->TryLoadXML();
4507 18 : CPLAcquireMutex(hNCMutex, 1000.0);
4508 18 : return( poDS );
4509 : }
4510 :
4511 : /* -------------------------------------------------------------------- */
4512 : /* If we are not treating things as a subdataset, then capture */
4513 : /* the name of the single available variable as the subdataset. */
4514 : /* -------------------------------------------------------------------- */
4515 190 : if( !bTreatAsSubdataset ) // nCount must be 1!
4516 : {
4517 : char szVarName[NC_MAX_NAME];
4518 183 : nc_inq_varname( cdfid, nVarID, szVarName);
4519 183 : osSubdatasetName = szVarName;
4520 : }
4521 :
4522 : /* -------------------------------------------------------------------- */
4523 : /* We have ignored at least one variable, so we should report them */
4524 : /* as subdatasets for reference. */
4525 : /* -------------------------------------------------------------------- */
4526 190 : if( (nIgnoredVars > 0) && !bTreatAsSubdataset )
4527 : {
4528 : CPLDebug( "GDAL_netCDF",
4529 : "As %d variables were ignored, creating subdataset list "
4530 : "for reference. Variable #%d [%s] is the main variable",
4531 5 : nIgnoredVars, nVarID, osSubdatasetName.c_str() );
4532 5 : poDS->CreateSubDatasetList();
4533 : }
4534 :
4535 : /* -------------------------------------------------------------------- */
4536 : /* Open the NETCDF subdataset NETCDF:"filename":subdataset */
4537 : /* -------------------------------------------------------------------- */
4538 190 : var=-1;
4539 190 : nc_inq_varid( cdfid, osSubdatasetName, &var);
4540 190 : nd = 0;
4541 190 : nc_inq_varndims ( cdfid, var, &nd );
4542 :
4543 190 : paDimIds = (int *)CPLCalloc(nd, sizeof( int ) );
4544 190 : panBandDimPos = ( int * ) CPLCalloc( nd, sizeof( int ) );
4545 :
4546 190 : nc_inq_vardimid( cdfid, var, paDimIds );
4547 :
4548 : /* -------------------------------------------------------------------- */
4549 : /* Check if somebody tried to pass a variable with less than 2D */
4550 : /* -------------------------------------------------------------------- */
4551 190 : if ( nd < 2 ) {
4552 : CPLError( CE_Warning, CPLE_AppDefined,
4553 1 : "Variable has %d dimension(s) - not supported.", nd );
4554 1 : CPLFree( paDimIds );
4555 1 : CPLFree( panBandDimPos );
4556 1 : CPLReleaseMutex(hNCMutex); // Release mutex otherwise we'll deadlock with GDALDataset own mutex
4557 1 : delete poDS;
4558 1 : CPLAcquireMutex(hNCMutex, 1000.0);
4559 1 : return NULL;
4560 : }
4561 :
4562 : /* -------------------------------------------------------------------- */
4563 : /* CF-1 Convention */
4564 : /* dimensions to appear in the relative order T, then Z, then Y, */
4565 : /* then X to the file. All other dimensions should, whenever */
4566 : /* possible, be placed to the left of the spatiotemporal */
4567 : /* dimensions. */
4568 : /* -------------------------------------------------------------------- */
4569 :
4570 : /* -------------------------------------------------------------------- */
4571 : /* Verify that dimensions are in the {T,Z,Y,X} or {T,Z,Y,X} order */
4572 : /* Ideally we should detect for other ordering and act accordingly */
4573 : /* Only done if file has Conventions=CF-* and only prints warning */
4574 : /* To disable set GDAL_NETCDF_VERIFY_DIMS=NO and to use only */
4575 : /* attributes (not varnames) set GDAL_NETCDF_VERIFY_DIMS=STRICT */
4576 : /* -------------------------------------------------------------------- */
4577 :
4578 189 : int bCheckDims = FALSE;
4579 : bCheckDims =
4580 : ( CSLTestBoolean( CPLGetConfigOption( "GDAL_NETCDF_VERIFY_DIMS", "YES" ) )
4581 189 : != FALSE ) && EQUALN( szConventions, "CF", 2 );
4582 :
4583 189 : if ( bCheckDims ) {
4584 : char szDimName1[NC_MAX_NAME], szDimName2[NC_MAX_NAME],
4585 : szDimName3[NC_MAX_NAME], szDimName4[NC_MAX_NAME];
4586 179 : szDimName1[0]='\0';
4587 179 : szDimName2[0]='\0';
4588 179 : szDimName3[0]='\0';
4589 179 : szDimName4[0]='\0';
4590 179 : nc_inq_dimname( cdfid, paDimIds[nd-1], szDimName1 );
4591 179 : nc_inq_dimname( cdfid, paDimIds[nd-2], szDimName2 );
4592 179 : if ( NCDFIsVarLongitude( cdfid, -1, szDimName1 )==FALSE &&
4593 : NCDFIsVarProjectionX( cdfid, -1, szDimName1 )==FALSE ) {
4594 : CPLError( CE_Warning, CPLE_AppDefined,
4595 : "dimension #%d (%s) is not a Longitude/X dimension.",
4596 0 : nd-1, szDimName1 );
4597 : }
4598 179 : if ( NCDFIsVarLatitude( cdfid, -1, szDimName2 )==FALSE &&
4599 : NCDFIsVarProjectionY( cdfid, -1, szDimName2 )==FALSE ) {
4600 : CPLError( CE_Warning, CPLE_AppDefined,
4601 : "dimension #%d (%s) is not a Latitude/Y dimension.",
4602 0 : nd-2, szDimName2 );
4603 : }
4604 179 : if ( nd >= 3 ) {
4605 26 : nc_inq_dimname( cdfid, paDimIds[nd-3], szDimName3 );
4606 26 : if ( nd >= 4 ) {
4607 9 : nc_inq_dimname( cdfid, paDimIds[nd-4], szDimName4 );
4608 9 : if ( NCDFIsVarVerticalCoord( cdfid, -1, szDimName3 )==FALSE ) {
4609 : CPLError( CE_Warning, CPLE_AppDefined,
4610 : "dimension #%d (%s) is not a Time dimension.",
4611 0 : nd-3, szDimName3 );
4612 : }
4613 9 : if ( NCDFIsVarTimeCoord( cdfid, -1, szDimName4 )==FALSE ) {
4614 : CPLError( CE_Warning, CPLE_AppDefined,
4615 : "dimension #%d (%s) is not a Time dimension.",
4616 0 : nd-4, szDimName4 );
4617 : }
4618 : }
4619 : else {
4620 17 : if ( NCDFIsVarVerticalCoord( cdfid, -1, szDimName3 )==FALSE &&
4621 : NCDFIsVarTimeCoord( cdfid, -1, szDimName3 )==FALSE ) {
4622 : CPLError( CE_Warning, CPLE_AppDefined,
4623 : "dimension #%d (%s) is not a Time or Vertical dimension.",
4624 0 : nd-3, szDimName3 );
4625 : }
4626 : }
4627 : }
4628 : }
4629 :
4630 : /* -------------------------------------------------------------------- */
4631 : /* Get X dimensions information */
4632 : /* -------------------------------------------------------------------- */
4633 189 : poDS->nXDimID = paDimIds[nd-1];
4634 189 : nc_inq_dimlen ( cdfid, poDS->nXDimID, &xdim );
4635 189 : poDS->nRasterXSize = xdim;
4636 :
4637 : /* -------------------------------------------------------------------- */
4638 : /* Get Y dimension information */
4639 : /* -------------------------------------------------------------------- */
4640 189 : poDS->nYDimID = paDimIds[nd-2];
4641 189 : nc_inq_dimlen ( cdfid, poDS->nYDimID, &ydim );
4642 189 : poDS->nRasterYSize = ydim;
4643 :
4644 :
4645 608 : for( j=0,k=0; j < nd; j++ ){
4646 419 : if( paDimIds[j] == poDS->nXDimID ){
4647 189 : panBandDimPos[0] = j; // Save Position of XDim
4648 189 : k++;
4649 : }
4650 419 : if( paDimIds[j] == poDS->nYDimID ){
4651 189 : panBandDimPos[1] = j; // Save Position of YDim
4652 189 : k++;
4653 : }
4654 : }
4655 : /* -------------------------------------------------------------------- */
4656 : /* X and Y Dimension Ids were not found! */
4657 : /* -------------------------------------------------------------------- */
4658 189 : if( k != 2 ) {
4659 0 : CPLFree( paDimIds );
4660 0 : CPLFree( panBandDimPos );
4661 0 : return NULL;
4662 : }
4663 :
4664 : /* -------------------------------------------------------------------- */
4665 : /* Read Metadata for this variable */
4666 : /* -------------------------------------------------------------------- */
4667 : /* should disable as is also done at band level, except driver needs the
4668 : variables as metadata (e.g. projection) */
4669 189 : poDS->ReadAttributes( cdfid, var );
4670 :
4671 : /* -------------------------------------------------------------------- */
4672 : /* Read Metadata for each dimension */
4673 : /* -------------------------------------------------------------------- */
4674 :
4675 608 : for( j=0; j < dim_count; j++ ){
4676 419 : nc_inq_dimname( cdfid, j, szTemp );
4677 419 : poDS->papszDimName.AddString( szTemp );
4678 419 : status = nc_inq_varid( cdfid, poDS->papszDimName[j], &nDimID );
4679 419 : if( status == NC_NOERR ) {
4680 343 : poDS->ReadAttributes( cdfid, nDimID );
4681 : }
4682 : }
4683 :
4684 : /* -------------------------------------------------------------------- */
4685 : /* Set projection info */
4686 : /* -------------------------------------------------------------------- */
4687 189 : poDS->SetProjectionFromVar( var );
4688 :
4689 : /* override bottom-up with GDAL_NETCDF_BOTTOMUP config option */
4690 189 : const char *pszValue = CPLGetConfigOption( "GDAL_NETCDF_BOTTOMUP", NULL );
4691 189 : if ( pszValue ) {
4692 1 : poDS->bBottomUp = CSLTestBoolean( pszValue ) != FALSE;
4693 : CPLDebug( "GDAL_netCDF",
4694 : "set bBottomUp=%d because GDAL_NETCDF_BOTTOMUP=%s",
4695 1 : poDS->bBottomUp, pszValue );
4696 : }
4697 :
4698 : /* -------------------------------------------------------------------- */
4699 : /* Save non-spatial dimension info */
4700 : /* -------------------------------------------------------------------- */
4701 :
4702 189 : nTotLevCount = 1;
4703 189 : if ( nd > 2 ) {
4704 28 : nDim=2;
4705 28 : panBandZLev = (int *)CPLCalloc( nd-2, sizeof( int ) );
4706 :
4707 28 : strcpy( szExtraDimNames, (char*)"{");
4708 :
4709 125 : for( j=0; j < nd; j++ ){
4710 166 : if( ( paDimIds[j] != poDS->nXDimID ) &&
4711 69 : ( paDimIds[j] != poDS->nYDimID ) ){
4712 41 : nc_inq_dimlen ( cdfid, paDimIds[j], &lev_count );
4713 41 : nTotLevCount *= lev_count;
4714 41 : panBandZLev[ nDim-2 ] = lev_count;
4715 41 : panBandDimPos[ nDim++ ] = j; //Save Position of ZDim
4716 : //Save non-spatial dimension names
4717 41 : if ( nc_inq_dimname( cdfid, paDimIds[j], szDimName )
4718 : == NC_NOERR ) {
4719 41 : strcat( szExtraDimNames, szDimName );
4720 41 : if ( j < nd-3 ) {
4721 13 : strcat( szExtraDimNames, (char *)"," );
4722 : }
4723 41 : nc_inq_varid( cdfid, szDimName, &nVarID );
4724 41 : nc_inq_vartype( cdfid, nVarID, &nType );
4725 41 : sprintf( szExtraDimDef, "{%ld,%d}", (long)lev_count, nType );
4726 41 : sprintf( szTemp, "NETCDF_DIM_%s_DEF", szDimName );
4727 : poDS->papszMetadata = CSLSetNameValue( poDS->papszMetadata,
4728 41 : szTemp, szExtraDimDef );
4729 41 : if ( NCDFGet1DVar( cdfid, nVarID, &pszTemp ) == CE_None ) {
4730 35 : sprintf( szTemp, "NETCDF_DIM_%s_VALUES", szDimName );
4731 : poDS->papszMetadata = CSLSetNameValue( poDS->papszMetadata,
4732 35 : szTemp, pszTemp );
4733 35 : CPLFree( pszTemp );
4734 : }
4735 : }
4736 : }
4737 : }
4738 28 : strcat( szExtraDimNames, (char *)"}" );
4739 : poDS->papszMetadata = CSLSetNameValue( poDS->papszMetadata,
4740 28 : "NETCDF_DIM_EXTRA", szExtraDimNames );
4741 : }
4742 189 : i=0;
4743 :
4744 : /* -------------------------------------------------------------------- */
4745 : /* Store Metadata */
4746 : /* -------------------------------------------------------------------- */
4747 189 : poDS->SetMetadata( poDS->papszMetadata );
4748 :
4749 : /* -------------------------------------------------------------------- */
4750 : /* Create bands */
4751 : /* -------------------------------------------------------------------- */
4752 455 : for ( unsigned int lev = 0; lev < nTotLevCount ; lev++ ) {
4753 : netCDFRasterBand *poBand =
4754 : new netCDFRasterBand(poDS, var, nDim, lev,
4755 : panBandZLev, panBandDimPos,
4756 266 : paDimIds, i+1 );
4757 266 : poDS->SetBand( i+1, poBand );
4758 266 : i++;
4759 : }
4760 :
4761 189 : CPLFree( paDimIds );
4762 189 : CPLFree( panBandDimPos );
4763 189 : if ( panBandZLev )
4764 28 : CPLFree( panBandZLev );
4765 :
4766 189 : poDS->nBands = i;
4767 :
4768 : // Handle angular geographic coordinates here
4769 :
4770 : /* -------------------------------------------------------------------- */
4771 : /* Initialize any PAM information. */
4772 : /* -------------------------------------------------------------------- */
4773 189 : if( bTreatAsSubdataset )
4774 : {
4775 7 : poDS->SetPhysicalFilename( poDS->osFilename );
4776 7 : poDS->SetSubdatasetName( osSubdatasetName );
4777 : }
4778 :
4779 189 : CPLReleaseMutex(hNCMutex); // Release mutex otherwise we'll deadlock with GDALDataset own mutex
4780 189 : poDS->TryLoadXML();
4781 :
4782 189 : if( bTreatAsSubdataset )
4783 7 : poDS->oOvManager.Initialize( poDS, ":::VIRTUAL:::" );
4784 : else
4785 182 : poDS->oOvManager.Initialize( poDS, poDS->osFilename );
4786 :
4787 189 : CPLAcquireMutex(hNCMutex, 1000.0);
4788 :
4789 189 : return( poDS );
4790 : }
4791 :
4792 :
4793 : /************************************************************************/
4794 : /* CopyMetadata() */
4795 : /* */
4796 : /* Create a copy of metadata for NC_GLOBAL or a variable */
4797 : /************************************************************************/
4798 :
4799 153 : void CopyMetadata( void *poDS, int fpImage, int CDFVarID,
4800 : const char *pszPrefix, int bIsBand ) {
4801 :
4802 153 : char **papszMetadata=NULL;
4803 153 : char **papszFieldData=NULL;
4804 : const char *pszField;
4805 : char szMetaName[ NCDF_MAX_STR_LEN ];
4806 : char szMetaValue[ NCDF_MAX_STR_LEN ];
4807 : char szTemp[ NCDF_MAX_STR_LEN ];
4808 : int nItems;
4809 :
4810 : /* Remove the following band meta but set them later from band data */
4811 : const char *papszIgnoreBand[] = { CF_ADD_OFFSET, CF_SCALE_FACTOR,
4812 : "valid_range", "_Unsigned",
4813 : _FillValue, "coordinates",
4814 153 : NULL };
4815 153 : const char *papszIgnoreGlobal[] = { "NETCDF_DIM_EXTRA", NULL };
4816 :
4817 153 : if( CDFVarID == NC_GLOBAL ) {
4818 61 : papszMetadata = GDALGetMetadata( (GDALDataset *) poDS,"");
4819 : } else {
4820 92 : papszMetadata = GDALGetMetadata( (GDALRasterBandH) poDS, NULL );
4821 : }
4822 :
4823 153 : nItems = CSLCount( papszMetadata );
4824 :
4825 1018 : for(int k=0; k < nItems; k++ ) {
4826 865 : pszField = CSLGetField( papszMetadata, k );
4827 865 : if ( papszFieldData ) CSLDestroy( papszFieldData );
4828 : papszFieldData = CSLTokenizeString2 (pszField, "=",
4829 865 : CSLT_HONOURSTRINGS );
4830 865 : if( papszFieldData[1] != NULL ) {
4831 865 : strcpy( szMetaName, papszFieldData[ 0 ] );
4832 865 : strcpy( szMetaValue, papszFieldData[ 1 ] );
4833 :
4834 : /* check for items that match pszPrefix if applicable */
4835 865 : if ( ( pszPrefix != NULL ) && ( !EQUAL( pszPrefix, "" ) ) ) {
4836 : /* remove prefix */
4837 163 : if ( EQUALN( szMetaName, pszPrefix, strlen(pszPrefix) ) ) {
4838 19 : strcpy( szTemp, szMetaName+strlen(pszPrefix) );
4839 19 : strcpy( szMetaName, szTemp );
4840 : }
4841 : /* only copy items that match prefix */
4842 : else
4843 144 : continue;
4844 : }
4845 :
4846 : /* Fix various issues with metadata translation */
4847 721 : if( CDFVarID == NC_GLOBAL ) {
4848 : /* Do not copy items in papszIgnoreGlobal and NETCDF_DIM_* */
4849 521 : if ( ( CSLFindString( (char **)papszIgnoreGlobal, szMetaName ) != -1 ) ||
4850 : ( strncmp( szMetaName, "NETCDF_DIM_", 11 ) == 0 ) )
4851 19 : continue;
4852 : /* Remove NC_GLOBAL prefix for netcdf global Metadata */
4853 502 : else if( strncmp( szMetaName, "NC_GLOBAL#", 10 ) == 0 ) {
4854 95 : strcpy( szTemp, szMetaName+10 );
4855 95 : strcpy( szMetaName, szTemp );
4856 : }
4857 : /* GDAL Metadata renamed as GDAL-[meta] */
4858 407 : else if ( strstr( szMetaName, "#" ) == NULL ) {
4859 39 : strcpy( szTemp, "GDAL_" );
4860 39 : strcat( szTemp, szMetaName );
4861 39 : strcpy( szMetaName, szTemp );
4862 : }
4863 : /* Keep time, lev and depth information for safe-keeping */
4864 : /* Time and vertical coordinate handling need improvements */
4865 : /*
4866 : else if( strncmp( szMetaName, "time#", 5 ) == 0 ) {
4867 : szMetaName[4] = '-';
4868 : }
4869 : else if( strncmp( szMetaName, "lev#", 4 ) == 0 ) {
4870 : szMetaName[3] = '-';
4871 : }
4872 : else if( strncmp( szMetaName, "depth#", 6 ) == 0 ) {
4873 : szMetaName[5] = '-';
4874 : }
4875 : */
4876 : /* Only copy data without # (previously all data was copied) */
4877 502 : if ( strstr( szMetaName, "#" ) != NULL )
4878 368 : continue;
4879 : // /* netCDF attributes do not like the '#' character. */
4880 : // for( unsigned int h=0; h < strlen( szMetaName ) -1 ; h++ ) {
4881 : // if( szMetaName[h] == '#' ) szMetaName[h] = '-';
4882 : // }
4883 : }
4884 : else {
4885 : /* Do not copy varname, stats, NETCDF_DIM_* and items in papszIgnoreBand */
4886 200 : if ( ( strncmp( szMetaName, "NETCDF_VARNAME", 14) == 0 ) ||
4887 : ( strncmp( szMetaName, "STATISTICS_", 11) == 0 ) ||
4888 : ( strncmp( szMetaName, "NETCDF_DIM_", 11 ) == 0 ) ||
4889 : ( CSLFindString( (char **)papszIgnoreBand, szMetaName ) != -1 ) )
4890 127 : continue;
4891 : }
4892 :
4893 207 : if ( NCDFPutAttr( fpImage, CDFVarID,szMetaName,
4894 : szMetaValue ) != CE_None )
4895 : CPLDebug( "GDAL_netCDF", "NCDFPutAttr(%d, %d, %s, %s) failed",
4896 0 : fpImage, CDFVarID,szMetaName, szMetaValue );
4897 : }
4898 : }
4899 153 : if ( papszFieldData ) CSLDestroy( papszFieldData );
4900 :
4901 : /* Set add_offset and scale_factor here if present */
4902 153 : if( ( CDFVarID != NC_GLOBAL ) && ( bIsBand ) ) {
4903 :
4904 : int bGotAddOffset, bGotScale;
4905 85 : GDALRasterBandH poRB = (GDALRasterBandH) poDS;
4906 85 : double dfAddOffset = GDALGetRasterOffset( poRB , &bGotAddOffset );
4907 85 : double dfScale = GDALGetRasterScale( poRB, &bGotScale );
4908 :
4909 85 : if ( bGotAddOffset && dfAddOffset != 0.0 && bGotScale && dfScale != 1.0 ) {
4910 0 : GDALSetRasterOffset( poRB, dfAddOffset );
4911 0 : GDALSetRasterScale( poRB, dfScale );
4912 : }
4913 :
4914 : }
4915 :
4916 153 : }
4917 :
4918 :
4919 : /************************************************************************/
4920 : /* CreateLL() */
4921 : /* */
4922 : /* Shared functionality between netCDFDataset::Create() and */
4923 : /* netCDF::CreateCopy() for creating netcdf file based on a set of */
4924 : /* options and a configuration. */
4925 : /************************************************************************/
4926 :
4927 : netCDFDataset *
4928 114 : netCDFDataset::CreateLL( const char * pszFilename,
4929 : int nXSize, int nYSize, int nBands,
4930 : char ** papszOptions )
4931 : {
4932 114 : int status = NC_NOERR;
4933 : netCDFDataset *poDS;
4934 :
4935 114 : CPLReleaseMutex(hNCMutex); // Release mutex otherwise we'll deadlock with GDALDataset own mutex
4936 114 : poDS = new netCDFDataset();
4937 114 : CPLAcquireMutex(hNCMutex, 1000.0);
4938 :
4939 114 : poDS->nRasterXSize = nXSize;
4940 114 : poDS->nRasterYSize = nYSize;
4941 114 : poDS->eAccess = GA_Update;
4942 228 : poDS->osFilename = pszFilename;
4943 :
4944 : /* from gtiff driver, is this ok? */
4945 : /*
4946 : poDS->nBlockXSize = nXSize;
4947 : poDS->nBlockYSize = 1;
4948 : poDS->nBlocksPerBand =
4949 : ((nYSize + poDS->nBlockYSize - 1) / poDS->nBlockYSize)
4950 : * ((nXSize + poDS->nBlockXSize - 1) / poDS->nBlockXSize);
4951 : */
4952 :
4953 : /* process options */
4954 114 : poDS->papszCreationOptions = CSLDuplicate( papszOptions );
4955 114 : poDS->ProcessCreationOptions( );
4956 :
4957 : /* -------------------------------------------------------------------- */
4958 : /* Create the dataset. */
4959 : /* -------------------------------------------------------------------- */
4960 114 : status = nc_create( pszFilename, poDS->nCreateMode, &(poDS->cdfid) );
4961 :
4962 : /* put into define mode */
4963 114 : poDS->SetDefineMode(TRUE);
4964 :
4965 114 : if( status != NC_NOERR )
4966 : {
4967 : CPLError( CE_Failure, CPLE_OpenFailed,
4968 : "Unable to create netCDF file %s (Error code %d): %s .\n",
4969 13 : pszFilename, status, nc_strerror(status) );
4970 13 : CPLReleaseMutex(hNCMutex); // Release mutex otherwise we'll deadlock with GDALDataset own mutex
4971 13 : delete poDS;
4972 13 : CPLAcquireMutex(hNCMutex, 1000.0);
4973 13 : return NULL;
4974 : }
4975 :
4976 : /* -------------------------------------------------------------------- */
4977 : /* Define dimensions */
4978 : /* -------------------------------------------------------------------- */
4979 101 : poDS->papszDimName.AddString( NCDF_DIMNAME_X );
4980 : status = nc_def_dim( poDS->cdfid, NCDF_DIMNAME_X, nXSize,
4981 101 : &(poDS->nXDimID) );
4982 101 : NCDF_ERR(status);
4983 : CPLDebug( "GDAL_netCDF", "status nc_def_dim( %d, %s, %d, -) got id %d",
4984 101 : poDS->cdfid, NCDF_DIMNAME_X, nXSize, poDS->nXDimID );
4985 :
4986 101 : poDS->papszDimName.AddString( NCDF_DIMNAME_Y );
4987 : status = nc_def_dim( poDS->cdfid, NCDF_DIMNAME_Y, nYSize,
4988 101 : &(poDS->nYDimID) );
4989 101 : NCDF_ERR(status);
4990 : CPLDebug( "GDAL_netCDF", "status nc_def_dim( %d, %s, %d, -) got id %d",
4991 101 : poDS->cdfid, NCDF_DIMNAME_Y, nYSize, poDS->nYDimID );
4992 :
4993 101 : return poDS;
4994 :
4995 : }
4996 :
4997 : /************************************************************************/
4998 : /* Create() */
4999 : /************************************************************************/
5000 :
5001 : GDALDataset *
5002 40 : netCDFDataset::Create( const char * pszFilename,
5003 : int nXSize, int nYSize, int nBands,
5004 : GDALDataType eType,
5005 : char ** papszOptions )
5006 : {
5007 : netCDFDataset *poDS;
5008 :
5009 : CPLDebug( "GDAL_netCDF",
5010 : "\n=====\nnetCDFDataset::Create( %s, ... )\n",
5011 40 : pszFilename );
5012 :
5013 40 : CPLMutexHolderD(&hNCMutex);
5014 :
5015 : poDS = netCDFDataset::CreateLL( pszFilename,
5016 : nXSize, nYSize, nBands,
5017 40 : papszOptions );
5018 :
5019 40 : if ( ! poDS )
5020 0 : return NULL;
5021 :
5022 : /* should we write signed or unsigned byte? */
5023 : /* TODO should this only be done in Create() */
5024 40 : poDS->bSignedData = TRUE;
5025 : const char *pszValue =
5026 40 : CSLFetchNameValue( papszOptions, "PIXELTYPE" );
5027 40 : if( pszValue == NULL )
5028 38 : pszValue = "";
5029 40 : if( eType == GDT_Byte && ( ! EQUAL(pszValue,"SIGNEDBYTE") ) )
5030 10 : poDS->bSignedData = FALSE;
5031 :
5032 : /* -------------------------------------------------------------------- */
5033 : /* Add Conventions, GDAL info and history */
5034 : /* -------------------------------------------------------------------- */
5035 40 : NCDFAddGDALHistory( poDS->cdfid, pszFilename, "", "Create" );
5036 :
5037 : /* -------------------------------------------------------------------- */
5038 : /* Define bands */
5039 : /* -------------------------------------------------------------------- */
5040 109 : for( int iBand = 1; iBand <= nBands; iBand++ )
5041 : {
5042 : poDS->SetBand( iBand, new netCDFRasterBand( poDS, eType, iBand,
5043 69 : poDS->bSignedData ) );
5044 : }
5045 :
5046 : CPLDebug( "GDAL_netCDF",
5047 : "netCDFDataset::Create( %s, ... ) done",
5048 40 : pszFilename );
5049 : /* -------------------------------------------------------------------- */
5050 : /* Return same dataset */
5051 : /* -------------------------------------------------------------------- */
5052 40 : return( poDS );
5053 :
5054 : }
5055 :
5056 :
5057 : template <class T>
5058 85 : CPLErr NCDFCopyBand( GDALRasterBand *poSrcBand, GDALRasterBand *poDstBand,
5059 : int nXSize, int nYSize,
5060 : GDALProgressFunc pfnProgress, void * pProgressData )
5061 : {
5062 85 : GDALDataType eDT = poSrcBand->GetRasterDataType();
5063 85 : CPLErr eErr = CE_None;
5064 85 : T *patScanline = (T *) CPLMalloc( nXSize * sizeof(T) );
5065 :
5066 3652 : for( int iLine = 0; iLine < nYSize && eErr == CE_None; iLine++ ) {
5067 3567 : eErr = poSrcBand->RasterIO( GF_Read, 0, iLine, nXSize, 1,
5068 : patScanline, nXSize, 1, eDT,
5069 : 0,0);
5070 3567 : if ( eErr != CE_None )
5071 0 : CPLDebug( "GDAL_netCDF",
5072 : "NCDFCopyBand(), poSrcBand->RasterIO() returned error code %d",
5073 : eErr );
5074 : else {
5075 3567 : eErr = poDstBand->RasterIO( GF_Write, 0, iLine, nXSize, 1,
5076 : patScanline, nXSize, 1, eDT,
5077 : 0,0);
5078 3567 : if ( eErr != CE_None )
5079 0 : CPLDebug( "GDAL_netCDF",
5080 : "NCDFCopyBand(), poDstBand->RasterIO() returned error code %d",
5081 : eErr );
5082 : }
5083 :
5084 3567 : if ( ( nYSize>10 ) && ( iLine % (nYSize/10) == 1 ) ) {
5085 494 : if( !pfnProgress( 1.0*iLine/nYSize , NULL, pProgressData ) )
5086 : {
5087 0 : eErr = CE_Failure;
5088 0 : CPLError( CE_Failure, CPLE_UserInterrupt,
5089 : "User terminated CreateCopy()" );
5090 : }
5091 : }
5092 : }
5093 :
5094 85 : CPLFree( patScanline );
5095 :
5096 85 : pfnProgress( 1.0, NULL, pProgressData );
5097 :
5098 85 : return eErr;
5099 : }
5100 :
5101 :
5102 : /************************************************************************/
5103 : /* CreateCopy() */
5104 : /************************************************************************/
5105 :
5106 : GDALDataset*
5107 79 : netCDFDataset::CreateCopy( const char * pszFilename, GDALDataset *poSrcDS,
5108 : int bStrict, char ** papszOptions,
5109 : GDALProgressFunc pfnProgress, void * pProgressData )
5110 : {
5111 : netCDFDataset *poDS;
5112 : void *pScaledProgress;
5113 : GDALDataType eDT;
5114 79 : CPLErr eErr = CE_None;
5115 : int nBands, nXSize, nYSize;
5116 : double adfGeoTransform[6];
5117 : const char *pszWKT;
5118 : int iBand;
5119 79 : int status = NC_NOERR;
5120 :
5121 79 : int nDim = 2;
5122 79 : int *panBandDimPos=NULL; // X, Y, Z postion in array
5123 79 : int *panBandZLev=NULL;
5124 79 : int *panDimIds=NULL;
5125 79 : int *panDimVarIds=NULL;
5126 : nc_type nVarType;
5127 : char szTemp[ NCDF_MAX_STR_LEN ];
5128 : double dfTemp,dfTemp2;
5129 79 : netCDFRasterBand *poBand = NULL;
5130 79 : GDALRasterBand *poSrcBand = NULL;
5131 79 : GDALRasterBand *poDstBand = NULL;
5132 79 : int nBandID = -1;
5133 :
5134 79 : CPLMutexHolderD(&hNCMutex);
5135 :
5136 : CPLDebug( "GDAL_netCDF",
5137 : "\n=====\nnetCDFDataset::CreateCopy( %s, ... )\n",
5138 79 : pszFilename );
5139 :
5140 79 : nBands = poSrcDS->GetRasterCount();
5141 79 : nXSize = poSrcDS->GetRasterXSize();
5142 79 : nYSize = poSrcDS->GetRasterYSize();
5143 79 : pszWKT = poSrcDS->GetProjectionRef();
5144 :
5145 : /* -------------------------------------------------------------------- */
5146 : /* Check input bands for errors */
5147 : /* -------------------------------------------------------------------- */
5148 :
5149 79 : if (nBands == 0)
5150 : {
5151 : CPLError( CE_Failure, CPLE_NotSupported,
5152 1 : "NetCDF driver does not support source dataset with zero band.\n");
5153 1 : return NULL;
5154 : }
5155 :
5156 186 : for( iBand=1; iBand <= nBands; iBand++ )
5157 : {
5158 112 : poSrcBand = poSrcDS->GetRasterBand( iBand );
5159 112 : eDT = poSrcBand->GetRasterDataType();
5160 112 : if (eDT == GDT_Unknown || GDALDataTypeIsComplex(eDT))
5161 : {
5162 : CPLError( CE_Failure, CPLE_NotSupported,
5163 4 : "NetCDF driver does not support source dataset with band of complex type.");
5164 4 : return NULL;
5165 : }
5166 : }
5167 :
5168 74 : if( !pfnProgress( 0.0, NULL, pProgressData ) )
5169 0 : return NULL;
5170 :
5171 : /* same as in Create() */
5172 : poDS = netCDFDataset::CreateLL( pszFilename,
5173 : nXSize, nYSize, nBands,
5174 74 : papszOptions );
5175 74 : if ( ! poDS )
5176 13 : return NULL;
5177 :
5178 : /* -------------------------------------------------------------------- */
5179 : /* Copy global metadata */
5180 : /* Add Conventions, GDAL info and history */
5181 : /* -------------------------------------------------------------------- */
5182 61 : CopyMetadata((void *) poSrcDS, poDS->cdfid, NC_GLOBAL, NULL, FALSE );
5183 : NCDFAddGDALHistory( poDS->cdfid, pszFilename,
5184 61 : poSrcDS->GetMetadataItem("NC_GLOBAL#history",""),
5185 122 : "CreateCopy" );
5186 :
5187 61 : pfnProgress( 0.1, NULL, pProgressData );
5188 :
5189 :
5190 : /* -------------------------------------------------------------------- */
5191 : /* Check for extra dimensions */
5192 : /* -------------------------------------------------------------------- */
5193 : char **papszExtraDimNames =
5194 61 : NCDFTokenizeArray( poSrcDS->GetMetadataItem("NETCDF_DIM_EXTRA","") );
5195 61 : char **papszExtraDimValues = NULL;
5196 61 : size_t nDimSize = -1;
5197 61 : size_t nDimSizeTot = 1;
5198 61 : if ( papszExtraDimNames != NULL && ( CSLCount( papszExtraDimNames )> 0 ) ) {
5199 : // first make sure dimensions lengths compatible with band count
5200 : // for ( int i=0; i<CSLCount( papszExtraDimNames ); i++ ) {
5201 12 : for ( int i=CSLCount( papszExtraDimNames )-1; i>=0; i-- ) {
5202 7 : sprintf( szTemp, "NETCDF_DIM_%s_DEF", papszExtraDimNames[i] );
5203 7 : papszExtraDimValues = NCDFTokenizeArray( poSrcDS->GetMetadataItem(szTemp,"") );
5204 7 : nDimSize = atol( papszExtraDimValues[0] );
5205 7 : CSLDestroy( papszExtraDimValues );
5206 7 : nDimSizeTot *= nDimSize;
5207 : }
5208 5 : if ( nDimSizeTot == (size_t)nBands ) {
5209 5 : nDim = 2 + CSLCount( papszExtraDimNames );
5210 : }
5211 : else {
5212 : // if nBands != #bands computed raise a warning
5213 : // just issue a debug message, because it was probably intentional
5214 : CPLDebug( "GDAL_netCDF",
5215 : "Warning: Number of bands (%d) is not compatible with dimensions "
5216 : "(total=%ld names=%s)", nBands, (long)nDimSizeTot,
5217 0 : poSrcDS->GetMetadataItem("NETCDF_DIM_EXTRA","") );
5218 0 : CSLDestroy( papszExtraDimNames );
5219 0 : papszExtraDimNames = NULL;
5220 : }
5221 : }
5222 :
5223 61 : panDimIds = (int *)CPLCalloc( nDim, sizeof( int ) );
5224 61 : panBandDimPos = (int *) CPLCalloc( nDim, sizeof( int ) );
5225 :
5226 61 : if ( nDim > 2 ) {
5227 5 : panBandZLev = (int *)CPLCalloc( nDim-2, sizeof( int ) );
5228 5 : panDimVarIds = (int *)CPLCalloc( nDim-2, sizeof( int ) );
5229 :
5230 : /* define all dims */
5231 12 : for ( int i=CSLCount( papszExtraDimNames )-1; i>=0; i-- ) {
5232 7 : poDS->papszDimName.AddString( papszExtraDimNames[i] );
5233 7 : sprintf( szTemp, "NETCDF_DIM_%s_DEF", papszExtraDimNames[i] );
5234 7 : papszExtraDimValues = NCDFTokenizeArray( poSrcDS->GetMetadataItem(szTemp,"") );
5235 7 : nDimSize = atol( papszExtraDimValues[0] );
5236 : /* nc_type is an enum in netcdf-3, needs casting */
5237 7 : nVarType = (nc_type) atol( papszExtraDimValues[1] );
5238 7 : CSLDestroy( papszExtraDimValues );
5239 7 : panBandZLev[ i ] = nDimSize;
5240 7 : panBandDimPos[ i+2 ] = i; //Save Position of ZDim
5241 :
5242 : /* define dim */
5243 7 : status = nc_def_dim( poDS->cdfid, papszExtraDimNames[i], nDimSize,
5244 14 : &(panDimIds[i]) );
5245 7 : NCDF_ERR(status);
5246 :
5247 : /* define dim var */
5248 : int anDim[1];
5249 7 : anDim[0] = panDimIds[i];
5250 7 : status = nc_def_var( poDS->cdfid, papszExtraDimNames[i],
5251 : nVarType, 1, anDim,
5252 14 : &(panDimVarIds[i]) );
5253 7 : NCDF_ERR(status);
5254 :
5255 : /* add dim metadata, using global var# items */
5256 7 : sprintf( szTemp, "%s#", papszExtraDimNames[i] );
5257 7 : CopyMetadata((void *) poSrcDS, poDS->cdfid, panDimVarIds[i], szTemp, FALSE );
5258 : }
5259 : }
5260 :
5261 : /* -------------------------------------------------------------------- */
5262 : /* Copy GeoTransform and Projection */
5263 : /* -------------------------------------------------------------------- */
5264 : /* copy geolocation info */
5265 61 : if ( poSrcDS->GetMetadata("GEOLOCATION") != NULL )
5266 0 : poDS->SetMetadata( poSrcDS->GetMetadata("GEOLOCATION"), "GEOLOCATION" );
5267 :
5268 : /* copy geotransform */
5269 61 : int bGotGeoTransform = FALSE;
5270 61 : eErr = poSrcDS->GetGeoTransform( adfGeoTransform );
5271 61 : if ( eErr == CE_None ) {
5272 59 : poDS->SetGeoTransform( adfGeoTransform );
5273 : /* disable AddProjectionVars() from being called */
5274 59 : bGotGeoTransform = TRUE;
5275 59 : poDS->bSetGeoTransform = FALSE;
5276 : }
5277 :
5278 : /* copy projection */
5279 61 : if ( pszWKT ) {
5280 61 : poDS->SetProjection( pszWKT );
5281 : /* now we can call AddProjectionVars() directly */
5282 61 : poDS->bSetGeoTransform = bGotGeoTransform;
5283 : pScaledProgress = GDALCreateScaledProgress( 0.20, 0.50, pfnProgress,
5284 61 : pProgressData );
5285 61 : poDS->AddProjectionVars( GDALScaledProgress, pScaledProgress );
5286 : /* save X,Y dim positions */
5287 61 : panDimIds[nDim-1] = poDS->nXDimID;
5288 61 : panBandDimPos[0] = nDim-1;
5289 61 : panDimIds[nDim-2] = poDS->nYDimID;
5290 61 : panBandDimPos[1] = nDim-2;
5291 61 : GDALDestroyScaledProgress( pScaledProgress );
5292 :
5293 : }
5294 :
5295 : /* write extra dim values - after projection for optimization */
5296 61 : if ( nDim > 2 ) {
5297 : /* make sure we are in data mode */
5298 5 : ( ( netCDFDataset * ) poDS )->SetDefineMode( FALSE );
5299 12 : for ( int i=CSLCount( papszExtraDimNames )-1; i>=0; i-- ) {
5300 7 : sprintf( szTemp, "NETCDF_DIM_%s_VALUES", papszExtraDimNames[i] );
5301 7 : if ( poSrcDS->GetMetadataItem( szTemp ) != NULL ) {
5302 : NCDFPut1DVar( poDS->cdfid, panDimVarIds[i],
5303 7 : poSrcDS->GetMetadataItem( szTemp ) );
5304 : }
5305 : }
5306 : }
5307 :
5308 61 : pfnProgress( 0.25, NULL, pProgressData );
5309 :
5310 : /* -------------------------------------------------------------------- */
5311 : /* Define Bands */
5312 : /* -------------------------------------------------------------------- */
5313 :
5314 146 : for( iBand=1; iBand <= nBands; iBand++ ) {
5315 : CPLDebug( "GDAL_netCDF", "creating band # %d/%d nDim = %d",
5316 85 : iBand, nBands, nDim );
5317 :
5318 : char szBandName[ NC_MAX_NAME ];
5319 : char szLongName[ NC_MAX_NAME ];
5320 : const char *tmpMetadata;
5321 85 : poSrcBand = poSrcDS->GetRasterBand( iBand );
5322 85 : eDT = poSrcBand->GetRasterDataType();
5323 :
5324 : /* Get var name from NETCDF_VARNAME */
5325 85 : tmpMetadata = poSrcBand->GetMetadataItem("NETCDF_VARNAME");
5326 85 : if( tmpMetadata != NULL) {
5327 35 : if( nBands > 1 && papszExtraDimNames == NULL )
5328 0 : sprintf(szBandName,"%s%d",tmpMetadata,iBand);
5329 35 : else strcpy( szBandName, tmpMetadata );
5330 : }
5331 : else
5332 50 : szBandName[0]='\0';
5333 :
5334 : /* Get long_name from <var>#long_name */
5335 : sprintf(szLongName,"%s#%s",
5336 85 : poSrcBand->GetMetadataItem("NETCDF_VARNAME"),
5337 170 : CF_LNG_NAME);
5338 85 : tmpMetadata = poSrcDS->GetMetadataItem(szLongName);
5339 85 : if( tmpMetadata != NULL)
5340 18 : strcpy( szLongName, tmpMetadata);
5341 : else
5342 67 : szLongName[0]='\0';
5343 :
5344 85 : int bSignedData = TRUE;
5345 85 : if ( eDT == GDT_Byte ) {
5346 : /* GDAL defaults to unsigned bytes, but check if metadata says its
5347 : signed, as NetCDF can support this for certain formats. */
5348 52 : bSignedData = FALSE;
5349 : tmpMetadata = poSrcBand->GetMetadataItem("PIXELTYPE",
5350 52 : "IMAGE_STRUCTURE");
5351 52 : if ( tmpMetadata && EQUAL(tmpMetadata,"SIGNEDBYTE") )
5352 1 : bSignedData = TRUE;
5353 : }
5354 :
5355 85 : if ( nDim > 2 )
5356 : poBand = new netCDFRasterBand( poDS, eDT, iBand,
5357 : bSignedData,
5358 : szBandName, szLongName,
5359 : nBandID, nDim, iBand-1,
5360 : panBandZLev, panBandDimPos,
5361 19 : panDimIds );
5362 : else
5363 : poBand = new netCDFRasterBand( poDS, eDT, iBand,
5364 : bSignedData,
5365 66 : szBandName, szLongName );
5366 :
5367 85 : poDS->SetBand( iBand, poBand );
5368 :
5369 : /* Copy Metadata for band */
5370 85 : poBand->SetNoDataValue( poSrcBand->GetNoDataValue(0) );
5371 : CopyMetadata( (void *) GDALGetRasterBand( poSrcDS, iBand ),
5372 85 : poDS->cdfid, poBand->nZId );
5373 :
5374 : /* if more than 2D pass the first band's netcdf var ID to subsequent bands */
5375 85 : if ( nDim > 2 )
5376 19 : nBandID = poBand->nZId;
5377 : }
5378 :
5379 : /* write projection variable to band variable */
5380 61 : poDS->AddGridMappingRef();
5381 :
5382 61 : pfnProgress( 0.5, NULL, pProgressData );
5383 :
5384 :
5385 : /* -------------------------------------------------------------------- */
5386 : /* Write Bands */
5387 : /* -------------------------------------------------------------------- */
5388 : /* make sure we are in data mode */
5389 61 : poDS->SetDefineMode( FALSE );
5390 :
5391 61 : dfTemp = dfTemp2 = 0.5;
5392 :
5393 61 : eErr = CE_None;
5394 :
5395 146 : for( iBand=1; iBand <= nBands && eErr == CE_None; iBand++ ) {
5396 :
5397 85 : dfTemp2 = dfTemp + 0.4/nBands;
5398 : pScaledProgress =
5399 : GDALCreateScaledProgress( dfTemp, dfTemp2,
5400 85 : pfnProgress, pProgressData );
5401 85 : dfTemp = dfTemp2;
5402 :
5403 : CPLDebug( "GDAL_netCDF", "copying band data # %d/%d ",
5404 85 : iBand,nBands );
5405 :
5406 85 : poSrcBand = poSrcDS->GetRasterBand( iBand );
5407 85 : eDT = poSrcBand->GetRasterDataType();
5408 :
5409 85 : poDstBand = poDS->GetRasterBand( iBand );
5410 :
5411 :
5412 : /* -------------------------------------------------------------------- */
5413 : /* Copy Band data */
5414 : /* -------------------------------------------------------------------- */
5415 85 : if( eDT == GDT_Byte ) {
5416 52 : CPLDebug( "GDAL_netCDF", "GByte Band#%d", iBand );
5417 : eErr = NCDFCopyBand<GByte>( poSrcBand, poDstBand, nXSize, nYSize,
5418 52 : GDALScaledProgress, pScaledProgress );
5419 : }
5420 38 : else if( ( eDT == GDT_UInt16 ) || ( eDT == GDT_Int16 ) ) {
5421 5 : CPLDebug( "GDAL_netCDF", "GInt16 Band#%d", iBand );
5422 : eErr = NCDFCopyBand<GInt16>( poSrcBand, poDstBand, nXSize, nYSize,
5423 5 : GDALScaledProgress, pScaledProgress );
5424 : }
5425 47 : else if( (eDT == GDT_UInt32) || (eDT == GDT_Int32) ) {
5426 19 : CPLDebug( "GDAL_netCDF", "GInt16 Band#%d", iBand );
5427 : eErr = NCDFCopyBand<GInt32>( poSrcBand, poDstBand, nXSize, nYSize,
5428 19 : GDALScaledProgress, pScaledProgress );
5429 : }
5430 9 : else if( eDT == GDT_Float32 ) {
5431 7 : CPLDebug( "GDAL_netCDF", "float Band#%d", iBand);
5432 : eErr = NCDFCopyBand<float>( poSrcBand, poDstBand, nXSize, nYSize,
5433 7 : GDALScaledProgress, pScaledProgress );
5434 : }
5435 2 : else if( eDT == GDT_Float64 ) {
5436 2 : CPLDebug( "GDAL_netCDF", "double Band#%d", iBand);
5437 : eErr = NCDFCopyBand<double>( poSrcBand, poDstBand, nXSize, nYSize,
5438 2 : GDALScaledProgress, pScaledProgress );
5439 : }
5440 : else {
5441 : CPLError( CE_Failure, CPLE_NotSupported,
5442 : "The NetCDF driver does not support GDAL data type %d",
5443 0 : eDT );
5444 : }
5445 :
5446 85 : GDALDestroyScaledProgress( pScaledProgress );
5447 : }
5448 :
5449 : /* -------------------------------------------------------------------- */
5450 : /* Cleanup and close. */
5451 : /* -------------------------------------------------------------------- */
5452 61 : delete( poDS );
5453 : // CPLFree(pszProj4Defn );
5454 :
5455 61 : if ( panDimIds )
5456 61 : CPLFree( panDimIds );
5457 61 : if( panBandDimPos )
5458 61 : CPLFree( panBandDimPos );
5459 61 : if ( panBandZLev )
5460 5 : CPLFree( panBandZLev );
5461 61 : if( panDimVarIds )
5462 5 : CPLFree( panDimVarIds );
5463 61 : if ( papszExtraDimNames )
5464 5 : CSLDestroy( papszExtraDimNames );
5465 :
5466 61 : if (eErr != CE_None)
5467 0 : return NULL;
5468 :
5469 61 : pfnProgress( 0.95, NULL, pProgressData );
5470 :
5471 : /* -------------------------------------------------------------------- */
5472 : /* Re-open dataset so we can return it. */
5473 : /* -------------------------------------------------------------------- */
5474 61 : poDS = (netCDFDataset *) GDALOpen( pszFilename, GA_ReadOnly );
5475 :
5476 : /* -------------------------------------------------------------------- */
5477 : /* PAM cloning is disabled. See bug #4244. */
5478 : /* -------------------------------------------------------------------- */
5479 : // if( poDS )
5480 : // poDS->CloneInfo( poSrcDS, GCIF_PAM_DEFAULT );
5481 :
5482 61 : pfnProgress( 1.0, NULL, pProgressData );
5483 :
5484 61 : return poDS;
5485 : }
5486 :
5487 : /* note: some logic depends on bIsProjected and bIsGeoGraphic */
5488 : /* which may not be known when Create() is called, see AddProjectionVars() */
5489 : void
5490 114 : netCDFDataset::ProcessCreationOptions( )
5491 : {
5492 : const char *pszValue;
5493 :
5494 : /* File format */
5495 114 : nFormat = NCDF_FORMAT_NC;
5496 114 : pszValue = CSLFetchNameValue( papszCreationOptions, "FORMAT" );
5497 114 : if ( pszValue != NULL ) {
5498 0 : if ( EQUAL( pszValue, "NC" ) ) {
5499 0 : nFormat = NCDF_FORMAT_NC;
5500 : }
5501 : #ifdef NETCDF_HAS_NC2
5502 0 : else if ( EQUAL( pszValue, "NC2" ) ) {
5503 0 : nFormat = NCDF_FORMAT_NC2;
5504 : }
5505 : #endif
5506 : #ifdef NETCDF_HAS_NC4
5507 : else if ( EQUAL( pszValue, "NC4" ) ) {
5508 : nFormat = NCDF_FORMAT_NC4;
5509 : }
5510 : else if ( EQUAL( pszValue, "NC4C" ) ) {
5511 : nFormat = NCDF_FORMAT_NC4C;
5512 : }
5513 : #endif
5514 : else {
5515 : CPLError( CE_Failure, CPLE_NotSupported,
5516 0 : "FORMAT=%s in not supported, using the default NC format.", pszValue );
5517 : }
5518 : }
5519 :
5520 : /* compression only available for NC4 */
5521 : #ifdef NETCDF_HAS_NC4
5522 :
5523 : /* COMPRESS option */
5524 : pszValue = CSLFetchNameValue( papszCreationOptions, "COMPRESS" );
5525 : if ( pszValue != NULL ) {
5526 : if ( EQUAL( pszValue, "NONE" ) ) {
5527 : nCompress = NCDF_COMPRESS_NONE;
5528 : }
5529 : else if ( EQUAL( pszValue, "DEFLATE" ) ) {
5530 : nCompress = NCDF_COMPRESS_DEFLATE;
5531 : if ( !((nFormat == NCDF_FORMAT_NC4) || (nFormat == NCDF_FORMAT_NC4C)) ) {
5532 : CPLError( CE_Warning, CPLE_IllegalArg,
5533 : "NOTICE: Format set to NC4C because compression is set to DEFLATE." );
5534 : nFormat = NCDF_FORMAT_NC4C;
5535 : }
5536 : }
5537 : else {
5538 : CPLError( CE_Failure, CPLE_NotSupported,
5539 : "COMPRESS=%s is not supported.", pszValue );
5540 : }
5541 : }
5542 :
5543 : /* ZLEVEL option */
5544 : pszValue = CSLFetchNameValue( papszCreationOptions, "ZLEVEL" );
5545 : if( pszValue != NULL )
5546 : {
5547 : nZLevel = atoi( pszValue );
5548 : if (!(nZLevel >= 1 && nZLevel <= 9))
5549 : {
5550 : CPLError( CE_Warning, CPLE_IllegalArg,
5551 : "ZLEVEL=%s value not recognised, ignoring.",
5552 : pszValue );
5553 : nZLevel = NCDF_DEFLATE_LEVEL;
5554 : }
5555 : }
5556 :
5557 : #endif
5558 :
5559 : /* set nCreateMode based on nFormat */
5560 114 : switch ( nFormat ) {
5561 : #ifdef NETCDF_HAS_NC2
5562 : case NCDF_FORMAT_NC2:
5563 0 : nCreateMode = NC_CLOBBER|NC_64BIT_OFFSET;
5564 0 : break;
5565 : #endif
5566 : #ifdef NETCDF_HAS_NC4
5567 : case NCDF_FORMAT_NC4:
5568 : nCreateMode = NC_CLOBBER|NC_NETCDF4;
5569 : break;
5570 : case NCDF_FORMAT_NC4C:
5571 : nCreateMode = NC_CLOBBER|NC_NETCDF4|NC_CLASSIC_MODEL;
5572 : break;
5573 : #endif
5574 : case NCDF_FORMAT_NC:
5575 : default:
5576 114 : nCreateMode = NC_CLOBBER;
5577 : break;
5578 : }
5579 :
5580 : CPLDebug( "GDAL_netCDF",
5581 : "file options: format=%d compress=%d zlevel=%d",
5582 114 : nFormat, nCompress, nZLevel );
5583 :
5584 114 : }
5585 :
5586 232 : int netCDFDataset::DefVarDeflate( int nVarId, int bChunking )
5587 : {
5588 : #ifdef NETCDF_HAS_NC4
5589 : if ( nCompress == NCDF_COMPRESS_DEFLATE ) {
5590 : // must set chunk size to avoid huge performace hit (set bChunking=TRUE)
5591 : // perhaps another solution it to change the chunk cache?
5592 : // http://www.unidata.ucar.edu/software/netcdf/docs/netcdf.html#Chunk-Cache
5593 : // TODO make sure this is ok
5594 : CPLDebug( "GDAL_netCDF",
5595 : "DefVarDeflate( %d, %d ) nZlevel=%d",
5596 : nVarId, bChunking, nZLevel );
5597 : status = nc_def_var_deflate(cdfid,nVarId,1,1,nZLevel);
5598 : NCDF_ERR(status);
5599 : if ( (status == NC_NOERR) && bChunking ) {
5600 : size_t chunksize[] = { 1, nRasterXSize };
5601 : CPLDebug( "GDAL_netCDF",
5602 : "DefVarDeflate() chunksize={%ld, %ld}",
5603 : (long)chunksize[0], (long)chunksize[1] );
5604 : status = nc_def_var_chunking( cdfid, nVarId,
5605 : NC_CHUNKED, chunksize );
5606 : NCDF_ERR(status);
5607 : }
5608 : return status;
5609 : }
5610 : #endif
5611 232 : return NC_NOERR;
5612 : }
5613 :
5614 : /************************************************************************/
5615 : /* NCDFUnloadDriver() */
5616 : /************************************************************************/
5617 :
5618 550 : static void NCDFUnloadDriver(GDALDriver* poDriver)
5619 : {
5620 550 : if( hNCMutex != NULL )
5621 4 : CPLDestroyMutex(hNCMutex);
5622 550 : hNCMutex = NULL;
5623 550 : }
5624 :
5625 : /************************************************************************/
5626 : /* GDALRegister_netCDF() */
5627 : /************************************************************************/
5628 :
5629 610 : void GDALRegister_netCDF()
5630 :
5631 : {
5632 610 : if (! GDAL_CHECK_VERSION("netCDF driver"))
5633 0 : return;
5634 :
5635 610 : if( GDALGetDriverByName( "netCDF" ) == NULL )
5636 : {
5637 : GDALDriver *poDriver;
5638 : char szCreateOptions[3072];
5639 :
5640 588 : poDriver = new GDALDriver( );
5641 :
5642 : /* -------------------------------------------------------------------- */
5643 : /* Build full creation option list. */
5644 : /* -------------------------------------------------------------------- */
5645 : sprintf( szCreateOptions, "%s",
5646 : "<CreationOptionList>"
5647 : " <Option name='FORMAT' type='string-select' default='NC'>"
5648 : " <Value>NC</Value>"
5649 : #ifdef NETCDF_HAS_NC2
5650 : " <Value>NC2</Value>"
5651 : #endif
5652 : #ifdef NETCDF_HAS_NC4
5653 : " <Value>NC4</Value>"
5654 : " <Value>NC4C</Value>"
5655 : #endif
5656 : " </Option>"
5657 : #ifdef NETCDF_HAS_NC4
5658 : " <Option name='COMPRESS' type='string-select' default='NONE'>"
5659 : " <Value>NONE</Value>"
5660 : " <Value>DEFLATE</Value>"
5661 : " </Option>"
5662 : " <Option name='ZLEVEL' type='int' description='DEFLATE compression level 1-9' default='1'/>"
5663 : #endif
5664 : " <Option name='WRITE_BOTTOMUP' type='boolean' default='YES'>"
5665 : " </Option>"
5666 : " <Option name='WRITE_GDAL_TAGS' type='boolean' default='YES'>"
5667 : " </Option>"
5668 : " <Option name='WRITE_LONLAT' type='string-select'>"
5669 : " <Value>YES</Value>"
5670 : " <Value>NO</Value>"
5671 : " <Value>IF_NEEDED</Value>"
5672 : " </Option>"
5673 : " <Option name='TYPE_LONLAT' type='string-select'>"
5674 : " <Value>float</Value>"
5675 : " <Value>double</Value>"
5676 : " </Option>"
5677 : " <Option name='PIXELTYPE' type='string-select' description='only used in Create()'>"
5678 : " <Value>DEFAULT</Value>"
5679 : " <Value>SIGNEDBYTE</Value>"
5680 : " </Option>"
5681 588 : "</CreationOptionList>" );
5682 :
5683 :
5684 : /* -------------------------------------------------------------------- */
5685 : /* Set the driver details. */
5686 : /* -------------------------------------------------------------------- */
5687 588 : poDriver->SetDescription( "netCDF" );
5688 : poDriver->SetMetadataItem( GDAL_DMD_LONGNAME,
5689 588 : "Network Common Data Format" );
5690 : poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC,
5691 588 : "frmt_netcdf.html" );
5692 588 : poDriver->SetMetadataItem( GDAL_DMD_EXTENSION, "nc" );
5693 : poDriver->SetMetadataItem( GDAL_DMD_CREATIONOPTIONLIST,
5694 588 : szCreateOptions );
5695 588 : poDriver->SetMetadataItem( GDAL_DMD_SUBDATASETS, "YES" );
5696 :
5697 : /* make driver config and capabilities available */
5698 588 : poDriver->SetMetadataItem( "NETCDF_VERSION", nc_inq_libvers() );
5699 588 : poDriver->SetMetadataItem( "NETCDF_CONVENTIONS", NCDF_CONVENTIONS_CF );
5700 : #ifdef NETCDF_HAS_NC2
5701 588 : poDriver->SetMetadataItem( "NETCDF_HAS_NC2", "YES" );
5702 : #endif
5703 : #ifdef NETCDF_HAS_NC4
5704 : poDriver->SetMetadataItem( "NETCDF_HAS_NC4", "YES" );
5705 : #endif
5706 : #ifdef NETCDF_HAS_HDF4
5707 : poDriver->SetMetadataItem( "NETCDF_HAS_HDF4", "YES" );
5708 : #endif
5709 : #ifdef HAVE_HDF4
5710 588 : poDriver->SetMetadataItem( "GDAL_HAS_HDF4", "YES" );
5711 : #endif
5712 : #ifdef HAVE_HDF5
5713 588 : poDriver->SetMetadataItem( "GDAL_HAS_HDF5", "YES" );
5714 : #endif
5715 :
5716 : /* set pfns and register driver */
5717 588 : poDriver->pfnOpen = netCDFDataset::Open;
5718 588 : poDriver->pfnCreateCopy = netCDFDataset::CreateCopy;
5719 588 : poDriver->pfnCreate = netCDFDataset::Create;
5720 588 : poDriver->pfnIdentify = netCDFDataset::Identify;
5721 588 : poDriver->pfnUnloadDriver = NCDFUnloadDriver;
5722 :
5723 588 : GetGDALDriverManager( )->RegisterDriver( poDriver );
5724 : }
5725 : }
5726 :
5727 : /************************************************************************/
5728 : /* New functions */
5729 : /************************************************************************/
5730 :
5731 : /* Test for GDAL version string >= target */
5732 159 : int NCDFIsGDALVersionGTE(const char* pszVersion, int nTarget)
5733 : {
5734 159 : int nVersion = 0;
5735 159 : int nVersions [] = {0,0,0,0};
5736 : char **papszTokens;
5737 :
5738 : /* Valid strings are "GDAL 1.9dev, released 2011/01/18" and "GDAL 1.8.1 " */
5739 159 : if ( pszVersion == NULL || EQUAL( pszVersion, "" ) )
5740 0 : return FALSE;
5741 159 : else if ( ! EQUALN("GDAL ", pszVersion, 5) )
5742 0 : return FALSE;
5743 : /* 2.0dev of 2011/12/29 has been later renamed as 1.10dev */
5744 159 : else if ( EQUAL("GDAL 2.0dev, released 2011/12/29", pszVersion) )
5745 0 : return nTarget <= GDAL_COMPUTE_VERSION(1,10,0);
5746 159 : else if ( EQUALN("GDAL 1.9dev", pszVersion,11 ) )
5747 0 : return nTarget <= 1900;
5748 159 : else if ( EQUALN("GDAL 1.8dev", pszVersion,11 ) )
5749 0 : return nTarget <= 1800;
5750 :
5751 159 : papszTokens = CSLTokenizeString2( pszVersion+5, ".", 0 );
5752 :
5753 477 : for ( int iToken = 0; papszTokens && papszTokens[iToken]; iToken++ ) {
5754 318 : nVersions[iToken] = atoi( papszTokens[iToken] );
5755 : }
5756 318 : if( nVersions[0] > 1 || nVersions[1] >= 10 )
5757 159 : nVersion = GDAL_COMPUTE_VERSION( nVersions[0], nVersions[1], nVersions[2] );
5758 : else
5759 0 : nVersion = nVersions[0]*1000 + nVersions[1]*100 +
5760 0 : nVersions[2]*10 + nVersions[3];
5761 :
5762 159 : CSLDestroy( papszTokens );
5763 159 : return nTarget <= nVersion;
5764 : }
5765 :
5766 : /* Add Conventions, GDAL version and history */
5767 101 : void NCDFAddGDALHistory( int fpImage,
5768 : const char * pszFilename, const char *pszOldHist,
5769 : const char * pszFunctionName)
5770 : {
5771 : char szTemp[NC_MAX_NAME];
5772 :
5773 : nc_put_att_text( fpImage, NC_GLOBAL, "Conventions",
5774 : strlen(NCDF_CONVENTIONS_CF),
5775 101 : NCDF_CONVENTIONS_CF );
5776 :
5777 101 : const char* pszNCDF_GDAL = GDALVersionInfo("--version");
5778 : nc_put_att_text( fpImage, NC_GLOBAL, "GDAL",
5779 101 : strlen(pszNCDF_GDAL), pszNCDF_GDAL );
5780 :
5781 : /* Add history */
5782 : #ifdef GDAL_SET_CMD_LINE_DEFINED_TMP
5783 : if ( ! EQUAL(GDALGetCmdLine(), "" ) )
5784 : strcpy( szTemp, GDALGetCmdLine() );
5785 : else
5786 : sprintf( szTemp, "GDAL %s( %s, ... )",pszFunctionName,pszFilename );
5787 : #else
5788 101 : sprintf( szTemp, "GDAL %s( %s, ... )",pszFunctionName,pszFilename );
5789 : #endif
5790 :
5791 101 : NCDFAddHistory( fpImage, szTemp, pszOldHist );
5792 :
5793 101 : }
5794 :
5795 : /* code taken from cdo and libcdi, used for writing the history attribute */
5796 : //void cdoDefHistory(int fileID, char *histstring)
5797 101 : void NCDFAddHistory(int fpImage, const char *pszAddHist, const char *pszOldHist)
5798 : {
5799 : char strtime[32];
5800 : time_t tp;
5801 : struct tm *ltime;
5802 :
5803 101 : char *pszNewHist = NULL;
5804 101 : size_t nNewHistSize = 0;
5805 101 : int disableHistory = FALSE;
5806 : int status;
5807 :
5808 : /* Check pszOldHist - as if there was no previous history, it will be
5809 : a null pointer - if so set as empty. */
5810 101 : if (NULL == pszOldHist) {
5811 45 : pszOldHist = "";
5812 : }
5813 :
5814 101 : tp = time(NULL);
5815 101 : if ( tp != -1 )
5816 : {
5817 101 : ltime = localtime(&tp);
5818 101 : (void) strftime(strtime, sizeof(strtime), "%a %b %d %H:%M:%S %Y: ", ltime);
5819 : }
5820 :
5821 : // status = nc_get_att_text( fpImage, NC_GLOBAL,
5822 : // "history", pszOldHist );
5823 : // printf("status: %d pszOldHist: [%s]\n",status,pszOldHist);
5824 :
5825 101 : nNewHistSize = strlen(pszOldHist)+strlen(strtime)+strlen(pszAddHist)+1+1;
5826 101 : pszNewHist = (char *) CPLMalloc(nNewHistSize * sizeof(char));
5827 :
5828 101 : strcpy(pszNewHist, strtime);
5829 101 : strcat(pszNewHist, pszAddHist);
5830 :
5831 101 : if ( disableHistory == FALSE && pszNewHist )
5832 : {
5833 101 : if ( ! EQUAL(pszOldHist,"") )
5834 16 : strcat(pszNewHist, "\n");
5835 101 : strcat(pszNewHist, pszOldHist);
5836 : }
5837 :
5838 : status = nc_put_att_text( fpImage, NC_GLOBAL,
5839 : "history", strlen(pszNewHist),
5840 101 : pszNewHist );
5841 101 : NCDF_ERR(status);
5842 :
5843 101 : CPLFree(pszNewHist);
5844 101 : }
5845 :
5846 :
5847 41 : int NCDFIsCfProjection( const char* pszProjection )
5848 : {
5849 : /* Find the appropriate mapping */
5850 823 : for (int iMap = 0; poNetcdfSRS_PT[iMap].WKT_SRS != NULL; iMap++ ) {
5851 : // printf("now at %d, proj=%s\n",i, poNetcdfSRS_PT[i].GDAL_SRS);
5852 823 : if ( EQUAL( pszProjection, poNetcdfSRS_PT[iMap].WKT_SRS ) ) {
5853 41 : if ( poNetcdfSRS_PT[iMap].mappings != NULL )
5854 41 : return TRUE;
5855 : else
5856 0 : return FALSE;
5857 : }
5858 : }
5859 0 : return FALSE;
5860 : }
5861 :
5862 :
5863 : /* Write any needed projection attributes *
5864 : * poPROJCS: ptr to proj crd system
5865 : * pszProjection: name of projection system in GDAL WKT
5866 : * fpImage: open NetCDF file in writing mode
5867 : * NCDFVarID: NetCDF Var Id of proj system we're writing in to
5868 : *
5869 : * The function first looks for the oNetcdfSRS_PP mapping object
5870 : * that corresponds to the input projection name. If none is found
5871 : * the generic mapping is used. In the case of specific mappings,
5872 : * the driver looks for each attribute listed in the mapping object
5873 : * and then looks up the value within the OGR_SRSNode. In the case
5874 : * of the generic mapping, the lookup is reversed (projection params,
5875 : * then mapping). For more generic code, GDAL->NETCDF
5876 : * mappings and the associated value are saved in std::map objects.
5877 : */
5878 :
5879 : /* NOTE modifications by ET to combine the specific and generic mappings */
5880 :
5881 41 : void NCDFWriteProjAttribs( const OGR_SRSNode *poPROJCS,
5882 : const char* pszProjection,
5883 : const int fpImage, const int NCDFVarID )
5884 : {
5885 : double dfStdP[2];
5886 41 : int bFoundStdP1=FALSE,bFoundStdP2=FALSE;
5887 41 : double dfValue=0.0;
5888 : const char *pszParamStr, *pszParamVal;
5889 : const std::string *pszNCDFAtt, *pszGDALAtt;
5890 : static const oNetcdfSRS_PP *poMap = NULL;
5891 41 : int nMapIndex = -1;
5892 41 : int bWriteVal = FALSE;
5893 :
5894 : //Attribute <GDAL,NCDF> and Value <NCDF,value> mappings
5895 41 : std::map< std::string, std::string > oAttMap;
5896 41 : std::map< std::string, std::string >::iterator oAttIter;
5897 41 : std::map< std::string, double > oValMap;
5898 41 : std::map< std::string, double >::iterator oValIter, oValIter2;
5899 : //results to write
5900 41 : std::vector< std::pair<std::string,double> > oOutList;
5901 :
5902 : /* Find the appropriate mapping */
5903 823 : for (int iMap = 0; poNetcdfSRS_PT[iMap].WKT_SRS != NULL; iMap++ ) {
5904 823 : if ( EQUAL( pszProjection, poNetcdfSRS_PT[iMap].WKT_SRS ) ) {
5905 41 : nMapIndex = iMap;
5906 41 : poMap = poNetcdfSRS_PT[iMap].mappings;
5907 41 : break;
5908 : }
5909 : }
5910 :
5911 : //ET TODO if projection name is not found, should we do something special?
5912 41 : if ( nMapIndex == -1 ) {
5913 : CPLError( CE_Warning, CPLE_AppDefined,
5914 : "projection name %s not found in the lookup tables!!!",
5915 0 : pszProjection);
5916 : }
5917 : /* if no mapping was found or assigned, set the generic one */
5918 41 : if ( !poMap ) {
5919 : CPLError( CE_Warning, CPLE_AppDefined,
5920 : "projection name %s in not part of the CF standard, will not be supported by CF!",
5921 0 : pszProjection);
5922 0 : poMap = poGenericMappings;
5923 : }
5924 :
5925 : /* initialize local map objects */
5926 464 : for ( int iMap = 0; poMap[iMap].WKT_ATT != NULL; iMap++ ) {
5927 191 : oAttMap[poMap[iMap].WKT_ATT] = poMap[iMap].CF_ATT;
5928 : }
5929 :
5930 415 : for( int iChild = 0; iChild < poPROJCS->GetChildCount(); iChild++ ) {
5931 :
5932 : const OGR_SRSNode *poNode;
5933 :
5934 374 : poNode = poPROJCS->GetChild( iChild );
5935 374 : if( !EQUAL(poNode->GetValue(),"PARAMETER")
5936 : || poNode->GetChildCount() != 2 )
5937 180 : continue;
5938 194 : pszParamStr = poNode->GetChild(0)->GetValue();
5939 194 : pszParamVal = poNode->GetChild(1)->GetValue();
5940 :
5941 194 : oValMap[pszParamStr] = atof(pszParamVal);
5942 : }
5943 :
5944 : /* Lookup mappings and fill output vector */
5945 41 : if ( poMap != poGenericMappings ) { /* specific mapping, loop over mapping values */
5946 :
5947 232 : for ( oAttIter = oAttMap.begin(); oAttIter != oAttMap.end(); oAttIter++ ) {
5948 :
5949 191 : pszGDALAtt = &(oAttIter->first);
5950 191 : pszNCDFAtt = &(oAttIter->second);
5951 191 : oValIter = oValMap.find( *pszGDALAtt );
5952 :
5953 191 : if ( oValIter != oValMap.end() ) {
5954 :
5955 191 : dfValue = oValIter->second;
5956 191 : bWriteVal = TRUE;
5957 :
5958 : /* special case for PS (Polar Stereographic) grid
5959 : See comments in netcdfdataset.h for this projection. */
5960 191 : if ( EQUAL( SRS_PP_LATITUDE_OF_ORIGIN, pszGDALAtt->c_str() ) &&
5961 : EQUAL(pszProjection, SRS_PT_POLAR_STEREOGRAPHIC) ) {
5962 3 : double dfLatPole = 0.0;
5963 3 : if ( dfValue > 0.0) dfLatPole = 90.0;
5964 3 : else dfLatPole = -90.0;
5965 : oOutList.push_back( std::make_pair( std::string(CF_PP_LAT_PROJ_ORIGIN),
5966 3 : dfLatPole ) );
5967 : }
5968 :
5969 : /* special case for LCC-1SP
5970 : See comments in netcdfdataset.h for this projection. */
5971 188 : else if ( EQUAL( SRS_PP_SCALE_FACTOR, pszGDALAtt->c_str() ) &&
5972 : EQUAL(pszProjection, SRS_PT_LAMBERT_CONFORMAL_CONIC_1SP) ) {
5973 : /* default is to not write as it is not CF-1 */
5974 0 : bWriteVal = FALSE;
5975 : /* test if there is no standard_parallel1 */
5976 0 : if ( oValMap.find( std::string(CF_PP_STD_PARALLEL_1) ) == oValMap.end() ) {
5977 : /* if scale factor != 1.0 write value for GDAL, but this is not supported by CF-1 */
5978 0 : if ( !CPLIsEqual(dfValue,1.0) ) {
5979 : CPLError( CE_Failure, CPLE_NotSupported,
5980 : "NetCDF driver export of LCC-1SP with scale factor != 1.0 "
5981 : "and no standard_parallel1 is not CF-1 (bug #3324).\n"
5982 0 : "Use the 2SP variant which is supported by CF." );
5983 0 : bWriteVal = TRUE;
5984 : }
5985 : /* else copy standard_parallel1 from latitude_of_origin, because scale_factor=1.0 */
5986 : else {
5987 0 : oValIter2 = oValMap.find( std::string(SRS_PP_LATITUDE_OF_ORIGIN) );
5988 0 : if (oValIter2 != oValMap.end() ) {
5989 : oOutList.push_back( std::make_pair( std::string(CF_PP_STD_PARALLEL_1),
5990 0 : oValIter2->second) );
5991 : }
5992 : else {
5993 : CPLError( CE_Failure, CPLE_NotSupported,
5994 : "NetCDF driver export of LCC-1SP with no standard_parallel1 "
5995 0 : "and no latitude_of_origin is not suported (bug #3324).");
5996 : }
5997 : }
5998 : }
5999 : }
6000 191 : if ( bWriteVal )
6001 191 : oOutList.push_back( std::make_pair( *pszNCDFAtt, dfValue ) );
6002 :
6003 : }
6004 : // else printf("NOT FOUND!!!\n");
6005 : }
6006 :
6007 : }
6008 : else { /* generic mapping, loop over projected values */
6009 :
6010 0 : for ( oValIter = oValMap.begin(); oValIter != oValMap.end(); oValIter++ ) {
6011 :
6012 0 : pszGDALAtt = &(oValIter->first);
6013 0 : dfValue = oValIter->second;
6014 :
6015 0 : oAttIter = oAttMap.find( *pszGDALAtt );
6016 :
6017 0 : if ( oAttIter != oAttMap.end() ) {
6018 0 : oOutList.push_back( std::make_pair( oAttIter->second, dfValue ) );
6019 : }
6020 : /* for SRS_PP_SCALE_FACTOR write 2 mappings */
6021 0 : else if ( EQUAL(pszGDALAtt->c_str(), SRS_PP_SCALE_FACTOR) ) {
6022 : oOutList.push_back( std::make_pair( std::string(CF_PP_SCALE_FACTOR_MERIDIAN),
6023 0 : dfValue ) );
6024 : oOutList.push_back( std::make_pair( std::string(CF_PP_SCALE_FACTOR_ORIGIN),
6025 0 : dfValue ) );
6026 : }
6027 : /* if not found insert the GDAL name */
6028 : else {
6029 0 : oOutList.push_back( std::make_pair( *pszGDALAtt, dfValue ) );
6030 : }
6031 : }
6032 : }
6033 :
6034 : /* Write all the values that were found */
6035 : // std::vector< std::pair<std::string,double> >::reverse_iterator it;
6036 : // for (it = oOutList.rbegin(); it != oOutList.rend(); it++ ) {
6037 41 : std::vector< std::pair<std::string,double> >::iterator it;
6038 235 : for (it = oOutList.begin(); it != oOutList.end(); it++ ) {
6039 194 : pszParamVal = (it->first).c_str();
6040 194 : dfValue = it->second;
6041 : /* Handle the STD_PARALLEL attrib */
6042 194 : if( EQUAL( pszParamVal, CF_PP_STD_PARALLEL_1 ) ) {
6043 16 : bFoundStdP1 = TRUE;
6044 16 : dfStdP[0] = dfValue;
6045 : }
6046 178 : else if( EQUAL( pszParamVal, CF_PP_STD_PARALLEL_2 ) ) {
6047 7 : bFoundStdP2 = TRUE;
6048 7 : dfStdP[1] = dfValue;
6049 : }
6050 : else {
6051 : nc_put_att_double( fpImage, NCDFVarID, pszParamVal,
6052 171 : NC_DOUBLE, 1,&dfValue );
6053 : }
6054 : }
6055 : /* Now write the STD_PARALLEL attrib */
6056 41 : if ( bFoundStdP1 ) {
6057 : /* one value or equal values */
6058 25 : if ( !bFoundStdP2 || dfStdP[0] == dfStdP[1] ) {
6059 : nc_put_att_double( fpImage, NCDFVarID, CF_PP_STD_PARALLEL,
6060 9 : NC_DOUBLE, 1, &dfStdP[0] );
6061 : }
6062 : else { /* two values */
6063 : nc_put_att_double( fpImage, NCDFVarID, CF_PP_STD_PARALLEL,
6064 7 : NC_DOUBLE, 2, dfStdP );
6065 : }
6066 41 : }
6067 41 : }
6068 :
6069 2811 : CPLErr NCDFSafeStrcat(char** ppszDest, char* pszSrc, size_t* nDestSize)
6070 : {
6071 : /* Reallocate the data string until the content fits */
6072 8021 : while(*nDestSize < (strlen(*ppszDest) + strlen(pszSrc) + 1)) {
6073 2399 : (*nDestSize) *= 2;
6074 2399 : *ppszDest = (char*) CPLRealloc((void*) *ppszDest, *nDestSize);
6075 : }
6076 2811 : strcat(*ppszDest, pszSrc);
6077 :
6078 2811 : return CE_None;
6079 : }
6080 :
6081 : /* helper function for NCDFGetAttr() */
6082 : /* sets pdfValue to first value returned */
6083 : /* and if bSetPszValue=True sets pszValue with all attribute values */
6084 : /* pszValue is the responsibility of the caller and must be freed */
6085 9067 : CPLErr NCDFGetAttr1( int nCdfId, int nVarId, const char *pszAttrName,
6086 : double *pdfValue, char **pszValue, int bSetPszValue )
6087 : {
6088 9067 : nc_type nAttrType = NC_NAT;
6089 9067 : size_t nAttrLen = 0;
6090 : size_t nAttrValueSize;
6091 9067 : int status = 0; /*rename this */
6092 : size_t m;
6093 : char szTemp[ NCDF_MAX_STR_LEN ];
6094 9067 : char *pszAttrValue = NULL;
6095 9067 : double dfValue = 0.0;
6096 :
6097 9067 : status = nc_inq_att( nCdfId, nVarId, pszAttrName, &nAttrType, &nAttrLen);
6098 9067 : if ( status != NC_NOERR )
6099 2936 : return CE_Failure;
6100 :
6101 : /* Allocate guaranteed minimum size */
6102 6131 : nAttrValueSize = nAttrLen + 1;
6103 6131 : pszAttrValue = (char *) CPLCalloc( nAttrValueSize, sizeof( char ));
6104 6131 : *pszAttrValue = '\0';
6105 :
6106 6131 : if ( nAttrLen > 1 && nAttrType != NC_CHAR )
6107 279 : NCDFSafeStrcat(&pszAttrValue, (char *)"{", &nAttrValueSize);
6108 :
6109 6131 : switch (nAttrType) {
6110 : case NC_CHAR:
6111 4354 : nc_get_att_text( nCdfId, nVarId, pszAttrName, pszAttrValue );
6112 4354 : pszAttrValue[nAttrLen]='\0';
6113 4354 : dfValue = 0.0;
6114 4354 : break;
6115 : /* TODO support NC_UBYTE */
6116 : case NC_BYTE:
6117 : signed char *pscTemp;
6118 285 : pscTemp = (signed char *) CPLCalloc( nAttrLen, sizeof( signed char ) );
6119 285 : nc_get_att_schar( nCdfId, nVarId, pszAttrName, pscTemp );
6120 285 : dfValue = (double)pscTemp[0];
6121 289 : for(m=0; m < nAttrLen-1; m++) {
6122 4 : sprintf( szTemp, "%d,", pscTemp[m] );
6123 4 : NCDFSafeStrcat(&pszAttrValue, szTemp, &nAttrValueSize);
6124 : }
6125 285 : sprintf( szTemp, "%d", pscTemp[m] );
6126 285 : NCDFSafeStrcat(&pszAttrValue, szTemp, &nAttrValueSize);
6127 285 : CPLFree(pscTemp);
6128 285 : break;
6129 : case NC_SHORT:
6130 : short *psTemp;
6131 243 : psTemp = (short *) CPLCalloc( nAttrLen, sizeof( short ) );
6132 243 : nc_get_att_short( nCdfId, nVarId, pszAttrName, psTemp );
6133 243 : dfValue = (double)psTemp[0];
6134 429 : for(m=0; m < nAttrLen-1; m++) {
6135 186 : sprintf( szTemp, "%hd,", psTemp[m] );
6136 186 : NCDFSafeStrcat(&pszAttrValue, szTemp, &nAttrValueSize);
6137 : }
6138 243 : sprintf( szTemp, "%hd", psTemp[m] );
6139 243 : NCDFSafeStrcat(&pszAttrValue, szTemp, &nAttrValueSize);
6140 243 : CPLFree(psTemp);
6141 243 : break;
6142 : case NC_INT:
6143 : int *pnTemp;
6144 255 : pnTemp = (int *) CPLCalloc( nAttrLen, sizeof( int ) );
6145 255 : nc_get_att_int( nCdfId, nVarId, pszAttrName, pnTemp );
6146 255 : dfValue = (double)pnTemp[0];
6147 318 : for(m=0; m < nAttrLen-1; m++) {
6148 63 : sprintf( szTemp, "%d,", pnTemp[m] );
6149 63 : NCDFSafeStrcat(&pszAttrValue, szTemp, &nAttrValueSize);
6150 : }
6151 255 : sprintf( szTemp, "%d", pnTemp[m] );
6152 255 : NCDFSafeStrcat(&pszAttrValue, szTemp, &nAttrValueSize);
6153 255 : CPLFree(pnTemp);
6154 255 : break;
6155 : case NC_FLOAT:
6156 : float *pfTemp;
6157 154 : pfTemp = (float *) CPLCalloc( nAttrLen, sizeof( float ) );
6158 154 : nc_get_att_float( nCdfId, nVarId, pszAttrName, pfTemp );
6159 154 : dfValue = (double)pfTemp[0];
6160 166 : for(m=0; m < nAttrLen-1; m++) {
6161 12 : sprintf( szTemp, "%.8g,", pfTemp[m] );
6162 12 : NCDFSafeStrcat(&pszAttrValue, szTemp, &nAttrValueSize);
6163 : }
6164 154 : sprintf( szTemp, "%.8g", pfTemp[m] );
6165 154 : NCDFSafeStrcat(&pszAttrValue,szTemp, &nAttrValueSize);
6166 154 : CPLFree(pfTemp);
6167 154 : break;
6168 : case NC_DOUBLE:
6169 : double *pdfTemp;
6170 840 : pdfTemp = (double *) CPLCalloc(nAttrLen, sizeof(double));
6171 840 : nc_get_att_double( nCdfId, nVarId, pszAttrName, pdfTemp );
6172 840 : dfValue = pdfTemp[0];
6173 878 : for(m=0; m < nAttrLen-1; m++) {
6174 38 : sprintf( szTemp, "%.16g,", pdfTemp[m] );
6175 38 : NCDFSafeStrcat(&pszAttrValue, szTemp, &nAttrValueSize);
6176 : }
6177 840 : sprintf( szTemp, "%.16g", pdfTemp[m] );
6178 840 : NCDFSafeStrcat(&pszAttrValue, szTemp, &nAttrValueSize);
6179 840 : CPLFree(pdfTemp);
6180 840 : break;
6181 : default:
6182 : CPLDebug( "GDAL_netCDF", "NCDFGetAttr unsupported type %d for attribute %s",
6183 0 : nAttrType,pszAttrName);
6184 0 : CPLFree( pszAttrValue );
6185 0 : pszAttrValue = NULL;
6186 : break;
6187 : }
6188 :
6189 6131 : if ( nAttrLen > 1 && nAttrType!= NC_CHAR )
6190 279 : NCDFSafeStrcat(&pszAttrValue, (char *)"}", &nAttrValueSize);
6191 :
6192 : /* set return values */
6193 6131 : if ( bSetPszValue == TRUE ) *pszValue = pszAttrValue;
6194 246 : else CPLFree ( pszAttrValue );
6195 6131 : if ( pdfValue ) *pdfValue = dfValue;
6196 :
6197 6131 : return CE_None;
6198 : }
6199 :
6200 :
6201 : /* sets pdfValue to first value found */
6202 246 : CPLErr NCDFGetAttr( int nCdfId, int nVarId, const char *pszAttrName,
6203 : double *pdfValue )
6204 : {
6205 246 : return NCDFGetAttr1( nCdfId, nVarId, pszAttrName, pdfValue, NULL, FALSE );
6206 : }
6207 :
6208 :
6209 : /* pszValue is the responsibility of the caller and must be freed */
6210 8821 : CPLErr NCDFGetAttr( int nCdfId, int nVarId, const char *pszAttrName,
6211 : char **pszValue )
6212 : {
6213 8821 : return NCDFGetAttr1( nCdfId, nVarId, pszAttrName, FALSE, pszValue, TRUE );
6214 : }
6215 :
6216 :
6217 : /* By default write NC_CHAR, but detect for int/float/double */
6218 207 : CPLErr NCDFPutAttr( int nCdfId, int nVarId,
6219 : const char *pszAttrName, const char *pszValue )
6220 : {
6221 207 : nc_type nAttrType = NC_CHAR;
6222 207 : nc_type nTmpAttrType = NC_CHAR;
6223 207 : size_t nAttrLen = 0;
6224 207 : int status = 0;
6225 : size_t i;
6226 : char szTemp[ NCDF_MAX_STR_LEN ];
6227 207 : char *pszTemp = NULL;
6228 207 : char **papszValues = NULL;
6229 :
6230 207 : int nValue = 0;
6231 207 : float fValue = 0.0f;
6232 207 : double dfValue = 0.0;
6233 :
6234 : /* get the attribute values as tokens */
6235 207 : papszValues = NCDFTokenizeArray( pszValue );
6236 207 : if ( papszValues == NULL )
6237 0 : return CE_Failure;
6238 :
6239 207 : nAttrLen = CSLCount(papszValues);
6240 :
6241 : /* first detect type */
6242 207 : nAttrType = NC_CHAR;
6243 420 : for ( i=0; i<nAttrLen; i++ ) {
6244 213 : nTmpAttrType = NC_CHAR;
6245 213 : errno = 0;
6246 213 : nValue = strtol( papszValues[i], &pszTemp, 10 );
6247 213 : dfValue = (double) nValue;
6248 : /* test for int */
6249 : /* TODO test for Byte and short - can this be done safely? */
6250 224 : if ( (errno == 0) && (papszValues[i] != pszTemp) && (*pszTemp == 0) ) {
6251 11 : nTmpAttrType = NC_INT;
6252 : }
6253 : else {
6254 : /* test for double */
6255 202 : errno = 0;
6256 202 : dfValue = strtod( papszValues[i], &pszTemp );
6257 202 : if ( (errno == 0) && (papszValues[i] != pszTemp) && (*pszTemp == 0) ) {
6258 : /* test for float instead of double */
6259 : /* strtof() is C89, which is not available in MSVC */
6260 : /* see if we loose precision if we cast to float and write to char* */
6261 6 : fValue = (float)dfValue;
6262 6 : sprintf( szTemp,"%.8g",fValue);
6263 6 : if ( EQUAL(szTemp, papszValues[i] ) )
6264 4 : nTmpAttrType = NC_FLOAT;
6265 : else
6266 2 : nTmpAttrType = NC_DOUBLE;
6267 : }
6268 : }
6269 213 : if ( nTmpAttrType > nAttrType )
6270 11 : nAttrType = nTmpAttrType;
6271 : }
6272 :
6273 : /* now write the data */
6274 207 : if ( nAttrType == NC_CHAR ) {
6275 : status = nc_put_att_text( nCdfId, nVarId, pszAttrName,
6276 196 : strlen( pszValue ), pszValue );
6277 196 : NCDF_ERR(status);
6278 : }
6279 : else {
6280 :
6281 11 : switch( nAttrType ) {
6282 : case NC_INT:
6283 : int *pnTemp;
6284 7 : pnTemp = (int *) CPLCalloc( nAttrLen, sizeof( int ) );
6285 18 : for(i=0; i < nAttrLen; i++) {
6286 11 : pnTemp[i] = strtol( papszValues[i], &pszTemp, 10 );
6287 : }
6288 : status = nc_put_att_int( nCdfId, nVarId, pszAttrName,
6289 7 : NC_INT, nAttrLen, pnTemp );
6290 7 : NCDF_ERR(status);
6291 7 : CPLFree(pnTemp);
6292 7 : break;
6293 : case NC_FLOAT:
6294 : float *pfTemp;
6295 3 : pfTemp = (float *) CPLCalloc( nAttrLen, sizeof( float ) );
6296 7 : for(i=0; i < nAttrLen; i++) {
6297 4 : pfTemp[i] = (float)strtod( papszValues[i], &pszTemp );
6298 : }
6299 : status = nc_put_att_float( nCdfId, nVarId, pszAttrName,
6300 3 : NC_FLOAT, nAttrLen, pfTemp );
6301 3 : NCDF_ERR(status);
6302 3 : CPLFree(pfTemp);
6303 3 : break;
6304 : case NC_DOUBLE:
6305 : double *pdfTemp;
6306 1 : pdfTemp = (double *) CPLCalloc( nAttrLen, sizeof( double ) );
6307 3 : for(i=0; i < nAttrLen; i++) {
6308 2 : pdfTemp[i] = strtod( papszValues[i], &pszTemp );
6309 : }
6310 : status = nc_put_att_double( nCdfId, nVarId, pszAttrName,
6311 1 : NC_DOUBLE, nAttrLen, pdfTemp );
6312 1 : NCDF_ERR(status);
6313 1 : CPLFree(pdfTemp);
6314 1 : break;
6315 : default:
6316 0 : if ( papszValues ) CSLDestroy( papszValues );
6317 0 : return CE_Failure;
6318 : break;
6319 : }
6320 : }
6321 :
6322 207 : if ( papszValues ) CSLDestroy( papszValues );
6323 :
6324 207 : return CE_None;
6325 : }
6326 :
6327 42 : CPLErr NCDFGet1DVar( int nCdfId, int nVarId, char **pszValue )
6328 : {
6329 42 : nc_type nVarType = NC_NAT;
6330 42 : size_t nVarLen = 0;
6331 42 : int status = 0;
6332 : size_t m;
6333 : char szTemp[ NCDF_MAX_STR_LEN ];
6334 42 : char *pszVarValue = NULL;
6335 : size_t nVarValueSize;
6336 42 : int nVarDimId=-1;
6337 : size_t start[1], count[1];
6338 :
6339 : /* get var information */
6340 42 : status = nc_inq_varndims( nCdfId, nVarId, &nVarDimId );
6341 42 : if ( status != NC_NOERR || nVarDimId != 1)
6342 6 : return CE_Failure;
6343 36 : status = nc_inq_vardimid( nCdfId, nVarId, &nVarDimId );
6344 36 : if ( status != NC_NOERR )
6345 0 : return CE_Failure;
6346 36 : status = nc_inq_vartype( nCdfId, nVarId, &nVarType );
6347 36 : if ( status != NC_NOERR )
6348 0 : return CE_Failure;
6349 36 : status = nc_inq_dimlen( nCdfId, nVarDimId, &nVarLen );
6350 36 : if ( status != NC_NOERR )
6351 0 : return CE_Failure;
6352 36 : start[0] = 0;
6353 36 : count[0] = nVarLen;
6354 :
6355 : /* Allocate guaranteed minimum size */
6356 36 : nVarValueSize = NCDF_MAX_STR_LEN;
6357 36 : pszVarValue = (char *) CPLCalloc( nVarValueSize, sizeof( char ));
6358 36 : *pszVarValue = '\0';
6359 :
6360 36 : if ( nVarLen > 1 && nVarType != NC_CHAR )
6361 19 : NCDFSafeStrcat(&pszVarValue, (char *)"{", &nVarValueSize);
6362 :
6363 36 : switch (nVarType) {
6364 : case NC_CHAR:
6365 0 : nc_get_vara_text( nCdfId, nVarId, start, count, pszVarValue );
6366 0 : pszVarValue[nVarLen]='\0';
6367 0 : break;
6368 : /* TODO support NC_UBYTE */
6369 : case NC_BYTE:
6370 : signed char *pscTemp;
6371 0 : pscTemp = (signed char *) CPLCalloc( nVarLen, sizeof( signed char ) );
6372 0 : nc_get_vara_schar( nCdfId, nVarId, start, count, pscTemp );
6373 0 : for(m=0; m < nVarLen-1; m++) {
6374 0 : sprintf( szTemp, "%d,", pscTemp[m] );
6375 0 : NCDFSafeStrcat(&pszVarValue, szTemp, &nVarValueSize);
6376 : }
6377 0 : sprintf( szTemp, "%d", pscTemp[m] );
6378 0 : NCDFSafeStrcat(&pszVarValue, szTemp, &nVarValueSize);
6379 0 : CPLFree(pscTemp);
6380 0 : break;
6381 : case NC_SHORT:
6382 : short *psTemp;
6383 0 : psTemp = (short *) CPLCalloc( nVarLen, sizeof( short ) );
6384 0 : nc_get_vara_short( nCdfId, nVarId, start, count, psTemp );
6385 0 : for(m=0; m < nVarLen-1; m++) {
6386 0 : sprintf( szTemp, "%hd,", psTemp[m] );
6387 0 : NCDFSafeStrcat(&pszVarValue, szTemp, &nVarValueSize);
6388 : }
6389 0 : sprintf( szTemp, "%hd", psTemp[m] );
6390 0 : NCDFSafeStrcat(&pszVarValue, szTemp, &nVarValueSize);
6391 0 : CPLFree(psTemp);
6392 0 : break;
6393 : case NC_INT:
6394 : int *pnTemp;
6395 9 : pnTemp = (int *) CPLCalloc( nVarLen, sizeof( int ) );
6396 9 : nc_get_vara_int( nCdfId, nVarId, start, count, pnTemp );
6397 18 : for(m=0; m < nVarLen-1; m++) {
6398 9 : sprintf( szTemp, "%d,", pnTemp[m] );
6399 9 : NCDFSafeStrcat(&pszVarValue, szTemp, &nVarValueSize);
6400 : }
6401 9 : sprintf( szTemp, "%d", pnTemp[m] );
6402 9 : NCDFSafeStrcat(&pszVarValue, szTemp, &nVarValueSize);
6403 9 : CPLFree(pnTemp);
6404 9 : break;
6405 : case NC_FLOAT:
6406 : float *pfTemp;
6407 1 : pfTemp = (float *) CPLCalloc( nVarLen, sizeof( float ) );
6408 1 : nc_get_vara_float( nCdfId, nVarId, start, count, pfTemp );
6409 64 : for(m=0; m < nVarLen-1; m++) {
6410 63 : sprintf( szTemp, "%.8g,", pfTemp[m] );
6411 63 : NCDFSafeStrcat(&pszVarValue, szTemp, &nVarValueSize);
6412 : }
6413 1 : sprintf( szTemp, "%.8g", pfTemp[m] );
6414 1 : NCDFSafeStrcat(&pszVarValue,szTemp, &nVarValueSize);
6415 1 : CPLFree(pfTemp);
6416 1 : break;
6417 : case NC_DOUBLE:
6418 : double *pdfTemp;
6419 26 : pdfTemp = (double *) CPLCalloc(nVarLen, sizeof(double));
6420 26 : nc_get_vara_double( nCdfId, nVarId, start, count, pdfTemp );
6421 53 : for(m=0; m < nVarLen-1; m++) {
6422 27 : sprintf( szTemp, "%.16g,", pdfTemp[m] );
6423 27 : NCDFSafeStrcat(&pszVarValue, szTemp, &nVarValueSize);
6424 : }
6425 26 : sprintf( szTemp, "%.16g", pdfTemp[m] );
6426 26 : NCDFSafeStrcat(&pszVarValue, szTemp, &nVarValueSize);
6427 26 : CPLFree(pdfTemp);
6428 26 : break;
6429 : default:
6430 : CPLDebug( "GDAL_netCDF", "NCDFGetVar1D unsupported type %d",
6431 0 : nVarType );
6432 0 : CPLFree( pszVarValue );
6433 0 : pszVarValue = NULL;
6434 : break;
6435 : }
6436 :
6437 36 : if ( nVarLen > 1 && nVarType!= NC_CHAR )
6438 19 : NCDFSafeStrcat(&pszVarValue, (char *)"}", &nVarValueSize);
6439 :
6440 : /* set return values */
6441 36 : *pszValue = pszVarValue;
6442 :
6443 36 : return CE_None;
6444 : }
6445 :
6446 7 : CPLErr NCDFPut1DVar( int nCdfId, int nVarId, const char *pszValue )
6447 : {
6448 7 : nc_type nVarType = NC_CHAR;
6449 7 : size_t nVarLen = 0;
6450 7 : int status = 0;
6451 : size_t i;
6452 7 : char *pszTemp = NULL;
6453 7 : char **papszValues = NULL;
6454 :
6455 7 : int nVarDimId=-1;
6456 : size_t start[1], count[1];
6457 :
6458 7 : if ( EQUAL( pszValue, "" ) )
6459 0 : return CE_Failure;
6460 :
6461 : /* get var information */
6462 7 : status = nc_inq_varndims( nCdfId, nVarId, &nVarDimId );
6463 7 : if ( status != NC_NOERR || nVarDimId != 1)
6464 0 : return CE_Failure;
6465 7 : status = nc_inq_vardimid( nCdfId, nVarId, &nVarDimId );
6466 7 : if ( status != NC_NOERR )
6467 0 : return CE_Failure;
6468 7 : status = nc_inq_vartype( nCdfId, nVarId, &nVarType );
6469 7 : if ( status != NC_NOERR )
6470 0 : return CE_Failure;
6471 7 : status = nc_inq_dimlen( nCdfId, nVarDimId, &nVarLen );
6472 7 : if ( status != NC_NOERR )
6473 0 : return CE_Failure;
6474 7 : start[0] = 0;
6475 7 : count[0] = nVarLen;
6476 :
6477 : /* get the values as tokens */
6478 7 : papszValues = NCDFTokenizeArray( pszValue );
6479 7 : if ( papszValues == NULL )
6480 0 : return CE_Failure;
6481 :
6482 7 : nVarLen = CSLCount(papszValues);
6483 :
6484 : /* now write the data */
6485 7 : if ( nVarType == NC_CHAR ) {
6486 : status = nc_put_vara_text( nCdfId, nVarId, start, count,
6487 0 : pszValue );
6488 0 : NCDF_ERR(status);
6489 : }
6490 : else {
6491 :
6492 7 : switch( nVarType ) {
6493 : /* TODO add other types */
6494 : case NC_INT:
6495 : int *pnTemp;
6496 2 : pnTemp = (int *) CPLCalloc( nVarLen, sizeof( int ) );
6497 6 : for(i=0; i < nVarLen; i++) {
6498 4 : pnTemp[i] = strtol( papszValues[i], &pszTemp, 10 );
6499 : }
6500 2 : status = nc_put_vara_int( nCdfId, nVarId, start, count, pnTemp );
6501 2 : NCDF_ERR(status);
6502 2 : CPLFree(pnTemp);
6503 2 : break;
6504 : case NC_FLOAT:
6505 : float *pfTemp;
6506 0 : pfTemp = (float *) CPLCalloc( nVarLen, sizeof( float ) );
6507 0 : for(i=0; i < nVarLen; i++) {
6508 0 : pfTemp[i] = (float)strtod( papszValues[i], &pszTemp );
6509 : }
6510 : status = nc_put_vara_float( nCdfId, nVarId, start, count,
6511 0 : pfTemp );
6512 0 : NCDF_ERR(status);
6513 0 : CPLFree(pfTemp);
6514 0 : break;
6515 : case NC_DOUBLE:
6516 : double *pdfTemp;
6517 5 : pdfTemp = (double *) CPLCalloc( nVarLen, sizeof( double ) );
6518 16 : for(i=0; i < nVarLen; i++) {
6519 11 : pdfTemp[i] = strtod( papszValues[i], &pszTemp );
6520 : }
6521 : status = nc_put_vara_double( nCdfId, nVarId, start, count,
6522 5 : pdfTemp );
6523 5 : NCDF_ERR(status);
6524 5 : CPLFree(pdfTemp);
6525 5 : break;
6526 : default:
6527 0 : if ( papszValues ) CSLDestroy( papszValues );
6528 0 : return CE_Failure;
6529 : break;
6530 : }
6531 : }
6532 :
6533 7 : if ( papszValues ) CSLDestroy( papszValues );
6534 :
6535 7 : return CE_None;
6536 : }
6537 :
6538 :
6539 : /************************************************************************/
6540 : /* GetDefaultNoDataValue() */
6541 : /************************************************************************/
6542 :
6543 174 : double NCDFGetDefaultNoDataValue( int nVarType )
6544 :
6545 : {
6546 174 : double dfNoData = 0.0;
6547 :
6548 174 : switch( nVarType ) {
6549 : case NC_BYTE:
6550 : #ifdef NETCDF_HAS_NC4
6551 : case NC_UBYTE:
6552 : #endif
6553 : /* don't do default fill-values for bytes, too risky */
6554 73 : dfNoData = 0.0;
6555 73 : break;
6556 : case NC_CHAR:
6557 0 : dfNoData = NC_FILL_CHAR;
6558 0 : break;
6559 : case NC_SHORT:
6560 10 : dfNoData = NC_FILL_SHORT;
6561 10 : break;
6562 : case NC_INT:
6563 24 : dfNoData = NC_FILL_INT;
6564 24 : break;
6565 : case NC_FLOAT:
6566 43 : dfNoData = NC_FILL_FLOAT;
6567 43 : break;
6568 : case NC_DOUBLE:
6569 24 : dfNoData = NC_FILL_DOUBLE;
6570 24 : break;
6571 : default:
6572 0 : dfNoData = 0.0;
6573 : break;
6574 : }
6575 :
6576 174 : return dfNoData;
6577 : }
6578 :
6579 :
6580 1116 : int NCDFDoesVarContainAttribVal( int nCdfId,
6581 : const char ** papszAttribNames,
6582 : const char ** papszAttribValues,
6583 : int nVarId,
6584 : const char * pszVarName,
6585 : int bStrict=TRUE )
6586 : {
6587 1116 : char *pszTemp = NULL;
6588 1116 : int bFound = FALSE;
6589 :
6590 1116 : if ( (nVarId == -1) && (pszVarName != NULL) )
6591 697 : nc_inq_varid( nCdfId, pszVarName, &nVarId );
6592 :
6593 1116 : if ( nVarId == -1 ) return -1;
6594 :
6595 3192 : for( int i=0; !bFound && i<CSLCount((char**)papszAttribNames); i++ ) {
6596 2212 : if ( NCDFGetAttr( nCdfId, nVarId, papszAttribNames[i], &pszTemp )
6597 : == CE_None ) {
6598 995 : if ( bStrict ) {
6599 995 : if ( EQUAL( pszTemp, papszAttribValues[i] ) )
6600 346 : bFound=TRUE;
6601 : }
6602 : else {
6603 0 : if ( EQUALN( pszTemp, papszAttribValues[i], strlen(papszAttribValues[i]) ) )
6604 0 : bFound=TRUE;
6605 : }
6606 995 : CPLFree( pszTemp );
6607 : }
6608 : }
6609 980 : return bFound;
6610 : }
6611 :
6612 69 : int NCDFDoesVarContainAttribVal2( int nCdfId,
6613 : const char * papszAttribName,
6614 : const char ** papszAttribValues,
6615 : int nVarId,
6616 : const char * pszVarName,
6617 : int bStrict=TRUE )
6618 : {
6619 69 : char *pszTemp = NULL;
6620 69 : int bFound = FALSE;
6621 :
6622 69 : if ( (nVarId == -1) && (pszVarName != NULL) )
6623 69 : nc_inq_varid( nCdfId, pszVarName, &nVarId );
6624 :
6625 69 : if ( nVarId == -1 ) return -1;
6626 :
6627 69 : if ( NCDFGetAttr( nCdfId, nVarId, papszAttribName, &pszTemp )
6628 0 : != CE_None ) return FALSE;
6629 :
6630 889 : for( int i=0; !bFound && i<CSLCount((char**)papszAttribValues); i++ ) {
6631 820 : if ( bStrict ) {
6632 716 : if ( EQUAL( pszTemp, papszAttribValues[i] ) )
6633 9 : bFound=TRUE;
6634 : }
6635 : else {
6636 104 : if ( EQUALN( pszTemp, papszAttribValues[i], strlen(papszAttribValues[i]) ) )
6637 26 : bFound=TRUE;
6638 : }
6639 : }
6640 :
6641 69 : CPLFree( pszTemp );
6642 :
6643 69 : return bFound;
6644 : }
6645 :
6646 136 : int NCDFEqual( const char * papszName, const char ** papszValues )
6647 : {
6648 136 : int bFound = FALSE;
6649 :
6650 136 : if ( papszName == NULL || EQUAL(papszName,"") )
6651 0 : return FALSE;
6652 :
6653 136 : for( int i=0; i<CSLCount((char**)papszValues); i++ ) {
6654 136 : if( EQUAL( papszName, papszValues[i] ) )
6655 68 : bFound = TRUE;
6656 136 : break;
6657 : }
6658 :
6659 136 : return bFound;
6660 : }
6661 :
6662 : /* test that a variable is longitude/latitude coordinate, following CF 4.1 and 4.2 */
6663 615 : int NCDFIsVarLongitude( int nCdfId, int nVarId,
6664 : const char * pszVarName )
6665 : {
6666 : /* check for matching attributes */
6667 : int bVal = NCDFDoesVarContainAttribVal( nCdfId,
6668 : papszCFLongitudeAttribNames,
6669 : papszCFLongitudeAttribValues,
6670 615 : nVarId, pszVarName );
6671 : /* if not found using attributes then check using var name */
6672 : /* unless GDAL_NETCDF_VERIFY_DIMS=STRICT */
6673 615 : if ( bVal == -1 ) {
6674 34 : if ( ! EQUAL( CPLGetConfigOption( "GDAL_NETCDF_VERIFY_DIMS", "YES" ),
6675 : "STRICT" ) )
6676 34 : bVal = NCDFEqual(pszVarName, papszCFLongitudeVarNames );
6677 : else
6678 0 : bVal = FALSE;
6679 : }
6680 615 : return bVal;
6681 : }
6682 :
6683 195 : int NCDFIsVarLatitude( int nCdfId, int nVarId, const char * pszVarName )
6684 : {
6685 : int bVal = NCDFDoesVarContainAttribVal( nCdfId,
6686 : papszCFLatitudeAttribNames,
6687 : papszCFLatitudeAttribValues,
6688 195 : nVarId, pszVarName );
6689 195 : if ( bVal == -1 ) {
6690 34 : if ( ! EQUAL( CPLGetConfigOption( "GDAL_NETCDF_VERIFY_DIMS", "YES" ),
6691 : "STRICT" ) )
6692 34 : bVal = NCDFEqual(pszVarName, papszCFLatitudeVarNames );
6693 : else
6694 0 : bVal = FALSE;
6695 : }
6696 195 : return bVal;
6697 : }
6698 :
6699 127 : int NCDFIsVarProjectionX( int nCdfId, int nVarId, const char * pszVarName )
6700 : {
6701 : int bVal = NCDFDoesVarContainAttribVal( nCdfId,
6702 : papszCFProjectionXAttribNames,
6703 : papszCFProjectionXAttribValues,
6704 127 : nVarId, pszVarName );
6705 127 : if ( bVal == -1 ) {
6706 34 : if ( ! EQUAL( CPLGetConfigOption( "GDAL_NETCDF_VERIFY_DIMS", "YES" ),
6707 : "STRICT" ) )
6708 34 : bVal = NCDFEqual(pszVarName, papszCFProjectionXVarNames );
6709 : else
6710 0 : bVal = FALSE;
6711 :
6712 : }
6713 127 : return bVal;
6714 : }
6715 :
6716 127 : int NCDFIsVarProjectionY( int nCdfId, int nVarId, const char * pszVarName )
6717 : {
6718 : int bVal = NCDFDoesVarContainAttribVal( nCdfId,
6719 : papszCFProjectionYAttribNames,
6720 : papszCFProjectionYAttribValues,
6721 127 : nVarId, pszVarName );
6722 127 : if ( bVal == -1 ) {
6723 34 : if ( ! EQUAL( CPLGetConfigOption( "GDAL_NETCDF_VERIFY_DIMS", "YES" ),
6724 : "STRICT" ) )
6725 34 : bVal = NCDFEqual(pszVarName, papszCFProjectionYVarNames );
6726 : else
6727 0 : bVal = FALSE;
6728 : }
6729 127 : return bVal;
6730 : }
6731 :
6732 : /* test that a variable is a vertical coordinate, following CF 4.3 */
6733 26 : int NCDFIsVarVerticalCoord( int nCdfId, int nVarId,
6734 : const char * pszVarName )
6735 : {
6736 : /* check for matching attributes */
6737 26 : if ( NCDFDoesVarContainAttribVal( nCdfId,
6738 : papszCFVerticalAttribNames,
6739 : papszCFVerticalAttribValues,
6740 : nVarId, pszVarName ) == TRUE )
6741 0 : return TRUE;
6742 : /* check for matching units */
6743 26 : else if ( NCDFDoesVarContainAttribVal2( nCdfId,
6744 : CF_UNITS,
6745 : papszCFVerticalUnitsValues,
6746 : nVarId, pszVarName ) == TRUE )
6747 9 : return TRUE;
6748 : /* check for matching standard name */
6749 17 : else if ( NCDFDoesVarContainAttribVal2( nCdfId,
6750 : CF_STD_NAME,
6751 : papszCFVerticalStandardNameValues,
6752 : nVarId, pszVarName ) == TRUE )
6753 0 : return TRUE;
6754 : else
6755 17 : return FALSE;
6756 : }
6757 :
6758 : /* test that a variable is a time coordinate, following CF 4.4 */
6759 26 : int NCDFIsVarTimeCoord( int nCdfId, int nVarId,
6760 : const char * pszVarName )
6761 : {
6762 : /* check for matching attributes */
6763 26 : if ( NCDFDoesVarContainAttribVal( nCdfId,
6764 : papszCFTimeAttribNames,
6765 : papszCFTimeAttribValues,
6766 : nVarId, pszVarName ) == TRUE )
6767 0 : return TRUE;
6768 : /* check for matching units */
6769 26 : else if ( NCDFDoesVarContainAttribVal2( nCdfId,
6770 : CF_UNITS,
6771 : papszCFTimeUnitsValues,
6772 : nVarId, pszVarName, FALSE ) == TRUE )
6773 26 : return TRUE;
6774 : else
6775 0 : return FALSE;
6776 : }
6777 :
6778 : /* parse a string, and return as a string list */
6779 : /* if it an array of the form {a,b} then tokenize it */
6780 : /* else return a copy */
6781 327 : char **NCDFTokenizeArray( const char *pszValue )
6782 : {
6783 327 : char **papszValues = NULL;
6784 327 : char *pszTemp = NULL;
6785 327 : int nLen = 0;
6786 :
6787 327 : if ( pszValue==NULL || EQUAL( pszValue, "" ) )
6788 56 : return NULL;
6789 :
6790 271 : nLen = strlen(pszValue);
6791 :
6792 318 : if ( ( pszValue[0] == '{' ) && ( pszValue[nLen-1] == '}' ) ) {
6793 47 : pszTemp = (char *) CPLCalloc(nLen-2,sizeof(char*));
6794 47 : strncpy( pszTemp, pszValue+1, nLen-2);
6795 47 : pszTemp[nLen-2] = '\0';
6796 47 : papszValues = CSLTokenizeString2( pszTemp, ",", CSLT_ALLOWEMPTYTOKENS );
6797 47 : CPLFree( pszTemp);
6798 : }
6799 : else {
6800 224 : papszValues = (char**) CPLCalloc(2,sizeof(char*));
6801 224 : papszValues[0] = CPLStrdup( pszValue );
6802 224 : papszValues[1] = NULL;
6803 : }
6804 :
6805 271 : return papszValues;
6806 : }
6807 :
|