Geographic Information Systems Asked by staf on May 18, 2021
I ambuilding a python plugin for QGIS. As part of the plugin, I do:
iface.addVectorLayer(geoj, self.selectedLayer, 'ogr')
This is basically adding a layer to the project, with
geoj: a geojson string,
self.selectedLayer: the name I want to give it, and
‘ogr’: the driver.
I get a layer in my project with most field lengths set to zero:
This is a problem, because when I then try to export the loaded layer as a shapefile or something, I get the following error:
*Export to vector file failed.
Error: Feature write errors:
Error converting value ((1:0)) for attribute field photos_captions: String of length 5 exceeds maximum field length (1)
*
And a complete QGIS Crash!
Do I need to specify some more parameters to be able to load the geojson in my project as a valid layer with correctly defined fields?
Here are the relevant sections of code:
def getGeoJsonFromSelectedLayer(self):
if self.selectedLayer:
url = "https://api.fulcrumapp.com/api/v2/query"
tableSelector = f"SELECT * FROM "{self.selectedLayer}""
querystring = {"q": tableSelector, "format": "geojson", "headers": "false",
"metadata": "false", "arrays": "false", "page": "1", "per_page": "20000"}
headers = {
"Accept": "application/json",
"X-ApiToken": self.API_TOKEN
}
response = requests.request(
"GET", url, headers=headers, params=querystring)
# For Debugging:
iface.messageBar().pushMessage(response.text)
self.createLayerFromGeojson(response.text)
else:
iface.messageBar().pushMessage("No Layer Selected")
def createLayerFromGeojson(self, geoj):
# if there are features in the list
if len(geoj) > 0:
# add the layer to the list of layers
iface.addVectorLayer(geoj, self.selectedLayer, 'ogr')
else:
iface.messageBar().pushMessage("No features found in the geoJSON")
To answer your question and to add some code to my comment :
I writed a snippet of code based on this answer :
import re
geoj_layer = QgsVectorLayer(geoj, '', 'ogr')
refactor = []
field_error = []
for field in geoj_layer.fields(): # creation of a list with the field and field type of the layer.
refactor_field = {}
refactor_field['expression']=str(""")+field.name()+str(""")
refactor_field['name']=field.name()
refactor_field['precision']=field.precision()
if field.typeName() == "IntegerList": # if the field as a not well handled type
field_error.append(field.name()[:10])
refactor_field['length']=0
refactor_field['type']=10
else :
refactor_field['type']=field.type()
refactor.append(refactor_field)
output_path = path + 'new_layer.shp'
# I'm using refactorfield algorithm from Qgis to create a new layer with some modification on specified fields.
processing.run("qgis:refactorfields", {'INPUT':geoj,'FIELDS_MAPPING':refactor,'OUTPUT':output_path})
iface.addVectorLayer(output_path,'', 'ogr')
# Modification of the value for a better value
geoj_layer = QgsVectorLayer(output_path, '', 'ogr')
for field_name in field_error:
for f in geoj_layer.getFeatures() :
attrs= f.attributes()
value = attrs[geoj_layer.fields().indexFromName(field_name)]
string = re.split(',|:|)', value)
new_value = ''
for elem in string[1:-1] :
if elem == string[1:-1][-1] :
new_value = new_value +str(elem)
else :
new_value = new_value +str(elem) + ", "
geoj_layer.startEditing()
geoj_layer.changeAttributeValue(f.id(), geoj_layer.fields().indexFromName(field_name), new_value )
geoj_layer.commitChanges()
geoj_layer.triggerRepaint()
This code will create a new shapefile with a field "photos_cap"
as a String
. Shapefile only accept 10 characters for field name.You'll get a value of 0 instead of (1:0).
Edit after the author's comment
You can still use refactorfield
on a geojson and create another geojson with the column and value you want.
Example :
This is my geojson with the IntegerList :
I can obtain this kind of result in a temporary layer :
Instead of calling the geojson from your api with iface.addVectorLayer
you can call it with QgsVectorLayer
so it doesn't appear in the canvas. You can then use refactorfield
to change the fields' type of your layer and save the result in a temporary layer (I use tempfile
to save the layer in a temporary directory).
With this code, only one layer appear in the canvas, a temporary layer without IntegerList :
import re, tempfile
tf = tempfile.TemporaryDirectory()
def format_to_string(value):
if isinstance(value, str) :
string = re.split(',|:|)', value)
new_value = ''
for elem in string[1:-1] :
if elem == string[1:-1][-1] :
new_value = new_value +str(elem)
else :
new_value = new_value +str(elem) + ", "
if isinstance(value, list) :
string = ""
for elem in value:
string += elem
if elem != value[-1] :
string = string + ', '
new_value = string
return (new_value)
path = tf.name + "/"
geoj = "/home/Documents/geojson/champ_listint.geojson"
geoj_layer = QgsVectorLayer(geoj, '', 'ogr')
refactor = []
field_error = []
for field in geoj_layer.fields(): # creation of a list with the field and field type of the layer.
refactor_field = {}
refactor_field['expression']=str(""")+field.name()+str(""")
refactor_field['name']=field.name()
refactor_field['precision']=field.precision()
if field.typeName() == "IntegerList": # if the field as a not well handled type
field_error.append(field.name())
refactor_field['length']=0
refactor_field['type']=10
elif field.typeName() == "StringList": # if the field as a not well handled type
field_error.append(field.name())
refactor_field['length']=0
refactor_field['type']=10
else :
refactor_field['type']=field.type()
refactor.append(refactor_field)
output_path = path + 'new_layer.geojson'
# I'm using refactorfield algorithm from Qgis to create a new layer with some modification on specified fields.
processing.run("qgis:refactorfields", {'INPUT':geoj,'FIELDS_MAPPING':refactor,'OUTPUT':output_path})
iface.addVectorLayer(output_path,'', 'ogr')
# Modification of the value for a better value
geoj_layer_2 = QgsVectorLayer(output_path, '', 'ogr')
for field_name in field_error:
for f in geoj_layer.getFeatures() :
attrs= f.attributes()
value = attrs[geoj_layer.fields().indexFromName(field_name)]
new_value = format_to_string(value)
geoj_layer_2.startEditing()
geoj_layer_2.changeAttributeValue(f.id(), geoj_layer.fields().indexFromName(field_name), new_value )
geoj_layer_2.commitChanges()
geoj_layer_2.triggerRepaint()
Correct answer by JULESG on May 18, 2021
Get help from others!
Recent Answers
Recent Questions
© 2024 TransWikia.com. All rights reserved. Sites we Love: PCI Database, UKBizDB, Menu Kuliner, Sharing RPP