Index: netcdfdataset.cpp =================================================================== --- netcdfdataset.cpp (revision 24215) +++ netcdfdataset.cpp (working copy) @@ -4142,10 +4142,13 @@ int *panBandZLev=NULL; int *paDimIds=NULL; size_t xdim, ydim; - char szTemp[NC_MAX_NAME]; + char szTemp[NC_MAX_NAME]; CPLString osSubdatasetName; int bTreatAsSubdataset; + char szVarName[NC_MAX_NAME]; + std::vector oBandNumbers; + int nBandNumber = -1; char **papszIgnoreVars = NULL; char *pszTemp = NULL; @@ -4173,53 +4176,76 @@ netCDFDataset *poDS; poDS = new netCDFDataset(); - poDS->SetDescription( poOpenInfo->pszFilename ); - /* -------------------------------------------------------------------- */ /* Check if filename start with NETCDF: tag */ /* -------------------------------------------------------------------- */ if( EQUALN( poOpenInfo->pszFilename,"NETCDF:",7) ) { - char **papszName = + char **papszTokens = CSLTokenizeString2( poOpenInfo->pszFilename, ":", CSLT_HONOURSTRINGS|CSLT_PRESERVEESCAPES ); - + int nFieldCount = CSLCount(papszTokens); + int nOffset = 1; + /* -------------------------------------------------------------------- */ /* Check for drive name in windows NETCDF:"D:\... */ /* -------------------------------------------------------------------- */ - if ( CSLCount(papszName) == 4 && - strlen(papszName[1]) == 1 && - (papszName[2][0] == '/' || papszName[2][0] == '\\') ) + if ( nFieldCount >= 4 && + strlen(papszTokens[1]) == 1 && + (papszTokens[2][0] == '/' || papszTokens[2][0] == '\\') ) { - poDS->osFilename = papszName[1]; + poDS->osFilename = papszTokens[1]; poDS->osFilename += ':'; - poDS->osFilename += papszName[2]; - osSubdatasetName = papszName[3]; - bTreatAsSubdataset = TRUE; - CSLDestroy( papszName ); + poDS->osFilename += papszTokens[2]; + nOffset = 2; } - else if( CSLCount(papszName) == 3 ) + else { + poDS->osFilename = papszTokens[1]; + } + + /* -------------------------------------------------------------------- */ + /* Check optional arguments passed to file string */ + /* NETCDF:filename.nc:varname:bandno */ + /* -------------------------------------------------------------------- */ + if( nFieldCount >= nOffset + 2 ) { - poDS->osFilename = papszName[1]; - osSubdatasetName = papszName[2]; + osSubdatasetName = papszTokens[1+nOffset]; bTreatAsSubdataset = TRUE; - CSLDestroy( papszName ); + if( nFieldCount > nOffset + 2 ) { + /* parse band numbers */ + int nTemp1=-1; + for ( int i = nOffset + 2; i < nFieldCount; i++ ) { + nTemp1 = atoi(papszTokens[i]); + if ( nTemp1 < 1 || + CPLGetValueType(papszTokens[i]) != CPL_VALUE_INTEGER ) { + // should exit here + CPLError( CE_Warning, CPLE_AppDefined, + "[%s] is not a positive integer",papszTokens[i] ); + } + else + oBandNumbers.push_back(nTemp1-1); + } + } } - else if( CSLCount(papszName) == 2 ) + else if( nFieldCount == nOffset + 1 ) { - poDS->osFilename = papszName[1]; osSubdatasetName = ""; bTreatAsSubdataset = FALSE; - CSLDestroy( papszName ); } else { - CSLDestroy( papszName ); + CSLDestroy( papszTokens ); delete poDS; CPLError( CE_Failure, CPLE_AppDefined, - "Failed to parse NETCDF: prefix string into expected 2, 3 or 4 fields." ); + "Failed to parse NETCDF: prefix string into expected 2, 3 or 4 fields.\n" + "Example: NETCDF:filename.nc:varname or NETCDF:c:\\\\filename.nc:varname" ); return NULL; } + CSLDestroy( papszTokens ); + CPLDebug( "GDAL_netCDF", "filename=%s subdataset=%d, subdatasetname=%s offset=%d #bandnumbers=%ld", + poDS->osFilename.c_str(), bTreatAsSubdataset, + osSubdatasetName.c_str(), nOffset, oBandNumbers.size() ); + /* Identify Format from real file, with bCheckExt=FALSE */ GDALOpenInfo* poOpenInfo2 = new GDALOpenInfo(poDS->osFilename.c_str(), GA_ReadOnly ); poDS->nFormat = IdentifyFormat( poOpenInfo2, FALSE ); @@ -4237,6 +4263,10 @@ poDS->nFormat = nTmpFormat; } + /* disable PAM if requested a subset of bands, because any stats will be invalid */ + if ( ! oBandNumbers.empty() ) + poDS->nPamFlags |= GPF_DISABLED; + /* -------------------------------------------------------------------- */ /* Try opening the dataset. */ /* -------------------------------------------------------------------- */ @@ -4299,7 +4329,24 @@ /* -------------------------------------------------------------------- */ if( bTreatAsSubdataset ) { - status = nc_inq_varid( cdfid, osSubdatasetName, &var); + // status = nc_inq_varid( cdfid, osSubdatasetName, &var); + /* detect integer value, convert to actual variable value */ + if ( CPLGetValueType(osSubdatasetName.c_str())==CPL_VALUE_INTEGER ) { + var = atoi( osSubdatasetName.c_str() ); + status = nc_inq_varname( cdfid, var, szVarName ); + if( status == NC_NOERR ) { + CPLDebug( "GDAL_netCDF", + "parsed variable number %d corresponding to variable name %s", + var, szVarName ); + osSubdatasetName = szVarName; + status = NC_NOERR; + } + } + /* get varid from name */ + else { + status = nc_inq_varid( cdfid, osSubdatasetName, &var); + } + if( status != NC_NOERR ) { CPLError( CE_Warning, CPLE_AppDefined, "%s is a netCDF file, but %s is not a variable.", @@ -4416,7 +4463,6 @@ /* -------------------------------------------------------------------- */ if( !bTreatAsSubdataset ) // nCount must be 1! { - char szVarName[NC_MAX_NAME]; nc_inq_varname( cdfid, nVarID, szVarName); osSubdatasetName = szVarName; } @@ -4451,7 +4497,7 @@ /* Check if somebody tried to pass a variable with less than 2D */ /* -------------------------------------------------------------------- */ if ( nd < 2 ) { - CPLError( CE_Warning, CPLE_AppDefined, + CPLError( CE_Fatal, CPLE_AppDefined, "Variable has %d dimension(s) - not supported.", nd ); CPLFree( paDimIds ); CPLFree( panBandDimPos ); @@ -4460,6 +4506,27 @@ } /* -------------------------------------------------------------------- */ +/* Check the band number arguments */ +/* -------------------------------------------------------------------- */ + if ( (int) oBandNumbers.size() > nd - 2 ) { + CPLError( CE_Fatal, CPLE_AppDefined, + "Variable has %d dimension(s) but %ld band numbers " + "given as arguments - must provide 1 or %d.", + nd, oBandNumbers.size(), nd - 2 ); + CPLFree( paDimIds ); + CPLFree( panBandDimPos ); + delete poDS; + return NULL; + } + if ( oBandNumbers.size() == 1 ) { + nBandNumber = oBandNumbers[0]; + } + else if ( oBandNumbers.size() > 1 ) { + //base value, will be modified later in "Save non-spatial dimension info" + nBandNumber = 0; + } + +/* -------------------------------------------------------------------- */ /* CF-1 Convention */ /* dimensions to appear in the relative order T, then Z, then Y, */ /* then X to the file. All other dimensions should, whenever */ @@ -4601,9 +4668,9 @@ nTotLevCount = 1; if ( nd > 2 ) { + int k=oBandNumbers.size()-1; //index in oBandNumbers, must loop backwards nDim=2; panBandZLev = (int *)CPLCalloc( nd-2, sizeof( int ) ); - strcpy( szExtraDimNames, (char*)"{"); for( j=0; j < nd; j++ ){ @@ -4612,9 +4679,12 @@ nc_inq_dimlen ( cdfid, paDimIds[j], &lev_count ); nTotLevCount *= lev_count; panBandZLev[ nDim-2 ] = lev_count; + if ( oBandNumbers.size() > 1 ) + nBandNumber += lev_count*oBandNumbers[k]; panBandDimPos[ nDim++ ] = j; //Save Position of ZDim //Save non-spatial dimension names - if ( nc_inq_dimname( cdfid, paDimIds[j], szDimName ) + if ( oBandNumbers.empty() && + nc_inq_dimname( cdfid, paDimIds[j], szDimName ) == NC_NOERR ) { strcat( szExtraDimNames, szDimName ); if ( j < nd-3 ) { @@ -4633,11 +4703,14 @@ CPLFree( pszTemp ); } } + k--; } } - strcat( szExtraDimNames, (char *)"}" ); - poDS->papszMetadata = CSLSetNameValue( poDS->papszMetadata, - "NETCDF_DIM_EXTRA", szExtraDimNames ); + if ( oBandNumbers.empty() ) { + strcat( szExtraDimNames, (char *)"}" ); + poDS->papszMetadata = CSLSetNameValue( poDS->papszMetadata, + "NETCDF_DIM_EXTRA", szExtraDimNames ); + } } i=0; @@ -4649,27 +4722,50 @@ /* -------------------------------------------------------------------- */ /* Create bands */ /* -------------------------------------------------------------------- */ + /* first verify that nBandNumber is within bounds */ + if ( nBandNumber != -1 && nBandNumber >= (int)nTotLevCount ) { + CPLError( CE_Fatal, CPLE_AppDefined, + "Requested band number %d is greater that variable band count of %ld", + nBandNumber+1, nTotLevCount ); + CPLFree( paDimIds ); + CPLFree( panBandDimPos ); + if ( panBandZLev ) + CPLFree( panBandZLev ); + delete poDS; + return NULL; + } + for ( unsigned int lev = 0; lev < nTotLevCount ; lev++ ) { - netCDFRasterBand *poBand = - new netCDFRasterBand(poDS, var, nDim, lev, - panBandZLev, panBandDimPos, - paDimIds, i+1 ); - poDS->SetBand( i+1, poBand ); - i++; + if ( nBandNumber == -1 || nBandNumber == (int)lev ) { + netCDFRasterBand *poBand = + new netCDFRasterBand(poDS, var, nDim, lev, + panBandZLev, panBandDimPos, + paDimIds, i+1 ); + poDS->SetBand( i+1, poBand ); + i++; + if ( nBandNumber != -1 ) + break; + } } + + if ( nBandNumber == -1 ) + poDS->nBands = i; + else + poDS->nBands = 1; CPLFree( paDimIds ); CPLFree( panBandDimPos ); if ( panBandZLev ) CPLFree( panBandZLev ); - poDS->nBands = i; // Handle angular geographic coordinates here /* -------------------------------------------------------------------- */ /* Initialize any PAM information. */ /* -------------------------------------------------------------------- */ + poDS->SetDescription( poOpenInfo->pszFilename ); + if( bTreatAsSubdataset ) { poDS->SetPhysicalFilename( poDS->osFilename );