ultimatepp/bazaar/plugin/gdal/frmts/netcdf/gmtdataset.cpp
cxl 23ff1e7e82 .gdal moved to bazaar
git-svn-id: svn://ultimatepp.org/upp/trunk@9273 f0d560ea-af0d-0410-9eb7-867de7ffcac7
2015-12-07 13:36:24 +00:00

623 lines
23 KiB
C++

/******************************************************************************
* $Id: gmtdataset.cpp 28785 2015-03-26 20:46:45Z goatbar $
*
* Project: netCDF read/write Driver
* Purpose: GDAL bindings over netCDF library for GMT Grids.
* Author: Frank Warmerdam, warmerdam@pobox.com
*
******************************************************************************
* Copyright (c) 2004, Frank Warmerdam
* Copyright (c) 2007-2013, Even Rouault <even dot rouault at mines-paris dot org>
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
****************************************************************************/
#include "gdal_pam.h"
#include "gdal_frmts.h"
#include "netcdf.h"
#include "cpl_multiproc.h"
CPL_CVSID("$Id: gmtdataset.cpp 28785 2015-03-26 20:46:45Z goatbar $");
extern CPLMutex *hNCMutex; /* shared with netcdf. See netcdfdataset.cpp */
/************************************************************************/
/* ==================================================================== */
/* GMTDataset */
/* ==================================================================== */
/************************************************************************/
class GMTRasterBand;
class GMTDataset : public GDALPamDataset
{
int z_id;
double adfGeoTransform[6];
public:
int cdfid;
~GMTDataset();
static GDALDataset *Open( GDALOpenInfo * );
CPLErr GetGeoTransform( double * padfTransform );
};
/************************************************************************/
/* ==================================================================== */
/* GMTRasterBand */
/* ==================================================================== */
/************************************************************************/
class GMTRasterBand : public GDALPamRasterBand
{
nc_type nc_datatype;
int nZId;
public:
GMTRasterBand( GMTDataset *poDS, int nZId, int nBand );
virtual CPLErr IReadBlock( int, int, void * );
};
/************************************************************************/
/* GMTRasterBand() */
/************************************************************************/
GMTRasterBand::GMTRasterBand( GMTDataset *poDS, int nZId, int nBand )
{
this->poDS = poDS;
this->nBand = nBand;
this->nZId = nZId;
nBlockXSize = poDS->GetRasterXSize();
nBlockYSize = 1;
/* -------------------------------------------------------------------- */
/* Get the type of the "z" variable, our target raster array. */
/* -------------------------------------------------------------------- */
if( nc_inq_var( poDS->cdfid, nZId, NULL, &nc_datatype, NULL, NULL,
NULL ) != NC_NOERR )
{
CPLError( CE_Failure, CPLE_AppDefined,
"Error in nc_var_inq() on 'z'." );
return;
}
if( nc_datatype == NC_BYTE )
eDataType = GDT_Byte;
else if( nc_datatype == NC_SHORT )
eDataType = GDT_Int16;
else if( nc_datatype == NC_INT )
eDataType = GDT_Int32;
else if( nc_datatype == NC_FLOAT )
eDataType = GDT_Float32;
else if( nc_datatype == NC_DOUBLE )
eDataType = GDT_Float64;
else
{
if( nBand == 1 )
CPLError( CE_Warning, CPLE_AppDefined,
"Unsupported GMT datatype (%d), treat as Float32.",
(int) nc_datatype );
eDataType = GDT_Float32;
}
}
/************************************************************************/
/* IReadBlock() */
/************************************************************************/
CPLErr GMTRasterBand::IReadBlock( CPL_UNUSED int nBlockXOff, int nBlockYOff,
void * pImage )
{
size_t start[2], edge[2];
int nErr = NC_NOERR;
int cdfid = ((GMTDataset *) poDS)->cdfid;
CPLMutexHolderD(&hNCMutex);
start[0] = nBlockYOff * nBlockXSize;
edge[0] = nBlockXSize;
if( eDataType == GDT_Byte )
nErr = nc_get_vara_uchar( cdfid, nZId, start, edge,
(unsigned char *) pImage );
else if( eDataType == GDT_Int16 )
nErr = nc_get_vara_short( cdfid, nZId, start, edge,
(short int *) pImage );
else if( eDataType == GDT_Int32 )
{
if( sizeof(long) == 4 )
nErr = nc_get_vara_long( cdfid, nZId, start, edge,
(long *) pImage );
else
nErr = nc_get_vara_int( cdfid, nZId, start, edge,
(int *) pImage );
}
else if( eDataType == GDT_Float32 )
nErr = nc_get_vara_float( cdfid, nZId, start, edge,
(float *) pImage );
else if( eDataType == GDT_Float64 )
nErr = nc_get_vara_double( cdfid, nZId, start, edge,
(double *) pImage );
if( nErr != NC_NOERR )
{
CPLError( CE_Failure, CPLE_AppDefined,
"GMT scanline fetch failed: %s",
nc_strerror( nErr ) );
return CE_Failure;
}
else
return CE_None;
}
/************************************************************************/
/* ==================================================================== */
/* GMTDataset */
/* ==================================================================== */
/************************************************************************/
/************************************************************************/
/* ~GMTDataset() */
/************************************************************************/
GMTDataset::~GMTDataset()
{
FlushCache();
nc_close (cdfid);
}
/************************************************************************/
/* GetGeoTransform() */
/************************************************************************/
CPLErr GMTDataset::GetGeoTransform( double * padfTransform )
{
memcpy( padfTransform, adfGeoTransform, sizeof(double) * 6 );
return CE_None;
}
/************************************************************************/
/* Open() */
/************************************************************************/
GDALDataset *GMTDataset::Open( GDALOpenInfo * poOpenInfo )
{
/* -------------------------------------------------------------------- */
/* Does this file have the GMT magic number? */
/* -------------------------------------------------------------------- */
if( poOpenInfo->fpL == NULL || poOpenInfo->nHeaderBytes < 50 )
return NULL;
if( poOpenInfo->pabyHeader[0] != 'C'
|| poOpenInfo->pabyHeader[1] != 'D'
|| poOpenInfo->pabyHeader[2] != 'F'
|| poOpenInfo->pabyHeader[3] != 1 )
return NULL;
CPLMutexHolderD(&hNCMutex);
/* -------------------------------------------------------------------- */
/* Try opening the dataset. */
/* -------------------------------------------------------------------- */
int cdfid, nm_id, dim_count, z_id;
if( nc_open( poOpenInfo->pszFilename, NC_NOWRITE, &cdfid ) != NC_NOERR )
return NULL;
if( nc_inq_varid( cdfid, "dimension", &nm_id ) != NC_NOERR
|| nc_inq_varid( cdfid, "z", &z_id ) != NC_NOERR )
{
#ifdef notdef
CPLError( CE_Warning, CPLE_AppDefined,
"%s is a GMT file, but not in GMT configuration.",
poOpenInfo->pszFilename );
#endif
nc_close( cdfid );
return NULL;
}
if( nc_inq_ndims( cdfid, &dim_count ) != NC_NOERR || dim_count < 2 )
{
nc_close( cdfid );
return NULL;
}
/* -------------------------------------------------------------------- */
/* Confirm the requested access is supported. */
/* -------------------------------------------------------------------- */
if( poOpenInfo->eAccess == GA_Update )
{
nc_close( cdfid );
CPLError( CE_Failure, CPLE_NotSupported,
"The GMT driver does not support update access to existing"
" datasets.\n" );
return NULL;
}
/* -------------------------------------------------------------------- */
/* Create a corresponding GDALDataset. */
/* -------------------------------------------------------------------- */
GMTDataset *poDS;
CPLReleaseMutex(hNCMutex); // Release mutex otherwise we'll deadlock with GDALDataset own mutex
poDS = new GMTDataset();
CPLAcquireMutex(hNCMutex, 1000.0);
poDS->cdfid = cdfid;
poDS->z_id = z_id;
/* -------------------------------------------------------------------- */
/* Get dimensions. If we can't find this, then this is a */
/* GMT file, but not a normal grid product. */
/* -------------------------------------------------------------------- */
size_t start[2], edge[2];
int nm[2];
start[0] = 0;
edge[0] = 2;
nc_get_vara_int(cdfid, nm_id, start, edge, nm);
poDS->nRasterXSize = nm[0];
poDS->nRasterYSize = nm[1];
/* -------------------------------------------------------------------- */
/* Fetch "z" attributes scale_factor, add_offset, and */
/* node_offset. */
/* -------------------------------------------------------------------- */
double scale_factor=1.0, add_offset=0.0;
int node_offset = 1;
nc_get_att_double( cdfid, z_id, "scale_factor", &scale_factor );
nc_get_att_double( cdfid, z_id, "add_offset", &add_offset );
nc_get_att_int( cdfid, z_id, "node_offset", &node_offset );
/* -------------------------------------------------------------------- */
/* Get x/y range information. */
/* -------------------------------------------------------------------- */
int x_range_id, y_range_id;
if( nc_inq_varid (cdfid, "x_range", &x_range_id) == NC_NOERR
&& nc_inq_varid (cdfid, "y_range", &y_range_id) == NC_NOERR )
{
double x_range[2], y_range[2];
nc_get_vara_double( cdfid, x_range_id, start, edge, x_range );
nc_get_vara_double( cdfid, y_range_id, start, edge, y_range );
// Pixel is area
if( node_offset == 1 )
{
poDS->adfGeoTransform[0] = x_range[0];
poDS->adfGeoTransform[1] =
(x_range[1] - x_range[0]) / poDS->nRasterXSize;
poDS->adfGeoTransform[2] = 0.0;
poDS->adfGeoTransform[3] = y_range[1];
poDS->adfGeoTransform[4] = 0.0;
poDS->adfGeoTransform[5] =
(y_range[0] - y_range[1]) / poDS->nRasterYSize;
}
// Pixel is point - offset by half pixel.
else /* node_offset == 0 */
{
poDS->adfGeoTransform[1] =
(x_range[1] - x_range[0]) / (poDS->nRasterXSize-1);
poDS->adfGeoTransform[0] =
x_range[0] - poDS->adfGeoTransform[1]*0.5;
poDS->adfGeoTransform[2] = 0.0;
poDS->adfGeoTransform[4] = 0.0;
poDS->adfGeoTransform[5] =
(y_range[0] - y_range[1]) / (poDS->nRasterYSize-1);
poDS->adfGeoTransform[3] =
y_range[1] - poDS->adfGeoTransform[5]*0.5;
}
}
else
{
poDS->adfGeoTransform[0] = 0.0;
poDS->adfGeoTransform[1] = 1.0;
poDS->adfGeoTransform[2] = 0.0;
poDS->adfGeoTransform[3] = 0.0;
poDS->adfGeoTransform[4] = 0.0;
poDS->adfGeoTransform[5] = 1.0;
}
/* -------------------------------------------------------------------- */
/* Create band information objects. */
/* -------------------------------------------------------------------- */
poDS->nBands = 1;
poDS->SetBand( 1, new GMTRasterBand( poDS, z_id, 1 ));
if( scale_factor != 1.0 || add_offset != 0.0 )
{
poDS->GetRasterBand(1)->SetOffset( add_offset );
poDS->GetRasterBand(1)->SetScale( scale_factor );
}
/* -------------------------------------------------------------------- */
/* Initialize any PAM information. */
/* -------------------------------------------------------------------- */
poDS->SetDescription( poOpenInfo->pszFilename );
CPLReleaseMutex(hNCMutex); // Release mutex otherwise we'll deadlock with GDALDataset own mutex
poDS->TryLoadXML();
/* -------------------------------------------------------------------- */
/* Check for external overviews. */
/* -------------------------------------------------------------------- */
poDS->oOvManager.Initialize( poDS, poOpenInfo->pszFilename, poOpenInfo->GetSiblingFiles() );
CPLAcquireMutex(hNCMutex, 1000.0);
return( poDS );
}
/************************************************************************/
/* GMTCreateCopy() */
/* */
/* This code mostly cribbed from GMT's "gmt_cdf.c" module. */
/************************************************************************/
static GDALDataset *
GMTCreateCopy( const char * pszFilename, GDALDataset *poSrcDS,
int bStrict, CPL_UNUSED char ** papszOptions,
CPL_UNUSED GDALProgressFunc pfnProgress,
CPL_UNUSED void * pProgressData )
{
/* -------------------------------------------------------------------- */
/* Figure out general characteristics. */
/* -------------------------------------------------------------------- */
nc_type nc_datatype;
GDALRasterBand *poBand;
int nXSize, nYSize;
CPLMutexHolderD(&hNCMutex);
if( poSrcDS->GetRasterCount() != 1 )
{
CPLError( CE_Failure, CPLE_AppDefined,
"Currently GMT export only supports 1 band datasets." );
return NULL;
}
poBand = poSrcDS->GetRasterBand(1);
nXSize = poSrcDS->GetRasterXSize();
nYSize = poSrcDS->GetRasterYSize();
if( poBand->GetRasterDataType() == GDT_Int16 )
nc_datatype = NC_SHORT;
else if( poBand->GetRasterDataType() == GDT_Int32 )
nc_datatype = NC_INT;
else if( poBand->GetRasterDataType() == GDT_Float32 )
nc_datatype = NC_FLOAT;
else if( poBand->GetRasterDataType() == GDT_Float64 )
nc_datatype = NC_DOUBLE;
else if( bStrict )
{
CPLError( CE_Failure, CPLE_AppDefined,
"Band data type %s not supported in GMT, giving up.",
GDALGetDataTypeName( poBand->GetRasterDataType() ) );
return NULL;
}
else if( poBand->GetRasterDataType() == GDT_Byte )
nc_datatype = NC_SHORT;
else if( poBand->GetRasterDataType() == GDT_UInt16 )
nc_datatype = NC_INT;
else if( poBand->GetRasterDataType() == GDT_UInt32 )
nc_datatype = NC_INT;
else
nc_datatype = NC_FLOAT;
/* -------------------------------------------------------------------- */
/* Establish bounds from geotransform. */
/* -------------------------------------------------------------------- */
double adfGeoTransform[6];
double dfXMax, dfYMin;
poSrcDS->GetGeoTransform( adfGeoTransform );
if( adfGeoTransform[2] != 0.0 || adfGeoTransform[4] != 0.0 )
{
CPLError( bStrict ? CE_Failure : CE_Warning, CPLE_AppDefined,
"Geotransform has rotational coefficients not supported in GMT." );
if( bStrict )
return NULL;
}
dfXMax = adfGeoTransform[0] + adfGeoTransform[1] * nXSize;
dfYMin = adfGeoTransform[3] + adfGeoTransform[5] * nYSize;
/* -------------------------------------------------------------------- */
/* Create base file. */
/* -------------------------------------------------------------------- */
int cdfid, err;
err = nc_create (pszFilename, NC_CLOBBER,&cdfid);
if( err != NC_NOERR )
{
CPLError( CE_Failure, CPLE_AppDefined,
"nc_create(%s): %s",
pszFilename, nc_strerror( err ) );
return NULL;
}
/* -------------------------------------------------------------------- */
/* Define the dimensions and so forth. */
/* -------------------------------------------------------------------- */
int side_dim, xysize_dim, dims[1];
int x_range_id, y_range_id, z_range_id, inc_id, nm_id, z_id;
nc_def_dim(cdfid, "side", 2, &side_dim);
nc_def_dim(cdfid, "xysize", (int) (nXSize * nYSize), &xysize_dim);
dims[0] = side_dim;
nc_def_var (cdfid, "x_range", NC_DOUBLE, 1, dims, &x_range_id);
nc_def_var (cdfid, "y_range", NC_DOUBLE, 1, dims, &y_range_id);
nc_def_var (cdfid, "z_range", NC_DOUBLE, 1, dims, &z_range_id);
nc_def_var (cdfid, "spacing", NC_DOUBLE, 1, dims, &inc_id);
nc_def_var (cdfid, "dimension", NC_LONG, 1, dims, &nm_id);
dims[0] = xysize_dim;
nc_def_var (cdfid, "z", nc_datatype, 1, dims, &z_id);
/* -------------------------------------------------------------------- */
/* Assign attributes. */
/* -------------------------------------------------------------------- */
double default_scale = 1.0;
double default_offset = 0.0;
int default_node_offset = 1; // pixel is area
nc_put_att_text (cdfid, x_range_id, "units", 7, "meters");
nc_put_att_text (cdfid, y_range_id, "units", 7, "meters");
nc_put_att_text (cdfid, z_range_id, "units", 7, "meters");
nc_put_att_double (cdfid, z_id, "scale_factor", NC_DOUBLE, 1,
&default_scale );
nc_put_att_double (cdfid, z_id, "add_offset", NC_DOUBLE, 1,
&default_offset );
nc_put_att_int (cdfid, z_id, "node_offset", NC_LONG, 1,
&default_node_offset );
nc_put_att_text (cdfid, NC_GLOBAL, "title", 1, "");
nc_put_att_text (cdfid, NC_GLOBAL, "source", 1, "");
/* leave define mode */
nc_enddef (cdfid);
/* -------------------------------------------------------------------- */
/* Get raster min/max. */
/* -------------------------------------------------------------------- */
double adfMinMax[2];
GDALComputeRasterMinMax( (GDALRasterBandH) poBand, FALSE, adfMinMax );
/* -------------------------------------------------------------------- */
/* Set range variables. */
/* -------------------------------------------------------------------- */
size_t start[2], edge[2];
double dummy[2];
int nm[2];
start[0] = 0;
edge[0] = 2;
dummy[0] = adfGeoTransform[0];
dummy[1] = dfXMax;
nc_put_vara_double(cdfid, x_range_id, start, edge, dummy);
dummy[0] = dfYMin;
dummy[1] = adfGeoTransform[3];
nc_put_vara_double(cdfid, y_range_id, start, edge, dummy);
dummy[0] = adfGeoTransform[1];
dummy[1] = -adfGeoTransform[5];
nc_put_vara_double(cdfid, inc_id, start, edge, dummy);
nm[0] = nXSize;
nm[1] = nYSize;
nc_put_vara_int(cdfid, nm_id, start, edge, nm);
nc_put_vara_double(cdfid, z_range_id, start, edge, adfMinMax);
/* -------------------------------------------------------------------- */
/* Write out the image one scanline at a time. */
/* -------------------------------------------------------------------- */
double *padfData;
int iLine;
padfData = (double *) CPLMalloc( sizeof(double) * nXSize );
edge[0] = nXSize;
for( iLine = 0; iLine < nYSize; iLine++ )
{
start[0] = iLine * nXSize;
poBand->RasterIO( GF_Read, 0, iLine, nXSize, 1,
padfData, nXSize, 1, GDT_Float64, 0, 0, NULL );
err = nc_put_vara_double( cdfid, z_id, start, edge, padfData );
if( err != NC_NOERR )
{
CPLError( CE_Failure, CPLE_AppDefined,
"nc_put_vara_double(%s): %s",
pszFilename, nc_strerror( err ) );
nc_close (cdfid);
return( NULL );
}
}
CPLFree( padfData );
/* -------------------------------------------------------------------- */
/* Close file, and reopen. */
/* -------------------------------------------------------------------- */
nc_close (cdfid);
/* -------------------------------------------------------------------- */
/* Re-open dataset, and copy any auxiliary pam information. */
/* -------------------------------------------------------------------- */
GDALPamDataset *poDS = (GDALPamDataset *)
GDALOpen( pszFilename, GA_ReadOnly );
if( poDS )
poDS->CloneInfo( poSrcDS, GCIF_PAM_DEFAULT );
return poDS;
}
/************************************************************************/
/* GDALRegister_GMT() */
/************************************************************************/
void GDALRegister_GMT()
{
GDALDriver *poDriver;
if (! GDAL_CHECK_VERSION("GMT driver"))
return;
if( GDALGetDriverByName( "GMT" ) == NULL )
{
poDriver = new GDALDriver();
poDriver->SetDescription( "GMT" );
poDriver->SetMetadataItem( GDAL_DCAP_RASTER, "YES" );
poDriver->SetMetadataItem( GDAL_DMD_LONGNAME,
"GMT NetCDF Grid Format" );
poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC,
"frmt_various.html#GMT" );
poDriver->SetMetadataItem( GDAL_DMD_EXTENSION, "nc" );
poDriver->SetMetadataItem( GDAL_DMD_CREATIONDATATYPES,
"Int16 Int32 Float32 Float64" );
poDriver->pfnOpen = GMTDataset::Open;
poDriver->pfnCreateCopy = GMTCreateCopy;
GetGDALDriverManager()->RegisterDriver( poDriver );
}
}