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.

Python-AWIPS Data Access

Introduction to Python-AWIPS

Python-AWIPS is a package that provides access to the AWIPS (Advanced Weather Interactive Processing System) data through the EDEX (Environmental Data EXchange) server. AWIPS is a weather display and analysis package developed by the National Weather Service for operational forecasting.

The DataAccessLayer is the primary interface for requesting meteorological data from an EDEX server. It provides methods for:

  • Discovering what data types are available

  • Exploring locations (models, sites, etc.)

  • Finding available parameters and levels

  • Querying available times

  • Retrieving the actual data

In this notebook, we’ll walk through the complete workflow from connecting to an EDEX server to getting data into an xarray Dataset that you can use for analysis and visualization.

Connecting to EDEX

The first step is to import the DataAccessLayer and establish a connection to an EDEX server. Unidata provides a publicly accessible cloud EDEX server for training and research purposes.

from awips.dataaccess import DataAccessLayer

# Connect to Unidata's cloud EDEX server
edex_url = "edex-cloud.unidata.ucar.edu"
DataAccessLayer.changeEDEXHost(edex_url)

print(f"Connected to EDEX server: {edex_url}")
Connected to EDEX server: edex-cloud.unidata.ucar.edu

Exploring Available Data Types

The getSupportedDatatypes() method returns a list of all data types available on the connected EDEX server. These represent different categories of meteorological data.

# Get and display all available data types
data_types = DataAccessLayer.getSupportedDatatypes()
data_types.sort()
list(data_types)
['acars', 'airep', 'binlightning', 'bufrmosavn', 'bufrmoseta', 'bufrmosgfs', 'bufrmoshpc', 'bufrmoslamp', 'bufrmosmrf', 'bufrua', 'climate', 'common_obs_spatial', 'dmw', 'gfe', 'gfeeditarea', 'grid', 'ldadmesonet', 'maps', 'modelsounding', 'obs', 'pirep', 'practicewarning', 'profiler', 'radar', 'radar_spatial', 'satellite', 'sfcobs', 'topo', 'warning']

Some commonly used data types include:

Data TypeDescription
gridGridded model data (GFS, NAM, RAP, HRRR, etc.)
obsSurface observations (METAR)
satelliteSatellite imagery
radarNEXRAD Level 3 radar data
modelsoundingModel sounding profiles
bufruaUpper air observations
warningNWS watches, warnings, and advisories

Creating a Data Request

To request data, we create a new data request object and specify what we want. The workflow typically involves:

  1. Create a new request with newDataRequest()

  2. Set the data type with setDatatype()

  3. Optionally set location, parameters, levels, etc.

For this example, we’ll request grid data, which contains model output like the GFS.

# Create a new data request for grid data
request = DataAccessLayer.newDataRequest()
request.setDatatype("grid")

print(f"Created request for datatype: grid")
Created request for datatype: grid

Exploring Available Data

Before we can request specific data, we need to know what’s available. The DataAccessLayer provides several methods for exploring the data catalog.

Available Locations

For grid data, “locations” correspond to different models or data sources. Use getAvailableLocationNames() to see what’s available.

# Get available locations (models) for grid data
locations = DataAccessLayer.getAvailableLocationNames(request)
locations.sort()
list(locations)
['AK-NAM11', 'BHP-ALPW', 'BLENDED-PCT_TPW-Sat', 'BLENDED-Rain_Rate-Sat', 'BLENDED-TPW-Sat', 'ESTOFS', 'ESTOFS-other', 'ETSS', 'FFG-ALR', 'FFG-FWR', 'FFG-KRF', 'FFG-MSR', 'FFG-ORN', 'FFG-PTR', 'FFG-RHA', 'FFG-RSA', 'FFG-STR', 'FFG-TAR', 'FFG-TIR', 'FFG-TUA', 'GFS1p0', 'GFS20', 'GOES-East_Gridded_Turbulence', 'GOES-West_Gridded_Turbulence', 'GlobalWave', 'HFR-EAST_6KM', 'HFR-EAST_PR_6KM', 'HFR-US_EAST_DELAWARE_1KM', 'HFR-US_EAST_FLORIDA_2KM', 'HFR-US_EAST_NORTH_2KM', 'HFR-US_EAST_SOUTH_2KM', 'HFR-US_EAST_VIRGINIA_1KM', 'HFR-US_HAWAII_1KM', 'HFR-US_HAWAII_2KM', 'HFR-US_HAWAII_6KM', 'HFR-US_WEST_500M', 'HFR-US_WEST_CENCAL_2KM', 'HFR-US_WEST_LOSANGELES_1KM', 'HFR-US_WEST_LOSOSOS_1KM', 'HFR-US_WEST_NORTH_2KM', 'HFR-US_WEST_SANFRAN_1KM', 'HFR-US_WEST_SOCAL_2KM', 'HFR-US_WEST_WASHINGTON_1KM', 'HFR-WEST_6KM', 'HPCqpfNDFD', 'HREF-AK', 'HREF-HI', 'HREF-SJU', 'HREF-US', 'HRRR', 'HRRR-Smoke', 'HiResW-ARW', 'HiResW-ARW-AK', 'HiResW-ARW-GU', 'HiResW-ARW-HI', 'HiResW-ARW-SJU', 'HiResW-FV3', 'HiResW-FV3-AK', 'HiResW-FV3-GU', 'HiResW-FV3-HI', 'HiResW-FV3-SJU', 'LAMP2p5', 'MRMS_0500', 'MRMS_1000', 'MRMS_AK_1000', 'MRMS_CA_0500', 'MRMS_CA_1000', 'MRMS_GU_0500', 'MRMS_HI_0500', 'NAM12', 'NAM40', 'NationalBlend', 'NationalBlendPR', 'RAP13', 'RTMA', 'RTOFS-HudsonBaffin', 'RTOFS-Now-HudsonBaffin', 'RTOFS-Now-WestAtl', 'RTOFS-Now-WestConus', 'RTOFS-WestAtl', 'RTOFS-WestConus', 'SPCGuide', 'SeaIce', 'TPCWindProb', 'URMA25', 'griddednucaps']

Some common model identifiers you might see:

Location NameDescription
GFS20Global Forecast System (20km)
NAM12North American Mesoscale (12km)
RAP13Rapid Refresh (13km)
HRRRHigh Resolution Rapid Refresh
MRMS_1000Multi-Radar Multi-Sensor

Let’s select the GFS 20km model for our example.

# Set the location (model) we want to use
request.setLocationNames("GFS20")

print("Selected model: GFS20")
Selected model: GFS20

Available Parameters

Now that we’ve specified a model, we can see what parameters (variables) are available.

# Get available parameters for the selected model
params = DataAccessLayer.getAvailableParameters(request)
params.sort()

# Display first 30 parameters (there are many!)
print(f"Total parameters available: {len(params)}")
print("\nFirst 30 parameters:")
list(params[:30])
Total parameters available: 497

First 30 parameters:
['-10C Hgt AGL', '-12 to -18C Omega', '-12 to -18C Thickness', '-12C Hgt', '-18C Hgt', '-20C Hgt AGL', '-30C Hgt AGL', '0 to 3km Line Normal Shear Vectors (Left)', '0 to 3km Line Normal Shear Vectors (Right)', '0-10km Blk Shr Mag', '0-10km Blk Shr Vect', '0-1km Blk Shr Mag', '0-1km Blk Shr Vect', '0-1km EHI', '0-1km Helicity', '0-1km Mixing Ratio', '0-1km Moisture Convergence', '0-1km Theta-E', '0-1km Theta-E Adv', '0-1km Wind Streamlines', '0-2km Blk Shr Mag', '0-2km Blk Shr Vect', '0-2km EHI', '0-2km Helicity', '0-3km Blk Shr Mag', '0-3km Blk Shr Vect', '0-3km EHI', '0-3km Helicity', '0-3km LR', '0-3km MLCAPE']

Some commonly used parameters include:

ParameterDescription
TTemperature
GHGeopotential Height
RHRelative Humidity
WindWind (vector)
TPTotal Precipitation
CAPEConvective Available Potential Energy

Let’s select Temperature (T) for our example.

# Set the parameter we want
request.setParameters("T")

print("Selected parameter: T (Temperature)")
Selected parameter: T (Temperature)

Available Levels

For 3D data like model output, we also need to specify which vertical level(s) we want. The getAvailableLevels() method shows what’s available.

# Get available levels for the selected parameter
levels = DataAccessLayer.getAvailableLevels(request)

# Display first 20 levels
print(f"Total levels available: {len(levels)}")
print("\nSample levels:")
for lvl in levels[:20]:
    print(f"  {lvl}")
Total levels available: 258

Sample levels:
  700.0MB
  1000.0FHAG
  0.0SFC
  2000.0_5000.0FHAG
  0.0_3000.0FHAG
  2.0FHAG
  1000.0_400.0MB
  850.0MB
  0.0_6000.0FHAG
  850.0_300.0MB
  250.0MB
  500.0MB
  925.0MB
  700.0_500.0MB
  340.0_350.0K
  290.0_300.0K
  700.0_600.0MB
  700.0_300.0MB
  320.0Ke
  3962.4FHAG

Level notation guide:

SuffixMeaning
MBPressure level in millibars
SFCSurface
FHAGFixed Height Above Ground (meters)
BLBoundary Layer
TROPTropopause
KIsentropic (Kelvin)

Let’s select the surface level (0.0SFC).

# Set the level we want
request.setLevels("0.0SFC")

print("Selected level: 0.0SFC (Surface)")
Selected level: 0.0SFC (Surface)

Working with Times

Model data includes multiple forecast times. Python-AWIPS provides methods to work with these:

  • getAvailableTimes(request, True) - Returns available run times (initialization times)

  • getAvailableTimes(request) - Returns all available times (run + forecast)

  • getForecastRun(cycle, times) - Returns times for a specific forecast cycle

# Get available forecast cycles (run times)
cycles = DataAccessLayer.getAvailableTimes(request, True)

print(f"Number of available forecast cycles: {len(cycles)}")
print(f"\nMost recent cycle: {cycles[-1]}")
Number of available forecast cycles: 3

Most recent cycle: 2026-01-25 06:00:00
# Get all available times
times = DataAccessLayer.getAvailableTimes(request)

# Get the forecast run for the most recent cycle
fcst_run = DataAccessLayer.getForecastRun(cycles[-1], times)

print(f"Number of forecast times in latest run: {len(fcst_run)}")
print("\nFirst 10 forecast times:")
for t in fcst_run[:10]:
    print(f"  {t.getRefTime()} : {t.getFcstTime()} seconds ({t.getFcstTime()/3600:.0f} hours)")
Number of forecast times in latest run: 24

First 10 forecast times:
  2026-01-25 06:00:00.000 : 0 seconds (0 hours)
  2026-01-25 06:00:00.000 : 10800 seconds (3 hours)
  2026-01-25 06:00:00.000 : 21600 seconds (6 hours)
  2026-01-25 06:00:00.000 : 32400 seconds (9 hours)
  2026-01-25 06:00:00.000 : 43200 seconds (12 hours)
  2026-01-25 06:00:00.000 : 54000 seconds (15 hours)
  2026-01-25 06:00:00.000 : 64800 seconds (18 hours)
  2026-01-25 06:00:00.000 : 75600 seconds (21 hours)
  2026-01-25 06:00:00.000 : 86400 seconds (24 hours)
  2026-01-25 06:00:00.000 : 97200 seconds (27 hours)

Retrieving the Data

Now we’re ready to actually retrieve the data. For gridded data, we use getGridData(). This returns a list of grid objects, each containing the data array and metadata.

# Request data for the first forecast time (analysis, 0 hour)
response = DataAccessLayer.getGridData(request, [fcst_run[0]])

print(f"Number of grid objects returned: {len(response)}")
Number of grid objects returned: 1
# Extract information from the first grid object
grid = response[0]

print("Grid Metadata:")
print(f"  Model:     {grid.getLocationName()}")
print(f"  Parameter: {grid.getParameter()}")
print(f"  Level:     {grid.getLevel()}")
print(f"  Unit:      {grid.getUnit()}")
print(f"  Time:      {grid.getDataTime()}")
print(f"  Forecast:  {grid.getDataTime().getFcstTime()/3600:.0f} hours")
Grid Metadata:
  Model:     GFS20
  Parameter: T
  Level:     0.0SFC
  Unit:      K
  Time:      2026-01-25 06:00:00 (0)
  Forecast:  0 hours
# Get the raw data array and coordinates
data = grid.getRawData()
lons, lats = grid.getLatLonCoords()

print(f"Data shape: {data.shape}")
print(f"Longitude shape: {lons.shape}")
print(f"Latitude shape: {lats.shape}")
print(f"\nData range: {data.min():.2f} to {data.max():.2f} {grid.getUnit()}")
Data shape: (257, 369)
Longitude shape: (257, 369)
Latitude shape: (257, 369)

Data range: 233.54 to 300.34 K

Converting to xarray

While python-awips returns numpy arrays, it’s often more convenient to work with xarray for analysis and visualization. xarray provides labeled dimensions, coordinate-aware operations, and integrates well with other tools like MetPy.

We can create an xarray Dataset from the numpy arrays returned by python-awips.

import xarray as xr
import numpy as np

# Create an xarray DataArray from the grid data
da = xr.DataArray(
    data=data,
    dims=['y', 'x'],
    coords={
        'latitude': (['y', 'x'], lats),
        'longitude': (['y', 'x'], lons),
    },
    attrs={
        'units': grid.getUnit(),
        'long_name': grid.getParameter(),
        'level': str(grid.getLevel()),
        'model': grid.getLocationName(),
        'valid_time': str(grid.getDataTime()),
    },
    name=grid.getParameter()
)

da
Loading...

Creating a Complete Dataset

For a more complete xarray Dataset with proper time coordinates, we can request multiple forecast times and combine them.

from datetime import datetime, timedelta

# Request data for the first 5 forecast times
num_times = 5
response = DataAccessLayer.getGridData(request, fcst_run[:num_times])

# Build arrays for the Dataset
data_list = []
times_list = []

for grid in response:
    data_list.append(grid.getRawData())
    # Parse the time from the grid object
    ref_time = grid.getDataTime().getRefTime()
    fcst_seconds = grid.getDataTime().getFcstTime()
    # Create a proper datetime
    valid_time = datetime.strptime(str(ref_time)[:19], '%Y-%m-%d %H:%M:%S') + timedelta(seconds=fcst_seconds)
    times_list.append(valid_time)

# Stack data into a 3D array (time, y, x)
data_3d = np.stack(data_list, axis=0)

print(f"Combined data shape: {data_3d.shape}")
Combined data shape: (5, 257, 369)
# Create the xarray Dataset
ds = xr.Dataset(
    data_vars={
        grid.getParameter(): (['time', 'y', 'x'], data_3d, {
            'units': grid.getUnit(),
            'long_name': 'Temperature',
            'level': str(grid.getLevel()),
        })
    },
    coords={
        'time': times_list,
        'latitude': (['y', 'x'], lats),
        'longitude': (['y', 'x'], lons),
    },
    attrs={
        'model': grid.getLocationName(),
        'source': 'EDEX via python-awips',
    }
)

ds
Loading...

Using the xarray Dataset

Now we have a proper xarray Dataset that we can use with all of xarray’s powerful features.

from metpy.units import units

ds = ds.metpy.parse_cf()

ds.T.metpy.units
Loading...
# Select data by time
temp_first = ds['T'].isel(time=0)
print(f"Data at first time step:")
print(f"  Valid time: {temp_first.time.values}")
print(f"  Shape: {temp_first.shape}")
Data at first time step:
  Valid time: 2026-01-25T15:00:00.000000
  Shape: (257, 369)
# Quick visualization
import matplotlib.pyplot as plt

# Convert from Kelvin to Celsius for plotting
temp_c = ds['T'].isel(time=0).metpy.convert_units('degC')

fig, ax = plt.subplots(figsize=(12, 8))
img = ax.pcolormesh(temp_c.longitude, temp_c.latitude, temp_c, cmap='RdBu_r', shading='auto')
ax.set_xlabel('Longitude')
ax.set_ylabel('Latitude')
ax.set_title(f'GFS Surface Temperature\nValid: {temp_c.metpy.time.dt.strftime("%Y-%m-%d: %H:%MM:%SS")}')
cbar = plt.colorbar(img, ax=ax, label=f'Temperature [{temp_c.metpy.units:~P}]')
plt.show()
<Figure size 1200x800 with 2 Axes>

Summary

In this notebook, we learned how to:

  1. Connect to an EDEX server using DataAccessLayer.changeEDEXHost()

  2. Explore available data types with getSupportedDatatypes()

  3. Create a data request with newDataRequest() and configure it

  4. Discover available locations, parameters, and levels

  5. Query available times and forecast runs

  6. Retrieve data with getGridData()

  7. Convert the data to an xarray Dataset for analysis

Key DataAccessLayer Methods

MethodPurpose
changeEDEXHost(url)Connect to an EDEX server
getSupportedDatatypes()List available data types
newDataRequest()Create a new request object
getAvailableLocationNames(request)Get available locations/models
getAvailableParameters(request)Get available parameters
getAvailableLevels(request)Get available vertical levels
getAvailableTimes(request)Get available times
getForecastRun(cycle, times)Get times for a specific run
getGridData(request, times)Retrieve gridded data
getGeometryData(request, times)Retrieve point/geometry data