Skip to content

Commit

Permalink
Merge pull request #4281 from nyalldawson/does_not_contain
Browse files Browse the repository at this point in the history
[FEATURE][processing] Add 'does not contain' to Extract By Attribute
  • Loading branch information
nyalldawson committed Mar 21, 2017
2 parents aad182f + 2a80d28 commit 2e9f996
Show file tree
Hide file tree
Showing 5 changed files with 117 additions and 28 deletions.
12 changes: 9 additions & 3 deletions python/plugins/processing/algs/qgis/ExtractByAttribute.py 100644 → 100755
Expand Up @@ -53,10 +53,12 @@ class ExtractByAttribute(GeoAlgorithm):
'begins with',
'contains',
'is null',
'is not null'
'is not null',
'does not contain'
]
STRING_OPERATORS = ['begins with',
'contains']
'contains',
'does not contain']

def defineCharacteristics(self):
self.name, self.i18n_name = self.trAlgorithm('Extract by attribute')
Expand All @@ -72,7 +74,9 @@ def defineCharacteristics(self):
self.tr('begins with'),
self.tr('contains'),
self.tr('is null'),
self.tr('is not null')]
self.tr('is not null'),
self.tr('does not contain')
]

self.addParameter(ParameterVector(self.INPUT,
self.tr('Input Layer')))
Expand Down Expand Up @@ -112,6 +116,8 @@ def processAlgorithm(self, feedback):
expr = """%s LIKE '%s%%'""" % (field_ref, value)
elif operator == 'contains':
expr = """%s LIKE '%%%s%%'""" % (field_ref, value)
elif operator == 'does not contain':
expr = """%s NOT LIKE '%%%s%%'""" % (field_ref, value)
else:
expr = '{} {} {}'.format(field_ref, operator, quoted_val)

Expand Down
58 changes: 33 additions & 25 deletions python/plugins/processing/algs/qgis/SelectByAttribute.py
Expand Up @@ -51,21 +51,32 @@ class SelectByAttribute(GeoAlgorithm):
'<',
'<=',
'begins with',
'contains'
'contains',
'is null',
'is not null',
'does not contain'
]
STRING_OPERATORS = ['begins with',
'contains',
'does not contain']

def defineCharacteristics(self):
self.name, self.i18n_name = self.trAlgorithm('Select by attribute')
self.group, self.i18n_group = self.trAlgorithm('Vector selection tools')
self.tags = self.tr('select,attribute,value,contains,null,field')

self.i18n_operators = ['=',
'!=',
'>',
'>=',
'<',
'<=',
self.tr('begins with '),
self.tr('contains')]
self.tr('begins with'),
self.tr('contains'),
self.tr('is null'),
self.tr('is not null'),
self.tr('does not contain')
]

self.addParameter(ParameterVector(self.INPUT,
self.tr('Input Layer')))
Expand All @@ -89,32 +100,29 @@ def processAlgorithm(self, feedback):
idx = layer.fields().lookupField(fieldName)
fieldType = fields[idx].type()

if fieldType != QVariant.String and operator in self.OPERATORS[-2:]:
op = ''.join(['"%s", ' % o for o in self.OPERATORS[-2:]])
if fieldType != QVariant.String and operator in self.STRING_OPERATORS:
op = ''.join(['"%s", ' % o for o in self.STRING_OPERATORS])
raise GeoAlgorithmExecutionException(
self.tr('Operators {0} can be used only with string fields.').format(op))

if fieldType in [QVariant.Int, QVariant.Double, QVariant.UInt, QVariant.LongLong, QVariant.ULongLong]:
expr = '"%s" %s %s' % (fieldName, operator, value)
elif fieldType == QVariant.String:
if operator not in self.OPERATORS[-2:]:
expr = """"%s" %s '%s'""" % (fieldName, operator, value)
elif operator == 'begins with':
expr = """"%s" LIKE '%s%%'""" % (fieldName, value)
elif operator == 'contains':
expr = """"%s" LIKE '%%%s%%'""" % (fieldName, value)
elif fieldType in [QVariant.Date, QVariant.DateTime]:
expr = """"%s" %s '%s'""" % (fieldName, operator, value)
field_ref = QgsExpression.quotedColumnRef(fieldName)
quoted_val = QgsExpression.quotedValue(value)
if operator == 'is null':
expression_string = '{} IS NULL'.format(field_ref)
elif operator == 'is not null':
expression_string = '{} IS NOT NULL'.format(field_ref)
elif operator == 'begins with':
expression_string = """%s LIKE '%s%%'""" % (field_ref, value)
elif operator == 'contains':
expression_string = """%s LIKE '%%%s%%'""" % (field_ref, value)
elif operator == 'does not contain':
expression_string = """%s NOT LIKE '%%%s%%'""" % (field_ref, value)
else:
raise GeoAlgorithmExecutionException(
self.tr('Unsupported field type "{0}"').format(fields[idx].typeName()))
expression_string = '{} {} {}'.format(field_ref, operator, quoted_val)

qExp = QgsExpression(expr)
if not qExp.hasParserError():
qReq = QgsFeatureRequest(qExp).setSubsetOfAttributes([])
else:
raise GeoAlgorithmExecutionException(qExp.parserErrorString())
selected = [f.id() for f in layer.getFeatures(qReq)]
expression = QgsExpression(expression_string)
if expression.hasParserError():
raise GeoAlgorithmExecutionException(expression.parserErrorString())

layer.selectByIds(selected)
layer.selectByExpression(expression_string)
self.setOutputValue(self.OUTPUT, fileName)
@@ -0,0 +1,32 @@
<GMLFeatureClassList>
<GMLFeatureClass>
<Name>extract_by_attribute_does_not_contain</Name>
<ElementPath>extract_by_attribute_does_not_contain</ElementPath>
<!--POLYGON-->
<GeometryType>3</GeometryType>
<SRSName>EPSG:4326</SRSName>
<DatasetSpecificInfo>
<FeatureCount>2</FeatureCount>
<ExtentXMin>2.00000</ExtentXMin>
<ExtentXMax>10.00000</ExtentXMax>
<ExtentYMin>-3.00000</ExtentYMin>
<ExtentYMax>2.00000</ExtentYMax>
</DatasetSpecificInfo>
<PropertyDefn>
<Name>name</Name>
<ElementPath>name</ElementPath>
<Type>String</Type>
<Width>4</Width>
</PropertyDefn>
<PropertyDefn>
<Name>intval</Name>
<ElementPath>intval</ElementPath>
<Type>Integer</Type>
</PropertyDefn>
<PropertyDefn>
<Name>floatval</Name>
<ElementPath>floatval</ElementPath>
<Type>Real</Type>
</PropertyDefn>
</GMLFeatureClass>
</GMLFeatureClassList>
@@ -0,0 +1,29 @@
<?xml version="1.0" encoding="utf-8" ?>
<ogr:FeatureCollection
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation=""
xmlns:ogr="http://ogr.maptools.org/"
xmlns:gml="http://www.opengis.net/gml">
<gml:boundedBy>
<gml:Box>
<gml:coord><gml:X>2</gml:X><gml:Y>-3</gml:Y></gml:coord>
<gml:coord><gml:X>10</gml:X><gml:Y>2</gml:Y></gml:coord>
</gml:Box>
</gml:boundedBy>

<gml:featureMember>
<ogr:extract_by_attribute_does_not_contain fid="polys.3">
<ogr:geometryProperty><gml:Polygon srsName="EPSG:4326"><gml:outerBoundaryIs><gml:LinearRing><gml:coordinates>6,1 10,1 10,-3 6,-3 6,1</gml:coordinates></gml:LinearRing></gml:outerBoundaryIs><gml:innerBoundaryIs><gml:LinearRing><gml:coordinates>7,0 7,-2 9,-2 9,0 7,0</gml:coordinates></gml:LinearRing></gml:innerBoundaryIs></gml:Polygon></ogr:geometryProperty>
<ogr:name>ASDF</ogr:name>
<ogr:intval>0</ogr:intval>
</ogr:extract_by_attribute_does_not_contain>
</gml:featureMember>
<gml:featureMember>
<ogr:extract_by_attribute_does_not_contain fid="polys.5">
<ogr:geometryProperty><gml:Polygon srsName="EPSG:4326"><gml:outerBoundaryIs><gml:LinearRing><gml:coordinates>3,2 6,1 6,-3 2,-1 2,2 3,2</gml:coordinates></gml:LinearRing></gml:outerBoundaryIs></gml:Polygon></ogr:geometryProperty>
<ogr:name>elim</ogr:name>
<ogr:intval>2</ogr:intval>
<ogr:floatval>3.33</ogr:floatval>
</ogr:extract_by_attribute_does_not_contain>
</gml:featureMember>
</ogr:FeatureCollection>
14 changes: 14 additions & 0 deletions python/plugins/processing/tests/testdata/qgis_algorithm_tests.yaml
Expand Up @@ -1523,6 +1523,20 @@ tests:
name: expected/extract_by_attribute_contains.gml
type: vector

- algorithm: qgis:extractbyattribute
name: Extract by attribute (does not contain)
params:
FIELD: name
INPUT:
name: polys.gml
type: vector
OPERATOR: '10'
VALUE: a
results:
OUTPUT:
name: expected/extract_by_attribute_does_not_contain.gml
type: vector

- algorithm: qgis:extractbyattribute
name: Extract by attribute (greater)
params:
Expand Down

0 comments on commit 2e9f996

Please sign in to comment.