apache_echarts_wrapper/
builder.rs

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