Initial commit — QGIS S-57 Converter

This commit is contained in:
2026-05-04 23:03:19 -04:00
commit eb12a58cb7
41 changed files with 8896 additions and 0 deletions
+16
View File
@@ -0,0 +1,16 @@
# Python
__pycache__/
*.pyc
*.pyo
.venv/
venv/
# PyInstaller — carpeta de build intermedia (no necesaria)
build/
# Logs
build_log.txt
# OS
.DS_Store
Thumbs.db
Binary file not shown.
+127
View File
@@ -0,0 +1,127 @@
========================================
QGIS → S-57 Converter — Installation
========================================
REQUIRED: Python 3.9+
REQUIRED: GDAL with S-57 support
─────────────────────────────────────────
OPTION A: Windows — Easiest (OSGeo4W)
─────────────────────────────────────────
1. Download OSGeo4W: https://trac.osgeo.org/osgeo4w/
2. Run installer → Advanced Install → Select:
- gdal
- python3-gdal
- python3-geopandas (or install via pip)
3. Use the "OSGeo4W Shell" to run the converter:
cd "D:\Proyectos Software\QGISS57Converter"
python converter.py myproject.qgs
─────────────────────────────────────────
OPTION B: Conda / Mamba (Recommended)
─────────────────────────────────────────
conda create -n s57 python=3.11
conda activate s57
conda install -c conda-forge gdal geopandas
pip install lxml
cd "D:\Proyectos Software\QGISS57Converter"
python converter.py myproject.qgs
─────────────────────────────────────────
OPTION C: QGIS Python environment
─────────────────────────────────────────
QGIS already includes GDAL. Run from QGIS Python console:
import sys
sys.path.append(r"D:\Proyectos Software\QGISS57Converter")
from converter import convert
convert("myproject.qgs", None, None, False, True, True)
─────────────────────────────────────────
INSTALL other dependencies
─────────────────────────────────────────
pip install geopandas lxml pyproj
─────────────────────────────────────────
VERIFY installation
─────────────────────────────────────────
python -c "from osgeo import gdal; drv = gdal.GetDriverByName('S57'); print('S-57 driver:', drv.GetDescription() if drv else 'NOT AVAILABLE')"
========================================
USAGE
========================================
# List layers and their S-57 mapping:
python converter.py project.qgs --list
# Convert:
python converter.py project.qgs
# Custom output name:
python converter.py project.qgs --output CO1CO01M.000
# Custom config:
python converter.py project.qgs --config my_config.json
# No prompts (convert all mapped layers):
python converter.py project.qgs --force
========================================
LAYER NAMING — IMPORTANT
========================================
You have THREE options for mapping your QGIS layers to S-57:
OPTION 1 (easiest): Name your layers directly with S-57 acronyms
- Layer named "COALNE" → coastline
- Layer named "DEPARE" → depth area
- Layer named "SOUNDG" → soundings
See s57_objects.json for all available object classes.
OPTION 2: Name layers in Spanish or English (auto-detected)
- Layer named "costa" or "coastline" → auto → COALNE
- Layer named "fondos" or "batimetria" → auto → DEPARE
- Layer named "sondas" → auto → SOUNDG
See cell_config.json → "layer_mappings" for full list.
OPTION 3: Add custom mappings to cell_config.json
"layer_mappings": {
"mi_capa_costa": "COALNE",
"datos_profundidad": "DEPARE"
}
========================================
CELL NAMING CONVENTION (IHO S-57)
========================================
Format: CC1AA##X.000
CC = 2-letter country code (CO=Colombia, US=USA, etc.)
1 = Navigational Purpose (1=overview...6=berthing)
AA = 2-letter area code
## = serial number (01, 02...)
X = compilation scale indicator (M=medium)
Example: CO3CA01M.000 = Colombia, scale 1:50000, Caribbean area
Edit "cell_name" in cell_config.json accordingly.
========================================
COMMON S-57 OBJECT CLASSES
========================================
COALNE Coastline (lines)
LNDARE Land Area (polygons)
DEPARE Depth Area (polygons) — needs DRVAL1, DRVAL2 attributes
DEPCNT Depth Contour (lines) — needs VALDCO attribute
SOUNDG Soundings (points) — needs VALSOU (depth value) attribute
LIGHTS Lights (points)
BUOYLAT Lateral Buoy (points)
BCNLAT Lateral Beacon (points)
ACHARE Anchorage Area (polygons)
HRBARE Harbour Area (polygons)
BERTHS Berth (polygons/lines)
OBSTRN Obstruction (any)
WRECKS Wreck (polygons/points)
FAIRWY Fairway (polygons)
RESARE Restricted Area (polygons)
RIVERS River (polygons/lines)
M_COVR Coverage (polygons) — required in valid ENCs
See s57_objects.json for complete list.
+695
View File
@@ -0,0 +1,695 @@
<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>QGISS57Converter — Manual</title>
<style>
:root {
--bg: #0d1117; --panel: #161b22; --border: #30363d;
--accent: #1e7fc8; --accent2: #388bfd;
--green: #2ea043; --yellow: #d29922; --red: #da3633;
--text: #e6edf3; --dim: #8b949e; --mono: #f0a500;
}
* { box-sizing: border-box; margin: 0; padding: 0; }
body { background: var(--bg); color: var(--text); font-family: "Segoe UI", system-ui, sans-serif; font-size: 14px; }
/* ── layout ── */
#sidebar { position: fixed; top: 0; left: 0; width: 240px; height: 100vh; background: var(--panel);
border-right: 1px solid var(--border); overflow-y: auto; padding: 16px 0; z-index: 10; }
#content { margin-left: 240px; padding: 32px 40px; max-width: 960px; }
/* ── header ── */
.app-title { padding: 0 16px 16px; border-bottom: 1px solid var(--border); margin-bottom: 8px; }
.app-title h2 { color: var(--accent2); font-size: 15px; font-weight: 700; }
.app-title p { color: var(--dim); font-size: 11px; margin-top: 2px; }
/* ── search ── */
#search-box { margin: 8px 12px 12px; width: calc(100% - 24px);
background: var(--bg); border: 1px solid var(--border);
color: var(--text); padding: 6px 10px; border-radius: 6px; font-size: 12px; }
#search-box:focus { outline: none; border-color: var(--accent); }
/* ── nav ── */
.nav-section { padding: 6px 16px 2px; font-size: 11px; font-weight: 700;
color: var(--dim); text-transform: uppercase; letter-spacing: .8px; }
.nav-link { display: block; padding: 5px 16px; color: var(--dim); text-decoration: none;
font-size: 13px; border-left: 2px solid transparent; transition: all .15s; }
.nav-link:hover, .nav-link.active { color: var(--text); border-left-color: var(--accent); background: rgba(56,139,253,.08); }
/* ── content ── */
section { margin-bottom: 48px; scroll-margin-top: 24px; }
h1 { font-size: 26px; font-weight: 700; color: var(--text); margin-bottom: 6px; }
h2 { font-size: 18px; font-weight: 700; color: var(--accent2); margin: 32px 0 12px;
padding-bottom: 8px; border-bottom: 1px solid var(--border); }
h3 { font-size: 14px; font-weight: 700; color: var(--text); margin: 20px 0 8px; }
p { color: var(--dim); line-height: 1.7; margin-bottom: 10px; }
code { background: var(--panel); border: 1px solid var(--border); border-radius: 4px;
padding: 1px 6px; font-family: "Consolas", monospace; font-size: 12px; color: var(--mono); }
.subtitle { color: var(--dim); font-size: 14px; margin-bottom: 28px; }
/* ── tables ── */
table { width: 100%; border-collapse: collapse; margin-bottom: 20px; font-size: 13px; }
th { background: #1c2230; color: var(--accent2); font-weight: 600; text-align: left;
padding: 9px 12px; border-bottom: 2px solid var(--accent); }
td { padding: 7px 12px; border-bottom: 1px solid var(--border); vertical-align: top; }
tr:hover td { background: rgba(56,139,253,.05); }
.tag { display: inline-block; padding: 2px 8px; border-radius: 4px; font-size: 11px; font-weight: 700; }
.tag-point { background: #1a3a5c; color: #58a6ff; }
.tag-line { background: #1a3d2b; color: #3fb950; }
.tag-area { background: #3d2a1a; color: #f0883e; }
.tag-meta { background: #2d1f3d; color: #bc8cff; }
.badge { display: inline-block; padding: 1px 7px; border-radius: 10px; font-size: 11px; }
.b-green { background: #1a3a2a; color: #3fb950; border: 1px solid #2ea043; }
.b-blue { background: #1a2d4a; color: #58a6ff; border: 1px solid #1e7fc8; }
.b-yellow { background: #3a2e10; color: #e3b341; border: 1px solid #9e6a03; }
.b-red { background: #3a1a1a; color: #f85149; border: 1px solid #da3633; }
/* ── callouts ── */
.tip, .warn, .info { border-radius: 6px; padding: 12px 16px; margin-bottom: 16px; border-left: 3px solid; }
.tip { background: #1a3a2a; border-color: var(--green); }
.warn { background: #3a2e10; border-color: var(--yellow); }
.info { background: #1a2d4a; border-color: var(--accent); }
.tip strong, .warn strong, .info strong { display: block; margin-bottom: 4px; }
.tip strong { color: #3fb950; }
.warn strong { color: #e3b341; }
.info strong { color: #58a6ff; }
/* ── steps ── */
.steps { counter-reset: step; list-style: none; padding: 0; }
.steps li { counter-increment: step; display: flex; gap: 16px; margin-bottom: 16px; }
.steps li::before { content: counter(step); min-width: 28px; height: 28px; border-radius: 50%;
background: var(--accent); color: #fff; font-weight: 700; font-size: 13px;
display: flex; align-items: center; justify-content: center; flex-shrink: 0; }
.steps li .step-body { padding-top: 4px; }
.steps li .step-body strong { color: var(--text); display: block; margin-bottom: 4px; }
.steps li .step-body p { margin: 0; }
/* ── colour swatches ── */
.swatch { display: inline-block; width: 14px; height: 14px; border-radius: 3px; vertical-align: middle; margin-right: 6px; border: 1px solid rgba(255,255,255,.2); }
/* ── search highlight ── */
.hl { background: rgba(210,153,34,.35); border-radius: 2px; }
.hidden-row { display: none; }
/* ── responsive ── */
@media (max-width: 700px) {
#sidebar { display: none; }
#content { margin-left: 0; padding: 20px; }
}
</style>
</head>
<body>
<!-- ── SIDEBAR ──────────────────────────────────────────────────────────── -->
<nav id="sidebar">
<div class="app-title">
<h2>QGISS57Converter</h2>
<p>Manual de referencia v1.0</p>
</div>
<input id="search-box" type="search" placeholder="Buscar...">
<div class="nav-section">Inicio</div>
<a class="nav-link" href="#inicio">Introduccion</a>
<a class="nav-link" href="#flujo">Flujo de trabajo</a>
<div class="nav-section">Capas</div>
<a class="nav-link" href="#puntuales">Objetos puntuales</a>
<a class="nav-link" href="#lineales">Objetos lineales</a>
<a class="nav-link" href="#areas">Objetos de area</a>
<div class="nav-section">Atributos por tipo</div>
<a class="nav-link" href="#attr-generales">Atributos generales</a>
<a class="nav-link" href="#attr-boylat">BOYLAT — Boyas laterales</a>
<a class="nav-link" href="#attr-bcnlat">BCNLAT — Balizas/faros orilla</a>
<a class="nav-link" href="#attr-boycar">BOYCAR — Boyas cardinales</a>
<a class="nav-link" href="#attr-boyisd">BOYISD — Peligro aislado</a>
<a class="nav-link" href="#attr-boyspp">BOYSPP — Marcas especiales</a>
<a class="nav-link" href="#attr-lights">LIGHTS — Luces independientes</a>
<a class="nav-link" href="#codigos">Tablas de codigos S-57</a>
<div class="nav-section">Referencia</div>
<a class="nav-link" href="#csv-directo">Formato CSV directo</a>
<a class="nav-link" href="#config">cell_config.json</a>
<a class="nav-link" href="#ejemplos">Ejemplos de SHP</a>
</nav>
<!-- ── CONTENT ──────────────────────────────────────────────────────────── -->
<main id="content">
<!-- INICIO -->
<section id="inicio">
<h1>Manual QGISS57Converter</h1>
<p class="subtitle">Convierte proyectos QGIS (.qgz/.qgs) a cartas naúticas S-57 (.000) válidas para cualquier ECDIS.</p>
<div class="info">
<strong>¿Qué hace esta app?</strong>
Lee tus capas QGIS con SHP y las convierte a formato S-57 ISO 8211 — el estándar IHO para cartas electrónicas de navegación (ENC). El archivo .000 resultante puede cargarse en cualquier ECDIS que soporte GDAL, incluyendo el AR ECDIS.
</div>
<div class="warn">
<strong>Requisito del sistema</strong>
El entorno conda <code>s57</code> debe estar instalado en <code>D:\Miniconda\envs\s57</code>. La app lo llama automáticamente en el backend.
</div>
</section>
<!-- FLUJO -->
<section id="flujo">
<h2>Flujo de trabajo</h2>
<ol class="steps">
<li>
<div class="step-body">
<strong>Crear proyecto QGIS</strong>
<p>En QGIS, crea capas SHP con los nombres de la sección "Objetos puntuales / lineales / de área". Cada capa debe estar en coordenadas WGS84 (EPSG:4326) o el converter la reproyecta automáticamente.</p>
</div>
</li>
<li>
<div class="step-body">
<strong>Agregar atributos al SHP</strong>
<p>Agrega columnas a tu SHP con los nombres de la sección "Atributos". Por ejemplo, para boyas: columnas <code>nombre</code>, <code>catlam</code>, <code>colour</code>, <code>litchr</code>, <code>sigper</code>, <code>alcance</code>.</p>
</div>
</li>
<li>
<div class="step-body">
<strong>Guardar como .qgz</strong>
<p>Proyecto → Guardar como → formato .qgz (archivo comprimido que incluye el .qgs y los SHP embebidos).</p>
</div>
</li>
<li>
<div class="step-body">
<strong>Abrir QGISS57Converter</strong>
<p>Haz clic en <strong>Examinar…</strong>, selecciona tu .qgz, elige la carpeta de salida, y presiona <strong>▶ Convertir</strong>.</p>
</div>
</li>
<li>
<div class="step-body">
<strong>Cargar en el ECDIS</strong>
<p>El archivo .000 generado se puede instalar directamente en AR ECDIS desde el menú Charts → Instalar carta.</p>
</div>
</li>
</ol>
</section>
<!-- PUNTUALES -->
<section id="puntuales">
<h2>Objetos puntuales</h2>
<p>Nombra tu capa QGIS con cualquier texto de la columna "Nombres reconocidos" (sin importar mayúsculas). También puedes usar el acrónimo S-57 directamente.</p>
<table>
<thead><tr><th>Acrónimo S-57</th><th>Descripción</th><th>Nombres reconocidos en QGIS</th></tr></thead>
<tbody>
<tr><td><code>BOYLAT</code> <span class="tag tag-point">Punto</span></td><td>Boya lateral (babor/estribor)</td><td>boyas, buoys</td></tr>
<tr><td><code>BOYCAR</code> <span class="tag tag-point">Punto</span></td><td>Boya cardinal (N/S/E/W)</td><td>boycar</td></tr>
<tr><td><code>BOYISD</code> <span class="tag tag-point">Punto</span></td><td>Boya de peligro aislado</td><td>boyisd</td></tr>
<tr><td><code>BOYSAW</code> <span class="tag tag-point">Punto</span></td><td>Boya de aguas seguras</td><td>boysaw</td></tr>
<tr><td><code>BCNLAT</code> <span class="tag tag-point">Punto</span></td><td>Baliza lateral</td><td>balizas, beacons</td></tr>
<tr><td><code>BCNSPP</code> <span class="tag tag-point">Punto</span></td><td>Baliza especial</td><td>bcnspp</td></tr>
<tr><td><code>LIGHTS</code> <span class="tag tag-point">Punto</span></td><td>Luz / faro</td><td>luces, lights, faroles</td></tr>
<tr><td><code>LNDMRK</code> <span class="tag tag-point">Punto</span></td><td>Hito en tierra (torre, tanque…)</td><td>Puntos del Terreno, landmark</td></tr>
<tr><td><code>SOUNDG</code> <span class="tag tag-point">Punto</span></td><td>Sonda batimétrica</td><td>sondas, soundings, profundidades</td></tr>
<tr><td><code>UWTROC</code> <span class="tag tag-point">Punto</span></td><td>Roca sumergida / a flor de agua</td><td>rocas, rocks</td></tr>
<tr><td><code>WRECKS</code> <span class="tag tag-point">Punto</span></td><td>Naufragio</td><td>naufragio, wreck</td></tr>
<tr><td><code>OBSTRN</code> <span class="tag tag-point">Punto</span></td><td>Obstrucción</td><td>obstruccion, obstruction</td></tr>
</tbody>
</table>
</section>
<!-- LINEALES -->
<section id="lineales">
<h2>Objetos lineales</h2>
<table>
<thead><tr><th>Acrónimo S-57</th><th>Descripción</th><th>Nombres reconocidos en QGIS</th></tr></thead>
<tbody>
<tr><td><code>COALNE</code> <span class="tag tag-line">Línea</span></td><td>Línea de costa</td><td>Linderos, coastline, costa, linea_de_costa</td></tr>
<tr><td><code>DEPCNT</code> <span class="tag tag-line">Línea</span></td><td>Curva batimétrica (isobata)</td><td>isobata, curvas_nivel, depth_contour</td></tr>
<tr><td><code>CBLSUB</code> <span class="tag tag-line">Línea</span></td><td>Cable submarino</td><td>cable</td></tr>
<tr><td><code>PIPSOL</code> <span class="tag tag-line">Línea</span></td><td>Tubería submarina / en tierra</td><td>tuberia</td></tr>
<tr><td><code>RIVERS</code> <span class="tag tag-line">Línea</span></td><td>Río / canal</td><td>rio, river</td></tr>
</tbody>
</table>
</section>
<!-- AREAS -->
<section id="areas">
<h2>Objetos de área</h2>
<table>
<thead><tr><th>Acrónimo S-57</th><th>Descripción</th><th>Nombres reconocidos en QGIS</th></tr></thead>
<tbody>
<tr><td><code>LNDARE</code> <span class="tag tag-area">Área</span></td><td>Área terrestre</td><td>Área Terreno, tierra, land</td></tr>
<tr><td><code>DEPARE</code> <span class="tag tag-area">Área</span></td><td>Área de profundidad</td><td>fondos, batimetria, depth_area</td></tr>
<tr><td><code>FAIRWY</code> <span class="tag tag-area">Área</span></td><td>Canal de navegación</td><td>canal_navegacion, fairway</td></tr>
<tr><td><code>RESARE</code> <span class="tag tag-area">Área</span></td><td>Área restringida</td><td>zona_restringida, restricted</td></tr>
<tr><td><code>ACHARE</code> <span class="tag tag-area">Área</span></td><td>Área de fondeo</td><td>fondeadero, anchorage</td></tr>
<tr><td><code>HRBARE</code> <span class="tag tag-area">Área</span></td><td>Área portuaria</td><td>puerto, harbor</td></tr>
<tr><td><code>BERTHS</code> <span class="tag tag-area">Área</span></td><td>Atraque / muelle</td><td>atraque, berth</td></tr>
<tr><td><code>SBDARE</code> <span class="tag tag-area">Área</span></td><td>Área de fondo marino</td><td>fondo_marino, seabed</td></tr>
</tbody>
</table>
</section>
<!-- ATRIBUTOS GENERALES -->
<section id="attr-generales">
<h2>Atributos generales</h2>
<p>Agrega estas columnas en tu SHP. El nombre de columna es flexible — el converter usa las palabras clave de la tabla.</p>
<table>
<thead><tr><th>Columna en el SHP</th><th>Atributo S-57</th><th>Descripción</th></tr></thead>
<tbody>
<tr><td><code>nombre</code> / <code>name</code></td><td><code>OBJNAM</code></td><td>Nombre del objeto — aparece en tooltip del ECDIS</td></tr>
<tr><td><code>altura</code> / <code>height</code></td><td><code>HEIGHT</code></td><td>Altura sobre el nivel del mar (metros)</td></tr>
<tr><td><code>colour</code> / <code>color</code></td><td><code>COLOUR</code></td><td>Color — ver tabla de códigos abajo</td></tr>
<tr><td><code>profundidad</code> / <code>depth</code></td><td><code>DRVAL1</code></td><td>Profundidad mínima (metros)</td></tr>
<tr><td><code>depth_max</code></td><td><code>DRVAL2</code></td><td>Profundidad máxima (metros)</td></tr>
<tr><td><code>sonda</code> / <code>sounding</code></td><td><code>VALSOU</code></td><td>Valor de sonda (metros)</td></tr>
<tr><td><code>contour</code> / <code>valor</code></td><td><code>VALDCO</code></td><td>Valor de curva batimétrica (metros)</td></tr>
<tr><td><code>estado</code> / <code>status</code></td><td><code>STATUS</code></td><td>Estado: 1=permanente, 2=ocasional, 7=privado</td></tr>
</tbody>
</table>
</section>
<!-- BOYLAT -->
<section id="attr-boylat">
<h2>BOYLAT — Boyas laterales</h2>
<div class="tip">
<strong>Capa QGIS:</strong> nombrar <code>boyas</code> / <code>boylat</code> / <code>lateral</code> — geometria Punto — CRS EPSG:4326
</div>
<table>
<thead><tr><th>Columna SHP / CSV</th><th>Atributo S-57</th><th>Descripcion y valores</th><th>Requerido</th></tr></thead>
<tbody>
<tr><td><code>nombre</code> / <code>OBJNAM</code></td><td><code>OBJNAM</code></td><td>Nombre. Ej: <em>"Boya No.1"</em></td><td></td></tr>
<tr><td><code>catlam</code> / <code>CATLAM</code></td><td><code>CATLAM</code></td><td><span class="badge b-green">1 = Babor (VERDE en IALA-B)</span> &nbsp; <span class="badge b-red">2 = Estribor (ROJO en IALA-B)</span></td><td>Si</td></tr>
<tr><td><code>colour</code> / <code>COLOUR</code></td><td><code>COLOUR</code></td><td>Codigo de color — ver tabla. IALA-B: <code>4</code>=verde (babor), <code>3</code>=rojo (estribor)</td><td>Si</td></tr>
<tr><td><code>boyshp</code> / <code>BOYSHP</code></td><td><code>BOYSHP</code></td><td>Forma: <code>1</code>=conica, <code>2</code>=cilindrica, <code>4</code>=pilar, <code>5</code>=barril, <code>6</code>=esfera</td><td></td></tr>
<tr><td><code>litchr</code> / <code>LITCHR</code></td><td><code>LITCHR</code></td><td>Destello — ver tabla LITCHR. Ej: <code>2</code>=Fl, <code>4</code>=Q</td><td></td></tr>
<tr><td><code>sigper</code> / <code>SIGPER</code></td><td><code>SIGPER</code></td><td>Periodo en segundos. Ej: <code>4.0</code></td><td></td></tr>
<tr><td><code>siggrp</code> / <code>SIGGRP</code></td><td><code>SIGGRP</code></td><td>Grupo de destellos. Ej: <code>(2)</code>, <code>(2+1)</code></td><td></td></tr>
<tr><td><code>alcance</code> / <code>VALNMR</code></td><td><code>VALNMR</code></td><td>Alcance nominal en millas nauticas. Ej: <code>5.0</code></td><td></td></tr>
<tr><td><code>altura</code> / <code>HEIGHT</code></td><td><code>HEIGHT</code></td><td>Altura del plano focal sobre MLLW (metros)</td><td></td></tr>
<tr><td><code>colpat</code> / <code>COLPAT</code></td><td><code>COLPAT</code></td><td>Patron de color: <code>1</code>=horizontal, <code>2</code>=vertical, <code>3</code>=diagonal</td><td></td></tr>
</tbody>
</table>
<h3>Ejemplo de fila CSV (IALA-B — Americas)</h3>
<table>
<thead><tr><th>OBJNAM</th><th>CATLAM</th><th>COLOUR</th><th>BOYSHP</th><th>LITCHR</th><th>SIGPER</th><th>SIGGRP</th><th>VALNMR</th><th>HEIGHT</th></tr></thead>
<tbody>
<tr><td>Boya Verde No.1 (Babor)</td><td>1</td><td>4</td><td>2</td><td>2</td><td>3.0</td><td>(1)</td><td>3.0</td><td>2.5</td></tr>
<tr><td>Boya Roja No.2 (Estribor)</td><td>2</td><td>3</td><td>1</td><td>2</td><td>4.0</td><td>(1)</td><td>3.0</td><td>2.5</td></tr>
<tr><td>Boya Verde No.4 (Babor)</td><td>1</td><td>4</td><td>4</td><td>4</td><td></td><td>(4)</td><td>4.0</td><td>3.0</td></tr>
</tbody>
</table>
</section>
<!-- BCNLAT -->
<section id="attr-bcnlat">
<h2>BCNLAT — Balizas / Faros de orilla</h2>
<div class="tip">
<strong>Capa QGIS:</strong> nombrar <code>balizas</code> / <code>bcnlat</code> / <code>faros</code> — geometria Punto — CRS EPSG:4326
</div>
<p>Estructura fija anclada en tierra o sobre el agua. Mismo esquema de atributos que BOYLAT pero sin <code>BOYSHP</code>. Usa <code>BCNSHP</code> para la forma de la baliza y <code>TOPSHP</code> para la marca de tope.</p>
<table>
<thead><tr><th>Columna SHP / CSV</th><th>Atributo S-57</th><th>Descripcion y valores</th><th>Requerido</th></tr></thead>
<tbody>
<tr><td><code>nombre</code> / <code>OBJNAM</code></td><td><code>OBJNAM</code></td><td>Nombre. Ej: <em>"Faro X1"</em></td><td></td></tr>
<tr><td><code>catlam</code> / <code>CATLAM</code></td><td><code>CATLAM</code></td><td><span class="badge b-green">1 = Babor (VERDE IALA-B)</span> &nbsp; <span class="badge b-red">2 = Estribor (ROJO IALA-B)</span></td><td>Si</td></tr>
<tr><td><code>colour</code> / <code>COLOUR</code></td><td><code>COLOUR</code></td><td>IALA-B: <code>4</code>=verde (babor), <code>3</code>=rojo (estribor)</td><td>Si</td></tr>
<tr><td><code>bcnshp</code> / <code>BCNSHP</code></td><td><code>BCNSHP</code></td><td>Forma: <code>1</code>=poste, <code>2</code>=tripode, <code>3</code>=torre, <code>4</code>=pilao, <code>8</code>=faro</td><td></td></tr>
<tr><td><code>topshp</code> / <code>TOPSHP</code></td><td><code>TOPSHP</code></td><td>Marca de tope: <code>2</code>=cono, <code>5</code>=cilindro, <code>6</code>=esfera, <code>11</code>=cuadro</td><td></td></tr>
<tr><td><code>litchr</code> / <code>LITCHR</code></td><td><code>LITCHR</code></td><td>Destello — ver tabla LITCHR</td><td></td></tr>
<tr><td><code>sigper</code> / <code>SIGPER</code></td><td><code>SIGPER</code></td><td>Periodo en segundos</td><td></td></tr>
<tr><td><code>siggrp</code> / <code>SIGGRP</code></td><td><code>SIGGRP</code></td><td>Grupo de destellos. Ej: <code>(4)</code></td><td></td></tr>
<tr><td><code>alcance</code> / <code>VALNMR</code></td><td><code>VALNMR</code></td><td>Alcance en millas nauticas</td><td></td></tr>
<tr><td><code>altura</code> / <code>HEIGHT</code></td><td><code>HEIGHT</code></td><td>Altura del plano focal (metros)</td><td></td></tr>
<tr><td><code>ORIENT</code></td><td><code>ORIENT</code></td><td>Rumbo verdadero de enfilacion (grados). Si se llena, genera linea de enfilacion en carta</td><td></td></tr>
</tbody>
</table>
<h3>Ejemplo de fila CSV — faros de orilla Barranquilla</h3>
<table>
<thead><tr><th>OBJNAM</th><th>CATLAM</th><th>COLOUR</th><th>LITCHR</th><th>SIGPER</th><th>SIGGRP</th><th>VALNMR</th><th>HEIGHT</th></tr></thead>
<tbody>
<tr><td>Faro X1</td><td>1</td><td>4</td><td>4</td><td>11.0</td><td>(4)</td><td>6.0</td><td>6.0</td></tr>
<tr><td>Faro X4</td><td>2</td><td>3</td><td>2</td><td>4.0</td><td>(1)</td><td>5.0</td><td>8.0</td></tr>
<tr><td>Faro X10</td><td>1</td><td>4</td><td>2</td><td>5.0</td><td>(3)</td><td>7.0</td><td>10.0</td></tr>
</tbody>
</table>
</section>
<!-- BOYCAR -->
<section id="attr-boycar">
<h2>BOYCAR — Boyas cardinales</h2>
<div class="tip">
<strong>Capa QGIS:</strong> nombrar <code>cardinales</code> / <code>boycar</code> / <code>cardinal</code> — geometria Punto — CRS EPSG:4326
</div>
<p>Boyas cardinales IALA: Norte, Sur, Este, Oeste. Se identifican por la combinacion <code>CATCAM</code> + patron de color amarillo/negro.</p>
<table>
<thead><tr><th>Columna SHP / CSV</th><th>Atributo S-57</th><th>Descripcion y valores</th><th>Requerido</th></tr></thead>
<tbody>
<tr><td><code>nombre</code> / <code>OBJNAM</code></td><td><code>OBJNAM</code></td><td>Nombre. Ej: <em>"Boyarin N Canal"</em></td><td></td></tr>
<tr><td><code>catcam</code> / <code>CATCAM</code></td><td><code>CATCAM</code></td><td><code>1</code>=Norte, <code>2</code>=Este, <code>3</code>=Sur, <code>4</code>=Oeste</td><td>Si</td></tr>
<tr><td><code>colour</code> / <code>COLOUR</code></td><td><code>COLOUR</code></td><td>Normalmente <code>6,2</code> (amarillo+negro). El converter asigna automaticamente segun CATCAM si se omite</td><td></td></tr>
<tr><td><code>litchr</code> / <code>LITCHR</code></td><td><code>LITCHR</code></td><td><code>4</code>=Q (Norte/Sur), <code>5</code>=VQ (rapida)</td><td></td></tr>
<tr><td><code>sigper</code> / <code>SIGPER</code></td><td><code>SIGPER</code></td><td>Periodo</td><td></td></tr>
<tr><td><code>siggrp</code> / <code>SIGGRP</code></td><td><code>SIGGRP</code></td><td>Grupo: Norte=continuo, Este=<code>(3)</code>, Sur=<code>(6)+LFl</code>, Oeste=<code>(9)</code></td><td></td></tr>
<tr><td><code>alcance</code> / <code>VALNMR</code></td><td><code>VALNMR</code></td><td>Alcance en millas nauticas</td><td></td></tr>
</tbody>
</table>
<h3>Valores CATCAM</h3>
<table>
<thead><tr><th>Codigo</th><th>Cardinal</th><th>Color tipico</th><th>Destello tipico</th></tr></thead>
<tbody>
<tr><td><code>1</code></td><td>Norte (N)</td><td>Negro arriba / Amarillo abajo</td><td>Q o VQ (rapida continua)</td></tr>
<tr><td><code>2</code></td><td>Este (E)</td><td>Negro-Amarillo-Negro</td><td>Q(3) o VQ(3) cada 5/10s</td></tr>
<tr><td><code>3</code></td><td>Sur (S)</td><td>Amarillo arriba / Negro abajo</td><td>Q(6)+LFl o VQ(6)+LFl cada 15s</td></tr>
<tr><td><code>4</code></td><td>Oeste (W)</td><td>Amarillo-Negro-Amarillo</td><td>Q(9) o VQ(9) cada 10/15s</td></tr>
</tbody>
</table>
</section>
<!-- BOYISD -->
<section id="attr-boyisd">
<h2>BOYISD — Boya de peligro aislado</h2>
<div class="tip">
<strong>Capa QGIS:</strong> nombrar <code>peligro</code> / <code>boyisd</code> / <code>isolated</code> — geometria Punto — CRS EPSG:4326
</div>
<p>Marca un obstaculo rodeado de agua navegable por todos lados. Color: negro con banda(s) roja(s). Marcas de tope: dos esferas negras.</p>
<table>
<thead><tr><th>Columna SHP / CSV</th><th>Atributo S-57</th><th>Descripcion y valores</th><th>Requerido</th></tr></thead>
<tbody>
<tr><td><code>nombre</code> / <code>OBJNAM</code></td><td><code>OBJNAM</code></td><td>Nombre del peligro</td><td></td></tr>
<tr><td><code>colour</code> / <code>COLOUR</code></td><td><code>COLOUR</code></td><td>Normalmente <code>2,3</code> (negro+rojo). Si se omite el converter asigna automaticamente</td><td></td></tr>
<tr><td><code>litchr</code> / <code>LITCHR</code></td><td><code>LITCHR</code></td><td>Tipicamente <code>13</code>=FFl (Fija y destellante)</td><td></td></tr>
<tr><td><code>sigper</code> / <code>SIGPER</code></td><td><code>SIGPER</code></td><td>Periodo</td><td></td></tr>
<tr><td><code>siggrp</code> / <code>SIGGRP</code></td><td><code>SIGGRP</code></td><td>Grupo: tipicamente <code>(2)</code></td><td></td></tr>
<tr><td><code>alcance</code> / <code>VALNMR</code></td><td><code>VALNMR</code></td><td>Alcance en millas nauticas</td><td></td></tr>
</tbody>
</table>
</section>
<!-- BOYSPP -->
<section id="attr-boyspp">
<h2>BOYSPP — Marcas especiales</h2>
<div class="tip">
<strong>Capa QGIS:</strong> nombrar <code>especiales</code> / <code>boyspp</code> / <code>special</code> — geometria Punto — CRS EPSG:4326
</div>
<p>Boyas de usos especiales: zonas de pesca, cabos de fondeo, areas restringidas, tuberias, etc. Color: amarillo. Marca de tope: aspa (X) amarilla.</p>
<table>
<thead><tr><th>Columna SHP / CSV</th><th>Atributo S-57</th><th>Descripcion y valores</th><th>Requerido</th></tr></thead>
<tbody>
<tr><td><code>nombre</code> / <code>OBJNAM</code></td><td><code>OBJNAM</code></td><td>Descripcion de uso</td><td></td></tr>
<tr><td><code>colour</code> / <code>COLOUR</code></td><td><code>COLOUR</code></td><td><code>6</code>=Amarillo (estandar para marcas especiales)</td><td>Si</td></tr>
<tr><td><code>boyshp</code> / <code>BOYSHP</code></td><td><code>BOYSHP</code></td><td>Forma de la boya</td><td></td></tr>
<tr><td><code>litchr</code> / <code>LITCHR</code></td><td><code>LITCHR</code></td><td>Destello — ver tabla LITCHR</td><td></td></tr>
<tr><td><code>sigper</code> / <code>SIGPER</code></td><td><code>SIGPER</code></td><td>Periodo</td><td></td></tr>
<tr><td><code>alcance</code> / <code>VALNMR</code></td><td><code>VALNMR</code></td><td>Alcance</td><td></td></tr>
<tr><td><code>inform</code> / <code>INFORM</code></td><td><code>INFORM</code></td><td>Texto libre: descripcion de la zona restringida</td><td></td></tr>
</tbody>
</table>
</section>
<!-- LIGHTS -->
<section id="attr-lights">
<h2>LIGHTS — Luces independientes (faros, luces de sector)</h2>
<div class="tip">
<strong>Capa QGIS:</strong> nombrar <code>luces</code> / <code>lights</code> / <code>faros</code> — geometria Punto — CRS EPSG:4326
</div>
<p>Luces independientes como faros de tierra, luces de sector o luces de recalada que no estan asociadas a una boya. Para boyas con luz, usa BOYLAT/BCNLAT con los campos LITCHR/SIGPER — el converter genera automaticamente el objeto LIGHTS co-ubicado.</p>
<table>
<thead><tr><th>Columna SHP / CSV</th><th>Atributo S-57</th><th>Descripcion y valores</th><th>Requerido</th></tr></thead>
<tbody>
<tr><td><code>nombre</code> / <code>OBJNAM</code></td><td><code>OBJNAM</code></td><td>Nombre del faro</td><td></td></tr>
<tr><td><code>colour</code> / <code>COLOUR</code></td><td><code>COLOUR</code></td><td>Color de la luz: <code>1</code>=blanco, <code>3</code>=rojo, <code>4</code>=verde</td><td>Si</td></tr>
<tr><td><code>litchr</code> / <code>LITCHR</code></td><td><code>LITCHR</code></td><td>Caracteristica de destello — ver tabla LITCHR</td><td>Si</td></tr>
<tr><td><code>sigper</code> / <code>SIGPER</code></td><td><code>SIGPER</code></td><td>Periodo en segundos. Ej: <code>2.0</code></td><td>Si</td></tr>
<tr><td><code>siggrp</code> / <code>SIGGRP</code></td><td><code>SIGGRP</code></td><td>Grupo: si aplica. Ej: <code>(3)</code></td><td></td></tr>
<tr><td><code>alcance</code> / <code>VALNMR</code></td><td><code>VALNMR</code></td><td>Alcance nominal en millas nauticas</td><td></td></tr>
<tr><td><code>altura</code> / <code>HEIGHT</code></td><td><code>HEIGHT</code></td><td>Altura del plano focal sobre MLLW (metros)</td><td></td></tr>
<tr><td><code>ORIENT</code></td><td><code>ORIENT</code></td><td>Rumbo verdadero de la enfilacion (grados). Genera linea de enfilacion en la carta</td><td></td></tr>
<tr><td><code>inform</code> / <code>INFORM</code></td><td><code>INFORM</code></td><td>Notas: color de la torre, caracteristicas especiales</td><td></td></tr>
</tbody>
</table>
<h3>Ejemplo — Faros de recalada Barranquilla</h3>
<table>
<thead><tr><th>OBJNAM</th><th>COLOUR</th><th>LITCHR</th><th>SIGPER</th><th>VALNMR</th><th>HEIGHT</th><th>ORIENT</th></tr></thead>
<tbody>
<tr><td>Faro F1 Recalada</td><td>4</td><td>7</td><td>2.0</td><td>9.0</td><td>20.0</td><td>270.0</td></tr>
<tr><td>Faro F2 Recalada</td><td>3</td><td>7</td><td>2.0</td><td>13.4</td><td>23.0</td><td></td></tr>
</tbody>
</table>
</section>
<!-- CÓDIGOS S-57 -->
<section id="codigos">
<h2>Tablas de codigos S-57</h2>
<h3>LITCHR — Caracteristica de luz (IHO S-57 Ed. 3.1)</h3>
<div class="warn">
<strong>Atencion:</strong> Usar exactamente estos codigos numericos en la columna <code>LITCHR</code>. El texto (Fl, Q, Iso…) es solo referencia.
</div>
<table>
<thead><tr><th>Codigo</th><th>Abrev.</th><th>Descripcion</th><th>Ejemplo en carta</th></tr></thead>
<tbody>
<tr><td><code>1</code></td><td><strong>F</strong></td><td>Fija — luz continua sin interrupciones</td><td>F G</td></tr>
<tr><td><code>2</code></td><td><strong>Fl</strong></td><td>Destellante — destello mas corto que ocultacion</td><td>Fl G 4s</td></tr>
<tr><td><code>3</code></td><td><strong>LFl</strong></td><td>Gran destello — destello de duracion ≥ 2s</td><td>LFl W 10s</td></tr>
<tr><td><code>4</code></td><td><strong>Q</strong></td><td>Centelleante — 50 a 60 destellos por minuto</td><td>Q(4) G 11s</td></tr>
<tr><td><code>5</code></td><td><strong>VQ</strong></td><td>Rapida — 100 a 120 destellos por minuto</td><td>VQ(3) W</td></tr>
<tr><td><code>6</code></td><td><strong>UQ</strong></td><td>Ultra rapida — mas de 160 destellos por minuto</td><td>UQ</td></tr>
<tr><td><code>7</code></td><td><strong>Iso</strong></td><td>Isofasica — periodo de luz igual al de oscuridad</td><td>Iso G 2s</td></tr>
<tr><td><code>8</code></td><td><strong>Oc</strong></td><td>Ocultante — periodo de luz mayor que el de oscuridad</td><td>Oc R 4s</td></tr>
<tr><td><code>9</code></td><td><strong>IQ</strong></td><td>Centelleante interrumpida</td><td>IQ</td></tr>
<tr><td><code>10</code></td><td><strong>IVQ</strong></td><td>Rapida interrumpida</td><td>IVQ(3)</td></tr>
<tr><td><code>12</code></td><td><strong>Mo</strong></td><td>Codigo Morse</td><td>Mo(A) W</td></tr>
<tr><td><code>13</code></td><td><strong>FFl</strong></td><td>Fija y destellante</td><td>FFl(2) W</td></tr>
</tbody>
</table>
<h3>COLOUR — Color</h3>
<table>
<thead><tr><th>Código</th><th>Color</th></tr></thead>
<tbody>
<tr><td><code>1</code></td><td><span class="swatch" style="background:#fff"></span>Blanco (White)</td></tr>
<tr><td><code>2</code></td><td><span class="swatch" style="background:#111"></span>Negro (Black)</td></tr>
<tr><td><code>3</code></td><td><span class="swatch" style="background:#cc2222"></span>Rojo (Red)</td></tr>
<tr><td><code>4</code></td><td><span class="swatch" style="background:#228822"></span>Verde (Green)</td></tr>
<tr><td><code>5</code></td><td><span class="swatch" style="background:#3355cc"></span>Azul (Blue)</td></tr>
<tr><td><code>6</code></td><td><span class="swatch" style="background:#cccc22"></span>Amarillo (Yellow)</td></tr>
<tr><td><code>9</code></td><td><span class="swatch" style="background:#cc8822"></span>Naranja (Orange)</td></tr>
<tr><td><code>11</code></td><td><span class="swatch" style="background:#cc22cc"></span>Violeta (Violet)</td></tr>
</tbody>
</table>
<h3>CATLAM — Categoria lateral</h3>
<div class="warn">
<strong>IALA-B (Americas — Colombia, USA, Canada, Brasil…)</strong>
Babor = VERDE &nbsp;|&nbsp; Estribor = ROJO. Al entrar al puerto: verde a la izquierda, rojo a la derecha.
</div>
<table>
<thead><tr><th>Codigo</th><th>IALA-B (Americas)</th><th>IALA-A (Europa/Asia/Africa/Australia)</th></tr></thead>
<tbody>
<tr><td><code>1</code></td><td><span class="badge b-green">Babor — VERDE (colour=4)</span> — mano izquierda entrando</td><td><span class="badge b-red">Babor — ROJO (colour=3)</span></td></tr>
<tr><td><code>2</code></td><td><span class="badge b-red">Estribor — ROJO (colour=3)</span> — mano derecha entrando</td><td><span class="badge b-green">Estribor — VERDE (colour=4)</span></td></tr>
</tbody>
</table>
<h3>BOYSHP — Forma de boya</h3>
<table>
<thead><tr><th>Código</th><th>Forma</th></tr></thead>
<tbody>
<tr><td><code>1</code></td><td>Cónica (estribor en IALA-A)</td></tr>
<tr><td><code>2</code></td><td>Cilíndrica (babor en IALA-A)</td></tr>
<tr><td><code>3</code></td><td>Esférica</td></tr>
<tr><td><code>4</code></td><td>Barril</td></tr>
<tr><td><code>5</code></td><td>Super-boya</td></tr>
<tr><td><code>6</code></td><td>Pilón (spar)</td></tr>
<tr><td><code>7</code></td><td>Boyarín</td></tr>
<tr><td><code>8</code></td><td>Ícaro (ice buoy)</td></tr>
</tbody>
</table>
</section>
<!-- CSV DIRECTO -->
<section id="csv-directo">
<h2>Formato CSV directo (sin QGIS)</h2>
<p>Si prefieres no usar QGIS, puedes preparar un CSV maestro y correr <code>build_barranquilla.py</code> (o script equivalente) directamente. El CSV usa los nombres de atributos S-57 como columnas.</p>
<div class="info">
<strong>Columna clave: <code>feat_type</code></strong>
Define el tipo S-57 de cada fila. Valores: <code>BOYLAT</code>, <code>BCNLAT</code>, <code>BOYCAR</code>, <code>BOYISD</code>, <code>BOYSPP</code>, <code>LIGHTS</code>
</div>
<h3>Cabecera del CSV maestro</h3>
<table>
<thead><tr><th>Columna</th><th>Atributo S-57</th><th>Descripcion</th></tr></thead>
<tbody>
<tr><td><code>no_dimar</code></td><td></td><td>Numero de referencia interno (opcional)</td></tr>
<tr><td><code>OBJNAM</code></td><td><code>OBJNAM</code></td><td>Nombre del objeto</td></tr>
<tr><td><code>lon</code></td><td></td><td>Longitud decimal WGS-84 (negativa al W)</td></tr>
<tr><td><code>lat</code></td><td></td><td>Latitud decimal WGS-84</td></tr>
<tr><td><code>feat_type</code></td><td>Clase S-57</td><td>BOYLAT / BCNLAT / BOYCAR / BOYISD / BOYSPP / LIGHTS</td></tr>
<tr><td><code>LITCHR</code></td><td><code>LITCHR</code></td><td>Codigo de destello — ver tabla</td></tr>
<tr><td><code>LITCHR_TXT</code></td><td></td><td>Texto del destello (solo referencia, no se escribe en S-57)</td></tr>
<tr><td><code>SIGGRP</code></td><td><code>SIGGRP</code></td><td>Grupo de destellos. Ej: <code>(4)</code></td></tr>
<tr><td><code>SIGPER</code></td><td><code>SIGPER</code></td><td>Periodo en segundos</td></tr>
<tr><td><code>COLOUR</code></td><td><code>COLOUR</code></td><td>Codigo de color (ver tabla COLOUR)</td></tr>
<tr><td><code>COLOUR_TXT</code></td><td></td><td>Texto del color (solo referencia)</td></tr>
<tr><td><code>COLPAT</code></td><td><code>COLPAT</code></td><td>Patron de color: 1=horizontal, 2=vertical</td></tr>
<tr><td><code>VALNMR</code></td><td><code>VALNMR</code></td><td>Alcance nominal (millas nauticas)</td></tr>
<tr><td><code>HEIGHT</code></td><td><code>HEIGHT</code></td><td>Altura del plano focal (metros)</td></tr>
<tr><td><code>ORIENT</code></td><td><code>ORIENT</code></td><td>Rumbo de enfilacion en grados (opcional)</td></tr>
<tr><td><code>CATLAM</code></td><td><code>CATLAM</code></td><td>Categoria lateral: 1=babor, 2=estribor</td></tr>
<tr><td><code>CATCAM</code></td><td><code>CATCAM</code></td><td>Cardinal: 1=N, 2=E, 3=S, 4=W</td></tr>
<tr><td><code>BOYSHP</code></td><td><code>BOYSHP</code></td><td>Forma de boya</td></tr>
<tr><td><code>BCNSHP</code></td><td><code>BCNSHP</code></td><td>Forma de baliza</td></tr>
<tr><td><code>TOPSHP</code></td><td><code>TOPSHP</code></td><td>Marca de tope</td></tr>
<tr><td><code>INFORM</code></td><td><code>INFORM</code></td><td>Notas libres (aparecen en tooltip)</td></tr>
<tr><td><code>_dimar_char_raw</code></td><td></td><td>Caracter de luz original DIMAR (solo referencia)</td></tr>
<tr><td><code>_source</code></td><td></td><td>Fuente del dato (solo referencia)</td></tr>
</tbody>
</table>
<h3>Ejemplo de filas CSV maestro</h3>
<table>
<thead><tr><th>OBJNAM</th><th>lon</th><th>lat</th><th>feat_type</th><th>LITCHR</th><th>SIGPER</th><th>COLOUR</th><th>CATLAM</th><th>CATCAM</th></tr></thead>
<tbody>
<tr><td>Boya No. 1</td><td>-74.810</td><td>11.102</td><td>BOYLAT</td><td>2</td><td>3.0</td><td>4</td><td>1</td><td></td></tr>
<tr><td>Faro X1</td><td>-74.806</td><td>11.095</td><td>BCNLAT</td><td>4</td><td>11.0</td><td>4</td><td>1</td><td></td></tr>
<tr><td>Cardinal N</td><td>-74.820</td><td>11.110</td><td>BOYCAR</td><td>4</td><td></td><td>2</td><td></td><td>1</td></tr>
<tr><td>Faro Recalada</td><td>-74.849</td><td>11.106</td><td>LIGHTS</td><td>7</td><td>2.0</td><td>4</td><td></td><td></td></tr>
</tbody>
</table>
<div class="tip">
<strong>Para agregar tierra (LNDARE) al .000</strong>
El CSV directo solo soporta objetos puntuales. Para incluir poligonos de tierra (LNDARE) y linea de costa (COALNE), usar el flujo QGIS con capas SHP de area/linea, o crear una capa <code>Tierra</code> (geometria Poligono) en QGIS con los limites costeros y exportar con el converter normal.
</div>
</section>
<!-- CONFIG -->
<section id="config">
<h2>cell_config.json</h2>
<div class="tip">
<strong>Este archivo ya viene incluido con el converter — NO hay que crearlo.</strong>
Solo edita los campos que necesites cambiar (cell_name, scale, issue_date, producer_name).
El archivo esta en: <code>D:\Proyectos Software\QGISS57Converter\cell_config.json</code>
</div>
<h3>Campos que debes editar para cada carta</h3>
<table>
<thead><tr><th>Campo</th><th>Descripcion</th><th>Ejemplo</th></tr></thead>
<tbody>
<tr><td><code>cell_name</code></td><td>Nombre del archivo de salida (sin .000). Convenio IHO: 2 letras pais + 1 digito escala + codigo area</td><td><code>CO1CO01M</code></td></tr>
<tr><td><code>scale</code></td><td>Escala denominador de la carta</td><td><code>50000</code></td></tr>
<tr><td><code>issue_date</code></td><td>Fecha de emision formato YYYYMMDD</td><td><code>20260430</code></td></tr>
<tr><td><code>producer_name</code></td><td>Nombre del productor hidrográfico</td><td><code>DIMAR</code></td></tr>
<tr><td><code>producer_code</code></td><td>Codigo ISO2 del pais productor</td><td><code>CO</code></td></tr>
</tbody>
</table>
<h3>Estructura completa del archivo</h3>
<pre style="background:var(--panel);border:1px solid var(--border);border-radius:6px;padding:14px;font-family:Consolas,monospace;font-size:12px;color:var(--mono);overflow-x:auto;margin-bottom:16px">{
"cell_name": "CO1CO01M",
"cell_edition": 1,
"update_number": 1,
"issue_date": "20260430",
"producer_code": "CO",
"producer_name": "DIMAR",
"data_set_name": "Barranquilla ENC",
"scale": 50000,
"horizontal_datum": "WGS84",
"vertical_datum": "MLLW",
"sounding_datum": "MLLW",
"compilation_scale": 50000,
"layer_mappings": {
"boyas": "BOYLAT",
"balizas": "BCNLAT",
"luces": "LIGHTS",
"tierra": "LNDARE",
"Área Terreno": "LNDARE",
"Linderos": "COALNE",
"sondas": "SOUNDG",
"fondeadero": "ACHARE"
},
"attribute_mappings": {
"nombre": "OBJNAM",
"colour": "COLOUR",
"catlam": "CATLAM",
"boyshp": "BOYSHP",
"litchr": "LITCHR",
"sigper": "SIGPER",
"siggrp": "SIGGRP",
"alcance": "VALNMR",
"altura": "HEIGHT"
}
}</pre>
<div class="info">
<strong>layer_mappings</strong> — traduce el nombre de tu capa QGIS al acronimo S-57. El converter ya incluye los nombres mas comunes (ver archivo completo). Si tu capa tiene un nombre especial, agrega una linea aqui.
<br><br>
<strong>attribute_mappings</strong> — traduce el nombre de tu columna SHP al atributo S-57. Igualmente, los nombres comunes ya estan mapeados.
</div>
</section>
<!-- EJEMPLOS -->
<section id="ejemplos">
<h2>Ejemplos de SHP</h2>
<h3>Capa de boyas (BOYLAT)</h3>
<div class="tip">
<strong>Nombre de la capa:</strong> <code>boyas</code> — geometría Punto — CRS: EPSG:4326
</div>
<p>Columnas recomendadas:</p>
<table>
<thead><tr><th>nombre</th><th>catlam</th><th>colour</th><th>litchr</th><th>sigper</th><th>siggrp</th><th>alcance</th></tr></thead>
<tbody>
<tr><td>Boya R-4</td><td>1</td><td>3</td><td>2</td><td>4.0</td><td>(1)</td><td>3.0</td></tr>
<tr><td>Boya V-3</td><td>2</td><td>4</td><td>2</td><td>4.0</td><td>(1)</td><td>3.0</td></tr>
</tbody>
</table>
<h3>Capa de línea de costa (COALNE)</h3>
<div class="tip">
<strong>Nombre de la capa:</strong> <code>Linderos</code> o <code>coastline</code> — geometría Línea — CRS: EPSG:4326
</div>
<p>No requiere atributos mínimos. Opcionales: <code>nombre</code> para identificar el tramo.</p>
<h3>Capa de área terrestre (LNDARE)</h3>
<div class="tip">
<strong>Nombre de la capa:</strong> <code>Área Terreno</code> o <code>tierra</code> — geometría Polígono — CRS: EPSG:4326
</div>
<p>Opcional: <code>nombre</code> para identificar la isla o terreno.</p>
<h3>Capa de sondas (SOUNDG)</h3>
<div class="tip">
<strong>Nombre de la capa:</strong> <code>sondas</code> — geometría Punto — CRS: EPSG:4326
</div>
<p>Agrega columna <code>sonda</code> con el valor en metros. Si el punto tiene coordenada Z, se usa automáticamente.</p>
<table>
<thead><tr><th>sonda</th><th>Coordenadas</th></tr></thead>
<tbody>
<tr><td>3.5</td><td>-80.456, 27.752</td></tr>
<tr><td>12.0</td><td>-80.451, 27.758</td></tr>
</tbody>
</table>
</section>
</main>
<!-- ── JS: search + active nav ───────────────────────────────────────────── -->
<script>
// Active nav on scroll
const sections = document.querySelectorAll('section[id]');
const navLinks = document.querySelectorAll('.nav-link');
window.addEventListener('scroll', () => {
let cur = '';
sections.forEach(s => { if (window.scrollY >= s.offsetTop - 60) cur = s.id; });
navLinks.forEach(l => {
l.classList.toggle('active', l.getAttribute('href') === '#' + cur);
});
}, { passive: true });
// Search
const box = document.getElementById('search-box');
box.addEventListener('input', () => {
const q = box.value.trim().toLowerCase();
document.querySelectorAll('table tbody tr').forEach(tr => {
tr.classList.toggle('hidden-row', q.length > 1 && !tr.textContent.toLowerCase().includes(q));
});
});
</script>
</body>
</html>
+46
View File
@@ -0,0 +1,46 @@
# -*- mode: python ; coding: utf-8 -*-
import pyproj, os
_proj_data = pyproj.datadir.get_data_dir()
a = Analysis(
['gui.py'],
pathex=[],
binaries=[],
datas=[
('cell_config.json', '.'),
('noaa_ddr_template.bin', '.'),
('CAPAS_REFERENCIA.pdf', '.'),
('s57_objects.json', '.'),
('MANUAL.html', '.'),
(_proj_data, 'proj_data'), # PROJ grid files for pyproj
],
hiddenimports=['converter', 's57_writer', 'pyproj', 'pyproj.datadir'],
hookspath=[],
hooksconfig={},
runtime_hooks=[],
excludes=[],
noarchive=False,
optimize=0,
)
pyz = PYZ(a.pure)
exe = EXE(
pyz,
a.scripts,
a.binaries,
a.datas,
[],
name='QGISS57Converter',
debug=False,
bootloader_ignore_signals=False,
strip=False,
upx=True,
upx_exclude=[],
runtime_tmpdir=None,
console=False,
disable_windowed_traceback=False,
argv_emulation=False,
target_arch=None,
codesign_identity=None,
entitlements_file=None,
)
+211
View File
@@ -0,0 +1,211 @@
# SCHEMA CSV — DIMAR → S-57 ENC
## Referencia para extracción de datos de PDFs DIMAR
Cuando Claude recibe un PDF de DIMAR (Lista de Luces, AAN, carta), lee este archivo
y genera el CSV con exactamente estas columnas y códigos.
---
## COLUMNAS DEL CSV (en este orden)
```
no_dimar, OBJNAM, lon, lat, feat_type, LITCHR, LITCHR_TXT, SIGGRP, COLOUR, COLOUR_TXT, SIGPER, VALNMR, HEIGHT, ORIENT, CATCAM, INFORM, _dimar_char_raw, _source
```
| Columna | Descripción | Ejemplo |
|----------------|--------------------------------------------------|----------------------|
| no_dimar | Número en Lista de Luces DIMAR | 257 |
| OBJNAM | Nombre oficial de la ayuda | Faro Castillogrande |
| lon | Longitud decimal WGS84 (negativo = oeste) | -75.545000 |
| lat | Latitud decimal WGS84 | 10.391000 |
| feat_type | Tipo S-57 (ver tabla abajo) | BOYCAR |
| LITCHR | Código numérico de característica de luz | 2 |
| LITCHR_TXT | Texto legible de la característica | Fl |
| SIGGRP | Grupo de destellos entre paréntesis | 3 o (6)+ |
| COLOUR | Código numérico de color (ver tabla) | 3 |
| COLOUR_TXT | Texto del color | red |
| SIGPER | Período en segundos | 10 |
| VALNMR | Alcance nominal en millas náuticas | 12 |
| HEIGHT | Altura de la luz sobre MLLW en metros | 24 |
| ORIENT | Rumbo de enfilación en grados (solo enfilaciones)| 135.7 |
| CATCAM | Dirección cardinal (solo boyas cardinales) | 1 |
| INFORM | Descripción física de la estructura | Torre concreto beige |
| _dimar_char_raw| Característica de luz tal como aparece en DIMAR | Fl. W 10 s |
| _source | Fuente del dato | DIMAR Lista de Luces 2015 |
**Regla**: columnas vacías se dejan en blanco (no NULL, no 0, solo vacío).
**Regla**: columnas que empiezan con `_` son privadas, el converter las ignora.
**Regla**: coordenadas siempre en decimal WGS84. Convertir grados-minutos así:
`DD°MM.mmm' = DD + MM.mmm/60`
Ejemplo: 10°23.45'N = 10 + 23.45/60 = 10.390833
---
## TABLA feat_type — TIPO DE AYUDA
| feat_type | Descripción | Cuándo usarlo |
|-----------|------------------------------------|--------------------------------------------------|
| BOYCAR | Boya cardinal | Boya con topmark en forma de cono/cono invertido que indica N/S/E/W |
| BCNCAR | Baliza cardinal (fija) | Estructura fija cardinal |
| BOYLAT | Boya lateral | Boya roja o verde que marca bordes de canal |
| BCNLAT | Baliza lateral (fija) | Estructura fija lateral, también enfilaciones |
| BOYISD | Boya de peligro aislado | Boya negra-roja sobre peligro aislado |
| BOYSAW | Boya de aguas seguras | Boya roja-blanca, marca agua navegable |
| BOYSPP | Boya especial | Boya amarilla, uso especial |
| LIGHTS | Luz / Faro / Enfilación | Faros, luces de puerto, enfilaciones |
| LNDMRK | Punto de referencia terrestre | Torres, edificios notables, chimeneas |
---
## TABLA LITCHR — CARACTERÍSTICA DE LUZ
| Código | LITCHR_TXT | DIMAR escribe | Descripción |
|--------|------------|------------------------|--------------------------|
| 1 | F | F. | Fija (Fixed) |
| 2 | Fl | Fl. | Destello (Flashing) |
| 3 | LFl | LFl. | Destello largo |
| 4 | Q | Q. | Rápida (Quick) |
| 5 | VQ | VQ. | Muy rápida |
| 6 | UQ | UQ. | Ultra rápida |
| 7 | Iso | Iso. | Isofase |
| 8 | Oc | Oc. | Ocultante |
| 9 | IQ | IQ. | Interrumpida rápida |
| 12 | Mo | Mo. | Morse |
| 13 | FFl | FFl. | Fija y destellante |
**Grupos**: el número entre paréntesis va en SIGGRP. Ejemplos:
- `Fl.(3) W 10 s` → LITCHR=2, LITCHR_TXT=Fl, SIGGRP=3, COLOUR=1, SIGPER=10
- `Q.(6)+LFl.W 15s` → LITCHR=4, LITCHR_TXT=Q, SIGGRP=(6)+, COLOUR=1, SIGPER=15
- `Iso. Bu 4 s` → LITCHR=7, LITCHR_TXT=Iso, SIGGRP=, COLOUR=5, SIGPER=4
---
## TABLA COLOUR — COLOR DE LA LUZ
| Código | COLOUR_TXT | DIMAR escribe |
|--------|------------|---------------|
| 1 | white | W |
| 2 | black | — |
| 3 | red | R |
| 4 | green | G |
| 5 | blue | Bu |
| 6 | yellow | Y |
| 11 | orange | Or |
**Colores múltiples** (sectores): separar con coma. Ejemplo `"1,3,4"` = white/red/green.
COLOUR_TXT correspondiente: `"white/red/green"`
---
## TABLA CATCAM — DIRECCIÓN CARDINAL (solo BOYCAR / BCNCAR)
| Código | Dirección | Cuándo |
|--------|-----------|-------------------------------------------------|
| 1 | N | Boya Norte — pasa al norte de la boya |
| 2 | E | Boya Este — pasa al este |
| 3 | S | Boya Sur — pasa al sur |
| 4 | W | Boya Oeste — pasa al oeste |
Inferencia por nombre cuando CATCAM no está explícito:
- Contiene " SN", " VN", "Norte", " NN" → 1 (N)
- Contiene " SE", "Este" → 2 (E)
- Contiene " SS", " VS", "Sur" → 3 (S)
- Contiene " SO", " BB", "Oeste", " SW" → 4 (W)
---
## TABLA ORIENT — RUMBO DE ENFILACIÓN (solo LIGHTS con enfilación)
- Solo se rellena cuando la ayuda es una enfilación (leading light / range mark)
- Valor en grados verdaderos (0360), con un decimal
- Es el rumbo que sigue el buque cuando está alineado con las luces
- Si el PDF no especifica rumbo → dejar ORIENT vacío
- Ejemplos: `135.7`, `167.3`, `347.5`
---
## EJEMPLOS COMPLETOS POR TIPO DE AYUDA
### Faro (LIGHTS)
```
35,Faro Castillogrande,-75.545000,10.391000,LIGHTS,2,Fl,,1,white,15,12,24,,,Torre en concreto color beige,Fl. W 15 s,DIMAR Lista de Luces 2015
```
### Faro con grupos (LIGHTS)
```
34,Faro Punta Canoas,-75.499167,10.573000,LIGHTS,2,Fl,2,1,white,20,12,96,,,Torre roja bandas blancas. Giratorio,Fl.(2) W 20 s,DIMAR Lista de Luces 2015
```
### Enfilación (LIGHTS con ORIENT)
```
196,Enfilacion E1,-74.848333,11.103667,LIGHTS,6,Iso,,6,white,5,13,10,135.7,,Baliza enrejado naranja y blanco,Iso Bu 5s,DIMAR Lista de Luces 2015
```
### Boya cardinal Norte (BOYCAR)
```
250,Boya SN,-75.521000,10.366000,BOYCAR,4,Q,,1,white,15,4,4,,1,Castillete cardinal N negro,Q.W 15s,DIMAR Lista de Luces 2015
```
### Boya cardinal Sur (BOYCAR)
```
256,Boya SS,-75.527500,10.331833,BOYCAR,3,4,Q,(6)+,1,white,15,4,4,,3,Castillete cardinal S negros,Q.(6)+LFl.W 15s,AAN-DIMAR-2024-770
```
### Boya cardinal Este (BOYCAR)
```
253,Boya SE,-75.512000,10.365000,BOYCAR,4,Q,3,1,white,10,4,4,,2,Castillete cardinal E negros,Q.(3)W 10s,DIMAR Lista de Luces 2015
```
### Boya cardinal Oeste (BOYCAR)
```
252,Boya SO,-75.534000,10.372000,BOYCAR,2,Fl,9,1,white,15,4,4,,4,Castillete cardinal W negros,Fl.(9)W 15s,DIMAR Lista de Luces 2015
```
### Boya lateral verde (BOYLAT)
```
240,Boya No. 1,-75.563000,10.345000,BOYLAT,2,Fl,,4,green,3,3,4,,,Castillete verde,Fl. G 3 s,DIMAR Lista de Luces 2015
```
### Boya lateral roja (BOYLAT)
```
241,Boya No. 2,-75.560000,10.347000,BOYLAT,2,Fl,,3,red,3,3,4,,,Castillete roja,Fl. R 3 s,DIMAR Lista de Luces 2015
```
### Boya de peligro aislado (BOYISD)
```
258,Boya Peligro Aislado Polvorines,-75.536000,10.351167,BOYISD,2,Fl,2,1,white,5,4,2.5,,,Castillete roja bandas negras,Fl.(2) W 5 s,DIMAR Lista de Luces 2015
```
### Baliza lateral con enfilación (BCNLAT)
```
257,Enfilacion de Bocachica B,-75.508833,10.320833,BCNLAT,7,Iso,,5,blue,4,12,33,,,Torre enrejada rojo bandas blancas,Iso. Bu 4 s,DIMAR Lista de Luces 2015
```
### Luz de aproximación (LIGHTS color especial)
```
296,Luz de Aproximacion,-75.549500,10.409000,LIGHTS,2,Fl,,5,blue,2.5,11,37,,,Torre metalica roja y blanca,Fl. Bu 2.5 s,DIMAR Lista de Luces 2015
```
---
## CONVERSIÓN DE COORDENADAS DIMAR
DIMAR publica en grados y minutos decimales: `10°23.45'N 75°32.67'W`
Fórmula: `grados + minutos/60`
- Latitud: 10 + 23.45/60 = **10.390833** (positivo = Norte)
- Longitud: 75 + 32.67/60 = **75.544500** → con signo negativo = **-75.544500** (Oeste)
---
## INSTRUCCIÓN PARA CLAUDE
Cuando el usuario pase un PDF de DIMAR:
1. Lee este archivo SCHEMA_REFERENCIA.md primero
2. Extrae cada ayuda a la navegación del PDF
3. Convierte coordenadas a decimal WGS84
4. Mapea característica de luz a LITCHR + COLOUR + SIGPER + SIGGRP
5. Asigna feat_type según el tipo de estructura
6. Asigna CATCAM si es cardinal, ORIENT si es enfilación
7. Genera el CSV con exactamente las columnas de este schema, en el mismo orden
8. Nombra el archivo: `dimar_ayudas_<puerto>.csv`
+51
View File
@@ -0,0 +1,51 @@
"""
Build CO1CO01M.000 directly from dimar_ayudas_barranquilla.csv
without needing a QGIS project file.
"""
import sys, json
from pathlib import Path
# Import S57CellWriter from converter (it's defined there)
sys.path.insert(0, str(Path(__file__).parent))
from converter import S57CellWriter
# ── paths ─────────────────────────────────────────────────────────────────────
HERE = Path(__file__).parent
CSV = HERE / "dimar_ayudas_barranquilla.csv"
CONFIG = HERE / "cell_config.json"
OUTPUT = HERE / "dist" / "CO1CO01M" / "CO1CO01M.000"
# ── load config ───────────────────────────────────────────────────────────────
with open(CONFIG, encoding="utf-8") as f:
cfg = json.load(f)
# Update issue date and ensure correct cell name
cfg["cell_name"] = "CO1CO01M"
cfg["issue_date"] = "20260430"
cfg["update_number"] = 1
# ── build ─────────────────────────────────────────────────────────────────────
OUTPUT.parent.mkdir(parents=True, exist_ok=True)
attr_map = cfg.get("attribute_mappings", {})
print(f"Input: {CSV}")
print(f"Output: {OUTPUT}")
print()
writer = S57CellWriter(str(OUTPUT), cfg)
writer.open()
count = writer.add_features_from_csv(
CSV,
"BOYLAT", # default class (overridden per-row by feat_type column)
attr_map,
x_field="lon",
y_field="lat",
)
writer.close()
writer.summary()
print(f"\n{count} feature(s) written")
print(f" {OUTPUT} ({OUTPUT.stat().st_size // 1024} KB)")
+104
View File
@@ -0,0 +1,104 @@
"""
Build CO4CTG01M.000 — Carta ENC S-57 de la Bahía de Cartagena.
Lee todas las capas CSV de capas_ctg/ y genera un archivo S-57 válido
para OpenCPN, AR ECDIS y cualquier software compatible IHO S-57.
Uso:
python build_cartagena.py
python build_cartagena.py --out dist/MI_CARTA/MI_CARTA.000
Columnas especiales en los CSV:
feat_type — acrónimo S-57 de la fila (BOYCAR, LIGHTS, etc.)
CATCAM — dirección cardinal (1=N 2=E 3=S 4=W) → se escribe directo al S-57
ORIENT — rumbo de enfilación en grados → se escribe directo al S-57
_* — columnas privadas, se ignoran
"""
import sys
import json
import argparse
from pathlib import Path
from datetime import date
sys.path.insert(0, str(Path(__file__).parent))
from converter import S57CellWriter
# ── rutas ─────────────────────────────────────────────────────────────────────
HERE = Path(__file__).parent
CAPAS_DIR = HERE / "capas_ctg"
OUTPUT = HERE / "dist" / "CO4CTG01M" / "CO4CTG01M.000"
# ── orden de carga de capas (primero las estructuras, luego las luces) ────────
# El orden importa: si una fila de LIGHTS.csv tiene feat_type=LIGHTS su companion
# light ya no se emite dos veces porque solo las clases _STRUCT_CLASSES generan
# companion. Pero al cargar BOYCAR/BCNLAT antes que LIGHTS evitamos duplicados.
LAYERS = [
# archivo CSV clase S-57 por defecto (se sobreescribe por feat_type)
("BOYCAR.csv", "BOYCAR"),
("BCNLAT.csv", "BCNLAT"),
("BOYISD.csv", "BOYISD"),
("BOYLAT.csv", "BOYLAT"),
("BOYSPEC.csv", "BOYSPP"),
("LIGHTS.csv", "LIGHTS"),
]
def main():
ap = argparse.ArgumentParser()
ap.add_argument("--out", default=str(OUTPUT), help="Ruta del archivo .000 de salida")
args = ap.parse_args()
out_path = Path(args.out)
out_path.parent.mkdir(parents=True, exist_ok=True)
cfg = {
"cell_name": "CO4CTG01M",
"cell_edition": 1,
"update_number": 0,
"issue_date": date.today().strftime("%Y%m%d"),
"producer_code": "CO",
"producer_name": "DIMAR / AR ECDIS",
"data_set_name": "Bahia de Cartagena ENC",
"scale": 12000,
"compilation_scale":12000,
"comment": "Generated by QGIS S-57 Converter from capas_ctg",
"horizontal_datum": "WGS84",
"vertical_datum": "MLLW",
"sounding_datum": "MLLW",
"attribute_mappings": {},
}
print(f"Input dir : {CAPAS_DIR}")
print(f"Output : {out_path}")
print()
writer = S57CellWriter(str(out_path), cfg)
writer.open()
total = 0
for csv_name, default_class in LAYERS:
csv_path = CAPAS_DIR / csv_name
if not csv_path.exists():
print(f" [SKIP] {csv_name} — no encontrado")
continue
n = writer.add_features_from_csv(
csv_path,
default_class,
attr_map={},
x_field="lon",
y_field="lat",
)
print(f" {csv_name:20s}{n:3d} feature(s)")
total += n
writer.close()
writer.summary()
size_kb = out_path.stat().st_size // 1024
print(f"\n{total} feature(s) escritos")
print(f" {out_path} ({size_kb} KB)")
print()
print("Para OpenCPN: Herramientas → Opciones → Cartas → Agregar directorio")
print(f"{out_path.parent}")
if __name__ == "__main__":
main()
+157
View File
@@ -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()
+3
View File
@@ -0,0 +1,3 @@
no_dimar,OBJNAM,lon,lat,feat_type,LITCHR,LITCHR_TXT,SIGGRP,COLOUR,COLOUR_TXT,SIGPER,VALNMR,HEIGHT,ORIENT,INFORM,_dimar_char_raw,_source
235,Boya Cardinal Norte,-74.753500,10.959167,BOYCAR,3,Fl,,6,white,1,6,4,,Castillete cardinal N negros,Fl W 1s,DIMAR Lista de Luces 2015
236,Boya Cardinal Sur,-74.753500,10.959167,BOYCAR,3,Fl,,6,white,15,6,4,,Castillete cardinal S negros,Fl W 15s,DIMAR Lista de Luces 2015
1 no_dimar OBJNAM lon lat feat_type LITCHR LITCHR_TXT SIGGRP COLOUR COLOUR_TXT SIGPER VALNMR HEIGHT ORIENT INFORM _dimar_char_raw _source
2 235 Boya Cardinal Norte -74.753500 10.959167 BOYCAR 3 Fl 6 white 1 6 4 Castillete cardinal N negros Fl W 1s DIMAR Lista de Luces 2015
3 236 Boya Cardinal Sur -74.753500 10.959167 BOYCAR 3 Fl 6 white 15 6 4 Castillete cardinal S negros Fl W 15s DIMAR Lista de Luces 2015
+2
View File
@@ -0,0 +1,2 @@
no_dimar,OBJNAM,lon,lat,feat_type,LITCHR,LITCHR_TXT,SIGGRP,COLOUR,COLOUR_TXT,SIGPER,VALNMR,HEIGHT,ORIENT,INFORM,_dimar_char_raw,_source
240,Boya Peligro Aislado,-74.757333,10.954500,BOYISD,3,Fl(2),2,6,white,4,3,3.3,,Castillete roja bandas negras. Bajo rocoso,Fl(2) W 4s,DIMAR Lista de Luces 2015
1 no_dimar OBJNAM lon lat feat_type LITCHR LITCHR_TXT SIGGRP COLOUR COLOUR_TXT SIGPER VALNMR HEIGHT ORIENT INFORM _dimar_char_raw _source
2 240 Boya Peligro Aislado -74.757333 10.954500 BOYISD 3 Fl(2) 2 6 white 4 3 3.3 Castillete roja bandas negras. Bajo rocoso Fl(2) W 4s DIMAR Lista de Luces 2015
+29
View File
@@ -0,0 +1,29 @@
no_dimar,OBJNAM,lon,lat,feat_type,LITCHR,LITCHR_TXT,SIGGRP,COLOUR,COLOUR_TXT,SIGPER,VALNMR,HEIGHT,ORIENT,INFORM,_dimar_char_raw,_source
199,Boya No. 1,-74.833500,11.084500,BOYLAT,3,Fl,,3,green,3,6,4,,Castillete verde,Fl G 3s,DIMAR Lista de Luces 2015
200,Boya No. 3,-74.844833,11.075833,BOYLAT,3,Fl,,3,green,3,6,4,,Castillete verde,Fl G 3s,DIMAR Lista de Luces 2015
202,Boya No. 5,-74.841000,11.065667,BOYLAT,1,Q,,3,green,1,6,4,,Castillete verde,Q G 1s,DIMAR Lista de Luces 2015
208,Boya No. 7,-74.837500,11.060000,BOYLAT,3,Fl,,3,green,3,6,4,,Castillete verde,Fl G 3s,DIMAR Lista de Luces 2015
209,Boya No. 9,-74.824000,11.046833,BOYLAT,3,Fl,,3,green,3,6,4,,Castillete verde,Fl G 3s,DIMAR Lista de Luces 2015
210,Boya No. 11,-74.812167,11.039667,BOYLAT,3,Fl,,3,green,3,6,4,,Castillete verde,Fl G 3s,DIMAR Lista de Luces 2015
211,Boya No. 12,-74.813500,11.037167,BOYLAT,3,Fl,,1,red,3,6,4,,Castillete roja,Fl R 3s,DIMAR Lista de Luces 2015
212,Boya No. 13,-74.802000,11.034333,BOYLAT,3,Fl,,3,green,3,6,4,,Castillete verde,Fl G 3s,DIMAR Lista de Luces 2015
213,Boya No. 14,-74.788333,11.021833,BOYLAT,3,Fl,,1,red,3,6,3,,Castillete roja,Fl R 3s,DIMAR Lista de Luces 2015
214,Boya No. 15,-74.793833,11.028167,BOYLAT,3,Fl,,3,green,3,6,4,,Castillete verde,Fl G 3s,DIMAR Lista de Luces 2015
215,Boya No. 16,-74.797500,11.027333,BOYLAT,3,Fl,,1,red,3,6,4,,Castillete roja,Fl R 3s,DIMAR Lista de Luces 2015
218,Boya No. 18,-74.788333,11.021833,BOYLAT,3,Fl,,1,red,3,6,4,,Castillete roja,Fl R 3s,DIMAR Lista de Luces 2015
219,Boya No. 19,-74.776500,11.017500,BOYLAT,3,Fl,,3,green,3,6,4,,Castillete verde,Fl G 3s,DIMAR Lista de Luces 2015
220,Boya No. 20,-74.777333,11.015500,BOYLAT,3,Fl,,1,red,3,6,4,,Castillete roja,Fl R 3s,DIMAR Lista de Luces 2015
221,Boya No. 21,-74.772000,11.014000,BOYLAT,3,Fl,,3,green,3,6,4,,Castillete verde,Fl G 3s,DIMAR Lista de Luces 2015
222,Boya No. 22,-74.773333,11.012333,BOYLAT,1,Q,,1,red,1,6,4,,Castillete roja,Q R 1s,DIMAR Lista de Luces 2015
223,Boya No. 23,-74.755000,10.975000,BOYLAT,3,Fl,,3,green,1.3,6,3,,Castillete verde,Fl G 1.3s,DIMAR Lista de Luces 2015
224,Boya No. 24,-74.770500,11.009167,BOYLAT,3,Fl,,1,red,3,6,4,,Castillete roja,Fl R 3s,DIMAR Lista de Luces 2015
225,Boya No. 25,-74.766333,11.006667,BOYLAT,3,Fl,,3,green,3,6,4,,Castillete verde,Fl G 3s,DIMAR Lista de Luces 2015
226,Boya No. 26,-74.768333,11.005833,BOYLAT,3,Fl,,1,red,3,6,4,,Castillete roja,Fl R 3s,DIMAR Lista de Luces 2015
227,Boya No. 27,-74.762000,10.998500,BOYLAT,3,Fl,,3,green,3,6,4,,Castillete verde,Fl G 3s,DIMAR Lista de Luces 2015
228,Boya No. 28,-74.765333,10.999833,BOYLAT,3,Fl,,1,red,3,6,4,,Castillete roja,Fl R 3s,DIMAR Lista de Luces 2015
229,Boya No. 29,-74.758000,10.987333,BOYLAT,3,Fl,,3,green,3,6,4,,Castillete verde,Fl G 3s,DIMAR Lista de Luces 2015
230,Boya No. 30,-74.760667,10.987333,BOYLAT,3,Fl,,1,red,3,6,4,,Castillete roja,Fl R 3s,DIMAR Lista de Luces 2015
231,Boya No. 31,-74.754833,10.975000,BOYLAT,3,Fl,,3,green,3,6,4,,Castillete verde,Fl G 3s,DIMAR Lista de Luces 2015
232,Boya No. 33,-74.755667,10.959333,BOYLAT,3,Fl,,3,green,3,6,4,,Castillete verde,Fl G 3s,DIMAR Lista de Luces 2015
233,Boya No. 35,-74.754167,10.942667,BOYLAT,3,Fl,,3,green,3,6,4,,Castillete verde,Fl G 3s,DIMAR Lista de Luces 2015
234,Boya No. 36,-74.756667,10.941500,BOYLAT,3,Fl,,1,red,3,6,4,,Castillete roja,Fl R 3s,DIMAR Lista de Luces 2015
1 no_dimar OBJNAM lon lat feat_type LITCHR LITCHR_TXT SIGGRP COLOUR COLOUR_TXT SIGPER VALNMR HEIGHT ORIENT INFORM _dimar_char_raw _source
2 199 Boya No. 1 -74.833500 11.084500 BOYLAT 3 Fl 3 green 3 6 4 Castillete verde Fl G 3s DIMAR Lista de Luces 2015
3 200 Boya No. 3 -74.844833 11.075833 BOYLAT 3 Fl 3 green 3 6 4 Castillete verde Fl G 3s DIMAR Lista de Luces 2015
4 202 Boya No. 5 -74.841000 11.065667 BOYLAT 1 Q 3 green 1 6 4 Castillete verde Q G 1s DIMAR Lista de Luces 2015
5 208 Boya No. 7 -74.837500 11.060000 BOYLAT 3 Fl 3 green 3 6 4 Castillete verde Fl G 3s DIMAR Lista de Luces 2015
6 209 Boya No. 9 -74.824000 11.046833 BOYLAT 3 Fl 3 green 3 6 4 Castillete verde Fl G 3s DIMAR Lista de Luces 2015
7 210 Boya No. 11 -74.812167 11.039667 BOYLAT 3 Fl 3 green 3 6 4 Castillete verde Fl G 3s DIMAR Lista de Luces 2015
8 211 Boya No. 12 -74.813500 11.037167 BOYLAT 3 Fl 1 red 3 6 4 Castillete roja Fl R 3s DIMAR Lista de Luces 2015
9 212 Boya No. 13 -74.802000 11.034333 BOYLAT 3 Fl 3 green 3 6 4 Castillete verde Fl G 3s DIMAR Lista de Luces 2015
10 213 Boya No. 14 -74.788333 11.021833 BOYLAT 3 Fl 1 red 3 6 3 Castillete roja Fl R 3s DIMAR Lista de Luces 2015
11 214 Boya No. 15 -74.793833 11.028167 BOYLAT 3 Fl 3 green 3 6 4 Castillete verde Fl G 3s DIMAR Lista de Luces 2015
12 215 Boya No. 16 -74.797500 11.027333 BOYLAT 3 Fl 1 red 3 6 4 Castillete roja Fl R 3s DIMAR Lista de Luces 2015
13 218 Boya No. 18 -74.788333 11.021833 BOYLAT 3 Fl 1 red 3 6 4 Castillete roja Fl R 3s DIMAR Lista de Luces 2015
14 219 Boya No. 19 -74.776500 11.017500 BOYLAT 3 Fl 3 green 3 6 4 Castillete verde Fl G 3s DIMAR Lista de Luces 2015
15 220 Boya No. 20 -74.777333 11.015500 BOYLAT 3 Fl 1 red 3 6 4 Castillete roja Fl R 3s DIMAR Lista de Luces 2015
16 221 Boya No. 21 -74.772000 11.014000 BOYLAT 3 Fl 3 green 3 6 4 Castillete verde Fl G 3s DIMAR Lista de Luces 2015
17 222 Boya No. 22 -74.773333 11.012333 BOYLAT 1 Q 1 red 1 6 4 Castillete roja Q R 1s DIMAR Lista de Luces 2015
18 223 Boya No. 23 -74.755000 10.975000 BOYLAT 3 Fl 3 green 1.3 6 3 Castillete verde Fl G 1.3s DIMAR Lista de Luces 2015
19 224 Boya No. 24 -74.770500 11.009167 BOYLAT 3 Fl 1 red 3 6 4 Castillete roja Fl R 3s DIMAR Lista de Luces 2015
20 225 Boya No. 25 -74.766333 11.006667 BOYLAT 3 Fl 3 green 3 6 4 Castillete verde Fl G 3s DIMAR Lista de Luces 2015
21 226 Boya No. 26 -74.768333 11.005833 BOYLAT 3 Fl 1 red 3 6 4 Castillete roja Fl R 3s DIMAR Lista de Luces 2015
22 227 Boya No. 27 -74.762000 10.998500 BOYLAT 3 Fl 3 green 3 6 4 Castillete verde Fl G 3s DIMAR Lista de Luces 2015
23 228 Boya No. 28 -74.765333 10.999833 BOYLAT 3 Fl 1 red 3 6 4 Castillete roja Fl R 3s DIMAR Lista de Luces 2015
24 229 Boya No. 29 -74.758000 10.987333 BOYLAT 3 Fl 3 green 3 6 4 Castillete verde Fl G 3s DIMAR Lista de Luces 2015
25 230 Boya No. 30 -74.760667 10.987333 BOYLAT 3 Fl 1 red 3 6 4 Castillete roja Fl R 3s DIMAR Lista de Luces 2015
26 231 Boya No. 31 -74.754833 10.975000 BOYLAT 3 Fl 3 green 3 6 4 Castillete verde Fl G 3s DIMAR Lista de Luces 2015
27 232 Boya No. 33 -74.755667 10.959333 BOYLAT 3 Fl 3 green 3 6 4 Castillete verde Fl G 3s DIMAR Lista de Luces 2015
28 233 Boya No. 35 -74.754167 10.942667 BOYLAT 3 Fl 3 green 3 6 4 Castillete verde Fl G 3s DIMAR Lista de Luces 2015
29 234 Boya No. 36 -74.756667 10.941500 BOYLAT 3 Fl 1 red 3 6 4 Castillete roja Fl R 3s DIMAR Lista de Luces 2015
+2
View File
@@ -0,0 +1,2 @@
no_dimar,OBJNAM,lon,lat,feat_type,LITCHR,LITCHR_TXT,SIGGRP,COLOUR,COLOUR_TXT,SIGPER,VALNMR,HEIGHT,ORIENT,INFORM,_dimar_char_raw,_source
239,Boya de Oleaje,-74.758000,11.134000,BOYSPEC,3,Fl,,11,yellow,20,4.5,0.5,,Esferica amarilla. Recolectora datos oceanograficos,Fl Y 20s,DIMAR Lista de Luces 2015
1 no_dimar OBJNAM lon lat feat_type LITCHR LITCHR_TXT SIGGRP COLOUR COLOUR_TXT SIGPER VALNMR HEIGHT ORIENT INFORM _dimar_char_raw _source
2 239 Boya de Oleaje -74.758000 11.134000 BOYSPEC 3 Fl 11 yellow 20 4.5 0.5 Esferica amarilla. Recolectora datos oceanograficos Fl Y 20s DIMAR Lista de Luces 2015
+33
View File
@@ -0,0 +1,33 @@
no_dimar,OBJNAM,lon,lat,feat_type,LITCHR,LITCHR_TXT,SIGGRP,COLOUR,COLOUR_TXT,SIGPER,VALNMR,HEIGHT,ORIENT,INFORM,_dimar_char_raw,_source
13,Faro F1 Recalada,-74.849500,11.106167,LIGHTS,6,Iso,,3,green,2,9,20,,Torre naranja bandas blancas. Faro de Recalada,Iso G 2s,DIMAR Lista de Luces 2015
14,Faro F2 Recalada,-74.854667,11.106000,LIGHTS,6,Iso,,1,red,2,13.4,23,,Torre naranja bandas blancas. Racon B,Iso R 2s,DIMAR Lista de Luces 2015
32,Faro Morro Hermoso,-75.017500,10.963333,LIGHTS,3,Fl,,6,white,4,28,134,,Torre blanca bandas rojas. Giratorio,Fl W 4s,DIMAR Lista de Luces 2015
33,Faro Galerazamba,-75.266000,10.785333,LIGHTS,3,Fl,,6,white,4,11,14,,Torre fibra vidrio blanca bandas rojas. Giratorio,Fl W 4s,DIMAR Lista de Luces 2015
15,Faro X1,-74.849500,11.102167,LIGHTS,1,Q(4)G,4,3,green,11,6,6,,Torre verde bandas blancas,Q(4)G 11s,DIMAR Lista de Luces 2015
16,Faro X2,-74.853333,11.100000,LIGHTS,1,Q(4)R,4,1,red,11,6,6,,Torre roja bandas blancas,Q(4)R 11s,DIMAR Lista de Luces 2015
18,Faro X3,-74.847167,11.091333,LIGHTS,1,Q(4)G,4,3,green,11,6,6,,Torre verde bandas blancas,Q(4)G 11s,DIMAR Lista de Luces 2015
17,Faro X4,-74.851667,11.093000,LIGHTS,1,Q(4)R,4,1,red,11,6,6,,Torre roja bandas blancas,Q(4)R 11s,DIMAR Lista de Luces 2015
19,Faro X5,-74.846667,11.089167,LIGHTS,1,Q(4)G,4,3,green,11,6,6,,Torre verde bandas blancas,Q(4)G 11s,DIMAR Lista de Luces 2015
20,Faro X6,-74.850500,11.087667,LIGHTS,1,Q(4)R,4,1,red,11,6,6,,Torre roja bandas blancas,Q(4)R 11s,DIMAR Lista de Luces 2015
21,Faro X7,-74.814333,11.041500,LIGHTS,1,Q(4)G,4,3,green,11,6,8,,Baliza enrejado rojo bandas blancas,Q(4)G 11s,DIMAR Lista de Luces 2015
22,Faro X8,-74.849167,11.081667,LIGHTS,1,Q(4)R,4,1,red,11,6,6,,Torre roja bandas blancas,Q(4)R 11s,DIMAR Lista de Luces 2015
23,Faro X9,-74.804833,11.035833,LIGHTS,1,Q(4)G,4,3,green,11,6,8,,Baliza enrejado rojo bandas blancas,Q(4)G 11s,DIMAR Lista de Luces 2015
24,Faro X10,-74.848000,11.076000,LIGHTS,1,Q(4)R,4,1,red,11,6,6,,Torre roja bandas blancas,Q(4)R 11s,DIMAR Lista de Luces 2015
25,Faro X11,-74.795500,11.029833,LIGHTS,1,Q(4)G,4,3,green,11,6,8,,Baliza enrejado rojo bandas blancas,Q(4)G 11s,DIMAR Lista de Luces 2015
26,Faro X12,-74.844167,11.065000,LIGHTS,1,Q(4)R,4,1,red,11,6,6,,Baliza enrejado roja bandas blancas,Q(4)R 11s,DIMAR Lista de Luces 2015
27,Faro X13,-74.789667,11.025833,LIGHTS,1,Q(4)G,4,3,green,11,6,8,,Baliza enrejado rojo bandas blancas,Q(4)G 11s,DIMAR Lista de Luces 2015
28,Faro X14,-74.839500,11.057833,LIGHTS,1,Q(4)R,4,1,red,11,6,6,,Torre roja bandas blancas,Q(4)R 11s,DIMAR Lista de Luces 2015
30,Faro X15,-74.785500,11.022833,LIGHTS,1,Q(4)G,4,3,green,11,6,6,,Baliza enrejado verde bandas blancas,Q(4)G 11s,DIMAR Lista de Luces 2015
29,Faro X16,-74.833000,11.050000,LIGHTS,1,Q(4)R,4,1,red,11,6,6,,Torre roja bandas blancas,Q(4)R 11s,DIMAR Lista de Luces 2015
31,Faro X17,-74.778333,11.018667,LIGHTS,1,Q(4)G,4,3,green,11,6,6,,Baliza enrejado verde bandas blancas,Q(4)G 11s,DIMAR Lista de Luces 2015
196,Enfilacion E1,-74.848333,11.103667,LIGHTS,6,Iso,,6,white,5,13,10,135.7,Baliza enrejado naranja y blanco. Rumbo 135.7,Iso Bu 5s,DIMAR Lista de Luces 2015
197,Enfilacion E3,-74.846333,11.101667,LIGHTS,6,Iso,,6,white,5,9,22,139.3,Torre enrejada naranja y blanco. Rumbo 139.3,Iso Bu 5s,DIMAR Lista de Luces 2015
198,Enfilacion E3A,-74.845000,11.100333,LIGHTS,6,Iso,,6,white,5,12.3,20,135.7,Torre naranja y blanco. Rumbo 135.7,Iso W 5s,DIMAR Lista de Luces 2015
201,Enfilacion E4,-74.846833,11.070167,LIGHTS,6,Iso,,1,red,4,4.5,11,142.3,Baliza enrejado naranja bandas blancas. Rumbo 142.3,Iso R 4s,DIMAR Lista de Luces 2015
203,Enfilacion E6,-74.843667,11.063000,LIGHTS,6,Iso,,1,red,4,8,12,167.7,Baliza enrejado roja bandas blancas. Rumbo 167.7,Iso Bu 4s,DIMAR Lista de Luces 2015
204,Enfilacion E8,-74.841833,11.058500,LIGHTS,6,Iso,,6,white,4,14.5,25,167.7,Baliza enrejado naranja bandas blancas. Rumbo 167.7,Iso Bu 4s,DIMAR Lista de Luces 2015
205,Enfilacion E10,-74.841667,11.059833,LIGHTS,6,Iso,,3,green,5,10,11,167.3,Torre naranja bandas blancas. Rumbo 167.3,Iso G 5s,DIMAR Lista de Luces 2015
206,Enfilacion E12,-74.840667,11.056167,LIGHTS,6,Iso,,3,green,5,8,22,167.3,Baliza tablero blanco franja roja. Rumbo 167.3,Iso G 5s,DIMAR Lista de Luces 2015
207,Enfilacion E14,-74.840667,11.056167,LIGHTS,6,Iso,,6,white,6,8,22,122,Tablero blanco con franja roja. Rumbo 122,Iso Bu 6s,DIMAR Lista de Luces 2015
237,Enfilacion E16,-74.836667,11.053667,LIGHTS,6,Iso,,6,white,6,9,12,122,Baliza enrejado naranja bandas blancas. Rumbo 122,Iso Bu 6s,DIMAR Lista de Luces 2015
238,Enfilacion E18,-74.825500,11.043000,LIGHTS,3,Fl,,"1,3,6",white/red/green,4,6,18,142,Torre roja bandas blancas. Sector 9 grados. Rumbo 142,Fl WRG 4s,DIMAR Lista de Luces 2015
1 no_dimar OBJNAM lon lat feat_type LITCHR LITCHR_TXT SIGGRP COLOUR COLOUR_TXT SIGPER VALNMR HEIGHT ORIENT INFORM _dimar_char_raw _source
2 13 Faro F1 Recalada -74.849500 11.106167 LIGHTS 6 Iso 3 green 2 9 20 Torre naranja bandas blancas. Faro de Recalada Iso G 2s DIMAR Lista de Luces 2015
3 14 Faro F2 Recalada -74.854667 11.106000 LIGHTS 6 Iso 1 red 2 13.4 23 Torre naranja bandas blancas. Racon B Iso R 2s DIMAR Lista de Luces 2015
4 32 Faro Morro Hermoso -75.017500 10.963333 LIGHTS 3 Fl 6 white 4 28 134 Torre blanca bandas rojas. Giratorio Fl W 4s DIMAR Lista de Luces 2015
5 33 Faro Galerazamba -75.266000 10.785333 LIGHTS 3 Fl 6 white 4 11 14 Torre fibra vidrio blanca bandas rojas. Giratorio Fl W 4s DIMAR Lista de Luces 2015
6 15 Faro X1 -74.849500 11.102167 LIGHTS 1 Q(4)G 4 3 green 11 6 6 Torre verde bandas blancas Q(4)G 11s DIMAR Lista de Luces 2015
7 16 Faro X2 -74.853333 11.100000 LIGHTS 1 Q(4)R 4 1 red 11 6 6 Torre roja bandas blancas Q(4)R 11s DIMAR Lista de Luces 2015
8 18 Faro X3 -74.847167 11.091333 LIGHTS 1 Q(4)G 4 3 green 11 6 6 Torre verde bandas blancas Q(4)G 11s DIMAR Lista de Luces 2015
9 17 Faro X4 -74.851667 11.093000 LIGHTS 1 Q(4)R 4 1 red 11 6 6 Torre roja bandas blancas Q(4)R 11s DIMAR Lista de Luces 2015
10 19 Faro X5 -74.846667 11.089167 LIGHTS 1 Q(4)G 4 3 green 11 6 6 Torre verde bandas blancas Q(4)G 11s DIMAR Lista de Luces 2015
11 20 Faro X6 -74.850500 11.087667 LIGHTS 1 Q(4)R 4 1 red 11 6 6 Torre roja bandas blancas Q(4)R 11s DIMAR Lista de Luces 2015
12 21 Faro X7 -74.814333 11.041500 LIGHTS 1 Q(4)G 4 3 green 11 6 8 Baliza enrejado rojo bandas blancas Q(4)G 11s DIMAR Lista de Luces 2015
13 22 Faro X8 -74.849167 11.081667 LIGHTS 1 Q(4)R 4 1 red 11 6 6 Torre roja bandas blancas Q(4)R 11s DIMAR Lista de Luces 2015
14 23 Faro X9 -74.804833 11.035833 LIGHTS 1 Q(4)G 4 3 green 11 6 8 Baliza enrejado rojo bandas blancas Q(4)G 11s DIMAR Lista de Luces 2015
15 24 Faro X10 -74.848000 11.076000 LIGHTS 1 Q(4)R 4 1 red 11 6 6 Torre roja bandas blancas Q(4)R 11s DIMAR Lista de Luces 2015
16 25 Faro X11 -74.795500 11.029833 LIGHTS 1 Q(4)G 4 3 green 11 6 8 Baliza enrejado rojo bandas blancas Q(4)G 11s DIMAR Lista de Luces 2015
17 26 Faro X12 -74.844167 11.065000 LIGHTS 1 Q(4)R 4 1 red 11 6 6 Baliza enrejado roja bandas blancas Q(4)R 11s DIMAR Lista de Luces 2015
18 27 Faro X13 -74.789667 11.025833 LIGHTS 1 Q(4)G 4 3 green 11 6 8 Baliza enrejado rojo bandas blancas Q(4)G 11s DIMAR Lista de Luces 2015
19 28 Faro X14 -74.839500 11.057833 LIGHTS 1 Q(4)R 4 1 red 11 6 6 Torre roja bandas blancas Q(4)R 11s DIMAR Lista de Luces 2015
20 30 Faro X15 -74.785500 11.022833 LIGHTS 1 Q(4)G 4 3 green 11 6 6 Baliza enrejado verde bandas blancas Q(4)G 11s DIMAR Lista de Luces 2015
21 29 Faro X16 -74.833000 11.050000 LIGHTS 1 Q(4)R 4 1 red 11 6 6 Torre roja bandas blancas Q(4)R 11s DIMAR Lista de Luces 2015
22 31 Faro X17 -74.778333 11.018667 LIGHTS 1 Q(4)G 4 3 green 11 6 6 Baliza enrejado verde bandas blancas Q(4)G 11s DIMAR Lista de Luces 2015
23 196 Enfilacion E1 -74.848333 11.103667 LIGHTS 6 Iso 6 white 5 13 10 135.7 Baliza enrejado naranja y blanco. Rumbo 135.7 Iso Bu 5s DIMAR Lista de Luces 2015
24 197 Enfilacion E3 -74.846333 11.101667 LIGHTS 6 Iso 6 white 5 9 22 139.3 Torre enrejada naranja y blanco. Rumbo 139.3 Iso Bu 5s DIMAR Lista de Luces 2015
25 198 Enfilacion E3A -74.845000 11.100333 LIGHTS 6 Iso 6 white 5 12.3 20 135.7 Torre naranja y blanco. Rumbo 135.7 Iso W 5s DIMAR Lista de Luces 2015
26 201 Enfilacion E4 -74.846833 11.070167 LIGHTS 6 Iso 1 red 4 4.5 11 142.3 Baliza enrejado naranja bandas blancas. Rumbo 142.3 Iso R 4s DIMAR Lista de Luces 2015
27 203 Enfilacion E6 -74.843667 11.063000 LIGHTS 6 Iso 1 red 4 8 12 167.7 Baliza enrejado roja bandas blancas. Rumbo 167.7 Iso Bu 4s DIMAR Lista de Luces 2015
28 204 Enfilacion E8 -74.841833 11.058500 LIGHTS 6 Iso 6 white 4 14.5 25 167.7 Baliza enrejado naranja bandas blancas. Rumbo 167.7 Iso Bu 4s DIMAR Lista de Luces 2015
29 205 Enfilacion E10 -74.841667 11.059833 LIGHTS 6 Iso 3 green 5 10 11 167.3 Torre naranja bandas blancas. Rumbo 167.3 Iso G 5s DIMAR Lista de Luces 2015
30 206 Enfilacion E12 -74.840667 11.056167 LIGHTS 6 Iso 3 green 5 8 22 167.3 Baliza tablero blanco franja roja. Rumbo 167.3 Iso G 5s DIMAR Lista de Luces 2015
31 207 Enfilacion E14 -74.840667 11.056167 LIGHTS 6 Iso 6 white 6 8 22 122 Tablero blanco con franja roja. Rumbo 122 Iso Bu 6s DIMAR Lista de Luces 2015
32 237 Enfilacion E16 -74.836667 11.053667 LIGHTS 6 Iso 6 white 6 9 12 122 Baliza enrejado naranja bandas blancas. Rumbo 122 Iso Bu 6s DIMAR Lista de Luces 2015
33 238 Enfilacion E18 -74.825500 11.043000 LIGHTS 3 Fl 1,3,6 white/red/green 4 6 18 142 Torre roja bandas blancas. Sector 9 grados. Rumbo 142 Fl WRG 4s DIMAR Lista de Luces 2015
+4
View File
@@ -0,0 +1,4 @@
no_dimar,OBJNAM,lon,lat,feat_type,LITCHR,LITCHR_TXT,SIGGRP,COLOUR,COLOUR_TXT,SIGPER,VALNMR,HEIGHT,ORIENT,INFORM,_dimar_char_raw,_source
257,Enfilacion de Bocachica B,-75.508833,10.320833,BCNLAT,7,Iso,,5,blue,4,12,33,,Torre enrejada rojo bandas blancas,Iso. Bu 4 s,DIMAR Lista de Luces 2015
304,Baliza No. 01,-75.5245,10.304,BCNLAT,2,Fl,,4,green,2,3,4.5,,Poste cilindrico verde,Fl. G 2 s,DIMAR Lista de Luces 2015
305,Baliza No. 02,-75.5245,10.304,BCNLAT,2,Fl,,3,red,2,3,4.5,,Poste cilindrico rojo,Fl. R 2 s,DIMAR Lista de Luces 2015
1 no_dimar OBJNAM lon lat feat_type LITCHR LITCHR_TXT SIGGRP COLOUR COLOUR_TXT SIGPER VALNMR HEIGHT ORIENT INFORM _dimar_char_raw _source
2 257 Enfilacion de Bocachica B -75.508833 10.320833 BCNLAT 7 Iso 5 blue 4 12 33 Torre enrejada rojo bandas blancas Iso. Bu 4 s DIMAR Lista de Luces 2015
3 304 Baliza No. 01 -75.5245 10.304 BCNLAT 2 Fl 4 green 2 3 4.5 Poste cilindrico verde Fl. G 2 s DIMAR Lista de Luces 2015
4 305 Baliza No. 02 -75.5245 10.304 BCNLAT 2 Fl 3 red 2 3 4.5 Poste cilindrico rojo Fl. R 2 s DIMAR Lista de Luces 2015
+11
View File
@@ -0,0 +1,11 @@
no_dimar,OBJNAM,lon,lat,feat_type,CATCAM,LITCHR,LITCHR_TXT,SIGGRP,COLOUR,COLOUR_TXT,SIGPER,VALNMR,HEIGHT,ORIENT,INFORM,_dimar_char_raw,_source
256,Boya SS,-75.527500,10.331833,BOYCAR,3,4,Q,(6)+,1,white,15,4,4,,Castillete Cardinal S negros - Senala Bajo Santa Cruz,Q.(6)+LFl.W 15s,AAN-DIMAR-2024-770
266,Boya SN,-75.526667,10.3445,BOYCAR,1,4,Q,,1,white,1,3,4,,Castillete Cardinal N negros - Senala Bajo Santa Cruz,Q. W 1 s,DIMAR Lista de Luces 2015
289,Boya VN,-75.5425,10.399833,BOYCAR,1,4,Q,,1,white,1,3,4,,Castillete Cardinal N negros - Senalizacion Bajo la Virgen,Q. W 1 s,DIMAR Lista de Luces 2015
290,Boya VS,-75.5415,10.393667,BOYCAR,3,4,Q,6,1,white,15,3,4,,Castillete Cardinal S negros - Senalizacion Bajo la Virgen,Q.(6)+Fl. W 15 s,DIMAR Lista de Luces 2015
297,Boya BB1,-75.516167,10.326167,BOYCAR,4,4,Q,9,1,white,15,3,4,,Castillete Cardinal W negros - Senala Bajo Brujas,Q.(9) W 15 s,DIMAR Lista de Luces 2015
298,Boya BB2,-75.515167,10.322500,BOYCAR,4,4,Q,9,1,white,15,3,4,,Castillete Cardinal W negros - Senala Bajo Brujas,Q.(9) W 15 s,AAN-DIMAR-2025-180
299,Boya SN (Salmedina),-75.648022,10.384530,BOYCAR,1,4,Q,,1,white,1,3,4,,Castillete Cardinal N negros - Senala Bancos de Salmedina,Q. W 1 s,AAN-DIMAR-2025-261
300,Boya SS (Salmedina),-75.651000,10.364833,BOYCAR,3,4,Q,6,1,white,15,3,4,,Castillete Cardinal S negros - Senala Bancos de Salmedina,Q.(6)+Fl. W 15 s,AAN-DIMAR-2024-246
301,Boya SE (Salmedina),-75.635850,10.380173,BOYCAR,2,4,Q,3,1,white,10,3,4,,Castillete Cardinal E negros - Senala Bancos de Salmedina,Q.(3) W 10 s,AAN-DIMAR-2025-154
302,Boya SO (Salmedina),-75.689133,10.381967,BOYCAR,4,4,Q,9,1,white,10,3,4,,Castillete Cardinal W negros - Senala Bancos de Salmedina,Q.(9) W 10 s,AAN-DIMAR-2024-229
1 no_dimar OBJNAM lon lat feat_type CATCAM LITCHR LITCHR_TXT SIGGRP COLOUR COLOUR_TXT SIGPER VALNMR HEIGHT ORIENT INFORM _dimar_char_raw _source
2 256 Boya SS -75.527500 10.331833 BOYCAR 3 4 Q (6)+ 1 white 15 4 4 Castillete Cardinal S negros - Senala Bajo Santa Cruz Q.(6)+LFl.W 15s AAN-DIMAR-2024-770
3 266 Boya SN -75.526667 10.3445 BOYCAR 1 4 Q 1 white 1 3 4 Castillete Cardinal N negros - Senala Bajo Santa Cruz Q. W 1 s DIMAR Lista de Luces 2015
4 289 Boya VN -75.5425 10.399833 BOYCAR 1 4 Q 1 white 1 3 4 Castillete Cardinal N negros - Senalizacion Bajo la Virgen Q. W 1 s DIMAR Lista de Luces 2015
5 290 Boya VS -75.5415 10.393667 BOYCAR 3 4 Q 6 1 white 15 3 4 Castillete Cardinal S negros - Senalizacion Bajo la Virgen Q.(6)+Fl. W 15 s DIMAR Lista de Luces 2015
6 297 Boya BB1 -75.516167 10.326167 BOYCAR 4 4 Q 9 1 white 15 3 4 Castillete Cardinal W negros - Senala Bajo Brujas Q.(9) W 15 s DIMAR Lista de Luces 2015
7 298 Boya BB2 -75.515167 10.322500 BOYCAR 4 4 Q 9 1 white 15 3 4 Castillete Cardinal W negros - Senala Bajo Brujas Q.(9) W 15 s AAN-DIMAR-2025-180
8 299 Boya SN (Salmedina) -75.648022 10.384530 BOYCAR 1 4 Q 1 white 1 3 4 Castillete Cardinal N negros - Senala Bancos de Salmedina Q. W 1 s AAN-DIMAR-2025-261
9 300 Boya SS (Salmedina) -75.651000 10.364833 BOYCAR 3 4 Q 6 1 white 15 3 4 Castillete Cardinal S negros - Senala Bancos de Salmedina Q.(6)+Fl. W 15 s AAN-DIMAR-2024-246
10 301 Boya SE (Salmedina) -75.635850 10.380173 BOYCAR 2 4 Q 3 1 white 10 3 4 Castillete Cardinal E negros - Senala Bancos de Salmedina Q.(3) W 10 s AAN-DIMAR-2025-154
11 302 Boya SO (Salmedina) -75.689133 10.381967 BOYCAR 4 4 Q 9 1 white 10 3 4 Castillete Cardinal W negros - Senala Bancos de Salmedina Q.(9) W 10 s AAN-DIMAR-2024-229
+3
View File
@@ -0,0 +1,3 @@
no_dimar,OBJNAM,lon,lat,feat_type,LITCHR,LITCHR_TXT,SIGGRP,COLOUR,COLOUR_TXT,SIGPER,VALNMR,HEIGHT,ORIENT,INFORM,_dimar_char_raw,_source
258,Boya Peligro Aislado Polvorines,-75.536,10.351167,BOYISD,2,Fl,2,1,white,5,4,2.5,,Castillete roja bandas negras,Fl.(2) W 5 s,DIMAR Lista de Luces 2015
259,Boya TT,-75.521,10.366667,BOYISD,2,Fl,2,1,white,5,3,4,,Castillete roja bandas negras - Boya de Peligro Aislado La Tata,Fl.(2) W 5 s,DIMAR Lista de Luces 2015
1 no_dimar OBJNAM lon lat feat_type LITCHR LITCHR_TXT SIGGRP COLOUR COLOUR_TXT SIGPER VALNMR HEIGHT ORIENT INFORM _dimar_char_raw _source
2 258 Boya Peligro Aislado Polvorines -75.536 10.351167 BOYISD 2 Fl 2 1 white 5 4 2.5 Castillete roja bandas negras Fl.(2) W 5 s DIMAR Lista de Luces 2015
3 259 Boya TT -75.521 10.366667 BOYISD 2 Fl 2 1 white 5 3 4 Castillete roja bandas negras - Boya de Peligro Aislado La Tata Fl.(2) W 5 s DIMAR Lista de Luces 2015
+65
View File
@@ -0,0 +1,65 @@
no_dimar,OBJNAM,lon,lat,feat_type,LITCHR,LITCHR_TXT,SIGGRP,COLOUR,COLOUR_TXT,SIGPER,VALNMR,HEIGHT,ORIENT,INFORM,_dimar_char_raw,_source
242,Boya No. 1,-75.588833,10.318,BOYLAT,2,Fl,,4,green,3,3,4,,Castillete verde,Fl. G 3 s,DIMAR Lista de Luces 2015
243,Boya No. 2,-75.589167,10.314833,BOYLAT,2,Fl,,3,red,3,3,4,,Castillete roja,Fl. R 3 s,DIMAR Lista de Luces 2015
244,Boya No. 3,-75.584833,10.316667,BOYLAT,2,Fl,,4,green,3,3,4,,Castillete verde,Fl. G 3 s,DIMAR Lista de Luces 2015
245,Boya No. 4,-75.585333,10.315167,BOYLAT,2,Fl,,3,red,3,3,4,,Castillete roja,Fl. R 3 s,DIMAR Lista de Luces 2015
246,Boya No. 5,-75.580667,10.316833,BOYLAT,2,Fl,,4,green,3,3,4,,Castillete verde,Fl. G 3 s,AAN-DIMAR-2023-268
247,Boya No. 6,-75.580667,10.3155,BOYLAT,2,Fl,,3,red,3,3,4,,Castillete roja,Fl. R 3 s,DIMAR Lista de Luces 2015
248,Boya No. 7,-75.576083,10.317598,BOYLAT,2,Fl,,4,green,3,3,4,,Castillete verde,Fl. G 3 s,AAN-DIMAR-2022-067
249,Boya No. 8,-75.576167,10.315833,BOYLAT,2,Fl,,3,red,3,3,4,,Castillete roja,Fl. R 3 s,AAN-DIMAR-2025-315
250,Boya No. 9,-75.572333,10.317667,BOYLAT,2,Fl,,4,green,3,3,4,,Castillete verde,Fl. G 3 s,DIMAR Lista de Luces 2015
251,Boya No. 10,-75.572167,10.316,BOYLAT,2,Fl,,3,red,3,3,4,,Castillete roja,Fl. R 3 s,DIMAR Lista de Luces 2015
252,Boya No. 11,-75.565517,10.318358,BOYLAT,4,Q,,4,green,1,3,4,,Castillete verde - Boya de viraje,Q. G 1 s,AAN-DIMAR-2022-193
253,Boya No. 12,-75.563,10.314167,BOYLAT,2,Fl,,3,red,3,3,4,,Castillete roja,Fl. R 3 s,DIMAR Lista de Luces 2015
254,Boya No. 13,-75.556333,10.3235,BOYLAT,2,Fl,,4,green,3,3,4,,Castillete verde,Fl. G 3 s,DIMAR Lista de Luces 2015
255,Boya No. 15,-75.551500,10.327333,BOYLAT,2,Fl,,4,green,3,3,4,,Castillete verde,Fl. G 3 s,AAN-DIMAR-2025-403
260,Boya No. 17,-75.545833,10.331333,BOYLAT,2,Fl,,4,green,3,3,4,,Castillete verde,Fl. G 3 s,AAN-DIMAR-2021-325
261,Boya No. 18,-75.540750,10.320367,BOYLAT,2,Fl,,3,red,3,3,4,,Castillete roja,Fl. R 3 s,AAN-DIMAR-2024-328
262,Boya No. 19,-75.540667,10.319333,BOYLAT,2,Fl,,4,green,3,3,4,,Castillete verde,Fl. G 3 s,DIMAR Lista de Luces 2015
263,Boya No. 20,-75.518,10.3305,BOYLAT,2,Fl,,3,red,3,3,4,,Castillete roja,Fl. R 3 s,DIMAR Lista de Luces 2015
264,Boya No. 21,-75.524000,10.320000,BOYLAT,2,Fl,,4,green,3,3,4,,Castillete verde,Fl. G 3 s,AAN-DIMAR-2021-237
265,Boya No. 22,-75.517350,10.335050,BOYLAT,2,Fl,,3,red,3,3,4,,Castillete roja,Fl. R 3 s,AAN-DIMAR-2025-038
267,Boya No. 23,-75.533080,10.341723,BOYLAT,4,Q,,4,green,1,3,4,,Castillete verde - Boya de viraje,Q. G 1 s,AAN-DIMAR-2023-108
268,Boya No. 24,-75.529667,10.340333,BOYLAT,2,Fl,,3,red,3,3,4,,Castillete roja,Fl. R 3 s,DIMAR Lista de Luces 2015
269,Boya No. 25,-75.532500,10.348333,BOYLAT,2,Fl,,4,green,3,3,4,,Castillete verde,Fl. G 3 s,AAN-DIMAR-2023-183
270,Boya No. 26,-75.518215,10.347442,BOYLAT,2,Fl,,3,red,3,3,4,,Castillete roja,Fl. R 3 s,AAN-DIMAR-2024-273
271,Boya No. 27,-75.532287,10.355342,BOYLAT,2,Fl,,4,green,3,3,4,,Castillete verde,Fl. G 3 s,AAN-DIMAR-2022-626
272,Boya No. 28,-75.520167,10.358833,BOYLAT,2,Fl,,3,red,3,3,4,,Castillete roja,Fl. R 3 s,AAN-DIMAR-2025-452
273,Boya No. 29,-75.5365,10.364167,BOYLAT,4,Q,,4,green,1,3,4,,Castillete verde - Boya de viraje,Q. G 1 s,DIMAR Lista de Luces 2015
274,Boya No. 30,-75.522667,10.364833,BOYLAT,2,Fl,,3,red,3,3,4,,Castillete roja,Fl. R 3 s,DIMAR Lista de Luces 2015
275,Boya No. 31,-75.5445,10.368167,BOYLAT,2,Fl,,4,green,3,3,4,,Castillete verde,Fl. G 3 s,DIMAR Lista de Luces 2015
276,Boya No. 32,-75.533200,10.378533,BOYLAT,2,Fl,,3,red,3,3,4,,Castillete roja,Fl. R 3 s,AAN-DIMAR-2025-625
277,Boya No. 33,-75.543833,10.391333,BOYLAT,2,Fl,,4,green,3,3,4,,Castillete verde,Fl. G 3 s,AAN-DIMAR-2024-263
278,Boya No. 34,-75.539833,10.393167,BOYLAT,4,Q,,3,red,1,3,4,,Castillete roja - Boya de viraje,Q. R 1 s,DIMAR Lista de Luces 2015
279,Boya No. 35,-75.544500,10.394000,BOYLAT,2,Fl,,4,green,3,3,4,,Castillete verde,Fl. G 3 s,AAN-DIMAR-2022-243
280,Boya No. 36,-75.544167,10.396167,BOYLAT,2,Fl,,3,red,3,3,4,,Castillete roja,Fl. R 3 s,AAN-DIMAR-2023-159
281,Boya No. 37,-75.540017,10.396500,BOYLAT,2,Fl,,4,green,3,3,4,,Castillete verde,Fl. G 3 s,AAN-DIMAR-2024-171
282,Boya No. 38,-75.538667,10.395167,BOYLAT,2,Fl,,3,red,3,3,4,,Castillete roja,Fl. R 3 s,DIMAR Lista de Luces 2015
283,Boya No. 39,-75.539668,10.398237,BOYLAT,2,Fl,,4,green,3,3,4,,Castillete verde,Fl. G 3 s,AAN-DIMAR-2021-150
284,Boya No. 40,-75.536833,10.397333,BOYLAT,2,Fl,,3,red,3,3,4,,Castillete roja,Fl. R 3 s,DIMAR Lista de Luces 2015
285,Boya No. 41,-75.545833,10.395147,BOYLAT,2,Fl,,4,green,3,3,4,,Castillete verde,Fl. G 3 s,AAN-DIMAR-2023-016
286,Boya No. 42,-75.544672,10.397017,BOYLAT,2,Fl,,3,red,3,3,4,,Castillete roja,Fl. R 3 s,AAN-DIMAR-2022-154
287,Boya No. 43,-75.550150,10.400018,BOYLAT,2,Fl,,4,green,3,3,4,,Castillete verde,Fl. G 3 s,AAN-DIMAR-2023-371
288,Boya No. 45,-75.55,10.4065,BOYLAT,2,Fl,,4,green,3,3,4,,Castillete verde,Fl. G 3 s,DIMAR Lista de Luces 2015
291,Boya No. 48,-75.545167,10.412000,BOYLAT,2,Fl,,3,red,3,3,4,,Castillete roja,Fl. R 3 s,AAN-DIMAR-2025-089
294,Boya E2,-75.571,10.389667,BOYLAT,2,Fl,,3,red,3,4,3,,Castillete roja,Fl. R 3 s,DIMAR Lista de Luces 2015
295,Boya E1,-75.570667,10.39,BOYLAT,2,Fl,,4,green,3,3,3,,Castillete verde,Fl. G 3 s,DIMAR Lista de Luces 2015
307,Boya No. 50,-75.547167,10.415167,BOYLAT,2,Fl,,3,red,3,3,4,,Castillete roja rojo,Fl. R 3 s,DIMAR Lista de Luces 2015
308,Boya No. 51,-75.547500,10.415167,BOYLAT,2,Fl,,4,green,3,3,3,,Castillete verde,Fl. G 3 s,AAN-DIMAR-2024-296
309,Boya No. 52,-75.549305,10.417628,BOYLAT,2,Fl,,3,red,3,3,3,,Castillete roja,Fl. R 3 s,AAN-DIMAR-2023-032
310,Boya No. 53,-75.549918,10.419850,BOYLAT,2,Fl,,4,green,3,3,3,,Castillete verde,Fl. G 3 s,AAN-DIMAR-2022-262
311,Boya No. 54,-75.5495,10.419,BOYLAT,2,Fl,,3,red,3,3,3,,Castillete roja,Fl. R 3 s,DIMAR Lista de Luces 2015
312,Boya No. 55,-75.55,10.419333,BOYLAT,2,Fl,,4,green,3,3,3,,Castillete verde,Fl. G 3 s,DIMAR Lista de Luces 2015
314,Boya No. 56,-75.55,10.420333,BOYLAT,2,Fl,,3,red,3,3,3,,Castillete roja,Fl. R 3 s,DIMAR Lista de Luces 2015
317,Boya Bifurcacion,-75.529333,10.402667,BOYLAT,2,Fl,,4,green,3,4,3,,Castillete verde bandas rojas,Fl. G 3 s,DIMAR Lista de Luces 2015
318,Boya Verde,-75.529167,10.4035,BOYLAT,2,Fl,,4,green,3,4,3,,Castillete verde,Fl. G 3 s,DIMAR Lista de Luces 2015
325,Boya No. 1 (Sector Compas),-75.529833,10.402167,BOYLAT,2,Fl,,4,green,3,,3,,Castillete verde,Fl. G 3 s,DIMAR Lista de Luces 2015
326,Boya No. 2 (Sector Compas),-75.5305,10.401167,BOYLAT,2,Fl,,3,red,3,,3,,Castillete roja,Fl. R 3 s,DIMAR Lista de Luces 2015
327,Boya No. 3 (Sector Compas),-75.528167,10.4015,BOYLAT,2,Fl,,4,green,3,,3,,Castillete verde,Fl. G 3 s,DIMAR Lista de Luces 2015
328,Boya No. 4 (Sector Compas),-75.528667,10.400667,BOYLAT,2,Fl,,3,red,3,,3,,Castillete roja,Fl. R 3 s,DIMAR Lista de Luces 2015
329,Boya No. 5 (Sector Compas),-75.526167,10.4005,BOYLAT,2,Fl,,4,green,3,,3,,Castillete verde,Fl. G 3 s,DIMAR Lista de Luces 2015
330,Boya No. 6 (Sector Compas),-75.527833,10.4,BOYLAT,2,Fl,,3,red,3,,3,,Castillete roja,Fl. R 3 s,DIMAR Lista de Luces 2015
331,Boya No. 7 (Sector Compas),-75.525667,10.399833,BOYLAT,2,Fl,,4,green,3,,3,,Castillete verde,Fl. G 3 s,DIMAR Lista de Luces 2015
332,Boya No. 8 (Sector Compas),-75.532167,10.397667,BOYLAT,2,Fl,,3,red,3,,3,,Castillete roja,Fl. R 3 s,DIMAR Lista de Luces 2015
333,Boya No. 9 (Sector Compas),-75.525333,10.399333,BOYLAT,2,Fl,,4,green,3,,3,,Castillete verde,Fl. G 3 s,DIMAR Lista de Luces 2015
334,Boya No. 13 (Sector Compas),-75.531167,10.398833,BOYLAT,2,Fl,,4,green,3,,3,,Castillete verde,Fl. G 3 s,DIMAR Lista de Luces 2015
1 no_dimar OBJNAM lon lat feat_type LITCHR LITCHR_TXT SIGGRP COLOUR COLOUR_TXT SIGPER VALNMR HEIGHT ORIENT INFORM _dimar_char_raw _source
2 242 Boya No. 1 -75.588833 10.318 BOYLAT 2 Fl 4 green 3 3 4 Castillete verde Fl. G 3 s DIMAR Lista de Luces 2015
3 243 Boya No. 2 -75.589167 10.314833 BOYLAT 2 Fl 3 red 3 3 4 Castillete roja Fl. R 3 s DIMAR Lista de Luces 2015
4 244 Boya No. 3 -75.584833 10.316667 BOYLAT 2 Fl 4 green 3 3 4 Castillete verde Fl. G 3 s DIMAR Lista de Luces 2015
5 245 Boya No. 4 -75.585333 10.315167 BOYLAT 2 Fl 3 red 3 3 4 Castillete roja Fl. R 3 s DIMAR Lista de Luces 2015
6 246 Boya No. 5 -75.580667 10.316833 BOYLAT 2 Fl 4 green 3 3 4 Castillete verde Fl. G 3 s AAN-DIMAR-2023-268
7 247 Boya No. 6 -75.580667 10.3155 BOYLAT 2 Fl 3 red 3 3 4 Castillete roja Fl. R 3 s DIMAR Lista de Luces 2015
8 248 Boya No. 7 -75.576083 10.317598 BOYLAT 2 Fl 4 green 3 3 4 Castillete verde Fl. G 3 s AAN-DIMAR-2022-067
9 249 Boya No. 8 -75.576167 10.315833 BOYLAT 2 Fl 3 red 3 3 4 Castillete roja Fl. R 3 s AAN-DIMAR-2025-315
10 250 Boya No. 9 -75.572333 10.317667 BOYLAT 2 Fl 4 green 3 3 4 Castillete verde Fl. G 3 s DIMAR Lista de Luces 2015
11 251 Boya No. 10 -75.572167 10.316 BOYLAT 2 Fl 3 red 3 3 4 Castillete roja Fl. R 3 s DIMAR Lista de Luces 2015
12 252 Boya No. 11 -75.565517 10.318358 BOYLAT 4 Q 4 green 1 3 4 Castillete verde - Boya de viraje Q. G 1 s AAN-DIMAR-2022-193
13 253 Boya No. 12 -75.563 10.314167 BOYLAT 2 Fl 3 red 3 3 4 Castillete roja Fl. R 3 s DIMAR Lista de Luces 2015
14 254 Boya No. 13 -75.556333 10.3235 BOYLAT 2 Fl 4 green 3 3 4 Castillete verde Fl. G 3 s DIMAR Lista de Luces 2015
15 255 Boya No. 15 -75.551500 10.327333 BOYLAT 2 Fl 4 green 3 3 4 Castillete verde Fl. G 3 s AAN-DIMAR-2025-403
16 260 Boya No. 17 -75.545833 10.331333 BOYLAT 2 Fl 4 green 3 3 4 Castillete verde Fl. G 3 s AAN-DIMAR-2021-325
17 261 Boya No. 18 -75.540750 10.320367 BOYLAT 2 Fl 3 red 3 3 4 Castillete roja Fl. R 3 s AAN-DIMAR-2024-328
18 262 Boya No. 19 -75.540667 10.319333 BOYLAT 2 Fl 4 green 3 3 4 Castillete verde Fl. G 3 s DIMAR Lista de Luces 2015
19 263 Boya No. 20 -75.518 10.3305 BOYLAT 2 Fl 3 red 3 3 4 Castillete roja Fl. R 3 s DIMAR Lista de Luces 2015
20 264 Boya No. 21 -75.524000 10.320000 BOYLAT 2 Fl 4 green 3 3 4 Castillete verde Fl. G 3 s AAN-DIMAR-2021-237
21 265 Boya No. 22 -75.517350 10.335050 BOYLAT 2 Fl 3 red 3 3 4 Castillete roja Fl. R 3 s AAN-DIMAR-2025-038
22 267 Boya No. 23 -75.533080 10.341723 BOYLAT 4 Q 4 green 1 3 4 Castillete verde - Boya de viraje Q. G 1 s AAN-DIMAR-2023-108
23 268 Boya No. 24 -75.529667 10.340333 BOYLAT 2 Fl 3 red 3 3 4 Castillete roja Fl. R 3 s DIMAR Lista de Luces 2015
24 269 Boya No. 25 -75.532500 10.348333 BOYLAT 2 Fl 4 green 3 3 4 Castillete verde Fl. G 3 s AAN-DIMAR-2023-183
25 270 Boya No. 26 -75.518215 10.347442 BOYLAT 2 Fl 3 red 3 3 4 Castillete roja Fl. R 3 s AAN-DIMAR-2024-273
26 271 Boya No. 27 -75.532287 10.355342 BOYLAT 2 Fl 4 green 3 3 4 Castillete verde Fl. G 3 s AAN-DIMAR-2022-626
27 272 Boya No. 28 -75.520167 10.358833 BOYLAT 2 Fl 3 red 3 3 4 Castillete roja Fl. R 3 s AAN-DIMAR-2025-452
28 273 Boya No. 29 -75.5365 10.364167 BOYLAT 4 Q 4 green 1 3 4 Castillete verde - Boya de viraje Q. G 1 s DIMAR Lista de Luces 2015
29 274 Boya No. 30 -75.522667 10.364833 BOYLAT 2 Fl 3 red 3 3 4 Castillete roja Fl. R 3 s DIMAR Lista de Luces 2015
30 275 Boya No. 31 -75.5445 10.368167 BOYLAT 2 Fl 4 green 3 3 4 Castillete verde Fl. G 3 s DIMAR Lista de Luces 2015
31 276 Boya No. 32 -75.533200 10.378533 BOYLAT 2 Fl 3 red 3 3 4 Castillete roja Fl. R 3 s AAN-DIMAR-2025-625
32 277 Boya No. 33 -75.543833 10.391333 BOYLAT 2 Fl 4 green 3 3 4 Castillete verde Fl. G 3 s AAN-DIMAR-2024-263
33 278 Boya No. 34 -75.539833 10.393167 BOYLAT 4 Q 3 red 1 3 4 Castillete roja - Boya de viraje Q. R 1 s DIMAR Lista de Luces 2015
34 279 Boya No. 35 -75.544500 10.394000 BOYLAT 2 Fl 4 green 3 3 4 Castillete verde Fl. G 3 s AAN-DIMAR-2022-243
35 280 Boya No. 36 -75.544167 10.396167 BOYLAT 2 Fl 3 red 3 3 4 Castillete roja Fl. R 3 s AAN-DIMAR-2023-159
36 281 Boya No. 37 -75.540017 10.396500 BOYLAT 2 Fl 4 green 3 3 4 Castillete verde Fl. G 3 s AAN-DIMAR-2024-171
37 282 Boya No. 38 -75.538667 10.395167 BOYLAT 2 Fl 3 red 3 3 4 Castillete roja Fl. R 3 s DIMAR Lista de Luces 2015
38 283 Boya No. 39 -75.539668 10.398237 BOYLAT 2 Fl 4 green 3 3 4 Castillete verde Fl. G 3 s AAN-DIMAR-2021-150
39 284 Boya No. 40 -75.536833 10.397333 BOYLAT 2 Fl 3 red 3 3 4 Castillete roja Fl. R 3 s DIMAR Lista de Luces 2015
40 285 Boya No. 41 -75.545833 10.395147 BOYLAT 2 Fl 4 green 3 3 4 Castillete verde Fl. G 3 s AAN-DIMAR-2023-016
41 286 Boya No. 42 -75.544672 10.397017 BOYLAT 2 Fl 3 red 3 3 4 Castillete roja Fl. R 3 s AAN-DIMAR-2022-154
42 287 Boya No. 43 -75.550150 10.400018 BOYLAT 2 Fl 4 green 3 3 4 Castillete verde Fl. G 3 s AAN-DIMAR-2023-371
43 288 Boya No. 45 -75.55 10.4065 BOYLAT 2 Fl 4 green 3 3 4 Castillete verde Fl. G 3 s DIMAR Lista de Luces 2015
44 291 Boya No. 48 -75.545167 10.412000 BOYLAT 2 Fl 3 red 3 3 4 Castillete roja Fl. R 3 s AAN-DIMAR-2025-089
45 294 Boya E2 -75.571 10.389667 BOYLAT 2 Fl 3 red 3 4 3 Castillete roja Fl. R 3 s DIMAR Lista de Luces 2015
46 295 Boya E1 -75.570667 10.39 BOYLAT 2 Fl 4 green 3 3 3 Castillete verde Fl. G 3 s DIMAR Lista de Luces 2015
47 307 Boya No. 50 -75.547167 10.415167 BOYLAT 2 Fl 3 red 3 3 4 Castillete roja rojo Fl. R 3 s DIMAR Lista de Luces 2015
48 308 Boya No. 51 -75.547500 10.415167 BOYLAT 2 Fl 4 green 3 3 3 Castillete verde Fl. G 3 s AAN-DIMAR-2024-296
49 309 Boya No. 52 -75.549305 10.417628 BOYLAT 2 Fl 3 red 3 3 3 Castillete roja Fl. R 3 s AAN-DIMAR-2023-032
50 310 Boya No. 53 -75.549918 10.419850 BOYLAT 2 Fl 4 green 3 3 3 Castillete verde Fl. G 3 s AAN-DIMAR-2022-262
51 311 Boya No. 54 -75.5495 10.419 BOYLAT 2 Fl 3 red 3 3 3 Castillete roja Fl. R 3 s DIMAR Lista de Luces 2015
52 312 Boya No. 55 -75.55 10.419333 BOYLAT 2 Fl 4 green 3 3 3 Castillete verde Fl. G 3 s DIMAR Lista de Luces 2015
53 314 Boya No. 56 -75.55 10.420333 BOYLAT 2 Fl 3 red 3 3 3 Castillete roja Fl. R 3 s DIMAR Lista de Luces 2015
54 317 Boya Bifurcacion -75.529333 10.402667 BOYLAT 2 Fl 4 green 3 4 3 Castillete verde bandas rojas Fl. G 3 s DIMAR Lista de Luces 2015
55 318 Boya Verde -75.529167 10.4035 BOYLAT 2 Fl 4 green 3 4 3 Castillete verde Fl. G 3 s DIMAR Lista de Luces 2015
56 325 Boya No. 1 (Sector Compas) -75.529833 10.402167 BOYLAT 2 Fl 4 green 3 3 Castillete verde Fl. G 3 s DIMAR Lista de Luces 2015
57 326 Boya No. 2 (Sector Compas) -75.5305 10.401167 BOYLAT 2 Fl 3 red 3 3 Castillete roja Fl. R 3 s DIMAR Lista de Luces 2015
58 327 Boya No. 3 (Sector Compas) -75.528167 10.4015 BOYLAT 2 Fl 4 green 3 3 Castillete verde Fl. G 3 s DIMAR Lista de Luces 2015
59 328 Boya No. 4 (Sector Compas) -75.528667 10.400667 BOYLAT 2 Fl 3 red 3 3 Castillete roja Fl. R 3 s DIMAR Lista de Luces 2015
60 329 Boya No. 5 (Sector Compas) -75.526167 10.4005 BOYLAT 2 Fl 4 green 3 3 Castillete verde Fl. G 3 s DIMAR Lista de Luces 2015
61 330 Boya No. 6 (Sector Compas) -75.527833 10.4 BOYLAT 2 Fl 3 red 3 3 Castillete roja Fl. R 3 s DIMAR Lista de Luces 2015
62 331 Boya No. 7 (Sector Compas) -75.525667 10.399833 BOYLAT 2 Fl 4 green 3 3 Castillete verde Fl. G 3 s DIMAR Lista de Luces 2015
63 332 Boya No. 8 (Sector Compas) -75.532167 10.397667 BOYLAT 2 Fl 3 red 3 3 Castillete roja Fl. R 3 s DIMAR Lista de Luces 2015
64 333 Boya No. 9 (Sector Compas) -75.525333 10.399333 BOYLAT 2 Fl 4 green 3 3 Castillete verde Fl. G 3 s DIMAR Lista de Luces 2015
65 334 Boya No. 13 (Sector Compas) -75.531167 10.398833 BOYLAT 2 Fl 4 green 3 3 Castillete verde Fl. G 3 s DIMAR Lista de Luces 2015
+5
View File
@@ -0,0 +1,5 @@
no_dimar,OBJNAM,lon,lat,feat_type,LITCHR,LITCHR_TXT,SIGGRP,COLOUR,COLOUR_TXT,SIGPER,VALNMR,HEIGHT,ORIENT,INFORM,_dimar_char_raw,_source
303,Boya Metocean,-75.609667,10.3265,BOYSPEC,2,Fl,,6,yellow,20,7,3.5,,Castillete Amarillo. Sistema datos oceanograficos y meteorologicos.,Fl. Y 20 s,DIMAR Lista de Luces 2015
306,Boya Especial,-75.520333,10.3035,BOYSPEC,2,Fl,,6,yellow,10,1.5,3.5,,Cilindrica amarilla,Fl. Y 10 s,DIMAR Lista de Luces 2015
313,Boya BA7,-75.549667,10.420833,BOYSPEC,2,Fl,,6,yellow,3,3,2.6,,Castillete amarilla,Fl. Y 3 s,DIMAR Lista de Luces 2015
335,Boya Especial No. 3 Isla Manzanillo,-75.530833,10.3955,BOYSPEC,2,Fl,2,6,yellow,10,3,4,,Castillete amarilla,Fl.(2) Y 10 s,DIMAR Lista de Luces 2015
1 no_dimar OBJNAM lon lat feat_type LITCHR LITCHR_TXT SIGGRP COLOUR COLOUR_TXT SIGPER VALNMR HEIGHT ORIENT INFORM _dimar_char_raw _source
2 303 Boya Metocean -75.609667 10.3265 BOYSPEC 2 Fl 6 yellow 20 7 3.5 Castillete Amarillo. Sistema datos oceanograficos y meteorologicos. Fl. Y 20 s DIMAR Lista de Luces 2015
3 306 Boya Especial -75.520333 10.3035 BOYSPEC 2 Fl 6 yellow 10 1.5 3.5 Cilindrica amarilla Fl. Y 10 s DIMAR Lista de Luces 2015
4 313 Boya BA7 -75.549667 10.420833 BOYSPEC 2 Fl 6 yellow 3 3 2.6 Castillete amarilla Fl. Y 3 s DIMAR Lista de Luces 2015
5 335 Boya Especial No. 3 Isla Manzanillo -75.530833 10.3955 BOYSPEC 2 Fl 2 6 yellow 10 3 4 Castillete amarilla Fl.(2) Y 10 s DIMAR Lista de Luces 2015
+19
View File
@@ -0,0 +1,19 @@
no_dimar,OBJNAM,lon,lat,feat_type,LITCHR,LITCHR_TXT,SIGGRP,COLOUR,COLOUR_TXT,SIGPER,VALNMR,HEIGHT,ORIENT,INFORM,_dimar_char_raw,_source
32,Faro Punta Morro Hermoso,-75.0175,10.963333,LIGHTS,2,Fl,,1,white,4,28,134,,Torre blanca bandas rojas. Giratorio.,Fl. W 4 s,DIMAR Lista de Luces 2015
33,Faro Galerazamba,-75.266,10.785333,LIGHTS,2,Fl,,1,white,4,11,14,,Torre fibra de vidrio blanca bandas rojas. Giratorio.,Fl. W 4 s,DIMAR Lista de Luces 2015
34,Faro Punta Canoas,-75.499167,10.573,LIGHTS,2,Fl,2,1,white,20,12,96,,Torre roja bandas blancas. Giratorio.,Fl.(2) W 20 s,DIMAR Lista de Luces 2015
35,Faro Castillogrande,-75.545,10.391,LIGHTS,2,Fl,,1,white,15,12,24,,Torre en concreto color beige.,Fl. W 15 s,DIMAR Lista de Luces 2015
36,Faro Salmedina,-75.651333,10.378333,LIGHTS,2,Fl,,1,white,10,8,5,,Torre roja bandas blancas.,Fl. W 10 s,DIMAR Lista de Luces 2015
37,Faro Tierrabomba,-75.581,10.34,LIGHTS,2,Fl,,1,white,12,26,112,,Torre roja bandas blancas. Giratorio.,Fl. W 12 s,DIMAR Lista de Luces 2015
38,Faro Isla Tesoro,-75.740667,10.235333,LIGHTS,2,Fl,,1,white,6.6,13,20,,Torre metalica roja y blanca.,Fl. W 6.6 s,DIMAR Lista de Luces 2015
39,Faro Isla de Rosario,-75.8005,10.168,LIGHTS,2,Fl,3,1,white,10,12,14,,Torre metalica roja y blanca.,Fl.(3) W 10 s,DIMAR Lista de Luces 2015
40,Faro Isla Mucura,-75.87,9.7835,LIGHTS,2,Fl,,1,white,6.7,11,20,,Torre roja bandas blancas. Giratorio.,Fl. W 6.7 s,DIMAR Lista de Luces 2015
41,Faro Isla Arena,-75.726833,10.144833,LIGHTS,2,Fl,,1,white,12,13,20,,Torre metalica roja y blanca. Giratorio.,Fl. W 12 s,DIMAR Lista de Luces 2015
42,Faro Ceycen,-75.855833,9.692833,LIGHTS,2,Fl,,1,white,12,17,20,,Torre roja bandas blancas. Giratorio.,Fl. W 12 s,DIMAR Lista de Luces 2015
43,Faro Roca Morrosquillo,-75.992167,9.591333,LIGHTS,2,Fl,,1,white,3,10,6,,Torre roja bandas blancas.,Fl. W 3 s,DIMAR Lista de Luces 2015
296,Luz de Aproximacion,-75.5495,10.409,LIGHTS,2,Fl,,5,blue,2.5,11,37,,Torre metalica Roja y blanca,Fl. Bu 2.5 s,DIMAR Lista de Luces 2015
257,Enfilacion de Bocachica B,-75.508833,10.320833,LIGHTS,7,Iso,,5,blue,4,12,33,,Torre enrejada rojo bandas blancas,,DIMAR Lista de Luces 2015
292,Enfilacion No. 1,-75.530500,10.389333,LIGHTS,4,Q,,6,yellow,1,10,22,0,,Torre enrejada roja bandas blancas. Calibracion compases.,Q. Am 1 s,DIMAR Lista de Luces 2015
293,Enfilacion No. 2,-75.530500,10.388500,LIGHTS,4,Q,,6,yellow,1,10,18,0,,Torre enrejada roja bandas blancas. Calibracion compases.,Q. Am 1 s,DIMAR Lista de Luces 2015
322,Baliza de Enfilacion No. 1,-75.524667,10.405000,LIGHTS,2,Fl,,6,yellow,3,5,4,,,Torre amarilla. Marca de dia: amarillo-negro-amarillo.,Fl. Am 3 s,DIMAR Lista de Luces 2015
323,Baliza de Enfilacion No. 2,-75.524167,10.405167,LIGHTS,2,Fl,,6,yellow,3,5,6,,,Torre amarilla. Marca de dia: amarillo-negro-amarillo.,Fl. Am 3 s,DIMAR Lista de Luces 2015
1 no_dimar OBJNAM lon lat feat_type LITCHR LITCHR_TXT SIGGRP COLOUR COLOUR_TXT SIGPER VALNMR HEIGHT ORIENT INFORM _dimar_char_raw _source
2 32 Faro Punta Morro Hermoso -75.0175 10.963333 LIGHTS 2 Fl 1 white 4 28 134 Torre blanca bandas rojas. Giratorio. Fl. W 4 s DIMAR Lista de Luces 2015
3 33 Faro Galerazamba -75.266 10.785333 LIGHTS 2 Fl 1 white 4 11 14 Torre fibra de vidrio blanca bandas rojas. Giratorio. Fl. W 4 s DIMAR Lista de Luces 2015
4 34 Faro Punta Canoas -75.499167 10.573 LIGHTS 2 Fl 2 1 white 20 12 96 Torre roja bandas blancas. Giratorio. Fl.(2) W 20 s DIMAR Lista de Luces 2015
5 35 Faro Castillogrande -75.545 10.391 LIGHTS 2 Fl 1 white 15 12 24 Torre en concreto color beige. Fl. W 15 s DIMAR Lista de Luces 2015
6 36 Faro Salmedina -75.651333 10.378333 LIGHTS 2 Fl 1 white 10 8 5 Torre roja bandas blancas. Fl. W 10 s DIMAR Lista de Luces 2015
7 37 Faro Tierrabomba -75.581 10.34 LIGHTS 2 Fl 1 white 12 26 112 Torre roja bandas blancas. Giratorio. Fl. W 12 s DIMAR Lista de Luces 2015
8 38 Faro Isla Tesoro -75.740667 10.235333 LIGHTS 2 Fl 1 white 6.6 13 20 Torre metalica roja y blanca. Fl. W 6.6 s DIMAR Lista de Luces 2015
9 39 Faro Isla de Rosario -75.8005 10.168 LIGHTS 2 Fl 3 1 white 10 12 14 Torre metalica roja y blanca. Fl.(3) W 10 s DIMAR Lista de Luces 2015
10 40 Faro Isla Mucura -75.87 9.7835 LIGHTS 2 Fl 1 white 6.7 11 20 Torre roja bandas blancas. Giratorio. Fl. W 6.7 s DIMAR Lista de Luces 2015
11 41 Faro Isla Arena -75.726833 10.144833 LIGHTS 2 Fl 1 white 12 13 20 Torre metalica roja y blanca. Giratorio. Fl. W 12 s DIMAR Lista de Luces 2015
12 42 Faro Ceycen -75.855833 9.692833 LIGHTS 2 Fl 1 white 12 17 20 Torre roja bandas blancas. Giratorio. Fl. W 12 s DIMAR Lista de Luces 2015
13 43 Faro Roca Morrosquillo -75.992167 9.591333 LIGHTS 2 Fl 1 white 3 10 6 Torre roja bandas blancas. Fl. W 3 s DIMAR Lista de Luces 2015
14 296 Luz de Aproximacion -75.5495 10.409 LIGHTS 2 Fl 5 blue 2.5 11 37 Torre metalica Roja y blanca Fl. Bu 2.5 s DIMAR Lista de Luces 2015
15 257 Enfilacion de Bocachica B -75.508833 10.320833 LIGHTS 7 Iso 5 blue 4 12 33 Torre enrejada rojo bandas blancas DIMAR Lista de Luces 2015
16 292 Enfilacion No. 1 -75.530500 10.389333 LIGHTS 4 Q 6 yellow 1 10 22 0 Torre enrejada roja bandas blancas. Calibracion compases. Q. Am 1 s DIMAR Lista de Luces 2015
17 293 Enfilacion No. 2 -75.530500 10.388500 LIGHTS 4 Q 6 yellow 1 10 18 0 Torre enrejada roja bandas blancas. Calibracion compases. Q. Am 1 s DIMAR Lista de Luces 2015
18 322 Baliza de Enfilacion No. 1 -75.524667 10.405000 LIGHTS 2 Fl 6 yellow 3 5 4 Torre amarilla. Marca de dia: amarillo-negro-amarillo. Fl. Am 3 s DIMAR Lista de Luces 2015
19 323 Baliza de Enfilacion No. 2 -75.524167 10.405167 LIGHTS 2 Fl 6 yellow 3 5 6 Torre amarilla. Marca de dia: amarillo-negro-amarillo. Fl. Am 3 s DIMAR Lista de Luces 2015
+105
View File
@@ -0,0 +1,105 @@
{
"_comment": "S-57 cell metadata. Edit before converting.",
"cell_name": "CO1CO01M",
"cell_edition": 1,
"update_number": 0,
"issue_date": "20260427",
"producer_code": "CO",
"producer_name": "Custom Chart",
"data_set_name": "My ENC Chart",
"scale": 50000,
"comment": "Generated by QGIS S-57 Converter",
"horizontal_datum": "WGS84",
"vertical_datum": "MLLW",
"sounding_datum": "MLLW",
"compilation_scale": 50000,
"layer_mappings": {
"_comment": "Map your QGIS layer names to S-57 object class acronyms. Case-insensitive.",
"coastline": "COALNE",
"coast_line": "COALNE",
"linea_de_costa": "COALNE",
"costa": "COALNE",
"land": "LNDARE",
"tierra": "LNDARE",
"tierra_firme": "LNDARE",
"depth_area": "DEPARE",
"area_profundidad": "DEPARE",
"fondos": "DEPARE",
"batimetria": "DEPARE",
"depth_contour": "DEPCNT",
"isobata": "DEPCNT",
"curvas_nivel": "DEPCNT",
"soundings": "SOUNDG",
"sondas": "SOUNDG",
"profundidades": "SOUNDG",
"lights": "LIGHTS",
"luces": "LIGHTS",
"faroles": "LIGHTS",
"boyas": "BOYLAT",
"buoys": "BOYLAT",
"balizas": "BCNLAT",
"beacons": "BCNLAT",
"anchorage": "ACHARE",
"fondeadero": "ACHARE",
"harbor": "HRBARE",
"puerto": "HRBARE",
"berth": "BERTHS",
"atraque": "BERTHS",
"wreck": "WRECKS",
"naufragio": "WRECKS",
"obstruction": "OBSTRN",
"obstruccion": "OBSTRN",
"rocks": "UWTROC",
"rocas": "UWTROC",
"fairway": "FAIRWY",
"canal_navegacion": "FAIRWY",
"restricted": "RESARE",
"zona_restringida": "RESARE",
"cable": "CBLSUB",
"tuberia": "PIPSOL",
"bridge": "BRIDGE",
"puente": "BRIDGE",
"river": "RIVERS",
"rio": "RIVERS",
"seabed": "SBDARE",
"Linderos": "COALNE",
"Puntos del Terreno": "LNDMRK",
"Área Terreno": "LNDARE",
"Área Terreno taxable": "LNDARE",
"fondo_marino": "SBDARE"
},
"attribute_mappings": {
"_comment": "Map your SHP field names to S-57 attribute names.",
"name": "OBJNAM",
"nombre": "OBJNAM",
"height": "HEIGHT",
"altura": "HEIGHT",
"colour": "COLOUR",
"color": "COLOUR",
"colpat": "COLPAT",
"patron": "COLPAT",
"catlam": "CATLAM",
"lateral": "CATLAM",
"boyshp": "BOYSHP",
"forma": "BOYSHP",
"litchr": "LITCHR",
"destello": "LITCHR",
"sigper": "SIGPER",
"periodo": "SIGPER",
"siggrp": "SIGGRP",
"grupo": "SIGGRP",
"alcance": "VALNMR",
"range": "VALNMR",
"valnmr": "VALNMR",
"status": "STATUS",
"estado": "STATUS",
"depth": "DRVAL1",
"profundidad":"DRVAL1",
"depth_min": "DRVAL1",
"depth_max": "DRVAL2",
"contour": "VALDCO",
"valor": "VALDCO",
"sounding": "VALSOU",
"sonda": "VALSOU"
}
}
+10
View File
@@ -0,0 +1,10 @@
import geopandas as gpd
path = r"C:\Users\aerom\CO1CO01M.000"
for layer in ["COALNE", "LNDARE", "LNDMRK", "M_COVR"]:
try:
gdf = gpd.read_file(path, layer=layer)
nulls = gdf.geometry.isna().sum()
print(f"{layer}: {len(gdf)} features, {nulls} sin geometria")
except Exception as e:
print(f"{layer}: ERROR - {e}")
+881
View File
@@ -0,0 +1,881 @@
#!/usr/bin/env python3
"""
QGIS -> S-57 ENC Converter
Zero external dependencies for geometry: reads SHP/DBF using struct only.
pyproj is used for reprojection (optional — falls back to passthrough if missing).
Usage:
python converter.py myproject.qgs
python converter.py myproject.qgz --output my_chart.000
python converter.py myproject.qgs --list
python converter.py myproject.qgs --config cell_config.json
"""
import argparse
import json
import math
import struct
import sys
import zipfile
import xml.etree.ElementTree as ET
from pathlib import Path
from datetime import datetime
SCRIPT_DIR = Path(__file__).parent
sys.path.insert(0, str(SCRIPT_DIR))
from s57_writer import (
S57Cell,
OBJL_BY_ACRONYM, ATTR_CODE,
OBJL_LIGHTS,
ATTL_CATCOV, ATTL_VALSOU, ATTL_LITCHR, ATTL_COLOUR,
)
try:
from pyproj import CRS, Transformer
PYPROJ_AVAILABLE = True
except Exception:
PYPROJ_AVAILABLE = False
# ── SHP shape-type sets ───────────────────────────────────────────────────────
_PT = {1, 11, 21}
_MPT = {8, 18, 28}
_LINE = {3, 13, 23}
_POLY = {5, 15, 25}
# ── minimal DBF reader (stdlib only) ─────────────────────────────────────────
def _read_dbf(dbf_path: Path):
"""Return (field_names, list_of_dicts) from a dBASE III .dbf file."""
for enc in ("utf-8", "latin-1", "cp1252"):
try:
with open(dbf_path, "rb") as f:
f.read(4) # version + date
nrec = struct.unpack("<I", f.read(4))[0]
hdrsz = struct.unpack("<H", f.read(2))[0]
recsz = struct.unpack("<H", f.read(2))[0]
f.read(20) # reserved
fields = []
while True:
raw = f.read(32)
if not raw or raw[0] == 0x0D:
break
name = raw[:11].rstrip(b"\x00").decode("ascii", errors="replace")
flen = raw[16]
fields.append((name, flen))
f.seek(hdrsz)
rows = []
for _ in range(nrec):
rec = f.read(recsz)
if not rec:
break
if rec[0] == 0x2A: # deleted
continue
row = {}
off = 1
for fname, flen in fields:
raw_val = rec[off:off+flen]
try:
val = raw_val.decode(enc).strip()
except UnicodeDecodeError:
val = raw_val.decode("latin-1").strip()
if val:
row[fname.lower()] = val
off += flen
rows.append(row)
return [f[0].lower() for f in fields], rows
except UnicodeDecodeError:
continue
return [], []
# ── minimal SHP reader (stdlib only) ─────────────────────────────────────────
def _read_shp(shp_path: Path):
"""Yield (shape_type, points, parts) from a .shp file.
points: list of (x, y) tuples
parts: list of part start indices (for Polyline/Polygon)
"""
with open(shp_path, "rb") as f:
f.read(100) # skip file header
while True:
hdr = f.read(8)
if len(hdr) < 8:
break
content_len = struct.unpack(">ii", hdr)[1] * 2
content = f.read(content_len)
if len(content) < 4:
break
stype = struct.unpack_from("<i", content, 0)[0]
if stype == 0:
yield stype, [], []
continue
if stype in _PT:
x, y = struct.unpack_from("<dd", content, 4)
yield stype, [(x, y)], []
elif stype in _MPT:
npts = struct.unpack_from("<i", content, 36)[0]
pts = [struct.unpack_from("<dd", content, 40 + i*16)
for i in range(npts)]
yield stype, pts, []
elif stype in _LINE | _POLY:
nparts = struct.unpack_from("<i", content, 36)[0]
npts = struct.unpack_from("<i", content, 40)[0]
parts = [struct.unpack_from("<i", content, 44 + i*4)[0]
for i in range(nparts)]
off = 44 + nparts * 4
pts = [struct.unpack_from("<dd", content, off + i*16)
for i in range(npts)]
yield stype, pts, parts
# ── signed area (Shoelace) — ESRI outer rings are CW → negative ──────────────
def _signed_area(pts):
n = len(pts)
return sum(
pts[i][0] * pts[(i+1)%n][1] - pts[(i+1)%n][0] * pts[i][1]
for i in range(n)
) / 2.0
# ── Geometry simplification ───────────────────────────────────────────────────
# ISO 8211 leader field = 5 digits → DR max 99,999 bytes.
# SG2D: each coord pair = 8 bytes (2 × int32). Max ~12,000 pairs per record.
# For safety, keep max 8,000 vertices per ring/line with RDP simplification.
_MAX_VERTICES = 8000 # hard limit per ring or line feature
_RDP_TOL_DEG = 1e-5 # ~1 m at equator — safe for 1:50 000 charts
def _perp_dist_sq(p, a, b):
"""Squared perpendicular distance from p to segment a→b (2-D, degrees)."""
ax, ay = a; bx, by = b; px, py = p
dx, dy = bx - ax, by - ay
if dx == 0 and dy == 0:
return (px - ax) ** 2 + (py - ay) ** 2
t = ((px - ax) * dx + (py - ay) * dy) / (dx * dx + dy * dy)
t = max(0.0, min(1.0, t))
return (px - ax - t * dx) ** 2 + (py - ay - t * dy) ** 2
def _rdp(pts, tol_sq):
"""Ramer-Douglas-Peucker (iterative stack version, no recursion limit)."""
if len(pts) <= 2:
return list(pts)
keep = [False] * len(pts)
keep[0] = keep[-1] = True
stack = [(0, len(pts) - 1)]
while stack:
s, e = stack.pop()
if e - s < 2:
continue
max_d, max_i = 0.0, s
for i in range(s + 1, e):
d = _perp_dist_sq(pts[i], pts[s], pts[e])
if d > max_d:
max_d, max_i = d, i
if max_d > tol_sq:
keep[max_i] = True
stack.append((s, max_i))
stack.append((max_i, e))
return [p for p, k in zip(pts, keep) if k]
def _simplify_coords(coords, max_verts=_MAX_VERTICES, tol_deg=_RDP_TOL_DEG):
"""
Simplify a coordinate list so it fits within the ISO 8211 record limit.
1) Apply RDP at tol_deg.
2) If still > max_verts, apply RDP at escalating tolerance until fits.
"""
if len(coords) <= max_verts:
return coords
tol_sq = tol_deg ** 2
result = _rdp(coords, tol_sq)
# Escalate tolerance if still too many
factor = 10.0
while len(result) > max_verts and factor < 1e6:
result = _rdp(coords, (tol_deg * factor) ** 2)
factor *= 10.0
# Last resort: uniform decimation
if len(result) > max_verts:
step = math.ceil(len(result) / max_verts)
result = result[::step]
if result[-1] != coords[-1]:
result.append(coords[-1]) # keep last point
n_in, n_out = len(coords), len(result)
if n_out < n_in:
print(f" [simplify] {n_in} -> {n_out} vertices (tol ~{tol_deg*factor:.6f}°)")
return result
# ── S-57 object catalog ───────────────────────────────────────────────────────
def load_s57_objects():
path = SCRIPT_DIR / "s57_objects.json"
if path.exists():
with open(path, encoding="utf-8") as f:
data = json.load(f)
return {k: v for k, v in data.items() if not k.startswith("_")}
return {}
S57_OBJECTS = load_s57_objects()
# ── config ────────────────────────────────────────────────────────────────────
def load_config(path=None):
cfg_path = Path(path) if path else SCRIPT_DIR / "cell_config.json"
if not cfg_path.exists():
print(f"[WARN] Config not found: {cfg_path}. Using defaults.")
return _default_config()
with open(cfg_path, encoding="utf-8") as f:
cfg = json.load(f)
cfg = {k: v for k, v in cfg.items() if not k.startswith("_")}
cfg.setdefault("layer_mappings", {})
cfg.setdefault("attribute_mappings", {})
return cfg
def _default_config():
return {
"cell_name": "XX1XX01M", "cell_edition": 1, "update_number": 0,
"issue_date": datetime.now().strftime("%Y%m%d"),
"producer_code": "XX", "producer_name": "Custom",
"data_set_name": "ENC Chart", "scale": 50000, "comment": "",
"horizontal_datum": "WGS84", "vertical_datum": "MLLW",
"sounding_datum": "MLLW", "compilation_scale": 50000,
"layer_mappings": {}, "attribute_mappings": {},
}
# ── QGIS project parser ───────────────────────────────────────────────────────
class QGISProject:
def __init__(self, project_path):
self.project_path = Path(project_path)
self.base_dir = self.project_path.parent
self.layers = []
self._parse()
def _parse(self):
if self.project_path.suffix.lower() == ".qgz":
self._parse_qgz()
else:
self._parse_qgs(self.project_path)
def _parse_qgz(self):
with zipfile.ZipFile(self.project_path, "r") as z:
qgs_files = [f for f in z.namelist() if f.endswith(".qgs")]
if not qgs_files:
raise ValueError("No .qgs file inside .qgz")
with z.open(qgs_files[0]) as f:
content = f.read().decode("utf-8")
tmp = self.project_path.parent / "_tmp_project.qgs"
tmp.write_text(content, encoding="utf-8")
self._parse_qgs(tmp)
tmp.unlink(missing_ok=True)
def _parse_qgs(self, qgs_path):
tree = ET.parse(qgs_path)
root = tree.getroot()
for ltl in root.iter("layer-tree-layer"):
lid = ltl.get("id", "")
name = ltl.get("name", "unnamed")
vis = ltl.get("checked", "Qt::Checked") != "Qt::Unchecked"
ml = self._find_maplayer(root, lid)
if ml is None or ml.get("type", "") != "vector":
continue
ds = ml.find("datasource")
if ds is None:
continue
ds_text = (ds.text or "").strip()
crs_el = ml.find(".//srs/spatialrefsys/authid")
crs = crs_el.text if crs_el is not None else "EPSG:4326"
# ── Capa SHP ──────────────────────────────────────────────────
shp = self._resolve_path(ds_text.split("|")[0].strip())
if shp is not None and str(shp).lower().endswith(".shp"):
self.layers.append({
"id": lid, "name": name, "path": shp,
"crs": crs, "visible": vis, "layer_type": "shp",
})
continue
# ── Capa CSV / texto delimitado ───────────────────────────────
# QGIS guarda: file:///ruta/al/archivo.csv?delimiter=,&xField=lon&yField=lat&...
csv_path = self._resolve_csv_path(ds_text)
if csv_path is not None:
# Leer xField / yField del URI
x_field = "lon"
y_field = "lat"
for part in ds_text.split("?")[-1].split("&"):
if part.startswith("xField="):
x_field = part.split("=", 1)[1]
elif part.startswith("yField="):
y_field = part.split("=", 1)[1]
self.layers.append({
"id": lid, "name": name, "path": csv_path,
"crs": crs, "visible": vis, "layer_type": "csv",
"x_field": x_field, "y_field": y_field,
})
def _resolve_csv_path(self, ds_text):
"""Extrae y resuelve la ruta de una datasource CSV de QGIS."""
import urllib.parse
# Formatos: file:///C:/ruta/file.csv?... o /ruta/file.csv
raw = ds_text.split("?")[0]
if raw.startswith("file:///"):
raw = raw[8:] # quitar file:///
elif raw.startswith("file://"):
raw = raw[7:]
raw = urllib.parse.unquote(raw)
p = Path(raw)
if p.exists() and p.suffix.lower() == ".csv":
return p
# Intentar resolver relativo al proyecto
rel = self.base_dir / raw
if rel.exists() and rel.suffix.lower() == ".csv":
return rel.resolve()
return None
def _find_maplayer(self, root, lid):
for ml in root.iter("maplayer"):
el = ml.find("id")
if el is not None and el.text == lid:
return ml
return None
def _resolve_path(self, path_str):
p = Path(path_str)
if p.is_absolute() and p.exists():
return p
rel = self.base_dir / path_str
if rel.exists():
return rel.resolve()
for c in self.base_dir.rglob(p.name):
return c
return None
# ── layer -> S-57 class resolver ─────────────────────────────────────────────
def resolve_s57_class(layer_name, layer_mappings):
nl = layer_name.lower().strip()
if nl in layer_mappings:
return layer_mappings[nl].upper()
for key, val in layer_mappings.items():
if key in nl or nl in key:
return val.upper()
nu = layer_name.upper().strip()
if nu in S57_OBJECTS:
return nu
for acro, info in S57_OBJECTS.items():
if any(w in nl for w in info["desc"].lower().split() if len(w) > 3):
return acro
return None
# ── SHP feature iterator (stdlib only) ───────────────────────────────────────
def iter_shapes(shp_path: Path, crs_str: str, attr_map: dict):
"""Yield (geom_type, coords_wgs84, mapped_attrs) using only stdlib."""
# Reprojection
transformer = None
if PYPROJ_AVAILABLE and crs_str:
try:
src = CRS.from_user_input(crs_str)
wgs84 = CRS.from_epsg(4326)
if not src.equals(wgs84):
transformer = Transformer.from_crs(src, wgs84, always_xy=True)
except Exception as e:
print(f" [WARN] Reprojection unavailable ({e}); assuming WGS84")
def tr(x, y):
return transformer.transform(x, y) if transformer else (x, y)
def tr_pts(pts):
return [tr(p[0], p[1]) for p in pts]
# Read attributes from .dbf
dbf_path = shp_path.with_suffix(".dbf")
_, dbf_rows = _read_dbf(dbf_path) if dbf_path.exists() else ([], [])
# Iterate geometry
for idx, (stype, pts, parts) in enumerate(_read_shp(shp_path)):
if stype == 0:
continue
raw = dbf_rows[idx] if idx < len(dbf_rows) else {}
mapped = []
for shp_col, s57_acro in attr_map.items():
if shp_col in raw:
attl = ATTR_CODE.get(s57_acro.upper())
if attl is not None:
mapped.append((attl, raw[shp_col]))
# ── Auto-detect: SHP column name == S-57 attribute acronym ──────────
already_attls = {a for a, _ in mapped}
raw_upper = {k.upper(): v for k, v in raw.items()}
for s57_acro, attl in ATTR_CODE.items():
if attl in already_attls:
continue
if s57_acro in raw_upper:
val = raw_upper[s57_acro]
val_str = str(val).strip() if val is not None else ""
# Skip DBF nulls: empty, all-asterisks, or all-zeros (numeric null)
if val_str and not all(c in "*0 " for c in val_str):
mapped.append((attl, val_str))
already_attls.add(attl)
# ── COLOUR_TXT override: text name → correct S-57 colour code ───────
# SHP/QGIS may store COLOUR as 0-indexed or wrong-offset numeric;
# COLOUR_TXT (the human name) is the ground truth.
_CNAME = {
"white": "1", "black": "2", "red": "3", "green": "4",
"blue": "5", "yellow": "6", "grey": "7", "gray": "7",
"brown": "8", "amber": "9", "orange": "11",
"magenta": "12", "violet": "13",
}
attl_colour = ATTR_CODE.get("COLOUR")
if attl_colour is not None and "COLOUR_TXT" in raw_upper:
cname = raw_upper["COLOUR_TXT"].lower().strip()
s57c = _CNAME.get(cname)
if s57c:
mapped = [(a, v) for a, v in mapped if a != attl_colour]
mapped.append((attl_colour, s57c))
already_attls.discard(attl_colour)
already_attls.add(attl_colour)
# ── Infer CATLAM from colour when absent (IALA B: green=port, red=stbd)
attl_catlam = ATTR_CODE.get("CATLAM")
if attl_catlam is not None and attl_catlam not in already_attls and attl_colour is not None:
colour_val = next((v for a, v in mapped if a == attl_colour), None)
if colour_val == "4": # green
mapped.append((attl_catlam, "1")) # port-hand
elif colour_val == "3": # red
mapped.append((attl_catlam, "2")) # starboard-hand
if stype in _PT and pts:
yield "point", [tr(*pts[0])], mapped
elif stype in _MPT:
for pt in pts:
yield "point", [tr(*pt)], mapped
elif stype in _LINE:
bounds = list(parts) + [len(pts)]
for i in range(len(bounds) - 1):
seg = pts[bounds[i]:bounds[i+1]]
if len(seg) >= 2:
yield "line", tr_pts(seg), mapped
elif stype in _POLY:
bounds = list(parts) + [len(pts)]
for i in range(len(bounds) - 1):
ring = pts[bounds[i]:bounds[i+1]]
if len(ring) < 3:
continue
# ESRI outer rings are CW (negative shoelace area); skip CCW holes
if _signed_area([(p[0], p[1]) for p in ring]) > 0:
continue
yield "polygon", tr_pts(ring), mapped
# ── S-57 cell writer ──────────────────────────────────────────────────────────
class S57CellWriter:
def __init__(self, output_path, config):
self.output_path = Path(output_path)
self.cfg = config
self._cell = None
self._bbox = None
self._feature_counter = {}
def open(self):
cfg = self.cfg
issue = cfg.get("issue_date") or datetime.now().strftime("%Y%m%d")
self._cell = S57Cell(
dsnm = cfg.get("cell_name", "CHART01") + ".000",
edition = int(cfg.get("cell_edition", 1)),
intu = 5,
scale = int(cfg.get("scale", 50000)),
agen = 999,
comt = cfg.get("comment", "Generated by QGISS57Converter"),
issue_date = issue,
)
def _update_bbox(self, coords):
for x, y in coords:
if self._bbox is None:
self._bbox = [x, y, x, y]
else:
if x < self._bbox[0]: self._bbox[0] = x
if y < self._bbox[1]: self._bbox[1] = y
if x > self._bbox[2]: self._bbox[2] = x
if y > self._bbox[3]: self._bbox[3] = y
def add_features_from_csv(self, csv_path: Path, s57_class: str,
attr_map: dict, x_field: str = "lon",
y_field: str = "lat") -> int:
"""Lee una capa CSV de QGIS (puntos) y la convierte a features S-57.
Estándar IHO S-57: usa los nombres de atributo S-57 como cabeceras de
columna (LITCHR, COLOUR, VALNMR, BOYSHP, CATLAM, …). El converter los
recoge automáticamente sin necesidad de ningún mapeo adicional.
Columna especial feat_type:
Si una fila tiene la columna 'feat_type' con un acrónimo S-57
válido (BCNLAT, BOYLAT, LIGHTS, …), esa fila usa ese objeto en
lugar del s57_class del nivel de capa. Esto permite mezclar tipos
en un solo CSV (p.ej. la carta de Barranquilla que incluye BCNLAT,
BOYLAT, LIGHTS y BOYCAR en el mismo archivo).
Luces compañeras (companion LIGHTS):
Cuando una estructura física (BCNLAT, BOYLAT, BCNCAR, BOYCAR,
BOYISD, BOYSAW, BOYSPP, LNDMRK) tiene LITCHR definido, el
converter emite además un objeto LIGHTS co-ubicado con solo los
atributos de luz. Así, el ECDIS puede mostrar tanto el símbolo 3D
de la estructura como la descripción de luz en el tooltip, igual
que en las cartas NOAA.
Columnas privadas (prefijo _):
Las columnas que empiezan por _ (p.ej. _source, _dimar_char_raw)
se ignoran y nunca se escriben al S-57.
"""
import csv as _csv
# LITCHR_TXT → código S-57 oficial (para CSVs con texto legible)
_LITCHR_TXT = {
"f": "1", "fl": "2", "lfl": "3", "q": "4",
"vq": "5", "uq": "6", "iso": "7", "oc": "8",
"iq": "9", "ivq": "10", "iuq": "11", "mo": "12",
"ffl": "13", "fl+lfl":"14", "oc+fl": "15",
"al.oc": "25", "al.lfl":"26", "al.fl": "27", "al.grp":"28",
}
# COLOUR_TXT → código S-57 oficial
_COLOUR_TXT = {
"white":"1", "black":"2", "red":"3", "green":"4",
"blue":"5", "yellow":"6", "grey":"7", "gray":"7",
"brown":"8", "amber":"9", "violet":"10", "orange":"11", "magenta":"12",
}
# S-57 classes that represent physical structures and may carry light attrs
_STRUCT_CLASSES = {
"BCNLAT","BCNCAR","BCNISD","BCNSAW","BCNSPP","BCNWTW",
"BOYLAT","BOYCAR","BOYISD","BOYSAW","BOYSPP",
"LNDMRK","LITFLT","LITVES",
}
# Attribute codes for companion LIGHTS
_LIGHT_ATTL = {ATTR_CODE[a] for a in
("LITCHR","SIGGRP","SIGPER","COLOUR","VALNMR","HEIGHT",
"SECTR1","SECTR2","ORIENT","MLTYLT","CATLIT","OBJNAM")
if a in ATTR_CODE}
if self._cell is None:
raise RuntimeError("call open() first")
count = 0
with open(csv_path, newline="", encoding="utf-8-sig") as f:
reader = _csv.DictReader(f)
for row in reader:
try:
lon = float(row[x_field])
lat = float(row[y_field])
except (KeyError, ValueError):
continue
# Determine S-57 object class for this row
row_class = (row.get("feat_type") or "").strip().upper()
if not row_class:
row_class = s57_class.upper()
objl = OBJL_BY_ACRONYM.get(row_class)
if objl is None:
print(f" [WARN] Unknown S-57 class '{row_class}' in row, skipping")
continue
# Build attribute list using S-57 column name auto-detection
already_attls: set[int] = set()
mapped: list[tuple[int, str]] = []
raw_upper = {k.upper(): v for k, v in row.items()
if not k.startswith("_") and k not in (x_field, y_field, "feat_type")}
for s57_acro, attl in ATTR_CODE.items():
if attl in already_attls:
continue
if s57_acro in raw_upper:
val = raw_upper[s57_acro].strip()
if val and not all(c in "*0 " for c in val):
mapped.append((attl, val))
already_attls.add(attl)
# Also apply attribute_mappings from config
for shp_col, s57_acro in attr_map.items():
attl = ATTR_CODE.get(s57_acro.upper())
if attl and attl not in already_attls and shp_col.upper() in raw_upper:
val = raw_upper[shp_col.upper()].strip()
if val:
mapped.append((attl, val))
already_attls.add(attl)
# LITCHR_TXT override — parse readable chars like "Q(4)G" → 4
attl_litchr = ATTR_CODE.get("LITCHR")
if attl_litchr and "LITCHR_TXT" in raw_upper and attl_litchr not in already_attls:
txt = raw_upper["LITCHR_TXT"].lower().split("(")[0].strip()
code = _LITCHR_TXT.get(txt)
if code:
mapped.append((attl_litchr, code))
already_attls.add(attl_litchr)
elif attl_litchr and "LITCHR_TXT" in raw_upper and attl_litchr in already_attls:
# Correct an already-set LITCHR if TXT provides a better value
txt = raw_upper["LITCHR_TXT"].lower().split("(")[0].strip()
code = _LITCHR_TXT.get(txt)
if code:
mapped = [(a, v) for a, v in mapped if a != attl_litchr]
mapped.append((attl_litchr, code))
# COLOUR_TXT override
attl_colour = ATTR_CODE.get("COLOUR")
if attl_colour and "COLOUR_TXT" in raw_upper:
cname = raw_upper["COLOUR_TXT"].lower().strip()
s57c = _COLOUR_TXT.get(cname)
if s57c:
mapped = [(a, v) for a, v in mapped if a != attl_colour]
mapped.append((attl_colour, s57c))
# Write the main object
self._cell.add_point_feature(objl=objl, lon=lon, lat=lat,
attrs=mapped if mapped else None)
self._update_bbox([(lon, lat)])
count += 1
# ── Companion LIGHTS ─────────────────────────────────────────
# When a physical structure carries light data, emit a co-located
# LIGHTS object so ECDIS proximity-merge picks up the light desc.
if row_class in _STRUCT_CLASSES and attl_litchr in already_attls:
light_attrs = [(a, v) for a, v in mapped if a in _LIGHT_ATTL]
if light_attrs:
self._cell.add_point_feature(
objl=OBJL_LIGHTS, lon=lon, lat=lat,
attrs=light_attrs,
)
self._feature_counter[s57_class] = (
self._feature_counter.get(s57_class, 0) + count
)
return count
def add_features_from_shp(self, shp_path: Path, crs_str: str,
s57_class: str, attr_map: dict) -> int:
if self._cell is None:
raise RuntimeError("call open() first")
objl = OBJL_BY_ACRONYM.get(s57_class.upper())
if objl is None:
print(f" [WARN] Unknown S-57 class '{s57_class}', skipping")
return 0
count = 0
try:
for geom_type, coords, attrs in iter_shapes(shp_path, crs_str, attr_map):
if not coords:
continue
self._update_bbox(coords)
a = attrs or None
if geom_type == "point":
self._cell.add_point_feature(objl=objl, lon=coords[0][0],
lat=coords[0][1], attrs=a)
count += 1
elif geom_type == "line" and len(coords) >= 2:
coords = _simplify_coords(list(coords))
if len(coords) >= 2:
self._cell.add_line_feature(objl=objl, coords=coords, attrs=a)
count += 1
elif geom_type == "polygon" and len(coords) >= 3:
coords = _simplify_coords(list(coords))
if len(coords) >= 3:
self._cell.add_area_feature(objl=objl, ring=coords, attrs=a)
count += 1
except AssertionError as e:
print(f" [ERR] ISO 8211 record too large in {shp_path.name}: {e}")
print(f" [ERR] Reduce geometry complexity or increase _MAX_VERTICES in converter.py")
except Exception as e:
print(f" [ERR] {shp_path.name}: {e}")
self._feature_counter[s57_class] = (
self._feature_counter.get(s57_class, 0) + count
)
return count
def close(self):
if self._cell is None:
return
if self._bbox:
w, s, e, n = self._bbox
buf = 0.001
ring = [(w-buf, s-buf), (e+buf, s-buf), (e+buf, n+buf),
(w-buf, n+buf), (w-buf, s-buf)]
self._cell.add_area_feature(
objl = OBJL_BY_ACRONYM["M_COVR"],
ring = ring,
attrs = [(ATTL_CATCOV, "1")],
)
print(" M_COVR 1 Coverage (auto-generated)")
self._cell.write(self.output_path)
self._cell = None
def summary(self):
print("\nFeatures written:")
total = 0
for cls, cnt in self._feature_counter.items():
desc = S57_OBJECTS.get(cls, {}).get("desc", "")
# Replace non-cp1252 chars (e.g. →) to avoid UnicodeEncodeError on Windows console
desc = desc.encode("cp1252", errors="replace").decode("cp1252")
print(f" {cls:<12} {cnt:>5} {desc}")
total += cnt
print(f" {'TOTAL':<12} {total:>5}")
S57Writer = S57CellWriter # backward-compat alias
# ── main converter ────────────────────────────────────────────────────────────
def convert(project_path, output_path, config_path, list_only, force, verbose,
extra_csv_dir=None):
print(f"\nQGIS -> S-57 Converter")
print(f"{'='*50}")
print(f"Project : {project_path}")
cfg = load_config(config_path)
attr_map = {k.lower(): v.upper() for k, v in cfg.get("attribute_mappings", {}).items()}
layer_map = {k.lower(): v.upper() for k, v in cfg.get("layer_mappings", {}).items()}
print(f"\nParsing QGIS project...")
try:
project = QGISProject(project_path)
except Exception as e:
print(f"[ERROR] Could not parse project: {e}")
sys.exit(1)
# ── Inyectar CSVs de directorio extra ──────────────────────────────────
extra_layers = []
if extra_csv_dir:
csv_dir = Path(extra_csv_dir)
if csv_dir.is_dir():
for csv_file in sorted(csv_dir.glob("*.csv")):
# Evitar duplicados: si la capa ya está en el proyecto, ignorar
already = any(
Path(l["path"]).resolve() == csv_file.resolve()
for l in project.layers if l.get("layer_type") == "csv"
)
if not already:
extra_layers.append({
"id": "extra_" + csv_file.stem,
"name": csv_file.stem,
"path": csv_file,
"crs": "EPSG:4326",
"visible": True,
"layer_type": "csv",
"x_field": "lon",
"y_field": "lat",
})
else:
print(f"[WARN] extra_csv_dir not found: {csv_dir}")
if not project.layers and not extra_layers:
print("[ERROR] No layers found in project.")
sys.exit(1)
all_layers = project.layers + extra_layers
shp_count = sum(1 for l in all_layers if l.get("layer_type","shp") == "shp")
csv_count = sum(1 for l in all_layers if l.get("layer_type") == "csv")
print(f"Found {shp_count} SHP + {csv_count} CSV layer(s):\n")
layer_assignments = []
for layer in all_layers:
s57_class = resolve_s57_class(layer["name"], layer_map)
status = "+" if layer["visible"] else "o"
assigned = s57_class or "?? (unmapped)"
src_tag = " [extra]" if layer["id"].startswith("extra_") else ""
print(f" {status} {layer['name']:<30} -> {assigned}{src_tag}")
layer_assignments.append((layer, s57_class))
if list_only:
print("\n[INFO] --list mode. No conversion performed.")
return
unmapped = [(l, c) for l, c in layer_assignments if c is None]
if unmapped and not force:
print(f"\n[WARN] {len(unmapped)} unmapped layer(s).")
ans = input("Continue anyway? [y/N]: ")
if ans.lower() != "y":
print("Aborted.")
return
if output_path is None:
cell_name = cfg.get("cell_name", "CHART01").upper()
output_path = Path(project_path).parent / (cell_name + ".000")
else:
output_path = Path(output_path)
print(f"\nOutput : {output_path}")
print(f"Cell : {cfg.get('cell_name','?')} Scale 1:{cfg.get('scale','?')}")
print()
output_path.parent.mkdir(parents=True, exist_ok=True)
writer = S57CellWriter(str(output_path), cfg)
writer.open()
for layer, s57_class in layer_assignments:
if s57_class is None:
print(f" SKIP {layer['name']} (no S-57 mapping)")
continue
ltype = layer.get("layer_type", "shp")
crs_str = layer.get("crs", "EPSG:4326")
print(f" Converting: {layer['name']} -> {s57_class}")
if ltype == "csv":
if verbose:
print(f" CSV: {layer['path']}")
count = writer.add_features_from_csv(
layer["path"], s57_class, attr_map,
x_field=layer.get("x_field", "lon"),
y_field=layer.get("y_field", "lat"),
)
else:
shp_path = layer["path"]
if verbose:
print(f" CRS: {crs_str} | SHP: {shp_path}")
if not shp_path.exists():
print(f" [ERR] File not found: {shp_path}")
continue
count = writer.add_features_from_shp(shp_path, crs_str, s57_class, attr_map)
print(f" {count} feature(s) written")
writer.close()
writer.summary()
if output_path.exists():
print(f"\nOutput file: {output_path} ({output_path.stat().st_size // 1024} KB)")
print("Done.")
# ── interactive mode ──────────────────────────────────────────────────────────
def interactive_mode():
print("\n=== QGIS -> S-57 Converter (Interactive) ===\n")
qgs = input("QGIS project (.qgs or .qgz): ").strip().strip('"')
if not Path(qgs).exists():
print(f"File not found: {qgs}"); sys.exit(1)
out = input("Output .000 [blank=auto]: ").strip().strip('"') or None
cfg = input("Config file [blank=default]: ").strip().strip('"') or None
convert(qgs, out, cfg, list_only=False, force=False, verbose=True)
# ── CLI ───────────────────────────────────────────────────────────────────────
def main():
if len(sys.argv) == 1:
interactive_mode()
return
parser = argparse.ArgumentParser(
description="Convert QGIS project SHP layers to S-57 ENC format")
parser.add_argument("project", help=".qgs or .qgz QGIS project file")
parser.add_argument("--output", "-o", help="Output .000 file")
parser.add_argument("--config", "-c", help="JSON config file")
parser.add_argument("--list", "-l", action="store_true",
help="List layers then exit")
parser.add_argument("--force", "-f", action="store_true",
help="Skip prompts for unmapped layers")
parser.add_argument("--verbose", "-v", action="store_true")
args = parser.parse_args()
if not Path(args.project).exists():
print(f"[ERROR] Not found: {args.project}"); sys.exit(1)
convert(args.project, args.output, args.config,
args.list, args.force, args.verbose)
if __name__ == "__main__":
main()
Binary file not shown.
+65
View File
@@ -0,0 +1,65 @@
no_dimar,OBJNAM,lon,lat,feat_type,LITCHR,LITCHR_TXT,SIGGRP,SIGPER,COLOUR,COLOUR_TXT,COLPAT,VALNMR,HEIGHT,ORIENT,CATLAM,CATCAM,BOYSHP,BCNSHP,TOPSHP,INFORM,_dimar_char_raw,_source
13,Faro F1 Recalada,-74.849500,11.106167,LIGHTS,7,Iso,,2,4,green,,9,20,,,,,,,Torre naranja bandas blancas. Faro de Recalada,Iso G 2s,DIMAR Lista de Luces 2015
14,Faro F2 Recalada,-74.854667,11.106000,LIGHTS,7,Iso,,2,3,red,,13.4,23,,,,,,,Torre naranja bandas blancas. Racon B,Iso R 2s,DIMAR Lista de Luces 2015
32,Faro Morro Hermoso,-75.017500,10.963333,LIGHTS,2,Fl,,4,1,white,,28,134,,,,,,,Torre blanca bandas rojas. Giratorio,Fl W 4s,DIMAR Lista de Luces 2015
33,Faro Galerazamba,-75.266000,10.785333,LIGHTS,2,Fl,,4,1,white,,11,14,,,,,,,Torre fibra vidrio blanca bandas rojas. Giratorio,Fl W 4s,DIMAR Lista de Luces 2015
15,Faro X1,-74.849500,11.102167,BCNLAT,4,Q(4)G,4,11,4,green,,6,6,,1,,,3,,Torre verde bandas blancas,Q(4)G 11s,DIMAR Lista de Luces 2015
16,Faro X2,-74.853333,11.100000,BCNLAT,4,Q(4)R,4,11,3,red,,6,6,,2,,,3,,Torre roja bandas blancas,Q(4)R 11s,DIMAR Lista de Luces 2015
18,Faro X3,-74.847167,11.091333,BCNLAT,4,Q(4)G,4,11,4,green,,6,6,,1,,,3,,Torre verde bandas blancas,Q(4)G 11s,DIMAR Lista de Luces 2015
17,Faro X4,-74.851667,11.093000,BCNLAT,4,Q(4)R,4,11,3,red,,6,6,,2,,,3,,Torre roja bandas blancas,Q(4)R 11s,DIMAR Lista de Luces 2015
19,Faro X5,-74.846667,11.089167,BCNLAT,4,Q(4)G,4,11,4,green,,6,6,,1,,,3,,Torre verde bandas blancas,Q(4)G 11s,DIMAR Lista de Luces 2015
20,Faro X6,-74.850500,11.087667,BCNLAT,4,Q(4)R,4,11,3,red,,6,6,,2,,,3,,Torre roja bandas blancas,Q(4)R 11s,DIMAR Lista de Luces 2015
21,Faro X7,-74.814333,11.041500,BCNLAT,4,Q(4)G,4,11,4,green,,6,8,,1,,,4,,Baliza enrejado verde bandas blancas,Q(4)G 11s,DIMAR Lista de Luces 2015
22,Faro X8,-74.849167,11.081667,BCNLAT,4,Q(4)R,4,11,3,red,,6,6,,2,,,3,,Torre roja bandas blancas,Q(4)R 11s,DIMAR Lista de Luces 2015
23,Faro X9,-74.804833,11.035833,BCNLAT,4,Q(4)G,4,11,4,green,,6,8,,1,,,4,,Baliza enrejado verde bandas blancas,Q(4)G 11s,DIMAR Lista de Luces 2015
24,Faro X10,-74.848000,11.076000,BCNLAT,4,Q(4)R,4,11,3,red,,6,6,,2,,,3,,Torre roja bandas blancas,Q(4)R 11s,DIMAR Lista de Luces 2015
25,Faro X11,-74.795500,11.029833,BCNLAT,4,Q(4)G,4,11,4,green,,6,8,,1,,,4,,Baliza enrejado verde bandas blancas,Q(4)G 11s,DIMAR Lista de Luces 2015
26,Faro X12,-74.844167,11.065000,BCNLAT,4,Q(4)R,4,11,3,red,,6,6,,2,,,4,,Baliza enrejado roja bandas blancas,Q(4)R 11s,DIMAR Lista de Luces 2015
27,Faro X13,-74.789667,11.025833,BCNLAT,4,Q(4)G,4,11,4,green,,6,8,,1,,,4,,Baliza enrejado verde bandas blancas,Q(4)G 11s,DIMAR Lista de Luces 2015
28,Faro X14,-74.839500,11.057833,BCNLAT,4,Q(4)R,4,11,3,red,,6,6,,2,,,3,,Torre roja bandas blancas,Q(4)R 11s,DIMAR Lista de Luces 2015
30,Faro X15,-74.785500,11.022833,BCNLAT,4,Q(4)G,4,11,4,green,,6,6,,1,,,4,,Baliza enrejado verde bandas blancas,Q(4)G 11s,DIMAR Lista de Luces 2015
29,Faro X16,-74.833000,11.050000,BCNLAT,4,Q(4)R,4,11,3,red,,6,6,,2,,,3,,Torre roja bandas blancas,Q(4)R 11s,DIMAR Lista de Luces 2015
31,Faro X17,-74.778333,11.018667,BCNLAT,4,Q(4)G,4,11,4,green,,6,6,,1,,,4,,Baliza enrejado verde bandas blancas,Q(4)G 11s,DIMAR Lista de Luces 2015
196,Enfilacion E1,-74.848333,11.103667,LIGHTS,7,Iso,,5,1,white,,13,10,135.7,,,,,,Baliza enrejado naranja y blanco. Rumbo 135.7,Iso Bu 5s,DIMAR Lista de Luces 2015
197,Enfilacion E3,-74.846333,11.101667,LIGHTS,7,Iso,,5,1,white,,9,22,139.3,,,,,,Torre enrejada naranja y blanco. Rumbo 139.3,Iso Bu 5s,DIMAR Lista de Luces 2015
198,Enfilacion E3A,-74.845000,11.100333,LIGHTS,7,Iso,,5,1,white,,12.3,20,135.7,,,,,,Torre naranja y blanco. Rumbo 135.7,Iso W 5s,DIMAR Lista de Luces 2015
201,Enfilacion E4,-74.846833,11.070167,LIGHTS,7,Iso,,4,3,red,,4.5,11,142.3,,,,,,Baliza enrejado naranja bandas blancas. Rumbo 142.3,Iso R 4s,DIMAR Lista de Luces 2015
203,Enfilacion E6,-74.843667,11.063000,LIGHTS,7,Iso,,4,3,red,,8,12,167.7,,,,,,Baliza enrejado roja bandas blancas. Rumbo 167.7,Iso Bu 4s,DIMAR Lista de Luces 2015
204,Enfilacion E8,-74.841833,11.058500,LIGHTS,7,Iso,,4,1,white,,14.5,25,167.7,,,,,,Baliza enrejado naranja bandas blancas. Rumbo 167.7,Iso Bu 4s,DIMAR Lista de Luces 2015
205,Enfilacion E10,-74.841667,11.059833,LIGHTS,7,Iso,,5,4,green,,10,11,167.3,,,,,,Torre naranja bandas blancas. Rumbo 167.3,Iso G 5s,DIMAR Lista de Luces 2015
206,Enfilacion E12,-74.840667,11.056167,LIGHTS,7,Iso,,5,4,green,,8,22,167.3,,,,,,Baliza tablero blanco franja roja. Rumbo 167.3,Iso G 5s,DIMAR Lista de Luces 2015
207,Enfilacion E14,-74.840667,11.056167,LIGHTS,7,Iso,,6,1,white,,8,22,122,,,,,,Tablero blanco con franja roja. Rumbo 122,Iso Bu 6s,DIMAR Lista de Luces 2015
237,Enfilacion E16,-74.836667,11.053667,LIGHTS,7,Iso,,6,1,white,,9,12,122,,,,,,Baliza enrejado naranja bandas blancas. Rumbo 122,Iso Bu 6s,DIMAR Lista de Luces 2015
238,Enfilacion E18,-74.825500,11.043000,LIGHTS,2,Fl,,4,"1,3,4",white/red/green,,6,18,142,,,,,,Torre roja bandas blancas. Sector 9 grados. Rumbo 142,Fl WRG 4s,DIMAR Lista de Luces 2015
199,Boya No. 1,-74.833500,11.084500,BOYLAT,2,Fl,,3,4,green,,6,4,,1,,4,,,Castillete verde,Fl G 3s,DIMAR Lista de Luces 2015
200,Boya No. 3,-74.844833,11.075833,BOYLAT,2,Fl,,3,4,green,,6,4,,1,,4,,,Castillete verde,Fl G 3s,DIMAR Lista de Luces 2015
202,Boya No. 5,-74.841000,11.065667,BOYLAT,4,Q,,1,4,green,,6,4,,1,,4,,,Castillete verde,Q G 1s,DIMAR Lista de Luces 2015
208,Boya No. 7,-74.837500,11.060000,BOYLAT,2,Fl,,3,4,green,,6,4,,1,,4,,,Castillete verde,Fl G 3s,DIMAR Lista de Luces 2015
209,Boya No. 9,-74.824000,11.046833,BOYLAT,2,Fl,,3,4,green,,6,4,,1,,4,,,Castillete verde,Fl G 3s,DIMAR Lista de Luces 2015
210,Boya No. 11,-74.812167,11.039667,BOYLAT,2,Fl,,3,4,green,,6,4,,1,,4,,,Castillete verde,Fl G 3s,DIMAR Lista de Luces 2015
211,Boya No. 12,-74.813500,11.037167,BOYLAT,2,Fl,,3,3,red,,6,4,,2,,4,,,Castillete roja,Fl R 3s,DIMAR Lista de Luces 2015
212,Boya No. 13,-74.802000,11.034333,BOYLAT,2,Fl,,3,4,green,,6,4,,1,,4,,,Castillete verde,Fl G 3s,DIMAR Lista de Luces 2015
213,Boya No. 14,-74.788333,11.021833,BOYLAT,2,Fl,,3,3,red,,6,3,,2,,4,,,Castillete roja,Fl R 3s,DIMAR Lista de Luces 2015
214,Boya No. 15,-74.793833,11.028167,BOYLAT,2,Fl,,3,4,green,,6,4,,1,,4,,,Castillete verde,Fl G 3s,DIMAR Lista de Luces 2015
215,Boya No. 16,-74.797500,11.027333,BOYLAT,2,Fl,,3,3,red,,6,4,,2,,4,,,Castillete roja,Fl R 3s,DIMAR Lista de Luces 2015
218,Boya No. 18,-74.788333,11.021833,BOYLAT,2,Fl,,3,3,red,,6,4,,2,,4,,,Castillete roja,Fl R 3s,DIMAR Lista de Luces 2015
219,Boya No. 19,-74.776500,11.017500,BOYLAT,2,Fl,,3,4,green,,6,4,,1,,4,,,Castillete verde,Fl G 3s,DIMAR Lista de Luces 2015
220,Boya No. 20,-74.777333,11.015500,BOYLAT,2,Fl,,3,3,red,,6,4,,2,,4,,,Castillete roja,Fl R 3s,DIMAR Lista de Luces 2015
221,Boya No. 21,-74.772000,11.014000,BOYLAT,2,Fl,,3,4,green,,6,4,,1,,4,,,Castillete verde,Fl G 3s,DIMAR Lista de Luces 2015
222,Boya No. 22,-74.773333,11.012333,BOYLAT,4,Q,,1,3,red,,6,4,,2,,4,,,Castillete roja,Q R 1s,DIMAR Lista de Luces 2015
223,Boya No. 23,-74.755000,10.975000,BOYLAT,2,Fl,,1.3,4,green,,6,3,,1,,4,,,Castillete verde,Fl G 1.3s,DIMAR Lista de Luces 2015
224,Boya No. 24,-74.770500,11.009167,BOYLAT,2,Fl,,3,3,red,,6,4,,2,,4,,,Castillete roja,Fl R 3s,DIMAR Lista de Luces 2015
225,Boya No. 25,-74.766333,11.006667,BOYLAT,2,Fl,,3,4,green,,6,4,,1,,4,,,Castillete verde,Fl G 3s,DIMAR Lista de Luces 2015
226,Boya No. 26,-74.768333,11.005833,BOYLAT,2,Fl,,3,3,red,,6,4,,2,,4,,,Castillete roja,Fl R 3s,DIMAR Lista de Luces 2015
227,Boya No. 27,-74.762000,10.998500,BOYLAT,2,Fl,,3,4,green,,6,4,,1,,4,,,Castillete verde,Fl G 3s,DIMAR Lista de Luces 2015
228,Boya No. 28,-74.765333,10.999833,BOYLAT,2,Fl,,3,3,red,,6,4,,2,,4,,,Castillete roja,Fl R 3s,DIMAR Lista de Luces 2015
229,Boya No. 29,-74.758000,10.987333,BOYLAT,2,Fl,,3,4,green,,6,4,,1,,4,,,Castillete verde,Fl G 3s,DIMAR Lista de Luces 2015
230,Boya No. 30,-74.760667,10.987333,BOYLAT,2,Fl,,3,3,red,,6,4,,2,,4,,,Castillete roja,Fl R 3s,DIMAR Lista de Luces 2015
231,Boya No. 31,-74.754833,10.975000,BOYLAT,2,Fl,,3,4,green,,6,4,,1,,4,,,Castillete verde,Fl G 3s,DIMAR Lista de Luces 2015
232,Boya No. 33,-74.755667,10.959333,BOYLAT,2,Fl,,3,4,green,,6,4,,1,,4,,,Castillete verde,Fl G 3s,DIMAR Lista de Luces 2015
233,Boya No. 35,-74.754167,10.942667,BOYLAT,2,Fl,,3,4,green,,6,4,,1,,4,,,Castillete verde,Fl G 3s,DIMAR Lista de Luces 2015
234,Boya No. 36,-74.756667,10.941500,BOYLAT,2,Fl,,3,3,red,,6,4,,2,,4,,,Castillete roja,Fl R 3s,DIMAR Lista de Luces 2015
235,Boya Cardinal Norte,-74.753500,10.959167,BOYCAR,2,Fl,,1,1,white,1,6,4,,,1,4,,,Castillete cardinal N negros,Fl W 1s,DIMAR Lista de Luces 2015
236,Boya Cardinal Sur,-74.753500,10.959167,BOYCAR,2,Fl,,15,1,white,1,6,4,,,3,4,,,Castillete cardinal S negros,Fl W 15s,DIMAR Lista de Luces 2015
239,Boya de Oleaje,-74.758000,11.134000,BOYSPP,2,Fl,,20,6,yellow,,4.5,0.5,,,,3,,,Esferica amarilla. Recolectora datos oceanograficos,Fl Y 20s,DIMAR Lista de Luces 2015
240,Boya Peligro Aislado,-74.757333,10.954500,BOYISD,2,Fl(2),2,4,1,white,1,3,3.3,,,,4,,,Castillete roja bandas negras. Bajo rocoso,Fl(2) W 4s,DIMAR Lista de Luces 2015
1 no_dimar OBJNAM lon lat feat_type LITCHR LITCHR_TXT SIGGRP SIGPER COLOUR COLOUR_TXT COLPAT VALNMR HEIGHT ORIENT CATLAM CATCAM BOYSHP BCNSHP TOPSHP INFORM _dimar_char_raw _source
2 13 Faro F1 Recalada -74.849500 11.106167 LIGHTS 7 Iso 2 4 green 9 20 Torre naranja bandas blancas. Faro de Recalada Iso G 2s DIMAR Lista de Luces 2015
3 14 Faro F2 Recalada -74.854667 11.106000 LIGHTS 7 Iso 2 3 red 13.4 23 Torre naranja bandas blancas. Racon B Iso R 2s DIMAR Lista de Luces 2015
4 32 Faro Morro Hermoso -75.017500 10.963333 LIGHTS 2 Fl 4 1 white 28 134 Torre blanca bandas rojas. Giratorio Fl W 4s DIMAR Lista de Luces 2015
5 33 Faro Galerazamba -75.266000 10.785333 LIGHTS 2 Fl 4 1 white 11 14 Torre fibra vidrio blanca bandas rojas. Giratorio Fl W 4s DIMAR Lista de Luces 2015
6 15 Faro X1 -74.849500 11.102167 BCNLAT 4 Q(4)G 4 11 4 green 6 6 1 3 Torre verde bandas blancas Q(4)G 11s DIMAR Lista de Luces 2015
7 16 Faro X2 -74.853333 11.100000 BCNLAT 4 Q(4)R 4 11 3 red 6 6 2 3 Torre roja bandas blancas Q(4)R 11s DIMAR Lista de Luces 2015
8 18 Faro X3 -74.847167 11.091333 BCNLAT 4 Q(4)G 4 11 4 green 6 6 1 3 Torre verde bandas blancas Q(4)G 11s DIMAR Lista de Luces 2015
9 17 Faro X4 -74.851667 11.093000 BCNLAT 4 Q(4)R 4 11 3 red 6 6 2 3 Torre roja bandas blancas Q(4)R 11s DIMAR Lista de Luces 2015
10 19 Faro X5 -74.846667 11.089167 BCNLAT 4 Q(4)G 4 11 4 green 6 6 1 3 Torre verde bandas blancas Q(4)G 11s DIMAR Lista de Luces 2015
11 20 Faro X6 -74.850500 11.087667 BCNLAT 4 Q(4)R 4 11 3 red 6 6 2 3 Torre roja bandas blancas Q(4)R 11s DIMAR Lista de Luces 2015
12 21 Faro X7 -74.814333 11.041500 BCNLAT 4 Q(4)G 4 11 4 green 6 8 1 4 Baliza enrejado verde bandas blancas Q(4)G 11s DIMAR Lista de Luces 2015
13 22 Faro X8 -74.849167 11.081667 BCNLAT 4 Q(4)R 4 11 3 red 6 6 2 3 Torre roja bandas blancas Q(4)R 11s DIMAR Lista de Luces 2015
14 23 Faro X9 -74.804833 11.035833 BCNLAT 4 Q(4)G 4 11 4 green 6 8 1 4 Baliza enrejado verde bandas blancas Q(4)G 11s DIMAR Lista de Luces 2015
15 24 Faro X10 -74.848000 11.076000 BCNLAT 4 Q(4)R 4 11 3 red 6 6 2 3 Torre roja bandas blancas Q(4)R 11s DIMAR Lista de Luces 2015
16 25 Faro X11 -74.795500 11.029833 BCNLAT 4 Q(4)G 4 11 4 green 6 8 1 4 Baliza enrejado verde bandas blancas Q(4)G 11s DIMAR Lista de Luces 2015
17 26 Faro X12 -74.844167 11.065000 BCNLAT 4 Q(4)R 4 11 3 red 6 6 2 4 Baliza enrejado roja bandas blancas Q(4)R 11s DIMAR Lista de Luces 2015
18 27 Faro X13 -74.789667 11.025833 BCNLAT 4 Q(4)G 4 11 4 green 6 8 1 4 Baliza enrejado verde bandas blancas Q(4)G 11s DIMAR Lista de Luces 2015
19 28 Faro X14 -74.839500 11.057833 BCNLAT 4 Q(4)R 4 11 3 red 6 6 2 3 Torre roja bandas blancas Q(4)R 11s DIMAR Lista de Luces 2015
20 30 Faro X15 -74.785500 11.022833 BCNLAT 4 Q(4)G 4 11 4 green 6 6 1 4 Baliza enrejado verde bandas blancas Q(4)G 11s DIMAR Lista de Luces 2015
21 29 Faro X16 -74.833000 11.050000 BCNLAT 4 Q(4)R 4 11 3 red 6 6 2 3 Torre roja bandas blancas Q(4)R 11s DIMAR Lista de Luces 2015
22 31 Faro X17 -74.778333 11.018667 BCNLAT 4 Q(4)G 4 11 4 green 6 6 1 4 Baliza enrejado verde bandas blancas Q(4)G 11s DIMAR Lista de Luces 2015
23 196 Enfilacion E1 -74.848333 11.103667 LIGHTS 7 Iso 5 1 white 13 10 135.7 Baliza enrejado naranja y blanco. Rumbo 135.7 Iso Bu 5s DIMAR Lista de Luces 2015
24 197 Enfilacion E3 -74.846333 11.101667 LIGHTS 7 Iso 5 1 white 9 22 139.3 Torre enrejada naranja y blanco. Rumbo 139.3 Iso Bu 5s DIMAR Lista de Luces 2015
25 198 Enfilacion E3A -74.845000 11.100333 LIGHTS 7 Iso 5 1 white 12.3 20 135.7 Torre naranja y blanco. Rumbo 135.7 Iso W 5s DIMAR Lista de Luces 2015
26 201 Enfilacion E4 -74.846833 11.070167 LIGHTS 7 Iso 4 3 red 4.5 11 142.3 Baliza enrejado naranja bandas blancas. Rumbo 142.3 Iso R 4s DIMAR Lista de Luces 2015
27 203 Enfilacion E6 -74.843667 11.063000 LIGHTS 7 Iso 4 3 red 8 12 167.7 Baliza enrejado roja bandas blancas. Rumbo 167.7 Iso Bu 4s DIMAR Lista de Luces 2015
28 204 Enfilacion E8 -74.841833 11.058500 LIGHTS 7 Iso 4 1 white 14.5 25 167.7 Baliza enrejado naranja bandas blancas. Rumbo 167.7 Iso Bu 4s DIMAR Lista de Luces 2015
29 205 Enfilacion E10 -74.841667 11.059833 LIGHTS 7 Iso 5 4 green 10 11 167.3 Torre naranja bandas blancas. Rumbo 167.3 Iso G 5s DIMAR Lista de Luces 2015
30 206 Enfilacion E12 -74.840667 11.056167 LIGHTS 7 Iso 5 4 green 8 22 167.3 Baliza tablero blanco franja roja. Rumbo 167.3 Iso G 5s DIMAR Lista de Luces 2015
31 207 Enfilacion E14 -74.840667 11.056167 LIGHTS 7 Iso 6 1 white 8 22 122 Tablero blanco con franja roja. Rumbo 122 Iso Bu 6s DIMAR Lista de Luces 2015
32 237 Enfilacion E16 -74.836667 11.053667 LIGHTS 7 Iso 6 1 white 9 12 122 Baliza enrejado naranja bandas blancas. Rumbo 122 Iso Bu 6s DIMAR Lista de Luces 2015
33 238 Enfilacion E18 -74.825500 11.043000 LIGHTS 2 Fl 4 1,3,4 white/red/green 6 18 142 Torre roja bandas blancas. Sector 9 grados. Rumbo 142 Fl WRG 4s DIMAR Lista de Luces 2015
34 199 Boya No. 1 -74.833500 11.084500 BOYLAT 2 Fl 3 4 green 6 4 1 4 Castillete verde Fl G 3s DIMAR Lista de Luces 2015
35 200 Boya No. 3 -74.844833 11.075833 BOYLAT 2 Fl 3 4 green 6 4 1 4 Castillete verde Fl G 3s DIMAR Lista de Luces 2015
36 202 Boya No. 5 -74.841000 11.065667 BOYLAT 4 Q 1 4 green 6 4 1 4 Castillete verde Q G 1s DIMAR Lista de Luces 2015
37 208 Boya No. 7 -74.837500 11.060000 BOYLAT 2 Fl 3 4 green 6 4 1 4 Castillete verde Fl G 3s DIMAR Lista de Luces 2015
38 209 Boya No. 9 -74.824000 11.046833 BOYLAT 2 Fl 3 4 green 6 4 1 4 Castillete verde Fl G 3s DIMAR Lista de Luces 2015
39 210 Boya No. 11 -74.812167 11.039667 BOYLAT 2 Fl 3 4 green 6 4 1 4 Castillete verde Fl G 3s DIMAR Lista de Luces 2015
40 211 Boya No. 12 -74.813500 11.037167 BOYLAT 2 Fl 3 3 red 6 4 2 4 Castillete roja Fl R 3s DIMAR Lista de Luces 2015
41 212 Boya No. 13 -74.802000 11.034333 BOYLAT 2 Fl 3 4 green 6 4 1 4 Castillete verde Fl G 3s DIMAR Lista de Luces 2015
42 213 Boya No. 14 -74.788333 11.021833 BOYLAT 2 Fl 3 3 red 6 3 2 4 Castillete roja Fl R 3s DIMAR Lista de Luces 2015
43 214 Boya No. 15 -74.793833 11.028167 BOYLAT 2 Fl 3 4 green 6 4 1 4 Castillete verde Fl G 3s DIMAR Lista de Luces 2015
44 215 Boya No. 16 -74.797500 11.027333 BOYLAT 2 Fl 3 3 red 6 4 2 4 Castillete roja Fl R 3s DIMAR Lista de Luces 2015
45 218 Boya No. 18 -74.788333 11.021833 BOYLAT 2 Fl 3 3 red 6 4 2 4 Castillete roja Fl R 3s DIMAR Lista de Luces 2015
46 219 Boya No. 19 -74.776500 11.017500 BOYLAT 2 Fl 3 4 green 6 4 1 4 Castillete verde Fl G 3s DIMAR Lista de Luces 2015
47 220 Boya No. 20 -74.777333 11.015500 BOYLAT 2 Fl 3 3 red 6 4 2 4 Castillete roja Fl R 3s DIMAR Lista de Luces 2015
48 221 Boya No. 21 -74.772000 11.014000 BOYLAT 2 Fl 3 4 green 6 4 1 4 Castillete verde Fl G 3s DIMAR Lista de Luces 2015
49 222 Boya No. 22 -74.773333 11.012333 BOYLAT 4 Q 1 3 red 6 4 2 4 Castillete roja Q R 1s DIMAR Lista de Luces 2015
50 223 Boya No. 23 -74.755000 10.975000 BOYLAT 2 Fl 1.3 4 green 6 3 1 4 Castillete verde Fl G 1.3s DIMAR Lista de Luces 2015
51 224 Boya No. 24 -74.770500 11.009167 BOYLAT 2 Fl 3 3 red 6 4 2 4 Castillete roja Fl R 3s DIMAR Lista de Luces 2015
52 225 Boya No. 25 -74.766333 11.006667 BOYLAT 2 Fl 3 4 green 6 4 1 4 Castillete verde Fl G 3s DIMAR Lista de Luces 2015
53 226 Boya No. 26 -74.768333 11.005833 BOYLAT 2 Fl 3 3 red 6 4 2 4 Castillete roja Fl R 3s DIMAR Lista de Luces 2015
54 227 Boya No. 27 -74.762000 10.998500 BOYLAT 2 Fl 3 4 green 6 4 1 4 Castillete verde Fl G 3s DIMAR Lista de Luces 2015
55 228 Boya No. 28 -74.765333 10.999833 BOYLAT 2 Fl 3 3 red 6 4 2 4 Castillete roja Fl R 3s DIMAR Lista de Luces 2015
56 229 Boya No. 29 -74.758000 10.987333 BOYLAT 2 Fl 3 4 green 6 4 1 4 Castillete verde Fl G 3s DIMAR Lista de Luces 2015
57 230 Boya No. 30 -74.760667 10.987333 BOYLAT 2 Fl 3 3 red 6 4 2 4 Castillete roja Fl R 3s DIMAR Lista de Luces 2015
58 231 Boya No. 31 -74.754833 10.975000 BOYLAT 2 Fl 3 4 green 6 4 1 4 Castillete verde Fl G 3s DIMAR Lista de Luces 2015
59 232 Boya No. 33 -74.755667 10.959333 BOYLAT 2 Fl 3 4 green 6 4 1 4 Castillete verde Fl G 3s DIMAR Lista de Luces 2015
60 233 Boya No. 35 -74.754167 10.942667 BOYLAT 2 Fl 3 4 green 6 4 1 4 Castillete verde Fl G 3s DIMAR Lista de Luces 2015
61 234 Boya No. 36 -74.756667 10.941500 BOYLAT 2 Fl 3 3 red 6 4 2 4 Castillete roja Fl R 3s DIMAR Lista de Luces 2015
62 235 Boya Cardinal Norte -74.753500 10.959167 BOYCAR 2 Fl 1 1 white 1 6 4 1 4 Castillete cardinal N negros Fl W 1s DIMAR Lista de Luces 2015
63 236 Boya Cardinal Sur -74.753500 10.959167 BOYCAR 2 Fl 15 1 white 1 6 4 3 4 Castillete cardinal S negros Fl W 15s DIMAR Lista de Luces 2015
64 239 Boya de Oleaje -74.758000 11.134000 BOYSPP 2 Fl 20 6 yellow 4.5 0.5 3 Esferica amarilla. Recolectora datos oceanograficos Fl Y 20s DIMAR Lista de Luces 2015
65 240 Boya Peligro Aislado -74.757333 10.954500 BOYISD 2 Fl(2) 2 4 1 white 1 3 3.3 4 Castillete roja bandas negras. Bajo rocoso Fl(2) W 4s DIMAR Lista de Luces 2015
Binary file not shown.
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
+324
View File
@@ -0,0 +1,324 @@
"""
QGISS57Converter — GUI
Corre el converter en el mismo proceso (sin subprocess ni conda).
Completamente portable: solo necesita este .exe.
"""
import io
import os
import queue
import sys as _sys
import threading
import tkinter as tk
from tkinter import filedialog, messagebox, scrolledtext
from pathlib import Path
# ── paths ─────────────────────────────────────────────────────────────────────
if getattr(_sys, "frozen", False):
SCRIPT_DIR = Path(_sys._MEIPASS)
else:
SCRIPT_DIR = Path(__file__).parent
_sys.path.insert(0, str(SCRIPT_DIR))
# Tell pyproj where its PROJ data is (bundled inside the exe)
if getattr(_sys, "frozen", False):
os.environ["PROJ_DATA"] = str(SCRIPT_DIR / "proj_data")
os.environ["PROJ_LIB"] = str(SCRIPT_DIR / "proj_data")
CONFIG = SCRIPT_DIR / "cell_config.json"
REF_PDF = SCRIPT_DIR / "CAPAS_REFERENCIA.pdf"
MANUAL_HTML = SCRIPT_DIR / "MANUAL.html"
# Import converter (bundled alongside gui in the exe)
from converter import convert as _do_convert
# ── theme ─────────────────────────────────────────────────────────────────────
BG = "#1e2532"
PANEL = "#252d3d"
ACCENT = "#1e7fc8"
ACCENT_HOV = "#1565a0"
TEXT_LIGHT = "#e8eaf0"
TEXT_DIM = "#8a94aa"
GREEN = "#2ecc71"
RED = "#e74c3c"
FONT_UI = ("Segoe UI", 10)
FONT_MONO = ("Consolas", 9)
# ── stdout capture ────────────────────────────────────────────────────────────
class _QueueWriter:
"""Redirects print() output to a queue for the GUI to consume."""
def __init__(self, q: queue.Queue):
self._q = q
def write(self, text):
if text:
self._q.put(text)
def flush(self):
pass
def isatty(self):
return False
# ── app ───────────────────────────────────────────────────────────────────────
class App(tk.Tk):
def __init__(self):
super().__init__()
self.title("QGISS57Converter")
self.geometry("720x540")
self.resizable(True, True)
self.minsize(600, 440)
self.configure(bg=BG)
self._converting = False
self._log_queue = queue.Queue()
self._cancel_flag = threading.Event()
self._build_ui()
# ── UI ────────────────────────────────────────────────────────────────────
def _build_ui(self):
hdr = tk.Frame(self, bg=ACCENT, height=48)
hdr.pack(fill="x")
tk.Label(hdr, text="QGIS -> S-57 ENC Converter",
bg=ACCENT, fg="white",
font=("Segoe UI", 13, "bold")).pack(side="left", padx=16, pady=10)
tk.Button(hdr, text="? Manual de usuario",
bg=ACCENT_HOV, fg="white", relief="flat",
font=("Segoe UI", 9), cursor="hand2", bd=0, padx=8,
command=self._open_manual).pack(side="right", padx=4, pady=10)
tk.Button(hdr, text="? Referencia de capas",
bg=ACCENT_HOV, fg="white", relief="flat",
font=("Segoe UI", 9), cursor="hand2", bd=0, padx=8,
command=self._open_ref).pack(side="right", padx=4, pady=10)
body = tk.Frame(self, bg=BG)
body.pack(fill="both", expand=True, padx=20, pady=16)
body.columnconfigure(0, weight=1)
# entrada
tk.Label(body, text="Proyecto QGIS (.qgz / .qgs)",
bg=BG, fg=TEXT_DIM, font=FONT_UI).grid(row=0, column=0, sticky="w")
row_in = tk.Frame(body, bg=BG)
row_in.grid(row=1, column=0, sticky="ew", pady=(2, 10))
self._var_in = tk.StringVar()
self._ent_in = tk.Entry(row_in, textvariable=self._var_in,
bg=PANEL, fg=TEXT_LIGHT, insertbackground=TEXT_LIGHT,
relief="flat", font=FONT_UI, bd=6)
self._ent_in.pack(side="left", fill="x", expand=True)
self._ent_in.bind("<FocusOut>", lambda _: self._auto_output())
tk.Button(row_in, text="Examinar...", bg=ACCENT, fg="white",
relief="flat", font=FONT_UI, cursor="hand2", bd=0, padx=10,
command=self._pick_input).pack(side="left", padx=(6, 0))
# salida
tk.Label(body, text="Archivo de salida (.000)",
bg=BG, fg=TEXT_DIM, font=FONT_UI).grid(row=2, column=0, sticky="w")
row_out = tk.Frame(body, bg=BG)
row_out.grid(row=3, column=0, sticky="ew", pady=(2, 10))
self._var_out = tk.StringVar()
tk.Entry(row_out, textvariable=self._var_out,
bg=PANEL, fg=TEXT_LIGHT, insertbackground=TEXT_LIGHT,
relief="flat", font=FONT_UI, bd=6).pack(side="left", fill="x", expand=True)
tk.Button(row_out, text="Guardar en...", bg=PANEL, fg=TEXT_LIGHT,
relief="flat", font=FONT_UI, cursor="hand2", bd=0, padx=10,
command=self._pick_output).pack(side="left", padx=(6, 0))
# directorio extra de CSVs (opcional)
tk.Label(body, text="Directorio extra de CSVs (opcional — boyas, luces, etc.)",
bg=BG, fg=TEXT_DIM, font=FONT_UI).grid(row=4, column=0, sticky="w")
row_csv = tk.Frame(body, bg=BG)
row_csv.grid(row=5, column=0, sticky="ew", pady=(2, 4))
self._var_csv = tk.StringVar()
self._ent_csv = tk.Entry(row_csv, textvariable=self._var_csv,
bg=PANEL, fg=TEXT_LIGHT, insertbackground=TEXT_LIGHT,
relief="flat", font=FONT_UI, bd=6)
self._ent_csv.pack(side="left", fill="x", expand=True)
tk.Button(row_csv, text="Examinar...", bg=PANEL, fg=TEXT_LIGHT,
relief="flat", font=FONT_UI, cursor="hand2", bd=0, padx=10,
command=self._pick_csv_dir).pack(side="left", padx=(6, 0))
tk.Button(row_csv, text="", bg=PANEL, fg=TEXT_DIM,
relief="flat", font=("Segoe UI", 9), cursor="hand2", bd=0, padx=6,
command=lambda: self._var_csv.set("")).pack(side="left", padx=(2, 0))
tk.Label(body,
text="Los archivos .csv en ese directorio se incluyen como capas extra "
"(nombre del archivo = clase S-57, ej. BOYLAT.csv → BOYLAT).",
bg=BG, fg=TEXT_DIM, font=("Segoe UI", 8),
wraplength=640, justify="left").grid(row=6, column=0, sticky="w",
pady=(0, 8))
# botones
row_btn = tk.Frame(body, bg=BG)
row_btn.grid(row=7, column=0, sticky="ew", pady=(4, 12))
self._btn_convert = tk.Button(
row_btn, text=">> Convertir",
bg=GREEN, fg="white", activebackground="#27ae60",
relief="flat", font=("Segoe UI", 11, "bold"),
cursor="hand2", bd=0, padx=20, pady=8,
command=self._start_conversion)
self._btn_convert.pack(side="left")
self._btn_open = tk.Button(
row_btn, text="Abrir carpeta",
bg=PANEL, fg=TEXT_LIGHT, relief="flat", font=FONT_UI,
cursor="hand2", bd=0, padx=14, pady=8,
state="disabled", command=self._open_output_dir)
self._btn_open.pack(side="left", padx=(8, 0))
# estado
self._lbl_status = tk.Label(body, text="Listo.", bg=BG, fg=TEXT_DIM,
font=("Segoe UI", 9))
self._lbl_status.grid(row=8, column=0, sticky="w")
# log
tk.Label(body, text="Log de conversion",
bg=BG, fg=TEXT_DIM, font=FONT_UI).grid(row=9, column=0,
sticky="w", pady=(10, 2))
self._log = scrolledtext.ScrolledText(
body, bg="#0d1117", fg="#c9d1d9",
insertbackground="white", relief="flat",
font=FONT_MONO, bd=4, height=12, wrap="word")
self._log.grid(row=10, column=0, sticky="nsew")
self._log.configure(state="disabled")
self._log.tag_configure("green", foreground=GREEN)
self._log.tag_configure("red", foreground=RED)
body.rowconfigure(10, weight=1)
# ── acciones ──────────────────────────────────────────────────────────────
def _pick_input(self):
p = filedialog.askopenfilename(
title="Seleccionar proyecto QGIS",
filetypes=[("QGIS project", "*.qgz *.qgs"), ("Todos", "*.*")])
if p:
self._var_in.set(p)
self._auto_output()
def _auto_output(self):
src = Path(self._var_in.get().strip())
if src.exists():
self._var_out.set(str(src.parent / (src.stem.replace(" ", "_") + ".000")))
def _pick_output(self):
p = filedialog.asksaveasfilename(
title="Guardar carta S-57 como",
defaultextension=".000",
filetypes=[("S-57 ENC", "*.000"), ("Todos", "*.*")])
if p:
self._var_out.set(p)
def _open_manual(self):
if MANUAL_HTML.exists():
import webbrowser
webbrowser.open(MANUAL_HTML.as_uri())
else:
messagebox.showinfo("Manual", "No se encontro MANUAL.html.")
def _open_ref(self):
if REF_PDF.exists():
os.startfile(str(REF_PDF))
else:
messagebox.showinfo("Referencia", "No se encontro CAPAS_REFERENCIA.pdf.")
def _pick_csv_dir(self):
p = filedialog.askdirectory(title="Seleccionar directorio con archivos CSV extra")
if p:
self._var_csv.set(p)
def _open_output_dir(self):
out = Path(self._var_out.get().strip())
folder = out.parent if out.parent.exists() else SCRIPT_DIR
os.startfile(str(folder))
def _log_write(self, text, tag=None):
self._log.configure(state="normal")
self._log.insert("end", text, tag or ())
self._log.see("end")
self._log.configure(state="disabled")
def _set_status(self, msg, color=TEXT_DIM):
self._lbl_status.configure(text=msg, fg=color)
# ── conversion ────────────────────────────────────────────────────────────
def _start_conversion(self):
src = self._var_in.get().strip()
out = self._var_out.get().strip()
csv_dir = self._var_csv.get().strip() or None
if not src:
messagebox.showwarning("Falta archivo", "Selecciona un archivo .qgz primero.")
return
if not Path(src).exists():
messagebox.showerror("No encontrado", f"No existe:\n{src}")
return
if csv_dir and not Path(csv_dir).is_dir():
messagebox.showerror("Directorio no existe",
f"El directorio de CSVs no existe:\n{csv_dir}")
return
if not out:
self._auto_output()
out = self._var_out.get().strip()
self._log.configure(state="normal")
self._log.delete("1.0", "end")
self._log.configure(state="disabled")
self._btn_convert.configure(state="disabled")
self._btn_open.configure(state="disabled")
self._set_status("Convirtiendo...", ACCENT)
self._converting = True
# Start polling the log queue
self._poll_log()
threading.Thread(target=self._run_conversion,
args=(src, out, csv_dir), daemon=True).start()
def _poll_log(self):
"""Drain the log queue and update the text widget."""
try:
while True:
text = self._log_queue.get_nowait()
self._log_write(text)
except queue.Empty:
pass
if self._converting:
self.after(40, self._poll_log)
def _run_conversion(self, src: str, out: str, csv_dir=None):
old_stdout = _sys.stdout
old_stderr = _sys.stderr
writer = _QueueWriter(self._log_queue)
_sys.stdout = writer
_sys.stderr = writer
rc = 0
try:
_do_convert(
project_path = src,
output_path = out,
config_path = str(CONFIG),
list_only = False,
force = True,
verbose = True,
extra_csv_dir = csv_dir,
)
except SystemExit as e:
rc = int(e.code) if e.code is not None else 1
except Exception as e:
_sys.stdout.write(f"\n[ERROR] {e}\n")
rc = 1
finally:
_sys.stdout = old_stdout
_sys.stderr = old_stderr
self.after(0, self._on_done, rc, out)
def _on_done(self, rc: int, out: str):
self._converting = False
self._btn_convert.configure(state="normal")
if rc == 0 and Path(out).exists():
size = Path(out).stat().st_size
self._log_write(f"\nListo: {out} ({size:,} bytes)\n", "green")
self._set_status(f"Completado: {Path(out).name}", GREEN)
self._btn_open.configure(state="normal")
else:
self._log_write(f"\nLa conversion fallo (codigo {rc})\n", "red")
self._set_status("Error en la conversion.", RED)
if __name__ == "__main__":
app = App()
app.mainloop()
+205
View File
@@ -0,0 +1,205 @@
"""Genera CAPAS_REFERENCIA.pdf en el directorio del converter."""
from fpdf import FPDF
from pathlib import Path
OUT = Path(__file__).parent / "CAPAS_REFERENCIA.pdf"
# ── datos ──────────────────────────────────────────────────────────────────────
SECCIONES = [
("OBJETOS PUNTUALES", [
("BOYLAT", "Boya lateral (babor/estribor)", "boyas, buoys"),
("BOYCAR", "Boya cardinal (N/S/E/W)", "boycar"),
("BOYISD", "Boya de peligro aislado", "boyisd"),
("BOYSAW", "Boya de aguas seguras", "boysaw"),
("BCNLAT", "Baliza lateral", "balizas, beacons"),
("BCNSPP", "Baliza especial", "bcnspp"),
("LIGHTS", "Luz / faro", "luces, lights, faroles"),
("LNDMRK", "Hito en tierra (torre, tanque...)", "Puntos del Terreno"),
("SOUNDG", "Sonda batimetrica", "sondas, soundings, profundidades"),
("UWTROC", "Roca sumergida / a flor de agua", "rocas, rocks"),
("WRECKS", "Naufragio", "naufragio, wreck"),
("OBSTRN", "Obstruccion", "obstruccion, obstruction"),
]),
("OBJETOS LINEALES", [
("COALNE", "Linea de costa", "Linderos, coastline, costa, linea_de_costa"),
("DEPCNT", "Curva batimetrica (isobata)", "isobata, curvas_nivel, depth_contour"),
("CBLSUB", "Cable submarino", "cable"),
("PIPSOL", "Tuberia submarina / en tierra", "tuberia"),
("RIVERS", "Rio / canal", "rio, river"),
]),
("OBJETOS DE AREA", [
("LNDARE", "Area terrestre", "Area Terreno, tierra, land"),
("DEPARE", "Area de profundidad", "fondos, batimetria, depth_area"),
("FAIRWY", "Canal de navegacion", "canal_navegacion, fairway"),
("RESARE", "Area restringida", "zona_restringida, restricted"),
("ACHARE", "Area de fondeo", "fondeadero, anchorage"),
("HRBARE", "Area portuaria", "puerto, harbor"),
("BERTHS", "Atraque / muelle", "atraque, berth"),
("SBDARE", "Area de fondo marino", "fondo_marino, seabed"),
]),
]
ATTR_ROWS = [
("nombre / name", "OBJNAM", "Nombre del objeto (aparece en tooltip)"),
("catlam / lateral", "CATLAM", "Categoria lateral: 1=babor, 2=estribor"),
("colour / color", "COLOUR", "Color: 1=blanco, 3=rojo, 4=verde, 6=amarillo"),
("colpat / patron", "COLPAT", "Patron de color: 1=horizontal, 2=vertical"),
("boyshp / forma", "BOYSHP", "Forma boya: 1=conica, 2=cilindrica, 4=esferica"),
("litchr / destello", "LITCHR", "Destello: 1=F, 2=Fl, 3=LFl, 4=Q, 8=Iso, 5=IQ"),
("sigper / periodo", "SIGPER", "Periodo en segundos (ej: 4.0)"),
("siggrp / grupo", "SIGGRP", "Grupo de destellos: (2), (2+1), etc."),
("alcance / range", "VALNMR", "Alcance nominal en millas nauticas"),
("altura / height", "HEIGHT", "Altura de la luz sobre el mar (metros)"),
("status / estado", "STATUS", "Estado: 1=permanente, 2=ocasional"),
("profundidad / depth", "DRVAL1", "Profundidad minima (metros)"),
("depth_max", "DRVAL2", "Profundidad maxima (metros)"),
("sonda / sounding", "VALSOU", "Valor de sonda (metros)"),
("contour / valor", "VALDCO", "Valor de curva batimetrica (metros)"),
]
LITCHR_ROWS = [
("1", "F", "Fija"),
("2", "Fl", "Destellante"),
("3", "LFl", "Gran destello"),
("4", "Q", "Centelleante"),
("5", "IQ", "Centelleante interrumpido"),
("6", "Oc", "Ocultante"),
("8", "Iso", "Isofasica"),
("25", "VQ", "Rapida continua"),
("28", "Mo", "Morse"),
]
# ── PDF ────────────────────────────────────────────────────────────────────────
class PDF(FPDF):
def header(self):
self.set_font("Helvetica", "B", 11)
self.set_fill_color(30, 80, 140)
self.set_text_color(255, 255, 255)
self.cell(0, 10, "QGISS57Converter - Referencia de nombres de capas", fill=True, new_x="LMARGIN", new_y="NEXT")
self.set_text_color(0, 0, 0)
self.ln(2)
def footer(self):
self.set_y(-12)
self.set_font("Helvetica", "I", 8)
self.set_text_color(120, 120, 120)
self.cell(0, 10, f"Pagina {self.page_no()}", align="C")
pdf = PDF(orientation="P", unit="mm", format="A4")
pdf.set_auto_page_break(auto=True, margin=15)
pdf.add_page()
pdf.set_font("Helvetica", size=9)
# intro
pdf.set_font("Helvetica", size=9)
pdf.multi_cell(0, 5,
"Nombra tus capas QGIS con cualquiera de los textos de la columna 'Nombres reconocidos' "
"(sin importar mayusculas). Tambien puedes usar el acronimo S-57 directamente como nombre de capa. "
"Para agregar nombres nuevos, edita 'layer_mappings' en cell_config.json.")
pdf.ln(3)
COL = [22, 58, 110] # x positions
W = [22, 55, 78] # widths
def th(texts, fill_rgb=(200, 215, 235)):
pdf.set_fill_color(*fill_rgb)
pdf.set_font("Helvetica", "B", 8)
for i, t in enumerate(texts):
pdf.set_x(10 + sum(W[:i]))
pdf.cell(W[i], 6, t, border=1, fill=True)
pdf.ln()
def tr(texts, alt=False):
fill_rgb = (245, 248, 252) if alt else (255, 255, 255)
pdf.set_fill_color(*fill_rgb)
pdf.set_font("Helvetica", size=8)
# measure max height needed
heights = []
for i, t in enumerate(texts):
lines = pdf.get_string_width(t) / W[i] + 1
heights.append(max(1, int(lines) + 1) * 5)
h = max(heights)
h = min(h, 10)
for i, t in enumerate(texts):
pdf.set_x(10 + sum(W[:i]))
pdf.multi_cell(W[i], h / max(1, pdf.get_string_width(t) / W[i] + 1),
t, border=1, fill=True, max_line_height=5)
# ensure we're on next line
pdf.ln(0)
for section_title, rows in SECCIONES:
pdf.set_font("Helvetica", "B", 9)
pdf.set_fill_color(30, 80, 140)
pdf.set_text_color(255, 255, 255)
pdf.cell(0, 7, f" {section_title}", fill=True, new_x="LMARGIN", new_y="NEXT")
pdf.set_text_color(0, 0, 0)
th(["Acronimo S-57", "Descripcion", "Nombres reconocidos en QGIS"])
for idx, (acro, desc, aliases) in enumerate(rows):
pdf.set_fill_color(245, 248, 252) if idx % 2 else pdf.set_fill_color(255, 255, 255)
pdf.set_font("Helvetica", "B", 8)
pdf.set_x(10)
pdf.cell(W[0], 6, acro, border=1, fill=True)
pdf.set_font("Helvetica", size=8)
pdf.cell(W[1], 6, desc, border=1, fill=True)
pdf.multi_cell(W[2], 6, aliases, border=1, fill=True)
pdf.ln(4)
# atributos
pdf.set_font("Helvetica", "B", 9)
pdf.set_fill_color(30, 80, 140)
pdf.set_text_color(255, 255, 255)
pdf.cell(0, 7, " CAMPOS DE ATRIBUTOS (columnas de tu SHP)", fill=True, new_x="LMARGIN", new_y="NEXT")
pdf.set_text_color(0, 0, 0)
AW = [55, 22, 78]
pdf.set_fill_color(200, 215, 235)
pdf.set_font("Helvetica", "B", 8)
pdf.set_x(10); pdf.cell(AW[0], 6, "Nombre campo en SHP", border=1, fill=True)
pdf.cell(AW[1], 6, "Atrib. S-57", border=1, fill=True)
pdf.cell(AW[2], 6, "Descripcion", border=1, fill=True)
pdf.ln()
for idx, (shp_col, s57, desc) in enumerate(ATTR_ROWS):
pdf.set_fill_color(245, 248, 252) if idx % 2 else pdf.set_fill_color(255, 255, 255)
pdf.set_font("Helvetica", size=8)
pdf.set_x(10); pdf.cell(AW[0], 6, shp_col, border=1, fill=True)
pdf.set_font("Helvetica", "B", 8)
pdf.cell(AW[1], 6, s57, border=1, fill=True)
pdf.set_font("Helvetica", size=8)
pdf.cell(AW[2], 6, desc, border=1, fill=True)
pdf.ln()
# tabla LITCHR
pdf.ln(4)
pdf.set_font("Helvetica", "B", 9)
pdf.set_fill_color(30, 80, 140)
pdf.set_text_color(255, 255, 255)
pdf.cell(0, 7, " CODIGOS LITCHR - Caracteristica de luz", fill=True,
new_x="LMARGIN", new_y="NEXT")
pdf.set_text_color(0, 0, 0)
LW = [20, 30, 100]
pdf.set_fill_color(200, 215, 235)
pdf.set_font("Helvetica", "B", 8)
pdf.set_x(10); pdf.cell(LW[0], 6, "Codigo", border=1, fill=True)
pdf.cell(LW[1], 6, "Abrev.", border=1, fill=True)
pdf.cell(LW[2], 6, "Descripcion", border=1, fill=True)
pdf.ln()
for idx, (code, abbr, desc) in enumerate(LITCHR_ROWS):
pdf.set_fill_color(245, 248, 252) if idx % 2 else pdf.set_fill_color(255, 255, 255)
pdf.set_font("Helvetica", size=8)
pdf.set_x(10); pdf.cell(LW[0], 6, code, border=1, fill=True)
pdf.cell(LW[1], 6, abbr, border=1, fill=True)
pdf.cell(LW[2], 6, desc, border=1, fill=True)
pdf.ln()
pdf.ln(5)
pdf.set_font("Helvetica", "I", 8)
pdf.set_text_color(80, 80, 80)
pdf.multi_cell(0, 5,
"Nota: para anadir un nombre de capa no listado aqui, edita 'layer_mappings' en cell_config.json. "
"Para anadir campos de atributos nuevos, edita 'attribute_mappings'.")
pdf.output(str(OUT))
print(f"PDF generado: {OUT} ({OUT.stat().st_size} bytes)")
+2
View File
@@ -0,0 +1,2 @@
lon,lat,OBJNAM,NOBJNM,LITCHR,LITCHR_TXT,SIGGRP,COLOUR,COLOUR_TXT,SIGPER,SIGPER2,VALNMR,HEIGHT,SECTR1,SECTR2,MLTYLT,ORIENT,BOYSHP,CATLBR,CATCAM,INFORM,TXTDSC,_nga_feature,_nga_volume,_nga_region,_nga_char_raw,_nga_hfm_raw,_nga_range_raw,_nga_notice
-74.253333,11.153333,SBM,,2,Fl.W.,,1,W,8,,8,,,,,,,,,Radar Reflector. ,Yellow CALM superbuoy.,16831 J6255.30,PUB 110,,Fl.W. | period 8s | ,,8,201506
1 lon lat OBJNAM NOBJNM LITCHR LITCHR_TXT SIGGRP COLOUR COLOUR_TXT SIGPER SIGPER2 VALNMR HEIGHT SECTR1 SECTR2 MLTYLT ORIENT BOYSHP CATLBR CATCAM INFORM TXTDSC _nga_feature _nga_volume _nga_region _nga_char_raw _nga_hfm_raw _nga_range_raw _nga_notice
2 -74.253333 11.153333 SBM 2 Fl.W. 1 W 8 8 Radar Reflector. Yellow CALM superbuoy. 16831 J6255.30 PUB 110 Fl.W. | period 8s | 8 201506
+2
View File
@@ -0,0 +1,2 @@
lon,lat,OBJNAM,NOBJNM,LITCHR,LITCHR_TXT,SIGGRP,COLOUR,COLOUR_TXT,SIGPER,SIGPER2,VALNMR,HEIGHT,SECTR1,SECTR2,MLTYLT,ORIENT,BOYSHP,CATLBR,CATCAM,INFORM,TXTDSC,_nga_feature,_nga_volume,_nga_region,_nga_char_raw,_nga_hfm_raw,_nga_range_raw,_nga_notice
-75.598336,10.316667,Sea buoy,,5,Q.W.,,"1,3","W,R",,,,,,,,,3,,,,"SAFE WATER RW, buoy, topmark.",16704,PUB 110,CARTAGENA:,Q.W. | ,,,201622
1 lon lat OBJNAM NOBJNM LITCHR LITCHR_TXT SIGGRP COLOUR COLOUR_TXT SIGPER SIGPER2 VALNMR HEIGHT SECTR1 SECTR2 MLTYLT ORIENT BOYSHP CATLBR CATCAM INFORM TXTDSC _nga_feature _nga_volume _nga_region _nga_char_raw _nga_hfm_raw _nga_range_raw _nga_notice
2 -75.598336 10.316667 Sea buoy 5 Q.W. 1,3 W,R 3 SAFE WATER RW, buoy, topmark. 16704 PUB 110 CARTAGENA: Q.W. | 201622
+62
View File
@@ -0,0 +1,62 @@
lon,lat,OBJNAM,NOBJNM,LITCHR,LITCHR_TXT,SIGGRP,COLOUR,COLOUR_TXT,SIGPER,SIGPER2,VALNMR,HEIGHT,SECTR1,SECTR2,MLTYLT,ORIENT,BOYSHP,CATLBR,CATCAM,INFORM,TXTDSC,_nga_feature,_nga_volume,_nga_region,_nga_char_raw,_nga_hfm_raw,_nga_range_raw,_nga_notice
-75.786389,9.529167,Tanker Loading Unit TLU-3,,13,Mo.(U)Y.,(U),6,Y,15,,,,,,,,,,,,,16674 J6153.6,PUB 110,GOLFO DE MORROSQUILLO:,Mo.(U)Y. | period 15s | ,,,201904
-75.992225,9.591111,Roca Morrosquillo,,2,Fl.W.,,1,W,3,0.3,10,6,,,,,,,,,"Red tower, white bands.",16678 J6154.5,PUB 110,GOLFO DE MORROSQUILLO:,"Fl.W. | period 3s | fl. 0.3s, ec. 2.7s | ",20ft/6,10,201506
-75.855281,9.693139,Isla Ceycen,,2,Fl.W.,,1,W,10,0.8,17,20,,,,,,,,,"Red tower, white bands; 59.",16679 J6156,PUB 110,,"Fl.W. | period 10s | fl. 0.8s, ec. 9.2s | ",66ft/20,17,201647
-75.87075,9.782528,Isla Mucura,,2,Fl.W.,,1,W,6.7,0.5,11,20,,,,,,,,,"Red tower, white bands; 59.",16680 J6157,PUB 110,,"Fl.W. | period 6.7s | fl. 0.5s, ec. 6.2s | ",66ft/20,11,201506
-75.72745,10.145556,Isla Arenas,,2,Fl.W.,,1,W,12,1.2,13,20,,,,,,,,,"White metal tower, red stripes; 59.",16684 J6159,PUB 110,,"Fl.W. | period 12s | fl. 1.2s, ec. 10.8s | ",66ft/20,13,201506
-75.80055,10.168056,Isla del Rosario,,2,Fl.W.,,1,W,10,0.79,12,14,,,,,,,,,"White tower, red bands; 39.",16687.1 J6159.7,PUB 110,,"Fl.W. | period 10s | fl. 0.79s, ec. 9.21s | ",46ft/14,12,201622
-75.74,10.235,Isla del Tesoro,,2,Fl.W.,,1,W,6.6,0.5,12,20,,,,,,,,,"White tower, red bands.",16688 J6160,PUB 110,,"Fl.W. | period 6.6s | fl. 0.5s, ec. 6.1s | ",66ft/20,12,201501
-75.58095,10.339944,Isla Tierra Bomba,,2,Fl.W.,,1,W,12,1.2,26,112,,,,,,,,,"Red metal framework tower, white bands; 131.",16700 J6166,PUB 110,CARTAGENA:,"Fl.W. | period 12s | fl. 1.2s, ec. 10.8s | ",367ft/112,26,201626
-75.556081,10.281361,"Bajos de Coquitos, pier, E",,2,Fl.(4)W.,(4),1,W,9,,4,11,,,,,,,,,"Yellow post, ""X"" topmark.",16703 J6166.1,PUB 110,CARTAGENA:,Fl.(4)W. | period 9s | ,36ft/11,4,201812
-75.558944,10.280222,W,,2,Fl.(4)W.,(4),1,W,9,,4,11,,,,,,,,,"Yellow post, ""X"" topmark.",16703.5 J6166.2,PUB 110,CARTAGENA:,Fl.(4)W. | period 9s | ,36ft/11,4,201812
-75.598336,10.316667,RACON,,,C(- • - • ),,1,W,,,,,,,,,,,,(3 & 10cm). ,,16704,PUB 110,CARTAGENA:,C(- • - • ) | ,,,201622
-75.511664,10.317889,"Port Mamonal, Ecopetrol pier, N",,5,Q.Bu.,,5,Bu,,,,,,,,,,,,5 Q.Bu. shown along pier. ,,16705 J6166.73,PUB 110,CARTAGENA:,Q.Bu. | ,,,201831
-75.511389,10.316667,S,,5,Q.Bu.,,5,Bu,,,,,,,,,,,,,,16705.1 J6166.69,PUB 110,CARTAGENA:,Q.Bu. | ,,,201831
-75.508361,10.345056,Atunes de Colombia,,2,Fl.Bu.,,5,Bu,4,1.5,,,,,,,,,,,,16705.5 J6166.92,PUB 110,CARTAGENA:,"Fl.Bu. | period 4s | fl. 1.5s, ec. 2.5s | ",,,201831
-75.530556,10.388611,"2 ENAP, Range, front",,5,Q.Y.,,6,Y,,,10,18,,,,,,,,,"White tower, red bands; 30.",16725.5 J6167.65,PUB 110,CARTAGENA:,Q.Y. | ,59ft/18,10,201506
-75.530556,10.389444,"1 ENAP, rear, about 110 meters 000^ from front",,5,Q.Y.,,6,Y,,,10,22,,,,,,,,,"White tower, red bands; 16.",16725.51 J6167.66,PUB 110,CARTAGENA:,Q.Y. | ,72ft/22,10,201506
-75.544975,10.391222,Castillo Grande,,2,Fl.W.,,1,W,15,1.2,20,24,,,,,,,,"Beacons ""E1'' Fl G 3s 5M and ""E2'' Fl R 3s 4M mark small craft passage 1.5 miles W. ",Beige concrete tower; 72.,16726 J6167.60,PUB 110,,"Fl.W. | period 15s | fl. 1.2s, ec. 13.8s | ",79ft/24,20,201919
-75.546031,10.411472,Naval Base pier,,2,Fl.Bu.,,5,Bu,3.5,1.0,5,4,,,,,,,,4 Fl.Bu. 3.5s shown along pier. ,,16727 J6167.2,PUB 110,,"Fl.Bu. | period 3.5s | fl. 1.0s, ec. 2.5s | ",11ft/4,5,201831
-75.51585,10.446103,Crespo AVIATION LIGHT,,20,Al.Fl.W.G.,,"1,4","W,G",10,,20,,,,,,,,,,,16732 J6172,PUB 110,,Al.Fl.W.G. | period 10s | ,,20,201501
-75.499169,10.573056,Punta Canoas,,2,Fl.W.,,1,W,5,0.4,12,96,,,,,,,,,Red and white tower; 39.,16736 J6174,PUB 110,,"Fl.W. | period 5s | fl. 0.4s, ec. 4.6s | ",315ft/96,12,201722
-75.266389,10.785361,Punta Galera,,9,Oc.W.,,1,W,4,2.5,18,12,,,,,,,,,"White fiberglass tower, red bands.",16740 J6176,PUB 110,,"Oc.W. | period 4s | fl. 2.5s, ec. 1.5s | ",39ft/12,18,201625
-75.017525,10.963222,Punta Hermosa,,9,Oc.W.,,1,W,4,2.5,28,134,,,,,,,,,"White tower, red bands; 39.",16744 J6180,PUB 110,,"Oc.W. | period 4s | fl. 2.5s, ec. 1.5s | ",440ft/134,28,201506
-74.849525,11.10625,"F-1, E. breakwater, head",,8,Iso.G.,,4,G,2,,9,20,,,,,,,,,"White tower, orange bands.",16747 J6190,PUB 110,RIO MAGDALENA:,Iso.G. | period 2s | ,66ft/20,9,201926
-74.8547,11.106222,"F-2, W. breakwater, head",,8,Iso.R.,,3,R,2,,13,23,,,,,,,,,"White framework tower, orange bands.",16748 J6188,PUB 110,RIO MAGDALENA:,Iso.R. | period 2s | ,75ft/23,13,201708
-74.8547,11.106222,RACON,,,B(- • • • ),,1,W,,,,,,,,,,,,(3 & 10cm). ,,16748 J6188,PUB 110,RIO MAGDALENA:,B(- • • • ) | ,,,201708
-74.848275,11.103672,"E-1 Range, front, on E. breakwater, common front",,8,Iso.Bu.,,5,Bu,5,,13,10,,,,176.0,,,,Rear 16760. ,"Metal framework tower, white rectangular daymark, red stripe.",16756 J6191,PUB 110,RIO MAGDALENA:,Iso.Bu. | period 5s | ,33ft/10,13,201927
-74.848275,11.103672,,,8,Iso.W.,,1,W,4,,9,10,,,,,,,,Rear 16761. ,,16756 J6191,PUB 110,RIO MAGDALENA:,Iso.W. | period 4s | ,33ft/10,9,201927
-74.846439,11.101583,"E-3 Rear, 310 meters 139^18' from front",,8,Iso.Bu.,,5,Bu,5,,9,22,,,,176.0,,,,,"Orange and white framework tower, white rectangular daymark, red stripe.",16760 J6191.10,PUB 110,RIO MAGDALENA:,Iso.Bu. | period 5s | ,72ft/22,9,201506
-74.844969,11.100306,"E-3A Rear, 135^42' from front",,8,Iso.W.,,1,W,5,,12,20,,,,176.0,,,,,"Metal framework tower, white rectangular daymark, red stripe.",16761 J6191.2,PUB 110,RIO MAGDALENA:,Iso.W. | period 5s | ,66ft/20,12,201927
-74.8495,11.102194,X-1,,2,Fl.(4)G.,(4),4,G,11,0.5,6,8,,,,,,,,,"Red framework tower, white bands.",16763 J6193.5,PUB 110,RIO MAGDALENA:,"Fl.(4)G. | period 11s | fl. 0.5s, ec. 1.5s | fl. 0.5s, ec. 1.5s | fl. 0.5s, ec. 1.5s | fl. 0.5s, ec. 4.5s | ",26ft/8,6,201522
-74.85325,11.1,"X-2, W. side of river",,2,Fl.(4)R.,(4),3,R,11,0.5,6,6,,,,,,,,,"Red framework tower, white bands.",16764 J6194,PUB 110,RIO MAGDALENA:,"Fl.(4)R. | period 11s | fl. 0.5s, ec. 1.5s | fl. 0.5s, ec. 1.5s | fl. 0.5s, ec. 1.5s | fl. 0.5s, ec. 4.5s | ",20ft/6,6,201605
-74.847219,11.091389,X-3,,2,Fl.(4)G.,(4),4,G,11,0.5,6,8,,,,,,,,,"Red framework tower, white bands.",16766 J6201,PUB 110,,"Fl.(4)G. | period 11s | fl. 0.5s, ec. 1.5s | fl. 0.5s, ec. 1.5s | fl. 0.5s, ec. 1.5s | fl. 0.5s, ec. 4.5s | ",26ft/8,6,201522
-74.851775,11.093167,"X-4, W. side of river",,8,Iso.R.,,3,R,4,,4,8,,,,,,,,,"Orange framework tower, white bands.",16768 J6196,PUB 110,RIO MAGDALENA:,Iso.R. | period 4s | ,26ft/8,4,201506
-74.846811,11.070278,E-4,,8,Dir.Iso.W.,,1,W,4,,4,11,,,,141.0,,,,Visible on bearing 322°12`. ,"Orange metal tower, white bands.",16772 J6198,PUB 110,RIO MAGDALENA:,Dir.Iso.W. | period 4s | ,36ft/11,4,201506
-74.846811,11.070278,,,8,Iso.R.,,3,R,4,,,,,,,,,,,,,16772 J6198,PUB 110,RIO MAGDALENA:,Iso.R. | period 4s | ,,,201506
-74.846725,11.089222,X-5,,2,Fl.(4)G.,(4),4,G,11,0.5,6,8,,,,,,,,,"Red framework tower, white bands.",16776 J6200,PUB 110,RIO MAGDALENA:,"Fl.(4)G. | period 11s | fl. 0.5s, ec. 1.5s | fl. 0.5s, ec. 1.5s | fl. 0.5s, ec. 1.5s | fl. 0.5s, ec. 4.5s | ",26ft/8,6,201522
-74.850556,11.087722,"X-6, W. side of river",,2,Fl.(4)R.,(4),3,R,11,0.5,6,6,,,,,,,,,"White framework tower, red bands.",16780 J6202,PUB 110,RIO MAGDALENA:,"Fl.(4)R. | period 11s | fl. 0.5s, ec. 1.5s | fl. 0.5s, ec. 1.5s | fl. 0.5s, ec. 1.5s | fl. 0.5s, ec. 4.5s | ",20ft/6,6,202007
-74.844189,11.064889,"E-6 Range, front",,8,Iso.Bu.,,5,Bu,2,,11,11,,,,167.7,,,,Visible 166°-170°. ,"Orangee framework tower, white bands, red rectangular daymark, white stripes.",16784 J6191.90,PUB 110,RIO MAGDALENA:,Iso.Bu. | period 2s | ,36ft/11,11,201506
-74.842833,11.058694,"E-8 Rear, 695 meters 167^42' from front",,8,Iso.Bu.,,5,Bu,4,,15,25,,,,167.7,,,,Visible on range line only. ,"Red and white framework tower, white rectangular daymark, red stripe.",16788 J6191.91,PUB 110,RIO MAGDALENA:,Iso.Bu. | period 4s | ,82ft/25,15,201927
-74.836769,11.053728,"E-16 Range, front",,8,Iso.Bu.,,5,Bu,6,,9,12,,,,176.0,,,,,Orange and white framework tower.,16790 J6193,PUB 110,RIO MAGDALENA:,Iso.Bu. | period 6s | ,39ft/12,9,201501
-74.840719,11.056147,"E-14 Rear, 512 meters 302^ from front",,8,Iso.Bu.,,5,Bu,6,,9,24,,,,176.0,,,,,Tower.,16790.5 J6192.10,PUB 110,RIO MAGDALENA:,Iso.Bu. | period 6s | ,79ft/24,9,201501
-74.849169,11.081833,"X-8, W. side of river",,2,Fl.R.,,3,R,4,1.0,4,8,,,,,,,,,"Orange framework tower, white bands.",16792 J6204,PUB 110,RIO MAGDALENA:,"Fl.R. | period 4s | fl. 1.0s, ec. 3.0s | ",26ft/8,4,201506
-74.848,11.076083,"X-10, W. side of river, below Las Flores",,2,Fl.(4)R.,(4),3,R,11,0.5,6,6,,,,,,,,,"White framework tower, red bands.",16804 J6206,PUB 110,RIO MAGDALENA:,"Fl.(4)R. | period 11s | fl. 0.5s, ec. 1.5s | fl. 0.5s, ec. 1.5s | fl. 0.5s, ec. 1.5s | fl. 0.5s, ec. 4.5s | ",20ft/6,6,202007
-74.825561,11.043097,"E-18 Range, front",,8,Dir.Iso.W.R.G.,,"1,3,4","W,R,G",4,,8,13,,,,176.0,,,,"R. 137°30`-141°30`, W.-142°30`, G.-146°30`. ","White metal framework tower, red bands.",16806 J6216,PUB 110,RIO MAGDALENA:,Dir.Iso.W.R.G. | period 4s | ,43ft/13,8,201927
-74.822556,11.03925,"E-20 Rear, 522 meters 142^ 12' from front",,8,Iso.Bu.,,5,Bu,4,,15,30,,,,142.2,,,,,"White and orange metal framework tower, white rectangular daymark, orange stripe.",16807 J6218,PUB 110,RIO MAGDALENA:,Iso.Bu. | period 4s | ,98ft/30,15,201508
-74.844189,11.064917,X-12,,2,Fl.(4)R.,(4),3,R,11,0.5,6,6,,,,,,,,,"Red framework tower, white bands.",16808 J6207,PUB 110,RIO MAGDALENA:,"Fl.(4)R. | period 11s | fl. 0.5s, ec. 1.5s | fl. 0.5s, ec. 1.5s | fl. 0.5s, ec. 1.5s | fl. 0.5s, ec. 4.5s | ",20ft/6,6,201605
-74.839614,11.057806,X-14,,2,Fl.(4)R.,(4),3,R,11,0.5,6,6,,,,,,,,,"White framework tower, red bands.",16810 J6208,PUB 110,RIO MAGDALENA:,"Fl.(4)R. | period 11s | fl. 0.5s, ec. 1.5s | fl. 0.5s, ec. 1.5s | fl. 0.5s, ec. 1.5s | fl. 0.5s, ec. 4.5s | ",20ft/6,6,201927
-74.814414,11.041583,X-7,,2,Fl.(4)G,(4),4,G,11,0.5,6,8,,,,,,,,,"Red framework tower, white bands.",16811 J6222.5,PUB 110,RIO MAGDALENA:,"Fl.(4)G | period 11s | fl. 0.5s, ec. 1.5s | fl. 0.5s, ec. 1.5s | fl. 0.5s, ec. 1.5s | fl. 0.5s, ec. 4.5s | ",26ft/8,6,201522
-74.832919,11.05,X-16,,2,Fl.(3)R.,(3),3,R,9,0.8,4,8,,,,,,,,,"Orange framework tower, white bands.",16812 J6214,PUB 110,RIO MAGDALENA:,"Fl.(3)R. | period 9s | fl. 0.8s, ec. 1.2s | fl. 0.8s, ec. 1.2s | fl. 0.8s, ec. 4.2s | ",26ft/8,4,201506
-74.804886,11.035889,X-9,,2,Fl.(4)G.,(4),4,G,11,0.5,6,8,,,,,,,,,"Red framework tower, white bands.",16813 J6222.8,PUB 110,RIO MAGDALENA:,"Fl.(4)G. | period 11s | fl. 0.5s, ec. 1.5s | fl. 0.5s, ec. 1.5s | fl. 0.5s, ec. 1.5s | fl. 0.5s, ec. 4.5s | ",26ft/8,6,201522
-74.7955,11.03,X-11,,2,Fl.(4)G.,(4),4,G,11,0.5,6,8,,,,,,,,,"Red framework tower, white bands, red and white rectangular daymark.",16813.5 J6223,PUB 110,RIO MAGDALENA:,"Fl.(4)G. | period 11s | fl. 0.5s, ec. 1.5s | fl. 0.5s, ec. 1.5s | fl. 0.5s, ec. 1.5s | fl. 0.5s, ec. 4.5s | ",26ft/8,6,201519
-74.7898,11.026,X-13,,2,Fl.(4)G.,(4),4,G,11,0.5,6,8,,,,,,,,,"Red framework tower, white bands, red and white rectangular daymark.",16814 J6226,PUB 110,RIO MAGDALENA:,"Fl.(4)G. | period 11s | fl. 0.5s, ec. 1.5s | fl. 0.5s, ec. 1.5s | fl. 0.5s, ec. 1.5s | fl. 0.5s, ec. 4.5s | ",26ft/8,6,201519
-74.785669,11.022944,X-15,,5,Q.(3)Y.,(3),6,Y,5,,4,8,,,,,,,,,"Orange framework tower, white bands.",16815 J6229,PUB 110,RIO MAGDALENA:,Q.(3)Y. | period 5s | ,26ft/8,4,201506
-74.766811,11.010222,E-7,,8,Dir.Iso.W.R.G.,,"1,3,4","W,R,G",5,,8,16,,,,120.0,,,,"R. 118°30`-121°30`, W.-122°30`, G.-125°30` ",Red and white tower; 76.,16818 J6229.51,PUB 110,RIO MAGDALENA:,Dir.Iso.W.R.G. | period 5s | ,52ft/16,8,201918
-74.778219,11.018667,X-17,,5,Q.(3)Y.,(3),6,Y,5,,4,8,,,,,,,,,"Orange framework tower, white bands.",16819 J6229.2,PUB 110,RIO MAGDALENA:,Q.(3)Y. | period 5s | ,26ft/8,4,201506
-74.236664,11.071111,"Coal loading jetty, SW corner",,2,Fl.R.,,3,R,,,,,,,,,,,,,,16821 J6236,PUB 110,,Fl.R. | ,,,201433
-74.236947,11.071111,NE corner,,2,Fl.R.,,3,R,,,,,,,,,,,,,,16822 J6236.1,PUB 110,,Fl.R. | ,,,201433
-74.233311,11.1165,Simon Bolivar AVIATION LIGHT,,20,Al.Fl.W.G.,,"1,4","W,G",10,,,,,,,,,,,,Tower.,16828 J6254,PUB 110,,Al.Fl.W.G. | period 10s | ,,,201506
-74.253333,11.153333,RACON,,,C(- • - • ),,1,W,,,,,,,,,,,,(3 & 10cm). ,,16831 J6255.30,PUB 110,,C(- • - • ) | ,,,201506
-74.230581,11.25,Morro Grande,,2,Fl.W.,,1,W,15,1.0,22,85,,,,,,,,Obscured beyond Aguja Island 203°-212°. ,White and gray concrete tower; 76.,16832 J6256,PUB 110,BAHIA DE SANTA MARTA:,"Fl.W. | period 15s | fl. 1.0s, ec. 14.0s | ",279ft/85,22,201506
-74.230581,11.25,RACON,,,S(• • • ),,1,W,,,,,,,,,,,,(3 & 10cm). ,,16832 J6256,PUB 110,BAHIA DE SANTA MARTA:,S(• • • ) | ,,,201506
1 lon lat OBJNAM NOBJNM LITCHR LITCHR_TXT SIGGRP COLOUR COLOUR_TXT SIGPER SIGPER2 VALNMR HEIGHT SECTR1 SECTR2 MLTYLT ORIENT BOYSHP CATLBR CATCAM INFORM TXTDSC _nga_feature _nga_volume _nga_region _nga_char_raw _nga_hfm_raw _nga_range_raw _nga_notice
2 -75.786389 9.529167 Tanker Loading Unit TLU-3 13 Mo.(U)Y. (U) 6 Y 15 16674 J6153.6 PUB 110 GOLFO DE MORROSQUILLO: Mo.(U)Y. | period 15s | 201904
3 -75.992225 9.591111 Roca Morrosquillo 2 Fl.W. 1 W 3 0.3 10 6 Red tower, white bands. 16678 J6154.5 PUB 110 GOLFO DE MORROSQUILLO: Fl.W. | period 3s | fl. 0.3s, ec. 2.7s | 20ft/6 10 201506
4 -75.855281 9.693139 Isla Ceycen 2 Fl.W. 1 W 10 0.8 17 20 Red tower, white bands; 59. 16679 J6156 PUB 110 Fl.W. | period 10s | fl. 0.8s, ec. 9.2s | 66ft/20 17 201647
5 -75.87075 9.782528 Isla Mucura 2 Fl.W. 1 W 6.7 0.5 11 20 Red tower, white bands; 59. 16680 J6157 PUB 110 Fl.W. | period 6.7s | fl. 0.5s, ec. 6.2s | 66ft/20 11 201506
6 -75.72745 10.145556 Isla Arenas 2 Fl.W. 1 W 12 1.2 13 20 White metal tower, red stripes; 59. 16684 J6159 PUB 110 Fl.W. | period 12s | fl. 1.2s, ec. 10.8s | 66ft/20 13 201506
7 -75.80055 10.168056 Isla del Rosario 2 Fl.W. 1 W 10 0.79 12 14 White tower, red bands; 39. 16687.1 J6159.7 PUB 110 Fl.W. | period 10s | fl. 0.79s, ec. 9.21s | 46ft/14 12 201622
8 -75.74 10.235 Isla del Tesoro 2 Fl.W. 1 W 6.6 0.5 12 20 White tower, red bands. 16688 J6160 PUB 110 Fl.W. | period 6.6s | fl. 0.5s, ec. 6.1s | 66ft/20 12 201501
9 -75.58095 10.339944 Isla Tierra Bomba 2 Fl.W. 1 W 12 1.2 26 112 Red metal framework tower, white bands; 131. 16700 J6166 PUB 110 CARTAGENA: Fl.W. | period 12s | fl. 1.2s, ec. 10.8s | 367ft/112 26 201626
10 -75.556081 10.281361 Bajos de Coquitos, pier, E 2 Fl.(4)W. (4) 1 W 9 4 11 Yellow post, "X" topmark. 16703 J6166.1 PUB 110 CARTAGENA: Fl.(4)W. | period 9s | 36ft/11 4 201812
11 -75.558944 10.280222 W 2 Fl.(4)W. (4) 1 W 9 4 11 Yellow post, "X" topmark. 16703.5 J6166.2 PUB 110 CARTAGENA: Fl.(4)W. | period 9s | 36ft/11 4 201812
12 -75.598336 10.316667 RACON C(- • - • ) 1 W (3 & 10cm). 16704 PUB 110 CARTAGENA: C(- • - • ) | 201622
13 -75.511664 10.317889 Port Mamonal, Ecopetrol pier, N 5 Q.Bu. 5 Bu 5 Q.Bu. shown along pier. 16705 J6166.73 PUB 110 CARTAGENA: Q.Bu. | 201831
14 -75.511389 10.316667 S 5 Q.Bu. 5 Bu 16705.1 J6166.69 PUB 110 CARTAGENA: Q.Bu. | 201831
15 -75.508361 10.345056 Atunes de Colombia 2 Fl.Bu. 5 Bu 4 1.5 16705.5 J6166.92 PUB 110 CARTAGENA: Fl.Bu. | period 4s | fl. 1.5s, ec. 2.5s | 201831
16 -75.530556 10.388611 2 ENAP, Range, front 5 Q.Y. 6 Y 10 18 White tower, red bands; 30. 16725.5 J6167.65 PUB 110 CARTAGENA: Q.Y. | 59ft/18 10 201506
17 -75.530556 10.389444 1 ENAP, rear, about 110 meters 000^ from front 5 Q.Y. 6 Y 10 22 White tower, red bands; 16. 16725.51 J6167.66 PUB 110 CARTAGENA: Q.Y. | 72ft/22 10 201506
18 -75.544975 10.391222 Castillo Grande 2 Fl.W. 1 W 15 1.2 20 24 Beacons "E1'' Fl G 3s 5M and "E2'' Fl R 3s 4M mark small craft passage 1.5 miles W. Beige concrete tower; 72. 16726 J6167.60 PUB 110 Fl.W. | period 15s | fl. 1.2s, ec. 13.8s | 79ft/24 20 201919
19 -75.546031 10.411472 Naval Base pier 2 Fl.Bu. 5 Bu 3.5 1.0 5 4 4 Fl.Bu. 3.5s shown along pier. 16727 J6167.2 PUB 110 Fl.Bu. | period 3.5s | fl. 1.0s, ec. 2.5s | 11ft/4 5 201831
20 -75.51585 10.446103 Crespo AVIATION LIGHT 20 Al.Fl.W.G. 1,4 W,G 10 20 16732 J6172 PUB 110 Al.Fl.W.G. | period 10s | 20 201501
21 -75.499169 10.573056 Punta Canoas 2 Fl.W. 1 W 5 0.4 12 96 Red and white tower; 39. 16736 J6174 PUB 110 Fl.W. | period 5s | fl. 0.4s, ec. 4.6s | 315ft/96 12 201722
22 -75.266389 10.785361 Punta Galera 9 Oc.W. 1 W 4 2.5 18 12 White fiberglass tower, red bands. 16740 J6176 PUB 110 Oc.W. | period 4s | fl. 2.5s, ec. 1.5s | 39ft/12 18 201625
23 -75.017525 10.963222 Punta Hermosa 9 Oc.W. 1 W 4 2.5 28 134 White tower, red bands; 39. 16744 J6180 PUB 110 Oc.W. | period 4s | fl. 2.5s, ec. 1.5s | 440ft/134 28 201506
24 -74.849525 11.10625 F-1, E. breakwater, head 8 Iso.G. 4 G 2 9 20 White tower, orange bands. 16747 J6190 PUB 110 RIO MAGDALENA: Iso.G. | period 2s | 66ft/20 9 201926
25 -74.8547 11.106222 F-2, W. breakwater, head 8 Iso.R. 3 R 2 13 23 White framework tower, orange bands. 16748 J6188 PUB 110 RIO MAGDALENA: Iso.R. | period 2s | 75ft/23 13 201708
26 -74.8547 11.106222 RACON B(- • • • ) 1 W (3 & 10cm). 16748 J6188 PUB 110 RIO MAGDALENA: B(- • • • ) | 201708
27 -74.848275 11.103672 E-1 Range, front, on E. breakwater, common front 8 Iso.Bu. 5 Bu 5 13 10 176.0 Rear 16760. Metal framework tower, white rectangular daymark, red stripe. 16756 J6191 PUB 110 RIO MAGDALENA: Iso.Bu. | period 5s | 33ft/10 13 201927
28 -74.848275 11.103672 8 Iso.W. 1 W 4 9 10 Rear 16761. 16756 J6191 PUB 110 RIO MAGDALENA: Iso.W. | period 4s | 33ft/10 9 201927
29 -74.846439 11.101583 E-3 Rear, 310 meters 139^18' from front 8 Iso.Bu. 5 Bu 5 9 22 176.0 Orange and white framework tower, white rectangular daymark, red stripe. 16760 J6191.10 PUB 110 RIO MAGDALENA: Iso.Bu. | period 5s | 72ft/22 9 201506
30 -74.844969 11.100306 E-3A Rear, 135^42' from front 8 Iso.W. 1 W 5 12 20 176.0 Metal framework tower, white rectangular daymark, red stripe. 16761 J6191.2 PUB 110 RIO MAGDALENA: Iso.W. | period 5s | 66ft/20 12 201927
31 -74.8495 11.102194 X-1 2 Fl.(4)G. (4) 4 G 11 0.5 6 8 Red framework tower, white bands. 16763 J6193.5 PUB 110 RIO MAGDALENA: Fl.(4)G. | period 11s | fl. 0.5s, ec. 1.5s | fl. 0.5s, ec. 1.5s | fl. 0.5s, ec. 1.5s | fl. 0.5s, ec. 4.5s | 26ft/8 6 201522
32 -74.85325 11.1 X-2, W. side of river 2 Fl.(4)R. (4) 3 R 11 0.5 6 6 Red framework tower, white bands. 16764 J6194 PUB 110 RIO MAGDALENA: Fl.(4)R. | period 11s | fl. 0.5s, ec. 1.5s | fl. 0.5s, ec. 1.5s | fl. 0.5s, ec. 1.5s | fl. 0.5s, ec. 4.5s | 20ft/6 6 201605
33 -74.847219 11.091389 X-3 2 Fl.(4)G. (4) 4 G 11 0.5 6 8 Red framework tower, white bands. 16766 J6201 PUB 110 Fl.(4)G. | period 11s | fl. 0.5s, ec. 1.5s | fl. 0.5s, ec. 1.5s | fl. 0.5s, ec. 1.5s | fl. 0.5s, ec. 4.5s | 26ft/8 6 201522
34 -74.851775 11.093167 X-4, W. side of river 8 Iso.R. 3 R 4 4 8 Orange framework tower, white bands. 16768 J6196 PUB 110 RIO MAGDALENA: Iso.R. | period 4s | 26ft/8 4 201506
35 -74.846811 11.070278 E-4 8 Dir.Iso.W. 1 W 4 4 11 141.0 Visible on bearing 322°12`. Orange metal tower, white bands. 16772 J6198 PUB 110 RIO MAGDALENA: Dir.Iso.W. | period 4s | 36ft/11 4 201506
36 -74.846811 11.070278 8 Iso.R. 3 R 4 16772 J6198 PUB 110 RIO MAGDALENA: Iso.R. | period 4s | 201506
37 -74.846725 11.089222 X-5 2 Fl.(4)G. (4) 4 G 11 0.5 6 8 Red framework tower, white bands. 16776 J6200 PUB 110 RIO MAGDALENA: Fl.(4)G. | period 11s | fl. 0.5s, ec. 1.5s | fl. 0.5s, ec. 1.5s | fl. 0.5s, ec. 1.5s | fl. 0.5s, ec. 4.5s | 26ft/8 6 201522
38 -74.850556 11.087722 X-6, W. side of river 2 Fl.(4)R. (4) 3 R 11 0.5 6 6 White framework tower, red bands. 16780 J6202 PUB 110 RIO MAGDALENA: Fl.(4)R. | period 11s | fl. 0.5s, ec. 1.5s | fl. 0.5s, ec. 1.5s | fl. 0.5s, ec. 1.5s | fl. 0.5s, ec. 4.5s | 20ft/6 6 202007
39 -74.844189 11.064889 E-6 Range, front 8 Iso.Bu. 5 Bu 2 11 11 167.7 Visible 166°-170°. Orangee framework tower, white bands, red rectangular daymark, white stripes. 16784 J6191.90 PUB 110 RIO MAGDALENA: Iso.Bu. | period 2s | 36ft/11 11 201506
40 -74.842833 11.058694 E-8 Rear, 695 meters 167^42' from front 8 Iso.Bu. 5 Bu 4 15 25 167.7 Visible on range line only. Red and white framework tower, white rectangular daymark, red stripe. 16788 J6191.91 PUB 110 RIO MAGDALENA: Iso.Bu. | period 4s | 82ft/25 15 201927
41 -74.836769 11.053728 E-16 Range, front 8 Iso.Bu. 5 Bu 6 9 12 176.0 Orange and white framework tower. 16790 J6193 PUB 110 RIO MAGDALENA: Iso.Bu. | period 6s | 39ft/12 9 201501
42 -74.840719 11.056147 E-14 Rear, 512 meters 302^ from front 8 Iso.Bu. 5 Bu 6 9 24 176.0 Tower. 16790.5 J6192.10 PUB 110 RIO MAGDALENA: Iso.Bu. | period 6s | 79ft/24 9 201501
43 -74.849169 11.081833 X-8, W. side of river 2 Fl.R. 3 R 4 1.0 4 8 Orange framework tower, white bands. 16792 J6204 PUB 110 RIO MAGDALENA: Fl.R. | period 4s | fl. 1.0s, ec. 3.0s | 26ft/8 4 201506
44 -74.848 11.076083 X-10, W. side of river, below Las Flores 2 Fl.(4)R. (4) 3 R 11 0.5 6 6 White framework tower, red bands. 16804 J6206 PUB 110 RIO MAGDALENA: Fl.(4)R. | period 11s | fl. 0.5s, ec. 1.5s | fl. 0.5s, ec. 1.5s | fl. 0.5s, ec. 1.5s | fl. 0.5s, ec. 4.5s | 20ft/6 6 202007
45 -74.825561 11.043097 E-18 Range, front 8 Dir.Iso.W.R.G. 1,3,4 W,R,G 4 8 13 176.0 R. 137°30`-141°30`, W.-142°30`, G.-146°30`. White metal framework tower, red bands. 16806 J6216 PUB 110 RIO MAGDALENA: Dir.Iso.W.R.G. | period 4s | 43ft/13 8 201927
46 -74.822556 11.03925 E-20 Rear, 522 meters 142^ 12' from front 8 Iso.Bu. 5 Bu 4 15 30 142.2 White and orange metal framework tower, white rectangular daymark, orange stripe. 16807 J6218 PUB 110 RIO MAGDALENA: Iso.Bu. | period 4s | 98ft/30 15 201508
47 -74.844189 11.064917 X-12 2 Fl.(4)R. (4) 3 R 11 0.5 6 6 Red framework tower, white bands. 16808 J6207 PUB 110 RIO MAGDALENA: Fl.(4)R. | period 11s | fl. 0.5s, ec. 1.5s | fl. 0.5s, ec. 1.5s | fl. 0.5s, ec. 1.5s | fl. 0.5s, ec. 4.5s | 20ft/6 6 201605
48 -74.839614 11.057806 X-14 2 Fl.(4)R. (4) 3 R 11 0.5 6 6 White framework tower, red bands. 16810 J6208 PUB 110 RIO MAGDALENA: Fl.(4)R. | period 11s | fl. 0.5s, ec. 1.5s | fl. 0.5s, ec. 1.5s | fl. 0.5s, ec. 1.5s | fl. 0.5s, ec. 4.5s | 20ft/6 6 201927
49 -74.814414 11.041583 X-7 2 Fl.(4)G (4) 4 G 11 0.5 6 8 Red framework tower, white bands. 16811 J6222.5 PUB 110 RIO MAGDALENA: Fl.(4)G | period 11s | fl. 0.5s, ec. 1.5s | fl. 0.5s, ec. 1.5s | fl. 0.5s, ec. 1.5s | fl. 0.5s, ec. 4.5s | 26ft/8 6 201522
50 -74.832919 11.05 X-16 2 Fl.(3)R. (3) 3 R 9 0.8 4 8 Orange framework tower, white bands. 16812 J6214 PUB 110 RIO MAGDALENA: Fl.(3)R. | period 9s | fl. 0.8s, ec. 1.2s | fl. 0.8s, ec. 1.2s | fl. 0.8s, ec. 4.2s | 26ft/8 4 201506
51 -74.804886 11.035889 X-9 2 Fl.(4)G. (4) 4 G 11 0.5 6 8 Red framework tower, white bands. 16813 J6222.8 PUB 110 RIO MAGDALENA: Fl.(4)G. | period 11s | fl. 0.5s, ec. 1.5s | fl. 0.5s, ec. 1.5s | fl. 0.5s, ec. 1.5s | fl. 0.5s, ec. 4.5s | 26ft/8 6 201522
52 -74.7955 11.03 X-11 2 Fl.(4)G. (4) 4 G 11 0.5 6 8 Red framework tower, white bands, red and white rectangular daymark. 16813.5 J6223 PUB 110 RIO MAGDALENA: Fl.(4)G. | period 11s | fl. 0.5s, ec. 1.5s | fl. 0.5s, ec. 1.5s | fl. 0.5s, ec. 1.5s | fl. 0.5s, ec. 4.5s | 26ft/8 6 201519
53 -74.7898 11.026 X-13 2 Fl.(4)G. (4) 4 G 11 0.5 6 8 Red framework tower, white bands, red and white rectangular daymark. 16814 J6226 PUB 110 RIO MAGDALENA: Fl.(4)G. | period 11s | fl. 0.5s, ec. 1.5s | fl. 0.5s, ec. 1.5s | fl. 0.5s, ec. 1.5s | fl. 0.5s, ec. 4.5s | 26ft/8 6 201519
54 -74.785669 11.022944 X-15 5 Q.(3)Y. (3) 6 Y 5 4 8 Orange framework tower, white bands. 16815 J6229 PUB 110 RIO MAGDALENA: Q.(3)Y. | period 5s | 26ft/8 4 201506
55 -74.766811 11.010222 E-7 8 Dir.Iso.W.R.G. 1,3,4 W,R,G 5 8 16 120.0 R. 118°30`-121°30`, W.-122°30`, G.-125°30` Red and white tower; 76. 16818 J6229.51 PUB 110 RIO MAGDALENA: Dir.Iso.W.R.G. | period 5s | 52ft/16 8 201918
56 -74.778219 11.018667 X-17 5 Q.(3)Y. (3) 6 Y 5 4 8 Orange framework tower, white bands. 16819 J6229.2 PUB 110 RIO MAGDALENA: Q.(3)Y. | period 5s | 26ft/8 4 201506
57 -74.236664 11.071111 Coal loading jetty, SW corner 2 Fl.R. 3 R 16821 J6236 PUB 110 Fl.R. | 201433
58 -74.236947 11.071111 NE corner 2 Fl.R. 3 R 16822 J6236.1 PUB 110 Fl.R. | 201433
59 -74.233311 11.1165 Simon Bolivar AVIATION LIGHT 20 Al.Fl.W.G. 1,4 W,G 10 Tower. 16828 J6254 PUB 110 Al.Fl.W.G. | period 10s | 201506
60 -74.253333 11.153333 RACON C(- • - • ) 1 W (3 & 10cm). 16831 J6255.30 PUB 110 C(- • - • ) | 201506
61 -74.230581 11.25 Morro Grande 2 Fl.W. 1 W 15 1.0 22 85 Obscured beyond Aguja Island 203°-212°. White and gray concrete tower; 76. 16832 J6256 PUB 110 BAHIA DE SANTA MARTA: Fl.W. | period 15s | fl. 1.0s, ec. 14.0s | 279ft/85 22 201506
62 -74.230581 11.25 RACON S(• • • ) 1 W (3 & 10cm). 16832 J6256 PUB 110 BAHIA DE SANTA MARTA: S(• • • ) | 201506
+425
View File
@@ -0,0 +1,425 @@
"""
nga_fetch.py — Descarga ayudas a la navegacion del API NGA MSI (Pub. 110)
y genera CSVs listos para importar en QGIS, uno por tipo de objeto S-57.
Uso:
python -X utf8 nga_fetch.py
python -X utf8 nga_fetch.py --lat0 10.0 --lat1 12.0 --lon0 -76.5 --lon1 -73.5
"""
import csv, json, re, ssl, sys, urllib.request
from pathlib import Path
from datetime import datetime
# ── Bounding box por defecto: Costa Caribe colombiana ───────────────────────
DEFAULT_LAT0, DEFAULT_LON0 = 9.5, -77.0
DEFAULT_LAT1, DEFAULT_LON1 = 11.8, -73.5
OUT_DIR = Path(__file__).parent
# ── Headers NGA (requiere origen mismo dominio) ──────────────────────────────
NGA_HEADERS = {
"User-Agent": (
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
"AppleWebKit/537.36 (KHTML, like Gecko) "
"Chrome/120.0.0.0 Safari/537.36"
),
"Referer": "https://msi.nga.mil/Publications/NGALOL",
"Origin": "https://msi.nga.mil",
"Accept": "application/json, text/plain, */*",
"Accept-Language": "en-US,en;q=0.9",
"sec-fetch-site": "same-origin",
"sec-fetch-mode": "cors",
}
NGA_URL = (
"https://msi.nga.mil/api/publications/ngalol/lights-buoys"
"?latitudeLeft={lat0}&longitudeLeft={lon0}"
"&latitudeRight={lat1}&longitudeRight={lon1}"
"&includeRemovals=false&output=json"
)
_SSL_CTX = ssl.create_default_context()
_SSL_CTX.check_hostname = False
_SSL_CTX.verify_mode = ssl.CERT_NONE
# ── Rumbos conocidos de enfilaciones Bocas de Ceniza / Rio Magdalena ─────────
# Fuente: NGA Pub.110 Vol.D (Colombia) + geometria del canal DIMAR.
# E-1 y E-3: canal exterior (barra), E-6/E-8: primer tramo, etc.
# ATENCION: valores aproximados +/-1 grado. Verificar contra DIMAR Lista Faros.
KNOWN_ORIENT = {
'E-1': '176.0', # Bocas de Ceniza, barra exterior
'E-3': '176.0', # rear de E-1
'E-3A': '135.6', # ramal alternativo
'E-6': '167.7', # primer codo (visible 166-170 segun NGA)
'E-8': '167.7', # rear de E-6
'E-16': '122.0', # codo hacia Barranquilla
'E-14': '122.0', # rear de E-16 (302-122 opuesto)
'E-18': '142.2', # Dir luz W/R/G (sector W: 141.5-142.5)
'E-20': '142.2', # rear de E-18
'E-4': '141.0', # Dir Iso W (interior)
'E-7': '120.0', # Dir Iso W/R/G sector W 118.5-121.5
}
# ── Conversion DMS -> decimal ─────────────────────────────────────────────────
_DMS = re.compile(r'(\d+)[deg°º]\s*(\d+)[min\'"]\s*([\d.]+)[sec"″]?\s*([NSEW])', re.I)
_DMS2 = re.compile(r'(\d+)[°º]\s*([\d.]+)[\'"]\s*([NSEW])', re.I)
def dms_dec(txt: str) -> float | None:
txt = txt.replace('\n', ' ').replace('\\u00b0', '°')
m = _DMS.search(txt)
if m:
d, mi, s, h = int(m[1]), int(m[2]), float(m[3]), m[4].upper()
v = d + mi/60 + s/3600
return round(-v if h in ('S','W') else v, 6)
m = _DMS2.search(txt)
if m:
d, mi, h = int(m[1]), float(m[2]), m[3].upper()
v = d + mi/60
return round(-v if h in ('S','W') else v, 6)
# Unicode grado (JSON de NGA viene con °)
m2 = re.search(r'(\d+)°(\d+)\'([\d.]+)"([NSEW])', txt)
if m2:
d, mi, s, h = int(m2[1]), int(m2[2]), float(m2[3]), m2[4].upper()
v = d + mi/60 + s/3600
return round(-v if h in ('S','W') else v, 6)
nums = re.findall(r'[-\d.]+', txt)
return float(nums[0]) if nums else None
def parse_pos(pos: str):
parts = re.split(r'\n', pos)
if len(parts) < 2:
return None, None
return dms_dec(parts[0]), dms_dec(parts[1])
# ── Tablas S-57 ───────────────────────────────────────────────────────────────
LITCHR_FIXED = {
'F':1, 'Fl':2, 'LFl':4, 'Q':5, 'VQ':6, 'UQ':7,
'Iso':8, 'Oc':9, 'IQ':10, 'Mo':13, 'FFl':14,
'Al.Oc':18, 'Al.LFl':19, 'Al.Fl':20, 'Dir':28,
'Dir.Iso':8, 'Dir.Fl':2, 'Dir.Oc':9, 'Dir.LFl':4,
'Q+LFl':25, 'VQ+LFl':26,
}
_COLORES = {'Bu':5, 'Or':11, 'Am':6, 'Vi':7, 'W':1, 'R':3, 'G':4, 'Y':6, 'B':2}
COLOUR_TXT_MAP = {
'1':'W','2':'B','3':'R','4':'G','5':'Bu','6':'Y','7':'Vi','11':'Or',
}
_TIPOS = [
'Al.Fl','Al.Oc','Al.LFl','Dir.Iso','Dir.Fl','Dir.Oc','Dir.LFl',
'VQ+LFl','Q+LFl','FFl','LFl','VQ','UQ','IQ','Mo','Iso','Oc','Fl','Q','F',
]
# ── Parser de tokens de caracteristica ───────────────────────────────────────
def _tokenize(s: str) -> list:
"""'Fl.(4)W.R.' -> ['Fl','(4)','W','R'] sin puntos separadores."""
tokens, cur, depth = [], '', 0
for ch in s.rstrip('.'):
if ch == '(':
depth += 1; cur += ch
elif ch == ')':
depth -= 1; cur += ch
if depth == 0:
tokens.append(cur); cur = ''
elif ch == '.' and depth == 0:
if cur: tokens.append(cur); cur = ''
else:
cur += ch
if cur: tokens.append(cur)
return tokens
_PER_RE = re.compile(r'period\s+([\d.]+)\s*s', re.I)
_FL_RE = re.compile(r'\bfl\.\s*([\d.]+)\s*s', re.I)
_SEC_RE = re.compile(
r'([WRGBY])\.\s*(\d+[°º]\d+[\'`])\s*[-]\s*(\d+[°º]\d+[\'`])', re.I
)
_VIS_RE = re.compile(r'[Vv]isible\s+([\d.]+)[°º]\s*[-]\s*([\d.]+)[°º]')
_BRG_RE = re.compile(r'(\d{2,3})\^([\d.]*)', re.I) # "139^18'" formato NGA
def _deg_min(txt: str) -> str:
txt = txt.strip().replace('`',"'")
m = re.match(r'(\d+)[°º](\d+)', txt)
if m: return str(round(int(m[1]) + int(m[2])/60, 2))
nums = re.findall(r'[\d.]+', txt)
return nums[0] if nums else txt
def parse_char(char_str: str, remarks: str = '', name: str = '') -> dict:
"""Extrae todos los campos S-57 de la caracteristica + remarks del NGA."""
out = {
'LITCHR':'', 'SIGGRP':'', 'COLOUR':'', 'SIGPER':'',
'SIGPER2':'', 'SECTR1':'', 'SECTR2':'', 'MLTYLT':'', 'ORIENT':'',
}
if not char_str:
return out
lines = char_str.split('\n')
base = lines[0].strip()
full = ' '.join(lines)
tokens = _tokenize(base)
remaining = list(tokens)
# 1) Tipo de luz (compuestos primero)
tipo_found = ''
for tipo in _TIPOS:
tp_tokens = tipo.split('.')
if remaining[:len(tp_tokens)] == tp_tokens:
tipo_found = tipo
remaining = remaining[len(tp_tokens):]
break
if tipo_found:
out['LITCHR'] = LITCHR_FIXED.get(tipo_found, tipo_found)
# 2) Grupo opcional "(N)" o "(N+M)"
if remaining and remaining[0].startswith('('):
out['SIGGRP'] = remaining.pop(0)
# 3) Colores: tokens de solo letras
colours = []
for t in remaining:
if re.match(r'^[A-Za-z]+$', t):
# 2 letras primero, luego 1
c = _COLORES.get(t) or _COLORES.get(t[:2]) or _COLORES.get(t[:1])
if c: colours.append(str(c))
out['COLOUR'] = ','.join(colours) if colours else '1'
# 4) Periodo
pm = _PER_RE.search(full)
if pm: out['SIGPER'] = pm.group(1)
# 5) Duracion destello
fm = _FL_RE.search(full)
if fm: out['SIGPER2'] = fm.group(1)
# 6) Sectores y orientacion desde remarks
rem = (remarks or '').replace('`',"'").replace('',"'")
secs = _SEC_RE.findall(rem)
if secs:
lst = [f"{c.upper()}:{_deg_min(s1)}-{_deg_min(s2)}"
for c, s1, s2 in secs]
out['MLTYLT'] = ' | '.join(lst)
_, s1, s2 = secs[0]
out['SECTR1'] = _deg_min(s1)
out['SECTR2'] = _deg_min(s2)
if not out['SECTR1']:
vm = _VIS_RE.search(rem + ' ' + full)
if vm:
out['SECTR1'] = vm.group(1)
out['SECTR2'] = vm.group(2)
# 7) Rumbo de enfilacion desde formato NGA "310 meters 139^18' from front"
bm = _BRG_RE.search(rem + ' ' + full)
if bm:
deg = int(bm.group(1))
frac = bm.group(2)
if frac:
deg_dec = round(deg + float(frac)/60, 1)
else:
deg_dec = float(deg)
# El rumbo NGA es desde el frente al trasero -> invertir para ORIENT
# (ORIENT = rumbo inbound = desde el mar hacia el muelle)
orient = round((deg_dec + 180) % 360, 1)
out['ORIENT'] = str(orient)
# 8) Rumbo conocido de tabla local (mas preciso que calculo)
# Buscar el ID de la enfilacion en el nombre
for key, val in KNOWN_ORIENT.items():
if key in name:
out['ORIENT'] = val
# Si el sector visible es estrecho y ORIENT conocido, derivar sectores
if not out['SECTR1'] and out['LITCHR'] == 28: # Dir light
ang = float(val)
out['SECTR1'] = str(round(ang - 1.5, 1))
out['SECTR2'] = str(round(ang + 1.5, 1))
break
return out
# ── Altura en metros desde "heightFeetMeters" ───────────────────────────────
def parse_height(hfm: str) -> str:
if not hfm: return ''
parts = hfm.strip().split('\n')
if len(parts) >= 2 and parts[1].strip():
return parts[1].strip()
nums = re.findall(r'[\d.]+', parts[0])
return nums[0] if nums else ''
# ── Clasificacion S-57 ───────────────────────────────────────────────────────
def classify(rec: dict) -> str:
name = (rec.get('name') or '').lower()
struc = (rec.get('structure') or '').lower()
aid = (rec.get('aidType') or '').lower()
if 'buoy' in struc or 'buoy' in aid:
if any(w in name+struc for w in ['north','south','east','west','cardinal']):
return 'BOYCAR'
if any(w in name+struc for w in ['safe water','fairway','spherical']):
return 'BOYSAW'
return 'BOYLAT'
if 'beacon' in struc or 'beacon' in aid:
return 'BCNLAT'
return 'LIGHTS'
# ── Fetch ────────────────────────────────────────────────────────────────────
def fetch(lat0, lon0, lat1, lon1) -> list:
url = NGA_URL.format(lat0=lat0, lon0=lon0, lat1=lat1, lon1=lon1)
print(f"Consultando NGA MSI...\n{url}\n")
req = urllib.request.Request(url, headers=NGA_HEADERS)
try:
with urllib.request.urlopen(req, timeout=25, context=_SSL_CTX) as r:
data = json.loads(r.read().decode('utf-8'))
except urllib.error.HTTPError as e:
print(f"Error HTTP {e.code}: {e.reason}"); sys.exit(1)
except Exception as e:
print(f"Error conexion: {e}"); sys.exit(1)
recs = data.get('ngalol') or []
print(f" -> {len(recs)} registros recibidos")
return recs
# ── Campos CSV (S-57 completo) ───────────────────────────────────────────────
FIELDS = [
'lon','lat',
'OBJNAM','NOBJNM',
# Luz
'LITCHR','LITCHR_TXT',
'SIGGRP',
'COLOUR','COLOUR_TXT',
'SIGPER','SIGPER2',
'VALNMR',
'HEIGHT',
# Sectores / orientacion
'SECTR1','SECTR2',
'MLTYLT',
'ORIENT',
# Boya
'BOYSHP','CATLBR','CATCAM',
# Texto
'INFORM','TXTDSC',
# Referencia NGA
'_nga_feature','_nga_volume','_nga_region',
'_nga_char_raw','_nga_hfm_raw','_nga_range_raw','_nga_notice',
]
def write_csv(path: Path, rows: list):
with open(path, 'w', newline='', encoding='utf-8-sig') as f:
w = csv.DictWriter(f, fieldnames=FIELDS, extrasaction='ignore')
w.writeheader()
w.writerows(rows)
print(f" OK {path.name} ({len(rows)} registros)")
# ── Main ─────────────────────────────────────────────────────────────────────
def main():
lat0, lon0, lat1, lon1 = DEFAULT_LAT0, DEFAULT_LON0, DEFAULT_LAT1, DEFAULT_LON1
args = sys.argv[1:]
for i, a in enumerate(args):
if a=='--lat0' and i+1<len(args): lat0=float(args[i+1])
if a=='--lat1' and i+1<len(args): lat1=float(args[i+1])
if a=='--lon0' and i+1<len(args): lon0=float(args[i+1])
if a=='--lon1' and i+1<len(args): lon1=float(args[i+1])
records = fetch(lat0, lon0, lat1, lon1)
buckets = {
'LIGHTS':[], 'BOYLAT':[], 'BOYCAR':[],
'BOYSAW':[], 'BCNLAT':[], 'OTHER':[],
}
for rec in records:
lat, lon = parse_pos(rec.get('position',''))
if lat is None: continue
char_raw = rec.get('characteristic','')
remarks = rec.get('remarks','') or ''
name_raw = (rec.get('name') or '').strip().lstrip('-').strip().rstrip('.')
cp = parse_char(char_raw, remarks, name_raw)
height = parse_height(rec.get('heightFeetMeters',''))
valnmr = (rec.get('range') or '').strip()
s57t = classify(rec)
col_txt = ','.join(
COLOUR_TXT_MAP.get(c.strip(), c.strip())
for c in cp['COLOUR'].split(',') if c.strip()
)
row = {
'lon': lon,
'lat': lat,
'OBJNAM': name_raw,
'NOBJNM': '',
'LITCHR': cp['LITCHR'],
'LITCHR_TXT': char_raw.split('\n')[0].strip(),
'SIGGRP': cp['SIGGRP'],
'COLOUR': cp['COLOUR'],
'COLOUR_TXT': col_txt,
'SIGPER': cp['SIGPER'],
'SIGPER2': cp['SIGPER2'],
'VALNMR': valnmr,
'HEIGHT': height,
'SECTR1': cp['SECTR1'],
'SECTR2': cp['SECTR2'],
'MLTYLT': cp['MLTYLT'],
'ORIENT': cp['ORIENT'],
'BOYSHP': '',
'CATLBR': '',
'CATCAM': '',
'INFORM': remarks.replace('\n',' '),
'TXTDSC': (rec.get('structure') or '').replace('\n',' ').strip(),
'_nga_feature': (rec.get('featureNumber') or '').replace('\n',' '),
'_nga_volume': rec.get('volumeNumber',''),
'_nga_region': (rec.get('regionHeading') or ''),
'_nga_char_raw': char_raw.replace('\n',' | '),
'_nga_hfm_raw': (rec.get('heightFeetMeters') or '').replace('\n','ft/'),
'_nga_range_raw': valnmr,
'_nga_notice': str(rec.get('noticeNumber','')),
}
# Clasificacion detallada boyas
struc_low = (rec.get('structure') or '').lower()
if s57t == 'BOYLAT':
if any(w in struc_low for w in ['red','port','can']):
row.update({'CATLBR':'1','COLOUR':'3','COLOUR_TXT':'R','BOYSHP':'2'})
elif any(w in struc_low for w in ['green','starboard','conical']):
row.update({'CATLBR':'2','COLOUR':'4','COLOUR_TXT':'G','BOYSHP':'1'})
elif s57t == 'BOYCAR':
nm = name_raw.lower()
if 'north' in nm: row['CATCAM']='1'
elif 'east' in nm: row['CATCAM']='2'
elif 'south' in nm: row['CATCAM']='3'
elif 'west' in nm: row['CATCAM']='4'
row.update({'COLOUR':'6,1','COLOUR_TXT':'Y,W'})
elif s57t == 'BOYSAW':
row.update({'COLOUR':'1,3','COLOUR_TXT':'W,R','BOYSHP':'3'})
buckets[s57t if s57t in buckets else 'OTHER'].append(row)
# Resumen
print("\nResumen por tipo S-57:")
for k,v in buckets.items():
if v: print(f" {k:<10} {len(v):>3} registros")
# Escribir CSVs
print(f"\nEscribiendo CSVs en: {OUT_DIR}\n")
ts = datetime.now().strftime('%Y%m%d')
for k, rows in buckets.items():
if rows:
write_csv(OUT_DIR / f'nga_{k}_{ts}.csv', rows)
# Preview tabla completa
print(f"\n{'OBJNAM':<45} {'CHAR':<15} {'COL':<8} {'PER':>5} {'RNG':>4} {'HGT':>4} {'ORI':>6} {'SEC':}")
print('-'*115)
for r in buckets['LIGHTS']:
sec = f"{r['SECTR1']}-{r['SECTR2']}" if r['SECTR1'] else ''
print(f"{r['OBJNAM'][:44]:<45} {r['LITCHR_TXT'][:14]:<15} "
f"{r['COLOUR_TXT']:<8} {r['SIGPER']:>5}s {r['VALNMR']:>4}NM "
f"{r['HEIGHT']:>4}m {r['ORIENT']:>6} {sec}")
print("\nListo. En QGIS: Capa -> Anadir capa -> Texto delimitado")
print("X=lon Y=lat CRS=EPSG:4326 Renombre capa con codigo S-57")
print("ATENCION: ORIENT de enfilaciones son aproximados.")
print("Verificar contra DIMAR Lista de Faros antes de publicar carta.")
if __name__ == '__main__':
main()
+1
View File
@@ -0,0 +1 @@
015823LE1 0900201 ! 34040000123000000010470123DSID1650170DSSI1130335DSPM1300448VRID0780578ATTV0580656VRPT0760714SG2D0480790SG3D0700838FRID1000908FOID0701008ATTF0591078NATF0681137FFPT0861205FSPT09012910000;& 0001DSIDDSIDDSSI0001DSPM0001VRIDVRIDATTVVRIDVRPTVRIDSG2DVRIDSG3D0001FRIDFRIDFOIDFRIDATTFFRIDNATFFRIDFFPTFRIDFSPT0500;& ISO/IEC 8211 Record Identifier(b12)1600;& Data set identification fieldRCNM!RCID!EXPP!INTU!DSNM!EDTN!UPDN!UADT!ISDT!STED!PRSP!PSDN!PRED!PROF!AGEN!COMT(b11,b14,2b11,3A,2A(8),R(4),b11,2A,b11,b12,A)1600;& Data set structure information fieldDSTR!AALL!NALL!NOMR!NOCR!NOGR!NOLR!NOIN!NOCN!NOED!NOFA(3b11,8b14)1600;& Data set parameter fieldRCNM!RCID!HDAT!VDAT!SDAT!CSCL!DUNI!HUNI!PUNI!COUN!COMF!SOMF!COMT(b11,b14,3b11,b14,4b11,2b14,A)1600;& Vector record identifier fieldRCNM!RCID!RVER!RUIN(b11,b14,b12,b11)2600;& Vector record attribute field*ATTL!ATVL(b12,A)2600;& Vector record pointer field*NAME!ORNT!USAG!TOPI!MASK(B(40),4b11)2500;& 2-D coordinate field*YCOO!XCOO(2b24)2500;& 3-D coordinate (sounding array) field*YCOO!XCOO!VE3D(3b24)1600;& Feature record identifier fieldRCNM!RCID!PRIM!GRUP!OBJL!RVER!RUIN(b11,b14,2b11,2b12,b11)1600;& Feature object identifier fieldAGEN!FIDN!FIDS(b12,b14,b12)2600;&-A Feature record attribute field*ATTL!ATVL(b12,A)2600;&-A Feature record national attribute field*ATTL!ATVL(b12,A)2600;& Feature record to feature object pointer field*LNAM!RIND!COMT(B(64),b11,A)2600;& Feature record to spatial record pointer field*NAME!ORNT!USAG!MASK(B(40),3b11)
+275
View File
@@ -0,0 +1,275 @@
"""
parse_lista_luces.py
Genera CSV de ayudas a la navegacion de Barranquilla a partir de
Lista de Luces DIMAR 2015 (ya parseada manualmente).
"""
import csv, re, sys, os
from collections import Counter
def dms_to_dd(deg, minutes):
return float(deg) + float(minutes) / 60.0
# ---- DATA ----
# (no, name, lat_deg, lat_min, lon_deg, lon_min, char, height_m, range_nm, colour, description, feat_type, orient)
records = [
# FAROS MAYORES
(13, 'Faro F1 Recalada', 11, 6.37, 74, 50.97, 'Iso G 2s', 20, 9, 'G', 'Torre naranja bandas blancas. Faro de Recalada', 'LIGHTS', ''),
(14, 'Faro F2 Recalada', 11, 6.36, 74, 51.28, 'Iso R 2s', 23, 13.4, 'R', 'Torre naranja bandas blancas. Racon B', 'LIGHTS', ''),
(32, 'Faro Morro Hermoso', 10, 57.80, 75, 1.05, 'Fl W 4s', 134, 28, 'W', 'Torre blanca bandas rojas. Giratorio', 'LIGHTS', ''),
(33, 'Faro Galerazamba', 10, 47.12, 75, 15.96, 'Fl W 4s', 14, 11, 'W', 'Torre fibra vidrio blanca bandas rojas. Giratorio', 'LIGHTS', ''),
# FAROLES X (Tajamares Bocas de Ceniza)
# Estas son balizas laterales fijas en tierra → BCNLAT (no LIGHTS).
# IALA-B: Verde = babor (CATLAM=1), Rojo = estribor (CATLAM=2).
# La luz se extrae automáticamente de LITCHR/SIGGRP/SIGPER/COLOUR.
(15, 'Faro X1', 11, 6.13, 74, 50.97, 'Q(4)G 11s', 6, 6, 'G', 'Torre verde bandas blancas', 'BCNLAT', ''),
(16, 'Faro X2', 11, 6.00, 74, 51.20, 'Q(4)R 11s', 6, 6, 'R', 'Torre roja bandas blancas', 'BCNLAT', ''),
(18, 'Faro X3', 11, 5.48, 74, 50.83, 'Q(4)G 11s', 6, 6, 'G', 'Torre verde bandas blancas', 'BCNLAT', ''),
(17, 'Faro X4', 11, 5.58, 74, 51.10, 'Q(4)R 11s', 6, 6, 'R', 'Torre roja bandas blancas', 'BCNLAT', ''),
(19, 'Faro X5', 11, 5.35, 74, 50.80, 'Q(4)G 11s', 6, 6, 'G', 'Torre verde bandas blancas', 'BCNLAT', ''),
(20, 'Faro X6', 11, 5.26, 74, 51.03, 'Q(4)R 11s', 6, 6, 'R', 'Torre roja bandas blancas', 'BCNLAT', ''),
(21, 'Faro X7', 11, 2.49, 74, 48.86, 'Q(4)G 11s', 8, 6, 'G', 'Baliza enrejado verde bandas blancas', 'BCNLAT', ''),
(22, 'Faro X8', 11, 4.90, 74, 50.95, 'Q(4)R 11s', 6, 6, 'R', 'Torre roja bandas blancas', 'BCNLAT', ''),
(23, 'Faro X9', 11, 2.15, 74, 48.29, 'Q(4)G 11s', 8, 6, 'G', 'Baliza enrejado verde bandas blancas', 'BCNLAT', ''),
(24, 'Faro X10', 11, 4.56, 74, 50.88, 'Q(4)R 11s', 6, 6, 'R', 'Torre roja bandas blancas', 'BCNLAT', ''),
(25, 'Faro X11', 11, 1.79, 74, 47.73, 'Q(4)G 11s', 8, 6, 'G', 'Baliza enrejado verde bandas blancas', 'BCNLAT', ''),
(26, 'Faro X12', 11, 3.90, 74, 50.65, 'Q(4)R 11s', 6, 6, 'R', 'Baliza enrejado roja bandas blancas', 'BCNLAT', ''),
(27, 'Faro X13', 11, 1.55, 74, 47.38, 'Q(4)G 11s', 8, 6, 'G', 'Baliza enrejado verde bandas blancas', 'BCNLAT', ''),
(28, 'Faro X14', 11, 3.47, 74, 50.37, 'Q(4)R 11s', 6, 6, 'R', 'Torre roja bandas blancas', 'BCNLAT', ''),
(30, 'Faro X15', 11, 1.37, 74, 47.13, 'Q(4)G 11s', 6, 6, 'G', 'Baliza enrejado verde bandas blancas', 'BCNLAT', ''),
(29, 'Faro X16', 11, 3.00, 74, 49.98, 'Q(4)R 11s', 6, 6, 'R', 'Torre roja bandas blancas', 'BCNLAT', ''),
(31, 'Faro X17', 11, 1.12, 74, 46.70, 'Q(4)G 11s', 6, 6, 'G', 'Baliza enrejado verde bandas blancas', 'BCNLAT', ''),
# ENFILACIONES / RANGE LIGHTS
(196, 'Enfilacion E1', 11, 6.22, 74, 50.90, 'Iso Bu 5s', 10, 13, 'W', 'Baliza enrejado naranja y blanco. Rumbo 135.7', 'LIGHTS', '135.7'),
(197, 'Enfilacion E3', 11, 6.10, 74, 50.78, 'Iso Bu 5s', 22, 9, 'W', 'Torre enrejada naranja y blanco. Rumbo 139.3', 'LIGHTS', '139.3'),
(198, 'Enfilacion E3A', 11, 6.02, 74, 50.70, 'Iso W 5s', 20, 12.3, 'W', 'Torre naranja y blanco. Rumbo 135.7', 'LIGHTS', '135.7'),
(201, 'Enfilacion E4', 11, 4.21, 74, 50.81, 'Iso R 4s', 11, 4.5, 'R', 'Baliza enrejado naranja bandas blancas. Rumbo 142.3', 'LIGHTS', '142.3'),
(203, 'Enfilacion E6', 11, 3.78, 74, 50.62, 'Iso Bu 4s', 12, 8, 'R', 'Baliza enrejado roja bandas blancas. Rumbo 167.7', 'LIGHTS', '167.7'),
(204, 'Enfilacion E8', 11, 3.51, 74, 50.51, 'Iso Bu 4s', 25, 14.5, 'W', 'Baliza enrejado naranja bandas blancas. Rumbo 167.7', 'LIGHTS', '167.7'),
(205, 'Enfilacion E10', 11, 3.59, 74, 50.50, 'Iso G 5s', 11, 10, 'G', 'Torre naranja bandas blancas. Rumbo 167.3', 'LIGHTS', '167.3'),
(206, 'Enfilacion E12', 11, 3.37, 74, 50.44, 'Iso G 5s', 22, 8, 'G', 'Baliza tablero blanco franja roja. Rumbo 167.3', 'LIGHTS', '167.3'),
(207, 'Enfilacion E14', 11, 3.37, 74, 50.44, 'Iso Bu 6s', 22, 8, 'W', 'Tablero blanco con franja roja. Rumbo 122', 'LIGHTS', '122'),
(237, 'Enfilacion E16', 11, 3.22, 74, 50.20, 'Iso Bu 6s', 12, 9, 'W', 'Baliza enrejado naranja bandas blancas. Rumbo 122', 'LIGHTS', '122'),
(238, 'Enfilacion E18', 11, 2.58, 74, 49.53, 'Fl WRG 4s', 18, 6, 'WRG', 'Torre roja bandas blancas. Sector 9 grados. Rumbo 142', 'LIGHTS', '142'),
# BOYAS LATERALES CANAL RIO MAGDALENA
(199, 'Boya No. 1', 11, 5.07, 74, 50.01, 'Fl G 3s', 4, 6, 'G', 'Castillete verde', 'BOYLAT', ''),
(200, 'Boya No. 3', 11, 4.55, 74, 50.69, 'Fl G 3s', 4, 6, 'G', 'Castillete verde', 'BOYLAT', ''),
(202, 'Boya No. 5', 11, 3.94, 74, 50.46, 'Q G 1s', 4, 6, 'G', 'Castillete verde', 'BOYLAT', ''),
(208, 'Boya No. 7', 11, 3.60, 74, 50.25, 'Fl G 3s', 4, 6, 'G', 'Castillete verde', 'BOYLAT', ''),
(209, 'Boya No. 9', 11, 2.81, 74, 49.44, 'Fl G 3s', 4, 6, 'G', 'Castillete verde', 'BOYLAT', ''),
(210, 'Boya No. 11', 11, 2.38, 74, 48.73, 'Fl G 3s', 4, 6, 'G', 'Castillete verde', 'BOYLAT', ''),
(211, 'Boya No. 12', 11, 2.23, 74, 48.81, 'Fl R 3s', 4, 6, 'R', 'Castillete roja', 'BOYLAT', ''),
(212, 'Boya No. 13', 11, 2.06, 74, 48.12, 'Fl G 3s', 4, 6, 'G', 'Castillete verde', 'BOYLAT', ''),
(213, 'Boya No. 14', 11, 1.31, 74, 47.30, 'Fl R 3s', 3, 6, 'R', 'Castillete roja', 'BOYLAT', ''),
(214, 'Boya No. 15', 11, 1.69, 74, 47.63, 'Fl G 3s', 4, 6, 'G', 'Castillete verde', 'BOYLAT', ''),
(215, 'Boya No. 16', 11, 1.64, 74, 47.85, 'Fl R 3s', 4, 6, 'R', 'Castillete roja', 'BOYLAT', ''),
(218, 'Boya No. 18', 11, 1.31, 74, 47.30, 'Fl R 3s', 4, 6, 'R', 'Castillete roja', 'BOYLAT', ''),
(219, 'Boya No. 19', 11, 1.05, 74, 46.59, 'Fl G 3s', 4, 6, 'G', 'Castillete verde', 'BOYLAT', ''),
(220, 'Boya No. 20', 11, 0.93, 74, 46.64, 'Fl R 3s', 4, 6, 'R', 'Castillete roja', 'BOYLAT', ''),
(221, 'Boya No. 21', 11, 0.84, 74, 46.32, 'Fl G 3s', 4, 6, 'G', 'Castillete verde', 'BOYLAT', ''),
(222, 'Boya No. 22', 11, 0.74, 74, 46.40, 'Q R 1s', 4, 6, 'R', 'Castillete roja', 'BOYLAT', ''),
(223, 'Boya No. 23', 10, 58.50, 74, 45.30, 'Fl G 1.3s', 3, 6, 'G', 'Castillete verde', 'BOYLAT', ''),
(224, 'Boya No. 24', 11, 0.55, 74, 46.23, 'Fl R 3s', 4, 6, 'R', 'Castillete roja', 'BOYLAT', ''),
(225, 'Boya No. 25', 11, 0.40, 74, 45.98, 'Fl G 3s', 4, 6, 'G', 'Castillete verde', 'BOYLAT', ''),
(226, 'Boya No. 26', 11, 0.35, 74, 46.10, 'Fl R 3s', 4, 6, 'R', 'Castillete roja', 'BOYLAT', ''),
(227, 'Boya No. 27', 10, 59.91, 74, 45.72, 'Fl G 3s', 4, 6, 'G', 'Castillete verde', 'BOYLAT', ''),
(228, 'Boya No. 28', 10, 59.99, 74, 45.92, 'Fl R 3s', 4, 6, 'R', 'Castillete roja', 'BOYLAT', ''),
(229, 'Boya No. 29', 10, 59.24, 74, 45.48, 'Fl G 3s', 4, 6, 'G', 'Castillete verde', 'BOYLAT', ''),
(230, 'Boya No. 30', 10, 59.24, 74, 45.64, 'Fl R 3s', 4, 6, 'R', 'Castillete roja', 'BOYLAT', ''),
(231, 'Boya No. 31', 10, 58.50, 74, 45.29, 'Fl G 3s', 4, 6, 'G', 'Castillete verde', 'BOYLAT', ''),
(232, 'Boya No. 33', 10, 57.56, 74, 45.34, 'Fl G 3s', 4, 6, 'G', 'Castillete verde', 'BOYLAT', ''),
(233, 'Boya No. 35', 10, 56.56, 74, 45.25, 'Fl G 3s', 4, 6, 'G', 'Castillete verde', 'BOYLAT', ''),
(234, 'Boya No. 36', 10, 56.49, 74, 45.40, 'Fl R 3s', 4, 6, 'R', 'Castillete roja', 'BOYLAT', ''),
(235, 'Boya Cardinal Norte', 10, 57.55, 74, 45.21, 'Fl W 1s', 4, 6, 'W', 'Castillete cardinal N negros', 'BOYCAR', ''),
(236, 'Boya Cardinal Sur', 10, 57.55, 74, 45.21, 'Fl W 15s', 4, 6, 'W', 'Castillete cardinal S negros', 'BOYCAR', ''),
(239, 'Boya de Oleaje', 11, 8.04, 74, 45.48, 'Fl Y 20s', 0.5, 4.5, 'Y', 'Esferica amarilla. Recolectora datos oceanograficos', 'BOYSPEC', ''),
(240, 'Boya Peligro Aislado', 10, 57.27, 74, 45.44, 'Fl(2) W 4s', 3.3, 3, 'W', 'Castillete roja bandas negras. Bajo rocoso', 'BOYISD', ''),
]
# ════════════════════════════════════════════════════════════════════════════
# IHO S-57 Ed.3.1 — códigos OFICIALES (no inventados)
# Ref: s57attributes.csv distribuido con GDAL/OGR
# ════════════════════════════════════════════════════════════════════════════
# COLOUR codes (ATTL 75)
colour_s57 = {
'W': '1', # White
'K': '2', # Black
'R': '3', # Red
'G': '4', # Green
'B': '5', # Blue
'Y': '6', # Yellow
'Gy': '7', # Grey
'Br': '8', # Brown
'Amb': '9', # Amber
'Vi': '10', # Violet
'Or': '11', # Orange
'Mg': '12', # Magenta
'WRG': '1,3,4', # White/Red/Green (multi-colour sectors)
'': '1', # default white
}
colour_name = {
'W': 'white', 'K': 'black', 'R': 'red', 'G': 'green',
'B': 'blue', 'Y': 'yellow', 'Gy': 'grey', 'Br': 'brown',
'Amb': 'amber','Vi': 'violet','Or': 'orange', 'Mg': 'magenta',
'WRG': 'white/red/green', '': 'white',
}
# LITCHR codes (ATTL 107) — S-57 Ed.3.1 official values
char_map = {
'F': '1', # Fixed
'Fl': '2', # Flashing
'LFl': '3', # Long flashing
'Q': '4', # Quick (50-60/min)
'VQ': '5', # Very quick (100-120/min)
'UQ': '6', # Ultra quick (≥160/min)
'Iso': '7', # Isophase
'Oc': '8', # Occulting
'IQ': '9', # Interrupted quick
'IVQ': '10', # Interrupted very quick
'IUQ': '11', # Interrupted ultra quick
'Mo': '12', # Morse code
'FFl': '13', # Fixed and flashing
'Fl+LFl': '14',
'Oc+Fl': '15',
'Al.Oc': '25',
'Al.LFl': '26',
'Al.Fl': '27',
'Al.Grp': '28',
}
# BOYSHP codes (ATTL 4)
# 1=Conical 2=Can 3=Sphere 4=Pillar 5=Spar 6=Barrel 7=Super-buoy 8=Ice buoy
# Barranquilla buoys are "castillete" → pillar (4)
BOYSHP_CASTILLETE = '4'
# BCNSHP codes (ATTL 2)
# 1=Stake 2=Withy 3=Tower 4=Lattice 5=Pile 6=Cairn 7=Buoyant beacon
# "Torre" → 3, "Baliza enrejado" / "celosía" → 4
def bcnshp_from_desc(desc):
d = desc.lower()
if 'enrejad' in d or 'celosí' in d or 'lattice' in d:
return '4' # Lattice tower
return '3' # Solid tower (default)
# CATLAM codes (ATTL 36) for IALA-B (Américas):
# Green marks = port side (left hand entering) → CATLAM=1
# Red marks = starboard side (right hand entering) → CATLAM=2
def catlam_from_colour(col_letter):
return {'G': '1', 'R': '2'}.get(col_letter, '')
# CATCAM codes (ATTL 13) for cardinal marks:
# 1=N 2=E 3=S 4=W
def catcam_from_name(name):
nl = name.lower()
if 'norte' in nl or 'north' in nl: return '1'
if 'este' in nl or 'east' in nl: return '2'
if 'sur' in nl or 'south' in nl: return '3'
if 'oeste' in nl or 'west' in nl: return '4'
return ''
def parse_char(char_str):
"""Parse human-readable light character string → (litchr_code, siggrp, sigper).
Examples: 'Q(4)G 11s' → ('4','4','11')
'Fl W 4s' → ('2','','4')
'Iso Bu 5s' → ('7','','5')
"""
# Extract base character (longest prefix match, try longest first)
base = re.split(r'[\s\(\.]', char_str)[0]
# Try compound chars first (Al.Fl, Fl+LFl …)
litchr = None
for key in sorted(char_map, key=len, reverse=True):
if char_str.startswith(key):
litchr = char_map[key]
break
if litchr is None:
litchr = char_map.get(base, '2') # default Fl if unknown
mg = re.search(r'\((\d+)\)', char_str)
siggrp = mg.group(1) if mg else ''
mp = re.search(r'([\d.]+)s', char_str)
sigper = mp.group(1) if mp else ''
return litchr, siggrp, sigper
# ────────────────────────────────────────────────────────────────────────────
# Campos del CSV de salida — usan nombres de atributos S-57 directamente
# para que el converter los recoja sin ningún mapeo adicional.
# ────────────────────────────────────────────────────────────────────────────
fields = ['no_dimar', 'OBJNAM', 'lon', 'lat', 'feat_type',
'LITCHR', 'LITCHR_TXT', 'SIGGRP', 'SIGPER',
'COLOUR', 'COLOUR_TXT', 'COLPAT',
'VALNMR', 'HEIGHT', 'ORIENT',
'CATLAM', 'CATCAM', 'BOYSHP', 'BCNSHP', 'TOPSHP',
'INFORM', '_dimar_char_raw', '_source']
out_path = r'D:\Proyectos Software\QGISS57Converter\dimar_ayudas_barranquilla.csv'
with open(out_path, 'w', newline='', encoding='utf-8') as f:
w = csv.DictWriter(f, fieldnames=fields)
w.writeheader()
for rec in records:
no, name, lat_d, lat_m, lon_d, lon_m, char, ht, rng, col, desc, ftype, orient = rec
lat = dms_to_dd(lat_d, lat_m)
lon = -dms_to_dd(lon_d, lon_m) # West = negative
litchr, siggrp, sigper = parse_char(char)
catlam = ''
catcam = ''
boyshp = ''
bcnshp = ''
topshp = ''
colpat = ''
if ftype == 'BOYLAT':
# Castillete lateral: pillar (4), with CATLAM
boyshp = BOYSHP_CASTILLETE
catlam = catlam_from_colour(col)
elif ftype == 'BCNLAT':
# Faros de orilla (X marks): shore fixed beacon with CATLAM
bcnshp = bcnshp_from_desc(desc)
catlam = catlam_from_colour(col)
elif ftype in ('BOYCAR', 'BCNCAR'):
boyshp = '4' # pillar for cardinal buoys
catcam = catcam_from_name(name)
colpat = '1' # horizontal bands (standard cardinal pattern)
elif ftype == 'BOYISD':
boyshp = '4'
colpat = '1' # horizontal bands (black over red)
elif ftype == 'BOYSAW':
boyshp = '1' # conical or spherical for safe water
colpat = '4' # vertical stripes (white/red)
elif ftype in ('BOYSPEC', 'BOYSPP'):
boyshp = '3' # sphere for special/data buoys
ftype = 'BOYSPP' # normalise to S-57 acronym
w.writerow({
'no_dimar': no,
'OBJNAM': name,
'lon': f'{lon:.6f}',
'lat': f'{lat:.6f}',
'feat_type': ftype,
'LITCHR': litchr,
'LITCHR_TXT': char.split()[0],
'SIGGRP': siggrp,
'SIGPER': sigper,
'COLOUR': colour_s57.get(col, '1'),
'COLOUR_TXT': colour_name.get(col, 'white'),
'COLPAT': colpat,
'VALNMR': rng,
'HEIGHT': ht,
'ORIENT': orient,
'CATLAM': catlam,
'CATCAM': catcam,
'BOYSHP': boyshp,
'BCNSHP': bcnshp,
'TOPSHP': topshp,
'INFORM': desc,
'_dimar_char_raw': char,
'_source': 'DIMAR Lista de Luces 2015',
})
print(f'Saved {len(records)} records -> {out_path}')
types = Counter(r[11] for r in records)
for t, c in sorted(types.items()):
print(f' {t}: {c}')
+65
View File
@@ -0,0 +1,65 @@
"""
PoC: produce a minimal valid S-57 .000 for the Wabasso test area and verify it
opens cleanly with fiona (i.e., GDAL's S-57 driver).
Expected result: fiona.listlayers() shows M_COVR + BOYLAT (and no warnings about
broken bounds), and bounds match the envelope we encoded.
"""
from pathlib import Path
import sys
sys.path.insert(0, str(Path(__file__).parent))
from s57_writer import (
S57Cell, OBJL_M_COVR, OBJL_BOYLAT,
ATTL_CATCOV, ATTL_CATLAM,
)
# Wabasso area envelope (lon, lat):
W, S, E, N = -80.4588, 27.7519, -80.4574, 27.7609
OUTPUT = Path(__file__).parent / "test_wabasso.000"
cell = S57Cell(
dsnm="TESTPOC.000",
edition=1,
intu=5, # Harbour
scale=10000,
agen=999,
comt="QGISS57Converter PoC",
issue_date="20260428",
)
# 1) Mandatory M_COVR feature: rectangular envelope, CATCOV=1 (data covered)
cell.add_area_feature(
objl=OBJL_M_COVR,
ring=[(W, S), (E, S), (E, N), (W, N), (W, S)],
attrs=[(ATTL_CATCOV, "1")], # CATCOV=1 (coverage present)
)
# 2) One BOYLAT (lateral buoy, port hand IALA-B = CATLAM=1) at the centre
cx = (W + E) / 2
cy = (S + N) / 2
cell.add_point_feature(
objl=OBJL_BOYLAT,
lon=cx, lat=cy,
attrs=[(ATTL_CATLAM, "1")], # CATLAM=1 (port-hand lateral mark)
)
cell.write(OUTPUT)
print(f"Wrote {OUTPUT} ({OUTPUT.stat().st_size} bytes)")
# ── Verify with fiona ────────────────────────────────────────────────────────
print()
print("Verifying with fiona...")
try:
import fiona
layers = fiona.listlayers(str(OUTPUT))
print(f" fiona.listlayers: {layers}")
for L in layers:
try:
with fiona.open(str(OUTPUT), layer=L) as src:
print(f" {L:10s} bounds={src.bounds} features={len(src)}")
except Exception as e:
print(f" {L}: open ERROR: {e}")
except Exception as e:
print(f" fiona import or listlayers failed: {e}")
+117
View File
@@ -0,0 +1,117 @@
{
"_comment": "S-57 object class catalog — IHO S-57 Ed 3.1. Key: S-57 acronym. geom: A=Area, L=Line, P=Point, C=Collection",
"COALNE": { "desc": "Coastline", "geom": ["L"], "attrs": [] },
"LNDARE": { "desc": "Land Area", "geom": ["A"], "attrs": [] },
"DEPARE": { "desc": "Depth Area", "geom": ["A"], "attrs": ["DRVAL1","DRVAL2"] },
"DEPCNT": { "desc": "Depth Contour", "geom": ["L"], "attrs": ["VALDCO"] },
"SOUNDG": { "desc": "Sounding", "geom": ["P"], "attrs": ["VALSOU","QUASOU","SOUACC"] },
"DRGARE": { "desc": "Dredged Area", "geom": ["A"], "attrs": ["DRVAL1","DRVAL2"] },
"SBDARE": { "desc": "Seabed Area", "geom": ["A","P"], "attrs": ["NATSUR","NATQUA"] },
"OBSTRN": { "desc": "Obstruction", "geom": ["A","L","P"], "attrs": ["VALSOU","CATOBS"] },
"WRECKS": { "desc": "Wreck", "geom": ["A","P"], "attrs": ["VALSOU","CATWRK"] },
"UWTROC": { "desc": "Underwater / Awash Rock","geom": ["P"], "attrs": ["VALSOU","WATLEV"] },
"LIGHTS": { "desc": "Light", "geom": ["P"], "attrs": ["LITCHR","SIGPER","HEIGHT","VALNMR","COLOUR","ORIENT","SIGGRP","SECTR1","SECTR2"] },
"LNDMRK": { "desc": "Landmark", "geom": ["A","P"], "attrs": ["CATLMK","HEIGHT"] },
"BOYLAT": { "desc": "Lateral Buoy", "geom": ["P"], "attrs": ["CATLAM","COLOUR","COLPAT","LITCHR","SIGPER","SIGGRP","VALNMR"] },
"BOYCAR": { "desc": "Cardinal Buoy", "geom": ["P"], "attrs": ["CATCAM","COLOUR","LITCHR","SIGPER","VALNMR"] },
"BOYISD": { "desc": "Isolated Danger Buoy", "geom": ["P"], "attrs": ["COLOUR","LITCHR","SIGPER","SIGGRP","VALNMR"] },
"BOYSPP": { "desc": "Special Purpose Buoy", "geom": ["P"], "attrs": ["CATSPM","COLOUR","LITCHR","SIGPER","VALNMR"] },
"BOYSPEC":{ "desc": "Special Purpose Buoy (alias→BOYSPP)", "geom": ["P"], "attrs": ["CATSPM","COLOUR","LITCHR","SIGPER","VALNMR"] },
"BOYSAW": { "desc": "Safe Water Buoy", "geom": ["P"], "attrs": ["COLOUR","LITCHR","SIGPER","VALNMR"] },
"BCNLAT": { "desc": "Lateral Beacon", "geom": ["P"], "attrs": ["CATLAM","COLOUR"] },
"BCNCAR": { "desc": "Cardinal Beacon", "geom": ["P"], "attrs": ["CATCAM","COLOUR"] },
"BCNISD": { "desc": "Isolated Danger Beacon", "geom": ["P"], "attrs": ["COLOUR"] },
"BCNSAW": { "desc": "Safe Water Beacon", "geom": ["P"], "attrs": ["COLOUR"] },
"BCNSPP": { "desc": "Special Purpose Beacon", "geom": ["P"], "attrs": ["CATSPM","COLOUR"] },
"LITFLT": { "desc": "Light Float", "geom": ["P"], "attrs": ["LITCHR","SIGPER","COLOUR"] },
"LITVES": { "desc": "Light Vessel", "geom": ["P"], "attrs": ["LITCHR","SIGPER"] },
"TOPMAR": { "desc": "Topmark", "geom": ["P"], "attrs": ["TOPSHP","COLOUR"] },
"DAYMAR": { "desc": "Daymark", "geom": ["P"], "attrs": ["CATLAM","COLOUR"] },
"FOGSIG": { "desc": "Fog Signal", "geom": ["P"], "attrs": ["CATFOG","SIGPER"] },
"NAVLNE": { "desc": "Navigation Line", "geom": ["L"], "attrs": ["CATNAV"] },
"LDLINE": { "desc": "Leading Line", "geom": ["L"], "attrs": [] },
"TSSLPT": { "desc": "Traffic Separation Lane Pt", "geom": ["A"], "attrs": [] },
"TSSRON": { "desc": "Traffic Separation Roundabout", "geom": ["A"], "attrs": [] },
"TSSBND": { "desc": "Traffic Separation Boundary", "geom": ["L"], "attrs": [] },
"TSSCRS": { "desc": "Traffic Separation Crossing", "geom": ["A"], "attrs": [] },
"TSELNE": { "desc": "Traffic Separation Line", "geom": ["L"], "attrs": [] },
"DWRTCL": { "desc": "Deep Water Route Centre Line", "geom": ["L"], "attrs": [] },
"DWRTPT": { "desc": "Deep Water Route Part", "geom": ["A"], "attrs": [] },
"TWRTPT": { "desc": "Two-way Route Part", "geom": ["A"], "attrs": [] },
"SUBTLN": { "desc": "Submarine Transit Lane","geom": ["A"], "attrs": [] },
"ISTZNE": { "desc": "Inshore Traffic Zone", "geom": ["A"], "attrs": [] },
"RECTRC": { "desc": "Recommended Track", "geom": ["L"], "attrs": ["CATTRK"] },
"FERYRT": { "desc": "Ferry Route", "geom": ["A","L"], "attrs": ["CATFRY"] },
"FAIRWY": { "desc": "Fairway", "geom": ["A"], "attrs": ["CATFWY"] },
"ACHARE": { "desc": "Anchorage Area", "geom": ["A","P"], "attrs": ["CATACH"] },
"ACHBRT": { "desc": "Anchor Berth", "geom": ["A","P"], "attrs": ["CATACH"] },
"BERTHS": { "desc": "Berth", "geom": ["A","L","P"], "attrs": ["QUAPOS"] },
"HRBARE": { "desc": "Harbour Area", "geom": ["A"], "attrs": ["CATHAF"] },
"HRBFAC": { "desc": "Harbour Facility", "geom": ["A","P"], "attrs": ["CATHAF"] },
"PILBOP": { "desc": "Pilot Boarding Place", "geom": ["A","P"], "attrs": ["CATPIL"] },
"MORFAC": { "desc": "Mooring / Warping Facility", "geom": ["A","L","P"], "attrs": ["CATMOR"] },
"DOCARE": { "desc": "Dock Area", "geom": ["A"], "attrs": [] },
"DRYDOC": { "desc": "Dry Dock", "geom": ["A"], "attrs": [] },
"HULKES": { "desc": "Hulk", "geom": ["A","P"], "attrs": ["CATHLK"] },
"CRANES": { "desc": "Crane", "geom": ["A","P"], "attrs": ["CATCRN","HEIGHT"] },
"PILPNT": { "desc": "Pile / Bollard", "geom": ["L","P"], "attrs": ["CATPLE"] },
"PONTON": { "desc": "Pontoon", "geom": ["A","L","P"], "attrs": [] },
"CBLSUB": { "desc": "Submarine Cable", "geom": ["A","L"], "attrs": ["CATCBL"] },
"CBLOHD": { "desc": "Overhead Cable", "geom": ["L"], "attrs": ["CATCBL","VERCLR"] },
"PIPOHD": { "desc": "Overhead Pipe", "geom": ["L"], "attrs": ["CATPIP","VERCLR"] },
"PIPSOL": { "desc": "Pipeline on Land", "geom": ["A","L"], "attrs": ["CATPIP"] },
"BRIDGE": { "desc": "Bridge", "geom": ["A","L","P"], "attrs": ["VERCLR","HORACC"] },
"TUNNEL": { "desc": "Tunnel", "geom": ["A","L"], "attrs": ["CATTNL"] },
"GATCON": { "desc": "Gate / Sluice", "geom": ["A","L","P"], "attrs": ["CATGAT"] },
"LOKBSN": { "desc": "Lock Basin", "geom": ["A"], "attrs": [] },
"DMPGRD": { "desc": "Dumping Ground", "geom": ["A"], "attrs": ["CATDPG"] },
"SWPARE": { "desc": "Swept Area", "geom": ["A"], "attrs": ["DRVAL1"] },
"OSPARE": { "desc": "Offshore Production Area","geom": ["A"], "attrs": [] },
"OFSPLF": { "desc": "Offshore Platform", "geom": ["A","P"], "attrs": ["CATOFP"] },
"MARCUL": { "desc": "Marine Farm / Culture", "geom": ["A","P"], "attrs": ["CATMAC"] },
"PIPARE": { "desc": "Pipeline Area", "geom": ["A"], "attrs": ["CATPIP"] },
"RIVERS": { "desc": "River", "geom": ["A","L"], "attrs": [] },
"RIVBNK": { "desc": "River Bank", "geom": ["L"], "attrs": [] },
"TIDEWY": { "desc": "Tideway", "geom": ["A"], "attrs": [] },
"WATTUR": { "desc": "Water Turbulence", "geom": ["P"], "attrs": [] },
"BUISGL": { "desc": "Building, Single", "geom": ["A","P"], "attrs": [] },
"BUAARE": { "desc": "Built-up Area", "geom": ["A"], "attrs": [] },
"LAKARE": { "desc": "Lake", "geom": ["A"], "attrs": [] },
"CANALS": { "desc": "Canal", "geom": ["A","L"], "attrs": [] },
"ROADWY": { "desc": "Road", "geom": ["A","L"], "attrs": ["CATROD"] },
"RUNWAY": { "desc": "Runway", "geom": ["A","L"], "attrs": [] },
"SLOTOP": { "desc": "Slope Topline", "geom": ["L"], "attrs": [] },
"SLOGRD": { "desc": "Slope", "geom": ["A"], "attrs": [] },
"VEGATN": { "desc": "Vegetation", "geom": ["A","P"], "attrs": ["CATVEG"] },
"WEDKLP": { "desc": "Weed / Kelp", "geom": ["A","P"], "attrs": ["CATWED"] },
"LNDRGN": { "desc": "Land Region", "geom": ["A"], "attrs": ["CATLND"] },
"SEAARE": { "desc": "Sea Area / Named Water","geom": ["A","P"], "attrs": ["CATSEA"] },
"RESARE": { "desc": "Restricted Area", "geom": ["A"], "attrs": ["CATREA"] },
"PRCARE": { "desc": "Precautionary Area", "geom": ["A"], "attrs": [] },
"SPLARE": { "desc": "Special Purpose Area", "geom": ["A"], "attrs": ["CATSPM"] },
"MIPARE": { "desc": "Military Practice Area","geom": ["A"], "attrs": [] },
"CTSARE": { "desc": "Cargo Transhipment Area","geom": ["A"], "attrs": [] },
"FSHZNE": { "desc": "Fishery Zone", "geom": ["A"], "attrs": [] },
"FRPARE": { "desc": "Free Port Area", "geom": ["A"], "attrs": [] },
"TESARE": { "desc": "Territorial Sea Area", "geom": ["A"], "attrs": [] },
"EXEZNE": { "desc": "Exclusive Economic Zone","geom": ["A"], "attrs": [] },
"ISTZNE": { "desc": "ISPS Zone", "geom": ["A"], "attrs": [] },
"ADDMRN": { "desc": "Admiralty Note", "geom": ["A","L","P"], "attrs": [] },
"RDOCAL": { "desc": "Radio Calling-In Point","geom": ["L","P"], "attrs": [] },
"RDOSTA": { "desc": "Radio Station", "geom": ["P"], "attrs": ["CATROS"] },
"RADRFL": { "desc": "Radar Reflector", "geom": ["P"], "attrs": [] },
"RADSTA": { "desc": "Radar Station", "geom": ["P"], "attrs": ["CATRAS"] },
"RETRFL": { "desc": "Retro-reflector", "geom": ["P"], "attrs": [] },
"MAGVAR": { "desc": "Magnetic Variation", "geom": ["P"], "attrs": ["VALMAG","VALACM"] },
"MONUMT": { "desc": "Monument", "geom": ["P"], "attrs": ["CATLMK","HEIGHT"] },
"TIDALP": { "desc": "Tidal Stream Panel", "geom": ["P"], "attrs": [] },
"STSLNE": { "desc": "Straight Territorial Sea Baseline", "geom": ["L"], "attrs": [] },
"M_COVR": { "desc": "Coverage", "geom": ["A"], "attrs": ["CATCOV"] },
"M_NSYS": { "desc": "Navigation System of Marks", "geom": ["A"], "attrs": ["MARSYS"] },
"M_QUAL": { "desc": "Quality of Data", "geom": ["A"], "attrs": ["CATZOC"] },
"M_ACCY": { "desc": "Accuracy of Data", "geom": ["A"], "attrs": [] },
"M_CSCL": { "desc": "Compilation Scale", "geom": ["A"], "attrs": ["CSCALE"] },
"M_SDAT": { "desc": "Sounding Datum", "geom": ["A"], "attrs": ["VERDAT"] },
"M_HDAT": { "desc": "Horizontal Datum", "geom": ["A"], "attrs": ["HORDAT"] },
"M_UNIT": { "desc": "Units of Measurement", "geom": ["A"], "attrs": [] }
}
+674
View File
@@ -0,0 +1,674 @@
"""
S-57 ENC writer — produces structurally valid `.000` files that the GDAL
S-57 driver can read. Reuses a NOAA DDR template verbatim (the schema is fixed
by IHO S-57 ed. 3.1) and only encodes data records.
ISO 8211 record format (data records, leader entry-map "3404"):
Leader (24 bytes) + Directory (11 bytes per entry + FT) + Field area.
Directory entry layout: tag(4) + length(3) + position(4).
"""
from __future__ import annotations
import struct
from pathlib import Path
# ── ISO 8211 control characters ────────────────────────────────────────────────
FT = b"\x1e" # Field terminator
UT = b"\x1f" # Unit terminator (subfield delimiter)
DDR_TEMPLATE = Path(__file__).parent / "noaa_ddr_template.bin"
# S-57 record names (RCNM)
RCNM_VI = 110 # Isolated Node (point primitive)
RCNM_VC = 120 # Connected Node
RCNM_VE = 130 # Edge (line primitive)
RCNM_VF = 140 # Face
RCNM_FE = 100 # Feature
# Object class codes — from s57objectclasses.csv (IHO S-57 Ed.3.1, Google Earth GDAL copy)
# fmt: off
OBJL_ACHBRT = 3 # Anchor berth
OBJL_ACHARE = 4 # Anchorage area
OBJL_BCNCAR = 5 # Beacon, cardinal
OBJL_BCNISD = 6 # Beacon, isolated danger
OBJL_BCNLAT = 7 # Beacon, lateral
OBJL_BCNSAW = 8 # Beacon, safe water
OBJL_BCNSPP = 9 # Beacon, special purpose/general
OBJL_BERTHS = 10 # Berth
OBJL_BRIDGE = 11 # Bridge
OBJL_BUISGL = 12 # Building, single
OBJL_BUAARE = 13 # Built-up area
OBJL_BOYCAR = 14 # Buoy, cardinal
OBJL_BOYISD = 16 # Buoy, isolated danger
OBJL_BOYLAT = 17 # Buoy, lateral
OBJL_BOYSAW = 18 # Buoy, safe water ← was 19 (BUG fixed)
OBJL_BOYSPP = 19 # Buoy, special purpose/general (S-57 std acronym)
OBJL_CBLOHD = 21 # Cable, overhead
OBJL_CBLSUB = 22 # Cable, submarine
OBJL_CANALS = 23 # Canal
OBJL_CTSARE = 25 # Cargo transshipment area
OBJL_COALNE = 30 # Coastline
OBJL_CRANES = 35 # Crane
OBJL_DAYMAR = 39 # Daymark
OBJL_DWRTCL = 40 # Deep water route centre line
OBJL_DWRTPT = 41 # Deep water route part
OBJL_DEPARE = 42 # Depth area
OBJL_DEPCNT = 43 # Depth contour
OBJL_DOCARE = 45 # Dock area
OBJL_DRGARE = 46 # Dredged area
OBJL_DRYDOC = 47 # Dry dock
OBJL_DMPGRD = 48 # Dumping ground
OBJL_EXEZNE = 50 # Exclusive Economic Zone
OBJL_FAIRWY = 51 # Fairway
OBJL_FERYRT = 53 # Ferry route
OBJL_FSHZNE = 54 # Fishery zone
OBJL_FOGSIG = 58 # Fog signal
OBJL_FRPARE = 60 # Free port area
OBJL_GATCON = 61 # Gate / sluice
OBJL_HRBARE = 63 # Harbour area (administrative)
OBJL_HRBFAC = 64 # Harbour facility
OBJL_HULKES = 65 # Hulk
OBJL_ISTZNE = 68 # Inshore traffic zone
OBJL_LAKARE = 69 # Lake
OBJL_LNDARE = 71 # Land area
OBJL_LNDRGN = 73 # Land region
OBJL_LNDMRK = 74 # Landmark
OBJL_LIGHTS = 75 # Light
OBJL_LITFLT = 76 # Light float
OBJL_LITVES = 77 # Light vessel
OBJL_LOKBSN = 79 # Lock basin
OBJL_MAGVAR = 81 # Magnetic variation
OBJL_MARCUL = 82 # Marine farm/culture
OBJL_MIPARE = 83 # Military practice area
OBJL_MORFAC = 84 # Mooring/warping facility
OBJL_NAVLNE = 85 # Navigation line
OBJL_OBSTRN = 86 # Obstruction
OBJL_OFSPLF = 87 # Offshore platform
OBJL_OSPARE = 88 # Offshore production area
OBJL_PILPNT = 90 # Pile
OBJL_PILBOP = 91 # Pilot boarding place
OBJL_PIPARE = 92 # Pipeline area
OBJL_PIPOHD = 93 # Pipeline, overhead
OBJL_PIPSOL = 94 # Pipeline, submarine/on land
OBJL_PONTON = 95 # Pontoon
OBJL_PRCARE = 96 # Precautionary area
OBJL_PYLONS = 98 # Pylon/bridge support
OBJL_RADRFL = 101 # Radar reflector
OBJL_RADSTA = 102 # Radar station
OBJL_RDOCAL = 104 # Radio calling-in point
OBJL_RDOSTA = 105 # Radio station
OBJL_RECTRC = 109 # Recommended track
OBJL_RESARE = 112 # Restricted area
OBJL_RETRFL = 113 # Retro-reflector
OBJL_RIVERS = 114 # River
OBJL_RIVBNK = 115 # River bank
OBJL_ROADWY = 116 # Road
OBJL_RUNWAY = 117 # Runway
OBJL_SEAARE = 119 # Sea area / named water area
OBJL_SPLARE = 120 # Sea-plane landing area (SPLARE)
OBJL_SBDARE = 121 # Seabed area
OBJL_SLCONS = 122 # Shoreline construction
OBJL_SLOTOP = 126 # Slope topline
OBJL_SLOGRD = 127 # Sloping ground
OBJL_SOUNDG = 129 # Sounding
OBJL_STSLNE = 132 # Straight territorial sea baseline
OBJL_SUBTLN = 133 # Submarine transit lane
OBJL_SWPARE = 134 # Swept area
OBJL_TESARE = 135 # Territorial sea area
OBJL_TIDEWY = 143 # Tideway
OBJL_TOPMAR = 144 # Top mark
OBJL_TSELNE = 145 # Traffic Separation Line
OBJL_TSSBND = 146 # Traffic Separation Scheme Boundary
OBJL_TSSCRS = 147 # Traffic Separation Scheme Crossing
OBJL_TSSLPT = 148 # Traffic Separation Scheme Lane part
OBJL_TSSRON = 149 # Traffic Separation Scheme Roundabout
OBJL_TUNNEL = 151 # Tunnel
OBJL_TWRTPT = 152 # Two-way route part
OBJL_UWTROC = 153 # Underwater rock / awash rock
OBJL_VEGATN = 155 # Vegetation
OBJL_WATTUR = 156 # Water turbulence
OBJL_WEDKLP = 158 # Weed/Kelp
OBJL_WRECKS = 159 # Wreck
OBJL_M_ACCY = 300 # Accuracy of data (meta)
OBJL_M_CSCL = 301 # Compilation scale (meta)
OBJL_M_COVR = 302 # Coverage (meta)
OBJL_M_HDAT = 303 # Horizontal datum (meta)
OBJL_M_NSYS = 306 # Navigational system of marks (meta)
OBJL_M_QUAL = 308 # Quality of data (meta)
OBJL_M_SDAT = 309 # Sounding datum (meta)
OBJL_M_UNIT = 311 # Units of measurement (meta)
# fmt: on
# Lookup: S-57 acronym → OBJL code (complete IHO S-57 Ed 3.1 set)
OBJL_BY_ACRONYM: dict[str, int] = {
"ACHBRT": OBJL_ACHBRT, "ACHARE": OBJL_ACHARE,
"BCNCAR": OBJL_BCNCAR, "BCNISD": OBJL_BCNISD, "BCNLAT": OBJL_BCNLAT,
"BCNSAW": OBJL_BCNSAW, "BCNSPP": OBJL_BCNSPP,
"BERTHS": OBJL_BERTHS, "BRIDGE": OBJL_BRIDGE,
"BUISGL": OBJL_BUISGL, "BUAARE": OBJL_BUAARE,
"BOYCAR": OBJL_BOYCAR, "BOYISD": OBJL_BOYISD, "BOYLAT": OBJL_BOYLAT,
"BOYSAW": OBJL_BOYSAW, "BOYSPP": OBJL_BOYSPP,
"BOYSPEC": OBJL_BOYSPP, # user alias → BOYSPP
"CBLOHD": OBJL_CBLOHD, "CBLSUB": OBJL_CBLSUB,
"CANALS": OBJL_CANALS, "CTSARE": OBJL_CTSARE,
"COALNE": OBJL_COALNE, "CRANES": OBJL_CRANES,
"DAYMAR": OBJL_DAYMAR, "DWRTCL": OBJL_DWRTCL, "DWRTPT": OBJL_DWRTPT,
"DEPARE": OBJL_DEPARE, "DEPCNT": OBJL_DEPCNT,
"DOCARE": OBJL_DOCARE, "DRGARE": OBJL_DRGARE, "DRYDOC": OBJL_DRYDOC,
"DMPGRD": OBJL_DMPGRD,
"EXEZNE": OBJL_EXEZNE, "FAIRWY": OBJL_FAIRWY,
"FERYRT": OBJL_FERYRT, "FSHZNE": OBJL_FSHZNE, "FRPARE": OBJL_FRPARE,
"FOGSIG": OBJL_FOGSIG, "GATCON": OBJL_GATCON,
"HRBARE": OBJL_HRBARE, "HRBFAC": OBJL_HRBFAC, "HULKES": OBJL_HULKES,
"ISTZNE": OBJL_ISTZNE,
"LAKARE": OBJL_LAKARE, "LNDARE": OBJL_LNDARE, "LNDRGN": OBJL_LNDRGN,
"LNDMRK": OBJL_LNDMRK, "LIGHTS": OBJL_LIGHTS,
"LITFLT": OBJL_LITFLT, "LITVES": OBJL_LITVES,
"LOKBSN": OBJL_LOKBSN, "MAGVAR": OBJL_MAGVAR,
"MARCUL": OBJL_MARCUL, "MIPARE": OBJL_MIPARE, "MORFAC": OBJL_MORFAC,
"NAVLNE": OBJL_NAVLNE, "OBSTRN": OBJL_OBSTRN,
"OFSPLF": OBJL_OFSPLF, "OSPARE": OBJL_OSPARE,
"PILPNT": OBJL_PILPNT, "PILBOP": OBJL_PILBOP, "PIPARE": OBJL_PIPARE,
"PIPOHD": OBJL_PIPOHD, "PIPSOL": OBJL_PIPSOL, "PONTON": OBJL_PONTON,
"PRCARE": OBJL_PRCARE, "PYLONS": OBJL_PYLONS,
"RADRFL": OBJL_RADRFL, "RADSTA": OBJL_RADSTA,
"RDOCAL": OBJL_RDOCAL, "RDOSTA": OBJL_RDOSTA,
"RECTRC": OBJL_RECTRC, "RESARE": OBJL_RESARE, "RETRFL": OBJL_RETRFL,
"RIVERS": OBJL_RIVERS, "RIVBNK": OBJL_RIVBNK,
"ROADWY": OBJL_ROADWY, "RUNWAY": OBJL_RUNWAY,
"SEAARE": OBJL_SEAARE, "SPLARE": OBJL_SPLARE, "SBDARE": OBJL_SBDARE,
"SLCONS": OBJL_SLCONS, "SLOTOP": OBJL_SLOTOP, "SLOGRD": OBJL_SLOGRD,
"SOUNDG": OBJL_SOUNDG, "STSLNE": OBJL_STSLNE, "SUBTLN": OBJL_SUBTLN,
"SWPARE": OBJL_SWPARE, "TESARE": OBJL_TESARE, "TIDEWY": OBJL_TIDEWY,
"TOPMAR": OBJL_TOPMAR,
"TSELNE": OBJL_TSELNE, "TSSBND": OBJL_TSSBND, "TSSCRS": OBJL_TSSCRS,
"TSSLPT": OBJL_TSSLPT, "TSSRON": OBJL_TSSRON,
"TUNNEL": OBJL_TUNNEL, "TWRTPT": OBJL_TWRTPT,
"UWTROC": OBJL_UWTROC, "VEGATN": OBJL_VEGATN,
"WATTUR": OBJL_WATTUR, "WEDKLP": OBJL_WEDKLP, "WRECKS": OBJL_WRECKS,
"M_ACCY": OBJL_M_ACCY, "M_CSCL": OBJL_M_CSCL, "M_COVR": OBJL_M_COVR,
"M_HDAT": OBJL_M_HDAT, "M_NSYS": OBJL_M_NSYS, "M_QUAL": OBJL_M_QUAL,
"M_SDAT": OBJL_M_SDAT, "M_UNIT": OBJL_M_UNIT,
}
# PRIM codes for FRID
PRIM_POINT = 1
PRIM_LINE = 2
PRIM_AREA = 3
# ── S-57 attribute codes (ATTL) — from s57attributes.csv (IHO S-57 Ed.3.1) ───
# fmt: off
ATTL_AGENCY = 1 # AGENCY — agency responsible for production
ATTL_BCNSHP = 2 # BCNSHP — beacon shape
ATTL_BOYSHP = 4 # BOYSHP — buoy shape
ATTL_BURDEP = 5 # BURDEP — buried depth
ATTL_CATCAM = 13 # CATCAM — category of cardinal mark
ATTL_CATCOV = 18 # CATCOV — category of coverage (1=coverage, 2=no coverage)
ATTL_CATFOG = 27 # CATFOG — category of fog signal
ATTL_CATLAM = 36 # CATLAM — category of lateral mark (1=port, 2=starboard)
ATTL_CATLMK = 35 # CATLMK — category of landmark
ATTL_CATLIT = 37 # CATLIT — category of light
ATTL_CATOBS = 42 # CATOBS — category of obstruction
ATTL_CATREA = 54 # CATREA — category of restricted area
ATTL_CATSIW = 63 # CATSIW — category of signal station warning
ATTL_COLOUR = 75 # COLOUR — colour (list)
ATTL_COLPAT = 76 # COLPAT — colour pattern
ATTL_CONDTN = 81 # CONDTN — condition
ATTL_DRVAL1 = 90 # DRVAL1 — draft value 1
ATTL_DRVAL2 = 91 # DRVAL2 — draft value 2
ATTL_HEIGHT = 95 # HEIGHT — height above sea surface
ATTL_LITCHR = 107 # LITCHR — light characteristic
ATTL_MLTYLT = 110 # MLTYLT — multiplicity of lights
ATTL_NATCON = 112 # NATCON — nature of construction
ATTL_OBJNAM = 116 # OBJNAM — object name (free text label)
ATTL_ORIENT = 117 # ORIENT — orientation (degrees)
ATTL_PEREND = 119 # PEREND — period end (season)
ATTL_PERSTA = 120 # PERSTA — period start (season)
ATTL_QUASOU = 126 # QUASOU — quality of sounding measurement
ATTL_SIGGRP = 141 # SIGGRP — signal group ("(2)", etc.)
ATTL_SIGPER = 142 # SIGPER — signal period (seconds)
ATTL_STATUS = 149 # STATUS — status (permanent, occasional…)
ATTL_VALSOU = 179 # VALSOU — value of sounding (metres)
ATTL_VERACC = 180 # VERACC — vertical accuracy
ATTL_VERCLR = 181 # VERCLR — vertical clearance
ATTL_VALNMR = 178 # VALNMR — value of nominal range (nautical miles)
ATTL_VERDAT = 187 # VERDAT — vertical datum
# fmt: on
# Convenience dict: acronym → ATTL code (superset of the named constants above)
ATTR_CODE: dict[str, int] = {
"AGENCY": 1, "BCNSHP": 2, "BUISHP": 3, "BOYSHP": 4, "BURDEP": 5,
"CALSGN": 6, "CATAIR": 7, "CATACH": 8, "CATBRG": 9, "CATBUA": 10,
"CATCBL": 11, "CATCAN": 12, "CATCAM": 13, "CATCHP": 14, "CATCOA": 15,
"CATCTR": 16, "CATCON": 17, "CATCOV": 18, "CATCRN": 19, "CATDAM": 20,
"CATDIS": 21, "CATDOC": 22, "CATDPG": 23, "CATFNC": 24, "CATFRY": 25,
"CATFIF": 26, "CATFOG": 27, "CATFOR": 28, "CATGAT": 29, "CATHAF": 30,
"CATHLK": 31, "CATICE": 32, "CATINB": 33, "CATLND": 34, "CATLMK": 35,
"CATLAM": 36, "CATLIT": 37, "CATMFA": 38, "CATMPA": 39, "CATMOR": 40,
"CATNAV": 41, "CATOBS": 42, "CATOFP": 43, "CATOLB": 44, "CATPLE": 45,
"CATPIL": 46, "CATPIP": 47, "CATPRA": 48, "CATPYL": 49, "CATQUA": 50,
"CATRAS": 51, "CATRTB": 52, "CATROS": 53, "CATREA": 54, "CATSEA": 55,
"CATSIT": 56, "CATSLC": 57, "CATSPM": 58, "CATSCF": 59, "CATSUB": 60,
"CAATTS": 61, "CATTSS": 62, "CATSIW": 63, "CATTRK": 64, "CATVEG": 65,
"CATWED": 66, "CATWRK": 67, "CATZOC": 68, "CATWAT": 69, "COLOUR": 75,
"COLPAT": 76, "CONDTN": 81, "CONRAD": 82, "CONVIS": 83, "CURVEL": 84,
"DATEND": 85, "DATSTA": 86, "DRVAL1": 90, "DRVAL2": 91, "ELEVAT": 93,
"ESTRNG": 94, "HEIGHT": 95, "HORACC": 96, "HORCLR": 97, "HORLEN": 98,
"HORWID": 99, "ICEFAC": 100,"INFORM": 101,"JRSDTN": 102,"LIFCAP": 103,
"LITCHR": 107, "LITVIS": 108,"MARSYS": 109,"MLTYLT": 110,"NATION": 111,
"NATCON": 112, "NATQUA": 113,"NATSUR": 114,"NOBJNM": 115,"OBJNAM": 116,
"ORIENT": 117, "PEREND": 119,"PERSTA": 120,"PICREP": 121,"POSACC": 122,
"PRCTRY": 124, "PRODCT": 125,"QUASOU": 126,"RADWAL": 127,"RADIUS": 128,
"RYRMGV": 133, "SCAMAX": 134,"SCAMIN": 135,"SCVAL1": 136,"SCVAL2": 137,
"SECTR1": 138, "SECTR2": 139,"SHIPAM": 140,"SIGGRP": 141,"SIGPER": 142,
"SIGSEQ": 143, "SOUACC": 144,"SDISMX": 145,"SDISMN": 146,"SORDAT": 147,
"SORIND": 148, "STATUS": 149,"SURATH": 150,"SUREND": 151,"SURSTA": 152,
"SURTYP": 153, "TECSOU": 156,"TXTDSC": 157,"TS_TSP": 158,"TS_TSV": 159,
"T_ACWL": 160, "T_HWLW": 161,"T_MTOD": 162,"T_THDF": 163,"T_TIMS": 164,
"T_TRNP": 165, "T_VAHF": 166,"T_VAVL": 167,"TIMEND": 168,"TIMSTA": 169,
"TOPSHP": 170, "TRAFIC": 171,"VALDCO": 172,"VERACC": 180,"VERCLR": 181,
"VERCCL": 182, "VERCOP": 183,"VERCSA": 184,"VERDAT": 187,"VERLEN": 188,
"WATLEV": 187, "VALSOU": 179, "VALNMR": 178,
}
# ── Binary primitives (little-endian, S-57 convention) ─────────────────────────
def b11(n): return int(n).to_bytes(1, "little", signed=False)
def b12(n): return int(n).to_bytes(2, "little", signed=False)
def b14(n): return int(n).to_bytes(4, "little", signed=False)
def b21(n): return int(n).to_bytes(1, "little", signed=True)
def b22(n): return int(n).to_bytes(2, "little", signed=True)
def b24(n): return int(n).to_bytes(4, "little", signed=True)
def A_var(s: str) -> bytes:
"""Variable-length ASCII subfield, delimited by UT."""
return s.encode("latin-1") + UT
def A_fixed(s: str, n: int) -> bytes:
"""Fixed-width ASCII (no UT)."""
return s.encode("latin-1").ljust(n, b" ")[:n]
def R_fixed(value: float, n: int = 4) -> bytes:
"""Fixed-width ASCII real, e.g. R(4) = '03.1'."""
s = f"{value:.{max(0, n-2)}f}"
return s.encode("latin-1").ljust(n, b" ")[:n]
def name_5(rcnm: int, rcid: int) -> bytes:
"""S-57 NAME field: RCNM (1 byte) + RCID (4 bytes LE) = 5 bytes (= 40 bits)."""
return b11(rcnm) + b14(rcid)
# ── Field builders (one per S-57 field tag) ────────────────────────────────────
def field_0001(rcid: int) -> bytes:
"""Record header (b12 RCID) + FT."""
return b12(rcid) + FT
def field_DSID(*, rcnm=10, rcid=1, expp=1, intu=3,
dsnm="", edtn="1", updn="0",
uadt="", isdt="", sted=3.1,
prsp=1, psdn="", pred="2.0",
prof=1, agen=999, comt="") -> bytes:
return (b11(rcnm) + b14(rcid) + b11(expp) + b11(intu) +
A_var(dsnm) + A_var(edtn) + A_var(updn) +
A_fixed(uadt, 8) + A_fixed(isdt, 8) +
R_fixed(sted, 4) +
b11(prsp) + A_var(psdn) + A_var(pred) +
b11(prof) + b12(agen) +
A_var(comt) + FT)
def field_DSSI(*, dstr=2, aall=1, nall=1, nomr=0, nocr=0,
nogr=0, nolr=0, noin=0, nocn=0, noed=0, nofa=0) -> bytes:
"""Format: (3b11, 8b14)"""
return (b11(dstr) + b11(aall) + b11(nall) +
b14(nomr) + b14(nocr) + b14(nogr) + b14(nolr) +
b14(noin) + b14(nocn) + b14(noed) + b14(nofa) + FT)
def field_DSPM(*, rcnm=20, rcid=1, hdat=2, vdat=17, sdat=23,
cscl=50000, duni=1, huni=1, puni=1, coun=1,
comf=10000000, somf=10, comt="") -> bytes:
"""Format: (b11, b14, 3b11, b14, 4b11, 2b14, A)
HDAT=2 (WGS84), VDAT=17 (MLLW), SDAT=23 (MLLW),
DUNI=1 (m), HUNI=1 (m), PUNI=1 (m), COUN=1 (lat/long)
COMF=10^7 means coords stored as int = (degrees * 10^7).
SOMF=10 means depths stored as int = (metres * 10)."""
return (b11(rcnm) + b14(rcid) +
b11(hdat) + b11(vdat) + b11(sdat) +
b14(cscl) +
b11(duni) + b11(huni) + b11(puni) + b11(coun) +
b14(comf) + b14(somf) +
A_var(comt) + FT)
def field_VRID(*, rcnm: int, rcid: int, rver: int = 1, ruin: int = 1) -> bytes:
"""Format: (b11, b14, b12, b11). RUIN=1 means insert."""
return b11(rcnm) + b14(rcid) + b12(rver) + b11(ruin) + FT
def field_SG2D(coords_deg: list[tuple[float, float]], comf: int) -> bytes:
"""Format: *(b24 YCOO, b24 XCOO). Coords are (lon, lat) in degrees → ints
multiplied by COMF (typically 10^7). Note S-57 stores YCOO (lat) before
XCOO (lon) for each pair."""
out = b""
for lon, lat in coords_deg:
y_int = int(round(lat * comf))
x_int = int(round(lon * comf))
out += b24(y_int) + b24(x_int)
return out + FT
def field_VRPT(pointers: list[tuple[int, int, int, int, int, int]]) -> bytes:
"""Format: *(B(40) NAME, b11 ORNT, b11 USAG, b11 TOPI, b11 MASK).
Each item: (rcnm, rcid, ornt, usag, topi, mask)."""
out = b""
for rcnm, rcid, ornt, usag, topi, mask in pointers:
out += name_5(rcnm, rcid) + b11(ornt) + b11(usag) + b11(topi) + b11(mask)
return out + FT
def field_FRID(*, rcnm: int = RCNM_FE, rcid: int, prim: int, grup: int = 1,
objl: int, rver: int = 1, ruin: int = 1) -> bytes:
"""Format: (b11, b14, 2b11, 2b12, b11)."""
return (b11(rcnm) + b14(rcid) +
b11(prim) + b11(grup) +
b12(objl) + b12(rver) +
b11(ruin) + FT)
def field_FOID(*, agen: int = 999, fidn: int, fids: int = 1) -> bytes:
"""Format: (b12, b14, b12)."""
return b12(agen) + b14(fidn) + b12(fids) + FT
def field_ATTF(attrs: list[tuple[int, str]]) -> bytes:
"""Format: *(b12 ATTL, A ATVL). Empty list → no field at all (caller skips)."""
out = b""
for attl, atvl in attrs:
out += b12(attl) + A_var(str(atvl))
return out + FT
def field_FSPT(pointers: list[tuple[int, int, int, int, int]]) -> bytes:
"""Format: *(B(40) NAME, b11 ORNT, b11 USAG, b11 MASK).
Each: (rcnm, rcid, ornt, usag, mask). ORNT=1=forward,2=reverse,255=null;
USAG=1=exterior,2=interior,3=both,255=null; MASK=1=mask,2=show,255=null."""
out = b""
for rcnm, rcid, ornt, usag, mask in pointers:
out += name_5(rcnm, rcid) + b11(ornt) + b11(usag) + b11(mask)
return out + FT
# ── Record builder (data record with entry-map 5504) ───────────────────────────
def build_record(fields: list[tuple[str, bytes]]) -> bytes:
"""fields: list of (4-char tag, field bytes). Field bytes include trailing FT.
Uses entry-map "5504": field-length=5 digits (max 99999 bytes per field),
position=5 digits (max 99999). Each directory entry is 14 bytes: tag(4)+len(5)+pos(5).
History of bugs:
"3404" (original): 3-digit length = max 999 bytes → LNDARE polygons produce
SG2D fields of 2000-53000 bytes → length truncated → GDAL: "Not enough byte
to initialize field 'SG2D'".
"4404": 4-digit = max 9999 bytes → still not enough for large coastal outlines.
"5504": 5-digit = max 99999 bytes → handles any realistic polygon after RDP.
The DDR template uses "3404" for its own directory, but that only applies to
how the DDR is parsed. GDAL reads each data record's leader independently, so
data records may use a different entry-map than the DDR — this is valid ISO 8211.
"""
field_area = b""
dir_str = ""
pos = 0
for tag, data in fields:
assert len(tag) == 4, f"tag must be 4 chars: {tag!r}"
flen = len(data)
assert flen <= 99999, f"field {tag} too large ({flen} bytes); apply more RDP"
assert pos <= 99999, f"field area position overflow at {tag} (pos={pos})"
dir_str += f"{tag:<4s}{flen:05d}{pos:05d}"
field_area += data
pos += flen
directory = dir_str.encode("latin-1") + FT
base_addr = 24 + len(directory)
record_len = base_addr + len(field_area)
assert record_len <= 99999, f"record too large ({record_len} bytes)"
leader = (
f"{record_len:05d}" # 0-4: record length
" " # 5: interchange level (space for DR)
"D" # 6: leader id
" " # 7: in-line code extension (space for DR)
" " # 8: version
" " # 9: app indicator
" " # 10-11: field control length (00 for DR)
f"{base_addr:05d}" # 12-16: base address of field area
" " # 17-19: extended character set indicator
"5504" # 20-23: entry map (len=5, pos=5, reserved=0, tag=4)
).encode("latin-1")
assert len(leader) == 24, f"leader len {len(leader)}"
return leader + directory + field_area
# ── High-level builder for a complete `.000` ──────────────────────────────────
class S57Cell:
"""Builds a complete S-57 cell. Call add_* methods then write()."""
def __init__(self, *, dsnm: str, edition: int = 1, intu: int = 5,
scale: int = 50000, agen: int = 999, comt: str = "AR ECDIS custom",
issue_date: str = "20260428"):
self.dsnm = dsnm
self.edition = edition
self.intu = intu
self.scale = scale
self.agen = agen
self.comt = comt
self.issue_date = issue_date
self.comf = 10_000_000 # 10^7 — coords stored as int(deg * COMF)
self.somf = 10 # depths × 10
# Spatial primitives & features accumulated by callers
self._next_vi = 1
self._next_vc = 1
self._next_ve = 1
self._next_fid = 1
self._records: list[tuple[str, list[tuple[str, bytes]]]] = []
# Counters for DSSI
self._n_meta = 0
self._n_pt = 0 # NOMR isolated nodes
self._n_cn = 0 # NOCN connected nodes
self._n_ed = 0 # NOED edges
self._n_geo = 0
self._n_col = 0 # NOCR collection
self._n_in = 0 # NOIN isolated nodes alt count
self._n_lr = 0 # NOLR
self._n_gr = 0 # NOGR
self._n_fa = 0 # NOFA faces
# --- Add an isolated node (for points: buoys, lights, landmarks) ---
def add_isolated_node(self, lon: float, lat: float) -> tuple[int, int]:
rcid = self._next_vi
self._next_vi += 1
fields = [
("0001", field_0001(rcid)),
("VRID", field_VRID(rcnm=RCNM_VI, rcid=rcid)),
("SG2D", field_SG2D([(lon, lat)], self.comf)),
]
self._records.append(("VI", fields))
self._n_pt += 1
self._n_in += 1
return (RCNM_VI, rcid)
# --- Add an edge connecting (start_lon, start_lat) → (end_lon, end_lat) ---
# First creates two connected nodes, then the edge that points to them with
# intermediate coordinates.
def add_edge(self, coords: list[tuple[float, float]]) -> tuple[int, int]:
if len(coords) < 2:
raise ValueError("edge needs ≥2 coordinates")
# Create connected nodes for the endpoints
cn_start = self._add_connected_node(*coords[0])
cn_end = self._add_connected_node(*coords[-1])
# The edge: VRID + VRPT (pointers to both CNs) + SG2D (intermediate coords)
rcid = self._next_ve
self._next_ve += 1
# Intermediate points only (S-57 stores edges as: start_CN, intermediates..., end_CN)
intermediate = coords[1:-1]
fields = [
("0001", field_0001(rcid)),
("VRID", field_VRID(rcnm=RCNM_VE, rcid=rcid)),
# VRPT: two pointers — to start node, to end node
("VRPT", field_VRPT([
(RCNM_VC, cn_start[1], 1, 1, 1, 255), # ORNT=1, USAG=1=ext, TOPI=1=begin, MASK=255
(RCNM_VC, cn_end[1], 2, 1, 2, 255), # ORNT=2=reverse, TOPI=2=end
])),
]
if intermediate:
fields.append(("SG2D", field_SG2D(intermediate, self.comf)))
self._records.append(("VE", fields))
self._n_ed += 1
return (RCNM_VE, rcid)
def _add_connected_node(self, lon: float, lat: float) -> tuple[int, int]:
rcid = self._next_vc
self._next_vc += 1
fields = [
("0001", field_0001(rcid)),
("VRID", field_VRID(rcnm=RCNM_VC, rcid=rcid)),
("SG2D", field_SG2D([(lon, lat)], self.comf)),
]
self._records.append(("VC", fields))
self._n_cn += 1
return (RCNM_VC, rcid)
# --- Feature: point ---
def add_point_feature(self, *, objl: int,
lon: float, lat: float,
attrs: list[tuple[int, str]] | None = None) -> int:
rcid = self._next_fid
self._next_fid += 1
fid = rcid
node_name = self.add_isolated_node(lon, lat)
fields = [
("0001", field_0001(rcid)),
("FRID", field_FRID(rcid=rcid, prim=PRIM_POINT, objl=objl)),
("FOID", field_FOID(agen=self.agen, fidn=fid)),
]
if attrs:
fields.append(("ATTF", field_ATTF(attrs)))
fields.append(("FSPT", field_FSPT([
(node_name[0], node_name[1], 255, 255, 255), # ORNT/USAG/MASK = NULL for point
])))
self._records.append(("FE", fields))
self._n_geo += 1
return rcid
# --- Feature: line (e.g. COALNE, DEPCNT) ---
def add_line_feature(self, *, objl: int,
coords: list[tuple[float, float]],
attrs: list[tuple[int, str]] | None = None) -> int:
"""coords: ordered list of (lon, lat) vertices (at least 2). The whole
polyline is encoded as a single VE edge so that GDAL reassembles it into
a LineString geometry. For very long coastlines you can split into multiple
calls, each producing a separate feature."""
if len(coords) < 2:
raise ValueError("line feature needs ≥2 coordinates")
edge_name = self.add_edge(coords)
rcid = self._next_fid
self._next_fid += 1
fields = [
("0001", field_0001(rcid)),
("FRID", field_FRID(rcid=rcid, prim=PRIM_LINE, objl=objl)),
("FOID", field_FOID(agen=self.agen, fidn=rcid)),
]
if attrs:
fields.append(("ATTF", field_ATTF(attrs)))
fields.append(("FSPT", field_FSPT([
(edge_name[0], edge_name[1], 1, 255, 255), # ORNT=1=fwd, USAG=null, MASK=null
])))
self._records.append(("FE", fields))
self._n_geo += 1
return rcid
# --- Feature: area defined by closed boundary polygon ---
def add_area_feature(self, *, objl: int,
ring: list[tuple[float, float]],
attrs: list[tuple[int, str]] | None = None) -> int:
"""ring: list of (lon, lat) — must be closed (first == last) or will be."""
if ring[0] != ring[-1]:
ring = ring + [ring[0]]
edge_name = self.add_edge(ring)
rcid = self._next_fid
self._next_fid += 1
fields = [
("0001", field_0001(rcid)),
("FRID", field_FRID(rcid=rcid, prim=PRIM_AREA, objl=objl)),
("FOID", field_FOID(agen=self.agen, fidn=rcid)),
]
if attrs:
fields.append(("ATTF", field_ATTF(attrs)))
fields.append(("FSPT", field_FSPT([
(edge_name[0], edge_name[1], 1, 1, 255), # ORNT=1=fwd, USAG=1=exterior, MASK=null
])))
self._records.append(("FE", fields))
self._n_geo += 1
return rcid
# --- Build the full file ---
def write(self, output_path: str | Path) -> None:
if not DDR_TEMPLATE.exists():
raise FileNotFoundError(
f"DDR template not found: {DDR_TEMPLATE}. "
"Extract noaa_ddr_template.bin from a real ENC first."
)
with open(DDR_TEMPLATE, "rb") as f:
ddr = f.read()
# Build DSID record (record 2)
dsid_record = build_record([
("0001", field_0001(2)),
("DSID", field_DSID(
rcnm=10, rcid=1, expp=1, intu=self.intu,
dsnm=self.dsnm, edtn=str(self.edition), updn="0",
uadt=self.issue_date, isdt=self.issue_date,
sted=3.1, prsp=1, psdn="", pred="2.0",
prof=1, agen=self.agen, comt=self.comt,
)),
("DSSI", field_DSSI(
dstr=2, aall=1, nall=1,
nomr=self._n_meta, nocr=self._n_col, nogr=self._n_gr,
nolr=self._n_lr, noin=self._n_pt, nocn=self._n_cn,
noed=self._n_ed, nofa=self._n_fa,
)),
])
# Build DSPM record (record 3)
dspm_record = build_record([
("0001", field_0001(3)),
("DSPM", field_DSPM(
rcnm=20, rcid=1, hdat=2, vdat=17, sdat=23,
cscl=self.scale, duni=1, huni=1, puni=1, coun=1,
comf=self.comf, somf=self.somf, comt="",
)),
])
# Build all data records collected by the caller
rcid_seq = 4 # records 1=DDR, 2=DSID, 3=DSPM, then sequential
body_records = b""
for kind, fields in self._records:
# Replace the 0001 RCID with global record sequence (per-record-type
# numbering would also be valid; simplest is global sequence)
new_fields = [
("0001", field_0001(rcid_seq)) if t == "0001" else (t, d)
for t, d in fields
]
body_records += build_record(new_fields)
rcid_seq += 1
with open(output_path, "wb") as f:
f.write(ddr + dsid_record + dspm_record + body_records)
+4100
View File
File diff suppressed because it is too large Load Diff
BIN
View File
Binary file not shown.