How the Transfer Detection Threshold Is Calculated in Google Timeline Visualizer
The transfer detection threshold is calculated dynamically by computing the median distance between consecutive location hops, adding a scaled median absolute deviation buffer, and bounding the result between configurable minimum and maximum limits.
In the mahlernim/google-timeline-visualizer repository, distinguishing between continuous travel and long-distance transfers (such as flights or train rides) requires a data-driven approach. This article breaks down exactly how the transfer detection threshold is calculated from raw Google Timeline data, referencing the specific statistical methods implemented in visualizer.py.
Step-by-Step Algorithm
Extracting Hop Distances
The calculation begins by deriving individual hop distances from the cumulative distance array cum_dist. Each hop represents the linear distance traveled between two consecutive timestamped points in your timeline.
hops = [after - before for before, after in zip(cum_dist, cum_dist[1:])]
Filtering Ordinary Segments
To establish a baseline for "normal" travel, the algorithm filters out hops that are already large enough to be considered transfers. Only hops greater than 0 km and less than MAX_TRANSFER_THRESHOLD_KM are retained as "ordinary" segments for statistical analysis.
ordinary = sorted(hop for hop in hops if 0 < hop < MAX_TRANSFER_THRESHOLD_KM)
Handling the No-Hop Edge Case
If the filtering step yields no ordinary hops—indicating all segments exceed the maximum or the dataset is sparse—the function returns the hard maximum immediately rather than attempting statistical calculations on an empty set.
if not ordinary:
return MAX_TRANSFER_THRESHOLD_KM
Calculating the Typical Hop
The algorithm treats the median of the ordinary hops as the "typical" intra-segment distance. Using the median rather than the mean ensures the baseline remains robust againstGPS noise or short irregular stops.
typical = statistics.median(ordinary)
Measuring Dispersion with Median Absolute Deviation
Rather than standard deviation, the implementation uses median absolute deviation (MAD) to quantify variability. This measures the median of absolute differences between each hop and the typical value, providing a resilient spread metric.
deviation = statistics.median(sorted(abs(hop - typical) for hop in ordinary))
Final Threshold Computation
The final threshold is determined by taking the maximum of three candidate values, then capping the result:
- Hard floor:
MIN_TRANSFER_THRESHOLD_KM(60.0 km) - Proportional baseline:
typical * TRANSFER_TO_TYPICAL_RATIO(3.0× the median) - Variability buffer:
typical + deviation * DEVIATION_MULTIPLIER(median + 2.0× MAD)
threshold = max(
MIN_TRANSFER_THRESHOLD_KM,
typical * TRANSFER_TO_TYPICAL_RATIO,
typical + deviation * DEVIATION_MULTIPLIER,
)
return min(MAX_TRANSFER_THRESHOLD_KM, threshold)
Source Code Reference
The complete threshold calculation logic resides in the transfer_threshold_km function in [visualizer.py at lines 46–58](https://github.com/mahlernim/google-timeline-visualizer/blob/main/visualizer.py#L46-L58). This function accepts a list of cumulative distances and returns the dynamically computed threshold.
The governing constants are defined at lines 81–85:
MIN_TRANSFER_THRESHOLD_KM = 60.0
MAX_TRANSFER_THRESHOLD_KM = 500.0
TRANSFER_TO_TYPICAL_RATIO = 3.0
DEVIATION_MULTIPLIER = 2.0
When segmenting journeys, the build_legs function relies on this threshold calculation to classify segments, invoking the logic at lines 61–70 unless a custom threshold is provided by the caller.
Practical Implementation Example
You can integrate the threshold calculation directly into your data processing pipeline as follows:
from visualizer import transfer_threshold_km, build_legs, cumulative_distances
# Compute cumulative distances from latitude/longitude arrays
cum_dist = cumulative_distances(latitudes, longitudes)
# Calculate the dynamic threshold specific to this dataset's travel patterns
threshold_km = transfer_threshold_km(cum_dist)
print(f"Computed transfer threshold: {threshold_km:.1f} km")
# Build journey legs using the statistically derived threshold
legs = build_legs(cum_dist)
for start_idx, end_idx, is_transfer in legs:
leg_type = "TRANSFER" if is_transfer else "TRAVEL"
print(f"{leg_type}: {cum_dist[start_idx]:.1f}km → {cum_dist[end_idx]:.1f}km")
Configuration Constants
The algorithm's sensitivity is controlled by four constants defined in visualizer.py:
- MIN_TRANSFER_THRESHOLD_KM (
60.0): The absolute floor for transfer classification, preventing short GPS gaps from triggering false positives. - MAX_TRANSFER_THRESHOLD_KM (
500.0): The ceiling for the final threshold and the cutoff for defining "ordinary" hops; also serves as the fallback value. - TRANSFER_TO_TYPICAL_RATIO (
3.0): Multiplier that scales the median hop distance to establish a proportional detection baseline. - DEVIATION_MULTIPLIER (
2.0): Factor applied to the median absolute deviation to create a safety margin above typical travel distances.
Summary
- The transfer detection threshold is calculated statistically from the distribution of hop distances in your specific dataset.
- The algorithm filters extreme values, computes the median typical hop, and measures dispersion using median absolute deviation.
- The final value is the highest of three candidates (60 km minimum, 3× typical, or typical + 2× deviation), capped at 500 km.
- All core logic is contained within the
transfer_threshold_kmfunction invisualizer.py.
Frequently Asked Questions
What happens if no ordinary hops are detected in the dataset?
If all hops exceed MAX_TRANSFER_THRESHOLD_KM or the dataset contains insufficient points, the function returns MAX_TRANSFER_THRESHOLD_KM (500 km) as a safe default. This ensures the visualizer maintains a functional boundary even when statistical calculation is impossible due to sparse or anomalous data.
Why does the implementation use median absolute deviation instead of standard deviation?
The repository uses median absolute deviation (MAD) because it is resistant to outliers that commonly appear in GPS trajectory data, such as signal jumps or brief location spikes. Unlike standard deviation, MAD does not square deviations, preventing extreme values from disproportionately influencing the threshold calculation.
How do the ratio and multiplier constants affect detection sensitivity?
Raising TRANSFER_TO_TYPICAL_RATIO increases the proportional baseline, making the algorithm more conservative by requiring longer distances to flag a transfer. Increasing DEVIATION_MULTIPLIER expands the variability buffer, effectively requiring stronger statistical evidence before classifying a gap as a transfer. Lower values for either constant increase sensitivity and may detect shorter transfers.
Can I override the automatic threshold calculation for specific routes?
Yes. While build_legs automatically invokes transfer_threshold_km when no threshold is provided, you can pass a specific threshold_km argument directly to build_legs. This bypasses the statistical derivation entirely, allowing you to set fixed thresholds for known routes or apply domain-specific rules to specific datasets.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →