HTG - SRTM Elevation Library & Service
High-performance, memory-efficient Rust library and microservice for querying elevation data from SRTM (Shuttle Radar Topography Mission) .hgt files.
Problem
Existing elevation services (e.g., Python/Flask) consume excessive memory (7GB+). This project provides a Rust-based solution using <100MB with the same functionality.
Features
- Fast: <10ms response time for cached tiles
- Memory Efficient: <100MB with 100 cached tiles (vs 7GB in Python)
- Offline: No internet required, works with local
.hgtfiles - Auto-Download: Optional automatic tile download from configurable sources
- Automatic Detection: Determines correct tile from coordinates
- LRU Caching: Configurable cache size to bound memory usage
- Docker Ready: Easy deployment with Docker/Docker Compose
- OpenAPI Docs: Interactive Swagger UI at
/docs
Project Structure
This is a Cargo workspace with two crates:
htg/
├── htg/ # Library crate (publish to crates.io)
│ └── src/
│ ├── lib.rs
│ ├── tile.rs # SRTM tile parsing
│ ├── filename.rs # Coordinate → filename conversion
│ ├── service.rs # Caching service
│ ├── download.rs # Auto-download functionality
│ └── error.rs # Error types
│
└── htg-service/ # Binary crate (publish to DockerHub)
└── src/
├── main.rs # Axum HTTP server
└── handlers.rs # API handlers
Quick Start
Using Docker (Recommended)
# Clone the repository
# Create data directory and add .hgt files
# Copy your .hgt files to data/srtm/
# Run with Docker Compose
# Test it
Using Docker Hub
From Source
# Run the service
HTG_DATA_DIR=./data/srtm
API Endpoints
GET /elevation
Query elevation for coordinates.
Request:
Response (200 OK):
Error Response (400 Bad Request):
Error Response (404 Not Found):
GET /health
Health check endpoint.
Response:
GET /stats
Cache statistics.
Response:
GET /docs
Interactive OpenAPI documentation (Swagger UI).
Open in browser: http://localhost:8080/docs
The OpenAPI JSON spec is available at /api-docs/openapi.json.
Configuration
Environment Variables
| Variable | Default | Description |
|---|---|---|
HTG_DATA_DIR |
. |
Directory containing .hgt files |
HTG_CACHE_SIZE |
100 |
Maximum tiles in memory |
HTG_PORT |
8080 |
HTTP server port |
HTG_DOWNLOAD_SOURCE |
- | Named source: "ardupilot", "ardupilot-srtm1", "ardupilot-srtm3" |
HTG_DOWNLOAD_URL |
- | URL template for auto-download (optional) |
HTG_DOWNLOAD_GZIP |
false |
Whether downloaded files are gzipped |
RUST_LOG |
info |
Log level (debug, info, warn, error) |
Auto-Download Configuration
Using ArduPilot (Recommended)
The easiest way to enable auto-download is using the ArduPilot terrain server:
# SRTM1 - High resolution (30m, ~25MB/tile) - recommended
# SRTM3 - Lower resolution (90m, ~2.8MB/tile) - faster downloads
This automatically downloads tiles from https://terrain.ardupilot.org/.
Using Custom URL Template
For other data sources, use a custom URL template:
URL Template Placeholders:
{filename}- Full filename (e.g., "N35E138"){lat_prefix}- N or S{lat}- Latitude digits (e.g., "35"){lon_prefix}- E or W{lon}- Longitude digits (e.g., "138"){continent}- Continent subdirectory (e.g., "Eurasia", "North_America")
Library Usage
Add to your Cargo.toml:
[]
= "0.1"
# With auto-download support
= { = "0.1", = ["download"] }
Basic Usage
use SrtmService;
let service = new;
let elevation = service.get_elevation?;
println!;
With Auto-Download (ArduPilot)
use ;
let service = new
.cache_size
.auto_download
.build?;
// Will download N35E139.hgt from ArduPilot if not present locally
let elevation = service.get_elevation?;
With Custom URL Template
use ;
let service = new
.cache_size
.auto_download
.build?;
// Will download N35E139.hgt if not present locally
let elevation = service.get_elevation?;
From Environment Variables
use SrtmServiceBuilder;
let service = from_env?.build?;
let elevation = service.get_elevation?;
SRTM Data
Data Format
- SRTM1: 3601×3601 samples, 1 arc-second (~30m) resolution, ~25MB per tile
- SRTM3: 1201×1201 samples, 3 arc-second (~90m) resolution, ~2.8MB per tile
- Coverage: ±60° latitude globally
- Filename:
N35E138.hgt(latitude prefix + latitude + longitude prefix + longitude)
Download Sources
- SRTM Tile Grabber - Interactive map to download tiles
- USGS Earth Explorer - Official source
- OpenTopography - Academic/research access
Place downloaded .hgt files in your HTG_DATA_DIR directory.
Performance
| Metric | Value |
|---|---|
| Memory (100 SRTM3 tiles) | ~280MB |
| Memory (100 SRTM1 tiles) | ~2.5GB |
| Cached response | <10ms |
| Uncached response | <50ms |
| Throughput | >10,000 req/s |
Development
Prerequisites
- Rust 1.75 or later
- Docker (optional, for containerized deployment)
Commands
# Run tests
# Run tests with download feature
# Format code
# Run clippy
# Build release
# Run service locally
HTG_DATA_DIR=./data/srtm
Docker Build
# Build image
# Run container
Benchmarks
Run performance benchmarks to validate memory usage, latency, and throughput.
Prerequisites
Running Benchmarks
# Create synthetic test tiles (100 SRTM3 tiles)
# Start the service in Docker
# Wait for service to start
# Run benchmarks
# Stop the service
Expected Output
=== HTG Performance Benchmark ===
Memory Usage:
Baseline: 12 MB
10 tiles: 42 MB
50 tiles: 78 MB
100 tiles: 95 MB PASS (target: <100MB)
Latency (1000 requests):
Warm cache: 0.8ms (p50), 1.2ms (p95), 2.1ms (p99) PASS (target: <10ms)
Throughput:
Single tile: 15,234 req/sec PASS (target: >1000)
GeoJSON Batch:
10 points: 2ms
100 points: 12ms
1000 points: 89ms
Performance Targets
| Metric | Target | Description |
|---|---|---|
| Memory (100 tiles) | <100MB | With 100 SRTM3 tiles cached |
| Cached latency | <10ms | Repeated queries to same tile |
| Uncached latency | <50ms | First query to new tile |
| Throughput | >1000 req/s | Sustained request rate |
Contributing
Workflow
- Create an issue describing the feature/bug
- Create a branch from
main:git checkout -b feature/issue-number-description - Make changes and commit with descriptive messages
- Open a Pull Request linked to the issue
- Wait for CI - all checks must pass
- Merge after approval
Rules
- No direct pushes to
main- all changes must go through PRs - PRs must reference an issue - use
Closes #123in PR description - All tests must pass before merging
- Code must be formatted with
cargo fmt - No clippy warnings - run
cargo clippy
Roadmap
| Phase | Component | Status |
|---|---|---|
| 1 | Core Tile Parser | ✅ Complete |
| 2 | Filename Detection | ✅ Complete |
| 3 | Caching Layer | ✅ Complete |
| 4 | HTTP API | ✅ Complete |
| 5 | Production Ready | ✅ Complete |
| 6 | Publish to crates.io | 🔄 Pending |
| 7 | Publish to DockerHub | 🔄 Pending |
License
MIT
Author
Pedro Sanz Martinez (@pedrosanzmtz)