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']
/home/docs/checkouts/readthedocs.org/user_builds/uxarray/conda/v2024.02.0/lib/python3.11/site-packages/uxarray/core/utils.py:22: FutureWarning: The return type of `Dataset.dims` will be changed to return a set of dimension names in future, in order to be more consistent with `DataArray.dims`. To access a mapping from dimension names to lengths, please use `Dataset.sizes`.
if ds.dims[dim] == grid.n_face:
/home/docs/checkouts/readthedocs.org/user_builds/uxarray/conda/v2024.02.0/lib/python3.11/site-packages/uxarray/grid/grid.py:435: FutureWarning: The return type of `Dataset.dims` will be changed to return a set of dimension names in future, in order to be more consistent with `DataArray.dims`. To access a mapping from dimension names to lengths, please use `Dataset.sizes`.
return self._ds.dims["n_face"]
/home/docs/checkouts/readthedocs.org/user_builds/uxarray/conda/v2024.02.0/lib/python3.11/site-packages/uxarray/core/utils.py:24: FutureWarning: The return type of `Dataset.dims` will be changed to return a set of dimension names in future, in order to be more consistent with `DataArray.dims`. To access a mapping from dimension names to lengths, please use `Dataset.sizes`.
elif ds.dims[dim] == grid.n_node:
/home/docs/checkouts/readthedocs.org/user_builds/uxarray/conda/v2024.02.0/lib/python3.11/site-packages/uxarray/grid/grid.py:429: FutureWarning: The return type of `Dataset.dims` will be changed to return a set of dimension names in future, in order to be more consistent with `DataArray.dims`. To access a mapping from dimension names to lengths, please use `Dataset.sizes`.
return self._ds.dims["n_node"]
/home/docs/checkouts/readthedocs.org/user_builds/uxarray/conda/v2024.02.0/lib/python3.11/site-packages/uxarray/core/utils.py:26: FutureWarning: The return type of `Dataset.dims` will be changed to return a set of dimension names in future, in order to be more consistent with `DataArray.dims`. To access a mapping from dimension names to lengths, please use `Dataset.sizes`.
elif ds.dims[dim] == grid.n_edge:
/home/docs/checkouts/readthedocs.org/user_builds/uxarray/conda/v2024.02.0/lib/python3.11/site-packages/uxarray/grid/grid.py:444: FutureWarning: The return type of `Dataset.dims` will be changed to return a set of dimension names in future, in order to be more consistent with `DataArray.dims`. To access a mapping from dimension names to lengths, please use `Dataset.sizes`.
return self._ds.dims["n_edge"]
<xarray.UxDataArray 'bottomDepth' (n_face: 1791)> 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']
/home/docs/checkouts/readthedocs.org/user_builds/uxarray/conda/v2024.02.0/lib/python3.11/site-packages/uxarray/core/utils.py:22: FutureWarning: The return type of `Dataset.dims` will be changed to return a set of dimension names in future, in order to be more consistent with `DataArray.dims`. To access a mapping from dimension names to lengths, please use `Dataset.sizes`.
if ds.dims[dim] == grid.n_face:
/home/docs/checkouts/readthedocs.org/user_builds/uxarray/conda/v2024.02.0/lib/python3.11/site-packages/uxarray/grid/grid.py:435: FutureWarning: The return type of `Dataset.dims` will be changed to return a set of dimension names in future, in order to be more consistent with `DataArray.dims`. To access a mapping from dimension names to lengths, please use `Dataset.sizes`.
return self._ds.dims["n_face"]
/home/docs/checkouts/readthedocs.org/user_builds/uxarray/conda/v2024.02.0/lib/python3.11/site-packages/uxarray/core/utils.py:24: FutureWarning: The return type of `Dataset.dims` will be changed to return a set of dimension names in future, in order to be more consistent with `DataArray.dims`. To access a mapping from dimension names to lengths, please use `Dataset.sizes`.
elif ds.dims[dim] == grid.n_node:
/home/docs/checkouts/readthedocs.org/user_builds/uxarray/conda/v2024.02.0/lib/python3.11/site-packages/uxarray/grid/grid.py:429: FutureWarning: The return type of `Dataset.dims` will be changed to return a set of dimension names in future, in order to be more consistent with `DataArray.dims`. To access a mapping from dimension names to lengths, please use `Dataset.sizes`.
return self._ds.dims["n_node"]
/home/docs/checkouts/readthedocs.org/user_builds/uxarray/conda/v2024.02.0/lib/python3.11/site-packages/uxarray/core/utils.py:26: FutureWarning: The return type of `Dataset.dims` will be changed to return a set of dimension names in future, in order to be more consistent with `DataArray.dims`. To access a mapping from dimension names to lengths, please use `Dataset.sizes`.
elif ds.dims[dim] == grid.n_edge:
/home/docs/checkouts/readthedocs.org/user_builds/uxarray/conda/v2024.02.0/lib/python3.11/site-packages/uxarray/grid/grid.py:444: FutureWarning: The return type of `Dataset.dims` will be changed to return a set of dimension names in future, in order to be more consistent with `DataArray.dims`. To access a mapping from dimension names to lengths, please use `Dataset.sizes`.
return self._ds.dims["n_edge"]
<xarray.UxDataArray 'v1' (time: 1, meshLayers: 20, n_node: 6000)> 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 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 Polygon plot.
uxds_mpas['bottomDepth'].plot(title="Default UXDataArray Plot for Face-Centered Data", height=350, width=700)
/home/docs/checkouts/readthedocs.org/user_builds/uxarray/conda/v2024.02.0/lib/python3.11/site-packages/uxarray/grid/grid.py:435: FutureWarning: The return type of `Dataset.dims` will be changed to return a set of dimension names in future, in order to be more consistent with `DataArray.dims`. To access a mapping from dimension names to lengths, please use `Dataset.sizes`.
return self._ds.dims["n_face"]
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)
/home/docs/checkouts/readthedocs.org/user_builds/uxarray/conda/v2024.02.0/lib/python3.11/site-packages/uxarray/plot/dataarray_plot.py:427: UserWarning: Including Antimeridian Polygons may lead to visual artifacts. It is suggested to keep 'exclude_antimeridian' set to True.
warnings.warn(
/home/docs/checkouts/readthedocs.org/user_builds/uxarray/conda/v2024.02.0/lib/python3.11/site-packages/uxarray/grid/grid.py:435: FutureWarning: The return type of `Dataset.dims` will be changed to return a set of dimension names in future, in order to be more consistent with `DataArray.dims`. To access a mapping from dimension names to lengths, please use `Dataset.sizes`.
return self._ds.dims["n_face"]
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)
/home/docs/checkouts/readthedocs.org/user_builds/uxarray/conda/v2024.02.0/lib/python3.11/site-packages/uxarray/grid/grid.py:435: FutureWarning: The return type of `Dataset.dims` will be changed to return a set of dimension names in future, in order to be more consistent with `DataArray.dims`. To access a mapping from dimension names to lengths, please use `Dataset.sizes`.
return self._ds.dims["n_face"]
/home/docs/checkouts/readthedocs.org/user_builds/uxarray/conda/v2024.02.0/lib/python3.11/site-packages/uxarray/plot/dataarray_plot.py:427: UserWarning: Including Antimeridian Polygons may lead to visual artifacts. It is suggested to keep 'exclude_antimeridian' set to True.
warnings.warn(
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)
/home/docs/checkouts/readthedocs.org/user_builds/uxarray/conda/v2024.02.0/lib/python3.11/site-packages/uxarray/plot/dataarray_plot.py:427: UserWarning: Including Antimeridian Polygons may lead to visual artifacts. It is suggested to keep 'exclude_antimeridian' set to True.
warnings.warn(
/home/docs/checkouts/readthedocs.org/user_builds/uxarray/conda/v2024.02.0/lib/python3.11/site-packages/uxarray/grid/grid.py:435: FutureWarning: The return type of `Dataset.dims` will be changed to return a set of dimension names in future, in order to be more consistent with `DataArray.dims`. To access a mapping from dimension names to lengths, please use `Dataset.sizes`.
return self._ds.dims["n_face"]
(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)
/home/docs/checkouts/readthedocs.org/user_builds/uxarray/conda/v2024.02.0/lib/python3.11/site-packages/uxarray/plot/dataarray_plot.py:427: UserWarning: Including Antimeridian Polygons may lead to visual artifacts. It is suggested to keep 'exclude_antimeridian' set to True.
warnings.warn(
/home/docs/checkouts/readthedocs.org/user_builds/uxarray/conda/v2024.02.0/lib/python3.11/site-packages/uxarray/grid/grid.py:435: FutureWarning: The return type of `Dataset.dims` will be changed to return a set of dimension names in future, in order to be more consistent with `DataArray.dims`. To access a mapping from dimension names to lengths, please use `Dataset.sizes`.
return self._ds.dims["n_face"]
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)
/home/docs/checkouts/readthedocs.org/user_builds/uxarray/conda/v2024.02.0/lib/python3.11/site-packages/uxarray/grid/grid.py:435: FutureWarning: The return type of `Dataset.dims` will be changed to return a set of dimension names in future, in order to be more consistent with `DataArray.dims`. To access a mapping from dimension names to lengths, please use `Dataset.sizes`.
return self._ds.dims["n_face"]
uxds_ugrid['v1'][0][0].nodal_average().plot.rasterize(method='polygon', cmap='coolwarm', title="V1 Nodal Average Raster Polygon Plot", height=350, width=700)#
/home/docs/checkouts/readthedocs.org/user_builds/uxarray/conda/v2024.02.0/lib/python3.11/site-packages/uxarray/grid/grid.py:435: FutureWarning: The return type of `Dataset.dims` will be changed to return a set of dimension names in future, in order to be more consistent with `DataArray.dims`. To access a mapping from dimension names to lengths, please use `Dataset.sizes`.
return self._ds.dims["n_face"]
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)
/home/docs/checkouts/readthedocs.org/user_builds/uxarray/conda/v2024.02.0/lib/python3.11/site-packages/uxarray/grid/grid.py:435: FutureWarning: The return type of `Dataset.dims` will be changed to return a set of dimension names in future, in order to be more consistent with `DataArray.dims`. To access a mapping from dimension names to lengths, please use `Dataset.sizes`.
return self._ds.dims["n_face"]
/home/docs/checkouts/readthedocs.org/user_builds/uxarray/conda/v2024.02.0/lib/python3.11/site-packages/uxarray/grid/grid.py:435: FutureWarning: The return type of `Dataset.dims` will be changed to return a set of dimension names in future, in order to be more consistent with `DataArray.dims`. To access a mapping from dimension names to lengths, please use `Dataset.sizes`.
return self._ds.dims["n_face"]
/home/docs/checkouts/readthedocs.org/user_builds/uxarray/conda/v2024.02.0/lib/python3.11/site-packages/uxarray/grid/grid.py:435: FutureWarning: The return type of `Dataset.dims` will be changed to return a set of dimension names in future, in order to be more consistent with `DataArray.dims`. To access a mapping from dimension names to lengths, please use `Dataset.sizes`.
return self._ds.dims["n_face"]
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")
/home/docs/checkouts/readthedocs.org/user_builds/uxarray/conda/v2024.02.0/lib/python3.11/site-packages/uxarray/grid/grid.py:435: FutureWarning: The return type of `Dataset.dims` will be changed to return a set of dimension names in future, in order to be more consistent with `DataArray.dims`. To access a mapping from dimension names to lengths, please use `Dataset.sizes`.
return self._ds.dims["n_face"]