Skip to content

Commit 4601883

Browse files
committed
rename TimeDynamicGeoJson to TimeSliderChoropleth and fix some minor stuff
1 parent 7bfabf3 commit 4601883

File tree

7 files changed

+472
-183
lines changed

7 files changed

+472
-183
lines changed

CHANGES.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
- Internal re-factor to reflect leaflet's organization (ocefpaf #725)
88
- Added `tooltip` support to `Marker`s (ocefpaf #724)
99
- Added `tooltip` support to all vector layers (ocefpaf #722)
10-
- Added TimeDynamicGeoJson plugin (halfdanrump #736)
10+
- Added `TimeSliderChoropleth` plugin (halfdanrump #736)
1111

1212
API changes
1313

examples/TilesExample.ipynb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -343,7 +343,7 @@
343343
"name": "python",
344344
"nbconvert_exporter": "python",
345345
"pygments_lexer": "ipython3",
346-
"version": "3.6.2"
346+
"version": "3.6.3"
347347
}
348348
},
349349
"nbformat": 4,

examples/TimeSliderChoropleth.ipynb

Lines changed: 403 additions & 92 deletions
Large diffs are not rendered by default.

folium/plugins/__init__.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,9 @@
2222
from folium.plugins.polyline_text_path import PolyLineTextPath
2323
from folium.plugins.scroll_zoom_toggler import ScrollZoomToggler
2424
from folium.plugins.terminator import Terminator
25+
from folium.plugins.time_slider_choropleth import TimeSliderChoropleth
2526
from folium.plugins.timestamped_geo_json import TimestampedGeoJson
2627
from folium.plugins.timestamped_wmstilelayer import TimestampedWmsTileLayers
27-
from folium.plugins.timedynamic_geo_json import TimeDynamicGeoJson
2828

2929
__all__ = [
3030
'BoatMarker',
@@ -39,7 +39,7 @@
3939
'PolyLineTextPath',
4040
'ScrollZoomToggler',
4141
'Terminator',
42+
'TimeSliderChoropleth',
4243
'TimestampedGeoJson',
4344
'TimestampedWmsTileLayers',
44-
'TimeDynamicGeoJson'
4545
]

folium/plugins/timedynamic_geo_json.py renamed to folium/plugins/time_slider_choropleth.py

Lines changed: 35 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -1,69 +1,30 @@
1-
"""
2-
module containing the class TimeDynamicGeoJson, which can be used to
3-
create choropleth maps with a time slider.
4-
"""
5-
from jinja2 import Template
6-
from branca.element import JavascriptLink, Figure
1+
import json
2+
3+
from branca.element import Figure, JavascriptLink
4+
75
from folium.features import GeoJson
86

7+
from jinja2 import Template
8+
99

10-
class TimeDynamicGeoJson(GeoJson):
11-
"""
12-
Creates a GeoJson object for plotting into a Map.
13-
14-
Parameters
15-
----------
16-
data: file, dict or str.
17-
The GeoJSON data you want to plot.
18-
* If file, then data will be read in the file and fully
19-
embedded in Leaflet's JavaScript.
20-
* If dict, then data will be converted to JSON and embedded
21-
in the JavaScript.
22-
* If str, then data will be passed to the JavaScript as-is.
23-
style_function: function, default None
24-
A function mapping a GeoJson Feature to a style dict.
25-
name : string, default None
26-
The name of the Layer, as it will appear in LayerControls
27-
overlay : bool, default False
28-
Adds the layer as an optional overlay (True) or the base layer (False).
29-
control : bool, default True
30-
Whether the Layer will be included in LayerControls
31-
smooth_factor: float, default None
32-
How much to simplify the polyline on each zoom level. More means
33-
better performance and smoother look, and less means more accurate
34-
representation. Leaflet defaults to 1.0.
35-
36-
Examples
37-
--------
38-
>>> # Providing file that shall be embedded.
39-
>>> GeoJson(open('foo.json'))
40-
>>> # Providing filename that shall not be embedded.
41-
>>> GeoJson('foo.json')
42-
>>> # Providing dict.
43-
>>> GeoJson(json.load(open('foo.json')))
44-
>>> # Providing string.
45-
>>> GeoJson(open('foo.json').read())
46-
47-
>>> # Provide a style_function that color all states green but Alabama.
48-
>>> style_function = lambda x: {'fillColor': '#0000ff' if
49-
... x['properties']['name']=='Alabama' else
50-
... '#00ff00'}
51-
>>> GeoJson(geojson, style_function=style_function)
52-
53-
"""
54-
def __init__(self, data, styledict, **kwargs):
55-
super(TimeDynamicGeoJson, self).__init__(data, **kwargs)
56-
assert isinstance(styledict, dict), 'styledict must be a dictionary'
10+
class TimeSliderChoropleth(GeoJson):
11+
def __init__(self, data, styledict, name=None, overlay=True, control=True, **kwargs):
12+
super(TimeSliderChoropleth, self).__init__(data, name=name, overlay=overlay, control=control)
13+
if not isinstance(styledict, dict):
14+
raise ValueError('styledict must be a dictionary, got {!r}'.format(styledict))
5715
for val in styledict.values():
58-
assert isinstance(val, dict), 'each item in styledict must be a dictionary'
16+
if not isinstance(val, dict):
17+
raise ValueError('Each item in styledict must be a dictionary, got {!r}'.format(val))
18+
5919

60-
self.styledict = styledict
20+
# Make set of timestamps.
21+
timestamps = set()
22+
for feature in styledict.values():
23+
timestamps.update(set(feature.keys()))
24+
timestamps = sorted(list(timestamps))
6125

62-
# make set of timestamps
63-
self.timestamps = set()
64-
for feature in self.styledict.values():
65-
self.timestamps.update(set(feature.keys()))
66-
self.timestamps = sorted(list(self.timestamps))
26+
self.timestamps = json.dumps(timestamps)
27+
self.styledict = json.dumps(styledict, sort_keys=True, indent=2)
6728

6829
self._template = Template(u"""
6930
{% macro script(this, kwargs) %}
@@ -102,7 +63,9 @@ def __init__(self, data, styledict, **kwargs):
10263
if (current_timestamp in style){
10364
fillColor = style[current_timestamp]['color'];
10465
opacity = style[current_timestamp]['opacity'];
105-
d3.selectAll('#feature-'+feature_id).attr('fill', fillColor).style('fill-opacity', opacity);
66+
d3.selectAll('#feature-'+feature_id
67+
).attr('fill', fillColor)
68+
.style('fill-opacity', opacity);
10669
}
10770
}
10871
}
@@ -155,21 +118,28 @@ def __init__(self, data, styledict, **kwargs):
155118
).addTo({{this._parent.get_name()}}
156119
);
157120
158-
{{this.get_name()}}.setStyle(function(feature) {feature.properties.style;});
121+
{{this.get_name()}}.setStyle(function(feature) {feature.properties.style;});
159122
160123
{{ this.get_name() }}.eachLayer(function (layer) {
161124
layer._path.id = 'feature-' + layer.feature.id;
162125
});
163126
164-
d3.selectAll('path').attr('stroke', 'white').attr('stroke-width', 0.8).attr('stroke-dasharray', '5,5').attr('fill-opacity', 0);
127+
d3.selectAll('path')
128+
.attr('stroke', 'white')
129+
.attr('stroke-width', 0.8)
130+
.attr('stroke-dasharray', '5,5')
131+
.attr('fill-opacity', 0);
165132
fill_map();
166133
167134
{% endmacro %}
168135
""")
169136

170137
def render(self, **kwargs):
171-
super(TimeDynamicGeoJson, self).render(**kwargs)
138+
super(TimeSliderChoropleth, self).render(**kwargs)
172139
figure = self.get_root()
173140
assert isinstance(figure, Figure), ('You cannot render this Element '
174141
'if it is not in a Figure.')
175-
figure.header.add_child(JavascriptLink('https://d3js.org/d3.v4.min.js')) # noqa
142+
figure.header.add_child(
143+
JavascriptLink('https://d3js.org/d3.v4.min.js'),
144+
name='d3v4'
145+
)

tests/plugins/test_fast_marker_cluster.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
"""
44
Test FastMarkerCluster
5-
------------------
5+
----------------------
66
"""
77

88
from __future__ import (absolute_import, division, print_function)
Lines changed: 29 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,42 @@
11
"""
2-
tests for folium.plugins.TimeDynamicGeoJson
2+
tests TimeSliderChoropleth
3+
--------------------------
4+
35
"""
6+
7+
from __future__ import (absolute_import, division, print_function)
8+
49
import json
10+
11+
from branca.colormap import linear
12+
13+
import folium
14+
from folium.plugins import TimeSliderChoropleth
15+
516
import geopandas as gpd
6-
import pandas as pd
17+
718
import numpy as np
8-
import folium
9-
from folium.plugins import TimeDynamicGeoJson
10-
from branca.colormap import linear
19+
20+
import pandas as pd
1121

1222

1323
def test_timedynamic_geo_json():
1424
"""
15-
tests folium.plugins.TimeDynamicGeoJson
25+
tests folium.plugins.TimeSliderChoropleth
1626
"""
1727
assert 'naturalearth_lowres' in gpd.datasets.available
1828
datapath = gpd.datasets.get_path('naturalearth_lowres')
1929
gdf = gpd.read_file(datapath)
2030

21-
n_periods = 10
31+
n_periods = 3
2232
dt_index = pd.date_range('2016-1-1', periods=n_periods, freq='M').strftime('%s')
2333

2434
styledata = {}
2535

2636
for country in gdf.index:
2737
pdf = pd.DataFrame(
2838
{'color': np.random.normal(size=n_periods),
29-
'opacity': np.random.normal(size=n_periods)},
39+
'opacity': np.random.normal(size=n_periods)},
3040
index=dt_index)
3141
styledata[country] = pdf.cumsum()
3242

@@ -51,23 +61,21 @@ def norm(col):
5161

5262
m = folium.Map((0, 0), tiles='Stamen Watercolor', zoom_start=2)
5363

54-
folium.plugins.TimeDynamicGeoJson(
64+
time_slider_choropleth = TimeSliderChoropleth(
5565
gdf.to_json(),
5666
styledict
57-
).add_to(m)
67+
)
68+
time_slider_choropleth.add_to(m)
5869

59-
m._repr_html_()
70+
rendered = time_slider_choropleth._template.module.script(time_slider_choropleth)
6071

72+
m._repr_html_()
6173
out = m._parent.render()
74+
assert '<script src="https://d3js.org/d3.v4.min.js"></script>' in out
6275

63-
# We verify that imports
64-
assert '<script src="https://d3js.org/d3.v4.min.js' in out # noqa
65-
66-
# We verify that data has been inserted currectly
67-
expected_timestamps = ("var timestamps = ['1454166000', '1456671600', '1459350000', "
68-
"'1461942000', '1464620400', '1467212400', '1469890800', "
69-
"'1472569200', '1475161200', '1477839600'];")
70-
#assert expected_timestamps in out
76+
# We verify that data has been inserted correctly
77+
expected_timestamps = """var timestamps = ["1454198400", "1456704000", "1459382400"];""" # noqa
78+
assert expected_timestamps.split(';')[0].strip() == rendered.split(';')[0].strip()
7179

72-
expected_styledict = json.dumps(styledict).replace('"', "'")
73-
assert expected_styledict in out
80+
expected_styledict = json.dumps(styledict, sort_keys=True, indent=2)
81+
assert expected_styledict in rendered

0 commit comments

Comments
 (0)