LCOV - code coverage report
Current view: directory - frmts/netcdf - netcdfdataset.cpp (source / functions) Found Hit Coverage
Test: gdal_filtered.info Lines: 2744 2235 81.5 %
Date: 2012-04-28 Functions: 84 75 89.3 %

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

Generated by: LCOV version 1.7