MPAS Ocean¶
In this recipe, you will learn how to create visualizations of global ocean depth data using MPAS-Ocean model output. The recipe includes techniques for both global-scale visualization and regional analysis, with a specific focus on the Gulf of Mexico region.
Visualization Objectives¶
This recipe will guide you through creating:
Global ocean visualizations using the Robinson projection with raster-based plotting
Regional depth analysis of the Gulf of Mexico using vector polygons
import cartopy.crs as ccrs
import uxarray as uxgrid_path = "../../meshfiles/oQU120.grid.nc"
data_path = "../../meshfiles/oQU120.data.nc"
uxds = ux.open_dataset(grid_path, data_path)Data Preparation¶
Global Ocean Data¶
The global MPAS-Ocean grid data requires no preprocessing for our visualization purposes, as it already encompasses the complete global domain.
bottom_depth = uxds["bottomDepth"]
bottom_depth.uxgrid.n_face28571Regional Focus: Gulf of Mexico¶
To create a detailed regional visualization of the Gulf of Mexico, we’ll perform a spatial subset of our global data. By applying a bounding circle operation centered at 90°W longitude and 25°N latitude with a 9-degree radius, we can focus our analysis specifically on the Gulf region. This subsetting approach significantly reduces the computational demands of our visualization routines.
bottom_depth_gulf = bottom_depth.subset.bounding_circle((-90.0, 25.0), r=9)
bottom_depth_gulf.uxgrid.n_face156Global Ocean Depth Visualization¶
For our global visualization, we selected the Robinson projection combined with raster plotting to effectively display worldwide ocean depth data. The 4.0 pixel ratio setting ensures clear resolution while maintaining efficient performance across the entire global domain.
bottom_depth.plot.polygons(
rasterize=True,
backend="matplotlib",
projection=ccrs.Robinson(),
coastline="110m",
cmap="Blues",
width=1000,
pixel_ratio=4.0,
title="Global Ocean Depth",
)Regional Visualization: Gulf of Mexico¶
For our detailed analysis of the Gulf of Mexico region, we transition to a vector-based polygon visualization approach. This shift from our global visualization strategy is driven by the reduced computational demands of our regional subset, which contains only 164 faces. Vector polygons offer several advantages at this scale, including precise boundary representation and enhanced detail visualization.
bottom_depth_gulf.plot(
rasterize=False,
backend="matplotlib",
projection=ccrs.Robinson(),
features=["states", "coastline"],
cmap="Blues",
width=500,
title="Gulf of Mexico Depth",
)/home/runner/micromamba/envs/unstructured-grid-viz-cookbook-dev/lib/python3.14/site-packages/cartopy/io/__init__.py:263: DownloadWarning: Downloading: https://naturalearth.s3.amazonaws.com/110m_cultural/ne_110m_admin_1_states_provinces_lakes.zip
warnings.warn(f'Downloading: {url}', DownloadWarning)
---------------------------------------------------------------------------
KeyError Traceback (most recent call last)
File ~/micromamba/envs/unstructured-grid-viz-cookbook-dev/lib/python3.14/site-packages/holoviews/core/dimension.py:1493, in Dimensioned._repr_mimebundle_(self, include, exclude)
1486 def _repr_mimebundle_(self, include=None, exclude=None):
1487 """Resolves the class hierarchy for the class rendering the
1488 object using any display hooks registered on Store.display
1489 hooks. The output of all registered display_hooks is then
1490 combined and returned.
1491
1492 """
-> 1493 return Store.render(self)
File ~/micromamba/envs/unstructured-grid-viz-cookbook-dev/lib/python3.14/site-packages/holoviews/core/options.py:1519, in Store.render(cls, obj)
1517 data, metadata = {}, {}
1518 for hook in hooks:
-> 1519 ret = hook(obj)
1520 if ret is None:
1521 continue
File ~/micromamba/envs/unstructured-grid-viz-cookbook-dev/lib/python3.14/site-packages/holoviews/ipython/display_hooks.py:330, in pprint_display(obj)
328 if not ip.display_formatter.formatters["text/plain"].pprint:
329 return None
--> 330 return display(obj, raw_output=True)
File ~/micromamba/envs/unstructured-grid-viz-cookbook-dev/lib/python3.14/site-packages/holoviews/ipython/display_hooks.py:298, in display(obj, raw_output, **kwargs)
296 elif isinstance(obj, (CompositeOverlay, ViewableElement)):
297 with option_state(obj):
--> 298 output = element_display(obj)
299 elif isinstance(obj, (Layout, NdLayout, AdjointLayout)):
300 with option_state(obj):
File ~/micromamba/envs/unstructured-grid-viz-cookbook-dev/lib/python3.14/site-packages/holoviews/ipython/display_hooks.py:193, in display_hook.<locals>.wrapped(element)
191 try:
192 max_frames = OutputSettings.options["max_frames"]
--> 193 mimebundle = fn(element, max_frames=max_frames)
194 if mimebundle is None:
195 return {}, {}
File ~/micromamba/envs/unstructured-grid-viz-cookbook-dev/lib/python3.14/site-packages/holoviews/ipython/display_hooks.py:231, in element_display(element, max_frames)
228 if type(element) not in Store.registry[backend]:
229 return None
--> 231 return render(element)
File ~/micromamba/envs/unstructured-grid-viz-cookbook-dev/lib/python3.14/site-packages/holoviews/ipython/display_hooks.py:62, in render(obj, **kwargs)
59 if renderer.fig == "pdf":
60 renderer = renderer.instance(fig="png")
---> 62 return renderer.components(obj, **kwargs)
File ~/micromamba/envs/unstructured-grid-viz-cookbook-dev/lib/python3.14/site-packages/holoviews/plotting/renderer.py:439, in Renderer.components(self, obj, fmt, comm, **kwargs)
437 plot = obj
438 else:
--> 439 plot, fmt = self._validate(obj, fmt)
441 if not isinstance(plot, Viewable):
442 html = self._figure_data(plot, fmt, as_script=True, **kwargs)
File ~/micromamba/envs/unstructured-grid-viz-cookbook-dev/lib/python3.14/site-packages/holoviews/plotting/renderer.py:370, in Renderer._validate(self, obj, fmt, **kwargs)
368 plot = HoloViewsPane(obj, center=self.center, backend=self.backend, renderer=self)
369 else:
--> 370 plot = self.get_plot(obj, renderer=self, **kwargs)
372 all_formats = set(fig_formats + holomap_formats)
373 if fmt not in all_formats:
File ~/micromamba/envs/unstructured-grid-viz-cookbook-dev/lib/python3.14/site-packages/holoviews/plotting/renderer.py:300, in Renderer.get_plot(self_or_cls, obj, doc, renderer, comm, **kwargs)
296 defaults = [kd.default for kd in plot.dimensions]
297 init_key = tuple(
298 v if d is None else d for v, d in zip(plot.keys[0], defaults, strict=None)
299 )
--> 300 plot.update(init_key)
301 else:
302 plot = obj
File ~/micromamba/envs/unstructured-grid-viz-cookbook-dev/lib/python3.14/site-packages/holoviews/plotting/mpl/plot.py:335, in MPLPlot.update(self, key)
333 def update(self, key):
334 if len(self) == 1 and key in (0, self.keys[0]) and not self.drawn:
--> 335 return self.initialize_plot()
336 return self.__getitem__(key)
File ~/micromamba/envs/unstructured-grid-viz-cookbook-dev/lib/python3.14/site-packages/holoviews/plotting/mpl/plot.py:64, in mpl_rc_context.<locals>.wrapper(self, *args, **kwargs)
62 def wrapper(self, *args, **kwargs):
63 with _rc_context(self.fig_rcparams):
---> 64 return f(self, *args, **kwargs)
File ~/micromamba/envs/unstructured-grid-viz-cookbook-dev/lib/python3.14/site-packages/holoviews/plotting/mpl/element.py:1444, in OverlayPlot.initialize_plot(self, ranges)
1442 ranges = self.compute_ranges(self.hmap, key, ranges)
1443 for k, subplot in self.subplots.items():
-> 1444 subplot.initialize_plot(ranges=ranges)
1445 if isinstance(element, CompositeOverlay):
1446 frame = element.get(k, None)
File ~/micromamba/envs/unstructured-grid-viz-cookbook-dev/lib/python3.14/site-packages/holoviews/plotting/mpl/plot.py:64, in mpl_rc_context.<locals>.wrapper(self, *args, **kwargs)
62 def wrapper(self, *args, **kwargs):
63 with _rc_context(self.fig_rcparams):
---> 64 return f(self, *args, **kwargs)
File ~/micromamba/envs/unstructured-grid-viz-cookbook-dev/lib/python3.14/site-packages/holoviews/plotting/mpl/element.py:685, in ElementPlot.initialize_plot(self, ranges)
683 if self.show_legend:
684 style["label"] = element.label
--> 685 handles, axis_kwargs = self.render_artists(element, ranges, style, ax)
686 self.handles.update(handles)
688 trigger = self._trigger
File ~/micromamba/envs/unstructured-grid-viz-cookbook-dev/lib/python3.14/site-packages/holoviews/plotting/mpl/element.py:656, in ElementPlot.render_artists(self, element, ranges, style, ax)
655 def render_artists(self, element, ranges, style, ax):
--> 656 plot_data, plot_kwargs, axis_kwargs = self.get_data(element, ranges, style)
657 legend = plot_kwargs.pop("cat_legend", None)
658 with abbreviated_exception():
File ~/micromamba/envs/unstructured-grid-viz-cookbook-dev/lib/python3.14/site-packages/geoviews/plotting/mpl/__init__.py:237, in GeoPlot.get_data(self, element, ranges, style)
235 def get_data(self, element, ranges, style):
236 if self._project_operation and self.geographic:
--> 237 element = self._project_operation(element, projection=self.projection)
238 return super().get_data(element, ranges, style)
File ~/micromamba/envs/unstructured-grid-viz-cookbook-dev/lib/python3.14/site-packages/param/parameterized.py:6426, in ParameterizedFunction.__new__(class_, *args, **params)
6424 inst = class_.instance()
6425 inst.param._set_name(class_.__name__)
-> 6426 return inst.__call__(*args, **params)
File ~/micromamba/envs/unstructured-grid-viz-cookbook-dev/lib/python3.14/site-packages/holoviews/core/operation.py:236, in Operation.__call__(self, element, **kwargs)
234 kwargs["link_dataset"] = self._propagate_dataset
235 kwargs["link_inputs"] = self.p.link_inputs
--> 236 return element.apply(self, **kwargs)
File ~/micromamba/envs/unstructured-grid-viz-cookbook-dev/lib/python3.14/site-packages/holoviews/core/accessors.py:47, in AccessorPipelineMeta.pipelined.<locals>.pipelined_call(*args, **kwargs)
44 inst._obj._in_method = True
46 try:
---> 47 result = __call__(*args, **kwargs)
49 if not in_method:
50 init_op = factory.instance(
51 output_type=type(inst),
52 kwargs={"mode": getattr(inst, "mode", None)},
53 )
File ~/micromamba/envs/unstructured-grid-viz-cookbook-dev/lib/python3.14/site-packages/holoviews/core/accessors.py:228, in Apply.__call__(self, apply_function, streams, link_inputs, link_dataset, dynamic, per_element, **kwargs)
226 if hasattr(apply_function, "dynamic"):
227 inner_kwargs["dynamic"] = False
--> 228 new_obj = apply_function(self._obj, **inner_kwargs)
229 if (
230 link_dataset
231 and isinstance(self._obj, Dataset)
232 and isinstance(new_obj, Dataset)
233 and new_obj._dataset is None
234 ):
235 new_obj._dataset = self._obj.dataset
File ~/micromamba/envs/unstructured-grid-viz-cookbook-dev/lib/python3.14/site-packages/holoviews/core/operation.py:230, in Operation.__call__(self, element, **kwargs)
226 return element.clone([(k, self._apply(el, key=k)) for k, el in element.items()])
227 elif (self._per_element and isinstance(element, Element)) or (
228 not self._per_element and isinstance(element, ViewableElement)
229 ):
--> 230 return self._apply(element)
231 elif "streams" not in kwargs:
232 kwargs["streams"] = self.p.streams
File ~/micromamba/envs/unstructured-grid-viz-cookbook-dev/lib/python3.14/site-packages/holoviews/core/operation.py:156, in Operation._apply(self, element, key)
154 if not in_method:
155 element._in_method = True
--> 156 ret = self._process(element, key)
157 if hasattr(element, "_in_method") and not in_method:
158 element._in_method = in_method
File ~/micromamba/envs/unstructured-grid-viz-cookbook-dev/lib/python3.14/site-packages/geoviews/operation/projection.py:72, in _project_operation._process(self, element, key)
71 def _process(self, element, key=None):
---> 72 return element.map(self._process_element, self.supported_types)
File ~/micromamba/envs/unstructured-grid-viz-cookbook-dev/lib/python3.14/site-packages/holoviews/core/data/__init__.py:234, in PipelineMeta.pipelined.<locals>.pipelined_fn(*args, **kwargs)
231 inst._in_method = True
233 try:
--> 234 result = method_fn(*args, **kwargs)
236 op = method_op.instance(
237 input_type=type(inst),
238 method_name=method_name,
239 args=list(args[1:]),
240 kwargs=kwargs,
241 )
243 if not in_method:
File ~/micromamba/envs/unstructured-grid-viz-cookbook-dev/lib/python3.14/site-packages/holoviews/core/data/__init__.py:1349, in Dataset.map(self, *args, **kwargs)
1347 @wraps(LabelledData.map)
1348 def map(self, *args, **kwargs):
-> 1349 return super().map(*args, **kwargs)
File ~/micromamba/envs/unstructured-grid-viz-cookbook-dev/lib/python3.14/site-packages/holoviews/core/dimension.py:846, in LabelledData.map(self, map_fn, specs, clone)
844 return deep_mapped
845 else:
--> 846 return map_fn(self) if applies else self
File ~/micromamba/envs/unstructured-grid-viz-cookbook-dev/lib/python3.14/site-packages/geoviews/operation/projection.py:89, in project_path._process_element(self, element)
86 crs = element.crs
87 proj = self.p.projection
88 if (isinstance(crs, ccrs.PlateCarree) and not isinstance(proj, ccrs.PlateCarree)
---> 89 and crs.proj4_params['lon_0'] != 0):
90 element = self.instance(projection=ccrs.PlateCarree())(element)
92 if isinstance(proj, ccrs.CRS) and not isinstance(proj, ccrs.Projection):
KeyError: 'lon_0':Overlay
.Polygons.I :Polygons [Longitude,Latitude] (bottomDepth)
.Coastline.I :Feature [Longitude,Latitude]
.States.I :Feature [Longitude,Latitude]