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 get_layer_fids_from_server_fids(server_fids, layer): """From a list of server_fids for a QGIS vector layer return layer fids :param server_fids: list of server_fids :param layer: QGIS vector layer to execute filtering :return: a list for QGIS vector layer fids :rtype: list """ qgis_feature_request = QgsFeatureRequest() exp = expression_from_server_fids(server_fids, layer.dataProvider()) qgis_feature_request.combineFilterExpression(exp) features = get_qgis_features(layer, qgis_feature_request) return [f.id() for f in features]
def get_constraint_geometry(self): """Returns the geometry from the constraint layer and rule :return: the constraint geometry and the number of matched records :rtype: tuple( MultiPolygon, integer) """ constraint_layer = get_qgis_layer(self.constraint.constraint_layer) layer = get_qgis_layer(self.constraint.layer) # Get the geometries from constraint layer and rule qgis_feature_request = QgsFeatureRequest() qgis_feature_request.combineFilterExpression(self.rule) features = get_qgis_features(constraint_layer, qgis_feature_request, exclude_fields='__all__') if not features: return '', 0 geometry = QgsMultiPolygon() for feature in features: geom = feature.geometry() if geom.isMultipart(): geom = [g for g in geom.constGet()] else: geom = [geom.constGet()] i = 0 for g in geom: geometry.insertGeometry(g.clone(), 0) i += 1 # Now, transform into a GEOS geometry if constraint_layer.crs() != layer.crs(): ct = QgsCoordinateTransform( QgsCoordinateReferenceSystem(constraint_layer.crs()), QgsCoordinateReferenceSystem(layer.crs()), QgsCoordinateTransformContext()) geometry.transform(ct) constraint_geometry = MultiPolygon.from_ewkt( 'SRID=%s;' % layer.crs().postgisSrid() + geometry.asWkt()) return constraint_geometry, constraint_geometry.num_geom
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 get_target_meshblock_ids_from_numbers( self, meshblock_numbers: List[int]) -> Dict[int, int]: """ Returns a dictionary of target meshblock feature IDs corresponding to the specified meshblock numbers :param meshblock_numbers: list of meshblock numbers to lookup """ assert self.scenario is not None request = QgsFeatureRequest() request.setSubsetOfAttributes([self.target_meshblock_number_idx]) request.setFilterExpression( QgsExpression.createFieldEqualityExpression( 'scenario_id', self.scenario)) meshblock_filter = 'meshblock_number IN ({})'.format(','.join( [str(mb) for mb in meshblock_numbers])) request.combineFilterExpression(meshblock_filter) # create dictionary of meshblock number to id mb_number_to_target_id = {} for f in self.meshblock_scenario_layer.getFeatures(request): mb_number_to_target_id[f[ self.target_meshblock_number_idx]] = f.id() return mb_number_to_target_id
def electorate_meshblocks(self, electorate_id, electorate_type: str, scenario_id) -> QgsFeatureIterator: """ Returns meshblock features currently assigned to an electorate in a given scenario :param electorate_id: electorate id :param electorate_type: electorate type, e.g. 'GN','GS','M' :param scenario_id: scenario id """ request = QgsFeatureRequest() type_field = '{}_id'.format(electorate_type.lower()) type_field_index = self.meshblock_electorate_layer.fields( ).lookupField(type_field) assert type_field_index >= 0 request.setFilterExpression( QgsExpression.createFieldEqualityExpression( 'scenario_id', scenario_id)) request.combineFilterExpression( QgsExpression.createFieldEqualityExpression( type_field, electorate_id)) return self.meshblock_electorate_layer.getFeatures(request)
def expression_eval(expression_text, project_id=None, qgs_layer_id=None, form_data=None, formatter=0): """Evaluates a QgsExpression and returns the result :param expression_text: The QgsExpression text :type expression_text: str :param project_id: ID of the qdjango project, defaults to None :type project_id: int, optional :param qgs_layer_id: ID of the QGIS Layer, defaults to None :type qgslayer_id: str, optional :param form_data: A dictionary that maps to a GeoJSON representation of the feature currently edited in the form :type form_data: dict, optional :param formatter: Indicate if form_data values contains formatter values or original features value. :type formatter: int, optional """ expression = QgsExpression(expression_text) expression_context = QgsExpressionContext() layer = None for func_name in expression.referencedFunctions(): if func_name in FORBIDDEN_FUNCTIONS: raise ExpressionForbiddenError( _('Function "{}" is not allowed for security reasons!').format( func_name)) for var_name in expression.referencedVariables(): if var_name in FORBIDDEN_VARIABLES: raise ExpressionForbiddenError( _('Variable "{}" is not allowed for security reasons!').format( var_name)) if project_id is not None: try: project = Project.objects.get(pk=project_id) if qgs_layer_id is not None: try: layer = project.layer_set.get(qgs_layer_id=qgs_layer_id) except Layer.DoesNotExist: raise ExpressionLayerError( _('QGIS layer with id "{}" could not be found!'). format(qgs_layer_id)) expression_contex = QgsExpressionContextUtils.globalProjectLayerScopes( layer.qgis_layer) else: expression_contex = QgsExpressionContextUtils.globalScope() expression_context.appendScope( QgsExpressionContextUtils.projectScope( project.qgis_project)) except Project.DoesNotExist: raise ExpressionProjectError( _('QDjango project with id "{}" could not be found!').format( project_id)) else: expression_contex = QgsExpressionContextUtils.globalScope() if form_data is not None: if layer is None: raise ExpressionLayerError( _('A valid QGIS layer is required to process form data!')) try: # Case by formatter # formatter == 1 : get featureid from layer, usually must be used with formatter form_data # formatter == 0 : default behavior if formatter == 0: fields = layer.qgis_layer.fields() form_feature = QgsJsonUtils.stringToFeatureList( json.dumps(form_data), fields, None)[0] # Set attributes manually because QgsJsonUtils does not respect order for k, v in form_data['properties'].items(): form_feature.setAttribute(k, v) else: qgis_feature_request = QgsFeatureRequest() exp = expression_from_server_fids( [form_data['id']], layer.qgis_layer.dataProvider()) qgis_feature_request.combineFilterExpression(exp) form_feature = get_qgis_features(layer.qgis_layer, qgis_feature_request)[0] expression_context.appendScope( QgsExpressionContextUtils.formScope(form_feature)) expression_context.setFeature(form_feature) except: raise ExpressionFormDataError() valid, errors = expression.checkExpression(expression_text, expression_context) if not valid: raise ExpressionParseError(errors) result = expression.evaluate(expression_context) if expression.hasEvalError(): raise ExpressionEvalError(expression.evalErrorString()) return result
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