-
Notifications
You must be signed in to change notification settings - Fork 592
/
type.ts
138 lines (122 loc) · 4.54 KB
/
type.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
import {isBinning} from '../../bin';
import {
getSizeChannel,
isColorChannel,
isScaleChannel,
isXorY,
isXorYOffset,
rangeType,
ScaleChannel
} from '../../channel';
import {DatumDef, isFieldDef, isPositionFieldOrDatumDef, ScaleDatumDef, TypedFieldDef} from '../../channeldef';
import * as log from '../../log';
import {isRelativeBandSize, MarkDef} from '../../mark';
import {channelSupportScaleType, Scale, ScaleType, scaleTypeSupportDataType} from '../../scale';
import {normalizeTimeUnit} from '../../timeunit';
import * as util from '../../util';
import {POLAR_POSITION_SCALE_CHANNEL_INDEX} from './../../channel';
export type RangeType = 'continuous' | 'discrete' | 'flexible' | undefined;
/**
* Determine if there is a specified scale type and if it is appropriate,
* or determine default type if type is unspecified or inappropriate.
*/
// NOTE: CompassQL uses this method.
export function scaleType(
specifiedScale: Scale,
channel: ScaleChannel,
fieldDef: TypedFieldDef<string> | DatumDef,
mark: MarkDef,
hasNestedOffsetScale = false
): ScaleType {
const defaultScaleType = defaultType(channel, fieldDef, mark, hasNestedOffsetScale);
const {type} = specifiedScale;
if (!isScaleChannel(channel)) {
// There is no scale for these channels
return null;
}
if (type !== undefined) {
// Check if explicitly specified scale type is supported by the channel
if (!channelSupportScaleType(channel, type)) {
log.warn(log.message.scaleTypeNotWorkWithChannel(channel, type, defaultScaleType));
return defaultScaleType;
}
// Check if explicitly specified scale type is supported by the data type
if (isFieldDef(fieldDef) && !scaleTypeSupportDataType(type, fieldDef.type)) {
log.warn(log.message.scaleTypeNotWorkWithFieldDef(type, defaultScaleType));
return defaultScaleType;
}
return type;
}
return defaultScaleType;
}
/**
* Determine appropriate default scale type.
*/
// NOTE: Voyager uses this method.
function defaultType(
channel: ScaleChannel,
fieldDef: TypedFieldDef<string> | ScaleDatumDef,
mark: MarkDef,
hasNestedOffsetScale: boolean
): ScaleType {
switch (fieldDef.type) {
case 'nominal':
case 'ordinal': {
if (isColorChannel(channel) || rangeType(channel) === 'discrete') {
if (channel === 'shape' && fieldDef.type === 'ordinal') {
log.warn(log.message.discreteChannelCannotEncode(channel, 'ordinal'));
}
return 'ordinal';
}
if (isXorY(channel) || isXorYOffset(channel)) {
if (util.contains(['rect', 'bar', 'image', 'tick', 'rule'], mark.type)) {
// The rect/bar mark should fit into a band.
// For rule, using band scale to make rule align with axis ticks better https://github.com/vega/vega-lite/issues/3429
return 'band';
}
if (hasNestedOffsetScale) {
// If there is a nested offset scale, then there is a "band" for the span of the nested scale.
return 'band';
}
} else if (mark.type === 'arc' && channel in POLAR_POSITION_SCALE_CHANNEL_INDEX) {
return 'band';
}
const dimensionSize = mark[getSizeChannel(channel)];
if (isRelativeBandSize(dimensionSize)) {
return 'band';
}
if (isPositionFieldOrDatumDef(fieldDef) && fieldDef.axis?.tickBand) {
return 'band';
}
// Otherwise, use ordinal point scale so we can easily get center positions of the marks.
return 'point';
}
case 'temporal':
if (isColorChannel(channel)) {
return 'time';
} else if (rangeType(channel) === 'discrete') {
log.warn(log.message.discreteChannelCannotEncode(channel, 'temporal'));
// TODO: consider using quantize (equivalent to binning) once we have it
return 'ordinal';
} else if (isFieldDef(fieldDef) && fieldDef.timeUnit && normalizeTimeUnit(fieldDef.timeUnit).utc) {
return 'utc';
}
return 'time';
case 'quantitative':
if (isColorChannel(channel)) {
if (isFieldDef(fieldDef) && isBinning(fieldDef.bin)) {
return 'bin-ordinal';
}
return 'linear';
} else if (rangeType(channel) === 'discrete') {
log.warn(log.message.discreteChannelCannotEncode(channel, 'quantitative'));
// TODO: consider using quantize (equivalent to binning) once we have it
return 'ordinal';
}
return 'linear';
case 'geojson':
return undefined;
}
/* istanbul ignore next: should never reach this */
throw new Error(log.message.invalidFieldType(fieldDef.type));
}