You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@superset.apache.org by ma...@apache.org on 2018/01/17 22:01:34 UTC
[incubator-superset] branch master updated: Improve deck.gl GeoJSON
visualization (#4220)
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 01043c9 Improve deck.gl GeoJSON visualization (#4220)
01043c9 is described below
commit 01043c9bf45150b430f40e8833111c2b0eb2d5e3
Author: Maxime Beauchemin <ma...@gmail.com>
AuthorDate: Wed Jan 17 14:01:32 2018 -0800
Improve deck.gl GeoJSON visualization (#4220)
* Improve geoJSON
* Addressing comments
* lint
---
superset/assets/package.json | 1 +
.../visualizations/deckgl/DeckGLContainer.jsx | 1 +
.../visualizations/deckgl/layers/geojson.jsx | 72 +++++++++++++++------
.../assets/visualizations/deckgl/layers/grid.jsx | 1 -
superset/viz.py | 73 +++++++++-------------
5 files changed, 86 insertions(+), 62 deletions(-)
diff --git a/superset/assets/package.json b/superset/assets/package.json
index 943e048..c944ad2 100644
--- a/superset/assets/package.json
+++ b/superset/assets/package.json
@@ -66,6 +66,7 @@
"jquery": "3.1.1",
"lodash.throttle": "^4.1.1",
"luma.gl": "^5.0.1",
+ "mapbox-gl": "^0.43.0",
"mathjs": "^3.16.3",
"moment": "2.18.1",
"mustache": "^2.2.1",
diff --git a/superset/assets/visualizations/deckgl/DeckGLContainer.jsx b/superset/assets/visualizations/deckgl/DeckGLContainer.jsx
index 64ee934..3166917 100644
--- a/superset/assets/visualizations/deckgl/DeckGLContainer.jsx
+++ b/superset/assets/visualizations/deckgl/DeckGLContainer.jsx
@@ -2,6 +2,7 @@ import React from 'react';
import PropTypes from 'prop-types';
import MapGL from 'react-map-gl';
import DeckGL from 'deck.gl';
+import 'mapbox-gl/dist/mapbox-gl.css';
const propTypes = {
viewport: PropTypes.object.isRequired,
diff --git a/superset/assets/visualizations/deckgl/layers/geojson.jsx b/superset/assets/visualizations/deckgl/layers/geojson.jsx
index 3ee1f62..c21ce3b 100644
--- a/superset/assets/visualizations/deckgl/layers/geojson.jsx
+++ b/superset/assets/visualizations/deckgl/layers/geojson.jsx
@@ -14,40 +14,74 @@ const propertyMap = {
'stroke-width': 'strokeWidth',
};
-const convertGeoJsonColorProps = (p, colors) => {
- const obj = Object.assign(...Object.keys(p).map(k => ({
- [(propertyMap[k]) ? propertyMap[k] : k]: p[k] })));
-
+const alterProps = (props, propOverrides) => {
+ const newProps = {};
+ Object.keys(props).forEach((k) => {
+ if (k in propertyMap) {
+ newProps[propertyMap[k]] = props[k];
+ } else {
+ newProps[k] = props[k];
+ }
+ });
+ if (typeof props.fillColor === 'string') {
+ newProps.fillColor = hexToRGB(p.fillColor);
+ }
+ if (typeof props.strokeColor === 'string') {
+ newProps.strokeColor = hexToRGB(p.strokeColor);
+ }
return {
- ...obj,
- fillColor: (colors.fillColor[3] !== 0) ? colors.fillColor : hexToRGB(obj.fillColor),
- strokeColor: (colors.strokeColor[3] !== 0) ? colors.strokeColor : hexToRGB(obj.strokeColor),
+ ...newProps,
+ ...propOverrides,
};
};
+let features;
+const recurseGeoJson = (node, propOverrides, jsFnMutator, extraProps) => {
+ if (node && node.features) {
+ node.features.forEach((obj) => {
+ recurseGeoJson(obj, propOverrides, jsFnMutator, node.extraProps || extraProps);
+ });
+ }
+ if (node && node.geometry) {
+ const newNode = {
+ ...node,
+ properties: alterProps(node.properties, propOverrides),
+ };
+ if (jsFnMutator) {
+ jsFnMutator(newNode);
+ }
+ if (!newNode.extraProps) {
+ newNode.extraProps = extraProps;
+ }
+ features.push(newNode);
+ }
+};
export default function geoJsonLayer(formData, payload, slice) {
const fd = formData;
const fc = fd.fill_color_picker;
const sc = fd.stroke_color_picker;
- let data = payload.data.geojson.features.map(d => ({
- ...d,
- properties: convertGeoJsonColorProps(
- d.properties, {
- fillColor: [fc.r, fc.g, fc.b, 255 * fc.a],
- strokeColor: [sc.r, sc.g, sc.b, 255 * sc.a],
- }),
- }));
+ const fillColor = [fc.r, fc.g, fc.b, 255 * fc.a];
+ const strokeColor = [sc.r, sc.g, sc.b, 255 * sc.a];
+ const propOverrides = {};
+ if (fillColor[3] > 0) {
+ propOverrides.fillColor = fillColor;
+ }
+ if (strokeColor[3] > 0) {
+ propOverrides.strokeColor = strokeColor;
+ }
+ let jsFnMutator;
if (fd.js_datapoint_mutator) {
// Applying user defined data mutator if defined
- const jsFnMutator = sandboxedEval(fd.js_datapoint_mutator);
- data = data.map(jsFnMutator);
+ jsFnMutator = sandboxedEval(fd.js_datapoint_mutator);
}
+ features = [];
+ recurseGeoJson(payload.data, propOverrides, jsFnMutator);
return new GeoJsonLayer({
- id: `path-layer-${fd.slice_id}`,
- data,
+ id: `geojson-layer-${fd.slice_id}`,
filled: fd.filled,
+ data: features,
stroked: fd.stroked,
extruded: fd.extruded,
pointRadiusScale: fd.point_radius_scale,
diff --git a/superset/assets/visualizations/deckgl/layers/grid.jsx b/superset/assets/visualizations/deckgl/layers/grid.jsx
index 51b1e03..a461eb9 100644
--- a/superset/assets/visualizations/deckgl/layers/grid.jsx
+++ b/superset/assets/visualizations/deckgl/layers/grid.jsx
@@ -7,7 +7,6 @@ export default function getLayer(formData, payload) {
...d,
color: [c.r, c.g, c.b, 255 * c.a],
}));
-
return new GridLayer({
id: `grid-layer-${fd.slice_id}`,
data,
diff --git a/superset/viz.py b/superset/viz.py
index 9d6a57d..65cc975 100644
--- a/superset/viz.py
+++ b/superset/viz.py
@@ -1825,14 +1825,6 @@ class BaseDeckGLViz(BaseViz):
self.metric = self.form_data.get('size')
return [self.metric] if self.metric else []
- def get_properties(self, d):
- return {
- 'weight': d.get(self.metric) or 1,
- }
-
- def get_position(self, d):
- 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:
@@ -1898,16 +1890,19 @@ class BaseDeckGLViz(BaseViz):
features = []
for d in df.to_dict(orient='records'):
- feature = dict(
- position=self.get_position(d),
- props=self.get_js_columns(d),
- **self.get_properties(d))
+ feature = self.get_properties(d)
+ extra_props = self.get_js_columns(d)
+ if extra_props:
+ feature['extraProps'] = extra_props
features.append(feature)
return {
'features': features,
'mapboxApiKey': config.get('MAPBOX_API_KEY'),
}
+ def get_properties(self, d):
+ raise NotImplementedError()
+
class DeckScatterViz(BaseDeckGLViz):
@@ -1923,9 +1918,6 @@ 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':
@@ -1937,6 +1929,7 @@ class DeckScatterViz(BaseDeckGLViz):
return {
'radius': self.fixed_value if self.fixed_value else d.get(self.metric),
'cat_color': d.get(self.dim) if self.dim else None,
+ 'position': d.get('spatial'),
}
def get_data(self, df):
@@ -1957,8 +1950,11 @@ class DeckScreengrid(BaseDeckGLViz):
verbose_name = _('Deck.gl - Screen Grid')
spatial_control_keys = ['spatial']
- def get_position(self, d):
- return d['spatial']
+ def get_properties(self, d):
+ return {
+ 'position': d.get('spatial'),
+ 'weight': d.get(self.metric) or 1,
+ }
class DeckGrid(BaseDeckGLViz):
@@ -1969,8 +1965,11 @@ class DeckGrid(BaseDeckGLViz):
verbose_name = _('Deck.gl - 3D Grid')
spatial_control_keys = ['spatial']
- def get_position(self, d):
- return d['spatial']
+ def get_properties(self, d):
+ return {
+ 'position': d.get('spatial'),
+ 'weight': d.get(self.metric) or 1,
+ }
class DeckPathViz(BaseDeckGLViz):
@@ -1985,9 +1984,6 @@ class DeckPathViz(BaseDeckGLViz):
}
spatial_control_keys = ['spatial']
- def get_position(self, d):
- return d['spatial']
-
def query_obj(self):
d = super(DeckPathViz, self).query_obj()
line_col = self.form_data.get('line_column')
@@ -2016,8 +2012,11 @@ class DeckHex(BaseDeckGLViz):
verbose_name = _('Deck.gl - 3D HEX')
spatial_control_keys = ['spatial']
- def get_position(self, d):
- return d['spatial']
+ def get_properties(self, d):
+ return {
+ 'position': d.get('spatial'),
+ 'weight': d.get(self.metric) or 1,
+ }
class DeckGeoJson(BaseDeckGLViz):
@@ -2029,22 +2028,14 @@ class DeckGeoJson(BaseDeckGLViz):
def query_obj(self):
d = super(DeckGeoJson, self).query_obj()
- d['columns'] = [self.form_data.get('geojson')]
+ d['columns'] += [self.form_data.get('geojson')]
d['metrics'] = []
d['groupby'] = []
return d
- def get_data(self, df):
- fd = self.form_data
- geojson = {
- 'type': 'FeatureCollection',
- 'features': [json.loads(item) for item in df[fd.get('geojson')]],
- }
-
- return {
- 'geojson': geojson,
- 'mapboxApiKey': config.get('MAPBOX_API_KEY'),
- }
+ def get_properties(self, d):
+ geojson = d.get(self.form_data.get('geojson'))
+ return json.loads(geojson)
class DeckArc(BaseDeckGLViz):
@@ -2055,14 +2046,12 @@ class DeckArc(BaseDeckGLViz):
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',
+ def get_properties(self, d):
+ return {
+ 'sourcePosition': d.get('start_spatial'),
+ 'targetPosition': d.get('end_spatial'),
}
- 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']
--
To stop receiving notification emails like this one, please contact
['"commits@superset.apache.org" <co...@superset.apache.org>'].