Decisiones de diseño¶
Decisiones cerradas. No re-litigar sin motivo nuevo.
Raster¶
- Artefacto raster único: COG calibrado en AEQD centrada en el radar. No se generan PNG. La paleta se aplica en el cliente y OpenLayers reproyecta el raster al CRS de la vista en GPU. Un solo artefacto sirve como dato y como visual; cambiar paleta/umbrales no requiere regenerar nada. AEQD preserva distancia/azimut desde la torre (mínima distorsión al resamplear datos polares) y sus parámetros salen solos de la posición del radar — no hay paralelos estándar que definir. Sin código EPSG: el viewer registra la definición proj4 por radar.
- Malla AEQD por producto: celda = gate nativo, extensión = rango nativo. Nada de grilla común por radar — inflaría todos los COGs a la resolución del producto más fino sin ganar dato. Peor caso: N0B 3680×3680 @ 0.25 km, bajo el cap de textura WebGL de 4096. Ver Productos.
- Resampleo nearest neighbor, no bilinear — datos calibrados con umbrales; la interpolación suave inventa valores entre categorías.
- Elevación única: 0.5° para los productos radiales por elevación (
N0B,N0G). Los cortes superiores del volumen se ignoran; los derivados de volumen (EET, DVL, precipitación) no tienen elevación que elegir.
Decodificación¶
- MetPy como decodificador base.
Level3File(Unidata, misma casa que LDM/IDD) decodifica cabeceras y paquetes radiales/raster; el grillado usa pyproj y la escritura Rasterio (driver COG). El parsing de fenómenos (granizo, meso, TVS, celdas) y VWP sobre Symbology/Tabular es propio — MetPy solo expone los bloques crudos. - Paquete compartido con LAMULA-Ingest. Los aspectos comunes (parsing de fenómenos/Tabular, tipos de dominio NEXRAD) van en un paquete Python compartido entre ambos proyectos, no como copia. La capa de decodificación se mantiene independiente del transporte (LDM aquí, nbtcp allá).
Transporte y entrega¶
- LDM como transporte, no nbtcp. La fuente es el IDD público de Unidata, no un ORPG propio.
- Entrega pqact → procesador: FILE + watcher, no PIPE. El import de MetPy/Rasterio (~1–2 s) hace inviable un proceso por producto (PIPE
-close); el PIPE persistente concatena binarios sin framing (frágil); el fichero en disco da tolerancia a fallos, reintento y replay de crudos durante desarrollo. Procesados se borran tras subir a R2/D1; fallidos quedan para reproceso. - pqact: un solo patrón ancho, no una entrada por producto. Una única regla captura todos los mnemónicos × sitios hacia un mismo directorio; el procesador discrimina por nombre de fichero. La selección fina de qué baja del IDD vive en el
requestdeldmd.conf(mismo dialecto de patrón), que es donde ahorra ancho de banda.
Almacenamiento¶
- D1 en vez de PostgreSQL/ClickHouse. Escala de demo (subset de radares → miles de filas, no millones). Tier gratuito, integrado con R2. Si el proyecto pasa a producción, el schema es migrable a PostgreSQL plano (contrato tipo LAMULA-Ingest).
- Retención: 3 días (72 h), configurable. Sweep periódico que borra filas D1 y objetos R2 fuera de ventana, con pase de reconciliación (huérfanos en R2 sin fila / filas apuntando a objeto inexistente). R2 lifecycle rules como red de seguridad.
Tooling y despliegue¶
- uv para gestión de paquetes y entornos Python. No pip/venv/poetry directos:
uv venv,uv pip install, lockfile con uv. Aplica al Dockerfile del procesador y al desarrollo local. - Despliegue en Docker Swarm (nodo único). Stack file con
deploy:, secrets de Swarm para credenciales R2/D1, healthchecks nativos. Ver Plan de implementación. - CI en GitHub Actions, imágenes en
ghcr.io. Swarm hace pull del registry, nunca build local. - Alertas por Telegram (bot existente) desde el monitor de frescura.