Skip to content

Commit bb46954

Browse files
authored
Merge pull request #634 from ocefpaf/png_repr
PNG repr
2 parents 172df66 + cf0395a commit bb46954

6 files changed

Lines changed: 198 additions & 104 deletions

File tree

.travis.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ before_install:
3333
- export PATH="$HOME/miniconda/bin:$PATH"
3434
- conda update --yes --all
3535
- conda config --add channels conda-forge --force
36-
- conda create --yes -n TEST python=$TRAVIS_PYTHON_VERSION --file requirements.txt --file requirements-dev.txt
36+
- conda create --yes -n TEST python=$TRAVIS_PYTHON_VERSION phantomjs --file requirements.txt --file requirements-dev.txt
3737
- source activate TEST
3838

3939
- if [[ "$TRAVIS_PYTHON_VERSION" == "2.7" ]]; then

examples/Features.ipynb

Lines changed: 95 additions & 90 deletions
Large diffs are not rendered by default.

folium/map.py

Lines changed: 38 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,10 @@
1010

1111
from __future__ import unicode_literals
1212

13+
import os
14+
import tempfile
15+
import time
16+
1317
import json
1418
from collections import OrderedDict
1519

@@ -153,10 +157,13 @@ def __init__(self, location=None, width='100%', height='100%',
153157
min_lon=-180, max_lon=180, max_bounds=True,
154158
detect_retina=False, crs='EPSG3857', control_scale=False,
155159
prefer_canvas=False, no_touch=False, disable_3d=False,
156-
subdomains='abc'):
160+
subdomains='abc', png_enabled=False):
157161
super(LegacyMap, self).__init__()
158162
self._name = 'Map'
159163
self._env = ENV
164+
# Undocumented for now b/c this will be subject to a re-factor soon.
165+
self._png_image = None
166+
self.png_enabled = png_enabled
160167

161168
if not location:
162169
# If location is not passed we center and ignore zoom.
@@ -236,8 +243,7 @@ def __init__(self, location=None, width='100%', height='100%',
236243
""") # noqa
237244

238245
def _repr_html_(self, **kwargs):
239-
"""Displays the Map in a Jupyter notebook.
240-
"""
246+
"""Displays the HTML Map in a Jupyter notebook."""
241247
if self._parent is None:
242248
self.add_to(Figure())
243249
out = self._parent._repr_html_(**kwargs)
@@ -246,6 +252,35 @@ def _repr_html_(self, **kwargs):
246252
out = self._parent._repr_html_(**kwargs)
247253
return out
248254

255+
def _to_png(self):
256+
"""Export the HTML to byte representation of a PNG image."""
257+
if self._png_image is None:
258+
import selenium.webdriver
259+
260+
with tempfile.NamedTemporaryFile(suffix=".html") as f:
261+
fname = f.name
262+
self.save(fname)
263+
driver = selenium.webdriver.PhantomJS(service_log_path=os.path.devnull)
264+
driver.get('file://{}'.format(fname))
265+
driver.maximize_window()
266+
# Ignore user map size.
267+
driver.execute_script("document.body.style.width = '100%';")
268+
# We should probably monitor if some element is present,
269+
# but this is OK for now.
270+
time.sleep(3)
271+
png = driver.get_screenshot_as_png()
272+
driver.quit()
273+
self._png_image = png
274+
return self._png_image
275+
276+
def _repr_png_(self):
277+
"""Displays the PNG Map in a Jupyter notebook."""
278+
# The notebook calls all _repr_*_ by default.
279+
# We don't want that here b/c this one is quite slow.
280+
if not self.png_enabled:
281+
return None
282+
return self._to_png()
283+
249284
def add_tile_layer(self, tiles='OpenStreetMap', name=None,
250285
API_key=None, max_zoom=18, min_zoom=1,
251286
continuous_world=False, attr=None, active=False,

requirements-dev.txt

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
1-
pycodestyle
2-
pytest
3-
nbconvert
4-
jupyter_client
5-
ipykernel
6-
vincent
7-
geopandas
81
cartopy
9-
gpxpy
102
geographiclib
3+
geopandas
4+
gpxpy
5+
ipykernel
6+
jupyter_client
117
mplleaflet
8+
nbconvert
9+
pycodestyle
10+
pytest
11+
selenium
12+
vincent

requirements.txt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
Jinja2
21
branca
3-
six
2+
jinja2
43
requests
4+
six

tests/test_repr.py

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
# -*- coding: utf-8 -*-
2+
""""
3+
Folium _repr_*_ Tests
4+
---------------------
5+
6+
"""
7+
8+
import io
9+
10+
import folium
11+
12+
import pytest
13+
14+
import PIL.Image
15+
16+
17+
@pytest.fixture
18+
def make_map(png_enabled=False):
19+
m = folium.Map(png_enabled=png_enabled)
20+
return m
21+
22+
23+
def test__repr_html_is_str():
24+
html = make_map()._repr_html_()
25+
assert isinstance(html, str)
26+
27+
28+
def test_valid_html():
29+
html = make_map()._repr_html_()
30+
parts = html.split('><')
31+
assert len(parts) == 6
32+
assert parts[0].lstrip('<div ') == 'style="width:100%;"'
33+
assert parts[1].lstrip('<div ') == 'style="position:relative;width:100%;height:0;padding-bottom:60%;"' # noqa
34+
assert parts[2].startswith('iframe')
35+
assert parts[3] == '/iframe'
36+
assert parts[4] == '/div'
37+
assert parts[5] == '/div>'
38+
39+
40+
def test__repr_png_no_image():
41+
png = make_map(png_enabled=False)._repr_png_()
42+
assert png is None
43+
44+
45+
def test__repr_png_is_bytes():
46+
png = make_map(png_enabled=True)._repr_png_()
47+
assert isinstance(png, bytes)
48+
49+
50+
def test_valid_png():
51+
png = make_map(png_enabled=True)._repr_png_()
52+
img = PIL.Image.open(io.BytesIO(png))
53+
isinstance(img, PIL.PngImagePlugin.PngImageFile)

0 commit comments

Comments
 (0)