Skip to content
Closed
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.
35 changes: 35 additions & 0 deletions superset/assets/javascripts/explore/stores/controls.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,22 @@ export const controls = {
renderTrigger: true,
},

fill_color_picker: {
label: t('Fill Color'),
description: t('Use to set fill color of geojson'),
type: 'ColorPickerControl',
default: colorPrimary,
renderTrigger: true,
},

stroke_color_picker: {
label: t('Stroke Color'),
description: t('Use to set stroke color of 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
29 changes: 29 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,35 @@ 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', null],
['stroke_color_picker', null],
['point_radius_scale', null],
],
},
],
},

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

import DeckGLContainer from './DeckGLContainer';

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

const convertGeoJsonProps = (p) => {
const obj = {}
Object.entries(p).forEach(
([key, value]) => { obj[(propertyMap[key]) ? propertyMap[key] : key] = value }
);
return obj
}

function DeckGeoJsonLayer(slice, payload, setControlValue) {
const fd = slice.formData;
const fillColor = fd.fill_color_picker;
const strokeColor = fd.stroke_color_picker;
const pointRadiusScale = fd.point_radius_scale;
const data = payload.data.geojson.features.map(d => ({
...d,
properties: convertGeoJsonProps(d.properties),
}));

const layer = new GeoJsonLayer({
id: 'geojson-layer',
data,
filled: true,
stroked: false,
extruded: true,
pointRadiusScale: pointRadiusScale,
});

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;
46 changes: 1 addition & 45 deletions superset/assets/visualizations/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,51 +42,7 @@ export const VIZ_TYPES = {
deck_screengrid: 'deck_screengrid',
deck_grid: 'deck_grid',
deck_hex: 'deck_hex',
deck_geojson: 'deck_geojson',
deck_path: 'deck_path',
};

const vizMap = {
[VIZ_TYPES.area]: require('./nvd3_vis.js'),
[VIZ_TYPES.bar]: require('./nvd3_vis.js'),
[VIZ_TYPES.big_number]: require('./big_number.js'),
[VIZ_TYPES.big_number_total]: require('./big_number.js'),
[VIZ_TYPES.box_plot]: require('./nvd3_vis.js'),
[VIZ_TYPES.bubble]: require('./nvd3_vis.js'),
[VIZ_TYPES.bullet]: require('./nvd3_vis.js'),
[VIZ_TYPES.cal_heatmap]: require('./cal_heatmap.js'),
[VIZ_TYPES.compare]: require('./nvd3_vis.js'),
[VIZ_TYPES.directed_force]: require('./directed_force.js'),
[VIZ_TYPES.chord]: require('./chord.jsx'),
[VIZ_TYPES.dist_bar]: require('./nvd3_vis.js'),
[VIZ_TYPES.filter_box]: require('./filter_box.jsx'),
[VIZ_TYPES.heatmap]: require('./heatmap.js'),
[VIZ_TYPES.histogram]: require('./histogram.js'),
[VIZ_TYPES.horizon]: require('./horizon.js'),
[VIZ_TYPES.iframe]: require('./iframe.js'),
[VIZ_TYPES.line]: require('./nvd3_vis.js'),
[VIZ_TYPES.time_pivot]: require('./nvd3_vis.js'),
[VIZ_TYPES.mapbox]: require('./mapbox.jsx'),
[VIZ_TYPES.markup]: require('./markup.js'),
[VIZ_TYPES.para]: require('./parallel_coordinates.js'),
[VIZ_TYPES.pie]: require('./nvd3_vis.js'),
[VIZ_TYPES.pivot_table]: require('./pivot_table.js'),
[VIZ_TYPES.sankey]: require('./sankey.js'),
[VIZ_TYPES.separator]: require('./markup.js'),
[VIZ_TYPES.sunburst]: require('./sunburst.js'),
[VIZ_TYPES.table]: require('./table.js'),
[VIZ_TYPES.time_table]: require('./time_table.jsx'),
[VIZ_TYPES.treemap]: require('./treemap.js'),
[VIZ_TYPES.country_map]: require('./country_map.js'),
[VIZ_TYPES.word_cloud]: require('./word_cloud.js'),
[VIZ_TYPES.world_map]: require('./world_map.js'),
[VIZ_TYPES.dual_line]: require('./nvd3_vis.js'),
[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'),
};
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
34 changes: 34 additions & 0 deletions superset/data/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -1522,6 +1522,37 @@ 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 Expand Up @@ -1550,3 +1581,6 @@ def load_bart_lines():
db.session.merge(tbl)
db.session.commit()
tbl.fetch_metadata()



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