Reading GeoJSON-String from CSV as Geometry in QGIS

If you can retrieve the GeoJSON text in a Python object, you can execute the code below :

# geoj is the string object that contains the GeoJSON
geoj = """
{ "type": "MultiLineString",
    "coordinates": [
        [[10, 10], [20, 20], [10, 40]],
        [[40, 40], [30, 30], [40, 20], [30, 10]]
    ]
}
"""
# PyQGIS has a parser class for JSON and GeoJSON
feats = QgsJsonUtils.stringToFeatureList(geoj, QgsFields(), None)
# if there are features in the list
if len(feats) > 0:
    # define the geometry type of the layer
    geom_type = feats[0].geometry().type()
    if geom_type == QgsWkbTypes.PointGeometry:
        geotype = "MultiPoint"
    elif geom_type == QgsWkbTypes.LineGeometry:
        geotype = "MultiLineString"
    elif geom_type == QgsWkbTypes.PolygonGeometry:
        geotype = "MultiLinePolygon"
    else:
        print("geometry type not defined in the script")
    # create a new memory layer with the features
    vl = QgsVectorLayer(geotype, "geojson_layer", "memory")
    with edit(vl):
        vl.addFeatures(feats)
        vl.updateExtents()
    # add this brand new layer
    QgsProject.instance().addMapLayer(vl)
else:
    print("no features found in the geoJSON")

Based on J. Monticolo's answer I have modified his script. The first one is for python console and the second one a processing algorithm. Both will ignore features, that have an invalid GeoJSON-String as content.

Script for Python-Console:

# Define settings:
source_layer = QgsProject.instance().mapLayersByName("mylayer")[0] # Define Source-layer
new_layer = QgsVectorLayer('MultiLineString', "new_geojson_layer", "memory") # Define new layer and its type
source_geojsonfield = 'geojsonstring' # Fieldname containing the GeoJSON

#####
# No Changes needed below #
#####

# Copy fields from old layer to new layer
source_fields = source_layer.fields()
new_layer_pr = new_layer.dataProvider()
new_layer.startEditing()
new_layer_pr.addAttributes(source_fields)
new_layer.commitChanges()

for feature in source_layer.getFeatures():
    # geoj is the string object that contains the GeoJSON
    geoj = feature.attributes()[source_fields.indexFromName(source_geojsonfield)]
    # PyQGIS has a parser class for JSON and GeoJSON
    geojfeats = QgsJsonUtils.stringToFeatureList(geoj, QgsFields(), None)
    # if there are features in the list
    if len(geojfeats) > 0:
        new_geom = geojfeats[0].geometry()
        with edit(new_layer):
            new_feat = QgsFeature(feature)
            new_feat.setGeometry(new_geom)
            new_layer.addFeature(new_feat)
            new_layer.updateExtents()
    else:
        print("No features found in the GeoJSON or no valid GeoJSON-String")

# add this brand new layer
QgsProject.instance().addMapLayer(new_layer)

Script for Processing-Tool:

The selection of the target-wkb-type in the processing tool is not an optimal implementation... if you know a better solution, feel free to edit it

from PyQt5.QtCore import QCoreApplication, QVariant
from qgis.core import (QgsJsonUtils, QgsField, QgsFeature, QgsProcessing, QgsExpression, QgsGeometry, QgsPoint, QgsFields, QgsWkbTypes,
                       QgsFeatureSink, QgsFeatureRequest, QgsProcessingAlgorithm,
                       QgsProcessingParameterFeatureSink, QgsProcessingParameterCrs, QgsProcessingParameterField, QgsProcessingParameterFeatureSource, QgsProcessingParameterEnum, QgsProcessingParameterString, QgsProcessingParameterNumber)

class GeometryLayerFromGeojsonStringField(QgsProcessingAlgorithm):
    SOURCE_LYR = 'SOURCE_LYR'
    GEOJSON_FIELD = 'GEOJSON_FIELD'
    #GEOMETRYTYPE_STRING = 'GEOMETRYTYPE_STRING'
    GEOMETRYTYPE_ENUM = 'GEOMETRYTYPE_ENUM'
    CRS = 'CRS'
    OUTPUT = 'OUTPUT'
    

    def initAlgorithm(self, config=None):  
        self.addParameter(
            QgsProcessingParameterFeatureSource(
                self.SOURCE_LYR, self.tr('Source'), [QgsProcessing.TypeMapLayer])) # Take any source layer, unfortunately no-geometry layers will not be available...
        self.addParameter(
            QgsProcessingParameterField(
                self.GEOJSON_FIELD, self.tr('Field containing the GeoJSON as string'),'GeoJSON','SOURCE_LYR', 1)) # Choose the field containing the GeoJSON as string
        #self.addParameter(
        #    QgsProcessingParameterNumber(
        #        self.GEOMETRYTYPE_STRING, self.tr('Geometry type of the target layer / of the GeoJSON content as number (lookup at https://qgis.org/pyqgis/3.0/core/Wkb/QgsWkbTypes.html)'),0,5)) # Unfortunately there is no WKB-Type-Input available...
        self.addParameter(
            QgsProcessingParameterEnum(
                self.GEOMETRYTYPE_ENUM, self.tr('Geometry type of the target layer / of the GeoJSON content'),
                ['Unknown','Point','LineString','Polygon','MultiPoint','MultiLineString','MultiPolygon','GeometryCollection','CircularString','CompoundCurve','CurvePolygon'],defaultValue=5)) # Only Works because these are ascending numerated in QGIS... NOT A GOOD SOLUTION!! But better than typing in a number by hand... see https://qgis.org/api/classQgsWkbTypes.html
        self.addParameter(
            QgsProcessingParameterCrs(
                self.CRS, self.tr('CRS of the target layer / of the GeoJSON content'),'EPSG:4326')) # CRS of the targetlayer
        self.addParameter(
            QgsProcessingParameterFeatureSink(
                self.OUTPUT, self.tr('new_geojson_layer'))) # Output

    def processAlgorithm(self, parameters, context, feedback):
        # Get Parameters and assign to variable to work with
        source_layer = self.parameterAsLayer(parameters, self.SOURCE_LYR, context)
        source_geojsonfield = self.parameterAsString(parameters, self.GEOJSON_FIELD, context)
        #wkbgeometrytype = self.parameterAsInt(parameters, self.GEOMETRYTYPE_STRING, context)
        wkbgeometrytype_fromenum = self.parameterAsInt(parameters, self.GEOMETRYTYPE_ENUM, context)
        wkbgeometrytype = wkbgeometrytype_fromenum # testing assignment        
        crsgeometry = self.parameterAsCrs(parameters, self.CRS, context)
        
        total = 100.0 / source_layer.featureCount() if source_layer.featureCount() else 0 # Initialize progress for progressbar
        
        source_fields = source_layer.fields() # get all fields of the sourcelayer
        
        (sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context, source_fields, wkbgeometrytype, crsgeometry)
                                               
        for current, feature in enumerate(source_layer.getFeatures()): # iterate over source 
            # geoj is the string object that contains the GeoJSON
            geoj = feature.attributes()[source_fields.indexFromName(source_geojsonfield)]
            # PyQGIS has a parser class for JSON and GeoJSON
            geojfeats = QgsJsonUtils.stringToFeatureList(geoj, QgsFields(), None)
            # if there are features in the list
            if len(geojfeats) > 0:
                new_geom = geojfeats[0].geometry()
                new_feat = QgsFeature(feature)
                new_feat.setGeometry(new_geom)
                sink.addFeature(new_feat, QgsFeatureSink.FastInsert) # add feature to the output
            
            if feedback.isCanceled(): # Cancel algorithm if button is pressed
                break
            
            feedback.setProgress(int(current * total)) # Set Progress in Progressbar

        return {self.OUTPUT: dest_id} # Return result of algorithm



    def tr(self, string):
        return QCoreApplication.translate('Processing', string)

    def createInstance(self):
        return GeometryLayerFromGeojsonStringField()

    def name(self):
        return 'GeometryLayerFromGeojsonStringField'

    def displayName(self):
        return self.tr('New Layer from GeoJSON String')

    def group(self):
        return self.tr('FROM GISSE')

    def groupId(self):
        return 'from_gisse'

    def shortHelpString(self):
        return self.tr('This Algorithm takes a source layer containing a GeoJSON as a String in a field and creates a copy of this layer with the geometry of this GeoJSON field')

Tags:

Csv

Geojson

Qgis