2026-11-06 21:48:46

This commit is contained in:
Your Name
2026-11-06 21:48:46 -03:00
parent e23122a66d
commit 1e231607c3
2 changed files with 56 additions and 18 deletions

View File

@@ -1,17 +1,19 @@
# ZarrViewer # ZarrViewer
Un explorador rápido y ligero para archivos `.zarr` desde la terminal. Permite visualizar la estructura, los tiempos y generar gráficos inteligentes (mapas o series de tiempo) con un solo comando. Un explorador rápido y ligero para archivos `.zarr` desde la terminal. Permite visualizar la estructura, los tiempos y generar gráficos inteligentes (mapas, series de tiempo o **snapshots estáticos**) con un solo comando.
## Instalación ## Instalación
1. Asegúrate de tener Python 3.6 o superior. 1. Asegúrate de tener Python 3.6 o superior.
2. Instala las dependencias necesarias: 2. Instala las dependencias necesarias:
```bash ```bash
pip install xarray pandas zarr matplotlib pip install xarray pandas zarr matplotlib
``` ```
3. Convierte el script en un comando nativo del sistema: 3. Convierte el script en un comando nativo del sistema:
```bash ```bash
# Quítale la extensión .py # Quítale la extensión .py
mv zarrviewer.py zarrviewer mv zarrviewer.py zarrviewer
@@ -26,6 +28,7 @@ Un explorador rápido y ligero para archivos `.zarr` desde la terminal. Permite
## Modo de Uso ## Modo de Uso
### 1. Exploración Básica (Listar variables y estructura) ### 1. Exploración Básica (Listar variables y estructura)
Si pasas solo la ruta del archivo (o usas la bandera `-h`), el programa imprimirá la estructura completa del archivo (similar a `ncdump -h`) y listará todas las variables disponibles para graficar. Si pasas solo la ruta del archivo (o usas la bandera `-h`), el programa imprimirá la estructura completa del archivo (similar a `ncdump -h`) y listará todas las variables disponibles para graficar.
```bash ```bash
@@ -35,23 +38,40 @@ zarrviewer ruta/al/archivo.zarr -h
``` ```
### 2. Ver Tiempos Disponibles ### 2. Ver Tiempos Disponibles
Muestra el inicio y el final de la serie de tiempo disponible. Muestra el inicio y el final de la serie de tiempo disponible.
```bash ```bash
zarrviewer ruta/al/archivo.zarr zarrviewer ruta/al/archivo.zarr
``` ```
Opciones adicionales: Opciones adicionales:
* `-f 10`: Muestra los primeros 10 tiempos (por defecto son 3).
* `-l 10`: Muestra los últimos 10 tiempos. * `-f 10`: Muestra los primeros 10 tiempos (por defecto son 3).
* `-a`: Imprime todos los tiempos disponibles en el archivo. * `-l 10`: Muestra los últimos 10 tiempos.
* `-a`: Imprime todos los tiempos disponibles en el archivo.
### 3. Generar un Mapa Geográfico (2D) ### 3. Generar un Mapa Geográfico (2D)
Si la variable tiene 2 dimensiones o más (ej. Lat/Lon, o X/Y en WRF), generará automáticamente un mapa georreferenciado del **último frame temporal disponible** con la escala corregida (1:1).
Si la variable tiene 2 dimensiones o más (ej. Lat/Lon, o X/Y en WRF y WindNinja), generará automáticamente un mapa georreferenciado con la escala corregida (1:1). Por defecto, grafica el **último frame temporal disponible** (o el único, si es un snapshot estático).
```bash ```bash
zarrviewer archivo.zarr -v T2 zarrviewer archivo.zarr -v T2
``` ```
**Seleccionar un frame específico:**
Puedes usar la opción `--frame` para elegir qué índice de tiempo graficar (0 es el primero, 1 el segundo, -1 el último).
```bash
# Graficar el primer frame (índice 0)
zarrviewer archivo.zarr -v T2 --frame 0
# Graficar el décimo frame (índice 9)
zarrviewer archivo.zarr -v T2 --frame 9
```
### 4. Generar una Serie de Tiempo (1D o Estaciones) ### 4. Generar una Serie de Tiempo (1D o Estaciones)
Si la variable está asociada a una dimensión unidimensional (como estaciones meteorológicas), debes especificar el valor/estación deseada separada por dos puntos `:` o un punto `.`. Si la variable está asociada a una dimensión unidimensional (como estaciones meteorológicas), debes especificar el valor/estación deseada separada por dos puntos `:` o un punto `.`.
```bash ```bash
@@ -60,9 +80,11 @@ zarrviewer archivo.zarr -v wind_speed_5m_prom:LSC
``` ```
**Sugerencia Interactiva:** Si olvidas especificar la estación, el programa te listará todos los valores posibles y te dará ejemplos listos para copiar y pegar: **Sugerencia Interactiva:** Si olvidas especificar la estación, el programa te listará todos los valores posibles y te dará ejemplos listos para copiar y pegar:
```bash ```bash
zarrviewer archivo.zarr -v wind_speed_5m_prom zarrviewer archivo.zarr -v wind_speed_5m_prom
``` ```
Opciones de graficado: Opciones de graficado:
* `-n 100`: Número de últimos registros a incluir en la serie de tiempo (por defecto extrae 50 puntos).
* `-n 100`: Número de últimos registros a incluir en la serie de tiempo (por defecto extrae 50 puntos).

View File

@@ -30,10 +30,14 @@ def main():
help="Mostrar todos los tiempos") help="Mostrar todos los tiempos")
parser.add_argument("--coord", "-c", type=str, default=None, parser.add_argument("--coord", "-c", type=str, default=None,
help="Nombre de la coord. temporal (auto-detectada por defecto)") help="Nombre de la coord. temporal (auto-detectada por defecto)")
# ARGUMENTOS DE GRAFICADO
parser.add_argument("--variable", "-v", type=str, default=None, parser.add_argument("--variable", "-v", type=str, default=None,
help="Variable a graficar. Sintaxis: var, var:dim, var.dim o 'var(dim)'") help="Variable a graficar. Sintaxis: var, var:dim, var.dim o 'var(dim)'")
parser.add_argument("-n", "--npoints", type=int, default=50, parser.add_argument("-n", "--npoints", type=int, default=50,
help="Número de registros a graficar en series de tiempo (default: 50)") help="Número de registros a graficar en series de tiempo (default: 50)")
parser.add_argument("--frame", type=int, default=-1,
help="Índice del frame temporal a graficar en mapas (default: -1, el último)")
args = parser.parse_args() args = parser.parse_args()
@@ -84,7 +88,6 @@ def main():
print("No se encontró una coordenada temporal.", file=sys.stderr) print("No se encontró una coordenada temporal.", file=sys.stderr)
sys.exit(1) sys.exit(1)
# === CORRECCIÓN VITAL: Manejo de snapshot estático (0 dimensiones temporales) ===
dims_of_time = ds[time_coord].dims dims_of_time = ds[time_coord].dims
if len(dims_of_time) > 0: if len(dims_of_time) > 0:
time_dim = dims_of_time[0] time_dim = dims_of_time[0]
@@ -92,7 +95,6 @@ def main():
total = len(times) total = len(times)
print(f"Usando coordenada temporal: '{time_coord}' (Eje dimensional: '{time_dim}')") print(f"Usando coordenada temporal: '{time_coord}' (Eje dimensional: '{time_dim}')")
else: else:
# Es un único frame estático
time_dim = None time_dim = None
times = [ds[time_coord].values[()]] times = [ds[time_coord].values[()]]
total = 1 total = 1
@@ -148,18 +150,28 @@ def main():
# CASO A: Mapa (2D o más) # CASO A: Mapa (2D o más)
if len(other_dims) >= 2: if len(other_dims) >= 2:
ultimo_time_str = format_time(times[-1]) frame_idx = args.frame
print(f"-> Detectado formato de mapa (Dimensiones: {other_dims}).")
print(f"-> Extrayendo último frame ({ultimo_time_str})...") # Validamos que el frame exista
if time_dim:
if frame_idx >= total or frame_idx < -total:
print(f"[ERROR] El frame {frame_idx} está fuera de rango. Rango válido: 0 a {total-1}.", file=sys.stderr)
sys.exit(1)
target_time_str = format_time(times[frame_idx])
print(f"-> Detectado formato de mapa (Dimensiones: {other_dims}).")
print(f"-> Extrayendo frame temporal {frame_idx} ({target_time_str})...")
else:
target_time_str = format_time(times[0])
print(f"-> Detectado formato de mapa estático (Dimensiones: {other_dims}).")
# Cortamos en tiempo SOLO si la variable depende del tiempo # Cortamos en tiempo SOLO si la variable depende del tiempo
ds_plot = ds[var_name] ds_plot = ds[var_name]
if time_dim and time_dim in ds_plot.dims: if time_dim and time_dim in ds_plot.dims:
ds_plot = ds_plot.isel({time_dim: -1}) ds_plot = ds_plot.isel({time_dim: frame_idx})
plot_kwargs = {} plot_kwargs = {}
# Buscamos georreferencia (agregué 'y' y 'x' para que use los de UTM por defecto) # Buscamos georreferencia
lat_name, lon_name = None, None lat_name, lon_name = None, None
for lat in ['lat', 'latitude', 'XLAT', 'xlat', 'y']: for lat in ['lat', 'latitude', 'XLAT', 'xlat', 'y']:
if lat in ds.variables or lat in ds.coords: if lat in ds.variables or lat in ds.coords:
@@ -174,11 +186,11 @@ def main():
print(f"-> Georreferencia detectada: '{lat_name}', '{lon_name}'. Aplicando ejes...") print(f"-> Georreferencia detectada: '{lat_name}', '{lon_name}'. Aplicando ejes...")
lat_arr = ds[lat_name] lat_arr = ds[lat_name]
if time_dim and time_dim in lat_arr.dims: if time_dim and time_dim in lat_arr.dims:
lat_arr = lat_arr.isel({time_dim: -1}) lat_arr = lat_arr.isel({time_dim: frame_idx})
lon_arr = ds[lon_name] lon_arr = ds[lon_name]
if time_dim and time_dim in lon_arr.dims: if time_dim and time_dim in lon_arr.dims:
lon_arr = lon_arr.isel({time_dim: -1}) lon_arr = lon_arr.isel({time_dim: frame_idx})
ds_plot = ds_plot.assign_coords({lat_name: lat_arr, lon_name: lon_arr}) ds_plot = ds_plot.assign_coords({lat_name: lat_arr, lon_name: lon_arr})
plot_kwargs['x'] = lon_name plot_kwargs['x'] = lon_name
@@ -192,8 +204,12 @@ def main():
ds_plot.plot(**plot_kwargs) ds_plot.plot(**plot_kwargs)
plt.gca().set_aspect('equal') plt.gca().set_aspect('equal')
plt.title(f"{var_name} | Frame: {ultimo_time_str}") plt.title(f"{var_name} | Frame: {target_time_str}")
nombre_imagen = f"mapa_{var_name}.png" if time_dim is None else f"mapa_{var_name}_{pd.to_datetime(times[-1]).strftime('%Y%m%d_%H%M')}.png"
if time_dim is None:
nombre_imagen = f"mapa_{var_name}.png"
else:
nombre_imagen = f"mapa_{var_name}_frame{frame_idx}_{pd.to_datetime(times[frame_idx]).strftime('%Y%m%d_%H%M')}.png"
# CASO B: Puntos / Estaciones (1D adicional) # CASO B: Puntos / Estaciones (1D adicional)
elif len(other_dims) == 1: elif len(other_dims) == 1:
@@ -204,7 +220,7 @@ def main():
print(f"Se detectó que la variable '{var_name}' está asociada a la dimensión: '{dim_name}'") print(f"Se detectó que la variable '{var_name}' está asociada a la dimensión: '{dim_name}'")
print(f"Valores posibles para {dim_name}:") print(f"Valores posibles para {dim_name}:")
print(", ".join(map(str, dim_values)) + "\n") print(", ".join(map(str, dim_values)) + "\n")
print("Ejemplos de uso (cópialos y pégalos):") print("Ejemplos de uso:")
for ex in dim_values[:3]: for ex in dim_values[:3]:
print(f" zarrviewer {args.zarr_path} -v {var_name}:{ex}") print(f" zarrviewer {args.zarr_path} -v {var_name}:{ex}")
sys.exit(0) sys.exit(0)