pax_global_header 0000666 0000000 0000000 00000000064 14716633541 0014524 g ustar 00root root 0000000 0000000 52 comment=0fb720f0f29977813a734eb7e1bb17390786d25a
branca-0.8.1/ 0000775 0000000 0000000 00000000000 14716633541 0012760 5 ustar 00root root 0000000 0000000 branca-0.8.1/.github/ 0000775 0000000 0000000 00000000000 14716633541 0014320 5 ustar 00root root 0000000 0000000 branca-0.8.1/.github/workflows/ 0000775 0000000 0000000 00000000000 14716633541 0016355 5 ustar 00root root 0000000 0000000 branca-0.8.1/.github/workflows/deploy-docs.yml 0000664 0000000 0000000 00000002003 14716633541 0021315 0 ustar 00root root 0000000 0000000
name: Documentation
on:
pull_request:
push:
branches:
- main
release:
types:
- published
jobs:
build-docs:
runs-on: ubuntu-latest
steps:
- name: checkout
uses: actions/checkout@v3
with:
fetch-depth: 0
- name: Setup Micromamba
uses: mamba-org/setup-micromamba@v1
with:
environment-name: TEST
init-shell: bash
create-args: >-
python=3 --file requirements.txt --file requirements-dev.txt --channel conda-forge
- name: Install branca
shell: bash -l {0}
run: |
python -m pip install -e . --no-deps --force-reinstall
- name: Build documentation
shell: bash -l {0}
run: >
set -e
&& pushd docs
&& make clean html linkcheck
&& popd
- name: Deploy
if: success() && github.event_name == 'release'
uses: peaceiris/actions-gh-pages@v3
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
publish_dir: docs/build/html
branca-0.8.1/.github/workflows/pypi.yml 0000664 0000000 0000000 00000002106 14716633541 0020060 0 ustar 00root root 0000000 0000000
name: Publish to PyPI
on:
pull_request:
push:
branches:
- main
release:
types:
- published
defaults:
run:
shell: bash
jobs:
packages:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: "3.x"
- name: Get tags
run: git fetch --depth=1 origin +refs/tags/*:refs/tags/*
- name: Install build tools
run: |
python -m pip install --upgrade pip build wheel twine check-manifest
- name: Build binary wheel
run: python -m build --sdist --wheel . --outdir dist
- name: CheckFiles
run: |
check-manifest --verbose
ls dist
- name: Test wheels
run: |
cd dist && python -m pip install branca*.whl
python -m twine check *
- name: Publish a Python distribution to PyPI
if: success() && github.event_name == 'release'
uses: pypa/gh-action-pypi-publish@release/v1
with:
user: __token__
password: ${{ secrets.PYPI_PASSWORD }}
branca-0.8.1/.github/workflows/test_code.yml 0000664 0000000 0000000 00000001532 14716633541 0021052 0 ustar 00root root 0000000 0000000 name: Code Tests
on:
pull_request:
push:
branches: [main]
jobs:
run:
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, windows-latest]
python-version: ["3.8", "3.12"]
steps:
- uses: actions/checkout@v3
- name: Setup Micromamba Python ${{ matrix.python-version }}
uses: mamba-org/setup-micromamba@v1
with:
environment-name: TEST
init-shell: bash
create-args: >-
python=${{ matrix.python-version }} pip --file requirements.txt --file requirements-dev.txt --channel conda-forge
- name: Install branca
shell: bash -l {0}
run: |
python -m pip install -e . --no-deps --force-reinstall
- name: Tests
shell: bash -l {0}
run: |
python -m pytest -vv -rxs tests -m "not headless"
branca-0.8.1/.github/workflows/test_code_notebooks.yml 0000664 0000000 0000000 00000001233 14716633541 0023133 0 ustar 00root root 0000000 0000000 name: Notebook Tests
on:
pull_request:
push:
branches: [main]
jobs:
run:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Setup Micromamba Python 3
uses: mamba-org/setup-micromamba@v1
with:
environment-name: TEST
init-shell: bash
create-args: >-
python=3 pip --file requirements.txt --file requirements-dev.txt --channel conda-forge
- name: Install branca
shell: bash -l {0}
run: |
python -m pip install -e . --no-deps --force-reinstall
- name: Notebook tests
shell: bash -l {0}
run: |
python -m pytest --nbval-lax examples
branca-0.8.1/.github/workflows/test_mypy.yml 0000664 0000000 0000000 00000001161 14716633541 0021134 0 ustar 00root root 0000000 0000000 name: Mypy type hint checks
on:
pull_request:
push:
branches:
- main
jobs:
run:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Micromamba env
uses: mamba-org/setup-micromamba@v1
with:
environment-name: TEST
create-args: >-
python=3
--file requirements.txt
--file requirements-dev.txt
- name: Install branca from source
shell: bash -l {0}
run: |
python -m pip install -e . --no-deps --force-reinstall
- name: Mypy test
shell: bash -l {0}
run: |
mypy branca
branca-0.8.1/.github/workflows/test_selenium.yml 0000664 0000000 0000000 00000001220 14716633541 0021753 0 ustar 00root root 0000000 0000000 name: Headless Tests
on:
pull_request:
push:
branches: [main]
jobs:
run:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Setup Micromamba Python 3
uses: mamba-org/setup-micromamba@v1
with:
environment-name: TEST
init-shell: bash
create-args: >-
python=3 pip --file requirements.txt --file requirements-dev.txt --channel conda-forge
- name: Install branca
shell: bash -l {0}
run: |
python -m pip install -e . --no-deps --force-reinstall
- name: Tests
shell: bash -l {0}
run: |
pytest -vv -rxs tests -m "headless"
branca-0.8.1/.gitignore 0000664 0000000 0000000 00000001070 14716633541 0014746 0 ustar 00root root 0000000 0000000 *.py[cod]
# C extensions
*.so
# Packages
*.egg
*.egg-info
dist
build
eggs
parts
bin
var
sdist
develop-eggs
.installed.cfg
lib
lib64
include
# Installer logs
pip-log.txt
# Unit test / coverage reports
.coverage
.tox
nosetests.xml
# Translations
*.mo
# Mr Developer
.mr.developer.cfg
.project
.pydevproject
#Mac
*.DS_Store
# IPython Notebook Checkpoints
.ipynb_checkpoints
#Virtualenv
ENV
.env
# Tests products
.cache
data.png
map.html
examples/foo.html
# documentation builds
docs/_build
geckodriver.exe
geckodriver.log
# Pycharm
.idea/
branca/_version.py
branca-0.8.1/.pre-commit-config.yaml 0000664 0000000 0000000 00000002274 14716633541 0017246 0 ustar 00root root 0000000 0000000 repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v5.0.0
hooks:
- id: trailing-whitespace
- id: check-ast
- id: debug-statements
- id: end-of-file-fixer
- id: check-docstring-first
- id: requirements-txt-fixer
- id: file-contents-sorter
files: requirements-dev.txt
- repo: https://github.com/PyCQA/flake8
rev: 7.1.1
hooks:
- id: flake8
exclude: docs/source/conf.py
args: [--max-line-length=105]
- repo: https://github.com/pycqa/isort
rev: 5.13.2
hooks:
- id: isort
additional_dependencies: [toml]
args: ["--profile", "black", "--filter-files"]
- repo: https://github.com/psf/black
rev: 24.10.0
hooks:
- id: black
language_version: python3
- repo: https://github.com/keewis/blackdoc
rev: v0.3.9
hooks:
- id: blackdoc
- repo: https://github.com/codespell-project/codespell
rev: v2.3.0
hooks:
- id: codespell
args:
- --ignore-words-list=thex
- repo: https://github.com/asottile/pyupgrade
rev: v3.19.0
hooks:
- id: pyupgrade
args:
- --py36-plus
- repo: https://github.com/asottile/add-trailing-comma
rev: v3.1.0
hooks:
- id: add-trailing-comma
branca-0.8.1/CHANGES.txt 0000664 0000000 0000000 00000004242 14716633541 0014573 0 ustar 00root root 0000000 0000000 0.7.0
~~~~~
- Make all Element with Template pickable natively (@BastienGauthier #144)
- Make _parse_size robust to already parsed values (@BastienGauthier #142)
- StepColormap: inclusive lower bound (@MxMartin #141)
- Add color schemes: plasma, inferno, magma (@FlorinAndrei #131)
- Allow branca ColorMap in write_png (@Conengmo #126)
- More flexible _parse_size (@Conengmo #125)
0.6.0
~~~~~
- Properly escape colormap caption (@Conengmo #117)
- Multiple fixes in color_brewer (@ajabep #115)
- Expose colorbar size variables (@Conengmo #77)
- Proper html tags in Figure template (@desrod #67)
- Make Element class pickleable (@bwest2397 #99)
- Improve colorbar representation in notebooks (@HaudinFlorence #110)
- Allow custom ticks on colorbar (@kota7 #113)
0.5.0
~~~~~
- Support for Pathlib when saving an `Element` (@wd60622 #103)
- Faster UUID generation for `Element` id (@bwest2397 #101)
- Store html content in `srcdoc` instead of `data-html` (@dstein64 #96)
- Add `max_labels` argument to color maps (@martinfleis #90)
- Pass caption when converting colormap to steps (@ndswaef #87)
0.4.2
~~~~~
- Fix special char encoding in notebooks, store as percent-encoded (@conengmo #76)
0.4.1
~~~~~
- Prompt Jupyter users to trust notebook (@conengmo #75)
- Removed Python 2 specific code (@ocefpaf #69)
0.4.0
~~~~~
- Dropped Python 2 support
- Store html content in a data-html attribute (#66)
- Colormap alpha #64
- Fix caption being propagated in scale functions #62
- Assert color type in color_brewer #52
0.3.1
~~~~~
- Added viridis scheme #47 (GillesC)
- Fixed testing, auto PyPI upload, and docs
0.3.0
~~~~~
- Add title to Figure (@fitoprincipe #33 and #39)
- Move templates to class attributes (@psarka #34 and #38)
- Explicit color support for range of ``n``
and diverging colormaps (@nanodan #29)
- Added class for hosting step colormap (@matsavage #25)
0.2.0
~~~~~
- Correct rendering utf-8 IFrame (@knil-sama #18)
- Remove embedded IFrame's border (@deelaka #17)
- Let IFrame contents go fullscreen (@sanga #13)
- Add HTML Popup Class to element.py (@samchorlton #6)
0.1.0
~~~~~
- Separate branca from folium (@bibmartin d678357)
- Enable HTML embedding in Html (@samchorlton 90f6b13)
branca-0.8.1/LICENSE.txt 0000664 0000000 0000000 00000002067 14716633541 0014610 0 ustar 00root root 0000000 0000000 Copyright (C) 2013, Martin Journois
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
branca-0.8.1/MANIFEST.in 0000664 0000000 0000000 00000000574 14716633541 0014524 0 ustar 00root root 0000000 0000000 include *.txt
include README.md
include branca/_cnames.json
include branca/_schemes.json
include branca/scheme_info.json
include branca/scheme_base_codes.json
include pyproject.toml
graft branca
prune docs
prune tests
prune examples
prune *.egg-info
exclude *.yml
exclude .pre-commit-config.yaml
exclude .gitignore
exclude .isort.cfg
exclude branca/_version.py
exclude .github
branca-0.8.1/README.md 0000664 0000000 0000000 00000001330 14716633541 0014234 0 ustar 00root root 0000000 0000000 [](https://pypi.python.org/pypi/branca)
[](https://github.com/python-visualization/branca/actions/workflows/test_code.yml)
[](https://gitter.im/python-visualization/folium)
# Branca
This library is a spinoff from [folium](https://github.com/python-visualization/folium). It can be used to generate HTML + JS. It is based on Jinja2.
- Documentation: https://python-visualization.github.io/branca/
- Examples: https://nbviewer.org/github/python-visualization/branca/tree/main/examples/
branca-0.8.1/branca/ 0000775 0000000 0000000 00000000000 14716633541 0014206 5 ustar 00root root 0000000 0000000 branca-0.8.1/branca/__init__.py 0000664 0000000 0000000 00000000317 14716633541 0016320 0 ustar 00root root 0000000 0000000 import branca.colormap as colormap
import branca.element as element
try:
from ._version import __version__
except ImportError:
__version__ = "unknown"
__all__ = [
"colormap",
"element",
]
branca-0.8.1/branca/_cnames.json 0000664 0000000 0000000 00000006724 14716633541 0016517 0 ustar 00root root 0000000 0000000 {"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-0.8.1/branca/_schemes.json 0000664 0000000 0000000 00000051310 14716633541 0016667 0 ustar 00root root 0000000 0000000 {"viridis":["#440154","#481567","#482677","#453781","#404788","#39568C","#33638D","#2D708E","#287D8E","#238A8D","#1F968B","#20A387","#29AF7F","#3CBB75","#55C667","#73D055","#95D840","#B8DE29","#DCE319","#FDE725"],"plasma":["#0D0887","#2C0594","#43039E","#5901A5","#6E00A8","#8305A7","#9511A1","#A72197","#B6308B","#C5407E","#D14E72","#DD5E66","#E76E5B","#F07F4F","#F79044","#FCA338","#FEB72D","#FCCD25","#F7E225","#F0F921"],"inferno":["#000004","#08051D","#180C3C","#2F0A5B","#450A69","#5C126E","#71196E","#87216B","#9B2964","#B1325A","#C43C4E","#D74B3F","#E55C30","#F1711F","#F8870E","#FCA108","#FBBA1F","#F6D543","#F1ED71","#FCFFA4"],"magma":["#000004","#07061C","#150E38","#29115A","#3F0F72","#56147D","#6A1C81","#802582","#942C80","#AB337C","#C03A76","#D6456C","#E85362","#F4695C","#FA815F","#FD9B6B","#FEB47B","#FECD90","#FDE5A7","#FCFDBF"],"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-0.8.1/branca/colormap.py 0000664 0000000 0000000 00000055000 14716633541 0016374 0 ustar 00root root 0000000 0000000 """
Colormap
--------
Utility module for dealing with colormaps.
"""
import json
import math
import os
from typing import Dict, List, Optional, Sequence, Tuple, Union
from jinja2 import Template
from branca.element import ENV, Figure, JavascriptLink, MacroElement
from branca.utilities import legend_scaler
rootpath: str = os.path.abspath(os.path.dirname(__file__))
with open(os.path.join(rootpath, "_cnames.json")) as f:
_cnames: Dict[str, str] = json.loads(f.read())
with open(os.path.join(rootpath, "_schemes.json")) as f:
_schemes: Dict[str, List[str]] = json.loads(f.read())
TypeRGBInts = Tuple[int, int, int]
TypeRGBFloats = Tuple[float, float, float]
TypeRGBAInts = Tuple[int, int, int, int]
TypeRGBAFloats = Tuple[float, float, float, float]
TypeAnyColorType = Union[TypeRGBInts, TypeRGBFloats, TypeRGBAInts, TypeRGBAFloats, str]
def _is_hex(x: str) -> bool:
return x.startswith("#") and len(x) == 7
def _parse_hex(color_code: str) -> TypeRGBAFloats:
return (
_color_int_to_float(int(color_code[1:3], 16)),
_color_int_to_float(int(color_code[3:5], 16)),
_color_int_to_float(int(color_code[5:7], 16)),
1.0,
)
def _color_int_to_float(x: int) -> float:
"""Convert an integer between 0 and 255 to a float between 0. and 1.0"""
return x / 255.0
def _color_float_to_int(x: float) -> int:
"""Convert a float between 0. and 1.0 to an integer between 0 and 255"""
return int(x * 255.9999)
def _parse_color(x: Union[tuple, list, str]) -> TypeRGBAFloats:
if isinstance(x, (tuple, list)):
return tuple(tuple(x) + (1.0,))[:4] # type: ignore
elif isinstance(x, str) and _is_hex(x):
return _parse_hex(x)
elif isinstance(x, str):
cname = _cnames.get(x.lower(), None)
if cname is None:
raise ValueError(f"Unknown color {cname!r}.")
return _parse_hex(cname)
else:
raise ValueError(f"Unrecognized color code {x!r}")
def _base(x: float) -> float:
if x > 0:
base = pow(10, math.floor(math.log10(x)))
return round(x / base) * base
else:
return 0
class ColorMap(MacroElement):
"""A generic class for creating colormaps.
Parameters
----------
vmin: float
The left bound of the color scale.
vmax: float
The right bound of the color scale.
caption: str
A caption to draw with the colormap.
text_color: str, default "black"
The color for the text.
max_labels : int, default 10
Maximum number of legend tick labels
"""
_template: Template = ENV.get_template("color_scale.js")
def __init__(
self,
vmin: float = 0.0,
vmax: float = 1.0,
caption: str = "",
text_color: str = "black",
max_labels: int = 10,
):
super().__init__()
self._name = "ColorMap"
self.vmin = vmin
self.vmax = vmax
self.caption = caption
self.text_color = text_color
self.index: List[float] = [vmin, vmax]
self.max_labels = max_labels
self.tick_labels: Optional[Sequence[Union[float, str]]] = None
self.width = 450
self.height = 40
def render(self, **kwargs):
"""Renders the HTML representation of the element."""
self.color_domain = [
float(self.vmin + (self.vmax - self.vmin) * k / 499.0) for k in range(500)
]
self.color_range = [self.__call__(x) for x in self.color_domain]
# sanitize possible numpy floats to native python floats
self.index = [float(i) for i in self.index]
if self.tick_labels is None:
self.tick_labels = legend_scaler(self.index, self.max_labels)
super().render(**kwargs)
figure = self.get_root()
assert isinstance(figure, Figure), (
"You cannot render this Element " "if it is not in a Figure."
)
figure.header.add_child(
JavascriptLink("https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.5/d3.min.js"),
name="d3",
) # noqa
def rgba_floats_tuple(self, x: float) -> TypeRGBAFloats:
"""
This class has to be implemented for each class inheriting from
Colormap. This has to be a function of the form float ->
(float, float, float, float) describing for each input float x,
the output color in RGBA format;
Each output value being between 0 and 1.
"""
raise NotImplementedError
def rgba_bytes_tuple(self, x: float) -> TypeRGBAInts:
"""Provides the color corresponding to value `x` in the
form of a tuple (R,G,B,A) with int values between 0 and 255.
"""
return tuple(_color_float_to_int(u) for u in self.rgba_floats_tuple(x)) # type: ignore
def rgb_bytes_tuple(self, x: float) -> TypeRGBInts:
"""Provides the color corresponding to value `x` in the
form of a tuple (R,G,B) with int values between 0 and 255.
"""
return self.rgba_bytes_tuple(x)[:3]
def rgb_hex_str(self, x: float) -> str:
"""Provides the color corresponding to value `x` in the
form of a string of hexadecimal values "#RRGGBB".
"""
return "#%02x%02x%02x" % self.rgb_bytes_tuple(x)
def rgba_hex_str(self, x: float) -> str:
"""Provides the color corresponding to value `x` in the
form of a string of hexadecimal values "#RRGGBBAA".
"""
return "#%02x%02x%02x%02x" % self.rgba_bytes_tuple(x)
def __call__(self, x: float) -> str:
"""Provides the color corresponding to value `x` in the
form of a string of hexadecimal values "#RRGGBBAA".
"""
return self.rgba_hex_str(x)
def _repr_html_(self) -> str:
"""Display the colormap in a Jupyter Notebook.
Does not support all the class arguments.
"""
nb_ticks = 7
delta_x = math.floor(self.width / (nb_ticks - 1))
x_ticks = [(i) * delta_x for i in range(0, nb_ticks)]
delta_val = delta_x * (self.vmax - self.vmin) / self.width
val_ticks = [round(self.vmin + (i) * delta_val, 1) for i in range(0, nb_ticks)]
return (
f'"
)
class LinearColormap(ColorMap):
"""Creates a ColorMap based on linear interpolation of a set of colors
over a given index.
Parameters
----------
colors : list-like object with at least two colors.
The set of colors to be used for interpolation.
Colors can be provided in the form:
* tuples of RGBA ints between 0 and 255 (e.g: `(255, 255, 0)` or
`(255, 255, 0, 255)`)
* tuples of RGBA floats between 0. and 1. (e.g: `(1.,1.,0.)` or
`(1., 1., 0., 1.)`)
* HTML-like string (e.g: `"#ffff00`)
* a color name or shortcut (e.g: `"y"` or `"yellow"`)
index : list of floats, default None
The values corresponding to each color.
It has to be sorted, and have the same length as `colors`.
If None, a regular grid between `vmin` and `vmax` is created.
vmin : float, default 0.
The minimal value for the colormap.
Values lower than `vmin` will be bound directly to `colors[0]`.
vmax : float, default 1.
The maximal value for the colormap.
Values higher than `vmax` will be bound directly to `colors[-1]`.
caption: str
A caption to draw with the colormap.
text_color: str, default "black"
The color for the text.
max_labels : int, default 10
Maximum number of legend tick labels
tick_labels: list of floats, default None
If given, used as the positions of ticks."""
def __init__(
self,
colors: Sequence[TypeAnyColorType],
index: Optional[Sequence[float]] = None,
vmin: float = 0.0,
vmax: float = 1.0,
caption: str = "",
text_color: str = "black",
max_labels: int = 10,
tick_labels: Optional[Sequence[float]] = None,
):
super().__init__(
vmin=vmin,
vmax=vmax,
caption=caption,
text_color=text_color,
max_labels=max_labels,
)
self.tick_labels: Optional[Sequence[float]] = tick_labels
n = len(colors)
if n < 2:
raise ValueError("You must provide at least 2 colors.")
if index is None:
self.index = [vmin + (vmax - vmin) * i * 1.0 / (n - 1) for i in range(n)]
else:
self.index = list(index)
self.colors: List[TypeRGBAFloats] = [_parse_color(x) for x in colors]
def rgba_floats_tuple(self, x: float) -> TypeRGBAFloats:
"""Provides the color corresponding to value `x` in the
form of a tuple (R,G,B,A) with float values between 0. and 1.
"""
if x <= self.index[0]:
return self.colors[0]
if x >= self.index[-1]:
return self.colors[-1]
i = len([u for u in self.index if u < x]) # 0 < i < n.
if self.index[i - 1] < self.index[i]:
p = (x - self.index[i - 1]) * 1.0 / (self.index[i] - self.index[i - 1])
elif self.index[i - 1] == self.index[i]:
p = 1.0
else:
raise ValueError("Thresholds are not sorted.")
return tuple( # type: ignore
(1.0 - p) * self.colors[i - 1][j] + p * self.colors[i][j] for j in range(4)
)
def to_step(
self,
n: Optional[int] = None,
index: Optional[Sequence[float]] = None,
data: Optional[Sequence[float]] = None,
method: str = "linear",
quantiles: Optional[Sequence[float]] = None,
round_method: Optional[str] = None,
max_labels: int = 10,
) -> "StepColormap":
"""Splits the LinearColormap into a StepColormap.
Parameters
----------
n : int, default None
The number of expected colors in the output StepColormap.
This will be ignored if `index` is provided.
index : list of floats, default None
The values corresponding to each color bounds.
It has to be sorted.
If None, a regular grid between `vmin` and `vmax` is created.
data : list of floats, default None
A sample of data to adapt the color map to.
method : str, default 'linear'
The method used to create data-based colormap.
It can be 'linear' for linear scale, 'log' for logarithmic,
or 'quant' for data's quantile-based scale.
quantiles : list of floats, default None
Alternatively, you can provide explicitly the quantiles you
want to use in the scale.
round_method : str, default None
The method used to round thresholds.
* If 'int', all values will be rounded to the nearest integer.
* If 'log10', all values will be rounded to the nearest
order-of-magnitude integer. For example, 2100 is rounded to
2000, 2790 to 3000.
max_labels : int, default 10
Maximum number of legend tick labels
Returns
-------
A StepColormap with `n=len(index)-1` colors.
Examples:
>> lc.to_step(n=12)
>> lc.to_step(index=[0, 2, 4, 6, 8, 10])
>> lc.to_step(data=some_list, n=12)
>> lc.to_step(data=some_list, n=12, method='linear')
>> lc.to_step(data=some_list, n=12, method='log')
>> lc.to_step(data=some_list, n=12, method='quantiles')
>> lc.to_step(data=some_list, quantiles=[0, 0.3, 0.7, 1])
>> lc.to_step(data=some_list, quantiles=[0, 0.3, 0.7, 1],
... round_method='log10')
"""
msg = "You must specify either `index` or `n`"
if index is None:
if data is None:
if n is None:
raise ValueError(msg)
else:
index = [
self.vmin + (self.vmax - self.vmin) * i * 1.0 / n
for i in range(1 + n)
]
scaled_cm = self
else:
max_ = max(data)
min_ = min(data)
scaled_cm = self.scale(vmin=min_, vmax=max_)
method = "quantiles" if quantiles is not None else method
if method.lower().startswith("lin"):
if n is None:
raise ValueError(msg)
index = [min_ + i * (max_ - min_) * 1.0 / n for i in range(1 + n)]
elif method.lower().startswith("log"):
if n is None:
raise ValueError(msg)
if min_ <= 0:
msg = "Log-scale works only with strictly " "positive values."
raise ValueError(msg)
index = [
math.exp(
math.log(min_)
+ i * (math.log(max_) - math.log(min_)) * 1.0 / n,
)
for i in range(1 + n)
]
elif method.lower().startswith("quant"):
if quantiles is None:
if n is None:
msg = (
"You must specify either `index`, `n` or" "`quantiles`."
)
raise ValueError(msg)
else:
quantiles = [i * 1.0 / n for i in range(1 + n)]
p = len(data) - 1
s = sorted(data)
index = [
s[int(q * p)] * (1.0 - (q * p) % 1)
+ s[min(int(q * p) + 1, p)] * ((q * p) % 1)
for q in quantiles
]
else:
raise ValueError(f"Unknown method {method}")
else:
scaled_cm = self.scale(vmin=min(index), vmax=max(index))
n = len(index) - 1
if round_method == "int":
index = [round(x) for x in index]
if round_method == "log10":
index = [_base(x) for x in index]
colors = [
scaled_cm.rgba_floats_tuple(
index[i] * (1.0 - i / (n - 1.0)) + index[i + 1] * i / (n - 1.0),
)
for i in range(n)
]
caption = self.caption
text_color = self.text_color
return StepColormap(
colors,
index=index,
vmin=index[0],
vmax=index[-1],
caption=caption,
text_color=text_color,
max_labels=max_labels,
tick_labels=self.tick_labels,
)
def scale(
self,
vmin: float = 0.0,
vmax: float = 1.0,
max_labels: int = 10,
) -> "LinearColormap":
"""Transforms the colorscale so that the minimal and maximal values
fit the given parameters.
"""
return LinearColormap(
self.colors,
index=[
vmin + (vmax - vmin) * (x - self.vmin) * 1.0 / (self.vmax - self.vmin)
for x in self.index
], # noqa
vmin=vmin,
vmax=vmax,
caption=self.caption,
text_color=self.text_color,
max_labels=max_labels,
)
class StepColormap(ColorMap):
"""Creates a ColorMap based on linear interpolation of a set of colors
over a given index.
Parameters
----------
colors : list-like object
The set of colors to be used for interpolation.
Colors can be provided in the form:
* tuples of int between 0 and 255 (e.g: `(255,255,0)` or
`(255, 255, 0, 255)`)
* tuples of floats between 0. and 1. (e.g: `(1.,1.,0.)` or
`(1., 1., 0., 1.)`)
* HTML-like string (e.g: `"#ffff00`)
* a color name or shortcut (e.g: `"y"` or `"yellow"`)
index : list of floats, default None
The bounds of the colors. The lower value is inclusive,
the upper value is exclusive.
It has to be sorted, and have the same length as `colors`.
If None, a regular grid between `vmin` and `vmax` is created.
vmin : float, default 0.
The minimal value for the colormap.
Values lower than `vmin` will be bound directly to `colors[0]`.
vmax : float, default 1.
The maximal value for the colormap.
Values higher than `vmax` will be bound directly to `colors[-1]`.
caption: str
A caption to draw with the colormap.
text_color: str, default "black"
The color for the text.
max_labels : int, default 10
Maximum number of legend tick labels
tick_labels: list of floats, default None
If given, used as the positions of ticks.
"""
def __init__(
self,
colors: Sequence[TypeAnyColorType],
index: Optional[Sequence[float]] = None,
vmin: float = 0.0,
vmax: float = 1.0,
caption: str = "",
text_color: str = "black",
max_labels: int = 10,
tick_labels: Optional[Sequence[float]] = None,
):
super().__init__(
vmin=vmin,
vmax=vmax,
caption=caption,
text_color=text_color,
max_labels=max_labels,
)
self.tick_labels = tick_labels
n = len(colors)
if n < 1:
raise ValueError("You must provide at least 1 colors.")
if index is None:
self.index = [vmin + (vmax - vmin) * i * 1.0 / n for i in range(n + 1)]
else:
self.index = list(index)
self.colors: List[TypeRGBAFloats] = [_parse_color(x) for x in colors]
def rgba_floats_tuple(self, x: float) -> TypeRGBAFloats:
"""
Provides the color corresponding to value `x` in the
form of a tuple (R,G,B,A) with float values between 0. and 1.
"""
if x <= self.index[0]:
return self.colors[0]
if x >= self.index[-1]:
return self.colors[-1]
i = len([u for u in self.index if u <= x]) # 0 < i < n.
return self.colors[i - 1]
def to_linear(
self,
index: Optional[Sequence[float]] = None,
max_labels: int = 10,
) -> LinearColormap:
"""
Transforms the StepColormap into a LinearColormap.
Parameters
----------
index : list of floats, default None
The values corresponding to each color in the output colormap.
It has to be sorted.
If None, a regular grid between `vmin` and `vmax` is created.
max_labels : int, default 10
Maximum number of legend tick labels
"""
if index is None:
n = len(self.index) - 1
index = [
self.index[i] * (1.0 - i / (n - 1.0))
+ self.index[i + 1] * i / (n - 1.0)
for i in range(n)
]
colors = [self.rgba_floats_tuple(x) for x in index]
return LinearColormap(
colors,
index=index,
vmin=self.vmin,
vmax=self.vmax,
caption=self.caption,
text_color=self.text_color,
max_labels=max_labels,
)
def scale(
self,
vmin: float = 0.0,
vmax: float = 1.0,
max_labels: int = 10,
) -> "StepColormap":
"""Transforms the colorscale so that the minimal and maximal values
fit the given parameters.
"""
return StepColormap(
self.colors,
index=[
vmin + (vmax - vmin) * (x - self.vmin) * 1.0 / (self.vmax - self.vmin)
for x in self.index
], # noqa
vmin=vmin,
vmax=vmax,
caption=self.caption,
text_color=self.text_color,
max_labels=max_labels,
)
class _LinearColormaps:
"""A class for hosting the list of built-in linear colormaps."""
def __init__(self):
self._schemes = _schemes.copy()
self._colormaps = {key: LinearColormap(val) for key, val in _schemes.items()}
for key, val in _schemes.items():
setattr(self, key, LinearColormap(val))
def _repr_html_(self) -> str:
return Template(
"""
{% for key,val in this._colormaps.items() %}
{{key}}
{{val._repr_html_()}}
{% endfor %}
""",
).render(this=self)
linear = _LinearColormaps()
class _StepColormaps:
"""A class for hosting the list of built-in step colormaps."""
def __init__(self):
self._schemes = _schemes.copy()
self._colormaps = {key: StepColormap(val) for key, val in _schemes.items()}
for key, val in _schemes.items():
setattr(self, key, StepColormap(val))
def _repr_html_(self) -> str:
return Template(
"""
{% for key,val in this._colormaps.items() %}
{{key}}
{{val._repr_html_()}}
{% endfor %}
""",
).render(this=self)
step = _StepColormaps()
branca-0.8.1/branca/element.py 0000664 0000000 0000000 00000056432 14716633541 0016223 0 ustar 00root root 0000000 0000000 """
Element
-------
A generic class for creating Elements.
"""
import base64
import json
import warnings
from binascii import hexlify
from collections import OrderedDict
from html import escape
from os import urandom
from pathlib import Path
from typing import BinaryIO, List, Optional, Tuple, Type, Union
from urllib.request import urlopen
from jinja2 import Environment, PackageLoader, Template
from .utilities import TypeParseSize, _camelify, _parse_size, none_max, none_min
ENV = Environment(loader=PackageLoader("branca", "templates"))
class Element:
"""Basic Element object that does nothing.
Other Elements may inherit from this one.
Parameters
----------
template : str, default None
A jinaj2-compatible template string for rendering the element.
If None, template will be:
.. code-block:: jinja
{% for name, element in this._children.items() %}
{{element.render(**kwargs)}}
{% endfor %}
so that all the element's children are rendered.
template_name : str, default None
If no template is provided, you can also provide a filename.
"""
_template: Template = Template(
"{% for name, element in this._children.items() %}\n"
" {{element.render(**kwargs)}}"
"{% endfor %}",
)
def __init__(
self,
template: Optional[str] = None,
template_name: Optional[str] = None,
):
self._name: str = "Element"
self._id: str = hexlify(urandom(16)).decode()
self._children: OrderedDict[str, Element] = OrderedDict()
self._parent: Optional[Element] = None
self._template_str: Optional[str] = template
self._template_name: Optional[str] = template_name
if template is not None:
self._template = Template(template)
elif template_name is not None:
self._template = ENV.get_template(template_name)
def __getstate__(self) -> dict:
"""Modify object state when pickling the object.
jinja2 Templates cannot be pickled, so remove the instance attribute
if it exists. It will be added back when unpickling (see __setstate__).
"""
state: dict = self.__dict__.copy()
state.pop("_template", None)
return state
def __setstate__(self, state: dict):
"""Re-add _template instance attribute when unpickling"""
if state["_template_str"] is not None:
state["_template"] = Template(state["_template_str"])
elif state["_template_name"] is not None:
state["_template"] = ENV.get_template(state["_template_name"])
self.__dict__.update(state)
def get_name(self) -> str:
"""Returns a string representation of the object.
This string has to be unique and to be a python and
javascript-compatible
variable name.
"""
return _camelify(self._name) + "_" + self._id
def _get_self_bounds(self) -> List[List[Optional[float]]]:
"""Computes the bounds of the object itself (not including it's children)
in the form [[lat_min, lon_min], [lat_max, lon_max]]
"""
return [[None, None], [None, None]]
def get_bounds(self) -> List[List[Optional[float]]]:
"""Computes the bounds of the object and all it's children
in the form [[lat_min, lon_min], [lat_max, lon_max]].
"""
bounds = self._get_self_bounds()
for child in self._children.values():
child_bounds = child.get_bounds()
bounds = [
[
none_min(bounds[0][0], child_bounds[0][0]),
none_min(bounds[0][1], child_bounds[0][1]),
],
[
none_max(bounds[1][0], child_bounds[1][0]),
none_max(bounds[1][1], child_bounds[1][1]),
],
]
return bounds
def add_children(
self,
child: "Element",
name: Optional[str] = None,
index: Optional[int] = None,
) -> "Element":
"""Add a child."""
warnings.warn(
"Method `add_children` is deprecated. Please use `add_child` instead.",
FutureWarning,
stacklevel=2,
)
return self.add_child(child, name=name, index=index)
def add_child(
self,
child: "Element",
name: Optional[str] = None,
index: Optional[int] = None,
) -> "Element":
"""Add a child."""
if name is None:
name = child.get_name()
if index is None:
self._children[name] = child
else:
items = [item for item in self._children.items() if item[0] != name]
items.insert(int(index), (name, child))
self._children = OrderedDict(items)
child._parent = self
return self
def add_to(
self,
parent: "Element",
name: Optional[str] = None,
index: Optional[int] = None,
) -> "Element":
"""Add element to a parent."""
parent.add_child(self, name=name, index=index)
return self
def to_dict(
self,
depth: int = -1,
ordered: bool = True,
**kwargs,
) -> Union[dict, OrderedDict]:
"""Returns a dict representation of the object."""
dict_fun: Type[Union[dict, OrderedDict]]
if ordered:
dict_fun = OrderedDict
else:
dict_fun = dict
out = dict_fun()
out["name"] = self._name
out["id"] = self._id
if depth != 0:
out["children"] = dict_fun(
[
(name, child.to_dict(depth=depth - 1))
for name, child in self._children.items()
],
)
return out
def to_json(self, depth: int = -1, **kwargs) -> str:
"""Returns a JSON representation of the object."""
return json.dumps(self.to_dict(depth=depth, ordered=True), **kwargs)
def get_root(self) -> "Element":
"""Returns the root of the elements tree."""
if self._parent is None:
return self
else:
return self._parent.get_root()
def render(self, **kwargs) -> str:
"""Renders the HTML representation of the element."""
return self._template.render(this=self, kwargs=kwargs)
def save(
self,
outfile: Union[str, bytes, Path, BinaryIO],
close_file: bool = True,
**kwargs,
):
"""Saves an Element into a file.
Parameters
----------
outfile : str or file object
The file (or filename) where you want to output the html.
close_file : bool, default True
Whether the file has to be closed after write.
"""
fid: BinaryIO
if isinstance(outfile, (str, bytes, Path)):
fid = open(outfile, "wb")
else:
fid = outfile
root = self.get_root()
html = root.render(**kwargs)
fid.write(html.encode("utf8"))
if close_file:
fid.close()
class Link(Element):
"""An abstract class for embedding a link in the HTML."""
def __init__(self, url: str, download: bool = False):
super().__init__()
self.url = url
self.code: Optional[bytes] = None
if download:
self.get_code()
def get_code(self) -> bytes:
"""Opens the link and returns the response's content."""
if self.code is None:
self.code = urlopen(self.url).read()
return self.code
def to_dict(
self,
depth: int = -1,
ordered: bool = True,
**kwargs,
) -> Union[dict, OrderedDict]:
"""Returns a dict representation of the object."""
out = super().to_dict(depth=depth, ordered=ordered, **kwargs)
out["url"] = self.url
return out
class JavascriptLink(Link):
"""Create a JavascriptLink object based on a url.
Parameters
----------
url : str
The url to be linked
download : bool, default False
Whether the target document shall be loaded right now.
"""
_template = Template(
'{% if kwargs.get("embedded",False) %}'
""
"{% else %}"
''
"{% endif %}",
)
def __init__(self, url: str, download: bool = False):
super().__init__(url=url, download=download)
self._name = "JavascriptLink"
class CssLink(Link):
"""Create a CssLink object based on a url.
Parameters
----------
url : str
The url to be linked
download : bool, default False
Whether the target document shall be loaded right now.
"""
_template = Template(
'{% if kwargs.get("embedded",False) %}'
""
"{% else %}"
''
"{% endif %}",
)
def __init__(self, url: str, download: bool = False):
super().__init__(url=url, download=download)
self._name = "CssLink"
class Figure(Element):
"""Create a Figure object, to plot things into it.
Parameters
----------
width : str, default "100%"
The width of the Figure.
It may be a percentage or pixel value (like "300px").
height : str, default None
The height of the Figure.
It may be a percentage or a pixel value (like "300px").
ratio : str, default "60%"
A percentage defining the aspect ratio of the Figure.
It will be ignored if height is not None.
title : str, default None
Figure title.
figsize : tuple of two int, default None
If you're a matplotlib addict, you can overwrite width and
height. Values will be converted into pixels in using 60 dpi.
For example figsize=(10, 5) will result in
width="600px", height="300px".
"""
_template = Template(
"\n"
"\n"
"\n"
"{% if this.title %}{{this.title}}{% endif %}"
" {{this.header.render(**kwargs)}}\n"
"\n"
"\n"
" {{this.html.render(**kwargs)}}\n"
"\n"
"\n"
"\n",
)
def __init__(
self,
width: str = "100%",
height: Optional[str] = None,
ratio: str = "60%",
title: Optional[str] = None,
figsize: Optional[Tuple[int, int]] = None,
):
super().__init__()
self._name = "Figure"
self.header = Element()
self.html = Element()
self.script = Element()
self.header._parent = self
self.html._parent = self
self.script._parent = self
self.width = width
self.height = height
self.ratio = ratio
self.title = title
if figsize is not None:
self.width = str(60 * figsize[0]) + "px"
self.height = str(60 * figsize[1]) + "px"
# Create the meta tag.
self.header.add_child(
Element(
'',
), # noqa
name="meta_http",
)
def to_dict(
self,
depth: int = -1,
ordered: bool = True,
**kwargs,
) -> Union[dict, OrderedDict]:
"""Returns a dict representation of the object."""
out = super().to_dict(depth=depth, **kwargs)
out["header"] = self.header.to_dict(depth=depth - 1, **kwargs)
out["html"] = self.html.to_dict(depth=depth - 1, **kwargs)
out["script"] = self.script.to_dict(depth=depth - 1, **kwargs)
return out
def get_root(self) -> "Figure":
"""Returns the root of the elements tree."""
return self
def render(self, **kwargs) -> str:
"""Renders the HTML representation of the element."""
for name, child in self._children.items():
child.render(**kwargs)
return self._template.render(this=self, kwargs=kwargs)
def _repr_html_(self, **kwargs) -> str:
"""Displays the Figure in a Jupyter notebook."""
html = escape(self.render(**kwargs))
if self.height is None:
iframe = (
'
'
'
' # noqa
'Make this Notebook Trusted to load map: File -> Trust Notebook' # noqa
'"
"
"
).format(html=html, width=self.width, ratio=self.ratio)
else:
iframe = (
'"
).format(html=html, width=self.width, height=self.height)
return iframe
def add_subplot(self, x: int, y: int, n: int, margin: float = 0.05) -> "Div":
"""Creates a div child subplot in a matplotlib.figure.add_subplot style.
Parameters
----------
x : int
The number of rows in the grid.
y : int
The number of columns in the grid.
n : int
The cell number in the grid, counted from 1 to x*y.
margin : float, default 0.05
Factor to add to the left, top, width and height parameters.
Example
-------
>>> fig.add_subplot(3, 2, 5)
# Create a div in the 5th cell of a 3rows x 2columns
grid(bottom-left corner).
"""
width = 1.0 / y
height = 1.0 / x
left = ((n - 1) % y) * width
top = ((n - 1) // y) * height
left = left + width * margin
top = top + height * margin
width = width * (1 - 2.0 * margin)
height = height * (1 - 2.0 * margin)
div = Div(
position="absolute",
width=f"{100.0 * width}%",
height=f"{100.0 * height}%",
left=f"{100.0 * left}%",
top=f"{100.0 * top}%",
)
self.add_child(div)
return div
class Html(Element):
"""Create an HTML div object for embedding data.
Parameters
----------
data : str
The HTML data to be embedded.
script : bool
If True, data will be embedded without escaping
(suitable for embedding html-ready code)
width : int or str, default '100%'
The width of the output div element.
Ex: 120 , '80%'
height : int or str, default '100%'
The height of the output div element.
Ex: 120 , '80%'
"""
_template = Template(
'
",
)
def __init__(
self,
data: str,
script: bool = False,
width: TypeParseSize = "100%",
height: TypeParseSize = "100%",
):
super().__init__()
self._name = "Html"
self.script = script
self.data = data
self.width = _parse_size(width)
self.height = _parse_size(height)
class Div(Figure):
"""Create a Div to be embedded in a Figure.
Parameters
----------
width: int or str, default '100%'
The width of the div in pixels (int) or percentage (str).
height: int or str, default '100%'
The height of the div in pixels (int) or percentage (str).
left: int or str, default '0%'
The left-position of the div in pixels (int) or percentage (str).
top: int or str, default '0%'
The top-position of the div in pixels (int) or percentage (str).
position: str, default 'relative'
The position policy of the div.
Usual values are 'relative', 'absolute', 'fixed', 'static'.
"""
_template = Template(
"{% macro header(this, kwargs) %}"
""
"{% endmacro %}"
"{% macro html(this, kwargs) %}"
'
{{this.html.render(**kwargs)}}
'
"{% endmacro %}",
)
def __init__(
self,
width: TypeParseSize = "100%",
height: TypeParseSize = "100%",
left: TypeParseSize = "0%",
top: TypeParseSize = "0%",
position: str = "relative",
):
super(Figure, self).__init__()
self._name = "Div"
# Size Parameters.
self.width = _parse_size(width) # type: ignore
self.height = _parse_size(height) # type: ignore
self.left = _parse_size(left)
self.top = _parse_size(top)
self.position = position
self.header = Element()
self.html = Element(
"{% for name, element in this._children.items() %}"
"{{element.render(**kwargs)}}"
"{% endfor %}",
)
self.script = Element()
self.header._parent = self
self.html._parent = self
self.script._parent = self
def get_root(self) -> "Div":
"""Returns the root of the elements tree."""
return self
def render(self, **kwargs):
"""Renders the HTML representation of the element."""
figure = self._parent
assert isinstance(figure, Figure), (
"You cannot render this Element " "if it is not in a Figure."
)
for name, element in self._children.items():
element.render(**kwargs)
for name, element in self.header._children.items():
figure.header.add_child(element, name=name)
for name, element in self.script._children.items():
figure.script.add_child(element, name=name)
header = self._template.module.__dict__.get("header", None)
if header is not None:
figure.header.add_child(Element(header(self, kwargs)), name=self.get_name())
html = self._template.module.__dict__.get("html", None)
if html is not None:
figure.html.add_child(Element(html(self, kwargs)), name=self.get_name())
script = self._template.module.__dict__.get("script", None)
if script is not None:
figure.script.add_child(Element(script(self, kwargs)), name=self.get_name())
def _repr_html_(self, **kwargs) -> str:
"""Displays the Div in a Jupyter notebook."""
if self._parent is None:
self.add_to(Figure())
out = self._parent._repr_html_(**kwargs) # type: ignore
self._parent = None
else:
out = self._parent._repr_html_(**kwargs) # type: ignore
return out
class IFrame(Element):
"""Create a Figure object, to plot things into it.
Parameters
----------
html : str, default None
Eventual HTML code that you want to put in the frame.
width : str, default "100%"
The width of the Figure.
It may be a percentage or pixel value (like "300px").
height : str, default None
The height of the Figure.
It may be a percentage or a pixel value (like "300px").
ratio : str, default "60%"
A percentage defining the aspect ratio of the Figure.
It will be ignored if height is not None.
figsize : tuple of two int, default None
If you're a matplotlib addict, you can overwrite width and
height. Values will be converted into pixels in using 60 dpi.
For example figsize=(10, 5) will result in
width="600px", height="300px".
"""
def __init__(
self,
html: Optional[Union[str, Element]] = None,
width: str = "100%",
height: Optional[str] = None,
ratio: str = "60%",
figsize: Optional[Tuple[int, int]] = None,
):
super().__init__()
self._name = "IFrame"
self.width = width
self.height = height
self.ratio = ratio
if figsize is not None:
self.width = str(60 * figsize[0]) + "px"
self.height = str(60 * figsize[1]) + "px"
if isinstance(html, str):
self.add_child(Element(html))
elif html is not None:
self.add_child(html)
def render(self, **kwargs) -> str:
"""Renders the HTML representation of the element."""
html = super().render(**kwargs)
html = "data:text/html;charset=utf-8;base64," + base64.b64encode(
html.encode("utf8"),
).decode("utf8")
if self.height is None:
iframe = (
'
'
'
' # noqa
'"
"
"
).format(html=html, width=self.width, ratio=self.ratio)
else:
iframe = (
''
).format(html=html, width=self.width, height=self.height)
return iframe
class MacroElement(Element):
"""This is a parent class for Elements defined by a macro template.
To compute your own element, all you have to do is:
* To inherit from this class
* Overwrite the '_name' attribute
* Overwrite the '_template' attribute with something of the form::
{% macro header(this, kwargs) %}
...
{% endmacro %}
{% macro html(this, kwargs) %}
...
{% endmacro %}
{% macro script(this, kwargs) %}
...
{% endmacro %}
"""
_template = Template("")
def __init__(self):
super().__init__()
self._name = "MacroElement"
def render(self, **kwargs):
"""Renders the HTML representation of the element."""
figure = self.get_root()
assert isinstance(figure, Figure), (
"You cannot render this Element " "if it is not in a Figure."
)
header = self._template.module.__dict__.get("header", None)
if header is not None:
figure.header.add_child(Element(header(self, kwargs)), name=self.get_name())
html = self._template.module.__dict__.get("html", None)
if html is not None:
figure.html.add_child(Element(html(self, kwargs)), name=self.get_name())
script = self._template.module.__dict__.get("script", None)
if script is not None:
figure.script.add_child(Element(script(self, kwargs)), name=self.get_name())
for name, element in self._children.items():
element.render(**kwargs)
branca-0.8.1/branca/py.typed 0000664 0000000 0000000 00000000000 14716633541 0015673 0 ustar 00root root 0000000 0000000 branca-0.8.1/branca/scheme_base_codes.json 0000664 0000000 0000000 00000000562 14716633541 0020517 0 ustar 00root root 0000000 0000000 {"codes": ["viridis", "plasma", "inferno", "magma", "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"]}
branca-0.8.1/branca/scheme_info.json 0000664 0000000 0000000 00000001610 14716633541 0017356 0 ustar 00root root 0000000 0000000 {"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", "Greens": "Sequential", "viridis": "Sequential", "plasma": "Sequential", "inferno": "Sequential", "magma": "Sequential", "Oranges": "Sequential", "Blues": "Sequential", "Greys": "Sequential", "Reds": "Sequential", "Purples": "Sequential"}
branca-0.8.1/branca/templates/ 0000775 0000000 0000000 00000000000 14716633541 0016204 5 ustar 00root root 0000000 0000000 branca-0.8.1/branca/templates/color_scale.js 0000664 0000000 0000000 00000004550 14716633541 0021033 0 ustar 00root root 0000000 0000000 {% macro script(this, kwargs) %}
var {{this.get_name()}} = {};
{%if this.color_range %}
{{this.get_name()}}.color = d3.scale.threshold()
.domain({{this.color_domain}})
.range({{this.color_range}});
{%else%}
{{this.get_name()}}.color = d3.scale.threshold()
.domain([{{ this.color_domain[0] }}, {{ this.color_domain[-1] }}])
.range(['{{ this.fill_color }}', '{{ this.fill_color }}']);
{%endif%}
{{this.get_name()}}.x = d3.scale.linear()
.domain([{{ this.color_domain[0] }}, {{ this.color_domain[-1] }}])
.range([0, {{ this.width }} - 50]);
{{this.get_name()}}.legend = L.control({position: 'topright'});
{{this.get_name()}}.legend.onAdd = function (map) {var div = L.DomUtil.create('div', 'legend'); return div};
{{this.get_name()}}.legend.addTo({{this._parent.get_name()}});
{{this.get_name()}}.xAxis = d3.svg.axis()
.scale({{this.get_name()}}.x)
.orient("top")
.tickSize(1)
.tickValues({{ this.tick_labels }});
{{this.get_name()}}.svg = d3.select(".legend.leaflet-control").append("svg")
.attr("id", 'legend')
.attr("width", {{ this.width }})
.attr("height", {{ this.height }});
{{this.get_name()}}.g = {{this.get_name()}}.svg.append("g")
.attr("class", "key")
.attr("fill", {{ this.text_color | tojson }})
.attr("transform", "translate(25,16)");
{{this.get_name()}}.g.selectAll("rect")
.data({{this.get_name()}}.color.range().map(function(d, i) {
return {
x0: i ? {{this.get_name()}}.x({{this.get_name()}}.color.domain()[i - 1]) : {{this.get_name()}}.x.range()[0],
x1: i < {{this.get_name()}}.color.domain().length ? {{this.get_name()}}.x({{this.get_name()}}.color.domain()[i]) : {{this.get_name()}}.x.range()[1],
z: d
};
}))
.enter().append("rect")
.attr("height", {{ this.height }} - 30)
.attr("x", function(d) { return d.x0; })
.attr("width", function(d) { return d.x1 - d.x0; })
.style("fill", function(d) { return d.z; });
{{this.get_name()}}.g.call({{this.get_name()}}.xAxis).append("text")
.attr("class", "caption")
.attr("y", 21)
.attr("fill", {{ this.text_color | tojson }})
.text({{ this.caption|tojson }});
{% endmacro %}
branca-0.8.1/branca/utilities.py 0000664 0000000 0000000 00000035065 14716633541 0016604 0 ustar 00root root 0000000 0000000 """
Utilities
-------
Utility module for Folium helper functions.
"""
import base64
import json
import math
import os
import re
import struct
import typing
import zlib
from typing import Any, Callable, List, Optional, Sequence, Tuple, Union
from jinja2 import Environment, PackageLoader
try:
import numpy as np
except ImportError:
np = None # type: ignore
if typing.TYPE_CHECKING:
from branca.colormap import ColorMap
rootpath: str = os.path.abspath(os.path.dirname(__file__))
TypeParseSize = Union[int, float, str, Tuple[float, str]]
def get_templates() -> Environment:
"""Get Jinja templates."""
return Environment(loader=PackageLoader("branca", "templates"))
def legend_scaler(
legend_values: Sequence[float],
max_labels: int = 10,
) -> List[Union[float, str]]:
"""
Downsamples the number of legend values so that there isn't a collision
of text on the legend colorbar (within reason). The colorbar seems to
support ~10 entries as a maximum.
"""
legend_ticks: List[Union[float, str]]
if len(legend_values) < max_labels:
legend_ticks = list(legend_values)
else:
spacer = int(math.ceil(len(legend_values) / max_labels))
legend_ticks = []
for i in legend_values[::spacer]:
legend_ticks += [i]
legend_ticks += [""] * (spacer - 1)
return legend_ticks
def linear_gradient(hexList: List[str], nColors: int) -> List[str]:
"""
Given a list of hexcode values, will return a list of length
nColors where the colors are linearly interpolated between the
(r, g, b) tuples that are given.
"""
def _scale(start, finish, length, i):
"""
Return the value correct value of a number that is in between start
and finish, for use in a loop of length *length*.
"""
base = 16
fraction = float(i) / (length - 1)
raynge = int(finish, base) - int(start, base)
thex = hex(int(int(start, base) + fraction * raynge)).split("x")[-1]
if len(thex) != 2:
thex = "0" + thex
return thex
allColors: List[str] = []
# Separate (R, G, B) pairs.
for start, end in zip(hexList[:-1], hexList[1:]):
# Linearly interpolate between pair of hex ###### values and
# add to list.
nInterpolate = 765
for index in range(nInterpolate):
r = _scale(start[1:3], end[1:3], nInterpolate, index)
g = _scale(start[3:5], end[3:5], nInterpolate, index)
b = _scale(start[5:7], end[5:7], nInterpolate, index)
allColors.append("".join(["#", r, g, b]))
# Pick only nColors colors from the total list.
result: List[str] = []
for counter in range(nColors):
fraction = float(counter) / (nColors - 1)
index = int(fraction * (len(allColors) - 1))
result.append(allColors[index])
return result
def color_brewer(color_code: str, n: int = 6) -> List[str]:
"""
Generate a colorbrewer color scheme of length 'len', type 'scheme.
Live examples can be seen at http://colorbrewer2.org/
"""
maximum_n = 253
minimum_n = 3
if not isinstance(n, int):
raise TypeError("n has to be an int, not a %s" % type(n))
# Raise an error if the n requested is greater than the maximum.
if n > maximum_n:
raise ValueError(
"The maximum number of colors in a"
" ColorBrewer sequential color series is 253",
)
if n < minimum_n:
raise ValueError(
"The minimum number of colors in a"
" ColorBrewer sequential color series is 3",
)
if not isinstance(color_code, str):
raise ValueError(f"color should be a string, not a {type(color_code)}.")
if color_code[-2:] == "_r":
base_code = color_code[:-2]
core_color_code = base_code + "_" + str(n).zfill(2)
color_reverse = True
else:
base_code = color_code
core_color_code = base_code + "_" + str(n).zfill(2)
color_reverse = False
with open(os.path.join(rootpath, "_schemes.json")) as f:
schemes = json.loads(f.read())
with open(os.path.join(rootpath, "scheme_info.json")) as f:
scheme_info = json.loads(f.read())
with open(os.path.join(rootpath, "scheme_base_codes.json")) as f:
core_schemes = json.loads(f.read())["codes"]
if base_code not in core_schemes:
raise ValueError(base_code + " is not a valid ColorBrewer code")
explicit_scheme = True
if schemes.get(core_color_code) is None:
explicit_scheme = False
# Only if n is greater than the scheme length do we interpolate values.
if not explicit_scheme:
# Check to make sure that it is not a qualitative scheme.
if scheme_info[base_code] == "Qualitative":
matching_quals = []
for key in schemes:
if base_code + "_" in key:
matching_quals.append(int(key.split("_")[1]))
raise ValueError(
"Expanded color support is not available"
" for Qualitative schemes; restrict the"
" number of colors for the "
+ base_code
+ " code to between "
+ str(min(matching_quals))
+ " and "
+ str(max(matching_quals)),
)
else:
longest_scheme_name = base_code
longest_scheme_n = 0
for sn_name in schemes.keys():
if "_" not in sn_name:
continue
if sn_name.split("_")[0] != base_code:
continue
if int(sn_name.split("_")[1]) > longest_scheme_n:
longest_scheme_name = sn_name
longest_scheme_n = int(sn_name.split("_")[1])
if not color_reverse:
color_scheme = linear_gradient(schemes.get(longest_scheme_name), n)
else:
color_scheme = linear_gradient(
schemes.get(longest_scheme_name)[::-1],
n,
)
else:
if not color_reverse:
color_scheme = schemes.get(core_color_code, None)
else:
color_scheme = schemes.get(core_color_code, None)[::-1]
return color_scheme
def image_to_url(
image: Any,
colormap: Union["ColorMap", Callable, None] = None,
origin: str = "upper",
) -> str:
"""Infers the type of an image argument and transforms it into a URL.
Parameters
----------
image: string, file or array-like object
* If string, it will be written directly in the output file.
* If file, it's content will be converted as embedded in the
output file.
* If array-like, it will be converted to PNG base64 string and
embedded in the output.
origin : ['upper' | 'lower'], optional, default 'upper'
Place the [0, 0] index of the array in the upper left or
lower left corner of the axes.
colormap : ColorMap or callable, used only for `mono` image.
Function of the form [x -> (r,g,b)] or [x -> (r,g,b,a)]
for transforming a mono image into RGB.
It must output iterables of length 3 or 4, with values between
0. and 1. Hint : you can use colormaps from `matplotlib.cm`.
"""
if hasattr(image, "read"):
# We got an image file.
if hasattr(image, "name"):
# We try to get the image format from the file name.
fileformat = image.name.lower().split(".")[-1]
else:
fileformat = "png"
url = "data:image/{};base64,{}".format(
fileformat,
base64.b64encode(image.read()).decode("utf-8"),
)
elif (not (isinstance(image, str) or isinstance(image, bytes))) and hasattr(
image,
"__iter__",
):
# We got an array-like object.
png = write_png(image, origin=origin, colormap=colormap)
url = "data:image/png;base64," + base64.b64encode(png).decode("utf-8")
else:
# We got an URL.
url = json.loads(json.dumps(image))
return url.replace("\n", " ")
def write_png(
data: Any,
origin: str = "upper",
colormap: Union["ColorMap", Callable, None] = None,
) -> bytes:
"""
Transform an array of data into a PNG string.
This can be written to disk using binary I/O, or encoded using base64
for an inline PNG like this:
>>> png_str = write_png(array)
>>> "data:image/png;base64," + png_str.encode("base64")
Inspired from
http://stackoverflow.com/questions/902761/saving-a-numpy-array-as-an-image
Parameters
----------
data: numpy array or equivalent list-like object.
Must be NxM (mono), NxMx3 (RGB) or NxMx4 (RGBA)
origin : ['upper' | 'lower'], optional, default 'upper'
Place the [0,0] index of the array in the upper left or lower left
corner of the axes.
colormap : ColorMap subclass or callable, optional
Only needed to transform mono images into RGB. You have three options:
- use a subclass of `ColorMap` like `LinearColorMap`
- use a colormap from `matplotlib.cm`
- use a custom function of the form [x -> (r,g,b)] or [x -> (r,g,b,a)].
It must output iterables of length 3 or 4 with values between 0 and 1.
Returns
-------
PNG formatted byte string
"""
from branca.colormap import ColorMap
if np is None:
raise ImportError("The NumPy package is required" " for this functionality")
if isinstance(colormap, ColorMap):
colormap_callable = colormap.rgba_floats_tuple
elif callable(colormap):
colormap_callable = colormap
else:
colormap_callable = lambda x: (x, x, x, 1) # noqa E731
array = np.atleast_3d(data)
height, width, nblayers = array.shape
if nblayers not in [1, 3, 4]:
raise ValueError("Data must be NxM (mono), " "NxMx3 (RGB), or NxMx4 (RGBA)")
assert array.shape == (height, width, nblayers)
if nblayers == 1:
array = np.array(list(map(colormap_callable, array.ravel())))
nblayers = array.shape[1]
if nblayers not in [3, 4]:
raise ValueError(
"colormap must provide colors of" "length 3 (RGB) or 4 (RGBA)",
)
array = array.reshape((height, width, nblayers))
assert array.shape == (height, width, nblayers)
if nblayers == 3:
array = np.concatenate((array, np.ones((height, width, 1))), axis=2)
nblayers = 4
assert array.shape == (height, width, nblayers)
assert nblayers == 4
# Normalize to uint8 if it isn't already.
if array.dtype != "uint8":
with np.errstate(divide="ignore", invalid="ignore"):
array = array * 255.0 / array.max(axis=(0, 1)).reshape((1, 1, 4))
array[~np.isfinite(array)] = 0
array = array.astype("uint8")
# Eventually flip the image.
if origin == "lower":
array = array[::-1, :, :]
# Transform the array to bytes.
raw_data = b"".join([b"\x00" + array[i, :, :].tobytes() for i in range(height)])
def png_pack(png_tag, data):
chunk_head = png_tag + data
return (
struct.pack("!I", len(data))
+ chunk_head
+ struct.pack("!I", 0xFFFFFFFF & zlib.crc32(chunk_head))
)
return b"".join(
[
b"\x89PNG\r\n\x1a\n",
png_pack(b"IHDR", struct.pack("!2I5B", width, height, 8, 6, 0, 0, 0)),
png_pack(b"IDAT", zlib.compress(raw_data, 9)),
png_pack(b"IEND", b""),
],
)
def _camelify(out: str) -> str:
return (
(
"".join(
[
(
"_" + x.lower()
if i < len(out) - 1 and x.isupper() and out[i + 1].islower()
else (
x.lower() + "_"
if i < len(out) - 1 and x.islower() and out[i + 1].isupper()
else x.lower()
)
)
for i, x in enumerate(list(out))
],
)
)
.lstrip("_")
.replace("__", "_")
)
def _parse_size(value: TypeParseSize) -> Tuple[float, str]:
if isinstance(value, (int, float)):
return float(value), "px"
elif isinstance(value, str):
# match digits or a point, possibly followed by a space,
# followed by a unit: either 1 to 5 letters or a percent sign
match = re.fullmatch(r"([\d.]+)\s?(\w{1,5}|%)", value.strip())
if match:
return float(match.group(1)), match.group(2)
else:
raise ValueError(
f"Cannot parse {value!r}, it should be a number followed by a unit.",
)
elif (
isinstance(value, tuple)
and isinstance(value[0], (int, float))
and isinstance(value[1], str)
):
# value had been already parsed
return (float(value[0]), value[1])
else:
raise TypeError(
f"Cannot parse {value!r}, it should be a number or a string containing a number and a unit.",
)
def _locations_mirror(x):
"""Mirrors the points in a list-of-list-of-...-of-list-of-points.
For example:
>>> _locations_mirror([[[1, 2], [3, 4]], [5, 6], [7, 8]])
[[[2, 1], [4, 3]], [6, 5], [8, 7]]
"""
if hasattr(x, "__iter__"):
if hasattr(x[0], "__iter__"):
return list(map(_locations_mirror, x))
else:
return list(x[::-1])
else:
return x
def _locations_tolist(x):
"""Transforms recursively a list of iterables into a list of list."""
if hasattr(x, "__iter__"):
return list(map(_locations_tolist, x))
else:
return x
def none_min(x: Optional[float], y: Optional[float]) -> Optional[float]:
if x is None:
return y
elif y is None:
return x
else:
return min(x, y)
def none_max(x: Optional[float], y: Optional[float]) -> Optional[float]:
if x is None:
return y
elif y is None:
return x
else:
return max(x, y)
def iter_points(x: Union[List, Tuple]) -> list:
"""Iterates over a list representing a feature, and returns a list of points,
whatever the shape of the array (Point, MultiPolyline, etc).
"""
if isinstance(x, (list, tuple)):
if len(x):
if isinstance(x[0], (list, tuple)):
out = []
for y in x:
out += iter_points(y)
return out
else:
return [x]
else:
return []
else:
raise ValueError(f"List/tuple type expected. Got {x!r}.")
branca-0.8.1/docs/ 0000775 0000000 0000000 00000000000 14716633541 0013710 5 ustar 00root root 0000000 0000000 branca-0.8.1/docs/Makefile 0000664 0000000 0000000 00000001111 14716633541 0015342 0 ustar 00root root 0000000 0000000 # Minimal makefile for Sphinx documentation
#
# You can set these variables from the command line.
SPHINXOPTS =
SPHINXBUILD = sphinx-build
SOURCEDIR = source
BUILDDIR = build
# Put it first so that "make" without argument is like "make help".
help:
@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
.PHONY: help Makefile
# Catch-all target: route all unknown targets to Sphinx using the new
# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
%: Makefile
@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
branca-0.8.1/docs/source/ 0000775 0000000 0000000 00000000000 14716633541 0015210 5 ustar 00root root 0000000 0000000 branca-0.8.1/docs/source/colormap.rst 0000664 0000000 0000000 00000000205 14716633541 0017553 0 ustar 00root root 0000000 0000000 :mod:`branca.colormap`
----------------------
.. automodule:: branca.colormap
:members:
:undoc-members:
:show-inheritance:
branca-0.8.1/docs/source/conf.py 0000664 0000000 0000000 00000012260 14716633541 0016510 0 ustar 00root root 0000000 0000000 #
# Configuration file for the Sphinx documentation builder.
#
# This file does only contain a selection of the most common options. For a
# full list see the documentation:
# http://www.sphinx-doc.org/en/master/config
# -- Path setup --------------------------------------------------------------
# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
#
# import os
# import sys
# sys.path.insert(0, os.path.abspath('.'))
# -- Project information -----------------------------------------------------
project = "branca"
copyright = "2018, Filipe Fernandes"
author = "Filipe Fernandes"
import branca
version = release = branca.__version__
# -- General configuration ---------------------------------------------------
# If your documentation needs a minimal Sphinx version, state it here.
#
# needs_sphinx = '1.0'
# Add any Sphinx extension module names here, as strings. They can be
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
# ones.
extensions = [
"sphinx.ext.autodoc",
"sphinx.ext.mathjax",
"sphinx.ext.githubpages",
"sphinx.ext.viewcode",
"sphinx.ext.napoleon",
"nbsphinx",
]
# Add any paths that contain templates here, relative to this directory.
templates_path = ["_templates"]
# The suffix(es) of source filenames.
# You can specify multiple suffix as a list of string:
#
# source_suffix = ['.rst', '.md']
source_suffix = ".rst"
# The master toctree document.
master_doc = "index"
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
#
# This is also used if you do content translation via gettext catalogs.
# Usually you set "language" from the command line for these cases.
language = "en"
# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
# This pattern also affects html_static_path and html_extra_path.
exclude_patterns = []
# The name of the Pygments (syntax highlighting) style to use.
pygments_style = None
# -- Options for HTML output -------------------------------------------------
# The theme to use for HTML and HTML Help pages. See the documentation for
# a list of builtin themes.
#
html_theme = "alabaster"
# Theme options are theme-specific and customize the look and feel of a theme
# further. For a list of options available for each theme, see the
# documentation.
#
# html_theme_options = {}
# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
html_static_path = ["_static"]
# Custom sidebar templates, must be a dictionary that maps document names
# to template names.
#
# The default sidebars (for documents that don't match any pattern) are
# defined by theme itself. Builtin themes are using these templates by
# default: ``['localtoc.html', 'relations.html', 'sourcelink.html',
# 'searchbox.html']``.
#
# html_sidebars = {}
# -- Options for HTMLHelp output ---------------------------------------------
# Output file base name for HTML help builder.
htmlhelp_basename = "brancadoc"
# -- Options for LaTeX output ------------------------------------------------
latex_elements = {
# The paper size ('letterpaper' or 'a4paper').
#
# 'papersize': 'letterpaper',
# The font size ('10pt', '11pt' or '12pt').
#
# 'pointsize': '10pt',
# Additional stuff for the LaTeX preamble.
#
# 'preamble': '',
# Latex figure (float) alignment
#
# 'figure_align': 'htbp',
}
# Grouping the document tree into LaTeX files. List of tuples
# (source start file, target name, title,
# author, documentclass [howto, manual, or own class]).
latex_documents = [
(master_doc, "branca.tex", "branca Documentation", "Filipe Fernandes", "manual"),
]
# -- Options for manual page output ------------------------------------------
# One entry per manual page. List of tuples
# (source start file, name, description, authors, manual section).
man_pages = [(master_doc, "branca", "branca Documentation", [author], 1)]
# -- Options for Texinfo output ----------------------------------------------
# Grouping the document tree into Texinfo files. List of tuples
# (source start file, target name, title, author,
# dir menu entry, description, category)
texinfo_documents = [
(
master_doc,
"branca",
"branca Documentation",
author,
"branca",
"One line description of project.",
"Miscellaneous",
),
]
# -- Options for Epub output -------------------------------------------------
# Bibliographic Dublin Core info.
epub_title = project
# The unique identifier of the text. This can be a ISBN number
# or the project homepage.
#
# epub_identifier = ''
# A unique identification for the text.
#
# epub_uid = ''
# A list of files that should not be packed into the epub file.
epub_exclude_files = ["search.html"]
# -- Extension configuration -------------------------------------------------
branca-0.8.1/docs/source/element.rst 0000664 0000000 0000000 00000000202 14716633541 0017365 0 ustar 00root root 0000000 0000000 :mod:`branca.element`
---------------------
.. automodule:: branca.element
:members:
:undoc-members:
:show-inheritance:
branca-0.8.1/docs/source/index.rst 0000664 0000000 0000000 00000000710 14716633541 0017047 0 ustar 00root root 0000000 0000000 .. branca documentation master file, created by
sphinx-quickstart on Mon Nov 5 13:22:48 2018.
You can adapt this file completely to your liking, but it should at least
contain the root `toctree` directive.
Welcome to branca's documentation!
==================================
.. toctree::
:maxdepth: 3
:caption: Contents:
element
colormap
Indices and tables
==================
* :ref:`genindex`
* :ref:`modindex`
* :ref:`search`
branca-0.8.1/examples/ 0000775 0000000 0000000 00000000000 14716633541 0014576 5 ustar 00root root 0000000 0000000 branca-0.8.1/examples/Custom_colormap.ipynb 0000664 0000000 0000000 00000125147 14716633541 0021021 0 ustar 00root root 0000000 0000000 {
"cells": [
{
"cell_type": "code",
"execution_count": 1,
"id": "1d544550",
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
""
],
"text/plain": [
""
]
},
"execution_count": 1,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"from branca.colormap import linear\n",
"\n",
"colormap_choice = linear.YlOrRd_04\n",
"vmin = 3.2\n",
"vmax = 10.3\n",
"colormap = colormap_choice.scale(vmin, vmax)\n",
"colormap.caption = 'Unemployment rate'\n",
"colormap.text_color = 'cyan'\n",
"colormap"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.10.12"
}
},
"nbformat": 4,
"nbformat_minor": 5
}
branca-0.8.1/examples/Elements.ipynb 0000664 0000000 0000000 00000047216 14716633541 0017427 0 ustar 00root root 0000000 0000000 {
"cells": [
{
"cell_type": "code",
"execution_count": 1,
"metadata": {},
"outputs": [],
"source": [
"import sys\n",
"sys.path.insert(0, '..')\n",
"\n",
"from branca.element import *"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Element"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"This is the base brick of `branca`. You can create an `Element` in providing a template string:"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {},
"outputs": [],
"source": [
"e = Element(\"This is fancy text\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"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."
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Element a1d0f648f7444f96b526931944247fd6\n",
"element_a1d0f648f7444f96b526931944247fd6\n"
]
}
],
"source": [
"print(e._name, e._id)\n",
"print(e.get_name())"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"You can render an `Element` using the method `render`:"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"'This is fancy text'"
]
},
"execution_count": 4,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"e.render()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"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:"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"'Hello World, my name is `element_6f17661abddb45c7bf2aa794cadd327d`.'"
]
},
"execution_count": 5,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"e = Element(\"Hello {{kwargs['you']}}, my name is `{{this.get_name()}}`.\")\n",
"e.render(you='World')"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"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`."
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {},
"outputs": [],
"source": [
"child = Element('This is the child.')\n",
"parent = Element('This is the parent.').add_child(child)\n",
"\n",
"parent = Element('This is the parent.')\n",
"child = Element('This is the child.').add_to(parent)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Now in the example above, embedding the one in the other does not change anything."
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"This is the parent. This is the child.\n"
]
}
],
"source": [
"print(parent.render(), child.render())"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"But you can use the tree structure in the template."
]
},
{
"cell_type": "code",
"execution_count": 8,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"''"
]
},
"execution_count": 8,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"parent = Element(\"{% for child in this._children.values() %}{{child.render()}}{% endfor %}\")\n",
"Element('').add_to(parent)\n",
"Element('').add_to(parent)\n",
"parent.render()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"As you can see, the child of an element are referenced in the `_children` attribute 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:"
]
},
{
"cell_type": "code",
"execution_count": 9,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"OrderedDict([('child_1', )])"
]
},
"execution_count": 9,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"parent = Element(\"{% for child in this._children.values() %}{{child.render()}}{% endfor %}\")\n",
"Element('').add_to(parent, name='child_1')\n",
"parent._children"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"That way, it's possible to overwrite a child in specifying the same name:"
]
},
{
"cell_type": "code",
"execution_count": 10,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"''"
]
},
"execution_count": 10,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"Element('').add_to(parent, name='child_1')\n",
"parent.render()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"I hope you start to find it useful.\n",
"\n",
"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."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Figure\n",
"\n",
"A `Figure` represents an HTML document. It's composed of 3 parts (attributes):\n",
"\n",
"* `header` : corresponds to the `` part of the HTML document,\n",
"* `html` : corresponds to the `` part,\n",
"* `script` : corresponds to a `\n"
]
}
],
"source": [
"f = Figure()\n",
"print(f.render())"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"You can for example create a beautiful cyan \"hello-world\" webpage in doing:"
]
},
{
"cell_type": "code",
"execution_count": 12,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"\n",
" \n",
" \n",
" \n",
"\n",
" \n",
"
\"))\n",
"print(f.render())"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"You can simply save the content of the `Figure` to a file, thanks to the `save` method:"
]
},
{
"cell_type": "code",
"execution_count": 13,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"\n",
" \n",
" \n",
" \n",
"\n",
" \n",
"
Hello world
\n",
"\n",
"\n"
]
}
],
"source": [
"f.save('foo.html')\n",
"print(open('foo.html').read())"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"If you want to visualize it in the notebook, you can let `Figure._repr_html_` method do it's job in typing: "
]
},
{
"cell_type": "code",
"execution_count": 14,
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"
"
],
"text/plain": [
""
]
},
"execution_count": 14,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"f"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"If this rendering is too large for you, you can force it's width and height:"
]
},
{
"cell_type": "code",
"execution_count": 15,
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
""
],
"text/plain": [
""
]
},
"execution_count": 15,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"f.width = 300\n",
"f.height = 200\n",
"f"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Note that you can also define a `Figure`'s size in a matplotlib way:"
]
},
{
"cell_type": "code",
"execution_count": 16,
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
""
],
"text/plain": [
""
]
},
"execution_count": 16,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"Figure(figsize=(5,5))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## MacroElement"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"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."
]
},
{
"cell_type": "code",
"execution_count": 17,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"\n",
" \n",
" \n",
" This is header of macro_element_ea36a310ab8a4212a8c7ca754a4140fc\n",
"\n",
" \n",
" This is html of macro_element_ea36a310ab8a4212a8c7ca754a4140fc\n",
"\n",
"\n"
]
}
],
"source": [
"macro = MacroElement()\n",
"macro._template = Template(\n",
" '{% macro header(this, kwargs) %}'\n",
" 'This is header of {{this.get_name()}}'\n",
" '{% endmacro %}'\n",
"\n",
" '{% macro html(this, kwargs) %}'\n",
" 'This is html of {{this.get_name()}}'\n",
" '{% endmacro %}'\n",
"\n",
" '{% macro script(this, kwargs) %}'\n",
" 'This is script of {{this.get_name()}}'\n",
" '{% endmacro %}'\n",
" )\n",
"\n",
"print(Figure().add_child(macro).render())"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Link"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"To embed javascript and css links in the header, you can use these class:"
]
},
{
"cell_type": "code",
"execution_count": 18,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"''"
]
},
"execution_count": 18,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"js_link = JavascriptLink('https://example.com/javascript.js')\n",
"js_link.render()"
]
},
{
"cell_type": "code",
"execution_count": 19,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"''"
]
},
"execution_count": 19,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"css_link = CssLink('https://example.com/style.css')\n",
"css_link.render()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Html"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"An `Html` element enables you to create custom div to put in the *body* of your page."
]
},
{
"cell_type": "code",
"execution_count": 26,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"'
Hello world
'"
]
},
"execution_count": 26,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"html = Html('Hello world')\n",
"html.render()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"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."
]
},
{
"cell_type": "code",
"execution_count": 25,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"'
<b>Hello world</b>
'"
]
},
"execution_count": 25,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"Html('Hello world').render()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"For this, you have to set `script=True` and it will work:"
]
},
{
"cell_type": "code",
"execution_count": 28,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"'
Hello world
'"
]
},
"execution_count": 28,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"Html('Hello world', script=True).render()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## IFrame"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"If you need to embed a full webpage (with separate javascript environment), you can use `IFrame`."
]
},
{
"cell_type": "code",
"execution_count": 21,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"'
'"
]
},
"execution_count": 21,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"iframe = IFrame('Hello World')\n",
"iframe.render()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"As you can see, it will embed the full content of the iframe in a *base64* string so that the output looks like:"
]
},
{
"cell_type": "code",
"execution_count": 22,
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
""
],
"text/plain": [
""
]
},
"execution_count": 22,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"f = Figure(height=180)\n",
"f.html.add_child(Element(\"Before the frame\"))\n",
"f.html.add_child(IFrame('In the frame', height='100px'))\n",
"f.html.add_child(Element(\"After the frame\"))\n",
"f"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Div"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"At last, you have the `Div` element that behaves almost like `Html` with a few differences:\n",
"\n",
"* The style is put in the header, while `Html`'s style is embedded inline.\n",
"* `Div` inherits from `MacroElement` so that:\n",
" * It cannot be rendered unless it's embedded in a `Figure`.\n",
" * It is a useful object toinherit from when you create new classes."
]
},
{
"cell_type": "code",
"execution_count": 29,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"\n",
" \n",
" \n",
" \n",
"\n",
" \n",
"
")
options = Options()
options.add_argument("-headless")
driver = Firefox(options=options)
driver.get("data:text/html," + iframe.render())
driver.switch_to.frame(0)
assert "Cerrahpaşa Tıp Fakültesi" in driver.page_source
@pytest.mark.headless
def test_rendering_figure_notebook():
"""Verify special characters are correctly rendered in Jupyter notebooks."""
text = '5/7 %, Линейная улица, "\u00e9 Berdsk"'
figure = elem.Figure()
elem.Html(text).add_to(figure.html)
html = figure._repr_html_()
filepath = "temp_test_rendering_figure_notebook.html"
filepath = os.path.abspath(filepath)
with open(filepath, "w") as f:
f.write(html)
options = Options()
options.add_argument("-headless")
driver = Firefox(options=options)
try:
driver.get("file://" + filepath)
driver.switch_to.frame(0)
text_div = driver.find_element(By.CSS_SELECTOR, "div")
assert text_div.text == text
finally:
os.remove(filepath)
driver.quit()
branca-0.8.1/tests/test_utilities.py 0000664 0000000 0000000 00000014430 14716633541 0017550 0 ustar 00root root 0000000 0000000 import json
import os
from pathlib import Path
import pytest
import branca.utilities as ut
from branca.colormap import LinearColormap
rootpath = Path(os.path.dirname(os.path.abspath(__file__))) / ".." / "branca"
color_brewer_minimum_n = 3
color_brewer_maximum_n = 253 # Why this limitation @ branca/utilities.py:108 ?
# Loads schemes and their meta-data
with open(rootpath / "_schemes.json") as f:
schemes = json.loads(f.read())
with open(rootpath / "scheme_info.json") as f:
scheme_info = json.loads(f.read())
with open(rootpath / "scheme_base_codes.json") as f:
core_schemes = json.loads(f.read())["codes"]
def test_color_brewer_base():
scheme = ut.color_brewer("YlGnBu", 9)
assert scheme == [
"#ffffd9",
"#edf8b1",
"#c7e9b4",
"#7fcdbb",
"#41b6c4",
"#1d91c0",
"#225ea8",
"#253494",
"#081d58",
]
def test_color_brewer_reverse():
scheme = ut.color_brewer("YlGnBu")
scheme_r = ut.color_brewer("YlGnBu_r")
assert scheme[::-1] == scheme_r
def test_color_brewer_extendability():
"""
The non-qualitative schemes should be extendable.
:see https://github.com/python-visualization/branca/issues/104
:see https://github.com/python-visualization/branca/issues/114
Moreover, the following error was not reported via issues:
* TypeError in the linear_gradient function when trying to extend any scheme.
Indeed, in color_brewer, the key searched in the scheme database was not found,
thus, it was passing `None` instead of a real scheme vector to linear_gradient.
"""
for sname in core_schemes:
for n in range(color_brewer_minimum_n, color_brewer_maximum_n + 1):
try:
scheme = ut.color_brewer(sname, n=n)
except Exception as e:
if scheme_info[sname] == "Qualitative" and isinstance(e, ValueError):
continue
raise
assert len(scheme) == n
# When we try to extend a scheme,
# the reverse is not always the exact reverse vector of the original one.
# Thus, we do not test this property!
_ = ut.color_brewer(sname + "_r", n=n)
def test_color_avoid_unexpected_error():
"""
We had unexpected errors by providing some scheme name with unexpected value of `n`.
This function tests them.
Identified errors which was not reported via issues:
* The scheme 'viridis' was not in the base_codes JSON;
* Multiple scheme hadn't any metadata in scheme_info JSON;
* When a `n` value provided to `color_scheme` was a float,
it tried to select an unknown scheme without raising the right Exception type.
"""
# Verify that every scheme has is present in base codes
scheme_names = set()
for sname in schemes.keys():
scheme_names.add(sname.split("_")[0])
assert scheme_names == set(core_schemes)
# Verify that every scheme has a metadata
assert scheme_names == set(scheme_info.keys())
# Verify that every scheme can be generated in color_brewer using exotic value of `n`.
# Note that big but valid values are generated by test_color_brewer_extendability.
for sname in scheme_names:
for n in (
[-10]
+ list(range(-1, color_brewer_minimum_n))
+ list(range(color_brewer_maximum_n + 1, color_brewer_maximum_n + 10))
):
with pytest.raises(ValueError):
ut.color_brewer(sname, n)
for n in [str(color_brewer_minimum_n), float(color_brewer_minimum_n), "abc"]:
with pytest.raises(TypeError):
ut.color_brewer(sname, n)
@pytest.mark.parametrize(
"value,result",
[
(1, (1.0, "px")),
("1 px", (1.0, "px")),
("80 % ", (80.0, "%")),
("100% ", (100.0, "%")),
("3 vw", (3.0, "vw")),
("3.14 rem", (3.14, "rem")),
((1, "px"), (1.0, "px")),
((80.0, "%"), (80.0, "%")),
],
)
def test_parse_size(value, result):
assert ut._parse_size(value) == result
@pytest.mark.parametrize(
"value",
[
"what?",
"1.21 jigawatts",
ut._parse_size,
(1.21, 4.9),
],
)
def test_parse_size_exceptions(value):
with pytest.raises((ValueError, TypeError)):
ut._parse_size(value)
def test_write_png_mono():
mono_image = [
[0.24309289, 0.75997446, 0.02971671, 0.52830537],
[0.62339252, 0.65369358, 0.41545387, 0.03307279],
]
mono_png = b"\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00\x04\x00\x00\x00\x02\x08\x06\x00\x00\x00\x7f\xa8}c\x00\x00\x00)IDATx\xdac\x08\x0c\x0c\xfc\x0f\x02\x9c\x9c\x9c\xff7n\xdc\xf8\x9f\xe1\xe2\xc5\x8b\xffo\xdf\xbe\xfd\xbf\xbb\xbb\xfb?77\xf7\x7f\x00f\x87\x14\xdd\x0c\r;\xc0\x00\x00\x00\x00IEND\xaeB`\x82" # noqa E501
assert ut.write_png(mono_image) == mono_png
colormap = LinearColormap(colors=["red", "yellow", "green"])
color_png = b"\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00\x04\x00\x00\x00\x02\x08\x06\x00\x00\x00\x7f\xa8}c\x00\x00\x00)IDATx\xdac\xf8_\xcf\xf0\xbf\xea\x10\xc3\xff\xff\xfc\x0c\xff?\xfcg\xf8\xcfp\xe0\x19\xc3\xff\r\xf7\x80\x02\xb7\x80X\x90\xe1?\x00N\xca\x13\xcd\xfb\xad\r\xb8\x00\x00\x00\x00IEND\xaeB`\x82" # noqa E501
assert ut.write_png(mono_image, colormap=colormap) == color_png
def test_write_png_rgb():
image_rgb = [
[
[0.8952778565195247, 0.6196806506704735, 0.2696137085302287],
[0.3940794236804127, 0.9432178293916365, 0.16500617914697335],
[0.5566755388192485, 0.10469673377265687, 0.27346260130585975],
[0.2029951628162342, 0.5357152681832641, 0.13692921080346832],
],
[
[0.5186482474007286, 0.8625240370164696, 0.6965561989987038],
[0.04425586727957387, 0.45448042432657076, 0.8552600511205423],
[0.696453974598333, 0.7508742900711168, 0.9646572952994652],
[0.7471809029502141, 0.3218907599994758, 0.789193070740859],
],
]
png = b'\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00\x04\x00\x00\x00\x02\x08\x06\x00\x00\x00\x7f\xa8}c\x00\x00\x00-IDATx\xda\x01"\x00\xdd\xff\x00\xff\xa7G\xffp\xff+\xff\x9e\x1cH\xff9\x90$\xff\x00\x93\xe9\xb8\xff\x0cz\xe2\xff\xc6\xca\xff\xff\xd4W\xd0\xffYw\x15\x95\xcf\xb9@D\x00\x00\x00\x00IEND\xaeB`\x82' # noqa E501
assert ut.write_png(image_rgb) == png