This is an automated email from the ASF dual-hosted git repository.
maximebeauchemin pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-superset.git
The following commit(s) were added to refs/heads/master by this push:
new e4903e6 [geo] add support for deck.gl's path layer (#4067)
e4903e6 is described below
commit e4903e6dc665aa0b4928031f6bf721884229bbe7
Author: Maxime Beauchemin <[email protected]>
AuthorDate: Tue Dec 19 12:38:03 2017 -0800
[geo] add support for deck.gl's path layer (#4067)
* [geo] add support for deck.gl's path layer
Works with json and polyline data.
* Lint
---
setup.py | 1 +
.../assets/images/viz_thumbnails/deck_path.png | Bin 0 -> 523094 bytes
.../assets/javascripts/explore/stores/controls.jsx | 38 +++++++++++++++
.../assets/javascripts/explore/stores/visTypes.js | 24 ++++++++++
superset/assets/visualizations/deckgl/path.jsx | 39 ++++++++++++++++
superset/assets/visualizations/main.js | 2 +
superset/cli.py | 3 ++
superset/data/__init__.py | 33 ++++++++++++-
superset/data/bart-lines.json.gz | Bin 0 -> 1267 bytes
superset/viz.py | 51 +++++++++++++++++----
10 files changed, 182 insertions(+), 9 deletions(-)
diff --git a/setup.py b/setup.py
index 8d0dba3..0ecd1ef 100644
--- a/setup.py
+++ b/setup.py
@@ -66,6 +66,7 @@ setup(
'pandas==0.20.3',
'parsedatetime==2.0.0',
'pathlib2==2.3.0',
+ 'polyline==1.3.2',
'pydruid==0.3.1',
'PyHive>=0.4.0',
'python-dateutil==2.6.0',
diff --git a/superset/assets/images/viz_thumbnails/deck_path.png
b/superset/assets/images/viz_thumbnails/deck_path.png
new file mode 100644
index 0000000..eede9da
Binary files /dev/null and
b/superset/assets/images/viz_thumbnails/deck_path.png differ
diff --git a/superset/assets/javascripts/explore/stores/controls.jsx
b/superset/assets/javascripts/explore/stores/controls.jsx
index 5c18b4d..7117712 100644
--- a/superset/assets/javascripts/explore/stores/controls.jsx
+++ b/superset/assets/javascripts/explore/stores/controls.jsx
@@ -1721,5 +1721,43 @@ export const controls = {
t('Partitions whose height to parent height proportions are ' +
'below this value are pruned'),
},
+
+ line_column: {
+ type: 'SelectControl',
+ label: t('Lines column'),
+ default: null,
+ description: t('The database columns that contains lines information'),
+ mapStateToProps: state => ({
+ choices: (state.datasource) ? state.datasource.all_cols : [],
+ }),
+ validators: [v.nonEmpty],
+ },
+ line_type: {
+ type: 'SelectControl',
+ label: t('Lines encoding'),
+ clearable: false,
+ default: 'json',
+ description: t('The encoding format of the lines'),
+ choices: [
+ ['polyline', 'Polyline'],
+ ['json', 'JSON'],
+ ],
+ },
+
+ line_width: {
+ type: 'TextControl',
+ label: t('Line width'),
+ renderTrigger: true,
+ isInt: true,
+ default: 10,
+ description: t('The width of the lines'),
+ },
+
+ reverse_long_lat: {
+ type: 'CheckboxControl',
+ label: t('Reverse Lat & Long'),
+ default: false,
+ },
+
};
export default controls;
diff --git a/superset/assets/javascripts/explore/stores/visTypes.js
b/superset/assets/javascripts/explore/stores/visTypes.js
index ef9dc41..2c5f2a6 100644
--- a/superset/assets/javascripts/explore/stores/visTypes.js
+++ b/superset/assets/javascripts/explore/stores/visTypes.js
@@ -397,6 +397,30 @@ export const visTypes = {
},
},
+ deck_path: {
+ label: t('Deck.gl - Grid'),
+ requiresTime: true,
+ controlPanelSections: [
+ {
+ label: t('Query'),
+ expanded: true,
+ controlSetRows: [
+ ['line_column', 'line_type'],
+ ['row_limit', null],
+ ],
+ },
+ {
+ label: t('Map'),
+ expanded: true,
+ controlSetRows: [
+ ['mapbox_style', 'viewport'],
+ ['color_picker', 'line_width'],
+ ['reverse_long_lat', null],
+ ],
+ },
+ ],
+ },
+
deck_screengrid: {
label: t('Deck.gl - Screen grid'),
requiresTime: true,
diff --git a/superset/assets/visualizations/deckgl/path.jsx
b/superset/assets/visualizations/deckgl/path.jsx
new file mode 100644
index 0000000..c814adc
--- /dev/null
+++ b/superset/assets/visualizations/deckgl/path.jsx
@@ -0,0 +1,39 @@
+import React from 'react';
+import ReactDOM from 'react-dom';
+import { PathLayer } from 'deck.gl';
+
+import DeckGLContainer from './DeckGLContainer';
+
+function deckPath(slice, payload, setControlValue) {
+ const fd = slice.formData;
+ const c = fd.color_picker;
+ const fixedColor = [c.r, c.g, c.b, 255 * c.a];
+ const data = payload.data.paths.map(path => ({
+ path,
+ width: fd.line_width,
+ color: fixedColor,
+ }));
+
+ const layer = new PathLayer({
+ id: `path-layer-${slice.containerId}`,
+ data,
+ rounded: true,
+ widthScale: 1,
+ });
+ const viewport = {
+ ...fd.viewport,
+ width: slice.width(),
+ height: slice.height(),
+ };
+ ReactDOM.render(
+ <DeckGLContainer
+ mapboxApiAccessToken={payload.data.mapboxApiKey}
+ viewport={viewport}
+ layers={[layer]}
+ mapStyle={fd.mapbox_style}
+ setControlValue={setControlValue}
+ />,
+ document.getElementById(slice.containerId),
+ );
+}
+module.exports = deckPath;
diff --git a/superset/assets/visualizations/main.js
b/superset/assets/visualizations/main.js
index fdcb7b1..06d30a1 100644
--- a/superset/assets/visualizations/main.js
+++ b/superset/assets/visualizations/main.js
@@ -42,6 +42,7 @@ export const VIZ_TYPES = {
deck_screengrid: 'deck_screengrid',
deck_grid: 'deck_grid',
deck_hex: 'deck_hex',
+ deck_path: 'deck_path',
};
const vizMap = {
@@ -86,5 +87,6 @@ const vizMap = {
[VIZ_TYPES.deck_screengrid]: require('./deckgl/screengrid.jsx'),
[VIZ_TYPES.deck_grid]: require('./deckgl/grid.jsx'),
[VIZ_TYPES.deck_hex]: require('./deckgl/hex.jsx'),
+ [VIZ_TYPES.deck_path]: require('./deckgl/path.jsx'),
};
export default vizMap;
diff --git a/superset/cli.py b/superset/cli.py
index 16500ac..56ead72 100755
--- a/superset/cli.py
+++ b/superset/cli.py
@@ -146,6 +146,9 @@ def load_examples(load_test_data):
print('Loading flights data')
data.load_flights()
+ print('Loading bart lines data')
+ data.load_bart_lines()
+
@manager.option(
'-d', '--datasource',
diff --git a/superset/data/__init__.py b/superset/data/__init__.py
index 742a32b..b3cb4a8 100644
--- a/superset/data/__init__.py
+++ b/superset/data/__init__.py
@@ -12,8 +12,9 @@ import random
import textwrap
import pandas as pd
-from sqlalchemy import BigInteger, Date, DateTime, Float, String
+from sqlalchemy import BigInteger, Date, DateTime, Float, String, Text
import geohash
+import polyline
from superset import app, db, utils
from superset.connectors.connector_registry import ConnectorRegistry
@@ -1519,3 +1520,33 @@ def load_flights():
db.session.merge(obj)
db.session.commit()
obj.fetch_metadata()
+
+
+def load_bart_lines():
+ tbl_name = 'bart_lines'
+ with gzip.open(os.path.join(DATA_FOLDER, 'bart-lines.json.gz')) as f:
+ df = pd.read_json(f, encoding='latin-1')
+ df['path_json'] = df.path.map(json.dumps)
+ df['polyline'] = df.path.map(polyline.encode)
+ del df['path']
+ df.to_sql(
+ tbl_name,
+ db.engine,
+ if_exists='replace',
+ chunksize=500,
+ dtype={
+ 'color': String(255),
+ 'name': String(255),
+ 'polyline': Text,
+ 'path_json': Text,
+ },
+ index=False)
+ print("Creating table {} reference".format(tbl_name))
+ tbl = db.session.query(TBL).filter_by(table_name=tbl_name).first()
+ if not tbl:
+ tbl = TBL(table_name=tbl_name)
+ tbl.description = "BART lines"
+ tbl.database = get_or_create_main_db()
+ db.session.merge(tbl)
+ db.session.commit()
+ tbl.fetch_metadata()
diff --git a/superset/data/bart-lines.json.gz b/superset/data/bart-lines.json.gz
new file mode 100644
index 0000000..91f50fb
Binary files /dev/null and b/superset/data/bart-lines.json.gz differ
diff --git a/superset/viz.py b/superset/viz.py
index 85bc854..55f2603 100644
--- a/superset/viz.py
+++ b/superset/viz.py
@@ -27,6 +27,7 @@ from markdown import markdown
import numpy as np
import pandas as pd
from pandas.tseries.frequencies import to_offset
+import polyline
import simplejson as json
from six import PY3, string_types, text_type
from six.moves import reduce
@@ -1796,13 +1797,14 @@ class BaseDeckGLViz(BaseViz):
gb = []
spatial = fd.get('spatial')
- if spatial.get('type') == 'latlong':
- gb += [spatial.get('lonCol')]
- gb += [spatial.get('latCol')]
- elif spatial.get('type') == 'delimited':
- gb += [spatial.get('lonlatCol')]
- elif spatial.get('type') == 'geohash':
- gb += [spatial.get('geohashCol')]
+ if spatial:
+ if spatial.get('type') == 'latlong':
+ gb += [spatial.get('lonCol')]
+ gb += [spatial.get('latCol')]
+ elif spatial.get('type') == 'delimited':
+ gb += [spatial.get('lonlatCol')]
+ elif spatial.get('type') == 'geohash':
+ gb += [spatial.get('geohashCol')]
if fd.get('dimension'):
gb += [fd.get('dimension')]
@@ -1863,8 +1865,10 @@ class DeckScatterViz(BaseDeckGLViz):
return super(DeckScatterViz, self).query_obj()
def get_metrics(self):
+ self.metric = None
if self.point_radius_fixed.get('type') == 'metric':
- return [self.point_radius_fixed.get('value')]
+ self.metric = self.point_radius_fixed.get('value')
+ return [self.metric]
return None
def get_properties(self, d):
@@ -1899,6 +1903,37 @@ class DeckGrid(BaseDeckGLViz):
verbose_name = _('Deck.gl - 3D Grid')
+class DeckPathViz(BaseDeckGLViz):
+
+ """deck.gl's PathLayer"""
+
+ viz_type = 'deck_path'
+ verbose_name = _('Deck.gl - Paths')
+ deser_map = {
+ 'json': json.loads,
+ 'polyline': polyline.decode,
+ }
+
+ def query_obj(self):
+ d = super(DeckPathViz, self).query_obj()
+ d['groupby'] = []
+ d['metrics'] = []
+ d['columns'] = [self.form_data.get('line_column')]
+ return d
+
+ def get_data(self, df):
+ fd = self.form_data
+ deser = self.deser_map[fd.get('line_type')]
+ paths = [deser(s) for s in df[fd.get('line_column')]]
+ if fd.get('reverse_long_lat'):
+ paths = [[(point[1], point[0]) for point in path] for path in
paths]
+ d = {
+ 'mapboxApiKey': config.get('MAPBOX_API_KEY'),
+ 'paths': paths,
+ }
+ return d
+
+
class DeckHex(BaseDeckGLViz):
"""deck.gl's DeckLayer"""
--
To stop receiving notification emails like this one, please contact
['"[email protected]" <[email protected]>'].