""" 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()