mirror of
https://github.com/apache/superset.git
synced 2026-04-28 20:44:24 +00:00
Compare commits
16 Commits
docs/testi
...
0.29.0rc5
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
915466fdfc | ||
|
|
e48def35d6 | ||
|
|
6b0f42c81c | ||
|
|
4e8411c7f1 | ||
|
|
3c6f34cabd | ||
|
|
b9c8b0a112 | ||
|
|
9c88fefdeb | ||
|
|
6581a827ee | ||
|
|
7d28a84cce | ||
|
|
ba8f3c784c | ||
|
|
b23505e5c7 | ||
|
|
82a6708d10 | ||
|
|
88ab67b69a | ||
|
|
c08f4eaee1 | ||
|
|
1d74f308ee | ||
|
|
60cb608839 |
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "superset",
|
||||
"version": "0.999.0dev",
|
||||
"version": "0.29.0rc5",
|
||||
"description": "Superset is a data exploration platform designed to be visual, intuitive, and interactive.",
|
||||
"license": "Apache-2.0",
|
||||
"directories": {
|
||||
@@ -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",
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,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,15 +31,13 @@ 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({
|
||||
@@ -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);
|
||||
|
||||
@@ -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(', ');
|
||||
|
||||
@@ -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":
|
||||
@@ -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"
|
||||
|
||||
@@ -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):
|
||||
|
||||
Reference in New Issue
Block a user