theta_chart/series/
series_time.rs

1use crate::{
2    chart::*,
3    coord::{Axes, Stick},
4    utils::cal_step::CalStep,
5};
6use chrono::{Datelike, Months, NaiveDate, NaiveDateTime, NaiveTime, ParseError};
7use std::vec;
8
9#[derive(Debug, Clone)]
10/// A series of numbers represented on a chart
11pub struct STime {
12    series: Vec<NaiveDateTime>,
13    format: String,
14    dirty: bool,
15    unit: String,
16}
17
18impl Default for STime {
19    fn default() -> Self {
20        Self {
21            series: Default::default(),
22            format: "%Y-%m-%d %H:%M:%S".to_string(),
23            dirty: false,
24            unit: "full".to_string(),
25        }
26    }
27}
28
29impl STime {
30    pub fn new(series: Vec<NaiveDateTime>) -> Self {
31        // let domain = min_max_vec(&series);
32        STime {
33            series,
34            format: "%Y-%m-%d %H:%M:%S".to_string(),
35            dirty: false,
36            unit: "full".to_string(),
37        }
38    }
39
40    pub fn set_format(&self, format: &str) -> Self {
41        STime {
42            series: self.series.clone(),
43            format: format.to_string(),
44            dirty: self.dirty,
45            unit: "full".to_string(),
46        }
47    }
48
49    pub fn set_data(&self, series: Vec<NaiveDateTime>) -> Self {
50        STime {
51            series: series,
52            format: self.format.clone(),
53            dirty: false,
54            unit: "full".to_string(),
55        }
56    }
57
58    pub fn get_unit(&self) -> String {
59        self.unit.clone()
60    }
61
62    pub fn get_format(&self) -> &str {
63        match self.unit.as_str() {
64            "date" => "%Y-%m-%d",
65            "month" => "%Y-%m",
66            "year" => "%Y",
67            "time" => "%H:%M:%S",
68            "hour" => "%H:%M:%S",
69            _ => "",
70        }
71    }
72
73    pub fn series(&self) -> Vec<NaiveDateTime> {
74        self.series.clone()
75    }
76
77    pub fn get_nv(&self, index: usize) -> NaiveDateTime {
78        self.series[index]
79    }
80
81    pub fn merge(&self, other: STime) -> Self {
82        let mut series = self.series.clone();
83        series.extend(&other.series);
84        Self {
85            series: series.clone(),
86            format: self.format.clone(),
87            dirty: self.dirty,
88            unit: self.unit.clone(),
89        }
90    }
91}
92
93impl From<(Vec<&str>, &str, &str)> for STime {
94    fn from(value: (Vec<&str>, &str, &str)) -> Self {
95        let vec = value.0;
96        let format = value.1.to_string();
97        let mut series: Vec<NaiveDateTime> = vec![];
98        let mut dirty = false;
99        let unit = value.2.to_string();
100        for i in 0..vec.len() {
101            let rndt = ndt_parse_from_str(vec[i], value.1, value.2);
102            match rndt {
103                Ok(ndt) => series.push(ndt),
104                Err(_) => dirty = true,
105            }
106        }
107        STime {
108            series,
109            format,
110            dirty,
111            unit,
112        }
113    }
114}
115
116fn ndt_parse_from_str(str: &str, format: &str, get: &str) -> Result<NaiveDateTime, ParseError> {
117    match get {
118        "full" => NaiveDateTime::parse_from_str(str, format),
119        "date" => {
120            let date = NaiveDate::parse_from_str(str, format);
121
122            match date {
123                Ok(d) => {
124                    let time = NaiveTime::default();
125                    Ok(NaiveDateTime::new(d, time))
126                }
127                Err(e) => Err(e),
128            }
129        }
130        "year" => {
131            let year = format!("{}-01-01", str);
132            ndt_parse_from_str(year.as_str(), "%Y-%m-%d", "date")
133        }
134        "month" => {
135            let month = format!("{}-01", str);
136            ndt_parse_from_str(&month.as_str(), "%Y-%m-%d", "date")
137        }
138
139        _ => NaiveDateTime::parse_from_str(str, format),
140    }
141}
142
143impl ScaleTime for STime {
144    fn domain(&self) -> (NaiveDateTime, NaiveDateTime) {
145        if self.series().len() > 0 {
146            let binding = self.series();
147            let min = binding.iter().min().unwrap();
148            let max = binding.iter().max().unwrap();
149            (*min, *max)
150        } else {
151            (NaiveDateTime::default(), NaiveDateTime::default())
152        }
153    }
154
155    fn domain_unix(&self) -> (f64, f64) {
156        let (min, max) = self.domain();
157        match self.unit.as_str() {
158            "year" => (min.year() as f64, max.year() as f64),
159            "month" => {
160                let min = min.year() as f64 * 12. + min.month0() as f64;
161                let max = max.year() as f64 * 12. + max.month0() as f64;
162                (min, max)
163            }
164            _ => (0., 0.),
165        }
166    }
167
168    // fn count_distance_step(&self) -> (f64, f64) {
169    //     match self.get_unit().as_str() {
170    //         "year" => {
171    //             let (min, max) = self.domain_unix();
172    //             let dur = max - min;
173
174    //             let mut step = dur as f64 / 5.;
175    //             step = CalStep::new(step.ceil()).cal_scale();
176
177    //             (dur as f64 / step, step)
178    //         }
179    //         // TODO: for month, day, time
180    //         _ => (1., 0.),
181    //     }
182    // }
183
184    // fn scale_intervale(&self, value: NaiveDateTime) -> f64 {
185    //     let (min, _max) = self.domain();
186    //     let unit = self.get_unit();
187    //     match unit.as_str() {
188    //         "year" => (value.year() - min.year()) as f64,
189    //         _ => (value.year() - min.year()) as f64,
190    //     }
191    // }
192
193    fn scale(&self, value: NaiveDateTime) -> f64 {
194        let unit = self.get_unit();
195        match unit.as_str() {
196            "year" => {
197                let (min, max) = self.domain_unix();
198                let range = max - min;
199
200                let diff = value.year() as f64 - min;
201                diff / range
202            }
203            "month" => {
204                let (min, max) = self.domain_unix();
205                let range = max - min;
206                let diff = value.year() as f64 * 12. + value.month0() as f64 - min;
207                diff / range
208            }
209            _ => 1.,
210        }
211    }
212
213    fn gen_axes(&self) -> Axes {
214        let mut style = String::default();
215        let mut vec_stick: Vec<Stick> = vec![];
216        let mut step = 0.;
217        let unit = self.get_unit();
218
219        let (min, max) = self.domain_unix();
220
221        match unit.as_str() {
222            "year" => {
223                style = "time-year".to_string();
224                step = (max - min) / 5.;
225                step = CalStep::new(step).cal_scale();
226
227                let first_stick = (min / step).ceil() * step;
228                let last_stick = (max / step).floor() * step;
229                for value in ((first_stick as i64)..(last_stick as i64 + 1)).step_by(step as usize)
230                {
231                    let string_value = value.to_string();
232                    let nv = ndt_parse_from_str(string_value.as_str(), "%Y", "year").unwrap();
233                    let stick = Stick::new(value.to_string(), self.scale(nv));
234                    vec_stick.push(stick);
235                }
236            }
237            "month" => {
238                style = "time-month".to_string();
239                step = cal_scale_time(max - min, "month");
240
241                let first_stick = (min / step).ceil();
242                let last_stick = (max / step).floor();
243
244                let interval = last_stick - first_stick;
245
246                let count = (interval) as i64;
247
248                for index in 0..(count + 1) {
249                    let value = (first_stick + index as f64) * step;
250                    let year = (value / 12.) as i32;
251                    let month = ((value / 12. - year as f64) * 12.).round();
252                    let nd = NaiveDate::from_ymd_opt(year, 1, 1).unwrap();
253                    let nd = nd.checked_add_months(Months::new(month as u32)).unwrap();
254                    let nv = NaiveDateTime::new(nd, NaiveTime::default());
255                    let stick = Stick::new(nv.format("%Y-%m").to_string(), self.scale(nv));
256                    vec_stick.push(stick);
257                }
258            }
259            _ => (),
260        }
261
262        let sticks = vec_stick
263            .into_iter()
264            .filter(|stick| stick.value >= -0.0000001 && stick.value <= 1.0000001)
265            .collect::<Vec<_>>();
266
267        Axes {
268            sticks,
269            step,
270            style,
271        }
272    }
273
274    fn to_stick(&self) -> Vec<Stick> {
275        let mut vec_stick: Vec<Stick> = vec![];
276        let unit = self.get_unit();
277        let len = self.series().len();
278        match unit.as_str() {
279            "year" => {
280                for index in 0..len {
281                    let nv = self.get_nv(index);
282                    let string_value = nv.format(self.get_format()).to_string();
283                    let stick = Stick::new(string_value, self.scale(nv));
284                    vec_stick.push(stick);
285                }
286            }
287            "month" => {
288                for index in 0..len {
289                    let nv = self.get_nv(index);
290                    let string_value = nv.format(self.get_format()).to_string();
291                    let stick = Stick::new(string_value, self.scale(nv));
292                    vec_stick.push(stick);
293                }
294            }
295            _ => (),
296        }
297
298        vec_stick
299    }
300}
301
302fn cal_scale_time(num: f64, unit: &str) -> f64 {
303    let num = num / 5.;
304    match unit {
305        "month" => {
306            let step_month = CalStep::new(num / 12.).cal_scale() * 12.;
307            if step_month > 1. {
308                return step_month.round();
309            } else {
310                step_month
311            }
312        }
313        _ => num,
314    }
315}