Skip to content

Commit 4549ee5

Browse files
elpasonyalldawson
authored andcommittedSep 14, 2018
In-place moved check logic into QgsAlgorithm
+ new tests + fixed fixer function + drop z/m
1 parent 11aaf90 commit 4549ee5

18 files changed

+244
-72
lines changed
 

‎python/core/auto_generated/processing/qgsprocessingalgorithm.sip.in

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -810,6 +810,16 @@ should correspond to the invalid source parameter name.
810810
.. seealso:: :py:func:`invalidRasterError`
811811

812812
.. versionadded:: 3.2
813+
%End
814+
815+
virtual bool supportInPlaceEdit( const QgsVectorLayer *layer ) const;
816+
%Docstring
817+
Checks whether this algorithm supports in-place editing on the given ``layer``
818+
Default implementation returns false.
819+
820+
:return: true if the algorithm supports in-place editing
821+
822+
.. versionadded:: 3.4
813823
%End
814824

815825
private:
@@ -964,6 +974,17 @@ called from a subclasses' processFeature() implementation.
964974
%Docstring
965975
Returns the feature request used for fetching features to process from the
966976
source layer. The default implementation requests all attributes and geometry.
977+
%End
978+
979+
virtual bool supportInPlaceEdit( const QgsVectorLayer *layer ) const;
980+
%Docstring
981+
Checks whether this algorithm supports in-place editing on the given ``layer``
982+
Default implementation for feature based algorithms run some basic compatibility
983+
checks based on the geometry type of the layer.
984+
985+
:return: true if the algorithm supports in-place editing
986+
987+
.. versionadded:: 3.4
967988
%End
968989

969990
};

‎python/gui/auto_generated/processing/qgsprocessingtoolboxmodel.sip.in

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -418,7 +418,7 @@ Returns any filters that affect how toolbox content is filtered.
418418
.. seealso:: :py:func:`setFilters`
419419
%End
420420

421-
void setInPlaceLayerType( QgsWkbTypes::GeometryType type );
421+
void setInPlaceLayer( QgsVectorLayer *layer );
422422

423423
void setFilterString( const QString &filter );
424424
%Docstring

‎python/gui/auto_generated/processing/qgsprocessingtoolboxtreeview.sip.in

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -74,10 +74,11 @@ Sets ``filters`` controlling the view's contents.
7474
%End
7575

7676

77-
void setInPlaceLayerType( QgsWkbTypes::GeometryType type );
77+
void setInPlaceLayer( QgsVectorLayer *layer );
7878
%Docstring
7979
Sets geometry \type for the in-place algorithms
80-
@param type
80+
81+
:param layer: the vector layer for in-place algorithm filter
8182
%End
8283

8384
public slots:

‎python/plugins/processing/gui/AlgorithmDialog.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,11 @@ def getParameterValues(self):
105105
if param.flags() & QgsProcessingParameterDefinition.FlagHidden:
106106
continue
107107
if not param.isDestination():
108+
109+
if self.in_place and param.name() == 'INPUT':
110+
parameters[param.name()] = iface.activeLayer()
111+
continue
112+
108113
try:
109114
wrapper = self.mainWidget().wrappers[param.name()]
110115
except KeyError:
@@ -124,9 +129,6 @@ def getParameterValues(self):
124129

125130
value = wrapper.parameterValue()
126131
parameters[param.name()] = value
127-
if self.in_place and param.name() == 'INPUT':
128-
parameters[param.name()] = iface.activeLayer()
129-
continue
130132

131133
wrapper = self.mainWidget().wrappers[param.name()]
132134
value = None

‎python/plugins/processing/gui/AlgorithmExecutor.py

Lines changed: 57 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,8 @@
3939
QgsFeatureRequest,
4040
QgsFeature,
4141
QgsExpression,
42-
QgsWkbTypes)
42+
QgsWkbTypes,
43+
QgsGeometry)
4344
from processing.gui.Postprocessing import handleAlgorithmResults
4445
from processing.tools import dataobjects
4546
from qgis.utils import iface
@@ -86,27 +87,53 @@ def make_feature_compatible(new_features, input_layer):
8687

8788
result_features = []
8889
for new_f in new_features:
89-
if (new_f.geometry().wkbType() != input_layer.wkbType() and
90-
QgsWkbTypes.isMultiType(input_layer.wkbType()) and not
91-
new_f.geometry().isMultipart()):
92-
new_geom = new_f.geometry()
93-
new_geom.convertToMultiType()
94-
new_f.setGeometry(new_geom)
95-
if len(new_f.fields()) > len(input_layer.fields()):
90+
if new_f.geometry().wkbType() != input_layer.wkbType():
91+
# Single -> Multi
92+
if (QgsWkbTypes.isMultiType(input_layer.wkbType()) and not
93+
new_f.geometry().isMultipart()):
94+
new_geom = new_f.geometry()
95+
new_geom.convertToMultiType()
96+
new_f.setGeometry(new_geom)
97+
# Drop Z/M
98+
if ((QgsWkbTypes.hasZ(new_f.geometry().wkbType()) and not QgsWkbTypes.hasZ(input_layer.wkbType())) or
99+
(QgsWkbTypes.hasM(new_f.geometry().wkbType()) and not QgsWkbTypes.hasM(input_layer.wkbType()))):
100+
# FIXME: there must be a better way!!!
101+
if new_f.geometry().type() == QgsWkbTypes.PointGeometry:
102+
if new_f.geometry().isMultipart():
103+
new_geom = QgsGeometry.fromWkt(new_f.geometry().asMultiPoint().asWkt())
104+
else:
105+
new_geom = QgsGeometry.fromWkt(new_f.geometry().asPoint().asWkt())
106+
elif new_f.geometry().type() == QgsWkbTypes.PolygonGeometry:
107+
if new_f.geometry().isMultipart():
108+
new_geom = QgsGeometry.fromWkt(new_f.geometry().asMultiPolygon().asWkt())
109+
else:
110+
new_geom = QgsGeometry.fromWkt(new_f.geometry().asPolygon().asWkt())
111+
elif new_f.geometry().type() == QgsWkbTypes.LineGeometry: # Linestring
112+
if new_f.geometry().isMultipart():
113+
new_geom = QgsGeometry.fromWkt(new_f.geometry().asPolyline().asWkt())
114+
else:
115+
new_geom = QgsGeometry.fromWkt(new_f.geometry().asMultiPolyline().asWkt())
116+
else:
117+
new_geom = QgsGeometry()
118+
new_f.setGeometry(new_geom)
119+
if len(new_f.attributes()) > len(input_layer.fields()):
96120
f = QgsFeature(input_layer.fields())
97121
f.setGeometry(new_f.geometry())
98122
f.setAttributes(new_f.attributes()[:len(input_layer.fields())])
123+
new_f = f
99124
result_features.append(new_f)
100125
return result_features
101126

102127

103-
def execute_in_place_run(alg, parameters, context=None, feedback=None):
128+
def execute_in_place_run(alg, active_layer, parameters, context=None, feedback=None, raise_exceptions=False):
104129
"""Executes an algorithm modifying features in-place in the input layer.
105130
106131
The input layer must be editable or an exception is raised.
107132
108133
:param alg: algorithm to run
109134
:type alg: QgsProcessingAlgorithm
135+
:param active_layer: the editable layer
136+
:type active_layer: QgsVectoLayer
110137
:param parameters: parameters of the algorithm
111138
:type parameters: dict
112139
:param context: context, defaults to None
@@ -123,16 +150,11 @@ def execute_in_place_run(alg, parameters, context=None, feedback=None):
123150
if context is None:
124151
context = dataobjects.createContext(feedback)
125152

126-
# It would be nicer to get the layer from INPUT with
127-
# alg.parameterAsVectorLayer(parameters, 'INPUT', context)
128-
# but it does not work.
129-
active_layer_id, ok = parameters['INPUT'].source.value(context.expressionContext())
130-
if ok:
131-
active_layer = QgsProject.instance().mapLayer(active_layer_id)
132-
if active_layer is None or not active_layer.isEditable():
133-
raise QgsProcessingException(tr("Layer is not editable or layer with id '%s' could not be found in the current project.") % active_layer_id)
134-
else:
135-
return False, {}
153+
if active_layer is None or not active_layer.isEditable():
154+
raise QgsProcessingException(tr("Layer is not editable or layer is None."))
155+
156+
if not alg.supportInPlaceEdit(active_layer):
157+
raise QgsProcessingException(tr("Selected algorithm and parameter configuration are not compatible with in-place modifications."))
136158

137159
parameters['OUTPUT'] = 'memory:'
138160

@@ -147,7 +169,13 @@ def execute_in_place_run(alg, parameters, context=None, feedback=None):
147169

148170
# Checks whether the algorithm has a processFeature method
149171
if hasattr(alg, 'processFeature'): # in-place feature editing
172+
# Make a clone or it will crash the second time the dialog
173+
# is opened and run
174+
alg = alg.create()
150175
alg.prepare(parameters, context, feedback)
176+
# Check again for compatibility after prepare
177+
if not alg.supportInPlaceEdit(active_layer):
178+
raise QgsProcessingException(tr("Selected algorithm and parameter configuration are not compatible with in-place modifications."))
151179
field_idxs = range(len(active_layer.fields()))
152180
feature_iterator = active_layer.getFeatures(QgsFeatureRequest(active_layer.selectedFeatureIds())) if parameters['INPUT'].selectedFeaturesOnly else active_layer.getFeatures()
153181
for f in feature_iterator:
@@ -166,7 +194,8 @@ def execute_in_place_run(alg, parameters, context=None, feedback=None):
166194
active_layer.deleteFeature(f.id())
167195
# Get the new ids
168196
old_ids = set([f.id() for f in active_layer.getFeatures(req)])
169-
active_layer.addFeatures(new_features)
197+
if not active_layer.addFeatures(new_features):
198+
raise QgsProcessingException(tr("Error adding processed features back into the layer."))
170199
new_ids = set([f.id() for f in active_layer.getFeatures(req)])
171200
new_feature_ids += list(new_ids - old_ids)
172201

@@ -188,21 +217,24 @@ def execute_in_place_run(alg, parameters, context=None, feedback=None):
188217
new_ids = set([f.id() for f in active_layer.getFeatures(req)])
189218
new_feature_ids += list(new_ids - old_ids)
190219

220+
active_layer.endEditCommand()
221+
191222
if ok and new_feature_ids:
192223
active_layer.selectByIds(new_feature_ids)
193224
elif not ok:
194225
active_layer.rollback()
195226

196-
active_layer.endEditCommand()
197-
198227
return ok, results
199228

200229
except QgsProcessingException as e:
230+
active_layer.endEditCommand()
231+
if raise_exceptions:
232+
raise e
201233
QgsMessageLog.logMessage(str(sys.exc_info()[0]), 'Processing', Qgis.Critical)
202234
if feedback is not None:
203-
feedback.reportError(e.msg)
235+
feedback.reportError(getattr(e, 'msg', str(e)))
204236

205-
return False, {}
237+
return False, {}
206238

207239

208240
def execute_in_place(alg, parameters, context=None, feedback=None):
@@ -224,7 +256,7 @@ def execute_in_place(alg, parameters, context=None, feedback=None):
224256
"""
225257

226258
parameters['INPUT'] = QgsProcessingFeatureSourceDefinition(iface.activeLayer().id(), True)
227-
ok, results = execute_in_place_run(alg, parameters, context=context, feedback=feedback)
259+
ok, results = execute_in_place_run(alg, iface.activeLayer(), parameters, context=context, feedback=feedback)
228260
if ok:
229261
iface.activeLayer().triggerRepaint()
230262
return ok, results

‎python/plugins/processing/gui/ProcessingToolbox.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,7 @@ def set_in_place_edit_mode(self, enabled):
124124
def layer_changed(self, layer):
125125
if layer is None or layer.type() != QgsMapLayer.VectorLayer:
126126
return
127-
self.algorithmTree.setInPlaceLayerType(QgsWkbTypes.geometryType(layer.wkbType()))
127+
self.algorithmTree.setInPlaceLayer(layer)
128128

129129
def disabledProviders(self):
130130
showTip = ProcessingConfig.getSetting(ProcessingConfig.SHOW_PROVIDERS_TOOLTIP)

‎src/analysis/processing/qgsalgorithmdropmzvalues.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,12 @@ QgsDropMZValuesAlgorithm *QgsDropMZValuesAlgorithm::createInstance() const
5959
return new QgsDropMZValuesAlgorithm();
6060
}
6161

62+
bool QgsDropMZValuesAlgorithm::supportInPlaceEdit( const QgsVectorLayer *layer ) const
63+
{
64+
Q_UNUSED( layer );
65+
return false;
66+
}
67+
6268
void QgsDropMZValuesAlgorithm::initParameters( const QVariantMap & )
6369
{
6470
addParameter( new QgsProcessingParameterBoolean( QStringLiteral( "DROP_M_VALUES" ), QObject::tr( "Drop M Values" ), false ) );

‎src/analysis/processing/qgsalgorithmdropmzvalues.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ class QgsDropMZValuesAlgorithm : public QgsProcessingFeatureBasedAlgorithm
4141
QString groupId() const override;
4242
QString shortHelpString() const override;
4343
QgsDropMZValuesAlgorithm *createInstance() const override SIP_FACTORY;
44+
bool supportInPlaceEdit( const QgsVectorLayer *layer ) const override;
4445

4546
protected:
4647

@@ -55,6 +56,7 @@ class QgsDropMZValuesAlgorithm : public QgsProcessingFeatureBasedAlgorithm
5556

5657
bool mDropM = false;
5758
bool mDropZ = false;
59+
5860
};
5961

6062
///@endcond PRIVATE

‎src/analysis/processing/qgsalgorithmtranslate.cpp

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
***************************************************************************/
1717

1818
#include "qgsalgorithmtranslate.h"
19+
#include "qgsvectorlayer.h"
1920

2021
///@cond PRIVATE
2122

@@ -141,9 +142,9 @@ QgsFeatureList QgsTranslateAlgorithm::processFeature( const QgsFeature &feature,
141142
if ( mDynamicDeltaM )
142143
deltaM = mDeltaMProperty.valueAsDouble( context.expressionContext(), deltaM );
143144

144-
if ( deltaZ != 0 && !geometry.constGet()->is3D() )
145+
if ( deltaZ != 0.0 && !geometry.constGet()->is3D() )
145146
geometry.get()->addZValue( 0 );
146-
if ( deltaM != 0 && !geometry.constGet()->isMeasure() )
147+
if ( deltaM != 0.0 && !geometry.constGet()->isMeasure() )
147148
geometry.get()->addMValue( 0 );
148149

149150
geometry.translate( deltaX, deltaY, deltaZ, deltaM );
@@ -155,13 +156,27 @@ QgsFeatureList QgsTranslateAlgorithm::processFeature( const QgsFeature &feature,
155156
QgsWkbTypes::Type QgsTranslateAlgorithm::outputWkbType( QgsWkbTypes::Type inputWkbType ) const
156157
{
157158
QgsWkbTypes::Type wkb = inputWkbType;
158-
if ( mDeltaZ != 0 )
159+
if ( mDeltaZ != 0.0 )
159160
wkb = QgsWkbTypes::addZ( wkb );
160-
if ( mDeltaM != 0 )
161+
if ( mDeltaM != 0.0 )
161162
wkb = QgsWkbTypes::addM( wkb );
162163
return wkb;
163164
}
164165

166+
167+
bool QgsTranslateAlgorithm::supportInPlaceEdit( const QgsVectorLayer *layer ) const
168+
{
169+
if ( ! QgsProcessingFeatureBasedAlgorithm::supportInPlaceEdit( layer ) )
170+
return false;
171+
172+
// Check if we can drop Z/M and still have some work done
173+
if ( mDeltaX != 0.0 || mDeltaY != 0.0 )
174+
return true;
175+
176+
// If the type differs there is no sense in executing the algorithm and drop the result
177+
QgsWkbTypes::Type inPlaceWkbType = layer->wkbType();
178+
return inPlaceWkbType == outputWkbType( inPlaceWkbType );
179+
}
165180
///@endcond
166181

167182

‎src/analysis/processing/qgsalgorithmtranslate.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ class QgsTranslateAlgorithm : public QgsProcessingFeatureBasedAlgorithm
4242
QString shortHelpString() const override;
4343
QgsTranslateAlgorithm *createInstance() const override SIP_FACTORY;
4444
void initParameters( const QVariantMap &configuration = QVariantMap() ) override;
45+
bool supportInPlaceEdit( const QgsVectorLayer *layer ) const override;
4546

4647
protected:
4748
QString outputName() const override;

‎src/core/locator/qgslocator.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ const QList<QString> QgsLocator::CORE_FILTERS = QList<QString>() << QStringLiter
2828
<< QStringLiteral( "allfeatures" )
2929
<< QStringLiteral( "calculator" )
3030
<< QStringLiteral( "bookmarks" )
31-
<< QStringLiteral( "optionpages" );
31+
<< QStringLiteral( "optionpages" )
3232
<< QStringLiteral( "edit_features" );
3333

3434
QgsLocator::QgsLocator( QObject *parent )

‎src/core/processing/qgsprocessingalgorithm.cpp

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
#include "qgsprocessingutils.h"
2626
#include "qgsexception.h"
2727
#include "qgsmessagelog.h"
28+
#include "qgsvectorlayer.h"
2829
#include "qgsprocessingfeedback.h"
2930

3031
QgsProcessingAlgorithm::~QgsProcessingAlgorithm()
@@ -761,6 +762,12 @@ QString QgsProcessingAlgorithm::invalidSinkError( const QVariantMap &parameters,
761762
}
762763
}
763764

765+
bool QgsProcessingAlgorithm::supportInPlaceEdit( const QgsVectorLayer *layer ) const
766+
{
767+
Q_UNUSED( layer );
768+
return false;
769+
}
770+
764771
bool QgsProcessingAlgorithm::createAutoOutputForParameter( QgsProcessingParameterDefinition *parameter )
765772
{
766773
if ( !parameter->isDestination() )
@@ -902,3 +909,27 @@ QgsFeatureRequest QgsProcessingFeatureBasedAlgorithm::request() const
902909
return QgsFeatureRequest();
903910
}
904911

912+
bool QgsProcessingFeatureBasedAlgorithm::supportInPlaceEdit( const QgsVectorLayer *layer ) const
913+
{
914+
QgsWkbTypes::GeometryType inPlaceGeometryType = layer->geometryType();
915+
if ( !inputLayerTypes().empty() &&
916+
!inputLayerTypes().contains( QgsProcessing::TypeVector ) &&
917+
!inputLayerTypes().contains( QgsProcessing::TypeVectorAnyGeometry ) &&
918+
( ( inPlaceGeometryType == QgsWkbTypes::PolygonGeometry && !inputLayerTypes().contains( QgsProcessing::TypeVectorPolygon ) ) ||
919+
( inPlaceGeometryType == QgsWkbTypes::LineGeometry && !inputLayerTypes().contains( QgsProcessing::TypeVectorLine ) ) ||
920+
( inPlaceGeometryType == QgsWkbTypes::PointGeometry && !inputLayerTypes().contains( QgsProcessing::TypeVectorPoint ) ) ) )
921+
return false;
922+
923+
QgsWkbTypes::Type type = QgsWkbTypes::Unknown;
924+
if ( inPlaceGeometryType == QgsWkbTypes::PointGeometry )
925+
type = QgsWkbTypes::Point;
926+
else if ( inPlaceGeometryType == QgsWkbTypes::LineGeometry )
927+
type = QgsWkbTypes::LineString;
928+
else if ( inPlaceGeometryType == QgsWkbTypes::PolygonGeometry )
929+
type = QgsWkbTypes::Polygon;
930+
if ( QgsWkbTypes::geometryType( outputWkbType( type ) ) != inPlaceGeometryType )
931+
return false;
932+
933+
return true;
934+
}
935+

‎src/core/processing/qgsprocessingalgorithm.h

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -805,6 +805,15 @@ class CORE_EXPORT QgsProcessingAlgorithm
805805
*/
806806
static QString invalidSinkError( const QVariantMap &parameters, const QString &name );
807807

808+
/**
809+
* Checks whether this algorithm supports in-place editing on the given \a layer
810+
* Default implementation returns false.
811+
*
812+
* \return true if the algorithm supports in-place editing
813+
* \since QGIS 3.4
814+
*/
815+
virtual bool supportInPlaceEdit( const QgsVectorLayer *layer ) const;
816+
808817
private:
809818

810819
QgsProcessingProvider *mProvider = nullptr;
@@ -971,6 +980,16 @@ class CORE_EXPORT QgsProcessingFeatureBasedAlgorithm : public QgsProcessingAlgor
971980
*/
972981
virtual QgsFeatureRequest request() const;
973982

983+
/**
984+
* Checks whether this algorithm supports in-place editing on the given \a layer
985+
* Default implementation for feature based algorithms run some basic compatibility
986+
* checks based on the geometry type of the layer.
987+
*
988+
* \return true if the algorithm supports in-place editing
989+
* \since QGIS 3.4
990+
*/
991+
virtual bool supportInPlaceEdit( const QgsVectorLayer *layer ) const override;
992+
974993
private:
975994

976995
std::unique_ptr< QgsProcessingFeatureSource > mSource;

‎src/gui/processing/qgsprocessingtoolboxmodel.cpp

Lines changed: 6 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515

1616
#include "qgsprocessingtoolboxmodel.h"
1717
#include "qgsapplication.h"
18+
#include "qgsvectorlayer.h"
1819
#include "qgsprocessingregistry.h"
1920
#include "qgsprocessingrecentalgorithmlog.h"
2021
#include <functional>
@@ -655,12 +656,13 @@ void QgsProcessingToolboxProxyModel::setFilters( QgsProcessingToolboxProxyModel:
655656
invalidateFilter();
656657
}
657658

658-
void QgsProcessingToolboxProxyModel::setInPlaceLayerType( QgsWkbTypes::GeometryType type )
659+
void QgsProcessingToolboxProxyModel::setInPlaceLayer( QgsVectorLayer *layer )
659660
{
660-
mInPlaceGeometryType = type;
661+
mInPlaceLayer = layer;
661662
invalidateFilter();
662663
}
663664

665+
664666
void QgsProcessingToolboxProxyModel::setFilterString( const QString &filter )
665667
{
666668
mFilterString = filter;
@@ -721,25 +723,9 @@ bool QgsProcessingToolboxProxyModel::filterAcceptsRow( int sourceRow, const QMod
721723
return false;
722724

723725
const QgsProcessingFeatureBasedAlgorithm *alg = dynamic_cast< const QgsProcessingFeatureBasedAlgorithm * >( mModel->algorithmForIndex( sourceIndex ) );
724-
if ( alg )
726+
if ( !( mInPlaceLayer && alg && alg->supportInPlaceEdit( mInPlaceLayer ) ) )
725727
{
726-
if ( !alg->inputLayerTypes().empty() &&
727-
!alg->inputLayerTypes().contains( QgsProcessing::TypeVector ) &&
728-
!alg->inputLayerTypes().contains( QgsProcessing::TypeVectorAnyGeometry ) &&
729-
( ( mInPlaceGeometryType == QgsWkbTypes::PolygonGeometry && !alg->inputLayerTypes().contains( QgsProcessing::TypeVectorPolygon ) ) ||
730-
( mInPlaceGeometryType == QgsWkbTypes::LineGeometry && !alg->inputLayerTypes().contains( QgsProcessing::TypeVectorLine ) ) ||
731-
( mInPlaceGeometryType == QgsWkbTypes::PointGeometry && !alg->inputLayerTypes().contains( QgsProcessing::TypeVectorPoint ) ) ) )
732-
return false;
733-
734-
QgsWkbTypes::Type type = QgsWkbTypes::Unknown;
735-
if ( mInPlaceGeometryType == QgsWkbTypes::PointGeometry )
736-
type = QgsWkbTypes::Point;
737-
else if ( mInPlaceGeometryType == QgsWkbTypes::LineGeometry )
738-
type = QgsWkbTypes::LineString;
739-
else if ( mInPlaceGeometryType == QgsWkbTypes::PolygonGeometry )
740-
type = QgsWkbTypes::Polygon;
741-
if ( QgsWkbTypes::geometryType( alg->outputWkbType( type ) ) != mInPlaceGeometryType )
742-
return false;
728+
return false;
743729
}
744730
}
745731
if ( mFilters & FilterModeler )

‎src/gui/processing/qgsprocessingtoolboxmodel.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
#include <QSortFilterProxyModel>
2323
#include <QPointer>
2424

25+
class QgsVectorLayer;
2526
class QgsProcessingRegistry;
2627
class QgsProcessingProvider;
2728
class QgsProcessingAlgorithm;
@@ -460,7 +461,7 @@ class GUI_EXPORT QgsProcessingToolboxProxyModel: public QSortFilterProxyModel
460461
*/
461462
Filters filters() const { return mFilters; }
462463

463-
void setInPlaceLayerType( QgsWkbTypes::GeometryType type );
464+
void setInPlaceLayer( QgsVectorLayer *layer );
464465

465466
/**
466467
* Sets a \a filter string, such that only algorithms matching the
@@ -488,7 +489,7 @@ class GUI_EXPORT QgsProcessingToolboxProxyModel: public QSortFilterProxyModel
488489
QgsProcessingToolboxModel *mModel = nullptr;
489490
Filters mFilters = nullptr;
490491
QString mFilterString;
491-
QgsWkbTypes::GeometryType mInPlaceGeometryType = QgsWkbTypes::UnknownGeometry;
492+
QPointer<QgsVectorLayer> mInPlaceLayer;
492493
};
493494

494495

‎src/gui/processing/qgsprocessingtoolboxtreeview.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -93,9 +93,9 @@ void QgsProcessingToolboxTreeView::setFilters( QgsProcessingToolboxProxyModel::F
9393
mModel->setFilters( filters );
9494
}
9595

96-
void QgsProcessingToolboxTreeView::setInPlaceLayerType( QgsWkbTypes::GeometryType type )
96+
void QgsProcessingToolboxTreeView::setInPlaceLayer( QgsVectorLayer *layer )
9797
{
98-
mModel->setInPlaceLayerType( type );
98+
mModel->setInPlaceLayer( layer );
9999
}
100100

101101
QModelIndex QgsProcessingToolboxTreeView::findFirstVisibleAlgorithm( const QModelIndex &parent )

‎src/gui/processing/qgsprocessingtoolboxtreeview.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -88,9 +88,9 @@ class GUI_EXPORT QgsProcessingToolboxTreeView : public QTreeView
8888

8989
/**
9090
* Sets geometry \type for the in-place algorithms
91-
* @param type
91+
* \param layer the vector layer for in-place algorithm filter
9292
*/
93-
void setInPlaceLayerType( QgsWkbTypes::GeometryType type );
93+
void setInPlaceLayer( QgsVectorLayer *layer );
9494

9595
public slots:
9696

‎tests/src/python/test_qgsprocessinginplace.py

Lines changed: 64 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -87,26 +87,30 @@ def _alg_tester(self, alg_name, input_layer, parameters):
8787
context = QgsProcessingContext()
8888
context.setProject(QgsProject.instance())
8989
feedback = QgsProcessingFeedback()
90-
# Failing:
91-
#self.assertEqual(alg.parameterAsVectorLayer(parameters, 'INPUT', context ), input_layer)
9290

93-
def _f():
91+
with self.assertRaises(QgsProcessingException) as cm:
9492
execute_in_place_run(
95-
alg, parameters, context=context, feedback=feedback)
96-
97-
self.assertRaises(QgsProcessingException, _f)
93+
alg, input_layer, parameters, context=context, feedback=feedback, raise_exceptions=True)
9894

95+
ok = False
9996
input_layer.startEditing()
10097
ok, _ = execute_in_place_run(
101-
alg, parameters, context=context, feedback=feedback)
98+
alg, input_layer, parameters, context=context, feedback=feedback, raise_exceptions=True)
99+
new_features = [f for f in input_layer.getFeatures()]
102100

101+
# Check ret values
103102
self.assertTrue(ok, alg_name)
104103

105-
return old_features, [f for f in input_layer.getFeatures()]
104+
# Check geometry types (drop Z or M)
105+
self.assertEqual(new_features[0].geometry().wkbType(), old_features[0].geometry().wkbType())
106+
107+
return old_features, new_features
106108

107109
def test_execute_in_place_run(self):
108110
"""Test the execution in place"""
109111

112+
self.vl.rollBack()
113+
110114
old_features, new_features = self._alg_tester(
111115
'native:translategeometry',
112116
self.vl,
@@ -129,6 +133,37 @@ def test_execute_in_place_run(self):
129133
# Check selected
130134
self.assertEqual(self.vl.selectedFeatureIds(), [old_features[0].id()])
131135

136+
# Check that if the only change is Z or M then we should fail
137+
with self.assertRaises(QgsProcessingException) as cm:
138+
self._alg_tester(
139+
'native:translategeometry',
140+
self.vl,
141+
{
142+
'DELTA_Z': 1.1,
143+
}
144+
)
145+
self.vl.rollBack()
146+
147+
# Check that if the only change is Z or M then we should fail
148+
with self.assertRaises(QgsProcessingException) as cm:
149+
self._alg_tester(
150+
'native:translategeometry',
151+
self.vl,
152+
{
153+
'DELTA_M': 1.1,
154+
}
155+
)
156+
self.vl.rollBack()
157+
158+
old_features, new_features = self._alg_tester(
159+
'native:translategeometry',
160+
self.vl,
161+
{
162+
'DELTA_X': 1.1,
163+
'DELTA_Z': 1.1,
164+
}
165+
)
166+
132167
def test_multi_to_single(self):
133168
"""Check that the geometry type is still multi after the alg is run"""
134169

@@ -142,7 +177,27 @@ def test_multi_to_single(self):
142177
self.assertEqual(len(new_features), 3)
143178

144179
# Check selected
145-
self.assertEqual(sorted(self.multipoly_vl.selectedFeatureIds()), [-3, -2])
180+
self.assertEqual(len(self.multipoly_vl.selectedFeatureIds()), 2)
181+
182+
def test_arrayfeatures(self):
183+
"""Check that this runs correctly and additional attributes are dropped"""
184+
185+
old_count = self.vl.featureCount()
186+
187+
old_features, new_features = self._alg_tester(
188+
'native:arrayfeatures',
189+
self.vl,
190+
{
191+
'COUNT': 2,
192+
'DELTA_X': 1.1,
193+
'DELTA_Z': 1.1,
194+
}
195+
)
196+
197+
self.assertEqual(len(new_features), old_count + 2)
198+
199+
# Check selected
200+
self.assertEqual(len(self.vl.selectedFeatureIds()), 3)
146201

147202
def test_make_compatible(self):
148203
"""Test fixer function"""

0 commit comments

Comments
 (0)
Please sign in to comment.