diff --git a/superset/assets/javascripts/chart/Chart.jsx b/superset/assets/javascripts/chart/Chart.jsx index 7bb71aca471..cc18bb4fe69 100644 --- a/superset/assets/javascripts/chart/Chart.jsx +++ b/superset/assets/javascripts/chart/Chart.jsx @@ -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); } } diff --git a/superset/assets/javascripts/explore/components/controls/TextControl.jsx b/superset/assets/javascripts/explore/components/controls/TextControl.jsx index bfe3f99177c..ed1238e5097 100644 --- a/superset/assets/javascripts/explore/components/controls/TextControl.jsx +++ b/superset/assets/javascripts/explore/components/controls/TextControl.jsx @@ -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} /> diff --git a/superset/assets/javascripts/explore/stores/controls.jsx b/superset/assets/javascripts/explore/stores/controls.jsx index 8e3420bd814..e8a34d950db 100644 --- a/superset/assets/javascripts/explore/stores/controls.jsx +++ b/superset/assets/javascripts/explore/stores/controls.jsx @@ -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'), diff --git a/superset/assets/javascripts/explore/stores/visTypes.js b/superset/assets/javascripts/explore/stores/visTypes.js index 9d62eada883..3135b2613dc 100644 --- a/superset/assets/javascripts/explore/stores/visTypes.js +++ b/superset/assets/javascripts/explore/stores/visTypes.js @@ -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], ], }, { diff --git a/superset/assets/package.json b/superset/assets/package.json index abc978c079d..fbfa32a27d5 100644 --- a/superset/assets/package.json +++ b/superset/assets/package.json @@ -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", diff --git a/superset/assets/visualizations/deckgl/DeckGLContainer.jsx b/superset/assets/visualizations/deckgl/DeckGLContainer.jsx index 3166917744f..1b7ca317cad 100644 --- a/superset/assets/visualizations/deckgl/DeckGLContainer.jsx +++ b/superset/assets/visualizations/deckgl/DeckGLContainer.jsx @@ -33,6 +33,7 @@ export default class DeckGLContainer extends React.Component { componentWillReceiveProps(nextProps) { this.setState(() => ({ viewport: { ...nextProps.viewport }, + previousViewport: this.state.viewport, })); } componentWillUnmount() { diff --git a/superset/assets/visualizations/deckgl/layers/arc.jsx b/superset/assets/visualizations/deckgl/layers/arc.jsx index ebeff3cb895..43583e01700 100644 --- a/superset/assets/visualizations/deckgl/layers/arc.jsx +++ b/superset/assets/visualizations/deckgl/layers/arc.jsx @@ -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( 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; diff --git a/superset/assets/visualizations/deckgl/layers/geojson.jsx b/superset/assets/visualizations/deckgl/layers/geojson.jsx index 7f0936304e1..fe7805a279f 100644 --- a/superset/assets/visualizations/deckgl/layers/geojson.jsx +++ b/superset/assets/visualizations/deckgl/layers/geojson.jsx @@ -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( 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( 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( { + 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( 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( , document.getElementById(slice.containerId), diff --git a/superset/assets/visualizations/deckgl/layers/screengrid.jsx b/superset/assets/visualizations/deckgl/layers/screengrid.jsx index 7494c67d3d0..7d6742e6e81 100644 --- a/superset/assets/visualizations/deckgl/layers/screengrid.jsx +++ b/superset/assets/visualizations/deckgl/layers/screengrid.jsx @@ -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(