Skip to content

Commit

Permalink
Merge pull request #41277 from nirvn/gdal_openoptions
Browse files Browse the repository at this point in the history
[gdal] Add open options support to gain dataset flexibility / fixes
  • Loading branch information
nirvn committed Feb 2, 2021
2 parents 5475a05 + 7961c92 commit e299a49
Show file tree
Hide file tree
Showing 8 changed files with 369 additions and 63 deletions.
46 changes: 9 additions & 37 deletions src/core/providers/gdal/qgsgdalprovider.cpp
Expand Up @@ -1991,12 +1991,12 @@ QString QgsGdalProvider::buildPyramids( const QList<QgsRasterPyramid> &rasterPyr
GDALClose( mGdalDataset );
//mGdalBaseDataset = GDALOpen( QFile::encodeName( dataSourceUri() ).constData(), GA_Update );

mGdalBaseDataset = gdalOpen( dataSourceUri( true ).toUtf8().constData(), GA_Update );
mGdalBaseDataset = gdalOpen( dataSourceUri( true ), GDAL_OF_UPDATE );

// if the dataset couldn't be opened in read / write mode, tell the user
if ( !mGdalBaseDataset )
{
mGdalBaseDataset = gdalOpen( dataSourceUri( true ).toUtf8().constData(), GA_ReadOnly );
mGdalBaseDataset = gdalOpen( dataSourceUri( true ), GDAL_OF_READONLY );
//Since we are not a virtual warped dataset, mGdalDataSet and mGdalBaseDataset are supposed to be the same
mGdalDataset = mGdalBaseDataset;
return QStringLiteral( "ERROR_WRITE_FORMAT" );
Expand Down Expand Up @@ -2105,7 +2105,7 @@ QString QgsGdalProvider::buildPyramids( const QList<QgsRasterPyramid> &rasterPyr
//something bad happenend
//QString myString = QString (CPLGetLastError());
GDALClose( mGdalBaseDataset );
mGdalBaseDataset = gdalOpen( dataSourceUri( true ).toUtf8().constData(), mUpdate ? GA_Update : GA_ReadOnly );
mGdalBaseDataset = gdalOpen( dataSourceUri( true ), mUpdate ? GDAL_OF_UPDATE : GDAL_OF_READONLY );
//Since we are not a virtual warped dataset, mGdalDataSet and mGdalBaseDataset are supposed to be the same
mGdalDataset = mGdalBaseDataset;

Expand Down Expand Up @@ -2157,7 +2157,7 @@ QString QgsGdalProvider::buildPyramids( const QList<QgsRasterPyramid> &rasterPyr
QgsDebugMsgLevel( QStringLiteral( "Reopening dataset ..." ), 2 );
//close the gdal dataset and reopen it in read only mode
GDALClose( mGdalBaseDataset );
mGdalBaseDataset = gdalOpen( dataSourceUri( true ).toUtf8().constData(), mUpdate ? GA_Update : GA_ReadOnly );
mGdalBaseDataset = gdalOpen( dataSourceUri( true ), mUpdate ? GDAL_OF_UPDATE : GDAL_OF_READONLY );
//Since we are not a virtual warped dataset, mGdalDataSet and mGdalBaseDataset are supposed to be the same
mGdalDataset = mGdalBaseDataset;
}
Expand Down Expand Up @@ -2342,40 +2342,12 @@ QStringList QgsGdalProvider::subLayers() const

QVariantMap QgsGdalProviderMetadata::decodeUri( const QString &uri ) const
{
QString path = uri;
QString layerName;

QString vsiPrefix = qgsVsiPrefix( path );
if ( !path.isEmpty() )
path = path.mid( vsiPrefix.count() );

if ( path.indexOf( ':' ) != -1 )
{
QStringList parts = path.split( ':' );
if ( parts[0].toLower() == QLatin1String( "gpkg" ) )
{
parts.removeFirst();
// Handle windows paths - which has an extra colon - and unix paths
if ( ( parts[0].length() > 1 && parts.count() > 1 ) || parts.count() > 2 )
{
layerName = parts[parts.length() - 1];
parts.removeLast();
}
path = parts.join( ':' );
}
}

QVariantMap uriComponents;
uriComponents.insert( QStringLiteral( "path" ), path );
uriComponents.insert( QStringLiteral( "layerName" ), layerName );
return uriComponents;
return QgsGdalProviderBase::decodeGdalUri( uri );
}

QString QgsGdalProviderMetadata::encodeUri( const QVariantMap &parts ) const
{
QString path = parts.value( QStringLiteral( "path" ) ).toString();
QString layerName = parts.value( QStringLiteral( "layerName" ) ).toString();
return path + ( !layerName.isEmpty() ? QStringLiteral( "|%1" ).arg( layerName ) : QString() );
return QgsGdalProviderBase::encodeGdalUri( parts );
}

bool QgsGdalProviderMetadata::uriIsBlocklisted( const QString &uri ) const
Expand Down Expand Up @@ -2629,7 +2601,7 @@ bool QgsGdalProvider::isValidRasterFileName( QString const &fileNameQString, QSt

//open the file using gdal making sure we have handled locale properly
//myDataset = GDALOpen( QFile::encodeName( fileNameQString ).constData(), GA_ReadOnly );
myDataset.reset( QgsGdalProviderBase::gdalOpen( fileName.toUtf8().constData(), GA_ReadOnly ) );
myDataset.reset( QgsGdalProviderBase::gdalOpen( fileName, GDAL_OF_READONLY ) );
if ( !myDataset )
{
if ( CPLGetLastErrorNo() != CPLE_OpenFailed )
Expand Down Expand Up @@ -2929,7 +2901,7 @@ bool QgsGdalProvider::initIfNeeded()
gdalUri = dataSourceUri( true );

CPLErrorReset();
mGdalBaseDataset = gdalOpen( gdalUri.toUtf8().constData(), mUpdate ? GA_Update : GA_ReadOnly );
mGdalBaseDataset = gdalOpen( gdalUri, mUpdate ? GDAL_OF_UPDATE : GDAL_OF_READONLY );

if ( !mGdalBaseDataset )
{
Expand Down Expand Up @@ -3524,7 +3496,7 @@ bool QgsGdalProvider::setEditable( bool enabled )
mUpdate = enabled;

// reopen the dataset
mGdalBaseDataset = gdalOpen( dataSourceUri( true ).toUtf8().constData(), mUpdate ? GA_Update : GA_ReadOnly );
mGdalBaseDataset = gdalOpen( dataSourceUri( true ), mUpdate ? GDAL_OF_UPDATE : GDAL_OF_READONLY );
if ( !mGdalBaseDataset )
{
QString msg = QStringLiteral( "Cannot reopen GDAL dataset %1:\n%2" ).arg( dataSourceUri(), QString::fromUtf8( CPLGetLastErrorMsg() ) );
Expand Down
95 changes: 93 additions & 2 deletions src/core/providers/gdal/qgsgdalproviderbase.cpp
Expand Up @@ -20,6 +20,7 @@

#define CPL_SUPRESS_CPLUSPLUS //#spellok
#include <cpl_conv.h>
#include <cpl_string.h>

#include "qgsapplication.h"
#include "qgslogger.h"
Expand Down Expand Up @@ -254,14 +255,29 @@ QgsRectangle QgsGdalProviderBase::extent( GDALDatasetH gdalDataset )const
return extent;
}

GDALDatasetH QgsGdalProviderBase::gdalOpen( const char *pszFilename, GDALAccess eAccess )
GDALDatasetH QgsGdalProviderBase::gdalOpen( const QString &uri, unsigned int nOpenFlags )
{
QVariantMap parts = decodeGdalUri( uri );
QString filePath = parts.value( QStringLiteral( "path" ) ).toString();
const QStringList openOptions = parts.value( QStringLiteral( "openOptions" ) ).toStringList();
parts.remove( QStringLiteral( "openOptions" ) );

char **papszOpenOptions = nullptr;
for ( const QString &option : openOptions )
{
papszOpenOptions = CSLAddString( papszOpenOptions,
option.toUtf8().constData() );
}

bool modify_OGR_GPKG_FOREIGN_KEY_CHECK = !CPLGetConfigOption( "OGR_GPKG_FOREIGN_KEY_CHECK", nullptr );
if ( modify_OGR_GPKG_FOREIGN_KEY_CHECK )
{
CPLSetThreadLocalConfigOption( "OGR_GPKG_FOREIGN_KEY_CHECK", "NO" );
}
GDALDatasetH hDS = GDALOpen( pszFilename, eAccess );

GDALDatasetH hDS = GDALOpenEx( encodeGdalUri( parts ).toUtf8().constData(), nOpenFlags, nullptr, papszOpenOptions, nullptr );
CSLDestroy( papszOpenOptions );

if ( modify_OGR_GPKG_FOREIGN_KEY_CHECK )
{
CPLSetThreadLocalConfigOption( "OGR_GPKG_FOREIGN_KEY_CHECK", nullptr );
Expand Down Expand Up @@ -304,4 +320,79 @@ int QgsGdalProviderBase::gdalGetOverviewCount( GDALRasterBandH hBand )
return count;
}

QVariantMap QgsGdalProviderBase::decodeGdalUri( const QString &uri )
{
QString path = uri;
QString layerName;
QStringList openOptions;

QString vsiPrefix = qgsVsiPrefix( path );
if ( !path.isEmpty() )
path = path.mid( vsiPrefix.count() );

if ( path.indexOf( ':' ) != -1 )
{
QStringList parts = path.split( ':' );
if ( parts[0].toLower() == QLatin1String( "gpkg" ) )
{
parts.removeFirst();
// Handle windows paths - which has an extra colon - and unix paths
if ( ( parts[0].length() > 1 && parts.count() > 1 ) || parts.count() > 2 )
{
layerName = parts[parts.length() - 1];
parts.removeLast();
}
path = parts.join( ':' );
}
}

if ( path.contains( '|' ) )
{
const QRegularExpression openOptionRegex( QStringLiteral( "\\|option:([^|]*)" ) );

while ( true )
{
QRegularExpressionMatch match = openOptionRegex.match( path );
if ( match.hasMatch() )
{
openOptions << match.captured( 1 );
path = path.remove( match.capturedStart( 0 ), match.capturedLength( 0 ) );
}
else
{
break;
}
}
}

QVariantMap uriComponents;
uriComponents.insert( QStringLiteral( "path" ), path );
uriComponents.insert( QStringLiteral( "layerName" ), layerName );
if ( !openOptions.isEmpty() )
uriComponents.insert( QStringLiteral( "openOptions" ), openOptions );
return uriComponents;
}

QString QgsGdalProviderBase::encodeGdalUri( const QVariantMap &parts )
{
QString path = parts.value( QStringLiteral( "path" ) ).toString();
QString layerName = parts.value( QStringLiteral( "layerName" ) ).toString();
QString uri;

if ( !layerName.isEmpty() && path.endsWith( QStringLiteral( "gpkg" ) ) )
uri = QStringLiteral( "GPKG:%1:%2" ).arg( path, layerName );
else
uri = path + ( !layerName.isEmpty() ? QStringLiteral( "|%1" ).arg( layerName ) : QString() );

const QStringList openOptions = parts.value( QStringLiteral( "openOptions" ) ).toStringList();

for ( const QString &openOption : openOptions )
{
uri += QStringLiteral( "|option:" );
uri += openOption;
}

return uri;
}

///@endcond
7 changes: 6 additions & 1 deletion src/core/providers/gdal/qgsgdalproviderbase.h
Expand Up @@ -42,13 +42,18 @@ class QgsGdalProviderBase
static void registerGdalDrivers();

//! Wrapper function for GDALOpen to get around possible bugs in GDAL
static GDALDatasetH gdalOpen( const char *pszFilename, GDALAccess eAccess );
static GDALDatasetH gdalOpen( const QString &uri, unsigned int nOpenFlags );

//! Wrapper function for GDALRasterIO to get around possible bugs in GDAL
static CPLErr gdalRasterIO( GDALRasterBandH hBand, GDALRWFlag eRWFlag, int nXOff, int nYOff, int nXSize, int nYSize, void *pData, int nBufXSize, int nBufYSize, GDALDataType eBufType, int nPixelSpace, int nLineSpace, QgsRasterBlockFeedback *feedback = nullptr );

//! Wrapper function for GDALRasterIO to get around possible bugs in GDAL
static int gdalGetOverviewCount( GDALRasterBandH hBand );

static QVariantMap decodeGdalUri( const QString &uri );

static QString encodeGdalUri( const QVariantMap &parts );

protected:

Qgis::DataType dataTypeFromGdal( GDALDataType gdalDataType ) const;
Expand Down

0 comments on commit e299a49

Please sign in to comment.