Skip to content

Commit 9e184fe

Browse files
committedJul 8, 2017
Add method to evaluate parameters to compatible vector layers
of a specified type
1 parent f8e37aa commit 9e184fe

File tree

7 files changed

+230
-0
lines changed

7 files changed

+230
-0
lines changed
 

‎python/core/processing/qgsprocessingparameters.sip

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -472,6 +472,22 @@ class QgsProcessingParameters
472472
:rtype: QgsProcessingFeatureSource
473473
%End
474474

475+
static QString parameterAsCompatibleSourceLayerPath( const QgsProcessingParameterDefinition *definition, const QVariantMap &parameters,
476+
QgsProcessingContext &context, const QStringList &compatibleFormats, const QString &preferredFormat = QString( "shp" ), QgsProcessingFeedback *feedback = 0 );
477+
%Docstring
478+
Evaluates the parameter with matching ``definition`` to a source vector layer file path of compatible format.
479+
480+
If the parameter is evaluated to an existing layer, and that layer is not of the format listed in the
481+
``compatibleFormats`` argument, then the layer will first be exported to a compatible format
482+
in a temporary location. The function will then return the path to that temporary file.
483+
484+
``compatibleFormats`` should consist entirely of lowercase file extensions, e.g. 'shp'.
485+
486+
The ``preferredFormat`` argument is used to specify to desired file extension to use when a temporary
487+
layer export is required. This defaults to shapefiles, because shapefiles are the future (don't believe the geopackage hype!).
488+
:rtype: str
489+
%End
490+
475491
static QgsMapLayer *parameterAsLayer( const QgsProcessingParameterDefinition *definition, const QVariantMap &parameters, QgsProcessingContext &context );
476492
%Docstring
477493
Evaluates the parameter with matching ``definition`` to a map layer.

‎python/core/processing/qgsprocessingutils.sip

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,27 @@ class QgsProcessingUtils
158158
:rtype: str
159159
%End
160160

161+
static QString convertToCompatibleFormat( const QgsVectorLayer *layer,
162+
bool selectedFeaturesOnly,
163+
const QString &baseName,
164+
const QStringList &compatibleFormats,
165+
const QString &preferredFormat,
166+
QgsProcessingContext &context,
167+
QgsProcessingFeedback *feedback );
168+
%Docstring
169+
Converts a source vector ``layer`` to a file path to a vector layer of compatible format.
170+
171+
If the specified ``layer`` is not of the format listed in the
172+
``compatibleFormats`` argument, then the layer will first be exported to a compatible format
173+
in a temporary location using ``baseName``. The function will then return the path to that temporary file.
174+
175+
``compatibleFormats`` should consist entirely of lowercase file extensions, e.g. 'shp'.
176+
177+
The ``preferredFormat`` argument is used to specify to desired file extension to use when a temporary
178+
layer export is required. This defaults to shapefiles.
179+
:rtype: str
180+
%End
181+
161182
};
162183

163184
class QgsProcessingFeatureSource : QgsFeatureSource

‎src/core/processing/qgsprocessingparameters.cpp

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
#include "qgsvectorlayerfeatureiterator.h"
2222
#include "qgsprocessingoutputs.h"
2323
#include "qgssettings.h"
24+
#include "qgsvectorfilewriter.h"
2425

2526
bool QgsProcessingParameters::isDynamic( const QVariantMap &parameters, const QString &name )
2627
{
@@ -327,6 +328,49 @@ QgsProcessingFeatureSource *QgsProcessingParameters::parameterAsSource( const Qg
327328
}
328329
}
329330

331+
QString QgsProcessingParameters::parameterAsCompatibleSourceLayerPath( const QgsProcessingParameterDefinition *definition, const QVariantMap &parameters, QgsProcessingContext &context, const QStringList &compatibleFormats, const QString &preferredFormat, QgsProcessingFeedback *feedback )
332+
{
333+
if ( !definition )
334+
return QString();
335+
336+
QVariant val = parameters.value( definition->name() );
337+
338+
bool selectedFeaturesOnly = false;
339+
if ( val.canConvert<QgsProcessingFeatureSourceDefinition>() )
340+
{
341+
// input is a QgsProcessingFeatureSourceDefinition - get extra properties from it
342+
QgsProcessingFeatureSourceDefinition fromVar = qvariant_cast<QgsProcessingFeatureSourceDefinition>( val );
343+
selectedFeaturesOnly = fromVar.selectedFeaturesOnly;
344+
val = fromVar.source;
345+
}
346+
347+
QString layerRef;
348+
if ( val.canConvert<QgsProperty>() )
349+
{
350+
layerRef = val.value< QgsProperty >().valueAsString( context.expressionContext(), definition->defaultValue().toString() );
351+
}
352+
else if ( !val.isValid() || val.toString().isEmpty() )
353+
{
354+
// fall back to default
355+
layerRef = definition->defaultValue().toString();
356+
}
357+
else
358+
{
359+
layerRef = val.toString();
360+
}
361+
362+
if ( layerRef.isEmpty() )
363+
return QString();
364+
365+
QgsVectorLayer *vl = qobject_cast< QgsVectorLayer *>( QgsProcessingUtils::mapLayerFromString( layerRef, context ) );
366+
if ( !vl )
367+
return QString();
368+
369+
return QgsProcessingUtils::convertToCompatibleFormat( vl, selectedFeaturesOnly, definition->name(),
370+
compatibleFormats, preferredFormat, context, feedback );
371+
}
372+
373+
330374
QgsMapLayer *QgsProcessingParameters::parameterAsLayer( const QgsProcessingParameterDefinition *definition, const QVariantMap &parameters, QgsProcessingContext &context )
331375
{
332376
if ( !definition )

‎src/core/processing/qgsprocessingparameters.h

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ class QgsFeatureSink;
3232
class QgsFeatureSource;
3333
class QgsProcessingFeatureSource;
3434
class QgsProcessingOutputDefinition;
35+
class QgsProcessingFeedback;
3536

3637
/**
3738
* \class QgsProcessingFeatureSourceDefinition
@@ -508,6 +509,21 @@ class CORE_EXPORT QgsProcessingParameters
508509
*/
509510
static QgsProcessingFeatureSource *parameterAsSource( const QgsProcessingParameterDefinition *definition, const QVariantMap &parameters, QgsProcessingContext &context ) SIP_FACTORY;
510511

512+
/**
513+
* Evaluates the parameter with matching \a definition to a source vector layer file path of compatible format.
514+
*
515+
* If the parameter is evaluated to an existing layer, and that layer is not of the format listed in the
516+
* \a compatibleFormats argument, then the layer will first be exported to a compatible format
517+
* in a temporary location. The function will then return the path to that temporary file.
518+
*
519+
* \a compatibleFormats should consist entirely of lowercase file extensions, e.g. 'shp'.
520+
*
521+
* The \a preferredFormat argument is used to specify to desired file extension to use when a temporary
522+
* layer export is required. This defaults to shapefiles, because shapefiles are the future (don't believe the geopackage hype!).
523+
*/
524+
static QString parameterAsCompatibleSourceLayerPath( const QgsProcessingParameterDefinition *definition, const QVariantMap &parameters,
525+
QgsProcessingContext &context, const QStringList &compatibleFormats, const QString &preferredFormat = QString( "shp" ), QgsProcessingFeedback *feedback = nullptr );
526+
511527
/**
512528
* Evaluates the parameter with matching \a definition to a map layer.
513529
*

‎src/core/processing/qgsprocessingutils.cpp

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -461,6 +461,42 @@ QString QgsProcessingUtils::formatHelpMapAsHtml( const QVariantMap &map, const Q
461461
return s;
462462
}
463463

464+
QString QgsProcessingUtils::convertToCompatibleFormat( const QgsVectorLayer *vl, bool selectedFeaturesOnly, const QString &baseName, const QStringList &compatibleFormats, const QString &preferredFormat, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
465+
{
466+
bool requiresTranslation = selectedFeaturesOnly;
467+
if ( !selectedFeaturesOnly )
468+
{
469+
QFileInfo fi( vl->source() );
470+
requiresTranslation = !compatibleFormats.contains( fi.suffix(), Qt::CaseInsensitive );
471+
}
472+
473+
if ( requiresTranslation )
474+
{
475+
QString temp = QgsProcessingUtils::generateTempFilename( baseName + '.' + preferredFormat );
476+
477+
QgsVectorFileWriter writer( temp, context.defaultEncoding(),
478+
vl->fields(), vl->wkbType(), vl->crs(), QgsVectorFileWriter::driverForExtension( preferredFormat ) );
479+
QgsFeature f;
480+
QgsFeatureIterator it;
481+
if ( selectedFeaturesOnly )
482+
it = vl->getSelectedFeatures();
483+
else
484+
it = vl->getFeatures();
485+
486+
while ( it.nextFeature( f ) )
487+
{
488+
if ( feedback->isCanceled() )
489+
return QString();
490+
writer.addFeature( f, QgsFeatureSink::FastInsert );
491+
}
492+
return temp;
493+
}
494+
else
495+
{
496+
return vl->source();
497+
}
498+
}
499+
464500

465501
//
466502
// QgsProcessingFeatureSource

‎src/core/processing/qgsprocessingutils.h

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
class QgsProject;
2929
class QgsProcessingContext;
3030
class QgsMapLayerStore;
31+
class QgsProcessingFeedback;
3132

3233
#include <QString>
3334
#include <QVariant>
@@ -189,6 +190,26 @@ class CORE_EXPORT QgsProcessingUtils
189190
*/
190191
static QString formatHelpMapAsHtml( const QVariantMap &map, const QgsProcessingAlgorithm *algorithm );
191192

193+
/**
194+
* Converts a source vector \a layer to a file path to a vector layer of compatible format.
195+
*
196+
* If the specified \a layer is not of the format listed in the
197+
* \a compatibleFormats argument, then the layer will first be exported to a compatible format
198+
* in a temporary location using \a baseName. The function will then return the path to that temporary file.
199+
*
200+
* \a compatibleFormats should consist entirely of lowercase file extensions, e.g. 'shp'.
201+
*
202+
* The \a preferredFormat argument is used to specify to desired file extension to use when a temporary
203+
* layer export is required. This defaults to shapefiles.
204+
*/
205+
static QString convertToCompatibleFormat( const QgsVectorLayer *layer,
206+
bool selectedFeaturesOnly,
207+
const QString &baseName,
208+
const QStringList &compatibleFormats,
209+
const QString &preferredFormat,
210+
QgsProcessingContext &context,
211+
QgsProcessingFeedback *feedback );
212+
192213
private:
193214

194215
static bool canUseLayer( const QgsRasterLayer *layer );

‎tests/src/core/testqgsprocessing.cpp

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -333,6 +333,7 @@ class TestQgsProcessing: public QObject
333333
void modelExecution();
334334
void modelAcceptableValues();
335335
void tempUtils();
336+
void convertCompatible();
336337

337338
private:
338339

@@ -5039,5 +5040,80 @@ void TestQgsProcessing::tempUtils()
50395040
QVERIFY( tempFile2.startsWith( tempFolder ) );
50405041
}
50415042

5043+
void TestQgsProcessing::convertCompatible()
5044+
{
5045+
// start with a compatible shapefile
5046+
QString testDataDir = QStringLiteral( TEST_DATA_DIR ) + '/'; //defined in CmakeLists.txt
5047+
QString vector = testDataDir + "points.shp";
5048+
QgsVectorLayer *layer = new QgsVectorLayer( vector, "vl" );
5049+
QgsProject p;
5050+
p.addMapLayer( layer );
5051+
5052+
QgsProcessingContext context;
5053+
context.setProject( &p );
5054+
QgsProcessingFeedback feedback;
5055+
QString out = QgsProcessingUtils::convertToCompatibleFormat( layer, false, QStringLiteral( "test" ), QStringList() << "shp", QString( "shp" ), context, &feedback );
5056+
// layer should be returned unchanged - underlying source is compatible
5057+
QCOMPARE( out, layer->source() );
5058+
5059+
// don't include shp as compatible type
5060+
out = QgsProcessingUtils::convertToCompatibleFormat( layer, false, QStringLiteral( "test" ), QStringList() << "tab", QString( "tab" ), context, &feedback );
5061+
QVERIFY( out != layer->source() );
5062+
QVERIFY( out.endsWith( ".tab" ) );
5063+
QVERIFY( out.startsWith( QgsProcessingUtils::tempFolder() ) );
5064+
5065+
// make sure all features are copied
5066+
QgsVectorLayer *t = new QgsVectorLayer( out, "vl2" );
5067+
QCOMPARE( layer->featureCount(), t->featureCount() );
5068+
QCOMPARE( layer->crs(), t->crs() );
5069+
5070+
// use a selection - this will require translation
5071+
QgsFeatureIds ids;
5072+
QgsFeature f;
5073+
QgsFeatureIterator it = layer->getFeatures();
5074+
it.nextFeature( f );
5075+
ids.insert( f.id() );
5076+
it.nextFeature( f );
5077+
ids.insert( f.id() );
5078+
5079+
layer->selectByIds( ids );
5080+
out = QgsProcessingUtils::convertToCompatibleFormat( layer, true, QStringLiteral( "test" ), QStringList() << "tab", QString( "tab" ), context, &feedback );
5081+
QVERIFY( out != layer->source() );
5082+
QVERIFY( out.endsWith( ".tab" ) );
5083+
QVERIFY( out.startsWith( QgsProcessingUtils::tempFolder() ) );
5084+
delete t;
5085+
t = new QgsVectorLayer( out, "vl2" );
5086+
QCOMPARE( t->featureCount(), static_cast< long >( ids.count() ) );
5087+
5088+
// using a selection but existing format - will still require translation
5089+
out = QgsProcessingUtils::convertToCompatibleFormat( layer, true, QStringLiteral( "test" ), QStringList() << "shp", QString( "shp" ), context, &feedback );
5090+
QVERIFY( out != layer->source() );
5091+
QVERIFY( out.endsWith( ".shp" ) );
5092+
QVERIFY( out.startsWith( QgsProcessingUtils::tempFolder() ) );
5093+
delete t;
5094+
t = new QgsVectorLayer( out, "vl2" );
5095+
QCOMPARE( t->featureCount(), static_cast< long >( ids.count() ) );
5096+
5097+
5098+
// also test evaluating parameter to compatible format
5099+
std::unique_ptr< QgsProcessingParameterDefinition > def( new QgsProcessingParameterFeatureSource( QStringLiteral( "source" ) ) );
5100+
QVariantMap params;
5101+
params.insert( QStringLiteral( "source" ), QgsProcessingFeatureSourceDefinition( layer->id(), false ) );
5102+
out = QgsProcessingParameters::parameterAsCompatibleSourceLayerPath( def.get(), params, context, QStringList() << "shp", QString( "shp" ), &feedback );
5103+
QCOMPARE( out, layer->source() );
5104+
5105+
out = QgsProcessingParameters::parameterAsCompatibleSourceLayerPath( def.get(), params, context, QStringList() << "tab", QString( "tab" ), &feedback );
5106+
QVERIFY( out != layer->source() );
5107+
QVERIFY( out.endsWith( ".tab" ) );
5108+
QVERIFY( out.startsWith( QgsProcessingUtils::tempFolder() ) );
5109+
5110+
// selected only, will force export
5111+
params.insert( QStringLiteral( "source" ), QgsProcessingFeatureSourceDefinition( layer->id(), true ) );
5112+
out = QgsProcessingParameters::parameterAsCompatibleSourceLayerPath( def.get(), params, context, QStringList() << "shp", QString( "shp" ), &feedback );
5113+
QVERIFY( out != layer->source() );
5114+
QVERIFY( out.endsWith( ".shp" ) );
5115+
QVERIFY( out.startsWith( QgsProcessingUtils::tempFolder() ) );
5116+
}
5117+
50425118
QGSTEST_MAIN( TestQgsProcessing )
50435119
#include "testqgsprocessing.moc"

0 commit comments

Comments
 (0)
Please sign in to comment.