Loading an object class into a list using an arc: Strange SearchCursor behavior

Using arcpy , my goal is to put a class of objects in a list for further processing. Each line will be a tick {'field name': value} , including geometry.

The most pythonic way to achieve this is to use list comprehension:

 fc = '/path/to/fc' fields = [f.name for f in arcpy.ListFields(fc)] # get field list features = [[row.getValue(f) for f in fields] for row in arcpy.SearchCursor(fc)] 

This method works for data, but the geometry in the list is the same (the last geometry is obtained in fc). stack overflow .

I tried a different approach:

 fc = '/path/to/fc' shape_field = arcpy.Describe(fc).shapeFieldName # load geometry in a list geom = arcpy.Geometry() feat = [{shape_field: f} for f in arcpy.CopyFeatures_management(fc, geom)] # slow # load data in a list fields = [f.name for f in arcpy.ListFields(fc)] data = [dict([(f, row.getValue(f)) for f in fields if f != shape_field]) for row in arcpy.SearchCursor(fc)] # slow # merge merge = zip(feat, data) merge = [dict([(k, v) for adict in line for (k, v) in adict.items()]) for line in merge] # sorry for that... 

It works with my dataset, but:

  • It is slow.
  • I am not sure that it is safe to claim that data and feat go in the same order.

Any opinion on this?

+4
source share
1 answer

If possible, move on to using 10.1, where you get arcpy.da , a significantly more efficient API for cursors. I wrote an entry in this thread about returning dictionaries . The geometry will be the same because it uses an internal recirculation pointer, so at 10.0 you will want to take shape.__geo_interface__ instead and use AsShape to return it to the geometry object.

The order in which you get the lines back is pretty arbitrary: you can expect it to be the same every time in the shapefile without a where clause, and that is pretty much why your two-pass approach won't really be reliable.

All of this is covered, you can do something like this:

 def cursor_to_dicts(cursor, field_names): for row in cursor: row_dict = {} for field in field_names: val = row.getValue(field) row_dict[field] = getattr(val, '__geo_interface__', val) yield row_dict fc = '/path/to/fc' fields = [f.name for f in arcpy.ListFields(fc)] # get field list features = list(cursor_to_dicts(arcpy.SearchCursor(fc), fields)) 

Magic - getattr() call - try to capture value.__geo_interface__ , if it exists, otherwise just default will be value .

Since this question concerns not only the Python language as a whole, but also the GIS-specific API ( arcpy ), you might be better off setting these things in gis.stackexchange in the future.

+5
source

All Articles