Example #1
0
    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)')
Example #2
0
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]
Example #3
0
    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
Example #4
0
    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)
Example #7
0
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
Example #8
0
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