Example #1
0
    def testSubsetAttributes(self):
        req = QgsFeatureRequest()
        self.assertFalse(req.subsetOfAttributes())
        self.assertFalse(req.flags() & QgsFeatureRequest.SubsetOfAttributes)

        req.setSubsetOfAttributes([1, 4])
        self.assertEqual(req.subsetOfAttributes(), [1, 4])
        self.assertTrue(req.flags() & QgsFeatureRequest.SubsetOfAttributes)

        req.setNoAttributes()
        self.assertEqual(req.subsetOfAttributes(), [])
        self.assertTrue(req.flags() & QgsFeatureRequest.SubsetOfAttributes)

        req.setSubsetOfAttributes([])
        self.assertFalse(req.subsetOfAttributes())
        self.assertTrue(req.flags() & QgsFeatureRequest.SubsetOfAttributes)

        req.setFlags(QgsFeatureRequest.Flags())
        f = QgsFields()
        f.append(QgsField('a', QVariant.String))
        f.append(QgsField('b', QVariant.String))
        f.append(QgsField('c', QVariant.String))
        req.setSubsetOfAttributes(['a', 'c'], f)
        self.assertEqual(req.subsetOfAttributes(), [0, 2])
        self.assertTrue(req.flags() & QgsFeatureRequest.SubsetOfAttributes)
Example #2
0
    def testAssignment(self):
        req = QgsFeatureRequest().setFilterFids([8, 9]).setFilterRect(
            QgsRectangle(1, 2, 3, 4)).setInvalidGeometryCheck(
                QgsFeatureRequest.GeometrySkipInvalid).setLimit(6).setFlags(
                    QgsFeatureRequest.ExactIntersect).setSubsetOfAttributes(
                        [1, 4]).setTimeout(6).setRequestMayBeNested(True)

        context = QgsExpressionContext()
        scope = QgsExpressionContextScope()
        scope.setVariable('a', 6)
        context.appendScope(scope)
        req.setExpressionContext(context)
        method = QgsSimplifyMethod()
        method.setMethodType(QgsSimplifyMethod.PreserveTopology)
        req.setSimplifyMethod(method)
        context = QgsCoordinateTransformContext()
        req.setDestinationCrs(QgsCoordinateReferenceSystem('EPSG:3857'),
                              context)

        req2 = QgsFeatureRequest(req)
        self.assertEqual(req2.limit(), 6)
        self.assertCountEqual(req2.filterFids(), [8, 9])
        self.assertEqual(req2.filterRect(), QgsRectangle(1, 2, 3, 4))
        self.assertEqual(req2.spatialFilterType(),
                         Qgis.SpatialFilterType.BoundingBox)
        self.assertEqual(req2.invalidGeometryCheck(),
                         QgsFeatureRequest.GeometrySkipInvalid)
        self.assertEqual(req2.expressionContext().scopeCount(), 1)
        self.assertEqual(req2.expressionContext().variable('a'), 6)
        self.assertEqual(
            req2.flags(), QgsFeatureRequest.ExactIntersect
            | QgsFeatureRequest.SubsetOfAttributes)
        self.assertEqual(req2.subsetOfAttributes(), [1, 4])
        self.assertEqual(req2.simplifyMethod().methodType(),
                         QgsSimplifyMethod.PreserveTopology)
        self.assertEqual(req2.destinationCrs().authid(), 'EPSG:3857')
        self.assertEqual(req2.timeout(), 6)
        self.assertTrue(req2.requestMayBeNested())

        # copy distance within request
        req = QgsFeatureRequest().setDistanceWithin(
            QgsGeometry.fromWkt('LineString( 0 0, 10 0, 11 2)'), 1.2)
        req2 = QgsFeatureRequest(req)
        self.assertEqual(req2.spatialFilterType(),
                         Qgis.SpatialFilterType.DistanceWithin)
        self.assertEqual(req2.referenceGeometry().asWkt(),
                         'LineString (0 0, 10 0, 11 2)')
        self.assertEqual(req2.distanceWithin(), 1.2)
        self.assertEqual(req2.filterRect(),
                         QgsRectangle(-1.2, -1.2, 12.2, 3.2))
Example #3
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)