158 lines
5.5 KiB
Python
158 lines
5.5 KiB
Python
"""
|
|
build_ecdis_manual.py — Reconstruye los GeoJSONs del ECDIS manual chart
|
|
desde los CSVs fuente. Funciona igual para CUALQUIER puerto.
|
|
|
|
Uso:
|
|
python build_ecdis_manual.py capas_ctg BAHÍA_DE_CARTAGENA
|
|
python build_ecdis_manual.py capas_baq BARRANQUILLA
|
|
python build_ecdis_manual.py capas_ptco BUENAVENTURA
|
|
|
|
El script:
|
|
1. Lee todos los *.csv del directorio de capas
|
|
2. Genera un GeoJSON por feat_type con atributos S-57 limpios
|
|
3. Escribe los GeoJSONs en el directorio manual del ECDIS
|
|
|
|
Reglas de datos:
|
|
- feat_type determina el archivo de salida (LIGHTS.geojson, BOYCAR.geojson...)
|
|
- SIGGRP "**" → null (limpia basura de GDAL)
|
|
- LITCHR_TXT se convierte a código S-57 si LITCHR está vacío
|
|
- COLOUR_TXT se convierte a código S-57 si COLOUR está vacío
|
|
- CATCAM y ORIENT se pasan directo al GeoJSON
|
|
- INFORM se preserva completo
|
|
"""
|
|
import csv, json, sys, argparse
|
|
from pathlib import Path
|
|
|
|
ECDIS_DATA = Path(__file__).parent.parent / "AR ECDIS" / "webecdis" / "data" / "charts" / "manual"
|
|
|
|
LITCHR_TXT = {
|
|
"f":"1","fl":"2","lfl":"3","q":"4","vq":"5","uq":"6",
|
|
"iso":"7","oc":"8","iq":"9","mo":"12","ffl":"13",
|
|
}
|
|
COLOUR_TXT = {
|
|
"white":"1","black":"2","red":"3","green":"4","blue":"5",
|
|
"yellow":"6","grey":"7","brown":"8","amber":"9","violet":"10",
|
|
"orange":"11","magenta":"12",
|
|
}
|
|
|
|
def _fval(s):
|
|
s = (s or "").strip()
|
|
if not s or all(c in "* " for c in s): return None
|
|
try: return float(s)
|
|
except: return None
|
|
|
|
def _ival(s):
|
|
s = (s or "").strip()
|
|
if not s or all(c in "* " for c in s): return None
|
|
try: return int(float(s))
|
|
except: return None
|
|
|
|
def _sval(s):
|
|
s = (s or "").strip()
|
|
return s if s and not all(c in "* " for c in s) else None
|
|
|
|
def _parse_litchr(row):
|
|
v = _ival(row.get("LITCHR", ""))
|
|
if v is not None: return v
|
|
txt = (row.get("LITCHR_TXT") or "").lower().split("(")[0].strip()
|
|
c = LITCHR_TXT.get(txt)
|
|
return int(c) if c else None
|
|
|
|
def _parse_colour(row):
|
|
v = (row.get("COLOUR") or "").strip()
|
|
if v and not all(c in "* " for c in v):
|
|
parts = [p.strip() for p in v.split(",") if p.strip().isdigit()]
|
|
if parts: return [int(p) for p in parts]
|
|
txt = (row.get("COLOUR_TXT") or "").lower().strip()
|
|
c = COLOUR_TXT.get(txt)
|
|
return [int(c)] if c else None
|
|
|
|
def build(capas_dir: Path, chart_name: str, ecdis_data: Path):
|
|
out_dir = ecdis_data / chart_name
|
|
if not out_dir.exists():
|
|
print(f"[WARN] Directorio ECDIS no existe: {out_dir}")
|
|
print(f" Creando...")
|
|
out_dir.mkdir(parents=True)
|
|
|
|
layers: dict[str, list] = {}
|
|
rcid = 1
|
|
|
|
for csv_file in sorted(capas_dir.glob("*.csv")):
|
|
default_layer = csv_file.stem.upper()
|
|
with open(csv_file, newline="", encoding="utf-8-sig") as f:
|
|
for row in csv.DictReader(f):
|
|
try:
|
|
lon = float(row.get("lon", "").strip())
|
|
lat = float(row.get("lat", "").strip())
|
|
except (ValueError, AttributeError):
|
|
continue
|
|
|
|
feat_type = (_sval(row.get("feat_type", "")) or default_layer).upper()
|
|
|
|
props = {
|
|
"RCID": rcid,
|
|
"PRIM": 1,
|
|
"GRUP": 1,
|
|
"OBJL": 75,
|
|
"RVER": 1,
|
|
"AGEN": 999,
|
|
"FIDN": rcid,
|
|
"FIDS": 1,
|
|
"LNAM": f"03E7{rcid:08X}0001",
|
|
"OBJNAM": _sval(row.get("OBJNAM", "")),
|
|
"LITCHR": _parse_litchr(row),
|
|
"COLOUR": _parse_colour(row),
|
|
"SIGGRP": _sval(row.get("SIGGRP", "")),
|
|
"SIGPER": _fval(row.get("SIGPER", "")),
|
|
"VALNMR": _fval(row.get("VALNMR", "")),
|
|
"HEIGHT": _fval(row.get("HEIGHT", "")),
|
|
"ORIENT": _fval(row.get("ORIENT", "")),
|
|
"CATCAM": _ival(row.get("CATCAM", "")),
|
|
"INFORM": _sval(row.get("INFORM", "")),
|
|
"NOBJNM": _sval(row.get("NOBJNM", "")),
|
|
}
|
|
|
|
feat = {
|
|
"type": "Feature",
|
|
"geometry": {
|
|
"coordinates": [lon, lat],
|
|
"type": "Point",
|
|
"geometries": None
|
|
},
|
|
"properties": props
|
|
}
|
|
|
|
layers.setdefault(feat_type, []).append(feat)
|
|
rcid += 1
|
|
|
|
total = 0
|
|
for layer, feats in sorted(layers.items()):
|
|
fc = {"type": "FeatureCollection", "features": feats}
|
|
out_file = out_dir / f"{layer}.geojson"
|
|
out_file.write_text(json.dumps(fc, ensure_ascii=False), encoding="utf-8")
|
|
print(f" {layer}.geojson : {len(feats)} features")
|
|
total += feats.__len__()
|
|
|
|
print(f"\nOK {total} features en {out_dir}")
|
|
return total
|
|
|
|
|
|
def main():
|
|
ap = argparse.ArgumentParser(description="Rebuild ECDIS manual GeoJSONs from CSV layers")
|
|
ap.add_argument("capas_dir", help="Directorio con los CSVs (capas_ctg, capas_baq...)")
|
|
ap.add_argument("chart_name", help="Nombre del chart en ECDIS (BAHÍA_DE_CARTAGENA, BARRANQUILLA...)")
|
|
ap.add_argument("--ecdis", default=str(ECDIS_DATA),
|
|
help=f"Ruta base de charts/manual del ECDIS [default: {ECDIS_DATA}]")
|
|
args = ap.parse_args()
|
|
|
|
capas = Path(args.capas_dir)
|
|
if not capas.exists():
|
|
print(f"ERROR: No existe {capas}")
|
|
sys.exit(1)
|
|
|
|
build(capas, args.chart_name, Path(args.ecdis))
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|