OLD | NEW |
| (Empty) |
1 /* | |
2 * Copyright 2014 Google Inc. All rights reserved. | |
3 * | |
4 * Use of this source code is governed by a BSD-style | |
5 * license that can be found in the LICENSE file or at | |
6 * https://developers.google.com/open-source/licenses/bsd | |
7 */ | |
8 | |
9 part of charted.charts; | |
10 | |
11 /** | |
12 * Transforms the ChartData by transposing the columns and rows. A label column | |
13 * index in the original data will need to be specified (default to 0), all | |
14 * values in the specified label column will be used as the label for the | |
15 * transformed data, all the labels in the original Chart data columns will be | |
16 * populated in the label column as values of that column. | |
17 * | |
18 * All values in the data except for the data in the label column must have the | |
19 * same type; All columns except for the label column must have the same | |
20 * formatter if a formatter exist for columns. | |
21 */ | |
22 class TransposeTransformer extends ChangeNotifier | |
23 implements ChartDataTransform, ChartData { | |
24 final SubscriptionsDisposer _dataSubscriptions = new SubscriptionsDisposer(); | |
25 ObservableList<ChartColumnSpec> columns = new ObservableList(); | |
26 ObservableList<Iterable> rows = new ObservableList(); | |
27 | |
28 // If specified, this values of this column in the input chart data will be | |
29 // used as labels of the transposed column label. Defaults to first column. | |
30 int _labelColumn; | |
31 ChartData _data; | |
32 | |
33 TransposeTransformer([this._labelColumn = 0]); | |
34 | |
35 /** | |
36 * Transforms the input data with the specified label column in the | |
37 * constructor. If the ChartData is Observable, changes fired by the input | |
38 * data will trigger transform to be performed again to update the output rows | |
39 * and columns. | |
40 */ | |
41 ChartData transform(ChartData data) { | |
42 _data = data; | |
43 _registerListeners(); | |
44 _transform(); | |
45 return this; | |
46 } | |
47 | |
48 /** Registers listeners if input ChartData is Observable. */ | |
49 _registerListeners() { | |
50 _dataSubscriptions.dispose(); | |
51 | |
52 if(_data is Observable) { | |
53 var observable = (_data as Observable); | |
54 _dataSubscriptions.add(observable.changes.listen((records) { | |
55 _transform(); | |
56 | |
57 // NOTE: Currently we're only passing the first change because the chart | |
58 // area just draw with the updated data. When we add partial update | |
59 // to chart area, we'll need to handle this better. | |
60 notifyChange(records.first); | |
61 })); | |
62 } | |
63 } | |
64 | |
65 /** | |
66 * Performs the transpose transform with _data. This is called on transform | |
67 * and on changes if ChartData is Observable. | |
68 */ | |
69 _transform() { | |
70 // Assert all columns are of the same type and formatter, excluding the | |
71 // label column. | |
72 var type; | |
73 var formatter; | |
74 for (var i = 0; i < _data.columns.length; i++) { | |
75 if (i != _labelColumn) { | |
76 if (type == null) { | |
77 type = _data.columns.elementAt(i).type; | |
78 } else { | |
79 assert(type == _data.columns.elementAt(i).type); | |
80 } | |
81 if (formatter == null) { | |
82 formatter = _data.columns.elementAt(i).formatter; | |
83 } else { | |
84 assert(formatter == _data.columns.elementAt(i).formatter); | |
85 } | |
86 } | |
87 } | |
88 | |
89 columns.clear(); | |
90 rows.clear(); | |
91 rows.addAll(new List<Iterable>.generate(_data.columns.length - 1, (i) => [])
); | |
92 | |
93 // Populate the transposed rows' data, excluding the label column, visit | |
94 // each value in the original data once. | |
95 var columnLabels = []; | |
96 for (var row in _data.rows) { | |
97 for (var i = 0; i < row.length; i++) { | |
98 var columnOffset = (i < _labelColumn) ? 0 : 1; | |
99 if (i != _labelColumn) { | |
100 (rows.elementAt(i - columnOffset) as List).add(row.elementAt(i)); | |
101 } else { | |
102 columnLabels.add(row.elementAt(i)); | |
103 } | |
104 } | |
105 } | |
106 | |
107 // Transpose the ColumnSpec's label into the column where the original | |
108 // column that is used as the new label. | |
109 for (var i = 0; i < rows.length; i++) { | |
110 var columnOffset = (i < _labelColumn) ? 0 : 1; | |
111 (rows.elementAt(i) as List).insert(_labelColumn, | |
112 _data.columns.elementAt(i + columnOffset).label); | |
113 | |
114 } | |
115 | |
116 // Construct new ColumnSpaces base on the label column. | |
117 for (var label in columnLabels) { | |
118 columns.add(new ChartColumnSpec(type: type, label: label, | |
119 formatter: formatter)); | |
120 } | |
121 columns.insert(_labelColumn, | |
122 new ChartColumnSpec(type: ChartColumnSpec.TYPE_STRING, label: | |
123 _data.columns.elementAt(_labelColumn).label)); | |
124 } | |
125 } | |
OLD | NEW |