Generate stylized hillshade and relief map tiles from real-world terrain data.
🌐 scriptedrelief.com — Browse the tile library
hillgen run --place "Artist Point, WA" --theme alpine-glacier --zoom 10-14brew tap emuehlstein/hillshade
brew install hillgenpip install hillgencurl -fsSL https://raw.githubusercontent.com/emuehlstein/hillshade-generator/main/install.sh | bashChecks for prerequisites (Python 3.10+, GDAL), installs hillgen, and adds it to your PATH.
git clone https://github.com/emuehlstein/hillshade-generator
cd hillshade-generator
pip install .# Generate a hillshade of Artist Point, WA
hillgen run --place "Artist Point, WA" --theme alpine-glacier --zoom 10-14
# Generate a dark hillshade of Mt. Rainier at high zoom
hillgen run --bbox "-121.85,46.72,-121.65,46.92" \
--theme midnight --zoom 10-16 --output rainier.pmtileshillgen run Full pipeline: fetch → reproject → shade → style → tile → package
hillgen fetch Download DEM for a location or bbox
hillgen reproject Reproject a cached DEM to EPSG:4326
hillgen shade Generate grayscale hillshade from a DEM
hillgen style Apply a color theme to a hillshade
hillgen tile Cut a styled raster into XYZ tiles
hillgen package Package tiles into MBTiles / PMTiles
hillgen themes List available themes
hillgen sources List available DEM sources
hillgen view Start a local Leaflet tile viewer
hillgen publish Upload a PMTiles file to scriptedrelief.com
hillgen cache Manage local cache (status / clean / pull)
hillgen auth Inspect contributor authentication
hillgen version Show version and environment info
- Python 3.10+
- GDAL 3.6+ —
brew install gdal(macOS) /apt install gdal-bin python3-gdal(Linux) - rasterio, numpy, click, requests — installed automatically via pip
- pmtiles CLI (optional, for PMTiles output) —
brew install pmtiles
- Acquires terrain data from public DEM sources (USGS 3DEP, Copernicus, SRTM, state LiDAR programs)
- Processes with configurable vertical exaggeration, shading modes, and composite blending
- Styles using a theme system: 20+ built-in themes, easy JSON-based custom themes
- Outputs PMTiles (serverless web maps), MBTiles (offline/ATAK), and reusable intermediates
brew install gdal python3
git clone https://github.com/emuehlstein/hillshade-generator.git
cd hillshade-generator
python3 -m venv .venv && source .venv/bin/activate
pip install -e .
hillgen versioncurl -fsSL https://raw.githubusercontent.com/emuehlstein/hillshade-generator/main/install.sh | bash# By bounding box
hillgen run --bbox "-105.35,39.6,-105.15,39.8" --theme midnight
# By named place (geocodes automatically)
hillgen run --place "Mt. St. Helens" --theme desert-sun --zoom 8-16
# Add a buffer around the area (degrees, ~111km per degree)
hillgen run --place "Mt. Hood" --buffer 0.15 --theme alpine-glacierhillgen themes # list all themes
hillgen themes --show midnight # details + preview link
hillgen themes --tag elevation # filter by tagEach pipeline stage is a standalone subcommand. Run one at a time or let run do them all:
# Just download and cache the DEM (no processing)
hillgen fetch --place "Mt. Hood" --dem usgs-3dep-10m
# Generate grayscale hillshade only (the expensive step)
hillgen shade --place "Mt. Hood" --exaggeration 9
# Apply a theme to an existing hillshade (seconds)
hillgen style --place "Mt. Hood" --theme midnight --exaggeration 9
# Re-tile at a different zoom without re-processing
hillgen tile --place "Mt. Hood" --theme midnight --zoom 8-18
# Package tiles into MBTiles/PMTiles
hillgen package --place "Mt. Hood" --theme midnightEach subcommand picks up from the cache — if a previous stage's output exists, it's reused automatically.
hillgen view ./output/ --port 9999 # opens a local Leaflet viewerThemes are the heart of Hillgen. Each theme bundles shading mode, color ramp, exaggeration strategy, and blending parameters into a single named preset.
| Theme | Style | Best For |
|---|---|---|
midnight |
Deep blue-black with subtle terrain | Dark basemaps, overlays |
daylight |
Warm grey with soft shadows | Light basemaps, print |
tactical |
Muted green-brown, high contrast | Military/outdoor overlays |
terrain |
Hypsometric tints (green→brown→white) | Educational, reference |
simmon |
Robert Simmon's composite technique | Publication-quality relief |
flat-terrain |
Heavy exaggeration for subtle landscapes | Great Plains, coastal, IL |
desert-sun |
Rust canyons → sandy mesas → white peaks | Arid landscapes |
alpine-glacier |
Deep indigo → teal → icy white | Volcanic peaks, heavy relief |
infrared |
False-color thermal palette | Dramatic visualization |
vivid |
Saturated blue→green→orange→red | Maximum feature contrast |
cool |
Desaturated blue-grey, cartographic | Professional base layers |
grayscale |
Pure hillshade, no color | Custom coloring base layer |
Create a JSON file:
{
"name": "my-custom-theme",
"description": "A warm amber hillshade",
"ramp": "path/to/ramp.txt",
"shading": "composite",
"composite_weights": [0.6, 0.3, 0.1],
"exaggeration": "auto",
"terrain_type": "auto",
"aspect_blend": 0.1,
"tags": ["warm", "custom"]
}hillgen run --place "Grand Canyon" --theme ./my-custom-theme.jsonColor ramps are GDAL color-relief format (one elevation→RGBA mapping per line). Drop a .txt file in themes/ramps/ or reference an absolute path in your theme JSON.
0% 20 20 40 255
25% 40 60 100 255
50% 80 90 110 255
75% 140 145 155 255
100% 220 220 230 255
We welcome community themes! See CONTRIBUTING.md for the submission process — essentially: add your ramp file + theme JSON, include a sample render, and open a PR.
Hillgen auto-selects the best available DEM source for your area, or you can specify one explicitly.
| Source | ID | Resolution | Coverage | Auto-select |
|---|---|---|---|---|
| NPS SfM Rainier 2021 | nps-sfm-rainier-2021 |
0.67m | Mt. Rainier NP | ✅ |
| Illinois ISGS / ILHMP LiDAR | isgs-ilhmp |
0.3m | Illinois (102 counties) | ✅ |
| Indiana IGIC LiDAR | igic-indiana-lidar |
0.76m | Indiana (92 counties) | ✅ |
| Wisconsin DNR LiDAR | wi-dnr-lidar |
1m | Wisconsin | ✅ |
| USGS 3DEP 1/3 arc-sec | usgs-3dep-10m |
~10m | CONUS / AK / HI | ✅ (fallback) |
| Local file | (path) | Any | Any | — |
High-resolution 1m LiDAR-derived DEM from the Wisconsin DNR ArcGIS ImageServer. Auto-selected for any bbox fully within Wisconsin.
- Coverage: Wisconsin only
- Resolution: 1m native (EPSG:3071), resampled to requested zoom
- Units: Meters, NAVD88
- Source: https://dnrmaps.wi.gov/arcgis_image/rest/services/DW_Elevation/EN_DEM_from_LiDAR/ImageServer
# Auto-selected when your area is in Wisconsin
hillgen run --place "Kettle Moraine Southern Unit" --theme simmon --zoom 10-18
# Or request explicitly
hillgen run --place "Kettle Moraine Southern Unit" --dem wi-dnr-lidar --theme simmon --zoom 10-18# Auto-select (picks best available resolution)
hillgen run --place "Denali" --theme alpine-glacier
# Force a specific source
hillgen run --bbox "..." --dem usgs-3dep-1m --theme midnight
# Use a local DEM file
hillgen run --dem ./my-dem.tif --theme simmonDEM sources are defined as catalog entries. New sources can be added by implementing the DEMSource interface in hillgen/sources/base.py and registering them in hillgen/sources/init.py. See the existing sources for reference implementations.
| Format | Use Case | Default |
|---|---|---|
| PMTiles | Serverless web maps (S3/CloudFront, no tile server) | ✅ |
| MBTiles | Offline maps, ATAK, mbtileserver | ✅ |
| Directory | XYZ tile directory ({z}/{x}/{y}.png) |
--format dir |
| GeoTIFF | Intermediate styled raster (for GIS workflows) | --keep-intermediates |
# PMTiles only (default)
hillgen run --place "Yosemite" --theme simmon
# MBTiles for ATAK
hillgen run --place "Yosemite" --theme tactical --format mbtiles
# Both
hillgen run --place "Yosemite" --theme simmon --format pmtiles,mbtiles
# Keep intermediates locally (reprojected DEM, grayscale hillshade, styled raster)
hillgen run --place "Yosemite" --theme simmon --keep-intermediates
# Skip the public cache (fully offline)
hillgen run --place "Yosemite" --theme simmon --no-cacheHillgen ships with a built-in public cache at s3://scriptedrelief-data/. Every run automatically checks the cache before downloading or processing — no config, no auth, no AWS account needed.
The source DEMs are all public domain (USGS, Copernicus, SRTM), so the derived intermediates are too. The more people use Hillgen, the fuller the cache gets, and the faster it is for everyone.
# This just works — pulls cached intermediates automatically
hillgen run --place "Crater Lake" --theme midnight
# Second run with a different theme reuses the cached DEM and reprojection
hillgen run --place "Crater Lake" --theme alpine-glacier
# Fully offline (skip cache reads)
hillgen run --place "Crater Lake" --theme midnight --no-cache
# Point at your own private S3 bucket instead
hillgen run --place "Crater Lake" --theme midnight --s3-cache s3://my-bucket/The cache is read-through with deterministic keys — same area + source + parameters = same cache key. If anyone has ever generated Crater Lake at 3x exaggeration, you skip the DEM download and reprojection entirely and start from the cached grayscale hillshade.
Help the community by sharing your cached intermediates back:
# Generate AND upload your intermediates to the public cache
hillgen run --place "Denali" --theme midnight --contribute--contribute is on by default. No AWS credentials needed — authentication uses your GitHub account via the gh CLI, and uploads use short-lived presigned URLs handed out by a small Lambda broker. Run hillgen auth status to verify your token, and see CONTRIBUTING.md for the one-time allowlist setup. Full architecture is in docs/submission.md.
Push your locally-generated hillshade to the public library:
hillgen publish ./output/crater-lake-alpine-glacier.pmtilesThe CLI validates your file locally, uploads it to staging, and opens a GitHub PR for review. Once merged, tiles go live on scriptedrelief.com automatically. See docs/submission.md for the full submission and validation process.
The web viewer at scriptedrelief.com lets you:
- Pan/zoom any published hillshade
- Switch themes on the same region
- Compare exaggeration levels
- Download MBTiles for offline use
See docs/ARCHITECTURE.md for the full system design, including:
- Processing pipeline stages
- Caching strategy (DEM → grayscale → styled → tiled)
- Theme system internals
- DEM source catalog design
See docs/submission.md for how community contributions (intermediates and PMTiles) are submitted, validated, and promoted.
Hillgen is the successor to ilhmp (Illinois Height Modernization Project), which started as an Illinois-specific hillshade generator for ATAK offline map packages. The lessons learned from generating 23GB of tiles across 15+ regions informed this broader, source-agnostic platform.
Key improvements over ilhmp:
- Global DEM support — not locked to Illinois/ISGS
- Theme-first design — themes are portable, shareable, and the primary user-facing concept
- PMTiles-native — serverless delivery as the default, not an afterthought
- Community library — shared public gallery at scriptedrelief.com
- One-liner CLI —
--placegeocoding, auto DEM selection, zero config for simple cases - Local-first — runs entirely on your machine with no cloud dependencies
MIT
- Website: scriptedrelief.com
- GitHub: emuehlstein/hillshade-generator
- ilhmp (predecessor): emuehlstein/illinois-hillshade-gen