Initial commit — QGIS S-57 Converter
This commit is contained in:
@@ -0,0 +1,157 @@
|
||||
"""
|
||||
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()
|
||||
Reference in New Issue
Block a user