Skip to article frontmatterSkip to article content
Site not loading correctly?

This may be due to an incorrect BASE_URL configuration. See the MyST Documentation for reference.

By: Kevin Goebbert

This example uses the declarative syntax available through the MetPy package to allow a more convenient method for creating simple maps of atmospheric data. To plot aboslute vorticity, the data is scaled and reassigned to the xarray object for use in the declarative plotting interface.

from datetime import datetime

import xarray as xr

from metpy.plots import declarative
from metpy.units import units
# Set date for desired dataset
dt = datetime(2012, 10, 31, 12)

# Open dataset from NCEI
ds = xr.open_dataset('https://www.ncei.noaa.gov/thredds/dodsC/'
                     f'model-gfs-g4-anl-files-old/{dt:%Y%m}/{dt:%Y%m%d}/'
                     f'gfsanl_4_{dt:%Y%m%d}_{dt:%H}00_000.grb2'
                     ).metpy.parse_cf()

# Subset Data to be just over CONUS
ds_us = ds.sel(lon=slice(360-160, 360-40), lat=slice(65, 10))
syntax error, unexpected WORD_WORD, expecting SCAN_ATTR or SCAN_DATASET or SCAN_ERROR
context: <!DOCTYPE^ HTML PUBLIC "-//IETF//DTD HTML 2.0//EN"><html><head><title>503 Service Unavailable</title></head><body><h1>Service Unavailable</h1><p>The server is temporarily unable to service yourrequest due to maintenance downtime or capacityproblems. Please try again later.</p><p>Additionally, a 404 Not Founderror was encountered while trying to use an ErrorDocument to handle the request.</p></body></html>
---------------------------------------------------------------------------
KeyError                                  Traceback (most recent call last)
File ~/micromamba/envs/metpy-cookbook/lib/python3.14/site-packages/xarray/backends/file_manager.py:219, in CachingFileManager._acquire_with_cache_info(self, needs_lock)
    218 try:
--> 219     file = self._cache[self._key]
    220 except KeyError:

File ~/micromamba/envs/metpy-cookbook/lib/python3.14/site-packages/xarray/backends/lru_cache.py:56, in LRUCache.__getitem__(self, key)
     55 with self._lock:
---> 56     value = self._cache[key]
     57     self._cache.move_to_end(key)

KeyError: [<class 'netCDF4._netCDF4.Dataset'>, ('https://www.ncei.noaa.gov/thredds/dodsC/model-gfs-g4-anl-files-old/201210/20121031/gfsanl_4_20121031_1200_000.grb2',), 'r', (('clobber', True), ('diskless', False), ('format', 'NETCDF4'), ('persist', False)), 'fd42aa58-053d-4b34-b5d5-bff14f238b1f']

During handling of the above exception, another exception occurred:

OSError                                   Traceback (most recent call last)
Cell In[2], line 5
      1 # Set date for desired dataset
      2 dt = datetime(2012, 10, 31, 12)
      3 
      4 # Open dataset from NCEI
----> 5 ds = xr.open_dataset('https://www.ncei.noaa.gov/thredds/dodsC/'
      6                      f'model-gfs-g4-anl-files-old/{dt:%Y%m}/{dt:%Y%m%d}/'
      7                      f'gfsanl_4_{dt:%Y%m%d}_{dt:%H}00_000.grb2'
      8                      ).metpy.parse_cf()

File ~/micromamba/envs/metpy-cookbook/lib/python3.14/site-packages/xarray/backends/api.py:613, in open_dataset(filename_or_obj, engine, chunks, cache, decode_cf, mask_and_scale, decode_times, decode_timedelta, use_cftime, concat_characters, decode_coords, drop_variables, create_default_indexes, inline_array, chunked_array_type, from_array_kwargs, backend_kwargs, **kwargs)
    601 decoders = _resolve_decoders_kwargs(
    602     decode_cf,
    603     open_backend_dataset_parameters=backend.open_dataset_parameters,
   (...)    609     decode_coords=decode_coords,
    610 )
    612 overwrite_encoded_chunks = kwargs.pop("overwrite_encoded_chunks", None)
--> 613 backend_ds = backend.open_dataset(
    614     filename_or_obj,
    615     drop_variables=drop_variables,
    616     **decoders,
    617     **kwargs,
    618 )
    619 ds = _dataset_from_backend_dataset(
    620     backend_ds,
    621     filename_or_obj,
   (...)    632     **kwargs,
    633 )
    634 return ds

File ~/micromamba/envs/metpy-cookbook/lib/python3.14/site-packages/xarray/backends/netCDF4_.py:771, in NetCDF4BackendEntrypoint.open_dataset(self, filename_or_obj, mask_and_scale, decode_times, concat_characters, decode_coords, drop_variables, use_cftime, decode_timedelta, group, mode, format, clobber, diskless, persist, auto_complex, lock, autoclose)
    749 def open_dataset(
    750     self,
    751     filename_or_obj: T_PathFileOrDataStore,
   (...)    768     autoclose=False,
    769 ) -> Dataset:
    770     filename_or_obj = _normalize_path(filename_or_obj)
--> 771     store = NetCDF4DataStore.open(
    772         filename_or_obj,
    773         mode=mode,
    774         format=format,
    775         group=group,
    776         clobber=clobber,
    777         diskless=diskless,
    778         persist=persist,
    779         auto_complex=auto_complex,
    780         lock=lock,
    781         autoclose=autoclose,
    782     )
    784     store_entrypoint = StoreBackendEntrypoint()
    785     with close_on_error(store):

File ~/micromamba/envs/metpy-cookbook/lib/python3.14/site-packages/xarray/backends/netCDF4_.py:529, in NetCDF4DataStore.open(cls, filename, mode, format, group, clobber, diskless, persist, auto_complex, lock, lock_maker, autoclose)
    525 else:
    526     manager = CachingFileManager(
    527         netCDF4.Dataset, filename, mode=mode, kwargs=kwargs, lock=lock
    528     )
--> 529 return cls(manager, group=group, mode=mode, lock=lock, autoclose=autoclose)

File ~/micromamba/envs/metpy-cookbook/lib/python3.14/site-packages/xarray/backends/netCDF4_.py:429, in NetCDF4DataStore.__init__(self, manager, group, mode, lock, autoclose)
    427 self._group = group
    428 self._mode = mode
--> 429 self.format = self.ds.data_model
    430 self._filename = self.ds.filepath()
    431 self.is_remote = is_remote_uri(self._filename)

File ~/micromamba/envs/metpy-cookbook/lib/python3.14/site-packages/xarray/backends/netCDF4_.py:538, in NetCDF4DataStore.ds(self)
    536 @property
    537 def ds(self):
--> 538     return self._acquire()

File ~/micromamba/envs/metpy-cookbook/lib/python3.14/site-packages/xarray/backends/netCDF4_.py:532, in NetCDF4DataStore._acquire(self, needs_lock)
    531 def _acquire(self, needs_lock=True):
--> 532     with self._manager.acquire_context(needs_lock) as root:
    533         ds = _nc4_require_group(root, self._group, self._mode)
    534     return ds

File ~/micromamba/envs/metpy-cookbook/lib/python3.14/contextlib.py:141, in _GeneratorContextManager.__enter__(self)
    139 del self.args, self.kwds, self.func
    140 try:
--> 141     return next(self.gen)
    142 except StopIteration:
    143     raise RuntimeError("generator didn't yield") from None

File ~/micromamba/envs/metpy-cookbook/lib/python3.14/site-packages/xarray/backends/file_manager.py:207, in CachingFileManager.acquire_context(self, needs_lock)
    204 @contextmanager
    205 def acquire_context(self, needs_lock: bool = True) -> Iterator[T_File]:
    206     """Context manager for acquiring a file."""
--> 207     file, cached = self._acquire_with_cache_info(needs_lock)
    208     try:
    209         yield file

File ~/micromamba/envs/metpy-cookbook/lib/python3.14/site-packages/xarray/backends/file_manager.py:225, in CachingFileManager._acquire_with_cache_info(self, needs_lock)
    223     kwargs = kwargs.copy()
    224     kwargs["mode"] = self._mode
--> 225 file = self._opener(*self._args, **kwargs)
    226 if self._mode == "w":
    227     # ensure file doesn't get overridden when opened again
    228     self._mode = "a"

File src/netCDF4/_netCDF4.pyx:2522
-> 2522 'Could not get source, probably due dynamically evaluated source code.'

File src/netCDF4/_netCDF4.pyx:2159
-> 2159 'Could not get source, probably due dynamically evaluated source code.'

OSError: [Errno -70] NetCDF: DAP server error: 'https://www.ncei.noaa.gov/thredds/dodsC/model-gfs-g4-anl-files-old/201210/20121031/gfsanl_4_20121031_1200_000.grb2'

Contour Intervals

Since absolute vorticity rarely goes below zero in the Northern Hemisphere, we can set up a list of contour levels that doesn’t include values near but greater than zero. The following code yields a list containing: [-8, -7, -6, -5, -4, -3, -2, -1, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45]

# Absolute Vorticity colors
# Use two different colormaps from matplotlib and combine into one color set
clevs_500_avor = list(range(-8, 1, 1))+list(range(8, 46, 1))

The Plot

Using the declarative interface in MetPy to plot the 500-hPa Geopotential Heights and Absolute Vorticity.

# Set Contour Plot Parameters
contour = declarative.ContourPlot()
contour.data = ds_us
contour.time = dt
contour.field = 'Geopotential_height_isobaric'
contour.level = 500 * units.hPa
contour.linecolor = 'black'
contour.linestyle = '-'
contour.linewidth = 2
contour.clabels = True
contour.contours = list(range(0, 20000, 60))

# Set Color-filled Contour Parameters
cfill = declarative.FilledContourPlot()
cfill.data = ds_us
cfill.time = dt
cfill.field = 'Absolute_vorticity_isobaric'
cfill.level = 500 * units.hPa
cfill.contours = clevs_500_avor
cfill.colormap = 'PuOr_r'
cfill.image_range = (-45, 45)
cfill.colorbar = 'horizontal'
cfill.scale = 1e5

# Panel for plot with Map features
panel = declarative.MapPanel()
panel.layout = (1, 1, 1)
panel.area = 'uslcc'
panel.projection = 'area'
panel.layers = ['coastline', 'states', 'borders']
panel.layers_edgecolor = ['black', 'grey', 'black']
panel.layers_linewidth = [1.25, .75, 1]
panel.title = (f'{cfill.level} GFS Geopotential Heights'
               f'and Absolute Vorticity at {dt}')
panel.plots = [cfill, contour]

# Bringing it all together
pc = declarative.PanelContainer()
pc.size = (15, 14)
pc.panels = [panel]

pc.show()