streamlit-folium icon indicating copy to clipboard operation
streamlit-folium copied to clipboard

How can I mange the features of a drawn layer in the session state?

Open iboates opened this issue 8 months ago • 2 comments
trafficstars

I'm trying to make it so that when I draw multiple polygons with the Draw plugin, I can store them in an array of features in the session state. I want to make it so that I can add features to it, delete them, etc. so that I can also attach a small form to each one which can be submitted to a backend.

However, whenever I add a feature, the only thing I seem to get is the last active drawing and the "all drawings" list. There is no unique identifier per feature.

Clicking on a drawn feature also seems to only return the last active drawing.

Deleting features will also just return a dict of enumerated feature objects, but the count will start over from 0 and not exclude the one that was deleted.

Is it even possible to do this? I just want to be able to manage a group of polygons and have each of them get a new identifier upon creation that I can then refer back to.

This my my code so far:

import streamlit as st
from streamlit_folium import st_folium
import folium
from folium.plugins import Draw
import json

st.set_page_config(layout="wide")
m = folium.Map(location=[0, 0], zoom_start=1, tiles='OpenStreetMap')
# folium.LayerControl().add_to(m)

my_feature_group = folium.FeatureGroup(name="my_feature_group")
my_feature_group.add_to(m)

my_draw_control = Draw(edit_options={ "featureGroup": "my_feature_group", })
my_draw_control.add_to(m)

c1, c2 = st.columns(2)
with c1:
	output = st_folium(m, width=700, height=500, returned_objects=["last_active_drawing", "all_drawings"])
	st.session_state["feature_storage"] = output["all_drawings"]
	st.session_state["last_active_drawing"] = output["last_active_drawing"]

with c2:
	st.write("feature_storage:")
	if st.session_state["feature_storage"]:
		st.write(st.session_state["feature_storage"])
	st.write("last_active_drawing:")
	if st.session_state["last_active_drawing"]:
		st.write(st.session_state["last_active_drawing"])

iboates avatar Feb 27 '25 14:02 iboates

I don't know the answer off-hand, but it's an interesting dilemma...my gut reaction is that streamlit-folium doesn't have a good way to add labels (since you're talking about a Folium plugin), but at the same time, it feels like this should be programmatically possible.

I wonder if there is a side solution where you use something a collapsible window to display the entire list of shapes passed back by all_drawings, use Shapely or something to give a small reference image, then use that table to add labels to the drawings.

randyzwitch avatar Mar 03 '25 19:03 randyzwitch

In 2024 we implemented javascript event handlers on Folium maps. (I am one of the contributing developers of Folium). One direction you could explore is to intercept the "draw:created" event and add a unique id property to the feature. A short example:

import folium
import streamlit as st
from folium.plugins import Draw

from streamlit_folium import st_folium

m = folium.Map(location=[39.949610, -75.150282], zoom_start=5)
Draw(export=False).add_to(m)
on_create = folium.JsCode("""
    (e) => {
        // Generate an id for the feature
        var feature = (e.layer.feature = e.layer.feature || {})
        feature.type = "Feature"
        feature.properties = feature.properties || {}
        feature.properties["id"] = Date.now();
    }
""")

handlers = {
    "draw:created": on_create,
}

m.on(**handlers)

c1, c2 = st.columns(2)
with c1:
    result = st_folium(m, width=700, height=500)

with c2:
    st.write(result)

hansthen avatar Apr 12 '25 06:04 hansthen

I don't have the context to validate the answer, but I trust @hansthen is correct above. If not, I'll re-open this issue

randyzwitch avatar Aug 01 '25 12:08 randyzwitch