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 bca27b4 [Geo] Added DeckGL Arc Layer and Refactor on BaseDeckGL class
(#4134)
bca27b4 is described below
commit bca27b436b4fc22ce061aca645f42455fbc620f1
Author: Hugh A. Miles II <[email protected]>
AuthorDate: Fri Jan 12 11:06:11 2018 -0800
[Geo] Added DeckGL Arc Layer and Refactor on BaseDeckGL class (#4134)
* Added DeckGL.arc layer
* added color controls
* added stroke_width control
* added process spatial key methods
* change exception to ValueError
* put location into tuple
* reference global spatial keys array
* linting
* refactor on process_spatial_data_obj
* rm whitespace
* refactor arc.get_data
* Revert "refactor arc.get_data"
This reverts commit 8d01b2a22ed0cc7bff3b3e880cca54031b6ebe4d.
* add spatial controls array
* refactor on spatial keys again :)
* return altered df
* Working refactor with deckGL Arcs
* working arcs refactor :)
* refactored all other deckGL viz types
---
superset/assets/images/viz_thumbnails/deck_arc.png | Bin 0 -> 230107 bytes
.../assets/javascripts/explore/stores/controls.jsx | 29 +++++
.../assets/javascripts/explore/stores/visTypes.js | 28 +++++
.../assets/visualizations/deckgl/layers/arc.jsx | 16 +++
.../assets/visualizations/deckgl/layers/index.js | 2 +
superset/assets/visualizations/main.js | 2 +
superset/viz.py | 123 ++++++++++++++-------
7 files changed, 163 insertions(+), 37 deletions(-)
diff --git a/superset/assets/images/viz_thumbnails/deck_arc.png
b/superset/assets/images/viz_thumbnails/deck_arc.png
new file mode 100644
index 0000000..f79f283
Binary files /dev/null and b/superset/assets/images/viz_thumbnails/deck_arc.png
differ
diff --git a/superset/assets/javascripts/explore/stores/controls.jsx
b/superset/assets/javascripts/explore/stores/controls.jsx
index 2df229a..fbfc893 100644
--- a/superset/assets/javascripts/explore/stores/controls.jsx
+++ b/superset/assets/javascripts/explore/stores/controls.jsx
@@ -519,6 +519,26 @@ export const controls = {
}),
},
+ start_spatial: {
+ type: 'SpatialControl',
+ label: t('Start Longitude & Latitude'),
+ validators: [v.nonEmpty],
+ description: t('Point to your spatial columns'),
+ mapStateToProps: state => ({
+ choices: (state.datasource) ? state.datasource.all_cols : [],
+ }),
+ },
+
+ end_spatial: {
+ type: 'SpatialControl',
+ label: t('End Longitude & Latitude'),
+ validators: [v.nonEmpty],
+ description: t('Point to your spatial columns'),
+ mapStateToProps: state => ({
+ choices: (state.datasource) ? state.datasource.all_cols : [],
+ }),
+ },
+
longitude: {
type: 'SelectControl',
label: t('Longitude'),
@@ -560,6 +580,15 @@ export const controls = {
choices: formatSelectOptions([0, 100, 200, 300, 500]),
},
+ stroke_width: {
+ type: 'SelectControl',
+ freeForm: true,
+ label: t('Stroke Width'),
+ validators: [v.integer],
+ default: null,
+ choices: formatSelectOptions([1, 2, 3, 4, 5]),
+ },
+
all_columns_x: {
type: 'SelectControl',
label: 'X',
diff --git a/superset/assets/javascripts/explore/stores/visTypes.js
b/superset/assets/javascripts/explore/stores/visTypes.js
index 0be54ec..e67b6cf 100644
--- a/superset/assets/javascripts/explore/stores/visTypes.js
+++ b/superset/assets/javascripts/explore/stores/visTypes.js
@@ -517,6 +517,34 @@ export const visTypes = {
],
},
+ deck_arc: {
+ label: t('Deck.gl - Arc'),
+ requiresTime: true,
+ controlPanelSections: [
+ {
+ label: t('Query'),
+ expanded: true,
+ controlSetRows: [
+ ['start_spatial', 'end_spatial'],
+ ['row_limit', null],
+ ],
+ },
+ {
+ label: t('Map'),
+ controlSetRows: [
+ ['mapbox_style', 'viewport'],
+ ],
+ },
+ {
+ label: t('Arc'),
+ controlSetRows: [
+ ['color_picker', null],
+ ['stroke_width', null],
+ ],
+ },
+ ],
+ },
+
deck_scatter: {
label: t('Deck.gl - Scatter plot'),
requiresTime: true,
diff --git a/superset/assets/visualizations/deckgl/layers/arc.jsx
b/superset/assets/visualizations/deckgl/layers/arc.jsx
new file mode 100644
index 0000000..38bd1e8
--- /dev/null
+++ b/superset/assets/visualizations/deckgl/layers/arc.jsx
@@ -0,0 +1,16 @@
+import { ArcLayer } from 'deck.gl';
+
+export default function arcLayer(formData, payload) {
+ const fd = formData;
+ const fc = fd.color_picker;
+ const data = payload.data.arcs.map(d => ({
+ ...d,
+ color: [fc.r, fc.g, fc.b, 255 * fc.a],
+ }));
+
+ return new ArcLayer({
+ id: `path-layer-${fd.slice_id}`,
+ data,
+ strokeWidth: (fd.stroke_width) ? fd.stroke_width : 3,
+ });
+}
diff --git a/superset/assets/visualizations/deckgl/layers/index.js
b/superset/assets/visualizations/deckgl/layers/index.js
index a382af5..4d14196 100644
--- a/superset/assets/visualizations/deckgl/layers/index.js
+++ b/superset/assets/visualizations/deckgl/layers/index.js
@@ -5,6 +5,7 @@ import deck_path from './path';
import deck_hex from './hex';
import deck_scatter from './scatter';
import deck_geojson from './geojson';
+import deck_arc from './arc';
const layerGenerators = {
deck_grid,
@@ -13,5 +14,6 @@ const layerGenerators = {
deck_hex,
deck_scatter,
deck_geojson,
+ deck_arc,
};
export default layerGenerators;
diff --git a/superset/assets/visualizations/main.js
b/superset/assets/visualizations/main.js
index af7b040..7cb040b 100644
--- a/superset/assets/visualizations/main.js
+++ b/superset/assets/visualizations/main.js
@@ -46,6 +46,7 @@ export const VIZ_TYPES = {
deck_path: 'deck_path',
deck_geojson: 'deck_geojson',
deck_multi: 'deck_multi',
+ deck_arc: 'deck_arc',
};
const vizMap = {
@@ -92,6 +93,7 @@ const vizMap = {
[VIZ_TYPES.deck_hex]: deckglFactory,
[VIZ_TYPES.deck_path]: deckglFactory,
[VIZ_TYPES.deck_geojson]: deckglFactory,
+ [VIZ_TYPES.deck_arc]: deckglFactory,
[VIZ_TYPES.deck_multi]: require('./deckgl/multi.jsx'),
};
export default vizMap;
diff --git a/superset/viz.py b/superset/viz.py
index 6f4d76c..d046241 100644
--- a/superset/viz.py
+++ b/superset/viz.py
@@ -1806,6 +1806,7 @@ class BaseDeckGLViz(BaseViz):
is_timeseries = False
credits = '<a href="https://uber.github.io/deck.gl/">deck.gl</a>'
+ spatial_control_keys = []
def get_metrics(self):
self.metric = self.form_data.get('size')
@@ -1817,26 +1818,48 @@ class BaseDeckGLViz(BaseViz):
}
def get_position(self, d):
- return [
- d.get('lon'),
- d.get('lat'),
- ]
+ raise Exception('Not implemented in child class!')
+
+ def process_spatial_query_obj(self, key, group_by):
+ spatial = self.form_data.get(key)
+ if spatial is None:
+ raise ValueError(_('Bad spatial key'))
+
+ if spatial.get('type') == 'latlong':
+ group_by += [spatial.get('lonCol')]
+ group_by += [spatial.get('latCol')]
+ elif spatial.get('type') == 'delimited':
+ group_by += [spatial.get('lonlatCol')]
+ elif spatial.get('type') == 'geohash':
+ group_by += [spatial.get('geohashCol')]
+
+ def process_spatial_data_obj(self, key, df):
+ spatial = self.form_data.get(key)
+ if spatial is None:
+ raise ValueError(_('Bad spatial key'))
+
+ if spatial.get('type') == 'latlong':
+ df[key] = list(zip(df[spatial.get('lonCol')],
df[spatial.get('latCol')]))
+ elif spatial.get('type') == 'delimited':
+ df[key] =
(df[spatial.get('lonlatCol')].str.split(spatial.get('delimiter')))
+ if spatial.get('reverseCheckbox'):
+ df[key] = [list(reversed(item))for item in df[key]]
+ del df[spatial.get('lonlatCol')]
+ elif spatial.get('type') == 'geohash':
+ latlong = df[spatial.get('geohashCol')].map(geohash.decode)
+ df[key] = list(zip(latlong.apply(lambda x: x[0]),
+ latlong.apply(lambda x: x[1])))
+ del df[spatial.get('geohashCol')]
+
+ return df
def query_obj(self):
d = super(BaseDeckGLViz, self).query_obj()
fd = self.form_data
-
gb = []
- spatial = fd.get('spatial')
- 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')]
+ for key in self.spatial_control_keys:
+ self.process_spatial_query_obj(key, gb)
if fd.get('dimension'):
gb += [fd.get('dimension')]
@@ -1849,6 +1872,7 @@ class BaseDeckGLViz(BaseViz):
d['metrics'] = self.get_metrics()
else:
d['columns'] = gb
+
return d
def get_js_columns(self, d):
@@ -1856,29 +1880,8 @@ class BaseDeckGLViz(BaseViz):
return {col: d.get(col) for col in cols}
def get_data(self, df):
- fd = self.form_data
- spatial = fd.get('spatial')
- if spatial:
- if spatial.get('type') == 'latlong':
- df = df.rename(columns={
- spatial.get('lonCol'): 'lon',
- spatial.get('latCol'): 'lat'})
- elif spatial.get('type') == 'delimited':
- cols = ['lon', 'lat']
- if spatial.get('reverseCheckbox'):
- cols.reverse()
- df[cols] = (
- df[spatial.get('lonlatCol')]
- .str
- .split(spatial.get('delimiter'), expand=True)
- .astype(np.float64)
- )
- del df[spatial.get('lonlatCol')]
- elif spatial.get('type') == 'geohash':
- latlong = df[spatial.get('geohashCol')].map(geohash.decode)
- df['lat'] = latlong.apply(lambda x: x[0])
- df['lon'] = latlong.apply(lambda x: x[1])
- del df['geohash']
+ for key in self.spatial_control_keys:
+ df = self.process_spatial_data_obj(key, df)
features = []
for d in df.to_dict(orient='records'):
@@ -1899,6 +1902,7 @@ class DeckScatterViz(BaseDeckGLViz):
viz_type = 'deck_scatter'
verbose_name = _('Deck.gl - Scatter plot')
+ spatial_control_keys = ['spatial']
def query_obj(self):
fd = self.form_data
@@ -1906,6 +1910,9 @@ class DeckScatterViz(BaseDeckGLViz):
fd.get('point_radius_fixed') or {'type': 'fix', 'value': 500})
return super(DeckScatterViz, self).query_obj()
+ def get_position(self, d):
+ return d['spatial']
+
def get_metrics(self):
self.metric = None
if self.point_radius_fixed.get('type') == 'metric':
@@ -1935,6 +1942,10 @@ class DeckScreengrid(BaseDeckGLViz):
viz_type = 'deck_screengrid'
verbose_name = _('Deck.gl - Screen Grid')
+ spatial_control_keys = ['spatial']
+
+ def get_position(self, d):
+ return d['spatial']
class DeckGrid(BaseDeckGLViz):
@@ -1943,6 +1954,10 @@ class DeckGrid(BaseDeckGLViz):
viz_type = 'deck_grid'
verbose_name = _('Deck.gl - 3D Grid')
+ spatial_control_keys = ['spatial']
+
+ def get_position(self, d):
+ return d['spatial']
class DeckPathViz(BaseDeckGLViz):
@@ -1955,6 +1970,10 @@ class DeckPathViz(BaseDeckGLViz):
'json': json.loads,
'polyline': polyline.decode,
}
+ spatial_control_keys = ['spatial']
+
+ def get_position(self, d):
+ return d['spatial']
def query_obj(self):
d = super(DeckPathViz, self).query_obj()
@@ -1982,6 +2001,10 @@ class DeckHex(BaseDeckGLViz):
viz_type = 'deck_hex'
verbose_name = _('Deck.gl - 3D HEX')
+ spatial_control_keys = ['spatial']
+
+ def get_position(self, d):
+ return d['spatial']
class DeckGeoJson(BaseDeckGLViz):
@@ -2011,6 +2034,32 @@ class DeckGeoJson(BaseDeckGLViz):
}
+class DeckArc(BaseDeckGLViz):
+
+ """deck.gl's Arc Layer"""
+
+ viz_type = 'deck_arc'
+ verbose_name = _('Deck.gl - Arc')
+ spatial_control_keys = ['start_spatial', 'end_spatial']
+
+ def get_position(self, d):
+ deck_map = {
+ 'start_spatial': 'sourcePosition',
+ 'end_spatial': 'targetPosition',
+ }
+
+ return {deck_map[key]: d[key] for key in self.spatial_control_keys}
+
+ def get_data(self, df):
+ d = super(DeckArc, self).get_data(df)
+ arcs = d['features']
+
+ return {
+ 'arcs': [arc['position'] for arc in arcs],
+ 'mapboxApiKey': config.get('MAPBOX_API_KEY'),
+ }
+
+
class EventFlowViz(BaseViz):
"""A visualization to explore patterns in event sequences"""
--
To stop receiving notification emails like this one, please contact
['"[email protected]" <[email protected]>'].