Visualizing Data as Polygons#
Authors: Philip Chmielowiec
Overview#
This notebook showcases how to visualize data variables as Polygons using the UXarray Plotting API.
Note
UXarray’s Plotting API is build around the Holoviews package. For details about customization and accepted parameters, pleases refer to their documentation.
import uxarray as ux
from holoviews import opts
Face-Centered Data Variable#
The first dataset in this example is taken from an MPAS Ocean Mesh, with the face-centered data variable “BottomDepth”.
base_path = "../../test/meshfiles/mpas/QU/"
grid_path = base_path + "oQU480.231010.nc"
uxds_mpas = ux.open_dataset(grid_path, grid_path)
uxds_mpas['bottomDepth']
<xarray.UxDataArray 'bottomDepth' (n_face: 1791)> Size: 14kB array([4973. , 4123. , 2639. , ..., 5197. , 5499.99027344, 4855. ]) Dimensions without coordinates: n_face
Node-Centered Data Variable#
The second dataset in this example is taken from the NOAA Geoflow Project, with a node-centered data variable “v1”
base_path = "../../test/meshfiles/ugrid/geoflow-small/"
grid_path = base_path + "grid.nc"
data_path = base_path + "v1.nc"
uxds_ugrid = ux.open_dataset(grid_path, data_path)
uxds_ugrid['v1']
<xarray.UxDataArray 'v1' (time: 1, meshLayers: 20, n_node: 6000)> Size: 960kB array([[[-0.00976561, -0.00073953, -0.00263392, ..., -0.43890442, -0.32490516, -0.27510509], [-0.0144434 , 0.03304476, -0.00292205, ..., -0.03070408, 0.09485523, -0.13563532], [ 0.01230115, -0.00757331, 0.01929995, ..., 0.48444664, -0.42457346, 0.12704078], ..., [ 0.03957743, 0.12801929, -0.0235884 , ..., 0.2553294 , -0.35610371, 0.18069488], [-0.03116356, -0.07373909, -0.0328255 , ..., 0.34230183, -0.46541344, 0.11903046], [-0.06851197, -0.10967764, 0.02253516, ..., -0.06210308, 0.14325973, 0.03282873]]]) Coordinates: * time (time) float64 8B 13.0 Dimensions without coordinates: meshLayers, n_node Attributes: units: standard_name: mesh: mesh location: node
Using the UxDataArray.plot()
Accessor#
For face-centered data, the default plotting method returns a rasterized polygon plot.
uxds_mpas['bottomDepth'].plot(title="Default UXDataArray Plot for Face-Centered Data", height=350, width=700)
Vector Polygon Plots#
We can plot each face as a shaded polygon using the UxDataArray.plot.polygons()
method.
Since “bottomDepth” is face-centered, we can use it to shade each polygon.
uxds_mpas['bottomDepth'].plot.polygons(title="Bottom Depth Polygon Plot", height=350, width=700)
Since, “v1” is a node-centered variable, we need to first transform it to be face-centered. We can easily do this by computing the nodal average, which takes the average of all the nodes to obtain face-centered values.
uxds_ugrid['v1'][0][0].nodal_average().plot.polygons(cmap='coolwarm', title="V1 Nodal Average Polygon Plot", height=350, width=700)
Excluding Antimeridian Faces#
The plots above identify and split polygons that have edges that cross the antimeridian. This operation can be costly for large datasets, so it’s suggested to set the exclude_antimeridian
paramter to True
when working with large datasets.
(uxds_mpas['bottomDepth'].plot.polygons(title="Bottom Depth Polygon Plot (Including Antimeridian)", height=350, width=700) + \
uxds_mpas['bottomDepth'].plot.polygons(exclude_antimeridian=True, title="Bottom Depth Polygon Plot (Excluding Antimeridian)", height=350, width=700)).cols(1)
(uxds_ugrid['v1'][0][0].nodal_average().plot.polygons(cmap='coolwarm', title="V1 Nodal Average Polygon Plot (Including Antimeridian)", height=350, width=700) + \
uxds_ugrid['v1'][0][0].nodal_average().plot.polygons(exclude_antimeridian=True, cmap='coolwarm', title="V1 Nodal Average Polygon Plot (Excluding Antimeridian)", height=350, width=700)).cols(1)
Rasterized Polygon Plots#
Plotting Vector Polygons is not feasible for large datasets and can be extremely slow, so in addition to the UxDataArray.plot.polygons
method, UXarray supports quickly rasterizing the polygons into a fixed-grid using the UxDataArray.plot.rasterize(method="polygon")
function.
uxds_mpas['bottomDepth'].plot.rasterize(method='polygon', title="Bottom Depth Raster Polygon Plot", height=350, width=700)
uxds_ugrid['v1'][0][0].nodal_average().plot.rasterize(method='polygon', cmap='coolwarm', title="V1 Nodal Average Raster Polygon Plot", height=350, width=700)#
Selecting an appropriate pixel_ratio
#
You can specify a pixel_ratio
value to tweak the resolution of the rasterization. Higher values will lead to a shaper image, with lower values producing highly pixalated plots.
(uxds_mpas['bottomDepth'].plot.rasterize(method='polygon', title="Bottom Depth Raster Polygon Plot (0.5 Pixel Ratio)", height=350, width=700, pixel_ratio = 0.5) + \
uxds_mpas['bottomDepth'].plot.rasterize(method='polygon', title="Bottom Depth Raster Polygon Plot (1.0 Pixel Ratio)", height=350, width=700, pixel_ratio = 1.0) + \
uxds_mpas['bottomDepth'].plot.rasterize(method='polygon', title="Bottom Depth Raster Polygon Plot (2.0 Pixel Ratio)", height=350, width=700, pixel_ratio = 2.0)).cols(1)
Overlaying Mesh Geometry on a Rasterized Plot#
We can overlay the mesh geometry using the Grid.plot.mesh()
method on top of the raster to see how rasterization approximates the geometry of our unstructured grid.
uxds_mpas['bottomDepth'].plot.rasterize(method='polygon', title="Bottom Depth Raster Polygon Plot (1.0 Pixel Ratio) with Mesh", height=350, width=700, pixel_ratio = 1.0, xlim=(-40, 40), ylim=(-20, 20)) * \
uxds_mpas.uxgrid.plot.mesh(color="Black")