4

I'm fairly new to the world of python and GIS - so this is definitely a beginner question.

I am following Tom Macwrights small guide to Shapely and Fiona, and I noticed that even when opening the previously created shapefile, it doesn't contain the schema I defined using:

schema = { 
      'geometry': 'Point', 
      'properties': { 'name': 'str' } }

This is evident because when I try to call it using schema = input.schema.copy(), it spits out AttributeError: 'function' object has no attribute 'schema'

Reading the Fiona documentation, it states that:

the schema of its record type (a vector file has a single type of record, remember) is accessed via a read-only schema attribute.

Does that mean that it is not possible to store a schema in the shapefile,a nd therefore I'm always required set up the variables in the beginning, rather than reading from the file using input.schema.copy()? Or do I setup the schema using Records?

If so, how do you read from a csv file (as in Tom's example) and place the data into the records to be read from input.schema.copy()? is it helpful to do this, or am I just complicating things and should define the schema every time i open the shapefile?

Thanks for the help - I'm really interested in developing my understanding of the processes occurring.

  • How was input defined? This needs more context. – Mike T Oct 19 '13 at 4:03
  • Like Mike, I think we need to see how a value is getting assigned to your input variable. An open Fiona collection should always have a schema attribute and this is well tested. – sgillies Oct 19 '13 at 17:11
6

I don't really understand your problem. Some explanations: for example, we want to

  1. create a new shapefile
  2. modify the original schema: for that, it's easier to just copy things to a new shapefile and make the changes as you copy

    • Create a new Polyline shapefile:
import fiona
# schema: it is a simple dictionary with geometry and properties as keys
schema = {'geometry': 'LineString','properties': {'test': 'int'}}
# for defining the geometry, you need Shapely
from shapely.geometry import LineString, mapping
# two simples geometries
lines = [LineString([(272830.63,155125.73),(273770.32,155467.75)]),LineString([(273536.47,155914.07),(272033.12,152265.71)])]
with fiona.open('myshp.shp', 'w', 'ESRI Shapefile', schema) as layer:
    for line in lines:
        # filling schema
        elem = {}
        # geometry with mapping function of shapely
        elem['geometry'] = mapping(line) 
        # attribute value (the same here)
        elem['properties'] = {'test': 145}
        # writing element in the file
        layer.write(elem)
  • Now we want to modify the schema of the original shapefile in a new shapefile:

1) Open the original shapefile:

shapefile =fiona.open('myshp.shp')
#read the schema
schema2 = shapefile.schema
print schema2
{'geometry': 'LineString', 'properties': OrderedDict([(u'test', 'int:10')])}

2) As it is a dictionary, it is easy to add new fields/keys in the properties:

schema2['properties']['string']='str'

3) Now we create a new shapefile copying myshp.shp with the new schema :

with fiona.open('myshp.shp', 'r') as input:
    schema = schema2
    # writing the new shapefile
    with fiona.open('myshp_copy.shp', 'w', 'ESRI Shapefile', schema) as output:
        for elem in input:
            # add the new attribute value
            elem['properties']['string']="hello"
            output.write({'properties': elem['properties'],'geometry': mapping(shape(elem['geometry']))})
  • verification
c = fiona.open('myshp_copy.shp')
c.schema
{'geometry': 'LineString', 'properties': OrderedDict([(u'test', 'int:10'), (u'string', 'str')])}
# first element of the shapefile
c.next()
{'geometry': {'type': 'LineString', 'coordinates': [(272830.63, 155125.73000000001), (273770.32000000001, 155467.75)]}, 'type': 'Feature', 'id': '0', 'properties': OrderedDict([(u'test', 145), (u'string', u'hello')])}
  • Conclusion

If you don't want to modify the shapefile, it is easier with schema.copy() that is only used to get a copy of the original schema (no definition here)

with fiona.open('myshp.shp', 'r') as input:
    schema = input.schema.copy()
     with fiona.open('myshp_copy2.shp', 'w', 'ESRI Shapefile', schema) as output:
         for elem in input:
             output.write({'properties': elem['properties'],'geometry': mapping(shape(elem['geometry']))})
  • Gene, I like this, but your example (and Tom's) is a little out of date. See the stuff in toblerity.org/fiona/manual.html#writing-new-files about ordering properties within a schema. – sgillies Oct 19 '13 at 17:12
  • Thanks for your detailed response, Gene. It wasn't a problem so much as trying to understand schema. Making a new shapefile, I defined 'schema = {'geometry': 'LineString','properties': {'test': 'int'}}', yet it seemed to be only temporary, because when I reopen the file and try to read the schema with '=input.schema.copy()' it spits back an error. I can see now that to permanently write the schema, I should use the method both you and sgillies provided. So, I guess that 'schema = {'geometry': 'LineString','properties': {'test': 'int'}}' is used when you only temporarily need the schema? – coreyjames Oct 19 '13 at 23:58

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.