A5 Geospatial Index
Encode lat/lng to a pentagonal cell ID and aggregate, join, or spatially filter by that ID. A5 is a pentagon-based discrete global grid system — newer than Uber's H3 (hexagonal) and Google's S2 (square), with more uniform cell area across the globe. 31 resolution levels from continental scale down to sub-square-meter precision.
Install
-- Install the extension
INSTALL a5 FROM community;
-- Load it into your session
LOAD a5;
-- Encode a point as an A5 cell at resolution 10
SELECT a5_lonlat_to_cell(-74.0060, 40.7128, 10) AS nyc_cell;
-- Aggregate point data into pentagonal buckets
SELECT a5_lonlat_to_cell(longitude, latitude, 10) AS cell_id,
COUNT(*) AS point_count
FROM points
GROUP BY cell_id
ORDER BY point_count DESC; Technical Overview
Why Use A5?
A5 is a pentagonal discrete global grid system — an alternative to Uber's H3 (hexagonal) and Google's S2 (square). Use it to convert (lon, lat) points into integer cell IDs that you can GROUP BY, JOIN, and index, when the area-uniformity of pentagonal cells matters for your bucketing.
⬟ What this extension is for
Any spatial analysis that benefits from collapsing (lon, lat) to a fixed-cardinality integer key. The output of a5_lonlat_to_cell is a UBIGINT you can put in a GROUP BY, an index, or a join key — same shape you'd use H3 or S2 IDs in.
- • Spatial aggregation:
a5_lonlat_to_cellreturns a single integer per point.GROUP BYit for heatmap-style density queries — counts, sums, percentiles per cell — without touching thespatialextension. - • Spatial joins via shared cell ID: Encode both sides of a join at the same resolution and join on the cell ID. Faster than polygon-on-polygon
ST_Intersectsfor the common "are these points in the same neighborhood" question. - • Hierarchical roll-up:
a5_cell_to_parentwalks a fine-grained cell up to a coarser resolution. The 31-level hierarchy means one encode at the finest resolution you'll need lets you re-aggregate at every coarser zoom without re-reading the source rows. - • Bounding-region search:
a5_grid_disk,a5_grid_disk_vertex, anda5_spherical_capgenerate a set of cell IDs covering a neighborhood — turn "all points within k cells / X meters" into anIN (...)predicate against an indexed cell column.
⬢ A5 vs H3 vs S2
All three are global cell-based spatial indices that produce integer IDs. The shape of the cell, and how uniform its area is across the globe, is what differs.
- • Cell shape: H3 tiles the world with hexagons (with 12 unavoidable pentagons at the icosahedral vertices). S2 uses curvilinear quadrilaterals derived from a cube projection. A5 uses pentagons throughout, derived from a dodecahedral subdivision — see the A5 motivation page for the geometric argument.
- • Area uniformity: H3 hex area drifts noticeably with latitude — same-resolution cells near the poles are smaller than at the equator. A5 cells are equal-area at each resolution by construction, which matters when you're computing densities or comparing counts across very different latitudes. The A5 vs H3 page in the upstream docs walks through the trade.
- • Neighbor count: Hexagons have a clean 6-neighbor structure that's nice for grid traversal. Pentagons have 5 edge-neighbors, which is slightly less convenient — but A5 has no "pentagon exceptions" the way H3 does, so neighborhood queries don't need special-case logic.
- • Ecosystem maturity: H3 is older and has a much larger ecosystem (Postgres, BigQuery, Snowflake, Spark, plus Uber's first-party libraries). S2 is widely deployed inside Google. A5 is newer — the upstream library and specification are at github.com/felixpalmer/a5. If you need the broadest tool support today, H3 is still the safest pick. Reach for A5 specifically when uniform cell area matters.
🧭 How it works
The extension is a thin DuckDB binding around the upstream A5 Rust library. Cell IDs are 64-bit unsigned integers (UBIGINT); the resolution level is encoded in the ID itself, so a5_get_resolution recovers it without a separate column.
- • Cell IDs are UBIGINTs: Every function returns or accepts
UBIGINTcell IDs. Usea5_u64_to_hex/a5_hex_to_u64when you need to round-trip them through systems that prefer the canonical hex string form (matching the A5 reference library). - • 31 resolution levels: Resolution 0 is the 12 base cells covering the globe (
a5_get_res0_cells); resolution 30 is sub-square-meter.a5_cell_areareturns the equal-area cell size for any level. Pick the coarsest resolution that still distinguishes your phenomena — every level finer is roughly a 4× row count. - • Boundary as a polygon:
a5_cell_to_boundaryreturns the cell's vertices as[lon, lat]pairs. Pair with thespatialextension'sST_MakePolygon/ST_AsGeoJSONto render cells on a map (the Cookbook shows the exact pattern). - • Compaction:
a5_compactreplaces complete groups of sibling cells with their parent — useful for shrinking a coverage set before storing or transmitting it.a5_uncompactis the inverse, expanding to a target resolution.
🎯 Common Use Cases
Heatmap-style density aggregation
GROUP BY a5_lonlat_to_cell(lon, lat, 10) to count points per cell, then join with a5_cell_to_boundary for rendering. Because cells are equal-area, the resulting counts are directly comparable across latitudes — no per-cell area normalization needed.
Pre-indexed proximity search
Encode all points into A5 cells at ingest time. For "events near (lon, lat)", expand to a a5_spherical_cap of cell IDs and filter via WHERE cell_id IN (...) — pushed down to whatever index you have on the cell column.
Multi-zoom rollup
Store the finest resolution you'll need; use a5_cell_to_parent to roll up to coarser views in the same query. One source table powers every zoom level without re-reading raw points.
Coverage set compression
Combine a5_compact with a5_uncompact when storing or shipping a region-of-interest as a set of cells — fewer rows to ship, expandable on the consumer side.
Deep Dive
Technical Details
DuckDB ↔ A5 Pentagonal Grid
A pentagon-based discrete global grid system — an alternative to H3 and S2 with equal-area cells at every resolution.
What you can do with one query
Turn a table of (longitude, latitude) points into a heatmap-style aggregation in one statement:
SELECT
a5_lonlat_to_cell(longitude, latitude, 10) AS cell_id,
COUNT(*) AS point_count
FROM pickups
GROUP BY cell_id
ORDER BY point_count DESC;
a5_lonlat_to_cell returns a UBIGINT you can GROUP BY, JOIN, or index — the same role H3 / S2 cell IDs play in those systems. Because A5 cells are equal-area at each resolution, the resulting counts are directly comparable across latitudes without per-cell normalization.
A5 is a newer discrete global grid system. Its specification and reference library at github.com/felixpalmer/a5 are stable, but the surrounding ecosystem is much smaller than H3’s — fewer first-party bindings (Postgres, BigQuery, Spark), fewer Stack Overflow answers, fewer existing pipelines that emit A5 IDs.
Reach for A5 specifically when uniform cell area matters: cross-latitude density comparisons, equal-area sampling, anything where H3’s hex-area variation near the poles is a problem. For “the broadest tool support” or “matching what the rest of my stack uses,” H3 is still the conservative pick. The upstream A5 vs H3 page walks through the trade in detail.
A5 vs H3 vs S2 at a glance
| Property | A5 (this extension) | H3 | S2 |
|---|---|---|---|
| Cell shape | Pentagons throughout | Hexagons (+ 12 pentagon exceptions) | Curvilinear quadrilaterals |
| Equal area | Yes, at every resolution | Approximate; varies with latitude | Approximate; varies with face |
| Edge neighbors | 5 | 6 (mostly) | 4 |
| ID type | UBIGINT | UBIGINT | UBIGINT |
| Resolution levels | 31 (0–30) | 16 (0–15) | 31 (0–30) |
| DuckDB extension here | Yes | Via h3 community extension | No first-party DuckDB extension as of writing |
| Ecosystem | Newer; small but growing | Large (Uber, Postgres, BigQuery, Snowflake, …) | Large inside Google products |
If you’re already on H3 and the area drift across latitudes isn’t biting you, there’s no reason to switch. If you’re starting fresh and equal-area cells matter, A5 is a good pick.
Resolution guide
Pick the coarsest resolution that still distinguishes the phenomena you’re measuring — each finer level is roughly a 4× row count after GROUP BY.
| Resolution | Cell area (approx) | Typical use |
|---|---|---|
| 0–5 | 42M km² – 33k km² | Continental / country-scale rollup |
| 6–10 | 8k km² – 130 km² | Regional / metro-scale analysis |
| 11–15 | 32 km² – 32 hectares | City / district analysis |
| 16–20 | 8 hectares – 124 m² | Neighborhood / building analysis |
| 21–25 | 31 m² – 0.5 m² | Room / vehicle scale |
| 26–30 | 8 cm² – 0.03 mm² | Sub-meter precision |
a5_cell_area returns the exact equal-area size for any resolution; a5_get_num_cells returns the global cell count.
Hierarchy and compaction
A5’s 31-level hierarchy means one encode at the finest resolution you’ll need supports every coarser zoom:
-- Encode once at the finest resolution
CREATE TABLE indexed AS
SELECT *, a5_lonlat_to_cell(lon, lat, 15) AS cell_15
FROM raw_points;
-- Roll up to a coarser view without re-reading raw points
SELECT a5_cell_to_parent(cell_15, 10) AS cell_10,
COUNT(*) AS n
FROM indexed
GROUP BY cell_10;
a5_cell_to_parent walks any cell to a coarser ancestor; a5_cell_to_children goes the other way. For a region-of-interest expressed as a set of cells, a5_compact replaces complete groups of siblings with their parent — useful before storing or shipping a coverage set. a5_uncompact is the inverse.
Neighborhoods and proximity search
Three traversal primitives, depending on how you define “near”:
a5_grid_disk— all cells withinkedge-steps of a center cell.a5_grid_disk_vertex— all cells withinkvertex-steps (a slightly wider disk that includes corner-touching cells).a5_spherical_cap— all cells within a metric radius (in meters) of a center cell.
Each returns a UBIGINT[]. The typical use is to expand to a coverage set, then probe an indexed cell column with WHERE cell_id IN (UNNEST(...)) — much cheaper than ST_DWithin against raw geometries.
Boundary rendering
a5_cell_to_boundary returns cell vertices as [lon, lat] pairs. Pair with the spatial extension to render cells as GeoJSON or any other format DuckDB-spatial supports — see the Cookbook for the exact polygon-construction pattern. The closed_ring variant repeats the first vertex at the end so the result is directly usable as a polygon ring; the segments overload interpolates additional points along each edge for smoother rendering on curved projections.
Cell IDs as hex strings
A5 IDs round-trip through the canonical 16-character hex form used by the reference A5 library:
SELECT a5_u64_to_hex(a5_lonlat_to_cell(-74.0060, 40.7128, 10));
-- e.g. '2607000000000000'
SELECT a5_hex_to_u64('2607000000000000');
-- the corresponding UBIGINT
Use a5_u64_to_hex / a5_hex_to_u64 when sharing cell IDs with non-DuckDB code (the upstream JS / TypeScript library expects hex strings; integer storage is more compact inside DuckDB).
Sibling extensions
For other geospatial / spatial-key work in DuckDB:
geosilo— geographic data utilities, complementary to A5 when you need richer spatial primitives alongside cell-based indexing.lindel— space-filling curves (Hilbert, Morton). A different approach to the same “flatten 2D into a sortable 1D key” problem. Use space-filling curves when you want neighbor locality on a sorted column; use A5 when you want explicit equal-area cell membership.
Reference
Extension Contents
Quick reference to all available functions and settings organized by category.
| Name | Description | |
|---|---|---|
| Cell Properties Inspect a specific cell — its area in square meters, its resolution level, and the polygon vertices that form its boundary. Useful for filtering, validation, and rendering cells on a map. | ||
| a5_cell_area() | Returns the area in square meters of an A5 cell at the specified resolution level | |
| a5_cell_to_boundary() | Returns the boundary vertices of an A5 cell as a closed ring of [lon, lat] points | |
| a5_get_resolution() | Returns the resolution level (0-30) of an A5 cell | |
| Coordinate Conversion Translate between geographic coordinates (longitude/latitude), spherical coordinates, and A5 cell IDs in their numeric or hex string forms. Use these as your entry and exit points for indexing data into A5. | ||
| a5_cell_to_lonlat() | Returns the center point [longitude, latitude] of an A5 cell | |
| a5_cell_to_spherical() | Returns the spherical coordinates [theta, phi] in radians of an A5 cell center | |
| a5_hex_to_u64() | Converts an A5 hex string representation to a UBIGINT cell ID | |
| a5_lonlat_to_cell() | Converts a longitude/latitude coordinate to an A5 cell at the specified resolution | |
| a5_u64_to_hex() | Converts a UBIGINT A5 cell ID to its hex string representation | |
| Hierarchy Navigate up and down A5's 31-level resolution hierarchy. Roll cells up to a coarser parent for aggregation, expand to children for fine-grained analysis, or compact a set of sibling cells back into their parent. | ||
| a5_cell_to_children() | Returns the immediate child A5 cells (one resolution finer) | |
| a5_cell_to_parent() | Returns the parent A5 cell at the specified coarser resolution | |
| a5_compact() | Compacts a list of A5 cells by merging complete sets of sibling cells into parent cells | |
| a5_get_num_children() | Returns the number of child cells at child_resolution that fit within a cell at parent_resolution | |
| a5_uncompact() | Expands a compacted list of A5 cells to the specified target resolution | |
| Traversal Find cells in a neighborhood: edge- or vertex-adjacent grid disks for fixed-step traversal, or all cells within a metric radius. Use these for proximity queries and spatial joins. | ||
| a5_grid_disk() | Returns all A5 cells within k edge-steps of the given cell (edge adjacency) | |
| a5_grid_disk_vertex() | Returns all A5 cells within k vertex-steps of the given cell (vertex adjacency) | |
| a5_spherical_cap() | Returns all A5 cells within the specified radius (in meters) of the given cell | |
| Utilities General-purpose helpers — total cell count at a resolution, the 12 base cells covering the globe at resolution 0, and parent/child cell ratios. | ||
| a5_get_num_cells() | Returns the total number of A5 cells at the specified resolution level (0-30) | |
| a5_get_res0_cells() | Returns all 12 resolution 0 (root) A5 cells covering the entire globe | |
API Reference
Function Documentation
Detailed documentation for each function including signatures, parameters, and examples.
a5_cell_area
Signature
Parameters (Positional)
| Parameter | Type | Mode | Description |
|---|---|---|---|
resolution | INTEGER | Positional |
Returns
Description
Returns the area in square meters of an A5 cell at the specified resolution level
Examples
SELECT a5_cell_area(10); Output
| a5_cell_area(10) |
|---|
| 32429099.068923894 |
Related Functions
a5_cell_to_boundary
Signature
Parameters (Positional)
| Parameter | Type | Mode | Description |
|---|---|---|---|
cell | UBIGINT | Positional |
Returns
Description
Returns the boundary vertices of an A5 cell as a closed ring of [lon, lat] points
Examples
SELECT a5_cell_to_boundary(a5_lonlat_to_cell(-122.4, 37.8, 5)); Output
| a5_cell_to_boundary(a5_lonlat_to_cell(-122.4, 37.8, 5)) |
|---|
| [(-123.91841441068892, 37.07072636323091), (-123.07208493364051, 36.93440251226853), (-122.23003392974138, 36.7923909949803), (-121.39776711864215, 36.97396218211518), (-120.56172281384751, 37.15058817528356), (-120.90288444799263, 37.72760911298699), (-121.2491402184125, 38.30267874469805), (-122.1294038096313, 38.23172342470604), (-123.00840835218182, 38.15508109275353), (-123.46729324681684, 37.61361779528323), (-123.91841441068892, 37.07072636323091)] |
Related Functions
a5_cell_to_children
Signature
Parameters (Positional)
| Parameter | Type | Mode | Description |
|---|---|---|---|
cell | UBIGINT | Positional |
Returns
Description
Returns the immediate child A5 cells (one resolution finer)
Examples
SELECT a5_cell_to_children(a5_lonlat_to_cell(-122.4, 37.8, 5)); Output
| a5_cell_to_children(a5_lonlat_to_cell(-122.4, 37.8, 5)) |
|---|
| [1936688577257668608, 1936970052234379264, 1937251527211089920, 1937533002187800576] |
Related Functions
a5_cell_to_lonlat
Signature
Parameters (Positional)
| Parameter | Type | Mode | Description |
|---|---|---|---|
cell | UBIGINT | Positional |
Returns
Description
Returns the center point [longitude, latitude] of an A5 cell
Examples
SELECT a5_cell_to_lonlat(a5_lonlat_to_cell(-122.4, 37.8, 10)); Output
| a5_cell_to_lonlat(a5_lonlat_to_cell(-122.4, 37.8, 10)) |
|---|
| (-122.37432272230852, 37.78289994807168) |
Related Functions
- a5_lonlat_to_cell() — Converts a longitude/latitude coordinate to an A5 cell at the specified resolution
- a5_cell_to_spherical() — Returns the spherical coordinates [theta, phi] in radians of an A5 cell center
- a5_cell_to_boundary() — Returns the boundary vertices of an A5 cell as a closed ring of [lon, lat] points
a5_cell_to_parent
Signature
Parameters (Positional)
| Parameter | Type | Mode | Description |
|---|---|---|---|
cell | UBIGINT | Positional | |
parent_resolution | INTEGER | Positional |
Returns
Description
Returns the parent A5 cell at the specified coarser resolution
Examples
SELECT a5_cell_to_parent(a5_lonlat_to_cell(-122.4, 37.8, 10), 5); Output
| a5_cell_to_parent(a5_lonlat_to_cell(-122.4, 37.8, 10), 5) |
|---|
| 1937110789722734592 |
Related Functions
- a5_cell_to_children() — Returns the immediate child A5 cells (one resolution finer)
- a5_get_num_children() — Returns the number of child cells at child_resolution that fit within a cell at parent_resolution
- a5_compact() — Compacts a list of A5 cells by merging complete sets of sibling cells into parent cells
a5_cell_to_spherical
Signature
Parameters (Positional)
| Parameter | Type | Mode | Description |
|---|---|---|---|
cell | UBIGINT | Positional |
Returns
Description
Returns the spherical coordinates [theta, phi] in radians of an A5 cell center
Examples
SELECT a5_cell_to_spherical(a5_lonlat_to_cell(-122.4, 37.8, 10)); Output
| a5_cell_to_spherical(a5_lonlat_to_cell(-122.4, 37.8, 10)) |
|---|
| (-0.5126786470476677, 0.913527819271573) |
Related Functions
a5_compact
Signature
Parameters (Positional)
| Parameter | Type | Mode | Description |
|---|---|---|---|
cells | UBIGINT[] | Positional |
Returns
Description
Compacts a list of A5 cells by merging complete sets of sibling cells into parent cells
Examples
SELECT a5_compact(a5_cell_to_children(a5_lonlat_to_cell(-122.4, 37.8, 5))); Output
| a5_compact(a5_cell_to_children(a5_lonlat_to_cell(-122.4, 37.8, 5))) |
|---|
| [1937110789722734592] |
Related Functions
a5_get_num_cells
Signature
Parameters (Positional)
| Parameter | Type | Mode | Description |
|---|---|---|---|
resolution | INTEGER | Positional |
Returns
Description
Returns the total number of A5 cells at the specified resolution level (0-30)
Examples
SELECT a5_get_num_cells(5); Output
| a5_get_num_cells(5) |
|---|
| 15360 |
Related Functions
- a5_cell_area() — Returns the area in square meters of an A5 cell at the specified resolution level
- a5_get_num_children() — Returns the number of child cells at child_resolution that fit within a cell at parent_resolution
- a5_get_res0_cells() — Returns all 12 resolution 0 (root) A5 cells covering the entire globe
a5_get_num_children
Signature
Parameters (Positional)
| Parameter | Type | Mode | Description |
|---|---|---|---|
parent_resolution | INTEGER | Positional | |
child_resolution | INTEGER | Positional |
Returns
Description
Returns the number of child cells at child_resolution that fit within a cell at parent_resolution
Examples
SELECT a5_get_num_children(0, 1); Output
| a5_get_num_children(0, 1) |
|---|
| 5 |
Related Functions
a5_get_res0_cells
Signature
Parameters
| Parameter | Type | Mode | Description |
|---|
Returns
Description
Returns all 12 resolution 0 (root) A5 cells covering the entire globe
Examples
SELECT a5_get_res0_cells(); Output
| a5_get_res0_cells() |
|---|
| [144115188075855872, 432345564227567616, 720575940379279360, 1008806316530991104, 1297036692682702848, 1585267068834414592, 1873497444986126336, 2161727821137838080, 2449958197289549824, 2738188573441261568, 3026418949592973312, 3314649325744685056] |
Related Functions
a5_get_resolution
Signature
Parameters (Positional)
| Parameter | Type | Mode | Description |
|---|---|---|---|
cell | UBIGINT | Positional |
Returns
Description
Returns the resolution level (0-30) of an A5 cell
Examples
SELECT a5_get_resolution(a5_lonlat_to_cell(-122.4, 37.8, 10)); Output
| a5_get_resolution(a5_lonlat_to_cell(-122.4, 37.8, 10)) |
|---|
| 10 |
Related Functions
a5_grid_disk
Signature
Parameters (Positional)
| Parameter | Type | Mode | Description |
|---|---|---|---|
cell | UBIGINT | Positional | |
k | INTEGER | Positional |
Returns
Description
Returns all A5 cells within k edge-steps of the given cell (edge adjacency)
Examples
SELECT a5_grid_disk(a5_lonlat_to_cell(-122.4, 37.8, 10), 1); Output
| a5_grid_disk(a5_lonlat_to_cell(-122.4, 37.8, 10), 1) |
|---|
| [1936189948734472192, 1936191048246099968, 1937277365734342656, 1937278465245970432, 1937279564757598208, 1937281763780853760] |
Related Functions
a5_grid_disk_vertex
Signature
Parameters (Positional)
| Parameter | Type | Mode | Description |
|---|---|---|---|
cell | UBIGINT | Positional | |
k | INTEGER | Positional |
Returns
Description
Returns all A5 cells within k vertex-steps of the given cell (vertex adjacency)
Examples
SELECT a5_grid_disk_vertex(a5_lonlat_to_cell(-122.4, 37.8, 10), 1); Output
| a5_grid_disk_vertex(a5_lonlat_to_cell(-122.4, 37.8, 10), 1) |
|---|
| [1936188849222844416, 1936189948734472192, 1936191048246099968, 1936192147757727744, 1937277365734342656, 1937278465245970432, 1937279564757598208, 1937281763780853760] |
Related Functions
a5_hex_to_u64
Signature
Parameters (Positional)
| Parameter | Type | Mode | Description |
|---|---|---|---|
hex | VARCHAR | Positional |
Returns
Description
Converts an A5 hex string representation to a UBIGINT cell ID
Examples
SELECT a5_hex_to_u64('1600000000000000'); Output
| a5_hex_to_u64('1600000000000000') |
|---|
| 1585267068834414592 |
a5_lonlat_to_cell
Signature
Parameters (Positional)
| Parameter | Type | Mode | Description |
|---|---|---|---|
longitude | DOUBLE | Positional | |
latitude | DOUBLE | Positional | |
resolution | INTEGER | Positional |
Returns
Description
Converts a longitude/latitude coordinate to an A5 cell at the specified resolution
Examples
SELECT a5_lonlat_to_cell(-122.4194, 37.7749, 10); Output
| a5_lonlat_to_cell(-122.4194, 37.7749, 10) |
|---|
| 1937278465245970432 |
Related Functions
a5_spherical_cap
Signature
Parameters (Positional)
| Parameter | Type | Mode | Description |
|---|---|---|---|
cell | UBIGINT | Positional | |
radius | DOUBLE | Positional |
Returns
Description
Returns all A5 cells within the specified radius (in meters) of the given cell
Examples
SELECT a5_spherical_cap(a5_lonlat_to_cell(-122.4, 37.8, 10), 1000.0); Output
| a5_spherical_cap(a5_lonlat_to_cell(-122.4, 37.8, 10), 1000.0) |
|---|
| [1937278465245970432] |
Related Functions
a5_u64_to_hex
Signature
Parameters (Positional)
| Parameter | Type | Mode | Description |
|---|---|---|---|
cell | UBIGINT | Positional |
Returns
Description
Converts a UBIGINT A5 cell ID to its hex string representation
Examples
SELECT a5_u64_to_hex(a5_lonlat_to_cell(-122.4, 37.8, 10)); Output
| a5_u64_to_hex(a5_lonlat_to_cell(-122.4, 37.8, 10)) |
|---|
| 1ae2988000000000 |
a5_uncompact
Signature
Parameters (Positional)
| Parameter | Type | Mode | Description |
|---|---|---|---|
cells | UBIGINT[] | Positional | |
target_resolution | INTEGER | Positional |
Returns
Description
Expands a compacted list of A5 cells to the specified target resolution
Examples
SELECT a5_uncompact([a5_lonlat_to_cell(-122.4, 37.8, 5)], 7); Output
| a5_uncompact(main.list_value(a5_lonlat_to_cell(-122.4, 37.8, 5)), 7) |
|---|
| [1936583024141402112, 1936653392885579776, 1936723761629757440, 1936794130373935104, 1936864499118112768, 1936934867862290432, 1937005236606468096, 1937075605350645760, 1937145974094823424, 1937216342839001088, 1937286711583178752, 1937357080327356416, 1937427449071534080, 1937497817815711744, 1937568186559889408, 1937638555304067072] |
Related Functions
Practical Examples
Cookbook
Real-world recipes and patterns for common use cases.
Encode a point as a cell ID
The everyday entry point — convert (lon, lat) to a UBIGINT cell ID at a chosen resolution:
SELECT a5_lonlat_to_cell(-74.0060, 40.7128, 15) AS nyc_cell;
Output
| nyc_cell |
|---|
| 2742821848331845632 |
Higher resolution = smaller cells. See a5_lonlat_to_cell for the full signature and a5_cell_area for the equal-area cell size at any resolution:
SELECT a5_cell_area(15) AS cell_area_m2;
Output
| cell_area_m2 |
|---|
| 31669.04205949599 |
Aggregate point data into cells
The common heatmap pattern — bucket points by cell, then count / sum / aggregate:
SELECT a5_lonlat_to_cell(longitude, latitude, 10) AS cell_id,
COUNT(*) AS point_count,
AVG(amount) AS avg_amount
FROM transactions
GROUP BY cell_id
ORDER BY point_count DESC
LIMIT 100;
Because A5 cells are equal-area at each resolution, the resulting counts are directly comparable across latitudes — no per-cell area normalization needed.
Recover the cell center
Round-trip a cell ID back to (lon, lat) for labeling or display:
SELECT a5_cell_to_lonlat(a5_lonlat_to_cell(-74.0060, 40.7128, 15)) AS center_coords;
Output
| center_coords |
|---|
| [-74.00764805615836, 40.71280225138428] |
For radians instead of degrees, use a5_cell_to_spherical.
Render a cell as a polygon
a5_cell_to_boundary returns the cell’s vertices as [lon, lat] pairs — pair with the spatial extension to produce GeoJSON:
SELECT
ST_AsGeoJSON(
ST_MakePolygon(
ST_MakeLine(
list_transform(
a5_cell_to_boundary(
a5_lonlat_to_cell(-3.7037, 40.41677, 10)
),
x -> ST_Point(x[1], x[2])
)
)
)
) AS geojson;
The cell at resolution 10 covering Madrid (-3.7037, 40.41677):
Roll up to a coarser resolution
Encode once at the finest resolution you’ll need, then re-aggregate at every coarser zoom without re-reading raw points:
-- Encode at resolution 15 once
CREATE TABLE indexed AS
SELECT *, a5_lonlat_to_cell(lon, lat, 15) AS cell_15
FROM raw_points;
-- Roll up to resolution 10
SELECT a5_cell_to_parent(cell_15, 10) AS cell_10,
COUNT(*) AS n
FROM indexed
GROUP BY cell_10;
a5_get_resolution recovers the resolution from a cell ID; a5_get_num_children returns the parent/child cell ratio between two resolutions.
Drill down to children
The inverse of roll-up — get the immediate children, or all descendants at a target resolution:
-- Immediate children (one level finer)
SELECT a5_cell_to_children(a5_lonlat_to_cell(-74.0060, 40.7128, 10)) AS kids;
-- All descendants at resolution 12
SELECT a5_cell_to_children(a5_lonlat_to_cell(-74.0060, 40.7128, 10), 12) AS descendants;
Neighborhood search by cell-step
a5_grid_disk returns all cells within k edge-steps of a center cell — the typical “k-ring” pattern from H3:
SELECT unnest(a5_grid_disk(a5_lonlat_to_cell(-74.0060, 40.7128, 15), 1)) AS neighbor_cell;
a5_grid_disk_vertex is the slightly wider variant that includes vertex-touching cells.
Neighborhood search by metric radius
When you have a real-world distance budget rather than a step count:
-- All cells within 5 km of Times Square at resolution 15
SELECT a5_spherical_cap(a5_lonlat_to_cell(-74.0060, 40.7128, 15), 5000.0) AS nearby_cells;
The typical proximity-query pattern: expand to a coverage set, then probe an indexed cell column.
WITH cells AS (
SELECT unnest(a5_spherical_cap(a5_lonlat_to_cell(-74.0060, 40.7128, 15), 5000.0)) AS c
)
SELECT t.*
FROM transactions t
JOIN cells ON cells.c = t.cell_15;
Compact a coverage set
a5_compact replaces complete groups of sibling cells with their parent — fewer rows to ship, expandable on the consumer side:
SELECT a5_compact(a5_cell_to_children(a5_lonlat_to_cell(-122.4, 37.8, 5))) AS compacted;
Output
| compacted |
|---|
| [1937110789722734592] |
a5_uncompact is the inverse — expand a mixed-resolution set to a uniform target resolution:
SELECT a5_uncompact([a5_lonlat_to_cell(-122.4, 37.8, 5)], 7) AS expanded;
Round-trip cell IDs as hex strings
Useful when sharing cell IDs with the upstream JS / TypeScript A5 library, which uses the canonical 16-character hex form:
SELECT a5_u64_to_hex(a5_lonlat_to_cell(-122.4, 37.8, 10)) AS hex_id;
-- '1ae2988000000000'
SELECT a5_hex_to_u64('1ae2988000000000') AS cell_id;
a5_u64_to_hex and a5_hex_to_u64 are pure conversions — store the UBIGINT form for compactness, hex for interchange.
Spatial join via shared cell ID
Encode both sides at the same resolution and join on the cell ID — much cheaper than ST_Intersects for the “are these in the same neighborhood” question:
WITH events AS (
SELECT *, a5_lonlat_to_cell(lon, lat, 12) AS cell FROM event_points
),
zones AS (
SELECT *, a5_lonlat_to_cell(lon, lat, 12) AS cell FROM zone_centroids
)
SELECT z.zone_name, COUNT(*) AS event_count
FROM events e
JOIN zones z USING (cell)
GROUP BY z.zone_name;
For polygon zones rather than centroids, encode each zone as a covering set (via a5_spherical_cap or by walking children of a parent cell) and UNNEST it before joining.
Inspect the base grid
The 12 root cells covering the entire globe at resolution 0:
SELECT unnest(a5_get_res0_cells()) AS root_cell;
a5_get_num_cells returns the total cell count at any resolution — useful for sizing decisions before encoding a large dataset.
Platform Support
Compatibility
Extension availability may vary by platform and DuckDB version. Check below to ensure this extension supports your environment before installation.
Quick Facts
Platforms
Supported platform architectures
Compiled binary sizes
| Platform | Architecture | Size |
|---|---|---|
| Linux | aarch64 | 3.69 MB |
| Linux | x86_64 | 4.09 MB |
| Linux (musl) | x86_64 | 3.30 MB |
| macOS | Apple Silicon | 2.07 MB |
| macOS | Intel | 2.38 MB |
| Windows | x86_64 | 7.65 MB |
| WASM | eh | 91.7 KB |
| WASM | mvp | 89.1 KB |
| WASM | threads | 89.0 KB |
Gzipped download size from the DuckDB community-extensions registry.
Spatial bucketing with pentagonal cells
Install A5 to index points by uniform-area pentagonal cells inside DuckDB — useful when H3's hex-area variation across latitudes is a problem for your aggregation.