What's Next - Software - Production Visualization

This is part of a follow-on conversation from the “What’s next for Pangeo” discussion that took place 2023-12-06. It is part of the overarching software topic.

We’re decent and visualizing data during data exploration. It might also be useful to think through production visualization with things like movies or higher quality renderings. This is useful both to give educational understanding, and also to inspire others to act, either by joining the effort or through funding.

There’s probably space here for some projects that try to create truly impressive looking visuals (2D, 3D, motion). This could have a strong impact on the perception of this space beyond developers.

What can this group do? Some ideas

  • Identify feasible projects in this space
  • Volunteer, or identify specific individuals that have the expertise and motivation to explore this area
4 Likes

I find that I use this video a bunch in conversation with sales prospects. It was definitely worth the effort in building it. I’d love to see Pangeo become great at producing visualizations like this. I personally probably won’t spend any time building on this topic though.

2 Likes

Just wanted to share a relevant discussion:

hvPlot and HoloViews should be well set up to make animations, at least with the Matplotlib backend. Once you have a HoloMap from some xarray dataset (e.g. over time), you call hv.output as shown at Plotting with Matplotlib — HoloViews v1.18.1. That example is clearly needing some love, e.g. the resolution is atrocious! @ahuang11 could maybe look at it and generate a much higher-resolution version from one of the xarray sample datasets and paste that here as an example, so we can see how close that is to what people want? It won’t support 3D flythroughs, but it’s a two-liner to do what Matt is showing above.

1 Like

It outputs in a format that YouTube accepts as upload I’m hoping?

Currently supported types are GIF and MP4, and at least the latter is good for YouTube.

Here’s a minimal example:

import hvplot.xarray
import holoviews as hv
import xarray as xr
hv.extension("matplotlib")

ds = xr.tutorial.open_dataset("air_temperature").isel(time=slice(25))
map = ds.hvplot.image(x="lon", y="lat", dynamic=False)
hv.save(map, "air.mp4")

(Originally output as mp4, I converted to GIF)
ezgif.com-video-to-gif

Here’s the more polished example:

import hvplot.xarray
import holoviews as hv
import xarray as xr
hv.extension("matplotlib")

ds = xr.tutorial.open_dataset("air_temperature").isel(time=slice(25))
map = ds.hvplot.image(
    x="lon", y="lat", features=["coastline", "borders"],
    cmap="Reds_r", clim=(220, 320), clabel="Temperature [K]", dynamic=False
).opts(fig_size=200)
hv.save(map, "air.mp4")

ezgif.com-video-to-gif (1)

A workaround for bokeh plots to output MP4 is use the player widget and manually screen record (or simply export as GIF and then convert to MP4 thru ffmpeg)).

import hvplot.xarray
import xarray as xr

ds = xr.tutorial.open_dataset("air_temperature").isel(time=slice(25))
map = ds.hvplot.image(
    x="lon", y="lat", features=["coastline", "borders"],
    cmap="Reds_r", clim=(220, 320), clabel="Temperature [K]", widget_type="scrubber", widget_location="bottom"
)
map

ezgif.com-video-to-gif (2)

4 Likes

Thanks! Just to add some notes on the above, for people not familiar with this code:

  • The clim option added to the second plot is what makes the colorbar not jump around between frames; it chooses a fixed range for the colorbar.
  • dynamic=False is what makes the output be a “HoloMap”, which is what can be rendered as a fixed video rather than as something dynamically computed and thus viewable only with widgets.
  • Not sure why there is so much whitespace?

BTW, it looks like Panel will soon have better support for rendering Bokeh plots to images based on Playwright; presumably that can be used to make videos of Bokeh plots too?

2 Likes

Amazing! That seems really easy!

Would this code snippet work with larger-than-memory dask data?

I am not entirely sure about Playwright, but this seems related?

Okay, it seems possible Videos | Playwright Python

Would this code snippet work with larger-than-memory dask data?

If exporting, I am not certain / haven’t tried. If dynamic + scrubber, it’ll likely work?

It’d also be interesting to see if it works with Kerchunk
https://projectpythia.org/kerchunk-cookbook/notebooks/case_studies/Streaming_Visualizations_with_Hvplot_Datashader.html

1 Like

I don’t have larger-than-memory or distributed xarray data handy to test with, but I don’t know of any reason it wouldn’t work. I believe HoloMaps will be lazy when the underlying data structure is (e.g. for a dask-backed xarray). Please try it out and raise a bug report if you run into problems (and post code here if it works).

1 Like

One issue encountered:

I was suggesting a HoloMap rather than a DynamicMap. I don’t think that would destroy the laziness for Dask-managed data, but could be wrong.

Cool, I was able to export a MP4 (later manually converted to GIF to upload here) using Pangeo Forge, Kerchunk, Datashader, and hvPlot (dynamic=False); took 22 minutes though for 731 frames; forgot to set a colorbar limit here though. Also, one thing I’m not sure how to do is control the animation speed and crop whitespace.

ezgif.com-video-to-gif (3)

import os
import time

import apache_beam as beam
import fsspec
import hvplot.xarray
import xarray as xr
from pangeo_forge_recipes.patterns import ConcatDim, FilePattern
from pangeo_forge_recipes.transforms import (
    OpenWithKerchunk,
    WriteCombinedReference,
)

# Constants
target_root = "references"
store_name = "Pangeo_Forge"
full_path = os.path.join(target_root, store_name, "reference.json")
years = list(range(1979, 1980))
time_dim = ConcatDim("time", keys=years)


# Functions
def format_function(time):
    return f"http://www.northwestknowledge.net/metdata/data/bi_{time}.nc"


# Patterns
pattern = FilePattern(format_function, time_dim, file_type="netcdf4")
pattern = pattern.prune()

# Apache Beam transforms
transforms = (
    beam.Create(pattern.items())
    | OpenWithKerchunk(file_type=pattern.file_type)
    | WriteCombinedReference(
        target_root=target_root,
        store_name=store_name,
        concat_dims=["day"],
        identical_dims=["lat", "lon", "crs"],
    )
)

# Constants
target_root = "references"
store_name = "Pangeo_Forge"
full_path = os.path.join(target_root, store_name, "reference.json")
years = list(range(1979, 1980))
time_dim = ConcatDim("time", keys=years)


# Functions
def format_function(time):
    return f"http://www.northwestknowledge.net/metdata/data/bi_{time}.nc"


# Patterns
pattern = FilePattern(format_function, time_dim, file_type="netcdf4")
pattern = pattern.prune()

# Apache Beam transforms
transforms = (
    beam.Create(pattern.items())
    | OpenWithKerchunk(file_type=pattern.file_type)
    | WriteCombinedReference(
        target_root=target_root,
        store_name=store_name,
        concat_dims=["day"],
        identical_dims=["lat", "lon", "crs"],
    )
)


import holoviews as hv

hv.extension("matplotlib")

mapper = fsspec.get_mapper(
    "reference://",
    fo=full_path,
    remote_protocol="http",
)
ds_kerchunk = xr.open_dataset(
    mapper, engine="zarr", decode_coords="all", backend_kwargs={"consolidated": False}
)
hv.save(
    ds_kerchunk
    .hvplot("lon", "lat", rasterize=True, dynamic=False)
    .opts(fig_size=200),
    "example.mp4",
    fmt="mp4",
)
2 Likes

We just published a new blog post on some recent progress on web-based Zarr visualization - Making our open source mapping toolkit more flexible – CarbonPlan! Also @aimeeb will be leading a presentation on Next Generation web based visualization at AGU on Thursday - AGU23.

On a more personal note - PyGMT is amazing at creating production quality maps. I haven’t had much time lately to work on it, but the developer community is super friendly and I’d encourage anyone interested in contributing to engage through their GitHub issues.

4 Likes

hv.output and hv.save accept an fps argument that can be used to control the speed. I have no idea why all that whitespace is there!