1use crate::axis_typing::{AxisKindMarker, ValueAxis};
2use crate::common::Size;
3use crate::options::*;
4use crate::templates::ScriptTemplate;
5use serde::Serialize;
6use serde_json::json;
7use uuid::Uuid;
8use crate::common;
9use crate::options::Position::Percent;
10
11pub trait RegressionChartBuilder<X, Y>: Sized
13where X: AxisKindMarker<AxisType=ValueAxis>,
14 Y: AxisKindMarker<AxisType=ValueAxis>,
15 EChartOptions<X,Y>:Serialize
16
17{
18 fn options(&mut self) -> &mut EChartOptions<X,Y>;
19
20
21 fn add_linear_regression_dataset(self, data_source_index: usize) -> usize {
22 self.add_regression_dataset(data_source_index, RegressionMethod::Linear, None)
23 }
24
25 fn add_polynomial_regression_dataset(self, data_source_index: usize, order: u8) -> usize {
26 self.add_regression_dataset(data_source_index, RegressionMethod::Polynomial, Some(order))
27 }
28
29 fn add_exponential_regression_dataset(self, data_source_index: usize) -> usize {
30 self.add_regression_dataset(data_source_index, RegressionMethod::Exponential, None)
31 }
32
33 fn add_regression_dataset(mut self, data_source_index: usize, method: RegressionMethod, order: Option<u8>) -> usize {
34 let index = self.options().dataset.as_mut().unwrap().len();
35 let regression_config = RegressionConfig {
36 method: method.clone(),
37 order,
38 extra: None,
39 };
40 let dataset = DatasetTransform::regression(regression_config);
41 self.options().dataset.as_mut().unwrap().push(DatasetComponent::tr(dataset, data_source_index));
42 index
43 }
44
45
46 fn add_regression_series<TData: Into<DatasetComponent<X,Y>>>(mut self, series_label: &str, data: TData,
48 method: RegressionMethod, order: Option<u8>) -> Self {
49 if self.options().dataset.is_none() {
51 self.options().dataset = Some(Vec::new());
52 }
53
54 let datasets = self.options().dataset.as_mut().unwrap();
56 let source_index = datasets.len();
57 datasets.push(data.into());
58
59
60 let transform_index = datasets.len();
62 let mut regression_config = RegressionConfig {
63 method: method.clone(),
64 order: None,
65 extra: None,
66 };
67
68 if method == RegressionMethod::Polynomial {
70 regression_config.order = order;
71 }
72
73 datasets.push(
74 DatasetComponent::tr(
75 DatasetTransform::regression(regression_config),
76 source_index
77 )
78 );
79
80 self.options().series.as_mut().unwrap().push(Series {
82 r#type: Some(SeriesType::Scatter),
83 name: Some(format!("{} (data)", series_label)),
84 smooth: None,
85 area_style: None,
86 data: SeriesDataSource::DatasetIndex(source_index),
87 symbol: Some(DataPointSymbol::Circle),
88 symbol_size: Some(8),
89 extra: None,
90 });
91
92 self.options().series.as_mut().unwrap().push(Series {
94 r#type: Some(SeriesType::Line),
95 name: Some(format!("{} (regression)", series_label)),
96 smooth: Some(true),
97 area_style: None,
98 data: SeriesDataSource::DatasetIndex(transform_index),
99 symbol: Some(DataPointSymbol::None),
100 symbol_size: None,
101 extra: None
102 });
103
104 self
105 }
106
107 fn add_linear_regression_series<TData:Into<DatasetComponent<X,Y>>>(self, series_label: &str, data: TData) -> Self {
109 self.add_regression_series(series_label, data, RegressionMethod::Linear, None)
110 }
111
112 fn add_polynomial_regression_series<TData:Into<DatasetComponent<X,Y>>>(self, series_label: &str, data: TData, order: u8) -> Self {
114 self.add_regression_series(series_label, data, RegressionMethod::Polynomial, Some(order))
115 }
116
117 fn add_exponential_regression_series<TData:Into<DatasetComponent<X,Y>>>(self, series_label: &str, data: TData) -> Self
119 {
120 self.add_regression_series(series_label, data, RegressionMethod::Exponential, None)
121 }
122
123 fn add_logarithmic_regression_series<TData:Into<DatasetComponent<X,Y>>>(self, series_label: &str, data: TData) -> Self {
125 self.add_regression_series(series_label, data, RegressionMethod::Logarithmic, None)
126 }
127}
128
129
130impl<X, Y> Default for EChartOptions<X,Y>
131where X: AxisKindMarker, Y: AxisKindMarker, EChartOptions<X,Y>:Serialize{
132 fn default() -> Self {
133 Self {
134 title: None,
135 tooltip: Some(Tooltip {
136 show: true,
137 show_delay: None,
138 hide_delay: None,
139 trigger: Some(TooltipTrigger::Item),
140 formatter: None,
141 axis_pointer: Some(AxisPointer {
142 r#type: Some(AxisPointerType::Cross),
143 snap: Some(false),
144 animation: None,
145 axis: None,
146 })
147 }),
148 legend: None, grid: None, extra: None, dataset: None,
149 x_axis: Axis::default(),
150 y_axis: Axis::default(),
151 series: Some(Vec::new()),
152 }
153 }
154}
155
156impl<X, Y> EChartOptions<X,Y>
157where X: AxisKindMarker, Y: AxisKindMarker, EChartOptions<X,Y>:Serialize {
158 pub fn new(x_axis:Axis<X>,y_axis: Axis<Y>) -> Self {
159 Self {
160 title: None,
161 tooltip: Some(Tooltip {
162 show: true,
163 show_delay: None,
164 hide_delay: None,
165 trigger: Some(TooltipTrigger::Item),
166 formatter: None,
167 axis_pointer: Some(AxisPointer {
168 r#type: Some(AxisPointerType::Cross),
169 snap: Some(false),
170 animation: None,
171 axis: None,
172 })
173 }),
174 legend: None, grid: None, extra: None, dataset: None,
175 x_axis,
176 y_axis,
177 series: Some(Vec::new()),
178 }
179 }
180
181
182 pub fn enable_legend(mut self) -> Self{
183 if self.legend.is_none(){
184 self.legend = Some(Legend{
185 data: None,
186 orient: Some("vertical".to_string()),
187 left: None,
188 right: Some(Percent(common::Percent(0.0))),
189 top: Some(Percent(common::Percent(20.0))),
190 bottom: Some(Percent(common::Percent(20.0)))
191 })
192 }else {
193 let mut legend = self.legend.unwrap();
194 legend.right = Some(Percent(common::Percent(0.0)));
195 legend.top = Some(Percent(common::Percent(20.0)));
196 legend.bottom = Some(Percent(common::Percent(20.0)));
197 legend.orient = Some("vertical".to_string());
198 self.legend = Some(legend);
199 }
200 if self.grid.is_none(){
201 self.grid = Some( Grid{
202 left: None,
203 right: Some(Percent(common::Percent(20.0))),
204 top: None,
205 bottom: None,
206 contain_label: None,
207 background_color: None,
208 border_color: None,
209 border_width: None,
210 show: None,
211 z: None,
212 zlevel: None,
213 extra: None,
214 })
215 }else {
216 let mut grid = self.grid.unwrap();
217 grid.right = Some(Percent(common::Percent(20.0)));
218 self.grid = Some(grid);
219 }
220 self
221 }
222
223 pub fn title_str(mut self, title: String) -> Self {
225 let t = self.title.get_or_insert_default();
226 t.left = Some(Position::Keyword(PositionKeyword::Center));
227 t.text = Some(title);
228 self
229 }
230
231 fn subtitle_str(mut self, subtitle: String) -> Self {
232 self.title.get_or_insert_default().sub_text = Some(subtitle);
233 self
234 }
235
236 fn title(mut self, title: Title) -> Self {
237 self.title = Some(title);
238 self
239 }
240
241
242 fn x_axis_label(mut self, x: String) -> Self {
243 self.x_axis.name = Some(x);
244 self
245 }
246
247 fn y_axis_label(mut self, y: String) -> Self {
248 self.y_axis.name = Some(y);
249 self
250 }
251
252 fn add_dataset<TData:Into<DatasetComponent<X,Y>>>(mut self, data: TData) -> usize {
254 let index = self.dataset.as_mut().unwrap().len();
255 self.dataset.as_mut().unwrap().push(data.into());
256 index
257 }
258
259 fn add_dataset_visualisation(mut self, series_label:String, series_type: SeriesType, dataset_index: usize) -> Self {
262 let datasets = &self.dataset;
263 if let Some(datasets) = datasets {
264 if let Some(dataset) = datasets.get(dataset_index){
265 match dataset {
266 DatasetComponent::Source(_) | DatasetComponent::Transform(_) => {
267 self.series.as_mut().unwrap().push(Series {
268 r#type: Some(series_type),
269 name: Some(series_label),
270 smooth: Some(true),
271 area_style: None,
272 symbol: None,
273 symbol_size: None,
274 data: SeriesDataSource::DatasetIndex(dataset_index),
275 extra: None
276 });
277 }
278 DatasetComponent::LabelledSource(_) => {
279 self.series.as_mut().unwrap().push(Series {
280 r#type: Some(series_type),
281 name: Some(series_label),
282 smooth: Some(true),
283 area_style: None,
284 symbol: None,
285 symbol_size: None,
286 data: SeriesDataSource::DatasetIndex(dataset_index),
287 extra: Some(json!(
288 {"encode": {"tooltip": [2,1], "x": 0, "y": 1 }}
289 ))
290 });
291 }
292 }
293
294 }
295 }
296 self
297 }
298
299
300 pub fn add_series<TData:Into<SeriesDataSource<X,Y>>>(mut self, series_type: SeriesType, series_label:String, data: TData) -> Self {
301 self.series.as_mut().unwrap().push(
302 Series::new(series_label,series_type,data.into())
303 );
304 self
305 }
306
307 pub fn build(self, width: Size, height: Size) -> ScriptTemplate<X,Y>{
308 ScriptTemplate::new(Uuid::new_v4().to_string(), width, height, self)
309 }
310}
311
312
313
314impl <X, Y> RegressionChartBuilder<X, Y> for EChartOptions<X, Y>
315where X: AxisKindMarker<AxisType = ValueAxis> + Serialize,
316 Y: AxisKindMarker<AxisType = ValueAxis> + Serialize{
317
318 fn options(&mut self) -> &mut EChartOptions<X,Y> {
319 self
320 }
321
322}