Update app.py
Browse files
app.py
CHANGED
@@ -1,37 +1,26 @@
|
|
1 |
-
# Standard library imports
|
2 |
import logging
|
3 |
-
import
|
4 |
-
import math
|
5 |
-
|
6 |
-
# Third-party imports
|
7 |
import pandas as pd
|
|
|
|
|
8 |
import numpy as np
|
9 |
import geopandas as gpd
|
10 |
-
import folium
|
11 |
-
import plotly.express as px
|
12 |
from folium.plugins import MarkerCluster
|
13 |
from sklearn.neighbors import BallTree
|
14 |
from geopy.geocoders import Nominatim
|
15 |
from geopy.extra.rate_limiter import RateLimiter
|
16 |
-
|
17 |
-
|
18 |
-
import gradio as gr
|
19 |
-
from gradio.components import Map # Import Map component
|
20 |
|
21 |
# Logger setup
|
22 |
logging.basicConfig(level=logging.INFO)
|
23 |
logger = logging.getLogger(__name__)
|
24 |
|
25 |
# Load datasets
|
26 |
-
|
27 |
-
|
28 |
-
cbg_geographic_data = pd.read_csv("data/cbg_geographic_data.csv")
|
29 |
-
except FileNotFoundError as e:
|
30 |
-
logger.error(f"Error loading data files: {e}")
|
31 |
-
raise
|
32 |
|
33 |
# Create DataFrames for the 2020 and 2010 populations
|
34 |
-
# For demonstration, we are using hardcoded data
|
35 |
population_2020_data = {
|
36 |
'County': ['Shelby', 'Davidson', 'Knox', 'Hamilton', 'Rutherford', 'Williamson', 'Montgomery', 'Sumner', 'Blount', 'Washington',
|
37 |
'Madison', 'Sevier', 'Maury', 'Wilson', 'Bradley'],
|
@@ -47,105 +36,94 @@ df_population_2010.rename(columns={'cntyname': 'County', 'pop10': 'Population_20
|
|
47 |
df_population_comparison = pd.merge(df_population_2010, df_population_2020, on='County')
|
48 |
|
49 |
# Define Business Types
|
50 |
-
|
51 |
-
|
52 |
-
|
53 |
-
|
54 |
-
|
55 |
-
|
56 |
-
|
57 |
-
|
58 |
-
|
59 |
-
|
60 |
-
|
61 |
-
|
62 |
-
|
63 |
-
return "Advance Auto"
|
64 |
-
elif any(dealer in name for dealer in ["toyota", "honda", "kia", "nissan", "chevy", "ford", "carmax", "gmc"]):
|
65 |
-
return "Car Dealership"
|
66 |
-
else:
|
67 |
-
return "Other Auto Repair Shops"
|
68 |
-
|
69 |
-
df_md_final1['business_type'] = df_md_final1['name'].apply(assign_business_type)
|
70 |
-
|
71 |
-
# Load shapefiles
|
72 |
-
def load_shapefile(shapefile_path, state_fips='47', state_abbr='TN'):
|
73 |
-
if not os.path.exists(shapefile_path):
|
74 |
-
raise FileNotFoundError(f"Shapefile not found at {shapefile_path}. Please ensure the file exists.")
|
75 |
-
geo_df = gpd.read_file(shapefile_path)
|
76 |
-
if 'statefp' in geo_df.columns:
|
77 |
-
geo_df = geo_df[geo_df['statefp'] == state_fips]
|
78 |
-
elif 'hsastate' in geo_df.columns:
|
79 |
-
geo_df = geo_df[geo_df['hsastate'] == state_abbr]
|
80 |
-
elif 'hrrstate' in geo_df.columns:
|
81 |
-
geo_df = geo_df[geo_df['hrrstate'] == state_abbr]
|
82 |
-
elif 'STATEFP' in geo_df.columns:
|
83 |
-
geo_df = geo_df[geo_df['STATEFP'] == state_fips]
|
84 |
-
else:
|
85 |
-
raise KeyError("Column to filter state in shapefile not found.")
|
86 |
-
return geo_df
|
87 |
|
88 |
# Load the County shapefile
|
89 |
county_shapefile_path = "data/county/01_county-shape-file.shp"
|
90 |
-
|
|
|
|
|
|
|
|
|
91 |
|
92 |
# Load the HSA shapefile
|
93 |
hsa_shapefile_path = "data/hsa/01_hsa-shape-file.shp"
|
94 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
95 |
|
96 |
# Load the HRR shapefile
|
97 |
hrr_shapefile_path = "data/hrr/01_hrr-shape-file.shp"
|
98 |
-
|
99 |
-
|
100 |
-
|
101 |
-
|
102 |
-
|
103 |
-
|
104 |
-
|
105 |
-
|
106 |
-
|
107 |
-
|
108 |
-
"Other Auto Repair Shops": "gray"
|
109 |
-
}
|
110 |
|
111 |
# Function to create a Folium map with selected geographical boundaries and markers
|
112 |
-
def create_map(geo_layer="Counties", business_filters=
|
113 |
-
if business_filters is None:
|
114 |
-
business_filters = ["All"]
|
115 |
logger.info(f"Creating map with geo_layer: {geo_layer} and business_filters: {business_filters}")
|
116 |
|
117 |
m = folium.Map(location=[35.8601, -86.6602], zoom_start=7)
|
118 |
|
119 |
-
|
120 |
-
|
121 |
-
|
122 |
-
|
123 |
-
|
124 |
-
|
125 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
126 |
|
127 |
# Initialize Marker Cluster
|
128 |
marker_cluster = MarkerCluster().add_to(m)
|
129 |
|
130 |
-
# Filter based on Business
|
131 |
if "All" not in business_filters:
|
132 |
filtered_df = df_md_final1[df_md_final1['business_type'].isin(business_filters)]
|
133 |
else:
|
134 |
filtered_df = df_md_final1.copy()
|
135 |
|
136 |
logger.info(f"Filtered {len(filtered_df)} businesses based on filters: {business_filters}")
|
137 |
-
|
138 |
# Add markers to the map
|
139 |
for _, row in filtered_df.iterrows():
|
140 |
-
business_type = row['business_type']
|
141 |
-
marker_color = business_colors.get(business_type, 'blue')
|
142 |
folium.Marker(
|
143 |
location=[row['md_y'], row['md_x']],
|
144 |
-
popup=
|
145 |
-
|
146 |
-
f"{row.get('city', 'N/A')}, TN {row.get('postal_code', 'N/A')}",
|
147 |
-
max_width=300),
|
148 |
-
icon=folium.Icon(color=marker_color, icon='info-sign')
|
149 |
).add_to(marker_cluster)
|
150 |
|
151 |
folium.LayerControl().add_to(m)
|
@@ -170,7 +148,6 @@ def plot_2020_population_top15():
|
|
170 |
def plot_population_comparison():
|
171 |
df_melted = df_population_comparison.melt(id_vars='County', value_vars=['Population_2010', 'Population_2020'],
|
172 |
var_name='Year', value_name='Population')
|
173 |
-
df_melted['Year'] = df_melted['Year'].str.replace('Population_', '')
|
174 |
fig = px.bar(df_melted,
|
175 |
x='County',
|
176 |
y='Population',
|
@@ -178,12 +155,13 @@ def plot_population_comparison():
|
|
178 |
barmode='group',
|
179 |
title="Tennessee Population Comparison: 2010 vs 2020",
|
180 |
labels={'County': 'County', 'Population': 'Population'},
|
181 |
-
color_discrete_map={'
|
182 |
|
183 |
fig.update_layout(xaxis={'categoryorder': 'total descending'}, template='plotly_white')
|
184 |
return fig
|
185 |
|
186 |
# Nearest Neighbor Search Setup
|
|
|
187 |
def prepare_nearest_neighbor():
|
188 |
# Convert coordinates to radians for BallTree
|
189 |
coords = df_md_final1[['md_y', 'md_x']].to_numpy()
|
@@ -229,11 +207,7 @@ def find_nearest_shop(address=None, latitude=None, longitude=None):
|
|
229 |
# Add nearest shop marker
|
230 |
folium.Marker(
|
231 |
location=[nearest_shop['md_y'], nearest_shop['md_x']],
|
232 |
-
popup=
|
233 |
-
f"Nearest Shop: {nearest_shop['name']}<br>{nearest_shop.get('address', 'N/A')}, "
|
234 |
-
f"{nearest_shop.get('city', 'N/A')}, TN {nearest_shop.get('postal_code', 'N/A')}<br>"
|
235 |
-
f"Distance: {distance_km:.2f} km",
|
236 |
-
max_width=300),
|
237 |
icon=folium.Icon(color='green', icon='shopping-cart')
|
238 |
).add_to(m)
|
239 |
|
@@ -248,9 +222,78 @@ with gr.Blocks(theme=gr.themes.Default()) as app:
|
|
248 |
|
249 |
with gr.Tab("Overview"):
|
250 |
gr.Markdown("## π Tennessee Population Statistics")
|
251 |
-
|
252 |
-
|
|
|
|
|
|
|
|
|
|
|
253 |
gr.Markdown("### π οΈ Auto Businesses in Tennessee")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
254 |
map_output_overview = gr.HTML(create_map(geo_layer="Counties", business_filters=["All"]))
|
255 |
|
256 |
with gr.Tab("π Shops in TN Counties"):
|
@@ -318,8 +361,7 @@ with gr.Blocks(theme=gr.themes.Default()) as app:
|
|
318 |
address_input = gr.Textbox(label="Enter Address", placeholder="e.g., 123 Main St, Nashville, TN")
|
319 |
address_suggestions = gr.Dropdown(label="Suggestions", choices=[])
|
320 |
gr.Markdown("**OR**")
|
321 |
-
|
322 |
-
location_input = Map(label="Select Location") # Use Map component
|
323 |
with gr.Column(scale=4):
|
324 |
nearest_map = gr.HTML()
|
325 |
message_output = gr.Textbox(label="Message", interactive=False)
|
@@ -360,22 +402,12 @@ with gr.Blocks(theme=gr.themes.Default()) as app:
|
|
360 |
message, updated_map = find_nearest_shop(address=selected_address)
|
361 |
return message, updated_map
|
362 |
|
363 |
-
# Function to update the map based on map click
|
364 |
-
def update_nearest_map_from_click(map_data):
|
365 |
-
if not map_data:
|
366 |
-
return "Please select a location on the map.", None
|
367 |
-
latitude, longitude = map_data['latitude'], map_data['longitude']
|
368 |
-
message, updated_map = find_nearest_shop(latitude=latitude, longitude=longitude)
|
369 |
-
return message, updated_map
|
370 |
-
|
371 |
# When the user types in the address_input, update suggestions
|
372 |
address_input.input(fn=get_address_suggestions, inputs=address_input, outputs=address_suggestions, every=1.0)
|
373 |
# When the user presses Enter in address_input, update map
|
374 |
address_input.submit(fn=update_nearest_map_from_input, inputs=address_input, outputs=[message_output, nearest_map])
|
375 |
# When the user selects an address from suggestions, update the map
|
376 |
address_suggestions.change(fn=update_nearest_map_from_suggestion, inputs=address_suggestions, outputs=[message_output, nearest_map])
|
377 |
-
# When the user clicks on the map
|
378 |
-
location_input.change(fn=update_nearest_map_from_click, inputs=location_input, outputs=[message_output, nearest_map])
|
379 |
|
380 |
with gr.Tab("π Help"):
|
381 |
gr.Markdown("""
|
@@ -387,15 +419,14 @@ with gr.Blocks(theme=gr.themes.Default()) as app:
|
|
387 |
- **Filter by Business Type:** Use the checkboxes to select one or multiple business types to display on the map.
|
388 |
- **Filter by Geographical Area:** Depending on the tab, you can filter businesses based on Counties, HSAs, or HRRs.
|
389 |
- **Reset Filters:** Click the reset button to clear all selected filters and view all businesses.
|
390 |
-
- **Interactive Map:** Zoom in/out, click on markers to view business details.
|
391 |
|
392 |
- **Nearest Shop Finder Tab:**
|
393 |
- **Enter Address:** Type your address in the textbox. As you type, address suggestions will appear. Select one or press Enter to find the nearest auto shop.
|
394 |
-
- **Click on Map:** Alternatively, click on the map to select your location.
|
395 |
- **View Results:** The map will display your location and the nearest auto shop with a line connecting them.
|
396 |
|
397 |
""")
|
398 |
|
399 |
gr.Markdown("### π Source: Yellow Pages")
|
400 |
|
401 |
-
app.launch(server_name="0.0.0.0", server_port=7860, share=True)
|
|
|
|
|
1 |
import logging
|
2 |
+
import gradio as gr
|
|
|
|
|
|
|
3 |
import pandas as pd
|
4 |
+
import plotly.express as px
|
5 |
+
import folium
|
6 |
import numpy as np
|
7 |
import geopandas as gpd
|
|
|
|
|
8 |
from folium.plugins import MarkerCluster
|
9 |
from sklearn.neighbors import BallTree
|
10 |
from geopy.geocoders import Nominatim
|
11 |
from geopy.extra.rate_limiter import RateLimiter
|
12 |
+
import math
|
13 |
+
import os
|
|
|
|
|
14 |
|
15 |
# Logger setup
|
16 |
logging.basicConfig(level=logging.INFO)
|
17 |
logger = logging.getLogger(__name__)
|
18 |
|
19 |
# Load datasets
|
20 |
+
df_md_final1 = pd.read_csv("data/location-of-auto-businesses.csv")
|
21 |
+
cbg_geographic_data = pd.read_csv("data/cbg_geographic_data.csv")
|
|
|
|
|
|
|
|
|
22 |
|
23 |
# Create DataFrames for the 2020 and 2010 populations
|
|
|
24 |
population_2020_data = {
|
25 |
'County': ['Shelby', 'Davidson', 'Knox', 'Hamilton', 'Rutherford', 'Williamson', 'Montgomery', 'Sumner', 'Blount', 'Washington',
|
26 |
'Madison', 'Sevier', 'Maury', 'Wilson', 'Bradley'],
|
|
|
36 |
df_population_comparison = pd.merge(df_population_2010, df_population_2020, on='County')
|
37 |
|
38 |
# Define Business Types
|
39 |
+
df_md_final1['business_type'] = np.where(df_md_final1['name'].str.contains("Autozone", case=False, na=False), "Autozone",
|
40 |
+
np.where(df_md_final1['name'].str.contains("Napa Auto Parts", case=False, na=False), "Napa Auto",
|
41 |
+
np.where(df_md_final1['name'].str.contains("Firestone Complete Auto Care", case=False, na=False), "Firestone",
|
42 |
+
np.where(df_md_final1['name'].str.contains("O'Reilly Auto Parts", case=False, na=False), "O'Reilly Auto",
|
43 |
+
np.where(df_md_final1['name'].str.contains("Advance Auto Parts", case=False, na=False), "Advance Auto",
|
44 |
+
np.where(df_md_final1['name'].str.contains("Toyota|Honda|Kia|Nissan|Chevy|Ford|Carmax|GMC", case=False, na=False),
|
45 |
+
"Car Dealership",
|
46 |
+
"Other Auto Repair Shops")
|
47 |
+
)
|
48 |
+
)
|
49 |
+
)
|
50 |
+
)
|
51 |
+
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
52 |
|
53 |
# Load the County shapefile
|
54 |
county_shapefile_path = "data/county/01_county-shape-file.shp"
|
55 |
+
if not os.path.exists(county_shapefile_path):
|
56 |
+
raise FileNotFoundError(f"County shapefile not found at {county_shapefile_path}. Please ensure the file exists.")
|
57 |
+
|
58 |
+
counties_geo = gpd.read_file(county_shapefile_path)
|
59 |
+
counties_geo = counties_geo[counties_geo['statefp'] == '47'] # Tennessee FIPS code
|
60 |
|
61 |
# Load the HSA shapefile
|
62 |
hsa_shapefile_path = "data/hsa/01_hsa-shape-file.shp"
|
63 |
+
if not os.path.exists(hsa_shapefile_path):
|
64 |
+
raise FileNotFoundError(f"HSA shapefile not found at {hsa_shapefile_path}. Please ensure the file exists.")
|
65 |
+
|
66 |
+
hsa_geo = gpd.read_file(hsa_shapefile_path)
|
67 |
+
if 'hsastate' in hsa_geo.columns:
|
68 |
+
hsa_geo = hsa_geo[hsa_geo['hsastate'].isin(['TN'])]
|
69 |
+
elif 'STATEFP' in hsa_geo.columns:
|
70 |
+
hsa_geo = hsa_geo[hsa_geo['STATEFP'].isin(['47'])]
|
71 |
+
else:
|
72 |
+
raise KeyError("Column to filter state in HSA shapefile not found.")
|
73 |
|
74 |
# Load the HRR shapefile
|
75 |
hrr_shapefile_path = "data/hrr/01_hrr-shape-file.shp"
|
76 |
+
if not os.path.exists(hrr_shapefile_path):
|
77 |
+
raise FileNotFoundError(f"HRR shapefile not found at {hrr_shapefile_path}. Please ensure the file exists.")
|
78 |
+
|
79 |
+
hrr_geo = gpd.read_file(hrr_shapefile_path)
|
80 |
+
if 'hrrstate' in hrr_geo.columns:
|
81 |
+
hrr_geo = hrr_geo[hrr_geo['hrrstate'].isin(['TN'])]
|
82 |
+
elif 'STATEFP' in hrr_geo.columns:
|
83 |
+
hrr_geo = hrr_geo[hrr_geo['STATEFP'].isin(['47'])]
|
84 |
+
else:
|
85 |
+
raise KeyError("Column to filter state in HRR shapefile not found.")
|
|
|
|
|
86 |
|
87 |
# Function to create a Folium map with selected geographical boundaries and markers
|
88 |
+
def create_map(geo_layer="Counties", business_filters=["All"]):
|
|
|
|
|
89 |
logger.info(f"Creating map with geo_layer: {geo_layer} and business_filters: {business_filters}")
|
90 |
|
91 |
m = folium.Map(location=[35.8601, -86.6602], zoom_start=7)
|
92 |
|
93 |
+
try:
|
94 |
+
# Select the appropriate GeoDataFrame based on geo_layer
|
95 |
+
if geo_layer == "Counties":
|
96 |
+
geo_data = counties_geo
|
97 |
+
elif geo_layer == "HSAs":
|
98 |
+
geo_data = hsa_geo
|
99 |
+
elif geo_layer == "HRRs":
|
100 |
+
geo_data = hrr_geo
|
101 |
+
else:
|
102 |
+
geo_data = counties_geo # Default to counties
|
103 |
+
logger.info(f"Geo layer {geo_layer} selected.")
|
104 |
+
|
105 |
+
# Add selected geographical boundaries
|
106 |
+
folium.GeoJson(geo_data, name=geo_layer).add_to(m)
|
107 |
+
except Exception as e:
|
108 |
+
logger.error(f"Error loading GeoData for {geo_layer}: {e}")
|
109 |
|
110 |
# Initialize Marker Cluster
|
111 |
marker_cluster = MarkerCluster().add_to(m)
|
112 |
|
113 |
+
# Filter based on Business
|
114 |
if "All" not in business_filters:
|
115 |
filtered_df = df_md_final1[df_md_final1['business_type'].isin(business_filters)]
|
116 |
else:
|
117 |
filtered_df = df_md_final1.copy()
|
118 |
|
119 |
logger.info(f"Filtered {len(filtered_df)} businesses based on filters: {business_filters}")
|
120 |
+
|
121 |
# Add markers to the map
|
122 |
for _, row in filtered_df.iterrows():
|
|
|
|
|
123 |
folium.Marker(
|
124 |
location=[row['md_y'], row['md_x']],
|
125 |
+
popup=f"<b>{row['name']}</b><br>{row.get('address', 'N/A')}, {row.get('city', 'N/A')}, TN {row.get('postal_code', 'N/A')}",
|
126 |
+
icon=folium.Icon(color='blue', icon='info-sign')
|
|
|
|
|
|
|
127 |
).add_to(marker_cluster)
|
128 |
|
129 |
folium.LayerControl().add_to(m)
|
|
|
148 |
def plot_population_comparison():
|
149 |
df_melted = df_population_comparison.melt(id_vars='County', value_vars=['Population_2010', 'Population_2020'],
|
150 |
var_name='Year', value_name='Population')
|
|
|
151 |
fig = px.bar(df_melted,
|
152 |
x='County',
|
153 |
y='Population',
|
|
|
155 |
barmode='group',
|
156 |
title="Tennessee Population Comparison: 2010 vs 2020",
|
157 |
labels={'County': 'County', 'Population': 'Population'},
|
158 |
+
color_discrete_map={'Population_2010': 'purple', 'Population_2020': 'blue'})
|
159 |
|
160 |
fig.update_layout(xaxis={'categoryorder': 'total descending'}, template='plotly_white')
|
161 |
return fig
|
162 |
|
163 |
# Nearest Neighbor Search Setup
|
164 |
+
# Prepare the data for nearest neighbor search
|
165 |
def prepare_nearest_neighbor():
|
166 |
# Convert coordinates to radians for BallTree
|
167 |
coords = df_md_final1[['md_y', 'md_x']].to_numpy()
|
|
|
207 |
# Add nearest shop marker
|
208 |
folium.Marker(
|
209 |
location=[nearest_shop['md_y'], nearest_shop['md_x']],
|
210 |
+
popup=f"Nearest Shop: {nearest_shop['name']}<br>{nearest_shop.get('address', 'N/A')}, {nearest_shop.get('city', 'N/A')}, TN {nearest_shop.get('postal_code', 'N/A')}<br>Distance: {distance_km:.2f} km",
|
|
|
|
|
|
|
|
|
211 |
icon=folium.Icon(color='green', icon='shopping-cart')
|
212 |
).add_to(m)
|
213 |
|
|
|
222 |
|
223 |
with gr.Tab("Overview"):
|
224 |
gr.Markdown("## π Tennessee Population Statistics")
|
225 |
+
|
226 |
+
with gr.Row():
|
227 |
+
|
228 |
+
with gr.Column():
|
229 |
+
gr.Markdown("### 2010 vs 2020 Population Comparison")
|
230 |
+
pop_comp = gr.Plot(plot_population_comparison)
|
231 |
+
|
232 |
gr.Markdown("### π οΈ Auto Businesses in Tennessee")
|
233 |
+
|
234 |
+
manual_table = gr.Dataframe(
|
235 |
+
headers=["Location Name", "Street Address", "City", "State", "Postal Code"],
|
236 |
+
datatype=["str", "str", "str", "str", "str"],
|
237 |
+
value=[
|
238 |
+
["AutoZone Auto Parts - Nashville #2080", "100 Donelson Pike", "Nashville", "Tennessee", "37214"],
|
239 |
+
["AutoZone Auto Parts - Nashville #3597", "1007 Murfreesboro Pike", "Nashville", "Tennessee", "37217"],
|
240 |
+
["AutoZone Auto Parts - Nashville #2095", "2340 Murfreesboro Pike", "Nashville", "Tennessee", "37217"],
|
241 |
+
["AutoZone Auto Parts - Nashville #69", "2800 Gallatin Pike", "Nashville", "Tennessee", "37216"],
|
242 |
+
["AutoZone Auto Parts - Nashville #67", "2917 Nolensville Pike", "Nashville", "Tennessee", "37211"],
|
243 |
+
["AutoZone Auto Parts - Nashville #66", "3041 Dickerson", "Nashville", "Tennessee", "37207"],
|
244 |
+
["AutoZone Auto Parts - Nashville #346", "3712 Clarksville Pike", "Nashville", "Tennessee", "37218"],
|
245 |
+
["AutoZone Auto Parts - Nashville #68", "3913 Charlotte Ave", "Nashville", "Tennessee", "37209"],
|
246 |
+
["AutoZone Auto Parts - Nashville #146", "4815 Nolensville Rd", "Nashville", "Tennessee", "37211"],
|
247 |
+
["AutoZone Auto Parts - Nashville #5963", "5731 Nolensville Pike", "Nashville", "Tennessee", "37211"],
|
248 |
+
["AutoZone Auto Parts - Nashville #2302", "6217 Charlotte Pike", "Nashville", "Tennessee", "37209"],
|
249 |
+
["AutoZone Auto Parts - Nashville #6320", "7621 Hwy 70 S # 3", "Nashville", "Tennessee", "37221"],
|
250 |
+
["AutoZone Auto Parts - Knoxville #4549", "10650 Kingston Pike", "Knoxville", "Tennessee", "37922"],
|
251 |
+
["AutoZone Auto Parts - Knoxville #3884", "110 Holston Ferry Rd", "Knoxville", "Tennessee", "37924"],
|
252 |
+
["AutoZone Auto Parts - Knoxville #700", "2800 North Broadway", "Knoxville", "Tennessee", "37917"],
|
253 |
+
["AutoZone Auto Parts - Knoxville #167", "3100 Magnolia Ave", "Knoxville", "Tennessee", "37914"],
|
254 |
+
["AutoZone Auto Parts - Knoxville #228", "3315 S Chapman Hwy", "Knoxville", "Tennessee", "37920"],
|
255 |
+
["AutoZone Auto Parts - Knoxville #3603", "4768 Centerline Dr", "Knoxville", "Tennessee", "37917"],
|
256 |
+
["AutoZone Auto Parts - Knoxville #154", "4910 N Broadway", "Knoxville", "Tennessee", "37918"],
|
257 |
+
["AutoZone Auto Parts - Knoxville #308", "5715 Western Ave", "Knoxville", "Tennessee", "37921"],
|
258 |
+
["AutoZone Auto Parts - Knoxville #356", "5723 Clinton Hwy", "Knoxville", "Tennessee", "37912"],
|
259 |
+
["AutoZone Auto Parts - Knoxville #2075", "7421 Maynardville", "Knoxville", "Tennessee", "37938"],
|
260 |
+
["AutoZone Auto Parts - Knoxville #118", "8309 Kingston Pike", "Knoxville", "Tennessee", "37919"],
|
261 |
+
["AutoZone Auto Parts - Knoxville #126", "9305 Kingston Pike", "Knoxville", "Tennessee", "37922"],
|
262 |
+
["AutoZone Auto Parts - Memphis #5625", "1203 E Shelby Dr", "Memphis", "Tennessee", "38116"],
|
263 |
+
["AutoZone Auto Parts - Memphis", "1209 E Raines Rd", "Memphis", "Tennessee", "38116"],
|
264 |
+
["AutoZone Auto Parts - Memphis #4395", "1290 Lamar Ave", "Memphis", "Tennessee", "38104"],
|
265 |
+
["AutoZone Auto Parts - Memphis #8", "1363 Getwell Rd", "Memphis", "Tennessee", "38111"],
|
266 |
+
["AutoZone Auto Parts - Memphis #2", "1510 Elvis Presley Blvd", "Memphis", "Tennessee", "38106"],
|
267 |
+
["AutoZone Auto Parts - Memphis #2098", "1770 Sycamore View Rd", "Memphis", "Tennessee", "38134"],
|
268 |
+
["AutoZone Auto Parts - Memphis #27", "1850 S Third St", "Memphis", "Tennessee", "38109"],
|
269 |
+
["AutoZone Auto Parts - Memphis #5421", "2010 Whitten Rd", "Memphis", "Tennessee", "38134"],
|
270 |
+
["AutoZone Auto Parts - Memphis #30", "2076 Frayser Blvd", "Memphis", "Tennessee", "38127"],
|
271 |
+
["AutoZone Auto Parts - Memphis #46", "2323 Lamar Ave", "Memphis", "Tennessee", "38114"],
|
272 |
+
["AutoZone Auto Parts - Memphis #5756", "2449 Jackson Ave", "Memphis", "Tennessee", "38108"],
|
273 |
+
["AutoZone Auto Parts - Memphis #6000", "2761 Kirby Rd", "Memphis", "Tennessee", "38119"],
|
274 |
+
["AutoZone Auto Parts - Memphis #45", "2900 Covington Pk", "Memphis", "Tennessee", "38128"],
|
275 |
+
["AutoZone Auto Parts - Memphis #598", "3166 N Thomas", "Memphis", "Tennessee", "38127"],
|
276 |
+
["AutoZone Auto Parts - Memphis #324", "3171 Summer Ave", "Memphis", "Tennessee", "38112"],
|
277 |
+
["AutoZone Auto Parts - Chattanooga #180", "2021 E 3rd St", "Chattanooga", "Tennessee", "37404"],
|
278 |
+
["AutoZone Auto Parts - Chattanooga #5517", "2114 Gunbarrel Rd", "Chattanooga", "Tennessee", "37421"],
|
279 |
+
["AutoZone Auto Parts - Chattanooga #149", "3201 Dayton Blvd", "Chattanooga", "Tennessee", "37415"],
|
280 |
+
["AutoZone Auto Parts - Chattanooga #116", "3536 Brainerd Rd", "Chattanooga", "Tennessee", "37411"],
|
281 |
+
["AutoZone Auto Parts - Chattanooga #123", "4307 Rossville Blvd", "Chattanooga", "Tennessee", "37407"],
|
282 |
+
["AutoZone Auto Parts - Chattanooga #76", "4818 Hwy 58", "Chattanooga", "Tennessee", "37416"],
|
283 |
+
["AutoZone Auto Parts - Chattanooga #151", "5317 Ringgold Rd", "Chattanooga", "Tennessee", "37412"],
|
284 |
+
["AutoZone Auto Parts - Chattanooga #2072", "7630 E Brainerd Rd", "Chattanooga", "Tennessee", "37421"],
|
285 |
+
["AutoZone Auto Parts - Clarksville #6301", "101 Merchants Blvd", "Clarksville", "Tennessee", "37040"],
|
286 |
+
["AutoZone Auto Parts - Clarksville #64", "1105 Riverwood Pl", "Clarksville", "Tennessee", "37040"],
|
287 |
+
["AutoZone Auto Parts - Clarksville #70", "1444 Fort Campbell Blvd", "Clarksville", "Tennessee", "37042"],
|
288 |
+
["AutoZone Auto Parts - Clarksville #3890", "1832 Tiny Town Rd", "Clarksville", "Tennessee", "37042"],
|
289 |
+
["AutoZone Auto Parts - Clarksville #586", "1959 Madison St", "Clarksville", "Tennessee", "37043"],
|
290 |
+
["AutoZone Auto Parts - Clarksville #3880", "2653 Ft Campbell Blvd", "Clarksville", "Tennessee", "37042"]
|
291 |
+
],
|
292 |
+
row_count=52, # Updated for the total number of rows
|
293 |
+
interactive=False
|
294 |
+
)
|
295 |
+
|
296 |
+
gr.Markdown("### π Interactive Map")
|
297 |
map_output_overview = gr.HTML(create_map(geo_layer="Counties", business_filters=["All"]))
|
298 |
|
299 |
with gr.Tab("π Shops in TN Counties"):
|
|
|
361 |
address_input = gr.Textbox(label="Enter Address", placeholder="e.g., 123 Main St, Nashville, TN")
|
362 |
address_suggestions = gr.Dropdown(label="Suggestions", choices=[])
|
363 |
gr.Markdown("**OR**")
|
364 |
+
# Note: Interactive map clicks are not implemented in this version
|
|
|
365 |
with gr.Column(scale=4):
|
366 |
nearest_map = gr.HTML()
|
367 |
message_output = gr.Textbox(label="Message", interactive=False)
|
|
|
402 |
message, updated_map = find_nearest_shop(address=selected_address)
|
403 |
return message, updated_map
|
404 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
405 |
# When the user types in the address_input, update suggestions
|
406 |
address_input.input(fn=get_address_suggestions, inputs=address_input, outputs=address_suggestions, every=1.0)
|
407 |
# When the user presses Enter in address_input, update map
|
408 |
address_input.submit(fn=update_nearest_map_from_input, inputs=address_input, outputs=[message_output, nearest_map])
|
409 |
# When the user selects an address from suggestions, update the map
|
410 |
address_suggestions.change(fn=update_nearest_map_from_suggestion, inputs=address_suggestions, outputs=[message_output, nearest_map])
|
|
|
|
|
411 |
|
412 |
with gr.Tab("π Help"):
|
413 |
gr.Markdown("""
|
|
|
419 |
- **Filter by Business Type:** Use the checkboxes to select one or multiple business types to display on the map.
|
420 |
- **Filter by Geographical Area:** Depending on the tab, you can filter businesses based on Counties, HSAs, or HRRs.
|
421 |
- **Reset Filters:** Click the reset button to clear all selected filters and view all businesses.
|
422 |
+
- **Interactive Map:** Zoom in/out, click on markers to view business details, and use the search bar to find specific businesses.
|
423 |
|
424 |
- **Nearest Shop Finder Tab:**
|
425 |
- **Enter Address:** Type your address in the textbox. As you type, address suggestions will appear. Select one or press Enter to find the nearest auto shop.
|
|
|
426 |
- **View Results:** The map will display your location and the nearest auto shop with a line connecting them.
|
427 |
|
428 |
""")
|
429 |
|
430 |
gr.Markdown("### π Source: Yellow Pages")
|
431 |
|
432 |
+
app.launch(server_name="0.0.0.0", server_port=7860, share=True)
|