All sections

Requested Vs Executed Geography

A campaign geography passes through six distinct forms between the ask and the outcome, and most reporting disputes trace back to two of those forms being silently conflated.

stableh36 min read

"We targeted the county" is a sentence that hides at least four decisions: what geometry represented the county, at what H3 resolution and containment rule it was polyfilled, what shape a delivery platform actually executed, and what geography the delivery report and the outcome attribution used to count results. Each of those is a distinct object. Treating them as one object — "the geography" — is why coverage numbers, delivered-impression counts, and lift estimates disagree without an obvious cause.

Six named forms

Requested
The buyer's ask, in the buyer's vocabulary: a DMA id, a 3-mile ring around a store, a named trade area. Frequently an identifier, not a geometry — see the geometry catalogue.
Source
The concrete geometry supplied to stand in for the request: a shapefile ring, a GeoJSON polygon, a list of store points with radii. Already a choice, and already lossy relative to the request.
Normalized
The source geometry after CRS detection, reprojection to EPSG:4326, ring closure, winding correction, and self-intersection flagging. See geometry normalization.
Canonical H3
A set of H3 cells at a stated resolution under a stated containment rule (center, full, intersect, or threshold). The interchange form every downstream conversion starts from.
Executed
What actually runs on a platform: point+radius circles, a simplified polygon, or a crosswalked platform-native id. An approximation of the canonical H3 set, never identical to it.
Reported / attributed
The geography a delivery report or an outcome-attribution join actually uses — often coarser than execution, and sometimes a different geometry family entirely.

Lifecycle

flowchart LR
  Req["Requested"] --> Src["Source"]
  Src --> Norm["Normalized"]
  Norm --> H3c["Canonical H3"]
  H3c --> Exec["Executed"]
  Exec --> Rep["Reported"]
  Rep --> Attr["Attributed"]

Every arrow is a documented conversion elsewhere in this knowledge base, and every arrow can change the geography's extent, resolution, or family. The model exists so a claim about any one stage can be checked against the adjacent stages rather than assumed to equal them.

Six concrete divergences

(a) An H3 cell executed as an outer circle. A DSP that only accepts point+radius targets receives the circumscribed circle of each canonical cell. The circumscribed disk strictly contains the cell — every point of the cell lies inside the disk — but it also covers ground outside the cell, and adjacent cells' circles overlap each other. The executed footprint is measurably larger than, and self-overlapping relative to, the canonical H3 set. Reporting "we targeted the cell" without stating the circle mode hides both the overreach and the double-eligibility.

This page has no ts algorithm block of its own — the conversions it narrates are each documented (and coded) on their own page. A short illustration with the Python bindings (h3-py v4) of just case (a), turning a requested cell set into executed circles:

import h3

def cell_to_circumscribed_circle(cell: str) -> tuple[tuple[float, float], float]:
    center = h3.cell_to_latlng(cell)
    # Radius = greatest great-circle distance from center to any boundary
    # vertex — the smallest circle that still fully contains the cell.
    radius = max(
        h3.great_circle_distance(center, vertex, unit="m")
        for vertex in h3.cell_to_boundary(cell)
    )
    return center, radius

requested_cells = h3.grid_disk("872830829ffffff", 1)
executed_circles = [cell_to_circumscribed_circle(c) for c in requested_cells]
# requested_cells: 7 discrete, non-overlapping hexagons.
# executed_circles: 7 overlapping disks whose union is strictly larger than
# the hexagons' union — requested != executed.

The tested reference implementation in this knowledge base is the TypeScript circumscribedCircle in lib/h3/circles, which derives the radius from a densified boundary (denser sampling near pentagons and face-crossing cells) rather than the six raw vertices used above for illustration.

H3 cells requested, but executed as circumscribed circles: the executed footprint overlaps and exceeds the cells.
Rendered from the tested conversion code · H3 cells requested, but executed as circumscribed circles: the executed footprint overlaps and exceeds the cells.

(b) A DMA polyfilled into H3. A Nielsen DMA polygon center-polyfilled at resolution 7 drops boundary cells whose centers fall just outside the DMA line, and — because DMA lines rarely align with H3 cell edges — a center-contained cell set will disagree with the DMA polygon along its entire perimeter, not just at a few points. The canonical H3 set is a genuine partition of the H3 grid, but it is not the DMA; it is the DMA as seen through one specific containment rule at one specific resolution.

(c) H3 mapped back to postal codes. Crosswalking the canonical cell set to ZIP codes for a platform that only accepts postal targeting introduces a second lossy hop: ZIP codes are USPS delivery routes, not polygons (an identifier is not a geometry), so the "ZIP polygon" used is itself a third-party ZCTA approximation. Two independent approximation errors compound — H3-to-DMA, then DMA-cells-to-ZIP — and neither is visible in a report that just says "targeted by ZIP."

(d) An exclusion the platform cannot express. The request is "include the metro, exclude the stadium." If the platform lacks exclusion support, the correct engineering response is to pre-subtract in cell space — effectiveGeography(inclusions, exclusions, res) — and target only the difference; the wrong response is to submit the inclusion alone and let the exclusion silently vanish. See unsupported exclusions: the reported geography and the executed geography will match in that failure mode, which is precisely what makes it dangerous — nothing downstream flags a mismatch, because the platform faithfully executed what it was given.

(e) Reporting only at state level. Execution ran at resolution 8 circles; the platform's reporting API only breaks delivery out by state. The attributed geography is now two resolution steps coarser than the canonical geography and a different geometry family (admin polygon vs. H3-derived circles) than the executed geography. Any lift measured against state-level delivery is measuring a geography that was never actually targeted.

(f) Cell-level experiment assignment diverging from delivery. A geo-experiment assigns treatment and control at the cell level using inscribed circles to guarantee no spill between neighboring cells. If the media platform instead delivers on circumscribed circles for reach, the executed footprint spills into neighboring cells that the experiment design assumed were clean control — contaminating the read without any single stage being "wrong" in isolation.

Provenance has to survive every hop

Each conversion above should append to a ConversionRecord, never overwrite the one before it. At minimum the record needs the source CRS and boundary vintage, the normalization actions taken, the H3 resolution and containment mode, the execution approximation mode (which circle, which simplification tolerance, which crosswalk vintage), and which inclusions or exclusions were dropped because a platform could not express them. Losing any one field converts a checkable claim ("intersect-fill polyfill at resolution 8, executed as circumscribed circles rounded to platform radius increments") into an unfalsifiable one ("we targeted the area").

Stale boundaries compound silently

Case (b) and (c) both depend on a boundary vintage. A DMA or ZCTA crosswalk built from a two-year-old file will misassign cells near any line that has since moved — see stale boundaries — and that misassignment looks identical, in the data, to a correct crosswalk on an outdated boundary. The only defense is recording validFrom/validTo on every crosswalk and refusing to join across a vintage gap.

Tip

Whenever a sentence about geography could be replaced by one of the six rows above, replace it. "Targeted the county" becomes "targeted the resolution-8 intersect-fill polyfill of the 2024-vintage county boundary, executed as circumscribed circles." The second sentence is checkable; the first is not.

Edge cases affecting this page
  • - 'Include A minus B' cannot be expressed on platforms without exclusion support; the exclusion is silently dropped.
  • - Admin/postal/DMA boundaries change; using an old vintage misassigns cells.