Publishing Surveillance Map Products
This guide is part of the Surveillance Pipeline Automation & Spatial Data Quality section. It covers the stage where analysis becomes something other people use — and where every upstream control either holds or is undone.
Concept & Epidemiological Alignment
Publication is where a surveillance pipeline’s responsibilities change shape. Upstream, the concern is correctness: are the counts right, is the cluster real. At publication the concern becomes interpretation and disclosure: will a reader understand what the map shows, and can the published product be combined with anything else to reveal an individual.
Four product types cover most agency needs, and each has a characteristic failure.
Interactive web maps built on vector tiles are the most visible product and the most exposed. The failure is publishing geometry or attributes at a finer resolution than the suppression rules permit, because a tile at a high zoom level is effectively a download of the underlying data.
Partner interfaces give other agencies programmatic access to the same aggregates. The failure is an interface that allows arbitrary queries, which lets a partner reconstruct suppressed cells through differencing — asking for a county total and every tract but one.
Static exports — maps and tables for weekly reports — are the products most often read by decision-makers. The failure is inconsistency: a figure produced by hand each week, with a different classification and a different colour ramp, so that week-over-week comparison is impossible.
Metadata accompanies all of them. The failure is its absence, which leaves a recipient unable to tell a suppressed product from an unsuppressed one, or a provisional count from a final one.
The unifying principle is that publication consumes only the output of the suppression stage, never an intermediate. A map service that reads from the analytic archive, however carefully configured, is one misconfiguration away from serving raw counts.
A further reason publication deserves its own discipline is audience. Analysts inside the agency know that a tract rate is unstable, that the last week is provisional, and that a suppressed area is not an empty one. A council member, a journalist or a worried resident knows none of that, and will read the map literally. Every design decision in this topic is partly a decision about what a literal reading of the product will conclude, and whether that conclusion is one the agency is prepared to stand behind.
Product Types and Their Controls
| Product | Built from | Characteristic risk | Control |
|---|---|---|---|
| Vector tile web map | Suppressed aggregates only | Fine geometry at high zoom | Cap maximum zoom to the suppression geography |
| Partner read-only API | Suppressed aggregates only | Differencing across queries | Fixed geographies, no arbitrary filters, rate limits |
| Static report exports | Suppressed aggregates plus templates | Week-to-week inconsistency | Fixed classification breaks and template per product |
| Downloadable tables | Suppressed aggregates | Complementary suppression gaps | Disclosure review of each released table set |
| Standards metadata | Run provenance | Recipient cannot interpret | Lineage, extent, constraints, provisional status |
Spatial Data Prerequisites
- A published store separate from the analytic archive, written only by the suppression stage.
- Fixed publication geographies — the set of area levels at which suppression was applied — with their boundary vintage recorded.
- Generalised geometry for web display, simplified to a tolerance appropriate to the maximum zoom, with topology preserved between neighbours.
- A web-appropriate coordinate reference system for tiles, applied at the last step so analysis stays in the equal-area working system.
- Classification breaks fixed per product, stored as configuration, so a weekly map uses the same legend every week.
- Provenance for every product from the run that produced it.
Production Implementation
The routine below takes suppressed aggregates, generalises geometry with shared-boundary topology, fixes classification, and writes a tile-ready layer with the zoom ceiling that the suppression geography supports.
# Publication-ready layer from suppressed aggregates with a zoom ceiling and fixed breaks.
# Pinned: geopandas==0.14.4, shapely==2.0.4, pandas==2.2.2, numpy==1.26.4
import json
import logging
import numpy as np
import pandas as pd
import geopandas as gpd
import shapely
logging.basicConfig(level=logging.INFO, format="%(asctime)s %(levelname)s %(message)s")
log = logging.getLogger("publish.layer")
# The finest geography suppression was applied at decides how far a viewer may zoom.
ZOOM_CEILING = {"county": 9, "tract": 12, "block_group": 13}
SIMPLIFY_M = {"county": 200.0, "tract": 25.0, "block_group": 10.0}
def build_publication_layer(suppressed: pd.DataFrame, boundaries: gpd.GeoDataFrame,
level: str, breaks: list[float], provenance: dict,
value: str = "rate") -> tuple[gpd.GeoDataFrame, dict]:
"""Join suppressed values to generalised geometry and attach tile metadata."""
if "suppressed" not in suppressed.columns:
raise ValueError("input is not the output of the suppression stage")
if level not in ZOOM_CEILING:
raise KeyError(f"no publication rules for level {level!r}")
# Topology-preserving simplification of the whole coverage at once, so shared
# boundaries stay shared and no slivers open between neighbours.
b = boundaries.sort_values("geoid").reset_index(drop=True)
coverage = shapely.coverage_simplify(b.geometry.values, SIMPLIFY_M[level])
b = b.set_geometry(gpd.GeoSeries(coverage, crs=b.crs))
layer = b[["geoid", "geometry"]].merge(suppressed, on="geoid", how="left")
missing = int(layer[value].isna().sum() - layer["suppressed"].fillna(False).sum())
if missing > 0:
log.warning("%d areas have no value and are not marked suppressed", missing)
# Fixed classification: the same breaks every week, never recomputed per run.
bins = [-np.inf] + list(breaks) + [np.inf]
layer["class"] = pd.cut(layer[value], bins=bins, labels=False).astype("Int64")
layer.loc[layer["suppressed"].fillna(False), "class"] = pd.NA
layer = layer.to_crs("EPSG:3857") # web display only, at the last step
meta = {"level": level, "maxzoom": ZOOM_CEILING[level], "minzoom": 4,
"breaks": breaks, "run_id": provenance["run_id"],
"config_sha256": provenance["config_sha256"],
"boundary_vintage": provenance["boundary_vintage"],
"provisional_after": provenance.get("provisional_from")}
log.info("publication layer level=%s areas=%d suppressed=%d maxzoom=%d run=%s",
level, len(layer), int(layer["suppressed"].fillna(False).sum()),
meta["maxzoom"], meta["run_id"])
return layer, meta
The zoom ceiling is the control most web-map deployments lack. A tile set built to the default maximum zoom lets a viewer inspect geometry at building scale, and if any attribute travels with that geometry — a rate, a flag — the tile set is a download of the data at a resolution the suppression rules never approved. Capping maximum zoom at the level suppression was applied closes that.
Fixed classification breaks are what make the weekly product comparable to itself. Recomputing quantile breaks from each week’s data means a county can change colour without changing rate, and readers comparing two weeks draw conclusions from the legend rather than the data.
Parameter Selection & Tuning
Simplification tolerance. Enough to make tiles small and fast; not so much that a reader can no longer recognise their own neighbourhood. Coverage-aware simplification is required, because simplifying polygons one at a time opens slivers between neighbours that look like data gaps.
Maximum zoom. Derived from the finest geography suppression was applied to, never from what the tiling tool allows.
Classification scheme. Fixed breaks chosen from a long historical window and reviewed annually suit a recurring product. Quantile breaks suit a one-off exploratory map and nothing else.
Update cadence. A product updated more often than the counts stabilise will show provisional numbers as though final. Match publication cadence to the reporting-delay tail, and mark provisional periods in both the map and its metadata.
Partner interface granularity. Offer the published geographies and nothing else. Every additional filter an interface accepts is a new way to difference two responses.
Edge Cases & Failure Modes
Tiles cached by a content delivery network. A corrected product does not replace tiles already cached at the edge. Version the tile path by run identifier so a correction is a new path rather than an overwrite that some viewers will not see.
Suppressed areas rendered as zero. A null rendered with the lowest colour class tells readers the area had no cases. Suppressed areas need their own distinct legend entry.
Hover tooltips that reveal more than the fill. A map whose fill shows a rate class and whose tooltip shows the count has published the count. Tooltips carry only what the product is approved to publish.
Differencing through the partner interface. A partner requesting the county total and all but one tract recovers the last tract. Complementary suppression must be applied to the whole published set, and the interface must not allow ad hoc geographies.
Rates on tiny denominators that pass suppression. A tract with 40 residents and 2 cases passes a numerator threshold of 1 and displays a rate of 5,000 per 100,000, which dominates the colour ramp and the reader’s attention. Suppression addresses disclosure, not stability. Apply a separate reliability rule — withhold or flag rates whose denominator or relative standard error falls outside a stated bound — and give unreliable rates a visual treatment distinct from both suppressed and reliable ones.
Accessibility of the map itself. A choropleth that encodes meaning only in colour excludes readers with colour vision deficiency and fails on greyscale printouts of the weekly report. Choose ramps that are distinguishable in luminance, include the value in an accessible table beside the map, and provide a text description of the main pattern for screen-reader users.
Static exports built by hand. A weekly map rebuilt manually drifts in colour, legend and extent. Automate the export from a fixed template, as in Automating Static Map Exports for Weekly Reports.
The reliability rule in that list needs its own visual treatment, distinct from both suppression and ordinary values, so a reader can tell the three states apart at a glance:
Compliance & Audit Controls
Every published product must be traceable to a single run, a single suppression pass and a single boundary vintage, and that trace must travel with the product in its metadata. The standards content — lineage, extent, constraints on use, contact and provisional status — is set out in Documenting a Release With ISO 19115 Metadata.
The publication infrastructure is part of the disclosure perimeter. The web map service, the partner interface and the export job should hold credentials only to the published store, never to the analytic archive, so that no configuration error in those services can expose unsuppressed data. Review the set of released tables as a whole under the disclosure process in Privacy-Preserving Spatial Analytics rather than product by product, because differencing attacks combine products.
Production Implementation Checklist
Frequently Asked Questions
Can the web map read directly from our analysis database with a view that applies suppression? It can, and it should not. A view is a configuration, and a single change to it, or a second view created for a different purpose, exposes the archive. A separate published store written only by the suppression stage makes the safe path structural rather than dependent on every future configuration being right.
Why cap zoom if the data are already aggregated to tracts? Because the geometry itself is published at whatever resolution the tiles allow, and some attributes — suppression flags, provisional markers — can still carry information at fine scale. Capping zoom also prevents readers from over-interpreting boundaries, which were never meant to be read at building scale.
How should a correction to a published map be handled? As a new version with its own run identifier, not an overwrite. Publish a correction notice that states what changed and why, keep the superseded version retrievable, and version tile paths so caches cannot keep serving the old product silently.
What should a partner interface allow? Retrieval of the published aggregates at the published geographies for published periods, and nothing else. Arbitrary spatial filters, custom geographies and fine-grained time slicing each create differencing opportunities that the suppression stage never evaluated.
How often should classification breaks be revisited? On a fixed schedule, such as annually, and whenever the distribution of values has moved so far that most areas fall in one class. Record every revision with its date, because a break change is a discontinuity in the product’s visual history.
Related Topics
- Surveillance Pipeline Automation & Spatial Data Quality — the parent section and the pipeline this stage completes.
- Generating Vector Tiles From Suppressed Rate Data — building the web layer with its zoom ceiling.
- Serving a Read-Only Spatial API for Partner Agencies — the partner interface without the differencing hole.
- Small-Count Cell Suppression in Rate Maps — the rules the published store enforces.