diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index 3e974cff2..7a0f090af 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -44,32 +44,37 @@ Whenever possible, please also include a [short, self-contained code example](ht First of all, thanks for your interest in contributing! -- If you are new to git/Github, please take check a few tutorials - on [git](https://git-scm.com/docs/gittutorial) and [GitHub](https://guides.github.com/). -- The basic workflow for contributing is: - 1. [Fork](https://help.github.com/articles/fork-a-repo/) the repository - 2. [Clone](https://help.github.com/articles/cloning-a-repository/) the repository to create a local copy on your computer: - ``` - git clone git@github.com:${user}/folium.git - cd folium - ``` - 3. Create a branch for your changes - ``` - git checkout -b name-of-your-branch - ``` - 4. Make change to your local copy of the folium repository - 5. Make sure the tests pass: - * in the repository folder do `pip install -e .` (needed for notebook tests) - * along with all the dependencies install `phantomjs` via `npm install -g phantomjs` or by downloading it from [here](http://phantomjs.org/download.html) and installing manually - * run `python -m pytest tests` - * resolve all errors - 6. Commit those changes - ``` - git add file1 file2 file3 - git commit -m 'a descriptive commit message' - ``` - 7. Push your updated branch to your fork - ``` - git push origin name-of-your-branch - ``` - 8. [Open a pull request](https://help.github.com/articles/creating-a-pull-request/) to the python-visualization/folium +If you are new to git/Github, please take check a few tutorials +on [git](https://git-scm.com/docs/gittutorial) and [GitHub](https://guides.github.com/). + +The basic workflow for contributing is: + +1. [Fork](https://help.github.com/articles/fork-a-repo/) the repository +2. [Clone](https://help.github.com/articles/cloning-a-repository/) the repository to create a local copy on your computer: + ``` + git clone git@github.com:${user}/folium.git + cd folium + ``` +3. Create a branch for your changes + ``` + git checkout -b name-of-your-branch + ``` +4. Install the dependencies listed in `requirements.txt` and `requirements-dev.txt`. +5. Install Firefox, download [geckodriver](https://github.com/mozilla/geckodriver/releases) + and put it in the PATH. +6. Make changes to your local copy of the folium repository +7. Make sure the tests pass: + * in the repository folder do `pip install -e .` (needed for notebook tests) + * run `python -m pytest tests` + * run `flake8 folium --max-line-length=120` + * resolve all errors +8. Commit those changes + ``` + git add file1 file2 file3 + git commit -m 'a descriptive commit message' + ``` +9. Push your updated branch to your fork + ``` + git push origin name-of-your-branch + ``` +10. [Open a pull request](https://help.github.com/articles/creating-a-pull-request/) to the python-visualization/folium diff --git a/.gitignore b/.gitignore index 677e7b15a..261ecaa0f 100644 --- a/.gitignore +++ b/.gitignore @@ -13,3 +13,4 @@ examples/results/* /codestyles/*.xml /_mac/*.xml /inspection/*.xml +geckodriver.log diff --git a/.travis.yml b/.travis.yml index f1a92e0e5..0dbb3c9b9 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,6 +2,12 @@ language: minimal sudo: false +env: + - MOZ_HEADLESS=1 + +addons: + firefox: latest + env: global: - secure: "gRT413onDOvwgiHpNXRsiqo+ZZSjwwBpjZryQ9h6IqYw6cTN9YVivYF15uTMD//mZyFeHRz+F/7/0EG2z+UYIBKbgktiNMbie/KizwRBnCThGpcch1VeizkBkPluWSQXndXM6STkHvn0eZBZBBh0QdTm1qaI0babUmgZuWhrX38=" @@ -31,8 +37,13 @@ before_install: - conda update conda - conda config --remove channels defaults --force - conda config --add channels conda-forge --force - - conda create --name TEST python=$PY phantomjs --file requirements.txt --file requirements-dev.txt + - conda create --name TEST python=$PY --file requirements.txt --file requirements-dev.txt - source activate TEST + # firefox headless driver + - wget https://github.com/mozilla/geckodriver/releases/download/v0.23.0/geckodriver-v0.23.0-linux64.tar.gz -O geckodriver.tar.gz + - mkdir geckodriver + - tar -xzf geckodriver.tar.gz -C geckodriver + - export PATH=$PATH:$PWD/geckodriver - if [[ "$PY" == "2.7" ]]; then conda install mock ; diff --git a/folium/folium.py b/folium/folium.py index 270e0a065..f809fa821 100644 --- a/folium/folium.py +++ b/folium/folium.py @@ -7,7 +7,6 @@ from __future__ import (absolute_import, division, print_function) -import os import time import warnings @@ -15,7 +14,7 @@ from folium.map import FitBounds from folium.raster_layers import TileLayer -from folium.utilities import _parse_size, _validate_location +from folium.utilities import _parse_size, _tmp_html, _validate_location from jinja2 import Environment, PackageLoader, Template @@ -202,10 +201,10 @@ class Map(MacroElement): zoomControl: {{this.zoom_control.__str__().lower()}}, }); {% if this.control_scale %}L.control.scale().addTo({{this.get_name()}});{% endif %} - + {% if this.objects_to_stay_in_front %} function objects_in_front() { - {% for obj in this.objects_to_stay_in_front %} + {% for obj in this.objects_to_stay_in_front %} {{ obj.get_name() }}.bringToFront(); {% endfor %} }; @@ -290,35 +289,30 @@ def _repr_html_(self, **kwargs): def _to_png(self, delay=3): """Export the HTML to byte representation of a PNG image. - Uses Phantom JS to render the HTML and record a PNG. You may need to + Uses selenium to render the HTML and record a PNG. You may need to adjust the `delay` time keyword argument if maps render without data or tiles. Examples -------- >>> map._to_png() >>> map._to_png(time=10) # Wait 10 seconds between render and snapshot. - """ + """ if self._png_image is None: - import selenium.webdriver + from selenium import webdriver + + options = webdriver.firefox.options.Options() + options.add_argument('--headless') + driver = webdriver.Firefox(options=options) - driver = selenium.webdriver.PhantomJS( - service_log_path=os.path.devnull - ) - driver.get('about:blank') html = self.get_root().render() - html = html.replace('\'', '"').replace('"', '\\"') - html = html.replace('\n', '') - driver.execute_script('document.write(\"{}\")'.format(html)) - driver.maximize_window() - # Ignore user map size. - # todo: fix this - # driver.execute_script("document.body.style.width = '100%';") # noqa - # We should probably monitor if some element is present, - # but this is OK for now. - time.sleep(delay) - png = driver.get_screenshot_as_png() - driver.quit() + with _tmp_html(html) as fname: + # We need the tempfile to avoid JS security issues. + driver.get('file:///{path}'.format(path=fname)) + driver.maximize_window() + time.sleep(delay) + png = driver.get_screenshot_as_png() + driver.quit() self._png_image = png return self._png_image diff --git a/folium/utilities.py b/folium/utilities.py index 2cf2fbfef..971f26816 100644 --- a/folium/utilities.py +++ b/folium/utilities.py @@ -6,7 +6,9 @@ import math import os import struct +import tempfile import zlib +from contextlib import contextmanager import numpy as np @@ -385,3 +387,17 @@ def iter_points(x): return [x] else: return [] + + +@contextmanager +def _tmp_html(data): + """Yields the path of a temporary HTML file containing data.""" + filepath = '' + try: + fid, filepath = tempfile.mkstemp(suffix='.html', prefix='folium_') + os.write(fid, data.encode('utf8')) + os.close(fid) + yield filepath + finally: + if os.path.isfile(filepath): + os.remove(filepath) diff --git a/tests/test_repr.py b/tests/test_repr.py index bccd77067..b7073a263 100644 --- a/tests/test_repr.py +++ b/tests/test_repr.py @@ -8,8 +8,8 @@ from __future__ import (absolute_import, division, print_function) -import sys import io +import sys import PIL.Image @@ -19,18 +19,22 @@ @pytest.fixture -def make_map(png_enabled=False): - m = folium.Map(png_enabled=png_enabled) - return m +def m(): + yield folium.Map(png_enabled=False) + + +@pytest.fixture +def m_png(): + yield folium.Map(png_enabled=True) -def test__repr_html_is_str(): - html = make_map()._repr_html_() +def test__repr_html_is_str(m): + html = m._repr_html_() assert isinstance(html, str) -def test_valid_html(): - html = make_map()._repr_html_() +def test_valid_html(m): + html = m._repr_html_() parts = html.split('><') assert len(parts) == 6 assert parts[0].lstrip('