Skip to content

Commit f25ca84

Browse files
authored
Merge pull request #665 from ocefpaf/video_overlay
Add video overlay plugin
2 parents b0a35ec + 53a9b56 commit f25ca84

3 files changed

Lines changed: 157 additions & 1 deletion

File tree

CHANGES.txt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
0.4.0
22
~~~~~
3-
- Added `TimestampedWmsTileLayers` (acrosby #644 and #660)
3+
- Added `VideoOverlay` plugin (ocefpaf #665)
4+
- Added `TimestampedWmsTileLayers` plugin (acrosby #644 and #660)
45
- Vega-Lite features support via altair (njwilson23 #643)
56
- Experimental support for a static png output (ocefpaf #634)
67
- Added support for subdomains options in TileLayer (damselem #623)

examples/VideoOverlayLayer.ipynb

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
{
2+
"cells": [
3+
{
4+
"cell_type": "code",
5+
"execution_count": 1,
6+
"metadata": {},
7+
"outputs": [
8+
{
9+
"name": "stdout",
10+
"output_type": "stream",
11+
"text": [
12+
"0.3.0+127.g5901668.dirty\n"
13+
]
14+
}
15+
],
16+
"source": [
17+
"import os\n",
18+
"import folium\n",
19+
"\n",
20+
"print(folium.__version__)"
21+
]
22+
},
23+
{
24+
"cell_type": "code",
25+
"execution_count": 2,
26+
"metadata": {},
27+
"outputs": [
28+
{
29+
"data": {
30+
"text/html": [
31+
"<div style=\"width:100%;\"><div style=\"position:relative;width:100%;height:0;padding-bottom:60%;\"><iframe src=\"data:text/html;charset=utf-8;base64,PCFET0NUWVBFIGh0bWw+CjxoZWFkPiAgICAKICAgIDxtZXRhIGh0dHAtZXF1aXY9ImNvbnRlbnQtdHlwZSIgY29udGVudD0idGV4dC9odG1sOyBjaGFyc2V0PVVURi04IiAvPgogICAgPHNjcmlwdD5MX1BSRUZFUl9DQU5WQVMgPSBmYWxzZTsgTF9OT19UT1VDSCA9IGZhbHNlOyBMX0RJU0FCTEVfM0QgPSBmYWxzZTs8L3NjcmlwdD4KICAgIDxzY3JpcHQgc3JjPSJodHRwczovL2Nkbi5qc2RlbGl2ci5uZXQvbnBtL2xlYWZsZXRAMS4xLjAvZGlzdC9sZWFmbGV0LmpzIj48L3NjcmlwdD4KICAgIDxzY3JpcHQgc3JjPSJodHRwczovL2FqYXguZ29vZ2xlYXBpcy5jb20vYWpheC9saWJzL2pxdWVyeS8xLjExLjEvanF1ZXJ5Lm1pbi5qcyI+PC9zY3JpcHQ+CiAgICA8c2NyaXB0IHNyYz0iaHR0cHM6Ly9tYXhjZG4uYm9vdHN0cmFwY2RuLmNvbS9ib290c3RyYXAvMy4yLjAvanMvYm9vdHN0cmFwLm1pbi5qcyI+PC9zY3JpcHQ+CiAgICA8c2NyaXB0IHNyYz0iaHR0cHM6Ly9jZG5qcy5jbG91ZGZsYXJlLmNvbS9hamF4L2xpYnMvTGVhZmxldC5hd2Vzb21lLW1hcmtlcnMvMi4wLjIvbGVhZmxldC5hd2Vzb21lLW1hcmtlcnMuanMiPjwvc2NyaXB0PgogICAgPHNjcmlwdCBzcmM9Imh0dHBzOi8vY2RuanMuY2xvdWRmbGFyZS5jb20vYWpheC9saWJzL2xlYWZsZXQubWFya2VyY2x1c3Rlci8xLjAuMC9sZWFmbGV0Lm1hcmtlcmNsdXN0ZXItc3JjLmpzIj48L3NjcmlwdD4KICAgIDxzY3JpcHQgc3JjPSJodHRwczovL2NkbmpzLmNsb3VkZmxhcmUuY29tL2FqYXgvbGlicy9sZWFmbGV0Lm1hcmtlcmNsdXN0ZXIvMS4wLjAvbGVhZmxldC5tYXJrZXJjbHVzdGVyLmpzIj48L3NjcmlwdD4KICAgIDxsaW5rIHJlbD0ic3R5bGVzaGVldCIgaHJlZj0iaHR0cHM6Ly9jZG4uanNkZWxpdnIubmV0L25wbS9sZWFmbGV0QDEuMS4wL2Rpc3QvbGVhZmxldC5jc3MiIC8+CiAgICA8bGluayByZWw9InN0eWxlc2hlZXQiIGhyZWY9Imh0dHBzOi8vbWF4Y2RuLmJvb3RzdHJhcGNkbi5jb20vYm9vdHN0cmFwLzMuMi4wL2Nzcy9ib290c3RyYXAubWluLmNzcyIgLz4KICAgIDxsaW5rIHJlbD0ic3R5bGVzaGVldCIgaHJlZj0iaHR0cHM6Ly9tYXhjZG4uYm9vdHN0cmFwY2RuLmNvbS9ib290c3RyYXAvMy4yLjAvY3NzL2Jvb3RzdHJhcC10aGVtZS5taW4uY3NzIiAvPgogICAgPGxpbmsgcmVsPSJzdHlsZXNoZWV0IiBocmVmPSJodHRwczovL21heGNkbi5ib290c3RyYXBjZG4uY29tL2ZvbnQtYXdlc29tZS80LjYuMy9jc3MvZm9udC1hd2Vzb21lLm1pbi5jc3MiIC8+CiAgICA8bGluayByZWw9InN0eWxlc2hlZXQiIGhyZWY9Imh0dHBzOi8vY2RuanMuY2xvdWRmbGFyZS5jb20vYWpheC9saWJzL0xlYWZsZXQuYXdlc29tZS1tYXJrZXJzLzIuMC4yL2xlYWZsZXQuYXdlc29tZS1tYXJrZXJzLmNzcyIgLz4KICAgIDxsaW5rIHJlbD0ic3R5bGVzaGVldCIgaHJlZj0iaHR0cHM6Ly9jZG5qcy5jbG91ZGZsYXJlLmNvbS9hamF4L2xpYnMvbGVhZmxldC5tYXJrZXJjbHVzdGVyLzEuMC4wL01hcmtlckNsdXN0ZXIuRGVmYXVsdC5jc3MiIC8+CiAgICA8bGluayByZWw9InN0eWxlc2hlZXQiIGhyZWY9Imh0dHBzOi8vY2RuanMuY2xvdWRmbGFyZS5jb20vYWpheC9saWJzL2xlYWZsZXQubWFya2VyY2x1c3Rlci8xLjAuMC9NYXJrZXJDbHVzdGVyLmNzcyIgLz4KICAgIDxsaW5rIHJlbD0ic3R5bGVzaGVldCIgaHJlZj0iaHR0cHM6Ly9yYXdnaXQuY29tL3B5dGhvbi12aXN1YWxpemF0aW9uL2ZvbGl1bS9tYXN0ZXIvZm9saXVtL3RlbXBsYXRlcy9sZWFmbGV0LmF3ZXNvbWUucm90YXRlLmNzcyIgLz4KICAgIDxzdHlsZT5odG1sLCBib2R5IHt3aWR0aDogMTAwJTtoZWlnaHQ6IDEwMCU7bWFyZ2luOiAwO3BhZGRpbmc6IDA7fTwvc3R5bGU+CiAgICA8c3R5bGU+I21hcCB7cG9zaXRpb246YWJzb2x1dGU7dG9wOjA7Ym90dG9tOjA7cmlnaHQ6MDtsZWZ0OjA7fTwvc3R5bGU+CiAgICAKICAgICAgICAgICAgPHN0eWxlPiAjbWFwX2ZlMGVmMjgyNTVhZTQ1ZjdhMzkwN2NlNWNkMjY2NjRkIHsKICAgICAgICAgICAgICAgIHBvc2l0aW9uIDogcmVsYXRpdmU7CiAgICAgICAgICAgICAgICB3aWR0aCA6IDEwMC4wJTsKICAgICAgICAgICAgICAgIGhlaWdodDogMTAwLjAlOwogICAgICAgICAgICAgICAgbGVmdDogMC4wJTsKICAgICAgICAgICAgICAgIHRvcDogMC4wJTsKICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgPC9zdHlsZT4KICAgICAgICAKPC9oZWFkPgo8Ym9keT4gICAgCiAgICAKICAgICAgICAgICAgPGRpdiBjbGFzcz0iZm9saXVtLW1hcCIgaWQ9Im1hcF9mZTBlZjI4MjU1YWU0NWY3YTM5MDdjZTVjZDI2NjY0ZCIgPjwvZGl2PgogICAgICAgIAo8L2JvZHk+CjxzY3JpcHQ+ICAgIAogICAgCgogICAgICAgICAgICAKICAgICAgICAgICAgICAgIHZhciBzb3V0aFdlc3QgPSBMLmxhdExuZygtOTAsIC0xODApOwogICAgICAgICAgICAgICAgdmFyIG5vcnRoRWFzdCA9IEwubGF0TG5nKDkwLCAxODApOwogICAgICAgICAgICAgICAgdmFyIGJvdW5kcyA9IEwubGF0TG5nQm91bmRzKHNvdXRoV2VzdCwgbm9ydGhFYXN0KTsKICAgICAgICAgICAgCgogICAgICAgICAgICB2YXIgbWFwX2ZlMGVmMjgyNTVhZTQ1ZjdhMzkwN2NlNWNkMjY2NjRkID0gTC5tYXAoCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAnbWFwX2ZlMGVmMjgyNTVhZTQ1ZjdhMzkwN2NlNWNkMjY2NjRkJywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHtjZW50ZXI6IFsyMi41LC0xMTUuMF0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB6b29tOiA0LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbWF4Qm91bmRzOiBib3VuZHMsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsYXllcnM6IFtdLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgd29ybGRDb3B5SnVtcDogZmFsc2UsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjcnM6IEwuQ1JTLkVQU0czODU3CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0pOwogICAgICAgICAgICAKICAgICAgICAKICAgIAogICAgICAgICAgICB2YXIgdGlsZV9sYXllcl8xMjNmMGRjNTNhMDU0YjhlODZjZWJmZmRmYTNhYWNkNCA9IEwudGlsZUxheWVyKAogICAgICAgICAgICAgICAgJ2h0dHBzOi8ve3N9LnRpbGUub3BlbnN0cmVldG1hcC5vcmcve3p9L3t4fS97eX0ucG5nJywKICAgICAgICAgICAgICAgIHsKICAgICAgICAgICAgICAgICAgICBtYXhab29tOiAxOCwKICAgICAgICAgICAgICAgICAgICBtaW5ab29tOiAxLAogICAgICAgICAgICAgICAgICAgIGNvbnRpbnVvdXNXb3JsZDogZmFsc2UsCiAgICAgICAgICAgICAgICAgICAgbm9XcmFwOiBmYWxzZSwKICAgICAgICAgICAgICAgICAgICBhdHRyaWJ1dGlvbjogJ0RhdGEgYnkgPGEgaHJlZj0iaHR0cDovL29wZW5zdHJlZXRtYXAub3JnIj5PcGVuU3RyZWV0TWFwPC9hPiwgdW5kZXIgPGEgaHJlZj0iaHR0cDovL3d3dy5vcGVuc3RyZWV0bWFwLm9yZy9jb3B5cmlnaHQiPk9EYkw8L2E+LicsCiAgICAgICAgICAgICAgICAgICAgZGV0ZWN0UmV0aW5hOiBmYWxzZSwKICAgICAgICAgICAgICAgICAgICBzdWJkb21haW5zOiAnYWJjJwogICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICkuYWRkVG8obWFwX2ZlMGVmMjgyNTVhZTQ1ZjdhMzkwN2NlNWNkMjY2NjRkKTsKCiAgICAgICAgCiAgICAKICAgICAgICAgICAgICAgIHZhciB2aWRlb19vdmVybGF5X2JhMDM5MGYzYTI5ZTQ4MGY4ZDdkYTQ3YWViOWY2N2Q5ID0gTC52aWRlb092ZXJsYXkoCiAgICAgICAgICAgICAgICAgICAgJ2h0dHBzOi8vd3d3Lm1hcGJveC5jb20vYml0ZXMvMDAxODgvcGF0cmljaWFfbmFzYS53ZWJtJywKICAgICAgICAgICAgICAgICAgICBbWzMyLCAtMTMwXSwgWzEzLCAtMTAwXV0sCiAgICAgICAgICAgICAgICAgICAgeyJvcGFjaXR5IjogMC42NSwgImF0dHJpYnV0aW9uIjogIlZpZGVvIGZyb20gcGF0cmljaWFfbmFzYSIsICJsb29wIjogZmFsc2UsICJhdXRvcGxheSI6IHRydWV9CiAgICAgICAgICAgICAgICAgICAgKS5hZGRUbyhtYXBfZmUwZWYyODI1NWFlNDVmN2EzOTA3Y2U1Y2QyNjY2NGQpOwogICAgICAgICAgICAKPC9zY3JpcHQ+\" style=\"position:absolute;width:100%;height:100%;left:0;top:0;border:none !important;\" allowfullscreen webkitallowfullscreen mozallowfullscreen></iframe></div></div>"
32+
],
33+
"text/plain": [
34+
"<folium.folium.Map at 0x7fc902900160>"
35+
]
36+
},
37+
"execution_count": 2,
38+
"metadata": {},
39+
"output_type": "execute_result"
40+
}
41+
],
42+
"source": [
43+
"from folium.plugins.video_overlay import VideoOverlay\n",
44+
"\n",
45+
"m = folium.Map(location=[22.5, -115], zoom_start=4)\n",
46+
"\n",
47+
"video = VideoOverlay(\n",
48+
" video_url='https://www.mapbox.com/bites/00188/patricia_nasa.webm',\n",
49+
" bounds=[[ 32, -130], [ 13, -100]],\n",
50+
" opacity=0.65,\n",
51+
" attr='Video from patricia_nasa',\n",
52+
" autoplay=True,\n",
53+
" loop=False,\n",
54+
")\n",
55+
"\n",
56+
"video.add_to(m)\n",
57+
"\n",
58+
"m.save(os.path.join('results', 'VideoOverlayLayer.html'))\n",
59+
"\n",
60+
"m"
61+
]
62+
}
63+
],
64+
"metadata": {
65+
"_draft": {
66+
"nbviewer_url": "https://gist.github.com/629da6f621481ed4d513258d2ec64589"
67+
},
68+
"gist": {
69+
"data": {
70+
"description": "folium video test",
71+
"public": true
72+
},
73+
"id": "629da6f621481ed4d513258d2ec64589"
74+
},
75+
"kernelspec": {
76+
"display_name": "Python [default]",
77+
"language": "python",
78+
"name": "python3"
79+
},
80+
"language_info": {
81+
"codemirror_mode": {
82+
"name": "ipython",
83+
"version": 3
84+
},
85+
"file_extension": ".py",
86+
"mimetype": "text/x-python",
87+
"name": "python",
88+
"nbconvert_exporter": "python",
89+
"pygments_lexer": "ipython3",
90+
"version": "3.6.2"
91+
}
92+
},
93+
"nbformat": 4,
94+
"nbformat_minor": 2
95+
}

folium/plugins/video_overlay.py

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
"""
2+
Video Overlay
3+
-------------
4+
5+
Used to load and display a video over the map.
6+
7+
"""
8+
9+
import json
10+
11+
from folium.map import Layer
12+
13+
from jinja2 import Template
14+
15+
16+
class VideoOverlay(Layer):
17+
def __init__(self, video_url, bounds, opacity=1., attr=None,
18+
autoplay=True, loop=True):
19+
"""
20+
Used to load and display a video over the map.
21+
22+
Parameters
23+
----------
24+
video_url: URL of the video
25+
bounds: list
26+
Video bounds on the map in the form [[lat_min, lon_min],
27+
[lat_max, lon_max]]
28+
opacity: float, default Leaflet's default (1.0)
29+
attr: string, default Leaflet's default ('')
30+
31+
"""
32+
super(VideoOverlay, self).__init__()
33+
self._name = 'VideoOverlay'
34+
35+
self.video_url = video_url
36+
37+
self.bounds = json.loads(json.dumps(bounds))
38+
options = {
39+
'opacity': opacity,
40+
'attribution': attr,
41+
'loop': loop,
42+
'autoplay': autoplay,
43+
}
44+
self.options = json.dumps(options)
45+
46+
self._template = Template(u"""
47+
{% macro script(this, kwargs) %}
48+
var {{this.get_name()}} = L.videoOverlay(
49+
'{{ this.video_url }}',
50+
{{ this.bounds }},
51+
{{ this.options }}
52+
).addTo({{this._parent.get_name()}});
53+
{% endmacro %}
54+
""")
55+
56+
def _get_self_bounds(self):
57+
"""Computes the bounds of the object itself (not including it's children)
58+
in the form [[lat_min, lon_min], [lat_max, lon_max]]
59+
"""
60+
return self.bounds

0 commit comments

Comments
 (0)