17  Bidirectional Inputs - Maps

For maps, we need to use the external streamlit_folium library, which must be installed via pip before use - it doesn’t come bundled with Streamlit itself.

17.1 Filtering with the bidirectional Folium Component

When using this component, data is constantly being returned as the map is updated.

Let’s take a look at what is being returned as the map is updated.

import geopandas
import pandas as pd
import matplotlib.pyplot as plt
import streamlit as st
import folium
from streamlit_folium import st_folium

gp_list_gdf_sw = geopandas.read_file("https://files.catbox.moe/atzk26.gpkg")

# Filter out instances with no geometry
gp_list_gdf_sw = gp_list_gdf_sw[~gp_list_gdf_sw['geometry'].is_empty]

# Create a geometry list from the GeoDataFrame
geo_df_list = [[point.xy[1][0], point.xy[0][0]] for point in gp_list_gdf_sw.geometry]

gp_map_tooltip = folium.Map(
    location=[50.7, -4.2],
    zoom_start=8,
    tiles='openstreetmap',
    )

for i, coordinates in enumerate(geo_df_list):

    gp_map_tooltip = gp_map_tooltip.add_child(
        folium.Marker(
            location=coordinates,
            tooltip=gp_list_gdf_sw['name'].values[i],
            icon=folium.Icon(icon="user-md", prefix='fa', color="black")
            )
     )

returned_map_data = st_folium(gp_map_tooltip)

st.write(returned_map_data)

17.2 Using the returned data

Let’s get the bounds of the map to filter a dataframe to just contain the points within the area the user has zoomed to.

import geopandas
import pandas as pd
import matplotlib.pyplot as plt
import streamlit as st
import folium
from streamlit_folium import st_folium

gp_list_gdf_sw = geopandas.read_file(
    "https://files.catbox.moe/atzk26.gpkg"
    )

# Filter out instances with no geometry
gp_list_gdf_sw = gp_list_gdf_sw[~gp_list_gdf_sw['geometry'].is_empty]

# Create a geometry list from the GeoDataFrame
geo_df_list = [[point.xy[1][0], point.xy[0][0]] for point in gp_list_gdf_sw.geometry]

gp_map_tooltip = folium.Map(
    location=[50.7, -4.2],
    zoom_start=8,
    tiles='openstreetmap',
    )

for i, coordinates in enumerate(geo_df_list):

    gp_map_tooltip = gp_map_tooltip.add_child(
        folium.Marker(
            location=coordinates,
            tooltip=gp_list_gdf_sw['name'].values[i],
            icon=folium.Icon(icon="user-md", prefix='fa', color="black")
            )
     )

returned_map_data = st_folium(gp_map_tooltip)

xmin = returned_map_data['bounds']['_southWest']['lng']
xmax = returned_map_data['bounds']['_northEast']['lng']
ymin = returned_map_data['bounds']['_southWest']['lat']
ymax =  returned_map_data['bounds']['_northEast']['lat']
gp_list_gdf_filtered = gp_list_gdf_sw.cx[xmin:xmax, ymin:ymax]

st.write(f"Returning data for {len(gp_list_gdf_filtered)} practices")

st.dataframe(
    gp_list_gdf_filtered[['name', 'address_1', 'postcode', 'Total List Size']]
    )