Using Python to parse config files

Alot of tools out there have some sort of configuration which, at run time, is read and used in the process accordingly.  When writing tools, my config file format has always been something like:

title: My Tool
# commented out line

description: This is my tool.  # another comment

Since I’m using Python for much of my scripting these days, I decided to write a small parser to handle this type of config.  So here’s what I’ve come up with:

import fileinput, re

def parse(file=None, delim=':'):
    '''
        Parses a config file formatted like:
        foo: bar
        # comments: out line
        - comments allowed (#)
        - empty lines allowed
        - spaces allowed

    '''

    d = {}

    if file is None:
        return -1

    for line in fileinput.input(file):
        if not line.strip(): # skip empty or space padded lines
            continue
        if re.compile('^#').search(line) is not None: # skip commented lines
            continue
        else: # pick up key and value pairs
            kvp = line.strip().split(delim)
            if kvp[1].strip().split('#') is not None:
                d[kvp[0].strip()] = kvp[1].split('#')[0].strip()
            else:
                d[kvp[0].strip()] = kvp[1].strip()
    return d

Seems to work well so far.  I wonder if there’s a config file standard out there?

Written from home:

MapServer Disaster: you have got to be kidding me

http://n2.nabble.com/FW%3A-MapServer-enhancements-refactoring-project-td2571268.html

I’m beyond words at this point.

Written from home:

fun with Shapelib

We have some existing C modules which do a bunch of data processing, and wanted the ability to spit out shapefiles on demand.  Shapelib is a C library which allows for reading and writing shapefiles and dbf files.  Thanks to the API docs, here’s a pared down version of how to write a new point shapefile (with, in this case, one record):

#include <stdio.h>
#include <stdlib.h>
#include <libshp/shapefil.h>
/*
 build with: gcc -O -Wall -ansi -pedantic -g -L/usr/local/lib -lshp foo.c
*/
int main() {
    int i = 0;
    double *x;
    double *y;

    SHPHandle  hSHP;
    SHPObject *oSHP;
    DBFHandle  hDBF;

    x = malloc(sizeof(*x));
    y = malloc(sizeof(*y));

    /* create shapefile and dbf */
    hSHP = SHPCreate("bar", SHPT_POINT);
    hDBF = DBFCreate("bar");

    DBFAddField(hDBF, "stationid", FTString, 25, 0);

    /* add record */
    x[0] = -75;
    y[0] = 45;
    oSHP = SHPCreateSimpleObject(SHPT_POINT, 1, x, y, NULL);
    SHPWriteObject(hSHP, -1, oSHP);
    DBFWriteStringAttribute(hDBF, 0, 0, "abcdef");

    /* destroy */
    SHPDestroyObject(oSHP);

    /* close shapefile and dbf */
    SHPClose(hSHP);
    DBFClose(hDBF);
    free(x);
    free(y);

    return 0;
}

Done!

Written from home:

Less Than 4 Hours

A benefit of open source.

< 4 hours.  That’s how long it took to address a MapServer bug in WMS 1.3.0.  Having been on the other side of these many times, it’s gratifying to bang out quick fixes as well.

Committing often :)

Written from home:

MapServer Code Sprint Progress

MapServer action from the Toronto Code Sprint 2009:

Paul has full details on his blog (day 1, day 2, day 3, day 4, post-mortem).  More details from Chris (day 1, day 2, day 3, day 4).  Also check out some pictures from the event.

Personally, I was happy to bang out fixes for:

  • optionally disabling SLD for WMS (#1395)
  • support for resultType=hits for WFS (#2907)
  • working code for WFS spatial filters against the new GEOS thread safe C API (#2929)
  • WFS 1.1.0 supporting OWS Common 1.0.0 instead of 1.1.0 (#2925)
  • The beginnings of support for correct axis ordering for WFS 1.1.0 (#2899)

Good times!

UPDATE 12 March 2009: here’s a Camptocamp report of the event.

Written from home:

TO Code Sprint is upon us

The code sprint starts Saturday, and there’s a good turnout of folks from the various OSGeo projects.

If you’d like to participate, you can join us on IRC at #tosprint and be there in spirit.

Written from home:

MapServer 5.4.0-beta1 is out

Check it out.  A few RFCs addressed, among them OGC WMS 1.3.0 server support.

Written from home:

WMS 1.3.0 now in MapServer trunk

Fresh in svn trunk, MapServer now has WMS 1.3.0 Server support and will be part of the forthcoming 5.4 release.

It will interesting to see the use WMS 1.3.0 gets, given the significant changes from 1.1.1.

Great work Assefa!

Written from home:

OWS Metadata Matters

This has seemingly been the theme for me in the last few weeks.  From publishing to discovery, lack of metadata in OWS endpoints results in increased metadata management away from source, as well as crappy search results.

So here’s some friendly advice:

Service Metadata

  • fill out title, abstract (representative of the OWS as a whole) with descriptive metadata
  • fill out keywords to categorize the service.  If possible, use a known thesaurus, or one specific to your organization.  Don’t use keywords like “OGC”; we already know it’s an OGC service from the get-go by interacting with it
  • fill out contact information.  OWS Common defines ServiceProvider metadata constructs, so if your organization has a service provider dishing out your OWS, they belong in this metadata.  This is a contact person for the service itself, not the data
  • fill out Fees and AccessConstraints.  If there aren’t any, use the term “None”
  • the OnlineResource for Service Metadata might be some website, not the URL of the service itself (we already get this from the OperationsMetadata)

Content Metadata

  • fill in title, abstract and keywords in the same manner as above, specific to the given Layer/FeatureType/Coverage/ObservationOffering.  A title like “ROAD_1M” doesn’t cut it
  • your data comes with an FGDC or ISO 19115 XML document already, right?  :) Use MetadataURL to point to the XML document.  Smart catalogues will harvest this too and associate it with the resource
  • WMS DataURL: if the data can be downloaded online (tgz/zip/etc.), point to it here.  Or, put a pointer to an access service like WFS/WCS/SOS
  • WMS Layer Attribution: this provides reference to the content provider (URL, title and LogoURL).  Filling in LogoURL is neat as catalogues can display this when users search for content.  If possible, use an image of smaller dimensions so as to display as a thumbnail
  • Last but not least, bounding boxes.  Whether your OWS software automagically calculates these per layer on the fly, or you can override these and set before runtime, please set spatial extents accordingly.  This improves searching spatially by leaps and bounds.  Don’t settle for the often used default of -180, -90, 180, 90 unless it is really a global dataset

From here, OGC Catalogues will be able to harvest your metadata and provide useful search results.  For wider spread discovery, throw an OpenSearch definition in front of your CSW.  Wrap your OWS endpoints in KML/GeoRSS documents (Geo Sitemaps too), and you’ll power mainstream use of your stuff.

Bye bye useless searches!

Written from home:

Fun with CGDI Services, OpenLayers and jQuery

For years in the CGDI, we’ve had various ‘common’ services; basic XML over HTTP / OGC-ish web services which allow a user to lookup and geocode based on different Canadian spatial identifiers, or keywords.  In their first life (mid 1990s), these existed as embedded lookup tools to facilitate searching and publishing in the GeoConnections Discovery Portal (GDP), then called Canadian Earth Observation Network (CEONet).

CEONet started to publish reusable components (RUCs), which allowed a developer to create an HTML template with special tags to embed these RUCs so as to spatially enable their applications.  Because of the JavaScript security model, the developer passed the template to the CEONet RUC server, which slurped the template and served it up from its own domain.

In both iterations, the backends were driven by database or HTML scrapes which outputted CSV-ish type output.

Since v3, (2001-ish) and the rise of Web Services, these RUCs became services themselves, thereby eliminating the need to go through GDP.

If I had a dime every time someone asked about middleware tools to be able to interact with these services, well….At any rate, the typical approach was as follows:

  • setup HTML form with input parameters
  • send request to middleware
  • middleware invokes web services request, gets result, spits back HTML accordingly to the user

I’ve done these in many different languages.  For awhile, one of our projects had bundled a spatial clients .war which Java developers can plop into their webapps.

These days, using OpenLayers and jQuery lets you develop light, interactive ways of accomplishing this without server side middleware.  Trying this against the CGDI NTS Lookup Service provides a neat example:

<form id="ntsForm" action="javascript:zoomToNTS();">
 <label for="nts">NTS Mapsheet:</label>
 <input type="text" name="nts" id="nts" size="6" maxlength="6"/>
</form>

And now the JavaScript:

function zoomToNTS() {
  // build request URL
  url = '/mapbuilder/server/php/proxy.php?url=' + // simple proxy script
  escape('http://geoservices.cgdi.ca/NTS/NTSLookup?') +
  escape('version=1.1.0&request=GetMapsheet&mapsheet=') +
  jQuery('input#nts').val();

  // send and process result
  jQuery.get(url,{},function(xml){
    // get the bbox of the result
    jQuery('gml\\:boundedBy',xml).each(function(i) {
      c = jQuery(this).find('gml\\:coordinates').text().split(',');
      map.zoomToExtent(new OpenLayers.Bounds(c[0], c[1], c[2], c[3]));
    });
  });
}

That’s it!  That will get your OpenLayers map zoomed in based on the NTS boundaries.  Notes:

  • you’ll need a proxy script to deal with remote URLs
  • you need to escape namespace’d XML elements/attributes per above, possibly wrapping into a function for reuse.  Same goes for elements seperated with ‘.’ (like <foo.bar>)

Anyone have suggestions on improving the example above?  Or any similar snippets?  It would be nice to build up plugins like this for gazetteers, catalogs, and the like.

Written from home: