randyzwitch / streamlit-folium

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

Document `JsCode` enabled plugins for use with streamlit-folium #184

Closed hansthen closed 5 months ago

hansthen commented 5 months ago

Folium has a new Realtime plugin. (This is a bit like a GeoJson layer, but it periodically retrieves the data from an external source.) This plugin allows javascript configuration elements through the use of JsCode. More javascript integration is on the way. Using JsCode allows users to bypass the st_folium returned_objects mechanism and return data directly.

I think this is a useful addition for users as it gives more flexibility what data is returned. If you also think it is useful, I can add an example to the examples page and maybe also include it in the automated tests.

Below is a toy example. It shows the path of the International Space Station. If the user clicks on the marker, the current location of the ISS is returned.

import folium
import streamlit as st
from streamlit_folium import st_folium
from folium.plugins import Realtime

m = folium.Map()
source = folium.JsCode("""
    function(responseHandler, errorHandler) {
        var url = 'https://api.wheretheiss.at/v1/satellites/25544';

        fetch(url)
        .then((response) => {
            return response.json().then((data) => {
                var { id, timestamp, longitude, latitude } = data;

                return {
                    'type': 'FeatureCollection',
                    'features': [{
                        'type': 'Feature',
                        'geometry': {
                            'type': 'Point',
                            'coordinates': [longitude, latitude]
                        },
                        'properties': {
                            'id': id,
                            'timestamp': timestamp
                        }
                    }]
                };
            })
        })
        .then(responseHandler)
        .catch(errorHandler);
    }
""")

on_each_feature = folium.JsCode("""
    (feature, layer) => {
        layer.on("click", (event) => {
            Streamlit.setComponentValue({
                id: feature.properties.id,
                // Be careful, on_each_feature binds only once.
                // You need to extract the current location from
                // the event, not the feature.
                location: event.sourceTarget.feature.geometry
            });
        });
    }
""")

Realtime(
    source,
    on_each_feature=on_each_feature,
    interval=10000
).add_to(m)

data = st_folium(
    m,
    returned_objects=[],
    debug=False
)

st.write(data)
randyzwitch commented 5 months ago

Interesting.

On the one hand, this adds considerably more functionality, but then it also moves away from the idea of simplicity (because now we'd be suggesting that users should expect to write inline JS)

What do you think about this/where it should be located @blackary?

hansthen commented 5 months ago

I agree it increases the complexity. Perhaps we can put it under an advanced section and explicitly describe for what use cases this would be an appropriate solution?

randyzwitch commented 5 months ago

If you're willing to add that page, I say go for it. Maybe in the docs Streamlit app in the sidebar, add a ## Advanced heading, then add your page.

Docs are starting to get a little unwieldy now, might need to completely revamp them đŸ˜…

hansthen commented 5 months ago

Docs are starting to get a little unwieldy now, might need to completely revamp them đŸ˜…

I created a new PR for it. What would you do to revamp the documentation? We use streamlit-folium at work. I can collect some improvement suggestions for the documentation.

randyzwitch commented 5 months ago

Not sure what I'd do, just pointing out that we never took a real principled approach to this package. We just made it to demonstrate it could be done, and 400-something stars in, we should probably be more serious about it