How To Use MapFish Server

This HowTo describes step by step how to use MapFish Server Framework to set up a MapFish project. A MapFish project defines Web Services on which MapFish Client components can rely. See Map Fish Protocol for a description of the interfaces provided by MapFish Web Services.

Warning: This HowTo assumes that the MapFish egg is installed. If not, go to the How To Install page.

Warning: The MapFish-0.1 egg (provided on http://dev.camptocamp.com/packages/eggs/) does not have support for paster commands. Therefore, make sure the installed egg comes from MapFish version > 0.1. If there's no MapFish version > 0.1 (which is the case at time of this writing), please build and install the MapFish egg from SVN trunk.

Create a MapFish project

Paste is used to create MapFish projects:

/path/to/vpython/bin/paster create --overwrite --no-interactive --template=mapfish MyMapFishProject mapfishclientmfbasepath=/path/to/MapFish/client/mfbase

Replace MyMapFishProject with your actual project name. In the rest of this HowTo, we'll refer to your project name as MyMapFishProject.

Note: the above command assumes that you have installed the MapFish egg and its dependencies in a virtual python.

Your project is now created. More concretely, a directory named MyMapFishProject populated with various files has been created. This is your project dir.

A MapFish project is really a Pylons with some geo-oriented specificities.

Set up PostGIS connections

To set up PostGIS connections edit the development.ini configuration file and adds lines of this form in the [app:main_pylons] section:

sqlalchemy.<db>.url = postgres://<dbuser>:<dbpassword>@<dbhost>/<db>

Replace <db>, <dbuser>, <dbpassword>, and <dbhost> appropriately.

If you need multiple database connections, use multiple lines of this sort.

Set up layers

You now need to create layers. In effect, a layer corresponds to a PostGIS table. To create layers edit the layers.ini and add your layers following the example given in the file.

Example of single-layer configuration:

# Layer configuration file

# Example:
#
# [users]
# singular=user
# plural=users
# db=dbname
# table=users
# epsg=4326
# units=degrees
# geomcolumn=the_geom
# idcolumn=Integer:id

[countries]
singular=country
plural=countries
db=geostat
table=world_factbk_simplified
epsg=4326
units=degrees
geomcolumn=simplify
idcolumn=Integer:gid

Note 1: as a convention use plural names as your layer names. Note 2: the idcolumn property can be omited as of v0.3

Generate model and controller code

Now it's time to get paster and MapFish generate code for you.

If you have created a layer named countries (as in the above example) use this command:

/path/to∕vpython/bin/paster mf-layer countries

This command creates three files: a controller file, its associated test file, and a model file. Take a look at the controller and model files to get a sense on what the code generator did for you. To do fancy things you'll have to edit these files anyway so don't be shy...

Note: two other paster commands are provided by MapFish: mf-controller and mf-model. The former takes care of creating the controller. The latter deals with the model. Internally, mf-layer composes mf-controller and mf-model.

Finish configuration

In a perfect world you'd be done. Since world isn't perfect (yet) you have to edit some files to complete your layer configuration.

Change dir to mymapfishproject and edit the files:

  • config/environment.py

Add this line after the from pylons import config line:

from sqlalchemy import engine_from_config

and this one at the end of the file:

    config['pylons.g'].sa_geostat_engine = engine_from_config(config, 'sqlalchemy.geostat.')

This latter line creates an sqlalchemy database engine associated with your database. Obviously, geostat must be replaced by your database connection name (what you've configured in development.ini).

Note: do no forget that indentation matter in Python! The line must be indented to be part of the load_environment() function.

  • config/routing.py

Some URL routing configuration is needed. Add the following lines:

    map.resource('country', 'countries')

before the lines for the default routes:

    map.connect(':controller/:action/:id')
    map.connect('*url', controller='template', action='view')

Again, check your indentation!

See the documentation of Routes to know more about routing configuration with Routes.

  • model/__init__.py

The layer must be bound to the database engine. Edit model/__init__.py and and replace the line

binds = {}

With our example, that line is replaced with:

binds = {'countries': MetaData(config['pylons.g'].sa_geostat_engine)}
  • lib/base.py

One last thing to do is change the BaseController code. The BaseController class must look like this:

class BaseController(WSGIController):

    def __call__(self, environ, start_response):
        try:
            """Invoke the Controller"""
            # WSGIController.__call__ dispatches to the Controller method
            # the request is routed to. This routing information is
            # available in environ['pylons.routes_dict']
            return WSGIController.__call__(self, environ, start_response)
        finally:
            model.Session.remove()

The .remove() method is very important! See this page for further details.

Starting the web server

You should be all set now. Try starting the paster web server:

/path/to/vpython/bin/paster serve --reload development.ini

and checkout http://localhost:5000/countries?maxfeatures=10

Your browser should be displaying a nice GeoJSON object!

You can now go back to your webpage and configure MapFish widgets to access your layer through the URL http://localhost:5000/countries.

If you this warning message:

/path/to/vpython/lib/python2.4/site-packages/SQLAlchemy-0.4.0-py2.4.egg/sqlalchemy/databases/postgres.py:497: RuntimeWarning: Did not recognize type 'geometry' of column 'the_geom'
  warnings.warn(RuntimeWarning("Did not recognize type '%s' of column '%s'" % (attype, name)))

don't worry, it's harmless.