Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
[FEATURE][API] Add new QgsFeatureSink subclass QgsRemappingProxyFeatu…
…reSink

This sink allows for transformation of incoming features to match the
requirements of storing in an existing destination layer, e.g. by reprojecting
the features to the destination's CRS, by coercing geometries to the
format required by the destination sink, and by mapping field values from
the source to the destination.
  • Loading branch information
nyalldawson committed Apr 7, 2020
1 parent 78c86ef commit 93f714d
Show file tree
Hide file tree
Showing 7 changed files with 559 additions and 2 deletions.
170 changes: 170 additions & 0 deletions python/core/auto_generated/qgsremappingproxyfeaturesink.sip.in
@@ -0,0 +1,170 @@
/************************************************************************
* This file has been generated automatically from *
* *
* src/core/qgsremappingproxyfeaturesink.h *
* *
* Do not edit manually ! Edit header and run scripts/sipify.pl again *
************************************************************************/




class QgsRemappingSinkDefinition
{
%Docstring
Defines the parameters used to remap features when creating a QgsRemappingProxyFeatureSink.

The definition includes parameters required to correctly map incoming features to the structure
of the destination sink, e.g. information about how to create output field values and how to transform
geometries to match the destination CRS.

.. versionadded:: 3.14
%End

%TypeHeaderCode
#include "qgsremappingproxyfeaturesink.h"
%End
public:

QMap< QString, QgsProperty > fieldMap() const;
%Docstring
Returns the field mapping, which defines how to map the values from incoming features to destination
field values.

Field values are mapped using a QgsProperty source object, which allows either direct field value to field value
mapping or use of QgsExpression expressions to transform values to the destination field.

.. seealso:: :py:func:`setFieldMap`

.. seealso:: :py:func:`addMappedField`
%End

void setFieldMap( const QMap< QString, QgsProperty > &map );
%Docstring
Sets the field mapping, which defines how to map the values from incoming features to destination
field values.

Field values are mapped using a QgsProperty source object, which allows either direct field value to field value
mapping or use of QgsExpression expressions to transform values to the destination field.

.. seealso:: :py:func:`fieldMap`

.. seealso:: :py:func:`addMappedField`
%End

void addMappedField( const QString &destinationField, const QgsProperty &property );
%Docstring
Adds a mapping for a destination field.

Field values are mapped using a QgsProperty source object, which allows either direct field value to field value
mapping or use of QgsExpression expressions to transform values to the destination field.

.. seealso:: :py:func:`setFieldMap`

.. seealso:: :py:func:`fieldMap`
%End

QgsCoordinateTransform transform() const;
%Docstring
Returns the transform used for reprojecting incoming features to the sink's destination CRS.

.. seealso:: :py:func:`setTransform`
%End

void setTransform( const QgsCoordinateTransform &transform );
%Docstring
Sets the ``transform`` used for reprojecting incoming features to the sink's destination CRS.

.. seealso:: :py:func:`transform`
%End

QgsWkbTypes::Type destinationWkbType() const;
%Docstring
Returns the WKB geometry type for the destination.

.. seealso:: :py:func:`setDestinationWkbType`
%End

void setDestinationWkbType( QgsWkbTypes::Type type );
%Docstring
Sets the WKB geometry ``type`` for the destination.

.. seealso:: :py:func:`setDestinationWkbType`
%End

QgsFields destinationFields() const;
%Docstring
Returns the fields for the destination sink.

.. seealso:: :py:func:`setDestinationFields`
%End

void setDestinationFields( const QgsFields &fields );
%Docstring
Sets the ``fields`` for the destination sink.

.. seealso:: :py:func:`destinationFields`
%End

};


class QgsRemappingProxyFeatureSink : QgsFeatureSink
{
%Docstring
A QgsFeatureSink which proxies incoming features to a destination feature sink, after applying
transformations and field value mappings.

This sink allows for transformation of incoming features to match the requirements of storing
in an existing destination layer, e.g. by reprojecting the features to the destination's CRS
and by coercing geometries to the format required by the destination sink.

.. versionadded:: 3.14
%End

%TypeHeaderCode
#include "qgsremappingproxyfeaturesink.h"
%End
public:

QgsRemappingProxyFeatureSink( const QgsRemappingSinkDefinition &mappingDefinition, QgsFeatureSink *sink );
%Docstring
Constructor for QgsRemappingProxyFeatureSink, using the specified ``mappingDefinition``
to manipulate features before sending them to the destination ``sink``.
%End

void setExpressionContext( const QgsExpressionContext &context );
%Docstring
Sets the expression ``context`` to use when evaluating mapped field values.
%End

QgsFeatureList remapFeature( const QgsFeature &feature ) const;
%Docstring
Remaps a ``feature`` to a set of features compatible with the destination sink.
%End

virtual bool addFeature( QgsFeature &feature, QgsFeatureSink::Flags flags = 0 );

virtual bool addFeatures( QgsFeatureList &features, QgsFeatureSink::Flags flags = 0 );

virtual bool addFeatures( QgsFeatureIterator &iterator, QgsFeatureSink::Flags flags = 0 );


QgsFeatureSink *destinationSink();
%Docstring
Returns the destination QgsFeatureSink which the proxy will forward features to.
%End

};





/************************************************************************
* This file has been generated automatically from *
* *
* src/core/qgsremappingproxyfeaturesink.h *
* *
* Do not edit manually ! Edit header and run scripts/sipify.pl again *
************************************************************************/
1 change: 1 addition & 0 deletions python/core/core_auto.sip
Expand Up @@ -175,6 +175,7 @@
%Include auto_generated/qgsreadwritelocker.sip
%Include auto_generated/qgsrelation.sip
%Include auto_generated/qgsrelationcontext.sip
%Include auto_generated/qgsremappingproxyfeaturesink.sip
%Include auto_generated/qgsrelationmanager.sip
%Include auto_generated/qgsrenderchecker.sip
%Include auto_generated/qgsrendercontext.sip
Expand Down
2 changes: 2 additions & 0 deletions src/core/CMakeLists.txt
Expand Up @@ -369,6 +369,7 @@ SET(QGIS_CORE_SRCS
qgsrelationcontext.cpp
qgsweakrelation.cpp
qgsrelationmanager.cpp
qgsremappingproxyfeaturesink.cpp
qgsrenderchecker.cpp
qgsrendercontext.cpp
qgsrunprocess.cpp
Expand Down Expand Up @@ -910,6 +911,7 @@ SET(QGIS_CORE_HDRS
qgsreadwritelocker.h
qgsrelation.h
qgsrelationcontext.h
qgsremappingproxyfeaturesink.h
qgsweakrelation.h
qgsrelationmanager.h
qgsrenderchecker.h
Expand Down
10 changes: 9 additions & 1 deletion src/core/geometry/qgsgeometry.cpp
Expand Up @@ -1313,12 +1313,20 @@ json QgsGeometry::asJsonObject( int precision ) const
QVector<QgsGeometry> QgsGeometry::coerceToType( const QgsWkbTypes::Type type ) const
{
QVector< QgsGeometry > res;
if ( wkbType() == type )
if ( isNull() )
return res;

if ( wkbType() == type || type == QgsWkbTypes::Unknown )
{
res << *this;
return res;
}

if ( type == QgsWkbTypes::NoGeometry )
{
return res;
}

QgsGeometry newGeom = *this;

// Curved -> straight
Expand Down
119 changes: 119 additions & 0 deletions src/core/qgsremappingproxyfeaturesink.cpp
@@ -0,0 +1,119 @@
/***************************************************************************
qgsremappingproxyfeaturesink.cpp
----------------------
begin : April 2020
copyright : (C) 2020 by Nyall Dawson
email : nyall dot dawson at gmail dot com
***************************************************************************/

/***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/

#include "qgsremappingproxyfeaturesink.h"
#include "qgslogger.h"

QgsRemappingProxyFeatureSink::QgsRemappingProxyFeatureSink( const QgsRemappingSinkDefinition &mappingDefinition, QgsFeatureSink *sink )
: QgsFeatureSink()
, mDefinition( mappingDefinition )
, mSink( sink )
{}

void QgsRemappingProxyFeatureSink::setExpressionContext( const QgsExpressionContext &context )
{
mContext = context;
}

QgsFeatureList QgsRemappingProxyFeatureSink::remapFeature( const QgsFeature &feature ) const
{
QgsFeatureList res;

mContext.setFeature( feature );

// remap fields first
QgsFeature f;
f.setFields( mDefinition.destinationFields(), true );
QgsAttributes attributes;
const QMap< QString, QgsProperty > fieldMap = mDefinition.fieldMap();
for ( const QgsField &field : mDefinition.destinationFields() )
{
if ( fieldMap.contains( field.name() ) )
{
attributes.append( fieldMap.value( field.name() ).value( mContext ) );
}
else
{
attributes.append( QVariant() );
}
}
f.setAttributes( attributes );

// make geometries compatible, and reproject if necessary
if ( feature.hasGeometry() )
{
const QVector< QgsGeometry > geometries = feature.geometry().coerceToType( mDefinition.destinationWkbType() );
if ( !geometries.isEmpty() )
{
res.reserve( geometries.size() );
for ( const QgsGeometry &geometry : geometries )
{
QgsFeature featurePart = f;

QgsGeometry reproject = geometry;
try
{
reproject.transform( mDefinition.transform() );
featurePart.setGeometry( reproject );
}
catch ( QgsCsException & )
{
QgsLogger::warning( QObject::tr( "Error reprojecting feature geometry" ) );
featurePart.clearGeometry();
}
res << featurePart;
}
}
else
{
f.clearGeometry();
res << f;
}
}
else
{
res << f;
}
return res;
}

bool QgsRemappingProxyFeatureSink::addFeature( QgsFeature &feature, QgsFeatureSink::Flags flags )
{
QgsFeatureList features = remapFeature( feature );
return mSink->addFeatures( features, flags );
}

bool QgsRemappingProxyFeatureSink::addFeatures( QgsFeatureList &features, QgsFeatureSink::Flags flags )
{
bool res = true;
for ( QgsFeature &f : features )
{
res = addFeature( f, flags ) && res;
}
return res;
}

bool QgsRemappingProxyFeatureSink::addFeatures( QgsFeatureIterator &iterator, QgsFeatureSink::Flags flags )
{
QgsFeature f;
bool res = true;
while ( iterator.nextFeature( f ) )
{
res = addFeature( f, flags ) && res;
}
return res;
}

0 comments on commit 93f714d

Please sign in to comment.