montana/Русский/Логистика/assemble_ports.py

162 lines
4.7 KiB
Python
Raw Normal View History

#!/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()