426 changes: 239 additions & 187 deletions src/providers/gdal/qgsgdalprovider.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -196,192 +196,7 @@ QgsGdalProvider::QgsGdalProvider( QString const & uri )
}

QgsDebugMsg( "GdalDataset opened" );

for ( int i = 0; i < GDALGetRasterCount( mGdalBaseDataset ); i++ )
{
mMinMaxComputed.append( false );
mMinimum.append( 0 );
mMaximum.append( 0 );
}

// Check if we need a warped VRT for this file.
bool hasGeoTransform = GDALGetGeoTransform( mGdalBaseDataset, mGeoTransform ) == CE_None;
if (( hasGeoTransform
&& ( mGeoTransform[1] < 0.0
|| mGeoTransform[2] != 0.0
|| mGeoTransform[4] != 0.0
|| mGeoTransform[5] > 0.0 ) )
|| GDALGetGCPCount( mGdalBaseDataset ) > 0 )
{
QgsLogger::warning( "Creating Warped VRT." );

mGdalDataset =
GDALAutoCreateWarpedVRT( mGdalBaseDataset, NULL, NULL,
GRA_NearestNeighbour, 0.2, NULL );

if ( mGdalDataset == NULL )
{
QgsLogger::warning( "Warped VRT Creation failed." );
mGdalDataset = mGdalBaseDataset;
GDALReferenceDataset( mGdalDataset );
}
else
{
GDALGetGeoTransform( mGdalDataset, mGeoTransform );
}
}
else
{
mGdalDataset = mGdalBaseDataset;
GDALReferenceDataset( mGdalDataset );
}

if ( !hasGeoTransform )
{
// Initialise the affine transform matrix
mGeoTransform[0] = 0;
mGeoTransform[1] = 1;
mGeoTransform[2] = 0;
mGeoTransform[3] = 0;
mGeoTransform[4] = 0;
mGeoTransform[5] = -1;
}

// get sublayers
mSubLayers = QgsGdalProvider::subLayers( mGdalDataset );

// check if this file has bands or subdatasets
CPLErrorReset();
GDALRasterBandH myGDALBand = GDALGetRasterBand( mGdalDataset, 1 ); //just use the first band
if ( myGDALBand == NULL )
{
QString msg = QString::fromUtf8( CPLGetLastErrorMsg() );

// if there are no subdatasets, then close the dataset
if ( mSubLayers.size() == 0 )
{
QMessageBox::warning( 0, QObject::tr( "Warning" ),
QObject::tr( "Cannot get GDAL raster band: %1" ).arg( msg ) );

GDALDereferenceDataset( mGdalBaseDataset );
mGdalBaseDataset = NULL;

GDALClose( mGdalDataset );
mGdalDataset = NULL;
return;
}
// if there are subdatasets, leave the dataset open for subsequent queries
else
{
QgsDebugMsg( QObject::tr( "Cannot get GDAL raster band: %1" ).arg( msg ) +
QString( " but dataset has %1 subdatasets" ).arg( mSubLayers.size() ) );
return;
}
}

// check if this file has pyramids
mHasPyramids = GDALGetOverviewCount( myGDALBand ) > 0;

// Get the layer's projection info and set up the
// QgsCoordinateTransform for this layer
// NOTE: we must do this before metadata is called

if ( !crsFromWkt( GDALGetProjectionRef( mGdalDataset ) ) &&
!crsFromWkt( GDALGetGCPProjection( mGdalDataset ) ) )
{
QgsDebugMsg( "No valid CRS identified" );
mCrs.validate();
}

//set up the coordinat transform - in the case of raster this is mainly used to convert
//the inverese projection of the map extents of the canvas when zooming in etc. so
//that they match the coordinate system of this layer
//QgsDebugMsg( "Layer registry has " + QString::number( QgsMapLayerRegistry::instance()->count() ) + "layers" );

//metadata();

// Use the affine transform to get geo coordinates for
// the corners of the raster
double myXMax = mGeoTransform[0] +
GDALGetRasterXSize( mGdalDataset ) * mGeoTransform[1] +
GDALGetRasterYSize( mGdalDataset ) * mGeoTransform[2];
double myYMin = mGeoTransform[3] +
GDALGetRasterXSize( mGdalDataset ) * mGeoTransform[4] +
GDALGetRasterYSize( mGdalDataset ) * mGeoTransform[5];

mExtent.setXMaximum( myXMax );
// The affine transform reduces to these values at the
// top-left corner of the raster
mExtent.setXMinimum( mGeoTransform[0] );
mExtent.setYMaximum( mGeoTransform[3] );
mExtent.setYMinimum( myYMin );

//
// Set up the x and y dimensions of this raster layer
//
mWidth = GDALGetRasterXSize( mGdalDataset );
mHeight = GDALGetRasterYSize( mGdalDataset );


GDALGetBlockSize( GDALGetRasterBand( mGdalDataset, 1 ), &mXBlockSize, &mYBlockSize );
//
// Determine the nodata value and data type
//
mValidNoDataValue = true;
for ( int i = 1; i <= GDALGetRasterCount( mGdalBaseDataset ); i++ )
{
computeMinMax( i );
GDALRasterBandH myGdalBand = GDALGetRasterBand( mGdalDataset, i );
GDALDataType myGdalDataType = GDALGetRasterDataType( myGdalBand );
int isValid = false;
double myNoDataValue = GDALGetRasterNoDataValue( GDALGetRasterBand( mGdalDataset, i ), &isValid );
if ( isValid )
{
QgsDebugMsg( QString( "GDALGetRasterNoDataValue = %1" ).arg( myNoDataValue ) ) ;
mGdalDataType.append( myGdalDataType );

}
else
{
// But we need a null value in case of reprojection and BTW also for
// aligned margines

switch ( srcDataType( i ) )
{
case QgsRasterDataProvider::Byte:
// Use longer data type to avoid conflict with real data
myNoDataValue = -32768.0;
mGdalDataType.append( GDT_Int16 );
break;
case QgsRasterDataProvider::Int16:
myNoDataValue = -2147483648.0;
mGdalDataType.append( GDT_Int32 );
break;
case QgsRasterDataProvider::UInt16:
myNoDataValue = -2147483648.0;
mGdalDataType.append( GDT_Int32 );
break;
case QgsRasterDataProvider::Int32:
myNoDataValue = -2147483648.0;
mGdalDataType.append( myGdalDataType );
break;
case QgsRasterDataProvider::UInt32:
myNoDataValue = 4294967295.0;
mGdalDataType.append( myGdalDataType );
break;
default:
myNoDataValue = std::numeric_limits<int>::max();
// Would NaN work well?
//myNoDataValue = std::numeric_limits<double>::quiet_NaN();
mGdalDataType.append( myGdalDataType );
}
}
mNoDataValue.append( myNoDataValue );
QgsDebugMsg( QString( "mNoDataValue[%1] = %2" ).arg( i - 1 ).arg( mNoDataValue[i-1] ) );
}

mValid = true;
QgsDebugMsg( "end" );
initBaseDataset();
}

bool QgsGdalProvider::crsFromWkt( const char *wkt )
Expand Down Expand Up @@ -1189,7 +1004,9 @@ int QgsGdalProvider::capabilities() const
| QgsRasterDataProvider::ExactResolution
| QgsRasterDataProvider::EstimatedMinimumMaximum
| QgsRasterDataProvider::BuildPyramids
| QgsRasterDataProvider::Histogram;
| QgsRasterDataProvider::Histogram
| QgsRasterDataProvider::Create
| QgsRasterDataProvider::Remove;
GDALDriverH myDriver = GDALGetDatasetDriver( mGdalDataset );
QString name = GDALGetDriverShortName( myDriver );
QgsDebugMsg( "driver short name = " + name );
Expand Down Expand Up @@ -2047,6 +1864,241 @@ QgsRasterBandStats QgsGdalProvider::bandStatistics( int theBandNo )

} // QgsGdalProvider::bandStatistics

void QgsGdalProvider::initBaseDataset()
{
for ( int i = 0; i < GDALGetRasterCount( mGdalBaseDataset ); i++ )
{
mMinMaxComputed.append( false );
mMinimum.append( 0 );
mMaximum.append( 0 );
}

// Check if we need a warped VRT for this file.
bool hasGeoTransform = GDALGetGeoTransform( mGdalBaseDataset, mGeoTransform ) == CE_None;
if (( hasGeoTransform
&& ( mGeoTransform[1] < 0.0
|| mGeoTransform[2] != 0.0
|| mGeoTransform[4] != 0.0
|| mGeoTransform[5] > 0.0 ) )
|| GDALGetGCPCount( mGdalBaseDataset ) > 0 )
{
QgsLogger::warning( "Creating Warped VRT." );

mGdalDataset =
GDALAutoCreateWarpedVRT( mGdalBaseDataset, NULL, NULL,
GRA_NearestNeighbour, 0.2, NULL );

if ( mGdalDataset == NULL )
{
QgsLogger::warning( "Warped VRT Creation failed." );
mGdalDataset = mGdalBaseDataset;
GDALReferenceDataset( mGdalDataset );
}
else
{
GDALGetGeoTransform( mGdalDataset, mGeoTransform );
}
}
else
{
mGdalDataset = mGdalBaseDataset;
GDALReferenceDataset( mGdalDataset );
}

if ( !hasGeoTransform )
{
// Initialise the affine transform matrix
mGeoTransform[0] = 0;
mGeoTransform[1] = 1;
mGeoTransform[2] = 0;
mGeoTransform[3] = 0;
mGeoTransform[4] = 0;
mGeoTransform[5] = -1;
}

// get sublayers
mSubLayers = QgsGdalProvider::subLayers( mGdalDataset );

// check if this file has bands or subdatasets
CPLErrorReset();
GDALRasterBandH myGDALBand = GDALGetRasterBand( mGdalDataset, 1 ); //just use the first band
if ( myGDALBand == NULL )
{
QString msg = QString::fromUtf8( CPLGetLastErrorMsg() );

// if there are no subdatasets, then close the dataset
if ( mSubLayers.size() == 0 )
{
QMessageBox::warning( 0, QObject::tr( "Warning" ),
QObject::tr( "Cannot get GDAL raster band: %1" ).arg( msg ) );

GDALDereferenceDataset( mGdalBaseDataset );
mGdalBaseDataset = NULL;

GDALClose( mGdalDataset );
mGdalDataset = NULL;
return;
}
// if there are subdatasets, leave the dataset open for subsequent queries
else
{
QgsDebugMsg( QObject::tr( "Cannot get GDAL raster band: %1" ).arg( msg ) +
QString( " but dataset has %1 subdatasets" ).arg( mSubLayers.size() ) );
return;
}
}

// check if this file has pyramids
mHasPyramids = GDALGetOverviewCount( myGDALBand ) > 0;

// Get the layer's projection info and set up the
// QgsCoordinateTransform for this layer
// NOTE: we must do this before metadata is called

if ( !crsFromWkt( GDALGetProjectionRef( mGdalDataset ) ) &&
!crsFromWkt( GDALGetGCPProjection( mGdalDataset ) ) )
{
QgsDebugMsg( "No valid CRS identified" );
mCrs.validate();
}

//set up the coordinat transform - in the case of raster this is mainly used to convert
//the inverese projection of the map extents of the canvas when zooming in etc. so
//that they match the coordinate system of this layer
//QgsDebugMsg( "Layer registry has " + QString::number( QgsMapLayerRegistry::instance()->count() ) + "layers" );

//metadata();

// Use the affine transform to get geo coordinates for
// the corners of the raster
double myXMax = mGeoTransform[0] +
GDALGetRasterXSize( mGdalDataset ) * mGeoTransform[1] +
GDALGetRasterYSize( mGdalDataset ) * mGeoTransform[2];
double myYMin = mGeoTransform[3] +
GDALGetRasterXSize( mGdalDataset ) * mGeoTransform[4] +
GDALGetRasterYSize( mGdalDataset ) * mGeoTransform[5];

mExtent.setXMaximum( myXMax );
// The affine transform reduces to these values at the
// top-left corner of the raster
mExtent.setXMinimum( mGeoTransform[0] );
mExtent.setYMaximum( mGeoTransform[3] );
mExtent.setYMinimum( myYMin );

//
// Set up the x and y dimensions of this raster layer
//
mWidth = GDALGetRasterXSize( mGdalDataset );
mHeight = GDALGetRasterYSize( mGdalDataset );


GDALGetBlockSize( GDALGetRasterBand( mGdalDataset, 1 ), &mXBlockSize, &mYBlockSize );
//
// Determine the nodata value and data type
//
mValidNoDataValue = true;
for ( int i = 1; i <= GDALGetRasterCount( mGdalBaseDataset ); i++ )
{
computeMinMax( i );
GDALRasterBandH myGdalBand = GDALGetRasterBand( mGdalDataset, i );
GDALDataType myGdalDataType = GDALGetRasterDataType( myGdalBand );
int isValid = false;
double myNoDataValue = GDALGetRasterNoDataValue( GDALGetRasterBand( mGdalDataset, i ), &isValid );
if ( isValid )
{
QgsDebugMsg( QString( "GDALGetRasterNoDataValue = %1" ).arg( myNoDataValue ) ) ;
mGdalDataType.append( myGdalDataType );

}
else
{
// But we need a null value in case of reprojection and BTW also for
// aligned margines

switch ( srcDataType( i ) )
{
case QgsRasterDataProvider::Byte:
// Use longer data type to avoid conflict with real data
myNoDataValue = -32768.0;
mGdalDataType.append( GDT_Int16 );
break;
case QgsRasterDataProvider::Int16:
myNoDataValue = -2147483648.0;
mGdalDataType.append( GDT_Int32 );
break;
case QgsRasterDataProvider::UInt16:
myNoDataValue = -2147483648.0;
mGdalDataType.append( GDT_Int32 );
break;
case QgsRasterDataProvider::Int32:
myNoDataValue = -2147483648.0;
mGdalDataType.append( myGdalDataType );
break;
case QgsRasterDataProvider::UInt32:
myNoDataValue = 4294967295.0;
mGdalDataType.append( myGdalDataType );
break;
default:
myNoDataValue = std::numeric_limits<int>::max();
// Would NaN work well?
//myNoDataValue = std::numeric_limits<double>::quiet_NaN();
mGdalDataType.append( myGdalDataType );
}
}
mNoDataValue.append( myNoDataValue );
QgsDebugMsg( QString( "mNoDataValue[%1] = %2" ).arg( i - 1 ).arg( mNoDataValue[i-1] ) );
}

mValid = true;
}

bool QgsGdalProvider::create( const QString& format, int nBands, QgsRasterDataProvider::DataType type, int width, int height,
double* geoTransform, const QgsCoordinateReferenceSystem& crs )
{
if ( isValid() ) //datasource already exists
{
return false;
}

//get driver
GDALDriverH driver = GDALGetDriverByName( format.toLocal8Bit().data() );
if ( !driver )
{
return false;
}

//create dataset
GDALDatasetH dataset = GDALCreate( driver, dataSourceUri().toLocal8Bit().data(), width, height, nBands, ( GDALDataType )type, 0 );
if ( dataset == NULL )
{
return false;
}

mGeoTransform[0] = geoTransform[0];
mGeoTransform[1] = geoTransform[1];
mGeoTransform[2] = geoTransform[2];
mGeoTransform[3] = geoTransform[3];
mGeoTransform[4] = geoTransform[4];
mGeoTransform[5] = geoTransform[5];

GDALSetGeoTransform( dataset, mGeoTransform );
GDALSetProjection( dataset, crs.toWkt().toLocal8Bit().data() );

mGdalBaseDataset = dataset;
initBaseDataset();
return mValid;
}

QStringList QgsGdalProvider::createFormats() const
{
return QStringList();
}

bool QgsGdalProvider::remove()
{
return false;
}

/**
Builds the list of file filter strings to later be used by
QgisApp::addRasterLayer()
Expand Down
14 changes: 14 additions & 0 deletions src/providers/gdal/qgsgdalprovider.h
Original file line number Diff line number Diff line change
Expand Up @@ -264,13 +264,27 @@ class QgsGdalProvider : public QgsRasterDataProvider

static QMap<QString, QString> supportedMimes();

/** Creates a new dataset with mDataSourceURI
@return true in case of success*/
bool create( const QString& format, int nBands, QgsRasterDataProvider::DataType type, int width, int height,
double* geoTransform, const QgsCoordinateReferenceSystem& crs );

/**Returns the formats supported by create()*/
QStringList createFormats() const;

/**Remove dataset*/
bool remove();

signals:
void statusChanged( QString );

private:
// initialize CRS from wkt
bool crsFromWkt( const char *wkt );

/**Do some initialisation on the dataset (e.g. handling of south-up datasets)*/
void initBaseDataset();

/**
* Flag indicating if the layer data source is a valid layer
*/
Expand Down