Skip to content

Commit 85b2efa

Browse files
committedApr 25, 2018
Move internal intersection to overlay utils, code de-duplication
1 parent d0130d2 commit 85b2efa

File tree

3 files changed

+144
-132
lines changed

3 files changed

+144
-132
lines changed
 

‎src/analysis/processing/qgsalgorithmintersection.cpp

Lines changed: 9 additions & 132 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717

1818
#include "qgsgeometrycollection.h"
1919
#include "qgsgeometryengine.h"
20+
#include "qgsoverlayutils.h"
2021

2122
///@cond PRIVATE
2223

@@ -87,147 +88,23 @@ QVariantMap QgsIntersectionAlgorithm::processAlgorithm( const QVariantMap &param
8788
const QStringList fieldsA = parameterAsFields( parameters, QStringLiteral( "INPUT_FIELDS" ), context );
8889
const QStringList fieldsB = parameterAsFields( parameters, QStringLiteral( "OVERLAY_FIELDS" ), context );
8990

90-
QgsFields fieldListA;
91-
QList<int> fieldIndicesA;
92-
if ( !fieldsA.isEmpty() )
93-
{
94-
for ( const QString &f : fieldsA )
95-
{
96-
int idxA = sourceA->fields().lookupField( f );
97-
if ( idxA >= 0 )
98-
{
99-
fieldIndicesA.append( idxA );
100-
fieldListA.append( sourceA->fields()[idxA] );
101-
}
102-
}
103-
}
104-
else
105-
{
106-
fieldListA = sourceA->fields();
107-
for ( int i = 0; i < fieldListA.count(); ++i )
108-
fieldIndicesA.append( i );
109-
}
110-
111-
QgsFields fieldListB;
112-
QList<int> fieldIndicesB;
113-
if ( !fieldsB.isEmpty() )
114-
{
115-
for ( const QString &f : fieldsB )
116-
{
117-
int idxB = sourceB->fields().lookupField( f );
118-
if ( idxB >= 0 )
119-
{
120-
fieldIndicesB.append( idxB );
121-
fieldListB.append( sourceB->fields()[idxB] );
122-
}
123-
}
124-
}
125-
else
126-
{
127-
fieldListB = sourceB->fields();
128-
for ( int i = 0; i < fieldListB.count(); ++i )
129-
fieldIndicesB.append( i );
130-
}
131-
132-
133-
QgsFields outputFields = QgsProcessingUtils::combineFields( fieldListA, fieldListB );
91+
QList<int> fieldIndicesA = QgsOverlayUtils::fieldNamesToIndices( fieldsA, sourceA->fields() );
92+
QList<int> fieldIndicesB = QgsOverlayUtils::fieldNamesToIndices( fieldsB, sourceB->fields() );
93+
94+
QgsFields outputFields = QgsProcessingUtils::combineFields(
95+
QgsOverlayUtils::indicesToFields( fieldIndicesA, sourceA->fields() ),
96+
QgsOverlayUtils::indicesToFields( fieldIndicesB, sourceB->fields() ) );
13497

13598
QString dest;
13699
std::unique_ptr< QgsFeatureSink > sink( parameterAsSink( parameters, QStringLiteral( "OUTPUT" ), context, dest, outputFields, geomType, sourceA->sourceCrs() ) );
137100

138101
QVariantMap outputs;
139102
outputs.insert( QStringLiteral( "OUTPUT" ), dest );
140103

141-
QgsFeatureRequest request;
142-
request.setSubsetOfAttributes( QgsAttributeList() );
143-
request.setDestinationCrs( sourceA->sourceCrs(), context.transformContext() );
144-
145-
QgsFeature outFeat;
146-
QgsSpatialIndex indexB( sourceB->getFeatures( request ), feedback );
147-
148-
double total = 100.0 / ( sourceA->featureCount() ? sourceA->featureCount() : 1 );
149104
int count = 0;
105+
int total = sourceA->featureCount();
150106

151-
QgsFeature featA;
152-
QgsFeatureIterator fitA = sourceA->getFeatures( QgsFeatureRequest().setSubsetOfAttributes( fieldIndicesA ) );
153-
while ( fitA.nextFeature( featA ) )
154-
{
155-
if ( feedback->isCanceled() )
156-
break;
157-
158-
if ( !featA.hasGeometry() )
159-
continue;
160-
161-
QgsGeometry geom( featA.geometry() );
162-
QgsFeatureIds intersects = indexB.intersects( geom.boundingBox() ).toSet();
163-
164-
QgsFeatureRequest request;
165-
request.setFilterFids( intersects );
166-
request.setDestinationCrs( sourceA->sourceCrs(), context.transformContext() );
167-
request.setSubsetOfAttributes( fieldIndicesB );
168-
169-
std::unique_ptr< QgsGeometryEngine > engine;
170-
if ( !intersects.isEmpty() )
171-
{
172-
// use prepared geometries for faster intersection tests
173-
engine.reset( QgsGeometry::createGeometryEngine( geom.constGet() ) );
174-
engine->prepareGeometry();
175-
}
176-
177-
QgsAttributes outAttributes( outputFields.count() );
178-
const QgsAttributes attrsA( featA.attributes() );
179-
for ( int i = 0; i < fieldIndicesA.count(); ++i )
180-
outAttributes[i] = attrsA[fieldIndicesA[i]];
181-
182-
QgsFeature featB;
183-
QgsFeatureIterator fitB = sourceB->getFeatures( request );
184-
while ( fitB.nextFeature( featB ) )
185-
{
186-
if ( feedback->isCanceled() )
187-
break;
188-
189-
QgsGeometry tmpGeom( featB.geometry() );
190-
if ( !engine->intersects( tmpGeom.constGet() ) )
191-
continue;
192-
193-
QgsGeometry intGeom = geom.intersection( tmpGeom );
194-
195-
if ( intGeom.isNull() )
196-
{
197-
// TODO: not sure if this ever happens - if it does, that means GEOS failed badly - would be good to have a test for such situation
198-
throw QgsProcessingException( QStringLiteral( "%1\n\n%2" ).arg( QObject::tr( "GEOS geoprocessing error: intersection failed." ), intGeom.lastError() ) );
199-
}
200-
201-
// Intersection of geometries may give use also geometries we do not want in our results.
202-
// For example, two square polygons touching at the corner have a point as the intersection, but no area.
203-
// In other cases we may get a mixture of geometries in the output - we want to keep only the expected types.
204-
if ( QgsWkbTypes::flatType( intGeom.wkbType() ) == QgsWkbTypes::GeometryCollection )
205-
{
206-
// try to filter out irrelevant parts with different geometry type than what we want
207-
intGeom.convertGeometryCollectionToSubclass( QgsWkbTypes::geometryType( geomType ) );
208-
if ( intGeom.isEmpty() )
209-
continue;
210-
}
211-
212-
if ( QgsWkbTypes::geometryType( intGeom.wkbType() ) != QgsWkbTypes::geometryType( geomType ) )
213-
{
214-
// we can't make use of this resulting geometry
215-
continue;
216-
}
217-
218-
const QgsAttributes attrsB( featB.attributes() );
219-
for ( int i = 0; i < fieldIndicesB.count(); ++i )
220-
outAttributes[fieldIndicesA.count() + i] = attrsB[fieldIndicesB[i]];
221-
222-
intGeom.convertToMultiType();
223-
outFeat.setGeometry( intGeom );
224-
outFeat.setAttributes( outAttributes );
225-
sink->addFeature( outFeat, QgsFeatureSink::FastInsert );
226-
}
227-
228-
++count;
229-
feedback->setProgress( int( count * total ) );
230-
}
107+
QgsOverlayUtils::intersection( *sourceA.get(), *sourceB.get(), *sink.get(), context, feedback, count, total, fieldIndicesA, fieldIndicesB );
231108

232109
return outputs;
233110
}

‎src/analysis/processing/qgsoverlayutils.cpp

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,4 +120,131 @@ void QgsOverlayUtils::difference( const QgsFeatureSource &sourceA, const QgsFeat
120120
}
121121
}
122122

123+
124+
void QgsOverlayUtils::intersection( const QgsFeatureSource &sourceA, const QgsFeatureSource &sourceB, QgsFeatureSink &sink, QgsProcessingContext &context, QgsProcessingFeedback *feedback, int &count, int totalCount, const QList<int> &fieldIndicesA, const QList<int> &fieldIndicesB )
125+
{
126+
QgsWkbTypes::GeometryType geometryType = QgsWkbTypes::geometryType( QgsWkbTypes::multiType( sourceA.wkbType() ) );
127+
int attrCount = fieldIndicesA.count() + fieldIndicesB.count();
128+
129+
QgsFeatureRequest request;
130+
request.setSubsetOfAttributes( QgsAttributeList() );
131+
request.setDestinationCrs( sourceA.sourceCrs(), context.transformContext() );
132+
133+
QgsFeature outFeat;
134+
QgsSpatialIndex indexB( sourceB.getFeatures( request ), feedback );
135+
136+
if ( totalCount == 0 )
137+
totalCount = 1; // avoid division by zero
138+
139+
QgsFeature featA;
140+
QgsFeatureIterator fitA = sourceA.getFeatures( QgsFeatureRequest().setSubsetOfAttributes( fieldIndicesA ) );
141+
while ( fitA.nextFeature( featA ) )
142+
{
143+
if ( feedback->isCanceled() )
144+
break;
145+
146+
if ( !featA.hasGeometry() )
147+
continue;
148+
149+
QgsGeometry geom( featA.geometry() );
150+
QgsFeatureIds intersects = indexB.intersects( geom.boundingBox() ).toSet();
151+
152+
QgsFeatureRequest request;
153+
request.setFilterFids( intersects );
154+
request.setDestinationCrs( sourceA.sourceCrs(), context.transformContext() );
155+
request.setSubsetOfAttributes( fieldIndicesB );
156+
157+
std::unique_ptr< QgsGeometryEngine > engine;
158+
if ( !intersects.isEmpty() )
159+
{
160+
// use prepared geometries for faster intersection tests
161+
engine.reset( QgsGeometry::createGeometryEngine( geom.constGet() ) );
162+
engine->prepareGeometry();
163+
}
164+
165+
QgsAttributes outAttributes( attrCount );
166+
const QgsAttributes attrsA( featA.attributes() );
167+
for ( int i = 0; i < fieldIndicesA.count(); ++i )
168+
outAttributes[i] = attrsA[fieldIndicesA[i]];
169+
170+
QgsFeature featB;
171+
QgsFeatureIterator fitB = sourceB.getFeatures( request );
172+
while ( fitB.nextFeature( featB ) )
173+
{
174+
if ( feedback->isCanceled() )
175+
break;
176+
177+
QgsGeometry tmpGeom( featB.geometry() );
178+
if ( !engine->intersects( tmpGeom.constGet() ) )
179+
continue;
180+
181+
QgsGeometry intGeom = geom.intersection( tmpGeom );
182+
183+
if ( intGeom.isNull() )
184+
{
185+
// TODO: not sure if this ever happens - if it does, that means GEOS failed badly - would be good to have a test for such situation
186+
throw QgsProcessingException( QStringLiteral( "%1\n\n%2" ).arg( QObject::tr( "GEOS geoprocessing error: intersection failed." ), intGeom.lastError() ) );
187+
}
188+
189+
// Intersection of geometries may give use also geometries we do not want in our results.
190+
// For example, two square polygons touching at the corner have a point as the intersection, but no area.
191+
// In other cases we may get a mixture of geometries in the output - we want to keep only the expected types.
192+
if ( QgsWkbTypes::flatType( intGeom.wkbType() ) == QgsWkbTypes::GeometryCollection )
193+
{
194+
// try to filter out irrelevant parts with different geometry type than what we want
195+
intGeom.convertGeometryCollectionToSubclass( geometryType );
196+
if ( intGeom.isEmpty() )
197+
continue;
198+
}
199+
200+
if ( QgsWkbTypes::geometryType( intGeom.wkbType() ) != geometryType )
201+
{
202+
// we can't make use of this resulting geometry
203+
continue;
204+
}
205+
206+
const QgsAttributes attrsB( featB.attributes() );
207+
for ( int i = 0; i < fieldIndicesB.count(); ++i )
208+
outAttributes[fieldIndicesA.count() + i] = attrsB[fieldIndicesB[i]];
209+
210+
intGeom.convertToMultiType();
211+
outFeat.setGeometry( intGeom );
212+
outFeat.setAttributes( outAttributes );
213+
sink.addFeature( outFeat, QgsFeatureSink::FastInsert );
214+
}
215+
216+
++count;
217+
feedback->setProgress( count / ( double ) totalCount * 100. );
218+
}
219+
}
220+
221+
222+
QList<int> QgsOverlayUtils::fieldNamesToIndices( const QStringList &fieldNames, const QgsFields &fields )
223+
{
224+
QList<int> indices;
225+
if ( !fieldNames.isEmpty() )
226+
{
227+
for ( const QString &f : fieldNames )
228+
{
229+
int idx = fields.lookupField( f );
230+
if ( idx >= 0 )
231+
indices.append( idx );
232+
}
233+
}
234+
else
235+
{
236+
for ( int i = 0; i < fields.count(); ++i )
237+
indices.append( i );
238+
}
239+
return indices;
240+
}
241+
242+
QgsFields QgsOverlayUtils::indicesToFields( const QList<int> &indices, const QgsFields &fields )
243+
{
244+
QgsFields fieldsSubset;
245+
for ( int i : indices )
246+
fieldsSubset.append( fields.at( i ) );
247+
return fieldsSubset;
248+
}
249+
123250
///@endcond PRIVATE

‎src/analysis/processing/qgsoverlayutils.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,15 @@
1616
#ifndef QGSOVERLAYUTILS_H
1717
#define QGSOVERLAYUTILS_H
1818

19+
#include <QList>
20+
1921
#define SIP_NO_FILE
2022

2123
///@cond PRIVATE
2224

2325
class QgsFeatureSource;
2426
class QgsFeatureSink;
27+
class QgsFields;
2528
class QgsProcessingContext;
2629
class QgsProcessingFeedback;
2730

@@ -38,6 +41,11 @@ namespace QgsOverlayUtils
3841

3942
void difference( const QgsFeatureSource &sourceA, const QgsFeatureSource &sourceB, QgsFeatureSink &sink, QgsProcessingContext &context, QgsProcessingFeedback *feedback, int &count, int totalCount, DifferenceOutput outputAttrs );
4043

44+
void intersection( const QgsFeatureSource &sourceA, const QgsFeatureSource &sourceB, QgsFeatureSink &sink, QgsProcessingContext &context, QgsProcessingFeedback *feedback, int &count, int totalCount, const QList<int> &fieldIndicesA, const QList<int> &fieldIndicesB );
45+
46+
QList<int> fieldNamesToIndices( const QStringList &fieldNames, const QgsFields &fields );
47+
48+
QgsFields indicesToFields( const QList<int> &indices, const QgsFields &fields );
4149
}
4250

4351
///@endcond PRIVATE

0 commit comments

Comments
 (0)
Please sign in to comment.