Iterators

How do you get a GIS feature from a Python collection/layer/provider/thingy? Let's look at 4 different popular GIS scripting environments.

ESRI (ArcGIS 9.3):

row = rows.next()
while row:
    # work with row
    ...
    row = rows.next()

FME:

self.my_collection_count = len(self.my_feature_collection)
for i in range(self.my_collection_count):
    self.my_feature_part = self.my_feature_collection[i]

    # work with self.my_feature_part
    ...

Note: don't use instance attributes like that. Use local variables.

QGIS:

feat = QgsFeature()
while provider.getNextFeature(feat):
    # work with feat
    ...

The oddball.

OGR (earlier than 1.5):

feature = layer.GetNextFeature()
while feature:
    # work with feature
    ...
    feature.Destroy()
    feature = layer.GetNextFeature()

Destroy!

4 different environments, 4 different ways, and none of them the natural Python way. There's one obviously right way to do it for Python, and that's the way that it's done in ogr.py versions >= 1.5, and how it's done in WorldMill. GeoDjango, too.

for f in layer:
    # work with f
    ...

where layer is among other things a generator that provides the iterator protocol just like Python strings, lists, and files do. It has a next method that yields a value, or raises a StopIteration exception when there are no more values. The advantages:

  • Clarity: it's agonizingly clear. More clear to a non-programmer, in my opinion, than the other alternatives. For each feature in the set: do something. And then forget about the feature and move on to the next.
  • Less error-prone: even a non-programmer can't screw up that one line of code any worse than to get a standard, understandable Python NameError, TypeError, or SyntaxError.
  • Standardization: core Python modules such as itertools and many other useful add-on packages reward you for using the iterator protocol.

Comments

Re: Iterators

Author: few people

I think ESRI has done away with the while: row = rows.next() stuff at 9.3.1: http://webhelp.esri.com/arcgisdesktop/9.3/index.cfm?TopicName=FeatureSets_and_RecordSets

Re: Iterators

Author: Mateusz Loskot

Sean, brilliant post! Finally, a voice clarity, correcness, robustness, discoverability, transparency, genericity, expressiveness, flexibility, consistency, meaning...

Quality Matters

Any of the interested parties are considering it in the same manner?

I'm afraid, that's the end of story.

Re: Iterators

Author: Michael Weisman

Just a quick clarification, FME does support iterators. The following snippet will iterate over the parts of an aggregate feature and extract the coordinates for each part:

for f in feature_collection:
  coords = f.getCoordinates()
  for m in coords:
    x = m[0]
    y = m[1]

Re: Iterators

Author: Mateusz Loskot

And a bit of clarification from me, I was referring to the Open Source projects mentioned above. Unfortunately, SWIG is in the house over there.

Re: Iterators

Author: Sean

Great, Michael. I simply didn't find examples of iterators employed by FME users. I haven't seen examples of programmers using Howard Butler's neater ogr.py API either.

QGIS

Author: Martin Dobias

Thanks for the nice article. Recently I've been thinking about adding a more pythonic way of accessing features in PyQGIS... currently it's just a crude wrapper around c++ api. So thanks for the inspiration :)

Re: Iterators

Author: Howard Butler

ogr.py is not mine. ogr.py is Frank's API ;), but I think I added support for this at GDAL 1.6. I guess we haven't advertised it so well.

for feature in layer:
    # COLUMN_NAME must be normalized to all caps due to some
    # data sources not caring.
    print feature.COLUMN_NAME