From 8f495caf2130e0de4b9c81cd35f2730863aa64fb Mon Sep 17 00:00:00 2001 From: Krist Wongsuphasawat Date: Thu, 30 Aug 2018 16:05:17 -0700 Subject: [PATCH 1/8] Break MapBox into smaller pieces --- .../src/visualizations/MapBox/MapBox.css | 16 ++ .../src/visualizations/MapBox/MapBox.jsx | 188 +++++++++++++++ .../ScatterPlotGlowOverlay.jsx} | 217 +++--------------- superset/assets/src/visualizations/index.js | 2 +- superset/assets/src/visualizations/mapbox.css | 16 -- 5 files changed, 235 insertions(+), 204 deletions(-) create mode 100644 superset/assets/src/visualizations/MapBox/MapBox.css create mode 100644 superset/assets/src/visualizations/MapBox/MapBox.jsx rename superset/assets/src/visualizations/{mapbox.jsx => MapBox/ScatterPlotGlowOverlay.jsx} (60%) delete mode 100644 superset/assets/src/visualizations/mapbox.css diff --git a/superset/assets/src/visualizations/MapBox/MapBox.css b/superset/assets/src/visualizations/MapBox/MapBox.css new file mode 100644 index 0000000000000..477d09a906232 --- /dev/null +++ b/superset/assets/src/visualizations/MapBox/MapBox.css @@ -0,0 +1,16 @@ +.mapbox div.widget .slice_container { + cursor: grab; + cursor: -moz-grab; + cursor: -webkit-grab; + overflow: hidden; +} + +.mapbox div.widget .slice_container:active { + cursor: grabbing; + cursor: -moz-grabbing; + cursor: -webkit-grabbing; +} + +.mapbox .slice_container div { + padding-top: 0px; +} diff --git a/superset/assets/src/visualizations/MapBox/MapBox.jsx b/superset/assets/src/visualizations/MapBox/MapBox.jsx new file mode 100644 index 0000000000000..c54f02dd5a046 --- /dev/null +++ b/superset/assets/src/visualizations/MapBox/MapBox.jsx @@ -0,0 +1,188 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import ReactDOM from 'react-dom'; +import MapGL from 'react-map-gl'; +import Immutable from 'immutable'; +import supercluster from 'supercluster'; +import ViewportMercator from 'viewport-mercator-project'; +import ScatterPlotGlowOverlay from './ScatterPlotGlowOverlay'; + +import { + DEFAULT_LONGITUDE, + DEFAULT_LATITUDE, + DEFAULT_ZOOM, +} from '../../utils/common'; +import './MapBox.css'; + +const NOOP = () => {}; +const DEFAULT_POINT_RADIUS = 60; +const DEFAULT_MAX_ZOOM = 16; + +const propTypes = { + aggregatorName: PropTypes.string, + clusterer: PropTypes.object, + setControlValue: PropTypes.func, + globalOpacity: PropTypes.number, + mapStyle: PropTypes.string, + mapboxApiKey: PropTypes.string, + pointRadius: PropTypes.number, + pointRadiusUnit: PropTypes.string, + renderWhileDragging: PropTypes.bool, + rgb: PropTypes.array, + sliceHeight: PropTypes.number, + sliceWidth: PropTypes.number, + viewportLatitude: PropTypes.number, + viewportLongitude: PropTypes.number, + viewportZoom: PropTypes.number, +}; + +const defaultProps = { + viewportLatitude: DEFAULT_LATITUDE, + viewportLongitude: DEFAULT_LONGITUDE, + viewportZoom: DEFAULT_ZOOM, +}; + +class MapBox extends React.Component { + constructor(props) { + super(props); + + const { + viewportLatitude: latitude, + viewportLongitude: longitude, + viewportZoom: zoom, + } = this.props; + + this.state = { + viewport: { + longitude, + latitude, + zoom, + startDragLngLat: [longitude, latitude], + }, + }; + this.onViewportChange = this.onViewportChange.bind(this); + } + + onViewportChange(viewport) { + this.setState({ viewport }); + this.props.setControlValue('viewport_longitude', viewport.longitude); + this.props.setControlValue('viewport_latitude', viewport.latitude); + this.props.setControlValue('viewport_zoom', viewport.zoom); + } + + render() { + const mercator = new ViewportMercator({ + width: this.props.sliceWidth, + height: this.props.sliceHeight, + longitude: this.state.viewport.longitude, + latitude: this.state.viewport.latitude, + zoom: this.state.viewport.zoom, + }); + const topLeft = mercator.unproject([0, 0]); + const bottomRight = mercator.unproject([this.props.sliceWidth, this.props.sliceHeight]); + const bbox = [topLeft[0], bottomRight[1], bottomRight[0], topLeft[1]]; + const clusters = this.props.clusterer.getClusters(bbox, Math.round(this.state.viewport.zoom)); + const isDragging = this.state.viewport.isDragging === undefined ? false : + this.state.viewport.isDragging; + return ( + + + + ); + } +} + +MapBox.propTypes = propTypes; +MapBox.defaultProps = defaultProps; + +function mapbox(slice, payload, setControlValue) { + const { selector } = slice; + const { + aggregatorName: aggName, + clusteringRadius, + color, + customMetric, + geoJSON, + } = payload.data; + + // Validate mapbox color + const rgb = /^rgb\((\d{1,3}),\s*(\d{1,3}),\s*(\d{1,3})\)$/ + .exec(color); + if (rgb === null) { + slice.error('Color field must be of form \'rgb(%d, %d, %d)\''); + return; + } + + let reducer; + + if (aggName === 'sum' || !customMetric) { + reducer = (a, b) => a + b; + } else if (aggName === 'min') { + reducer = Math.min; + } else if (aggName === 'max') { + reducer = Math.max; + } else { + reducer = function (a, b) { + if (a instanceof Array) { + if (b instanceof Array) { + return a.concat(b); + } + a.push(b); + return a; + } + if (b instanceof Array) { + b.push(a); + return b; + } + return [a, b]; + }; + } + + const clusterer = supercluster({ + radius: clusteringRadius, + maxZoom: DEFAULT_MAX_ZOOM, + metricKey: 'metric', + metricReducer: reducer, + }); + clusterer.load(geoJSON.features); + + ReactDOM.render( + , + document.querySelector(selector), + ); +} + +export default mapbox; diff --git a/superset/assets/src/visualizations/mapbox.jsx b/superset/assets/src/visualizations/MapBox/ScatterPlotGlowOverlay.jsx similarity index 60% rename from superset/assets/src/visualizations/mapbox.jsx rename to superset/assets/src/visualizations/MapBox/ScatterPlotGlowOverlay.jsx index 1a156ae520e9d..f09829e338233 100644 --- a/superset/assets/src/visualizations/mapbox.jsx +++ b/superset/assets/src/visualizations/MapBox/ScatterPlotGlowOverlay.jsx @@ -1,26 +1,39 @@ -/* eslint-disable no-param-reassign */ -/* eslint-disable react/no-multi-comp */ import d3 from 'd3'; +import Immutable from 'immutable'; import React from 'react'; import PropTypes from 'prop-types'; -import ReactDOM from 'react-dom'; -import MapGL from 'react-map-gl'; -import Immutable from 'immutable'; -import supercluster from 'supercluster'; import ViewportMercator from 'viewport-mercator-project'; - import { kmToPixels, rgbLuminance, isNumeric, MILES_PER_KM, - DEFAULT_LONGITUDE, - DEFAULT_LATITUDE, - DEFAULT_ZOOM, -} from '../utils/common'; -import './mapbox.css'; +} from '../../utils/common'; -const NOOP = () => {}; +const propTypes = { + locations: PropTypes.instanceOf(Immutable.List).isRequired, + lngLatAccessor: PropTypes.func, + renderWhileDragging: PropTypes.bool, + globalOpacity: PropTypes.number, + dotRadius: PropTypes.number, + dotFill: PropTypes.string, + compositeOperation: PropTypes.string, +}; + +const defaultProps = { + lngLatAccessor: location => [location.get(0), location.get(1)], + renderWhileDragging: true, + dotRadius: 4, + dotFill: '#1FBAD6', + globalOpacity: 1, + // Same as browser default. + compositeOperation: 'source-over', +}; + +const contextTypes = { + viewport: PropTypes.object, + isDragging: PropTypes.bool, +}; class ScatterPlotGlowOverlay extends React.Component { componentDidMount() { @@ -215,179 +228,9 @@ class ScatterPlotGlowOverlay extends React.Component { ); } } -ScatterPlotGlowOverlay.propTypes = { - locations: PropTypes.instanceOf(Immutable.List).isRequired, - lngLatAccessor: PropTypes.func, - renderWhileDragging: PropTypes.bool, - globalOpacity: PropTypes.number, - dotRadius: PropTypes.number, - dotFill: PropTypes.string, - compositeOperation: PropTypes.string, -}; -ScatterPlotGlowOverlay.defaultProps = { - lngLatAccessor: location => [location.get(0), location.get(1)], - renderWhileDragging: true, - dotRadius: 4, - dotFill: '#1FBAD6', - globalOpacity: 1, - // Same as browser default. - compositeOperation: 'source-over', -}; -ScatterPlotGlowOverlay.contextTypes = { - viewport: PropTypes.object, - isDragging: PropTypes.bool, -}; - -class MapboxViz extends React.Component { - constructor(props) { - super(props); - const longitude = this.props.viewportLongitude || DEFAULT_LONGITUDE; - const latitude = this.props.viewportLatitude || DEFAULT_LATITUDE; - - this.state = { - viewport: { - longitude, - latitude, - zoom: this.props.viewportZoom || DEFAULT_ZOOM, - startDragLngLat: [longitude, latitude], - }, - }; - this.onViewportChange = this.onViewportChange.bind(this); - } - - onViewportChange(viewport) { - this.setState({ viewport }); - this.props.setControlValue('viewport_longitude', viewport.longitude); - this.props.setControlValue('viewport_latitude', viewport.latitude); - this.props.setControlValue('viewport_zoom', viewport.zoom); - } - - render() { - const mercator = new ViewportMercator({ - width: this.props.sliceWidth, - height: this.props.sliceHeight, - longitude: this.state.viewport.longitude, - latitude: this.state.viewport.latitude, - zoom: this.state.viewport.zoom, - }); - const topLeft = mercator.unproject([0, 0]); - const bottomRight = mercator.unproject([this.props.sliceWidth, this.props.sliceHeight]); - const bbox = [topLeft[0], bottomRight[1], bottomRight[0], topLeft[1]]; - const clusters = this.props.clusterer.getClusters(bbox, Math.round(this.state.viewport.zoom)); - const isDragging = this.state.viewport.isDragging === undefined ? false : - this.state.viewport.isDragging; - return ( - - - - ); - } -} -MapboxViz.propTypes = { - aggregatorName: PropTypes.string, - clusterer: PropTypes.object, - setControlValue: PropTypes.func, - globalOpacity: PropTypes.number, - mapStyle: PropTypes.string, - mapboxApiKey: PropTypes.string, - pointRadius: PropTypes.number, - pointRadiusUnit: PropTypes.string, - renderWhileDragging: PropTypes.bool, - rgb: PropTypes.array, - sliceHeight: PropTypes.number, - sliceWidth: PropTypes.number, - viewportLatitude: PropTypes.number, - viewportLongitude: PropTypes.number, - viewportZoom: PropTypes.number, -}; - -function mapbox(slice, json, setControlValue) { - const div = d3.select(slice.selector); - const DEFAULT_POINT_RADIUS = 60; - const DEFAULT_MAX_ZOOM = 16; - - // Validate mapbox color - const rgb = /^rgb\((\d{1,3}),\s*(\d{1,3}),\s*(\d{1,3})\)$/.exec(json.data.color); - if (rgb === null) { - slice.error('Color field must be of form \'rgb(%d, %d, %d)\''); - return; - } - - const aggName = json.data.aggregatorName; - let reducer; - - if (aggName === 'sum' || !json.data.customMetric) { - reducer = function (a, b) { - return a + b; - }; - } else if (aggName === 'min') { - reducer = Math.min; - } else if (aggName === 'max') { - reducer = Math.max; - } else { - reducer = function (a, b) { - if (a instanceof Array) { - if (b instanceof Array) { - return a.concat(b); - } - a.push(b); - return a; - } - if (b instanceof Array) { - b.push(a); - return b; - } - return [a, b]; - }; - } - - const clusterer = supercluster({ - radius: json.data.clusteringRadius, - maxZoom: DEFAULT_MAX_ZOOM, - metricKey: 'metric', - metricReducer: reducer, - }); - clusterer.load(json.data.geoJSON.features); - - div.selectAll('*').remove(); - ReactDOM.render( - , - div.node(), - ); -} +ScatterPlotGlowOverlay.propTypes = propTypes; +ScatterPlotGlowOverlay.defaultProps = defaultProps; +ScatterPlotGlowOverlay.contextTypes = contextTypes; -module.exports = mapbox; +export default ScatterPlotGlowOverlay; diff --git a/superset/assets/src/visualizations/index.js b/superset/assets/src/visualizations/index.js index c322bef52831d..fc4b2ec51a8de 100644 --- a/superset/assets/src/visualizations/index.js +++ b/superset/assets/src/visualizations/index.js @@ -89,7 +89,7 @@ const vizMap = { [VIZ_TYPES.line_multi]: () => loadVis(import(/* webpackChunkName: "line_multi" */ './line_multi.js')), [VIZ_TYPES.time_pivot]: loadNvd3, - [VIZ_TYPES.mapbox]: () => loadVis(import(/* webpackChunkName: "mapbox" */ './mapbox.jsx')), + [VIZ_TYPES.mapbox]: () => loadVis(import(/* webpackChunkName: "mapbox" */ './MapBox/MapBox.jsx')), [VIZ_TYPES.markup]: () => loadVis(import(/* webpackChunkName: "markup" */ './markup.js')), [VIZ_TYPES.para]: () => loadVis(import(/* webpackChunkName: "parallel_coordinates" */ './parallel_coordinates.js')), diff --git a/superset/assets/src/visualizations/mapbox.css b/superset/assets/src/visualizations/mapbox.css deleted file mode 100644 index babb33be0eace..0000000000000 --- a/superset/assets/src/visualizations/mapbox.css +++ /dev/null @@ -1,16 +0,0 @@ -.mapbox div.widget .slice_container { - cursor: grab; - cursor: -moz-grab; - cursor: -webkit-grab; - overflow: hidden; -} - -.mapbox div.widget .slice_container:active { - cursor: grabbing; - cursor: -moz-grabbing; - cursor: -webkit-grabbing; -} - -.mapbox .slice_container div { - padding-top: 0px; -} From 5a233999e3d45afa46ebd33ccf9d0202ebf2ed36 Mon Sep 17 00:00:00 2001 From: Krist Wongsuphasawat Date: Thu, 30 Aug 2018 16:12:15 -0700 Subject: [PATCH 2/8] Replace React.createElement with regular jsx --- .../MapBox/ScatterPlotGlowOverlay.jsx | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/superset/assets/src/visualizations/MapBox/ScatterPlotGlowOverlay.jsx b/superset/assets/src/visualizations/MapBox/ScatterPlotGlowOverlay.jsx index f09829e338233..ec6b09773bda8 100644 --- a/superset/assets/src/visualizations/MapBox/ScatterPlotGlowOverlay.jsx +++ b/superset/assets/src/visualizations/MapBox/ScatterPlotGlowOverlay.jsx @@ -75,8 +75,7 @@ class ScatterPlotGlowOverlay extends React.Component { redraw() { const props = this.props; const pixelRatio = window.devicePixelRatio || 1; - const canvas = this.refs.overlay; - const ctx = canvas.getContext('2d'); + const ctx = this.canvas.getContext('2d'); const radius = props.dotRadius; const mercator = new ViewportMercator(props); const rgb = props.rgb; @@ -211,11 +210,11 @@ class ScatterPlotGlowOverlay extends React.Component { const { globalOpacity } = this.props; const pixelRatio = window.devicePixelRatio || 1; return ( - React.createElement('canvas', { - ref: 'overlay', - width: width * pixelRatio, - height: height * pixelRatio, - style: { + { this.canvas = c; }} + width={width * pixelRatio} + height={height * pixelRatio} + style={{ width: `${width}px`, height: `${height}px`, position: 'absolute', @@ -223,8 +222,8 @@ class ScatterPlotGlowOverlay extends React.Component { opacity: globalOpacity, left: 0, top: 0, - }, - }) + }} + /> ); } } From 0ee988d20715b5425dd6d387fbd853cc74c90d50 Mon Sep 17 00:00:00 2001 From: Krist Wongsuphasawat Date: Thu, 30 Aug 2018 16:12:21 -0700 Subject: [PATCH 3/8] detach setControlValue --- .../assets/src/visualizations/MapBox/MapBox.jsx | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/superset/assets/src/visualizations/MapBox/MapBox.jsx b/superset/assets/src/visualizations/MapBox/MapBox.jsx index c54f02dd5a046..60bbc15e039f9 100644 --- a/superset/assets/src/visualizations/MapBox/MapBox.jsx +++ b/superset/assets/src/visualizations/MapBox/MapBox.jsx @@ -21,10 +21,10 @@ const DEFAULT_MAX_ZOOM = 16; const propTypes = { aggregatorName: PropTypes.string, clusterer: PropTypes.object, - setControlValue: PropTypes.func, globalOpacity: PropTypes.number, mapStyle: PropTypes.string, mapboxApiKey: PropTypes.string, + onViewportChange: PropTypes.func, pointRadius: PropTypes.number, pointRadiusUnit: PropTypes.string, renderWhileDragging: PropTypes.bool, @@ -37,6 +37,7 @@ const propTypes = { }; const defaultProps = { + onViewportChange: NOOP, viewportLatitude: DEFAULT_LATITUDE, viewportLongitude: DEFAULT_LONGITUDE, viewportZoom: DEFAULT_ZOOM, @@ -65,9 +66,7 @@ class MapBox extends React.Component { onViewportChange(viewport) { this.setState({ viewport }); - this.props.setControlValue('viewport_longitude', viewport.longitude); - this.props.setControlValue('viewport_latitude', viewport.latitude); - this.props.setControlValue('viewport_zoom', viewport.zoom); + this.props.onViewportChange(viewport); } render() { @@ -179,7 +178,11 @@ function mapbox(slice, payload, setControlValue) { clusterer={clusterer} pointRadius={DEFAULT_POINT_RADIUS} aggregatorName={aggName} - setControlValue={setControlValue || NOOP} + onViewportChange={({ latitude, longitude, zoom }) => { + setControlValue('viewport_longitude', longitude); + setControlValue('viewport_latitude', latitude); + setControlValue('viewport_zoom', zoom); + }} />, document.querySelector(selector), ); From b4e2ea065391016d9925fd9190badb221fff04a7 Mon Sep 17 00:00:00 2001 From: Krist Wongsuphasawat Date: Thu, 30 Aug 2018 16:35:36 -0700 Subject: [PATCH 4/8] enable render trigger --- superset/assets/src/explore/controls.jsx | 3 +++ 1 file changed, 3 insertions(+) diff --git a/superset/assets/src/explore/controls.jsx b/superset/assets/src/explore/controls.jsx index 8c063a7fc70db..6df8f49d1926a 100644 --- a/superset/assets/src/explore/controls.jsx +++ b/superset/assets/src/explore/controls.jsx @@ -1802,6 +1802,7 @@ export const controls = { viewport_zoom: { type: 'TextControl', label: t('Zoom'), + renderTrigger: true, isFloat: true, default: 11, description: t('Zoom level of the map'), @@ -1813,6 +1814,7 @@ export const controls = { viewport_latitude: { type: 'TextControl', label: t('Default latitude'), + renderTrigger: true, default: 37.772123, isFloat: true, description: t('Latitude of default viewport'), @@ -1824,6 +1826,7 @@ export const controls = { viewport_longitude: { type: 'TextControl', label: t('Default longitude'), + renderTrigger: true, default: -122.405293, isFloat: true, description: t('Longitude of default viewport'), From ddf9ccdb058a7995b510d39d9611054bdabef336 Mon Sep 17 00:00:00 2001 From: Krist Wongsuphasawat Date: Thu, 30 Aug 2018 16:36:24 -0700 Subject: [PATCH 5/8] Pass explicit props rather than pass all that exists in payload.data. Also use formData when possible. --- .../src/visualizations/MapBox/MapBox.jsx | 44 ++++++++++++++----- 1 file changed, 33 insertions(+), 11 deletions(-) diff --git a/superset/assets/src/visualizations/MapBox/MapBox.jsx b/superset/assets/src/visualizations/MapBox/MapBox.jsx index 60bbc15e039f9..7a8d430c890cf 100644 --- a/superset/assets/src/visualizations/MapBox/MapBox.jsx +++ b/superset/assets/src/visualizations/MapBox/MapBox.jsx @@ -38,6 +38,7 @@ const propTypes = { const defaultProps = { onViewportChange: NOOP, + pointRadius: DEFAULT_POINT_RADIUS, viewportLatitude: DEFAULT_LATITUDE, viewportLongitude: DEFAULT_LONGITUDE, viewportZoom: DEFAULT_ZOOM, @@ -119,14 +120,28 @@ MapBox.propTypes = propTypes; MapBox.defaultProps = defaultProps; function mapbox(slice, payload, setControlValue) { - const { selector } = slice; + const { formData, selector } = slice; const { - aggregatorName: aggName, - clusteringRadius, - color, customMetric, geoJSON, + mapboxApiKey, } = payload.data; + const { + clustering_radius: clusteringRadius, + global_opacity: globalOpacity, + mapbox_color: color, + mapbox_style: mapStyle, + pandas_aggfunc: aggregatorName, + point_radius: pointRadius, + point_radius_unit: pointRadiusUnit, + render_while_dragging: renderWhileDragging, + viewport_latitude: viewportLatitude, + viewport_longitude: viewportLongitude, + viewport_zoom: viewportZoom, + } = formData; + + console.log('ayload.data', payload.data, slice.formData); + // return; // Validate mapbox color const rgb = /^rgb\((\d{1,3}),\s*(\d{1,3}),\s*(\d{1,3})\)$/ @@ -138,7 +153,7 @@ function mapbox(slice, payload, setControlValue) { let reducer; - if (aggName === 'sum' || !customMetric) { + if (aggregatorName === 'sum' || !customMetric) { reducer = (a, b) => a + b; } else if (aggName === 'min') { reducer = Math.min; @@ -171,18 +186,25 @@ function mapbox(slice, payload, setControlValue) { ReactDOM.render( { setControlValue('viewport_longitude', longitude); setControlValue('viewport_latitude', latitude); setControlValue('viewport_zoom', zoom); }} + pointRadius={pointRadius === 'Auto' ? DEFAULT_POINT_RADIUS : pointRadius} + pointRadiusUnit={pointRadiusUnit} + renderWhileDragging={renderWhileDragging} + rgb={rgb} + sliceHeight={slice.height()} + sliceWidth={slice.width()} + viewportLatitude={viewportLatitude} + viewportLongitude={viewportLongitude} + viewportZoom={viewportZoom} />, document.querySelector(selector), ); From 3abba9fff6971391cb7d0f1d482b0ef7b4340215 Mon Sep 17 00:00:00 2001 From: Krist Wongsuphasawat Date: Thu, 30 Aug 2018 16:47:52 -0700 Subject: [PATCH 6/8] Rename sliceWidth, sliceHeight to width, height. Use deconstructor. Extract function. --- .../src/visualizations/MapBox/MapBox.jsx | 122 ++++++++++-------- 1 file changed, 67 insertions(+), 55 deletions(-) diff --git a/superset/assets/src/visualizations/MapBox/MapBox.jsx b/superset/assets/src/visualizations/MapBox/MapBox.jsx index 7a8d430c890cf..cf9b0f10a2821 100644 --- a/superset/assets/src/visualizations/MapBox/MapBox.jsx +++ b/superset/assets/src/visualizations/MapBox/MapBox.jsx @@ -19,6 +19,8 @@ const DEFAULT_POINT_RADIUS = 60; const DEFAULT_MAX_ZOOM = 16; const propTypes = { + width: PropTypes.number, + height: PropTypes.number, aggregatorName: PropTypes.string, clusterer: PropTypes.object, globalOpacity: PropTypes.number, @@ -29,16 +31,16 @@ const propTypes = { pointRadiusUnit: PropTypes.string, renderWhileDragging: PropTypes.bool, rgb: PropTypes.array, - sliceHeight: PropTypes.number, - sliceWidth: PropTypes.number, viewportLatitude: PropTypes.number, viewportLongitude: PropTypes.number, viewportZoom: PropTypes.number, }; const defaultProps = { + globalOpacity: 1, onViewportChange: NOOP, pointRadius: DEFAULT_POINT_RADIUS, + pointRadiusUnit: 'Pixels', viewportLatitude: DEFAULT_LATITUDE, viewportLongitude: DEFAULT_LONGITUDE, viewportZoom: DEFAULT_ZOOM, @@ -71,41 +73,55 @@ class MapBox extends React.Component { } render() { + const { + width, + height, + aggregatorName, + globalOpacity, + mapStyle, + mapboxApiKey, + pointRadius, + pointRadiusUnit, + renderWhileDragging, + rgb, + } = this.props; + const { viewport } = this.state; + const { latitude, longitude, zoom } = viewport; const mercator = new ViewportMercator({ - width: this.props.sliceWidth, - height: this.props.sliceHeight, - longitude: this.state.viewport.longitude, - latitude: this.state.viewport.latitude, - zoom: this.state.viewport.zoom, + width, + height, + longitude, + latitude, + zoom, }); const topLeft = mercator.unproject([0, 0]); - const bottomRight = mercator.unproject([this.props.sliceWidth, this.props.sliceHeight]); + const bottomRight = mercator.unproject([width, height]); const bbox = [topLeft[0], bottomRight[1], bottomRight[0], topLeft[1]]; - const clusters = this.props.clusterer.getClusters(bbox, Math.round(this.state.viewport.zoom)); - const isDragging = this.state.viewport.isDragging === undefined ? false : - this.state.viewport.isDragging; + const clusters = this.props.clusterer.getClusters(bbox, Math.round(zoom)); + const isDragging = viewport.isDragging === undefined ? false : + viewport.isDragging; return ( a + b; + } else if (aggName === 'min') { + return Math.min; + } else if (aggName === 'max') { + return Math.max; + } + return function (a, b) { + if (a instanceof Array) { + if (b instanceof Array) { + return a.concat(b); + } + a.push(b); + return a; + } + if (b instanceof Array) { + b.push(a); + return b; + } + return [a, b]; + }; +} + function mapbox(slice, payload, setControlValue) { const { formData, selector } = slice; const { @@ -140,9 +180,6 @@ function mapbox(slice, payload, setControlValue) { viewport_zoom: viewportZoom, } = formData; - console.log('ayload.data', payload.data, slice.formData); - // return; - // Validate mapbox color const rgb = /^rgb\((\d{1,3}),\s*(\d{1,3}),\s*(\d{1,3})\)$/ .exec(color); @@ -151,41 +188,18 @@ function mapbox(slice, payload, setControlValue) { return; } - let reducer; - - if (aggregatorName === 'sum' || !customMetric) { - reducer = (a, b) => a + b; - } else if (aggName === 'min') { - reducer = Math.min; - } else if (aggName === 'max') { - reducer = Math.max; - } else { - reducer = function (a, b) { - if (a instanceof Array) { - if (b instanceof Array) { - return a.concat(b); - } - a.push(b); - return a; - } - if (b instanceof Array) { - b.push(a); - return b; - } - return [a, b]; - }; - } - const clusterer = supercluster({ radius: clusteringRadius, maxZoom: DEFAULT_MAX_ZOOM, metricKey: 'metric', - metricReducer: reducer, + metricReducer: createReducer(aggregatorName, customMetric), }); clusterer.load(geoJSON.features); ReactDOM.render( Date: Wed, 5 Sep 2018 11:16:42 -0700 Subject: [PATCH 7/8] use arrow function --- superset/assets/src/visualizations/MapBox/MapBox.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/superset/assets/src/visualizations/MapBox/MapBox.jsx b/superset/assets/src/visualizations/MapBox/MapBox.jsx index cf9b0f10a2821..23d734628d50f 100644 --- a/superset/assets/src/visualizations/MapBox/MapBox.jsx +++ b/superset/assets/src/visualizations/MapBox/MapBox.jsx @@ -122,7 +122,7 @@ class MapBox extends React.Component { compositeOperation={'screen'} renderWhileDragging={renderWhileDragging} aggregatorName={aggregatorName} - lngLatAccessor={function (location) { + lngLatAccessor={location => { const coordinates = location.get('geometry').get('coordinates'); return [coordinates.get(0), coordinates.get(1)]; }} From 71065f9d4e09f2f52ca06b288b075d166f911573 Mon Sep 17 00:00:00 2001 From: Krist Wongsuphasawat Date: Wed, 5 Sep 2018 11:29:12 -0700 Subject: [PATCH 8/8] fix linting and remove css --- .../assets/src/visualizations/MapBox/MapBox.css | 13 ------------- .../assets/src/visualizations/MapBox/MapBox.jsx | 2 +- .../MapBox/ScatterPlotGlowOverlay.jsx | 14 ++++++++++++-- 3 files changed, 13 insertions(+), 16 deletions(-) diff --git a/superset/assets/src/visualizations/MapBox/MapBox.css b/superset/assets/src/visualizations/MapBox/MapBox.css index 477d09a906232..3ec640dac6e99 100644 --- a/superset/assets/src/visualizations/MapBox/MapBox.css +++ b/superset/assets/src/visualizations/MapBox/MapBox.css @@ -1,16 +1,3 @@ -.mapbox div.widget .slice_container { - cursor: grab; - cursor: -moz-grab; - cursor: -webkit-grab; - overflow: hidden; -} - -.mapbox div.widget .slice_container:active { - cursor: grabbing; - cursor: -moz-grabbing; - cursor: -webkit-grabbing; -} - .mapbox .slice_container div { padding-top: 0px; } diff --git a/superset/assets/src/visualizations/MapBox/MapBox.jsx b/superset/assets/src/visualizations/MapBox/MapBox.jsx index 23d734628d50f..81f41f074b1d2 100644 --- a/superset/assets/src/visualizations/MapBox/MapBox.jsx +++ b/superset/assets/src/visualizations/MapBox/MapBox.jsx @@ -122,7 +122,7 @@ class MapBox extends React.Component { compositeOperation={'screen'} renderWhileDragging={renderWhileDragging} aggregatorName={aggregatorName} - lngLatAccessor={location => { + lngLatAccessor={(location) => { const coordinates = location.get('geometry').get('coordinates'); return [coordinates.get(0), coordinates.get(1)]; }} diff --git a/superset/assets/src/visualizations/MapBox/ScatterPlotGlowOverlay.jsx b/superset/assets/src/visualizations/MapBox/ScatterPlotGlowOverlay.jsx index ec6b09773bda8..ea4e115de3b00 100644 --- a/superset/assets/src/visualizations/MapBox/ScatterPlotGlowOverlay.jsx +++ b/superset/assets/src/visualizations/MapBox/ScatterPlotGlowOverlay.jsx @@ -36,6 +36,11 @@ const contextTypes = { }; class ScatterPlotGlowOverlay extends React.Component { + constructor(props) { + super(props); + this.setCanvasRef = this.setCanvasRef.bind(this); + } + componentDidMount() { this.redraw(); } @@ -43,6 +48,11 @@ class ScatterPlotGlowOverlay extends React.Component { componentDidUpdate() { this.redraw(); } + + setCanvasRef(element) { + this.canvas = element; + } + drawText(ctx, pixel, options = {}) { const IS_DARK_THRESHOLD = 110; const { fontHeight = 0, label = '', radius = 0, rgb = [0, 0, 0], shadow = false } = options; @@ -197,9 +207,9 @@ class ScatterPlotGlowOverlay extends React.Component { } }, this); } - ctx.restore(); } + render() { let width = 0; let height = 0; @@ -211,7 +221,7 @@ class ScatterPlotGlowOverlay extends React.Component { const pixelRatio = window.devicePixelRatio || 1; return ( { this.canvas = c; }} + ref={this.setCanvasRef} width={width * pixelRatio} height={height * pixelRatio} style={{