Toward a Better Python Feature API

Previously, I asserted that the Python Cartographic Library feature API was superior to anything generated trivially from C++ code (even excellent C++) by SWIG. Of course, even PCL's API can be improved. I've been inspired by Django's database abstraction API to experiment with something even easier to use. Friday night I hacked on PCL's GeoRSS module, and tied up loose ends this afternoon. See branches/PCL-newfeatureapi/PCL-GeoRSS.

Feature sources are absent from the new API. A feature type class has a query manager attribute, features, and methods of this object provide iterators over features. For example, let's find items of a GeoRSS in a region of interest using a bounding box filter:

>>> url = 'http://pleiades.stoa.org/places/settlement.atom'
>>> store = GeoRSSFeatureStore(url)
>>> Entry = store.featuretype('entry')
>>> Entry.features.count
230

# Filter for intersection with a
# (29dE, 36dN, 30dE, 37dN) bounding box

>>> features = [f for f in Entry.features.filter(
...                        bbox=(29.0, 36.0, 30.0, 37.0)
...                        )
...            ]
>>> len(features)
62

# Inspect the first feature

>>> f = features[0]
>>> f.id
'http://pleiades.stoa.org/places/638749'
>>> f.properties.title
u'Antiphellos/Habesos'
>>> f.properties.the_geom.toWKT()
'POINT (29.6370000000000005 36.1931999999999974)'
>>> type(f.context)
<class 'feedparser.FeedParserDict'>

A GeoRSS feature's context is a reference to that item's parsed data structure. Everything feedparser can glean about the item (and that's nearly everything) is thereby available to a programmer.

Here's an example, using a better feed, of using a Python filter expression to obtain an iterator over only certain items tagged "web":

>>> url = 'http://sgillies.net/blog/feeds/entries/'
>>> store = GeoRSSFeatureStore(url)
>>> Entry = store.featuretype('entry')
>>> Entry.features.count
31

>>> features = [f for f in Entry.features.filter(
...                        properties="'web' in f.tags"
...                        )
...            ]
>>> len(features)
12

>>> f = features[0]
>>> f.id
'http://sgillies.net/blog/entries/412'
>>> f.properties.title
u'GeoRSS and Validation'
>>> f.properties.tags
[u'web']
>>> f.properties.the_geom.toWKT()
'POINT (-105.0958300000000065 40.5869900000000001)'

# More detail about the tags, via the feature context

>>> tag = f.context.tags[0]
>>> tag.term
u'web'
>>> tag.scheme
u'http://sgillies.net/blog/categories/'
>>> tag.label
u'Web'

That's dirt simple. Following the Django lead, creating and saving new features ought to be as straightforward as:

>>> new_georss_entry = Entry(title=u'GeoRSS Everywhere', ...)
>>> new_georss_entry.save()
>>> Entries.features.count
32