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.

CoreyJames
  • 63
  • 1
  • 7
  • How was input defined? This needs more context. – Mike T Oct 19 '13 at 04: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

1 Answers1

8

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
  • 54,868
  • 3
  • 110
  • 187
  • Gene, I like this, but your example (and Tom's) is a little out of date. See the stuff in http://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