File size: 14,219 Bytes
e79ca1c
 
a57e10d
e79ca1c
 
cd9383e
 
 
a57e10d
 
 
 
27114c3
2184754
 
a57e10d
 
 
e79ca1c
eb468eb
 
2184754
e876ecf
b2a826b
 
 
 
 
eb468eb
 
b2a826b
 
 
 
 
 
eb468eb
e876ecf
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
c56d2c9
2184754
 
 
 
 
 
e876ecf
 
 
 
 
2184754
 
 
c56d2c9
b2a826b
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
e79ca1c
 
 
 
cd9383e
 
 
 
 
 
 
e79ca1c
 
 
 
 
 
cd9383e
e79ca1c
 
 
 
 
 
 
 
 
 
 
 
cd9383e
 
e79ca1c
cd9383e
 
 
 
 
 
 
 
 
a57e10d
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
eb468eb
714bb18
b2a826b
714bb18
b2a826b
 
714bb18
eb468eb
b2a826b
 
b2c28b9
 
b718025
 
b2a826b
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
b2d675f
b2a826b
 
 
b05a953
b2a826b
b2d675f
b2a826b
 
 
 
 
 
3a302ae
 
 
 
b2a826b
 
b2dc618
3a302ae
 
 
 
b2a826b
 
3a302ae
9468499
5150f61
3cd20f3
b05a953
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
b2a826b
e79ca1c
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
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
# server.py
import matplotlib.pyplot as plt
from shiny import render, ui, reactive
import faicons as fa
#from fontawesomefree import icons
import numpy as np
from utils import conlogo, vendorlogo
from utils import create_pie_chart, create_bar_chart, create_bar_chart, create_stacked_bar_chart
import plotly.express as px
from utils import load_data, filter_trips
from shinywidgets import output_widget, render_widget
import plotly.graph_objects as go
import pandas as pd
import json


# Load the data
zones, trips = load_data()

# Path to Excel file
EXCEL_FILE = "data/trips_data.xlsx"  # Update with your file path
Json_title_FILE = "data/old_table_title.json"  
Json_table_FILE =  "data/old_table.json"  
Q1_FILE =  "data/q1.json"  
Q2_FILE =  "data/q2.json"  
Q3_FILE =  "data/q3.json"  
Q4_FILE =  "data/q4.json"  


# Question-to-Sheet Mapping
#QUESTION_TO_SHEET = {
#    "What types of trips are occurring within Napa County?": "TTyp1",
#    "What types of inter-county trips are occurring at Napa County gateways?": "GWTTyp1",
#    "Where are trips into Napa County coming from?": "TT1",
#    "Where are trips starting and ending within Napa County?": "OD1",
#}

#tables_json = {
#    "TTyp1": {
#        "Trip Type": [
#            "Intra-Napa County Trips",
#            "Trips into Napa County",
#            "Trips out of Napa County",
#            "Napa County Pass-Through Trips",
#            "Total Trips Interacting with Napa County"
#        ],
#        "Trips": [238000, 52000, 51000, 12000, 353000],
#        "Previous TBS Trips": [238000, 48000, 47000, 12000, 345000],
#        "Percent": ["67%", "15%", "15%", "3%", "100%"],
#        "Previous TBS Percentage": ["68%", "14%", "14%", "3%", "100%"]
#    },
#    "GWTTyp1": {
#        "Napa County Gateway": [
#            "SR 29 at Napa/Solano County Line - Northbound",
#            "SR 29 at Napa/Solano County Line - Southbound",
#            "SR 12 at Napa/Solano County Line - Westbound",
#            "SR 12 at Napa/Solano County Line - Eastbound",
#            "SR 29 at Napa/Lake County Line - Southbound",
#            "SR 29 at Napa/Lake County Line - Northbound",
#            "SR 128 at Napa/Sonoma County Line - Eastbound",
#            "SR 128 at Napa/Sonoma County Line - Westbound",
#            "SR 12 at Napa/Sonoma County Line - Eastbound",
#            "SR 12 at Napa/Sonoma County Line - Westbound",
#            "SR 128 at Napa/Solano County Line - Westbound",
#            "SR 128 at Napa/Solano County Line - Eastbound",
#            "SMR at Napa/Sonoma County Line - Westbound",
#            "SMR at Napa/Sonoma County Line - Eastbound",
#            "All Napa County Gateway Trips"
#        ],
#        "Total Trips": [
#            23000, 24000, 24000, 22000, 4000, 4000, 1000, 1000, 16000, 15000,
#            600, 600, 400, 300, 124000
#        ],
#        "Napa County Pass-Through Trips": [
#            "4%", "6%", "16%", "16%", "56%", "48%", "22%", "26%", "29%", "29%",
#            "17%", "11%", "3%", "7%", "10%"
#        ],
#        "Trips into Napa County": [
#            "82%", "2%", "83%", "0%", "27%", "0%", "70%", "0%", "70%", "70%",
#            "69%", "69%", "33%", "50%", "42%"
#        ],
#        "Trips out of Napa County": [
#            "1%", "76%", "0%", "84%", "6%", "27%", "0%", "70%", "0%", "0%",
#            "6%", "6%", "60%", "0%", "41%"
#        ]
#    },
#    "TT1": {
#        "Location": [
#            "Vallejo", "Fairfield", "Santa Rosa", "Vacaville", "City of Sonoma", "Total"
#        ],
#        "Calistoga": [16, 35, 149, 9, 4, 213],
#        "Angwin": [48, 0, 7, 0, 4, 59],
#        "Saint Helena": [67, 207, 189, 126, 66, 655],
#        "Yountville": [69, 75, 26, 107, 105, 382],
#        "Napa": [5125, 4384, 3131, 2005, 1290, 15935],
#        "American Canyon": [6534, 1449, 326, 412, 154, 8875],
#        "Unincorporated": [1374, 1271, 1350, 762, 303, 5060],
#        "Total": [13233, 7421, 5178, 3421, 1926, 31179]
#    },
#    "OD1": {
#        "City": [
#            "Calistoga", "Angwin", "Saint Helena", "Yountville",
#            "Napa", "American Canyon", "Unincorporated", "Total"
#        ],
#        "Trip Origins - Trips": [6600, 3600, 11800, 5200, 187600, 33100, 41000, 289100],
#        "Trip Origins - Percent": ["2%", "1%", "4%", "2%", "65%", "11%", "14%", "100%"],
#        "Trip Destinations - Trips": [6600, 3600, 11900, 5400, 186500, 33400, 43000, 290300],
#        "Trip Destinations - Percent": ["2%", "1%", "4%", "2%", "64%", "12%", "15%", "100%"]
#    }
#}

#tables_title_json = {
#    "TTyp1": ["Table 4.1: Weekday All-Day Trip Types"],
#    "GWTTyp1": ["Table 4.2: Weekday All-Day Gateway Trip Types"],
#    "TT1": ["Table 4.3: Weekday All-Day Top Five Generators of Inbound Trips"],
#    "OD1": ["Table 4.4: Weekday All-Day Origins and Destinations by City"]
#}

# Load the JSON file into a Python dictionary
with open(Json_table_FILE, "r") as file:
    tables_json = json.load(file)

# Load the JSON file into a Python dictionary
with open(Json_title_FILE, "r") as file:
    tables_title_json = json.load(file)

# Load the JSON file into a Python dictionary
with open(Q1_FILE, "r") as file:
    q1 = json.load(file)

# Load the JSON file into a Python dictionary
with open(Q2_FILE, "r") as file:
    q2 = json.load(file)

# Load the JSON file into a Python dictionary
with open(Q3_FILE, "r") as file:
    q3 = json.load(file)

# Load the JSON file into a Python dictionary
with open(Q4_FILE, "r") as file:
    q4 = json.load(file)


def create_server(app_dir):
    def server(input, output, session):

        @render.image
        def rsglogo():
            return conlogo()
        
        @render.image
        def clientlogo():
            return vendorlogo()
        
        @render.plot
        def bar_chart():
            # Sample data for bar chart
            categories = ["Category A", "Category B", "Category C"]
            values = [10, 20, 30]

            return create_bar_chart(categories, values, plottitle = "Bar Chart Example")

        @render.plot
        def pie_chart():
            # Data for pie chart
            labels = ["Home-Based Work", "Home-Based Other", "Non-Home-Based"]
            #labels = [f"{icons['fa-car']} Cars", f"{icons['fa-bicycle']} Bikes", f"{icons['fa-bus']} Buses"]
            #labels = [f"{fa.icon_svg("briefcase")} Cars", f"{fa.icon_svg("briefcase")} Bikes", f"{fa.icon_svg("briefcase")} Buses"]
            

            sizes = [22, 41, 37]
            colors = ["#00b3b3", "#70d281", "#ff7f0e"]
            explode = (0.1, 0, 0)  # Explode the first slice for emphasis
            
            return create_pie_chart(labels, sizes, plottitle ="What Types of Trips are Occuring within Napa County on a Weekday?")

        @render.plot
        def stacked_bar_chart():
            # Data for the stacked bar chart
            categories = ["Early AM", "AM Peak", "Mid-Day", "PM Peak", "Evening"]
            intra_napa = [2000, 16000, 14000, 12000, 5000]
            into_napa = [1000, 8000, 2000, 3000, 2000]
            out_napa = [2000, 4000, 2000, 5000, 1000]

            return create_stacked_bar_chart(categories, intra_napa, into_napa, out_napa)
        
        # Reactive values to track selections
        selected_origin = reactive.Value(None)
        selected_destination = reactive.Value(None)

        @reactive.Effect
        #@reactive.event(input)
        @reactive.event(input.origin_map_click, input.destination_map_click)
        def update_selections():
            """Update selections when a map is clicked."""
            selected_origin.set(input.origin_map_click.get("customdata"))
            selected_destination.set(input.destination_map_click.get("customdata"))

        @reactive.Calc
        def filtered_data():
            """Filter trips based on selected origin or destination."""
            return filter_trips(trips, zones, selected_origin.get(), selected_destination.get())

        @output
        #@render.plot
        #@render.ui
        @render_widget
        def origin_map():
            filtered_origins, _ = filtered_data()
            fig = px.choropleth(
                filtered_origins,
                geojson=filtered_origins.geometry,
                locations=filtered_origins.index,
                color="trip_count",
                title="Trip Origins",
                custom_data=["FPID"],
                #mapbox_style="open-street-map",
                #zoom=7.5,
                #center={"lat": filtered_origins.geometry.centroid.y.mean(), "lon": filtered_origins.geometry.centroid.x.mean()},
                #opacity=0.7,
                # color_continuous_scale="amp",
                # labels={
                #     "Zone ID": "FPID",
                #     "Trips": "trip_count"
                # }
            )
            fig.update_geos(fitbounds="locations", visible=False)
            #fig.update_layout(coloraxis_colorbar=dict(title="Trip Count"))  # Add legend
            return fig
            #return ui.HTML(fig.to_html())
            #return ui.HTML(fig.to_html(full_html=False, include_plotlyjs="cdn"))

        @output
        #@render.plot
        #@render.ui
        @render_widget
        def destination_map():
            _, filtered_destinations = filtered_data()
            fig = px.choropleth(
                filtered_destinations,
                geojson=filtered_destinations.geometry,
                locations=filtered_destinations.index,
                color="trip_count",
                title="Trip Destinations",
                custom_data=["FPID"]
            )
            fig.update_geos(fitbounds="locations", visible=False)
            #fig.update_layout(coloraxis_colorbar=dict(title="Trip Count"))  # Add legend
            return fig
            #return ui.HTML(fig.to_html())
            #return ui.HTML(fig.to_html(full_html=False, include_plotlyjs="cdn"))

        @reactive.Calc
        def selected_sheet1():
            """Get the corresponding sheet name for the selected question."""
            question1 = input.question_selector1()
            return q1.get(question1, None)
            
        @reactive.Calc
        def sheet_data1():
            sheet_name = selected_sheet1()
            data = pd.DataFrame(tables_json[sheet_name])
            title = tables_title_json[sheet_name]
            return {"title": title, "data": data}

        # Render the table title
        @output
        @render.ui
        def table_title1():
            title = sheet_data1()["title"]
            return ui.tags.h5(title, style="text-align: center; margin-top: 20px;")

        # Render the table
        @output
        @render.table
        def trip_table1():
            data = sheet_data1()["data"]
            if data.empty:
                return pd.DataFrame({"Error": ["No data available"]})
            # Return the raw DataFrame without styling
            return data.reset_index(drop=True).style.bar(color='#52bf89')

        @reactive.Calc
        def selected_sheet2():
            """Get the corresponding sheet name for the selected question."""
            question2 = input.question_selector2()
            return q2.get(question2, None)
            
        @reactive.Calc
        def sheet_data2():
            sheet_name = selected_sheet2()
            data = pd.DataFrame(tables_json[sheet_name])
            title = tables_title_json[sheet_name]
            return {"title": title, "data": data}

        # Render the table title
        @output
        @render.ui
        def table_title2():
            title = sheet_data2()["title"]
            return ui.tags.h5(title, style="text-align: center; margin-top: 20px;")

        # Render the table
        @output
        @render.table
        def trip_table2():
            data = sheet_data2()["data"]
            if data.empty:
                return pd.DataFrame({"Error": ["No data available"]})
            # Return the raw DataFrame without styling
            return data.reset_index(drop=True).style.bar(color='#52bf89')

        @reactive.Calc
        def selected_sheet3():
            """Get the corresponding sheet name for the selected question."""
            question3 = input.question_selector3()
            return q3.get(question3, None)
            
        @reactive.Calc
        def sheet_data3():
            sheet_name = selected_sheet3()
            data = pd.DataFrame(tables_json[sheet_name])
            title = tables_title_json[sheet_name]
            return {"title": title, "data": data}

        # Render the table title
        @output
        @render.ui
        def table_title3():
            title = sheet_data3()["title"]
            return ui.tags.h5(title, style="text-align: center; margin-top: 20px;")

        # Render the table
        @output
        @render.table
        def trip_table3():
            data = sheet_data3()["data"]
            if data.empty:
                return pd.DataFrame({"Error": ["No data available"]})
            # Return the raw DataFrame without styling
            return data.reset_index(drop=True).style.bar(color='#52bf89')

        @reactive.Calc
        def selected_sheet4():
            """Get the corresponding sheet name for the selected question."""
            question4 = input.question_selector4()
            return q4.get(question4, None)
            
        @reactive.Calc
        def sheet_data4():
            sheet_name = selected_sheet4()
            data = pd.DataFrame(tables_json[sheet_name])
            title = tables_title_json[sheet_name]
            return {"title": title, "data": data}

        # Render the table title
        @output
        @render.ui
        def table_title4():
            title = sheet_data4()["title"]
            return ui.tags.h5(title, style="text-align: center; margin-top: 20px;")

        # Render the table
        @output
        @render.table
        def trip_table4():
            data = sheet_data4()["data"]
            if data.empty:
                return pd.DataFrame({"Error": ["No data available"]})
            # Return the raw DataFrame without styling
            return data.reset_index(drop=True).style.bar(color='#52bf89')
    
    return server