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
launch PostgreSQL...
docker-compose up -d postgis
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
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;
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
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
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
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
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
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
In QGIS, you can get tiles as following...
- add vectortile source
- zoom into a area data exists in
- add vectortile layer
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.
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.
Top comments (0)