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