A high-performance DuckDB extension that provides functions for the A5 global geospatial index - a millimeter-accurate, equal-area indexing system for geospatial data.
A5 is an innovative geospatial index that partitions the world into pentagonal cells based on a geodesic grid. Key features include:
- π Global Coverage: Seamless indexing from global to millimeter scales
- π Equal Area: All cells at the same resolution level have identical area (OGC compliant)
- π 31 Resolution Levels: From world-spanning cells to sub-30mmΒ² precision
- β‘ Fast Spatial Operations: Optimized for aggregation, filtering, and spatial joins
Group point data spatially to understand distributions:
-- Analyze restaurant density by A5 cells
SELECT a5_lonlat_to_cell(longitude, latitude, 15) as cell_id, COUNT(*) as restaurant_count
FROM restaurants
GROUP BY cell_id
ORDER BY restaurant_count DESC;The a5 extension is available as a DuckDB Community Extension:
INSTALL a5 FROM community;
LOAD a5;-- Get the A5 cell for a specific location (latitude, longitude, resolution)
SELECT a5_lonlat_to_cell(-74.0060, 40.7128, 15) as nyc_cell; -- Times Square
βββββββββββββββββββββββ
β nyc_cell β
β uint64 β
βββββββββββββββββββββββ€
β 2742821848331845632 β
βββββββββββββββββββββββ
-- Find the area of that cell in square meters
SELECT a5_cell_area(15) as cell_area_m2;
βββββββββββββββββββββ
β cell_area_m2 β
β double β
βββββββββββββββββββββ€
β 31669.04205949599 β
βββββββββββββββββββββ
-- Get the center coordinates of a cell
SELECT a5_cell_to_lonlat(a5_lonlat_to_cell(-74.0060, 40.7128, 15)) as center_coords;
βββββββββββββββββββββββββββββββββββββββββββ
β center_coords β
β double[2] β
βββββββββββββββββββββββββββββββββββββββββββ€
β [-74.00764805615836, 40.71280225138428] β
βββββββββββββββββββββββββββββββββββββββββββ
-- Find parent cell at lower resolution
SELECT a5_cell_to_parent(a5_lonlat_to_cell(-74.0060, 40.7128, 15), 10) as parent_cell;
βββββββββββββββββββββββ
β parent_cell β
β uint64 β
βββββββββββββββββββββββ€
β 2742821365684895744 β
βββββββββββββββββββββββ
-- Get all children cells at higher resolution
SELECT a5_cell_to_children(a5_lonlat_to_cell(-74.0060, 40.7128, 10), 11) as child_cells;
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β child_cells β
β uint64[] β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β [2742820953368035328, 2742821228245942272, 2742821503123849216, 2742821778001756160] β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββTo generate a GeoJSON polygon for the A5 cell above use this SQL along with DuckDB's spatial extension:
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 gThis produces:
{
"type":"Polygon",
"coordinates":[
[
[-3.639321611065313,40.44502900567739],
[-3.6973300524360155,40.44427170464865],
[-3.7459288918337563,40.424159040292615],
[-3.70791029038422,40.394201800420205],
[-3.654438659632305,40.4080830654645],
[-3.639321611065313,40.44502900567739]
]
]
}
Visualizing that A5 cell shows:
{
"type":"Polygon",
"coordinates":[
[
[-3.639321611065313,40.44502900567739],
[-3.6973300524360155,40.44427170464865],
[-3.7459288918337563,40.424159040292615],
[-3.70791029038422,40.394201800420205],
[-3.654438659632305,40.4080830654645],
[-3.639321611065313,40.44502900567739]
]
]
}
Returns the A5 cell ID for given coordinates and resolution level.
Parameters:
latitude(DOUBLE): Latitude in decimal degrees (-90 to 90)longitude(DOUBLE): Longitude in decimal degrees (-180 to 180)resolution(INTEGER): Resolution level (0-30, where 0 is coarsest)
Example:
SELECT a5_lonlat_to_cell(-0.1278, 51.5074, 12) as london_cell;
βββββββββββββββββββββββ
β london_cell β
β uint64 β
βββββββββββββββββββββββ€
β 7161033366718906368 β
βββββββββββββββββββββββReturns the area of an A5 cell in the specified resolution in square meters.
Example:
SELECT a5_cell_area(5) as area_m2;
ββββββββββββββββββββββ
β area_m2 β
β double β
ββββββββββββββββββββββ€
β 33207397446.578068 β
ββββββββββββββββββββββReturns the resolution level of an A5 cell.
Example:
SELECT a5_get_resolution(207618739568) as resolution;
ββββββββββββββ
β resolution β
β int32 β
ββββββββββββββ€
β 27 β
ββββββββββββββReturns the parent cell at a coarser resolution level.
Example:
SELECT a5_cell_to_parent(207618739568, 10) as parent_cell;
ββββββββββββββββββββ
β parent_cell β
β uint64 β
ββββββββββββββββββββ€
β 549755813888 β
ββββββββββββββββββββReturns all children cells at a finer resolution level.
Example:
SELECT unnest(a5_cell_to_children(207618739568, 28)) as child_cells;
ββββββββββββββββ
β child_cells β
β uint64 β
ββββββββββββββββ€
β 207618739528 β
β 207618739544 β
β 207618739560 β
β 207618739576 β
ββββββββββββββββReturns the center coordinates [longitude, latitude] of a cell.
Example:
SELECT a5_cell_to_lonlat(207618739568) as center;
βββββββββββββββββββββββββββββββββββββββββββ
β center β
β double[2] β
βββββββββββββββββββββββββββββββββββββββββββ€
β [-129.0078555564143, 52.76769886727584] β
βββββββββββββββββββββββββββββββββββββββββββReturns the boundary vertices of a cell as an array of [latitude, longitude] pairs.
Parameters:
cell_id(UBIGINT): The A5 cellclosed_ring(BOOLEAN): Whether to close the ring by repeating the first point at the end. Defaults to true.segments(INTEGER): Number of segments to use for each edge. If this argument is not supplied or a value is supplied that is <= 0, a resolution-appropriate value will be used.
Examples:
SELECT unnest(a5_cell_to_boundary(207618739568)) as boundary_points;
βββββββββββββββββββββββββββββββββββββββββββββ
β boundary_points β
β double[2] β
βββββββββββββββββββββββββββββββββββββββββββββ€
β [-129.00785542696357, 52.767699205314614] β
β [-129.00785579342767, 52.767698942751544] β
β [-129.0078559316034, 52.76769861890205] β
β [-129.00785542684645, 52.76769862844177] β
β [-129.0078552032305, 52.767698940969176] β
β [-129.00785542696357, 52.767699205314614] β
βββββββββββββββββββββββββββββββββββββββββββββSELECT unnest(a5_cell_to_boundary(207618739568, false, 5)) as boundary_points;
βββββββββββββββββββββββββββββββββββββββββββββ
β boundary_points β
β double[2] β
βββββββββββββββββββββββββββββββββββββββββββββ€
β [-129.00785550025637, 52.767699152801974] β
β [-129.0078555735492, 52.76769910028939] β
β [-129.00785564684202, 52.76769904777675] β
β [-129.00785572013484, 52.767698995264155] β
β [-129.00785579342767, 52.767698942751544] β
β [-129.0078558210628, 52.76769887798164] β
β [-129.00785584869794, 52.767698813211744] β
β [-129.00785587633308, 52.767698748441845] β
β [-129.00785590396822, 52.76769868367194] β
β [-129.0078559316034, 52.76769861890205] β
β [-129.007855830652, 52.76769862081001] β
β [-129.00785572970062, 52.76769862271794] β
β [-129.0078556287492, 52.76769862462589] β
β [-129.00785552779783, 52.76769862653383] β
β [-129.00785542684645, 52.76769862844177] β
β [-129.00785538212327, 52.767698690947256] β
β [-129.00785533740006, 52.76769875345273] β
β [-129.00785529267688, 52.7676988159582] β
β [-129.00785524795367, 52.7676988784637] β
β [-129.0078552032305, 52.767698940969176] β
β [-129.00785524797712, 52.767698993838245] β
β [-129.00785529272372, 52.76769904670734] β
β [-129.00785533747032, 52.76769909957643] β
β [-129.00785538221695, 52.767699152445516] β
β [-129.00785542696357, 52.767699205314614] β
βββββββββββββββββββββββββββββββββββββββββββββ€
β 25 rows β
βββββββββββββββββββββββββββββββββββββββββββββReturns the total number of A5 cells at a given resolution level.
Example:
SELECT a5_get_num_cells(15) as total_cells;
βββββββββββββββββββ
β total_cells β
β uint64 β
βββββββββββββββββββ€
β 16106127360 β
βββββββββββββββββββReturns all 12 base cells at resolution level 0.
Example:
SELECT unnest(a5_get_res0_cells()) as base_cells;
βββββββββββββββββββββββ
β base_cells β
β uint64 β
βββββββββββββββββββββββ€
β 144115188075855872 β
β 432345564227567616 β
β 720575940379279360 β
β 1008806316530991104 β
β 1297036692682702848 β
β 1585267068834414592 β
β 1873497444986126336 β
β 2161727821137838080 β
β 2449958197289549824 β
β 2738188573441261568 β
β 3026418949592973312 β
β 3314649325744685056 β
βββββββββββββββββββββββ€
β 12 rows β
βββββββββββββββββββββββCompacts a set of A5 cells by replacing complete groups of sibling cells with their parent cells.
Example:
SELECT a5_compact([324259173170675712, 396316767208603648, 468374361246531584, 540431955284459520]::ubigint[]) as result;
ββββββββββββββββββββββββ
β result β
β uint64[] β
ββββββββββββββββββββββββ€
β [360287970189639680] β
ββββββββββββββββββββββββExpands a set of A5 cells to a target resolution by generating all descendant cells.
Example:
SELECT a5_uncompact([324259173170675712, 396316767208603648, 468374361246531584, 540431955284459520]::ubigint[], 2) as result;
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β result β
β uint64[] β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β [324259173170675712, 396316767208603648, 468374361246531584, 540431955284459520] β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ| Resolution | Cell Area (approx) | Use Case |
|---|---|---|
| 0-5 | 42M kmΒ² - 33k kmΒ² | Continental/Country analysis |
| 6-10 | 8k kmΒ² - 130 kmΒ² | Regional/State 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 analysis |
| 26-30 | 8 cmΒ² - 0.03 mmΒ² | Precision measurements |
This project is licensed under the MIT License - see the LICENSE file for details.
- A5 Algorithm: This extension utilizes the
a5Rust crate created by felixpalmer - A5 Specification: Based on the A5 geospatial index specification
- DuckDB Community: Built on the excellent DuckDB database system