Compare commits

...
Author SHA1 Message Date
Maxime Beauchemin 4735829b97 0.29.0rc8 2019-01-29 21:37:21 -08:00
zihengandMaxime Beauchemin 2025e14fb4 [fix] pandas>=0.24.0 datetimelike API changes (#6765) 2019-01-29 21:36:40 -08:00
Maxime Beauchemin 32738aa796 0.29.0rc7 2018-12-05 14:18:19 -08:00
Krist WongsuphasawatandMaxime Beauchemin 3bede69740 Update @superset-ui/colors (#6464)
* Update package version and update breaking changes for color

* update color function usage

* update dependency

(cherry picked from commit 20e1ac6242)
2018-12-05 14:17:50 -08:00
Maxime Beauchemin a1048a0995 0.29.0rc6 2018-12-05 11:27:10 -08:00
Maxime Beauchemin dac6219ef6 Pypi has a 60mb package size limit, and we're over
* removing india country map which is 12MB+
* removing src/ in general, keeping the countries folder which has geo
data

(cherry picked from commit 321c2e1e73)
2018-12-05 11:26:53 -08:00
Maxime Beauchemin 915466fdfc 0.29.0rc5 2018-12-01 12:39:20 -08:00
Grace GuoandMaxime Beauchemin e48def35d6 [logging] Add render failure error into log (#6422)
(cherry picked from commit 2489876bc9)
2018-11-30 13:32:05 -08:00
Krist WongsuphasawatandMaxime Beauchemin 6b0f42c81c [bugfix] Display raw value in addition to ERROR (#6417)
* Display raw value in addition to ERROR

* update unit test

(cherry picked from commit 3c6e882d28)
2018-11-30 13:32:01 -08:00
Grace GuoandMaxime Beauchemin 4e8411c7f1 [fix] view results in sql lab (#6405)
(cherry picked from commit e469086d61)
2018-11-30 13:31:57 -08:00
Maxime Beauchemin 3c6f34cabd [bugfix] deck.gl grid&hex don't respond to zoom&drag (#6363)
* [bugfix] deck.gl grid&hex don't respond to zoom&drag

Following recent changes, Hex & Grid component wouldn't respond to
viewport interactions. This fixes the issue.

This PR also greatly improve rendering performance by only recomputing
the deck.gl layers when needed, as opposed to every time the viewport
changes, which is very inneficient.

Note that most other deck.gl layers seem to be systematically
recomputing layers, we'll have to fix this in another PR.

* Addressing comments

(cherry picked from commit 8177fa17fa)
2018-11-30 13:31:53 -08:00
Maxime Beauchemin b9c8b0a112 Fix adhoc metrics in Polygon (#6399)
* Fix adhoc metrics in Polygon

* Addressing comments

(cherry picked from commit 4c94447635)
2018-11-30 13:31:45 -08:00
Jeffrey WangandMaxime Beauchemin 9c88fefdeb Set default limit on creation of new qe and on run (#6400)
(cherry picked from commit 8c4f723a61)
2018-11-30 13:31:40 -08:00
Grace GuoandMaxime Beauchemin 6581a827ee [fix] JS error after delete row level tab (#6393)
(cherry picked from commit ec1316a5a8)
2018-11-30 13:31:33 -08:00
Maxime Beauchemin 7d28a84cce [bugfix] prevent d3-format from raising (#6386)
Since https://github.com/apache/incubator-superset/pull/6287 and
effectively moving to a new version of d3, d3-format and d3-time-format
raises when receiving invalid input strings.

This code wraps the potential issues inside `try` blocks that will
effectively return an `ERROR` string as output to the formatting
function.

(cherry picked from commit 4690563d40)
2018-11-30 13:31:24 -08:00
Krist WongsuphasawatandMaxime Beauchemin ba8f3c784c do not convert value to lowercase when looking up color (#6384)
(cherry picked from commit cb556688ec)
2018-11-30 13:31:20 -08:00
CongleiandMaxime Beauchemin b23505e5c7 Fixed the cache issue for BigNumber (#6375)
* fixed the cache issue for bignumber

* fixed

(cherry picked from commit 8e2d28dd6e)
2018-11-30 13:31:16 -08:00
Maxime Beauchemin 82a6708d10 0.29.0rc4 2018-11-12 10:54:56 -08:00
Maxime Beauchemin 88ab67b69a Merge branch 'master' into 0.29 2018-11-12 10:54:43 -08:00
Maxime Beauchemin c08f4eaee1 0.29.0rc3 2018-11-10 12:39:44 -08:00
Maxime Beauchemin 1d74f308ee Merge branch 'master' into 0.29 2018-11-10 12:39:02 -08:00
Maxime Beauchemin 60cb608839 0.29.0rc2 2018-11-08 09:39:24 -08:00
35 changed files with 240 additions and 242 deletions
+2
View File
@@ -6,6 +6,8 @@ recursive-exclude superset/static/assets/docs *
recursive-exclude superset/static/assets/images/viz_thumbnails_large *
recursive-exclude superset/static/docs *
recursive-exclude superset/static/spec *
recursive-exclude superset/static/assets/src *
recursive-include superset/static/assets/src/visualizations/CountryMap/ *
recursive-exclude superset/static/images/viz_thumbnails_large *
recursive-exclude superset/static/assets/node_modules *
recursive-include superset/templates *
+4
View File
@@ -3,6 +3,10 @@
This file documents any backwards-incompatible changes in Superset and
assists people when migrating to a new version.
## Superset 0.29.0
* India was removed from the "Country Map" visualization as the geojson
file included in the package was very large
## Superset 0.28.0
* Support for Python 2 is deprecated, we only support >=3.6 from
`0.28.0` onwards
+2 -2
View File
@@ -53,9 +53,9 @@ linear-tsv==1.1.0 # via tabulator
mako==1.0.7 # via alembic
markdown==3.0
markupsafe==1.0 # via jinja2, mako
numpy==1.15.2 # via pandas
openpyxl==2.4.11 # via tabulator
pandas==0.23.1
numpy==1.15.2 # via numpy
pandas==0.23.4 # via pandas
parsedatetime==2.0.0
pathlib2==2.3.0
polyline==1.3.2
+2 -2
View File
@@ -57,7 +57,7 @@ setup(
'boto3>=1.4.7, <2.0.0',
'botocore>=1.7.0, <1.8.0',
'celery>=4.2.0, <5.0.0',
'click>=6.0, <7.0.0', # click >=7 forces "-" instead of "_"
'click>=6.0, <7.0.0', # `click`>=7 forces "-" instead of "_"
'colorama',
'contextlib2',
'cryptography',
@@ -74,7 +74,7 @@ setup(
'idna',
'isodate',
'markdown>=3.0',
'pandas>=0.18.0',
'pandas>=0.18.0, <0.24.0', # `pandas`>=0.24.0 changes datetimelike API
'parsedatetime',
'pathlib2',
'polyline',
+6 -6
View File
@@ -1,6 +1,6 @@
{
"name": "superset",
"version": "0.999.0dev",
"version": "0.29.0rc8",
"description": "Superset is a data exploration platform designed to be visual, intuitive, and interactive.",
"license": "Apache-2.0",
"directories": {
@@ -51,11 +51,11 @@
"@data-ui/sparkline": "^0.0.54",
"@data-ui/theme": "^0.0.62",
"@data-ui/xy-chart": "^0.0.61",
"@superset-ui/chart": "^0.5.0",
"@superset-ui/color": "^0.5.0",
"@superset-ui/chart": "^0.7.0",
"@superset-ui/color": "^0.7.0",
"@superset-ui/connection": "^0.5.0",
"@superset-ui/core": "^0.5.0",
"@superset-ui/translation": "^0.5.0",
"@superset-ui/core": "^0.7.0",
"@superset-ui/translation": "^0.7.0",
"@vx/legend": "^0.0.170",
"@vx/responsive": "0.0.172",
"@vx/scale": "^0.0.165",
@@ -79,7 +79,7 @@
"d3-tip": "^0.9.1",
"datamaps": "^0.5.8",
"datatables.net-bs": "^1.10.15",
"deck.gl": "^5.3.4",
"deck.gl": "^5.3.5",
"distributions": "^1.0.0",
"dnd-core": "^2.6.0",
"dompurify": "^1.0.3",
@@ -19,10 +19,10 @@ describe('ColorPickerControl', () => {
beforeEach(() => {
getCategoricalSchemeRegistry()
.registerValue('test', new CategoricalScheme({
name: 'test',
id: 'test',
colors: ['red', 'green', 'blue'],
}))
.setDefaultSchemeName('test');
.setDefaultKey('test');
wrapper = shallow(<ColorPickerControl {...defaultProps} />);
inst = wrapper.instance();
});
@@ -29,6 +29,7 @@ describe('utils', () => {
expect(d3format('.3s', 1234)).toBe('1.23k');
expect(d3format('.3s', 1237)).toBe('1.24k');
expect(d3format('', 1237)).toBe('1.24k');
expect(d3format('.2efd2.ef.2.e', 1237)).toBe('1237 (Invalid format: .2efd2.ef.2.e)');
});
});
@@ -45,8 +46,9 @@ describe('utils', () => {
it('is a function', () => {
expect(typeof d3TimeFormatPreset).toBe('function');
});
it('returns a working time formatter', () => {
it('returns a working formatter', () => {
expect(d3FormatPreset('smart_date')(0)).toBe('1970');
expect(d3FormatPreset('%%GIBBERISH')(0)).toBe('0 (Invalid format: %%GIBBERISH)');
});
});
@@ -4,6 +4,8 @@ import {
getBuckets,
} from '../../../../src/visualizations/deckgl/utils';
const metricAccessor = d => d.count;
describe('getBreakPoints', () => {
it('is a function', () => {
expect(typeof getBreakPoints).toBe('function');
@@ -11,7 +13,7 @@ describe('getBreakPoints', () => {
it('returns sorted break points', () => {
const fd = { break_points: ['0', '10', '100', '50', '1000'] };
const result = getBreakPoints(fd, []);
const result = getBreakPoints(fd, [], metricAccessor);
const expected = ['0', '10', '50', '100', '1000'];
expect(result).toEqual(expected);
});
@@ -19,7 +21,7 @@ describe('getBreakPoints', () => {
it('returns evenly distributed break points when no break points are specified', () => {
const fd = { metric: 'count' };
const features = [0, 1, 2, 10].map(count => ({ count }));
const result = getBreakPoints(fd, features);
const result = getBreakPoints(fd, features, metricAccessor);
const expected = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '10'];
expect(result).toEqual(expected);
});
@@ -27,7 +29,7 @@ describe('getBreakPoints', () => {
it('formats number with proper precision', () => {
const fd = { metric: 'count', num_buckets: 2 };
const features = [0, 1 / 3, 2 / 3, 1].map(count => ({ count }));
const result = getBreakPoints(fd, features);
const result = getBreakPoints(fd, features, metricAccessor);
const expected = ['0.0', '0.5', '1.0'];
expect(result).toEqual(expected);
});
@@ -35,7 +37,7 @@ describe('getBreakPoints', () => {
it('works with a zero range', () => {
const fd = { metric: 'count', num_buckets: 1 };
const features = [1, 1, 1].map(count => ({ count }));
const result = getBreakPoints(fd, features);
const result = getBreakPoints(fd, features, metricAccessor);
const expected = ['1', '1'];
expect(result).toEqual(expected);
});
@@ -53,7 +55,7 @@ describe('getBreakPointColorScaler', () => {
opacity: 100,
};
const features = [10, 20, 30].map(count => ({ count }));
const scaler = getBreakPointColorScaler(fd, features);
const scaler = getBreakPointColorScaler(fd, features, metricAccessor);
expect(scaler({ count: 10 })).toEqual([0, 0, 0, 255]);
expect(scaler({ count: 15 })).toEqual([64, 64, 64, 255]);
expect(scaler({ count: 30 })).toEqual([255, 255, 255, 255]);
@@ -67,7 +69,7 @@ describe('getBreakPointColorScaler', () => {
opacity: 100,
};
const features = [];
const scaler = getBreakPointColorScaler(fd, features);
const scaler = getBreakPointColorScaler(fd, features, metricAccessor);
expect(scaler({ count: 0 })).toEqual([0, 0, 0, 255]);
expect(scaler({ count: 0.5 })).toEqual([0, 0, 0, 255]);
expect(scaler({ count: 1 })).toEqual([255, 255, 255, 255]);
@@ -82,7 +84,7 @@ describe('getBreakPointColorScaler', () => {
opacity: 100,
};
const features = [];
const scaler = getBreakPointColorScaler(fd, features);
const scaler = getBreakPointColorScaler(fd, features, metricAccessor);
expect(scaler({ count: -1 })).toEqual([0, 0, 0, 0]);
expect(scaler({ count: 11 })).toEqual([255, 255, 255, 0]);
});
@@ -101,7 +103,7 @@ describe('getBuckets', () => {
opacity: 100,
};
const features = [];
const result = getBuckets(fd, features);
const result = getBuckets(fd, features, metricAccessor);
const expected = {
'0 - 1': { color: [0, 0, 0, 255], enabled: true },
'1 - 10': { color: [255, 255, 255, 255], enabled: true },
@@ -28,6 +28,7 @@ const defaultProps = {
csv: true,
actions: {},
cache: false,
database: {},
};
const SEARCH_HEIGHT = 46;
@@ -149,7 +149,7 @@ class SqlEditor extends React.PureComponent {
schema: qe.schema,
tempTableName: ctas ? this.state.ctas : '',
templateParams: qe.templateParams,
queryLimit: qe.queryLimit,
queryLimit: qe.queryLimit || this.props.defaultQueryLimit,
runAsync,
ctas,
};
@@ -138,6 +138,7 @@ class TabbedSqlEditors extends React.PureComponent {
sql: `${t(
'-- Note: Unless you save your query, these tabs will NOT persist if you clear your cookies or change browsers.',
)}\n\nSELECT ...`,
queryLimit: this.props.defaultQueryLimit,
};
this.props.actions.addQueryEditor(qe);
}
+33 -27
View File
@@ -95,6 +95,14 @@ class Chart extends React.PureComponent {
const { actions, chartId } = this.props;
console.warn(error); // eslint-disable-line
actions.chartRenderingFailed(error.toString(), chartId, info ? info.componentStack : null);
Logger.append(LOG_ACTIONS_RENDER_CHART, {
slice_id: chartId,
has_err: true,
error_details: error.toString(),
start_offset: this.renderStartTime,
duration: Logger.getTimestamp() - this.renderStartTime,
});
}
prepareChartProps() {
@@ -126,8 +134,7 @@ class Chart extends React.PureComponent {
renderTooltip() {
const { tooltip } = this.state;
if (tooltip) {
if (tooltip && tooltip.content) {
return (
<Tooltip
className="chart-tooltip"
@@ -173,32 +180,31 @@ class Chart extends React.PureComponent {
this.renderStartTime = Logger.getTimestamp();
return (
<div
className={`chart-container ${isLoading ? 'is-loading' : ''}`}
style={containerStyles}
>
{this.renderTooltip()}
<ErrorBoundary onError={this.handleRenderFailure} showMessage={false}>
<div
className={`chart-container ${isLoading ? 'is-loading' : ''}`}
style={containerStyles}
>
{this.renderTooltip()}
{['loading', 'success'].indexOf(chartStatus) >= 0 && <Loading size={50} />}
{['loading', 'success'].indexOf(chartStatus) >= 0 && <Loading size={50} />}
{chartAlert && (
<StackTraceMessage
message={chartAlert}
link={queryResponse ? queryResponse.link : null}
stackTrace={chartStackTrace}
/>
)}
{chartAlert && (
<StackTraceMessage
message={chartAlert}
link={queryResponse ? queryResponse.link : null}
stackTrace={chartStackTrace}
/>
)}
{!isLoading && !chartAlert && isFaded && (
<RefreshChartOverlay
width={width}
height={height}
onQuery={onQuery}
onDismiss={onDismissRefreshOverlay}
/>
)}
<ErrorBoundary onError={this.handleRenderFailure} showMessage={false}>
{!isLoading && !chartAlert && isFaded && (
<RefreshChartOverlay
width={width}
height={height}
onQuery={onQuery}
onDismiss={onDismissRefreshOverlay}
/>
)}
<SuperChart
className={`slice_container ${snakeCase(vizType)} ${isFaded ? ' faded' : ''}`}
chartType={vizType}
@@ -207,8 +213,8 @@ class Chart extends React.PureComponent {
onRenderFailure={this.handleRenderFailure}
skipRendering={skipChartRendering}
/>
</ErrorBoundary>
</div>
</div>
</ErrorBoundary>
);
}
}
@@ -39,15 +39,17 @@ function mapStateToProps(
// rows and columns need more data about their child dimensions
// doing this allows us to not pass the entire component lookup to all Components
const componentType = component.type;
if (componentType === ROW_TYPE || componentType === COLUMN_TYPE) {
const { occupiedWidth, minimumWidth } = getDetailedComponentWidth({
id,
components: dashboardLayout,
});
if (component) {
const componentType = component.type;
if (componentType === ROW_TYPE || componentType === COLUMN_TYPE) {
const { occupiedWidth, minimumWidth } = getDetailedComponentWidth({
id,
components: dashboardLayout,
});
if (componentType === ROW_TYPE) props.occupiedColumnCount = occupiedWidth;
if (componentType === COLUMN_TYPE) props.minColumnWidth = minimumWidth;
if (componentType === ROW_TYPE) props.occupiedColumnCount = occupiedWidth;
if (componentType === COLUMN_TYPE) props.minColumnWidth = minimumWidth;
}
}
return props;
@@ -68,7 +70,7 @@ function mapDispatchToProps(dispatch) {
class DashboardComponent extends React.PureComponent {
render() {
const { component } = this.props;
const Component = ComponentLookup[component.type];
const Component = component ? ComponentLookup[component.type] : null;
return Component ? <Component {...this.props} /> : null;
}
}
-1
View File
@@ -519,7 +519,6 @@ export const controls = {
'Egypt',
'France',
'Germany',
'India',
'Italy',
'Portugal',
'Morocco',
+15 -3
View File
@@ -22,7 +22,13 @@ export function d3FormatPreset(format) {
return formatDate;
}
if (format) {
return d3Format(format);
try {
return d3Format(format);
} catch (e) {
// eslint-disable-next-line no-console
console.warn(e);
return value => `${value} (Invalid format: ${format})`;
}
}
return defaultNumberFormatter;
}
@@ -45,12 +51,18 @@ export function d3format(format, number) {
format = format || '.3s';
// Formats a number and memoizes formatters to be reused
if (!(format in formatters)) {
formatters[format] = d3Format(format);
try {
formatters[format] = d3Format(format);
} catch (e) {
// eslint-disable-next-line no-console
console.warn(e);
return `${number} (Invalid format: ${format})`;
}
}
try {
return formatters[format](number);
} catch (e) {
return 'ERR';
return `${number} (Invalid format: ${format})`;
}
}
+3 -3
View File
@@ -11,15 +11,15 @@ export default function setupColors() {
const categoricalSchemeRegistry = getCategoricalSchemeRegistry();
[airbnb, categoricalD3, google, lyft].forEach((group) => {
group.forEach((scheme) => {
categoricalSchemeRegistry.registerValue(scheme.name, scheme);
categoricalSchemeRegistry.registerValue(scheme.id, scheme);
});
});
categoricalSchemeRegistry.setDefaultSchemeName('bnbColors');
categoricalSchemeRegistry.setDefaultKey('bnbColors');
const sequentialSchemeRegistry = getSequentialSchemeRegistry();
[sequentialCommon, sequentialD3].forEach((group) => {
group.forEach((scheme) => {
sequentialSchemeRegistry.registerValue(scheme.name, scheme);
sequentialSchemeRegistry.registerValue(scheme.id, scheme);
});
});
}
@@ -29,7 +29,7 @@ function Chord(element, props) {
const div = d3.select(element);
const { nodes, matrix } = data;
const f = d3.format(numberFormat);
const colorFn = CategoricalColorNamespace.getScale(colorScheme).toFunction();
const colorFn = CategoricalColorNamespace.getScale(colorScheme);
const outerRadius = Math.min(width, height) / 2 - 10;
const innerRadius = outerRadius - 24;
File diff suppressed because one or more lines are too long
@@ -47,7 +47,7 @@ class CustomHistogram extends React.PureComponent {
yAxisLabel,
} = this.props;
const colorFn = CategoricalColorNamespace.getScale(colorScheme).toFunction();
const colorFn = CategoricalColorNamespace.getScale(colorScheme);
const keys = data.map(d => d.key);
const colorScale = scaleOrdinal({
domain: keys,
@@ -95,7 +95,7 @@ function Icicle(element, props) {
const hasTime = ['adv_anal', 'time_series'].indexOf(chartType) >= 0;
const format = d3.format(numberFormat);
const timeFormat = d3TimeFormatPreset(dateTimeFormat);
const colorFn = CategoricalColorNamespace.getScale(colorScheme).toFunction();
const colorFn = CategoricalColorNamespace.getScale(colorScheme);
div.selectAll('*').remove();
const tooltip = div
@@ -60,7 +60,7 @@ function Rose(element, props) {
const numGroups = datum[times[0]].length;
const format = d3.format(numberFormat);
const timeFormat = d3TimeFormatPreset(dateTimeFormat);
const colorFn = CategoricalColorNamespace.getScale(colorScheme).toFunction();
const colorFn = CategoricalColorNamespace.getScale(colorScheme);
d3.select('.nvtooltip').remove();
div.selectAll('*').remove();
@@ -47,7 +47,7 @@ function Sankey(element, props) {
.attr('class', 'sankey-tooltip')
.style('opacity', 0);
const colorFn = CategoricalColorNamespace.getScale(colorScheme).toFunction();
const colorFn = CategoricalColorNamespace.getScale(colorScheme);
const sankey = d3Sankey()
.nodeWidth(15)
@@ -66,7 +66,7 @@ function Sunburst(element, props) {
let arcs;
let gMiddleText; // dom handles
const colorFn = CategoricalColorNamespace.getScale(colorScheme).toFunction();
const colorFn = CategoricalColorNamespace.getScale(colorScheme);
// Helper + path gen functions
const partition = d3.layout.partition()
@@ -68,7 +68,7 @@ function Treemap(element, props) {
} = props;
const div = d3.select(element);
const formatNumber = d3.format(numberFormat);
const colorFn = CategoricalColorNamespace.getScale(colorScheme).toFunction();
const colorFn = CategoricalColorNamespace.getScale(colorScheme);
const data = clone(rawData);
function draw(data, eltWidth, eltHeight) {
@@ -15,7 +15,7 @@ const { getScale } = CategoricalColorNamespace;
function getCategories(fd, data) {
const c = fd.color_picker || { r: 0, g: 0, b: 0, a: 1 };
const fixedColor = [c.r, c.g, c.b, 255 * c.a];
const colorFn = getScale(fd.color_scheme).toFunction();
const colorFn = getScale(fd.color_scheme);
const categories = {};
data.forEach((d) => {
if (d.cat_color != null && !categories.hasOwnProperty(d.cat_color)) {
@@ -159,7 +159,7 @@ export default class CategoricalDeckGLContainer extends React.PureComponent {
}
addColor(data, fd) {
const c = fd.color_picker || { r: 0, g: 0, b: 0, a: 1 };
const colorFn = getScale(fd.color_scheme).toFunction();
const colorFn = getScale(fd.color_scheme);
return data.map((d) => {
let color;
if (fd.dimension) {
@@ -3,8 +3,9 @@ import PropTypes from 'prop-types';
import MapGL from 'react-map-gl';
import DeckGL from 'deck.gl';
import 'mapbox-gl/dist/mapbox-gl.css';
import { isEqual } from 'lodash';
const TICK = 1000; // milliseconds
const TICK = 2000; // milliseconds
const propTypes = {
viewport: PropTypes.object.isRequired,
@@ -23,12 +24,13 @@ const defaultProps = {
export default class DeckGLContainer extends React.Component {
constructor(props) {
super(props);
this.tick = this.tick.bind(this);
this.onViewportChange = this.onViewportChange.bind(this);
// This has to be placed after this.tick is bound to this
this.state = {
previousViewport: props.viewport,
timer: setInterval(this.tick, TICK),
};
this.tick = this.tick.bind(this);
this.onViewportChange = this.onViewportChange.bind(this);
}
static getDerivedStateFromProps(nextProps, prevState) {
if (nextProps.viewport !== prevState.viewport) {
@@ -53,7 +55,9 @@ export default class DeckGLContainer extends React.Component {
}
tick() {
// Limiting updating viewport controls through Redux at most 1*sec
if (this.state && this.state.previousViewport !== this.props.viewport) {
// Deep compare is needed as shallow equality doesn't work here, viewport object
// changes id at every change
if (this.state && !isEqual(this.state.previousViewport, this.props.viewport)) {
const setCV = this.props.setControlValue;
const vp = this.props.viewport;
if (setCV) {
@@ -1,5 +1,7 @@
import React from 'react';
import PropTypes from 'prop-types';
import { isEqual } from 'lodash';
import DeckGLContainer from './DeckGLContainer';
import CategoricalDeckGLContainer from './CategoricalDeckGLContainer';
import { fitViewport } from './layers/common';
@@ -18,36 +20,66 @@ const defaultProps = {
};
export function createDeckGLComponent(getLayer, getPoints) {
function Component(props) {
const {
formData,
payload,
setControlValue,
onAddFilter,
setTooltip,
viewport: originalViewport,
} = props;
const viewport = formData.autozoom
? fitViewport(originalViewport, getPoints(payload.data.features))
: originalViewport;
const layer = getLayer(formData, payload, onAddFilter, setTooltip);
return (
<DeckGLContainer
mapboxApiAccessToken={payload.data.mapboxApiKey}
viewport={viewport}
layers={[layer]}
mapStyle={formData.mapbox_style}
setControlValue={setControlValue}
/>
);
// Higher order component
class Component extends React.PureComponent {
constructor(props) {
super(props);
const originalViewport = props.viewport;
const viewport = props.formData.autozoom
? fitViewport(originalViewport, getPoints(props.payload.data.features))
: originalViewport;
this.state = {
viewport,
layer: this.computeLayer(props),
};
this.onViewportChange = this.onViewportChange.bind(this);
}
componentWillReceiveProps(nextProps) {
// Only recompute the layer if anything BUT the viewport has changed
const nextFdNoVP = { ...nextProps.formData, viewport: null };
const currFdNoVP = { ...this.props.formData, viewport: null };
if (
!isEqual(nextFdNoVP, currFdNoVP) ||
nextProps.payload !== this.props.payload
) {
this.setState({ layer: this.computeLayer(nextProps) });
}
}
onViewportChange(viewport) {
this.setState({ viewport });
}
computeLayer(props) {
const {
formData,
payload,
onAddFilter,
setTooltip,
} = props;
return getLayer(formData, payload, onAddFilter, setTooltip);
}
render() {
const {
formData,
payload,
setControlValue,
} = this.props;
const {
layer,
viewport,
} = this.state;
return (
<DeckGLContainer
mapboxApiAccessToken={payload.data.mapboxApiKey}
viewport={viewport}
layers={[layer]}
mapStyle={formData.mapbox_style}
setControlValue={setControlValue}
onViewportChange={this.onViewportChange}
/>);
}
}
Component.propTypes = propTypes;
Component.defaultProps = defaultProps;
return Component;
}
@@ -48,10 +48,12 @@ export function getLayer(formData, payload, setTooltip, selected, onSelect, filt
data = jsFnMutator(data);
}
const metricLabel = fd.metric ? fd.metric.label || fd.metric : null;
const accessor = d => d[metricLabel];
// base color for the polygons
const baseColorScaler = fd.metric === null
? () => [fc.r, fc.g, fc.b, 255 * fc.a]
: getBreakPointColorScaler(fd, data);
: getBreakPointColorScaler(fd, data, accessor);
// when polygons are selected, reduce the opacity of non-selected polygons
const colorScaler = (d) => {
@@ -61,7 +63,6 @@ export function getLayer(formData, payload, setTooltip, selected, onSelect, filt
}
return baseColor;
};
return new PolygonLayer({
id: `path-layer-${fd.slice_id}`,
data,
@@ -211,7 +212,12 @@ class DeckGLPolygon extends React.Component {
render() {
const { payload, formData, setControlValue } = this.props;
const { start, end, getStep, values, disabled, viewport } = this.state;
const buckets = getBuckets(formData, payload.data.features);
const fd = formData;
const metricLabel = fd.metric ? fd.metric.label || fd.metric : null;
const accessor = d => d[metricLabel];
const buckets = getBuckets(formData, payload.data.features, accessor);
return (
<div style={{ position: 'relative' }}>
<AnimatableDeckGLContainer
@@ -38,12 +38,13 @@ export function commonLayerProps(formData, setTooltip, onSelect) {
let tooltipContentGenerator;
if (fd.js_tooltip) {
tooltipContentGenerator = sandboxedEval(fd.js_tooltip);
} else if (fd.line_column && fd.line_type === 'geohash') {
} else if (fd.line_column && fd.metric && ['geohash', 'zipcode'].indexOf(fd.line_type) >= 0) {
const metricLabel = fd.metric.label || fd.metric;
tooltipContentGenerator = o => (
<div>
<div>{fd.line_column}: <strong>{o.object[fd.line_column]}</strong></div>
{fd.metric &&
<div>{fd.metric}: <strong>{o.object[fd.metric]}</strong></div>}
<div>{metricLabel}: <strong>{o.object[metricLabel]}</strong></div>}
</div>);
}
if (tooltipContentGenerator) {
@@ -3,20 +3,19 @@ import { scaleThreshold } from 'd3-scale';
import { getSequentialSchemeRegistry, SequentialScheme } from '@superset-ui/color';
import { hexToRGB } from '../../modules/colors';
const DEFAULT_NUM_BUCKETS = 10;
export function getBreakPoints({
break_points: formDataBreakPoints,
num_buckets: formDataNumBuckets,
metric,
}, features) {
}, features, accessor) {
if (!features) {
return [];
}
if (formDataBreakPoints === undefined || formDataBreakPoints.length === 0) {
// compute evenly distributed break points based on number of buckets
const numBuckets = formDataNumBuckets
? parseInt(formDataNumBuckets, 10)
: 10;
const [minValue, maxValue] = extent(features, d => d[metric]);
const numBuckets = formDataNumBuckets ? parseInt(formDataNumBuckets, 10) : DEFAULT_NUM_BUCKETS;
const [minValue, maxValue] = extent(features, accessor);
const delta = (maxValue - minValue) / numBuckets;
const precision = delta === 0
? 0
@@ -32,19 +31,17 @@ export function getBreakPointColorScaler({
break_points: formDataBreakPoints,
num_buckets: formDataNumBuckets,
linear_color_scheme: linearColorScheme,
metric,
opacity,
}, features) {
}, features, accessor) {
const breakPoints = formDataBreakPoints || formDataNumBuckets
? getBreakPoints({
break_points: formDataBreakPoints,
num_buckets: formDataNumBuckets,
metric,
}, features)
}, features, accessor)
: null;
const colorScheme = Array.isArray(linearColorScheme)
? new SequentialScheme({
name: 'custom',
id: 'custom',
colors: linearColorScheme,
})
: getSequentialSchemeRegistry().get(linearColorScheme);
@@ -69,13 +66,14 @@ export function getBreakPointColorScaler({
maskPoint = value => value > breakPoints[n] || value < breakPoints[0];
} else {
// interpolate colors linearly
scaler = colorScheme.createLinearScale(extent(features, d => d[metric]));
scaler = colorScheme.createLinearScale(extent(features, accessor));
maskPoint = () => false;
}
return (d) => {
const c = hexToRGB(scaler(d[metric]));
if (maskPoint(d[metric])) {
const v = accessor(d);
const c = hexToRGB(scaler(v));
if (maskPoint(v)) {
c[3] = 0;
} else {
c[3] = (opacity / 100.0) * 255;
@@ -84,15 +82,15 @@ export function getBreakPointColorScaler({
};
}
export function getBuckets(fd, features) {
const breakPoints = getBreakPoints(fd, features, true);
const colorScaler = getBreakPointColorScaler(fd, features);
export function getBuckets(fd, features, accessor) {
const breakPoints = getBreakPoints(fd, features, accessor);
const colorScaler = getBreakPointColorScaler(fd, features, accessor);
const buckets = {};
breakPoints.slice(1).forEach((value, i) => {
const range = breakPoints[i] + ' - ' + breakPoints[i + 1];
const mid = 0.5 * (parseInt(breakPoints[i], 10) + parseInt(breakPoints[i + 1], 10));
buckets[range] = {
color: colorScaler({ [fd.metric]: mid }),
color: colorScaler({ [fd.metric.label || fd.metric]: mid }),
enabled: true,
};
});
@@ -456,11 +456,13 @@ function nvd3Vis(element, props) {
chart.xScale(d3.scale.log());
}
let xAxisFormatter = d3FormatPreset(xAxisFormat);
let xAxisFormatter;
if (isTimeSeries) {
xAxisFormatter = d3TimeFormatPreset(xAxisFormat);
// In tooltips, always use the verbose time format
chart.interactiveLayer.tooltip.headerFormatter(formatDateVerbose);
} else {
xAxisFormatter = d3FormatPreset(xAxisFormat);
}
if (chart.x2Axis && chart.x2Axis.tickFormat) {
chart.x2Axis.tickFormat(xAxisFormatter);
@@ -504,7 +506,7 @@ function nvd3Vis(element, props) {
});
}
} else if (vizType !== 'bullet') {
const colorFn = getScale(colorScheme).toFunction();
const colorFn = getScale(colorScheme);
chart.color(d => d.color || colorFn(cleanColorInput(d[colorKey])));
}
@@ -10,7 +10,6 @@ const TIME_SHIFT_PATTERN = /\d+ \w+ offset/;
export function cleanColorInput(value) {
// for superset series that should have the same color
return String(value).trim()
.toLowerCase()
.split(', ')
.filter(k => !TIME_SHIFT_PATTERN.test(k))
.join(', ');
@@ -48,7 +48,7 @@ function WordCloud(element, props) {
.fontWeight('bold')
.fontSize(d => scale(d.size));
const colorFn = CategoricalColorNamespace.getScale(colorScheme).toFunction();
const colorFn = CategoricalColorNamespace.getScale(colorScheme);
function draw(words) {
chart.selectAll('*').remove();
+34 -34
View File
@@ -307,30 +307,30 @@
d3-array "^1.2.0"
prop-types "^15.5.10"
"@deck.gl/core@^5.3.3":
version "5.3.3"
resolved "https://registry.yarnpkg.com/@deck.gl/core/-/core-5.3.3.tgz#a13c07e5fa3e22297fd450d6da8ab9aac334b1f0"
"@deck.gl/core@^5.3.5":
version "5.3.5"
resolved "https://registry.yarnpkg.com/@deck.gl/core/-/core-5.3.5.tgz#24e6b3164a36b89b05cd020c2f7bcb5d08e3c266"
dependencies:
luma.gl "^5.3.0"
luma.gl "^5.3.1"
math.gl "^1.2.1"
mjolnir.js "^1.0.0"
probe.gl "^1.0.0"
seer "^0.2.4"
viewport-mercator-project "^5.1.0"
"@deck.gl/layers@^5.3.4":
version "5.3.4"
resolved "https://registry.yarnpkg.com/@deck.gl/layers/-/layers-5.3.4.tgz#ab3de1bf8bb68d67772642acbb4e0f87f4f11300"
"@deck.gl/layers@^5.3.5":
version "5.3.5"
resolved "https://registry.yarnpkg.com/@deck.gl/layers/-/layers-5.3.5.tgz#79c19be42961b909772e9dcd4eef5416d226d58e"
dependencies:
"@deck.gl/core" "^5.3.3"
"@deck.gl/core" "^5.3.5"
d3-hexbin "^0.2.1"
earcut "^2.0.6"
"@deck.gl/react@^5.3.3":
version "5.3.3"
resolved "https://registry.yarnpkg.com/@deck.gl/react/-/react-5.3.3.tgz#e7352934f6742d3ce672a394cbff312aab5ccaa0"
"@deck.gl/react@^5.3.5":
version "5.3.5"
resolved "https://registry.yarnpkg.com/@deck.gl/react/-/react-5.3.5.tgz#caace72d4afc6531103fbb87292400de7c6f292a"
dependencies:
"@deck.gl/core" "^5.3.3"
"@deck.gl/core" "^5.3.5"
prop-types "^15.6.0"
"@mapbox/geojson-area@0.2.2":
@@ -399,18 +399,18 @@
dependencies:
array-from "^2.1.1"
"@superset-ui/chart@^0.5.0":
version "0.5.0"
resolved "https://registry.yarnpkg.com/@superset-ui/chart/-/chart-0.5.0.tgz#1420ce7b6ac3bf1b06875e5f2541fa98505b5a9b"
"@superset-ui/chart@^0.7.0":
version "0.7.0"
resolved "https://registry.yarnpkg.com/@superset-ui/chart/-/chart-0.7.0.tgz#c297642bf6968e9e24b8de68c595942ad02da949"
dependencies:
"@superset-ui/core" "^0.3.0"
reselect "^4.0.0"
"@superset-ui/color@^0.5.0":
version "0.5.0"
resolved "https://registry.yarnpkg.com/@superset-ui/color/-/color-0.5.0.tgz#3064bec063fdf43d568a39b4d9ded352d0aeed55"
"@superset-ui/color@^0.7.0":
version "0.7.0"
resolved "https://registry.yarnpkg.com/@superset-ui/color/-/color-0.7.0.tgz#9d3eca8da493e1241a047f88b06d668b2cd20e3c"
dependencies:
"@superset-ui/core" "^0.5.0"
"@superset-ui/core" "^0.7.0"
d3-scale "^2.1.2"
"@superset-ui/connection@^0.5.0":
@@ -426,15 +426,15 @@
dependencies:
lodash "^4.17.11"
"@superset-ui/core@^0.5.0":
version "0.5.0"
resolved "https://registry.yarnpkg.com/@superset-ui/core/-/core-0.5.0.tgz#8784465e312cac5015df28d8540d27a2fd6060ba"
"@superset-ui/core@^0.7.0":
version "0.7.0"
resolved "https://registry.yarnpkg.com/@superset-ui/core/-/core-0.7.0.tgz#aa116248d56fd22d57a7bbc9afbdfc0cb367146a"
dependencies:
lodash "^4.17.11"
"@superset-ui/translation@^0.5.0":
version "0.5.0"
resolved "https://registry.yarnpkg.com/@superset-ui/translation/-/translation-0.5.0.tgz#d02af7be94ac6b9e48d3b09f9f1059b11b2d283d"
"@superset-ui/translation@^0.7.0":
version "0.7.0"
resolved "https://registry.yarnpkg.com/@superset-ui/translation/-/translation-0.7.0.tgz#8b9426a97d523df5aefe9242084264897efe252c"
dependencies:
jed "^1.1.1"
@@ -3679,13 +3679,13 @@ decimal.js@9.0.1:
version "9.0.1"
resolved "https://registry.yarnpkg.com/decimal.js/-/decimal.js-9.0.1.tgz#1cc8b228177da7ab6498c1cc06eb130a290e6e1e"
deck.gl@^5.3.4:
version "5.3.4"
resolved "https://registry.yarnpkg.com/deck.gl/-/deck.gl-5.3.4.tgz#35e5a7087ef0d8ca7811d06a721ea289edbe7c24"
deck.gl@^5.3.5:
version "5.3.5"
resolved "https://registry.yarnpkg.com/deck.gl/-/deck.gl-5.3.5.tgz#a30b8a6ee6caba1133167de461edc15ee3bb0f8b"
dependencies:
"@deck.gl/core" "^5.3.3"
"@deck.gl/layers" "^5.3.4"
"@deck.gl/react" "^5.3.3"
"@deck.gl/core" "^5.3.5"
"@deck.gl/layers" "^5.3.5"
"@deck.gl/react" "^5.3.5"
decode-uri-component@^0.2.0:
version "0.2.0"
@@ -7241,9 +7241,9 @@ lru-cache@^4.0.1, lru-cache@^4.1.1, lru-cache@^4.1.3:
pseudomap "^1.0.2"
yallist "^2.1.2"
luma.gl@^5.3.0:
version "5.3.0"
resolved "https://registry.yarnpkg.com/luma.gl/-/luma.gl-5.3.0.tgz#a93b2f34489d8230eb6d8c871335800d9b83ee67"
luma.gl@^5.3.1:
version "5.3.1"
resolved "https://registry.yarnpkg.com/luma.gl/-/luma.gl-5.3.1.tgz#d380d554fe4c9de0b883e5d75f58d2a2142cfa05"
dependencies:
math.gl "^1.1.0"
probe.gl "^1.0.0"
+1 -1
View File
@@ -489,7 +489,7 @@ class BaseViz(object):
return df.to_csv(index=include_index, **config.get('CSV_EXPORT'))
def get_data(self, df):
return self.get_df().to_dict(orient='records')
return df.to_dict(orient='records')
@property
def json_data(self):