2

I'm getting the following error when trying to add a property to a .shp file using fiona in python: Traceback (most recent call last):

  File "<ipython-input-38-5f04c3b56601>", line 17, in <module>
    j=j+1

  File "/Users/alex/anaconda/lib/python2.7/site-packages/fiona/collection.py", line 402, in __exit__
    self.close()

  File "/Users/alex/anaconda/lib/python2.7/site-packages/fiona/collection.py", line 386, in close
    self.flush()

  File "/Users/alex/anaconda/lib/python2.7/site-packages/fiona/collection.py", line 376, in flush
    self.session.sync(self)

  File "fiona/ogrext.pyx", line 939, in fiona.ogrext.WritingSession.sync (fiona/ogrext.c:15649)

RuntimeError: Failed to sync to disk

Here is my code - I'm adding a dummy property 'jazz' for now but I have many properties with both float and string types that I want to add once I figure out how to get it to work...

import random
#
import fiona

with fiona.open("data/subplace_maps/SP_SA_2011.shp") as input:
    schema2 = input.schema.copy()
    schema2['properties']['jazz'.decode('utf-8')] = 'str:50'
    temp = input.crs
    with fiona.open('data/subplace_maps/output.shp', 'w', crs=input.crs, driver=input.driver, schema=schema2) as sink:
        # iterate over shapefile shapes
        j = 0
        for elem in input:
            # update progress
            if(j % 500 == 0):
                print j,'/',len(input)
            #
            ja = ('jazz' + str(random.random())).decode('utf-8')
            elem['properties'].update(jazz=ja)
            #
            sink.write(elem)
            j=j+1

print 'done'
3

Fiona works with Python dictionaries (schema, features) and and more specifically Ordered Dictionaries

shape = fiona.open("a_shapefile.shp")
# schema of the shapefile
shape.schema
{'geometry': 'Point', 'properties': OrderedDict([(u'id', 'str:10')])}
# keys of the dictionary
shape.schema.keys()
['geometry', 'properties']
# first feature of the shapefile
elem = shape.next()
# keys of the dictionary
elem.keys()
['geometry', 'type', 'id', 'properties']
#  properties of the first feature
elem['properties']
OrderedDict([(u'id', u'1')])
#keys of the dictionary
elem['properties'].keys()
[u'id']
# value of the field/key 'id'
elem['properties']['id']
u'1'

When you modify the schema of your shapefile you simply add a new key:value to the dictionary

 schema2 = input.schema.copy() 
 # or
 schema2 = input.schema
 # add a new field to the dictionary (key:value)
 schema2['properties']['jazz'] = 'str:50' 
 print schema2
 {'geometry': 'Point', 'properties': OrderedDict([(u'id', 'int:10'), ...., (u'jazz', 'str:50')])}

And when you want to add/modify a value in the jazz field you simply add/modify the value of the key [jazz] (= elem['properties']['jazz'])

 with fiona.open('output.shp', 'w', crs=input.crs, driver=input.driver, schema=schema2) as sink:
    for elem in input:
        # elem is a dictionary with keys
        # original shapefile
        print "original: ", elem['properties']
        # now add the new value to the dictionary
        ja = ('jazz' + str(random.random()))
        elem['properties']['jazz'] = ja 
        print "modified: ", elem['properties']
        sink.write(elem)

original:  OrderedDict([(u'id', None)])
modified:  OrderedDict([(u'id', None),('jazz', 'jazz0.551109926166')])
....

Control (all is dictionaries)

test = fiona.open('output.shp')
elem = test.next()
elem
{'geometry': {'type': 'Point', 'coordinates': (0.21784342830509873, 0.37825773080975944)}, 'type': 'Feature', 'id': '0', 'properties': OrderedDict([(u'id', u'1'), (u'jazz', u'jazz0.551109926166')])}
elem['geometry']
{'type': 'Point', 'coordinates': (0.21784342830509873, 0.37825773080975944)}
elem['geometry']['coordinates']
(0.21784342830509873, 0.37825773080975944)
elem['properties']
OrderedDict([(u'id', u'1'), (u'jazz', u'jazz0.551109926166')])
elem['properties']['jazz']
u'jazz0.551109926166'
0

As far as I know, the Fiona package in the default Anaconda channel is broken. Is that the one you're using? If so, can you try the "conda-forge" channel instead?

  • hmm it's worked for extracting features out of shapefiles, just exporting not working... I ended up doing some ninja tactics by directly altering a topojson version of the shp file by parsing the json object and adding the features i need - see below :) – Alex Conway May 2 '16 at 14:23
0

I couldn't get it to work in time for my deadline so ended up converting the .shp file to topojson and adding the features directly to the json (the final output I needed was topojson for d3 anyway...)

see below:

import json
import csv
from collections import OrderedDict
import io

# read proeprties to be added to topojson
csvdata = csv.DictReader(open('data/addtoshapes.csv', 'rb'))
csvdict = OrderedDict()
for line in csvdata:
    spcode = line['spcode']
    del line['spcode']
    csvdict[spcode]=line

# iterate over existing topojson file and add properties from csvdata    
# map geometry id -> sp_code then lookup sp_code in csv and write to property
temp = OrderedDict()
with open("data/SP_SA_2011.json") as json_file:
    json_data = json.load(json_file)

    #iterate over jsondata and add properties by looking up spcode from csvdict
    for i in range(0,len(json_data['objects']['SP_SA_2011']['geometries']) -1):
        if(i % 500 == 0):
            print i,'/',len(json_data['objects']['SP_SA_2011']['geometries'])
        # need to set json_data['objects']['SP_SA_2011']['geometries'][i]['properties'] = tempdict at end...
        tempdict = json_data['objects']['SP_SA_2011']['geometries'][i]['properties']
        del tempdict['ALBERS_ARE']
        del tempdict['DC_MDB_C']
        del tempdict['DC_MN_C']
        del tempdict['DC_MN_C_st']
        del tempdict['MN_CODE']
        del tempdict['MN_CODE_st']
        del tempdict['MN_MDB_C']
        del tempdict['MP_CODE']
        del tempdict['PR_CODE']
        del tempdict['PR_MDB_C']
        del tempdict['SP_CODE_st']
        # look up properties to add
        spcode = tempdict['SP_CODE']
        # add new properties to tempdict
        tempdict.update(csvdict[str(spcode)])
        #
        tempdict2={}
        for key in tempdict:
            newkey = key.decode('utf-8', 'ignore')
            tempdict2[newkey] = tempdict[key]
            if(type(tempdict[key]) is str):
                tempdict2[newkey] = tempdict[key].decode('utf-8', 'ignore')

        # write new properties to json_data
        json_data['objects']['SP_SA_2011']['geometries'][i]['properties'] = tempdict

    # write new topojson to file
    with io.open('data/sp_finaltopo.json', 'w', encoding='utf-8') as f:
        data = json.dumps(json_data, encoding='latin1')
        f.write(unicode(data))

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.