Files
2024-09-29 01:46:07 -04:00

136 lines
4.9 KiB
Python

from typing import Optional, Union
from branca.element import MacroElement
from jinja2 import Template
from folium.elements import JSCSSMixin
from folium.map import Layer
from folium.utilities import JsCode, camelize, parse_options
class Realtime(JSCSSMixin, MacroElement):
"""Put realtime data on a Leaflet map: live tracking GPS units,
sensor data or just about anything.
Based on: https://github.com/perliedman/leaflet-realtime
Parameters
----------
source: str, dict, JsCode
The source can be one of:
* a string with the URL to get data from
* a dict that is passed to javascript's `fetch` function
for fetching the data
* a `folium.JsCode` object in case you need more freedom.
start: bool, default True
Should automatic updates be enabled when layer is added
on the map and stopped when layer is removed from the map
interval: int, default 60000
Automatic update interval, in milliseconds
get_feature_id: str or JsCode, optional
A JS function with a geojson `feature` as parameter
default returns `feature.properties.id`
Function to get an identifier to uniquely identify a feature over time
update_feature: str or JsCode, optional
A JS function with a geojson `feature` as parameter
Used to update an existing feature's layer;
by default, points (markers) are updated, other layers are discarded
and replaced with a new, updated layer.
Allows to create more complex transitions,
for example, when a feature is updated
remove_missing: bool, default False
Should missing features between updates been automatically
removed from the layer
container: Layer, default GeoJson
The container will typically be a `FeatureGroup`, `MarkerCluster` or
`GeoJson`, but it can be anything that generates a javascript
L.LayerGroup object, i.e. something that has the methods
`addLayer` and `removeLayer`.
Other keyword arguments are passed to the GeoJson layer, so you can pass
`style`, `point_to_layer` and/or `on_each_feature`. Make sure to wrap
Javascript functions in the JsCode class.
Examples
--------
>>> from folium import JsCode
>>> m = folium.Map(location=[40.73, -73.94], zoom_start=12)
>>> rt = Realtime(
... "https://raw.githubusercontent.com/python-visualization/folium-example-data/main/subway_stations.geojson",
... get_feature_id=JsCode("(f) => { return f.properties.objectid; }"),
... point_to_layer=JsCode(
... "(f, latlng) => { return L.circleMarker(latlng, {radius: 8, fillOpacity: 0.2})}"
... ),
... interval=10000,
... )
>>> rt.add_to(m)
"""
_template = Template(
"""
{% macro script(this, kwargs) %}
var {{ this.get_name() }}_options = {{ this.options|tojson }};
{% for key, value in this.functions.items() %}
{{ this.get_name() }}_options["{{key}}"] = {{ value }};
{% endfor %}
{% if this.container -%}
{{ this.get_name() }}_options["container"]
= {{ this.container.get_name() }};
{% endif -%}
var {{ this.get_name() }} = L.realtime(
{% if this.src is string or this.src is mapping -%}
{{ this.src|tojson }},
{% else -%}
{{ this.src.js_code }},
{% endif -%}
{{ this.get_name() }}_options
);
{{ this._parent.get_name() }}.addLayer(
{{ this.get_name() }}._container);
{% endmacro %}
"""
)
default_js = [
(
"Leaflet_Realtime_js",
"https://cdnjs.cloudflare.com/ajax/libs/leaflet-realtime/2.2.0/leaflet-realtime.js",
)
]
def __init__(
self,
source: Union[str, dict, JsCode],
start: bool = True,
interval: int = 60000,
get_feature_id: Union[JsCode, str, None] = None,
update_feature: Union[JsCode, str, None] = None,
remove_missing: bool = False,
container: Optional[Layer] = None,
**kwargs
):
super().__init__()
self._name = "Realtime"
self.src = source
self.container = container
kwargs["start"] = start
kwargs["interval"] = interval
if get_feature_id is not None:
kwargs["get_feature_id"] = JsCode(get_feature_id)
if update_feature is not None:
kwargs["update_feature"] = JsCode(update_feature)
kwargs["remove_missing"] = remove_missing
# extract JsCode objects
self.functions = {}
for key, value in list(kwargs.items()):
if isinstance(value, JsCode):
self.functions[camelize(key)] = value.js_code
kwargs.pop(key)
self.options = parse_options(**kwargs)