hedtorresca commited on
Commit
229f907
·
verified ·
1 Parent(s): 963bfd6

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +182 -31
app.py CHANGED
@@ -1,85 +1,236 @@
 
 
 
 
 
 
 
 
1
 
2
  import pandas as pd
3
  import folium
4
  import gradio as gr
 
5
 
6
- # ----------------- CONFIGURACIÓN -----------------
7
- DATA_PATH = "agrupacion_municipio_linea.xlsx" # Ruta al archivo que consolidaste
8
- HUB_NAME = "Bogotá" # Cambia al nombre real de tu hub
9
- HUB_LAT, HUB_LON = 4.7110, -74.0721 # Coordenadas del hub (ejemplo Bogotá)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
10
 
11
- # Colores por línea (ajusta si agregas nuevas)
12
  LINE_COLOR = {
13
  "Línea 1": "red",
14
  "Línea 2": "blue",
15
  "BIAC": "green"
16
  }
17
- # -------------------------------------------------
 
 
 
18
 
19
- def load_data():
20
- \"\"\"Carga el Excel y devuelve un DataFrame con lat/lon obligatorios.\"\"\"
21
- df = pd.read_excel(DATA_PATH)
22
- if not {{'lat','lon'}}.issubset(df.columns):
23
- raise ValueError("El archivo debe contener columnas 'lat' y 'lon' con las coordenadas de cada municipio.")
24
- return df
25
 
26
- df = load_data()
 
27
 
28
- # Desplegables dinámicos
29
- all_areas = sorted({area.strip() for sub in df['Áreas Únicas'].dropna().tolist() for area in sub.split(',')})
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
30
  all_areas.insert(0, "Todas")
31
- all_lines = ["Todas"] + sorted(df['Línea_short'].dropna().unique().tolist())
32
 
33
- def make_map(selected_area, selected_line):
34
- \"\"\"Genera el HTML de un mapa Folium filtrado.\"\"\"
35
  m = folium.Map(location=[HUB_LAT, HUB_LON], zoom_start=6, tiles="cartodbpositron")
36
 
37
- # Marcador del hub
38
  folium.Marker(
39
  [HUB_LAT, HUB_LON],
40
  tooltip=f"Hub: {HUB_NAME}",
41
  icon=folium.Icon(color='orange', icon='home')
42
  ).add_to(m)
43
 
44
- # Filtrado
45
  filt = df.copy()
46
  if selected_line != "Todas":
47
- filt = filt[filt['Línea_short'] == selected_line]
48
  if selected_area != "Todas":
49
  filt = filt[filt['Áreas Únicas'].str.contains(selected_area, case=False, na=False)]
50
 
51
- # Añadir rutas
52
  for _, row in filt.iterrows():
53
- lat, lon = row['lat'], row['lon']
54
  if pd.isna(lat) or pd.isna(lon):
55
  continue
56
  folium.Marker(
57
  [lat, lon],
58
- tooltip=(f"{row['Ciudad']} ({row['Línea_short']})\\n"
59
- f"Áreas: {row['Áreas Únicas']}"),
60
- icon=folium.Icon(color='blue', icon='circle')
61
  ).add_to(m)
62
-
63
  folium.PolyLine(
64
  [[HUB_LAT, HUB_LON], [lat, lon]],
65
- color=LINE_COLOR.get(row['Línea_short'], 'gray'),
66
  weight=2,
67
  opacity=0.8
68
  ).add_to(m)
69
 
70
  return m._repr_html_()
71
 
 
72
  demo = gr.Interface(
73
  fn=make_map,
74
  inputs=[
75
  gr.Dropdown(choices=all_areas, value="Todas", label="Filtrar por Área"),
76
  gr.Dropdown(choices=all_lines, value="Todas", label="Filtrar por Línea")
77
  ],
78
- outputs=gr.HTML(),
79
  title="Mapa de Rutas: Hub → Municipios",
80
- description="Seleccione un Área artística y/o Línea para visualizar las rutas desde el hub a cada municipio."
 
81
  )
82
 
83
  if __name__ == "__main__":
84
  demo.launch()
85
-
 
1
+ """
2
+ Standalone Gradio App: Rutas del Hub a Municipios
3
+ ------------------------------------------------
4
+ * Datos CSV embebidos (110 filas) con columnas:
5
+ Departamento, Ciudad, Línea, Áreas Únicas
6
+ * Si no existen columnas lat/lon, geocodifica con geopy
7
+ * No necesita archivos externos
8
+ """
9
 
10
  import pandas as pd
11
  import folium
12
  import gradio as gr
13
+ import io
14
 
15
+ try:
16
+ from geopy.geocoders import Nominatim
17
+ from geopy.extra.rate_limiter import RateLimiter
18
+ except ImportError:
19
+ Nominatim = None # geopy no instalada
20
+
21
+ # ============ CONFIGURACIÓN =============
22
+ DATA_CSV = """Departamento,Ciudad,Línea,Áreas Únicas
23
+ ANTIOQUIA,CAÑASGORDAS,Línea 1,"ARTES VISUALES Y PLÁSTICAS, DANZA, LITERATURA, MÚSICA, OTRA"
24
+ ANTIOQUIA,LA CEJA,BIAC,Sin información (no registrada en BD)
25
+ ANTIOQUIA,MEDELLÍN,BIAC,"ARTES VISUALES Y PLÁSTICAS, AUDIOVISUALES, CIRCO, DANZA, LITERATURA, OTRA, TEATRO"
26
+ ANTIOQUIA,MEDELLÍN,Línea 1,
27
+ ANTIOQUIA,MEDELLÍN,Línea 2,"ARTES VISUALES Y PLÁSTICAS, AUDIOVISUALES, DANZA, MÚSICA, TEATRO"
28
+ "ARCHIPIÉLAGO DE SAN ANDRÉS, PROVIDENCIA Y SANTA CATALINA",SAN ANDRÉS,Línea 1,"ARTES VISUALES Y PLÁSTICAS, LITERATURA, TEATRO"
29
+ ATLÁNTICO,BARANOA,Línea 1,"DANZA, MÚSICA, TEATRO"
30
+ ATLÁNTICO,BARRANQUILLA,BIAC,"ARTES VISUALES Y PLÁSTICAS, DANZA, LITERATURA, MÚSICA, TEATRO, TÍTERES"
31
+ ATLÁNTICO,BARRANQUILLA,Línea 2,
32
+ ATLÁNTICO,PUERTO COLOMBIA,Línea 2,"ARTES VISUALES Y PLÁSTICAS, CIRCO, DANZA, MÚSICA, TEATRO, TÍTERES"
33
+ ATLÁNTICO,SABANALARGA,BIAC,Sin información (no registrada en BD)
34
+ ATLÁNTICO,SANTO TOMÁS,Línea 2,"ARTES VISUALES Y PLÁSTICAS, DANZA, LITERATURA, TÍTERES"
35
+ ATLÁNTICO,SOLEDAD,BIAC,Sin información (no registrada en BD)
36
+ "BOGOTÁ, D.C.","BOGOTÁ, D.C.",BIAC,"ARTES VISUALES Y PLÁSTICAS, AUDIOVISUALES, DANZA, LITERATURA, MÚSICA, OTRA, TEATRO, TÍTERES"
37
+ "BOGOTÁ, D.C.","BOGOTÁ, D.C.",Línea 1,
38
+ "BOGOTÁ, D.C.","BOGOTÁ, D.C.",Línea 2,"ARTES VISUALES Y PLÁSTICAS, AUDIOVISUALES, CIRCO, DANZA, LITERATURA, MÚSICA, TEATRO, TÍTERES"
39
+ BOLÍVAR,CARTAGENA DE INDIAS,Línea 1,"ARTES VISUALES Y PLÁSTICAS, LITERATURA, MÚSICA"
40
+ BOLÍVAR,CARTAGENA DE INDIAS,Línea 2,"ARTES VISUALES Y PLÁSTICAS, CIRCO, DANZA, LITERATURA, MÚSICA, TEATRO, TÍTERES"
41
+ BOLÍVAR,MAHATES,Línea 2,"AUDIOVISUALES, DANZA, LITERATURA, MÚSICA, OTRA"
42
+ BOLÍVAR,SAN JACINTO,Línea 1,"DANZA, MÚSICA, TEATRO"
43
+ BOLÍVAR,SAN JACINTO,Línea 2,"DANZA, LITERATURA, MÚSICA, TEATRO"
44
+ BOLÍVAR,SAN JUAN NEPOMUCENO,Línea 2,"CIRCO, DANZA, MÚSICA, TEATRO"
45
+ BOLÍVAR,SOPLAVIENTO,Línea 1,"ARTES VISUALES Y PLÁSTICAS, DANZA, MÚSICA"
46
+ BOLÍVAR,TURBACO,Línea 1,"ARTES VISUALES Y PLÁSTICAS, DANZA, MÚSICA"
47
+ BOYACÁ,CHIQUINQUIRÁ,Línea 1,"ARTES VISUALES Y PLÁSTICAS, LITERATURA, OTRA"
48
+ BOYACÁ,DUITAMA,Línea 1,"ARTES VISUALES Y PLÁSTICAS, AUDIOVISUALES, CIRCO, DANZA, LITERATURA, MÚSICA, TEATRO, TÍTERES"
49
+ BOYACÁ,GACHANTIVÁ,BIAC,Sin información (no registrada en BD)
50
+ BOYACÁ,SOGAMOSO,Línea 1,"ARTES VISUALES Y PLÁSTICAS, AUDIOVISUALES, DANZA, MÚSICA, TEATRO"
51
+ BOYACÁ,TUNJA,Línea 2,"ARTES VISUALES Y PLÁSTICAS, TEATRO, TÍTERES"
52
+ BOYACÁ,VILLA DE LEYVA,Línea 1,"ARTES VISUALES Y PLÁSTICAS, AUDIOVISUALES, OTRA"
53
+ CALDAS,ANSERMA,Línea 2,"ARTES VISUALES Y PLÁSTICAS, DANZA, LITERATURA, MÚSICA, OTRA, TEATRO"
54
+ CALDAS,MANIZALES,Línea 1,"AUDIOVISUALES, DANZA, LITERATURA, MÚSICA, TEATRO, TÍTERES"
55
+ CALDAS,SAMANÁ,Línea 1,"ARTES VISUALES Y PLÁSTICAS, AUDIOVISUALES, DANZA, TEATRO"
56
+ CAQUETÁ,CARTAGENA DEL CHAIRÁ,BIAC,Sin información (no registrada en BD)
57
+ CAQUETÁ,FLORENCIA,Línea 2,"DANZA, LITERATURA, MÚSICA, TEATRO"
58
+ CAQUETÁ,LA MONTAÑITA,BIAC,"ARTES VISUALES Y PLÁSTICAS, MÚSICA, OTRA, TEATRO"
59
+ CAQUETÁ,LA MONTAÑITA,Línea 2,
60
+ CAUCA,INZÁ,Línea 2,"DANZA, MÚSICA, OTRA, TEATRO, TÍTERES"
61
+ CAUCA,MERCADERES,BIAC,Sin información (no registrada en BD)
62
+ CAUCA,POPAYÁN,BIAC,"DANZA, LITERATURA, MÚSICA, TEATRO"
63
+ CAUCA,POPAYÁN,Línea 1,
64
+ CAUCA,POPAYÁN,Línea 2,"DANZA, LITERATURA, MÚSICA, OTRA"
65
+ CAUCA,PUERTO TEJADA,BIAC,"ARTES VISUALES Y PLÁSTICAS, DANZA, MÚSICA"
66
+ CAUCA,PUERTO TEJADA,Línea 1,
67
+ CAUCA,SAN SEBASTIÁN,Línea 1,"DANZA, MÚSICA, TEATRO"
68
+ CESAR,LA JAGUA DE IBIRICO,Línea 2,"ARTES VISUALES Y PLÁSTICAS, AUDIOVISUALES, DANZA, LITERATURA, MÚSICA, TEATRO, TÍTERES"
69
+ CHOCÓ,CONDOTO,Línea 1,"MÚSICA, TEATRO"
70
+ CHOCÓ,ISTMINA,BIAC,Sin información (no registrada en BD)
71
+ CUNDINAMARCA,CHOACHÍ,Línea 2,"ARTES VISUALES Y PLÁSTICAS, DANZA, LITERATURA, MÚSICA"
72
+ CUNDINAMARCA,SOACHA,BIAC,Sin información (no registrada en BD)
73
+ CUNDINAMARCA,SOPÓ,Línea 1,"MÚSICA, TEATRO, TÍTERES"
74
+ CUNDINAMARCA,TABIO,Línea 1,"ARTES VISUALES Y PLÁSTICAS, AUDIOVISUALES, MÚSICA"
75
+ CUNDINAMARCA,TENJO,Línea 1,"ARTES VISUALES Y PLÁSTICAS, AUDIOVISUALES, DANZA, MÚSICA"
76
+ CÓRDOBA,CERETÉ,BIAC,Sin información (no registrada en BD)
77
+ CÓRDOBA,MONTERÍA,Línea 1,"ARTES VISUALES Y PLÁSTICAS, LITERATURA, TEATRO"
78
+ CÓRDOBA,PUERTO LIBERTADOR,BIAC,Sin información (no registrada en BD)
79
+ CÓRDOBA,SAHAGÚN,Línea 2,"ARTES VISUALES Y PLÁSTICAS, LITERATURA, OTRA, TEATRO"
80
+ GUAVIARE,SAN JOSÉ DEL GUAVIARE,BIAC,Sin información (no registrada en BD)
81
+ HUILA,ALGECIRAS,Línea 1,"DANZA, MÚSICA, TEATRO"
82
+ HUILA,GARZÓN,Línea 1,"DANZA, LITERATURA, MÚSICA"
83
+ HUILA,NEIVA,Línea 1,"DANZA, LITERATURA, MÚSICA"
84
+ HUILA,SAN AGUSTÍN,BIAC,Sin información (no registrada en BD)
85
+ LA GUAJIRA,MAICAO,Línea 1,"DANZA, MÚSICA, TEATRO"
86
+ LA GUAJIRA,RIOHACHA,Línea 1,"DANZA, MÚSICA, TEATRO"
87
+ LA GUAJIRA,URUMITA,Línea 2,"ARTES VISUALES Y PLÁSTICAS, AUDIOVISUALES, DANZA, MÚSICA, TEATRO, TÍTERES"
88
+ MAGDALENA,CIÉNAGA,Línea 1,"DANZA, TEATRO, TÍTERES"
89
+ MAGDALENA,EL BANCO,Línea 1,"ARTES VISUALES Y PLÁSTICAS, MÚSICA, TEATRO"
90
+ META,SAN MARTÍN,BIAC,Sin información (no registrada en BD)
91
+ META,VILLAVICENCIO,Línea 2,"ARTES VISUALES Y PLÁSTICAS, LITERATURA, MÚSICA, OTRA"
92
+ NARIÑO,LA CRUZ,Línea 2,"ARTES VISUALES Y PLÁSTICAS, DANZA, MÚSICA, TEATRO, TÍTERES"
93
+ NARIÑO,LA TOLA,Línea 1,"DANZA, LITERATURA, MÚSICA"
94
+ NARIÑO,PASTO,BIAC,"DANZA, LITERATURA, MÚSICA"
95
+ NARIÑO,PASTO,Línea 1,"CIRCO, DANZA, MÚSICA, TEATRO, TÍTERES"
96
+ NARIÑO,PASTO,Línea 2,"LITERATURA, MÚSICA, OTRA"
97
+ NARIÑO,ROBERTO PAYÁN,Línea 1,
98
+ NARIÑO,SAN ANDRÉS DE TUMACO,Línea 1,"ARTES VISUALES Y PLÁSTICAS, DANZA, MÚSICA"
99
+ NORTE DE SANTANDER,SAN JOSÉ DE CÚCUTA,Línea 2,"ARTES VISUALES Y PLÁSTICAS, AUDIOVISUALES, CIRCO, DANZA, LITERATURA, MÚSICA, OTRA, TEATRO, TÍTERES"
100
+ PUTUMAYO,SAN FRANCISCO,Línea 1,"DANZA, MÚSICA, OTRA"
101
+ PUTUMAYO,SIBUNDOY,Línea 1,"AUDIOVISUALES, MÚSICA, OTRA"
102
+ QUINDÍO,ARMENIA,BIAC,"ARTES VISUALES Y PLÁSTICAS, LITERATURA, MÚSICA"
103
+ QUINDÍO,ARMENIA,Línea 1,
104
+ QUINDÍO,BUENAVISTA,Línea 1,"LITERATURA, MÚSICA, TEATRO"
105
+ QUINDÍO,MONTENEGRO,BIAC,Sin información (no registrada en BD)
106
+ RISARALDA,PEREIRA,BIAC,"ARTES VISUALES Y PLÁSTICAS, DANZA, LITERATURA, MÚSICA, TEATRO"
107
+ RISARALDA,PEREIRA,Línea 1,
108
+ RISARALDA,PEREIRA,Línea 2,"ARTES VISUALES Y PLÁSTICAS, DANZA, LITERATURA, MÚSICA, TEATRO"
109
+ SANTANDER,BARRANCABERMEJA,Línea 2,"CIRCO, MÚSICA, TEATRO, TÍTERES"
110
+ SANTANDER,BUCARAMANGA,BIAC,"ARTES VISUALES Y PLÁSTICAS, AUDIOVISUALES, DANZA, LITERATURA, MÚSICA, TEATRO"
111
+ SANTANDER,BUCARAMANGA,Línea 1,
112
+ SANTANDER,SAN VICENTE DE CHUCURÍ,Línea 1,"ARTES VISUALES Y PLÁSTICAS, AUDIOVISUALES, LITERATURA, OTRA"
113
+ SUCRE,COROZAL,Línea 1,"DANZA, LITERATURA, MÚSICA"
114
+ SUCRE,SAMPUÉS,Línea 2,"ARTES VISUALES Y PLÁSTICAS, DANZA, LITERATURA, MÚSICA"
115
+ SUCRE,SAN JOSÉ DE TOLUVIEJO,Línea 1,"DANZA, LITERATURA, MÚSICA"
116
+ SUCRE,SINCELEJO,Línea 1,"DANZA, LITERATURA, MÚSICA"
117
+ SUCRE,SINCELEJO,Línea 2,"ARTES VISUALES Y PLÁSTICAS, DANZA, MÚSICA, TEATRO"
118
+ TOLIMA,IBAGUÉ,Línea 1,"LITERATURA, MÚSICA, TEATRO, TÍTERES"
119
+ VALLE DEL CAUCA,BUENAVENTURA,Línea 2,"ARTES VISUALES Y PLÁSTICAS, DANZA, LITERATURA, TEATRO"
120
+ VALLE DEL CAUCA,CAICEDONIA,Línea 1,"ARTES VISUALES Y PLÁSTICAS, AUDIOVISUALES, LITERATURA"
121
+ VALLE DEL CAUCA,CALIMA,Línea 1,"ARTES VISUALES Y PLÁSTICAS, MÚSICA, TEATRO"
122
+ VALLE DEL CAUCA,FLORIDA,BIAC,Sin información (no registrada en BD)
123
+ VALLE DEL CAUCA,GUACARÍ,Línea 2,"DANZA, LITERATURA, MÚSICA, TEATRO"
124
+ VALLE DEL CAUCA,GUADALAJARA DE BUGA,Línea 2,"ARTES VISUALES Y PLÁSTICAS, LITERATURA, TEATRO, TÍTERES"
125
+ VALLE DEL CAUCA,LA CUMBRE,Línea 2,"ARTES VISUALES Y PLÁSTICAS, DANZA, MÚSICA, TEATRO"
126
+ VALLE DEL CAUCA,RIOFRÍO,BIAC,Sin información (no registrada en BD)
127
+ VALLE DEL CAUCA,SAN PEDRO,Línea 1,"ARTES VISUALES Y PLÁSTICAS, DANZA, MÚSICA"
128
+ VALLE DEL CAUCA,SANTIAGO DE CALI,BIAC,"ARTES VISUALES Y PLÁSTICAS, AUDIOVISUALES, DANZA, LITERATURA, MÚSICA, TEATRO"
129
+ VALLE DEL CAUCA,SANTIAGO DE CALI,Línea 1,
130
+ VALLE DEL CAUCA,SANTIAGO DE CALI,Línea 2,"ARTES VISUALES Y PLÁSTICAS, DANZA, LITERATURA, MÚSICA, OTRA, TEATRO"
131
+ VALLE DEL CAUCA,SEVILLA,Línea 1,"ARTES VISUALES Y PLÁSTICAS, AUDIOVISUALES, DANZA, MÚSICA, TEATRO"
132
+ VALLE DEL CAUCA,TULUÁ,Línea 1,"CIRCO, DANZA, MÚSICA"
133
+ """
134
+
135
+ HUB_NAME = "Bogotá"
136
+ HUB_LAT, HUB_LON = 4.7110, -74.0721
137
 
 
138
  LINE_COLOR = {
139
  "Línea 1": "red",
140
  "Línea 2": "blue",
141
  "BIAC": "green"
142
  }
143
+ # =========================================
144
+
145
+ # Leer DataFrame
146
+ df = pd.read_csv(io.StringIO(DATA_CSV))
147
 
148
+ # ------------ Geocodificar ---------------
149
+ def geocode_missing(dframe: pd.DataFrame) -> pd.DataFrame:
150
+ if {'lat','lon'}.issubset(dframe.columns) and not dframe[['lat','lon']].isna().any().any():
151
+ return dframe
 
 
152
 
153
+ if Nominatim is None:
154
+ raise ImportError("Faltan 'lat' y 'lon' y geopy no está instalado.")
155
 
156
+ geolocator = Nominatim(user_agent="clanes_mapa_app")
157
+ geocode = RateLimiter(geolocator.geocode, min_delay_seconds=1)
158
+
159
+ if 'lat' not in dframe.columns:
160
+ dframe['lat'] = pd.NA
161
+ dframe['lon'] = pd.NA
162
+
163
+ cache = {}
164
+ for i, row in dframe.iterrows():
165
+ if not pd.isna(row['lat']) and not pd.isna(row['lon']):
166
+ continue
167
+ key = (row['Ciudad'], row['Departamento'])
168
+ if key in cache:
169
+ lat, lon = cache[key]
170
+ else:
171
+ loc = geocode(f"{row['Ciudad']}, {row['Departamento']}, Colombia")
172
+ lat, lon = (loc.latitude, loc.longitude) if loc else (pd.NA, pd.NA)
173
+ cache[key] = (lat, lon)
174
+ dframe.at[i, 'lat'] = lat
175
+ dframe.at[i, 'lon'] = lon
176
+ return dframe
177
+
178
+ df = geocode_missing(df)
179
+
180
+ # --------- Helpers para filtros ----------
181
+ def explode_areas(series, sep=','):
182
+ return {a.strip() for sub in series.dropna() for a in str(sub).split(sep)}
183
+
184
+ all_areas = sorted(explode_areas(df['Áreas Únicas']))
185
  all_areas.insert(0, "Todas")
186
+ all_lines = ["Todas"] + sorted(df['Línea'].dropna().unique().tolist())
187
 
188
+ # -------------- Mapa ---------------------
189
+ def make_map(selected_area: str, selected_line: str) -> str:
190
  m = folium.Map(location=[HUB_LAT, HUB_LON], zoom_start=6, tiles="cartodbpositron")
191
 
 
192
  folium.Marker(
193
  [HUB_LAT, HUB_LON],
194
  tooltip=f"Hub: {HUB_NAME}",
195
  icon=folium.Icon(color='orange', icon='home')
196
  ).add_to(m)
197
 
 
198
  filt = df.copy()
199
  if selected_line != "Todas":
200
+ filt = filt[filt['Línea'] == selected_line]
201
  if selected_area != "Todas":
202
  filt = filt[filt['Áreas Únicas'].str.contains(selected_area, case=False, na=False)]
203
 
 
204
  for _, row in filt.iterrows():
205
+ lat, lon = row.get('lat'), row.get('lon')
206
  if pd.isna(lat) or pd.isna(lon):
207
  continue
208
  folium.Marker(
209
  [lat, lon],
210
+ tooltip=(f"{row['Ciudad']} {row['Línea']}\nÁreas: {row['Áreas Únicas']}"),
211
+ icon=folium.Icon(color='blue', icon='user')
 
212
  ).add_to(m)
 
213
  folium.PolyLine(
214
  [[HUB_LAT, HUB_LON], [lat, lon]],
215
+ color=LINE_COLOR.get(row['Línea'], 'gray'),
216
  weight=2,
217
  opacity=0.8
218
  ).add_to(m)
219
 
220
  return m._repr_html_()
221
 
222
+ # -------------- Interface -----------------
223
  demo = gr.Interface(
224
  fn=make_map,
225
  inputs=[
226
  gr.Dropdown(choices=all_areas, value="Todas", label="Filtrar por Área"),
227
  gr.Dropdown(choices=all_lines, value="Todas", label="Filtrar por Línea")
228
  ],
229
+ outputs=gr.HTML(label="Mapa"),
230
  title="Mapa de Rutas: Hub → Municipios",
231
+ description="Filtra por Área artística y/o Línea para visualizar las rutas.",
232
+ allow_flagging="never"
233
  )
234
 
235
  if __name__ == "__main__":
236
  demo.launch()