| Title: | Equal-Area Hex Grids on the 'Snyder' 'ISEA' 'Icosahedron' |
|---|---|
| Description: | Provides functions to build and use hexagonal discrete global grids using the 'Snyder' 'ISEA' projection ('Snyder' 1992 <doi:10.3138/27H7-8K88-4882-1752>) and the 'H3' hierarchical hexagonal system ('Uber' Technologies). Implements the 'ISEA' discrete global grid system ('Sahr', 'White' and 'Kimerling' 2003 <doi:10.1559/152304003100011090>). Includes a fast 'C++' core for 'ISEA' projection and aperture quantization, an included 'H3' v4.4.1 C library for native 'H3' grid operations, and 'sf'/'terra'-compatible R wrappers for grid generation and coordinate assignment. Output is compatible with 'dggridR' for interoperability. |
| Authors: | Gilles Colling [aut, cre, cph] (ORCID: <https://orcid.org/0000-0003-3070-6066>) |
| Maintainer: | Gilles Colling <[email protected]> |
| License: | MIT + file LICENSE |
| Version: | 0.7.1 |
| Built: | 2026-06-08 07:56:20 UTC |
| Source: | https://github.com/gcol33/hexify |
Core icosahedron and 'Snyder' projection helpers.
Maintainer: Gilles Colling [email protected] (ORCID) [copyright holder]
Useful links:
Creates a 'dggridR'-compatible grid specification from a hexify_grid object. The resulting object can be used with 'dggridR' functions that accept a dggs object.
as_dggrid(grid)as_dggrid(grid)
grid |
A hexify_grid object from hexify_grid() |
A list with 'dggridR'-compatible fields:
pole_lon_deg |
Longitude of grid pole (default 11.25) |
pole_lat_deg |
Latitude of grid pole (default 58.28252559) |
azimuth_deg |
Grid azimuth rotation (default 0) |
aperture |
Grid aperture (3, 4, or 7) |
res |
Resolution level |
topology |
Grid topology ("HEXAGON") |
projection |
Map projection ('ISEA') |
precision |
Output decimal precision (default 7) |
Other 'dggridR' compatibility:
dggrid_43h_sequence(),
dggrid_is_compatible(),
from_dggrid()
Converts a HexData object to an sf spatial features object. Can create either point geometries (cell centers) or polygon geometries (cell boundaries).
as_sf(x, geometry = c("point", "polygon"), ...)as_sf(x, geometry = c("point", "polygon"), ...)
x |
A HexData object |
geometry |
Type of geometry: "point" (default) or "polygon" |
... |
Additional arguments (ignored) |
For point geometry, cell centers (cell_cen_lon, cell_cen_lat) are used. For polygon geometry, cell boundaries are computed using the grid specification.
An sf object
df <- data.frame(lon = c(0, 10, 20), lat = c(45, 50, 55)) result <- hexify(df, lon = "lon", lat = "lat", area_km2 = 1000) # Get sf points sf_pts <- as_sf(result) # Get sf polygons sf_poly <- as_sf(result, geometry = "polygon")df <- data.frame(lon = c(0, 10, 20), lat = c(45, 50, 55)) result <- hexify(df, lon = "lon", lat = "lat", area_km2 = 1000) # Get sf points sf_pts <- as_sf(result) # Get sf polygons sf_poly <- as_sf(result, geometry = "polygon")
Convert HexData to tibble
as_tibble.HexData(x, ...)as_tibble.HexData(x, ...)
x |
A HexData object |
... |
Additional arguments (ignored) |
A tibble
Returns the area of each cell in square kilometers. For ISEA grids, all cells have the same area (equal-area property). For H3 grids, each cell has a different geodesic area depending on its location.
cell_area(cell_id = NULL, grid)cell_area(cell_id = NULL, grid)
cell_id |
Cell IDs to compute area for. For ISEA grids, these are
numeric; for H3 grids, character strings. When |
grid |
A HexGridInfo or HexData object. |
For ISEA grids the area is constant across all cells and is read directly from the grid specification.
For H3 grids the area varies by latitude. This function computes geodesic
area via sf::st_area() on H3 cell polygons, with results cached in a
session-scoped environment so repeated calls for the same cells are fast.
Named numeric vector of areas in km², one per cell_id.
hex_grid for grid specifications,
h3_crosswalk for ISEA/H3 interoperability
# ISEA: constant area grid <- hex_grid(area_km2 = 1000) cells <- lonlat_to_cell(c(0, 10, 20), c(45, 50, 55), grid) cell_area(cells, grid) # H3: area varies by location h3 <- hex_grid(resolution = 5, type = "h3") h3_cells <- lonlat_to_cell(c(0, 0), c(0, 80), h3) cell_area(h3_cells, h3) # equator vs polar — different areas# ISEA: constant area grid <- hex_grid(area_km2 = 1000) cells <- lonlat_to_cell(c(0, 10, 20), c(45, 50, 55), grid) cell_area(cells, grid) # H3: area varies by location h3 <- hex_grid(resolution = 5, type = "h3") h3_cells <- lonlat_to_cell(c(0, 0), c(0, 80), h3) cell_area(h3_cells, h3) # equator vs polar — different areas
Converts DGGS cell IDs back to geographic coordinates (cell centers).
cell_to_lonlat(cell_id, grid)cell_to_lonlat(cell_id, grid)
cell_id |
Numeric vector of cell IDs |
grid |
A HexGridInfo or HexData object |
Data frame with lon_deg and lat_deg columns
lonlat_to_cell for the forward operation
grid <- hex_grid(area_km2 = 1000) cells <- lonlat_to_cell(c(0, 10), c(45, 50), grid) coords <- cell_to_lonlat(cells, grid)grid <- hex_grid(area_km2 = 1000) cells <- lonlat_to_cell(c(0, 10), c(45, 50), grid) coords <- cell_to_lonlat(cells, grid)
Creates sf polygon geometries for hexagonal grid cells.
cell_to_sf(cell_id = NULL, grid, wrap_dateline = TRUE)cell_to_sf(cell_id = NULL, grid, wrap_dateline = TRUE)
cell_id |
Numeric vector of cell IDs. If NULL and x is HexData, uses cells from x. |
grid |
A HexGridInfo or HexData object. If HexData and cell_id is NULL, polygons are generated for all cells in the data. |
wrap_dateline |
Logical. If TRUE (default), calls
|
When called with a HexData object and no cell_id argument, this function generates polygons for all unique cells in the data, which is useful for plotting.
sf object with cell_id and geometry columns
hex_grid for grid specifications,
as_sf for converting HexData to sf
# From grid specification grid <- hex_grid(area_km2 = 1000) cells <- lonlat_to_cell(c(0, 10, 20), c(45, 50, 55), grid) polys <- cell_to_sf(cells, grid) # From HexData (all cells) df <- data.frame(lon = c(0, 10, 20), lat = c(45, 50, 55)) result <- hexify(df, lon = "lon", lat = "lat", area_km2 = 1000) polys <- cell_to_sf(grid = result)# From grid specification grid <- hex_grid(area_km2 = 1000) cells <- lonlat_to_cell(c(0, 10, 20), c(45, 50, 55), grid) polys <- cell_to_sf(cells, grid) # From HexData (all cells) df <- data.frame(lon = c(0, 10, 20), lat = c(45, 50, 55)) result <- hexify(df, lon = "lon", lat = "lat", area_km2 = 1000) polys <- cell_to_sf(grid = result)
Extract the unique cell IDs present in a HexData object.
cells(x)cells(x)
x |
A HexData object |
A vector of cell IDs
Calculates statistics about the hexagonal grid at the current resolution, including total number of cells, cell area, and cell spacing.
dgearthstat(dggs)dgearthstat(dggs)
dggs |
Grid specification from hexify_grid() |
List with components:
area_km |
Total Earth surface area in km^2 |
n_cells |
Total number of cells at this resolution |
cell_area_km2 |
Average cell area in km^2 |
cell_spacing_km |
Average distance between cell centers in km |
resolution |
Resolution level |
aperture |
Grid aperture |
Other grid statistics:
dg_closest_res_to_area(),
hexify_area_to_eff_res(),
hexify_compare_resolutions(),
hexify_eff_res_to_area(),
hexify_eff_res_to_resolution(),
hexify_resolution_to_eff_res()
grid <- hexify_grid(area = 1000, aperture = 3) stats <- dgearthstat(grid) print(sprintf("Resolution %d has %.0f cells", stats$resolution, stats$n_cells)) print(sprintf("Average cell area: %.2f km^2", stats$cell_area_km2)) print(sprintf("Average cell spacing: %.2f km", stats$cell_spacing_km))grid <- hexify_grid(area = 1000, aperture = 3) stats <- dgearthstat(grid) print(sprintf("Resolution %d has %.0f cells", stats$resolution, stats$n_cells)) print(sprintf("Average cell area: %.2f km^2", stats$cell_area_km2)) print(sprintf("Average cell spacing: %.2f km", stats$cell_spacing_km))
Checks whether a 'dggridR' grid object is compatible with hexify functions. Returns TRUE if compatible, or throws an error describing incompatibilities.
dggrid_is_compatible(dggs, strict = TRUE)dggrid_is_compatible(dggs, strict = TRUE)
dggs |
A 'dggridR' grid object |
strict |
If TRUE (default), throw errors for incompatibilities. If FALSE, return FALSE instead of throwing errors. |
TRUE if compatible, FALSE if not compatible (when strict=FALSE)
Other 'dggridR' compatibility:
as_dggrid(),
dggrid_43h_sequence(),
from_dggrid()
Validates that a grid object has all required fields and valid values. This function is called internally by most hexify functions to ensure grid integrity.
dgverify(dggs)dgverify(dggs)
dggs |
Grid object to verify (from hexify_grid) |
TRUE (invisibly) if valid, otherwise throws an error
grid <- hexify_grid(area = 1000, aperture = 3) dgverify(grid) # Should pass silently # Invalid grid will throw error bad_grid <- list(aperture = 5) try(dgverify(bad_grid)) # Will errorgrid <- hexify_grid(area = 1000, aperture = 3) dgverify(grid) # Should pass silently # Invalid grid will throw error bad_grid <- list(aperture = 5) try(dgverify(bad_grid)) # Will error
Creates a hexify_grid object from a 'dggridR' dggs object. This allows using hexify functions with grids created by 'dggridR' dgconstruct().
from_dggrid(dggs)from_dggrid(dggs)
dggs |
A 'dggridR' grid object from dgconstruct() |
Only 'ISEA' projection with HEXAGON topology is fully supported. Other configurations will generate warnings.
The function validates that the 'dggridR' grid uses compatible settings:
Projection must be 'ISEA' (FULLER not supported)
Topology must be "HEXAGON" (DIAMOND, TRIANGLE not supported)
Aperture must be 3, 4, or 7
A hexify_grid object
Other 'dggridR' compatibility:
as_dggrid(),
dggrid_43h_sequence(),
dggrid_is_compatible()
Returns the k-ring (disk) of cells neighboring the input cells.
For k = 1, returns the immediate 6 neighbors (5 for pentagons).
For k > 1, returns all cells within k grid hops.
get_neighbors(cell_id, grid, k = 1L, include_self = FALSE, distances = FALSE)get_neighbors(cell_id, grid, k = 1L, include_self = FALSE, distances = FALSE)
cell_id |
Cell IDs to find neighbors for. Numeric vector for ISEA grids, character vector for H3 grids. |
grid |
A HexGridInfo or HexData object specifying the grid. |
k |
Integer. Ring distance (default 1). |
include_self |
Logical. If |
distances |
Logical. If |
For ISEA grids, neighbors are computed using axial coordinate offsets in the quad IJ space. Cells at quad boundaries are handled by reprojection through lon/lat coordinates.
For H3 grids, neighbors use the vendored H3 gridDisk /
gridDiskDistances functions.
Pentagon cells (the 12 icosahedron vertices) have only 5 neighbors instead of the usual 6.
If distances = FALSE (default): a list of cell ID vectors, one
per input cell. If distances = TRUE: a list of data.frames with columns
cell_id and ring_distance.
hexify() for creating HexData objects,
hex_distance() for grid distances between cells
# ISEA grid neighbors g <- hex_grid(area_km2 = 1000) cell <- lonlat_to_cell(10, 50, g) nbrs <- get_neighbors(cell, g) nbrs[[1]] # H3 grid neighbors g_h3 <- hex_grid(resolution = 5, type = "h3") cell_h3 <- lonlat_to_cell(10, 50, g_h3) get_neighbors(cell_h3, g_h3, k = 2) # With distances get_neighbors(cell, g, k = 2, distances = TRUE)# ISEA grid neighbors g <- hex_grid(area_km2 = 1000) cell <- lonlat_to_cell(10, 50, g) nbrs <- get_neighbors(cell, g) nbrs[[1]] # H3 grid neighbors g_h3 <- hex_grid(resolution = 5, type = "h3") cell_h3 <- lonlat_to_cell(10, 50, g_h3) get_neighbors(cell_h3, g_h3, k = 2) # With distances get_neighbors(cell, g, k = 2, distances = TRUE)
Named list of lon/lat coordinates for common globe views.
Used by plot_globe when center is specified as a string.
globe_centersglobe_centers
Named list with elements:
c(10, 50) - Western/Central Europe
c(-100, 45) - USA and Canada
c(-60, -15) - Full continent
c(20, 5) - Central Africa
c(100, 35) - China, SE Asia, Japan
c(135, -25) - Australia, NZ, Indonesia
c(45, 25) - Arabian Peninsula, Iran, Turkey
c(80, 20) - India, Pakistan, Bangladesh
c(-160, -10) - Polynesia, Pacific islands
c(-70, 18) - Caribbean islands
c(0, 90) - North pole view
c(0, -90) - South pole view
globe_centers$europe globe_centers$oceaniaglobe_centers$europe globe_centers$oceania
Creates hexagon polygons clipped to a given polygon boundary. This is useful for generating grids that conform to country borders, study areas, or other irregular boundaries.
grid_clip(boundary, grid, crop = TRUE)grid_clip(boundary, grid, crop = TRUE)
boundary |
An sf/sfc polygon to clip to. Can be a country boundary, study area, or any polygon geometry. |
grid |
A HexGridInfo object specifying the grid parameters |
crop |
If TRUE (default), cells are cropped to the boundary. If FALSE, only cells whose centroids fall within the boundary are kept (no cropping). |
The function first generates cells covering the boundary polygon, then clips or filters them. For H3 grids, all cells that overlap the boundary are included (not just cells whose center falls inside), ensuring full spatial coverage with no gaps along the boundary edge.
When crop = TRUE, hexagons are geometrically intersected with the
boundary, which may produce partial hexagons at the edges. When
crop = FALSE, only complete hexagons whose centroids fall within
the boundary are returned.
sf object with hexagon polygons clipped to the boundary
grid_rect for rectangular grids,
grid_global for global grids
# Get France boundary from built-in world map france <- hexify_world[hexify_world$name == "France", ] # Create grid clipped to France grid <- hex_grid(area_km2 = 2000) france_grid <- grid_clip(france, grid) # Plot result library(ggplot2) ggplot() + geom_sf(data = france, fill = "gray95") + geom_sf(data = france_grid, fill = alpha("steelblue", 0.3), color = "steelblue") + theme_minimal() # Keep only complete hexagons (no cropping) france_grid_complete <- grid_clip(france, grid, crop = FALSE)# Get France boundary from built-in world map france <- hexify_world[hexify_world$name == "France", ] # Create grid clipped to France grid <- hex_grid(area_km2 = 2000) france_grid <- grid_clip(france, grid) # Plot result library(ggplot2) ggplot() + geom_sf(data = france, fill = "gray95") + geom_sf(data = france_grid, fill = alpha("steelblue", 0.3), color = "steelblue") + theme_minimal() # Keep only complete hexagons (no cropping) france_grid_complete <- grid_clip(france, grid, crop = FALSE)
Creates hexagon polygons covering the entire Earth.
grid_global(grid, wrap_dateline = TRUE)grid_global(grid, wrap_dateline = TRUE)
grid |
A HexGridInfo object specifying the grid parameters |
wrap_dateline |
Logical. If TRUE (default), antimeridian-crossing polygons are split at +/-180 degrees. Set to FALSE for orthographic/globe projections where wrapping creates gaps. |
This function generates a complete global grid by sampling points
densely across the globe. For large grids (many small cells),
consider using grid_rect() to generate regional subsets.
sf object with hexagon polygons
grid_rect for regional grids
# Coarse global grid grid <- hex_grid(area_km2 = 100000) global <- grid_global(grid) plot(global)# Coarse global grid grid <- hex_grid(area_km2 = 100000) global <- grid_global(grid) plot(global)
Extract the grid specification from a HexData object.
grid_info(x)grid_info(x)
x |
A HexData object |
A HexGridInfo object
df <- data.frame(lon = c(0, 10, 20), lat = c(45, 50, 55)) result <- hexify(df, lon = "lon", lat = "lat", area_km2 = 1000) grid_spec <- grid_info(result)df <- data.frame(lon = c(0, 10, 20), lat = c(45, 50, 55)) result <- hexify(df, lon = "lon", lat = "lat", area_km2 = 1000) grid_spec <- grid_info(result)
Creates hexagon polygons covering a rectangular geographic region. For H3 grids, all cells that overlap the bounding box are included (not just cells whose center falls inside), ensuring full spatial coverage.
grid_rect(bbox, grid)grid_rect(bbox, grid)
bbox |
Bounding box as c(xmin, ymin, xmax, ymax), or an sf/sfc object |
grid |
A HexGridInfo object specifying the grid parameters |
sf object with hexagon polygons
grid_global for global grids
grid <- hex_grid(area_km2 = 5000) europe <- grid_rect(c(-10, 35, 30, 60), grid) plot(europe)grid <- hex_grid(area_km2 = 5000) europe <- grid_rect(c(-10, 35, 30, 60), grid) plot(europe)
Maps cell IDs between ISEA (equal-area) and H3 grid systems by looking up each cell's center coordinate in the target grid. This enables workflows where analysis is done in ISEA (exact equal-area) and reporting in H3 (industry-standard).
h3_crosswalk( cell_id = NULL, grid, h3_resolution = NULL, isea_grid = NULL, direction = c("isea_to_h3", "h3_to_isea") )h3_crosswalk( cell_id = NULL, grid, h3_resolution = NULL, isea_grid = NULL, direction = c("isea_to_h3", "h3_to_isea") )
cell_id |
Cell IDs to translate. Numeric for ISEA, character for H3.
When |
grid |
A HexGridInfo or HexData object. For |
h3_resolution |
Target H3 resolution for |
isea_grid |
A HexGridInfo for the target ISEA grid. Required when
|
direction |
One of |
The crosswalk works by computing the center coordinate of each source cell, then finding which cell in the target grid contains that center. This is a many-to-one mapping: multiple ISEA cells may map to the same H3 cell (or vice versa) depending on the relative resolutions.
When h3_resolution is NULL and direction = "isea_to_h3",
the H3 resolution whose average cell area is closest to the ISEA cell area
is chosen automatically. This gives the best 1:1 correspondence.
A data frame with columns:
ISEA cell ID (numeric)
H3 cell ID (character)
Area of the ISEA cell in km2
Geodesic area of the H3 cell in km2
Ratio of ISEA area to H3 area
cell_area for per-cell area computation,
hex_grid for creating grids
# ISEA -> H3 grid <- hex_grid(area_km2 = 1000) cells <- lonlat_to_cell(c(0, 10, 20), c(45, 50, 55), grid) xwalk <- h3_crosswalk(cells, grid) head(xwalk) # H3 -> ISEA h3 <- hex_grid(resolution = 5, type = "h3") h3_cells <- lonlat_to_cell(c(0, 10), c(45, 50), h3) xwalk2 <- h3_crosswalk(h3_cells, h3, isea_grid = grid, direction = "h3_to_isea")# ISEA -> H3 grid <- hex_grid(area_km2 = 1000) cells <- lonlat_to_cell(c(0, 10, 20), c(45, 50, 55), grid) xwalk <- h3_crosswalk(cells, grid) head(xwalk) # H3 -> ISEA h3 <- hex_grid(resolution = 5, type = "h3") h3_cells <- lonlat_to_cell(c(0, 10), c(45, 50), h3) xwalk2 <- h3_crosswalk(h3_cells, h3, isea_grid = grid, direction = "h3_to_isea")
Opens an interactive leaflet map with hexagonal cell polygons. Cells can be colored by a data column, with popups showing cell information on click.
hex_browse( hex_data, grid = NULL, value = NULL, palette = "viridis", opacity = 0.7 )hex_browse( hex_data, grid = NULL, value = NULL, palette = "viridis", opacity = 0.7 )
hex_data |
A HexData object, or a data.frame with a |
grid |
A HexGridInfo object. Required if |
value |
Optional column name (character) to color cells by.
If |
palette |
Color palette name for continuous values. Default |
opacity |
Fill opacity (0-1). Default 0.7. |
Requires the leaflet package (in Suggests). The map is built using
as_sf() to generate polygon geometries, then rendered as a leaflet
choropleth.
A leaflet map object (can be printed or embedded in Shiny).
hexify_heatmap() for static ggplot2 maps,
as_sf() for sf conversion
if (requireNamespace("leaflet", quietly = TRUE)) { df <- data.frame( lon = runif(50, -5, 5), lat = runif(50, 45, 55), value = rnorm(50) ) hd <- hexify(df, lon = "lon", lat = "lat", area_km2 = 500) hex_browse(hd, value = "value") }if (requireNamespace("leaflet", quietly = TRUE)) { df <- data.frame( lon = runif(50, -5, 5), lat = runif(50, 45, 55), value = rnorm(50) ) hd <- hexify(df, lon = "lon", lat = "lat", area_km2 = 500) hex_browse(hd, value = "value") }
Merges child cells into their parent when all children are present. This is a lossless compression — no spatial information is lost. The compact representation uses fewer cells to cover the same area.
hex_compact(cell_ids, grid)hex_compact(cell_ids, grid)
cell_ids |
Cell IDs to compact. For H3 grids, a character vector. For ISEA grids, a character vector of hierarchical index strings. |
grid |
A HexGridInfo object specifying the grid. |
H3 backend: Uses the vendored H3 compactCells function.
ISEA backend (aperture 7, Z7 index): Groups cells by parent index (dropping the last digit). If all 7 children are present, replaces them with the parent. Iterates until no further compaction is possible.
A character vector of compacted cell IDs. Cells that could be merged into parents appear as parent IDs at coarser resolution.
hex_uncompact() for the inverse operation,
get_parent(), get_children() for hierarchical operations
# H3 compaction g <- hex_grid(resolution = 3, type = "h3") parent <- "832830fffffffff" children <- get_children(parent, g)[[1]] compact <- hex_compact(children, g) compact # Should return the parent# H3 compaction g <- hex_grid(resolution = 3, type = "h3") parent <- "832830fffffffff" children <- get_children(parent, g)[[1]] compact <- hex_compact(children, g) compact # Should return the parent
Computes the grid distance (minimum number of hops) between pairs of hexagonal cells. This is a discrete distance measured in cell steps, not a geodesic distance.
hex_distance(cell_a, cell_b, grid)hex_distance(cell_a, cell_b, grid)
cell_a, cell_b
|
Cell IDs. Must be the same length (pairs) or one of them length 1 (broadcast). For H3 grids, character vectors. For ISEA grids, numeric vectors. |
grid |
A HexGridInfo or HexData object specifying the grid. |
H3 backend: Uses the vendored H3 gridDistance function.
Returns the exact shortest path length in cell hops.
ISEA backend: For cells on the same quad, computes the cube-coordinate
distance: max(|di|, |dj|, |di + dj|). Cross-quad distances use BFS
expansion and may return NA for very distant cells.
For geodesic (geographic) distances between cell centers, convert to
lon/lat with cell_to_lonlat() and use sf::st_distance().
An integer vector of grid distances. NA where the distance
cannot be computed (e.g., pentagon path issues in H3).
get_neighbors() for finding cells within a given distance,
cell_to_lonlat() for geographic coordinates
# H3 grid distance g <- hex_grid(resolution = 5, type = "h3") a <- lonlat_to_cell(10, 50, g) b <- lonlat_to_cell(10.1, 50.1, g) hex_distance(a, b, g)# H3 grid distance g <- hex_grid(resolution = 5, type = "h3") a <- lonlat_to_cell(10, 50, g) b <- lonlat_to_cell(10.1, 50.1, g) hex_distance(a, b, g)
Samples raster values at hexagonal cell centers. Faster than
hex_zonal() because it only queries cell center points, not full
polygons.
hex_extract(raster, grid, cells = NULL, boundary = NULL)hex_extract(raster, grid, cells = NULL, boundary = NULL)
raster |
A |
grid |
A HexGridInfo or HexData object specifying the grid. |
cells |
Optional cell IDs to extract. If |
boundary |
Optional sf polygon to limit extraction extent. |
Requires the terra package (in Suggests). The function:
Generates cell centers for the raster extent
Calls terra::extract(raster, cell_center_matrix)
Attaches cell IDs
For full zonal statistics (aggregating all pixels within each hex polygon),
use hex_zonal() instead.
A data.frame with columns cell_id, plus one column per raster
layer.
hex_zonal() for polygon-based zonal statistics,
hexify() for creating HexData objects
if (requireNamespace("terra", quietly = TRUE)) { # Create a small synthetic raster r <- terra::rast(nrows = 10, ncols = 10, xmin = -10, xmax = 10, ymin = 40, ymax = 55) terra::values(r) <- runif(100) names(r) <- "temperature" # Extract at hex cell centers g <- hex_grid(area_km2 = 500) df <- data.frame(lon = c(0, 5), lat = c(45, 50)) hd <- hexify(df, lon = "lon", lat = "lat", grid = g) hex_extract(r, hd) }if (requireNamespace("terra", quietly = TRUE)) { # Create a small synthetic raster r <- terra::rast(nrows = 10, ncols = 10, xmin = -10, xmax = 10, ymin = 40, ymax = 55) terra::values(r) <- runif(100) names(r) <- "temperature" # Extract at hex cell centers g <- hex_grid(area_km2 = 500) df <- data.frame(lon = c(0, 5), lat = c(45, 50)) hd <- hexify(df, lon = "lon", lat = "lat", grid = g) hex_extract(r, hd) }
Creates a HexGridInfo object that stores all parameters needed for hexagonal grid operations. Use this to define the grid once and pass it to all downstream functions.
hex_grid( area_km2 = NULL, resolution = NULL, aperture = 3, type = c("isea", "h3"), resround = "nearest", crs = 4326L )hex_grid( area_km2 = NULL, resolution = NULL, aperture = 3, type = c("isea", "h3"), resround = "nearest", crs = 4326L )
area_km2 |
Target cell area in square kilometers. Mutually exclusive
with |
resolution |
Grid resolution level (0-30 for ISEA, 0-15 for H3).
Mutually exclusive with
|
aperture |
Grid aperture: 3 (default), 4, 7, or "4/3" for mixed. Ignored for H3 grids (fixed at 7). |
type |
Grid type: "isea" (default) or "h3". |
resround |
Resolution rounding when using |
crs |
Coordinate reference system EPSG code (default 4326 = 'WGS84'). |
Exactly one of area_km2 or resolution must be provided.
When area_km2 is provided, the resolution is calculated automatically
using the cell count formula: N = 10 * aperture^res + 2 (ISEA) or by
matching the closest H3 resolution.
H3 grids use the Uber H3 hierarchical hexagonal system. Unlike ISEA grids, H3 cells are NOT exactly equal-area (area varies by ~3-5\ location).
A HexGridInfo object containing the grid specification.
A HexGridInfo acts as a shared spatial reference system - like a CRS, but discrete and equal-area. Define the grid once, then attach multiple datasets without repeating parameters:
# Step 1: Define the grid once
grid <- hex_grid(area_km2 = 1000)
# Step 2: Attach multiple datasets to the same grid
birds <- hexify(bird_obs, lon = "longitude", lat = "latitude", grid = grid)
mammals <- hexify(mammal_obs, lon = "lon", lat = "lat", grid = grid)
climate <- hexify(weather_stations, lon = "x", lat = "y", grid = grid)
# No aperture, resolution, or area needed after step 1 - the grid
# travels with the data.
# Step 3: Work at the cell level
# Once hexified, lon/lat no longer matter - cell_id is the shared key
bird_counts <- aggregate(species ~ cell_id, data = as.data.frame(birds), length)
mammal_richness <- aggregate(species ~ cell_id, data = as.data.frame(mammals),
function(x) length(unique(x)))
# Join datasets by cell_id - guaranteed to align because same grid
combined <- merge(bird_counts, mammal_richness, by = "cell_id")
# Step 4: Visual confirmation
# All datasets produce identical grid overlays
plot(birds) # See the grid
plot(mammals) # Same grid, different data
hexify for assigning points to cells,
HexGridInfo-class for class documentation
# Create grid by target area grid <- hex_grid(area_km2 = 1000) print(grid) # Create grid by resolution grid <- hex_grid(resolution = 8, aperture = 3) # Create grid with different aperture grid4 <- hex_grid(area_km2 = 500, aperture = 4) # Create mixed aperture grid grid43 <- hex_grid(area_km2 = 1000, aperture = "4/3") # Use grid in hexify df <- data.frame(lon = c(0, 10, 20), lat = c(45, 50, 55)) result <- hexify(df, lon = "lon", lat = "lat", grid = grid)# Create grid by target area grid <- hex_grid(area_km2 = 1000) print(grid) # Create grid by resolution grid <- hex_grid(resolution = 8, aperture = 3) # Create grid with different aperture grid4 <- hex_grid(area_km2 = 500, aperture = 4) # Create mixed aperture grid grid43 <- hex_grid(area_km2 = 1000, aperture = "4/3") # Use grid in hexify df <- data.frame(lon = c(0, 10, 20), lat = c(45, 50, 55)) result <- hexify(df, lon = "lon", lat = "lat", grid = grid)
Aggregates data within each hexagonal cell, similar to
dplyr::group_by(cell_id) |> summarize(...). Returns a data.frame
with one row per unique cell, including cell center coordinates and area.
hex_summarize(hex_data, ..., .fns = NULL, geometry = FALSE)hex_summarize(hex_data, ..., .fns = NULL, geometry = FALSE)
hex_data |
A HexData object (from |
... |
Named summary expressions (tidyeval). Each expression is
evaluated per cell group. Examples: |
.fns |
Optional named list of functions for formula-style aggregation.
Example: |
geometry |
Logical. If |
The function works entirely in R (no C++ needed). It groups by cell_id
and evaluates the summary expressions within each group.
If no summary expressions are provided, returns cell counts only.
A data.frame with columns:
Unique cell identifier
Cell center coordinates
Cell area in km^2
Number of data points in this cell
User-defined summary columns
If geometry = TRUE, returns an sf object with POINT geometry.
hexify() for creating HexData objects,
get_neighbors() for finding neighboring cells
df <- data.frame( lon = runif(100, -10, 10), lat = runif(100, 40, 55), temperature = rnorm(100, 15, 5), species = sample(letters[1:5], 100, replace = TRUE) ) hd <- hexify(df, lon = "lon", lat = "lat", area_km2 = 500) # Count points per cell hex_summarize(hd) # Custom summaries hex_summarize(hd, mean_temp = mean(temperature), n_species = length(unique(species)))df <- data.frame( lon = runif(100, -10, 10), lat = runif(100, 40, 55), temperature = rnorm(100, 15, 5), species = sample(letters[1:5], 100, replace = TRUE) ) hd <- hexify(df, lon = "lon", lat = "lat", area_km2 = 500) # Count points per cell hex_summarize(hd) # Custom summaries hex_summarize(hd, mean_temp = mean(temperature), n_species = length(unique(species)))
Expands compacted cells to a target resolution. All cells in the output share the same resolution.
hex_uncompact(cell_ids, grid, target_resolution)hex_uncompact(cell_ids, grid, target_resolution)
cell_ids |
Character vector of (possibly mixed-resolution) cell IDs. |
grid |
A HexGridInfo object specifying the grid. |
target_resolution |
Integer. The resolution to expand all cells to. |
H3 backend: Uses the vendored H3 uncompactCells function.
ISEA backend (aperture 7, Z7 index): Appends digits 0-6 to expand each cell to its 7 children, repeating until the target resolution is reached.
A character vector of cell IDs, all at target_resolution.
hex_compact() for the inverse operation
g <- hex_grid(resolution = 3, type = "h3") parent <- "832830fffffffff" hex_uncompact(parent, g, target_resolution = 4L)g <- hex_grid(resolution = 3, type = "h3") parent <- "832830fffffffff" hex_uncompact(parent, g, target_resolution = 4L)
Computes zonal statistics by aggregating all raster pixels falling
within each hexagonal cell polygon. More accurate than hex_extract()
but slower because it requires polygon geometries.
hex_zonal(raster, grid, fun = "mean", boundary = NULL, cells = NULL)hex_zonal(raster, grid, fun = "mean", boundary = NULL, cells = NULL)
raster |
A |
grid |
A HexGridInfo or HexData object specifying the grid. |
fun |
Summary function name: |
boundary |
Optional sf polygon to limit the analysis extent. |
cells |
Optional cell IDs. If provided, only these cells are included. |
Requires the terra package (in Suggests). The function:
Generates hex polygon geometries via cell_to_sf()
Calls terra::extract(raster, polygons, fun = fun)
Joins results back to cell IDs
For point-based extraction (faster, at cell centers only), use
hex_extract() instead.
A data.frame with columns cell_id, plus one column per raster
layer containing the aggregated values.
hex_extract() for cell-center extraction,
cell_to_sf() for hex polygon generation
if (requireNamespace("terra", quietly = TRUE)) { r <- terra::rast(nrows = 100, ncols = 100, xmin = -10, xmax = 10, ymin = 40, ymax = 55) terra::values(r) <- runif(10000) names(r) <- "temperature" g <- hex_grid(area_km2 = 500) df <- data.frame(lon = c(0, 5), lat = c(45, 50)) hd <- hexify(df, lon = "lon", lat = "lat", grid = g) hex_zonal(r, hd, fun = "mean") }if (requireNamespace("terra", quietly = TRUE)) { r <- terra::rast(nrows = 100, ncols = 100, xmin = -10, xmax = 10, ymin = 40, ymax = 55) terra::values(r) <- runif(10000) names(r) <- "temperature" g <- hex_grid(area_km2 = 500) df <- data.frame(lon = c(0, 5), lat = c(45, 50)) hd <- hexify(df, lon = "lon", lat = "lat", grid = g) hex_zonal(r, hd, fun = "mean") }
An S4 class representing hexified data. Contains the original user data plus cell assignments from the hexification process.
HexData objects are created by hexify. The original data
is preserved in the data slot, while cell assignments are stored
separately in cell_id and cell_center.
Use as.data.frame() to get a combined data frame with cell columns.
dataData frame or sf object. The original user data (untouched).
gridHexGridInfo object. The grid specification used.
cell_idCell IDs for each row of data. Numeric for ISEA grids, character for H3 grids.
cell_centerMatrix. Two-column matrix (lon, lat) of cell centers.
hexify for creating HexData objects,
HexGridInfo-class for grid specifications
An S4 class representing a hexagonal grid specification. Stores all parameters needed for grid operations.
Create HexGridInfo objects using the hex_grid constructor function.
Do not use new("HexGridInfo", ...) directly.
The aperture can be "3", "4", "7" for standard grids, or "4/3" for mixed aperture grids that start with aperture 4 and switch to aperture 3.
For H3 grids, the aperture is fixed at "7" and resolution ranges from 0 to 15.
apertureCharacter. Grid aperture: "3", "4", "7", or "4/3" for mixed.
resolutionInteger. Grid resolution level (0-30 for ISEA, 0-15 for H3).
area_km2Numeric. Cell area in square kilometers.
diagonal_kmNumeric. Cell diagonal (long diagonal) in kilometers.
crsInteger. Coordinate reference system (default 4326 = 'WGS84').
grid_typeCharacter. Grid system: "isea" (default) or "h3".
hex_grid for the constructor function,
HexData-class for hexified data objects
Takes a data.frame or sf object with geographic coordinates and returns a HexData object that stores the original data plus cell assignments. The original data is preserved unchanged; cell IDs and centers are stored in separate slots.
hexify( data, grid = NULL, lon = "lon", lat = "lat", area_km2 = NULL, diagonal = NULL, resolution = NULL, aperture = 3, resround = "nearest" )hexify( data, grid = NULL, lon = "lon", lat = "lat", area_km2 = NULL, diagonal = NULL, resolution = NULL, aperture = 3, resround = "nearest" )
data |
A data.frame or sf object containing coordinates |
grid |
A HexGridInfo object from |
lon |
Column name for longitude (ignored if data is sf) |
lat |
Column name for latitude (ignored if data is sf) |
area_km2 |
Target cell area in km^2 (mutually exclusive with diagonal). |
diagonal |
Target cell diagonal (long diagonal) in km |
resolution |
Grid resolution (0-30). Alternative to area_km2. |
aperture |
Grid aperture: 3, 4, 7, or "4/3" for mixed (default 3) |
resround |
How to round resolution: "nearest", "up", or "down" |
For sf objects, coordinates are automatically extracted and transformed to 'WGS84' (EPSG:4326) if needed. The geometry column is preserved.
Either area_km2 (or area), diagonal, or resolution
must be provided unless a grid object is supplied.
The HexData return type (default) stores the grid specification so downstream
functions like plot(), hexify_cell_to_sf(), etc. don't need
grid parameters repeated.
A HexData object containing:
data: The original input data (unchanged)
grid: The HexGridInfo specification
cell_id: Numeric vector of cell IDs for each row
cell_center: Matrix of cell center coordinates (lon, lat)
Use as.data.frame(result) to extract the original data.
Use cells(result) to get unique cell IDs.
Use result@cell_id to get all cell IDs.
Use result@cell_center to get cell center coordinates.
You can create a grid specification once and reuse it:
grid <- hex_grid(area_km2 = 1000) result1 <- hexify(df1, grid = grid) result2 <- hexify(df2, grid = grid)
hex_grid for grid specification,
HexData-class for return object details,
as_sf for converting to sf
Other hexify main:
hexify_grid()
# Simple data.frame df <- data.frame( site = c("Vienna", "Paris", "Madrid"), lon = c(16.37, 2.35, -3.70), lat = c(48.21, 48.86, 40.42) ) # New recommended workflow: use grid object grid <- hex_grid(area_km2 = 1000) result <- hexify(df, grid = grid, lon = "lon", lat = "lat") print(result) # Shows grid info plot(result) # Plot with default styling # Direct area specification (grid created internally) result <- hexify(df, lon = "lon", lat = "lat", area_km2 = 1000) # Extract plain data.frame df_result <- as.data.frame(result) # With sf object (any CRS) library(sf) pts <- st_as_sf(df, coords = c("lon", "lat"), crs = 4326) result_sf <- hexify(pts, area_km2 = 1000) # Different apertures result_ap4 <- hexify(df, lon = "lon", lat = "lat", area_km2 = 1000, aperture = 4) # Mixed aperture (ISEA43H) result_mixed <- hexify(df, lon = "lon", lat = "lat", area_km2 = 1000, aperture = "4/3")# Simple data.frame df <- data.frame( site = c("Vienna", "Paris", "Madrid"), lon = c(16.37, 2.35, -3.70), lat = c(48.21, 48.86, 40.42) ) # New recommended workflow: use grid object grid <- hex_grid(area_km2 = 1000) result <- hexify(df, grid = grid, lon = "lon", lat = "lat") print(result) # Shows grid info plot(result) # Plot with default styling # Direct area specification (grid created internally) result <- hexify(df, lon = "lon", lat = "lat", area_km2 = 1000) # Extract plain data.frame df_result <- as.data.frame(result) # With sf object (any CRS) library(sf) pts <- st_as_sf(df, coords = c("lon", "lat"), crs = 4326) result_sf <- hexify(pts, area_km2 = 1000) # Different apertures result_ap4 <- hexify(df, lon = "lon", lat = "lat", area_km2 = 1000, aperture = 4) # Mixed aperture (ISEA43H) result_mixed <- hexify(df, lon = "lon", lat = "lat", area_km2 = 1000, aperture = "4/3")
Sets up the icosahedron state for ISEA projection. Uses standard ISEA3H orientation by default (vertex 0 at 11.25E, 58.28N).
hexify_build_icosa( vert0_lon = ISEA_VERT0_LON_DEG, vert0_lat = ISEA_VERT0_LAT_DEG, azimuth = ISEA_AZIMUTH_DEG )hexify_build_icosa( vert0_lon = ISEA_VERT0_LON_DEG, vert0_lat = ISEA_VERT0_LAT_DEG, azimuth = ISEA_AZIMUTH_DEG )
vert0_lon |
Vertex 0 longitude in degrees (default ISEA_VERT0_LON_DEG) |
vert0_lat |
Vertex 0 latitude in degrees (default ISEA_VERT0_LAT_DEG) |
azimuth |
Azimuth rotation in degrees (default ISEA_AZIMUTH_DEG) |
The icosahedron is initialized lazily at the C++ level when first needed. Manual call is only required for non-standard orientations.
Invisible NULL. Called for side effect.
Other projection:
hexify_face_centers(),
hexify_forward(),
hexify_forward_to_face(),
hexify_get_precision(),
hexify_inverse(),
hexify_projection_stats(),
hexify_set_precision(),
hexify_set_verbose(),
hexify_which_face()
# Use standard ISEA3H orientation hexify_build_icosa() # Custom orientation hexify_build_icosa(vert0_lon = 0, vert0_lat = 90, azimuth = 0)# Use standard ISEA3H orientation hexify_build_icosa() # Custom orientation hexify_build_icosa(vert0_lon = 0, vert0_lat = 90, azimuth = 0)
Generates a table comparing different resolution levels for a given grid configuration. Useful for choosing appropriate resolution.
hexify_compare_resolutions( aperture = 3, res_range = 0:15, type = c("isea", "h3"), print = FALSE )hexify_compare_resolutions( aperture = 3, res_range = 0:15, type = c("isea", "h3"), print = FALSE )
aperture |
Grid aperture (3, 4, or 7). Ignored for H3 grids. |
res_range |
Range of resolutions to compare (e.g., 1:10) |
type |
Grid type: "isea" (default) or "h3". |
print |
If TRUE, prints a formatted table to console. If FALSE (default), returns a data frame. |
If print=FALSE: data frame with columns resolution, n_cells, cell_area_km2, cell_spacing_km, cls_km. If print=TRUE: invisibly returns the data frame after printing.
Other grid statistics:
dg_closest_res_to_area(),
dgearthstat(),
hexify_area_to_eff_res(),
hexify_eff_res_to_area(),
hexify_eff_res_to_resolution(),
hexify_resolution_to_eff_res()
# Get data frame of resolutions 0-10 for aperture 3 comparison <- hexify_compare_resolutions(aperture = 3, res_range = 0:10) print(comparison) # Print formatted table directly hexify_compare_resolutions(aperture = 3, res_range = 0:10, print = TRUE) # Find resolution with cells ~1000 km^2 subset(comparison, cell_area_km2 > 900 & cell_area_km2 < 1100)# Get data frame of resolutions 0-10 for aperture 3 comparison <- hexify_compare_resolutions(aperture = 3, res_range = 0:10) print(comparison) # Print formatted table directly hexify_compare_resolutions(aperture = 3, res_range = 0:10, print = TRUE) # Find resolution with cells ~1000 km^2 subset(comparison, cell_area_km2 > 900 & cell_area_km2 < 1100)
Returns the center coordinates of all 20 icosahedral faces.
hexify_face_centers()hexify_face_centers()
Data frame with 20 rows and columns lon, lat (degrees)
Other projection:
hexify_build_icosa(),
hexify_forward(),
hexify_forward_to_face(),
hexify_get_precision(),
hexify_inverse(),
hexify_projection_stats(),
hexify_set_precision(),
hexify_set_verbose(),
hexify_which_face()
centers <- hexify_face_centers() plot(centers$lon, centers$lat)centers <- hexify_face_centers() plot(centers$lon, centers$lat)
Projects geographic coordinates onto the icosahedron, returning face index and planar coordinates (tx, ty).
hexify_forward(lon, lat)hexify_forward(lon, lat)
lon |
Longitude in degrees |
lat |
Latitude in degrees |
tx and ty are normalized coordinates within the triangular face, typically in range [0, 1].
Named numeric vector: c(face, tx, ty)
Other projection:
hexify_build_icosa(),
hexify_face_centers(),
hexify_forward_to_face(),
hexify_get_precision(),
hexify_inverse(),
hexify_projection_stats(),
hexify_set_precision(),
hexify_set_verbose(),
hexify_which_face()
result <- hexify_forward(16.37, 48.21) # result["face"], result["icosa_triangle_x"], result["icosa_triangle_y"]result <- hexify_forward(16.37, 48.21) # result["face"], result["icosa_triangle_x"], result["icosa_triangle_y"]
Projects to a known face (skips face detection).
hexify_forward_to_face(face, lon, lat)hexify_forward_to_face(face, lon, lat)
face |
Face index (0-19) |
lon |
Longitude in degrees |
lat |
Latitude in degrees |
Named numeric vector: c(icosa_triangle_x, icosa_triangle_y)
Other projection:
hexify_build_icosa(),
hexify_face_centers(),
hexify_forward(),
hexify_get_precision(),
hexify_inverse(),
hexify_projection_stats(),
hexify_set_precision(),
hexify_set_verbose(),
hexify_which_face()
Get current precision settings
hexify_get_precision()hexify_get_precision()
List with tol and max_iters
Other projection:
hexify_build_icosa(),
hexify_face_centers(),
hexify_forward(),
hexify_forward_to_face(),
hexify_inverse(),
hexify_projection_stats(),
hexify_set_precision(),
hexify_set_verbose(),
hexify_which_face()
Creates a discrete global grid system (DGGS) object with hexagonal cells at a specified resolution. This is the main constructor for hexify grids.
hexify_grid( area, topology = "HEXAGON", metric = TRUE, resround = "nearest", aperture = 3, projection = "ISEA" )hexify_grid( area, topology = "HEXAGON", metric = TRUE, resround = "nearest", aperture = 3, projection = "ISEA" )
area |
Target cell area in km^2 (if metric=TRUE) or area code |
topology |
Grid topology (only "HEXAGON" supported) |
metric |
Whether area is in metric units (km^2) |
resround |
How to round resolution ("nearest", "up", "down") |
aperture |
Aperture sequence (3, 4, or 7) |
projection |
Projection type (only 'ISEA' supported currently) |
A hexify_grid object containing:
area |
Target cell area |
resolution |
Calculated resolution level |
aperture |
Grid aperture (3, 4, or 7) |
topology |
Grid topology ("HEXAGON") |
projection |
Map projection ("ISEA") |
index_type |
Index encoding type ("z3", "z7", or "zorder") |
hexify for the main user function,
hexify_grid_to_cell for coordinate conversion
Other hexify main:
hexify()
# Create a grid with ~1000 km^2 cells grid <- hexify_grid(area = 1000, aperture = 3) print(grid) # Create a finer resolution grid (~100 km^2 cells) fine_grid <- hexify_grid(area = 100, aperture = 3, resround = "up")# Create a grid with ~1000 km^2 cells grid <- hexify_grid(area = 1000, aperture = 3) print(grid) # Create a finer resolution grid (~100 km^2 cells) fine_grid <- hexify_grid(area = 100, aperture = 3, resround = "up")
Creates a ggplot2-based visualization of hexagonal grid cells, optionally colored by a value column. Supports continuous and discrete color scales, projection transformation, and customizable styling.
hexify_heatmap( data, value = NULL, basemap = NULL, crs = NULL, colors = NULL, breaks = NULL, labels = NULL, hex_border = "#5D4E37", hex_lwd = 0.3, hex_alpha = 0.7, basemap_fill = "gray90", basemap_border = "gray50", basemap_lwd = 0.5, mask_outside = FALSE, aperture = 3L, xlim = NULL, ylim = NULL, title = NULL, legend_title = NULL, na_color = "gray90", theme_void = TRUE )hexify_heatmap( data, value = NULL, basemap = NULL, crs = NULL, colors = NULL, breaks = NULL, labels = NULL, hex_border = "#5D4E37", hex_lwd = 0.3, hex_alpha = 0.7, basemap_fill = "gray90", basemap_border = "gray50", basemap_lwd = 0.5, mask_outside = FALSE, aperture = 3L, xlim = NULL, ylim = NULL, title = NULL, legend_title = NULL, na_color = "gray90", theme_void = TRUE )
data |
A HexData object from |
value |
Column name (as string) to use for fill color. If NULL, cells are drawn with a uniform fill color. If not specified but data has a 'count' or 'n' column, that will be used automatically. |
basemap |
Optional basemap. Can be:
|
crs |
Target CRS for the map projection. Can be:
|
colors |
Color palette for the heatmap. Can be:
|
breaks |
Numeric vector of break points for binning continuous values,
or NULL for continuous scale. Use |
labels |
Labels for the breaks (length should be one less than breaks). If NULL, labels are auto-generated. |
hex_border |
Border color for hexagons |
hex_lwd |
Line width for hexagon borders |
hex_alpha |
Transparency for hexagon fill (0-1) |
basemap_fill |
Fill color for basemap polygons |
basemap_border |
Border color for basemap polygons |
basemap_lwd |
Line width for basemap borders |
mask_outside |
Logical. If TRUE and basemap is provided, mask hexagon portions that fall outside the basemap polygons. |
aperture |
Grid aperture (default 3), used if data is from hexify() |
xlim |
Optional x-axis limits (in target CRS units) as c(min, max) |
ylim |
Optional y-axis limits (in target CRS units) as c(min, max) |
title |
Plot title |
legend_title |
Title for the color legend |
na_color |
Color for NA values |
theme_void |
Logical. If TRUE (default), use a minimal theme without axes, gridlines, or background. |
This function provides publication-quality heatmap visualizations of hexagonal grids using ggplot2. It returns a ggplot object that can be further customized with standard ggplot2 functions.
A ggplot2 object that can be further customized or saved.
The function supports three types of color scales:
Set breaks = NULL for a continuous gradient
Provide breaks vector to bin values into categories
If value column is a factor, discrete colors are used
Common projections:
'WGS84' (unprojected lat/lon)
LAEA Europe
Web Mercator
Robinson (world maps)
Mollweide (equal-area world maps)
plot_grid for base R plotting,
cell_to_sf to generate polygons manually
Other visualization:
plot_world()
library(hexify) # Sample data with counts cities <- data.frame( lon = c(16.37, 2.35, -3.70, 12.5, 4.9), lat = c(48.21, 48.86, 40.42, 41.9, 52.4), count = c(100, 250, 75, 180, 300) ) result <- hexify(cities, lon = "lon", lat = "lat", area_km2 = 5000) # Simple plot (uniform fill, no value mapping) hexify_heatmap(result) library(ggplot2) # With world basemap hexify_heatmap(result, basemap = "world") # Heatmap with value mapping hexify_heatmap(result, value = "count") # With world basemap and custom colors hexify_heatmap(result, value = "count", basemap = "world", colors = "YlOrRd", title = "City Density") # Binned values with custom breaks hexify_heatmap(result, value = "count", basemap = "world", breaks = c(-Inf, 100, 200, Inf), labels = c("Low", "Medium", "High"), colors = c("#fee8c8", "#fdbb84", "#e34a33")) # Different projection (LAEA Europe) hexify_heatmap(result, value = "count", basemap = "world", crs = 3035, xlim = c(2500000, 6500000), ylim = c(1500000, 5500000)) # Customize further with ggplot2 hexify_heatmap(result, value = "count", basemap = "world") + labs(caption = "Data source: Example") + theme(legend.position = "bottom")library(hexify) # Sample data with counts cities <- data.frame( lon = c(16.37, 2.35, -3.70, 12.5, 4.9), lat = c(48.21, 48.86, 40.42, 41.9, 52.4), count = c(100, 250, 75, 180, 300) ) result <- hexify(cities, lon = "lon", lat = "lat", area_km2 = 5000) # Simple plot (uniform fill, no value mapping) hexify_heatmap(result) library(ggplot2) # With world basemap hexify_heatmap(result, basemap = "world") # Heatmap with value mapping hexify_heatmap(result, value = "count") # With world basemap and custom colors hexify_heatmap(result, value = "count", basemap = "world", colors = "YlOrRd", title = "City Density") # Binned values with custom breaks hexify_heatmap(result, value = "count", basemap = "world", breaks = c(-Inf, 100, 200, Inf), labels = c("Low", "Medium", "High"), colors = c("#fee8c8", "#fdbb84", "#e34a33")) # Different projection (LAEA Europe) hexify_heatmap(result, value = "count", basemap = "world", crs = 3035, xlim = c(2500000, 6500000), ylim = c(1500000, 5500000)) # Customize further with ggplot2 hexify_heatmap(result, value = "count", basemap = "world") + labs(caption = "Data source: Example") + theme(legend.position = "bottom")
Converts face plane coordinates back to geographic coordinates.
hexify_inverse(x, y, face, tol = NULL, max_iters = NULL)hexify_inverse(x, y, face, tol = NULL, max_iters = NULL)
x |
X coordinate on face plane |
y |
Y coordinate on face plane |
face |
Face index (0-19) |
tol |
Convergence tolerance (NULL for default) |
max_iters |
Maximum iterations (NULL for default) |
Named numeric vector: c(lon_deg, lat_deg)
Other projection:
hexify_build_icosa(),
hexify_face_centers(),
hexify_forward(),
hexify_forward_to_face(),
hexify_get_precision(),
hexify_projection_stats(),
hexify_set_precision(),
hexify_set_verbose(),
hexify_which_face()
coords <- hexify_inverse(0.5, 0.3, face = 2)coords <- hexify_inverse(0.5, 0.3, face = 2)
Returns and optionally resets convergence statistics.
hexify_projection_stats(reset = TRUE)hexify_projection_stats(reset = TRUE)
reset |
Whether to reset statistics after retrieval (default TRUE) |
List with statistics (iterations, convergence info, etc.)
Other projection:
hexify_build_icosa(),
hexify_face_centers(),
hexify_forward(),
hexify_forward_to_face(),
hexify_get_precision(),
hexify_inverse(),
hexify_set_precision(),
hexify_set_verbose(),
hexify_which_face()
Tests the accuracy of the coordinate conversion functions by converting coordinates to cells and back, measuring the distance between original and reconstructed coordinates.
hexify_roundtrip_test(grid, lon, lat, units = "km")hexify_roundtrip_test(grid, lon, lat, units = "km")
grid |
Grid specification |
lon |
Longitude to test |
lat |
Latitude to test |
units |
Distance units ("km" or "degrees") |
List with:
original |
Original coordinates |
cell |
Cell index |
reconstructed |
Reconstructed coordinates |
error |
Distance between original and reconstructed |
Other coordinate conversion:
hexify_cell_id_to_quad_ij(),
hexify_cell_to_icosa_tri(),
hexify_cell_to_lonlat(),
hexify_cell_to_plane(),
hexify_cell_to_quad_ij(),
hexify_cell_to_quad_xy(),
hexify_grid_cell_to_lonlat(),
hexify_grid_to_cell(),
hexify_icosa_tri_to_plane(),
hexify_icosa_tri_to_quad_ij(),
hexify_icosa_tri_to_quad_xy(),
hexify_lonlat_to_cell(),
hexify_lonlat_to_plane(),
hexify_lonlat_to_quad_ij(),
hexify_quad_ij_to_cell(),
hexify_quad_ij_to_icosa_tri(),
hexify_quad_ij_to_xy(),
hexify_quad_xy_to_cell(),
hexify_quad_xy_to_icosa_tri()
Controls the accuracy/speed tradeoff for inverse Snyder projection.
hexify_set_precision( mode = c("fast", "default", "high", "ultra"), tol = NULL, max_iters = NULL )hexify_set_precision( mode = c("fast", "default", "high", "ultra"), tol = NULL, max_iters = NULL )
mode |
Preset mode: "fast", "default", "high", or "ultra" |
tol |
Custom tolerance (overrides mode if provided) |
max_iters |
Custom max iterations (overrides mode if provided) |
Invisible NULL
Other projection:
hexify_build_icosa(),
hexify_face_centers(),
hexify_forward(),
hexify_forward_to_face(),
hexify_get_precision(),
hexify_inverse(),
hexify_projection_stats(),
hexify_set_verbose(),
hexify_which_face()
hexify_set_precision("high") hexify_set_precision(tol = 1e-12, max_iters = 100)hexify_set_precision("high") hexify_set_precision(tol = 1e-12, max_iters = 100)
When enabled, prints convergence information.
hexify_set_verbose(verbose = TRUE)hexify_set_verbose(verbose = TRUE)
verbose |
Logical |
Invisible NULL
Other projection:
hexify_build_icosa(),
hexify_face_centers(),
hexify_forward(),
hexify_forward_to_face(),
hexify_get_precision(),
hexify_inverse(),
hexify_projection_stats(),
hexify_set_precision(),
hexify_which_face()
Returns the icosahedral face index (0-19) containing the given coordinates.
hexify_which_face(lon, lat)hexify_which_face(lon, lat)
lon |
Longitude in degrees |
lat |
Latitude in degrees |
Integer face index (0-19)
Other projection:
hexify_build_icosa(),
hexify_face_centers(),
hexify_forward(),
hexify_forward_to_face(),
hexify_get_precision(),
hexify_inverse(),
hexify_projection_stats(),
hexify_set_precision(),
hexify_set_verbose()
face <- hexify_which_face(16.37, 48.21)face <- hexify_which_face(16.37, 48.21)
A lightweight sf object containing simplified world country borders, suitable for use as a basemap when visualizing hexagonal grids.
hexify_worldhexify_world
An sf object with 177 features and 15 fields:
Country short name
Country full name
Administrative name
Sovereignty
ISO 3166-1 alpha-2 country code
ISO 3166-1 alpha-3 country code
ISO 3166-1 numeric code
Continent name
UN region
UN subregion
World Bank region
Population estimate
GDP in millions USD
Income group classification
Economy type
MULTIPOLYGON geometry in 'WGS84' (EPSG:4326)
Simplified from Natural Earth 1:110m Cultural Vectors (https://www.naturalearthdata.com/)
library(sf) # Plot the built-in world map plot(st_geometry(hexify_world), col = "lightgray", border = "white") # Filter by continent europe <- hexify_world[hexify_world$continent == "Europe", ] plot(st_geometry(europe))library(sf) # Plot the built-in world map plot(st_geometry(hexify_world), col = "lightgray", border = "white") # Filter by continent europe <- hexify_world[hexify_world$continent == "Europe", ] plot(st_geometry(europe))
Ingests H3 cell IDs from an external source (another H3 library, a database, or a CSV file) into hexify. Validates cell IDs, infers the H3 resolution, and optionally attaches data to build a HexData object.
import_h3(cell_ids, data = NULL, validate = TRUE)import_h3(cell_ids, data = NULL, validate = TRUE)
cell_ids |
Character vector of H3 cell ID strings |
data |
Optional data frame to attach. Must have the same number of rows
as |
validate |
If |
For converting between grid specs, use
hex_grid(type = "h3") directly. For cell-level ISEA/H3
mapping, use h3_crosswalk.
H3 cell IDs encode their resolution in the index itself, so no resolution argument is needed. The resolution is inferred automatically. All cell IDs must share the same resolution; mixed resolutions produce an error.
If data = NULL, a HexGridInfo object for the inferred H3
resolution. If data is provided, a HexData object with data
attached at the specified cells.
hex_grid for creating grids directly,
h3_crosswalk for cell-level ISEA/H3 mapping
# Import external H3 cell IDs (grid spec only) h3_ids <- c("8528342bfffffff", "85283473fffffff", "85283447fffffff") grid <- import_h3(h3_ids) grid # Import with data attached df <- data.frame(species = c("oak", "pine", "birch"), count = c(10, 5, 3)) hd <- import_h3(h3_ids, data = df) hd# Import external H3 cell IDs (grid spec only) h3_ids <- c("8528342bfffffff", "85283473fffffff", "85283447fffffff") grid <- import_h3(h3_ids) grid # Import with data attached df <- data.frame(species = c("oak", "pine", "birch"), count = c(10, 5, 3)) hd <- import_h3(h3_ids, data = df) hd
Check if object is HexData
is_hex_data(x)is_hex_data(x)
x |
Object to check |
Logical
Check if object is HexGridInfo
is_hex_grid(x)is_hex_grid(x)
x |
Object to check |
Logical
Identifies which cells are pentagons. Any hexagonal tiling of the sphere must contain exactly 12 pentagons (at the icosahedron vertices). Pentagon cells have 5 neighbors instead of 6.
is_pentagon(cell_id, grid)is_pentagon(cell_id, grid)
cell_id |
Cell IDs to check. Numeric for ISEA, character for H3. |
grid |
A HexGridInfo or HexData object specifying the grid. |
H3 backend: Uses the vendored H3 isPentagon function.
ISEA backend: The 12 pentagons are located at icosahedron vertices. At any resolution, the pentagon cells are those whose IJ coordinates are (0, 0) within the 12 vertex quads (quads 0 and 11 for the poles, and specific cells in quads 1-10).
A logical vector. TRUE for pentagon cells, FALSE for hexagons.
get_neighbors() for neighbor finding (pentagons have 5 neighbors)
# H3 pentagon detection g <- hex_grid(resolution = 1, type = "h3") cells <- grid_global(g) pent <- is_pentagon(cells$cell_id, g) sum(pent) # Should be 12# H3 pentagon detection g <- hex_grid(resolution = 1, type = "h3") cells <- grid_global(g) pent <- is_pentagon(cells$cell_id, g) sum(pent) # Should be 12
Converts geographic coordinates to DGGS cell IDs using a grid specification.
lonlat_to_cell(lon, lat, grid)lonlat_to_cell(lon, lat, grid)
lon |
Numeric vector of longitudes in degrees |
lat |
Numeric vector of latitudes in degrees |
grid |
A HexGridInfo or HexData object, or legacy hexify_grid |
This function accepts either a HexGridInfo object from hex_grid() or
a HexData object from hexify(). If a HexData object is provided,
its grid specification is extracted automatically.
Numeric vector of cell IDs
cell_to_lonlat for the inverse operation,
hex_grid for creating grid specifications
grid <- hex_grid(area_km2 = 1000) cells <- lonlat_to_cell(lon = c(0, 10), lat = c(45, 50), grid = grid) # Or use HexData object df <- data.frame(lon = c(0, 10, 20), lat = c(45, 50, 55)) result <- hexify(df, lon = "lon", lat = "lat", area_km2 = 1000) cells <- lonlat_to_cell(lon = 5, lat = 48, grid = result)grid <- hex_grid(area_km2 = 1000) cells <- lonlat_to_cell(lon = c(0, 10), lat = c(45, 50), grid = grid) # Or use HexData object df <- data.frame(lon = c(0, 10, 20), lat = c(45, 50, 55)) result <- hexify(df, lon = "lon", lat = "lat", area_km2 = 1000) cells <- lonlat_to_cell(lon = 5, lat = 48, grid = result)
Get the number of unique cells in a HexData object.
n_cells(x)n_cells(x)
x |
A HexData object |
Integer count of unique cells
Renders a global hexagonal grid on an orthographic projection with customizable rotation, land clipping, and styling options.
plot_globe( area = 50000, center = "europe", clip_to_land = FALSE, land_data = NULL, exclude_antarctica = TRUE, fill = "#D4B896", border = "grey30", border_width = 0.2, ocean_fill = "white", ocean_border = "grey50", show_land = clip_to_land, land_fill = NA, land_border = "grey40", land_width = 0.3, use_ggplot = NULL, return_data = FALSE, aperture = 3L )plot_globe( area = 50000, center = "europe", clip_to_land = FALSE, land_data = NULL, exclude_antarctica = TRUE, fill = "#D4B896", border = "grey30", border_width = 0.2, ocean_fill = "white", ocean_border = "grey50", show_land = clip_to_land, land_fill = NA, land_border = "grey40", land_width = 0.3, use_ggplot = NULL, return_data = FALSE, aperture = 3L )
area |
Cell area in km^2 (passed to |
center |
Globe center: either a preset name (e.g., "europe") or
numeric vector c(lon, lat). See |
clip_to_land |
If TRUE, clip hexagons to land boundaries |
land_data |
Optional sf object for land boundaries. If NULL and clip_to_land is TRUE, uses rnaturalearth::ne_countries() |
exclude_antarctica |
If TRUE, exclude Antarctica from land clipping |
fill |
Fill color for hexagons (default "#D4B896") |
border |
Border color for hexagons (default "grey30") |
border_width |
Border width for hexagons (default 0.2) |
ocean_fill |
Fill color for ocean/globe background (default "white") |
ocean_border |
Border color for globe circle (default "grey50") |
show_land |
If TRUE, show land boundaries (default TRUE when clipping) |
land_fill |
Fill color for land (default NA, transparent) |
land_border |
Border color for land boundaries (default "grey40") |
land_width |
Border width for land boundaries (default 0.3) |
use_ggplot |
NULL = auto-detect, TRUE = force ggplot2, FALSE = force base |
return_data |
If TRUE, return sf objects instead of plotting |
aperture |
Grid aperture (default 3L) |
The function handles several technical challenges:
Hexagons on the back side of the globe fail to transform - these are filtered out gracefully
Invalid geometries after projection are repaired with st_buffer(0)
Clipping is done in orthographic CRS to avoid topology errors
If use_ggplot = TRUE: ggplot2 object (can add layers with +) If use_ggplot = FALSE: NULL invisibly (plots directly) If return_data = TRUE: list of sf objects (hexagons, land, ocean_circle, crs)
globe_centers for available presets,
grid_global for generating global grids without plotting
# Get data for custom plotting (no rendering) data <- plot_globe(area = 100000, center = "europe", return_data = TRUE) nrow(data$hexagons) class(data$ocean_circle) # Basic usage - Europe-centered globe plot_globe(area = 80000, center = "europe")# Get data for custom plotting (no rendering) data <- plot_globe(area = 100000, center = "europe", return_data = TRUE) nrow(data$hexagons) class(data$ocean_circle) # Basic usage - Europe-centered globe plot_globe(area = 80000, center = "europe")
A convenience function that creates a grid, clips it to a boundary polygon, and plots the result in a single call.
plot_grid( boundary, grid, crop = TRUE, boundary_fill = "gray95", boundary_border = "gray40", boundary_lwd = 0.5, grid_fill = "steelblue", grid_border = "steelblue", grid_lwd = 0.3, grid_alpha = 0.3, title = NULL, expand = 0.05 )plot_grid( boundary, grid, crop = TRUE, boundary_fill = "gray95", boundary_border = "gray40", boundary_lwd = 0.5, grid_fill = "steelblue", grid_border = "steelblue", grid_lwd = 0.3, grid_alpha = 0.3, title = NULL, expand = 0.05 )
boundary |
An sf/sfc polygon to clip to (e.g., country boundary) |
grid |
A HexGridInfo object from |
crop |
If TRUE (default), cells are cropped to boundary. If FALSE, only complete hexagons within boundary are shown. |
boundary_fill |
Fill color for the boundary polygon (default "gray95") |
boundary_border |
Border color for boundary (default "gray40") |
boundary_lwd |
Line width for boundary (default 0.5) |
grid_fill |
Fill color for grid cells (default "steelblue") |
grid_border |
Border color for grid cells (default "steelblue") |
grid_lwd |
Line width for cell borders (default 0.3) |
grid_alpha |
Transparency for cell fill (0-1, default 0.3) |
title |
Plot title. If NULL (default), auto-generates title with cell area. |
expand |
Expansion factor for plot limits (default 0.05) |
This is a convenience wrapper around grid_clip() that handles the
common use case of visualizing a hexagonal grid over a geographic region.
A ggplot object that can be further customized
grid_clip for the underlying clipping function,
hex_grid for grid specification
# Plot grid over France france <- hexify_world[hexify_world$name == "France", ] grid <- hex_grid(area_km2 = 2000) plot_grid(france, grid) # Customize colors plot_grid(france, grid, grid_fill = "coral", grid_alpha = 0.5, boundary_fill = "lightyellow") # Keep only complete hexagons plot_grid(france, grid, crop = FALSE) # Add ggplot2 customizations library(ggplot2) plot_grid(france, grid) + labs(subtitle = "ISEA3H Discrete Global Grid") + theme_void()# Plot grid over France france <- hexify_world[hexify_world$name == "France", ] grid <- hex_grid(area_km2 = 2000) plot_grid(france, grid) # Customize colors plot_grid(france, grid, grid_fill = "coral", grid_alpha = 0.5, boundary_fill = "lightyellow") # Keep only complete hexagons plot_grid(france, grid, crop = FALSE) # Add ggplot2 customizations library(ggplot2) plot_grid(france, grid) + labs(subtitle = "ISEA3H Discrete Global Grid") + theme_void()
Simple wrapper to plot the built-in world map.
plot_world(fill = "gray90", border = "gray50", ...)plot_world(fill = "gray90", border = "gray50", ...)
fill |
Fill color for countries |
border |
Border color for countries |
... |
Additional arguments passed to plot() |
NULL invisibly. Creates a plot as side effect.
Other visualization:
hexify_heatmap()
# Quick world map plot_world() # Custom colors plot_world(fill = "lightblue", border = "darkblue")# Quick world map plot_world() # Custom colors plot_world(fill = "lightblue", border = "darkblue")
Default plot method for HexData objects. Draws hexagonal grid cells with an optional basemap.
## S4 method for signature 'HexData,missing' plot( x, y, basemap = TRUE, clip_basemap = TRUE, basemap_fill = "gray90", basemap_border = "gray50", basemap_lwd = 0.5, grid_fill = "#E69F00", grid_border = "#5D4E37", grid_lwd = 0.8, grid_alpha = 0.7, fill = NULL, show_points = FALSE, point_size = "auto", point_color = "red", crop = TRUE, crop_expand = 0.1, main = NULL, ... )## S4 method for signature 'HexData,missing' plot( x, y, basemap = TRUE, clip_basemap = TRUE, basemap_fill = "gray90", basemap_border = "gray50", basemap_lwd = 0.5, grid_fill = "#E69F00", grid_border = "#5D4E37", grid_lwd = 0.8, grid_alpha = 0.7, fill = NULL, show_points = FALSE, point_size = "auto", point_color = "red", crop = TRUE, crop_expand = 0.1, main = NULL, ... )
x |
A HexData object from |
y |
Ignored (for S4 method compatibility) |
basemap |
Basemap specification:
|
clip_basemap |
Clip basemap to data extent (default TRUE). Clipping temporarily disables S2 spherical geometry to avoid edge-crossing errors. |
basemap_fill |
Fill color for basemap (default "gray90") |
basemap_border |
Border color for basemap (default "gray50") |
basemap_lwd |
Line width for basemap borders (default 0.5) |
grid_fill |
Fill color for grid cells (default "#E69F00" - amber/orange) |
grid_border |
Border color for grid cells (default "#5D4E37" - dark brown) |
grid_lwd |
Line width for cell borders (default 0.8) |
grid_alpha |
Transparency for cell fill (0-1, default 0.7) |
fill |
Column name for fill mapping (optional) |
show_points |
Show original points on top of cells (default FALSE). Points are jittered within their assigned hexagon. |
point_size |
Size of points. Can be:
|
point_color |
Color of points (default "red") |
crop |
Crop to data extent (default TRUE) |
crop_expand |
Expansion factor for crop (default 0.1) |
main |
Plot title |
... |
Additional arguments passed to base plot() |
This function generates polygon geometries for the cells present in the data and plots them. Polygons are computed on demand, not stored, to minimize memory usage.
Invisibly returns the HexData object
hexify_heatmap for ggplot2 plotting
df <- data.frame(lon = runif(50, -5, 5), lat = runif(50, 45, 50)) result <- hexify(df, lon = "lon", lat = "lat", area_km2 = 2000) # Basic plot plot(result, basemap = FALSE) # With basemap and custom styling plot(result, grid_fill = "lightblue", grid_border = "darkblue")df <- data.frame(lon = runif(50, -5, 5), lat = runif(50, 45, 50)) result <- hexify(df, lon = "lon", lat = "lat", area_km2 = 2000) # Basic plot plot(result, basemap = FALSE) # With basemap and custom styling plot(result, grid_fill = "lightblue", grid_border = "darkblue")