UNPKG

compassql

Version:

CompassQL visualization query language

292 lines (266 loc) 8.21 kB
import * as CHANNEL from 'vega-lite/build/src/channel'; import * as MARK from 'vega-lite/build/src/mark'; import {Mark} from 'vega-lite/build/src/mark'; import {QueryConfig} from '../../config'; import {SpecQueryModel} from '../../model'; import {Schema} from '../../schema'; import {Dict, forEach} from '../../util'; import {FeatureScore} from '../ranking'; import {Scorer} from './base'; import {BIN_Q, ExtendedType, getExtendedType, K, N, NONE, O, Q, T, TIMEUNIT_O, TIMEUNIT_T} from './type'; export class MarkScorer extends Scorer { constructor() { super('Mark'); } protected initScore() { return init(); } public getScore(specM: SpecQueryModel, _: Schema, __: QueryConfig): FeatureScore[] { let mark = specM.getMark() as Mark; if (mark === MARK.CIRCLE || mark === MARK.SQUARE) { mark = MARK.POINT; } const xEncQ = specM.getEncodingQueryByChannel(CHANNEL.X); const xType = xEncQ ? getExtendedType(xEncQ) : NONE; const yEncQ = specM.getEncodingQueryByChannel(CHANNEL.Y); const yType = yEncQ ? getExtendedType(yEncQ) : NONE; const isOccluded = !specM.isAggregate(); // FIXME const feature = xType + '_' + yType + '_' + isOccluded + '_' + mark; const featureScore = this.getFeatureScore(feature); if (featureScore) { return [featureScore]; } console.error('feature score missing for', feature); return []; } } export function featurize(xType: ExtendedType, yType: ExtendedType, hasOcclusion: boolean, mark: Mark) { return xType + '_' + yType + '_' + hasOcclusion + '_' + mark; } function init() { const MEASURES = [Q, T]; const DISCRETE = [BIN_Q, TIMEUNIT_O, O, N, K]; const DISCRETE_OR_NONE = DISCRETE.concat([NONE]); let SCORE = {} as Dict<number>; // QxQ MEASURES.forEach(xType => { MEASURES.forEach(yType => { // has occlusion const occludedQQMark = { point: 0, text: -0.2, tick: -0.5, rect: -1, bar: -2, line: -2, area: -2, rule: -2.5 }; forEach(occludedQQMark, (score, mark: Mark) => { const feature = featurize(xType, yType, true, mark); SCORE[feature] = score; }); // no occlusion // TODO: possible to use connected scatter plot const noOccludedQQMark = { point: 0, text: -0.2, tick: -0.5, bar: -2, line: -2, area: -2, rule: -2.5 }; forEach(noOccludedQQMark, (score, mark: Mark) => { const feature = featurize(xType, yType, false, mark); SCORE[feature] = score; }); }); }); // DxQ, QxD MEASURES.forEach(xType => { // HAS OCCLUSION DISCRETE_OR_NONE.forEach(yType => { const occludedDimensionMeasureMark = { tick: 0, point: -0.2, text: -0.5, bar: -2, line: -2, area: -2, rule: -2.5 }; forEach(occludedDimensionMeasureMark, (score, mark: Mark) => { const feature = featurize(xType, yType, true, mark); SCORE[feature] = score; // also do the inverse const feature2 = featurize(yType, xType, true, mark); SCORE[feature2] = score; }); }); [TIMEUNIT_T].forEach(yType => { const occludedDimensionMeasureMark = { // For Time Dimension with time scale, tick is not good point: 0, text: -0.5, tick: -1, bar: -2, line: -2, area: -2, rule: -2.5 }; forEach(occludedDimensionMeasureMark, (score, mark: Mark) => { const feature = featurize(xType, yType, true, mark); SCORE[feature] = score; // also do the inverse const feature2 = featurize(yType, xType, true, mark); SCORE[feature2] = score; }); }); // NO OCCLUSION [NONE, N, O, K].forEach(yType => { const noOccludedQxN = { bar: 0, point: -0.2, tick: -0.25, text: -0.3, // Line / Area can mislead trend for N line: -2, // FIXME line vs area? area: -2, // Non-sense to use rule here rule: -2.5 }; forEach(noOccludedQxN, (score, mark: Mark) => { const feature = featurize(xType, yType, false, mark); SCORE[feature] = score; // also do the inverse const feature2 = featurize(yType, xType, false, mark); SCORE[feature2] = score; }); }); [BIN_Q].forEach(yType => { const noOccludedQxBinQ = { bar: 0, point: -0.2, tick: -0.25, text: -0.3, // Line / Area isn't the best fit for bin line: -0.5, // FIXME line vs area? area: -0.5, // Non-sense to use rule here rule: -2.5 }; forEach(noOccludedQxBinQ, (score, mark: Mark) => { const feature = featurize(xType, yType, false, mark); SCORE[feature] = score; // also do the inverse const feature2 = featurize(yType, xType, false, mark); SCORE[feature2] = score; }); }); [TIMEUNIT_T, TIMEUNIT_O].forEach(yType => { // For aggregate / surely no occlusion plot, Temporal with time or ordinal // are not that different. const noOccludedQxBinQ = { line: 0, area: -0.1, bar: -0.2, point: -0.3, tick: -0.35, text: -0.4, // Non-sense to use rule here rule: -2.5 }; forEach(noOccludedQxBinQ, (score, mark: Mark) => { const feature = featurize(xType, yType, false, mark); SCORE[feature] = score; // also do the inverse const feature2 = featurize(yType, xType, false, mark); SCORE[feature2] = score; }); }); }); [TIMEUNIT_T].forEach(xType => { [TIMEUNIT_T].forEach(yType => { // has occlusion const ttMark = { point: 0, rect: -0.1, // assuming rect will be small text: -0.5, tick: -1, bar: -2, line: -2, area: -2, rule: -2.5 }; // No difference between has occlusion and no occlusion // as most of the time, it will be the occluded case. forEach(ttMark, (score, mark: Mark) => { const feature = featurize(xType, yType, true, mark); SCORE[feature] = score; }); forEach(ttMark, (score, mark: Mark) => { const feature = featurize(xType, yType, false, mark); SCORE[feature] = score; }); }); DISCRETE_OR_NONE.forEach(yType => { // has occlusion const tdMark = { tick: 0, point: -0.2, text: -0.5, rect: -1, bar: -2, line: -2, area: -2, rule: -2.5 }; // No difference between has occlusion and no occlusion // as most of the time, it will be the occluded case. forEach(tdMark, (score, mark: Mark) => { const feature = featurize(xType, yType, true, mark); SCORE[feature] = score; }); forEach(tdMark, (score, mark: Mark) => { const feature = featurize(yType, xType, true, mark); SCORE[feature] = score; }); forEach(tdMark, (score, mark: Mark) => { const feature = featurize(xType, yType, false, mark); SCORE[feature] = score; }); forEach(tdMark, (score, mark: Mark) => { const feature = featurize(yType, xType, false, mark); SCORE[feature] = score; }); }); }); // DxD // Note: We use for loop here because using forEach sometimes leads to a mysterious bug for (const xType of DISCRETE_OR_NONE) { for (const yType of DISCRETE_OR_NONE) { // has occlusion const ddMark = { point: 0, rect: 0, text: -0.1, tick: -1, bar: -2, line: -2, area: -2, rule: -2.5 }; forEach(ddMark, (score, mark: Mark) => { const feature = featurize(xType, yType, true, mark); SCORE[feature] = score; }); // same for no occlusion. forEach(ddMark, (score, mark: Mark) => { const feature = featurize(xType, yType, false, mark); SCORE[feature] = score; }); } } return SCORE; }