Beispiel #1
0
    def testStringToFields(self):
        """test retrieving fields from GeoJSON strings"""

        # empty string
        fields = QgsJsonUtils.stringToFields("", codec)
        self.assertEqual(fields.count(), 0)

        # bad string
        fields = QgsJsonUtils.stringToFields("asdasdas", codec)
        self.assertEqual(fields.count(), 0)

        # geojson string
        fields = QgsJsonUtils.stringToFields('{\n"type": "Feature","geometry": {"type": "Point","coordinates": [125, 10]},"properties": {"name": "Dinagat Islands","height":5.5}}', codec)
        self.assertEqual(fields.count(), 2)
        self.assertEqual(fields[0].name(), "name")
        self.assertEqual(fields[0].type(), QVariant.String)
        self.assertEqual(fields[1].name(), "height")
        self.assertEqual(fields[1].type(), QVariant.Double)
Beispiel #2
0
    def testStringToFeatureList(self):
        """Test converting json string to features"""

        fields = QgsFields()
        fields.append(QgsField("name", QVariant.String))

        # empty string
        features = QgsJsonUtils.stringToFeatureList("", fields, codec)
        self.assertEqual(features, [])

        # bad string
        features = QgsJsonUtils.stringToFeatureList("asdasdas", fields, codec)
        self.assertEqual(features, [])

        # geojson string with 1 feature
        features = QgsJsonUtils.stringToFeatureList('{\n"type": "Feature","geometry": {"type": "Point","coordinates": [125, 10]},"properties": {"name": "Dinagat Islands"}}', fields, codec)
        self.assertEqual(len(features), 1)
        self.assertFalse(features[0].geometry().isNull())
        self.assertEqual(features[0].geometry().wkbType(), QgsWkbTypes.Point)
        point = features[0].geometry().constGet()
        self.assertEqual(point.x(), 125.0)
        self.assertEqual(point.y(), 10.0)
        self.assertEqual(features[0]['name'], "Dinagat Islands")

        # geojson string with 2 features
        features = QgsJsonUtils.stringToFeatureList('{ "type": "FeatureCollection","features":[{\n"type": "Feature","geometry": {"type": "Point","coordinates": [125, 10]},"properties": {"name": "Dinagat Islands"}}, {\n"type": "Feature","geometry": {"type": "Point","coordinates": [110, 20]},"properties": {"name": "Henry Gale Island"}}]}', fields, codec)
        self.assertEqual(len(features), 2)
        self.assertFalse(features[0].geometry().isNull())
        self.assertEqual(features[0].geometry().wkbType(), QgsWkbTypes.Point)
        point = features[0].geometry().constGet()
        self.assertEqual(point.x(), 125.0)
        self.assertEqual(point.y(), 10.0)
        self.assertEqual(features[0]['name'], "Dinagat Islands")
        self.assertFalse(features[1].geometry().isNull())
        self.assertEqual(features[1].geometry().wkbType(), QgsWkbTypes.Point)
        point = features[1].geometry().constGet()
        self.assertEqual(point.x(), 110.0)
        self.assertEqual(point.y(), 20.0)
        self.assertEqual(features[1]['name'], "Henry Gale Island")
Beispiel #3
0
    def testExportAttributes(self):
        """ test exporting feature's attributes to JSON object """
        fields = QgsFields()

        # test empty attributes
        feature = QgsFeature(fields, 5)
        expected = "{}"
        self.assertEqual(QgsJsonUtils.exportAttributes(feature), expected)

        # test feature with attributes
        fields.append(QgsField("name", QVariant.String))
        fields.append(QgsField("cost", QVariant.Double))
        fields.append(QgsField("population", QVariant.Int))

        feature = QgsFeature(fields, 5)
        feature.setGeometry(QgsGeometry(QgsPoint(5, 6)))
        feature.setAttributes(['Valsier Peninsula', 6.8, 198])

        expected = """{"name":"Valsier Peninsula",
"cost":6.8,
"population":198}"""
        self.assertEqual(QgsJsonUtils.exportAttributes(feature), expected)

        # test using field formatters
        source = QgsVectorLayer("Point?field=fldtxt:string&field=fldint:integer",
                                "parent", "memory")
        pf1 = QgsFeature()
        pf1.setFields(source.fields())
        pf1.setAttributes(["test1", 1])

        setup = QgsEditorWidgetSetup('ValueMap', {"map": {"one": 1, "two": 2, "three": 3}})
        source.setEditorWidgetSetup(1, setup)

        expected = """{"fldtxt":"test1",
"fldint":"one"}"""
        self.assertEqual(QgsJsonUtils.exportAttributes(pf1, source), expected)
Beispiel #4
0
    def testExportAttributes(self):
        """ test exporting feature's attributes to JSON object """
        fields = QgsFields()

        # test empty attributes
        feature = QgsFeature(fields, 5)
        expected = "{}"
        self.assertEqual(QgsJsonUtils.exportAttributes(feature), expected)

        # test feature with attributes
        fields.append(QgsField("name", QVariant.String))
        fields.append(QgsField("cost", QVariant.Double))
        fields.append(QgsField("population", QVariant.Int))

        feature = QgsFeature(fields, 5)
        feature.setGeometry(QgsGeometry(QgsPoint(5, 6)))
        feature.setAttributes(['Valsier Peninsula', 6.8, 198])

        expected = """{"name":"Valsier Peninsula",
"cost":6.8,
"population":198}"""
        self.assertEqual(QgsJsonUtils.exportAttributes(feature), expected)

        # test using field formatters
        source = QgsVectorLayer("Point?field=fldtxt:string&field=fldint:integer",
                                "parent", "memory")
        pf1 = QgsFeature()
        pf1.setFields(source.fields())
        pf1.setAttributes(["test1", 1])

        setup = QgsEditorWidgetSetup('ValueMap', {"map": {"one": 1, "two": 2, "three": 3}})
        source.setEditorWidgetSetup(1, setup)

        expected = """{"fldtxt":"test1",
"fldint":"one"}"""
        self.assertEqual(QgsJsonUtils.exportAttributes(pf1, source), expected)
Beispiel #5
0
    def qgsfeature_feature_from_node(self, node: PlanetNode):

        # TODO: Resolve geometry by node_type or do that under node.geometry()?
        # geom = None
        # if node.node_type() == NodeT.DAILY_SCENE_IMAGE:
        #     geom = node.geometry()

        # TODO: Add node
        # feature_collect = {
        #     'type': 'FeatureCollection',
        #     'features': [
        #         {
        #             'type': 'Feature',
        #             'geometry': node.geometry(),
        #             'properties': {
        #                 'id': node.item_id()
        #             }
        #         }
        #     ]
        # }

        feature_collect = {
            'type': 'FeatureCollection',
            'features': [
                node.resource()
            ]
        }

        feature_collect_json = json.dumps(feature_collect)

        # noinspection PyUnusedLocal
        features = []
        # noinspection PyBroadException
        try:
            utf8 = QTextCodec.codecForName('UTF-8')
            # TODO: Add node id, properties as fields?
            fields = QgsFields()
            features = QgsJsonUtils().stringToFeatureList(
                string=feature_collect_json, fields=fields, encoding=utf8)
        except Exception:
            log.debug('Footprint GeoJSON could not be parsed')
            return

        if not len(features) > 0:
            log.debug('GeoJSON parsing created no features')
            return

        return features[0]
Beispiel #6
0
def getFeaturesFromResponse(response):
    """Return a list of features from a valhalla response object
    """
    fields = QgsFields()
    fields.append(QgsField("opacity", QVariant.Double))
    fields.append(QgsField("fill", QVariant.String))
    fields.append(QgsField("fillOpacity", QVariant.Double))
    fields.append(QgsField("fill-opacity", QVariant.Double))
    # FIXME: in fact, due to a bug in qgis parser, we cannot use this field
    fields.append(QgsField("contour", QVariant.Int))
    fields.append(QgsField("color", QVariant.String))
    fields.append(QgsField("fillColor", QVariant.String))
    codec = QTextCodec.codecForName("UTF-8")
    features = QgsJsonUtils.stringToFeatureList(json.dumps(response), fields,
                                                codec)
    # LOG.debug('features : {}'.format(features))
    return features
Beispiel #7
0
    def _single_feature(feat):
        # existing feature json
        if feat is None:
            return None
        obj = {"type": "Feature"}
        json_str = QgsJsonUtils.exportAttributes(feat)
        props = json.loads(json_str)
        props.pop(QGS_ID, None)
        v = props.pop(QGS_XYZ_ID, None)
        if v is not None and v != "":
            if v in exist_feat_id:
                return None
            exist_feat_id.add(v)
            if not is_new:
                obj[XYZ_ID] = v
        fields = feat.fields()
        expression_field_names = [
            f.name()
            for i, f in enumerate(fields)
            if fields.fieldOrigin(i) == fields.OriginExpression
        ]
        # print({k.name(): fields.fieldOrigin(i) for i, k in enumerate(fields)})
        props = _xyz_props(props, ignore_keys=expression_field_names)
        livemap_props = _livemap_props(props, xyz_id=obj.get(XYZ_ID)) if is_livemap else dict()
        props = _clean_props(props)
        obj["properties"] = dict(props, **livemap_props)

        geom = feat.geometry()
        geom_ = json.loads(geom.asJson())
        if geom_ is None:
            # print_qgis(obj)
            return obj
        obj["geometry"] = make_valid_xyz_json_geom(geom_)

        # bbox = geom.boundingBox() # print_qgis("bbox: %s"%bbox.toString()) if bbox.isEmpty():
        # if "coordinates" in geom_: obj["bbox"] = list(geom_["coordinates"]) * 2 else: obj[
        # "bbox"] = [bbox.xMinimum(), bbox.yMinimum(), 0.0, bbox.xMaximum(), bbox.yMaximum(),
        # 0.0] obj["bbox"] = [bbox.xMinimum(), bbox.yMinimum(), bbox.xMaximum(), bbox.yMaximum()]
        return obj
Beispiel #8
0
    def addFeature(self):
        text = self.lineEdit.text().strip()
        if text == "":
            return
        layer = self.iface.activeLayer()
        if layer is None:
            return
        try:
            if (self.inputProjection == 0) or (text[0] == '{'):
                # If this is GeoJson it does not matter what inputProjection is
                if text[0] == '{':  # This may be a GeoJSON point
                    codec = QTextCodec.codecForName("UTF-8")
                    fields = QgsJsonUtils.stringToFields(text, codec)
                    fet = QgsJsonUtils.stringToFeatureList(text, fields, codec)
                    if (len(fet) == 0) or not fet[0].isValid():
                        raise ValueError('Invalid Coordinates')

                    geom = fet[0].geometry()
                    if geom.isEmpty() or (geom.wkbType() != QgsWkbTypes.Point):
                        raise ValueError('Invalid GeoJSON Geometry')
                    pt = geom.asPoint()
                    lat = pt.y()
                    lon = pt.x()
                elif re.search(r'POINT\(', text) is not None:
                    m = re.findall(
                        r'POINT\(\s*([+-]?\d*\.?\d*)\s+([+-]?\d*\.?\d*)', text)
                    if len(m) != 1:
                        raise ValueError('Invalid Coordinates')
                    lon = float(m[0][0])
                    lat = float(m[0][1])
                else:
                    lat, lon = LatLon.parseDMSString(text, self.inputXYOrder)
                srcCrs = epsg4326
            elif self.inputProjection == 1:
                # This is an MGRS coordinate
                text = re.sub(r'\s+', '',
                              unicode(text))  # Remove all white space
                lat, lon = mgrs.toWgs(text)
                srcCrs = epsg4326
            elif self.inputProjection == 4:
                text = text.strip()
                coord = olc.decode(text)
                lat = coord.latitudeCenter
                lon = coord.longitudeCenter
                srcCrs = epsg4326
            else:  # Is either the project or custom CRS
                if re.search(r'POINT\(', text) is None:
                    coords = re.split(r'[\s,;:]+', text, 1)
                    if len(coords) < 2:
                        raise ValueError('Invalid Coordinates')
                    if self.inputXYOrder == 0:  # Y, X Order
                        lat = float(coords[0])
                        lon = float(coords[1])
                    else:
                        lon = float(coords[0])
                        lat = float(coords[1])
                else:
                    m = re.findall(
                        r'POINT\(\s*([+-]?\d*\.?\d*)\s+([+-]?\d*\.?\d*)', text)
                    if len(m) != 1:
                        raise ValueError('Invalid Coordinates')
                    lon = float(m[0][0])
                    lat = float(m[0][1])
                if self.inputProjection == 2:  # Project CRS
                    srcCrs = self.canvas.mapSettings().destinationCrs()
                else:
                    srcCrs = QgsCoordinateReferenceSystem(self.inputCustomCRS)
        except Exceptions:
            # traceback.print_exc()
            self.iface.messageBar().pushMessage("",
                                                "Invalid Coordinate",
                                                level=Qgis.Warning,
                                                duration=2)
            return
        self.lineEdit.clear()
        caps = layer.dataProvider().capabilities()
        if caps & QgsVectorDataProvider.AddFeatures:
            destCRS = layer.crs(
            )  # Get the CRS of the layer we are adding a point toWgs
            transform = QgsCoordinateTransform(srcCrs, destCRS,
                                               QgsProject.instance())
            # Transform the input coordinate projection to the layer CRS
            x, y = transform.transform(float(lon), float(lat))
            feat = QgsFeature(layer.fields())
            feat.setGeometry(QgsGeometry.fromPointXY(QgsPointXY(x, y)))
            if layer.fields().count() == 0:
                layer.addFeature(feat)
                self.lltools.zoomTo(srcCrs, lat, lon)
            else:
                if self.iface.openFeatureForm(layer, feat):
                    layer.addFeature(feat)
                    self.lltools.zoomTo(srcCrs, lat, lon)
Beispiel #9
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"}}')
Beispiel #10
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)
    def get_feature_with_form_scope(params: Dict[str, str], response: QgsServerResponse, project: QgsProject) -> None:
        """ Get filtered features with a form scope
        In parameters:
            LAYER=wms-layer-name
            FILTER=An expression to filter layer
            FORM_FEATURE={"type": "Feature", "geometry": {}, "properties": {}}
            // optionals
            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 'GetFeatureWithFormScope' 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 'VirtualField': {} provided".format(layer_name),
                400)

        # get filter
        exp_filter = params.get('FILTER', '')
        if not exp_filter:
            raise ExpressionServiceError(
                "Bad request error",
                "Invalid 'GetFeatureWithFormScope' REQUEST: FILTER parameter is mandatory",
                400)

        # get form feature
        form_feature = params.get('FORM_FEATURE', '')
        if not form_feature:
            raise ExpressionServiceError(
                "Bad request error",
                "Invalid 'GetFeatureWithFormScope' REQUEST: FORM_FEATURE parameter is mandatory",
                400)

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

        if not geojson or not isinstance(geojson, dict):
            raise ExpressionServiceError(
                "Bad request error",
                "Invalid 'GetFeatureWithFormScope' REQUEST: FORM_FEATURE '{}' are not well formed".format(form_feature),
                400)

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

        # try to load form feature
        # read fields
        form_feature_fields = QgsJsonUtils.stringToFields(
            form_feature,
            QTextCodec.codecForName("UTF-8"))
        # read features
        form_feature_list = QgsJsonUtils.stringToFeatureList(
            form_feature,
            form_feature_fields,
            QTextCodec.codecForName("UTF-8"))

        # features not well formed
        if not form_feature_list:
            raise ExpressionServiceError(
                "Bad request error",
                ("Invalid FORM_FEATURE for 'GetFeatureWithFormScope': not GeoJSON feature provided\n"
                 "{}").format(form_feature),
                400)

        if len(form_feature_list) != 1:
            raise ExpressionServiceError(
                "Bad request error",
                ("Invalid FORM_FEATURE for 'GetFeatureWithFormScope': not GeoJSON feature provided\n"
                 "{}").format(form_feature),
                400)

        # Get the form feature
        form_feat = form_feature_list[0]

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

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

        # Get filter expression
        exp_f = QgsExpression(exp_filter)
        exp_f.setGeomCalculator(da)
        exp_f.setDistanceUnits(project.distanceUnits())
        exp_f.setAreaUnits(project.areaUnits())

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

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

        exp_f.prepare(exp_context)

        req = QgsFeatureRequest(exp_f, 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)
            response.write(separator + json_exporter.exportFeature(feat, {}, fid))
            response.flush()
            separator = ',\n'
        response.write(']}')
        return
    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 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
    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
Beispiel #15
0
def add_geojson_features(geojson,
                         project,
                         qgis_layer_id=None,
                         connection_id=None,
                         new_layer_name=None,
                         name=None,
                         style=None):
    """Add geojson features to a destination layer, the layer can be specified
    by passing a QgsVectorLayer instance or by specifying a connection and
    a new layer name plus the QDjango project for the new layer.

    The connection may assume the special values `__shapefile__`, `__spatialite__` or `__geopackage__`
    for the creation of new OGR files of the corresponding type.

    The creation of the new layer may raise an exception is a layer with the same name as
    new_layer_name already exists. For already existing layers the `qgis_layer_id` can be used,
    provided that the layer belongs to the current `project`.

    Returns the qgis_layer_id

    :param geojson: new features in GeoJSON format
    :type geojson: str
    :param project: QDjango Project instance for the new or the existing layer
    :type project: Project instance
    :param layer_id: optional, QGIS layer id
    :type layer_id: QGIS layer id
    :param connection: optional, connection id or the special value `__shapefile__`, `__spatialite__` or `__geopackage__`
    :type connection: str
    :param new_layer_name: optional, name of the new layer
    :type new_layer_name: str
    :param name: optional, name of the isochrone, default to current datetime
    :type name: str
    :param style: optional, dictionary with style properties: example {'color': [100, 50, 123], 'transparency': 0.5, 'stroke_width: 3 }
    :type style: dict
    :raises Exception: raise on error
    :rtype: str
    """

    # Additional fields that are not returned by the service as feature attributes
    json_data = json.loads(geojson)

    if name is None:
        name = "Isochrone %s" % QDateTime.currentDateTime().toString(
            Qt.ISODateWithMs)

    metadata = {
        'range_type': json_data['metadata']['query']['range_type'],
        'name': name,
        # 'timestamp': json_data['metadata']['timestamp'],  // Not supported
    }

    for f in json_data['features']:
        f['properties'].update(metadata)

    geojson = json.dumps(json_data)

    fields = QgsJsonUtils.stringToFields(geojson)

    # Patch timestamp type to DateTime // Not supported
    # fields.remove(fields.lookupField('timestamp'))
    #fields.append(QgsField('timestamp', QVariant.DateTime))

    # Create the new layer
    if connection_id is not None:

        def _write_to_ogr(destination_path, new_layer_name, driverName=None):
            """Writes features to new or existing OGR layer"""

            tmp_dir = QTemporaryDir()
            tmp_path = os.path.join(tmp_dir.path(), 'isochrone.json')
            with open(tmp_path, 'w+') as f:
                f.write(geojson)

            tmp_layer = QgsVectorLayer(tmp_path, 'tmp_isochrone', 'ogr')

            if not tmp_layer.isValid():
                raise Exception(
                    _('Cannot create temporary layer for isochrone result.'))

            # Note: shp attribute names are max 10 chars long
            save_options = QgsVectorFileWriter.SaveVectorOptions()
            if driverName is not None:
                save_options.driverName = driverName
            save_options.layerName = new_layer_name
            save_options.fileEncoding = 'utf-8'

            # This is nonsense to me: if the file does not exist the actionOnExistingFile
            # should be ignored instead of raising an error, probable QGIS bug
            if os.path.exists(destination_path):
                # Check if the layer already exists
                layer_exists = QgsVectorFileWriter.targetLayerExists(
                    destination_path, new_layer_name)

                if layer_exists:
                    raise Exception(
                        _('Cannot save isochrone result to destination layer: layer already exists (use "qgis_layer_id" instead)!'
                          ))

                save_options.actionOnExistingFile = QgsVectorFileWriter.CreateOrOverwriteLayer

            error_code, error_message = QgsVectorFileWriter.writeAsVectorFormatV2(
                tmp_layer, destination_path,
                project.qgis_project.transformContext(), save_options)

            if error_code != QgsVectorFileWriter.NoError:
                raise Exception(
                    _('Cannot save isochrone result to destination layer: ') +
                    error_message)

            layer_uri = destination_path

            if driverName != 'ESRI Shapefile':
                layer_uri += '|layername=' + new_layer_name

            provider = 'ogr'
            return layer_uri, provider

        destination_path = None

        if connection_id == '__shapefile__':  # new shapefile
            driverName = 'ESRI Shapefile'
            extension = 'shp'
        elif connection_id == '__spatialite__':  # new sqlite
            driverName = 'SpatiaLite'
            extension = 'sqlite'
        elif connection_id == '__geopackage__':  # new gpkg
            driverName = 'GPKG'
            extension = 'gpkg'
        else:  # Add new table to an existing DB connection

            try:
                connection = get_db_connections(
                    project.qgis_project)[connection_id]
            except:
                raise Exception(_('Wrong connection id.'))

            if connection['provider'] == 'ogr':
                destination_path = connection_id
                driverName = 'GPKG' if destination_path.lower().endswith(
                    '.gpkg') else 'SpatiaLite'
            else:
                driverName = None

        # Create a new file/layer
        if driverName is not None:
            new_layer_name = os.path.basename(new_layer_name)

            if destination_path is None:  # new files!
                destination_path = os.path.join(
                    settings.DATASOURCE_PATH,
                    "{}.{}".format(new_layer_name, extension))
                i = 0
                while os.path.exists(destination_path):
                    i += 1
                    destination_path = os.path.join(
                        settings.DATASOURCE_PATH,
                        "{}_{}.{}".format(new_layer_name, i, extension))

            layer_uri, provider = _write_to_ogr(destination_path,
                                                new_layer_name, driverName)

        # Create a new DB table
        else:
            assert connection['provider'] != 'ogr'
            md = QgsProviderRegistry.instance().providerMetadata(
                connection['provider'])
            if not md:
                raise Exception(
                    _('Error creating destination layer connection.'))
            conn = md.createConnection(connection_id, {})
            try:
                conn.createVectorTable(connection['schema'], new_layer_name,
                                       fields, QgsWkbTypes.Polygon,
                                       QgsCoordinateReferenceSystem(4326),
                                       False, {'geometryColumn': 'geom'})
            except QgsProviderConnectionException as ex:
                raise Exception(
                    _('Error creating destination layer: ') + str(ex))

            uri = QgsDataSourceUri(conn.uri())
            uri.setTable(new_layer_name)
            uri.setSchema(connection['schema'])
            uri.setSrid('4326')
            uri.setGeometryColumn('geom')
            provider = connection['provider']
            layer_uri = uri.uri()

        # Now reload the new layer and add it to the project
        qgis_layer = QgsVectorLayer(layer_uri, new_layer_name, provider)
        if not qgis_layer.isValid():
            raise Exception(
                _('Error creating destination layer: layer is not valid!'))

        qgis_layer_id = qgis_layer.id()

        with QgisProjectFileLocker(project) as project:
            apply_style(qgis_layer, style, True, name)
            project.qgis_project.addMapLayers([qgis_layer])
            root = project.qgis_project.layerTreeRoot()
            if qgis_layer_id not in root.findLayerIds():
                # Append layer at the end
                root.insertLayer(-1, qgis_layer)
            if not project.update_qgis_project():
                raise Exception(
                    _('Error saving the destination layer: could not write project!'
                      ))

        # Retrieve the layer again because saving the project deleted it
        qgis_layer = project.qgis_project.mapLayer(qgis_layer_id)

        # Create Layer object
        instance, created = Layer.objects.get_or_create(
            qgs_layer_id=qgis_layer_id,
            project=project,
            defaults={
                'origname':
                new_layer_name,
                'name':
                new_layer_name,
                'title':
                new_layer_name,
                'is_visible':
                True,
                'layer_type':
                provider,
                'srid':
                4326,
                'datasource':
                layer_uri,
                'geometrytype':
                'Polygon',
                # TODO: make this a property of the Layer object
                'database_columns':
                str([{
                    'name': f.name(),
                    'type': QVariant.typeToName(f.type()).upper(),
                    'label': f.displayName()
                } for f in qgis_layer.fields()]),
            })

        if not created:
            raise Exception(
                _('Error adding destination Layer to the project: layer already exists.'
                  ))

        # for OGR (already filled with features) returns the id of the new layer
        if driverName is not None:
            return qgis_layer_id

    # Append to an existing layer
    qgis_layer = project.qgis_project.mapLayer(qgis_layer_id)
    if qgis_layer is None:
        raise Exception(
            _('Error opening destination layer %s: layer not found in QGIS project!'
              % qgis_layer_id))

    features = QgsJsonUtils.stringToFeatureList(geojson, fields)

    compatible_features = []
    for f in features:
        compatible_features.extend(
            QgsVectorLayerUtils.makeFeatureCompatible(f, qgis_layer))

    if qgis_layer.crs().isValid(
    ) and qgis_layer.crs() != QgsCoordinateReferenceSystem(4326):
        ct = QgsCoordinateTransform(QgsCoordinateReferenceSystem(4326),
                                    qgis_layer.crs(), project.qgis_project)
        for f in compatible_features:
            geom = f.geometry()
            if geom.transform(ct) != QgsGeometry.Success:
                raise Exception(
                    _('Error transforming geometry from 4326 to destination layer CRS.'
                      ))
            f.setGeometry(geom)

    if len(compatible_features) == 0:
        raise Exception(_('No compatible features returned from isochrone.'))

    if not qgis_layer.startEditing():
        raise Exception(_('Destination layer is not editable.'))

    # Add features to the layer
    if not qgis_layer.addFeatures(compatible_features):
        raise Exception(_('Error adding features to the destination layer.'))

    if not qgis_layer.commitChanges():
        raise Exception(
            _('Error committing features to the destination layer.'))

    if style is not None:
        with QgisProjectFileLocker(project) as project:
            apply_style(qgis_layer, style, False, name)
            project.update_qgis_project()

    return qgis_layer_id
Beispiel #16
0
    def handle_add_layer(self):
        """Create a new layer by name (rev_lyr)"""

        slds = self.get_sld()

        selected_name = self.dlg.mComboBox.currentText()
        selected_id = self.dataset_dictionary[selected_name]

        # Group name equals selected dataset name
        group = self.create_group(selected_name)

        # Get metadata and features from NgisOpenAPI
        try:
            metadata_from_api = self.client.getDatasetMetadata(selected_id)
            epsg = metadata_from_api.crs_epsg
            features_from_api = self.client.getDatasetFeatures(
                metadata_from_api.id, metadata_from_api.bbox, epsg)
        except Exception as e:
            error = ApiError("Nedlasting av data mislyktes",
                             "Kunne ikke laste ned datasett", e)
            self.iface.messageBar().pushMessage(error.title,
                                                error.detail,
                                                error.show_more,
                                                level=2,
                                                duration=10)
            return
        crs_from_api = features_from_api['crs']['properties']['name']
        features_by_type = {}

        # Extract features from GeoJSON into dictionary
        for feature in features_from_api['features']:
            feature_type = feature['properties']['featuretype']
            features_by_type.setdefault(feature_type, []).append(feature)

        features_from_api['features'] = None

        for feature_type, features_list in features_by_type.items():
            # Create a new GeoJSON object containing a single featuretype
            features_dict = features_from_api.copy()
            features_dict['features'] = features_list

            features_json = json.dumps(features_dict, ensure_ascii=False)

            # Identify fields and features from GeoJSON
            codec = QTextCodec.codecForName("UTF-8")
            fields = QgsJsonUtils.stringToFields(features_json, codec)
            newFeatures = QgsJsonUtils.stringToFeatureList(
                features_json, fields, codec)

            # If different geometry types are identified, separate them into individual layers
            geometry_dict = {}
            if newFeatures:
                for feature in newFeatures:

                    featuretype = feature.attribute('featuretype')
                    geom_type = feature.geometry()
                    geom_type = QgsWkbTypes.displayString(
                        int(geom_type.wkbType()))
                    if geom_type not in geometry_dict:
                        geometry_dict[geom_type] = {}
                    if featuretype not in geometry_dict[geom_type]:
                        geometry_dict[geom_type][featuretype] = []

                    geometry_dict[geom_type][featuretype].append(feature)

            for geom_type, feature_types in geometry_dict.items():
                for feature_type, features in feature_types.items():
                    lyr = QgsVectorLayer(f'{geom_type}?crs={crs_from_api}',
                                         f'{feature_type}-{geom_type}',
                                         "memory")
                    #lyr = QgsVectorLayer(f'{geom_type}?crs=EPSG:25832', f'{feature_type}-{geom_type}', "memory") #TODO Remove
                    QgsProject.instance().addMapLayer(lyr, False)

                    lyr.startEditing()

                    add_fields_to_layer(lyr, fields, feature_type)

                    lyr.commitChanges()
                    l_d = lyr.dataProvider()
                    l_d.addFeatures(features)
                    # update the extent of rev_lyr
                    lyr.updateExtents()
                    # save changes made in 'rev_lyr'
                    lyr.commitChanges()
                    group.addLayer(lyr)

                    #lyr.committedFeaturesAdded.connect(self.handleCommitedAddedFeatures)
                    #lyr.committedFeaturesRemoved.connect(self.handleCommittedFeaturesRemoved)
                    #lyr.featuresDeleted.connect(self.handleDeletedFeatures)
                    #lyr.committedGeometriesChanges(self.ee)

                    lyr.beforeCommitChanges.connect(
                        self.handle_before_commitchanges)

                    if feature_type in slds:
                        lyr.loadSldStyle(slds[feature_type])

                    self.dataset_dictionary[lyr.id()] = selected_id
                    self.feature_type_dictionary[lyr.id()] = feature_type
    def convertCoordinate(self, text):
        try:
            if self.settings.zoomToProjIsMGRS():
                # An MGRS coordinate only format has been specified. This will result in an exception
                # if it is not a valid MGRS coordinate
                text2 = re.sub(r'\s+', '', str(text))  # Remove all white space
                lat, lon = mgrs.toWgs(text2)
                return (lat, lon, epsg4326)

            if self.settings.zoomToProjIsPlusCodes():
                # A Plus Codes coordinate has been selected. This will result in an exception
                # if it is not a valid plus codes coordinate.
                coord = olc.decode(text)
                lat = coord.latitudeCenter
                lon = coord.longitudeCenter
                return (lat, lon, epsg4326)

            if self.settings.zoomToProjIsStandardUtm():
                # A Standard UTM coordinate has been selected. This will result in an exception
                # if it is not a valid utm coordinate.
                pt = utmString2Crs(text)
                return (pt.y(), pt.x(), epsg4326)

            if self.settings.zoomToProjIsGeohash():
                # A Geohash coordinate has been selected. This will result in an exception
                # if it is not a valid Geohash coordinate.
                (lat, lon) = geohash.decode(text)
                return (float(lat), float(lon), epsg4326)

            # Check for other formats
            if text[0] == '{':  # This may be a GeoJSON point
                codec = QTextCodec.codecForName("UTF-8")
                fields = QgsJsonUtils.stringToFields(text, codec)
                fet = QgsJsonUtils.stringToFeatureList(text, fields, codec)
                if (len(fet) == 0) or not fet[0].isValid():
                    raise ValueError('Invalid Coordinates')

                geom = fet[0].geometry()
                if geom.isEmpty() or (geom.wkbType() != QgsWkbTypes.Point):
                    raise ValueError('Invalid GeoJSON Geometry')
                pt = geom.asPoint()
                return (pt.y(), pt.x(), epsg4326)

            # Check to see if it is standard UTM
            if isUtm(text):
                pt = utmString2Crs(text)
                return (pt.y(), pt.x(), epsg4326)

            # Check to see if it is an MGRS coordinate
            try:
                text2 = re.sub(r'\s+', '', str(text))
                lat, lon = mgrs.toWgs(text2)
                return (lat, lon, epsg4326)
            except Exception:
                pass

            # Check to see if it is a plus codes string
            try:
                coord = olc.decode(text)
                lat = coord.latitudeCenter
                lon = coord.longitudeCenter
                return (lat, lon, epsg4326)
            except Exception:
                pass

            # Check to see if it is a WKT POINT format
            if re.search(r'POINT\(', text) is not None:
                m = re.findall(
                    r'POINT\(\s*([+-]?\d*\.?\d*)\s+([+-]?\d*\.?\d*)', text)
                if len(m) != 1:
                    raise ValueError('Invalid Coordinates')
                lon = float(m[0][0])
                lat = float(m[0][1])
                if self.settings.zoomToProjIsWgs84():
                    srcCrs = epsg4326
                elif self.settings.zoomToProjIsProjectCRS():
                    srcCrs = self.canvas.mapSettings().destinationCrs()
                else:
                    srcCrs = self.settings.zoomToCustomCRS()
                return (lat, lon, srcCrs)

            # We are left with either DMS or decimal degrees in one of the projections
            if self.settings.zoomToProjIsWgs84():
                lat, lon = parseDMSString(text, self.settings.zoomToCoordOrder)
                return (lat, lon, epsg4326)

            # We are left with a non WGS 84 decimal projection
            coords = re.split(r'[\s,;:]+', text, 1)
            if len(coords) < 2:
                raise ValueError('Invalid Coordinates')
            if self.settings.zoomToCoordOrder == self.settings.OrderYX:
                lat = float(coords[0])
                lon = float(coords[1])
            else:
                lon = float(coords[0])
                lat = float(coords[1])
            if self.settings.zoomToProjIsProjectCRS():
                srcCrs = self.canvas.mapSettings().destinationCrs()
            else:
                srcCrs = self.settings.zoomToCustomCRS()
            return (lat, lon, srcCrs)

        except Exception:
            raise ValueError('Invalid Coordinates')
Beispiel #18
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 #19
0
    def save_vector_data(self,
                         metadata_layer,
                         post_layer_data,
                         has_transactions,
                         post_save_signal=True,
                         **kwargs):
        """Save vector editing data

        :param metadata_layer: metadata of the layer being edited
        :type metadata_layer: MetadataVectorLayer
        :param post_layer_data: post data with 'add', 'delete' etc.
        :type post_layer_data: dict
        :param has_transactions: true if the layer support transactions
        :type has_transactions: bool
        :param post_save_signal: if this is a post_save_signal call, defaults to True
        :type post_save_signal: bool, optional
        """

        # Check atomic capabilities for validation
        # -----------------------------------------------
        #for mode_editing in (EDITING_POST_DATA_ADDED, EDITING_POST_DATA_UPDATED, EDITING_POST_DATA_DELETED):

        # try to get layer model object from metatada_layer
        layer = getattr(metadata_layer, 'layer', self.layer)

        if EDITING_POST_DATA_ADDED in post_layer_data and len(
                post_layer_data[EDITING_POST_DATA_ADDED]) > 0:
            if not self.request.user.has_perm('qdjango.add_feature', layer):
                raise ValidationError(
                    _('Sorry but your user doesn\'t has \'Add Feature\' capability'
                      ))

        if EDITING_POST_DATA_DELETED in post_layer_data and len(
                post_layer_data[EDITING_POST_DATA_DELETED]) > 0:
            if not self.request.user.has_perm('qdjango.delete_feature', layer):
                raise ValidationError(
                    _('Sorry but your user doesn\'t has \'Delete Feature\' capability'
                      ))

        if EDITING_POST_DATA_UPDATED in post_layer_data and len(
                post_layer_data[EDITING_POST_DATA_UPDATED]) > 0:
            if not self.request.user.has_perm('qdjango.change_feature', layer) and \
                    not self.request.user.has_perm('qdjango.change_attr_feature', layer):
                raise ValidationError(
                    _('Sorry but your user doesn\'t has \'Change or Change Attributes Features\' capability'
                      ))

        # get initial featurelocked
        metadata_layer.lock.getInitialFeatureLockedIds()

        # get lockids from client
        metadata_layer.lock.setLockeFeaturesFromClient(
            post_layer_data['lockids'])

        # data for response
        insert_ids = list()
        lock_ids = list()

        # FIXME: check this out
        # for add check if is a metadata_layer and referenced field is a pk
        is_referenced_field_is_pk = 'referenced_layer_insert_ids' in kwargs and kwargs['referenced_layer_insert_ids'] \
            and hasattr(metadata_layer, 'referenced_field_is_pk') \
            and metadata_layer.referenced_field_is_pk

        # Get the layer
        qgis_layer = metadata_layer.qgis_layer

        for mode_editing in (EDITING_POST_DATA_ADDED,
                             EDITING_POST_DATA_UPDATED):

            if mode_editing in post_layer_data:

                for geojson_feature in post_layer_data[mode_editing]:
                    data_extra_fields = {'feature': geojson_feature}

                    # Clear any old error
                    qgis_layer.dataProvider().clearErrors()

                    # add media data
                    self.add_media_property(geojson_feature, metadata_layer)

                    # for GEOSGeometry of Django 2.2 it must add crs to feature if is not set if a geo feature
                    if metadata_layer.geometry_type != QGIS_LAYER_TYPE_NO_GEOM:
                        if geojson_feature[
                                'geometry'] and 'crs' not in geojson_feature[
                                    'geometry']:
                            geojson_feature['geometry'][
                                'crs'] = "{}:{}".format(
                                    self.layer.project.group.srid.auth_name,
                                    self.layer.project.group.srid.auth_srid)

                    # reproject data if necessary
                    if kwargs[
                            'reproject'] and metadata_layer.geometry_type != QGIS_LAYER_TYPE_NO_GEOM:
                        self.reproject_feature(geojson_feature, to_layer=True)

                    # case relation data ADD, if father referenced field is pk
                    if is_referenced_field_is_pk:
                        for newid in kwargs['referenced_layer_insert_ids']:
                            if geojson_feature['properties'][
                                    metadata_layer.
                                    referencing_field] == newid['clientid']:
                                geojson_feature['properties'][
                                    metadata_layer.
                                    referencing_field] = newid['id']

                    if mode_editing == EDITING_POST_DATA_UPDATED:
                        # control feature locked
                        if not metadata_layer.lock.checkFeatureLocked(
                                geojson_feature['id']):
                            raise Exception(
                                self.no_more_lock_feature_msg.format(
                                    geojson_feature['id'],
                                    metadata_layer.client_var))

                    # Send for validation
                    # Note that this may raise a validation error
                    pre_save_maplayer.send(self,
                                           layer_metadata=metadata_layer,
                                           mode=mode_editing,
                                           data=data_extra_fields,
                                           user=self.request.user)

                    # Validate and save
                    try:

                        original_feature = None
                        feature = QgsFeature(qgis_layer.fields())
                        if mode_editing == EDITING_POST_DATA_UPDATED:

                            # add patch for shapefile type, geojson_feature['id'] id int() instead of str()
                            # path to fix into QGIS api
                            geojson_feature[
                                'id'] = get_layer_fids_from_server_fids(
                                    [str(geojson_feature['id'])],
                                    qgis_layer)[0]
                            feature.setId(geojson_feature['id'])

                            # Get feature from data provider before update
                            original_feature = qgis_layer.getFeature(
                                geojson_feature['id'])

                        # We use this feature for geometry parsing only:
                        imported_feature = QgsJsonUtils.stringToFeatureList(
                            json.dumps(geojson_feature),
                            qgis_layer.fields(),
                            None  # UTF8 codec
                        )[0]

                        feature.setGeometry(imported_feature.geometry())

                        # There is something wrong in QGIS 3.10 (fixed in later versions)
                        # so, better loop through the fields and set attributes individually
                        for name, value in geojson_feature['properties'].items(
                        ):
                            feature.setAttribute(name, value)

                        # Loop again for set expressions value:
                        # For update store expression result to use later into update condition
                        field_expresion_values = {}
                        for qgis_field in qgis_layer.fields():
                            if qgis_field.defaultValueDefinition().expression(
                            ):
                                exp = QgsExpression(
                                    qgis_field.defaultValueDefinition(
                                    ).expression())
                                if exp.rootNode().nodeType(
                                ) != QgsExpressionNode.ntLiteral and not exp.hasParserError(
                                ):
                                    context = QgsExpressionContextUtils.createFeatureBasedContext(
                                        feature, qgis_layer.fields())
                                    context.appendScopes(
                                        QgsExpressionContextUtils.
                                        globalProjectLayerScopes(qgis_layer))
                                    result = exp.evaluate(context)
                                    if not exp.hasEvalError():
                                        feature.setAttribute(
                                            qgis_field.name(), result)

                                        # Check update if expression default value has to run also on update e not
                                        # only on insert newone
                                        if qgis_field.defaultValueDefinition(
                                        ).applyOnUpdate():
                                            field_expresion_values[
                                                qgis_field.name()] = result

                            elif qgis_field.typeName() in ('date', 'datetime',
                                                           'time'):

                                if qgis_field.typeName() == 'date':
                                    qtype = QDate
                                elif qgis_field.typeName() == 'datetime':
                                    qtype = QDateTime
                                else:
                                    qtype = QTime

                                field_idx = qgis_layer.fields().indexFromName(
                                    qgis_field.name())
                                options = qgis_layer.editorWidgetSetup(
                                    field_idx).config()

                                if 'field_iso_format' in options and not options[
                                        'field_iso_format']:
                                    if geojson_feature['properties'][
                                            qgis_field.name()]:
                                        value = qtype.fromString(
                                            geojson_feature['properties'][
                                                qgis_field.name()],
                                            options['field_format'])
                                        feature.setAttribute(
                                            qgis_field.name(), value)

                        # Call validator!
                        errors = feature_validator(feature,
                                                   metadata_layer.qgis_layer)
                        if errors:
                            raise ValidationError(errors)

                        # Save the feature
                        if mode_editing == EDITING_POST_DATA_ADDED:
                            if has_transactions:
                                if not qgis_layer.addFeature(feature):
                                    raise Exception(
                                        _('Error adding feature: %s') %
                                        ', '.join(qgis_layer.dataProvider().
                                                  errors()))
                            else:
                                if not qgis_layer.dataProvider().addFeature(
                                        feature):
                                    raise Exception(
                                        _('Error adding feature: %s') %
                                        ', '.join(qgis_layer.dataProvider().
                                                  errors()))

                                # Patch for Spatialite provider on pk
                                if qgis_layer.dataProvider().name(
                                ) == 'spatialite':
                                    pks = qgis_layer.primaryKeyAttributes()
                                    if len(pks) > 1:
                                        raise Exception(
                                            _(f'Error adding feature on Spatialite provider: '
                                              f'layer {qgis_layer.id()} has more than one pk column'
                                              ))

                                    # update pk attribute:
                                    feature.setAttribute(
                                        pks[0],
                                        server_fid(feature,
                                                   qgis_layer.dataProvider()))

                        elif mode_editing == EDITING_POST_DATA_UPDATED:
                            attr_map = {}
                            for name, value in geojson_feature[
                                    'properties'].items():
                                if name in qgis_layer.dataProvider(
                                ).fieldNameMap():
                                    if name in field_expresion_values:
                                        value = field_expresion_values[name]
                                    attr_map[qgis_layer.dataProvider().
                                             fieldNameMap()[name]] = value

                            if has_transactions:
                                if not qgis_layer.changeAttributeValues(
                                        geojson_feature['id'], attr_map):
                                    raise Exception(
                                        _('Error changing attribute values: %s'
                                          ) %
                                        ', '.join(qgis_layer.dataProvider().
                                                  errors()))
                                # Check for errors because of https://github.com/qgis/QGIS/issues/36583
                                if qgis_layer.dataProvider().errors():
                                    raise Exception(', '.join(
                                        qgis_layer.dataProvider().errors()))
                                if not feature.geometry().isNull(
                                ) and not qgis_layer.changeGeometry(
                                        geojson_feature['id'],
                                        feature.geometry()):
                                    raise Exception(
                                        _('Error changing geometry: %s') %
                                        ', '.join(qgis_layer.dataProvider().
                                                  errors()))
                            else:
                                if not qgis_layer.dataProvider(
                                ).changeAttributeValues(
                                    {geojson_feature['id']: attr_map}):
                                    raise Exception(
                                        _('Error changing attribute values: %s'
                                          ) %
                                        ', '.join(qgis_layer.dataProvider().
                                                  errors()))
                                if not feature.geometry().isNull(
                                ) and not qgis_layer.dataProvider(
                                ).changeGeometryValues({
                                        geojson_feature['id']:
                                        feature.geometry()
                                }):
                                    raise Exception(
                                        _('Error changing geometry: %s') %
                                        ', '.join(qgis_layer.dataProvider().
                                                  errors()))

                        to_res = {}
                        to_res_lock = {}

                        if mode_editing == EDITING_POST_DATA_ADDED:

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

                            fnames = [f.name() for f in feature.fields()]
                            jfeature = json.loads(
                                ex.exportFeature(
                                    feature,
                                    dict(zip(fnames, feature.attributes()))))

                            to_res.update({
                                'clientid':
                                geojson_feature['id'],
                                # This might be the internal QGIS feature id (< 0)
                                'id':
                                server_fid(
                                    feature,
                                    metadata_layer.qgis_layer.dataProvider()),
                                'properties':
                                jfeature['properties']
                            })

                            # lock news:
                            to_res_lock = metadata_layer.lock.modelLock2dict(
                                metadata_layer.lock.lockFeature(server_fid(
                                    feature,
                                    metadata_layer.qgis_layer.dataProvider()),
                                                                save=True))

                        if bool(to_res):
                            insert_ids.append(to_res)
                        if bool(to_res_lock):
                            lock_ids.append(to_res_lock)

                        # Send post vase signal
                        post_save_maplayer.send(
                            self,
                            layer_metadata=metadata_layer,
                            mode=mode_editing,
                            data=data_extra_fields,
                            user=self.request.user,
                            original_feature=original_feature,
                            to_res=to_res)

                    except ValidationError as ex:
                        raise ValidationError({
                            metadata_layer.client_var: {
                                mode_editing: {
                                    'id': geojson_feature['id'],
                                    'fields': ex.detail,
                                }
                            }
                        })

                    except Exception as ex:
                        raise ValidationError({
                            metadata_layer.client_var: {
                                mode_editing: {
                                    'id': geojson_feature['id'],
                                    'fields': str(ex),
                                }
                            }
                        })

        # erasing feature if to do
        if EDITING_POST_DATA_DELETED in post_layer_data:

            fids = post_layer_data[EDITING_POST_DATA_DELETED]

            # get feature fids from server fids from client.
            fids = get_layer_fids_from_server_fids([str(id) for id in fids],
                                                   qgis_layer)

            for feature_id in fids:

                # control feature locked
                if not metadata_layer.lock.checkFeatureLocked(str(feature_id)):
                    raise Exception(
                        self.no_more_lock_feature_msg.format(
                            feature_id, metadata_layer.client_var))

                # Get feature to delete
                ex = QgsJsonExporter(qgis_layer)
                deleted_feature = ex.exportFeature(
                    qgis_layer.getFeature(feature_id))

                pre_delete_maplayer.send(self,
                                         layer_metatada=metadata_layer,
                                         data=deleted_feature,
                                         user=self.request.user)

                qgis_layer.dataProvider().clearErrors()

                if has_transactions:
                    if not qgis_layer.deleteFeatures(
                        [feature_id]) or qgis_layer.dataProvider().errors():
                        raise Exception(
                            _('Cannot delete feature: %s') %
                            ', '.join(qgis_layer.dataProvider().errors()))
                else:
                    if not qgis_layer.dataProvider().deleteFeatures(
                        [feature_id]) or qgis_layer.dataProvider().errors():
                        raise Exception(
                            _('Cannot delete feature: %s') %
                            ', '.join(qgis_layer.dataProvider().errors()))

        return insert_ids, lock_ids
Beispiel #20
0
def expression_eval(expression_text,
                    project_id=None,
                    qgs_layer_id=None,
                    form_data=None,
                    formatter=0):
    """Evaluates a QgsExpression and returns the result

    :param expression_text: The QgsExpression text
    :type expression_text: str
    :param project_id: ID of the qdjango project, defaults to None
    :type project_id: int, optional
    :param qgs_layer_id: ID of the QGIS Layer, defaults to None
    :type qgslayer_id: str, optional
    :param form_data: A dictionary that maps to a GeoJSON representation of the feature currently edited in the form
    :type form_data: dict, optional
    :param formatter: Indicate if form_data values contains formatter values or original features value.
    :type formatter: int, optional
    """

    expression = QgsExpression(expression_text)
    expression_context = QgsExpressionContext()

    layer = None

    for func_name in expression.referencedFunctions():
        if func_name in FORBIDDEN_FUNCTIONS:
            raise ExpressionForbiddenError(
                _('Function "{}" is not allowed for security reasons!').format(
                    func_name))

    for var_name in expression.referencedVariables():
        if var_name in FORBIDDEN_VARIABLES:
            raise ExpressionForbiddenError(
                _('Variable "{}" is not allowed for security reasons!').format(
                    var_name))

    if project_id is not None:

        try:
            project = Project.objects.get(pk=project_id)

            if qgs_layer_id is not None:
                try:
                    layer = project.layer_set.get(qgs_layer_id=qgs_layer_id)
                except Layer.DoesNotExist:
                    raise ExpressionLayerError(
                        _('QGIS layer with id "{}" could not be found!').
                        format(qgs_layer_id))

                expression_contex = QgsExpressionContextUtils.globalProjectLayerScopes(
                    layer.qgis_layer)

            else:
                expression_contex = QgsExpressionContextUtils.globalScope()
                expression_context.appendScope(
                    QgsExpressionContextUtils.projectScope(
                        project.qgis_project))

        except Project.DoesNotExist:
            raise ExpressionProjectError(
                _('QDjango project with id "{}" could not be found!').format(
                    project_id))

    else:
        expression_contex = QgsExpressionContextUtils.globalScope()

    if form_data is not None:

        if layer is None:
            raise ExpressionLayerError(
                _('A valid QGIS layer is required to process form data!'))

        try:
            # Case by formatter
            # formatter == 1 : get featureid from layer, usually must be used with formatter form_data
            # formatter == 0 : default behavior
            if formatter == 0:
                fields = layer.qgis_layer.fields()
                form_feature = QgsJsonUtils.stringToFeatureList(
                    json.dumps(form_data), fields, None)[0]

                # Set attributes manually because QgsJsonUtils does not respect order
                for k, v in form_data['properties'].items():
                    form_feature.setAttribute(k, v)
            else:
                qgis_feature_request = QgsFeatureRequest()
                exp = expression_from_server_fids(
                    [form_data['id']], layer.qgis_layer.dataProvider())
                qgis_feature_request.combineFilterExpression(exp)
                form_feature = get_qgis_features(layer.qgis_layer,
                                                 qgis_feature_request)[0]

            expression_context.appendScope(
                QgsExpressionContextUtils.formScope(form_feature))
            expression_context.setFeature(form_feature)
        except:
            raise ExpressionFormDataError()

    valid, errors = expression.checkExpression(expression_text,
                                               expression_context)

    if not valid:
        raise ExpressionParseError(errors)

    result = expression.evaluate(expression_context)

    if expression.hasEvalError():
        raise ExpressionEvalError(expression.evalErrorString())

    return result
Beispiel #21
0
    def save_vector_data(self,
                         metadata_layer,
                         post_layer_data,
                         has_transactions,
                         post_save_signal=True,
                         **kwargs):
        """Save vector editing data

        :param metadata_layer: metadata of the layer being edited
        :type metadata_layer: MetadataVectorLayer
        :param post_layer_data: post data with 'add', 'delete' etc.
        :type post_layer_data: dict
        :param has_transactions: true if the layer support transactions
        :type has_transactions: bool
        :param post_save_signal: if this is a post_save_signal call, defaults to True
        :type post_save_signal: bool, optional
        """

        # get initial featurelocked
        metadata_layer.lock.getInitialFeatureLockedIds()

        # get lockids from client
        metadata_layer.lock.setLockeFeaturesFromClient(
            post_layer_data['lockids'])

        # data for response
        insert_ids = list()
        lock_ids = list()

        # FIXME: check this out
        # for add check if is a metadata_layer and referenced field is a pk
        is_referenced_field_is_pk = 'referenced_layer_insert_ids' in kwargs and kwargs['referenced_layer_insert_ids'] \
            and hasattr(metadata_layer, 'referenced_field_is_pk') \
            and metadata_layer.referenced_field_is_pk

        # Get the layer
        qgis_layer = metadata_layer.qgis_layer

        for mode_editing in (EDITING_POST_DATA_ADDED,
                             EDITING_POST_DATA_UPDATED):

            if mode_editing in post_layer_data:

                for geojson_feature in post_layer_data[mode_editing]:
                    data_extra_fields = {'feature': geojson_feature}

                    # Clear any old error
                    qgis_layer.dataProvider().clearErrors()

                    # add media data
                    self.add_media_property(geojson_feature, metadata_layer)

                    # for GEOSGeometry of Django 2.2 it must add crs to feature if is not set if a geo feature
                    if metadata_layer.geometry_type != QGIS_LAYER_TYPE_NO_GEOM:
                        self.add_crs_to_feature(geojson_feature)

                    # reproject data if necessary
                    if self.reproject:
                        self.reproject_feature(geojson_feature, to_layer=True)

                    # case relation data ADD, if father referenced field is pk
                    if is_referenced_field_is_pk:
                        for newid in kwargs['referenced_layer_insert_ids']:
                            if geojson_feature['properties'][
                                    metadata_layer.
                                    referencing_field] == newid['clientid']:
                                geojson_feature['properties'][
                                    metadata_layer.
                                    referencing_field] = newid['id']

                    if mode_editing == EDITING_POST_DATA_UPDATED:
                        # control feature locked
                        if not metadata_layer.lock.checkFeatureLocked(
                                geojson_feature['id']):
                            raise Exception(
                                self.no_more_lock_feature_msg.format(
                                    geojson_feature['id'],
                                    metadata_layer.client_var))

                    # Send for validation
                    # Note that this may raise a validation error
                    pre_save_maplayer.send(self,
                                           layer_metadata=metadata_layer,
                                           mode=mode_editing,
                                           data=data_extra_fields,
                                           user=self.request.user)

                    # Validate and save
                    try:

                        feature = QgsFeature(qgis_layer.fields())
                        if mode_editing == EDITING_POST_DATA_UPDATED:
                            feature.setId(geojson_feature['id'])

                        # We use this feature for geometry parsing only:
                        imported_feature = QgsJsonUtils.stringToFeatureList(
                            json.dumps(geojson_feature),
                            qgis_layer.fields(),
                            None  # UTF8 codec
                        )[0]

                        feature.setGeometry(imported_feature.geometry())

                        # There is something wrong in QGIS 3.10 (fixed in later versions)
                        # so, better loop through the fields and set attributes individually
                        for name, value in geojson_feature['properties'].items(
                        ):
                            feature.setAttribute(name, value)

                        # Call validator!
                        errors = feature_validator(feature,
                                                   metadata_layer.qgis_layer)
                        if errors:
                            raise ValidationError(errors)

                        # Save the feature
                        if mode_editing == EDITING_POST_DATA_ADDED:
                            if has_transactions:
                                if not qgis_layer.addFeature(feature):
                                    raise Exception(
                                        _('Error adding feature: %s') %
                                        ', '.join(qgis_layer.dataProvider().
                                                  errors()))
                            else:
                                if not qgis_layer.dataProvider().addFeature(
                                        feature):
                                    raise Exception(
                                        _('Error adding feature: %s') %
                                        ', '.join(qgis_layer.dataProvider().
                                                  errors()))

                                # Patch for Spatialite provider on pk
                                if qgis_layer.dataProvider().name(
                                ) == 'spatialite':
                                    pks = qgis_layer.primaryKeyAttributes()
                                    if len(pks) > 1:
                                        raise Exception(
                                            _(f'Error adding feature on Spatialite provider: '
                                              f'layer {qgis_layer.id()} has more than one pk column'
                                              ))

                                    # update pk attribute:
                                    feature.setAttribute(pks[0], feature.id())

                        elif mode_editing == EDITING_POST_DATA_UPDATED:
                            attr_map = {}
                            for name, value in geojson_feature[
                                    'properties'].items():
                                if name in qgis_layer.dataProvider(
                                ).fieldNameMap():
                                    attr_map[qgis_layer.dataProvider().
                                             fieldNameMap()[name]] = value

                            if has_transactions:
                                if not qgis_layer.changeAttributeValues(
                                        geojson_feature['id'], attr_map):
                                    raise Exception(
                                        _('Error changing attribute values: %s'
                                          ) %
                                        ', '.join(qgis_layer.dataProvider().
                                                  errors()))
                                # Check for errors because of https://github.com/qgis/QGIS/issues/36583
                                if qgis_layer.dataProvider().errors():
                                    raise Exception(', '.join(
                                        qgis_layer.dataProvider().errors()))
                                if not feature.geometry().isNull(
                                ) and not qgis_layer.changeGeometry(
                                        geojson_feature['id'],
                                        feature.geometry()):
                                    raise Exception(
                                        _('Error changing geometry: %s') %
                                        ', '.join(qgis_layer.dataProvider().
                                                  errors()))
                            else:
                                if not qgis_layer.dataProvider(
                                ).changeAttributeValues(
                                    {geojson_feature['id']: attr_map}):
                                    raise Exception(
                                        _('Error changing attribute values: %s'
                                          ) %
                                        ', '.join(qgis_layer.dataProvider().
                                                  errors()))
                                if not feature.geometry().isNull(
                                ) and not qgis_layer.dataProvider(
                                ).changeGeometryValues({
                                        geojson_feature['id']:
                                        feature.geometry()
                                }):
                                    raise Exception(
                                        _('Error changing geometry: %s') %
                                        ', '.join(qgis_layer.dataProvider().
                                                  errors()))

                        to_res = {}
                        to_res_lock = {}

                        if mode_editing == EDITING_POST_DATA_ADDED:

                            ex = QgsJsonExporter(qgis_layer)
                            jfeature = json.loads(ex.exportFeature(feature))

                            to_res.update({
                                'clientid': geojson_feature['id'],
                                # This might be the internal QGIS feature id (< 0)
                                'id': feature.id(),
                                'properties': jfeature['properties']
                            })

                            # lock news:
                            to_res_lock = metadata_layer.lock.modelLock2dict(
                                metadata_layer.lock.lockFeature(feature.id(),
                                                                save=True))

                        if bool(to_res):
                            insert_ids.append(to_res)
                        if bool(to_res_lock):
                            lock_ids.append(to_res_lock)

                    except ValidationError as ex:
                        raise ValidationError({
                            metadata_layer.client_var: {
                                mode_editing: {
                                    'id': geojson_feature['id'],
                                    'fields': ex.detail,
                                }
                            }
                        })

                    except Exception as ex:
                        raise ValidationError({
                            metadata_layer.client_var: {
                                mode_editing: {
                                    'id': geojson_feature['id'],
                                    'fields': str(ex),
                                }
                            }
                        })

        # erasing feature if to do
        if EDITING_POST_DATA_DELETED in post_layer_data:

            for feature_id in post_layer_data[EDITING_POST_DATA_DELETED]:

                # control feature locked
                if not metadata_layer.lock.checkFeatureLocked(feature_id):
                    raise Exception(
                        self.no_more_lock_feature_msg.format(
                            feature_id, metadata_layer.client_var))

                # FIXME: pre_delete_maplayer
                # pre_delete_maplayer.send(metadata_layer.serializer, layer=metadata_layer.layer_id, # data=serializer.data, user=self.request.user)

                qgis_layer.dataProvider().clearErrors()

                if has_transactions:
                    if not qgis_layer.deleteFeatures(
                        [feature_id]) or qgis_layer.dataProvider().errors():
                        raise Exception(
                            _('Cannot delete feature: %s') %
                            ', '.join(qgis_layer.dataProvider().errors()))
                else:
                    if not qgis_layer.dataProvider().deleteFeatures(
                        [feature_id]) or qgis_layer.dataProvider().errors():
                        raise Exception(
                            _('Cannot delete feature: %s') %
                            ', '.join(qgis_layer.dataProvider().errors()))

        return insert_ids, lock_ids
Beispiel #22
0
    def zoomToPressed(self):
        try:
            text = self.coordTxt.text().strip()
            if text[0] == '{':  # This may be a GeoJSON point
                codec = QTextCodec.codecForName("UTF-8")
                fields = QgsJsonUtils.stringToFields(text, codec)
                fet = QgsJsonUtils.stringToFeatureList(text, fields, codec)
                if (len(fet) == 0) or not fet[0].isValid():
                    raise ValueError('Invalid Coordinates')

                geom = fet[0].geometry()
                if geom.isEmpty() or (geom.wkbType() != QgsWkbTypes.Point):
                    raise ValueError('Invalid GeoJSON Geometry')
                pt = geom.asPoint()
                lat = pt.y()
                lon = pt.x()
                srcCrs = epsg4326
            elif self.settings.zoomToProjIsWgs84():
                if re.search(r'POINT\(', text) == None:
                    lat, lon = LatLon.parseDMSString(
                        text, self.settings.zoomToCoordOrder)
                else:
                    m = re.findall(
                        r'POINT\(\s*([+-]?\d*\.?\d*)\s+([+-]?\d*\.?\d*)', text)
                    if len(m) != 1:
                        raise ValueError('Invalid Coordinates')
                    lon = float(m[0][0])
                    lat = float(m[0][1])
                srcCrs = epsg4326
            elif self.settings.zoomToProjIsPlusCodes() and olc.isValid(text):
                # This looks like a Plus Codes coordinate
                coord = olc.decode(text)
                srcCrs = epsg4326
                lat = coord.latitudeCenter
                lon = coord.longitudeCenter
            elif self.settings.zoomToProjIsMGRS():
                # This is an MGRS coordinate
                text = re.sub(r'\s+', '', str(text))  # Remove all white space
                lat, lon = mgrs.toWgs(text)
                srcCrs = epsg4326
            else:  # Is either the project or custom CRS
                if re.search(r'POINT\(', text) == None:
                    coords = re.split(r'[\s,;:]+', text, 1)
                    if len(coords) < 2:
                        raise ValueError('Invalid Coordinates')
                    if self.settings.zoomToCoordOrder == self.settings.OrderYX:
                        lat = float(coords[0])
                        lon = float(coords[1])
                    else:
                        lon = float(coords[0])
                        lat = float(coords[1])
                else:
                    m = re.findall(
                        r'POINT\(\s*([+-]?\d*\.?\d*)\s+([+-]?\d*\.?\d*)', text)
                    if len(m) != 1:
                        raise ValueError('Invalid Coordinates')
                    lon = float(m[0][0])
                    lat = float(m[0][1])
                if self.settings.zoomToProjIsProjectCRS():
                    srcCrs = self.canvas.mapSettings().destinationCrs()
                else:
                    srcCrs = self.settings.zoomToCustomCRS()

            pt = self.lltools.zoomTo(srcCrs, lat, lon)
            if self.settings.persistentMarker:
                if self.marker is None:
                    self.marker = QgsVertexMarker(self.canvas)
                self.marker.setCenter(pt)
                self.marker.setIconSize(18)
                self.marker.setPenWidth(2)
                self.marker.setIconType(QgsVertexMarker.ICON_CROSS)
            elif self.marker is not None:
                self.removeMarker()
        except:
            #traceback.print_exc()
            self.iface.messageBar().pushMessage("",
                                                "Invalid Coordinate",
                                                level=Qgis.Warning,
                                                duration=2)
            return
Beispiel #23
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
Beispiel #24
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 addFeature(self):
     text = self.lineEdit.text().strip()
     if text == "":
         return
     layer = self.iface.activeLayer()
     if layer == None:
         return
     try:
         if (self.inputProjection == 0) or (text[0] == '{'):
             # If this is GeoJson it does not matter what inputProjection is
             if text[0] == '{': # This may be a GeoJSON point
                 codec = QTextCodec.codecForName("UTF-8")
                 fields = QgsJsonUtils.stringToFields(text, codec)
                 fet = QgsJsonUtils.stringToFeatureList(text, fields, codec)
                 if (len(fet) == 0) or not fet[0].isValid():
                     raise ValueError('Invalid Coordinates')
                 
                 geom = fet[0].geometry()
                 if geom.isEmpty() or (geom.wkbType() != QgsWkbTypes.Point):
                     raise ValueError('Invalid GeoJSON Geometry')
                 pt = geom.asPoint()
                 lat = pt.y()
                 lon = pt.x()
             elif re.search(r'POINT\(', text) != None:
                 m = re.findall(r'POINT\(\s*([+-]?\d*\.?\d*)\s+([+-]?\d*\.?\d*)', text)
                 if len(m) != 1:
                     raise ValueError('Invalid Coordinates')
                 lon = float(m[0][0])
                 lat = float(m[0][1])
             else:
                 lat, lon = LatLon.parseDMSString(text, self.inputXYOrder)
             srcCrs = epsg4326
         elif self.inputProjection == 1:
             # This is an MGRS coordinate
             text = re.sub(r'\s+', '', unicode(text)) # Remove all white space
             lat, lon = mgrs.toWgs(text)
             srcCrs = epsg4326
         elif self.inputProjection == 4:
             text = text.strip()
             coord = olc.decode(text)
             lat = coord.latitudeCenter
             lon = coord.longitudeCenter
             srcCrs = epsg4326
         else: # Is either the project or custom CRS
             if re.search(r'POINT\(', text) == None:
                 coords = re.split(r'[\s,;:]+', text, 1)
                 if len(coords) < 2:
                     raise ValueError('Invalid Coordinates')
                 if self.inputXYOrder == 0: # Y, X Order
                     lat = float(coords[0])
                     lon = float(coords[1])
                 else:
                     lon = float(coords[0])
                     lat = float(coords[1])
             else:
                 m = re.findall(r'POINT\(\s*([+-]?\d*\.?\d*)\s+([+-]?\d*\.?\d*)', text)
                 if len(m) != 1:
                     raise ValueError('Invalid Coordinates')
                 lon = float(m[0][0])
                 lat = float(m[0][1])
             if self.inputProjection == 2: # Project CRS
                 srcCrs = self.canvas.mapSettings().destinationCrs()
             else:
                 srcCrs = QgsCoordinateReferenceSystem(self.inputCustomCRS)
     except:
         #traceback.print_exc()
         self.iface.messageBar().pushMessage("", "Invalid Coordinate" , level=Qgis.Warning, duration=2)
         return
     self.lineEdit.clear()
     caps = layer.dataProvider().capabilities()
     if caps & QgsVectorDataProvider.AddFeatures:
         destCRS = layer.crs() # Get the CRS of the layer we are adding a point toWgs
         transform = QgsCoordinateTransform(srcCrs, destCRS, QgsProject.instance())
         # Transform the input coordinate projection to the layer CRS
         x, y = transform.transform(float(lon), float(lat))
         feat = QgsFeature(layer.fields())
         feat.setGeometry(QgsGeometry.fromPointXY(QgsPointXY(x, y)))
         if layer.fields().count() == 0:
             layer.addFeature(feat)
             self.lltools.zoomTo(srcCrs, lat, lon)
         else:
             if self.iface.openFeatureForm(layer, feat):
                 layer.addFeature(feat)
                 self.lltools.zoomTo(srcCrs, lat, lon)