uxarray
uxarray copied to clipboard
Add `imshow` function for plotting with Matplotlib
Closes #1268
Overview
There's been strong community demand for first-class Matplotlib support alongside our existing HoloViz workflows. To address this, this PR introduces a native Matplotlib backend based on a screen-space pixel sampling strategy, designed to efficiently visualize unstructured-grid data without expensive geometry constructions.
Key Features
-
Screen-space Sampling:
- Utilizes our optimized
Grid.get_faces_containing_point()method to map each Matplotlib pixel center directly to the corresponding unstructured-grid face.
- Utilizes our optimized
-
Simplified Matplotlib Interface (
uxarray.plot.matplotlib.imshow):- Provides a single, intuitive, and self-configuring
imshowfunction that seamlessly integrates with Cartopy for map projections. - Employs a nearest-neighbor sampling approach by default, making it straightforward to use and easy to integrate into existing visualization workflows.
- Provides a single, intuitive, and self-configuring
-
Performance and Extensibility:
- By directly sampling in display-space, the backend avoids the computational cost associated with triangulation or and intermediate geometry creation (i.e. with
shapely) - Establishes a robust foundation for future enhancements, such as adding bilinear or higher-order interpolation methods, without altering the public-facing API (we can add a
resampleor similar parameter once multiple approaches are supported)
- By directly sampling in display-space, the backend avoids the computational cost associated with triangulation or and intermediate geometry creation (i.e. with
Documentation Changes
I have restructured the "Plotting with Matplotlib" notebook to showcase the imshow functionality instead of the PolyCollection & LineCollection, since these will either be deprecated or reorganized moving forward. They are still available in the API, but will not be immediately covered in the notebook
This addition significantly enhances our visualization toolkit, allowing for efficient and high-quality visualizations with Matplotlib, and lays the groundwork for more advanced interpolation and rendering techniques moving forward.
Usage
import uxarray as ux
import cartopy.crs as ccrs
import uxarray.plot.matplotlib as uxplot
uxda ...
img = uxplot.imshow(uxda)
img = uxplot.imshow(uxda, projection=ccrs.Orthographic())
End to End Visualization Workflow
fig, ax = plt.subplots(
subplot_kw={'projection': ccrs.Orthographic(central_longitude=-90, central_latitude=45)},
constrained_layout=True,
figsize=(10, 5),
)
scale = '110m'
ax.set_extent([-90, -80, 40, 50]) # great lakes
var = uxds['lwupt'].isel(Time=0)
my_plot = uxplot.imshow(var, ax=ax)
ax.coastlines(resolution=scale, color='black', linewidth=1)
ax.add_feature(cfeature.BORDERS.with_scale(scale), linestyle=':', linewidth=0.8)
ax.add_feature(cfeature.STATES.with_scale(scale), linestyle=':', linewidth=0.8)
gl = ax.gridlines(
draw_labels=True,
linewidth=0.5,
linestyle='--',
color='gray',
alpha=0.7,
)
lakes = NaturalEarthFeature(
category='physical',
name='lakes',
scale='10m',
edgecolor='black',
facecolor='none',
alpha=0.7,
)
ax.add_feature(lakes, linewidth=1, zorder=2)
gl.top_labels = False
gl.right_labels = False
ax.set_title(f"15km MPAS: {var.long_name}", pad=4, fontsize=10)
cbar = fig.colorbar(
my_plot,
ax=ax,
orientation='vertical',
shrink=0.6,
pad=0.03,
)
label = rf"$\mathit{{{var.name}}}\ \left[\mathrm{{{var.units}}}\right]$"
cbar.set_label(label, labelpad=8, fontsize=12)
plt.show()