Wildfire 'Living' GIF

In [2]:
import geopandas as gpd
import matplotlib.pyplot as plt
import matplotlib
from tqdm import tqdm
In [3]:
# Wildland Fire Incident Locations data acquired from the National Interagency Fire Center
# https://data-nifc.opendata.arcgis.com/datasets/nifc::wildland-fire-incident-locations/about
gdf = gpd.read_file("../Data/Wildland_Fire_Incident_Locations.geojson")
gdf.head()
Out[3]:
OBJECTID SourceOID ABCDMisc ADSPermissionState ContainmentDateTime ControlDateTime CreatedBySystem IncidentSize DiscoveryAcres DispatchCenterID ... OrganizationalAssessment StrategicDecisionPublishDate CreatedOnDateTime_dt ModifiedOnDateTime_dt IsCpxChild CpxName CpxID SourceGlobalID GlobalID geometry
0 1 7747595 None DEFAULT None None lacocad NaN 0.10 CALACC ... None NaT 2020-02-28 20:52:36+00:00 2020-02-28 20:52:36+00:00 0 None None {6A311ABB-DF4F-4947-B8DD-3900BDA784F6} {48D2C0E2-5E38-4D40-9D5E-066B076C7D98} POINT (-118.18071 33.80898)
1 2 6384391 None DEFAULT None None firecode NaN NaN CAMVIC ... None NaT 2019-07-01 20:10:12+00:00 2019-07-01 20:10:12+00:00 0 None None {1AF2C949-B159-4D8F-8D39-90CB58BC5DD5} {17D2D66A-D451-4592-A172-7B2C860A2CC9} POINT (-117.15390 33.17639)
2 3 1383752 None DEFAULT None None firecode NaN NaN None ... None NaT 2016-06-20 22:39:02+00:00 2016-06-20 22:39:02+00:00 0 None None {1B179EA1-97CE-4699-915B-374754BCBC5B} {60C471FF-3C85-41B4-9135-E7338D7EC90B} POINT (-121.10418 38.83473)
3 4 22499589 None DEFAULT None None cfcad NaN 0.10 CARRCC ... None NaT 2021-11-25 15:24:53+00:00 2021-11-25 15:24:53+00:00 0 None None {E61E387B-4ED7-4971-9604-C5D7391FAF77} {149237EC-A42E-43D6-9318-22207A705DD9} POINT (-117.22859 33.78244)
4 5 23869477 None DEFAULT None None lacocad NaN 0.01 CALACC ... None NaT 2022-11-21 11:28:49+00:00 2022-11-21 11:28:49+00:00 0 None None {AEB6F7A3-A109-4132-9FEB-FB1EE1DF3193} {EF7675E3-D5BE-412A-A6C1-0D63FC7153C8} POINT (-118.30903 33.94181)

5 rows × 95 columns

In [4]:
# Vector data for the US map was acquired from the United States Census Bureau
# https://www.census.gov/cgi-bin/geo/shapefiles/index.php
usa = gpd.read_file("../Data/tl_2022_us_state/tl_2022_us_state.shp")
non_continental = ['HI','VI','MP','GU','AK','AS','PR']
us49 = usa[~usa.STUSPS.isin(non_continental)]
us49 = us49.cx[-124.7:-66.9, 24.5:49.4]
us49.boundary.plot()
plt.plot()
Out[4]:
[]
In [5]:
# Custom Colormap
named_color = 'maroon'
c_white = matplotlib.colors.colorConverter.to_rgba(named_color, alpha = 0.2)
c_dark= matplotlib.colors.colorConverter.to_rgba(named_color, alpha = 1)
my_cmap = matplotlib.colors.LinearSegmentedColormap.from_list('rb_cmap',[c_white,c_dark],512)
my_cmap
Out[5]:
rb_cmap
rb_cmap colormap
under
bad
over
In [86]:
days = [1, 5, 10, 15, 20, 25]
dates = [f'{y}-{m:02}-{d:02}' for y in range(2014,2023) for m in range(1,13) for d in days][4:]
months = ['January', 'February', 'March', 'April', 'May', 'June', 'July',
          'August', 'September', 'October', 'November', 'December']

# Save a figure for each 30-day period, adding (30 / len(days)) days to each consecutive figure
for i in tqdm(range(len(dates) - len(days))):
    ### Test Cases:
    # if dates[i] not in ['2016-07-01', '2016-07-15']:
    #     continue

    fig, ax = plt.subplots(1, figsize=(10,10))

    us49.boundary.plot(ax=ax)

    fires = gdf[(gdf["FireDiscoveryDateTime"] > dates[i]) & (gdf["FireDiscoveryDateTime"] < dates[i+len(days)])]
    fires = fires.cx[-124.7:-66.9, 24.5:49.4]
    fires.plot(ax=ax, column="IncidentSize", cmap=my_cmap)

    ax.set_title("Wildfire Incidents Reported (Monthly)",
                 y=1.0, fontsize=24)
    ax.axis('off')

    label_date = f"{months[int(dates[i][5:7])-1]} {dates[i][:4]}" # shows month AND year
    # label_date = dates[i][:4]
    ax.annotate(label_date,
                xy=(0.335, 0.24), xycoords='figure fraction',
                horizontalalignment='right', verticalalignment='top',
                fontsize=24)
    ax.annotate("Source: National Interagency Fire Center",
                xy=(0.05, 0.20), xycoords='figure fraction',
                horizontalalignment='left', verticalalignment='top',
                fontsize=10)

    ax.annotate("Christopher Abib (Team: I'll Figure It Out Later)",
                xy=(0.95, 0.20), xycoords='figure fraction',
                horizontalalignment='right', verticalalignment='top',
                fontsize=10)

    ### Hard to read with the wiggling.
    ax.annotate("This graphic visualizes the locations of each wildfire incident reported to the\nNational Interagency Fire Center on a monthly period from 2014 to 2022.",
                xy=(0.05, 0.15), xycoords='figure fraction',
                horizontalalignment='left', verticalalignment='top',
                fontsize=16)

    ax.legend(handles=[matplotlib.lines.Line2D([0], [0], marker='o', color='maroon', label='1 Incident Reported', alpha=0.5, linestyle='None')])

    fig.subplots_adjust(top=1.4, bottom=-0.3, right=1.0, left=0, hspace=0, wspace=0)

    fig.savefig(f"./maps/wildfire_incidents/{dates[i]}_fires.jpg", dpi=300)
    plt.close()
100%|██████████| 638/638 [33:01<00:00,  3.11s/it]    
In [79]:
# Use the CLI ImageMagick to combine each chart into a gif (delay = 10ms)
!cd maps/wildfire_incidents; convert -delay 10 -loop 0 ./* ../wildfire_incidents.gif
^C

To do

  • Implement a color scheme that properly shows fire intensity
  • Show months by year text or a time progress bar.
  • Satellite image background inside of states borders to show landscape\biome?
  • Stop the wiggling!!!
  • Add an interactive time slider and intensity filter.
  • Lower res for better sharing ability