Sizing Mix Zones at Road Intersections
The radius that holds concurrent devices is , where is the measured arrival rate and the mean speed through the intersection. Size it against the quietest hour the service runs, not the peak — a mix zone sized for rush hour is an empty zone at three in the morning.
Core Calculation permalink
Little’s law relates the expected number of devices in the zone to the arrival rate and the time each spends inside:
For a roughly circular zone of radius crossed at mean speed , the mean transit path is the mean chord, which for a uniform distribution of crossing directions is . Practitioners commonly use the diameter as a slightly conservative approximation, giving
Inverting for the radius that achieves a target occupancy :
The occupancy sets the entropy ceiling at , so the target follows from the entropy floor you intend to enforce: at best, and in practice somewhat more, because measured entropy always falls short of the ceiling.
Worked numeric example permalink
A four-way urban intersection, evening off-peak band (21:00–22:00):
- measured arrivals: 640 vehicles per hour →
- measured mean speed through the junction: (31 km/h)
- entropy floor: bits → ceiling occupancy ; target for headroom
At that radius the mean transit time is s, so a device is silent for about half a minute. Now check the quietest band, 03:00–04:00, where arrivals fall to 40 per hour () and mean speed rises to 12 m/s:
A 3.3 km mix zone is not a mix zone; it is a hole in the service. The correct conclusion is that this intersection cannot support a 2-bit floor overnight, and the options are to merge several intersections into one larger zone for that band, to suppress reporting entirely, or to accept a documented lower floor.
Python Implementation permalink
from __future__ import annotations
import numpy as np
import pandas as pd
def size_mix_zone(
crossings: pd.DataFrame,
target_occupancy: int,
max_radius_m: float = 400.0,
band_col: str = "hour",
) -> pd.DataFrame:
"""Required mix-zone radius per time band, from measured flow and speed.
Sizing must be evaluated per band: the arrival rate at an urban intersection
varies by more than an order of magnitude over a day, and the radius scales
inversely with it.
Args:
crossings: one row per observed crossing, with columns
`band_col` (int hour), `speed_mps` (float, measured in the junction).
target_occupancy: concurrent devices required; the entropy ceiling is
log2 of this, so pick it above 2**H_min to leave headroom.
max_radius_m: operational ceiling; bands needing more cannot use this zone.
Returns:
One row per band with the required radius, the implied silent period,
and whether the band is feasible at this location.
"""
if target_occupancy < 2:
raise ValueError("a mix zone needs at least two concurrent devices")
out = []
for band, grp in crossings.groupby(band_col):
lam = len(grp) / 3600.0 # arrivals per second
v_bar = float(grp["speed_mps"].mean())
if lam <= 0 or not np.isfinite(v_bar) or v_bar <= 0:
continue
radius = target_occupancy * v_bar / (2.0 * lam)
out.append({
band_col: band,
"arrivals_per_h": len(grp),
"mean_speed_mps": round(v_bar, 2),
"required_radius_m": round(radius, 1),
"silent_period_s": round(2.0 * radius / v_bar, 1),
"entropy_ceiling_bits": round(float(np.log2(target_occupancy)), 2),
"feasible": bool(radius <= max_radius_m),
})
return pd.DataFrame(out).sort_values(band_col).reset_index(drop=True)
Verification permalink
Sizing from a formula is a starting point; the zone has to be verified against the observed crossings, because Little’s law assumes a steady state that a signalised junction does not have.
def observed_occupancy(entries_s: np.ndarray, exits_s: np.ndarray) -> dict:
"""Concurrent occupancy from real entry/exit timestamps, not from the model."""
events = np.concatenate([
np.stack([entries_s, np.ones_like(entries_s)], axis=1),
np.stack([exits_s, -np.ones_like(exits_s)], axis=1),
])
events = events[np.argsort(events[:, 0], kind="stable")]
occupancy = np.cumsum(events[:, 1])
return {
"min_occupancy": int(occupancy.min()),
"median_occupancy": float(np.median(occupancy)),
"frac_time_below_2": float((occupancy < 2).mean()),
"frac_time_below_target": float((occupancy < 6).mean()),
}
The field that decides the design is frac_time_below_2: the share of the band during which the zone holds fewer than two devices and therefore performs no mixing at all. A zone whose median occupancy is six and whose frac_time_below_2 is 0.3 spends nearly a third of the band doing nothing, and the arrivals during those windows are exactly the ones a signalised junction clusters.
Edge Cases & Adjustments permalink
- Signalised junctions. Platoon arrivals make occupancy bursty. Either extend the silent period beyond the physical crossing so that devices from consecutive platoons overlap inside the zone, or size against the observed
frac_time_below_2rather than the mean. - Asymmetric approaches. A junction where 90% of traffic uses one through-movement offers little assignment ambiguity even at high occupancy. Weight the target occupancy by the number of distinct turn movements actually used.
- Very low speeds. In congestion falls, which shrinks the required radius — helpfully — but also lengthens the silent period. A 143 m zone at 2 m/s means 143 s without a position, which many navigation services cannot tolerate.
- Merging adjacent junctions. When no single intersection is feasible for a band, a zone spanning several is usually better than abandoning the control. It costs a longer silent period and buys a much wider transit-time distribution, which raises the achieved entropy more than the occupancy alone would suggest.
- Pedestrian and micromobility flows. Speeds differ by an order of magnitude across modes, so a mixed-mode zone should be sized per mode and the smallest feasible radius taken. Mixing a pedestrian and a vehicle in one zone rarely produces ambiguity, because transit times separate them.
FAQ permalink
Should I size for the peak or the trough?
For the quietest band during which the service reports. A zone sized for the peak is correct for the peak and empty overnight, which is when a single crossing is most identifying — the pattern shown in the per-band entropy series on the mix zones topic page.
Is a larger zone always better?
No. Beyond the point where occupancy is comfortable, extra radius buys only a longer silent period, and the silent period is what the application pays for. The right size is the smallest one that clears the entropy floor in the band.
What if the intersection is a roundabout?
Roundabouts are usually better mix zones than signalised junctions: they have more turn movements, more variable transit times, and no platooning. Size them the same way and expect a smaller radius for the same occupancy.
How does this interact with temporal cloaking?
They compose well. A wider transit-time distribution is exactly what temporal cloaking produces artificially, so adding jitter to the exit timestamps raises the achieved entropy of a zone whose physical transit times are too tight.
Related permalink
- Mix Zones & Path Confusion for Location Services — the definition and the entropy the radius is chosen against
- Measuring Mix-Zone Entropy for Location Queries — verifying what the sized zone actually delivers
- Temporal Cloaking & Time Obfuscation — widening transit times when the geometry cannot
- Trajectory Anonymization Techniques — the batch-release alternative