DEV Community

Kanahiro Iguchi for MIERUNE

Posted on • Edited on

Comparing PostGIS-backend Vectortile servers: tegola/martin/pg_tileserv

Abstract

In ordinary cases, vectortiles are statically served as files converted from GeoJSON or other datasources. This is the best way to serve tiles in terms of performances and costs for servers but there are some typical problems.

  • Need computing costs for converting, this can be a bigger problem when a number of tiles is huger.
  • Impossible to serve data equal to database.

Therefore, it is very important to be able to dynamically serve tiles directly from DB. Offcourse, in this approach has other challenges such as performances so you need to consider which approach should be taken and why, how.

There are some implementations in PostGIS-backend Vectortile server in mid 2022. These can be used to serve vectortiles from PostGIS tables.

martin and pg_tileserv is based on ST_AsMVT in PostGIS functions and t-rex and tegola are not. This is because martin and pg_tileserv are newer than others. In this article, I compared tegola, martin and pg_tileserv.

Setting servers

PostgreSQL

First, let's launch PostGIS and insert sample data, using Docker…



  postgis:
    image: kartoza/postgis:12.4
    environment:
      - POSTGRES_USER=docker
      - POSTGRES_PASS=docker
      - POSTGRES_DB=postgres
    ports:
      - "5432:5432"
    volumes:
      - postgis-data:/var/lib/postgresql
      - ./postgres:/usr/src/app
    networks:
      - default
    healthcheck:
      test: [ "CMD-SHELL", "pg_isready" ]
      interval: 10s
      timeout: 5s
      retries: 5


Enter fullscreen mode Exit fullscreen mode

launch PostgreSQL...



docker-compose up -d postgis


Enter fullscreen mode Exit fullscreen mode

Next, get and insert data. I used geofabrik shapefiles.



wget https://download.geofabrik.de/asia/japan/kanto-latest-free.shp.zip
unzip kanto-latest-free.shp.zip
ogr2ogr PG:postgresql://docker:docker@localhost:5432/postgres gis_osm_roads_free_1.shp
ogr2ogr PG:postgresql://docker:docker@localhost:5432/postgres gis_osm_buildings_a_free_1.shp -nlt MultiPolygon


Enter fullscreen mode Exit fullscreen mode

Then, you can see tables in PostgreSQL.



SELECT * FROM gis_osm_roads_free_1 LIMIT 10;
SELECT * FROM gis_osm_buildings_a_free_1 LIMIT 10;


Enter fullscreen mode Exit fullscreen mode

tegola

writting compose.yml...



  tegola:
    image: gospatial/tegola
    ports:
      - "8080:8080"
    depends_on:
      postgis:
        condition: service_healthy
    volumes:
      - ./tegola:/opt/tegola_config
    command: --config /opt/tegola_config/config.toml serve
    networks:
      - default


Enter fullscreen mode Exit fullscreen mode

tegola uses config.toml to define settings how generate and serve vectortiles, as following



# /tegola/config.toml
[webserver]
port = ":8080"
CORSAllowedOrigin = "*"

# register data providers
[[providers]]
name = "japan"         # provider name is referenced from map layers (required)
type = "postgis"      # the type of data provider. currently only supports postgis (required)
host = "postgis"      # postgis database host (required)
port = 5432           # postgis database port (required)
database = "postgres"     # postgis database name (required)
user = "docker"       # postgis database user (required)
password = "docker"         # postgis database password (required)
srid = 4326             # The default srid for this provider. If not provided it will be WebMercator (3857)

  [[providers.layers]]
  name = "roads"
  geometry_fieldname = "wkb_geometry"
  id_fieldname = "ogc_fid"
  tablename = "gis_osm_roads_free_1"

  [[providers.layers]]
  name = "buildings"
  geometry_fieldname = "wkb_geometry"
  id_fieldname = "ogc_fid"
  tablename = "gis_osm_buildings_a_free_1"

[[maps]]
name = "japan"
center = [139.72120, 35.73273, 11.0] # set the center of the map so the user is auto navigated to Bonn


  [[maps.layers]]
  provider_layer = "japan.buildings"
  min_zoom = 5
  max_zoom = 20

  [[maps.layers]]
  provider_layer = "japan.roads"
  min_zoom = 5
  max_zoom = 20


Enter fullscreen mode Exit fullscreen mode

With these settings, vectortiles will be served as http://localhost:8080/maps/japan/{z}/{x}/{y}.vector.pbf.

martin

compose



  martin:
    image: urbica/martin
    restart: unless-stopped
    ports:
      - 3000:3000
    depends_on:
      postgis:
        condition: service_healthy
    networks:
      - default
    volumes:
      - ./martin:/opt/martin_config
    command: martin --config /opt/martin_config/config.yaml


Enter fullscreen mode Exit fullscreen mode

martin takes a similar approach to tegola, using setting file in yml.



# The socket address to bind [default: 0.0.0.0:3000]
listen_addresses: '0.0.0.0:3000'
# Database connection string
connection_string: 'postgres://docker:docker@postgis/postgres'
# Maximum connections pool size [default: 20]
pool_size: 20
# Connection keep alive timeout [default: 75]
keep_alive: 75
# Number of web server workers
worker_processes: 8
# If a spatial table has SRID 0, then this default SRID will be used as a fallback
default_srid: 4326
# Enable watch mode
watch: false
# Trust invalid certificates. This introduces significant vulnerabilities, and should only be used as a last resort.
danger_accept_invalid_certs: true

# Associative arrays of table sources
table_sources:
  public.buildings:
    # Table source id (required)
    id: public.gis_osm_buildings_a_free_1
    # Table schema (required)
    schema: public
    # Table name (required)
    table: gis_osm_buildings_a_free_1
    # Geometry SRID (required)
    srid: 4326
    # Geometry column name (required)
    geometry_column: wkb_geometry
    # Feature id column name
    id_column: ogc_fid
    # An integer specifying the minimum zoom level
    minzoom: 0
    # An integer specifying the maximum zoom level. MUST be >= minzoom
    maxzoom: 30
    # The maximum extent of available map tiles. Bounds MUST define an area
    # covered by all zoom levels. The bounds are represented in WGS:84
    # latitude and longitude values, in the order left, bottom, right, top.
    # Values may be integers or floating point numbers.
    bounds: [-180.0, -90.0, 180.0, 90.0]
    # Tile extent in tile coordinate space
    extent: 4096
    # Buffer distance in tile coordinate space to optionally clip geometries
    buffer: 64
    # Boolean to control if geometries should be clipped or encoded as is
    clip_geom: true
    # Geometry type
    geometry_type: GEOMETRY
    # List of columns, that should be encoded as tile properties (required)
    properties:
      ogc_fid: integer
      name: string
      fclass: string

  public.roads:
    id: public.gis_osm_roads_free_1
    schema: public
    table: gis_osm_roads_free_1
    srid: 4326
    geometry_column: wkb_geometry
    id_column: ogc_fid
    minzoom: 0
    maxzoom: 30
    bounds: [-180.0, -90.0, 180.0, 90.0]
    extent: 4096
    buffer: 64
    clip_geom: true
    geometry_type: GEOMETRY
    properties:
      ogc_fid: integer
      name: string
      fclass: string
      maxspeed: smallint


Enter fullscreen mode Exit fullscreen mode

Vectortiles will be served as http://localhost:3000/public.roads,public.buildings/{z}/{x}/{y}.pbf.

pg_tileserv

compose



  pg_tileserv:
    image: pramsey/pg_tileserv
    container_name: pg_tileserv
    environment:
      - DATABASE_URL=postgres://docker:docker@postgis/postgres
    depends_on:
      - postgis
    ports:
      - 7800:7800


Enter fullscreen mode Exit fullscreen mode

pg_tileserv can be launched without config files. Tables are automatically loaded.

Vectortiles will be served as http://localhost:7800/public.gis_osm_buildings_a_free_1,public.gis_osm_roads_free_1/{z}/{x}/{y}.vector.pbf.

Comparing

Now we can launch vectortile server!



docker compose up


Enter fullscreen mode Exit fullscreen mode

In QGIS, you can get tiles as following...

  1. add vectortile source
  2. zoom into a area data exists in
  3. add vectortile layer

Image description

Image description

Image description

Then you can see no difference between three servers in terms of apperance because we use same database, but there should be a big differnce in performances. martin and pg_tileserv should be much faster than tegola.

Image description

why different?

I'm not familiar with details in implementation of tegola and martin/pg_tileserv but I guess it is because martin/pg_tileserv uses ST_AsMVT and tegola doesn't.

As far as I digged, ST_AsMVT was implemented in 2018 first, and the performance improved in 2019.

Actually newest version of tegola seems to support ST_AsMVT but I couldn't work it, troubled in settings.

Besides, martin is clearly faster than pg_tileserv. The reason of this difference is less sure than between tegola and martin/pg_tileserv but martin uses Actix-webserver, which is one of the fastest server(https://www.techempower.com/benchmarks/#section=data-r21)) and this could explain a certain extent of the difference.

Conclusion

  • martin is the fastest PostGIS-backend vectortile server.
  • pg_tileserv is second but easy config and rich functions.

Full Codes

https://github.com/Kanahiro/postgis-tileservers-compare

Top comments (0)