Changeset 3839 for trunk/LMDZ.MARS/util/display_netcdf.py
- Timestamp:
- Jul 7, 2025, 6:00:37 PM (5 days ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/LMDZ.MARS/util/display_netcdf.py
r3824 r3839 85 85 # Attempt to load MOLA topography 86 86 try: 87 MOLA = np.load('MOLA_1px_per_deg.npy') 87 MOLA = np.load('MOLA_1px_per_deg.npy') # shape (nlat, nlon) at 1° per pixel: lat from -90 to 90, lon from 0 to 360 88 88 nlat, nlon = MOLA.shape 89 89 topo_lats = np.linspace(90 - 0.5, -90 + 0.5, nlat) … … 171 171 transform=transform 172 172 ) 173 174 175 def attach_format_coord(ax, mat, x, y, is_pcolormesh=True): 176 """ 177 Attach a format_coord function to the axes to display x, y, and value at cursor. 178 Works for both pcolormesh and imshow style grids. 179 """ 180 # Determine dimensions 181 if mat.ndim == 2: 182 ny, nx = mat.shape 183 elif mat.ndim == 3 and mat.shape[2] in (3, 4): 184 ny, nx, nc = mat.shape 185 else: 186 raise ValueError(f"Unsupported mat shape {mat.shape}") 187 # Edges or extents 188 if is_pcolormesh: 189 xedges, yedges = x, y 190 else: 191 x0, x1 = x.min(), x.max() 192 y0, y1 = y.min(), y.max() 193 194 def format_coord(xp, yp): 195 # Map to indices 196 if is_pcolormesh: 197 col = np.searchsorted(xedges, xp) - 1 198 row = np.searchsorted(yedges, yp) - 1 199 else: 200 col = int((xp - x0) / (x1 - x0) * nx) 201 row = int((yp - y0) / (y1 - y0) * ny) 202 # Within bounds? 203 if 0 <= row < ny and 0 <= col < nx: 204 if mat.ndim == 2: 205 v = mat[row, col] 206 return f"x={xp:.3g}, y={yp:.3g}, val={v:.3g}" 207 else: 208 vals = mat[row, col] 209 txt = ", ".join(f"{vv:.3g}" for vv in vals[:3]) 210 return f"x={xp:.3g}, y={yp:.3g}, val=({txt})" 211 return f"x={xp:.3g}, y={yp:.3g}" 212 213 ax.format_coord = format_coord 173 214 174 215 … … 396 437 data_full = np.where(data_full.mask, np.nan, data_full.data) 397 438 439 # If Time and altitude are both present and neither indexed, 440 # and every other dim has size 1: 441 t_idx = find_dim_index(dims, TIME_DIMS) 442 a_idx = find_dim_index(dims, ALT_DIMS) 443 shape = var.shape 444 if (t_idx is not None and a_idx is not None 445 and time_index is None and alt_index is None 446 and all(size == 1 for i, size in enumerate(shape) if i not in (t_idx, a_idx))): 447 448 # Build a slicer that keeps Time & altitude, drops other singletons 449 slicer = [0] * len(dims) 450 slicer[t_idx] = slice(None) 451 slicer[a_idx] = slice(None) 452 data2d = data_full[tuple(slicer)] # shape (ntime, nalt) 453 454 # Coordinate arrays 455 tvar = find_coord_var(dataset, TIME_DIMS) 456 avar = find_coord_var(dataset, ALT_DIMS) 457 tvals = dataset.variables[tvar][:] 458 avals = dataset.variables[avar][:] 459 460 # Unmask if necessary 461 if hasattr(tvals, "mask"): 462 tvals = np.where(tvals.mask, np.nan, tvals.data) 463 if hasattr(avals, "mask"): 464 avals = np.where(avals.mask, np.nan, avals.data) 465 466 # Plot heatmap with x=time, y=altitude 467 fig, ax = plt.subplots(figsize=(10, 6)) 468 T, A = np.meshgrid(tvals, avals) 469 im = ax.pcolormesh( 470 T, A, data2d.T, 471 shading="auto", cmap=colormap 472 ) 473 dt = tvals[1] - tvals[0] 474 da = avals[1] - avals[0] 475 x_edges = np.concatenate([tvals - dt/2, [tvals[-1] + dt/2]]) 476 y_edges = np.concatenate([avals - da/2, [avals[-1] + da/2]]) 477 attach_format_coord(ax, data2d.T, x_edges, y_edges, is_pcolormesh=True) 478 ax.set_xlabel(tvar) 479 ax.set_ylabel(avar) 480 cbar = fig.colorbar(im, ax=ax) 481 cbar.set_label(varname + (f" ({getattr(var, 'units','')})")) 482 ax.set_title(f"{varname} — {avar} vs {tvar}", fontweight="bold") 483 484 if output_path: 485 fig.savefig(output_path, bbox_inches="tight") 486 print(f"Saved to {output_path}") 487 else: 488 plt.show() 489 return 490 398 491 # Pure 1D time series 399 492 if len(dims) == 1 and find_dim_index(dims, TIME_DIMS) is not None: … … 434 527 if hasattr(lons, "mask"): 435 528 lons = np.where(lons.mask, np.nan, lons.data) 436 plt.figure(figsize=(10, 6)) 437 plt.pcolormesh(lons, tvals, data_avg, shading="auto", cmap=colormap) 438 plt.xlabel(f"Longitude ({getattr(dataset.variables[lon_var], 'units', 'deg')})") 439 plt.ylabel(time_var) 440 cbar = plt.colorbar() 529 fig, ax = ax.subplots(figsize=(10, 6)) 530 im = plt.pcolormesh(lons, tvals, data_avg, shading="auto", cmap=colormap) 531 dx = lons[1] - lons[0] 532 dy = tvals[1] - tvals[0] 533 x_edges = np.concatenate([lons - dx/2, [lons[-1] + dx/2]]) 534 y_edges = np.concatenate([tvals - dy/2, [tvals[-1] + dy/2]]) 535 attach_format_coord(ax, data_avg.T, x_edges, y_edges, is_pcolormesh=True) 536 ax.set_xlabel(f"Longitude ({getattr(dataset.variables[lon_var], 'units', 'deg')})") 537 ax.set_ylabel(time_var) 538 cbar = fig.colorbar(im, ax=ax) 441 539 cbar.set_label(varname + (f" ({var.units})" if hasattr(var, "units") else "")) 442 plt.title(f"{varname} averaged over latitude", fontweight='bold')540 ax.set_title(f"{varname} averaged over latitude", fontweight='bold') 443 541 if output_path: 444 plt.savefig(output_path, bbox_inches="tight")542 fig.savefig(output_path, bbox_inches="tight") 445 543 print(f"Saved to {output_path}") 446 544 else: … … 700 798 701 799 # Generic 2D 702 plt.figure(figsize=(8, 6)) 703 plt.imshow(dslice, aspect="auto") 704 plt.colorbar(label=varname + (f" ({var.units})" if hasattr(var, "units") else "")) 705 plt.xlabel("Dim 2 index") 706 plt.ylabel("Dim 1 index") 707 plt.title(f"{varname} (2D)", fontweight='bold') 800 fig, ax = plt.subplots(figsize=(8, 6)) 801 im = ax.imshow( 802 dslice, 803 aspect="auto", 804 interpolation='nearest' 805 ) 806 x0, x1 = 0, dslice.shape[1] - 1 807 y0, y1 = 0, dslice.shape[0] - 1 808 x_centers = np.linspace(x0, x1, dslice.shape[1]) 809 y_centers = np.linspace(y0, y1, dslice.shape[0]) 810 attach_format_coord(ax, dslice, x_centers, y_centers, is_pcolormesh=False) 811 cbar = fig.colorbar(im, ax=ax, orientation='vertical') 812 cbar.set_label(varname + (f" ({var.units})" if hasattr(var, "units") else "")) 813 ax.set_xlabel("Dim 2 index") 814 ax.set_ylabel("Dim 1 index") 815 ax.set_title(f"{varname} (2D)") 816 708 817 if output_path: 709 plt.savefig(output_path, bbox_inches="tight")818 fig.savefig(output_path, bbox_inches="tight") 710 819 print(f"Saved to {output_path}") 711 820 else:
Note: See TracChangeset
for help on using the changeset viewer.