mirror of https://github.com/apache/superset.git
[geo] Added DeckGL GeoJson layer (#4097)
* added deckgl geojson layer * linting * fixed comments * addressed comments * added override with controls.color_picker > 0 * set var properly * set colors if property doesnt exist at all * refacator on property mapping
This commit is contained in:
parent
69195f8d2d
commit
f905726c24
Binary file not shown.
After Width: | Height: | Size: 218 KiB |
|
@ -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 = [
|
||||
|
@ -152,6 +152,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'),
|
||||
|
@ -505,6 +521,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',
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
|
@ -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 = {
|
||||
|
@ -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;
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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:
|
||||
|
|
Binary file not shown.
|
@ -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"""
|
||||
|
|
Loading…
Reference in New Issue