Waiting for PostGIS 3: ST_TileEnvelope(z,x,y)
With the availability of MVT tile format in PostGIS via ST_AsMVT(), more and more people are generating tiles directly from the database. Doing so usually involves a couple common steps:
- exposing a tiled web map API over HTTP
- converting tile coordinates to ground coordinates to drive tile generation
Tile coordinates consist of three values:
- zoom, the level of the tile pyramid the tile is from
- x, the coordinate of the tile at that zoom, counting from the left, starting at zero
- y, the coordinate of the tile at that zoom, counting from the top, starting at zero
Most tile coordinates reference tiles built in the "spherical mercator" projection, which is a planar project that covers most of the world, albeit with substantial distance distortions the further north you go.
Knowing the zoom level and tile coordinates, the math to find the tile bounds in web mercator is fairly straightforward.
Most of the people generating tiles from the database write their own small wrapper to convert from tile coordinate to mercator, as we demonstrated in a blog post last month.
It seems duplicative for everyone to do that, so we have added a utility function: ST_TileEnvelope()
By default, ST_TileEnvelope() takes in zoom, x and y coordinates and generates bounds in Spherical Mercator.
SELECT ST_AsText(ST_TileEnvelope(3, 4, 2));
st_astext
--------------------------------------------------------------------------------------
POLYGON((0 5009377.5,0 10018755,5009377.5 10018755,5009377.5 5009377.5,0 5009377.5))
If you need to generate tiles in another coordinate system -- a rare but not impossible use case -- you can swap in a different spatial reference system and tile set bounds via the bounds
parameter, which can encode both the tile plane bounds and the spatial reference system in a geometry:
SELECT ST_AsText(ST_TileEnvelope(3, 4, 2, bounds => ST_MakeEnvelope(0, 0, 1000000, 1000000, 3005)));
Note that the same tile coordinates generate different bounds -- because the base level tile bounds are different.
st_astext
----------------------------------------------------------------------------------
POLYGON((500000 625000,500000 750000,625000 750000,625000 625000,500000 625000))
ST_TileEnvelope() will also happily generate non-square tiles, if you provide a non-square bounds.
Related Articles
- Crunchy Data Warehouse: Postgres with Iceberg for High Performance Analytics
8 min read
- Loading the World! OpenStreetMap Import In Under 4 Hours
6 min read
- Easy Totals and Subtotals in Postgres with Rollup and Cube
5 min read
- A change to ResultRelInfo - A Near Miss with Postgres 17.1
8 min read
- Accessing Large Language Models from PostgreSQL
5 min read