Py.Cafe

asafnb/

dash-od-matrix-map

OD Matrix Map Visualizer

DocsPricing
  • app.py
  • od_33_geo.csv
  • requirements.txt
app.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
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)