Extending geoalchemy through monkeypatching

I’ve been working on the data collection part of my cycle route modelling. I’m hoping that I can, as a first output, put together a map of where people are cycling in Melbourne. A crowd-sourced view of the best places to cycle, if you will. Given I will probably be running this in the cloud1, I thought it was best to actually store the data in a GIS database, rather than lots and lots of flat files.

A quick Google turned up GeoAlchemy, which are GIS extensions for SQLAlchemy. Provides lots of the standard things you want to do as methods on fields, but this is only a limited set of what you can do with PostGIS. Since I’m going to be wanting to do things like binning data, I thought it was worth figuring out how hard it was to call other PostGIS methods.

GeoAlchemy supports subclassing to create new dialects, but you have to subclass 3 classes, and it’s basically a pain in the neck when you just want to extend the functionality of the PostGIS dialect. Probably what I should do is submit a pull request with the rest of the PostGIS API as extensions, but I’m lazy. Henceforth, for the second time this week I am employing monkey patching to get the job done (and for the second time this week, kittens cry).

Functions in GeoAlchemy require two things, a method stub saying how we collect the arguments and the return (look at geoalchemy.postgis.pg_functions) and a mapping from this to the SQL function. Since we only care about one dialect, we can make this easier on ourselves by combining these two things. Firstly we monkeypatch in the method stubs:

from geoalchemy.functions import BaseFunction
from geoalchemy.postgis import pg_functions

class more_pg_functions:
    Additional functions to support for PostGIS

    class length_spheroid(BaseFunction):
        _method = 'ST_Length_Spheroid'

Note the _method attribute which isn’t something used anywhere else. We can then patch in support for this:

from geoalchemy.dialect import SpatialDialect

def get_function(self, function_cls):
    Add support for the _method attribute

        return function_cls._method
    except AttributeError:
        return self.__super__get_function(function_cls)

The monkeypatching functions look like this:

def monkeypatch(*args):
    Decorator to monkeypatch a function into class as a method

    def inner(func):
        name = func.__name__

        for cls in args:
            old = getattr(cls, name)
            setattr(cls, '__super__{}'.format(name), old)

            setattr(cls, name, func)

    return inner

def monkeypatchclass(cls):
    Decorator to monkeypatch a class as a baseclass of @cls

    def inner(basecls):
        cls.__bases__ += (basecls,)

        return basecls

    return inner

Finally we can do queries like this:

>>> track = session.query(Track).get(1)
>>> session.scalar(track.points.length_spheroid('SPHEROID["WGS 84",6378137,298.257223563]'))

Code on GitHub.

  1. your recommendations for cloud-based services please, must be able to run Flask and PostGIS and be super cheap []

Author: Danielle

Danielle is an Australian software engineer, computer scientist and feminist. She doesn't really work on GNOME any more (sadly). Opinions and writing are solely her own and so not represent her employer, the GNOME Foundation, or anyone else but herself.

4 thoughts on “Extending geoalchemy through monkeypatching”

  1. Hello there,

    until a few hours ago I had only experience with Heroku as PaaS. For your requirement with PostGIS support and cheap Heroku is not a option (PostGIS is supported by Postgres databases Ronin and higher, so that would be 200+$/month).

    A alternative is RedHats OpenShift. It is possible to use OpenShift completely for free. Then the number of applications is limited to 3, with 1 GB storage for each.
    It should be also compatible with flask and supports PostGIS.

    And currently in closed beta – not sure if it supports PostGIS though:

  2. Hey Danielle,

    I used to work on OpenShift before I left Red Hat. As long as the modules can be pip installed it should run under OpenShift and since the base OpenShift Origin is open source, you can always work with the devs to fix issues.

    BTW they already have a flask example:


    Pricing beyond the free stuff is here:


    I also believe they have an Open Source project tier which is either free or discounted. I left before that was all put in place so I don’t know the details.

Comments are closed.

Leave a Reply

Your email address will not be published. Required fields are marked *