Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
[FEATURE][processing] Extract by attribute can extract for null/notnu…
…ll values

Adds support for filtering where an attribute value is null or not null
  • Loading branch information
nyalldawson committed Nov 17, 2016
1 parent fb8b931 commit 24ffa15
Show file tree
Hide file tree
Showing 14 changed files with 419 additions and 18 deletions.
41 changes: 23 additions & 18 deletions python/plugins/processing/algs/qgis/ExtractByAttribute.py
Expand Up @@ -51,12 +51,17 @@ class ExtractByAttribute(GeoAlgorithm):
'<',
'<=',
'begins with',
'contains'
'contains',
'is null',
'is not null'
]
STRING_OPERATORS = ['begins with',
'contains']

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

self.i18n_operators = ['=',
'!=',
Expand All @@ -65,15 +70,17 @@ def defineCharacteristics(self):
'<',
'<=',
self.tr('begins with'),
self.tr('contains')]
self.tr('contains'),
self.tr('is null'),
self.tr('is not null')]

self.addParameter(ParameterVector(self.INPUT,
self.tr('Input Layer')))
self.addParameter(ParameterTableField(self.FIELD,
self.tr('Selection attribute'), self.INPUT))
self.addParameter(ParameterSelection(self.OPERATOR,
self.tr('Operator'), self.i18n_operators))
self.addParameter(ParameterString(self.VALUE, self.tr('Value')))
self.addParameter(ParameterString(self.VALUE, self.tr('Value'), optional=True))

self.addOutput(OutputVector(self.OUTPUT, self.tr('Extracted (attribute)')))

Expand All @@ -90,25 +97,23 @@ def processAlgorithm(self, progress):
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 %s can be used only with string fields.' % 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':
expr = '{} IS NULL'.format(field_ref)
elif operator == 'is not null':
expr = '{} IS NOT NULL'.format(field_ref)
elif operator == 'begins with':
expr = """%s LIKE '%s%%'""" % (field_ref, value)
elif operator == 'contains':
expr = """%s LIKE '%%%s%%'""" % (field_ref, value)
else:
raise GeoAlgorithmExecutionException(
self.tr('Unsupported field type "%s"' % fields[idx].typeName()))
expr = '{} {} {}'.format(field_ref, operator, quoted_val)

expression = QgsExpression(expr)
if not expression.hasParserError():
Expand Down
1 change: 1 addition & 0 deletions python/plugins/processing/algs/qgis/ExtractByExpression.py
Expand Up @@ -43,6 +43,7 @@ class ExtractByExpression(GeoAlgorithm):
def defineCharacteristics(self):
self.name, self.i18n_name = self.trAlgorithm('Extract by expression')
self.group, self.i18n_group = self.trAlgorithm('Vector selection tools')
self.tags = self.tr('extract,filter,expression,field')

self.addParameter(ParameterVector(self.INPUT,
self.tr('Input Layer')))
Expand Down
2 changes: 2 additions & 0 deletions python/plugins/processing/algs/qgis/ExtractByLocation.py
Expand Up @@ -46,6 +46,8 @@ class ExtractByLocation(GeoAlgorithm):
def defineCharacteristics(self):
self.name, self.i18n_name = self.trAlgorithm('Extract by location')
self.group, self.i18n_group = self.trAlgorithm('Vector selection tools')
self.tags = self.tr('extract,filter,location,intersects,contains,within')

self.addParameter(ParameterVector(self.INPUT,
self.tr('Layer to select from')))
self.addParameter(ParameterVector(self.INTERSECT,
Expand Down
@@ -0,0 +1,32 @@
<GMLFeatureClassList>
<GMLFeatureClass>
<Name>extract_by_attribute_contains</Name>
<ElementPath>extract_by_attribute_contains</ElementPath>
<!--POLYGON-->
<GeometryType>3</GeometryType>
<SRSName>EPSG:4326</SRSName>
<DatasetSpecificInfo>
<FeatureCount>3</FeatureCount>
<ExtentXMin>-1.00000</ExtentXMin>
<ExtentXMax>6.00000</ExtentXMax>
<ExtentYMin>-1.00000</ExtentYMin>
<ExtentYMax>6.00000</ExtentYMax>
</DatasetSpecificInfo>
<PropertyDefn>
<Name>name</Name>
<ElementPath>name</ElementPath>
<Type>String</Type>
<Width>5</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,37 @@
<?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>-1</gml:X><gml:Y>-1</gml:Y></gml:coord>
<gml:coord><gml:X>6</gml:X><gml:Y>6</gml:Y></gml:coord>
</gml:Box>
</gml:boundedBy>

<gml:featureMember>
<ogr:extract_by_attribute_contains fid="polys.0">
<ogr:geometryProperty><gml:Polygon srsName="EPSG:4326"><gml:outerBoundaryIs><gml:LinearRing><gml:coordinates>-1,-1 -1,3 3,3 3,2 2,2 2,-1 -1,-1</gml:coordinates></gml:LinearRing></gml:outerBoundaryIs></gml:Polygon></ogr:geometryProperty>
<ogr:name>aaaaa</ogr:name>
<ogr:intval>33</ogr:intval>
<ogr:floatval>44.123456</ogr:floatval>
</ogr:extract_by_attribute_contains>
</gml:featureMember>
<gml:featureMember>
<ogr:extract_by_attribute_contains fid="polys.1">
<ogr:geometryProperty><gml:Polygon srsName="EPSG:4326"><gml:outerBoundaryIs><gml:LinearRing><gml:coordinates>5,5 6,4 4,4 5,5</gml:coordinates></gml:LinearRing></gml:outerBoundaryIs></gml:Polygon></ogr:geometryProperty>
<ogr:name>Aaaaa</ogr:name>
<ogr:intval>-33</ogr:intval>
<ogr:floatval>0</ogr:floatval>
</ogr:extract_by_attribute_contains>
</gml:featureMember>
<gml:featureMember>
<ogr:extract_by_attribute_contains fid="polys.2">
<ogr:geometryProperty><gml:Polygon srsName="EPSG:4326"><gml:outerBoundaryIs><gml:LinearRing><gml:coordinates>2,5 2,6 3,6 3,5 2,5</gml:coordinates></gml:LinearRing></gml:outerBoundaryIs></gml:Polygon></ogr:geometryProperty>
<ogr:name>bbaaa</ogr:name>
<ogr:floatval>0.123</ogr:floatval>
</ogr:extract_by_attribute_contains>
</gml:featureMember>
</ogr:FeatureCollection>
@@ -0,0 +1,32 @@
<GMLFeatureClassList>
<GMLFeatureClass>
<Name>extract_by_attribute_greater</Name>
<ElementPath>extract_by_attribute_greater</ElementPath>
<!--POLYGON-->
<GeometryType>3</GeometryType>
<SRSName>EPSG:4326</SRSName>
<DatasetSpecificInfo>
<FeatureCount>2</FeatureCount>
<ExtentXMin>-1.00000</ExtentXMin>
<ExtentXMax>6.00000</ExtentXMax>
<ExtentYMin>-3.00000</ExtentYMin>
<ExtentYMax>3.00000</ExtentYMax>
</DatasetSpecificInfo>
<PropertyDefn>
<Name>name</Name>
<ElementPath>name</ElementPath>
<Type>String</Type>
<Width>5</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,30 @@
<?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>-1</gml:X><gml:Y>-3</gml:Y></gml:coord>
<gml:coord><gml:X>6</gml:X><gml:Y>3</gml:Y></gml:coord>
</gml:Box>
</gml:boundedBy>

<gml:featureMember>
<ogr:extract_by_attribute_greater fid="polys.0">
<ogr:geometryProperty><gml:Polygon srsName="EPSG:4326"><gml:outerBoundaryIs><gml:LinearRing><gml:coordinates>-1,-1 -1,3 3,3 3,2 2,2 2,-1 -1,-1</gml:coordinates></gml:LinearRing></gml:outerBoundaryIs></gml:Polygon></ogr:geometryProperty>
<ogr:name>aaaaa</ogr:name>
<ogr:intval>33</ogr:intval>
<ogr:floatval>44.123456</ogr:floatval>
</ogr:extract_by_attribute_greater>
</gml:featureMember>
<gml:featureMember>
<ogr:extract_by_attribute_greater 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_greater>
</gml:featureMember>
</ogr:FeatureCollection>
@@ -0,0 +1,32 @@
<GMLFeatureClassList>
<GMLFeatureClass>
<Name>extract_by_expression_not_null</Name>
<ElementPath>extract_by_expression_not_null</ElementPath>
<!--POLYGON-->
<GeometryType>3</GeometryType>
<SRSName>EPSG:4326</SRSName>
<DatasetSpecificInfo>
<FeatureCount>5</FeatureCount>
<ExtentXMin>-1.00000</ExtentXMin>
<ExtentXMax>10.00000</ExtentXMax>
<ExtentYMin>-3.00000</ExtentYMin>
<ExtentYMax>5.00000</ExtentYMax>
</DatasetSpecificInfo>
<PropertyDefn>
<Name>name</Name>
<ElementPath>name</ElementPath>
<Type>String</Type>
<Width>5</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,51 @@
<?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>-1</gml:X><gml:Y>-3</gml:Y></gml:coord>
<gml:coord><gml:X>10</gml:X><gml:Y>5</gml:Y></gml:coord>
</gml:Box>
</gml:boundedBy>

<gml:featureMember>
<ogr:extract_by_expression_not_null fid="polys.0">
<ogr:geometryProperty><gml:Polygon srsName="EPSG:4326"><gml:outerBoundaryIs><gml:LinearRing><gml:coordinates>-1,-1 -1,3 3,3 3,2 2,2 2,-1 -1,-1</gml:coordinates></gml:LinearRing></gml:outerBoundaryIs></gml:Polygon></ogr:geometryProperty>
<ogr:name>aaaaa</ogr:name>
<ogr:intval>33</ogr:intval>
<ogr:floatval>44.123456</ogr:floatval>
</ogr:extract_by_expression_not_null>
</gml:featureMember>
<gml:featureMember>
<ogr:extract_by_expression_not_null fid="polys.1">
<ogr:geometryProperty><gml:Polygon srsName="EPSG:4326"><gml:outerBoundaryIs><gml:LinearRing><gml:coordinates>5,5 6,4 4,4 5,5</gml:coordinates></gml:LinearRing></gml:outerBoundaryIs></gml:Polygon></ogr:geometryProperty>
<ogr:name>Aaaaa</ogr:name>
<ogr:intval>-33</ogr:intval>
<ogr:floatval>0</ogr:floatval>
</ogr:extract_by_expression_not_null>
</gml:featureMember>
<gml:featureMember>
<ogr:extract_by_expression_not_null 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_expression_not_null>
</gml:featureMember>
<gml:featureMember>
<ogr:extract_by_expression_not_null fid="polys.4">
<ogr:intval>120</ogr:intval>
<ogr:floatval>-100291.43213</ogr:floatval>
</ogr:extract_by_expression_not_null>
</gml:featureMember>
<gml:featureMember>
<ogr:extract_by_expression_not_null 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_expression_not_null>
</gml:featureMember>
</ogr:FeatureCollection>
@@ -0,0 +1,27 @@
<GMLFeatureClassList>
<GMLFeatureClass>
<Name>extract_by_expression_null</Name>
<ElementPath>extract_by_expression_null</ElementPath>
<!--POLYGON-->
<GeometryType>3</GeometryType>
<SRSName>EPSG:4326</SRSName>
<DatasetSpecificInfo>
<FeatureCount>1</FeatureCount>
<ExtentXMin>2.00000</ExtentXMin>
<ExtentXMax>3.00000</ExtentXMax>
<ExtentYMin>5.00000</ExtentYMin>
<ExtentYMax>6.00000</ExtentYMax>
</DatasetSpecificInfo>
<PropertyDefn>
<Name>name</Name>
<ElementPath>name</ElementPath>
<Type>String</Type>
<Width>5</Width>
</PropertyDefn>
<PropertyDefn>
<Name>floatval</Name>
<ElementPath>floatval</ElementPath>
<Type>Real</Type>
</PropertyDefn>
</GMLFeatureClass>
</GMLFeatureClassList>
@@ -0,0 +1,21 @@
<?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>5</gml:Y></gml:coord>
<gml:coord><gml:X>3</gml:X><gml:Y>6</gml:Y></gml:coord>
</gml:Box>
</gml:boundedBy>

<gml:featureMember>
<ogr:extract_by_expression_null fid="polys.2">
<ogr:geometryProperty><gml:Polygon srsName="EPSG:4326"><gml:outerBoundaryIs><gml:LinearRing><gml:coordinates>2,5 2,6 3,6 3,5 2,5</gml:coordinates></gml:LinearRing></gml:outerBoundaryIs></gml:Polygon></ogr:geometryProperty>
<ogr:name>bbaaa</ogr:name>
<ogr:floatval>0.123</ogr:floatval>
</ogr:extract_by_expression_null>
</gml:featureMember>
</ogr:FeatureCollection>
@@ -0,0 +1,32 @@
<GMLFeatureClassList>
<GMLFeatureClass>
<Name>extract_by_expression_startswith</Name>
<ElementPath>extract_by_expression_startswith</ElementPath>
<!--POLYGON-->
<GeometryType>3</GeometryType>
<SRSName>EPSG:4326</SRSName>
<DatasetSpecificInfo>
<FeatureCount>2</FeatureCount>
<ExtentXMin>4.00000</ExtentXMin>
<ExtentXMax>10.00000</ExtentXMax>
<ExtentYMin>-3.00000</ExtentYMin>
<ExtentYMax>5.00000</ExtentYMax>
</DatasetSpecificInfo>
<PropertyDefn>
<Name>name</Name>
<ElementPath>name</ElementPath>
<Type>String</Type>
<Width>5</Width>
</PropertyDefn>
<PropertyDefn>
<Name>intval</Name>
<ElementPath>intval</ElementPath>
<Type>Integer</Type>
</PropertyDefn>
<PropertyDefn>
<Name>floatval</Name>
<ElementPath>floatval</ElementPath>
<Type>Integer</Type>
</PropertyDefn>
</GMLFeatureClass>
</GMLFeatureClassList>

0 comments on commit 24ffa15

Please sign in to comment.