Hey Streamlit community:)
I’m building a Streamlit web app that allows users to create and edit geospatial data. The app takes various user inputs to load or generate data, which is then displayed on a map. Users can also manipulate this data by interacting with the map.
The issue I’m facing is performance-related:
- Since Streamlit reruns the script on every interaction, interacting with the map causes the entire script to rerun.
- This leads to slow and unresponsive behavior, especially when certain operations take time (e.g., loading data, processing polygons).
- I simulated this delay using
time.sleep()
, in my real code it occurs due to computationally intensive operations.
I tried to use st.session_state
to store values and prevent unnecessary reruns, but it doesn’t seem to help, since the map generation is the problem. I also came across st.fragment
, which might help, but I’m not sure if it is the proper solution and how to integrate it properly into my code.
Does anyone have suggestions on how to Prevent unnecessary reruns of time intensive operations when interacting with the map?
Here’s a simplified version of my code that demonstrates the issue. Its very crude and patched together but shows the issues im having:
import streamlit as st
from streamlit_folium import st_folium
import folium
from folium.plugins import Draw
import geojson
import numpy as np
import time
bbox=None
start_y=None
end_y=None
species=None
buffer=None
distance=None
def mapgeojson(poly_file):
##Upload the polygon file
#Get the center of the Polygon for Map display
def get_bounding_box(geom):
coords = np.array(list(geojson.utils.coords(geom)))
return [coords[:, 0].min(), coords[:, 1].min(), coords[:, 0].max(), coords[:, 1].max()]
bbox=get_bounding_box(poly_file)
#st.write (bbox)
y=bbox[2]-(bbox[2]-bbox[0])/2
x=bbox[3]-(bbox[3]-bbox[1])/2
#st.write (x,y)
#set Map center to the center of the polygon
if "center" not in st.session_state:
st.session_state["center"] = [x,y] # Default center for the map
#set Map zoom level
if "zoom" not in st.session_state:
st.session_state["zoom"] = 6 # Default zoom level
# if poly_file != st.session_state.poly: #reset if new file is uploaded
# st.session_state.poly=poly_file
# st.session_state["center"] = [x,y]
# st.session_state["zoom"] = 6
##Create the map
m = folium.Map(location=st.session_state["center"], zoom_start=st.session_state["zoom"])
fg = folium.FeatureGroup(name="Markers")
fg.add_child(folium.GeoJson(poly_file))
##Display the map
st_folium(
m,
center=st.session_state["center"],
zoom=st.session_state["zoom"],
key="new",
feature_group_to_add=fg,
height=400,
width=700,
)
def mapbbox():
#create the map
m = folium.Map(location=[0,0], zoom_start=2)
draw = Draw(export=False, draw_options={
'polyline': False, # Disable polyline
'polygon': False, # Enable polygon
'circle': False, # Disable circle
'rectangle': True, # Enable rectangle
'marker': False, # Enable marker
'circlemarker': False # Disable circle marker
},
edit_options={
'edit': False, # Enable editing of drawn shapes
'remove': True # Enable deleting of drawn shapes
})
draw.add_to(m)
def get_bounding_box(geom):
coords = np.array(list(geojson.utils.coords(geom)))
return [coords[:, 0].min(), coords[:, 1].min(), coords[:, 0].max(), coords[:, 1].max()]
#display the map
output = st_folium(m, width=700, height=500)
#get the bounding box of the last clicked polygon
if output["last_active_drawing"] is not None:
geometry = output["last_active_drawing"]["geometry"]
return get_bounding_box(geometry)
species=st.text_input("Species Name", placeholder="Example: Quercus sartorii")
st.write("You entered: ", species)
if species:
start_y=st.number_input("Start year", value=None, step=1, min_value=1900, max_value=2021)
st.write("You entered: ", start_y)
if start_y:
end_y=st.number_input("End year", value=None, step=1, min_value=1900, max_value=2021)
st.write("You entered: ", end_y)
st.write("You entered: ", end_y)
time.sleep(1)
if end_y:
bbox = mapbbox()
st.write("You entered: ", bbox)
time.sleep(1)
if bbox:
buffer=st.number_input("Buffer", value=None)
distance=st.number_input("Distance", value=None)
if buffer and distance:
polygon_file_path = "/Users/simonrabenmeister/Desktop/Genes_from_Space/Genes_from_Space_interface/Test files/population_polygons_switzerland.geojson"
st.write("You entered: ", polygon_file_path)
f = open(polygon_file_path)
polygons=geojson.load(f)
mapgeojson(polygons)