Skip to content

Merge branch 'plugins' #160

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
Aug 5, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6,037 changes: 6,037 additions & 0 deletions examples/plugins_examples.ipynb

Large diffs are not rendered by default.

13 changes: 12 additions & 1 deletion folium/folium.py
Original file line number Diff line number Diff line change
Expand Up @@ -657,6 +657,17 @@ def fit_bounds(self, bounds, padding_top_left=None,

self.template_vars.update({'fit_bounds': fit_bounds_str})

def add_plugin(self, plugin):
"""Adds a plugin to the map.

Parameters
----------
plugin: folium.plugins object
A plugin to be added to the map. It has to implement the methods
`render_html`, `render_css` and `render_js`.
"""
plugin.add_to_map(self)

def _auto_bounds(self):
if 'fit_bounds' in self.template_vars:
return
Expand Down Expand Up @@ -1051,7 +1062,7 @@ def _build_map(self, html_templ=None, templ_type='string'):
if templ_type == 'string':
html_templ = self.env.from_string(html_templ)

self.HTML = html_templ.render(self.template_vars)
self.HTML = html_templ.render(self.template_vars, plugins=self.plugins)

def create_map(self, path='map.html', plugin_data_out=True, template=None):
"""Write Map output to HTML and data output to JSON if available.
Expand Down
13 changes: 13 additions & 0 deletions folium/plugins/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +1,14 @@
# -*- coding: utf-8 -*-
"""
Folium plugins
--------------

Add different objetcs/effects on a folium map.
"""
from .marker_cluster import MarkerCluster
from .scroll_zoom_toggler import ScrollZoomToggler
from .terminator import Terminator
from .boat_marker import BoatMarker
from .layer import Layer, LayerControl
from .geo_json import GeoJson
from .timestamped_geo_json import TimestampedGeoJson
61 changes: 61 additions & 0 deletions folium/plugins/boat_marker.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
# -*- coding: utf-8 -*-
"""
Boat marker
-----------

Creates a marker shaped like a boat. Optionally you can append a wind direction.
"""
import json

from .plugin import Plugin

class BoatMarker(Plugin):
"""Adds a BoatMarker layer on the map."""
def __init__(self, position=None, heading=0, wind_heading=None, wind_speed=0, **kwargs):
"""Creates a BoatMarker plugin to append into a map with
Map.add_plugin.

Parameters
----------
position: tuple of length 2, default None
The latitude and longitude of the marker.
If None, then the middle of the map is used.

heading: int, default 0
Heading of the boat to an angle value between 0 and 360 degrees

wind_heading: int, default None
Heading of the wind to an angle value between 0 and 360 degrees
If None, then no wind is represented.

wind_speed: int, default 0
Speed of the wind in knots.
"""
super(BoatMarker, self).__init__()
self.plugin_name = 'BoatMarker'
self.position = None if position is None else tuple(position)
self.heading = heading
self.wind_heading = wind_heading
self.wind_speed = wind_speed
self.kwargs = kwargs.copy()

def render_header(self, nb):
"""Generates the HTML part of the plugin."""
return """
<script src="https://thomasbrueggemann.github.io/leaflet.boatmarker/js/leaflet.boatmarker.min.js"></script>
""" if nb==0 else ""

def render_js(self, nb):
"""Generates the Javascript part of the plugin."""
kwargs_str = "{%s}" % ",".join(["%s : %s" % (key,json.dumps(val)) for (key,val) in self.kwargs.items()])
position_str = "map.getCenter()" if self.position is None else "[%.12f,%.12f]"%self.position
out = 'var boatMarker_%s = L.boatMarker(%s, %s).addTo(map);' % (nb,position_str,kwargs_str)

if self.wind_heading is None:
out += "boatMarker_%s.setHeading(%s);" % (nb,int(self.heading))
else:
out += "boatMarker_%s.setHeadingWind(%s, %s, %s);"%(nb,int(self.heading),
int(self.wind_speed),
int(self.wind_heading),
)
return out
50 changes: 50 additions & 0 deletions folium/plugins/geo_json.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# -*- coding: utf-8 -*-
"""
GeoJson plugin
--------------

Add a geojson feature collection on a folium map.
"""
import json

from .plugin import Plugin

class GeoJson(Plugin):
"""Adds a GeoJson layer on the map."""
def __init__(self, data):
"""Creates a GeoJson plugin to append into a map with
Map.add_plugin.

Parameters
----------
data: file, dict or str.
The geo-json data you want to plot.
If file, then data will be read in the file and fully embeded in Leaflet's javascript.
If dict, then data will be converted to json and embeded in the javascript.
If str, then data will be passed to the javascript as-is.

examples :
# providing file
GeoJson(open('foo.json'))

# providing dict
GeoJson(json.load(open('foo.json')))

# providing string
GeoJson(open('foo.json').read())
"""
super(GeoJson, self).__init__()
self.plugin_name = 'GeoJson'
if 'read' in dir(data):
self.data = data.read()
elif type(data) is dict:
self.data = json.dumps(data)
else:
self.data = data

def render_js(self, nb):
"""Generates the Javascript part of the plugin."""
out = """
var geojson_{nb} = L.geoJson({data}).addTo(map);
""".format(nb=nb, data = self.data)
return out
77 changes: 77 additions & 0 deletions folium/plugins/layer.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
# -*- coding: utf-8 -*-
"""
Layer plugin
------------

Add layers and layer control to the map.
"""
from .plugin import Plugin

class Layer(Plugin):
"""Adds a layer to the map."""
def __init__(self, url=None, layer_name = None, min_zoom=1, max_zoom=18, attribution=''):
"""Crates a layer object to be added on a folium map.

Parameters
----------
url : str
The url of the layer service, in the classical leaflet form.
example: url='//otile1.mqcdn.com/tiles/1.0.0/osm/{z}/{x}/{y}.png'
layer_name : str
Tha name of the layer that will be displayed in the layer control.
If None, a random hexadecimal string will be created.
min_zoom : int, default 1
The minimal zoom allowed for this layer
max_zoom : int, default 18
The maximal zoom allowed for this layer
attribution : str, default ''
Tha atribution string for the layer.
"""
super(Layer, self).__init__()
self.plugin_name = 'Layer'
self.tile_url = url
self.attribution = attribution
self.min_zoom = min_zoom
self.max_zoom = max_zoom
self.object_id = self.object_name
if layer_name is not None:
self.object_name = layer_name

def render_js(self, nb):
"""Generates the JS part of the plugin."""
return """
var layer_"""+self.object_id+""" = L.tileLayer('"""+self.tile_url+"""', {
maxZoom: """+str(self.max_zoom)+""",
minZoom: """+str(self.min_zoom)+""",
attribution: '"""+str(self.attribution)+"""'
});
layer_"""+self.object_id+""".addTo(map);
"""

class LayerControl(Plugin):
"""Adds a layer control to the map."""
def __init__(self, base_layer_name="Base Layer"):
"""Creates a LayerControl object to be added on a folium map.

Parameters
----------
base_layer_name : str, default "Base Layer"
The name of the base layer that you want to see on the control.
"""
super(LayerControl, self).__init__()
self.plugin_name = 'LayerControl'
self.base_layer_name = base_layer_name

def render_js(self, nb):
"""Generates the JS part of the plugin."""
return """
var baseLayer = {
"%s": base_tile,"""% self.base_layer_name+\
",".join(['"%s" : layer_%s ' % (x.object_name,x.object_id) for x in self.map.plugins['Layer']])+\
"""};

L.control.layers(baseLayer, layer_list).addTo(map);
"""



52 changes: 52 additions & 0 deletions folium/plugins/marker_cluster.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
# -*- coding: utf-8 -*-
"""
Marker Cluster plugin
---------------------

Creates a MarkerCluster plugin to add on a folium map.
"""
import json

from .plugin import Plugin

class MarkerCluster(Plugin):
"""Adds a MarkerCluster layer on the map."""
def __init__(self, data):
"""Creates a MarkerCluster plugin to append into a map with
Map.add_plugin.

Parameters
----------
data: list of list or array of shape (n,3).
Data points of the form [[lat, lng, popup]].
"""
super(MarkerCluster, self).__init__()
self.plugin_name = 'MarkerCluster'
self.data = [tuple(x) for x in data]

def render_header(self, nb):
"""Generates the HTML part of the plugin."""
return """
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/leaflet.markercluster/0.4.0/MarkerCluster.css" />
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/leaflet.markercluster/0.4.0/MarkerCluster.Default.css" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/leaflet.markercluster/0.4.0/leaflet.markercluster.js"></script>
""" if nb==0 else ""

def render_js(self, nb):
"""Generates the Javascript part of the plugin."""
out = """
var addressPoints = """+json.dumps(self.data)+""";

var markers = L.markerClusterGroup();

for (var i = 0; i < addressPoints.length; i++) {
var a = addressPoints[i];
var title = a[2];
var marker = L.marker(new L.LatLng(a[0], a[1]), { title: title });
marker.bindPopup(title);
markers.addLayer(marker);
}

map.addLayer(markers);
"""
return out
42 changes: 42 additions & 0 deletions folium/plugins/plugin.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# -*- coding: utf-8 -*-
"""
Plugin
------

A generic class for creating plugins.
Basic plugin object that does nothing.
Other plugins may inherit from this one.
"""
from uuid import uuid4

from jinja2 import Environment, PackageLoader
ENV = Environment(loader=PackageLoader('folium', 'plugins'))

class Plugin(object):
"""Basic plugin object that does nothing.
Other plugins may inherit from this one."""
def __init__(self):
"""Creates a plugin to append into a map with Map.add_plugin. """
self.plugin_name = 'Plugin'
self.object_name = uuid4().hex
self.env = ENV

def add_to_map(self, map):
"""Adds the plugin on a folium.map object."""
map.plugins.setdefault(self.plugin_name,[]).append(self)
self.map = map

def render_html(self, nb):
"""Generates the HTML part of the plugin."""
return ""

def render_css(self, nb):
"""Generates the CSS part of the plugin."""
return ""

def render_js(self, nb):
"""Generates the Javascript part of the plugin."""
return ""
def render_header(self, nb):
"""Generates the Header part of the plugin."""
return ""
52 changes: 52 additions & 0 deletions folium/plugins/scroll_zoom_toggler.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
# -*- coding: utf-8 -*-
"""
ScrollZoomToggler plugin
------------------------

Adds a button to enable/disable zoom scrolling.
"""
from .template_plugin import TemplatePlugin

class ScrollZoomToggler(TemplatePlugin):
"""Adds a button to enable/disable zoom scrolling."""
template = """
{% set plugin_name = "ScrollZoomToggler" %}
{% macro css(nb) %}
#ScrollZoomToggler_{{nb}} {
position:absolute;
width:35px;
bottom:10px;
height:35px;
left:10px;
background-color:#fff;
text-align:center;
line-height:35px;
vertical-align: middle;
}
{% endmacro %}

{% macro html(nb) %}
<img id="ScrollZoomToggler_{{nb}}" alt="scroll"
src="https://cdnjs.cloudflare.com/ajax/libs/ionicons/1.5.2/png/512/arrow-move.png"
onclick="toggleScroll()"></img>
{% endmacro %}

{% macro js(nb) %}
{% if nb==0 %}
map.scrollEnabled = true;

var toggleScroll = function() {
if (map.scrollEnabled) {
map.scrollEnabled = false;
map.scrollWheelZoom.disable();
}
else {
map.scrollEnabled = true;
map.scrollWheelZoom.enable();
}
};

toggleScroll();
{% endif %}
{% endmacro %}
"""
Loading