Skip to content

Commit

Permalink
Add ParameterGoemetryPredicate and use it in XxxByLocation algorithms
Browse files Browse the repository at this point in the history
  • Loading branch information
arnaud-morvan committed Feb 6, 2015
1 parent 88c928a commit 84936e5
Show file tree
Hide file tree
Showing 12 changed files with 490 additions and 129 deletions.
104 changes: 45 additions & 59 deletions python/plugins/processing/algs/qgis/ExtractByLocation.py
Expand Up @@ -28,7 +28,7 @@
from qgis.core import QGis, QgsFeatureRequest, QgsGeometry
from processing.core.GeoAlgorithm import GeoAlgorithm
from processing.core.parameters import ParameterVector
from processing.core.parameters import ParameterBoolean
from processing.core.parameters import ParameterGeometryPredicate
from processing.core.outputs import OutputVector
from processing.tools import dataobjects, vector

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

INPUT = 'INPUT'
INTERSECT = 'INTERSECT'
TOUCHES = 'TOUCHES'
OVERLAPS = 'OVERLAPS'
WITHIN = 'WITHIN'
PREDICATE = 'PREDICATE'
OUTPUT = 'OUTPUT'

METHODS = ['creating new selection', 'adding to current selection',
'removing from current selection']
opFlags = 0
operators = {'TOUCHES':1,'OVERLAPS':2,'WITHIN':4}

def defineCharacteristics(self):
self.name = 'Extract by location'
self.group = 'Vector selection tools'
self.addParameter(ParameterVector(self.INPUT,
self.tr('Layer to select from'), [ParameterVector.VECTOR_TYPE_ANY]))
self.tr('Layer to select from'),
[ParameterVector.VECTOR_TYPE_ANY]))
self.addParameter(ParameterVector(self.INTERSECT,
self.tr('Additional layer (intersection layer)'),
[ParameterVector.VECTOR_TYPE_ANY]))
self.addParameter(ParameterBoolean(
self.TOUCHES,
self.tr('Include input features that touch the selection features'),
[True]))
self.addParameter(ParameterBoolean(
self.OVERLAPS,
self.tr('Include input features that overlap/cross the selection features'),
[True]))
self.addParameter(ParameterBoolean(
self.WITHIN,
self.tr('Include input features completely within the selection features'),
[True]))
self.addParameter(ParameterGeometryPredicate(self.PREDICATE,
self.tr('Geometric predicate'),
left=self.INPUT, right=self.INTERSECT))
self.addOutput(OutputVector(self.OUTPUT, self.tr('Selection')))

def processAlgorithm(self, progress):
filename = self.getParameterValue(self.INPUT)
layer = dataobjects.getObjectFromUri(filename)
filename = self.getParameterValue(self.INTERSECT)
selectLayer = dataobjects.getObjectFromUri(filename)
index = vector.spatialindex(layer)
predicates = self.getParameterValue(self.PREDICATE)

def _points_op(geomA,geomB):
return geomA.intersects(geomB)

def _poly_lines_op(geomA,geomB):
if geomA.disjoint(geomB):
return False
intersects = False
if self.opFlags & self.operators['TOUCHES']:
intersects |= geomA.touches(geomB)
if not intersects and (self.opFlags & self.operators['OVERLAPS']):
if geomB.type() == QGis.Line or geomA.type() == QGis.Line:
intersects |= geomA.crosses(geomB)
else:
intersects |= geomA.overlaps(geomB)
if not intersects and (self.opFlags & self.operators['WITHIN']):
intersects |= geomA.contains(geomB)
return intersects

def _sp_operator():
if layer.geometryType() == QGis.Point:
return _points_op
else:
return _poly_lines_op

self.opFlags = 0
if self.getParameterValue(self.TOUCHES):
self.opFlags |= self.operators['TOUCHES']
if self.getParameterValue(self.OVERLAPS):
self.opFlags |= self.operators['OVERLAPS']
if self.getParameterValue(self.WITHIN):
self.opFlags |= self.operators['WITHIN']

sp_operator = _sp_operator()
index = vector.spatialindex(layer)

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

if 'disjoint' in predicates:
disjoinSet = []
for feat in vector.features(layer):
disjoinSet.append(feat.id())

geom = QgsGeometry()
selectedSet = []
current = 0
features = vector.features(selectLayer)
featureCount = len(features)
total = 100.0 / float(len(features))
for current,f in enumerate(features):
for current, f in enumerate(features):
geom = QgsGeometry(f.geometry())
intersects = index.intersects(geom.boundingBox())
for i in intersects:
request = QgsFeatureRequest().setFilterFid(i)
feat = layer.getFeatures(request).next()
tmpGeom = QgsGeometry(feat.geometry())
if sp_operator(geom,tmpGeom):
selectedSet.append(feat.id())
res = False
for predicate in predicates:
if predicate == 'disjoint':
if tmpGeom.intersects(geom):
try:
disjoinSet.remove(feat.id())
except:
pass # already removed
else:
if predicate == 'intersects':
res = tmpGeom.intersects()
elif predicate == 'contains':
res = tmpGeom.contains(geom)
elif predicate == 'equals':
res = tmpGeom.equals(geom)
elif predicate == 'touches':
res = tmpGeom.touches(geom)
elif predicate == 'overlaps':
res = tmpGeom.overlaps(geom)
elif predicate == 'within':
res = tmpGeom.within(geom)
elif predicate == 'crosses':
res = tmpGeom.crosses(geom)
if res:
selectedSet.append(feat.id())
break

progress.setPercentage(int(current * total))

if 'disjoint' in predicates:
selectedSet = selectedSet + disjoinSet

for i, f in enumerate(vector.features(layer)):
if f.id() in selectedSet:
writer.addFeature(f)
Expand Down
109 changes: 51 additions & 58 deletions python/plugins/processing/algs/qgis/SelectByLocation.py
Expand Up @@ -29,7 +29,7 @@
from processing.core.GeoAlgorithm import GeoAlgorithm
from processing.core.parameters import ParameterSelection
from processing.core.parameters import ParameterVector
from processing.core.parameters import ParameterBoolean
from processing.core.parameters import ParameterGeometryPredicate
from processing.core.outputs import OutputVector
from processing.tools import dataobjects, vector

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

INPUT = 'INPUT'
INTERSECT = 'INTERSECT'
TOUCHES = 'TOUCHES'
OVERLAPS = 'OVERLAPS'
WITHIN = 'WITHIN'
PREDICATE = 'PREDICATE'
METHOD = 'METHOD'
OUTPUT = 'OUTPUT'

METHODS = ['creating new selection', 'adding to current selection',
METHODS = ['creating new selection',
'adding to current selection',
'removing from current selection']
opFlags = 0
operators = {'TOUCHES':1,'OVERLAPS':2,'WITHIN':4}


def defineCharacteristics(self):
self.name = 'Select by location'
self.group = 'Vector selection tools'
self.addParameter(ParameterVector(self.INPUT, 'Layer to select from',
[ParameterVector.VECTOR_TYPE_ANY]))
self.addParameter(ParameterVector(self.INPUT,
self.tr('Layer to select from'),
[ParameterVector.VECTOR_TYPE_ANY]))
self.addParameter(ParameterVector(self.INTERSECT,
'Additional layer (intersection layer)',
[ParameterVector.VECTOR_TYPE_ANY]))
self.addParameter(ParameterBoolean(self.TOUCHES,
'Include input features that touch the selection features',
[True]))
self.addParameter(ParameterBoolean(self.OVERLAPS,
'Include input features that overlap/cross the selection features',
[True]))
self.addParameter(ParameterBoolean(self.WITHIN,
'Include input features completely within the selection features',
[True]))
self.tr('Additional layer (intersection layer)'),
[ParameterVector.VECTOR_TYPE_ANY]))
self.addParameter(ParameterGeometryPredicate(self.PREDICATE,
self.tr('Geometric predicate'),
left=self.INPUT, right=self.INTERSECT))
self.addParameter(ParameterSelection(self.METHOD,
'Modify current selection by', self.METHODS, 0))
self.addOutput(OutputVector(self.OUTPUT, 'Selection', True))
self.tr('Modify current selection by'),
self.METHODS, 0))
self.addOutput(OutputVector(self.OUTPUT, self.tr('Selection'), True))

def processAlgorithm(self, progress):
filename = self.getParameterValue(self.INPUT)
inputLayer = dataobjects.getObjectFromUri(filename)
method = self.getParameterValue(self.METHOD)
filename = self.getParameterValue(self.INTERSECT)
selectLayer = dataobjects.getObjectFromUri(filename)
predicates = self.getParameterValue(self.PREDICATE)

oldSelection = set(inputLayer.selectedFeaturesIds())
inputLayer.removeSelection()
index = vector.spatialindex(inputLayer)

def _points_op(geomA,geomB):
return geomA.intersects(geomB)

def _poly_lines_op(geomA,geomB):
if geomA.disjoint(geomB):
return False
intersects = False
if self.opFlags & self.operators['TOUCHES']:
intersects |= geomA.touches(geomB)
if not intersects and (self.opFlags & self.operators['OVERLAPS']):
if geomB.type() == QGis.Line or geomA.type() == QGis.Line:
intersects |= geomA.crosses(geomB)
else:
intersects |= geomA.overlaps(geomB)
if not intersects and (self.opFlags & self.operators['WITHIN']):
intersects |= geomA.contains(geomB)
return intersects

def _sp_operator():
if inputLayer.geometryType() == QGis.Point:
return _points_op
else:
return _poly_lines_op

self.opFlags = 0
if self.getParameterValue(self.TOUCHES):
self.opFlags |= self.operators['TOUCHES']
if self.getParameterValue(self.OVERLAPS):
self.opFlags |= self.operators['OVERLAPS']
if self.getParameterValue(self.WITHIN):
self.opFlags |= self.operators['WITHIN']

sp_operator = _sp_operator()
if 'disjoint' in predicates:
disjoinSet = []
for feat in vector.features(inputLayer):
disjoinSet.append(feat.id())

geom = QgsGeometry()
selectedSet = []
Expand All @@ -123,16 +87,45 @@ def _sp_operator():
total = 100.0 / float(len(features))
for f in features:
geom = QgsGeometry(f.geometry())

intersects = index.intersects(geom.boundingBox())
for i in intersects:
request = QgsFeatureRequest().setFilterFid(i)
feat = inputLayer.getFeatures(request).next()
tmpGeom = QgsGeometry(feat.geometry())
if sp_operator(geom,tmpGeom):
selectedSet.append(feat.id())
res = False
for predicate in predicates:
if predicate == 'disjoint':
if tmpGeom.intersects(geom):
try:
disjoinSet.remove(feat.id())
except:
pass # already removed
else:
if predicate == 'intersects':
res = tmpGeom.intersects()
elif predicate == 'contains':
res = tmpGeom.contains(geom)
elif predicate == 'equals':
res = tmpGeom.equals(geom)
elif predicate == 'touches':
res = tmpGeom.touches(geom)
elif predicate == 'overlaps':
res = tmpGeom.overlaps(geom)
elif predicate == 'within':
res = tmpGeom.within(geom)
elif predicate == 'crosses':
res = tmpGeom.crosses(geom)
if res:
selectedSet.append(feat.id())
break

current += 1
progress.setPercentage(int(current * total))

if 'disjoint' in predicates:
selectedSet = selectedSet + disjoinSet

if method == 1:
selectedSet = list(oldSelection.union(selectedSet))
elif method == 2:
Expand Down

2 comments on commit 84936e5

@badewannencaptain
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will the documentation also be updated? See http://gis.stackexchange.com/a/151005/49233

@arnaud-morvan
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for pointing this.
I've just seen that you've made a pull request on QGIS-Documentation for select and extract by location.
I will made one for the SpatialJoin (Join attributes by location).

For information, there is some tools in processing.tools.help to generate algorithm help files, but the help files layout has recently been changed on QGIS-Documentation without updating the help files generator.

Please sign in to comment.