Skip to content

Commit f3df0dd

Browse files
committed
Merge pull request #224 from BibMartin/issue213
Create HeatMap as suggested in #213
2 parents 57f2e37 + 79ff0ce commit f3df0dd

File tree

3 files changed

+87
-0
lines changed

3 files changed

+87
-0
lines changed

folium/plugins/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,3 +10,4 @@
1010
from .terminator import Terminator
1111
from .boat_marker import BoatMarker
1212
from .timestamped_geo_json import TimestampedGeoJson
13+
from .heat_map import HeatMap

folium/plugins/heat_map.py

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
# -*- coding: utf-8 -*-
2+
"""
3+
Heat map
4+
--------
5+
6+
Create a HeatMap layer
7+
"""
8+
import json
9+
from jinja2 import Template
10+
11+
from folium.element import JavascriptLink, Figure
12+
from folium.map import TileLayer
13+
14+
class HeatMap(TileLayer):
15+
def __init__(self, data, name=None,
16+
min_opacity = .5, max_zoom=18, max_val=1.,
17+
radius=25, blur=15, gradient=None,
18+
overlay = True):
19+
"""Create a Heatmap layer
20+
21+
Parameters
22+
----------
23+
data : list of points of the form [lat, lng] or [lat, lng, weight]
24+
The points you want to plot.
25+
You can also provide a numpy.array of shape (n,2) or (n,3).
26+
name : str
27+
The name of the layer that will be created.
28+
min_opacity : default 1.
29+
The minimum opacity the heat will start at.
30+
max_zoom : default 18
31+
Zoom level where the points reach maximum intensity (as intensity scales with zoom),
32+
equals maxZoom of the map by default
33+
max_val : float, default 1.
34+
Maximum point intensity
35+
radius : int, default 25
36+
Radius of each "point" of the heatmap
37+
blur : int, default 15
38+
Amount of blur
39+
gradient : dict, default None
40+
Color gradient config. e.g. {0.4: 'blue', 0.65: 'lime', 1: 'red'}
41+
"""
42+
super(TileLayer, self).__init__()
43+
self._name = 'HeatMap'
44+
self.tile_name = name if name is not None else self.get_name()
45+
46+
self.data = [[x for x in line] for line in data]
47+
self.min_opacity = min_opacity
48+
self.max_zoom = max_zoom
49+
self.max_val = max_val
50+
self.radius = radius
51+
self.blur = blur
52+
self.gradient = json.dumps(gradient, sort_keys=True) if gradient is not None else "null"
53+
self.overlay = overlay
54+
55+
self._template = Template(u"""
56+
{% macro script(this, kwargs) %}
57+
var {{this.get_name()}} = L.heatLayer(
58+
{{this.data}},
59+
{
60+
minOpacity: {{this.min_opacity}},
61+
maxZoom: {{this.max_zoom}},
62+
max: {{this.max_val}},
63+
radius: {{this.radius}},
64+
blur: {{this.blur}},
65+
gradient: {{this.gradient}}
66+
})
67+
.addTo({{this._parent.get_name()}});
68+
{% endmacro %}
69+
""")
70+
71+
def render(self, **kwargs):
72+
super(TileLayer, self).render()
73+
74+
figure = self.get_root()
75+
assert isinstance(figure,Figure), ("You cannot render this Element "
76+
"if it's not in a Figure.")
77+
78+
figure.header.add_children(\
79+
JavascriptLink("https://leaflet.github.io/Leaflet.heat/dist/leaflet-heat.js"),
80+
name='leaflet-heat.js')

tests/test_plugins.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,3 +134,9 @@ def test_timestamped_geo_json(self):
134134
mape = folium.Map([47, 3], zoom_start=1)
135135
mape.add_children(plugins.TimestampedGeoJson(data))
136136
mape._repr_html_()
137+
138+
def test_heat_map(self):
139+
data = (np.random.normal(size=(100,2))*np.array([[1,1]])+np.array([[48,5]])).tolist()
140+
mapa = folium.Map([48., 5.], tiles='stamentoner', zoom_start=6)
141+
mapa.add_children(plugins.HeatMap(data))
142+
mapa._repr_html_()

0 commit comments

Comments
 (0)