Merge remote-tracking branch 'origin/sprint-35/server'

This commit is contained in:
2026-04-15 09:36:04 +02:00
285 changed files with 28794 additions and 702 deletions
+16 -1
View File
@@ -3,7 +3,7 @@ GODOT := $(shell command -v godot4 2>/dev/null || command -v godot 2>/dev/null)
.PHONY: help setup build check-protocol client server game stop test lint lint-python setup-venv ci ci-client ci-server clean \
decisions-sync decisions-coverage decisions-active decisions-orphan \
db-backup db-install validate-content check-fact-ids setup-hooks \
audit atlas-verify economy-db \
audit atlas-verify economy-db atlas-generate \
pre-pr pre-pr-lint pre-pr-build pre-pr-test pre-pr-validate pre-pr-fixtures \
pre-pr-server pre-pr-client pre-pr-content \
fixtures-client fixtures-gauntlet golden-diff golden-update \
@@ -56,6 +56,7 @@ help:
@echo " make star-map-data Regenerate client/data/star_map_data.json from systems.db + wiki"
@echo " make check-star-map Assert star_map_data.json is up to date (part of pre-pr-client)"
@echo " make economy-db Import economics data into systems.db (TOML/JSON → SQLite)"
@echo " make atlas-generate Generate atlas city/road/rail markers for all inhabited bodies"
@echo " make fixtures-client Generate GDScript->Rust cross-encoder fixtures (#475)"
@echo " make golden-diff Show diff if golden file output has changed"
@echo " make golden-update Regenerate golden file and stage for commit"
@@ -329,6 +330,20 @@ db-install:
economy-db: ## Import economics data (commodities, chains, gate links) into systems.db
@python3 tooling/economy-db/import_economics.py
atlas-generate: ## Generate atlas markers (cities, roads, rail) for all inhabited bodies (#832)
@# Loud guard: generate_atlas.py reads bodies with a non-NULL terrain_reference.
@# If populate_terrain_reference.py has not run on a fresh DB, the generator
@# silently processes zero bodies and exits 0 — fail fast instead.
@count=$$(python3 -c "import sqlite3; c = sqlite3.connect('server/data/systems.db'); print(c.execute('SELECT COUNT(*) FROM bodies WHERE terrain_reference IS NOT NULL').fetchone()[0])"); \
if [ "$$count" = "0" ]; then \
echo "ERROR: no bodies have terrain_reference populated yet."; \
echo "Run: python3 tooling/planet-gen/populate_terrain_reference.py"; \
echo "(This is a prerequisite for atlas-generate — see D-191 §9 pipeline order.)"; \
exit 1; \
fi; \
echo " [guard] $$count bodies with terrain_reference — proceeding."
@python3 tooling/planet-gen/generate_atlas.py --seed 42
econ-sim: ## Build the economics simulation binary (Layer 1+2: Leontief + tâtonnement trade)
@cargo build --manifest-path tooling/econ-sim/Cargo.toml --release
@echo "Built: tooling/econ-sim/target/release/econ-sim"
+16 -2
View File
@@ -710,7 +710,21 @@ Technical foundation decisions that constrain implementation: engine, client-ser
- Maps to D-181 signal visibility ladder
**8. Settlement Data Model**
- markers.json schema per body: `cities` (name, lat/lon, population_tier, primary_function, gate_terminal, continent_id), `roads` (path polylines, connects), `railroads` (path polylines, connects), `pois` (name, kind, position), plus existing rivers/oceans/mountains with names filled
- **Amendment (2026-04-15):** The original §8 (below) described marker positions as lat/lon objects and city records keyed by `population_tier`/`primary_function`/`gate_terminal`/`continent_id`. That shape was aspirational — neither the generator nor the hand-authored templates ever emitted it. Both ended up writing pixel-space row/col arrays against a `512 × 256` storage grid, and PR #129 canonizes that shape so the code and the decision stop drifting. The original prose is preserved immediately below; the current shape follows.
- **Original (2026-04-10, superseded):** markers.json schema per body: `cities` (name, lat/lon, population_tier, primary_function, gate_terminal, continent_id), `roads` (path polylines, connects), `railroads` (path polylines, connects), `pois` (name, kind, position), plus existing rivers/oceans/mountains with names filled. Population tier → city count: `floor(log10(pop/1M))`, modified by `settlement_pattern`. Gate terminal POI at largest population center, sometimes scattered to a smaller one. Moons: same depth as planets, scale with population.
- **Current canonical format:** markers.json is stored in heightmap pixel space. Every marker file declares a `grid: { w, h }` header — the generator, the 6 hand-authored templates (Lendel, Edict, Vuurkloof, Røros, Cairnside, Estrade), and all 2394 procedural seed files ship `{"w": 512, "h": 256}`. Every position is a two-element **array** `[row, col]` of integer pixels into that grid, where `row ∈ [0, h)` and `col ∈ [0, w)` (row is the first axis to match NumPy convention and the flood-fill / A* / cost-grid code that `tooling/planet-gen/` already runs in). Polyline geometry (`roads[*].path`, `railroads[*].path`, `rivers[*].path`) is `[[row, col], [row, col], ...]`.
- **markers.json top-level schema:**
- `grid`: `{"w": 512, "h": 256}`
- `cities[]`: `{id, name, kind, center: [row, col], population}` — `kind` is `capital` or `city`; `name` is empty when awaiting gemma_naming.py (#833).
- `roads[]`: `{id, name, kind, path: [[row, col], …]}` — `kind` is `commercial` by default for generated roads; hand-authored roads use `highway`, `rural`, etc.
- `railroads[]`: same shape as `roads[]`; generated default `kind` is `passenger_freight`.
- `pois[]`: `{id, name, kind, center: [row, col]}` — generated POIs are `kind: "transit"`; hand-authored POIs use `institutional`, `cultural`, `corporate`, etc.
- `rivers[]`: `{id, name, path: [[row, col], …]}`
- `oceans[]`: `{id, name, kind, center: [row, col], area_fraction}` where `kind` is `lake` | `sea` | `ocean`.
- `mountain_ranges[]`: `{id, name, center: [row, col], peak: [row, col], area_cells}`
- Pixel space is what the heightmap analysis (flood-fill, A* cost grid, city placement) natively operates on; it is deterministic and avoids double-conversion through a projection.
- Lat/lon strings are a **display-time derivation**, not a storage format. The atlas UI converts `[row, col]` + `grid: {w, h}` into an equirectangular `lat°N/S, lon°E/W` string for the city data panel and hover tooltips, using the body's radius for any great-circle distances it needs. This keeps the immersive surface without paying conversion cost in the generator, the DB, or the diff churn on hand-authored files.
- Atlas DB index (`atlas_cities`, `atlas_roads`, `atlas_railroads`, `atlas_pois`, `atlas_rivers`, `atlas_oceans`, `atlas_mountain_ranges`, `atlas_body_grids`) mirrors these scalar fields row-by-row for implant-app and development queries; polyline geometry stays in the JSON files next to the heightmaps.
- Population tier → city count: `floor(log10(pop/1M))`, modified by `settlement_pattern`
- Gate terminal POI: at largest population center, sometimes scattered to a smaller one
- Moons: same depth as planets, scale with population
@@ -737,4 +751,4 @@ Technical foundation decisions that constrain implementation: engine, client-ser
---
*53 decisions. Last updated: 2026-04-10 (D-191 Atlas of the Reach — Phase 3 scope and pipeline)*
*53 decisions. Last updated: 2026-04-15 (D-191 §8 amendment — markers.json canonical format is pixel space `[row, col]` arrays against a `512 × 256` grid, following the D-094 amendment pattern; lat/lon is a display-time derivation)*
+180 -1
View File
@@ -167,7 +167,16 @@ CREATE TABLE IF NOT EXISTS bodies (
industrial_corridor TEXT, -- MVG, Gate_Corp, DSMC, Prometheus, Agricultural_Syndic
-- Rendering
terrain_reference TEXT, -- heightmap path when authored, NULL otherwise
-- terrain_reference: repo-root-relative path to the body's heightmap PNG.
-- Convention (enforced by populate_terrain_reference.py and assumed by
-- generate_atlas.py and the Godot client's atlas scene loader):
-- wiki/star-systems/<system_slug>/bodies/<body_id>/heightmap.png
-- where <system_slug> = system_id with spaces replaced by hyphens
-- (e.g. "GJ 244A" → "GJ-244A"). NULL means no heightmap has been
-- generated for this body yet. The three downstream pipelines
-- (populate, atlas generator, client loader) all assume this format —
-- changing it requires updating all three sites together.
terrain_reference TEXT,
screenshot_path TEXT, -- planetary shader screenshot path
updated_at TEXT DEFAULT (datetime('now'))
@@ -276,6 +285,169 @@ CREATE TABLE IF NOT EXISTS corp_presence (
PRIMARY KEY (corp_id, location_id)
);
-- Brand layer (D-189) — administered-price layer above commodity tâtonnement.
-- Brands consume commodities as demand nodes; they are NOT commodities (D-185).
CREATE TABLE IF NOT EXISTS brand_products (
brand_product_id TEXT PRIMARY KEY,
corp_id TEXT NOT NULL REFERENCES corporations(corp_id),
product_name TEXT NOT NULL,
brand_category TEXT NOT NULL, -- terroir|heritage_craft|tech_premium|cultural|service_premium|commodity_branded|design_heritage|platform_catalogue
value_trajectory TEXT NOT NULL, -- appreciating|depreciating|timeless
scarcity_class TEXT NOT NULL, -- capped|constrained|scalable|unlimited
product_subcategory TEXT,
base_premium_multiplier REAL NOT NULL DEFAULT 1.0,
premium_floor REAL NOT NULL DEFAULT 0.0,
origin_system TEXT REFERENCES star_systems(system_id),
terroir_locked INTEGER NOT NULL DEFAULT 0, -- boolean: production bound to origin_system
currency_denomination TEXT NOT NULL DEFAULT 'tractus', -- tractus|mark|mixed|sol_adjacent
shadow_viable INTEGER NOT NULL DEFAULT 0, -- boolean: circulates in shadow economy
brand_tier TEXT NOT NULL, -- halo|volume
halo_brand_id TEXT REFERENCES brand_products(brand_product_id), -- NULL for halo tier
updated_at TEXT DEFAULT (datetime('now'))
);
CREATE TABLE IF NOT EXISTS brand_inputs (
brand_product_id TEXT NOT NULL REFERENCES brand_products(brand_product_id),
commodity_id TEXT NOT NULL REFERENCES commodities(commodity_id),
quantity REAL NOT NULL,
PRIMARY KEY (brand_product_id, commodity_id)
);
-- Fiscal parameters per star system (D-189 section 5 + section 6)
CREATE TABLE IF NOT EXISTS system_fiscal (
system_id TEXT PRIMARY KEY REFERENCES star_systems(system_id),
corp_tax_rate REAL NOT NULL DEFAULT 0.22, -- 0.01.0
collection_efficiency REAL NOT NULL DEFAULT 1.0, -- derived: 1.0 - shadow_intensity × 0.6
updated_at TEXT DEFAULT (datetime('now'))
);
-- Phase 2: passive corp health tracking (D-189 section 8)
CREATE TABLE IF NOT EXISTS corp_financial_state (
corp_id TEXT PRIMARY KEY REFERENCES corporations(corp_id),
health_metric REAL NOT NULL DEFAULT 1.0, -- 0.0 (distressed) to 1.0 (healthy)
updated_at TEXT DEFAULT (datetime('now'))
);
-- Phase 3 lifecycle state machine stub (D-189 section 8) — schema correct, not driven yet
CREATE TABLE IF NOT EXISTS corp_lifecycle_events (
event_id INTEGER PRIMARY KEY AUTOINCREMENT,
corp_id TEXT NOT NULL REFERENCES corporations(corp_id),
event_type TEXT NOT NULL, -- Founded|Growing|Active|Distressed|Acquired|Dissolved
event_tick INTEGER NOT NULL DEFAULT 0,
event_data TEXT, -- JSON blob for future lifecycle detail
created_at TEXT DEFAULT (datetime('now'))
);
-- 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, 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) ON DELETE CASCADE,
grid_w INTEGER NOT NULL,
grid_h INTEGER NOT NULL,
updated_at TEXT DEFAULT (datetime('now'))
);
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) 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
center_row INTEGER NOT NULL,
center_col INTEGER NOT NULL,
population INTEGER NOT NULL DEFAULT 0,
updated_at TEXT DEFAULT (datetime('now'))
);
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) ON DELETE CASCADE,
local_id TEXT NOT NULL,
name TEXT NOT NULL DEFAULT '',
kind TEXT NOT NULL, -- commercial|highway|rural|...
point_count INTEGER NOT NULL, -- path length proxy (full geometry in markers.json)
updated_at TEXT DEFAULT (datetime('now'))
);
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) ON DELETE CASCADE,
local_id TEXT NOT NULL,
name TEXT NOT NULL DEFAULT '',
kind TEXT NOT NULL, -- passenger_freight|freight|maglev|...
point_count INTEGER NOT NULL,
updated_at TEXT DEFAULT (datetime('now'))
);
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) ON DELETE CASCADE,
local_id TEXT NOT NULL,
name TEXT NOT NULL DEFAULT '',
kind TEXT NOT NULL, -- transit|institutional|cultural|...
center_row INTEGER NOT NULL,
center_col INTEGER NOT NULL,
updated_at TEXT DEFAULT (datetime('now'))
);
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) ON DELETE CASCADE,
local_id TEXT NOT NULL,
name TEXT NOT NULL DEFAULT '', -- empty for untitled procedural rivers
point_count INTEGER NOT NULL,
updated_at TEXT DEFAULT (datetime('now'))
);
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) ON DELETE CASCADE,
local_id TEXT NOT NULL,
name TEXT NOT NULL DEFAULT '',
kind TEXT NOT NULL, -- lake|sea|ocean
center_row INTEGER NOT NULL,
center_col INTEGER NOT NULL,
area_fraction REAL NOT NULL DEFAULT 0.0, -- fraction of heightmap area covered
updated_at TEXT DEFAULT (datetime('now'))
);
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) ON DELETE CASCADE,
local_id TEXT NOT NULL,
name TEXT NOT NULL DEFAULT '',
center_row INTEGER NOT NULL,
center_col INTEGER NOT NULL,
peak_row INTEGER NOT NULL,
peak_col INTEGER NOT NULL,
area_cells INTEGER NOT NULL DEFAULT 0,
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);
@@ -304,3 +476,10 @@ CREATE INDEX IF NOT EXISTS idx_production_chains_output ON production_chains(out
CREATE INDEX IF NOT EXISTS idx_chain_inputs_commodity ON chain_inputs(input_commodity_id);
CREATE INDEX IF NOT EXISTS idx_corp_presence_corp ON corp_presence(corp_id);
CREATE INDEX IF NOT EXISTS idx_corp_presence_location ON corp_presence(location_id);
-- Brand layer indexes (D-189 section 5 — composite for UI queries)
CREATE INDEX IF NOT EXISTS idx_brand_products_corp_category ON brand_products(corp_id, brand_category);
CREATE INDEX IF NOT EXISTS idx_brand_products_origin ON brand_products(origin_system);
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) — see BEGIN/END ATLAS INDEX block above.
Binary file not shown.
+14 -3
View File
@@ -147,7 +147,7 @@ fn main() {
let mut app = App::new();
settled_reach_server::tick_phases::TickPhase::configure(&mut app);
app.add_plugins(SimulationPlugin);
app.add_plugins(SimulationPlugin { seed });
app.add_plugins(BridgePlugin);
app.add_plugins(settled_reach_server::knowledge::KnowledgePlugin);
app.add_plugins(settled_reach_server::npc::NpcPlugin);
@@ -176,7 +176,18 @@ fn main() {
app.insert_resource(BridgeResource::new(bridge));
app.insert_resource(HandshakeState::Complete);
// Override SimRng with the chosen seed (SimulationPlugin defaults to seed 0)
// SimulationPlugin { seed } already inserts SimRng with the correct seed
// during plugin build. We re-insert here as a defensive override for one
// specific ordering risk: any future plugin that registers *before*
// SimulationPlugin in `App::add_plugins` order (e.g. a pre-simulation
// observability plugin) and consumes SimRng at plugin build time would
// see a stale resource that was never seeded from StartupMessage. This
// `insert_resource` call happens AFTER all plugins have built, so it
// always overwrites whatever SimRng is currently in the world with the
// authoritative value from the StartupMessage. If you remove this line,
// also audit every `app.add_plugins(...)` call in this file and in
// `SimulationPlugin::build` for plugins that touch SimRng, and verify
// none of them run before SimulationPlugin's seeding logic. #826 thread.
app.insert_resource(settled_reach_server::simulation::rng::SimRng::new(seed));
// Initialize empty line pool index (populated by generator pipeline in v0.2).
@@ -359,7 +370,7 @@ fn dump_schedule_graph() {
let mut app = App::new();
settled_reach_server::tick_phases::TickPhase::configure(&mut app);
app.add_plugins(SimulationPlugin);
app.add_plugins(SimulationPlugin { seed: 0 });
app.add_plugins(BridgePlugin);
app.add_plugins(settled_reach_server::knowledge::KnowledgePlugin);
app.add_plugins(settled_reach_server::npc::NpcPlugin);
+2 -1
View File
@@ -358,9 +358,10 @@ pub fn try_load_economy(run_seed: u64) -> Option<(EconSimResource, EconStateReso
match Simulation::load_auto(run_seed) {
Ok(sim) => {
tracing::info!(
econ_seed = run_seed,
commodities = sim.economy().commodities.len(),
active_nodes = sim.nodes.len(),
"Economy simulation loaded"
"Economy simulation loaded with seed"
);
Some((EconSimResource::new(sim), EconStateResource::default()))
}
+7 -3
View File
@@ -8,13 +8,17 @@ use bevy_ecs::schedule::IntoScheduleConfigs;
use crate::tick_phases::TickPhase;
pub struct EconomyPlugin;
pub struct EconomyPlugin {
/// World seed from `StartupMessage` (#826). Passed to `try_load_economy` so the
/// tâtonnement simulation is seeded deterministically from the client's world seed.
pub seed: u64,
}
impl Plugin for EconomyPlugin {
fn build(&self, app: &mut App) {
// Economy simulation (#821, D-031) — loaded once at startup, no-op when DB absent.
// Uses seed 0 for now; will be threaded through StartupMessage world seed (#826).
if let Some((econ_sim, econ_state)) = super::economy::try_load_economy(0) {
// Seed comes from StartupMessage.world_seed, threaded via SimulationPlugin (#826).
if let Some((econ_sim, econ_state)) = super::economy::try_load_economy(self.seed) {
app.insert_resource(econ_sim).insert_resource(econ_state);
}
// EconQueryBuffer: always registered so EconStateQuery PlayerActions are accepted
+8 -3
View File
@@ -50,7 +50,12 @@ pub mod zone;
/// Each sub-plugin owns its domain's systems, resources, and phase assignment.
/// No system registration happens here — only sub-plugin composition and
/// shared resources needed by multiple sub-plugins.
pub struct SimulationPlugin;
///
/// `seed` is threaded through to [`economy_plugin::EconomyPlugin`] so the
/// economy simulation uses the world seed from `StartupMessage` (#826).
pub struct SimulationPlugin {
pub seed: u64,
}
impl Plugin for SimulationPlugin {
fn build(&self, app: &mut App) {
@@ -59,7 +64,7 @@ impl Plugin for SimulationPlugin {
// Shared resources needed by multiple sub-plugins.
// Each sub-plugin inits its own domain-specific resources.
app.insert_resource(rng::SimRng::new(0))
app.insert_resource(rng::SimRng::new(self.seed))
.init_resource::<save_io::SaveLoadPending>()
.init_resource::<crate::knowledge::EntityRegistry>()
// Triangle escalation event queue (#250) — consumed by storyteller + npc plugins
@@ -81,7 +86,7 @@ impl Plugin for SimulationPlugin {
app.add_plugins(input_plugin::InputPlugin);
app.add_plugins(movement_plugin::MovementPlugin);
app.add_plugins(social_plugin::SocialPlugin);
app.add_plugins(economy_plugin::EconomyPlugin);
app.add_plugins(economy_plugin::EconomyPlugin { seed: self.seed });
// Background worker tick integration (#843 Part C)
app.add_systems(
+5 -5
View File
@@ -695,7 +695,7 @@ mod tests {
#[test]
fn gauntlet_setup_creates_expected_entities() {
let mut app = App::new();
app.add_plugins(crate::simulation::SimulationPlugin);
app.add_plugins(crate::simulation::SimulationPlugin { seed: 0 });
app.add_plugins(crate::knowledge::KnowledgePlugin);
app.add_plugins(crate::npc::NpcPlugin);
@@ -718,7 +718,7 @@ mod tests {
#[test]
fn hub_center_is_walkable() {
let mut app = App::new();
app.add_plugins(crate::simulation::SimulationPlugin);
app.add_plugins(crate::simulation::SimulationPlugin { seed: 0 });
app.add_plugins(crate::knowledge::KnowledgePlugin);
app.add_plugins(crate::npc::NpcPlugin);
@@ -735,7 +735,7 @@ mod tests {
#[test]
fn occlusion_north_wall_blocks() {
let mut app = App::new();
app.add_plugins(crate::simulation::SimulationPlugin);
app.add_plugins(crate::simulation::SimulationPlugin { seed: 0 });
app.add_plugins(crate::knowledge::KnowledgePlugin);
app.add_plugins(crate::npc::NpcPlugin);
@@ -754,7 +754,7 @@ mod tests {
#[test]
fn corridor_connects_hub_to_occlusion() {
let mut app = App::new();
app.add_plugins(crate::simulation::SimulationPlugin);
app.add_plugins(crate::simulation::SimulationPlugin { seed: 0 });
app.add_plugins(crate::knowledge::KnowledgePlugin);
app.add_plugins(crate::npc::NpcPlugin);
@@ -771,7 +771,7 @@ mod tests {
#[test]
fn stable_id_ranges_match_spec() {
let mut app = App::new();
app.add_plugins(crate::simulation::SimulationPlugin);
app.add_plugins(crate::simulation::SimulationPlugin { seed: 0 });
app.add_plugins(crate::knowledge::KnowledgePlugin);
app.add_plugins(crate::npc::NpcPlugin);
+1 -1
View File
@@ -78,7 +78,7 @@ mod gauntlet_integration {
/// Build a minimal Gauntlet app with the given archetype and run one tick.
fn boot_gauntlet(archetype: CharacterArchetype) -> App {
let mut app = App::new();
app.add_plugins(SimulationPlugin);
app.add_plugins(SimulationPlugin { seed: 0 });
test_world::setup_gauntlet(&mut app, archetype);
app.update();
app
+1 -1
View File
@@ -221,7 +221,7 @@ fn t3_pause_mid_corridor_discards_movement_and_resumes() {
use settled_reach_server::simulation::SimulationPlugin;
let mut app = App::new();
app.add_plugins(SimulationPlugin);
app.add_plugins(SimulationPlugin { seed: 0 });
// 200×200 walkability map covers the full gauntlet coordinate space.
app.insert_resource(WalkabilityMap::new(200, 200, 1));
+1 -1
View File
@@ -38,7 +38,7 @@ use settled_reach_server::simulation::SimulationPlugin;
/// Snapshots are written to SnapshotBuffer for direct inspection.
fn build_deterministic_app(seed: u64) -> App {
let mut app = App::new();
app.add_plugins(SimulationPlugin);
app.add_plugins(SimulationPlugin { seed });
app.add_plugins(BridgePlugin);
app.add_plugins(KnowledgePlugin);
app.add_plugins(NpcPlugin);
+2 -2
View File
@@ -46,7 +46,7 @@ fn malformed_input_produces_sim_error_and_server_continues() {
let bridge = TcpBridge::accept_on(listener).expect("accept connection");
let mut app = App::new();
app.add_plugins(SimulationPlugin);
app.add_plugins(SimulationPlugin { seed: 0 });
app.add_plugins(BridgePlugin);
app.add_plugins(KnowledgePlugin);
app.init_resource::<TrustEventQueue>();
@@ -177,7 +177,7 @@ fn state_hash_populated_in_snapshot() {
let bridge = TcpBridge::accept_on(listener).expect("accept connection");
let mut app = App::new();
app.add_plugins(SimulationPlugin);
app.add_plugins(SimulationPlugin { seed: 0 });
app.add_plugins(BridgePlugin);
app.add_plugins(KnowledgePlugin);
app.init_resource::<TrustEventQueue>();
+1 -1
View File
@@ -28,7 +28,7 @@ fn player_moves_north_through_full_pipeline() {
// Build app
let mut app = App::new();
app.add_plugins(SimulationPlugin);
app.add_plugins(SimulationPlugin { seed: 0 });
app.add_plugins(BridgePlugin);
app.add_plugins(KnowledgePlugin);
app.init_resource::<TrustEventQueue>();
+1 -1
View File
@@ -39,7 +39,7 @@ const FIXTURE_DIR: &str = "../tests/fixtures/gauntlet";
/// Identical to what the server runs in --test-mode.
fn build_gauntlet(seed: u64) -> App {
let mut app = App::new();
app.add_plugins(SimulationPlugin);
app.add_plugins(SimulationPlugin { seed: 0 });
app.add_plugins(BridgePlugin);
app.add_plugins(KnowledgePlugin);
app.add_plugins(NpcPlugin);
+1 -1
View File
@@ -46,7 +46,7 @@ const NUM_TICKS: usize = 10;
/// Mirrors the setup in determinism.rs / main.rs.
fn build_app(seed: u64) -> App {
let mut app = App::new();
app.add_plugins(SimulationPlugin);
app.add_plugins(SimulationPlugin { seed: 0 });
app.add_plugins(BridgePlugin);
app.add_plugins(KnowledgePlugin);
app.add_plugins(NpcPlugin);
+2 -2
View File
@@ -6,7 +6,7 @@ use settled_reach_server::simulation::SimulationPlugin;
#[test]
fn movement_validated_within_app() {
let mut app = App::new();
app.add_plugins(SimulationPlugin);
app.add_plugins(SimulationPlugin { seed: 0 });
// Insert a walkability map with one blocked tile
let mut map = WalkabilityMap::new(10, 10, 1);
@@ -56,7 +56,7 @@ fn movement_validated_within_app() {
#[test]
fn entity_collision_blocks_movement() {
let mut app = App::new();
app.add_plugins(SimulationPlugin);
app.add_plugins(SimulationPlugin { seed: 0 });
app.insert_resource(WalkabilityMap::new(10, 10, 1));
// Stationary entity at (5,4)
+1 -1
View File
@@ -66,7 +66,7 @@ fn perf_tick_timing() {
let bridge = TcpBridge::accept_on(listener).expect("accept connection");
let mut app = App::new();
app.add_plugins(SimulationPlugin);
app.add_plugins(SimulationPlugin { seed: 0 });
app.add_plugins(BridgePlugin);
app.add_plugins(settled_reach_server::knowledge::KnowledgePlugin);
app.add_plugins(settled_reach_server::npc::NpcPlugin);
+1 -1
View File
@@ -7,7 +7,7 @@ use settled_reach_server::simulation::SimulationPlugin;
#[test]
fn world_boots_and_ticks() {
let mut app = App::new();
app.add_plugins(SimulationPlugin);
app.add_plugins(SimulationPlugin { seed: 0 });
// Verify initial state
let time = app.world().resource::<SimulationTime>();
+1 -1
View File
@@ -112,7 +112,7 @@ fn build_storyteller_app() -> App {
bridge::types::CharacterArchetype, simulation::SimulationPlugin, test_world,
};
let mut app = App::new();
app.add_plugins(SimulationPlugin);
app.add_plugins(SimulationPlugin { seed: 0 });
test_world::setup_gauntlet(&mut app, CharacterArchetype::default());
app
}
+408 -76
View File
@@ -39,6 +39,13 @@ COMMODITIES_TOML = REPO_ROOT / "wiki" / "economics" / "commodities.toml"
CHAINS_TOML = REPO_ROOT / "wiki" / "economics" / "production_chains.toml"
SCHEMA_SQL = REPO_ROOT / "server" / "data" / "systems-schema.sql"
CORPORATIONS_DIR = REPO_ROOT / "wiki" / "corporations"
BRANDS_TOML = REPO_ROOT / "wiki" / "economics" / "corporations" / "brands.toml"
class _ImportAborted(Exception):
"""Raised internally by main() when a validation step wants a clean
rollback + exit 1. Caught only by main(); error messages are printed
before raising so the user sees them."""
# ---------------------------------------------------------------------------
@@ -48,6 +55,61 @@ CORPORATIONS_DIR = REPO_ROOT / "wiki" / "corporations"
MIGRATION_SQL = """
-- Economics tables (idempotent — safe to re-run)
-- Brand layer tables (D-189, #827)
CREATE TABLE IF NOT EXISTS brand_products (
brand_product_id TEXT PRIMARY KEY,
corp_id TEXT NOT NULL REFERENCES corporations(corp_id),
product_name TEXT NOT NULL,
brand_category TEXT NOT NULL,
value_trajectory TEXT NOT NULL,
scarcity_class TEXT NOT NULL,
product_subcategory TEXT,
base_premium_multiplier REAL NOT NULL DEFAULT 1.0,
premium_floor REAL NOT NULL DEFAULT 0.0,
origin_system TEXT REFERENCES star_systems(system_id),
terroir_locked INTEGER NOT NULL DEFAULT 0,
currency_denomination TEXT NOT NULL DEFAULT 'tractus',
shadow_viable INTEGER NOT NULL DEFAULT 0,
brand_tier TEXT NOT NULL,
halo_brand_id TEXT REFERENCES brand_products(brand_product_id),
updated_at TEXT DEFAULT (datetime('now'))
);
CREATE TABLE IF NOT EXISTS brand_inputs (
brand_product_id TEXT NOT NULL REFERENCES brand_products(brand_product_id),
commodity_id TEXT NOT NULL REFERENCES commodities(commodity_id),
quantity REAL NOT NULL,
PRIMARY KEY (brand_product_id, commodity_id)
);
CREATE TABLE IF NOT EXISTS system_fiscal (
system_id TEXT PRIMARY KEY REFERENCES star_systems(system_id),
corp_tax_rate REAL NOT NULL DEFAULT 0.22,
collection_efficiency REAL NOT NULL DEFAULT 1.0,
updated_at TEXT DEFAULT (datetime('now'))
);
CREATE TABLE IF NOT EXISTS corp_financial_state (
corp_id TEXT PRIMARY KEY REFERENCES corporations(corp_id),
health_metric REAL NOT NULL DEFAULT 1.0,
updated_at TEXT DEFAULT (datetime('now'))
);
CREATE TABLE IF NOT EXISTS corp_lifecycle_events (
event_id INTEGER PRIMARY KEY AUTOINCREMENT,
corp_id TEXT NOT NULL REFERENCES corporations(corp_id),
event_type TEXT NOT NULL,
event_tick INTEGER NOT NULL DEFAULT 0,
event_data TEXT,
created_at TEXT DEFAULT (datetime('now'))
);
CREATE INDEX IF NOT EXISTS idx_brand_products_corp_category ON brand_products(corp_id, brand_category);
CREATE INDEX IF NOT EXISTS idx_brand_products_origin ON brand_products(origin_system);
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);
CREATE TABLE IF NOT EXISTS gate_links (
from_system_id TEXT NOT NULL REFERENCES star_systems(system_id),
to_system_id TEXT NOT NULL REFERENCES star_systems(system_id),
@@ -669,6 +731,237 @@ def _validate_system_coverage(
]
# ---------------------------------------------------------------------------
# Brand layer import (D-189, #827)
# ---------------------------------------------------------------------------
VALID_BRAND_CATEGORIES = {
"terroir", "heritage_craft", "tech_premium", "cultural",
"service_premium", "commodity_branded", "design_heritage", "platform_catalogue",
}
VALID_VALUE_TRAJECTORIES = {"appreciating", "depreciating", "timeless"}
VALID_SCARCITY_CLASSES = {"capped", "constrained", "scalable", "unlimited"}
VALID_BRAND_TIERS = {"halo", "volume"}
VALID_CURRENCY_DENOMINATIONS = {"tractus", "mark", "mixed", "sol_adjacent"}
def import_brands(
conn: sqlite3.Connection, dry_run: bool
) -> tuple[int, int]:
"""Import brand_products and brand_inputs from wiki/economics/corporations/brands.toml.
Returns (n_products, n_inputs).
"""
if not BRANDS_TOML.exists():
print(" warning: brands.toml not found — brand layer skipped")
return 0, 0
with open(BRANDS_TOML, "rb") as f:
data = tomllib.load(f)
products = data.get("brand_products", [])
inputs = data.get("brand_inputs", [])
product_rows = []
for p in products:
product_rows.append((
p["brand_product_id"],
p["corp_id"],
p["product_name"],
p["brand_category"],
p["value_trajectory"],
p["scarcity_class"],
p.get("product_subcategory"),
p.get("base_premium_multiplier", 1.0),
p.get("premium_floor", 0.0),
p.get("origin_system"),
int(p.get("terroir_locked", False)),
p.get("currency_denomination", "tractus"),
int(p.get("shadow_viable", False)),
p["brand_tier"],
p.get("halo_brand_id"),
))
input_rows = []
for inp in inputs:
input_rows.append((
inp["brand_product_id"],
inp["commodity_id"],
inp["quantity"],
))
if not dry_run:
conn.executemany(
"""INSERT OR REPLACE INTO brand_products (
brand_product_id, corp_id, product_name, brand_category,
value_trajectory, scarcity_class, product_subcategory,
base_premium_multiplier, premium_floor, origin_system,
terroir_locked, currency_denomination, shadow_viable,
brand_tier, halo_brand_id
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)""",
product_rows,
)
conn.executemany(
"""INSERT OR REPLACE INTO brand_inputs
(brand_product_id, commodity_id, quantity) VALUES (?, ?, ?)""",
input_rows,
)
return len(product_rows), len(input_rows)
def import_system_fiscal(conn: sqlite3.Connection, dry_run: bool) -> int:
"""Populate system_fiscal with hardcoded Phase 2 values.
Phase 2 values (NOT derived from D-189 §6 yet):
- corp_tax_rate = 0.22 (flat default)
- collection_efficiency = 0.85 (mid-reach average placeholder)
The D-189 §6 formula `collection_efficiency = 1.0 - shadow_economy_intensity × 0.6`
is deliberately NOT implemented here — `shadow_economy_intensity` is not
yet per-system in the DB (pending the shadow_economy.toml pipeline). When
that pipeline lands, replace the hardcoded 0.85 with the derivation and
wire `shadow_economy_intensity` through the SELECT. Tracked as a Phase 3
follow-up.
"""
inhabited = conn.execute("""
SELECT ss.system_id, COALESCE(se.population, 0)
FROM star_systems ss
LEFT JOIN system_economy se ON ss.system_id = se.system_id
WHERE ss.inhabited_planet_count > 0 OR se.population > 0
ORDER BY ss.system_id
""").fetchall()
PHASE2_CORP_TAX_RATE = 0.22
PHASE2_COLLECTION_EFFICIENCY = 0.85
rows = [
(system_id, PHASE2_CORP_TAX_RATE, PHASE2_COLLECTION_EFFICIENCY)
for system_id, _pop in inhabited
]
if not dry_run:
conn.executemany(
"""INSERT OR IGNORE INTO system_fiscal
(system_id, corp_tax_rate, collection_efficiency) VALUES (?, ?, ?)""",
rows,
)
return len(rows)
def validate_brands(conn: sqlite3.Connection) -> list[str]:
"""Brand layer structural validation rules V-B01 through V-B06.
V-B01: Every brand_products row has a valid corp_id (FK to corporations).
V-B02: Every brand_inputs row has valid brand_product_id and commodity_id FKs.
V-B03: Every halo brand has at least one brand_inputs entry (demand stub must consume).
V-B04: Every volume tier must reference an existing halo brand_product_id.
V-B05: No brand_product_id is used as halo_brand_id by a non-volume-tier product.
V-B06: Every enum column (brand_category, value_trajectory, scarcity_class,
brand_tier, currency_denomination) is a member of its VALID_* set.
"""
errors: list[str] = []
# V-B01: brand_products → corporations FK
orphan_corps = conn.execute("""
SELECT bp.brand_product_id, bp.corp_id
FROM brand_products bp
LEFT JOIN corporations c ON bp.corp_id = c.corp_id
WHERE c.corp_id IS NULL
""").fetchall()
for pid, corp_id in orphan_corps:
errors.append(
f"V-B01: brand_product '{pid}' references unknown corp_id '{corp_id}'"
)
# V-B02: brand_inputs → brand_products and brand_inputs → commodities FKs
orphan_inputs_bp = conn.execute("""
SELECT bi.brand_product_id, bi.commodity_id
FROM brand_inputs bi
LEFT JOIN brand_products bp ON bi.brand_product_id = bp.brand_product_id
WHERE bp.brand_product_id IS NULL
""").fetchall()
for pid, cid in orphan_inputs_bp:
errors.append(
f"V-B02: brand_inputs row ({pid}, {cid}) references unknown brand_product_id"
)
orphan_inputs_comm = conn.execute("""
SELECT bi.brand_product_id, bi.commodity_id
FROM brand_inputs bi
LEFT JOIN commodities c ON bi.commodity_id = c.commodity_id
WHERE c.commodity_id IS NULL
""").fetchall()
for pid, cid in orphan_inputs_comm:
errors.append(
f"V-B02: brand_inputs row ({pid}, {cid}) references unknown commodity_id '{cid}'"
)
# V-B03: every halo brand has at least one brand_inputs entry
halo_no_inputs = conn.execute("""
SELECT bp.brand_product_id
FROM brand_products bp
WHERE bp.brand_tier = 'halo'
AND bp.brand_product_id NOT IN (SELECT brand_product_id FROM brand_inputs)
""").fetchall()
for (pid,) in halo_no_inputs:
errors.append(
f"V-B03: halo brand '{pid}' has no brand_inputs entries "
f"(must consume at least one commodity as a demand node)"
)
# V-B04: volume tiers reference valid halo_brand_id
volume_bad_halo = conn.execute("""
SELECT bp.brand_product_id, bp.halo_brand_id
FROM brand_products bp
WHERE bp.brand_tier = 'volume'
AND (bp.halo_brand_id IS NULL
OR bp.halo_brand_id NOT IN (SELECT brand_product_id FROM brand_products))
""").fetchall()
for pid, halo_id in volume_bad_halo:
errors.append(
f"V-B04: volume brand '{pid}' has invalid halo_brand_id '{halo_id}'"
)
# V-B05: halo_brand_id must only point to halo-tier products
halo_points_to_non_halo = conn.execute("""
SELECT child.brand_product_id, child.halo_brand_id, parent.brand_tier
FROM brand_products child
JOIN brand_products parent ON child.halo_brand_id = parent.brand_product_id
WHERE child.brand_tier = 'volume'
AND parent.brand_tier != 'halo'
""").fetchall()
for child_id, halo_id, parent_tier in halo_points_to_non_halo:
errors.append(
f"V-B05: volume brand '{child_id}' points to '{halo_id}' "
f"which has brand_tier='{parent_tier}', not 'halo'"
)
# V-B06: every enum column is in its VALID_* set. The SQL columns are
# plain TEXT without CHECK constraints, so a typo like `terrior` would
# otherwise silently import.
enum_checks = [
("brand_category", VALID_BRAND_CATEGORIES),
("value_trajectory", VALID_VALUE_TRAJECTORIES),
("scarcity_class", VALID_SCARCITY_CLASSES),
("brand_tier", VALID_BRAND_TIERS),
("currency_denomination", VALID_CURRENCY_DENOMINATIONS),
]
for column, valid_set in enum_checks:
bad = conn.execute(
f"SELECT brand_product_id, {column} FROM brand_products"
).fetchall()
for pid, value in bad:
if value not in valid_set:
errors.append(
f"V-B06: brand_product '{pid}' has {column}='{value}'"
f"must be one of {sorted(valid_set)}"
)
return errors
# ---------------------------------------------------------------------------
# Main
# ---------------------------------------------------------------------------
@@ -698,91 +991,128 @@ def main():
conn = sqlite3.connect(str(db_path))
conn.execute("PRAGMA foreign_keys=ON")
# 1. Migrate schema
print(" [1/8] Schema migration...")
for table, col, col_type in COLUMN_MIGRATIONS:
_add_column(conn, table, col, col_type)
conn.executescript(MIGRATION_SQL)
print(" tables and columns ready")
# The clear-then-reimport cycle below runs as a single explicit
# transaction. Any crash, validation error, or KeyboardInterrupt
# between the first DELETE and the final commit rolls everything
# back — the DB never ends up half-cleared with stale rows in some
# tables and empty rows in others. On success we commit exactly
# once, immediately after structural validation passes.
conn.execute("BEGIN")
try:
# 1. Migrate schema (idempotent, inside the tx so a crash here
# leaves no half-applied ALTER TABLE.)
print(" [1/10] Schema migration...")
for table, col, col_type in COLUMN_MIGRATIONS:
_add_column(conn, table, col, col_type)
conn.executescript(MIGRATION_SQL)
print(" tables and columns ready")
# Clear economics tables in FK-safe order (children before parents)
# corp_presence cleared here; corporations table is append-only (never cleared)
if not args.dry_run:
conn.execute("DELETE FROM corp_presence")
conn.execute("DELETE FROM chain_inputs")
conn.execute("DELETE FROM production_chains")
conn.execute("DELETE FROM commodities")
conn.execute("DELETE FROM gate_links")
# Clear economics tables in FK-safe order (children before parents)
# corp_presence cleared here; corporations table is append-only
# (never cleared).
if not args.dry_run:
conn.execute("DELETE FROM corp_presence")
conn.execute("DELETE FROM brand_inputs")
conn.execute("DELETE FROM brand_products")
conn.execute("DELETE FROM system_fiscal")
conn.execute("DELETE FROM corp_financial_state")
conn.execute("DELETE FROM corp_lifecycle_events")
conn.execute("DELETE FROM chain_inputs")
conn.execute("DELETE FROM production_chains")
conn.execute("DELETE FROM commodities")
conn.execute("DELETE FROM gate_links")
# 2. Gate links
print(" [2/8] Importing gate links...")
n_links = import_gate_links(conn, args.dry_run)
print(f" {n_links} rows (bidirectional)")
# 2. Gate links
print(" [2/10] Importing gate links...")
n_links = import_gate_links(conn, args.dry_run)
print(f" {n_links} rows (bidirectional)")
# 3. Commodities
print(" [3/8] Importing commodities...")
n_commodities = import_commodities(conn, args.dry_run)
print(f" {n_commodities} commodities")
# 3. Commodities
print(" [3/10] Importing commodities...")
n_commodities = import_commodities(conn, args.dry_run)
print(f" {n_commodities} commodities")
# 4. Production chains
print(" [4/8] Importing production chains...")
n_chains, n_inputs = import_chains(conn, args.dry_run)
print(f" {n_chains} chains, {n_inputs} inputs")
# 4. Production chains
print(" [4/10] Importing production chains...")
n_chains, n_inputs = import_chains(conn, args.dry_run)
print(f" {n_chains} chains, {n_inputs} inputs")
# 5. Currency zones
print(" [5/8] Setting currency zones...")
zones = set_currency_zones(conn, args.dry_run)
for zone, count in sorted(zones.items()):
print(f" {zone}: {count}")
# 5. Currency zones
print(" [5/10] Setting currency zones...")
zones = set_currency_zones(conn, args.dry_run)
for zone, count in sorted(zones.items()):
print(f" {zone}: {count}")
# 6. Gate energy connectivity (D-186) — must run after currency zones
print(" [6/8] Setting gate energy connectivity...")
energy = set_gate_energy(conn, args.dry_run)
for label, count in sorted(energy.items()):
print(f" {label}: {count}")
# 6. Gate energy connectivity (D-186) — must run after currency zones
print(" [6/10] Setting gate energy connectivity...")
energy = set_gate_energy(conn, args.dry_run)
for label, count in sorted(energy.items()):
print(f" {label}: {count}")
# 7. Sync corporations from wiki (D-182: hard error on name divergence)
print(" [7/8] Syncing corporations...")
corp_errors = sync_corporations(conn, wiki_corps, args.dry_run)
if corp_errors:
print(f" ERRORS ({len(corp_errors)}) — name divergence detected (D-182):")
for e in corp_errors:
print(f" - {e}")
print(" Fix: update wiki title or DB proper_name to match, then re-run.")
# 7. Sync corporations from wiki (D-182: hard error on name divergence)
print(" [7/10] Syncing corporations...")
corp_errors = sync_corporations(conn, wiki_corps, args.dry_run)
if corp_errors:
print(f" ERRORS ({len(corp_errors)}) — name divergence detected (D-182):")
for e in corp_errors:
print(f" - {e}")
print(" Fix: update wiki title or DB proper_name to match, then re-run.")
raise _ImportAborted()
n_db_corps = conn.execute("SELECT COUNT(*) FROM corporations").fetchone()[0]
print(f" {n_db_corps} corporations in DB ({len(wiki_corps)} from wiki)")
# 8. Corp presence from wiki headquarters data
print(" [8/10] Importing corp presence...")
commodity_ids = {
r[0] for r in conn.execute("SELECT commodity_id FROM commodities").fetchall()
}
n_presence = import_corp_presence(conn, wiki_corps, commodity_ids, args.dry_run)
print(f" {n_presence} corp_presence rows")
# 9. Brand products and inputs (D-189, #827)
print(" [9/10] Importing brand products and inputs...")
n_brands, n_brand_inputs = import_brands(conn, args.dry_run)
print(f" {n_brands} brand_products, {n_brand_inputs} brand_inputs")
# 10. System fiscal parameters (D-189 section 6)
print(" [10/10] Populating system_fiscal...")
n_fiscal = import_system_fiscal(conn, args.dry_run)
print(f" {n_fiscal} system_fiscal rows")
# Validate structural integrity (FK, chain refs, chain completeness).
# These errors indicate broken imported data — do NOT commit.
print("\n Validating structural integrity...")
struct_errors = validate(conn)
struct_errors.extend(validate_brands(conn))
if struct_errors:
print(f" STRUCTURAL ERRORS ({len(struct_errors)}) — rolling back:")
for e in struct_errors:
print(f" - {e}")
raise _ImportAborted()
print(" FK integrity, chain completeness, and brand layer (V-B01V-B06) OK")
# Commit all imported data (corps, presence, etc.) before coverage check.
# Coverage validation is a Phase 2 gate (D-175) — data should be persisted
# so tools can query it and report gaps clearly.
if not args.dry_run:
conn.commit()
print(" Data committed.")
else:
# Dry-run: leave the transaction open so the coverage check below
# can still SELECT against the in-memory imported data. The
# transaction is discarded when conn.close() runs on exit.
print(" Dry run — no changes written.")
except _ImportAborted:
conn.rollback()
conn.close()
sys.exit(1)
n_db_corps = conn.execute("SELECT COUNT(*) FROM corporations").fetchone()[0]
print(f" {n_db_corps} corporations in DB ({len(wiki_corps)} from wiki)")
# 8. Corp presence from wiki headquarters data
print(" [8/8] Importing corp presence...")
commodity_ids = {
r[0] for r in conn.execute("SELECT commodity_id FROM commodities").fetchall()
}
n_presence = import_corp_presence(conn, wiki_corps, commodity_ids, args.dry_run)
print(f" {n_presence} corp_presence rows")
# Validate structural integrity (FK, chain refs, chain completeness).
# These errors indicate broken imported data — do NOT commit.
print("\n Validating structural integrity...")
struct_errors = validate(conn)
if struct_errors:
print(f" STRUCTURAL ERRORS ({len(struct_errors)}) — rolling back:")
for e in struct_errors:
print(f" - {e}")
except BaseException:
# Any other exception (KeyboardInterrupt, MemoryError, DB error,
# programmer error) triggers a rollback so the DB is never left in
# a half-imported state. Re-raise so the user sees the traceback.
conn.rollback()
conn.close()
sys.exit(1)
else:
print(" FK integrity and chain completeness OK")
# Commit all imported data (corps, presence, etc.) before coverage check.
# Coverage validation is a Phase 2 gate (D-175) — data should be persisted
# so tools can query it and report gaps clearly.
if not args.dry_run:
conn.commit()
print(" Data committed.")
else:
print(" Dry run — no changes written.")
raise
# Validate coverage (hard errors per D-175, but after commit so data is usable).
print("\n Validating coverage (D-175 Phase 2 gate)...")
@@ -809,7 +1139,9 @@ def main():
conn.close()
print(f"\n Done: {n_links} gate_links, {n_commodities} commodities, "
f"{n_chains} chains, {n_inputs} inputs, {n_presence} corp_presence\n")
f"{n_chains} chains, {n_inputs} inputs, {n_presence} corp_presence, "
f"{n_brands} brand_products, {n_brand_inputs} brand_inputs, "
f"{n_fiscal} system_fiscal\n")
if __name__ == "__main__":
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,139 @@
#!/usr/bin/env python3
"""
Batch populate terrain_reference column in systems.db bodies table.
For each inhabited body with NULL terrain_reference:
1. Construct expected wiki heightmap path:
wiki/star-systems/{system_slug}/bodies/{body_id}/heightmap.png
where system_slug = system_id with spaces replaced by hyphens.
2. Verify the file exists.
3. Update terrain_reference to the relative path.
Bodies with missing heightmaps are logged to stdout for remediation.
This is the prerequisite for generate_atlas.py (#832).
Usage:
python3 tooling/planet-gen/populate_terrain_reference.py
python3 tooling/planet-gen/populate_terrain_reference.py --dry-run
python3 tooling/planet-gen/populate_terrain_reference.py --db path/to/systems.db
Decisions: D-191 (atlas pipeline prerequisites)
"""
import argparse
import sqlite3
from pathlib import Path
SCRIPT_DIR = Path(__file__).resolve().parent
REPO_ROOT = (SCRIPT_DIR / ".." / "..").resolve()
DB_PATH = REPO_ROOT / "server" / "data" / "systems.db"
WIKI_DIR = REPO_ROOT / "wiki" / "star-systems"
def system_slug(system_id: str) -> str:
"""Convert a system_id to its wiki directory slug.
'GJ 71''GJ-71'
'GJ 244A''GJ-244A'
'GJ 559B''GJ-559B'
"""
return system_id.replace(" ", "-")
def expected_heightmap_path(system_id: str, body_id: str) -> Path:
"""Return the expected absolute heightmap path for a body."""
return WIKI_DIR / system_slug(system_id) / "bodies" / body_id / "heightmap.png"
def relative_terrain_reference(system_id: str, body_id: str) -> str:
"""Return the terrain_reference value to store in the DB.
Stored as a repo-root-relative path so it is portable across checkouts.
"""
return f"wiki/star-systems/{system_slug(system_id)}/bodies/{body_id}/heightmap.png"
def main():
parser = argparse.ArgumentParser(
description="Populate terrain_reference column in systems.db bodies table"
)
parser.add_argument("--db", default=str(DB_PATH), help="Path to systems.db")
parser.add_argument(
"--dry-run",
action="store_true",
help="Report without writing changes",
)
args = parser.parse_args()
db_path = Path(args.db)
if not db_path.exists():
print(f"error: {db_path} not found")
raise SystemExit(1)
conn = sqlite3.connect(str(db_path))
conn.execute("PRAGMA foreign_keys=ON")
# Fetch all inhabited bodies with NULL terrain_reference.
rows = conn.execute("""
SELECT b.body_id, b.system_id
FROM bodies b
WHERE b.terrain_reference IS NULL
ORDER BY b.system_id, b.body_id
""").fetchall()
print(f"\n terrain_reference population pass")
print(f" DB: {db_path}")
if args.dry_run:
print(f" Mode: DRY RUN")
print(f"\n {len(rows)} bodies with NULL terrain_reference\n")
found = []
missing = []
for body_id, system_id in rows:
path = expected_heightmap_path(system_id, body_id)
if path.exists():
found.append((body_id, system_id))
else:
missing.append((body_id, system_id, str(path)))
# Report missing heightmaps before writing — helps flag gaps early.
if missing:
print(f" MISSING heightmaps ({len(missing)} bodies — no update for these):")
for body_id, system_id, path in missing:
print(f" {body_id} ({system_id}) → {path}")
print()
if found:
print(f" Updating {len(found)} bodies with terrain_reference:")
for body_id, system_id in found:
ref = relative_terrain_reference(system_id, body_id)
print(f" {body_id} ({system_id}) → {ref}")
if not args.dry_run:
conn.execute(
"UPDATE bodies SET terrain_reference = ? WHERE body_id = ?",
(ref, body_id),
)
if not args.dry_run:
conn.commit()
print(f"\n Committed {len(found)} terrain_reference updates.")
else:
print(f"\n Dry run — no changes written.")
conn.close()
# Summary
print(f"\n Summary:")
print(f" Updated: {len(found)}")
print(f" Missing: {len(missing)}")
print(f" Total: {len(rows)}\n")
if missing:
print(f" Action required: generate heightmaps for {len(missing)} bodies "
f"before running generate_atlas.py (#832).")
print(f" Use: make generate-terrain (or run generate.py per body)\n")
if __name__ == "__main__":
main()
+283
View File
@@ -0,0 +1,283 @@
# ==========================================================================
# Settled Reach — Brand Product Records (Phase 2 Demand Stubs)
# Source of truth for brand_products and brand_inputs tables.
# Compiled to systems.db via `make economy-db`.
#
# These 4 canonical brand corps register as commodity demand nodes in the
# tâtonnement simulation (D-185: brands consume commodities, not the reverse).
# Pricing model and cultural premium curves are Phase 3+ deliverables (D-189).
#
# Phase 2 boundary: only the 4 anchor brands from D-189 §5 are authored here
# (Calloway, VGV, thrds, Bífröst Marmor). The additional ~23 brand corps
# listed in D-189 §11 are deliberately deferred to Phase 3 — Phase 2 only
# needs the demand-node plumbing + V-B01..V-B06 validation to be exercised
# end-to-end. Add new brands to this file; the importer and validators pick
# them up without schema changes.
#
# Fields per [[brand_products]] entry:
# brand_product_id Unique slug (corp_id + product slug)
# corp_id Must match a slug in wiki/corporations/
# product_name Display name
# brand_category terroir|heritage_craft|tech_premium|cultural|
# service_premium|commodity_branded|design_heritage|platform_catalogue
# value_trajectory appreciating|depreciating|timeless
# scarcity_class capped|constrained|scalable|unlimited
# product_subcategory Readable product type descriptor
# base_premium_multiplier Multiplier over raw commodity input cost (Phase 3 pricing)
# premium_floor Minimum administered price floor (Phase 3 pricing)
# origin_system GJ catalog ID — must match star_systems.system_id
# terroir_locked true: production cannot move from origin_system
# currency_denomination tractus|mark|mixed|sol_adjacent
# shadow_viable true: circulates in shadow economy
# brand_tier halo|volume
# halo_brand_id For volume tiers: brand_product_id of parent halo product
#
# Fields per [[brand_inputs]] entry:
# brand_product_id References a brand_products entry above
# commodity_id Must match commodities.toml
# quantity Demand coefficient (annual units consumed per production run)
#
# Decisions: D-185 (brands as demand nodes), D-189 (brand layer architecture),
# D-190 (volume calibration), D-182 (TOML pipeline)
# ==========================================================================
# ==========================================================================
# CALLOWAY DISTILLERY (north_reach — terroir, appreciating)
# Eleven distilleries, 400 years of production. GJ 3325.
# Inputs: grain (agricultural_produce) + water for single malt whisky.
# ==========================================================================
[[brand_products]]
brand_product_id = "calloway-single-malt-halo"
corp_id = "calloway-distillery"
product_name = "Calloway Single Malt 25yr"
brand_category = "terroir"
value_trajectory = "appreciating"
scarcity_class = "capped"
product_subcategory = "aged_spirits"
base_premium_multiplier = 18.0
premium_floor = 0.85
origin_system = "GJ 3325"
terroir_locked = true
currency_denomination = "tractus"
shadow_viable = true
brand_tier = "halo"
[[brand_inputs]]
brand_product_id = "calloway-single-malt-halo"
commodity_id = "agricultural_produce"
quantity = 0.12 # grain — ~12% of annual produce demand per distillery output unit
[[brand_inputs]]
brand_product_id = "calloway-single-malt-halo"
commodity_id = "water"
quantity = 0.08 # distillation water
[[brand_products]]
brand_product_id = "calloway-reserve-volume"
corp_id = "calloway-distillery"
product_name = "Calloway Reserve"
brand_category = "terroir"
value_trajectory = "appreciating"
scarcity_class = "constrained"
product_subcategory = "aged_spirits"
base_premium_multiplier = 4.5
premium_floor = 0.30
origin_system = "GJ 3325"
terroir_locked = true
currency_denomination = "tractus"
shadow_viable = true
brand_tier = "volume"
halo_brand_id = "calloway-single-malt-halo"
[[brand_inputs]]
brand_product_id = "calloway-reserve-volume"
commodity_id = "agricultural_produce"
quantity = 0.55 # larger grain demand — volume tier drives commodity draw
[[brand_inputs]]
brand_product_id = "calloway-reserve-volume"
commodity_id = "water"
quantity = 0.35
# ==========================================================================
# VINS DE GRAND VIDE (west_reach — terroir, appreciating / timeless)
# Négociant cooperative, GJ 395. Wine production from châteaux network.
# Inputs: agricultural_produce (grapes) + water.
# ==========================================================================
[[brand_products]]
brand_product_id = "vgv-premier-cru-halo"
corp_id = "vins-de-grand-vide"
product_name = "Grand Vide Premier Cru"
brand_category = "terroir"
value_trajectory = "appreciating"
scarcity_class = "capped"
product_subcategory = "fine_wine"
base_premium_multiplier = 12.0
premium_floor = 0.70
origin_system = "GJ 395"
terroir_locked = true
currency_denomination = "tractus"
shadow_viable = true
brand_tier = "halo"
[[brand_inputs]]
brand_product_id = "vgv-premier-cru-halo"
commodity_id = "agricultural_produce"
quantity = 0.18 # estate grapes — limited châteaux harvest
[[brand_inputs]]
brand_product_id = "vgv-premier-cru-halo"
commodity_id = "water"
quantity = 0.04
[[brand_products]]
brand_product_id = "vgv-standard-volume"
corp_id = "vins-de-grand-vide"
product_name = "Grand Vide Standard"
brand_category = "terroir"
value_trajectory = "timeless"
scarcity_class = "scalable"
product_subcategory = "wine"
base_premium_multiplier = 2.2
premium_floor = 0.15
origin_system = "GJ 395"
terroir_locked = false
currency_denomination = "tractus"
shadow_viable = false
brand_tier = "volume"
halo_brand_id = "vgv-premier-cru-halo"
[[brand_inputs]]
brand_product_id = "vgv-standard-volume"
commodity_id = "agricultural_produce"
quantity = 0.80 # négociant aggregation — largest agricultural demand node
[[brand_inputs]]
brand_product_id = "vgv-standard-volume"
commodity_id = "water"
quantity = 0.20
# ==========================================================================
# THRDS (north_reach — heritage_craft, timeless)
# Cold-weather technical clothing cooperative, GJ 475.
# Inputs: textiles (brach fiber) + organic_compounds (dye, finish).
# ==========================================================================
[[brand_products]]
brand_product_id = "thrds-origin-halo"
corp_id = "thrds"
product_name = "thrds Origin"
brand_category = "heritage_craft"
value_trajectory = "timeless"
scarcity_class = "constrained"
product_subcategory = "technical_clothing"
base_premium_multiplier = 6.0
premium_floor = 0.50
origin_system = "GJ 475"
terroir_locked = true
currency_denomination = "tractus"
shadow_viable = false
brand_tier = "halo"
[[brand_inputs]]
brand_product_id = "thrds-origin-halo"
commodity_id = "textiles"
quantity = 0.30 # brach fiber — origin-specific weave
[[brand_inputs]]
brand_product_id = "thrds-origin-halo"
commodity_id = "organic_compounds"
quantity = 0.08 # plant-derived dyes and finishing compounds
[[brand_products]]
brand_product_id = "thrds-standard-volume"
corp_id = "thrds"
product_name = "thrds Standard"
brand_category = "heritage_craft"
value_trajectory = "timeless"
scarcity_class = "scalable"
product_subcategory = "technical_clothing"
base_premium_multiplier = 2.8
premium_floor = 0.20
origin_system = "GJ 475"
terroir_locked = false
currency_denomination = "tractus"
shadow_viable = false
brand_tier = "volume"
halo_brand_id = "thrds-origin-halo"
[[brand_inputs]]
brand_product_id = "thrds-standard-volume"
commodity_id = "textiles"
quantity = 1.20 # primary textile demand driver (volume tier production)
[[brand_inputs]]
brand_product_id = "thrds-standard-volume"
commodity_id = "organic_compounds"
quantity = 0.25
# ==========================================================================
# BÍFRÖST MARMOR (Compact / north_reach — terroir, appreciating)
# Kvitfjell moon marble quarry, Nyrheim (GJ 3737). Nyrheim Cooperative subsidiary.
# Inputs: stone (raw marble) + chemicals (polishing, finishing agents).
# Shadow viable: Compact-adjacent, some Sol trade on interior luxury markets.
# ==========================================================================
[[brand_products]]
brand_product_id = "bifrost-grade-a-halo"
corp_id = "bifrost-marmor"
product_name = "Kvitfjell Grade A"
brand_category = "terroir"
value_trajectory = "appreciating"
scarcity_class = "capped"
product_subcategory = "architectural_stone"
base_premium_multiplier = 22.0
premium_floor = 1.20
origin_system = "GJ 3737"
terroir_locked = true
currency_denomination = "mark"
shadow_viable = true
brand_tier = "halo"
[[brand_inputs]]
brand_product_id = "bifrost-grade-a-halo"
commodity_id = "stone"
quantity = 0.40 # high-purity calcite marble extraction — geological scarcity
[[brand_inputs]]
brand_product_id = "bifrost-grade-a-halo"
commodity_id = "chemicals"
quantity = 0.10 # precision polishing compounds
[[brand_products]]
brand_product_id = "bifrost-commercial-volume"
corp_id = "bifrost-marmor"
product_name = "Kvitfjell Commercial"
brand_category = "terroir"
value_trajectory = "appreciating"
scarcity_class = "constrained"
product_subcategory = "architectural_stone"
base_premium_multiplier = 5.5
premium_floor = 0.40
origin_system = "GJ 3737"
terroir_locked = true
currency_denomination = "mixed"
shadow_viable = true
brand_tier = "volume"
halo_brand_id = "bifrost-grade-a-halo"
[[brand_inputs]]
brand_product_id = "bifrost-commercial-volume"
commodity_id = "stone"
quantity = 1.50 # commercial-grade quarrying — volume demand node
[[brand_inputs]]
brand_product_id = "bifrost-commercial-volume"
commodity_id = "chemicals"
quantity = 0.30
@@ -804,7 +804,28 @@
}
],
"roads": [],
"cities": [],
"cities": [
{
"id": "city_0",
"name": "",
"kind": "capital",
"center": [
97,
207
],
"population": 12000000
}
],
"railroads": [],
"pois": []
"pois": [
{
"id": "poi_0",
"name": "",
"kind": "transit",
"center": [
97,
207
]
}
]
}
@@ -1134,7 +1134,28 @@
}
],
"roads": [],
"cities": [],
"cities": [
{
"id": "city_0",
"name": "",
"kind": "capital",
"center": [
5,
240
],
"population": 1200
}
],
"railroads": [],
"pois": []
"pois": [
{
"id": "poi_0",
"name": "",
"kind": "transit",
"center": [
5,
240
]
}
]
}
@@ -454,7 +454,28 @@
}
],
"roads": [],
"cities": [],
"cities": [
{
"id": "city_0",
"name": "",
"kind": "capital",
"center": [
159,
509
],
"population": 35000000
}
],
"railroads": [],
"pois": []
"pois": [
{
"id": "poi_0",
"name": "",
"kind": "transit",
"center": [
159,
509
]
}
]
}
@@ -493,7 +493,28 @@
}
],
"roads": [],
"cities": [],
"cities": [
{
"id": "city_0",
"name": "",
"kind": "capital",
"center": [
167,
467
],
"population": 320000
}
],
"railroads": [],
"pois": []
"pois": [
{
"id": "poi_0",
"name": "",
"kind": "transit",
"center": [
167,
467
]
}
]
}
@@ -336,7 +336,28 @@
}
],
"roads": [],
"cities": [],
"cities": [
{
"id": "city_0",
"name": "",
"kind": "capital",
"center": [
237,
3
],
"population": 4200
}
],
"railroads": [],
"pois": []
"pois": [
{
"id": "poi_0",
"name": "",
"kind": "transit",
"center": [
237,
3
]
}
]
}
@@ -782,7 +782,28 @@
}
],
"roads": [],
"cities": [],
"cities": [
{
"id": "city_0",
"name": "",
"kind": "capital",
"center": [
113,
0
],
"population": 2000000
}
],
"railroads": [],
"pois": []
"pois": [
{
"id": "poi_0",
"name": "",
"kind": "transit",
"center": [
113,
0
]
}
]
}
@@ -512,7 +512,28 @@
}
],
"roads": [],
"cities": [],
"cities": [
{
"id": "city_0",
"name": "",
"kind": "capital",
"center": [
115,
462
],
"population": 32000
}
],
"railroads": [],
"pois": []
"pois": [
{
"id": "poi_0",
"name": "",
"kind": "transit",
"center": [
115,
462
]
}
]
}
@@ -457,7 +457,28 @@
}
],
"roads": [],
"cities": [],
"cities": [
{
"id": "city_0",
"name": "",
"kind": "capital",
"center": [
179,
427
],
"population": 74000
}
],
"railroads": [],
"pois": []
"pois": [
{
"id": "poi_0",
"name": "",
"kind": "transit",
"center": [
179,
427
]
}
]
}
@@ -496,7 +496,28 @@
}
],
"roads": [],
"cities": [],
"cities": [
{
"id": "city_0",
"name": "",
"kind": "capital",
"center": [
154,
155
],
"population": 180000
}
],
"railroads": [],
"pois": []
"pois": [
{
"id": "poi_0",
"name": "",
"kind": "transit",
"center": [
154,
155
]
}
]
}
@@ -501,7 +501,28 @@
}
],
"roads": [],
"cities": [],
"cities": [
{
"id": "city_0",
"name": "",
"kind": "capital",
"center": [
83,
1
],
"population": 1200
}
],
"railroads": [],
"pois": []
"pois": [
{
"id": "poi_0",
"name": "",
"kind": "transit",
"center": [
83,
1
]
}
]
}
@@ -1097,7 +1097,28 @@
}
],
"roads": [],
"cities": [],
"cities": [
{
"id": "city_0",
"name": "",
"kind": "capital",
"center": [
211,
352
],
"population": 380000
}
],
"railroads": [],
"pois": []
"pois": [
{
"id": "poi_0",
"name": "",
"kind": "transit",
"center": [
211,
352
]
}
]
}
@@ -294,8 +294,575 @@
"area_cells": 634
}
],
"roads": [],
"cities": [],
"railroads": [],
"pois": []
"roads": [
{
"id": "road_0",
"name": "",
"kind": "commercial",
"path": [
[
41,
423
],
[
42,
424
],
[
43,
425
],
[
45,
427
],
[
46,
428
],
[
48,
430
],
[
49,
431
],
[
50,
432
],
[
52,
434
],
[
53,
435
],
[
55,
437
],
[
56,
438
],
[
58,
440
],
[
59,
441
],
[
60,
442
],
[
62,
444
],
[
63,
445
],
[
65,
446
],
[
66,
446
],
[
68,
446
],
[
69,
446
],
[
70,
446
],
[
72,
446
],
[
73,
446
],
[
75,
446
],
[
76,
446
],
[
77,
446
],
[
79,
446
],
[
80,
446
],
[
82,
446
],
[
83,
446
],
[
85,
446
],
[
86,
446
],
[
87,
446
],
[
89,
446
],
[
90,
446
],
[
92,
446
],
[
93,
446
],
[
95,
446
],
[
96,
446
],
[
97,
446
],
[
99,
446
],
[
100,
446
],
[
102,
446
],
[
103,
446
],
[
104,
447
],
[
106,
448
],
[
107,
448
],
[
109,
449
],
[
110,
449
],
[
112,
450
],
[
113,
451
],
[
114,
451
],
[
116,
452
],
[
117,
453
],
[
119,
454
],
[
120,
454
],
[
122,
455
],
[
123,
455
],
[
124,
456
],
[
126,
457
],
[
127,
457
],
[
129,
458
],
[
130,
458
],
[
131,
459
]
]
}
],
"cities": [
{
"id": "city_0",
"name": "",
"kind": "capital",
"center": [
41,
423
],
"population": 466670000
},
{
"id": "city_1",
"name": "",
"kind": "city",
"center": [
131,
459
],
"population": 233330000
}
],
"railroads": [
{
"id": "railroad_0",
"name": "",
"kind": "passenger_freight",
"path": [
[
41,
423
],
[
42,
424
],
[
43,
425
],
[
45,
427
],
[
46,
428
],
[
48,
430
],
[
49,
431
],
[
50,
432
],
[
52,
434
],
[
53,
435
],
[
55,
437
],
[
56,
438
],
[
58,
440
],
[
59,
441
],
[
60,
442
],
[
62,
444
],
[
63,
445
],
[
65,
446
],
[
66,
446
],
[
68,
446
],
[
69,
446
],
[
70,
446
],
[
72,
446
],
[
73,
446
],
[
75,
446
],
[
76,
446
],
[
77,
446
],
[
79,
446
],
[
80,
446
],
[
82,
446
],
[
83,
446
],
[
85,
446
],
[
86,
446
],
[
87,
446
],
[
89,
446
],
[
90,
446
],
[
92,
446
],
[
93,
446
],
[
95,
446
],
[
96,
446
],
[
97,
446
],
[
99,
446
],
[
100,
446
],
[
102,
446
],
[
103,
446
],
[
104,
447
],
[
106,
448
],
[
107,
448
],
[
109,
449
],
[
110,
449
],
[
112,
450
],
[
113,
451
],
[
114,
451
],
[
116,
452
],
[
117,
453
],
[
119,
454
],
[
120,
454
],
[
122,
455
],
[
123,
455
],
[
124,
456
],
[
126,
457
],
[
127,
457
],
[
129,
458
],
[
130,
458
],
[
131,
459
]
]
}
],
"pois": [
{
"id": "poi_0",
"name": "",
"kind": "transit",
"center": [
41,
423
]
}
]
}
@@ -539,7 +539,28 @@
}
],
"roads": [],
"cities": [],
"cities": [
{
"id": "city_0",
"name": "",
"kind": "capital",
"center": [
94,
143
],
"population": 22000000
}
],
"railroads": [],
"pois": []
"pois": [
{
"id": "poi_0",
"name": "",
"kind": "transit",
"center": [
94,
143
]
}
]
}
@@ -534,7 +534,28 @@
}
],
"roads": [],
"cities": [],
"cities": [
{
"id": "city_0",
"name": "",
"kind": "capital",
"center": [
250,
508
],
"population": 140000
}
],
"railroads": [],
"pois": []
"pois": [
{
"id": "poi_0",
"name": "",
"kind": "transit",
"center": [
250,
508
]
}
]
}
@@ -357,8 +357,575 @@
"area_cells": 57
}
],
"roads": [],
"cities": [],
"railroads": [],
"pois": []
"roads": [
{
"id": "road_0",
"name": "",
"kind": "commercial",
"path": [
[
78,
508
],
[
79,
507
],
[
80,
507
],
[
82,
507
],
[
83,
507
],
[
85,
507
],
[
86,
507
],
[
88,
507
],
[
89,
507
],
[
91,
507
],
[
92,
507
],
[
94,
506
],
[
95,
506
],
[
97,
506
],
[
98,
505
],
[
100,
503
],
[
101,
502
],
[
103,
500
],
[
104,
499
],
[
106,
497
],
[
107,
497
],
[
109,
497
],
[
110,
497
],
[
112,
497
],
[
113,
497
],
[
115,
497
],
[
116,
497
],
[
118,
497
],
[
119,
497
],
[
121,
497
],
[
122,
497
],
[
124,
497
],
[
125,
497
],
[
126,
497
],
[
128,
497
],
[
129,
497
],
[
131,
495
],
[
132,
495
],
[
134,
495
],
[
135,
495
],
[
137,
496
],
[
138,
497
],
[
140,
499
],
[
141,
500
],
[
143,
500
],
[
144,
500
],
[
146,
500
],
[
147,
499
],
[
149,
498
],
[
150,
498
],
[
152,
497
],
[
153,
497
],
[
155,
496
],
[
156,
495
],
[
158,
494
],
[
159,
494
],
[
161,
493
],
[
162,
493
],
[
164,
492
],
[
165,
491
],
[
167,
490
],
[
168,
490
],
[
170,
489
],
[
171,
489
],
[
172,
488
]
]
}
],
"cities": [
{
"id": "city_0",
"name": "",
"kind": "capital",
"center": [
78,
508
],
"population": 800000000
},
{
"id": "city_1",
"name": "",
"kind": "city",
"center": [
172,
488
],
"population": 400000000
}
],
"railroads": [
{
"id": "railroad_0",
"name": "",
"kind": "passenger_freight",
"path": [
[
78,
508
],
[
79,
507
],
[
80,
507
],
[
82,
507
],
[
83,
507
],
[
85,
507
],
[
86,
507
],
[
88,
507
],
[
89,
507
],
[
91,
507
],
[
92,
507
],
[
94,
506
],
[
95,
506
],
[
97,
506
],
[
98,
505
],
[
100,
503
],
[
101,
502
],
[
103,
500
],
[
104,
499
],
[
106,
497
],
[
107,
497
],
[
109,
497
],
[
110,
497
],
[
112,
497
],
[
113,
497
],
[
115,
497
],
[
116,
497
],
[
118,
497
],
[
119,
497
],
[
121,
497
],
[
122,
497
],
[
124,
497
],
[
125,
497
],
[
126,
497
],
[
128,
497
],
[
129,
497
],
[
131,
495
],
[
132,
495
],
[
134,
495
],
[
135,
495
],
[
137,
496
],
[
138,
497
],
[
140,
499
],
[
141,
500
],
[
143,
500
],
[
144,
500
],
[
146,
500
],
[
147,
499
],
[
149,
498
],
[
150,
498
],
[
152,
497
],
[
153,
497
],
[
155,
496
],
[
156,
495
],
[
158,
494
],
[
159,
494
],
[
161,
493
],
[
162,
493
],
[
164,
492
],
[
165,
491
],
[
167,
490
],
[
168,
490
],
[
170,
489
],
[
171,
489
],
[
172,
488
]
]
}
],
"pois": [
{
"id": "poi_0",
"name": "",
"kind": "transit",
"center": [
172,
488
]
}
]
}
@@ -1099,7 +1099,28 @@
}
],
"roads": [],
"cities": [],
"cities": [
{
"id": "city_0",
"name": "",
"kind": "capital",
"center": [
219,
0
],
"population": 8000000
}
],
"railroads": [],
"pois": []
"pois": [
{
"id": "poi_0",
"name": "",
"kind": "transit",
"center": [
219,
0
]
}
]
}
@@ -642,7 +642,28 @@
}
],
"roads": [],
"cities": [],
"cities": [
{
"id": "city_0",
"name": "",
"kind": "capital",
"center": [
169,
144
],
"population": 52000000
}
],
"railroads": [],
"pois": []
"pois": [
{
"id": "poi_0",
"name": "",
"kind": "transit",
"center": [
169,
144
]
}
]
}
@@ -172,7 +172,28 @@
}
],
"roads": [],
"cities": [],
"cities": [
{
"id": "city_0",
"name": "",
"kind": "capital",
"center": [
206,
422
],
"population": 420
}
],
"railroads": [],
"pois": []
"pois": [
{
"id": "poi_0",
"name": "",
"kind": "transit",
"center": [
206,
422
]
}
]
}
@@ -533,7 +533,28 @@
}
],
"roads": [],
"cities": [],
"cities": [
{
"id": "city_0",
"name": "",
"kind": "capital",
"center": [
61,
1
],
"population": 28000
}
],
"railroads": [],
"pois": []
"pois": [
{
"id": "poi_0",
"name": "",
"kind": "transit",
"center": [
61,
1
]
}
]
}
@@ -686,7 +686,28 @@
}
],
"roads": [],
"cities": [],
"cities": [
{
"id": "city_0",
"name": "",
"kind": "capital",
"center": [
239,
1
],
"population": 380000
}
],
"railroads": [],
"pois": []
"pois": [
{
"id": "poi_0",
"name": "",
"kind": "transit",
"center": [
239,
1
]
}
]
}
@@ -319,7 +319,28 @@
}
],
"roads": [],
"cities": [],
"cities": [
{
"id": "city_0",
"name": "",
"kind": "capital",
"center": [
64,
0
],
"population": 600
}
],
"railroads": [],
"pois": []
"pois": [
{
"id": "poi_0",
"name": "",
"kind": "transit",
"center": [
64,
0
]
}
]
}
@@ -221,7 +221,28 @@
}
],
"roads": [],
"cities": [],
"cities": [
{
"id": "city_0",
"name": "",
"kind": "capital",
"center": [
60,
373
],
"population": 4200
}
],
"railroads": [],
"pois": []
"pois": [
{
"id": "poi_0",
"name": "",
"kind": "transit",
"center": [
60,
373
]
}
]
}
@@ -617,7 +617,28 @@
}
],
"roads": [],
"cities": [],
"cities": [
{
"id": "city_0",
"name": "",
"kind": "capital",
"center": [
7,
507
],
"population": 32000
}
],
"railroads": [],
"pois": []
"pois": [
{
"id": "poi_0",
"name": "",
"kind": "transit",
"center": [
7,
507
]
}
]
}
@@ -577,7 +577,28 @@
}
],
"roads": [],
"cities": [],
"cities": [
{
"id": "city_0",
"name": "",
"kind": "capital",
"center": [
207,
394
],
"population": 10000
}
],
"railroads": [],
"pois": []
"pois": [
{
"id": "poi_0",
"name": "",
"kind": "transit",
"center": [
207,
394
]
}
]
}
@@ -379,7 +379,28 @@
}
],
"roads": [],
"cities": [],
"cities": [
{
"id": "city_0",
"name": "",
"kind": "capital",
"center": [
189,
154
],
"population": 500
}
],
"railroads": [],
"pois": []
"pois": [
{
"id": "poi_0",
"name": "",
"kind": "transit",
"center": [
189,
154
]
}
]
}
@@ -978,7 +978,28 @@
}
],
"roads": [],
"cities": [],
"cities": [
{
"id": "city_0",
"name": "",
"kind": "capital",
"center": [
101,
380
],
"population": 85000
}
],
"railroads": [],
"pois": []
"pois": [
{
"id": "poi_0",
"name": "",
"kind": "transit",
"center": [
101,
380
]
}
]
}
@@ -474,7 +474,28 @@
}
],
"roads": [],
"cities": [],
"cities": [
{
"id": "city_0",
"name": "",
"kind": "capital",
"center": [
90,
448
],
"population": 500000000
}
],
"railroads": [],
"pois": []
"pois": [
{
"id": "poi_0",
"name": "",
"kind": "transit",
"center": [
90,
448
]
}
]
}
@@ -1186,8 +1186,575 @@
"area_cells": 151
}
],
"roads": [],
"cities": [],
"railroads": [],
"pois": []
"roads": [
{
"id": "road_0",
"name": "",
"kind": "commercial",
"path": [
[
57,
450
],
[
59,
450
],
[
61,
450
],
[
63,
449
],
[
65,
449
],
[
67,
447
],
[
69,
447
],
[
71,
447
],
[
73,
447
],
[
75,
447
],
[
77,
447
],
[
79,
447
],
[
81,
447
],
[
83,
447
],
[
85,
447
],
[
87,
446
],
[
89,
444
],
[
91,
442
],
[
92,
440
],
[
93,
438
],
[
94,
436
],
[
95,
434
],
[
97,
432
],
[
99,
430
],
[
101,
428
],
[
101,
426
],
[
102,
424
],
[
102,
422
],
[
103,
420
],
[
103,
418
],
[
103,
416
],
[
103,
414
],
[
105,
412
],
[
107,
410
],
[
109,
408
],
[
111,
406
],
[
113,
404
],
[
114,
402
],
[
115,
400
],
[
115,
398
],
[
115,
396
],
[
115,
394
],
[
114,
392
],
[
114,
390
],
[
113,
388
],
[
113,
386
],
[
113,
384
],
[
112,
382
],
[
112,
380
],
[
111,
378
],
[
110,
376
],
[
109,
374
],
[
108,
372
],
[
106,
370
],
[
106,
368
],
[
104,
367
],
[
102,
366
],
[
100,
365
],
[
98,
364
],
[
96,
364
],
[
94,
364
],
[
92,
364
],
[
90,
364
],
[
88,
364
],
[
87,
364
]
]
}
],
"cities": [
{
"id": "city_0",
"name": "",
"kind": "capital",
"center": [
57,
450
],
"population": 53330000
},
{
"id": "city_1",
"name": "",
"kind": "city",
"center": [
87,
364
],
"population": 26670000
}
],
"railroads": [
{
"id": "railroad_0",
"name": "",
"kind": "passenger_freight",
"path": [
[
57,
450
],
[
59,
450
],
[
61,
450
],
[
63,
449
],
[
65,
449
],
[
67,
447
],
[
69,
447
],
[
71,
447
],
[
73,
447
],
[
75,
447
],
[
77,
447
],
[
79,
447
],
[
81,
447
],
[
83,
447
],
[
85,
447
],
[
87,
446
],
[
89,
444
],
[
91,
442
],
[
92,
440
],
[
93,
438
],
[
94,
436
],
[
95,
434
],
[
97,
432
],
[
99,
430
],
[
101,
428
],
[
101,
426
],
[
102,
424
],
[
102,
422
],
[
103,
420
],
[
103,
418
],
[
103,
416
],
[
103,
414
],
[
105,
412
],
[
107,
410
],
[
109,
408
],
[
111,
406
],
[
113,
404
],
[
114,
402
],
[
115,
400
],
[
115,
398
],
[
115,
396
],
[
115,
394
],
[
114,
392
],
[
114,
390
],
[
113,
388
],
[
113,
386
],
[
113,
384
],
[
112,
382
],
[
112,
380
],
[
111,
378
],
[
110,
376
],
[
109,
374
],
[
108,
372
],
[
106,
370
],
[
106,
368
],
[
104,
367
],
[
102,
366
],
[
100,
365
],
[
98,
364
],
[
96,
364
],
[
94,
364
],
[
92,
364
],
[
90,
364
],
[
88,
364
],
[
87,
364
]
]
}
],
"pois": [
{
"id": "poi_0",
"name": "",
"kind": "transit",
"center": [
57,
450
]
}
]
}
@@ -801,8 +801,575 @@
"area_cells": 55
}
],
"roads": [],
"cities": [],
"railroads": [],
"pois": []
"roads": [
{
"id": "road_0",
"name": "",
"kind": "commercial",
"path": [
[
163,
404
],
[
163,
405
],
[
163,
406
],
[
163,
408
],
[
162,
409
],
[
162,
410
],
[
162,
412
],
[
162,
413
],
[
162,
414
],
[
162,
416
],
[
161,
417
],
[
161,
418
],
[
161,
420
],
[
161,
421
],
[
161,
422
],
[
161,
424
],
[
161,
425
],
[
161,
426
],
[
161,
428
],
[
161,
429
],
[
161,
430
],
[
161,
432
],
[
161,
433
],
[
162,
434
],
[
162,
436
],
[
162,
437
],
[
162,
438
],
[
162,
440
],
[
162,
441
],
[
162,
442
],
[
162,
444
],
[
162,
445
],
[
162,
447
],
[
162,
448
],
[
162,
449
],
[
162,
451
],
[
162,
452
],
[
162,
453
],
[
162,
455
],
[
162,
456
],
[
162,
457
],
[
162,
459
],
[
162,
460
],
[
162,
461
],
[
163,
463
],
[
164,
464
],
[
164,
465
],
[
165,
467
],
[
165,
468
],
[
166,
469
],
[
167,
471
],
[
167,
472
],
[
168,
473
],
[
169,
475
],
[
169,
476
],
[
170,
477
],
[
170,
479
],
[
171,
480
],
[
171,
481
],
[
172,
483
],
[
173,
484
],
[
173,
485
],
[
174,
487
],
[
174,
488
],
[
175,
489
]
]
}
],
"cities": [
{
"id": "city_0",
"name": "",
"kind": "capital",
"center": [
163,
404
],
"population": 133330000
},
{
"id": "city_1",
"name": "",
"kind": "city",
"center": [
175,
489
],
"population": 66670000
}
],
"railroads": [
{
"id": "railroad_0",
"name": "",
"kind": "passenger_freight",
"path": [
[
163,
404
],
[
163,
405
],
[
163,
406
],
[
163,
408
],
[
162,
409
],
[
162,
410
],
[
162,
412
],
[
162,
413
],
[
162,
414
],
[
162,
416
],
[
161,
417
],
[
161,
418
],
[
161,
420
],
[
161,
421
],
[
161,
422
],
[
161,
424
],
[
161,
425
],
[
161,
426
],
[
161,
428
],
[
161,
429
],
[
161,
430
],
[
161,
432
],
[
161,
433
],
[
162,
434
],
[
162,
436
],
[
162,
437
],
[
162,
438
],
[
162,
440
],
[
162,
441
],
[
162,
442
],
[
162,
444
],
[
162,
445
],
[
162,
447
],
[
162,
448
],
[
162,
449
],
[
162,
451
],
[
162,
452
],
[
162,
453
],
[
162,
455
],
[
162,
456
],
[
162,
457
],
[
162,
459
],
[
162,
460
],
[
162,
461
],
[
163,
463
],
[
164,
464
],
[
164,
465
],
[
165,
467
],
[
165,
468
],
[
166,
469
],
[
167,
471
],
[
167,
472
],
[
168,
473
],
[
169,
475
],
[
169,
476
],
[
170,
477
],
[
170,
479
],
[
171,
480
],
[
171,
481
],
[
172,
483
],
[
173,
484
],
[
173,
485
],
[
174,
487
],
[
174,
488
],
[
175,
489
]
]
}
],
"pois": [
{
"id": "poi_0",
"name": "",
"kind": "transit",
"center": [
175,
489
]
}
]
}
@@ -582,7 +582,28 @@
}
],
"roads": [],
"cities": [],
"cities": [
{
"id": "city_0",
"name": "",
"kind": "capital",
"center": [
212,
412
],
"population": 50000
}
],
"railroads": [],
"pois": []
"pois": [
{
"id": "poi_0",
"name": "",
"kind": "transit",
"center": [
212,
412
]
}
]
}
@@ -586,7 +586,28 @@
}
],
"roads": [],
"cities": [],
"cities": [
{
"id": "city_0",
"name": "",
"kind": "capital",
"center": [
117,
3
],
"population": 280000
}
],
"railroads": [],
"pois": []
"pois": [
{
"id": "poi_0",
"name": "",
"kind": "transit",
"center": [
117,
3
]
}
]
}
@@ -355,7 +355,28 @@
}
],
"roads": [],
"cities": [],
"cities": [
{
"id": "city_0",
"name": "",
"kind": "capital",
"center": [
32,
452
],
"population": 860000
}
],
"railroads": [],
"pois": []
"pois": [
{
"id": "poi_0",
"name": "",
"kind": "transit",
"center": [
32,
452
]
}
]
}
@@ -1070,7 +1070,28 @@
}
],
"roads": [],
"cities": [],
"cities": [
{
"id": "city_0",
"name": "",
"kind": "capital",
"center": [
250,
23
],
"population": 12000
}
],
"railroads": [],
"pois": []
"pois": [
{
"id": "poi_0",
"name": "",
"kind": "transit",
"center": [
250,
23
]
}
]
}
@@ -286,7 +286,28 @@
}
],
"roads": [],
"cities": [],
"cities": [
{
"id": "city_0",
"name": "",
"kind": "capital",
"center": [
162,
505
],
"population": 350000000
}
],
"railroads": [],
"pois": []
"pois": [
{
"id": "poi_0",
"name": "",
"kind": "transit",
"center": [
162,
505
]
}
]
}
@@ -961,7 +961,28 @@
}
],
"roads": [],
"cities": [],
"cities": [
{
"id": "city_0",
"name": "",
"kind": "capital",
"center": [
100,
321
],
"population": 3000
}
],
"railroads": [],
"pois": []
"pois": [
{
"id": "poi_0",
"name": "",
"kind": "transit",
"center": [
100,
321
]
}
]
}
@@ -588,8 +588,575 @@
"area_cells": 154
}
],
"roads": [],
"cities": [],
"railroads": [],
"pois": []
"roads": [
{
"id": "road_0",
"name": "",
"kind": "commercial",
"path": [
[
250,
311
],
[
251,
310
],
[
252,
309
],
[
254,
307
],
[
254,
306
],
[
254,
304
],
[
254,
303
],
[
254,
301
],
[
254,
300
],
[
254,
299
],
[
254,
297
],
[
253,
296
],
[
253,
294
],
[
253,
293
],
[
253,
291
],
[
253,
290
],
[
253,
288
],
[
253,
287
],
[
252,
286
],
[
250,
284
],
[
249,
283
],
[
247,
281
],
[
247,
280
],
[
247,
278
],
[
247,
277
],
[
247,
276
],
[
247,
274
],
[
247,
273
],
[
247,
271
],
[
247,
270
],
[
247,
268
],
[
247,
267
],
[
247,
265
],
[
247,
264
],
[
247,
263
],
[
247,
261
],
[
247,
260
],
[
247,
258
],
[
247,
257
],
[
247,
255
],
[
247,
254
],
[
247,
253
],
[
247,
251
],
[
246,
250
],
[
246,
248
],
[
246,
247
],
[
246,
245
],
[
246,
244
],
[
246,
242
],
[
246,
241
],
[
246,
240
],
[
246,
238
],
[
246,
237
],
[
246,
235
],
[
246,
234
],
[
246,
232
],
[
246,
231
],
[
246,
230
],
[
246,
228
],
[
246,
227
],
[
246,
225
],
[
246,
224
],
[
246,
222
],
[
246,
221
],
[
246,
220
]
]
}
],
"cities": [
{
"id": "city_0",
"name": "",
"kind": "capital",
"center": [
250,
311
],
"population": 1333330000
},
{
"id": "city_1",
"name": "",
"kind": "city",
"center": [
246,
220
],
"population": 666670000
}
],
"railroads": [
{
"id": "railroad_0",
"name": "",
"kind": "passenger_freight",
"path": [
[
250,
311
],
[
251,
310
],
[
252,
309
],
[
254,
307
],
[
254,
306
],
[
254,
304
],
[
254,
303
],
[
254,
301
],
[
254,
300
],
[
254,
299
],
[
254,
297
],
[
253,
296
],
[
253,
294
],
[
253,
293
],
[
253,
291
],
[
253,
290
],
[
253,
288
],
[
253,
287
],
[
252,
286
],
[
250,
284
],
[
249,
283
],
[
247,
281
],
[
247,
280
],
[
247,
278
],
[
247,
277
],
[
247,
276
],
[
247,
274
],
[
247,
273
],
[
247,
271
],
[
247,
270
],
[
247,
268
],
[
247,
267
],
[
247,
265
],
[
247,
264
],
[
247,
263
],
[
247,
261
],
[
247,
260
],
[
247,
258
],
[
247,
257
],
[
247,
255
],
[
247,
254
],
[
247,
253
],
[
247,
251
],
[
246,
250
],
[
246,
248
],
[
246,
247
],
[
246,
245
],
[
246,
244
],
[
246,
242
],
[
246,
241
],
[
246,
240
],
[
246,
238
],
[
246,
237
],
[
246,
235
],
[
246,
234
],
[
246,
232
],
[
246,
231
],
[
246,
230
],
[
246,
228
],
[
246,
227
],
[
246,
225
],
[
246,
224
],
[
246,
222
],
[
246,
221
],
[
246,
220
]
]
}
],
"pois": [
{
"id": "poi_0",
"name": "",
"kind": "transit",
"center": [
250,
311
]
}
]
}
@@ -775,8 +775,531 @@
"area_cells": 52
}
],
"roads": [],
"cities": [],
"railroads": [],
"pois": []
"roads": [
{
"id": "road_0",
"name": "",
"kind": "commercial",
"path": [
[
212,
426
],
[
212,
425
],
[
212,
424
],
[
212,
423
],
[
212,
422
],
[
212,
421
],
[
212,
420
],
[
212,
419
],
[
212,
418
],
[
212,
417
],
[
212,
416
],
[
212,
415
],
[
212,
414
],
[
212,
413
],
[
212,
412
],
[
212,
411
],
[
212,
410
],
[
212,
409
],
[
212,
408
],
[
212,
407
],
[
212,
406
],
[
212,
405
],
[
212,
404
],
[
212,
403
],
[
212,
402
],
[
212,
401
],
[
212,
400
],
[
212,
399
],
[
213,
398
],
[
214,
397
],
[
215,
396
],
[
215,
395
],
[
215,
394
],
[
215,
393
],
[
215,
392
],
[
215,
391
],
[
215,
390
],
[
215,
389
],
[
215,
388
],
[
215,
387
],
[
215,
386
],
[
215,
385
],
[
215,
384
],
[
215,
383
],
[
216,
382
],
[
216,
381
],
[
216,
380
],
[
217,
379
],
[
217,
378
],
[
218,
377
],
[
218,
376
],
[
219,
375
],
[
219,
374
],
[
220,
373
],
[
220,
372
],
[
220,
371
],
[
221,
370
]
]
}
],
"cities": [
{
"id": "city_0",
"name": "",
"kind": "capital",
"center": [
212,
426
],
"population": 640000000
},
{
"id": "city_1",
"name": "",
"kind": "city",
"center": [
221,
370
],
"population": 320000000
},
{
"id": "city_2",
"name": "",
"kind": "city",
"center": [
7,
353
],
"population": 160000000
},
{
"id": "city_3",
"name": "",
"kind": "city",
"center": [
46,
156
],
"population": 80000000
}
],
"railroads": [
{
"id": "railroad_0",
"name": "",
"kind": "passenger_freight",
"path": [
[
212,
426
],
[
212,
425
],
[
212,
424
],
[
212,
423
],
[
212,
422
],
[
212,
421
],
[
212,
420
],
[
212,
419
],
[
212,
418
],
[
212,
417
],
[
212,
416
],
[
212,
415
],
[
212,
414
],
[
212,
413
],
[
212,
412
],
[
212,
411
],
[
212,
410
],
[
212,
409
],
[
212,
408
],
[
212,
407
],
[
212,
406
],
[
212,
405
],
[
212,
404
],
[
212,
403
],
[
212,
402
],
[
212,
401
],
[
212,
400
],
[
212,
399
],
[
213,
398
],
[
214,
397
],
[
215,
396
],
[
215,
395
],
[
215,
394
],
[
215,
393
],
[
215,
392
],
[
215,
391
],
[
215,
390
],
[
215,
389
],
[
215,
388
],
[
215,
387
],
[
215,
386
],
[
215,
385
],
[
215,
384
],
[
215,
383
],
[
216,
382
],
[
216,
381
],
[
216,
380
],
[
217,
379
],
[
217,
378
],
[
218,
377
],
[
218,
376
],
[
219,
375
],
[
219,
374
],
[
220,
373
],
[
220,
372
],
[
220,
371
],
[
221,
370
]
]
}
],
"pois": [
{
"id": "poi_0",
"name": "",
"kind": "transit",
"center": [
221,
370
]
}
]
}
@@ -694,7 +694,28 @@
}
],
"roads": [],
"cities": [],
"cities": [
{
"id": "city_0",
"name": "",
"kind": "capital",
"center": [
217,
394
],
"population": 700
}
],
"railroads": [],
"pois": []
"pois": [
{
"id": "poi_0",
"name": "",
"kind": "transit",
"center": [
217,
394
]
}
]
}
@@ -906,7 +906,28 @@
}
],
"roads": [],
"cities": [],
"cities": [
{
"id": "city_0",
"name": "",
"kind": "capital",
"center": [
38,
135
],
"population": 120000000
}
],
"railroads": [],
"pois": []
"pois": [
{
"id": "poi_0",
"name": "",
"kind": "transit",
"center": [
38,
135
]
}
]
}
@@ -910,8 +910,575 @@
"area_cells": 39
}
],
"roads": [],
"cities": [],
"railroads": [],
"pois": []
"roads": [
{
"id": "road_0",
"name": "",
"kind": "commercial",
"path": [
[
123,
509
],
[
123,
508
],
[
123,
506
],
[
123,
504
],
[
125,
503
],
[
126,
502
],
[
128,
500
],
[
130,
498
],
[
132,
498
],
[
134,
497
],
[
135,
496
],
[
137,
496
],
[
139,
495
],
[
141,
494
],
[
142,
493
],
[
144,
491
],
[
146,
490
],
[
148,
490
],
[
150,
490
],
[
151,
490
],
[
153,
490
],
[
155,
490
],
[
157,
490
],
[
158,
490
],
[
160,
490
],
[
162,
490
],
[
164,
490
],
[
166,
490
],
[
167,
490
],
[
169,
490
],
[
171,
490
],
[
173,
490
],
[
175,
490
],
[
176,
490
],
[
178,
490
],
[
180,
490
],
[
182,
490
],
[
183,
490
],
[
185,
490
],
[
187,
491
],
[
189,
493
],
[
191,
492
],
[
192,
491
],
[
194,
489
],
[
196,
487
],
[
198,
485
],
[
199,
484
],
[
201,
482
],
[
203,
480
],
[
205,
478
],
[
207,
476
],
[
208,
475
],
[
210,
473
],
[
212,
471
],
[
214,
469
],
[
215,
468
],
[
217,
466
],
[
219,
464
],
[
221,
462
],
[
222,
460
],
[
223,
459
],
[
224,
457
],
[
224,
455
],
[
225,
453
],
[
226,
452
]
]
}
],
"cities": [
{
"id": "city_0",
"name": "",
"kind": "capital",
"center": [
123,
509
],
"population": 1866670000
},
{
"id": "city_1",
"name": "",
"kind": "city",
"center": [
226,
452
],
"population": 933330000
}
],
"railroads": [
{
"id": "railroad_0",
"name": "",
"kind": "passenger_freight",
"path": [
[
123,
509
],
[
123,
508
],
[
123,
506
],
[
123,
504
],
[
125,
503
],
[
126,
502
],
[
128,
500
],
[
130,
498
],
[
132,
498
],
[
134,
497
],
[
135,
496
],
[
137,
496
],
[
139,
495
],
[
141,
494
],
[
142,
493
],
[
144,
491
],
[
146,
490
],
[
148,
490
],
[
150,
490
],
[
151,
490
],
[
153,
490
],
[
155,
490
],
[
157,
490
],
[
158,
490
],
[
160,
490
],
[
162,
490
],
[
164,
490
],
[
166,
490
],
[
167,
490
],
[
169,
490
],
[
171,
490
],
[
173,
490
],
[
175,
490
],
[
176,
490
],
[
178,
490
],
[
180,
490
],
[
182,
490
],
[
183,
490
],
[
185,
490
],
[
187,
491
],
[
189,
493
],
[
191,
492
],
[
192,
491
],
[
194,
489
],
[
196,
487
],
[
198,
485
],
[
199,
484
],
[
201,
482
],
[
203,
480
],
[
205,
478
],
[
207,
476
],
[
208,
475
],
[
210,
473
],
[
212,
471
],
[
214,
469
],
[
215,
468
],
[
217,
466
],
[
219,
464
],
[
221,
462
],
[
222,
460
],
[
223,
459
],
[
224,
457
],
[
224,
455
],
[
225,
453
],
[
226,
452
]
]
}
],
"pois": [
{
"id": "poi_0",
"name": "",
"kind": "transit",
"center": [
123,
509
]
}
]
}
@@ -558,8 +558,575 @@
"area_cells": 26
}
],
"roads": [],
"cities": [],
"railroads": [],
"pois": []
"roads": [
{
"id": "road_0",
"name": "",
"kind": "commercial",
"path": [
[
128,
198
],
[
127,
199
],
[
125,
201
],
[
124,
202
],
[
122,
204
],
[
121,
205
],
[
120,
207
],
[
120,
208
],
[
120,
210
],
[
120,
212
],
[
120,
213
],
[
120,
215
],
[
120,
216
],
[
120,
218
],
[
120,
219
],
[
120,
221
],
[
120,
223
],
[
120,
224
],
[
120,
226
],
[
120,
227
],
[
120,
229
],
[
120,
230
],
[
118,
232
],
[
118,
233
],
[
118,
235
],
[
118,
237
],
[
118,
238
],
[
118,
240
],
[
118,
241
],
[
118,
243
],
[
118,
244
],
[
118,
246
],
[
118,
248
],
[
118,
249
],
[
118,
251
],
[
118,
252
],
[
118,
254
],
[
118,
255
],
[
118,
257
],
[
118,
258
],
[
118,
260
],
[
118,
262
],
[
118,
263
],
[
118,
265
],
[
118,
266
],
[
118,
268
],
[
118,
269
],
[
118,
271
],
[
118,
273
],
[
118,
274
],
[
118,
276
],
[
118,
277
],
[
118,
279
],
[
118,
280
],
[
118,
282
],
[
118,
283
],
[
118,
285
],
[
118,
287
],
[
119,
288
],
[
120,
290
],
[
120,
291
],
[
121,
293
],
[
122,
294
],
[
122,
296
],
[
123,
297
]
]
}
],
"cities": [
{
"id": "city_0",
"name": "",
"kind": "capital",
"center": [
128,
198
],
"population": 16670000
},
{
"id": "city_1",
"name": "",
"kind": "city",
"center": [
123,
297
],
"population": 8330000
}
],
"railroads": [
{
"id": "railroad_0",
"name": "",
"kind": "passenger_freight",
"path": [
[
128,
198
],
[
127,
199
],
[
125,
201
],
[
124,
202
],
[
122,
204
],
[
121,
205
],
[
120,
207
],
[
120,
208
],
[
120,
210
],
[
120,
212
],
[
120,
213
],
[
120,
215
],
[
120,
216
],
[
120,
218
],
[
120,
219
],
[
120,
221
],
[
120,
223
],
[
120,
224
],
[
120,
226
],
[
120,
227
],
[
120,
229
],
[
120,
230
],
[
118,
232
],
[
118,
233
],
[
118,
235
],
[
118,
237
],
[
118,
238
],
[
118,
240
],
[
118,
241
],
[
118,
243
],
[
118,
244
],
[
118,
246
],
[
118,
248
],
[
118,
249
],
[
118,
251
],
[
118,
252
],
[
118,
254
],
[
118,
255
],
[
118,
257
],
[
118,
258
],
[
118,
260
],
[
118,
262
],
[
118,
263
],
[
118,
265
],
[
118,
266
],
[
118,
268
],
[
118,
269
],
[
118,
271
],
[
118,
273
],
[
118,
274
],
[
118,
276
],
[
118,
277
],
[
118,
279
],
[
118,
280
],
[
118,
282
],
[
118,
283
],
[
118,
285
],
[
118,
287
],
[
119,
288
],
[
120,
290
],
[
120,
291
],
[
121,
293
],
[
122,
294
],
[
122,
296
],
[
123,
297
]
]
}
],
"pois": [
{
"id": "poi_0",
"name": "",
"kind": "transit",
"center": [
128,
198
]
}
]
}
@@ -655,7 +655,28 @@
}
],
"roads": [],
"cities": [],
"cities": [
{
"id": "city_0",
"name": "",
"kind": "capital",
"center": [
250,
311
],
"population": 6000
}
],
"railroads": [],
"pois": []
"pois": [
{
"id": "poi_0",
"name": "",
"kind": "transit",
"center": [
250,
311
]
}
]
}
@@ -524,7 +524,28 @@
}
],
"roads": [],
"cities": [],
"cities": [
{
"id": "city_0",
"name": "",
"kind": "capital",
"center": [
95,
97
],
"population": 520000
}
],
"railroads": [],
"pois": []
"pois": [
{
"id": "poi_0",
"name": "",
"kind": "transit",
"center": [
95,
97
]
}
]
}
@@ -591,7 +591,28 @@
}
],
"roads": [],
"cities": [],
"cities": [
{
"id": "city_0",
"name": "",
"kind": "capital",
"center": [
88,
131
],
"population": 420000
}
],
"railroads": [],
"pois": []
"pois": [
{
"id": "poi_0",
"name": "",
"kind": "transit",
"center": [
88,
131
]
}
]
}
@@ -486,7 +486,28 @@
}
],
"roads": [],
"cities": [],
"cities": [
{
"id": "city_0",
"name": "",
"kind": "capital",
"center": [
250,
506
],
"population": 20000
}
],
"railroads": [],
"pois": []
"pois": [
{
"id": "poi_0",
"name": "",
"kind": "transit",
"center": [
250,
506
]
}
]
}
@@ -1096,7 +1096,28 @@
}
],
"roads": [],
"cities": [],
"cities": [
{
"id": "city_0",
"name": "",
"kind": "capital",
"center": [
79,
386
],
"population": 80000000
}
],
"railroads": [],
"pois": []
"pois": [
{
"id": "poi_0",
"name": "",
"kind": "transit",
"center": [
79,
386
]
}
]
}
@@ -836,7 +836,28 @@
}
],
"roads": [],
"cities": [],
"cities": [
{
"id": "city_0",
"name": "",
"kind": "capital",
"center": [
156,
141
],
"population": 18000
}
],
"railroads": [],
"pois": []
"pois": [
{
"id": "poi_0",
"name": "",
"kind": "transit",
"center": [
156,
141
]
}
]
}
@@ -691,7 +691,28 @@
}
],
"roads": [],
"cities": [],
"cities": [
{
"id": "city_0",
"name": "",
"kind": "capital",
"center": [
5,
179
],
"population": 30000
}
],
"railroads": [],
"pois": []
"pois": [
{
"id": "poi_0",
"name": "",
"kind": "transit",
"center": [
5,
179
]
}
]
}
@@ -668,7 +668,28 @@
}
],
"roads": [],
"cities": [],
"cities": [
{
"id": "city_0",
"name": "",
"kind": "capital",
"center": [
59,
453
],
"population": 85000000
}
],
"railroads": [],
"pois": []
"pois": [
{
"id": "poi_0",
"name": "",
"kind": "transit",
"center": [
59,
453
]
}
]
}
@@ -547,7 +547,28 @@
}
],
"roads": [],
"cities": [],
"cities": [
{
"id": "city_0",
"name": "",
"kind": "capital",
"center": [
98,
94
],
"population": 8000000
}
],
"railroads": [],
"pois": []
"pois": [
{
"id": "poi_0",
"name": "",
"kind": "transit",
"center": [
98,
94
]
}
]
}
@@ -1301,7 +1301,28 @@
}
],
"roads": [],
"cities": [],
"cities": [
{
"id": "city_0",
"name": "",
"kind": "capital",
"center": [
73,
498
],
"population": 25000000
}
],
"railroads": [],
"pois": []
"pois": [
{
"id": "poi_0",
"name": "",
"kind": "transit",
"center": [
73,
498
]
}
]
}
@@ -891,7 +891,28 @@
}
],
"roads": [],
"cities": [],
"cities": [
{
"id": "city_0",
"name": "",
"kind": "capital",
"center": [
5,
55
],
"population": 58000
}
],
"railroads": [],
"pois": []
"pois": [
{
"id": "poi_0",
"name": "",
"kind": "transit",
"center": [
5,
55
]
}
]
}
@@ -768,7 +768,28 @@
}
],
"roads": [],
"cities": [],
"cities": [
{
"id": "city_0",
"name": "",
"kind": "capital",
"center": [
5,
430
],
"population": 15000
}
],
"railroads": [],
"pois": []
"pois": [
{
"id": "poi_0",
"name": "",
"kind": "transit",
"center": [
5,
430
]
}
]
}
@@ -1050,7 +1050,28 @@
}
],
"roads": [],
"cities": [],
"cities": [
{
"id": "city_0",
"name": "",
"kind": "capital",
"center": [
208,
127
],
"population": 420000
}
],
"railroads": [],
"pois": []
"pois": [
{
"id": "poi_0",
"name": "",
"kind": "transit",
"center": [
208,
127
]
}
]
}
@@ -697,7 +697,28 @@
}
],
"roads": [],
"cities": [],
"cities": [
{
"id": "city_0",
"name": "",
"kind": "capital",
"center": [
244,
91
],
"population": 800
}
],
"railroads": [],
"pois": []
"pois": [
{
"id": "poi_0",
"name": "",
"kind": "transit",
"center": [
244,
91
]
}
]
}
@@ -632,7 +632,28 @@
}
],
"roads": [],
"cities": [],
"cities": [
{
"id": "city_0",
"name": "",
"kind": "capital",
"center": [
22,
8
],
"population": 40000
}
],
"railroads": [],
"pois": []
"pois": [
{
"id": "poi_0",
"name": "",
"kind": "transit",
"center": [
22,
8
]
}
]
}
@@ -923,7 +923,28 @@
}
],
"roads": [],
"cities": [],
"cities": [
{
"id": "city_0",
"name": "",
"kind": "capital",
"center": [
218,
504
],
"population": 10000
}
],
"railroads": [],
"pois": []
"pois": [
{
"id": "poi_0",
"name": "",
"kind": "transit",
"center": [
218,
504
]
}
]
}
@@ -234,7 +234,28 @@
}
],
"roads": [],
"cities": [],
"cities": [
{
"id": "city_0",
"name": "",
"kind": "capital",
"center": [
88,
141
],
"population": 35000
}
],
"railroads": [],
"pois": []
"pois": [
{
"id": "poi_0",
"name": "",
"kind": "transit",
"center": [
88,
141
]
}
]
}
@@ -1909,7 +1909,28 @@
}
],
"roads": [],
"cities": [],
"cities": [
{
"id": "city_0",
"name": "",
"kind": "capital",
"center": [
54,
241
],
"population": 8000
}
],
"railroads": [],
"pois": []
"pois": [
{
"id": "poi_0",
"name": "",
"kind": "transit",
"center": [
54,
241
]
}
]
}
@@ -1328,7 +1328,28 @@
}
],
"roads": [],
"cities": [],
"cities": [
{
"id": "city_0",
"name": "",
"kind": "capital",
"center": [
65,
372
],
"population": 40000000
}
],
"railroads": [],
"pois": []
"pois": [
{
"id": "poi_0",
"name": "",
"kind": "transit",
"center": [
65,
372
]
}
]
}
@@ -890,7 +890,28 @@
}
],
"roads": [],
"cities": [],
"cities": [
{
"id": "city_0",
"name": "",
"kind": "capital",
"center": [
208,
46
],
"population": 3800000
}
],
"railroads": [],
"pois": []
"pois": [
{
"id": "poi_0",
"name": "",
"kind": "transit",
"center": [
208,
46
]
}
]
}
@@ -988,7 +988,28 @@
}
],
"roads": [],
"cities": [],
"cities": [
{
"id": "city_0",
"name": "",
"kind": "capital",
"center": [
142,
287
],
"population": 35000000
}
],
"railroads": [],
"pois": []
"pois": [
{
"id": "poi_0",
"name": "",
"kind": "transit",
"center": [
142,
287
]
}
]
}
@@ -781,7 +781,28 @@
}
],
"roads": [],
"cities": [],
"cities": [
{
"id": "city_0",
"name": "",
"kind": "capital",
"center": [
45,
128
],
"population": 12000
}
],
"railroads": [],
"pois": []
"pois": [
{
"id": "poi_0",
"name": "",
"kind": "transit",
"center": [
45,
128
]
}
]
}
@@ -905,7 +905,28 @@
}
],
"roads": [],
"cities": [],
"cities": [
{
"id": "city_0",
"name": "",
"kind": "capital",
"center": [
130,
67
],
"population": 60000000
}
],
"railroads": [],
"pois": []
"pois": [
{
"id": "poi_0",
"name": "",
"kind": "transit",
"center": [
130,
67
]
}
]
}
@@ -482,8 +482,575 @@
"area_cells": 46
}
],
"roads": [],
"cities": [],
"railroads": [],
"pois": []
"roads": [
{
"id": "road_0",
"name": "",
"kind": "commercial",
"path": [
[
123,
369
],
[
123,
368
],
[
123,
367
],
[
124,
365
],
[
124,
364
],
[
124,
363
],
[
125,
361
],
[
125,
360
],
[
125,
358
],
[
125,
357
],
[
125,
356
],
[
125,
354
],
[
125,
353
],
[
125,
351
],
[
125,
350
],
[
125,
349
],
[
125,
347
],
[
125,
346
],
[
124,
344
],
[
123,
343
],
[
122,
342
],
[
120,
340
],
[
120,
339
],
[
120,
338
],
[
120,
336
],
[
120,
335
],
[
120,
333
],
[
120,
332
],
[
120,
331
],
[
120,
329
],
[
120,
328
],
[
120,
326
],
[
120,
325
],
[
120,
324
],
[
120,
322
],
[
120,
321
],
[
120,
319
],
[
120,
318
],
[
120,
317
],
[
120,
315
],
[
120,
314
],
[
119,
312
],
[
119,
311
],
[
118,
310
],
[
117,
308
],
[
117,
307
],
[
116,
306
],
[
115,
304
],
[
115,
303
],
[
114,
301
],
[
114,
300
],
[
113,
299
],
[
112,
297
],
[
112,
296
],
[
111,
294
],
[
110,
293
],
[
110,
292
],
[
109,
290
],
[
109,
289
],
[
108,
287
],
[
107,
286
],
[
107,
285
],
[
106,
283
],
[
106,
282
],
[
105,
281
]
]
}
],
"cities": [
{
"id": "city_0",
"name": "",
"kind": "capital",
"center": [
123,
369
],
"population": 3333330000
},
{
"id": "city_1",
"name": "",
"kind": "city",
"center": [
105,
281
],
"population": 1666670000
}
],
"railroads": [
{
"id": "railroad_0",
"name": "",
"kind": "passenger_freight",
"path": [
[
123,
369
],
[
123,
368
],
[
123,
367
],
[
124,
365
],
[
124,
364
],
[
124,
363
],
[
125,
361
],
[
125,
360
],
[
125,
358
],
[
125,
357
],
[
125,
356
],
[
125,
354
],
[
125,
353
],
[
125,
351
],
[
125,
350
],
[
125,
349
],
[
125,
347
],
[
125,
346
],
[
124,
344
],
[
123,
343
],
[
122,
342
],
[
120,
340
],
[
120,
339
],
[
120,
338
],
[
120,
336
],
[
120,
335
],
[
120,
333
],
[
120,
332
],
[
120,
331
],
[
120,
329
],
[
120,
328
],
[
120,
326
],
[
120,
325
],
[
120,
324
],
[
120,
322
],
[
120,
321
],
[
120,
319
],
[
120,
318
],
[
120,
317
],
[
120,
315
],
[
120,
314
],
[
119,
312
],
[
119,
311
],
[
118,
310
],
[
117,
308
],
[
117,
307
],
[
116,
306
],
[
115,
304
],
[
115,
303
],
[
114,
301
],
[
114,
300
],
[
113,
299
],
[
112,
297
],
[
112,
296
],
[
111,
294
],
[
110,
293
],
[
110,
292
],
[
109,
290
],
[
109,
289
],
[
108,
287
],
[
107,
286
],
[
107,
285
],
[
106,
283
],
[
106,
282
],
[
105,
281
]
]
}
],
"pois": [
{
"id": "poi_0",
"name": "",
"kind": "transit",
"center": [
123,
369
]
}
]
}
@@ -688,7 +688,28 @@
}
],
"roads": [],
"cities": [],
"cities": [
{
"id": "city_0",
"name": "",
"kind": "capital",
"center": [
103,
237
],
"population": 290000
}
],
"railroads": [],
"pois": []
"pois": [
{
"id": "poi_0",
"name": "",
"kind": "transit",
"center": [
103,
237
]
}
]
}
@@ -1057,8 +1057,575 @@
"area_cells": 139
}
],
"roads": [],
"cities": [],
"railroads": [],
"pois": []
"roads": [
{
"id": "road_0",
"name": "",
"kind": "commercial",
"path": [
[
249,
386
],
[
248,
386
],
[
246,
386
],
[
245,
386
],
[
243,
386
],
[
241,
386
],
[
240,
386
],
[
238,
386
],
[
237,
386
],
[
235,
386
],
[
233,
386
],
[
232,
386
],
[
230,
386
],
[
229,
385
],
[
227,
383
],
[
225,
381
],
[
224,
380
],
[
222,
378
],
[
221,
377
],
[
219,
376
],
[
217,
376
],
[
216,
376
],
[
214,
376
],
[
212,
376
],
[
211,
376
],
[
209,
376
],
[
208,
376
],
[
206,
376
],
[
204,
376
],
[
203,
376
],
[
201,
376
],
[
200,
376
],
[
198,
376
],
[
196,
376
],
[
195,
376
],
[
193,
376
],
[
192,
376
],
[
190,
376
],
[
188,
375
],
[
187,
375
],
[
185,
375
],
[
184,
375
],
[
182,
375
],
[
180,
375
],
[
179,
375
],
[
177,
375
],
[
175,
375
],
[
174,
375
],
[
172,
375
],
[
171,
375
],
[
169,
375
],
[
167,
375
],
[
166,
375
],
[
164,
375
],
[
163,
375
],
[
161,
375
],
[
159,
375
],
[
158,
375
],
[
156,
375
],
[
155,
375
],
[
153,
375
],
[
151,
375
],
[
150,
375
],
[
148,
375
],
[
147,
374
]
]
}
],
"cities": [
{
"id": "city_0",
"name": "",
"kind": "capital",
"center": [
249,
386
],
"population": 533330000
},
{
"id": "city_1",
"name": "",
"kind": "city",
"center": [
147,
374
],
"population": 266670000
}
],
"railroads": [
{
"id": "railroad_0",
"name": "",
"kind": "passenger_freight",
"path": [
[
249,
386
],
[
248,
386
],
[
246,
386
],
[
245,
386
],
[
243,
386
],
[
241,
386
],
[
240,
386
],
[
238,
386
],
[
237,
386
],
[
235,
386
],
[
233,
386
],
[
232,
386
],
[
230,
386
],
[
229,
385
],
[
227,
383
],
[
225,
381
],
[
224,
380
],
[
222,
378
],
[
221,
377
],
[
219,
376
],
[
217,
376
],
[
216,
376
],
[
214,
376
],
[
212,
376
],
[
211,
376
],
[
209,
376
],
[
208,
376
],
[
206,
376
],
[
204,
376
],
[
203,
376
],
[
201,
376
],
[
200,
376
],
[
198,
376
],
[
196,
376
],
[
195,
376
],
[
193,
376
],
[
192,
376
],
[
190,
376
],
[
188,
375
],
[
187,
375
],
[
185,
375
],
[
184,
375
],
[
182,
375
],
[
180,
375
],
[
179,
375
],
[
177,
375
],
[
175,
375
],
[
174,
375
],
[
172,
375
],
[
171,
375
],
[
169,
375
],
[
167,
375
],
[
166,
375
],
[
164,
375
],
[
163,
375
],
[
161,
375
],
[
159,
375
],
[
158,
375
],
[
156,
375
],
[
155,
375
],
[
153,
375
],
[
151,
375
],
[
150,
375
],
[
148,
375
],
[
147,
374
]
]
}
],
"pois": [
{
"id": "poi_0",
"name": "",
"kind": "transit",
"center": [
147,
374
]
}
]
}
@@ -1027,7 +1027,28 @@
}
],
"roads": [],
"cities": [],
"cities": [
{
"id": "city_0",
"name": "",
"kind": "capital",
"center": [
27,
246
],
"population": 120000
}
],
"railroads": [],
"pois": []
"pois": [
{
"id": "poi_0",
"name": "",
"kind": "transit",
"center": [
27,
246
]
}
]
}
@@ -562,7 +562,28 @@
}
],
"roads": [],
"cities": [],
"cities": [
{
"id": "city_0",
"name": "",
"kind": "capital",
"center": [
206,
103
],
"population": 800
}
],
"railroads": [],
"pois": []
"pois": [
{
"id": "poi_0",
"name": "",
"kind": "transit",
"center": [
206,
103
]
}
]
}
@@ -309,7 +309,28 @@
}
],
"roads": [],
"cities": [],
"cities": [
{
"id": "city_0",
"name": "",
"kind": "capital",
"center": [
88,
54
],
"population": 1200
}
],
"railroads": [],
"pois": []
"pois": [
{
"id": "poi_0",
"name": "",
"kind": "transit",
"center": [
88,
54
]
}
]
}
@@ -661,7 +661,28 @@
}
],
"roads": [],
"cities": [],
"cities": [
{
"id": "city_0",
"name": "",
"kind": "capital",
"center": [
114,
276
],
"population": 15000
}
],
"railroads": [],
"pois": []
"pois": [
{
"id": "poi_0",
"name": "",
"kind": "transit",
"center": [
114,
276
]
}
]
}
@@ -539,7 +539,28 @@
}
],
"roads": [],
"cities": [],
"cities": [
{
"id": "city_0",
"name": "",
"kind": "capital",
"center": [
115,
374
],
"population": 3200000
}
],
"railroads": [],
"pois": []
"pois": [
{
"id": "poi_0",
"name": "",
"kind": "transit",
"center": [
115,
374
]
}
]
}
@@ -453,7 +453,28 @@
}
],
"roads": [],
"cities": [],
"cities": [
{
"id": "city_0",
"name": "",
"kind": "capital",
"center": [
95,
124
],
"population": 3000
}
],
"railroads": [],
"pois": []
"pois": [
{
"id": "poi_0",
"name": "",
"kind": "transit",
"center": [
95,
124
]
}
]
}
@@ -456,7 +456,28 @@
}
],
"roads": [],
"cities": [],
"cities": [
{
"id": "city_0",
"name": "",
"kind": "capital",
"center": [
250,
92
],
"population": 42000
}
],
"railroads": [],
"pois": []
"pois": [
{
"id": "poi_0",
"name": "",
"kind": "transit",
"center": [
250,
92
]
}
]
}
@@ -603,7 +603,28 @@
}
],
"roads": [],
"cities": [],
"cities": [
{
"id": "city_0",
"name": "",
"kind": "capital",
"center": [
247,
68
],
"population": 2000
}
],
"railroads": [],
"pois": []
"pois": [
{
"id": "poi_0",
"name": "",
"kind": "transit",
"center": [
247,
68
]
}
]
}
@@ -555,7 +555,28 @@
}
],
"roads": [],
"cities": [],
"cities": [
{
"id": "city_0",
"name": "",
"kind": "capital",
"center": [
150,
509
],
"population": 72000
}
],
"railroads": [],
"pois": []
"pois": [
{
"id": "poi_0",
"name": "",
"kind": "transit",
"center": [
150,
509
]
}
]
}

Some files were not shown because too many files have changed in this diff Show More