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