The grid interface
Use this interface to locate cells, read their geometry and navigate between levels. An AbstractGrid is a finite collection of cells; an AbstractHierarchicalGridSystem defines grids and their hierarchy. levelgrid returns one complete level of a system.
A bare Int addresses a local position in 1:ncells(grid). An AbstractCellIndex identifies a cell and carries its level. Keep that distinction when working with subsets, where local positions change. Grids and cell indices introduces these concepts. The architecture guide explains the design.
What a grid answers
Locate a cell with longitude and latitude in degrees. Geometry methods return unit-sphere points, and cell_area returns steradians. Convert a point to geographic coordinates when the next consumer expects longitude and latitude:
import DiscreteGlobalGrids as DGG
import GeometryOps as GO
sys = DGG.HEALPixSystem()
grid = DGG.levelgrid(sys, 2)
cell = DGG.cellat(grid, 8.5, 47.4)
togeographic = GO.GeographicFromUnitSphere()
centroid_lonlat = togeographic(DGG.cell_centroid(grid, cell))
boundary_lonlat = togeographic.(DGG.cell_boundary(grid, cell))
(; centroid_lonlat, boundary_points=length(boundary_lonlat))(centroid_lonlat = (11.25, 41.810314895778596), boundary_points = 32)The converted pairs are (longitude, latitude) in degrees, with longitude in [-180, 180]. Coordinate conversion does not change the latitude frame; see Choosing a grid for authalic and geodetic data.
ConservativeRegridding.Trees.ncells Function
ncells(grid::AbstractGrid) -> IntThe number of cells in grid, which is the length of its canonical dense order: indices run over 1:ncells(grid).
Required of every AbstractGrid.
This is ConservativeRegridding.Trees.ncells — the same binding, extended here — so any grid is a Trees source without an import or a wrapper.
Must be O(1) and must not change over the lifetime of the grid object.
sourcencells(space::RegridSpace) -> IntReturn the stable cell count in O(1). Cell indices are 1:ncells(space).
DiscreteGlobalGrids.levelgrid Function
levelgrid(sys::AbstractHierarchicalGridSystem, l::Integer) -> AbstractGridThe complete grid of sys at level l: every cell the system has at that level, in the system's canonical dense order.
Derived, with a default, and the entry point every consumer uses. The default is HierarchicalLevelGrid(sys, l), checked against levels — so a system implements the five level-grid primitives below and never writes a grid type. Overriding this is the escape hatch for a system whose grid genuinely carries state beyond (sys, l); none of the seven shipped here does.
system(levelgrid(sys, l)) === sys and level(levelgrid(sys, l)) == l. The grid is a lightweight descriptor, not a materialised cell list — constructing one must not be O(cells).
l outside levels(sys) throws an ArgumentError. For a subset of a level, see PartialGrid.
DiscreteGlobalGrids.cell_boundary Function
cell_boundary(grid::AbstractGrid, c::AbstractCellIndex) -> AbstractVector{<:GO.UnitSphericalPoint}The exact boundary ring of cell c, as points on the unit sphere.
Required of every AbstractGrid.
Contract:
The ring is implicitly closed: the first vertex is not repeated at the end.
cell_polygoncloses it.Vertices are in counter-clockwise order seen from outside the sphere (right-hand rule about the outward normal), so the ring bounds the cell rather than its complement and spherical signed area comes out positive.
Consecutive vertices are joined by great-circle arcs. A boundary that is curved in the system's own chart must be densified here, because every consumer treats the result as a spherical polygon.
Every point is unit-norm to within a few
eps.
The container may be any AbstractVector — a Vector, a static vector, or a lazily computed one. Callers must not mutate it.
DiscreteGlobalGrids.cell_corners Function
cell_corners(grid::AbstractGrid, c::AbstractCellIndex) -> AbstractVector{<:GO.UnitSphericalPoint}The corner vertices of cell c, as points on the unit sphere: the ring cell_boundary returns with every densification vertex left out.
Same contract as cell_boundary otherwise: implicitly closed, counter-clockwise seen from outside the sphere, unit-norm points. The fallback returns cell_boundary itself, which is exact for a system whose chart edges are great-circle arcs; a system that densifies curved edges returns its chart corners here. Callers must not mutate the result.
DiscreteGlobalGrids.cell_centroid Function
cell_centroid(grid::AbstractGrid, c::AbstractCellIndex) -> GO.UnitSphericalPointA representative point of cell c on the unit sphere.
Required of every AbstractGrid.
The point must lie strictly inside the cell, never on its boundary — it is what cellat(grid, cell_centroid(grid, c)) == c is tested against, and what labelling, plotting and nearest-cell code uses as the location of the cell. Systems that can compute a true area centroid should; systems that cannot may return any interior point, and should say which in their own documentation.
DiscreteGlobalGrids.cell_polygon Function
cell_polygon(grid::AbstractGrid, c::AbstractCellIndex) -> GI.PolygonCell c as a GeoInterface polygon on the unit sphere: the form the query predicates, getcell, regridding, and GeometryOps operations under the GO.Spherical() manifold all read a cell through.
Contract:
A
GI.Polygonwith one exteriorGI.LinearRingand no holes.The ring is explicitly closed: the first vertex is repeated as the last.
Vertices are
GO.UnitSphericalPoints in unit-sphere(x, y, z), joined by great-circle arcs.Vertices wind counter-clockwise seen from outside the sphere, so the spherical area of the polygon is positive.
cell_boundary returns the same vertices as an implicitly closed AbstractVector; cell_polygon closes that ring and wraps it. Systems provide cell_boundary; the generic method here builds the polygon from it.
Accepted inputs:
A grid and a typed cell index: a
levelgrid, aPartialGrid, or a standaloneAbstractGrid.A
MultiOrderCellSetand a cell index at any of its levels.A
SubsetIndexedCellhandle from amapneighborssweep in the cell slot, answering for its cell.A local
Intindex, throughgetcell(grid, i).
A grid is required in the first slot; a grid system answers through levelgrid(sys, level(c)).
Longitude/latitude vertices in degrees come from a coordinate transform:
GO.transform(GO.GeographicFromUnitSphere(), cell_polygon(grid, c))DiscreteGlobalGrids.cell_area Function
cell_area(grid::AbstractGrid, c::AbstractCellIndex) -> Float64The true spherical area of cell c, in steradians. Multiply by R^2 for area on a sphere of radius R; using authalic_sphere gives ellipsoidal area for an equal-area DGGS.
The generic fallback computes the spherical area of cell_polygon. Systems must override when the published boundary only approximates the true cell, as for a densified curved boundary, and document the returned quantity. The result is never a planar area.
DiscreteGlobalGrids.cell_extent Function
cell_extent(grid::AbstractGrid, c::AbstractCellIndex) -> Extents.Extent{(:X, :Y)}The longitude/latitude bounding box of cell c, in degrees: the GeoInterface extent of the cell, for interoperating with extent-based machinery that does not know about spheres.
This is a lon/lat rectangle, and it is conservative where a rectangle cannot be exact: a cell containing a pole, or crossing the antimeridian, gets an X span of (-180, 180). It is therefore not the quantity tree pruning uses — that is node_extent, a SphericalCap, which has no such degeneracies. Reach for cell_extent at an interoperability boundary, not in an algorithm.
GlobalRegridding.cellat Function
cellat(grid::AbstractGrid, p::GO.UnitSphericalPoint) -> Union{AbstractCellIndex,Nothing}
cellat(grid::AbstractGrid, lon::Real, lat::Real)The cell of grid containing point p, or nothing if the point is outside the grid's coverage.
The unit-sphere method is the primitive; the (lon, lat) method is a converting wrapper and takes degrees.
nothing is a real answer, not an error: grids need not cover the sphere.
Ties. A shared-boundary point is assigned deterministically per platform to one incident cell. The generic fallback selects the first candidate in canonical id order. Floating-point boundary tests do not guarantee cross-platform bit-identical ties; each system documents its rule. A tie must never select a nonincident cell or return nothing inside coverage.
The generic implementation descends treeify(grid) to a candidate set and then tests point-in-cell; systems with a closed-form inverse projection override it and should say what their tie rule is.
cellat(space::RegridSpace, p::GO.UnitSphericalPoint) -> Union{Int,Nothing}Return the index containing p, or nothing outside coverage. Point-based methods require this optional interface. Assign boundary points consistently to an incident cell.
DiscreteGlobalGrids.system Function
system(grid::AbstractGrid) -> Union{AbstractHierarchicalGridSystem,Nothing}The hierarchical system containing grid, or nothing for a standalone grid. The default is nothing.
See also level.
DiscreteGlobalGrids.level Function
level(c::AbstractCellIndex) -> Int
level(grid::AbstractGrid) -> Union{Int,Nothing}The refinement level of a cell id or grid. level(c) is total on AbstractCellIndex: each id encodes its level without a system or grid.
level(grid) is the level of the system grid grid is drawn from, or nothing for a standalone grid with no hierarchy. See system.
Levels are non-negative, increase with refinement, and are compared with <; the valid range for a system is levels(sys).
Cell size, and choosing a level
cellsize measures a typical cell width in metres; levelfor finds the closest level for a requested width or another dataset's resolution. Both take an over area of interest, within which levelfor measures both sides.
DiscreteGlobalGrids.cellsize Function
cellsize(grid::AbstractGrid; over, radius, samples) -> Float64
cellsize(sys::AbstractHierarchicalGridSystem, l::Integer; over, radius, samples) -> Float64
cellsize(x; over, radius, samples) -> Float64The typical width of a cell, in metres: the side of a square whose area is the median cell area of the grid, laid on a sphere of radius.
x may be a raster (a DimensionalData.AbstractDimArray, measured through its X/Y lookups) or a GlobalRegridding.RegridSpace, which is how a source resolution is put on the same footing as a system's levels.
The number is a median over a sample, not a bound: cell area varies within a level on every system that is not equal-area, and with latitude on a system whose cells are lon/lat boxes. Use cell_area for one exact cell.
Keywords
over = nothing: measure only the cells that meet an area of interest — anExtents.Extentin lon/lat degrees, a GeoInterface geometry, or aSphericalCap, the targetsqueryaccepts. Where cell area varies with position this can differ from the global number by a factor of two. A grid is sampled through its own system; a raster or regridding space is sampled at points spread evenly by area across the area of interest, each located byGlobalRegridding.cellat, so a projected raster needs the inverse chart that call reads. An area of interest that meets no cell ofxthrows anArgumentError.radius = 6.371007180918474e6: the sphere the area is laid on, in metres. The default is the WGS84 authalic radius, so an equal-area system's answer is an ellipsoidal one.samples = 256: how many cells the median is taken over.
See also levelfor, which asks the question the other way round.
DiscreteGlobalGrids.levelfor Function
levelfor(sys::AbstractHierarchicalGridSystem, target; over, radius, samples) -> IntThe level of sys whose cells come closest to target, so that levelgrid(sys, levelfor(sys, target)) is the grid of sys nearest target in resolution.
target is either a cell size in metres (a Real) or anything cellsize measures: a raster, an AbstractGrid, or a GlobalRegridding.RegridSpace.
Levels are compared in ratio rather than in difference, so a target falling between two levels takes the geometrically nearer of the two. A target coarser than every level of sys, or finer than every level, takes that end of levels.
radius and samples follow cellsize. over restricts the comparison to an area of interest: the candidate levels of sys and a raster, grid, or regridding-space target are each measured within it.
A global lon/lat raster asks for a finer level over the Arctic than over the equator.
A
targetin metres has one size everywhere and ignoresover.An
overmeeting no cell of the target throws anArgumentError.
The hierarchy
Larger level numbers mean finer cells. Choose the output needed by your task:
| Operation | Result |
|---|---|
parent(sys, cell) / children(sys, cell) | Immediate coarser / finer relatives |
ancestor(sys, cell, l) | One ancestor at l <= level(cell) |
descendants(sys, cell, l) | All descendants at l >= level(cell) |
descendant_range(sys, cell, l) | Their complete-level positions, only with sorted subtrees |
subtree(sys, cell, l) | A regional grid holding those descendants |
coarse = parent(sys, cell)
leaves = DGG.descendants(sys, coarse, 3)
positions = DGG.descendant_range(sys, coarse, 3)
regional = DGG.subtree(sys, coarse, 3)
@assert length(leaves) == length(positions) == DGG.ncells(regional)
(; parent_level=DGG.level(coarse), fine_cells=length(leaves))(parent_level = 1, fine_cells = 16)descendant_range requires has_sorted_subtrees(sys). A5 does not provide that property. Hierarchical descendants need not geometrically tile their parent; that stronger property is has_congruent_refinement(sys). See Region boundaries to compute on a subtree region.
DiscreteGlobalGrids.levels Function
levels(sys::AbstractHierarchicalGridSystem) -> AbstractUnitRange{Int}The valid refinement levels of sys, coarsest first: first(levels(sys)) is the level of rootcells and last(levels(sys)) is maxlevel.
Required.
A system without an intrinsic depth limit must still bound the range at the deepest level supported by its id encoding and Int cell counts.
DiscreteGlobalGrids.maxlevel Function
maxlevel(sys::AbstractHierarchicalGridSystem) -> IntThe deepest valid level of sys: last(levels(sys)), which is the default implementation.
DiscreteGlobalGrids.rootcells Function
rootcells(sys::AbstractHierarchicalGridSystem)The top-level cells of sys — the base tessellation everything else refines — in ascending canonical order, all at level first(levels(sys)).
Required.
These are the roots the generic tree descent starts from, so this must be a small, cheap collection (12 for HEALPix, 122 for H3, 12 for IGeo7).
sourceDiscreteGlobalGrids.children Function
children(sys::AbstractHierarchicalGridSystem, c::AbstractCellIndex)The immediate children of c — its refinement at level(c) + 1 — in ascending canonical order.
Required, and required to be analytic, for the same reason as parent.
Child count may vary by cell; generic code must not assume it equals the nominal aperture.
Calling it on a cell at maxlevel(sys) throws an ArgumentError.
Base.parent Method
Base.parent(sys::AbstractHierarchicalGridSystem, c::AbstractCellIndex) -> AbstractCellIndexThe parent of c: the cell at level(c) - 1 whose children contain c.
Required, and required to be analytic — index arithmetic on the id, with no lookup table, no allocation and no geometry. Tree descent and ancestry walks call this in inner loops.
Calling it on a root cell (level(c) == first(levels(sys))) throws an ArgumentError; there is no nothing sentinel here, because a caller that descends the hierarchy always knows the level it is at.
parent(sys, c) and children(sys, c) are inverses: c in children(sys, parent(sys, c)).
DiscreteGlobalGrids.ancestor Function
ancestor(sys::AbstractHierarchicalGridSystem, c::AbstractCellIndex, l::Integer) -> AbstractCellIndexThe ancestor of c at level l, for first(levels(sys)) <= l <= level(c).
ancestor(sys, c, level(c)) is c itself, and ancestor(sys, c, level(c) - 1) is parent(sys, c). l > level(c) throws an ArgumentError — an ancestor is never deeper than the cell, and a system with a closed-form answer (drop level(c) - l digits) should override rather than iterate.
DiscreteGlobalGrids.descendants Function
descendants(sys::AbstractHierarchicalGridSystem, c::AbstractCellIndex, l::Integer)Every descendant of c at level l, in ascending canonical order, for level(c) <= l <= maxlevel(sys).
descendants(sys, c, level(c)) is [c]. l < level(c) throws an ArgumentError (uniformly across systems, so generic code can catch it).
The result is a vector of cell IDs; its storage and mutability depend on the system. Some systems materialize the IDs, while CopernicusDEM returns a lazy, read-only vector. Use collect when an owned mutable vector is required. Use descendant_range when available, or border(subtree(sys, c, l)) when only the border is needed.
DiscreteGlobalGrids.descendant_range Function
descendant_range(sys::AbstractHierarchicalGridSystem, c::AbstractCellIndex, l::Integer) -> UnitRange{Int}The contiguous interval of indices in levelgrid(sys, l)'s canonical dense order occupied by the descendants of c at level l.
Available only when has_sorted_subtrees(sys) is true; otherwise there is no method and the call is a MethodError. A system that declares the trait and implements nothing gets an ArgumentError naming both at the first call, rather than that MethodError.
Both directions are required:
every level-
ldescendant ofchas its index in the range, andevery index in the range is a level-
ldescendant ofc.
The range is over valid dense indices, not raw ids, and therefore has no id encoding gaps. It can be intersected with sorted index vectors by binary search.
Sibling ranges are disjoint and partition the parent's range in canonical order.
l < level(c) throws an ArgumentError.
Trees over a grid
treeify exposes a grid as a spatial tree for queries and regridding. Hierarchical grids can use their existing parent/child structure and compute node geometry as the traversal needs it.
ConservativeRegridding.Trees.treeify Function
treeify(grid::AbstractGrid)
treeify(manifold::GeometryOpsCore.Manifold, grid::AbstractGrid)A spatial tree over grid, used for traversal pruning and ConservativeRegridding. This extends ConservativeRegridding.Trees.treeify and is total on AbstractGrid.
The result implements GeometryOps.SpatialTreeInterface. Node extents are GO.UnitSpherical.SphericalCaps at every level of every tree here, which is the whole predicate vocabulary tree descent needs.
A hierarchical grid uses its node_extent hierarchy; other grids use a fallback tree over index space.
The one-argument form picks the manifold with best_manifold(grid).
ConservativeRegridding.Trees.getcell Function
getcell(grid::AbstractGrid, i::Int) -> GI.PolygonThe cell at local index i as a unit-sphere polygon. This extends ConservativeRegridding.Trees.getcell and is implemented as
cell_polygon(grid, cellindex(grid, i))getcell takes a local index; cell_polygon takes an id.
getcell(space::RegridSpace, i::Int) -> GI.PolygonReturn cell i as a GeoInterface polygon with one explicitly closed ring of unit-sphere (x, y, z) coordinates. The ring is counter-clockwise from outside the sphere and its segments are great-circle arcs. Densify non-geodesic edges. Throw BoundsError for an invalid index.
Identifiers
DiscreteGlobalGrids.cellindex Function
cellindex(grid::AbstractGrid, i::Int) -> AbstractCellIndexThe canonical typed id of the cell at local index i in grid's dense order.
Required of every AbstractGrid.
The returned id is of type cellindextype(system(grid)) for a grid that has a system, and of the grid's own canonical id type otherwise. Together with localindex this is a bijection 1:ncells(grid) ↔ the grid's cells:
localindex(grid, cellindex(grid, i)) == i for all i in 1:ncells(grid)i outside 1:ncells(grid) throws a BoundsError.
See also cellindex(grid, i, T), which requests a specific index scheme.
DiscreteGlobalGrids.localindex Function
localindex(collection, c::AbstractCellIndex) -> Union{Int,Nothing}The local index (storage index) of cell c in collection's own dense order, or nothing when absent. It is the inverse of cellindex.
collection is anything that stores cells in an order of its own: a grid, a CellVector, a PartialGrid, a cell lookup. On a complete grid the local index and the globalindex coincide — its storage IS the level — so generic code that means "wherever this collection put it" should ask for the local index and be correct in both cases.
c may be given in any scheme in cellindextypes; it is reindexed to canonical first. A c at a different level than the collection is not an error either — it is simply not held, so the answer is nothing.
Locating a point
localindex(collection, p::GO.UnitSphericalPoint) -> Union{Int,Nothing}
localindex(collection, lon::Real, lat::Real)The local index of the cell containing a point.
nothingwhere the collection covers the point nowhere. Degrees for the(lon, lat)method, as everywhere else.One search where the collection can answer in one: a subset resolves membership while it locates, and keeps the index that produced.
See also globalindex, cellindex.
DiscreteGlobalGrids.globalindex Function
globalindex(collection, c::AbstractCellIndex) -> Union{Int,Nothing}The global index of cell c: its index in the complete grid at c's level, independent of which subset is asking, or nothing when c is not a valid cell of the system.
This is the index space a subset's own storage is carved out of. Asking a CellVector for a global index answers for its underlying grid, so two different subsets of one level agree on it where their localindex values do not. It is the numeric counterpart of cellid: the way to carry a cell between collections without carrying a stale offset.
See also localindex, cellindex.
DiscreteGlobalGrids.LevelIndex Type
LevelIndex(level, index) <: AbstractCellIndexAn id consisting of an explicit level and a system-defined linear index.
The system documents whether index is zero- or one-based. It must increase in canonical cell order; LevelIndex orders lexicographically by (level, index).
Here index is an identity component, not an offset into a collection. Use globalindex for the index in the complete grid and localindex for the index in a subset's own storage.
Construction does not validate level or index ranges. Validation occurs when an id is used with a system or grid.
julia> c = LevelIndex(3, 17); (level(c), rawid(c))
(3, 17)DiscreteGlobalGrids.Engine.cellid Function
cellid(h::SubsetIndexedCell)
cellid(c::AbstractCellIndex)Return the bare cell from an indexed handle, or return a bare cell unchanged. The escape from a handle's single-axis contract: localindex(other, cellid(h)) resolves against any axis, where the handle's own index is valid only against the collection that minted it.
DiscreteGlobalGrids.rawid Function
rawid(c::AbstractCellIndex) -> IntegerThe encoded integer behind a typed cell id: the UInt64 of an H3Cell or Z7Cell, the linear index of a LevelIndex.
This is the value to write to disk, print in hex, or hand to a C library — not an index, and not something to do arithmetic on unless the system documents what the arithmetic means. Round-tripping it back into a typed id needs the id type (and, for LevelIndex-style schemes, the level), so rawid is lossy on its own; prefer passing typed ids.
DiscreteGlobalGrids.reindex Function
reindex(T::Type{<:AbstractCellIndex}, sys::AbstractHierarchicalGridSystem, c::AbstractCellIndex) -> TConvert cell id c to index scheme T within the same system: same cell, same level, different encoding. reindex(typeof(c), sys, c) === c.
Conversion is exact and total between any two schemes the system supports — they are alternative names for the same tessellation, not approximations of it. Requesting an unsupported T throws an ArgumentError naming cellindextypes(sys).
DiscreteGlobalGrids.cellindextype Function
cellindextype(sys::AbstractHierarchicalGridSystem) -> Type{<:AbstractCellIndex}The canonical cell index type of sys: the scheme its grids return from cellindex, its ids sort in, and its neighbour containers have as eltype.
Required. One canonical scheme per system; alternates are declared with cellindextypes and converted with reindex.
DiscreteGlobalGrids.cellindextypes Function
cellindextypes(sys::AbstractHierarchicalGridSystem) -> Tuple{Vararg{Type{<:AbstractCellIndex}}}
cellindextypes(grid::AbstractGrid)The index schemes sys can name its cells in, canonical one first (so first(cellindextypes(sys)) === cellindextype(sys)). Every listed type is reachable through reindex in both directions.
Defaults to (cellindextype(sys),) — one canonical scheme, no conversions.
The grid form asks the grid's system; a standalone grid (system(grid) === nothing) defaults to (typeof(cellindex(grid, 1)),) — the one scheme it demonstrably names cells in (empty grids have no schemes to report).
Grids that stand for a system
levelgrid returns a HierarchicalLevelGrid. AuthalicSystem adapts supported systems to geodetic latitude while preserving cell ids and hierarchy. See Choosing a grid for coordinate guidance and the gallery for the available systems.
DiscreteGlobalGrids.Fallbacks.HierarchicalLevelGrid Type
HierarchicalLevelGrid(sys, level) <: AbstractGridThe complete grid of sys at level: every cell the system has there, in the system's canonical dense order. This is what levelgrid returns unless a system overrides it, and it is to a complete level what PartialGrid is to a subset of one.
It stores the system and the level and nothing else, so constructing one is O(1). The base grid interface is answered by forwarding to system-level counterparts, which are the implementor surface:
| grid method | system method |
|---|---|
ncells(grid) | ncells(sys, level) |
cellindex(grid, i) | cellindex(sys, level, i) |
localindex(grid, c) | globalindex(sys, c) |
cell_boundary(grid, c) | cell_boundary(sys, c) |
cell_centroid(grid, c) | cell_centroid(sys, c) |
The geometry pair takes no level: an AbstractCellIndex carries its own, so the level would be redundant and could disagree with the id. The grid methods supply what the level is needed for — the bounds check on an index, and the rejection of an id from another level, which a complete-level grid must not answer about.
Fast paths stay available: a system attaches them to HierarchicalLevelGrid{TheSystem}, so cellat, neighbors, ring, cell_area and the rest dispatch on the type parameter.
julia> grid = levelgrid(HEALPixSystem(), 2);
julia> grid isa HierarchicalLevelGrid{HEALPixSystem}
trueDiscreteGlobalGrids.Fallbacks.AuthalicSystem Type
AuthalicSystem(sys, ellipsoid = Helpers.WGS84_AUTHALIC) <: AbstractHierarchicalGridSystemRead every level grid's geometry at geodetic latitude. Hierarchical identities, levels, ordering, and descendant ranges are forwarded unchanged.
ellipsoid is read exactly as AuthalicGrid's is. Wrapping an AuthalicSystem throws for the same reason, as does wrapping a system that is geodetic already (see Fallbacks.publishes_geodetic_geometry).
node_extent is recomputed with the analytic authalic_stretch bound because the warp is not an isometry.
DiscreteGlobalGrids.Fallbacks.AuthalicGrid Type
AuthalicGrid(grid, ellipsoid = Helpers.WGS84_AUTHALIC) <: AbstractGridRead grid geometry at geodetic rather than authalic latitude. Cell ids, indices, hierarchy, adjacency, and winding are unchanged. cellat takes its query point in that same geodetic frame.
ellipsoid may be a Helpers.AuthalicTransform or a GeometryOpsCore.Manifold (Geodesic(; semimajor_axis, inv_flattening) for an ellipsoid, or Spherical(; radius) for the identity). Planar and AutoManifold throw. The default is WGS84.
Areas
cell_area returns the warped ring's unit-sphere area, not true ellipsoidal area. For true ellipsoidal area, multiply the base grid's area by Helpers.authalic_radius(ellipsoid)^2.
Wrapping an AuthalicGrid throws; use parent(grid) before changing ellipsoid.
A PartialGrid is also rejected. Wrap its system instead:
PartialGrid(AuthalicSystem(sys), level, ids)A grid whose system publishes geodetic geometry already — A5, Copernicus DEM; see Fallbacks.publishes_geodetic_geometry — is rejected too, because warping it would convert a second time.
See also AuthalicSystem.
The types the contract is stated in
DiscreteGlobalGrids.AbstractGrid Type
abstract type AbstractGridOne finite collection of unit-sphere cells: a complete DGGS level, a regional subset, or a standalone structured grid. A grid need not cover the sphere; treeify's root extent defines its coverage.
Index types and index spaces
A grid defines the canonical dense order 1:ncells(grid). The same cell can be named three ways, differing in what kind of index it is and which space that index counts in:
a cell index — an
AbstractCellIndex: a typed identity, and the only one of the three that names the cell with no collection at all;a local index (a storage index) — a bare
Intinto this collection's own order,1:ncells(grid);a global index — a bare
Intinto the complete grid at that level.
On a complete grid the local and global indices of a cell coincide, because its storage IS the level. On a subset they do not, and code that confuses them reads one cell's data for another. Never store a bare Int without knowing which of the two it is.
The canonical order is the grid's own choice, but it must be stable for the lifetime of the grid object and consistent with cellindex / localindex, which are inverses of each other over it.
Required interface
A grid type writes exactly four methods:
| method | contract |
|---|---|
ncells(grid) | number of cells |
cellindex(grid, i) | local index → canonical cell index |
cell_boundary(grid, c) | exact boundary ring, unit-sphere points |
cell_centroid(grid, c) | representative interior point |
Everything else—localindex, cell_polygon, cell_area, cell_extent, getcell, cellat, neighbors, ring, treeify, and query—is provided generically and may be optimized without changing semantics. The generic localindex scans 1:ncells(grid) linearly, so a grid that can search should override it.
A hierarchical system needs no grid type: it answers this interface with the five level-grid primitives listed under AbstractHierarchicalGridSystem, which HierarchicalLevelGrid forwards to. That list is five rather than four because a linear scan is not an acceptable globalindex for a complete level.
A grid produced by a hierarchical system reports it through system and level; a standalone grid returns nothing from both and stops at the base interface.
See also AbstractHierarchicalGridSystem, AbstractCellIndex.
DiscreteGlobalGrids.Engine.PartialGrid Type
PartialGrid(sys, level, ids; bucket_size = 0, root = nothing)A subset of levelgrid(sys, level). ids must be strictly ascending canonical ids at level and is stored by reference without copying or reordering.
subtree is the constructor for the subtree case, and the one spelling of it.
Keywords
bucket_sizestops descent at that many stored cells;0reaches cells.rootdeclares a common ancestor and starts descent there.
What is checked
Validation covers the level, id type, strict ascent, endpoint levels, and rooted endpoint ancestry. For sorted subtrees, endpoint checks cover the entire vector.
Interior ids are not individually validated; a bad one surfaces at the first geometry call that decodes it.
sourceDiscreteGlobalGrids.AbstractHierarchicalGridSystem Type
abstract type AbstractHierarchicalGridSystemA family of grid levels related by analytic parent/child structure. The system names cells; levelgrid returns a complete level grid. Hierarchical methods accelerate base-grid operations without changing their results.
Required interface
Identity and hierarchy:
| method | contract |
|---|---|
cellindextype(sys) | canonical typed id type |
levels(sys) | valid level range |
rootcells(sys) | top-level cells, ascending |
Base.parent(sys, c) | analytic parent, no lookup tables |
children(sys, c) | analytic children, no lookup tables |
parent is a method on Base.parent, so it is spelled Base.parent(sys, c) at the definition site and reached unqualified from any session.
The five level-grid primitives, which answer the AbstractGrid interface for the complete level:
| method | contract |
|---|---|
ncells(sys, l) | number of cells at level l |
cellindex(sys, l, i) | index → canonical typed id |
globalindex(sys, c) | id → global index, or nothing |
cell_boundary(sys, c) | exact boundary ring, unit-sphere points |
cell_centroid(sys, c) | representative interior point |
maxneighbors(sys, connectivity) sizes the neighbourhood family: neighbors, ring on a subset and adjacency use it for their fixed-capacity containers. It defaults to nothing — no bound declared — and the same verbs then buffer in heap Vectors: identical answers, one allocation per cell. Declaring the bound is the fast path.
Defaults an implementor may override
| method | default |
|---|---|
levelgrid(sys, l) | HierarchicalLevelGrid(sys, l), checked against levels |
node_extent(sys, c) | the cell's bounding cap, inflated — covers descendant geometry, not descendant caps; see the covering law |
cap_inflation(sys) | 1.2 |
maxlevel(sys) | last(levels(sys)) |
has_sorted_subtrees(sys) | false; declaring it true obliges descendant_range |
has_congruent_refinement(sys) | false; true asserts that children tile their parent |
has_direct_location(sys) | false; declaring it true obliges cellat on the level grid |
Grid methods and system methods
Identity and geometry dispatch on the system: the tables above are all (sys, ...) methods, and the level-grid primitives are what HierarchicalLevelGrid forwards the base interface to.
Everything else dispatches on the grid. A system's fast paths — cellat, neighbors, ring, cell_area, treeify, the subtree engines — attach to HierarchicalLevelGrid{typeof(sys)}, for which each system here keeps a local alias:
import DiscreteGlobalGrids as DGG
const MySystemLevelGrid = DGG.HierarchicalLevelGrid{MySystem}
DGG.cellat(g::MySystemLevelGrid, p::DGG.UnitSphericalPoint) = ...Every fast path is optional, and must return what the generic implementation would have returned.
See also AbstractGrid, AbstractQuadFaceGridSystem, node_extent.
DiscreteGlobalGrids.AbstractQuadFaceGridSystem Type
abstract type AbstractQuadFaceGridSystem <: AbstractHierarchicalGridSystemA system whose cells are an aligned 2^level × 2^level lattice on each of nbasefaces(sys) congruent faces, named by the dense 0-based id face * 4^level + curvecode and indexed at id + 1. S2, HEALPix and ISEA4R are that one family.
A subtype inherits every method that identity alone determines: the hierarchy block (rootcells, parent, children, ancestor, descendant_range, descendants), the level-grid arithmetic (ncells, cellindex, globalindex, cellindextype, has_sorted_subtrees), and the subtree engines (border_engine/interior_engine/halo_engine), which read a subtree as the square lattice block it is.
It must still provide its own face layout and projection: levels, maxneighbors, cell_boundary, cell_centroid, node_extent, cellat, one_ring, the lattice codec hooks lattice_decode/lattice_cell/face_orientation, and the three declarations nbasefaces, systemname, idname. A system whose curve carries orientation state also overrides subtree_curve and subtree_orientation.
See also AbstractHierarchicalGridSystem.
DiscreteGlobalGrids.AbstractCellIndex Type
abstract type AbstractCellIndexA typed, self-describing name for one cell of one system.
A cell index is an identity, not an offset into any collection, and names the same cell in complete grids, subsets, or without a grid at all.
Required of every subtype
isbits. Indices must be immutable and allocation-free.
level(c)is total. Every id encodes its level.rawid(c)returns the encoded integer.A total order.
Base.islessmust implement canonical cell order, with consistent==andhash.
One scheme per system is canonical (cellindextype); alternates are reached through reindex and listed by cellindextypes.
Implementation traits and traversal helpers are in the Grid extension reference.