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())
Beispiel #3
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)')
Beispiel #4
0
    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
Beispiel #5
0
    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))
Beispiel #6
0
    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())
Beispiel #8
0
    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
Beispiel #9
0
    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]))
Beispiel #10
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
Beispiel #11
0
    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)
Beispiel #12
0
    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