randyzwitch / streamlit-folium

Streamlit Component for rendering Folium maps
https://folium.streamlit.app/
MIT License
475 stars 178 forks source link

Session state key #169

Open patrontheo opened 10 months ago

patrontheo commented 10 months ago

Not really an issue but is there a reason why you use a hash to create the key of the map ? The other widgets in streamlit just use the key provided as an argument.

It could be useful to get the returned objects before the call to st_folium(...). For this we need to know in advance what is the key of the widget.

A workaround I found is to look for a key that is 64 characters long in st.session_state..keys(), but it is not reliable and limited to one map being displayed.

An easy fix could be to return the key along with the other returned objects, and the user can save this key to a session state if he needs to use the returned objects before the call to st_folium.

PaulRosenfield commented 2 weeks ago

I would like to bump this from a Question to a Feature Request, as I believe it is actually an substantial issue and a meaningful departure from typical Streamlit widget design. My current use case is one example of why this is a potential problem.

In the app I'm building, I am using st_folium to allow my users to draw polygons over satellite imagery. Once the user draws a polygon, I need to perform calculations on that polygon during the resulting rerun before hitting the st_folium() call again. Ordinarily, Streamlit provides three methods for updating backend values from a widget interaction:

  1. Accessing the updated widget value via st.session_state["widget_key"] in a callback function, like on_click=some_callback_function();
  2. Accessing the updated widget value via st.session_state["widget_key"] in the normal code for rendering your page; or
  3. Accessing the updated widget value via its return value in the normal code for rendering your page, e.g. map_data = st_folium(...).

Thee key difference between these three options is when in the page rerun control flow they allow you to access the updated widget value. st.session_state["widget_key"] updates immediately after a user interacts with a widget, hence, methods 1 and 2 allow you can access the updated value at any point during your page rerun. But the widget return value will not update until after the control flow reaches map_data = st_folium(...) again. Any attempt to access map_data before then will give you the stale value of map_data rather than the updated one.

I don't see a particular need for callback functions, but respecting the user's choice of Session State key name would be an important feature.

randyzwitch commented 2 weeks ago

If I'm not mistaken, we make our own hash to determine whether to re-draw the map or keep the map but let the user keep interacting with it. Because of the Streamlit execution model, Folium re-runs every time the Streamlit app goes top-to-bottom, and Folium randomly generates a hash inside their code. So we need to normalize the Folium JS code before rendering it to determine whether to re-draw the app.

Is that how you remember it @blackary?

That said, I'm not against this (or most things) as an enhancement, but it might not be possible. Happy to accept a PR if someone can demonstrate a better way for it to work.

blackary commented 2 weeks ago

The reason for the random hash is actual on our end -- I did that on purpose so that if you change the inputs to the map, it automatically recreates the map. This takes advantage of the way streamlit decides whether it needs to recreate a component, and it seems to be based on whether the key has changed.

I think it would be relatively easy to do something like this:

def st_folium(..., key: None):
    hashed_key_plus_arguments = ...
    value = component(key=hashed_key_plus_arguments)

    if key is not None:
        st.sesison_state[key] = value

    return value

It might not be that easy, but I think it probably would. That would mean that the hashed 60 character long session state key would be there in addition to the user-passed key, but I think that would be fine)

iainsc commented 5 days ago

I am seconding this as a feature request. My use case is along the lines of this question: https://discuss.streamlit.io/t/show-data-points-based-on-bounding-box/61678 That thread features a hack ( first_bounds = st.session_state[session_keys[0]]['bounds'] ) to access the bounds of the map before the call to st_folium. This doesn't work reliably with multiple components on the page. Using the return value from st_folium allows access to the stale map bounds, generating the frustrating experience of displaying the markers from the place the user just moved from. I have tried unsuccessfully to implement the above suggestion by @blackary