initial commit
This commit is contained in:
@@ -0,0 +1,135 @@
|
||||
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)
|
||||
Reference in New Issue
Block a user