Beispiel #1
0
    def layer_is_empty(self, layer):
        """
        Check if a vector layer is empty (not data)

        :param layer: qdjango Layer model instance
        :return: True or False
        :rtype: boolean
        """

        qgis_layer = get_qgis_layer(layer)
        if qgis_layer.type() == QgsMapLayer.VectorLayer:
            return not bool(count_qgis_features(qgis_layer))
        else:
            return False
Beispiel #2
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)
Beispiel #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)