From 843476b4c9dab7985932d53ae7445456f060d54b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rom=C3=A1n=20Jim=C3=A9nez?= Date: Tue, 7 May 2019 19:55:41 +0200 Subject: [PATCH 01/20] Point to cartoframes version --- cartoframes/carto_vl/utils/defaults.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/cartoframes/carto_vl/utils/defaults.py b/cartoframes/carto_vl/utils/defaults.py index 7ea238e9c..807c9201c 100644 --- a/cartoframes/carto_vl/utils/defaults.py +++ b/cartoframes/carto_vl/utils/defaults.py @@ -6,10 +6,10 @@ _AIRSHIP_BRIDGE_SCRIPT = '/packages/bridge/dist/asbridge.js' _AIRSHIP_STYLE = '/packages/styles/dist/airship.css' _AIRSHIP_ICONS_STYLE = '/packages/icons/dist/icons.css' -_AIRSHIP_COMPONENTS_PATH = 'https://libs.cartocdn.com/airship-components/v2.0/airship.js' -_AIRSHIP_BRIDGE_PATH = 'https://libs.cartocdn.com/airship-bridge/v2.0/asbridge.js' -_AIRSHIP_STYLES_PATH = 'https://libs.cartocdn.com/airship-style/v2.0/airship.css' -_AIRSHIP_ICONS_PATH = 'https://libs.cartocdn.com/airship-icons/v2.0/icons.css' +_AIRSHIP_COMPONENTS_PATH = 'https://libs.cartocdn.com/airship-components/cartoframes/airship.js' +_AIRSHIP_BRIDGE_PATH = 'https://libs.cartocdn.com/airship-bridge/cartoframes/asbridge.js' +_AIRSHIP_STYLES_PATH = 'https://libs.cartocdn.com/airship-style/cartoframes/airship.css' +_AIRSHIP_ICONS_PATH = 'https://libs.cartocdn.com/airship-icons/cartoframes/icons.css' _CREDENTIALS = { 'username': 'default', From cc8c2b848b68dca9ef6fc664a33780759fb4a9ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rom=C3=A1n=20Jim=C3=A9nez?= Date: Wed, 8 May 2019 18:08:07 +0200 Subject: [PATCH 02/20] Create default layer --- cartoframes/assets/templates/vector/basic.html.j2 | 7 ++++--- cartoframes/assets/templates/vector/legends.html.j2 | 8 ++++++++ .../assets/templates/vector/scripts/legends.js.j2 | 11 +++++++++++ cartoframes/assets/templates/vector/scripts/map.js.j2 | 11 +++++++++++ cartoframes/carto_vl/maps/map.py | 7 +++++++ cartoframes/carto_vl/utils/html.py | 9 +++++++-- 6 files changed, 48 insertions(+), 5 deletions(-) create mode 100644 cartoframes/assets/templates/vector/legends.html.j2 create mode 100644 cartoframes/assets/templates/vector/scripts/legends.js.j2 diff --git a/cartoframes/assets/templates/vector/basic.html.j2 b/cartoframes/assets/templates/vector/basic.html.j2 index 44d097c2c..d0ab6783b 100644 --- a/cartoframes/assets/templates/vector/basic.html.j2 +++ b/cartoframes/assets/templates/vector/basic.html.j2 @@ -80,9 +80,10 @@
-
- +
+ {% if has_legends %} + {% include 'vector/legends.html.j2' %} + {% endif %}
diff --git a/cartoframes/assets/templates/vector/legends.html.j2 b/cartoframes/assets/templates/vector/legends.html.j2 new file mode 100644 index 000000000..0591b6980 --- /dev/null +++ b/cartoframes/assets/templates/vector/legends.html.j2 @@ -0,0 +1,8 @@ +
+ {% if basic_legend != none %} + + + + + {% endif %} +
\ No newline at end of file diff --git a/cartoframes/assets/templates/vector/scripts/legends.js.j2 b/cartoframes/assets/templates/vector/scripts/legends.js.j2 new file mode 100644 index 000000000..8028c014c --- /dev/null +++ b/cartoframes/assets/templates/vector/scripts/legends.js.j2 @@ -0,0 +1,11 @@ +function createDefaultLegend(layers) { + AsBridge.VL.Legends.layersLegend( + '#basicLegend', + layers.map(layer => ({ + layer: layer, + props: { + label: layer.id + } + })) + ) +} \ No newline at end of file diff --git a/cartoframes/assets/templates/vector/scripts/map.js.j2 b/cartoframes/assets/templates/vector/scripts/map.js.j2 index bff5d17e2..7092092c0 100644 --- a/cartoframes/assets/templates/vector/scripts/map.js.j2 +++ b/cartoframes/assets/templates/vector/scripts/map.js.j2 @@ -1,3 +1,6 @@ +{% if has_legends %} + {% include 'vector/scripts/legends.js.j2' %} +{% endif %} {% include 'error/parser.js.j2' %} {% include 'utils/base64.js.j2' %} @@ -40,12 +43,16 @@ function onReady() { map.flyTo({{ camera|clear_none|tojson }}); {% endif %} + const layers = []; + sources.forEach((elem, idx) => { const factory = new SourceFactory(); const source = factory.createSource(elem.source, elem.query); const viz = new carto.Viz(elem['viz']); const layer = new carto.Layer(`layer${idx}`, source, viz); + layers.push(layer); + try { layer._updateLayer.catch(displayError); } catch (err) { @@ -130,6 +137,10 @@ function onReady() { } } }); + + {% if default_legend %} + createDefaultLegend(layers); + {% endif %} } function setReady () { diff --git a/cartoframes/carto_vl/maps/map.py b/cartoframes/carto_vl/maps/map.py index d2bdc3318..eff5b6fe3 100644 --- a/cartoframes/carto_vl/maps/map.py +++ b/cartoframes/carto_vl/maps/map.py @@ -122,6 +122,7 @@ def __init__(self, bounds=None, viewport=None, template=None, + default_legend=None, **kwargs): self.layers = _init_layers(layers) @@ -136,6 +137,11 @@ def __init__(self, self._airship_path = kwargs.get('_airship_path', None) self._htmlMap = HTMLMap() + if default_legend is None and all(layer.legend is None for layer in self.layers): + self.default_legend = True + else: + self.default_legend = default_legend + self._htmlMap.set_content( size=self.size, sources=self.sources, @@ -143,6 +149,7 @@ def __init__(self, viewport=self.viewport, creds=self.context.creds if self.context else None, basemap=self.basemap, + default_legend=self.default_legend, _carto_vl_path=self._carto_vl_path, _airship_path=self._airship_path) diff --git a/cartoframes/carto_vl/utils/html.py b/cartoframes/carto_vl/utils/html.py index 5bcdb1222..23eacec59 100644 --- a/cartoframes/carto_vl/utils/html.py +++ b/cartoframes/carto_vl/utils/html.py @@ -26,14 +26,15 @@ def __init__(self): def set_content( self, size, sources, bounds, viewport=None, creds=None, basemap=None, + default_legend=None, _carto_vl_path=defaults._CARTO_VL_PATH, _airship_path=None): self.html = self._parse_html_content( - size, sources, bounds, viewport, creds, basemap, + size, sources, bounds, viewport, creds, basemap, default_legend, _carto_vl_path, _airship_path) def _parse_html_content( - self, size, sources, bounds, viewport, creds=None, basemap=None, + self, size, sources, bounds, viewport, creds=None, basemap=None, default_legend=None, _carto_vl_path=defaults._CARTO_VL_PATH, _airship_path=None): token = '' @@ -77,6 +78,8 @@ def _parse_html_content( 'pitch': viewport.get('pitch') } + has_legends = any(source['legend'] is not None for source in sources) or default_legend + return self._template.render( width=size[0] if size is not None else None, height=size[1] if size is not None else None, @@ -87,6 +90,8 @@ def _parse_html_content( bounds=bounds, camera=camera, carto_vl_path=_carto_vl_path, + has_legends=has_legends, + default_legend=default_legend, airship_components_path=airship_components_path, airship_bridge_path=airship_bridge_path, airship_styles_path=airship_styles_path, From 2b5049801316f5ebae6cc3a249cc138d8cb061a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rom=C3=A1n=20Jim=C3=A9nez?= Date: Wed, 8 May 2019 18:43:29 +0200 Subject: [PATCH 03/20] Hide legend until layer loads --- cartoframes/assets/templates/vector/basic.html.j2 | 2 +- cartoframes/assets/templates/vector/legends.html.j2 | 2 +- .../assets/templates/vector/scripts/legends.js.j2 | 13 +++++++------ 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/cartoframes/assets/templates/vector/basic.html.j2 b/cartoframes/assets/templates/vector/basic.html.j2 index d0ab6783b..41ad871ce 100644 --- a/cartoframes/assets/templates/vector/basic.html.j2 +++ b/cartoframes/assets/templates/vector/basic.html.j2 @@ -83,7 +83,7 @@
{% if has_legends %} {% include 'vector/legends.html.j2' %} - {% endif %} + {% endif %}
diff --git a/cartoframes/assets/templates/vector/legends.html.j2 b/cartoframes/assets/templates/vector/legends.html.j2 index 0591b6980..4327585ba 100644 --- a/cartoframes/assets/templates/vector/legends.html.j2 +++ b/cartoframes/assets/templates/vector/legends.html.j2 @@ -1,6 +1,6 @@
{% if basic_legend != none %} - + diff --git a/cartoframes/assets/templates/vector/scripts/legends.js.j2 b/cartoframes/assets/templates/vector/scripts/legends.js.j2 index 8028c014c..a7aa3d5e0 100644 --- a/cartoframes/assets/templates/vector/scripts/legends.js.j2 +++ b/cartoframes/assets/templates/vector/scripts/legends.js.j2 @@ -1,11 +1,12 @@ function createDefaultLegend(layers) { + const defaultLegendContainer = document.querySelector('#basicLegendContainer'); + defaultLegendContainer.style.display = 'none'; + AsBridge.VL.Legends.layersLegend( '#basicLegend', - layers.map(layer => ({ - layer: layer, - props: { - label: layer.id - } - })) + layers, + { + onLoad: () => defaultLegendContainer.style.display = 'unset' + } ) } \ No newline at end of file From 65fc53afac7785513a0dbbd7b678a5ac0ec0890c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rom=C3=A1n=20Jim=C3=A9nez?= Date: Mon, 20 May 2019 19:21:35 +0200 Subject: [PATCH 04/20] legends generation api --- .../assets/templates/vector/legends.html.j2 | 29 ++++++++++++++++++- .../templates/vector/scripts/legends.js.j2 | 14 +++++++++ .../assets/templates/vector/scripts/map.js.j2 | 4 +++ 3 files changed, 46 insertions(+), 1 deletion(-) diff --git a/cartoframes/assets/templates/vector/legends.html.j2 b/cartoframes/assets/templates/vector/legends.html.j2 index 4327585ba..e73ce7064 100644 --- a/cartoframes/assets/templates/vector/legends.html.j2 +++ b/cartoframes/assets/templates/vector/legends.html.j2 @@ -1,8 +1,35 @@ +{% macro legend(legend_data, id) -%} + {% set type = legend_data.type %} + {% if type == 'basic' %} + + {% elif type == 'gradient' %} + + {% elif type == 'color_steps' %} + + {% else %} +
+

Unknown legend type {{ type }}

+
+ {% endif %} +{%- endmacro %} +
- {% if basic_legend != none %} + {% if default_legend != none %} {% endif %} + + {% for source in sources %} + {% if source.legend %} + + {{ legend(source.legend, 'source%d_legend' % loop.index0) }} + + {% endif %} + {% endfor %}
\ No newline at end of file diff --git a/cartoframes/assets/templates/vector/scripts/legends.js.j2 b/cartoframes/assets/templates/vector/scripts/legends.js.j2 index a7aa3d5e0..7c2858539 100644 --- a/cartoframes/assets/templates/vector/scripts/legends.js.j2 +++ b/cartoframes/assets/templates/vector/scripts/legends.js.j2 @@ -9,4 +9,18 @@ function createDefaultLegend(layers) { onLoad: () => defaultLegendContainer.style.display = 'unset' } ) +} + +function createLegend(layer, legendData, layerIndex) { + const element = document.querySelector(`#source${layerIndex}_legend`); + + if (legendData.ramp) { + AsBridge.VL.Legends.rampLegend( + element, + layer, + legendData.ramp + ); + } else { + // TODO: we don't have a bridge for this case, should this even be a case? + } } \ No newline at end of file diff --git a/cartoframes/assets/templates/vector/scripts/map.js.j2 b/cartoframes/assets/templates/vector/scripts/map.js.j2 index 7092092c0..1a688653b 100644 --- a/cartoframes/assets/templates/vector/scripts/map.js.j2 +++ b/cartoframes/assets/templates/vector/scripts/map.js.j2 @@ -77,6 +77,10 @@ function onReady() { } } + if (elem.legend) { + createLegend(layer, elem.legend, idx); + } + function setPopupsClick(tempPopup, interactivity, popupHeader, values) { interactivity.off('featureHover', (event) => { updatePopup(tempPopup, event, popupHeader, values) From 0a7f0568616a5eca305883c32b981a11ca7682e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jes=C3=BAs=20Arroyo=20Torrens?= Date: Wed, 22 May 2019 11:20:47 +0200 Subject: [PATCH 05/20] Add color_category_layer helper --- cartoframes/viz/helpers/__init__.py | 22 + examples/debug/API/basemaps.ipynb | 2 +- examples/debug/API/helpers.ipynb | 728 ++++++++++++++++++++++++++++ 3 files changed, 751 insertions(+), 1 deletion(-) create mode 100644 cartoframes/viz/helpers/__init__.py create mode 100644 examples/debug/API/helpers.ipynb diff --git a/cartoframes/viz/helpers/__init__.py b/cartoframes/viz/helpers/__init__.py new file mode 100644 index 000000000..7ebdde0eb --- /dev/null +++ b/cartoframes/viz/helpers/__init__.py @@ -0,0 +1,22 @@ +from __future__ import absolute_import + +from ..layer import Layer + + +def color_category_layer(source, category, top=11, palette='bold'): + return Layer( + source, + style={ + 'point': { + 'color': 'ramp(top(${0}, {1}), {2})'.format(category, top, palette) + }, + 'line': { + 'color': 'ramp(top(${0}, {1}), {2})'.format(category, top, palette) + }, + 'polygon': { + 'color': 'opacity(ramp(top(${0}, {1}), {2}),0.9)'.format(category, top, palette) + } + }, + popup={}, + legend={} + ) diff --git a/examples/debug/API/basemaps.ipynb b/examples/debug/API/basemaps.ipynb index 8a65bc4a6..3b7135b37 100644 --- a/examples/debug/API/basemaps.ipynb +++ b/examples/debug/API/basemaps.ipynb @@ -7,7 +7,7 @@ "outputs": [], "source": [ "from cartoframes.auth import Context, set_default_context\n", - "from cartoframes.viz import Map, Layer, basemaps\n", + "from cartoframes.viz import Map, Layer, basemapsc\n", "\n", "# Context\n", "context = Context(\n", diff --git a/examples/debug/API/helpers.ipynb b/examples/debug/API/helpers.ipynb new file mode 100644 index 000000000..609081024 --- /dev/null +++ b/examples/debug/API/helpers.ipynb @@ -0,0 +1,728 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "from cartoframes.auth import Context, set_default_context\n", + "from cartoframes.viz import Map\n", + "\n", + "# Context\n", + "context = Context(\n", + " base_url='https://cartovl.carto.com/', \n", + " api_key='default_public'\n", + ")\n", + "set_default_context(context)" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "\n", + " \n", + " \n", + " CARTO VL + CARTOframes\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "\n", + " \n", + " \n", + "\n", + " \n", + " \n", + "
\n", + "
\n", + "
\n", + "
\n", + "
\n", + "
\n", + "
\n", + "
\n", + "
\n", + "
\n", + "
\n", + "
\n", + "\n", + "
\n", + "

There is a \n", + " from the CARTO VL library:

\n", + "
\n", + " \n", + " \n", + " \n", + "
\n", + "\n", + "
\n", + " StackTrace\n", + "
    \n", + "
    \n", + "
    \n", + "\n", + " \n", + " \n", + " \n", + "\">\n", + "\n", + "" + ], + "text/plain": [ + "" + ] + }, + "execution_count": 2, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from cartoframes.viz.helpers import color_category_layer\n", + "\n", + "# Simplest version\n", + "Map(\n", + " color_category_layer('populated_places', 'adm0name')\n", + ")\n", + "\n", + "# OR\n", + "\n", + "Map(\n", + " color_category_layer('populated_places', category='adm0name', top=11, palette='bold')\n", + ")" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.6.7" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} From 3827b7829f3095c99302c509f53883fb795abfef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jes=C3=BAs=20Arroyo=20Torrens?= Date: Wed, 22 May 2019 11:28:10 +0200 Subject: [PATCH 06/20] Add popup in color_category_layer --- cartoframes/viz/helpers/__init__.py | 11 +++++++++-- examples/debug/API/helpers.ipynb | 7 +++---- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/cartoframes/viz/helpers/__init__.py b/cartoframes/viz/helpers/__init__.py index 7ebdde0eb..3b68f53d1 100644 --- a/cartoframes/viz/helpers/__init__.py +++ b/cartoframes/viz/helpers/__init__.py @@ -17,6 +17,13 @@ def color_category_layer(source, category, top=11, palette='bold'): 'color': 'opacity(ramp(top(${0}, {1}), {2}),0.9)'.format(category, top, palette) } }, - popup={}, - legend={} + popup={ + 'hover': [{ + 'label': category, + 'value': '$' + category + }] + }, + legend={ + + } ) diff --git a/examples/debug/API/helpers.ipynb b/examples/debug/API/helpers.ipynb index 609081024..ff44a7d20 100644 --- a/examples/debug/API/helpers.ipynb +++ b/examples/debug/API/helpers.ipynb @@ -528,7 +528,7 @@ " dragRotate: false\n", " });\n", "\n", - " const sources = [{"credentials": {"api_key": "default_public", "base_url": "https://cartovl.carto.com", "username": "cartovl"}, "interactivity": [], "legend": {}, "query": "SELECT * FROM \\"public\\".\\"populated_places\\"", "type": "Query", "viz": "color: ramp(top($adm0name, 11), bold)\\nwidth: ramp(linear(zoom(),0,18),[2,10])\\nstrokeWidth: ramp(linear(zoom(),0,18),[0,1])\\nstrokeColor: opacity(#222,ramp(linear(zoom(),0,18),[0,1]))\\n"}];\n", + " const sources = [{"credentials": {"api_key": "default_public", "base_url": "https://cartovl.carto.com", "username": "cartovl"}, "interactivity": [{"attrs": [{"label": "adm0name", "name": "v58f267"}], "event": "hover"}], "legend": {}, "query": "SELECT * FROM \\"public\\".\\"populated_places\\"", "type": "Query", "viz": "@v58f267: $adm0name\\ncolor: ramp(top($adm0name, 11), bold)\\nwidth: ramp(linear(zoom(),0,18),[2,10])\\nstrokeWidth: ramp(linear(zoom(),0,18),[0,1])\\nstrokeColor: opacity(#222,ramp(linear(zoom(),0,18),[0,1]))\\n"}];\n", "\n", " map.fitBounds([[-179.589978884, -89.9828938613], [179.383303588, 82.4833231804]], {animate: false, padding: 50, maxZoom: 14});\n", "\n", @@ -680,7 +680,7 @@ "" ], "text/plain": [ - "" + "" ] }, "execution_count": 2, @@ -696,8 +696,7 @@ " color_category_layer('populated_places', 'adm0name')\n", ")\n", "\n", - "# OR\n", - "\n", + "# With all params\n", "Map(\n", " color_category_layer('populated_places', category='adm0name', top=11, palette='bold')\n", ")" From 4fcd4f344432d372d2d6c29bef8b69bf85c68949 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jes=C3=BAs=20Arroyo=20Torrens?= Date: Wed, 22 May 2019 11:34:16 +0200 Subject: [PATCH 07/20] Allow non list objects in Popup API --- cartoframes/viz/helpers/__init__.py | 2 +- cartoframes/viz/popup.py | 12 ++++++++++-- examples/debug/API/popup.ipynb | 14 +++++++------- test/viz/test_popup.py | 14 ++++++++++++++ 4 files changed, 32 insertions(+), 10 deletions(-) diff --git a/cartoframes/viz/helpers/__init__.py b/cartoframes/viz/helpers/__init__.py index 3b68f53d1..4c3b7ce78 100644 --- a/cartoframes/viz/helpers/__init__.py +++ b/cartoframes/viz/helpers/__init__.py @@ -24,6 +24,6 @@ def color_category_layer(source, category, top=11, palette='bold'): }] }, legend={ - + } ) diff --git a/cartoframes/viz/popup.py b/cartoframes/viz/popup.py index d98c21f11..a640cf5b5 100644 --- a/cartoframes/viz/popup.py +++ b/cartoframes/viz/popup.py @@ -64,9 +64,17 @@ def _init_popup(self, data): if isinstance(data, dict): # TODO: error control if 'click' in data: - self._click = data.get('click', []) + click_data = data.get('click', []) + if isinstance(click_data, list): + self._click = click_data + else: + self._click = [click_data] if 'hover' in data: - self._hover = data.get('hover', []) + hover_data = data.get('hover', []) + if isinstance(hover_data, list): + self._hover = hover_data + else: + self._hover = [hover_data] else: raise ValueError('Wrong popup input') diff --git a/examples/debug/API/popup.ipynb b/examples/debug/API/popup.ipynb index 85ab6a91e..42137af53 100644 --- a/examples/debug/API/popup.ipynb +++ b/examples/debug/API/popup.ipynb @@ -680,7 +680,7 @@ "" ], "text/plain": [ - "" + "" ] }, "execution_count": 2, @@ -707,7 +707,7 @@ " 'populated_places',\n", " 'color: blue',\n", " {\n", - " 'hover': ['$name'],\n", + " 'hover': '$name',\n", " 'click': ['$name', '$pop_max']\n", " }\n", " )\n", @@ -1377,7 +1377,7 @@ "" ], "text/plain": [ - "" + "" ] }, "execution_count": 3, @@ -2072,7 +2072,7 @@ "" ], "text/plain": [ - "" + "" ] }, "execution_count": 4, @@ -2108,10 +2108,10 @@ " 'populated_places',\n", " 'color: blue',\n", " {\n", - " 'hover': [{\n", + " 'hover': {\n", " 'label': 'Name',\n", " 'value': '$name'\n", - " }],\n", + " },\n", " 'click': [{\n", " 'label': 'Name',\n", " 'value': '$name'\n", @@ -2787,7 +2787,7 @@ "" ], "text/plain": [ - "" + "" ] }, "execution_count": 5, diff --git a/test/viz/test_popup.py b/test/viz/test_popup.py index 4dcf99dcc..0547685b2 100644 --- a/test/viz/test_popup.py +++ b/test/viz/test_popup.py @@ -23,6 +23,20 @@ def test_popup_init(self): 'value': '$pop' }]) + popup = Popup({ + 'click': '$pop', + 'hover': { + 'label': 'Pop', + 'value': '$pop' + } + }) + + self.assertEqual(popup._click, ['$pop']) + self.assertEqual(popup._hover, [{ + 'label': 'Pop', + 'value': '$pop' + }]) + def test_popup_interactivity(self): """Popup should return a proper interactivity object""" popup = Popup({ From 1359529fb66e34bd310ae6b813270bdd50fbc96f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jes=C3=BAs=20Arroyo=20Torrens?= Date: Wed, 22 May 2019 12:34:20 +0200 Subject: [PATCH 08/20] Add legend in color_category_layer helper --- cartoframes/viz/helpers/__init__.py | 5 +- cartoframes/viz/map.py | 2 +- examples/debug/API/helpers.ipynb | 77 +++++++++++++++++++++++++---- 3 files changed, 72 insertions(+), 12 deletions(-) diff --git a/cartoframes/viz/helpers/__init__.py b/cartoframes/viz/helpers/__init__.py index 4c3b7ce78..38e983bcb 100644 --- a/cartoframes/viz/helpers/__init__.py +++ b/cartoframes/viz/helpers/__init__.py @@ -24,6 +24,9 @@ def color_category_layer(source, category, top=11, palette='bold'): }] }, legend={ - + 'type': 'color_steps', + 'ramp': 'color', + 'heading': category, + 'description': '' } ) diff --git a/cartoframes/viz/map.py b/cartoframes/viz/map.py index 83241fbde..caf425b1b 100644 --- a/cartoframes/viz/map.py +++ b/cartoframes/viz/map.py @@ -140,7 +140,7 @@ def __init__(self, size=None, viewport=None, template=None, - default_legend=None + default_legend=None, **kwargs): self.layers = _init_layers(layers) diff --git a/examples/debug/API/helpers.ipynb b/examples/debug/API/helpers.ipynb index ff44a7d20..005d977a1 100644 --- a/examples/debug/API/helpers.ipynb +++ b/examples/debug/API/helpers.ipynb @@ -47,10 +47,10 @@ " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", "\n", " \n", + " \n", + "\n", + " \n", + " \n", + "
    \n", + "
    \n", + "
    \n", + "
    \n", + "
    \n", + " \n", + " \n", + "\n", + "
    \n", + " \n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
    \n", + " \n", + "
    \n", + "
    \n", + "
    \n", + "
    \n", + "
    \n", + "\n", + "
    \n", + "

    There is a \n", + " from the CARTO VL library:

    \n", + "
    \n", + " \n", + " \n", + " \n", + "
    \n", + "\n", + "
    \n", + " StackTrace\n", + "
      \n", + "
      \n", + "
      \n", + "\n", + " \n", + " \n", + " \n", + "\">\n", + "\n", + "" + ], + "text/plain": [ + "" + ] + }, + "execution_count": 3, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from cartoframes.viz.helpers import color_bins_layer\n", + "\n", + "# Simplest version\n", + "Map(\n", + " color_bins_layer('populated_places', 'pop_max')\n", + ")\n", + "\n", + "# With all params\n", + "Map(\n", + " color_bins_layer('populated_places', number='pop_max', bins=5, palette='reverse(purpor)')\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, "outputs": [ { "name": "stdout", From 6a8c5d9f6333a2d4efb41f8054d36c20d8c362fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jes=C3=BAs=20Arroyo=20Torrens?= Date: Wed, 22 May 2019 16:42:55 +0200 Subject: [PATCH 12/20] Add label to helper methods --- cartoframes/viz/helpers/__init__.py | 12 ++++++------ examples/debug/API/helpers.ipynb | 22 +++++++++++----------- 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/cartoframes/viz/helpers/__init__.py b/cartoframes/viz/helpers/__init__.py index 7d44cbc4d..1669144e7 100644 --- a/cartoframes/viz/helpers/__init__.py +++ b/cartoframes/viz/helpers/__init__.py @@ -9,7 +9,7 @@ def inspect(helper): print(lines) -def color_category_layer(source, category, top=11, palette='bold'): +def color_category_layer(source, category, top=11, palette='bold', label=''): return Layer( source, style={ @@ -25,20 +25,20 @@ def color_category_layer(source, category, top=11, palette='bold'): }, popup={ 'hover': [{ - 'label': category, + 'label': label or category, 'value': '$' + category }] }, legend={ 'type': 'basic', 'ramp': 'color', - 'heading': category, + 'heading': label or category, 'description': '' } ) -def color_bins_layer(source, number, bins=5, palette='reverse(purpor)'): +def color_bins_layer(source, number, bins=5, palette='reverse(purpor)', label=''): return Layer( source, style={ @@ -54,14 +54,14 @@ def color_bins_layer(source, number, bins=5, palette='reverse(purpor)'): }, popup={ 'hover': [{ - 'label': number, + 'label': label or number, 'value': '$' + number }] }, legend={ 'type': 'basic', 'ramp': 'color', - 'heading': number, + 'heading': label or number, 'description': '' } ) diff --git a/examples/debug/API/helpers.ipynb b/examples/debug/API/helpers.ipynb index a6722be55..48b254da4 100644 --- a/examples/debug/API/helpers.ipynb +++ b/examples/debug/API/helpers.ipynb @@ -146,7 +146,7 @@ " \n", " \n", " \n", @@ -576,7 +576,7 @@ " dragRotate: false\n", " });\n", "\n", - " const sources = [{"credentials": {"api_key": "default_public", "base_url": "https://cartovl.carto.com", "username": "cartovl"}, "interactivity": [{"attrs": [{"label": "adm0name", "name": "v58f267"}], "event": "hover"}], "legend": {"description": "", "heading": "adm0name", "ramp": "color", "type": "basic"}, "query": "SELECT * FROM \\"public\\".\\"populated_places\\"", "type": "Query", "viz": "@v58f267: $adm0name\\ncolor: ramp(top($adm0name, 11), bold)\\nwidth: ramp(linear(zoom(),0,18),[2,10])\\nstrokeWidth: ramp(linear(zoom(),0,18),[0,1])\\nstrokeColor: opacity(#222,ramp(linear(zoom(),0,18),[0,1]))\\n"}];\n", + " const sources = [{"credentials": {"api_key": "default_public", "base_url": "https://cartovl.carto.com", "username": "cartovl"}, "interactivity": [{"attrs": [{"label": "Country", "name": "v58f267"}], "event": "hover"}], "legend": {"description": "", "heading": "Country", "ramp": "color", "type": "basic"}, "query": "SELECT * FROM \\"public\\".\\"populated_places\\"", "type": "Query", "viz": "@v58f267: $adm0name\\ncolor: ramp(top($adm0name, 11), bold)\\nwidth: ramp(linear(zoom(),0,18),[2,10])\\nstrokeWidth: ramp(linear(zoom(),0,18),[0,1])\\nstrokeColor: opacity(#222,ramp(linear(zoom(),0,18),[0,1]))\\n"}];\n", "\n", " map.fitBounds([[-179.589978884, -89.9828938613], [179.383303588, 82.4833231804]], {animate: false, padding: 50, maxZoom: 14});\n", "\n", @@ -738,7 +738,7 @@ "" ], "text/plain": [ - "" + "" ] }, "execution_count": 2, @@ -756,7 +756,7 @@ "\n", "# With all params\n", "Map(\n", - " color_category_layer('populated_places', category='adm0name', top=11, palette='bold')\n", + " color_category_layer('populated_places', category='adm0name', top=11, palette='bold', label='Country')\n", ")" ] }, @@ -889,7 +889,7 @@ " \n", " \n", " \n", @@ -1319,7 +1319,7 @@ " dragRotate: false\n", " });\n", "\n", - " const sources = [{"credentials": {"api_key": "default_public", "base_url": "https://cartovl.carto.com", "username": "cartovl"}, "interactivity": [{"attrs": [{"label": "pop_max", "name": "vfdf67e"}], "event": "hover"}], "legend": {"description": "", "heading": "pop_max", "ramp": "color", "type": "basic"}, "query": "SELECT * FROM \\"public\\".\\"populated_places\\"", "type": "Query", "viz": "@vfdf67e: $pop_max\\ncolor: ramp(globalQuantiles($pop_max,5), reverse(purpor))\\nwidth: ramp(linear(zoom(),0,18),[2,10])\\nstrokeWidth: ramp(linear(zoom(),0,18),[0,1])\\nstrokeColor: opacity(#222,ramp(linear(zoom(),0,18),[0,1]))\\n"}];\n", + " const sources = [{"credentials": {"api_key": "default_public", "base_url": "https://cartovl.carto.com", "username": "cartovl"}, "interactivity": [{"attrs": [{"label": "Pop max", "name": "vfdf67e"}], "event": "hover"}], "legend": {"description": "", "heading": "Pop max", "ramp": "color", "type": "basic"}, "query": "SELECT * FROM \\"public\\".\\"populated_places\\"", "type": "Query", "viz": "@vfdf67e: $pop_max\\ncolor: ramp(globalQuantiles($pop_max,5), reverse(purpor))\\nwidth: ramp(linear(zoom(),0,18),[2,10])\\nstrokeWidth: ramp(linear(zoom(),0,18),[0,1])\\nstrokeColor: opacity(#222,ramp(linear(zoom(),0,18),[0,1]))\\n"}];\n", "\n", " map.fitBounds([[-179.589978884, -89.9828938613], [179.383303588, 82.4833231804]], {animate: false, padding: 50, maxZoom: 14});\n", "\n", @@ -1481,7 +1481,7 @@ "" ], "text/plain": [ - "" + "" ] }, "execution_count": 3, @@ -1499,7 +1499,7 @@ "\n", "# With all params\n", "Map(\n", - " color_bins_layer('populated_places', number='pop_max', bins=5, palette='reverse(purpor)')\n", + " color_bins_layer('populated_places', number='pop_max', bins=5, palette='reverse(purpor)', label='Pop max')\n", ")" ] }, @@ -1512,7 +1512,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "def color_category_layer(source, category, top=11, palette='bold'):\n", + "def color_category_layer(source, category, top=11, palette='bold', label=''):\n", " return Layer(\n", " source,\n", " style={\n", @@ -1528,14 +1528,14 @@ " },\n", " popup={\n", " 'hover': [{\n", - " 'label': category,\n", + " 'label': label or category,\n", " 'value': '$' + category\n", " }]\n", " },\n", " legend={\n", " 'type': 'basic',\n", " 'ramp': 'color',\n", - " 'heading': category,\n", + " 'heading': label or category,\n", " 'description': ''\n", " }\n", " )\n", From f8056c5b8bf1afc8e809bdd81276574fee3b06c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jes=C3=BAs=20Arroyo=20Torrens?= Date: Wed, 22 May 2019 16:49:36 +0200 Subject: [PATCH 13/20] Add each helper function in a new file --- cartoframes/viz/helpers/__init__.py | 63 ++----------------- cartoframes/viz/helpers/color_bins_layer.py | 32 ++++++++++ .../viz/helpers/color_category_layer.py | 32 ++++++++++ 3 files changed, 70 insertions(+), 57 deletions(-) create mode 100644 cartoframes/viz/helpers/color_bins_layer.py create mode 100644 cartoframes/viz/helpers/color_category_layer.py diff --git a/cartoframes/viz/helpers/__init__.py b/cartoframes/viz/helpers/__init__.py index 1669144e7..acf3b9b35 100644 --- a/cartoframes/viz/helpers/__init__.py +++ b/cartoframes/viz/helpers/__init__.py @@ -1,6 +1,7 @@ from __future__ import absolute_import -from ..layer import Layer +from .color_category_layer import color_category_layer +from .color_bins_layer import color_bins_layer def inspect(helper): @@ -9,59 +10,7 @@ def inspect(helper): print(lines) -def color_category_layer(source, category, top=11, palette='bold', label=''): - return Layer( - source, - style={ - 'point': { - 'color': 'ramp(top(${0}, {1}), {2})'.format(category, top, palette) - }, - 'line': { - 'color': 'ramp(top(${0}, {1}), {2})'.format(category, top, palette) - }, - 'polygon': { - 'color': 'opacity(ramp(top(${0}, {1}), {2}),0.9)'.format(category, top, palette) - } - }, - popup={ - 'hover': [{ - 'label': label or category, - 'value': '$' + category - }] - }, - legend={ - 'type': 'basic', - 'ramp': 'color', - 'heading': label or category, - 'description': '' - } - ) - - -def color_bins_layer(source, number, bins=5, palette='reverse(purpor)', label=''): - return Layer( - source, - style={ - 'point': { - 'color': 'ramp(globalQuantiles(${0},{1}), {2})'.format(number, bins, palette) - }, - 'line': { - 'color': 'ramp(globalQuantiles(${0},{1}), {2})'.format(number, bins, palette) - }, - 'polygon': { - 'color': 'opacity(ramp(globalQuantiles(${0},{1}), {2}),0.9)'.format(number, bins, palette) - } - }, - popup={ - 'hover': [{ - 'label': label or number, - 'value': '$' + number - }] - }, - legend={ - 'type': 'basic', - 'ramp': 'color', - 'heading': label or number, - 'description': '' - } - ) +__all__ = [ + 'color_category_layer', + 'color_bins_layer' +] diff --git a/cartoframes/viz/helpers/color_bins_layer.py b/cartoframes/viz/helpers/color_bins_layer.py new file mode 100644 index 000000000..6f9122401 --- /dev/null +++ b/cartoframes/viz/helpers/color_bins_layer.py @@ -0,0 +1,32 @@ +from __future__ import absolute_import + +from ..layer import Layer + + +def color_bins_layer(source, number, bins=5, palette='reverse(purpor)', label=''): + return Layer( + source, + style={ + 'point': { + 'color': 'ramp(globalQuantiles(${0},{1}), {2})'.format(number, bins, palette) + }, + 'line': { + 'color': 'ramp(globalQuantiles(${0},{1}), {2})'.format(number, bins, palette) + }, + 'polygon': { + 'color': 'opacity(ramp(globalQuantiles(${0},{1}), {2}),0.9)'.format(number, bins, palette) + } + }, + popup={ + 'hover': [{ + 'label': label or number, + 'value': '$' + number + }] + }, + legend={ + 'type': 'basic', + 'ramp': 'color', + 'heading': label or number, + 'description': '' + } + ) diff --git a/cartoframes/viz/helpers/color_category_layer.py b/cartoframes/viz/helpers/color_category_layer.py new file mode 100644 index 000000000..2ed2084ad --- /dev/null +++ b/cartoframes/viz/helpers/color_category_layer.py @@ -0,0 +1,32 @@ +from __future__ import absolute_import + +from ..layer import Layer + + +def color_category_layer(source, category, top=11, palette='bold', label=''): + return Layer( + source, + style={ + 'point': { + 'color': 'ramp(top(${0}, {1}), {2})'.format(category, top, palette) + }, + 'line': { + 'color': 'ramp(top(${0}, {1}), {2})'.format(category, top, palette) + }, + 'polygon': { + 'color': 'opacity(ramp(top(${0}, {1}), {2}),0.9)'.format(category, top, palette) + } + }, + popup={ + 'hover': [{ + 'label': label or category, + 'value': '$' + category + }] + }, + legend={ + 'type': 'basic', + 'ramp': 'color', + 'heading': label or category, + 'description': '' + } + ) From f29c48d027edbf33eaa9cb9fe88541ddef64207e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jes=C3=BAs=20Arroyo=20Torrens?= Date: Wed, 22 May 2019 18:55:03 +0200 Subject: [PATCH 14/20] Add more helper examples --- cartoframes/viz/helpers/color_bins_layer.py | 18 +- .../viz/helpers/color_category_layer.py | 18 +- examples/debug/API/helpers.ipynb | 789 +++++++++++++++++- 3 files changed, 784 insertions(+), 41 deletions(-) diff --git a/cartoframes/viz/helpers/color_bins_layer.py b/cartoframes/viz/helpers/color_bins_layer.py index 6f9122401..4162691d2 100644 --- a/cartoframes/viz/helpers/color_bins_layer.py +++ b/cartoframes/viz/helpers/color_bins_layer.py @@ -3,30 +3,30 @@ from ..layer import Layer -def color_bins_layer(source, number, bins=5, palette='reverse(purpor)', label=''): +def color_bins_layer(source, value, bins=5, palette='purpor', title=''): return Layer( source, style={ 'point': { - 'color': 'ramp(globalQuantiles(${0},{1}), {2})'.format(number, bins, palette) + 'color': 'ramp(globalQuantiles(${0},{1}),reverse({2}))'.format(value, bins, palette) }, 'line': { - 'color': 'ramp(globalQuantiles(${0},{1}), {2})'.format(number, bins, palette) + 'color': 'ramp(globalQuantiles(${0},{1}),reverse({2}))'.format(value, bins, palette) }, 'polygon': { - 'color': 'opacity(ramp(globalQuantiles(${0},{1}), {2}),0.9)'.format(number, bins, palette) + 'color': 'opacity(ramp(globalQuantiles(${0},{1}),reverse({2})),0.9)'.format(value, bins, palette) } }, popup={ - 'hover': [{ - 'label': label or number, - 'value': '$' + number - }] + 'hover': { + 'label': title or value, + 'value': '$' + value + } }, legend={ 'type': 'basic', 'ramp': 'color', - 'heading': label or number, + 'heading': title or value, 'description': '' } ) diff --git a/cartoframes/viz/helpers/color_category_layer.py b/cartoframes/viz/helpers/color_category_layer.py index 2ed2084ad..877fe9b96 100644 --- a/cartoframes/viz/helpers/color_category_layer.py +++ b/cartoframes/viz/helpers/color_category_layer.py @@ -3,30 +3,30 @@ from ..layer import Layer -def color_category_layer(source, category, top=11, palette='bold', label=''): +def color_category_layer(source, value, top=11, palette='bold', title=''): return Layer( source, style={ 'point': { - 'color': 'ramp(top(${0}, {1}), {2})'.format(category, top, palette) + 'color': 'ramp(top(${0}, {1}), {2})'.format(value, top, palette) }, 'line': { - 'color': 'ramp(top(${0}, {1}), {2})'.format(category, top, palette) + 'color': 'ramp(top(${0}, {1}), {2})'.format(value, top, palette) }, 'polygon': { - 'color': 'opacity(ramp(top(${0}, {1}), {2}),0.9)'.format(category, top, palette) + 'color': 'opacity(ramp(top(${0}, {1}), {2}),0.9)'.format(value, top, palette) } }, popup={ - 'hover': [{ - 'label': label or category, - 'value': '$' + category - }] + 'hover': { + 'label': title or value, + 'value': '$' + value + } }, legend={ 'type': 'basic', 'ramp': 'color', - 'heading': label or category, + 'heading': title or value, 'description': '' } ) diff --git a/examples/debug/API/helpers.ipynb b/examples/debug/API/helpers.ipynb index 48b254da4..801c4b23e 100644 --- a/examples/debug/API/helpers.ipynb +++ b/examples/debug/API/helpers.ipynb @@ -11,7 +11,7 @@ "\n", "# Context\n", "context = Context(\n", - " base_url='https://cartovl.carto.com/', \n", + " base_url='https://cartovl.carto.com/',\n", " api_key='default_public'\n", ")\n", "set_default_context(context)" @@ -146,7 +146,7 @@ " \n", " \n", " \n", @@ -576,9 +576,752 @@ " dragRotate: false\n", " });\n", "\n", - " const sources = [{"credentials": {"api_key": "default_public", "base_url": "https://cartovl.carto.com", "username": "cartovl"}, "interactivity": [{"attrs": [{"label": "Country", "name": "v58f267"}], "event": "hover"}], "legend": {"description": "", "heading": "Country", "ramp": "color", "type": "basic"}, "query": "SELECT * FROM \\"public\\".\\"populated_places\\"", "type": "Query", "viz": "@v58f267: $adm0name\\ncolor: ramp(top($adm0name, 11), bold)\\nwidth: ramp(linear(zoom(),0,18),[2,10])\\nstrokeWidth: ramp(linear(zoom(),0,18),[0,1])\\nstrokeColor: opacity(#222,ramp(linear(zoom(),0,18),[0,1]))\\n"}];\n", + " const sources = [{"credentials": {"api_key": "default_public", "base_url": "https://cartovl.carto.com", "username": "cartovl"}, "interactivity": [{"attrs": [{"label": "Neighborhoods", "name": "v8e0f74"}], "event": "hover"}], "legend": {"description": "", "heading": "Neighborhoods", "ramp": "color", "type": "basic"}, "query": "SELECT * FROM \\"public\\".\\"sf_neighborhoods\\"", "type": "Query", "viz": "@v8e0f74: $name\\ncolor: ramp(top($name, 11), bold)\\nstrokeWidth: ramp(linear(zoom(),2,18),[0.5,1])\\nstrokeColor: opacity(#2c2c2c,ramp(linear(zoom(),2,18),[0.2,0.6]))\\n"}];\n", "\n", - " map.fitBounds([[-179.589978884, -89.9828938613], [179.383303588, 82.4833231804]], {animate: false, padding: 50, maxZoom: 14});\n", + " map.fitBounds([[-122.514947579485, 37.7080892080235], [-122.356966876637, 37.8332976403531]], {animate: false, padding: 50, maxZoom: 14});\n", + "\n", + " \n", + "\n", + " const layers = [];\n", + "\n", + " sources.forEach((elem, idx) => {\n", + " const factory = new SourceFactory();\n", + " const source = factory.createSource(elem);\n", + " const viz = new carto.Viz(elem['viz']);\n", + " const layer = new carto.Layer(`layer${idx}`, source, viz);\n", + "\n", + " layers.push(layer);\n", + "\n", + " try {\n", + " layer._updateLayer.catch(displayError);\n", + " } catch (err) {\n", + " throw err;\n", + " }\n", + "\n", + " layer.addTo(map);\n", + "\n", + " if (elem.interactivity) {\n", + " const interactivity = new carto.Interactivity(layer);\n", + " const tempPopup = new mapboxgl.Popup({\n", + " closeButton: false,\n", + " closeOnClick: false\n", + " });\n", + "\n", + " interactivity.off('featureHover');\n", + " interactivity.off('featureClick');\n", + "\n", + " for (const { event, attrs } of elem.interactivity) {\n", + " if (event === 'click') {\n", + " setPopupsClick(tempPopup, interactivity, attrs);\n", + " }\n", + " if (event === 'hover') {\n", + " setPopupsHover(tempPopup, interactivity, attrs);\n", + " }\n", + " }\n", + " }\n", + "\n", + " if (elem.legend) {\n", + " createLegend(layer, elem.legend, idx);\n", + " }\n", + "\n", + " function setPopupsClick(tempPopup, interactivity, attrs) {\n", + " interactivity.on('featureClick', (event) => {\n", + " updatePopup(tempPopup, event, attrs)\n", + " });\n", + " }\n", + " \n", + " function setPopupsHover(tempPopup, interactivity, attrs) {\n", + " interactivity.on('featureHover', (event) => {\n", + " updatePopup(tempPopup, event, attrs)\n", + " });\n", + " }\n", + "\n", + " function updatePopup(layer_popup, event, attrs) {\n", + " if (event.features.length > 0) {\n", + " const feature = event.features[0];\n", + "\n", + " let popupHTML = '';\n", + " \n", + " attrs.forEach((item) => {\n", + " let value = feature.variables[item.name].value;\n", + " if (typeof value === 'number') {\n", + " if (Math.abs(value) > 1000) {\n", + " value = value.toExponential(2);\n", + " } else if (!Number.isInteger(value)) {\n", + " value = value.toFixed(2);\n", + " }\n", + " }\n", + " popupHTML += `\n", + " ${item.label}\n", + " ${value}\n", + " `;\n", + " });\n", + " \n", + " layer_popup\n", + " .setLngLat([event.coordinates.lng, event.coordinates.lat])\n", + " .setHTML(`
      ${popupHTML}
      `);\n", + " \n", + " if (!layer_popup.isOpen()) {\n", + " layer_popup.addTo(map);\n", + " }\n", + " } else {\n", + " layer_popup.remove();\n", + " }\n", + " }\n", + "\n", + " function SourceFactory() {\n", + " const sourceTypes = {\n", + " GeoJSON: (elem) => new carto.source.GeoJSON(_decodeJSONQuery(elem.query)),\n", + " Query: (elem) => {\n", + " const auth = {\n", + " username: elem.credentials['username'],\n", + " apiKey: elem.credentials['api_key'] || 'default_public'\n", + " };\n", + " const config = {\n", + " serverURL: elem.credentials['base_url'] || `https://${elem.credentials['username']}.carto.com/`\n", + " };\n", + " return new carto.source.SQL(elem.query, auth, config)\n", + " },\n", + " MVT: (elem) => new carto.source.MVT(elem.query.file, JSON.parse(elem.query.metadata)),\n", + " }\n", + "\n", + " this.createSource = (elem) => {\n", + " return sourceTypes[elem.type](elem);\n", + " }\n", + " }\n", + " });\n", + "\n", + " \n", + "}\n", + "\n", + "function setReady () {\n", + " try {\n", + " onReady()\n", + " } catch (e) {\n", + " displayError(e);\n", + " }\n", + "}\n", + "\n", + "function displayError(e) {\n", + " const error$ = document.getElementById('error-container');\n", + " const errors$ = error$.getElementsByClassName('errors');\n", + " const stacktrace$ = document.getElementById('error-stacktrace');\n", + "\n", + " errors$[0].innerHTML = e.name;\n", + " errors$[1].innerHTML = e.name;\n", + " errors$[2].innerHTML = e.type;\n", + " errors$[3].innerHTML = e.message.replace(e.type, '');\n", + "\n", + " error$.style.visibility = 'visible';\n", + "\n", + " const stack = parse(e.stack);\n", + " const list = stack.map(item => {\n", + " return `
    • \n", + " at ${item.methodName}:\n", + " (${item.file}:${item.lineNumber}:${item.column})\n", + "
    • `;\n", + " });\n", + "\n", + " stacktrace$.innerHTML = list.join('\\n');\n", + "}\n", + "\n", + "function _decodeJSONQuery(query) {\n", + " return JSON.parse(Base64.decode(query.replace(/b\\'/, '\\'')))\n", + "}\n", + "\n", + "responsive.addEventListener('ready', setReady);\n", + " \n", + " \n", + " \n", + "\">\n", + "\n", + "" + ], + "text/plain": [ + "" + ] + }, + "execution_count": 2, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from cartoframes.viz.helpers import color_category_layer\n", + "\n", + "# Simplest version\n", + "Map(\n", + " color_category_layer('sf_neighborhoods', 'name')\n", + ")\n", + "\n", + "# With all params\n", + "Map(\n", + " color_category_layer('sf_neighborhoods', 'name', top=11, palette='bold', title='Neighborhoods')\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "\n", + " \n", + " \n", + " CARTO VL + CARTOframes\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "\n", + " \n", + " \n", + "\n", + " \n", + " \n", + "
      \n", + "
      \n", + "
      \n", + "
      \n", + "
      \n", + " \n", + " \n", + "\n", + "
      \n", + " \n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
      \n", + " \n", + "
      \n", + "
      \n", + "
      \n", + "
      \n", + "
      \n", + "\n", + "
      \n", + "

      There is a \n", + " from the CARTO VL library:

      \n", + "
      \n", + " \n", + " \n", + " \n", + "
      \n", + "\n", + "
      \n", + " StackTrace\n", + "
        \n", + "
        \n", + "
        \n", + "\n", + " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", "\n", " \n", - " \n", - "\n", - " \n", - " \n", - "
        \n", - "
        \n", - "
        \n", - "
        \n", - "
        \n", - "
        \n", - "
        \n", - "
        \n", - "
        \n", - "
        \n", - "
        \n", - "
        \n", - "\n", - "
        \n", - "

        There is a \n", - " from the CARTO VL library:

        \n", - "
        \n", - " \n", - " \n", - " \n", - "
        \n", - "\n", - "
        \n", - " StackTrace\n", - "
          \n", - "
          \n", - "
          \n", - "\n", - " \n", - " \n", - " \n", - "\">\n", - "\n", - "" - ], - "text/plain": [ - "" - ] - }, - "execution_count": 5, - "metadata": {}, - "output_type": "execute_result" - } - ], "source": [ "Map(Layer('populated_places'), basemap='#fabada')" ] From 347e6d8b98a604994d7cad3046924e6eda996133 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jes=C3=BAs=20Arroyo=20Torrens?= Date: Thu, 23 May 2019 11:05:22 +0200 Subject: [PATCH 17/20] Improve style props detection --- cartoframes/viz/style.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/cartoframes/viz/style.py b/cartoframes/viz/style.py index 1471073f6..c2301c827 100644 --- a/cartoframes/viz/style.py +++ b/cartoframes/viz/style.py @@ -1,5 +1,7 @@ from __future__ import absolute_import +import re + from .defaults import STYLE_DEFAULTS, STYLE_PROPERTIES from ..utils import merge_dicts @@ -116,12 +118,12 @@ def _serialize_properties(self, properties={}): def _prune_defaults(self, defaults, style): output = defaults.copy() - if 'color:' in style: + if re.match(r'color\s*:', style): del output['color'] - if 'width:' in style: + if re.match(r'width\s*:', style): del output['width'] - if 'strokeWidth:' in style: + if re.match(r'strokeWidth\s*:', style): del output['strokeWidth'] - if 'strokeColor:' in style: + if re.match(r'strokeColor\s*:', style): del output['strokeColor'] return output From 221266d0dc472fee157c46d367c2413eda6aa414 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jes=C3=BAs=20Arroyo=20Torrens?= Date: Thu, 23 May 2019 12:09:39 +0200 Subject: [PATCH 18/20] Update NEWS --- NEWS.rst | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/NEWS.rst b/NEWS.rst index 5e0f29fd9..0d4a88c5f 100644 --- a/NEWS.rst +++ b/NEWS.rst @@ -13,26 +13,30 @@ Updates - Add new visualization API (#662) - Add Source class (with param detection) - Add Dataset methods - .from_table(name, context) - .from_query(query, context) - .from_geojson(data) - .from_dataframe(data) - - Add set_default_context methods - - Add Context in cartoframes namespace - - Add Map, Layer, Source, Style in cartoframes.viz namespace + .from_table(...) + .from_query(...) + .from_geojson(...) + .from_dataframe(...) + - Add set_default_context method - Use sources' context (credentials, bounds) - Fix Style class API for variables - Remove Dataset, SQL, GeoJSON sources - Remove sources namespace - Remove context from Map - Remove contrib namespace - - Update docs in vis classes - - Add/Update vis tests + - Update docs in viz classes + - Add/Update viz tests - Pass PEP 8 - Add default style, based on the geom type (#648) -- Add basemap None, bool and color interface (#635) +- Add basemap None and color interface (#635) - Add Popup API (click and hover) (#677) - Apply default style for not overwritten properties (#684) +- Add namespaces (#683) + - cartoframes.viz: Map, Layer, Source, Style, Popup, basemaps, helpers + - cartoframes.auth: Context, set_default_context +- Add color helpers (#692) + - color_category + - color_bins 0.9.2 ----- From 463bffa15e7b6b75e885fc09534c25b7a40e1f67 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jes=C3=BAs=20Arroyo=20Torrens?= Date: Thu, 23 May 2019 12:33:15 +0200 Subject: [PATCH 19/20] Fix style property match --- cartoframes/utils.py | 6 ++++++ cartoframes/viz/style.py | 12 ++++++------ 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/cartoframes/utils.py b/cartoframes/utils.py index acff7f418..27e437782 100644 --- a/cartoframes/utils.py +++ b/cartoframes/utils.py @@ -1,4 +1,6 @@ """general utility functions""" + +import re import sys import hashlib @@ -111,3 +113,7 @@ def merge_dicts(dict1, dict2): d = dict1.copy() d.update(dict2) return d + + +def text_match(regex, text): + return len(re.findall(regex, text, re.MULTILINE)) > 0 diff --git a/cartoframes/viz/style.py b/cartoframes/viz/style.py index c2301c827..2cdbe3c3c 100644 --- a/cartoframes/viz/style.py +++ b/cartoframes/viz/style.py @@ -3,7 +3,7 @@ import re from .defaults import STYLE_DEFAULTS, STYLE_PROPERTIES -from ..utils import merge_dicts +from ..utils import merge_dicts, text_match class Style(object): @@ -118,12 +118,12 @@ def _serialize_properties(self, properties={}): def _prune_defaults(self, defaults, style): output = defaults.copy() - if re.match(r'color\s*:', style): + if text_match(r'color\s*:', style): del output['color'] - if re.match(r'width\s*:', style): + if text_match(r'width\s*:', style): del output['width'] - if re.match(r'strokeWidth\s*:', style): - del output['strokeWidth'] - if re.match(r'strokeColor\s*:', style): + if text_match(r'strokeColor\s*:', style): del output['strokeColor'] + if text_match(r'strokeWidth\s*:', style): + del output['strokeWidth'] return output From 93619876a6e1274351f628680a8ebf185972d086 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jes=C3=BAs=20Arroyo=20Torrens?= Date: Thu, 23 May 2019 12:38:16 +0200 Subject: [PATCH 20/20] Fix linter --- cartoframes/__init__.py | 2 +- cartoframes/viz/style.py | 2 -- setup.py | 3 ++- test/test_utils.py | 2 -- 4 files changed, 3 insertions(+), 6 deletions(-) diff --git a/cartoframes/__init__.py b/cartoframes/__init__.py index d7b68e27e..10d8847d0 100644 --- a/cartoframes/__init__.py +++ b/cartoframes/__init__.py @@ -17,7 +17,7 @@ 'BinMethod', # New API - 'Dataset' + 'Dataset', '__version__' ] diff --git a/cartoframes/viz/style.py b/cartoframes/viz/style.py index 2cdbe3c3c..0d5b79648 100644 --- a/cartoframes/viz/style.py +++ b/cartoframes/viz/style.py @@ -1,7 +1,5 @@ from __future__ import absolute_import -import re - from .defaults import STYLE_DEFAULTS, STYLE_PROPERTIES from ..utils import merge_dicts, text_match diff --git a/setup.py b/setup.py index 1c0f2f2f4..008368ff1 100644 --- a/setup.py +++ b/setup.py @@ -17,6 +17,7 @@ def walk_subpkg(name): data_files.append(os.path.join(sub_dir, f)) return data_files + REQUIRES = [ 'pandas>=0.20.1', 'webcolors>=1.7.0', @@ -52,7 +53,7 @@ def walk_subpkg(name): about = {} with open(os.path.join(here, 'cartoframes', '__version__.py'), 'r', 'utf-8') as f: - exec(f.read(), about) + exec(f.read(), about) setup( name=about['__title__'], diff --git a/test/test_utils.py b/test/test_utils.py index d3b024fa6..c1f123dcc 100644 --- a/test/test_utils.py +++ b/test/test_utils.py @@ -4,8 +4,6 @@ import unittest from collections import OrderedDict -import pandas as pd - from cartoframes.utils import (dict_items, cssify, importify_params)