How the TeslaMate Terrain Module Handles Elevation Data

The TeslaMate Terrain module is a GenStateMachine that asynchronously enriches GPS positions with elevation data from the Shuttle Radar Topography Mission (SRTM), using circuit-breaker protection, disk caching, and batched updates every six hours.

The TeslaMate Terrain module handles elevation data by integrating with NASA's SRTM dataset to augment vehicle location logs with accurate altitude measurements. Implemented as a robust GenStateMachine in the teslamate-org/teslamate repository, this background process ensures reliable elevation lookups without blocking the main application workflow.

Architecture and State Machine Design

The TeslaMate.Terrain module operates as a GenStateMachine that cycles through distinct states to manage the elevation enrichment pipeline. Located in lib/teslamate/terrain.ex, the process begins in the :ready state and coordinates with the Log module to identify and update position records lacking altitude information.

The module uses dependency injection for its SRTM client, defaulting to the SRTM module but allowing test mocks via the :deps_srtm option. This design enables isolated testing while maintaining production reliability through circuit-breaker patterns and asynchronous task processing.

Step-by-Step Elevation Processing Workflow

Initialization and Scheduling

Upon startup, the init/1 function transitions the state machine to :ready and immediately triggers an internal event to fetch positions. The system schedules periodic re-fetch operations using schedule_fetch/0, which sets a six-hour timeout to continuously process new location data.

Batched Position Retrieval

The state machine queries the database through Log.get_positions_without_elevation/2, retrieving up to 1,000 positions per batch. If no elevation-less records exist, the process idles until the next scheduled fetch. This batching strategy prevents memory overload while ensuring comprehensive coverage of historical GPS data.

Asynchronous Elevation Lookup

For each retrieved position, the module spawns a supervised Task to execute do_get_elevation/2. These tasks operate with a strict 100-millisecond timeout to prevent blocking the main process. The lookup function checks a Fuse circuit-breaker before forwarding requests to the SRTM dependency via SRTM.get_elevation/3.

Circuit-Breaker Protection

The module implements fault tolerance using the Fuse library with a 2-failure threshold and 3-minute lockout period. When the breaker is closed, requests proceed to the external service; when blown, do_get_elevation/2 immediately returns {:error, :unavailable}, protecting the SRTM service from overload during outages.

Disk Caching Strategy

Elevation requests include a disk_cache_path parameter extracted from the application environment (:srtm_cache). The SRTM library stores raw .hgt tiles locally, eliminating redundant network calls for previously accessed geographic regions. This caching mechanism significantly improves performance for repeated routes.

Database Updates and Error Handling

Successful elevation lookups trigger Log.update_position/2 to persist altitude data to the database. When tasks exceed the timeout threshold, the state machine transitions to a {:waiting, task.ref} state, eventually processing delayed replies through :info messages without data loss. Failures are logged at warning level, while informational messages track delayed queries.

Integration with the SRTM Library

The Terrain module delegates actual elevation calculations to the SRTM library through a well-defined interface. The SRTM.get_elevation/3 function accepts latitude, longitude, and options including the disk cache path, returning {:ok, meters} or {:error, reason}.

For testing, the repository includes test/support/mocks/srtm.ex, which implements a lightweight mock returning fixed elevation values:

defmodule SRTMMock do
  def get_elevation(lat, lng, _opts) do
    send(state.pid, {SRTM, {:get_elevation, lat, lng, []}})
    {:ok, 123}
  end
end

This mock demonstrates the expected contract: accepting coordinates and options, then returning a tuple with the elevation in meters.

Practical Code Examples

Querying Elevation Directly

You can request elevation data for specific coordinates through the public API:

{:ok, elevation} = TeslaMate.Terrain.get_elevation({52.5200, 13.4050})
IO.puts("Elevation: #{elevation} m")

This call blocks for up to the configured timeout (default 2000ms) and returns nil if the service is unavailable.

Custom SRTM Implementation

During testing or when using alternative elevation sources, inject a custom module:

{:ok, pid} = TeslaMate.Terrain.start_link(
  deps_srtm: MyApp.CustomSRTM,
  timeout: 500
)

{:ok, elevation} = TeslaMate.Terrain.get_elevation({40.7128, -74.0060})

Inspecting Process State

Monitor the background enrichment process using Erlang's system functions:

terrain_pid = Process.whereis(TeslaMate.Terrain)
{:state, state, data} = :sys.get_state(terrain_pid)
IO.inspect(state)  # => :ready, :waiting, etc.

Summary

  • The TeslaMate.Terrain module is a GenStateMachine in lib/teslamate/terrain.ex that manages elevation enrichment as a background process.
  • It retrieves up to 1,000 positions per batch from the Log module, processing them asynchronously with 100ms timeouts.
  • Circuit-breaker protection using Fuse prevents cascade failures with a 2-failure threshold and 3-minute recovery window.
  • Disk caching via the :srtm_cache configuration stores SRTM tiles locally to minimize network requests.
  • The module updates elevation data through Log.update_position/2 and operates on a 6-hour polling cycle for continuous data enrichment.

Frequently Asked Questions

How does the Terrain module handle SRTM service outages?

The module implements a circuit-breaker pattern using the Fuse library. After two consecutive failures, the breaker opens for three minutes, immediately returning {:error, :unavailable} for subsequent requests rather than overwhelming the external service. This protects both the TeslaMate application and the SRTM data source during network interruptions.

What happens when elevation lookups take too long?

When an asynchronous task exceeds the 100-millisecond timeout, the state machine transitions to a {:waiting, task.ref} state and returns nil to the caller. The process later handles the delayed result via :info messages, ensuring the position remains available for future processing cycles without blocking the pipeline.

Can I use a different elevation data source instead of SRTM?

Yes. The module supports dependency injection through the :deps_srtm option. You can provide any module implementing the get_elevation/3 callback that accepts latitude, longitude, and options, then returns {:ok, elevation} or {:error, reason}. The test suite demonstrates this pattern using SRTMMock in test/support/mocks/srtm.ex.

Where is the SRTM tile cache stored?

The cache location is configured via the :srtm_cache application environment variable, typically set in config/config.exs. The Terrain module passes this path as disk_cache_path to SRTM.get_elevation/3, allowing the underlying library to persist .hgt tiles locally and avoid redundant downloads for previously accessed coordinates.

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 →