Skip to content

Instantly share code, notes, and snippets.

@ivanlonel
Last active May 22, 2025 00:20
Show Gist options
  • Save ivanlonel/c9cc2b17421a774b36410f217ea22ec8 to your computer and use it in GitHub Desktop.
Save ivanlonel/c9cc2b17421a774b36410f217ea22ec8 to your computer and use it in GitHub Desktop.
Given a geometry and a plus code length (2, 4, 6, 8, 10, 11, 12, 13, 14, or 15), returns a grid of all rectangles represented by plus codes of that length that intersect the geometry.
-- https://dbfiddle.uk/3CgLervE
-- This uses function pluscode_decode, implemented here:
-- https://github.com/google/open-location-code/blob/main/plpgsql/pluscode_functions.sql
CREATE OR REPLACE FUNCTION pluscode_grid(original_geometry geometry, pluscode_length integer)
-- Given a geometry and a plus code length (2, 4, 6, 8, 10, 11, 12, 13, 14, or 15), returns a
-- grid of all rectangles represented by plus codes of that length that intersect the geometry.
RETURNS TABLE (pluscode text, geom geometry)
LANGUAGE SQL
IMMUTABLE
RETURNS NULL ON NULL INPUT
PARALLEL SAFE
BEGIN ATOMIC
WITH RECURSIVE constants AS (
SELECT
'23456789CFGHJMPQRVWX' AS code_alphabet,
ST_Transform($1, 4326) AS wgs84_geom
), grid0 AS (
-- Base case: Generate the 2-character plus codes
SELECT
geom,
pluscode,
2 AS code_length
FROM constants AS c,
generate_series(1, 9) AS i,
generate_series(1, 18) AS j,
concat(
substr(c.code_alphabet, i, 1),
substr(c.code_alphabet, j, 1),
'000000+'
) AS pluscode,
pluscode_decode(pluscode),
ST_MakeEnvelope(lng_lo, lat_lo, lng_hi, lat_hi, 4326) AS geom
WHERE ST_Intersects(wgs84_geom, geom)
UNION ALL
-- Recursive step for lengths 4, 6, 8, 10: Add two characters at a time
SELECT
pluscode_geom,
code,
g.code_length + 2
FROM grid0 AS g,
constants AS c,
generate_series(1, 20) AS i,
generate_series(1, 20) AS j,
concat(
CASE g.code_length WHEN 8 THEN g.pluscode ELSE left(g.pluscode, g.code_length) END,
substr(c.code_alphabet, i, 1),
substr(c.code_alphabet, j, 1),
right('0000+', greatest(7 - g.code_length, 0))
) AS code,
pluscode_decode(code),
ST_MakeEnvelope(lng_lo, lat_lo, lng_hi, lat_hi, 4326) AS pluscode_geom
WHERE g.code_length < 10
AND g.code_length < $2
AND ST_Intersects(wgs84_geom, pluscode_geom)
), grid AS (
SELECT * FROM grid0
UNION ALL
-- Recursive step for lengths 11 to 15: Add one character at a time
SELECT
pluscode_geom,
code,
g.code_length + 1
FROM grid AS g,
constants AS c,
generate_series(1, 20) AS i,
concat(
g.pluscode,
substr(c.code_alphabet, i, 1)
) AS code,
pluscode_decode(code),
ST_MakeEnvelope(lng_lo, lat_lo, lng_hi, lat_hi, 4326) AS pluscode_geom
WHERE g.code_length >= 10
AND g.code_length < $2
AND ST_Intersects(wgs84_geom, pluscode_geom)
)
SELECT
pluscode,
ST_Transform(geom, ST_SRID($1)) AS geom
FROM grid
WHERE code_length = $2;
END;
-- https://dbfiddle.uk/3CgLervE
-- This uses function pluscode_decode, implemented here:
-- https://github.com/google/open-location-code/blob/main/plpgsql/pluscode_functions.sql
CREATE OR REPLACE FUNCTION pluscode_grid_compact(original_geometry geometry, pluscode_length integer)
-- Given a geometry and a plus code length, returns a set of non-overlapping rectangles represented
-- by plus codes of that length or lower which, together, contain the geometry, aiming to minimize
-- both the number of rectangles inside the geometry and the area outside it.
RETURNS TABLE (pluscode text, geom geometry)
LANGUAGE SQL
IMMUTABLE
RETURNS NULL ON NULL INPUT
PARALLEL SAFE
BEGIN ATOMIC
WITH RECURSIVE constants AS (
SELECT
'23456789CFGHJMPQRVWX' AS code_alphabet,
ST_Transform($1, 4326) AS wgs84_geom
), grid0 AS (
-- Base case: Generate the 2-character plus codes
SELECT
geom,
pluscode,
2 AS code_length,
FALSE AS contained
FROM constants AS c,
generate_series(1, 9) AS i,
generate_series(1, 18) AS j,
concat(
substr(c.code_alphabet, i, 1),
substr(c.code_alphabet, j, 1),
'000000+'
) AS pluscode,
pluscode_decode(pluscode),
ST_MakeEnvelope(lng_lo, lat_lo, lng_hi, lat_hi, 4326) AS geom
WHERE ST_Intersects(wgs84_geom, geom)
UNION ALL
-- Recursive step for lengths 4, 6, 8, 10: Add two characters at a time
SELECT
pluscode_geom,
code,
g.code_length + 2,
ST_Contains(wgs84_geom, pluscode_geom)
FROM grid0 AS g,
constants AS c,
generate_series(1, 20) AS i,
generate_series(1, 20) AS j,
concat(
CASE g.code_length WHEN 8 THEN g.pluscode ELSE left(g.pluscode, g.code_length) END,
substr(c.code_alphabet, i, 1),
substr(c.code_alphabet, j, 1),
right('0000+', greatest(7 - g.code_length, 0))
) AS code,
pluscode_decode(code),
ST_MakeEnvelope(lng_lo, lat_lo, lng_hi, lat_hi, 4326) AS pluscode_geom
WHERE g.code_length < 10
AND g.code_length < $2
AND NOT g.contained
AND ST_Intersects(wgs84_geom, pluscode_geom)
), grid AS (
SELECT * FROM grid0
UNION ALL
-- Recursive step for lengths 11 to 15: Add one character at a time
SELECT
pluscode_geom,
code,
g.code_length + 1,
ST_Contains(wgs84_geom, pluscode_geom)
FROM grid AS g,
constants AS c,
generate_series(1, 20) AS i,
concat(
g.pluscode,
substr(c.code_alphabet, i, 1)
) AS code,
pluscode_decode(code),
ST_MakeEnvelope(lng_lo, lat_lo, lng_hi, lat_hi, 4326) AS pluscode_geom
WHERE g.code_length >= 10
AND g.code_length < $2
AND NOT g.contained
AND ST_Intersects(wgs84_geom, pluscode_geom)
)
SELECT
pluscode,
ST_Transform(geom, ST_SRID($1)) AS geom
FROM grid
WHERE contained OR code_length = $2;
END;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment