Static Map Georeferencing & Deployment Guide
End-to-end SOP for calibrating a static map image (e.g. supplier-provided mine site map) and deploying it as a tile overlay on the Dashboard heatmap.
Pipeline Overview
Raw Image (PNG/JPG/TIF)
→ QGIS Georeferencer (calibrate with GCPs)
→ GeoTIFF (.tif with embedded coordinates)
→ gdal2tiles.py (generate TMS tile pyramid)
→ convert-tiles-to-pmtiles.sh (PNG → WebP → MBTiles → PMTiles)
→ Cloudflare R2 upload
→ overlayConfig.ts (register in Dashboard)Prerequisites
| Tool | Install | Purpose |
|---|---|---|
| QGIS | qgis.org (latest stable) | Georeferencing |
| GDAL | Bundled with QGIS, or brew install gdal | Tile generation (gdal2tiles.py) |
| cwebp | brew install webp | PNG → WebP compression |
| mb-util | pip install mbutil | WebP tiles → MBTiles |
| pmtiles | brew install pmtiles | MBTiles → PMTiles |
| wrangler | npm install -g wrangler | Cloudflare R2 upload |
QGIS plugin: QuickMapServices (for satellite reference basemap).
Step 1: QGIS Project Setup
- Open QGIS
- Set project CRS to EPSG:3857 (bottom-right corner → click CRS → search 3857 → OK)
Load a Reference Basemap
- Plugins → Manage and Install Plugins → search
QuickMapServices→ Install - Web → QuickMapServices → Settings → More services → Get contributed pack
- Web → QuickMapServices → Google → Google Satellite
The satellite imagery is now visible in the main map canvas.
Step 2: Georeferencing
Open Georeferencer
Raster → Georeferencer… (opens a separate window)
Load the Source Image
In the Georeferencer window: Open Raster → select your image (PNG/JPG/TIF).
If prompted for CRS: select EPSG:3857 or Unknown — doesn't matter, the GCPs determine the actual position.
Add Ground Control Points (GCPs)
This is the most critical step. Each GCP maps a pixel on your image to a real-world coordinate.
Per-point workflow:
- Click Add Point in the Georeferencer toolbar
- Click a clear feature on your image (road intersection, building corner, pit edge)
- In the popup, select From Map Canvas
- Switch to the main QGIS window → click the same feature on the satellite basemap
- Confirm — the point appears in the GCP table
Point selection rules:
- Minimum 6 points, recommended 10–15
- Distribute evenly: four corners + center + key inflection points
- Use stable features: road intersections, building corners, bridge ends, pit edges
- Avoid: tree shadows, water edges, temporary stockpile boundaries
Strategy: Start with 4 corner points to anchor the overall extent, then add interior points to reduce local distortion.
Save GCP Points
Before running the transform — save your points so you can reload and adjust later:
Georeferencer toolbar → Save GCP Points as… → e.g. site-name_v1.points
To reload: Load GCP Points… after opening the same source image.
Transformation Settings
Georeferencer → gear icon (or Settings → Transformation Settings):
| Setting | Recommended Value | Notes |
|---|---|---|
| Transformation type | Polynomial 1 (affine) | Start here. Handles translation, rotation, non-uniform scaling |
| Target SRS | EPSG:3857 | Matches project CRS |
| Resampling method | Nearest neighbour | Sharper edges for line maps. Use Bilinear for photo imagery |
| Output raster | site_georef.tif | GeoTIFF output path |
| Load in QGIS when done | ✅ | Auto-loads result for verification |
If edges don't align after running:
- Try Polynomial 2 (needs more points, handles moderate distortion)
- Or Thin Plate Spline (rubber-sheet, handles severe distortion but can warp locally)
- Never upgrade transform type without enough well-distributed points
Check Residuals
The GCP table shows a Residual column for each point.
- If one point has a significantly higher residual than others → you likely clicked the wrong spot
- Right-click → Delete, then re-add at a more precise location
- Goal: uniform residuals across all points. A few bad points are worse than fewer good points.
Run Georeferencing
Click Start Georeferencing in the toolbar.
Output: a GeoTIFF with embedded coordinate information.
Step 3: Verify Alignment
Back in the main QGIS window:
- The output GeoTIFF should appear as a layer (if you checked "Load in QGIS when done")
- Right-click the layer → Properties → Transparency → set global opacity to 40–60%
- Zoom in and check road intersections, boundaries, buildings against the satellite basemap
Troubleshooting:
| Symptom | Fix |
|---|---|
| Center accurate, edges drift | Add more edge GCPs, or upgrade to Polynomial 2 / TPS |
| Entire image is rotated/offset | A few GCPs are wrong — check residuals, delete outliers |
| Local warping/distortion | TPS + unstable points — switch back to Polynomial 1 or remove bad points |
Iterate: adjust points → re-run → verify until satisfied.
Step 4: Generate TMS Tiles
Convert the GeoTIFF into a TMS tile directory using gdal2tiles.py:
gdal2tiles.py -z 14-20 -w none -r bilinear --processes=4 site_georef.tif ./tiles| Flag | Purpose |
|---|---|
-z 14-20 | Zoom levels to generate (14 = overview, 20 = max detail) |
-w none | Skip HTML viewer generation |
-r bilinear | Resampling method (smooth interpolation) |
--processes=4 | Parallel processing |
Output: ./tiles/{z}/{x}/{y}.png in TMS scheme.
This can take a while for high-zoom large areas. Marandoo (zoom 14–20) produced ~138K tiles.
Step 5: Convert to PMTiles
./scripts/convert-tiles-to-pmtiles.sh ./tiles site-namePipeline: PNG → WebP (quality 80) → MBTiles → PMTiles
Output: build/pmtiles/site-name.pmtiles
The script is idempotent — delete intermediate outputs (build/pmtiles/site-name-webp/, .mbtiles) to reconvert.
Step 6: Upload to Cloudflare R2
npx wrangler r2 object put map-tiles/site-name.pmtiles \
--file=build/pmtiles/site-name.pmtiles \
--remoteNo Worker changes needed — the Worker automatically maps {site} to {site}.pmtiles in the R2 bucket.
Step 7: Register in Dashboard
Add an entry in src/features/heatmap/overlayConfig.ts:
{
type: "tile",
id: "site-name-overlay",
name: "Site Name",
mineSiteId: "uuid-from-cfg_mine_sites",
tileBasePath: "https://map-tiles.fudong.workers.dev/site-name",
minZoom: 14,
maxZoom: 20,
bounds: { north: ..., south: ..., east: ..., west: ... },
}Get bounds from tilemapresource.xml in the tiles output directory, or from the GeoTIFF extent in QGIS (right-click layer → Properties → Information → Extent).
Deploy the Dashboard with the updated config.
Quick Reference
Full command sequence after QGIS georeferencing is done:
# Generate tiles
gdal2tiles.py -z 14-20 -w none -r bilinear --processes=4 site_georef.tif ./tiles
# Convert to PMTiles
./scripts/convert-tiles-to-pmtiles.sh ./tiles site-name
# Upload to R2
npx wrangler r2 object put map-tiles/site-name.pmtiles \
--file=build/pmtiles/site-name.pmtiles \
--remoteThen update overlayConfig.ts and deploy.