1 | import {Transform} from 'vega-dataflow';
|
2 | import {
|
3 | TIME_UNITS, timeBin, timeFloor, timeInterval, timeUnits,
|
4 | utcFloor, utcInterval
|
5 | } from 'vega-time';
|
6 | import {accessorFields, extent, inherits, peek} from 'vega-util';
|
7 |
|
8 |
|
9 |
|
10 |
|
11 |
|
12 |
|
13 |
|
14 | export default function TimeUnit(params) {
|
15 | Transform.call(this, null, params);
|
16 | }
|
17 |
|
18 | const OUTPUT = ['unit0', 'unit1'];
|
19 |
|
20 | TimeUnit.Definition = {
|
21 | 'type': 'TimeUnit',
|
22 | 'metadata': {'modifies': true},
|
23 | 'params': [
|
24 | { 'name': 'field', 'type': 'field', 'required': true },
|
25 | { 'name': 'interval', 'type': 'boolean', 'default': true },
|
26 | { 'name': 'units', 'type': 'enum', 'values': TIME_UNITS, 'array': true },
|
27 | { 'name': 'step', 'type': 'number', 'default': 1 },
|
28 | { 'name': 'maxbins', 'type': 'number', 'default': 40 },
|
29 | { 'name': 'extent', 'type': 'date', 'array': true},
|
30 | { 'name': 'timezone', 'type': 'enum', 'default': 'local', 'values': ['local', 'utc'] },
|
31 | { 'name': 'as', 'type': 'string', 'array': true, 'length': 2, 'default': OUTPUT }
|
32 | ]
|
33 | };
|
34 |
|
35 | inherits(TimeUnit, Transform, {
|
36 | transform(_, pulse) {
|
37 | const field = _.field,
|
38 | band = _.interval !== false,
|
39 | utc = _.timezone === 'utc',
|
40 | floor = this._floor(_, pulse),
|
41 | offset = (utc ? utcInterval : timeInterval)(floor.unit).offset,
|
42 | as = _.as || OUTPUT,
|
43 | u0 = as[0],
|
44 | u1 = as[1],
|
45 | step = floor.step;
|
46 |
|
47 | let min = floor.start || Infinity,
|
48 | max = floor.stop || -Infinity,
|
49 | flag = pulse.ADD;
|
50 |
|
51 | if (
|
52 | _.modified() ||
|
53 | pulse.changed(pulse.REM) ||
|
54 | pulse.modified(accessorFields(field))
|
55 | ) {
|
56 | pulse = pulse.reflow(true);
|
57 | flag = pulse.SOURCE;
|
58 | min = Infinity;
|
59 | max = -Infinity;
|
60 | }
|
61 |
|
62 | pulse.visit(flag, t => {
|
63 | const v = field(t);
|
64 | let a, b;
|
65 | if (v == null) {
|
66 | t[u0] = null;
|
67 | if (band) t[u1] = null;
|
68 | } else {
|
69 | t[u0] = a = b = floor(v);
|
70 | if (band) t[u1] = b = offset(a, step);
|
71 | if (a < min) min = a;
|
72 | if (b > max) max = b;
|
73 | }
|
74 | });
|
75 |
|
76 | floor.start = min;
|
77 | floor.stop = max;
|
78 |
|
79 | return pulse.modifies(band ? as : u0);
|
80 | },
|
81 |
|
82 | _floor(_, pulse) {
|
83 | const utc = _.timezone === 'utc';
|
84 |
|
85 |
|
86 | const {units, step} = _.units
|
87 | ? {units: _.units, step: _.step || 1}
|
88 | : timeBin({
|
89 | extent: _.extent || extent(pulse.materialize(pulse.SOURCE).source, _.field),
|
90 | maxbins: _.maxbins
|
91 | });
|
92 |
|
93 |
|
94 | const tunits = timeUnits(units),
|
95 | prev = this.value || {},
|
96 | floor = (utc ? utcFloor : timeFloor)(tunits, step);
|
97 |
|
98 | floor.unit = peek(tunits);
|
99 | floor.units = tunits;
|
100 | floor.step = step;
|
101 | floor.start = prev.start;
|
102 | floor.stop = prev.stop;
|
103 | return this.value = floor;
|
104 | }
|
105 | });
|