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")