Purpose
Compaction is a lossless rewrite of an H3 cell set: wherever a
resolution-r cell's complete set of seven children at resolution r+1 is
present in the set, those seven children are replaced by the single parent
cell. Uncompaction is the exact inverse: every cell in a (possibly mixed-
resolution) set is expanded down to a single stated resolution. Both
operations exist for two reasons — reducing the cell count needed to
represent a target for storage, transmission, or platform target-count
limits, and producing rollup summaries at a coarser resolution without
re-deriving them from source geometry.
Compaction
Compaction operates purely on the H3 index hierarchy, not on the geometry the
cells represent. Given a cell set, it repeatedly checks: for a candidate
parent cell at resolution r, are all seven of its resolution-r+1 children
present in the set? If so, remove all seven children and insert the parent.
This check cascades upward — a newly inserted parent may itself complete
its parent's set of seven, and so on — so a fully compacted set can contain
cells from many different resolutions at once, each representing the
coarsest complete grouping available in that part of the set. A cell set
with no complete sibling groups compacts to itself unchanged; compaction
never removes coverage, it only changes how completely-covered regions are
indexed.
"Complete set of seven children" is a statement about the H3 indexing
hierarchy, established by cellToChildren / cellToParent, not a statement
about the children's polygons tiling the parent's polygon with zero gap or
overlap in physical space. The two agree closely in practice but are not
defined to be identical — see the containment caveat on
mixed H3 resolutions.
Uncompaction
Uncompaction reverses this: every cell in the input, regardless of its
current resolution, is expanded via cellToChildren down to the single
target resolution requested. A cell already at the target resolution passes
through unchanged; a coarser cell is expanded into all of its descendants at
that resolution. The output is always a uniform-resolution set, which is why
uncompaction is the standard first step before running any per-cell
aggregation, area estimate, or comparison against another uniform-resolution
set — see mixed H3 resolutions for why skipping
this step produces double-counted or incomparable results.
The round-trip property
uncompact(compact(S), r) == S
for any cell set S that is already uniform at resolution r. This is the
property that makes compaction safe for storage: compacting a set before
writing it and uncompacting it back to the original resolution on read must
reproduce the exact original set, cell for cell, with no loss and no drift.
This round trip is a tested invariant — property-based tests generate
uniform-resolution cell sets (including adversarial ones seeded near
pentagons and face-crossing cells), compact them, uncompact back to the
original resolution, and assert set equality against the input on every run.
An implementation that fails this property is not an acceptable trade-off,
it is broken.
uncompact(compact(S), r) reproduces S only when r is the resolution S
was uniform at before compaction. Uncompacting a compacted set to a coarser
resolution than the original discards information (folding fine detail
upward loses the finer partition); uncompacting to a finer resolution than
the original manufactures cells that were never in the source set. Always
uncompact to the resolution the set was compacted from unless the intent is
a deliberate resolution change, in which case use
normalizeToResolution and treat it as a
resolution conversion, not a round trip.
Use cases
Compaction is the standard technique for target-count optimization: many platforms cap the number of discrete geo targets accepted per campaign, and a compacted set expresses the same effective geography in far fewer rows whenever the source geometry contains large, uniformly-covered interior regions (a full county polyfilled at resolution 9 compacts to a small number of resolution-6 or resolution-5 cells for its interior, with only the boundary remaining at finer resolution). It is equally the standard technique for reporting rollups: a delivery report aggregated at resolution 6 can be produced directly by compacting resolution-9 delivery data, rather than re-querying source geometry at the coarser resolution.
Algorithm
import { compact, uncompact } from "@/lib/h3/hierarchy";
// Reduce row count for storage / platform target-count limits.
const compacted = compact(uniformResolution9Cells);
// Recover the exact original set — property-tested round trip.
const restored = uncompact(compacted, { resolution: 9 });
// restored is set-equal to uniformResolution9Cells
The same conversion with the Python bindings (h3-py v4):
import h3
# Reduce row count for storage / platform target-count limits.
compacted = h3.compact_cells(uniform_resolution_9_cells)
# Recover the exact original set — round-trip invariant, not a best effort.
restored = h3.uncompact_cells(compacted, 9)
assert set(restored) == set(uniform_resolution_9_cells)
The tested reference implementation in this knowledge base is the
TypeScript in lib/, which asserts this round trip as a property-based
test (including adversarial cell sets seeded near pentagons and
face-crossing cells) rather than checking it once by hand.
Parameters
For compact: none beyond the input cell set — the algorithm always
compacts maximally. For uncompact: the target resolution, which must be
greater than or equal to the coarsest cell present in the compacted input, or
the operation is undefined for any cell coarser than the requested target.
Outputs
compact returns a mixed-resolution cell set, typically substantially
smaller in cell count than the input for geometries with large uniform
interiors. uncompact returns a uniform-resolution cell set at the requested
resolution, set-equal to the pre-compaction input when uncompacted back to
the original resolution.
Edge cases
Parent-child duplicates are what a broken or
partial compaction leaves behind — a set that replaced some but not all of a
sibling group, or that was merged with another set after compaction without
re-checking for completed groups, ends up with both a parent and some of its
children present simultaneously; hasParentChildDuplicate should be run on
any compacted set before it is trusted as fully compacted.
Mixed resolutions are the expected, correct
output shape of compact itself — the presence of multiple resolutions in a
compacted set is not a defect, but it does mean the set must be uncompacted
(or otherwise normalized) before any operation that assumes a uniform
resolution.
Assumptions and limitations
Compaction assumes the input cell set is already uniform at one resolution;
running compact on an already-mixed set (rather than uncompacting first)
is only correct if the input is known to already reflect a valid partial
compaction — otherwise sibling groups that exist across the mixed boundary
may go undetected. Compaction reduces cell count; it does not change the
geography represented, and it provides no benefit when the source geometry
has no large uniformly-covered interior regions (a thin corridor or a
boundary-heavy shape compacts to nearly its original size).
