Merge pull request #5057 from boundlessgeo/gpkg-raster-import2

Geopackage: handle raster drop in browser
This commit is contained in:
Alessandro Pasotti 2017-08-29 08:48:51 +02:00 committed by GitHub
commit daa60d1a0e
15 changed files with 564 additions and 180 deletions

View File

@ -102,7 +102,7 @@ Required build dependencies:
- Sqlite3 >= 3.0.0
- SpatiaLite
- libspatialindex
- GDAL/OGR >= 2.0
- GDAL/OGR >= 2.1
- Qwt >= 5.0 & (< 6.1 with internal QwtPolar)
- expat >= 1.95
- QScintilla2

View File

@ -62,8 +62,11 @@ ELSE(WIN32)
STRING(REGEX REPLACE "([0-9]+)\\.([0-9]+)\\.([0-9]+)" "\\1" GDAL_VERSION_MAJOR "${GDAL_VERSION}")
STRING(REGEX REPLACE "([0-9]+)\\.([0-9]+)\\.([0-9]+)" "\\2" GDAL_VERSION_MINOR "${GDAL_VERSION}")
IF (GDAL_VERSION_MAJOR LESS 2)
MESSAGE (FATAL_ERROR "GDAL version is too old (${GDAL_VERSION}). Use 2.0 or higher.")
MESSAGE (FATAL_ERROR "GDAL version is too old (${GDAL_VERSION}). Use 2.1 or higher.")
ENDIF (GDAL_VERSION_MAJOR LESS 2)
IF ( (GDAL_VERSION_MAJOR EQUAL 2) AND (GDAL_VERSION_MINOR LESS 1) )
MESSAGE (FATAL_ERROR "GDAL version is too old (${GDAL_VERSION}). Use 2.1 or higher.")
ENDIF( (GDAL_VERSION_MAJOR EQUAL 2) AND (GDAL_VERSION_MINOR LESS 1) )
ENDIF (GDAL_LIBRARY)
SET (CMAKE_FIND_FRAMEWORK ${CMAKE_FIND_FRAMEWORK_save} CACHE STRING "" FORCE)
@ -101,10 +104,13 @@ ELSE(WIN32)
# check for gdal version
# version 1.2.5 is known NOT to be supported (missing CPL_STDCALL macro)
# According to INSTALL, 2.0+ is required
# According to INSTALL, 2.1+ is required
IF (GDAL_VERSION_MAJOR LESS 2)
MESSAGE (FATAL_ERROR "GDAL version is too old (${GDAL_VERSION}). Use 2.0 or higher.")
MESSAGE (FATAL_ERROR "GDAL version is too old (${GDAL_VERSION}). Use 2.1 or higher.")
ENDIF (GDAL_VERSION_MAJOR LESS 2)
IF ( (GDAL_VERSION_MAJOR EQUAL 2) AND (GDAL_VERSION_MINOR LESS 1) )
MESSAGE (FATAL_ERROR "GDAL version is too old (${GDAL_VERSION}). Use 2.1 or higher.")
ENDIF( (GDAL_VERSION_MAJOR EQUAL 2) AND (GDAL_VERSION_MINOR LESS 1) )
# set INCLUDE_DIR to prefix+include
EXEC_PROGRAM(${GDAL_CONFIG}

View File

@ -48,6 +48,14 @@ Returns encoded representation of the object
:rtype: QgsVectorLayer
%End
QgsRasterLayer *rasterLayer( bool &owner, QString &error ) const;
%Docstring
Get raster layer from uri if possible, otherwise returns 0 and error is set
\param owner set to true if caller becomes owner
\param error set to error message if cannot get raster
:rtype: QgsRasterLayer
%End
QString layerType;
%Docstring
Type of URI. Recognized types: "vector" / "raster" / "plugin" / "custom"

View File

@ -100,6 +100,18 @@ QgsVectorLayer *QgsMimeDataUtils::Uri::vectorLayer( bool &owner, QString &error
return new QgsVectorLayer( uri, name, providerKey );
}
QgsRasterLayer *QgsMimeDataUtils::Uri::rasterLayer( bool &owner, QString &error ) const
{
owner = false;
if ( layerType != QLatin1String( "raster" ) )
{
error = QObject::tr( "%1: Not a raster layer." ).arg( name );
return nullptr;
}
owner = true;
return new QgsRasterLayer( uri, name, providerKey );
}
// -----
bool QgsMimeDataUtils::isUriList( const QMimeData *data )

View File

@ -23,6 +23,7 @@
class QgsLayerItem;
class QgsLayerTreeNode;
class QgsVectorLayer;
class QgsRasterLayer;
/** \ingroup core
* \class QgsMimeDataUtils
@ -51,6 +52,12 @@ class CORE_EXPORT QgsMimeDataUtils
*/
QgsVectorLayer *vectorLayer( bool &owner, QString &error ) const;
/** Get raster layer from uri if possible, otherwise returns 0 and error is set
* \param owner set to true if caller becomes owner
* \param error set to error message if cannot get raster
*/
QgsRasterLayer *rasterLayer( bool &owner, QString &error ) const;
//! Type of URI. Recognized types: "vector" / "raster" / "plugin" / "custom"
QString layerType;
//! For "vector" / "raster" type: provider id.

View File

@ -808,15 +808,9 @@ QMap<QString, QgsVectorFileWriter::MetaData> QgsVectorFileWriter::initMetaData()
"Can be one of NULL for a simple .dbf file with no .shp file, POINT, "
"ARC, POLYGON or MULTIPOINT for 2D, or POINTZ, ARCZ, POLYGONZ or "
"MULTIPOINTZ for 3D;" ) +
#if defined(GDAL_COMPUTE_VERSION) && GDAL_VERSION_NUM < GDAL_COMPUTE_VERSION(2,1,0)
QObject::tr( " Shapefiles with measure values are not supported,"
" nor are MULTIPATCH files." ) +
#endif
#if defined(GDAL_COMPUTE_VERSION) && GDAL_VERSION_NUM >= GDAL_COMPUTE_VERSION(2,1,0)
QObject::tr( " POINTM, ARCM, POLYGONM or MULTIPOINTM for measured geometries"
" and POINTZM, ARCZM, POLYGONZM or MULTIPOINTZM for 3D measured"
" geometries." ) +
#endif
#if defined(GDAL_COMPUTE_VERSION) && GDAL_VERSION_NUM >= GDAL_COMPUTE_VERSION(2,2,0)
QObject::tr( " MULTIPATCH files are supported since GDAL 2.2." ) +
#endif
@ -831,7 +825,6 @@ QMap<QString, QgsVectorFileWriter::MetaData> QgsVectorFileWriter::initMetaData()
<< QStringLiteral( "ARCZ" )
<< QStringLiteral( "POLYGONZ" )
<< QStringLiteral( "MULTIPOINTZ" )
#if defined(GDAL_COMPUTE_VERSION) && GDAL_VERSION_NUM >= GDAL_COMPUTE_VERSION(2,1,0)
<< QStringLiteral( "POINTM" )
<< QStringLiteral( "ARCM" )
<< QStringLiteral( "POLYGONM" )
@ -840,7 +833,6 @@ QMap<QString, QgsVectorFileWriter::MetaData> QgsVectorFileWriter::initMetaData()
<< QStringLiteral( "ARCZM" )
<< QStringLiteral( "POLYGONZM" )
<< QStringLiteral( "MULTIPOINTZM" )
#endif
#if defined(GDAL_COMPUTE_VERSION) && GDAL_VERSION_NUM >= GDAL_COMPUTE_VERSION(2,2,0)
<< QStringLiteral( "MULTIPATCH" )
#endif
@ -1330,7 +1322,6 @@ QMap<QString, QgsVectorFileWriter::MetaData> QgsVectorFileWriter::initMetaData()
true // Allow None
) );
#if GDAL_VERSION_NUM >= GDAL_COMPUTE_VERSION(2,0,2)
datasetOptions.insert( QStringLiteral( "BLOCK_SIZE" ), new IntOption(
QObject::tr( "(multiples of 512): Block size for .map files. Defaults "
"to 512. MapInfo 15.2 and above creates .tab files with a "
@ -1338,15 +1329,12 @@ QMap<QString, QgsVectorFileWriter::MetaData> QgsVectorFileWriter::initMetaData()
"able to handle block sizes from 512 to 32256." ),
512
) );
#endif
#if GDAL_VERSION_NUM >= GDAL_COMPUTE_VERSION(2,0,0)
layerOptions.insert( QStringLiteral( "BOUNDS" ), new StringOption(
QObject::tr( "xmin,ymin,xmax,ymax: Define custom layer bounds to increase the "
"accuracy of the coordinates. Note: the geometry of written "
"features must be within the defined box." ),
QLatin1String( "" ) // Default value
) );
#endif
driverMetadata.insert( QStringLiteral( "MapInfo File" ),
MetaData(
@ -1894,9 +1882,6 @@ QStringList QgsVectorFileWriter::defaultLayerOptions( const QString &driverName
OGRwkbGeometryType QgsVectorFileWriter::ogrTypeFromWkbType( QgsWkbTypes::Type type )
{
#if GDAL_VERSION_NUM < GDAL_COMPUTE_VERSION(2,1,0)
type = QgsWkbTypes::dropM( type );
#endif
OGRwkbGeometryType ogrType = static_cast<OGRwkbGeometryType>( type );

View File

@ -8,6 +8,8 @@ SET (OGR_SRCS
qgsogrsourceselect.cpp
qgsgeopackagedataitems.cpp
qgsgeopackageconnection.cpp
qgsgeopackagerasterwriter.cpp
qgsgeopackagerasterwritertask.cpp
)
SET(OGR_MOC_HDRS
@ -17,6 +19,7 @@ SET(OGR_MOC_HDRS
qgsogrsourceselect.h
qgsgeopackagedataitems.h
qgsgeopackageconnection.h
qgsgeopackagerasterwritertask.h
)
########################################################

View File

@ -0,0 +1,48 @@
/***************************************************************************
qgscplerrorhandler.h - QgsCplErrorHandler
---------------------
begin : Oct 29, 2003
copyright : (C) 2003 by Gary E.Sherman
email : sherman at mrcc.com
***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
#ifndef QGSCPLERRORHANDLER_H
#define QGSCPLERRORHANDLER_H
#include "gdal.h"
#include "qgsmessagelog.h"
class QgsCPLErrorHandler
{
static void CPL_STDCALL showError( CPLErr errClass, int errNo, const char *msg )
{
if ( errNo != OGRERR_NONE )
QgsMessageLog::logMessage( QObject::tr( "OGR[%1] error %2: %3" ).arg( errClass ).arg( errNo ).arg( msg ), QObject::tr( "OGR" ) );
}
public:
QgsCPLErrorHandler()
{
CPLPushErrorHandler( showError );
}
~QgsCPLErrorHandler()
{
CPLPopErrorHandler();
}
private:
QgsCPLErrorHandler( const QgsCPLErrorHandler &other );
QgsCPLErrorHandler &operator=( const QgsCPLErrorHandler &other );
};
#endif // QGSCPLERRORHANDLER_H

View File

@ -26,6 +26,7 @@
#include "qgsnewgeopackagelayerdialog.h"
#include "qgsmessageoutput.h"
#include "qgsvectorlayerexporter.h"
#include "qgsgeopackagerasterwritertask.h"
#include "gdal.h"
#include <QAction>
@ -327,57 +328,68 @@ bool QgsGeoPackageConnectionItem::handleDrop( const QMimeData *data, Qt::DropAct
bool hasError = false;
QgsMimeDataUtils::UriList lst = QgsMimeDataUtils::decodeUriList( data );
Q_FOREACH ( const QgsMimeDataUtils::Uri &u, lst )
Q_FOREACH ( const QgsMimeDataUtils::Uri &dropUri, lst )
{
if ( u.layerType == QStringLiteral( "vector" ) )
// Check that we are not copying over self
if ( dropUri.uri.startsWith( mPath ) )
{
// Check that we are not copying over self
if ( u.uri.startsWith( mPath ) )
{
importResults.append( tr( "You cannot import layer %1 over itself!" ).arg( u.name ) );
hasError = true;
importResults.append( tr( "You cannot import layer %1 over itself!" ).arg( dropUri.name ) );
hasError = true;
}
else
{
QgsMapLayer *srcLayer;
bool owner;
bool isVector = false;
QString error;
// Common checks for raster and vector
// aspatial is treated like vector
if ( dropUri.layerType == QStringLiteral( "vector" ) )
{
// open the source layer
srcLayer = dropUri.vectorLayer( owner, error );
isVector = true;
}
else
{
// open the source layer
bool owner;
QString error;
QgsVectorLayer *srcLayer = u.vectorLayer( owner, error );
if ( !srcLayer )
{
importResults.append( tr( "%1: %2" ).arg( u.name ).arg( error ) );
hasError = true;
continue;
}
srcLayer = dropUri.rasterLayer( owner, error );
}
if ( !srcLayer )
{
importResults.append( tr( "%1: %2" ).arg( dropUri.name ).arg( error ) );
hasError = true;
continue;
}
if ( srcLayer->isValid() )
{
uri = mPath;
QgsDebugMsgLevel( "URI " + uri, 3 );
if ( srcLayer->isValid() )
{
uri = mPath;
QgsDebugMsgLevel( "URI " + uri, 3 );
// check if the destination layer already exists
bool exists = false;
// Q_FOREACH won't detach ...
for ( const auto child : children() )
// check if the destination layer already exists
bool exists = false;
// Q_FOREACH won't detach ...
for ( const auto child : children() )
{
if ( child->name() == dropUri.name )
{
if ( child->name() == u.name )
{
exists = true;
}
exists = true;
}
if ( ! exists || QMessageBox::question( nullptr, tr( "Overwrite Layer" ),
tr( "Destination layer <b>%1</b> already exists. Do you want to overwrite it?" ).arg( u.name ), QMessageBox::Yes | QMessageBox::No ) == QMessageBox::Yes )
}
if ( ! exists || QMessageBox::question( nullptr, tr( "Overwrite Layer" ),
tr( "Destination layer <b>%1</b> already exists. Do you want to overwrite it?" ).arg( dropUri.name ), QMessageBox::Yes | QMessageBox::No ) == QMessageBox::Yes )
{
if ( isVector ) // Import vectors and aspatial
{
QgsVectorLayer *vectorSrcLayer = dynamic_cast < QgsVectorLayer * >( srcLayer );
QVariantMap options;
options.insert( QStringLiteral( "driverName" ), QStringLiteral( "GPKG" ) );
options.insert( QStringLiteral( "update" ), true );
options.insert( QStringLiteral( "overwrite" ), true );
options.insert( QStringLiteral( "layerName" ), u.name );
std::unique_ptr< QgsVectorLayerExporterTask > exportTask( new QgsVectorLayerExporterTask( srcLayer, uri, QStringLiteral( "ogr" ), srcLayer->crs(), options, owner ) );
options.insert( QStringLiteral( "layerName" ), dropUri.name );
std::unique_ptr< QgsVectorLayerExporterTask > exportTask( new QgsVectorLayerExporterTask( vectorSrcLayer, uri, QStringLiteral( "ogr" ), vectorSrcLayer->crs(), options, owner ) );
// when export is successful:
connect( exportTask.get(), &QgsVectorLayerExporterTask::exportComplete, this, [ = ]()
{
@ -393,31 +405,52 @@ bool QgsGeoPackageConnectionItem::handleDrop( const QMimeData *data, Qt::DropAct
{
QgsMessageOutput *output = QgsMessageOutput::createMessageOutput();
output->setTitle( tr( "Import to GeoPackage database" ) );
output->setMessage( tr( "Failed to import some layers!\n\n" ) + errorMessage, QgsMessageOutput::MessageText );
output->setMessage( tr( "Failed to import some vector layers!\n\n" ) + errorMessage, QgsMessageOutput::MessageText );
output->showMessage();
}
} );
QgsApplication::taskManager()->addTask( exportTask.release() );
}
}
else
{
importResults.append( tr( "%1: Not a valid layer!" ).arg( u.name ) );
hasError = true;
}
}
}
else
{
// TODO: implement raster import
QgsMessageOutput *output = QgsMessageOutput::createMessageOutput();
output->setTitle( tr( "Import to GeoPackage database failed" ) );
output->setMessage( tr( "Failed to import some layers!\n\n" ) + QStringLiteral( "Raster import is not yet implemented!\n" ), QgsMessageOutput::MessageText );
output->showMessage();
}
else // Import raster
{
}
std::unique_ptr< QgsGeoPackageRasterWriterTask > exportTask( new QgsGeoPackageRasterWriterTask( dropUri, mPath ) );
// when export is successful:
connect( exportTask.get(), &QgsGeoPackageRasterWriterTask::writeComplete, this, [ = ]()
{
// this is gross - TODO - find a way to get access to messageBar from data items
QMessageBox::information( nullptr, tr( "Import to GeoPackage database" ), tr( "Import was successful." ) );
refreshConnections();
} );
// when an error occurs:
connect( exportTask.get(), &QgsGeoPackageRasterWriterTask::errorOccurred, this, [ = ]( QgsGeoPackageRasterWriter::WriterError error, const QString & errorMessage )
{
if ( error != QgsGeoPackageRasterWriter::WriterError::ErrUserCanceled )
{
QgsMessageOutput *output = QgsMessageOutput::createMessageOutput();
output->setTitle( tr( "Import to GeoPackage database" ) );
output->setMessage( tr( "Failed to import some raster layers!\n\n" ) + errorMessage, QgsMessageOutput::MessageText );
output->showMessage();
}
// Always try to delete the imported raster, in case the gpkg has been left
// in an inconsistent status. Ignore delete errors.
QString deleteErr;
deleteGeoPackageRasterLayer( QStringLiteral( "GPKG:%1:%2" ).arg( mPath, dropUri.name ), deleteErr );
} );
QgsApplication::taskManager()->addTask( exportTask.release() );
}
} // do not overwrite
}
else
{
importResults.append( tr( "%1: Not a valid layer!" ).arg( dropUri.name ) );
hasError = true;
}
} // check for self copy
} // for each
if ( hasError )
{
@ -426,7 +459,6 @@ bool QgsGeoPackageConnectionItem::handleDrop( const QMimeData *data, Qt::DropAct
output->setMessage( tr( "Failed to import some layers!\n\n" ) + importResults.join( QStringLiteral( "\n" ) ), QgsMessageOutput::MessageText );
output->showMessage();
}
return true;
}
@ -457,6 +489,124 @@ QgsLayerItem::LayerType QgsGeoPackageConnectionItem::layerTypeFromDb( const QStr
return QgsLayerItem::LayerType::TableLayer;
}
bool QgsGeoPackageConnectionItem::deleteGeoPackageRasterLayer( const QString uri, QString &errCause )
{
bool result = false;
// Better safe than sorry
if ( ! uri.isEmpty( ) )
{
QStringList pieces( uri.split( ':' ) );
if ( pieces.size() != 3 )
{
errCause = QStringLiteral( "Layer URI is malformed: layer <b>%1</b> cannot be deleted!" ).arg( uri );
}
else
{
QString baseUri = pieces.at( 1 );
QString layerName = pieces.at( 2 );
sqlite3 *handle;
int status = sqlite3_open_v2( baseUri.toUtf8().constData(), &handle, SQLITE_OPEN_READWRITE, NULL );
if ( status != SQLITE_OK )
{
errCause = sqlite3_errmsg( handle );
}
else
{
// Remove table
char *errmsg = nullptr;
char *sql = sqlite3_mprintf(
"DROP table IF EXISTS \"%w\";"
"DELETE FROM gpkg_contents WHERE table_name = '%q';"
"DELETE FROM gpkg_tile_matrix WHERE table_name = '%q';"
"DELETE FROM gpkg_tile_matrix_set WHERE table_name = '%q';",
layerName.toUtf8().constData(),
layerName.toUtf8().constData(),
layerName.toUtf8().constData(),
layerName.toUtf8().constData() );
status = sqlite3_exec(
handle, /* An open database */
sql, /* SQL to be evaluated */
NULL, /* Callback function */
NULL, /* 1st argument to callback */
&errmsg /* Error msg written here */
);
sqlite3_free( sql );
// Remove from optional tables, may silently fail
QStringList optionalTables;
optionalTables << QStringLiteral( "gpkg_extensions" )
<< QStringLiteral( "gpkg_metadata_reference" );
Q_FOREACH ( const QString &tableName, optionalTables )
{
char *sql = sqlite3_mprintf( "DELETE FROM %w WHERE table_name = '%q'",
tableName.toUtf8().constData(),
layerName.toUtf8().constData() );
sqlite3_exec(
handle, /* An open database */
sql, /* SQL to be evaluated */
NULL, /* Callback function */
NULL, /* 1st argument to callback */
NULL /* Error msg written here */
);
sqlite3_free( sql );
}
// Other tables, ignore errors
{
char *sql = sqlite3_mprintf( "DELETE FROM gpkg_2d_gridded_coverage_ancillary WHERE tile_matrix_set_name = '%q'",
layerName.toUtf8().constData() );
sqlite3_exec(
handle, /* An open database */
sql, /* SQL to be evaluated */
NULL, /* Callback function */
NULL, /* 1st argument to callback */
NULL /* Error msg written here */
);
sqlite3_free( sql );
}
{
char *sql = sqlite3_mprintf( "DELETE FROM gpkg_2d_gridded_tile_ancillary WHERE tpudt_name = '%q'",
layerName.toUtf8().constData() );
sqlite3_exec(
handle, /* An open database */
sql, /* SQL to be evaluated */
NULL, /* Callback function */
NULL, /* 1st argument to callback */
NULL /* Error msg written here */
);
sqlite3_free( sql );
}
// Vacuum
{
sqlite3_exec(
handle, /* An open database */
"VACUUM", /* SQL to be evaluated */
NULL, /* Callback function */
NULL, /* 1st argument to callback */
NULL /* Error msg written here */
);
}
if ( status == SQLITE_OK )
{
result = true;
}
else
{
errCause = tr( "There was an error deleting the layer %1: %2" ).arg( layerName, QString::fromUtf8( errmsg ) );
}
sqlite3_free( errmsg );
}
sqlite3_close( handle );
}
}
else
{
// This should never happen!
errCause = tr( "Layer URI is empty: layer cannot be deleted!" );
}
return result;
}
void QgsGeoPackageConnectionItem::deleteConnection()
{
QgsGeoPackageConnection::deleteConnection( name() );
@ -525,7 +675,7 @@ void QgsGeoPackageAbstractLayerItem::deleteLayer()
{
QMessageBox::information( nullptr, tr( "Delete Layer" ), tr( "Layer <b>%1</b> deleted successfully." ).arg( mName ) );
if ( mParent )
mParent->refresh();
mParent->refreshConnections();
}
}
else
@ -564,83 +714,7 @@ QgsGeoPackageRasterLayerItem::QgsGeoPackageRasterLayerItem( QgsDataItem *parent,
bool QgsGeoPackageRasterLayerItem::executeDeleteLayer( QString &errCause )
{
bool result = false;
// Better safe than sorry
if ( ! mUri.isEmpty( ) )
{
QStringList pieces( mUri.split( ':' ) );
if ( pieces.size() != 3 )
{
errCause = QStringLiteral( "Layer URI is malformed: layer <b>%1</b> cannot be deleted!" ).arg( mName );
}
else
{
QString baseUri = pieces.at( 1 );
QString layerName = pieces.at( 2 );
sqlite3 *handle;
int status = sqlite3_open_v2( baseUri.toUtf8().constData(), &handle, SQLITE_OPEN_READWRITE, NULL );
if ( status != SQLITE_OK )
{
errCause = sqlite3_errmsg( handle );
}
else
{
// Remove table
char *errmsg = nullptr;
char *sql = sqlite3_mprintf(
"DROP table %w;"
"DELETE FROM gpkg_contents WHERE table_name = '%q';"
"DELETE FROM gpkg_tile_matrix WHERE table_name = '%q';"
"DELETE FROM gpkg_tile_matrix_set WHERE table_name = '%q';",
layerName.toUtf8().constData(),
layerName.toUtf8().constData(),
layerName.toUtf8().constData(),
layerName.toUtf8().constData() );
status = sqlite3_exec(
handle, /* An open database */
sql, /* SQL to be evaluated */
NULL, /* Callback function */
NULL, /* 1st argument to callback */
&errmsg /* Error msg written here */
);
sqlite3_free( sql );
// Remove from optional tables, may silently fail
QStringList optionalTables;
optionalTables << QStringLiteral( "gpkg_extensions" )
<< QStringLiteral( "gpkg_metadata_reference" );
for ( const auto tableName : optionalTables )
{
char *sql = sqlite3_mprintf( "DELETE FROM table %w WHERE table_name = '%q",
tableName.toUtf8().constData(),
layerName.toUtf8().constData() );
sqlite3_exec(
handle, /* An open database */
sql, /* SQL to be evaluated */
NULL, /* Callback function */
NULL, /* 1st argument to callback */
NULL /* Error msg written here */
);
sqlite3_free( sql );
}
if ( status == SQLITE_OK )
{
result = true;
}
else
{
errCause = tr( "There was an error deleting the layer: %1" ).arg( QString::fromUtf8( errmsg ) );
}
sqlite3_free( errmsg );
}
sqlite3_close( handle );
}
}
else
{
// This should never happen!
errCause = QStringLiteral( "Layer URI is empty: layer <b>%1</b> cannot be deleted!" ).arg( mName );
}
return result;
return QgsGeoPackageConnectionItem::deleteGeoPackageRasterLayer( mUri, errCause );
}

View File

@ -82,6 +82,9 @@ class QgsGeoPackageConnectionItem : public QgsDataCollectionItem
//! Return the layer type from \a geometryType
static QgsLayerItem::LayerType layerTypeFromDb( const QString &geometryType );
//! Delete a geopackage layer
static bool deleteGeoPackageRasterLayer( const QString uri, QString &errCause );
public slots:
#ifdef HAVE_GUI
void editConnection();

View File

@ -0,0 +1,74 @@
/***************************************************************************
qgsgeopackagerasterwriter.cpp - QgsGeoPackageRasterWriter
---------------------
begin : 23.8.2017
copyright : (C) 2017 by Alessandro Pasotti
email : apasotti at boundlessgeo dot com
***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
///@cond PRIVATE
#include "gdal.h"
#include "gdal_utils.h"
#include "qgsgeopackagerasterwriter.h"
#include "qgscplerrorhandler.h"
#include <QMessageBox>
QgsGeoPackageRasterWriter::QgsGeoPackageRasterWriter( const QgsMimeDataUtils::Uri sourceUri, const QString outputUrl ):
mSourceUri( sourceUri ),
mOutputUrl( outputUrl )
{
}
QgsGeoPackageRasterWriter::WriterError QgsGeoPackageRasterWriter::writeRaster( QgsFeedback *feedback, QString *errorMessage )
{
const char *args[] = { "-of", "gpkg", "-co", QStringLiteral( "RASTER_TABLE=%1" ).arg( mSourceUri.name ).toUtf8().constData(), "-co", "APPEND_SUBDATASET=YES", nullptr };
// This sends OGR/GDAL errors to the message log
QgsCPLErrorHandler handler;
GDALTranslateOptions *psOptions = GDALTranslateOptionsNew( ( char ** )args, nullptr );
GDALTranslateOptionsSetProgress( psOptions, [ ]( double dfComplete, const char *pszMessage, void *pProgressData ) -> int
{
Q_UNUSED( pszMessage );
QgsFeedback *feedback = static_cast< QgsFeedback * >( pProgressData );
feedback->setProgress( dfComplete * 100 );
return ! feedback->isCanceled();
}, feedback );
GDALDatasetH hSrcDS = GDALOpen( mSourceUri.uri.toUtf8().constData(), GA_ReadOnly );
if ( ! hSrcDS )
{
*errorMessage = QObject::tr( "Failed to open source layer %1! See the OGR panel in the message logs for details.\n\n" ).arg( mSourceUri.name );
mHasError = true;
}
else
{
CPLErrorReset();
GDALDatasetH hOutDS = GDALTranslate( mOutputUrl.toUtf8().constData(), hSrcDS, psOptions, NULL );
if ( ! hOutDS )
{
*errorMessage = QObject::tr( "Failed to import layer %1! See the OGR panel in the message logs for details.\n\n" ).arg( mSourceUri.name );
mHasError = true;
}
else // All good!
{
GDALClose( hOutDS );
}
GDALClose( hSrcDS );
}
GDALTranslateOptionsFree( psOptions );
return ( feedback && feedback->isCanceled() ) ? ErrUserCanceled : ( mHasError ? WriteError : NoError ) ;
}
///@endcond

View File

@ -0,0 +1,52 @@
/***************************************************************************
qgsgeopackagerasterwriter.h - QgsGeoPackageRasterWriter
---------------------
begin : 23.8.2017
copyright : (C) 2017 by Alessandro Pasotti
email : apasotti at boundlessgeo dot com
***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
#ifndef QGSGEOPACKAGERASTERWRITER_H
#define QGSGEOPACKAGERASTERWRITER_H
///@cond PRIVATE
#define SIP_NO_FILE
#include "qgsmimedatautils.h"
#include "qgsfeedback.h"
class QgsGeoPackageRasterWriter
{
public:
//! Error codes
enum WriterError
{
NoError = 0, //!< No errors were encountered
WriteError, //! Generic GDAL Translate error
ErrUserCanceled, //!< User canceled the export
};
QgsGeoPackageRasterWriter( const QgsMimeDataUtils::Uri sourceUri, const QString destinationPath );
WriterError writeRaster( QgsFeedback *feedback, QString *errorMessage );
const QString outputUrl() const { return mOutputUrl; }
private:
QgsMimeDataUtils::Uri mSourceUri;
QString mOutputUrl;
bool mHasError = false;
};
///@endcond
#endif // QGSGEOPACKAGERASTERWRITER_H

View File

@ -0,0 +1,55 @@
/***************************************************************************
qgsgeopackagerasterwritertask.cpp - QgsGeoPackageRasterWriterTask
---------------------
begin : 23.8.2017
copyright : (C) 2017 by Alessandro Pasotti
email : apasotti at boundlessgeo dot com
***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
#include "qgsgeopackagerasterwritertask.h"
///@cond PRIVATE
QgsGeoPackageRasterWriterTask::QgsGeoPackageRasterWriterTask( const QgsMimeDataUtils::Uri sourceUri, const QString destinationPath )
: QgsTask( tr( "Saving %1" ).arg( destinationPath ), QgsTask::CanCancel )
, mWriter( sourceUri, destinationPath )
, mFeedback( new QgsFeedback() )
{
}
void QgsGeoPackageRasterWriterTask::cancel()
{
mError = QgsGeoPackageRasterWriter::WriterError::ErrUserCanceled;
mFeedback.get()->cancel();
}
bool QgsGeoPackageRasterWriterTask::run()
{
connect( mFeedback.get(), &QgsFeedback::progressChanged, this, &QgsGeoPackageRasterWriterTask::setProgress );
mError = mWriter.writeRaster( mFeedback.get(), &mErrorMessage );
return mError == QgsGeoPackageRasterWriter::WriterError::NoError;
}
void QgsGeoPackageRasterWriterTask::finished( bool result )
{
if ( result )
{
emit writeComplete( mWriter.outputUrl() );
}
else
{
emit errorOccurred( mError, mErrorMessage );
}
}
///@endcond

View File

@ -0,0 +1,83 @@
/***************************************************************************
qgsgeopackagerasterwritertask.h - QgsGeoPackageRasterWriterTask
---------------------
begin : 23.8.2017
copyright : (C) 2017 by Alessandro Pasotti
email : apasotti at boundlessgeo dot com
***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
#ifndef QGSGEOPACKAGERASTERWRITERTASK_H
#define QGSGEOPACKAGERASTERWRITERTASK_H
///@cond PRIVATE
#define SIP_NO_FILE
#include "qgis_core.h"
#include "qgsgeopackagerasterwriter.h"
#include "qgstaskmanager.h"
#include "qgsfeedback.h"
/**
* \class QgsGeoPackageRasterWriterTask
* QgsTask task which performs a QgsGeoPackageRasterWriter layer saving operation as a background
* task. This can be used to save a raster layer out to a file without blocking the
* QGIS interface.
* \since QGIS 3.0
* \see QgsGeoPackageRasterWriterTask
*/
class QgsGeoPackageRasterWriterTask : public QgsTask
{
Q_OBJECT
public:
/**
* Constructor for QgsVectorFileWriterTask. Takes a source \a layer, destination \a fileName
* and save \a options.
*/
QgsGeoPackageRasterWriterTask( const QgsMimeDataUtils::Uri sourceUri, const QString destinationPath );
virtual void cancel() override;
signals:
/**
* Emitted when writing the layer is successfully completed. The \a newFilename
* parameter indicates the file path for the written file.
*/
void writeComplete( const QString &newFilename );
/**
* Emitted when an error occurs which prevented the file being written (or if
* the task is canceled). The writing \a error and \a errorMessage will be reported.
*/
void errorOccurred( QgsGeoPackageRasterWriter::WriterError error, const QString &errorMessage );
protected:
virtual bool run() override;
virtual void finished( bool result ) override;
private:
QgsGeoPackageRasterWriter mWriter;
std::unique_ptr< QgsFeedback > mFeedback;
QgsGeoPackageRasterWriter::WriterError mError = QgsGeoPackageRasterWriter::WriterError::NoError ;
QString mErrorMessage;
};
///@endcond
#endif // QGSGEOPACKAGERASTERWRITERTASK_H

View File

@ -16,6 +16,7 @@ email : sherman at mrcc.com
***************************************************************************/
#include "qgsogrprovider.h"
#include "qgscplerrorhandler.h"
#include "qgsogrfeatureiterator.h"
#include "qgslogger.h"
#include "qgsmessagelog.h"
@ -80,31 +81,6 @@ static const QString TEXT_PROVIDER_DESCRIPTION =
static OGRwkbGeometryType ogrWkbGeometryTypeFromName( const QString &typeName );
class QgsCPLErrorHandler
{
static void CPL_STDCALL showError( CPLErr errClass, int errNo, const char *msg )
{
if ( errNo != OGRERR_NONE )
QgsMessageLog::logMessage( QObject::tr( "OGR[%1] error %2: %3" ).arg( errClass ).arg( errNo ).arg( msg ), QObject::tr( "OGR" ) );
}
public:
QgsCPLErrorHandler()
{
CPLPushErrorHandler( showError );
}
~QgsCPLErrorHandler()
{
CPLPopErrorHandler();
}
private:
QgsCPLErrorHandler( const QgsCPLErrorHandler &other );
QgsCPLErrorHandler &operator=( const QgsCPLErrorHandler &other );
};
bool QgsOgrProvider::convertField( QgsField &field, const QTextCodec &encoding )
{
@ -593,7 +569,6 @@ QString QgsOgrProvider::ogrWkbGeometryTypeName( OGRwkbGeometryType type ) const
QString geom;
// GDAL 2.1 can return M/ZM geometries
#if defined(GDAL_COMPUTE_VERSION) && GDAL_VERSION_NUM >= GDAL_COMPUTE_VERSION(2,1,0)
if ( wkbHasM( type ) )
{
geom = ogrWkbGeometryTypeName( wkbFlatten( type ) );
@ -603,7 +578,6 @@ QString QgsOgrProvider::ogrWkbGeometryTypeName( OGRwkbGeometryType type ) const
geom += "M";
return geom;
}
#endif
switch ( ( long )type )
{
@ -4365,10 +4339,10 @@ QGISEXTERN bool deleteLayer( const QString &uri, QString &errCause )
errCause = QObject::tr( "Success" );
break;
}
errCause = QObject::tr( "GDAL result code: %s" ).arg( errCause );
errCause = QObject::tr( "GDAL result code: %1" ).arg( errCause );
return error == OGRERR_NONE;
}
// This should never happen:
errCause = QObject::tr( "Layer not found: %s" ).arg( uri );
errCause = QObject::tr( "Layer not found: %1" ).arg( uri );
return false;
}