import dash
from dash import dcc, html
from dash.dependencies import Input, Output, State
import pandas as pd
import plotly.graph_objects as go
import numpy as np
import datashader as ds
import datashader.transfer_functions as tf
from colorcet import fire
import xarray as xr
# Create a sample dataset
# Assuming the CSV file is in the same directory as the script
# script_dir = os.path.dirname(os.path.realpath(__file__))
# csv_path = os.path.join(script_dir, 'od_33_geo.csv')
# # Create a sample dataset
OD_MATRIX_DATA = pd.read_csv(r'od_33_geo.csv')
# Function to extract coordinates from POINT string
def extract_coordinates(point_str):
coords = point_str.replace('POINT (', '').replace(')', '').split()
return float(coords[1]), float(coords[0]) # Return lat, lon
# Preprocess the data
OD_MATRIX_DATA['origin_lat'], OD_MATRIX_DATA['origin_lon'] = zip(*OD_MATRIX_DATA['From_Centroid'].apply(extract_coordinates))
OD_MATRIX_DATA['dest_lat'], OD_MATRIX_DATA['dest_lon'] = zip(*OD_MATRIX_DATA['To_Centroid'].apply(extract_coordinates))
app = dash.Dash(__name__)
app.layout = html.Div([
html.H1("OD Matrix Map"),
html.Div([
dcc.Dropdown(
id='vehicle-type-dropdown',
options=[{'label': vt, 'value': vt} for vt in OD_MATRIX_DATA['VehicleType'].unique()],
value='All',
multi=True
),
dcc.RangeSlider(
id='count-range-slider',
min=OD_MATRIX_DATA['Count'].min(),
max=OD_MATRIX_DATA['Count'].max(),
value=[OD_MATRIX_DATA['Count'].min(), OD_MATRIX_DATA['Count'].max()],
marks={int(OD_MATRIX_DATA['Count'].min()): str(int(OD_MATRIX_DATA['Count'].min())),
int(OD_MATRIX_DATA['Count'].max()): str(int(OD_MATRIX_DATA['Count'].max()))}
),
]),
dcc.Graph(id='od-matrix-map', style={'height': '90vh'})
])
@app.callback(
Output('od-matrix-map', 'figure'),
[Input('vehicle-type-dropdown', 'value'),
Input('count-range-slider', 'value')]
)
def update_map(selected_vehicle_types, count_range):
# Filter data based on user selection
if 'All' in selected_vehicle_types or not selected_vehicle_types:
filtered_data = OD_MATRIX_DATA
else:
filtered_data = OD_MATRIX_DATA[OD_MATRIX_DATA['VehicleType'].isin(selected_vehicle_types)]
filtered_data = filtered_data[(filtered_data['Count'] >= count_range[0]) &
(filtered_data['Count'] <= count_range[1])]
# Prepare data for datashader
lines = pd.DataFrame({
'x': np.concatenate([filtered_data['origin_lon'], filtered_data['dest_lon']]),
'y': np.concatenate([filtered_data['origin_lat'], filtered_data['dest_lat']]),
'weight': np.concatenate([filtered_data['Count'], filtered_data['Count']])
})
# Use datashader to aggregate the data
cvs = ds.Canvas(plot_width=1000, plot_height=1000)
agg = cvs.line(lines, 'x', 'y', agg=ds.sum('weight'))
# Convert the aggregate to an image
img = tf.shade(agg, cmap=fire)[::-1].to_pil()
# Create the figure
fig = go.Figure(go.Image(z=img))
# Update layout
fig.update_layout(
mapbox_style="carto-positron",
mapbox=dict(
center=dict(lat=filtered_data['origin_lat'].mean(), lon=filtered_data['origin_lon'].mean()),
zoom=6
),
margin={"r":0,"t":0,"l":0,"b":0},
height=800
)
# Add centroids
fig.add_trace(go.Scattermapbox(
mode="markers",
lon=filtered_data['origin_lon'].tolist() + filtered_data['dest_lon'].tolist(),
lat=filtered_data['origin_lat'].tolist() + filtered_data['dest_lat'].tolist(),
marker=dict(size=2, color='black'),
hoverinfo='text',
hovertext=filtered_data['From_TAZ_33'].tolist() + filtered_data['To_TAZ_33'].tolist()
))
return fig
if __name__ == '__main__':
app.run_server(debug=False)