Skip to contents

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.

Usage

hex_grid(
  area_km2 = NULL,
  resolution = NULL,
  aperture = 3,
  type = c("isea", "h3"),
  resround = "nearest",
  crs = NULL,
  radius_km = EARTH_RADIUS_KM,
  orientation = "standard",
  projection = c("isea", "fuller"),
  region = NULL,
  polyhedron = c("icosahedron", "octahedron", "tetrahedron")
)

Arguments

area_km2

Target cell area in square kilometers. Mutually exclusive with resolution.

resolution

Grid resolution level: for ISEA 0-30 for aperture 3, 0-29 for aperture 4, 0-21 for aperture 7 on the icosahedron, where the cell count stays within the 2^63 - 1 cell IDs a 64-bit integer numbers; 0-15 for H3. Mutually exclusive with area_km2. For H3, typical use cases by resolution:

  • 0-3: continental/country scale

  • 4-7: regional/city scale

  • 8-10: neighborhood/block scale (FCC uses 8-9)

  • 11-15: building/sub-meter scale

aperture

Grid aperture: 3 (default), 4, 7, a mixed family such as "4/3", "4/7" or "7/4", or one aperture per resolution level as a vector, e.g. c(4, 4, 7, 3). A family name refines by the first aperture for the first floor(resolution / 2) levels and by the second for the rest, which is how DGGRID arranges ISEA43H. A per-level vector needs resolution rather than area_km2; one shorter than resolution recurs, as OGC Topic 21 reads a list of refinement ratios, so c(4, 3) at resolution 5 refines by 4, 3, 4, 3, 4. It is stored as a comma-separated string with one aperture per level ("4,3,4,3,4"), which aperture also accepts. It names no level finer than resolution, so get_children() needs a family name. Ignored for H3 grids (fixed at 7).

type

Grid type: "isea" (default) or "h3".

resround

Resolution rounding when using area_km2: "nearest" (default), "up", or "down".

crs

Coordinate reference system: an EPSG code, or a 'PROJ' or 'WKT' string. Defaults to 'WGS84' on Earth, and to a longlat CRS on the sphere of radius_km on any other body, which has no EPSG code to name it.

radius_km

Radius of the body the grid covers, in kilometers, or the name of a body: "mercury", "venus", "earth" (default), "moon", "mars", "ceres", "jupiter", "io", "europa", "ganymede", "callisto", "saturn", "enceladus", "titan", "uranus", "neptune", "pluto".

orientation

Where the icosahedron of an ISEA grid sits on the sphere: "standard" (default), "dymaxion", "gosper", "random", "region", "face", or c(vert0_lon, vert0_lat, azimuth) in degrees. See the Orientation section. H3 fixes its own orientation, so H3 grids take only "standard".

projection

How an ISEA-family grid projects each icosahedron face onto its plane triangle: "isea" (default), Snyder's equal-area projection, or "fuller", Fuller's projection. See the Projection section. H3 fixes its own projection, so H3 grids take only "isea".

region

For orientation = "region" or "face", the area to centre the grid on: c(lon, lat) in degrees, or an sf, sfc or bbox object, whose spherical centroid is used.

polyhedron

The solid an ISEA-family grid is built on: "icosahedron" (default) or "octahedron". See the Polyhedron section. H3 grids are built on the icosahedron and take only the default.

Value

A HexGridInfo object containing the grid specification.

Details

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 on the icosahedron, 4 * aperture^res + 2 on the octahedron) 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: hexagon area varies by about a factor of 2 within a resolution, and by about 2.4 once the twelve pentagons are counted. The variation follows position on the icosahedron rather than latitude, with the smallest cells near face centres.

Other Bodies

A grid is a partition of the sphere, and radius_km sets the sphere it is measured on. Cell geometry – which cell a coordinate lands in, where cell centres and corners sit, the hierarchy, the neighbours – is angular and identical on every body; the radius sets the kilometer figures: cell area, diagonal, spacing, and the resolution that area_km2 picks. Earth's area comes from the 'WGS84' ellipsoid, every other radius gives the sphere area 4pir^2.


mars <- hex_grid(area_km2 = 1000, radius_km = "mars")
hex_grid(resolution = 8, radius_km = 3389.5)   # the same grid

Both backends take a radius. 'H3' reports a cell's area as its solid angle times Earth's radius squared, so another radius scales those areas by the square of the radius ratio, exactly. One caveat carries: an 'H3' cell ID names a position in 'H3”s topology, which 'Uber”s 'H3' reads on Earth, so the IDs of a grid on another body are that topology on that body and are not interchangeable with Earth 'H3' data.

Orientation

An ISEA grid is built on a solid, an icosahedron unless polyhedron names another, and its orientation places that solid on the sphere: vertex 0 at longitude vert0_lon and latitude vert0_lat, and vertex 1 at azimuth azimuth seen from vertex 0, all in degrees, as DGGRID's dggs_vert0_lon, dggs_vert0_lat and dggs_vert0_azimuth do.

  • "standard" is the ISEA orientation on the icosahedron: vertex 0 at 11.25E, 58.28N, azimuth 0, symmetric about the equator, with eleven of the twelve pentagons over the oceans and one in Sichuan (Sahr et al. 2003). On the octahedron it puts vertices at both poles and at longitudes 0, 90E, 180 and 90W on the equator.

  • "dymaxion" is Fuller's orientation of the icosahedron for his Dymaxion map: vertex 0 at 5.2454W, 2.3009N, azimuth 7.46658 (Sahr et al. 2003), with all twelve pentagons in the ocean. With projection = "fuller" the grid lies on Fuller's Dymaxion map.

  • "gosper" places the octahedron as Van de Sande's Gosper World places its poles, at midpoints of octahedron edges: vertex 0 at 21.25W, 45N, vertex 1 across the north pole from it, all six vertices at least 619 km offshore. net_layout lays it out as four hexagons. The standard octahedron puts the poles at vertices instead, as Rus's four-hexagon map does.

  • "random" draws vertex 0 uniformly on the sphere and the azimuth uniformly in [0, 360), from R's random number generator, so set.seed() repeats it. Grids in several random orientations show how much a result depends on where the cell boundaries fall.

  • "region" places the grid as DGGRID's REGION_CENTER does: the centre of region lands on the midpoint of an edge of the solid, the middle of the two faces sharing it, far from the vertices where the vertex cells and the largest distortion sit.

  • "face" places the centre of region on the centre of a face. On the icosahedron that is about 20.9 degrees from the nearest face edge and 37.4 degrees from the nearest vertex, so a region up to about 40 degrees across lies on one face, away from the face edges where the projection bends.

Rotating the icosahedron rotates the grid with it, so cell IDs, the cell hierarchy and neighbours are the same under every orientation; only where each cell sits on the sphere changes. Every function taking the grid reads its orientation.


alps <- hex_grid(area_km2 = 100, orientation = "region", region = c(10, 46.5))
hex_grid(resolution = 8, orientation = c(0, 90, 0))   # vertex 0 at the pole

Projection

An ISEA-family grid lays its cells out on the plane triangles of the icosahedron's faces, and the projection carries each spherical face onto its triangle. With "isea", Snyder's icosahedral equal-area projection, every cell of a resolution has the same area. With "fuller", Fuller's projection keeps lengths along the face edges and is not equal-area, so area_km2 is the mean cell area and cell_area() reports each cell's own; the Equal-Area Earth Reference System of OGC Topic 21 asks for cells of equal area, so a Fuller grid falls outside it. These are DGGRID's dggs_proj ISEA and FULLER, giving FULLER3H, FULLER4H, FULLER7H and FULLER43H. Cell IDs, the hierarchy and neighbours are the same under both projections; where each cell's centre and corners sit on the sphere differs.


hex_grid(resolution = 5, aperture = 4, projection = "fuller")   # FULLER4H

Polyhedron

Snyder's equal-area projection is defined on every regular solid with triangular faces, and a hexagonal grid lies on the icosahedron and on the octahedron alike: each pair of faces sharing an edge forms a diamond of cells, and the cell at each vertex has one side per face meeting there. The icosahedron has twelve pentagons and the octahedron six squares; every other cell is a hexagon, and every cell of a resolution has the same area (the vertex cells two thirds or five sixths of it, by their sides). The octahedron's cells are more distorted than the icosahedron's, so it suits layouts that need its four-fold symmetry rather than analysis. Fuller's projection is defined on the icosahedron only. The tetrahedron carries the face projection (see hexify_forward) but no grid: its faces do not pair into diamonds.


hex_grid(resolution = 5, aperture = 4, polyhedron = "octahedron")

One Grid, Many Datasets

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

See also

hexify for assigning points to cells, HexGridInfo-class for class documentation

Examples

# 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")

# Mix in aperture 7, either as a family or level by level
grid47 <- hex_grid(area_km2 = 1000, aperture = "4/7")
grid_seq <- hex_grid(resolution = 4, aperture = c(4, 4, 7, 3))

# Grid on another body, by name or by radius
mars <- hex_grid(area_km2 = 1000, radius_km = "mars")
titan <- hex_grid(resolution = 6, radius_km = 2574.76)

# 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)