Well, I tried at first but couldn’t arrive to anything, but that was because I wan’t figuring it out , neither chatgpt haha. But, BUT! I passed the " Interactivity via the hideout prop" code from the geojson link to chatgpt and started constructing my app since there, and little by little y could add the rest of the features of my app.
I haven’t tried out on production, but in Jupyter the speed difference is really huge, I’m impressed.
Basically, the changes where:
dl.GeoJSON(data=geojson_data, filter=geojson_filter, hideout=dd_defaults, id="geojson", zoomToBounds=True,
options=dict(pointToLayer=assign(
"""function(feature, latlng){
return L.marker(latlng, {
icon: L.icon({
iconUrl: feature.properties.icon_url,
iconSize: [15, 23],
iconAnchor: [12, 23],
popupAnchor: [1, -34],
shadowSize: [41, 41]
})
}).bindTooltip(feature.properties.tooltip, {direction: "top", offset: L.point(0, -20), opacity: 0.9, className: 'marker-tooltip'})
.bindPopup(feature.properties.popup);
}""")))
I add that part to the “app.layout([html.Div([dl.Map(children” section, with the “geojson_data = dlx.dicts_to_geojson([{” previuos to the app.layout.
The other significant change was converting the return from “update_map” from a dataframe to a geojson
@app.callback(
Output('geojson', 'data'),
[Input('feature-filter', 'value'),
Input('filtro-dias', 'value'),
Input('filtro-barrios', 'value'),
Input('search-input', 'value'),
Input('rating-slider', 'value'),
Input('map-style-dropdown', 'value')]
)
def update_map(features, days, barrios, search, rating, map_style):
filtered_df = df.copy()
# Aplicar los filtros
if features:
for feature in features:
filtered_df = filtered_df[filtered_df[feature] == True]
if days:
day_filters = [f"{day}_open" for day in days]
filtered_df = filtered_df.dropna(subset=day_filters, how='all')
if barrios:
filtered_df = filtered_df[filtered_df['Barrio'].isin(barrios)]
if search:
filtered_df = filtered_df[filtered_df['Nombre'].str.contains(search, case=False)]
if rating:
filtered_df = filtered_df[(filtered_df['Rating'] >= rating[0]) & (filtered_df['Rating'] <= rating[1])]
# Convertir los datos filtrados a GeoJSON
geojson_data = dlx.dicts_to_geojson([{
"name": row["Barrio"],
"lat": row["Latitud"],
"lon": row["Longitud"],
"tooltip": f"""
<p class='nombre'>{row['Nombre']}</p>
<p class='stars'>{generate_stars(row['Rating'])}</p>
<p><span class='bold-text'>Reviews: </span>{row['Cantidad Reviews']}</p>
<p><span class='bold-text'>Dirección: </span>{row['Dirección']}</p>
""",
"popup": f"""
<h4 style='font-family: Montserrat; font-size: 16px; font-weight: bold;'><u>{row['Nombre']}</u></h4>
<p style='font-family: Montserrat; font-size: 14px;'><strong>Rating: </strong>{row['Rating']}</p>
<p style='font-family: Montserrat; font-size: 14px;'><strong>Cantidad Reviews: </strong>{row['Cantidad Reviews']}</p>
<p style='font-family: Montserrat; font-size: 14px;'><strong>Sitio Web: </strong><a href='{row['Sitio Web']}' target='_blank'>{row['Sitio Web']}</a></p>
<p style='font-family: Montserrat; font-size: 14px;'><strong>Dirección: </strong>{row['Dirección']}</p>
<div style='font-family: Montserrat; font-size: 14px;'>{' '.join(format_hours(row))}</div>
""",
"icon_url": get_icon_url(row["Rating"])
} for _, row in filtered_df.iterrows()])
return geojson_data
Before it was:
@app.callback(
Output('filtered-data', 'data'),
[Input('rating-slider', 'value'), Input('feature-filter', 'value'), Input('filtro-dias', 'value'), Input('filtro-barrios', 'value'), Input('search-input', 'value')]
)
@cache.memoize()
def filter_data(rating_range, selected_features, selected_days, selected_barrios, search_input):
filtered_df = df2[(df2['Rating'] >= rating_range[0]) & (df2['Rating'] <= rating_range[1])]
if search_input and isinstance(search_input, str):
filtered_df = filtered_df[filtered_df['Nombre'].str.contains(search_input, case=False)]
for feature in selected_features:
filtered_df = filtered_df[filtered_df[feature] == True]
for day in selected_days:
open_column = f'{day}_open'
close_column = f'{day}_close'
filtered_df = filtered_df[(~filtered_df[open_column].isna()) & (~filtered_df[close_column].isna())]
if selected_barrios:
filtered_df = filtered_df[filtered_df['Barrio'].isin(selected_barrios)]
return filtered_df.to_dict('records')
@app.callback(
Output('layer', 'children'),
Input('filtered-data', 'data')
)
@cache.memoize()
def update_map(filtered_data):
filtered_df = pd.DataFrame(filtered_data)
def generate_stars(rating):
full_star = '★'
empty_star = '☆'
return full_star * int(rating) + empty_star * (5 - int(rating))
markers = [
dl.Marker(
position=[row['Latitud'], row['Longitud']],
icon={
"iconUrl": get_marker_icon(row['Rating']),
"iconSize": [5, 5],
"iconAnchor": [10, 20],
},
children=[
dl.Tooltip(
html.Div([
html.P(row['Nombre'], className='nombre'), # Clase CSS 'nombre' añadida aquí
html.P(generate_stars(row['Rating']), className='stars'), # Clase CSS 'stars' añadida aquí
html.P([html.Span("Reviews: ", className='bold-text'), row['Cantidad Reviews']]),
html.P([html.Span("Dirección: ", className='bold-text'), row['Dirección']])
]),
className='marker-tooltip'
),
dl.Popup(
html.Div(
children=[
html.H4(html.U(row['Nombre']), style={'font-family': 'Montserrat', 'font-size': '16px', 'font-weight': 'bold'}),
html.P([html.Strong("Rating: "), str(row['Rating'])], style={'font-family': 'Montserrat', 'font-size': '14px'}),
html.P([html.Strong("Cantidad Reviews: "), str(row['Cantidad Reviews'])], style={'font-family': 'Montserrat', 'font-size': '14px'}),
html.P([html.Strong("Sitio Web: "), html.A(row['Sitio Web'], href=row['Sitio Web'], target="_blank")], style={'font-family': 'Montserrat', 'font-size': '14px'}),
html.P([html.Strong("Dirección: "), str(row['Dirección'])], style={'font-family': 'Montserrat', 'font-size': '14px'}),
*format_hours(row) # Aquí se añade el resultado de format_hours
],
className='marker-popup'
)
)
]
) for _, row in filtered_df.iterrows()
]
return markers
In the first case, both functions “filter” and the one with the markers are in the same one, and they will return a geojson instead of the dataframe and the markers. Well, I don’t really understand at all what happened lol, I just managed to guide chatgpt, of course with your advices.
I’ll keep you in touch when trying in production.