Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[reviewable] Organize d3 utilities usage #6287

Merged
merged 5 commits into from
Nov 9, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions superset/assets/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -66,9 +66,13 @@
"d3-array": "^1.2.4",
"d3-cloud": "^1.2.1",
"d3-color": "^1.2.0",
"d3-format": "^1.3.2",
"d3-hierarchy": "^1.1.5",
"d3-sankey": "^0.4.2",
"d3-scale": "^2.1.2",
"d3-selection": "^1.3.2",
"d3-svg-legend": "^1.x",
"d3-time-format": "^2.1.3",
"d3-tip": "^0.9.1",
"datamaps": "^0.5.8",
"datatables.net-bs": "^1.10.15",
Expand Down
14 changes: 14 additions & 0 deletions superset/assets/src/modules/geo.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { round as d3Round } from 'd3-format';

export const defaultViewport = {
longitude: 6.85236157047845,
latitude: 31.222656842808707,
Expand All @@ -7,6 +9,7 @@ export const defaultViewport = {
};

const METER_TO_MILE = 1609.34;

export function unitToRadius(unit, num) {
if (unit === 'square_m') {
return Math.sqrt(num / Math.PI);
Expand All @@ -23,3 +26,14 @@ export function unitToRadius(unit, num) {
}
return null;
}

export const EARTH_CIRCUMFERENCE_KM = 40075.16;
export const MILES_PER_KM = 1.60934;

export function kmToPixels(kilometers, latitude, zoomLevel) {
// Algorithm from: http://wiki.openstreetmap.org/wiki/Zoom_levels
const latitudeRad = latitude * (Math.PI / 180);
// Seems like the zoomLevel is off by one
const kmPerPixel = (EARTH_CIRCUMFERENCE_KM * Math.cos(latitudeRad)) / Math.pow(2, zoomLevel + 9);
return d3Round(kilometers / kmPerPixel, 2);
}
44 changes: 24 additions & 20 deletions superset/assets/src/modules/utils.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
/* eslint camelcase: 0 */
import $ from 'jquery';
import d3 from 'd3';
import { format as d3Format } from 'd3-format';
import { d3Select } from 'd3-selection';
import { timeFormat as d3TimeFormat } from 'd3-time-format';
import { formatDate, UTC } from './dates';

const siFormatter = d3.format('.3s');
const siFormatter = d3Format('.3s');

export function defaultNumberFormatter(n) {
let si = siFormatter(n);
Expand All @@ -15,27 +17,43 @@ export function defaultNumberFormatter(n) {
}

export function d3FormatPreset(format) {
// like d3.format, but with support for presets like 'smart_date'
// like d3Format, but with support for presets like 'smart_date'
if (format === 'smart_date') {
return formatDate;
}
if (format) {
return d3.format(format);
return d3Format(format);
}
return defaultNumberFormatter;
}

export const d3TimeFormatPreset = function (format) {
const effFormat = format || 'smart_date';
if (effFormat === 'smart_date') {
return formatDate;
}
const f = d3.time.format(effFormat);
const f = d3TimeFormat(effFormat);
return function (dttm) {
const d = UTC(new Date(dttm));
return f(d);
};
};

const formatters = {};

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 {
return formatters[format](number);
} catch (e) {
return 'ERR';
}
}

/*
Utility function that takes a d3 svg:text selection and a max width, and splits the
text's text across multiple tspan lines such that any given line does not exceed max width
Expand All @@ -47,7 +65,7 @@ export function wrapSvgText(text, width, adjustedY) {
const lineHeight = 1;
// ems
text.each(function () {
const d3Text = d3.select(this);
const d3Text = d3Select(this);
const words = d3Text.text().split(/\s+/);
let word;
let line = [];
Expand Down Expand Up @@ -118,20 +136,6 @@ export const fixDataTableBodyHeight = function ($tableDom, height) {
$tableDom.find('.dataTables_scrollBody').css('max-height', height - headHeight - controlsHeight - paginationHeight);
};

export function d3format(format, number) {
const formatters = {};
// Formats a number and memoizes formatters to be reused
format = format || '.3s';
if (!(format in formatters)) {
formatters[format] = d3.format(format);
}
try {
return formatters[format](number);
} catch (e) {
return 'ERR';
}
}

export function formatSelectOptionsForRange(start, end) {
// outputs array of arrays
// formatSelectOptionsForRange(1, 5)
Expand Down
11 changes: 0 additions & 11 deletions superset/assets/src/utils/common.js
Original file line number Diff line number Diff line change
@@ -1,24 +1,13 @@
import d3 from 'd3';
import { SupersetClient } from '@superset-ui/connection';
import getClientErrorObject from './getClientErrorObject';

export const EARTH_CIRCUMFERENCE_KM = 40075.16;
export const LUMINANCE_RED_WEIGHT = 0.2126;
export const LUMINANCE_GREEN_WEIGHT = 0.7152;
export const LUMINANCE_BLUE_WEIGHT = 0.0722;
export const MILES_PER_KM = 1.60934;

// Regexp for the label added to time shifted series (1 hour offset, 2 days offset, etc.)
export const TIME_SHIFT_PATTERN = /\d+ \w+ offset/;

export function kmToPixels(kilometers, latitude, zoomLevel) {
// Algorithm from: http://wiki.openstreetmap.org/wiki/Zoom_levels
const latitudeRad = latitude * (Math.PI / 180);
// Seems like the zoomLevel is off by one
const kmPerPixel = (EARTH_CIRCUMFERENCE_KM * Math.cos(latitudeRad)) / Math.pow(2, zoomLevel + 9);
return d3.round(kilometers / kmPerPixel, 2);
}

export function rgbLuminance(r, g, b) {
// Formula: https://en.wikipedia.org/wiki/Relative_luminance
return LUMINANCE_RED_WEIGHT * r + LUMINANCE_GREEN_WEIGHT * g + LUMINANCE_BLUE_WEIGHT * b;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import * as color from 'd3-color';
import d3 from 'd3';
import { format as d3Format } from 'd3-format';
import { d3FormatPreset } from '../../modules/utils';
import { renderTooltipFactory } from './BigNumber';

Expand Down Expand Up @@ -43,7 +43,7 @@ export default function transformProps(chartProps) {
const compareValue = sortedData[compareIndex][metricName];
percentChange = compareValue === 0
? 0 : (bigNumber - compareValue) / Math.abs(compareValue);
const formatPercentChange = d3.format('+.1%');
const formatPercentChange = d3Format('+.1%');
formattedSubheader = `${formatPercentChange(percentChange)} ${compareSuffix}`;
}
}
Expand Down
4 changes: 2 additions & 2 deletions superset/assets/src/visualizations/Horizon/HorizonChart.jsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import React from 'react';
import PropTypes from 'prop-types';
import d3 from 'd3';
import { extent as d3Extent } from 'd3-array';
import HorizonRow, { DEFAULT_COLORS } from './HorizonRow';
import './HorizonChart.css';

Expand Down Expand Up @@ -52,7 +52,7 @@ class HorizonChart extends React.PureComponent {
(acc, current) => acc.concat(current.values),
[],
);
yDomain = d3.extent(allValues, d => d.y);
yDomain = d3Extent(allValues, d => d.y);
}

return (
Expand Down
7 changes: 4 additions & 3 deletions superset/assets/src/visualizations/Horizon/HorizonRow.jsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import React from 'react';
import PropTypes from 'prop-types';
import d3 from 'd3';
import { extent as d3Extent } from 'd3-array';
import { scaleLinear } from 'd3-scale';

export const DEFAULT_COLORS = [
'#313695',
Expand Down Expand Up @@ -91,8 +92,8 @@ class HorizonRow extends React.PureComponent {
}

// Create y-scale
const [min, max] = yDomain || d3.extent(data, d => d.y);
const y = d3.scale.linear()
const [min, max] = yDomain || d3Extent(data, d => d.y);
const y = scaleLinear()
.domain([0, Math.max(-min, max)])
.range([0, height]);

Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,10 @@
import d3 from 'd3';
import Immutable from 'immutable';
import React from 'react';
import PropTypes from 'prop-types';
import ViewportMercator from 'viewport-mercator-project';
import {
kmToPixels,
rgbLuminance,
MILES_PER_KM,
} from '../../utils/common';
import { round as d3Round } from 'd3-format';
import { kmToPixels, MILES_PER_KM } from '../../modules/geo';
import { rgbLuminance } from '../../utils/common';

const propTypes = {
aggregation: PropTypes.string,
Expand Down Expand Up @@ -131,7 +128,7 @@ class ScatterPlotGlowOverlay extends React.Component {
if ((props.renderWhileDragging || !props.isDragging) && props.locations) {
props.locations.forEach(function _forEach(location, i) {
const pixel = mercator.project(props.lngLatAccessor(location));
const pixelRounded = [d3.round(pixel[0], 1), d3.round(pixel[1], 1)];
const pixelRounded = [d3Round(pixel[0], 1), d3Round(pixel[1], 1)];

if (pixelRounded[0] + radius >= 0
&& pixelRounded[0] - radius < props.width
Expand All @@ -140,8 +137,8 @@ class ScatterPlotGlowOverlay extends React.Component {
ctx.beginPath();
if (location.get('properties').get('cluster')) {
let clusterLabel = clusterLabelMap[i];
const scaledRadius = d3.round(Math.pow(clusterLabel / maxLabel, 0.5) * radius, 1);
const fontHeight = d3.round(scaledRadius * 0.5, 1);
const scaledRadius = d3Round(Math.pow(clusterLabel / maxLabel, 0.5) * radius, 1);
const fontHeight = d3Round(scaledRadius * 0.5, 1);
const gradient = ctx.createRadialGradient(
pixelRounded[0], pixelRounded[1], scaledRadius,
pixelRounded[0], pixelRounded[1], 0,
Expand Down Expand Up @@ -177,17 +174,17 @@ class ScatterPlotGlowOverlay extends React.Component {
if (radiusProperty !== null) {
const pointLatitude = props.lngLatAccessor(location)[1];
if (props.pointRadiusUnit === 'Kilometers') {
pointLabel = d3.round(pointRadius, 2) + 'km';
pointLabel = d3Round(pointRadius, 2) + 'km';
pointRadius = kmToPixels(pointRadius, pointLatitude, props.zoom);
} else if (props.pointRadiusUnit === 'Miles') {
pointLabel = d3.round(pointRadius, 2) + 'mi';
pointLabel = d3Round(pointRadius, 2) + 'mi';
pointRadius = kmToPixels(pointRadius * MILES_PER_KM, pointLatitude, props.zoom);
}
}

if (pointMetric !== null) {
pointLabel = Number.isFinite(parseFloat(pointMetric))
? d3.round(pointMetric, 2)
? d3Round(pointMetric, 2)
: pointMetric;
}

Expand All @@ -196,13 +193,13 @@ class ScatterPlotGlowOverlay extends React.Component {
pointRadius = defaultRadius;
}

ctx.arc(pixelRounded[0], pixelRounded[1], d3.round(pointRadius, 1), 0, Math.PI * 2);
ctx.arc(pixelRounded[0], pixelRounded[1], d3Round(pointRadius, 1), 0, Math.PI * 2);
ctx.fillStyle = 'rgb(' + rgb[1] + ', ' + rgb[2] + ', ' + rgb[3] + ')';
ctx.fill();

if (pointLabel !== undefined) {
this.drawText(ctx, pixelRounded, {
fontHeight: d3.round(pointRadius, 1),
fontHeight: d3Round(pointRadius, 1),
label: pointLabel,
radius: pointRadius,
rgb,
Expand Down
7 changes: 4 additions & 3 deletions superset/assets/src/visualizations/Table/Table.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import PropTypes from 'prop-types';
import dt from 'datatables.net-bs';
import 'datatables.net-bs/css/dataTables.bootstrap.css';
import dompurify from 'dompurify';
import { format as d3Format } from 'd3-format';
import { fixDataTableBodyHeight, d3TimeFormatPreset } from '../../modules/utils';
import './Table.css';

Expand Down Expand Up @@ -45,8 +46,8 @@ const propTypes = {
]),
};

const formatValue = d3.format('0,000');
const formatPercent = d3.format('.3p');
const formatValue = d3Format(',.0d');
const formatPercent = d3Format('.3p');
function NOOP() {}

function TableVis(element, props) {
Expand Down Expand Up @@ -129,7 +130,7 @@ function TableVis(element, props) {
html = `<span class="like-pre">${dompurify.sanitize(val)}</span>`;
}
if (isMetric) {
html = d3.format(format || '0.3s')(val);
html = d3Format(format || '0.3s')(val);
}
if (key[0] === '%') {
html = formatPercent(val);
Expand Down
6 changes: 3 additions & 3 deletions superset/assets/src/visualizations/TimeTable/TimeTable.jsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import React from 'react';
import PropTypes from 'prop-types';
import d3 from 'd3';
import Mustache from 'mustache';
import { scaleLinear } from 'd3-scale';
import { Table, Thead, Th, Tr, Td } from 'reactable';

import MetricOption from '../../components/MetricOption';
Expand All @@ -19,7 +19,7 @@ function colorFromBounds(value, bounds, colorBounds = ACCESSIBLE_COLOR_BOUNDS) {
const [min, max] = bounds;
const [minColor, maxColor] = colorBounds;
if (min !== null && max !== null) {
const colorScale = d3.scale.linear()
const colorScale = scaleLinear()
.domain([min, (max + min) / 2, max])
.range([minColor, 'grey', maxColor]);
return colorScale(value);
Expand Down Expand Up @@ -131,7 +131,7 @@ class TimeTable extends React.PureComponent {
showYAxis={column.showYAxis}
renderTooltip={({ index }) => (
<div>
<strong>{d3format(column.d3Format, sparkData[index])}</strong>
<strong>{d3format(column.d3format, sparkData[index])}</strong>
<div>{formatDate(entries[index].time)}</div>
</div>
)}
Expand Down
8 changes: 4 additions & 4 deletions superset/assets/yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -3433,7 +3433,7 @@ d3-ease@1:
version "1.0.5"
resolved "https://registry.yarnpkg.com/d3-ease/-/d3-ease-1.0.5.tgz#8ce59276d81241b1b72042d6af2d40e76d936ffb"

d3-format@1, d3-format@^1.2.0:
d3-format@1, d3-format@^1.2.0, d3-format@^1.3.2:
version "1.3.2"
resolved "https://registry.yarnpkg.com/d3-format/-/d3-format-1.3.2.tgz#6a96b5e31bcb98122a30863f7d92365c00603562"

Expand Down Expand Up @@ -3489,7 +3489,7 @@ d3-scale@^1.0.5, d3-scale@^1.0.6:
d3-time "1"
d3-time-format "2"

d3-scale@^2.0.0:
d3-scale@^2.0.0, d3-scale@^2.1.2:
version "2.1.2"
resolved "https://registry.yarnpkg.com/d3-scale/-/d3-scale-2.1.2.tgz#4e932b7b60182aee9073ede8764c98423e5f9a94"
dependencies:
Expand All @@ -3500,7 +3500,7 @@ d3-scale@^2.0.0:
d3-time "1"
d3-time-format "2"

d3-selection@1, d3-selection@^1.1.0, d3-selection@^1.3.0:
d3-selection@1, d3-selection@^1.1.0, d3-selection@^1.3.0, d3-selection@^1.3.2:
version "1.3.2"
resolved "https://registry.yarnpkg.com/d3-selection/-/d3-selection-1.3.2.tgz#6e70a9df60801c8af28ac24d10072d82cbfdf652"

Expand All @@ -3514,7 +3514,7 @@ d3-svg-legend@^1.x:
version "1.13.0"
resolved "https://registry.yarnpkg.com/d3-svg-legend/-/d3-svg-legend-1.13.0.tgz#6217478c9add9d62cb333617e1961311a41a4db3"

d3-time-format@2:
d3-time-format@2, d3-time-format@^2.1.3:
version "2.1.3"
resolved "https://registry.yarnpkg.com/d3-time-format/-/d3-time-format-2.1.3.tgz#ae06f8e0126a9d60d6364eac5b1533ae1bac826b"
dependencies:
Expand Down