Exemple #1
0
    def testGetQgisFeaturesAttributeFilters(self):
        """Test QGIS API attribute filters"""

        qgis_layer = get_qgis_layer(self.layer)
        self.assertTrue(qgis_layer.isValid())

        features = get_qgis_features(qgis_layer,
                                     attribute_filters={'name': 'a point'})
        self.assertEqual(len(features), 1)
        self.assertTrue(features[0].id(), 1)

        features = get_qgis_features(
            qgis_layer, attribute_filters={'name': 'another point'})
        self.assertEqual(len(features), 1)
        self.assertTrue(features[0].id(), 2)

        features = get_qgis_features(qgis_layer,
                                     attribute_filters={
                                         'name': 'another point',
                                         'pkuid': 2
                                     })
        self.assertEqual(len(features), 1)
        self.assertTrue(features[0].id(), 2)

        features = get_qgis_features(qgis_layer,
                                     attribute_filters={
                                         'name': 'another point',
                                         'pkuid': 9999
                                     })
        self.assertEqual(len(features), 0)

        features = get_qgis_features(
            qgis_layer, attribute_filters={'name': 'not_existent'})
        self.assertEqual(len(features), 0)
Exemple #2
0
    def testGetQgisFeaturesExcludeFields(self):
        """Test QGIS API get_qgis_features with exclude fields filter"""

        qgis_layer = get_qgis_layer(self.layer)
        self.assertTrue(qgis_layer.isValid())

        features = get_qgis_features(qgis_layer, exclude_fields=['pkuid'])
        self.assertEqual(len(features), 2)
        self.assertIsNone(features[0].attribute('pkuid'))
        self.assertIsNotNone(features[0].attribute('name'))

        features = get_qgis_features(qgis_layer, exclude_fields=['name'])
        self.assertEqual(len(features), 2)
        self.assertIsNotNone(features[0].attribute('pkuid'))
        self.assertIsNone(features[0].attribute('name'))
Exemple #3
0
    def testGetQgisFeaturesBbox(self):
        """Test QGIS API get_qgis_features with BBOX filter"""

        qgis_layer = get_qgis_layer(self.layer)
        self.assertTrue(qgis_layer.isValid())

        features = get_qgis_features(qgis_layer,
                                     bbox_filter=qgis_layer.extent())
        self.assertEqual(len(features), 2)

        bbox = QgsRectangle(qgis_layer.extent().xMinimum(),
                            qgis_layer.extent().yMinimum(),
                            qgis_layer.extent().xMinimum() + 1,
                            qgis_layer.extent().yMinimum() + 1)

        features = get_qgis_features(qgis_layer, bbox_filter=bbox)
        self.assertEqual(len(features), 1)
Exemple #4
0
    def testGetQgisFeaturesAll(self):
        """Test QGIS API get_qgis_features with all features"""

        qgis_layer = get_qgis_layer(self.layer)
        self.assertTrue(qgis_layer.isValid())

        # Test get all features
        features = get_qgis_features(qgis_layer)
        self.assertEqual(len(features), qgis_layer.featureCount())
        # Check has geometry
        self.assertFalse(features[0].geometry().isNull())

        # Get all features, no geometry
        features = get_qgis_features(qgis_layer, with_geometry=False)
        self.assertEqual(len(features), qgis_layer.featureCount())
        # Check has geometry
        self.assertTrue(features[0].geometry().isNull())
Exemple #5
0
    def testGetQgisFeaturesSearch(self):
        """Test QGIS API get_qgis_features with search filter"""

        qgis_layer = get_qgis_layer(self.layer)
        self.assertTrue(qgis_layer.isValid())

        features = get_qgis_features(qgis_layer, search_filter='1')
        self.assertEqual(len(features), 1)
        self.assertTrue(features[0].id(), 1)

        features = get_qgis_features(qgis_layer, search_filter='another')
        self.assertEqual(len(features), 1)
        self.assertTrue(features[0].id(), 2)

        features = get_qgis_features(qgis_layer, search_filter='point')
        self.assertEqual(len(features), 2)

        # not existent
        features = get_qgis_features(qgis_layer, search_filter='not_exists')
        self.assertEqual(len(features), 0)
Exemple #6
0
    def testGetQgisFeaturesCombinedFilters(self):
        """Test QGIS API attribute combined filters"""

        qgis_layer = get_qgis_layer(self.layer)
        self.assertTrue(qgis_layer.isValid())

        features = get_qgis_features(qgis_layer, attribute_filters={
            'name': 'a point'
        }, search_filter='not_existent')
        self.assertEqual(len(features), 0)

        features = get_qgis_features(qgis_layer, attribute_filters={
            'name': 'another point'
        }, search_filter='1')
        self.assertEqual(len(features), 0)

        features = get_qgis_features(qgis_layer, attribute_filters={
            'name': 'another point'
        }, search_filter='2')
        self.assertEqual(len(features), 1)
        self.assertTrue(features[0].id(), 2)
Exemple #7
0
    def testGetQgisFeaturesPagination(self):
        """Test QGIS API get_qgis_features with pagination"""

        qgis_layer = get_qgis_layer(self.layer)
        self.assertTrue(qgis_layer.isValid())

        # Test get first page
        features = get_qgis_features(qgis_layer, page=1, page_size=1)
        self.assertEqual(len(features), 1)
        self.assertTrue(features[0].id(), 1)

        # second page
        features = get_qgis_features(qgis_layer, page=1, page_size=1)
        self.assertEqual(len(features), 1)
        self.assertTrue(features[0].id(), 2)

        # out of range offset
        features = get_qgis_features(qgis_layer, page=100, page_size=1)
        self.assertEqual(len(features), 0)

        # out of range feature_count
        features = get_qgis_features(qgis_layer, page_size=1000)
        self.assertEqual(len(features), 2)
Exemple #8
0
    def testGetQgisFeaturesOrdering(self):
        """Test QGIS API get_qgis_features with ordering"""

        qgis_layer = get_qgis_layer(self.layer)
        self.assertTrue(qgis_layer.isValid())

        # Test get first page
        features = get_qgis_features(qgis_layer, ordering='name')
        self.assertEqual(len(features), 2)
        self.assertTrue(features[0].id(), 1)

        # second page
        features = get_qgis_features(qgis_layer, ordering='name')
        self.assertEqual(len(features), 2)
        self.assertTrue(features[0].id(), 2)

        # Test get first page
        features = get_qgis_features(qgis_layer, ordering='-name')
        self.assertEqual(len(features), 2)
        self.assertTrue(features[0].id(), 2)

        # not existent field (ignored)
        features = get_qgis_features(qgis_layer, ordering='not_exists')
        self.assertEqual(len(features), 2)
Exemple #9
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.setFilterExpression(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
Exemple #10
0
    def testGetQgisFeaturesExtraSubsetString(self):
        """Test QGIS API get_qgis_features with subset string filter"""

        qgis_layer = get_qgis_layer(self.layer)
        self.assertTrue(qgis_layer.isValid())

        features = get_qgis_features(qgis_layer)
        self.assertEqual(len(features), 2)

        features = get_qgis_features(
            qgis_layer, extra_subset_string='name != \'another point\'')
        self.assertEqual(len(features), 1)
        self.assertEqual(features[0]['name'], 'a point')

        # Check if restored
        features = get_qgis_features(qgis_layer)
        self.assertEqual(len(features), 2)

        features = get_qgis_features(
            qgis_layer, extra_subset_string='name == \'another point\'')
        self.assertEqual(len(features), 1)
        self.assertEqual(features[0]['name'], 'another point')

        # Check if original subset string is ANDed
        qgis_layer_clone = qgis_layer.clone()
        qgis_layer_clone.setSubsetString('name == \'another point\'')
        features = get_qgis_features(qgis_layer_clone)
        self.assertEqual(len(features), 1)
        self.assertEqual(features[0]['name'], 'another point')

        features = get_qgis_features(
            qgis_layer_clone, extra_subset_string='name != \'another point\'')
        self.assertEqual(len(features), 0)

        # Check if restored
        qgis_layer_clone.setSubsetString('name == \'another point\'')
        features = get_qgis_features(qgis_layer_clone)
        self.assertEqual(len(features), 1)
        self.assertEqual(features[0]['name'], 'another point')
Exemple #11
0
    def response_data_mode(self, request, export_features=False):
        """
        Query layer and return data
        :param request: DjangoREST API request object
        :param formatter: Boolean, default False, True for to use QgsJsonExport.exportFeatures method
        :return: response dict data
        """

        # Create the QGIS feature request, it will be passed through filters
        # and to the final QGIS API get features call.
        qgis_feature_request = QgsFeatureRequest()

        # Prepare arguments for the get feature call
        kwargs = {}

        # Apply filter backends, store original subset string
        original_subset_string = self.metadata_layer.qgis_layer.subsetString()
        if hasattr(self, 'filter_backends'):
            for backend in self.filter_backends:
                backend().apply_filter(request, self.metadata_layer.qgis_layer, qgis_feature_request, self)

        # Paging cannot be a backend filter
        if 'page' in request.query_params:
            kwargs['page'] = request.query_params.get('page')
            kwargs['page_size'] = request.query_params.get('page_size', 10)

        self.features = get_qgis_features(
            self.metadata_layer.qgis_layer, qgis_feature_request, **kwargs)
        ex = QgsJsonExporter(self.metadata_layer.qgis_layer)

        # If 'unique' request params is set,
        # api return a list of unique
        # field name sent with 'unique' param.
        # --------------------------------------
        # IDEA:     for big data it'll be iterate over features to get unique
        #           c++ iteration is fast. Instead memory layer fith to much features can be a problem.
        if 'unique' in request.query_params:

            vl = QgsVectorLayer(QgsWkbTypes.displayString(self.metadata_layer.qgis_layer.wkbType()),
                                "temporary_vector", "memory")
            pr = vl.dataProvider()

            # add fields
            pr.addAttributes(self.metadata_layer.qgis_layer.fields())
            vl.updateFields()  # tell the vector layer to fetch changes from the provider

            res = pr.addFeatures(self.features)

            uniques = vl.uniqueValues(
                self.metadata_layer.qgis_layer.fields().indexOf(request.query_params.get('unique'))
            )

            values = []
            for u in uniques:
                try:
                    if u:
                        values.append(json.loads(QgsJsonUtils.encodeValue(u)))
                except Exception as e:
                    logger.error(f'Response vector widget unique: {e}')
                    continue

            self.results.update({
                'data': values,
                'count': len(values)
            })

            del(vl)

        else:

            # patch for return GeoJson feature with CRS different from WGS84
            # TODO: use .setTransformGeometries( false ) with QGIS >= 3.12
            ex.setSourceCrs(QgsCoordinateReferenceSystem('EPSG:4326'))

            # check for formatter query url param and check if != 0
            if 'formatter' in request.query_params:
                formatter = request.query_params.get('formatter')
                if formatter.isnumeric() and int(formatter) == 0:
                    export_features = False
                else:
                    export_features = True

            if export_features:
                feature_collection = json.loads(ex.exportFeatures(self.features))
            else:

                # to exclude QgsFormater used into QgsJsonjExporter is necessary build by hand single json feature
                ex.setIncludeAttributes(False)

                feature_collection = {
                    'type': 'FeatureCollection',
                    'features': []
                }

                for feature in self.features:
                    fnames = [f.name() for f in feature.fields()]
                    feature_collection['features'].append(
                        json.loads(ex.exportFeature(feature, dict(zip(fnames, feature.attributes()))))
                    )



            # FIXME: QGIS api reprojecting?
            # Reproject if necessary
            # if self.reproject:
            #    self.reproject_featurecollection(feature_collection)

            # Change media
            self.change_media(feature_collection)

            self.results.update(APIVectorLayerStructure(**{
                'data': feature_collection,
                'count': count_qgis_features(self.metadata_layer.qgis_layer, qgis_feature_request, **kwargs),
                'geometryType': self.metadata_layer.geometry_type,
            }).as_dict())

            # FIXME: add extra fields data by signals and receivers
            # FIXME: featurecollection = post_serialize_maplayer.send(layer_serializer, layer=self.layer_name)
            # FIXME: Not sure how to map this to the new QGIS API

        # Restore the original subset string
        self.metadata_layer.qgis_layer.setSubsetString(original_subset_string)
Exemple #12
0
    def response_data_mode(self, request, export_features=False):
        """
        Query layer and return data
        :param request: DjangoREST API request object
        :param formatter: Boolean, default False, True for to use QgsJsonExport.exportFeatures method
        :return: response dict data
        """

        # Create the QGIS feature request, it will be passed through filters
        # and to the final QGIS API get features call.
        qgis_feature_request = QgsFeatureRequest()

        # Prepare arguments for the get feature call
        kwargs = {}

        # Apply filter backends, store original subset string
        original_subset_string = self.metadata_layer.qgis_layer.subsetString()
        if hasattr(self, 'filter_backends'):
            try:
                for backend in self.filter_backends:
                    backend().apply_filter(request, self.metadata_layer,
                                           qgis_feature_request, self)
            except Exception as e:
                raise APIException(e)

        # Paging cannot be a backend filter
        if 'page' in request.query_params:
            kwargs['page'] = request.query_params.get('page')
            kwargs['page_size'] = request.query_params.get('page_size', 10)

        # Make sure we have all attrs we need to build the server FID
        provider = self.metadata_layer.qgis_layer.dataProvider()
        if qgis_feature_request.flags() & QgsFeatureRequest.SubsetOfAttributes:
            attrs = qgis_feature_request.subsetOfAttributes()
            for attr_idx in provider.pkAttributeIndexes():
                if attr_idx not in attrs:
                    attrs.append(attr_idx)
            qgis_feature_request.setSubsetOfAttributes(attrs)

        self.features = get_qgis_features(self.metadata_layer.qgis_layer,
                                          qgis_feature_request, **kwargs)

        # Reproject feature if layer CRS != Project CRS
        if self.reproject:
            for f in self.features:
                self.reproject_feature(f)

        ex = QgsJsonExporter(self.metadata_layer.qgis_layer)

        # If 'unique' request params is set,
        # api return a list of unique
        # field name sent with 'unique' param.
        # --------------------------------------
        # IDEA:     for big data it'll be iterate over features to get unique
        #           c++ iteration is fast. Instead memory layer with too many features can be a problem.
        if 'unique' in request.query_params:

            vl = QgsVectorLayer(
                QgsWkbTypes.displayString(
                    self.metadata_layer.qgis_layer.wkbType()),
                "temporary_vector", "memory")
            pr = vl.dataProvider()

            # add fields
            pr.addAttributes(self.metadata_layer.qgis_layer.fields())
            vl.updateFields(
            )  # tell the vector layer to fetch changes from the provider

            res = pr.addFeatures(self.features)

            uniques = vl.uniqueValues(
                self.metadata_layer.qgis_layer.fields().indexOf(
                    request.query_params.get('unique')))

            values = []
            for u in uniques:
                try:
                    if u:
                        values.append(json.loads(QgsJsonUtils.encodeValue(u)))
                except Exception as e:
                    logger.error(f'Response vector widget unique: {e}')
                    continue

            # sort values
            values.sort()
            self.results.update({'data': values, 'count': len(values)})

            del (vl)

        else:

            ex.setTransformGeometries(False)

            # check for formatter query url param and check if != 0
            if 'formatter' in request.query_params:
                formatter = request.query_params.get('formatter')
                if formatter.isnumeric() and int(formatter) == 0:
                    export_features = False
                else:
                    export_features = True

            if export_features:
                feature_collection = json.loads(
                    ex.exportFeatures(self.features))
            else:

                # to exclude QgsFormater used into QgsJsonExporter is necessary build by hand single json feature
                ex.setIncludeAttributes(False)

                feature_collection = {
                    'type': 'FeatureCollection',
                    'features': []
                }

                for feature in self.features:
                    fnames = []
                    date_fields = []
                    for f in feature.fields():
                        fnames.append(f.name())
                        if f.typeName() in ('date', 'datetime', 'time'):
                            date_fields.append(f)

                    jsonfeature = json.loads(
                        ex.exportFeature(
                            feature, dict(zip(fnames, feature.attributes()))))

                    # Update date and datetime fields value if widget is active
                    if len(date_fields) > 0:
                        for f in date_fields:
                            field_idx = self.metadata_layer.qgis_layer.fields(
                            ).indexFromName(f.name())
                            options = self.metadata_layer.qgis_layer.editorWidgetSetup(
                                field_idx).config()
                            if 'field_iso_format' in options and not options[
                                    'field_iso_format']:
                                try:
                                    jsonfeature['properties'][f.name()] = feature.attribute(f.name())\
                                        .toString(options['field_format'])
                                except:
                                    pass

                    feature_collection['features'].append(jsonfeature)

            # Change media
            self.change_media(feature_collection)

            # Patch feature IDs with server featureIDs
            fids_map = {}
            for f in self.features:
                fids_map[f.id()] = server_fid(f, provider)

            for i in range(len(feature_collection['features'])):
                f = feature_collection['features'][i]
                f['id'] = fids_map[f['id']]

            self.results.update(
                APIVectorLayerStructure(
                    **{
                        'data':
                        feature_collection,
                        'count':
                        count_qgis_features(self.metadata_layer.qgis_layer,
                                            qgis_feature_request, **kwargs),
                        'geometryType':
                        self.metadata_layer.geometry_type,
                    }).as_dict())

            # FIXME: add extra fields data by signals and receivers
            # FIXME: featurecollection = post_serialize_maplayer.send(layer_serializer, layer=self.layer_name)
            # FIXME: Not sure how to map this to the new QGIS API

        # Restore the original subset string
        self.metadata_layer.qgis_layer.setSubsetString(original_subset_string)