Skip to content

'A5' Discrete Global Grid System

1packages depend
4.7Kdownloads / year
–test coverage
13/13flavors OK

Overview

About

Bindings for the "A5 geospatial index" https://a5geo.org/. 'A5' partitions the Earth's surface into pentagonal cells across 31 resolution levels using an equal-area projection onto a dodecahedron. Provides functions for indexing coordinates to cells, traversing the cell hierarchy, computing cell boundaries, and compacting/uncompacting cell sets. Powered by the 'A5' 'Rust' crate via 'extendr'.

Changes in 1.0.0

Breaking changes

  • a5R now uses the a5 Rust crate 1.0.0, which introduces A5's permanent cell-ID system. The space-filling curve and the order of the dodecahedron faces are new, so **every cell id changes**: an id written by an earlier version of a5R is still a valid cell, but in a different place, and nothing in the id says which version wrote it. Re-index stored data from its coordinates, or convert the ids once with the new a5_migrate().
  • Resolution-1 cells are now pentagons rather than triangles, so their boundaries and centres change. Cells at every other resolution keep their shape and position.
  • Compacted cell sets are now a5_covering vectors, one covering per element. A covering stores its compacted cells followed by a compaction marker recording the resolution they stand for, as in the other A5 libraries, so a5_uncompact() no longer needs a resolution. Coverings print as their resolution, cell count and stored-cell count, work as data frame columns, and compare equal with == when they hold the same cells. Use a5_uncompact() for the cells; as_a5_cell() refuses a covering rather than treat its stored cells, whose outlines do not nest, as a grid.
Show all changes in 1.0.0Hide the rest of the changes in 1.0.0
  • a5_compact(), a5_grid_disk() and a5_spherical_cap() return coverings. a5_grid_disk() and a5_spherical_cap() give one covering per centre cell and lose their simplify argument, which moves to a5_uncompact(). An NA centre gives an NA covering.
  • a5_polygon_to_cells() returns one covering per feature instead of the union of all features, so it can fill a column of an sf data frame. A feature's parts and holes are still combined. A missing geometry gives an NA covering and an empty geometry an empty covering; both used to be errors.
  • a5_linestring_to_cells() is renamed a5_line_string_to_cells(), to match line_string_to_cells() in the other A5 libraries. The old name still works but warns.
  • a5_set_threads() and a5_get_threads() are replaced by a5_threads(), which sets the count when given one and reports it otherwise, as options() and par() do. The old functions still work but warn.
  • a5_grid_disk() loses its vertex argument. Use the new a5_grid_disk_vertex(), which matches grid_disk_vertex() in the other A5 libraries, for vertex-sharing neighbours.
  • Arguments take the names used by the a5 crate and a5-js: resolution becomes parent_resolution in a5_cell_to_parent() and child_resolution in a5_cell_to_children(), and closed becomes closed_ring in a5_cell_to_boundary() and a5_cell_to_boundary_xy(). The old names still work but warn. a5_cell_child() and a5_cell_children_range() also take child_resolution.
  • a5_cell_to_lonlat(), a5_cell_to_boundary(), a5_cell_to_boundary_xy() and a5_cell_to_supercell() error on an id that is not an A5 cell, as the a5 crate and a5R's other functions do. They used to return NA or an empty boundary.
  • a5_line_string_to_cells() gains simplify: with simplify = FALSE it returns an a5_cell_list with one ordered path per feature, for a list column. The default flat form concatenates the paths, so a cell crossed by several features now appears once per feature rather than once overall; within a feature, each cell still appears once. Missing and empty geometries contribute no cells instead of an error.
  • a5_uncompact() takes coverings, and its resolution argument is optional: by default each covering expands to its own resolution. It refuses to build more than .Machine$integer.max cells.
  • a5_is_valid() now checks the index encoding. It used to return TRUE for any non-missing a5_cell and for any hex string that parsed, whether or not the value was a cell. Hex strings are read as a5_cell() reads them, so unpadded ids, as the a5 crate writes them, are accepted.
  • a5_compact()'s first argument is now cell, as in other functions that take cells, rather than cells.

New features

  • a5_polygon_to_cells() accepts containment = "center" as well as "centre", and a5_spherical_cap() gains units, taking the radius in any length unit or as a units object.
  • Errors have class a5R_error and a subclass naming what went wrong, such as a5R_error_resolution or a5R_error_geometry, so code can handle them without matching messages. See ?a5_errors.
  • New a5_info() gives the a5R, bundled a5 crate, A5 index and C API versions and the thread count, as a list that prints as a summary. The same summary is shown on one line when a5R is attached. Record its index with stored cells.
  • a5R exports a C API (a5R.h, see ?a5_c_api) for packages that convert between 64-bit ids and a5_cell vectors in compiled code, so they need not rely on how a5_cell stores its data.
  • New a5_bbox() gives the longitude/latitude box of polygons as A5 sees them, with great-circle edges that can bow beyond the vertices' latitudes.
  • New a5_covering_to_supercells() gives the coarse cells containing the centres of a covering's cells without building them. A covering of few fine cells takes each one's supercell directly, so converting many small coverings, such as one per feature, costs microseconds each.
  • with(a5_threads(n), expr) runs expr on n threads and restores the previous count afterwards, as with(mirai::daemons(n), expr) does. a5_threads(n) returns the previous count as an a5_threads object to make this work.
  • New a5_to_arrow_table() and a5_from_arrow_table() convert data frames with cell and covering columns to and from Arrow tables, recording the index version under the schema metadata key a5_index and refusing tables from another index.
  • New a5_migrate() converts ids from a5R before 1.0 to the current index.
  • New as_a5_covering() builds coverings from cells, cell lists or raw 64-bit values written by other A5 libraries, sorting and compacting them; a5_covering_resolution() returns the resolution each covering stands for. a5_is_valid() checks covering structure.
  • New covering operations, computed on the compacted cells without expanding them: a5_union(), a5_intersect() and a5_difference() combine coverings element by element, and a5_union(x) merges every covering in x; a5_contains() tests cells against coverings (a point-in-polygon test at the covering's resolution) and a5_overlaps() tests pairs of coverings; a5_count() and a5_area() give the number of cells and the area each covering stands for. Combining coverings at different resolutions is an error, as upstream.
  • New a5_covering_stored() returns the compacted cells a covering stores, without the compaction marker, for index range queries with a5_cell_children_range().
  • New a5_covering_to_arrow() and a5_covering_from_arrow() convert coverings to and from Arrow list<uint64> with the compaction marker, the layout DuckDB, Python and JavaScript A5 code read.
  • New a5_is_valid_cell() checks that values are A5 cells, mirroring the upstream is_valid_cell. a5_is_valid() becomes a generic that also accepts an a5_cell_list, returning one value per element.
  • New a5_cell_to_boundary_xy() returns the boundary vertices of many cells as flat x and y vectors with per-cell lengths, ready for a single grid::polygonGrob(). It skips building a geometry per cell, so it is about 1.5 times faster than a5_cell_to_boundary() followed by wk::wk_coords() on one thread, 3 times faster on four, and allocates an eighth of the memory.

Other changes

  • As in the other A5 libraries, a5_line_string_to_cells() gives the cell containing a single waypoint, and a5_polygon_to_cells() gives an empty covering for an outer ring of fewer than 3 distinct vertices and ignores such holes. Both used to error.
  • a5_cell_to_parent() accepts resolution = -1, giving the world cell.
  • The a5 crate 1.0.0 fixes two wrong results. a5_polygon_to_cells() with containment = "centre" no longer returns a whole quintant of cells for thin sliver polygons, and a5_lonlat_to_cell() no longer returns a cell on the wrong dodecahedron face when the previous point fell near the centre of another face.
  • Functions returning many cells write them straight into R's vectors, in parallel when threads are enabled, instead of staging them first: a5_uncompact() is about 2.5 times faster on millions of cells, and a5_cell_to_lonlat() and a5_cell_to_parent() up to a third faster, with lower peak memory.
  • a5_lonlat_to_cell() returns NA for NaN and infinite coordinates, which used to give valid cells, and errors on latitudes outside \[-90, 90\], which used to wrap onto plausible but wrong cells.
  • a5_grid_disk(), a5_grid_disk_vertex() and a5_spherical_cap() return the world cell's covering for the world cell, which is its own disk and cap. They used to fail with a Rust panic.
  • Forked workers (parallel::mclapply(), future::multicore) no longer hang after a5_threads() sets more than one thread: they inherited the thread pool but not its threads. a5R now runs on one thread in a forked worker until the worker sets its own count.
  • A panic inside the a5 crate during a multi-threaded call no longer leaves every later parallel call, and a5_threads(), failing until R restarts. a5_threads() rejects counts below 1 and NA, and keeps the previous count if the thread pool cannot be built.
  • a5_cell() accepts ids of fewer than 16 hex digits, as written by a5-js and DuckDB, and a 0x prefix. A string that is not a hex id is now an error instead of a silent NA.
  • NA cells are missing to vctrs: == against NA gives NA, and sorting puts NA last instead of among the resolution-30 cells.
  • as_a5_covering() treats NULL list elements as missing coverings, so null coverings read back from Arrow stay NA instead of becoming empty.
  • a5_polygon_to_cells() and a5_line_string_to_cells() error on sf and terra input in a projected CRS, and on coordinates outside the longitude/latitude range. A ring that crosses itself, such as a bow tie, is an error: the a5 crate filled it inconsistently, flooding whole quintants and running out of memory at fine resolutions.
  • a5_grid_disk() rejects a negative or NA k, and a5_spherical_cap() a negative or non-finite radius.
  • a5_uncompact()'s size guard counts the 5 children of a resolution-0 cell, so it no longer lets through requests up to 25% over the limit.
  • Scalar resolution arguments reject NA with a clear error, and a5_get_num_children() errors when the child resolution is coarser than the parent instead of returning 0.
  • a5_cell_from_arrow() and a5_covering_from_arrow() give a clear error for input that is not an Arrow array.
  • a5_cell_children_range() and a5_cell_child() reach resolution 29 from any cell, as documented, instead of erroring beyond 20 levels.
  • The world cell's boundary is POLYGON EMPTY instead of an invalid polygon.
  • a5_cell_to_parent() errors when resolution is finer than a cell's resolution, instead of returning a descendant.
  • a5_cell_to_boundary() builds its output faster, with identical results: on a million cells, WKB takes about 25% less time on one thread and half the time on four, and WKT about 20% less.
  • a5_cell_distance() from one cell to many (or many to one) finds the single cell's centroid once instead of once per pair, with identical results: on a million cells it is about twice as fast for "haversine" and "rhumb" and a quarter faster for "geodesic".
  • a5_cell_to_boundary() rejects a segments value below 1 or NA; a negative value used to hang. Its documentation now gives the default, max(1, 2^(6 - resolution)) segments per edge, and how far a single segment strays from the curved cell edge at each resolution.
  • a5_polygon_to_cells() handles wk::rct() boxes that previously gave wrong results. A box covering the whole globe gives every cell instead of none, and a box reaching from pole to pole keeps its west and east edges on their meridians. Any other box spanning 180 degrees of longitude or more is an error, because its great-circle edges would run the short way round. The documentation now states that polygon edges are great circles.
  • New vignette("coverings") on building, combining, testing and measuring coverings. vignette("arrow-parquet") covers storing coverings and migrating data written before 1.0, and vignette("spatial-hierarchy") is rewritten for supercells and subcells.
  • a5_cell_to_arrow(), a5_cell_from_arrow(), a5_covering_to_arrow() and a5_covering_from_arrow() run in Rust and pass one contiguous uint64 buffer through the Arrow C data interface, instead of building an R object per cell. On 10 million cells, writing to Arrow is about 90 times faster and reading about 40 times faster. a5_covering_from_arrow() now refuses a covering that holds a null cell; it used to drop the null silently.
  • a5_cell_child() and a5_cell_children_range() follow the new order of a5_cell_to_children() (children are now in ascending id order).
  • a5_get_num_cells() is correct at high resolutions (a5 0.10.1).
  • a5_polygon_to_cells() merges a feature's parts with the upstream covering union, without expanding each part to full resolution first.
  • New a5_cell_to_supercell() and a5_cell_to_subcells() move between resolutions by cell centre rather than by index, wrapping the upstream cell_to_supercell() and cell_to_subcells(). The A5 index hierarchy is not spatially nested: for about half of cells one resolution apart, and about 35% at four or more apart, the index parent is not the coarse cell containing the fine cell's centre. Supercells and subcells form an exact partition, so aggregating fine cells to a coarser grid counts each fine cell once, inside the coarse cell it lies in. Subcells are returned as one covering per cell. a5_cell_to_parent() and a5_cell_to_children() are unchanged and now document the difference.
  • Missing values filled in by vctrs are now NA, not the world cell (#27). Combining with NA, subsetting with an NA index, assigning NA, vctrs::vec_init(), and unmatched rows from merge(all = TRUE), vctrs::vec_rbind() or dplyr joins used to produce id 0, a valid cell. The top byte of each id is now stored XOR 0xFC, so a filled-in gap decodes to the NA sentinel, at no measurable cost. This changes the stored byte layout: a5_cell objects saved with saveRDS(), save() or qs by earlier versions do not read correctly. Re-create them from hex strings or Arrow, which store the true id.
  • Resolution-30 cells whose id starts with 0xFC (about 0.3% of them, in quintants 31, 39 and 41) are no longer treated as NA. The NA check now compares the full id rather than its top byte.
  • The benchmark suite now mirrors the TypeScript, Python and Rust A5 ports (same case names, same deterministic inputs) and runs in CI on every pull request, failing on regressions above 15%. The old cross-language comparison scripts are gone.
  • a5_cell_to_children(), a5_grid_disk() and a5_spherical_cap() are vectorised over their cell argument and gain a simplify argument. The default simplify = TRUE returns one flat a5_cell vector, so single-cell calls are unchanged; simplify = FALSE returns a vctrs::list_of() with one element per input, for list columns. An NA cell now contributes no cells instead of raising an error.
  • New a5_cell_list class for those lists, with an unlist() method that concatenates the elements into one a5_cell vector. Base unlist() on a plain list of a5_cell vectors returns a meaningless raw vector; as_a5_cell_list() wraps such a list so unlist() works.
  • Base R compatibility for a5_cell: match() and %in% are about ten times faster via an exact mtfrm() method instead of hex conversion; x[i] <- value past the end and length(x) <- n grow the vector with NA, so rbind() on data frames with a5_cell columns works. split() and tapply() with an a5_cell grouping vector cannot be intercepted and give wr

The NEWS section is longer than shown here.

From NEWS.md in the release

Install

Health

CRAN checks
13OK
Slowest check: 6.6 min · r-oldrel-windows-x86_64
Status changes
  • OK13 OK2026-09-19
  • ERROR12 OK, 1 ERROR2026-09-17
  • OK13 OK2026-09-08
  • ERROR12 OK, 1 ERROR2026-09-07
  • OK13 OK2026-08-05
Show 8 earlier changesHide 8 earlier changes
  • NOTE12 OK, 1 NOTE2026-08-01
  • OK13 OK2026-07-30
  • NOTE12 OK, 1 NOTE2026-07-29
  • OK13 OK2026-05-02
  • NOTE11 OK, 1 NOTE2026-04-25
  • OK14 OK2026-04-16
  • NOTE13 OK, 1 NOTE2026-03-28
  • OK5 OK2026-03-17
Advisories

none known

Documentation

In release 1.0.0
READMEYes · 389 wordsVignettesYes · 7 · evaluation depends on the environmentNEWSYes · 100% structured · mentions 1.0.0Citation fileNoWebsiteYes · pkgdown
Help pages
39 (4 internal)includes a package overview page
Examples that run
100%34 run in a plain check (3 of them under a condition)
Exported functions with examples
94%
Documented parameters
92%
Return-value docs
100%
Help pages citing references
0%

Downloads

4.7K
CRAN downloads in the past year
Rank #4,883 · ~13/day · ~390/mo
Daily download trend is not available in this view yet.
65030 days
1.7K90 days
4.7K1 year
Compare downloads with other packages →
Also on261 r2u

Repository

30Stars
0Forks
1Open issues
0Open PRs
6Releases
373Commits
3Commit identities
License file reads as Apache-2.0 · 373 commits · Last activity 2026-10-19 · 0% stars, 30d
Responsiveness
How these are sampled
PRs merged (all time)
100%
Median days to close, 50 most recently updated closed issues
2
Median days to close, 50 most recently updated closed PRs
0

Stars over time

2026-02-17 · 22026-08-07 · 30
PracticesBeta

9 development-tooling and community-health practices detected across 4 families in the upstream repository

Checks run against github.com/belian-earth/a5r on 2026-10-18.

Continuous integration (2)
GitHub ActionsR CMD check workflow

The R CMD check workflow runs on Linux, macOS and Windows, and on R-devel

Docs source (4)
README.Rmdpkgdown

GitHub Pages site last deployed 2026-10-16 · The repository has a site deploy workflow

pkgdown site last built 2026-10-13, before release 1.0.0 (2026-10-19)

Coverage (1)
Coverage reporting
CRAN release process (2)
cran-comments.mdrevdep/
How this is detected·Detection ruleset v3 (2026-09-26)
AI coding toolsBeta

This repository shows signs of AI coding tools.

  • Claude Codefirst seen 2026-02-16
    Named in .Rbuildignore: .claude
    Named in .gitignore: .claude
    Commits: 2 by its own account · at least 17 crediting it in the message
Models named in its commits
  • Claude Fable 5.115 commits · Sep 2026
  • Claude Opus 4.62 commits · Mar 2026
Counts are commits, not lines of code. Commits by a tool's accounts count once across all of its accounts. Commits crediting it are the largest count among its credit lines, a minimum.How this is detected·Detection ruleset v4 (updated 2026-09-27)

Dependencies

Declared dependencies
15 external dependencies (excludes base and recommended)

Installing a5R adds 8 packages (7 with compiled code, 5.7 MB of source tarballs). Heaviest: Rcpp 1.8 MB, vctrs 1.0 MB, wk 1.0 MB.

Depends (1)
R >= 4.2
LinkingTo (0)
none
Enhances (0)
none
Reverse dependencies
1direct
0indirect
Imports 1
Depends, Imports or LinkingTo, most downloaded first

People & History

People (4)
Author, Maintainer since 0.2.0 (2026)
Contributor, Copyright holder · added in 1.0.0
Author of the bundled 'a5' Rust crate
Copyright holder · added in 1.0.0
see LICENSE.note
Copyright holder
Releases (6)

Pick two releases to compare their code metrics.

  • 1.0.0Latest
    record
    2026-10-19 · current release · diff ↗ ·
    added 29 exports
    a5_area, a5_bbox, a5_cell_child, a5_cell_children_range, a5_cell_to_boundary_xy, a5_cell_to_subcells, a5_cell_to_supercell, a5_contains, a5_count, a5_covering_from_arrow, a5_covering_resolution, a5_covering_stored, a5_covering_to_arrow, a5_covering_to_supercells, a5_difference, a5_from_arrow_table, a5_grid_disk_vertex, a5_info, a5_intersect, a5_is_valid_cell, a5_line_string_to_cells, a5_migrate, a5_overlaps, a5_threads, a5_to_arrow_table, a5_union, as_a5_cell_list, as_a5_covering, is_a5_covering
  • 0.6.0
    record
    2026-09-08 · diff ↗ ·
    added 1 export
    a5_cell_edge_length_avg
  • 0.5.0
    record
    2026-05-14 · diff ↗ ·
    removed 1 export
    a5_grid
  • R
    R 4.6.0 released · 2026-04-24
  • 0.4.0
    record
    2026-03-26 · diff ↗ ·
    added 2 exports
    a5_linestring_to_cells, a5_polygon_to_cells
  • 0.3.1
    record
    2026-03-26 · diff ↗ ·
    removed 1 export
    a5_is_cell
    added 5 exports
    a5_cell_from_arrow, a5_cell_to_arrow, a5_hex_to_u64, a5_is_valid, a5_u64_to_hex
  • 0.2.0
    record
    2026-03-16
  • R
    R 4.5.0 released · 2025-04-11

Package metadata

First published
2026-03-16
Total releases
6 / 1 yrs
License
Apache License (>= 2)
Minimum R
≥ 4.2
Download size
2.0 MB

Cite

Cite this package

Run in R for the authors' preferred citation:

citation("a5R")
Graham, H., Authors of the dependency Rust crates, belian.earth, & Palmer, F. (2026). a5R: 'A5' Discrete Global Grid System (Version 1.0.0) [Computer software]. https://doi.org/10.32614/CRAN.package.a5R

This package has no citation file, so citation() returns this entry built from DESCRIPTION.

Cite the R Observatory

For a number measured here: a download total, a coverage figure, an archival date.

APA

Balamuta, J. J. (2026). R Observatory: Metrics for a5R version 1.0.0 [Data set]. HJJB, LLC. Data release v2026-10-21. https://doi.org/10.5281/zenodo.21843040

From data release v2026-10-21, which the citation names so these numbers can be found later. More on citing and the projects behind them.

Report a problem with this page →

Privacy choices

These apply to this browser and are stored on this device only. Nothing about your choice is sent to us.

Read the privacy policy