How the Python CLI for Google Timeline Visualizer Works

The Python CLI for Google Timeline Visualizer converts Google Timeline JSON exports into animated travel videos through a 14-step pipeline orchestrated by main() in visualizer.py.

The Google Timeline Visualizer command-line interface transforms raw location history into cinematic travel animations. According to the mahlernim/google-timeline-visualizer source code, the entire workflow lives in a single entry-point script that handles everything from argument parsing to FFmpeg video encoding.

CLI Architecture and Entry Point

The CLI is implemented in visualizer.py. When executed via python -m visualizer or directly as ./visualizer.py, the main() function coordinates all operations.

The design follows a linear pipeline: arguments → validation → data extraction → camera track → frame generation → animation → video file. This structure keeps the codebase maintainable while allowing precise control over each rendering stage.

Step 1: Argument Parsing and Validation

The build_argument_parser() function (lines [1003-1034]) constructs an argparse.ArgumentParser with all user-facing options:

  • Input file path (validated via existing_file())
  • Year, start-date, and end-date filtering
  • Camera movement presets
  • Aspect ratio and resolution
  • Output file path

The parser rejects invalid inputs immediately before any expensive processing begins.

Step 2: Preset Token Handling

Users can supply a Base64-encoded preset token via --preset. The decode_preset() function (lines [167-207]) decodes this to a PresetValues object (lines [139-155]), which overwrites corresponding CLI arguments.

This allows sharing complex configurations as single strings:


# Decode and apply a preset token

python -m visualizer \
  --input Timeline.json \
  --preset aGV2c2NvcmU= \
  --output mytrip.mp4

Step 3: Date Range Resolution

parse_date_argument() (lines [1036-1049]) interprets date strings like 2023-04 and returns datetime.date objects. If no dates or year are specified, the CLI defaults to the current year.

Step 4: FFmpeg Availability Check

Before heavy processing, ensure_ffmpeg_available() (lines [996-1002]) verifies that Matplotlib can access the ffmpeg writer. If unavailable, the custom FfmpegUnavailableError aborts execution with a clear error message.

Step 5: Timeline JSON Parsing and Cleaning

The parse_timeline() function (lines [1051-1088]) orchestrates data loading:

  1. Opens Timeline.json and loads via json.load()
  2. Calls extract_timeline_points() (lines [624-660]) to normalize location points across semantic activities, visits, and raw path points
  3. Filters GPS spikes using filter_location_outliers() (lines [1254-1259])

Step 6: Geometry Conversion and Distance Calculation

Latitude/longitude pairs are projected to Web-Mercator meters via latlon_to_meters() (lines [1212-1245]). Cumulative distances are computed with the Haversine formula (lines [1085-1092]), creating a continuous distance metric for animation timing.

Step 7: Trip Detection and Journey Pacing

Three functions collaborate on motion dynamics:

  • transfer_threshold_km() (lines [1249-1262]): Determines distance threshold for "trip" legs based on median hop lengths
  • build_legs(): Splits routes into transfer vs. local segments
  • build_journey_timing() (lines [2124-2146]): Creates non-linear distance-vs-progress mapping with configurable compression exponent

Step 8: Camera Track Generation

build_camera_track() (lines [1588-1667]) samples approximately 480 points (CAMERA_TRACK_SAMPLES) across the journey. For each sample, raw_camera_sample() (lines [1580-1620]) calculates:

  • Map center coordinates (cx, cy)
  • Viewport span (span_x, span_y)
  • Padding and zoom based on CAMERA_MOVEMENTS preset

The result is a list of viewport tuples defining the camera path.

Step 9: Overview Viewport Calculation

After the journey completes, calculate_overview_viewport() (lines [2589-2599]) computes a final zoom-out view for the video outro.

Step 10: Frame Data Preparation

A loop (lines [1235-1286]) builds frame_data for every video frame:

  • Journey frames plus outro frames
  • Distance selection per frame
  • Viewport selection via camera_at() (lines [1444-1455])

Step 11: Matplotlib Figure and Map Tile Setup

The figure is created with figsize = width_px/100 inches to achieve target resolution. Map tiles are fetched on-demand through get_map_image() (lines [844-865]), which uses fetch_tile_img() with a tile cache for performance.

Step 12: Animation Callback Function

The update(i) callback (lines [1312-1365]) handles each frame:

  • Updates map extent
  • Refreshes map tiles every 4 frames
  • Draws travelled trail (historical and recent segments)
  • Renders head marker
  • Blends to overview during outro
  • Updates subtitle with date and cumulative distance

Step 13: Video Encoding with FFmpeg

The final animation saves via:

ani.save(args.output, writer='ffmpeg', fps=fps, dpi=100)

This call at line 1370 completes the rendering pipeline.

Step 14: Exit Code Handling

Success returns 0. Any caught TimelineCliError (parsing errors, outlier failures, FFmpeg issues) prints to stderr and returns 1 (lines [842-847], [864-867]).

Exception Hierarchy and Error Handling

All custom exceptions reside at the top of visualizer.py:

Exception Purpose
TimelineCliError Base class for all CLI errors
TimelineParseError JSON parsing failures
NoDataFoundError Empty or filtered datasets
FfmpegUnavailableError Missing encoder dependency

The test suite tests/test_cli_errors.py verifies error handling behavior across these types.

Practical Usage Examples

Basic year-based rendering:

python -m visualizer \
  --input path/to/Timeline.json \
  --year 2022 \
  --output travel_2022.mp4

Custom date range with portrait orientation:

python -m visualizer \
  --input Timeline.json \
  --start-date 2023-05 \
  --end-date 2023-07 \
  --aspect-ratio portrait \
  --resolution 1080 \
  --duration 45 \
  --camera-movement dynamic \
  --output summer_2023.mp4

Preset-based configuration sharing:


# Create preset from string

PRESET=$(echo -n "steady square balanced balanced off" | base64 -w0)

python -m visualizer \
  --input Timeline.json \
  --preset $PRESET \
  --output mytrip.mp4

Key Source Files

File Purpose
visualizer.py Core CLI, 2000+ lines containing parsing, camera logic, animation, and export
tests/test_cli_errors.py Unit tests for argument validation and error conditions
tests/test_parser.py Timeline parsing and outlier filtering tests
requirements.txt Dependencies: dateutil, matplotlib, numpy, pillow

Summary

  • The Python CLI for Google Timeline Visualizer runs as a single-module application through visualizer.py
  • main() orchestrates 14 sequential steps from argument parsing to video export
  • Preset tokens enable portable configuration sharing via Base64 encoding
  • Camera track generation samples ~480 points with configurable movement presets
  • Matplotlib + FFmpeg handle rendering and encoding with tile-cached map backgrounds
  • Typed exceptions provide clear error messages with deterministic exit codes

Frequently Asked Questions

What input file format does the CLI require?

The CLI requires Google's Timeline.json export format. The parse_timeline() function loads this with standard json.load() and normalizes diverse location record types (semantic activities, visits, raw path points) into uniform point structures.

How does the CLI handle missing FFmpeg?

ensure_ffmpeg_available() (lines [996-1002]) queries Matplotlib for FFmpeg writer availability before any processing. If absent, it raises FfmpegUnavailableError, prints to stderr, and exits with code 1. This early validation prevents wasted computation on systems that cannot complete video encoding.

Can I animate a specific month instead of a full year?

Yes. Use --start-date and --end-date with YYYY-MM format. The parse_date_argument() function (lines [1036-1049]) converts these strings to datetime.date objects for filtering. Omitting both dates defaults to the current calendar year.

What camera movement options are available?

The CLI supports multiple CAMERA_MOVEMENTS presets including dynamic, steady, and others. These control how raw_camera_sample() (lines [1580-1620]) calculates viewport center, span, padding, and zoom transitions throughout the journey animation.

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 →