How Google Timeline Visualizer Detects Long Hops and Transfers

The visualizer detects long hops by calculating timestamp gaps exceeding 43,200,000 milliseconds (12 hours) and identifies transfers by comparing deviceId values between consecutive location points in visualizer.py.

The Google Timeline Visualizer analyzes Google Takeout location history JSON to automatically surface long hops (extended temporal gaps) and transfers (device switches) on an interactive map. These detection algorithms run during the backend parsing phase before the frontend renders visual indicators.

Backend Detection Logic in visualizer.py

The core detection mechanism resides in visualizer.py, which processes the raw location history array chronologically. The parser maintains a reference to the previous point while iterating through the sorted dataset, applying two distinct heuristics to flag anomalous transitions.

Long Hop Detection via Timestamp Thresholds

The visualizer defines a hardcoded constant MAX_GAP_MS set to 12 * 60 * 60 * 1000 (12 hours in milliseconds). During iteration, the parser calculates the delta between the current point's timestamp and the previous point's timestamp. When this difference exceeds the threshold, the current point is flagged with is_long_hop = True.


# visualizer.py

MAX_GAP_MS = 12 * 60 * 60 * 1000  # 12 hours in milliseconds

processed = []
prev = None

for point in sorted_locations:
    if prev:
        delta = point.timestamp_ms - prev.timestamp_ms
        if delta > MAX_GAP_MS:
            point.is_long_hop = True  # Flag for frontend rendering

    processed.append(point)
    prev = point

Device Transfer Detection via deviceId Comparison

Simultaneously, the parser checks for transfers by comparing the deviceId field between consecutive points. Google Takeout includes this identifier when location data originates from multiple devices (phones, tablets, or wearables). If the identifiers differ, the visualizer marks the transition point with is_transfer = True.


# visualizer.py

prev = None

for point in sorted_locations:
    if prev and point.device_id != prev.device_id:
        point.is_transfer = True  # Flag device switch

    # Long hop detection logic runs here

    prev = point

Frontend Visualization in Map.vue

After processing, the backend serves a JSON payload containing the boolean flags is_long_hop and is_transfer. The frontend component web/src/components/Map.vue consumes these flags to differentiate rendering styles.

Long hops display as dotted polylines connecting the disconnected segments, while transfers render as distinct map markers with specialized icons.

// web/src/components/Map.vue
points.forEach((p, i) => {
  if (p.is_long_hop && i > 0) {
    // Render dotted line between previous and current point
    const hopSegment = new google.maps.Polyline({
      path: [prevCoords, currentCoords],
      strokeOpacity: 0,
      icons: [{ icon: { path: 'M 0,-1 0,1', strokeOpacity: 1 }, offset: '0', repeat: '10px' }]
    });
  }
  
  if (p.is_transfer) {
    new google.maps.Marker({
      position: { lat: p.lat, lng: p.lng },
      icon: '/assets/transfer-icon.png',
      map: this.map
    });
  }
});

Summary

  • Long hops are detected in visualizer.py by comparing consecutive timestamps against the MAX_GAP_MS constant (default 12 hours).
  • Transfers are identified by monitoring changes in the deviceId field between consecutive location points during the same parsing iteration.
  • Flagged points carry boolean properties (is_long_hop and is_transfer) that persist through the API response to the frontend.
  • The Vue frontend (Map.vue) renders long hops as dotted line segments and transfers as specialized map markers for immediate visual recognition.

Frequently Asked Questions

What threshold defines a long hop in the visualizer?

The default threshold is 12 hours, calculated as 43,200,000 milliseconds in the MAX_GAP_MS constant within visualizer.py. You can modify this value to adjust sensitivity for different travel patterns.

How does the visualizer distinguish between different devices?

It compares the deviceId field between consecutive location points. When this identifier changes—indicating data originated from a different phone, tablet, or wearable—the point is flagged as a transfer.

Can the detection parameters be customized without modifying source code?

Currently, no. The MAX_GAP_MS threshold requires editing visualizer.py directly. The deviceId detection relies on Google's export schema and cannot be configured without altering the comparison logic in the source.

Where are these detections visualized on the interface?

The Map.vue component renders long hops as dotted polylines connecting distant points and transfers as distinct icons at the transition coordinates. A companion Timeline.vue component lists these events in the textual sidebar.

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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →