Skip to content

Commit 7762bf1

Browse files
authored
Merge pull request #585 from JamesGardiner/fast-marker-cluster
Add Fast marker cluster to plugins
2 parents dc34582 + 015c852 commit 7762bf1

File tree

5 files changed

+255
-0
lines changed

5 files changed

+255
-0
lines changed

CHANGES.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,12 @@
33
- Experimental support for a static png output (ocefpaf #634)
44
- Added support for subdomains options in TileLayer (damselem #623)
55
- Updated to leaflet 1.1.0 (ocefpaf #639)
6+
- Added `FastMarkerCluster` (James Gardiner #585 (proposed by @ruoyu0088))
67

78
0.3.0
89
~~~~~
910

11+
- Added `FastMarkerCluster` (James Gardiner #585 (proposed by @ruoyu0088))
1012
- Added style option to 'Timestamped geojson' plugin (soymsk #627)
1113
- Switched to `leaflet 1.0.1` (juoceano #531 and ocefpaf #535)
1214
- Added `continuous_world`, `world_copy_jump`, and `no_wrap` options (ocefpaf #508)

examples/FastMarkerCluster.ipynb

Lines changed: 119 additions & 0 deletions
Large diffs are not rendered by default.

folium/plugins/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
"""
88

99
from .boat_marker import BoatMarker
10+
from .fast_marker_cluster import FastMarkerCluster
1011
from .float_image import FloatImage
1112
from .fullscreen import Fullscreen
1213
from .heat_map import HeatMap
@@ -19,6 +20,7 @@
1920

2021
__all__ = [
2122
'MarkerCluster',
23+
'FastMarkerCluster',
2224
'ScrollZoomToggler',
2325
'Terminator',
2426
'BoatMarker',

folium/plugins/fast_marker_cluster.py

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
# -*- coding: utf-8 -*-
2+
"""
3+
Marker Cluster plugin
4+
---------------------
5+
6+
Creates a MarkerCluster plugin to add on a folium map.
7+
"""
8+
9+
from jinja2 import Template
10+
11+
from folium.plugins.marker_cluster import MarkerCluster
12+
13+
14+
class FastMarkerCluster(MarkerCluster):
15+
"""Add marker clusters to a map using in-browser rendering"""
16+
def __init__(self, data, callback=None):
17+
"""Add marker clusters to a map using in-browser rendering.
18+
Using FastMarkerCluster it is possible to render 000's of
19+
points far quicker than the MarkerCluster class.
20+
21+
Be aware that the FastMarkerCluster class passes an empty
22+
list to the parent class' __init__ method during initialisation.
23+
This means that the add_child method is never called, and
24+
no reference to any marker data are retained. Methods such
25+
as get_bounds() are therefore not available when using it.
26+
27+
Parameters
28+
----------
29+
data: list
30+
List of list of shape [[], []]. Data points should be of
31+
the form [[lat, lng]].
32+
33+
callback: string, default None
34+
A string representation of a valid Javascript function
35+
that will be passed a lat, lon coordinate pair. See the
36+
FasterMarkerCluster for an example of a custom callback.
37+
38+
"""
39+
super(FastMarkerCluster, self).__init__([])
40+
self._name = 'FastMarkerCluster'
41+
self._data = data
42+
43+
if callback is None:
44+
self._callback = ('var callback;\n' +
45+
'callback = function (row) {\n' +
46+
'\tvar icon, marker;\n' +
47+
'\t// Returns a L.marker object\n' +
48+
'\ticon = L.AwesomeMarkers.icon();\n' +
49+
'\tmarker = L.marker(new L.LatLng(row[0], ' +
50+
'row[1]));\n' +
51+
'\tmarker.setIcon(icon);\n' +
52+
'\treturn marker;\n' +
53+
'};')
54+
else:
55+
self._callback = "var callback = {};".format(callback)
56+
57+
self._template = Template(u"""
58+
{% macro script(this, kwargs) %}
59+
{{this._callback}}
60+
61+
(function(){
62+
var data = {{this._data}};
63+
var map = {{this._parent.get_name()}};
64+
var cluster = L.markerClusterGroup();
65+
66+
for (var i = 0; i < data.length; i++) {
67+
var row = data[i];
68+
var marker = callback(row);
69+
marker.addTo(cluster);
70+
}
71+
72+
cluster.addTo(map);
73+
})();
74+
{% endmacro %}""")
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
# -*- coding: utf-8 -*-
2+
"""
3+
Test FastMarkerCluster
4+
------------------
5+
"""
6+
7+
from jinja2 import Template
8+
import numpy as np
9+
10+
import folium
11+
from folium import plugins
12+
13+
14+
def test_fast_marker_cluster():
15+
n = 100
16+
np.random.seed(seed=26082009)
17+
data = np.array([
18+
np.random.uniform(low=35, high=60, size=n), # Random latitudes.
19+
np.random.uniform(low=-12, high=30, size=n), # Random longitudes.
20+
range(n), # Popups.
21+
]).tolist()
22+
m = folium.Map([45., 3.], zoom_start=4)
23+
mc = plugins.FastMarkerCluster(data, callback=None)
24+
m.add_child(mc)
25+
m._repr_html_()
26+
27+
out = m._parent.render()
28+
29+
# We verify that imports
30+
assert ('<script src="https://cdnjs.cloudflare.com/ajax/libs/leaflet.'
31+
'markercluster/1.0.0/leaflet.markercluster.js"></script>') in out
32+
assert ('<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/'
33+
'libs/leaflet.markercluster/1.0.0/MarkerCluster.css" />') in out
34+
assert ('<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/'
35+
'libs/leaflet.markercluster/1.0.0/MarkerCluster.Default.css" />'
36+
) in out
37+
38+
# Verify the script part is okay.
39+
tmpl = Template("""
40+
{% macro script(this, kwargs) %}
41+
(function() {
42+
var data = {{this._data}};
43+
var map = {{this._parent.get_name()}};
44+
var cluster = L.markerClusterGroup();
45+
{{this._callback}}
46+
47+
for (var i = 0; i < data.length; i++) {
48+
var row = data[i];
49+
var marker = callback(row, popup='names');
50+
marker.addTo(cluster);
51+
}
52+
53+
cluster.addTo(map);
54+
})();
55+
{% endmacro %}
56+
""")
57+
58+
assert ''.join(tmpl.render(this=mc).split()) in ''.join(out.split())

0 commit comments

Comments
 (0)