I'm trying to streamline the process whereby we build tiles (of ~30 layers, representing planning zone and zone-overlay boundaries). I have a strong preference for FOSS tools.

A couple of our people - and a couple of key clients - are only really comfortable in MapInfo, but I'm comfortable converting across platforms (using OGR) because I prefer PostGIS/QGIS/Python. However the source files are always (or nearly always) MapInfo TAB.

Many of the TAB files are 'thematic'; the theme style results either from the values taken by one aspatial field (e.g., 'LGA'), or from the interaction of 2 aspatial fields (e.g., 'Zone' and 'Status'). The thematically-different combinations ought to be different layers (or better yet, different files) but they ain't: I have no control over that, otherwise it would change and this question would be moot.

So I'm trying to separate out these layers, style them (that's easy) and then chop them into a maptile pyramid (super-easy). The eventual aim is to build XML files so that (python + mapnik) can style each layer, then chew it into a tile pyramid.

I'ma do it in Python with mapnik and ogr.


ds = mapnik.Ogr(base=[dir], file =[tabfile], layer_by_index=0)` 

or just

ds = ogr.Open('[tabfile]')

brings a TAB file into a Python script without any problem - analogous entries in the <Datasource> tag in an XML file would do likewise.

In the mapnik version, ds.fields() shows me the right headers, and I can add rule filters to extract specific subsets of the data to style as I wish, e.g.,

r.filter = mapnik.Expression("([Zone]='IN2Z') and ([Status]='n')")
m.append_style('IND_01', s)

What I would like to do, is to loop through distinct values of, say, Zone and Status, and filter a new dataset (and apply a new style) at each step.

If there was a way to return a vector of the unique values of the relevant field, I could simply iterate through the vector and do my dastardly work: for example, if I have a TAB file BH.TAB with column 'LGA', at the command-line I can do

ogrinfo -sql "select distinct LGA from BH" BH.TAB

This (piped to a file, say) can be parsed to fetch the vector of unique LGA in BH.TAB.

So now - finally - the question.

Does anyone know ogr's Python bindings well enough to know how to emulate the ogrinfo command-line syntax shown above? Once I can get that sucker into the mix I will find a way to parse it.

I swear I have RTFD (e.g., the Python/OGR Cookbook, which is pretty comprehensive), to no avail.


I should point out that I have already tried stuff like

v = subprocess.check_output("ogrinfo -sql \"select distinct LGA from BH\" BH.TAB")
w = v.split("\r\n")

The output of that is a PITA to parse.


OK, so it turns out that R'ing TFD yields the right way to do this, eventually - so long as you cast a wide net in your search for TFD.

first: use ogr to open, not mapnik. (ogr has been import-ed at the start of the file)...

l = []                   # initialise vector of LGA codes
ds = ogr.Open('BH.TAB')

Then take advantage of the fact (mentioned here (PDF)) that ogr datasources have an "ExecuteSQL" method; mapnik might not (as far as I have been able to determine).

Once exposed to SQL the problem doesn't stand a chance.

r = ds.ExecuteSQL("select distinct LGA from BH")
f = r.GetNextFeature
while f:
    f = r.GetNextFeature()


One thing that seems strange, though: if your data has lazy capitalisation (e.g., LGA = 'Trn' and LGA = 'trn', when all LGA codes are supposed to be upper-case), you can't amend the ExecuteSQL() to select distinct upper(LGA) from BH... it won't work, for some reason (and nor will select distinct ucase(LGA) from BH ). Any capitalisation-correction has to be done after the ExecuteSQL(): not a big deal, but odd nonetheless.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.