Skip to main content

Example: Raw Data Publication

Exploring the raw OpenStreetMap data can be done in the database itself, but this limits the audience of users. To enable public access we need a web API that allows exploring, filtering and downloading of the data. This objective is achieved through the use of the established OGC API Features standard in conjunction with the pg_featureserv program. The following example showcases this process. We want to analyse restaurants, so we create the Lua file restaurants.lua with this content:

Download Lua config
local restaurants = osm2pgsql.define_table({
    name = 'restaurants',
    ids = { type = 'any', id_column = 'osm_id' },
    columns = {
        { column = 'name',    type = 'text' },
        { column = 'cuisine', type = 'text' },
        { column = 'geom',    type = 'point', projection = 4326, not_null = true }
    }
})

function osm2pgsql.process_node(object)
    if object.tags.amenity == 'restaurant' then
        restaurants:insert(
            {
                name = object.tags.name,
                cuisine = object.tags.cuisine,
                geom = object:as_point()
            }
        )
    end
end

function osm2pgsql.process_way(object)
    if object.tags.amenity == 'restaurant' and object.is_closed then
        restaurants:insert(
            {
                name = object.tags.name,
                cuisine = object.tags.cuisine,
                geom = object:as_polygon():centroid()
            }
        )
    end
end

function osm2pgsql.process_relation(object)
    if object.tags.amenity == 'restaurant' and object.tags.type == 'multipolygon' then
        restaurants:insert(
            {
                name = object.tags.name,
                cuisine = object.tags.cuisine,
                geom = object:as_multipolygon():centroid()
            }
        )
    end
end

Then we load the data in our database.

osm2pgsql -d osm -O flex -S restaurants.lua OSMDATA.osm.pbf

We can install pg_featureserv by downloading the binary from here. Normally, pg_featureserv can run without any extra settings. But because we plan to show a simple map on the website later, we require further configuration. So, we create a configuration file called pg_featureserv.toml that has this content:

[Website]
# Add a source URL of maptiles to be shown as background for our features
BasemapUrl = "http://a.tile.openstreetmap.fr/hot/{z}/{x}/{y}.png"

We must adjust the DATABASE_URL to match our setup. Then, we can initiate the API using this command:

DATABASE_URL=postgres://username:password@localhost/osm ./pg_featureserv --config=./pg_featureserv.toml

This service starts at the address http://localhost:9000/ and can be accessed directly from the browser. The metadata for our table, like the the extent or available attribute names, can be viewed on this page:

http://localhost:9000/collections/public.restaurants.html

The link below shows the data on the map. Please note that only a subset of all the features are displayed, but this can be changed in the user interface of the map. Clicking on a feature shows provides additional information of it:

http://localhost:9000/collections/public.restaurants/items.html

Screenshot of pg_featureserv

By changing the .html ending to .json, the data will be returned as GeoJSON. It includes all raw data like attributes or geometry. A GeoJSON can be processed with many clients such as OpenLayers, Leaflet, MapLibre, or QGIS. For performance reasons, in default mode pg_featureserv returns only 10 features per request. With the parameter limit we can increase this count :

http://localhost:9000/collections/public.restaurants/items.json?
limit=100

The response can be filtered by both spatial and non-spatial properties. Here we request restaurants with Indian cuisine in an specific area.

http://localhost:9000/collections/public.restaurants/items.json?
limit=100&
bbox=8.4877,49.2583,9.1773,49.4579&
cuisine=indian

The response is a GeoJSON like this (download). Many programs or websites can display it as table like this:

More advanced filtering can be done by using the filter keyword in CQL (Common Query Language). With the parameter filter=name ILIKE '%restaurant%' we can retrieve all features that have the word restaurant in their name. Note the value of filter must be encoded.

http://localhost:9000/collections/public.restaurants/items.json?
filter=name%20ILIKE%20%27%25restaurant%25%27%