Skip to content

Commit

Permalink
Improve deck.gl GeoJSON visualization (#4220)
Browse files Browse the repository at this point in the history
* Improve geoJSON

* Addressing comments

* lint
  • Loading branch information
mistercrunch authored Jan 17, 2018
1 parent a9610e2 commit 01043c9
Show file tree
Hide file tree
Showing 5 changed files with 86 additions and 62 deletions.
1 change: 1 addition & 0 deletions superset/assets/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
1 change: 1 addition & 0 deletions superset/assets/visualizations/deckgl/DeckGLContainer.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
72 changes: 53 additions & 19 deletions superset/assets/visualizations/deckgl/layers/geojson.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
1 change: 0 additions & 1 deletion superset/assets/visualizations/deckgl/layers/grid.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
73 changes: 31 additions & 42 deletions superset/viz.py
Original file line number Diff line number Diff line change
Expand Up @@ -1825,14 +1825,6 @@ def get_metrics(self):
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:
Expand Down Expand Up @@ -1898,16 +1890,19 @@ def get_data(self, df):

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):

Expand All @@ -1923,9 +1918,6 @@ def query_obj(self):
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':
Expand All @@ -1937,6 +1929,7 @@ def get_properties(self, d):
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):
Expand All @@ -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):
Expand All @@ -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):
Expand All @@ -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')
Expand Down Expand Up @@ -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):
Expand All @@ -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):
Expand All @@ -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']
Expand Down

0 comments on commit 01043c9

Please sign in to comment.