Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[geo] Added DeckGL GeoJson layer #4097

Merged
merged 8 commits into from
Dec 22, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
37 changes: 36 additions & 1 deletion superset/assets/javascripts/explore/stores/controls.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ const timeColumnOption = {
verbose_name: 'Time',
column_name: '__timestamp',
description: t(
'A reference to the [Time] configuration, taking granularity into ' +
'A reference to the [Time] configuration, taking granularity into ' +
'account'),
};
const sortAxisChoices = [
Expand Down Expand Up @@ -143,6 +143,22 @@ export const controls = {
renderTrigger: true,
},

fill_color_picker: {
label: t('Fill Color'),
description: t(' Set the opacity to 0 if you do not want to override the color specified in the GeoJSON'),
type: 'ColorPickerControl',
default: colorPrimary,
renderTrigger: true,
},

stroke_color_picker: {
label: t('Stroke Color'),
description: t(' Set the opacity to 0 if you do not want to override the color specified in the GeoJSON'),
type: 'ColorPickerControl',
default: colorPrimary,
renderTrigger: true,
},

metric: {
type: 'SelectControl',
label: t('Metric'),
Expand Down Expand Up @@ -496,6 +512,25 @@ export const controls = {
}),
},

geojson: {
type: 'SelectControl',
label: t('GeoJson Column'),
validators: [v.nonEmpty],
description: t('Select the geojson column'),
mapStateToProps: state => ({
choices: (state.datasource) ? state.datasource.all_cols : [],
}),
},

point_radius_scale: {
type: 'SelectControl',
freeForm: true,
label: t('Point Radius Scale'),
validators: [v.integer],
default: null,
choices: formatSelectOptions([0, 100, 200, 300, 500]),
},

all_columns_x: {
type: 'SelectControl',
label: 'X',
Expand Down
27 changes: 27 additions & 0 deletions superset/assets/javascripts/explore/stores/visTypes.js
Original file line number Diff line number Diff line change
Expand Up @@ -455,6 +455,33 @@ export const visTypes = {
},
},

deck_geojson: {
label: t('Deck.gl - geoJson'),
requiresTime: true,
controlPanelSections: [
{
label: t('Query'),
expanded: true,
controlSetRows: [
['geojson', 'row_limit'],
],
},
{
label: t('Map'),
controlSetRows: [
['mapbox_style', 'viewport'],
],
},
{
label: t('GeoJson Settings'),
controlSetRows: [
['fill_color_picker', 'stroke_color_picker'],
['point_radius_scale', null],
],
},
],
},

deck_scatter: {
label: t('Deck.gl - Scatter plot'),
requiresTime: true,
Expand Down
67 changes: 67 additions & 0 deletions superset/assets/visualizations/deckgl/geojson.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import React from 'react';
import ReactDOM from 'react-dom';
import { GeoJsonLayer } from 'deck.gl';
import { hexToRGB } from '../../javascripts/modules/colors';

import DeckGLContainer from './DeckGLContainer';

const propertyMap = {
fillColor: 'fillColor',
color: 'fillColor',
fill: 'fillColor',
'fill-color': 'fillColor',
strokeColor: 'strokeColor',
'stroke-color': 'strokeColor',
'stroke-width': 'strokeWidth',
};

const convertGeoJsonColorProps = (p, colors) => {
const obj = Object.assign(...Object.keys(p).map(k => ({
[(propertyMap[k]) ? propertyMap[k] : k]: p[k] })));

return {
...obj,
fillColor: (colors.fillColor[3] !== 0) ? colors.fillColor : hexToRGB(obj.fillColor),
strokeColor: (colors.strokeColor[3] !== 0) ? colors.strokeColor : hexToRGB(obj.strokeColor),
};
};

function DeckGeoJsonLayer(slice, payload, setControlValue) {
const fd = slice.formData;
const fc = fd.fill_color_picker;
const sc = fd.stroke_color_picker;
const 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 layer = new GeoJsonLayer({
id: 'geojson-layer',
data,
filled: true,
stroked: false,
extruded: true,
pointRadiusScale: fd.point_radius_scale,
});

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 = DeckGeoJsonLayer;
2 changes: 2 additions & 0 deletions superset/assets/visualizations/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ export const VIZ_TYPES = {
deck_grid: 'deck_grid',
deck_hex: 'deck_hex',
deck_path: 'deck_path',
deck_geojson: 'deck_geojson',
};

const vizMap = {
Expand Down Expand Up @@ -88,5 +89,6 @@ const vizMap = {
[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'),
};
export default vizMap;
3 changes: 3 additions & 0 deletions superset/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,9 @@ def load_examples(load_test_data):
print('Loading DECK.gl demo')
data.load_deck_dash()

print('Loading Paris geojson data')
data.load_paris_iris_geojson()

if load_test_data:
print('Loading [Unicode test data]')
data.load_unicode_test_data()
Expand Down
30 changes: 30 additions & 0 deletions superset/data/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -1522,6 +1522,36 @@ def load_flights():
obj.fetch_metadata()


def load_paris_iris_geojson():
tbl_name = 'paris_iris_mapping'

with gzip.open(os.path.join(DATA_FOLDER, 'paris_iris.json.gz')) as f:
df = pd.read_json(f)
df['features'] = df.features.map(json.dumps)

df.to_sql(
tbl_name,
db.engine,
if_exists='replace',
chunksize=500,
dtype={
'color': String(255),
'name': String(255),
'features': Text,
'type': 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 = "Map of Paris"
tbl.database = get_or_create_main_db()
db.session.merge(tbl)
db.session.commit()
tbl.fetch_metadata()


def load_bart_lines():
tbl_name = 'bart_lines'
with gzip.open(os.path.join(DATA_FOLDER, 'bart-lines.json.gz')) as f:
Expand Down
Binary file added superset/data/paris_iris.json.gz
Binary file not shown.
27 changes: 27 additions & 0 deletions superset/viz.py
Original file line number Diff line number Diff line change
Expand Up @@ -1942,6 +1942,33 @@ class DeckHex(BaseDeckGLViz):
verbose_name = _('Deck.gl - 3D HEX')


class DeckGeoJson(BaseDeckGLViz):

"""deck.gl's GeoJSONLayer"""

viz_type = 'deck_geojson'
verbose_name = _('Deck.gl - GeoJSON')

def query_obj(self):
d = super(DeckGeoJson, self).query_obj()
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'),
}


class EventFlowViz(BaseViz):

"""A visualization to explore patterns in event sequences"""
Expand Down