Skip to content

Commit c41dca9

Browse files
committed
Port processing combineFields to c++
1 parent cf636dc commit c41dca9

File tree

11 files changed

+100
-31
lines changed

11 files changed

+100
-31
lines changed

python/core/processing/qgsprocessingutils.sip

+9
Original file line numberDiff line numberDiff line change
@@ -193,6 +193,15 @@ class QgsProcessingUtils
193193
:rtype: str
194194
%End
195195

196+
static QgsFields combineFields( const QgsFields &fieldsA, const QgsFields &fieldsB );
197+
%Docstring
198+
Combines two field lists, avoiding duplicate field names (in a case-insensitive manner).
199+
200+
Duplicate field names will be altered to "name_2", "name_3", etc, finding the first
201+
non-duplicate name.
202+
:rtype: QgsFields
203+
%End
204+
196205
};
197206

198207
class QgsProcessingFeatureSource : QgsFeatureSource

python/plugins/processing/algs/qgis/HubLines.py

+3-2
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,8 @@
3737
QgsProcessingParameterField,
3838
QgsProcessingParameterFeatureSink,
3939
QgsProcessingException,
40-
QgsExpression)
40+
QgsExpression,
41+
QgsProcessingUtils)
4142
from processing.algs.qgis.QgisAlgorithm import QgisAlgorithm
4243
from processing.tools import vector
4344

@@ -89,7 +90,7 @@ def processAlgorithm(self, parameters, context, feedback):
8990
field_spoke = self.parameterAsString(parameters, self.SPOKE_FIELD, context)
9091
field_spoke_index = hub_source.fields().lookupField(field_spoke)
9192

92-
fields = vector.combineFields(hub_source.fields(), spoke_source.fields())
93+
fields = QgsProcessingUtils.combineFields(hub_source.fields(), spoke_source.fields())
9394

9495
(sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context,
9596
fields, QgsWkbTypes.LineString, hub_source.sourceCrs())

python/plugins/processing/algs/qgis/JoinAttributes.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ def processAlgorithm(self, parameters, context, feedback):
8282
joinField1Index = input.fields().lookupField(field)
8383
joinField2Index = input2.fields().lookupField(field2)
8484

85-
outFields = vector.combineFields(input.fields(), input2.fields())
85+
outFields = QgsProcessingUtils.combineFields(input.fields(), input2.fields())
8686

8787
(sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context,
8888
outFields, input.wkbType(), input.sourceCrs())

python/plugins/processing/algs/qgis/SpatialJoin.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
QgsFeatureRequest,
3838
QgsGeometry,
3939
QgsProcessing,
40+
QgsProcessingUtils,
4041
QgsProcessingParameterBoolean,
4142
QgsProcessingParameterFeatureSource,
4243
QgsProcessingParameterEnum,
@@ -148,7 +149,7 @@ def processAlgorithm(self, parameters, context, feedback):
148149
if idx >= 0:
149150
fields_to_join.append(join_source.fields().at(idx))
150151

151-
out_fields = vector.combineFields(source_fields, fields_to_join)
152+
out_fields = QgsProcessingUtils.combineFields(source_fields, fields_to_join)
152153

153154
(sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context,
154155
out_fields, source.wkbType(), source.sourceCrs())

python/plugins/processing/algs/qgis/SpatialJoinSummary.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@
4343
QgsDateTimeStatisticalSummary,
4444
QgsStringStatisticalSummary,
4545
QgsProcessing,
46+
QgsProcessingUtils,
4647
QgsProcessingParameterBoolean,
4748
QgsProcessingParameterFeatureSource,
4849
QgsProcessingParameterEnum,
@@ -247,7 +248,7 @@ def addField(original, stat, type):
247248
else:
248249
addFieldKeepType(join_field, f[0])
249250

250-
out_fields = vector.combineFields(source_fields, fields_to_join)
251+
out_fields = QgsProcessingUtils.combineFields(source_fields, fields_to_join)
251252

252253
(sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context,
253254
out_fields, source.wkbType(), source.sourceCrs())

python/plugins/processing/algs/qgis/SymmetricalDifference.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
NULL,
3737
QgsWkbTypes,
3838
QgsMessageLog,
39+
QgsProcessingUtils,
3940
QgsProcessingParameterFeatureSource,
4041
QgsProcessingParameterFeatureSink,
4142
QgsSpatialIndex)
@@ -79,7 +80,7 @@ def processAlgorithm(self, parameters, context, feedback):
7980
sourceB = self.parameterAsSource(parameters, self.OVERLAY, context)
8081

8182
geomType = QgsWkbTypes.multiType(sourceA.wkbType())
82-
fields = vector.combineFields(sourceA.fields(), sourceB.fields())
83+
fields = QgsProcessingUtils.combineFields(sourceA.fields(), sourceB.fields())
8384

8485
(sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context,
8586
fields, geomType, sourceA.sourceCrs())

python/plugins/processing/algs/qgis/Union.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@
3434
QgsFeatureSink,
3535
QgsGeometry,
3636
QgsWkbTypes,
37-
QgsMessageLog,
37+
QgsProcessingUtils,
3838
QgsProcessingParameterFeatureSource,
3939
QgsProcessingParameterFeatureSink,
4040
QgsSpatialIndex)
@@ -79,7 +79,7 @@ def processAlgorithm(self, parameters, context, feedback):
7979
sourceB = self.parameterAsSource(parameters, self.OVERLAY, context)
8080

8181
geomType = QgsWkbTypes.multiType(sourceA.wkbType())
82-
fields = vector.combineFields(sourceA.fields(), sourceB.fields())
82+
fields = QgsProcessingUtils.combineFields(sourceA.fields(), sourceB.fields())
8383

8484
(sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context,
8585
fields, geomType, sourceA.sourceCrs())

python/plugins/processing/tools/vector.py

-23
Original file line numberDiff line numberDiff line change
@@ -174,29 +174,6 @@ def extractPoints(geom):
174174
return points
175175

176176

177-
def combineFields(fieldsA, fieldsB):
178-
"""Create single field map from two input field maps.
179-
"""
180-
fields = []
181-
fields.extend(fieldsA)
182-
namesA = [str(f.name()).lower() for f in fieldsA]
183-
for field in fieldsB:
184-
name = str(field.name()).lower()
185-
if name in namesA:
186-
idx = 2
187-
newName = name + '_' + str(idx)
188-
while newName in namesA:
189-
idx += 1
190-
newName = name + '_' + str(idx)
191-
field = QgsField(newName, field.type(), field.typeName())
192-
fields.append(field)
193-
194-
real_fields = QgsFields()
195-
for f in fields:
196-
real_fields.append(f)
197-
return real_fields
198-
199-
200177
def checkMinDistance(point, index, distance, points):
201178
"""Check if distance from given point to all other points is greater
202179
than given value.

src/core/processing/qgsprocessingutils.cpp

+35
Original file line numberDiff line numberDiff line change
@@ -555,6 +555,41 @@ QString QgsProcessingUtils::convertToCompatibleFormat( const QgsVectorLayer *vl,
555555
}
556556
}
557557

558+
QgsFields QgsProcessingUtils::combineFields( const QgsFields &fieldsA, const QgsFields &fieldsB )
559+
{
560+
QgsFields outFields = fieldsA;
561+
QSet< QString > usedNames;
562+
for ( const QgsField &f : fieldsA )
563+
{
564+
usedNames.insert( f.name().toLower() );
565+
}
566+
567+
for ( const QgsField &f : fieldsB )
568+
{
569+
if ( usedNames.contains( f.name().toLower() ) )
570+
{
571+
int idx = 2;
572+
QString newName = f.name() + '_' + QString::number( idx );
573+
while ( usedNames.contains( newName.toLower() ) )
574+
{
575+
idx++;
576+
newName = f.name() + '_' + QString::number( idx );
577+
}
578+
QgsField newField = f;
579+
newField.setName( newName );
580+
outFields.append( newField );
581+
usedNames.insert( newName.toLower() );
582+
}
583+
else
584+
{
585+
usedNames.insert( f.name().toLower() );
586+
outFields.append( f );
587+
}
588+
}
589+
590+
return outFields;
591+
}
592+
558593

559594
//
560595
// QgsProcessingFeatureSource

src/core/processing/qgsprocessingutils.h

+8
Original file line numberDiff line numberDiff line change
@@ -224,6 +224,14 @@ class CORE_EXPORT QgsProcessingUtils
224224
QgsProcessingContext &context,
225225
QgsProcessingFeedback *feedback );
226226

227+
/**
228+
* Combines two field lists, avoiding duplicate field names (in a case-insensitive manner).
229+
*
230+
* Duplicate field names will be altered to "name_2", "name_3", etc, finding the first
231+
* non-duplicate name.
232+
*/
233+
static QgsFields combineFields( const QgsFields &fieldsA, const QgsFields &fieldsB );
234+
227235
private:
228236

229237
static bool canUseLayer( const QgsRasterLayer *layer );

tests/src/core/testqgsprocessing.cpp

+36
Original file line numberDiff line numberDiff line change
@@ -352,6 +352,7 @@ class TestQgsProcessing: public QObject
352352
void tempUtils();
353353
void convertCompatible();
354354
void create();
355+
void combineFields();
355356

356357
private:
357358

@@ -5443,5 +5444,40 @@ void TestQgsProcessing::create()
54435444
QCOMPARE( newInstance->provider(), &p );
54445445
}
54455446

5447+
void TestQgsProcessing::combineFields()
5448+
{
5449+
QgsFields a;
5450+
QgsFields b;
5451+
// combine empty fields
5452+
QgsFields res = QgsProcessingUtils::combineFields( a, b );
5453+
QVERIFY( res.isEmpty() );
5454+
5455+
// fields in a
5456+
a.append( QgsField( "name" ) );
5457+
res = QgsProcessingUtils::combineFields( a, b );
5458+
QCOMPARE( res.count(), 1 );
5459+
QCOMPARE( res.at( 0 ).name(), QStringLiteral( "name" ) );
5460+
b.append( QgsField( "name" ) );
5461+
res = QgsProcessingUtils::combineFields( a, b );
5462+
QCOMPARE( res.count(), 2 );
5463+
QCOMPARE( res.at( 0 ).name(), QStringLiteral( "name" ) );
5464+
QCOMPARE( res.at( 1 ).name(), QStringLiteral( "name_2" ) );
5465+
5466+
a.append( QgsField( "NEW" ) );
5467+
res = QgsProcessingUtils::combineFields( a, b );
5468+
QCOMPARE( res.count(), 3 );
5469+
QCOMPARE( res.at( 0 ).name(), QStringLiteral( "name" ) );
5470+
QCOMPARE( res.at( 1 ).name(), QStringLiteral( "NEW" ) );
5471+
QCOMPARE( res.at( 2 ).name(), QStringLiteral( "name_2" ) );
5472+
5473+
b.append( QgsField( "new" ) );
5474+
res = QgsProcessingUtils::combineFields( a, b );
5475+
QCOMPARE( res.count(), 4 );
5476+
QCOMPARE( res.at( 0 ).name(), QStringLiteral( "name" ) );
5477+
QCOMPARE( res.at( 1 ).name(), QStringLiteral( "NEW" ) );
5478+
QCOMPARE( res.at( 2 ).name(), QStringLiteral( "name_2" ) );
5479+
QCOMPARE( res.at( 3 ).name(), QStringLiteral( "new_2" ) );
5480+
}
5481+
54465482
QGSTEST_MAIN( TestQgsProcessing )
54475483
#include "testqgsprocessing.moc"

0 commit comments

Comments
 (0)