162 lines
4.7 KiB
Python
162 lines
4.7 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
Assemble world_ports.json from generate_ports.py + ports_part2.py + ports_part3.py
|
|
Run: python assemble_ports.py
|
|
"""
|
|
import json
|
|
import os
|
|
import sys
|
|
|
|
# Auto-assign region from coordinates (matches marinetraffic_parser.py logic)
|
|
def assign_region(lat, lon):
|
|
if lon < -100 and lat > 25:
|
|
return 'USWC'
|
|
elif lon < -30 and lat > 25:
|
|
return 'USEC'
|
|
elif lon < -30 and -5 < lat <= 25:
|
|
return 'CARIB'
|
|
elif lon < -70 and lat <= -5:
|
|
return 'SAW'
|
|
elif lon < -30 and lat <= -5:
|
|
return 'SAE'
|
|
elif lon > 100 and lat < -10:
|
|
return 'AUSNZ'
|
|
elif lon > 100:
|
|
return 'EASIA'
|
|
elif 60 < lon <= 100 and lat > 0:
|
|
return 'SASIA'
|
|
elif 40 < lon <= 60 and lat > 10:
|
|
return 'GULF'
|
|
elif 10 < lon < 45 and lat < -20:
|
|
return 'SAFR'
|
|
elif 30 < lon <= 45 and -10 < lat < 20:
|
|
return 'ERED'
|
|
elif -5 < lon < 15 and -5 < lat < 15:
|
|
return 'WAFR'
|
|
elif 25 < lon <= 40 and 40 < lat <= 48:
|
|
return 'BSEA'
|
|
elif lon < 40 and lat > 48:
|
|
return 'NEUR'
|
|
elif -10 <= lon <= 40 and 25 < lat <= 48:
|
|
return 'MED'
|
|
else:
|
|
return 'OTHER'
|
|
|
|
# Size code mapping
|
|
SIZE_MAP = {'VL': 'large', 'L': 'large', 'M': 'medium', 'S': 'small',
|
|
'large': 'large', 'medium': 'medium', 'small': 'small'}
|
|
|
|
def load_part1():
|
|
"""Load ports from generate_ports.py (NEUR + BALTIC + SEUR)"""
|
|
# Execute the script to get 'ports' list
|
|
ns = {}
|
|
script = os.path.join(os.path.dirname(__file__), 'generate_ports.py')
|
|
with open(script, 'r', encoding='utf-8') as f:
|
|
code = f.read()
|
|
exec(code, ns)
|
|
return ns.get('ports', [])
|
|
|
|
def load_part2():
|
|
"""Load ports from ports_part2.py (GULF + ERED + WAFR + SAFR)"""
|
|
ns = {}
|
|
script = os.path.join(os.path.dirname(__file__), 'ports_part2.py')
|
|
if not os.path.exists(script):
|
|
print(f"WARNING: {script} not found, skipping")
|
|
return []
|
|
with open(script, 'r', encoding='utf-8') as f:
|
|
code = f.read()
|
|
exec(code, ns)
|
|
return ns.get('part2_ports', [])
|
|
|
|
def load_part3():
|
|
"""Load ports from ports_part3.py (Americas + AUSNZ)"""
|
|
ns = {}
|
|
script = os.path.join(os.path.dirname(__file__), 'ports_part3.py')
|
|
if not os.path.exists(script):
|
|
print(f"WARNING: {script} not found, skipping")
|
|
return []
|
|
with open(script, 'r', encoding='utf-8') as f:
|
|
code = f.read()
|
|
exec(code, ns)
|
|
return ns.get('part3_ports', [])
|
|
|
|
def main():
|
|
all_ports = []
|
|
|
|
print("Loading Part 1 (Europe)...")
|
|
p1 = load_part1()
|
|
print(f" -> {len(p1)} ports")
|
|
all_ports.extend(p1)
|
|
|
|
print("Loading Part 2 (Middle East + Africa)...")
|
|
p2 = load_part2()
|
|
print(f" -> {len(p2)} ports")
|
|
all_ports.extend(p2)
|
|
|
|
print("Loading Part 3 (Americas + Pacific)...")
|
|
p3 = load_part3()
|
|
print(f" -> {len(p3)} ports")
|
|
all_ports.extend(p3)
|
|
|
|
print(f"\nTotal raw ports: {len(all_ports)}")
|
|
|
|
# Deduplicate by key, first occurrence wins
|
|
seen = {}
|
|
for port in all_ports:
|
|
key = port.get('key', '')
|
|
if not key:
|
|
continue
|
|
if key not in seen:
|
|
seen[key] = port
|
|
|
|
# Normalize and build final dict
|
|
final = {}
|
|
for key, port in seen.items():
|
|
# Normalize size
|
|
port['size'] = SIZE_MAP.get(port.get('size', 'M'), 'medium')
|
|
# Override region with coordinate-based for consistency
|
|
port['region'] = assign_region(port['lat'], port['lon'])
|
|
# Add radius_nm based on size
|
|
if port['size'] == 'large':
|
|
port['radius_nm'] = 12
|
|
elif port['size'] == 'medium':
|
|
port['radius_nm'] = 8
|
|
else:
|
|
port['radius_nm'] = 5
|
|
final[key] = port
|
|
|
|
# Validate
|
|
errors = 0
|
|
for key, port in final.items():
|
|
if not (-90 <= port['lat'] <= 90):
|
|
print(f" ERROR: {key} lat={port['lat']}")
|
|
errors += 1
|
|
if not (-180 <= port['lon'] <= 180):
|
|
print(f" ERROR: {key} lon={port['lon']}")
|
|
errors += 1
|
|
|
|
if errors:
|
|
print(f"\n{errors} validation errors!")
|
|
sys.exit(1)
|
|
|
|
# Write JSON
|
|
output = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'world_ports.json')
|
|
with open(output, 'w', encoding='utf-8') as f:
|
|
json.dump(final, f, indent=1, ensure_ascii=False)
|
|
|
|
print(f"\nGenerated {len(final)} unique ports -> {output}")
|
|
|
|
# Stats by region
|
|
from collections import Counter
|
|
regions = Counter(p['region'] for p in final.values())
|
|
for r, c in sorted(regions.items(), key=lambda x: -x[1]):
|
|
print(f" {r}: {c}")
|
|
|
|
# Stats by size
|
|
sizes = Counter(p['size'] for p in final.values())
|
|
for s, c in sorted(sizes.items()):
|
|
print(f" {s}: {c}")
|
|
|
|
if __name__ == '__main__':
|
|
main()
|