Skip to content

Commit afd21cd

Browse files
committed
Merge pull request #1759 from arnaud-morvan/processing_bylocation_operators
Processing bylocation operators
2 parents b1a2ccd + 84936e5 commit afd21cd

12 files changed

+490
-129
lines changed

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

+45-59
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@
2828
from qgis.core import QGis, QgsFeatureRequest, QgsGeometry
2929
from processing.core.GeoAlgorithm import GeoAlgorithm
3030
from processing.core.parameters import ParameterVector
31-
from processing.core.parameters import ParameterBoolean
31+
from processing.core.parameters import ParameterGeometryPredicate
3232
from processing.core.outputs import OutputVector
3333
from processing.tools import dataobjects, vector
3434

@@ -37,100 +37,86 @@ class ExtractByLocation(GeoAlgorithm):
3737

3838
INPUT = 'INPUT'
3939
INTERSECT = 'INTERSECT'
40-
TOUCHES = 'TOUCHES'
41-
OVERLAPS = 'OVERLAPS'
42-
WITHIN = 'WITHIN'
40+
PREDICATE = 'PREDICATE'
4341
OUTPUT = 'OUTPUT'
4442

45-
METHODS = ['creating new selection', 'adding to current selection',
46-
'removing from current selection']
47-
opFlags = 0
48-
operators = {'TOUCHES':1,'OVERLAPS':2,'WITHIN':4}
49-
5043
def defineCharacteristics(self):
5144
self.name = 'Extract by location'
5245
self.group = 'Vector selection tools'
5346
self.addParameter(ParameterVector(self.INPUT,
54-
self.tr('Layer to select from'), [ParameterVector.VECTOR_TYPE_ANY]))
47+
self.tr('Layer to select from'),
48+
[ParameterVector.VECTOR_TYPE_ANY]))
5549
self.addParameter(ParameterVector(self.INTERSECT,
5650
self.tr('Additional layer (intersection layer)'),
5751
[ParameterVector.VECTOR_TYPE_ANY]))
58-
self.addParameter(ParameterBoolean(
59-
self.TOUCHES,
60-
self.tr('Include input features that touch the selection features'),
61-
[True]))
62-
self.addParameter(ParameterBoolean(
63-
self.OVERLAPS,
64-
self.tr('Include input features that overlap/cross the selection features'),
65-
[True]))
66-
self.addParameter(ParameterBoolean(
67-
self.WITHIN,
68-
self.tr('Include input features completely within the selection features'),
69-
[True]))
52+
self.addParameter(ParameterGeometryPredicate(self.PREDICATE,
53+
self.tr('Geometric predicate'),
54+
left=self.INPUT, right=self.INTERSECT))
7055
self.addOutput(OutputVector(self.OUTPUT, self.tr('Selection')))
7156

7257
def processAlgorithm(self, progress):
7358
filename = self.getParameterValue(self.INPUT)
7459
layer = dataobjects.getObjectFromUri(filename)
7560
filename = self.getParameterValue(self.INTERSECT)
7661
selectLayer = dataobjects.getObjectFromUri(filename)
77-
index = vector.spatialindex(layer)
62+
predicates = self.getParameterValue(self.PREDICATE)
7863

79-
def _points_op(geomA,geomB):
80-
return geomA.intersects(geomB)
81-
82-
def _poly_lines_op(geomA,geomB):
83-
if geomA.disjoint(geomB):
84-
return False
85-
intersects = False
86-
if self.opFlags & self.operators['TOUCHES']:
87-
intersects |= geomA.touches(geomB)
88-
if not intersects and (self.opFlags & self.operators['OVERLAPS']):
89-
if geomB.type() == QGis.Line or geomA.type() == QGis.Line:
90-
intersects |= geomA.crosses(geomB)
91-
else:
92-
intersects |= geomA.overlaps(geomB)
93-
if not intersects and (self.opFlags & self.operators['WITHIN']):
94-
intersects |= geomA.contains(geomB)
95-
return intersects
96-
97-
def _sp_operator():
98-
if layer.geometryType() == QGis.Point:
99-
return _points_op
100-
else:
101-
return _poly_lines_op
102-
103-
self.opFlags = 0
104-
if self.getParameterValue(self.TOUCHES):
105-
self.opFlags |= self.operators['TOUCHES']
106-
if self.getParameterValue(self.OVERLAPS):
107-
self.opFlags |= self.operators['OVERLAPS']
108-
if self.getParameterValue(self.WITHIN):
109-
self.opFlags |= self.operators['WITHIN']
110-
111-
sp_operator = _sp_operator()
64+
index = vector.spatialindex(layer)
11265

11366
output = self.getOutputFromName(self.OUTPUT)
11467
writer = output.getVectorWriter(layer.pendingFields(),
11568
layer.dataProvider().geometryType(), layer.crs())
11669

70+
if 'disjoint' in predicates:
71+
disjoinSet = []
72+
for feat in vector.features(layer):
73+
disjoinSet.append(feat.id())
74+
11775
geom = QgsGeometry()
11876
selectedSet = []
11977
current = 0
12078
features = vector.features(selectLayer)
12179
featureCount = len(features)
12280
total = 100.0 / float(len(features))
123-
for current,f in enumerate(features):
81+
for current, f in enumerate(features):
12482
geom = QgsGeometry(f.geometry())
12583
intersects = index.intersects(geom.boundingBox())
12684
for i in intersects:
12785
request = QgsFeatureRequest().setFilterFid(i)
12886
feat = layer.getFeatures(request).next()
12987
tmpGeom = QgsGeometry(feat.geometry())
130-
if sp_operator(geom,tmpGeom):
131-
selectedSet.append(feat.id())
88+
res = False
89+
for predicate in predicates:
90+
if predicate == 'disjoint':
91+
if tmpGeom.intersects(geom):
92+
try:
93+
disjoinSet.remove(feat.id())
94+
except:
95+
pass # already removed
96+
else:
97+
if predicate == 'intersects':
98+
res = tmpGeom.intersects()
99+
elif predicate == 'contains':
100+
res = tmpGeom.contains(geom)
101+
elif predicate == 'equals':
102+
res = tmpGeom.equals(geom)
103+
elif predicate == 'touches':
104+
res = tmpGeom.touches(geom)
105+
elif predicate == 'overlaps':
106+
res = tmpGeom.overlaps(geom)
107+
elif predicate == 'within':
108+
res = tmpGeom.within(geom)
109+
elif predicate == 'crosses':
110+
res = tmpGeom.crosses(geom)
111+
if res:
112+
selectedSet.append(feat.id())
113+
break
114+
132115
progress.setPercentage(int(current * total))
133116

117+
if 'disjoint' in predicates:
118+
selectedSet = selectedSet + disjoinSet
119+
134120
for i, f in enumerate(vector.features(layer)):
135121
if f.id() in selectedSet:
136122
writer.addFeature(f)

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

+51-58
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@
2929
from processing.core.GeoAlgorithm import GeoAlgorithm
3030
from processing.core.parameters import ParameterSelection
3131
from processing.core.parameters import ParameterVector
32-
from processing.core.parameters import ParameterBoolean
32+
from processing.core.parameters import ParameterGeometryPredicate
3333
from processing.core.outputs import OutputVector
3434
from processing.tools import dataobjects, vector
3535

@@ -38,83 +38,47 @@ class SelectByLocation(GeoAlgorithm):
3838

3939
INPUT = 'INPUT'
4040
INTERSECT = 'INTERSECT'
41-
TOUCHES = 'TOUCHES'
42-
OVERLAPS = 'OVERLAPS'
43-
WITHIN = 'WITHIN'
41+
PREDICATE = 'PREDICATE'
4442
METHOD = 'METHOD'
4543
OUTPUT = 'OUTPUT'
4644

47-
METHODS = ['creating new selection', 'adding to current selection',
45+
METHODS = ['creating new selection',
46+
'adding to current selection',
4847
'removing from current selection']
49-
opFlags = 0
50-
operators = {'TOUCHES':1,'OVERLAPS':2,'WITHIN':4}
51-
5248

5349
def defineCharacteristics(self):
5450
self.name = 'Select by location'
5551
self.group = 'Vector selection tools'
56-
self.addParameter(ParameterVector(self.INPUT, 'Layer to select from',
57-
[ParameterVector.VECTOR_TYPE_ANY]))
52+
self.addParameter(ParameterVector(self.INPUT,
53+
self.tr('Layer to select from'),
54+
[ParameterVector.VECTOR_TYPE_ANY]))
5855
self.addParameter(ParameterVector(self.INTERSECT,
59-
'Additional layer (intersection layer)',
60-
[ParameterVector.VECTOR_TYPE_ANY]))
61-
self.addParameter(ParameterBoolean(self.TOUCHES,
62-
'Include input features that touch the selection features',
63-
[True]))
64-
self.addParameter(ParameterBoolean(self.OVERLAPS,
65-
'Include input features that overlap/cross the selection features',
66-
[True]))
67-
self.addParameter(ParameterBoolean(self.WITHIN,
68-
'Include input features completely within the selection features',
69-
[True]))
56+
self.tr('Additional layer (intersection layer)'),
57+
[ParameterVector.VECTOR_TYPE_ANY]))
58+
self.addParameter(ParameterGeometryPredicate(self.PREDICATE,
59+
self.tr('Geometric predicate'),
60+
left=self.INPUT, right=self.INTERSECT))
7061
self.addParameter(ParameterSelection(self.METHOD,
71-
'Modify current selection by', self.METHODS, 0))
72-
self.addOutput(OutputVector(self.OUTPUT, 'Selection', True))
62+
self.tr('Modify current selection by'),
63+
self.METHODS, 0))
64+
self.addOutput(OutputVector(self.OUTPUT, self.tr('Selection'), True))
7365

7466
def processAlgorithm(self, progress):
7567
filename = self.getParameterValue(self.INPUT)
7668
inputLayer = dataobjects.getObjectFromUri(filename)
7769
method = self.getParameterValue(self.METHOD)
7870
filename = self.getParameterValue(self.INTERSECT)
7971
selectLayer = dataobjects.getObjectFromUri(filename)
72+
predicates = self.getParameterValue(self.PREDICATE)
8073

8174
oldSelection = set(inputLayer.selectedFeaturesIds())
8275
inputLayer.removeSelection()
8376
index = vector.spatialindex(inputLayer)
8477

85-
def _points_op(geomA,geomB):
86-
return geomA.intersects(geomB)
87-
88-
def _poly_lines_op(geomA,geomB):
89-
if geomA.disjoint(geomB):
90-
return False
91-
intersects = False
92-
if self.opFlags & self.operators['TOUCHES']:
93-
intersects |= geomA.touches(geomB)
94-
if not intersects and (self.opFlags & self.operators['OVERLAPS']):
95-
if geomB.type() == QGis.Line or geomA.type() == QGis.Line:
96-
intersects |= geomA.crosses(geomB)
97-
else:
98-
intersects |= geomA.overlaps(geomB)
99-
if not intersects and (self.opFlags & self.operators['WITHIN']):
100-
intersects |= geomA.contains(geomB)
101-
return intersects
102-
103-
def _sp_operator():
104-
if inputLayer.geometryType() == QGis.Point:
105-
return _points_op
106-
else:
107-
return _poly_lines_op
108-
109-
self.opFlags = 0
110-
if self.getParameterValue(self.TOUCHES):
111-
self.opFlags |= self.operators['TOUCHES']
112-
if self.getParameterValue(self.OVERLAPS):
113-
self.opFlags |= self.operators['OVERLAPS']
114-
if self.getParameterValue(self.WITHIN):
115-
self.opFlags |= self.operators['WITHIN']
116-
117-
sp_operator = _sp_operator()
78+
if 'disjoint' in predicates:
79+
disjoinSet = []
80+
for feat in vector.features(inputLayer):
81+
disjoinSet.append(feat.id())
11882

11983
geom = QgsGeometry()
12084
selectedSet = []
@@ -123,16 +87,45 @@ def _sp_operator():
12387
total = 100.0 / float(len(features))
12488
for f in features:
12589
geom = QgsGeometry(f.geometry())
90+
12691
intersects = index.intersects(geom.boundingBox())
12792
for i in intersects:
12893
request = QgsFeatureRequest().setFilterFid(i)
12994
feat = inputLayer.getFeatures(request).next()
13095
tmpGeom = QgsGeometry(feat.geometry())
131-
if sp_operator(geom,tmpGeom):
132-
selectedSet.append(feat.id())
96+
res = False
97+
for predicate in predicates:
98+
if predicate == 'disjoint':
99+
if tmpGeom.intersects(geom):
100+
try:
101+
disjoinSet.remove(feat.id())
102+
except:
103+
pass # already removed
104+
else:
105+
if predicate == 'intersects':
106+
res = tmpGeom.intersects()
107+
elif predicate == 'contains':
108+
res = tmpGeom.contains(geom)
109+
elif predicate == 'equals':
110+
res = tmpGeom.equals(geom)
111+
elif predicate == 'touches':
112+
res = tmpGeom.touches(geom)
113+
elif predicate == 'overlaps':
114+
res = tmpGeom.overlaps(geom)
115+
elif predicate == 'within':
116+
res = tmpGeom.within(geom)
117+
elif predicate == 'crosses':
118+
res = tmpGeom.crosses(geom)
119+
if res:
120+
selectedSet.append(feat.id())
121+
break
122+
133123
current += 1
134124
progress.setPercentage(int(current * total))
135125

126+
if 'disjoint' in predicates:
127+
selectedSet = selectedSet + disjoinSet
128+
136129
if method == 1:
137130
selectedSet = list(oldSelection.union(selectedSet))
138131
elif method == 2:

0 commit comments

Comments
 (0)