зеркало из https://github.com/microsoft/SandDance.git
Show z axis scale (#349)
* use UrlData type * refactor SpecBuilderProps * remove unused layout * remove linear scale from band * use src * z axis options * axisSelection on z * add z scale * add density treemap spec test * stacks z scale for count * show z scale * remove zaxiscolor * show z axis only in 3d view * filter null scales * show z axis * minor version bump * version bump + changelog * eslint * use vega-typings
This commit is contained in:
Родитель
bd60883ccf
Коммит
2dc07f2d51
|
@ -56,6 +56,7 @@
|
|||
<option>column.json</option>
|
||||
<option>density-facet-cross.json</option>
|
||||
<option>density-facet-wrap.json</option>
|
||||
<option>density-treemap.json</option>
|
||||
<option>density.json</option>
|
||||
<option>scatter-facet-cross.json</option>
|
||||
<option>scatter-facet-wrap.json</option>
|
||||
|
|
|
@ -0,0 +1,19 @@
|
|||
{
|
||||
"columns": {
|
||||
"x": "Longitude",
|
||||
"y": "Latitude",
|
||||
"color": "Obama",
|
||||
"z": "Education",
|
||||
"sort": "State",
|
||||
"size": "TotalPop"
|
||||
},
|
||||
"facetStyle": "wrap",
|
||||
"scheme": "redblue",
|
||||
"totalStyle": "sum-treemap",
|
||||
"size": {
|
||||
"height": 600,
|
||||
"width": 800
|
||||
},
|
||||
"chart": "density",
|
||||
"view": "2d"
|
||||
}
|
|
@ -1,5 +1,8 @@
|
|||
# Change Log
|
||||
|
||||
## [3.3.0]
|
||||
- Show the z axis scale in 3d
|
||||
|
||||
## [3.2.0]
|
||||
- Fix for last bin of quantitative band scale
|
||||
- Support virtual workspaces
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
"name": "azdata-sanddance",
|
||||
"displayName": "SandDance for Azure Data Studio",
|
||||
"description": "Visually explore, understand, and present your data.",
|
||||
"version": "3.2.0",
|
||||
"version": "3.3.0",
|
||||
"icon": "sanddance-logo.png",
|
||||
"preview": true,
|
||||
"publisher": "msrvida",
|
||||
|
|
|
@ -1,5 +1,8 @@
|
|||
# Change Log
|
||||
|
||||
## [3.3.0]
|
||||
- Show the z axis scale in 3d
|
||||
|
||||
## [3.2.0]
|
||||
- Fix for last bin of quantitative band scale
|
||||
- Support virtual workspaces
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
"name": "vscode-sanddance",
|
||||
"displayName": "SandDance for VSCode",
|
||||
"description": "Visually explore, understand, and present your data.",
|
||||
"version": "3.2.0",
|
||||
"version": "3.3.0",
|
||||
"icon": "sanddance-logo.png",
|
||||
"publisher": "msrvida",
|
||||
"repository": {
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
"displayName": "SandDance 2019",
|
||||
"guid": "SandDance201929976D117A654D0BAB8E96507442D80B",
|
||||
"visualClassName": "Visual",
|
||||
"version": "3.2.0",
|
||||
"version": "3.3.0",
|
||||
"description": "Visually explore, understand, and present your data.",
|
||||
"supportUrl": "https://github.com/Microsoft/SandDance/issues",
|
||||
"gitHubUrl": "https://github.com/microsoft/SandDance"
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@msrvida/sanddance-specs",
|
||||
"version": "1.3.0",
|
||||
"version": "1.4.0",
|
||||
"description": "SandDance Vega specification generator.",
|
||||
"main": "dist/es6/index.js",
|
||||
"types": "dist/es6/index.d.ts",
|
||||
|
|
|
@ -6,7 +6,7 @@ import { GlobalScope } from './globalScope';
|
|||
import { AxisScale, AxisScales, GlobalScales } from './interfaces';
|
||||
import { addAxes, addScales } from './scope';
|
||||
import { SpecColumns, SpecViewOptions } from './types';
|
||||
import { Column } from '@msrvida/chart-types';
|
||||
import { Column, View } from '@msrvida/chart-types';
|
||||
import {
|
||||
Axis,
|
||||
NewSignal,
|
||||
|
@ -28,16 +28,17 @@ export interface AxesScopeMap {
|
|||
}
|
||||
|
||||
interface Props {
|
||||
globalScope: GlobalScope,
|
||||
allGlobalScales: GlobalScales[],
|
||||
axisScales: AxisScales,
|
||||
plotOffsetSignals: { x: NewSignal, y: NewSignal },
|
||||
axesOffsets: { x: number, y: number },
|
||||
axesTitlePadding: { x: number, y: number },
|
||||
labelBaseline: { x: TextBaselineValue, y: TextBaselineValue },
|
||||
specColumns: SpecColumns,
|
||||
specViewOptions: SpecViewOptions,
|
||||
axesScopes: AxesScopeMap
|
||||
globalScope: GlobalScope;
|
||||
allGlobalScales: GlobalScales[];
|
||||
axisScales: AxisScales;
|
||||
plotOffsetSignals: { x: NewSignal, y: NewSignal };
|
||||
axesOffsets: { x: number, y: number };
|
||||
axesTitlePadding: { x: number, y: number };
|
||||
labelBaseline: { x: TextBaselineValue, y: TextBaselineValue };
|
||||
specColumns: SpecColumns;
|
||||
specViewOptions: SpecViewOptions;
|
||||
axesScopes: AxesScopeMap;
|
||||
view: View;
|
||||
}
|
||||
|
||||
export function addGlobalAxes(props: Props) {
|
||||
|
@ -46,16 +47,27 @@ export function addGlobalAxes(props: Props) {
|
|||
|
||||
allGlobalScales.forEach(globalScales => {
|
||||
const { scales } = globalScales;
|
||||
for (let s in scales) {
|
||||
let _scales: Scale[] = scales[s];
|
||||
for (let xyz in scales) {
|
||||
let _scales: Scale[] = scales[xyz];
|
||||
if (_scales) {
|
||||
addScales(scope, ..._scales);
|
||||
if (globalScales.showAxes && axisScales && s !== 'z') {
|
||||
let axisScale: AxisScale = axisScales[s];
|
||||
let { showAxes } = globalScales;
|
||||
let zindex: number = undefined;
|
||||
if (xyz === 'z') {
|
||||
showAxes = false;
|
||||
if (props.view === '3d' && specViewOptions.zAxisOptions) {
|
||||
if (specViewOptions.zAxisOptions.showZAxis) {
|
||||
showAxes = true;
|
||||
zindex = specViewOptions.zAxisOptions.zIndex;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (showAxes && axisScales) {
|
||||
let axisScale: AxisScale = axisScales[xyz];
|
||||
if (axisScale) {
|
||||
const lineColor = specViewOptions.colors.axisLine;
|
||||
const horizontal = s === 'x';
|
||||
const column: Column = specColumns[s] || { quantitative: true };
|
||||
const horizontal = xyz === 'x';
|
||||
const column: Column = specColumns[xyz] || { quantitative: true };
|
||||
const title = axisScale.title;
|
||||
const props: AxisProps = {
|
||||
title,
|
||||
|
@ -63,8 +75,9 @@ export function addGlobalAxes(props: Props) {
|
|||
column,
|
||||
specViewOptions,
|
||||
lineColor,
|
||||
titlePadding: axesTitlePadding[s],
|
||||
labelBaseline: labelBaseline[s]
|
||||
titlePadding: axesTitlePadding[xyz],
|
||||
labelBaseline: labelBaseline[xyz],
|
||||
zindex
|
||||
};
|
||||
axesScopes['main'].forEach(a => addAxes(a.scope, createAxis({
|
||||
...props,
|
||||
|
@ -74,8 +87,8 @@ export function addGlobalAxes(props: Props) {
|
|||
showLines: a.lines
|
||||
})));
|
||||
|
||||
if (axesScopes[s]) {
|
||||
axesScopes[s].forEach(a => addAxes(a.scope, createAxis({
|
||||
if (axesScopes[xyz]) {
|
||||
axesScopes[xyz].forEach(a => addAxes(a.scope, createAxis({
|
||||
...props,
|
||||
scale: a.scale || _scales[0],
|
||||
showTitle: a.title,
|
||||
|
@ -84,9 +97,9 @@ export function addGlobalAxes(props: Props) {
|
|||
})));
|
||||
}
|
||||
|
||||
if (plotOffsetSignals[s] && axesOffsets[s]) {
|
||||
const plotOffsetSignal = plotOffsetSignals[s] as NewSignal;
|
||||
plotOffsetSignal.update = `${axesOffsets[s]}`;
|
||||
if (plotOffsetSignals[xyz] && axesOffsets[xyz]) {
|
||||
const plotOffsetSignal = plotOffsetSignals[xyz] as NewSignal;
|
||||
plotOffsetSignal.update = `${axesOffsets[xyz]}`;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -107,11 +120,13 @@ interface AxisProps {
|
|||
showLabels?: boolean;
|
||||
titlePadding: number;
|
||||
labelBaseline: TextBaselineValue;
|
||||
zindex: number;
|
||||
}
|
||||
|
||||
function createAxis(props: AxisProps) {
|
||||
const { column, horizontal, labelBaseline, lineColor, scale, showLabels, showTitle, showLines, specViewOptions, title, titlePadding } = props;
|
||||
const { column, horizontal, labelBaseline, lineColor, scale, showLabels, showTitle, showLines, specViewOptions, title, titlePadding, zindex } = props;
|
||||
const axis: Axis = {
|
||||
zindex,
|
||||
scale: scale.name,
|
||||
orient: horizontal ? 'bottom' : 'left',
|
||||
domain: showLines,
|
||||
|
|
|
@ -1,13 +1,14 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
import { getSpecBuilderForChart } from './charts';
|
||||
import { getSpecBuilderPropsForChart } from './charts';
|
||||
import { inferAll } from './inference';
|
||||
import { SpecContext } from './types';
|
||||
import { SpecResult } from './interfaces';
|
||||
import { ValuesData } from 'vega-typings';
|
||||
import { SpecBuilder } from './specBuilder';
|
||||
|
||||
export function build(context: SpecContext, currData: object[]): SpecResult {
|
||||
const { specColumns } = context;
|
||||
export function build(specContext: SpecContext, currData: object[]): SpecResult {
|
||||
const { specColumns } = specContext;
|
||||
const columns = [
|
||||
specColumns.color,
|
||||
specColumns.facet,
|
||||
|
@ -21,12 +22,22 @@ export function build(context: SpecContext, currData: object[]): SpecResult {
|
|||
];
|
||||
inferAll(columns, currData);
|
||||
|
||||
const specBuilder = getSpecBuilderForChart(context);
|
||||
const specBuilderProps = getSpecBuilderPropsForChart(specContext);
|
||||
const specBuilder = new SpecBuilder(specBuilderProps, specContext);
|
||||
let specResult: SpecResult;
|
||||
|
||||
if (specBuilder) {
|
||||
try {
|
||||
specResult = specBuilder.build();
|
||||
const errors = specBuilder.validate();
|
||||
if (errors.length) {
|
||||
specResult = {
|
||||
errors,
|
||||
specCapabilities: specBuilderProps.specCapabilities,
|
||||
vegaSpec: null
|
||||
};
|
||||
} else {
|
||||
specResult = specBuilder.build();
|
||||
}
|
||||
}
|
||||
catch (e) {
|
||||
specResult = {
|
||||
|
@ -43,7 +54,7 @@ export function build(context: SpecContext, currData: object[]): SpecResult {
|
|||
specResult = {
|
||||
specCapabilities: null,
|
||||
vegaSpec: null,
|
||||
errors: [`could not build spec for ${context.insight.chart}`]
|
||||
errors: [`could not build spec for ${specContext.insight.chart}`]
|
||||
};
|
||||
}
|
||||
return specResult;
|
||||
|
|
|
@ -3,12 +3,12 @@
|
|||
import { SignalNames } from '../constants';
|
||||
import { defaultBins, maxbins, minBarBandWidth } from '../defaults';
|
||||
import { AxisScale, AxisScales } from '../interfaces';
|
||||
import { AggregateContainer, AggregateContainerProps } from '../layouts/aggregateContainer';
|
||||
import { Band, BandProps } from '../layouts/band';
|
||||
import { AggregateContainerProps } from '../layouts/aggregateContainer';
|
||||
import { BandProps } from '../layouts/band';
|
||||
import { LayoutPair } from '../layouts/layout';
|
||||
import { Square, SquareProps } from '../layouts/square';
|
||||
import { Strip, StripProps } from '../layouts/strip';
|
||||
import { Treemap, TreemapProps } from '../layouts/treemap';
|
||||
import { SquareProps } from '../layouts/square';
|
||||
import { StripProps } from '../layouts/strip';
|
||||
import { TreemapProps } from '../layouts/treemap';
|
||||
import { allowNoneForSize } from '../size';
|
||||
import { SpecBuilderProps } from '../specBuilder';
|
||||
import { SpecContext } from '../types';
|
||||
|
@ -35,7 +35,7 @@ export default function (specContext: SpecContext): SpecBuilderProps {
|
|||
z: { title: specColumns.z && specColumns.z.name }
|
||||
};
|
||||
const layouts: LayoutPair[] = [{
|
||||
layoutClass: Band,
|
||||
layoutType: 'Band',
|
||||
props: bandProps
|
||||
}];
|
||||
if (insight.totalStyle === 'sum-strip-percent') {
|
||||
|
@ -50,7 +50,7 @@ export default function (specContext: SpecContext): SpecBuilderProps {
|
|||
z: specColumns.z
|
||||
};
|
||||
layouts.push({
|
||||
layoutClass: Strip,
|
||||
layoutType: 'Strip',
|
||||
props: stripProps
|
||||
});
|
||||
} else {
|
||||
|
@ -63,7 +63,7 @@ export default function (specContext: SpecContext): SpecBuilderProps {
|
|||
showAxes: true
|
||||
};
|
||||
layouts.push({
|
||||
layoutClass: AggregateContainer,
|
||||
layoutType: 'AggregateContainer',
|
||||
props: aggProps
|
||||
});
|
||||
switch (insight.totalStyle) {
|
||||
|
@ -77,7 +77,7 @@ export default function (specContext: SpecContext): SpecBuilderProps {
|
|||
z: specColumns.z
|
||||
};
|
||||
layouts.push({
|
||||
layoutClass: Treemap,
|
||||
layoutType: 'Treemap',
|
||||
props: treemapProps
|
||||
});
|
||||
break;
|
||||
|
@ -93,7 +93,7 @@ export default function (specContext: SpecContext): SpecBuilderProps {
|
|||
z: specColumns.z
|
||||
};
|
||||
layouts.push({
|
||||
layoutClass: Strip,
|
||||
layoutType: 'Strip',
|
||||
props: stripProps
|
||||
});
|
||||
break;
|
||||
|
@ -109,7 +109,7 @@ export default function (specContext: SpecContext): SpecBuilderProps {
|
|||
z: specColumns.z
|
||||
};
|
||||
layouts.push({
|
||||
layoutClass: Strip,
|
||||
layoutType: 'Strip',
|
||||
props: stripProps
|
||||
});
|
||||
break;
|
||||
|
@ -125,7 +125,7 @@ export default function (specContext: SpecContext): SpecBuilderProps {
|
|||
maxGroupedFillSize: aggProps.globalAggregateMaxExtentScaledSignal
|
||||
};
|
||||
layouts.push({
|
||||
layoutClass: Square,
|
||||
layoutType: 'Square',
|
||||
props: squareProps
|
||||
});
|
||||
break;
|
||||
|
@ -147,6 +147,7 @@ export default function (specContext: SpecContext): SpecBuilderProps {
|
|||
},
|
||||
{
|
||||
role: 'z',
|
||||
axisSelection: specColumns.z && specColumns.z.quantitative ? 'range' : 'exact',
|
||||
allowNone: true
|
||||
},
|
||||
{
|
||||
|
|
|
@ -3,12 +3,12 @@
|
|||
import { SignalNames } from '../constants';
|
||||
import { defaultBins, maxbins, minBarBandWidth } from '../defaults';
|
||||
import { AxisScale, AxisScales } from '../interfaces';
|
||||
import { AggregateContainer, AggregateContainerProps } from '../layouts/aggregateContainer';
|
||||
import { Band, BandProps } from '../layouts/band';
|
||||
import { AggregateContainerProps } from '../layouts/aggregateContainer';
|
||||
import { BandProps } from '../layouts/band';
|
||||
import { LayoutPair } from '../layouts/layout';
|
||||
import { Square, SquareProps } from '../layouts/square';
|
||||
import { Strip, StripProps } from '../layouts/strip';
|
||||
import { Treemap, TreemapProps } from '../layouts/treemap';
|
||||
import { SquareProps } from '../layouts/square';
|
||||
import { StripProps } from '../layouts/strip';
|
||||
import { TreemapProps } from '../layouts/treemap';
|
||||
import { allowNoneForSize } from '../size';
|
||||
import { SpecBuilderProps } from '../specBuilder';
|
||||
import { SpecContext } from '../types';
|
||||
|
@ -35,7 +35,7 @@ export default function (specContext: SpecContext): SpecBuilderProps {
|
|||
z: { title: specColumns.z && specColumns.z.name }
|
||||
};
|
||||
const layouts: LayoutPair[] = [{
|
||||
layoutClass: Band,
|
||||
layoutType: 'Band',
|
||||
props: bandProps
|
||||
}];
|
||||
if (insight.totalStyle === 'sum-strip-percent') {
|
||||
|
@ -50,7 +50,7 @@ export default function (specContext: SpecContext): SpecBuilderProps {
|
|||
z: specColumns.z
|
||||
};
|
||||
layouts.push({
|
||||
layoutClass: Strip,
|
||||
layoutType: 'Strip',
|
||||
props: stripProps
|
||||
});
|
||||
} else {
|
||||
|
@ -63,7 +63,7 @@ export default function (specContext: SpecContext): SpecBuilderProps {
|
|||
showAxes: true
|
||||
};
|
||||
layouts.push({
|
||||
layoutClass: AggregateContainer,
|
||||
layoutType: 'AggregateContainer',
|
||||
props: aggProps
|
||||
});
|
||||
switch (insight.totalStyle) {
|
||||
|
@ -77,7 +77,7 @@ export default function (specContext: SpecContext): SpecBuilderProps {
|
|||
z: specColumns.z
|
||||
};
|
||||
layouts.push({
|
||||
layoutClass: Treemap,
|
||||
layoutType: 'Treemap',
|
||||
props: treemapProps
|
||||
});
|
||||
break;
|
||||
|
@ -93,7 +93,7 @@ export default function (specContext: SpecContext): SpecBuilderProps {
|
|||
z: specColumns.z
|
||||
};
|
||||
layouts.push({
|
||||
layoutClass: Strip,
|
||||
layoutType: 'Strip',
|
||||
props: stripProps
|
||||
});
|
||||
break;
|
||||
|
@ -108,7 +108,7 @@ export default function (specContext: SpecContext): SpecBuilderProps {
|
|||
z: specColumns.z
|
||||
};
|
||||
layouts.push({
|
||||
layoutClass: Strip,
|
||||
layoutType: 'Strip',
|
||||
props: stripProps
|
||||
});
|
||||
break;
|
||||
|
@ -124,7 +124,7 @@ export default function (specContext: SpecContext): SpecBuilderProps {
|
|||
maxGroupedFillSize: aggProps.globalAggregateMaxExtentScaledSignal
|
||||
};
|
||||
layouts.push({
|
||||
layoutClass: Square,
|
||||
layoutType: 'Square',
|
||||
props: squareProps
|
||||
});
|
||||
break;
|
||||
|
@ -146,6 +146,7 @@ export default function (specContext: SpecContext): SpecBuilderProps {
|
|||
},
|
||||
{
|
||||
role: 'z',
|
||||
axisSelection: specColumns.z && specColumns.z.quantitative ? 'range' : 'exact',
|
||||
allowNone: true
|
||||
},
|
||||
{
|
||||
|
|
|
@ -3,12 +3,12 @@
|
|||
import { SignalNames } from '../constants';
|
||||
import { defaultBins, maxbins, minBarBandWidth } from '../defaults';
|
||||
import { AxisScales } from '../interfaces';
|
||||
import { AggregateSquare, AggregateSquareProps } from '../layouts/aggregateSquare';
|
||||
import { Band, BandProps } from '../layouts/band';
|
||||
import { AggregateSquareProps } from '../layouts/aggregateSquare';
|
||||
import { BandProps } from '../layouts/band';
|
||||
import { LayoutPair } from '../layouts/layout';
|
||||
import { Square, SquareProps } from '../layouts/square';
|
||||
import { Strip, StripProps } from '../layouts/strip';
|
||||
import { Treemap, TreemapProps } from '../layouts/treemap';
|
||||
import { SquareProps } from '../layouts/square';
|
||||
import { StripProps } from '../layouts/strip';
|
||||
import { TreemapProps } from '../layouts/treemap';
|
||||
import { allowNoneForSize } from '../size';
|
||||
import { SpecBuilderProps } from '../specBuilder';
|
||||
import { SpecContext } from '../types';
|
||||
|
@ -53,15 +53,15 @@ export default function (specContext: SpecContext): SpecBuilderProps {
|
|||
};
|
||||
const layouts: LayoutPair[] = [
|
||||
{
|
||||
layoutClass: Band,
|
||||
layoutType: 'Band',
|
||||
props: vBandProps
|
||||
},
|
||||
{
|
||||
layoutClass: Band,
|
||||
layoutType: 'Band',
|
||||
props: hBandProps
|
||||
},
|
||||
{
|
||||
layoutClass: AggregateSquare,
|
||||
layoutType: 'AggregateSquare',
|
||||
props: aggProps
|
||||
}
|
||||
];
|
||||
|
@ -75,7 +75,7 @@ export default function (specContext: SpecContext): SpecBuilderProps {
|
|||
z: specColumns.z
|
||||
};
|
||||
layouts.push({
|
||||
layoutClass: Treemap,
|
||||
layoutType: 'Treemap',
|
||||
props: treemapProps
|
||||
});
|
||||
break;
|
||||
|
@ -90,7 +90,7 @@ export default function (specContext: SpecContext): SpecBuilderProps {
|
|||
z: specColumns.z
|
||||
};
|
||||
layouts.push({
|
||||
layoutClass: Strip,
|
||||
layoutType: 'Strip',
|
||||
props: stripProps
|
||||
});
|
||||
break;
|
||||
|
@ -104,7 +104,7 @@ export default function (specContext: SpecContext): SpecBuilderProps {
|
|||
z: specColumns.z
|
||||
};
|
||||
layouts.push({
|
||||
layoutClass: Strip,
|
||||
layoutType: 'Strip',
|
||||
props: stripProps
|
||||
});
|
||||
break;
|
||||
|
@ -123,7 +123,7 @@ export default function (specContext: SpecContext): SpecBuilderProps {
|
|||
squareProps.maxGroupedFillSize = aggMaxExtentScaled;
|
||||
};
|
||||
layouts.push({
|
||||
layoutClass: Square,
|
||||
layoutType: 'Square',
|
||||
props: squareProps
|
||||
});
|
||||
break;
|
||||
|
@ -149,6 +149,7 @@ export default function (specContext: SpecContext): SpecBuilderProps {
|
|||
},
|
||||
{
|
||||
role: 'z',
|
||||
axisSelection: specColumns.z && specColumns.z.quantitative ? 'range' : 'exact',
|
||||
allowNone: true
|
||||
},
|
||||
{
|
||||
|
|
|
@ -4,7 +4,7 @@ import { AxisScales } from '../interfaces';
|
|||
import { SignalNames } from '../constants';
|
||||
import { SpecBuilderProps } from '../specBuilder';
|
||||
import { SpecContext } from '../types';
|
||||
import { Square, SquareProps } from '../layouts/square';
|
||||
import { SquareProps } from '../layouts/square';
|
||||
|
||||
export default function (specContext: SpecContext): SpecBuilderProps {
|
||||
const { specColumns } = specContext;
|
||||
|
@ -21,7 +21,7 @@ export default function (specContext: SpecContext): SpecBuilderProps {
|
|||
axisScales,
|
||||
layouts: [
|
||||
{
|
||||
layoutClass: Square,
|
||||
layoutType: 'Square',
|
||||
props: squareProps
|
||||
}
|
||||
],
|
||||
|
@ -30,6 +30,7 @@ export default function (specContext: SpecContext): SpecBuilderProps {
|
|||
roles: [
|
||||
{
|
||||
role: 'z',
|
||||
axisSelection: specColumns.z && specColumns.z.quantitative ? 'range' : 'exact',
|
||||
allowNone: true
|
||||
},
|
||||
{
|
||||
|
|
|
@ -8,8 +8,12 @@ import scatterplot from './scatterplot';
|
|||
import stacks from './stacks';
|
||||
import strips from './strips';
|
||||
import treemap from './treemap';
|
||||
import { SpecBuilder, SpecBuilderProps } from '../specBuilder';
|
||||
import { SpecBuilderProps } from '../specBuilder';
|
||||
import { SpecContext } from '../types';
|
||||
import { getFacetLayout } from '../facetLayout';
|
||||
import { DiscreteColumn } from '../interfaces';
|
||||
import { SignalNames } from '../constants';
|
||||
import { defaultBins, maxbins } from '../defaults';
|
||||
|
||||
const map: { [chart: string]: (specContext: SpecContext) => SpecBuilderProps } = {
|
||||
barchart: barchartV,
|
||||
|
@ -23,13 +27,30 @@ const map: { [chart: string]: (specContext: SpecContext) => SpecBuilderProps } =
|
|||
treemap
|
||||
};
|
||||
|
||||
export function getSpecBuilderForChart(specContext: SpecContext) {
|
||||
const { insight } = specContext;
|
||||
let props: SpecBuilderProps;
|
||||
|
||||
export function getSpecBuilderPropsForChart(specContext: SpecContext) {
|
||||
const { insight, specColumns, specViewOptions } = specContext;
|
||||
const fn = map[insight.chart];
|
||||
if (fn) {
|
||||
props = fn(specContext);
|
||||
return new SpecBuilder({ ...props, specContext });
|
||||
const props = fn(specContext);
|
||||
if (insight.columns.facet) {
|
||||
const discreteFacetColumn: DiscreteColumn = {
|
||||
column: specColumns.facet,
|
||||
defaultBins,
|
||||
maxbins,
|
||||
maxbinsSignalDisplayName: specViewOptions.language.FacetMaxBins,
|
||||
maxbinsSignalName: SignalNames.FacetBins
|
||||
};
|
||||
const discreteFacetVColumn: DiscreteColumn = {
|
||||
column: specColumns.facetV,
|
||||
defaultBins,
|
||||
maxbins,
|
||||
maxbinsSignalDisplayName: specViewOptions.language.FacetVMaxBins,
|
||||
maxbinsSignalName: SignalNames.FacetVBins
|
||||
};
|
||||
const { facetLayout, layoutPair } = getFacetLayout(insight.facetStyle, discreteFacetColumn, discreteFacetVColumn, specViewOptions.colors.axisText);
|
||||
props.layouts.unshift(layoutPair);
|
||||
props.facetLayout = facetLayout;
|
||||
}
|
||||
return props;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
import { AxisScales } from '../interfaces';
|
||||
import { Scatter, ScatterProps } from '../layouts/scatter';
|
||||
import { ScatterProps } from '../layouts/scatter';
|
||||
import { SignalNames } from '../constants';
|
||||
import { SpecBuilderProps } from '../specBuilder';
|
||||
import { SpecContext } from '../types';
|
||||
|
@ -25,7 +25,7 @@ export default function (specContext: SpecContext): SpecBuilderProps {
|
|||
axisScales,
|
||||
layouts: [
|
||||
{
|
||||
layoutClass: Scatter,
|
||||
layoutType: 'Scatter',
|
||||
props: scatterProps
|
||||
}
|
||||
],
|
||||
|
@ -42,6 +42,7 @@ export default function (specContext: SpecContext): SpecBuilderProps {
|
|||
},
|
||||
{
|
||||
role: 'z',
|
||||
axisSelection: specColumns.z && specColumns.z.quantitative ? 'range' : 'exact',
|
||||
allowNone: true
|
||||
},
|
||||
{
|
||||
|
|
|
@ -1,18 +1,19 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
import { AxisScales } from '../interfaces';
|
||||
import { Band, BandProps } from '../layouts/band';
|
||||
import { BandProps } from '../layouts/band';
|
||||
import { defaultBins, maxbins, minBarBandWidth } from '../defaults';
|
||||
import { SignalNames } from '../constants';
|
||||
import { SpecBuilderProps } from '../specBuilder';
|
||||
import { SpecContext } from '../types';
|
||||
import { Stack, StackProps } from '../layouts/stack';
|
||||
import { StackProps } from '../layouts/stack';
|
||||
|
||||
export default function (specContext: SpecContext): SpecBuilderProps {
|
||||
const { specColumns } = specContext;
|
||||
const { specColumns, specViewOptions } = specContext;
|
||||
const axisScales: AxisScales = {
|
||||
x: { title: specColumns.x && specColumns.x.name },
|
||||
y: { title: specColumns.y && specColumns.y.name }
|
||||
y: { title: specColumns.y && specColumns.y.name },
|
||||
z: { title: specViewOptions.language.count }
|
||||
};
|
||||
const hBandProps: BandProps = {
|
||||
excludeEncodingRuleMap: true,
|
||||
|
@ -48,15 +49,15 @@ export default function (specContext: SpecContext): SpecBuilderProps {
|
|||
customZScale: true,
|
||||
layouts: [
|
||||
{
|
||||
layoutClass: Band,
|
||||
layoutType: 'Band',
|
||||
props: vBandProps
|
||||
},
|
||||
{
|
||||
layoutClass: Band,
|
||||
layoutType: 'Band',
|
||||
props: hBandProps
|
||||
},
|
||||
{
|
||||
layoutClass: Stack,
|
||||
layoutType: 'Stack',
|
||||
props: stackProps
|
||||
}
|
||||
],
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
import { AggregateContainer, AggregateContainerProps } from '../layouts/aggregateContainer';
|
||||
import { AggregateContainerProps } from '../layouts/aggregateContainer';
|
||||
import { AxisScales } from '../interfaces';
|
||||
import { LayoutPair } from '../layouts/layout';
|
||||
import { SignalNames } from '../constants';
|
||||
import { SpecBuilderProps } from '../specBuilder';
|
||||
import { SpecContext } from '../types';
|
||||
import { Strip, StripProps } from '../layouts/strip';
|
||||
import { StripProps } from '../layouts/strip';
|
||||
|
||||
export default function (specContext: SpecContext): SpecBuilderProps {
|
||||
const { specColumns } = specContext;
|
||||
|
@ -37,12 +37,12 @@ export default function (specContext: SpecContext): SpecBuilderProps {
|
|||
showAxes: false
|
||||
};
|
||||
layouts.push({
|
||||
layoutClass: AggregateContainer,
|
||||
layoutType: 'AggregateContainer',
|
||||
props
|
||||
});
|
||||
}
|
||||
layouts.push({
|
||||
layoutClass: Strip,
|
||||
layoutType: 'Strip',
|
||||
props: stripProps
|
||||
});
|
||||
return {
|
||||
|
@ -58,6 +58,7 @@ export default function (specContext: SpecContext): SpecBuilderProps {
|
|||
},
|
||||
{
|
||||
role: 'z',
|
||||
axisSelection: specColumns.z && specColumns.z.quantitative ? 'range' : 'exact',
|
||||
allowNone: true
|
||||
},
|
||||
{
|
||||
|
|
|
@ -4,9 +4,9 @@ import { AxisScales } from '../interfaces';
|
|||
import { SignalNames } from '../constants';
|
||||
import { SpecBuilderProps } from '../specBuilder';
|
||||
import { SpecContext } from '../types';
|
||||
import { Treemap, TreemapProps } from '../layouts/treemap';
|
||||
import { TreemapProps } from '../layouts/treemap';
|
||||
import { LayoutPair } from '../layouts/layout';
|
||||
import { AggregateContainer, AggregateContainerProps } from '../layouts/aggregateContainer';
|
||||
import { AggregateContainerProps } from '../layouts/aggregateContainer';
|
||||
|
||||
export default function (specContext: SpecContext): SpecBuilderProps {
|
||||
const { specColumns, specViewOptions } = specContext;
|
||||
|
@ -37,12 +37,12 @@ export default function (specContext: SpecContext): SpecBuilderProps {
|
|||
showAxes: false
|
||||
};
|
||||
layouts.push({
|
||||
layoutClass: AggregateContainer,
|
||||
layoutType: 'AggregateContainer',
|
||||
props
|
||||
});
|
||||
}
|
||||
layouts.push({
|
||||
layoutClass: Treemap,
|
||||
layoutType: 'Treemap',
|
||||
props: treemapProps
|
||||
});
|
||||
return {
|
||||
|
@ -61,6 +61,7 @@ export default function (specContext: SpecContext): SpecBuilderProps {
|
|||
},
|
||||
{
|
||||
role: 'z',
|
||||
axisSelection: specColumns.z && specColumns.z.quantitative ? 'range' : 'exact',
|
||||
allowNone: true
|
||||
},
|
||||
{
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
import { Cross, CrossProps } from './layouts/cross';
|
||||
import { CrossProps } from './layouts/cross';
|
||||
import { DiscreteColumn } from './interfaces';
|
||||
import {
|
||||
facetPaddingBottom,
|
||||
|
@ -10,26 +10,32 @@ import {
|
|||
} from './defaults';
|
||||
import { FacetStyle } from './types';
|
||||
import { LayoutPair } from './layouts/layout';
|
||||
import { Scale, Signal } from 'vega-typings';
|
||||
import { SignalNames } from './constants';
|
||||
import { Wrap, WrapProps } from './layouts/wrap';
|
||||
import { WrapProps } from './layouts/wrap';
|
||||
|
||||
export interface FacetLayout {
|
||||
layoutPair: LayoutPair;
|
||||
plotPadding: { x: number, y: number };
|
||||
scales: Scale[];
|
||||
signals: Signal[];
|
||||
export interface PlotPadding {
|
||||
x: number;
|
||||
y: number;
|
||||
}
|
||||
|
||||
export function getFacetLayout(facetStyle: FacetStyle, facetColumn: DiscreteColumn, facetVColumn: DiscreteColumn, axisTextColor: string): FacetLayout {
|
||||
export interface FacetPadding {
|
||||
top: number;
|
||||
left: number;
|
||||
bottom: number;
|
||||
}
|
||||
|
||||
export interface FacetLayout {
|
||||
facetPadding: FacetPadding;
|
||||
plotPadding: PlotPadding;
|
||||
}
|
||||
|
||||
export function getFacetLayout(facetStyle: FacetStyle, facetColumn: DiscreteColumn, facetVColumn: DiscreteColumn, axisTextColor: string) {
|
||||
let layoutPair: LayoutPair;
|
||||
const scales: Scale[] = [];
|
||||
let signals: Signal[];
|
||||
const groupby = facetColumn;
|
||||
const plotPadding = {
|
||||
const plotPadding: PlotPadding = {
|
||||
x: 0,
|
||||
y: 0
|
||||
};
|
||||
let facetPadding: FacetPadding;
|
||||
switch (facetStyle) {
|
||||
case 'cross': {
|
||||
const props: CrossProps = {
|
||||
|
@ -39,23 +45,14 @@ export function getFacetLayout(facetStyle: FacetStyle, facetColumn: DiscreteColu
|
|||
groupbyY: facetVColumn
|
||||
};
|
||||
layoutPair = {
|
||||
layoutClass: Cross,
|
||||
layoutType: 'Cross',
|
||||
props
|
||||
};
|
||||
signals = [
|
||||
{
|
||||
name: SignalNames.FacetPaddingBottom,
|
||||
update: `${facetPaddingBottom}`
|
||||
},
|
||||
{
|
||||
name: SignalNames.FacetPaddingLeft,
|
||||
update: `${facetPaddingLeft}`
|
||||
},
|
||||
{
|
||||
name: SignalNames.FacetPaddingTop,
|
||||
update: '0'
|
||||
}
|
||||
];
|
||||
facetPadding = {
|
||||
bottom: facetPaddingBottom,
|
||||
left: facetPaddingLeft,
|
||||
top: 0
|
||||
};
|
||||
plotPadding.y = facetPaddingTop;
|
||||
plotPadding.x = facetPaddingRight;
|
||||
break;
|
||||
|
@ -68,26 +65,25 @@ export function getFacetLayout(facetStyle: FacetStyle, facetColumn: DiscreteColu
|
|||
groupby
|
||||
};
|
||||
layoutPair = {
|
||||
layoutClass: Wrap,
|
||||
layoutType: 'Wrap',
|
||||
props
|
||||
};
|
||||
signals = [
|
||||
{
|
||||
name: SignalNames.FacetPaddingBottom,
|
||||
update: `${facetPaddingBottom}`
|
||||
},
|
||||
{
|
||||
name: SignalNames.FacetPaddingLeft,
|
||||
update: `${facetPaddingLeft}`
|
||||
},
|
||||
{
|
||||
name: SignalNames.FacetPaddingTop,
|
||||
update: `${facetPaddingTop}`
|
||||
}
|
||||
];
|
||||
facetPadding =
|
||||
{
|
||||
bottom: facetPaddingBottom,
|
||||
left: facetPaddingLeft,
|
||||
top: facetPaddingTop
|
||||
};
|
||||
break;
|
||||
}
|
||||
}
|
||||
return { layoutPair, plotPadding, scales, signals };
|
||||
const facetLayout: FacetLayout = {
|
||||
facetPadding,
|
||||
plotPadding
|
||||
};
|
||||
return {
|
||||
layoutPair,
|
||||
facetLayout
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -81,10 +81,6 @@ export class AggregateSquare extends Layout {
|
|||
layoutHeight: null,
|
||||
layoutWidth: null
|
||||
},
|
||||
globalScales: {
|
||||
showAxes: false,
|
||||
scales: {}
|
||||
},
|
||||
encodingRuleMap: {
|
||||
y: [{
|
||||
test: testForCollapseSelection(),
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
import { Layout, LayoutBuildProps, LayoutProps } from './layout';
|
||||
import { AugmentBinnable, binnable, Binnable } from '../bin';
|
||||
import { binnable, Binnable } from '../bin';
|
||||
import { safeFieldName } from '../expr';
|
||||
import {
|
||||
DiscreteColumn,
|
||||
|
@ -18,7 +18,7 @@ import {
|
|||
} from '../scope';
|
||||
import { testForCollapseSelection } from '../selection';
|
||||
import { modifySignal } from '../signals';
|
||||
import { BandScale, LinearScale, Scale } from 'vega-typings';
|
||||
import { BandScale, Scale } from 'vega-typings';
|
||||
|
||||
export interface BandProps extends LayoutProps {
|
||||
excludeEncodingRuleMap?: boolean;
|
||||
|
@ -178,55 +178,6 @@ export class Band extends Layout {
|
|||
const binField = safeFieldName(bin.fields[0]);
|
||||
|
||||
const scales: Scale[] = [];
|
||||
|
||||
// function axisScaleName(scaleName: string) {
|
||||
// return `${scaleName}_axis`;
|
||||
// }
|
||||
|
||||
// if (bin.discreteColumn.column.quantitative) {
|
||||
// const { binSignal } = <AugmentBinnable>bin;
|
||||
|
||||
// let linearScale: LinearScale;
|
||||
// if (horizontal) {
|
||||
// linearScale = {
|
||||
// type: 'linear',
|
||||
// name: axisScaleName(names.yScale),
|
||||
// range: [
|
||||
// 0,
|
||||
// {
|
||||
// signal: parentScope.sizeSignals.layoutHeight
|
||||
// }
|
||||
// ],
|
||||
// domain: {
|
||||
// signal: `[${binSignal}.start, ${binSignal}.stop]`
|
||||
// },
|
||||
// bins: {
|
||||
// signal: binSignal
|
||||
// },
|
||||
// reverse: true,
|
||||
// zero: false
|
||||
// };
|
||||
// } else {
|
||||
// linearScale = {
|
||||
// type: 'linear',
|
||||
// name: axisScaleName(names.xScale),
|
||||
// range: [
|
||||
// 0,
|
||||
// {
|
||||
// signal: parentScope.sizeSignals.layoutWidth
|
||||
// }
|
||||
// ],
|
||||
// domain: {
|
||||
// signal: `[${binSignal}.start, ${binSignal}.stop]`
|
||||
// },
|
||||
// bins: {
|
||||
// signal: binSignal
|
||||
// },
|
||||
// zero: false
|
||||
// };
|
||||
// }
|
||||
// scales.push(linearScale);
|
||||
// }
|
||||
let bandScale: BandScale;
|
||||
if (horizontal) {
|
||||
bandScale = {
|
||||
|
|
|
@ -0,0 +1,40 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
import { AggregateContainer } from './aggregateContainer';
|
||||
import { AggregateSquare } from './aggregateSquare';
|
||||
import { Band } from './band';
|
||||
import { Cross } from './cross';
|
||||
import { Layout } from './layout';
|
||||
import { Scatter } from './scatter';
|
||||
import { Square } from './square';
|
||||
import { Stack } from './stack';
|
||||
import { Strip } from './strip';
|
||||
import { Treemap } from './treemap';
|
||||
import { Wrap } from './wrap';
|
||||
|
||||
export type LayoutType =
|
||||
'AggregateContainer' |
|
||||
'AggregateSquare' |
|
||||
'Band' |
|
||||
'Cross' |
|
||||
'Scatter' |
|
||||
'Square' |
|
||||
'Stack' |
|
||||
'Strip' |
|
||||
'Treemap' |
|
||||
'Wrap'
|
||||
;
|
||||
|
||||
export const layoutClasses: { [key in LayoutType]: typeof Layout } = {
|
||||
AggregateContainer,
|
||||
AggregateSquare,
|
||||
Band,
|
||||
Cross,
|
||||
Scatter,
|
||||
Square,
|
||||
Stack,
|
||||
Strip,
|
||||
Treemap,
|
||||
Wrap
|
||||
};
|
|
@ -1,5 +1,6 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
import { LayoutType } from './index';
|
||||
import { GlobalScope } from '../globalScope';
|
||||
import {
|
||||
AxisScales,
|
||||
|
@ -13,7 +14,7 @@ export interface LayoutProps {
|
|||
|
||||
export interface LayoutPair {
|
||||
props?: LayoutProps;
|
||||
layoutClass: typeof Layout;
|
||||
layoutType: LayoutType;
|
||||
}
|
||||
|
||||
export interface LayoutBuildProps {
|
||||
|
|
|
@ -1,74 +0,0 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
import { Layout, LayoutBuildProps, LayoutProps } from './layout';
|
||||
import { binnable, Binnable } from '../bin';
|
||||
import { DiscreteColumn, InnerScope, Orientation } from '../interfaces';
|
||||
import { addTransforms } from '../scope';
|
||||
import { Mark } from 'vega-typings';
|
||||
|
||||
export interface SliceProps extends LayoutProps {
|
||||
groupby: DiscreteColumn;
|
||||
orientation: Orientation;
|
||||
}
|
||||
|
||||
export class Slice extends Layout {
|
||||
private bin: Binnable;
|
||||
|
||||
constructor(public props: SliceProps & LayoutBuildProps) {
|
||||
super(props);
|
||||
this.prefix = `slice_${this.id}`;
|
||||
this.bin = binnable(this.prefix, props.globalScope.data.name, props.groupby);
|
||||
}
|
||||
|
||||
public getGrouping() {
|
||||
return this.bin.fields;
|
||||
}
|
||||
|
||||
public build(): InnerScope {
|
||||
const { bin, prefix, props } = this;
|
||||
const { globalScope, parentScope } = props;
|
||||
const facetDataName = `data_${prefix}_facet`;
|
||||
|
||||
if (bin.native === false) {
|
||||
globalScope.scope.signals.push(...bin.signals);
|
||||
addTransforms(globalScope.data, ...bin.transforms);
|
||||
globalScope.scope.data.push(bin.dataSequence);
|
||||
}
|
||||
// const mark: Mark = {
|
||||
// style: 'cell',
|
||||
// name: prefix,
|
||||
// type: 'group',
|
||||
// from: {
|
||||
// facet: {
|
||||
// name: facetDataName,
|
||||
// data: parentScope.dataName,
|
||||
// groupby: bin.fields
|
||||
// }
|
||||
// },
|
||||
// encode: {
|
||||
// },
|
||||
// marks: [
|
||||
// {
|
||||
// type: 'text',
|
||||
// encode: {
|
||||
// update: {
|
||||
// text: {
|
||||
// signal: `length(data(${JSON.stringify(facetDataName)}))`
|
||||
// },
|
||||
// fontSize: {
|
||||
// value: 20
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// ]
|
||||
// };
|
||||
// parentScope.scope.marks.push(mark);
|
||||
|
||||
return {
|
||||
offsets: null,
|
||||
sizeSignals: { layoutHeight: 'TODO', layoutWidth: 'TODO' }
|
||||
};
|
||||
|
||||
}
|
||||
}
|
|
@ -50,7 +50,7 @@ export class Square extends Layout {
|
|||
public build(): InnerScope {
|
||||
const { names, prefix, props } = this;
|
||||
const { fillDirection, globalScope, groupings, parentScope, collapseYHeight, sortBy, z } = props;
|
||||
addZScale(z, globalScope.zSize, globalScope, names.zScale);
|
||||
const zScale = addZScale(z, globalScope.zSize, globalScope.data.name, names.zScale);
|
||||
|
||||
addTransforms(globalScope.data, {
|
||||
type: 'stack',
|
||||
|
@ -112,6 +112,14 @@ export class Square extends Layout {
|
|||
const { tx, ty } = this.transformXY(gap, levelSize, squaresPerBand);
|
||||
|
||||
return {
|
||||
...z && {
|
||||
globalScales: {
|
||||
showAxes: true,
|
||||
scales: {
|
||||
z: [zScale]
|
||||
}
|
||||
}
|
||||
},
|
||||
offsets: {
|
||||
x: addOffsets(parentScope.offsets.x, tx.expr),
|
||||
y: addOffsets(parentScope.offsets.y, ty.expr),
|
||||
|
|
|
@ -13,7 +13,7 @@ import {
|
|||
} from '../scope';
|
||||
import { testForCollapseSelection } from '../selection';
|
||||
import { Column } from '@msrvida/chart-types';
|
||||
import { RectMark } from 'vega-typings';
|
||||
import { LinearScale, RectMark } from 'vega-typings';
|
||||
|
||||
export interface StackProps extends LayoutProps {
|
||||
sort: Column;
|
||||
|
@ -32,7 +32,9 @@ export class Stack extends Layout {
|
|||
sides: string,
|
||||
size: string,
|
||||
squared: string,
|
||||
squaredExtent: string
|
||||
maxCount: string,
|
||||
maxLevels: string,
|
||||
zScale: string,
|
||||
};
|
||||
|
||||
constructor(public props: StackProps & LayoutBuildProps) {
|
||||
|
@ -50,7 +52,9 @@ export class Stack extends Layout {
|
|||
sides: `${p}_sides`,
|
||||
size: `${p}_size`,
|
||||
squared: `${p}_squared`,
|
||||
squaredExtent: `${p}_squared_extent`
|
||||
maxCount: `${p}_maxCount`,
|
||||
maxLevels: `${p}_maxLevels`,
|
||||
zScale: `${p}_zScale`,
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -134,11 +138,6 @@ export class Stack extends Layout {
|
|||
{
|
||||
type: 'filter',
|
||||
expr: 'datum.row_number === 1'
|
||||
},
|
||||
{
|
||||
type: 'extent',
|
||||
field: 'squared',
|
||||
signal: names.squaredExtent
|
||||
}
|
||||
]
|
||||
}
|
||||
|
@ -151,7 +150,7 @@ export class Stack extends Layout {
|
|||
},
|
||||
{
|
||||
name: names.squared,
|
||||
update: `${names.squaredExtent}[0]`
|
||||
update: `data('${names.sequence}')[0].squared`
|
||||
},
|
||||
{
|
||||
name: names.sides,
|
||||
|
@ -160,6 +159,14 @@ export class Stack extends Layout {
|
|||
{
|
||||
name: names.cube,
|
||||
update: `(${names.size} - (${names.sides} - 1)) / ${names.sides}`
|
||||
},
|
||||
{
|
||||
name: names.maxLevels,
|
||||
update: `data('${names.sequence}')[0].maxlevels`
|
||||
},
|
||||
{
|
||||
name: names.maxCount,
|
||||
update: `${names.maxLevels} * ${names.squared}`
|
||||
}
|
||||
);
|
||||
|
||||
|
@ -199,6 +206,24 @@ export class Stack extends Layout {
|
|||
};
|
||||
addMarks(globalScope.markGroup, mark);
|
||||
|
||||
const zScale: LinearScale = {
|
||||
type: 'linear',
|
||||
name: names.zScale,
|
||||
domain: [
|
||||
0,
|
||||
{
|
||||
signal: names.maxCount
|
||||
}
|
||||
],
|
||||
range: [
|
||||
0,
|
||||
{
|
||||
signal: `${names.maxLevels} * (${names.cube} + 1) - 1`
|
||||
}
|
||||
],
|
||||
nice: false
|
||||
};
|
||||
|
||||
return {
|
||||
offsets,
|
||||
mark,
|
||||
|
@ -207,8 +232,10 @@ export class Stack extends Layout {
|
|||
layoutWidth: names.size
|
||||
},
|
||||
globalScales: {
|
||||
showAxes: false,
|
||||
scales: {}
|
||||
showAxes: true,
|
||||
scales: {
|
||||
z: [zScale]
|
||||
}
|
||||
},
|
||||
encodingRuleMap: {
|
||||
y: [{
|
||||
|
|
|
@ -55,7 +55,7 @@ export class Strip extends Layout {
|
|||
const { names, prefix, props } = this;
|
||||
const { addPercentageScale, globalScope, groupings, orientation, size, sort, sortOrder, parentScope, z } = props;
|
||||
|
||||
addZScale(z, globalScope.zSize, globalScope, names.zScale);
|
||||
const zScale = addZScale(z, globalScope.zSize, globalScope.data.name, names.zScale);
|
||||
|
||||
const horizontal = orientation === 'horizontal';
|
||||
|
||||
|
@ -182,7 +182,8 @@ export class Strip extends Layout {
|
|||
showAxes: true,
|
||||
scales: {
|
||||
x: horizontal ? [percentageScale] : undefined,
|
||||
y: horizontal ? undefined : [percentageScale]
|
||||
y: horizontal ? undefined : [percentageScale],
|
||||
z: zScale && [zScale]
|
||||
}
|
||||
},
|
||||
offsets,
|
||||
|
|
|
@ -77,7 +77,7 @@ export class Treemap extends Layout {
|
|||
const { names, props } = this;
|
||||
const { globalScope, parentScope, treeMapMethod, z } = props;
|
||||
|
||||
addZScale(z, globalScope.zSize, globalScope, names.zScale);
|
||||
const zScale = addZScale(z, globalScope.zSize, globalScope.data.name, names.zScale);
|
||||
|
||||
const offsets: LayoutOffsets = {
|
||||
x: addOffsets(parentScope.offsets.x, fn(names.fieldX0)),
|
||||
|
@ -101,6 +101,14 @@ export class Treemap extends Layout {
|
|||
});
|
||||
|
||||
return {
|
||||
...z && {
|
||||
globalScales: {
|
||||
showAxes: true,
|
||||
scales: {
|
||||
z: [zScale]
|
||||
}
|
||||
}
|
||||
},
|
||||
mark,
|
||||
offsets,
|
||||
sizeSignals: {
|
||||
|
|
|
@ -36,7 +36,7 @@ export function addScales(scope: Scope, ...scale: Scale[]) {
|
|||
if (!scope.scales) {
|
||||
scope.scales = [];
|
||||
}
|
||||
scope.scales.push(...scale);
|
||||
scope.scales.push(...scale.filter(Boolean));
|
||||
}
|
||||
|
||||
export function addSignals(scope: Scope, ...signal: Signal[]) {
|
||||
|
|
|
@ -9,29 +9,24 @@ import {
|
|||
axesTitlePaddingFacetX,
|
||||
axesTitlePaddingFacetY,
|
||||
axesTitlePaddingX,
|
||||
axesTitlePaddingY,
|
||||
defaultBins,
|
||||
maxbins
|
||||
axesTitlePaddingY
|
||||
} from './defaults';
|
||||
import { minFacetHeight, minFacetWidth } from './defaults';
|
||||
import { FacetLayout, getFacetLayout } from './facetLayout';
|
||||
import { FacetLayout } from './facetLayout';
|
||||
import { addFacetAxesGroupMarks } from './facetTitle';
|
||||
import { fill, opacity } from './fill';
|
||||
import { GlobalScope, GlobalSignals } from './globalScope';
|
||||
import {
|
||||
AxisScales,
|
||||
DiscreteColumn,
|
||||
EncodingRule,
|
||||
GlobalScales,
|
||||
Grouping,
|
||||
InnerScope,
|
||||
LayoutOffsets,
|
||||
SpecResult
|
||||
} from './interfaces';
|
||||
import { LayoutBuildProps, LayoutPair, LayoutProps } from './layouts/layout';
|
||||
import {
|
||||
addData,
|
||||
addScales,
|
||||
addSignals
|
||||
} from './scope';
|
||||
import { textSignals } from './signals';
|
||||
|
@ -41,6 +36,7 @@ import {
|
|||
LinearScale,
|
||||
Spec
|
||||
} from 'vega-typings';
|
||||
import { layoutClasses } from './layouts/index';
|
||||
|
||||
export interface SpecBuilderProps {
|
||||
axisScales?: AxisScales;
|
||||
|
@ -48,12 +44,13 @@ export interface SpecBuilderProps {
|
|||
errors?: string[];
|
||||
specCapabilities: SpecCapabilities;
|
||||
customZScale?: boolean;
|
||||
facetLayout?: FacetLayout;
|
||||
}
|
||||
|
||||
export class SpecBuilder {
|
||||
private globalSignals: GlobalSignals;
|
||||
|
||||
constructor(public props: SpecBuilderProps & { specContext: SpecContext }) {
|
||||
constructor(public props: SpecBuilderProps, public specContext: SpecContext) {
|
||||
this.globalSignals = {
|
||||
minCellWidth: {
|
||||
name: SignalNames.MinCellWidth,
|
||||
|
@ -70,7 +67,8 @@ export class SpecBuilder {
|
|||
}
|
||||
|
||||
public validate() {
|
||||
const { specCapabilities, specContext } = this.props;
|
||||
const { specContext } = this;
|
||||
const { specCapabilities } = this.props;
|
||||
const { roles } = specCapabilities;
|
||||
const required = roles.filter(r => {
|
||||
switch (typeof r.allowNone) {
|
||||
|
@ -109,186 +107,174 @@ export class SpecBuilder {
|
|||
}
|
||||
|
||||
public build(): SpecResult {
|
||||
const { specCapabilities } = this.props;
|
||||
const errors = this.validate();
|
||||
if (errors.length) {
|
||||
return {
|
||||
errors,
|
||||
specCapabilities,
|
||||
vegaSpec: null
|
||||
};
|
||||
} else {
|
||||
const { specContext } = this.props;
|
||||
const { insight, specColumns, specViewOptions } = specContext;
|
||||
const dataName = 'data_source';
|
||||
const { vegaSpec, groupMark } = this.initSpec(dataName);
|
||||
const { topColorField, colorDataName } = addColor({
|
||||
scope: vegaSpec,
|
||||
dataName,
|
||||
specContext,
|
||||
scaleName: ScaleNames.Color,
|
||||
legendDataName: 'data_legend',
|
||||
topLookupName: 'data_topcolorlookup',
|
||||
colorReverseSignalName: SignalNames.ColorReverse
|
||||
});
|
||||
const globalScope = new GlobalScope({
|
||||
dataName: colorDataName,
|
||||
markGroup: groupMark,
|
||||
scope: vegaSpec,
|
||||
signals: this.globalSignals
|
||||
});
|
||||
let facetLayout: FacetLayout;
|
||||
if (insight.columns.facet) {
|
||||
const discreteFacetColumn: DiscreteColumn = {
|
||||
column: specColumns.facet,
|
||||
defaultBins,
|
||||
maxbins,
|
||||
maxbinsSignalDisplayName: specViewOptions.language.FacetMaxBins,
|
||||
maxbinsSignalName: SignalNames.FacetBins
|
||||
};
|
||||
const discreteFacetVColumn: DiscreteColumn = {
|
||||
column: specColumns.facetV,
|
||||
defaultBins,
|
||||
maxbins,
|
||||
maxbinsSignalDisplayName: specViewOptions.language.FacetVMaxBins,
|
||||
maxbinsSignalName: SignalNames.FacetVBins
|
||||
};
|
||||
facetLayout = getFacetLayout(insight.facetStyle, discreteFacetColumn, discreteFacetVColumn, specViewOptions.colors.axisText);
|
||||
addSignals(vegaSpec, ...facetLayout.signals);
|
||||
addScales(vegaSpec, ...facetLayout.scales);
|
||||
this.props.layouts = [facetLayout.layoutPair, ...this.props.layouts];
|
||||
this.globalSignals.plotOffsetTop.update = `${facetLayout.plotPadding.y}`;
|
||||
this.globalSignals.plotOffsetRight.update = `${facetLayout.plotPadding.x}`;
|
||||
const { specContext } = this;
|
||||
|
||||
const { facetLayout, specCapabilities } = this.props;
|
||||
const { insight, specColumns, specViewOptions } = specContext;
|
||||
const dataName = 'data_source';
|
||||
const { vegaSpec, groupMark } = this.initSpec(dataName);
|
||||
const { topColorField, colorDataName } = addColor({
|
||||
scope: vegaSpec,
|
||||
dataName,
|
||||
specContext,
|
||||
scaleName: ScaleNames.Color,
|
||||
legendDataName: 'data_legend',
|
||||
topLookupName: 'data_topcolorlookup',
|
||||
colorReverseSignalName: SignalNames.ColorReverse
|
||||
});
|
||||
const globalScope = new GlobalScope({
|
||||
dataName: colorDataName,
|
||||
markGroup: groupMark,
|
||||
scope: vegaSpec,
|
||||
signals: this.globalSignals
|
||||
});
|
||||
if (facetLayout) {
|
||||
addSignals(vegaSpec,
|
||||
{
|
||||
name: SignalNames.FacetPaddingBottom,
|
||||
update: `${facetLayout.facetPadding.bottom}`
|
||||
},
|
||||
{
|
||||
name: SignalNames.FacetPaddingLeft,
|
||||
update: `${facetLayout.facetPadding.left}`
|
||||
},
|
||||
{
|
||||
name: SignalNames.FacetPaddingTop,
|
||||
update: `${facetLayout.facetPadding.top}`
|
||||
}
|
||||
);
|
||||
this.globalSignals.plotOffsetTop.update = `${facetLayout.plotPadding.y}`;
|
||||
this.globalSignals.plotOffsetRight.update = `${facetLayout.plotPadding.x}`;
|
||||
}
|
||||
const {
|
||||
firstScope,
|
||||
finalScope,
|
||||
specResult,
|
||||
allGlobalScales,
|
||||
allEncodingRules
|
||||
} = this.iterateLayouts(globalScope, (i, innerScope) => {
|
||||
if (facetLayout && i === 0) {
|
||||
globalScope.zSize = innerScope.offsets.h;
|
||||
}
|
||||
const {
|
||||
firstScope,
|
||||
finalScope,
|
||||
specResult,
|
||||
});
|
||||
|
||||
if (specResult) {
|
||||
return specResult;
|
||||
}
|
||||
if (allGlobalScales.length > 0) {
|
||||
const plotHeightOut = this.globalSignals.plotHeightOut.name;
|
||||
const plotWidthOut = this.globalSignals.plotWidthOut.name;
|
||||
|
||||
const colTitleScale: LinearScale = {
|
||||
type: 'linear',
|
||||
name: 'scale_facet_col_title',
|
||||
domain: [0, 1],
|
||||
range: [0, { signal: plotWidthOut }]
|
||||
};
|
||||
|
||||
const rowTitleScale: LinearScale = {
|
||||
type: 'linear',
|
||||
name: 'scale_facet_row_title',
|
||||
domain: [0, 1],
|
||||
range: [{ signal: plotHeightOut }, 0]
|
||||
};
|
||||
|
||||
let axesScopes: AxesScopeMap = facetLayout ?
|
||||
addFacetAxesGroupMarks({
|
||||
globalScope: globalScope.scope,
|
||||
plotScope: groupMark,
|
||||
facetScope: firstScope,
|
||||
colTitleScale,
|
||||
rowTitleScale,
|
||||
colSeqName: 'data_FacetCellColTitles',
|
||||
rowSeqName: 'data_FacetCellRowTitles'
|
||||
})
|
||||
:
|
||||
{
|
||||
main: [{
|
||||
scope: groupMark,
|
||||
lines: true,
|
||||
labels: true,
|
||||
title: true
|
||||
}]
|
||||
};
|
||||
addGlobalAxes({
|
||||
globalScope,
|
||||
allGlobalScales,
|
||||
allEncodingRules
|
||||
} = this.iterateLayouts(globalScope, (i, innerScope) => {
|
||||
if (facetLayout && i === 0) {
|
||||
globalScope.zSize = innerScope.offsets.h;
|
||||
axisScales: this.props.axisScales,
|
||||
plotOffsetSignals: { x: this.globalSignals.plotOffsetLeft, y: this.globalSignals.plotOffsetBottom },
|
||||
axesOffsets: { x: axesOffsetX, y: axesOffsetY },
|
||||
axesTitlePadding: facetLayout ? { x: axesTitlePaddingFacetX, y: axesTitlePaddingFacetY } : { x: axesTitlePaddingX, y: axesTitlePaddingY },
|
||||
labelBaseline: { x: 'top', y: 'middle' },
|
||||
specColumns,
|
||||
specViewOptions,
|
||||
axesScopes,
|
||||
view: insight.view
|
||||
});
|
||||
}
|
||||
|
||||
//add mark to the final scope
|
||||
if (finalScope.mark) {
|
||||
const { update } = finalScope.mark.encode;
|
||||
|
||||
const outputDataName = 'output';
|
||||
finalScope.mark.from.data = outputDataName;
|
||||
addData(globalScope.markGroup,
|
||||
{
|
||||
name: outputDataName,
|
||||
source: globalScope.markDataName,
|
||||
transform: [
|
||||
{
|
||||
type: 'formula',
|
||||
expr: finalScope.offsets.x,
|
||||
as: FieldNames.OffsetX
|
||||
},
|
||||
{
|
||||
type: 'formula',
|
||||
expr: finalScope.offsets.y,
|
||||
as: FieldNames.OffsetY
|
||||
}
|
||||
]
|
||||
}
|
||||
);
|
||||
update.x = {
|
||||
field: FieldNames.OffsetX
|
||||
};
|
||||
update.y = {
|
||||
field: FieldNames.OffsetY
|
||||
};
|
||||
|
||||
allEncodingRules.forEach(map => {
|
||||
for (let key in map) {
|
||||
if (update[key]) {
|
||||
let arrIn = map[key];
|
||||
if (!Array.isArray(update[key])) {
|
||||
let value = update[key];
|
||||
let arrOut = [];
|
||||
update[key] = arrOut;
|
||||
arrIn.forEach(rule => arrOut.push(rule));
|
||||
arrOut.push(value);
|
||||
} else {
|
||||
let arrOut = update[key] as {}[];
|
||||
arrIn.forEach(rule => arrOut.unshift(rule));
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (specResult) {
|
||||
return specResult;
|
||||
}
|
||||
if (allGlobalScales.length > 0) {
|
||||
const plotHeightOut = this.globalSignals.plotHeightOut.name;
|
||||
const plotWidthOut = this.globalSignals.plotWidthOut.name;
|
||||
|
||||
const colTitleScale: LinearScale = {
|
||||
type: 'linear',
|
||||
name: 'scale_facet_col_title',
|
||||
domain: [0, 1],
|
||||
range: [0, { signal: plotWidthOut }]
|
||||
};
|
||||
|
||||
const rowTitleScale: LinearScale = {
|
||||
type: 'linear',
|
||||
name: 'scale_facet_row_title',
|
||||
domain: [0, 1],
|
||||
range: [{ signal: plotHeightOut }, 0]
|
||||
};
|
||||
|
||||
let axesScopes: AxesScopeMap = facetLayout ?
|
||||
addFacetAxesGroupMarks({
|
||||
globalScope: globalScope.scope,
|
||||
plotScope: groupMark,
|
||||
facetScope: firstScope,
|
||||
colTitleScale,
|
||||
rowTitleScale,
|
||||
colSeqName: 'data_FacetCellColTitles',
|
||||
rowSeqName: 'data_FacetCellRowTitles'
|
||||
})
|
||||
:
|
||||
{
|
||||
main: [{
|
||||
scope: groupMark,
|
||||
lines: true,
|
||||
labels: true,
|
||||
title: true
|
||||
}]
|
||||
};
|
||||
addGlobalAxes({
|
||||
globalScope,
|
||||
allGlobalScales,
|
||||
axisScales: this.props.axisScales,
|
||||
plotOffsetSignals: { x: this.globalSignals.plotOffsetLeft, y: this.globalSignals.plotOffsetBottom },
|
||||
axesOffsets: { x: axesOffsetX, y: axesOffsetY },
|
||||
axesTitlePadding: facetLayout ? { x: axesTitlePaddingFacetX, y: axesTitlePaddingFacetY } : { x: axesTitlePaddingX, y: axesTitlePaddingY },
|
||||
labelBaseline: { x: 'top', y: 'middle' },
|
||||
specColumns,
|
||||
specViewOptions,
|
||||
axesScopes
|
||||
});
|
||||
}
|
||||
|
||||
//add mark to the final scope
|
||||
if (finalScope.mark) {
|
||||
const { update } = finalScope.mark.encode;
|
||||
|
||||
const outputDataName = 'output';
|
||||
finalScope.mark.from.data = outputDataName;
|
||||
addData(globalScope.markGroup,
|
||||
{
|
||||
name: outputDataName,
|
||||
source: globalScope.markDataName,
|
||||
transform: [
|
||||
{
|
||||
type: 'formula',
|
||||
expr: finalScope.offsets.x,
|
||||
as: FieldNames.OffsetX
|
||||
},
|
||||
{
|
||||
type: 'formula',
|
||||
expr: finalScope.offsets.y,
|
||||
as: FieldNames.OffsetY
|
||||
}
|
||||
]
|
||||
}
|
||||
);
|
||||
update.x = {
|
||||
field: FieldNames.OffsetX
|
||||
};
|
||||
update.y = {
|
||||
field: FieldNames.OffsetY
|
||||
};
|
||||
|
||||
allEncodingRules.forEach(map => {
|
||||
for (let key in map) {
|
||||
if (update[key]) {
|
||||
let arrIn = map[key];
|
||||
if (!Array.isArray(update[key])) {
|
||||
let value = update[key];
|
||||
let arrOut = [];
|
||||
update[key] = arrOut;
|
||||
arrIn.forEach(rule => arrOut.push(rule));
|
||||
arrOut.push(value);
|
||||
} else {
|
||||
let arrOut = update[key] as {}[];
|
||||
arrIn.forEach(rule => arrOut.unshift(rule));
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
update.fill = fill(specContext, topColorField, ScaleNames.Color);
|
||||
update.opacity = opacity(specContext);
|
||||
}
|
||||
return {
|
||||
specCapabilities,
|
||||
vegaSpec
|
||||
};
|
||||
update.fill = fill(specContext, topColorField, ScaleNames.Color);
|
||||
update.opacity = opacity(specContext);
|
||||
}
|
||||
return {
|
||||
specCapabilities,
|
||||
vegaSpec
|
||||
};
|
||||
}
|
||||
|
||||
private initSpec(dataName: string) {
|
||||
const { globalSignals } = this;
|
||||
const { minCellWidth, minCellHeight, plotOffsetLeft, plotOffsetBottom, plotOffsetTop, plotOffsetRight, plotHeightOut, plotWidthOut } = globalSignals;
|
||||
const { specContext } = this.props;
|
||||
const { specContext } = this;
|
||||
const { insight } = specContext;
|
||||
const groupMark: GroupMark = {
|
||||
type: 'group',
|
||||
|
@ -410,11 +396,12 @@ export class SpecBuilder {
|
|||
}
|
||||
|
||||
private createLayout(layoutPair: LayoutPair, buildProps: LayoutBuildProps) {
|
||||
const { layoutClass, props } = layoutPair;
|
||||
const { layoutType, props } = layoutPair;
|
||||
const layoutBuildProps: LayoutProps & LayoutBuildProps = {
|
||||
...props,
|
||||
...buildProps
|
||||
};
|
||||
const layoutClass = layoutClasses[layoutType];
|
||||
const layout = new layoutClass(layoutBuildProps);
|
||||
layout.id = buildProps.id;
|
||||
return layout;
|
||||
|
|
|
@ -353,6 +353,11 @@ export interface SpecColumns {
|
|||
facetV?: Column;
|
||||
}
|
||||
|
||||
export interface ZAxisOptions {
|
||||
showZAxis?: boolean;
|
||||
zIndex?: number;
|
||||
}
|
||||
|
||||
export interface SpecViewOptions {
|
||||
|
||||
/**
|
||||
|
@ -370,7 +375,9 @@ export interface SpecViewOptions {
|
|||
*/
|
||||
maxLegends: number;
|
||||
|
||||
tickSize: number
|
||||
tickSize: number;
|
||||
|
||||
zAxisOptions?: ZAxisOptions;
|
||||
}
|
||||
|
||||
export interface SpecContext {
|
||||
|
|
|
@ -1,19 +1,19 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
import { SignalNames } from './constants';
|
||||
import { GlobalScope } from './globalScope';
|
||||
import { linearScale, pointScale } from './scales';
|
||||
import { addScales } from './scope';
|
||||
import { Column } from '@msrvida/chart-types';
|
||||
import { RangeScheme } from 'vega-typings';
|
||||
|
||||
export function addZScale(z: Column, zSize: string, globalScope: GlobalScope, zScaleName: string) {
|
||||
export function addZScale(z: Column, zSize: string, dataName: string, zScaleName: string) {
|
||||
if (z) {
|
||||
const zRange: RangeScheme = [0, { signal: `(${zSize}) * ${SignalNames.ZProportion}` }];
|
||||
addScales(globalScope.scope, z.quantitative
|
||||
const scale = z.quantitative
|
||||
?
|
||||
linearScale(zScaleName, globalScope.data.name, z.name, zRange, false, true)
|
||||
linearScale(zScaleName, dataName, z.name, zRange, false, true)
|
||||
:
|
||||
pointScale(zScaleName, globalScope.data.name, zRange, z.name, false));
|
||||
pointScale(zScaleName, dataName, zRange, z.name, false)
|
||||
;
|
||||
return scale;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
import { build, getColumnsFromData, getSpecColumns, Insight, SpecContext, SpecResult, SpecViewOptions } from '../dist/es6';
|
||||
import { build, getColumnsFromData, getSpecColumns, Insight, SpecContext, SpecViewOptions } from '../src/index';
|
||||
import { Column } from '@msrvida/chart-types';
|
||||
import * as Vega from 'vega-typings';
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@msrvida/sanddance",
|
||||
"version": "3.1.1",
|
||||
"version": "3.2.0",
|
||||
"description": "SandDance visualization canvas component.",
|
||||
"main": "dist/es6/index.js",
|
||||
"types": "dist/es6/index.d.ts",
|
||||
|
|
|
@ -30,6 +30,7 @@ export function recolorAxes(stage: VegaDeckGl.types.Stage, oldColors: SpecColorS
|
|||
let axes: {
|
||||
x: VegaDeckGl.types.Axis[];
|
||||
y: VegaDeckGl.types.Axis[];
|
||||
z: VegaDeckGl.types.Axis[];
|
||||
};
|
||||
let textData: VegaDeckGl.types.VegaTextLayerDatum[];
|
||||
|
||||
|
@ -38,7 +39,8 @@ export function recolorAxes(stage: VegaDeckGl.types.Stage, oldColors: SpecColorS
|
|||
const textColor = VegaDeckGl.util.colorFromString(newColors.axisText || oldColors.axisText);
|
||||
axes = {
|
||||
x: cloneAxis(stage.axes.x, lineColor, textColor),
|
||||
y: cloneAxis(stage.axes.y, lineColor, textColor)
|
||||
y: cloneAxis(stage.axes.y, lineColor, textColor),
|
||||
z: cloneAxis(stage.axes.z, lineColor, textColor)
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -16,11 +16,11 @@ export class CharacterSet {
|
|||
if (!this.chars) {
|
||||
const map: { [char: string]: true } = {};
|
||||
const addText = (text: string) => {
|
||||
Array.from(text).forEach(char => { map[char] = true });
|
||||
Array.from(text).forEach(char => { map[char] = true; });
|
||||
};
|
||||
stage.textData.forEach(t => addText(t.text));
|
||||
const { x, y } = stage.axes;
|
||||
[x, y].forEach(axes => {
|
||||
const { x, y, z } = stage.axes;
|
||||
[x, y, z].forEach(axes => {
|
||||
axes.forEach(axis => {
|
||||
if (axis.tickText) axis.tickText.forEach(t => addText(t.text));
|
||||
if (axis.title) addText(axis.title.text);
|
||||
|
|
|
@ -55,6 +55,8 @@ import { CharacterSet } from './characterSet';
|
|||
|
||||
const { defaultView } = VegaDeckGl.defaults;
|
||||
|
||||
const zAxisZindex = 1010;
|
||||
|
||||
let didRegisterColorSchemes = false;
|
||||
|
||||
/**
|
||||
|
@ -280,7 +282,17 @@ export class Viewer {
|
|||
|
||||
private async renderNewLayout(signalValues: SignalValues, presenterConfig?: VegaDeckGl.types.PresenterConfig, view?: View) {
|
||||
const currData = this._dataScope.currentData();
|
||||
const context: SpecContext = { specColumns: this.getSpecColumnsWithFilteredStats(), insight: this.insight, specViewOptions: this.options };
|
||||
const context: SpecContext = {
|
||||
specColumns: this.getSpecColumnsWithFilteredStats(),
|
||||
insight: this.insight,
|
||||
specViewOptions: {
|
||||
...this.options,
|
||||
zAxisOptions: {
|
||||
showZAxis: true,
|
||||
zIndex: zAxisZindex
|
||||
}
|
||||
}
|
||||
};
|
||||
const specResult = build(context, currData);
|
||||
if (!specResult.errors) {
|
||||
const uiValues = extractSignalValuesFromView(this.vegaViewGl, this.vegaSpec);
|
||||
|
@ -312,7 +324,7 @@ export class Viewer {
|
|||
|
||||
this.vegaSpec.signals.forEach(s => {
|
||||
this.vegaViewGl.addSignalListener(s.name, handler);
|
||||
})
|
||||
});
|
||||
|
||||
//capture new color color contexts via signals
|
||||
this.configForSignalCapture(config.presenterConfig);
|
||||
|
@ -622,6 +634,7 @@ export class Viewer {
|
|||
private createConfig(c?: VegaDeckGl.types.PresenterConfig): VegaDeckGl.types.ViewGlConfig {
|
||||
const { getTextColor, getTextHighlightColor, getTextHighlightAlphaCutoff, onTextClick } = this.options;
|
||||
const defaultPresenterConfig: VegaDeckGl.types.PresenterConfig = {
|
||||
zAxisZindex,
|
||||
getCharacterSet: stage => this._characterSet.getCharacterSet(stage),
|
||||
getTextColor,
|
||||
getTextHighlightColor,
|
||||
|
@ -679,7 +692,7 @@ export class Viewer {
|
|||
if (this.options.onBeforeCreateLayers) {
|
||||
defaultPresenterConfig.preLayer = stage => {
|
||||
this.options.onBeforeCreateLayers(stage, this.specCapabilities);
|
||||
}
|
||||
};
|
||||
}
|
||||
const config: VegaDeckGl.types.ViewGlConfig = {
|
||||
presenter: this.presenter,
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@msrvida/vega-deck.gl",
|
||||
"version": "3.2.1",
|
||||
"version": "3.3.0",
|
||||
"description": "Deck.gl renderer for Vega",
|
||||
"main": "dist/es6/index.js",
|
||||
"files": [
|
||||
|
|
|
@ -50,7 +50,8 @@ export function createStage(view: View) {
|
|||
polygonData: [],
|
||||
axes: {
|
||||
x: [],
|
||||
y: []
|
||||
y: [],
|
||||
z: [],
|
||||
},
|
||||
gridLines: [],
|
||||
textData: [],
|
||||
|
|
|
@ -30,11 +30,14 @@ export interface TickText extends VegaTextLayerDatum {
|
|||
value: number | string;
|
||||
}
|
||||
|
||||
export type AxisRole = 'x' | 'y' | 'z';
|
||||
|
||||
export interface Axis {
|
||||
domain: StyledLine;
|
||||
ticks: StyledLine[];
|
||||
tickText: TickText[];
|
||||
title?: VegaTextLayerDatum;
|
||||
role?: AxisRole;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -69,7 +72,7 @@ export interface Path {
|
|||
}
|
||||
|
||||
export interface Polygon {
|
||||
positions: Position[];
|
||||
positions: Position[];
|
||||
strokeColor: RGBAColor;
|
||||
fillColor: RGBAColor;
|
||||
strokeWidth: number;
|
||||
|
@ -102,6 +105,7 @@ export interface Stage {
|
|||
axes?: {
|
||||
x?: Axis[];
|
||||
y?: Axis[];
|
||||
z?: Axis[];
|
||||
};
|
||||
textData?: VegaTextLayerDatum[];
|
||||
view?: View;
|
||||
|
@ -171,6 +175,7 @@ export interface PresenterConfig {
|
|||
onSceneRectAssignCubeOrdinal?: (d: object) => number | undefined;
|
||||
onTargetViewState?: (height: number, width: number) => { height: number, width: number, newViewStateTarget?: boolean };
|
||||
preserveDrawingBuffer?: boolean;
|
||||
zAxisZindex?: number;
|
||||
}
|
||||
|
||||
export interface PresenterStyle {
|
||||
|
|
|
@ -32,10 +32,10 @@ export function getLayers(
|
|||
guideLines: StyledLine[]
|
||||
): Layer<any>[] {
|
||||
const cubeLayer = newCubeLayer(presenter, config, stage.cubeData, presenter.style.highlightColor, lightSettings, lightingMix, interpolator);
|
||||
const { x, y } = stage.axes;
|
||||
const { x, y, z } = stage.axes;
|
||||
const lines = concat(stage.gridLines, guideLines);
|
||||
const texts = [...stage.textData];
|
||||
[x, y].forEach(axes => {
|
||||
[x, y, z].forEach(axes => {
|
||||
axes.forEach(axis => {
|
||||
if (axis.domain) lines.push(axis.domain);
|
||||
if (axis.ticks) lines.push.apply(lines, axis.ticks);
|
||||
|
|
|
@ -2,13 +2,14 @@
|
|||
// Licensed under the MIT license.
|
||||
import { Axis, Stage } from '../interfaces';
|
||||
import { RGBAColor } from '@deck.gl/core/utils/color';
|
||||
import { Scene, SceneGroup } from 'vega-typings';
|
||||
import { Mark, Orient, Scene, SceneGroup } from 'vega-typings';
|
||||
|
||||
export enum GroupType {
|
||||
none = 0,
|
||||
legend = 1,
|
||||
xAxis = 2,
|
||||
yAxis = 3
|
||||
yAxis = 3,
|
||||
zAxis = 4,
|
||||
}
|
||||
|
||||
export interface MarkStagerOptions {
|
||||
|
@ -16,12 +17,14 @@ export interface MarkStagerOptions {
|
|||
currAxis: Axis;
|
||||
defaultCubeColor: RGBAColor;
|
||||
assignCubeOrdinal: (d: object) => number | undefined;
|
||||
zAxisZindex: number;
|
||||
}
|
||||
|
||||
//TODO - use vega-typings below
|
||||
export type SceneGroup2 = SceneGroup & {
|
||||
export type AxisSceneGroup = SceneGroup & {
|
||||
datum?: any;
|
||||
orient?: 'bottom' | 'left' | 'right' | 'top';
|
||||
orient?: Orient;
|
||||
mark: Mark
|
||||
};
|
||||
export interface LabelDatum {
|
||||
value: any;
|
||||
|
|
|
@ -6,6 +6,7 @@ import { GroupType, MarkStager, MarkStagerOptions } from './interfaces';
|
|||
import { lineZ } from '../defaults';
|
||||
import { Scene, SceneLine } from 'vega-typings';
|
||||
import { Stage, StyledLine } from '../interfaces';
|
||||
import { zSwap } from '../zaxis';
|
||||
|
||||
const markStager: MarkStager = (options: MarkStagerOptions, stage: Stage, scene: Scene, x: number, y: number, groupType: GroupType) => {
|
||||
|
||||
|
@ -19,8 +20,16 @@ const markStager: MarkStager = (options: MarkStagerOptions, stage: Stage, scene:
|
|||
const lineItem = styledLine(x1 + x, y1 + y, x2 + x, y2 + y, item.stroke, item.strokeWidth);
|
||||
|
||||
if (item.mark.role === 'axis-tick') {
|
||||
if (options.currAxis.role === 'z') {
|
||||
zSwap(lineItem.sourcePosition);
|
||||
zSwap(lineItem.targetPosition);
|
||||
}
|
||||
options.currAxis.ticks.push(lineItem);
|
||||
} else if (item.mark.role === 'axis-domain') {
|
||||
if (options.currAxis.role === 'z') {
|
||||
zSwap(lineItem.sourcePosition);
|
||||
zSwap(lineItem.targetPosition);
|
||||
}
|
||||
options.currAxis.domain = lineItem;
|
||||
} else {
|
||||
stage.gridLines.push(lineItem);
|
||||
|
|
|
@ -16,6 +16,7 @@ import {
|
|||
SceneTextBaseline
|
||||
} from 'vega-typings';
|
||||
import { Stage, TickText, VegaTextLayerDatum } from '../interfaces';
|
||||
import { zSwap } from '../zaxis';
|
||||
|
||||
interface SceneText2 extends SceneText {
|
||||
metaData?: any;
|
||||
|
@ -49,8 +50,14 @@ const markStager: MarkStager = (options: MarkStagerOptions, stage: Stage, scene:
|
|||
if (item.mark.role === 'axis-label') {
|
||||
const tickText = textItem as TickText;
|
||||
tickText.value = (item.datum as LabelDatum).value;
|
||||
if (options.currAxis.role === 'z') {
|
||||
zSwap(tickText.position);
|
||||
}
|
||||
options.currAxis.tickText.push(tickText);
|
||||
} else if (item.mark.role === 'axis-title') {
|
||||
if (options.currAxis.role === 'z') {
|
||||
zSwap(textItem.position);
|
||||
}
|
||||
options.currAxis.title = textItem;
|
||||
} else {
|
||||
stage.textData.push(textItem);
|
||||
|
|
|
@ -160,7 +160,8 @@ export class Presenter {
|
|||
maxOrdinal: 0,
|
||||
currAxis: null,
|
||||
defaultCubeColor: this.style.defaultCubeColor,
|
||||
assignCubeOrdinal: (config && config.onSceneRectAssignCubeOrdinal) || (() => options.maxOrdinal++)
|
||||
assignCubeOrdinal: config?.onSceneRectAssignCubeOrdinal || (() => options.maxOrdinal++),
|
||||
zAxisZindex: config?.zAxisZindex
|
||||
};
|
||||
//determine if this is a vega scene
|
||||
if (scene.marktype) {
|
||||
|
@ -181,7 +182,7 @@ export class Presenter {
|
|||
|
||||
let glOptions: WebGLContextAttributes;
|
||||
if (config && config.preserveDrawingBuffer) {
|
||||
glOptions = { preserveDrawingBuffer: true }
|
||||
glOptions = { preserveDrawingBuffer: true };
|
||||
}
|
||||
|
||||
const deckProps: DeckGLInternalProps = {
|
||||
|
|
|
@ -9,6 +9,7 @@ import area from './marks/area';
|
|||
|
||||
import {
|
||||
Axis,
|
||||
AxisRole,
|
||||
FacetRect,
|
||||
Stage,
|
||||
StyledLine
|
||||
|
@ -20,30 +21,33 @@ import {
|
|||
GroupType,
|
||||
MarkStager,
|
||||
MarkStagerOptions,
|
||||
SceneGroup2
|
||||
AxisSceneGroup
|
||||
} from './marks/interfaces';
|
||||
import { Scene } from 'vega-typings';
|
||||
import { Orient, Scene, SceneGroup } from 'vega-typings';
|
||||
|
||||
interface VegaAxisDatum {
|
||||
domain: boolean;
|
||||
grid: boolean;
|
||||
labels: boolean;
|
||||
orient: 'bottom' | 'left' | 'right' | 'top';
|
||||
orient: Orient;
|
||||
ticks: boolean;
|
||||
title: boolean;
|
||||
}
|
||||
|
||||
function getOrientItem(group: SceneGroup2): { orient?: 'bottom' | 'left' | 'right' | 'top'; } {
|
||||
function getOrientItem(group: AxisSceneGroup): { orient?: Orient; } {
|
||||
if (group.orient) {
|
||||
return group;
|
||||
}
|
||||
return group.datum as VegaAxisDatum;
|
||||
}
|
||||
|
||||
function convertGroupRole(group: SceneGroup2): GroupType {
|
||||
function convertGroupRole(group: SceneGroup, options: MarkStagerOptions): GroupType {
|
||||
if (group.mark.role === 'legend') return GroupType.legend;
|
||||
if (group.mark.role === 'axis') {
|
||||
const orientItem = getOrientItem(group);
|
||||
if (((group as AxisSceneGroup).mark).zindex === options.zAxisZindex && options.zAxisZindex !== undefined) {
|
||||
return GroupType.zAxis;
|
||||
}
|
||||
const orientItem = getOrientItem(group as AxisSceneGroup);
|
||||
if (orientItem) {
|
||||
switch (orientItem.orient) {
|
||||
case 'bottom':
|
||||
|
@ -59,7 +63,7 @@ function convertGroupRole(group: SceneGroup2): GroupType {
|
|||
|
||||
const group: MarkStager = (options: MarkStagerOptions, stage: Stage, scene: Scene, x: number, y: number, groupType: GroupType) => {
|
||||
|
||||
base.vega.sceneVisit(scene, function (g: SceneGroup2) {
|
||||
base.vega.sceneVisit(scene, function (g: SceneGroup) {
|
||||
|
||||
const gx = g.x || 0, gy = g.y || 0;
|
||||
if (g.context && g.context.background && !stage.backgroundColor) {
|
||||
|
@ -73,7 +77,7 @@ const group: MarkStager = (options: MarkStagerOptions, stage: Stage, scene: Scen
|
|||
stage.facets.push(facetRect);
|
||||
}
|
||||
|
||||
groupType = convertGroupRole(g) || groupType;
|
||||
groupType = convertGroupRole(g, options) || groupType;
|
||||
setCurrentAxis(options, stage, groupType);
|
||||
|
||||
// draw group contents
|
||||
|
@ -86,12 +90,19 @@ const group: MarkStager = (options: MarkStagerOptions, stage: Stage, scene: Scen
|
|||
|
||||
function setCurrentAxis(options: MarkStagerOptions, stage: Stage, groupType: GroupType) {
|
||||
let axes: Axis[];
|
||||
let role: AxisRole;
|
||||
switch (groupType) {
|
||||
case GroupType.xAxis:
|
||||
axes = stage.axes.x;
|
||||
role = 'x';
|
||||
break;
|
||||
case GroupType.yAxis:
|
||||
axes = stage.axes.y;
|
||||
role = 'y';
|
||||
break;
|
||||
case GroupType.zAxis:
|
||||
axes = stage.axes.z;
|
||||
role = 'z';
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
|
@ -99,7 +110,8 @@ function setCurrentAxis(options: MarkStagerOptions, stage: Stage, groupType: Gro
|
|||
options.currAxis = {
|
||||
domain: null,
|
||||
tickText: [],
|
||||
ticks: []
|
||||
ticks: [],
|
||||
role
|
||||
};
|
||||
axes.push(options.currAxis);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
import { Position } from '@deck.gl/core';
|
||||
import { lineZ } from './defaults';
|
||||
import { Vec3 } from './interfaces';
|
||||
|
||||
export function zSwap(v3: Vec3 | Position) {
|
||||
let temp = -v3[1]; //negeative y to positive z
|
||||
if(v3[0]===lineZ) {
|
||||
v3[0] = 0;
|
||||
}
|
||||
v3[1] = v3[2];
|
||||
v3[2] = temp;
|
||||
}
|
|
@ -70,7 +70,7 @@ export function unitizeBar(inputSpec: TopLevelUnitSpec, outputSpec: Vega.Spec, u
|
|||
|
||||
data0.source = 'source_00';
|
||||
delete data0Url.url;
|
||||
delete data0.format;
|
||||
delete data0Url.format;
|
||||
|
||||
if (info.quantitativeBand) {
|
||||
bandBinTransform = findBinTransform(data0, info.bandEncoding.field).transform;
|
||||
|
|
Загрузка…
Ссылка в новой задаче