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:
- Opens
Timeline.jsonand loads viajson.load() - Calls
extract_timeline_points()(lines [624-660]) to normalize location points across semantic activities, visits, and raw path points - 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 lengthsbuild_legs(): Splits routes into transfer vs. local segmentsbuild_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_MOVEMENTSpreset
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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →