In [34]:
 
In [38]:
# ---------- Load and Preprocess Data ----------
df = pd.read_csv("Compiled_Data.csv")

# Filter coordinates (Allamakee County bounding box)
df = df[
    (df["Latitude"] >= 43.1) & (df["Latitude"] <= 43.5) &
    (df["Longitude"] >= -91.7) & (df["Longitude"] <= -91.1)
].copy()

# Clean data
df = df.dropna(subset=["Latitude", "Longitude"])
df["Sex"] = df["Sex"].str.title()
df["Age"] = df["Age"].str.title()

# Include "Yearling" explicitly in Age options
all_ages = set(df["Age"].dropna().unique())
all_ages.add("Yearling")
age_options = [{"label": "All", "value": "All"}] + [
    {"label": age, "value": age} for age in sorted(all_ages)
]

# Do NOT filter years — keep full range including 2025 and 2026
default_year = 2025

# Sex options
sex_options = [{"label": "All", "value": "All"}] + [
    {"label": sex, "value": sex} for sex in sorted(df["Sex"].dropna().unique())
]

app = Dash(__name__)

def generate_timelapse_figure(filtered_df):
    if filtered_df.empty:
        return px.scatter_mapbox(
            lat=[], lon=[], zoom=9, height=600
        ).update_layout(
            mapbox_style="carto-positron",
            title="No Data Available for Selected Filters"
        )

    fig = px.scatter_mapbox(
        filtered_df,
        lat="Latitude",
        lon="Longitude",
        color="Sex",
        # symbol="Age",  # Uncomment if you want different shapes for each age group
        animation_frame="Year",
        center={"lat": 43.3, "lon": -91.4},
        zoom=9,
        height=600,
        hover_data={
            "Sex": True,
            "Age": True,
            "Latitude": False,
            "Longitude": False,
            "Year": False
        }
    )
    fig.update_layout(
        mapbox_style="carto-positron",
        title="CWD Marker Map - Allamakee County (Timelapse Starting 2025)"
    )
    return fig

# Initial figure includes all years starting at 2025
initial_figure = generate_timelapse_figure(df)

app.layout = html.Div([
    html.H2("CWD Marker Map - Allamakee County, Iowa", style={"marginBottom": "20px"}),

    html.Div([
        html.Label("Filter by Age:", style={"fontWeight": "bold"}),
        dcc.Dropdown(id="age-dropdown", options=age_options, value="All", clearable=False),

        html.Br(),
        html.Label("Filter by Sex:", style={"fontWeight": "bold"}),
        dcc.Dropdown(id="sex-dropdown", options=sex_options, value="All", clearable=False),

        html.Br(),
        html.Button("Reset Filters", id="reset-button", n_clicks=0),
    ], style={"width": "25%", "display": "inline-block", "verticalAlign": "top"}),

    html.Div([
        dcc.Graph(id="heatmap", figure=initial_figure)
    ], style={"width": "70%", "display": "inline-block", "paddingLeft": "30px"})
])

@app.callback(
    Output("heatmap", "figure"),
    Input("age-dropdown", "value"),
    Input("sex-dropdown", "value")
)
def update_heatmap(selected_age, selected_sex):
    filtered_df = df.copy()
    if selected_age != "All":
        filtered_df = filtered_df[filtered_df["Age"] == selected_age]
    if selected_sex != "All":
        filtered_df = filtered_df[filtered_df["Sex"] == selected_sex]
    return generate_timelapse_figure(filtered_df)

@app.callback(
    Output("age-dropdown", "value"),
    Output("sex-dropdown", "value"),
    Input("reset-button", "n_clicks")
)
def reset_filters(n_clicks):
    if n_clicks is None or n_clicks == 0:
        raise dash.exceptions.PreventUpdate
    return "All", "All"

if __name__ == "__main__":
    app.run_server(debug=True, port=8030)
In [ ]: