Closes 9 of 10 review items (blocker 1 handled inadd2507e+d78d3b59): - decisions/economics.md: reformat D-189 §5 amendment to standard Amendment (YYYY-MM-DD) block pattern (review #4) - tooling/planet-gen/sol_name_fixes.py: dedup guard + argparse --dry-run (reviews #5, #10a) - tooling/planet-gen/sol_markers/earth_features.json: trim to 11 cities with selection rationale (review #6A); user-approved rebalance Sydney → Lagos and London → Brussels (review #6B) - wiki/star-systems/GJ-380/bodies/GJ380c/markers.json: 2 secondary features renamed to Akan/Asante register — Kesset → Nkwanta Beck, Holt Spur → Bosomtwe Spur (review #7) - docs/atlas/hand-refine-log.md: Aethelred lore-migration documentation + see-also cross-link to refine_log_849.md (reviews #8, #13) - tooling/planet-gen/refine_log_849.md: rebalance addendum - wiki/star-systems/GJ-0/bodies/GJ0d/markers.json, server/data/ systems.db: re-synced after rebalance Stub depth (review blocker #2) handled via split — tracked as follow-up ticket #861 (three-layer narrative authoring). Final Earth cities (11): Beijing, Brussels, Cairo, Delhi, Istanbul, Lagos, Moscow, New York, São Paulo, Singapore, Tokyo. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
148 lines
6.3 KiB
Python
148 lines
6.3 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
sol_name_fixes.py — Name previously-unnamed Sol body auto-detected features.
|
|
|
|
Targets features with null names: Earth oceans/rivers, Luna/Mars/Europa mountain ranges.
|
|
All names are real-world geographic names for Sol bodies.
|
|
|
|
Usage:
|
|
python3 sol_name_fixes.py # apply all fixes
|
|
python3 sol_name_fixes.py --dry-run # print planned changes without writing
|
|
"""
|
|
import argparse
|
|
import json
|
|
from pathlib import Path
|
|
|
|
REPO_ROOT = (Path(__file__).resolve().parent / ".." / "..").resolve()
|
|
WIKI = REPO_ROOT / "wiki" / "star-systems" / "GJ-0" / "bodies"
|
|
|
|
# Keys are feature IDs; values are the names to assign.
|
|
FIXES = {
|
|
# Earth (GJ0d): 1 unnamed ocean + 3 unnamed rivers (auto-detected terrain artifacts)
|
|
"GJ0d": {
|
|
"oceans": {
|
|
"water_1": "The World Ocean", # 70% surface = all Earth's oceans unified
|
|
},
|
|
"rivers": {
|
|
"river_1": "Dnieper", # Eastern Europe ~50°N, 17°E
|
|
"river_4": "Tone River", # Japan, Kanto plain ~36°N, 140°E
|
|
"river_10": "Cagayan", # Northern Philippines ~18°N, 121°E
|
|
},
|
|
},
|
|
# Luna (GJ0d-1): 24 unnamed mountain ranges — all auto-detected; named after
|
|
# real lunar mountain systems, massifs, and scarps sorted by descending area.
|
|
"GJ0d-1": {
|
|
"mountain_ranges": {
|
|
"range_5": "Montes Apenninus", # 11221 cells — largest lunar mountain range
|
|
"range_34": "Montes Caucasus", # 3670 cells
|
|
"range_209": "Montes Jura", # 367 cells — on Mare Imbrium NW
|
|
"range_551": "Montes Alpes", # 289 cells — lunar Alps
|
|
"range_576": "Montes Rook", # 258 cells — south polar mountains
|
|
"range_599": "Montes Cordillera", # 164 cells — Orientale basin rim
|
|
"range_326": "Montes Carpatus", # 106 cells — south of Mare Imbrium
|
|
"range_555": "Montes Pyrenaeus", # 94 cells — east of Mare Nectaris
|
|
"range_586": "Montes Taurus", # 90 cells — NE of Mare Serenitatis
|
|
"range_589": "Montes Teneriffe", # 52 cells — isolated massif
|
|
"range_124": "Montes Harbinger", # 51 cells — west of Aristarchus
|
|
"range_587": "Montes Recti", # 48 cells — north Mare Imbrium
|
|
"range_522": "Pico Mons", # 35 cells — isolated peak
|
|
"range_579": "Haemus Montes", # 33 cells — south of Mare Serenitatis
|
|
"range_231": "Gruithuisen Domes", # 25 cells — volcanic domes
|
|
"range_580": "Mons La Hire", # 25 cells — volcanic dome, Mare Imbrium
|
|
"range_570": "Montes Riphaeus", # 24 cells — Oceanus Procellarum ridge
|
|
"range_596": "Montes Secchi", # 24 cells — east of Mare Fecunditatis
|
|
"range_604": "Montes Spitzbergen", # 24 cells — isolated massif
|
|
"range_493": "Malapert Mountain", # 23 cells — south pole region
|
|
"range_406": "Doerfel Mountains", # 26 cells — south pole far side
|
|
"range_432": "Altai Scarp", # 20 cells — Rupes Altai
|
|
"range_79": "Mons Rümker", # 21 cells — shield volcano
|
|
"range_348": "Leibnitz Mountains", # 21 cells — south polar range
|
|
},
|
|
},
|
|
# Mars (GJ0e): 4 unnamed mountain ranges in the anti-Tharsis hemisphere
|
|
"GJ0e": {
|
|
"mountain_ranges": {
|
|
"range_2": "Tyrrhena Mons", # ancient volcanic highland (area=67)
|
|
"range_3": "Hadriaca Mons", # old shield volcano (area=31)
|
|
"range_4": "Amphitrites Montes", # near south polar region (area=28)
|
|
"range_5": "Hellespontus Montes", # cross-refs Hellas Station (area=87)
|
|
},
|
|
},
|
|
# Europa (GJ0f-2): 1 unnamed mountain — Europa's ridged ice terrain (area=123557 cells)
|
|
"GJ0f-2": {
|
|
"mountain_ranges": {
|
|
"range_1": "Conamara Ridges", # cross-refs Conamara Station + Conamara Chaos
|
|
},
|
|
},
|
|
}
|
|
|
|
SECTION_MAP = {
|
|
"oceans": "oceans",
|
|
"rivers": "rivers",
|
|
"mountain_ranges": "mountain_ranges",
|
|
}
|
|
|
|
|
|
def _check_dedup(fixes: dict) -> None:
|
|
"""Raise if any name is assigned to more than one feature ID within a body."""
|
|
for body_id, sections in fixes.items():
|
|
for section, id_map in sections.items():
|
|
seen: dict[str, str] = {}
|
|
for fid, name in id_map.items():
|
|
if name in seen:
|
|
raise ValueError(
|
|
f"Duplicate name {name!r} in {body_id}/{section}: "
|
|
f"{seen[name]} and {fid} both assigned"
|
|
)
|
|
seen[name] = fid
|
|
|
|
|
|
def apply_sol_fixes(dry_run: bool = False) -> None:
|
|
_check_dedup(FIXES)
|
|
for body_id, body_fixes in FIXES.items():
|
|
path = WIKI / body_id / "markers.json"
|
|
if not path.exists():
|
|
print(f" SKIP {body_id}: markers.json not found")
|
|
continue
|
|
|
|
with open(path) as f:
|
|
markers = json.load(f)
|
|
|
|
changed = False
|
|
for section, id_map in body_fixes.items():
|
|
feature_list = markers.get(SECTION_MAP[section], [])
|
|
for feature in feature_list:
|
|
fid = feature.get("id", "")
|
|
if fid in id_map:
|
|
old = feature.get("name")
|
|
new = id_map[fid]
|
|
if old != new:
|
|
print(f" [{body_id}/{section}] {fid}: {old!r} → {new!r}")
|
|
if not dry_run:
|
|
feature["name"] = new
|
|
changed = True
|
|
|
|
if changed:
|
|
if dry_run:
|
|
print(f" (dry-run) Would write: {path}")
|
|
else:
|
|
with open(path, "w") as f:
|
|
json.dump(markers, f, indent=2)
|
|
print(f" Written: {path}")
|
|
else:
|
|
print(f" No changes for {body_id}")
|
|
print()
|
|
|
|
|
|
if __name__ == "__main__":
|
|
parser = argparse.ArgumentParser(
|
|
description="Name previously-unnamed Sol body auto-detected features."
|
|
)
|
|
parser.add_argument(
|
|
"--dry-run",
|
|
action="store_true",
|
|
help="print planned changes without writing any files",
|
|
)
|
|
args = parser.parse_args()
|
|
apply_sol_fixes(dry_run=args.dry_run)
|