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 ToInstall 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 one 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
# db=dbname
# table=users
# epsg=4326
# units=degrees
# geomcolumn=the_geom
# idcolumn=Integer:id

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

Note: as a convention use plural names as your layer names. You also need to provide a singular name. Actually, this name can be anything, but to get pretty generated code consider using the singular name associated with your layer name.

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.connect('countries/', controller='countries', action='get', conditions=dict(method=['GET']))
    map.connect('countries/:(id).:(format)', controller='countries', action='get', conditions=dict(method=['GET']))

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!

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!

If you see 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.

If you get exceptions, check out the Server FAQ page, you might find answers there.