UXARRAY / uxarray

Xarray extension for unstructured climate and global weather data analysis and visualization.
https://uxarray.readthedocs.io/
Apache License 2.0
159 stars 32 forks source link

Support for contour plots #366

Open cdeciampa opened 1 year ago

cdeciampa commented 1 year ago

I assume this is way down the pipeline, but support for contour plots would be great. I think it would have to do with the way the underlying polygons are treated (by combining them or something?). I took a swing at contours with holoviews' hv.Polygons and hv.Contours, but they were both the same result and only plotted the outlines of the polygons. This is supposed to be vertical wind shear contours overlaid on top of outgoing longwave radiation.

storm_0755_olr_shear_0920200006

Additionally, it doesn't look like labeling contour plots is a feature supported by holoviews yet.

philipc2 commented 1 year ago

support for contour plots

This is on our list of priorities for visualization and something our Intern @ifranda is currently exploring.

vertical wind shear contours

Can you explain further in detail how this data stored? Specifically, if the contours are defined as a Path or Polygon, it should be possible to overlay the lines with any other data variable. However, if the data is vertex/face/edge centered, it would require some additional processing to compute and visualize the contours (regridding to structured or triangulating the polygons and using tricontourf).

cdeciampa commented 1 year ago

These data are located on the face (it's CAM5-SE) and are plotted as polygons. I'm still using PolyMesh, so I fed the spatialpandas dataframe to hv.Polygons for one attempt and hv.Contours for the other attempt. I think the image I shared is the output using hv.Contours, but like I said, the two outputs look identical.

it would require some additional processing to compute and visualize the contours (regridding to structured or triangulating the polygons and using tricontourf)

The approach I was considering is: say you have 5 touching polygons in a given area and the values for all 5 polygons are within a contour bound that you set (e.g., the values are between 10 and 20), then you combine the vertices of these polygons to form 1 large polygon. So, instead of 5 polygons in that area, you'd only have 1 and the contour would be the outline of this polygon. It would avoid interpolation, but it would be clunky under the hood and also still be bound to the specific shape of the underlying grid instead of showing smooth contours.

Either way, I'm excited to see what comes of this.

philipc2 commented 1 year ago

@cdeciampa

Thanks for the explanation! The implementation you describe sounds like it would work, and would allow for some neat contour lines along the boundaries of the sets of polygons since it avoids any interpolation.

I've been playing around a bit using Datashader paired with Holoviews to obtain the contours from a rasterized image of our grid, and it seems to be showing some promise. I'll need to mess it with it more tomorrow.

Original Plot

image

Plot with Contours

image

# using code from PR 354
uxds = ux.open_dataset(grid_path, data_path)
gdf = uxds['psi'].to_gdf()

# obtain raster 
cvs = ds.Canvas(plot_width=2000, plot_height=1000)
agg = cvs.polygons(gdf, geometry='geometry', agg=ds.mean('psi'))

# store as a Holoviews Image 
hv_img = hv.element.raster.Image(agg)
hv_img.opts(cmap='Greys')

# taken from Holoviews Contour Example
example = hv_img * hv.operation.contours(hv_img, levels=6)
example.opts(opts.Contours(cmap='plasma', colorbar=True, line_width=3))
example.opts(width=1000, height=500)

While this does look quite clunky, I bet that on higher-resolution datasets the lines would look much smoother.

philipc2 commented 1 year ago

Additionally, here's the rasterized contours overlayed with the non-rasterized grid

image

cdeciampa commented 1 year ago

This looks great! I'll see if I can apply this method to the 3 km MPAS output I have tomorrow so we can both see what it looks like for a hi-res grid.

cdeciampa commented 1 year ago

mpas_olr_shear_0920200006

This is exactly what I was looking to do, thanks!

import datashader as ds
from holoviews import opts

# Sets up basemap
cvs = ds.Canvas(plot_width=w, plot_height=h, x_range=x_range, y_range=y_range)

# Adds OLR polygons
agg_olr = cvs.polygons(c_olr_dfs[1], geometry='geometry', agg=ds.mean('faces'))

# Rasterizes layer
hv_img_olr = hv.element.raster.Image(agg_olr).opts(**olr_plot_opts)

# Adds geographic layers
hv_img_olr = hv_img_olr * coastline_layer * states_layer

# Adds shear polygons
agg_shear = cvs.polygons(c_shear_dfs[1], geometry='geometry', agg=ds.mean('faces'))

# Rasterizes layer
hv_img_shear = hv.element.raster.Image(agg_shear).opts(**shear_plot_opts)

# Retrieves contours from shear layer and adds to plot
shear_contours = hv_img_olr * hv.operation.contours(hv_img_shear, levels=shear_levs)
shear_contours = shear_contours.opts(opts.Contours(**shear_cmap_opts, line_width=2.0))
shear_contours = shear_contours.opts(**plot_opts)