How the TeslaMate Terrain Module Fetches and Caches Elevation Data from SRTM

The TeslaMate Terrain module enriches vehicle position logs with elevation data by querying the Shuttle Radar Topography Mission (SRTM) dataset through a circuit-breaker-protected GenStateMachine that automatically caches DEM tiles to disk for subsequent lookups.

The TeslaMate.Terrain module in the teslamate-org/teslamate repository provides altitude information for logged drives and charging sessions. It integrates with the external SRTM library to fetch elevation data from NASA's Shuttle Radar Topography Mission while implementing robust error handling and local disk caching to minimize external API calls.

Circuit-Breaker Protection with Fuse

Before attempting any network request, the module validates system health using the Fuse circuit-breaker library. This prevents cascade failures when the SRTM service experiences downtime.

Fuse Initialization and Policies

When the Terrain process starts, it checks for an existing fuse using :fuse.ask/2. If no fuse exists, the module creates one with a standard policy defined in lib/teslamate/terrain.ex: 2 failures within a 3-minute window trigger a 15-minute reset period. This conservative approach ensures temporary network blips don't permanentlydisable elevation lookups.

Fuse State Handling

The module evaluates fuse state before each elevation request:

  • If OK: The request proceeds to do_get_elevation/2
  • If blown: Returns {:error, :unavailable} immediately, preventing further load on the SRTM service
  • If melted: The fuse enters recovery mode based on the configured reset timeout

When the SRTM call fails, the code explicitly melts the fuse:

with {:error, reason} <-
       call(srtm, :get_elevation, [lat, lng, [disk_cache_path: cache_path()]]) do
  :fuse.melt(name)
  {:error, reason}
end

Fetching Elevation from SRTM

The actual elevation lookup occurs in the private function do_get_elevation/2, which coordinates between the GenStateMachine state and the external SRTM library.

The do_get_elevation/2 Implementation

This function forwards coordinates to the SRTM library along with disk cache configuration:

defp do_get_elevation(%{data: %{srtm: srtm}} = state, {lat, lng} = coord) do
  case call(srtm, :get_elevation, [lat, lng, [disk_cache_path: cache_path()]]) do
    {:ok, elevation} -> 
      {:ok, elevation, state}
    {:error, reason} -> 
      :fuse.melt(name)
      {:error, reason}
  end
end

The disk cache path is retrieved from the application environment via Application.fetch_env!(:teslamate, :srtm_cache), ensuring each downloaded DEM tile persists between application restarts.

Dependency Injection Pattern

The module uses a deps map injected during initialization, defaulting to the SRTM module. This architecture, visible in lib/teslamate/terrain.ex, facilitates testing through mocks while maintaining clean separation between the state machine logic and the external HTTP client.

Disk Caching Strategy

The SRTM library handles tile management automatically when provided with a cache directory. This eliminates redundant network requests for coordinates falling within previously fetched tiles.

Cache Configuration

Configure the cache location in config/config.exs:

config :teslamate,
  srtm_cache: Path.expand("../srtm_cache", __DIR__)

This path is passed to every elevation lookup via the :disk_cache_path option. The SRTM library writes each downloaded GeoTIFF tile to this directory, checking for local file existence before initiating HTTP requests.

Automatic Tile Reuse

When do_get_elevation/2 requests elevation for coordinates, the SRTM library:

  1. Calculates the containing tile index
  2. Checks the configured cache path for existing files
  3. Returns cached elevation data if available
  4. Downloads only missing tiles from the SRTM server

This strategy dramatically reduces latency for subsequent queries within the same geographic region and minimizes external bandwidth consumption.

Background Processing Pipeline

The Terrain module operates as a GenStateMachine that continuously backfills missing elevation data for historical positions.

Scanning log_positions

A background job periodically executes get_positions_without_elevation/2, querying the database for entries lacking altitude data. For each batch of positions, the state machine spawns short-lived Tasks that call do_get_elevation/2 independently, enabling concurrent processing without blocking the main event loop.

Handling Timeouts

Elevation requests exceeding the configured timeout are handled asynchronously through :info messages. This ensures the state machine remains responsive to new vehicle telemetry while elevation data populates in the background. Successfully retrieved elevations are persisted to the database via Log.update_position/2.

Configuration and Usage Examples

Basic Elevation Lookup

Fetch elevation for specific coordinates after ensuring the Terrain GenServer runs:

{:ok, _pid} = TeslaMate.Terrain.start_link(name: MyTerrain)
elevation = TeslaMate.Terrain.get_elevation(MyTerrain, {37.7749, -122.4194})
IO.puts("Elevation: #{elevation || "unknown"} m")

Manual Backfill Trigger

Force processing of the next pending batch (normally handled automatically):

:gen_statem.cast(MyTerrain, :process)

Cache Directory Setup

Ensure the cache directory exists with appropriate permissions:

cache_dir = Application.fetch_env!(:teslamate, :srtm_cache)
File.mkdir_p!(cache_dir)

Summary

  • The TeslaMate Terrain module uses a GenStateMachine architecture to asynchronously enrich vehicle logs with elevation data from NASA's SRTM dataset.
  • Circuit-breaker protection via the Fuse library prevents system overload during SRTM service outages, implementing automatic recovery with a 15-minute reset window after 2 failures.
  • Intelligent disk caching stores downloaded DEM tiles locally using the configured :srtm_cache path, eliminating redundant network requests for coordinates within cached regions.
  • Background processing continuously scans the log_positions table for entries lacking elevation, handling timeouts asynchronously to maintain system responsiveness.

Frequently Asked Questions

What is SRTM and why does TeslaMate use it?

SRTM stands for Shuttle Radar Topography Mission, a NASA dataset providing near-global elevation data. TeslaMate uses this public domain data to add altitude context to driving and charging logs without requiring proprietary mapping APIs or additional subscription costs.

Where does TeslaMate store cached elevation tiles?

Cached tiles are stored in the directory specified by the :srtm_cache configuration value in config/config.exs. The SRTM library automatically manages these files, organizing them by tile coordinates and validating checksums to ensure data integrity.

What happens when elevation requests fail or timeout?

If the SRTM service is unavailable, the Fuse circuit breaker melts after 2 failures, returning {:error, :unavailable} for subsequent requests during the 15-minute cooldown. Individual request timeouts are handled asynchronously—the Task result arrives via a :info message later, allowing the state machine to continue processing other positions without blocking.

Can I disable or configure the elevation fetching behavior?

While you cannot completely disable the Terrain module without modifying the supervision tree, you can configure the cache location via the :srtm_cache environment variable or adjust fuse parameters by modifying the standard policy defined in lib/teslamate/terrain.ex. The module gracefully handles missing elevation data, storing NULL values when the service is unavailable.

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 →