Territory
Editorial fixed-zone territory chart. Use it when the story should be readable in two seconds, including repeated-zone grids where each cell needs a fast editorial read rather than an exact-count analysis surface. The hero uses real touch data from a WhoScored-derived fixture.
Interactive props.
Canonical behaviors.
More granular vertical zones preserve the editorial read while giving the attacking axis more detail.
When the story still wants editorial clarity but should map to benchmark tactical zones, use the shared positional preset instead of the uniform grids.
Same fixed-zone model rotated for wide layouts.
Territory works well in repeated editorial grids because the 5×3 or 3×3 zone read survives compact cards without needing a shared-domain story.
Useful when the upstream data stream mixes teams but the chart wants one side.
Badge labels keep centered percentages readable when the graphic wants a stronger broadcast treatment.
Territory is supported by the static export pipeline. This is the right path for share cards and report snapshots.
Single-zone dominance is a useful stress case because the chart should resolve into one 100% cell cleanly.
No zones lit and no misleading percentages when nothing is plottable.
Shared concerns.
Choose Territory
Use Territory when the chart needs an editorial, instantly readable zone
summary. Use
Heatmap for exact bins and KDE for a smoothed estimate.
Heatmap Vs Territory
Both can now use the same named zone presets. Choose Territory when the
output should privilege share labels and editorial speed; choose
Heatmap when the user needs hover inspection or exact bin counts.
Labels
offset keeps the graphic quiet by moving the label away from pitch lines.
badge keeps labels centered and readable over dense markings.
Themeability
Pitch theming comes from Stadia. Dark ramps automatically switch pitch lines to a light contrast treatment unless disabled.
Export
Territory is supported by the Phase 1 static export path. Labels-off is still useful for thumbnails, but full static snapshots are already supported.
Filtering And Crop
teamFilter runs before the binning step, and half-pitch crop renormalizes
the percentages against only the visible events.
Normal, small, smallest.
Pitch-based charts scale fluidly to whatever width the parent gives them. The 'smallest' cell is sized for a 5×4 small-multiples grid (~140px wide). Vertical orientation only — horizontal pitches are too wide for small-multiples.
`grid` and the exported `TerritoryGrid` type now describe only the uniform editorial layouts (`"3x3"` and `"5x3"`). Tactical 18 / 20 zone layouts moved to `zonePreset` / `TerritoryZonePreset` so the API makes the distinction explicit.
Best-practice examples.
Minimal usage
Use Territory when the chart should answer 'where did the team play?' in one glance.
import { Territory } from "@withqwerty/campos-react";
type EventPoint = {
x: number | null;
y: number | null;
};
type Props = {
events: EventPoint[];
};
export function TeamTerritory({ events }: Props) {
return <Territory events={events} metricLabel="touches" />;
}
Badge-label variant
Grid choice and label treatment are the main first-class customizations here.
import { Territory } from "@withqwerty/campos-react";
type EventPoint = {
x: number | null;
y: number | null;
team?: string;
};
type Props = {
events: EventPoint[];
};
export function TeamTerritoryBadgeLabels({ events }: Props) {
return (
<Territory
events={events}
grid="5x3"
metricLabel="passes"
labelStyle="badge"
/>
);
}
Repeated-grid territory cell
Territory now belongs in the official SmallMultiples story as the editorial repeated-zone option.
import { Territory } from "@withqwerty/campos-react";
type EventPoint = {
x: number | null;
y: number | null;
};
type Props = {
events: EventPoint[];
};
export function TeamTerritoryCell({ events }: Props) {
return (
<Territory
events={events}
grid="5x3"
attackingDirection="right"
metricLabel="passes"
labelStyle="badge"
framePadding={8}
/>
);
}
Public surface.
| Prop | Type | Default | Description |
|---|---|---|---|
events | readonly { x: number | null; y: number | null; team?: string }[] | required | Array of positional events. `team` is optional, but required if you want to use teamFilter. |
grid | "3x3" | "5x3" | "3x3" | Compatibility grid API for uniform editorial layouts. |
zonePreset | "3x3" | "5x3" | "18" | "20" | — | Shared named pitch-zone preset. Overrides grid when supplied and unlocks positional 18 / 20 zone layouts. |
attackingDirection | "up" | "down" | "left" | "right" | "up" | Controls which direction of play the pitch projects toward on screen. Territory defaults to the editorial vertical convention. |
crop | "full" | "half" | "full" | Pitch crop. Half renormalizes the percentages against the cropped total. |
showLabels | boolean | true | Toggles the in-cell percentage labels. |
labelStyle | "offset" | "badge" | "offset" | Controls whether labels dodge pitch lines or sit on a pill background. |
teamFilter | string | — | Case-sensitive filter against event.team before the binning step. |
colorScale | "magma" | "viridis" | "inferno" | "blues" | "greens" | "custom" | "magma" | Sequential ramp shared with Heatmap. |
metricLabel | string | "events" | Noun used in accessible labels and cell descriptions. |
pitchTheme | "primary" | "secondary" | "primary" | Delegated Stadia surface preset. |
pitchColors | PitchColors | — | Direct pitch color overrides for branded or editorial surfaces. |
ariaLabel | string | auto | Override the section-level accessible label. |
cells | TerritoryCellsStyle | — | First-class styling surface for territory cells. |
labels | TerritoryLabelsStyle | — | First-class styling surface for in-cell percentage labels. |
Create a React component using Campos Territory. Import Territory from @withqwerty/campos-react, keep data parsing outside the component, show the smallest good usage first, then show one editorial variant and one compact repeated-grid territory cell.