LCOV - code coverage report
Current view: directory - frmts/netcdf - netcdfdataset.cpp (source / functions) Found Hit Coverage
Test: gdal_filtered.info Lines: 2771 2274 82.1 %
Date: 2012-12-26 Functions: 84 76 90.5 %

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

Generated by: LCOV version 1.7