Skip to content

Commit d9dfb85

Browse files
authored
Merge pull request #644 from acrosby/wms-timedim
Adding Leaflet.timeDimension support for WmsTileLayers
2 parents 07f8e99 + 017c48c commit d9dfb85

3 files changed

Lines changed: 175 additions & 2 deletions

File tree

folium/features.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,14 +49,18 @@ class WmsTileLayer(Layer):
4949
Adds the layer as an optional overlay (True) or the base layer (False).
5050
control : bool, default True
5151
Whether the Layer will be included in LayerControls
52+
**kwargs : additional keyword arguments
53+
Passed through to the underlying tileLayer.wms object and can be used
54+
for setting extra tileLayer.wms parameters or as extra parameters in
55+
the WMS request.
5256
5357
For more information see:
5458
http://leafletjs.com/reference.html#tilelayer-wms
5559
5660
"""
5761
def __init__(self, url, name=None, layers=None, styles=None, fmt=None,
5862
transparent=True, version='1.1.1', attr=None, overlay=True,
59-
control=True):
63+
control=True, **kwargs):
6064
super(WmsTileLayer, self).__init__(overlay=overlay, control=control, name=name) # noqa
6165
self.url = url
6266
self.attribution = attr if attr is not None else ''
@@ -66,11 +70,15 @@ def __init__(self, url, name=None, layers=None, styles=None, fmt=None,
6670
self.format = fmt if fmt else 'image/jpeg'
6771
self.transparent = transparent
6872
self.version = version
73+
self.kwargs = kwargs
6974
self._template = Template(u"""
7075
{% macro script(this, kwargs) %}
7176
var {{this.get_name()}} = L.tileLayer.wms(
7277
'{{ this.url }}',
7378
{
79+
{% for key, value in this.kwargs.items() %}
80+
{{key}}: '{{ value }}',
81+
{% endfor %}
7482
layers: '{{ this.layers }}',
7583
styles: '{{ this.styles }}',
7684
format: '{{ this.format }}',

folium/plugins/__init__.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
from .scroll_zoom_toggler import ScrollZoomToggler
1818
from .terminator import Terminator
1919
from .timestamped_geo_json import TimestampedGeoJson
20+
from .timestamped_wmstilelayer import TimestampedWmsTileLayers
2021

2122
__all__ = [
2223
'MarkerCluster',
@@ -29,5 +30,6 @@
2930
'ImageOverlay',
3031
'Fullscreen',
3132
'PolyLineTextPath',
32-
'FloatImage'
33+
'FloatImage',
34+
'TimestampedWmsTileLayer'
3335
]
Lines changed: 163 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,163 @@
1+
# -*- coding: utf-8 -*-
2+
"""
3+
TimestampedGeoJson plugin
4+
--------------
5+
6+
Add a timestamped geojson feature collection on a folium map.
7+
This is based on Leaflet.TimeDimension.
8+
9+
https://github.com/socib/Leaflet.TimeDimension
10+
11+
A geo-json is timestamped if:
12+
* it contains only features of types LineString, MultiPoint,
13+
MultiLineString and MultiPolygon.
14+
* each feature has a "times" property with the same length as the
15+
coordinates array.
16+
* each element of each "times" property is a timestamp in ms since epoch,
17+
or in ISO string. Eventually, you may have Point features with a
18+
"times" property being an array of length 1.
19+
20+
"""
21+
22+
from jinja2 import Template
23+
24+
from branca.element import Figure, JavascriptLink, CssLink
25+
from ..map import Layer
26+
from ..features import WmsTileLayer
27+
28+
29+
class TimestampedWmsTileLayers(Layer):
30+
def __init__(self, data, transition_time=200, loop=False, auto_play=False,
31+
period="P1D", time_interval=False):
32+
"""Creates a TimestampedWmsTileLayer subclass of Layer that takes a
33+
WmsTileLayer and adds time control with the Leaflet.TimeDimension
34+
plugin.
35+
36+
Parameters
37+
----------
38+
data: WmsTileLayer.
39+
The WmsTileLayer that you want to add time support to.
40+
41+
* Should be created like a typical WmsTileLayer and added to the map
42+
before being passed to this class.
43+
44+
examples :
45+
# Create WmsTileLayer
46+
w1 = features.WmsTileLayer(
47+
"http://this.wms.server/ncWMS/wms",
48+
name="Test WMS Data",
49+
styles="",
50+
format="image/png",
51+
transparent=True,
52+
layers="test_data",
53+
COLORSCALERANGE="0,10",
54+
)
55+
# Add to map
56+
w1.add_to(m)
57+
58+
# Create WmsTileLayer
59+
w2 = features.WmsTileLayer(
60+
"http://this.wms.server/ncWMS/wms",
61+
name="Test WMS Data",
62+
styles="",
63+
format="image/png",
64+
transparent=True,
65+
layers="test_data_2",
66+
COLORSCALERANGE="0,5",
67+
)
68+
# Add to map
69+
w2.add_to(m)
70+
71+
# Add WmsTileLayers to time control
72+
time = plugins.TimestampedWmsTileLayers([w1,w2])
73+
time.add_to(m)
74+
75+
transition_time : int, default 200.
76+
The duration in ms of a transition from between timestamps.
77+
loop : bool, default False
78+
Whether the animation shall loop, default is to reduce load on WMS
79+
services.
80+
auto_play : bool, default False
81+
Whether the animation shall start automatically at startup, default
82+
is to reduce load on WMS services.
83+
period : str, default "P1D"
84+
Used to construct the array of available times starting
85+
from the first available time. Format: ISO8601 Duration
86+
ex: "P1M" -> 1/month
87+
"P1D" -> 1/day
88+
"PT1H" -> 1/hour
89+
"PT1M" -> 1/minute
90+
Note: this seems to be overridden by the WMS Tile Layer
91+
GetCapabilities.
92+
"""
93+
super(TimestampedWmsTileLayers, self).__init__(overlay=True,
94+
control=False,
95+
name="timestampedwms")
96+
self._name = 'TimestampedWmsTileLayers'
97+
98+
self.transition_time = int(transition_time)
99+
self.loop = bool(loop)
100+
self.auto_play = bool(auto_play)
101+
self.period = period
102+
self.time_interval = time_interval
103+
if isinstance(data, WmsTileLayer):
104+
self.layers = [data]
105+
else:
106+
self.layers = data # Assume iterable
107+
self._template = Template("""
108+
{% macro script(this, kwargs) %}
109+
{{this._parent.get_name()}}.timeDimension = L.timeDimension({
110+
period:"{{this.period}}",
111+
{% if this.time_interval %}
112+
timeInterval: "{{ this.time_interval }}",
113+
{% endif %}
114+
});
115+
{{this._parent.get_name()}}.timeDimensionControl = L.control.timeDimension({
116+
position: 'bottomleft',
117+
autoPlay: {{'true' if this.auto_play else 'false'}},
118+
playerOptions: {
119+
transitionTime: {{this.transition_time}},
120+
loop: {{'true' if this.loop else 'false'}}}
121+
});
122+
{{this._parent.get_name()}}.addControl({{this._parent.get_name()}}.timeDimensionControl);
123+
124+
{% for layer in this.layers %}
125+
var {{ layer.get_name() }} = L.timeDimension.layer.wms({{ layer.get_name() }},
126+
{updateTimeDimension: false,
127+
wmsVersion: '{{ layer.version }}',
128+
}
129+
).addTo({{this._parent.get_name()}});
130+
{% endfor %}
131+
{% endmacro %}
132+
""")
133+
134+
def render(self, **kwargs):
135+
super(TimestampedWmsTileLayers, self).render()
136+
137+
figure = self.get_root()
138+
assert isinstance(figure, Figure), ("You cannot render this Element "
139+
"if it's not in a Figure.")
140+
141+
figure.header.add_child(
142+
JavascriptLink("https://cdnjs.cloudflare.com/ajax/libs/jquery/2.0.0/jquery.min.js"), # noqa
143+
name='jquery2.0.0')
144+
145+
figure.header.add_child(
146+
JavascriptLink("https://cdnjs.cloudflare.com/ajax/libs/jqueryui/1.10.2/jquery-ui.min.js"), # noqa
147+
name='jqueryui1.10.2')
148+
149+
figure.header.add_child(
150+
JavascriptLink("https://rawgit.com/nezasa/iso8601-js-period/master/iso8601.min.js"), # noqa
151+
name='iso8601')
152+
153+
figure.header.add_child(
154+
JavascriptLink("https://rawgit.com/socib/Leaflet.TimeDimension/master/dist/leaflet.timedimension.min.js"), # noqa
155+
name='leaflet.timedimension')
156+
157+
figure.header.add_child(
158+
CssLink("https://cdnjs.cloudflare.com/ajax/libs/highlight.js/8.4/styles/default.min.css"), # noqa
159+
name='highlight.js_css')
160+
161+
figure.header.add_child(
162+
CssLink("http://apps.socib.es/Leaflet.TimeDimension/dist/leaflet.timedimension.control.min.css"), # noqa
163+
name='leaflet.timedimension_css')

0 commit comments

Comments
 (0)