Files
settled-reach/tooling/planet-gen/sol_data/io_moon.py
T
jpmschweitzerandClaude Opus 4.6 18bdb1ed3d feat(assets): add Sol system handcrafted terrain pipeline
Custom pipeline for GJ-0 (Sol) that imports real NASA/USGS
planetary data instead of procedural generation. Produces the
same output format (heightmap.png, globe.png, markers.json).

Real data bodies:
- Earth: ETOPO2022 elevation + WorldClim climate + 14 rivers
- Mars: MOLA DEM + ferric biome classes + terraformed water
- Luna: LOLA DEM + lunar biome palette

Procedural fallback for Mercury, Venus, Phobos, Deimos.
Synthetic elevation from albedo for Io, Europa, Ganymede,
Callisto, Titan, Enceladus. Gas giants use existing renderer.

New biome classes 34-36 (ferric_dust/highland/lowland) for
Mars iron oxide surface. Earth features: 50 cities (smart
scatter by continent), 15 named rivers, oceans, mountains.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-07 22:22:18 +02:00

118 lines
4.7 KiB
Python

"""
Io (GJ0f-1) terrain builder.
Io is the most volcanically active body in the solar system due to
tidal heating from Jupiter. Surface is covered in sulfur and volcanic
deposits. No published global DEM exists at useful resolution — we use
the Galileo/Voyager global mosaic (albedo) to derive synthetic elevation.
Data source:
- Surface: USGS Io Galileo/Voyager global mosaic
- Elevation: synthetic from albedo (dark = caldera/lava, bright = sulfur)
Properties:
- Surface temp: ~130K background, 400-1800K at volcanic hotspots
- planet_class: "volcanic", atmosphere: "none"
"""
import numpy as np
from pathlib import Path
from scipy.ndimage import gaussian_filter
from sol_data.download import ensure_cached
from sol_data.shared import (
GRID_W, GRID_H,
load_image_as_elevation, resample_to_grid, normalize_01,
compute_hillshade, assemble_terrain,
temperature_grid_analytical,
)
# Io global mosaic (Galileo SSI + Voyager) — JPEG from USGS
# If direct download isn't available, fall back to procedural
IO_MOSAIC_URL = "https://astrogeology.usgs.gov/cache/images/bf08a5b6fa0c2ed73117dc1b6c516fa8_io_galileo_voyager_global_mosaic_1km.jpg"
IO_MOSAIC_FILE = "io_galileo_mosaic.jpg"
IO_BACKGROUND_TEMP_K = 130.0
IO_HOTSPOT_TEMP_K = 600.0
def _load_io_mosaic() -> np.ndarray:
"""Load Io global mosaic and convert to synthetic elevation."""
try:
path = ensure_cached(IO_MOSAIC_URL, IO_MOSAIC_FILE)
print(f" loading Io mosaic: {path}")
albedo = load_image_as_elevation(str(path), invert=False)
except Exception as e:
print(f" WARNING: Io mosaic unavailable ({e}), generating synthetic")
return _synthetic_io_terrain()
# Resample to grid
albedo = resample_to_grid(albedo, GRID_H, GRID_W, order=1)
# Convert albedo to elevation:
# Dark regions (low albedo) = calderas/lava flows = low elevation
# Bright regions (high albedo) = sulfur deposits = high elevation
# Smooth to create plausible topography
elevation = gaussian_filter(albedo, sigma=3.0)
elevation = normalize_01(elevation)
return elevation
def _synthetic_io_terrain() -> np.ndarray:
"""Generate synthetic Io-like terrain if mosaic unavailable."""
rng = np.random.default_rng(42)
base = rng.random((GRID_H, GRID_W)).astype(np.float32)
base = gaussian_filter(base, sigma=8.0)
# Add volcanic calderas (circular depressions)
for _ in range(30):
cy, cx = rng.integers(0, GRID_H), rng.integers(0, GRID_W)
r = rng.integers(3, 15)
y, x = np.ogrid[-cy:GRID_H-cy, -cx:GRID_W-cx]
mask = x*x + y*y <= r*r
base[mask] *= 0.3
return normalize_01(base)
def build_terrain(body_def: dict) -> dict:
"""Build Io terrain dict."""
import sys
sys.path.insert(0, str(Path(__file__).resolve().parent.parent))
from planet_simulation import compute_biome
print(" Io: loading data...")
# ── 1. Elevation ────────────────────────────────────────────────────
elevation = _load_io_mosaic()
sea_level = 0.0
surface_water = np.zeros((GRID_H, GRID_W), dtype=bool)
# ── 2. Temperature ──────────────────────────────────────────────────
# Background ~130K, volcanic hotspots much hotter
temperature_K = temperature_grid_analytical(
base_T_K=IO_BACKGROUND_TEMP_K,
elevation=elevation,
lapse_rate_K_per_unit=-200.0, # low elevation = hot (lava)
lat_gradient_K=10.0,
)
# Volcanic hotspots: low-elevation areas are hot
hotspot_mask = elevation < 0.25
temperature_K[hotspot_mask] += 300.0
# ── 3. Moisture ─────────────────────────────────────────────────────
moisture = np.zeros((GRID_H, GRID_W), dtype=np.float32)
# ── 4. Biome ────────────────────────────────────────────────────────
biome = compute_biome(body_def, elevation, sea_level, surface_water,
temperature_K, moisture)
# ── 5. Hillshade ────────────────────────────────────────────────────
hillshade = compute_hillshade(elevation)
return assemble_terrain(
elevation=elevation, temperature_K=temperature_K,
moisture=moisture, biome=biome,
surface_water=surface_water, hillshade=hillshade,
rivers=[], sea_level=sea_level,
)