fix(tooling): address PR #129 review — atlas generator, schema, Makefile
- ON DELETE CASCADE added to every atlas_* foreign key (atlas_body_grids,
atlas_cities, atlas_roads, atlas_railroads, atlas_pois, atlas_rivers,
atlas_oceans, atlas_mountain_ranges). Previously, deleting a body from
the bodies table or NULL-ing its terrain_reference would leave orphan
atlas rows forever — sync_markers_to_db only cleans up for bodies it
re-processes. The existing atlas tables in systems.db were dropped and
recreated with the new constraint; FK list now reports CASCADE.
- Atlas DDL deduplicated. systems-schema.sql is now the single source of
truth, bracketed by `-- BEGIN ATLAS INDEX` / `-- END ATLAS INDEX`
markers. generate_atlas.py reads that block via `_load_atlas_schema()`
and applies it at runtime, so there is no second copy of the DDL to
keep in sync. Adding a column requires one edit, not two.
- Uniqueness guard on city coordinates. `_enforce_unique_city_coords`
runs at the end of `place_cities` and deterministically perturbs any
duplicate (row, col) via a fixed spiral walk to the first free
walkable land cell. Rare in practice but the MST collapses to a
zero-distance edge otherwise, producing an empty A* path and silently
dropping the road.
- Grid header validation. `load_markers` now raises `AtlasGridMismatch`
if the loaded `grid: {w, h}` header does not match `GRID_W`/`GRID_H`.
Both the incremental-skip path and the regenerate path route through
this loader, so a hand-authored template shipping a different grid
size fails loud with a per-body error rather than silently producing
half-scale coordinates.
- Unused `seed_rng` parameter removed from `_analyse_terrain`. The
function is RNG-free (continent flood-fill, habitability scoring,
river-mouth dedup, cost grid — all pure functions of terrain). The
false API contract made it look like terrain analysis consumed RNG
state and had to be sequenced with downstream RNG use.
- `_score_capital_sites` river-mouth bonus now builds one sparse
accumulator with all mouth points set at once and runs a single
`gaussian_filter` call, instead of O(n_mouths) filter calls over
single-point images.
- `binary_dilation(analysis["land_mask"] == False)` replaced with the
idiomatic `~analysis["land_mask"]`, matching the convention used
elsewhere in the file.
- `atlas-generate` Makefile target now guards on
`SELECT COUNT(*) FROM bodies WHERE terrain_reference IS NOT NULL`.
On a fresh DB that count is 0 and the generator previously exited
"success" after processing zero bodies. The target now fails loud
with a pointer to `populate_terrain_reference.py`.
- `main.rs` SimRng defensive re-insertion gains a long comment
explaining the exact plugin-ordering hazard it guards against, so
future readers don't treat the line as dead code. Tied to #826.
This commit is contained in:
@@ -339,17 +339,20 @@ CREATE TABLE IF NOT EXISTS corp_lifecycle_events (
|
||||
created_at TEXT DEFAULT (datetime('now'))
|
||||
);
|
||||
|
||||
-- Atlas index (D-191 §8, #832) — scalar metadata mirror of markers.json files.
|
||||
-- BEGIN ATLAS INDEX (D-191 §8, #832) -- DO NOT EDIT THE MARKER COMMENTS
|
||||
-- Atlas index — scalar metadata mirror of markers.json files.
|
||||
-- Source of truth is wiki/star-systems/.../markers.json (next to the heightmap);
|
||||
-- these tables exist so the atlas implant app and development queries don't
|
||||
-- have to scan hundreds of JSON files. Polyline geometry stays in the files —
|
||||
-- the DB only stores scalar/filterable fields + `point_count` as a rough length
|
||||
-- proxy. Populated and refreshed by tooling/planet-gen/generate_atlas.py.
|
||||
-- proxy. Populated and refreshed by tooling/planet-gen/generate_atlas.py, which
|
||||
-- extracts this entire block (between BEGIN/END ATLAS INDEX markers) from this
|
||||
-- file at runtime so the DDL lives in exactly one place.
|
||||
|
||||
-- Per-body grid dimensions (one row per body that has a markers.json).
|
||||
-- Lets any query interpret the pixel coordinates below without touching disk.
|
||||
CREATE TABLE IF NOT EXISTS atlas_body_grids (
|
||||
body_id TEXT PRIMARY KEY REFERENCES bodies(body_id),
|
||||
body_id TEXT PRIMARY KEY REFERENCES bodies(body_id) ON DELETE CASCADE,
|
||||
grid_w INTEGER NOT NULL,
|
||||
grid_h INTEGER NOT NULL,
|
||||
updated_at TEXT DEFAULT (datetime('now'))
|
||||
@@ -357,7 +360,7 @@ CREATE TABLE IF NOT EXISTS atlas_body_grids (
|
||||
|
||||
CREATE TABLE IF NOT EXISTS atlas_cities (
|
||||
city_id TEXT PRIMARY KEY, -- "<body_id>/<local_id>" e.g. "GJ380c/city_0"
|
||||
body_id TEXT NOT NULL REFERENCES bodies(body_id),
|
||||
body_id TEXT NOT NULL REFERENCES bodies(body_id) ON DELETE CASCADE,
|
||||
local_id TEXT NOT NULL, -- "city_0", "city_1" — matches markers.json id
|
||||
name TEXT NOT NULL DEFAULT '',
|
||||
kind TEXT NOT NULL, -- capital|city
|
||||
@@ -369,7 +372,7 @@ CREATE TABLE IF NOT EXISTS atlas_cities (
|
||||
|
||||
CREATE TABLE IF NOT EXISTS atlas_roads (
|
||||
road_id TEXT PRIMARY KEY, -- "<body_id>/<local_id>"
|
||||
body_id TEXT NOT NULL REFERENCES bodies(body_id),
|
||||
body_id TEXT NOT NULL REFERENCES bodies(body_id) ON DELETE CASCADE,
|
||||
local_id TEXT NOT NULL,
|
||||
name TEXT NOT NULL DEFAULT '',
|
||||
kind TEXT NOT NULL, -- commercial|highway|rural|...
|
||||
@@ -379,7 +382,7 @@ CREATE TABLE IF NOT EXISTS atlas_roads (
|
||||
|
||||
CREATE TABLE IF NOT EXISTS atlas_railroads (
|
||||
railroad_id TEXT PRIMARY KEY, -- "<body_id>/<local_id>"
|
||||
body_id TEXT NOT NULL REFERENCES bodies(body_id),
|
||||
body_id TEXT NOT NULL REFERENCES bodies(body_id) ON DELETE CASCADE,
|
||||
local_id TEXT NOT NULL,
|
||||
name TEXT NOT NULL DEFAULT '',
|
||||
kind TEXT NOT NULL, -- passenger_freight|freight|maglev|...
|
||||
@@ -389,7 +392,7 @@ CREATE TABLE IF NOT EXISTS atlas_railroads (
|
||||
|
||||
CREATE TABLE IF NOT EXISTS atlas_pois (
|
||||
poi_id TEXT PRIMARY KEY, -- "<body_id>/<local_id>"
|
||||
body_id TEXT NOT NULL REFERENCES bodies(body_id),
|
||||
body_id TEXT NOT NULL REFERENCES bodies(body_id) ON DELETE CASCADE,
|
||||
local_id TEXT NOT NULL,
|
||||
name TEXT NOT NULL DEFAULT '',
|
||||
kind TEXT NOT NULL, -- transit|institutional|cultural|...
|
||||
@@ -400,7 +403,7 @@ CREATE TABLE IF NOT EXISTS atlas_pois (
|
||||
|
||||
CREATE TABLE IF NOT EXISTS atlas_rivers (
|
||||
river_id TEXT PRIMARY KEY, -- "<body_id>/<local_id>"
|
||||
body_id TEXT NOT NULL REFERENCES bodies(body_id),
|
||||
body_id TEXT NOT NULL REFERENCES bodies(body_id) ON DELETE CASCADE,
|
||||
local_id TEXT NOT NULL,
|
||||
name TEXT NOT NULL DEFAULT '', -- empty for untitled procedural rivers
|
||||
point_count INTEGER NOT NULL,
|
||||
@@ -409,7 +412,7 @@ CREATE TABLE IF NOT EXISTS atlas_rivers (
|
||||
|
||||
CREATE TABLE IF NOT EXISTS atlas_oceans (
|
||||
water_id TEXT PRIMARY KEY, -- "<body_id>/<local_id>"
|
||||
body_id TEXT NOT NULL REFERENCES bodies(body_id),
|
||||
body_id TEXT NOT NULL REFERENCES bodies(body_id) ON DELETE CASCADE,
|
||||
local_id TEXT NOT NULL,
|
||||
name TEXT NOT NULL DEFAULT '',
|
||||
kind TEXT NOT NULL, -- lake|sea|ocean
|
||||
@@ -421,7 +424,7 @@ CREATE TABLE IF NOT EXISTS atlas_oceans (
|
||||
|
||||
CREATE TABLE IF NOT EXISTS atlas_mountain_ranges (
|
||||
range_id TEXT PRIMARY KEY, -- "<body_id>/<local_id>"
|
||||
body_id TEXT NOT NULL REFERENCES bodies(body_id),
|
||||
body_id TEXT NOT NULL REFERENCES bodies(body_id) ON DELETE CASCADE,
|
||||
local_id TEXT NOT NULL,
|
||||
name TEXT NOT NULL DEFAULT '',
|
||||
center_row INTEGER NOT NULL,
|
||||
@@ -432,6 +435,19 @@ CREATE TABLE IF NOT EXISTS atlas_mountain_ranges (
|
||||
updated_at TEXT DEFAULT (datetime('now'))
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_atlas_cities_body ON atlas_cities(body_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_atlas_cities_kind ON atlas_cities(kind);
|
||||
CREATE INDEX IF NOT EXISTS idx_atlas_cities_population ON atlas_cities(population);
|
||||
CREATE INDEX IF NOT EXISTS idx_atlas_cities_name ON atlas_cities(name);
|
||||
CREATE INDEX IF NOT EXISTS idx_atlas_roads_body ON atlas_roads(body_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_atlas_railroads_body ON atlas_railroads(body_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_atlas_pois_body ON atlas_pois(body_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_atlas_pois_kind ON atlas_pois(kind);
|
||||
CREATE INDEX IF NOT EXISTS idx_atlas_rivers_body ON atlas_rivers(body_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_atlas_oceans_body ON atlas_oceans(body_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_atlas_mountain_ranges_body ON atlas_mountain_ranges(body_id);
|
||||
-- END ATLAS INDEX (D-191 §8, #832)
|
||||
|
||||
-- Indexes
|
||||
-- astronomical_id removed: system_id IS the GJ catalog number
|
||||
CREATE INDEX IF NOT EXISTS idx_star_systems_sector ON star_systems(geographic_sector);
|
||||
@@ -466,15 +482,4 @@ CREATE INDEX IF NOT EXISTS idx_brand_products_origin ON brand_products(origin_sy
|
||||
CREATE INDEX IF NOT EXISTS idx_brand_products_tier ON brand_products(brand_tier);
|
||||
CREATE INDEX IF NOT EXISTS idx_brand_inputs_commodity ON brand_inputs(commodity_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_corp_lifecycle_events_corp ON corp_lifecycle_events(corp_id);
|
||||
-- Atlas index (D-191 §8, #832)
|
||||
CREATE INDEX IF NOT EXISTS idx_atlas_cities_body ON atlas_cities(body_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_atlas_cities_kind ON atlas_cities(kind);
|
||||
CREATE INDEX IF NOT EXISTS idx_atlas_cities_population ON atlas_cities(population);
|
||||
CREATE INDEX IF NOT EXISTS idx_atlas_cities_name ON atlas_cities(name);
|
||||
CREATE INDEX IF NOT EXISTS idx_atlas_roads_body ON atlas_roads(body_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_atlas_railroads_body ON atlas_railroads(body_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_atlas_pois_body ON atlas_pois(body_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_atlas_pois_kind ON atlas_pois(kind);
|
||||
CREATE INDEX IF NOT EXISTS idx_atlas_rivers_body ON atlas_rivers(body_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_atlas_oceans_body ON atlas_oceans(body_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_atlas_mountain_ranges_body ON atlas_mountain_ranges(body_id);
|
||||
-- Atlas index (D-191 §8, #832) — see BEGIN/END ATLAS INDEX block above.
|
||||
|
||||
Reference in New Issue
Block a user