diff --git a/setup.py b/setup.py
index 0ecd1ef1be6e3..f662e885c36ac 100644
--- a/setup.py
+++ b/setup.py
@@ -73,7 +73,7 @@ def get_git_sha():
'pyyaml>=3.11',
'requests==2.17.3',
'simplejson==3.10.0',
- 'six==1.10.0',
+ 'six==1.11.0',
'sqlalchemy==1.1.9',
'sqlalchemy-utils==0.32.16',
'sqlparse==0.2.3',
diff --git a/superset/assets/images/viz_thumbnails/multi.png b/superset/assets/images/viz_thumbnails/multi.png
new file mode 100644
index 0000000000000..be62cd40e9002
Binary files /dev/null and b/superset/assets/images/viz_thumbnails/multi.png differ
diff --git a/superset/assets/javascripts/chart/chartAction.js b/superset/assets/javascripts/chart/chartAction.js
index a6341ddb6a7ba..393400d6bcd6f 100644
--- a/superset/assets/javascripts/chart/chartAction.js
+++ b/superset/assets/javascripts/chart/chartAction.js
@@ -120,7 +120,20 @@ export function runQuery(formData, force = false, timeout = 60, key) {
if (err.statusText === 'timeout') {
dispatch(chartUpdateTimeout(err.statusText, timeout, key));
} else if (err.statusText !== 'abort') {
- dispatch(chartUpdateFailed(err.responseJSON, key));
+ let errObject;
+ if (err.responseJSON) {
+ errObject = err.responseJSON;
+ } else if (err.stack) {
+ errObject = {
+ error: 'Unexpected error: ' + err.description,
+ stacktrace: err.stack,
+ };
+ } else {
+ errObject = {
+ error: 'Unexpected error.',
+ };
+ }
+ dispatch(chartUpdateFailed(errObject, key));
}
});
const annotationLayers = formData.annotation_layers || [];
diff --git a/superset/assets/javascripts/explore/stores/controls.jsx b/superset/assets/javascripts/explore/stores/controls.jsx
index 7aee160d9c3ab..70cc2314a21e5 100644
--- a/superset/assets/javascripts/explore/stores/controls.jsx
+++ b/superset/assets/javascripts/explore/stores/controls.jsx
@@ -1389,6 +1389,7 @@ export const controls = {
mapbox_style: {
type: 'SelectControl',
label: t('Map Style'),
+ clearable: false,
renderTrigger: true,
choices: [
['mapbox://styles/mapbox/streets-v9', 'Streets'],
@@ -1816,5 +1817,23 @@ export const controls = {
and returns a similarly shaped object. {sandboxedEvalInfo}
),
},
+
+ deck_slices: {
+ type: 'SelectAsyncControl',
+ multi: true,
+ label: t('deck.gl charts'),
+ validators: [v.nonEmpty],
+ default: [],
+ description: t('Pick a set of deck.gl charts to layer on top of one another'),
+ dataEndpoint: '/sliceasync/api/read?_flt_0_viz_type=deck_',
+ placeholder: t('Select charts'),
+ onAsyncErrorMessage: t('Error while fetching charts'),
+ mutator: (data) => {
+ if (!data || !data.result) {
+ return [];
+ }
+ return data.result.map(o => ({ value: o.id, label: o.slice_name }));
+ },
+ },
};
export default controls;
diff --git a/superset/assets/javascripts/explore/stores/visTypes.js b/superset/assets/javascripts/explore/stores/visTypes.js
index f4720f9932fc7..f2e668f8f1829 100644
--- a/superset/assets/javascripts/explore/stores/visTypes.js
+++ b/superset/assets/javascripts/explore/stores/visTypes.js
@@ -338,6 +338,21 @@ export const visTypes = {
},
},
+ deck_multi: {
+ label: t('Deck.gl - Multiple Layers'),
+ requiresTime: true,
+ controlPanelSections: [
+ {
+ label: t('Map'),
+ expanded: true,
+ controlSetRows: [
+ ['mapbox_style', 'viewport'],
+ ['deck_slices', null],
+ ],
+ },
+ ],
+ },
+
deck_hex: {
label: t('Deck.gl - Hexagons'),
requiresTime: true,
@@ -398,7 +413,7 @@ export const visTypes = {
},
deck_path: {
- label: t('Deck.gl - Grid'),
+ label: t('Deck.gl - Paths'),
requiresTime: true,
controlPanelSections: [
{
diff --git a/superset/assets/visualizations/deckgl/path.jsx b/superset/assets/visualizations/deckgl/factory.jsx
similarity index 54%
rename from superset/assets/visualizations/deckgl/path.jsx
rename to superset/assets/visualizations/deckgl/factory.jsx
index c814adc501ccb..d715bc1a9cbe0 100644
--- a/superset/assets/visualizations/deckgl/path.jsx
+++ b/superset/assets/visualizations/deckgl/factory.jsx
@@ -1,25 +1,12 @@
import React from 'react';
import ReactDOM from 'react-dom';
-import { PathLayer } from 'deck.gl';
import DeckGLContainer from './DeckGLContainer';
+import layerGenerators from './layers';
-function deckPath(slice, payload, setControlValue) {
+export default function deckglFactory(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 layer = layerGenerators[fd.viz_type](fd, payload);
const viewport = {
...fd.viewport,
width: slice.width(),
@@ -36,4 +23,3 @@ function deckPath(slice, payload, setControlValue) {
document.getElementById(slice.containerId),
);
}
-module.exports = deckPath;
diff --git a/superset/assets/visualizations/deckgl/grid.jsx b/superset/assets/visualizations/deckgl/grid.jsx
deleted file mode 100644
index 1ef2394873bad..0000000000000
--- a/superset/assets/visualizations/deckgl/grid.jsx
+++ /dev/null
@@ -1,43 +0,0 @@
-import React from 'react';
-import ReactDOM from 'react-dom';
-import { GridLayer } from 'deck.gl';
-
-import DeckGLContainer from './DeckGLContainer';
-
-function deckScreenGridLayer(slice, payload, setControlValue) {
- const fd = slice.formData;
- const c = fd.color_picker;
- const data = payload.data.features.map(d => ({
- ...d,
- color: [c.r, c.g, c.b, 255 * c.a],
- }));
-
- const layer = new GridLayer({
- id: `grid-layer-${slice.containerId}`,
- data,
- pickable: true,
- cellSize: fd.grid_size,
- minColor: [0, 0, 0, 0],
- extruded: fd.extruded,
- maxColor: [c.r, c.g, c.b, 255 * c.a],
- outline: false,
- getElevationValue: points => points.reduce((sum, point) => sum + point.weight, 0),
- getColorValue: points => points.reduce((sum, point) => sum + point.weight, 0),
- });
- const viewport = {
- ...fd.viewport,
- width: slice.width(),
- height: slice.height(),
- };
- ReactDOM.render(
- ,
- document.getElementById(slice.containerId),
- );
-}
-module.exports = deckScreenGridLayer;
diff --git a/superset/assets/visualizations/deckgl/hex.jsx b/superset/assets/visualizations/deckgl/hex.jsx
deleted file mode 100644
index 9526825d250b6..0000000000000
--- a/superset/assets/visualizations/deckgl/hex.jsx
+++ /dev/null
@@ -1,43 +0,0 @@
-import React from 'react';
-import ReactDOM from 'react-dom';
-import { HexagonLayer } from 'deck.gl';
-
-import DeckGLContainer from './DeckGLContainer';
-
-function deckHex(slice, payload, setControlValue) {
- const fd = slice.formData;
- const c = fd.color_picker;
- const data = payload.data.features.map(d => ({
- ...d,
- color: [c.r, c.g, c.b, 255 * c.a],
- }));
-
- const layer = new HexagonLayer({
- id: `hex-layer-${slice.containerId}`,
- data,
- pickable: true,
- radius: fd.grid_size,
- minColor: [0, 0, 0, 0],
- extruded: fd.extruded,
- maxColor: [c.r, c.g, c.b, 255 * c.a],
- outline: false,
- getElevationValue: points => points.reduce((sum, point) => sum + point.weight, 0),
- getColorValue: points => points.reduce((sum, point) => sum + point.weight, 0),
- });
- const viewport = {
- ...fd.viewport,
- width: slice.width(),
- height: slice.height(),
- };
- ReactDOM.render(
- ,
- document.getElementById(slice.containerId),
- );
-}
-module.exports = deckHex;
diff --git a/superset/assets/visualizations/deckgl/geojson.jsx b/superset/assets/visualizations/deckgl/layers/geojson.jsx
similarity index 59%
rename from superset/assets/visualizations/deckgl/geojson.jsx
rename to superset/assets/visualizations/deckgl/layers/geojson.jsx
index 080d7ee3f1891..11a7b8375f5b2 100644
--- a/superset/assets/visualizations/deckgl/geojson.jsx
+++ b/superset/assets/visualizations/deckgl/layers/geojson.jsx
@@ -1,9 +1,6 @@
-import React from 'react';
-import ReactDOM from 'react-dom';
import { GeoJsonLayer } from 'deck.gl';
-import { hexToRGB } from '../../javascripts/modules/colors';
+import { hexToRGB } from '../../../javascripts/modules/colors';
-import DeckGLContainer from './DeckGLContainer';
const propertyMap = {
fillColor: 'fillColor',
@@ -26,8 +23,8 @@ const convertGeoJsonColorProps = (p, colors) => {
};
};
-function DeckGeoJsonLayer(slice, payload, setControlValue) {
- const fd = slice.formData;
+export default function geoJsonLayer(formData, payload) {
+ const fd = formData;
const fc = fd.fill_color_picker;
const sc = fd.stroke_color_picker;
const data = payload.data.geojson.features.map(d => ({
@@ -39,29 +36,12 @@ function DeckGeoJsonLayer(slice, payload, setControlValue) {
}),
}));
- const layer = new GeoJsonLayer({
- id: 'geojson-layer',
+ return new GeoJsonLayer({
+ id: `path-layer-${fd.slice_id}`,
data,
filled: true,
stroked: false,
extruded: true,
pointRadiusScale: fd.point_radius_scale,
});
-
- const viewport = {
- ...fd.viewport,
- width: slice.width(),
- height: slice.height(),
- };
- ReactDOM.render(
- ,
- document.getElementById(slice.containerId),
- );
}
-module.exports = DeckGeoJsonLayer;
diff --git a/superset/assets/visualizations/deckgl/layers/grid.jsx b/superset/assets/visualizations/deckgl/layers/grid.jsx
new file mode 100644
index 0000000000000..51b1e03d9de84
--- /dev/null
+++ b/superset/assets/visualizations/deckgl/layers/grid.jsx
@@ -0,0 +1,23 @@
+import { GridLayer } from 'deck.gl';
+
+export default function getLayer(formData, payload) {
+ const fd = formData;
+ const c = fd.color_picker;
+ const data = payload.data.features.map(d => ({
+ ...d,
+ color: [c.r, c.g, c.b, 255 * c.a],
+ }));
+
+ return new GridLayer({
+ id: `grid-layer-${fd.slice_id}`,
+ data,
+ pickable: true,
+ cellSize: fd.grid_size,
+ minColor: [0, 0, 0, 0],
+ extruded: fd.extruded,
+ maxColor: [c.r, c.g, c.b, 255 * c.a],
+ outline: false,
+ getElevationValue: points => points.reduce((sum, point) => sum + point.weight, 0),
+ getColorValue: points => points.reduce((sum, point) => sum + point.weight, 0),
+ });
+}
diff --git a/superset/assets/visualizations/deckgl/layers/hex.jsx b/superset/assets/visualizations/deckgl/layers/hex.jsx
new file mode 100644
index 0000000000000..0e33e9495305a
--- /dev/null
+++ b/superset/assets/visualizations/deckgl/layers/hex.jsx
@@ -0,0 +1,23 @@
+import { HexagonLayer } from 'deck.gl';
+
+export default function getLayer(formData, payload) {
+ const fd = formData;
+ const c = fd.color_picker;
+ const data = payload.data.features.map(d => ({
+ ...d,
+ color: [c.r, c.g, c.b, 255 * c.a],
+ }));
+
+ return new HexagonLayer({
+ id: `hex-layer-${fd.slice_id}`,
+ data,
+ pickable: true,
+ radius: fd.grid_size,
+ minColor: [0, 0, 0, 0],
+ extruded: fd.extruded,
+ maxColor: [c.r, c.g, c.b, 255 * c.a],
+ outline: false,
+ getElevationValue: points => points.reduce((sum, point) => sum + point.weight, 0),
+ getColorValue: points => points.reduce((sum, point) => sum + point.weight, 0),
+ });
+}
diff --git a/superset/assets/visualizations/deckgl/layers/index.js b/superset/assets/visualizations/deckgl/layers/index.js
new file mode 100644
index 0000000000000..a382af55b8a66
--- /dev/null
+++ b/superset/assets/visualizations/deckgl/layers/index.js
@@ -0,0 +1,17 @@
+/* eslint camelcase: 0 */
+import deck_grid from './grid';
+import deck_screengrid from './screengrid';
+import deck_path from './path';
+import deck_hex from './hex';
+import deck_scatter from './scatter';
+import deck_geojson from './geojson';
+
+const layerGenerators = {
+ deck_grid,
+ deck_screengrid,
+ deck_path,
+ deck_hex,
+ deck_scatter,
+ deck_geojson,
+};
+export default layerGenerators;
diff --git a/superset/assets/visualizations/deckgl/layers/path.jsx b/superset/assets/visualizations/deckgl/layers/path.jsx
new file mode 100644
index 0000000000000..c288ff0576a8e
--- /dev/null
+++ b/superset/assets/visualizations/deckgl/layers/path.jsx
@@ -0,0 +1,19 @@
+import { PathLayer } from 'deck.gl';
+
+export default function getLayer(formData, payload) {
+ const fd = 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,
+ }));
+
+ return new PathLayer({
+ id: `path-layer-${fd.slice_id}`,
+ data,
+ rounded: true,
+ widthScale: 1,
+ });
+}
diff --git a/superset/assets/visualizations/deckgl/layers/scatter.jsx b/superset/assets/visualizations/deckgl/layers/scatter.jsx
new file mode 100644
index 0000000000000..d44e7272adb97
--- /dev/null
+++ b/superset/assets/visualizations/deckgl/layers/scatter.jsx
@@ -0,0 +1,35 @@
+import { ScatterplotLayer } from 'deck.gl';
+
+import { getColorFromScheme, hexToRGB } from '../../../javascripts/modules/colors';
+import { unitToRadius } from '../../../javascripts/modules/geo';
+
+export default function getLayer(formData, payload) {
+ const fd = formData;
+ const c = fd.color_picker || { r: 0, g: 0, b: 0, a: 1 };
+ const fixedColor = [c.r, c.g, c.b, 255 * c.a];
+
+ const data = payload.data.features.map((d) => {
+ let radius = unitToRadius(fd.point_unit, d.radius) || 10;
+ if (fd.multiplier) {
+ radius *= fd.multiplier;
+ }
+ let color;
+ if (fd.dimension) {
+ color = hexToRGB(getColorFromScheme(d.cat_color, fd.color_scheme), c.a * 255);
+ } else {
+ color = fixedColor;
+ }
+ return {
+ ...d,
+ radius,
+ color,
+ };
+ });
+ return new ScatterplotLayer({
+ id: `scatter-layer-${fd.slice_id}`,
+ data,
+ pickable: true,
+ fp64: true,
+ outline: false,
+ });
+}
diff --git a/superset/assets/visualizations/deckgl/layers/screengrid.jsx b/superset/assets/visualizations/deckgl/layers/screengrid.jsx
new file mode 100644
index 0000000000000..54edd9eaba0f1
--- /dev/null
+++ b/superset/assets/visualizations/deckgl/layers/screengrid.jsx
@@ -0,0 +1,23 @@
+import { ScreenGridLayer } from 'deck.gl';
+
+export default function getLayer(formData, payload) {
+ const fd = formData;
+ const c = fd.color_picker;
+ const data = payload.data.features.map(d => ({
+ ...d,
+ color: [c.r, c.g, c.b, 255 * c.a],
+ }));
+
+ // Passing a layer creator function instead of a layer since the
+ // layer needs to be regenerated at each render
+ return new ScreenGridLayer({
+ id: `screengrid-layer-${fd.slice_id}`,
+ data,
+ pickable: true,
+ cellSizePixels: fd.grid_size,
+ minColor: [c.r, c.g, c.b, 0],
+ maxColor: [c.r, c.g, c.b, 255 * c.a],
+ outline: false,
+ getWeight: d => d.weight || 0,
+ });
+}
diff --git a/superset/assets/visualizations/deckgl/multi.jsx b/superset/assets/visualizations/deckgl/multi.jsx
new file mode 100644
index 0000000000000..63f1a8801174b
--- /dev/null
+++ b/superset/assets/visualizations/deckgl/multi.jsx
@@ -0,0 +1,44 @@
+import React from 'react';
+import ReactDOM from 'react-dom';
+import $ from 'jquery';
+
+import DeckGLContainer from './DeckGLContainer';
+import { getExploreUrl } from '../../javascripts/explore/exploreUtils';
+import layerGenerators from './layers';
+
+
+function deckMulti(slice, payload, setControlValue) {
+ if (!slice.subSlicesLayers) {
+ slice.subSlicesLayers = {}; // eslint-disable-line no-param-reassign
+ }
+ const fd = slice.formData;
+ const render = () => {
+ const viewport = {
+ ...fd.viewport,
+ width: slice.width(),
+ height: slice.height(),
+ };
+ const layers = Object.keys(slice.subSlicesLayers).map(k => slice.subSlicesLayers[k]);
+ ReactDOM.render(
+ ,
+ document.getElementById(slice.containerId),
+ );
+ };
+ render();
+ payload.data.slices.forEach((subslice) => {
+ const url = getExploreUrl(subslice.form_data, 'json');
+ $.get(url, (data) => {
+ // Late import to avoid circular deps
+ const layer = layerGenerators[subslice.form_data.viz_type](subslice.form_data, data);
+ slice.subSlicesLayers[subslice.slice_id] = layer; // eslint-disable-line no-param-reassign
+ render();
+ });
+ });
+}
+module.exports = deckMulti;
diff --git a/superset/assets/visualizations/deckgl/scatter.jsx b/superset/assets/visualizations/deckgl/scatter.jsx
deleted file mode 100644
index 18cec553fa1c9..0000000000000
--- a/superset/assets/visualizations/deckgl/scatter.jsx
+++ /dev/null
@@ -1,55 +0,0 @@
-import React from 'react';
-import ReactDOM from 'react-dom';
-import { ScatterplotLayer } from 'deck.gl';
-
-import DeckGLContainer from './DeckGLContainer';
-import { getColorFromScheme, hexToRGB } from '../../javascripts/modules/colors';
-import { unitToRadius } from '../../javascripts/modules/geo';
-
-function deckScatter(slice, payload, setControlValue) {
- const fd = slice.formData;
- const c = fd.color_picker || { r: 0, g: 0, b: 0, a: 1 };
- const fixedColor = [c.r, c.g, c.b, 255 * c.a];
-
- const data = payload.data.features.map((d) => {
- let radius = unitToRadius(fd.point_unit, d.radius) || 10;
- if (fd.multiplier) {
- radius *= fd.multiplier;
- }
- let color;
- if (fd.dimension) {
- color = hexToRGB(getColorFromScheme(d.cat_color, fd.color_scheme), c.a * 255);
- } else {
- color = fixedColor;
- }
- return {
- ...d,
- radius,
- color,
- };
- });
-
- const layer = new ScatterplotLayer({
- id: `scatter-layer-${slice.containerId}`,
- data,
- pickable: true,
- fp64: true,
- outline: false,
- });
- const viewport = {
- ...fd.viewport,
- width: slice.width(),
- height: slice.height(),
- };
- ReactDOM.render(
- ,
- document.getElementById(slice.containerId),
- );
-}
-module.exports = deckScatter;
diff --git a/superset/assets/visualizations/deckgl/screengrid.jsx b/superset/assets/visualizations/deckgl/screengrid.jsx
deleted file mode 100644
index b8b58ec056d59..0000000000000
--- a/superset/assets/visualizations/deckgl/screengrid.jsx
+++ /dev/null
@@ -1,43 +0,0 @@
-import React from 'react';
-import ReactDOM from 'react-dom';
-import { ScreenGridLayer } from 'deck.gl';
-
-import DeckGLContainer from './DeckGLContainer';
-
-function deckScreenGridLayer(slice, payload, setControlValue) {
- const fd = slice.formData;
- const c = fd.color_picker;
- const data = payload.data.features.map(d => ({
- ...d,
- color: [c.r, c.g, c.b, 255 * c.a],
- }));
-
- const viewport = {
- ...fd.viewport,
- width: slice.width(),
- height: slice.height(),
- };
- // Passing a layer creator function instead of a layer since the
- // layer needs to be regenerated at each render
- const layer = () => new ScreenGridLayer({
- id: `screengrid-layer-${slice.containerId}`,
- data,
- pickable: true,
- cellSizePixels: fd.grid_size,
- minColor: [c.r, c.g, c.b, 0],
- maxColor: [c.r, c.g, c.b, 255 * c.a],
- outline: false,
- getWeight: d => d.weight || 0,
- });
- ReactDOM.render(
- ,
- document.getElementById(slice.containerId),
- );
-}
-module.exports = deckScreenGridLayer;
diff --git a/superset/assets/visualizations/main.js b/superset/assets/visualizations/main.js
index e692c107d8f2f..af7b0401017f4 100644
--- a/superset/assets/visualizations/main.js
+++ b/superset/assets/visualizations/main.js
@@ -1,4 +1,5 @@
/* eslint-disable global-require */
+import deckglFactory from './deckgl/factory';
// You ***should*** use these to reference viz_types in code
export const VIZ_TYPES = {
@@ -44,6 +45,7 @@ export const VIZ_TYPES = {
deck_hex: 'deck_hex',
deck_path: 'deck_path',
deck_geojson: 'deck_geojson',
+ deck_multi: 'deck_multi',
};
const vizMap = {
@@ -84,11 +86,12 @@ const vizMap = {
[VIZ_TYPES.event_flow]: require('./EventFlow.jsx'),
[VIZ_TYPES.paired_ttest]: require('./paired_ttest.jsx'),
[VIZ_TYPES.partition]: require('./partition.js'),
- [VIZ_TYPES.deck_scatter]: require('./deckgl/scatter.jsx'),
- [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'),
- [VIZ_TYPES.deck_geojson]: require('./deckgl/geojson.jsx'),
+ [VIZ_TYPES.deck_scatter]: deckglFactory,
+ [VIZ_TYPES.deck_screengrid]: deckglFactory,
+ [VIZ_TYPES.deck_grid]: deckglFactory,
+ [VIZ_TYPES.deck_hex]: deckglFactory,
+ [VIZ_TYPES.deck_path]: deckglFactory,
+ [VIZ_TYPES.deck_geojson]: deckglFactory,
+ [VIZ_TYPES.deck_multi]: require('./deckgl/multi.jsx'),
};
export default vizMap;
diff --git a/superset/views/core.py b/superset/views/core.py
index 02e6f1d8a5b02..802fda9ef3705 100755
--- a/superset/views/core.py
+++ b/superset/views/core.py
@@ -493,7 +493,7 @@ def add(self):
class SliceAsync(SliceModelView): # noqa
list_columns = [
- 'slice_link', 'viz_type',
+ 'id', 'slice_link', 'viz_type', 'slice_name',
'creator', 'modified', 'icons']
label_columns = {
'icons': ' ',
diff --git a/superset/viz.py b/superset/viz.py
index 2a6b4940c93db..bb052d17aba00 100644
--- a/superset/viz.py
+++ b/superset/viz.py
@@ -86,6 +86,8 @@ def get_df(self, query_obj=None):
"""Returns a pandas dataframe based on the query object"""
if not query_obj:
query_obj = self.query_obj()
+ if not query_obj:
+ return None
self.error_msg = ''
self.results = None
@@ -1768,6 +1770,32 @@ def get_data(self, df):
}
+class DeckGLMultiLayer(BaseViz):
+
+ """Pile on multiple DeckGL layers"""
+
+ viz_type = 'deck_multi'
+ verbose_name = _('Deck.gl - Multiple Layers')
+
+ is_timeseries = False
+ credits = 'deck.gl'
+
+ def query_obj(self):
+ return None
+
+ def get_data(self, df):
+ fd = self.form_data
+ # Late imports to avoid circular import issues
+ from superset.models.core import Slice
+ from superset import db
+ slice_ids = fd.get('deck_slices')
+ slices = db.session.query(Slice).filter(Slice.id.in_(slice_ids)).all()
+ return {
+ 'mapboxApiKey': config.get('MAPBOX_API_KEY'),
+ 'slices': [slc.data for slc in slices],
+ }
+
+
class BaseDeckGLViz(BaseViz):
"""Base class for deck.gl visualizations"""