Пример #1
0
    def response_data_mode(self, request):
        """
        Query layer and return data
        :param request: DjangoREST API request object
        :return: responce dict data
        """
        # Instance geo filtering
        #self.set_geo_filter()
        self.set_filters()

        # apply bbox filter
        self.features_layer = self.metadata_layer.get_queryset()
        if self.bbox_filter:
            self.features_layer = self.bbox_filter.filter_queryset(
                request, self.features_layer, self)

        if self.sql_filter:
            self.features_layer = self.features_layer.filter(**self.sql_filter)

        if hasattr(self, 'filter_backends'):
            for backend in list(self.filter_backends):
                self.features_layer = backend().filter_queryset(
                    self.request, self.features_layer, self)

        if 'page' in request.query_params:
            self.features_layer = self.paginate_queryset(self.features_layer)

        # instance of geoserializer
        layer_serializer = self.metadata_layer.serializer(
            self.features_layer, many=True, **self.get_geoserializer_kwargs())

        # add extra fields data by signals and receivers
        featurecollection = post_serialize_maplayer.send(layer_serializer,
                                                         layer=self.layer_name)
        if isinstance(featurecollection, list) and featurecollection:
            featurecollection = featurecollection[0][1]
        else:
            featurecollection = layer_serializer.data

        # reproject if necessary
        if self.reproject:
            self.reproject_featurecollection(featurecollection)

        # change media
        self.change_media(featurecollection)

        self.results.update(
            APIVectorLayerStructure(
                **{
                    'data':
                    featurecollection,
                    'count':
                    self._paginator.page.paginator.count if 'page' in
                    request.query_params else None,
                    'geomentryType':
                    self.metadata_layer.geometry_type,
                    'pkField':
                    self.metadata_layer.model._meta.pk.name
                }).as_dict())
Пример #2
0
    def response_config_mode(self, request):
        """
        Perform config operation, return form fields data for editing layer.
        :param request: API Object Request
        :param layer_name: editing layer name
        :return: Vector params
        """

        forms = self.get_forms()

        # add forms data if exist
        kwargs = {'fields': forms[self.layer_name]['fields']
                  } if forms and forms.get(self.layer_name) else {}

        if hasattr(self.metadata_layer, 'fields_to_exclude'):
            kwargs['exclude'] = self.metadata_layer.fields_to_exclude
        if hasattr(self.metadata_layer, 'order'):
            kwargs['order'] = self.metadata_layer.order

       # FIXME: is this a wreck of pre-qdjango era? Can we remove it and always use mapLayerAttributesFromQgisLayer ?
        if self.mapping_layer_attributes_function.__func__ == mapLayerAttributesFromQgisLayer:
            fields = list(self.mapping_layer_attributes_function.__func__(
                self.metadata_layer.qgis_layer,
                **kwargs
            ).values())
        else:
            fields = list(self.mapping_layer_attributes_function.__func__(
                self.layer,
                formField=True,
                **kwargs
            ).values())

        vector_params = {
            'geometryType': self.metadata_layer.geometry_type,
            'fields': fields,
        }

        # post_create_maplayerattributes signal
        extra_fields = post_create_maplayerattributes.send(
            self, layer=self.layer)
        for extra_field in extra_fields:
            if extra_field[1]:
                vector_params['fields'] = vector_params['fields'] + \
                    extra_field[1]

        self.results.update(APIVectorLayerStructure(**vector_params).as_dict())
Пример #3
0
    def response_config_mode(self, request):
        """
        Perform config operation, return form fields data for editing layer.
        :param request: API Object Request
        :param layer_name: editing layer name
        :return: Vector params
        """

        forms = self.get_forms()

        # add forms data if exist
        kwargs = {
            'fields': forms[self.layer_name]['fields']
        } if forms and forms.get(self.layer_name) else {}

        if hasattr(self.metadata_layer, 'fields_to_exlude'):
            kwargs['exlude'] = self.metadata_layer.fields_to_exlude
        if hasattr(self.metadata_layer, 'order'):
            kwargs['order'] = self.metadata_layer.order

        if self.mapping_layer_attributes_function.im_func == mapLayerAttributesFromModel:
            fields = self.mapping_layer_attributes_function.im_func(
                self.metadata_layer.model, **kwargs).values()
        else:
            fields = self.mapping_layer_attributes_function.im_func(
                self.layer, formField=True, **kwargs).values()

        vector_params = {
            'geomentryType': self.metadata_layer.geometry_type,
            'fields': fields,
            'pkField': self.metadata_layer.model._meta.pk.name
        }

        # post_create_maplayerattributes signal
        extra_fields = post_create_maplayerattributes.send(self,
                                                           layer=self.layer)
        for extra_field in extra_fields:
            if extra_field[1]:
                vector_params[
                    'fields'] = vector_params['fields'] + extra_field[1]

        self.results.update(APIVectorLayerStructure(**vector_params).as_dict())
Пример #4
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)
Пример #5
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)