randyzwitch / streamlit-folium

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

st_folium not taking style attribute into account #146

Closed patrontheo closed 11 months ago

patrontheo commented 11 months ago

Hello,

I am trying to display dynamically polygons on a map (e.g. I click on a button outside the map and it overlays a polygon on the map, and if I click on another button then another polygon appears).

As explained in dynamic_map_vs_rerender.py, I use the feature_group_to_add parameter of st_folium to show the polygons, but any style parameter specified into a folium.GeoJson object are not taken into account when displayed on the map.

Here is example code:

style_parcels = {'fillColor': 'red', 'fillOpacity': 0.2} 
st.session_state['parcels'].append(folium.GeoJson(data=footprint.parcels, style_function = lambda x: style_parcels))

map = folium.Map(location=START_LOCATION, zoom_start=START_ZOOM, tiles='OpenStreetMap', max_zoom=21)
fg = folium.FeatureGroup(name="Parcels")
for parcel in st.session_state["parcels"]:
    fg.add_child(parcel)

st_folium(
      map, 
      center=st.session_state['center'],
      zoom=st.session_state["zoom"], 
      width=800, 
      height=450,
      feature_group_to_add=fg,
  )

Above, the style_parcel dict will not be taken into account and the polygon will be displayed blue. If I specify some style into a GeoJsonTooltip, it will also not be taken into account when displayed on the map.

I think the style works if I add the folium.GeoJson polygon statically to the map (with .add_to(m)), as in geojson_popup.py, but won't it break the dynamic aspect of the map ?

Is there any workaround possible to make it work ?

I am using python 3.11.6, streamlit 1.27.2, streamlit-folium 0.15, folium 0.14.

Thank you

blackary commented 11 months ago

It's a bit tricky to test out your code to help debug, because I don't know what is in footprint and style_parcels. Would you be able to simplify down to some example data (e.g. just a static pandas dataframe or something like that) and share a complete reproducible code snippet that others can test out?

In general, you don't really need to use the feature_group_to_add unless you are actively adding or removing elements from the map as the user interacts with your app. So, if you're not doing that, then just adding it directly to the map should be fine for this case. HOWEVER, this sounds like it's probably a bug with the way feature_group_to_add works (if it indeed works fine if you add it directly to the map), so if you could create a reproducible example that works if you add it to the map, but not if you use a feature group, that would be super helpful!

patrontheo commented 11 months ago

Hey @blackary, thank you for your answer. style_parcels is defined at the very beginning of the code.

I am adding and removing polygons dynamically upon user interaction with buttons outside the map, that's why I wanted to use this feature_group_to_add feature.

Here are two sample codes. The first one is using feature_group_to_add:

import streamlit as st
import geopandas as gpd
import folium
from streamlit_folium import st_folium
import shapely

START_LOCATION = [37.7934347109497, -122.399077892527]
START_ZOOM = 18

wkt = 'POLYGON ((-122.399077892527 37.7934347109497, -122.398922660838 37.7934544916178, -122.398980265018 37.7937266504805, -122.399133972495 37.7937070646238, -122.399077892527 37.7934347109497))'
polygon_ = shapely.wkt.loads(wkt)
gdf = gpd.GeoDataFrame(geometry=[polygon_]).set_crs(epsg=4326)

style_parcels = {'fillColor': 'red', 'fillOpacity': 0.2}

polygon_folium = folium.GeoJson(data=gdf, style_function = lambda x: style_parcels)

map = folium.Map(location=START_LOCATION, zoom_start=START_ZOOM, tiles='OpenStreetMap', max_zoom=21)
fg = folium.FeatureGroup(name="Parcels")
fg.add_child(polygon_folium)

st_folium(
      map,
      width=800, 
      height=450,
      feature_group_to_add=fg,
  )

The second one here is using the static map:

import streamlit as st
import geopandas as gpd
import folium
from streamlit_folium import st_folium
import shapely

START_LOCATION = [37.7934347109497, -122.399077892527]
START_ZOOM = 18

wkt = 'POLYGON ((-122.399077892527 37.7934347109497, -122.398922660838 37.7934544916178, -122.398980265018 37.7937266504805, -122.399133972495 37.7937070646238, -122.399077892527 37.7934347109497))'
polygon_ = shapely.wkt.loads(wkt)
gdf = gpd.GeoDataFrame(geometry=[polygon_]).set_crs(epsg=4326)

style_parcels = {'fillColor': 'red', 'fillOpacity': 0.2}

polygon_folium = folium.GeoJson(data=gdf, style_function = lambda x: style_parcels)

map = folium.Map(location=START_LOCATION, zoom_start=START_ZOOM, tiles='OpenStreetMap', max_zoom=21)
polygon_folium.add_to(map)

st_folium(
      map,
      width=800, 
      height=450,
  )

As you can see, with the first sample code, the polygon displayed is blue, while it is red with the second sample code (as it should be). Hope this helps

patrontheo commented 11 months ago

Hey @blackary, I merged the PR on my fork and installed streamlit-folium from my repo. However, I run into this issue:

Traceback (most recent call last):
  File "/home/patron/mambaforge/envs/app_footprinter/lib/python3.11/site-packages/streamlit/runtime/scriptrunner/script_runner.py", line 534, in _run_script
    exec(code, module.__dict__)
  File "/home/patron/AerialRiskDetection/app_prototype/app.py", line 6, in <module>
    from streamlit_folium import st_folium
  File "/home/patron/mambaforge/envs/app_footprinter/lib/python3.11/site-packages/streamlit_folium/__init__.py", line 28, in <module>
    _component_func = components.declare_component("st_folium", path=build_dir)
                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/patron/mambaforge/envs/app_footprinter/lib/python3.11/site-packages/streamlit/components/v1/components.py", line 320, in declare_component
    ComponentRegistry.instance().register_component(component)
  File "/home/patron/mambaforge/envs/app_footprinter/lib/python3.11/site-packages/streamlit/components/v1/components.py", line 360, in register_component
    raise StreamlitAPIException(f"No such component directory: '{abspath}'")
streamlit.errors.StreamlitAPIException: No such component directory: '/home/patron/mambaforge/envs/app_footprinter/lib/python3.11/site-packages/streamlit_folium/frontend/build'

I understand I need to rebuild the front-end at some point (I don't hove the folder frontend in the streamlit_folium folder). For now, I copy-pasted the folder frontend from my last installation and it works, but I guess it's not the right way to do. Could you explain how to rebuild the frontend ? Maybe by addind a bit of documentation on that on the readme ? Thanks !