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