├── .gitignore ├── .travis.yml ├── CHANGELOG.md ├── CONTRIBUTING.md ├── LICENSE ├── README.rst ├── ROADMAP.md ├── docs ├── Makefile ├── api │ ├── mapboxgl.rst │ └── modules.rst ├── conf.py ├── index.rst ├── utils.md └── viz.md ├── examples ├── data │ ├── 2010_us_population_by_postcode.csv │ ├── cdec.csv │ ├── cdec.geojson │ ├── cdec_temp.geojson │ ├── healthcare_points.geojson │ ├── mosaic.png │ ├── points.csv │ ├── points1.geojson │ └── us-states.geojson ├── notebooks │ ├── choropleth-viz-example.ipynb │ ├── circle-vector.ipynb │ ├── image-vis-type-example.ipynb │ ├── legend-controls.ipynb │ ├── linestring-viz.ipynb │ ├── point-viz-categorical-example.ipynb │ ├── point-viz-types-example.ipynb │ ├── rastertile-viz-slippymap-example.ipynb │ ├── rastertile-viz-type-example.ipynb │ └── scale-annotation.ipynb └── screenshots │ └── screenshot.png ├── mapboxgl ├── __init__.py ├── colors.py ├── errors.py ├── templates.py ├── templates │ ├── base.html │ ├── choropleth.html │ ├── circle.html │ ├── clustered_circle.html │ ├── export_canvas.html │ ├── graduated_circle.html │ ├── heatmap.html │ ├── image.html │ ├── linestring.html │ ├── main.html │ ├── map.html │ ├── raster.html │ ├── symbol.html │ ├── vector_choropleth.html │ ├── vector_circle.html │ ├── vector_graduated_circle.html │ ├── vector_heatmap.html │ ├── vector_linestring.html │ └── vector_map.html ├── utils.py └── viz.py ├── rtd-requirements.txt ├── setup.cfg ├── setup.py └── tests ├── linestrings.geojson ├── mosaic.png ├── points.csv ├── points.geojson ├── polygons.geojson ├── test_colors.py ├── test_html.py └── test_utils.py /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | 5 | # C extensions 6 | *.so 7 | 8 | # Distribution / packaging 9 | .Python 10 | env/ 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | lib/ 17 | lib64/ 18 | parts/ 19 | sdist/ 20 | var/ 21 | *.egg-info/ 22 | .installed.cfg 23 | *.egg 24 | 25 | # PyInstaller 26 | # Usually these files are written by a python script from a template 27 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 28 | *.manifest 29 | *.spec 30 | 31 | # Installer logs 32 | pip-log.txt 33 | pip-delete-this-directory.txt 34 | 35 | # Unit test / coverage reports 36 | htmlcov/ 37 | .tox/ 38 | .coverage 39 | .cache 40 | nosetests.xml 41 | coverage.xml 42 | 43 | # Translations 44 | *.mo 45 | *.pot 46 | 47 | # Django stuff: 48 | *.log 49 | 50 | # Sphinx documentation 51 | docs/_build/ 52 | 53 | # PyBuilder 54 | target/ 55 | 56 | # OS X 57 | .DS_Store 58 | 59 | .*.swp 60 | .hypothesis 61 | *.ipynb_checkpoints* 62 | out.geojson 63 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: python 2 | python: 3 | - "2.7" 4 | - "3.6" 5 | install: 6 | - "pip install -e .[test]" 7 | - "pip install coveralls" 8 | script: "py.test -v --cov mapboxgl --cov-report term-missing" 9 | after_success: 10 | - coveralls || echo "intermittent coveralls failure" 11 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | 0.1.1 (12/26/2017) 2 | ----- 3 | - Added ClusteredCircleViz type 4 | - Updated to Mapbox GL JS v0.43.0 5 | - Added support for viz opacity and a custom layer order 6 | - Updated documentation in Readme 7 | - Use PySAL for jenks natural breaks domain calculations in example 8 | 9 | 0.1.0 10 | ----- 11 | - HeatmapViz 12 | - jinja templates 13 | 14 | 0.0.2 15 | ----- 16 | - added GraduatedCircleViz with graduated sizes/color ramps for point data. 17 | 18 | 0.0.1 19 | ----- 20 | - Initial pre-alpha release, CircleViz with color ramps for point data. 21 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | 1. Clone this repo `git clone git@github.com:mapbox/mapboxgl-jupyter.git` 2 | 2. Create a new branch `git branch my-branch-name` 3 | 3. Check out the branch `git checkout my-branch-name` 4 | 4. Commit your code changes 5 | 5. Push your branch `git push origin my-branch-name` 6 | 7. Make a PR from branch to merge into `master`. 7 | 8. Get a code review. 8 | 9. Merge into master. 9 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2017 Mapbox 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | ========================================================= 2 | Location Data Visualization library for Jupyter Notebooks 3 | ========================================================= 4 | 5 | .. image:: https://travis-ci.org/mapbox/mapboxgl-jupyter.svg?branch=master 6 | :target: https://travis-ci.org/mapbox/mapboxgl-jupyter 7 | :alt: Build Status 8 | 9 | .. image:: https://coveralls.io/repos/github/mapbox/mapboxgl-jupyter/badge.svg?branch=master 10 | :target: https://coveralls.io/github/mapbox/mapboxgl-jupyter?branch=master 11 | :alt: Coverage Status 12 | 13 | .. image:: https://badge.fury.io/py/mapboxgl.svg 14 | :target: https://badge.fury.io/py/mapboxgl 15 | :alt: PyPI version 16 | 17 | 18 | Library documentation at https://mapbox-mapboxgl-jupyter.readthedocs-hosted.com/en/latest/. 19 | 20 | Create `Mapbox GL JS `__ data 21 | visualizations natively in Jupyter Notebooks with Python and Pandas. *mapboxgl* 22 | is a high-performance, interactive, WebGL-based data visualization tool that 23 | drops directly into Jupyter. *mapboxgl* is similar to `Folium 24 | `__ built on top of the raster 25 | `Leaflet `__ map library, but with much higher 26 | performance for large data sets using WebGL and Mapbox Vector Tiles. 27 | 28 | .. image:: https://cl.ly/3a0K2m1o2j1A/download/Image%202018-02-22%20at%207.16.58%20PM.png 29 | 30 | Try out the interactive map example notebooks from the /examples directory in 31 | this repository 32 | 33 | 1. `Categorical points `__ 34 | 2. `All visualization types `__ 35 | 3. `Choropleth Visualization types `__ 36 | 4. `Image Visualization types `__ 37 | 5. `Raster Tile Visualization types `__ 38 | 39 | Installation 40 | ============ 41 | 42 | .. code-block:: bash 43 | 44 | $ pip install mapboxgl 45 | 46 | Documentation 47 | ============= 48 | 49 | Documentation is on Read The Docs at https://mapbox-mapboxgl-jupyter.readthedocs-hosted.com/en/latest/. 50 | 51 | Usage 52 | ===== 53 | 54 | The ``examples`` directory contains sample Jupyter notebooks demonstrating usage. 55 | 56 | .. code-block:: python 57 | 58 | import os 59 | 60 | import pandas as pd 61 | 62 | from mapboxgl.utils import create_color_stops, df_to_geojson 63 | from mapboxgl.viz import CircleViz 64 | 65 | 66 | # Load data from sample csv 67 | data_url = 'https://raw.githubusercontent.com/mapbox/mapboxgl-jupyter/master/examples/data/points.csv' 68 | df = pd.read_csv(data_url) 69 | 70 | # Must be a public token, starting with `pk` 71 | token = os.getenv('MAPBOX_ACCESS_TOKEN') 72 | 73 | # Create a geojson file export from a Pandas dataframe 74 | df_to_geojson(df, filename='points.geojson', 75 | properties=['Avg Medicare Payments', 'Avg Covered Charges', 'date'], 76 | lat='lat', lon='lon', precision=3) 77 | 78 | # Generate data breaks and color stops from colorBrewer 79 | color_breaks = [0,10,100,1000,10000] 80 | color_stops = create_color_stops(color_breaks, colors='YlGnBu') 81 | 82 | # Create the viz from the dataframe 83 | viz = CircleViz('points.geojson', 84 | access_token=token, 85 | height='400px', 86 | color_property = "Avg Medicare Payments", 87 | color_stops = color_stops, 88 | center = (-95, 40), 89 | zoom = 3, 90 | below_layer = 'waterway-label' 91 | ) 92 | viz.show() 93 | 94 | Development 95 | =========== 96 | 97 | Install the python library locally with pip: 98 | 99 | .. code-block:: console 100 | 101 | $ pip install -e . 102 | 103 | To run tests use pytest: 104 | 105 | .. code-block:: console 106 | 107 | $ pip install mock pytest 108 | $ python -m pytest 109 | 110 | To run the Jupyter examples, 111 | 112 | .. code-block:: console 113 | 114 | $ cd examples 115 | $ pip install jupyter 116 | $ jupyter notebook 117 | 118 | We follow the `PEP8 style guide for Python `__ for all Python code. 119 | 120 | Release process 121 | =============== 122 | 123 | - After merging all relevant PRs for the upcoming release, pull the master branch 124 | * ``git checkout master`` 125 | * ``git pull`` 126 | - Update the version number in ``mapboxgl/__init__.py`` and push directly to master. 127 | - Tag the release 128 | * ``git tag `` 129 | * ``git push --tags`` 130 | - Setup for pypi (one time only) 131 | * You'll need to ``pip install twine`` and set up your credentials in a `~/.pypirc `__ `file `__. 132 | - Create the release files 133 | * ``rm dist/*`` # clean out old releases if they exist 134 | * ``python setup.py sdist bdist_wheel`` 135 | - Upload the release files 136 | * ``twine upload dist/mapboxgl-*`` 137 | -------------------------------------------------------------------------------- /ROADMAP.md: -------------------------------------------------------------------------------- 1 | # Roadmap for `mapboxgl-jupyter` 1.0 release 2 | 3 | Here are some ideas for future releases. If you're 4 | interested in any of these features and would like to help out, 5 | please start an Issue on github with more details. 6 | 7 | ## Working on Next 8 | 9 | - [ ] Multiple visual layers in one map 10 | - [ ] Hover/Highlight/select styles on mousemove and selection on click 11 | - [ ] PNG/JPG image export of map and data to file 12 | - [ ] Great Arc map layers from source-to-destination point 13 | 14 | ## Down-the-road 15 | 16 | - [ ] Control map interactivity with python vars 17 | - [ ] Add shapely points, lines, and polygons to map (with projection wrapper around non-4326 shapes) 18 | - [ ] `dashboard-like` tabular views with filtering 19 | - [ ] Add user-defined html templates for popups 20 | 21 | ## Recently completed 22 | 23 | - [x] Add methods for determining breaks (jenks, etc) 24 | * Added jerks support in 0.2.0 25 | - [x] graduated symbols for circleviz 26 | * Implemented in 0.2.0 27 | - [x] legend 28 | * Implemented in 0.2.0 29 | - [x] html templates for property display on mouseover 30 | * Implemented in 0.3.0 31 | - [x] Add heatmap visualization type 32 | * Implemented in 0.1.0 33 | - [x] Added clustered circle viz type 34 | * Implemented in 0.1.1 35 | - [x] Use PySAL for data domain natural breaks classification in example 36 | * Implemented in 0.1.1 37 | - [x] Support categorical measure data. 38 | * Implemneted in 0.5.1 39 | - [x] Add raster tile and image data layers 40 | * Added in 0.6.0 41 | - [x] Add choropleth / fill visualization type 42 | * Added in 0.6.0 43 | - [x] 3D extrusion map 44 | * Added in 0.7.0 45 | - [x] Linestring map 46 | * Added in 0.7.0 47 | -------------------------------------------------------------------------------- /docs/Makefile: -------------------------------------------------------------------------------- 1 | # Minimal makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line. 5 | SPHINXOPTS = 6 | SPHINXBUILD = sphinx-build 7 | SPHINXPROJ = mapbox-gl-js-jupyter 8 | SOURCEDIR = . 9 | BUILDDIR = _build 10 | 11 | # Put it first so that "make" without argument is like "make help". 12 | help: 13 | @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 14 | 15 | .PHONY: help Makefile 16 | 17 | # Catch-all target: route all unknown targets to Sphinx using the new 18 | # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). 19 | %: Makefile 20 | @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) -------------------------------------------------------------------------------- /docs/api/mapboxgl.rst: -------------------------------------------------------------------------------- 1 | mapboxgl package 2 | ================ 3 | 4 | Submodules 5 | ---------- 6 | 7 | mapboxgl.colors module 8 | ---------------------- 9 | 10 | .. automodule:: mapboxgl.colors 11 | :members: 12 | :undoc-members: 13 | :show-inheritance: 14 | 15 | mapboxgl.errors module 16 | ---------------------- 17 | 18 | .. automodule:: mapboxgl.errors 19 | :members: 20 | :undoc-members: 21 | :show-inheritance: 22 | 23 | mapboxgl.templates module 24 | ------------------------- 25 | 26 | .. automodule:: mapboxgl.templates 27 | :members: 28 | :undoc-members: 29 | :show-inheritance: 30 | 31 | mapboxgl.utils module 32 | --------------------- 33 | 34 | .. automodule:: mapboxgl.utils 35 | :members: 36 | :undoc-members: 37 | :show-inheritance: 38 | 39 | mapboxgl.viz module 40 | ------------------- 41 | 42 | .. automodule:: mapboxgl.viz 43 | :members: 44 | :undoc-members: 45 | :show-inheritance: 46 | 47 | 48 | Module contents 49 | --------------- 50 | 51 | .. automodule:: mapboxgl 52 | :members: 53 | :undoc-members: 54 | :show-inheritance: 55 | -------------------------------------------------------------------------------- /docs/api/modules.rst: -------------------------------------------------------------------------------- 1 | mapboxgl 2 | ======== 3 | 4 | .. toctree:: 5 | :maxdepth: 4 6 | 7 | mapboxgl 8 | -------------------------------------------------------------------------------- /docs/conf.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # Configuration file for the Sphinx documentation builder. 4 | # 5 | # This file does only contain a selection of the most common options. For a 6 | # full list see the documentation: 7 | # http://www.sphinx-doc.org/en/master/config 8 | 9 | # -- Path setup -------------------------------------------------------------- 10 | 11 | # If extensions (or modules to document with autodoc) are in another directory, 12 | # add these directories to sys.path here. If the directory is relative to the 13 | # documentation root, use os.path.abspath to make it absolute, like shown here. 14 | # 15 | # import os 16 | # import sys 17 | # sys.path.insert(0, os.path.abspath('.')) 18 | 19 | 20 | # -- Project information ----------------------------------------------------- 21 | 22 | project = 'mapboxgljs-jupyter' 23 | copyright = '2018, Mapbox' 24 | author = 'Mapbox' 25 | 26 | # The short X.Y version 27 | version = '' 28 | # The full version, including alpha/beta/rc tags 29 | release = '' 30 | 31 | 32 | # -- General configuration --------------------------------------------------- 33 | 34 | # If your documentation needs a minimal Sphinx version, state it here. 35 | # 36 | # needs_sphinx = '1.0' 37 | 38 | source_parsers = { 39 | '.md': 'recommonmark.parser.CommonMarkParser', 40 | } 41 | 42 | # Add any Sphinx extension module names here, as strings. They can be 43 | # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom 44 | # ones. 45 | extensions = [ 46 | 'sphinx.ext.autodoc', 47 | 'sphinx.ext.autosummary', 48 | # 'numpydoc' 49 | ] 50 | 51 | # Add any paths that contain templates here, relative to this directory. 52 | templates_path = ['_templates'] 53 | 54 | # The suffix(es) of source filenames. 55 | # You can specify multiple suffix as a list of string: 56 | source_suffix = ['.rst', '.md'] 57 | 58 | # The master toctree document. 59 | master_doc = 'index' 60 | 61 | # The language for content autogenerated by Sphinx. Refer to documentation 62 | # for a list of supported languages. 63 | # 64 | # This is also used if you do content translation via gettext catalogs. 65 | # Usually you set "language" from the command line for these cases. 66 | language = None 67 | 68 | # List of patterns, relative to source directory, that match files and 69 | # directories to ignore when looking for source files. 70 | # This pattern also affects html_static_path and html_extra_path . 71 | exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store'] 72 | 73 | # The name of the Pygments (syntax highlighting) style to use. 74 | pygments_style = 'sphinx' 75 | 76 | 77 | # -- Options for HTML output ------------------------------------------------- 78 | 79 | # The theme to use for HTML and HTML Help pages. See the documentation for 80 | # a list of builtin themes. 81 | # 82 | html_theme = 'alabaster' 83 | 84 | # Theme options are theme-specific and customize the look and feel of a theme 85 | # further. For a list of options available for each theme, see the 86 | # documentation. 87 | # 88 | # html_theme_options = {} 89 | 90 | # Add any paths that contain custom static files (such as style sheets) here, 91 | # relative to this directory. They are copied after the builtin static files, 92 | # so a file named "default.css" will overwrite the builtin "default.css". 93 | html_static_path = ['_static'] 94 | 95 | # Custom sidebar templates, must be a dictionary that maps document names 96 | # to template names. 97 | # 98 | # The default sidebars (for documents that don't match any pattern) are 99 | # defined by theme itself. Builtin themes are using these templates by 100 | # default: ``['localtoc.html', 'relations.html', 'sourcelink.html', 101 | # 'searchbox.html']``. 102 | # 103 | # html_sidebars = {} 104 | 105 | 106 | # -- Options for HTMLHelp output --------------------------------------------- 107 | 108 | # Output file base name for HTML help builder. 109 | htmlhelp_basename = 'mapbox-gl-js-jupyterdoc' 110 | 111 | 112 | # -- Options for LaTeX output ------------------------------------------------ 113 | 114 | latex_elements = { 115 | # The paper size ('letterpaper' or 'a4paper'). 116 | # 117 | # 'papersize': 'letterpaper', 118 | 119 | # The font size ('10pt', '11pt' or '12pt'). 120 | # 121 | # 'pointsize': '10pt', 122 | 123 | # Additional stuff for the LaTeX preamble. 124 | # 125 | # 'preamble': '', 126 | 127 | # Latex figure (float) alignment 128 | # 129 | # 'figure_align': 'htbp', 130 | } 131 | 132 | # Grouping the document tree into LaTeX files. List of tuples 133 | # (source start file, target name, title, 134 | # author, documentclass [howto, manual, or own class]). 135 | latex_documents = [ 136 | (master_doc, 'mapbox-gl-js-jupyter.tex', 'mapbox-gl-js-jupyter Documentation', 137 | 'Mapbox', 'manual'), 138 | ] 139 | 140 | 141 | # -- Options for manual page output ------------------------------------------ 142 | 143 | # One entry per manual page. List of tuples 144 | # (source start file, name, description, authors, manual section). 145 | man_pages = [ 146 | (master_doc, 'mapbox-gl-js-jupyter', 'mapbox-gl-js-jupyter Documentation', 147 | [author], 1) 148 | ] 149 | 150 | 151 | # -- Options for Texinfo output ---------------------------------------------- 152 | 153 | # Grouping the document tree into Texinfo files. List of tuples 154 | # (source start file, target name, title, author, 155 | # dir menu entry, description, category) 156 | texinfo_documents = [ 157 | (master_doc, 'mapbox-gl-js-jupyter', 'mapbox-gl-js-jupyter Documentation', 158 | author, 'mapbox-gl-js-jupyter', 'One line description of project.', 159 | 'Miscellaneous'), 160 | ] 161 | -------------------------------------------------------------------------------- /docs/index.rst: -------------------------------------------------------------------------------- 1 | .. mapbox-gl-js-jupyter documentation master file, created by 2 | sphinx-quickstart on Fri May 4 11:31:00 2018. 3 | You can adapt this file completely to your liking, but it should at least 4 | contain the root `toctree` directive. 5 | 6 | 7 | .. include:: ../README.rst 8 | 9 | .. toctree:: 10 | :maxdepth: 1 11 | :caption: Contents: 12 | 13 | utils.md 14 | viz.md 15 | api/mapboxgl.rst 16 | api/modules.rst 17 | 18 | Indices and tables 19 | ================== 20 | 21 | * :ref:`genindex` 22 | * :ref:`modindex` 23 | * :ref:`search` 24 | -------------------------------------------------------------------------------- /docs/utils.md: -------------------------------------------------------------------------------- 1 | ## row_to_geojson 2 | Convert a pandas dataframe row to a geojson format object. Converts all datetimes to epoch seconds by default, ISO format, or formatted date/datetime strings. 3 | 4 | ### Params 5 | **row_to_geojson**(_row, lon, lat_) 6 | 7 | Parameter | Description 8 | --|-- 9 | row | Pandas dataframe row. 10 | lon | Name of dataframe column containing latitude values. 11 | lat | Name of dataframe column containing longitude values. 12 | precision | Number of decimal points for latitude and longitude values. 13 | date_format | string date format for serialization of date/datetime objects to GeoJSON (ex: 'epoch', 'iso', '%Y-%m-%d') 14 | 15 | ## df_to_geojson 16 | Convert a Pandas dataframe to a geojson format Python dictionary or as a line-delimited geojson file. 17 | 18 | ### Params 19 | **df_to_geojson**(_df, properties=None, lat='lat', lon='lon', precision=None, date_format='epoch', filename=None_) 20 | 21 | Parameter | Description 22 | --|-- 23 | df | Pandas dataframe 24 | properties | List of dataframe columns to include as object properties. Does not accept lat or lon as a valid property. 25 | lon | Name of dataframe column containing latitude values. 26 | lat | Name of dataframe column containing longitude values. 27 | precision | Accuracy of lat/lon values. Values are rounded to the desired precision. 28 | date_format | Date format for date and datetime data columns. Compatible with all Python datetime string formats or 'epoch', 'iso'. Default is epoch seconds. 29 | filename | Name of file for writing geojson data. Data is stored as an object if filename is not provided. 30 | 31 | ### Usage 32 | 33 | ```python 34 | import pandas as pd 35 | from mapboxgl.utils import * 36 | 37 | # Load gage data from sample csv 38 | data_url = "https://raw.githubusercontent.com/mapbox/mapboxgl-jupyter/master/examples/cdec.csv" 39 | df = pd.read_csv(data_url) 40 | 41 | # Convert Elevation series to float 42 | df['Elevation (feet)'] = df['Elevation (feet)'].astype(float) 43 | 44 | # Clean up by dropping null rows 45 | df.dropna(axis=1, how='all', inplace=True) 46 | 47 | # Create geojson file output 48 | df_to_geojson( 49 | df.fillna(''), 50 | filename="cdec.geojson", 51 | properties=['CDEC ID', 'CNRFC ID', 'Gage Type', 'Elevation (feet)'], 52 | precision=4 53 | ) 54 | >>> {'feature_count': 2353, 'filename': 'cdec.geojson', 'type': 'file'} 55 | 56 | # Create geojson FeatureCollection python dict saved to a variable named data 57 | data = df_to_geojson( 58 | df.fillna(''), 59 | properties=['CDEC ID', 'CNRFC ID', 'Gage Type', 'Elevation (feet)'], 60 | precision=4 61 | ) 62 | ``` 63 | 64 | ## geojson_to_dict_list 65 | Convert data passed as GeoJSON object, filename, URL to a Python list of dictionaries representing the join data from each feature. 66 | 67 | ### Params 68 | **geojson_to_dict_list**(_data_) 69 | 70 | Parameter | Description 71 | --|-- 72 | data | GeoJSON join-data for use with vector tiles 73 | 74 | 75 | ## convert_date_columns 76 | Convert datetime dataframe columns to JSON-serializable format (epoch seconds, ISO format, or Python strftime format, filename); returns dataframe with updated columns. 77 | 78 | ### Params 79 | **convert_date_columns**(_df, date_format='epoch'_) 80 | 81 | Parameter | Description 82 | --|-- 83 | df | Pandas dataframe 84 | date_format | Python datetime format string or one of `epoch`, `iso` 85 | 86 | 87 | ## scale_between 88 | Scale a minimum and maximum value to an equal interval domain list, with `numStops` values in in the list. 89 | 90 | ### Params 91 | **scale_between**(_minval, maxval, numStops_) 92 | 93 | Parameter | Description 94 | --|-- 95 | minval | minimum value 96 | maxval | maximum value 97 | numStops | number of intervals 98 | 99 | ## create_radius_stops 100 | Convert a Python list of a data domain (such as `[0, 1, 5, 100, 10000]`) into a radius ramp between a minimum and maxium radius value. 101 | 102 | ### Params 103 | **create_radius_stops**(_breaks, min_radius, max_radius_) 104 | 105 | Parameter | Description 106 | --|-- 107 | breaks | List of float values 108 | min_radius | Minimum radius value 109 | max_radius | Maximum radius value 110 | 111 | ## create_weight_stops 112 | Convert a Python list of a data domain (such as `[0, 1, 5, 100, 10000]`) into a weight-ramp for a heatmap. 113 | 114 | ### Params 115 | **create_weight_stops**(_breaks_) 116 | 117 | Parameter | Description 118 | --|-- 119 | breaks | List of float values 120 | 121 | 122 | ## create_numeric_stops 123 | Convert a Python list of a data domain (such as `[0, 1, 5, 100, 10000]`) into a generic numeric ramp between a minimum and maximum value, as in for a heatmap, choropleth 3-D extrusions, etc. 124 | 125 | ### Params 126 | **create_numeric_stops**(_breaks, min_value, max_value_) 127 | 128 | Parameter | Description 129 | --|-- 130 | breaks | List of float values 131 | min_value | Minimum ramp value 132 | max_value | Maximum ramp value 133 | 134 | 135 | ## create_color_stops 136 | Convert a Python list of a data domain (such as `[0, 1, 5, 100, 10000]`) into color ramp stops. Color ramps can be from colorBrewer, or a custom list of color values. 137 | 138 | ### Params 139 | **create_color_stops**(_breaks, colors='RdYlGn'_) 140 | 141 | Parameter | Description 142 | --|-- 143 | breaks | List of float values 144 | colors | String value for color ramp OR a list of colors as hex, RGB, or RGBA strings. 145 | 146 | ### Color Options 147 | 148 | **Multi-Hue** | **Single Hue** | **Diverging** | **Qualitative** 149 | --|--|--|-- 150 | YlGn | Blues | BrBG | Accent 151 | YlGnB | Greens | PiYG | Dark2 152 | BuGn | Greys | PRGn | Paired 153 | BuPu | Oranges | PuOr | Pastel1 154 | GnBu | Purples | RdBu | Pastel2 155 | OrRd | Reds | RdGy | Set1 156 | PuBu | | RdYlBu | Set2 157 | PuBuGn | | RdYlGn | Set3 158 | PuRd | | Spectral | 159 | RdPu | | | 160 | YlGn | | | 161 | YlOrBr | | | 162 | YlOrRd | | | 163 | 164 | ### Usage 165 | ```python 166 | from mapboxgl.utils import * 167 | import pandas as pd 168 | 169 | # Load data from sample csv 170 | data_url = 'https://raw.githubusercontent.com/mapbox/mapboxgl-jupyter/master/examples/points.csv' 171 | df = pd.read_csv(data_url) 172 | 173 | # Generate a new data domain breaks and a new color palette from colorBrewer2 174 | color_breaks = [0,10,100,1000,10000] 175 | color_stops = create_color_stops(color_breaks, colors='YlOrRd') 176 | ``` 177 | 178 | ## rgb_tuple_from_str 179 | Convert color represented as a string in format 'rgb(RRR,GGG,BBB)', 'rgba(RRR,GGG,BBB,alpha)', '#RRGGBB' or limited English color name (eg 'red') to tuple of integers from 0 to 255, (RRR, GGG, BBB). 180 | 181 | ### Params 182 | **rgb_tuple_from_str**(_rgb_string_) 183 | 184 | Parameter | Description 185 | --|-- 186 | rgb_string | color represented as string in form 'rgb(RRR,GGG,BBB)', 'rgba(RRR,GGG,BBB,alpha)', '#RRGGBB', or limited HTML color names (eg 'red') 187 | 188 | ### Usage 189 | ```python 190 | from mapboxgl.utils import rgb_tuple_from_str 191 | 192 | # convert color string to tuple of integers 193 | rgb_tuple_from_str('rgb(255,143,17') 194 | ``` 195 | 196 | ## color_map 197 | Convert color represented as a string in format 'rgb(RRR,GGG,BBB)' to tuple of integers from 0 to 255, (RRR, GGG, BBB). 198 | 199 | ### Params 200 | **color_map**(_lookup, color_stops, default_color='rgb(122,122,122)'_) 201 | 202 | Parameter | Description 203 | --|-- 204 | lookup | value is numeric for interpolated colors or string for categorical color stops 205 | color_stops | color ramp stops generated from `create_color_stops`, or custom list of numeric or categorical stops with paired colors 206 | default_color | representation of color as hex, RGB, or RGBA strings 207 | 208 | ### Usage 209 | ```python 210 | from mapboxgl.utils import create_color_stops, color_map 211 | 212 | # interpolate color for numeric color_stops 213 | color_stops = create_color_stops([0, 50, 100, 500, 1500], colors='YlOrRd') 214 | color = color_map(73, color_stops) 215 | 216 | # categorical look up 217 | match_color_stops = [ 218 | ['Massachusetts', 'rgb(46,204,113)'], 219 | ['Utah', 'rgb(231,76,60)'], 220 | ['California', 'rgb(142,68,173)'], 221 | ] 222 | color = color_map('California', match_color_stops, default_color='grey)') 223 | ``` 224 | 225 | 226 | ## height_map 227 | Return a height value (in meters) interpolated from given height_stops; for use with vector-based visualizations using fill-extrusion layers. 228 | 229 | ### Params 230 | **height_map**(_lookup, height_stops, default_height=10.0_) 231 | 232 | Parameter | Description 233 | --|-- 234 | lookup | value is numeric for interpolated heights or string for categorical height stops 235 | height_stops | height ramp stops generated from `create_numeric_stops`, or custom list of numeric or categorical stops with paired heights 236 | default_height | height, in meters, for display of 3-D extrusion on map 237 | 238 | ### Usage 239 | ```python 240 | from mapboxgl.utils import create_numeric_stops, height_map 241 | 242 | # interpolate height 243 | height_stops = create_numeric_stops([0, 50, 100, 500, 1500], 0, 150000) 244 | height = height_map(117, height_stops) 245 | ``` 246 | -------------------------------------------------------------------------------- /examples/data/mosaic.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mapbox/mapboxgl-jupyter/6c3cbb94e7a1684f9eb7ea6c6653228bede53b3d/examples/data/mosaic.png -------------------------------------------------------------------------------- /examples/notebooks/circle-vector.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# Mapboxgl Python Library\n", 8 | "\n", 9 | "https://github.com/mapbox/mapboxgl-jupyter" 10 | ] 11 | }, 12 | { 13 | "cell_type": "code", 14 | "execution_count": null, 15 | "metadata": {}, 16 | "outputs": [], 17 | "source": [ 18 | "import os\n", 19 | "from mapboxgl.viz import *\n", 20 | "from mapboxgl.utils import *\n", 21 | "import pandas as pd\n", 22 | "\n", 23 | "# Must be a public token, starting with `pk`\n", 24 | "token = os.getenv('MAPBOX_ACCESS_TOKEN')" 25 | ] 26 | }, 27 | { 28 | "cell_type": "markdown", 29 | "metadata": {}, 30 | "source": [ 31 | "## Vector source layer with data-join technique using JSON object" 32 | ] 33 | }, 34 | { 35 | "cell_type": "code", 36 | "execution_count": null, 37 | "metadata": {}, 38 | "outputs": [], 39 | "source": [ 40 | "# Load data from sample csv\n", 41 | "data_url = 'https://raw.githubusercontent.com/mapbox/mapboxgl-jupyter/master/examples/data/points.csv'\n", 42 | "df = pd.read_csv(data_url).round(3)\n", 43 | "\n", 44 | "# Generate data breaks using numpy quantiles and color stops from colorBrewer\n", 45 | "measure = 'Avg Medicare Payments'\n", 46 | "color_breaks = [round(df[measure].quantile(q=x*0.1), 2) for x in range(1,9)]\n", 47 | "color_stops = create_color_stops(color_breaks, colors='YlGnBu')\n", 48 | "\n", 49 | "data = json.loads(df.to_json(orient='records'))\n", 50 | "v = CircleViz(data,\n", 51 | " access_token=token,\n", 52 | " vector_url='mapbox://rsbaumann.2pgmr66a',\n", 53 | " vector_layer_name='healthcare-points-2yaw54',\n", 54 | " vector_join_property='Provider Id',\n", 55 | " data_join_property='Provider Id',\n", 56 | " color_property=measure,\n", 57 | " color_stops=color_stops,\n", 58 | " radius=2.5,\n", 59 | " stroke_color='black',\n", 60 | " stroke_width=0.2,\n", 61 | " center=(-95, 40),\n", 62 | " zoom=3,\n", 63 | " below_layer='waterway-label',\n", 64 | " legend_text_numeric_precision=0)\n", 65 | "v.show()" 66 | ] 67 | }, 68 | { 69 | "cell_type": "markdown", 70 | "metadata": {}, 71 | "source": [ 72 | "## GraduatedCircleViz with data-driven style using vector data only" 73 | ] 74 | }, 75 | { 76 | "cell_type": "code", 77 | "execution_count": null, 78 | "metadata": {}, 79 | "outputs": [], 80 | "source": [ 81 | "# Generate data breaks and color stops from colorBrewer\n", 82 | "measure_color = 'Avg Covered Charges'\n", 83 | "color_breaks = [round(df[measure_color].quantile(q=x*0.1), 2) for x in range(2, 10)]\n", 84 | "color_stops = create_color_stops(color_breaks, colors='Blues')\n", 85 | "\n", 86 | "# Generate radius breaks from data domain and circle-radius range\n", 87 | "measure_radius = 'Avg Medicare Payments'\n", 88 | "radius_breaks = [round(df[measure_radius].quantile(q=x*0.1), 2) for x in range(2,10)]\n", 89 | "radius_stops = create_radius_stops(radius_breaks, 0.5, 10)\n", 90 | "\n", 91 | "# Create the viz\n", 92 | "viz2 = GraduatedCircleViz([],\n", 93 | " access_token=token,\n", 94 | " vector_url='mapbox://rsbaumann.2pgmr66a',\n", 95 | " vector_layer_name='healthcare-points-2yaw54',\n", 96 | " disable_data_join=True,\n", 97 | " color_property='Avg Covered Charges',\n", 98 | " color_stops=color_stops,\n", 99 | " radius_property=\"Avg Medicare Payments\",\n", 100 | " radius_stops=radius_stops,\n", 101 | " stroke_color='black',\n", 102 | " stroke_width=0.5,\n", 103 | " center=(-95, 40),\n", 104 | " zoom=3,\n", 105 | " opacity=0.75,\n", 106 | " legend_text_numeric_precision=0,\n", 107 | " below_layer='waterway-label')\n", 108 | "\n", 109 | "viz2.show()" 110 | ] 111 | }, 112 | { 113 | "cell_type": "markdown", 114 | "metadata": {}, 115 | "source": [ 116 | "## HeatmapViz with data-driven style using vector data only" 117 | ] 118 | }, 119 | { 120 | "cell_type": "code", 121 | "execution_count": null, 122 | "metadata": {}, 123 | "outputs": [], 124 | "source": [ 125 | "measure = 'Avg Medicare Payments'\n", 126 | "heatmap_color_stops = create_color_stops([0.01, 0.25, 0.5, 0.75, 1], colors='RdPu')\n", 127 | "heatmap_radius_stops = [[0, 3], [14, 100]]\n", 128 | "color_breaks = [round(df[measure].quantile(q=x*0.1), 2) for x in range(2,10)]\n", 129 | "color_stops = create_color_stops(color_breaks, colors='Spectral')\n", 130 | "heatmap_weight_stops = create_weight_stops(color_breaks)\n", 131 | "\n", 132 | "viz = HeatmapViz([], \n", 133 | " disable_data_join=True,\n", 134 | " access_token=token,\n", 135 | " vector_url='mapbox://rsbaumann.2pgmr66a',\n", 136 | " vector_layer_name='healthcare-points-2yaw54',\n", 137 | " weight_property='Avg Medicare Payments',\n", 138 | " weight_stops=heatmap_weight_stops,\n", 139 | " color_stops=heatmap_color_stops,\n", 140 | " radius_stops=heatmap_radius_stops,\n", 141 | " opacity=0.8,\n", 142 | " center=(-95, 40),\n", 143 | " zoom=3,\n", 144 | " below_layer='waterway-label')\n", 145 | "\n", 146 | "viz.show()" 147 | ] 148 | } 149 | ], 150 | "metadata": { 151 | "kernelspec": { 152 | "display_name": "Python 3", 153 | "language": "python", 154 | "name": "python3" 155 | }, 156 | "language_info": { 157 | "codemirror_mode": { 158 | "name": "ipython", 159 | "version": 3 160 | }, 161 | "file_extension": ".py", 162 | "mimetype": "text/x-python", 163 | "name": "python", 164 | "nbconvert_exporter": "python", 165 | "pygments_lexer": "ipython3", 166 | "version": "3.6.1" 167 | } 168 | }, 169 | "nbformat": 4, 170 | "nbformat_minor": 2 171 | } 172 | -------------------------------------------------------------------------------- /examples/notebooks/image-vis-type-example.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# Mapboxgl Python Library for location data visualization\n", 8 | "\n", 9 | "https://github.com/mapbox/mapboxgl-jupyter\n", 10 | "\n", 11 | "### Requirements\n", 12 | "\n", 13 | "These examples require the installation of the following python modules\n", 14 | "\n", 15 | "```\n", 16 | "pip install mapboxgl\n", 17 | "```\n", 18 | "\n", 19 | "### Notes\n", 20 | "\n", 21 | "`ImageViz` object accepts either an url, a local path or a numpy ndarray data as input for an image source" 22 | ] 23 | }, 24 | { 25 | "cell_type": "code", 26 | "execution_count": null, 27 | "metadata": {}, 28 | "outputs": [], 29 | "source": [ 30 | "import os\n", 31 | "import numpy\n", 32 | "from matplotlib.pyplot import imread\n", 33 | "from mapboxgl.viz import ImageViz" 34 | ] 35 | }, 36 | { 37 | "cell_type": "markdown", 38 | "metadata": {}, 39 | "source": [ 40 | "## Set your Mapbox access token. \n", 41 | "Set a `MAPBOX_ACCESS_TOKEN` environment variable or copy/paste your token \n", 42 | "If you do not have a Mapbox access token, sign up for an account at https://www.mapbox.com/ \n", 43 | "If you already have an account, you can grab your token at https://www.mapbox.com/account/" 44 | ] 45 | }, 46 | { 47 | "cell_type": "code", 48 | "execution_count": null, 49 | "metadata": {}, 50 | "outputs": [], 51 | "source": [ 52 | "# Must be a public token, starting with `pk`\n", 53 | "token = os.getenv('MAPBOX_ACCESS_TOKEN')" 54 | ] 55 | }, 56 | { 57 | "cell_type": "markdown", 58 | "metadata": {}, 59 | "source": [ 60 | "## Display an image given an URL" 61 | ] 62 | }, 63 | { 64 | "cell_type": "code", 65 | "execution_count": null, 66 | "metadata": { 67 | "scrolled": false 68 | }, 69 | "outputs": [], 70 | "source": [ 71 | "img_url = 'https://raw.githubusercontent.com/mapbox/mapboxgl-jupyter/master/examples/data/mosaic.png'\n", 72 | "\n", 73 | "# Coordinates must be an array in the form of [UL, UR, LR, LL]\n", 74 | "coordinates = [[-123.40515640309, 38.534294809274336],\n", 75 | " [-115.92938988349292, 38.534294809274336],\n", 76 | " [-115.92938988349292, 32.08296982365502], \n", 77 | " [-123.40515640309, 32.08296982365502]]\n", 78 | "\n", 79 | "# Create the viz from the dataframe\n", 80 | "viz = ImageViz(img_url, \n", 81 | " coordinates, \n", 82 | " access_token=token,\n", 83 | " height='600px',\n", 84 | " center=(-119, 35),\n", 85 | " zoom=5,\n", 86 | " below_layer='waterway-label')\n", 87 | "viz.show()" 88 | ] 89 | }, 90 | { 91 | "cell_type": "markdown", 92 | "metadata": {}, 93 | "source": [ 94 | "## Display an image given a numpy.ndarray" 95 | ] 96 | }, 97 | { 98 | "cell_type": "code", 99 | "execution_count": null, 100 | "metadata": { 101 | "scrolled": false 102 | }, 103 | "outputs": [], 104 | "source": [ 105 | "img = imread(img_url)\n", 106 | "img = numpy.mean(img[::10, ::10], axis=2)\n", 107 | "\n", 108 | "# Coordinates must be an array in the form of [UL, UR, LR, LL]\n", 109 | "coordinates = [[-123.40515640309, 38.534294809274336],\n", 110 | " [-115.92938988349292, 38.534294809274336],\n", 111 | " [-115.92938988349292, 32.08296982365502], \n", 112 | " [-123.40515640309, 32.08296982365502]]\n", 113 | "\n", 114 | "# Create the viz from the dataframe\n", 115 | "viz = ImageViz(img, \n", 116 | " coordinates, \n", 117 | " access_token=token,\n", 118 | " height='600px',\n", 119 | " center=(-119, 35),\n", 120 | " zoom=5,\n", 121 | " below_layer='waterway-label')\n", 122 | "viz.show()" 123 | ] 124 | }, 125 | { 126 | "cell_type": "markdown", 127 | "metadata": {}, 128 | "source": [ 129 | "## Display an image given a local path" 130 | ] 131 | }, 132 | { 133 | "cell_type": "code", 134 | "execution_count": null, 135 | "metadata": { 136 | "scrolled": false 137 | }, 138 | "outputs": [], 139 | "source": [ 140 | "# Coordinates must be an array in the form of [UL, UR, LR, LL]\n", 141 | "coordinates = [[-123.40515640309, 38.534294809274336],\n", 142 | " [-115.92938988349292, 38.534294809274336],\n", 143 | " [-115.92938988349292, 32.08296982365502], \n", 144 | " [-123.40515640309, 32.08296982365502]]\n", 145 | "\n", 146 | "# Create the viz from the dataframe\n", 147 | "viz = ImageViz(img_url, \n", 148 | " coordinates, \n", 149 | " access_token=token,\n", 150 | " height='600px',\n", 151 | " center=(-119, 35),\n", 152 | " zoom=5,\n", 153 | " below_layer='waterway-label')\n", 154 | "viz.show()" 155 | ] 156 | }, 157 | { 158 | "cell_type": "markdown", 159 | "metadata": {}, 160 | "source": [ 161 | "## Choose a colormap" 162 | ] 163 | }, 164 | { 165 | "cell_type": "code", 166 | "execution_count": null, 167 | "metadata": {}, 168 | "outputs": [], 169 | "source": [ 170 | "from matplotlib import cm\n", 171 | "\n", 172 | "img = imread(img_url)\n", 173 | "img = numpy.mean(img[::10, ::10], axis=2)\n", 174 | "img = cm.magma(img)\n", 175 | "\n", 176 | "# Coordinates must be an array in the form of [UL, UR, LR, LL]\n", 177 | "coordinates = [[-123.40515640309, 38.534294809274336],\n", 178 | " [-115.92938988349292, 38.534294809274336],\n", 179 | " [-115.92938988349292, 32.08296982365502], \n", 180 | " [-123.40515640309, 32.08296982365502]]\n", 181 | "\n", 182 | "# Create the viz from the dataframe\n", 183 | "viz = ImageViz(img_url, \n", 184 | " coordinates, \n", 185 | " access_token=token,\n", 186 | " height='600px',\n", 187 | " center=(-119, 35),\n", 188 | " zoom=5,\n", 189 | " below_layer='waterway-label')\n", 190 | "viz.show()" 191 | ] 192 | } 193 | ], 194 | "metadata": { 195 | "anaconda-cloud": { 196 | "attach-environment": true, 197 | "environment": "Root", 198 | "summary": "Mapboxgl Python Data Visualization example" 199 | }, 200 | "kernelspec": { 201 | "display_name": "Python 3", 202 | "language": "python", 203 | "name": "python3" 204 | }, 205 | "language_info": { 206 | "codemirror_mode": { 207 | "name": "ipython", 208 | "version": 3 209 | }, 210 | "file_extension": ".py", 211 | "mimetype": "text/x-python", 212 | "name": "python", 213 | "nbconvert_exporter": "python", 214 | "pygments_lexer": "ipython3", 215 | "version": "3.6.1" 216 | } 217 | }, 218 | "nbformat": 4, 219 | "nbformat_minor": 1 220 | } 221 | -------------------------------------------------------------------------------- /examples/notebooks/legend-controls.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# Mapboxgl Python Library for location data visualization\n", 8 | "\n", 9 | "https://github.com/mapbox/mapboxgl-jupyter\n", 10 | "\n", 11 | "\n", 12 | "# Legend Styles and Controls" 13 | ] 14 | }, 15 | { 16 | "cell_type": "markdown", 17 | "metadata": {}, 18 | "source": [ 19 | "| Property | Description | Example |\n", 20 | "|:--------- |:-------------|:--------|\n", 21 | "| legend | controls visibility of map legend | True |\n", 22 | "| legend_layout | controls orientation of map legend | 'horizontal' |\n", 23 | "| legend_style | reserved for future custom CSS loading | '' |\n", 24 | "| legend_gradient | boolean to determine appearance of legend keys; takes precedent over legend_key_shape | False |\n", 25 | "| legend_fill | string background color for legend | 'white' |\n", 26 | "| legend_header_fill | string background color for legend header (in vertical layout) | 'white' |\n", 27 | "| legend_text_color | string color for legend text | '#6e6e6e' |\n", 28 | "| legend_text_numeric_precision | decimal precision for numeric legend values | 0 |\n", 29 | "| legend_title_halo_color | color of legend title text halo | 'white' |\n", 30 | "| legend_key_shape | shape of the legend item keys, default varies by viz type; one of square, contiguous_bar, rounded-square, circle, line | 'square' |\n", 31 | "| legend_key_borders_on | boolean for whether to show/hide legend key borders | False |" 32 | ] 33 | }, 34 | { 35 | "cell_type": "markdown", 36 | "metadata": {}, 37 | "source": [ 38 | "## Create a visualization from example data file" 39 | ] 40 | }, 41 | { 42 | "cell_type": "code", 43 | "execution_count": null, 44 | "metadata": {}, 45 | "outputs": [], 46 | "source": [ 47 | "import pandas as pd\n", 48 | "import os\n", 49 | "from mapboxgl.utils import *\n", 50 | "from mapboxgl.viz import *\n", 51 | "\n", 52 | "# Set Mapbox Acces Token; Must be a public token, starting with `pk`\n", 53 | "token = os.getenv('MAPBOX_ACCESS_TOKEN')\n", 54 | "\n", 55 | "# Load data from sample csv\n", 56 | "data_url = 'https://raw.githubusercontent.com/mapbox/mapboxgl-jupyter/master/examples/data/points.csv'\n", 57 | "df = pd.read_csv(data_url).round(3)\n", 58 | "\n", 59 | "# Create a geojson Feature Collection from the current dataframe\n", 60 | "geodata = df_to_geojson(df, \n", 61 | " properties=['Avg Medicare Payments', 'Avg Covered Charges', 'date'], \n", 62 | " lat='lat', \n", 63 | " lon='lon', \n", 64 | " precision=3)\n", 65 | "\n", 66 | "# Generate data breaks using numpy quantiles and color stops from colorBrewer\n", 67 | "measure = 'Avg Medicare Payments'\n", 68 | "color_breaks = [round(df[measure].quantile(q=x*0.1), 2) for x in range(1,9)]\n", 69 | "color_stops = create_color_stops(color_breaks, colors='YlOrRd')\n", 70 | "\n", 71 | "# Create the viz from the dataframe\n", 72 | "viz = CircleViz(geodata, \n", 73 | " access_token=token, \n", 74 | " color_property='Avg Medicare Payments',\n", 75 | " color_stops=color_stops,\n", 76 | " radius=2.5,\n", 77 | " stroke_width=0.2,\n", 78 | " center=(-95, 40),\n", 79 | " zoom=2.5,\n", 80 | " below_layer='waterway-label',\n", 81 | " height='300px')\n", 82 | "\n", 83 | "# Show the viz\n", 84 | "viz.show()" 85 | ] 86 | }, 87 | { 88 | "cell_type": "markdown", 89 | "metadata": {}, 90 | "source": [ 91 | "## Change the legend style to a horizontal contiguous bar" 92 | ] 93 | }, 94 | { 95 | "cell_type": "code", 96 | "execution_count": null, 97 | "metadata": {}, 98 | "outputs": [], 99 | "source": [ 100 | "viz.legend = True\n", 101 | "viz.legend_layout = 'horizontal'\n", 102 | "viz.legend_key_borders_on = True\n", 103 | "viz.legend_key_shape = 'contiguous-bar'\n", 104 | "viz.legend_text_numeric_precision = 0\n", 105 | "viz.legend_key_borders_on = False\n", 106 | "viz.show()" 107 | ] 108 | }, 109 | { 110 | "cell_type": "markdown", 111 | "metadata": {}, 112 | "source": [ 113 | "## Gradient legend (assumes linear color gradient)" 114 | ] 115 | }, 116 | { 117 | "cell_type": "code", 118 | "execution_count": null, 119 | "metadata": {}, 120 | "outputs": [], 121 | "source": [ 122 | "viz.legend_gradient = True\n", 123 | "viz.show()" 124 | ] 125 | }, 126 | { 127 | "cell_type": "markdown", 128 | "metadata": {}, 129 | "source": [ 130 | "## Change the viz legend colors" 131 | ] 132 | }, 133 | { 134 | "cell_type": "code", 135 | "execution_count": null, 136 | "metadata": { 137 | "scrolled": true 138 | }, 139 | "outputs": [], 140 | "source": [ 141 | "viz.legend_layout = 'vertical'\n", 142 | "viz.legend_gradient = False\n", 143 | "viz.legend_fill = '#f0f0ef'\n", 144 | "viz.legend_text_color = '#000000'\n", 145 | "viz.legend_header_fill = '#819092'\n", 146 | "viz.legend_key_borders_on = False\n", 147 | "viz.legend_title_halo_color = '#777'\n", 148 | "viz.legend_key_shape = 'rounded-square'\n", 149 | "viz.show()" 150 | ] 151 | }, 152 | { 153 | "cell_type": "markdown", 154 | "metadata": {}, 155 | "source": [ 156 | "## Update legend to match Mapbox Dark-v9 style" 157 | ] 158 | }, 159 | { 160 | "cell_type": "code", 161 | "execution_count": null, 162 | "metadata": {}, 163 | "outputs": [], 164 | "source": [ 165 | "# Map settings\n", 166 | "viz.style='mapbox://styles/mapbox/dark-v9?optimize=true'\n", 167 | "viz.label_color = 'hsl(0, 0%, 70%)'\n", 168 | "viz.label_halo_color = 'hsla(0, 0%, 10%, 0.75)'\n", 169 | "viz.height = '400px'\n", 170 | "\n", 171 | "# Legend settings\n", 172 | "viz.legend_gradient = False\n", 173 | "viz.legend_fill = '#343332'\n", 174 | "viz.legend_header_fill = '#343332'\n", 175 | "viz.legend_text_color = 'hsl(0, 0%, 70%)'\n", 176 | "viz.legend_key_borders_on = False\n", 177 | "viz.legend_title_halo_color = 'hsla(0, 0%, 10%, 0.75)'\n", 178 | "\n", 179 | "# Render map\n", 180 | "viz.show()" 181 | ] 182 | }, 183 | { 184 | "cell_type": "markdown", 185 | "metadata": { 186 | "collapsed": true 187 | }, 188 | "source": [ 189 | "## Default legend for a graduated circle viz" 190 | ] 191 | }, 192 | { 193 | "cell_type": "code", 194 | "execution_count": null, 195 | "metadata": {}, 196 | "outputs": [], 197 | "source": [ 198 | "# Generate data breaks and color stops from colorBrewer\n", 199 | "measure_color = 'Avg Covered Charges'\n", 200 | "color_breaks = [round(df[measure_color].quantile(q=x*0.1), 2) for x in range(2, 10)]\n", 201 | "color_stops = create_color_stops(color_breaks, colors='Blues')\n", 202 | "\n", 203 | "# Generate radius breaks from data domain and circle-radius range\n", 204 | "measure_radius = 'Avg Medicare Payments'\n", 205 | "radius_breaks = [round(df[measure_radius].quantile(q=x*0.1), 2) for x in range(2,10)]\n", 206 | "radius_stops = create_radius_stops(radius_breaks, 0.5, 10)\n", 207 | "\n", 208 | "# Create the viz\n", 209 | "viz2 = GraduatedCircleViz(geodata, \n", 210 | " access_token=token,\n", 211 | " color_property='Avg Covered Charges',\n", 212 | " color_stops=color_stops,\n", 213 | " radius_property='Avg Medicare Payments',\n", 214 | " radius_stops=radius_stops,\n", 215 | " stroke_color='black',\n", 216 | " stroke_width=0.5,\n", 217 | " center=(-95, 40),\n", 218 | " zoom=2.5,\n", 219 | " opacity=0.75,\n", 220 | " below_layer='waterway-label',\n", 221 | " height='300px')\n", 222 | "viz2.show()" 223 | ] 224 | }, 225 | { 226 | "cell_type": "markdown", 227 | "metadata": {}, 228 | "source": [ 229 | "## Variable Radius Legend for a graduated circle viz" 230 | ] 231 | }, 232 | { 233 | "cell_type": "code", 234 | "execution_count": null, 235 | "metadata": {}, 236 | "outputs": [], 237 | "source": [ 238 | "# Modify the viz\n", 239 | "viz2.legend_layout = 'horizontal'\n", 240 | "viz2.legend_text_numeric_precision = 0\n", 241 | "\n", 242 | "# Switch to a legend based on the radius property\n", 243 | "viz2.legend_function = 'radius'\n", 244 | "\n", 245 | "# Variable radius legend uses MapViz.color_default to set legend item color\n", 246 | "viz2.color_default = '#0d3d79'\n", 247 | "\n", 248 | "# Show updated viz\n", 249 | "viz2.show()" 250 | ] 251 | }, 252 | { 253 | "cell_type": "markdown", 254 | "metadata": {}, 255 | "source": [ 256 | "## Default legend for a clustered circle map" 257 | ] 258 | }, 259 | { 260 | "cell_type": "code", 261 | "execution_count": null, 262 | "metadata": { 263 | "scrolled": false 264 | }, 265 | "outputs": [], 266 | "source": [ 267 | "# Create a clustered circle map\n", 268 | "color_stops = create_color_stops([1, 10, 25, 50, 75, 100], colors='YlOrRd')\n", 269 | "\n", 270 | "viz3 = ClusteredCircleViz(geodata, \n", 271 | " access_token=token,\n", 272 | " color_stops=color_stops,\n", 273 | " stroke_color='black',\n", 274 | " radius_stops=[[1,5], [10, 10], [50, 15], [100, 20]],\n", 275 | " radius_default=2,\n", 276 | " cluster_maxzoom=10,\n", 277 | " cluster_radius=30,\n", 278 | " label_size=12,\n", 279 | " opacity=0.9,\n", 280 | " center=(-95, 40),\n", 281 | " zoom=2.5,\n", 282 | " height='300px')\n", 283 | "viz3.show()" 284 | ] 285 | }, 286 | { 287 | "cell_type": "markdown", 288 | "metadata": {}, 289 | "source": [ 290 | "## Default legend for choropleth viz" 291 | ] 292 | }, 293 | { 294 | "cell_type": "code", 295 | "execution_count": null, 296 | "metadata": {}, 297 | "outputs": [], 298 | "source": [ 299 | "# create choropleth from polygon features stored as GeoJSON\n", 300 | "viz4 = ChoroplethViz('https://raw.githubusercontent.com/mapbox/mapboxgl-jupyter/master/examples/data/us-states.geojson', \n", 301 | " color_property='density',\n", 302 | " color_stops=create_color_stops([0, 50, 100, 500, 1500], colors='YlOrRd'),\n", 303 | " color_function_type='interpolate',\n", 304 | " line_stroke='--',\n", 305 | " line_color='rgb(128,0,38)',\n", 306 | " line_width=1,\n", 307 | " opacity=0.8,\n", 308 | " center=(-96, 37.8),\n", 309 | " zoom=2.5,\n", 310 | " below_layer='waterway-label',\n", 311 | " height='300px')\n", 312 | "viz4.show()" 313 | ] 314 | }, 315 | { 316 | "cell_type": "markdown", 317 | "metadata": {}, 318 | "source": [ 319 | "## Change legend to vertical contiguous bar" 320 | ] 321 | }, 322 | { 323 | "cell_type": "code", 324 | "execution_count": null, 325 | "metadata": {}, 326 | "outputs": [], 327 | "source": [ 328 | "viz4.legend_layout = 'vertical'\n", 329 | "viz4.legend_key_borders_on = False\n", 330 | "viz4.legend_key_shape = 'contiguous-bar'\n", 331 | "viz4.show()" 332 | ] 333 | }, 334 | { 335 | "cell_type": "markdown", 336 | "metadata": {}, 337 | "source": [ 338 | "## Default legend for linestring viz" 339 | ] 340 | }, 341 | { 342 | "cell_type": "code", 343 | "execution_count": null, 344 | "metadata": {}, 345 | "outputs": [], 346 | "source": [ 347 | "viz5 = LinestringViz([{\"elevation\": x} for x in range(0, 21000, 10)], \n", 348 | " vector_url='mapbox://mapbox.mapbox-terrain-v2',\n", 349 | " vector_layer_name='contour',\n", 350 | " vector_join_property='ele',\n", 351 | " data_join_property='elevation',\n", 352 | " color_property='elevation',\n", 353 | " color_stops=create_color_stops([0, 50, 100, 200, 300], colors='YlOrRd'),\n", 354 | " line_width_stops=create_numeric_stops([0, 50, 100, 200, 300], 0.1, 4),\n", 355 | " line_width_property='elevation',\n", 356 | " line_width_function_type='interpolate',\n", 357 | " line_width_default='1',\n", 358 | " opacity=0.8,\n", 359 | " center=(-122.48, 37.83),\n", 360 | " zoom=13,\n", 361 | " below_layer='waterway-label',\n", 362 | " height='300px')\n", 363 | "viz5.show()" 364 | ] 365 | }, 366 | { 367 | "cell_type": "code", 368 | "execution_count": null, 369 | "metadata": {}, 370 | "outputs": [], 371 | "source": [] 372 | } 373 | ], 374 | "metadata": { 375 | "anaconda-cloud": { 376 | "attach-environment": true, 377 | "environment": "Root", 378 | "summary": "Mapboxgl Python Data Visualization example" 379 | }, 380 | "kernelspec": { 381 | "display_name": "Python 3", 382 | "language": "python", 383 | "name": "python3" 384 | }, 385 | "language_info": { 386 | "codemirror_mode": { 387 | "name": "ipython", 388 | "version": 3 389 | }, 390 | "file_extension": ".py", 391 | "mimetype": "text/x-python", 392 | "name": "python", 393 | "nbconvert_exporter": "python", 394 | "pygments_lexer": "ipython3", 395 | "version": "3.6.1" 396 | } 397 | }, 398 | "nbformat": 4, 399 | "nbformat_minor": 1 400 | } 401 | -------------------------------------------------------------------------------- /examples/notebooks/linestring-viz.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# Mapboxgl Python Library for location data visualization\n", 8 | "\n", 9 | "https://github.com/mapbox/mapboxgl-jupyter\n", 10 | "\n", 11 | "\n", 12 | "# Linestring Visualizations" 13 | ] 14 | }, 15 | { 16 | "cell_type": "code", 17 | "execution_count": null, 18 | "metadata": {}, 19 | "outputs": [], 20 | "source": [ 21 | "import random\n", 22 | "import os\n", 23 | "\n", 24 | "from mapboxgl.viz import LinestringViz\n", 25 | "from mapboxgl.utils import create_color_stops, create_numeric_stops\n", 26 | "\n", 27 | "# Must be a public token, starting with `pk`\n", 28 | "token = os.getenv('MAPBOX_ACCESS_TOKEN')\n", 29 | "\n", 30 | "# JSON join-data object\n", 31 | "data = [{\"elevation\": x, \"weight\": random.randint(0,100)} for x in range(0, 21000, 10)]\n", 32 | "\n", 33 | "# GeoJSON data object\n", 34 | "geojson = {\n", 35 | " \"type\": \"FeatureCollection\",\n", 36 | " \"features\": [{\n", 37 | " \"type\": \"Feature\",\n", 38 | " \"id\": \"01\", \n", 39 | " \"properties\": {\"sample\": 50, \"weight\": 1}, \n", 40 | " \"geometry\": {\n", 41 | " \"type\": \"LineString\",\n", 42 | " \"coordinates\": [\n", 43 | " [-122.4833858013153, 37.829607404976734],\n", 44 | " [-122.4830961227417, 37.82932776098012],\n", 45 | " [-122.4830746650696, 37.82932776098012],\n", 46 | " [-122.48218417167662, 37.82889558180985],\n", 47 | " [-122.48218417167662, 37.82890193740421],\n", 48 | " [-122.48221099376678, 37.82868372835086],\n", 49 | " [-122.4822163581848, 37.82868372835086],\n", 50 | " [-122.48205006122589, 37.82801003030873]\n", 51 | " ]\n", 52 | " }\n", 53 | " }, {\n", 54 | " \"type\": \"Feature\",\n", 55 | " \"id\": \"02\",\n", 56 | " \"properties\": {\"sample\": 500, \"weight\": 2},\n", 57 | " \"geometry\": {\n", 58 | " \"type\": \"LineString\",\n", 59 | " \"coordinates\": [\n", 60 | " [-122.4833858013153, 37.929607404976734],\n", 61 | " [-122.4830961227417, 37.83]\n", 62 | " ]\n", 63 | " }\n", 64 | " }, {\n", 65 | " \"type\": \"Feature\",\n", 66 | " \"properties\": {\"sample\": 5000, \"weight\": 1},\n", 67 | " \"geometry\": {\n", 68 | " \"type\": \"LineString\",\n", 69 | " \"coordinates\": [\n", 70 | " [-122.48369693756104, 37.83381888486939],\n", 71 | " [-122.48348236083984, 37.83317489144141],\n", 72 | " [-122.48339653015138, 37.83270036637107],\n", 73 | " [-122.48356819152832, 37.832056363179625],\n", 74 | " [-122.48404026031496, 37.83114119107971],\n", 75 | " [-122.48404026031496, 37.83049717427869],\n", 76 | " [-122.48348236083984, 37.829920943955045],\n", 77 | " [-122.48356819152832, 37.82954808664175],\n", 78 | " [-122.48507022857666, 37.82944639795659],\n", 79 | " [-122.48610019683838, 37.82880236636284],\n", 80 | " [-122.48695850372314, 37.82931081282506],\n", 81 | " [-122.48700141906738, 37.83080223556934],\n", 82 | " [-122.48751640319824, 37.83168351665737],\n", 83 | " [-122.48803138732912, 37.832158048267786],\n", 84 | " [-122.48888969421387, 37.83297152392784],\n", 85 | " [-122.48987674713133, 37.83263257682617],\n", 86 | " [-122.49043464660643, 37.832937629287755],\n", 87 | " [-122.49125003814696, 37.832429207817725],\n", 88 | " [-122.49163627624512, 37.832564787218985],\n", 89 | " [-122.49223709106445, 37.83337825839438],\n", 90 | " [-122.49378204345702, 37.83368330777276]\n", 91 | " ]\n", 92 | " }\n", 93 | " }]\n", 94 | "}" 95 | ] 96 | }, 97 | { 98 | "cell_type": "markdown", 99 | "metadata": {}, 100 | "source": [ 101 | "## GeoJSON Test Linestring Source" 102 | ] 103 | }, 104 | { 105 | "cell_type": "code", 106 | "execution_count": null, 107 | "metadata": { 108 | "scrolled": false 109 | }, 110 | "outputs": [], 111 | "source": [ 112 | "# make viz with GeoJSON source\n", 113 | "viz = LinestringViz(geojson, \n", 114 | " access_token=token,\n", 115 | " color_property='sample',\n", 116 | " color_stops=create_color_stops([0, 50, 100, 500, 1500], colors='Blues'),\n", 117 | " line_stroke='--',\n", 118 | " line_width_property='weight',\n", 119 | " line_width_stops=create_numeric_stops([0, 1, 2, 3, 4, 5], 0, 10),\n", 120 | " opacity=0.8,\n", 121 | " center=(-122.48, 37.83),\n", 122 | " zoom=12,\n", 123 | " below_layer='waterway-label')\n", 124 | "viz.show()" 125 | ] 126 | }, 127 | { 128 | "cell_type": "markdown", 129 | "metadata": {}, 130 | "source": [ 131 | "## Vector Linestring Source (Topography)" 132 | ] 133 | }, 134 | { 135 | "cell_type": "code", 136 | "execution_count": null, 137 | "metadata": { 138 | "scrolled": false 139 | }, 140 | "outputs": [], 141 | "source": [ 142 | "viz = LinestringViz(data, \n", 143 | " access_token=token,\n", 144 | " vector_url='mapbox://mapbox.mapbox-terrain-v2',\n", 145 | " vector_layer_name='contour',\n", 146 | " vector_join_property='ele',\n", 147 | " data_join_property='elevation',\n", 148 | " color_property='elevation',\n", 149 | " color_stops=create_color_stops([0, 50, 100, 200, 300], colors='YlOrRd'),\n", 150 | " line_width_stops=create_numeric_stops([0, 50, 100, 200, 300], 0.1, 4),\n", 151 | " line_width_property='elevation',\n", 152 | " line_width_function_type='interpolate',\n", 153 | " line_width_default='1',\n", 154 | " opacity=0.8,\n", 155 | " center=(-122.48, 37.83),\n", 156 | " zoom=13,\n", 157 | " below_layer='waterway-label')\n", 158 | "viz.show()" 159 | ] 160 | } 161 | ], 162 | "metadata": { 163 | "kernelspec": { 164 | "display_name": "Python 3", 165 | "language": "python", 166 | "name": "python3" 167 | }, 168 | "language_info": { 169 | "codemirror_mode": { 170 | "name": "ipython", 171 | "version": 3 172 | }, 173 | "file_extension": ".py", 174 | "mimetype": "text/x-python", 175 | "name": "python", 176 | "nbconvert_exporter": "python", 177 | "pygments_lexer": "ipython3", 178 | "version": "3.6.1" 179 | } 180 | }, 181 | "nbformat": 4, 182 | "nbformat_minor": 2 183 | } 184 | -------------------------------------------------------------------------------- /examples/notebooks/point-viz-categorical-example.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# Mapboxgl Python Library for location data visualization\n", 8 | "\n", 9 | "https://github.com/mapbox/mapboxgl-jupyter" 10 | ] 11 | }, 12 | { 13 | "cell_type": "markdown", 14 | "metadata": {}, 15 | "source": [ 16 | "## Example with Match-type Color Assignment (Categorical Data)" 17 | ] 18 | }, 19 | { 20 | "cell_type": "code", 21 | "execution_count": null, 22 | "metadata": { 23 | "scrolled": false 24 | }, 25 | "outputs": [], 26 | "source": [ 27 | "import pandas as pd\n", 28 | "import os\n", 29 | "\n", 30 | "from mapboxgl.viz import *\n", 31 | "from mapboxgl.utils import *" 32 | ] 33 | }, 34 | { 35 | "cell_type": "markdown", 36 | "metadata": {}, 37 | "source": [ 38 | "## Set your Mapbox access token. \n", 39 | "Set a `MAPBOX_ACCESS_TOKEN` environment variable or copy/paste your token \n", 40 | "If you do not have a Mapbox access token, sign up for an account at https://www.mapbox.com/ \n", 41 | "If you already have an account, you can grab your token at https://www.mapbox.com/account/" 42 | ] 43 | }, 44 | { 45 | "cell_type": "code", 46 | "execution_count": null, 47 | "metadata": {}, 48 | "outputs": [], 49 | "source": [ 50 | "# Must be a public token, starting with `pk`\n", 51 | "token = os.getenv('MAPBOX_ACCESS_TOKEN')" 52 | ] 53 | }, 54 | { 55 | "cell_type": "code", 56 | "execution_count": null, 57 | "metadata": {}, 58 | "outputs": [], 59 | "source": [ 60 | "# Load data from sample csv\n", 61 | "data_url = 'https://raw.githubusercontent.com/mapbox/mapboxgl-jupyter/master/examples/data/cdec.csv'\n", 62 | "df = pd.read_csv(data_url)\n", 63 | "\n", 64 | "# Convert Elevation series to float\n", 65 | "df['Elevation (feet)'] = df['Elevation (feet)'].astype(float)\n", 66 | "\n", 67 | "# Clean up by dropping null rows\n", 68 | "df = df.dropna(axis=1, how='all')\n", 69 | "\n", 70 | "df.head(2)" 71 | ] 72 | }, 73 | { 74 | "cell_type": "code", 75 | "execution_count": null, 76 | "metadata": {}, 77 | "outputs": [], 78 | "source": [ 79 | "# Create geojson data object\n", 80 | "cdec_data = df_to_geojson(df.fillna(''), \n", 81 | " properties=['CDEC ID', 'CNRFC ID', 'Gage Type', 'Elevation (feet)'], \n", 82 | " precision=4) " 83 | ] 84 | }, 85 | { 86 | "cell_type": "code", 87 | "execution_count": null, 88 | "metadata": {}, 89 | "outputs": [], 90 | "source": [ 91 | "# Assign color stops\n", 92 | "category_color_stops = [['reservoir', 'rgb(211,47,47)'], \n", 93 | " ['river', 'rgb(81,45,168)'], \n", 94 | " ['snow', 'rgb(2,136,209)'], \n", 95 | " ['precip', 'rgb(139,195,74)'], \n", 96 | " ['temp', 'rgb(255,160,0)']]\n", 97 | "\n", 98 | "# Initialize CircleViz with Categorical Measure Data\n", 99 | "viz = CircleViz(cdec_data, \n", 100 | " access_token=token,\n", 101 | " height='500px',\n", 102 | " label_property='CDEC ID',\n", 103 | " color_property='Gage Type',\n", 104 | " color_default='grey',\n", 105 | " color_function_type='match',\n", 106 | " color_stops=category_color_stops,\n", 107 | " radius=2,\n", 108 | " center=(-121, 38.5),\n", 109 | " zoom=4.5)\n", 110 | "\n", 111 | "# Render map\n", 112 | "viz.show() " 113 | ] 114 | }, 115 | { 116 | "cell_type": "markdown", 117 | "metadata": {}, 118 | "source": [ 119 | "## Standard linear interpolation behavior" 120 | ] 121 | }, 122 | { 123 | "cell_type": "code", 124 | "execution_count": null, 125 | "metadata": {}, 126 | "outputs": [], 127 | "source": [ 128 | "# Numeric color stops from ColorBrewer\n", 129 | "sample_color_stops = [[0.0, 'rgb(255,255,204)'],\n", 130 | " [100.0, 'rgb(255,237,160)'],\n", 131 | " [250.0, 'rgb(254,217,118)'],\n", 132 | " [500.0, 'rgb(254,178,76)'],\n", 133 | " [1000.0, 'rgb(253,141,60)'],\n", 134 | " [2000.0, 'rgb(252,78,42)'],\n", 135 | " [4000.0, 'rgb(227,26,28)'],\n", 136 | " [6000.0, 'rgb(189,0,38)'],\n", 137 | " [10000.0,'rgb(128,0,38)']]\n", 138 | "\n", 139 | "# Select temperature gage records with numeric elevation data\n", 140 | "temperature_df = df[df['Gage Type']=='temp']\n", 141 | "temperature_data = df_to_geojson(temperature_df, \n", 142 | " properties=['CDEC ID', 'Elevation (feet)'])\n", 143 | "\n", 144 | "# Test CircleViz with interval measure data\n", 145 | "viz2 = CircleViz(temperature_data, \n", 146 | " access_token=token, \n", 147 | " height='400px',\n", 148 | " color_property='Elevation (feet)', \n", 149 | " color_function_type='interpolate', \n", 150 | " color_stops=sample_color_stops, \n", 151 | " radius=2, \n", 152 | " center=(-121, 37.5), \n", 153 | " zoom=4.5, \n", 154 | " below_layer='waterway-label',\n", 155 | " legend_key_shape='contiguous-bar',\n", 156 | " legend_key_borders_on=False)\n", 157 | "\n", 158 | "# Render map\n", 159 | "viz2.show() " 160 | ] 161 | }, 162 | { 163 | "cell_type": "markdown", 164 | "metadata": {}, 165 | "source": [ 166 | "## Combination of match-type and interpolate-type color and radius assignment" 167 | ] 168 | }, 169 | { 170 | "cell_type": "code", 171 | "execution_count": null, 172 | "metadata": {}, 173 | "outputs": [], 174 | "source": [ 175 | "# Radius stops for linear interpolation\n", 176 | "sample_radius_stops = [[0.0, 1.0],\n", 177 | " [100.0, 2.0],\n", 178 | " [500.0, 3.0],\n", 179 | " [1000.0, 4.0],\n", 180 | " [5000.0, 5.0],\n", 181 | " [10000.0, 6.0]]\n", 182 | "\n", 183 | "# Initialize Graduated Circle Visualization \n", 184 | "viz3 = GraduatedCircleViz(cdec_data, \n", 185 | " access_token=token, \n", 186 | " height='400px',\n", 187 | " color_function_type='match', \n", 188 | " color_stops=category_color_stops, \n", 189 | " color_property='Gage Type', \n", 190 | " color_default='grey', \n", 191 | " opacity=0.5, \n", 192 | " radius_property='Elevation (feet)', \n", 193 | " radius_stops=sample_radius_stops, \n", 194 | " radius_function_type='interpolate', \n", 195 | " radius_default=2, \n", 196 | " center=(-121, 37.5), \n", 197 | " zoom=4.5, \n", 198 | " below_layer='waterway-label')\n", 199 | "\n", 200 | "# Render map\n", 201 | "viz3.show()" 202 | ] 203 | }, 204 | { 205 | "cell_type": "markdown", 206 | "metadata": {}, 207 | "source": [ 208 | "# Use match function for both color and radius" 209 | ] 210 | }, 211 | { 212 | "cell_type": "code", 213 | "execution_count": null, 214 | "metadata": {}, 215 | "outputs": [], 216 | "source": [ 217 | "# Radius stops for linear interpolation\n", 218 | "category_radius_stops = [['reservoir', 3], \n", 219 | " ['river', 5], \n", 220 | " ['snow', 8], \n", 221 | " ['precip', 11], \n", 222 | " ['temp', 14]]\n", 223 | "\n", 224 | "# Initialize Graduated Circle Visualization \n", 225 | "viz4 = GraduatedCircleViz(cdec_data, \n", 226 | " access_token=token, \n", 227 | " height='400px',\n", 228 | " color_function_type='match',\n", 229 | " color_stops=category_color_stops,\n", 230 | " color_property='Gage Type',\n", 231 | " color_default='grey',\n", 232 | " opacity=0.5,\n", 233 | " radius_property='Elevation (feet)',\n", 234 | " radius_stops=category_radius_stops,\n", 235 | " radius_function_type='match',\n", 236 | " radius_default=2,\n", 237 | " center=(-121, 37.5),\n", 238 | " zoom=4.5,\n", 239 | " below_layer='waterway-label')\n", 240 | "\n", 241 | "# Render map\n", 242 | "viz4.show()" 243 | ] 244 | }, 245 | { 246 | "cell_type": "code", 247 | "execution_count": null, 248 | "metadata": {}, 249 | "outputs": [], 250 | "source": [] 251 | } 252 | ], 253 | "metadata": { 254 | "anaconda-cloud": { 255 | "attach-environment": true, 256 | "environment": "Root", 257 | "summary": "Mapboxgl Python Data Visualization example" 258 | }, 259 | "kernelspec": { 260 | "display_name": "Python 3", 261 | "language": "python", 262 | "name": "python3" 263 | }, 264 | "language_info": { 265 | "codemirror_mode": { 266 | "name": "ipython", 267 | "version": 3 268 | }, 269 | "file_extension": ".py", 270 | "mimetype": "text/x-python", 271 | "name": "python", 272 | "nbconvert_exporter": "python", 273 | "pygments_lexer": "ipython3", 274 | "version": "3.6.1" 275 | } 276 | }, 277 | "nbformat": 4, 278 | "nbformat_minor": 1 279 | } 280 | -------------------------------------------------------------------------------- /examples/notebooks/point-viz-types-example.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# Mapboxgl Python Library for location data visualization\n", 8 | "\n", 9 | "https://github.com/mapbox/mapboxgl-jupyter\n", 10 | "\n", 11 | "### Requirements\n", 12 | "\n", 13 | "These examples require the installation of the following python modules\n", 14 | "\n", 15 | "```\n", 16 | "pip install mapboxgl\n", 17 | "pip install pandas\n", 18 | "```" 19 | ] 20 | }, 21 | { 22 | "cell_type": "code", 23 | "execution_count": null, 24 | "metadata": {}, 25 | "outputs": [], 26 | "source": [ 27 | "import json\n", 28 | "import pandas as pd\n", 29 | "import os\n", 30 | "from mapboxgl.utils import *\n", 31 | "from mapboxgl.viz import *" 32 | ] 33 | }, 34 | { 35 | "cell_type": "code", 36 | "execution_count": null, 37 | "metadata": {}, 38 | "outputs": [], 39 | "source": [ 40 | "# Load data from sample csv\n", 41 | "data_url = 'https://raw.githubusercontent.com/mapbox/mapboxgl-jupyter/master/examples/data/points.csv'\n", 42 | "df = pd.read_csv(data_url).round(3)\n", 43 | "df.head(2)" 44 | ] 45 | }, 46 | { 47 | "cell_type": "markdown", 48 | "metadata": {}, 49 | "source": [ 50 | "## Set your Mapbox access token. \n", 51 | "Set a `MAPBOX_ACCESS_TOKEN` environment variable or copy/paste your token \n", 52 | "If you do not have a Mapbox access token, sign up for an account at https://www.mapbox.com/ \n", 53 | "If you already have an account, you can grab your token at https://www.mapbox.com/account/" 54 | ] 55 | }, 56 | { 57 | "cell_type": "code", 58 | "execution_count": null, 59 | "metadata": {}, 60 | "outputs": [], 61 | "source": [ 62 | "# Must be a public token, starting with `pk`\n", 63 | "token = os.getenv('MAPBOX_ACCESS_TOKEN')" 64 | ] 65 | }, 66 | { 67 | "cell_type": "markdown", 68 | "metadata": {}, 69 | "source": [ 70 | "## Create a visualization from a Pandas dataframe" 71 | ] 72 | }, 73 | { 74 | "cell_type": "code", 75 | "execution_count": null, 76 | "metadata": {}, 77 | "outputs": [], 78 | "source": [ 79 | "# Create a geojson Feature Collection from the current dataframe\n", 80 | "# optionally create a geojson file export by including the argument \n", 81 | "# filename='../data/healthcare_points.geojson',\n", 82 | "geodata = df_to_geojson(df, \n", 83 | " properties=['Avg Medicare Payments', 'Avg Covered Charges', 'date'], \n", 84 | " lat='lat', \n", 85 | " lon='lon', \n", 86 | " precision=3)" 87 | ] 88 | }, 89 | { 90 | "cell_type": "code", 91 | "execution_count": null, 92 | "metadata": {}, 93 | "outputs": [], 94 | "source": [ 95 | "# Just show a map of the data\n", 96 | "viz = CircleViz(geodata, \n", 97 | " access_token=token, \n", 98 | " radius=2, \n", 99 | " center=(-95, 40), \n", 100 | " zoom=3)\n", 101 | "viz.show()" 102 | ] 103 | }, 104 | { 105 | "cell_type": "markdown", 106 | "metadata": {}, 107 | "source": [ 108 | "## Add data-driven styles and a legend to the viz" 109 | ] 110 | }, 111 | { 112 | "cell_type": "code", 113 | "execution_count": null, 114 | "metadata": { 115 | "scrolled": false 116 | }, 117 | "outputs": [], 118 | "source": [ 119 | "# Generate data breaks using numpy quantiles and color stops from colorBrewer\n", 120 | "measure = 'Avg Medicare Payments'\n", 121 | "color_breaks = [round(df[measure].quantile(q=x*0.1), 2) for x in range(1, 9)]\n", 122 | "color_stops = create_color_stops(color_breaks, colors='YlGnBu')\n", 123 | "\n", 124 | "# Create the viz from the dataframe\n", 125 | "viz = CircleViz(geodata,\n", 126 | " access_token=token, \n", 127 | " color_property=\"Avg Medicare Payments\",\n", 128 | " color_stops=color_stops,\n", 129 | " radius=2.5,\n", 130 | " stroke_color='black',\n", 131 | " stroke_width=0.2,\n", 132 | " center=(-95, 40),\n", 133 | " zoom=3,\n", 134 | " below_layer='waterway-label')\n", 135 | "viz.show()" 136 | ] 137 | }, 138 | { 139 | "cell_type": "markdown", 140 | "metadata": {}, 141 | "source": [ 142 | "## Add a scale bar to the viz" 143 | ] 144 | }, 145 | { 146 | "cell_type": "code", 147 | "execution_count": null, 148 | "metadata": {}, 149 | "outputs": [], 150 | "source": [ 151 | "viz.scale = True\n", 152 | "viz.scale_unit_system = 'imperial'\n", 153 | "viz.show()" 154 | ] 155 | }, 156 | { 157 | "cell_type": "markdown", 158 | "metadata": {}, 159 | "source": [ 160 | "## Add labels to the viz" 161 | ] 162 | }, 163 | { 164 | "cell_type": "code", 165 | "execution_count": null, 166 | "metadata": {}, 167 | "outputs": [], 168 | "source": [ 169 | "viz.label_property = \"Avg Medicare Payments\"\n", 170 | "viz.stroke_width = 0\n", 171 | "viz.label_size = 8\n", 172 | "viz.legend_text_numeric_precision = 2\n", 173 | "viz.show()" 174 | ] 175 | }, 176 | { 177 | "cell_type": "markdown", 178 | "metadata": {}, 179 | "source": [ 180 | "## Change viz data property and color scale" 181 | ] 182 | }, 183 | { 184 | "cell_type": "code", 185 | "execution_count": null, 186 | "metadata": {}, 187 | "outputs": [], 188 | "source": [ 189 | "# Generate a new data domain breaks and a new color palette from colorBrewer2\n", 190 | "measure = 'Avg Covered Charges'\n", 191 | "color_breaks = [round(df[measure].quantile(q=x*0.1), 1) for x in range(1, 9)]\n", 192 | "color_stops = create_color_stops(color_breaks, colors='YlOrRd')\n", 193 | "\n", 194 | "# Show the viz\n", 195 | "viz.color_property = 'Avg Covered Charges'\n", 196 | "viz.color_stops = color_stops\n", 197 | "viz.show()" 198 | ] 199 | }, 200 | { 201 | "cell_type": "markdown", 202 | "metadata": {}, 203 | "source": [ 204 | "### Change the viz map style" 205 | ] 206 | }, 207 | { 208 | "cell_type": "code", 209 | "execution_count": null, 210 | "metadata": { 211 | "scrolled": false 212 | }, 213 | "outputs": [], 214 | "source": [ 215 | "# Map settings\n", 216 | "viz.style = 'mapbox://styles/mapbox/dark-v9?optimize=true'\n", 217 | "viz.label_color = 'hsl(0, 0%, 70%)'\n", 218 | "viz.label_halo_color = 'hsla(0, 0%, 10%, 0.75)'\n", 219 | "\n", 220 | "# Legend settings\n", 221 | "viz.legend_gradient = False\n", 222 | "viz.legend_fill = '#343332'\n", 223 | "viz.legend_header_fill = '#343332'\n", 224 | "viz.legend_text_color = 'hsl(0, 0%, 70%)'\n", 225 | "viz.legend_key_borders_on = False\n", 226 | "viz.legend_title_halo_color = 'hsla(0, 0%, 10%, 0.75)'\n", 227 | "\n", 228 | "# Scale bar settings\n", 229 | "viz.scale = True\n", 230 | "viz.scale_border_color = 'hsla(0, 0%, 10%, 0.75)'\n", 231 | "viz.scale_position = 'bottom-left'\n", 232 | "viz.scale_background_color = '#343332'\n", 233 | "viz.scale_text_color = 'hsl(0, 0%, 70%)'\n", 234 | "\n", 235 | "# Render map\n", 236 | "viz.show()" 237 | ] 238 | }, 239 | { 240 | "cell_type": "markdown", 241 | "metadata": { 242 | "collapsed": true 243 | }, 244 | "source": [ 245 | "## Create a graduated cricle viz based on two data properties" 246 | ] 247 | }, 248 | { 249 | "cell_type": "code", 250 | "execution_count": null, 251 | "metadata": {}, 252 | "outputs": [], 253 | "source": [ 254 | "# Generate data breaks and color stops from colorBrewer\n", 255 | "measure_color = 'Avg Covered Charges'\n", 256 | "color_breaks = [round(df[measure_color].quantile(q=x*0.1), 2) for x in range(2, 10)]\n", 257 | "color_stops = create_color_stops(color_breaks, colors='Blues')\n", 258 | "\n", 259 | "# Generate radius breaks from data domain and circle-radius range\n", 260 | "measure_radius = 'Avg Medicare Payments'\n", 261 | "radius_breaks = [round(df[measure_radius].quantile(q=x*0.1), 2) for x in range(2, 10)]\n", 262 | "radius_stops = create_radius_stops(radius_breaks, 0.5, 10)\n", 263 | "\n", 264 | "# Create the viz\n", 265 | "viz2 = GraduatedCircleViz(geodata, \n", 266 | " access_token=token,\n", 267 | " color_property=\"Avg Covered Charges\",\n", 268 | " color_stops=color_stops,\n", 269 | " radius_property=\"Avg Medicare Payments\",\n", 270 | " radius_stops=radius_stops,\n", 271 | " stroke_color='black',\n", 272 | " stroke_width=0.5,\n", 273 | " center=(-95, 40),\n", 274 | " zoom=3,\n", 275 | " opacity=0.75,\n", 276 | " below_layer='waterway-label')\n", 277 | "viz2.show()" 278 | ] 279 | }, 280 | { 281 | "cell_type": "markdown", 282 | "metadata": {}, 283 | "source": [ 284 | "## Create a heatmap viz" 285 | ] 286 | }, 287 | { 288 | "cell_type": "code", 289 | "execution_count": null, 290 | "metadata": {}, 291 | "outputs": [], 292 | "source": [ 293 | "measure = 'Avg Medicare Payments'\n", 294 | "heatmap_color_stops = create_color_stops([0.01, 0.25, 0.5, 0.75, 1], colors='RdPu')\n", 295 | "heatmap_radius_stops = [[0, 3], [14, 100]] # increase radius with zoom\n", 296 | "\n", 297 | "color_breaks = [round(df[measure].quantile(q=x*0.1), 2) for x in range(2, 10)]\n", 298 | "color_stops = create_color_stops(color_breaks, colors='Spectral')\n", 299 | "\n", 300 | "heatmap_weight_stops = create_weight_stops(color_breaks)\n", 301 | "\n", 302 | "# Create the heatmap \n", 303 | "viz3 = HeatmapViz(geodata, \n", 304 | " access_token=token,\n", 305 | " weight_property=\"Avg Medicare Payments\",\n", 306 | " weight_stops=heatmap_weight_stops,\n", 307 | " color_stops=heatmap_color_stops,\n", 308 | " radius_stops=heatmap_radius_stops,\n", 309 | " opacity=0.8,\n", 310 | " center=(-95, 40),\n", 311 | " zoom=3,\n", 312 | " below_layer='waterway-label')\n", 313 | "viz3.show()" 314 | ] 315 | }, 316 | { 317 | "cell_type": "markdown", 318 | "metadata": {}, 319 | "source": [ 320 | "## Create a clustered circle map" 321 | ] 322 | }, 323 | { 324 | "cell_type": "code", 325 | "execution_count": null, 326 | "metadata": {}, 327 | "outputs": [], 328 | "source": [ 329 | "# Create a clustered circle map\n", 330 | "color_stops = create_color_stops([1, 10, 25, 50, 75, 100], colors='YlOrBr')\n", 331 | "\n", 332 | "viz4 = ClusteredCircleViz(geodata, \n", 333 | " access_token=token,\n", 334 | " color_stops=color_stops,\n", 335 | " stroke_color='black',\n", 336 | " radius_stops=[[1, 5], [10, 10], [50, 15], [100, 20]],\n", 337 | " radius_default=2,\n", 338 | " cluster_maxzoom=10,\n", 339 | " cluster_radius=30,\n", 340 | " label_size=12,\n", 341 | " opacity=0.9,\n", 342 | " center=(-95, 40),\n", 343 | " zoom=3)\n", 344 | "viz4.show()" 345 | ] 346 | }, 347 | { 348 | "cell_type": "markdown", 349 | "metadata": {}, 350 | "source": [ 351 | "# Save our viz to an HTML file for distribution\n", 352 | "### Note\n", 353 | "Viz export contains a reference to the data in this visualization. Serve data from the same directory as the HTML file to vis your visualization." 354 | ] 355 | }, 356 | { 357 | "cell_type": "code", 358 | "execution_count": null, 359 | "metadata": {}, 360 | "outputs": [], 361 | "source": [ 362 | "with open('viz4.html', 'w') as f:\n", 363 | " f.write(viz4.create_html())" 364 | ] 365 | }, 366 | { 367 | "cell_type": "markdown", 368 | "metadata": {}, 369 | "source": [ 370 | "### Run exported HTML example\n", 371 | "\n", 372 | "Python2: `python -m SimpleHTTPServer 8080`\n", 373 | "\n", 374 | "Python3: `python3 -m http.server 8080`\n", 375 | "\n", 376 | "Now navigate your browser to `http://localhost:8080/viz4.html` to see the viz" 377 | ] 378 | }, 379 | { 380 | "cell_type": "markdown", 381 | "metadata": {}, 382 | "source": [ 383 | "# Download the map canvas to a PNG file\n", 384 | "### Note\n", 385 | "Screen captures of the map and legend are separate; additional image processing is required to export a map with visible legend and other Mapbox control elements. Click either the `Map` or `Legend` links to download the corresponding PNG file." 386 | ] 387 | }, 388 | { 389 | "cell_type": "code", 390 | "execution_count": null, 391 | "metadata": {}, 392 | "outputs": [], 393 | "source": [ 394 | "viz4.add_snapshot_links = True\n", 395 | "viz4.show()" 396 | ] 397 | } 398 | ], 399 | "metadata": { 400 | "anaconda-cloud": { 401 | "attach-environment": true, 402 | "environment": "Root", 403 | "summary": "Mapboxgl Python Data Visualization example" 404 | }, 405 | "kernelspec": { 406 | "display_name": "Python 3", 407 | "language": "python", 408 | "name": "python3" 409 | }, 410 | "language_info": { 411 | "codemirror_mode": { 412 | "name": "ipython", 413 | "version": 3 414 | }, 415 | "file_extension": ".py", 416 | "mimetype": "text/x-python", 417 | "name": "python", 418 | "nbconvert_exporter": "python", 419 | "pygments_lexer": "ipython3", 420 | "version": "3.6.1" 421 | } 422 | }, 423 | "nbformat": 4, 424 | "nbformat_minor": 1 425 | } 426 | -------------------------------------------------------------------------------- /examples/notebooks/rastertile-viz-slippymap-example.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# Mapboxgl Python Library for location data visualizaiton\n", 8 | "\n", 9 | "https://github.com/mapbox/mapboxgl-jupyter\n", 10 | "\n", 11 | "### Requirements\n", 12 | "\n", 13 | "These examples require the installation of the following python modules\n", 14 | "\n", 15 | "```\n", 16 | "pip install mapboxgl rio-glui==1.0.0\n", 17 | "```" 18 | ] 19 | }, 20 | { 21 | "cell_type": "code", 22 | "execution_count": null, 23 | "metadata": {}, 24 | "outputs": [], 25 | "source": [ 26 | "import os\n", 27 | "\n", 28 | "from rio_glui.server import TileServer\n", 29 | "from rio_glui.raster import RasterTiles\n", 30 | "\n", 31 | "from mapboxgl.utils import *\n", 32 | "from mapboxgl.viz import *" 33 | ] 34 | }, 35 | { 36 | "cell_type": "code", 37 | "execution_count": null, 38 | "metadata": {}, 39 | "outputs": [], 40 | "source": [ 41 | "token = os.getenv('MAPBOX_ACCESS_TOKEN')" 42 | ] 43 | }, 44 | { 45 | "cell_type": "code", 46 | "execution_count": null, 47 | "metadata": {}, 48 | "outputs": [], 49 | "source": [ 50 | "# Create local tile server using `rio-glui`\n", 51 | "url = 'https://.s3.amazonaws.com/.tif'\n", 52 | "\n", 53 | "# Create raster tile object \n", 54 | "# More info: https://github.com/mapbox/rio-glui/blob/master/rio_glui/raster.py#L16-L44\n", 55 | "raster = RasterTiles(url, indexes=(2, 1, 3))\n", 56 | "\n", 57 | "# Create local tile server\n", 58 | "# More info: https://github.com/mapbox/rio-glui/blob/master/rio_glui/server.py#L21-L56\n", 59 | "ts = TileServer(raster)\n", 60 | "\n", 61 | "# Start tile server \n", 62 | "ts.start()" 63 | ] 64 | }, 65 | { 66 | "cell_type": "code", 67 | "execution_count": null, 68 | "metadata": { 69 | "scrolled": false 70 | }, 71 | "outputs": [], 72 | "source": [ 73 | "# Initialize RasterTiles Viz by passing our local tile server url `ts.get_tiles_url` \n", 74 | "viz = RasterTilesViz(ts.get_tiles_url(), \n", 75 | " tiles_bounds=ts.get_bounds(),\n", 76 | " center=ts.get_center(),\n", 77 | " access_token=token, \n", 78 | " height='1000px', \n", 79 | " zoom=13)\n", 80 | "viz.show()" 81 | ] 82 | }, 83 | { 84 | "cell_type": "code", 85 | "execution_count": null, 86 | "metadata": {}, 87 | "outputs": [], 88 | "source": [ 89 | "# We need to stop the previous server in order to create a new one\n", 90 | "ts.stop()\n", 91 | "\n", 92 | "raster = RasterTiles(url, indexes=(1,2,3))\n", 93 | "ts = TileServer(raster)\n", 94 | "ts.start()" 95 | ] 96 | }, 97 | { 98 | "cell_type": "code", 99 | "execution_count": null, 100 | "metadata": {}, 101 | "outputs": [], 102 | "source": [ 103 | "viz = RasterTilesViz(ts.get_tiles_url(), \n", 104 | " tiles_bounds=ts.get_bounds(),\n", 105 | " center=ts.get_center(),\n", 106 | " access_token=token, \n", 107 | " height='1000px', \n", 108 | " zoom=13)\n", 109 | "viz.show()" 110 | ] 111 | } 112 | ], 113 | "metadata": { 114 | "kernelspec": { 115 | "display_name": "Python 3", 116 | "language": "python", 117 | "name": "python3" 118 | }, 119 | "language_info": { 120 | "codemirror_mode": { 121 | "name": "ipython", 122 | "version": 3 123 | }, 124 | "file_extension": ".py", 125 | "mimetype": "text/x-python", 126 | "name": "python", 127 | "nbconvert_exporter": "python", 128 | "pygments_lexer": "ipython3", 129 | "version": "3.6.1" 130 | } 131 | }, 132 | "nbformat": 4, 133 | "nbformat_minor": 2 134 | } 135 | -------------------------------------------------------------------------------- /examples/notebooks/rastertile-viz-type-example.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# Mapboxgl Python Library for location data visualization\n", 8 | "\n", 9 | "https://github.com/mapbox/mapboxgl-jupyter\n", 10 | "\n", 11 | "### Requirements\n", 12 | "\n", 13 | "These examples require the installation of the following python modules\n", 14 | "\n", 15 | "```\n", 16 | "pip install mapboxgl\n", 17 | "```" 18 | ] 19 | }, 20 | { 21 | "cell_type": "code", 22 | "execution_count": null, 23 | "metadata": {}, 24 | "outputs": [], 25 | "source": [ 26 | "import os\n", 27 | "from mapboxgl.viz import RasterTilesViz\n", 28 | "\n", 29 | "# Set Mapbox Acces Token; Must be a public token, starting with `pk`\n", 30 | "token = os.getenv('MAPBOX_ACCESS_TOKEN')" 31 | ] 32 | }, 33 | { 34 | "cell_type": "markdown", 35 | "metadata": {}, 36 | "source": [ 37 | "## Create a map with raster tiles from OpenStreetMap.org" 38 | ] 39 | }, 40 | { 41 | "cell_type": "code", 42 | "execution_count": null, 43 | "metadata": {}, 44 | "outputs": [], 45 | "source": [ 46 | "tiles_url = 'https://a.tile.openstreetmap.org/{z}/{x}/{y}.png'\n", 47 | "\n", 48 | "# Create an empty style\n", 49 | "style = {'version': 8, 'sources': {}, 'layers': []}\n", 50 | "\n", 51 | "viz = RasterTilesViz(tiles_url, \n", 52 | " access_token=token,\n", 53 | " tiles_size=256,\n", 54 | " height='500px', \n", 55 | " zoom=3, \n", 56 | " style=style)\n", 57 | "viz.show()" 58 | ] 59 | } 60 | ], 61 | "metadata": { 62 | "kernelspec": { 63 | "display_name": "3.10.14", 64 | "language": "python", 65 | "name": "python3" 66 | }, 67 | "language_info": { 68 | "codemirror_mode": { 69 | "name": "ipython", 70 | "version": 3 71 | }, 72 | "file_extension": ".py", 73 | "mimetype": "text/x-python", 74 | "name": "python", 75 | "nbconvert_exporter": "python", 76 | "pygments_lexer": "ipython3", 77 | "version": "3.10.14" 78 | } 79 | }, 80 | "nbformat": 4, 81 | "nbformat_minor": 2 82 | } 83 | -------------------------------------------------------------------------------- /examples/notebooks/scale-annotation.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# Mapboxgl Python Library for location data visualization\n", 8 | "\n", 9 | "https://github.com/mapbox/mapboxgl-jupyter\n", 10 | "\n", 11 | "\n", 12 | "# Add a scale annotation to the map" 13 | ] 14 | }, 15 | { 16 | "cell_type": "markdown", 17 | "metadata": {}, 18 | "source": [ 19 | "| Property | Description | Example |\n", 20 | "|:--------- |:-------------|:--------|\n", 21 | "| scale | controls visibility of map scale annotation | True |\n", 22 | "| scale_position | controls the position of the map scale in the map controls pane | 'bottom-right' |\n", 23 | "| scale_background_color | string background color for scale | 'white' |\n", 24 | "| scale_border_color | string border color for scale | 'white' |\n", 25 | "| scale_text_color | string color for legend text | '#6e6e6e' |\n", 26 | "\n", 27 | " scale=True,\n", 28 | " scale_border_color='#eee',\n", 29 | " scale_background_color='red'," 30 | ] 31 | }, 32 | { 33 | "cell_type": "markdown", 34 | "metadata": {}, 35 | "source": [ 36 | "## Create a visualization from example data file" 37 | ] 38 | }, 39 | { 40 | "cell_type": "code", 41 | "execution_count": null, 42 | "metadata": {}, 43 | "outputs": [], 44 | "source": [ 45 | "import pandas as pd\n", 46 | "import os\n", 47 | "from mapboxgl.utils import *\n", 48 | "from mapboxgl.viz import *\n", 49 | "\n", 50 | "# Set Mapbox Acces Token; Must be a public token, starting with `pk`\n", 51 | "token = os.getenv('MAPBOX_ACCESS_TOKEN')\n", 52 | "\n", 53 | "# Load data from sample csv\n", 54 | "data_url = 'https://raw.githubusercontent.com/mapbox/mapboxgl-jupyter/master/examples/data/points.csv'\n", 55 | "df = pd.read_csv(data_url).round(3)\n", 56 | "\n", 57 | "# Generate data breaks using numpy quantiles and color stops from colorBrewer\n", 58 | "measure = 'Avg Medicare Payments'\n", 59 | "color_breaks = [round(df[measure].quantile(q=x*0.1), 2) for x in range(1,9)]\n", 60 | "color_stops = create_color_stops(color_breaks, colors='YlOrRd')\n", 61 | "\n", 62 | "# Create a geojson Feature Collection from the current dataframe\n", 63 | "geodata = df_to_geojson(df, \n", 64 | " properties=['Avg Medicare Payments', 'Avg Covered Charges', 'date'], \n", 65 | " lat='lat', \n", 66 | " lon='lon', \n", 67 | " precision=3)\n", 68 | "\n", 69 | "# Create the viz from the dataframe\n", 70 | "viz = CircleViz(geodata,\n", 71 | " access_token=token, \n", 72 | " color_property='Avg Medicare Payments',\n", 73 | " color_stops=color_stops,\n", 74 | " radius=2.5,\n", 75 | " stroke_width=0.2,\n", 76 | " center=(-95, 40),\n", 77 | " zoom=2.5,\n", 78 | " scale=True,\n", 79 | " scale_unit_system='imperial',\n", 80 | " below_layer='waterway-label',\n", 81 | " height='300px')\n", 82 | "\n", 83 | "# Show the viz\n", 84 | "viz.show()" 85 | ] 86 | }, 87 | { 88 | "cell_type": "markdown", 89 | "metadata": {}, 90 | "source": [ 91 | "## Update scale to match Mapbox Dark-v9 style" 92 | ] 93 | }, 94 | { 95 | "cell_type": "code", 96 | "execution_count": null, 97 | "metadata": {}, 98 | "outputs": [], 99 | "source": [ 100 | "# Map settings\n", 101 | "viz.style='mapbox://styles/mapbox/dark-v9?optimize=true'\n", 102 | "viz.label_color = 'hsl(0, 0%, 70%)'\n", 103 | "viz.label_halo_color = 'hsla(0, 0%, 10%, 0.75)'\n", 104 | "viz.height = '400px'\n", 105 | "\n", 106 | "# Legend settings\n", 107 | "viz.legend_gradient = False\n", 108 | "viz.legend_fill = '#343332'\n", 109 | "viz.legend_header_fill = '#343332'\n", 110 | "viz.legend_text_color = 'hsl(0, 0%, 70%)'\n", 111 | "viz.legend_key_borders_on = False\n", 112 | "viz.legend_title_halo_color = 'hsla(0, 0%, 10%, 0.75)'\n", 113 | "\n", 114 | "# Scale settings\n", 115 | "viz.scale_border_color = 'hsla(0, 0%, 10%, 0.75)'\n", 116 | "viz.scale_position = 'top-left'\n", 117 | "viz.scale_background_color = '#343332'\n", 118 | "viz.scale_text_color = 'hsl(0, 0%, 70%)'\n", 119 | "\n", 120 | "# Render map\n", 121 | "viz.show()" 122 | ] 123 | } 124 | ], 125 | "metadata": { 126 | "anaconda-cloud": { 127 | "attach-environment": true, 128 | "environment": "Root", 129 | "summary": "Mapboxgl Python Data Visualization example" 130 | }, 131 | "kernelspec": { 132 | "display_name": "Python 3", 133 | "language": "python", 134 | "name": "python3" 135 | }, 136 | "language_info": { 137 | "codemirror_mode": { 138 | "name": "ipython", 139 | "version": 3 140 | }, 141 | "file_extension": ".py", 142 | "mimetype": "text/x-python", 143 | "name": "python", 144 | "nbconvert_exporter": "python", 145 | "pygments_lexer": "ipython3", 146 | "version": "3.6.1" 147 | } 148 | }, 149 | "nbformat": 4, 150 | "nbformat_minor": 1 151 | } 152 | -------------------------------------------------------------------------------- /examples/screenshots/screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mapbox/mapboxgl-jupyter/6c3cbb94e7a1684f9eb7ea6c6653228bede53b3d/examples/screenshots/screenshot.png -------------------------------------------------------------------------------- /mapboxgl/__init__.py: -------------------------------------------------------------------------------- 1 | from .viz import CircleViz, GraduatedCircleViz, HeatmapViz, ClusteredCircleViz, ImageViz, RasterTilesViz, ChoroplethViz, LinestringViz 2 | 3 | __version__ = "0.10.2" 4 | __all__ = ['CircleViz', 'GraduatedCircleViz', 'HeatmapViz', 'ClusteredCircleViz', 'ImageViz', 'RasterTilesViz', 'ChoroplethViz', 'LinestringViz'] -------------------------------------------------------------------------------- /mapboxgl/errors.py: -------------------------------------------------------------------------------- 1 | class TokenError(ValueError): 2 | pass 3 | 4 | 5 | class ValueError(ValueError): 6 | pass 7 | 8 | 9 | class SourceDataError(ValueError): 10 | pass 11 | 12 | 13 | class LegendError(ValueError): 14 | pass 15 | 16 | 17 | class DateConversionError(ValueError): 18 | pass -------------------------------------------------------------------------------- /mapboxgl/templates.py: -------------------------------------------------------------------------------- 1 | from jinja2 import Environment, PackageLoader, StrictUndefined 2 | 3 | env = Environment( 4 | loader=PackageLoader('mapboxgl', 'templates'), 5 | autoescape=False, 6 | undefined=StrictUndefined 7 | ) 8 | 9 | 10 | def format(viz, **kwargs): 11 | template = env.get_template('{}.html'.format(viz)) 12 | return template.render(viz=viz, **kwargs) 13 | -------------------------------------------------------------------------------- /mapboxgl/templates/base.html: -------------------------------------------------------------------------------- 1 | {% extends "main.html" %} 2 | 3 | {% block javascript %} 4 | 5 | mapboxgl.accessToken = '{{ accessToken }}'; 6 | 7 | var transformRequest = function(url, resourceType) { 8 | const isMapboxRequest = url.slice(8, 22) === 'api.mapbox.com' || 9 | url.slice(10, 26) === 'tiles.mapbox.com'; 10 | 11 | return { 12 | url: isMapboxRequest ? url.replace('?', '?pluginName=PythonMapboxgl&') : url 13 | } 14 | }; 15 | 16 | var map = new mapboxgl.Map({ 17 | container: 'map', 18 | attributionControl: false, 19 | style: {{ style }}, 20 | center: {{ center }}, 21 | zoom: {{ zoom }}, 22 | pitch: {{ pitch }}, 23 | bearing: {{ bearing }}, 24 | scrollZoom: {{ scrollZoomOn|safe }}, 25 | touchZoom: {{ touchZoomOn|safe }}, 26 | doubleClickZoom: {{ doubleClickZoomOn|safe }}, 27 | boxZoom: {{ boxZoomOn|safe }}, 28 | preserveDrawingBuffer: {{ preserveDrawingBuffer|safe }}, 29 | transformRequest: transformRequest 30 | }); 31 | 32 | {% block attribution %} 33 | 34 | map.addControl(new mapboxgl.AttributionControl({ compact: true })); 35 | 36 | {% endblock attribution %} 37 | 38 | {% block navigation %} 39 | 40 | map.addControl(new mapboxgl.NavigationControl()); 41 | 42 | {% endblock navigation %} 43 | 44 | {% block legend %}{% endblock legend %} 45 | 46 | {% block scale %} 47 | 48 | {% if showScale %} 49 | 50 | map.addControl(new mapboxgl.ScaleControl({ unit: '{{ scaleUnits }}' }), '{{ scalePosition }}'); 51 | 52 | {% endif %} 53 | 54 | {% endblock scale %} 55 | 56 | {% block map %}{% endblock map %} 57 | 58 | {% endblock %} 59 | -------------------------------------------------------------------------------- /mapboxgl/templates/choropleth.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | 4 | {% block extra_css %} 5 | 8 | {% endblock extra_css %} 9 | 10 | {% block legend %} 11 | 12 | {% if showLegend %} 13 | {% if extrudeChoropleth %} 14 | {% if colorStops and colorProperty and heightProperty %} 15 | {% if colorProperty != heightProperty and extrudeChoropleth %} 16 | calcColorLegend({{ colorStops }}, "{{ colorProperty }} vs. {{ heightProperty }}"); 17 | {% else %} 18 | calcColorLegend({{ colorStops }}, "{{ colorProperty }}"); 19 | {% endif %} 20 | {% endif %} 21 | {% else %} 22 | calcColorLegend({{ colorStops }}, "{{ colorProperty }}"); 23 | {% endif %} 24 | {% endif %} 25 | 26 | {% endblock legend %} 27 | 28 | {% block map %} 29 | 30 | map.on('style.load', function() { 31 | 32 | {% block choropleth %} 33 | 34 | // Add geojson data source 35 | map.addSource("data", { 36 | "type": "geojson", 37 | "data": {{ geojson_data }}, 38 | "buffer": 1, 39 | "maxzoom": 14 40 | }); 41 | 42 | // Add data layer 43 | map.addLayer({ 44 | "id": "choropleth-fill", 45 | "source": "data", 46 | "type": "fill", 47 | "paint": { 48 | "fill-color": ["case", 49 | ["boolean", ["feature-state", "hover"], false], 50 | "{{ highlightColor }}", 51 | generatePropertyExpression("{{ colorType }}", "{{ colorProperty }}", {{ colorStops }}, "{{ defaultColor }}")], 52 | "fill-opacity": {{ opacity }} 53 | } 54 | }, "{{ belowLayer }}" ); 55 | 56 | // Add border layer 57 | map.addLayer({ 58 | "id": "choropleth-line", 59 | "source": "data", 60 | "type": "line", 61 | "layout": { 62 | "line-join": "round", 63 | "line-cap": "round" 64 | }, 65 | "paint": { 66 | {% if lineDashArray %} 67 | "line-dasharray": {{ lineDashArray }}, 68 | {% endif %} 69 | "line-color": ["case", 70 | ["boolean", ["feature-state", "hover"], false], 71 | "{{ highlightColor }}", 72 | "{{ lineColor }}"], 73 | "line-width": {{ lineWidth }}, 74 | "line-opacity": {{ lineOpacity }} 75 | } 76 | }, "{{ belowLayer }}" ); 77 | 78 | // Add label layer 79 | map.addLayer({ 80 | "id": "choropleth-label", 81 | "source": "data", 82 | "type": "symbol", 83 | "layout": { 84 | {% if labelProperty %} 85 | "text-field": "{{ labelProperty }}", 86 | {% endif %} 87 | "text-size" : generateInterpolateExpression('zoom', [[0, {{ labelSize }}],[22, 3* {{ labelSize }}]] ), 88 | "text-offset": [0,-1] 89 | }, 90 | "paint": { 91 | "text-halo-color": "{{ labelHaloColor }}", 92 | "text-halo-width": generatePropertyExpression('interpolate', 'zoom', [[0,{{ labelHaloWidth }}], [18,5* {{ labelHaloWidth }}]]), 93 | "text-color": ["case", 94 | ["boolean", ["feature-state", "hover"], false], 95 | "{{ highlightColor }}", "{{ labelColor }}"] 96 | } 97 | }, "{{ belowLayer }}" ); 98 | 99 | // Optional extrusion layer 100 | {% if extrudeChoropleth %} 101 | 102 | map.addLayer({ 103 | id: "choropleth-extrusion", 104 | type: "fill-extrusion", 105 | source: "data", 106 | paint: { 107 | "fill-extrusion-opacity": {{ opacity }}, 108 | "fill-extrusion-color": ["case", 109 | ["boolean", ["feature-state", "hover"], false], 110 | "{{ highlightColor }}", 111 | generatePropertyExpression("{{ colorType }}", "{{ colorProperty }}", {{ colorStops }}, "{{ defaultColor }}")], 112 | "fill-extrusion-height": generatePropertyExpression("{{ heightType }}", "{{ heightProperty }}", {{ heightStops }}, {{ defaultHeight }}), 113 | } 114 | }, "{{ belowLayer }}"); 115 | 116 | {% endif %} 117 | 118 | {% endblock choropleth %} 119 | 120 | // Popups 121 | var interactionLayer = {% if extrudeChoropleth %} 'choropleth-extrusion' {% else %} 'choropleth-fill' {% endif %}; 122 | {% if popupOpensOnHover %} 123 | var popupAction = 'mousemove', 124 | popupSettings = { 125 | closeButton: false, 126 | closeOnClick: false 127 | }; 128 | {% else %} 129 | var popupAction = 'click', 130 | popupSettings = { 131 | closeButton: true, 132 | closeOnClick: true 133 | }; 134 | {% endif %} 135 | 136 | // Create a popup 137 | var popup = new mapboxgl.Popup(popupSettings); 138 | 139 | {% block choropleth_popup %} 140 | 141 | // Show the popup on mouseover 142 | var hoveredStateId = 0; 143 | 144 | map.on(popupAction, function(e) { 145 | var features = map.queryRenderedFeatures(e.point, {layers: [interactionLayer, 'choropleth-label'] }); 146 | 147 | if (features.length > 0) { 148 | map.getCanvas().style.cursor = 'pointer'; 149 | var f = features[0]; 150 | newHoveredStateId = f.id; 151 | if (newHoveredStateId != hoveredStateId) { 152 | map.removeFeatureState({source: 'data', id: hoveredStateId}); 153 | hoveredStateId = newHoveredStateId; 154 | } 155 | map.setFeatureState({source: 'data', id: hoveredStateId}, { hover: true}); 156 | let popup_html = '
'; 157 | 158 | for (key in f.properties) { 159 | popup_html += '
  • ' + key + ': ' + f.properties[key] + '
  • ' 160 | } 161 | 162 | popup_html += '
    ' 163 | popup.setLngLat(e.lngLat) 164 | .setHTML(popup_html) 165 | .addTo(map); 166 | } 167 | else { 168 | map.getCanvas().style.cursor = ''; 169 | popup.remove(); 170 | map.removeFeatureState({source: 'data', id: hoveredStateId}); 171 | } 172 | }); 173 | 174 | {% endblock choropleth_popup %} 175 | 176 | // Fly to selected feature on click 177 | map.on('click', interactionLayer, function(e) { 178 | map.easeTo({ 179 | center: e.lngLat 180 | }); 181 | }); 182 | 183 | }); 184 | 185 | {% endblock map %} 186 | -------------------------------------------------------------------------------- /mapboxgl/templates/circle.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | 4 | {% block extra_css %} 5 | 8 | {% endblock extra_css %} 9 | 10 | {% block legend %} 11 | 12 | {% if showLegend %} 13 | {% if colorStops and colorProperty %} 14 | calcColorLegend({{ colorStops }} , "{{ colorProperty }}"); 15 | {% endif %} 16 | {% endif %} 17 | 18 | {% endblock legend %} 19 | 20 | {% block map %} 21 | 22 | map.on('style.load', function() { 23 | 24 | {% block circle %} 25 | 26 | map.addSource("data", { 27 | "type": "geojson", 28 | "data": {{ geojson_data }}, 29 | "buffer": 1, 30 | "maxzoom": 14, 31 | "generateId": true 32 | }); 33 | 34 | map.addLayer({ 35 | "id": "label", 36 | "source": "data", 37 | "type": "symbol", 38 | "maxzoom": {{ maxzoom }}, 39 | "minzoom": {{ minzoom }}, 40 | "layout": { 41 | {% if labelProperty %} 42 | "text-field": "{{ labelProperty }}", 43 | {% endif %} 44 | "text-size" : generateInterpolateExpression('zoom', [[0, {{ labelSize }}],[22, 3* {{ labelSize }}]] ), 45 | "text-offset": [0,-1] 46 | }, 47 | "paint": { 48 | "text-halo-color": "{{ labelHaloColor }}", 49 | "text-halo-width": generatePropertyExpression('interpolate', 'zoom', [[0,{{ labelHaloWidth }}], [18,5* {{ labelHaloWidth }}]]), 50 | "text-color": ["case", 51 | ["boolean", ["feature-state", "hover"], false], 52 | "{{ highlightColor }}", 53 | "{{ labelColor }}"] 54 | } 55 | }, "{{belowLayer}}" ) 56 | 57 | map.addLayer({ 58 | "id": "circle", 59 | "source": "data", 60 | "type": "circle", 61 | "maxzoom": {{ maxzoom }}, 62 | "minzoom": {{ minzoom }}, 63 | "paint": { 64 | {% if colorProperty %} 65 | "circle-color": ["case", 66 | ["boolean", ["feature-state", "hover"], false], 67 | "{{ highlightColor }}", 68 | generatePropertyExpression("{{ colorType }}", "{{ colorProperty }}", {{ colorStops }}, "{{ defaultColor }}" )], 69 | {% else %} 70 | "circle-color": ["case", 71 | ["boolean", ["feature-state", "hover"], false], 72 | "{{ highlightColor }}", "{{ defaultColor }}"], 73 | {% endif %} 74 | "circle-radius" : generatePropertyExpression('interpolate', 'zoom', [[0,{{ radius }}], [22,10 * {{ radius }}]]), 75 | "circle-stroke-color": ["case", 76 | ["boolean", ["feature-state", "hover"], false], 77 | "{{ highlightColor }}", 78 | "{{ strokeColor }}"], 79 | "circle-stroke-width": generatePropertyExpression('interpolate', 'zoom', [[0,{{ strokeWidth }}], [18,5* {{ strokeWidth }}]]), 80 | "circle-opacity" : {{ opacity }}, 81 | "circle-stroke-opacity" : {{ opacity }} 82 | } 83 | }, "label"); 84 | 85 | {% endblock circle %} 86 | 87 | // Popups 88 | {% if popupOpensOnHover %} 89 | var popupAction = 'mousemove', 90 | popupSettings = { 91 | closeButton: false, 92 | closeOnClick: false 93 | }; 94 | {% else %} 95 | var popupAction = 'click', 96 | popupSettings = { 97 | closeButton: true, 98 | closeOnClick: true 99 | }; 100 | {% endif %} 101 | 102 | // Create a popup 103 | var popup = new mapboxgl.Popup(popupSettings); 104 | 105 | {% block circle_popup %} 106 | 107 | var hoveredStateId = 0; 108 | 109 | // Show the popup on mouseover 110 | map.on(popupAction, function(e) { 111 | 112 | var features = map.queryRenderedFeatures(e.point, {layers: ['circle', 'label'] }); 113 | 114 | if (features.length > 0) { 115 | map.getCanvas().style.cursor = 'pointer'; 116 | var f = features[0]; 117 | newHoveredStateId = f.id; 118 | if (newHoveredStateId != hoveredStateId) { 119 | map.removeFeatureState({source: 'data', id: hoveredStateId}); 120 | hoveredStateId = newHoveredStateId; 121 | } 122 | map.setFeatureState({source: 'data', id: hoveredStateId}, { hover: true}); 123 | let popup_html = '
  • Location: ' + f.geometry.coordinates[0].toPrecision(6) + 124 | ', ' + f.geometry.coordinates[1].toPrecision(6) + '
  • '; 125 | 126 | for (key in f.properties) { 127 | popup_html += '
  • ' + key + ': ' + f.properties[key] + '
  • ' 128 | } 129 | 130 | popup_html += '
    ' 131 | popup.setLngLat(e.lngLat) 132 | .setHTML(popup_html) 133 | .addTo(map); 134 | } 135 | else { 136 | map.getCanvas().style.cursor = ''; 137 | popup.remove(); 138 | map.removeFeatureState({source: 'data', id: hoveredStateId}); 139 | } 140 | }); 141 | 142 | {% endblock circle_popup %} 143 | 144 | // Fly to on click 145 | map.on('click', 'circle', function(e) { 146 | map.easeTo({ 147 | center: e.features[0].geometry.coordinates 148 | }); 149 | }); 150 | }); 151 | 152 | {% endblock map %} 153 | -------------------------------------------------------------------------------- /mapboxgl/templates/clustered_circle.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | 4 | {% block extra_css %} 5 | 8 | {% endblock extra_css %} 9 | 10 | {% block legend %} 11 | 12 | {% if showLegend %} 13 | calcColorLegend({{ colorStops }}, "Point Density"); 14 | {% endif %} 15 | 16 | {% endblock legend %} 17 | 18 | {% block map %} 19 | 20 | map.on('style.load', function() { 21 | 22 | {% block clustered_circle %} 23 | 24 | map.addSource("data", { 25 | "type": "geojson", 26 | "data": {{ geojson_data }}, 27 | "buffer": 0, 28 | "maxzoom": {{ clusterMaxZoom }} + 1, 29 | "cluster": true, 30 | "clusterMaxZoom": {{ clusterMaxZoom }}, 31 | "clusterRadius": {{ clusterRadius }}, 32 | "generateId": true 33 | }); 34 | 35 | map.addLayer({ 36 | "id": "label", 37 | "source": "data", 38 | "type": "symbol", 39 | "maxzoom": {{ maxzoom }}, 40 | "minzoom": {{ minzoom }}, 41 | "layout": { 42 | "text-field": "{point_count_abbreviated}", 43 | "text-size" : generateInterpolateExpression('zoom', [[0, {{ labelSize }}],[22, 3* {{ labelSize }}]] ), 44 | }, 45 | "paint": { 46 | "text-halo-color": "{{ labelHaloColor }}", 47 | "text-halo-width": generatePropertyExpression('interpolate', 'zoom', [[0,{{ labelHaloWidth }}], [18,5* {{ labelHaloWidth }}]]), 48 | "text-color": ["case", 49 | ["boolean", ["feature-state", "hover"], false], 50 | "{{ highlightColor }}", 51 | "{{ labelColor }}"] 52 | } 53 | }, "{{belowLayer}}" ) 54 | 55 | map.addLayer({ 56 | "id": "circle-cluster", 57 | "source": "data", 58 | "type": "circle", 59 | "maxzoom": {{ maxzoom }}, 60 | "minzoom": {{ minzoom }}, 61 | "filter": ["has", "point_count"], 62 | "paint": { 63 | "circle-color": ["case", 64 | ["boolean", ["feature-state", "hover"], false], 65 | "{{ highlightColor }}", 66 | generateInterpolateExpression( "point_count", {{ colorStops }} )], 67 | "circle-radius" : generateInterpolateExpression( "point_count", {{ radiusStops }} ), 68 | "circle-stroke-color": ["case", 69 | ["boolean", ["feature-state", "hover"], false], 70 | "{{ highlightColor }}", "{{ strokeColor }}"], 71 | "circle-stroke-width": generatePropertyExpression('interpolate', 'zoom', [[0,{{ strokeWidth }}], [18,5* {{ strokeWidth }}]]), 72 | "circle-opacity" : {{ opacity }}, 73 | "circle-stroke-opacity" : {{ opacity }} 74 | } 75 | }, "label"); 76 | 77 | map.addLayer({ 78 | "id": "circle-unclustered", 79 | "source": "data", 80 | "type": "circle", 81 | "maxzoom": {{ maxzoom }}, 82 | "minzoom": {{ minzoom }}, 83 | "filter": ["!has", "point_count"], 84 | "paint": { 85 | "circle-color": ["case", 86 | ["boolean", ["feature-state", "hover"], false], 87 | "{{ highlightColor }}", 88 | "{{ colorDefault }}"], 89 | "circle-radius" : generateInterpolateExpression( 'zoom', [[0, {{ radiusDefault }} ], [22,10 * {{ radiusDefault }}]]), 90 | "circle-stroke-color": ["case", 91 | ["boolean", ["feature-state", "hover"], false], 92 | "{{ highlightColor }}", 93 | "{{ strokeColor }}"], 94 | "circle-stroke-width": generatePropertyExpression('interpolate', 'zoom', [[0,{{ strokeWidth }}], [18,5* {{ strokeWidth }}]]), 95 | "circle-opacity" : {{ opacity }}, 96 | "circle-stroke-opacity" : {{ opacity }} 97 | } 98 | }, "circle-cluster"); 99 | 100 | {% endblock clustered_circle %} 101 | 102 | // Popups 103 | {% if popupOpensOnHover %} 104 | var popupAction = 'mousemove', 105 | popupSettings = { 106 | closeButton: false, 107 | closeOnClick: false 108 | }; 109 | {% else %} 110 | var popupAction = 'click', 111 | popupSettings = { 112 | closeButton: true, 113 | closeOnClick: true 114 | }; 115 | {% endif %} 116 | 117 | // Create a popup 118 | var popup = new mapboxgl.Popup(popupSettings); 119 | 120 | {% block clustered_circle_popup %} 121 | 122 | var hoveredStateId = 0; 123 | 124 | // Show the popup on mouseover 125 | map.on(popupAction, function(e) { 126 | 127 | var features = map.queryRenderedFeatures(e.point, {layers: ['circle-unclustered', 'circle-cluster', 'label'] }); 128 | 129 | if (features.length > 0) { 130 | map.getCanvas().style.cursor = 'pointer'; 131 | var f = features[0]; 132 | newHoveredStateId = f.id; 133 | if (newHoveredStateId != hoveredStateId) { 134 | map.removeFeatureState({source: 'data', id: hoveredStateId}); 135 | hoveredStateId = newHoveredStateId; 136 | } 137 | map.setFeatureState({source: 'data', id: hoveredStateId}, { hover: true}); 138 | let popup_html = '
  • Location: ' + f.geometry.coordinates[0].toPrecision(6) + 139 | ', ' + f.geometry.coordinates[1].toPrecision(6) + '
  • '; 140 | 141 | for (key in f.properties) { 142 | popup_html += '
  • ' + key + ': ' + f.properties[key] + '
  • ' 143 | } 144 | 145 | popup_html += '
    ' 146 | popup.setLngLat(e.lngLat) 147 | .setHTML(popup_html) 148 | .addTo(map); 149 | } 150 | else { 151 | map.getCanvas().style.cursor = ''; 152 | popup.remove(); 153 | map.removeFeatureState({source: 'data', id: hoveredStateId}); 154 | } 155 | }); 156 | 157 | {% endblock clustered_circle_popup %} 158 | 159 | map.on('click', 'circle-unclustered', function(e) { 160 | map.easeTo({ 161 | center: e.features[0].geometry.coordinates 162 | }); 163 | }); 164 | 165 | map.on('click', 'circle-cluster', function(e) { 166 | map.easeTo({ 167 | center: e.features[0].geometry.coordinates 168 | }); 169 | }); 170 | }); 171 | 172 | {% endblock map %} 173 | -------------------------------------------------------------------------------- /mapboxgl/templates/export_canvas.html: -------------------------------------------------------------------------------- 1 | 10 | 11 | -------------------------------------------------------------------------------- /mapboxgl/templates/graduated_circle.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | 4 | {% block extra_css %} 5 | 8 | {% endblock extra_css %} 9 | 10 | {% block legend %} 11 | 12 | {% if showLegend %} 13 | 14 | {% if colorStops and colorProperty and radiusProperty %} 15 | 16 | calcColorLegend({{ colorStops }}, "{{ colorProperty }}"); 17 | 18 | {% endif %} 19 | 20 | {% if radiusStops and radiusProperty %} 21 | 22 | calcRadiusLegend({{ radiusStops }}, "{{ radiusProperty }}", "{{ defaultColor }}"); 23 | 24 | {% endif %} 25 | 26 | {% endif %} 27 | 28 | {% endblock legend %} 29 | 30 | {% block map %} 31 | 32 | map.on('style.load', function() { 33 | 34 | {% block graduated_circle %} 35 | 36 | map.addSource("data", { 37 | "type": "geojson", 38 | "data": {{ geojson_data }}, //data from dataframe output to geojson 39 | "buffer": 0, 40 | "maxzoom": 14, 41 | "generateId": true 42 | }); 43 | 44 | map.addLayer({ 45 | "id": "label", 46 | "source": "data", 47 | "type": "symbol", 48 | "maxzoom": {{ maxzoom }}, 49 | "minzoom": {{ minzoom }}, 50 | "layout": { 51 | {% if labelProperty %} 52 | "text-field": "{{ labelProperty }}", 53 | {% endif %} 54 | "text-size" : generateInterpolateExpression('zoom', [[0, {{ labelSize }}],[22, 3* {{ labelSize }}]] ), 55 | "text-offset": [0,-1] 56 | }, 57 | "paint": { 58 | "text-halo-color": "{{ labelHaloColor }}", 59 | "text-halo-width": generatePropertyExpression('interpolate', 'zoom', [[0,{{ labelHaloWidth }}], [18,5* {{ labelHaloWidth }}]]), 60 | "text-color": ["case", 61 | ["boolean", ["feature-state", "hover"], false], 62 | "{{ highlightColor }}", 63 | "{{ labelColor }}"] 64 | } 65 | }, "{{belowLayer}}" ) 66 | 67 | map.addLayer({ 68 | "id": "circle", 69 | "source": "data", 70 | "type": "circle", 71 | "maxzoom": {{ maxzoom }}, 72 | "minzoom": {{ minzoom }}, 73 | "paint": { 74 | {% if colorProperty %} 75 | "circle-color": ["case", 76 | ["boolean", ["feature-state", "hover"], false], 77 | "{{ highlightColor }}", 78 | generatePropertyExpression("{{ colorType }}", "{{ colorProperty }}", {{ colorStops }}, "{{ defaultColor }}" )], 79 | {% else %} 80 | "circle-color": ["case", 81 | ["boolean", ["feature-state", "hover"], false], 82 | "{{ highlightColor }}", 83 | "{{ defaultColor }}"], 84 | {% endif %} 85 | {% if radiusProperty %} 86 | "circle-radius" : generatePropertyExpression("{{ radiusType }}", "{{ radiusProperty }}", {{ radiusStops }}, {{ defaultRadius }} ), 87 | {% else %} 88 | "circle-radius": {{ defaultRadius }}, 89 | {% endif %} 90 | "circle-stroke-color": ["case", 91 | ["boolean", ["feature-state", "hover"], false], 92 | "{{ highlightColor }}", 93 | "{{ strokeColor }}"], 94 | "circle-stroke-width": generatePropertyExpression('interpolate', 'zoom', [[0,{{ strokeWidth }}], [18,5* {{ strokeWidth }}]]), 95 | "circle-opacity" : {{ opacity }}, 96 | "circle-stroke-opacity" : {{ opacity }} 97 | } 98 | }, "label"); 99 | 100 | {% endblock graduated_circle %} 101 | 102 | // Popups 103 | {% if popupOpensOnHover %} 104 | var popupAction = 'mousemove', 105 | popupSettings = { 106 | closeButton: false, 107 | closeOnClick: false 108 | }; 109 | {% else %} 110 | var popupAction = 'click', 111 | popupSettings = { 112 | closeButton: true, 113 | closeOnClick: true 114 | }; 115 | {% endif %} 116 | 117 | // Create a popup 118 | var popup = new mapboxgl.Popup(popupSettings); 119 | 120 | {% block graduated_circle_popup %} 121 | 122 | var hoveredStateId = 0; 123 | 124 | // Show the popup on mouseover 125 | map.on(popupAction, function(e) { 126 | 127 | var features = map.queryRenderedFeatures(e.point, {layers: ['circle', 'label'] }); 128 | 129 | if (features.length > 0) { 130 | map.getCanvas().style.cursor = 'pointer'; 131 | var f = features[0]; 132 | newHoveredStateId = f.id; 133 | if (newHoveredStateId != hoveredStateId) { 134 | map.removeFeatureState({source: 'data', id: hoveredStateId}); 135 | hoveredStateId = newHoveredStateId; 136 | } 137 | map.setFeatureState({source: 'data', id: hoveredStateId}, { hover: true}); 138 | let popup_html = '
  • Location: ' + f.geometry.coordinates[0].toPrecision(6) + 139 | ', ' + f.geometry.coordinates[1].toPrecision(6) + '
  • '; 140 | 141 | for (key in f.properties) { 142 | popup_html += '
  • ' + key + ': ' + f.properties[key] + '
  • ' 143 | } 144 | 145 | popup_html += '
    ' 146 | popup.setLngLat(e.lngLat) 147 | .setHTML(popup_html) 148 | .addTo(map); 149 | } 150 | else { 151 | map.getCanvas().style.cursor = ''; 152 | popup.remove(); 153 | map.removeFeatureState({source: 'data', id: hoveredStateId}); 154 | } 155 | }); 156 | 157 | {% endblock graduated_circle_popup %} 158 | 159 | // Fly to on click 160 | map.on('click', 'circle', function(e) { 161 | map.easeTo({ 162 | center: e.features[0].geometry.coordinates 163 | }); 164 | }); 165 | }); 166 | 167 | {% endblock map %} 168 | -------------------------------------------------------------------------------- /mapboxgl/templates/heatmap.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | {% block legend %}{% endblock legend %} 4 | 5 | {% block map %} 6 | 7 | map.on('style.load', function() { 8 | 9 | {% block heatmap %} 10 | 11 | map.addSource("data", { 12 | "type": "geojson", 13 | "data": {{ geojson_data }}, //data from dataframe output to geojson 14 | "buffer": 0, 15 | "maxzoom": 14 16 | }); 17 | 18 | map.addLayer({ 19 | "id": "heatmap", 20 | "source": "data", 21 | "type": "heatmap", 22 | "maxzoom": {{ maxzoom }}, 23 | "minzoom": {{ minzoom }}, 24 | "paint": { 25 | {% if radiusStops %} 26 | "heatmap-radius": generatePropertyExpression('interpolate', 'zoom', {{ radiusStops }}), 27 | {% endif %} 28 | {% if weightProperty and weightStops %} 29 | "heatmap-weight": generateInterpolateExpression( "{{ weightProperty }}", {{ weightStops }} ), 30 | {% endif %} 31 | {% if intensityStops %} 32 | "heatmap-intensity": generateInterpolateExpression('zoom', {{ intensityStops }} ), 33 | {% endif %} 34 | {% if colorStops %} 35 | "heatmap-color" : generateInterpolateExpression('heatmap-density', {{ colorStops }} ), 36 | {% endif %} 37 | "heatmap-opacity" : {{ opacity }} 38 | } 39 | }, "{{belowLayer}}" ); 40 | 41 | {% endblock heatmap %} 42 | 43 | }); 44 | 45 | {% endblock map %} 46 | -------------------------------------------------------------------------------- /mapboxgl/templates/image.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | {% block legend %}{% endblock legend %} 4 | 5 | {% block map %} 6 | 7 | map.on('style.load', function() { 8 | 9 | map.addSource("image", { 10 | "type": "image", 11 | "url": "{{ image }}", // url or base64 encoded image 12 | "coordinates": {{ coordinates }} 13 | }); 14 | 15 | map.addLayer({ 16 | "id": 'image', 17 | "type": "raster", 18 | "source": "image" 19 | }, "{{belowLayer}}"); 20 | 21 | }); 22 | 23 | {% endblock map %} 24 | -------------------------------------------------------------------------------- /mapboxgl/templates/linestring.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | {% block legend %} 4 | 5 | {% if showLegend %} 6 | {% if colorStops and colorProperty and widthProperty %} 7 | {% if colorProperty != widthProperty %} 8 | calcColorLegend({{ colorStops }}, "{{ colorProperty }} vs. {{ widthProperty }}"); 9 | {% else %} 10 | calcColorLegend({{ colorStops }}, "{{ colorProperty }}"); 11 | {% endif %} 12 | {% elif colorStops and colorProperty %} 13 | calcColorLegend({{ colorStops }}, "{{ colorProperty }}"); 14 | {% endif %} 15 | {% endif %} 16 | 17 | {% endblock legend %} 18 | 19 | {% block map %} 20 | 21 | map.on('style.load', function() { 22 | 23 | {% block linestring %} 24 | 25 | // Add geojson data source 26 | map.addSource("data", { 27 | "type": "geojson", 28 | "data": {{ geojson_data }}, 29 | "buffer": 1, 30 | "maxzoom": 14, 31 | "generateId": true 32 | }); 33 | 34 | // Add data layer 35 | map.addLayer({ 36 | "id": "linestring", 37 | "source": "data", 38 | "type": "line", 39 | "layout": { 40 | "line-join": "round", 41 | "line-cap": "round" 42 | }, 43 | "paint": { 44 | {% if lineDashArray %} 45 | "line-dasharray": {{ lineDashArray }}, 46 | {% endif %} 47 | {% if colorProperty %} 48 | "line-color": ["case", 49 | ["boolean", ["feature-state", "hover"], false], 50 | "{{ highlightColor }}", 51 | generatePropertyExpression("{{ colorType }}", "{{ colorProperty }}", {{ colorStops }}, "{{ defaultColor }}")], 52 | {% else %} 53 | "line-color": ["case", 54 | ["boolean", ["feature-state", "hover"], false], 55 | "{{ highlightColor }}","{{ defaultColor }}"], 56 | {% endif %} 57 | {% if widthProperty %} 58 | "line-width": generatePropertyExpression("{{ widthType }}", "{{ widthProperty }}", {{ widthStops }}, {{ defaultWidth }}), 59 | {% else %} 60 | "line-width": {{ defaultWidth }}, 61 | {% endif %} 62 | "line-opacity": {{ opacity }} 63 | } 64 | }, "{{ belowLayer }}" ); 65 | 66 | // Add label layer 67 | map.addLayer({ 68 | "id": "linestring-label", 69 | "source": "data", 70 | "type": "symbol", 71 | "layout": { 72 | {% if labelProperty %} 73 | "text-field": "{{ labelProperty }}", 74 | {% endif %} 75 | "text-size" : generateInterpolateExpression('zoom', [[0, {{ labelSize }}],[22, 3* {{ labelSize }}]] ), 76 | "text-offset": [0,-1] 77 | }, 78 | "paint": { 79 | "text-halo-color": "{{ labelHaloColor }}", 80 | "text-halo-width": generatePropertyExpression('interpolate', 'zoom', [[0,{{ labelHaloWidth }}], [18,5* {{ labelHaloWidth }}]]), 81 | "text-color": ["case", 82 | ["boolean", ["feature-state", "hover"], false], 83 | "{{ highlightColor }}", 84 | "{{ labelColor }}"] 85 | } 86 | }, "{{belowLayer}}" ); 87 | 88 | {% endblock linestring %} 89 | 90 | // Popups 91 | {% if popupOpensOnHover %} 92 | var popupAction = 'mousemove', 93 | popupSettings = { 94 | closeButton: false, 95 | closeOnClick: false 96 | }; 97 | {% else %} 98 | var popupAction = 'click', 99 | popupSettings = { 100 | closeButton: true, 101 | closeOnClick: true 102 | }; 103 | {% endif %} 104 | 105 | // Create a popup 106 | var popup = new mapboxgl.Popup(popupSettings); 107 | 108 | {% block linestring_popup %} 109 | 110 | var hoveredStateId = 0; 111 | 112 | map.on(popupAction, function(e) { 113 | var features = map.queryRenderedFeatures(e.point, {layers: ['linestring', 'linestring-label'] }); 114 | 115 | if (features.length > 0) { 116 | map.getCanvas().style.cursor = 'pointer'; 117 | var f = features[0]; 118 | newHoveredStateId = f.id; 119 | if (newHoveredStateId != hoveredStateId) { 120 | map.removeFeatureState({source: 'data', id: hoveredStateId}); 121 | hoveredStateId = newHoveredStateId; 122 | } 123 | map.setFeatureState({source: 'data', id: hoveredStateId}, { hover: true}); 124 | let popup_html = '
    '; 125 | 126 | for (key in f.properties) { 127 | popup_html += '
  • ' + key + ': ' + f.properties[key] + '
  • ' 128 | } 129 | 130 | popup_html += '
    ' 131 | popup.setLngLat(e.lngLat) 132 | .setHTML(popup_html) 133 | .addTo(map); 134 | } 135 | else { 136 | map.getCanvas().style.cursor = ''; 137 | popup.remove(); 138 | map.removeFeatureState({source: 'data', id: hoveredStateId}); 139 | } 140 | }); 141 | 142 | {% endblock linestring_popup %} 143 | 144 | // Fly to on click 145 | map.on('click', 'linestring', function(e) { 146 | map.flyTo({ 147 | center: e.lngLat 148 | }); 149 | }); 150 | 151 | }); 152 | 153 | {% endblock map %} 154 | -------------------------------------------------------------------------------- /mapboxgl/templates/main.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | mapboxgl-jupyter viz 5 | 6 | 8 | 10 | 13 | 14 | 84 | 85 | {% block extra_css %}{% endblock extra_css %} 86 | 87 | 88 | 89 | 90 |
    91 | 92 | 373 | 374 | 375 | 376 | 377 | 378 | {% if includeSnapshotLinks %} 379 | 380 | {% include 'export_canvas.html' %} 381 | {% endif %} 382 | 383 | 384 | 385 | -------------------------------------------------------------------------------- /mapboxgl/templates/map.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | {% block legend %}{% endblock legend %} 4 | 5 | {% block map %} 6 | 7 | map.on('style.load', function() { 8 | 9 | // Add data source 10 | map.addSource("data", { 11 | "type": "geojson", 12 | "data": {{ geojson_data }}, 13 | "buffer": 1, 14 | "maxzoom": 14, 15 | generateId: true 16 | }); 17 | 18 | // Add data layer 19 | map.addLayer({ 20 | "id": "circle", 21 | "source": "data", 22 | "type": "circle", 23 | "maxzoom": {{ maxzoom }}, 24 | "minzoom": {{ minzoom }}, 25 | "paint": { "circle-radius": 1 } 26 | }); 27 | 28 | }); 29 | 30 | {% endblock map %} 31 | -------------------------------------------------------------------------------- /mapboxgl/templates/raster.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | {% block legend %}{% endblock legend %} 4 | 5 | {% block map %} 6 | 7 | map.on('style.load', function() { 8 | console.log('Yoooooo'); 9 | let params = { 10 | "type": "raster", 11 | "tiles": [ 12 | "{{ tiles_url }}" 13 | ], 14 | "tileSize": {{ tiles_size }}, 15 | "minzoom": {{ tiles_minzoom }}, 16 | "maxzoom": {{ tiles_maxzoom }} 17 | } 18 | if ({{ tiles_bounds }} !== undefined) params.bounds = {{ tiles_bounds }}; 19 | 20 | map.addSource("raster-tiles", params); 21 | 22 | map.addLayer({ 23 | "id": "raster-tiles", 24 | "type": "raster", 25 | "source": "raster-tiles" 26 | }, "{{belowLayer}}" ); 27 | 28 | }); 29 | 30 | {% endblock map %} 31 | -------------------------------------------------------------------------------- /mapboxgl/templates/symbol.html: -------------------------------------------------------------------------------- 1 | //https://www.mapbox.com/help/custom-markers-gl-js/ 2 | 3 | {% extends "base.html" %} 4 | 5 | {% block legend %}{% endblock legend %} 6 | 7 | {% block map %} 8 | 9 | map.on('style.load', function() { 10 | 11 | // Add data source 12 | map.addSource("data", { 13 | "type": "geojson", 14 | "data": {{ geojson_data }}, 15 | "buffer": 1, 16 | "maxzoom": 14 17 | }); 18 | 19 | // Add symbol layer 20 | map.addLayer({ 21 | "id": "symbol", 22 | "source": "data", 23 | "type": "symbol", 24 | "maxzoom": {{ maxzoom }}, 25 | "minzoom": {{ minzoom }}, 26 | 27 | "layout": { 28 | {% if labelProperty %} 29 | "text-field": "{{ labelProperty }}", 30 | {% endif %} 31 | "text-size" : generateInterpolateExpression('zoom', [[0, {{ labelSize }}],[22, 3* {{ labelSize }}]] ), 32 | "text-offset": [0,-1] 33 | }, 34 | "paint": { 35 | "text-halo-color": "{{ labelHaloColor }}", 36 | "text-halo-width": generatePropertyExpression('interpolate', 'zoom', [[0,{{ labelHaloWidth }}], [18,5* {{ labelHaloWidth }}]]), 37 | "text-color": "{{ labelColor }}" 38 | } 39 | }, "{{ belowLayer }}" ); 40 | 41 | // Add label layer 42 | map.addLayer({ 43 | "id": "symbol-label", 44 | "source": "data", 45 | "type": "symbol", 46 | "maxzoom": {{ maxzoom }}, 47 | "minzoom": {{ minzoom }}, 48 | "layout": { 49 | {% if labelProperty %} 50 | "text-field": "{{ labelProperty }}", 51 | {% endif %} 52 | "text-size" : generateInterpolateExpression('zoom', [[0, {{ labelSize }}],[22, 3* {{ labelSize }}]] ), 53 | "text-offset": [0,-1] 54 | }, 55 | "paint": { 56 | "text-halo-color": "{{ labelHaloColor }}", 57 | "text-halo-width": generatePropertyExpression('interpolate', 'zoom', [[0,{{ labelHaloWidth }}], [18,5* {{ labelHaloWidth }}]]), 58 | "text-color": "{{ labelColor }}" 59 | } 60 | }, "{{ belowLayer }}" ); 61 | 62 | }); 63 | 64 | {% endblock map %} 65 | -------------------------------------------------------------------------------- /mapboxgl/templates/vector_choropleth.html: -------------------------------------------------------------------------------- 1 | {% extends "choropleth.html" %} 2 | 3 | {% block choropleth %} 4 | 5 | {% if enableDataJoin %} 6 | 7 | // extract JSON property used for data-driven styling to add to popup 8 | {% if joinData %} 9 | 10 | let joinData = {{ joinData }}; 11 | var popUpKeys = {}, 12 | heightPopUpKeys = {}; 13 | 14 | // Create filter for layers from join data 15 | let layerFilter = ['in', "{{ vectorJoinDataProperty }}"] 16 | 17 | joinData.forEach(function(row, index) { 18 | popUpKeys[row["{{ dataJoinProperty }}"]] = row["{{ colorProperty }}"]; 19 | 20 | {% if extrudeChoropleth %} 21 | {% if colorProperty != heightProperty %} 22 | heightPopUpKeys[row["{{ dataJoinProperty }}"]] = row["{{ heightProperty }}"]; 23 | {% endif %} 24 | {% endif %} 25 | 26 | layerFilter.push(row["{{ dataJoinProperty }}"]); 27 | }); 28 | 29 | {% endif %} 30 | 31 | {% endif %} 32 | 33 | // Add vector data source 34 | map.addSource("vector-data", { 35 | type: "vector", 36 | url: "{{ vectorUrl }}", 37 | }); 38 | 39 | // Add layer from the vector tile source with data-driven style 40 | map.addLayer({ 41 | "id": "choropleth-fill", 42 | "type": "fill", 43 | "source": "vector-data", 44 | "source-layer": "{{ vectorLayer }}", 45 | "paint": { 46 | {% if enableDataJoin %} 47 | "fill-color": ["case", 48 | ["boolean", ["feature-state", "hover"], false], 49 | "{{ highlightColor }}", 50 | generatePropertyExpression('match', "{{ vectorJoinDataProperty }}", {{ vectorColorStops }}, "{{ defaultColor }}")], 51 | {% else %} 52 | "fill-color": ["case", 53 | ["boolean", ["feature-state", "hover"], false], 54 | "{{ highlightColor }}", 55 | generatePropertyExpression("{{ colorType }}", "{{ colorProperty }}", {{ colorStops }}, "{{ defaultColor }}")], 56 | {% endif %} 57 | "fill-opacity": {{ opacity }} 58 | } 59 | {% if enableDataJoin %} 60 | , "filter": layerFilter 61 | {% endif %} 62 | }, "{{ belowLayer }}"); 63 | 64 | // Add border layer 65 | map.addLayer({ 66 | "id": "choropleth-line", 67 | "source": "vector-data", 68 | "source-layer": "{{ vectorLayer }}", 69 | "type": "line", 70 | "layout": { 71 | "line-join": "round", 72 | "line-cap": "round" 73 | }, 74 | "paint": { 75 | {% if lineDashArray %} 76 | "line-dasharray": {{ lineDashArray }}, 77 | {% endif %} 78 | "line-color": ["case", 79 | ["boolean", ["feature-state", "hover"], false], 80 | "{{ highlightColor }}", 81 | "{{ lineColor }}"], 82 | "line-width": {{ lineWidth }}, 83 | "line-opacity": {{ lineOpacity }} 84 | } 85 | {% if enableDataJoin %} 86 | , "filter": layerFilter 87 | {% endif %} 88 | }, "{{ belowLayer }}"); 89 | 90 | // Add label layer 91 | map.addLayer({ 92 | "id": "choropleth-label", 93 | "source": "vector-data", 94 | "source-layer": "{{ vectorLayer }}", 95 | "type": "symbol", 96 | "layout": { 97 | {% if labelProperty %} 98 | "text-field": "{{ labelProperty }}", 99 | {% endif %} 100 | "text-size" : generateInterpolateExpression('zoom', [[0, {{ labelSize }}],[22, 3* {{ labelSize }}]] ), 101 | "text-offset": [0,-1] 102 | }, 103 | "paint": { 104 | "text-halo-color": "{{ labelHaloColor }}", 105 | "text-halo-width": generatePropertyExpression('interpolate', 'zoom', [[0,{{ labelHaloWidth }}], [18,5* {{ labelHaloWidth }}]]), 106 | "text-color": ["case", 107 | ["boolean", ["feature-state", "hover"], false], 108 | "{{ highlightColor }}", "{{ labelColor }}"] 109 | } 110 | {% if enableDataJoin %} 111 | , "filter": layerFilter 112 | {% endif %} 113 | }, "{{ belowLayer }}"); 114 | 115 | // Optional extrusion layer 116 | {% if extrudeChoropleth %} 117 | 118 | map.addLayer({ 119 | id: "choropleth-extrusion", 120 | type: "fill-extrusion", 121 | "source": "vector-data", 122 | "source-layer": "{{ vectorLayer }}", 123 | paint: { 124 | "fill-extrusion-opacity": {{ opacity }}, 125 | {% if enableDataJoin %} 126 | "fill-extrusion-color": ["case", 127 | ["boolean", ["feature-state", "hover"], false], 128 | "{{ highlightColor }}", 129 | generatePropertyExpression('match', "{{ vectorJoinDataProperty }}", {{ vectorColorStops }}, "{{ defaultColor }}")], 130 | "fill-extrusion-height": generatePropertyExpression('match', "{{ vectorJoinDataProperty }}", {{ vectorHeightStops }}, {{ defaultHeight }}) 131 | {% else %} 132 | "fill-extrusion-color": ["case", 133 | ["boolean", ["feature-state", "hover"], false], 134 | "{{ highlightColor }}", 135 | generatePropertyExpression("{{ colorType }}", "{{ colorProperty }}", {{ colorStops }}, "{{ defaultColor }}")], 136 | "fill-extrusion-height": generatePropertyExpression("{{ heightType }}", "{{ heightProperty }}", {{ heightStops }}, {{ defaultHeight }}), 137 | {% endif %} 138 | } 139 | {% if enableDataJoin %} 140 | , "filter": layerFilter 141 | {% endif %} 142 | }, "{{ belowLayer }}"); 143 | 144 | {% endif %} 145 | 146 | {% endblock choropleth %} 147 | 148 | {% block choropleth_popup %} 149 | 150 | // Show the popup on mouseover 151 | var interactionLayer = {% if extrudeChoropleth %} 'choropleth-extrusion' {% else %} 'choropleth-fill' {% endif %}; 152 | var hoveredStateId = 0; 153 | 154 | map.on(popupAction, function(e) { 155 | var features = map.queryRenderedFeatures(e.point, {layers: [interactionLayer, 'choropleth-label'] }); 156 | 157 | if (features.length > 0) { 158 | map.getCanvas().style.cursor = 'pointer'; 159 | var f = features[0]; 160 | newHoveredStateId = f.id; 161 | if (newHoveredStateId != hoveredStateId) { 162 | map.removeFeatureState({source: 'vector-data', sourceLayer: "{{ vectorLayer }}", id: hoveredStateId}); 163 | hoveredStateId = newHoveredStateId; 164 | } 165 | map.setFeatureState({source: 'vector-data', sourceLayer: "{{ vectorLayer }}", id: hoveredStateId}, { hover: true}); 166 | let popup_html = '
    '; 167 | 168 | for (key in f.properties) { 169 | popup_html += '
  • ' + key + ': ' + f.properties[key] + '
  • ' 170 | } 171 | 172 | popup_html += '
    ' 173 | popup.setLngLat(e.lngLat) 174 | .setHTML(popup_html) 175 | .addTo(map); 176 | } 177 | else { 178 | map.getCanvas().style.cursor = ''; 179 | popup.remove(); 180 | map.removeFeatureState({source: 'vector-data', sourceLayer: "{{ vectorLayer }}", id: hoveredStateId}); 181 | } 182 | }); 183 | 184 | {% endblock choropleth_popup %} 185 | -------------------------------------------------------------------------------- /mapboxgl/templates/vector_circle.html: -------------------------------------------------------------------------------- 1 | {% extends "circle.html" %} 2 | 3 | {% block circle %} 4 | 5 | // extract JSON property used for data-driven styling to add to popup 6 | {% if enableDataJoin %} 7 | 8 | {% if joinData %} 9 | 10 | let joinData = {{ joinData }}; 11 | var popUpKeys = {}; 12 | 13 | // Create filter for layers from join data 14 | let layerFilter = ['in', "{{ vectorJoinDataProperty }}"] 15 | 16 | joinData.forEach(function(row, index) { 17 | popUpKeys[row["{{ dataJoinProperty }}"]] = row["{{ colorProperty }}"]; 18 | layerFilter.push(row["{{ dataJoinProperty }}"]); 19 | }); 20 | 21 | {% endif %} 22 | 23 | {% endif %} 24 | 25 | // Add vector data source 26 | map.addSource("vector-data", { 27 | type: "vector", 28 | url: "{{ vectorUrl }}", 29 | }); 30 | 31 | // Add layer from the vector tile source with data-driven style 32 | map.addLayer({ 33 | "id": "circle", 34 | "type": "circle", 35 | "source": "vector-data", 36 | "source-layer": "{{ vectorLayer }}", 37 | "maxzoom": {{ maxzoom }}, 38 | "minzoom": {{ minzoom }}, 39 | "paint": { 40 | {% if enableDataJoin %} 41 | "circle-color": ["case", 42 | ["boolean", ["feature-state", "hover"], false], 43 | "{{ highlightColor }}", 44 | generatePropertyExpression('match', "{{ vectorJoinDataProperty }}", {{ vectorColorStops }}, "{{ defaultColor }}")], 45 | {% else %} 46 | {% if colorProperty %} 47 | "circle-color": ["case", 48 | ["boolean", ["feature-state", "hover"], false], 49 | "{{ highlightColor }}", 50 | generatePropertyExpression("{{ colorType }}", "{{ colorProperty }}", {{ colorStops }}, "{{ defaultColor }}")], 51 | {% else %} 52 | "circle-color": ["case", 53 | ["boolean", ["feature-state", "hover"], false], 54 | "{{ highlightColor }}", 55 | "{{ defaultColor }}"], 56 | {% endif %} 57 | {% endif %} 58 | "circle-radius" : generatePropertyExpression('interpolate', 'zoom', [[0,{{ radius }}], [22,10 * {{ radius }}]]), 59 | "circle-stroke-color": ["case", 60 | ["boolean", ["feature-state", "hover"], false], 61 | "{{ highlightColor }}", 62 | "{{ strokeColor }}"], 63 | "circle-stroke-width": generatePropertyExpression('interpolate', 'zoom', [[0,{{ strokeWidth }}], [18,5* {{ strokeWidth }}]]), 64 | "circle-opacity" : {{ opacity }}, 65 | "circle-stroke-opacity" : {{ opacity }} 66 | } 67 | {% if enableDataJoin %} 68 | , filter: layerFilter 69 | {% endif %} 70 | }, "{{ belowLayer }}" ); 71 | 72 | // Add label layer 73 | map.addLayer({ 74 | "id": "circle-label", 75 | "source": "vector-data", 76 | "source-layer": "{{ vectorLayer }}", 77 | "type": "symbol", 78 | "maxzoom": {{ maxzoom }}, 79 | "minzoom": {{ minzoom }}, 80 | "layout": { 81 | {% if labelProperty %} 82 | "text-field": "{{ labelProperty }}", 83 | {% endif %} 84 | "text-size" : generateInterpolateExpression('zoom', [[0, {{ labelSize }}],[22, 3* {{ labelSize }}]] ), 85 | "text-offset": [0,-1] 86 | }, 87 | "paint": { 88 | "text-halo-color": "{{ labelHaloColor }}", 89 | "text-halo-width": generatePropertyExpression('interpolate', 'zoom', [[0,{{ labelHaloWidth }}], [18,5* {{ labelHaloWidth }}]]), 90 | "text-color": ["case", 91 | ["boolean", ["feature-state", "hover"], false], 92 | "{{ highlightColor }}", 93 | "{{ labelColor }}"] 94 | } 95 | {% if enableDataJoin %} 96 | , filter: layerFilter 97 | {% endif %} 98 | }, "{{belowLayer}}" ); 99 | 100 | {% endblock circle %} 101 | 102 | {% block circle_popup %} 103 | 104 | var hoveredStateId = 0; 105 | 106 | map.on(popupAction, function(e) { 107 | var features = map.queryRenderedFeatures(e.point, {layers: ['circle', 'circle-label'] }); 108 | 109 | if (features.length > 0) { 110 | map.getCanvas().style.cursor = 'pointer'; 111 | var f = features[0]; 112 | newHoveredStateId = f.id; 113 | if (newHoveredStateId != hoveredStateId) { 114 | map.removeFeatureState({source: 'vector-data', sourceLayer: "{{ vectorLayer }}", id: hoveredStateId}); 115 | hoveredStateId = newHoveredStateId; 116 | } 117 | map.setFeatureState({source: 'vector-data', sourceLayer: "{{ vectorLayer }}", id: hoveredStateId}, { hover: true}); 118 | let popup_html = '
    '; 119 | 120 | for (key in f.properties) { 121 | popup_html += '
  • ' + key + ': ' + f.properties[key] + '
  • ' 122 | } 123 | 124 | popup_html += '
    ' 125 | popup.setLngLat(e.lngLat) 126 | .setHTML(popup_html) 127 | .addTo(map); 128 | } 129 | else { 130 | map.getCanvas().style.cursor = ''; 131 | popup.remove(); 132 | map.removeFeatureState({source: 'vector-data', sourceLayer: "{{ vectorLayer }}", id: hoveredStateId}); 133 | } 134 | }); 135 | 136 | {% endblock circle_popup %} 137 | -------------------------------------------------------------------------------- /mapboxgl/templates/vector_graduated_circle.html: -------------------------------------------------------------------------------- 1 | {% extends "graduated_circle.html" %} 2 | 3 | {% block graduated_circle %} 4 | 5 | // extract JSON property used for data-driven styling to add to popup 6 | {% if enableDataJoin %} 7 | 8 | {% if joinData %} 9 | 10 | let joinData = {{ joinData }}; 11 | var popUpKeys = {}, 12 | radiusPopUpKeys = {}; 13 | 14 | // Create filter for layers from join data 15 | let layerFilter = ['in', "{{ vectorJoinDataProperty }}"] 16 | 17 | joinData.forEach(function(row, index) { 18 | 19 | {% if colorProperty %} 20 | popUpKeys[row["{{ dataJoinProperty }}"]] = row["{{ colorProperty }}"]; 21 | {% endif %} 22 | 23 | {% if radiusProperty %} 24 | {% if colorProperty != radiusProperty %} 25 | radiusPopUpKeys[row["{{ dataJoinProperty }}"]] = row["{{ radiusProperty }}"]; 26 | {% endif %} 27 | {% endif %} 28 | 29 | layerFilter.push(row["{{ dataJoinProperty }}"]); 30 | }); 31 | 32 | {% endif %} 33 | 34 | {% endif %} 35 | 36 | // Add vector data source 37 | map.addSource("vector-data", { 38 | type: "vector", 39 | url: "{{ vectorUrl }}", 40 | }); 41 | 42 | // Add label layer 43 | map.addLayer({ 44 | "id": "circle-label", 45 | "source": "vector-data", 46 | "source-layer": "{{ vectorLayer }}", 47 | "type": "symbol", 48 | "maxzoom": {{ maxzoom }}, 49 | "minzoom": {{ minzoom }}, 50 | "layout": { 51 | {% if labelProperty %} 52 | "text-field": "{{ labelProperty }}", 53 | {% endif %} 54 | "text-size" : generateInterpolateExpression('zoom', [[0, {{ labelSize }}],[22, 3* {{ labelSize }}]] ), 55 | "text-offset": [0,-1] 56 | }, 57 | "paint": { 58 | "text-halo-color": "{{ labelHaloColor }}", 59 | "text-halo-width": generatePropertyExpression('interpolate', 'zoom', [[0,{{ labelHaloWidth }}], [18,5* {{ labelHaloWidth }}]]), 60 | "text-color": ["case", 61 | ["boolean", ["feature-state", "hover"], false], 62 | "{{ highlightColor }}", 63 | "{{ labelColor }}"] 64 | } 65 | {% if enableDataJoin %} 66 | , filter: layerFilter 67 | {% endif %} 68 | }, "{{ belowLayer }}"); 69 | 70 | // Add layer from the vector tile source with data-driven style 71 | map.addLayer({ 72 | "id": "circle", 73 | "source": "vector-data", 74 | "source-layer": "{{ vectorLayer }}", 75 | "type": "circle", 76 | "maxzoom": {{ maxzoom }}, 77 | "minzoom": {{ minzoom }}, 78 | "paint": { 79 | {% if enableDataJoin %} 80 | "circle-color": ["case", 81 | ["boolean", ["feature-state", "hover"], false], 82 | "{{ highlightColor }}", 83 | generatePropertyExpression('match', "{{ vectorJoinDataProperty }}", {{ vectorColorStops }}, "{{ defaultColor }}")], 84 | "circle-radius": generatePropertyExpression('match', "{{ vectorJoinDataProperty }}", {{ vectorRadiusStops }}, "{{ defaultRadius }}"), 85 | {% else %} 86 | {% if colorProperty %} 87 | "circle-color": ["case", 88 | ["boolean", ["feature-state", "hover"], false], 89 | "{{ highlightColor }}", 90 | generatePropertyExpression("{{ colorType }}", "{{ colorProperty }}", {{ colorStops }}, "{{ defaultColor }}" )], 91 | {% else %} 92 | "circle-color": ["case", 93 | ["boolean", ["feature-state", "hover"], false], 94 | "{{ highlightColor }}", 95 | "{{ defaultColor }}"], 96 | {% endif %} 97 | {% if radiusProperty %} 98 | "circle-radius" : generatePropertyExpression("{{ radiusType }}", "{{ radiusProperty }}", {{ radiusStops }}, {{ defaultRadius }} ), 99 | {% else %} 100 | "circle-radius": {{ defaultRadius }}, 101 | {% endif %} 102 | {% endif %} 103 | "circle-stroke-color": ["case", 104 | ["boolean", ["feature-state", "hover"], false], 105 | "{{ highlightColor }}", 106 | "{{ strokeColor }}"], 107 | "circle-stroke-width": generatePropertyExpression('interpolate', 'zoom', [[0,{{ strokeWidth }}], [18,5* {{ strokeWidth }}]]), 108 | "circle-opacity" : {{ opacity }}, 109 | "circle-stroke-opacity" : {{ opacity }} 110 | } 111 | {% if enableDataJoin %} 112 | , filter: layerFilter 113 | {% endif %} 114 | }, "circle-label"); 115 | 116 | {% endblock graduated_circle %} 117 | 118 | {% block graduated_circle_popup %} 119 | 120 | var hoveredStateId = 0; 121 | 122 | map.on(popupAction, function(e) { 123 | var features = map.queryRenderedFeatures(e.point, {layers: ['circle', 'circle-label'] }); 124 | 125 | if (features.length > 0) { 126 | map.getCanvas().style.cursor = 'pointer'; 127 | var f = features[0]; 128 | newHoveredStateId = f.id; 129 | if (newHoveredStateId != hoveredStateId) { 130 | map.removeFeatureState({source: 'vector-data', sourceLayer: "{{ vectorLayer }}", id: hoveredStateId}); 131 | hoveredStateId = newHoveredStateId; 132 | } 133 | map.setFeatureState({source: 'vector-data', sourceLayer: "{{ vectorLayer }}", id: hoveredStateId}, { hover: true}); 134 | let popup_html = '
  • Location: ' + f.geometry.coordinates[0].toPrecision(6) + 135 | ', ' + f.geometry.coordinates[1].toPrecision(6) + '
  • '; 136 | 137 | for (key in f.properties) { 138 | popup_html += '
  • ' + key + ': ' + f.properties[key] + '
  • ' 139 | } 140 | 141 | popup_html += '
    ' 142 | popup.setLngLat(e.lngLat) 143 | .setHTML(popup_html) 144 | .addTo(map); 145 | } 146 | else { 147 | map.getCanvas().style.cursor = ''; 148 | popup.remove(); 149 | map.removeFeatureState({source: 'vector-data', sourceLayer: "{{ vectorLayer }}", id: hoveredStateId}); 150 | } 151 | }); 152 | 153 | {% endblock graduated_circle_popup %} 154 | -------------------------------------------------------------------------------- /mapboxgl/templates/vector_heatmap.html: -------------------------------------------------------------------------------- 1 | {% extends "heatmap.html" %} 2 | 3 | {% block heatmap %} 4 | 5 | // extract JSON property used for data-driven styling to add to popup 6 | {% if enableDataJoin %} 7 | 8 | {% if joinData %} 9 | 10 | // Create filter for layers from join data 11 | let joinData = {{ joinData }}, 12 | layerFilter = ['in', "{{ vectorJoinDataProperty }}"] 13 | joinData.forEach(function(row, index) { 14 | layerFilter.push(row["{{ dataJoinProperty }}"]); 15 | }); 16 | 17 | {% endif %} 18 | 19 | {% endif %} 20 | 21 | // Add vector data source 22 | map.addSource("vector-data", { 23 | type: "vector", 24 | url: "{{ vectorUrl }}", 25 | }); 26 | 27 | // Add layer from the vector tile source with data-driven style 28 | map.addLayer({ 29 | "id": "heatmap", 30 | "source": "vector-data", 31 | "source-layer": "{{ vectorLayer }}", 32 | "type": "heatmap", 33 | "maxzoom": {{ maxzoom }}, 34 | "minzoom": {{ minzoom }}, 35 | "paint": { 36 | {% if radiusStops %} 37 | "heatmap-radius": generatePropertyExpression('interpolate', 'zoom', {{ radiusStops }}), 38 | {% endif %} 39 | {% if enableDataJoin %} 40 | {% if weightProperty and vectorWeightStops %} 41 | "heatmap-weight": { 42 | "type": "categorical", 43 | "property": "{{ vectorJoinDataProperty }}", 44 | "stops": {{ vectorWeightStops }}, 45 | "default": 0 46 | }, 47 | {% endif %} 48 | {% else %} 49 | {% if weightProperty and weightStops %} 50 | "heatmap-weight": generateInterpolateExpression( "{{ weightProperty }}", {{ weightStops }} ), 51 | {% endif %} 52 | {% endif %} 53 | {% if intensityStops %} 54 | "heatmap-intensity": generateInterpolateExpression('zoom', {{ intensityStops }} ), 55 | {% endif %} 56 | {% if colorStops %} 57 | "heatmap-color" : generateInterpolateExpression('heatmap-density', {{ colorStops }} ), 58 | {% endif %} 59 | "heatmap-opacity" : {{ opacity }}, 60 | } 61 | {% if enableDataJoin %} 62 | , filter: layerFilter 63 | {% endif %} 64 | }, "{{belowLayer}}"); 65 | 66 | {% endblock heatmap %} 67 | -------------------------------------------------------------------------------- /mapboxgl/templates/vector_linestring.html: -------------------------------------------------------------------------------- 1 | {% extends "linestring.html" %} 2 | 3 | {% block linestring %} 4 | 5 | {% if enableDataJoin %} 6 | 7 | // extract JSON property used for data-driven styling to add to popup 8 | {% if joinData %} 9 | 10 | let joinData = {{ joinData }}; 11 | var popUpKeys = {}, 12 | lineWidthPopUpKeys = {}; 13 | 14 | // Create filter for layers from join data 15 | let layerFilter = ['in', "{{ vectorJoinDataProperty }}"] 16 | 17 | joinData.forEach(function(row, index) { 18 | 19 | {% if colorProperty %} 20 | popUpKeys[row["{{ dataJoinProperty }}"]] = row["{{ colorProperty }}"]; 21 | {% endif %} 22 | 23 | {% if widthProperty %} 24 | {% if colorProperty != widthProperty %} 25 | lineWidthPopUpKeys[row["{{ dataJoinProperty }}"]] = row["{{ widthProperty }}"]; 26 | {% endif %} 27 | {% endif %} 28 | 29 | layerFilter.push(row["{{ dataJoinProperty }}"]); 30 | }); 31 | 32 | {% endif %} 33 | 34 | {% endif %} 35 | 36 | // Add vector data source 37 | map.addSource("vector-data", { 38 | type: "vector", 39 | url: "{{ vectorUrl }}", 40 | }); 41 | 42 | // Add data layer from the vector tile source with data-driven style 43 | map.addLayer({ 44 | "id": "linestring", 45 | "source": "vector-data", 46 | "source-layer": "{{ vectorLayer }}", 47 | "type": "line", 48 | "layout": { 49 | "line-join": "round", 50 | "line-cap": "round" 51 | }, 52 | "paint": { 53 | {% if lineDashArray %} 54 | "line-dasharray": {{ lineDashArray }}, 55 | {% endif %} 56 | 57 | {% if enableDataJoin %} 58 | "line-color": ["case", 59 | ["boolean", ["feature-state", "hover"], false], 60 | "{{ highlightColor }}", 61 | generatePropertyExpression('match', "{{ vectorJoinDataProperty }}", {{ vectorColorStops }}, "{{ defaultColor }}")], 62 | "line-width": generatePropertyExpression('match', "{{ vectorJoinDataProperty }}", {{ vectorWidthStops }}, {{ defaultWidth}} ), 63 | {% else %} 64 | {% if colorProperty %} 65 | "line-color": ["case", 66 | ["boolean", ["feature-state", "hover"], false], 67 | "{{ highlightColor }}", 68 | generatePropertyExpression("{{ colorType }}", "{{ colorProperty }}", {{ colorStops }}, "{{ defaultColor }}")], 69 | {% else %} 70 | "line-color": ["case", 71 | ["boolean", ["feature-state", "hover"], false], 72 | "{{ highlightColor }}", 73 | "{{ defaultColor }}"], 74 | {% endif %} 75 | {% if widthProperty %} 76 | "line-width": generatePropertyExpression("{{ widthType }}", "{{ widthProperty }}", {{ widthStops }}, {{ defaultWidth }}), 77 | {% else %} 78 | "line-width": {{ defaultWidth }}, 79 | {% endif %} 80 | {% endif %} 81 | 82 | "line-opacity": {{ opacity }} 83 | } 84 | {% if enableDataJoin %} 85 | , filter: layerFilter 86 | {% endif %} 87 | }, "{{ belowLayer }}" ); 88 | 89 | // Add label layer 90 | map.addLayer({ 91 | "id": "linestring-label", 92 | "source": "vector-data", 93 | "source-layer": "{{ vectorLayer }}", 94 | "type": "symbol", 95 | "layout": { 96 | {% if labelProperty %} 97 | "text-field": "{{ labelProperty }}", 98 | {% endif %} 99 | "text-size" : generateInterpolateExpression('zoom', [[0, {{ labelSize }}],[22, 3* {{ labelSize }}]] ), 100 | "text-offset": [0,-1] 101 | }, 102 | "paint": { 103 | "text-halo-color": "{{ labelHaloColor }}", 104 | "text-halo-width": generatePropertyExpression('interpolate', 'zoom', [[0,{{ labelHaloWidth }}], [18,5* {{ labelHaloWidth }}]]), 105 | "text-color": ["case", 106 | ["boolean", ["feature-state", "hover"], false], 107 | "{{ highlightColor }}", 108 | "{{ labelColor }}"] 109 | } 110 | {% if enableDataJoin %} 111 | , filter: layerFilter 112 | {% endif %} 113 | }, "{{belowLayer}}" ); 114 | 115 | {% endblock linestring %} 116 | 117 | {% block linestring_popup %} 118 | 119 | var hoveredStateId = 0; 120 | 121 | map.on(popupAction, function(e) { 122 | var features = map.queryRenderedFeatures(e.point, {layers: ['linestring', 'linestring-label'] }); 123 | 124 | if (features.length > 0) { 125 | map.getCanvas().style.cursor = 'pointer'; 126 | var f = features[0]; 127 | newHoveredStateId = f.id; 128 | if (newHoveredStateId != hoveredStateId) { 129 | map.removeFeatureState({source: 'vector-data', sourceLayer: "{{ vectorLayer }}", id: hoveredStateId}); 130 | hoveredStateId = newHoveredStateId; 131 | } 132 | map.setFeatureState({source: 'vector-data', sourceLayer: "{{ vectorLayer }}", id: hoveredStateId}, { hover: true}); 133 | let popup_html = '
    '; 134 | 135 | for (key in f.properties) { 136 | popup_html += '
  • ' + key + ': ' + f.properties[key] + '
  • ' 137 | } 138 | 139 | popup_html += '
    ' 140 | popup.setLngLat(e.lngLat) 141 | .setHTML(popup_html) 142 | .addTo(map); 143 | } 144 | else { 145 | map.getCanvas().style.cursor = ''; 146 | popup.remove(); 147 | map.removeFeatureState({source: 'vector-data', sourceLayer: "{{ vectorLayer }}", id: hoveredStateId}); 148 | } 149 | }); 150 | 151 | {% endblock linestring_popup %} 152 | -------------------------------------------------------------------------------- /mapboxgl/templates/vector_map.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | {% block legend %}{% endblock legend %} 4 | 5 | {% block map %} 6 | 7 | map.on('style.load', function() { 8 | 9 | // Add vector data source 10 | map.addSource("vector-data", { 11 | type: "vector", 12 | url: "{{ vectorUrl }}", 13 | }); 14 | 15 | // Add vector data layer 16 | map.addLayer({ 17 | "id": "circle", 18 | "source": "vector-data", 19 | "source-layer": "{{ vectorLayer }}", 20 | "type": "circle", 21 | "maxzoom": {{ maxzoom }}, 22 | "minzoom": {{ minzoom }}, 23 | "paint": { "circle-radius": 1 } 24 | }); 25 | 26 | }); 27 | 28 | {% endblock map %} 29 | -------------------------------------------------------------------------------- /rtd-requirements.txt: -------------------------------------------------------------------------------- 1 | numpydoc 2 | -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [bdist_wheel] 2 | universal = 1 -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | from setuptools import setup, find_packages 2 | 3 | # Parse the version from the rasterio module. 4 | with open('mapboxgl/__init__.py') as f: 5 | for line in f: 6 | if line.find("__version__") >= 0: 7 | version = line.split("=")[1].strip() 8 | version = version.strip('"') 9 | version = version.strip("'") 10 | continue 11 | 12 | setup( 13 | name='mapboxgl', 14 | version=version, 15 | description=u"MapboxGL plugin for Jupyter Notebooks", 16 | long_description="Use Mapbox GL natively in your Jupyter Notebook workflows", 17 | classifiers=[ 18 | 'Development Status :: 1 - Planning', 19 | 'Intended Audience :: Developers', 20 | 'Intended Audience :: Science/Research', 21 | 'License :: OSI Approved :: MIT License', 22 | 'Programming Language :: Python :: 2', 23 | 'Programming Language :: Python :: 3', 24 | 'Topic :: Multimedia :: Graphics :: Graphics Conversion', 25 | 'Topic :: Scientific/Engineering :: GIS'], 26 | author=u"Ryan Baumann", 27 | url='https://github.com/mapbox/mapboxgl-jupyter', 28 | license='MIT', 29 | packages=find_packages(exclude=['ez_setup', 'examples', 'tests']), 30 | package_data={ 31 | 'mapboxgl': ['templates/*']}, 32 | include_package_data=True, 33 | zip_safe=False, 34 | install_requires=['jinja2', 'geojson', 'chroma-py', 'colour', 'matplotlib', 'ipython', 'requests'], 35 | extras_require={ 36 | 'test': ['pytest>=3.6', 'pytest-cov', 'codecov', 'mock', 'jupyter', 'Sphinx', 'pandas']}) 37 | -------------------------------------------------------------------------------- /tests/linestrings.geojson: -------------------------------------------------------------------------------- 1 | { 2 | "type": "FeatureCollection", 3 | "features": [{ 4 | "type": "Feature", 5 | "id": "01", 6 | "properties": {"sample": 50, "width": 1}, 7 | "geometry": { 8 | "type": "LineString", 9 | "coordinates": [ 10 | [-122.4833858013153, 37.829607404976734], 11 | [-122.4830961227417, 37.82932776098012], 12 | [-122.4830746650696, 37.82932776098012], 13 | [-122.48218417167662, 37.82889558180985], 14 | [-122.48218417167662, 37.82890193740421], 15 | [-122.48221099376678, 37.82868372835086], 16 | [-122.4822163581848, 37.82868372835086], 17 | [-122.48205006122589, 37.82801003030873] 18 | ] 19 | } 20 | }, { 21 | "type": "Feature", 22 | "id": "02", 23 | "properties": {"sample": 500, "width": 2}, 24 | "geometry": { 25 | "type": "LineString", 26 | "coordinates": [ 27 | [-122.4833858013153, 37.929607404976734], 28 | [-122.4830961227417, 37.83] 29 | ] 30 | } 31 | }, { 32 | "type": "Feature", 33 | "properties": {"sample": 5000, "width": 1}, 34 | "geometry": { 35 | "type": "LineString", 36 | "coordinates": [ 37 | [-122.48369693756104, 37.83381888486939], 38 | [-122.48348236083984, 37.83317489144141], 39 | [-122.48339653015138, 37.83270036637107], 40 | [-122.48356819152832, 37.832056363179625], 41 | [-122.48404026031496, 37.83114119107971], 42 | [-122.48404026031496, 37.83049717427869], 43 | [-122.48348236083984, 37.829920943955045], 44 | [-122.48356819152832, 37.82954808664175], 45 | [-122.48507022857666, 37.82944639795659], 46 | [-122.48610019683838, 37.82880236636284], 47 | [-122.48695850372314, 37.82931081282506], 48 | [-122.48700141906738, 37.83080223556934], 49 | [-122.48751640319824, 37.83168351665737], 50 | [-122.48803138732912, 37.832158048267786], 51 | [-122.48888969421387, 37.83297152392784], 52 | [-122.48987674713133, 37.83263257682617], 53 | [-122.49043464660643, 37.832937629287755], 54 | [-122.49125003814696, 37.832429207817725], 55 | [-122.49163627624512, 37.832564787218985], 56 | [-122.49223709106445, 37.83337825839438], 57 | [-122.49378204345702, 37.83368330777276] 58 | ] 59 | } 60 | }] 61 | } -------------------------------------------------------------------------------- /tests/mosaic.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mapbox/mapboxgl-jupyter/6c3cbb94e7a1684f9eb7ea6c6653228bede53b3d/tests/mosaic.png -------------------------------------------------------------------------------- /tests/points.csv: -------------------------------------------------------------------------------- 1 | Avg Medicare Payments,Avg Covered Charges,Avg Total Payments,lon,lat,date 2 | 7678.214347826088,35247.02815217392,8749.025108695649,-85.36285599999992,31.216214999999963,1/1/14 12:00 AM 3 | 5793.63142857143,16451.092040816326,6812.1312244897945,-88.14279690000002,32.452976300000024,1/2/14 12:00 AM 4 | 1200,1800,2200,-89.14279690000002,31.452976300000024,11/11/11 11:11 AM -------------------------------------------------------------------------------- /tests/points.geojson: -------------------------------------------------------------------------------- 1 | { 2 | "type": "FeatureCollection", 3 | "features": [ 4 | { 5 | "type": "Feature", 6 | "properties": { 7 | "Avg Medicare Payments": 7678.214347826088, 8 | "Avg Covered Charges": 35247.02815217392, 9 | "Avg Total Payments": 8749.025108695649, 10 | "Provider Id": 10001 11 | }, 12 | "geometry": { 13 | "type": "Point", 14 | "coordinates": [ 15 | -85.36285599999992, 16 | 31.216214999999963 17 | ] 18 | } 19 | }, 20 | { 21 | "type": "Feature", 22 | "properties": { 23 | "Avg Medicare Payments": 5793.63142857143, 24 | "Avg Covered Charges": 16451.092040816326, 25 | "Avg Total Payments": 6812.1312244897945, 26 | "Provider Id": 10005 27 | }, 28 | "geometry": { 29 | "type": "Point", 30 | "coordinates": [ 31 | -88.14279690000002, 32 | 32.452976300000024 33 | ] 34 | } 35 | }, 36 | { 37 | "type": "Feature", 38 | "properties": { 39 | "Avg Medicare Payments": 1200, 40 | "Avg Covered Charges": 1800, 41 | "Avg Total Payments": 2200, 42 | "Provider Id": 0 43 | }, 44 | "geometry": { 45 | "type": "Point", 46 | "coordinates": [ 47 | -89.14279690000002, 48 | 31.452976300000024 49 | ] 50 | } 51 | } 52 | ] 53 | } 54 | -------------------------------------------------------------------------------- /tests/polygons.geojson: -------------------------------------------------------------------------------- 1 | { 2 | "type": "FeatureCollection", 3 | "features": [ 4 | { 5 | "type": "Feature", 6 | "id": "06", 7 | "properties": { 8 | "name": "California", 9 | "density": 241.7 10 | }, 11 | "geometry": { 12 | "type": "Polygon", 13 | "coordinates": [ 14 | [ 15 | [-123.233256, 42.006186], 16 | [-122.378853, 42.011663], 17 | [-121.037003, 41.995232], 18 | [-120.001861, 41.995232], 19 | [-119.996384, 40.264519], 20 | [-120.001861, 38.999346], 21 | [-118.71478, 38.101128], 22 | [-117.498899, 37.21934], 23 | [-116.540435, 36.501861], 24 | [-115.85034, 35.970598], 25 | [-114.634459, 35.00118], 26 | [-114.634459, 34.87521], 27 | [-114.470151, 34.710902], 28 | [-114.333228, 34.448009], 29 | [-114.136058, 34.305608], 30 | [-114.256551, 34.174162], 31 | [-114.415382, 34.108438], 32 | [-114.535874, 33.933176], 33 | [-114.497536, 33.697668], 34 | [-114.524921, 33.54979], 35 | [-114.727567, 33.40739], 36 | [-114.661844, 33.034958], 37 | [-114.524921, 33.029481], 38 | [-114.470151, 32.843265], 39 | [-114.524921, 32.755634], 40 | [-114.72209, 32.717295], 41 | [-116.04751, 32.624187], 42 | [-117.126467, 32.536556], 43 | [-117.24696, 32.668003], 44 | [-117.252437, 32.876127], 45 | [-117.329114, 33.122589], 46 | [-117.471515, 33.297851], 47 | [-117.7837, 33.538836], 48 | [-118.183517, 33.763391], 49 | [-118.260194, 33.703145], 50 | [-118.413548, 33.741483], 51 | [-118.391641, 33.840068], 52 | [-118.566903, 34.042715], 53 | [-118.802411, 33.998899], 54 | [-119.218659, 34.146777], 55 | [-119.278905, 34.26727], 56 | [-119.558229, 34.415147], 57 | [-119.875891, 34.40967], 58 | [-120.138784, 34.475393], 59 | [-120.472878, 34.448009], 60 | [-120.64814, 34.579455], 61 | [-120.609801, 34.858779], 62 | [-120.670048, 34.902595], 63 | [-120.631709, 35.099764], 64 | [-120.894602, 35.247642], 65 | [-120.905556, 35.450289], 66 | [-121.004141, 35.461243], 67 | [-121.168449, 35.636505], 68 | [-121.283465, 35.674843], 69 | [-121.332757, 35.784382], 70 | [-121.716143, 36.195153], 71 | [-121.896882, 36.315645], 72 | [-121.935221, 36.638785], 73 | [-121.858544, 36.6114], 74 | [-121.787344, 36.803093], 75 | [-121.929744, 36.978355], 76 | [-122.105006, 36.956447], 77 | [-122.335038, 37.115279], 78 | [-122.417192, 37.241248], 79 | [-122.400761, 37.361741], 80 | [-122.515777, 37.520572], 81 | [-122.515777, 37.783465], 82 | [-122.329561, 37.783465], 83 | [-122.406238, 38.15042], 84 | [-122.488392, 38.112082], 85 | [-122.504823, 37.931343], 86 | [-122.701993, 37.893004], 87 | [-122.937501, 38.029928], 88 | [-122.97584, 38.265436], 89 | [-123.129194, 38.451652], 90 | [-123.331841, 38.566668], 91 | [-123.44138, 38.698114], 92 | [-123.737134, 38.95553], 93 | [-123.687842, 39.032208], 94 | [-123.824765, 39.366301], 95 | [-123.764519, 39.552517], 96 | [-123.85215, 39.831841], 97 | [-124.109566, 40.105688], 98 | [-124.361506, 40.259042], 99 | [-124.410798, 40.439781], 100 | [-124.158859, 40.877937], 101 | [-124.109566, 41.025814], 102 | [-124.158859, 41.14083], 103 | [-124.065751, 41.442061], 104 | [-124.147905, 41.715908], 105 | [-124.257444, 41.781632], 106 | [-124.213628, 42.000709], 107 | [-123.233256, 42.006186] 108 | ] 109 | ] 110 | } 111 | }, 112 | { 113 | "type": "Feature", 114 | "id": "11", 115 | "properties": { 116 | "name": "District of Columbia", 117 | "density": 10065 118 | }, 119 | "geometry": { 120 | "type": "Polygon", 121 | "coordinates": [ 122 | [ 123 | [-77.035264, 38.993869], 124 | [-76.909294, 38.895284], 125 | [-77.040741, 38.791222], 126 | [-77.117418, 38.933623], 127 | [-77.035264, 38.993869] 128 | ] 129 | ] 130 | } 131 | }, 132 | { 133 | "type": "Feature", 134 | "id": "25", 135 | "properties": { 136 | "name": "Massachusetts", 137 | "density": 840.2 138 | }, 139 | "geometry": { 140 | "type": "Polygon", 141 | "coordinates": [ 142 | [ 143 | [-70.917521, 42.887974], 144 | [-70.818936, 42.871543], 145 | [-70.780598, 42.696281], 146 | [-70.824413, 42.55388], 147 | [-70.983245, 42.422434], 148 | [-70.988722, 42.269079], 149 | [-70.769644, 42.247172], 150 | [-70.638197, 42.08834], 151 | [-70.660105, 41.962371], 152 | [-70.550566, 41.929509], 153 | [-70.539613, 41.814493], 154 | [-70.260289, 41.715908], 155 | [-69.937149, 41.809016], 156 | [-70.008349, 41.672093], 157 | [-70.484843, 41.5516], 158 | [-70.660105, 41.546123], 159 | [-70.764167, 41.639231], 160 | [-70.928475, 41.611847], 161 | [-70.933952, 41.540646], 162 | [-71.120168, 41.496831], 163 | [-71.196845, 41.67757], 164 | [-71.22423, 41.710431], 165 | [-71.328292, 41.781632], 166 | [-71.383061, 42.01714], 167 | [-71.530939, 42.01714], 168 | [-71.799309, 42.006186], 169 | [-71.799309, 42.022617], 170 | [-73.053528, 42.039048], 171 | [-73.486206, 42.050002], 172 | [-73.508114, 42.08834], 173 | [-73.267129, 42.745573], 174 | [-72.456542, 42.729142], 175 | [-71.29543, 42.696281], 176 | [-71.185891, 42.789389], 177 | [-70.917521, 42.887974] 178 | ] 179 | ] 180 | } 181 | }, 182 | { 183 | "type": "Feature", 184 | "id": "30", 185 | "properties": { 186 | "name": "Montana", 187 | "density": 6.858 188 | }, 189 | "geometry": { 190 | "type": "Polygon", 191 | "coordinates": [ 192 | [ 193 | [-104.047534, 49.000239], 194 | [-104.042057, 47.861036], 195 | [-104.047534, 45.944106], 196 | [-104.042057, 44.996596], 197 | [-104.058488, 44.996596], 198 | [-105.91517, 45.002073], 199 | [-109.080842, 45.002073], 200 | [-111.05254, 45.002073], 201 | [-111.047063, 44.476286], 202 | [-111.227803, 44.580348], 203 | [-111.386634, 44.75561], 204 | [-111.616665, 44.547487], 205 | [-111.819312, 44.509148], 206 | [-111.868605, 44.563917], 207 | [-112.104113, 44.520102], 208 | [-112.241036, 44.569394], 209 | [-112.471068, 44.481763], 210 | [-112.783254, 44.48724], 211 | [-112.887315, 44.394132], 212 | [-113.002331, 44.448902], 213 | [-113.133778, 44.772041], 214 | [-113.341901, 44.782995], 215 | [-113.456917, 44.865149], 216 | [-113.45144, 45.056842], 217 | [-113.571933, 45.128042], 218 | [-113.736241, 45.330689], 219 | [-113.834826, 45.522382], 220 | [-113.807441, 45.604536], 221 | [-113.98818, 45.703121], 222 | [-114.086765, 45.593582], 223 | [-114.333228, 45.456659], 224 | [-114.546828, 45.560721], 225 | [-114.497536, 45.670259], 226 | [-114.568736, 45.774321], 227 | [-114.387997, 45.88386], 228 | [-114.492059, 46.037214], 229 | [-114.464674, 46.272723], 230 | [-114.322274, 46.645155], 231 | [-114.612552, 46.639678], 232 | [-114.623506, 46.705401], 233 | [-114.886399, 46.809463], 234 | [-114.930214, 46.919002], 235 | [-115.302646, 47.187372], 236 | [-115.324554, 47.258572], 237 | [-115.527201, 47.302388], 238 | [-115.718894, 47.42288], 239 | [-115.724371, 47.696727], 240 | [-116.04751, 47.976051], 241 | [-116.04751, 49.000239], 242 | [-111.50165, 48.994762], 243 | [-109.453274, 49.000239], 244 | [-104.047534, 49.000239] 245 | ] 246 | ] 247 | } 248 | }, 249 | { 250 | "type": "Feature", 251 | "id": "36", 252 | "properties": { 253 | "name": "New York", 254 | "density": 412.3 255 | }, 256 | "geometry": { 257 | "type": "Polygon", 258 | "coordinates": [ 259 | [ 260 | [-73.343806, 45.013027], 261 | [-73.332852, 44.804903], 262 | [-73.387622, 44.618687], 263 | [-73.294514, 44.437948], 264 | [-73.321898, 44.246255], 265 | [-73.436914, 44.043608], 266 | [-73.349283, 43.769761], 267 | [-73.404052, 43.687607], 268 | [-73.245221, 43.523299], 269 | [-73.278083, 42.833204], 270 | [-73.267129, 42.745573], 271 | [-73.508114, 42.08834], 272 | [-73.486206, 42.050002], 273 | [-73.55193, 41.294184], 274 | [-73.48073, 41.21203], 275 | [-73.727192, 41.102491], 276 | [-73.655992, 40.987475], 277 | [-73.22879, 40.905321], 278 | [-73.141159, 40.965568], 279 | [-72.774204, 40.965568], 280 | [-72.587988, 40.998429], 281 | [-72.28128, 41.157261], 282 | [-72.259372, 41.042245], 283 | [-72.100541, 40.992952], 284 | [-72.467496, 40.845075], 285 | [-73.239744, 40.625997], 286 | [-73.562884, 40.582182], 287 | [-73.776484, 40.593136], 288 | [-73.935316, 40.543843], 289 | [-74.022947, 40.708151], 290 | [-73.902454, 40.998429], 291 | [-74.236547, 41.14083], 292 | [-74.69661, 41.359907], 293 | [-74.740426, 41.431108], 294 | [-74.89378, 41.436584], 295 | [-75.074519, 41.60637], 296 | [-75.052611, 41.754247], 297 | [-75.173104, 41.869263], 298 | [-75.249781, 41.863786], 299 | [-75.35932, 42.000709], 300 | [-79.76278, 42.000709], 301 | [-79.76278, 42.252649], 302 | [-79.76278, 42.269079], 303 | [-79.149363, 42.55388], 304 | [-79.050778, 42.690804], 305 | [-78.853608, 42.783912], 306 | [-78.930285, 42.953697], 307 | [-79.012439, 42.986559], 308 | [-79.072686, 43.260406], 309 | [-78.486653, 43.375421], 310 | [-77.966344, 43.369944], 311 | [-77.75822, 43.34256], 312 | [-77.533665, 43.233021], 313 | [-77.391265, 43.276836], 314 | [-76.958587, 43.271359], 315 | [-76.695693, 43.34256], 316 | [-76.41637, 43.523299], 317 | [-76.235631, 43.528776], 318 | [-76.230154, 43.802623], 319 | [-76.137046, 43.961454], 320 | [-76.3616, 44.070993], 321 | [-76.312308, 44.196962], 322 | [-75.912491, 44.366748], 323 | [-75.764614, 44.514625], 324 | [-75.282643, 44.848718], 325 | [-74.828057, 45.018503], 326 | [-74.148916, 44.991119], 327 | [-73.343806, 45.013027] 328 | ] 329 | ] 330 | } 331 | }, 332 | { 333 | "type": "Feature", 334 | "id": "49", 335 | "properties": { 336 | "name": "Utah", 337 | "density": 34.3 338 | }, 339 | "geometry": { 340 | "type": "Polygon", 341 | "coordinates": [ 342 | [ 343 | [-112.164359, 41.995232], 344 | [-111.047063, 42.000709], 345 | [-111.047063, 40.998429], 346 | [-109.04798, 40.998429], 347 | [-109.053457, 39.125316], 348 | [-109.058934, 38.27639], 349 | [-109.042503, 38.166851], 350 | [-109.042503, 37.000263], 351 | [-110.499369, 37.00574], 352 | [-114.048427, 37.000263], 353 | [-114.04295, 41.995232], 354 | [-112.164359, 41.995232] 355 | ] 356 | ] 357 | } 358 | } 359 | ] 360 | } -------------------------------------------------------------------------------- /tests/test_colors.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from mapboxgl.utils import create_color_stops 4 | 5 | 6 | def test_colors(): 7 | assert create_color_stops([1, 2, 3], colors='PuOr') == \ 8 | [[1, 'rgb(241,163,64)'], [2, 'rgb(247,247,247)'], [3, 'rgb(153,142,195)']] 9 | 10 | 11 | def test_bad_ramp(): 12 | with pytest.raises(ValueError): 13 | create_color_stops([1, 2, 3], colors='DoubleRainbow') 14 | 15 | 16 | def test_too_many_colors(): 17 | with pytest.raises(ValueError): 18 | create_color_stops(list(range(100)), colors='PuOr') 19 | -------------------------------------------------------------------------------- /tests/test_utils.py: -------------------------------------------------------------------------------- 1 | import os 2 | import json 3 | import numpy 4 | import pytest 5 | import pandas as pd 6 | from pandas.util.testing import assert_frame_equal 7 | 8 | from matplotlib.pyplot import imread 9 | 10 | from mapboxgl.errors import SourceDataError, DateConversionError 11 | from mapboxgl.utils import (df_to_geojson, geojson_to_dict_list, scale_between, create_radius_stops, 12 | create_weight_stops, create_numeric_stops, create_color_stops, 13 | img_encode, rgb_tuple_from_str, color_map, height_map, numeric_map, 14 | convert_date_columns) 15 | 16 | 17 | @pytest.fixture() 18 | def df(): 19 | return pd.read_csv('tests/points.csv') 20 | 21 | 22 | @pytest.fixture() 23 | def df_no_properties(): 24 | df = pd.read_csv('tests/points.csv') 25 | return df[['lon', 'lat']] 26 | 27 | 28 | def test_df_geojson(df): 29 | features = df_to_geojson(df)['features'] 30 | assert len(features) == 3 31 | 32 | 33 | def test_df_properties(df): 34 | features = df_to_geojson(df, properties=['Avg Medicare Payments'])[ 35 | 'features'] 36 | assert tuple(features[0]['properties'].keys()) == ('Avg Medicare Payments',) 37 | 38 | 39 | def test_df_no_properties(df_no_properties): 40 | features = df_to_geojson(df_no_properties)[ 41 | 'features'] 42 | assert tuple(features[0]['properties'].keys()) == () 43 | 44 | 45 | def test_df_geojson_file(df): 46 | features = df_to_geojson(df, filename='out.geojson') 47 | with open('out.geojson', 'r') as f: 48 | testdata = json.load(f) 49 | assert len(testdata['features']) == 3 50 | 51 | 52 | def test_df_geojson_file_nonsequential_index(df): 53 | df.set_index('Avg Total Payments', inplace=True) 54 | features = df_to_geojson(df, filename='out.geojson') 55 | with open('out.geojson', 'r') as f: 56 | testdata = json.load(f) 57 | assert len(testdata['features']) == 3 58 | 59 | 60 | def test_scale_between(): 61 | scale = scale_between(0, 1, 4) 62 | assert scale == [0.0, 0.25, 0.5, 0.75] 63 | 64 | 65 | def test_scale_between_valueError(): 66 | """Create radius stops raises ValueError""" 67 | with pytest.raises(ValueError): 68 | scale_between(1, 0, 10) 69 | 70 | 71 | def test_scale_between_maxMin(): 72 | """Create radius stops raises ValueError""" 73 | scale = scale_between(0,1,1) 74 | assert scale == [0,1] 75 | 76 | 77 | def test_color_stops(): 78 | """Create color stops from breaks using colorBrewer""" 79 | stops = create_color_stops([0, 1, 2], colors='YlGn') 80 | assert stops == [[0,"rgb(247,252,185)"], [1,"rgb(173,221,142)"], [2,"rgb(49,163,84)"]] 81 | 82 | 83 | def test_color_stops_custom(): 84 | """Create color stops from custom color breaks""" 85 | stops = create_color_stops([0, 1, 2], colors=['red', 'yellow', 'green']) 86 | assert stops == [[0,"red"], [1,"yellow"], [2,"green"]] 87 | 88 | 89 | def test_color_stops_custom_invalid(): 90 | """Create invalid color stops from custom color breaks and throw value error""" 91 | with pytest.raises(ValueError): 92 | create_color_stops([0, 1, 2], colors=['x', 'yellow', 'green']) 93 | 94 | 95 | def test_color_stops_custom_null(): 96 | """Create invalid number of color stops that do not match the number of breaks""" 97 | with pytest.raises(ValueError): 98 | create_color_stops([0, 1, 2], colors=['red', 'yellow', 'green', 'grey']) 99 | 100 | 101 | def test_create_radius_stops(df): 102 | domain = [7678.214347826088, 5793.63142857143, 1200] 103 | radius_stops = create_radius_stops(domain, 1, 10) 104 | assert radius_stops == [[7678.214347826088, 1.0], [5793.63142857143, 4.0], [1200, 7.0]] 105 | 106 | 107 | def test_create_weight_stops(df): 108 | res = create_weight_stops([1, 2, 3, 4]) 109 | assert res == [[1, 0.0], [2, 0.25], [3, 0.5], [4, 0.75]] 110 | 111 | 112 | def test_img_encode(): 113 | image_path = os.path.join(os.path.dirname(__file__), 'mosaic.png') 114 | image = imread(image_path) 115 | assert img_encode(image).startswith('data:image/png;base64') 116 | 117 | 118 | def test_rgb_tuple_from_str(): 119 | """Extract RGB values as tuple from string RGB color representation""" 120 | assert rgb_tuple_from_str('rgb(122,43,17)') == (122, 43, 17) 121 | 122 | 123 | def test_rgb_tuple_from_str_rgba(): 124 | """Extract RGBA values as tuple from string RGBA color representation""" 125 | assert rgb_tuple_from_str('rgba(122,43,17,0.5)') == (122, 43, 17, 0.5) 126 | 127 | 128 | def test_rgb_tuple_from_str_hex(): 129 | """Extract RGB(A) values as tuple from string HEX color representation""" 130 | assert rgb_tuple_from_str('#bada55') == (186, 218, 85) 131 | 132 | 133 | def test_rgb_tuple_from_str_english(): 134 | """Extract RGB(A) values as tuple from limited English color name strings""" 135 | assert rgb_tuple_from_str('red') == (255, 0, 0) 136 | 137 | 138 | def test_color_map(): 139 | """Compute color for lookup value in gradient based on color_stops argument using categorical match""" 140 | match_stops = [[0.0, 'rgb(255,0,255)'],['CA', 'rgb(255,0,0)'], ['NY', 'rgb(255,255,0)'], ['MA', 'rgb(0,0,255)']] 141 | assert color_map('CA', match_stops, default_color='gray') == 'rgb(255,0,0)' 142 | 143 | 144 | def test_color_map_numeric_default_color(): 145 | """Default color when look up value does not match any stop in categorical color stops""" 146 | match_stops = [[0.0, 'rgb(255,0,255)'],['CA', 'rgb(255,0,0)'], ['NY', 'rgb(255,255,0)'], ['MA', 'rgb(0,0,255)']] 147 | assert color_map(17, match_stops, 'blue') == 'blue' 148 | 149 | 150 | def test_color_map_default_color(): 151 | """Default color when look up value does not match any stop in categorical color stops""" 152 | match_stops = [[0.0, 'rgb(255,0,255)'],['CA', 'rgb(255,0,0)'], ['NY', 'rgb(255,255,0)'], ['MA', 'rgb(0,0,255)']] 153 | assert color_map('MI', match_stops, 'gray') == 'gray' 154 | 155 | 156 | def test_color_map_numeric_match(): 157 | """Get color for numeric lookup value in categorical color stops if number exists in stops""" 158 | match_stops = [[0.0, 'rgb(255,0,255)'],['CA', 'rgb(255,0,0)'], ['NY', 'rgb(255,255,0)'], ['MA', 'rgb(0,0,255)']] 159 | assert color_map(0.0, match_stops, 'green') == 'rgb(255,0,255)' 160 | 161 | 162 | def test_color_map_interp(): 163 | """Compute color for lookup value by interpolation of color stops""" 164 | interp_stops = [[0.0, 'rgb(255,0,0)'], [50.0, 'rgb(255,255,0)'], [1000.0, 'rgb(0,0,255)']] 165 | assert color_map(17, interp_stops, 'orange') == 'rgb(255,87,0)' 166 | 167 | 168 | def test_color_map_interp_exact(): 169 | """Compute color for lookup value exactly matching numeric stop in color stops""" 170 | interp_stops = [[0.0, 'rgb(255,0,0)'], [50.0, 'rgb(255,255,0)'], [1000.0, 'rgb(0,0,255)']] 171 | assert color_map(0.0, interp_stops, 'rgb(32,32,32)') == 'rgb(255,0,0)' 172 | 173 | 174 | def test_numeric_map(): 175 | """Map interpolated (or matched) value from numeric stops""" 176 | stops = [[0.0, 0], [50.0, 5000.0], [1000.0, 100000.0]] 177 | assert numeric_map(117.0, stops, 0.0) == 11700.0 178 | 179 | 180 | def test_numeric_map_match(): 181 | """Match value from numeric stops""" 182 | match_stops = [['road', 1.0], ['fence', 15.0], ['wall', 10.0]] 183 | assert numeric_map('fence', match_stops, 0.0) == 15.0 184 | 185 | 186 | def test_numeric_map_no_stops(): 187 | """Return default if length of stops argument is 0""" 188 | stops = [] 189 | assert numeric_map(117.0, stops, 42) == 42 190 | 191 | 192 | def test_numeric_map_default(): 193 | """Default value when look up does not match any stop in stops""" 194 | stops = [[0.0, 0], [50.0, 5000.0], [1000.0, 100000.0]] 195 | assert numeric_map(-1.0, stops, 42) == 0 196 | 197 | 198 | def test_numeric_map_exact(): 199 | """Compute mapping for lookup value exactly matching numeric stop in stops""" 200 | stops = [[0.0, 0], [50.0, 5000.0], [1000.0, 100000.0]] 201 | assert numeric_map(50.0, stops, 42) == 5000.0 202 | 203 | 204 | def test_create_numeric_stops(): 205 | """Create numeric stops from custom breaks""" 206 | domain = [7678.214347826088, 5793.63142857143, 1200] 207 | stops = create_numeric_stops(domain, 1, 10) 208 | assert stops == [[7678.214347826088, 1.0], [5793.63142857143, 4.0], [1200, 7.0]] 209 | 210 | 211 | def test_height_map(): 212 | """Interpolate height from numeric height stops""" 213 | stops = [[0.0, 0], [50.0, 5000.0], [1000.0, 100000.0]] 214 | assert height_map(117.0, stops, 0.0) == 11700.0 215 | 216 | 217 | def test_height_map_match(): 218 | """Interpolate height from numeric height stops""" 219 | match_stops = [['road', 1.0], ['fence', 15.0], ['wall', 10.0]] 220 | assert height_map('fence', match_stops, 0.0) == 15.0 221 | 222 | 223 | def test_height_map_no_stops(): 224 | """Return default if length of stops argument is 0""" 225 | stops = [] 226 | assert height_map(117.0, stops, 42) == 42 227 | 228 | 229 | def test_height_map_default(): 230 | """Default value when look up does not match any stop in stops""" 231 | stops = [[0.0, 0], [50.0, 5000.0], [1000.0, 100000.0]] 232 | assert height_map(-1.0, stops, 42) == 0 233 | 234 | 235 | def test_height_map_exact(): 236 | """Compute mapping for lookup value exactly matching numeric stop in stops""" 237 | stops = [[0.0, 0], [50.0, 5000.0], [1000.0, 100000.0]] 238 | assert height_map(50.0, stops, 42) == 5000.0 239 | 240 | 241 | def test_geojson_to_dict_list_json(df): 242 | """Ensure data converted to Python dict""" 243 | data = json.loads(df.to_json(orient='records')) 244 | assert type(geojson_to_dict_list(data)) == list 245 | 246 | 247 | def test_geojson_to_dict_list_file(): 248 | """Ensure data converted to Python dict""" 249 | data = 'tests/points.geojson' 250 | assert type(geojson_to_dict_list(data)) == list 251 | 252 | 253 | def test_geojson_to_dict_list_url(): 254 | """Ensure data converted to Python dict""" 255 | data = 'https://raw.githubusercontent.com/mapbox/mapboxgl-jupyter/master/tests/points.geojson' 256 | assert type(geojson_to_dict_list(data)) == list 257 | 258 | 259 | def test_geojson_to_dict_list_invalid(): 260 | """Ensure data converted to Python dict""" 261 | with pytest.raises(SourceDataError): 262 | geojson_to_dict_list(0) 263 | 264 | 265 | def test_convert_date_columns(df): 266 | """Ensure datetime data converted to string format""" 267 | df['date'] = pd.to_datetime(df['date']) 268 | df = convert_date_columns(df, date_format='%Y-%m-%d') 269 | assert df['date'].values[0] == '2014-01-01' 270 | 271 | 272 | def test_convert_date_columns_default(df): 273 | """Tests default datetime format for dataframe date serialization 274 | remains as numpy datetime format""" 275 | df['date'] = pd.to_datetime(df['date']) 276 | df = convert_date_columns(df) 277 | assert isinstance(df['date'].values[0], numpy.datetime64) 278 | 279 | 280 | def test_convert_date_columns_error(df): 281 | """Raise DateConversionError with improper date_format""" 282 | with pytest.raises(DateConversionError): 283 | convert_date_columns(df, date_format='') 284 | 285 | --------------------------------------------------------------------------------