diff --git a/client/data/star_map_data.json b/client/data/star_map_data.json new file mode 100644 index 000000000..42d8268fb --- /dev/null +++ b/client/data/star_map_data.json @@ -0,0 +1,4363 @@ +{ + "_meta": { + "generated_from": "docs/design/star-map.json + server/data/systems.db", + "system_count": 301, + "edge_count": 335, + "note": "Client-side star map data. Regenerate with: tooling/generate-star-map-data.py" + }, + "nodes": [ + { + "system_id": "GJ 71", + "proper_name": "Tau Ceti", + "geographic_sector": "core", + "geographic_band": "core", + "gate_topology": "hub", + "aperture_count": 5, + "gate_connections": 5, + "hop_distance": 0, + "is_gateway": true + }, + { + "system_id": "GJ 0", + "proper_name": "Sol", + "geographic_sector": "core", + "geographic_band": "core", + "gate_topology": "spur_end", + "aperture_count": 1, + "gate_connections": 1, + "hop_distance": 1 + }, + { + "system_id": "GJ 144", + "proper_name": "Ran", + "geographic_sector": "core", + "geographic_band": "core", + "gate_topology": "junction", + "aperture_count": 3, + "gate_connections": 3, + "hop_distance": 1 + }, + { + "system_id": "GJ 244A", + "proper_name": "Sirius", + "geographic_sector": "core", + "geographic_band": "core", + "gate_topology": "hub", + "aperture_count": 5, + "gate_connections": 5, + "hop_distance": 1 + }, + { + "system_id": "GJ 559B", + "proper_name": "ACB", + "geographic_sector": "core", + "geographic_band": "core", + "gate_topology": "hub", + "aperture_count": 5, + "gate_connections": 5, + "hop_distance": 1 + }, + { + "system_id": "GJ 725B", + "proper_name": "Struve", + "geographic_sector": "core", + "geographic_band": "core", + "gate_topology": "junction", + "aperture_count": 3, + "gate_connections": 3, + "hop_distance": 1 + }, + { + "system_id": "GJ 1221", + "proper_name": "GJ 1221", + "geographic_sector": "east_reach", + "geographic_band": "inner", + "gate_topology": "dead_end", + "aperture_count": 1, + "gate_connections": 1, + "hop_distance": 2 + }, + { + "system_id": "GJ 166A", + "proper_name": "Keid", + "geographic_sector": "core", + "geographic_band": "core", + "gate_topology": "dead_end", + "aperture_count": 1, + "gate_connections": 1, + "hop_distance": 2 + }, + { + "system_id": "GJ 251", + "proper_name": "Renaissance", + "geographic_sector": "core", + "geographic_band": "core", + "gate_topology": "hub", + "aperture_count": 5, + "gate_connections": 5, + "hop_distance": 2 + }, + { + "system_id": "GJ 280A", + "proper_name": "Parallax", + "geographic_sector": "core", + "geographic_band": "core", + "gate_topology": "junction", + "aperture_count": 3, + "gate_connections": 3, + "hop_distance": 2 + }, + { + "system_id": "GJ 3522", + "proper_name": "Sindri", + "geographic_sector": "core", + "geographic_band": "core", + "gate_topology": "junction", + "aperture_count": 3, + "gate_connections": 3, + "hop_distance": 2 + }, + { + "system_id": "GJ 380", + "proper_name": "Groombridge", + "geographic_sector": "core", + "geographic_band": "core", + "gate_topology": "loop_member", + "aperture_count": 2, + "gate_connections": 2, + "hop_distance": 2 + }, + { + "system_id": "GJ 3877", + "proper_name": "Tessera", + "geographic_sector": "core", + "geographic_band": "core", + "gate_topology": "hub", + "aperture_count": 5, + "gate_connections": 5, + "hop_distance": 2 + }, + { + "system_id": "GJ 551", + "proper_name": "Proxima", + "geographic_sector": "core", + "geographic_band": "core", + "gate_topology": "junction", + "aperture_count": 4, + "gate_connections": 4, + "hop_distance": 2 + }, + { + "system_id": "GJ 674", + "proper_name": "Meridian", + "geographic_sector": "core", + "geographic_band": "core", + "gate_topology": "loop_member", + "aperture_count": 2, + "gate_connections": 2, + "hop_distance": 2 + }, + { + "system_id": "GJ 699", + "proper_name": "Barnard's Star", + "geographic_sector": "core", + "geographic_band": "core", + "gate_topology": "loop_member", + "aperture_count": 2, + "gate_connections": 2, + "hop_distance": 2 + }, + { + "system_id": "GJ 702B", + "proper_name": "Prometheus", + "geographic_sector": "core", + "geographic_band": "core", + "gate_topology": "dead_end", + "aperture_count": 1, + "gate_connections": 1, + "hop_distance": 2 + }, + { + "system_id": "GJ 820B", + "proper_name": "Cygni B", + "geographic_sector": "core", + "geographic_band": "core", + "gate_topology": "hub", + "aperture_count": 5, + "gate_connections": 5, + "hop_distance": 2 + }, + { + "system_id": "GJ 845", + "proper_name": "Cairn", + "geographic_sector": "north_reach", + "geographic_band": "inner", + "gate_topology": "hub", + "aperture_count": 7, + "gate_connections": 7, + "hop_distance": 2 + }, + { + "system_id": "GJ 1156", + "proper_name": "Travessia", + "geographic_sector": "south_reach", + "geographic_band": "inner", + "gate_topology": "junction", + "aperture_count": 4, + "gate_connections": 4, + "hop_distance": 3 + }, + { + "system_id": "GJ 1286", + "proper_name": "Kensho", + "geographic_sector": "core", + "geographic_band": "core", + "gate_topology": "junction", + "aperture_count": 4, + "gate_connections": 4, + "hop_distance": 3 + }, + { + "system_id": "GJ 139", + "proper_name": "Okafor Station", + "geographic_sector": "north_reach", + "geographic_band": "inner", + "gate_topology": "junction", + "aperture_count": 3, + "gate_connections": 3, + "hop_distance": 3 + }, + { + "system_id": "GJ 15A", + "proper_name": "Lu Ban", + "geographic_sector": "core", + "geographic_band": "core", + "gate_topology": "dead_end", + "aperture_count": 1, + "gate_connections": 1, + "hop_distance": 3 + }, + { + "system_id": "GJ 338B", + "proper_name": "Arbour", + "geographic_sector": "core", + "geographic_band": "core", + "gate_topology": "loop_member", + "aperture_count": 2, + "gate_connections": 2, + "hop_distance": 3 + }, + { + "system_id": "GJ 34A", + "proper_name": "Tanegashima", + "geographic_sector": "north_reach", + "geographic_band": "inner", + "gate_topology": "junction", + "aperture_count": 3, + "gate_connections": 3, + "hop_distance": 3 + }, + { + "system_id": "GJ 34B", + "proper_name": "Yongjin", + "geographic_sector": "east_reach", + "geographic_band": "inner", + "gate_topology": "junction", + "aperture_count": 4, + "gate_connections": 4, + "hop_distance": 3 + }, + { + "system_id": "GJ 406", + "proper_name": "Wolf 359", + "geographic_sector": "north_reach", + "geographic_band": "inner", + "gate_topology": "hub", + "aperture_count": 5, + "gate_connections": 5, + "hop_distance": 3 + }, + { + "system_id": "GJ 411", + "proper_name": "Lalande", + "geographic_sector": "south_reach", + "geographic_band": "inner", + "gate_topology": "hub", + "aperture_count": 5, + "gate_connections": 5, + "hop_distance": 3 + }, + { + "system_id": "GJ 4274", + "proper_name": "Suzhou", + "geographic_sector": "east_reach", + "geographic_band": "outer", + "gate_topology": "dead_end", + "aperture_count": 1, + "gate_connections": 1, + "hop_distance": 3 + }, + { + "system_id": "GJ 447", + "proper_name": "Ross 128", + "geographic_sector": "core", + "geographic_band": "core", + "gate_topology": "loop_member", + "aperture_count": 2, + "gate_connections": 2, + "hop_distance": 3 + }, + { + "system_id": "GJ 570A", + "proper_name": "Bastion", + "geographic_sector": "core", + "geographic_band": "core", + "gate_topology": "hub", + "aperture_count": 5, + "gate_connections": 5, + "hop_distance": 3 + }, + { + "system_id": "GJ 661A", + "proper_name": "Crown's Hollow", + "geographic_sector": "north_reach", + "geographic_band": "inner", + "gate_topology": "junction", + "aperture_count": 4, + "gate_connections": 4, + "hop_distance": 3 + }, + { + "system_id": "GJ 663A", + "proper_name": "Johannsen's Star", + "geographic_sector": "north_reach", + "geographic_band": "inner", + "gate_topology": "junction", + "aperture_count": 3, + "gate_connections": 3, + "hop_distance": 3 + }, + { + "system_id": "GJ 664", + "proper_name": "Caldwell Point", + "geographic_sector": "west_reach", + "geographic_band": "inner", + "gate_topology": "junction", + "aperture_count": 3, + "gate_connections": 3, + "hop_distance": 3 + }, + { + "system_id": "GJ 729", + "proper_name": "Ross 154", + "geographic_sector": "west_reach", + "geographic_band": "inner", + "gate_topology": "junction", + "aperture_count": 3, + "gate_connections": 3, + "hop_distance": 3 + }, + { + "system_id": "GJ 764", + "proper_name": "Nova Roma", + "geographic_sector": "core", + "geographic_band": "core", + "gate_topology": "hub", + "aperture_count": 5, + "gate_connections": 5, + "hop_distance": 3 + }, + { + "system_id": "GJ 768", + "proper_name": "Altair", + "geographic_sector": "south_reach", + "geographic_band": "inner", + "gate_topology": "junction", + "aperture_count": 3, + "gate_connections": 3, + "hop_distance": 3 + }, + { + "system_id": "GJ 780", + "proper_name": "Puerto Rivera", + "geographic_sector": "north_reach", + "geographic_band": "inner", + "gate_topology": "junction", + "aperture_count": 3, + "gate_connections": 3, + "hop_distance": 3 + }, + { + "system_id": "GJ 783A", + "proper_name": "Aurelius", + "geographic_sector": "core", + "geographic_band": "core", + "gate_topology": "junction", + "aperture_count": 4, + "gate_connections": 4, + "hop_distance": 3 + }, + { + "system_id": "GJ 866A", + "proper_name": "EZ Aqr", + "geographic_sector": "east_reach", + "geographic_band": "outer", + "gate_topology": "through_route", + "aperture_count": 2, + "gate_connections": 2, + "hop_distance": 3 + }, + { + "system_id": "GJ 887", + "proper_name": "Lacaille", + "geographic_sector": "core", + "geographic_band": "core", + "gate_topology": "loop_member", + "aperture_count": 2, + "gate_connections": 2, + "hop_distance": 3 + }, + { + "system_id": "GJ 892", + "proper_name": "Cairnside", + "geographic_sector": "deep_frontier", + "geographic_band": "outer", + "gate_topology": "spur_end", + "aperture_count": 2, + "gate_connections": 2, + "hop_distance": 3 + }, + { + "system_id": "GJ 1", + "proper_name": "Grünfeld", + "geographic_sector": "west_reach", + "geographic_band": "inner", + "gate_topology": "loop_member", + "aperture_count": 2, + "gate_connections": 2, + "hop_distance": 4 + }, + { + "system_id": "GJ 1002", + "proper_name": "Caparica", + "geographic_sector": "south_reach", + "geographic_band": "inner", + "gate_topology": "spur_end", + "aperture_count": 2, + "gate_connections": 2, + "hop_distance": 4 + }, + { + "system_id": "GJ 105A", + "proper_name": "Nová Tržnice", + "geographic_sector": "east_reach", + "geographic_band": "inner", + "gate_topology": "junction", + "aperture_count": 3, + "gate_connections": 3, + "hop_distance": 4 + }, + { + "system_id": "GJ 1061", + "proper_name": "Crux Station", + "geographic_sector": "south_reach", + "geographic_band": "inner", + "gate_topology": "junction", + "aperture_count": 4, + "gate_connections": 4, + "hop_distance": 4 + }, + { + "system_id": "GJ 1111", + "proper_name": "Zenzele", + "geographic_sector": "south_reach", + "geographic_band": "inner", + "gate_topology": "hub", + "aperture_count": 5, + "gate_connections": 5, + "hop_distance": 4 + }, + { + "system_id": "GJ 1230A", + "proper_name": "Uusi Vantaa", + "geographic_sector": "north_reach", + "geographic_band": "inner", + "gate_topology": "loop_member", + "aperture_count": 2, + "gate_connections": 2, + "hop_distance": 4 + }, + { + "system_id": "GJ 1245A", + "proper_name": "Okoro's Claim", + "geographic_sector": "north_reach", + "geographic_band": "outer", + "gate_topology": "spur_end", + "aperture_count": 2, + "gate_connections": 2, + "hop_distance": 4 + }, + { + "system_id": "GJ 1245B", + "proper_name": "Koeberg", + "geographic_sector": "south_reach", + "geographic_band": "outer", + "gate_topology": "dead_end", + "aperture_count": 1, + "gate_connections": 1, + "hop_distance": 4 + }, + { + "system_id": "GJ 19", + "proper_name": "Solheim", + "geographic_sector": "west_reach", + "geographic_band": "inner", + "gate_topology": "loop_member", + "aperture_count": 2, + "gate_connections": 2, + "hop_distance": 4 + }, + { + "system_id": "GJ 191", + "proper_name": "Kapteyn's Star", + "geographic_sector": "west_reach", + "geographic_band": "inner", + "gate_topology": "junction", + "aperture_count": 3, + "gate_connections": 3, + "hop_distance": 4 + }, + { + "system_id": "GJ 234A", + "proper_name": "Carvalhais", + "geographic_sector": "south_reach", + "geographic_band": "inner", + "gate_topology": "junction", + "aperture_count": 3, + "gate_connections": 3, + "hop_distance": 4 + }, + { + "system_id": "GJ 273", + "proper_name": "Fremantle", + "geographic_sector": "north_reach", + "geographic_band": "inner", + "gate_topology": "junction", + "aperture_count": 3, + "gate_connections": 3, + "hop_distance": 4 + }, + { + "system_id": "GJ 33", + "proper_name": "96 Piscium", + "geographic_sector": "south_reach", + "geographic_band": "inner", + "gate_topology": "loop_member", + "aperture_count": 2, + "gate_connections": 2, + "hop_distance": 4 + }, + { + "system_id": "GJ 3618", + "proper_name": "Pemberton's Loop", + "geographic_sector": "north_reach", + "geographic_band": "inner", + "gate_topology": "loop_member", + "aperture_count": 2, + "gate_connections": 2, + "hop_distance": 4 + }, + { + "system_id": "GJ 3622", + "proper_name": "Havnfjord", + "geographic_sector": "north_reach", + "geographic_band": "inner", + "gate_topology": "loop_member", + "aperture_count": 2, + "gate_connections": 2, + "hop_distance": 4 + }, + { + "system_id": "GJ 412A", + "proper_name": "Morales' End", + "geographic_sector": "south_reach", + "geographic_band": "outer", + "gate_topology": "dead_end", + "aperture_count": 1, + "gate_connections": 1, + "hop_distance": 4 + }, + { + "system_id": "GJ 412B", + "proper_name": "Korhonen", + "geographic_sector": "east_reach", + "geographic_band": "outer", + "gate_topology": "dead_end", + "aperture_count": 1, + "gate_connections": 1, + "hop_distance": 4 + }, + { + "system_id": "GJ 473B", + "proper_name": "Nkosi's Bridge", + "geographic_sector": "north_reach", + "geographic_band": "inner", + "gate_topology": "loop_member", + "aperture_count": 2, + "gate_connections": 2, + "hop_distance": 4 + }, + { + "system_id": "GJ 53A", + "proper_name": "Bridgepoint", + "geographic_sector": "east_reach", + "geographic_band": "inner", + "gate_topology": "through_route", + "aperture_count": 2, + "gate_connections": 2, + "hop_distance": 4 + }, + { + "system_id": "GJ 54.1", + "proper_name": "Ribeiro's Star", + "geographic_sector": "south_reach", + "geographic_band": "inner", + "gate_topology": "hub", + "aperture_count": 5, + "gate_connections": 5, + "hop_distance": 4 + }, + { + "system_id": "GJ 566A", + "proper_name": "Haodu", + "geographic_sector": "east_reach", + "geographic_band": "inner", + "gate_topology": "hub", + "aperture_count": 5, + "gate_connections": 5, + "hop_distance": 4 + }, + { + "system_id": "GJ 566B", + "proper_name": "Confluência", + "geographic_sector": "south_reach", + "geographic_band": "inner", + "gate_topology": "hub", + "aperture_count": 6, + "gate_connections": 6, + "hop_distance": 4 + }, + { + "system_id": "GJ 628", + "proper_name": "Brennan's Drift", + "geographic_sector": "west_reach", + "geographic_band": "inner", + "gate_topology": "loop_member", + "aperture_count": 2, + "gate_connections": 2, + "hop_distance": 4 + }, + { + "system_id": "GJ 65A", + "proper_name": "UV Ceti", + "geographic_sector": "north_reach", + "geographic_band": "outer", + "gate_topology": "spur_end", + "aperture_count": 2, + "gate_connections": 2, + "hop_distance": 4 + }, + { + "system_id": "GJ 667A", + "proper_name": "New Ballarat", + "geographic_sector": "north_reach", + "geographic_band": "inner", + "gate_topology": "junction", + "aperture_count": 3, + "gate_connections": 3, + "hop_distance": 4 + }, + { + "system_id": "GJ 667B", + "proper_name": "Kampala Gate", + "geographic_sector": "north_reach", + "geographic_band": "inner", + "gate_topology": "junction", + "aperture_count": 4, + "gate_connections": 4, + "hop_distance": 4 + }, + { + "system_id": "GJ 66B", + "proper_name": "Voss", + "geographic_sector": "west_reach", + "geographic_band": "outer", + "gate_topology": "dead_end", + "aperture_count": 1, + "gate_connections": 1, + "hop_distance": 4 + }, + { + "system_id": "GJ 673", + "proper_name": "Lindström Station", + "geographic_sector": "north_reach", + "geographic_band": "outer", + "gate_topology": "spur_end", + "aperture_count": 2, + "gate_connections": 2, + "hop_distance": 4 + }, + { + "system_id": "GJ 68", + "proper_name": "Schuilhoek", + "geographic_sector": "deep_frontier", + "geographic_band": "outer", + "gate_topology": "through_route", + "aperture_count": 2, + "gate_connections": 2, + "hop_distance": 4 + }, + { + "system_id": "GJ 687", + "proper_name": "Matsue", + "geographic_sector": "east_reach", + "geographic_band": "outer", + "gate_topology": "through_route", + "aperture_count": 2, + "gate_connections": 2, + "hop_distance": 4 + }, + { + "system_id": "GJ 825", + "proper_name": "Lacaille", + "geographic_sector": "north_reach", + "geographic_band": "inner", + "gate_topology": "junction", + "aperture_count": 3, + "gate_connections": 3, + "hop_distance": 4 + }, + { + "system_id": "GJ 860A", + "proper_name": "Kruger 60", + "geographic_sector": "south_reach", + "geographic_band": "inner", + "gate_topology": "junction", + "aperture_count": 3, + "gate_connections": 3, + "hop_distance": 4 + }, + { + "system_id": "GJ 860B", + "proper_name": "Changwon", + "geographic_sector": "east_reach", + "geographic_band": "outer", + "gate_topology": "junction", + "aperture_count": 3, + "gate_connections": 3, + "hop_distance": 4 + }, + { + "system_id": "GJ 876", + "proper_name": "Koskinen's Reach", + "geographic_sector": "north_reach", + "geographic_band": "inner", + "gate_topology": "spur_end", + "aperture_count": 2, + "gate_connections": 2, + "hop_distance": 4 + }, + { + "system_id": "GJ 879", + "proper_name": "Singh's Landing", + "geographic_sector": "north_reach", + "geographic_band": "outer", + "gate_topology": "spur_end", + "aperture_count": 2, + "gate_connections": 2, + "hop_distance": 4 + }, + { + "system_id": "GJ 1005A", + "proper_name": "Port Desrochers", + "geographic_sector": "east_reach", + "geographic_band": "outer", + "gate_topology": "junction", + "aperture_count": 3, + "gate_connections": 3, + "hop_distance": 5 + }, + { + "system_id": "GJ 1116A", + "proper_name": "Mabaso Junction", + "geographic_sector": "north_reach", + "geographic_band": "inner", + "gate_topology": "junction", + "aperture_count": 3, + "gate_connections": 3, + "hop_distance": 5 + }, + { + "system_id": "GJ 1116B", + "proper_name": "Kraków Junction", + "geographic_sector": "west_reach", + "geographic_band": "inner", + "gate_topology": "junction", + "aperture_count": 3, + "gate_connections": 3, + "hop_distance": 5 + }, + { + "system_id": "GJ 1289", + "proper_name": "Athabasca", + "geographic_sector": "north_reach", + "geographic_band": "outer", + "gate_topology": "junction", + "aperture_count": 3, + "gate_connections": 3, + "hop_distance": 5 + }, + { + "system_id": "GJ 178", + "proper_name": "Ashfield", + "geographic_sector": "north_reach", + "geographic_band": "inner", + "gate_topology": "through_route", + "aperture_count": 2, + "gate_connections": 2, + "hop_distance": 5 + }, + { + "system_id": "GJ 183", + "proper_name": "Waterkant", + "geographic_sector": "north_reach", + "geographic_band": "outer", + "gate_topology": "through_route", + "aperture_count": 2, + "gate_connections": 2, + "hop_distance": 5 + }, + { + "system_id": "GJ 205", + "proper_name": "Kurashiki", + "geographic_sector": "east_reach", + "geographic_band": "outer", + "gate_topology": "through_route", + "aperture_count": 2, + "gate_connections": 2, + "hop_distance": 5 + }, + { + "system_id": "GJ 213", + "proper_name": "Kaapse Baai", + "geographic_sector": "south_reach", + "geographic_band": "inner", + "gate_topology": "spur_end", + "aperture_count": 2, + "gate_connections": 2, + "hop_distance": 5 + }, + { + "system_id": "GJ 216B", + "proper_name": "Gemini Point", + "geographic_sector": "north_reach", + "geographic_band": "outer", + "gate_topology": "dead_end", + "aperture_count": 1, + "gate_connections": 1, + "hop_distance": 5 + }, + { + "system_id": "GJ 222A", + "proper_name": "Karoo", + "geographic_sector": "south_reach", + "geographic_band": "inner", + "gate_topology": "through_route", + "aperture_count": 2, + "gate_connections": 2, + "hop_distance": 5 + }, + { + "system_id": "GJ 229", + "proper_name": "Port Mäkelä", + "geographic_sector": "west_reach", + "geographic_band": "inner", + "gate_topology": "spur_end", + "aperture_count": 2, + "gate_connections": 2, + "hop_distance": 5 + }, + { + "system_id": "GJ 250A", + "proper_name": "Carrington's Post", + "geographic_sector": "north_reach", + "geographic_band": "outer", + "gate_topology": "spur_end", + "aperture_count": 2, + "gate_connections": 2, + "hop_distance": 5 + }, + { + "system_id": "GJ 285", + "proper_name": "Fenwick's Lantern", + "geographic_sector": "north_reach", + "geographic_band": "outer", + "gate_topology": "dead_end", + "aperture_count": 1, + "gate_connections": 1, + "hop_distance": 5 + }, + { + "system_id": "GJ 300", + "proper_name": "Dunmore Stillwater", + "geographic_sector": "north_reach", + "geographic_band": "outer", + "gate_topology": "dead_end", + "aperture_count": 1, + "gate_connections": 1, + "hop_distance": 5 + }, + { + "system_id": "GJ 3379", + "proper_name": "Shenwei", + "geographic_sector": "east_reach", + "geographic_band": "inner", + "gate_topology": "junction", + "aperture_count": 3, + "gate_connections": 3, + "hop_distance": 5 + }, + { + "system_id": "GJ 3820", + "proper_name": "Uusi Vantaa", + "geographic_sector": "north_reach", + "geographic_band": "outer", + "gate_topology": "dead_end", + "aperture_count": 1, + "gate_connections": 1, + "hop_distance": 5 + }, + { + "system_id": "GJ 388", + "proper_name": "The Lantern", + "geographic_sector": "south_reach", + "geographic_band": "inner", + "gate_topology": "hub", + "aperture_count": 5, + "gate_connections": 5, + "hop_distance": 5 + }, + { + "system_id": "GJ 3991", + "proper_name": "Dunbar", + "geographic_sector": "north_reach", + "geographic_band": "inner", + "gate_topology": "loop_member", + "aperture_count": 2, + "gate_connections": 2, + "hop_distance": 5 + }, + { + "system_id": "GJ 4053", + "proper_name": "Dzimbahwe", + "geographic_sector": "south_reach", + "geographic_band": "outer", + "gate_topology": "spur_end", + "aperture_count": 2, + "gate_connections": 2, + "hop_distance": 5 + }, + { + "system_id": "GJ 438", + "proper_name": "Relay 7", + "geographic_sector": "east_reach", + "geographic_band": "inner", + "gate_topology": "loop_member", + "aperture_count": 2, + "gate_connections": 2, + "hop_distance": 5 + }, + { + "system_id": "GJ 445", + "proper_name": "Jinghu", + "geographic_sector": "east_reach", + "geographic_band": "inner", + "gate_topology": "junction", + "aperture_count": 3, + "gate_connections": 3, + "hop_distance": 5 + }, + { + "system_id": "GJ 475", + "proper_name": "Braemar", + "geographic_sector": "north_reach", + "geographic_band": "outer", + "gate_topology": "loop_member", + "aperture_count": 2, + "gate_connections": 2, + "hop_distance": 5 + }, + { + "system_id": "GJ 506", + "proper_name": "Dokkum", + "geographic_sector": "west_reach", + "geographic_band": "outer", + "gate_topology": "loop_member", + "aperture_count": 2, + "gate_connections": 2, + "hop_distance": 5 + }, + { + "system_id": "GJ 526", + "proper_name": "Neustadt", + "geographic_sector": "west_reach", + "geographic_band": "outer", + "gate_topology": "loop_member", + "aperture_count": 2, + "gate_connections": 2, + "hop_distance": 5 + }, + { + "system_id": "GJ 54", + "proper_name": "Inhambane", + "geographic_sector": "south_reach", + "geographic_band": "inner", + "gate_topology": "junction", + "aperture_count": 3, + "gate_connections": 3, + "hop_distance": 5 + }, + { + "system_id": "GJ 588", + "proper_name": "Harrowfield", + "geographic_sector": "north_reach", + "geographic_band": "outer", + "gate_topology": "dead_end", + "aperture_count": 1, + "gate_connections": 1, + "hop_distance": 5 + }, + { + "system_id": "GJ 644C", + "proper_name": "Moçâmedes", + "geographic_sector": "south_reach", + "geographic_band": "outer", + "gate_topology": "spur_end", + "aperture_count": 2, + "gate_connections": 2, + "hop_distance": 5 + }, + { + "system_id": "GJ 66A", + "proper_name": "p Eridani", + "geographic_sector": "east_reach", + "geographic_band": "inner", + "gate_topology": "junction", + "aperture_count": 3, + "gate_connections": 3, + "hop_distance": 5 + }, + { + "system_id": "GJ 682", + "proper_name": "Taishan", + "geographic_sector": "east_reach", + "geographic_band": "inner", + "gate_topology": "junction", + "aperture_count": 3, + "gate_connections": 3, + "hop_distance": 5 + }, + { + "system_id": "GJ 693", + "proper_name": "Mwangaza", + "geographic_sector": "south_reach", + "geographic_band": "inner", + "gate_topology": "spur_end", + "aperture_count": 2, + "gate_connections": 2, + "hop_distance": 5 + }, + { + "system_id": "GJ 695A", + "proper_name": "GJ 695A", + "geographic_sector": "south_reach", + "geographic_band": "inner", + "gate_topology": "junction", + "aperture_count": 3, + "gate_connections": 3, + "hop_distance": 5 + }, + { + "system_id": "GJ 713A", + "proper_name": "Samuels Reach", + "geographic_sector": "east_reach", + "geographic_band": "inner", + "gate_topology": "loop_member", + "aperture_count": 2, + "gate_connections": 2, + "hop_distance": 5 + }, + { + "system_id": "GJ 752A", + "proper_name": "Ribeirão", + "geographic_sector": "south_reach", + "geographic_band": "outer", + "gate_topology": "spur_end", + "aperture_count": 2, + "gate_connections": 2, + "hop_distance": 5 + }, + { + "system_id": "GJ 754", + "proper_name": "Miryang", + "geographic_sector": "east_reach", + "geographic_band": "outer", + "gate_topology": "through_route", + "aperture_count": 2, + "gate_connections": 2, + "hop_distance": 5 + }, + { + "system_id": "GJ 785", + "proper_name": "Porto Fechado", + "geographic_sector": "south_reach", + "geographic_band": "outer", + "gate_topology": "dead_end", + "aperture_count": 1, + "gate_connections": 1, + "hop_distance": 5 + }, + { + "system_id": "GJ 832", + "proper_name": "Hansestadt", + "geographic_sector": "west_reach", + "geographic_band": "inner", + "gate_topology": "hub", + "aperture_count": 6, + "gate_connections": 6, + "hop_distance": 5 + }, + { + "system_id": "GJ 873", + "proper_name": "Ashworth Junction", + "geographic_sector": "north_reach", + "geographic_band": "inner", + "gate_topology": "junction", + "aperture_count": 3, + "gate_connections": 3, + "hop_distance": 5 + }, + { + "system_id": "GJ 877", + "proper_name": "Holmgren's Passage", + "geographic_sector": "north_reach", + "geographic_band": "inner", + "gate_topology": "through_route", + "aperture_count": 2, + "gate_connections": 2, + "hop_distance": 5 + }, + { + "system_id": "GJ 884", + "proper_name": "Matamba", + "geographic_sector": "south_reach", + "geographic_band": "inner", + "gate_topology": "junction", + "aperture_count": 3, + "gate_connections": 3, + "hop_distance": 5 + }, + { + "system_id": "GJ 905", + "proper_name": "Isibaya", + "geographic_sector": "south_reach", + "geographic_band": "inner", + "gate_topology": "junction", + "aperture_count": 3, + "gate_connections": 3, + "hop_distance": 5 + }, + { + "system_id": "GJ 908", + "proper_name": "Thornfield Station", + "geographic_sector": "east_reach", + "geographic_band": "outer", + "gate_topology": "dead_end", + "aperture_count": 1, + "gate_connections": 1, + "hop_distance": 5 + }, + { + "system_id": "GJ 1125", + "proper_name": "Hwaseong", + "geographic_sector": "east_reach", + "geographic_band": "outer", + "gate_topology": "spur_end", + "aperture_count": 2, + "gate_connections": 2, + "hop_distance": 6 + }, + { + "system_id": "GJ 1128", + "proper_name": "Devereux Point", + "geographic_sector": "east_reach", + "geographic_band": "inner", + "gate_topology": "loop_member", + "aperture_count": 2, + "gate_connections": 2, + "hop_distance": 6 + }, + { + "system_id": "GJ 137", + "proper_name": "Novaya Zarya", + "geographic_sector": "east_reach", + "geographic_band": "inner", + "gate_topology": "junction", + "aperture_count": 4, + "gate_connections": 4, + "hop_distance": 6 + }, + { + "system_id": "GJ 150", + "proper_name": "Rana", + "geographic_sector": "west_reach", + "geographic_band": "inner", + "gate_topology": "junction", + "aperture_count": 3, + "gate_connections": 3, + "hop_distance": 6 + }, + { + "system_id": "GJ 17", + "proper_name": "Binh Trang", + "geographic_sector": "east_reach", + "geographic_band": "inner", + "gate_topology": "junction", + "aperture_count": 3, + "gate_connections": 3, + "hop_distance": 6 + }, + { + "system_id": "GJ 2097", + "proper_name": "Nascente", + "geographic_sector": "south_reach", + "geographic_band": "outer", + "gate_topology": "through_route", + "aperture_count": 2, + "gate_connections": 2, + "hop_distance": 6 + }, + { + "system_id": "GJ 216A", + "proper_name": "Kelburn Crossing", + "geographic_sector": "north_reach", + "geographic_band": "inner", + "gate_topology": "through_route", + "aperture_count": 2, + "gate_connections": 2, + "hop_distance": 6 + }, + { + "system_id": "GJ 268", + "proper_name": "Yeongwol", + "geographic_sector": "east_reach", + "geographic_band": "inner", + "gate_topology": "through_route", + "aperture_count": 2, + "gate_connections": 2, + "hop_distance": 6 + }, + { + "system_id": "GJ 299", + "proper_name": "Várzea", + "geographic_sector": "south_reach", + "geographic_band": "inner", + "gate_topology": "junction", + "aperture_count": 3, + "gate_connections": 3, + "hop_distance": 6 + }, + { + "system_id": "GJ 3135", + "proper_name": "Whitfield Crossing", + "geographic_sector": "east_reach", + "geographic_band": "outer", + "gate_topology": "through_route", + "aperture_count": 2, + "gate_connections": 2, + "hop_distance": 6 + }, + { + "system_id": "GJ 3323", + "proper_name": "Entroncamento", + "geographic_sector": "south_reach", + "geographic_band": "inner", + "gate_topology": "junction", + "aperture_count": 3, + "gate_connections": 3, + "hop_distance": 6 + }, + { + "system_id": "GJ 3325", + "proper_name": "Calloway Reach", + "geographic_sector": "north_reach", + "geographic_band": "inner", + "gate_topology": "loop_member", + "aperture_count": 2, + "gate_connections": 2, + "hop_distance": 6 + }, + { + "system_id": "GJ 3454", + "proper_name": "Eisentor", + "geographic_sector": "west_reach", + "geographic_band": "inner", + "gate_topology": "junction", + "aperture_count": 4, + "gate_connections": 4, + "hop_distance": 6 + }, + { + "system_id": "GJ 35", + "proper_name": "Vuurkloof", + "geographic_sector": "south_reach", + "geographic_band": "outer", + "gate_topology": "through_route", + "aperture_count": 2, + "gate_connections": 2, + "hop_distance": 6 + }, + { + "system_id": "GJ 357", + "proper_name": "Altmark", + "geographic_sector": "west_reach", + "geographic_band": "inner", + "gate_topology": "hub", + "aperture_count": 5, + "gate_connections": 5, + "hop_distance": 6 + }, + { + "system_id": "GJ 3988", + "proper_name": "GJ 3988", + "geographic_sector": "north_reach", + "geographic_band": "outer", + "gate_topology": "spur_end", + "aperture_count": 2, + "gate_connections": 2, + "hop_distance": 6 + }, + { + "system_id": "GJ 402", + "proper_name": "Prospect Hill", + "geographic_sector": "north_reach", + "geographic_band": "outer", + "gate_topology": "spur_end", + "aperture_count": 2, + "gate_connections": 2, + "hop_distance": 6 + }, + { + "system_id": "GJ 408", + "proper_name": "Oude Kraal", + "geographic_sector": "north_reach", + "geographic_band": "outer", + "gate_topology": "spur_end", + "aperture_count": 2, + "gate_connections": 2, + "hop_distance": 6 + }, + { + "system_id": "GJ 4285", + "proper_name": "Lam's Prospect", + "geographic_sector": "east_reach", + "geographic_band": "outer", + "gate_topology": "dead_end", + "aperture_count": 1, + "gate_connections": 1, + "hop_distance": 6 + }, + { + "system_id": "GJ 432A", + "proper_name": "Kaur's Observatory", + "geographic_sector": "east_reach", + "geographic_band": "inner", + "gate_topology": "spur_end", + "aperture_count": 2, + "gate_connections": 2, + "hop_distance": 6 + }, + { + "system_id": "GJ 434", + "proper_name": "GJ 434", + "geographic_sector": "east_reach", + "geographic_band": "inner", + "gate_topology": "spur_end", + "aperture_count": 2, + "gate_connections": 2, + "hop_distance": 6 + }, + { + "system_id": "GJ 442A", + "proper_name": "Torsby", + "geographic_sector": "west_reach", + "geographic_band": "inner", + "gate_topology": "loop_member", + "aperture_count": 2, + "gate_connections": 2, + "hop_distance": 6 + }, + { + "system_id": "GJ 451A", + "proper_name": "Groombridge 1830", + "geographic_sector": "south_reach", + "geographic_band": "inner", + "gate_topology": "junction", + "aperture_count": 3, + "gate_connections": 3, + "hop_distance": 6 + }, + { + "system_id": "GJ 473A", + "proper_name": "Passagem", + "geographic_sector": "south_reach", + "geographic_band": "inner", + "gate_topology": "through_route", + "aperture_count": 2, + "gate_connections": 2, + "hop_distance": 6 + }, + { + "system_id": "GJ 49", + "proper_name": "Dernier Quai", + "geographic_sector": "east_reach", + "geographic_band": "inner", + "gate_topology": "dead_end", + "aperture_count": 1, + "gate_connections": 1, + "hop_distance": 6 + }, + { + "system_id": "GJ 502", + "proper_name": "Meridian", + "geographic_sector": "west_reach", + "geographic_band": "inner", + "gate_topology": "junction", + "aperture_count": 3, + "gate_connections": 3, + "hop_distance": 6 + }, + { + "system_id": "GJ 536", + "proper_name": "Retiro", + "geographic_sector": "south_reach", + "geographic_band": "outer", + "gate_topology": "dead_end", + "aperture_count": 1, + "gate_connections": 1, + "hop_distance": 6 + }, + { + "system_id": "GJ 555", + "proper_name": "Tanaka Station", + "geographic_sector": "east_reach", + "geographic_band": "outer", + "gate_topology": "junction", + "aperture_count": 3, + "gate_connections": 3, + "hop_distance": 6 + }, + { + "system_id": "GJ 581", + "proper_name": "Rødvik", + "geographic_sector": "west_reach", + "geographic_band": "inner", + "gate_topology": "loop_member", + "aperture_count": 2, + "gate_connections": 2, + "hop_distance": 6 + }, + { + "system_id": "GJ 625", + "proper_name": "Songjiang", + "geographic_sector": "east_reach", + "geographic_band": "outer", + "gate_topology": "spur_end", + "aperture_count": 2, + "gate_connections": 2, + "hop_distance": 6 + }, + { + "system_id": "GJ 631", + "proper_name": "Bakshi Spur", + "geographic_sector": "west_reach", + "geographic_band": "outer", + "gate_topology": "spur_end", + "aperture_count": 2, + "gate_connections": 2, + "hop_distance": 6 + }, + { + "system_id": "GJ 638", + "proper_name": "Greymantle", + "geographic_sector": "north_reach", + "geographic_band": "outer", + "gate_topology": "dead_end", + "aperture_count": 1, + "gate_connections": 1, + "hop_distance": 6 + }, + { + "system_id": "GJ 643", + "proper_name": "Marktfeld", + "geographic_sector": "west_reach", + "geographic_band": "outer", + "gate_topology": "spur_end", + "aperture_count": 2, + "gate_connections": 2, + "hop_distance": 6 + }, + { + "system_id": "GJ 644A", + "proper_name": "Mavela", + "geographic_sector": "south_reach", + "geographic_band": "inner", + "gate_topology": "loop_member", + "aperture_count": 2, + "gate_connections": 2, + "hop_distance": 6 + }, + { + "system_id": "GJ 661B", + "proper_name": "Breëvlei", + "geographic_sector": "north_reach", + "geographic_band": "outer", + "gate_topology": "through_route", + "aperture_count": 2, + "gate_connections": 2, + "hop_distance": 6 + }, + { + "system_id": "GJ 667C", + "proper_name": "Gliese 667", + "geographic_sector": "east_reach", + "geographic_band": "outer", + "gate_topology": "dead_end", + "aperture_count": 1, + "gate_connections": 1, + "hop_distance": 6 + }, + { + "system_id": "GJ 680", + "proper_name": "Carnarvon", + "geographic_sector": "north_reach", + "geographic_band": "inner", + "gate_topology": "spur_end", + "aperture_count": 2, + "gate_connections": 2, + "hop_distance": 6 + }, + { + "system_id": "GJ 784", + "proper_name": "Kovács Loop", + "geographic_sector": "east_reach", + "geographic_band": "inner", + "gate_topology": "loop_member", + "aperture_count": 2, + "gate_connections": 2, + "hop_distance": 6 + }, + { + "system_id": "GJ 809", + "proper_name": "Seiðrmaðr", + "geographic_sector": "west_reach", + "geographic_band": "outer", + "gate_topology": "dead_end", + "aperture_count": 1, + "gate_connections": 1, + "hop_distance": 6 + }, + { + "system_id": "GJ 827", + "proper_name": "Solandar", + "geographic_sector": "south_reach", + "geographic_band": "outer", + "gate_topology": "dead_end", + "aperture_count": 1, + "gate_connections": 1, + "hop_distance": 6 + }, + { + "system_id": "GJ 829", + "proper_name": "Posto Avançado", + "geographic_sector": "south_reach", + "geographic_band": "inner", + "gate_topology": "spur_end", + "aperture_count": 2, + "gate_connections": 2, + "hop_distance": 6 + }, + { + "system_id": "GJ 83.1", + "proper_name": "Farol", + "geographic_sector": "south_reach", + "geographic_band": "outer", + "gate_topology": "spur_end", + "aperture_count": 2, + "gate_connections": 2, + "hop_distance": 6 + }, + { + "system_id": "GJ 880", + "proper_name": "Holgersen's Landing", + "geographic_sector": "north_reach", + "geographic_band": "outer", + "gate_topology": "dead_end", + "aperture_count": 1, + "gate_connections": 1, + "hop_distance": 6 + }, + { + "system_id": "GJ 896A", + "proper_name": "Nowa Huta", + "geographic_sector": "south_reach", + "geographic_band": "inner", + "gate_topology": "junction", + "aperture_count": 3, + "gate_connections": 3, + "hop_distance": 6 + }, + { + "system_id": "GJ 103", + "proper_name": "Bestevaer", + "geographic_sector": "west_reach", + "geographic_band": "outer", + "gate_topology": "dead_end", + "aperture_count": 1, + "gate_connections": 1, + "hop_distance": 7 + }, + { + "system_id": "GJ 1075", + "proper_name": "Espinho", + "geographic_sector": "south_reach", + "geographic_band": "outer", + "gate_topology": "dead_end", + "aperture_count": 1, + "gate_connections": 1, + "hop_distance": 7 + }, + { + "system_id": "GJ 107A", + "proper_name": "Tình Yên", + "geographic_sector": "east_reach", + "geographic_band": "outer", + "gate_topology": "dead_end", + "aperture_count": 1, + "gate_connections": 1, + "hop_distance": 7 + }, + { + "system_id": "GJ 117", + "proper_name": "GJ 117", + "geographic_sector": "east_reach", + "geographic_band": "outer", + "gate_topology": "through_route", + "aperture_count": 2, + "gate_connections": 2, + "hop_distance": 7 + }, + { + "system_id": "GJ 1232", + "proper_name": "Gammelstad", + "geographic_sector": "west_reach", + "geographic_band": "inner", + "gate_topology": "through_route", + "aperture_count": 2, + "gate_connections": 2, + "hop_distance": 7 + }, + { + "system_id": "GJ 1235", + "proper_name": "Knotenpunkt", + "geographic_sector": "deep_frontier", + "geographic_band": "outer", + "gate_topology": "junction", + "aperture_count": 3, + "gate_connections": 3, + "hop_distance": 7 + }, + { + "system_id": "GJ 124", + "proper_name": "Hạ Long", + "geographic_sector": "east_reach", + "geographic_band": "outer", + "gate_topology": "through_route", + "aperture_count": 2, + "gate_connections": 2, + "hop_distance": 7 + }, + { + "system_id": "GJ 169", + "proper_name": "Posto Avançado", + "geographic_sector": "deep_frontier", + "geographic_band": "inner", + "gate_topology": "dead_end", + "aperture_count": 1, + "gate_connections": 1, + "hop_distance": 7 + }, + { + "system_id": "GJ 172", + "proper_name": "Kaelong", + "geographic_sector": "east_reach", + "geographic_band": "inner", + "gate_topology": "junction", + "aperture_count": 3, + "gate_connections": 3, + "hop_distance": 7 + }, + { + "system_id": "GJ 208", + "proper_name": "Pereira's Rest", + "geographic_sector": "west_reach", + "geographic_band": "outer", + "gate_topology": "dead_end", + "aperture_count": 1, + "gate_connections": 1, + "hop_distance": 7 + }, + { + "system_id": "GJ 231", + "proper_name": "Thorvaldsen's Circuit", + "geographic_sector": "west_reach", + "geographic_band": "outer", + "gate_topology": "loop_member", + "aperture_count": 2, + "gate_connections": 2, + "hop_distance": 7 + }, + { + "system_id": "GJ 27", + "proper_name": "Okahandja", + "geographic_sector": "south_reach", + "geographic_band": "inner", + "gate_topology": "spur_end", + "aperture_count": 2, + "gate_connections": 2, + "hop_distance": 7 + }, + { + "system_id": "GJ 286", + "proper_name": "Weitblick", + "geographic_sector": "deep_frontier", + "geographic_band": "inner", + "gate_topology": "through_route", + "aperture_count": 2, + "gate_connections": 2, + "hop_distance": 7 + }, + { + "system_id": "GJ 320", + "proper_name": "Sonnwend", + "geographic_sector": "west_reach", + "geographic_band": "outer", + "gate_topology": "spur_end", + "aperture_count": 2, + "gate_connections": 2, + "hop_distance": 7 + }, + { + "system_id": "GJ 325A", + "proper_name": "Purnima", + "geographic_sector": "east_reach", + "geographic_band": "outer", + "gate_topology": "dead_end", + "aperture_count": 1, + "gate_connections": 1, + "hop_distance": 7 + }, + { + "system_id": "GJ 325B", + "proper_name": "GJ 325B", + "geographic_sector": "south_reach", + "geographic_band": "outer", + "gate_topology": "dead_end", + "aperture_count": 1, + "gate_connections": 1, + "hop_distance": 7 + }, + { + "system_id": "GJ 3442", + "proper_name": "Último Farol", + "geographic_sector": "deep_frontier", + "geographic_band": "outer", + "gate_topology": "dead_end", + "aperture_count": 1, + "gate_connections": 1, + "hop_distance": 7 + }, + { + "system_id": "GJ 356A", + "proper_name": "Lichtung", + "geographic_sector": "west_reach", + "geographic_band": "outer", + "gate_topology": "dead_end", + "aperture_count": 1, + "gate_connections": 1, + "hop_distance": 7 + }, + { + "system_id": "GJ 370", + "proper_name": "Traagwater", + "geographic_sector": "west_reach", + "geographic_band": "outer", + "gate_topology": "dead_end", + "aperture_count": 1, + "gate_connections": 1, + "hop_distance": 7 + }, + { + "system_id": "GJ 3737", + "proper_name": "Nyrheim", + "geographic_sector": "west_reach", + "geographic_band": "inner", + "gate_topology": "loop_member", + "aperture_count": 2, + "gate_connections": 2, + "hop_distance": 7 + }, + { + "system_id": "GJ 393", + "proper_name": "Encrucijada", + "geographic_sector": "south_reach", + "geographic_band": "inner", + "gate_topology": "hub", + "aperture_count": 5, + "gate_connections": 5, + "hop_distance": 7 + }, + { + "system_id": "GJ 410", + "proper_name": "Wagtoring", + "geographic_sector": "north_reach", + "geographic_band": "outer", + "gate_topology": "dead_end", + "aperture_count": 1, + "gate_connections": 1, + "hop_distance": 7 + }, + { + "system_id": "GJ 423A", + "proper_name": "Marunong", + "geographic_sector": "east_reach", + "geographic_band": "outer", + "gate_topology": "through_route", + "aperture_count": 2, + "gate_connections": 2, + "hop_distance": 7 + }, + { + "system_id": "GJ 423B", + "proper_name": "Quilombo", + "geographic_sector": "south_reach", + "geographic_band": "outer", + "gate_topology": "through_route", + "aperture_count": 2, + "gate_connections": 2, + "hop_distance": 7 + }, + { + "system_id": "GJ 449", + "proper_name": "Murakami's Lantern", + "geographic_sector": "east_reach", + "geographic_band": "outer", + "gate_topology": "dead_end", + "aperture_count": 1, + "gate_connections": 1, + "hop_distance": 7 + }, + { + "system_id": "GJ 453", + "proper_name": "Freiholt", + "geographic_sector": "west_reach", + "geographic_band": "inner", + "gate_topology": "junction", + "aperture_count": 3, + "gate_connections": 3, + "hop_distance": 7 + }, + { + "system_id": "GJ 4A", + "proper_name": "Velha Guarda", + "geographic_sector": "south_reach", + "geographic_band": "outer", + "gate_topology": "dead_end", + "aperture_count": 1, + "gate_connections": 1, + "hop_distance": 7 + }, + { + "system_id": "GJ 505A", + "proper_name": "Bergtor", + "geographic_sector": "west_reach", + "geographic_band": "inner", + "gate_topology": "spur_end", + "aperture_count": 2, + "gate_connections": 2, + "hop_distance": 7 + }, + { + "system_id": "GJ 508A", + "proper_name": "Stilwater", + "geographic_sector": "north_reach", + "geographic_band": "outer", + "gate_topology": "through_route", + "aperture_count": 2, + "gate_connections": 2, + "hop_distance": 7 + }, + { + "system_id": "GJ 534", + "proper_name": "Stillvakt", + "geographic_sector": "west_reach", + "geographic_band": "outer", + "gate_topology": "dead_end", + "aperture_count": 1, + "gate_connections": 1, + "hop_distance": 7 + }, + { + "system_id": "GJ 541", + "proper_name": "Chandra Deep", + "geographic_sector": "east_reach", + "geographic_band": "outer", + "gate_topology": "dead_end", + "aperture_count": 1, + "gate_connections": 1, + "hop_distance": 7 + }, + { + "system_id": "GJ 567", + "proper_name": "Fragua", + "geographic_sector": "south_reach", + "geographic_band": "outer", + "gate_topology": "dead_end", + "aperture_count": 1, + "gate_connections": 1, + "hop_distance": 7 + }, + { + "system_id": "GJ 603", + "proper_name": "Mossbank", + "geographic_sector": "north_reach", + "geographic_band": "outer", + "gate_topology": "dead_end", + "aperture_count": 1, + "gate_connections": 1, + "hop_distance": 7 + }, + { + "system_id": "GJ 635A", + "proper_name": "Stilmark", + "geographic_sector": "west_reach", + "geographic_band": "outer", + "gate_topology": "dead_end", + "aperture_count": 1, + "gate_connections": 1, + "hop_distance": 7 + }, + { + "system_id": "GJ 635B", + "proper_name": "Clearwater Station", + "geographic_sector": "east_reach", + "geographic_band": "inner", + "gate_topology": "spur_end", + "aperture_count": 2, + "gate_connections": 2, + "hop_distance": 7 + }, + { + "system_id": "GJ 653", + "proper_name": "Dunmore", + "geographic_sector": "north_reach", + "geographic_band": "inner", + "gate_topology": "through_route", + "aperture_count": 2, + "gate_connections": 2, + "hop_distance": 7 + }, + { + "system_id": "GJ 660A", + "proper_name": "Seongho", + "geographic_sector": "east_reach", + "geographic_band": "inner", + "gate_topology": "junction", + "aperture_count": 3, + "gate_connections": 3, + "hop_distance": 7 + }, + { + "system_id": "GJ 688", + "proper_name": "Aguas Bravas", + "geographic_sector": "south_reach", + "geographic_band": "outer", + "gate_topology": "spur_end", + "aperture_count": 2, + "gate_connections": 2, + "hop_distance": 7 + }, + { + "system_id": "GJ 706", + "proper_name": "Ngata's Star", + "geographic_sector": "north_reach", + "geographic_band": "inner", + "gate_topology": "spur_end", + "aperture_count": 2, + "gate_connections": 2, + "hop_distance": 7 + }, + { + "system_id": "GJ 75", + "proper_name": "Enugu", + "geographic_sector": "north_reach", + "geographic_band": "outer", + "gate_topology": "junction", + "aperture_count": 3, + "gate_connections": 3, + "hop_distance": 7 + }, + { + "system_id": "GJ 79", + "proper_name": "Oshima", + "geographic_sector": "east_reach", + "geographic_band": "inner", + "gate_topology": "spur_end", + "aperture_count": 2, + "gate_connections": 2, + "hop_distance": 7 + }, + { + "system_id": "GJ 86", + "proper_name": "Kumasi", + "geographic_sector": "south_reach", + "geographic_band": "outer", + "gate_topology": "through_route", + "aperture_count": 2, + "gate_connections": 2, + "hop_distance": 7 + }, + { + "system_id": "GJ 902", + "proper_name": "GJ 902", + "geographic_sector": "south_reach", + "geographic_band": "outer", + "gate_topology": "dead_end", + "aperture_count": 1, + "gate_connections": 1, + "hop_distance": 7 + }, + { + "system_id": "GJ 909A", + "proper_name": "Tam Giang", + "geographic_sector": "east_reach", + "geographic_band": "outer", + "gate_topology": "spur_end", + "aperture_count": 2, + "gate_connections": 2, + "hop_distance": 7 + }, + { + "system_id": "GJ 92", + "proper_name": "Pollux", + "geographic_sector": "south_reach", + "geographic_band": "outer", + "gate_topology": "through_route", + "aperture_count": 2, + "gate_connections": 2, + "hop_distance": 7 + }, + { + "system_id": "GJ 1248", + "proper_name": "Haltefenn", + "geographic_sector": "west_reach", + "geographic_band": "outer", + "gate_topology": "dead_end", + "aperture_count": 1, + "gate_connections": 1, + "hop_distance": 8 + }, + { + "system_id": "GJ 136", + "proper_name": "Dài Lộ", + "geographic_sector": "east_reach", + "geographic_band": "inner", + "gate_topology": "through_route", + "aperture_count": 2, + "gate_connections": 2, + "hop_distance": 8 + }, + { + "system_id": "GJ 138", + "proper_name": "Sertão", + "geographic_sector": "deep_frontier", + "geographic_band": "inner", + "gate_topology": "through_route", + "aperture_count": 2, + "gate_connections": 2, + "hop_distance": 8 + }, + { + "system_id": "GJ 142", + "proper_name": "Leuchtturm", + "geographic_sector": "deep_frontier", + "geographic_band": "outer", + "gate_topology": "spur_end", + "aperture_count": 2, + "gate_connections": 2, + "hop_distance": 8 + }, + { + "system_id": "GJ 189", + "proper_name": "Altgrund", + "geographic_sector": "deep_frontier", + "geographic_band": "inner", + "gate_topology": "through_route", + "aperture_count": 2, + "gate_connections": 2, + "hop_distance": 8 + }, + { + "system_id": "GJ 197", + "proper_name": "Puerto Último", + "geographic_sector": "south_reach", + "geographic_band": "outer", + "gate_topology": "dead_end", + "aperture_count": 1, + "gate_connections": 1, + "hop_distance": 8 + }, + { + "system_id": "GJ 211", + "proper_name": "Encruzilhada", + "geographic_sector": "deep_frontier", + "geographic_band": "inner", + "gate_topology": "through_route", + "aperture_count": 2, + "gate_connections": 2, + "hop_distance": 8 + }, + { + "system_id": "GJ 302", + "proper_name": "Lichtbrücke", + "geographic_sector": "west_reach", + "geographic_band": "outer", + "gate_topology": "spur_end", + "aperture_count": 2, + "gate_connections": 2, + "hop_distance": 8 + }, + { + "system_id": "GJ 309", + "proper_name": "Vorposten", + "geographic_sector": "deep_frontier", + "geographic_band": "outer", + "gate_topology": "through_route", + "aperture_count": 2, + "gate_connections": 2, + "hop_distance": 8 + }, + { + "system_id": "GJ 324A", + "proper_name": "", + "geographic_sector": "deep_frontier", + "geographic_band": "outer", + "gate_topology": "spur_end", + "aperture_count": 2, + "gate_connections": 2, + "hop_distance": 8 + }, + { + "system_id": "GJ 3250", + "proper_name": "Rionegro", + "geographic_sector": "south_reach", + "geographic_band": "outer", + "gate_topology": "dead_end", + "aperture_count": 1, + "gate_connections": 1, + "hop_distance": 8 + }, + { + "system_id": "GJ 3333", + "proper_name": "Groenland", + "geographic_sector": "west_reach", + "geographic_band": "inner", + "gate_topology": "spur_end", + "aperture_count": 2, + "gate_connections": 2, + "hop_distance": 8 + }, + { + "system_id": "GJ 3533", + "proper_name": "Trung's Crossing", + "geographic_sector": "east_reach", + "geographic_band": "outer", + "gate_topology": "through_route", + "aperture_count": 2, + "gate_connections": 2, + "hop_distance": 8 + }, + { + "system_id": "GJ 3728B", + "proper_name": "Jeonnam", + "geographic_sector": "east_reach", + "geographic_band": "outer", + "gate_topology": "through_route", + "aperture_count": 2, + "gate_connections": 2, + "hop_distance": 8 + }, + { + "system_id": "GJ 414A", + "proper_name": "Vindkast", + "geographic_sector": "west_reach", + "geographic_band": "outer", + "gate_topology": "loop_member", + "aperture_count": 2, + "gate_connections": 2, + "hop_distance": 8 + }, + { + "system_id": "GJ 435", + "proper_name": "Mirante", + "geographic_sector": "south_reach", + "geographic_band": "inner", + "gate_topology": "spur_end", + "aperture_count": 2, + "gate_connections": 2, + "hop_distance": 8 + }, + { + "system_id": "GJ 454", + "proper_name": "Steinfeld", + "geographic_sector": "west_reach", + "geographic_band": "outer", + "gate_topology": "dead_end", + "aperture_count": 1, + "gate_connections": 1, + "hop_distance": 8 + }, + { + "system_id": "GJ 482A", + "proper_name": "Dagat", + "geographic_sector": "east_reach", + "geographic_band": "inner", + "gate_topology": "through_route", + "aperture_count": 2, + "gate_connections": 2, + "hop_distance": 8 + }, + { + "system_id": "GJ 482B", + "proper_name": "Travessa", + "geographic_sector": "deep_frontier", + "geographic_band": "outer", + "gate_topology": "through_route", + "aperture_count": 2, + "gate_connections": 2, + "hop_distance": 8 + }, + { + "system_id": "GJ 542", + "proper_name": "Kopparhytta", + "geographic_sector": "west_reach", + "geographic_band": "inner", + "gate_topology": "loop_member", + "aperture_count": 2, + "gate_connections": 2, + "hop_distance": 8 + }, + { + "system_id": "GJ 575A", + "proper_name": "Ratnagiri", + "geographic_sector": "north_reach", + "geographic_band": "inner", + "gate_topology": "spur_end", + "aperture_count": 2, + "gate_connections": 2, + "hop_distance": 8 + }, + { + "system_id": "GJ 598", + "proper_name": "Helle Wacht", + "geographic_sector": "deep_frontier", + "geographic_band": "outer", + "gate_topology": "through_route", + "aperture_count": 2, + "gate_connections": 2, + "hop_distance": 8 + }, + { + "system_id": "GJ 620.1A", + "proper_name": "Suối Vàng", + "geographic_sector": "east_reach", + "geographic_band": "outer", + "gate_topology": "dead_end", + "aperture_count": 1, + "gate_connections": 1, + "hop_distance": 8 + }, + { + "system_id": "GJ 624", + "proper_name": "Halvøy", + "geographic_sector": "west_reach", + "geographic_band": "outer", + "gate_topology": "dead_end", + "aperture_count": 1, + "gate_connections": 1, + "hop_distance": 8 + }, + { + "system_id": "GJ 67", + "proper_name": "Roça Nova", + "geographic_sector": "south_reach", + "geographic_band": "outer", + "gate_topology": "dead_end", + "aperture_count": 1, + "gate_connections": 1, + "hop_distance": 8 + }, + { + "system_id": "GJ 737B", + "proper_name": "Punta del Sol", + "geographic_sector": "south_reach", + "geographic_band": "outer", + "gate_topology": "dead_end", + "aperture_count": 1, + "gate_connections": 1, + "hop_distance": 8 + }, + { + "system_id": "GJ 798", + "proper_name": "Brückenau", + "geographic_sector": "west_reach", + "geographic_band": "inner", + "gate_topology": "through_route", + "aperture_count": 2, + "gate_connections": 2, + "hop_distance": 8 + }, + { + "system_id": "GJ 848", + "proper_name": "Shimanami", + "geographic_sector": "east_reach", + "geographic_band": "outer", + "gate_topology": "through_route", + "aperture_count": 2, + "gate_connections": 2, + "hop_distance": 8 + }, + { + "system_id": "GJ 914A", + "proper_name": "Droëland", + "geographic_sector": "deep_frontier", + "geographic_band": "outer", + "gate_topology": "through_route", + "aperture_count": 2, + "gate_connections": 2, + "hop_distance": 8 + }, + { + "system_id": "GJ 95", + "proper_name": "Caledonia's End", + "geographic_sector": "north_reach", + "geographic_band": "outer", + "gate_topology": "dead_end", + "aperture_count": 1, + "gate_connections": 1, + "hop_distance": 8 + }, + { + "system_id": "GJ 1073", + "proper_name": "Mabuhay", + "geographic_sector": "east_reach", + "geographic_band": "inner", + "gate_topology": "through_route", + "aperture_count": 2, + "gate_connections": 2, + "hop_distance": 9 + }, + { + "system_id": "GJ 167", + "proper_name": "GJ 167", + "geographic_sector": "deep_frontier", + "geographic_band": "outer", + "gate_topology": "through_route", + "aperture_count": 2, + "gate_connections": 2, + "hop_distance": 9 + }, + { + "system_id": "GJ 174", + "proper_name": "Tiefenbach", + "geographic_sector": "deep_frontier", + "geographic_band": "inner", + "gate_topology": "through_route", + "aperture_count": 2, + "gate_connections": 2, + "hop_distance": 9 + }, + { + "system_id": "GJ 194B", + "proper_name": "Pemberton", + "geographic_sector": "north_reach", + "geographic_band": "outer", + "gate_topology": "through_route", + "aperture_count": 2, + "gate_connections": 2, + "hop_distance": 9 + }, + { + "system_id": "GJ 204", + "proper_name": "Pedra Funda", + "geographic_sector": "deep_frontier", + "geographic_band": "outer", + "gate_topology": "through_route", + "aperture_count": 2, + "gate_connections": 2, + "hop_distance": 9 + }, + { + "system_id": "GJ 349", + "proper_name": "Randgebiet", + "geographic_sector": "deep_frontier", + "geographic_band": "outer", + "gate_topology": "through_route", + "aperture_count": 2, + "gate_connections": 2, + "hop_distance": 9 + }, + { + "system_id": "GJ 3612", + "proper_name": "Xa Vời", + "geographic_sector": "east_reach", + "geographic_band": "outer", + "gate_topology": "dead_end", + "aperture_count": 1, + "gate_connections": 1, + "hop_distance": 9 + }, + { + "system_id": "GJ 425A", + "proper_name": "GJ 425A", + "geographic_sector": "deep_frontier", + "geographic_band": "outer", + "gate_topology": "through_route", + "aperture_count": 2, + "gate_connections": 2, + "hop_distance": 9 + }, + { + "system_id": "GJ 556", + "proper_name": "Recanto", + "geographic_sector": "deep_frontier", + "geographic_band": "outer", + "gate_topology": "dead_end", + "aperture_count": 1, + "gate_connections": 1, + "hop_distance": 9 + }, + { + "system_id": "GJ 601A", + "proper_name": "Ostmark", + "geographic_sector": "west_reach", + "geographic_band": "outer", + "gate_topology": "through_route", + "aperture_count": 2, + "gate_connections": 2, + "hop_distance": 9 + }, + { + "system_id": "GJ 675", + "proper_name": "Neugrund", + "geographic_sector": "deep_frontier", + "geographic_band": "outer", + "gate_topology": "through_route", + "aperture_count": 2, + "gate_connections": 2, + "hop_distance": 9 + }, + { + "system_id": "GJ 707", + "proper_name": "Dois Sóis", + "geographic_sector": "deep_frontier", + "geographic_band": "outer", + "gate_topology": "through_route", + "aperture_count": 2, + "gate_connections": 2, + "hop_distance": 9 + }, + { + "system_id": "GJ 722", + "proper_name": "Skuilplek", + "geographic_sector": "deep_frontier", + "geographic_band": "inner", + "gate_topology": "through_route", + "aperture_count": 2, + "gate_connections": 2, + "hop_distance": 9 + }, + { + "system_id": "GJ 775", + "proper_name": "Randsholm", + "geographic_sector": "west_reach", + "geographic_band": "inner", + "gate_topology": "through_route", + "aperture_count": 2, + "gate_connections": 2, + "hop_distance": 9 + }, + { + "system_id": "GJ 1267", + "proper_name": "Saudade", + "geographic_sector": "deep_frontier", + "geographic_band": "inner", + "gate_topology": "through_route", + "aperture_count": 2, + "gate_connections": 2, + "hop_distance": 10 + }, + { + "system_id": "GJ 146", + "proper_name": "GJ 146", + "geographic_sector": "deep_frontier", + "geographic_band": "outer", + "gate_topology": "through_route", + "aperture_count": 2, + "gate_connections": 2, + "hop_distance": 10 + }, + { + "system_id": "GJ 177", + "proper_name": "GJ 177", + "geographic_sector": "deep_frontier", + "geographic_band": "outer", + "gate_topology": "through_route", + "aperture_count": 2, + "gate_connections": 2, + "hop_distance": 10 + }, + { + "system_id": "GJ 3403", + "proper_name": "GJ 3403", + "geographic_sector": "deep_frontier", + "geographic_band": "outer", + "gate_topology": "through_route", + "aperture_count": 2, + "gate_connections": 2, + "hop_distance": 10 + }, + { + "system_id": "GJ 3586", + "proper_name": "Eisfeld", + "geographic_sector": "west_reach", + "geographic_band": "outer", + "gate_topology": "through_route", + "aperture_count": 2, + "gate_connections": 2, + "hop_distance": 10 + }, + { + "system_id": "GJ 395", + "proper_name": "Confluent", + "geographic_sector": "west_reach", + "geographic_band": "inner", + "gate_topology": "through_route", + "aperture_count": 2, + "gate_connections": 2, + "hop_distance": 10 + }, + { + "system_id": "GJ 528A", + "proper_name": "Ouplaas", + "geographic_sector": "deep_frontier", + "geographic_band": "outer", + "gate_topology": "through_route", + "aperture_count": 2, + "gate_connections": 2, + "hop_distance": 10 + }, + { + "system_id": "GJ 69", + "proper_name": "GJ 69", + "geographic_sector": "deep_frontier", + "geographic_band": "outer", + "gate_topology": "through_route", + "aperture_count": 2, + "gate_connections": 2, + "hop_distance": 10 + }, + { + "system_id": "GJ 716", + "proper_name": "Murchison", + "geographic_sector": "north_reach", + "geographic_band": "outer", + "gate_topology": "through_route", + "aperture_count": 2, + "gate_connections": 2, + "hop_distance": 10 + }, + { + "system_id": "GJ 868", + "proper_name": "Encontro", + "geographic_sector": "deep_frontier", + "geographic_band": "outer", + "gate_topology": "through_route", + "aperture_count": 2, + "gate_connections": 2, + "hop_distance": 10 + }, + { + "system_id": "GJ 1292", + "proper_name": "Moyale", + "geographic_sector": "north_reach", + "geographic_band": "outer", + "gate_topology": "through_route", + "aperture_count": 2, + "gate_connections": 2, + "hop_distance": 11 + }, + { + "system_id": "GJ 394", + "proper_name": "GJ 394", + "geographic_sector": "deep_frontier", + "geographic_band": "outer", + "gate_topology": "through_route", + "aperture_count": 2, + "gate_connections": 2, + "hop_distance": 11 + }, + { + "system_id": "GJ 428A", + "proper_name": "Fluchtburg", + "geographic_sector": "deep_frontier", + "geographic_band": "outer", + "gate_topology": "through_route", + "aperture_count": 2, + "gate_connections": 2, + "hop_distance": 11 + }, + { + "system_id": "GJ 5", + "proper_name": "GJ 5", + "geographic_sector": "deep_frontier", + "geographic_band": "outer", + "gate_topology": "through_route", + "aperture_count": 2, + "gate_connections": 2, + "hop_distance": 11 + }, + { + "system_id": "GJ 524", + "proper_name": "GJ 524", + "geographic_sector": "deep_frontier", + "geographic_band": "outer", + "gate_topology": "through_route", + "aperture_count": 2, + "gate_connections": 2, + "hop_distance": 11 + }, + { + "system_id": "GJ 615", + "proper_name": "Dunkelholz", + "geographic_sector": "west_reach", + "geographic_band": "outer", + "gate_topology": "dead_end", + "aperture_count": 1, + "gate_connections": 1, + "hop_distance": 11 + }, + { + "system_id": "GJ 656", + "proper_name": "GJ 656", + "geographic_sector": "deep_frontier", + "geographic_band": "outer", + "gate_topology": "through_route", + "aperture_count": 2, + "gate_connections": 2, + "hop_distance": 11 + }, + { + "system_id": "GJ 771A", + "proper_name": "Alshain", + "geographic_sector": "west_reach", + "geographic_band": "outer", + "gate_topology": "through_route", + "aperture_count": 2, + "gate_connections": 2, + "hop_distance": 11 + }, + { + "system_id": "GJ 853A", + "proper_name": "GJ 853A", + "geographic_sector": "deep_frontier", + "geographic_band": "outer", + "gate_topology": "through_route", + "aperture_count": 2, + "gate_connections": 2, + "hop_distance": 11 + }, + { + "system_id": "GJ 354A", + "proper_name": "GJ 354A", + "geographic_sector": "deep_frontier", + "geographic_band": "outer", + "gate_topology": "through_route", + "aperture_count": 2, + "gate_connections": 2, + "hop_distance": 12 + }, + { + "system_id": "GJ 4056", + "proper_name": "GJ 4056", + "geographic_sector": "west_reach", + "geographic_band": "inner", + "gate_topology": "through_route", + "aperture_count": 2, + "gate_connections": 2, + "hop_distance": 12 + }, + { + "system_id": "GJ 407", + "proper_name": "Einde", + "geographic_sector": "deep_frontier", + "geographic_band": "outer", + "gate_topology": "dead_end", + "aperture_count": 1, + "gate_connections": 1, + "hop_distance": 12 + }, + { + "system_id": "GJ 61", + "proper_name": "Fernlicht", + "geographic_sector": "deep_frontier", + "geographic_band": "outer", + "gate_topology": "spur_end", + "aperture_count": 2, + "gate_connections": 2, + "hop_distance": 12 + }, + { + "system_id": "GJ 616", + "proper_name": "GJ 616", + "geographic_sector": "deep_frontier", + "geographic_band": "inner", + "gate_topology": "through_route", + "aperture_count": 2, + "gate_connections": 2, + "hop_distance": 12 + }, + { + "system_id": "GJ 770", + "proper_name": "Rekohu", + "geographic_sector": "north_reach", + "geographic_band": "outer", + "gate_topology": "dead_end", + "aperture_count": 1, + "gate_connections": 1, + "hop_distance": 12 + }, + { + "system_id": "GJ 127B", + "proper_name": "Echternach", + "geographic_sector": "west_reach", + "geographic_band": "inner", + "gate_topology": "through_route", + "aperture_count": 2, + "gate_connections": 2, + "hop_distance": 13 + }, + { + "system_id": "GJ 201", + "proper_name": "GJ 201", + "geographic_sector": "deep_frontier", + "geographic_band": "inner", + "gate_topology": "junction", + "aperture_count": 3, + "gate_connections": 3, + "hop_distance": 13 + }, + { + "system_id": "GJ 684B", + "proper_name": "Paso Largo", + "geographic_sector": "deep_frontier", + "geographic_band": "outer", + "gate_topology": "through_route", + "aperture_count": 2, + "gate_connections": 2, + "hop_distance": 13 + }, + { + "system_id": "GJ 903", + "proper_name": "Errai", + "geographic_sector": "deep_frontier", + "geographic_band": "inner", + "gate_topology": "through_route", + "aperture_count": 2, + "gate_connections": 2, + "hop_distance": 13 + }, + { + "system_id": "GJ 42", + "proper_name": "Langemark", + "geographic_sector": "west_reach", + "geographic_band": "inner", + "gate_topology": "through_route", + "aperture_count": 2, + "gate_connections": 2, + "hop_distance": 14 + }, + { + "system_id": "GJ 481", + "proper_name": "Orchard's End", + "geographic_sector": "deep_frontier", + "geographic_band": "outer", + "gate_topology": "spur_end", + "aperture_count": 2, + "gate_connections": 2, + "hop_distance": 14 + }, + { + "system_id": "GJ 684A", + "proper_name": "Sonnenkap", + "geographic_sector": "deep_frontier", + "geographic_band": "outer", + "gate_topology": "spur_end", + "aperture_count": 2, + "gate_connections": 2, + "hop_distance": 14 + }, + { + "system_id": "GJ 875", + "proper_name": "The Narrows", + "geographic_sector": "deep_frontier", + "geographic_band": "outer", + "gate_topology": "through_route", + "aperture_count": 2, + "gate_connections": 2, + "hop_distance": 14 + }, + { + "system_id": "GJ 282A", + "proper_name": "Bout du Chemin", + "geographic_sector": "west_reach", + "geographic_band": "outer", + "gate_topology": "through_route", + "aperture_count": 2, + "gate_connections": 2, + "hop_distance": 15 + }, + { + "system_id": "GJ 282B", + "proper_name": "Travessia", + "geographic_sector": "deep_frontier", + "geographic_band": "outer", + "gate_topology": "through_route", + "aperture_count": 2, + "gate_connections": 2, + "hop_distance": 15 + }, + { + "system_id": "GJ 532", + "proper_name": "Kettenschmied", + "geographic_sector": "deep_frontier", + "geographic_band": "outer", + "gate_topology": "through_route", + "aperture_count": 2, + "gate_connections": 2, + "hop_distance": 15 + }, + { + "system_id": "GJ 421B", + "proper_name": "Pedra Seca", + "geographic_sector": "deep_frontier", + "geographic_band": "outer", + "gate_topology": "through_route", + "aperture_count": 2, + "gate_connections": 2, + "hop_distance": 16 + }, + { + "system_id": "GJ 807", + "proper_name": "Grenzstein", + "geographic_sector": "west_reach", + "geographic_band": "outer", + "gate_topology": "through_route", + "aperture_count": 2, + "gate_connections": 2, + "hop_distance": 16 + }, + { + "system_id": "GJ 904", + "proper_name": "Claraboia", + "geographic_sector": "deep_frontier", + "geographic_band": "outer", + "gate_topology": "through_route", + "aperture_count": 2, + "gate_connections": 2, + "hop_distance": 16 + }, + { + "system_id": "GJ 3943", + "proper_name": "GJ 3943", + "geographic_sector": "deep_frontier", + "geographic_band": "outer", + "gate_topology": "through_route", + "aperture_count": 2, + "gate_connections": 2, + "hop_distance": 17 + }, + { + "system_id": "GJ 529", + "proper_name": "Yttermark", + "geographic_sector": "west_reach", + "geographic_band": "outer", + "gate_topology": "dead_end", + "aperture_count": 1, + "gate_connections": 1, + "hop_distance": 17 + }, + { + "system_id": "GJ 726", + "proper_name": "GJ 726", + "geographic_sector": "deep_frontier", + "geographic_band": "outer", + "gate_topology": "through_route", + "aperture_count": 2, + "gate_connections": 2, + "hop_distance": 17 + }, + { + "system_id": "GJ 546", + "proper_name": "Eerste Wacht", + "geographic_sector": "deep_frontier", + "geographic_band": "outer", + "gate_topology": "through_route", + "aperture_count": 2, + "gate_connections": 2, + "hop_distance": 18 + }, + { + "system_id": "GJ 672", + "proper_name": "Nova Estrada", + "geographic_sector": "deep_frontier", + "geographic_band": "outer", + "gate_topology": "through_route", + "aperture_count": 2, + "gate_connections": 2, + "hop_distance": 18 + }, + { + "system_id": "GJ 111", + "proper_name": "Helderoog", + "geographic_sector": "deep_frontier", + "geographic_band": "outer", + "gate_topology": "dead_end", + "aperture_count": 1, + "gate_connections": 1, + "hop_distance": 19 + }, + { + "system_id": "GJ 147", + "proper_name": "Entremeio", + "geographic_sector": "deep_frontier", + "geographic_band": "outer", + "gate_topology": "through_route", + "aperture_count": 2, + "gate_connections": 2, + "hop_distance": 19 + }, + { + "system_id": "GJ 127A", + "proper_name": "Dalim", + "geographic_sector": "deep_frontier", + "geographic_band": "outer", + "gate_topology": "through_route", + "aperture_count": 2, + "gate_connections": 2, + "hop_distance": 20 + }, + { + "system_id": "GJ 501B", + "proper_name": "Candeia", + "geographic_sector": "deep_frontier", + "geographic_band": "outer", + "gate_topology": "through_route", + "aperture_count": 2, + "gate_connections": 2, + "hop_distance": 21 + }, + { + "system_id": "GJ 7547", + "proper_name": "Skemeraand", + "geographic_sector": "deep_frontier", + "geographic_band": "outer", + "gate_topology": "through_route", + "aperture_count": 2, + "gate_connections": 2, + "hop_distance": 22 + }, + { + "system_id": "GJ 6711", + "proper_name": "Abzu", + "geographic_sector": "deep_frontier", + "geographic_band": "outer", + "gate_topology": "dead_end", + "aperture_count": 1, + "gate_connections": 1, + "hop_distance": 23 + } + ], + "edges": [ + [ + "GJ 71", + "GJ 244A" + ], + [ + "GJ 71", + "GJ 559B" + ], + [ + "GJ 783A", + "GJ 1" + ], + [ + "GJ 1", + "GJ 664" + ], + [ + "GJ 664", + "GJ 19" + ], + [ + "GJ 19", + "GJ 506" + ], + [ + "GJ 506", + "GJ 442A" + ], + [ + "GJ 442A", + "GJ 3737" + ], + [ + "GJ 3737", + "GJ 414A" + ], + [ + "GJ 414A", + "GJ 542" + ], + [ + "GJ 542", + "GJ 231" + ], + [ + "GJ 764", + "GJ 15A" + ], + [ + "GJ 231", + "GJ 581" + ], + [ + "GJ 581", + "GJ 526" + ], + [ + "GJ 526", + "GJ 628" + ], + [ + "GJ 664", + "GJ 66B" + ], + [ + "GJ 628", + "GJ 729" + ], + [ + "GJ 729", + "GJ 191" + ], + [ + "GJ 191", + "GJ 229" + ], + [ + "GJ 631", + "GJ 1232" + ], + [ + "GJ 1232", + "GJ 624" + ], + [ + "GJ 229", + "GJ 809" + ], + [ + "GJ 505A", + "GJ 454" + ], + [ + "GJ 832", + "GJ 502" + ], + [ + "GJ 502", + "GJ 208" + ], + [ + "GJ 635A", + "GJ 357" + ], + [ + "GJ 357", + "GJ 103" + ], + [ + "GJ 502", + "GJ 453" + ], + [ + "GJ 189", + "GJ 174" + ], + [ + "GJ 3333", + "GJ 534" + ], + [ + "GJ 174", + "GJ 69" + ], + [ + "GJ 69", + "GJ 428A" + ], + [ + "GJ 302", + "GJ 775" + ], + [ + "GJ 775", + "GJ 395" + ], + [ + "GJ 395", + "GJ 771A" + ], + [ + "GJ 251", + "GJ 338B" + ], + [ + "GJ 771A", + "GJ 4056" + ], + [ + "GJ 428A", + "GJ 61" + ], + [ + "GJ 4056", + "GJ 127B" + ], + [ + "GJ 481", + "GJ 903" + ], + [ + "GJ 903", + "GJ 616" + ], + [ + "GJ 127B", + "GJ 42" + ], + [ + "GJ 616", + "GJ 853A" + ], + [ + "GJ 853A", + "GJ 146" + ], + [ + "GJ 146", + "GJ 349" + ], + [ + "GJ 349", + "GJ 309" + ], + [ + "GJ 42", + "GJ 282A" + ], + [ + "GJ 309", + "GJ 286" + ], + [ + "GJ 282A", + "GJ 807" + ], + [ + "GJ 603", + "GJ 3988" + ], + [ + "GJ 370", + "GJ 150" + ], + [ + "GJ 356A", + "GJ 643" + ], + [ + "GJ 3442", + "GJ 211" + ], + [ + "GJ 169", + "GJ 482B" + ], + [ + "GJ 211", + "GJ 117" + ], + [ + "GJ 482B", + "GJ 707" + ], + [ + "GJ 556", + "GJ 1267" + ], + [ + "GJ 707", + "GJ 868" + ], + [ + "GJ 868", + "GJ 204" + ], + [ + "GJ 117", + "GJ 434" + ], + [ + "GJ 1267", + "GJ 524" + ], + [ + "GJ 524", + "GJ 5" + ], + [ + "GJ 682", + "GJ 784" + ], + [ + "GJ 784", + "GJ 1128" + ], + [ + "GJ 189", + "GJ 675" + ], + [ + "GJ 204", + "GJ 138" + ], + [ + "GJ 5", + "GJ 528A" + ], + [ + "GJ 528A", + "GJ 722" + ], + [ + "GJ 675", + "GJ 3403" + ], + [ + "GJ 1128", + "GJ 438" + ], + [ + "GJ 138", + "GJ 92" + ], + [ + "GJ 722", + "GJ 914A" + ], + [ + "GJ 438", + "GJ 566A" + ], + [ + "GJ 107A", + "GJ 660A" + ], + [ + "GJ 908", + "GJ 17" + ], + [ + "GJ 17", + "GJ 172" + ], + [ + "GJ 172", + "GJ 555" + ], + [ + "GJ 555", + "GJ 79" + ], + [ + "GJ 66A", + "GJ 412B" + ], + [ + "GJ 79", + "GJ 848" + ], + [ + "GJ 848", + "GJ 136" + ], + [ + "GJ 3403", + "GJ 656" + ], + [ + "GJ 136", + "GJ 449" + ], + [ + "GJ 34B", + "GJ 860B" + ], + [ + "GJ 860B", + "GJ 713A" + ], + [ + "GJ 320", + "GJ 798" + ], + [ + "GJ 909A", + "GJ 3533" + ], + [ + "GJ 3533", + "GJ 1073" + ], + [ + "GJ 656", + "GJ 354A" + ], + [ + "GJ 1073", + "GJ 482A" + ], + [ + "GJ 4285", + "GJ 635B" + ], + [ + "GJ 286", + "GJ 598" + ], + [ + "GJ 482A", + "GJ 124" + ], + [ + "GJ 684A", + "GJ 532" + ], + [ + "GJ 914A", + "GJ 508A" + ], + [ + "GJ 541", + "GJ 432A" + ], + [ + "GJ 354A", + "GJ 684B" + ], + [ + "GJ 684B", + "GJ 875" + ], + [ + "GJ 508A", + "GJ 661B" + ], + [ + "GJ 124", + "GJ 268" + ], + [ + "GJ 532", + "GJ 904" + ], + [ + "GJ 904", + "GJ 726" + ], + [ + "GJ 726", + "GJ 672" + ], + [ + "GJ 432A", + "GJ 1005A" + ], + [ + "GJ 268", + "GJ 754" + ], + [ + "GJ 661B", + "GJ 183" + ], + [ + "GJ 1005A", + "GJ 105A" + ], + [ + "GJ 672", + "GJ 147" + ], + [ + "GJ 147", + "GJ 127A" + ], + [ + "GJ 105A", + "GJ 445" + ], + [ + "GJ 875", + "GJ 282B" + ], + [ + "GJ 625", + "GJ 423A" + ], + [ + "GJ 754", + "GJ 53A" + ], + [ + "GJ 53A", + "GJ 866A" + ], + [ + "GJ 282B", + "GJ 421B" + ], + [ + "GJ 127A", + "GJ 501B" + ], + [ + "GJ 183", + "GJ 68" + ], + [ + "GJ 501B", + "GJ 7547" + ], + [ + "GJ 423A", + "GJ 3135" + ], + [ + "GJ 421B", + "GJ 3943" + ], + [ + "GJ 598", + "GJ 167" + ], + [ + "GJ 798", + "GJ 601A" + ], + [ + "GJ 3135", + "GJ 205" + ], + [ + "GJ 453", + "GJ 1248" + ], + [ + "GJ 167", + "GJ 324A" + ], + [ + "GJ 172", + "GJ 620.1A" + ], + [ + "GJ 68", + "GJ 892" + ], + [ + "GJ 92", + "GJ 2097" + ], + [ + "GJ 205", + "GJ 687" + ], + [ + "GJ 555", + "GJ 3379" + ], + [ + "GJ 3612", + "GJ 3728B" + ], + [ + "GJ 137", + "GJ 325A" + ], + [ + "GJ 601A", + "GJ 3586" + ], + [ + "GJ 142", + "GJ 425A" + ], + [ + "GJ 2097", + "GJ 752A" + ], + [ + "GJ 687", + "GJ 4274" + ], + [ + "GJ 3943", + "GJ 546" + ], + [ + "GJ 845", + "GJ 34A" + ], + [ + "GJ 34A", + "GJ 473B" + ], + [ + "GJ 54.1", + "GJ 411" + ], + [ + "GJ 3820", + "GJ 876" + ], + [ + "GJ 425A", + "GJ 177" + ], + [ + "GJ 807", + "GJ 529" + ], + [ + "GJ 54.1", + "GJ 695A" + ], + [ + "GJ 34A", + "GJ 667A" + ], + [ + "GJ 3728B", + "GJ 27" + ], + [ + "GJ 876", + "GJ 661A" + ], + [ + "GJ 695A", + "GJ 299" + ], + [ + "GJ 567", + "GJ 896A" + ], + [ + "GJ 896A", + "GJ 388" + ], + [ + "GJ 661A", + "GJ 667B" + ], + [ + "GJ 667B", + "GJ 3618" + ], + [ + "GJ 1245B", + "GJ 54" + ], + [ + "GJ 667A", + "GJ 3991" + ], + [ + "GJ 3379", + "GJ 667C" + ], + [ + "GJ 388", + "GJ 33" + ], + [ + "GJ 54", + "GJ 1061" + ], + [ + "GJ 1061", + "GJ 768" + ], + [ + "GJ 3586", + "GJ 615" + ], + [ + "GJ 785", + "GJ 234A" + ], + [ + "GJ 768", + "GJ 1002" + ], + [ + "GJ 3991", + "GJ 3325" + ], + [ + "GJ 3325", + "GJ 475" + ], + [ + "GJ 177", + "GJ 394" + ], + [ + "GJ 234A", + "GJ 566B" + ], + [ + "GJ 475", + "GJ 3622" + ], + [ + "GJ 1002", + "GJ 222A" + ], + [ + "GJ 4A", + "GJ 829" + ], + [ + "GJ 222A", + "GJ 35" + ], + [ + "GJ 3622", + "GJ 663A" + ], + [ + "GJ 33", + "GJ 1156" + ], + [ + "GJ 663A", + "GJ 1230A" + ], + [ + "GJ 412A", + "GJ 860A" + ], + [ + "GJ 394", + "GJ 407" + ], + [ + "GJ 1230A", + "GJ 825" + ], + [ + "GJ 667B", + "GJ 1116A" + ], + [ + "GJ 1075", + "GJ 435" + ], + [ + "GJ 1116A", + "GJ 873" + ], + [ + "GJ 300", + "GJ 216A" + ], + [ + "GJ 435", + "GJ 393" + ], + [ + "GJ 393", + "GJ 644A" + ], + [ + "GJ 644A", + "GJ 905" + ], + [ + "GJ 216A", + "GJ 706" + ], + [ + "GJ 905", + "GJ 1111" + ], + [ + "GJ 1111", + "GJ 693" + ], + [ + "GJ 825", + "GJ 780" + ], + [ + "GJ 566B", + "GJ 213" + ], + [ + "GJ 213", + "GJ 473A" + ], + [ + "GJ 825", + "GJ 139" + ], + [ + "GJ 879", + "GJ 178" + ], + [ + "GJ 706", + "GJ 75" + ], + [ + "GJ 473A", + "GJ 423B" + ], + [ + "GJ 178", + "GJ 638" + ], + [ + "GJ 693", + "GJ 3323" + ], + [ + "GJ 75", + "GJ 575A" + ], + [ + "GJ 1061", + "GJ 884" + ], + [ + "GJ 75", + "GJ 680" + ], + [ + "GJ 451A", + "GJ 688" + ], + [ + "GJ 680", + "GJ 1289" + ], + [ + "GJ 216B", + "GJ 673" + ], + [ + "GJ 35", + "GJ 86" + ], + [ + "GJ 673", + "GJ 406" + ], + [ + "GJ 1111", + "GJ 4053" + ], + [ + "GJ 1245A", + "GJ 877" + ], + [ + "GJ 575A", + "GJ 194B" + ], + [ + "GJ 402", + "GJ 653" + ], + [ + "GJ 653", + "GJ 95" + ], + [ + "GJ 406", + "GJ 65A" + ], + [ + "GJ 3323", + "GJ 325B" + ], + [ + "GJ 1289", + "GJ 273" + ], + [ + "GJ 273", + "GJ 250A" + ], + [ + "GJ 65A", + "GJ 285" + ], + [ + "GJ 194B", + "GJ 716" + ], + [ + "GJ 716", + "GJ 1292" + ], + [ + "GJ 250A", + "GJ 588" + ], + [ + "GJ 546", + "GJ 111" + ], + [ + "GJ 408", + "GJ 410" + ], + [ + "GJ 4053", + "GJ 827" + ], + [ + "GJ 393", + "GJ 197" + ], + [ + "GJ 86", + "GJ 3250" + ], + [ + "GJ 688", + "GJ 737B" + ], + [ + "GJ 877", + "GJ 880" + ], + [ + "GJ 451A", + "GJ 902" + ], + [ + "GJ 1292", + "GJ 770" + ], + [ + "GJ 388", + "GJ 536" + ], + [ + "GJ 423B", + "GJ 67" + ], + [ + "GJ 7547", + "GJ 6711" + ], + [ + "GJ 3454", + "GJ 505A" + ], + [ + "GJ 3454", + "GJ 1116B" + ], + [ + "GJ 3454", + "GJ 357" + ], + [ + "GJ 411", + "GJ 1061" + ], + [ + "GJ 411", + "GJ 1111" + ], + [ + "GJ 411", + "GJ 860A" + ], + [ + "GJ 411", + "GJ 234A" + ], + [ + "GJ 3522", + "GJ 1286" + ], + [ + "GJ 845", + "GJ 663A" + ], + [ + "GJ 845", + "GJ 406" + ], + [ + "GJ 845", + "GJ 661A" + ], + [ + "GJ 845", + "GJ 780" + ], + [ + "GJ 137", + "GJ 66A" + ], + [ + "GJ 137", + "GJ 635B" + ], + [ + "GJ 406", + "GJ 273" + ], + [ + "GJ 406", + "GJ 3618" + ], + [ + "GJ 832", + "GJ 357" + ], + [ + "GJ 832", + "GJ 3454" + ], + [ + "GJ 832", + "GJ 191" + ], + [ + "GJ 832", + "GJ 150" + ], + [ + "GJ 566B", + "GJ 884" + ], + [ + "GJ 566B", + "GJ 388" + ], + [ + "GJ 566B", + "GJ 1156" + ], + [ + "GJ 71", + "GJ 725B" + ], + [ + "GJ 71", + "GJ 144" + ], + [ + "GJ 566A", + "GJ 445" + ], + [ + "GJ 566A", + "GJ 34B" + ], + [ + "GJ 566A", + "GJ 3379" + ], + [ + "GJ 566A", + "GJ 713A" + ], + [ + "GJ 783A", + "GJ 570A" + ], + [ + "GJ 682", + "GJ 860B" + ], + [ + "GJ 667A", + "GJ 873" + ], + [ + "GJ 829", + "GJ 54" + ], + [ + "GJ 473B", + "GJ 667B" + ], + [ + "GJ 27", + "GJ 3323" + ], + [ + "GJ 695A", + "GJ 566B" + ], + [ + "GJ 1116A", + "GJ 408" + ], + [ + "GJ 905", + "GJ 54.1" + ], + [ + "GJ 660A", + "GJ 17" + ], + [ + "GJ 105A", + "GJ 34B" + ], + [ + "GJ 725B", + "GJ 674" + ], + [ + "GJ 380", + "GJ 244A" + ], + [ + "GJ 251", + "GJ 280A" + ], + [ + "GJ 1286", + "GJ 3877" + ], + [ + "GJ 570A", + "GJ 764" + ], + [ + "GJ 702B", + "GJ 144" + ], + [ + "GJ 447", + "GJ 551" + ], + [ + "GJ 820B", + "GJ 887" + ], + [ + "GJ 559B", + "GJ 845" + ], + [ + "GJ 699", + "GJ 139" + ], + [ + "GJ 820B", + "GJ 768" + ], + [ + "GJ 3877", + "GJ 1156" + ], + [ + "GJ 1221", + "GJ 866A" + ], + [ + "GJ 280A", + "GJ 34B" + ], + [ + "GJ 1289", + "GJ 3988" + ], + [ + "GJ 832", + "GJ 643" + ], + [ + "GJ 1005A", + "GJ 49" + ], + [ + "GJ 661A", + "GJ 729" + ], + [ + "GJ 1116B", + "GJ 896A" + ], + [ + "GJ 570A", + "GJ 244A" + ], + [ + "GJ 570A", + "GJ 551" + ], + [ + "GJ 570A", + "GJ 559B" + ], + [ + "GJ 244A", + "GJ 380" + ], + [ + "GJ 244A", + "GJ 338B" + ], + [ + "GJ 244A", + "GJ 820B" + ], + [ + "GJ 783A", + "GJ 1286" + ], + [ + "GJ 783A", + "GJ 251" + ], + [ + "GJ 725B", + "GJ 764" + ], + [ + "GJ 1286", + "GJ 447" + ], + [ + "GJ 251", + "GJ 3522" + ], + [ + "GJ 3877", + "GJ 280A" + ], + [ + "GJ 551", + "GJ 166A" + ], + [ + "GJ 820B", + "GJ 699" + ], + [ + "GJ 887", + "GJ 3877" + ], + [ + "GJ 674", + "GJ 380" + ], + [ + "GJ 764", + "GJ 3522" + ], + [ + "GJ 1156", + "GJ 299" + ], + [ + "GJ 3877", + "GJ 551" + ], + [ + "GJ 357", + "GJ 3333" + ], + [ + "GJ 1111", + "GJ 393" + ], + [ + "GJ 764", + "GJ 144" + ], + [ + "GJ 388", + "GJ 299" + ], + [ + "GJ 820B", + "GJ 559B" + ], + [ + "GJ 54.1", + "GJ 393" + ], + [ + "GJ 251", + "GJ 559B" + ], + [ + "GJ 892", + "GJ 845" + ], + [ + "GJ 324A", + "GJ 1235" + ], + [ + "GJ 142", + "GJ 1235" + ], + [ + "GJ 481", + "GJ 201" + ], + [ + "GJ 684A", + "GJ 201" + ], + [ + "GJ 61", + "GJ 201" + ], + [ + "GJ 752A", + "GJ 54.1" + ], + [ + "GJ 434", + "GJ 682" + ], + [ + "GJ 83.1", + "GJ 1235" + ], + [ + "GJ 909A", + "GJ 137" + ], + [ + "GJ 83.1", + "GJ 884" + ], + [ + "GJ 625", + "GJ 445" + ], + [ + "GJ 879", + "GJ 780" + ], + [ + "GJ 644C", + "GJ 451A" + ], + [ + "GJ 644C", + "GJ 860A" + ], + [ + "GJ 302", + "GJ 453" + ], + [ + "GJ 1125", + "GJ 66A" + ], + [ + "GJ 1125", + "GJ 660A" + ], + [ + "GJ 1245A", + "GJ 139" + ], + [ + "GJ 631", + "GJ 1116B" + ], + [ + "GJ 320", + "GJ 150" + ], + [ + "GJ 402", + "GJ 873" + ], + [ + "GJ 0", + "GJ 71" + ] + ] +} \ No newline at end of file diff --git a/client/scripts/main.gd b/client/scripts/main.gd index 12555c944..7d45d99a5 100644 --- a/client/scripts/main.gd +++ b/client/scripts/main.gd @@ -24,6 +24,7 @@ extends Node2D @onready var loading_screen = $ModalLayer/LoadingScreen # #257: blocking overlay during load @onready var debug_console = $ModalLayer/DebugConsole # #581: tilde debug console @onready var news_ticker = $UILayer/NewsTicker # #592: scrolling headline bar (D-049 z-7) +@onready var star_map = $UILayer/HUD/StarMap # #674: star map insert module (hop-ring view) var _last_dialogue_npc_id: int = -1 # D-064: NPC entity_id for WalkAway input var _last_dialogue_npc_name: String = "" # #535: NPC name for dialogue_response attribution @@ -254,6 +255,8 @@ func _propagate_insert_state() -> void: interaction_prompt.set_insert_active(insert_state) if minimap: minimap.set_insert_active(insert_state) + if star_map: + star_map.set_insert_active(insert_state) # D-057: Update interaction list from game state. diff --git a/client/ui/hud.tscn b/client/ui/hud.tscn index 4e6d2b32f..12a94112c 100644 --- a/client/ui/hud.tscn +++ b/client/ui/hud.tscn @@ -1,6 +1,7 @@ -[gd_scene load_steps=2 format=3 uid="uid://cq1y5w3hmxr8b"] +[gd_scene load_steps=3 format=3 uid="uid://cq1y5w3hmxr8b"] [ext_resource type="Script" path="res://ui/hud.gd" id="1_hud"] +[ext_resource type="PackedScene" path="res://ui/star_map.tscn" id="2_starmap"] [node name="HUD" type="Control"] layout_mode = 3 @@ -36,3 +37,7 @@ text = "Mode: Baseline" [node name="TimeLabel" type="Label" parent="MarginContainer/VBoxContainer"] layout_mode = 2 text = "Time: 08:00" + +; #674: Star map insert — concentric hop-ring view, hidden by default, toggled via keybind +[node name="StarMap" parent="." instance=ExtResource("2_starmap")] +visible = false diff --git a/client/ui/star_map.gd b/client/ui/star_map.gd new file mode 100644 index 000000000..39a1071da --- /dev/null +++ b/client/ui/star_map.gd @@ -0,0 +1,525 @@ +class_name StarMapRenderer +extends Control + +## Star map — concentric hop-ring view of the Settled Reach gate network (#674). +## Renders 301 systems as dots on concentric rings (hop distance from Gateway). +## Sector-colored: core (white-gold), north (blue), south (orange), east (green), west (tan). +## +## Data source: res://data/star_map_data.json (generated from star-map.json + systems.db). +## Regenerate with: tooling/generate-star-map-data.py +## +## D-013: Diegetic neural insert overlay. Accessible from the insert UI. +## Parent epic: #51 (Diegetic Insert/Minimap), ticket #674. + +const DATA_PATH := "res://data/star_map_data.json" + +# Layout +const MAP_CENTER_FRACTION := Vector2(0.5, 0.5) # center of control +const MIN_RING_RADIUS: float = 30.0 # innermost ring (hop 0 = gateway dot only) +const RING_SPACING: float = 22.0 # pixels between hop rings +const MAX_HOP_RINGS: int = 24 # max hop distance we render rings for + +# Dot sizing +const DOT_RADIUS_HUB: float = 4.5 +const DOT_RADIUS_JUNCTION: float = 3.5 +const DOT_RADIUS_DEFAULT: float = 2.5 +const DOT_RADIUS_DEAD_END: float = 2.0 +const GATEWAY_RADIUS: float = 6.0 + +# Selection +const SELECTION_RING_RADIUS: float = 8.0 +const HIT_RADIUS: float = 10.0 # click tolerance + +# Edge rendering +const EDGE_WIDTH: float = 0.4 +const EDGE_ALPHA: float = 0.12 +const EDGE_SELECTED_ALPHA: float = 0.5 + +# Colors — sector palette from wireframe +const COLOR_BG: Color = Color("#0d1117") +const COLOR_RING: Color = Color("#1a2030") +const COLOR_RING_MAJOR: Color = Color("#222a3a") +const COLOR_GATEWAY: Color = Color("#f0d060") +const COLOR_SELECTION: Color = Color("#f0d060") +const COLOR_TEXT: Color = Color("#c8d0e0") +const COLOR_TEXT_DIM: Color = Color("#667788") +const COLOR_INFO_BG: Color = Color(0.05, 0.08, 0.14, 0.92) + +const SECTOR_COLORS: Dictionary = { + "core": Color("#c8d0e0"), + "north_reach": Color("#4488aa"), + "south_reach": Color("#aa6644"), + "east_reach": Color("#44aa66"), + "west_reach": Color("#aa8844"), + "deep_frontier": Color("#556677"), + "unknown": Color("#445566"), +} + +const SECTOR_LABELS: Dictionary = { + "north_reach": "NORTH REACH", + "south_reach": "SOUTH REACH", + "east_reach": "EAST REACH", + "west_reach": "WEST REACH", +} + +# Quadrant angles for sector placement (radians, 0 = right, counterclockwise) +# North = top (-PI/2), East = right (0), South = bottom (PI/2), West = left (PI) +const SECTOR_ANGLE_CENTER: Dictionary = { + "north_reach": -PI / 2.0, + "east_reach": 0.0, + "south_reach": PI / 2.0, + "west_reach": PI, +} +const SECTOR_ANGLE_SPREAD: float = PI / 2.5 # each sector occupies ~72° of arc +const CORE_ANGLE_SPREAD: float = TAU # core systems spread full circle +const DEEP_FRONTIER_ANGLE_SPREAD: float = TAU # deep frontier wraps entire outer edge + +# Pan/zoom +const ZOOM_MIN: float = 0.3 +const ZOOM_MAX: float = 3.0 +const ZOOM_STEP: float = 0.15 + +# Internal state +var _nodes: Array = [] +var _edges: Array = [] +var _node_positions: Dictionary = {} # system_id -> Vector2 (screen coords relative to map center) +var _node_lookup: Dictionary = {} # system_id -> node dict +var _selected_system: String = "" +var _hovered_system: String = "" + +var _zoom: float = 1.0 +var _pan_offset: Vector2 = Vector2.ZERO +var _is_panning: bool = false +var _pan_start: Vector2 = Vector2.ZERO +var _pan_start_offset: Vector2 = Vector2.ZERO + +var _data_loaded: bool = false +var _insert_active: bool = true +var _show_edges: bool = false # toggle edge display + + +func _ready() -> void: + mouse_filter = Control.MOUSE_FILTER_STOP + _load_data() + if _data_loaded: + _compute_layout() + + +func _process(_delta: float) -> void: + if _insert_active and _data_loaded: + queue_redraw() + + +## Called from main.gd when insert state changes. +## Only force-hides when insert is inactive. Does NOT auto-show — star map is +## modal (player opens via toggle_visible()), not always-on like the minimap. +func set_insert_active(active: bool) -> void: + _insert_active = active + if not active: + visible = false + + +## Toggle visibility (e.g., from a keybind or button). +func toggle_visible() -> void: + visible = not visible + + +## Return the currently selected system data, or empty dict. +func get_selected_system() -> Dictionary: + return _node_lookup.get(_selected_system, {}) + + +## Return total system count. +func get_system_count() -> int: + return _nodes.size() + + +## Return total edge count. +func get_edge_count() -> int: + return _edges.size() + + +# ============================================================================= +# Data loading +# ============================================================================= + +func _load_data() -> void: + if not FileAccess.file_exists(DATA_PATH): + push_warning("StarMapRenderer: data file not found at %s" % DATA_PATH) + return + var file := FileAccess.open(DATA_PATH, FileAccess.READ) + if file == null: + push_warning("StarMapRenderer: could not open %s" % DATA_PATH) + return + var parsed: Variant = JSON.parse_string(file.get_as_text()) + file.close() + if not (parsed is Dictionary): + push_warning("StarMapRenderer: invalid JSON in %s" % DATA_PATH) + return + var data := parsed as Dictionary + _nodes = data.get("nodes", []) + _edges = data.get("edges", []) + for node: Dictionary in _nodes: + _node_lookup[node.get("system_id", "")] = node + _data_loaded = true + + +# ============================================================================= +# Layout — place systems on concentric rings by hop distance +# ============================================================================= + +func _compute_layout() -> void: + _node_positions.clear() + + # Group nodes by hop distance + var rings: Dictionary = {} # hop -> Array of nodes + for node: Dictionary in _nodes: + var hop: int = int(node.get("hop_distance", 0)) + if not rings.has(hop): + rings[hop] = [] + rings[hop].append(node) + + # Place each ring + for hop: int in rings: + var ring_nodes: Array = rings[hop] + var radius: float = MIN_RING_RADIUS + hop * RING_SPACING + + if hop == 0: + # Gateway at center + for node: Dictionary in ring_nodes: + _node_positions[node["system_id"]] = Vector2.ZERO + continue + + # Sort nodes within ring by sector for angular grouping + ring_nodes.sort_custom(_sort_by_sector_angle) + + # Distribute nodes within their sector's angular range + var sector_groups: Dictionary = {} + for node: Dictionary in ring_nodes: + var sector: String = node.get("geographic_sector", "unknown") + if not sector_groups.has(sector): + sector_groups[sector] = [] + sector_groups[sector].append(node) + + for sector: String in sector_groups: + var group: Array = sector_groups[sector] + var count: int = group.size() + + var center_angle: float + var spread: float + if sector == "core": + center_angle = 0.0 + spread = CORE_ANGLE_SPREAD + elif sector == "deep_frontier": + center_angle = 0.0 + spread = DEEP_FRONTIER_ANGLE_SPREAD + elif SECTOR_ANGLE_CENTER.has(sector): + center_angle = SECTOR_ANGLE_CENTER[sector] + spread = SECTOR_ANGLE_SPREAD + else: + center_angle = 0.0 + spread = TAU + + # Distribute evenly within sector arc, with deterministic offset per system + for i: int in range(count): + var node: Dictionary = group[i] + var t: float + if count == 1: + t = 0.0 + else: + t = float(i) / float(count) - 0.5 # -0.5 to +0.5 + var angle: float = center_angle + t * spread + # Add small per-node jitter based on system_id hash for visual variety + var jitter: float = _system_hash(node["system_id"]) * 0.08 + angle += jitter + # Slight radial variation to avoid perfect circles + var r_var: float = radius + _system_hash(node["system_id"] + "r") * RING_SPACING * 0.3 + _node_positions[node["system_id"]] = Vector2(cos(angle), sin(angle)) * r_var + + +func _sort_by_sector_angle(a: Dictionary, b: Dictionary) -> bool: + var sa: float = _sector_sort_key(a) + var sb: float = _sector_sort_key(b) + if sa != sb: + return sa < sb + return a.get("system_id", "") < b.get("system_id", "") + + +func _sector_sort_key(node: Dictionary) -> float: + var sector: String = node.get("geographic_sector", "unknown") + match sector: + "core": return 0.0 + "north_reach": return 1.0 + "east_reach": return 2.0 + "south_reach": return 3.0 + "west_reach": return 4.0 + "deep_frontier": return 5.0 + _: return 6.0 + + +## Deterministic float in [-1, 1] from a string key. +func _system_hash(key: String) -> float: + var h: int = key.hash() + return fmod(float(h) / 2147483647.0, 1.0) * 2.0 - 1.0 + + +# ============================================================================= +# Drawing +# ============================================================================= + +func _draw() -> void: + if not _data_loaded: + return + + var sz: Vector2 = get_rect().size + var center: Vector2 = sz * MAP_CENTER_FRACTION + _pan_offset + + # Background + draw_rect(Rect2(Vector2.ZERO, sz), COLOR_BG) + + # Hop rings (concentric circles) + _draw_rings(center) + + # Sector labels + _draw_sector_labels(center) + + # Edges (gate connections) + if _show_edges or _selected_system != "": + _draw_edges(center) + + # System dots + _draw_systems(center) + + # Selection highlight + if _selected_system != "": + _draw_selection(center) + + # Info panel for selected system + if _selected_system != "": + _draw_info_panel(sz) + + # Title + _draw_title() + + +func _draw_rings(center: Vector2) -> void: + for hop: int in range(MAX_HOP_RINGS + 1): + var radius: float = (MIN_RING_RADIUS + hop * RING_SPACING) * _zoom + if radius < 1.0 or radius > 2000.0: + continue + var color: Color = COLOR_RING_MAJOR if hop % 5 == 0 else COLOR_RING + draw_arc(center, radius, 0.0, TAU, 64, color, 0.5 if hop % 5 == 0 else 0.3, true) + + +func _draw_sector_labels(center: Vector2) -> void: + var label_radius: float = (MIN_RING_RADIUS + 12 * RING_SPACING) * _zoom + for sector: String in SECTOR_LABELS: + var angle: float = SECTOR_ANGLE_CENTER.get(sector, 0.0) + var pos: Vector2 = center + Vector2(cos(angle), sin(angle)) * label_radius + var label: String = SECTOR_LABELS[sector] + var color: Color = SECTOR_COLORS.get(sector, COLOR_TEXT_DIM) + var font := get_theme_default_font() + var font_size: int = 10 + var text_size: Vector2 = font.get_string_size(label, HORIZONTAL_ALIGNMENT_LEFT, -1, font_size) + draw_string(font, pos - text_size / 2.0, label, HORIZONTAL_ALIGNMENT_LEFT, -1, font_size, color) + + +func _draw_systems(center: Vector2) -> void: + for node: Dictionary in _nodes: + var sid: String = node.get("system_id", "") + if not _node_positions.has(sid): + continue + var pos: Vector2 = center + _node_positions[sid] * _zoom + var sector: String = node.get("geographic_sector", "unknown") + var topology: String = node.get("gate_topology", "") + var color: Color = SECTOR_COLORS.get(sector, COLOR_TEXT_DIM) + var radius: float = _dot_radius(topology) + + # Gateway gets special treatment + if node.get("is_gateway", false): + color = COLOR_GATEWAY + radius = GATEWAY_RADIUS + + # Dim deep frontier slightly + if sector == "deep_frontier": + color.a = 0.7 + + # Hover highlight + if sid == _hovered_system and sid != _selected_system: + draw_arc(pos, radius + 3.0, 0.0, TAU, 16, Color(color.r, color.g, color.b, 0.4), 1.0, true) + + draw_circle(pos, radius, color) + + +func _draw_edges(center: Vector2) -> void: + for edge: Array in _edges: + if edge.size() < 2: + continue + var sid_a: String = edge[0] + var sid_b: String = edge[1] + if not _node_positions.has(sid_a) or not _node_positions.has(sid_b): + continue + var pos_a: Vector2 = center + _node_positions[sid_a] * _zoom + var pos_b: Vector2 = center + _node_positions[sid_b] * _zoom + + var alpha: float = EDGE_ALPHA + # Highlight edges connected to selected system + if _selected_system == sid_a or _selected_system == sid_b: + alpha = EDGE_SELECTED_ALPHA + + var color := Color(COLOR_TEXT.r, COLOR_TEXT.g, COLOR_TEXT.b, alpha) + draw_line(pos_a, pos_b, color, EDGE_WIDTH, true) + + +func _draw_selection(center: Vector2) -> void: + if not _node_positions.has(_selected_system): + return + var pos: Vector2 = center + _node_positions[_selected_system] * _zoom + draw_arc(pos, SELECTION_RING_RADIUS, 0.0, TAU, 24, COLOR_SELECTION, 1.2, true) + + # Draw label next to selection + var node: Dictionary = _node_lookup.get(_selected_system, {}) + var label: String = node.get("proper_name", _selected_system) + if label.is_empty(): + label = _selected_system + var font := get_theme_default_font() + draw_string(font, pos + Vector2(SELECTION_RING_RADIUS + 4, 4), label, HORIZONTAL_ALIGNMENT_LEFT, -1, 12, COLOR_SELECTION) + + +func _draw_info_panel(sz: Vector2) -> void: + var node: Dictionary = _node_lookup.get(_selected_system, {}) + if node.is_empty(): + return + + var panel_w: float = 220.0 + var panel_h: float = 130.0 + var margin: float = 16.0 + var panel_pos := Vector2(sz.x - panel_w - margin, margin) + + # Background + draw_rect(Rect2(panel_pos, Vector2(panel_w, panel_h)), COLOR_INFO_BG) + draw_rect(Rect2(panel_pos, Vector2(panel_w, panel_h)), Color(COLOR_TEXT.r, COLOR_TEXT.g, COLOR_TEXT.b, 0.15), false, 1.0) + + var font := get_theme_default_font() + var y: float = panel_pos.y + 20.0 + var x: float = panel_pos.x + 12.0 + var line_h: float = 18.0 + + # System name + var name: String = node.get("proper_name", "") + if name.is_empty(): + name = node.get("system_id", "Unknown") + draw_string(font, Vector2(x, y), name, HORIZONTAL_ALIGNMENT_LEFT, -1, 14, COLOR_TEXT) + y += line_h + + # System ID + draw_string(font, Vector2(x, y), node.get("system_id", ""), HORIZONTAL_ALIGNMENT_LEFT, -1, 10, COLOR_TEXT_DIM) + y += line_h + + # Sector + var sector: String = node.get("geographic_sector", "").replace("_", " ").capitalize() + var sector_color: Color = SECTOR_COLORS.get(node.get("geographic_sector", ""), COLOR_TEXT_DIM) + draw_string(font, Vector2(x, y), "Sector: " + sector, HORIZONTAL_ALIGNMENT_LEFT, -1, 11, sector_color) + y += line_h + + # Hop distance + draw_string(font, Vector2(x, y), "Hop distance: " + str(node.get("hop_distance", "?")), HORIZONTAL_ALIGNMENT_LEFT, -1, 11, COLOR_TEXT_DIM) + y += line_h + + # Topology + var topo: String = node.get("gate_topology", "").replace("_", " ").capitalize() + draw_string(font, Vector2(x, y), "Topology: " + topo, HORIZONTAL_ALIGNMENT_LEFT, -1, 11, COLOR_TEXT_DIM) + y += line_h + + # Gate connections + draw_string(font, Vector2(x, y), "Gates: " + str(node.get("aperture_count", 0)) + " apertures", HORIZONTAL_ALIGNMENT_LEFT, -1, 11, COLOR_TEXT_DIM) + + +func _draw_title() -> void: + var font := get_theme_default_font() + draw_string(font, Vector2(16, 28), "THE REACH — NAVIGATOR", HORIZONTAL_ALIGNMENT_LEFT, -1, 16, COLOR_TEXT) + draw_string(font, Vector2(16, 44), "Concord Assembly Gate Network — %d Systems" % _nodes.size(), HORIZONTAL_ALIGNMENT_LEFT, -1, 10, COLOR_TEXT_DIM) + + +func _dot_radius(topology: String) -> float: + match topology: + "hub": return DOT_RADIUS_HUB + "junction": return DOT_RADIUS_JUNCTION + "dead_end": return DOT_RADIUS_DEAD_END + _: return DOT_RADIUS_DEFAULT + + +# ============================================================================= +# Input — selection, pan, zoom +# ============================================================================= + +func _gui_input(event: InputEvent) -> void: + if event is InputEventMouseButton: + var mb := event as InputEventMouseButton + if mb.pressed: + match mb.button_index: + MOUSE_BUTTON_LEFT: + _handle_click(mb.position) + MOUSE_BUTTON_MIDDLE: + _is_panning = true + _pan_start = mb.position + _pan_start_offset = _pan_offset + MOUSE_BUTTON_WHEEL_UP: + _zoom = clampf(_zoom + ZOOM_STEP, ZOOM_MIN, ZOOM_MAX) + MOUSE_BUTTON_WHEEL_DOWN: + _zoom = clampf(_zoom - ZOOM_STEP, ZOOM_MIN, ZOOM_MAX) + else: + if mb.button_index == MOUSE_BUTTON_MIDDLE: + _is_panning = false + + elif event is InputEventMouseMotion: + var mm := event as InputEventMouseMotion + if _is_panning: + _pan_offset = _pan_start_offset + (mm.position - _pan_start) + else: + _update_hover(mm.position) + + +func _handle_click(pos: Vector2) -> void: + var sz: Vector2 = get_rect().size + var center: Vector2 = sz * MAP_CENTER_FRACTION + _pan_offset + + var best_dist: float = HIT_RADIUS + var best_sid: String = "" + + for node: Dictionary in _nodes: + var sid: String = node.get("system_id", "") + if not _node_positions.has(sid): + continue + var node_pos: Vector2 = center + _node_positions[sid] * _zoom + var dist: float = pos.distance_to(node_pos) + if dist < best_dist: + best_dist = dist + best_sid = sid + + if best_sid != "": + _selected_system = best_sid + _show_edges = true + else: + _selected_system = "" + _show_edges = false + + +func _update_hover(pos: Vector2) -> void: + var sz: Vector2 = get_rect().size + var center: Vector2 = sz * MAP_CENTER_FRACTION + _pan_offset + + var best_dist: float = HIT_RADIUS + var best_sid: String = "" + + for node: Dictionary in _nodes: + var sid: String = node.get("system_id", "") + if not _node_positions.has(sid): + continue + var node_pos: Vector2 = center + _node_positions[sid] * _zoom + var dist: float = pos.distance_to(node_pos) + if dist < best_dist: + best_dist = dist + best_sid = sid + + _hovered_system = best_sid diff --git a/client/ui/star_map.tscn b/client/ui/star_map.tscn new file mode 100644 index 000000000..0754fb0f6 --- /dev/null +++ b/client/ui/star_map.tscn @@ -0,0 +1,18 @@ +[gd_scene load_steps=2 format=3] + +[ext_resource type="Script" path="res://ui/star_map.gd" id="1_starmap"] + +; #674: Star map insert module — concentric hop-ring view of the Settled Reach gate network. +; Sector-colored, interactive selection, pan/zoom. Accessible from the insert UI. +; Data source: res://data/star_map_data.json (enriched from star-map.json + systems.db). +; Positioned as full-size overlay. Toggle visibility via set_insert_active() or toggle_visible(). + +[node name="StarMapRenderer" type="Control"] +layout_mode = 3 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +mouse_filter = 1 +script = ExtResource("1_starmap") diff --git a/tooling/generate-star-map-data.py b/tooling/generate-star-map-data.py new file mode 100755 index 000000000..daa730135 --- /dev/null +++ b/tooling/generate-star-map-data.py @@ -0,0 +1,108 @@ +#!/usr/bin/env python3 +"""Generate client/data/star_map_data.json from star-map.json + systems.db. + +Run from the project root (any worktree): + python3 tooling/generate-star-map-data.py + +Sources: + docs/design/star-map.json — graph topology (nodes + edges) + server/server/data/systems.db — proper names, geographic sectors + +Output: + client/data/star_map_data.json — self-contained client data for the star map UI +""" + +import json +import os +import sqlite3 +import sys + + +def find_file(candidates: list[str]) -> str | None: + for p in candidates: + if os.path.exists(p): + return p + return None + + +def main() -> None: + # Find star-map.json + star_map_path = find_file([ + "docs/design/star-map.json", + "../docs/design/star-map.json", + "../../docs/design/star-map.json", + ]) + if not star_map_path: + print("ERROR: docs/design/star-map.json not found", file=sys.stderr) + sys.exit(1) + + # Find systems.db + db_path = find_file([ + "server/server/data/systems.db", + "../server/server/data/systems.db", + "../../server/server/data/systems.db", + ]) + if not db_path: + print("ERROR: server/server/data/systems.db not found", file=sys.stderr) + sys.exit(1) + + # Load star map topology + with open(star_map_path) as f: + star_map = json.load(f) + + # Load DB data + conn = sqlite3.connect(db_path) + conn.row_factory = sqlite3.Row + cur = conn.cursor() + cur.execute( + "SELECT system_id, proper_name, geographic_sector, geographic_band " + "FROM star_systems" + ) + db_lookup = {row["system_id"]: dict(row) for row in cur.fetchall()} + conn.close() + + # Merge + nodes = [] + for n in star_map["nodes"]: + sid = n["system_id"] + db = db_lookup.get(sid, {}) + entry = { + "system_id": sid, + "proper_name": db.get("proper_name", ""), + "geographic_sector": db.get("geographic_sector", "unknown"), + "geographic_band": db.get("geographic_band", ""), + "gate_topology": n["gate_topology"], + "aperture_count": n["aperture_count"], + "gate_connections": n["gate_connections"], + "hop_distance": n["hop_distance_from_gateway"], + } + if n.get("is_gateway"): + entry["is_gateway"] = True + nodes.append(entry) + + nodes.sort(key=lambda x: (x["hop_distance"], x["system_id"])) + + output = { + "_meta": { + "generated_from": f"{star_map_path} + {db_path}", + "system_count": len(nodes), + "edge_count": len(star_map["edges"]), + "note": "Client-side star map data. Regenerate with: tooling/generate-star-map-data.py", + }, + "nodes": nodes, + "edges": star_map["edges"], + } + + # Write output + out_path = find_file(["client/data"]) or "client/data" + os.makedirs(out_path, exist_ok=True) + out_file = os.path.join(out_path, "star_map_data.json") + with open(out_file, "w") as f: + json.dump(output, f, indent=2, ensure_ascii=False) + + print(f"Generated {out_file}") + print(f" Nodes: {len(nodes)}, Edges: {len(star_map['edges'])}") + + +if __name__ == "__main__": + main()