Skip to content

Commit

Permalink
add overlay line, area
Browse files Browse the repository at this point in the history
  • Loading branch information
kanitw committed Jun 23, 2016
1 parent 92da1f8 commit 7da5591
Show file tree
Hide file tree
Showing 4 changed files with 239 additions and 9 deletions.
17 changes: 11 additions & 6 deletions src/compile/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import {Config} from '../config';
import {Encoding} from '../encoding';
import {isAggregate, has} from '../encoding';
import {isMeasure} from '../fielddef';
import {POINT, LINE, TICK, CIRCLE, SQUARE, RULE, Mark} from '../mark';
import {AREA, POINT, LINE, TICK, CIRCLE, SQUARE, RULE, Mark} from '../mark';
import {contains, extend} from '../util';

/**
Expand All @@ -21,11 +21,16 @@ export function initMarkConfig(mark: Mark, encoding: Encoding, config: Config) {
}
break;
case 'opacity':
if (value === undefined && contains([POINT, TICK, CIRCLE, SQUARE], mark)) {
// point-based marks and bar
if (!isAggregate(encoding) || has(encoding, DETAIL)) {
cfg[property] = 0.7;
}
if (value === undefined) {
if (contains([POINT, TICK, CIRCLE, SQUARE], mark)) {
// point-based marks and bar
if (!isAggregate(encoding) || has(encoding, DETAIL)) {
cfg[property] = 0.7;
}
}
if (mark === AREA) {
cfg[property] = 0.6; // inspired by Tableau
}
}
break;
case 'orient':
Expand Down
38 changes: 38 additions & 0 deletions src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,40 @@ export enum Interpolate {
MONOTONE = 'monotone' as any,
}

export enum AreaOverlay {
LINE = 'line' as any,
LINEPOINT = 'linepoint' as any,
NONE = 'none' as any
}

export interface OverlayConfig {
/**
* Whether to overlay line with point.
*/
line?: boolean;

/**
* Type of overlay for area mark (line or linepoint)
*/
area?: AreaOverlay;

/**
* Default style for the overlayed point.
*/
pointStyle?: MarkConfig;

/**
* Default style for the overlayed point.
*/
lineStyle?: MarkConfig;
}

export const defaultOverlayConfig: OverlayConfig = {
line: false,
pointStyle: {filled: true},
lineStyle: {}
};

export interface MarkConfig {

// ---------- Color ----------
Expand Down Expand Up @@ -390,6 +424,9 @@ export interface Config {
/** Mark Config */
mark?: MarkConfig;

/** Mark Overlay Config */
overlay?: OverlayConfig;

/** Scale Config */
scale?: ScaleConfig;

Expand All @@ -409,6 +446,7 @@ export const defaultConfig: Config = {

cell: defaultCellConfig,
mark: defaultMarkConfig,
overlay: defaultOverlayConfig,
scale: defaultScaleConfig,
axis: defaultAxisConfig,
legend: defaultLegendConfig,
Expand Down
70 changes: 67 additions & 3 deletions src/spec.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
/* Package of defining Vega-lite Specification's json schema at its utility functions */

import {ROW, COLUMN} from './channel';
import {Config} from './config';
import {Config, defaultOverlayConfig, AreaOverlay} from './config';
import {Data} from './data';
import {Encoding, UnitEncoding, has} from './encoding';
import {Facet} from './facet';
import {FieldDef} from './fielddef';
import {Mark} from './mark';
import {Mark, LINE, AREA, POINT} from './mark';
import {stack} from './stack';
import {Transform} from './transform';

import * as vlEncoding from './encoding';
import {duplicate, extend, pick} from './util';
import {contains, duplicate, extend, keys, pick, omit} from './util';

export interface BaseSpec {
/**
Expand Down Expand Up @@ -133,13 +133,16 @@ export function isLayerSpec(spec: ExtendedSpec): spec is LayerSpec {
return spec['layers'] !== undefined;
}


/**
* Decompose extended unit specs into composition of pure unit specs.
*/
// TODO: consider moving this to another file. Maybe vl.spec.normalize or vl.normalize
export function normalize(spec: ExtendedSpec): Spec {
if (isExtendedUnitSpec(spec)) {
return normalizeExtendedUnitSpec(spec);
} else if (isUnitSpec(spec)) {
return normalizeUnitSpec(spec as any);
}

return spec;
Expand All @@ -164,6 +167,67 @@ export function normalizeExtendedUnitSpec(spec: ExtendedUnitSpec) {
);
}

export function normalizeUnitSpec(spec: UnitSpec): Spec {
const config = spec.config;
const overlayConfig = config && config.overlay;
const overlayWithLine = overlayConfig && spec.mark === AREA &&
contains([AreaOverlay.LINEPOINT, AreaOverlay.LINE], overlayConfig.area);
const overlayWithPoint = overlayConfig && (
(overlayConfig.line && spec.mark === LINE) ||
(overlayConfig.area === AreaOverlay.LINEPOINT && spec.mark === AREA)
);

if (isStacked(spec)) {
// We can't overlay stacked area yet!
return spec;
}

if (overlayWithPoint || overlayWithLine) {
return normalizeOverlay(spec, overlayWithPoint, overlayWithLine);
}
return spec;
}

export function normalizeOverlay(spec: UnitSpec, overlayWithPoint: boolean, overlayWithLine: boolean): LayerSpec {
let outerProps = ['name', 'description', 'data', 'transform'];
let baseSpec = omit(spec, outerProps.concat('config'));

let baseConfig = duplicate(spec.config);
delete baseConfig.overlay;
// TODO: remove shape, size

const layerSpec = extend(
pick(spec, outerProps),
{ layers: [baseSpec] },
keys(baseConfig).length > 0 ? { config: baseConfig } : {}
);

if (overlayWithLine) {
// TODO: add name with suffix
let lineSpec = duplicate(baseSpec);
lineSpec.mark = LINE;
// TODO: remove shape, size
let markConfig = extend({}, defaultOverlayConfig.lineStyle, spec.config.overlay.lineStyle);
if (keys(markConfig).length > 0) {
lineSpec.config = {mark: markConfig};
}

layerSpec.layers.push(lineSpec);
}

if (overlayWithPoint) {
// TODO: add name with suffix
let pointSpec = duplicate(baseSpec);
pointSpec.mark = POINT;
let markConfig = extend({}, defaultOverlayConfig.pointStyle, spec.config.overlay.pointStyle);;
if (keys(markConfig).length > 0) {
pointSpec.config = {mark: markConfig};
}
layerSpec.layers.push(pointSpec);
}
return layerSpec;
}

// TODO: add vl.spec.validate & move stuff from vl.validate to here

export function alwaysNoOcclusion(spec: ExtendedUnitSpec): boolean {
Expand Down
123 changes: 123 additions & 0 deletions test/spec.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,4 +64,127 @@ describe('normalize()', function () {
});
});
});

describe('normalizeOverlay', () => {
describe('line', () => {
it('should be normalized correctly', () => {
const spec: any = {
"description": "Google's stock price over time.",
"data": {"url": "data/stocks.csv", "format": {"type": "csv"}},
"transform": {"filter": "datum.symbol==='GOOG'"},
"mark": "line",
"encoding": {
"x": {"field": "date", "type": "temporal"},
"y": {"field": "price", "type": "quantitative"}
},
"config": {"overlay": {"line": true}}
};
const normalizedSpec = normalize(spec);
assert.deepEqual(normalizedSpec, {
"description": "Google's stock price over time.",
"data": {"url": "data/stocks.csv","format": {"type": "csv"}},
"transform": {"filter": "datum.symbol==='GOOG'"},
"layers": [
{
"mark": "line",
"encoding": {
"x": {"field": "date","type": "temporal"},
"y": {"field": "price","type": "quantitative"}
}
},
{
"mark": "point",
"encoding": {
"x": {"field": "date","type": "temporal"},
"y": {"field": "price","type": "quantitative"}
},
"config": {"mark": {"filled": true}}
}
]
});
});
});

describe('area', () => {
it('with linepoint should be normalized correctly', () => {
const spec: any = {
"description": "Google's stock price over time.",
"data": {"url": "data/stocks.csv", "format": {"type": "csv"}},
"transform": {"filter": "datum.symbol==='GOOG'"},
"mark": "area",
"encoding": {
"x": {"field": "date", "type": "temporal"},
"y": {"field": "price", "type": "quantitative"}
},
"config": {"overlay": {"area": 'linepoint'}}
};
const normalizedSpec = normalize(spec);
assert.deepEqual(normalizedSpec, {
"description": "Google's stock price over time.",
"data": {"url": "data/stocks.csv","format": {"type": "csv"}},
"transform": {"filter": "datum.symbol==='GOOG'"},
"layers": [
{
"mark": "area",
"encoding": {
"x": {"field": "date","type": "temporal"},
"y": {"field": "price","type": "quantitative"}
}
},
{
"mark": "line",
"encoding": {
"x": {"field": "date","type": "temporal"},
"y": {"field": "price","type": "quantitative"}
}
},
{
"mark": "point",
"encoding": {
"x": {"field": "date","type": "temporal"},
"y": {"field": "price","type": "quantitative"}
},
"config": {"mark": {"filled": true}}
}
]
});
});

it('with linepoint should be normalized correctly', () => {
const spec: any = {
"description": "Google's stock price over time.",
"data": {"url": "data/stocks.csv", "format": {"type": "csv"}},
"transform": {"filter": "datum.symbol==='GOOG'"},
"mark": "area",
"encoding": {
"x": {"field": "date", "type": "temporal"},
"y": {"field": "price", "type": "quantitative"}
},
"config": {"overlay": {"area": 'line'}}
};
const normalizedSpec = normalize(spec);
assert.deepEqual(normalizedSpec, {
"description": "Google's stock price over time.",
"data": {"url": "data/stocks.csv","format": {"type": "csv"}},
"transform": {"filter": "datum.symbol==='GOOG'"},
"layers": [
{
"mark": "area",
"encoding": {
"x": {"field": "date","type": "temporal"},
"y": {"field": "price","type": "quantitative"}
}
},
{
"mark": "line",
"encoding": {
"x": {"field": "date","type": "temporal"},
"y": {"field": "price","type": "quantitative"}
}
}
]
});
});
});
});
});

0 comments on commit 7da5591

Please sign in to comment.