{"id":47718142,"url":"https://github.com/mapriot/massif","last_synced_at":"2026-04-03T20:01:16.646Z","repository":{"id":348107649,"uuid":"1194530650","full_name":"mapriot/massif","owner":"mapriot","description":"Fast terrain-RGB tile generator — elevation rasters to PMTiles/MBTiles for MapLibre GL hillshading and 3D terrain","archived":false,"fork":false,"pushed_at":"2026-03-31T14:45:31.000Z","size":54,"stargazers_count":3,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"master","last_synced_at":"2026-04-03T05:09:44.525Z","etag":null,"topics":["dem","elevation","gdal","geotiff","hillshading","maplibre","mbtiles","pmtiles","png","rust","terrain","terrain-rgb","webp"],"latest_commit_sha":null,"homepage":"","language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/mapriot.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-03-28T13:37:15.000Z","updated_at":"2026-04-03T04:59:36.000Z","dependencies_parsed_at":null,"dependency_job_id":"2bea5732-534d-491f-9fae-05d04d1a006d","html_url":"https://github.com/mapriot/massif","commit_stats":null,"previous_names":["mapriot/massif"],"tags_count":2,"template":false,"template_full_name":null,"purl":"pkg:github/mapriot/massif","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mapriot%2Fmassif","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mapriot%2Fmassif/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mapriot%2Fmassif/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mapriot%2Fmassif/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/mapriot","download_url":"https://codeload.github.com/mapriot/massif/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mapriot%2Fmassif/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31374051,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-03T17:53:18.093Z","status":"ssl_error","status_checked_at":"2026-04-03T17:53:17.617Z","response_time":107,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["dem","elevation","gdal","geotiff","hillshading","maplibre","mbtiles","pmtiles","png","rust","terrain","terrain-rgb","webp"],"created_at":"2026-04-02T19:08:40.534Z","updated_at":"2026-04-03T20:01:16.628Z","avatar_url":"https://github.com/mapriot.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"# massif\n\nFast terrain-RGB tile generator from elevation rasters.\n\nConverts GeoTIFF, VRT, or any GDAL-supported elevation raster into Mapbox or Terrarium terrain-RGB tiles, packaged as [PMTiles](https://protomaps.com/docs/pmtiles) or [MBTiles](https://wiki.openstreetmap.org/wiki/MBTiles). Ready to use with MapLibre GL for hillshading and 3D terrain.\n\nBuilt as a fast Rust replacement for [rio-rgbify](https://github.com/mapbox/rio-rgbify). Uses all CPU cores via [rayon](https://github.com/rayon-rs/rayon), shows real-time progress, and outputs to modern tile containers — no Python overhead, no guessing when it'll finish.\n\n## Installation\n\n### Prerequisites\n\nGDAL must be installed on your system.\n\n| Platform | Command |\n|---|---|\n| macOS | `brew install gdal` |\n| Ubuntu / Debian | `sudo apt install libgdal-dev gdal-bin` |\n| Fedora / RHEL | `sudo dnf install gdal-devel` |\n| Windows | [OSGeo4W](https://trac.osgeo.org/osgeo4w/) or [Conda](https://anaconda.org/conda-forge/gdal) — ensure `gdal-config` is on your PATH *(untested)* |\n\n### Install massif\n\n**From crates.io**\n```bash\ncargo install massif\n```\n\n**From source**\n```bash\ngit clone https://github.com/mapriot/massif\ncd massif\ncargo build --release\n# Binary is at target/release/massif\n```\n\nOn macOS with Homebrew GDAL you may need:\n```bash\nPKG_CONFIG_PATH=\"/opt/homebrew/lib/pkgconfig\" cargo build --release\n```\n\n## Usage\n\n```\nmassif [OPTIONS] \u003cINPUT\u003e \u003cOUTPUT\u003e\n```\n\n`INPUT` is any GDAL-supported elevation raster (GeoTIFF, VRT, HGT, etc., any CRS).\n`OUTPUT` is `.pmtiles` or `.mbtiles` — the container format is inferred from the extension.\n\n### Quick start\n\n```bash\n# Fastest — preview / iteration (WebP, no extra compression)\nmassif input.tif output.pmtiles\n\n# Production — good balance of size and speed\nmassif --compress 6 input.tif output.pmtiles\n\n# MBTiles output — same flags, different extension\nmassif --compress 6 input.tif output.mbtiles\n\n# Terrarium encoding\nmassif --encoding terrarium --compress 6 input.tif output.pmtiles\n\n# PNG tiles\nmassif --format png --compress 6 input.tif output.pmtiles\n\n# Maximum compression for smallest files (diminishing returns past r=5)\nmassif --compress 6 -r 5 input.tif output.pmtiles\n```\n\n### All options\n\n| Flag | Default | Description |\n|---|---|---|\n| `--encoding` | `mapbox` | RGB encoding: `mapbox` or `terrarium` |\n| `--format` | `webp` | Tile image format: `webp` or `png` |\n| `--compress` | *(omitted)* | Compression effort 1–9; omit for fastest |\n| `--min-z` | `5` | Minimum zoom level |\n| `--max-z` | `12` | Maximum zoom level |\n| `--nodata` | *(from raster)* | Override nodata value (e.g. `0`, `-9999`, `-32768`) |\n| `-j, --workers` | all CPUs | Thread count |\n\n**Mapbox encoding only:**\n\n| Flag | Default | Description |\n|---|---|---|\n| `-b, --base-val` | `-10000` | Base elevation offset |\n| `-i, --interval` | `0.1` | Elevation precision in metres |\n| `-r, --round-digits` | `3` | Zero out lowest N bits of encoded value (reduces entropy) |\n\n## Input preparation\n\n**GDAL overviews** precompute downsampled versions of your raster so massif can read low-zoom tiles cheaply instead of resampling the full-resolution data each time. This reduces processing time by 20–40%.\n\n```bash\n# Single TIF — writes a sidecar .ovr file, does not modify the input\ngdaladdo -ro -r average input.tif 2 4 8 16 32 64 128 256\n\n# VRT — same approach, creates merged.vrt.ovr\ngdaladdo -ro -r average merged.vrt 2 4 8 16 32 64 128 256\n```\n\nMassif (via GDAL) picks up the `.ovr` sidecar automatically. The tradeoff is storage: the `.ovr` file can be as large as the source data itself. If disk space is constrained, skip overviews and run without — massif handles it, just slower.\n\n## Performance\n\n### Single large TIF — 7.2 GB (Indonesia, zoom 5–12, ~142K tiles)\n\n| Machine | Version | Overviews | Command | Time | Output |\n|---|---|---|---|---|---|\n| **Apple M4 Pro, 14 threads** | **v0.1.1** | **yes** | **`massif`** | **0:51** | **4,560 MB** |\n| **Apple M4 Pro, 14 threads** | **v0.1.1** | **yes** | **`massif --compress 6`** | **4:52** | **2,844 MB** |\n| **Apple M4 Pro, 14 threads** | **v0.1.1** | **no** | **`massif`** | **2:02** | **4,560 MB** |\n| **Apple M4 Pro, 14 threads** | **v0.1.1** | **no** | **`massif --compress 6`** | **6:18** | **2,844 MB** |\n| Apple M4 Pro, 14 threads | v0.1.0 | no | `massif` | 2:30 | 4,560 MB |\n| Apple M4 Pro, 14 threads | v0.1.0 | yes | `massif` | 1:28 | 4,560 MB |\n| Apple M4 Pro, 14 threads | v0.1.0 | no | `massif --compress 6` | 6:29 | 2,844 MB |\n| Apple M4 Pro, 14 threads | v0.1.0 | yes | `massif --compress 6` | 5:35 | 2,844 MB |\n| Xeon Silver 4210, 20 threads | v0.1.0 | no | `massif` | 7:20 | 4,560 MB |\n| Xeon Silver 4210, 20 threads | v0.1.0 | yes | `massif` | 5:42 | 4,560 MB |\n| Xeon Silver 4210, 20 threads | v0.1.0 | no | `massif --compress 6` | 16:21 | 2,844 MB |\n| Xeon Silver 4210, 20 threads | v0.1.0 | yes | `massif --compress 6` | 12:44 | 2,844 MB |\n| Xeon Silver 4210, 20 threads | — | no | `rio-rgbify` | 25:51 | ~2,810 MB |\n\n### VRT of 70 TIFs — 66 GB total (Europe + Oceania, zoom 5–12)\n\n| Machine | Command | Version |Time | Output |\n|---|---|---|---|---|\n| Xeon Silver 4210, 20 threads | `massif` | v0.1.1 |**1h 36m** | 48,062 MB |\n| Xeon Silver 4210, 20 threads | `massif --compress 6` | v0.1.1 |**4h 00m** | 29,877 MB |\n| Xeon Silver 4210, 20 threads | `massif` | v0.1.0 |**15h 47m** | 48,062 MB |\n| Xeon Silver 4210, 20 threads | `rio-rgbify` | - | DNF after 48h | — |\n\nrio-rgbify did not finish after 48 hours on the same machine and dataset. All massif tiles are 512×512 lossless WebP images. The Xeon results were measured on a server under normal production load — actual times on an idle machine would be lower.\n\n| Setting | Impact | Notes |\n|---|---|---|\n| EPSG:4326 input | **~2.5× faster** (no compress) | massif skips GDAL transforms entirely; use `gdalwarp -t_srs EPSG:4326` |\n| GDAL overviews | **−20–40%** time | Effective for single TIFs; `.ovr` can match source file size |\n| WebP vs PNG | WebP is **2× smaller** | Use PNG only if client doesn't support WebP |\n| `--compress 6` | **−38%** size vs no compression | Best size/speed tradeoff; gains flatten past 5 |\n| `-r 3` (default) | **−43%** size vs r=0 | Biggest lever for file size; invisible for hillshading at most latitudes |\n| Terrarium vs Mapbox | Terrarium is **3.1× larger** | No round-digits equivalent; use Mapbox when possible |\n\nFor full benchmark methodology, all 36 parameter combinations, and recommended settings by use case, see [docs/benchmarks.md](docs/benchmarks.md).\n\n## Encoding schemes\n\n### Mapbox (default)\n\n```\nencoded = floor((elevation - base_val) / interval)\nR = (encoded \u003e\u003e 16) \u0026 0xFF\nG = (encoded \u003e\u003e 8)  \u0026 0xFF\nB =  encoded        \u0026 0xFF\n```\n\nMapLibre decodes as:\n```\nheight = base_val + (R × 65536 + G × 256 + B) × interval\n```\n\nWith the defaults (`-b -10000 -i 0.1`), the encodable range is −10,000 m to +1,677,721.5 m at 0.1 m precision. The `-r` flag zeroes the lowest N bits of the encoded integer — this reduces entropy for better compression with negligible quality loss for hillshading. Note: `-r 3` may produce visible artifacts at high latitudes (e.g. northern Norway, Svalbard, Greenland) where elevation gradients are subtle; use `-r 1` or `-r 0` for polar regions.\n\n### Terrarium\n\n```\nval = elevation + 32768\nR = floor(val / 256)\nG = floor(val) mod 256\nB = floor(frac(val) × 256)\n```\n\nMapLibre decodes as:\n```\nheight = (R × 256 + G + B / 256) − 32768\n```\n\nRange: −32,768 m to +32,767.996 m at ~0.004 m precision. Used by Mapzen and many open elevation datasets. No configurable parameters — `-b`, `-i`, and `-r` are ignored with a warning.\n\n## Using with MapLibre GL\n\n```json\n{\n  \"sources\": {\n    \"terrain\": {\n      \"type\": \"raster-dem\",\n      \"url\": \"pmtiles://https://example.com/terrain.pmtiles\",\n      \"encoding\": \"mapbox\",\n      \"tileSize\": 512\n    }\n  },\n  \"terrain\": {\n    \"source\": \"terrain\",\n    \"exaggeration\": 1.5\n  },\n  \"layers\": [\n    {\n      \"id\": \"hillshading\",\n      \"type\": \"hillshade\",\n      \"source\": \"terrain\"\n    }\n  ]\n}\n```\n\nFor Terrarium output, set `\"encoding\": \"terrarium\"` in the source.\n\n## Input formats\n\nAny raster supported by GDAL — GeoTIFF (`.tif`), Virtual Raster (`.vrt`), HGT, IMG, and more. Any pixel data type works (Float32, Float64, Int16, UInt16, etc.) — GDAL converts to Float32 internally. The input can be in any CRS; massif reprojects each tile to Web Mercator on the fly.\n\nCommon elevation data sources:\n- [ALOS World 3D](https://www.eorc.jaxa.jp/ALOS/en/dataset/aw3d30/aw3d30_e.htm)\n- [SRTM](https://www.usgs.gov/centers/eros/science/usgs-eros-archive-digital-elevation-shuttle-radar-topography-mission-srtm)\n- [Copernicus DEM](https://dataspace.copernicus.eu/explore-data/data-collections/copernicus-contributing-missions/collections-description/COP-DEM) (GLO-30, GLO-90)\n\n## License\n\nMIT — see [LICENSE](LICENSE)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmapriot%2Fmassif","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmapriot%2Fmassif","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmapriot%2Fmassif/lists"}