Skip to content

Commit 59db576

Browse files
authored
Merge pull request #537 from juoceano/float_image
Add a float image plugin
2 parents d2c495b + d6f26d1 commit 59db576

4 files changed

Lines changed: 178 additions & 9 deletions

File tree

examples/FloatImage.ipynb

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
{
2+
"cells": [
3+
{
4+
"cell_type": "code",
5+
"execution_count": 1,
6+
"metadata": {
7+
"collapsed": false
8+
},
9+
"outputs": [
10+
{
11+
"name": "stdout",
12+
"output_type": "stream",
13+
"text": [
14+
"0.3.0.dev\n"
15+
]
16+
}
17+
],
18+
"source": [
19+
"import os\n",
20+
"import folium\n",
21+
"\n",
22+
"print(folium.__version__)"
23+
]
24+
},
25+
{
26+
"cell_type": "code",
27+
"execution_count": 2,
28+
"metadata": {
29+
"collapsed": false
30+
},
31+
"outputs": [
32+
{
33+
"data": {
34+
"text/html": [
35+
"<div style=\"width:100%;\"><div style=\"position:relative;width:100%;height:0;padding-bottom:60%;\"><iframe src=\"data:text/html;base64,PCFET0NUWVBFIGh0bWw+CjxoZWFkPiAgICAKICAgIDxtZXRhIGh0dHAtZXF1aXY9ImNvbnRlbnQtdHlwZSIgY29udGVudD0idGV4dC9odG1sOyBjaGFyc2V0PVVURi04IiAvPgogICAgPHNjcmlwdD5MX1BSRUZFUl9DQU5WQVMgPSBmYWxzZTsgTF9OT19UT1VDSCA9IGZhbHNlOyBMX0RJU0FCTEVfM0QgPSBmYWxzZTs8L3NjcmlwdD4KICAgIDxzY3JpcHQgc3JjPSJodHRwczovL3VucGtnLmNvbS9sZWFmbGV0QDEuMC4xL2Rpc3QvbGVhZmxldC5qcyI+PC9zY3JpcHQ+CiAgICA8c2NyaXB0IHNyYz0iaHR0cHM6Ly9hamF4Lmdvb2dsZWFwaXMuY29tL2FqYXgvbGlicy9qcXVlcnkvMS4xMS4xL2pxdWVyeS5taW4uanMiPjwvc2NyaXB0PgogICAgPHNjcmlwdCBzcmM9Imh0dHBzOi8vbWF4Y2RuLmJvb3RzdHJhcGNkbi5jb20vYm9vdHN0cmFwLzMuMi4wL2pzL2Jvb3RzdHJhcC5taW4uanMiPjwvc2NyaXB0PgogICAgPHNjcmlwdCBzcmM9Imh0dHBzOi8vY2RuanMuY2xvdWRmbGFyZS5jb20vYWpheC9saWJzL0xlYWZsZXQuYXdlc29tZS1tYXJrZXJzLzIuMC4yL2xlYWZsZXQuYXdlc29tZS1tYXJrZXJzLmpzIj48L3NjcmlwdD4KICAgIDxzY3JpcHQgc3JjPSJodHRwczovL2NkbmpzLmNsb3VkZmxhcmUuY29tL2FqYXgvbGlicy9sZWFmbGV0Lm1hcmtlcmNsdXN0ZXIvMS4wLjAvbGVhZmxldC5tYXJrZXJjbHVzdGVyLXNyYy5qcyI+PC9zY3JpcHQ+CiAgICA8c2NyaXB0IHNyYz0iaHR0cHM6Ly9jZG5qcy5jbG91ZGZsYXJlLmNvbS9hamF4L2xpYnMvbGVhZmxldC5tYXJrZXJjbHVzdGVyLzEuMC4wL2xlYWZsZXQubWFya2VyY2x1c3Rlci5qcyI+PC9zY3JpcHQ+CiAgICA8bGluayByZWw9InN0eWxlc2hlZXQiIGhyZWY9Imh0dHBzOi8vdW5wa2cuY29tL2xlYWZsZXRAMS4wLjEvZGlzdC9sZWFmbGV0LmNzcyIgLz4KICAgIDxsaW5rIHJlbD0ic3R5bGVzaGVldCIgaHJlZj0iaHR0cHM6Ly9tYXhjZG4uYm9vdHN0cmFwY2RuLmNvbS9ib290c3RyYXAvMy4yLjAvY3NzL2Jvb3RzdHJhcC5taW4uY3NzIiAvPgogICAgPGxpbmsgcmVsPSJzdHlsZXNoZWV0IiBocmVmPSJodHRwczovL21heGNkbi5ib290c3RyYXBjZG4uY29tL2Jvb3RzdHJhcC8zLjIuMC9jc3MvYm9vdHN0cmFwLXRoZW1lLm1pbi5jc3MiIC8+CiAgICA8bGluayByZWw9InN0eWxlc2hlZXQiIGhyZWY9Imh0dHBzOi8vbWF4Y2RuLmJvb3RzdHJhcGNkbi5jb20vZm9udC1hd2Vzb21lLzQuNi4zL2Nzcy9mb250LWF3ZXNvbWUubWluLmNzcyIgLz4KICAgIDxsaW5rIHJlbD0ic3R5bGVzaGVldCIgaHJlZj0iaHR0cHM6Ly9jZG5qcy5jbG91ZGZsYXJlLmNvbS9hamF4L2xpYnMvTGVhZmxldC5hd2Vzb21lLW1hcmtlcnMvMi4wLjIvbGVhZmxldC5hd2Vzb21lLW1hcmtlcnMuY3NzIiAvPgogICAgPGxpbmsgcmVsPSJzdHlsZXNoZWV0IiBocmVmPSJodHRwczovL2NkbmpzLmNsb3VkZmxhcmUuY29tL2FqYXgvbGlicy9sZWFmbGV0Lm1hcmtlcmNsdXN0ZXIvMS4wLjAvTWFya2VyQ2x1c3Rlci5EZWZhdWx0LmNzcyIgLz4KICAgIDxsaW5rIHJlbD0ic3R5bGVzaGVldCIgaHJlZj0iaHR0cHM6Ly9jZG5qcy5jbG91ZGZsYXJlLmNvbS9hamF4L2xpYnMvbGVhZmxldC5tYXJrZXJjbHVzdGVyLzEuMC4wL01hcmtlckNsdXN0ZXIuY3NzIiAvPgogICAgPGxpbmsgcmVsPSJzdHlsZXNoZWV0IiBocmVmPSJodHRwczovL3Jhd2dpdC5jb20vcHl0aG9uLXZpc3VhbGl6YXRpb24vZm9saXVtL21hc3Rlci9mb2xpdW0vdGVtcGxhdGVzL2xlYWZsZXQuYXdlc29tZS5yb3RhdGUuY3NzIiAvPgogICAgPHN0eWxlPmh0bWwsIGJvZHkge3dpZHRoOiAxMDAlO2hlaWdodDogMTAwJTttYXJnaW46IDA7cGFkZGluZzogMDt9PC9zdHlsZT4KICAgIDxzdHlsZT4jbWFwIHtwb3NpdGlvbjphYnNvbHV0ZTt0b3A6MDtib3R0b206MDtyaWdodDowO2xlZnQ6MDt9PC9zdHlsZT4KICAgIAogICAgICAgICAgICA8c3R5bGU+ICNtYXBfMGRmMDU5YmVlMDQyNDRhNWE1MmJmYjY3NTgxYzI3ZDkgewogICAgICAgICAgICAgICAgcG9zaXRpb24gOiByZWxhdGl2ZTsKICAgICAgICAgICAgICAgIHdpZHRoIDogMTAwLjAlOwogICAgICAgICAgICAgICAgaGVpZ2h0OiAxMDAuMCU7CiAgICAgICAgICAgICAgICBsZWZ0OiAwLjAlOwogICAgICAgICAgICAgICAgdG9wOiAwLjAlOwogICAgICAgICAgICAgICAgfQogICAgICAgICAgICA8L3N0eWxlPgogICAgICAgIAogICAgCiAgICAgICAgICAgICAgICA8c3R5bGU+CiAgICAgICAgICAgICAgICAgICAgI2Zsb2F0X2ltYWdlX2Y4MTc3ZGM4MWFjYjQ2M2ViZjEwOThiMWRjZmVlZjFiIHsKICAgICAgICAgICAgICAgICAgICAgICAgcG9zaXRpb246YWJzb2x1dGU7CiAgICAgICAgICAgICAgICAgICAgICAgIGJvdHRvbTo2MCU7CiAgICAgICAgICAgICAgICAgICAgICAgIGxlZnQ6NzAlOwogICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICA8L3N0eWxlPgogICAgICAgICAgICAKPC9oZWFkPgo8Ym9keT4gICAgCiAgICAKICAgICAgICAgICAgPGRpdiBjbGFzcz0iZm9saXVtLW1hcCIgaWQ9Im1hcF8wZGYwNTliZWUwNDI0NGE1YTUyYmZiNjc1ODFjMjdkOSIgPjwvZGl2PgogICAgICAgIAogICAgCiAgICAgICAgICAgIDxpbWcgaWQ9ImZsb2F0X2ltYWdlX2Y4MTc3ZGM4MWFjYjQ2M2ViZjEwOThiMWRjZmVlZjFiIiBhbHQ9ImZsb2F0X2ltYWdlIgogICAgICAgICAgICAgICAgIHNyYz0iaHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL1NFQ09PUkEvc3RhdGljX2Fzc2V0cy9tYXN0ZXIvbWFwcy9pbWcvcm9zZS5wbmciCiAgICAgICAgICAgICAgICAgc3R5bGU9InotaW5kZXg6IDk5OTk5OSI+CiAgICAgICAgICAgIDwvaW1nPgogICAgICAgICAgICAKPC9ib2R5Pgo8c2NyaXB0PiAgICAKICAgIAoKICAgICAgICAgICAgCiAgICAgICAgICAgICAgICB2YXIgc291dGhXZXN0ID0gTC5sYXRMbmcoLTkwLCAtMTgwKTsKICAgICAgICAgICAgICAgIHZhciBub3J0aEVhc3QgPSBMLmxhdExuZyg5MCwgMTgwKTsKICAgICAgICAgICAgICAgIHZhciBib3VuZHMgPSBMLmxhdExuZ0JvdW5kcyhzb3V0aFdlc3QsIG5vcnRoRWFzdCk7CiAgICAgICAgICAgIAoKICAgICAgICAgICAgdmFyIG1hcF8wZGYwNTliZWUwNDI0NGE1YTUyYmZiNjc1ODFjMjdkOSA9IEwubWFwKAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgJ21hcF8wZGYwNTliZWUwNDI0NGE1YTUyYmZiNjc1ODFjMjdkOScsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB7Y2VudGVyOiBbLTEzLC0zOC4xNV0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB6b29tOiAxMCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1heEJvdW5kczogYm91bmRzLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGF5ZXJzOiBbXSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHdvcmxkQ29weUp1bXA6IGZhbHNlLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY3JzOiBMLkNSUy5FUFNHMzg1NwogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9KTsKICAgICAgICAgICAgCiAgICAgICAgCiAgICAKICAgICAgICAgICAgdmFyIHRpbGVfbGF5ZXJfYThkZDMyMjhlZjY1NGM0YmE5MzVmNmUxNDQ0YTIzNDUgPSBMLnRpbGVMYXllcigKICAgICAgICAgICAgICAgICdodHRwczovL3tzfS50aWxlLm9wZW5zdHJlZXRtYXAub3JnL3t6fS97eH0ve3l9LnBuZycsCiAgICAgICAgICAgICAgICB7CiAgICAgICAgICAgICAgICAgICAgbWF4Wm9vbTogMTgsCiAgICAgICAgICAgICAgICAgICAgbWluWm9vbTogMSwKICAgICAgICAgICAgICAgICAgICBjb250aW51b3VzV29ybGQ6IGZhbHNlLAogICAgICAgICAgICAgICAgICAgIG5vV3JhcDogZmFsc2UsCiAgICAgICAgICAgICAgICAgICAgYXR0cmlidXRpb246ICdEYXRhIGJ5IDxhIGhyZWY9Imh0dHA6Ly9vcGVuc3RyZWV0bWFwLm9yZyI+T3BlblN0cmVldE1hcDwvYT4sIHVuZGVyIDxhIGhyZWY9Imh0dHA6Ly93d3cub3BlbnN0cmVldG1hcC5vcmcvY29weXJpZ2h0Ij5PRGJMPC9hPi4nLAogICAgICAgICAgICAgICAgICAgIGRldGVjdFJldGluYTogZmFsc2UKICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICApLmFkZFRvKG1hcF8wZGYwNTliZWUwNDI0NGE1YTUyYmZiNjc1ODFjMjdkOSk7CgogICAgICAgIAo8L3NjcmlwdD4=\" style=\"position:absolute;width:100%;height:100%;left:0;top:0;\" allowfullscreen webkitallowfullscreen mozallowfullscreen></iframe></div></div>"
36+
],
37+
"text/plain": [
38+
"<folium.folium.Map at 0x7f61c4154c18>"
39+
]
40+
},
41+
"execution_count": 2,
42+
"metadata": {},
43+
"output_type": "execute_result"
44+
}
45+
],
46+
"source": [
47+
"from folium.plugins import FloatImage\n",
48+
"\n",
49+
"url = 'https://raw.githubusercontent.com/SECOORA/static_assets/master/maps/img/rose.png'\n",
50+
"\n",
51+
"m = folium.Map([-13, -38.15], zoom_start=10)\n",
52+
"\n",
53+
"FloatImage(url, bottom=60, left=70).add_to(m)\n",
54+
"\n",
55+
"m.save(os.path.join('results', 'FloatImage.html'))\n",
56+
"\n",
57+
"m"
58+
]
59+
}
60+
],
61+
"metadata": {
62+
"kernelspec": {
63+
"display_name": "Python 3",
64+
"language": "python",
65+
"name": "python3"
66+
},
67+
"language_info": {
68+
"codemirror_mode": {
69+
"name": "ipython",
70+
"version": 3
71+
},
72+
"file_extension": ".py",
73+
"mimetype": "text/x-python",
74+
"name": "python",
75+
"nbconvert_exporter": "python",
76+
"pygments_lexer": "ipython3",
77+
"version": "3.5.2"
78+
}
79+
},
80+
"nbformat": 4,
81+
"nbformat_minor": 1
82+
}

folium/plugins/__init__.py

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,17 @@
1515
from .image_overlay import ImageOverlay
1616
from .fullscreen import Fullscreen
1717
from .polyline_text_path import PolyLineTextPath
18+
from .float_image import FloatImage
1819

19-
__all__ = ['MarkerCluster',
20-
'ScrollZoomToggler',
21-
'Terminator',
22-
'BoatMarker',
23-
'TimestampedGeoJson',
24-
'HeatMap',
25-
'ImageOverlay',
26-
'Fullscreen',
27-
'PolyLineTextPath']
20+
__all__ = [
21+
'MarkerCluster',
22+
'ScrollZoomToggler',
23+
'Terminator',
24+
'BoatMarker',
25+
'TimestampedGeoJson',
26+
'HeatMap',
27+
'ImageOverlay',
28+
'Fullscreen',
29+
'PolyLineTextPath',
30+
'FloatImage'
31+
]

folium/plugins/float_image.py

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
# -*- coding: utf-8 -*-
2+
"""
3+
FloatImage plugin
4+
-----------------
5+
6+
Adds a floating image in HTML canvas on top of the map.
7+
"""
8+
from jinja2 import Template
9+
10+
from branca.element import MacroElement
11+
12+
13+
class FloatImage(MacroElement):
14+
def __init__(self, image, bottom=75, left=75):
15+
"""Adds a floating image in HTML canvas on top of the map."""
16+
super(FloatImage, self).__init__()
17+
self._name = 'FloatImage'
18+
self.image = image
19+
self.bottom = bottom
20+
self.left = left
21+
22+
self._template = Template("""
23+
{% macro header(this,kwargs) %}
24+
<style>
25+
#{{this.get_name()}} {
26+
position:absolute;
27+
bottom:{{this.bottom}}%;
28+
left:{{this.left}}%;
29+
}
30+
</style>
31+
{% endmacro %}
32+
33+
{% macro html(this,kwargs) %}
34+
<img id="{{this.get_name()}}" alt="float_image"
35+
src="{{ this.image }}"
36+
style="z-index: 999999">
37+
</img>
38+
{% endmacro %}
39+
""")

tests/plugins/test_float_image.py

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
# -*- coding: utf-8 -*-
2+
"""
3+
Test FloatImage
4+
---------------
5+
"""
6+
7+
from jinja2 import Template
8+
9+
import folium
10+
from folium import plugins
11+
12+
13+
def test_float_image():
14+
m = folium.Map([45., 3.], zoom_start=4)
15+
url = 'https://raw.githubusercontent.com/SECOORA/static_assets/master/maps/img/rose.png'
16+
szt = plugins.FloatImage(url, bottom=60, left=70)
17+
m.add_child(szt)
18+
m._repr_html_()
19+
20+
out = m._parent.render()
21+
22+
# Verify that the div has been created.
23+
tmpl = Template("""
24+
<img id="{{this.get_name()}}" alt="float_image"
25+
src="https://raw.githubusercontent.com/SECOORA/static_assets/master/maps/img/rose.png"
26+
style="z-index: 999999">
27+
</img>
28+
""")
29+
assert ''.join(tmpl.render(this=szt).split()) in ''.join(out.split())
30+
31+
# Verify that the style has been created.
32+
tmpl = Template("""
33+
<style>
34+
#{{this.get_name()}} {
35+
position:absolute;
36+
bottom:60%;
37+
left:70%;
38+
}
39+
</style>
40+
""")
41+
assert ''.join(tmpl.render(this=szt).split()) in ''.join(out.split())
42+
43+
bounds = m.get_bounds()
44+
assert bounds == [[None, None], [None, None]], bounds

0 commit comments

Comments
 (0)