I am using Streamlit to create a Plotly Express choropleth map of counties in Georgia (USA) with shading based on the county population. I also have a slider widget above the map for the user to change the opacity of the map. Here is the full working example of the app, including URL for reading in the data:
import streamlit as st
import geopandas as gpd
import plotly.express as px
# set slider for map layer opacity
opacity = st.slider(
label='Set map layer opacity',
min_value=0.2,
max_value=1.0,
value=0.6,
step=0.1
)
# Define a function to fetch and cache the data
@st.cache_data
def fetch_data(url):
# clean the input URL
url = url.replace(" ", "%20")
# Read the GeoJSON data from the URL
gdf = gpd.read_file(url)
# Select the required columns for choropleth map
gdf = gdf[['GEOID', 'TotPop_e22', 'geometry']]
return gdf
# read in population, geometry of Georgia counties
url = "https://services1.arcgis.com/Ug5xGQbHsD8zuZzM/arcgis/rest/services/ACS 2022 Demographic Population GeoSplitJoined/FeatureServer/9/query?outFields=*&where=1%3D1&f=geojson"
gdf = fetch_data(url)
# create mapping figure
fig = px.choropleth_mapbox(
gdf,
geojson=gdf.geometry,
locations=gdf.index,
color='TotPop_e22',
center={"lat": 32.90, "lon": -83.50},
mapbox_style='carto-positron',
zoom=6,
opacity=opacity,
height=650
)
# plot the mapping figure
st.plotly_chart(
fig,
theme='streamlit',
use_container_width=True
)
The app works fine, but I can’t figure out why the map (indeed the entire application) will re-render each time the slider changes. As the slider only changes the visual style of the map, not the underlying data structure or fig object, I am unsure how to keep this from happening.
Desired outcome: I would like the app to only load the data once, when the app initially opens, by using the st.cache_data decorator. Once the app is open, I would like to interact with the map by zooming, panning, etc. and change the opacity via the slider widget WITHOUT the entire application reloading & the map resetting each time the slider widget gets a new value for the opacity. Note that I’m already using the decorator to initially load the data from the API. Thank you for the help.
To achieve your desired outcome of running the map independently of the other parts of the app upon interacting with the ruler, you can use the decorator @st.experimental_fragment to decorate the function to renders the map. Wrap the map rendering code in a function and decorate it with fragment and it will only be reloaded and not the entire app.
I have looked at the @st.experimental_fragment documentation and made the following edits to my code to incorporate this decorator:
import streamlit as st
import geopandas as gpd
import plotly.express as px
# Define a function to fetch and cache the data
@st.cache_data
def fetch_data(url):
# clean the input URL
url = url.replace(" ", "%20")
# Read the GeoJSON data from the URL
gdf = gpd.read_file(url)
# Select the required columns for choropleth map
gdf = gdf[['GEOID', 'TotPop_e22', 'geometry']]
return gdf
# read in population, geometry of Georgia counties
url = "https://services1.arcgis.com/Ug5xGQbHsD8zuZzM/arcgis/rest/services/ACS 2022 Demographic Population GeoSplitJoined/FeatureServer/9/query?outFields=*&where=1%3D1&f=geojson"
gdf = fetch_data(url)
# create a function to render the map
@st.experimental_fragment
def render_map(gdf, opacity):
# create mapping figure
fig = px.choropleth_mapbox(
gdf,
geojson=gdf.geometry,
locations=gdf.index,
color='TotPop_e22',
center={"lat": 32.90, "lon": -83.50},
mapbox_style='carto-positron',
zoom=6,
height=650
)
# update marker opacity based on slider value
fig.update_traces(marker=dict(opacity=opacity))
return fig
# set slider for map layer opacity
opacity = st.slider(
label='Set map layer opacity',
min_value=0.2,
max_value=1.0,
value=0.6,
step=0.1
)
# create fig using defined function
fig = render_map(gdf, opacity)
# plot the mapping figure
st.plotly_chart(
fig,
theme='streamlit',
use_container_width=True
)
However, the app still does not have the desired effect. To summarize, I would like to be able to zoom in on the map and then make a change to the opacity via the slider widget in the app and have the opacity of the blue choropleth map layer change without the entire app re-running and, essentially, “starting over.” If you copy-paste the above code and run it, you’ll see that the app will still re-run any time a change is made to the opacity slider.
Have I incorrectly used the decorator or wrapped the map-rendering portion of the code inside a function? Thank you for your help!
Thanks for stopping by! We use cookies to help us understand how you interact with our website.
By clicking “Accept all”, you consent to our use of cookies. For more information, please see our privacy policy.
Cookie settings
Strictly necessary cookies
These cookies are necessary for the website to function and cannot be switched off. They are usually only set in response to actions made by you which amount to a request for services, such as setting your privacy preferences, logging in or filling in forms.
Performance cookies
These cookies allow us to count visits and traffic sources so we can measure and improve the performance of our site. They help us understand how visitors move around the site and which pages are most frequently visited.
Functional cookies
These cookies are used to record your choices and settings, maintain your preferences over time and recognize you when you return to our website. These cookies help us to personalize our content for you and remember your preferences.
Targeting cookies
These cookies may be deployed to our site by our advertising partners to build a profile of your interest and provide you with content that is relevant to you, including showing you relevant ads on other websites.