Module 5 - Creating Web Services

In this module you are going to learn how to use the framework to create MapFish web services in your application.

MapFish web services are web services for creating, reading, updating and deleting geographic objects (features) through the MapFish Protocol.

The MapFish Protocol is a collection of HTTP APIs. It is highly recommended to take some time to go through the description of these APIs [1] before moving on with the rest of this module.

Installing data

A MapFish web service relies on a geographic data table.

Note

Only PostgreSQL/PostGIS tables are currently supported by MapFish. There’s currently work being done to support SQLite/Spatialite and MySQL.

Before creating the web service we need to create a PostGIS table with some data into it. You’re going to create a PostGIS table from a Shapefile of countries.

First, open the explorer, go into the C:\MapFish\mapfish_workshop\data folder and extract the countries.zip file.

Create a PostGIS-enabled database named mapfish_workshop, and enter the following commands to import the countries Shapefile as a table named countries in the mapfish_workshop database:

C:\MapFish>cd mapfish_workshop\data
C:\MapFish\mapfish_workshop\data>"C:\Program Files\PostgreSQL\8.3\bin\ shp2pgsql.exe" -s 4326 -I countries.shp countries | "C:\Program Files\PostgreSQL\8.3\bin\psql.exe" -d mapfish_workshop -U postgres

You can start pgAdmin and connect to the mapfish_workshop database to check that the countries table is present and non-empty.

Connecting to database

You now need to set up the connection to the mapfish_workshop database from MapFishApp. This is done in the development.ini file.

Edit development.ini and replace the line

sqlalchemy.url = sqlite:///%(here)s/development.db

by this one:

sqlalchemy.url = postgres://postgres:postgres@localhost:5432/mapfish_workshop

The connection string specifies that the postgres driver must be used, the database system listens on localhost and on port 5432, and the name of the database is mapfish_workshop.

Creating web service

Now that the table is created and the connection to the database is set up, you’re ready to create the web service.

Creating a web service is done in three steps:

  1. create a layer configuration in the layers.ini file, in our case:

    [countries]
    singular=country
    plural=countries
    table=countries
    epsg=4326
    geomcolumn=the_geom
    

    singular provides a singular name for the layer. plural provides a plural name for the layer. Both are used by the code generator when substituting variables. table provides the name of the database. epsg provides the coordinate system of the table data. geomcolumn provides the name of the geometry column.

  2. generate the web service code with the mf-layer command:

    <env> C:\MapFish\MapFishApp>paster mf-layer countries
  3. configure a route to the countries controller, this is done by adding the following statement after the “CUSTOM ROUTES HERE” comment in the mapfishapp/config/routing.py file:

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

Watch the indentation! 4 spaces are needed here.

If you killed paster serve or if you did not add the --reload switch, restart MapFishApp with:

<env> C:\MapFish\MapFishApp>paster serve --reload development.ini

You can now open http://localhost:5000/countries?limit=1 in your browser, you should see a GeoJSON representation of the first object in the countries table:

_images/geojson.png

Bonus task

Open the MapFish Protocol description again and write the URLs for the following queries:

  • get the country whose identifier is 1
  • get the country which contains the point (5, 45)
  • get the country which contains the point (5, 45) but don’t receive its geometry
  • get the country which contains the point (5, 45) and receive only the attributes pays and population

Studying the web service code

The paster mf-layer countries created three Python files:

mapfishapp/controllers/countries.py

This file includes the controller code of the countries web service. This is the core of the web service.

class CountriesController(BaseController):
    readonly = False # if set to True, only GET is supported

    def __init__(self):
        self.protocol = Protocol(Session, Country, self.readonly)

    def index(self, format='json'):
        """GET /: return all features."""
        return self.protocol.index(request, response, format=format)

    def show(self, id, format='json'):
        """GET /id: Show a specific feature."""
        return self.protocol.show(request, response, id, format=format)

    def create(self):
        """POST /: Create a new feature."""
        return self.protocol.create(request, response)

    def update(self, id):
        """PUT /id: Update an existing feature."""
        return self.protocol.update(request, response, id)

    def delete(self, id):
        """DELETE /id: Delete an existing feature."""
        return self.protocol.delete(request, response, id)

The controller has methods for each protocol operation: get features (index), get a feature (show), create features (create), update a feature (update), and delete a feature (delete). These methods all rely on Protocol object, this protocol object includes all the logic of the MapFish Protocol as defined in the description.

mapfishapp/model/countries.py

This file includes the model code of the countries web service. The model defines the countries table object, the Country class representing a table record, and the mapping between the two.

countries_table = Table(
    'countries', metadata,
    Column('the_geom', Geometry(4326)),
    autoload=True, autoload_with=engine)

class Country(GeometryTableMixIn):
    # for GeometryTableMixIn to do its job the __table__ property
    # must be set here
    __table__ = countries_table

mapper(Country, countries_table)
tests/functional/test_countries.py
This file is where the application developer can put functional tests for the countries web service. This is an empty shell.

The code generated by the paster mf-layer belongs to the application developer. The developer is free to modify it, based on his needs.

[1]http://www.mapfish.org/doc/1.2/protocol.html