import pandas as pd
import geopandas as gpd
import folium
import solara
from branca.colormap import LinearColormap
# Load data
od_data = pd.read_csv("aggregated_od_33.csv")
zones = gpd.read_file("20240301_DE2_TAZ_LAYER.json")
# Ensure zones is in WGS84 (EPSG:4326) for web mapping
zones = zones.to_crs(epsg=4326)
# Calculate centroids
zones['centroid'] = zones.geometry.centroid
# Prepare data
od_data['total_flow'] = od_data.groupby(['From_TAZ_33', 'To_TAZ_33'])['Count'].transform('sum')
# Create a color scale
color_scale = LinearColormap(colors=['blue', 'green', 'yellow', 'red'], vmin=od_data['total_flow'].min(), vmax=od_data['total_flow'].max())
@solara.component
def ODMap():
# State variables
vehicle_type, set_vehicle_type = solara.use_state("All")
min_count, set_min_count = solara.use_state(0)
# Filter data based on user input
def filter_data():
filtered = od_data
if vehicle_type != "All":
filtered = filtered[filtered['VehicleType'] == vehicle_type]
filtered = filtered[filtered['Count'] >= min_count]
return filtered
# Create map
def create_map():
# Calculate the center of all centroids
center_lat = zones['centroid'].y.mean()
center_lon = zones['centroid'].x.mean()
m = folium.Map(location=[center_lat, center_lon], zoom_start=8)
# Add OD lines
filtered_data = filter_data()
for _, row in filtered_data.iterrows():
origin = zones[zones['taz_33'] == str(row['From_TAZ_33'])]['centroid'].iloc[0]
destination = zones[zones['taz_33'] == str(row['To_TAZ_33'])]['centroid'].iloc[0]
folium.PolyLine(
locations=[[origin.y, origin.x], [destination.y, destination.x]],
color=color_scale(row['total_flow']),
weight=1 + (row['Count'] / filtered_data['Count'].max()) * 5,
opacity=0.8,
popup=f"From: {row['From_TAZ_33']}, To: {row['To_TAZ_33']}<br>Vehicle Type: {row['VehicleType']}<br>Count: {row['Count']}<br>Total Flow: {row['total_flow']}"
).add_to(m)
# Add color scale legend
color_scale.add_to(m)
return m
# Solara components for user input
vehicle_type_select = solara.Select(
label="Vehicle Type",
value=vehicle_type,
values=["All"] + list(od_data['VehicleType'].unique()),
on_value=set_vehicle_type
)
min_count_slider = solara.SliderFloat(
label="Minimum Count",
value=min_count,
min=0,
max=od_data['Count'].max(),
step=10,
on_value=set_min_count
)
# Create Folium map
map_figure = create_map()
# Layout
return solara.Column([
solara.Row([
solara.Column([vehicle_type_select, min_count_slider]),
solara.FoliumMap(map_figure, style={"width": "800px", "height": "600px"})
])
])
# Main app
@solara.component
def Page():
return ODMap()