Example #1
0
    def response_widget_unique_data(self, request_data):
        """
        Execute a distinct query for unique editing qgis widget
        """
        if 'fields' not in request_data:
            raise APIException('The \'fields\' param not in request data')

        # get fields to get unique value:
        fields = request_data['fields'].split(',')

        if len(fields) == 0:
            raise APIException('The \'fields\' param is empty')

        res = dict()
        for field in fields:
            uniques = self.metadata_layer.qgis_layer.uniqueValues(
                self.metadata_layer.qgis_layer.fields().indexOf(field)
            )
            tores = []
            for u in uniques:
                try:
                    tores.append(json.loads(QgsJsonUtils.encodeValue(u)))
                except Exception as e:
                    logger.error(f'Response vector widget unique: {e}')
                    continue

            res[field] = tores

        return res
Example #2
0
 def testEncodeValue(self):
     """ test encoding various values for use in GeoJSON strings """
     self.assertEqual(QgsJsonUtils.encodeValue(NULL), 'null')
     self.assertEqual(QgsJsonUtils.encodeValue(5), '5')
     self.assertEqual(QgsJsonUtils.encodeValue(5.9), '5.9')
     self.assertEqual(QgsJsonUtils.encodeValue(5999999999), '5999999999')
     self.assertEqual(QgsJsonUtils.encodeValue('string'), '"string"')
     self.assertEqual(QgsJsonUtils.encodeValue('str\ning'), '"str\\ning"')
     self.assertEqual(QgsJsonUtils.encodeValue('str\ring'), '"str\\ring"')
     self.assertEqual(QgsJsonUtils.encodeValue('str\bing'), '"str\\bing"')
     self.assertEqual(QgsJsonUtils.encodeValue('str\ting'), '"str\\ting"')
     self.assertEqual(QgsJsonUtils.encodeValue('str\\ing'), '"str\\\\ing"')
     self.assertEqual(QgsJsonUtils.encodeValue('str\\ning'),
                      '"str\\\\ning"')
     self.assertEqual(QgsJsonUtils.encodeValue('str\n\\\\ing'),
                      '"str\\n\\\\\\\\ing"')
     self.assertEqual(QgsJsonUtils.encodeValue('str/ing'), '"str\\/ing"')
     self.assertEqual(QgsJsonUtils.encodeValue([5, 6]), '[5,6]')
     self.assertEqual(QgsJsonUtils.encodeValue(['a', 'b', 'c']),
                      '["a","b","c"]')
     self.assertEqual(QgsJsonUtils.encodeValue(['a', 3, 'c']),
                      '["a",3,"c"]')
     self.assertEqual(QgsJsonUtils.encodeValue(['a', 'c\nd']),
                      '["a","c\\nd"]')
     # handle differences due to Qt5 version, where compact output now lacks \n
     enc_str = QgsJsonUtils.encodeValue({'key': 'value', 'key2': 5})
     self.assertTrue(enc_str == '{"key":"value",\n"key2":5}'
                     or enc_str == '{"key":"value","key2":5}')
     enc_str = QgsJsonUtils.encodeValue({
         'key': [1, 2, 3],
         'key2': {
             'nested': 'nested\\result'
         }
     })
     self.assertTrue(
         enc_str == '{"key":[1,2,3],\n"key2":{"nested":"nested\\\\result"}}'
         or enc_str
         == '{"key":[1,2,3],"key2":{"nested":"nested\\\\result"}}')
Example #3
0
 def testEncodeValue(self):
     """ test encoding various values for use in GeoJSON strings """
     self.assertEqual(QgsJsonUtils.encodeValue(NULL), 'null')
     self.assertEqual(QgsJsonUtils.encodeValue(5), '5')
     self.assertEqual(QgsJsonUtils.encodeValue(5.9), '5.9')
     self.assertEqual(QgsJsonUtils.encodeValue(5999999999), '5999999999')
     self.assertEqual(QgsJsonUtils.encodeValue('string'), '"string"')
     self.assertEqual(QgsJsonUtils.encodeValue('str\ning'), '"str\\ning"')
     self.assertEqual(QgsJsonUtils.encodeValue('str\ring'), '"str\\ring"')
     self.assertEqual(QgsJsonUtils.encodeValue('str\bing'), '"str\\bing"')
     self.assertEqual(QgsJsonUtils.encodeValue('str\ting'), '"str\\ting"')
     self.assertEqual(QgsJsonUtils.encodeValue('str\\ing'), '"str\\\\ing"')
     self.assertEqual(QgsJsonUtils.encodeValue('str\\ning'), '"str\\\\ning"')
     self.assertEqual(QgsJsonUtils.encodeValue('str\n\\\\ing'), '"str\\n\\\\\\\\ing"')
     self.assertEqual(QgsJsonUtils.encodeValue('str/ing'), '"str\\/ing"')
     self.assertEqual(QgsJsonUtils.encodeValue([5, 6]), '[5,6]')
     self.assertEqual(QgsJsonUtils.encodeValue(['a', 'b', 'c']), '["a","b","c"]')
     self.assertEqual(QgsJsonUtils.encodeValue(['a', 3, 'c']), '["a",3,"c"]')
     self.assertEqual(QgsJsonUtils.encodeValue(['a', 'c\nd']), '["a","c\\nd"]')
     # handle differences due to Qt5 version, where compact output now lacks \n
     enc_str = QgsJsonUtils.encodeValue({'key': 'value', 'key2': 5})
     self.assertTrue(enc_str == '{"key":"value",\n"key2":5}' or enc_str == '{"key":"value","key2":5}')
     enc_str = QgsJsonUtils.encodeValue({'key': [1, 2, 3], 'key2': {'nested': 'nested\\result'}})
     self.assertTrue(enc_str == '{"key":[1,2,3],\n"key2":{"nested":"nested\\\\result"}}' or enc_str == '{"key":[1,2,3],"key2":{"nested":"nested\\\\result"}}')
    def virtualFields(params: Dict[str, str], response: QgsServerResponse, project: QgsProject) -> None:
        """ Get virtual fields for features
        In parameters:
            LAYER=wms-layer-name
            VIRTUALS={"key1": "first expression", "key2": "second expression"}
            // optionals
            FILTER=An expression to filter layer
            FIELDS=list of requested field separated by comma
            WITH_GEOMETRY=False
        """
        layer_name = params.get('LAYER', '')
        if not layer_name:
            raise ExpressionServiceError(
                "Bad request error",
                "Invalid 'VirtualFields' REQUEST: LAYER parameter is mandatory",
                400)

        # get layer
        layer = find_vector_layer(layer_name, project)
        # layer not found
        if not layer:
            raise ExpressionServiceError(
                "Bad request error",
                "Invalid LAYER parameter for 'VirtualFields': {} provided".format(layer_name),
                400)

        # get virtuals
        virtuals = params.get('VIRTUALS', '')
        if not virtuals:
            raise ExpressionServiceError(
                "Bad request error",
                "Invalid 'VirtualFields' REQUEST: VIRTUALS parameter is mandatory",
                400)

        # try to load virtuals dict
        try:
            vir_json = json.loads(virtuals)
        except Exception:
            QgsMessageLog.logMessage(
                "JSON loads virtuals '{}' exception:\n{}".format(virtuals, traceback.format_exc()),
                "lizmap", Qgis.Critical)
            raise ExpressionServiceError(
                "Bad request error",
                "Invalid 'VirtualFields' REQUEST: VIRTUALS '{}' are not well formed".format(virtuals),
                400)

        if not isinstance(vir_json, dict):
            raise ExpressionServiceError(
                "Bad request error",
                "Invalid 'VirtualFields' REQUEST: VIRTUALS '{}' are not well formed".format(virtuals),
                400)

        # create expression context
        exp_context = QgsExpressionContext()
        exp_context.appendScope(QgsExpressionContextUtils.globalScope())
        exp_context.appendScope(QgsExpressionContextUtils.projectScope(project))
        exp_context.appendScope(QgsExpressionContextUtils.layerScope(layer))

        # create distance area context
        da = QgsDistanceArea()
        da.setSourceCrs(layer.crs(), project.transformContext())
        da.setEllipsoid(project.ellipsoid())

        # parse virtuals
        exp_map = {}
        exp_parser_errors = []
        for k, e in vir_json.items():
            exp = QgsExpression(e)
            exp.setGeomCalculator(da)
            exp.setDistanceUnits(project.distanceUnits())
            exp.setAreaUnits(project.areaUnits())

            if exp.hasParserError():
                exp_parser_errors.append('Error "{}": {}'.format(e, exp.parserErrorString()))
                continue

            if not exp.isValid():
                exp_parser_errors.append('Expression not valid "{}"'.format(e))
                continue

            exp.prepare(exp_context)
            exp_map[k] = exp

        # expression parser errors found
        if exp_parser_errors:
            raise ExpressionServiceError(
                "Bad request error",
                "Invalid VIRTUALS for 'VirtualFields':\n{}".format('\n'.join(exp_parser_errors)),
                400)

        req = QgsFeatureRequest()

        # get filter
        req_filter = params.get('FILTER', '')
        if req_filter:
            req_exp = QgsExpression(req_filter)
            req_exp.setGeomCalculator(da)
            req_exp.setDistanceUnits(project.distanceUnits())
            req_exp.setAreaUnits(project.areaUnits())

            if req_exp.hasParserError():
                raise ExpressionServiceError(
                    "Bad request error",
                    "Invalid FILTER for 'VirtualFields' Error \"{}\": {}".format(
                        req_filter, req_exp.parserErrorString()),
                    400)

            if not req_exp.isValid():
                raise ExpressionServiceError(
                    "Bad request error",
                    "Invalid FILTER for 'VirtualFields' Expression not valid \"{}\"".format(req_filter),
                    400)

            req_exp.prepare(exp_context)
            req = QgsFeatureRequest(req_exp, exp_context)

        # With geometry
        with_geom = params.get('WITH_GEOMETRY', '').lower() in ['true', '1', 't']
        if not with_geom:
            req.setFlags(QgsFeatureRequest.NoGeometry)

        # Fields
        pk_attributes = layer.primaryKeyAttributes()
        attribute_list = [i for i in pk_attributes]
        fields = layer.fields()
        r_fields = [f.strip() for f in params.get('FIELDS', '').split(',') if f]
        for f in r_fields:
            attribute_list.append(fields.indexOf(f))

        # response
        response.setStatusCode(200)
        response.setHeader("Content-Type", "application/json")
        response.write('{ "type": "FeatureCollection","features":[')
        response.flush()

        json_exporter = QgsJsonExporter(layer)
        if attribute_list:
            json_exporter.setAttributes(attribute_list)

        separator = ''
        for feat in layer.getFeatures(req):
            fid = layer_name + '.' + get_server_fid(feat, pk_attributes)

            extra = {}

            # Update context
            exp_context.setFeature(feat)
            exp_context.setFields(feat.fields())

            # Evaluate expressions for virtual fields
            errors = {}
            for k, exp in exp_map.items():
                value = exp.evaluate(exp_context)
                if exp.hasEvalError():
                    extra[k] = None
                    errors[k] = exp.evalErrorString()
                else:
                    extra[k] = json.loads(QgsJsonUtils.encodeValue(value))
                    errors[k] = exp.expression()

            response.write(separator + json_exporter.exportFeature(feat, extra, fid))
            response.flush()
            separator = ',\n'
        response.write(']}')
        return
    def replace_expression_text(params: Dict[str, str], response: QgsServerResponse, project: QgsProject) -> None:
        """ Replace expression texts against layer or features
        In parameters:
            LAYER=wms-layer-name
            STRING=A string with expression between [% and %]
            or
            STRINGS=["first string with expression", "second string with expression"]
            or
            STRINGS={"key1": "first string with expression", "key2": "second string with expression"}
            // optionals
            FEATURE={"type": "Feature", "geometry": {}, "properties": {}}
            or
            FEATURES=[{"type": "Feature", "geometry": {}, "properties": {}}, {"type": "Feature", "geometry": {},
            "properties": {}}]
            FORM_SCOPE=boolean to add formScope based on provided features
        """
        layername = params.get('LAYER', '')
        if not layername:
            raise ExpressionServiceError(
                "Bad request error",
                "Invalid 'ReplaceExpressionText' REQUEST: LAYER parameter is mandatory",
                400)

        # get layer
        layer = find_vector_layer(layername, project)
        # layer not found
        if not layer:
            raise ExpressionServiceError(
                "Bad request error",
                "Invalid LAYER parameter for 'ReplaceExpressionText': {} provided".format(layername),
                400)

        # get strings
        strings = params.get('STRINGS', '')
        if not strings:
            the_string = params.get('STRING', '')
            if not the_string:
                raise ExpressionServiceError(
                    "Bad request error",
                    "Invalid 'ReplaceExpressionText' REQUEST: STRING or STRINGS parameter is mandatory",
                    400)
            strings = '["{}"]'.format(the_string)

        # try to load expressions list or dict
        try:
            str_json = json.loads(strings)
        except Exception:
            QgsMessageLog.logMessage(
                "JSON loads strings '{}' exception:\n{}".format(strings, traceback.format_exc()),
                "lizmap", Qgis.Critical)
            raise ExpressionServiceError(
                "Bad request error",
                "Invalid 'ReplaceExpressionText' REQUEST: STRINGS '{}' are not well formed".format(strings),
                400)

        # get features
        features = params.get('FEATURES', '')
        if not features:
            feature = params.get('FEATURE', '')
            if feature:
                features = '[' + feature + ']'

        # create expression context
        exp_context = QgsExpressionContext()
        exp_context.appendScope(QgsExpressionContextUtils.globalScope())
        exp_context.appendScope(QgsExpressionContextUtils.projectScope(project))
        exp_context.appendScope(QgsExpressionContextUtils.layerScope(layer))

        # create distance area context
        da = QgsDistanceArea()
        da.setSourceCrs(layer.crs(), project.transformContext())
        da.setEllipsoid(project.ellipsoid())

        # organized strings
        str_map = {}
        str_items = []
        if isinstance(str_json, list):
            str_items = enumerate(str_json)
        elif isinstance(str_json, dict):
            str_items = str_json.items()
        for k, s in str_items:
            str_map[k] = s

        # create the body
        body = {
            'status': 'success',
            'results': [],
            'errors': [],
            'features': 0
        }

        # without features just replace expression string with layer context
        if not features:
            result = {}
            for k, s in str_map.items():
                value = QgsExpression.replaceExpressionText(s, exp_context, da)
                result[k] = json.loads(QgsJsonUtils.encodeValue(value))
            body['results'].append(result)
            write_json_response(body, response)
            return

        # Check features
        try:
            geojson = json.loads(features)
        except Exception:
            QgsMessageLog.logMessage(
                "JSON loads features '{}' exception:\n{}".format(features, traceback.format_exc()),
                "lizmap", Qgis.Critical)
            raise ExpressionServiceError(
                "Bad request error",
                "Invalid 'Evaluate' REQUEST: FEATURES '{}' are not well formed".format(features),
                400)

        if not geojson or not isinstance(geojson, list) or len(geojson) == 0:
            raise ExpressionServiceError(
                "Bad request error",
                "Invalid 'Evaluate' REQUEST: FEATURES '{}' are not well formed".format(features),
                400)

        if ('type' not in geojson[0]) or geojson[0]['type'] != 'Feature':
            raise ExpressionServiceError(
                "Bad request error",
                ("Invalid 'Evaluate' REQUEST: FEATURES '{}' are not well formed: type not defined or not "
                 "Feature.").format(features),
                400)

        # try to load features
        # read fields
        feature_fields = QgsJsonUtils.stringToFields(
            '{ "type": "FeatureCollection","features":' + features + '}',
            QTextCodec.codecForName("UTF-8"))
        # read features
        feature_list = QgsJsonUtils.stringToFeatureList(
            '{ "type": "FeatureCollection","features":' + features + '}',
            feature_fields,
            QTextCodec.codecForName("UTF-8"))

        # features not well formed
        if not feature_list:
            raise ExpressionServiceError(
                "Bad request error",
                ("Invalid FEATURES for 'ReplaceExpressionText': not GeoJSON features array "
                 "provided\n{}").format(features),
                400)

        # Extend layer fields with this provided in GeoJSON Features
        feat_fields = QgsFields(layer.fields())
        feat_fields.extend(feature_fields)

        # form scope
        add_form_scope = params.get('FORM_SCOPE', '').lower() in ['true', '1', 't']

        # loop through provided features to replace expression strings
        for f in feature_list:
            # clone the features with all attributes
            # those defined in layer + fields from GeoJSON Features
            feat = QgsFeature(feat_fields)
            feat.setGeometry(f.geometry())
            for field in f.fields():
                field_name = field.name()
                if feat_fields.indexOf(field_name) != -1:
                    feat.setAttribute(field_name, f[field_name])

            # Add form scope to expression context
            if add_form_scope:
                exp_context.appendScope(QgsExpressionContextUtils.formScope(feat))

            exp_context.setFeature(feat)
            exp_context.setFields(feat.fields())

            # replace expression strings with the new feature
            result = {}
            for k, s in str_map.items():
                value = QgsExpression.replaceExpressionText(s, exp_context, da)
                result[k] = json.loads(QgsJsonUtils.encodeValue(value))
            body['results'].append(result)

        write_json_response(body, response)
        return
    def evaluate(params: Dict[str, str], response: QgsServerResponse, project: QgsProject) -> None:
        """ Evaluate expressions against layer or features
        In parameters:
            LAYER=wms-layer-name
            EXPRESSION=An expression to evaluate
            or
            EXPRESSIONS=["first expression", "second expression"]
            or
            EXPRESSIONS={"key1": "first expression", "key2": "second expression"}
            // optionals
            FEATURE={"type": "Feature", "geometry": {}, "properties": {}}
            or
            FEATURES=[{"type": "Feature", "geometry": {}, "properties": {}}, {"type": "Feature", "geometry": {},
            "properties": {}}]
            FORM_SCOPE=boolean to add formScope based on provided features
        """

        layername = params.get('LAYER', '')
        if not layername:
            raise ExpressionServiceError(
                "Bad request error",
                "Invalid 'Evaluate' REQUEST: LAYER parameter is mandatory",
                400)

        # get layer
        layer = find_vector_layer(layername, project)
        # layer not found
        if not layer:
            raise ExpressionServiceError(
                "Bad request error",
                "Invalid LAYER parameter for 'Evaluate': {} provided".format(layername),
                400)

        # get expressions
        expressions = params.get('EXPRESSIONS', '')
        if not expressions:
            expression = params.get('EXPRESSION', '')
            if not expression:
                raise ExpressionServiceError(
                    "Bad request error",
                    "Invalid 'Evaluate' REQUEST: EXPRESSION or EXPRESSIONS parameter is mandatory",
                    400)
            expressions = '["{}"]'.format(expression)

        # try to load expressions list or dict
        try:
            exp_json = json.loads(expressions)
        except Exception:
            QgsMessageLog.logMessage(
                "JSON loads expressions '{}' exception:\n{}".format(expressions, traceback.format_exc()),
                "lizmap", Qgis.Critical)
            raise ExpressionServiceError(
                "Bad request error",
                "Invalid 'Evaluate' REQUEST: EXPRESSIONS '{}' are not well formed".format(expressions),
                400)

        # create expression context
        exp_context = QgsExpressionContext()
        exp_context.appendScope(QgsExpressionContextUtils.globalScope())
        exp_context.appendScope(QgsExpressionContextUtils.projectScope(project))
        exp_context.appendScope(QgsExpressionContextUtils.layerScope(layer))

        # create distance area context
        da = QgsDistanceArea()
        da.setSourceCrs(layer.crs(), project.transformContext())
        da.setEllipsoid(project.ellipsoid())

        # parse expressions
        exp_map = {}
        exp_parser_errors = []
        exp_items = []
        if isinstance(exp_json, list):
            exp_items = enumerate(exp_json)
        elif isinstance(exp_json, dict):
            exp_items = exp_json.items()
        for k, e in exp_items:
            exp = QgsExpression(e)
            exp.setGeomCalculator(da)
            exp.setDistanceUnits(project.distanceUnits())
            exp.setAreaUnits(project.areaUnits())

            if exp.hasParserError():
                exp_parser_errors.append('Error "{}": {}'.format(e, exp.parserErrorString()))
                continue

            if not exp.isValid():
                exp_parser_errors.append('Expression not valid "{}"'.format(e))
                continue

            exp.prepare(exp_context)
            exp_map[k] = exp

        # expression parser errors found
        if exp_parser_errors:
            raise ExpressionServiceError(
                "Bad request error",
                "Invalid EXPRESSIONS for 'Evaluate':\n{}".format('\n'.join(exp_parser_errors)),
                400)

        # get features
        features = params.get('FEATURES', '')
        if not features:
            feature = params.get('FEATURE', '')
            if feature:
                features = '[' + feature + ']'

        # create the body
        body = {
            'status': 'success',
            'results': [],
            'errors': [],
            'features': 0
        }

        # without features just evaluate expression with layer context
        if not features:
            result = {}
            error = {}
            for k, exp in exp_map.items():
                value = exp.evaluate(exp_context)
                if exp.hasEvalError():
                    result[k] = None
                    error[k] = exp.evalErrorString()
                else:
                    result[k] = json.loads(QgsJsonUtils.encodeValue(value))
            body['results'].append(result)
            body['errors'].append(error)
            write_json_response(body, response)
            return

        # Check features
        try:
            geojson = json.loads(features)
        except Exception:
            QgsMessageLog.logMessage(
                "JSON loads features '{}' exception:\n{}".format(features, traceback.format_exc()),
                "lizmap", Qgis.Critical)
            raise ExpressionServiceError(
                "Bad request error",
                "Invalid 'Evaluate' REQUEST: FEATURES '{}' are not well formed".format(features),
                400)

        if not geojson or not isinstance(geojson, list) or len(geojson) == 0:
            raise ExpressionServiceError(
                "Bad request error",
                "Invalid 'Evaluate' REQUEST: FEATURES '{}' are not well formed".format(features),
                400)

        if 'type' not in geojson[0] or geojson[0]['type'] != 'Feature':
            raise ExpressionServiceError(
                "Bad request error",
                ("Invalid 'Evaluate' REQUEST: FEATURES '{}' are not well formed: type not defined or not "
                 "Feature.").format(features),
                400)

        # try to load features
        # read fields
        feature_fields = QgsJsonUtils.stringToFields(
            '{ "type": "FeatureCollection","features":' + features + '}',
            QTextCodec.codecForName("UTF-8"))
        # read features
        feature_list = QgsJsonUtils.stringToFeatureList(
            '{ "type": "FeatureCollection","features":' + features + '}',
            feature_fields,
            QTextCodec.codecForName("UTF-8"))

        # features not well formed
        if not feature_list:
            raise ExpressionServiceError(
                "Bad request error",
                "Invalid FEATURES for 'Evaluate': not GeoJSON features array provided\n{}".format(features),
                400)

        # Extend layer fields with this provided in GeoJSON Features
        feat_fields = QgsFields(layer.fields())
        feat_fields.extend(feature_fields)

        # form scope
        add_form_scope = params.get('FORM_SCOPE', '').lower() in ['true', '1', 't']

        # loop through provided features to evaluate expressions
        for f in feature_list:
            # clone the features with all attributes
            # those defined in layer + fields from GeoJSON Features
            feat = QgsFeature(feat_fields)
            feat.setGeometry(f.geometry())
            for field in f.fields():
                fname = field.name()
                if feat_fields.indexOf(fname) != -1:
                    feat.setAttribute(fname, f[fname])

            # Add form scope to expression context
            if add_form_scope:
                exp_context.appendScope(QgsExpressionContextUtils.formScope(feat))

            exp_context.setFeature(feat)
            exp_context.setFields(feat.fields())

            # Evaluate expressions with the new feature
            result = {}
            error = {}
            for k, exp in exp_map.items():
                if add_form_scope:
                    # need to prepare the expression because the context has been updated with a new scope
                    exp.prepare(exp_context)
                value = exp.evaluate(exp_context)
                if exp.hasEvalError():
                    result[k] = None
                    error[k] = exp.evalErrorString()
                else:
                    result[k] = json.loads(QgsJsonUtils.encodeValue(value))
                    error[k] = exp.expression()
            body['results'].append(result)
            body['errors'].append(error)

        write_json_response(body, response)
        return
Example #7
0
    def to_representation(self, instance):
        ret = super(WidgetSerializer, self).to_representation(instance)
        ret['type'] = instance.widget_type

        # get edittype
        edittypes = eval(self.layer.edittypes)

        if ret['type'] == 'search':
            body = json.loads(instance.body)
            ret['options'] = {
                'queryurl': None,
                'title': body['title'],
                'results': body['results'],
                'filter': [],
                'dozoomtoextent': body['dozoomtoextent'],
                #'zoom': body['zoom'],
            }
            for field in body['fields']:

                # if widgettype is selectbox, get values
                if 'widgettype' in field and field['widgettype'] == 'selectbox':

                    qgis_layer = get_qgis_layer(self.layer)

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

                    field['input']['type'] = 'selectfield'
                    if 'dependance' not in field['input']['options']:

                        # check if field has a widget edit type
                        # todo: add 'ValueRelation' case
                        if field['name'] in edittypes and \
                                edittypes[field['name']]['widgetv2type'] in ('ValueMap'):
                            field['input']['options']['values'] = edittypes[
                                field['name']]['values']
                        else:
                            field['input']['options']['values'] = values
                    else:
                        field['input']['options']['values'] = []

                #For AutoccOmpleteBox imput type
                if 'widgettype' in field and field[
                        'widgettype'] == 'autocompletebox':
                    field['input']['type'] = 'autocompletefield'

                input = field['input']
                input['options']['blanktext'] = field['blanktext']
                ret['options']['filter'].append({
                    'op':
                    field['filterop'],
                    'attribute':
                    field['name'],
                    'label':
                    field['label'],
                    'input':
                    input,
                    'logicop':
                    field.get('logicop', 'AND').upper()
                })
        else:
            ret['body'] = json.loads(instance.body)
        return ret
Example #8
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)
Example #9
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)