| import datetime | |
| import io | |
| import os | |
| import requests | |
| import geopandas as gpd | |
| import matplotlib.pyplot as plt | |
| import numpy as np | |
| import streamlit as st | |
| import xarray as xr | |
| from geogif import gif | |
| from rasterio import features | |
| provinces = gpd.read_file("./data/prov_stat.geojson") | |
| geoms = provinces.geometry.boundary | |
| image = features.rasterize( | |
| geoms, | |
| out_shape=[381, 1081], | |
| transform=(0.10000000149011612, 0.0, -160.0, 0.0, 0.10000000149011612, 32.0), | |
| ) | |
| cmap = st.selectbox("Colormap", plt.colormaps(), index=1) | |
| fps = st.slider("Frames per second", 0.01, 50.0, 16.0) | |
| show_dt = st.checkbox("Show timestamp", True) | |
| fetch_data = st.checkbox("Refresh data", False) | |
| if st.button("Create GIF"): | |
| smoke_url = "https://firesmoke.ca/forecasts/current/dispersion.nc" | |
| smoke_nc = "./data/dispersion.nc" | |
| if not os.path.isfile(smoke_nc) or fetch_data: | |
| response = requests.head(smoke_url) | |
| total_size = int(response.headers.get("Content-Length")) | |
| progress_text = "Downloading data. Please wait." | |
| progress_bar = st.progress(0, text=progress_text) | |
| size_downloaded = 0 | |
| chunk_size = 8192 | |
| with requests.get(smoke_url, stream=True) as r: | |
| r.raise_for_status() | |
| with open(smoke_nc, "wb") as f: | |
| for chunk in r.iter_content(chunk_size=chunk_size): | |
| f.write(chunk) | |
| size_downloaded += chunk_size | |
| percent_complete = min(size_downloaded / total_size, 1) | |
| progress_bar.progress(percent_complete, text=progress_text) | |
| progress_bar.empty() | |
| ds_disk = xr.open_dataset(smoke_nc) | |
| tstep = ds_disk.TFLAG | |
| dts = [] | |
| for i in tstep: | |
| data = i.data[0] | |
| dt = datetime.datetime.strptime( | |
| str(data[0]) + str(data[1]).zfill(6), "%Y%j%H%M%S" | |
| ) | |
| dts.append(dt) | |
| pm25 = ds_disk.PM25 | |
| pm25["TSTEP"] = dts | |
| for idx, i in enumerate(pm25.data): | |
| pm25[idx].data[0] = np.maximum(i[0], image * 6) | |
| buffer = io.BytesIO() | |
| if show_dt: | |
| date_format = "%Y-%m-%d %H:%M:%S" | |
| else: | |
| date_format = None | |
| gif( | |
| pm25[:, :, ::-1, :], | |
| date_format=date_format, | |
| fps=fps, | |
| cmap=cmap, | |
| date_color=(255, 255, 255), | |
| to=buffer, | |
| ) | |
| first_dt = dts[0] | |
| last_dt = dts[-1] | |
| st.image(buffer, caption=f"{first_dt} to {last_dt}") | |
| st.download_button( | |
| label="Download GIF", data=buffer, file_name="firesmoke.gif", mime="image/gif" | |
| ) | |
| with st.expander("Data Sources"): | |
| st.markdown(f"Smoke forecast: [FireSmoke Canada](https://firesmoke.ca/)") | |
| st.markdown( | |
| f"Administrative boundaries: [Natural Earth, 1:50M Admin 1 – States, provinces](https://www.naturalearthdata.com/http//www.naturalearthdata.com/download/50m/cultural/ne_50m_admin_1_states_provinces.zip)" | |
| ) | |