Add method to evaluate parameters to compatible vector layers

of a specified type
This commit is contained in:
Nyall Dawson 2017-06-27 15:24:48 +10:00
parent f8e37aa7cb
commit 9e184feaed
7 changed files with 230 additions and 0 deletions

View File

@ -472,6 +472,22 @@ class QgsProcessingParameters
:rtype: QgsProcessingFeatureSource :rtype: QgsProcessingFeatureSource
%End %End
static QString parameterAsCompatibleSourceLayerPath( const QgsProcessingParameterDefinition *definition, const QVariantMap &parameters,
QgsProcessingContext &context, const QStringList &compatibleFormats, const QString &preferredFormat = QString( "shp" ), QgsProcessingFeedback *feedback = 0 );
%Docstring
Evaluates the parameter with matching ``definition`` to a source vector layer file path 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!).
:rtype: str
%End
static QgsMapLayer *parameterAsLayer( const QgsProcessingParameterDefinition *definition, const QVariantMap &parameters, QgsProcessingContext &context ); static QgsMapLayer *parameterAsLayer( const QgsProcessingParameterDefinition *definition, const QVariantMap &parameters, QgsProcessingContext &context );
%Docstring %Docstring
Evaluates the parameter with matching ``definition`` to a map layer. Evaluates the parameter with matching ``definition`` to a map layer.

View File

@ -158,6 +158,27 @@ class QgsProcessingUtils
:rtype: str :rtype: str
%End %End
static QString convertToCompatibleFormat( const QgsVectorLayer *layer,
bool selectedFeaturesOnly,
const QString &baseName,
const QStringList &compatibleFormats,
const QString &preferredFormat,
QgsProcessingContext &context,
QgsProcessingFeedback *feedback );
%Docstring
Converts a source vector ``layer`` to a file path to 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.
:rtype: str
%End
}; };
class QgsProcessingFeatureSource : QgsFeatureSource class QgsProcessingFeatureSource : QgsFeatureSource

View File

@ -21,6 +21,7 @@
#include "qgsvectorlayerfeatureiterator.h" #include "qgsvectorlayerfeatureiterator.h"
#include "qgsprocessingoutputs.h" #include "qgsprocessingoutputs.h"
#include "qgssettings.h" #include "qgssettings.h"
#include "qgsvectorfilewriter.h"
bool QgsProcessingParameters::isDynamic( const QVariantMap &parameters, const QString &name ) bool QgsProcessingParameters::isDynamic( const QVariantMap &parameters, const QString &name )
{ {
@ -327,6 +328,49 @@ QgsProcessingFeatureSource *QgsProcessingParameters::parameterAsSource( const Qg
} }
} }
QString QgsProcessingParameters::parameterAsCompatibleSourceLayerPath( const QgsProcessingParameterDefinition *definition, const QVariantMap &parameters, QgsProcessingContext &context, const QStringList &compatibleFormats, const QString &preferredFormat, QgsProcessingFeedback *feedback )
{
if ( !definition )
return QString();
QVariant val = parameters.value( definition->name() );
bool selectedFeaturesOnly = false;
if ( val.canConvert<QgsProcessingFeatureSourceDefinition>() )
{
// input is a QgsProcessingFeatureSourceDefinition - get extra properties from it
QgsProcessingFeatureSourceDefinition fromVar = qvariant_cast<QgsProcessingFeatureSourceDefinition>( val );
selectedFeaturesOnly = fromVar.selectedFeaturesOnly;
val = fromVar.source;
}
QString layerRef;
if ( val.canConvert<QgsProperty>() )
{
layerRef = val.value< QgsProperty >().valueAsString( context.expressionContext(), definition->defaultValue().toString() );
}
else if ( !val.isValid() || val.toString().isEmpty() )
{
// fall back to default
layerRef = definition->defaultValue().toString();
}
else
{
layerRef = val.toString();
}
if ( layerRef.isEmpty() )
return QString();
QgsVectorLayer *vl = qobject_cast< QgsVectorLayer *>( QgsProcessingUtils::mapLayerFromString( layerRef, context ) );
if ( !vl )
return QString();
return QgsProcessingUtils::convertToCompatibleFormat( vl, selectedFeaturesOnly, definition->name(),
compatibleFormats, preferredFormat, context, feedback );
}
QgsMapLayer *QgsProcessingParameters::parameterAsLayer( const QgsProcessingParameterDefinition *definition, const QVariantMap &parameters, QgsProcessingContext &context ) QgsMapLayer *QgsProcessingParameters::parameterAsLayer( const QgsProcessingParameterDefinition *definition, const QVariantMap &parameters, QgsProcessingContext &context )
{ {
if ( !definition ) if ( !definition )

View File

@ -32,6 +32,7 @@ class QgsFeatureSink;
class QgsFeatureSource; class QgsFeatureSource;
class QgsProcessingFeatureSource; class QgsProcessingFeatureSource;
class QgsProcessingOutputDefinition; class QgsProcessingOutputDefinition;
class QgsProcessingFeedback;
/** /**
* \class QgsProcessingFeatureSourceDefinition * \class QgsProcessingFeatureSourceDefinition
@ -508,6 +509,21 @@ class CORE_EXPORT QgsProcessingParameters
*/ */
static QgsProcessingFeatureSource *parameterAsSource( const QgsProcessingParameterDefinition *definition, const QVariantMap &parameters, QgsProcessingContext &context ) SIP_FACTORY; static QgsProcessingFeatureSource *parameterAsSource( const QgsProcessingParameterDefinition *definition, const QVariantMap &parameters, QgsProcessingContext &context ) SIP_FACTORY;
/**
* Evaluates the parameter with matching \a definition to a source vector layer file path 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!).
*/
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 map layer. * Evaluates the parameter with matching \a definition to a map layer.
* *

View File

@ -461,6 +461,42 @@ QString QgsProcessingUtils::formatHelpMapAsHtml( const QVariantMap &map, const Q
return s; return s;
} }
QString QgsProcessingUtils::convertToCompatibleFormat( const QgsVectorLayer *vl, bool selectedFeaturesOnly, const QString &baseName, const QStringList &compatibleFormats, const QString &preferredFormat, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
{
bool requiresTranslation = selectedFeaturesOnly;
if ( !selectedFeaturesOnly )
{
QFileInfo fi( vl->source() );
requiresTranslation = !compatibleFormats.contains( fi.suffix(), Qt::CaseInsensitive );
}
if ( requiresTranslation )
{
QString temp = QgsProcessingUtils::generateTempFilename( baseName + '.' + preferredFormat );
QgsVectorFileWriter writer( temp, context.defaultEncoding(),
vl->fields(), vl->wkbType(), vl->crs(), QgsVectorFileWriter::driverForExtension( preferredFormat ) );
QgsFeature f;
QgsFeatureIterator it;
if ( selectedFeaturesOnly )
it = vl->getSelectedFeatures();
else
it = vl->getFeatures();
while ( it.nextFeature( f ) )
{
if ( feedback->isCanceled() )
return QString();
writer.addFeature( f, QgsFeatureSink::FastInsert );
}
return temp;
}
else
{
return vl->source();
}
}
// //
// QgsProcessingFeatureSource // QgsProcessingFeatureSource

View File

@ -28,6 +28,7 @@
class QgsProject; class QgsProject;
class QgsProcessingContext; class QgsProcessingContext;
class QgsMapLayerStore; class QgsMapLayerStore;
class QgsProcessingFeedback;
#include <QString> #include <QString>
#include <QVariant> #include <QVariant>
@ -189,6 +190,26 @@ class CORE_EXPORT QgsProcessingUtils
*/ */
static QString formatHelpMapAsHtml( const QVariantMap &map, const QgsProcessingAlgorithm *algorithm ); 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.
*
* 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.
*/
static QString convertToCompatibleFormat( const QgsVectorLayer *layer,
bool selectedFeaturesOnly,
const QString &baseName,
const QStringList &compatibleFormats,
const QString &preferredFormat,
QgsProcessingContext &context,
QgsProcessingFeedback *feedback );
private: private:
static bool canUseLayer( const QgsRasterLayer *layer ); static bool canUseLayer( const QgsRasterLayer *layer );

View File

@ -333,6 +333,7 @@ class TestQgsProcessing: public QObject
void modelExecution(); void modelExecution();
void modelAcceptableValues(); void modelAcceptableValues();
void tempUtils(); void tempUtils();
void convertCompatible();
private: private:
@ -5039,5 +5040,80 @@ void TestQgsProcessing::tempUtils()
QVERIFY( tempFile2.startsWith( tempFolder ) ); QVERIFY( tempFile2.startsWith( tempFolder ) );
} }
void TestQgsProcessing::convertCompatible()
{
// start with a compatible shapefile
QString testDataDir = QStringLiteral( TEST_DATA_DIR ) + '/'; //defined in CmakeLists.txt
QString vector = testDataDir + "points.shp";
QgsVectorLayer *layer = new QgsVectorLayer( vector, "vl" );
QgsProject p;
p.addMapLayer( layer );
QgsProcessingContext context;
context.setProject( &p );
QgsProcessingFeedback feedback;
QString out = QgsProcessingUtils::convertToCompatibleFormat( layer, false, QStringLiteral( "test" ), QStringList() << "shp", QString( "shp" ), context, &feedback );
// layer should be returned unchanged - underlying source is compatible
QCOMPARE( out, layer->source() );
// don't include shp as compatible type
out = QgsProcessingUtils::convertToCompatibleFormat( layer, false, QStringLiteral( "test" ), QStringList() << "tab", QString( "tab" ), context, &feedback );
QVERIFY( out != layer->source() );
QVERIFY( out.endsWith( ".tab" ) );
QVERIFY( out.startsWith( QgsProcessingUtils::tempFolder() ) );
// make sure all features are copied
QgsVectorLayer *t = new QgsVectorLayer( out, "vl2" );
QCOMPARE( layer->featureCount(), t->featureCount() );
QCOMPARE( layer->crs(), t->crs() );
// use a selection - this will require translation
QgsFeatureIds ids;
QgsFeature f;
QgsFeatureIterator it = layer->getFeatures();
it.nextFeature( f );
ids.insert( f.id() );
it.nextFeature( f );
ids.insert( f.id() );
layer->selectByIds( ids );
out = QgsProcessingUtils::convertToCompatibleFormat( layer, true, QStringLiteral( "test" ), QStringList() << "tab", QString( "tab" ), context, &feedback );
QVERIFY( out != layer->source() );
QVERIFY( out.endsWith( ".tab" ) );
QVERIFY( out.startsWith( QgsProcessingUtils::tempFolder() ) );
delete t;
t = new QgsVectorLayer( out, "vl2" );
QCOMPARE( t->featureCount(), static_cast< long >( ids.count() ) );
// 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() );
QVERIFY( out.endsWith( ".shp" ) );
QVERIFY( out.startsWith( QgsProcessingUtils::tempFolder() ) );
delete t;
t = new QgsVectorLayer( out, "vl2" );
QCOMPARE( t->featureCount(), static_cast< long >( ids.count() ) );
// 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, layer->source() );
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() ) );
// selected only, will force export
params.insert( QStringLiteral( "source" ), QgsProcessingFeatureSourceDefinition( layer->id(), true ) );
out = QgsProcessingParameters::parameterAsCompatibleSourceLayerPath( def.get(), params, context, QStringList() << "shp", QString( "shp" ), &feedback );
QVERIFY( out != layer->source() );
QVERIFY( out.endsWith( ".shp" ) );
QVERIFY( out.startsWith( QgsProcessingUtils::tempFolder() ) );
}
QGSTEST_MAIN( TestQgsProcessing ) QGSTEST_MAIN( TestQgsProcessing )
#include "testqgsprocessing.moc" #include "testqgsprocessing.moc"