1 : /******************************************************************************
2 : * $Id: netcdfdataset.cpp 23580 2011-12-15 22:24:12Z etourigny $
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 : CPL_CVSID("$Id: netcdfdataset.cpp 23580 2011-12-15 22:24:12Z etourigny $");
33 :
34 : #include <map> //for NCDFWriteProjAttribs()
35 :
36 : /* Internal function declarations */
37 :
38 : int NCDFIsGDALVersionGTE(const char* pszVersion, int nTarget);
39 :
40 : void NCDFAddGDALHistory( int fpImage,
41 : const char * pszFilename, const char *pszOldHist,
42 : const char * pszFunctionName );
43 :
44 : void NCDFAddHistory(int fpImage, const char *pszAddHist, const char *pszOldHist);
45 :
46 : int NCDFIsCfProjection( const char* pszProjection );
47 :
48 : void NCDFWriteProjAttribs(const OGR_SRSNode *poPROJCS,
49 : const char* pszProjection,
50 : const int fpImage, const int NCDFVarID);
51 :
52 : CPLErr NCDFSafeStrcat(char** ppszDest, char* pszSrc, size_t* nDestSize);
53 :
54 : CPLErr NCDFGetAttr( int nCdfId, int nVarId, const char *pszAttrName,
55 : double *pdfValue );
56 :
57 : CPLErr NCDFGetAttr( int nCdfId, int nVarId, const char *pszAttrName,
58 : char **pszValue );
59 :
60 : CPLErr NCDFPutAttr( int nCdfId, int nVarId,
61 : const char *pszAttrName, const char *pszValue );
62 :
63 :
64 : /************************************************************************/
65 : /* ==================================================================== */
66 : /* netCDFRasterBand */
67 : /* ==================================================================== */
68 : /************************************************************************/
69 :
70 : class netCDFRasterBand : public GDALPamRasterBand
71 : {
72 : friend class netCDFDataset;
73 :
74 : nc_type nc_datatype;
75 : int cdfid;
76 : int nZId;
77 : int nZDim;
78 : int nLevel;
79 : int nBandXPos;
80 : int nBandYPos;
81 : int *panBandZPos;
82 : int *panBandZLev;
83 : int bNoDataSet;
84 : double dfNoDataValue;
85 : double adfValidRange[2];
86 : double dfScale;
87 : double dfOffset;
88 : int bSignedData;
89 : int status;
90 :
91 : CPLErr CreateBandMetadata( int *paDimIds );
92 : template <class T> void CheckValidData ( void * pImage,
93 : int bCheckIsNan=FALSE ) ;
94 :
95 : public:
96 :
97 : netCDFRasterBand( netCDFDataset *poDS,
98 : int nZId,
99 : int nZDim,
100 : int nLevel,
101 : int *panBandZLen,
102 : int *panBandPos,
103 : int *paDimIds,
104 : int nBand );
105 : netCDFRasterBand( netCDFDataset *poDS,
106 : GDALDataType eType,
107 : int nBand,
108 : int bSigned=TRUE,
109 : char *pszBandName=NULL,
110 : char *pszLongName=NULL );
111 : ~netCDFRasterBand( );
112 :
113 : virtual double GetNoDataValue( int * );
114 : virtual CPLErr SetNoDataValue( double );
115 : virtual double GetOffset( int * );
116 : virtual CPLErr SetOffset( double );
117 : virtual double GetScale( int * );
118 : virtual CPLErr SetScale( double );
119 : virtual CPLErr IReadBlock( int, int, void * );
120 : virtual CPLErr IWriteBlock( int, int, void * );
121 :
122 : };
123 :
124 : /************************************************************************/
125 : /* netCDFRasterBand() */
126 : /************************************************************************/
127 :
128 180 : netCDFRasterBand::netCDFRasterBand( netCDFDataset *poNCDFDS,
129 : int nZId,
130 : int nZDim,
131 : int nLevel,
132 : int *panBandZLev,
133 : int *panBandDimPos,
134 : int *paDimIds,
135 180 : int nBand)
136 :
137 : {
138 180 : double dfNoData = 0.0;
139 180 : int bNoDataSet = FALSE;
140 180 : nc_type vartype=NC_NAT;
141 180 : nc_type atttype=NC_NAT;
142 : size_t attlen;
143 : char szNoValueName[NCDF_MAX_STR_LEN];
144 :
145 180 : this->poDS = poNCDFDS;
146 180 : this->panBandZPos = NULL;
147 180 : this->panBandZLev = NULL;
148 180 : this->nBand = nBand;
149 180 : this->nZId = nZId;
150 180 : this->nZDim = nZDim;
151 180 : this->nLevel = nLevel;
152 180 : this->nBandXPos = panBandDimPos[0];
153 180 : this->nBandYPos = panBandDimPos[1];
154 180 : this->bSignedData = TRUE; //default signed, except for Byte
155 180 : this->cdfid = poNCDFDS->GetCDFID();
156 180 : this->status = NC_NOERR;
157 :
158 : /* -------------------------------------------------------------------- */
159 : /* Take care of all other dimmensions */
160 : /* ------------------------------------------------------------------ */
161 180 : if( nZDim > 2 ) {
162 : this->panBandZPos =
163 24 : (int *) CPLCalloc( nZDim-1, sizeof( int ) );
164 : this->panBandZLev =
165 24 : (int *) CPLCalloc( nZDim-1, sizeof( int ) );
166 :
167 80 : for ( int i=0; i < nZDim - 2; i++ ){
168 56 : this->panBandZPos[i] = panBandDimPos[i+2];
169 56 : this->panBandZLev[i] = panBandZLev[i];
170 : }
171 : }
172 180 : bNoDataSet = FALSE;
173 180 : dfNoDataValue = 0.0;
174 :
175 180 : nRasterXSize = poDS->GetRasterXSize( );
176 180 : nRasterYSize = poDS->GetRasterYSize( );
177 180 : nBlockXSize = poDS->GetRasterXSize( );
178 180 : nBlockYSize = 1;
179 :
180 : /* -------------------------------------------------------------------- */
181 : /* Get the type of the "z" variable, our target raster array. */
182 : /* -------------------------------------------------------------------- */
183 180 : if( nc_inq_var( cdfid, nZId, NULL, &nc_datatype, NULL, NULL,
184 : NULL ) != NC_NOERR ){
185 : CPLError( CE_Failure, CPLE_AppDefined,
186 0 : "Error in nc_var_inq() on 'z'." );
187 0 : return;
188 : }
189 :
190 180 : if( nc_datatype == NC_BYTE )
191 93 : eDataType = GDT_Byte;
192 : #ifdef NETCDF_HAS_NC4
193 : /* NC_UBYTE (unsigned byte) is only available for NC4 */
194 : else if( nc_datatype == NC_UBYTE )
195 : eDataType = GDT_Byte;
196 : #endif
197 87 : else if( nc_datatype == NC_CHAR )
198 0 : eDataType = GDT_Byte;
199 87 : else if( nc_datatype == NC_SHORT )
200 9 : eDataType = GDT_Int16;
201 78 : else if( nc_datatype == NC_INT )
202 9 : eDataType = GDT_Int32;
203 69 : else if( nc_datatype == NC_FLOAT )
204 44 : eDataType = GDT_Float32;
205 25 : else if( nc_datatype == NC_DOUBLE )
206 25 : eDataType = GDT_Float64;
207 : else
208 : {
209 0 : if( nBand == 1 )
210 : CPLError( CE_Warning, CPLE_AppDefined,
211 : "Unsupported netCDF datatype (%d), treat as Float32.",
212 0 : (int) nc_datatype );
213 0 : eDataType = GDT_Float32;
214 : }
215 :
216 : /* -------------------------------------------------------------------- */
217 : /* Find out what is No Data for this variable */
218 : /* -------------------------------------------------------------------- */
219 :
220 : status = nc_inq_att( cdfid, nZId,
221 180 : _FillValue, &atttype, &attlen);
222 :
223 : /* -------------------------------------------------------------------- */
224 : /* Look for either Missing_Value or _FillValue attributes */
225 : /* -------------------------------------------------------------------- */
226 :
227 180 : if( status == NC_NOERR ) {
228 153 : strcpy(szNoValueName, _FillValue );
229 : }
230 : else {
231 : status = nc_inq_att( cdfid, nZId,
232 27 : "missing_value", &atttype, &attlen );
233 27 : if( status == NC_NOERR ) {
234 7 : strcpy( szNoValueName, "missing_value" );
235 : }
236 : }
237 :
238 180 : if( status == NC_NOERR ) {
239 160 : if ( NCDFGetAttr( cdfid, nZId, szNoValueName,
240 : &dfNoData ) == CE_None )
241 160 : bNoDataSet = TRUE;
242 : }
243 :
244 180 : nc_inq_vartype( cdfid, nZId, &vartype );
245 :
246 : /* if not found NoData, set the default one */
247 180 : if ( ! bNoDataSet ) {
248 20 : switch( vartype ) {
249 : case NC_BYTE:
250 : #ifdef NETCDF_HAS_NC4
251 : case NC_UBYTE:
252 : #endif
253 : /* don't do default fill-values for bytes, too risky */
254 0 : dfNoData = 0.0;
255 : /* should print a warning as users might not be expecting this */
256 : /* CPLError(CE_Warning, 1,"GDAL netCDF driver is setting default NoData value to 0.0 for NC_BYTE data\n"); */
257 0 : break;
258 : case NC_CHAR:
259 0 : dfNoData = NC_FILL_CHAR;
260 0 : break;
261 : case NC_SHORT:
262 0 : dfNoData = NC_FILL_SHORT;
263 0 : break;
264 : case NC_INT:
265 0 : dfNoData = NC_FILL_INT;
266 0 : break;
267 : case NC_FLOAT:
268 4 : dfNoData = NC_FILL_FLOAT;
269 4 : break;
270 : case NC_DOUBLE:
271 16 : dfNoData = NC_FILL_DOUBLE;
272 16 : break;
273 : default:
274 0 : dfNoData = 0.0;
275 : break;
276 : }
277 20 : bNoDataSet = TRUE;
278 : }
279 :
280 180 : if ( bNoDataSet )
281 180 : SetNoDataValue( dfNoData );
282 : else
283 0 : CPLDebug( "GDAL_netCDF", "did not get nodata value for variable #%d", nZId );
284 :
285 : /* -------------------------------------------------------------------- */
286 : /* Look for valid_range or valid_min/valid_max */
287 : /* -------------------------------------------------------------------- */
288 : /* set valid_range to nodata, then check for actual values */
289 180 : adfValidRange[0] = dfNoData;
290 180 : adfValidRange[1] = dfNoData;
291 : /* first look for valid_range */
292 180 : int bGotValidRange = FALSE;
293 : status = nc_inq_att( cdfid, nZId,
294 180 : "valid_range", &atttype, &attlen);
295 180 : if( (status == NC_NOERR) && (attlen == 2)) {
296 : int vrange[2];
297 : int vmin, vmax;
298 : status = nc_get_att_int( cdfid, nZId,
299 91 : "valid_range", vrange );
300 91 : if( status == NC_NOERR ) {
301 91 : bGotValidRange = TRUE;
302 91 : adfValidRange[0] = vrange[0];
303 91 : adfValidRange[1] = vrange[1];
304 : }
305 : /* if not found look for valid_min and valid_max */
306 : else {
307 : status = nc_get_att_int( cdfid, nZId,
308 0 : "valid_min", &vmin );
309 0 : if( status == NC_NOERR ) {
310 0 : adfValidRange[0] = vmin;
311 : status = nc_get_att_int( cdfid, nZId,
312 0 : "valid_max", &vmax );
313 0 : if( status == NC_NOERR ) {
314 0 : adfValidRange[1] = vmax;
315 0 : bGotValidRange = TRUE;
316 : }
317 : }
318 : }
319 : }
320 :
321 : CPLDebug( "GDAL_netCDF", "valid_range={%f,%f}",
322 180 : adfValidRange[0], adfValidRange[1] );
323 :
324 : /* -------------------------------------------------------------------- */
325 : /* Special For Byte Bands: check for signed/unsigned byte */
326 : /* -------------------------------------------------------------------- */
327 180 : if ( nc_datatype == NC_BYTE ) {
328 :
329 : /* netcdf uses signed byte by default, but GDAL uses unsigned by default */
330 : /* This may cause unexpected results, but is needed for back-compat */
331 93 : if ( poNCDFDS->bIsGdalFile )
332 91 : this->bSignedData = FALSE;
333 : else
334 2 : this->bSignedData = TRUE;
335 :
336 : /* For NC4 format NC_BYTE is signed, NC_UBYTE is unsigned */
337 93 : if ( poNCDFDS->nFormat == NCDF_FORMAT_NC4 ) {
338 0 : this->bSignedData = TRUE;
339 : }
340 : else {
341 : /* if we got valid_range, test for signed/unsigned range */
342 : /* http://www.unidata.ucar.edu/software/netcdf/docs/netcdf/Attribute-Conventions.html */
343 93 : if ( bGotValidRange == TRUE ) {
344 : /* If we got valid_range={0,255}, treat as unsigned */
345 175 : if ( (adfValidRange[0] == 0) && (adfValidRange[1] == 255) ) {
346 84 : bSignedData = FALSE;
347 : /* reset valid_range */
348 84 : adfValidRange[0] = dfNoData;
349 84 : adfValidRange[1] = dfNoData;
350 : }
351 : /* If we got valid_range={-128,127}, treat as signed */
352 7 : else if ( (adfValidRange[0] == -128) && (adfValidRange[1] == 127) ) {
353 7 : bSignedData = TRUE;
354 : /* reset valid_range */
355 7 : adfValidRange[0] = dfNoData;
356 7 : adfValidRange[1] = dfNoData;
357 : }
358 : }
359 : /* else test for _Unsigned */
360 : /* http://www.unidata.ucar.edu/software/netcdf/docs/BestPractices.html */
361 : else {
362 2 : char *pszTemp = NULL;
363 2 : if ( NCDFGetAttr( cdfid, nZId, "_Unsigned", &pszTemp )
364 :
365 : == CE_None ) {
366 2 : if ( EQUAL(pszTemp,"true"))
367 2 : bSignedData = FALSE;
368 0 : else if ( EQUAL(pszTemp,"false"))
369 0 : bSignedData = TRUE;
370 2 : CPLFree( pszTemp );
371 : }
372 : }
373 : }
374 :
375 93 : if ( bSignedData )
376 : {
377 : /* set PIXELTYPE=SIGNEDBYTE */
378 : /* See http://trac.osgeo.org/gdal/wiki/rfc14_imagestructure */
379 7 : SetMetadataItem( "PIXELTYPE", "SIGNEDBYTE", "IMAGE_STRUCTURE" );
380 7 : CPLDebug( "GDAL_netCDF", "got signed Byte" );
381 : }
382 : else
383 86 : CPLDebug( "GDAL_netCDF", "got unsigned Byte" );
384 :
385 : }
386 :
387 : /* -------------------------------------------------------------------- */
388 : /* Create Band Metadata */
389 : /* -------------------------------------------------------------------- */
390 180 : CreateBandMetadata( paDimIds );
391 :
392 : /* -------------------------------------------------------------------- */
393 : /* Attempt to fetch the scale_factor and add_offset attributes for the */
394 : /* variable and set them. If these values are not available, set */
395 : /* offset to 0 and scale to 1 */
396 : /* -------------------------------------------------------------------- */
397 180 : double dfOff = 0.0;
398 180 : double dfScale = 1.0;
399 :
400 180 : if ( nc_inq_attid ( cdfid, nZId, CF_ADD_OFFSET, NULL) == NC_NOERR ) {
401 3 : status = nc_get_att_double( cdfid, nZId, CF_ADD_OFFSET, &dfOff );
402 3 : CPLDebug( "GDAL_netCDF", "got add_offset=%.16g, status=%d", dfOff, status );
403 : }
404 180 : if ( nc_inq_attid ( cdfid, nZId,
405 : CF_SCALE_FACTOR, NULL) == NC_NOERR ) {
406 3 : status = nc_get_att_double( cdfid, nZId, CF_SCALE_FACTOR, &dfScale );
407 3 : CPLDebug( "GDAL_netCDF", "got scale_factor=%.16g, status=%d", dfScale, status );
408 : }
409 180 : SetOffset( dfOff );
410 180 : SetScale( dfScale );
411 0 : }
412 :
413 : /* constructor in create mode, assume just 2 dimensions for now */
414 136 : netCDFRasterBand::netCDFRasterBand( netCDFDataset *poNCDFDS,
415 : GDALDataType eType,
416 : int nBand,
417 : int bSigned,
418 : char *pszBandName,
419 136 : char *pszLongName )
420 :
421 : {
422 : int anBandDims[ NC_MAX_DIMS ];
423 : int status;
424 :
425 136 : double dfNoData = 0.0;
426 : char szTemp[NCDF_MAX_STR_LEN];
427 :
428 136 : this->poDS = poNCDFDS;
429 136 : this->nBand = nBand;
430 136 : this->nZId = -1;
431 136 : this->nZDim = 2;
432 136 : this->nLevel = 0;
433 136 : this->panBandZPos = NULL;
434 136 : this->panBandZLev = NULL;
435 136 : this->nBandXPos = 1;
436 136 : this->nBandYPos = 0;
437 136 : this->bSignedData = bSigned;
438 :
439 136 : this->status = NC_NOERR;
440 136 : this->cdfid = poNCDFDS->GetCDFID();
441 :
442 136 : bNoDataSet = FALSE;
443 136 : dfNoDataValue = 0.0;
444 :
445 136 : nRasterXSize = poDS->GetRasterXSize( );
446 136 : nRasterYSize = poDS->GetRasterYSize( );
447 136 : nBlockXSize = poDS->GetRasterXSize( );
448 136 : nBlockYSize = 1;
449 :
450 136 : if ( poDS->GetAccess() != GA_Update ) {
451 : CPLError( CE_Failure, CPLE_NotSupported,
452 0 : "Dataset is not in update mode, wrong netCDFRasterBand constructor" );
453 0 : return;
454 : }
455 :
456 : /* -------------------------------------------------------------------- */
457 : /* Take care of all other dimmensions */
458 : /* ------------------------------------------------------------------ */
459 : // if( nZDim > 2 ) {
460 : // this->panBandZPos =
461 : // (int *) CPLCalloc( nZDim-1, sizeof( int ) );
462 : // this->panBandZLev =
463 : // (int *) CPLCalloc( nZDim-1, sizeof( int ) );
464 :
465 : // for ( int i=0; i < nZDim - 2; i++ ){
466 : // this->panBandZPos[i] = panBandDimPos[i+2];
467 : // this->panBandZLev[i] = panBandZLev[i];
468 : // }
469 : // }
470 :
471 : /* -------------------------------------------------------------------- */
472 : /* Get the type of the "z" variable, our target raster array. */
473 : /* -------------------------------------------------------------------- */
474 136 : eDataType = eType;
475 :
476 136 : switch ( eDataType )
477 : {
478 : case GDT_Byte:
479 73 : nc_datatype = NC_BYTE;
480 : #ifdef NETCDF_HAS_NC4
481 : /* NC_UBYTE (unsigned byte) is only available for NC4 */
482 : if ( ! bSignedData && (poNCDFDS->nFormat == NCDF_FORMAT_NC4) )
483 : nc_datatype = NC_UBYTE;
484 : #endif
485 73 : break;
486 : case GDT_Int16:
487 8 : nc_datatype = NC_SHORT;
488 8 : break;
489 : case GDT_Int32:
490 8 : nc_datatype = NC_INT;
491 8 : break;
492 : case GDT_Float32:
493 13 : nc_datatype = NC_FLOAT;
494 13 : break;
495 : case GDT_Float64:
496 8 : nc_datatype = NC_DOUBLE;
497 8 : break;
498 : default:
499 26 : if( nBand == 1 )
500 : CPLError( CE_Warning, CPLE_AppDefined,
501 : "Unsupported GDAL datatype (%d), treat as NC_FLOAT.",
502 14 : (int) eDataType );
503 26 : nc_datatype = NC_FLOAT;
504 : break;
505 : }
506 :
507 : /* -------------------------------------------------------------------- */
508 : /* Define the variable */
509 : /* -------------------------------------------------------------------- */
510 136 : anBandDims[0] = poNCDFDS->nYDimID;
511 136 : anBandDims[1] = poNCDFDS->nXDimID;
512 :
513 255 : if ( !pszBandName || EQUAL(pszBandName,"") )
514 119 : sprintf( szTemp, "Band%d", nBand );
515 : else
516 17 : strcpy( szTemp, pszBandName );
517 : CPLDebug( "GDAL_netCDF", "nc_def_var(%d,%s,%d)",
518 136 : cdfid, szTemp, nc_datatype );
519 : status = nc_def_var( cdfid, szTemp, nc_datatype,
520 136 : NCDF_NBDIM, anBandDims, &nZId );
521 136 : NCDF_ERR(status);
522 :
523 256 : if ( !pszLongName || EQUAL(pszLongName,"") )
524 120 : sprintf( szTemp, "GDAL Band Number %d", nBand );
525 : else
526 16 : strcpy( szTemp, pszLongName );
527 : status = nc_put_att_text( cdfid, nZId, CF_LNG_NAME,
528 136 : strlen( szTemp ), szTemp );
529 136 : NCDF_ERR(status);
530 :
531 :
532 136 : poNCDFDS->DefVarDeflate(nZId, TRUE);
533 :
534 : /* for Byte data add signed/unsigned info */
535 136 : if ( eDataType == GDT_Byte ) {
536 :
537 73 : CPLDebug( "GDAL_netCDF", "adding valid_range attributes for Byte Band" );
538 : /* For unsigned NC_BYTE (except NC4 format) */
539 : /* add valid_range and _Unsigned ( defined in CF-1 and NUG ) */
540 73 : if ( (nc_datatype == NC_BYTE) && (poNCDFDS->nFormat != NCDF_FORMAT_NC4) ) {
541 : short int adfValidRange[2];
542 73 : if ( bSignedData ) {
543 3 : adfValidRange[0] = -128;
544 3 : adfValidRange[1] = 127;
545 : status = nc_put_att_text( cdfid,nZId,
546 3 : "_Unsigned", 5, "false" );
547 : }
548 : else {
549 70 : adfValidRange[0] = 0;
550 70 : adfValidRange[1] = 255;
551 : status = nc_put_att_text( cdfid,nZId,
552 70 : "_Unsigned", 4, "true" );
553 : }
554 : status=nc_put_att_short( cdfid,nZId, "valid_range",
555 73 : NC_SHORT, 2, adfValidRange );
556 : }
557 : /* for unsigned byte set PIXELTYPE=SIGNEDBYTE */
558 : /* See http://trac.osgeo.org/gdal/wiki/rfc14_imagestructure */
559 73 : if ( bSignedData )
560 3 : SetMetadataItem( "PIXELTYPE", "SIGNEDBYTE", "IMAGE_STRUCTURE" );
561 :
562 : }
563 :
564 : /* set default nodata */
565 136 : switch( nc_datatype ) {
566 : case NC_BYTE:
567 : #ifdef NETCDF_HAS_NC4
568 : case NC_UBYTE:
569 : #endif
570 : /* don't do default fill-values for bytes, too risky */
571 73 : dfNoData = 0.0;
572 : /* should print a warning as users might not be expecting this */
573 : /* CPLError(CE_Warning, 1,"GDAL netCDF driver is setting default NoData value to 0.0 for NC_BYTE data\n"); */
574 73 : break;
575 : case NC_CHAR:
576 0 : dfNoData = NC_FILL_CHAR;
577 0 : break;
578 : case NC_SHORT:
579 8 : dfNoData = NC_FILL_SHORT;
580 8 : break;
581 : case NC_INT:
582 8 : dfNoData = NC_FILL_INT;
583 8 : break;
584 : case NC_FLOAT:
585 39 : dfNoData = NC_FILL_FLOAT;
586 39 : break;
587 : case NC_DOUBLE:
588 8 : dfNoData = NC_FILL_DOUBLE;
589 8 : break;
590 : default:
591 0 : dfNoData = 0.0;
592 : break;
593 : }
594 :
595 136 : SetNoDataValue( dfNoData );
596 :
597 0 : }
598 :
599 : /************************************************************************/
600 : /* ~netCDFRasterBand() */
601 : /************************************************************************/
602 :
603 316 : netCDFRasterBand::~netCDFRasterBand()
604 : {
605 316 : FlushCache();
606 316 : if( panBandZPos )
607 24 : CPLFree( panBandZPos );
608 316 : if( panBandZLev )
609 24 : CPLFree( panBandZLev );
610 316 : }
611 :
612 : /************************************************************************/
613 : /* GetOffset() */
614 : /************************************************************************/
615 21 : double netCDFRasterBand::GetOffset( int *pbSuccess )
616 : {
617 21 : if( pbSuccess != NULL )
618 21 : *pbSuccess = TRUE;
619 :
620 21 : return dfOffset;
621 : }
622 :
623 : /************************************************************************/
624 : /* SetOffset() */
625 : /************************************************************************/
626 180 : CPLErr netCDFRasterBand::SetOffset( double dfNewOffset )
627 : {
628 180 : dfOffset = dfNewOffset;
629 :
630 : /* write value if in update mode */
631 180 : if ( poDS->GetAccess() == GA_Update ) {
632 :
633 : /* make sure we are in define mode */
634 0 : ( ( netCDFDataset * ) poDS )->SetDefineMode( TRUE );
635 :
636 : status = nc_put_att_double( cdfid, nZId, CF_ADD_OFFSET,
637 0 : NC_DOUBLE, 1, &dfOffset );
638 :
639 0 : NCDF_ERR(status);
640 0 : if ( status == NC_NOERR )
641 0 : return CE_None;
642 : else
643 0 : return CE_Failure;
644 :
645 : }
646 :
647 180 : return CE_None;
648 : }
649 :
650 : /************************************************************************/
651 : /* GetScale() */
652 : /************************************************************************/
653 21 : double netCDFRasterBand::GetScale( int *pbSuccess )
654 : {
655 21 : if( pbSuccess != NULL )
656 21 : *pbSuccess = TRUE;
657 21 : return dfScale;
658 : }
659 :
660 : /************************************************************************/
661 : /* SetScale() */
662 : /************************************************************************/
663 180 : CPLErr netCDFRasterBand::SetScale( double dfNewScale )
664 : {
665 180 : dfScale = dfNewScale;
666 :
667 : /* write value if in update mode */
668 180 : if ( poDS->GetAccess() == GA_Update ) {
669 :
670 : /* make sure we are in define mode */
671 0 : ( ( netCDFDataset * ) poDS )->SetDefineMode( TRUE );
672 :
673 : status = nc_put_att_double( cdfid, nZId, CF_SCALE_FACTOR,
674 0 : NC_DOUBLE, 1, &dfScale );
675 :
676 0 : NCDF_ERR(status);
677 0 : if ( status == NC_NOERR )
678 0 : return CE_None;
679 : else
680 0 : return CE_Failure;
681 :
682 : }
683 :
684 180 : return CE_None;
685 : }
686 :
687 : /************************************************************************/
688 : /* GetNoDataValue() */
689 : /************************************************************************/
690 :
691 121 : double netCDFRasterBand::GetNoDataValue( int * pbSuccess )
692 :
693 : {
694 121 : if( pbSuccess )
695 105 : *pbSuccess = bNoDataSet;
696 :
697 121 : if( bNoDataSet )
698 121 : return dfNoDataValue;
699 : else
700 0 : return GDALPamRasterBand::GetNoDataValue( pbSuccess );
701 : }
702 :
703 : /************************************************************************/
704 : /* SetNoDataValue() */
705 : /************************************************************************/
706 :
707 389 : CPLErr netCDFRasterBand::SetNoDataValue( double dfNoData )
708 :
709 : {
710 389 : bNoDataSet = TRUE;
711 389 : dfNoDataValue = dfNoData;
712 :
713 : /* write value if in update mode */
714 389 : if ( poDS->GetAccess() == GA_Update ) {
715 :
716 : /* make sure we are in define mode */
717 209 : ( ( netCDFDataset * ) poDS )->SetDefineMode( TRUE );
718 :
719 209 : if ( eDataType == GDT_Byte) {
720 127 : if ( bSignedData ) {
721 5 : signed char cNoDataValue = (signed char) dfNoData;
722 : status = nc_put_att_schar( cdfid, nZId, _FillValue,
723 5 : nc_datatype, 1, &cNoDataValue );
724 : }
725 : else {
726 122 : unsigned char ucNoDataValue = (unsigned char) dfNoDataValue;
727 : status = nc_put_att_uchar( cdfid, nZId, _FillValue,
728 122 : nc_datatype, 1, &ucNoDataValue );
729 : }
730 : }
731 82 : else if ( eDataType == GDT_Int16 ) {
732 11 : short int nsNoDataValue = (short int) dfNoDataValue;
733 : status = nc_put_att_short( cdfid, nZId, _FillValue,
734 11 : nc_datatype, 1, &nsNoDataValue );
735 : }
736 71 : else if ( eDataType == GDT_Int32) {
737 11 : int nNoDataValue = (int) dfNoDataValue;
738 : status = nc_put_att_int( cdfid, nZId, _FillValue,
739 11 : nc_datatype, 1, &nNoDataValue );
740 : }
741 60 : else if ( eDataType == GDT_Float32) {
742 21 : float fNoDataValue = (float) dfNoDataValue;
743 : status = nc_put_att_float( cdfid, nZId, _FillValue,
744 21 : nc_datatype, 1, &fNoDataValue );
745 : }
746 : else
747 : status = nc_put_att_double( cdfid, nZId, _FillValue,
748 39 : nc_datatype, 1, &dfNoDataValue );
749 :
750 209 : NCDF_ERR(status);
751 209 : if ( status == NC_NOERR )
752 209 : return CE_None;
753 : else
754 0 : return CE_Failure;
755 :
756 : }
757 180 : return CE_None;
758 : }
759 :
760 : /************************************************************************/
761 : /* CreateBandMetadata() */
762 : /************************************************************************/
763 :
764 180 : CPLErr netCDFRasterBand::CreateBandMetadata( int *paDimIds )
765 :
766 : {
767 : char szVarName[NC_MAX_NAME];
768 : char szMetaName[NC_MAX_NAME];
769 : char szMetaTemp[NCDF_MAX_STR_LEN];
770 180 : char *pszMetaValue = NULL;
771 : char szTemp[NC_MAX_NAME];
772 : const char *pszValue;
773 :
774 : int nd;
775 : int i,j;
776 180 : int Sum = 1;
777 180 : int Taken = 0;
778 180 : int result = 0;
779 : int status;
780 180 : int nVarID = -1;
781 : int nDims;
782 : size_t start[1];
783 : size_t count[1];
784 180 : nc_type nVarType = NC_NAT;
785 180 : int nAtt=0;
786 :
787 180 : netCDFDataset *poDS = (netCDFDataset *) this->poDS;
788 :
789 : /* -------------------------------------------------------------------- */
790 : /* Compute all dimensions from Band number and save in Metadata */
791 : /* -------------------------------------------------------------------- */
792 180 : nc_inq_varname( cdfid, nZId, szVarName );
793 180 : nc_inq_varndims( cdfid, nZId, &nd );
794 : /* -------------------------------------------------------------------- */
795 : /* Compute multidimention band position */
796 : /* */
797 : /* BandPosition = (Total - sum(PastBandLevels) - 1)/sum(remainingLevels)*/
798 : /* if Data[2,3,4,x,y] */
799 : /* */
800 : /* BandPos0 = (nBand ) / (3*4) */
801 : /* BandPos1 = (nBand - BandPos0*(3*4) ) / (4) */
802 : /* BandPos2 = (nBand - BandPos0*(3*4) ) % (4) */
803 : /* -------------------------------------------------------------------- */
804 :
805 180 : sprintf( szMetaName,"NETCDF_VARNAME");
806 180 : sprintf( szMetaTemp,"%s",szVarName);
807 180 : SetMetadataItem( szMetaName, szMetaTemp );
808 180 : if( nd == 3 ) {
809 8 : Sum *= panBandZLev[0];
810 : }
811 :
812 236 : for( i=0; i < nd-2 ; i++ ) {
813 56 : if( i != nd - 2 -1 ) {
814 32 : Sum = 1;
815 80 : for( j=i+1; j < nd-2; j++ ) {
816 48 : Sum *= panBandZLev[j];
817 : }
818 32 : result = (int) ( ( nLevel-Taken) / Sum );
819 : }
820 : else {
821 24 : result = (int) ( ( nLevel-Taken) % Sum );
822 : }
823 :
824 : strcpy(szVarName,
825 56 : poDS->papszDimName[paDimIds[panBandZPos[i]]] );
826 :
827 56 : sprintf( szMetaName,"NETCDF_DIMENSION_%s", szVarName );
828 :
829 56 : status=nc_inq_varid( cdfid, szVarName, &nVarID );
830 :
831 : /* -------------------------------------------------------------------- */
832 : /* Try to uppercase the first letter of the variable */
833 : /* -------------------------------------------------------------------- */
834 :
835 56 : if( status != NC_NOERR ) {
836 48 : szVarName[0]=(char) toupper(szVarName[0]);
837 48 : status=nc_inq_varid( cdfid, szVarName, &nVarID );
838 : }
839 :
840 56 : status = nc_inq_vartype( cdfid, nVarID, &nVarType );
841 :
842 56 : nDims = 0;
843 56 : status = nc_inq_varndims( cdfid, nVarID, &nDims );
844 :
845 56 : if( nDims == 1 ) {
846 8 : count[0]=1;
847 8 : start[0]=result;
848 8 : switch( nVarType ) {
849 : case NC_SHORT:
850 : short sData;
851 : status = nc_get_vara_short( cdfid, nVarID,
852 : start,
853 0 : count, &sData );
854 0 : sprintf( szMetaTemp,"%d", sData );
855 0 : break;
856 : case NC_INT:
857 : int nData;
858 : status = nc_get_vara_int( cdfid, nVarID,
859 : start,
860 0 : count, &nData );
861 0 : sprintf( szMetaTemp,"%d", nData );
862 0 : break;
863 : case NC_FLOAT:
864 : float fData;
865 : status = nc_get_vara_float( cdfid, nVarID,
866 : start,
867 0 : count, &fData );
868 0 : sprintf( szMetaTemp,"%.8g", fData );
869 0 : break;
870 : case NC_DOUBLE:
871 : double dfData;
872 : status = nc_get_vara_double( cdfid, nVarID,
873 : start,
874 8 : count, &dfData);
875 8 : sprintf( szMetaTemp,"%.16g", dfData );
876 8 : break;
877 : default:
878 : CPLDebug( "GDAL_netCDF", "invalid dim %s, type=%d",
879 0 : szMetaTemp, nVarType);
880 : break;
881 : }
882 : }
883 : else
884 48 : sprintf( szMetaTemp,"%d", result+1);
885 :
886 : CPLDebug( "GDAL_netCDF", "setting dimension metadata %s=%s",
887 56 : szMetaName, szMetaTemp );
888 :
889 56 : SetMetadataItem( szMetaName, szMetaTemp );
890 :
891 : /* -------------------------------------------------------------------- */
892 : /* Fetch dimension units */
893 : /* -------------------------------------------------------------------- */
894 :
895 56 : strcpy( szTemp, szVarName );
896 56 : strcat( szTemp, "#units" );
897 56 : pszValue = CSLFetchNameValue(poDS->papszMetadata, szTemp);
898 56 : if( pszValue != NULL ) {
899 8 : if( EQUAL( pszValue, "T") ) {
900 0 : strcpy( szTemp, szVarName );
901 0 : strcat( szTemp, "#original_units" );
902 0 : pszValue = CSLFetchNameValue(poDS->papszMetadata, szTemp);
903 0 : strcpy( szTemp, "NETCDF_");
904 0 : strcat( szTemp, szVarName );
905 0 : strcat( szTemp, "_original_units" );
906 0 : SetMetadataItem( szTemp, pszValue );
907 : }
908 : else {
909 8 : strcpy( szTemp, "NETCDF_");
910 8 : strcat( szTemp, szVarName );
911 8 : strcat( szTemp, "_units" );
912 8 : SetMetadataItem( szTemp, pszValue );
913 : }
914 : }
915 56 : Taken += result * Sum;
916 : }
917 :
918 : /* -------------------------------------------------------------------- */
919 : /* Get all other metadata */
920 : /* -------------------------------------------------------------------- */
921 180 : nc_inq_varnatts( cdfid, nZId, &nAtt );
922 :
923 995 : for( i=0; i < nAtt ; i++ ) {
924 :
925 815 : status = nc_inq_attname( cdfid, nZId, i, szTemp);
926 : // if(strcmp(szTemp,_FillValue) ==0) continue;
927 815 : sprintf( szMetaName,"%s",szTemp);
928 :
929 815 : if ( NCDFGetAttr( cdfid, nZId, szMetaName, &pszMetaValue)
930 : == CE_None ) {
931 815 : SetMetadataItem( szMetaName, pszMetaValue );
932 : }
933 : else {
934 0 : CPLDebug( "GDAL_netCDF", "invalid Band metadata %s", szMetaName );
935 : }
936 :
937 815 : if ( pszMetaValue ) {
938 815 : CPLFree( pszMetaValue );
939 815 : pszMetaValue = NULL;
940 : }
941 :
942 : }
943 :
944 180 : return CE_None;
945 : }
946 :
947 : /************************************************************************/
948 : /* CheckValidData() */
949 : /************************************************************************/
950 : template <class T>
951 5804 : void netCDFRasterBand::CheckValidData ( void * pImage, int bCheckIsNan )
952 : {
953 : int i;
954 5804 : CPLAssert( pImage != NULL );
955 :
956 : /* check if needed or requested */
957 5804 : if ( (adfValidRange[0] != dfNoDataValue) ||
958 : (adfValidRange[1] != dfNoDataValue) ||
959 : bCheckIsNan ) {
960 :
961 44038 : for( i=0; i<nBlockXSize; i++ ) {
962 : /* check for nodata and nan */
963 43193 : if ( CPLIsEqual( (double) ((T *)pImage)[i], dfNoDataValue ) )
964 0 : continue;
965 43193 : if( bCheckIsNan && CPLIsNan( ( (T *) pImage)[i] ) ) {
966 0 : ( (T *)pImage )[i] = (T)dfNoDataValue;
967 0 : continue;
968 : }
969 : /* check for valid_range */
970 43193 : if ( ( ( adfValidRange[0] != dfNoDataValue ) &&
971 : ( ((T *)pImage)[i] < (T)adfValidRange[0] ) )
972 : ||
973 : ( ( adfValidRange[1] != dfNoDataValue ) &&
974 : ( ((T *)pImage)[i] > (T)adfValidRange[1] ) ) ) {
975 0 : ( (T *)pImage )[i] = (T)dfNoDataValue;
976 : }
977 : }
978 :
979 : }
980 :
981 5804 : }
982 :
983 : /************************************************************************/
984 : /* IReadBlock() */
985 : /************************************************************************/
986 :
987 5804 : CPLErr netCDFRasterBand::IReadBlock( int nBlockXOff, int nBlockYOff,
988 : void * pImage )
989 :
990 : {
991 : size_t start[ MAX_NC_DIMS ];
992 : size_t edge[ MAX_NC_DIMS ];
993 : char pszName[ NCDF_MAX_STR_LEN ];
994 : int i,j;
995 5804 : int Sum=-1;
996 5804 : int Taken=-1;
997 : int nd;
998 :
999 5804 : *pszName='\0';
1000 5804 : memset( start, 0, sizeof( start ) );
1001 5804 : memset( edge, 0, sizeof( edge ) );
1002 5804 : nc_inq_varndims ( cdfid, nZId, &nd );
1003 :
1004 : /* -------------------------------------------------------------------- */
1005 : /* Locate X, Y and Z position in the array */
1006 : /* -------------------------------------------------------------------- */
1007 :
1008 5804 : start[nBandXPos] = 0; // x dim can move arround in array
1009 : // check y order
1010 5804 : if( ( ( netCDFDataset *) poDS )->bBottomUp ) {
1011 184 : start[nBandYPos] = nRasterYSize - 1 - nBlockYOff;
1012 : } else {
1013 5620 : start[nBandYPos] = nBlockYOff; // y
1014 : }
1015 :
1016 5804 : edge[nBandXPos] = nBlockXSize;
1017 5804 : edge[nBandYPos] = 1;
1018 :
1019 5804 : if( nd == 3 ) {
1020 80 : start[panBandZPos[0]] = nLevel; // z
1021 80 : edge [panBandZPos[0]] = 1;
1022 : }
1023 :
1024 : /* -------------------------------------------------------------------- */
1025 : /* Compute multidimention band position */
1026 : /* */
1027 : /* BandPosition = (Total - sum(PastBandLevels) - 1)/sum(remainingLevels)*/
1028 : /* if Data[2,3,4,x,y] */
1029 : /* */
1030 : /* BandPos0 = (nBand ) / (3*4) */
1031 : /* BandPos1 = (nBand - (3*4) ) / (4) */
1032 : /* BandPos2 = (nBand - (3*4) ) % (4) */
1033 : /* -------------------------------------------------------------------- */
1034 5804 : if (nd > 3)
1035 : {
1036 0 : Taken = 0;
1037 0 : for( i=0; i < nd-2 ; i++ )
1038 : {
1039 0 : if( i != nd - 2 -1 ) {
1040 0 : Sum = 1;
1041 0 : for( j=i+1; j < nd-2; j++ ) {
1042 0 : Sum *= panBandZLev[j];
1043 : }
1044 0 : start[panBandZPos[i]] = (int) ( ( nLevel-Taken) / Sum );
1045 0 : edge[panBandZPos[i]] = 1;
1046 : } else {
1047 0 : start[panBandZPos[i]] = (int) ( ( nLevel-Taken) % Sum );
1048 0 : edge[panBandZPos[i]] = 1;
1049 : }
1050 0 : Taken += start[panBandZPos[i]] * Sum;
1051 : }
1052 : }
1053 :
1054 5804 : if( eDataType == GDT_Byte )
1055 : {
1056 4839 : if (this->bSignedData)
1057 : {
1058 : status = nc_get_vara_schar( cdfid, nZId, start, edge,
1059 60 : (signed char *) pImage );
1060 60 : if ( status == NC_NOERR ) CheckValidData<signed char>( pImage );
1061 : }
1062 : else {
1063 : status = nc_get_vara_uchar( cdfid, nZId, start, edge,
1064 4779 : (unsigned char *) pImage );
1065 4779 : if ( status == NC_NOERR ) CheckValidData<unsigned char>( pImage );
1066 : }
1067 : }
1068 965 : else if( eDataType == GDT_Int16 )
1069 : {
1070 : status = nc_get_vara_short( cdfid, nZId, start, edge,
1071 60 : (short int *) pImage );
1072 60 : if ( status == NC_NOERR ) CheckValidData<short int>( pImage );
1073 : }
1074 905 : else if( eDataType == GDT_Int32 )
1075 : {
1076 : if( sizeof(long) == 4 )
1077 : {
1078 : status = nc_get_vara_long( cdfid, nZId, start, edge,
1079 : (long *) pImage );
1080 : if ( status == NC_NOERR ) CheckValidData<long>( pImage );
1081 : }
1082 : else
1083 : {
1084 : status = nc_get_vara_int( cdfid, nZId, start, edge,
1085 60 : (int *) pImage );
1086 60 : if ( status == NC_NOERR ) CheckValidData<int>( pImage );
1087 : }
1088 : }
1089 845 : else if( eDataType == GDT_Float32 )
1090 : {
1091 : status = nc_get_vara_float( cdfid, nZId, start, edge,
1092 785 : (float *) pImage );
1093 785 : if ( status == NC_NOERR ) CheckValidData<float>( pImage, TRUE );
1094 : }
1095 60 : else if( eDataType == GDT_Float64 )
1096 : {
1097 : status = nc_get_vara_double( cdfid, nZId, start, edge,
1098 60 : (double *) pImage );
1099 60 : if ( status == NC_NOERR ) CheckValidData<double>( pImage, TRUE );
1100 : }
1101 : else
1102 0 : status = NC_EBADTYPE;
1103 :
1104 5804 : if( status != NC_NOERR )
1105 : {
1106 : CPLError( CE_Failure, CPLE_AppDefined,
1107 : "netCDF scanline fetch failed: %s",
1108 0 : nc_strerror( status ) );
1109 0 : return CE_Failure;
1110 : }
1111 : else
1112 5804 : return CE_None;
1113 : }
1114 :
1115 : /************************************************************************/
1116 : /* IWriteBlock() */
1117 : /************************************************************************/
1118 :
1119 3478 : CPLErr netCDFRasterBand::IWriteBlock( int nBlockXOff, int nBlockYOff,
1120 : void * pImage )
1121 :
1122 : {
1123 3478 : int status=-1;//, status2=-1;
1124 : // int cdfid = ( ( netCDFDataset * ) poDS )->cdfid;
1125 : size_t start[ NCDF_NBDIM ];
1126 : size_t edge[ MAX_NC_DIMS ];
1127 : char pszName[ NCDF_MAX_STR_LEN ];
1128 : int i,j;
1129 3478 : int Sum=-1;
1130 3478 : int Taken=-1;
1131 : int nd;
1132 :
1133 3478 : *pszName='\0';
1134 3478 : memset( start, 0, sizeof( start ) );
1135 3478 : memset( edge, 0, sizeof( edge ) );
1136 : // nc_inq_varndims ( cdfid, nZId, &nd );
1137 3478 : nd=2;
1138 :
1139 : /* -------------------------------------------------------------------- */
1140 : /* Locate X, Y and Z position in the array */
1141 : /* -------------------------------------------------------------------- */
1142 :
1143 3478 : start[nBandXPos] = 0; // x dim can move arround in array
1144 : // check y order
1145 3478 : if( ( ( netCDFDataset *) poDS )->bBottomUp ) {
1146 0 : start[nBandYPos] = nRasterYSize - 1 - nBlockYOff;
1147 : } else {
1148 3478 : start[nBandYPos] = nBlockYOff; // y
1149 : }
1150 :
1151 3478 : edge[nBandXPos] = nBlockXSize;
1152 3478 : edge[nBandYPos] = 1;
1153 :
1154 3478 : if( nd == 3 ) {
1155 0 : start[panBandZPos[0]] = nLevel; // z
1156 0 : edge [panBandZPos[0]] = 1;
1157 : }
1158 :
1159 : /* -------------------------------------------------------------------- */
1160 : /* Compute multidimention band position */
1161 : /* */
1162 : /* BandPosition = (Total - sum(PastBandLevels) - 1)/sum(remainingLevels)*/
1163 : /* if Data[2,3,4,x,y] */
1164 : /* */
1165 : /* BandPos0 = (nBand ) / (3*4) */
1166 : /* BandPos1 = (nBand - (3*4) ) / (4) */
1167 : /* BandPos2 = (nBand - (3*4) ) % (4) */
1168 : /* -------------------------------------------------------------------- */
1169 3478 : if (nd > 3)
1170 : {
1171 0 : Taken = 0;
1172 0 : for( i=0; i < nd-2 ; i++ )
1173 : {
1174 0 : if( i != nd - 2 -1 ) {
1175 0 : Sum = 1;
1176 0 : for( j=i+1; j < nd-2; j++ ) {
1177 0 : Sum *= panBandZLev[j];
1178 : }
1179 0 : start[panBandZPos[i]] = (int) ( ( nLevel-Taken) / Sum );
1180 0 : edge[panBandZPos[i]] = 1;
1181 : } else {
1182 0 : start[panBandZPos[i]] = (int) ( ( nLevel-Taken) % Sum );
1183 0 : edge[panBandZPos[i]] = 1;
1184 : }
1185 0 : Taken += start[panBandZPos[i]] * Sum;
1186 : }
1187 : }
1188 :
1189 : /* make sure we are in data mode */
1190 3478 : ( ( netCDFDataset * ) poDS )->SetDefineMode( FALSE );
1191 :
1192 : /* copy data according to type */
1193 3478 : if( eDataType == GDT_Byte ) {
1194 3067 : if ( this->bSignedData )
1195 : status = nc_put_vara_schar( cdfid, nZId, start, edge,
1196 40 : (signed char*) pImage);
1197 : else
1198 : status = nc_put_vara_uchar( cdfid, nZId, start, edge,
1199 3027 : (unsigned char*) pImage);
1200 : }
1201 471 : else if( ( eDataType == GDT_UInt16 ) || ( eDataType == GDT_Int16 ) ) {
1202 : status = nc_put_vara_short( cdfid, nZId, start, edge,
1203 60 : (short int *) pImage);
1204 : }
1205 351 : else if( eDataType == GDT_Int32 ) {
1206 : status = nc_put_vara_int( cdfid, nZId, start, edge,
1207 50 : (int *) pImage);
1208 : }
1209 301 : else if( eDataType == GDT_Float32 ) {
1210 : status = nc_put_vara_float( cdfid, nZId, start, edge,
1211 250 : (float *) pImage);
1212 : }
1213 51 : else if( eDataType == GDT_Float64 ) {
1214 : status = nc_put_vara_double( cdfid, nZId, start, edge,
1215 50 : (double *) pImage);
1216 : }
1217 : else {
1218 : CPLError( CE_Failure, CPLE_NotSupported,
1219 : "The NetCDF driver does not support GDAL data type %d",
1220 1 : eDataType );
1221 1 : status = NC_EBADTYPE;
1222 : }
1223 3478 : NCDF_ERR(status);
1224 :
1225 3478 : if( status != NC_NOERR )
1226 : {
1227 : CPLError( CE_Failure, CPLE_AppDefined,
1228 : "netCDF scanline write failed: %s",
1229 1 : nc_strerror( status ) );
1230 1 : return CE_Failure;
1231 : }
1232 : else
1233 3477 : return CE_None;
1234 :
1235 : }
1236 :
1237 : /************************************************************************/
1238 : /* ==================================================================== */
1239 : /* netCDFDataset */
1240 : /* ==================================================================== */
1241 : /************************************************************************/
1242 :
1243 : /************************************************************************/
1244 : /* netCDFDataset() */
1245 : /************************************************************************/
1246 :
1247 284 : netCDFDataset::netCDFDataset()
1248 :
1249 : {
1250 : /* basic dataset vars */
1251 284 : cdfid = -1;
1252 284 : papszSubDatasets = NULL;
1253 284 : papszMetadata = NULL;
1254 284 : bBottomUp = FALSE;
1255 284 : nFormat = NCDF_FORMAT_NONE;
1256 284 : bIsGdalFile = FALSE;
1257 284 : bIsGdalCfFile = FALSE;
1258 :
1259 : /* projection/GT */
1260 284 : adfGeoTransform[0] = 0.0;
1261 284 : adfGeoTransform[1] = 1.0;
1262 284 : adfGeoTransform[2] = 0.0;
1263 284 : adfGeoTransform[3] = 0.0;
1264 284 : adfGeoTransform[4] = 0.0;
1265 284 : adfGeoTransform[5] = 1.0;
1266 284 : pszProjection = NULL;
1267 284 : nXDimID = -1;
1268 284 : nYDimID = -1;
1269 284 : bIsProjected = FALSE;
1270 284 : bIsGeographic = FALSE; /* can be not projected, and also not geographic */
1271 :
1272 : /* state vars */
1273 284 : status = NC_NOERR;
1274 284 : bDefineMode = TRUE;
1275 284 : bSetProjection = FALSE;
1276 284 : bSetGeoTransform = FALSE;
1277 284 : bAddedProjectionVars = FALSE;
1278 :
1279 : /* create vars */
1280 284 : papszCreationOptions = NULL;
1281 284 : nCompress = NCDF_COMPRESS_NONE;
1282 284 : nZLevel = NCDF_DEFLATE_LEVEL;
1283 284 : nCreateMode = NC_CLOBBER;
1284 284 : bSignedData = TRUE;
1285 284 : }
1286 :
1287 :
1288 : /************************************************************************/
1289 : /* ~netCDFDataset() */
1290 : /************************************************************************/
1291 :
1292 284 : netCDFDataset::~netCDFDataset()
1293 :
1294 : {
1295 : /* make sure projection is written if GeoTransform OR Projection are missing */
1296 284 : if( (GetAccess() == GA_Update) && (! bAddedProjectionVars) ) {
1297 15 : if ( bSetProjection && ! bSetGeoTransform )
1298 0 : AddProjectionVars();
1299 15 : else if ( bSetGeoTransform && ! bSetProjection )
1300 1 : AddProjectionVars();
1301 : // CPLError( CE_Warning, CPLE_AppDefined,
1302 : // "netCDFDataset::~netCDFDataset() Projection was not defined, projection will be missing" );
1303 : }
1304 284 : FlushCache();
1305 :
1306 284 : CSLDestroy( papszMetadata );
1307 284 : CSLDestroy( papszSubDatasets );
1308 284 : CSLDestroy( papszCreationOptions );
1309 :
1310 284 : if( pszProjection )
1311 223 : CPLFree( pszProjection );
1312 :
1313 284 : if( cdfid )
1314 284 : nc_close( cdfid );
1315 284 : }
1316 :
1317 : /************************************************************************/
1318 : /* SetDefineMode() */
1319 : /************************************************************************/
1320 4013 : int netCDFDataset::SetDefineMode( int bNewDefineMode )
1321 : {
1322 : /* do nothing, already in same mode */
1323 4013 : if ( bDefineMode == bNewDefineMode ) return CE_None;
1324 :
1325 : CPLDebug( "GDAL_netCDF", "SetDefineMode(%d) old=%d",
1326 91 : bNewDefineMode, bDefineMode );
1327 :
1328 91 : bDefineMode = bNewDefineMode;
1329 :
1330 91 : if ( bDefineMode == TRUE )
1331 0 : status = nc_redef( cdfid );
1332 : else
1333 91 : status = nc_enddef( cdfid );
1334 :
1335 91 : NCDF_ERR(status);
1336 91 : return status;
1337 : }
1338 :
1339 : /************************************************************************/
1340 : /* GetMetadata() */
1341 : /************************************************************************/
1342 18 : char **netCDFDataset::GetMetadata( const char *pszDomain )
1343 : {
1344 18 : if( pszDomain != NULL && EQUALN( pszDomain, "SUBDATASETS", 11 ) )
1345 0 : return papszSubDatasets;
1346 : else
1347 18 : return GDALDataset::GetMetadata( pszDomain );
1348 : }
1349 :
1350 : /************************************************************************/
1351 : /* GetProjectionRef() */
1352 : /************************************************************************/
1353 :
1354 67 : const char * netCDFDataset::GetProjectionRef()
1355 : {
1356 67 : if( bSetProjection )
1357 67 : return pszProjection;
1358 : else
1359 0 : return GDALPamDataset::GetProjectionRef();
1360 : }
1361 :
1362 : /************************************************************************/
1363 : /* FetchCopyParm() */
1364 : /************************************************************************/
1365 :
1366 991 : double netCDFDataset::FetchCopyParm( const char *pszGridMappingValue,
1367 : const char *pszParm, double dfDefault )
1368 :
1369 : {
1370 : char szTemp[ MAX_NC_NAME ];
1371 : const char *pszValue;
1372 :
1373 991 : strcpy(szTemp,pszGridMappingValue);
1374 991 : strcat( szTemp, "#" );
1375 991 : strcat( szTemp, pszParm );
1376 991 : pszValue = CSLFetchNameValue(papszMetadata, szTemp);
1377 :
1378 991 : if( pszValue )
1379 : {
1380 728 : return CPLAtofM(pszValue);
1381 : }
1382 : else
1383 263 : return dfDefault;
1384 : }
1385 :
1386 : /************************************************************************/
1387 : /* FetchStandardParallels() */
1388 : /************************************************************************/
1389 :
1390 43 : char** netCDFDataset::FetchStandardParallels( const char *pszGridMappingValue )
1391 : {
1392 : char szTemp[ MAX_NC_NAME ];
1393 : const char *pszValue;
1394 43 : char **papszValues = NULL;
1395 : //cf-1.0 tags
1396 43 : strcpy( szTemp,pszGridMappingValue );
1397 43 : strcat( szTemp, "#" );
1398 43 : strcat( szTemp, CF_PP_STD_PARALLEL );
1399 43 : pszValue = CSLFetchNameValue( papszMetadata, szTemp );
1400 43 : if( pszValue != NULL ) {
1401 : // papszValues = CSLTokenizeString2( pszValue, ",", CSLT_STRIPLEADSPACES |
1402 : // CSLT_STRIPENDSPACES );
1403 : /* format has changed to { std1, std2 }, must remove { and } */
1404 37 : strcpy( szTemp,pszValue );
1405 37 : int last_char = strlen(pszValue) - 1;
1406 37 : if ( szTemp[0] == '{' ) szTemp[0] = ' ';
1407 37 : if ( szTemp[last_char] == '}' ) szTemp[last_char] = ' ';
1408 : papszValues = CSLTokenizeString2( szTemp, ",", CSLT_STRIPLEADSPACES |
1409 37 : CSLT_STRIPENDSPACES );
1410 : }
1411 : //try gdal tags
1412 : else
1413 : {
1414 6 : strcpy( szTemp, pszGridMappingValue );
1415 6 : strcat( szTemp, "#" );
1416 6 : strcat( szTemp, CF_PP_STD_PARALLEL_1 );
1417 :
1418 6 : pszValue = CSLFetchNameValue( papszMetadata, szTemp );
1419 :
1420 6 : if ( pszValue != NULL )
1421 0 : papszValues = CSLAddString( papszValues, pszValue );
1422 :
1423 6 : strcpy( szTemp,pszGridMappingValue );
1424 6 : strcat( szTemp, "#" );
1425 6 : strcat( szTemp, CF_PP_STD_PARALLEL_2 );
1426 :
1427 6 : pszValue = CSLFetchNameValue( papszMetadata, szTemp );
1428 :
1429 6 : if( pszValue != NULL )
1430 0 : papszValues = CSLAddString( papszValues, pszValue );
1431 : }
1432 :
1433 43 : return papszValues;
1434 : }
1435 :
1436 : /************************************************************************/
1437 : /* SetProjectionFromVar() */
1438 : /************************************************************************/
1439 166 : void netCDFDataset::SetProjectionFromVar( int var )
1440 : {
1441 : size_t start[2], edge[2];
1442 : unsigned int i;
1443 : const char *pszValue;
1444 : int nVarProjectionID;
1445 : char szVarName[ MAX_NC_NAME ];
1446 : char szTemp[ MAX_NC_NAME ];
1447 : char szGridMappingName[ MAX_NC_NAME ];
1448 : char szGridMappingValue[ MAX_NC_NAME ];
1449 :
1450 166 : double dfStdP1=0.0;
1451 166 : double dfStdP2=0.0;
1452 : double dfCenterLat;
1453 : double dfCenterLon;
1454 : double dfScale;
1455 : double dfFalseEasting;
1456 : double dfFalseNorthing;
1457 : double dfCentralMeridian;
1458 : double dfEarthRadius;
1459 : double dfInverseFlattening;
1460 : double dfLonPrimeMeridian;
1461 : double dfSemiMajorAxis;
1462 : double dfSemiMinorAxis;
1463 :
1464 166 : int bGotGeogCS = FALSE;
1465 166 : int bGotCfSRS = FALSE;
1466 166 : int bGotGdalSRS = FALSE;
1467 166 : int bLookForWellKnownGCS = FALSE; //this could be a Config Option
1468 :
1469 : /* These values from CF metadata */
1470 166 : OGRSpatialReference oSRS;
1471 166 : int nVarDimXID = -1;
1472 166 : int nVarDimYID = -1;
1473 : double *pdfXCoord;
1474 : double *pdfYCoord;
1475 : char szDimNameX[ MAX_NC_NAME ];
1476 : char szDimNameY[ MAX_NC_NAME ];
1477 : int nSpacingBegin;
1478 : int nSpacingMiddle;
1479 : int nSpacingLast;
1480 166 : size_t xdim = nRasterXSize;
1481 166 : size_t ydim = nRasterYSize;
1482 :
1483 166 : const char *pszUnits = NULL;
1484 :
1485 : /* These values from GDAL metadata */
1486 166 : const char *pszWKT = NULL;
1487 166 : const char *pszGeoTransform = NULL;
1488 166 : char **papszGeoTransform = NULL;
1489 :
1490 166 : netCDFDataset * poDS = this; /* perhaps this should be removed for clarity */
1491 :
1492 : /* temp variables to use in SetGeoTransform() and SetProjection() */
1493 : double adfTempGeoTransform[6];
1494 : char *pszTempProjection;
1495 :
1496 166 : int bGotGeoTransform = FALSE;
1497 :
1498 166 : CPLDebug( "GDAL_netCDF", "\n=====\nSetProjectionFromVar( %d )\n", var );
1499 :
1500 : /* -------------------------------------------------------------------- */
1501 : /* Get x/y range information. */
1502 : /* -------------------------------------------------------------------- */
1503 :
1504 166 : adfTempGeoTransform[0] = 0.0;
1505 166 : adfTempGeoTransform[1] = 1.0;
1506 166 : adfTempGeoTransform[2] = 0.0;
1507 166 : adfTempGeoTransform[3] = 0.0;
1508 166 : adfTempGeoTransform[4] = 0.0;
1509 166 : adfTempGeoTransform[5] = 1.0;
1510 166 : pszTempProjection = NULL;
1511 :
1512 : /* -------------------------------------------------------------------- */
1513 : /* Look for grid_mapping metadata */
1514 : /* -------------------------------------------------------------------- */
1515 :
1516 166 : strcpy( szGridMappingValue, "" );
1517 166 : strcpy( szGridMappingName, "" );
1518 :
1519 166 : nc_inq_varname( cdfid, var, szVarName );
1520 166 : strcpy(szTemp,szVarName);
1521 166 : strcat(szTemp,"#");
1522 166 : strcat(szTemp,CF_GRD_MAPPING);
1523 :
1524 166 : pszValue = CSLFetchNameValue(poDS->papszMetadata, szTemp);
1525 166 : if( pszValue ) {
1526 116 : strcpy(szGridMappingName,szTemp);
1527 116 : strcpy(szGridMappingValue,pszValue);
1528 : }
1529 :
1530 166 : if( !EQUAL( szGridMappingValue, "" ) ) {
1531 :
1532 : /* Read grid_mapping metadata */
1533 116 : nc_inq_varid( cdfid, szGridMappingValue, &nVarProjectionID );
1534 116 : poDS->ReadAttributes( cdfid, nVarProjectionID );
1535 :
1536 : /* -------------------------------------------------------------------- */
1537 : /* Look for GDAL spatial_ref and GeoTransform within grid_mapping */
1538 : /* -------------------------------------------------------------------- */
1539 116 : CPLDebug( "GDAL_netCDF", "got grid_mapping %s", szGridMappingValue );
1540 116 : strcpy( szTemp,szGridMappingValue);
1541 116 : strcat( szTemp, "#" );
1542 116 : strcat( szTemp, NCDF_SPATIAL_REF);
1543 :
1544 116 : pszWKT = CSLFetchNameValue(poDS->papszMetadata, szTemp);
1545 :
1546 116 : if( pszWKT != NULL ) {
1547 111 : strcpy( szTemp,szGridMappingValue);
1548 111 : strcat( szTemp, "#" );
1549 111 : strcat( szTemp, NCDF_GEOTRANSFORM);
1550 111 : pszGeoTransform = CSLFetchNameValue(poDS->papszMetadata, szTemp);
1551 : }
1552 : }
1553 :
1554 : /* -------------------------------------------------------------------- */
1555 : /* Get information about the file. */
1556 : /* -------------------------------------------------------------------- */
1557 : /* Was this file created by the GDAL netcdf driver? */
1558 : /* Was this file created by the newer (CF-conformant) driver? */
1559 : /* -------------------------------------------------------------------- */
1560 : /* 1) If GDAL netcdf metadata is set, and version >= 1.9, */
1561 : /* it was created with the new driver */
1562 : /* 2) Else, if spatial_ref and GeoTransform are present in the */
1563 : /* grid_mapping variable, it was created by the old driver */
1564 : /* -------------------------------------------------------------------- */
1565 166 : pszValue = CSLFetchNameValue(poDS->papszMetadata, "NC_GLOBAL#GDAL");
1566 :
1567 166 : if( pszValue && NCDFIsGDALVersionGTE(pszValue, 1900)) {
1568 147 : bIsGdalFile = TRUE;
1569 147 : bIsGdalCfFile = TRUE;
1570 : }
1571 19 : else if( pszWKT != NULL && pszGeoTransform != NULL ) {
1572 0 : bIsGdalFile = TRUE;
1573 0 : bIsGdalCfFile = FALSE;
1574 : }
1575 :
1576 : /* -------------------------------------------------------------------- */
1577 : /* Set default bottom-up default value */
1578 : /* Config option GDAL_NETCDF_BOTTOMUP and Y axis dimension variable*/
1579 : /* override the default */
1580 : /* -------------------------------------------------------------------- */
1581 166 : pszValue = CPLGetConfigOption( "GDAL_NETCDF_BOTTOMUP", NULL );
1582 166 : if ( pszValue ) {
1583 0 : poDS->bBottomUp = CSLTestBoolean( pszValue ) != FALSE;
1584 : }
1585 : else {
1586 : // if ( bIsGdalFile && ! bIsGdalCfFile )
1587 : /* new driver is not not bottom by default any more */
1588 166 : if ( bIsGdalFile )
1589 147 : poDS->bBottomUp = FALSE;
1590 : else
1591 19 : poDS->bBottomUp = TRUE;
1592 : }
1593 : CPLDebug( "GDAL_netCDF",
1594 : "bIsGdalFile=%d bIsGdalCfFile=%d bBottomUp=%d",
1595 166 : bIsGdalFile, bIsGdalCfFile, bBottomUp );
1596 :
1597 : /* -------------------------------------------------------------------- */
1598 : /* Look for dimension: lon */
1599 : /* -------------------------------------------------------------------- */
1600 :
1601 166 : memset( szDimNameX, '\0', sizeof( char ) * MAX_NC_NAME );
1602 166 : memset( szDimNameY, '\0', sizeof( char ) * MAX_NC_NAME );
1603 :
1604 416 : for( i = 0; (i < strlen( poDS->papszDimName[ poDS->nXDimID ] ) &&
1605 : i < 3 ); i++ ) {
1606 250 : szDimNameX[i]=(char)tolower( ( poDS->papszDimName[poDS->nXDimID] )[i] );
1607 : }
1608 166 : szDimNameX[3] = '\0';
1609 416 : for( i = 0; (i < strlen( poDS->papszDimName[ poDS->nYDimID ] ) &&
1610 : i < 3 ); i++ ) {
1611 250 : szDimNameY[i]=(char)tolower( ( poDS->papszDimName[poDS->nYDimID] )[i] );
1612 : }
1613 166 : szDimNameY[3] = '\0';
1614 :
1615 : /* -------------------------------------------------------------------- */
1616 : /* Read grid_mapping information and set projections */
1617 : /* -------------------------------------------------------------------- */
1618 :
1619 166 : if( !( EQUAL(szGridMappingName,"" ) ) ) {
1620 :
1621 116 : strcpy( szTemp, szGridMappingValue );
1622 116 : strcat( szTemp, "#" );
1623 116 : strcat( szTemp, CF_GRD_MAPPING_NAME );
1624 116 : pszValue = CSLFetchNameValue(poDS->papszMetadata, szTemp);
1625 :
1626 116 : if( pszValue != NULL ) {
1627 :
1628 : /* -------------------------------------------------------------------- */
1629 : /* Check for datum/spheroid information */
1630 : /* -------------------------------------------------------------------- */
1631 : dfEarthRadius =
1632 : poDS->FetchCopyParm( szGridMappingValue,
1633 : CF_PP_EARTH_RADIUS,
1634 116 : -1.0 );
1635 :
1636 : dfLonPrimeMeridian =
1637 : poDS->FetchCopyParm( szGridMappingValue,
1638 : CF_PP_LONG_PRIME_MERIDIAN,
1639 116 : 0.0 );
1640 :
1641 : dfInverseFlattening =
1642 : poDS->FetchCopyParm( szGridMappingValue,
1643 : CF_PP_INVERSE_FLATTENING,
1644 116 : -1.0 );
1645 :
1646 : dfSemiMajorAxis =
1647 : poDS->FetchCopyParm( szGridMappingValue,
1648 : CF_PP_SEMI_MAJOR_AXIS,
1649 116 : -1.0 );
1650 :
1651 : dfSemiMinorAxis =
1652 : poDS->FetchCopyParm( szGridMappingValue,
1653 : CF_PP_SEMI_MINOR_AXIS,
1654 116 : -1.0 );
1655 :
1656 : //see if semi-major exists if radius doesn't
1657 116 : if( dfEarthRadius < 0.0 )
1658 116 : dfEarthRadius = dfSemiMajorAxis;
1659 :
1660 : //if still no radius, check old tag
1661 116 : if( dfEarthRadius < 0.0 )
1662 : dfEarthRadius = poDS->FetchCopyParm( szGridMappingValue,
1663 : CF_PP_EARTH_RADIUS_OLD,
1664 5 : -1.0 );
1665 :
1666 : //has radius value
1667 116 : if( dfEarthRadius > 0.0 ) {
1668 : //check for inv_flat tag
1669 114 : if( dfInverseFlattening < 0.0 ) {
1670 : //no inv_flat tag, check for semi_minor
1671 2 : if( dfSemiMinorAxis < 0.0 ) {
1672 : //no way to get inv_flat, use sphere
1673 : oSRS.SetGeogCS( "unknown",
1674 : NULL,
1675 : "Sphere",
1676 2 : dfEarthRadius, 0.0 );
1677 2 : bGotGeogCS = TRUE;
1678 : }
1679 : else {
1680 0 : if( dfSemiMajorAxis < 0.0 )
1681 0 : dfSemiMajorAxis = dfEarthRadius;
1682 : //set inv_flat using semi_minor/major
1683 : dfInverseFlattening =
1684 0 : 1.0 / ( dfSemiMajorAxis - dfSemiMinorAxis ) / dfSemiMajorAxis;
1685 : oSRS.SetGeogCS( "unknown",
1686 : NULL,
1687 : "Spheroid",
1688 0 : dfEarthRadius, dfInverseFlattening );
1689 0 : bGotGeogCS = TRUE;
1690 : }
1691 : }
1692 : else {
1693 : oSRS.SetGeogCS( "unknown",
1694 : NULL,
1695 : "Spheroid",
1696 112 : dfEarthRadius, dfInverseFlattening );
1697 112 : bGotGeogCS = TRUE;
1698 : }
1699 :
1700 114 : if ( bGotGeogCS )
1701 114 : CPLDebug( "GDAL_netCDF", "got spheroid from CF: (%f , %f)", dfEarthRadius, dfInverseFlattening );
1702 :
1703 : }
1704 : //no radius, set as wgs84 as default?
1705 : else {
1706 : // This would be too indiscrimant. But we should set
1707 : // it if we know the data is geographic.
1708 : //oSRS.SetWellKnownGeogCS( "WGS84" );
1709 : }
1710 :
1711 : /* -------------------------------------------------------------------- */
1712 : /* Transverse Mercator */
1713 : /* -------------------------------------------------------------------- */
1714 :
1715 116 : if( EQUAL( pszValue, CF_PT_TM ) ) {
1716 :
1717 : dfScale =
1718 : poDS->FetchCopyParm( szGridMappingValue,
1719 26 : CF_PP_SCALE_FACTOR_MERIDIAN, 1.0 );
1720 :
1721 : dfCenterLon =
1722 : poDS->FetchCopyParm( szGridMappingValue,
1723 26 : CF_PP_LONG_CENTRAL_MERIDIAN, 0.0 );
1724 :
1725 : dfCenterLat =
1726 : poDS->FetchCopyParm( szGridMappingValue,
1727 26 : CF_PP_LAT_PROJ_ORIGIN, 0.0 );
1728 :
1729 : dfFalseEasting =
1730 : poDS->FetchCopyParm( szGridMappingValue,
1731 26 : CF_PP_FALSE_EASTING, 0.0 );
1732 :
1733 : dfFalseNorthing =
1734 : poDS->FetchCopyParm( szGridMappingValue,
1735 26 : CF_PP_FALSE_NORTHING, 0.0 );
1736 :
1737 26 : bGotCfSRS = TRUE;
1738 : oSRS.SetTM( dfCenterLat,
1739 : dfCenterLon,
1740 : dfScale,
1741 : dfFalseEasting,
1742 26 : dfFalseNorthing );
1743 :
1744 26 : if( !bGotGeogCS )
1745 0 : oSRS.SetWellKnownGeogCS( "WGS84" );
1746 : }
1747 :
1748 : /* -------------------------------------------------------------------- */
1749 : /* Albers Equal Area */
1750 : /* -------------------------------------------------------------------- */
1751 :
1752 116 : if( EQUAL( pszValue, CF_PT_AEA ) ) {
1753 7 : char **papszStdParallels = NULL;
1754 :
1755 : dfCenterLon =
1756 : poDS->FetchCopyParm( szGridMappingValue,
1757 7 : CF_PP_LONG_CENTRAL_MERIDIAN, 0.0 );
1758 :
1759 : dfCenterLat =
1760 : poDS->FetchCopyParm( szGridMappingValue,
1761 7 : CF_PP_LAT_PROJ_ORIGIN, 0.0 );
1762 :
1763 : dfFalseEasting =
1764 : poDS->FetchCopyParm( szGridMappingValue,
1765 7 : CF_PP_FALSE_EASTING, 0.0 );
1766 :
1767 : dfFalseNorthing =
1768 : poDS->FetchCopyParm( szGridMappingValue,
1769 7 : CF_PP_FALSE_NORTHING, 0.0 );
1770 :
1771 : papszStdParallels =
1772 7 : FetchStandardParallels( szGridMappingValue );
1773 :
1774 7 : if( papszStdParallels != NULL ) {
1775 :
1776 7 : if ( CSLCount( papszStdParallels ) == 1 ) {
1777 : /* TODO CF-1 standard says it allows AEA to be encoded with only 1 standard parallel */
1778 : /* how should this actually map to a 2StdP OGC WKT version? */
1779 : CPLError( CE_Warning, CPLE_NotSupported,
1780 0 : "NetCDF driver import of AEA-1SP is not tested, using identical std. parallels\n" );
1781 0 : dfStdP1 = CPLAtofM( papszStdParallels[0] );
1782 0 : dfStdP2 = dfStdP1;
1783 :
1784 : }
1785 :
1786 7 : else if( CSLCount( papszStdParallels ) == 2 ) {
1787 7 : dfStdP1 = CPLAtofM( papszStdParallels[0] );
1788 7 : dfStdP2 = CPLAtofM( papszStdParallels[1] );
1789 : }
1790 : }
1791 : //old default
1792 : else {
1793 : dfStdP1 =
1794 : poDS->FetchCopyParm( szGridMappingValue,
1795 0 : CF_PP_STD_PARALLEL_1, 0.0 );
1796 :
1797 : dfStdP2 =
1798 : poDS->FetchCopyParm( szGridMappingValue,
1799 0 : CF_PP_STD_PARALLEL_2, 0.0 );
1800 : }
1801 :
1802 : dfCenterLat =
1803 : poDS->FetchCopyParm( szGridMappingValue,
1804 7 : CF_PP_LAT_PROJ_ORIGIN, 0.0 );
1805 :
1806 7 : bGotCfSRS = TRUE;
1807 : oSRS.SetACEA( dfStdP1, dfStdP2, dfCenterLat, dfCenterLon,
1808 7 : dfFalseEasting, dfFalseNorthing );
1809 :
1810 7 : if( !bGotGeogCS )
1811 0 : oSRS.SetWellKnownGeogCS( "WGS84" );
1812 :
1813 7 : CSLDestroy( papszStdParallels );
1814 : }
1815 :
1816 : /* -------------------------------------------------------------------- */
1817 : /* Cylindrical Equal Area */
1818 : /* -------------------------------------------------------------------- */
1819 :
1820 115 : else if( EQUAL( pszValue, CF_PT_CEA ) || EQUAL( pszValue, CF_PT_LCEA ) ) {
1821 6 : char **papszStdParallels = NULL;
1822 :
1823 : papszStdParallels =
1824 6 : FetchStandardParallels( szGridMappingValue );
1825 :
1826 6 : if( papszStdParallels != NULL ) {
1827 6 : dfStdP1 = CPLAtofM( papszStdParallels[0] );
1828 : }
1829 : else {
1830 : //TODO: add support for 'scale_factor_at_projection_origin' variant to standard parallel
1831 : //Probably then need to calc a std parallel equivalent
1832 : CPLError( CE_Failure, CPLE_NotSupported,
1833 : "NetCDF driver does not support import of CF-1 LCEA "
1834 0 : "'scale_factor_at_projection_origin' variant yet.\n" );
1835 : }
1836 :
1837 : dfCentralMeridian =
1838 : poDS->FetchCopyParm( szGridMappingValue,
1839 6 : CF_PP_LONG_CENTRAL_MERIDIAN, 0.0 );
1840 :
1841 : dfFalseEasting =
1842 : poDS->FetchCopyParm( szGridMappingValue,
1843 6 : CF_PP_FALSE_EASTING, 0.0 );
1844 :
1845 : dfFalseNorthing =
1846 : poDS->FetchCopyParm( szGridMappingValue,
1847 6 : CF_PP_FALSE_NORTHING, 0.0 );
1848 :
1849 6 : bGotCfSRS = TRUE;
1850 : oSRS.SetCEA( dfStdP1, dfCentralMeridian,
1851 6 : dfFalseEasting, dfFalseNorthing );
1852 :
1853 6 : if( !bGotGeogCS )
1854 0 : oSRS.SetWellKnownGeogCS( "WGS84" );
1855 :
1856 : }
1857 :
1858 : /* -------------------------------------------------------------------- */
1859 : /* lambert_azimuthal_equal_area */
1860 : /* -------------------------------------------------------------------- */
1861 103 : else if( EQUAL( pszValue, CF_PT_LAEA ) ) {
1862 : dfCenterLon =
1863 : poDS->FetchCopyParm( szGridMappingValue,
1864 6 : CF_PP_LON_PROJ_ORIGIN, 0.0 );
1865 :
1866 : dfCenterLat =
1867 : poDS->FetchCopyParm( szGridMappingValue,
1868 6 : CF_PP_LAT_PROJ_ORIGIN, 0.0 );
1869 :
1870 : dfFalseEasting =
1871 : poDS->FetchCopyParm( szGridMappingValue,
1872 6 : CF_PP_FALSE_EASTING, 0.0 );
1873 :
1874 : dfFalseNorthing =
1875 : poDS->FetchCopyParm( szGridMappingValue,
1876 6 : CF_PP_FALSE_NORTHING, 0.0 );
1877 :
1878 6 : oSRS.SetProjCS( "LAEA (WGS84) " );
1879 :
1880 6 : bGotCfSRS = TRUE;
1881 : oSRS.SetLAEA( dfCenterLat, dfCenterLon,
1882 6 : dfFalseEasting, dfFalseNorthing );
1883 :
1884 6 : if( !bGotGeogCS )
1885 0 : oSRS.SetWellKnownGeogCS( "WGS84" );
1886 :
1887 : }
1888 :
1889 : /* -------------------------------------------------------------------- */
1890 : /* Azimuthal Equidistant */
1891 : /* -------------------------------------------------------------------- */
1892 97 : else if( EQUAL( pszValue, CF_PT_AE ) ) {
1893 : dfCenterLon =
1894 : poDS->FetchCopyParm( szGridMappingValue,
1895 6 : CF_PP_LON_PROJ_ORIGIN, 0.0 );
1896 :
1897 : dfCenterLat =
1898 : poDS->FetchCopyParm( szGridMappingValue,
1899 6 : CF_PP_LAT_PROJ_ORIGIN, 0.0 );
1900 :
1901 : dfFalseEasting =
1902 : poDS->FetchCopyParm( szGridMappingValue,
1903 6 : CF_PP_FALSE_EASTING, 0.0 );
1904 :
1905 : dfFalseNorthing =
1906 : poDS->FetchCopyParm( szGridMappingValue,
1907 6 : CF_PP_FALSE_NORTHING, 0.0 );
1908 :
1909 6 : bGotCfSRS = TRUE;
1910 : oSRS.SetAE( dfCenterLat, dfCenterLon,
1911 6 : dfFalseEasting, dfFalseNorthing );
1912 :
1913 6 : if( !bGotGeogCS )
1914 0 : oSRS.SetWellKnownGeogCS( "WGS84" );
1915 :
1916 : }
1917 :
1918 : /* -------------------------------------------------------------------- */
1919 : /* Lambert conformal conic */
1920 : /* -------------------------------------------------------------------- */
1921 91 : else if( EQUAL( pszValue, CF_PT_LCC ) ) {
1922 :
1923 12 : char **papszStdParallels = NULL;
1924 :
1925 : dfCenterLon =
1926 : poDS->FetchCopyParm( szGridMappingValue,
1927 12 : CF_PP_LONG_CENTRAL_MERIDIAN, 0.0 );
1928 :
1929 : dfCenterLat =
1930 : poDS->FetchCopyParm( szGridMappingValue,
1931 12 : CF_PP_LAT_PROJ_ORIGIN, 0.0 );
1932 :
1933 : dfFalseEasting =
1934 : poDS->FetchCopyParm( szGridMappingValue,
1935 12 : CF_PP_FALSE_EASTING, 0.0 );
1936 :
1937 : dfFalseNorthing =
1938 : poDS->FetchCopyParm( szGridMappingValue,
1939 12 : CF_PP_FALSE_NORTHING, 0.0 );
1940 :
1941 : papszStdParallels =
1942 12 : FetchStandardParallels( szGridMappingValue );
1943 :
1944 : /* 2SP variant */
1945 12 : if( CSLCount( papszStdParallels ) == 2 ) {
1946 11 : dfStdP1 = CPLAtofM( papszStdParallels[0] );
1947 11 : dfStdP2 = CPLAtofM( papszStdParallels[1] );
1948 : oSRS.SetLCC( dfStdP1, dfStdP2, dfCenterLat, dfCenterLon,
1949 11 : dfFalseEasting, dfFalseNorthing );
1950 : }
1951 : /* 1SP variant (with standard_parallel or center lon) */
1952 : /* See comments in netcdfdataset.h for this projection. */
1953 : else {
1954 :
1955 : dfScale =
1956 : poDS->FetchCopyParm( szGridMappingValue,
1957 1 : CF_PP_SCALE_FACTOR_ORIGIN, -1.0 );
1958 :
1959 : /* CF definition, without scale factor */
1960 1 : if( CPLIsEqual(dfScale, -1.0) ) {
1961 :
1962 : /* with standard_parallel */
1963 1 : if( CSLCount( papszStdParallels ) == 1 )
1964 1 : dfStdP1 = CPLAtofM( papszStdParallels[0] );
1965 : /* with center lon instead */
1966 : else
1967 0 : dfStdP1 = dfCenterLat;
1968 1 : dfStdP2 = dfStdP1;
1969 :
1970 : /* test if we should actually compute scale factor */
1971 1 : if ( ! CPLIsEqual( dfStdP1, dfCenterLat ) ) {
1972 : CPLError( CE_Warning, CPLE_NotSupported,
1973 : "NetCDF driver import of LCC-1SP with standard_parallel1 != latitude_of_projection_origin\n"
1974 0 : "(which forces a computation of scale_factor) is experimental (bug #3324)\n" );
1975 : /* use Snyder eq. 15-4 to compute dfScale from dfStdP1 and dfCenterLat */
1976 : /* only tested for dfStdP1=dfCenterLat and (25,26), needs more data for testing */
1977 : /* other option: use the 2SP variant - how to compute new standard parallels? */
1978 : #define NCDF_PI 3.14159265358979323846
1979 : dfScale = ( cos(dfStdP1) * pow( tan(NCDF_PI/4 + dfStdP1/2), sin(dfStdP1) ) ) /
1980 0 : ( cos(dfCenterLat) * pow( tan(NCDF_PI/4 + dfCenterLat/2), sin(dfCenterLat) ) );
1981 : }
1982 : /* default is 1.0 */
1983 : else
1984 1 : dfScale = 1.0;
1985 :
1986 : oSRS.SetLCC1SP( dfCenterLat, dfCenterLon, dfScale,
1987 1 : dfFalseEasting, dfFalseNorthing );
1988 : /* store dfStdP1 so we can output it to CF later */
1989 1 : oSRS.SetNormProjParm( SRS_PP_STANDARD_PARALLEL_1, dfStdP1 );
1990 : }
1991 : /* OGC/PROJ.4 definition with scale factor */
1992 : else {
1993 : oSRS.SetLCC1SP( dfCenterLat, dfCenterLon, dfScale,
1994 0 : dfFalseEasting, dfFalseNorthing );
1995 : }
1996 : }
1997 :
1998 :
1999 12 : bGotCfSRS = TRUE;
2000 12 : if( !bGotGeogCS )
2001 2 : oSRS.SetWellKnownGeogCS( "WGS84" );
2002 :
2003 12 : CSLDestroy( papszStdParallels );
2004 : }
2005 :
2006 : /* -------------------------------------------------------------------- */
2007 : /* Is this Latitude/Longitude Grid explicitly */
2008 : /* -------------------------------------------------------------------- */
2009 :
2010 79 : else if ( EQUAL ( pszValue, CF_PT_LATITUDE_LONGITUDE ) ) {
2011 23 : bGotCfSRS = TRUE;
2012 23 : if( !bGotGeogCS )
2013 0 : oSRS.SetWellKnownGeogCS( "WGS84" );
2014 : }
2015 : /* -------------------------------------------------------------------- */
2016 : /* Mercator */
2017 : /* -------------------------------------------------------------------- */
2018 :
2019 56 : else if ( EQUAL ( pszValue, CF_PT_MERCATOR ) ) {
2020 12 : char **papszStdParallels = NULL;
2021 :
2022 : /* If there is a standard_parallel, know it is Mercator 2SP */
2023 : papszStdParallels =
2024 12 : FetchStandardParallels( szGridMappingValue );
2025 :
2026 12 : if (NULL != papszStdParallels) {
2027 : /* CF-1 Mercator 2SP always has lat centered at equator */
2028 6 : dfStdP1 = CPLAtofM( papszStdParallels[0] );
2029 :
2030 6 : dfCenterLat = 0.0;
2031 :
2032 : dfCenterLon =
2033 : poDS->FetchCopyParm( szGridMappingValue,
2034 6 : CF_PP_LON_PROJ_ORIGIN, 0.0 );
2035 :
2036 : dfFalseEasting =
2037 : poDS->FetchCopyParm( szGridMappingValue,
2038 6 : CF_PP_FALSE_EASTING, 0.0 );
2039 :
2040 : dfFalseNorthing =
2041 : poDS->FetchCopyParm( szGridMappingValue,
2042 6 : CF_PP_FALSE_NORTHING, 0.0 );
2043 :
2044 : oSRS.SetMercator2SP( dfStdP1, dfCenterLat, dfCenterLon,
2045 6 : dfFalseEasting, dfFalseNorthing );
2046 : }
2047 : else {
2048 : dfCenterLon =
2049 : poDS->FetchCopyParm( szGridMappingValue,
2050 6 : CF_PP_LON_PROJ_ORIGIN, 0.0 );
2051 :
2052 : dfCenterLat =
2053 : poDS->FetchCopyParm( szGridMappingValue,
2054 6 : CF_PP_LAT_PROJ_ORIGIN, 0.0 );
2055 :
2056 : dfScale =
2057 : poDS->FetchCopyParm( szGridMappingValue,
2058 : CF_PP_SCALE_FACTOR_ORIGIN,
2059 6 : 1.0 );
2060 :
2061 : dfFalseEasting =
2062 : poDS->FetchCopyParm( szGridMappingValue,
2063 6 : CF_PP_FALSE_EASTING, 0.0 );
2064 :
2065 : dfFalseNorthing =
2066 : poDS->FetchCopyParm( szGridMappingValue,
2067 6 : CF_PP_FALSE_NORTHING, 0.0 );
2068 :
2069 : oSRS.SetMercator( dfCenterLat, dfCenterLon, dfScale,
2070 6 : dfFalseEasting, dfFalseNorthing );
2071 : }
2072 :
2073 12 : bGotCfSRS = TRUE;
2074 :
2075 12 : if( !bGotGeogCS )
2076 0 : oSRS.SetWellKnownGeogCS( "WGS84" );
2077 : }
2078 :
2079 : /* -------------------------------------------------------------------- */
2080 : /* Orthographic */
2081 : /* -------------------------------------------------------------------- */
2082 :
2083 :
2084 44 : else if ( EQUAL ( pszValue, CF_PT_ORTHOGRAPHIC ) ) {
2085 : dfCenterLon =
2086 : poDS->FetchCopyParm( szGridMappingValue,
2087 6 : CF_PP_LON_PROJ_ORIGIN, 0.0 );
2088 :
2089 : dfCenterLat =
2090 : poDS->FetchCopyParm( szGridMappingValue,
2091 6 : CF_PP_LAT_PROJ_ORIGIN, 0.0 );
2092 :
2093 : dfFalseEasting =
2094 : poDS->FetchCopyParm( szGridMappingValue,
2095 6 : CF_PP_FALSE_EASTING, 0.0 );
2096 :
2097 : dfFalseNorthing =
2098 : poDS->FetchCopyParm( szGridMappingValue,
2099 6 : CF_PP_FALSE_NORTHING, 0.0 );
2100 :
2101 6 : bGotCfSRS = TRUE;
2102 :
2103 : oSRS.SetOrthographic( dfCenterLat, dfCenterLon,
2104 6 : dfFalseEasting, dfFalseNorthing );
2105 :
2106 6 : if( !bGotGeogCS )
2107 0 : oSRS.SetWellKnownGeogCS( "WGS84" );
2108 : }
2109 :
2110 : /* -------------------------------------------------------------------- */
2111 : /* Polar Stereographic */
2112 : /* -------------------------------------------------------------------- */
2113 :
2114 38 : else if ( EQUAL ( pszValue, CF_PT_POLAR_STEREO ) ) {
2115 : /* Note: reversing our current mapping on export, from
2116 : 'latitude_of_origin' in OGC WKT to 'standard_parallel' in CF-1
2117 : See comments in netcdfdataset.h for this projection. */
2118 6 : char **papszStdParallels = NULL;
2119 :
2120 : papszStdParallels =
2121 6 : FetchStandardParallels( szGridMappingValue );
2122 :
2123 6 : if (NULL != papszStdParallels) {
2124 6 : dfCenterLat = CPLAtofM( papszStdParallels[0] );
2125 : }
2126 : else {
2127 : //TODO: not sure how to handle if CF-1 doesn't include a std_parallel:
2128 0 : dfCenterLat = 0.0; //just to avoid warning at compilation
2129 : CPLError( CE_Failure, CPLE_NotSupported,
2130 : "The NetCDF driver does not yet to support import of CF-1 Polar stereographic "
2131 0 : "without a std_parallel attribute.\n" );
2132 : }
2133 : dfScale =
2134 : poDS->FetchCopyParm( szGridMappingValue,
2135 : CF_PP_SCALE_FACTOR_ORIGIN,
2136 6 : 1.0 );
2137 :
2138 : dfCenterLon =
2139 : poDS->FetchCopyParm( szGridMappingValue,
2140 6 : CF_PP_VERT_LONG_FROM_POLE, 0.0 );
2141 :
2142 : dfFalseEasting =
2143 : poDS->FetchCopyParm( szGridMappingValue,
2144 6 : CF_PP_FALSE_EASTING, 0.0 );
2145 :
2146 : dfFalseNorthing =
2147 : poDS->FetchCopyParm( szGridMappingValue,
2148 6 : CF_PP_FALSE_NORTHING, 0.0 );
2149 :
2150 6 : bGotCfSRS = TRUE;
2151 : oSRS.SetPS( dfCenterLat, dfCenterLon, dfScale,
2152 6 : dfFalseEasting, dfFalseNorthing );
2153 :
2154 6 : if( !bGotGeogCS )
2155 0 : oSRS.SetWellKnownGeogCS( "WGS84" );
2156 : }
2157 :
2158 : /* -------------------------------------------------------------------- */
2159 : /* Stereographic */
2160 : /* -------------------------------------------------------------------- */
2161 :
2162 32 : else if ( EQUAL ( pszValue, CF_PT_STEREO ) ) {
2163 :
2164 : dfCenterLon =
2165 : poDS->FetchCopyParm( szGridMappingValue,
2166 6 : CF_PP_LON_PROJ_ORIGIN, 0.0 );
2167 :
2168 : dfCenterLat =
2169 : poDS->FetchCopyParm( szGridMappingValue,
2170 6 : CF_PP_LAT_PROJ_ORIGIN, 0.0 );
2171 :
2172 : dfScale =
2173 : poDS->FetchCopyParm( szGridMappingValue,
2174 : CF_PP_SCALE_FACTOR_ORIGIN,
2175 6 : 1.0 );
2176 :
2177 : dfFalseEasting =
2178 : poDS->FetchCopyParm( szGridMappingValue,
2179 6 : CF_PP_FALSE_EASTING, 0.0 );
2180 :
2181 : dfFalseNorthing =
2182 : poDS->FetchCopyParm( szGridMappingValue,
2183 6 : CF_PP_FALSE_NORTHING, 0.0 );
2184 :
2185 6 : bGotCfSRS = TRUE;
2186 : oSRS.SetStereographic( dfCenterLat, dfCenterLon, dfScale,
2187 6 : dfFalseEasting, dfFalseNorthing );
2188 :
2189 6 : if( !bGotGeogCS )
2190 0 : oSRS.SetWellKnownGeogCS( "WGS84" );
2191 : }
2192 :
2193 : /* -------------------------------------------------------------------- */
2194 : /* Is this Latitude/Longitude Grid, default */
2195 : /* -------------------------------------------------------------------- */
2196 :
2197 0 : } else if( EQUAL( szDimNameX,"lon" ) ) {
2198 0 : oSRS.SetWellKnownGeogCS( "WGS84" );
2199 :
2200 : } else {
2201 : // This would be too indiscrimant. But we should set
2202 : // it if we know the data is geographic.
2203 : //oSRS.SetWellKnownGeogCS( "WGS84" );
2204 : }
2205 : }
2206 : /* -------------------------------------------------------------------- */
2207 : /* Read projection coordinates */
2208 : /* -------------------------------------------------------------------- */
2209 :
2210 166 : nc_inq_varid( cdfid, poDS->papszDimName[nXDimID], &nVarDimXID );
2211 166 : nc_inq_varid( cdfid, poDS->papszDimName[nYDimID], &nVarDimYID );
2212 :
2213 166 : if( ( nVarDimXID != -1 ) && ( nVarDimYID != -1 ) ) {
2214 140 : pdfXCoord = (double *) CPLCalloc( xdim, sizeof(double) );
2215 140 : pdfYCoord = (double *) CPLCalloc( ydim, sizeof(double) );
2216 :
2217 140 : start[0] = 0;
2218 140 : edge[0] = xdim;
2219 : status = nc_get_vara_double( cdfid, nVarDimXID,
2220 140 : start, edge, pdfXCoord);
2221 :
2222 140 : edge[0] = ydim;
2223 : status = nc_get_vara_double( cdfid, nVarDimYID,
2224 140 : start, edge, pdfYCoord);
2225 :
2226 : /* -------------------------------------------------------------------- */
2227 : /* Check for bottom-up from the Y-axis order */
2228 : /* see bugs #4284 and #4251 */
2229 : /* -------------------------------------------------------------------- */
2230 :
2231 140 : if ( pdfYCoord[0] > pdfYCoord[1] )
2232 124 : poDS->bBottomUp = FALSE;
2233 : else
2234 16 : poDS->bBottomUp = TRUE;
2235 :
2236 140 : CPLDebug( "GDAL_netCDF", "set bBottomUp = %d from Y axis", poDS->bBottomUp );
2237 :
2238 :
2239 : /* -------------------------------------------------------------------- */
2240 : /* Is pixel spacing is uniform accross the map? */
2241 : /* -------------------------------------------------------------------- */
2242 :
2243 : /* -------------------------------------------------------------------- */
2244 : /* Check Longitude */
2245 : /* -------------------------------------------------------------------- */
2246 :
2247 140 : nSpacingBegin = (int) poDS->rint((pdfXCoord[1]-pdfXCoord[0]) * 1000);
2248 :
2249 140 : nSpacingMiddle = (int) poDS->rint((pdfXCoord[xdim / 2] -
2250 140 : pdfXCoord[(xdim / 2) + 1]) * 1000);
2251 :
2252 140 : nSpacingLast = (int) poDS->rint((pdfXCoord[xdim - 2] -
2253 140 : pdfXCoord[xdim-1]) * 1000);
2254 :
2255 279 : if( ( abs( nSpacingBegin ) == abs( nSpacingLast ) ) &&
2256 : ( abs( nSpacingBegin ) == abs( nSpacingMiddle ) ) &&
2257 : ( abs( nSpacingMiddle ) == abs( nSpacingLast ) ) ) {
2258 :
2259 : /* -------------------------------------------------------------------- */
2260 : /* Longitude is equally spaced, check latitude */
2261 : /* -------------------------------------------------------------------- */
2262 139 : nSpacingBegin = (int) poDS->rint((pdfYCoord[1]-pdfYCoord[0]) *
2263 139 : 1000);
2264 :
2265 139 : nSpacingMiddle = (int) poDS->rint((pdfYCoord[ydim / 2] -
2266 139 : pdfYCoord[(ydim / 2) + 1]) *
2267 139 : 1000);
2268 :
2269 139 : nSpacingLast = (int) poDS->rint((pdfYCoord[ydim - 2] -
2270 139 : pdfYCoord[ydim-1]) *
2271 139 : 1000);
2272 :
2273 :
2274 : /* -------------------------------------------------------------------- */
2275 : /* For Latitude we allow an error of 0.1 degrees for gaussian */
2276 : /* gridding */
2277 : /* -------------------------------------------------------------------- */
2278 :
2279 278 : if((( abs( abs(nSpacingBegin) - abs(nSpacingLast) ) ) < 100 ) &&
2280 : (( abs( abs(nSpacingBegin) - abs(nSpacingMiddle) ) ) < 100 ) &&
2281 : (( abs( abs(nSpacingMiddle) - abs(nSpacingLast) ) ) < 100) ) {
2282 :
2283 139 : if( ( abs( nSpacingBegin ) != abs( nSpacingLast ) ) ||
2284 : ( abs( nSpacingBegin ) != abs( nSpacingMiddle ) ) ||
2285 : ( abs( nSpacingMiddle ) != abs( nSpacingLast ) ) ) {
2286 :
2287 0 : CPLError(CE_Warning, 1,"Latitude grid not spaced evenly.\nSeting projection for grid spacing is within 0.1 degrees threshold.\n");
2288 :
2289 : }
2290 : /* -------------------------------------------------------------------- */
2291 : /* We have gridded data s we can set the Gereferencing info. */
2292 : /* -------------------------------------------------------------------- */
2293 :
2294 : /* -------------------------------------------------------------------- */
2295 : /* Enable GeoTransform */
2296 : /* -------------------------------------------------------------------- */
2297 : /* ----------------------------------------------------------*/
2298 : /* In the following "actual_range" and "node_offset" */
2299 : /* are attributes used by netCDF files created by GMT. */
2300 : /* If we find them we know how to proceed. Else, use */
2301 : /* the original algorithm. */
2302 : /* --------------------------------------------------------- */
2303 : double dummy[2], xMinMax[2], yMinMax[2];
2304 139 : int node_offset = 0;
2305 :
2306 139 : bGotGeoTransform = TRUE;
2307 :
2308 139 : nc_get_att_int (cdfid, NC_GLOBAL, "node_offset", &node_offset);
2309 :
2310 139 : if (!nc_get_att_double (cdfid, nVarDimXID, "actual_range", dummy)) {
2311 5 : xMinMax[0] = dummy[0];
2312 5 : xMinMax[1] = dummy[1];
2313 : }
2314 : else {
2315 134 : xMinMax[0] = pdfXCoord[0];
2316 134 : xMinMax[1] = pdfXCoord[xdim-1];
2317 134 : node_offset = 0;
2318 : }
2319 :
2320 139 : if (!nc_get_att_double (cdfid, nVarDimYID, "actual_range", dummy)) {
2321 5 : yMinMax[0] = dummy[0];
2322 5 : yMinMax[1] = dummy[1];
2323 : }
2324 : else {
2325 134 : yMinMax[0] = pdfYCoord[0];
2326 134 : yMinMax[1] = pdfYCoord[ydim-1];
2327 134 : node_offset = 0;
2328 : }
2329 :
2330 : /* for CF-1 conventions, assume bottom first */
2331 : /* was here, now is higher up in the code */
2332 : // if( ( EQUAL( szDimNameY, "lat" ) || EQUAL( szDimNameY, "y" ) )
2333 : // && pdfYCoord[0] < pdfYCoord[1] )
2334 : // poDS->bBottomUp = TRUE;
2335 :
2336 : /* Check for reverse order of y-coordinate */
2337 139 : if ( yMinMax[0] > yMinMax[1] ) {
2338 123 : dummy[0] = yMinMax[1];
2339 123 : dummy[1] = yMinMax[0];
2340 123 : yMinMax[0] = dummy[0];
2341 123 : yMinMax[1] = dummy[1];
2342 : }
2343 :
2344 : /* ----------------------------------------------------------*/
2345 : /* Many netcdf files are weather files distributed */
2346 : /* in km for the x/y resolution. This isn't perfect, */
2347 : /* but geotransforms can be terribly off if this isn't */
2348 : /* checked and accounted for. Maybe one more level of */
2349 : /* checking (grid_mapping_value#GRIB_param_Dx, or */
2350 : /* x#grid_spacing), but those are not cf tags. */
2351 : /* Have to change metadata value if change Create() to */
2352 : /* write cf tags */
2353 : /* ----------------------------------------------------------*/
2354 :
2355 : //check units for x and y, expand to other values
2356 : //and conversions.
2357 139 : if( oSRS.IsProjected( ) ) {
2358 93 : strcpy( szTemp, "x" );
2359 93 : strcat( szTemp, "#units" );
2360 : pszValue = CSLFetchNameValue( poDS->papszMetadata,
2361 93 : szTemp );
2362 93 : if( pszValue != NULL ) {
2363 93 : pszUnits = pszValue;
2364 93 : if( EQUAL( pszValue, "km" ) ) {
2365 5 : xMinMax[0] = xMinMax[0] * 1000;
2366 5 : xMinMax[1] = xMinMax[1] * 1000;
2367 : }
2368 : }
2369 93 : strcpy( szTemp, "y" );
2370 93 : strcat( szTemp, "#units" );
2371 : pszValue = CSLFetchNameValue( poDS->papszMetadata,
2372 93 : szTemp );
2373 93 : if( pszValue != NULL ) {
2374 : /* TODO: see how to deal with diff. values */
2375 : // if ( ! EQUAL( pszValue, szUnits ) )
2376 : // strcpy( szUnits, "\0" );
2377 93 : if( EQUAL( pszValue, "km" ) ) {
2378 5 : yMinMax[0] = yMinMax[0] * 1000;
2379 5 : yMinMax[1] = yMinMax[1] * 1000;
2380 : }
2381 : }
2382 : }
2383 :
2384 139 : adfTempGeoTransform[0] = xMinMax[0];
2385 139 : adfTempGeoTransform[2] = 0;
2386 139 : adfTempGeoTransform[3] = yMinMax[1];
2387 139 : adfTempGeoTransform[4] = 0;
2388 139 : adfTempGeoTransform[1] = ( xMinMax[1] - xMinMax[0] ) /
2389 139 : ( poDS->nRasterXSize + (node_offset - 1) );
2390 139 : adfTempGeoTransform[5] = ( yMinMax[0] - yMinMax[1] ) /
2391 139 : ( poDS->nRasterYSize + (node_offset - 1) );
2392 :
2393 : /* -------------------------------------------------------------------- */
2394 : /* Compute the center of the pixel */
2395 : /* -------------------------------------------------------------------- */
2396 139 : if ( !node_offset ) { // Otherwise its already the pixel center
2397 139 : adfTempGeoTransform[0] -= (adfTempGeoTransform[1] / 2);
2398 139 : adfTempGeoTransform[3] -= (adfTempGeoTransform[5] / 2);
2399 : }
2400 : }// end if (Latitude is equally spaced, within 0.1 degrees)
2401 : else {
2402 : CPLDebug( "GDAL_netCDF",
2403 0 : "Latitude is not equally spaced." );
2404 : }
2405 : }// end if (Longitude is equally spaced)
2406 : else {
2407 : CPLDebug( "GDAL_netCDF",
2408 1 : "Longitude is not equally spaced." );
2409 : }
2410 :
2411 140 : CPLFree( pdfXCoord );
2412 140 : CPLFree( pdfYCoord );
2413 : }// end if (has dims)
2414 :
2415 : CPLDebug( "GDAL_netCDF",
2416 : "bGotGeogCS=%d bGotCfSRS=%d bGotGeoTransform=%d",
2417 166 : bGotGeogCS, bGotCfSRS, bGotGeoTransform );
2418 :
2419 : /* -------------------------------------------------------------------- */
2420 : /* Set Projection if we got a geotransform */
2421 : /* -------------------------------------------------------------------- */
2422 166 : if ( bGotGeoTransform ) {
2423 : /* Set SRS Units */
2424 : /* TODO: check for other units */
2425 139 : if ( pszUnits != NULL && ! EQUAL(pszUnits,"") ) {
2426 93 : if ( EQUAL(pszUnits,"m") ) {
2427 88 : oSRS.SetLinearUnits( CF_UNITS_M, 1.0 );
2428 88 : oSRS.SetAuthority( "PROJCS|UNIT", "EPSG", 9001 );
2429 : }
2430 5 : else if ( EQUAL(pszUnits,"km") ) {
2431 5 : oSRS.SetLinearUnits( CF_UNITS_M, 1000.0 );
2432 5 : oSRS.SetAuthority( "PROJCS|UNIT", "EPSG", 9001 );
2433 : }
2434 0 : else if ( EQUALN(pszUnits,"degrees",7) ) {
2435 0 : oSRS.SetAngularUnits( CF_UNITS_D, CPLAtof(SRS_UA_DEGREE_CONV) );
2436 0 : oSRS.SetAuthority( "GEOGCS|UNIT", "EPSG", 9108 );
2437 : }
2438 : // else
2439 : // oSRS.SetLinearUnits(pszUnits, 1.0);
2440 : }
2441 :
2442 : /* Set GeoTransform */
2443 139 : SetGeoTransform( adfTempGeoTransform );
2444 : /* Set Projection */
2445 139 : oSRS.exportToWkt( &(pszTempProjection) );
2446 139 : SetProjection( pszTempProjection );
2447 139 : CPLFree( pszTempProjection );
2448 139 : CPLDebug( "GDAL_netCDF", "set WKT from CF [%s]\n", pszProjection );
2449 : }
2450 27 : else if ( bGotGeogCS || bGotCfSRS ) {
2451 0 : CPLError(CE_Warning, 1,"got SRS but no geotransform from CF!");
2452 : }
2453 : /* -------------------------------------------------------------------- */
2454 : /* Process custom GDAL values (spatial_ref, GeoTransform) */
2455 : /* -------------------------------------------------------------------- */
2456 166 : if( !EQUAL( szGridMappingValue, "" ) ) {
2457 :
2458 116 : if( pszWKT != NULL ) {
2459 :
2460 : /* -------------------------------------------------------------------- */
2461 : /* Compare CRS obtained from CF attributes and GDAL WKT */
2462 : /* If possible use the more complete GDAL WKT */
2463 : /* -------------------------------------------------------------------- */
2464 : /* Set the CRS to the one written by GDAL */
2465 111 : if ( ! bGotCfSRS || poDS->pszProjection == NULL || ! bIsGdalCfFile ) {
2466 0 : bGotGdalSRS = TRUE;
2467 0 : SetProjection( pszWKT );
2468 0 : CPLDebug( "GDAL_netCDF", "set WKT from GDAL [%s]\n", pszProjection );
2469 : }
2470 : else { /* use the SRS from GDAL if it doesn't conflict with the one from CF */
2471 111 : char *pszProjectionGDAL = (char*) pszWKT ;
2472 111 : OGRSpatialReference oSRSGDAL;
2473 111 : oSRSGDAL.importFromWkt( &pszProjectionGDAL );
2474 : /* set datum to unknown or else datums will not match, see bug #4281 */
2475 111 : if ( oSRSGDAL.GetAttrNode( "DATUM" ) )
2476 111 : oSRSGDAL.GetAttrNode( "DATUM" )->GetChild(0)->SetValue( "unknown" );
2477 : /* need this for setprojection autotest */
2478 111 : if ( oSRSGDAL.GetAttrNode( "PROJCS" ) )
2479 88 : oSRSGDAL.GetAttrNode( "PROJCS" )->GetChild(0)->SetValue( "unnamed" );
2480 111 : if ( oSRSGDAL.GetAttrNode( "GEOGCS" ) )
2481 111 : oSRSGDAL.GetAttrNode( "GEOGCS" )->GetChild(0)->SetValue( "unknown" );
2482 111 : oSRSGDAL.GetRoot()->StripNodes( "UNIT" );
2483 111 : if ( oSRS.IsSame(&oSRSGDAL) ) {
2484 : // printf("ARE SAME, using GDAL WKT\n");
2485 111 : bGotGdalSRS = TRUE;
2486 111 : SetProjection( pszWKT );
2487 111 : CPLDebug( "GDAL_netCDF", "set WKT from GDAL [%s]\n", pszProjection );
2488 : }
2489 : else {
2490 : CPLDebug( "GDAL_netCDF",
2491 : "got WKT from GDAL \n[%s]\nbut not using it because conflicts with CF\n[%s]\n",
2492 0 : pszWKT, poDS->pszProjection );
2493 111 : }
2494 : }
2495 : /* -------------------------------------------------------------------- */
2496 : /* Look for GeoTransform Array, if not found previously */
2497 : /* -------------------------------------------------------------------- */
2498 111 : if ( !bGotGeoTransform ) {
2499 :
2500 : /* TODO read the GT values and detect for conflict with CF */
2501 : /* this could resolve the GT precision loss issue */
2502 :
2503 0 : if( pszGeoTransform != NULL ) {
2504 : papszGeoTransform = CSLTokenizeString2( pszGeoTransform,
2505 : " ",
2506 0 : CSLT_HONOURSTRINGS );
2507 0 : bGotGeoTransform = TRUE;
2508 :
2509 0 : adfTempGeoTransform[0] = atof( papszGeoTransform[0] );
2510 0 : adfTempGeoTransform[1] = atof( papszGeoTransform[1] );
2511 0 : adfTempGeoTransform[2] = atof( papszGeoTransform[2] );
2512 0 : adfTempGeoTransform[3] = atof( papszGeoTransform[3] );
2513 0 : adfTempGeoTransform[4] = atof( papszGeoTransform[4] );
2514 0 : adfTempGeoTransform[5] = atof( papszGeoTransform[5] );
2515 : /* -------------------------------------------------------------------- */
2516 : /* Look for corner array values */
2517 : /* -------------------------------------------------------------------- */
2518 : } else {
2519 0 : double dfNN=0.0, dfSN=0.0, dfEE=0.0, dfWE=0.0;
2520 0 : int bGotNN=FALSE, bGotSN=FALSE, bGotEE=FALSE, bGotWE=FALSE;
2521 : // CPLDebug( "GDAL_netCDF", "looking for geotransform corners\n" );
2522 0 : strcpy(szTemp,szGridMappingValue);
2523 0 : strcat( szTemp, "#" );
2524 0 : strcat( szTemp, "Northernmost_Northing");
2525 0 : pszValue = CSLFetchNameValue(poDS->papszMetadata, szTemp);
2526 :
2527 0 : if( pszValue != NULL ) {
2528 0 : dfNN = atof( pszValue );
2529 0 : bGotNN = TRUE;
2530 : }
2531 0 : strcpy(szTemp,szGridMappingValue);
2532 0 : strcat( szTemp, "#" );
2533 0 : strcat( szTemp, "Southernmost_Northing");
2534 0 : pszValue = CSLFetchNameValue(poDS->papszMetadata, szTemp);
2535 :
2536 0 : if( pszValue != NULL ) {
2537 0 : dfSN = atof( pszValue );
2538 0 : bGotSN = TRUE;
2539 : }
2540 :
2541 0 : strcpy(szTemp,szGridMappingValue);
2542 0 : strcat( szTemp, "#" );
2543 0 : strcat( szTemp, "Easternmost_Easting");
2544 0 : pszValue = CSLFetchNameValue(poDS->papszMetadata, szTemp);
2545 :
2546 0 : if( pszValue != NULL ) {
2547 0 : dfEE = atof( pszValue );
2548 0 : bGotEE = TRUE;
2549 : }
2550 :
2551 0 : strcpy(szTemp,szGridMappingValue);
2552 0 : strcat( szTemp, "#" );
2553 0 : strcat( szTemp, "Westernmost_Easting");
2554 0 : pszValue = CSLFetchNameValue(poDS->papszMetadata, szTemp);
2555 :
2556 0 : if( pszValue != NULL ) {
2557 0 : dfWE = atof( pszValue );
2558 0 : bGotWE = TRUE;
2559 : }
2560 :
2561 : /* Only set the GeoTransform if we got all the values */
2562 0 : if ( bGotNN && bGotSN && bGotEE && bGotWE ) {
2563 0 : adfTempGeoTransform[0] = dfWE;
2564 : adfTempGeoTransform[1] = (dfEE - dfWE) /
2565 0 : ( poDS->GetRasterXSize() - 1 );
2566 0 : adfTempGeoTransform[2] = 0.0;
2567 0 : adfTempGeoTransform[3] = dfNN;
2568 0 : adfTempGeoTransform[4] = 0.0;
2569 : adfTempGeoTransform[5] = (dfSN - dfNN) /
2570 0 : ( poDS->GetRasterYSize() - 1 );
2571 : /* -------------------------------------------------------------------- */
2572 : /* Compute the center of the pixel */
2573 : /* -------------------------------------------------------------------- */
2574 : adfTempGeoTransform[0] = dfWE
2575 0 : - (adfTempGeoTransform[1] / 2);
2576 :
2577 : adfTempGeoTransform[3] = dfNN
2578 0 : - (adfTempGeoTransform[5] / 2);
2579 :
2580 :
2581 0 : bGotGeoTransform = TRUE;
2582 : }
2583 : } // (pszGeoTransform != NULL)
2584 0 : CSLDestroy( papszGeoTransform );
2585 :
2586 : /* Issue a warning if we did not get a geotransform from GDAL */
2587 0 : if ( !bGotGeoTransform ) {
2588 0 : CPLError(CE_Warning, 1,"got SRS but not geotransform from GDAL!");
2589 : }
2590 : } // (!bGotGeoTransform)
2591 : }
2592 : }
2593 :
2594 166 : if ( bGotGeoTransform ) {
2595 : CPLDebug( "GDAL_netCDF", "Got GeoTransform:"
2596 : " %.16g, %.16g, %.16g"
2597 : " %.16g, %.16g, %.16g",
2598 : adfGeoTransform[0], adfGeoTransform[1],
2599 : adfGeoTransform[2], adfGeoTransform[3],
2600 139 : adfGeoTransform[4], adfGeoTransform[5] );
2601 : }
2602 :
2603 : /* -------------------------------------------------------------------- */
2604 : /* Search for Well-known GeogCS if got only CF WKT */
2605 : /* Disabled for now, as a named datum also include control points */
2606 : /* (see mailing list and bug#4281 */
2607 : /* For example, WGS84 vs. GDA94 (EPSG:3577) - AEA in netcdf_cf.py */
2608 : /* -------------------------------------------------------------------- */
2609 : /* disabled for now, but could be set in a config option */
2610 166 : bLookForWellKnownGCS = FALSE;
2611 166 : if ( bLookForWellKnownGCS && bGotCfSRS && ! bGotGdalSRS ) {
2612 : /* ET - could use a more exhaustive method by scanning all EPSG codes in data/gcs.csv */
2613 : /* as proposed by Even in the gdal-dev mailing list "help for comparing two WKT" */
2614 : /* this code could be contributed to a new function */
2615 : /* OGRSpatialReference * OGRSpatialReference::FindMatchingGeogCS( const OGRSpatialReference *poOther ) */
2616 0 : CPLDebug( "GDAL_netCDF", "Searching for Well-known GeogCS" );
2617 0 : const char *pszWKGCSList[] = { "WGS84", "WGS72", "NAD27", "NAD83" };
2618 0 : char *pszWKGCS = NULL;
2619 0 : oSRS.exportToPrettyWkt( &pszWKGCS );
2620 0 : for( size_t i=0; i<sizeof(pszWKGCSList)/8; i++ ) {
2621 0 : pszWKGCS = CPLStrdup( pszWKGCSList[i] );
2622 0 : OGRSpatialReference oSRSTmp;
2623 0 : oSRSTmp.SetWellKnownGeogCS( pszWKGCSList[i] );
2624 : /* set datum to unknown, bug #4281 */
2625 0 : if ( oSRSTmp.GetAttrNode( "DATUM" ) )
2626 0 : oSRSTmp.GetAttrNode( "DATUM" )->GetChild(0)->SetValue( "unknown" );
2627 : /* could use OGRSpatialReference::StripCTParms() but let's keep TOWGS84 */
2628 0 : oSRSTmp.GetRoot()->StripNodes( "AXIS" );
2629 0 : oSRSTmp.GetRoot()->StripNodes( "AUTHORITY" );
2630 0 : oSRSTmp.GetRoot()->StripNodes( "EXTENSION" );
2631 :
2632 0 : oSRSTmp.exportToPrettyWkt( &pszWKGCS );
2633 0 : if ( oSRS.IsSameGeogCS(&oSRSTmp) ) {
2634 0 : oSRS.SetWellKnownGeogCS( pszWKGCSList[i] );
2635 0 : oSRS.exportToWkt( &(pszTempProjection) );
2636 0 : SetProjection( pszTempProjection );
2637 0 : CPLFree( pszTempProjection );
2638 : }
2639 : }
2640 166 : }
2641 :
2642 :
2643 166 : }
2644 :
2645 : /************************************************************************/
2646 : /* SetProjection() */
2647 : /************************************************************************/
2648 334 : CPLErr netCDFDataset::SetProjection( const char * pszNewProjection )
2649 : {
2650 : /* TODO look if proj. already defined, like in geotiff */
2651 334 : if( pszNewProjection == NULL )
2652 : {
2653 0 : CPLError( CE_Failure, CPLE_AppDefined, "NULL projection." );
2654 0 : return CE_Failure;
2655 : }
2656 :
2657 334 : if( bSetProjection && (GetAccess() == GA_Update) )
2658 : {
2659 : CPLError( CE_Warning, CPLE_AppDefined,
2660 : "netCDFDataset::SetProjection() should only be called once "
2661 : "in update mode!\npszNewProjection=\n%s",
2662 0 : pszNewProjection );
2663 : }
2664 :
2665 334 : CPLDebug( "GDAL_netCDF", "SetProjection, WKT = %s", pszNewProjection );
2666 :
2667 334 : if( !EQUALN(pszNewProjection,"GEOGCS",6)
2668 : && !EQUALN(pszNewProjection,"PROJCS",6)
2669 : && !EQUAL(pszNewProjection,"") )
2670 : {
2671 : CPLError( CE_Failure, CPLE_AppDefined,
2672 : "Only OGC WKT GEOGCS and PROJCS Projections supported for writing to NetCDF.\n"
2673 : "%s not supported.",
2674 0 : pszNewProjection );
2675 :
2676 0 : return CE_Failure;
2677 : }
2678 :
2679 334 : CPLFree( pszProjection );
2680 334 : pszProjection = CPLStrdup( pszNewProjection );
2681 :
2682 334 : if( GetAccess() == GA_Update )
2683 : {
2684 84 : if ( bSetGeoTransform && ! bSetProjection ) {
2685 27 : bSetProjection = TRUE;
2686 27 : return AddProjectionVars();
2687 : }
2688 : }
2689 :
2690 307 : bSetProjection = TRUE;
2691 :
2692 307 : return CE_None;
2693 :
2694 : }
2695 :
2696 : /************************************************************************/
2697 : /* SetGeoTransform() */
2698 : /************************************************************************/
2699 :
2700 224 : CPLErr netCDFDataset::SetGeoTransform ( double * padfTransform )
2701 : {
2702 224 : memcpy( adfGeoTransform, padfTransform, sizeof(double)*6 );
2703 : // bGeoTransformValid = TRUE;
2704 : // bGeoTIFFInfoChanged = TRUE;
2705 :
2706 : CPLDebug( "GDAL_netCDF",
2707 : "SetGeoTransform(%f,%f,%f,%f,%f,%f)",
2708 : padfTransform[0],padfTransform[1],padfTransform[2],
2709 224 : padfTransform[3],padfTransform[4],padfTransform[5]);
2710 :
2711 224 : if( GetAccess() == GA_Update )
2712 : {
2713 85 : if ( bSetProjection && ! bSetGeoTransform ) {
2714 0 : bSetGeoTransform = TRUE;
2715 0 : return AddProjectionVars();
2716 : }
2717 : }
2718 :
2719 224 : bSetGeoTransform = TRUE;
2720 :
2721 224 : return CE_None;
2722 :
2723 : }
2724 :
2725 : /************************************************************************/
2726 : /* AddProjectionVars() */
2727 : /************************************************************************/
2728 :
2729 85 : CPLErr netCDFDataset::AddProjectionVars( GDALProgressFunc pfnProgress,
2730 : void * pProgressData )
2731 : {
2732 85 : OGRSpatialReference oSRS;
2733 85 : int NCDFVarID = -1;
2734 85 : double dfTemp = 0.0;
2735 85 : const char *pszValue = NULL;
2736 : char szTemp[ NCDF_MAX_STR_LEN ];
2737 :
2738 : char szGeoTransform[ NCDF_MAX_STR_LEN ];
2739 85 : *szGeoTransform = '\0';
2740 85 : char *pszWKT = NULL;
2741 :
2742 85 : int bWriteGridMapping = FALSE;
2743 85 : int bWriteLonLat = FALSE;
2744 85 : int bWriteGDALTags = FALSE;
2745 85 : int bWriteGeoTransform = FALSE;
2746 :
2747 85 : nc_type eLonLatType = NC_NAT;
2748 : // int nLonSize = nRasterXSize;
2749 : // int nLatSize = nRasterYSize;
2750 85 : int nVarLonID=-1, nVarLatID=-1;
2751 85 : int nVarXID=-1, nVarYID=-1;
2752 :
2753 : char szNetcdfProjection[ NC_MAX_NAME ];
2754 85 : szNetcdfProjection[0]='\0';
2755 :
2756 85 : bAddedProjectionVars = TRUE;
2757 :
2758 85 : pszWKT = (char *) pszProjection;
2759 85 : oSRS.importFromWkt( &pszWKT );
2760 :
2761 85 : if( oSRS.IsProjected() )
2762 41 : bIsProjected = TRUE;
2763 44 : else if( oSRS.IsGeographic() )
2764 39 : bIsGeographic = TRUE;
2765 :
2766 : CPLDebug( "GDAL_netCDF", "SetProjection, WKT now = [%s]\nprojected: %d geographic: %d",
2767 85 : pszProjection,bIsProjected,bIsGeographic );
2768 :
2769 85 : if ( ! bSetGeoTransform )
2770 : CPLError( CE_Warning, CPLE_AppDefined,
2771 0 : "netCDFDataset::DefineDims() called, but GeoTransform has not yet been defined!" );
2772 :
2773 85 : if ( ! bSetProjection )
2774 : CPLError( CE_Warning, CPLE_AppDefined,
2775 1 : "netCDFDataset::DefineDims() called, but Projection has not yet been defined!" );
2776 :
2777 : /* go through ProcessOptions again as defaults depend on projection type */
2778 : // ProcessCreationOptions( );
2779 :
2780 : /* process options */
2781 85 : if( bIsProjected )
2782 : {
2783 41 : int bIsCfProjection = NCDFIsCfProjection( oSRS.GetAttrValue( "PROJECTION" ) );
2784 41 : bWriteGridMapping = TRUE;
2785 41 : bWriteGDALTags = CSLFetchBoolean( papszCreationOptions, "WRITE_GDAL_TAGS", TRUE );
2786 : /* force WRITE_GDAL_TAGS if is not a CF projection */
2787 41 : if ( ! bWriteGDALTags && ! bIsCfProjection )
2788 0 : bWriteGDALTags = TRUE;
2789 41 : if ( bWriteGDALTags )
2790 41 : bWriteGeoTransform = TRUE;
2791 :
2792 41 : pszValue = CSLFetchNameValueDef(papszCreationOptions,"WRITE_LONLAT", "NO");
2793 41 : if ( EQUAL( pszValue, "IF_NEEDED" ) ) {
2794 0 : if ( bIsCfProjection )
2795 0 : bWriteLonLat = FALSE;
2796 : else
2797 0 : bWriteLonLat = TRUE;
2798 : }
2799 41 : else bWriteLonLat = CSLTestBoolean( pszValue );
2800 : // if ( bWriteLonLat == TRUE ) {
2801 : // nLonSize = nRasterXSize * nRasterYSize;
2802 : // nLatSize = nRasterXSize * nRasterYSize;
2803 : // }
2804 41 : eLonLatType = NC_FLOAT;
2805 41 : pszValue = CSLFetchNameValueDef(papszCreationOptions,"TYPE_LONLAT", "FLOAT");
2806 41 : if ( EQUAL(pszValue, "DOUBLE" ) )
2807 0 : eLonLatType = NC_DOUBLE;
2808 : }
2809 : else
2810 : {
2811 : /* files without a Datum will not have a grid_mapping variable and geographic information */
2812 44 : if ( bIsGeographic ) bWriteGridMapping = TRUE;
2813 5 : else bWriteGridMapping = FALSE;
2814 44 : bWriteGDALTags = CSLFetchBoolean( papszCreationOptions, "WRITE_GDAL_TAGS", bWriteGridMapping );
2815 44 : if ( bWriteGDALTags )
2816 39 : bWriteGeoTransform = TRUE;
2817 :
2818 44 : pszValue = CSLFetchNameValueDef(papszCreationOptions,"WRITE_LONLAT", "YES");
2819 44 : if ( EQUAL( pszValue, "IF_NEEDED" ) )
2820 0 : bWriteLonLat = TRUE;
2821 44 : else bWriteLonLat = CSLTestBoolean( pszValue );
2822 : /* Don't write lon/lat if no source geotransform */
2823 44 : if ( ! bSetGeoTransform )
2824 0 : bWriteLonLat = FALSE;
2825 : /* If we don't write lon/lat, set dimnames to X/Y and write gdal tags*/
2826 44 : if ( ! bWriteLonLat ) {
2827 : CPLError( CE_Warning, CPLE_AppDefined,
2828 0 : "creating geographic file without lon/lat values!");
2829 0 : if ( bSetGeoTransform ) {
2830 0 : bWriteGDALTags = TRUE; //not desireable if no geotransform
2831 0 : bWriteGeoTransform = TRUE;
2832 : }
2833 : // pszLonDimName = NCDF_DIMNAME_X;
2834 : // pszLatDimName = NCDF_DIMNAME_Y;
2835 : // bBottomUp = FALSE;
2836 : }
2837 :
2838 : // nLonSize = nRasterXSize;
2839 : // nLatSize = nRasterYSize;
2840 :
2841 44 : eLonLatType = NC_DOUBLE;
2842 44 : pszValue = CSLFetchNameValueDef(papszCreationOptions,"TYPE_LONLAT", "DOUBLE");
2843 44 : if ( EQUAL(pszValue, "FLOAT" ) )
2844 0 : eLonLatType = NC_FLOAT;
2845 : }
2846 :
2847 : /* make sure we write grid_mapping if we need to write GDAL tags */
2848 85 : if ( bWriteGDALTags ) bWriteGridMapping = TRUE;
2849 :
2850 : CPLDebug( "GDAL_netCDF",
2851 : "bIsProjected=%d bIsGeographic=%d bWriteGridMapping=%d bWriteGDALTags=%d bWriteLonLat=%d bBottomUp=%d",
2852 85 : bIsProjected,bIsGeographic,bWriteGridMapping,bWriteGDALTags,bWriteLonLat,bBottomUp );
2853 :
2854 :
2855 : /* -------------------------------------------------------------------- */
2856 : /* Define dimension names */
2857 : /* -------------------------------------------------------------------- */
2858 : /* make sure we are in define mode */
2859 85 : SetDefineMode( TRUE );
2860 :
2861 :
2862 : /* -------------------------------------------------------------------- */
2863 : /* Rename dimensions if lon/lat */
2864 : /* -------------------------------------------------------------------- */
2865 85 : if( ! bIsProjected )
2866 : {
2867 : /* rename dims to lat/lon */
2868 44 : papszDimName.Clear(); //if we add other dims one day, this has to change
2869 44 : papszDimName.AddString( NCDF_DIMNAME_LAT );
2870 44 : papszDimName.AddString( NCDF_DIMNAME_LON );
2871 :
2872 44 : status = nc_rename_dim(cdfid, nYDimID, NCDF_DIMNAME_LAT );
2873 44 : NCDF_ERR(status);
2874 44 : status = nc_rename_dim(cdfid, nXDimID, NCDF_DIMNAME_LON );
2875 44 : NCDF_ERR(status);
2876 : // anBandDims[0] = nLatDimID;
2877 : // anBandDims[1] = nLonDimID;
2878 : }
2879 :
2880 : /* -------------------------------------------------------------------- */
2881 : /* Write projection attributes */
2882 : /* -------------------------------------------------------------------- */
2883 85 : if( bIsProjected )
2884 : {
2885 : /* -------------------------------------------------------------------- */
2886 : /* Write CF-1.5 compliant Projected attributes */
2887 : /* -------------------------------------------------------------------- */
2888 :
2889 41 : const OGR_SRSNode *poPROJCS = oSRS.GetAttrNode( "PROJCS" );
2890 : const char *pszProjName;
2891 41 : pszProjName = oSRS.GetAttrValue( "PROJECTION" );
2892 :
2893 : /* Basic Projection info (grid_mapping and datum) */
2894 823 : for( int i=0; poNetcdfSRS_PT[i].WKT_SRS != NULL; i++ ) {
2895 823 : if( EQUAL( poNetcdfSRS_PT[i].WKT_SRS, pszProjName ) ) {
2896 : CPLDebug( "GDAL_netCDF", "GDAL PROJECTION = %s , NCDF PROJECTION = %s",
2897 : poNetcdfSRS_PT[i].WKT_SRS,
2898 41 : poNetcdfSRS_PT[i].CF_SRS);
2899 41 : strcpy( szNetcdfProjection, poNetcdfSRS_PT[i].CF_SRS );
2900 : CPLDebug( "GDAL_netCDF", "nc_def_var(%d,%s,%d)",
2901 41 : cdfid, poNetcdfSRS_PT[i].CF_SRS, NC_CHAR );
2902 : status = nc_def_var( cdfid,
2903 : poNetcdfSRS_PT[i].CF_SRS,
2904 : NC_CHAR,
2905 41 : 0, NULL, &NCDFVarID );
2906 41 : NCDF_ERR(status);
2907 41 : break;
2908 : }
2909 : }
2910 : nc_put_att_text( cdfid, NCDFVarID, CF_GRD_MAPPING_NAME,
2911 : strlen( szNetcdfProjection ),
2912 41 : szNetcdfProjection );
2913 :
2914 : /* Various projection attributes */
2915 : // PDS: keep in synch with SetProjection function
2916 41 : NCDFWriteProjAttribs(poPROJCS, pszProjName, cdfid, NCDFVarID);
2917 :
2918 : }
2919 : else
2920 : {
2921 : /* -------------------------------------------------------------------- */
2922 : /* Write CF-1.5 compliant Geographics attributes */
2923 : /* Note: WKT information will not be preserved (e.g. WGS84) */
2924 : /* -------------------------------------------------------------------- */
2925 :
2926 44 : if( bWriteGridMapping == TRUE )
2927 : {
2928 39 : strcpy( szNetcdfProjection, "crs" );
2929 : CPLDebug( "GDAL_netCDF", "nc_def_var(%d,%s,%d)",
2930 39 : cdfid, szNetcdfProjection, NC_CHAR );
2931 : nc_def_var( cdfid, szNetcdfProjection, NC_CHAR,
2932 39 : 0, NULL, &NCDFVarID );
2933 39 : NCDF_ERR(status);
2934 : nc_put_att_text( cdfid, NCDFVarID, CF_GRD_MAPPING_NAME,
2935 : strlen(CF_PT_LATITUDE_LONGITUDE),
2936 39 : CF_PT_LATITUDE_LONGITUDE );
2937 : }
2938 :
2939 : }
2940 :
2941 : /* -------------------------------------------------------------------- */
2942 : /* Write CF-1.5 compliant common attributes */
2943 : /* -------------------------------------------------------------------- */
2944 :
2945 : /* DATUM information */
2946 85 : dfTemp = oSRS.GetPrimeMeridian();
2947 : nc_put_att_double( cdfid, NCDFVarID, CF_PP_LONG_PRIME_MERIDIAN,
2948 85 : NC_DOUBLE, 1, &dfTemp );
2949 85 : dfTemp = oSRS.GetSemiMajor();
2950 : nc_put_att_double( cdfid, NCDFVarID, CF_PP_SEMI_MAJOR_AXIS,
2951 85 : NC_DOUBLE, 1, &dfTemp );
2952 85 : dfTemp = oSRS.GetInvFlattening();
2953 : nc_put_att_double( cdfid, NCDFVarID, CF_PP_INVERSE_FLATTENING,
2954 85 : NC_DOUBLE, 1, &dfTemp );
2955 :
2956 :
2957 : /* Optional GDAL custom projection tags */
2958 85 : if ( bWriteGDALTags == TRUE ) {
2959 :
2960 80 : *szGeoTransform = '\0';
2961 560 : for( int i=0; i<6; i++ ) {
2962 : sprintf( szTemp, "%.16g ",
2963 480 : adfGeoTransform[i] );
2964 480 : strcat( szGeoTransform, szTemp );
2965 : }
2966 80 : CPLDebug( "GDAL_netCDF", "szGeoTranform = %s", szGeoTransform );
2967 :
2968 : // if ( strlen(pszProj4Defn) > 0 ) {
2969 : // nc_put_att_text( cdfid, NCDFVarID, "proj4",
2970 : // strlen( pszProj4Defn ), pszProj4Defn );
2971 : // }
2972 : nc_put_att_text( cdfid, NCDFVarID, NCDF_SPATIAL_REF,
2973 80 : strlen( pszProjection ), pszProjection );
2974 : /* for now write the geotransform for back-compat or else
2975 : the old (1.8.1) driver overrides the CF geotransform with
2976 : empty values from dfNN, dfSN, dfEE, dfWE; */
2977 : /* TODO: fix this in 1.8 branch */
2978 80 : if ( bWriteGeoTransform && bSetGeoTransform ) {
2979 : nc_put_att_text( cdfid, NCDFVarID, NCDF_GEOTRANSFORM,
2980 : strlen( szGeoTransform ),
2981 80 : szGeoTransform );
2982 : }
2983 : }
2984 :
2985 : /* -------------------------------------------------------------------- */
2986 : /* Write Projection var in Bands */
2987 : /* -------------------------------------------------------------------- */
2988 85 : if( bWriteGridMapping == TRUE ) {
2989 199 : for( int i=1; i <= nBands; i++ ) {
2990 : netCDFRasterBand *poSrcBand =
2991 119 : (netCDFRasterBand *) GetRasterBand( i );
2992 : status = nc_put_att_text( cdfid, poSrcBand->nZId,
2993 : CF_GRD_MAPPING,
2994 : strlen( szNetcdfProjection ),
2995 119 : szNetcdfProjection );
2996 119 : NCDF_ERR(status);
2997 119 : if ( bWriteLonLat == TRUE ) {
2998 : status = nc_put_att_text( cdfid, poSrcBand->nZId,
2999 : CF_COORDINATES,
3000 78 : strlen( NCDF_LONLAT ), NCDF_LONLAT );
3001 78 : NCDF_ERR(status);
3002 : }
3003 : }
3004 : }
3005 :
3006 85 : pfnProgress( 0.10, NULL, pProgressData );
3007 :
3008 : /* -------------------------------------------------------------------- */
3009 : /* Write CF Projection vars */
3010 : /* -------------------------------------------------------------------- */
3011 :
3012 : /* -------------------------------------------------------------------- */
3013 : /* Write X/Y attributes */
3014 : /* -------------------------------------------------------------------- */
3015 85 : if( bIsProjected )
3016 : {
3017 : /* X */
3018 : int anXDims[1];
3019 41 : anXDims[0] = nXDimID;
3020 : CPLDebug( "GDAL_netCDF", "nc_def_var(%d,%s,%d)",
3021 41 : cdfid, NCDF_DIMNAME_X, NC_DOUBLE );
3022 : status = nc_def_var( cdfid, NCDF_DIMNAME_X, NC_DOUBLE,
3023 41 : 1, anXDims, &NCDFVarID );
3024 41 : NCDF_ERR(status);
3025 41 : nVarXID=NCDFVarID;
3026 : nc_put_att_text( cdfid, NCDFVarID, CF_STD_NAME,
3027 : strlen(CF_PROJ_X_COORD),
3028 41 : CF_PROJ_X_COORD );
3029 : nc_put_att_text( cdfid, NCDFVarID, CF_LNG_NAME,
3030 : strlen("x coordinate of projection"),
3031 41 : "x coordinate of projection" );
3032 : /* TODO verify this is ok */
3033 41 : nc_put_att_text( cdfid, NCDFVarID, CF_UNITS, 1, "m" );
3034 :
3035 : /* Y */
3036 : int anYDims[1];
3037 41 : anYDims[0] = nYDimID;
3038 : CPLDebug( "GDAL_netCDF", "nc_def_var(%d,%s,%d)",
3039 41 : cdfid, NCDF_DIMNAME_Y, NC_DOUBLE );
3040 : status = nc_def_var( cdfid, NCDF_DIMNAME_Y, NC_DOUBLE,
3041 41 : 1, anYDims, &NCDFVarID );
3042 41 : NCDF_ERR(status);
3043 41 : nVarYID=NCDFVarID;
3044 : nc_put_att_text( cdfid, NCDFVarID, CF_STD_NAME,
3045 : strlen(CF_PROJ_Y_COORD),
3046 41 : CF_PROJ_Y_COORD );
3047 : nc_put_att_text( cdfid, NCDFVarID, CF_LNG_NAME,
3048 : strlen("y coordinate of projection"),
3049 41 : "y coordinate of projection" );
3050 41 : nc_put_att_text( cdfid, NCDFVarID, CF_UNITS, 1, "m" );
3051 : }
3052 :
3053 : /* -------------------------------------------------------------------- */
3054 : /* Write lat/lon attributes if needed */
3055 : /* -------------------------------------------------------------------- */
3056 85 : if ( bWriteLonLat == TRUE ) {
3057 :
3058 : /* latitude attributes */
3059 44 : if ( bIsProjected ) {
3060 : int anLatDims[2];
3061 0 : anLatDims[0] = nYDimID;
3062 0 : anLatDims[1] = nXDimID;
3063 : CPLDebug( "GDAL_netCDF", "nc_def_var(%d,%s,%d)",
3064 0 : cdfid, NCDF_DIMNAME_LAT, eLonLatType );
3065 : status = nc_def_var( cdfid, NCDF_DIMNAME_LAT, eLonLatType,
3066 0 : 2, anLatDims, &NCDFVarID );
3067 0 : NCDF_ERR(status);
3068 : /* compress lon/lat to save space */
3069 0 : DefVarDeflate( NCDFVarID );
3070 : }
3071 : else {
3072 : int anLatDims[1];
3073 : // anLatDims[0] = nLatDimID;
3074 44 : anLatDims[0] = nYDimID;
3075 : CPLDebug( "GDAL_netCDF", "nc_def_var(%d,%s,%d)",
3076 44 : cdfid, NCDF_DIMNAME_LAT, eLonLatType );
3077 : status = nc_def_var( cdfid, NCDF_DIMNAME_LAT, eLonLatType,
3078 44 : 1, anLatDims, &NCDFVarID );
3079 44 : NCDF_ERR(status);
3080 : }
3081 44 : nVarLatID = NCDFVarID;
3082 : nc_put_att_text( cdfid, NCDFVarID, CF_STD_NAME,
3083 44 : 8,"latitude" );
3084 : nc_put_att_text( cdfid, NCDFVarID, CF_LNG_NAME,
3085 44 : 8, "latitude" );
3086 : nc_put_att_text( cdfid, NCDFVarID, CF_UNITS,
3087 44 : 13, "degrees_north" );
3088 :
3089 : /* longitude attributes */
3090 44 : if ( bIsProjected ) {
3091 : int anLonDims[2];
3092 0 : anLonDims[0] = nYDimID;
3093 0 : anLonDims[1] = nXDimID;
3094 : CPLDebug( "GDAL_netCDF", "nc_def_var(%d,%s,%d)",
3095 0 : cdfid, NCDF_DIMNAME_LON, eLonLatType );
3096 : status = nc_def_var( cdfid, NCDF_DIMNAME_LON, eLonLatType,
3097 0 : 2, anLonDims, &NCDFVarID );
3098 0 : NCDF_ERR(status);
3099 : /* compress lon/lat to save space */
3100 0 : DefVarDeflate( NCDFVarID );
3101 : }
3102 : else {
3103 : int anLonDims[1];
3104 : // anLonDims[0] = nLonDimID;
3105 44 : anLonDims[0] = nXDimID;
3106 : CPLDebug( "GDAL_netCDF", "nc_def_var(%d,%s,%d)",
3107 44 : cdfid, NCDF_DIMNAME_LON, eLonLatType );
3108 : status = nc_def_var( cdfid, NCDF_DIMNAME_LON, eLonLatType,
3109 44 : 1, anLonDims, &NCDFVarID );
3110 44 : NCDF_ERR(status);
3111 : }
3112 44 : nVarLonID = NCDFVarID;
3113 : nc_put_att_text( cdfid, NCDFVarID, CF_STD_NAME,
3114 44 : 9, "longitude" );
3115 : nc_put_att_text( cdfid, NCDFVarID, CF_LNG_NAME,
3116 44 : 9, "longitude" );
3117 : nc_put_att_text( cdfid, NCDFVarID, CF_UNITS,
3118 44 : 12, "degrees_east" );
3119 : }
3120 :
3121 85 : pfnProgress( 0.50, NULL, pProgressData );
3122 :
3123 : /* -------------------------------------------------------------------- */
3124 : /* Get projection values */
3125 : /* -------------------------------------------------------------------- */
3126 :
3127 : double dfX0, dfDX, dfY0, dfDY;
3128 85 : dfX0=0.0, dfDX=0.0, dfY0=0.0, dfDY=0.0;
3129 85 : double *padLonVal = NULL;
3130 85 : double *padLatVal = NULL; /* should use float for projected, save space */
3131 :
3132 85 : if( bIsProjected )
3133 : {
3134 : // const char *pszProjection;
3135 41 : OGRSpatialReference oSRS;
3136 41 : OGRSpatialReference *poLatLonCRS = NULL;
3137 41 : OGRCoordinateTransformation *poTransform = NULL;
3138 :
3139 41 : char *pszWKT = (char *) pszProjection;
3140 41 : oSRS.importFromWkt( &pszWKT );
3141 :
3142 41 : double *padYVal = NULL;
3143 41 : double *padXVal = NULL;
3144 : size_t startX[1];
3145 : size_t countX[1];
3146 : size_t startY[1];
3147 : size_t countY[1];
3148 :
3149 41 : CPLDebug("GDAL_netCDF", "Getting (X,Y) values" );
3150 :
3151 41 : padXVal = (double *) CPLMalloc( nRasterXSize * sizeof( double ) );
3152 41 : padYVal = (double *) CPLMalloc( nRasterYSize * sizeof( double ) );
3153 :
3154 : /* -------------------------------------------------------------------- */
3155 : /* Get Y values */
3156 : /* -------------------------------------------------------------------- */
3157 41 : if ( ! bBottomUp )
3158 41 : dfY0 = adfGeoTransform[3];
3159 : else /* invert latitude values */
3160 0 : dfY0 = adfGeoTransform[3] + ( adfGeoTransform[5] * nRasterYSize );
3161 41 : dfDY = adfGeoTransform[5];
3162 :
3163 3015 : for( int j=0; j<nRasterYSize; j++ ) {
3164 : /* The data point is centered inside the pixel */
3165 2974 : if ( ! bBottomUp )
3166 2974 : padYVal[j] = dfY0 + (j+0.5)*dfDY ;
3167 : else /* invert latitude values */
3168 0 : padYVal[j] = dfY0 - (j+0.5)*dfDY ;
3169 : }
3170 41 : startX[0] = 0;
3171 41 : countX[0] = nRasterXSize;
3172 :
3173 : /* -------------------------------------------------------------------- */
3174 : /* Get X values */
3175 : /* -------------------------------------------------------------------- */
3176 41 : dfX0 = adfGeoTransform[0];
3177 41 : dfDX = adfGeoTransform[1];
3178 :
3179 6585 : for( int i=0; i<nRasterXSize; i++ ) {
3180 : /* The data point is centered inside the pixel */
3181 6544 : padXVal[i] = dfX0 + (i+0.5)*dfDX ;
3182 : }
3183 41 : startY[0] = 0;
3184 41 : countY[0] = nRasterYSize;
3185 :
3186 : /* -------------------------------------------------------------------- */
3187 : /* Write X/Y values */
3188 : /* -------------------------------------------------------------------- */
3189 : /* make sure we are in data mode */
3190 41 : SetDefineMode( FALSE );
3191 :
3192 41 : CPLDebug("GDAL_netCDF", "Writing X values" );
3193 : status = nc_put_vara_double( cdfid, nVarXID, startX,
3194 41 : countX, padXVal);
3195 41 : NCDF_ERR(status);
3196 :
3197 41 : CPLDebug("GDAL_netCDF", "Writing Y values" );
3198 : status = nc_put_vara_double( cdfid, nVarYID, startY,
3199 41 : countY, padYVal);
3200 41 : NCDF_ERR(status);
3201 :
3202 41 : pfnProgress( 0.20, NULL, pProgressData );
3203 :
3204 : /* -------------------------------------------------------------------- */
3205 : /* Transform (X,Y) values to (lon,lat) */
3206 : /* -------------------------------------------------------------------- */
3207 :
3208 : /* Get OGR transform */
3209 41 : if ( bWriteLonLat == TRUE ) {
3210 0 : poLatLonCRS = oSRS.CloneGeogCS();
3211 0 : if ( poLatLonCRS != NULL )
3212 0 : poTransform = OGRCreateCoordinateTransformation( &oSRS, poLatLonCRS );
3213 : /* if no OGR transform, then don't write CF lon/lat */
3214 0 : if( poTransform == NULL ) {
3215 : CPLError( CE_Failure, CPLE_AppDefined,
3216 0 : "Unable to get Coordinate Transform" );
3217 0 : bWriteLonLat = FALSE;
3218 : }
3219 : }
3220 :
3221 41 : if ( bWriteLonLat == TRUE ) {
3222 :
3223 0 : CPLDebug("GDAL_netCDF", "Transforming (X,Y)->(lon,lat)" );
3224 :
3225 0 : padLatVal = (double *) CPLMalloc( nRasterXSize * sizeof( double ) );
3226 0 : padLonVal = (double *) CPLMalloc( nRasterXSize * sizeof( double ) );
3227 : size_t start[2], count[2];
3228 0 : start[1] = 0; //X
3229 0 : count[1] = nRasterXSize;
3230 0 : start[0] = 0; //Y
3231 0 : count[0] = 1;
3232 :
3233 0 : int bOK = TRUE;
3234 : double dfTemp;
3235 : int i,j;
3236 0 : dfTemp = 0.2;
3237 :
3238 0 : for( j = 0; (j < nRasterYSize) && bOK && (status == NC_NOERR); j++ ) {
3239 :
3240 0 : start[0] = j;
3241 :
3242 : /* fill values to transform */
3243 0 : for( i=0; i<nRasterXSize; i++ ) {
3244 0 : padLatVal[i] = padYVal[j];
3245 0 : padLonVal[i] = padXVal[i];
3246 : }
3247 :
3248 : /* do the transform */
3249 : bOK = poTransform->Transform( nRasterXSize,
3250 0 : padLonVal, padLatVal, NULL );
3251 : /* write data */
3252 0 : if ( bOK ) {
3253 : status = nc_put_vara_double( cdfid, nVarLatID, start,
3254 0 : count, padLatVal);
3255 0 : NCDF_ERR(status);
3256 : status = nc_put_vara_double( cdfid, nVarLonID, start,
3257 0 : count, padLonVal);
3258 0 : NCDF_ERR(status);
3259 : }
3260 : else
3261 : CPLError( CE_Failure, CPLE_AppDefined,
3262 0 : "Unable to Transform (X,Y) to (lon,lat).\n" );
3263 :
3264 0 : if ( j % (nRasterYSize/10) == 0 ) {
3265 0 : dfTemp += 0.08;
3266 0 : pfnProgress( dfTemp , NULL, pProgressData );
3267 : }
3268 : }
3269 :
3270 : }
3271 :
3272 : /* Free the srs and transform objects */
3273 41 : if ( poLatLonCRS != NULL ) CPLFree( poLatLonCRS );
3274 41 : if ( poTransform != NULL ) CPLFree( poTransform );
3275 :
3276 : /* Free data */
3277 41 : CPLFree( padXVal );
3278 41 : CPLFree( padYVal );
3279 41 : CPLFree( padLonVal );
3280 41 : CPLFree( padLatVal);
3281 :
3282 : } // projected
3283 :
3284 : else { /* If not Projected assume Geographic to catch grids without Datum */
3285 :
3286 : /* -------------------------------------------------------------------- */
3287 : /* Get latitude values */
3288 : /* -------------------------------------------------------------------- */
3289 44 : if ( ! bBottomUp )
3290 44 : dfY0 = adfGeoTransform[3];
3291 : else /* invert latitude values */
3292 0 : dfY0 = adfGeoTransform[3] + ( adfGeoTransform[5] * nRasterYSize );
3293 44 : dfDY = adfGeoTransform[5];
3294 :
3295 44 : padLatVal = (double *) CPLMalloc( nRasterYSize * sizeof( double ) );
3296 2977 : for( int i=0; i<nRasterYSize; i++ ) {
3297 : /* The data point is centered inside the pixel */
3298 2933 : if ( ! bBottomUp )
3299 2933 : padLatVal[i] = dfY0 + (i+0.5)*dfDY ;
3300 : else /* invert latitude values */
3301 0 : padLatVal[i] = dfY0 - (i+0.5)*dfDY ;
3302 : }
3303 :
3304 : size_t startLat[1];
3305 : size_t countLat[1];
3306 44 : startLat[0] = 0;
3307 44 : countLat[0] = nRasterYSize;
3308 :
3309 : /* -------------------------------------------------------------------- */
3310 : /* Get longitude values */
3311 : /* -------------------------------------------------------------------- */
3312 44 : dfX0 = adfGeoTransform[0];
3313 44 : dfDX = adfGeoTransform[1];
3314 :
3315 44 : padLonVal = (double *) CPLMalloc( nRasterXSize * sizeof( double ) );
3316 2977 : for( int i=0; i<nRasterXSize; i++ ) {
3317 : /* The data point is centered inside the pixel */
3318 2933 : padLonVal[i] = dfX0 + (i+0.5)*dfDX ;
3319 : }
3320 :
3321 : size_t startLon[1];
3322 : size_t countLon[1];
3323 44 : startLon[0] = 0;
3324 44 : countLon[0] = nRasterXSize;
3325 :
3326 : /* -------------------------------------------------------------------- */
3327 : /* Write latitude and longitude values */
3328 : /* -------------------------------------------------------------------- */
3329 : /* latitude values */
3330 44 : CPLDebug("GDAL_netCDF", "Writing lat values" );
3331 :
3332 : /* make sure we are in data mode */
3333 44 : SetDefineMode( FALSE );
3334 :
3335 : status = nc_put_vara_double( cdfid, nVarLatID, startLat,
3336 44 : countLat, padLatVal);
3337 44 : NCDF_ERR(status);
3338 :
3339 : /* free values */
3340 44 : CPLFree( padLatVal );
3341 :
3342 : /* longitude values */
3343 44 : CPLDebug("GDAL_netCDF", "Writing lon values" );
3344 : status = nc_put_vara_double( cdfid, nVarLonID, startLon,
3345 44 : countLon, padLonVal);
3346 44 : NCDF_ERR(status);
3347 :
3348 : /* free values */
3349 44 : CPLFree( padLonVal );
3350 :
3351 : }// not projected
3352 :
3353 :
3354 85 : pfnProgress( 1.00, NULL, pProgressData );
3355 :
3356 85 : return CE_None;
3357 : }
3358 :
3359 : /************************************************************************/
3360 : /* GetGeoTransform() */
3361 : /************************************************************************/
3362 :
3363 61 : CPLErr netCDFDataset::GetGeoTransform( double * padfTransform )
3364 :
3365 : {
3366 61 : memcpy( padfTransform, adfGeoTransform, sizeof(double) * 6 );
3367 61 : if( bSetGeoTransform )
3368 61 : return CE_None;
3369 : else
3370 0 : return GDALPamDataset::GetGeoTransform( padfTransform );
3371 : }
3372 :
3373 : /************************************************************************/
3374 : /* rint() */
3375 : /************************************************************************/
3376 :
3377 837 : double netCDFDataset::rint( double dfX)
3378 : {
3379 837 : if( dfX > 0 ) {
3380 402 : int nX = (int) (dfX+0.5);
3381 402 : if( nX % 2 ) {
3382 10 : double dfDiff = dfX - (double)nX;
3383 10 : if( dfDiff == -0.5 )
3384 0 : return double( nX-1 );
3385 : }
3386 402 : return double( nX );
3387 : } else {
3388 435 : int nX= (int) (dfX-0.5);
3389 435 : if( nX % 2 ) {
3390 21 : double dfDiff = dfX - (double)nX;
3391 21 : if( dfDiff == 0.5 )
3392 0 : return double(nX+1);
3393 : }
3394 435 : return double(nX);
3395 : }
3396 : }
3397 :
3398 : /************************************************************************/
3399 : /* ReadAttributes() */
3400 : /************************************************************************/
3401 757 : CPLErr netCDFDataset::ReadAttributes( int cdfid, int var)
3402 :
3403 : {
3404 : char szAttrName[ NC_MAX_NAME ];
3405 : char szVarName [ NC_MAX_NAME ];
3406 : char szMetaName[ NC_MAX_NAME * 2 ];
3407 757 : char *pszMetaTemp = NULL;
3408 : int nbAttr;
3409 :
3410 757 : nc_inq_varnatts( cdfid, var, &nbAttr );
3411 757 : if( var == NC_GLOBAL ) {
3412 185 : strcpy( szVarName,"NC_GLOBAL" );
3413 : }
3414 : else {
3415 572 : nc_inq_varname( cdfid, var, szVarName );
3416 : }
3417 :
3418 4431 : for( int l=0; l < nbAttr; l++) {
3419 :
3420 3674 : nc_inq_attname( cdfid, var, l, szAttrName);
3421 3674 : sprintf( szMetaName, "%s#%s", szVarName, szAttrName );
3422 :
3423 3674 : if ( NCDFGetAttr( cdfid, var, szAttrName, &pszMetaTemp )
3424 : == CE_None ) {
3425 : papszMetadata = CSLSetNameValue(papszMetadata,
3426 : szMetaName,
3427 3674 : pszMetaTemp);
3428 3674 : CPLFree(pszMetaTemp);
3429 3674 : pszMetaTemp = NULL;
3430 : }
3431 : else {
3432 0 : CPLDebug( "GDAL_netCDF", "invalid global metadata %s", szMetaName );
3433 : }
3434 :
3435 : }
3436 :
3437 757 : return CE_None;
3438 :
3439 : }
3440 :
3441 :
3442 : /************************************************************************/
3443 : /* netCDFDataset::CreateSubDatasetList() */
3444 : /************************************************************************/
3445 18 : void netCDFDataset::CreateSubDatasetList( )
3446 : {
3447 :
3448 : char szDim[ MAX_NC_NAME ];
3449 : char szTemp[ MAX_NC_NAME ];
3450 : char szType[ MAX_NC_NAME ];
3451 : char szName[ MAX_NC_NAME ];
3452 : char szVarStdName[ MAX_NC_NAME ];
3453 : int nDims;
3454 : int nVar;
3455 : int nVarCount;
3456 : int i;
3457 : nc_type nVarType;
3458 : int *ponDimIds;
3459 : size_t nDimLen;
3460 : int nSub;
3461 : nc_type nAttype;
3462 : size_t nAttlen;
3463 :
3464 : netCDFDataset *poDS;
3465 18 : poDS = this;
3466 :
3467 18 : nSub=1;
3468 18 : nc_inq_nvars ( cdfid, &nVarCount );
3469 130 : for ( nVar = 0; nVar < nVarCount; nVar++ ) {
3470 :
3471 112 : nc_inq_varndims ( cdfid, nVar, &nDims );
3472 112 : if( nDims >= 2 ) {
3473 58 : ponDimIds = (int *) CPLCalloc( nDims, sizeof( int ) );
3474 58 : nc_inq_vardimid ( cdfid, nVar, ponDimIds );
3475 :
3476 : /* -------------------------------------------------------------------- */
3477 : /* Create Sub dataset list */
3478 : /* -------------------------------------------------------------------- */
3479 58 : szDim[0]='\0';
3480 174 : for( i = 0; i < nDims; i++ ) {
3481 116 : nc_inq_dimlen ( cdfid, ponDimIds[i], &nDimLen );
3482 116 : sprintf(szTemp, "%d", (int) nDimLen);
3483 116 : strcat(szTemp, "x" );
3484 116 : strcat(szDim, szTemp);
3485 : }
3486 :
3487 58 : nc_inq_vartype( cdfid, nVar, &nVarType );
3488 : /* -------------------------------------------------------------------- */
3489 : /* Get rid of the last "x" character */
3490 : /* -------------------------------------------------------------------- */
3491 58 : szDim[strlen(szDim) - 1] = '\0';
3492 58 : switch( nVarType ) {
3493 :
3494 : case NC_BYTE:
3495 : #ifdef NETCDF_HAS_NC4
3496 : case NC_UBYTE:
3497 : #endif
3498 : case NC_CHAR:
3499 28 : strcpy(szType, "8-bit character");
3500 28 : break;
3501 :
3502 : case NC_SHORT:
3503 3 : strcpy(szType, "8-bit integer");
3504 3 : break;
3505 : case NC_INT:
3506 3 : strcpy(szType, "16-bit integer");
3507 3 : break;
3508 : case NC_FLOAT:
3509 21 : strcpy(szType, "32-bit floating-point");
3510 21 : break;
3511 : case NC_DOUBLE:
3512 3 : strcpy(szType, "64-bit floating-point");
3513 : break;
3514 :
3515 : default:
3516 : break;
3517 : }
3518 58 : nc_inq_varname( cdfid, nVar, szName);
3519 58 : nc_inq_att( cdfid, nVar, CF_STD_NAME, &nAttype, &nAttlen);
3520 58 : if( nc_get_att_text ( cdfid, nVar, CF_STD_NAME,
3521 : szVarStdName ) == NC_NOERR ) {
3522 0 : szVarStdName[nAttlen] = '\0';
3523 : }
3524 : else {
3525 58 : strcpy( szVarStdName, szName );
3526 : }
3527 :
3528 58 : sprintf( szTemp, "SUBDATASET_%d_NAME", nSub) ;
3529 :
3530 : poDS->papszSubDatasets =
3531 : CSLSetNameValue( poDS->papszSubDatasets, szTemp,
3532 : CPLSPrintf( "NETCDF:\"%s\":%s",
3533 : poDS->osFilename.c_str(),
3534 58 : szName) ) ;
3535 :
3536 58 : sprintf( szTemp, "SUBDATASET_%d_DESC", nSub++ );
3537 :
3538 : poDS->papszSubDatasets =
3539 : CSLSetNameValue( poDS->papszSubDatasets, szTemp,
3540 : CPLSPrintf( "[%s] %s (%s)",
3541 : szDim,
3542 : szVarStdName,
3543 58 : szType ) );
3544 58 : CPLFree(ponDimIds);
3545 : }
3546 : }
3547 :
3548 18 : }
3549 :
3550 : /************************************************************************/
3551 : /* IdentifyFormat() */
3552 : /************************************************************************/
3553 :
3554 12247 : int netCDFDataset::IdentifyFormat( GDALOpenInfo * poOpenInfo, bool bCheckExt = TRUE )
3555 :
3556 : {
3557 : /* -------------------------------------------------------------------- */
3558 : /* Does this appear to be a netcdf file? If so, which format? */
3559 : /* http://www.unidata.ucar.edu/software/netcdf/docs/faq.html#fv1_5 */
3560 : /* -------------------------------------------------------------------- */
3561 :
3562 12247 : if( EQUALN(poOpenInfo->pszFilename,"NETCDF:",7) )
3563 0 : return NCDF_FORMAT_UNKNOWN;
3564 12247 : if ( poOpenInfo->nHeaderBytes < 4 )
3565 10860 : return NCDF_FORMAT_NONE;
3566 1387 : if ( EQUALN((char*)poOpenInfo->pabyHeader,"CDF\001",4) )
3567 184 : return NCDF_FORMAT_NC;
3568 1203 : else if ( EQUALN((char*)poOpenInfo->pabyHeader,"CDF\002",4) )
3569 1 : return NCDF_FORMAT_NC2;
3570 1202 : else if ( EQUALN((char*)poOpenInfo->pabyHeader,"\211HDF\r\n\032\n",8) ) {
3571 : /* Requires netCDF-4/HDF5 support in libnetcdf (not just libnetcdf-v4).
3572 : If HDF5 is not supported in GDAL, this driver will try to open the file
3573 : Else, make sure this driver does not try to open HDF5 files
3574 : If user really wants to open with this driver, use NETCDF:file.h5 format.
3575 : This check should be relaxed, but there is no clear way to make a difference.
3576 : */
3577 :
3578 : /* Check for HDF5 support in GDAL */
3579 : #ifdef HAVE_HDF5
3580 5 : if ( bCheckExt ) { /* Check by default */
3581 5 : const char* pszExtension = CPLGetExtension( poOpenInfo->pszFilename );
3582 5 : if ( ! ( EQUAL( pszExtension, "nc") || EQUAL( pszExtension, "cdf")
3583 : || EQUAL( pszExtension, "nc2") || EQUAL( pszExtension, "nc4") ) )
3584 5 : return NCDF_FORMAT_HDF5;
3585 : }
3586 : #endif
3587 :
3588 : /* Check for netcdf-4 support in libnetcdf */
3589 : #ifdef NETCDF_HAS_NC4
3590 : return NCDF_FORMAT_NC4;
3591 : #else
3592 0 : return NCDF_FORMAT_HDF5;
3593 : #endif
3594 :
3595 : }
3596 1197 : else if ( EQUALN((char*)poOpenInfo->pabyHeader,"\016\003\023\001",4) ) {
3597 : /* Requires HDF4 support in libnetcdf, but if HF4 is supported by GDAL don't try to open. */
3598 : /* If user really wants to open with this driver, use NETCDF:file.hdf syntax. */
3599 :
3600 : /* Check for HDF4 support in GDAL */
3601 : #ifdef HAVE_HDF4
3602 257 : if ( bCheckExt ) { /* Check by default */
3603 : /* Always treat as HDF4 file */
3604 257 : return NCDF_FORMAT_HDF4;
3605 : }
3606 : #endif
3607 :
3608 : /* Check for HDF4 support in libnetcdf */
3609 : #ifdef NETCDF_HAS_HDF4
3610 : return NCDF_FORMAT_NC4;
3611 : #else
3612 0 : return NCDF_FORMAT_HDF4;
3613 : #endif
3614 : }
3615 :
3616 940 : return NCDF_FORMAT_NONE;
3617 : }
3618 :
3619 : /************************************************************************/
3620 : /* Identify() */
3621 : /************************************************************************/
3622 :
3623 9246 : int netCDFDataset::Identify( GDALOpenInfo * poOpenInfo )
3624 :
3625 : {
3626 9246 : if( EQUALN(poOpenInfo->pszFilename,"NETCDF:",7) ) {
3627 0 : return TRUE;
3628 : }
3629 9246 : int nTmpFormat = IdentifyFormat( poOpenInfo );
3630 9246 : if( NCDF_FORMAT_NC == nTmpFormat ||
3631 : NCDF_FORMAT_NC2 == nTmpFormat ||
3632 : NCDF_FORMAT_NC4 == nTmpFormat ||
3633 : NCDF_FORMAT_NC4C == nTmpFormat )
3634 0 : return TRUE;
3635 : else
3636 9246 : return FALSE;
3637 : }
3638 :
3639 : /************************************************************************/
3640 : /* Open() */
3641 : /************************************************************************/
3642 :
3643 3001 : GDALDataset *netCDFDataset::Open( GDALOpenInfo * poOpenInfo )
3644 :
3645 : {
3646 : int j;
3647 : unsigned int k;
3648 : int nd;
3649 : int cdfid, dim_count, var, var_count;
3650 3001 : int i = 0;
3651 : size_t lev_count;
3652 3001 : size_t nTotLevCount = 1;
3653 3001 : int nDim = 2;
3654 : int status;
3655 : int nDimID;
3656 : char attname[NC_MAX_NAME];
3657 : int ndims, nvars, ngatts, unlimdimid;
3658 3001 : int nCount=0;
3659 3001 : int nVarID=-1;
3660 :
3661 3001 : int nTmpFormat=NCDF_FORMAT_NONE;
3662 : int *panBandDimPos; // X, Y, Z postion in array
3663 : int *panBandZLev;
3664 : int *paDimIds;
3665 : size_t xdim, ydim;
3666 : char szTemp[NC_MAX_NAME];
3667 :
3668 3001 : CPLString osSubdatasetName;
3669 : int bTreatAsSubdataset;
3670 :
3671 : /* -------------------------------------------------------------------- */
3672 : /* Does this appear to be a netcdf file? */
3673 : /* -------------------------------------------------------------------- */
3674 3001 : if( ! EQUALN(poOpenInfo->pszFilename,"NETCDF:",7) ) {
3675 2996 : nTmpFormat = IdentifyFormat( poOpenInfo );
3676 : /* Note: not calling Identify() directly, because we want the file type */
3677 : /* Only support NCDF_FORMAT* formats */
3678 2996 : if( ! ( NCDF_FORMAT_NC == nTmpFormat ||
3679 : NCDF_FORMAT_NC2 == nTmpFormat ||
3680 : NCDF_FORMAT_NC4 == nTmpFormat ||
3681 : NCDF_FORMAT_NC4C == nTmpFormat ) )
3682 2816 : return NULL;
3683 : }
3684 :
3685 : netCDFDataset *poDS;
3686 185 : poDS = new netCDFDataset();
3687 :
3688 : /* -------------------------------------------------------------------- */
3689 : /* Disable PAM, at least temporarily. See bug #4244 */
3690 : /* -------------------------------------------------------------------- */
3691 185 : poDS->nPamFlags |= GPF_DISABLED;
3692 :
3693 :
3694 185 : poDS->SetDescription( poOpenInfo->pszFilename );
3695 :
3696 : /* -------------------------------------------------------------------- */
3697 : /* Check if filename start with NETCDF: tag */
3698 : /* -------------------------------------------------------------------- */
3699 185 : if( EQUALN( poOpenInfo->pszFilename,"NETCDF:",7) )
3700 : {
3701 : char **papszName =
3702 : CSLTokenizeString2( poOpenInfo->pszFilename,
3703 5 : ":", CSLT_HONOURSTRINGS|CSLT_PRESERVEESCAPES );
3704 :
3705 : /* -------------------------------------------------------------------- */
3706 : /* Check for drive name in windows NETCDF:"D:\... */
3707 : /* -------------------------------------------------------------------- */
3708 5 : if ( CSLCount(papszName) == 4 &&
3709 0 : strlen(papszName[1]) == 1 &&
3710 0 : (papszName[2][0] == '/' || papszName[2][0] == '\\') )
3711 : {
3712 0 : poDS->osFilename = papszName[1];
3713 0 : poDS->osFilename += ':';
3714 0 : poDS->osFilename += papszName[2];
3715 0 : osSubdatasetName = papszName[3];
3716 0 : bTreatAsSubdataset = TRUE;
3717 0 : CSLDestroy( papszName );
3718 : }
3719 5 : else if( CSLCount(papszName) == 3 )
3720 : {
3721 5 : poDS->osFilename = papszName[1];
3722 5 : osSubdatasetName = papszName[2];
3723 5 : bTreatAsSubdataset = TRUE;
3724 5 : CSLDestroy( papszName );
3725 : }
3726 0 : else if( CSLCount(papszName) == 2 )
3727 : {
3728 0 : poDS->osFilename = papszName[1];
3729 0 : osSubdatasetName = "";
3730 0 : bTreatAsSubdataset = FALSE;
3731 0 : CSLDestroy( papszName );
3732 : }
3733 : else
3734 : {
3735 0 : CSLDestroy( papszName );
3736 0 : delete poDS;
3737 : CPLError( CE_Failure, CPLE_AppDefined,
3738 0 : "Failed to parse NETCDF: prefix string into expected 2, 3 or 4 fields." );
3739 0 : return NULL;
3740 : }
3741 : /* Identify Format from real file, with bCheckExt=FALSE */
3742 5 : GDALOpenInfo* poOpenInfo2 = new GDALOpenInfo(poDS->osFilename.c_str(), GA_ReadOnly );
3743 10 : poDS->nFormat = IdentifyFormat( poOpenInfo2, FALSE );
3744 5 : delete poOpenInfo2;
3745 5 : if( NCDF_FORMAT_NONE == poDS->nFormat ||
3746 : NCDF_FORMAT_UNKNOWN == poDS->nFormat ) {
3747 0 : delete poDS;
3748 0 : return NULL;
3749 : }
3750 : }
3751 : else
3752 : {
3753 180 : poDS->osFilename = poOpenInfo->pszFilename;
3754 180 : bTreatAsSubdataset = FALSE;
3755 180 : poDS->nFormat = nTmpFormat;
3756 : }
3757 :
3758 : /* -------------------------------------------------------------------- */
3759 : /* Try opening the dataset. */
3760 : /* -------------------------------------------------------------------- */
3761 185 : CPLDebug( "GDAL_netCDF", "\n=====\ncalling nc_open( %s )\n", poDS->osFilename.c_str() );
3762 185 : if( nc_open( poDS->osFilename, NC_NOWRITE, &cdfid ) != NC_NOERR ) {
3763 0 : delete poDS;
3764 0 : return NULL;
3765 : }
3766 :
3767 : /* -------------------------------------------------------------------- */
3768 : /* Is this a real netCDF file? */
3769 : /* -------------------------------------------------------------------- */
3770 185 : status = nc_inq(cdfid, &ndims, &nvars, &ngatts, &unlimdimid);
3771 185 : if( status != NC_NOERR ) {
3772 0 : delete poDS;
3773 0 : return NULL;
3774 : }
3775 :
3776 : /* -------------------------------------------------------------------- */
3777 : /* Get file type from netcdf */
3778 : /* -------------------------------------------------------------------- */
3779 185 : status = nc_inq_format (cdfid, &nTmpFormat);
3780 185 : if ( status != NC_NOERR ) {
3781 0 : NCDF_ERR(status);
3782 : }
3783 : else {
3784 : CPLDebug( "GDAL_netCDF",
3785 : "driver detected file type=%d, libnetcdf detected type=%d",
3786 185 : poDS->nFormat, nTmpFormat );
3787 185 : if ( nTmpFormat != poDS->nFormat ) {
3788 : /* warn if file detection conflicts with that from libnetcdf */
3789 : /* except for NC4C, which we have no way of detecting initially */
3790 0 : if ( nTmpFormat != NCDF_FORMAT_NC4C ) {
3791 : CPLError( CE_Warning, CPLE_AppDefined,
3792 : "NetCDF driver detected file type=%d, but libnetcdf detected type=%d",
3793 0 : poDS->nFormat, nTmpFormat );
3794 : }
3795 : CPLDebug( "GDAL_netCDF", "seting file type to %d, was %d",
3796 0 : nTmpFormat, poDS->nFormat );
3797 0 : poDS->nFormat = nTmpFormat;
3798 : }
3799 : }
3800 :
3801 : /* -------------------------------------------------------------------- */
3802 : /* Confirm the requested access is supported. */
3803 : /* -------------------------------------------------------------------- */
3804 185 : if( poOpenInfo->eAccess == GA_Update )
3805 : {
3806 : CPLError( CE_Failure, CPLE_NotSupported,
3807 : "The NETCDF driver does not support update access to existing"
3808 0 : " datasets.\n" );
3809 0 : nc_close( cdfid );
3810 0 : delete poDS;
3811 0 : return NULL;
3812 : }
3813 :
3814 : /* -------------------------------------------------------------------- */
3815 : /* Does the request variable exist? */
3816 : /* -------------------------------------------------------------------- */
3817 185 : if( bTreatAsSubdataset )
3818 : {
3819 5 : status = nc_inq_varid( cdfid, osSubdatasetName, &var);
3820 5 : if( status != NC_NOERR ) {
3821 : CPLError( CE_Warning, CPLE_AppDefined,
3822 : "%s is a netCDF file, but %s is not a variable.",
3823 : poOpenInfo->pszFilename,
3824 0 : osSubdatasetName.c_str() );
3825 :
3826 0 : nc_close( cdfid );
3827 0 : delete poDS;
3828 0 : return NULL;
3829 : }
3830 : }
3831 :
3832 185 : if( nc_inq_ndims( cdfid, &dim_count ) != NC_NOERR || dim_count < 2 )
3833 : {
3834 : CPLError( CE_Warning, CPLE_AppDefined,
3835 : "%s is a netCDF file, but not in GMT configuration.",
3836 0 : poOpenInfo->pszFilename );
3837 :
3838 0 : nc_close( cdfid );
3839 0 : delete poDS;
3840 0 : return NULL;
3841 : }
3842 :
3843 185 : CPLDebug( "GDAL_netCDF", "dim_count = %d", dim_count );
3844 :
3845 185 : if( (status = nc_get_att_text( cdfid, NC_GLOBAL, "Conventions",
3846 : attname )) != NC_NOERR ) {
3847 : CPLError( CE_Warning, CPLE_AppDefined,
3848 3 : "No UNIDATA NC_GLOBAL:Conventions attribute");
3849 : /* note that 'Conventions' is always capital 'C' in CF spec*/
3850 : }
3851 :
3852 :
3853 : /* -------------------------------------------------------------------- */
3854 : /* Create band information objects. */
3855 : /* -------------------------------------------------------------------- */
3856 185 : if ( nc_inq_nvars ( cdfid, &var_count) != NC_NOERR )
3857 : {
3858 0 : delete poDS;
3859 0 : return NULL;
3860 : }
3861 :
3862 185 : CPLDebug( "GDAL_netCDF", "var_count = %d", var_count );
3863 :
3864 : /* -------------------------------------------------------------------- */
3865 : /* Create a corresponding GDALDataset. */
3866 : /* Create Netcdf Subdataset if filename as NETCDF tag */
3867 : /* -------------------------------------------------------------------- */
3868 185 : poDS->cdfid = cdfid;
3869 :
3870 185 : poDS->ReadAttributes( cdfid, NC_GLOBAL );
3871 :
3872 : /* -------------------------------------------------------------------- */
3873 : /* Verify if only one variable has 2 dimensions */
3874 : /* -------------------------------------------------------------------- */
3875 876 : for ( j = 0; j < nvars; j++ ) {
3876 :
3877 691 : nc_inq_varndims ( cdfid, j, &ndims );
3878 691 : if( ndims >= 2 ) {
3879 228 : nVarID=j;
3880 228 : nCount++;
3881 : }
3882 : }
3883 :
3884 : /* -------------------------------------------------------------------- */
3885 : /* We have more than one variable with 2 dimensions in the */
3886 : /* file, then treat this as a subdataset container dataset. */
3887 : /* -------------------------------------------------------------------- */
3888 185 : if( (nCount > 1) && !bTreatAsSubdataset )
3889 : {
3890 18 : poDS->CreateSubDatasetList();
3891 18 : poDS->SetMetadata( poDS->papszMetadata );
3892 18 : poDS->TryLoadXML();
3893 18 : return( poDS );
3894 : }
3895 :
3896 : /* -------------------------------------------------------------------- */
3897 : /* If we are not treating things as a subdataset, then capture */
3898 : /* the name of the single available variable as the subdataset. */
3899 : /* -------------------------------------------------------------------- */
3900 167 : if( !bTreatAsSubdataset ) // nCount must be 1!
3901 : {
3902 : char szVarName[NC_MAX_NAME];
3903 :
3904 162 : nc_inq_varname( cdfid, nVarID, szVarName);
3905 162 : osSubdatasetName = szVarName;
3906 : }
3907 :
3908 : /* -------------------------------------------------------------------- */
3909 : /* Open the NETCDF subdataset NETCDF:"filename":subdataset */
3910 : /* -------------------------------------------------------------------- */
3911 167 : var=-1;
3912 167 : nc_inq_varid( cdfid, osSubdatasetName, &var);
3913 167 : nd = 0;
3914 167 : nc_inq_varndims ( cdfid, var, &nd );
3915 :
3916 167 : paDimIds = (int *)CPLCalloc(nd, sizeof( int ) );
3917 167 : panBandDimPos = ( int * ) CPLCalloc( nd, sizeof( int ) );
3918 :
3919 167 : nc_inq_vardimid( cdfid, var, paDimIds );
3920 :
3921 : /* -------------------------------------------------------------------- */
3922 : /* Check fi somebody tried to pass a variable with less than 2D */
3923 : /* -------------------------------------------------------------------- */
3924 167 : if ( nd < 2 ) {
3925 1 : CPLFree( paDimIds );
3926 1 : CPLFree( panBandDimPos );
3927 1 : delete poDS;
3928 1 : return NULL;
3929 : }
3930 :
3931 : /* -------------------------------------------------------------------- */
3932 : /* CF-1 Convention */
3933 : /* dimensions to appear in the relative order T, then Z, then Y, */
3934 : /* then X to the file. All other dimensions should, whenever */
3935 : /* possible, be placed to the left of the spatiotemporal */
3936 : /* dimensions. */
3937 : /* -------------------------------------------------------------------- */
3938 :
3939 : /* -------------------------------------------------------------------- */
3940 : /* Get X dimensions information */
3941 : /* -------------------------------------------------------------------- */
3942 166 : poDS->nXDimID = paDimIds[nd-1];
3943 166 : nc_inq_dimlen ( cdfid, poDS->nXDimID, &xdim );
3944 166 : poDS->nRasterXSize = xdim;
3945 :
3946 : /* -------------------------------------------------------------------- */
3947 : /* Get Y dimension information */
3948 : /* -------------------------------------------------------------------- */
3949 166 : poDS->nYDimID = paDimIds[nd-2];
3950 166 : nc_inq_dimlen ( cdfid, poDS->nYDimID, &ydim );
3951 166 : poDS->nRasterYSize = ydim;
3952 :
3953 :
3954 512 : for( j=0,k=0; j < nd; j++ ){
3955 346 : if( paDimIds[j] == poDS->nXDimID ){
3956 166 : panBandDimPos[0] = j; // Save Position of XDim
3957 166 : k++;
3958 : }
3959 346 : if( paDimIds[j] == poDS->nYDimID ){
3960 166 : panBandDimPos[1] = j; // Save Position of YDim
3961 166 : k++;
3962 : }
3963 : }
3964 : /* -------------------------------------------------------------------- */
3965 : /* X and Y Dimension Ids were not found! */
3966 : /* -------------------------------------------------------------------- */
3967 166 : if( k != 2 ) {
3968 0 : CPLFree( paDimIds );
3969 0 : CPLFree( panBandDimPos );
3970 0 : return NULL;
3971 : }
3972 :
3973 : /* -------------------------------------------------------------------- */
3974 : /* Read Metadata for this variable */
3975 : /* -------------------------------------------------------------------- */
3976 : /* should disable as is also done at band level, except driver needs the
3977 : variables as metadata (e.g. projection) */
3978 166 : poDS->ReadAttributes( cdfid, var );
3979 :
3980 : /* -------------------------------------------------------------------- */
3981 : /* Read Metadata for each dimension */
3982 : /* -------------------------------------------------------------------- */
3983 :
3984 512 : for( j=0; j < dim_count; j++ ){
3985 346 : nc_inq_dimname( cdfid, j, szTemp );
3986 346 : poDS->papszDimName.AddString( szTemp );
3987 346 : status = nc_inq_varid( cdfid, poDS->papszDimName[j], &nDimID );
3988 346 : if( status == NC_NOERR ) {
3989 290 : poDS->ReadAttributes( cdfid, nDimID );
3990 : }
3991 : }
3992 :
3993 166 : poDS->SetProjectionFromVar( var );
3994 166 : poDS->SetMetadata( poDS->papszMetadata );
3995 :
3996 : /* -------------------------------------------------------------------- */
3997 : /* Create bands */
3998 : /* -------------------------------------------------------------------- */
3999 166 : panBandZLev = (int *)CPLCalloc( nd-2, sizeof( int ) );
4000 :
4001 166 : nTotLevCount = 1;
4002 166 : if ( dim_count > 2 ) {
4003 10 : nDim=2;
4004 44 : for( j=0; j < nd; j++ ){
4005 58 : if( ( paDimIds[j] != poDS->nXDimID ) &&
4006 24 : ( paDimIds[j] != poDS->nYDimID ) ){
4007 14 : nc_inq_dimlen ( cdfid, paDimIds[j], &lev_count );
4008 14 : nTotLevCount *= lev_count;
4009 14 : panBandZLev[ nDim-2 ] = lev_count;
4010 14 : panBandDimPos[ nDim++ ] = j; //Save Position of ZDim
4011 : }
4012 : }
4013 : }
4014 166 : i=0;
4015 :
4016 346 : for ( unsigned int lev = 0; lev < nTotLevCount ; lev++ ) {
4017 : char ** papszToken;
4018 180 : papszToken=NULL;
4019 :
4020 : netCDFRasterBand *poBand =
4021 : new netCDFRasterBand(poDS, var, nDim, lev,
4022 : panBandZLev, panBandDimPos,
4023 180 : paDimIds, i+1 );
4024 :
4025 180 : poDS->SetBand( i+1, poBand );
4026 180 : i++;
4027 : }
4028 :
4029 166 : CPLFree( paDimIds );
4030 166 : CPLFree( panBandDimPos );
4031 166 : CPLFree( panBandZLev );
4032 :
4033 166 : poDS->nBands = i;
4034 :
4035 : // Handle angular geographic coordinates here
4036 :
4037 : /* -------------------------------------------------------------------- */
4038 : /* Initialize any PAM information. */
4039 : /* -------------------------------------------------------------------- */
4040 166 : if( bTreatAsSubdataset )
4041 : {
4042 5 : poDS->SetPhysicalFilename( poDS->osFilename );
4043 5 : poDS->SetSubdatasetName( osSubdatasetName );
4044 : }
4045 :
4046 166 : poDS->TryLoadXML();
4047 :
4048 166 : if( bTreatAsSubdataset )
4049 5 : poDS->oOvManager.Initialize( poDS, ":::VIRTUAL:::" );
4050 : else
4051 161 : poDS->oOvManager.Initialize( poDS, poDS->osFilename );
4052 :
4053 166 : return( poDS );
4054 : }
4055 :
4056 :
4057 : /************************************************************************/
4058 : /* CopyMetadata() */
4059 : /* */
4060 : /* Create a copy of metadata for NC_GLOBAL or a variable */
4061 : /************************************************************************/
4062 :
4063 124 : void CopyMetadata( void *poDS, int fpImage, int CDFVarID ) {
4064 :
4065 : char **papszMetadata;
4066 : char **papszFieldData;
4067 : const char *pszField;
4068 : char szMetaName[ NCDF_MAX_STR_LEN ];
4069 : char szMetaValue[ NCDF_MAX_STR_LEN ];
4070 : char szTemp[ NCDF_MAX_STR_LEN ];
4071 : int nItems;
4072 : int bCopyItem;
4073 :
4074 : /* Remove the following band meta but set them later from band data */
4075 : const char *papszIgnore[] = { CF_ADD_OFFSET, CF_SCALE_FACTOR,
4076 : "valid_range", "_Unsigned",
4077 124 : _FillValue, NULL };
4078 :
4079 124 : if( CDFVarID == NC_GLOBAL ) {
4080 57 : papszMetadata = GDALGetMetadata( (GDALDataset *) poDS,"");
4081 : } else {
4082 67 : papszMetadata = GDALGetMetadata( (GDALRasterBandH) poDS, NULL );
4083 : }
4084 :
4085 124 : nItems = CSLCount( papszMetadata );
4086 :
4087 714 : for(int k=0; k < nItems; k++ ) {
4088 590 : bCopyItem = TRUE;
4089 590 : pszField = CSLGetField( papszMetadata, k );
4090 : papszFieldData = CSLTokenizeString2 (pszField, "=",
4091 590 : CSLT_HONOURSTRINGS );
4092 590 : if( papszFieldData[1] != NULL ) {
4093 590 : strcpy( szMetaName, papszFieldData[ 0 ] );
4094 590 : strcpy( szMetaValue, papszFieldData[ 1 ] );
4095 :
4096 : /* Fix various issues with metadata translation */
4097 590 : if( CDFVarID == NC_GLOBAL ) {
4098 : /* Remove NC_GLOBAL prefix for netcdf global Metadata */
4099 475 : if( strncmp( szMetaName, "NC_GLOBAL#", 10 ) == 0 ) {
4100 100 : strcpy( szTemp, szMetaName+10 );
4101 100 : strcpy( szMetaName, szTemp );
4102 : }
4103 : /* GDAL Metadata renamed as GDAL-[meta] */
4104 375 : else if ( strstr( szMetaName, "#" ) == NULL ) {
4105 38 : strcpy( szTemp, "GDAL_" );
4106 38 : strcat( szTemp, szMetaName );
4107 38 : strcpy( szMetaName, szTemp );
4108 : }
4109 : /* Keep time, lev and depth information for safe-keeping */
4110 : /* Time and vertical coordinate handling need improvements */
4111 337 : else if( strncmp( szMetaName, "time#", 5 ) == 0 ) {
4112 9 : szMetaName[4] = '-';
4113 : }
4114 328 : else if( strncmp( szMetaName, "lev#", 4 ) == 0 ) {
4115 0 : szMetaName[3] = '-';
4116 : }
4117 328 : else if( strncmp( szMetaName, "depth#", 6 ) == 0 ) {
4118 0 : szMetaName[5] = '-';
4119 : }
4120 : /* Only copy data without # (previously all data was copied) */
4121 475 : if ( strstr( szMetaName, "#" ) != NULL ) {
4122 328 : bCopyItem = FALSE;
4123 : }
4124 : /* netCDF attributes do not like the '#' character. */
4125 : // for( unsigned int h=0; h < strlen( szMetaName ) -1 ; h++ ) {
4126 : // if( szMetaName[h] == '#' ) szMetaName[h] = '-';
4127 : // }
4128 : }
4129 : else {
4130 : /* for variables, don't copy varname */
4131 115 : if ( strncmp( szMetaName, "NETCDF_VARNAME", 14) == 0 )
4132 17 : bCopyItem = FALSE;
4133 : /* Don't copy band statistics */
4134 98 : else if ( strncmp( szMetaName, "STATISTICS_", 11) == 0 )
4135 0 : bCopyItem = FALSE;
4136 115 : if ( CSLFindString( (char **)papszIgnore, szMetaName ) != -1 ) {
4137 39 : bCopyItem = FALSE;
4138 : }
4139 : }
4140 :
4141 590 : if ( bCopyItem ) {
4142 206 : if ( NCDFPutAttr( fpImage, CDFVarID,szMetaName,
4143 : szMetaValue ) != CE_None )
4144 : CPLDebug( "GDAL_netCDF", "NCDFPutAttr(%d, %d, %s, %s) failed",
4145 0 : fpImage, CDFVarID,szMetaName, szMetaValue );
4146 : }
4147 :
4148 : }
4149 590 : CSLDestroy( papszFieldData );
4150 : }
4151 :
4152 : /* Set add_offset and scale_factor here if present */
4153 124 : if( CDFVarID != NC_GLOBAL ) {
4154 :
4155 : int bGotAddOffset, bGotScale;
4156 67 : GDALRasterBandH poRB = (GDALRasterBandH) poDS;
4157 67 : double dfAddOffset = GDALGetRasterOffset( poRB , &bGotAddOffset );
4158 67 : double dfScale = GDALGetRasterScale( poRB, &bGotScale );
4159 :
4160 67 : if ( bGotAddOffset && dfAddOffset != 0.0 && bGotScale && dfScale != 1.0 ) {
4161 0 : GDALSetRasterOffset( poRB, dfAddOffset );
4162 0 : GDALSetRasterScale( poRB, dfScale );
4163 : }
4164 :
4165 : }
4166 :
4167 124 : }
4168 :
4169 : /*
4170 : Driver options:
4171 :
4172 : FORMAT=NC/NC2/NC4/NC4C (COMPRESS=DEFLATE sets FORMAT=NC4C)
4173 : COMPRESS=NONE/DEFLATE (default: NONE)
4174 : ZLEVEL=[1-9] (default: 1)
4175 : WRITE_BOTTOMUP=NO/YES (default: NO)
4176 : WRITE_GDAL_TAGS=YES/NO (default: YES)
4177 : WRITE_LONLAT=YES/NO/IF_NEEDED (default: YES for geographic, NO for projected)
4178 : TYPE_LONLAT=float/double (default: double for geographic, float for projected)
4179 : PIXELTYPE=DEFAULT/SIGNEDBYTE (use SIGNEDBYTE to get a signed Byte Band)
4180 :
4181 : Config Options:
4182 :
4183 : GDAL_NETCDF_BOTTOMUP=YES/NO (default:NO) sets the default of WRITE_BOTTOMUP
4184 : and also the default behavior for import
4185 :
4186 : */
4187 :
4188 : /************************************************************************/
4189 : /* CreateLL() */
4190 : /* */
4191 : /* Shared functionality between netCDFDataset::Create() and */
4192 : /* netCDF::CreateCopy() for creating netcdf file based on a set of */
4193 : /* options and a configuration. */
4194 : /************************************************************************/
4195 :
4196 : netCDFDataset *
4197 99 : netCDFDataset::CreateLL( const char * pszFilename,
4198 : int nXSize, int nYSize, int nBands,
4199 : char ** papszOptions )
4200 : {
4201 99 : int status = NC_NOERR;
4202 : netCDFDataset *poDS;
4203 :
4204 99 : poDS = new netCDFDataset();
4205 99 : poDS->nRasterXSize = nXSize;
4206 99 : poDS->nRasterYSize = nYSize;
4207 99 : poDS->eAccess = GA_Update;
4208 198 : poDS->osFilename = pszFilename;
4209 :
4210 : /* from gtiff driver, is this ok? */
4211 : /*
4212 : poDS->nBlockXSize = nXSize;
4213 : poDS->nBlockYSize = 1;
4214 : poDS->nBlocksPerBand =
4215 : ((nYSize + poDS->nBlockYSize - 1) / poDS->nBlockYSize)
4216 : * ((nXSize + poDS->nBlockXSize - 1) / poDS->nBlockXSize);
4217 : */
4218 :
4219 : /* process options */
4220 99 : poDS->papszCreationOptions = CSLDuplicate( papszOptions );
4221 99 : poDS->ProcessCreationOptions( );
4222 :
4223 : /* -------------------------------------------------------------------- */
4224 : /* Create the dataset. */
4225 : /* -------------------------------------------------------------------- */
4226 99 : status = nc_create( pszFilename, poDS->nCreateMode, &(poDS->cdfid) );
4227 :
4228 : /* put into define mode */
4229 99 : poDS->SetDefineMode(TRUE);
4230 :
4231 99 : if( status != NC_NOERR )
4232 : {
4233 : CPLError( CE_Failure, CPLE_OpenFailed,
4234 : "Unable to create netCDF file %s (Error code %d): %s .\n",
4235 2 : pszFilename, status, nc_strerror(status) );
4236 2 : delete poDS;
4237 2 : return NULL;
4238 : }
4239 :
4240 : /* -------------------------------------------------------------------- */
4241 : /* Define dimensions */
4242 : /* -------------------------------------------------------------------- */
4243 97 : poDS->papszDimName.AddString( NCDF_DIMNAME_Y );
4244 97 : poDS->papszDimName.AddString( NCDF_DIMNAME_X );
4245 :
4246 : status = nc_def_dim( poDS->cdfid, NCDF_DIMNAME_X, nXSize,
4247 97 : &(poDS->nXDimID) );
4248 97 : NCDF_ERR(status);
4249 : CPLDebug( "GDAL_netCDF", "status nc_def_dim %s = %d, nXDimID = %d",
4250 97 : NCDF_DIMNAME_X, status, poDS->nXDimID );
4251 :
4252 : status = nc_def_dim( poDS->cdfid, NCDF_DIMNAME_Y, nYSize,
4253 97 : &(poDS->nYDimID) );
4254 97 : NCDF_ERR(status);
4255 : CPLDebug( "GDAL_netCDF", "status nc_def_dim %s = %d, nYDimID = %d",
4256 97 : NCDF_DIMNAME_Y, status, poDS->nYDimID );
4257 :
4258 97 : return poDS;
4259 :
4260 : }
4261 :
4262 : /************************************************************************/
4263 : /* Create() */
4264 : /************************************************************************/
4265 :
4266 : GDALDataset *
4267 40 : netCDFDataset::Create( const char * pszFilename,
4268 : int nXSize, int nYSize, int nBands,
4269 : GDALDataType eType,
4270 : char ** papszOptions )
4271 : {
4272 : netCDFDataset *poDS;
4273 :
4274 : CPLDebug( "GDAL_netCDF",
4275 : "\n=====\nnetCDFDataset::Create( %s, ... )\n",
4276 40 : pszFilename );
4277 :
4278 : poDS = netCDFDataset::CreateLL( pszFilename,
4279 : nXSize, nYSize, nBands,
4280 40 : papszOptions );
4281 :
4282 40 : if ( ! poDS )
4283 0 : return NULL;
4284 :
4285 : /* should we write signed or unsigned byte? */
4286 : /* TODO should this only be done in Create() */
4287 40 : poDS->bSignedData = TRUE;
4288 : const char *pszValue =
4289 40 : CSLFetchNameValue( papszOptions, "PIXELTYPE" );
4290 40 : if( pszValue == NULL )
4291 38 : pszValue = "";
4292 40 : if( eType == GDT_Byte && ( ! EQUAL(pszValue,"SIGNEDBYTE") ) )
4293 10 : poDS->bSignedData = FALSE;
4294 :
4295 : /* -------------------------------------------------------------------- */
4296 : /* Add Conventions, GDAL info and history */
4297 : /* -------------------------------------------------------------------- */
4298 40 : NCDFAddGDALHistory( poDS->cdfid, pszFilename, "", "Create" );
4299 :
4300 : /* -------------------------------------------------------------------- */
4301 : /* Define bands */
4302 : /* -------------------------------------------------------------------- */
4303 218 : for( int iBand = 1; iBand <= nBands; iBand++ )
4304 : {
4305 : poDS->SetBand( iBand, new netCDFRasterBand( poDS, eType, iBand,
4306 69 : poDS->bSignedData ) );
4307 : }
4308 :
4309 : CPLDebug( "GDAL_netCDF",
4310 : "netCDFDataset::Create( %s, ... ) done",
4311 40 : pszFilename );
4312 : /* -------------------------------------------------------------------- */
4313 : /* Return same dataset */
4314 : /* -------------------------------------------------------------------- */
4315 40 : return( poDS );
4316 :
4317 : }
4318 :
4319 :
4320 : template <class T>
4321 67 : CPLErr NCDFCopyBand( GDALRasterBand *poSrcBand, GDALRasterBand *poBand,
4322 : int nXSize, int nYSize,
4323 : GDALProgressFunc pfnProgress, void * pProgressData )
4324 : {
4325 67 : GDALDataType eDT = poSrcBand->GetRasterDataType();
4326 67 : CPLErr eErr = CE_None;
4327 67 : T *patScanline = (T *) CPLMalloc( nXSize * sizeof(T) );
4328 :
4329 3434 : for( int iLine = 0; iLine < nYSize && eErr == CE_None; iLine++ ) {
4330 3367 : eErr = poSrcBand->RasterIO( GF_Read, 0, iLine, nXSize, 1,
4331 : patScanline, nXSize, 1, eDT,
4332 : 0,0);
4333 3367 : if ( eErr == CE_None )
4334 3367 : eErr = poBand->RasterIO( GF_Write, 0, iLine, nXSize, 1,
4335 : patScanline, nXSize, 1, eDT,
4336 : 0,0);
4337 :
4338 3367 : if ( ( nYSize>10 ) && ( iLine % (nYSize/10) == 1 ) ) {
4339 474 : pfnProgress( 1.0*iLine/nYSize , NULL, pProgressData );
4340 : }
4341 : }
4342 :
4343 67 : CPLFree( patScanline );
4344 :
4345 67 : pfnProgress( 1.0, NULL, pProgressData );
4346 :
4347 67 : return eErr;
4348 : }
4349 :
4350 :
4351 : /************************************************************************/
4352 : /* CreateCopy() */
4353 : /************************************************************************/
4354 :
4355 : GDALDataset*
4356 64 : netCDFDataset::CreateCopy( const char * pszFilename, GDALDataset *poSrcDS,
4357 : int bStrict, char ** papszOptions,
4358 : GDALProgressFunc pfnProgress, void * pProgressData )
4359 : {
4360 : netCDFDataset *poDS;
4361 : void *pScaledProgress;
4362 : GDALDataType eDT;
4363 64 : CPLErr eErr = CE_None;
4364 : int nBands, nXSize, nYSize;
4365 : double adfGeoTransform[6];
4366 : const char *pszWKT;
4367 : int iBand;
4368 :
4369 : CPLDebug( "GDAL_netCDF",
4370 : "\n=====\nnetCDFDataset::CreateCopy( %s, ... )\n",
4371 64 : pszFilename );
4372 :
4373 64 : nBands = poSrcDS->GetRasterCount();
4374 64 : nXSize = poSrcDS->GetRasterXSize();
4375 64 : nYSize = poSrcDS->GetRasterYSize();
4376 :
4377 : /* -------------------------------------------------------------------- */
4378 : /* Check input bands for errors */
4379 : /* -------------------------------------------------------------------- */
4380 :
4381 64 : if (nBands == 0)
4382 : {
4383 : CPLError( CE_Failure, CPLE_NotSupported,
4384 1 : "NetCDF driver does not support source dataset with zero band.\n");
4385 1 : return NULL;
4386 : }
4387 :
4388 132 : for( iBand=1; iBand <= nBands; iBand++ )
4389 : {
4390 73 : GDALRasterBand *poSrcBand = poSrcDS->GetRasterBand( iBand );
4391 73 : eDT = poSrcBand->GetRasterDataType();
4392 73 : if (eDT == GDT_Unknown || GDALDataTypeIsComplex(eDT))
4393 : {
4394 : CPLError( CE_Failure, CPLE_NotSupported,
4395 4 : "NetCDF driver does not support source dataset with band of complex type.");
4396 4 : return NULL;
4397 : }
4398 : }
4399 :
4400 59 : if( !pfnProgress( 0.0, NULL, pProgressData ) )
4401 0 : return NULL;
4402 :
4403 : /* same as in Create() */
4404 : poDS = netCDFDataset::CreateLL( pszFilename,
4405 : nXSize, nYSize, nBands,
4406 59 : papszOptions );
4407 59 : if ( ! poDS )
4408 2 : return NULL;
4409 :
4410 : /* -------------------------------------------------------------------- */
4411 : /* Copy global metadata */
4412 : /* Add Conventions, GDAL info and history */
4413 : /* -------------------------------------------------------------------- */
4414 57 : CopyMetadata((void *) poSrcDS, poDS->cdfid, NC_GLOBAL );
4415 : NCDFAddGDALHistory( poDS->cdfid, pszFilename,
4416 57 : poSrcDS->GetMetadataItem("NC_GLOBAL#history",""),
4417 114 : "CreateCopy" );
4418 :
4419 57 : pfnProgress( 0.1, NULL, pProgressData );
4420 :
4421 : /* -------------------------------------------------------------------- */
4422 : /* Define Bands */
4423 : /* -------------------------------------------------------------------- */
4424 248 : for( iBand=1; iBand <= nBands; iBand++ ) {
4425 : CPLDebug( "GDAL_netCDF", "creating band # %d/%d ",
4426 67 : iBand,nBands );
4427 :
4428 : char szBandName[ NC_MAX_NAME ];
4429 : char szLongName[ NC_MAX_NAME ];
4430 : const char *tmpMetadata;
4431 : netCDFRasterBand *poBand;
4432 67 : GDALRasterBand *poSrcBand = poSrcDS->GetRasterBand( iBand );
4433 67 : eDT = poSrcBand->GetRasterDataType();
4434 :
4435 : /* Get var name from NETCDF_VARNAME */
4436 67 : tmpMetadata = poSrcBand->GetMetadataItem("NETCDF_VARNAME");
4437 67 : if( tmpMetadata != NULL) {
4438 17 : if( nBands > 1 ) sprintf(szBandName,"%s%d",tmpMetadata,iBand);
4439 17 : else strcpy( szBandName, tmpMetadata );
4440 : // poSrcBand->SetMetadataItem("NETCDF_VARNAME","");
4441 : }
4442 : else
4443 50 : szBandName[0]='\0';
4444 :
4445 : /* Get long_name from <var>#long_name */
4446 : sprintf(szLongName,"%s#%s",
4447 67 : poSrcBand->GetMetadataItem("NETCDF_VARNAME"),
4448 134 : CF_LNG_NAME);
4449 67 : tmpMetadata = poSrcDS->GetMetadataItem(szLongName);
4450 67 : if( tmpMetadata != NULL)
4451 16 : strcpy( szLongName, tmpMetadata);
4452 : else
4453 51 : szLongName[0]='\0';
4454 :
4455 67 : int bSignedData = TRUE;
4456 67 : if ( eDT == GDT_Byte ) {
4457 : /* GDAL defaults to unsigned bytes, but check if metadata says its
4458 : signed, as NetCDF can support this for certain formats. */
4459 52 : bSignedData = FALSE;
4460 : tmpMetadata = poSrcBand->GetMetadataItem("PIXELTYPE",
4461 52 : "IMAGE_STRUCTURE");
4462 52 : if ( tmpMetadata && EQUAL(tmpMetadata,"SIGNEDBYTE") )
4463 1 : bSignedData = TRUE;
4464 : }
4465 :
4466 : poBand = new netCDFRasterBand( poDS, eDT, iBand,
4467 : bSignedData,
4468 67 : szBandName, szLongName );
4469 67 : poDS->SetBand( iBand, poBand );
4470 :
4471 : /* Copy Metadata for band */
4472 67 : poBand->SetNoDataValue( poSrcBand->GetNoDataValue(0) );
4473 : CopyMetadata( (void *) GDALGetRasterBand( poSrcDS, iBand ),
4474 67 : poDS->cdfid, poBand->nZId );
4475 :
4476 : }
4477 :
4478 57 : pfnProgress( 0.2, NULL, pProgressData );
4479 :
4480 : /* -------------------------------------------------------------------- */
4481 : /* Copy GeoTransform and Projection */
4482 : /* -------------------------------------------------------------------- */
4483 57 : eErr = poSrcDS->GetGeoTransform( adfGeoTransform );
4484 57 : if ( eErr == CE_None ) {
4485 57 : poDS->SetGeoTransform( adfGeoTransform );
4486 : // set this is to disable AddProjectionVars() from being called
4487 57 : poDS->bSetGeoTransform = FALSE;
4488 : }
4489 :
4490 57 : pszWKT = poSrcDS->GetProjectionRef( );
4491 57 : if ( pszWKT ) {
4492 57 : poDS->SetProjection( pszWKT );
4493 : // now we can call AddProjectionVars()
4494 57 : poDS->bSetGeoTransform = TRUE;
4495 : pScaledProgress = GDALCreateScaledProgress( 0.20, 0.50, pfnProgress,
4496 57 : pProgressData );
4497 57 : poDS->AddProjectionVars( GDALScaledProgress, pScaledProgress );
4498 57 : GDALDestroyScaledProgress( pScaledProgress );
4499 :
4500 : }
4501 :
4502 57 : pfnProgress( 0.5, NULL, pProgressData );
4503 :
4504 : /* -------------------------------------------------------------------- */
4505 : /* Write Bands */
4506 : /* -------------------------------------------------------------------- */
4507 : /* make sure we are in data mode */
4508 57 : poDS->SetDefineMode( FALSE );
4509 :
4510 : double dfTemp,dfTemp2;
4511 57 : dfTemp = dfTemp2 = 0.5;
4512 :
4513 124 : for( iBand=1; iBand <= nBands; iBand++ ) {
4514 :
4515 67 : dfTemp2 = dfTemp + 0.4/nBands;
4516 : pScaledProgress =
4517 : GDALCreateScaledProgress( dfTemp, dfTemp2,
4518 67 : pfnProgress, pProgressData );
4519 67 : dfTemp = dfTemp2;
4520 :
4521 67 : GDALRasterBand *poSrcBand = poSrcDS->GetRasterBand( iBand );
4522 :
4523 : /* new */
4524 : GDALDataType eDT;
4525 67 : CPLErr eErr = CE_None;
4526 :
4527 : CPLDebug( "GDAL_netCDF", "copying band data # %d/%d ",
4528 67 : iBand,nBands );
4529 :
4530 : // hBand = GDALGetRasterBand( poSrcDS, i );
4531 67 : GDALRasterBand *poBand = poDS->GetRasterBand( iBand );
4532 67 : eDT = poSrcBand->GetRasterDataType();
4533 67 : eErr = CE_None;
4534 :
4535 : /* -------------------------------------------------------------------- */
4536 : /* Copy Band data */
4537 : /* -------------------------------------------------------------------- */
4538 67 : if( eDT == GDT_Byte ) {
4539 52 : CPLDebug( "GDAL_netCDF", "GByte Band#%d", iBand );
4540 : NCDFCopyBand<GByte>( poSrcBand, poBand, nXSize, nYSize,
4541 52 : GDALScaledProgress, pScaledProgress );
4542 : }
4543 18 : else if( ( eDT == GDT_UInt16 ) || ( eDT == GDT_Int16 ) ) {
4544 3 : CPLDebug( "GDAL_netCDF", "GInt16 Band#%d", iBand );
4545 : NCDFCopyBand<GInt16>( poSrcBand, poBand, nXSize, nYSize,
4546 3 : GDALScaledProgress, pScaledProgress );
4547 : }
4548 15 : else if( (eDT == GDT_UInt32) || (eDT == GDT_Int32) ) {
4549 3 : CPLDebug( "GDAL_netCDF", "GInt16 Band#%d", iBand );
4550 : NCDFCopyBand<GInt32>( poSrcBand, poBand, nXSize, nYSize,
4551 3 : GDALScaledProgress, pScaledProgress );
4552 : }
4553 9 : else if( (eDT == GDT_Float32) ) {
4554 7 : CPLDebug( "GDAL_netCDF", "float Band#%d", iBand);
4555 : NCDFCopyBand<float>( poSrcBand, poBand, nXSize, nYSize,
4556 7 : GDALScaledProgress, pScaledProgress );
4557 : }
4558 2 : else if( (eDT == GDT_Float64) ) {
4559 2 : CPLDebug( "GDAL_netCDF", "double Band#%d", iBand);
4560 : NCDFCopyBand<double>( poSrcBand, poBand, nXSize, nYSize,
4561 2 : GDALScaledProgress, pScaledProgress );
4562 : }
4563 : else {
4564 : CPLError( CE_Failure, CPLE_NotSupported,
4565 : "The NetCDF driver does not support GDAL data type %d",
4566 0 : eDT );
4567 : }
4568 :
4569 67 : GDALDestroyScaledProgress( pScaledProgress );
4570 :
4571 : }
4572 :
4573 : /* -------------------------------------------------------------------- */
4574 : /* Cleanup and close. */
4575 : /* -------------------------------------------------------------------- */
4576 57 : delete( poDS );
4577 : // CPLFree(pszProj4Defn );
4578 :
4579 57 : pfnProgress( 0.95, NULL, pProgressData );
4580 :
4581 : /* -------------------------------------------------------------------- */
4582 : /* Re-open dataset, and copy any auxilary pam information. */
4583 : /* Disable PAM, at least temporarily. See bug #4244 */
4584 : /* -------------------------------------------------------------------- */
4585 57 : poDS = (netCDFDataset *) GDALOpen( pszFilename, GA_ReadOnly );
4586 :
4587 : // if( poDS )
4588 : // poDS->CloneInfo( poSrcDS, GCIF_PAM_DEFAULT );
4589 :
4590 57 : pfnProgress( 1.0, NULL, pProgressData );
4591 :
4592 57 : return poDS;
4593 : }
4594 :
4595 :
4596 : /* note: some logic depends on bIsProjected and bIsGeoGraphic */
4597 : /* which may not be known when Create() is called */
4598 : void
4599 99 : netCDFDataset::ProcessCreationOptions( )
4600 : {
4601 : const char *pszValue;
4602 :
4603 : /* File format */
4604 99 : nFormat = NCDF_FORMAT_NC;
4605 99 : pszValue = CSLFetchNameValue( papszCreationOptions, "FORMAT" );
4606 99 : if ( pszValue != NULL ) {
4607 0 : if ( EQUAL( pszValue, "NC" ) ) {
4608 0 : nFormat = NCDF_FORMAT_NC;
4609 : }
4610 : #ifdef NETCDF_HAS_NC2
4611 0 : else if ( EQUAL( pszValue, "NC2" ) ) {
4612 0 : nFormat = NCDF_FORMAT_NC2;
4613 : }
4614 : #endif
4615 : #ifdef NETCDF_HAS_NC4
4616 : else if ( EQUAL( pszValue, "NC4" ) ) {
4617 : nFormat = NCDF_FORMAT_NC4;
4618 : }
4619 : else if ( EQUAL( pszValue, "NC4C" ) ) {
4620 : nFormat = NCDF_FORMAT_NC4C;
4621 : }
4622 : #endif
4623 : else {
4624 : CPLError( CE_Failure, CPLE_NotSupported,
4625 0 : "FORMAT=%s in not supported, using the default NC format.", pszValue );
4626 : }
4627 : }
4628 :
4629 : /* compression only available for NC4 */
4630 : #ifdef NETCDF_HAS_NC4
4631 :
4632 : /* COMPRESS option */
4633 : pszValue = CSLFetchNameValue( papszCreationOptions, "COMPRESS" );
4634 : if ( pszValue != NULL ) {
4635 : if ( EQUAL( pszValue, "NONE" ) ) {
4636 : nCompress = NCDF_COMPRESS_NONE;
4637 : }
4638 : else if ( EQUAL( pszValue, "DEFLATE" ) ) {
4639 : nCompress = NCDF_COMPRESS_DEFLATE;
4640 : if ( !((nFormat == NCDF_FORMAT_NC4) || (nFormat == NCDF_FORMAT_NC4C)) ) {
4641 : CPLError( CE_Warning, CPLE_IllegalArg,
4642 : "NOTICE: Format set to NC4C because compression is set to DEFLATE." );
4643 : nFormat = NCDF_FORMAT_NC4C;
4644 : }
4645 : }
4646 : else {
4647 : CPLError( CE_Failure, CPLE_NotSupported,
4648 : "COMPRESS=%s is not supported.", pszValue );
4649 : }
4650 : }
4651 :
4652 : /* ZLEVEL option */
4653 : pszValue = CSLFetchNameValue( papszCreationOptions, "ZLEVEL" );
4654 : if( pszValue != NULL )
4655 : {
4656 : nZLevel = atoi( pszValue );
4657 : if (!(nZLevel >= 1 && nZLevel <= 9))
4658 : {
4659 : CPLError( CE_Warning, CPLE_IllegalArg,
4660 : "ZLEVEL=%s value not recognised, ignoring.",
4661 : pszValue );
4662 : nZLevel = NCDF_DEFLATE_LEVEL;
4663 : }
4664 : }
4665 :
4666 : #endif
4667 :
4668 : /* set nCreateMode based on nFormat */
4669 99 : switch ( nFormat ) {
4670 : #ifdef NETCDF_HAS_NC2
4671 : case NCDF_FORMAT_NC2:
4672 0 : nCreateMode = NC_CLOBBER|NC_64BIT_OFFSET;
4673 0 : break;
4674 : #endif
4675 : #ifdef NETCDF_HAS_NC4
4676 : case NCDF_FORMAT_NC4:
4677 : nCreateMode = NC_CLOBBER|NC_NETCDF4;
4678 : break;
4679 : case NCDF_FORMAT_NC4C:
4680 : nCreateMode = NC_CLOBBER|NC_NETCDF4|NC_CLASSIC_MODEL;
4681 : break;
4682 : #endif
4683 : case NCDF_FORMAT_NC:
4684 : default:
4685 99 : nCreateMode = NC_CLOBBER;
4686 : break;
4687 : }
4688 :
4689 : CPLDebug( "GDAL_netCDF",
4690 : "file options: format=%d compress=%d zlevel=%d",
4691 99 : nFormat, nCompress, nZLevel );
4692 :
4693 : /* netcdf standard is bottom-up */
4694 : /* overriden by config option GDAL_NETCDF_BOTTOMUP and -co option WRITE_BOTTOMUP */
4695 : // bBottomUp = CSLTestBoolean( CPLGetConfigOption( "GDAL_NETCDF_BOTTOMUP", "YES" ) );
4696 99 : bBottomUp = CSLTestBoolean( CPLGetConfigOption( "GDAL_NETCDF_BOTTOMUP", "NO" ) );
4697 99 : bBottomUp = CSLFetchBoolean( papszCreationOptions, "WRITE_BOTTOMUP", bBottomUp );
4698 :
4699 : /* TODO could add a config option GDAL_NETCDF_PREF=GDAL/CF */
4700 :
4701 : /* moved over projection stuff to setprojection */
4702 :
4703 99 : }
4704 :
4705 136 : int netCDFDataset::DefVarDeflate( int nVarId, int bChunking )
4706 : {
4707 : #ifdef NETCDF_HAS_NC4
4708 : // must set chunk size to avoid huge performace hit
4709 : // perhaps another solution it to change the chunk cache?
4710 : // http://www.unidata.ucar.edu/software/netcdf/docs/netcdf.html#Chunk-Cache
4711 : if ( nCompress == NCDF_COMPRESS_DEFLATE ) {
4712 : status = nc_def_var_deflate(cdfid,nVarId,1,1,nZLevel);
4713 : NCDF_ERR(status);
4714 : if ( status != NC_NOERR && bChunking ) {
4715 : //TODO make sure this is ok
4716 : size_t chunksize[] = { 1, nRasterXSize };
4717 : status = nc_def_var_chunking( cdfid, nVarId,
4718 : NC_CHUNKED, chunksize );
4719 : NCDF_ERR(status);
4720 : }
4721 : return status;
4722 : }
4723 : #endif
4724 136 : return NC_NOERR;
4725 : }
4726 :
4727 :
4728 : /************************************************************************/
4729 : /* GDALRegister_netCDF() */
4730 : /************************************************************************/
4731 :
4732 558 : void GDALRegister_netCDF()
4733 :
4734 : {
4735 558 : if (! GDAL_CHECK_VERSION("netCDF driver"))
4736 0 : return;
4737 :
4738 558 : if( GDALGetDriverByName( "netCDF" ) == NULL )
4739 : {
4740 : GDALDriver *poDriver;
4741 : char szCreateOptions[3072];
4742 :
4743 537 : poDriver = new GDALDriver( );
4744 :
4745 : /* -------------------------------------------------------------------- */
4746 : /* Build full creation option list. */
4747 : /* -------------------------------------------------------------------- */
4748 : sprintf( szCreateOptions, "%s",
4749 : "<CreationOptionList>"
4750 : " <Option name='FORMAT' type='string-select' default='NC'>"
4751 : " <Value>NC</Value>"
4752 : #ifdef NETCDF_HAS_NC2
4753 : " <Value>NC2</Value>"
4754 : #endif
4755 : #ifdef NETCDF_HAS_NC4
4756 : " <Value>NC4</Value>"
4757 : " <Value>NC4C</Value>"
4758 : #endif
4759 : " </Option>"
4760 : #ifdef NETCDF_HAS_NC4
4761 : " <Option name='COMPRESS' type='string-select' default='NONE'>"
4762 : " <Value>NONE</Value>"
4763 : " <Value>DEFLATE</Value>"
4764 : " </Option>"
4765 : " <Option name='ZLEVEL' type='int' description='DEFLATE compression level 1-9' default='1'/>"
4766 : #endif
4767 : " <Option name='WRITE_BOTTOMUP' type='boolean' default='NO'>"
4768 : " </Option>"
4769 : " <Option name='WRITE_GDAL_TAGS' type='boolean' default='YES'>"
4770 : " </Option>"
4771 : " <Option name='WRITE_LONLAT' type='string-select'>"
4772 : " <Value>YES</Value>"
4773 : " <Value>NO</Value>"
4774 : " <Value>IF_NEEDED</Value>"
4775 : " </Option>"
4776 : " <Option name='TYPE_LONLAT' type='string-select'>"
4777 : " <Value>float</Value>"
4778 : " <Value>double</Value>"
4779 : " </Option>"
4780 : " <Option name='PIXELTYPE' type='string-select' description='only used in Create()'>"
4781 : " <Value>DEFAULT</Value>"
4782 : " <Value>SIGNEDBYTE</Value>"
4783 : " </Option>"
4784 537 : "</CreationOptionList>" );
4785 :
4786 :
4787 : /* -------------------------------------------------------------------- */
4788 : /* Set the driver details. */
4789 : /* -------------------------------------------------------------------- */
4790 537 : poDriver->SetDescription( "netCDF" );
4791 : poDriver->SetMetadataItem( GDAL_DMD_LONGNAME,
4792 537 : "Network Common Data Format" );
4793 : poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC,
4794 537 : "frmt_netcdf.html" );
4795 537 : poDriver->SetMetadataItem( GDAL_DMD_EXTENSION, "nc" );
4796 : poDriver->SetMetadataItem( GDAL_DMD_CREATIONOPTIONLIST,
4797 537 : szCreateOptions );
4798 :
4799 : /* make driver config and capabilities available */
4800 537 : poDriver->SetMetadataItem( "NETCDF_VERSION", nc_inq_libvers() );
4801 537 : poDriver->SetMetadataItem( "NETCDF_CONVENTIONS", NCDF_CONVENTIONS_CF );
4802 : #ifdef NETCDF_HAS_NC2
4803 537 : poDriver->SetMetadataItem( "NETCDF_HAS_NC2", "YES" );
4804 : #endif
4805 : #ifdef NETCDF_HAS_NC4
4806 : poDriver->SetMetadataItem( "NETCDF_HAS_NC4", "YES" );
4807 : #endif
4808 : #ifdef NETCDF_HAS_HDF4
4809 : poDriver->SetMetadataItem( "NETCDF_HAS_HDF4", "YES" );
4810 : #endif
4811 : #ifdef HAVE_HDF4
4812 537 : poDriver->SetMetadataItem( "GDAL_HAS_HDF4", "YES" );
4813 : #endif
4814 : #ifdef HAVE_HDF5
4815 537 : poDriver->SetMetadataItem( "GDAL_HAS_HDF5", "YES" );
4816 : #endif
4817 :
4818 : /* set pfns and register driver */
4819 537 : poDriver->pfnOpen = netCDFDataset::Open;
4820 537 : poDriver->pfnCreateCopy = netCDFDataset::CreateCopy;
4821 537 : poDriver->pfnCreate = netCDFDataset::Create;
4822 537 : poDriver->pfnIdentify = netCDFDataset::Identify;
4823 :
4824 537 : GetGDALDriverManager( )->RegisterDriver( poDriver );
4825 : }
4826 : }
4827 :
4828 : /************************************************************************/
4829 : /* New functions */
4830 : /************************************************************************/
4831 :
4832 : /* Test for GDAL version string >= target */
4833 147 : int NCDFIsGDALVersionGTE(const char* pszVersion, int nTarget)
4834 : {
4835 147 : int nVersion = 0;
4836 147 : int nVersions [] = {0,0,0,0};
4837 : char **papszTokens;
4838 :
4839 : /* Valid strings are "GDAL 1.9dev, released 2011/01/18" and "GDAL 1.8.1 " */
4840 147 : if ( pszVersion == NULL || EQUAL( pszVersion, "" ) )
4841 0 : return FALSE;
4842 147 : else if ( ! EQUALN("GDAL ", pszVersion, 5) )
4843 0 : return FALSE;
4844 147 : else if ( EQUALN("GDAL 1.9dev", pszVersion,11 ) )
4845 147 : return nTarget <= 1900;
4846 0 : else if ( EQUALN("GDAL 1.8dev", pszVersion,11 ) )
4847 0 : return nTarget <= 1800;
4848 :
4849 0 : papszTokens = CSLTokenizeString2( pszVersion+5, ".", 0 );
4850 :
4851 0 : for ( int iToken = 0; papszTokens && papszTokens[iToken]; iToken++ ) {
4852 0 : nVersions[iToken] = atoi( papszTokens[iToken] );
4853 : }
4854 : /* (GDAL_VERSION_MAJOR*1000+GDAL_VERSION_MINOR*100+GDAL_VERSION_REV*10+GDAL_VERSION_BUILD) */
4855 0 : nVersion = nVersions[0]*1000 + nVersions[1]*100 +
4856 0 : nVersions[2]*10 + nVersions[3];
4857 :
4858 0 : CSLDestroy( papszTokens );
4859 0 : return nTarget <= nVersion;
4860 : }
4861 :
4862 : /* Add Conventions, GDAL version and history */
4863 97 : void NCDFAddGDALHistory( int fpImage,
4864 : const char * pszFilename, const char *pszOldHist,
4865 : const char * pszFunctionName)
4866 : {
4867 : char szTemp[NC_MAX_NAME];
4868 :
4869 : nc_put_att_text( fpImage, NC_GLOBAL, "Conventions",
4870 : strlen(NCDF_CONVENTIONS_CF),
4871 97 : NCDF_CONVENTIONS_CF );
4872 :
4873 : nc_put_att_text( fpImage, NC_GLOBAL, "GDAL",
4874 97 : strlen(NCDF_GDAL), NCDF_GDAL );
4875 :
4876 : /* Add history */
4877 : #ifdef GDAL_SET_CMD_LINE_DEFINED_TMP
4878 : if ( ! EQUAL(GDALGetCmdLine(), "" ) )
4879 : strcpy( szTemp, GDALGetCmdLine() );
4880 : else
4881 : sprintf( szTemp, "GDAL %s( %s, ... )",pszFunctionName,pszFilename );
4882 : #else
4883 97 : sprintf( szTemp, "GDAL %s( %s, ... )",pszFunctionName,pszFilename );
4884 : #endif
4885 :
4886 97 : NCDFAddHistory( fpImage, szTemp, pszOldHist );
4887 :
4888 97 : }
4889 :
4890 : /* code taken from cdo and libcdi, used for writing the history attribute */
4891 : //void cdoDefHistory(int fileID, char *histstring)
4892 97 : void NCDFAddHistory(int fpImage, const char *pszAddHist, const char *pszOldHist)
4893 : {
4894 : char strtime[32];
4895 : time_t tp;
4896 : struct tm *ltime;
4897 :
4898 97 : char *pszNewHist = NULL;
4899 97 : size_t nNewHistSize = 0;
4900 97 : int disableHistory = FALSE;
4901 : int status;
4902 :
4903 : /* Check pszOldHist - as if there was no previous history, it will be
4904 : a null pointer - if so set as empty. */
4905 97 : if (NULL == pszOldHist) {
4906 41 : pszOldHist = "";
4907 : }
4908 :
4909 97 : tp = time(NULL);
4910 97 : if ( tp != -1 )
4911 : {
4912 97 : ltime = localtime(&tp);
4913 97 : (void) strftime(strtime, sizeof(strtime), "%a %b %d %H:%M:%S %Y: ", ltime);
4914 : }
4915 :
4916 : // status = nc_get_att_text( fpImage, NC_GLOBAL,
4917 : // "history", pszOldHist );
4918 : // printf("status: %d pszOldHist: [%s]\n",status,pszOldHist);
4919 :
4920 97 : nNewHistSize = strlen(pszOldHist)+strlen(strtime)+strlen(pszAddHist)+1+1;
4921 97 : pszNewHist = (char *) CPLMalloc(nNewHistSize * sizeof(char));
4922 :
4923 97 : strcpy(pszNewHist, strtime);
4924 97 : strcat(pszNewHist, pszAddHist);
4925 :
4926 97 : if ( disableHistory == FALSE && pszNewHist )
4927 : {
4928 97 : if ( ! EQUAL(pszOldHist,"") )
4929 16 : strcat(pszNewHist, "\n");
4930 97 : strcat(pszNewHist, pszOldHist);
4931 : }
4932 :
4933 : status = nc_put_att_text( fpImage, NC_GLOBAL,
4934 : "history", strlen(pszNewHist),
4935 97 : pszNewHist );
4936 97 : NCDF_ERR(status);
4937 :
4938 97 : CPLFree(pszNewHist);
4939 97 : }
4940 :
4941 :
4942 41 : int NCDFIsCfProjection( const char* pszProjection )
4943 : {
4944 : /* Find the appropriate mapping */
4945 823 : for (int iMap = 0; poNetcdfSRS_PT[iMap].WKT_SRS != NULL; iMap++ ) {
4946 : // printf("now at %d, proj=%s\n",i, poNetcdfSRS_PT[i].GDAL_SRS);
4947 823 : if ( EQUAL( pszProjection, poNetcdfSRS_PT[iMap].WKT_SRS ) ) {
4948 41 : if ( poNetcdfSRS_PT[iMap].mappings != NULL )
4949 41 : return TRUE;
4950 : else
4951 0 : return FALSE;
4952 : }
4953 : }
4954 0 : return FALSE;
4955 : }
4956 :
4957 :
4958 : /* Write any needed projection attributes *
4959 : * poPROJCS: ptr to proj crd system
4960 : * pszProjection: name of projection system in GDAL WKT
4961 : * fpImage: open NetCDF file in writing mode
4962 : * NCDFVarID: NetCDF Var Id of proj system we're writing in to
4963 : *
4964 : * The function first looks for the oNetcdfSRS_PP mapping object
4965 : * that corresponds to the input projection name. If none is found
4966 : * the generic mapping is used. In the case of specific mappings,
4967 : * the driver looks for each attribute listed in the mapping object
4968 : * and then looks up the value within the OGR_SRSNode. In the case
4969 : * of the generic mapping, the lookup is reversed (projection params,
4970 : * then mapping). For more generic code, GDAL->NETCDF
4971 : * mappings and the associated value are saved in std::map objects.
4972 : */
4973 :
4974 : /* NOTE modifications by ET to combine the specific and generic mappings */
4975 :
4976 41 : void NCDFWriteProjAttribs( const OGR_SRSNode *poPROJCS,
4977 : const char* pszProjection,
4978 : const int fpImage, const int NCDFVarID )
4979 : {
4980 : double dfStdP[2];
4981 41 : int bFoundStdP1=FALSE,bFoundStdP2=FALSE;
4982 41 : double dfValue=0.0;
4983 : const char *pszParamStr, *pszParamVal;
4984 : const std::string *pszNCDFAtt, *pszGDALAtt;
4985 : static const oNetcdfSRS_PP *poMap = NULL;
4986 41 : int nMapIndex = -1;
4987 41 : int bWriteVal = FALSE;
4988 :
4989 : //Attribute <GDAL,NCDF> and Value <NCDF,value> mappings
4990 41 : std::map< std::string, std::string > oAttMap;
4991 41 : std::map< std::string, std::string >::iterator oAttIter;
4992 41 : std::map< std::string, double > oValMap;
4993 41 : std::map< std::string, double >::iterator oValIter, oValIter2;
4994 : //results to write
4995 41 : std::vector< std::pair<std::string,double> > oOutList;
4996 :
4997 : /* Find the appropriate mapping */
4998 823 : for (int iMap = 0; poNetcdfSRS_PT[iMap].WKT_SRS != NULL; iMap++ ) {
4999 823 : if ( EQUAL( pszProjection, poNetcdfSRS_PT[iMap].WKT_SRS ) ) {
5000 41 : nMapIndex = iMap;
5001 41 : poMap = poNetcdfSRS_PT[iMap].mappings;
5002 41 : break;
5003 : }
5004 : }
5005 :
5006 : //ET TODO if projection name is not found, should we do something special?
5007 41 : if ( nMapIndex == -1 ) {
5008 : CPLError( CE_Warning, CPLE_AppDefined,
5009 : "projection name %s not found in the lookup tables!!!",
5010 0 : pszProjection);
5011 : }
5012 : /* if no mapping was found or assigned, set the generic one */
5013 41 : if ( !poMap ) {
5014 : CPLError( CE_Warning, CPLE_AppDefined,
5015 : "projection name %s in not part of the CF standard, will not be supported by CF!",
5016 0 : pszProjection);
5017 0 : poMap = poGenericMappings;
5018 : }
5019 :
5020 : /* initialize local map objects */
5021 470 : for ( int iMap = 0; poMap[iMap].WKT_ATT != NULL; iMap++ ) {
5022 194 : oAttMap[poMap[iMap].WKT_ATT] = poMap[iMap].CF_ATT;
5023 : }
5024 :
5025 415 : for( int iChild = 0; iChild < poPROJCS->GetChildCount(); iChild++ ) {
5026 :
5027 : const OGR_SRSNode *poNode;
5028 :
5029 374 : poNode = poPROJCS->GetChild( iChild );
5030 374 : if( !EQUAL(poNode->GetValue(),"PARAMETER")
5031 : || poNode->GetChildCount() != 2 )
5032 180 : continue;
5033 194 : pszParamStr = poNode->GetChild(0)->GetValue();
5034 194 : pszParamVal = poNode->GetChild(1)->GetValue();
5035 :
5036 194 : oValMap[pszParamStr] = atof(pszParamVal);
5037 : }
5038 :
5039 : /* Lookup mappings and fill output vector */
5040 41 : if ( poMap != poGenericMappings ) { /* specific mapping, loop over mapping values */
5041 :
5042 235 : for ( oAttIter = oAttMap.begin(); oAttIter != oAttMap.end(); oAttIter++ ) {
5043 :
5044 194 : pszGDALAtt = &(oAttIter->first);
5045 194 : pszNCDFAtt = &(oAttIter->second);
5046 194 : oValIter = oValMap.find( *pszGDALAtt );
5047 :
5048 194 : if ( oValIter != oValMap.end() ) {
5049 :
5050 194 : dfValue = oValIter->second;
5051 194 : bWriteVal = TRUE;
5052 :
5053 : /* special case for PS (Polar Stereographic) grid
5054 : See comments in netcdfdataset.h for this projection. */
5055 194 : if ( EQUAL( SRS_PP_LATITUDE_OF_ORIGIN, pszGDALAtt->c_str() ) &&
5056 : EQUAL(pszProjection, SRS_PT_POLAR_STEREOGRAPHIC) ) {
5057 3 : double dfLatPole = 0.0;
5058 3 : if ( dfValue > 0.0) dfLatPole = 90.0;
5059 3 : else dfLatPole = -90.0;
5060 : oOutList.push_back( std::make_pair( CF_PP_LAT_PROJ_ORIGIN,
5061 3 : dfLatPole ) );
5062 : }
5063 : /* special case for LCC-1SP
5064 : See comments in netcdfdataset.h for this projection. */
5065 191 : else if ( EQUAL( SRS_PP_SCALE_FACTOR, pszGDALAtt->c_str() ) &&
5066 : EQUAL(pszProjection, SRS_PT_LAMBERT_CONFORMAL_CONIC_1SP) ) {
5067 : /* default is to not write as it is not CF-1 */
5068 0 : bWriteVal = FALSE;
5069 : /* test if there is no standard_parallel1 */
5070 0 : if ( oValMap.find( std::string(CF_PP_STD_PARALLEL_1) ) == oValMap.end() ) {
5071 : /* if scale factor != 1.0 write value for GDAL, but this is not supported by CF-1 */
5072 0 : if ( !CPLIsEqual(dfValue,1.0) ) {
5073 : CPLError( CE_Failure, CPLE_NotSupported,
5074 : "NetCDF driver export of LCC-1SP with scale factor != 1.0 "
5075 : "and no standard_parallel1 is not CF-1 (bug #3324).\n"
5076 0 : "Use the 2SP variant which is supported by CF." );
5077 0 : bWriteVal = TRUE;
5078 : }
5079 : /* else copy standard_parallel1 from latitude_of_origin, because scale_factor=1.0 */
5080 : else {
5081 0 : oValIter2 = oValMap.find( std::string(SRS_PP_LATITUDE_OF_ORIGIN) );
5082 0 : if (oValIter2 != oValMap.end() ) {
5083 : oOutList.push_back( std::make_pair( CF_PP_STD_PARALLEL_1,
5084 0 : oValIter2->second) );
5085 : }
5086 : else {
5087 : CPLError( CE_Failure, CPLE_NotSupported,
5088 : "NetCDF driver export of LCC-1SP with no standard_parallel1 "
5089 0 : "and no latitude_of_origin is not suuported (bug #3324).");
5090 : }
5091 : }
5092 : }
5093 : }
5094 194 : if ( bWriteVal )
5095 194 : oOutList.push_back( std::make_pair( *pszNCDFAtt, dfValue ) );
5096 :
5097 : }
5098 : // else printf("NOT FOUND!!!\n");
5099 : }
5100 :
5101 : }
5102 : else { /* generic mapping, loop over projected values */
5103 :
5104 0 : for ( oValIter = oValMap.begin(); oValIter != oValMap.end(); oValIter++ ) {
5105 :
5106 0 : pszGDALAtt = &(oValIter->first);
5107 0 : dfValue = oValIter->second;
5108 :
5109 0 : oAttIter = oAttMap.find( *pszGDALAtt );
5110 :
5111 0 : if ( oAttIter != oAttMap.end() ) {
5112 0 : oOutList.push_back( std::make_pair( oAttIter->second, dfValue ) );
5113 : }
5114 : /* for SRS_PP_SCALE_FACTOR write 2 mappings */
5115 0 : else if ( EQUAL(pszGDALAtt->c_str(), SRS_PP_SCALE_FACTOR) ) {
5116 : oOutList.push_back( std::make_pair( CF_PP_SCALE_FACTOR_MERIDIAN,
5117 0 : dfValue ) );
5118 : oOutList.push_back( std::make_pair( CF_PP_SCALE_FACTOR_ORIGIN,
5119 0 : dfValue ) );
5120 : }
5121 : /* if not found insert the GDAL name */
5122 : else {
5123 0 : oOutList.push_back( std::make_pair( *pszGDALAtt, dfValue ) );
5124 : }
5125 : }
5126 : }
5127 :
5128 : /* Write all the values that were found */
5129 : // std::vector< std::pair<std::string,double> >::reverse_iterator it;
5130 : // for (it = oOutList.rbegin(); it != oOutList.rend(); it++ ) {
5131 41 : std::vector< std::pair<std::string,double> >::iterator it;
5132 238 : for (it = oOutList.begin(); it != oOutList.end(); it++ ) {
5133 197 : pszParamVal = (it->first).c_str();
5134 197 : dfValue = it->second;
5135 : /* Handle the STD_PARALLEL attrib */
5136 197 : if( EQUAL( pszParamVal, CF_PP_STD_PARALLEL_1 ) ) {
5137 16 : bFoundStdP1 = TRUE;
5138 16 : dfStdP[0] = dfValue;
5139 : }
5140 181 : else if( EQUAL( pszParamVal, CF_PP_STD_PARALLEL_2 ) ) {
5141 7 : bFoundStdP2 = TRUE;
5142 7 : dfStdP[1] = dfValue;
5143 : }
5144 : else {
5145 : nc_put_att_double( fpImage, NCDFVarID, pszParamVal,
5146 174 : NC_DOUBLE, 1,&dfValue );
5147 : }
5148 : }
5149 : /* Now write the STD_PARALLEL attrib */
5150 41 : if ( bFoundStdP1 ) {
5151 : /* one value or equal values */
5152 25 : if ( !bFoundStdP2 || dfStdP[0] == dfStdP[1] ) {
5153 : nc_put_att_double( fpImage, NCDFVarID, CF_PP_STD_PARALLEL,
5154 9 : NC_DOUBLE, 1, &dfStdP[0] );
5155 : }
5156 : else { /* two values */
5157 : nc_put_att_double( fpImage, NCDFVarID, CF_PP_STD_PARALLEL,
5158 7 : NC_DOUBLE, 2, dfStdP );
5159 : }
5160 41 : }
5161 41 : }
5162 :
5163 2483 : CPLErr NCDFSafeStrcat(char** ppszDest, char* pszSrc, size_t* nDestSize)
5164 : {
5165 : /* Reallocate the data string until the content fits */
5166 7092 : while(*nDestSize < (strlen(*ppszDest) + strlen(pszSrc) + 1)) {
5167 2126 : (*nDestSize) *= 2;
5168 2126 : *ppszDest = (char*) CPLRealloc((void*) *ppszDest, *nDestSize);
5169 : }
5170 2483 : strcat(*ppszDest, pszSrc);
5171 :
5172 2483 : return CE_None;
5173 : }
5174 :
5175 : /* helper function for NCDFGetAttr() */
5176 : /* sets pdfValue to first value returned */
5177 : /* and if bSetPszValue=True sets szValue with all values attribute values */
5178 : /* pszValue is the responsibility of the caller and must be freed */
5179 4651 : CPLErr NCDFGetAttr1( int nCdfId, int nVarId, const char *pszAttrName,
5180 : double *pdfValue, char **pszValue, int bSetPszValue )
5181 : {
5182 4651 : nc_type nAttrType = NC_NAT;
5183 4651 : size_t nAttrLen = 0;
5184 : size_t nAttrValueSize;
5185 4651 : int status = 0; /*rename this */
5186 : size_t m;
5187 : char szTemp[ NCDF_MAX_STR_LEN ];
5188 4651 : char *pszAttrValue = NULL;
5189 4651 : double dfValue = 0.0;
5190 :
5191 4651 : status = nc_inq_att( nCdfId, nVarId, pszAttrName, &nAttrType, &nAttrLen);
5192 4651 : if ( status != NC_NOERR )
5193 0 : return CE_Failure;
5194 :
5195 : /* Allocate guaranteed minimum size */
5196 4651 : nAttrValueSize = nAttrLen + 1;
5197 4651 : pszAttrValue = (char *) CPLCalloc( nAttrValueSize, sizeof( char ));
5198 4651 : *pszAttrValue = '\0';
5199 :
5200 4651 : if ( nAttrLen > 1 && nAttrType != NC_CHAR )
5201 279 : NCDFSafeStrcat(&pszAttrValue, (char *)"{ ", &nAttrValueSize);
5202 :
5203 4651 : switch (nAttrType) {
5204 : case NC_CHAR:
5205 3029 : nc_get_att_text( nCdfId, nVarId, pszAttrName, pszAttrValue );
5206 3029 : pszAttrValue[nAttrLen]='\0';
5207 3029 : dfValue = 0.0;
5208 3029 : break;
5209 : /* TODO support NC_UBYTE */
5210 : case NC_BYTE:
5211 : signed char *pscTemp;
5212 285 : pscTemp = (signed char *) CPLCalloc( nAttrLen, sizeof( signed char ) );
5213 285 : nc_get_att_schar( nCdfId, nVarId, pszAttrName, pscTemp );
5214 285 : dfValue = (double)pscTemp[0];
5215 289 : for(m=0; m < nAttrLen-1; m++) {
5216 4 : sprintf( szTemp, "%d, ", pscTemp[m] );
5217 4 : NCDFSafeStrcat(&pszAttrValue, szTemp, &nAttrValueSize);
5218 : }
5219 285 : sprintf( szTemp, "%d", pscTemp[m] );
5220 285 : NCDFSafeStrcat(&pszAttrValue, szTemp, &nAttrValueSize);
5221 285 : CPLFree(pscTemp);
5222 285 : break;
5223 : case NC_SHORT:
5224 : short *psTemp;
5225 213 : psTemp = (short *) CPLCalloc( nAttrLen, sizeof( short ) );
5226 213 : nc_get_att_short( nCdfId, nVarId, pszAttrName, psTemp );
5227 213 : dfValue = (double)psTemp[0];
5228 399 : for(m=0; m < nAttrLen-1; m++) {
5229 186 : sprintf( szTemp, "%hd, ", psTemp[m] );
5230 186 : NCDFSafeStrcat(&pszAttrValue, szTemp, &nAttrValueSize);
5231 : }
5232 213 : sprintf( szTemp, "%hd", psTemp[m] );
5233 213 : NCDFSafeStrcat(&pszAttrValue, szTemp, &nAttrValueSize);
5234 213 : CPLFree(psTemp);
5235 213 : break;
5236 : case NC_INT:
5237 : int *pnTemp;
5238 110 : pnTemp = (int *) CPLCalloc( nAttrLen, sizeof( int ) );
5239 110 : nc_get_att_int( nCdfId, nVarId, pszAttrName, pnTemp );
5240 110 : dfValue = (double)pnTemp[0];
5241 173 : for(m=0; m < nAttrLen-1; m++) {
5242 63 : sprintf( szTemp, "%d, ", pnTemp[m] );
5243 63 : NCDFSafeStrcat(&pszAttrValue, szTemp, &nAttrValueSize);
5244 : }
5245 110 : sprintf( szTemp, "%d", pnTemp[m] );
5246 110 : NCDFSafeStrcat(&pszAttrValue, szTemp, &nAttrValueSize);
5247 110 : CPLFree(pnTemp);
5248 110 : break;
5249 : case NC_FLOAT:
5250 : float *pfTemp;
5251 144 : pfTemp = (float *) CPLCalloc( nAttrLen, sizeof( float ) );
5252 144 : nc_get_att_float( nCdfId, nVarId, pszAttrName, pfTemp );
5253 144 : dfValue = (double)pfTemp[0];
5254 156 : for(m=0; m < nAttrLen-1; m++) {
5255 12 : sprintf( szTemp, "%.8g, ", pfTemp[m] );
5256 12 : NCDFSafeStrcat(&pszAttrValue, szTemp, &nAttrValueSize);
5257 : }
5258 144 : sprintf( szTemp, "%.8g", pfTemp[m] );
5259 144 : NCDFSafeStrcat(&pszAttrValue,szTemp, &nAttrValueSize);
5260 144 : CPLFree(pfTemp);
5261 144 : break;
5262 : case NC_DOUBLE:
5263 : double *pdfTemp;
5264 870 : pdfTemp = (double *) CPLCalloc(nAttrLen, sizeof(double));
5265 870 : nc_get_att_double( nCdfId, nVarId, pszAttrName, pdfTemp );
5266 870 : dfValue = pdfTemp[0];
5267 908 : for(m=0; m < nAttrLen-1; m++) {
5268 38 : sprintf( szTemp, "%.16g, ", pdfTemp[m] );
5269 38 : NCDFSafeStrcat(&pszAttrValue, szTemp, &nAttrValueSize);
5270 : }
5271 870 : sprintf( szTemp, "%.16g", pdfTemp[m] );
5272 870 : NCDFSafeStrcat(&pszAttrValue, szTemp, &nAttrValueSize);
5273 870 : CPLFree(pdfTemp);
5274 870 : break;
5275 : default:
5276 : CPLDebug( "GDAL_netCDF", "NCDFGetAttr unsupported type %d for attribute %s",
5277 0 : nAttrType,pszAttrName);
5278 0 : CPLFree( pszAttrValue );
5279 0 : pszAttrValue = NULL;
5280 : break;
5281 : }
5282 :
5283 4651 : if ( nAttrLen > 1 && nAttrType!= NC_CHAR )
5284 279 : NCDFSafeStrcat(&pszAttrValue, (char *)" }", &nAttrValueSize);
5285 :
5286 : // CPLDebug( "GDAL_netCDF", "NCDFGetAttr got %s=%s / %f",
5287 : // pszAttrName,pszAttrValue,dfValue);
5288 :
5289 : /* set return values */
5290 4651 : if ( bSetPszValue == TRUE ) *pszValue = pszAttrValue;
5291 160 : else CPLFree ( pszAttrValue );
5292 4651 : if ( pdfValue ) *pdfValue = dfValue;
5293 :
5294 4651 : return CE_None;
5295 : }
5296 :
5297 :
5298 : /* sets pdfValue to first value found */
5299 160 : CPLErr NCDFGetAttr( int nCdfId, int nVarId, const char *pszAttrName,
5300 : double *pdfValue )
5301 : {
5302 160 : return NCDFGetAttr1( nCdfId, nVarId, pszAttrName, pdfValue, NULL, FALSE );
5303 : }
5304 :
5305 : /* pszValue is the responsibility of the caller and must be freed */
5306 4491 : CPLErr NCDFGetAttr( int nCdfId, int nVarId, const char *pszAttrName,
5307 : char **pszValue )
5308 : {
5309 4491 : return NCDFGetAttr1( nCdfId, nVarId, pszAttrName, FALSE, pszValue, TRUE );
5310 : }
5311 :
5312 :
5313 : /* By default write NC_CHAR, but detect for int/float/double */
5314 206 : CPLErr NCDFPutAttr( int nCdfId, int nVarId,
5315 : const char *pszAttrName, const char *pszValue )
5316 : {
5317 206 : nc_type nAttrType = NC_CHAR;
5318 206 : nc_type nTmpAttrType = NC_CHAR;
5319 206 : size_t nAttrLen = 0;
5320 206 : int status = 0;
5321 : size_t i;
5322 : char szTemp[ NCDF_MAX_STR_LEN ];
5323 206 : char *pszTemp = NULL;
5324 206 : char **papszValues = NULL;
5325 :
5326 206 : int bIsArray = FALSE;
5327 206 : int nValue = 0;
5328 206 : float fValue = 0.0f;
5329 206 : double dfValue = 0.0;
5330 :
5331 206 : if ( EQUAL( pszValue, "" ) )
5332 0 : return CE_Failure;
5333 :
5334 206 : strcpy( szTemp,pszValue );
5335 :
5336 : /* tokenize to find multiple values */
5337 206 : int last_char = strlen(pszValue) - 1;
5338 206 : if ( ( szTemp[0] == '{' ) && ( szTemp[last_char] == '}' ) ) {
5339 6 : bIsArray = TRUE;
5340 6 : szTemp[0] = ' ';
5341 6 : szTemp[last_char] = ' ';
5342 : }
5343 : papszValues = CSLTokenizeString2( szTemp, ",", CSLT_STRIPLEADSPACES |
5344 206 : CSLT_STRIPENDSPACES );
5345 206 : if ( papszValues == NULL )
5346 0 : return CE_Failure;
5347 :
5348 206 : nAttrLen = CSLCount(papszValues);
5349 :
5350 : /* first detect type */
5351 206 : nAttrType = NC_CHAR;
5352 539 : for ( i=0; i<nAttrLen; i++ ) {
5353 333 : nTmpAttrType = NC_CHAR;
5354 333 : errno = 0;
5355 333 : nValue = strtol( papszValues[i], &pszTemp, 10 );
5356 : /* test for int */
5357 : /* TODO test for Byte and short - can this be done safely? */
5358 364 : if ( (errno == 0) && (papszValues[i] != pszTemp) && (*pszTemp == 0) ) {
5359 31 : nTmpAttrType = NC_INT;
5360 : }
5361 : else {
5362 : /* test for double */
5363 302 : errno = 0;
5364 302 : dfValue = strtod( papszValues[i], &pszTemp );
5365 302 : if ( (errno == 0) && (papszValues[i] != pszTemp) && (*pszTemp == 0) ) {
5366 : /* test for float instead of double */
5367 : /* strtof() is C89, which is not available in MSVC */
5368 : /* see if we loose precision if we cast to float and write to char* */
5369 22 : fValue = (float)dfValue;
5370 22 : sprintf( szTemp,"%.8g",fValue);
5371 22 : if ( EQUAL(szTemp, papszValues[i] ) )
5372 19 : nTmpAttrType = NC_FLOAT;
5373 : else
5374 3 : nTmpAttrType = NC_DOUBLE;
5375 : }
5376 : }
5377 333 : if ( nTmpAttrType > nAttrType )
5378 27 : nAttrType = nTmpAttrType;
5379 : }
5380 :
5381 : /* now write the data */
5382 390 : if ( !bIsArray && nAttrType == NC_CHAR ) {
5383 : status = nc_put_att_text( nCdfId, nVarId, pszAttrName,
5384 184 : strlen( pszValue ), pszValue );
5385 184 : NCDF_ERR(status);
5386 : }
5387 : else {
5388 :
5389 22 : switch( nAttrType ) {
5390 : case NC_INT:
5391 : int *pnTemp;
5392 12 : pnTemp = (int *) CPLCalloc( nAttrLen, sizeof( int ) );
5393 28 : for(i=0; i < nAttrLen; i++) {
5394 16 : pnTemp[i] = strtol( papszValues[i], &pszTemp, 10 );
5395 : }
5396 : status = nc_put_att_int( nCdfId, nVarId, pszAttrName,
5397 12 : NC_INT, nAttrLen, pnTemp );
5398 12 : NCDF_ERR(status);
5399 12 : CPLFree(pnTemp);
5400 12 : break;
5401 : case NC_FLOAT:
5402 : float *pfTemp;
5403 8 : pfTemp = (float *) CPLCalloc( nAttrLen, sizeof( float ) );
5404 114 : for(i=0; i < nAttrLen; i++) {
5405 106 : pfTemp[i] = (float)strtod( papszValues[i], &pszTemp );
5406 : }
5407 : status = nc_put_att_float( nCdfId, nVarId, pszAttrName,
5408 8 : NC_FLOAT, nAttrLen, pfTemp );
5409 8 : NCDF_ERR(status);
5410 8 : CPLFree(pfTemp);
5411 8 : break;
5412 : case NC_DOUBLE:
5413 : double *pdfTemp;
5414 2 : pdfTemp = (double *) CPLCalloc( nAttrLen, sizeof( double ) );
5415 5 : for(i=0; i < nAttrLen; i++) {
5416 3 : pdfTemp[i] = strtod( papszValues[i], &pszTemp );
5417 : }
5418 : status = nc_put_att_double( nCdfId, nVarId, pszAttrName,
5419 2 : NC_DOUBLE, nAttrLen, pdfTemp );
5420 2 : NCDF_ERR(status);
5421 2 : CPLFree(pdfTemp);
5422 2 : break;
5423 : default:
5424 0 : if ( papszValues ) CSLDestroy( papszValues );
5425 0 : return CE_Failure;
5426 : break;
5427 : }
5428 : }
5429 :
5430 206 : if ( papszValues ) CSLDestroy( papszValues );
5431 :
5432 206 : return CE_None;
5433 : }
|