Skip to content

Commit

Permalink
[geo] introduce "Auto Zoom" control (#4389)
Browse files Browse the repository at this point in the history
* [geo] introduce "Auto Zoom" control

On geospatial visualization, checking the "Auto Zoom" control makes it
such that the viewport is fitted to the data upon rendering the chart.

For dashboards with region filters, the map should jump to the right
position.

Eventually we should enhance this to fly and ease to the position in an
animated way.

* Added TODO notes
mistercrunch authored Feb 13, 2018

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature.
1 parent db24cef commit e0bbb0c
Showing 16 changed files with 141 additions and 34 deletions.
1 change: 1 addition & 0 deletions superset/assets/javascripts/chart/Chart.jsx
Original file line number Diff line number Diff line change
@@ -188,6 +188,7 @@ class Chart extends React.PureComponent {
});
this.props.actions.chartRenderingSucceeded(this.props.chartKey);
} catch (e) {
console.error(e); // eslint-disable-line
this.props.actions.chartRenderingFailed(e, this.props.chartKey);
}
}
Original file line number Diff line number Diff line change
@@ -13,6 +13,7 @@ const propTypes = {
]),
isFloat: PropTypes.bool,
isInt: PropTypes.bool,
disabled: PropTypes.bool,
};

const defaultProps = {
@@ -21,6 +22,7 @@ const defaultProps = {
value: '',
isInt: false,
isFloat: false,
disabled: false,
};

export default class TextControl extends React.Component {
@@ -63,6 +65,7 @@ export default class TextControl extends React.Component {
onChange={this.onChange}
onFocus={this.props.onFocus}
value={value}
disabled={this.props.disabled}
/>
</FormGroup>
</div>
8 changes: 8 additions & 0 deletions superset/assets/javascripts/explore/stores/controls.jsx
Original file line number Diff line number Diff line change
@@ -331,6 +331,14 @@ export const controls = {
default: false,
},

autozoom: {
type: 'CheckboxControl',
label: t('Auto Zoom'),
default: true,
renderTrigger: true,
description: t('When checked, the map will zoom to your data after each query'),
},

show_perc: {
type: 'CheckboxControl',
label: t('Show percentage'),
11 changes: 8 additions & 3 deletions superset/assets/javascripts/explore/stores/visTypes.js
Original file line number Diff line number Diff line change
@@ -369,7 +369,7 @@ export const visTypes = {
label: t('Map'),
controlSetRows: [
['mapbox_style', 'viewport'],
['color_picker', null],
['color_picker', 'autozoom'],
['grid_size', 'extruded'],
],
},
@@ -407,7 +407,7 @@ export const visTypes = {
label: t('Map'),
controlSetRows: [
['mapbox_style', 'viewport'],
['color_picker', null],
['color_picker', 'autozoom'],
['grid_size', 'extruded'],
],
},
@@ -448,7 +448,7 @@ export const visTypes = {
controlSetRows: [
['mapbox_style', 'viewport'],
['color_picker', 'line_width'],
['reverse_long_lat', null],
['reverse_long_lat', 'autozoom'],
],
},
{
@@ -479,6 +479,7 @@ export const visTypes = {
label: t('Map'),
controlSetRows: [
['mapbox_style', 'viewport'],
['autozoom', null],
],
},
{
@@ -521,6 +522,7 @@ export const visTypes = {
label: t('Map'),
controlSetRows: [
['mapbox_style', 'viewport'],
// TODO ['autozoom', null],
],
},
{
@@ -600,6 +602,7 @@ export const visTypes = {
label: t('Map'),
controlSetRows: [
['mapbox_style', 'viewport'],
['autozoom', null],
],
},
{
@@ -635,8 +638,10 @@ export const visTypes = {
},
{
label: t('Map'),
expanded: true,
controlSetRows: [
['mapbox_style', 'viewport'],
['autozoom', null],
],
},
{
4 changes: 3 additions & 1 deletion superset/assets/package.json
Original file line number Diff line number Diff line change
@@ -60,6 +60,7 @@
"distributions": "^1.0.0",
"dompurify": "^1.0.3",
"fastdom": "^1.0.6",
"geojson-extent": "^0.3.2",
"geolib": "^2.0.24",
"immutable": "^3.8.2",
"jed": "^1.1.1",
@@ -105,7 +106,7 @@
"supercluster": "https://github.com/georgeke/supercluster/tarball/ac3492737e7ce98e07af679623aad452373bbc40",
"underscore": "^1.8.3",
"urijs": "^1.18.10",
"viewport-mercator-project": "^2.1.0"
"viewport-mercator-project": "^5.0.0"
},
"devDependencies": {
"babel-cli": "^6.14.0",
@@ -137,6 +138,7 @@
"less": "^2.6.1",
"less-loader": "^4.0.3",
"mocha": "^3.2.0",
"npm-check-updates": "^2.14.0",
"react-addons-test-utils": "^15.6.2",
"react-test-renderer": "^15.6.2",
"redux-mock-store": "^1.2.3",
1 change: 1 addition & 0 deletions superset/assets/visualizations/deckgl/DeckGLContainer.jsx
Original file line number Diff line number Diff line change
@@ -33,6 +33,7 @@ export default class DeckGLContainer extends React.Component {
componentWillReceiveProps(nextProps) {
this.setState(() => ({
viewport: { ...nextProps.viewport },
previousViewport: this.state.viewport,
}));
}
componentWillUnmount() {
15 changes: 14 additions & 1 deletion superset/assets/visualizations/deckgl/layers/arc.jsx
Original file line number Diff line number Diff line change
@@ -8,6 +8,15 @@ import DeckGLContainer from './../DeckGLContainer';
import * as common from './common';
import sandboxedEval from '../../../javascripts/modules/sandbox';

function getPoints(data) {
const points = [];
data.forEach((d) => {
points.push(d.sourcePosition);
points.push(d.targetPosition);
});
return points;
}

function getLayer(formData, payload, slice) {
const fd = formData;
const fc = fd.color_picker;
@@ -32,11 +41,15 @@ function getLayer(formData, payload, slice) {

function deckArc(slice, payload, setControlValue) {
const layer = getLayer(slice.formData, payload, slice);
const viewport = {
let viewport = {
...slice.formData.viewport,
width: slice.width(),
height: slice.height(),
};

if (slice.formData.autozoom) {
viewport = common.fitViewport(viewport, getPoints(payload.data.arcs));
}
ReactDOM.render(
<DeckGLContainer
mapboxApiAccessToken={payload.data.mapboxApiKey}
24 changes: 24 additions & 0 deletions superset/assets/visualizations/deckgl/layers/common.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,30 @@
import dompurify from 'dompurify';
import { fitBounds } from 'viewport-mercator-project';

import sandboxedEval from '../../../javascripts/modules/sandbox';

export function getBounds(points) {
const latExt = d3.extent(points, d => d[1]);
const lngExt = d3.extent(points, d => d[0]);
return [
[lngExt[0], latExt[0]],
[lngExt[1], latExt[1]],
];
}

export function fitViewport(viewport, points, padding = 10) {
const bounds = getBounds(points);
return {
...viewport,
...fitBounds({
height: viewport.height,
width: viewport.width,
padding,
bounds,
}),
};
}

export function commonLayerProps(formData, slice) {
const fd = formData;
let onHover;
8 changes: 6 additions & 2 deletions superset/assets/visualizations/deckgl/layers/geojson.jsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
import React from 'react';
import ReactDOM from 'react-dom';

import { GeoJsonLayer } from 'deck.gl';
// TODO import geojsonExtent from 'geojson-extent';

import DeckGLContainer from './../DeckGLContainer';

import * as common from './common';
import { hexToRGB } from '../../../javascripts/modules/colors';
import sandboxedEval from '../../../javascripts/modules/sandbox';
@@ -100,6 +99,11 @@ function deckGeoJson(slice, payload, setControlValue) {
width: slice.width(),
height: slice.height(),
};
if (slice.formData.autozoom) {
// TODO get this to work
// viewport = common.fitViewport(viewport, geojsonExtent(payload.data.features));
}

ReactDOM.render(
<DeckGLContainer
mapboxApiAccessToken={payload.data.mapboxApiKey}
11 changes: 10 additions & 1 deletion superset/assets/visualizations/deckgl/layers/grid.jsx
Original file line number Diff line number Diff line change
@@ -37,13 +37,22 @@ function getLayer(formData, payload, slice) {
});
}

function getPoints(data) {
return data.map(d => d.position);
}

function deckGrid(slice, payload, setControlValue) {
const layer = getLayer(slice.formData, payload, slice);
const viewport = {
let viewport = {
...slice.formData.viewport,
width: slice.width(),
height: slice.height(),
};

if (slice.formData.autozoom) {
viewport = common.fitViewport(viewport, getPoints(payload.data.features));
}

ReactDOM.render(
<DeckGLContainer
mapboxApiAccessToken={payload.data.mapboxApiKey}
11 changes: 10 additions & 1 deletion superset/assets/visualizations/deckgl/layers/hex.jsx
Original file line number Diff line number Diff line change
@@ -37,13 +37,22 @@ function getLayer(formData, payload, slice) {
});
}

function getPoints(data) {
return data.map(d => d.position);
}

function deckHex(slice, payload, setControlValue) {
const layer = getLayer(slice.formData, payload, slice);
const viewport = {
let viewport = {
...slice.formData.viewport,
width: slice.width(),
height: slice.height(),
};

if (slice.formData.autozoom) {
viewport = common.fitViewport(viewport, getPoints(payload.data.features));
}

ReactDOM.render(
<DeckGLContainer
mapboxApiAccessToken={payload.data.mapboxApiKey}
15 changes: 14 additions & 1 deletion superset/assets/visualizations/deckgl/layers/path.jsx
Original file line number Diff line number Diff line change
@@ -33,13 +33,26 @@ function getLayer(formData, payload, slice) {
});
}

function getPoints(data) {
let points = [];
data.forEach((d) => {
points = points.concat(d.path);
});
return points;
}

function deckPath(slice, payload, setControlValue) {
const layer = getLayer(slice.formData, payload, slice);
const viewport = {
let viewport = {
...slice.formData.viewport,
width: slice.width(),
height: slice.height(),
};

if (slice.formData.autozoom) {
viewport = common.fitViewport(viewport, getPoints(payload.data.features));
}

ReactDOM.render(
<DeckGLContainer
mapboxApiAccessToken={payload.data.mapboxApiKey}
24 changes: 17 additions & 7 deletions superset/assets/visualizations/deckgl/layers/scatter.jsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,17 @@
import React from 'react';
import ReactDOM from 'react-dom';

import { ScatterplotLayer } from 'deck.gl';

import DeckGLContainer from './../DeckGLContainer';

import * as common from './common';
import { getColorFromScheme, hexToRGB } from '../../../javascripts/modules/colors';
import { unitToRadius } from '../../../javascripts/modules/geo';
import sandboxedEval from '../../../javascripts/modules/sandbox';

function getPoints(data) {
return data.map(d => d.position);
}

function getLayer(formData, payload, slice) {
const fd = formData;
const c = fd.color_picker || { r: 0, g: 0, b: 0, a: 1 };
@@ -50,17 +52,25 @@ function getLayer(formData, payload, slice) {

function deckScatter(slice, payload, setControlValue) {
const layer = getLayer(slice.formData, payload, slice);
const viewport = {
...slice.formData.viewport,
width: slice.width(),
height: slice.height(),
const fd = slice.formData;
const width = slice.width();
const height = slice.height();
let viewport = {
...fd.viewport,
width,
height,
};

if (fd.autozoom) {
viewport = common.fitViewport(viewport, getPoints(payload.data.features));
}

ReactDOM.render(
<DeckGLContainer
mapboxApiAccessToken={payload.data.mapboxApiKey}
viewport={viewport}
layers={[layer]}
mapStyle={slice.formData.mapbox_style}
mapStyle={fd.mapbox_style}
setControlValue={setControlValue}
/>,
document.getElementById(slice.containerId),
9 changes: 8 additions & 1 deletion superset/assets/visualizations/deckgl/layers/screengrid.jsx
Original file line number Diff line number Diff line change
@@ -37,13 +37,20 @@ function getLayer(formData, payload, slice) {
});
}

function getPoints(data) {
return data.map(d => d.position);
}

function deckScreenGrid(slice, payload, setControlValue) {
const layer = getLayer(slice.formData, payload, slice);
const viewport = {
let viewport = {
...slice.formData.viewport,
width: slice.width(),
height: slice.height(),
};
if (slice.formData.autozoom) {
viewport = common.fitViewport(viewport, getPoints(payload.data.features));
}
ReactDOM.render(
<DeckGLContainer
mapboxApiAccessToken={payload.data.mapboxApiKey}
Loading

0 comments on commit e0bbb0c

Please sign in to comment.