[processing] Extend api for retrieving a layer in a compatible format

A few releases ago a bug fix was implemented which forced conversions
of multi-layer sources. This was a valid bug fix, but the consequence
was that any algorithm using this api with a source file containing
multiple layers (e.g. gpkg) performed a complete copy of the target
layer to a new file, severely impacting performance.

This commit adds new API to retrieve a compatible layer path in the
case when an algorithm CAN correctly handle specific target layer names.
In this case, the forced copy of the source layer is avoided when
using multi-layer inputs like geopackage.
This commit is contained in:
Nyall Dawson 2019-09-25 06:17:35 +10:00
parent b3f9ce1c26
commit 651c507180
10 changed files with 425 additions and 8 deletions

View File

@ -661,6 +661,43 @@ in a temporary location. The function will then return the path to that temporar
The ``preferredFormat`` argument is used to specify to desired file extension to use when a temporary
layer export is required.
When an algorithm is capable of handling multi-layer input files (such as Geopackage), it is preferable
to use parameterAsCompatibleSourceLayerPathAndLayerName() which may avoid conversion in more situations.
%End
QString parameterAsCompatibleSourceLayerPathAndLayerName( const QVariantMap &parameters, const QString &name,
QgsProcessingContext &context, const QStringList &compatibleFormats, const QString &preferredFormat = QString( "shp" ), QgsProcessingFeedback *feedback = 0, QString *layerName /Out/ = 0 );
%Docstring
Evaluates the parameter with matching ``name`` to a source vector layer file path and layer name of compatible format.
If the parameter is evaluated to an existing layer, and that layer is not of the format listed in the
``compatibleFormats`` argument, then the layer will first be exported to a compatible format
in a temporary location. The function will then return the path to that temporary file.
``compatibleFormats`` should consist entirely of lowercase file extensions, e.g. 'shp'.
The ``preferredFormat`` argument is used to specify to desired file extension to use when a temporary
layer export is required. This defaults to shapefiles, because shapefiles are the future (don't believe the geopackage hype!).
This method should be preferred over parameterAsCompatibleSourceLayerPath() when an algorithm is able
to correctly handle files with multiple layers. Unlike parameterAsCompatibleSourceLayerPath(), it will not force
a conversion in this case and will return the target layer name in the ``layerName`` argument.
:param parameters: input parameter value map
:param name: name of target parameter
:param context: processing context
:param compatibleFormats: a list of lowercase file extensions compatible with the algorithm
:param preferredFormat: preferred format extension to use if conversion if required
:param feedback: feedback object
:return: - path to source layer, or nearly converted compatible layer
- layerName: will be set to the target layer name for multi-layer sources (e.g. Geopackage)
.. seealso:: :py:func:`parameterAsCompatibleSourceLayerPath`
.. versionadded:: 3.10
%End
QgsMapLayer *parameterAsLayer( const QVariantMap &parameters, const QString &name, QgsProcessingContext &context ) const;

View File

@ -732,6 +732,43 @@ in a temporary location. The function will then return the path to that temporar
The ``preferredFormat`` argument is used to specify to desired file extension to use when a temporary
layer export is required. This defaults to shapefiles, because shapefiles are the future (don't believe the geopackage hype!).
When an algorithm is capable of handling multi-layer input files (such as Geopackage), it is preferable
to use parameterAsCompatibleSourceLayerPathAndLayerName() which may avoid conversion in more situations.
%End
static QString parameterAsCompatibleSourceLayerPathAndLayerName( const QgsProcessingParameterDefinition *definition, const QVariantMap &parameters,
QgsProcessingContext &context, const QStringList &compatibleFormats, const QString &preferredFormat = QString( "shp" ), QgsProcessingFeedback *feedback = 0, QString *layerName /Out/ = 0 );
%Docstring
Evaluates the parameter with matching ``definition`` to a source vector layer file path and layer name of compatible format.
If the parameter is evaluated to an existing layer, and that layer is not of the format listed in the
``compatibleFormats`` argument, then the layer will first be exported to a compatible format
in a temporary location. The function will then return the path to that temporary file.
``compatibleFormats`` should consist entirely of lowercase file extensions, e.g. 'shp'.
The ``preferredFormat`` argument is used to specify to desired file extension to use when a temporary
layer export is required. This defaults to shapefiles, because shapefiles are the future (don't believe the geopackage hype!).
This method should be preferred over parameterAsCompatibleSourceLayerPath() when an algorithm is able
to correctly handle files with multiple layers. Unlike parameterAsCompatibleSourceLayerPath(), it will not force
a conversion in this case and will return the target layer name in the ``layerName`` argument.
:param definition: associated parameter definition
:param parameters: input parameter value map
:param context: processing context
:param compatibleFormats: a list of lowercase file extensions compatible with the algorithm
:param preferredFormat: preferred format extension to use if conversion if required
:param feedback: feedback object
:return: - path to source layer, or nearly converted compatible layer
- layerName: will be set to the target layer name for multi-layer sources (e.g. Geopackage)
.. seealso:: :py:func:`parameterAsCompatibleSourceLayerPath`
.. versionadded:: 3.10
%End
static QgsMapLayer *parameterAsLayer( const QgsProcessingParameterDefinition *definition, const QVariantMap &parameters, QgsProcessingContext &context );

View File

@ -232,7 +232,7 @@ a specified ``algorithm``.
QgsProcessingContext &context,
QgsProcessingFeedback *feedback );
%Docstring
Converts a source vector ``layer`` to a file path to a vector layer of compatible format.
Converts a source vector ``layer`` to a file path of a vector layer of compatible format.
If the specified ``layer`` is not of the format listed in the
``compatibleFormats`` argument, then the layer will first be exported to a compatible format
@ -242,6 +242,52 @@ in a temporary location using ``baseName``. The function will then return the pa
The ``preferredFormat`` argument is used to specify to desired file extension to use when a temporary
layer export is required. This defaults to shapefiles.
When an algorithm is capable of handling multi-layer input files (such as Geopackage), it is preferable
to use convertToCompatibleFormatAndLayerName() which may avoid conversion in more situations.
.. seealso:: :py:func:`convertToCompatibleFormatAndLayerName`
%End
static QString convertToCompatibleFormatAndLayerName( const QgsVectorLayer *layer,
bool selectedFeaturesOnly,
const QString &baseName,
const QStringList &compatibleFormats,
const QString &preferredFormat,
QgsProcessingContext &context,
QgsProcessingFeedback *feedback,
QString &layerName /Out/ );
%Docstring
Converts a source vector ``layer`` to a file path and layer name of a vector layer of compatible format.
If the specified ``layer`` is not of the format listed in the
``compatibleFormats`` argument, then the layer will first be exported to a compatible format
in a temporary location using ``baseName``. The function will then return the path to that temporary file.
``compatibleFormats`` should consist entirely of lowercase file extensions, e.g. 'shp'.
The ``preferredFormat`` argument is used to specify to desired file extension to use when a temporary
layer export is required. This defaults to shapefiles.
This method should be preferred over convertToCompatibleFormat() when an algorithm is able
to correctly handle files with multiple layers. Unlike convertToCompatibleFormat(), it will not force
a conversion in this case and will return the target layer name in the ``layerName`` argument.
:param layer: source layer to convert (if required)
:param selectedFeaturesOnly: ``True`` if only selected features from the layer should be used
:param baseName: base file name for converted layer, if required
:param compatibleFormats: a list of lowercase file extensions compatible with the algorithm
:param preferredFormat: preferred format extension to use if conversion if required
:param context: processing context
:param feedback: feedback object
:return: - path to source layer, or nearly converted compatible layer
- layerName: will be set to the target layer name for multi-layer sources (e.g. Geopackage)
.. seealso:: :py:func:`convertToCompatibleFormat`
.. versionadded:: 3.10
%End
static QgsFields combineFields( const QgsFields &fieldsA, const QgsFields &fieldsB, const QString &fieldsBPrefix = QString() );

View File

@ -612,6 +612,11 @@ QString QgsProcessingAlgorithm::parameterAsCompatibleSourceLayerPath( const QVar
return QgsProcessingParameters::parameterAsCompatibleSourceLayerPath( parameterDefinition( name ), parameters, context, compatibleFormats, preferredFormat, feedback );
}
QString QgsProcessingAlgorithm::parameterAsCompatibleSourceLayerPathAndLayerName( const QVariantMap &parameters, const QString &name, QgsProcessingContext &context, const QStringList &compatibleFormats, const QString &preferredFormat, QgsProcessingFeedback *feedback, QString *layerName )
{
return QgsProcessingParameters::parameterAsCompatibleSourceLayerPathAndLayerName( parameterDefinition( name ), parameters, context, compatibleFormats, preferredFormat, feedback, layerName );
}
QgsMapLayer *QgsProcessingAlgorithm::parameterAsLayer( const QVariantMap &parameters, const QString &name, QgsProcessingContext &context ) const
{
return QgsProcessingParameters::parameterAsLayer( parameterDefinition( name ), parameters, context );

View File

@ -653,10 +653,45 @@ class CORE_EXPORT QgsProcessingAlgorithm
*
* The \a preferredFormat argument is used to specify to desired file extension to use when a temporary
* layer export is required.
*
* When an algorithm is capable of handling multi-layer input files (such as Geopackage), it is preferable
* to use parameterAsCompatibleSourceLayerPathAndLayerName() which may avoid conversion in more situations.
*/
QString parameterAsCompatibleSourceLayerPath( const QVariantMap &parameters, const QString &name,
QgsProcessingContext &context, const QStringList &compatibleFormats, const QString &preferredFormat = QString( "shp" ), QgsProcessingFeedback *feedback = nullptr );
/**
* Evaluates the parameter with matching \a name to a source vector layer file path and layer name of compatible format.
*
* If the parameter is evaluated to an existing layer, and that layer is not of the format listed in the
* \a compatibleFormats argument, then the layer will first be exported to a compatible format
* in a temporary location. The function will then return the path to that temporary file.
*
* \a compatibleFormats should consist entirely of lowercase file extensions, e.g. 'shp'.
*
* The \a preferredFormat argument is used to specify to desired file extension to use when a temporary
* layer export is required. This defaults to shapefiles, because shapefiles are the future (don't believe the geopackage hype!).
*
* This method should be preferred over parameterAsCompatibleSourceLayerPath() when an algorithm is able
* to correctly handle files with multiple layers. Unlike parameterAsCompatibleSourceLayerPath(), it will not force
* a conversion in this case and will return the target layer name in the \a layerName argument.
*
* \param parameters input parameter value map
* \param name name of target parameter
* \param context processing context
* \param compatibleFormats a list of lowercase file extensions compatible with the algorithm
* \param preferredFormat preferred format extension to use if conversion if required
* \param feedback feedback object
* \param layerName will be set to the target layer name for multi-layer sources (e.g. Geopackage)
*
* \returns path to source layer, or nearly converted compatible layer
*
* \see parameterAsCompatibleSourceLayerPath()
* \since QGIS 3.10
*/
QString parameterAsCompatibleSourceLayerPathAndLayerName( const QVariantMap &parameters, const QString &name,
QgsProcessingContext &context, const QStringList &compatibleFormats, const QString &preferredFormat = QString( "shp" ), QgsProcessingFeedback *feedback = nullptr, QString *layerName SIP_OUT = nullptr );
/**
* Evaluates the parameter with matching \a name to a map layer.
*

View File

@ -480,7 +480,7 @@ QgsProcessingFeatureSource *QgsProcessingParameters::parameterAsSource( const Qg
return QgsProcessingUtils::variantToSource( value, context, definition->defaultValue() );
}
QString QgsProcessingParameters::parameterAsCompatibleSourceLayerPath( const QgsProcessingParameterDefinition *definition, const QVariantMap &parameters, QgsProcessingContext &context, const QStringList &compatibleFormats, const QString &preferredFormat, QgsProcessingFeedback *feedback )
QString parameterAsCompatibleSourceLayerPathInternal( const QgsProcessingParameterDefinition *definition, const QVariantMap &parameters, QgsProcessingContext &context, const QStringList &compatibleFormats, const QString &preferredFormat, QgsProcessingFeedback *feedback, QString *layerName )
{
if ( !definition )
return QString();
@ -544,8 +544,29 @@ QString QgsProcessingParameters::parameterAsCompatibleSourceLayerPath( const Qgs
if ( !vl )
return QString();
return QgsProcessingUtils::convertToCompatibleFormat( vl, selectedFeaturesOnly, definition->name(),
compatibleFormats, preferredFormat, context, feedback );
if ( layerName )
return QgsProcessingUtils::convertToCompatibleFormatAndLayerName( vl, selectedFeaturesOnly, definition->name(),
compatibleFormats, preferredFormat, context, feedback, *layerName );
else
return QgsProcessingUtils::convertToCompatibleFormat( vl, selectedFeaturesOnly, definition->name(),
compatibleFormats, preferredFormat, context, feedback );
}
QString QgsProcessingParameters::parameterAsCompatibleSourceLayerPath( const QgsProcessingParameterDefinition *definition, const QVariantMap &parameters, QgsProcessingContext &context, const QStringList &compatibleFormats, const QString &preferredFormat, QgsProcessingFeedback *feedback )
{
return parameterAsCompatibleSourceLayerPathInternal( definition, parameters, context, compatibleFormats, preferredFormat, feedback, nullptr );
}
QString QgsProcessingParameters::parameterAsCompatibleSourceLayerPathAndLayerName( const QgsProcessingParameterDefinition *definition, const QVariantMap &parameters, QgsProcessingContext &context, const QStringList &compatibleFormats, const QString &preferredFormat, QgsProcessingFeedback *feedback, QString *layerName )
{
QString *destLayer = layerName;
QString tmp;
if ( destLayer )
destLayer->clear();
else
destLayer = &tmp;
return parameterAsCompatibleSourceLayerPathInternal( definition, parameters, context, compatibleFormats, preferredFormat, feedback, destLayer );
}

View File

@ -793,10 +793,45 @@ class CORE_EXPORT QgsProcessingParameters
*
* The \a preferredFormat argument is used to specify to desired file extension to use when a temporary
* layer export is required. This defaults to shapefiles, because shapefiles are the future (don't believe the geopackage hype!).
*
* When an algorithm is capable of handling multi-layer input files (such as Geopackage), it is preferable
* to use parameterAsCompatibleSourceLayerPathAndLayerName() which may avoid conversion in more situations.
*/
static QString parameterAsCompatibleSourceLayerPath( const QgsProcessingParameterDefinition *definition, const QVariantMap &parameters,
QgsProcessingContext &context, const QStringList &compatibleFormats, const QString &preferredFormat = QString( "shp" ), QgsProcessingFeedback *feedback = nullptr );
/**
* Evaluates the parameter with matching \a definition to a source vector layer file path and layer name of compatible format.
*
* If the parameter is evaluated to an existing layer, and that layer is not of the format listed in the
* \a compatibleFormats argument, then the layer will first be exported to a compatible format
* in a temporary location. The function will then return the path to that temporary file.
*
* \a compatibleFormats should consist entirely of lowercase file extensions, e.g. 'shp'.
*
* The \a preferredFormat argument is used to specify to desired file extension to use when a temporary
* layer export is required. This defaults to shapefiles, because shapefiles are the future (don't believe the geopackage hype!).
*
* This method should be preferred over parameterAsCompatibleSourceLayerPath() when an algorithm is able
* to correctly handle files with multiple layers. Unlike parameterAsCompatibleSourceLayerPath(), it will not force
* a conversion in this case and will return the target layer name in the \a layerName argument.
*
* \param definition associated parameter definition
* \param parameters input parameter value map
* \param context processing context
* \param compatibleFormats a list of lowercase file extensions compatible with the algorithm
* \param preferredFormat preferred format extension to use if conversion if required
* \param feedback feedback object
* \param layerName will be set to the target layer name for multi-layer sources (e.g. Geopackage)
*
* \returns path to source layer, or nearly converted compatible layer
*
* \see parameterAsCompatibleSourceLayerPath()
* \since QGIS 3.10
*/
static QString parameterAsCompatibleSourceLayerPathAndLayerName( const QgsProcessingParameterDefinition *definition, const QVariantMap &parameters,
QgsProcessingContext &context, const QStringList &compatibleFormats, const QString &preferredFormat = QString( "shp" ), QgsProcessingFeedback *feedback = nullptr, QString *layerName SIP_OUT = nullptr );
/**
* Evaluates the parameter with matching \a definition to a map layer.
*

View File

@ -779,7 +779,7 @@ QString QgsProcessingUtils::formatHelpMapAsHtml( const QVariantMap &map, const Q
return s;
}
QString QgsProcessingUtils::convertToCompatibleFormat( const QgsVectorLayer *vl, bool selectedFeaturesOnly, const QString &baseName, const QStringList &compatibleFormats, const QString &preferredFormat, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
QString convertToCompatibleFormatInternal( const QgsVectorLayer *vl, bool selectedFeaturesOnly, const QString &baseName, const QStringList &compatibleFormats, const QString &preferredFormat, QgsProcessingContext &context, QgsProcessingFeedback *feedback, QString *layerName )
{
bool requiresTranslation = false;
@ -813,8 +813,17 @@ QString QgsProcessingUtils::convertToCompatibleFormat( const QgsVectorLayer *vl,
// if the layer name doesn't match the filename, we need to convert the layer. This method can only return
// a filename, and cannot handle layernames as well as file paths
const QString layerName = parts.value( QStringLiteral( "layerName" ) ).toString();
requiresTranslation = requiresTranslation || ( !layerName.isEmpty() && layerName != fi.baseName() );
const QString srcLayerName = parts.value( QStringLiteral( "layerName" ) ).toString();
if ( layerName )
{
// differing layer names are acceptable
*layerName = srcLayerName;
}
else
{
// differing layer names are NOT acceptable
requiresTranslation = requiresTranslation || ( !srcLayerName.isEmpty() && srcLayerName != fi.baseName() );
}
}
else
{
@ -849,6 +858,17 @@ QString QgsProcessingUtils::convertToCompatibleFormat( const QgsVectorLayer *vl,
}
}
QString QgsProcessingUtils::convertToCompatibleFormat( const QgsVectorLayer *vl, bool selectedFeaturesOnly, const QString &baseName, const QStringList &compatibleFormats, const QString &preferredFormat, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
{
return convertToCompatibleFormatInternal( vl, selectedFeaturesOnly, baseName, compatibleFormats, preferredFormat, context, feedback, nullptr );
}
QString QgsProcessingUtils::convertToCompatibleFormatAndLayerName( const QgsVectorLayer *layer, bool selectedFeaturesOnly, const QString &baseName, const QStringList &compatibleFormats, const QString &preferredFormat, QgsProcessingContext &context, QgsProcessingFeedback *feedback, QString &layerName )
{
layerName.clear();
return convertToCompatibleFormatInternal( layer, selectedFeaturesOnly, baseName, compatibleFormats, preferredFormat, context, feedback, &layerName );
}
QgsFields QgsProcessingUtils::combineFields( const QgsFields &fieldsA, const QgsFields &fieldsB, const QString &fieldsBPrefix )
{
QgsFields outFields = fieldsA;

View File

@ -258,7 +258,7 @@ class CORE_EXPORT QgsProcessingUtils
static QString formatHelpMapAsHtml( const QVariantMap &map, const QgsProcessingAlgorithm *algorithm );
/**
* Converts a source vector \a layer to a file path to a vector layer of compatible format.
* Converts a source vector \a layer to a file path of a vector layer of compatible format.
*
* If the specified \a layer is not of the format listed in the
* \a compatibleFormats argument, then the layer will first be exported to a compatible format
@ -268,6 +268,11 @@ class CORE_EXPORT QgsProcessingUtils
*
* The \a preferredFormat argument is used to specify to desired file extension to use when a temporary
* layer export is required. This defaults to shapefiles.
*
* When an algorithm is capable of handling multi-layer input files (such as Geopackage), it is preferable
* to use convertToCompatibleFormatAndLayerName() which may avoid conversion in more situations.
*
* \see convertToCompatibleFormatAndLayerName()
*/
static QString convertToCompatibleFormat( const QgsVectorLayer *layer,
bool selectedFeaturesOnly,
@ -277,6 +282,45 @@ class CORE_EXPORT QgsProcessingUtils
QgsProcessingContext &context,
QgsProcessingFeedback *feedback );
/**
* Converts a source vector \a layer to a file path and layer name of a vector layer of compatible format.
*
* If the specified \a layer is not of the format listed in the
* \a compatibleFormats argument, then the layer will first be exported to a compatible format
* in a temporary location using \a baseName. The function will then return the path to that temporary file.
*
* \a compatibleFormats should consist entirely of lowercase file extensions, e.g. 'shp'.
*
* The \a preferredFormat argument is used to specify to desired file extension to use when a temporary
* layer export is required. This defaults to shapefiles.
*
* This method should be preferred over convertToCompatibleFormat() when an algorithm is able
* to correctly handle files with multiple layers. Unlike convertToCompatibleFormat(), it will not force
* a conversion in this case and will return the target layer name in the \a layerName argument.
*
* \param layer source layer to convert (if required)
* \param selectedFeaturesOnly TRUE if only selected features from the layer should be used
* \param baseName base file name for converted layer, if required
* \param compatibleFormats a list of lowercase file extensions compatible with the algorithm
* \param preferredFormat preferred format extension to use if conversion if required
* \param context processing context
* \param feedback feedback object
* \param layerName will be set to the target layer name for multi-layer sources (e.g. Geopackage)
*
* \returns path to source layer, or nearly converted compatible layer
*
* \see convertToCompatibleFormat()
* \since QGIS 3.10
*/
static QString convertToCompatibleFormatAndLayerName( const QgsVectorLayer *layer,
bool selectedFeaturesOnly,
const QString &baseName,
const QStringList &compatibleFormats,
const QString &preferredFormat,
QgsProcessingContext &context,
QgsProcessingFeedback *feedback,
QString &layerName SIP_OUT );
/**
* Combines two field lists, avoiding duplicate field names (in a case-insensitive manner).
*

View File

@ -8184,6 +8184,12 @@ void TestQgsProcessing::convertCompatible()
// layer should be returned unchanged - underlying source is compatible
QCOMPARE( out, layer->source() );
QString layerName;
out = QgsProcessingUtils::convertToCompatibleFormatAndLayerName( layer, false, QStringLiteral( "test" ), QStringList() << "shp", QString( "shp" ), context, &feedback, layerName );
// layer should be returned unchanged - underlying source is compatible
QCOMPARE( out, layer->source() );
QCOMPARE( layerName, QString() );
// path with layer suffix
QString vectorWithLayer = testDataDir + "points.shp|layername=points";
QgsVectorLayer *layer2 = new QgsVectorLayer( vectorWithLayer, "vl" );
@ -8191,6 +8197,9 @@ void TestQgsProcessing::convertCompatible()
out = QgsProcessingUtils::convertToCompatibleFormat( layer2, false, QStringLiteral( "test" ), QStringList() << "shp", QString( "shp" ), context, &feedback );
// layer should be returned unchanged - underlying source is compatible
QCOMPARE( out, vector );
out = QgsProcessingUtils::convertToCompatibleFormatAndLayerName( layer2, false, QStringLiteral( "test" ), QStringList() << "shp", QString( "shp" ), context, &feedback, layerName );
QCOMPARE( out, vector );
QCOMPARE( layerName, QStringLiteral( "points" ) );
// don't include shp as compatible type
out = QgsProcessingUtils::convertToCompatibleFormat( layer, false, QStringLiteral( "test" ), QStringList() << "tab", QString( "tab" ), context, &feedback );
@ -8203,6 +8212,12 @@ void TestQgsProcessing::convertCompatible()
QCOMPARE( layer->featureCount(), t->featureCount() );
QCOMPARE( layer->crs(), t->crs() );
out = QgsProcessingUtils::convertToCompatibleFormatAndLayerName( layer, false, QStringLiteral( "test2" ), QStringList() << "tab", QString( "tab" ), context, &feedback, layerName );
QVERIFY( out != layer->source() );
QVERIFY( out.endsWith( ".tab" ) );
QVERIFY( out.startsWith( QgsProcessingUtils::tempFolder() ) );
QCOMPARE( layerName, QString() );
// use a selection - this will require translation
QgsFeatureIds ids;
QgsFeature f;
@ -8220,6 +8235,14 @@ void TestQgsProcessing::convertCompatible()
t = qgis::make_unique< QgsVectorLayer >( out, "vl2" );
QCOMPARE( t->featureCount(), static_cast< long >( ids.count() ) );
out = QgsProcessingUtils::convertToCompatibleFormatAndLayerName( layer, true, QStringLiteral( "test" ), QStringList() << "tab", QString( "tab" ), context, &feedback, layerName );
QVERIFY( out != layer->source() );
QVERIFY( out.endsWith( ".tab" ) );
QVERIFY( out.startsWith( QgsProcessingUtils::tempFolder() ) );
t = qgis::make_unique< QgsVectorLayer >( out, "vl2" );
QCOMPARE( t->featureCount(), static_cast< long >( ids.count() ) );
QCOMPARE( layerName, QString() );
// using a selection but existing format - will still require translation
out = QgsProcessingUtils::convertToCompatibleFormat( layer, true, QStringLiteral( "test" ), QStringList() << "shp", QString( "shp" ), context, &feedback );
QVERIFY( out != layer->source() );
@ -8228,6 +8251,14 @@ void TestQgsProcessing::convertCompatible()
t = qgis::make_unique< QgsVectorLayer >( out, "vl2" );
QCOMPARE( t->featureCount(), static_cast< long >( ids.count() ) );
out = QgsProcessingUtils::convertToCompatibleFormatAndLayerName( layer, true, QStringLiteral( "test" ), QStringList() << "shp", QString( "shp" ), context, &feedback, layerName );
QVERIFY( out != layer->source() );
QVERIFY( out.endsWith( ".shp" ) );
QVERIFY( out.startsWith( QgsProcessingUtils::tempFolder() ) );
t = qgis::make_unique< QgsVectorLayer >( out, "vl2" );
QCOMPARE( t->featureCount(), static_cast< long >( ids.count() ) );
QCOMPARE( layerName, QString() );
// using a feature filter -- this will require translation
layer->setSubsetString( "1 or 2" );
out = QgsProcessingUtils::convertToCompatibleFormat( layer, false, QStringLiteral( "test" ), QStringList() << "shp", QString( "shp" ), context, &feedback );
@ -8236,6 +8267,14 @@ void TestQgsProcessing::convertCompatible()
QVERIFY( out.startsWith( QgsProcessingUtils::tempFolder() ) );
t = qgis::make_unique< QgsVectorLayer >( out, "vl2" );
QCOMPARE( t->featureCount(), layer->featureCount() );
out = QgsProcessingUtils::convertToCompatibleFormatAndLayerName( layer, false, QStringLiteral( "test" ), QStringList() << "shp", QString( "shp" ), context, &feedback, layerName );
QVERIFY( out != layer->source() );
QVERIFY( out.endsWith( ".shp" ) );
QVERIFY( out.startsWith( QgsProcessingUtils::tempFolder() ) );
t = qgis::make_unique< QgsVectorLayer >( out, "vl2" );
QCOMPARE( t->featureCount(), layer->featureCount() );
QCOMPARE( layerName, QString() );
layer->setSubsetString( QString() );
// using GDAL's virtual I/O (/vsizip/, etc.)
@ -8250,6 +8289,15 @@ void TestQgsProcessing::convertCompatible()
t = qgis::make_unique< QgsVectorLayer >( out, "vl2" );
QCOMPARE( t->featureCount(), layer->featureCount() );
out = QgsProcessingUtils::convertToCompatibleFormatAndLayerName( vsiLayer, false, QStringLiteral( "test" ), QStringList() << "shp", QString( "shp" ), context, &feedback, layerName );
QVERIFY( out != layer->source() );
QVERIFY( out.endsWith( ".shp" ) );
QVERIFY( !out.contains( "/vsizip" ) );
QVERIFY( out.startsWith( QgsProcessingUtils::tempFolder() ) );
t = qgis::make_unique< QgsVectorLayer >( out, "vl2" );
QCOMPARE( t->featureCount(), layer->featureCount() );
QCOMPARE( layerName, QString() );
// non-OGR source -- must be translated, regardless of extension. (e.g. delimited text provider handles CSV very different to OGR!)
std::unique_ptr< QgsVectorLayer > memLayer = qgis::make_unique< QgsVectorLayer> ( "Point", "v1", "memory" );
for ( int i = 1; i < 6; ++i )
@ -8265,6 +8313,14 @@ void TestQgsProcessing::convertCompatible()
t = qgis::make_unique< QgsVectorLayer >( out, "vl2" );
QCOMPARE( t->featureCount(), memLayer->featureCount() );
out = QgsProcessingUtils::convertToCompatibleFormatAndLayerName( memLayer.get(), false, QStringLiteral( "test" ), QStringList() << "shp", QString( "shp" ), context, &feedback, layerName );
QVERIFY( out != memLayer->source() );
QVERIFY( out.endsWith( ".shp" ) );
QVERIFY( out.startsWith( QgsProcessingUtils::tempFolder() ) );
t = qgis::make_unique< QgsVectorLayer >( out, "vl2" );
QCOMPARE( t->featureCount(), memLayer->featureCount() );
QCOMPARE( layerName, QString() );
//delimited text -- must be translated, regardless of extension. (delimited text provider handles CSV very different to OGR!)
QString csvPath = "file://" + testDataDir + "delimitedtext/testpt.csv?type=csv&useHeader=No&detectTypes=yes&xyDms=yes&geomType=none&subsetIndex=no&watchFile=no";
std::unique_ptr< QgsVectorLayer > csvLayer = qgis::make_unique< QgsVectorLayer >( csvPath, "vl", "delimitedtext" );
@ -8276,6 +8332,14 @@ void TestQgsProcessing::convertCompatible()
t = qgis::make_unique< QgsVectorLayer >( out, "vl2" );
QCOMPARE( t->featureCount(), csvLayer->featureCount() );
out = QgsProcessingUtils::convertToCompatibleFormatAndLayerName( csvLayer.get(), false, QStringLiteral( "test" ), QStringList() << "csv", QString( "csv" ), context, &feedback, layerName );
QVERIFY( out != csvLayer->source() );
QVERIFY( out.endsWith( ".csv" ) );
QVERIFY( out.startsWith( QgsProcessingUtils::tempFolder() ) );
t = qgis::make_unique< QgsVectorLayer >( out, "vl2" );
QCOMPARE( t->featureCount(), csvLayer->featureCount() );
QCOMPARE( layerName, QString() );
// geopackage with layer
QString gpkgPath = testDataDir + "points_gpkg.gpkg|layername=points_gpkg";
std::unique_ptr< QgsVectorLayer > gpkgLayer = qgis::make_unique< QgsVectorLayer >( gpkgPath, "vl" );
@ -8290,29 +8354,59 @@ void TestQgsProcessing::convertCompatible()
QVERIFY( out.endsWith( ".shp" ) );
QVERIFY( out.startsWith( QgsProcessingUtils::tempFolder() ) );
gpkgPath = testDataDir + "points_gpkg.gpkg|layername=points_gpkg";
gpkgLayer = qgis::make_unique< QgsVectorLayer >( gpkgPath, "vl" );
QVERIFY( gpkgLayer->isValid() );
out = QgsProcessingUtils::convertToCompatibleFormatAndLayerName( gpkgLayer.get(), false, QStringLiteral( "test" ), QStringList() << "gpkg" << "shp", QString( "shp" ), context, &feedback, layerName );
// layer SHOULD NOT be translated -- in this case we know that the external tool can handle specifying the correct layer
QCOMPARE( out, testDataDir + QStringLiteral( "points_gpkg.gpkg" ) );
QCOMPARE( layerName, QStringLiteral( "points_gpkg" ) );
gpkgPath = testDataDir + "points_gpkg.gpkg|layername=points_small";
gpkgLayer = qgis::make_unique< QgsVectorLayer >( gpkgPath, "vl" );
QVERIFY( gpkgLayer->isValid() );
out = QgsProcessingUtils::convertToCompatibleFormatAndLayerName( gpkgLayer.get(), false, QStringLiteral( "test" ), QStringList() << "gpkg" << "shp", QString( "shp" ), context, &feedback, layerName );
QCOMPARE( out, testDataDir + QStringLiteral( "points_gpkg.gpkg" ) );
QCOMPARE( layerName, QStringLiteral( "points_small" ) );
// also test evaluating parameter to compatible format
std::unique_ptr< QgsProcessingParameterDefinition > def( new QgsProcessingParameterFeatureSource( QStringLiteral( "source" ) ) );
QVariantMap params;
params.insert( QStringLiteral( "source" ), QgsProcessingFeatureSourceDefinition( layer->id(), false ) );
out = QgsProcessingParameters::parameterAsCompatibleSourceLayerPath( def.get(), params, context, QStringList() << "shp", QString( "shp" ), &feedback );
QCOMPARE( out, testDataDir + "points.shp" );
out = QgsProcessingParameters::parameterAsCompatibleSourceLayerPathAndLayerName( def.get(), params, context, QStringList() << "shp", QString( "shp" ), &feedback, &layerName );
QCOMPARE( out, testDataDir + "points.shp" );
QCOMPARE( layerName, QString() );
// incompatible format, will be converted
out = QgsProcessingParameters::parameterAsCompatibleSourceLayerPath( def.get(), params, context, QStringList() << "tab", QString( "tab" ), &feedback );
QVERIFY( out != layer->source() );
QVERIFY( out.endsWith( ".tab" ) );
QVERIFY( out.startsWith( QgsProcessingUtils::tempFolder() ) );
out = QgsProcessingParameters::parameterAsCompatibleSourceLayerPathAndLayerName( def.get(), params, context, QStringList() << "tab", QString( "tab" ), &feedback, &layerName );
QVERIFY( out != layer->source() );
QVERIFY( out.endsWith( ".tab" ) );
QVERIFY( out.startsWith( QgsProcessingUtils::tempFolder() ) );
QCOMPARE( layerName, QString() );
// layer as input
params.insert( QStringLiteral( "source" ), QVariant::fromValue( layer ) );
out = QgsProcessingParameters::parameterAsCompatibleSourceLayerPath( def.get(), params, context, QStringList() << "shp", QString( "shp" ), &feedback );
QCOMPARE( out, testDataDir + "points.shp" );
out = QgsProcessingParameters::parameterAsCompatibleSourceLayerPathAndLayerName( def.get(), params, context, QStringList() << "shp", QString( "shp" ), &feedback, &layerName );
QCOMPARE( out, testDataDir + "points.shp" );
QCOMPARE( layerName, QString() );
// incompatible format, will be converted
out = QgsProcessingParameters::parameterAsCompatibleSourceLayerPath( def.get(), params, context, QStringList() << "tab", QString( "tab" ), &feedback );
QVERIFY( out != layer->source() );
QVERIFY( out.endsWith( ".tab" ) );
QVERIFY( out.startsWith( QgsProcessingUtils::tempFolder() ) );
out = QgsProcessingParameters::parameterAsCompatibleSourceLayerPathAndLayerName( def.get(), params, context, QStringList() << "tab", QString( "tab" ), &feedback, &layerName );
QVERIFY( out != layer->source() );
QVERIFY( out.endsWith( ".tab" ) );
QVERIFY( out.startsWith( QgsProcessingUtils::tempFolder() ) );
QCOMPARE( layerName, QString() );
// selected only, will force export
params.insert( QStringLiteral( "source" ), QgsProcessingFeatureSourceDefinition( layer->id(), true ) );
@ -8320,17 +8414,60 @@ void TestQgsProcessing::convertCompatible()
QVERIFY( out != layer->source() );
QVERIFY( out.endsWith( ".shp" ) );
QVERIFY( out.startsWith( QgsProcessingUtils::tempFolder() ) );
out = QgsProcessingParameters::parameterAsCompatibleSourceLayerPathAndLayerName( def.get(), params, context, QStringList() << "shp", QString( "shp" ), &feedback, &layerName );
QVERIFY( out != layer->source() );
QVERIFY( out.endsWith( ".shp" ) );
QVERIFY( out.startsWith( QgsProcessingUtils::tempFolder() ) );
QCOMPARE( layerName, QString() );
// vector layer as default
def.reset( new QgsProcessingParameterFeatureSource( QStringLiteral( "source" ), QString(), QList<int>(), QVariant::fromValue( layer ) ) );
params.remove( QStringLiteral( "source" ) );
out = QgsProcessingParameters::parameterAsCompatibleSourceLayerPath( def.get(), params, context, QStringList() << "shp", QString( "shp" ), &feedback );
QCOMPARE( out, testDataDir + "points.shp" );
out = QgsProcessingParameters::parameterAsCompatibleSourceLayerPathAndLayerName( def.get(), params, context, QStringList() << "shp", QString( "shp" ), &feedback, &layerName );
QCOMPARE( out, testDataDir + "points.shp" );
QCOMPARE( layerName, QString() );
// geopackage with layer
gpkgPath = testDataDir + "points_gpkg.gpkg|layername=points_gpkg";
gpkgLayer = qgis::make_unique< QgsVectorLayer >( gpkgPath, "vl" );
QVERIFY( gpkgLayer->isValid() );
params.insert( QStringLiteral( "source" ), QVariant::fromValue( gpkgLayer.get() ) );
out = QgsProcessingParameters::parameterAsCompatibleSourceLayerPath( def.get(), params, context, QStringList() << "gpkg" << "shp", QString( "shp" ), &feedback );
// layer must be translated -- we do not know if external tool can handle picking the correct layer automatically
QCOMPARE( out, testDataDir + QStringLiteral( "points_gpkg.gpkg" ) );
gpkgPath = testDataDir + "points_gpkg.gpkg|layername=points_small";
gpkgLayer = qgis::make_unique< QgsVectorLayer >( gpkgPath, "vl" );
QVERIFY( gpkgLayer->isValid() );
params.insert( QStringLiteral( "source" ), QVariant::fromValue( gpkgLayer.get() ) );
out = QgsProcessingParameters::parameterAsCompatibleSourceLayerPath( def.get(), params, context, QStringList() << "gpkg" << "shp", QString( "shp" ), &feedback );
QVERIFY( out.endsWith( ".shp" ) );
QVERIFY( out.startsWith( QgsProcessingUtils::tempFolder() ) );
gpkgPath = testDataDir + "points_gpkg.gpkg|layername=points_gpkg";
gpkgLayer = qgis::make_unique< QgsVectorLayer >( gpkgPath, "vl" );
QVERIFY( gpkgLayer->isValid() );
params.insert( QStringLiteral( "source" ), QVariant::fromValue( gpkgLayer.get() ) );
out = QgsProcessingParameters::parameterAsCompatibleSourceLayerPathAndLayerName( def.get(), params, context, QStringList() << "gpkg" << "shp", QString( "shp" ), &feedback, &layerName );
// layer SHOULD NOT be translated -- in this case we know that the external tool can handle specifying the correct layer
QCOMPARE( out, testDataDir + QStringLiteral( "points_gpkg.gpkg" ) );
QCOMPARE( layerName, QStringLiteral( "points_gpkg" ) );
gpkgPath = testDataDir + "points_gpkg.gpkg|layername=points_small";
gpkgLayer = qgis::make_unique< QgsVectorLayer >( gpkgPath, "vl" );
QVERIFY( gpkgLayer->isValid() );
params.insert( QStringLiteral( "source" ), QVariant::fromValue( gpkgLayer.get() ) );
out = QgsProcessingParameters::parameterAsCompatibleSourceLayerPathAndLayerName( def.get(), params, context, QStringList() << "gpkg" << "shp", QString( "shp" ), &feedback, &layerName );
QCOMPARE( out, testDataDir + QStringLiteral( "points_gpkg.gpkg" ) );
QCOMPARE( layerName, QStringLiteral( "points_small" ) );
// output layer as input - e.g. from a previous model child
params.insert( QStringLiteral( "source" ), QgsProcessingOutputLayerDefinition( layer->id() ) );
out = QgsProcessingParameters::parameterAsCompatibleSourceLayerPath( def.get(), params, context, QStringList() << "shp", QString( "shp" ), &feedback );
QCOMPARE( out, testDataDir + "points.shp" );
out = QgsProcessingParameters::parameterAsCompatibleSourceLayerPathAndLayerName( def.get(), params, context, QStringList() << "shp", QString( "shp" ), &feedback, &layerName );
QCOMPARE( out, testDataDir + "points.shp" );
QCOMPARE( layerName, QString() );
}
void TestQgsProcessing::create()