Why is fiona not writing these records

fionapython

I'm running through a UTM grid in geojson format with fiona, attempting to add/populate a field based on other field values and then write out to a new file. It appears to successfully build the record but not write it out. An input record looks like this:

{
      "type" : "Feature", 
      "id" : 1201, 
      "geometry" : 
      {
        "type" : "Polygon", 
        "coordinates" : 
        [
          [
            [-47.9999998938292, -63.9999999562278], 
            [-47.9999998938292, -64.9999998998828],... 
          ]
        ]
      }, 
      "properties" : {
        "FID" : 1201, 
        "ZONE" : 22, 
        "ROW_" : "D", 
        "WEST_VALUE" : " 54W", 
        "CM_VALUE" : " 51W", 
        "EAST_VALUE" : " 48W"
      }
    }

Here is my code:

from collections import OrderedDict
import fiona


gj_file = "/home/me/Downloads/utmzones.geojson"
recs = []  # list of new records that will be written

with fiona.open(gj_file) as source:
    source_driver = source.driver
    source_crs = source.crs

    for f in source:
        props = f['properties']
        geom = f['geometry']
        if props['ROW_'] in ['N', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X']:
            r = {
                'type': 'Feature',
                'id': f['id'],
                'geometry': geom,
                'properties': OrderedDict([
                    ('FID', props['FID']),
                    ('ZONE', props['ZONE']),
                    ('ROW_', props['ROW_']),
                    ('UTMZONE', str(props['ZONE']) + 'N'),  # make a ZONE[N/S] value
                    ('WEST_VALUE', props['WEST_VALUE']),
                    ('CM_VALUE', props['CM_VALUE']),
                    ('EAST_VALUE', props['EAST_VALUE'])
                ])
            }
            print(r)
            recs.append(r)
        elif props['ROW_'] in ['M', 'L', 'K', 'J', 'H', 'G', 'F', 'E', 'D', 'C']:
            r = {
                'type': 'Feature',
                'id': f['id'],
                'geometry': geom,
                'properties': OrderedDict([
                    ('FID', props['FID']),
                    ('ZONE', props['ZONE']),
                    ('ROW_', props['ROW_']),
                    ('UTMZONE', str(props['ZONE']) + 'N'),
                    ('WEST_VALUE', props['WEST_VALUE']),
                    ('CM_VALUE', props['CM_VALUE']),
                    ('EAST_VALUE', props['EAST_VALUE'])
                ])
            }
            recs.append(r)

The record generated looks like this:

{
  'type': 'Feature',
  'id': '1201',
  'geometry': {
    'type': 'Polygon',
    'coordinates': [
      [
        (-47.9999998938292, -63.9999999562278),
        (-47.9999998938292, -64.9999998998828),...
      ]
    ]
  },
  'properties': OrderedDict([
    ('FID', 1201),
    ('ZONE', 22),
    ('ROW_', 'D'),
    ('UTMZONE', '22N'),
    ('WEST_VALUE', ' 54W'),
    ('CM_VALUE', ' 51W'),
    ('EAST_VALUE', ' 48W')
  ])
}

My schema looks like this:

schema = {
    'geometry': 'Polygon',
    'properties': OrderedDict([
        ('FID', int),
        ('ZONE', int),
        ('ROW_', str),
        ('UTMZONE', str),
        ('WEST_VALUE', str),
        ('CM_VALUE', str),
        ('EAST_VALUE', str)
    ])
}

And the write action looks like this, based on my (probably incorrect) interpretation of the fiona docs:

with fiona.open(
    '/home/me/Downloads/utm_out.geojson',
    'w',
    driver=source_driver,
    crs=source_crs,
    schema=schema) as c:
    
    for rec in recs:
        c.write(rec) 

This raises an AttributeError: type object 'int' has no attribute 'split'. When I coerce the two values FID and ZONE to str and update the schema, it then raises a TypeError: argument of type 'type' is not iterable.

UPDATE: entire stack trace as requested

Traceback (most recent call last):
  File "/home/me/PycharmProjects/UTM/join_utm_zones.py", line 63, in <module>
    with fiona.open(
  File "/home/me/anaconda3/envs/foss4g/lib/python3.9/site-packages/fiona/env.py", line 417, in wrapper
    return f(*args, **kwargs)
  File "/home/me/anaconda3/envs/foss4g/lib/python3.9/site-packages/fiona/__init__.py", line 272, in open
    c = Collection(path, mode, crs=crs, driver=driver, schema=this_schema,
  File "/home/me/anaconda3/envs/foss4g/lib/python3.9/site-packages/fiona/collection.py", line 151, in __init__
    self._check_schema_driver_support()
  File "/home/me/anaconda3/envs/foss4g/lib/python3.9/site-packages/fiona/collection.py", line 424, in _check_schema_driver_support
    field_type = field.split(":")[0]
AttributeError: type object 'int' has no attribute 'split'

Best Answer

Your schema is defined incorrectly. The types need to be defined using strings. You are trying to pass the actual Python object types to the functions, which is causing issues.

Your first error is caused because the library's write utility is trying to call the .split() function on the object int; it can't because int does have have .split() method. The second error is different because a str object does have .split() method, but then it trips when, presumably, the write utility tries to iterate over a Python type object; a Python type has the type of 'type', hence the weird error.

TypeError: argument of type 'type' is not iterable

Instead try defining your schema as:

schema = {
    'geometry': 'Polygon',
    'properties': OrderedDict([
        ('FID', 'int'),
        ('ZONE', 'int'),
        ('ROW_', 'str'),
        ('UTMZONE', 'str'),
        ('WEST_VALUE', 'str'),
        ('CM_VALUE', 'str'),
        ('EAST_VALUE', 'str')
    ])
}

See this section of the documents.

Related Question