2026-11-06 21:48:46
This commit is contained in:
26
README.md
26
README.md
@@ -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).
|
* `-f 10`: Muestra los primeros 10 tiempos (por defecto son 3).
|
||||||
* `-l 10`: Muestra los últimos 10 tiempos.
|
* `-l 10`: Muestra los últimos 10 tiempos.
|
||||||
* `-a`: Imprime todos los tiempos disponibles en el archivo.
|
* `-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).
|
||||||
|
|||||||
38
zarrviewer
38
zarrviewer
@@ -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
|
||||||
|
|
||||||
|
# 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"-> Detectado formato de mapa (Dimensiones: {other_dims}).")
|
||||||
print(f"-> Extrayendo último frame ({ultimo_time_str})...")
|
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)
|
||||||
|
|||||||
Reference in New Issue
Block a user