LCOV - code coverage report
Current view: directory - frmts/netcdf - netcdfdataset.cpp (source / functions) Found Hit Coverage
Test: gdal_filtered.info Lines: 2818 2305 81.8 %
Date: 2013-03-30 Functions: 85 77 90.6 %

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

Generated by: LCOV version 1.7