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

Added y-axis split to area, y-scatter and column charts #581

Merged
merged 4 commits into from
May 22, 2019
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
6 changes: 5 additions & 1 deletion packages/perspective-viewer-d3fc/src/js/axis/axisFactory.js
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,9 @@ export const axisFactory = settings => {
labelFunction: axis.labelFunction,
component: {
bottom: component.bottom,
left: component.left
left: component.left,
top: component.top,
right: component.right
},
size: component.size,
decorate: component.decorate,
Expand All @@ -73,6 +75,8 @@ export const axisFactory = settings => {
const defaultComponent = () => ({
bottom: fc.axisBottom,
left: fc.axisLeft,
top: fc.axisTop,
right: fc.axisRight,
decorate: () => {}
});

Expand Down
43 changes: 38 additions & 5 deletions packages/perspective-viewer-d3fc/src/js/axis/axisSplitter.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,10 @@
*/
import {splitterLabels} from "./splitterLabels";

export const axisSplitter = (settings, sourceData) => {
export const axisSplitter = (settings, sourceData, splitFn = dataSplitFunction) => {
let color;
let data;
let altData;

// splitMainValues is an array of main-value names to put into the alt-axis
const splitMainValues = settings.splitMainValues || [];
Expand All @@ -21,8 +23,8 @@ export const axisSplitter = (settings, sourceData) => {
const haveSplit = settings["mainValues"].some(m => altValue(m.name));

// Split the data into main and alt displays
const data = haveSplit ? sourceData.map(d => d.filter(v => !altValue(v.key))) : sourceData;
const altData = haveSplit ? sourceData.map(d => d.filter(v => altValue(v.key))) : null;
data = haveSplit ? splitFn(sourceData, key => !altValue(key)) : sourceData;
altData = haveSplit ? splitFn(sourceData, altValue) : null;

// Renderer to show the special controls for moving between axes
const splitter = selection => {
Expand Down Expand Up @@ -54,8 +56,39 @@ export const axisSplitter = (settings, sourceData) => {
};

splitter.haveSplit = () => haveSplit;
splitter.data = () => data;
splitter.altData = () => altData;

splitter.data = (...args) => {
if (!args.length) {
return data;
}
data = args[0];
return splitter;
};
splitter.altData = (...args) => {
if (!args.length) {
return altData;
}
altData = args[0];
return splitter;
};

return splitter;
};

export const dataSplitFunction = (sourceData, isIncludedFn) => {
return sourceData.map(d => d.filter(v => isIncludedFn(v.key)));
};

export const dataBlankFunction = (sourceData, isIncludedFn) => {
return sourceData.map(series => {
if (!isIncludedFn(series.key)) {
// Blank this data
return series.map(v => Object.assign({}, v, {mainValue: null}));
}
return series;
});
};

export const groupedBlankFunction = (sourceData, isIncludedFn) => {
return sourceData.map(group => dataBlankFunction(group, isIncludedFn));
};
6 changes: 4 additions & 2 deletions packages/perspective-viewer-d3fc/src/js/axis/chartFactory.js
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ const chartFactory = (xAxis, yAxis, cartesian, canvas) => {
.style("-ms-grid-column", 5)
.style("grid-row", 3)
.style("-ms-grid-row", 3)
.style("width", "1em")
.style("width", altAxis.size || "1em")
.style("display", "flex")
.style("align-items", "center")
.style("justify-content", "center")
Expand All @@ -89,7 +89,9 @@ const chartFactory = (xAxis, yAxis, cartesian, canvas) => {
.style("transform", "rotate(-90deg)");

const y2Scale = altAxis.scale.domain(altAxis.domain);
const yAxisComponent = fc.axisRight(y2Scale);
const yAxisComponent = altAxis.component.right(y2Scale);
yAxisComponent.tickFormat(altAxis.tickFormatFunction);
if (altAxis.decorate) yAxisComponent.decorate(altAxis.decorate);

// Render the axis
y2AxisDataJoin(container, ["right"])
Expand Down
16 changes: 16 additions & 0 deletions packages/perspective-viewer-d3fc/src/js/axis/domainMatchOrigins.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
export default (domain1, domain2) => {
if (!isMatchable(domain1) || !isMatchable(domain2)) return;

const ratio1 = originRatio(domain1);
const ratio2 = originRatio(domain2);

if (ratio1 > ratio2) {
domain2[0] = adjustLowerBound(domain2, ratio1);
} else {
domain1[0] = adjustLowerBound(domain1, ratio2);
}
};

const isMatchable = domain => domain.length === 2 && !isNaN(domain[0]) && !isNaN(domain[1]) && domain[0] !== domain[1];
const originRatio = domain => (0 - domain[0]) / (domain[1] - domain[0]);
const adjustLowerBound = (domain, ratio) => (ratio * domain[1]) / (ratio - 1);
31 changes: 26 additions & 5 deletions packages/perspective-viewer-d3fc/src/js/axis/ordinalAxis.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import * as d3 from "d3";
import * as fc from "d3fc";
import minBandwidth from "./minBandwidth";
import {flattenArray} from "./flatten";
import {multiAxisBottom, multiAxisLeft} from "../d3fc/axis/multi-axis";
import {multiAxisBottom, multiAxisLeft, multiAxisTop, multiAxisRight} from "../d3fc/axis/multi-axis";
import {getChartContainer} from "../plugin/root";

export const scale = () => minBandwidth(d3.scaleBand()).padding(0.5);
Expand Down Expand Up @@ -69,8 +69,8 @@ export const component = settings => {
const tickSizeInner = multiLevel ? groupTickLayout.map(l => l.size) : groupTickLayout[0].size;
const tickSizeOuter = groupTickLayout.reduce((s, v) => s + v.size, 0);

const createAxis = scale => {
const axis = pickAxis(multiLevel)(scale);
const createAxis = base => scale => {
const axis = base(scale);

if (multiLevel) {
axis.groups(levelGroups)
Expand All @@ -87,9 +87,12 @@ export const component = settings => {
hideOverlappingLabels(s, rotation);
};

const axisSet = getAxisSet(multiLevel);
return {
bottom: createAxis,
left: createAxis,
bottom: createAxis(axisSet.bottom),
left: createAxis(axisSet.left),
right: createAxis(axisSet.right),
top: createAxis(axisSet.top),
size: `${tickSizeOuter + 10}px`,
decorate
};
Expand All @@ -102,6 +105,24 @@ export const component = settings => {
return orient === "horizontal" ? fc.axisOrdinalBottom : fc.axisOrdinalLeft;
};

const getAxisSet = multiLevel => {
if (multiLevel) {
return {
bottom: multiAxisBottom,
left: multiAxisLeft,
top: multiAxisTop,
right: multiAxisRight
};
} else {
return {
bottom: fc.axisOrdinalBottom,
left: fc.axisOrdinalLeft,
top: fc.axisOrdinalTop,
right: fc.axisOrdinalRight
};
}
};

const axisGroups = domain => {
const groups = [];
domain.forEach(tick => {
Expand Down
29 changes: 24 additions & 5 deletions packages/perspective-viewer-d3fc/src/js/charts/area.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import * as fc from "d3fc";
import {axisFactory} from "../axis/axisFactory";
import {chartSvgFactory} from "../axis/chartFactory";
import {axisSplitter} from "../axis/axisSplitter";
import {AXIS_TYPES} from "../axis/axisType";
import {areaSeries} from "../series/areaSeries";
import {seriesColors} from "../series/seriesColors";
Expand All @@ -35,15 +36,25 @@ function areaChart(container, settings) {
.excludeType(AXIS_TYPES.linear)
.settingName("crossValues")
.valueName("crossValue")(data);
const yAxis = axisFactory(settings)
const yAxisFactory = axisFactory(settings)
.settingName("mainValues")
.valueName("mainValue")
.excludeType(AXIS_TYPES.ordinal)
.orient("vertical")
.include([0])
.paddingStrategy(hardLimitZeroPadding())(data);
.paddingStrategy(hardLimitZeroPadding());

const chart = chartSvgFactory(xAxis, yAxis).plotArea(withGridLines(series).orient("vertical"));
// Check whether we've split some values into a second y-axis
const splitter = axisSplitter(settings, data).color(color);

const yAxis1 = yAxisFactory(splitter.data());

// No grid lines if splitting y-axis
const plotSeries = splitter.haveSplit() ? series : withGridLines(series).orient("vertical");

const chart = chartSvgFactory(xAxis, yAxis1)
.axisSplitter(splitter)
.plotArea(plotSeries);

chart.yNice && chart.yNice();

Expand All @@ -55,12 +66,20 @@ function areaChart(container, settings) {
const toolTip = nearbyTip()
.settings(settings)
.xScale(xAxis.scale)
.yScale(yAxis.scale)
.yScale(yAxis1.scale)
.color(color)
.data(data);

if (splitter.haveSplit()) {
// Create the y-axis data for the alt-axis
const yAxis2 = yAxisFactory(splitter.altData());
chart.altAxis(yAxis2);
// Give the tooltip the information (i.e. 2 datasets with different scales)
toolTip.data(splitter.data()).altDataWithScale({yScale: yAxis2.scale, data: splitter.altData()});
}

// render
container.datum(data).call(zoomChart);
container.datum(splitter.data()).call(zoomChart);
container.call(toolTip);
container.call(legend);
}
Expand Down
29 changes: 25 additions & 4 deletions packages/perspective-viewer-d3fc/src/js/charts/column.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
import * as fc from "d3fc";
import {axisFactory} from "../axis/axisFactory";
import {chartSvgFactory} from "../axis/chartFactory";
import domainMatchOrigins from "../axis/domainMatchOrigins";
import {axisSplitter, dataBlankFunction, groupedBlankFunction} from "../axis/axisSplitter";
import {AXIS_TYPES} from "../axis/axisType";
import {barSeries} from "../series/barSeries";
import {seriesColors} from "../series/seriesColors";
Expand Down Expand Up @@ -37,15 +39,26 @@ function columnChart(container, settings) {
.excludeType(AXIS_TYPES.linear)
.settingName("crossValues")
.valueName("crossValue")(data);
const yAxis = axisFactory(settings)
const yAxisFactory = axisFactory(settings)
.settingName("mainValues")
.valueName("mainValue")
.excludeType(AXIS_TYPES.ordinal)
.orient("vertical")
.include([0])
.paddingStrategy(hardLimitZeroPadding())(data);
.paddingStrategy(hardLimitZeroPadding());

const chart = chartSvgFactory(xAxis, yAxis).plotArea(withGridLines(series).orient("vertical"));
// Check whether we've split some values into a second y-axis
const blankFunction = settings.mainValues.length > 1 ? groupedBlankFunction : dataBlankFunction;
const splitter = axisSplitter(settings, data, blankFunction).color(color);

const yAxis1 = yAxisFactory(splitter.data());

// No grid lines if splitting y-axis
const plotSeries = splitter.haveSplit() ? series : withGridLines(series).orient("vertical");

const chart = chartSvgFactory(xAxis, yAxis1)
.axisSplitter(splitter)
.plotArea(plotSeries);

if (chart.xPaddingInner) {
chart.xPaddingInner(0.5);
Expand All @@ -59,8 +72,16 @@ function columnChart(container, settings) {
.settings(settings)
.xScale(xAxis.scale);

if (splitter.haveSplit()) {
// Create the y-axis data for the alt-axis
const yAxis2 = yAxisFactory(splitter.altData());

domainMatchOrigins(yAxis1.domain, yAxis2.domain);
chart.yDomain(yAxis1.domain).altAxis(yAxis2);
}

// render
container.datum(data).call(zoomChart);
container.datum(splitter.data()).call(zoomChart);
container.call(legend);
}
columnChart.plugin = {
Expand Down
29 changes: 24 additions & 5 deletions packages/perspective-viewer-d3fc/src/js/charts/y-scatter.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import * as fc from "d3fc";
import {axisFactory} from "../axis/axisFactory";
import {AXIS_TYPES} from "../axis/axisType";
import {chartSvgFactory} from "../axis/chartFactory";
import {axisSplitter} from "../axis/axisSplitter";
import {seriesColors} from "../series/seriesColors";
import {categoryPointSeries, symbolType} from "../series/categoryPointSeries";
import {groupData} from "../data/groupData";
Expand Down Expand Up @@ -43,13 +44,23 @@ function yScatter(container, settings) {
.excludeType(AXIS_TYPES.linear)
.settingName("crossValues")
.valueName("crossValue")(data);
const yAxis = axisFactory(settings)
const yAxisFactory = axisFactory(settings)
.settingName("mainValues")
.valueName("mainValue")
.orient("vertical")
.paddingStrategy(paddingStrategy)(data);
.paddingStrategy(paddingStrategy);

const chart = chartSvgFactory(xAxis, yAxis).plotArea(withGridLines(series).orient("vertical"));
// Check whether we've split some values into a second y-axis
const splitter = axisSplitter(settings, data).color(color);

const yAxis1 = yAxisFactory(splitter.data());

// No grid lines if splitting y-axis
const plotSeries = splitter.haveSplit() ? series : withGridLines(series).orient("vertical");

const chart = chartSvgFactory(xAxis, yAxis1)
.axisSplitter(splitter)
.plotArea(plotSeries);

chart.yNice && chart.yNice();

Expand All @@ -61,12 +72,20 @@ function yScatter(container, settings) {
const toolTip = nearbyTip()
.settings(settings)
.xScale(xAxis.scale)
.yScale(yAxis.scale)
.yScale(yAxis1.scale)
.color(color)
.data(data);

if (splitter.haveSplit()) {
// Create the y-axis data for the alt-axis
const yAxis2 = yAxisFactory(splitter.altData());
chart.altAxis(yAxis2);
// Give the tooltip the information (i.e. 2 datasets with different scales)
toolTip.data(splitter.data()).altDataWithScale({yScale: yAxis2.scale, data: splitter.altData()});
}

// render
container.datum(data).call(zoomChart);
container.datum(splitter.data()).call(zoomChart);
container.call(toolTip);
if (legend) {
container.call(legend);
Expand Down
Loading