superset-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From maximebeauche...@apache.org
Subject [incubator-superset] branch master updated: [geo] Added DeckGL GeoJson layer (#4097)
Date Fri, 22 Dec 2017 22:40:12 GMT
This is an automated email from the ASF dual-hosted git repository.

maximebeauchemin pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-superset.git


The following commit(s) were added to refs/heads/master by this push:
     new f905726  [geo] Added DeckGL GeoJson layer (#4097)
f905726 is described below

commit f905726c24b1d1f65a55b38592cb51c0f45deb15
Author: Hugh A. Miles II <hughmil3s@gmail.com>
AuthorDate: Fri Dec 22 17:40:08 2017 -0500

    [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
---
 .../assets/images/viz_thumbnails/deck_geojson.png  | Bin 0 -> 223242 bytes
 .../assets/javascripts/explore/stores/controls.jsx |  37 +++++++++++-
 .../assets/javascripts/explore/stores/visTypes.js  |  27 +++++++++
 superset/assets/visualizations/deckgl/geojson.jsx  |  67 +++++++++++++++++++++
 superset/assets/visualizations/main.js             |   2 +
 superset/cli.py                                    |   3 +
 superset/data/__init__.py                          |  30 +++++++++
 superset/data/paris_iris.json.gz                   | Bin 0 -> 4427142 bytes
 superset/viz.py                                    |  27 +++++++++
 9 files changed, 192 insertions(+), 1 deletion(-)

diff --git a/superset/assets/images/viz_thumbnails/deck_geojson.png b/superset/assets/images/viz_thumbnails/deck_geojson.png
new file mode 100644
index 0000000..40d1b63
Binary files /dev/null and b/superset/assets/images/viz_thumbnails/deck_geojson.png differ
diff --git a/superset/assets/javascripts/explore/stores/controls.jsx b/superset/assets/javascripts/explore/stores/controls.jsx
index 95d4813..7aee160 100644
--- a/superset/assets/javascripts/explore/stores/controls.jsx
+++ b/superset/assets/javascripts/explore/stores/controls.jsx
@@ -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',
diff --git a/superset/assets/javascripts/explore/stores/visTypes.js b/superset/assets/javascripts/explore/stores/visTypes.js
index 88f6710..f4720f9 100644
--- a/superset/assets/javascripts/explore/stores/visTypes.js
+++ b/superset/assets/javascripts/explore/stores/visTypes.js
@@ -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,
diff --git a/superset/assets/visualizations/deckgl/geojson.jsx b/superset/assets/visualizations/deckgl/geojson.jsx
new file mode 100644
index 0000000..080d7ee
--- /dev/null
+++ b/superset/assets/visualizations/deckgl/geojson.jsx
@@ -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;
diff --git a/superset/assets/visualizations/main.js b/superset/assets/visualizations/main.js
index 06d30a1..e692c10 100644
--- a/superset/assets/visualizations/main.js
+++ b/superset/assets/visualizations/main.js
@@ -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;
diff --git a/superset/cli.py b/superset/cli.py
index 56ead72..14a592b 100755
--- a/superset/cli.py
+++ b/superset/cli.py
@@ -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()
diff --git a/superset/data/__init__.py b/superset/data/__init__.py
index b3cb4a8..bddc014 100644
--- a/superset/data/__init__.py
+++ b/superset/data/__init__.py
@@ -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:
diff --git a/superset/data/paris_iris.json.gz b/superset/data/paris_iris.json.gz
new file mode 100644
index 0000000..4a964c9
Binary files /dev/null and b/superset/data/paris_iris.json.gz differ
diff --git a/superset/viz.py b/superset/viz.py
index 55f2603..2a6b494 100644
--- a/superset/viz.py
+++ b/superset/viz.py
@@ -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"""

-- 
To stop receiving notification emails like this one, please contact
['"commits@superset.apache.org" <commits@superset.apache.org>'].

Mime
View raw message