Skip to content
Permalink
Browse files

Add ParameterGoemetryPredicate and use it in XxxByLocation algorithms

  • Loading branch information
arnaud-morvan committed Feb 6, 2015
1 parent 88c928a commit 84936e59e76dc33ae08873952e5643570d8ef40a
@@ -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

@@ -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)
@@ -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

@@ -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 = []
@@ -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:

2 comments on commit 84936e5

@badewannencaptain

This comment has been minimized.

Copy link

@badewannencaptain badewannencaptain replied Jun 15, 2015

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

@arnaud-morvan

This comment has been minimized.

Copy link
Contributor Author

@arnaud-morvan arnaud-morvan replied Jun 16, 2015

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.
You can’t perform that action at this time.