[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:
Hugh A. Miles II 2017-12-22 17:40:08 -05:00 committed by Maxime Beauchemin
parent 69195f8d2d
commit f905726c24
9 changed files with 192 additions and 1 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 218 KiB

View File

@ -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',

View File

@ -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,

View File

@ -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;

View File

@ -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;

View File

@ -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()

View File

@ -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.

View File

@ -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"""