Interactive
Required Packages
Code for Generating the Outlines of the Iowa Counties¶
name | state | |
---|---|---|
fips | ||
0 | UNITED STATES | None |
1000 | ALABAMA | None |
1001 | Autauga County | AL |
1003 | Baldwin County | AL |
1005 | Barbour County | AL |
Not on computer
In [36]:
import pandas as pd
import plotly.express as px
from dash import Dash, dcc, html, Input, Output
import base64
import qrcode
from io import BytesIO
# ---------- QR CODE SETUP ----------
public_url = "https://your-deployment-url.com/" # Change this after deployment
def generate_qr_code(url):
qr = qrcode.QRCode(box_size=4, border=2)
qr.add_data(url)
qr.make(fit=True)
img = qr.make_image(fill="black", back_color="white")
buffer = BytesIO()
img.save(buffer, format="PNG")
encoded = base64.b64encode(buffer.getvalue()).decode()
return f"data:image/png;base64,{encoded}"
qr_code_img = generate_qr_code(public_url)
# ---------- DATA PREPARATION ----------
df = pd.read_csv("New_DNR.csv")
positive_df = df[df['Confirmed'] == 'Y'].copy()
positive_df = positive_df.dropna(subset=['X', 'Y'])
positive_df['County'] = positive_df['County'].str.strip().str.title()
positive_df['Sex'] = positive_df['Sex'].str.title()
positive_df['Age'] = positive_df['Age'].str.title()
positive_df['Year'] = positive_df['Year'].astype(int)
sex_options = [{'label': 'All', 'value': 'All'}] + [{'label': s, 'value': s} for s in sorted(positive_df['Sex'].dropna().unique())]
age_options = [{'label': 'All', 'value': 'All'}] + [{'label': a, 'value': a} for a in sorted(positive_df['Age'].dropna().unique())]
county_options = [{'label': 'All', 'value': 'All'}] + [{'label': c, 'value': c} for c in sorted(positive_df['County'].dropna().unique())]
years = sorted(positive_df['Year'].unique())
cumulative_data = []
for year in years:
subset = positive_df[positive_df['Year'] <= year].copy()
subset['Animation_Year'] = str(year)
cumulative_data.append(subset)
cumulative_df = pd.concat(cumulative_data)
# ---------- DASH APP ----------
app = Dash(__name__)
server = app.server # For deployment on platforms like Render
app.layout = html.Div([
html.H1("CWD Cumulative Timelapse in Iowa from 2013-2024", style={'paddingBottom': '10px'}),
html.Div([
html.Label("Sex", style={'fontWeight': 'bold'}),
dcc.Dropdown(id='sex-dropdown', options=sex_options, value='All', clearable=False),
html.Br(),
html.Label("Age", style={'fontWeight': 'bold'}),
dcc.Dropdown(id='age-dropdown', options=age_options, value='All', clearable=False),
html.Br(),
html.Label("County", style={'fontWeight': 'bold'}),
dcc.Dropdown(id='county-dropdown', options=county_options, value='All', clearable=False),
html.Br(),
html.Button("Reset Filters", id="reset-button", n_clicks=0, style={
'marginTop': '10px', 'backgroundColor': '#f0f0f0', 'border': '1px solid #ccc'
}),
html.Br(), html.Br(),
html.Label("Scan to View on Phone:", style={'fontWeight': 'bold'}),
html.Img(src=qr_code_img, style={"width": "150px", "height": "150px", "marginTop": "10px"}),
], style={'width': '25%', 'display': 'inline-block', 'verticalAlign': 'top', 'paddingRight': '20px'}),
html.Div([
dcc.Loading(
id="loading",
type="circle",
children=dcc.Graph(id='timelapse-map')
),
html.Div(id='no-data-message', style={'color': 'red', 'fontWeight': 'bold', 'paddingTop': '10px'})
], style={'width': '70%', 'display': 'inline-block'})
], style={'padding': '20px'})
@app.callback(
Output('sex-dropdown', 'value'),
Output('age-dropdown', 'value'),
Output('county-dropdown', 'value'),
Input('reset-button', 'n_clicks'),
prevent_initial_call=True
)
def reset_filters(n_clicks):
return 'All', 'All', 'All'
@app.callback(
Output('timelapse-map', 'figure'),
Output('no-data-message', 'children'),
Input('sex-dropdown', 'value'),
Input('age-dropdown', 'value'),
Input('county-dropdown', 'value')
)
def update_timelapse(selected_sex, selected_age, selected_county):
df = cumulative_df.copy()
if selected_sex != 'All':
df = df[df['Sex'] == selected_sex]
if selected_age != 'All':
df = df[df['Age'] == selected_age]
if selected_county != 'All':
df = df[df['County'] == selected_county]
if df.empty:
fig = px.scatter_mapbox(
pd.DataFrame(columns=['X', 'Y']),
lat=[],
lon=[],
zoom=6,
height=550
)
fig.update_layout(
mapbox_style="carto-positron",
margin={"r": 0, "t": 40, "l": 0, "b": 0},
title="Cumulative Spread of CWD Over Time"
)
return fig, "No data available for the selected filters."
fig = px.scatter_mapbox(
df,
lat="Y",
lon="X",
hover_name="County",
hover_data={
"X": False, "Y": False, "County": False,
"Year": True, "Sex": True, "Age": True,
"Animation_Year": False
},
animation_frame="Animation_Year",
color_discrete_sequence=["red"],
zoom=6,
height=550
)
if fig.layout.sliders:
for slider in fig.layout.sliders:
slider.currentvalue.visible = False
slider.pad = {"t": 30, "r": 150, "b": 0, "l": 100}
fig.update_layout(
mapbox_style="carto-positron",
margin={"r": 0, "t": 40, "l": 0, "b": 0},
title="Cumulative Spread of CWD Over Time"
)
return fig, ""
if __name__ == '__main__':
app.run_server()