`__.
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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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/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/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/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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/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/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 | }
--------------------------------------------------------------------------------