python-visualization / folium

Python Data. Leaflet.js Maps.
https://python-visualization.github.io/folium/
MIT License
6.93k stars 2.23k forks source link

Realtime Circles have non-changing radii. #1958

Closed yishaiSilver closed 5 months ago

yishaiSilver commented 5 months ago

Describe the bug When using Circle in the context of a realtime map, the radius never changes. If using differing IDs, the radius of each additional circle will, in fact, change. But, prior circles will still be on the map (you can see this if you change 'properties': { 'id': 0, }) to 'properties': { 'id': id, }). The position of the circle, however, will be updated without issue.

To Reproduce

FastAPI API:

from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
import threading
import uvicorn
import numpy as np

# Initialize the FastAPI app
app = FastAPI()

app.add_middleware(
    CORSMiddleware,
    allow_origins=["*"],  # Allows all origins, you can specify your domain here
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)

# Define a route to get the latest message
@app.get("/latest_message")
def get_latest_message():
    global id
    id += 1
    return {
            "id" : id,
            "longitude": -118.32790622288792 + np.random.randint(1, 20)*0.000001,
            "latitude": 33.34318994102025 + np.random.randint(1, 20)*0.000001,
            "radius": np.random.randint(1, 20)*1.1
        }

uvicorn.run(app, host="0.0.0.0", port=8000)

Folium code:

import folium
from folium.plugins import Realtime
from folium import JsCode

m = folium.Map([33.34318994102025, -118.32790622288792], zoom_start=17, min_zoom=15, max_zoom = 20)

source = folium.JsCode("""
    function(responseHandler, errorHandler) {
        var url = 'http://0.0.0.0:8000/latest_message';

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

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

Realtime(
    source,
    get_feature_id=JsCode("(f) => { return f.properties.id }"),
    point_to_layer=JsCode("(f, latlng) => { return L.circle(latlng, {radius: f.geometry.radius, fillOpacity: 0.2})}"),
    interval=2000,
).add_to(m)

m

Expected behavior In the same way that the lat and long values cause the circle to change, I expect that the radius also changes.

Environment (please complete the following information):

Possible solutions A rather dirty fix would be to add a new circle each time the map is updated and send the prior circle(s) to the some irrelevant part of the globe (off-camera). I have tested this and it works, but I suspect that this would incur serious performance issues if used for an extended period of time.

hansthen commented 5 months ago

@yishaiSilver Can you have a look at the update_feature parameter? This accepts a javascript (JsCode) function that can be used to update existing layers. Have a look at the function prototype for inspiration here: https://github.com/perliedman/leaflet-realtime/blob/master/src/L.Realtime.js.

You can define something like this:

update_feature = JsCode("""
    function (feature, oldLayer) {
        var layer = L.Realtime.prototype.options.updateFeature(feature, oldLayer);
        var type = feature.geometry && feature.geometry.type
        if(type === "Point") {
            layer.setRadius(feature.properties.radius);
        }
        return layer;
    }
""")
yishaiSilver commented 5 months ago

Yes, that looks like what I was looking for. Thank you so much.