Waiting for PostGIS 3.1: Grid Generators
Summarizing data against a fixed grid is a common way of preparing data for analysis. Fixed grids have some advantages over natural and administrative boundaries:
- No appeal to higher authorities
- Equal unit areas
- Equal distances between cells
- Good for passing data from the "spatial" computational realm to a "non-spatial" realm
Ideally, we want to be able to generate grids that have some key features:
- Fixed origin point, so that grid can be re-generated and not move
- Fixed cell coordinates for a given cell size, so that the same cell can be referred to just using a cell address, without having to materialize the cell bounds
ST_SquareGrid()
The ST_SquareGrid(size, bounds) function generates a grid with an origin at (0, 0) in the coordinate plane, and fills in the square bounds of the provided geometry.
SELECT (ST_SquareGrid(400000, ST_Transform(a.geom, 3857))).*
FROM admin a
WHERE name = 'Brazil';
So a grid generated using Brazil as the driving geometry looks like this.
ST_HexagonGrid()
The ST_HexagonGrid(size, bounds) function works much the same as the square grid function.
Hexagons are popular for some cartographic display purposes and modeling purposes. Surprisingly they can also be indexed using the same two-dimensional indexing scheme as squares.
The hexagon grid starts with a (0, 0) hexagon centered at the origin, and the gridding for a bounds includes all hexagons that touch the bounds.
As with the square gridding, the coordinates of hexes are fixed for a particular gridding size.
SELECT (ST_HexagonGrid(100000, ST_Transform(a.geom, 3857))).*
FROM admin a
WHERE name = 'Germany';
Here's a 100km hexagon gridding of Germany.
Summarizing with Grids
It's possible to materialize grid-based summaries, without actually materializing the grids, using the generator functions to create the desired grids on-the-fly.
Here's a summary of population points, using a hex grid.
SELECT sum(pop_max) as pop_max, hexes.geom
FROM
ST_HexagonGrid(
4.0,
ST_SetSRID(ST_EstimatedExtent('places', 'geom'), 4326)
) AS hexes
INNER JOIN
places AS p
ON ST_Intersects(p.geom, hexes.geom)
GROUP BY hexes.geom;
It's also possible to join up on-the-fly gridding to visualization tools, for very dynamic user experiences, feeding these dynamically generated grids out to the end user via pg_tileserv.
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