def testFilterExpression(self): req = QgsFeatureRequest().setFilterExpression('a=5') self.assertEqual(req.filterType(), QgsFeatureRequest.FilterExpression) self.assertEqual(req.spatialFilterType(), Qgis.SpatialFilterType.NoFilter) self.assertEqual(req.filterFid(), -1) self.assertFalse(req.filterFids()) self.assertEqual(req.filterExpression().expression(), 'a=5') self.assertTrue(req.filterRect().isNull()) self.assertTrue(req.referenceGeometry().isNull()) # filter rect doesn't affect fids filter req.setFilterRect(QgsRectangle(1, 2, 3, 4)) self.assertEqual(req.filterFid(), -1) self.assertFalse(req.filterFids()) self.assertEqual(req.filterExpression().expression(), 'a=5') self.assertEqual(req.spatialFilterType(), Qgis.SpatialFilterType.BoundingBox) self.assertEqual(req.filterRect(), QgsRectangle(1, 2, 3, 4)) self.assertTrue(req.referenceGeometry().isNull()) req.setFilterExpression('a=8') self.assertEqual(req.filterFid(), -1) self.assertFalse(req.filterFids()) self.assertEqual(req.filterExpression().expression(), 'a=8') self.assertEqual(req.spatialFilterType(), Qgis.SpatialFilterType.BoundingBox) self.assertEqual(req.filterRect(), QgsRectangle(1, 2, 3, 4)) self.assertTrue(req.referenceGeometry().isNull())
def testDisableFilter(self): req = QgsFeatureRequest().setFilterFid(5).disableFilter() self.assertEqual(req.filterType(), QgsFeatureRequest.FilterNone) self.assertEqual(req.spatialFilterType(), Qgis.SpatialFilterType.NoFilter) req = QgsFeatureRequest().setFilterFids([5, 6]).disableFilter() self.assertEqual(req.filterType(), QgsFeatureRequest.FilterNone) self.assertEqual(req.spatialFilterType(), Qgis.SpatialFilterType.NoFilter) req = QgsFeatureRequest().setFilterExpression('a=5').disableFilter() self.assertEqual(req.filterType(), QgsFeatureRequest.FilterNone) self.assertEqual(req.spatialFilterType(), Qgis.SpatialFilterType.NoFilter) self.assertFalse(req.filterExpression()) # disable filter does not disable spatial filter req = QgsFeatureRequest().setFilterExpression('a=5').setFilterRect( QgsRectangle(1, 2, 3, 4)) req.disableFilter() self.assertEqual(req.filterType(), QgsFeatureRequest.FilterNone) self.assertEqual(req.spatialFilterType(), Qgis.SpatialFilterType.BoundingBox) self.assertEqual(req.filterRect(), QgsRectangle(1, 2, 3, 4)) self.assertFalse(req.filterExpression())
def testCombineFilter(self): req = QgsFeatureRequest() req.combineFilterExpression('b=9') self.assertEqual(req.filterType(), QgsFeatureRequest.FilterExpression) self.assertEqual(req.filterFid(), -1) self.assertFalse(req.filterFids()) self.assertEqual(req.filterExpression().expression(), 'b=9') req.combineFilterExpression('a=11') self.assertEqual(req.filterExpression().expression(), '(b=9) AND (a=11)')
def validate_sql(self): """Checks if the rule can be executed without errors :raises ValidationError: error :return: (True, None) if rule has valid SQL, (False, ValidationError) if it is not valid :rtype: tuple (bool, ValidationError) """ try: req = QgsFeatureRequest() req.setFilterExpression(self.rule) expression = req.filterExpression() if expression is None: return False, QgsExpression(self.rule).parserErrorString() if not expression.isValid(): return False, expression.parserErrorString() project = self.constraint.layer.project.qgis_project if project is None: return False, 'QGIS project was not found!' try: layer = project.mapLayers()[self.constraint.layer.qgs_layer_id] except KeyError: return False, 'QGIS layer id not found in the project!' ctx = QgsExpressionContext() ctx.appendScope(QgsExpressionContextUtils.layerScope(layer)) if not expression.prepare(ctx): return False, expression.parserErrorString( ) + '\n' + expression.evalErrorString() except Exception as ex: logger.debug('Validate SQL failed: %s' % ex) return False, ex return True, None
def testFilterExpression(self): req = QgsFeatureRequest().setFilterExpression('a=5') self.assertEqual(req.filterType(), QgsFeatureRequest.FilterExpression) self.assertEqual(req.filterFid(), -1) self.assertFalse(req.filterFids()) self.assertEqual(req.filterExpression().expression(), 'a=5') # filter rect doesn't affect fids filter req.setFilterRect(QgsRectangle(1, 2, 3, 4)) self.assertEqual(req.filterFid(), -1) self.assertFalse(req.filterFids()) self.assertEqual(req.filterExpression().expression(), 'a=5') req.setFilterExpression('a=8') self.assertEqual(req.filterFid(), -1) self.assertFalse(req.filterFids()) self.assertEqual(req.filterExpression().expression(), 'a=8') self.assertEqual(req.filterRect(), QgsRectangle(1, 2, 3, 4))
def testDisableFilter(self): req = QgsFeatureRequest().setFilterFid(5).disableFilter() self.assertEqual(req.filterType(), QgsFeatureRequest.FilterNone) req = QgsFeatureRequest().setFilterFids([5, 6]).disableFilter() self.assertEqual(req.filterType(), QgsFeatureRequest.FilterNone) req = QgsFeatureRequest().setFilterExpression('a=5').disableFilter() self.assertEqual(req.filterType(), QgsFeatureRequest.FilterNone) self.assertFalse(req.filterExpression())
def testCombineFilter(self): req = QgsFeatureRequest() req.combineFilterExpression('b=9') self.assertEqual(req.filterType(), QgsFeatureRequest.FilterExpression) self.assertEqual(req.filterFid(), -1) self.assertFalse(req.filterFids()) self.assertEqual(req.filterExpression().expression(), 'b=9') self.assertEqual(req.spatialFilterType(), Qgis.SpatialFilterType.NoFilter) self.assertTrue(req.filterRect().isNull()) self.assertTrue(req.referenceGeometry().isNull()) req.combineFilterExpression('a=11') self.assertEqual(req.filterType(), QgsFeatureRequest.FilterExpression) self.assertEqual(req.filterExpression().expression(), '(b=9) AND (a=11)') self.assertEqual(req.spatialFilterType(), Qgis.SpatialFilterType.NoFilter) self.assertTrue(req.filterRect().isNull()) self.assertTrue(req.referenceGeometry().isNull())
def validate_sql(self): """Checks if the rule can be executed without errors :return: (True, None) if rule has valid SQL, (False, ValidationError) if it is not valid :rtype: tuple (bool, ValidationError) """ try: req = QgsFeatureRequest() req.setFilterExpression(self.get_qgis_expression()) expression = req.filterExpression() if expression is None: return False, QgsExpression(self.rule).parserErrorString() if not expression.isValid(): return False, expression.parserErrorString() except Exception as ex: logger.debug('Validate SQL failed: %s' % ex) return False, ex return True, None
def test_relationonetomany_api(self): """ Test relationonetomany filter """ cities = Layer.objects.get(project_id=self.project310.instance.pk, origname='cities10000eu') qgis_project = get_qgs_project(cities.project.qgis_file.path) qgis_layer = qgis_project.mapLayer(cities.qgs_layer_id) total_count = qgis_layer.featureCount() resp = json.loads( self._testApiCall('core-vector-api', [ 'data', 'qdjango', self.project310.instance.pk, cities.qgs_layer_id ], { FILTER_RELATIONONETOMANY_PARAM: '' }).content) self.assertEqual(resp['vector']['count'], total_count) # check filter by relations resp = json.loads( self._testApiCall( 'core-vector-api', [ 'data', 'qdjango', self.project310.instance.pk, cities.qgs_layer_id ], { FILTER_RELATIONONETOMANY_PARAM: 'cities1000_ISO2_CODE_countries__ISOCODE|18' }).content) qgs_request = QgsFeatureRequest() qgs_request.setFilterExpression('"ISO2_CODE" = \'IT\'') qgis_layer.selectByExpression( qgs_request.filterExpression().expression()) features = qgis_layer.getFeatures(qgs_request) self.assertEqual(resp['vector']['count'], len([f for f in features]))
def __get_qgis_features(qgis_layer, qgis_feature_request=None, bbox_filter=None, attribute_filters=None, search_filter=None, with_geometry=True, page=None, page_size=None, ordering=None, exclude_fields=None, extra_expression=None, extra_subset_string=None): """Private implementation for count and get""" if qgis_feature_request is None: qgis_feature_request = QgsFeatureRequest() if exclude_fields is not None: if exclude_fields == '__all__': qgis_feature_request.setNoAttributes() else: qgis_feature_request.setSubsetOfAttributes([ name for name in qgis_layer.fields().names() if name not in exclude_fields ], qgis_layer.fields()) expression_parts = [] if extra_expression is not None: expression_parts.append(extra_expression) if not with_geometry: qgis_feature_request.setFlags(QgsFeatureRequest.NoGeometry) if bbox_filter is not None: assert isinstance(bbox_filter, QgsRectangle) qgis_feature_request.setFilterRect(bbox_filter) # Ordering if ordering is not None: ascending = True if ordering.startswith('-'): ordering = ordering[1:] ascending = False order_by = QgsFeatureRequest.OrderBy( [QgsFeatureRequest.OrderByClause('"%s"' % ordering, ascending)]) qgis_feature_request.setOrderBy(order_by) # Search if search_filter is not None: exp_template = '"{field_name}" ILIKE \'%' + search_filter.replace( '\'', '\\\'') + '%\'' exp_parts = [] for f in qgis_layer.fields(): exp_parts.append( exp_template.format(field_name=f.name().replace('"', '\\"'))) expression_parts.append(' OR '.join(exp_parts)) # Attribute filters if attribute_filters is not None: exp_parts = [] for field_name, field_value in attribute_filters.items(): exp_parts.append('"{field_name}" ILIKE \'%{field_value}%\''.format( field_name=field_name.replace('"', '\\"'), field_value=str(field_value).replace('\'', '\\\''))) expression_parts.append(' AND '.join(exp_parts)) offset = 0 feature_count = qgis_layer.featureCount() if page is not None and page_size is not None: page_size = int(page_size) page = int(page) offset = page_size * (page - 1) feature_count = page_size * page # Set to max, without taking filters into account qgis_feature_request.setLimit(feature_count) else: page_size = None # make sure it's none # Fetch features if expression_parts: qgis_feature_request.combineFilterExpression( '(' + ') AND ('.join(expression_parts) + ')') logger.debug( 'Fetching features from layer {layer_name} - filter expression: {filter} - BBOX: {bbox}' .format(layer_name=qgis_layer.name(), filter=qgis_feature_request.filterExpression(), bbox=qgis_feature_request.filterRect())) features = [] original_subset_string = qgis_layer.subsetString() if extra_subset_string is not None: subset_string = original_subset_string if subset_string: qgis_layer.setSubsetString( "({original_subset_string}) AND ({extra_subset_string})". format(original_subset_string=original_subset_string, extra_subset_string=extra_subset_string)) else: qgis_layer.setSubsetString(extra_subset_string) iterator = qgis_layer.getFeatures(qgis_feature_request) try: for _ in range(offset): next(iterator) if page_size is not None: for __ in range(page_size): features.append(next(iterator)) else: while True: features.append(next(iterator)) except StopIteration: pass if extra_subset_string is not None: qgis_layer.setSubsetString(original_subset_string) return features
def name_callback(self, key, target, year): """ A callback to manage when key is "name" """ # get the index name, that should be the value of display_name in # indexs_metadata table index_name = target[key] self.feedback.pushInfo("Managing index: {}".format(index_name)) # get the metadata associated to the diplay name index_metadata = self.getIndexMetadata(index_name) index_table_name = index_metadata['index_table_name'] index_classes = index_metadata['classes'] # would be similar to: [{"low": 0,"high": 15,"value": "Bajo","color": "dark-red"},...] index_metric_column = index_metadata['metric_column'] # the column name inside index_table_name where to get the value index_hiperlinks = index_metadata.setdefault('hyperlinks', '<some hiperlinks>') # look for layer into the project having the name as: # index_name or index_table_name index_layer = QgsProject.instance().mapLayersByName(index_name) if len(index_layer) == 0: index_layer = QgsProject.instance().mapLayersByName(index_table_name) if (len(index_layer) == 0) or not index_layer[0].isValid(): raise QgsProcessingException(self.tr('Index layer: "{}" or "{}" not loaded into project or invalid, load all index tables before!').format(index_name, index_table_name)) index_layer = index_layer[0] # get the index value to the specified year expression = '"{}" = {}'.format(YEAR_COLUMN, year) # TODO: year column should be configurable request = QgsFeatureRequest() request.setFilterExpression(expression) features = index_layer.getFeatures(request) features = [f for f in features] if len(features) != 1: self.feedback.reportError(self.tr('Skipped! Found: {} features instead of only 1 with expression: "{}" for layer: "{}"').format(len(features), request.filterExpression().dump(), index_layer.name())) # then do nothing to the target return # convert index_classes json in dictionary try: index_classes = json.loads(index_classes) except Exception as ex: raise QgsProcessingException(self.tr('Configured index: "{}" classes are malformed: {}').format(index_name, str(ex))) # add the value to the target feature = features[0] percentage = feature.attribute(index_metric_column) if percentage == QVariant(): # manage if value in DB is NULL percentage = None target['percentage'] = percentage # basing of index_classes set evaluation string and related color representation # btw set before value to undefined in case the value is outside the intervals target['value'] = None if isinstance(target['percentage'], int) else target['percentage'] target['color'] = 'black' if target['percentage'] is not None: for color_class in index_classes["classes"]: if (isinstance(target['percentage'], str) and target['percentage'] == color_class["value"]): target['color'] = color_class["color"] if (not isinstance(target['percentage'], str) and target['percentage'] >= color_class["low"] and target['percentage'] < color_class["high"]): target['value'] = self.tr(color_class["value"]) target['color'] = color_class["color"] # add all hiperlinks target.setdefault('hiperlinks', []) for i, hyperlink in enumerate(index_hiperlinks.split(',')): target['hiperlinks'].append(hyperlink)
def getIndexMetadata(self, index_name): if not self.indexes_metadata: raise QgsProcessingException(self.tr('No index metadata layer setup')) request = QgsFeatureRequest().setFilterExpression('"display_name" = \'{}\''.format(index_name)) features = self.indexes_metadata.getFeatures(request) features = [f for f in features] if len(features) != 1: raise QgsProcessingException(self.tr('Found: {} features instead of only 1 with expression: "{}" for index: "{}"').format(len(features), request.filterExpression().dump(), index_name)) # get metadata in form o dict with column name and value metadata = dict(zip(features[0].fields().names(), features[0].attributes())) return metadata