├── requirements.txt
├── requirements-dev.txt
├── docs
└── index.rst
├── branca
├── plugins
│ └── __init__.py
├── __init__.py
├── scheme_base_codes.json
├── six.py
├── scheme_info.json
├── templates
│ └── color_scale.js
├── _cnames.json
├── utilities.py
├── colormap.py
├── _schemes.json
└── element.py
├── MANIFEST.in
├── tests
├── test_utilities.py
├── test_iframe.py
├── test_notebooks.py
└── test_colormap.py
├── CHANGES.txt
├── .gitignore
├── README.rst
├── LICENSE.txt
├── .travis.yml
├── setup.py
└── examples
└── Elements.ipynb
/requirements.txt:
--------------------------------------------------------------------------------
1 | Jinja2
2 |
--------------------------------------------------------------------------------
/requirements-dev.txt:
--------------------------------------------------------------------------------
1 | flake8
2 | pytest
3 | selenium
4 |
--------------------------------------------------------------------------------
/docs/index.rst:
--------------------------------------------------------------------------------
1 | Branca
2 | ######
3 |
4 | TODO : Doc
5 |
6 |
--------------------------------------------------------------------------------
/branca/plugins/__init__.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | """
3 | Branca plugins
4 | --------------
5 |
6 | Add different objects/effects in a branca webpage.
7 | """
8 |
9 | __all__ = []
10 |
--------------------------------------------------------------------------------
/branca/__init__.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | from __future__ import absolute_import
4 |
5 | import branca.colormap as colormap
6 | import branca.element as element
7 |
8 | __version__ = '0.2.0'
9 |
10 | __all__ = [
11 | 'colormap',
12 | 'element',
13 | ]
14 |
--------------------------------------------------------------------------------
/branca/scheme_base_codes.json:
--------------------------------------------------------------------------------
1 | {"codes": ["Spectral", "RdYlGn", "PuBu", "Accent", "OrRd", "Set1", "Set2", "Set3", "BuPu", "Dark2", "RdBu", "Oranges", "BuGn", "PiYG", "YlOrBr", "YlGn", "Pastel2", "RdPu", "Greens", "PRGn", "YlGnBu", "RdYlBu", "Paired", "BrBG", "Purples", "Reds", "Pastel1", "GnBu", "Greys", "RdGy", "YlOrRd", "PuOr", "PuRd", "Blues", "PuBuGn"]}
--------------------------------------------------------------------------------
/MANIFEST.in:
--------------------------------------------------------------------------------
1 | include *.txt
2 | include README.rst
3 | include branca/_cnames.json
4 | include branca/_schemes.json
5 | include branca/scheme_info.json
6 | include branca/scheme_base_codes.json
7 |
8 | recursive-include branca *.py
9 | recursive-include branca *.js
10 | recursive-include branca/plugins *
11 | recursive-include branca/templates *
12 |
--------------------------------------------------------------------------------
/tests/test_utilities.py:
--------------------------------------------------------------------------------
1 | import branca.utilities as ut
2 |
3 |
4 | def test_color_brewer_base():
5 | scheme = ut.color_brewer('YlGnBu', 9)
6 | assert scheme == [
7 | '#ffffd9', '#edf8b1', '#c7e9b4',
8 | '#7fcdbb', '#41b6c4', '#1d91c0',
9 | '#225ea8', '#253494', '#081d58'
10 | ]
11 |
12 |
13 | def test_color_brewer_reverse():
14 | scheme = ut.color_brewer('YlGnBu')
15 | scheme_r = ut.color_brewer('YlGnBu_r')
16 | assert scheme[::-1] == scheme_r
17 |
--------------------------------------------------------------------------------
/branca/six.py:
--------------------------------------------------------------------------------
1 | import sys
2 |
3 | PY3 = sys.version_info[0] == 3
4 |
5 | if PY3:
6 | text_type = str
7 | binary_type = bytes
8 | else:
9 | text_type = unicode # noqa
10 | binary_type = str
11 |
12 | if PY3:
13 | def iteritems(d, **kw):
14 | return iter(d.items(**kw))
15 | else:
16 | def iteritems(d, **kw):
17 | return iter(d.iteritems(**kw))
18 |
19 | if PY3:
20 | import urllib.request
21 | urlopen = urllib.request.urlopen
22 | else:
23 | import urllib
24 | urlopen = urllib.urlopen
25 |
--------------------------------------------------------------------------------
/branca/scheme_info.json:
--------------------------------------------------------------------------------
1 | {"Spectral": "Diverging", "RdYlGn": "Diverging", "Set2": "Qualitative", "Accent": "Qualitative", "OrRd": "Sequential", "Set1": "Qualitative", "PuBu": "Sequential", "Set3": "Qualitative", "BuPu": "Sequential", "Dark2": "Qualitative", "RdBu": "Diverging", "BuGn": "Sequential", "PiYG": "Diverging", "YlOrBr": "Sequential", "YlGn": "Sequential", "RdPu": "Sequential", "PRGn": "Diverging", "YlGnBu": "Sequential", "RdYlBu": "Diverging", "Paired": "Qualitative", "Pastel2": "Qualitative", "Pastel1": "Qualitative", "GnBu": "Sequential", "RdGy": "Diverging", "YlOrRd": "Sequential", "PuOr": "Diverging", "PuRd": "Sequential", "BrBg": "Diverging", "PuBuGn": "Sequential"}
--------------------------------------------------------------------------------
/CHANGES.txt:
--------------------------------------------------------------------------------
1 | - Added class for hosting step colormap (matsavage https://github.com/python-visualization/branca/pull/25)
2 |
3 | 0.2.0
4 | ~~~~~
5 | - Correct rendering utf-8 IFrame (knil-sama https://github.com/python-visualization/branca/pull/18)
6 | - Remove embedded IFrame's border (deelaka https://github.com/python-visualization/branca/pull/17)
7 | - Let IFrame contents go fullscreen (sanga https://github.com/python-visualization/branca/pull/13)
8 | - Add HTML Popup Class to element.py (samchorlton https://github.com/python-visualization/branca/pull/6)
9 |
10 | 0.1.0
11 | ~~~~~
12 | - Separate branca from folium (bibmartin d678357)
13 | - Enable HTML embedding in Html (samchorlton 90f6b13)
14 |
--------------------------------------------------------------------------------
/tests/test_iframe.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | """"
3 | Folium Element Module class IFrame
4 | ----------------------
5 | """
6 | import branca.element as elem
7 | from selenium import webdriver
8 |
9 |
10 | def test_create_empty_iframe():
11 | iframe = elem.IFrame()
12 | iframe.render()
13 |
14 |
15 | def test_create_iframe():
16 | iframe = elem.IFrame(html="
test content
", width=60, height=45)
17 | iframe.render()
18 |
19 |
20 | def test_rendering_utf8_iframe():
21 | iframe = elem.IFrame(html=u"
Cerrahpaşa Tıp Fakültesi
")
22 | driver = webdriver.PhantomJS()
23 | driver.get("data:text/html," + iframe.render())
24 | driver.switch_to.frame(0)
25 | assert u"Cerrahpaşa Tıp Fakültesi" in driver.page_source
26 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.py[cod]
2 |
3 | # C extensions
4 | *.so
5 |
6 | # Packages
7 | *.egg
8 | *.egg-info
9 | dist
10 | build
11 | eggs
12 | parts
13 | bin
14 | var
15 | sdist
16 | develop-eggs
17 | .installed.cfg
18 | lib
19 | lib64
20 | include
21 |
22 | # Installer logs
23 | pip-log.txt
24 |
25 | # Unit test / coverage reports
26 | .coverage
27 | .tox
28 | nosetests.xml
29 |
30 | # Translations
31 | *.mo
32 |
33 | # Mr Developer
34 | .mr.developer.cfg
35 | .project
36 | .pydevproject
37 |
38 | #Mac
39 | *.DS_Store
40 |
41 | # IPython Notebook Checkpoints
42 | .ipynb_checkpoints
43 |
44 | #Virtualenv
45 | ENV
46 | .env
47 |
48 | # Tests products
49 | .cache
50 | data.png
51 | map.html
52 | examples/foo.html
53 |
54 | # documentation builds
55 | docs/_build
56 |
--------------------------------------------------------------------------------
/README.rst:
--------------------------------------------------------------------------------
1 | .. image:: https://badge.fury.io/py/branca.png
2 | :target: http://badge.fury.io/py/branca
3 | .. image:: https://api.travis-ci.org/python-visualization/branca.png?branch=master
4 | :target: https://travis-ci.org/python-visualization/branca
5 | .. image:: https://badges.gitter.im/Join%20Chat.svg
6 | :target: https://gitter.im/python-visualization/folium?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge
7 |
8 | Branca
9 | ======
10 |
11 | This library is a spinoff from `folium`_, that would host the non-map-specific features.
12 |
13 | It may become a HTML+JS generation library in the future.
14 |
15 | It is based on Jinja2 only.
16 |
17 | There's no documentation, but you can browse the `examples`_ gallery.
18 |
19 | .. _`examples`: http://nbviewer.jupyter.org/github/python-visualization/branca/tree/master/examples
20 | .. _`folium`: https://github.com/python-visualization/folium
21 |
--------------------------------------------------------------------------------
/LICENSE.txt:
--------------------------------------------------------------------------------
1 | Copyright (C) 2013, Martin Journois
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining a copy of
4 | this software and associated documentation files (the "Software"), to deal in
5 | the Software without restriction, including without limitation the rights to
6 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
7 | of the Software, and to permit persons to whom the Software is furnished to do
8 | so, subject to the following conditions:
9 |
10 | The above copyright notice and this permission notice shall be included in all
11 | copies or substantial portions of the Software.
12 |
13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19 | SOFTWARE.
20 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: python
2 |
3 | sudo: false
4 |
5 | matrix:
6 | fast_finish: true
7 | include:
8 | - python: 2.7
9 | env: TEST_TARGET=default
10 | - python: 3.5
11 | env: TEST_TARGET=default
12 | - python: 3.6
13 | env: TEST_TARGET=default
14 | - python: 3.6
15 | env: TEST_TARGET=coding_standards
16 |
17 |
18 | before_install:
19 | - wget http://bit.ly/miniconda -O miniconda.sh
20 | - bash miniconda.sh -b -p $HOME/miniconda
21 | - export PATH="$HOME/miniconda/bin:$PATH"
22 | - conda config --add channels conda-forge --force
23 | - conda update conda --yes
24 | - travis_retry conda create --yes -n TEST python=$TRAVIS_PYTHON_VERSION --file requirements.txt --file requirements-dev.txt
25 | - source activate TEST
26 |
27 | # Test source distribution.
28 | install:
29 | - python setup.py sdist && version=$(python setup.py --version) && pushd dist && pip install branca-${version}.tar.gz && popd
30 |
31 | script:
32 | - if [[ $TEST_TARGET == 'default' ]]; then
33 | python setup.py test ;
34 | fi
35 | - if [[ $TEST_TARGET == 'coding_standards' ]]; then
36 | find . -type f -name "*.py" ! -name 'conf.py' | xargs flake8 --max-line-length=100 ;
37 | fi
38 |
--------------------------------------------------------------------------------
/tests/test_notebooks.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | """
3 | Branca Notebooks Tests
4 | ----------------------
5 |
6 | Here we try to execute all notebooks that are in `branca/examples`.
7 | """
8 |
9 | import os
10 | import sys
11 | import branca.utilities
12 |
13 | if sys.version_info[:2] == (3, 4):
14 | import nbconvert
15 |
16 | rootpath = os.path.abspath(os.path.dirname(__file__))
17 |
18 | class NotebookTester(object):
19 | def __init__(self, filename):
20 | self.filename = filename
21 |
22 | def __call__(self, exporter=None, filename=None):
23 | raw_nb = nbconvert.exporters.Exporter().from_filename(self.filename)
24 | raw_nb[0].metadata.setdefault('kernelspec', {})['name'] = 'python'
25 | exec_nb = nbconvert.preprocessors.ExecutePreprocessor().preprocess(*raw_nb)
26 |
27 | if exporter is not None:
28 | out_nb = nbconvert.exporters.MarkdownExporter().from_notebook_node(*exec_nb)
29 | if filename is None:
30 | assert self.filename.endswith('.ipynb')
31 | filename = self.filename[:-6] + exporter.file_extension
32 | open(filename, 'w').write(out_nb[0].encode('utf-8'))
33 |
34 | class TestNotebooks(object):
35 | _filepath = rootpath.rstrip('/')+'/../examples/'
36 | _nblist = [x for x in os.listdir(_filepath) if x.endswith('.ipynb')]
37 |
38 | for fn in TestNotebooks._nblist:
39 | setattr(TestNotebooks,
40 | 'test_'+branca.utilities._camelify(fn[:-6]),
41 | NotebookTester(TestNotebooks._filepath+fn).__call__
42 | )
43 |
--------------------------------------------------------------------------------
/tests/test_colormap.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | """"
3 | Folium Colormap Module
4 | ----------------------
5 | """
6 | import branca.colormap as cm
7 |
8 |
9 | def test_simple_step():
10 | step = cm.StepColormap(['green', 'yellow', 'red'],
11 | vmin=3., vmax=10.,
12 | index=[3, 4, 8, 10], caption='step')
13 | step = cm.StepColormap(['r', 'y', 'g', 'c', 'b', 'm'])
14 | step._repr_html_()
15 |
16 |
17 | def test_simple_linear():
18 | linear = cm.LinearColormap(['green', 'yellow', 'red'], vmin=3., vmax=10.)
19 | linear = cm.LinearColormap(['red', 'orange', 'yellow', 'green'],
20 | index=[0, 0.1, 0.9, 1.])
21 | linear._repr_html_()
22 |
23 |
24 | def test_linear_to_step():
25 | some_list = [30.6, 50, 51, 52, 53, 54, 55, 60, 70, 100]
26 | lc = cm.linear.YlOrRd_06
27 | lc.to_step(n=12)
28 | lc.to_step(index=[0, 2, 4, 6, 8, 10])
29 | lc.to_step(data=some_list, n=12)
30 | lc.to_step(data=some_list, n=12, method='linear')
31 | lc.to_step(data=some_list, n=12, method='log')
32 | lc.to_step(data=some_list, n=30, method='quantiles')
33 | lc.to_step(data=some_list, quantiles=[0, 0.3, 0.7, 1])
34 | lc.to_step(data=some_list, quantiles=[0, 0.3, 0.7, 1], round_method='int')
35 | lc.to_step(data=some_list, quantiles=[0, 0.3, 0.7, 1],
36 | round_method='log10')
37 |
38 |
39 | def test_step_to_linear():
40 | step = cm.StepColormap(['green', 'yellow', 'red'],
41 | vmin=3., vmax=10.,
42 | index=[3, 4, 8, 10], caption='step')
43 | step.to_linear()
44 |
45 |
46 | def test_linear_object():
47 | cm.linear.OrRd_06._repr_html_()
48 | cm.linear.PuBu_06.to_step(12)
49 | cm.linear.YlGn_06.scale(3, 12)
50 | cm.linear._repr_html_()
51 |
52 |
53 | def test_step_object():
54 | cm.step.OrRd_06._repr_html_()
55 | cm.step.PuBu_06.to_linear()
56 | cm.step.YlGn_06.scale(3, 12)
57 | cm.step._repr_html_()
58 |
--------------------------------------------------------------------------------
/branca/templates/color_scale.js:
--------------------------------------------------------------------------------
1 | {% macro script(this, kwargs) %}
2 | var {{this.get_name()}} = {};
3 |
4 | {%if this.color_range %}
5 | {{this.get_name()}}.color = d3.scale.threshold()
6 | .domain({{this.color_domain}})
7 | .range({{this.color_range}});
8 | {%else%}
9 | {{this.get_name()}}.color = d3.scale.threshold()
10 | .domain([{{ this.color_domain[0] }}, {{ this.color_domain[-1] }}])
11 | .range(['{{ this.fill_color }}', '{{ this.fill_color }}']);
12 | {%endif%}
13 |
14 | {{this.get_name()}}.x = d3.scale.linear()
15 | .domain([{{ this.color_domain[0] }}, {{ this.color_domain[-1] }}])
16 | .range([0, 400]);
17 |
18 | {{this.get_name()}}.legend = L.control({position: 'topright'});
19 | {{this.get_name()}}.legend.onAdd = function (map) {var div = L.DomUtil.create('div', 'legend'); return div};
20 | {{this.get_name()}}.legend.addTo({{this._parent.get_name()}});
21 |
22 | {{this.get_name()}}.xAxis = d3.svg.axis()
23 | .scale({{this.get_name()}}.x)
24 | .orient("top")
25 | .tickSize(1)
26 | .tickValues({{ this.tick_labels }});
27 |
28 | {{this.get_name()}}.svg = d3.select(".legend.leaflet-control").append("svg")
29 | .attr("id", 'legend')
30 | .attr("width", 450)
31 | .attr("height", 40);
32 |
33 | {{this.get_name()}}.g = {{this.get_name()}}.svg.append("g")
34 | .attr("class", "key")
35 | .attr("transform", "translate(25,16)");
36 |
37 | {{this.get_name()}}.g.selectAll("rect")
38 | .data({{this.get_name()}}.color.range().map(function(d, i) {
39 | return {
40 | x0: i ? {{this.get_name()}}.x({{this.get_name()}}.color.domain()[i - 1]) : {{this.get_name()}}.x.range()[0],
41 | x1: i < {{this.get_name()}}.color.domain().length ? {{this.get_name()}}.x({{this.get_name()}}.color.domain()[i]) : {{this.get_name()}}.x.range()[1],
42 | z: d
43 | };
44 | }))
45 | .enter().append("rect")
46 | .attr("height", 10)
47 | .attr("x", function(d) { return d.x0; })
48 | .attr("width", function(d) { return d.x1 - d.x0; })
49 | .style("fill", function(d) { return d.z; });
50 |
51 | {{this.get_name()}}.g.call({{this.get_name()}}.xAxis).append("text")
52 | .attr("class", "caption")
53 | .attr("y", 21)
54 | .text('{{ this.caption }}');
55 | {% endmacro %}
--------------------------------------------------------------------------------
/setup.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | import os
4 | import sys
5 | from setuptools import setup
6 | from setuptools.command.test import test as TestCommand
7 |
8 | rootpath = os.path.abspath(os.path.dirname(__file__))
9 |
10 |
11 | class PyTest(TestCommand):
12 | def finalize_options(self):
13 | TestCommand.finalize_options(self)
14 | self.verbose = True
15 |
16 | def run_tests(self):
17 | import pytest
18 | errno = pytest.main(self.test_args)
19 | sys.exit(errno)
20 |
21 |
22 | def read(*parts):
23 | return open(os.path.join(rootpath, *parts), 'r').read()
24 |
25 |
26 | def extract_version(module='branca'):
27 | version = None
28 | fname = os.path.join(rootpath, module, '__init__.py')
29 | with open(fname) as f:
30 | for line in f:
31 | if (line.startswith('__version__')):
32 | _, version = line.split('=')
33 | version = version.strip()[1:-1] # Remove quotation characters.
34 | break
35 | return version
36 |
37 |
38 | def walk_subpkg(name):
39 | data_files = []
40 | package_dir = 'branca'
41 | for parent, dirs, files in os.walk(os.path.join(package_dir, name)):
42 | # Remove package_dir from the path.
43 | sub_dir = os.sep.join(parent.split(os.sep)[1:])
44 | for f in files:
45 | data_files.append(os.path.join(sub_dir, f))
46 | return data_files
47 |
48 |
49 | pkg_data = {'': ['*.js',
50 | 'plugins/*.js',
51 | 'plugins/*.html',
52 | 'plugins/*.css',
53 | 'plugins/*.tpl',
54 | 'templates/*.html',
55 | 'templates/*.js',
56 | 'templates/*.txt']}
57 | pkgs = ['branca', ]
58 |
59 | LICENSE = read('LICENSE.txt')
60 | long_description = '{}\n{}'.format(read('README.rst'), read('CHANGES.txt'))
61 |
62 | # Dependencies.
63 | with open('requirements.txt') as f:
64 | tests_require = f.readlines()
65 | install_requires = [t.strip() for t in tests_require]
66 |
67 |
68 | config = dict(name='branca',
69 | version=extract_version(),
70 | description='Generate complex HTML+JS pages with Python',
71 | long_description=long_description,
72 | author='Martin Journois',
73 | url='https://github.com/python-visualization/branca',
74 | keywords='data visualization',
75 | classifiers=['Programming Language :: Python :: 2.7',
76 | 'Programming Language :: Python :: 3.4',
77 | 'Programming Language :: Python :: 3.5',
78 | 'License :: OSI Approved :: MIT License',
79 | 'Development Status :: 5 - Production/Stable'],
80 | packages=pkgs,
81 | package_data=pkg_data,
82 | include_package_data=True,
83 | cmdclass=dict(test=PyTest),
84 | tests_require=['pytest'],
85 | license=LICENSE,
86 | install_requires=install_requires,
87 | zip_safe=False)
88 |
89 |
90 | setup(**config)
91 |
--------------------------------------------------------------------------------
/branca/_cnames.json:
--------------------------------------------------------------------------------
1 | {"indigo": "#4B0082", "gold": "#FFD700", "hotpink": "#FF69B4", "firebrick": "#B22222", "indianred": "#CD5C5C", "sage": "#87AE73", "yellow": "#FFFF00", "mistyrose": "#FFE4E1", "darkolivegreen": "#556B2F", "olive": "#808000", "darkseagreen": "#8FBC8F", "pink": "#FFC0CB", "tomato": "#FF6347", "lightcoral": "#F08080", "orangered": "#FF4500", "navajowhite": "#FFDEAD", "lime": "#00FF00", "palegreen": "#98FB98", "greenyellow": "#ADFF2F", "burlywood": "#DEB887", "seashell": "#FFF5EE", "mediumspringgreen": "#00FA9A", "fuchsia": "#FF00FF", "papayawhip": "#FFEFD5", "blanchedalmond": "#FFEBCD", "chartreuse": "#7FFF00", "dimgray": "#696969", "black": "#000000", "peachpuff": "#FFDAB9", "springgreen": "#00FF7F", "aquamarine": "#7FFFD4", "white": "#FFFFFF", "b": "#0000FF", "orange": "#FFA500", "lightsalmon": "#FFA07A", "darkslategray": "#2F4F4F", "brown": "#A52A2A", "ivory": "#FFFFF0", "dodgerblue": "#1E90FF", "peru": "#CD853F", "lawngreen": "#7CFC00", "chocolate": "#D2691E", "crimson": "#DC143C", "forestgreen": "#228B22", "slateblue": "#6A5ACD", "lightseagreen": "#20B2AA", "cyan": "#00FFFF", "mintcream": "#F5FFFA", "silver": "#C0C0C0", "antiquewhite": "#FAEBD7", "mediumorchid": "#BA55D3", "skyblue": "#87CEEB", "gray": "#808080", "darkturquoise": "#00CED1", "goldenrod": "#DAA520", "darkgreen": "#006400", "floralwhite": "#FFFAF0", "darkviolet": "#9400D3", "darkgray": "#A9A9A9", "moccasin": "#FFE4B5", "saddlebrown": "#8B4513", "darkslateblue": "#483D8B", "lightskyblue": "#87CEFA", "lightpink": "#FFB6C1", "mediumvioletred": "#C71585", "r": "#FF0000", "red": "#FF0000", "deeppink": "#FF1493", "limegreen": "#32CD32", "k": "#000000", "darkmagenta": "#8B008B", "palegoldenrod": "#EEE8AA", "plum": "#DDA0DD", "turquoise": "#40E0D0", "m": "#FF00FF", "lightgoldenrodyellow": "#FAFAD2", "darkgoldenrod": "#B8860B", "lavender": "#E6E6FA", "maroon": "#800000", "yellowgreen": "#9ACD32", "sandybrown": "#FAA460", "thistle": "#D8BFD8", "violet": "#EE82EE", "navy": "#000080", "magenta": "#FF00FF", "tan": "#D2B48C", "rosybrown": "#BC8F8F", "olivedrab": "#6B8E23", "blue": "#0000FF", "lightblue": "#ADD8E6", "ghostwhite": "#F8F8FF", "honeydew": "#F0FFF0", "cornflowerblue": "#6495ED", "linen": "#FAF0E6", "darkblue": "#00008B", "powderblue": "#B0E0E6", "seagreen": "#2E8B57", "darkkhaki": "#BDB76B", "snow": "#FFFAFA", "sienna": "#A0522D", "mediumblue": "#0000CD", "royalblue": "#4169E1", "lightcyan": "#E0FFFF", "green": "#008000", "mediumpurple": "#9370DB", "midnightblue": "#191970", "cornsilk": "#FFF8DC", "paleturquoise": "#AFEEEE", "bisque": "#FFE4C4", "slategray": "#708090", "darkcyan": "#008B8B", "khaki": "#F0E68C", "wheat": "#F5DEB3", "teal": "#008080", "darkorchid": "#9932CC", "deepskyblue": "#00BFFF", "salmon": "#FA8072", "y": "#FFFF00", "darkred": "#8B0000", "steelblue": "#4682B4", "g": "#008000", "palevioletred": "#DB7093", "lightslategray": "#778899", "aliceblue": "#F0F8FF", "lightgreen": "#90EE90", "orchid": "#DA70D6", "gainsboro": "#DCDCDC", "mediumseagreen": "#3CB371", "lightgray": "#D3D3D3", "c": "#00FFFF", "mediumturquoise": "#48D1CC", "darksage": "#598556", "lemonchiffon": "#FFFACD", "cadetblue": "#5F9EA0", "lightyellow": "#FFFFE0", "lavenderblush": "#FFF0F5", "coral": "#FF7F50", "purple": "#800080", "aqua": "#00FFFF", "lightsage": "#BCECAC", "whitesmoke": "#F5F5F5", "mediumslateblue": "#7B68EE", "darkorange": "#FF8C00", "mediumaquamarine": "#66CDAA", "darksalmon": "#E9967A", "beige": "#F5F5DC", "w": "#FFFFFF", "blueviolet": "#8A2BE2", "azure": "#F0FFFF", "lightsteelblue": "#B0C4DE", "oldlace": "#FDF5E6"}
--------------------------------------------------------------------------------
/branca/utilities.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | """
3 | Utilities
4 | -------
5 |
6 | Utility module for Folium helper functions.
7 |
8 | """
9 |
10 | from __future__ import absolute_import
11 | from __future__ import print_function
12 | from __future__ import division
13 |
14 | import math
15 | import zlib
16 | import struct
17 | import json
18 | import pkg_resources
19 | import base64
20 | from jinja2 import Environment, PackageLoader
21 |
22 | try:
23 | import pandas as pd
24 | except ImportError:
25 | pd = None
26 |
27 | try:
28 | import numpy as np
29 | except ImportError:
30 | np = None
31 |
32 | from branca.six import text_type, binary_type
33 |
34 |
35 | def get_templates():
36 | """Get Jinja templates."""
37 | return Environment(loader=PackageLoader('branca', 'templates'))
38 |
39 |
40 | def legend_scaler(legend_values, max_labels=10.0):
41 | """
42 | Downsamples the number of legend values so that there isn't a collision
43 | of text on the legend colorbar (within reason). The colorbar seems to
44 | support ~10 entries as a maximum.
45 |
46 | """
47 | if len(legend_values) < max_labels:
48 | legend_ticks = legend_values
49 | else:
50 | spacer = int(math.ceil(len(legend_values)/max_labels))
51 | legend_ticks = []
52 | for i in legend_values[::spacer]:
53 | legend_ticks += [i]
54 | legend_ticks += ['']*(spacer-1)
55 | return legend_ticks
56 |
57 |
58 | def linear_gradient(hexList, nColors):
59 | """
60 | Given a list of hexcode values, will return a list of length
61 | nColors where the colors are linearly interpolated between the
62 | (r, g, b) tuples that are given.
63 |
64 | Examples
65 | --------
66 | >>> linear_gradient([(0, 0, 0), (255, 0, 0), (255, 255, 0)], 100)
67 |
68 | """
69 | def _scale(start, finish, length, i):
70 | """
71 | Return the value correct value of a number that is in between start
72 | and finish, for use in a loop of length *length*.
73 |
74 | """
75 | base = 16
76 |
77 | fraction = float(i) / (length - 1)
78 | raynge = int(finish, base) - int(start, base)
79 | thex = hex(int(int(start, base) + fraction * raynge)).split('x')[-1]
80 | if len(thex) != 2:
81 | thex = '0' + thex
82 | return thex
83 |
84 | allColors = []
85 | # Separate (R, G, B) pairs.
86 | for start, end in zip(hexList[:-1], hexList[1:]):
87 | # Linearly intepolate between pair of hex ###### values and
88 | # add to list.
89 | nInterpolate = 765
90 | for index in range(nInterpolate):
91 | r = _scale(start[1:3], end[1:3], nInterpolate, index)
92 | g = _scale(start[3:5], end[3:5], nInterpolate, index)
93 | b = _scale(start[5:7], end[5:7], nInterpolate, index)
94 | allColors.append(''.join(['#', r, g, b]))
95 |
96 | # Pick only nColors colors from the total list.
97 | result = []
98 | for counter in range(nColors):
99 | fraction = float(counter) / (nColors - 1)
100 | index = int(fraction * (len(allColors) - 1))
101 | result.append(allColors[index])
102 | return result
103 |
104 |
105 | def color_brewer(color_code, n=6):
106 | """
107 | Generate a colorbrewer color scheme of length 'len', type 'scheme.
108 | Live examples can be seen at http://colorbrewer2.org/
109 |
110 | """
111 | maximum_n = 253
112 | minimum_n = 3
113 |
114 | # Raise an error if the n requested is greater than the maximum.
115 | if n > maximum_n:
116 | raise ValueError("The maximum number of colors in a"
117 | " ColorBrewer sequential color series is 253")
118 | if n < minimum_n:
119 | raise ValueError("The minimum number of colors in a"
120 | " ColorBrewer sequential color series is 3")
121 |
122 | if color_code[-2:] == '_r':
123 | base_code = color_code[:-2]
124 | core_color_code = base_code + '_' + str(n).zfill(2)
125 | color_reverse = True
126 | else:
127 | base_code = color_code
128 | core_color_code = base_code + '_' + str(n).zfill(2)
129 | color_reverse = False
130 |
131 | resource_package = __name__
132 | resource_path_schemes = '/_schemes.json'
133 | resource_path_scheme_info = '/_cnames.json'
134 | resource_path_scheme_base_codes = '/scheme_base_codes.json'
135 |
136 | schemes_string = pkg_resources.resource_stream(
137 | resource_package, resource_path_schemes).read().decode()
138 | schemes = json.loads(schemes_string)
139 |
140 | scheme_info_string = pkg_resources.resource_stream(
141 | resource_package, resource_path_scheme_info).read().decode()
142 | scheme_info = json.loads(scheme_info_string)
143 |
144 | core_schemes_string = pkg_resources.resource_stream(
145 | resource_package, resource_path_scheme_base_codes).read().decode()
146 | core_schemes = json.loads(core_schemes_string)['codes']
147 |
148 | if base_code not in core_schemes:
149 | raise ValueError(base_code + " is not a valid ColorBrewer code")
150 |
151 | try:
152 | schemes[core_color_code]
153 | explicit_scheme = True
154 | except KeyError:
155 | explicit_scheme = False
156 |
157 | # Only if n is greater than the scheme length do we interpolate values.
158 | if not explicit_scheme:
159 | # Check to make sure that it is not a qualitative scheme.
160 | if scheme_info[base_code] == 'Qualitative':
161 | matching_quals = []
162 | for key in schemes:
163 | if base_code + '_' in key:
164 | matching_quals.append(int(key.split('_')[1]))
165 |
166 | raise ValueError("Expanded color support is not available"
167 | " for Qualitative schemes; restrict the"
168 | " number of colors for the " + base_code +
169 | " code to between " + str(min(matching_quals)) +
170 | " and " + str(max(matching_quals))
171 | )
172 | else:
173 | if not color_reverse:
174 | color_scheme = linear_gradient(schemes.get(core_color_code), n)
175 | else:
176 | color_scheme = linear_gradient(schemes.get(core_color_code)[::-1], n)
177 | else:
178 | if not color_reverse:
179 | color_scheme = schemes.get(core_color_code, None)
180 | else:
181 | color_scheme = schemes.get(core_color_code, None)[::-1]
182 | return color_scheme
183 |
184 |
185 | def split_six(series=None):
186 | """
187 | Given a Pandas Series, get a domain of values from zero to the 90% quantile
188 | rounded to the nearest order-of-magnitude integer. For example, 2100 is
189 | rounded to 2000, 2790 to 3000.
190 |
191 | Parameters
192 | ----------
193 | series: Pandas series, default None
194 |
195 | Returns
196 | -------
197 | list
198 |
199 | """
200 | if pd is None:
201 | raise ImportError("The Pandas package is required"
202 | " for this functionality")
203 | if np is None:
204 | raise ImportError("The NumPy package is required"
205 | " for this functionality")
206 |
207 | def base(x):
208 | if x > 0:
209 | base = pow(10, math.floor(math.log10(x)))
210 | return round(x/base)*base
211 | else:
212 | return 0
213 |
214 | quants = [0, 50, 75, 85, 90]
215 | # Some weirdness in series quantiles a la 0.13.
216 | arr = series.values
217 | return [base(np.percentile(arr, x)) for x in quants]
218 |
219 |
220 | def image_to_url(image, colormap=None, origin='upper'):
221 | """Infers the type of an image argument and transforms it into a URL.
222 |
223 | Parameters
224 | ----------
225 | image: string, file or array-like object
226 | * If string, it will be written directly in the output file.
227 | * If file, it's content will be converted as embedded in the
228 | output file.
229 | * If array-like, it will be converted to PNG base64 string and
230 | embedded in the output.
231 | origin : ['upper' | 'lower'], optional, default 'upper'
232 | Place the [0, 0] index of the array in the upper left or
233 | lower left corner of the axes.
234 | colormap : callable, used only for `mono` image.
235 | Function of the form [x -> (r,g,b)] or [x -> (r,g,b,a)]
236 | for transforming a mono image into RGB.
237 | It must output iterables of length 3 or 4, with values between
238 | 0. and 1. Hint : you can use colormaps from `matplotlib.cm`.
239 | """
240 | if hasattr(image, 'read'):
241 | # We got an image file.
242 | if hasattr(image, 'name'):
243 | # We try to get the image format from the file name.
244 | fileformat = image.name.lower().split('.')[-1]
245 | else:
246 | fileformat = 'png'
247 | url = "data:image/{};base64,{}".format(
248 | fileformat, base64.b64encode(image.read()).decode('utf-8'))
249 | elif (not (isinstance(image, text_type) or
250 | isinstance(image, binary_type))) and hasattr(image, '__iter__'):
251 | # We got an array-like object.
252 | png = write_png(image, origin=origin, colormap=colormap)
253 | url = "data:image/png;base64," + base64.b64encode(png).decode('utf-8')
254 | else:
255 | # We got an URL.
256 | url = json.loads(json.dumps(image))
257 |
258 | return url.replace('\n', ' ')
259 |
260 |
261 | def write_png(data, origin='upper', colormap=None):
262 | """
263 | Transform an array of data into a PNG string.
264 | This can be written to disk using binary I/O, or encoded using base64
265 | for an inline PNG like this:
266 |
267 | >>> png_str = write_png(array)
268 | >>> "data:image/png;base64,"+png_str.encode('base64')
269 |
270 | Inspired from
271 | http://stackoverflow.com/questions/902761/saving-a-numpy-array-as-an-image
272 |
273 | Parameters
274 | ----------
275 | data: numpy array or equivalent list-like object.
276 | Must be NxM (mono), NxMx3 (RGB) or NxMx4 (RGBA)
277 |
278 | origin : ['upper' | 'lower'], optional, default 'upper'
279 | Place the [0,0] index of the array in the upper left or lower left
280 | corner of the axes.
281 |
282 | colormap : callable, used only for `mono` image.
283 | Function of the form [x -> (r,g,b)] or [x -> (r,g,b,a)]
284 | for transforming a mono image into RGB.
285 | It must output iterables of length 3 or 4, with values between
286 | 0. and 1. Hint: you can use colormaps from `matplotlib.cm`.
287 |
288 | Returns
289 | -------
290 | PNG formatted byte string
291 | """
292 | if np is None:
293 | raise ImportError("The NumPy package is required"
294 | " for this functionality")
295 |
296 | if colormap is None:
297 | def colormap(x):
298 | return (x, x, x, 1)
299 |
300 | array = np.atleast_3d(data)
301 | height, width, nblayers = array.shape
302 |
303 | if nblayers not in [1, 3, 4]:
304 | raise ValueError("Data must be NxM (mono), "
305 | "NxMx3 (RGB), or NxMx4 (RGBA)")
306 | assert array.shape == (height, width, nblayers)
307 |
308 | if nblayers == 1:
309 | array = np.array(list(map(colormap, array.ravel())))
310 | nblayers = array.shape[1]
311 | if nblayers not in [3, 4]:
312 | raise ValueError("colormap must provide colors of"
313 | "length 3 (RGB) or 4 (RGBA)")
314 | array = array.reshape((height, width, nblayers))
315 | assert array.shape == (height, width, nblayers)
316 |
317 | if nblayers == 3:
318 | array = np.concatenate((array, np.ones((height, width, 1))), axis=2)
319 | nblayers = 4
320 | assert array.shape == (height, width, nblayers)
321 | assert nblayers == 4
322 |
323 | # Normalize to uint8 if it isn't already.
324 | if array.dtype != 'uint8':
325 | array = array * 255./array.max(axis=(0, 1)).reshape((1, 1, 4))
326 | array = array.astype('uint8')
327 |
328 | # Eventually flip the image.
329 | if origin == 'lower':
330 | array = array[::-1, :, :]
331 |
332 | # Transform the array to bytes.
333 | raw_data = b''.join([b'\x00' + array[i, :, :].tobytes()
334 | for i in range(height)])
335 |
336 | def png_pack(png_tag, data):
337 | chunk_head = png_tag + data
338 | return (struct.pack("!I", len(data)) +
339 | chunk_head +
340 | struct.pack("!I", 0xFFFFFFFF & zlib.crc32(chunk_head)))
341 |
342 | return b''.join([
343 | b'\x89PNG\r\n\x1a\n',
344 | png_pack(b'IHDR', struct.pack("!2I5B", width, height, 8, 6, 0, 0, 0)),
345 | png_pack(b'IDAT', zlib.compress(raw_data, 9)),
346 | png_pack(b'IEND', b'')])
347 |
348 |
349 | def _camelify(out):
350 | return (''.join(["_" + x.lower() if i < len(out)-1 and x.isupper() and out[i+1].islower() # noqa
351 | else x.lower() + "_" if i < len(out)-1 and x.islower() and out[i+1].isupper() # noqa
352 | else x.lower() for i, x in enumerate(list(out))])).lstrip('_').replace('__', '_') # noqa
353 |
354 |
355 | def _parse_size(value):
356 | try:
357 | if isinstance(value, int) or isinstance(value, float):
358 | value_type = 'px'
359 | value = float(value)
360 | assert value > 0
361 | else:
362 | value_type = '%'
363 | value = float(value.strip('%'))
364 | assert 0 <= value <= 100
365 | except:
366 | msg = "Cannot parse value {!r} as {!r}".format
367 | raise ValueError(msg(value, value_type))
368 | return value, value_type
369 |
370 |
371 | def _locations_mirror(x):
372 | """Mirrors the points in a list-of-list-of-...-of-list-of-points.
373 | For example:
374 | >>> _locations_mirror([[[1, 2], [3, 4]], [5, 6], [7, 8]])
375 | [[[2, 1], [4, 3]], [6, 5], [8, 7]]
376 |
377 | """
378 | if hasattr(x, '__iter__'):
379 | if hasattr(x[0], '__iter__'):
380 | return list(map(_locations_mirror, x))
381 | else:
382 | return list(x[::-1])
383 | else:
384 | return x
385 |
386 |
387 | def _locations_tolist(x):
388 | """Transforms recursively a list of iterables into a list of list.
389 | """
390 | if hasattr(x, '__iter__'):
391 | return list(map(_locations_tolist, x))
392 | else:
393 | return x
394 |
395 |
396 | def none_min(x, y):
397 | if x is None:
398 | return y
399 | elif y is None:
400 | return x
401 | else:
402 | return min(x, y)
403 |
404 |
405 | def none_max(x, y):
406 | if x is None:
407 | return y
408 | elif y is None:
409 | return x
410 | else:
411 | return max(x, y)
412 |
413 |
414 | def iter_points(x):
415 | """Iterates over a list representing a feature, and returns a list of points,
416 | whatever the shape of the array (Point, MultiPolyline, etc).
417 | """
418 | if isinstance(x, (list, tuple)):
419 | if len(x):
420 | if isinstance(x[0], (list, tuple)):
421 | out = []
422 | for y in x:
423 | out += iter_points(y)
424 | return out
425 | else:
426 | return [x]
427 | else:
428 | return []
429 | else:
430 | raise ValueError('List/tuple type expected. Got {!r}.'.format(x))
431 |
--------------------------------------------------------------------------------
/branca/colormap.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | """
3 | Colormap
4 | -------
5 |
6 | Utility module for dealing with colormaps.
7 | """
8 |
9 | from __future__ import absolute_import
10 |
11 | import math
12 | import json
13 | import pkg_resources
14 | from jinja2 import Template
15 | from branca.six import text_type, binary_type
16 | from branca.element import MacroElement, Figure, JavascriptLink
17 | from branca.utilities import legend_scaler
18 |
19 |
20 | resource_package = __name__
21 | resource_path_schemes = '/_schemes.json'
22 | resource_path_cnames = '/_cnames.json'
23 |
24 | cnames_string = pkg_resources.resource_stream(
25 | resource_package, resource_path_cnames).read().decode()
26 | _cnames = json.loads(cnames_string)
27 |
28 | schemes_string = pkg_resources.resource_stream(
29 | resource_package, resource_path_schemes).read().decode()
30 | _schemes = json.loads(schemes_string)
31 |
32 |
33 | def _parse_color(x):
34 | """
35 | """
36 | if isinstance(x, (tuple, list)):
37 | color_tuple = tuple(x)[:4]
38 | elif isinstance(x, (text_type, binary_type)):
39 | if x.startswith('#') and len(x) == 7:
40 | # Color of the form #RRGGBB
41 | color_tuple = (int(x[1:3], 16),
42 | int(x[3:5], 16),
43 | int(x[5:7], 16))
44 | else:
45 | color_code = _cnames.get(x.lower(), None)
46 | if color_code is None:
47 | raise ValueError('Unknown color {!r}.'.format(x))
48 | color_tuple = (int(color_code[1:3], 16),
49 | int(color_code[3:5], 16),
50 | int(color_code[5:7], 16))
51 | if max(color_tuple) > 1.:
52 | color_tuple = tuple(u/255. for u in color_tuple)
53 | return tuple(map(float, (color_tuple+(1.,))[:4]))
54 |
55 |
56 | def _base(x):
57 | if x > 0:
58 | base = pow(10, math.floor(math.log10(x)))
59 | return round(x/base)*base
60 | else:
61 | return 0
62 |
63 |
64 | class ColorMap(MacroElement):
65 | """A generic class for creating colormaps.
66 |
67 | Parameters
68 | ----------
69 | vmin: float
70 | The left bound of the color scale.
71 | vmax: float
72 | The right bound of the color scale.
73 | caption: str
74 | A caption to draw with the colormap.
75 | """
76 | def __init__(self, vmin=0., vmax=1., caption=""):
77 | super(ColorMap, self).__init__()
78 | self._name = 'ColorMap'
79 |
80 | self.vmin = vmin
81 | self.vmax = vmax
82 | self.caption = caption
83 | self.index = [vmin, vmax]
84 |
85 | self._template = self._env.get_template('color_scale.js')
86 |
87 | def render(self, **kwargs):
88 | """Renders the HTML representation of the element."""
89 | self.color_domain = [self.vmin + (self.vmax-self.vmin) * k/499. for
90 | k in range(500)]
91 | self.color_range = [self.__call__(x) for x in self.color_domain]
92 | self.tick_labels = legend_scaler(self.index)
93 |
94 | super(ColorMap, self).render(**kwargs)
95 |
96 | figure = self.get_root()
97 | assert isinstance(figure, Figure), ("You cannot render this Element "
98 | "if it's not in a Figure.")
99 |
100 | figure.header.add_child(JavascriptLink("https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.5/d3.min.js"), name='d3') # noqa
101 |
102 | def rgba_floats_tuple(self, x):
103 | """
104 | This class has to be implemented for each class inheriting from
105 | Colormap. This has to be a function of the form float ->
106 | (float, float, float, float) describing for each input float x,
107 | the output color in RGBA format;
108 | Each output value being between 0 and 1.
109 | """
110 | raise NotImplementedError
111 |
112 | def rgba_bytes_tuple(self, x):
113 | """Provides the color corresponding to value `x` in the
114 | form of a tuple (R,G,B,A) with int values between 0 and 255.
115 | """
116 | return tuple(int(u*255.9999) for u in self.rgba_floats_tuple(x))
117 |
118 | def rgb_bytes_tuple(self, x):
119 | """Provides the color corresponding to value `x` in the
120 | form of a tuple (R,G,B) with int values between 0 and 255.
121 | """
122 | return self.rgba_bytes_tuple(x)[:3]
123 |
124 | def rgb_hex_str(self, x):
125 | """Provides the color corresponding to value `x` in the
126 | form of a string of hewadecimal values "#RRGGBB".
127 | """
128 | return "#%02x%02x%02x" % self.rgb_bytes_tuple(x)
129 |
130 | def __call__(self, x):
131 | """Provides the color corresponding to value `x` in the
132 | form of a string of hewadecimal values "#RRGGBB".
133 | """
134 | return self.rgb_hex_str(x)
135 |
136 | def _repr_html_(self):
137 | return (
138 | '' +
139 | ''.join(
140 | [(' ').format(
142 | i=i*1,
143 | color=self.rgb_hex_str(self.vmin +
144 | (self.vmax-self.vmin)*i/499.))
145 | for i in range(500)]) +
146 | '{} '.format(self.vmin) +
147 | '{} '.format(
148 | self.vmax) +
149 | ' ')
150 |
151 |
152 | class LinearColormap(ColorMap):
153 | """Creates a ColorMap based on linear interpolation of a set of colors
154 | over a given index.
155 |
156 | Parameters
157 | ----------
158 |
159 | colors : list-like object
160 | The set of colors to be used for interpolation.
161 | Colors can be provided in the form:
162 | * tuples of int between 0 and 255 (e.g: `(255,255,0)` or
163 | `(255, 255, 0, 255)`)
164 | * tuples of floats between 0. and 1. (e.g: `(1.,1.,0.)` or
165 | `(1., 1., 0., 1.)`)
166 | * HTML-like string (e.g: `"#ffff00`)
167 | * a color name or shortcut (e.g: `"y"` or `"yellow"`)
168 | index : list of floats, default None
169 | The values corresponding to each color.
170 | It has to be sorted, and have the same length as `colors`.
171 | If None, a regular grid between `vmin` and `vmax` is created.
172 | vmin : float, default 0.
173 | The minimal value for the colormap.
174 | Values lower than `vmin` will be bound directly to `colors[0]`.
175 | vmax : float, default 1.
176 | The maximal value for the colormap.
177 | Values higher than `vmax` will be bound directly to `colors[-1]`."""
178 |
179 | def __init__(self, colors, index=None, vmin=0., vmax=1., caption=""):
180 | super(LinearColormap, self).__init__(vmin=vmin, vmax=vmax,
181 | caption=caption)
182 |
183 | n = len(colors)
184 | if n < 2:
185 | raise ValueError('You must provide at least 2 colors.')
186 | if index is None:
187 | self.index = [vmin + (vmax-vmin)*i*1./(n-1) for i in range(n)]
188 | else:
189 | self.index = [x for x in index]
190 | self.colors = [_parse_color(x) for x in colors]
191 |
192 | def rgba_floats_tuple(self, x):
193 | """Provides the color corresponding to value `x` in the
194 | form of a tuple (R,G,B,A) with float values between 0. and 1.
195 | """
196 | if x <= self.index[0]:
197 | return self.colors[0]
198 | if x >= self.index[-1]:
199 | return self.colors[-1]
200 |
201 | i = len([u for u in self.index if u < x]) # 0 < i < n.
202 | if self.index[i-1] < self.index[i]:
203 | p = (x - self.index[i-1])*1./(self.index[i]-self.index[i-1])
204 | elif self.index[i-1] == self.index[i]:
205 | p = 1.
206 | else:
207 | raise ValueError('Thresholds are not sorted.')
208 |
209 | return tuple((1.-p) * self.colors[i-1][j] + p*self.colors[i][j] for j
210 | in range(4))
211 |
212 | def to_step(self, n=None, index=None, data=None, method=None,
213 | quantiles=None, round_method=None):
214 | """Splits the LinearColormap into a StepColormap.
215 |
216 | Parameters
217 | ----------
218 | n : int, default None
219 | The number of expected colors in the ouput StepColormap.
220 | This will be ignored if `index` is provided.
221 | index : list of floats, default None
222 | The values corresponding to each color bounds.
223 | It has to be sorted.
224 | If None, a regular grid between `vmin` and `vmax` is created.
225 | data : list of floats, default None
226 | A sample of data to adapt the color map to.
227 | method : str, default 'linear'
228 | The method used to create data-based colormap.
229 | It can be 'linear' for linear scale, 'log' for logarithmic,
230 | or 'quant' for data's quantile-based scale.
231 | quantiles : list of floats, default None
232 | Alternatively, you can provide explicitely the quantiles you
233 | want to use in the scale.
234 | round_method : str, default None
235 | The method used to round thresholds.
236 | * If 'int', all values will be rounded to the nearest integer.
237 | * If 'log10', all values will be rounded to the nearest
238 | order-of-magnitude integer. For example, 2100 is rounded to
239 | 2000, 2790 to 3000.
240 |
241 | Returns
242 | -------
243 | A StepColormap with `n=len(index)-1` colors.
244 |
245 | Examples:
246 | >> lc.to_step(n=12)
247 | >> lc.to_step(index=[0, 2, 4, 6, 8, 10])
248 | >> lc.to_step(data=some_list, n=12)
249 | >> lc.to_step(data=some_list, n=12, method='linear')
250 | >> lc.to_step(data=some_list, n=12, method='log')
251 | >> lc.to_step(data=some_list, n=12, method='quantiles')
252 | >> lc.to_step(data=some_list, quantiles=[0, 0.3, 0.7, 1])
253 | >> lc.to_step(data=some_list, quantiles=[0, 0.3, 0.7, 1],
254 | ... round_method='log10')
255 |
256 | """
257 | msg = 'You must specify either `index` or `n`'
258 | if index is None:
259 | if data is None:
260 | if n is None:
261 | raise ValueError(msg)
262 | else:
263 | index = [self.vmin + (self.vmax-self.vmin)*i*1./n for
264 | i in range(1+n)]
265 | scaled_cm = self
266 | else:
267 | max_ = max(data)
268 | min_ = min(data)
269 | scaled_cm = self.scale(vmin=min_, vmax=max_)
270 | method = ('quantiles' if quantiles is not None
271 | else method if method is not None
272 | else 'linear'
273 | )
274 | if method.lower().startswith('lin'):
275 | if n is None:
276 | raise ValueError(msg)
277 | index = [min_ + i*(max_-min_)*1./n for i in range(1+n)]
278 | elif method.lower().startswith('log'):
279 | if n is None:
280 | raise ValueError(msg)
281 | if min_ <= 0:
282 | msg = ('Log-scale works only with strictly '
283 | 'positive values.')
284 | raise ValueError(msg)
285 | index = [math.exp(
286 | math.log(min_) + i * (math.log(max_) -
287 | math.log(min_)) * 1./n
288 | ) for i in range(1+n)]
289 | elif method.lower().startswith('quant'):
290 | if quantiles is None:
291 | if n is None:
292 | msg = ('You must specify either `index`, `n` or'
293 | '`quantiles`.')
294 | raise ValueError(msg)
295 | else:
296 | quantiles = [i*1./n for i in range(1+n)]
297 | p = len(data)-1
298 | s = sorted(data)
299 | index = [s[int(q*p)] * (1.-(q*p) % 1) +
300 | s[min(int(q*p) + 1, p)] * ((q*p) % 1) for
301 | q in quantiles]
302 | else:
303 | raise ValueError('Unknown method {}'.format(method))
304 | else:
305 | scaled_cm = self.scale(vmin=min(index), vmax=max(index))
306 |
307 | n = len(index)-1
308 |
309 | if round_method == 'int':
310 | index = [round(x) for x in index]
311 |
312 | if round_method == 'log10':
313 | index = [_base(x) for x in index]
314 |
315 | colors = [scaled_cm.rgba_floats_tuple(index[i] * (1.-i/(n-1.)) +
316 | index[i+1] * i/(n-1.)) for
317 | i in range(n)]
318 |
319 | return StepColormap(colors, index=index, vmin=index[0], vmax=index[-1])
320 |
321 | def scale(self, vmin=0., vmax=1.):
322 | """Transforms the colorscale so that the minimal and maximal values
323 | fit the given parameters.
324 | """
325 | return LinearColormap(
326 | self.colors,
327 | index=[vmin + (vmax-vmin)*(x-self.vmin)*1./(self.vmax-self.vmin) for x in self.index], # noqa
328 | vmin=vmin,
329 | vmax=vmax,
330 | )
331 |
332 |
333 | class StepColormap(ColorMap):
334 | """Creates a ColorMap based on linear interpolation of a set of colors
335 | over a given index.
336 |
337 | Parameters
338 | ----------
339 | colors : list-like object
340 | The set of colors to be used for interpolation.
341 | Colors can be provided in the form:
342 | * tuples of int between 0 and 255 (e.g: `(255,255,0)` or
343 | `(255, 255, 0, 255)`)
344 | * tuples of floats between 0. and 1. (e.g: `(1.,1.,0.)` or
345 | `(1., 1., 0., 1.)`)
346 | * HTML-like string (e.g: `"#ffff00`)
347 | * a color name or shortcut (e.g: `"y"` or `"yellow"`)
348 | index : list of floats, default None
349 | The values corresponding to each color.
350 | It has to be sorted, and have the same length as `colors`.
351 | If None, a regular grid between `vmin` and `vmax` is created.
352 | vmin : float, default 0.
353 | The minimal value for the colormap.
354 | Values lower than `vmin` will be bound directly to `colors[0]`.
355 | vmax : float, default 1.
356 | The maximal value for the colormap.
357 | Values higher than `vmax` will be bound directly to `colors[-1]`.
358 |
359 | """
360 | def __init__(self, colors, index=None, vmin=0., vmax=1., caption=""):
361 | super(StepColormap, self).__init__(vmin=vmin, vmax=vmax,
362 | caption=caption)
363 |
364 | n = len(colors)
365 | if n < 1:
366 | raise ValueError('You must provide at least 1 colors.')
367 | if index is None:
368 | self.index = [vmin + (vmax-vmin)*i*1./n for i in range(n+1)]
369 | else:
370 | self.index = [x for x in index]
371 | self.colors = [_parse_color(x) for x in colors]
372 |
373 | def rgba_floats_tuple(self, x):
374 | """
375 | Provides the color corresponding to value `x` in the
376 | form of a tuple (R,G,B,A) with float values between 0. and 1.
377 |
378 | """
379 | if x <= self.index[0]:
380 | return self.colors[0]
381 | if x >= self.index[-1]:
382 | return self.colors[-1]
383 |
384 | i = len([u for u in self.index if u < x]) # 0 < i < n.
385 | return tuple(self.colors[i-1])
386 |
387 | def to_linear(self, index=None):
388 | """
389 | Transforms the StepColormap into a LinearColormap.
390 |
391 | Parameters
392 | ----------
393 | index : list of floats, default None
394 | The values corresponding to each color in the output colormap.
395 | It has to be sorted.
396 | If None, a regular grid between `vmin` and `vmax` is created.
397 |
398 | """
399 | if index is None:
400 | n = len(self.index)-1
401 | index = [self.index[i]*(1.-i/(n-1.))+self.index[i+1]*i/(n-1.) for
402 | i in range(n)]
403 |
404 | colors = [self.rgba_floats_tuple(x) for x in index]
405 | return LinearColormap(colors, index=index,
406 | vmin=self.vmin, vmax=self.vmax)
407 |
408 | def scale(self, vmin=0., vmax=1.):
409 | """Transforms the colorscale so that the minimal and maximal values
410 | fit the given parameters.
411 | """
412 | return StepColormap(
413 | self.colors,
414 | index=[vmin + (vmax-vmin)*(x-self.vmin)*1./(self.vmax-self.vmin) for x in self.index], # noqa
415 | vmin=vmin,
416 | vmax=vmax,
417 | )
418 |
419 |
420 | class _LinearColormaps(object):
421 | """A class for hosting the list of built-in linear colormaps."""
422 | def __init__(self):
423 | self._schemes = _schemes.copy()
424 | self._colormaps = {key: LinearColormap(val) for
425 | key, val in _schemes.items()}
426 | for key, val in _schemes.items():
427 | setattr(self, key, LinearColormap(val))
428 |
429 | def _repr_html_(self):
430 | return Template("""
431 |
432 | {% for key,val in this._colormaps.items() %}
433 | {{key}} {{val._repr_html_()}}
434 | {% endfor %}
435 | """).render(this=self)
436 |
437 |
438 | linear = _LinearColormaps()
439 |
440 |
441 | class _StepColormaps(object):
442 | """A class for hosting the list of built-in step colormaps."""
443 | def __init__(self):
444 | self._schemes = _schemes.copy()
445 | self._colormaps = {key: StepColormap(val) for
446 | key, val in _schemes.items()}
447 | for key, val in _schemes.items():
448 | setattr(self, key, StepColormap(val))
449 |
450 | def _repr_html_(self):
451 | return Template("""
452 |
453 | {% for key,val in this._colormaps.items() %}
454 | {{key}} {{val._repr_html_()}}
455 | {% endfor %}
456 | """).render(this=self)
457 |
458 |
459 | step = _StepColormaps()
460 |
--------------------------------------------------------------------------------
/branca/_schemes.json:
--------------------------------------------------------------------------------
1 | {"Pastel1_03": ["#fbb4ae", "#b3cde3", "#ccebc5"], "Pastel1_05": ["#fbb4ae", "#b3cde3", "#ccebc5", "#decbe4", "#fed9a6"], "Pastel1_04": ["#fbb4ae", "#b3cde3", "#ccebc5", "#decbe4"], "Pastel1_07": ["#fbb4ae", "#b3cde3", "#ccebc5", "#decbe4", "#fed9a6", "#ffffcc", "#e5d8bd"], "YlOrRd_04": ["#ffffb2", "#fecc5c", "#fd8d3c", "#e31a1c"], "Pastel1_09": ["#fbb4ae", "#b3cde3", "#ccebc5", "#decbe4", "#fed9a6", "#ffffcc", "#e5d8bd", "#fddaec", "#f2f2f2"], "Pastel1_08": ["#fbb4ae", "#b3cde3", "#ccebc5", "#decbe4", "#fed9a6", "#ffffcc", "#e5d8bd", "#fddaec"], "Spectral_07": ["#d53e4f", "#fc8d59", "#fee08b", "#ffffbf", "#e6f598", "#99d594", "#3288bd"], "RdYlBu_05": ["#d7191c", "#fdae61", "#ffffbf", "#abd9e9", "#2c7bb6"], "PuBuGn_03": ["#ece2f0", "#a6bddb", "#1c9099"], "Set1_08": ["#e41a1c", "#377eb8", "#4daf4a", "#984ea3", "#ff7f00", "#ffff33", "#a65628", "#f781bf"], "PuBuGn_05": ["#f6eff7", "#bdc9e1", "#67a9cf", "#1c9099", "#016c59"], "PuBuGn_04": ["#f6eff7", "#bdc9e1", "#67a9cf", "#02818a"], "PuBuGn_07": ["#f6eff7", "#d0d1e6", "#a6bddb", "#67a9cf", "#3690c0", "#02818a", "#016450"], "PuBuGn_06": ["#f6eff7", "#d0d1e6", "#a6bddb", "#67a9cf", "#1c9099", "#016c59"], "PuBuGn_09": ["#fff7fb", "#ece2f0", "#d0d1e6", "#a6bddb", "#67a9cf", "#3690c0", "#02818a", "#016c59", "#014636"], "PuBuGn_08": ["#fff7fb", "#ece2f0", "#d0d1e6", "#a6bddb", "#67a9cf", "#3690c0", "#02818a", "#016450"], "YlOrBr_04": ["#ffffd4", "#fed98e", "#fe9929", "#cc4c02"], "YlOrBr_05": ["#ffffd4", "#fed98e", "#fe9929", "#d95f0e", "#993404"], "Set1_07": ["#e41a1c", "#377eb8", "#4daf4a", "#984ea3", "#ff7f00", "#ffff33", "#a65628"], "YlOrBr_03": ["#fff7bc", "#fec44f", "#d95f0e"], "Set1_05": ["#e41a1c", "#377eb8", "#4daf4a", "#984ea3", "#ff7f00"], "YlOrRd_03": ["#ffeda0", "#feb24c", "#f03b20"], "PuOr_06": ["#b35806", "#f1a340", "#fee0b6", "#d8daeb", "#998ec3", "#542788"], "PuOr_07": ["#b35806", "#f1a340", "#fee0b6", "#f7f7f7", "#d8daeb", "#998ec3", "#542788"], "PuOr_04": ["#e66101", "#fdb863", "#b2abd2", "#5e3c99"], "PuOr_05": ["#e66101", "#fdb863", "#f7f7f7", "#b2abd2", "#5e3c99"], "PuOr_03": ["#f1a340", "#f7f7f7", "#998ec3"], "Purples_09": ["#fcfbfd", "#efedf5", "#dadaeb", "#bcbddc", "#9e9ac8", "#807dba", "#6a51a3", "#54278f", "#3f007d"], "Set2_06": ["#66c2a5", "#fc8d62", "#8da0cb", "#e78ac3", "#a6d854", "#ffd92f"], "RdYlBu_11": ["#a50026", "#d73027", "#f46d43", "#fdae61", "#fee090", "#ffffbf", "#e0f3f8", "#abd9e9", "#74add1", "#4575b4", "#313695"], "PuOr_08": ["#b35806", "#e08214", "#fdb863", "#fee0b6", "#d8daeb", "#b2abd2", "#8073ac", "#542788"], "PuOr_09": ["#b35806", "#e08214", "#fdb863", "#fee0b6", "#f7f7f7", "#d8daeb", "#b2abd2", "#8073ac", "#542788"], "Paired_03": ["#a6cee3", "#1f78b4", "#b2df8a"], "RdBu_03": ["#ef8a62", "#f7f7f7", "#67a9cf"], "RdYlBu_10": ["#a50026", "#d73027", "#f46d43", "#fdae61", "#fee090", "#e0f3f8", "#abd9e9", "#74add1", "#4575b4", "#313695"], "Paired_07": ["#a6cee3", "#1f78b4", "#b2df8a", "#33a02c", "#fb9a99", "#e31a1c", "#fdbf6f"], "Paired_06": ["#a6cee3", "#1f78b4", "#b2df8a", "#33a02c", "#fb9a99", "#e31a1c"], "Paired_05": ["#a6cee3", "#1f78b4", "#b2df8a", "#33a02c", "#fb9a99"], "Paired_04": ["#a6cee3", "#1f78b4", "#b2df8a", "#33a02c"], "Paired_09": ["#a6cee3", "#1f78b4", "#b2df8a", "#33a02c", "#fb9a99", "#e31a1c", "#fdbf6f", "#ff7f00", "#cab2d6"], "Paired_08": ["#a6cee3", "#1f78b4", "#b2df8a", "#33a02c", "#fb9a99", "#e31a1c", "#fdbf6f", "#ff7f00"], "RdGy_03": ["#ef8a62", "#ffffff", "#999999"], "PiYG_04": ["#d01c8b", "#f1b6da", "#b8e186", "#4dac26"], "Accent_03": ["#7fc97f", "#beaed4", "#fdc086"], "BuGn_08": ["#f7fcfd", "#e5f5f9", "#ccece6", "#99d8c9", "#66c2a4", "#41ae76", "#238b45", "#005824"], "BuGn_09": ["#f7fcfd", "#e5f5f9", "#ccece6", "#99d8c9", "#66c2a4", "#41ae76", "#238b45", "#006d2c", "#00441b"], "BuGn_04": ["#edf8fb", "#b2e2e2", "#66c2a4", "#238b45"], "BuGn_05": ["#edf8fb", "#b2e2e2", "#66c2a4", "#2ca25f", "#006d2c"], "BuGn_06": ["#edf8fb", "#ccece6", "#99d8c9", "#66c2a4", "#2ca25f", "#006d2c"], "BuGn_07": ["#edf8fb", "#ccece6", "#99d8c9", "#66c2a4", "#41ae76", "#238b45", "#005824"], "BuGn_03": ["#e5f5f9", "#99d8c9", "#2ca25f"], "YlGnBu_07": ["#ffffcc", "#c7e9b4", "#7fcdbb", "#41b6c4", "#1d91c0", "#225ea8", "#0c2c84"], "YlGnBu_06": ["#ffffcc", "#c7e9b4", "#7fcdbb", "#41b6c4", "#2c7fb8", "#253494"], "YlGnBu_05": ["#ffffcc", "#a1dab4", "#41b6c4", "#2c7fb8", "#253494"], "YlGnBu_04": ["#ffffcc", "#a1dab4", "#41b6c4", "#225ea8"], "YlGnBu_03": ["#edf8b1", "#7fcdbb", "#2c7fb8"], "RdBu_06": ["#b2182b", "#ef8a62", "#fddbc7", "#d1e5f0", "#67a9cf", "#2166ac"], "RdBu_05": ["#ca0020", "#f4a582", "#f7f7f7", "#92c5de", "#0571b0"], "RdBu_04": ["#ca0020", "#f4a582", "#92c5de", "#0571b0"], "Accent_08": ["#7fc97f", "#beaed4", "#fdc086", "#ffff99", "#386cb0", "#f0027f", "#bf5b17", "#666666"], "RdBu_09": ["#b2182b", "#d6604d", "#f4a582", "#fddbc7", "#f7f7f7", "#d1e5f0", "#92c5de", "#4393c3", "#2166ac"], "RdBu_08": ["#b2182b", "#d6604d", "#f4a582", "#fddbc7", "#d1e5f0", "#92c5de", "#4393c3", "#2166ac"], "Set2_04": ["#66c2a5", "#fc8d62", "#8da0cb", "#e78ac3"], "YlGnBu_09": ["#ffffd9", "#edf8b1", "#c7e9b4", "#7fcdbb", "#41b6c4", "#1d91c0", "#225ea8", "#253494", "#081d58"], "YlGnBu_08": ["#ffffd9", "#edf8b1", "#c7e9b4", "#7fcdbb", "#41b6c4", "#1d91c0", "#225ea8", "#0c2c84"], "Blues_08": ["#f7fbff", "#deebf7", "#c6dbef", "#9ecae1", "#6baed6", "#4292c6", "#2171b5", "#084594"], "Blues_09": ["#f7fbff", "#deebf7", "#c6dbef", "#9ecae1", "#6baed6", "#4292c6", "#2171b5", "#08519c", "#08306b"], "RdPu_09": ["#fff7f3", "#fde0dd", "#fcc5c0", "#fa9fb5", "#f768a1", "#dd3497", "#ae017e", "#7a0177", "#49006a"], "RdPu_08": ["#fff7f3", "#fde0dd", "#fcc5c0", "#fa9fb5", "#f768a1", "#dd3497", "#ae017e", "#7a0177"], "Set3_07": ["#8dd3c7", "#ffffb3", "#bebada", "#fb8072", "#80b1d3", "#fdb462", "#b3de69"], "Set3_06": ["#8dd3c7", "#ffffb3", "#bebada", "#fb8072", "#80b1d3", "#fdb462"], "RdPu_05": ["#feebe2", "#fbb4b9", "#f768a1", "#c51b8a", "#7a0177"], "RdPu_04": ["#feebe2", "#fbb4b9", "#f768a1", "#ae017e"], "RdPu_07": ["#feebe2", "#fcc5c0", "#fa9fb5", "#f768a1", "#dd3497", "#ae017e", "#7a0177"], "RdPu_06": ["#feebe2", "#fcc5c0", "#fa9fb5", "#f768a1", "#c51b8a", "#7a0177"], "Blues_06": ["#eff3ff", "#c6dbef", "#9ecae1", "#6baed6", "#3182bd", "#08519c"], "Blues_07": ["#eff3ff", "#c6dbef", "#9ecae1", "#6baed6", "#4292c6", "#2171b5", "#084594"], "RdPu_03": ["#fde0dd", "#fa9fb5", "#c51b8a"], "Blues_05": ["#eff3ff", "#bdd7e7", "#6baed6", "#3182bd", "#08519c"], "Paired_10": ["#a6cee3", "#1f78b4", "#b2df8a", "#33a02c", "#fb9a99", "#e31a1c", "#fdbf6f", "#ff7f00", "#cab2d6", "#6a3d9a"], "Paired_11": ["#a6cee3", "#1f78b4", "#b2df8a", "#33a02c", "#fb9a99", "#e31a1c", "#fdbf6f", "#ff7f00", "#cab2d6", "#6a3d9a", "#ffff99"], "Paired_12": ["#a6cee3", "#1f78b4", "#b2df8a", "#33a02c", "#fb9a99", "#e31a1c", "#fdbf6f", "#ff7f00", "#cab2d6", "#6a3d9a", "#ffff99", "#b15928"], "PuBu_06": ["#f1eef6", "#d0d1e6", "#a6bddb", "#74a9cf", "#2b8cbe", "#045a8d"], "PuBu_07": ["#f1eef6", "#d0d1e6", "#a6bddb", "#74a9cf", "#3690c0", "#0570b0", "#034e7b"], "PuBu_04": ["#f1eef6", "#bdc9e1", "#74a9cf", "#0570b0"], "PuBu_05": ["#f1eef6", "#bdc9e1", "#74a9cf", "#2b8cbe", "#045a8d"], "PuRd_05": ["#f1eef6", "#d7b5d8", "#df65b0", "#dd1c77", "#980043"], "PuBu_03": ["#ece7f2", "#a6bddb", "#2b8cbe"], "PuRd_07": ["#f1eef6", "#d4b9da", "#c994c7", "#df65b0", "#e7298a", "#ce1256", "#91003f"], "PuRd_06": ["#f1eef6", "#d4b9da", "#c994c7", "#df65b0", "#dd1c77", "#980043"], "PuRd_09": ["#f7f4f9", "#e7e1ef", "#d4b9da", "#c994c7", "#df65b0", "#e7298a", "#ce1256", "#980043", "#67001f"], "PuRd_08": ["#f7f4f9", "#e7e1ef", "#d4b9da", "#c994c7", "#df65b0", "#e7298a", "#ce1256", "#91003f"], "Set2_07": ["#66c2a5", "#fc8d62", "#8da0cb", "#e78ac3", "#a6d854", "#ffd92f", "#e5c494"], "PuBu_08": ["#fff7fb", "#ece7f2", "#d0d1e6", "#a6bddb", "#74a9cf", "#3690c0", "#0570b0", "#034e7b"], "PuBu_09": ["#fff7fb", "#ece7f2", "#d0d1e6", "#a6bddb", "#74a9cf", "#3690c0", "#0570b0", "#045a8d", "#023858"], "RdBu_10": ["#67001f", "#b2182b", "#d6604d", "#f4a582", "#fddbc7", "#d1e5f0", "#92c5de", "#4393c3", "#2166ac", "#053061"], "RdBu_11": ["#67001f", "#b2182b", "#d6604d", "#f4a582", "#fddbc7", "#f7f7f7", "#d1e5f0", "#92c5de", "#4393c3", "#2166ac", "#053061"], "Accent_06": ["#7fc97f", "#beaed4", "#fdc086", "#ffff99", "#386cb0", "#f0027f"], "Set3_03": ["#8dd3c7", "#ffffb3", "#bebada"], "Set3_05": ["#8dd3c7", "#ffffb3", "#bebada", "#fb8072", "#80b1d3"], "Set3_12": ["#8dd3c7", "#ffffb3", "#bebada", "#fb8072", "#80b1d3", "#fdb462", "#b3de69", "#fccde5", "#d9d9d9", "#bc80bd", "#ccebc5", "#ffed6f"], "Set3_10": ["#8dd3c7", "#ffffb3", "#bebada", "#fb8072", "#80b1d3", "#fdb462", "#b3de69", "#fccde5", "#d9d9d9", "#bc80bd"], "Set3_04": ["#8dd3c7", "#ffffb3", "#bebada", "#fb8072"], "RdGy_11": ["#67001f", "#b2182b", "#d6604d", "#f4a582", "#fddbc7", "#ffffff", "#e0e0e0", "#bababa", "#878787", "#4d4d4d", "#1a1a1a"], "RdGy_10": ["#67001f", "#b2182b", "#d6604d", "#f4a582", "#fddbc7", "#e0e0e0", "#bababa", "#878787", "#4d4d4d", "#1a1a1a"], "Set1_03": ["#e41a1c", "#377eb8", "#4daf4a"], "Set1_09": ["#e41a1c", "#377eb8", "#4daf4a", "#984ea3", "#ff7f00", "#ffff33", "#a65628", "#f781bf", "#999999"], "Set3_09": ["#8dd3c7", "#ffffb3", "#bebada", "#fb8072", "#80b1d3", "#fdb462", "#b3de69", "#fccde5", "#d9d9d9"], "BuPu_08": ["#f7fcfd", "#e0ecf4", "#bfd3e6", "#9ebcda", "#8c96c6", "#8c6bb1", "#88419d", "#6e016b"], "BuPu_09": ["#f7fcfd", "#e0ecf4", "#bfd3e6", "#9ebcda", "#8c96c6", "#8c6bb1", "#88419d", "#810f7c", "#4d004b"], "RdYlGn_11": ["#a50026", "#d73027", "#f46d43", "#fdae61", "#fee08b", "#ffffbf", "#d9ef8b", "#a6d96a", "#66bd63", "#1a9850", "#006837"], "Blues_03": ["#deebf7", "#9ecae1", "#3182bd"], "Set2_05": ["#66c2a5", "#fc8d62", "#8da0cb", "#e78ac3", "#a6d854"], "BuPu_03": ["#e0ecf4", "#9ebcda", "#8856a7"], "BuPu_06": ["#edf8fb", "#bfd3e6", "#9ebcda", "#8c96c6", "#8856a7", "#810f7c"], "BuPu_07": ["#edf8fb", "#bfd3e6", "#9ebcda", "#8c96c6", "#8c6bb1", "#88419d", "#6e016b"], "BuPu_04": ["#edf8fb", "#b3cde3", "#8c96c6", "#88419d"], "BuPu_05": ["#edf8fb", "#b3cde3", "#8c96c6", "#8856a7", "#810f7c"], "Accent_04": ["#7fc97f", "#beaed4", "#fdc086", "#ffff99"], "YlOrRd_05": ["#ffffb2", "#fecc5c", "#fd8d3c", "#f03b20", "#bd0026"], "YlOrBr_08": ["#ffffe5", "#fff7bc", "#fee391", "#fec44f", "#fe9929", "#ec7014", "#cc4c02", "#8c2d04"], "Oranges_08": ["#fff5eb", "#fee6ce", "#fdd0a2", "#fdae6b", "#fd8d3c", "#f16913", "#d94801", "#8c2d04"], "Oranges_09": ["#fff5eb", "#fee6ce", "#fdd0a2", "#fdae6b", "#fd8d3c", "#f16913", "#d94801", "#a63603", "#7f2704"], "Oranges_06": ["#feedde", "#fdd0a2", "#fdae6b", "#fd8d3c", "#e6550d", "#a63603"], "Oranges_07": ["#feedde", "#fdd0a2", "#fdae6b", "#fd8d3c", "#f16913", "#d94801", "#8c2d04"], "Oranges_04": ["#feedde", "#fdbe85", "#fd8d3c", "#d94701"], "YlOrBr_09": ["#ffffe5", "#fff7bc", "#fee391", "#fec44f", "#fe9929", "#ec7014", "#cc4c02", "#993404", "#662506"], "Oranges_03": ["#fee6ce", "#fdae6b", "#e6550d"], "YlOrBr_06": ["#ffffd4", "#fee391", "#fec44f", "#fe9929", "#d95f0e", "#993404"], "Dark2_06": ["#1b9e77", "#d95f02", "#7570b3", "#e7298a", "#66a61e", "#e6ab02"], "Blues_04": ["#eff3ff", "#bdd7e7", "#6baed6", "#2171b5"], "YlOrBr_07": ["#ffffd4", "#fee391", "#fec44f", "#fe9929", "#ec7014", "#cc4c02", "#8c2d04"], "RdYlGn_05": ["#d7191c", "#fdae61", "#ffffbf", "#a6d96a", "#1a9641"], "Set3_08": ["#8dd3c7", "#ffffb3", "#bebada", "#fb8072", "#80b1d3", "#fdb462", "#b3de69", "#fccde5"], "YlOrRd_06": ["#ffffb2", "#fed976", "#feb24c", "#fd8d3c", "#f03b20", "#bd0026"], "Dark2_03": ["#1b9e77", "#d95f02", "#7570b3"], "Accent_05": ["#7fc97f", "#beaed4", "#fdc086", "#ffff99", "#386cb0"], "RdYlGn_08": ["#d73027", "#f46d43", "#fdae61", "#fee08b", "#d9ef8b", "#a6d96a", "#66bd63", "#1a9850"], "RdYlGn_09": ["#d73027", "#f46d43", "#fdae61", "#fee08b", "#ffffbf", "#d9ef8b", "#a6d96a", "#66bd63", "#1a9850"], "PuOr_11": ["#7f3b08", "#b35806", "#e08214", "#fdb863", "#fee0b6", "#f7f7f7", "#d8daeb", "#b2abd2", "#8073ac", "#542788", "#2d004b"], "YlOrRd_07": ["#ffffb2", "#fed976", "#feb24c", "#fd8d3c", "#fc4e2a", "#e31a1c", "#b10026"], "Spectral_11": ["#9e0142", "#d53e4f", "#f46d43", "#fdae61", "#fee08b", "#ffffbf", "#e6f598", "#abdda4", "#66c2a5", "#3288bd", "#5e4fa2"], "RdGy_08": ["#b2182b", "#d6604d", "#f4a582", "#fddbc7", "#e0e0e0", "#bababa", "#878787", "#4d4d4d"], "RdGy_09": ["#b2182b", "#d6604d", "#f4a582", "#fddbc7", "#ffffff", "#e0e0e0", "#bababa", "#878787", "#4d4d4d"], "RdGy_06": ["#b2182b", "#ef8a62", "#fddbc7", "#e0e0e0", "#999999", "#4d4d4d"], "RdGy_07": ["#b2182b", "#ef8a62", "#fddbc7", "#ffffff", "#e0e0e0", "#999999", "#4d4d4d"], "RdGy_04": ["#ca0020", "#f4a582", "#bababa", "#404040"], "RdGy_05": ["#ca0020", "#f4a582", "#ffffff", "#bababa", "#404040"], "RdYlGn_04": ["#d7191c", "#fdae61", "#a6d96a", "#1a9641"], "PiYG_09": ["#c51b7d", "#de77ae", "#f1b6da", "#fde0ef", "#f7f7f7", "#e6f5d0", "#b8e186", "#7fbc41", "#4d9221"], "RdYlGn_06": ["#d73027", "#fc8d59", "#fee08b", "#d9ef8b", "#91cf60", "#1a9850"], "RdYlGn_07": ["#d73027", "#fc8d59", "#fee08b", "#ffffbf", "#d9ef8b", "#91cf60", "#1a9850"], "Spectral_04": ["#d7191c", "#fdae61", "#abdda4", "#2b83ba"], "Spectral_05": ["#d7191c", "#fdae61", "#ffffbf", "#abdda4", "#2b83ba"], "Spectral_06": ["#d53e4f", "#fc8d59", "#fee08b", "#e6f598", "#99d594", "#3288bd"], "PiYG_08": ["#c51b7d", "#de77ae", "#f1b6da", "#fde0ef", "#e6f5d0", "#b8e186", "#7fbc41", "#4d9221"], "Set2_03": ["#66c2a5", "#fc8d62", "#8da0cb"], "Spectral_03": ["#fc8d59", "#ffffbf", "#99d594"], "Reds_08": ["#fff5f0", "#fee0d2", "#fcbba1", "#fc9272", "#fb6a4a", "#ef3b2c", "#cb181d", "#99000d"], "Set1_04": ["#e41a1c", "#377eb8", "#4daf4a", "#984ea3"], "Spectral_08": ["#d53e4f", "#f46d43", "#fdae61", "#fee08b", "#e6f598", "#abdda4", "#66c2a5", "#3288bd"], "Spectral_09": ["#d53e4f", "#f46d43", "#fdae61", "#fee08b", "#ffffbf", "#e6f598", "#abdda4", "#66c2a5", "#3288bd"], "Set2_08": ["#66c2a5", "#fc8d62", "#8da0cb", "#e78ac3", "#a6d854", "#ffd92f", "#e5c494", "#b3b3b3"], "Reds_09": ["#fff5f0", "#fee0d2", "#fcbba1", "#fc9272", "#fb6a4a", "#ef3b2c", "#cb181d", "#a50f15", "#67000d"], "Greys_07": ["#f7f7f7", "#d9d9d9", "#bdbdbd", "#969696", "#737373", "#525252", "#252525"], "Greys_06": ["#f7f7f7", "#d9d9d9", "#bdbdbd", "#969696", "#636363", "#252525"], "Greys_05": ["#f7f7f7", "#cccccc", "#969696", "#636363", "#252525"], "Greys_04": ["#f7f7f7", "#cccccc", "#969696", "#525252"], "Greys_03": ["#f0f0f0", "#bdbdbd", "#636363"], "PuOr_10": ["#7f3b08", "#b35806", "#e08214", "#fdb863", "#fee0b6", "#d8daeb", "#b2abd2", "#8073ac", "#542788", "#2d004b"], "Accent_07": ["#7fc97f", "#beaed4", "#fdc086", "#ffff99", "#386cb0", "#f0027f", "#bf5b17"], "Reds_06": ["#fee5d9", "#fcbba1", "#fc9272", "#fb6a4a", "#de2d26", "#a50f15"], "Greys_09": ["#ffffff", "#f0f0f0", "#d9d9d9", "#bdbdbd", "#969696", "#737373", "#525252", "#252525", "#000000"], "Greys_08": ["#ffffff", "#f0f0f0", "#d9d9d9", "#bdbdbd", "#969696", "#737373", "#525252", "#252525"], "Reds_07": ["#fee5d9", "#fcbba1", "#fc9272", "#fb6a4a", "#ef3b2c", "#cb181d", "#99000d"], "RdYlBu_08": ["#d73027", "#f46d43", "#fdae61", "#fee090", "#e0f3f8", "#abd9e9", "#74add1", "#4575b4"], "RdYlBu_09": ["#d73027", "#f46d43", "#fdae61", "#fee090", "#ffffbf", "#e0f3f8", "#abd9e9", "#74add1", "#4575b4"], "BrBG_09": ["#8c510a", "#bf812d", "#dfc27d", "#f6e8c3", "#f5f5f5", "#c7eae5", "#80cdc1", "#35978f", "#01665e"], "BrBG_08": ["#8c510a", "#bf812d", "#dfc27d", "#f6e8c3", "#c7eae5", "#80cdc1", "#35978f", "#01665e"], "BrBG_07": ["#8c510a", "#d8b365", "#f6e8c3", "#f5f5f5", "#c7eae5", "#5ab4ac", "#01665e"], "BrBG_06": ["#8c510a", "#d8b365", "#f6e8c3", "#c7eae5", "#5ab4ac", "#01665e"], "BrBG_05": ["#a6611a", "#dfc27d", "#f5f5f5", "#80cdc1", "#018571"], "BrBG_04": ["#a6611a", "#dfc27d", "#80cdc1", "#018571"], "BrBG_03": ["#d8b365", "#f5f5f5", "#5ab4ac"], "PiYG_06": ["#c51b7d", "#e9a3c9", "#fde0ef", "#e6f5d0", "#a1d76a", "#4d9221"], "Reds_03": ["#fee0d2", "#fc9272", "#de2d26"], "Set3_11": ["#8dd3c7", "#ffffb3", "#bebada", "#fb8072", "#80b1d3", "#fdb462", "#b3de69", "#fccde5", "#d9d9d9", "#bc80bd", "#ccebc5"], "Set1_06": ["#e41a1c", "#377eb8", "#4daf4a", "#984ea3", "#ff7f00", "#ffff33"], "PuRd_03": ["#e7e1ef", "#c994c7", "#dd1c77"], "PiYG_07": ["#c51b7d", "#e9a3c9", "#fde0ef", "#f7f7f7", "#e6f5d0", "#a1d76a", "#4d9221"], "RdBu_07": ["#b2182b", "#ef8a62", "#fddbc7", "#f7f7f7", "#d1e5f0", "#67a9cf", "#2166ac"], "Pastel1_06": ["#fbb4ae", "#b3cde3", "#ccebc5", "#decbe4", "#fed9a6", "#ffffcc"], "Spectral_10": ["#9e0142", "#d53e4f", "#f46d43", "#fdae61", "#fee08b", "#e6f598", "#abdda4", "#66c2a5", "#3288bd", "#5e4fa2"], "PuRd_04": ["#f1eef6", "#d7b5d8", "#df65b0", "#ce1256"], "OrRd_03": ["#fee8c8", "#fdbb84", "#e34a33"], "PiYG_03": ["#e9a3c9", "#f7f7f7", "#a1d76a"], "Oranges_05": ["#feedde", "#fdbe85", "#fd8d3c", "#e6550d", "#a63603"], "OrRd_07": ["#fef0d9", "#fdd49e", "#fdbb84", "#fc8d59", "#ef6548", "#d7301f", "#990000"], "OrRd_06": ["#fef0d9", "#fdd49e", "#fdbb84", "#fc8d59", "#e34a33", "#b30000"], "OrRd_05": ["#fef0d9", "#fdcc8a", "#fc8d59", "#e34a33", "#b30000"], "OrRd_04": ["#fef0d9", "#fdcc8a", "#fc8d59", "#d7301f"], "Reds_04": ["#fee5d9", "#fcae91", "#fb6a4a", "#cb181d"], "Reds_05": ["#fee5d9", "#fcae91", "#fb6a4a", "#de2d26", "#a50f15"], "OrRd_09": ["#fff7ec", "#fee8c8", "#fdd49e", "#fdbb84", "#fc8d59", "#ef6548", "#d7301f", "#b30000", "#7f0000"], "OrRd_08": ["#fff7ec", "#fee8c8", "#fdd49e", "#fdbb84", "#fc8d59", "#ef6548", "#d7301f", "#990000"], "BrBG_10": ["#543005", "#8c510a", "#bf812d", "#dfc27d", "#f6e8c3", "#c7eae5", "#80cdc1", "#35978f", "#01665e", "#003c30"], "BrBG_11": ["#543005", "#8c510a", "#bf812d", "#dfc27d", "#f6e8c3", "#f5f5f5", "#c7eae5", "#80cdc1", "#35978f", "#01665e", "#003c30"], "PiYG_05": ["#d01c8b", "#f1b6da", "#f7f7f7", "#b8e186", "#4dac26"], "YlOrRd_08": ["#ffffcc", "#ffeda0", "#fed976", "#feb24c", "#fd8d3c", "#fc4e2a", "#e31a1c", "#b10026"], "GnBu_04": ["#f0f9e8", "#bae4bc", "#7bccc4", "#2b8cbe"], "GnBu_05": ["#f0f9e8", "#bae4bc", "#7bccc4", "#43a2ca", "#0868ac"], "GnBu_06": ["#f0f9e8", "#ccebc5", "#a8ddb5", "#7bccc4", "#43a2ca", "#0868ac"], "GnBu_07": ["#f0f9e8", "#ccebc5", "#a8ddb5", "#7bccc4", "#4eb3d3", "#2b8cbe", "#08589e"], "Purples_08": ["#fcfbfd", "#efedf5", "#dadaeb", "#bcbddc", "#9e9ac8", "#807dba", "#6a51a3", "#4a1486"], "GnBu_03": ["#e0f3db", "#a8ddb5", "#43a2ca"], "Purples_06": ["#f2f0f7", "#dadaeb", "#bcbddc", "#9e9ac8", "#756bb1", "#54278f"], "Purples_07": ["#f2f0f7", "#dadaeb", "#bcbddc", "#9e9ac8", "#807dba", "#6a51a3", "#4a1486"], "Purples_04": ["#f2f0f7", "#cbc9e2", "#9e9ac8", "#6a51a3"], "Purples_05": ["#f2f0f7", "#cbc9e2", "#9e9ac8", "#756bb1", "#54278f"], "GnBu_08": ["#f7fcf0", "#e0f3db", "#ccebc5", "#a8ddb5", "#7bccc4", "#4eb3d3", "#2b8cbe", "#08589e"], "GnBu_09": ["#f7fcf0", "#e0f3db", "#ccebc5", "#a8ddb5", "#7bccc4", "#4eb3d3", "#2b8cbe", "#0868ac", "#084081"], "YlOrRd_09": ["#ffffcc", "#ffeda0", "#fed976", "#feb24c", "#fd8d3c", "#fc4e2a", "#e31a1c", "#bd0026", "#800026"], "Purples_03": ["#efedf5", "#bcbddc", "#756bb1"], "RdYlBu_04": ["#d7191c", "#fdae61", "#abd9e9", "#2c7bb6"], "PRGn_09": ["#762a83", "#9970ab", "#c2a5cf", "#e7d4e8", "#f7f7f7", "#d9f0d3", "#a6dba0", "#5aae61", "#1b7837"], "PRGn_08": ["#762a83", "#9970ab", "#c2a5cf", "#e7d4e8", "#d9f0d3", "#a6dba0", "#5aae61", "#1b7837"], "PRGn_07": ["#762a83", "#af8dc3", "#e7d4e8", "#f7f7f7", "#d9f0d3", "#7fbf7b", "#1b7837"], "PRGn_06": ["#762a83", "#af8dc3", "#e7d4e8", "#d9f0d3", "#7fbf7b", "#1b7837"], "PRGn_05": ["#7b3294", "#c2a5cf", "#f7f7f7", "#a6dba0", "#008837"], "PRGn_04": ["#7b3294", "#c2a5cf", "#a6dba0", "#008837"], "PRGn_03": ["#af8dc3", "#f7f7f7", "#7fbf7b"], "RdYlBu_06": ["#d73027", "#fc8d59", "#fee090", "#e0f3f8", "#91bfdb", "#4575b4"], "RdYlGn_10": ["#a50026", "#d73027", "#f46d43", "#fdae61", "#fee08b", "#d9ef8b", "#a6d96a", "#66bd63", "#1a9850", "#006837"], "YlGn_08": ["#ffffe5", "#f7fcb9", "#d9f0a3", "#addd8e", "#78c679", "#41ab5d", "#238443", "#005a32"], "YlGn_09": ["#ffffe5", "#f7fcb9", "#d9f0a3", "#addd8e", "#78c679", "#41ab5d", "#238443", "#006837", "#004529"], "RdYlBu_07": ["#d73027", "#fc8d59", "#fee090", "#ffffbf", "#e0f3f8", "#91bfdb", "#4575b4"], "PiYG_10": ["#8e0152", "#c51b7d", "#de77ae", "#f1b6da", "#fde0ef", "#e6f5d0", "#b8e186", "#7fbc41", "#4d9221", "#276419"], "PiYG_11": ["#8e0152", "#c51b7d", "#de77ae", "#f1b6da", "#fde0ef", "#f7f7f7", "#e6f5d0", "#b8e186", "#7fbc41", "#4d9221", "#276419"], "YlGn_03": ["#f7fcb9", "#addd8e", "#31a354"], "YlGn_04": ["#ffffcc", "#c2e699", "#78c679", "#238443"], "YlGn_05": ["#ffffcc", "#c2e699", "#78c679", "#31a354", "#006837"], "YlGn_06": ["#ffffcc", "#d9f0a3", "#addd8e", "#78c679", "#31a354", "#006837"], "YlGn_07": ["#ffffcc", "#d9f0a3", "#addd8e", "#78c679", "#41ab5d", "#238443", "#005a32"], "Dark2_05": ["#1b9e77", "#d95f02", "#7570b3", "#e7298a", "#66a61e"], "Dark2_04": ["#1b9e77", "#d95f02", "#7570b3", "#e7298a"], "Dark2_07": ["#1b9e77", "#d95f02", "#7570b3", "#e7298a", "#66a61e", "#e6ab02", "#a6761d"], "Pastel2_03": ["#b3e2cd", "#fdcdac", "#cbd5e8"], "Pastel2_04": ["#b3e2cd", "#fdcdac", "#cbd5e8", "#f4cae4"], "Pastel2_05": ["#b3e2cd", "#fdcdac", "#cbd5e8", "#f4cae4", "#e6f5c9"], "Pastel2_06": ["#b3e2cd", "#fdcdac", "#cbd5e8", "#f4cae4", "#e6f5c9", "#fff2ae"], "Pastel2_07": ["#b3e2cd", "#fdcdac", "#cbd5e8", "#f4cae4", "#e6f5c9", "#fff2ae", "#f1e2cc"], "Pastel2_08": ["#b3e2cd", "#fdcdac", "#cbd5e8", "#f4cae4", "#e6f5c9", "#fff2ae", "#f1e2cc", "#cccccc"], "RdYlBu_03": ["#fc8d59", "#ffffbf", "#91bfdb"], "Dark2_08": ["#1b9e77", "#d95f02", "#7570b3", "#e7298a", "#66a61e", "#e6ab02", "#a6761d", "#666666"], "RdYlGn_03": ["#fc8d59", "#ffffbf", "#91cf60"], "PRGn_11": ["#40004b", "#762a83", "#9970ab", "#c2a5cf", "#e7d4e8", "#f7f7f7", "#d9f0d3", "#a6dba0", "#5aae61", "#1b7837", "#00441b"], "Greens_08": ["#f7fcf5", "#e5f5e0", "#c7e9c0", "#a1d99b", "#74c476", "#41ab5d", "#238b45", "#005a32"], "Greens_09": ["#f7fcf5", "#e5f5e0", "#c7e9c0", "#a1d99b", "#74c476", "#41ab5d", "#238b45", "#006d2c", "#00441b"], "Greens_06": ["#edf8e9", "#c7e9c0", "#a1d99b", "#74c476", "#31a354", "#006d2c"], "Greens_07": ["#edf8e9", "#c7e9c0", "#a1d99b", "#74c476", "#41ab5d", "#238b45", "#005a32"], "Greens_04": ["#edf8e9", "#bae4b3", "#74c476", "#238b45"], "Greens_05": ["#edf8e9", "#bae4b3", "#74c476", "#31a354", "#006d2c"], "PRGn_10": ["#40004b", "#762a83", "#9970ab", "#c2a5cf", "#e7d4e8", "#d9f0d3", "#a6dba0", "#5aae61", "#1b7837", "#00441b"], "Greens_03": ["#e5f5e0", "#a1d99b", "#31a354"]}
--------------------------------------------------------------------------------
/branca/element.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | """
3 | Element
4 | -------
5 |
6 | A generic class for creating Elements.
7 | """
8 | import warnings
9 | from uuid import uuid4
10 |
11 | from jinja2 import Environment, PackageLoader, Template
12 | from collections import OrderedDict
13 | import json
14 | import base64
15 |
16 | from .six import urlopen, text_type, binary_type
17 | from .utilities import _camelify, _parse_size, none_min, none_max
18 |
19 |
20 | ENV = Environment(loader=PackageLoader('branca', 'templates'))
21 |
22 |
23 | class Element(object):
24 | """Basic Element object that does nothing.
25 | Other Elements may inherit from this one.
26 |
27 | Parameters
28 | ----------
29 | template: str, default None
30 | A jinaj2-compatible template string for rendering the element.
31 | If None, template will be:
32 | {% for name, element in this._children.items() %}
33 | {{element.render(**kwargs)}}
34 | {% endfor %}
35 | so that all the element's children are rendered.
36 | template_name: str, default None
37 | If no template is provided, you can also provide a filename.
38 | """
39 | def __init__(self, template=None, template_name=None):
40 | self._name = 'Element'
41 | self._id = uuid4().hex
42 | self._env = ENV
43 | self._children = OrderedDict()
44 | self._parent = None
45 | self._template = Template(template) if template is not None\
46 | else ENV.get_template(template_name) if template_name is not None\
47 | else Template(
48 | "{% for name, element in this._children.items() %}\n"
49 | " {{element.render(**kwargs)}}"
50 | "{% endfor %}"
51 | )
52 |
53 | def get_name(self):
54 | """Returns a string representation of the object.
55 | This string has to be unique and to be a python and
56 | javascript-compatible
57 | variable name.
58 | """
59 | return _camelify(self._name) + '_' + self._id
60 |
61 | def _get_self_bounds(self):
62 | """Computes the bounds of the object itself (not including it's children)
63 | in the form [[lat_min, lon_min], [lat_max, lon_max]]
64 | """
65 | return [[None, None], [None, None]]
66 |
67 | def get_bounds(self):
68 | """Computes the bounds of the object and all it's children
69 | in the form [[lat_min, lon_min], [lat_max, lon_max]].
70 | """
71 | bounds = self._get_self_bounds()
72 |
73 | for child in self._children.values():
74 | child_bounds = child.get_bounds()
75 | bounds = [
76 | [
77 | none_min(bounds[0][0], child_bounds[0][0]),
78 | none_min(bounds[0][1], child_bounds[0][1]),
79 | ],
80 | [
81 | none_max(bounds[1][0], child_bounds[1][0]),
82 | none_max(bounds[1][1], child_bounds[1][1]),
83 | ],
84 | ]
85 | return bounds
86 |
87 | def add_children(self, child, name=None, index=None):
88 | """Add a child."""
89 | warnings.warn("Method `add_children` is deprecated. Please use `add_child` instead.",
90 | FutureWarning, stacklevel=2)
91 | return self.add_child(child, name=name, index=index)
92 |
93 | def add_child(self, child, name=None, index=None):
94 | """Add a child."""
95 | if name is None:
96 | name = child.get_name()
97 | if index is None:
98 | self._children[name] = child
99 | else:
100 | items = [item for item in self._children.items()
101 | if item[0] != name]
102 | items.insert(int(index), (name, child))
103 | self._children = items
104 | child._parent = self
105 | return self
106 |
107 | def add_to(self, parent, name=None, index=None):
108 | """Add element to a parent."""
109 | parent.add_child(self, name=name, index=index)
110 | return self
111 |
112 | def to_dict(self, depth=-1, ordered=True, **kwargs):
113 | """Returns a dict representation of the object."""
114 | if ordered:
115 | dict_fun = OrderedDict
116 | else:
117 | dict_fun = dict
118 | out = dict_fun()
119 | out['name'] = self._name
120 | out['id'] = self._id
121 | if depth != 0:
122 | out['children'] = dict_fun([(name, child.to_dict(depth=depth-1))
123 | for name, child in self._children.items()]) # noqa
124 | return out
125 |
126 | def to_json(self, depth=-1, **kwargs):
127 | """Returns a JSON representation of the object."""
128 | return json.dumps(self.to_dict(depth=depth, ordered=True), **kwargs)
129 |
130 | def get_root(self):
131 | """Returns the root of the elements tree."""
132 | if self._parent is None:
133 | return self
134 | else:
135 | return self._parent.get_root()
136 |
137 | def render(self, **kwargs):
138 | """Renders the HTML representation of the element."""
139 | return self._template.render(this=self, kwargs=kwargs)
140 |
141 | def save(self, outfile, close_file=True, **kwargs):
142 | """Saves an Element into a file.
143 |
144 | Parameters
145 | ----------
146 | outfile : str or file object
147 | The file (or filename) where you want to output the html.
148 | close_file : bool, default True
149 | Whether the file has to be closed after write.
150 | """
151 | if isinstance(outfile, text_type) or isinstance(outfile, binary_type):
152 | fid = open(outfile, 'wb')
153 | else:
154 | fid = outfile
155 |
156 | root = self.get_root()
157 | html = root.render(**kwargs)
158 | fid.write(html.encode('utf8'))
159 | if close_file:
160 | fid.close()
161 |
162 |
163 | class Link(Element):
164 | """An abstract class for embedding a link in the HTML."""
165 | def get_code(self):
166 | """Opens the link and returns the response's content."""
167 | if self.code is None:
168 | self.code = urlopen(self.url).read()
169 | return self.code
170 |
171 | def to_dict(self, depth=-1, **kwargs):
172 | """Returns a dict representation of the object."""
173 | out = super(Link, self).to_dict(depth=-1, **kwargs)
174 | out['url'] = self.url
175 | return out
176 |
177 |
178 | class JavascriptLink(Link):
179 | """Create a JavascriptLink object based on a url.
180 | Parameters
181 | ----------
182 | url : str
183 | The url to be linked
184 | download : bool, default False
185 | Whether the target document shall be loaded right now.
186 | """
187 | def __init__(self, url, download=False):
188 | super(JavascriptLink, self).__init__()
189 | self._name = 'JavascriptLink'
190 | self.url = url
191 | self.code = None
192 | if download:
193 | self.get_code()
194 |
195 | self._template = Template(
196 | '{% if kwargs.get("embedded",False) %}'
197 | ''
198 | '{% else %}'
199 | ''
200 | '{% endif %}'
201 | )
202 |
203 |
204 | class CssLink(Link):
205 | """Create a CssLink object based on a url.
206 | Parameters
207 | ----------
208 | url : str
209 | The url to be linked
210 | download : bool, default False
211 | Whether the target document shall be loaded right now.
212 | """
213 | def __init__(self, url, download=False):
214 | super(CssLink, self).__init__()
215 | self._name = 'CssLink'
216 | self.url = url
217 | self.code = None
218 | if download:
219 | self.get_code()
220 |
221 | self._template = Template(
222 | '{% if kwargs.get("embedded",False) %}'
223 | ''
224 | '{% else %}'
225 | ' '
226 | '{% endif %}'
227 | )
228 |
229 |
230 | class Figure(Element):
231 | """Create a Figure object, to plot things into it.
232 |
233 | Parameters
234 | ----------
235 | width : str, default "100%"
236 | The width of the Figure.
237 | It may be a percentage or pixel value (like "300px").
238 | height : str, default None
239 | The height of the Figure.
240 | It may be a percentage or a pixel value (like "300px").
241 | ratio : str, default "60%"
242 | A percentage defining the aspect ratio of the Figure.
243 | It will be ignored if height is not None.
244 | figsize : tuple of two int, default None
245 | If you're a matplotlib addict, you can overwrite width and
246 | height. Values will be converted into pixels in using 60 dpi.
247 | For example figsize=(10, 5) will result in
248 | width="600px", height="300px".
249 | """
250 | def __init__(self, width="100%", height=None, ratio="60%", figsize=None):
251 | super(Figure, self).__init__()
252 | self._name = 'Figure'
253 | self.header = Element()
254 | self.html = Element()
255 | self.script = Element()
256 |
257 | self.header._parent = self
258 | self.html._parent = self
259 | self.script._parent = self
260 |
261 | self.width = width
262 | self.height = height
263 | self.ratio = ratio
264 | if figsize is not None:
265 | self.width = str(60*figsize[0])+'px'
266 | self.height = str(60*figsize[1])+'px'
267 |
268 | self._template = Template(
269 | '\n'
270 | ''
271 | ' {{this.header.render(**kwargs)}}\n'
272 | '\n'
273 | ''
274 | ' {{this.html.render(**kwargs)}}\n'
275 | '\n'
276 | '\n'
279 | )
280 |
281 | # Create the meta tag.
282 | self.header.add_child(Element(
283 | ' '), # noqa
284 | name='meta_http')
285 |
286 | def to_dict(self, depth=-1, **kwargs):
287 | """Returns a dict representation of the object."""
288 | out = super(Figure, self).to_dict(depth=depth, **kwargs)
289 | out['header'] = self.header.to_dict(depth=depth-1, **kwargs)
290 | out['html'] = self.html.to_dict(depth=depth-1, **kwargs)
291 | out['script'] = self.script.to_dict(depth=depth-1, **kwargs)
292 | return out
293 |
294 | def get_root(self):
295 | """Returns the root of the elements tree."""
296 | return self
297 |
298 | def render(self, **kwargs):
299 | """Renders the HTML representation of the element."""
300 | for name, child in self._children.items():
301 | child.render(**kwargs)
302 | return self._template.render(this=self, kwargs=kwargs)
303 |
304 | def _repr_html_(self, **kwargs):
305 | """Displays the Figure in a Jupyter notebook.
306 |
307 | Parameters
308 | ----------
309 |
310 | """
311 | html = self.render(**kwargs)
312 | html = "data:text/html;charset=utf-8;base64," + base64.b64encode(html.encode('utf8')).decode('utf8') # noqa
313 |
314 | if self.height is None:
315 | iframe = (
316 | ''
317 | '
' # noqa
318 | ''
322 | '
').format
323 | iframe = iframe(html=html,
324 | width=self.width,
325 | ratio=self.ratio)
326 | else:
327 | iframe = ('').format
331 | iframe = iframe(html=html, width=self.width, height=self.height)
332 | return iframe
333 |
334 | def add_subplot(self, x, y, n, margin=0.05):
335 | """Creates a div child subplot in a matplotlib.figure.add_subplot style.
336 |
337 | Parameters
338 | ----------
339 | x : int
340 | The number of rows in the grid.
341 | y : int
342 | The number of columns in the grid.
343 | n : int
344 | The cell number in the grid, counted from 1 to x*y.
345 |
346 | Example:
347 | >>> fig.add_subplot(3,2,5)
348 | # Create a div in the 5th cell of a 3rows x 2columns
349 | grid(bottom-left corner).
350 | """
351 | width = 1./y
352 | height = 1./x
353 | left = ((n-1) % y)*width
354 | top = ((n-1)//y)*height
355 |
356 | left = left+width*margin
357 | top = top+height*margin
358 | width = width*(1-2.*margin)
359 | height = height*(1-2.*margin)
360 |
361 | div = Div(position='absolute',
362 | width="{}%".format(100.*width),
363 | height="{}%".format(100.*height),
364 | left="{}%".format(100.*left),
365 | top="{}%".format(100.*top),
366 | )
367 | self.add_child(div)
368 | return div
369 |
370 |
371 | class Html(Element):
372 | """Create an HTML div object for embedding data.
373 |
374 | Parameters
375 | ----------
376 | data : str
377 | The HTML data to be embedded.
378 | script : bool
379 | If True, data will be embedded without escaping
380 | (suitable for embedding html-ready code)
381 | width : int or str, default '100%'
382 | The width of the output div element.
383 | Ex: 120 , '120px', '80%'
384 | height : int or str, default '100%'
385 | The height of the output div element.
386 | Ex: 120 , '120px', '80%'
387 | """
388 |
389 | def __init__(self, data, script=False, width="100%", height="100%"):
390 | super(Html, self).__init__()
391 | self._name = 'Html'
392 | self.script = script
393 | self.data = data
394 |
395 | self.width = _parse_size(width)
396 | self.height = _parse_size(height)
397 |
398 | self._template = Template(
399 | '' # noqa
401 | '{% if this.script %}{{this.data}}{% else %}{{this.data|e}}{% endif %}
'
402 | ) # noqa
403 |
404 |
405 | class Div(Figure):
406 | """Create a Div to be embedded in a Figure.
407 |
408 | Parameters
409 | ----------
410 | width: int or str, default '100%'
411 | The width of the div in pixels (int) or percentage (str).
412 | height: int or str, default '100%'
413 | The height of the div in pixels (int) or percentage (str).
414 | left: int or str, default '0%'
415 | The left-position of the div in pixels (int) or percentage (str).
416 | top: int or str, default '0%'
417 | The top-position of the div in pixels (int) or percentage (str).
418 | position: str, default 'relative'
419 | The position policy of the div.
420 | Usual values are 'relative', 'absolute', 'fixed', 'static'.
421 | """
422 | def __init__(self, width='100%', height='100%',
423 | left="0%", top="0%", position='relative'):
424 | super(Figure, self).__init__()
425 | self._name = 'Div'
426 |
427 | # Size Parameters.
428 | self.width = _parse_size(width)
429 | self.height = _parse_size(height)
430 | self.left = _parse_size(left)
431 | self.top = _parse_size(top)
432 | self.position = position
433 |
434 | self.header = Element()
435 | self.html = Element(
436 | '{% for name, element in this._children.items() %}'
437 | '{{element.render(**kwargs)}}'
438 | '{% endfor %}'
439 | )
440 | self.script = Element()
441 |
442 | self.header._parent = self
443 | self.html._parent = self
444 | self.script._parent = self
445 |
446 | self._template = Template(
447 | '{% macro header(this, kwargs) %}'
448 | ''
455 | '{% endmacro %}'
456 | '{% macro html(this, kwargs) %}'
457 | '{{this.html.render(**kwargs)}}
'
458 | '{% endmacro %}'
459 | )
460 |
461 | def get_root(self):
462 | """Returns the root of the elements tree."""
463 | return self
464 |
465 | def render(self, **kwargs):
466 | """Renders the HTML representation of the element."""
467 | figure = self._parent
468 | assert isinstance(figure, Figure), ("You cannot render this Element "
469 | "if it's not in a Figure.")
470 |
471 | for name, element in self._children.items():
472 | element.render(**kwargs)
473 |
474 | for name, element in self.header._children.items():
475 | figure.header.add_child(element, name=name)
476 |
477 | for name, element in self.script._children.items():
478 | figure.script.add_child(element, name=name)
479 |
480 | header = self._template.module.__dict__.get('header', None)
481 | if header is not None:
482 | figure.header.add_child(Element(header(self, kwargs)),
483 | name=self.get_name())
484 |
485 | html = self._template.module.__dict__.get('html', None)
486 | if html is not None:
487 | figure.html.add_child(Element(html(self, kwargs)),
488 | name=self.get_name())
489 |
490 | script = self._template.module.__dict__.get('script', None)
491 | if script is not None:
492 | figure.script.add_child(Element(script(self, kwargs)),
493 | name=self.get_name())
494 |
495 | def _repr_html_(self, **kwargs):
496 | """Displays the Div in a Jupyter notebook."""
497 | if self._parent is None:
498 | self.add_to(Figure())
499 | out = self._parent._repr_html_(**kwargs)
500 | self._parent = None
501 | else:
502 | out = self._parent._repr_html_(**kwargs)
503 | return out
504 |
505 |
506 | class IFrame(Element):
507 | """Create a Figure object, to plot things into it.
508 |
509 | Parameters
510 | ----------
511 | html : str, default None
512 | Eventual HTML code that you want to put in the frame.
513 | width : str, default "100%"
514 | The width of the Figure.
515 | It may be a percentage or pixel value (like "300px").
516 | height : str, default None
517 | The height of the Figure.
518 | It may be a percentage or a pixel value (like "300px").
519 | ratio : str, default "60%"
520 | A percentage defining the aspect ratio of the Figure.
521 | It will be ignored if height is not None.
522 | figsize : tuple of two int, default None
523 | If you're a matplotlib addict, you can overwrite width and
524 | height. Values will be converted into pixels in using 60 dpi.
525 | For example figsize=(10, 5) will result in
526 | width="600px", height="300px".
527 | """
528 | def __init__(self, html=None, width="100%", height=None, ratio="60%",
529 | figsize=None):
530 | super(IFrame, self).__init__()
531 | self._name = 'IFrame'
532 |
533 | self.width = width
534 | self.height = height
535 | self.ratio = ratio
536 | if figsize is not None:
537 | self.width = str(60*figsize[0])+'px'
538 | self.height = str(60*figsize[1])+'px'
539 |
540 | if isinstance(html, text_type) or isinstance(html, binary_type):
541 | self.add_child(Element(html))
542 | elif html is not None:
543 | self.add_child(html)
544 |
545 | def render(self, **kwargs):
546 | """Renders the HTML representation of the element."""
547 | html = super(IFrame, self).render(**kwargs)
548 | html = "data:text/html;charset=utf-8;base64," + base64.b64encode(html.encode('utf8')).decode('utf8') # noqa
549 |
550 | if self.height is None:
551 | iframe = (
552 | ''
553 | '
' # noqa
554 | ''
557 | '
').format
558 | iframe = iframe(html=html,
559 | width=self.width,
560 | ratio=self.ratio)
561 | else:
562 | iframe = ('').format
564 | iframe = iframe(html=html, width=self.width, height=self.height)
565 | return iframe
566 |
567 |
568 | class MacroElement(Element):
569 | """This is a parent class for Elements defined by a macro template.
570 | To compute your own element, all you have to do is:
571 |
572 | * To inherit from this class
573 | * Overwrite the '_name' attribute
574 | * Overwrite the '_template' attribute with something of the form::
575 |
576 | {% macro header(this, kwargs) %}
577 | ...
578 | {% endmacro %}
579 |
580 | {% macro html(this, kwargs) %}
581 | ...
582 | {% endmacro %}
583 |
584 | {% macro script(this, kwargs) %}
585 | ...
586 | {% endmacro %}
587 |
588 | """
589 | def __init__(self):
590 | super(MacroElement, self).__init__()
591 | self._name = 'MacroElement'
592 |
593 | self._template = Template(u"")
594 |
595 | def render(self, **kwargs):
596 | """Renders the HTML representation of the element."""
597 | figure = self.get_root()
598 | assert isinstance(figure, Figure), ("You cannot render this Element "
599 | "if it's not in a Figure.")
600 |
601 | header = self._template.module.__dict__.get('header', None)
602 | if header is not None:
603 | figure.header.add_child(Element(header(self, kwargs)),
604 | name=self.get_name())
605 |
606 | html = self._template.module.__dict__.get('html', None)
607 | if html is not None:
608 | figure.html.add_child(Element(html(self, kwargs)),
609 | name=self.get_name())
610 |
611 | script = self._template.module.__dict__.get('script', None)
612 | if script is not None:
613 | figure.script.add_child(Element(script(self, kwargs)),
614 | name=self.get_name())
615 |
616 | for name, element in self._children.items():
617 | element.render(**kwargs)
618 |
--------------------------------------------------------------------------------
/examples/Elements.ipynb:
--------------------------------------------------------------------------------
1 | {
2 | "cells": [
3 | {
4 | "cell_type": "code",
5 | "execution_count": 1,
6 | "metadata": {
7 | "collapsed": false
8 | },
9 | "outputs": [],
10 | "source": [
11 | "import sys\n",
12 | "sys.path.insert(0, '..')\n",
13 | "\n",
14 | "from branca.element import *"
15 | ]
16 | },
17 | {
18 | "cell_type": "markdown",
19 | "metadata": {},
20 | "source": [
21 | "## Element"
22 | ]
23 | },
24 | {
25 | "cell_type": "markdown",
26 | "metadata": {},
27 | "source": [
28 | "This is the base brick of `branca`. You can create an `Element` in providing a template string:"
29 | ]
30 | },
31 | {
32 | "cell_type": "code",
33 | "execution_count": 2,
34 | "metadata": {
35 | "collapsed": false
36 | },
37 | "outputs": [],
38 | "source": [
39 | "e = Element(\"This is fancy text\")"
40 | ]
41 | },
42 | {
43 | "cell_type": "markdown",
44 | "metadata": {},
45 | "source": [
46 | "Each element has an attribute `_name` and a unique `_id`. You also have a method `get_name` to get a unique string representation of the element."
47 | ]
48 | },
49 | {
50 | "cell_type": "code",
51 | "execution_count": 3,
52 | "metadata": {
53 | "collapsed": false
54 | },
55 | "outputs": [
56 | {
57 | "name": "stdout",
58 | "output_type": "stream",
59 | "text": [
60 | "Element a1d0f648f7444f96b526931944247fd6\n",
61 | "element_a1d0f648f7444f96b526931944247fd6\n"
62 | ]
63 | }
64 | ],
65 | "source": [
66 | "print(e._name, e._id)\n",
67 | "print(e.get_name())"
68 | ]
69 | },
70 | {
71 | "cell_type": "markdown",
72 | "metadata": {},
73 | "source": [
74 | "You can render an `Element` using the method `render`:"
75 | ]
76 | },
77 | {
78 | "cell_type": "code",
79 | "execution_count": 4,
80 | "metadata": {
81 | "collapsed": false
82 | },
83 | "outputs": [
84 | {
85 | "data": {
86 | "text/plain": [
87 | "'This is fancy text'"
88 | ]
89 | },
90 | "execution_count": 4,
91 | "metadata": {},
92 | "output_type": "execute_result"
93 | }
94 | ],
95 | "source": [
96 | "e.render()"
97 | ]
98 | },
99 | {
100 | "cell_type": "markdown",
101 | "metadata": {},
102 | "source": [
103 | "In the template, you can use keyword `this` for accessing the object itself ; and the keyword `kwargs` for accessing any keyword argument provided in the `render` method:"
104 | ]
105 | },
106 | {
107 | "cell_type": "code",
108 | "execution_count": 5,
109 | "metadata": {
110 | "collapsed": false
111 | },
112 | "outputs": [
113 | {
114 | "data": {
115 | "text/plain": [
116 | "'Hello World, my name is `element_6f17661abddb45c7bf2aa794cadd327d`.'"
117 | ]
118 | },
119 | "execution_count": 5,
120 | "metadata": {},
121 | "output_type": "execute_result"
122 | }
123 | ],
124 | "source": [
125 | "e = Element(\"Hello {{kwargs['you']}}, my name is `{{this.get_name()}}`.\")\n",
126 | "e.render(you='World')"
127 | ]
128 | },
129 | {
130 | "cell_type": "markdown",
131 | "metadata": {},
132 | "source": [
133 | "Well, this is not really cool for now. What makes elements useful lies in the fact that you can create trees out of them. To do so, you can either use the method `add_child` or the method `add_to`."
134 | ]
135 | },
136 | {
137 | "cell_type": "code",
138 | "execution_count": 6,
139 | "metadata": {
140 | "collapsed": false
141 | },
142 | "outputs": [],
143 | "source": [
144 | "child = Element('This is the child.')\n",
145 | "parent = Element('This is the parent.').add_child(child)\n",
146 | "\n",
147 | "parent = Element('This is the parent.')\n",
148 | "child = Element('This is the child.').add_to(parent)"
149 | ]
150 | },
151 | {
152 | "cell_type": "markdown",
153 | "metadata": {},
154 | "source": [
155 | "Now in the example above, embedding the one in the other does not change anything."
156 | ]
157 | },
158 | {
159 | "cell_type": "code",
160 | "execution_count": 7,
161 | "metadata": {
162 | "collapsed": false
163 | },
164 | "outputs": [
165 | {
166 | "name": "stdout",
167 | "output_type": "stream",
168 | "text": [
169 | "This is the parent. This is the child.\n"
170 | ]
171 | }
172 | ],
173 | "source": [
174 | "print(parent.render(), child.render())"
175 | ]
176 | },
177 | {
178 | "cell_type": "markdown",
179 | "metadata": {},
180 | "source": [
181 | "But you can use the tree structure in the template."
182 | ]
183 | },
184 | {
185 | "cell_type": "code",
186 | "execution_count": 8,
187 | "metadata": {
188 | "collapsed": false
189 | },
190 | "outputs": [
191 | {
192 | "data": {
193 | "text/plain": [
194 | "' '"
195 | ]
196 | },
197 | "execution_count": 8,
198 | "metadata": {},
199 | "output_type": "execute_result"
200 | }
201 | ],
202 | "source": [
203 | "parent = Element(\"{% for child in this._children.values() %}{{child.render()}}{% endfor %} \")\n",
204 | "Element(' ').add_to(parent)\n",
205 | "Element(' ').add_to(parent)\n",
206 | "parent.render()"
207 | ]
208 | },
209 | {
210 | "cell_type": "markdown",
211 | "metadata": {},
212 | "source": [
213 | "As you can see, the child of an element are referenced in the `_children` attibute in the form of an `OrderedDict`. You can choose the key of each child in specifying a `name` in the `add_child` (or `add_to`) method:"
214 | ]
215 | },
216 | {
217 | "cell_type": "code",
218 | "execution_count": 9,
219 | "metadata": {
220 | "collapsed": false
221 | },
222 | "outputs": [
223 | {
224 | "data": {
225 | "text/plain": [
226 | "OrderedDict([('child_1', )])"
227 | ]
228 | },
229 | "execution_count": 9,
230 | "metadata": {},
231 | "output_type": "execute_result"
232 | }
233 | ],
234 | "source": [
235 | "parent = Element(\"{% for child in this._children.values() %}{{child.render()}}{% endfor %} \")\n",
236 | "Element(' ').add_to(parent, name='child_1')\n",
237 | "parent._children"
238 | ]
239 | },
240 | {
241 | "cell_type": "markdown",
242 | "metadata": {},
243 | "source": [
244 | "That way, it's possible to overwrite a child in specifying the same name:"
245 | ]
246 | },
247 | {
248 | "cell_type": "code",
249 | "execution_count": 10,
250 | "metadata": {
251 | "collapsed": false
252 | },
253 | "outputs": [
254 | {
255 | "data": {
256 | "text/plain": [
257 | "' '"
258 | ]
259 | },
260 | "execution_count": 10,
261 | "metadata": {},
262 | "output_type": "execute_result"
263 | }
264 | ],
265 | "source": [
266 | "Element(' ').add_to(parent, name='child_1')\n",
267 | "parent.render()"
268 | ]
269 | },
270 | {
271 | "cell_type": "markdown",
272 | "metadata": {},
273 | "source": [
274 | "I hope you start to find it useful.\n",
275 | "\n",
276 | "In fact, the real interest of `Element` lies in the classes that inherit from it. The most important one is `Figure` described in the next section."
277 | ]
278 | },
279 | {
280 | "cell_type": "markdown",
281 | "metadata": {},
282 | "source": [
283 | "## Figure\n",
284 | "\n",
285 | "A `Figure` represents an HTML document. It's composed of 3 parts (attributes):\n",
286 | "\n",
287 | "* `header` : corresponds to the `` part of the HTML document,\n",
288 | "* `html` : corresponds to the `` part,\n",
289 | "* `script` : corresponds to a `\n"
311 | ]
312 | }
313 | ],
314 | "source": [
315 | "f = Figure()\n",
316 | "print(f.render())"
317 | ]
318 | },
319 | {
320 | "cell_type": "markdown",
321 | "metadata": {},
322 | "source": [
323 | "You can for example create a beatiful cyan \"hello-world\" webpage in doing:"
324 | ]
325 | },
326 | {
327 | "cell_type": "code",
328 | "execution_count": 12,
329 | "metadata": {
330 | "collapsed": false
331 | },
332 | "outputs": [
333 | {
334 | "name": "stdout",
335 | "output_type": "stream",
336 | "text": [
337 | "\n",
338 | " \n",
339 | " \n",
340 | " \n",
341 | "\n",
342 | " \n",
343 | " Hello world \n",
344 | "\n",
345 | "\n"
347 | ]
348 | }
349 | ],
350 | "source": [
351 | "f.header.add_child(Element(\"\"))\n",
352 | "f.html.add_child(Element(\"Hello world \"))\n",
353 | "print(f.render())"
354 | ]
355 | },
356 | {
357 | "cell_type": "markdown",
358 | "metadata": {},
359 | "source": [
360 | "You can simply save the content of the `Figure` to a file, thanks to the `save` method:"
361 | ]
362 | },
363 | {
364 | "cell_type": "code",
365 | "execution_count": 13,
366 | "metadata": {
367 | "collapsed": false
368 | },
369 | "outputs": [
370 | {
371 | "name": "stdout",
372 | "output_type": "stream",
373 | "text": [
374 | "\n",
375 | " \n",
376 | " \n",
377 | " \n",
378 | "\n",
379 | " \n",
380 | " Hello world \n",
381 | "\n",
382 | "\n"
384 | ]
385 | }
386 | ],
387 | "source": [
388 | "f.save('foo.html')\n",
389 | "print(open('foo.html').read())"
390 | ]
391 | },
392 | {
393 | "cell_type": "markdown",
394 | "metadata": {},
395 | "source": [
396 | "If you want to visualize it in the notebook, you can let `Figure._repr_html_` method do it's job in typing: "
397 | ]
398 | },
399 | {
400 | "cell_type": "code",
401 | "execution_count": 14,
402 | "metadata": {
403 | "collapsed": false
404 | },
405 | "outputs": [
406 | {
407 | "data": {
408 | "text/html": [
409 | ""
410 | ],
411 | "text/plain": [
412 | ""
413 | ]
414 | },
415 | "execution_count": 14,
416 | "metadata": {},
417 | "output_type": "execute_result"
418 | }
419 | ],
420 | "source": [
421 | "f"
422 | ]
423 | },
424 | {
425 | "cell_type": "markdown",
426 | "metadata": {},
427 | "source": [
428 | "If this rendering is too large for you, you can force it's width and height:"
429 | ]
430 | },
431 | {
432 | "cell_type": "code",
433 | "execution_count": 15,
434 | "metadata": {
435 | "collapsed": false
436 | },
437 | "outputs": [
438 | {
439 | "data": {
440 | "text/html": [
441 | ""
442 | ],
443 | "text/plain": [
444 | ""
445 | ]
446 | },
447 | "execution_count": 15,
448 | "metadata": {},
449 | "output_type": "execute_result"
450 | }
451 | ],
452 | "source": [
453 | "f.width = 300\n",
454 | "f.height = 200\n",
455 | "f"
456 | ]
457 | },
458 | {
459 | "cell_type": "markdown",
460 | "metadata": {},
461 | "source": [
462 | "Note that you can also define a `Figure`'s size in a matplotlib way:"
463 | ]
464 | },
465 | {
466 | "cell_type": "code",
467 | "execution_count": 16,
468 | "metadata": {
469 | "collapsed": false
470 | },
471 | "outputs": [
472 | {
473 | "data": {
474 | "text/html": [
475 | ""
476 | ],
477 | "text/plain": [
478 | ""
479 | ]
480 | },
481 | "execution_count": 16,
482 | "metadata": {},
483 | "output_type": "execute_result"
484 | }
485 | ],
486 | "source": [
487 | "Figure(figsize=(5,5))"
488 | ]
489 | },
490 | {
491 | "cell_type": "markdown",
492 | "metadata": {},
493 | "source": [
494 | "## MacroElement"
495 | ]
496 | },
497 | {
498 | "cell_type": "markdown",
499 | "metadata": {},
500 | "source": [
501 | "It happens you need to create elements that have multiple effects on a Figure. For this, you can use `MacroElement` whose template contains macros ; each macro writes something into the parent Figure's header, body and script."
502 | ]
503 | },
504 | {
505 | "cell_type": "code",
506 | "execution_count": 17,
507 | "metadata": {
508 | "collapsed": false
509 | },
510 | "outputs": [
511 | {
512 | "name": "stdout",
513 | "output_type": "stream",
514 | "text": [
515 | "\n",
516 | " \n",
517 | " \n",
518 | " This is header of macro_element_ea36a310ab8a4212a8c7ca754a4140fc\n",
519 | "\n",
520 | " \n",
521 | " This is html of macro_element_ea36a310ab8a4212a8c7ca754a4140fc\n",
522 | "\n",
523 | "\n"
526 | ]
527 | }
528 | ],
529 | "source": [
530 | "macro = MacroElement()\n",
531 | "macro._template = Template(\n",
532 | " '{% macro header(this, kwargs) %}'\n",
533 | " 'This is header of {{this.get_name()}}'\n",
534 | " '{% endmacro %}'\n",
535 | "\n",
536 | " '{% macro html(this, kwargs) %}'\n",
537 | " 'This is html of {{this.get_name()}}'\n",
538 | " '{% endmacro %}'\n",
539 | "\n",
540 | " '{% macro script(this, kwargs) %}'\n",
541 | " 'This is script of {{this.get_name()}}'\n",
542 | " '{% endmacro %}'\n",
543 | " )\n",
544 | "\n",
545 | "print(Figure().add_child(macro).render())"
546 | ]
547 | },
548 | {
549 | "cell_type": "markdown",
550 | "metadata": {},
551 | "source": [
552 | "## Link"
553 | ]
554 | },
555 | {
556 | "cell_type": "markdown",
557 | "metadata": {},
558 | "source": [
559 | "To embed javascript and css links in the header, you can use these class:"
560 | ]
561 | },
562 | {
563 | "cell_type": "code",
564 | "execution_count": 18,
565 | "metadata": {
566 | "collapsed": false
567 | },
568 | "outputs": [
569 | {
570 | "data": {
571 | "text/plain": [
572 | "''"
573 | ]
574 | },
575 | "execution_count": 18,
576 | "metadata": {},
577 | "output_type": "execute_result"
578 | }
579 | ],
580 | "source": [
581 | "js_link = JavascriptLink('https://example.com/javascript.js')\n",
582 | "js_link.render()"
583 | ]
584 | },
585 | {
586 | "cell_type": "code",
587 | "execution_count": 19,
588 | "metadata": {
589 | "collapsed": false
590 | },
591 | "outputs": [
592 | {
593 | "data": {
594 | "text/plain": [
595 | "' '"
596 | ]
597 | },
598 | "execution_count": 19,
599 | "metadata": {},
600 | "output_type": "execute_result"
601 | }
602 | ],
603 | "source": [
604 | "css_link = CssLink('https://example.com/style.css')\n",
605 | "css_link.render()"
606 | ]
607 | },
608 | {
609 | "cell_type": "markdown",
610 | "metadata": {},
611 | "source": [
612 | "## Html"
613 | ]
614 | },
615 | {
616 | "cell_type": "markdown",
617 | "metadata": {},
618 | "source": [
619 | "An `Html` element enables you to create custom div to put in the *body* of your page."
620 | ]
621 | },
622 | {
623 | "cell_type": "code",
624 | "execution_count": 26,
625 | "metadata": {
626 | "collapsed": false
627 | },
628 | "outputs": [
629 | {
630 | "data": {
631 | "text/plain": [
632 | "'Hello world
'"
633 | ]
634 | },
635 | "execution_count": 26,
636 | "metadata": {},
637 | "output_type": "execute_result"
638 | }
639 | ],
640 | "source": [
641 | "html = Html('Hello world')\n",
642 | "html.render()"
643 | ]
644 | },
645 | {
646 | "cell_type": "markdown",
647 | "metadata": {},
648 | "source": [
649 | "It's designed to render the text *as you gave it*, so it won't work directly it you want to embed HTML code inside the div."
650 | ]
651 | },
652 | {
653 | "cell_type": "code",
654 | "execution_count": 25,
655 | "metadata": {
656 | "collapsed": false
657 | },
658 | "outputs": [
659 | {
660 | "data": {
661 | "text/plain": [
662 | "'<b>Hello world</b>
'"
663 | ]
664 | },
665 | "execution_count": 25,
666 | "metadata": {},
667 | "output_type": "execute_result"
668 | }
669 | ],
670 | "source": [
671 | "Html('Hello world ').render()"
672 | ]
673 | },
674 | {
675 | "cell_type": "markdown",
676 | "metadata": {},
677 | "source": [
678 | "For this, you have to set `script=True` and it will work:"
679 | ]
680 | },
681 | {
682 | "cell_type": "code",
683 | "execution_count": 28,
684 | "metadata": {
685 | "collapsed": false
686 | },
687 | "outputs": [
688 | {
689 | "data": {
690 | "text/plain": [
691 | "'Hello world
'"
692 | ]
693 | },
694 | "execution_count": 28,
695 | "metadata": {},
696 | "output_type": "execute_result"
697 | }
698 | ],
699 | "source": [
700 | "Html('Hello world ', script=True).render()"
701 | ]
702 | },
703 | {
704 | "cell_type": "markdown",
705 | "metadata": {},
706 | "source": [
707 | "## IFrame"
708 | ]
709 | },
710 | {
711 | "cell_type": "markdown",
712 | "metadata": {},
713 | "source": [
714 | "If you need to embed a full webpage (with separate javascript environment), you can use `IFrame`."
715 | ]
716 | },
717 | {
718 | "cell_type": "code",
719 | "execution_count": 21,
720 | "metadata": {
721 | "collapsed": false
722 | },
723 | "outputs": [
724 | {
725 | "data": {
726 | "text/plain": [
727 | "''"
728 | ]
729 | },
730 | "execution_count": 21,
731 | "metadata": {},
732 | "output_type": "execute_result"
733 | }
734 | ],
735 | "source": [
736 | "iframe = IFrame('Hello World')\n",
737 | "iframe.render()"
738 | ]
739 | },
740 | {
741 | "cell_type": "markdown",
742 | "metadata": {},
743 | "source": [
744 | "As you can see, it will embed the full content of the iframe in a *base64* string so that the ouput looks like:"
745 | ]
746 | },
747 | {
748 | "cell_type": "code",
749 | "execution_count": 22,
750 | "metadata": {
751 | "collapsed": false
752 | },
753 | "outputs": [
754 | {
755 | "data": {
756 | "text/html": [
757 | ""
758 | ],
759 | "text/plain": [
760 | ""
761 | ]
762 | },
763 | "execution_count": 22,
764 | "metadata": {},
765 | "output_type": "execute_result"
766 | }
767 | ],
768 | "source": [
769 | "f = Figure(height=180)\n",
770 | "f.html.add_child(Element(\"Before the frame\"))\n",
771 | "f.html.add_child(IFrame('In the frame', height='100px'))\n",
772 | "f.html.add_child(Element(\"After the frame\"))\n",
773 | "f"
774 | ]
775 | },
776 | {
777 | "cell_type": "markdown",
778 | "metadata": {},
779 | "source": [
780 | "## Div"
781 | ]
782 | },
783 | {
784 | "cell_type": "markdown",
785 | "metadata": {},
786 | "source": [
787 | "At last, you have the `Div` element that behaves almost like `Html` with a few differences:\n",
788 | "\n",
789 | "* The style is put in the header, while `Html`'s style is embedded inline.\n",
790 | "* `Div` inherits from `MacroElement` so that:\n",
791 | " * It cannot be rendered unless it's embedded in a `Figure`.\n",
792 | " * It is a useful object toinherit from when you create new classes."
793 | ]
794 | },
795 | {
796 | "cell_type": "code",
797 | "execution_count": 29,
798 | "metadata": {
799 | "collapsed": false
800 | },
801 | "outputs": [
802 | {
803 | "name": "stdout",
804 | "output_type": "stream",
805 | "text": [
806 | "\n",
807 | " \n",
808 | " \n",
809 | " \n",
816 | "\n",
817 | " \n",
818 | " Hello world
\n",
819 | "\n",
820 | "\n"
822 | ]
823 | }
824 | ],
825 | "source": [
826 | "div = Div()\n",
827 | "div.html.add_child(Element('Hello world'))\n",
828 | "print(Figure().add_child(div).render())"
829 | ]
830 | }
831 | ],
832 | "metadata": {
833 | "kernelspec": {
834 | "display_name": "Python 3",
835 | "language": "python",
836 | "name": "python3"
837 | },
838 | "language_info": {
839 | "codemirror_mode": {
840 | "name": "ipython",
841 | "version": 3
842 | },
843 | "file_extension": ".py",
844 | "mimetype": "text/x-python",
845 | "name": "python",
846 | "nbconvert_exporter": "python",
847 | "pygments_lexer": "ipython3",
848 | "version": "3.5.1"
849 | }
850 | },
851 | "nbformat": 4,
852 | "nbformat_minor": 0
853 | }
854 |
--------------------------------------------------------------------------------