mirror of
https://github.com/qgis/QGIS.git
synced 2025-12-07 00:03:52 -05:00
[processing] Port vector.createVectorWriter to c++
This implements an improved version of vector.createVectorWriter in QgsProcessingUtils. The improved version relies on the core class QgsVectorLayerImport to create empty layers, which: - reduces duplicate code and reuses the mature QgsVectorLayerImport routines - avoids manual conversion of field types to destination provider field types - potentially allows any writable provider to be used as a feature sink for algorithms (e.g. output direct to MSSQL/Oracle/db2). This should work now - it just needs exposing via UI.
This commit is contained in:
parent
7efcfee28c
commit
a8a3cc82ed
@ -118,9 +118,23 @@ class QgsProcessingContext
|
|||||||
%End
|
%End
|
||||||
|
|
||||||
|
|
||||||
|
QString defaultEncoding() const;
|
||||||
|
%Docstring
|
||||||
|
Returns the default encoding to use for newly created files.
|
||||||
|
.. seealso:: setDefaultEncoding()
|
||||||
|
:rtype: str
|
||||||
|
%End
|
||||||
|
|
||||||
|
void setDefaultEncoding( const QString &encoding );
|
||||||
|
%Docstring
|
||||||
|
Sets the default ``encoding`` to use for newly created files.
|
||||||
|
.. seealso:: defaultEncoding()
|
||||||
|
%End
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QgsProcessingContext( const QgsProcessingContext &other );
|
QgsProcessingContext( const QgsProcessingContext &other );
|
||||||
};
|
};
|
||||||
|
|
||||||
QFlags<QgsProcessingContext::Flag> operator|(QgsProcessingContext::Flag f1, QFlags<QgsProcessingContext::Flag> f2);
|
QFlags<QgsProcessingContext::Flag> operator|(QgsProcessingContext::Flag f1, QFlags<QgsProcessingContext::Flag> f2);
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -122,6 +122,38 @@ class QgsProcessingUtils
|
|||||||
:rtype: list of QVariant
|
:rtype: list of QVariant
|
||||||
%End
|
%End
|
||||||
|
|
||||||
|
|
||||||
|
static void createFeatureSinkPython(
|
||||||
|
QgsFeatureSink **sink /Out,TransferBack/,
|
||||||
|
QString &destination /In,Out/,
|
||||||
|
const QString &encoding,
|
||||||
|
const QgsFields &fields,
|
||||||
|
QgsWkbTypes::Type geometryType,
|
||||||
|
const QgsCoordinateReferenceSystem &crs,
|
||||||
|
QgsProcessingContext &context,
|
||||||
|
QgsVectorLayer **outputLayer /Out/ ) /PyName=createFeatureSink/;
|
||||||
|
%Docstring
|
||||||
|
Creates a feature sink ready for adding features. The ``destination`` specifies a destination
|
||||||
|
URI for the resultant layer. It may be updated in place to reflect the actual destination
|
||||||
|
for the layer.
|
||||||
|
|
||||||
|
Sink parameters such as desired ``encoding``, ``fields``, ``geometryType`` and ``crs`` must be specified.
|
||||||
|
|
||||||
|
If the ``encoding`` is not specified, the default encoding from the ``context`` will be used.
|
||||||
|
|
||||||
|
If a layer is created for the feature sink, the layer will automatically be added to the ``context``'s
|
||||||
|
temporary layer store, and the ``outputLayer`` argument updated to point at this newly created layer.
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
|
||||||
|
this version of the createFeatureSink() function has an API designed around use from the
|
||||||
|
SIP bindings. c++ code should call the other createFeatureSink() version.
|
||||||
|
.. note::
|
||||||
|
|
||||||
|
available in Python bindings as createFeatureSink()
|
||||||
|
%End
|
||||||
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -85,6 +85,9 @@ def createContext():
|
|||||||
|
|
||||||
context.setInvalidGeometryCallback(raise_error)
|
context.setInvalidGeometryCallback(raise_error)
|
||||||
|
|
||||||
|
settings = QgsSettings()
|
||||||
|
context.setDefaultEncoding(settings.value("/Processing/encoding", "System"))
|
||||||
|
|
||||||
return context
|
return context
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -460,6 +460,7 @@ NOGEOMETRY_EXTENSIONS = [
|
|||||||
|
|
||||||
|
|
||||||
def createVectorWriter(destination, encoding, fields, geometryType, crs, context):
|
def createVectorWriter(destination, encoding, fields, geometryType, crs, context):
|
||||||
|
return QgsProcessingUtils.createFeatureSink(destination, encoding, fields, geometryType, crs, context)
|
||||||
layer = None
|
layer = None
|
||||||
sink = None
|
sink = None
|
||||||
|
|
||||||
|
|||||||
@ -142,6 +142,18 @@ class CORE_EXPORT QgsProcessingContext
|
|||||||
*/
|
*/
|
||||||
SIP_SKIP std::function< void( const QgsFeature & ) > invalidGeometryCallback() const { return mInvalidGeometryCallback; }
|
SIP_SKIP std::function< void( const QgsFeature & ) > invalidGeometryCallback() const { return mInvalidGeometryCallback; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the default encoding to use for newly created files.
|
||||||
|
* \see setDefaultEncoding()
|
||||||
|
*/
|
||||||
|
QString defaultEncoding() const { return mDefaultEncoding; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the default \a encoding to use for newly created files.
|
||||||
|
* \see defaultEncoding()
|
||||||
|
*/
|
||||||
|
void setDefaultEncoding( const QString &encoding ) { mDefaultEncoding = encoding; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
QgsProcessingContext::Flags mFlags = 0;
|
QgsProcessingContext::Flags mFlags = 0;
|
||||||
@ -151,11 +163,13 @@ class CORE_EXPORT QgsProcessingContext
|
|||||||
QgsExpressionContext mExpressionContext;
|
QgsExpressionContext mExpressionContext;
|
||||||
QgsFeatureRequest::InvalidGeometryCheck mInvalidGeometryCheck = QgsFeatureRequest::GeometryNoCheck;
|
QgsFeatureRequest::InvalidGeometryCheck mInvalidGeometryCheck = QgsFeatureRequest::GeometryNoCheck;
|
||||||
std::function< void( const QgsFeature & ) > mInvalidGeometryCallback;
|
std::function< void( const QgsFeature & ) > mInvalidGeometryCallback;
|
||||||
|
QString mDefaultEncoding;
|
||||||
|
|
||||||
#ifdef SIP_RUN
|
#ifdef SIP_RUN
|
||||||
QgsProcessingContext( const QgsProcessingContext &other );
|
QgsProcessingContext( const QgsProcessingContext &other );
|
||||||
#endif
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
Q_DECLARE_OPERATORS_FOR_FLAGS( QgsProcessingContext::Flags )
|
Q_DECLARE_OPERATORS_FOR_FLAGS( QgsProcessingContext::Flags )
|
||||||
|
|
||||||
#endif // QGSPROCESSINGPARAMETERS_H
|
#endif // QGSPROCESSINGPARAMETERS_H
|
||||||
|
|||||||
@ -19,6 +19,9 @@
|
|||||||
#include "qgsproject.h"
|
#include "qgsproject.h"
|
||||||
#include "qgssettings.h"
|
#include "qgssettings.h"
|
||||||
#include "qgsprocessingcontext.h"
|
#include "qgsprocessingcontext.h"
|
||||||
|
#include "qgsvectorlayerimport.h"
|
||||||
|
#include "qgsvectorfilewriter.h"
|
||||||
|
#include "qgsmemoryproviderutils.h"
|
||||||
|
|
||||||
QList<QgsRasterLayer *> QgsProcessingUtils::compatibleRasterLayers( QgsProject *project, bool sort )
|
QList<QgsRasterLayer *> QgsProcessingUtils::compatibleRasterLayers( QgsProject *project, bool sort )
|
||||||
{
|
{
|
||||||
@ -289,4 +292,116 @@ QList<QVariant> QgsProcessingUtils::uniqueValues( QgsVectorLayer *layer, int fie
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void parseDestinationString( QString &destination, QString &providerKey, QString &uri, QString &format, QMap<QString, QVariant> &options )
|
||||||
|
{
|
||||||
|
QRegularExpression splitRx( "^(.*?):(.*)$" );
|
||||||
|
QRegularExpressionMatch match = splitRx.match( destination );
|
||||||
|
if ( match.hasMatch() )
|
||||||
|
{
|
||||||
|
providerKey = match.captured( 1 );
|
||||||
|
if ( providerKey == QStringLiteral( "postgis" ) ) // older processing used "postgis" instead of "postgres"
|
||||||
|
{
|
||||||
|
providerKey = QStringLiteral( "postgres" );
|
||||||
|
}
|
||||||
|
uri = match.captured( 2 );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
providerKey = QStringLiteral( "ogr" );
|
||||||
|
QRegularExpression splitRx( "^(.*)\\.(.*?)$" );
|
||||||
|
QRegularExpressionMatch match = splitRx.match( destination );
|
||||||
|
QString extension;
|
||||||
|
if ( match.hasMatch() )
|
||||||
|
{
|
||||||
|
extension = match.captured( 2 );
|
||||||
|
format = QgsVectorFileWriter::driverForExtension( extension );
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( format.isEmpty() )
|
||||||
|
{
|
||||||
|
format = QStringLiteral( "ESRI Shapefile" );
|
||||||
|
destination = destination + QStringLiteral( ".shp" );
|
||||||
|
}
|
||||||
|
|
||||||
|
options.insert( QStringLiteral( "driverName" ), format );
|
||||||
|
uri = destination;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QgsFeatureSink *QgsProcessingUtils::createFeatureSink( QString &destination, const QString &encoding, const QgsFields &fields, QgsWkbTypes::Type geometryType, const QgsCoordinateReferenceSystem &crs, QgsProcessingContext &context, QgsVectorLayer *&outputLayer )
|
||||||
|
{
|
||||||
|
outputLayer = nullptr;
|
||||||
|
QgsVectorLayer *layer = nullptr;
|
||||||
|
|
||||||
|
QString destEncoding = encoding;
|
||||||
|
if ( destEncoding.isEmpty() )
|
||||||
|
{
|
||||||
|
// no destination encoding specified, use default
|
||||||
|
destEncoding = context.defaultEncoding().isEmpty() ? QStringLiteral( "system" ) : context.defaultEncoding();
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( destination.isEmpty() || destination.startsWith( QStringLiteral( "memory:" ) ) )
|
||||||
|
{
|
||||||
|
// memory provider cannot be used with QgsVectorLayerImport - so create layer manually
|
||||||
|
layer = QgsMemoryProviderUtils::createMemoryLayer( destination, fields, geometryType, crs );
|
||||||
|
if ( layer && layer->isValid() )
|
||||||
|
destination = layer->id();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
QMap<QString, QVariant> options;
|
||||||
|
options.insert( QStringLiteral( "fileEncoding" ), destEncoding );
|
||||||
|
|
||||||
|
QString providerKey;
|
||||||
|
QString uri;
|
||||||
|
QString format;
|
||||||
|
parseDestinationString( destination, providerKey, uri, format, options );
|
||||||
|
|
||||||
|
if ( providerKey == "ogr" )
|
||||||
|
{
|
||||||
|
// use QgsVectorFileWriter for OGR destinations instead of QgsVectorLayerImport, as that allows
|
||||||
|
// us to use any OGR format which supports feature addition
|
||||||
|
QString finalFileName;
|
||||||
|
QgsVectorFileWriter *writer = new QgsVectorFileWriter( destination, destEncoding, fields, geometryType, crs, format, QgsVectorFileWriter::defaultDatasetOptions( format ),
|
||||||
|
QgsVectorFileWriter::defaultLayerOptions( format ), &finalFileName );
|
||||||
|
destination = finalFileName;
|
||||||
|
return writer;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
//create empty layer
|
||||||
|
{
|
||||||
|
QgsVectorLayerImport import( uri, providerKey, fields, geometryType, crs, false, &options );
|
||||||
|
if ( import.hasError() )
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
layer = new QgsVectorLayer( uri, destination, providerKey );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( !layer )
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
if ( !layer->isValid() )
|
||||||
|
{
|
||||||
|
delete layer;
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
context.temporaryLayerStore()->addMapLayer( layer );
|
||||||
|
|
||||||
|
outputLayer = layer;
|
||||||
|
// this is a factory, so we need to return a proxy
|
||||||
|
return new QgsProxyFeatureSink( layer->dataProvider() );
|
||||||
|
}
|
||||||
|
|
||||||
|
void QgsProcessingUtils::createFeatureSinkPython( QgsFeatureSink **sink, QString &destination, const QString &encoding, const QgsFields &fields, QgsWkbTypes::Type geometryType, const QgsCoordinateReferenceSystem &crs, QgsProcessingContext &context, QgsVectorLayer **outputLayer )
|
||||||
|
{
|
||||||
|
QgsVectorLayer *layer = nullptr;
|
||||||
|
*sink = createFeatureSink( destination, encoding, fields, geometryType, crs, context, layer );
|
||||||
|
if ( outputLayer )
|
||||||
|
*outputLayer = layer;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -131,6 +131,58 @@ class CORE_EXPORT QgsProcessingUtils
|
|||||||
*/
|
*/
|
||||||
static QList< QVariant > uniqueValues( QgsVectorLayer *layer, int fieldIndex, const QgsProcessingContext &context );
|
static QList< QVariant > uniqueValues( QgsVectorLayer *layer, int fieldIndex, const QgsProcessingContext &context );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a feature sink ready for adding features. The \a destination specifies a destination
|
||||||
|
* URI for the resultant layer. It may be updated in place to reflect the actual destination
|
||||||
|
* for the layer.
|
||||||
|
*
|
||||||
|
* Sink parameters such as desired \a encoding, \a fields, \a geometryType and \a crs must be specified.
|
||||||
|
*
|
||||||
|
* If the \a encoding is not specified, the default encoding from the \a context will be used.
|
||||||
|
*
|
||||||
|
* If a layer is created for the feature sink, the layer will automatically be added to the \a context's
|
||||||
|
* temporary layer store, and the \a outputLayer argument updated to point at this newly created layer.
|
||||||
|
*
|
||||||
|
* The caller takes responsibility for deleting the returned sink.
|
||||||
|
*/
|
||||||
|
#ifndef SIP_RUN
|
||||||
|
static QgsFeatureSink *createFeatureSink(
|
||||||
|
QString &destination,
|
||||||
|
const QString &encoding,
|
||||||
|
const QgsFields &fields,
|
||||||
|
QgsWkbTypes::Type geometryType,
|
||||||
|
const QgsCoordinateReferenceSystem &crs,
|
||||||
|
QgsProcessingContext &context,
|
||||||
|
QgsVectorLayer *&outputLayer ) SIP_FACTORY;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a feature sink ready for adding features. The \a destination specifies a destination
|
||||||
|
* URI for the resultant layer. It may be updated in place to reflect the actual destination
|
||||||
|
* for the layer.
|
||||||
|
*
|
||||||
|
* Sink parameters such as desired \a encoding, \a fields, \a geometryType and \a crs must be specified.
|
||||||
|
*
|
||||||
|
* If the \a encoding is not specified, the default encoding from the \a context will be used.
|
||||||
|
*
|
||||||
|
* If a layer is created for the feature sink, the layer will automatically be added to the \a context's
|
||||||
|
* temporary layer store, and the \a outputLayer argument updated to point at this newly created layer.
|
||||||
|
*
|
||||||
|
* \note this version of the createFeatureSink() function has an API designed around use from the
|
||||||
|
* SIP bindings. c++ code should call the other createFeatureSink() version.
|
||||||
|
* \note available in Python bindings as createFeatureSink()
|
||||||
|
*/
|
||||||
|
static void createFeatureSinkPython(
|
||||||
|
QgsFeatureSink **sink SIP_OUT SIP_TRANSFERBACK,
|
||||||
|
QString &destination SIP_INOUT,
|
||||||
|
const QString &encoding,
|
||||||
|
const QgsFields &fields,
|
||||||
|
QgsWkbTypes::Type geometryType,
|
||||||
|
const QgsCoordinateReferenceSystem &crs,
|
||||||
|
QgsProcessingContext &context,
|
||||||
|
QgsVectorLayer **outputLayer SIP_OUT ) SIP_PYNAME( createFeatureSink );
|
||||||
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
static bool canUseLayer( const QgsRasterLayer *layer );
|
static bool canUseLayer( const QgsRasterLayer *layer );
|
||||||
|
|||||||
@ -28,6 +28,7 @@
|
|||||||
#include "qgsproject.h"
|
#include "qgsproject.h"
|
||||||
#include "qgspointv2.h"
|
#include "qgspointv2.h"
|
||||||
#include "qgsgeometry.h"
|
#include "qgsgeometry.h"
|
||||||
|
#include "qgsvectorfilewriter.h"
|
||||||
|
|
||||||
class DummyAlgorithm : public QgsProcessingAlgorithm
|
class DummyAlgorithm : public QgsProcessingAlgorithm
|
||||||
{
|
{
|
||||||
@ -104,6 +105,7 @@ class TestQgsProcessing: public QObject
|
|||||||
void removeProvider();
|
void removeProvider();
|
||||||
void compatibleLayers();
|
void compatibleLayers();
|
||||||
void normalizeLayerSource();
|
void normalizeLayerSource();
|
||||||
|
void context();
|
||||||
void mapLayers();
|
void mapLayers();
|
||||||
void mapLayerFromStore();
|
void mapLayerFromStore();
|
||||||
void mapLayerFromString();
|
void mapLayerFromString();
|
||||||
@ -111,6 +113,7 @@ class TestQgsProcessing: public QObject
|
|||||||
void features();
|
void features();
|
||||||
void uniqueValues();
|
void uniqueValues();
|
||||||
void createIndex();
|
void createIndex();
|
||||||
|
void createFeatureSink();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
@ -124,6 +127,9 @@ void TestQgsProcessing::initTestCase()
|
|||||||
|
|
||||||
void TestQgsProcessing::cleanupTestCase()
|
void TestQgsProcessing::cleanupTestCase()
|
||||||
{
|
{
|
||||||
|
QFile::remove( QDir::tempPath() + "/create_feature_sink.tab" );
|
||||||
|
QgsVectorFileWriter::deleteShapeFile( QDir::tempPath() + "/create_feature_sink2.shp" );
|
||||||
|
|
||||||
QgsApplication::exitQgis();
|
QgsApplication::exitQgis();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -334,6 +340,27 @@ void TestQgsProcessing::normalizeLayerSource()
|
|||||||
QCOMPARE( QgsProcessingUtils::normalizeLayerSource( "data\\layers \"new\"\\test.shp" ), QString( "data/layers 'new'/test.shp" ) );
|
QCOMPARE( QgsProcessingUtils::normalizeLayerSource( "data\\layers \"new\"\\test.shp" ), QString( "data/layers 'new'/test.shp" ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void TestQgsProcessing::context()
|
||||||
|
{
|
||||||
|
QgsProcessingContext context;
|
||||||
|
|
||||||
|
// simple tests for getters/setters
|
||||||
|
context.setDefaultEncoding( "my_enc" );
|
||||||
|
QCOMPARE( context.defaultEncoding(), QStringLiteral( "my_enc" ) );
|
||||||
|
|
||||||
|
context.setFlags( QgsProcessingContext::UseSelectionIfPresent );
|
||||||
|
QCOMPARE( context.flags(), QgsProcessingContext::UseSelectionIfPresent );
|
||||||
|
context.setFlags( QgsProcessingContext::Flags( 0 ) );
|
||||||
|
QCOMPARE( context.flags(), QgsProcessingContext::Flags( 0 ) );
|
||||||
|
|
||||||
|
QgsProject p;
|
||||||
|
context.setProject( &p );
|
||||||
|
QCOMPARE( context.project(), &p );
|
||||||
|
|
||||||
|
context.setInvalidGeometryCheck( QgsFeatureRequest::GeometrySkipInvalid );
|
||||||
|
QCOMPARE( context.invalidGeometryCheck(), QgsFeatureRequest::GeometrySkipInvalid );
|
||||||
|
}
|
||||||
|
|
||||||
void TestQgsProcessing::mapLayers()
|
void TestQgsProcessing::mapLayers()
|
||||||
{
|
{
|
||||||
QString testDataDir = QStringLiteral( TEST_DATA_DIR ) + '/'; //defined in CmakeLists.txt
|
QString testDataDir = QStringLiteral( TEST_DATA_DIR ) + '/'; //defined in CmakeLists.txt
|
||||||
@ -711,5 +738,114 @@ void TestQgsProcessing::createIndex()
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void TestQgsProcessing::createFeatureSink()
|
||||||
|
{
|
||||||
|
QgsProcessingContext context;
|
||||||
|
|
||||||
|
// empty destination
|
||||||
|
QString destination;
|
||||||
|
destination = QString();
|
||||||
|
QgsVectorLayer *layer = nullptr;
|
||||||
|
|
||||||
|
// should create a memory layer
|
||||||
|
QgsFeatureSink *sink = QgsProcessingUtils::createFeatureSink( destination, QString(), QgsFields(), QgsWkbTypes::Point, QgsCoordinateReferenceSystem(), context, layer );
|
||||||
|
QVERIFY( sink );
|
||||||
|
QVERIFY( layer );
|
||||||
|
QCOMPARE( static_cast< QgsProxyFeatureSink *>( sink )->destinationSink(), layer->dataProvider() );
|
||||||
|
QCOMPARE( layer->dataProvider()->name(), QStringLiteral( "memory" ) );
|
||||||
|
QCOMPARE( destination, layer->id() );
|
||||||
|
QCOMPARE( context.temporaryLayerStore()->mapLayer( layer->id() ), layer ); // layer should be in store
|
||||||
|
QgsFeature f;
|
||||||
|
QCOMPARE( layer->featureCount(), 0L );
|
||||||
|
QVERIFY( sink->addFeature( f ) );
|
||||||
|
QCOMPARE( layer->featureCount(), 1L );
|
||||||
|
context.temporaryLayerStore()->removeAllMapLayers();
|
||||||
|
layer = nullptr;
|
||||||
|
delete sink;
|
||||||
|
|
||||||
|
// specific memory layer output
|
||||||
|
destination = QStringLiteral( "memory:mylayer" );
|
||||||
|
sink = QgsProcessingUtils::createFeatureSink( destination, QString(), QgsFields(), QgsWkbTypes::Point, QgsCoordinateReferenceSystem(), context, layer );
|
||||||
|
QVERIFY( sink );
|
||||||
|
QVERIFY( layer );
|
||||||
|
QCOMPARE( static_cast< QgsProxyFeatureSink *>( sink )->destinationSink(), layer->dataProvider() );
|
||||||
|
QCOMPARE( layer->dataProvider()->name(), QStringLiteral( "memory" ) );
|
||||||
|
QCOMPARE( layer->name(), QStringLiteral( "memory:mylayer" ) );
|
||||||
|
QCOMPARE( destination, layer->id() );
|
||||||
|
QCOMPARE( context.temporaryLayerStore()->mapLayer( layer->id() ), layer ); // layer should be in store
|
||||||
|
QCOMPARE( layer->featureCount(), 0L );
|
||||||
|
QVERIFY( sink->addFeature( f ) );
|
||||||
|
QCOMPARE( layer->featureCount(), 1L );
|
||||||
|
context.temporaryLayerStore()->removeAllMapLayers();
|
||||||
|
layer = nullptr;
|
||||||
|
delete sink;
|
||||||
|
|
||||||
|
// memory layer parameters
|
||||||
|
destination = QStringLiteral( "memory:mylayer" );
|
||||||
|
QgsFields fields;
|
||||||
|
fields.append( QgsField( QStringLiteral( "my_field" ), QVariant::String, QString(), 100 ) );
|
||||||
|
sink = QgsProcessingUtils::createFeatureSink( destination, QString(), fields, QgsWkbTypes::PointZM, QgsCoordinateReferenceSystem::fromEpsgId( 3111 ), context, layer );
|
||||||
|
QVERIFY( sink );
|
||||||
|
QVERIFY( layer );
|
||||||
|
QCOMPARE( static_cast< QgsProxyFeatureSink *>( sink )->destinationSink(), layer->dataProvider() );
|
||||||
|
QCOMPARE( layer->dataProvider()->name(), QStringLiteral( "memory" ) );
|
||||||
|
QCOMPARE( layer->name(), QStringLiteral( "memory:mylayer" ) );
|
||||||
|
QCOMPARE( layer->wkbType(), QgsWkbTypes::PointZM );
|
||||||
|
QCOMPARE( layer->crs().authid(), QStringLiteral( "EPSG:3111" ) );
|
||||||
|
QCOMPARE( layer->fields().size(), 1 );
|
||||||
|
QCOMPARE( layer->fields().at( 0 ).name(), QStringLiteral( "my_field" ) );
|
||||||
|
QCOMPARE( layer->fields().at( 0 ).type(), QVariant::String );
|
||||||
|
QCOMPARE( destination, layer->id() );
|
||||||
|
QCOMPARE( context.temporaryLayerStore()->mapLayer( layer->id() ), layer ); // layer should be in store
|
||||||
|
QCOMPARE( layer->featureCount(), 0L );
|
||||||
|
QVERIFY( sink->addFeature( f ) );
|
||||||
|
QCOMPARE( layer->featureCount(), 1L );
|
||||||
|
context.temporaryLayerStore()->removeAllMapLayers();
|
||||||
|
layer = nullptr;
|
||||||
|
delete sink;
|
||||||
|
|
||||||
|
// non memory layer output
|
||||||
|
destination = QDir::tempPath() + "/create_feature_sink.tab";
|
||||||
|
QString prevDest = destination;
|
||||||
|
sink = QgsProcessingUtils::createFeatureSink( destination, QString(), fields, QgsWkbTypes::Polygon, QgsCoordinateReferenceSystem::fromEpsgId( 3111 ), context, layer );
|
||||||
|
QVERIFY( sink );
|
||||||
|
f = QgsFeature( fields );
|
||||||
|
f.setGeometry( QgsGeometry::fromWkt( QStringLiteral( "Polygon((0 0, 0 1, 1 1, 1 0, 0 0 ))" ) ) );
|
||||||
|
f.setAttributes( QgsAttributes() << "val" );
|
||||||
|
QVERIFY( sink->addFeature( f ) );
|
||||||
|
QVERIFY( !layer );
|
||||||
|
QCOMPARE( destination, prevDest );
|
||||||
|
delete sink;
|
||||||
|
layer = new QgsVectorLayer( destination, "test_layer", "ogr" );
|
||||||
|
QVERIFY( layer->isValid() );
|
||||||
|
QCOMPARE( layer->crs().authid(), QStringLiteral( "EPSG:3111" ) );
|
||||||
|
QCOMPARE( layer->fields().size(), 1 );
|
||||||
|
QCOMPARE( layer->fields().at( 0 ).name(), QStringLiteral( "my_field" ) );
|
||||||
|
QCOMPARE( layer->fields().at( 0 ).type(), QVariant::String );
|
||||||
|
QCOMPARE( layer->featureCount(), 1L );
|
||||||
|
delete layer;
|
||||||
|
layer = nullptr;
|
||||||
|
|
||||||
|
// no extension, should default to shp
|
||||||
|
destination = QDir::tempPath() + "/create_feature_sink2";
|
||||||
|
prevDest = QDir::tempPath() + "/create_feature_sink2.shp";
|
||||||
|
sink = QgsProcessingUtils::createFeatureSink( destination, QString(), fields, QgsWkbTypes::Point25D, QgsCoordinateReferenceSystem::fromEpsgId( 3111 ), context, layer );
|
||||||
|
QVERIFY( sink );
|
||||||
|
f.setGeometry( QgsGeometry::fromWkt( QStringLiteral( "PointZ(1 2 3)" ) ) );
|
||||||
|
QVERIFY( sink->addFeature( f ) );
|
||||||
|
QVERIFY( !layer );
|
||||||
|
QCOMPARE( destination, prevDest );
|
||||||
|
delete sink;
|
||||||
|
layer = new QgsVectorLayer( destination, "test_layer", "ogr" );
|
||||||
|
QCOMPARE( layer->wkbType(), QgsWkbTypes::Point25D );
|
||||||
|
QCOMPARE( layer->crs().authid(), QStringLiteral( "EPSG:3111" ) );
|
||||||
|
QCOMPARE( layer->fields().size(), 1 );
|
||||||
|
QCOMPARE( layer->fields().at( 0 ).name(), QStringLiteral( "my_field" ) );
|
||||||
|
QCOMPARE( layer->fields().at( 0 ).type(), QVariant::String );
|
||||||
|
QCOMPARE( layer->featureCount(), 1L );
|
||||||
|
delete layer;
|
||||||
|
layer = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
QGSTEST_MAIN( TestQgsProcessing )
|
QGSTEST_MAIN( TestQgsProcessing )
|
||||||
#include "testqgsprocessing.moc"
|
#include "testqgsprocessing.moc"
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user