Skip to content

Commit d1aa03a

Browse files
committedAug 3, 2016
Better clip for very separate features
1 parent 71ebdb8 commit d1aa03a

File tree

1 file changed

+65
-47
lines changed
  • python/plugins/processing/algs/qgis

1 file changed

+65
-47
lines changed
 

‎python/plugins/processing/algs/qgis/Clip.py

Lines changed: 65 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -60,70 +60,88 @@ def defineCharacteristics(self):
6060
self.addOutput(OutputVector(Clip.OUTPUT, self.tr('Clipped')))
6161

6262
def processAlgorithm(self, progress):
63-
sourceLayer = dataobjects.getObjectFromUri(
63+
source_layer = dataobjects.getObjectFromUri(
6464
self.getParameterValue(Clip.INPUT))
65-
maskLayer = dataobjects.getObjectFromUri(
65+
mask_layer = dataobjects.getObjectFromUri(
6666
self.getParameterValue(Clip.OVERLAY))
6767

6868
writer = self.getOutputFromName(self.OUTPUT).getVectorWriter(
69-
sourceLayer.pendingFields(),
70-
sourceLayer.dataProvider().geometryType(),
71-
sourceLayer.dataProvider().crs())
72-
73-
inFeatA = QgsFeature()
74-
inFeatB = QgsFeature()
75-
outFeat = QgsFeature()
69+
source_layer.fields(),
70+
source_layer.dataProvider().geometryType(),
71+
source_layer.crs())
7672

7773
# first build up a list of clip geometries
7874
clip_geoms = []
79-
for maskFeat in vector.features(maskLayer, QgsFeatureRequest().setSubsetOfAttributes([])):
75+
for maskFeat in vector.features(mask_layer, QgsFeatureRequest().setSubsetOfAttributes([])):
8076
clip_geoms.append(maskFeat.geometry())
8177

78+
# are we clipping against a single feature? if so, we can show finer progress reports
8279
if len(clip_geoms) > 1:
8380
combined_clip_geom = QgsGeometry.unaryUnion(clip_geoms)
81+
single_clip_feature = False
8482
else:
8583
combined_clip_geom = clip_geoms[0]
84+
single_clip_feature = True
8685

87-
# use prepared geometries for faster insection tests
86+
# use prepared geometries for faster intersection tests
8887
engine = QgsGeometry.createGeometryEngine(combined_clip_geom.geometry())
8988
engine.prepareGeometry()
9089

91-
input_features = [f for f in vector.features(sourceLayer, QgsFeatureRequest().setFilterRect(combined_clip_geom.boundingBox()))]
92-
total = 100.0 / len(input_features)
93-
for current, in_feat in enumerate(input_features):
94-
if not in_feat.geometry():
95-
continue
96-
97-
if not engine.intersects(in_feat.geometry().geometry()):
98-
continue
99-
100-
if not engine.contains(in_feat.geometry().geometry()):
101-
cur_geom = in_feat.geometry()
102-
new_geom = combined_clip_geom.intersection(cur_geom)
103-
if new_geom.wkbType() == Qgis.WKBUnknown or QgsWKBTypes.flatType(new_geom.geometry().wkbType()) == QgsWKBTypes.GeometryCollection:
104-
int_com = in_feat.geometry().combine(new_geom)
105-
int_sym = in_feat.geometry().symDifference(new_geom)
106-
new_geom = int_com.difference(int_sym)
107-
if new_geom.isGeosEmpty() or not new_geom.isGeosValid():
108-
ProcessingLog.addToLog(ProcessingLog.LOG_ERROR,
109-
self.tr('GEOS geoprocessing error: One or more '
110-
'input features have invalid geometry.'))
90+
tested_feature_ids = set()
91+
92+
for i, clip_geom in enumerate(clip_geoms):
93+
input_features = [f for f in vector.features(source_layer, QgsFeatureRequest().setFilterRect(clip_geom.boundingBox()))]
94+
95+
if single_clip_feature:
96+
total = 100.0 / len(input_features)
11197
else:
112-
# clip geometry totally contains feature geometry, so no need to perform intersection
113-
new_geom = in_feat.geometry()
114-
115-
try:
116-
outFeat = QgsFeature()
117-
outFeat.setGeometry(new_geom)
118-
outFeat.setAttributes(in_feat.attributes())
119-
writer.addFeature(outFeat)
120-
except:
121-
ProcessingLog.addToLog(ProcessingLog.LOG_ERROR,
122-
self.tr('Feature geometry error: One or more '
123-
'output features ignored due to '
124-
'invalid geometry.'))
125-
continue
126-
127-
progress.setPercentage(int(current * total))
98+
total = 0
99+
100+
for current, in_feat in enumerate(input_features):
101+
if not in_feat.geometry():
102+
continue
103+
104+
if in_feat.id() in tested_feature_ids:
105+
# don't retest a feature we have already checked
106+
continue
107+
108+
tested_feature_ids.add(in_feat.id())
109+
110+
if not engine.intersects(in_feat.geometry().geometry()):
111+
continue
112+
113+
if not engine.contains(in_feat.geometry().geometry()):
114+
cur_geom = in_feat.geometry()
115+
new_geom = combined_clip_geom.intersection(cur_geom)
116+
if new_geom.wkbType() == Qgis.WKBUnknown or QgsWKBTypes.flatType(new_geom.geometry().wkbType()) == QgsWKBTypes.GeometryCollection:
117+
int_com = in_feat.geometry().combine(new_geom)
118+
int_sym = in_feat.geometry().symDifference(new_geom)
119+
new_geom = int_com.difference(int_sym)
120+
if new_geom.isGeosEmpty() or not new_geom.isGeosValid():
121+
ProcessingLog.addToLog(ProcessingLog.LOG_ERROR,
122+
self.tr('GEOS geoprocessing error: One or more '
123+
'input features have invalid geometry.'))
124+
else:
125+
# clip geometry totally contains feature geometry, so no need to perform intersection
126+
new_geom = in_feat.geometry()
127+
128+
try:
129+
out_feat = QgsFeature()
130+
out_feat.setGeometry(new_geom)
131+
out_feat.setAttributes(in_feat.attributes())
132+
writer.addFeature(out_feat)
133+
except:
134+
ProcessingLog.addToLog(ProcessingLog.LOG_ERROR,
135+
self.tr('Feature geometry error: One or more '
136+
'output features ignored due to '
137+
'invalid geometry.'))
138+
continue
139+
140+
if single_clip_feature:
141+
progress.setPercentage(int(current * total))
142+
143+
if not single_clip_feature:
144+
# coarse progress report for multiple clip geometries
145+
progress.setPercentage(100.0 * i / len(clip_geoms))
128146

129147
del writer

0 commit comments

Comments
 (0)
Please sign in to comment.