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

Seemingly infinite drawing after dropping pin with geocoder plugin

Open xnolandx opened this issue 8 months ago • 3 comments
trafficstars

Apologies if this issue has already been raised and addressed. I looked at the closed issues and didn't see this so I wanted to bring up a bug that I am currently facing. Additionally, this is the first issue I've raised on GitHub so if I leave out any pertinent information let me know and I'll include it.

I am simply trying to enable the dropping of a pin with the Geocoder plugin and then draw a rectangle with the Draw plugin. For the sake of troubleshooting I tried other shapes and the issue seems to exist with those as well. I also tried a handful of different Geocoder providers to no avail.

dependencies:

folium==0.19.5
streamlit_folium==0.24.0
streamlit==1.41.1

mvp to reproduce:

import folium 
from streamlit_folium import st_folium
from folium.plugins import Draw, Geocoder

def search_area():
    m = folium.Map()

    Draw().add_to(m)
    Geocoder().add_to(m)
    st_folium(m, 
              height=350, 
              use_container_width=True)

steps to reproduce:

  1. use the geocoder to drop a pin at the following coordinates: 43.089774, -76.209267
  2. select the rectangle in the draw tool bar
  3. try to draw a rectangle

On mouse release to end the drawing, the draw tool seems to be getting stuck in a loop where the mouse release triggers the beginning of a new drawing.

xnolandx avatar Mar 03 '25 02:03 xnolandx

Have you tried to do this without including this code inside a function?


def search_area():
    m = folium.Map()

    Draw().add_to(m)
    Geocoder().add_to(m)
    st_folium(m, 
              height=350, 
              use_container_width=True)

Because of the Streamlit execution model, I feel like this re-draws the map every time. Moving the map code outside of a function to the top-level would allow you to interact with the map and keep the various objects.

randyzwitch avatar Mar 03 '25 19:03 randyzwitch

Hey Randy, I appreciate your response. I tried your suggestion and the described behavior is still happening.

The updated code (in a brand new streamlit app):

`import folium from streamlit_folium import st_folium from folium.plugins import Draw, Geocoder

m = folium.Map()

Draw().add_to(m) Geocoder().add_to(m) st_folium(m, height=350, use_container_width=True)`

screen recording of unexpected behavior: https://github.com/user-attachments/assets/cfd39d0c-262d-4728-8a12-359d421cb23e

xnolandx avatar Mar 04 '25 02:03 xnolandx

@randyzwitch I wanted to provide some more context because after re-reading your reply, I feel like I might have not made it clear what the issue is.

So if you add either the Draw plugin OR the Geocoder plugin, they operate normally as you would expect. If you add both and DON'T drop a pin using the Geocoder, the Draw tool operates normally. The buggy interaction happens specifically AFTER a pin has been dropped with the Geocoder. I am not familiar enough with Leaflet or Folium but I assume there is some event handler that is not being processed correctly, likely involving the Geocoder.

The behavior can be seen in my previously linked video.

Thanks for your time, I'm sure you and the team are very busy.

xnolandx avatar Mar 06 '25 14:03 xnolandx

I can reproduce the issue. When drawing a figure, the browser console shows an error: "Uncaught TypeError: t.sourceTarget.getPopup is not a function".

hansthen avatar Aug 01 '25 13:08 hansthen

I have not found the cause of the issue yet. However, as a workaround, you may consider using the folium.Geoman plugin instead of the folium.Draw plugin. Geoman is a newer, better supported version of the Draw plugin and it also supports things like shapes with holes. The big disadvantage is that it does not integrate as nicely with streamlit-folium out of the box. You need to write your own event handlers. You can find example code below. I was not very thorough matching events to the event handler, so you may have to tweak those a bit.

with st.echo(code_location="below"):
    left, right = st.columns([4,1])
    with left:
        m = folium.Map(location=[39.949610, -75.150282], zoom_start=5)
        handler = JsCode("""
            (e) => {
                var map = %(map)s;
                var layers = L.PM.Utils.findLayers(map);
                var jsons = []
                layers.forEach((layer) => {
                    var json = layer.toGeoJSON();
                    if(layer.options.radius) {
                        json.properties.radius = layer.options.radius;
                    }
                    jsons.push(json);
                });
                var geojson = {
                    type: 'FeatureCollection',
                    features: jsons,
                };
                Streamlit.setComponentValue(geojson);
            }
        """ % dict(map=m.get_name()))


        GeoMan(
            on={
                "pm:edit": handler,
                "pm:rotateend": handler,
            }
        ).add_to(m)
        Geocoder().add_to(m)

        event_handlers = {
            "pm:create": handler,
            "pm:remove": handler,
        }

        m.on(**event_handlers)

        with st.expander("generated html"):
            st.code(m.get_root().render())

        result = st_folium(
            m,
            width=1200,
            height=500,
            returned_objects=[],
        )
    with right:
        st.write(result)

hansthen avatar Aug 01 '25 13:08 hansthen