python-visualization / folium

Python Data. Leaflet.js Maps.

Home Page:https://python-visualization.github.io/folium/

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Realtime Circles have non-changing radii.

yishaiSilver opened this issue · comments

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):

  • Jupyter Notebook
  • Python version: 3.10.12
  • folium version: 0.16.0
  • branca version: 0.7.2

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.

@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;
    }
""")

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