Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion DESCRIPTION
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
Package: hexify
Title: Equal-Area Hex Grids on the 'Snyder' 'ISEA' 'Icosahedron'
Version: 0.7.3.9000
Version: 0.7.4
Authors@R:
person("Gilles", "Colling", , "gilles.colling051@gmail.com", role = c("aut", "cre", "cph"),
comment = c(ORCID = "0000-0003-3070-6066"))
Expand Down
12 changes: 9 additions & 3 deletions NEWS.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
# hexify 0.7.4 (development version)

## New features
# hexify 0.7.4

## Documentation

* Documented the bijective aperture-7 Z7 format, exact round-trip guarantee,
index structure, and the limited pentagon-region difference from DGGRID's
non-injective raw Z7 encoding.

## New features

* Hierarchical navigation now works for mixed aperture `"4/3"` (ISEA43H) grids:
`get_parent()`, `get_children()`, and `cell_to_index()` no longer error on
Expand Down
2 changes: 1 addition & 1 deletion R/grid_helpers.R
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
#' Normalize antimeridian-crossing polygon coordinates
#'
#' Shifts longitudes of a ring's coordinate matrix so an antimeridian-crossing
#' polygon becomes contiguous instead of spanning nearly the full [-180, 180]
#' polygon becomes contiguous instead of spanning nearly the full -180 to 180
#' range. Downstream `sf::st_wrap_dateline()` then splits it correctly for
#' flat-map rendering.
#'
Expand Down
83 changes: 58 additions & 25 deletions R/hexify_index.R
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@

#' Convert cell coordinates to index string
#'
#' Converts DGGRID cell coordinates (face, i, j) to a hierarchical index string.
#' The index type is automatically selected based on aperture unless specified.
#' Converts cell coordinates (`face`, `i`, `j`) to a hierarchical index string.
#' The index type is selected from `aperture` when `index_type = "auto"`.
#'
#' @param face Face/quad number (0-19)
#' @param i I coordinate
Expand All @@ -25,19 +25,30 @@
#' @param aperture Aperture (3, 4, or 7)
#' @param index_type Index encoding: "auto" (default), "z3", "z7", or "zorder"
#'
#' @return Index string (e.g., "051223")
#' @return A character vector of index strings.
#'
#' @details
#' Default index types by aperture:
#' - Aperture 3: Z3 (optimized digit selection)
#' - Aperture 4: Z-order (Morton curve)
#' - Aperture 7: Z7 (hierarchical with Class III handling)
#' - Aperture 7: Z7 (one base-7 child digit per resolution)
#'
#' A Z7 index has the form `BBd1...dr`, where `BB` is the two-digit base
#' cell (00--11), `r` is the resolution, and every child digit is in 0--6.
#' hexify's Z7 encoding is bijective: decoding and re-encoding a valid index
#' returns the same string. It follows DGGRID's Z7 layout for ordinary cells,
#' but retains distinct indices in pentagon regions where DGGRID's encoder can
#' map different cells to the same string.
#'
#' @family hierarchical index
#' @keywords internal
#' @export
#' @examples
#' idx <- hexify_cell_to_index(5, 10, 15, resolution = 3, aperture = 3)
#' @examples
#' idx <- hexify_cell_to_index(5, 10, 15, resolution = 3, aperture = 3)
#'
#' # Aperture-7 indices contain a two-digit base cell and one digit per level
#' z7 <- hexify_cell_to_index(5, 1, 1, resolution = 2, aperture = 7)
#' nchar(z7) == 4L
hexify_cell_to_index <- function(face, i, j, resolution, aperture = 3L,
index_type = c("auto", "z3", "z7", "zorder")) {
index_type <- match.arg(index_type)
Expand All @@ -51,19 +62,26 @@ hexify_cell_to_index <- function(face, i, j, resolution, aperture = 3L,

#' Convert index string to cell coordinates
#'
#' Decodes a hierarchical index string back to cell coordinates.
#' Decodes a hierarchical index string back to its cell coordinates and
#' resolution. For Z7, valid indices round-trip exactly through
#' [hexify_cell_to_index()].
#'
#' @param index Index string
#' @param aperture Aperture (3, 4, or 7)
#' @param index_type Index encoding used. Default "auto" infers from aperture.
#'
#' @return List with face, i, j, and resolution
#' @return A list with `face`, `i`, `j`, and `resolution`.
#'
#' @family hierarchical index
#' @keywords internal
#' @export
#' @examples
#' cell <- hexify_index_to_cell("0012012", aperture = 3)
#' @examples
#' cell <- hexify_index_to_cell("0012012", aperture = 3)
#'
#' z7_cell <- hexify_index_to_cell("110001", aperture = 7)
#' hexify_cell_to_index(z7_cell$face, z7_cell$i, z7_cell$j,
#' z7_cell$resolution, aperture = 7
#' )
hexify_index_to_cell <- function(index, aperture = 3L,
index_type = c("auto", "z3", "z7", "zorder")) {
index_type <- match.arg(index_type)
Expand All @@ -73,21 +91,25 @@ hexify_index_to_cell <- function(index, aperture = 3L,

#' Convert longitude/latitude to index string
#'
#' Main entry point for geocoding points to grid cells.
#' Projects geographic coordinates to grid cells and returns their hierarchical
#' index strings. Inputs are vectorized over `lon` and `lat`.
#'
#' @param lon Longitude in degrees
#' @param lat Latitude in degrees
#' @param resolution Resolution level
#' @param aperture Aperture (3, 4, or 7)
#' @param index_type Index encoding: "auto" (default), "z3", "z7", or "zorder"
#'
#' @return Index string
#' @return A character vector of index strings.
#'
#' @family hierarchical index
#' @keywords internal
#' @export
#' @examples
#' idx <- hexify_lonlat_to_index(16.37, 48.21, resolution = 5, aperture = 3)
#' @examples
#' idx <- hexify_lonlat_to_index(16.37, 48.21, resolution = 5, aperture = 3)
#' idx7 <- hexify_lonlat_to_index(16.37, 48.21,
#' resolution = 4, aperture = 7
#' )
hexify_lonlat_to_index <- function(lon, lat, resolution, aperture = 3L,
index_type = c("auto", "z3", "z7", "zorder")) {
index_type <- match.arg(index_type)
Expand All @@ -99,13 +121,15 @@ hexify_lonlat_to_index <- function(lon, lat, resolution, aperture = 3L,

#' Convert index string to longitude/latitude
#'
#' Returns the cell center coordinates for a given index.
#' Returns the geographic coordinates of an indexed cell's center. This is a
#' cell-level inverse of [hexify_lonlat_to_index()]: the returned point is the
#' center, not necessarily the original input point.
#'
#' @param index Index string
#' @param aperture Aperture (3, 4, or 7)
#' @param index_type Index encoding. Default "auto" infers from aperture.
#'
#' @return Named numeric vector with lon and lat in degrees
#' @return A named numeric vector with `lon` and `lat` in degrees.
#'
#' @family hierarchical index
#' @keywords internal
Expand Down Expand Up @@ -292,22 +316,31 @@ hexify_cell_id_to_quad_ij <- function(cell_id, resolution, aperture) {

#' Get canonical form of Z7 index
#'
#' For Z7 indices that form cycles during decode/encode, returns the
#' lexicographically smallest index in the cycle. Provides stable
#' unique identifiers for aperture 7 grids.
#' Decodes and re-encodes a Z7 index until it reaches a stable form. Current Z7
#' indices are bijective, so every valid index is already canonical and this
#' function normally returns its input unchanged. It remains available for
#' validating or normalizing indices created by older hexify versions.
#'
#' @param index Z7 index string
#' @param max_iterations Maximum iterations for cycle detection (default 128)
#' @param index A length-one Z7 index string.
#' @param max_iterations Maximum number of decode/encode iterations. This is a
#' safety bound for legacy indices; the default is 128.
#'
#' @return Canonical form (lexicographically smallest in cycle)
#' @return A length-one character string containing the stable index.
#'
#' @family hierarchical index
#' @keywords internal
#' @export
#' @examples
#' # These all return the same canonical form
#' hexify_z7_canonical("110001")
#' hexify_z7_canonical("110002")
#' # Valid Z7 indices are stable
#' hexify_z7_canonical("110001")
#'
#' cell <- hexify_index_to_cell("110001", aperture = 7)
#' identical(
#' hexify_cell_to_index(cell$face, cell$i, cell$j,
#' cell$resolution, aperture = 7
#' ),
#' "110001"
#' )
hexify_z7_canonical <- function(index, max_iterations = 128L) {
cpp_z7_canonical_form(as.character(index), as.integer(max_iterations))
}
Expand Down
19 changes: 15 additions & 4 deletions man/hexify_cell_to_index.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

11 changes: 9 additions & 2 deletions man/hexify_index_to_cell.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 4 additions & 2 deletions man/hexify_index_to_lonlat.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 6 additions & 2 deletions man/hexify_lonlat_to_index.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

25 changes: 17 additions & 8 deletions man/hexify_z7_canonical.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions src/index_z7.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -492,8 +492,8 @@ std::string canonical_form(const std::string& z7_index, int max_iterations) {
long long i, j;
int res = current.length() - 2;

decode(current, res, quadNum, i, j);
std::string next = encode(quadNum, i, j, res);
decode_bijective(current, res, quadNum, i, j);
std::string next = encode_bijective(quadNum, i, j, res);

// Check for fixed point
if (next == current) {
Expand Down
Binary file modified tests/testthat/fixtures/test_cache.rds
Binary file not shown.
2 changes: 1 addition & 1 deletion tests/testthat/generate_cache.R
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ coords7 <- hexify_cell_to_lonlat(cells7, resolution = 3, aperture = 7)
cache$cells_ap7 <- list(cells = cells7, lon = coords7$lon_deg, lat = coords7$lat_deg)

# Projection forward
proj <- cpp_snyder_forward(10, 45)
proj <- hexify:::cpp_snyder_forward(10, 45)
cache$proj_forward <- as.list(proj)

# Face centers
Expand Down
Loading
Loading