CROCUS Lake Breeze (May 7th, 2023)#

  • Lake Breeze are winds that blow off of Lake Michigan on shore due to difference in temperature between the lake and the shoreline.

  • While providing some relief from the heat during the Spring/Summer, these winds also can trap pollutants (in gas and particle form) at the surface creating unsafe air quality conditions.

  • With an unstable atmosphere, these lake breeze circulations can also initiate convection (i.e. vertically building clouds) and produce thunderstorms as was the case on 7 May 2023.

import glob
import os
import tempfile
import warnings
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import matplotlib.gridspec as gridspec
import cartopy.crs as ccrs
import cartopy.feature as cfeature

from math import atan2 as atan2
from datetime import datetime

from metpy.plots import USCOUNTIES
from metpy.calc import dewpoint_from_relative_humidity
from metpy.units import units
import imageio
import pyart
import nexradaws

import sage_data_client

templocation = tempfile.mkdtemp()

warnings.filterwarnings("ignore")
## You are using the Python ARM Radar Toolkit (Py-ART), an open source
## library for working with weather radar data. Py-ART is partly
## supported by the U.S. Department of Energy as part of the Atmospheric
## Radiation Measurement (ARM) Climate Research Facility, an Office of
## Science user facility.
##
## If you use this software to prepare a publication, please cite:
##
##     JJ Helmus and SM Collis, JORS 2016, doi: 10.5334/jors.119
---------------------------------------------------------------------------
ImportError                               Traceback (most recent call last)
Cell In[1], line 19
     17 from metpy.units import units
     18 import imageio
---> 19 import pyart
     20 import nexradaws
     22 import sage_data_client

File ~/miniconda3/envs/instrument-cookbooks-dev/lib/python3.10/site-packages/pyart/__init__.py:29
     25 import importlib.metadata as _importlib_metadata
     27 # import subpackages
     28 # print out helpful message if build fails or importing from source tree
---> 29 from . import (
     30     __check_build,  # noqa
     31     aux_io,  # noqa
     32     bridge,  # noqa
     33     config,  # noqa
     34     core,  # noqa
     35     correct,  # noqa
     36     filters,  # noqa
     37     graph,  # noqa
     38     io,  # noqa
     39     map,  # noqa
     40     retrieve,  # noqa
     41     testing,  # noqa
     42     util,  # noqa
     43     xradar,  # noqa
     44 )
     45 from ._debug_info import _debug_info  # noqa
     47 # root level functions

File ~/miniconda3/envs/instrument-cookbooks-dev/lib/python3.10/site-packages/pyart/xradar/__init__.py:1
----> 1 from .accessor import Xradar, Xgrid  # noqa

File ~/miniconda3/envs/instrument-cookbooks-dev/lib/python3.10/site-packages/pyart/xradar/accessor.py:8
      1 """
      2 Utilities for interfacing between xradar and Py-ART
      3 
      4 """
      6 import copy
----> 8 import datatree
      9 import numpy as np
     10 import pandas as pd

File ~/miniconda3/envs/instrument-cookbooks-dev/lib/python3.10/site-packages/datatree/__init__.py:2
      1 # import public API
----> 2 from .datatree import DataTree
      3 from .extensions import register_datatree_accessor
      4 from .io import open_datatree

File ~/miniconda3/envs/instrument-cookbooks-dev/lib/python3.10/site-packages/datatree/datatree.py:33
     31 from xarray.core.merge import dataset_update_method
     32 from xarray.core.options import OPTIONS as XR_OPTS
---> 33 from xarray.core.utils import (
     34     Default,
     35     Frozen,
     36     HybridMappingProxy,
     37     _default,
     38     either_dict_or_kwargs,
     39     maybe_wrap_array,
     40 )
     41 from xarray.core.variable import Variable
     43 from . import formatting, formatting_html

ImportError: cannot import name 'HybridMappingProxy' from 'xarray.core.utils' (/home/runner/miniconda3/envs/instrument-cookbooks-dev/lib/python3.10/site-packages/xarray/core/utils.py)

Define Time and Location of the Event#

DATE = "2023-05-07"
# Times must be in UTC
TIME_START = "16:00:00"
TIME_STOP = "21:00:00"
RADAR_ID = "KLOT"
## convert input date and time to timestamps
START = pd.Timestamp(int(DATE.split('-')[0]), 
                     int(DATE.split('-')[1]), 
                     int(DATE.split('-')[2]), 
                     int(TIME_START.split(':')[0]), 
                     int(TIME_START.split(':')[1]), 
                     int(TIME_START.split(':')[2])).tz_localize(tz="UTC")
STOP = pd.Timestamp(int(DATE.split('-')[0]), 
                    int(DATE.split('-')[1]), 
                    int(DATE.split('-')[2]), 
                    int(TIME_STOP.split(':')[0]), 
                    int(TIME_STOP.split(':')[1]), 
                    int(TIME_STOP.split(':')[2])).tz_localize(tz="UTC")
# Define the domain to display for Chicago
MIN_LON = -89.0
MAX_LON = -87.0
MAX_LAT = 42.5
MIN_LAT = 40.5
BOUNDING_BOX = [MIN_LON, MAX_LON, MIN_LAT, MAX_LAT]
# Define the CROCUS Waggle Nodes to Display
WAGGLE_SITES = {'NEIU'  : [41.980289109, -87.71703552],
                'ATMOS' : [41.701605152, -87.995196552]
               }

Define Helper Functions#

def gc_latlon_bear_dist(lat1, lon1, bear, dist):
    """
    Input lat1/lon1 as decimal degrees, as well as bearing and distance from
    the coordinate. Returns lat2/lon2 of final destination. Cannot be
    vectorized due to atan2.
    """
    re = 6371.1  # km
    lat1r = np.deg2rad(lat1)
    lon1r = np.deg2rad(lon1)
    bearr = np.deg2rad(bear)
    lat2r = np.arcsin(
        (np.sin(lat1r) * np.cos(dist / re))
        + (np.cos(lat1r) * np.sin(dist / re) * np.cos(bearr))
    )
    lon2r = lon1r + atan2(
        np.sin(bearr) * np.sin(dist / re) * np.cos(lat1r),
        np.cos(dist / re) - np.sin(lat1r) * np.sin(lat2r),
    )
    return np.rad2deg(lat2r), np.rad2deg(lon2r)

def add_scale_line(
    scale, ax, projection, color="k", linewidth=None, fontsize=None, fontweight=None
):
    """
    Adds a line that shows the map scale in km. The line will automatically
    scale based on zoom level of the map. Works with cartopy.

    Parameters
    ----------
    scale : scalar
        Length of line to draw, in km.
    ax : matplotlib.pyplot.Axes instance
        Axes instance to draw line on. It is assumed that this was created
        with a map projection.
    projection : cartopy.crs projection
        Cartopy projection being used in the plot.

    Other Parameters
    ----------------
    color : str
        Color of line and text to draw. Default is black.
    """
    frac_lat = 0.15  # distance fraction from bottom of plot
    frac_lon = 0.35  # distance fraction from left of plot
    e1 = ax.get_extent()
    center_lon = e1[0] + frac_lon * (e1[1] - e1[0])
    center_lat = e1[2] + frac_lat * (e1[3] - e1[2])
    # Main line
    lat1, lon1 = gc_latlon_bear_dist(
        center_lat, center_lon, -90, scale / 2.0
    )  # left point
    lat2, lon2 = gc_latlon_bear_dist(
        center_lat, center_lon, 90, scale / 2.0
    )  # right point
    if lon1 <= e1[0] or lon2 >= e1[1]:
        warnings.warn(
            "Scale line longer than extent of plot! "
            + "Try shortening for best effect."
        )
    ax.plot(
        [lon1, lon2],
        [lat1, lat2],
        linestyle="-",
        color=color,
        transform=projection,
        linewidth=linewidth,
    )
    # Draw a vertical hash on the left edge
    lat1a, lon1a = gc_latlon_bear_dist(
        lat1, lon1, 180, frac_lon * scale / 20.0
    )  # bottom left hash
    lat1b, lon1b = gc_latlon_bear_dist(
        lat1, lon1, 0, frac_lon * scale / 20.0
    )  # top left hash
    ax.plot(
        [lon1a, lon1b],
        [lat1a, lat1b],
        linestyle="-",
        color=color,
        transform=projection,
        linewidth=linewidth,
    )
    # Draw a vertical hash on the right edge
    lat2a, lon2a = gc_latlon_bear_dist(
        lat2, lon2, 180, frac_lon * scale / 20.0
    )  # bottom right hash
    lat2b, lon2b = gc_latlon_bear_dist(
        lat2, lon2, 0, frac_lon * scale / 20.0
    )  # top right hash
    ax.plot(
        [lon2a, lon2b],
        [lat2a, lat2b],
        linestyle="-",
        color=color,
        transform=projection,
        linewidth=linewidth,
    )
    # Draw scale label
    ax.text(
        center_lon,
        center_lat - frac_lat * (e1[3] - e1[2]) / 4.0,
        str(int(scale)) + " km",
        horizontalalignment="center",
        verticalalignment="center",
        color=color,
        fontweight=fontweight,
        fontsize=fontsize,
    )

Create Display Function to Display CROCUS Domain#

def chicago_aq_display(nradar, bounds, sites, aqvals, rad_time, rad_site='KLOT', **kwargs):
    #---------------------------------------------------
    # Define the GridSpec for Detailed Subplot Placement
    #---------------------------------------------------
    fig = plt.figure(figsize=(16, 10))

    gs0 = gridspec.GridSpec(1, 2, figure=fig)
    gs00 = gs0[1].subgridspec(4, 1, hspace=0.9, wspace=0.85)

    # update the extent of the subplot
    gs0.update(top=.90, bottom=0.1, left=0.1, right=.99)

    #-------------------
    # Plot the Radar PPI
    #-------------------
    ax1 = fig.add_subplot(gs0[0], projection=ccrs.PlateCarree())

    ax1.add_feature(cfeature.STATES, linewidth=3)
    ax1.add_feature(USCOUNTIES, alpha=0.4)

    # Create the Radar Display Object
    display = pyart.graph.RadarMapDisplay(nradar)

    # Plot the reflectivty
    display.plot_ppi_map('reflectivity', 
                         ax=ax1,
                         sweep=3, 
                         vmin=-20,
                         vmax=45,
                         lat_lines=None,
                         lon_lines=None,
                         min_lat=bounds[2],
                         max_lat=bounds[3],
                         min_lon=bounds[0],
                         max_lon=bounds[1],
                         ticklabs='',
                         embellish=False,
                         norm=None,
                         cmap="Spectral_r")
    
    # Add the scale line
    add_scale_line(10, ax1, ccrs.PlateCarree())

    # Add the locations of the waggle nodes
    for nsite in sites:
        ax1.scatter(sites[nsite][1], sites[nsite][0], s=60, marker='d', label=nsite)
    # Add a legend
    plt.legend(loc='upper left',
               fontsize=12)
    
    #-------------------------------------------
    # Add the air quality transmitter (AQT) data
    #-------------------------------------------
    ## Note: I am using the radar scan time to time sync the AQT and radar data
    aq_temp = aqvals.loc['2023-05-07 16:00:00':rad_time]
    # Add the second plot for the gridspec
    axs2 = fig.add_subplot(gs00[0])
    aq_temp['pm1.0'].plot(xlabel='UTC Time \n [HH:MM:SS]',
                          ylabel='Part Matter ug/m3',
                          title='CROCUS NEIU Node (W08D) - Vaisala AQT530',
                          ax=axs2, 
                          color='r', 
                          label='PM1.0',
                          )

    aq_temp['pm2.5'].plot(xlabel='UTC Time \n [HH:MM:SS]',
                          ylabel='Part Matter ug/m3',
                          title='CROCUS NEIU Node (W08D) - Vaisala AQT530',
                          ax=axs2, 
                          color='g', 
                          label='PM2.5',
                         )

    aq_temp['pm10.0'].plot(xlabel='UTC Time \n [HH:MM:SS]',
                           ylabel='Part Matter ug/m3',
                           title='CROCUS NEIU Node (W08D) - Vaisala AQT530',
                           ax=axs2, 
                           color='b', 
                           label='PM10.0',
                          )

    axs2.legend()
    axs2.grid(True)
    # Note: force the axis to display the entire time range
    axs2.set_xlim([DATE + ' ' + TIME_START, DATE + ' ' + TIME_STOP])
    #axs2.set_ylim([0.0, 0.3])

    axs3 = fig.add_subplot(gs00[1])
    (aq_temp['co']/10.0).plot(xlabel='UTC Time \n [HH:MM:SS]',
                              ylabel='Molecules of per million',
                              title='CROCUS NEIU Node (W08D) - Vaisala AQT530',
                              ax=axs3, 
                              color='b', 
                              label='CO/10',
                             )

    aq_temp['no'].plot(xlabel='UTC Time \n [HH:MM:SS]',
                       ylabel='Molecules of per million',
                       title='CROCUS NEIU Node (W08D) - Vaisala AQT530',
                       ax=axs3, 
                       color='g', 
                       label='NO',
                      )


    aq_temp['no2'].plot(xlabel='UTC Time \n [HH:MM:SS]',
                        ylabel='Molecules of per million',
                        title='CROCUS NEIU Node (W08D) - Vaisala AQT530',
                        ax=axs3, 
                        color='r', 
                        label='NO2',
                       )


    aq_temp['o3'].plot(xlabel='UTC Time \n [HH:MM:SS]',
                       ylabel='Molecules of per million',
                       title='CROCUS NEIU Node (W08D) - Vaisala AQT530',
                       ax=axs3, 
                       color='purple', 
                       label='o3',
                      )
    axs3.legend()
    axs3.grid(True)
    axs3.set_xlim([DATE + ' ' + TIME_START, DATE + ' ' + TIME_STOP])
    axs3.set_ylim([0.0, 0.05])

    axs4 = fig.add_subplot(gs00[2])
    aq_temp.temp.plot(xlabel='UTC Time \n [HH:MM:SS]',
                      ylabel='Ambient Temperature \n [Degree Celsius]',
                      title='CROCUS NEIU Node (W08D) - Vaisala AQT530',
                      ax=axs4, color='r', label='Dry Bulb',
                     )

    aq_temp.dewpoint.plot(xlabel='UTC Time \n [HH:MM:SS]',
                          ylabel='Ambient Temperature \n [Degree Celsius]',
                          title='CROCUS NEIU Node (W08D) - Vaisala AQT530',
                          color='b', label='Dew Point',
                          ax=axs4
                         )

    axs4.legend()
    axs4.grid(True)
    axs4.set_xlim([DATE + ' ' + TIME_START, DATE + ' ' + TIME_STOP])
    #axs4.set_ylim([0.0, 0.3])

    # Search to see if temporary directory was supplied to save the file
    if kwargs['templocation']:
        plt.savefig(kwargs['templocation'] + '/' + 'lakebreeze' + '_' + rad_site + '_' + str(rad_time) +'.png')
    else:
        plt.savefig('lakebreeze' + '_' + rad_site + '_' + str(rad_time) +'.png')

Data Query#

National Weather Service Radar - Romoville, IL (KLOT)#

  • The National Weather Service Next Generation Radar (NEXRAD) in Romoville, IL provides radar coverage for the Chicago Region, allowing meteorologists to understand potential weather hazards (severe storms).

  • We show how to query data from this station and create a phase plane indictator (PPI) movie for the events of the user’s desire

  • This will allow us to investigate meteorological events within the region and compare against our CROCUS Nodes

# Configure the nexrad interface using our time and location
conn = nexradaws.NexradAwsInterface()
scans = conn.get_avail_scans_in_range(START, STOP, RADAR_ID)
print("There are {} scans available between {} and {}\n".format(len(scans), START, STOP))

# Download the files
results = conn.download(scans, templocation)
There are 48 scans available between 2023-05-07 16:00:00+00:00 and 2023-05-07 21:00:00+00:00

Downloaded KLOT20230507_163235_V06
Downloaded KLOT20230507_162536_V06
Downloaded KLOT20230507_161027_V06
Downloaded KLOT20230507_160329_V06
Downloaded KLOT20230507_163934_V06
Downloaded KLOT20230507_161726_V06
Downloaded KLOT20230507_165332_V06_MDM
Downloaded KLOT20230507_164633_V06
Downloaded KLOT20230507_165332_V06
Downloaded KLOT20230507_170031_V06
Downloaded KLOT20230507_171428_V06
Downloaded KLOT20230507_170729_V06
Downloaded KLOT20230507_172127_V06
Downloaded KLOT20230507_175622_V06_MDM
Downloaded KLOT20230507_172827_V06
Downloaded KLOT20230507_173525_V06
Downloaded KLOT20230507_174224_V06
Downloaded KLOT20230507_174923_V06
Downloaded KLOT20230507_175622_V06
Downloaded KLOT20230507_180321_V06
Downloaded KLOT20230507_181020_V06
Downloaded KLOT20230507_181719_V06
Downloaded KLOT20230507_183117_V06
Downloaded KLOT20230507_182419_V06
Downloaded KLOT20230507_183817_V06
Downloaded KLOT20230507_184517_V06
Downloaded KLOT20230507_185917_V06_MDM
Downloaded KLOT20230507_185217_V06
Downloaded KLOT20230507_185917_V06
Downloaded KLOT20230507_190617_V06
Downloaded KLOT20230507_191316_V06
Downloaded KLOT20230507_192714_V06
Downloaded KLOT20230507_192015_V06
Downloaded KLOT20230507_195509_V06_MDM
Downloaded KLOT20230507_193413_V06
Downloaded KLOT20230507_194112_V06
Downloaded KLOT20230507_194811_V06
Downloaded KLOT20230507_195509_V06
Downloaded KLOT20230507_200209_V06
Downloaded KLOT20230507_200908_V06
Downloaded KLOT20230507_201607_V06
Downloaded KLOT20230507_202307_V06
Downloaded KLOT20230507_205801_V06_MDM
Downloaded KLOT20230507_204403_V06
Downloaded KLOT20230507_203704_V06
Downloaded KLOT20230507_203006_V06
Downloaded KLOT20230507_205103_V06
Downloaded KLOT20230507_205801_V06
48 out of 48 files downloaded...0 errors

CROCUS Level 3 Node Data Query (W08D) - Northeastern Illinois University#

  • Please check our notebooks on the CROCUS-Level1-Node for more information on data query with the SAGE Data Client

  • Northeastern Illinois University (NEIU) served as the first CROCUS node deployed to the Chicago region.

## NEIU AQT 
df_aq = sage_data_client.query(start = DATE + 'T' + TIME_START + 'Z',
                               end = DATE + 'T' + TIME_STOP + 'Z', 
                               filter={
                                        "plugin": "registry.sagecontinuum.org/jrobrien/waggle-aqt:0.23.5.*",
                                       }
)

# extract the particle measurements from the DataFrame
pm25 = df_aq[df_aq['name']=='aqt.particle.pm2.5']
pm10 = df_aq[df_aq['name']=='aqt.particle.pm1']
pm100 = df_aq[df_aq['name']=='aqt.particle.pm10']

# extract the gas measurements from the DataFrame
no = df_aq[df_aq['name']=='aqt.gas.no']
o3 = df_aq[df_aq['name']=='aqt.gas.ozone']
no2 = df_aq[df_aq['name']=='aqt.gas.no2']
co = df_aq[df_aq['name']=='aqt.gas.co']

# extract the environment data from the DataFrame
aqtemp = df_aq[df_aq['name']=='aqt.env.temp']
aqhum = df_aq[df_aq['name']=='aqt.env.humidity']

# Convert values to numpy arrays
aqvals = pm25.set_index('timestamp')
aqvals['pm2.5'] = aqvals.value.to_numpy().astype(float)
aqvals['pm1.0'] = pm10.value.to_numpy().astype(float)
aqvals['pm10.0'] = pm100.value.to_numpy().astype(float)
aqvals['no'] = no.value.to_numpy().astype(float)
aqvals['o3'] = o3.value.to_numpy().astype(float)
aqvals['no2'] = no2.value.to_numpy().astype(float)
aqvals['co'] = co.value.to_numpy().astype(float)
aqvals['temp'] =  aqtemp.value.to_numpy().astype(float)
aqvals['hum'] =  aqhum.value.to_numpy().astype(float)

# Use MetPY to calculate dewpoint from temperature and relative humidity
dp = dewpoint_from_relative_humidity( aqvals.temp.to_numpy() * units.degC, 
                                     aqvals.hum.to_numpy() * units.percent)

aqvals['dewpoint'] = dp

Create Display#

for i, scan in enumerate(results.iter_success(), start=1):
    ## skip the files ending in "MDM"
    if scan.filename[-3:] != "MDM":
        if i > 1:
            # define what file you are working on
            print("working on " + scan.filename)
            # define the radar time
            rad_time = scan.filename[4:8] + '-' + scan.filename[8:10] + '-' + scan.filename[10:12] + \
                       ' ' + scan.filename[13:15] + ':' + scan.filename[15:17] + ':' + scan.filename[17:19]
            # Open the file with PyART
            radar = scan.open_pyart()
            # Call the CROCUS AQ Display Function
            chicago_aq_display(radar, 
                               BOUNDING_BOX, 
                               WAGGLE_SITES, 
                               aqvals, 
                               rad_time,
                               templocation=templocation)
            # close the radar file
            del radar
working on KLOT20230507_161027_V06
working on KLOT20230507_161726_V06
working on KLOT20230507_162536_V06
working on KLOT20230507_163235_V06
working on KLOT20230507_163934_V06
working on KLOT20230507_164633_V06
working on KLOT20230507_165332_V06
working on KLOT20230507_170031_V06
working on KLOT20230507_170729_V06
working on KLOT20230507_171428_V06
working on KLOT20230507_172127_V06
working on KLOT20230507_172827_V06
working on KLOT20230507_173525_V06
working on KLOT20230507_174224_V06
working on KLOT20230507_174923_V06
working on KLOT20230507_175622_V06
working on KLOT20230507_180321_V06
working on KLOT20230507_181020_V06
working on KLOT20230507_181719_V06
working on KLOT20230507_182419_V06
working on KLOT20230507_183117_V06
working on KLOT20230507_183817_V06
working on KLOT20230507_184517_V06
working on KLOT20230507_185217_V06
working on KLOT20230507_185917_V06
working on KLOT20230507_190617_V06
working on KLOT20230507_191316_V06
working on KLOT20230507_192015_V06
working on KLOT20230507_192714_V06
working on KLOT20230507_193413_V06
working on KLOT20230507_194112_V06
working on KLOT20230507_194811_V06
working on KLOT20230507_195509_V06
working on KLOT20230507_200209_V06
working on KLOT20230507_200908_V06
working on KLOT20230507_201607_V06
working on KLOT20230507_202307_V06
working on KLOT20230507_203006_V06
working on KLOT20230507_203704_V06
working on KLOT20230507_204403_V06
working on KLOT20230507_205103_V06
working on KLOT20230507_205801_V06
../../_images/4ba33a52731c7e3e11c0bdb6bbabca6d4da074e5f6e4a2110cce583d7810c856.png ../../_images/5345b31e110ab4e401a33523227767e55ec4daa8d6b02c9d5f0d2c007ff80dab.png ../../_images/0b08d8098736532adbf43a58bdd4630e24d965d62a56d2351b5a7950f91a2275.png ../../_images/3527ba0c92eca89ace49fbb2c7370e92e6501977a4e4d22c0666d1490eabe205.png ../../_images/adc5f0b64024e94f4fdc9195e6975cd6b2390b87b693f4b710c850e6e1639e66.png ../../_images/5de9896abfca7a452323f24f86585d9bad6b87ed4328b34247867489da8d2850.png ../../_images/fd1f6b3c06d529dc80af303a5d2ac10a025f43834ef969820b9834fe77d72e20.png ../../_images/3e324fc68f192f02c341c93aed56db6d7b8676f42d6b40c4be9a782accd56f3a.png ../../_images/01118b7c896d2eefb259e58f5018058516ba2679ce264fb4b8fc47b57b98bcab.png ../../_images/24a04bd17b8780b3eb4d0eb843649fca8b06e4370766f0a9be8473d5cab48863.png ../../_images/d1d886af23a4c0066b90232eb34d27e40a52616257ae5177e4717e8f4f1d558e.png ../../_images/86c5e4fb29caad661ce84035ee9031b46ccb699afe6901c5f5fa101f27aa7d3f.png ../../_images/7f16c0c61eeb94b95bed9b5e48c6de98c0ad64376e43bebcf60fe3d5b83dde39.png ../../_images/c3bde93840c8d69f56a6dc24d435a52e04ab7b03b11b80dd95f59e4d9e4d7dcc.png ../../_images/b7e891ad9fd8ef4a44de55be644d10692a7af9a716c561af3d5aa6f4c322e1db.png ../../_images/c319cfea7f36159aa8791e652e1f78806bbaf1c932f606938d40f59ee5513c33.png ../../_images/ef99b81ec540e29953bb23ae815a6ac9fe65e09a09326d2b9e84e8bc78d82e9f.png ../../_images/442dc4614c2f2e695ae3a71475fa98632ea366f086185481c4273f8746023f84.png ../../_images/ee7568851c47539022ab844ae0fe6a15713820636f023260654818908add4e30.png ../../_images/6cf2a0fd057291240cec72819ef8c59d901ad83bbec65c551e66e535d868a6e3.png ../../_images/9573494a5672fc2ade3a45bdfe9294c4a927723ba5f111cbe71fed97d7c36975.png ../../_images/b05d30094ce392cb4e83a1dfb11c70b913b4c1cc70cc8c49ad416b8e3b3ae396.png ../../_images/ad858118cdec298293f198bfa8d14a1d6deb2f7834ace72061b4cd1b430018b0.png ../../_images/d23644be98eae2cfb7c40787e2cb79ebb8579f21b720c187f7e5b1951a8e7bf0.png ../../_images/7d91722f4f16eeedcc5db7c6921ad64b5a99ad654d4df4740456b3c7af977d84.png ../../_images/d85611ea4c2c16e9b40d6b6dabd3ff61f3c7ecf63992dd10aa706db15767c7ef.png ../../_images/8095c3530eae3a6c81be7a6dab954ef17a288224341cc50c8a979d08064a767c.png ../../_images/671ba5d06158934ad1c5a978c9be8c81712b404dc6c69685b5c5b6efab4491f4.png ../../_images/b2128e2bcbe3ebc87b6ca6d9b4349c74fa756301c21098ed467fae676d84f9fb.png ../../_images/db1164f352bf456cc3b6837b7e0f3e31a9ca19b87cf6d987c1752b3df93c95d0.png ../../_images/6452585e24ebfeca56607e61f546ed5119756c41f3fac670fe7c2a06b3169f05.png ../../_images/045e54328b4c814b4b222b7f6a3568a59915b6fcf65802363198db40d8afa3f0.png ../../_images/aaf24edb48ebcea311507efc645ae37fdb2a4807d981561bfdcf13e1279e7818.png ../../_images/5d44986b996fd3e37ecda0a530f7f4d746846b7f9a652bf4803523ae45bfbf18.png ../../_images/13da178b033845d26b92f5d207e2204d8aee1186922ae0923c5249cac1697a5f.png ../../_images/6bccf0381aebb930022c74a9eb34df16fff7de24b116eaf67f1fbdee3cb8672b.png ../../_images/b1ff879c41ae4ce03c1193f990058111412b2aeb7491a01221c365e732b178e1.png ../../_images/63afde29d2a56d224e3d25f972191d953bc896abbe80a9a10c18137ddf1955b8.png ../../_images/922a6eac95b602530b778c83705288a1273af0a2b9f85dbc8b3a1e785d75493b.png ../../_images/5638cc04d95bbbfee8f0535db7c0293da79b3adb8ec03edf66d23aed2f649f9e.png ../../_images/49d4ded71655b5782b42e9eab8b581a8f8fafe575448088352c5f09c9595a270.png ../../_images/6b5ad9104fe94f7a8f10b6878dde205ff702528dc6cc6b6daa7b3c7927089d0d.png

Assemble Static Plots into a Movie#

# Define files created and define movie path
map_images = sorted(glob.glob(templocation + f"/lakebreeze*"))
gif_title = f"lakebreeze_{RADAR_ID}-map-animation.gif"

# Check to see if the file exists - if it does, delete it
if os.path.exists(gif_title):
    os.remove(gif_title)

# Loop through and create the gif
with imageio.get_writer(gif_title, mode='I', duration=0.2) as writer:
    for filename in map_images:
        image = imageio.imread(filename)
        writer.append_data(image)

SegmentLocal