What Is the Purpose of the TileRepository in Google Timeline Visualizer?

The TileRepository in Google Timeline Visualizer is a centralized caching and loading component that supplies map-tile images to the rendering pipeline, managing both in-memory LRU caching and persistent disk storage while fetching missing tiles from remote basemap services.

This core utility, found in TileRepository.kt, abstracts away the complexity of tile retrieval so that the visualizer's UI and export modules can render geographic backgrounds efficiently. According to the mahlernim/google-timeline-visualizer source code, the repository handles four critical responsibilities that enable smooth, flicker-free map visualization.

Core Responsibilities of the TileRepository

The TileRepository serves as the single source of truth for all map tile operations within the app. Its design separates concerns cleanly across caching, network I/O, key generation, and pipeline integration.

Cache Management

The TileRepository implements a two-tier caching strategy to minimize network requests and latency:

  • Memory LRU cache: Stores recently accessed tiles in RAM for immediate retrieval during rapid scrolling or animation
  • Disk persistence: Saves tiles to the app's cache directory (cacheDirectory) so they survive process restarts and are available offline

This dual approach ensures that once a tile has been loaded once, subsequent requests resolve in milliseconds without touching the network.

Tile Loading from Remote Sources

When a requested tile is absent from both cache layers, the repository fetches it from the Carto basemap service at https://a.basemaps.cartocdn.com/.... The implementation follows a robust download pattern:

// Simplified flow from TileRepository.kt
withContext(Dispatchers.IO) {
    val tempFile = File(cacheDirectory, "$key.tmp")
    // Download to temporary location
    downloadTile(url, tempFile)
    // Atomic move prevents partial files
    tempFile.renameTo(File(cacheDirectory, key))
}

Network operations run on the IO dispatcher, and the atomic file move guarantees cache integrity even if the download is interrupted.

Deterministic Key Generation

To ensure consistent cache hits, the TileRepository generates keys from TileId objects using a predictable format:

"${zoom}_${x}_${y}"

This string becomes both the memory cache key and the on-disk filename, mapping any geographic tile coordinates to exactly one storage location.

Integration with the Rendering Pipeline

The repository exposes two primary interfaces for consumers:

Method Purpose Caller
cached(tileId) Synchronous lookup from memory/disk TimelineView, TimelinePainter, Mp4Exporter
load(tileId) Suspending network fetch and cache write Coroutines launched by TimelineView

The TimelineView coordinates these calls: it first attempts tiles.cached(tileId), and if null, launches a coroutine to tiles.load(tileId) followed by markFrameDirty() to trigger redraw.

How the TileRepository Fits Into the Architecture

The repository sits between remote tile servers and three downstream consumers:

TimelineView Orchestration

In TimelineView.kt, the view acts as coordinator. During onDraw(), it passes a function reference to the painter:

painter.draw(
    targetCanvas,
    width,
    height,
    data,
    animationFrame,
    journeyDurationSeconds,
    videoTitle,
    renderText,
    cameraSettings,
    allowCameraTrackBuild = isCameraReady,
    tiles = tiles::cached   // cache lookup injected into renderer
)

This design lets the painter query tile availability synchronously during each frame without blocking on network I/O.

TimelinePainter Rendering

TimelinePainter.kt receives tiles::cached as a parameter. While drawing the map background, it invokes this function for every visible tile coordinate, skipping any tiles not yet available. This prevents frame drops—missing tiles simply render as blank or background color until the cache populates.

Mp4Exporter Video Generation

The export pipeline in Mp4Exporter.kt also relies on TileRepository. When generating video frames offline, it pre-fetches all required tiles via the same cached()/load() interface, ensuring the exported video contains complete map backgrounds without runtime network dependencies.

Practical Usage Example

The following pattern demonstrates typical tile acquisition in the visualizer:

val tileId = TileId(zoom = 4, x = 12, y = 9)

// Try memory or disk cache first
val cachedBitmap = tiles.cached(tileId)

// If not cached, load from network (suspend function)
if (cachedBitmap == null) {
    scope.launch {
        tiles.load(tileId)          // downloads and caches the tile
        markFrameDirty()           // triggers a redraw once ready
    }
}

This idiom appears throughout TimelineView.kt and Mp4Exporter.kt, with the coroutine scope tied to the component's lifecycle to prevent leaks.

Key Files and Their Relationships

File Path Role
TileRepository.kt app/src/main/java/dev/mahlernim/timelinevisualizer/data/TileRepository.kt Core implementation of caching, storage, and network loading
TimelineView.kt app/src/main/java/dev/mahlernim/timelinevisualizer/ui/TimelineView.kt UI component that requests tiles and schedules redraws
TimelinePainter.kt app/src/main/java/dev/mahlernim/timelinevisualizer/render/TimelinePainter.kt Renderer that queries cached tiles during frame drawing
Mp4Exporter.kt app/src/main/java/dev/mahlernim/timelinevisualizer/export/Mp4Exporter.kt Video exporter that fetches tiles for offline frame generation

Summary

  • The TileRepository centralizes all map tile operations in Google Timeline Visualizer, eliminating duplicate network requests and providing fast, consistent access to geographic imagery.

  • Its dual-layer caching (memory LRU plus disk persistence) optimizes for both speed and offline capability.

  • Deterministic key generation from TileId coordinates ensures cache consistency across the codebase.

  • The repository's synchronous cached() and suspending load() methods let UI and export consumers choose appropriate concurrency models.

  • By shielding TimelineView, TimelinePainter, and Mp4Exporter from network details, the TileRepository enables smooth 60fps rendering and reliable video export regardless of connectivity conditions.

Frequently Asked Questions

What happens if a tile download fails in TileRepository?

Failed downloads do not cache partial data. The temporary file mechanism in TileRepository.kt writes to a .tmp file first, then atomically renames only on successful completion. If the network request throws, the temporary file is discarded and the next cached() call returns null, allowing retry on the next frame or export attempt.

How does TileRepository handle concurrent requests for the same tile?

The implementation uses Kotlin coroutines with structured concurrency. Multiple callers may simultaneously request the same uncached tile, but the disk write is atomic and idempotent—subsequent writes with identical content produce the same result. The memory cache absorbs redundant lookups once the first successful load completes.

Why does TimelinePainter use a function reference instead of direct TileRepository access?

Passing tiles::cached as a parameter decouples the renderer from data dependencies. This inversion of control lets TimelinePainter remain a pure rendering component while TimelineView manages lifecycle, threading, and cache invalidation. The same painter code works for both real-time preview and video export with different tile providers if needed.

Can TileRepository work with other basemap providers besides Carto?

The repository's design is provider-agnostic. The Carto URL is currently hardcoded, but the key generation and caching logic depend only on TileId coordinates. To support additional providers, one would modify the URL construction in load() without changing the cache layer or public interface.

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 →