plotly_fork/traces/
contour.rs

1//! Contour trace
2
3use plotly_derive::FieldSetter;
4use serde::Serialize;
5
6use crate::{
7    color::Color,
8    common::{
9        Calendar, ColorBar, ColorScale, Dim, Font, HoverInfo, Label, LegendGroupTitle, Line,
10        PlotType, Visible,
11    },
12    private, Trace,
13};
14
15#[derive(Serialize, Debug, Clone)]
16#[serde(rename_all = "lowercase")]
17pub enum ContoursType {
18    Levels,
19    Constraint,
20}
21
22#[derive(Serialize, Debug, Clone)]
23#[serde(rename_all = "lowercase")]
24pub enum Coloring {
25    Fill,
26    HeatMap,
27    Lines,
28    None,
29}
30
31#[derive(Serialize, Debug, Clone)]
32pub enum Operation {
33    #[serde(rename = "=")]
34    Equals,
35    #[serde(rename = "<")]
36    LessThan,
37    #[serde(rename = "<=")]
38    LessThanOrEqual,
39    #[serde(rename = ">")]
40    GreaterThan,
41    #[serde(rename = ">=")]
42    GreaterThanOrEqual,
43    #[serde(rename = "[]")]
44    Inside,
45    #[serde(rename = "][")]
46    Outside,
47}
48
49#[serde_with::skip_serializing_none]
50#[derive(Serialize, Debug, Clone, FieldSetter)]
51pub struct Contours {
52    #[field_setter(skip)]
53    r#type: Option<ContoursType>,
54    start: Option<f64>,
55    end: Option<f64>,
56    size: Option<usize>,
57    coloring: Option<Coloring>,
58    #[serde(rename = "showlines")]
59    show_lines: Option<bool>,
60    #[serde(rename = "showlabels")]
61    show_labels: Option<bool>,
62    #[serde(rename = "labelfont")]
63    label_font: Option<Font>,
64    #[serde(rename = "labelformat")]
65    label_format: Option<String>,
66    operation: Option<Operation>,
67    value: Option<f64>,
68}
69
70impl Contours {
71    pub fn new() -> Self {
72        Default::default()
73    }
74
75    pub fn type_(mut self, t: ContoursType) -> Self {
76        self.r#type = Some(t);
77        self
78    }
79}
80
81/// Construct a contour trace.
82///
83/// # Examples
84///
85/// ```
86/// use plotly::Contour;
87///
88/// let trace = Contour::new(
89///     vec![0.0, 2.5, 5.0],
90///     vec![0.0, 7.5, 15.0],
91///     vec![
92///         vec![0.0, 5.0, 10.0],
93///         vec![5.0, 2.5, 0.0],
94///         vec![10.0, 5.0, 0.0],
95///     ],
96/// );
97///
98/// let expected = serde_json::json!({
99///     "type": "contour",
100///     "x": [0.0, 2.5, 5.0],
101///     "y": [0.0, 7.5, 15.0],
102///     "z": [[0.0, 5.0, 10.0], [5.0, 2.5, 0.0], [10.0, 5.0, 0.0]]
103/// });
104///
105/// assert_eq!(serde_json::to_value(trace).unwrap(), expected);
106/// ```
107#[serde_with::skip_serializing_none]
108#[derive(Serialize, Debug, Clone)]
109pub struct Contour<Z, X = f64, Y = f64>
110where
111    X: Serialize + Clone,
112    Y: Serialize + Clone,
113    Z: Serialize + Clone,
114{
115    r#type: PlotType,
116    name: Option<String>,
117    visible: Option<Visible>,
118    #[serde(rename = "showlegend")]
119    show_legend: Option<bool>,
120    #[serde(rename = "legendgroup")]
121    legend_group: Option<String>,
122    #[serde(rename = "legendgrouptitle")]
123    legend_group_title: Option<LegendGroupTitle>,
124    opacity: Option<f64>,
125    x: Option<Vec<X>>,
126    x0: Option<X>,
127    dx: Option<X>,
128    y: Option<Vec<Y>>,
129    y0: Option<Y>,
130    dy: Option<Y>,
131    z: Option<Vec<Z>>,
132    text: Option<Vec<String>>,
133    #[serde(rename = "hovertext")]
134    hover_text: Option<Vec<String>>,
135    #[serde(rename = "hoverinfo")]
136    hover_info: Option<HoverInfo>,
137    #[serde(rename = "hovertemplate")]
138    hover_template: Option<Dim<String>>,
139    #[serde(rename = "xaxis")]
140    x_axis: Option<String>,
141    #[serde(rename = "yaxis")]
142    y_axis: Option<String>,
143    line: Option<Line>,
144    #[serde(rename = "colorbar")]
145    color_bar: Option<ColorBar>,
146    #[serde(rename = "autocolorscale")]
147    auto_color_scale: Option<bool>,
148    #[serde(rename = "colorscale")]
149    color_scale: Option<ColorScale>,
150    #[serde(rename = "showscale")]
151    show_scale: Option<bool>,
152    #[serde(rename = "reversescale")]
153    reverse_scale: Option<bool>,
154    zauto: Option<bool>,
155    #[serde(rename = "zhoverformat")]
156    zhover_format: Option<String>,
157    zmax: Option<Z>,
158    zmid: Option<Z>,
159    zmin: Option<Z>,
160    #[serde(rename = "autocontour")]
161    auto_contour: Option<bool>,
162    #[serde(rename = "connectgaps")]
163    connect_gaps: Option<bool>,
164    contours: Option<Contours>,
165    #[serde(rename = "fillcolor")]
166    fill_color: Option<Box<dyn Color>>,
167    #[serde(rename = "hoverlabel")]
168    hover_label: Option<Label>,
169    #[serde(rename = "hoverongaps")]
170    hover_on_gaps: Option<bool>,
171    #[serde(rename = "ncontours")]
172    n_contours: Option<usize>,
173    transpose: Option<bool>,
174    #[serde(rename = "xcalendar")]
175    x_calendar: Option<Calendar>,
176    #[serde(rename = "ycalendar")]
177    y_calendar: Option<Calendar>,
178}
179
180impl<Z, X, Y> Default for Contour<Z, X, Y>
181where
182    X: Serialize + Clone,
183    Y: Serialize + Clone,
184    Z: Serialize + Clone,
185{
186    fn default() -> Self {
187        Self {
188            r#type: PlotType::Contour,
189            name: None,
190            visible: None,
191            show_legend: None,
192            legend_group: None,
193            legend_group_title: None,
194            opacity: None,
195            x: None,
196            x0: None,
197            dx: None,
198            y: None,
199            y0: None,
200            dy: None,
201            z: None,
202            text: None,
203            hover_text: None,
204            hover_info: None,
205            hover_template: None,
206            x_axis: None,
207            y_axis: None,
208            line: None,
209            color_bar: None,
210            auto_color_scale: None,
211            color_scale: None,
212            show_scale: None,
213            reverse_scale: None,
214            zauto: None,
215            zhover_format: None,
216            zmax: None,
217            zmid: None,
218            zmin: None,
219            auto_contour: None,
220            connect_gaps: None,
221            contours: None,
222            fill_color: None,
223            hover_label: None,
224            hover_on_gaps: None,
225            n_contours: None,
226            transpose: None,
227            x_calendar: None,
228            y_calendar: None,
229        }
230    }
231}
232
233impl<Z> Contour<Z, f64, f64>
234where
235    Z: Serialize + Clone,
236{
237    pub fn new_z(z: Vec<Z>) -> Box<Self> {
238        Box::new(Contour {
239            z: Some(z),
240            ..Default::default()
241        })
242    }
243}
244
245impl<Z, X, Y> Contour<Z, X, Y>
246where
247    X: Serialize + Clone,
248    Y: Serialize + Clone,
249    Z: Serialize + Clone,
250{
251    pub fn new(x: Vec<X>, y: Vec<Y>, z: Vec<Z>) -> Box<Self> {
252        Box::new(Contour {
253            x: Some(x),
254            y: Some(y),
255            z: Some(z),
256            ..Default::default()
257        })
258    }
259
260    pub fn auto_color_scale(mut self, auto_color_scale: bool) -> Box<Self> {
261        self.auto_color_scale = Some(auto_color_scale);
262        Box::new(self)
263    }
264
265    pub fn auto_contour(mut self, auto_contour: bool) -> Box<Self> {
266        self.auto_contour = Some(auto_contour);
267        Box::new(self)
268    }
269
270    pub fn color_bar(mut self, color_bar: ColorBar) -> Box<Self> {
271        self.color_bar = Some(color_bar);
272        Box::new(self)
273    }
274
275    pub fn color_scale(mut self, color_scale: ColorScale) -> Box<Self> {
276        self.color_scale = Some(color_scale);
277        Box::new(self)
278    }
279
280    pub fn connect_gaps(mut self, connect_gaps: bool) -> Box<Self> {
281        self.connect_gaps = Some(connect_gaps);
282        Box::new(self)
283    }
284
285    pub fn contours(mut self, contours: Contours) -> Box<Self> {
286        self.contours = Some(contours);
287        Box::new(self)
288    }
289
290    pub fn dx(mut self, dx: X) -> Box<Self> {
291        self.dx = Some(dx);
292        Box::new(self)
293    }
294
295    pub fn dy(mut self, dy: Y) -> Box<Self> {
296        self.dy = Some(dy);
297        Box::new(self)
298    }
299
300    pub fn fill_color<C: Color>(mut self, fill_color: C) -> Box<Self> {
301        self.fill_color = Some(Box::new(fill_color));
302        Box::new(self)
303    }
304
305    pub fn hover_info(mut self, hover_info: HoverInfo) -> Box<Self> {
306        self.hover_info = Some(hover_info);
307        Box::new(self)
308    }
309
310    pub fn hover_label(mut self, hover_label: Label) -> Box<Self> {
311        self.hover_label = Some(hover_label);
312        Box::new(self)
313    }
314
315    pub fn hover_on_gaps(mut self, hover_on_gaps: bool) -> Box<Self> {
316        self.hover_on_gaps = Some(hover_on_gaps);
317        Box::new(self)
318    }
319
320    pub fn hover_template(mut self, hover_template: &str) -> Box<Self> {
321        self.hover_template = Some(Dim::Scalar(hover_template.to_string()));
322        Box::new(self)
323    }
324
325    pub fn hover_template_array<S: AsRef<str>>(mut self, hover_template: Vec<S>) -> Box<Self> {
326        let hover_template = private::owned_string_vector(hover_template);
327        self.hover_template = Some(Dim::Vector(hover_template));
328        Box::new(self)
329    }
330
331    pub fn hover_text<S: AsRef<str>>(mut self, hover_text: Vec<S>) -> Box<Self> {
332        let hover_text = private::owned_string_vector(hover_text);
333        self.hover_text = Some(hover_text);
334        Box::new(self)
335    }
336
337    pub fn legend_group(mut self, legend_group: &str) -> Box<Self> {
338        self.legend_group = Some(legend_group.to_string());
339        Box::new(self)
340    }
341
342    pub fn legend_group_title(mut self, legend_group_title: LegendGroupTitle) -> Box<Self> {
343        self.legend_group_title = Some(legend_group_title);
344        Box::new(self)
345    }
346
347    pub fn line(mut self, line: Line) -> Box<Self> {
348        self.line = Some(line);
349        Box::new(self)
350    }
351
352    pub fn name(mut self, name: &str) -> Box<Self> {
353        self.name = Some(name.to_string());
354        Box::new(self)
355    }
356
357    pub fn n_contours(mut self, n_contours: usize) -> Box<Self> {
358        self.n_contours = Some(n_contours);
359        Box::new(self)
360    }
361
362    pub fn opacity(mut self, opacity: f64) -> Box<Self> {
363        self.opacity = Some(opacity);
364        Box::new(self)
365    }
366
367    pub fn reverse_scale(mut self, reverse_scale: bool) -> Box<Self> {
368        self.reverse_scale = Some(reverse_scale);
369        Box::new(self)
370    }
371
372    pub fn show_legend(mut self, show_legend: bool) -> Box<Self> {
373        self.show_legend = Some(show_legend);
374        Box::new(self)
375    }
376
377    pub fn show_scale(mut self, show_scale: bool) -> Box<Self> {
378        self.show_scale = Some(show_scale);
379        Box::new(self)
380    }
381
382    pub fn text<S: AsRef<str>>(mut self, text: Vec<S>) -> Box<Self> {
383        let text = private::owned_string_vector(text);
384        self.text = Some(text);
385        Box::new(self)
386    }
387
388    pub fn transpose(mut self, transpose: bool) -> Box<Self> {
389        self.transpose = Some(transpose);
390        Box::new(self)
391    }
392
393    pub fn visible(mut self, visible: Visible) -> Box<Self> {
394        self.visible = Some(visible);
395        Box::new(self)
396    }
397
398    pub fn x(mut self, x: Vec<X>) -> Box<Self> {
399        self.x = Some(x);
400        Box::new(self)
401    }
402
403    pub fn x_axis(mut self, axis: &str) -> Box<Self> {
404        self.x_axis = Some(axis.to_string());
405        Box::new(self)
406    }
407
408    pub fn x_calendar(mut self, x_calendar: Calendar) -> Box<Self> {
409        self.x_calendar = Some(x_calendar);
410        Box::new(self)
411    }
412
413    pub fn x0(mut self, x0: X) -> Box<Self> {
414        self.x0 = Some(x0);
415        Box::new(self)
416    }
417
418    pub fn y_axis(mut self, axis: &str) -> Box<Self> {
419        self.y_axis = Some(axis.to_string());
420        Box::new(self)
421    }
422
423    pub fn y(mut self, y: Vec<Y>) -> Box<Self> {
424        self.y = Some(y);
425        Box::new(self)
426    }
427
428    pub fn y_calendar(mut self, y_calendar: Calendar) -> Box<Self> {
429        self.y_calendar = Some(y_calendar);
430        Box::new(self)
431    }
432
433    pub fn y0(mut self, y0: Y) -> Box<Self> {
434        self.y0 = Some(y0);
435        Box::new(self)
436    }
437
438    pub fn zauto(mut self, zauto: bool) -> Box<Self> {
439        self.zauto = Some(zauto);
440        Box::new(self)
441    }
442
443    pub fn zhover_format(mut self, zhover_format: &str) -> Box<Self> {
444        self.zhover_format = Some(zhover_format.to_string());
445        Box::new(self)
446    }
447
448    pub fn zmax(mut self, zmax: Z) -> Box<Self> {
449        self.zmax = Some(zmax);
450        Box::new(self)
451    }
452
453    pub fn zmid(mut self, zmid: Z) -> Box<Self> {
454        self.zmid = Some(zmid);
455        Box::new(self)
456    }
457
458    pub fn zmin(mut self, zmin: Z) -> Box<Self> {
459        self.zmin = Some(zmin);
460        Box::new(self)
461    }
462}
463
464impl<X, Y, Z> Trace for Contour<X, Y, Z>
465where
466    X: Serialize + Clone,
467    Y: Serialize + Clone,
468    Z: Serialize + Clone,
469{
470    fn to_json(&self) -> String {
471        serde_json::to_string(self).unwrap()
472    }
473}
474
475#[cfg(test)]
476mod tests {
477    use serde_json::{json, to_value};
478
479    use super::*;
480    use crate::common::ColorScalePalette;
481
482    #[test]
483    #[rustfmt::skip]
484    fn test_serialize_contours_type() {
485        assert_eq!(to_value(ContoursType::Levels).unwrap(), json!("levels"));
486        assert_eq!(to_value(ContoursType::Constraint).unwrap(), json!("constraint"));
487    }
488
489    #[test]
490    fn test_serialize_coloring() {
491        assert_eq!(to_value(Coloring::Fill).unwrap(), json!("fill"));
492        assert_eq!(to_value(Coloring::HeatMap).unwrap(), json!("heatmap"));
493        assert_eq!(to_value(Coloring::Lines).unwrap(), json!("lines"));
494        assert_eq!(to_value(Coloring::None).unwrap(), json!("none"));
495    }
496
497    #[test]
498    #[rustfmt::skip]
499    fn test_serialize_operation() {
500        assert_eq!(to_value(Operation::Equals).unwrap(), json!("="));
501        assert_eq!(to_value(Operation::LessThan).unwrap(), json!("<"));
502        assert_eq!(to_value(Operation::LessThanOrEqual).unwrap(), json!("<="));
503        assert_eq!(to_value(Operation::GreaterThan).unwrap(), json!(">"));
504        assert_eq!(to_value(Operation::GreaterThanOrEqual).unwrap(), json!(">="));
505        assert_eq!(to_value(Operation::Inside).unwrap(), json!("[]"));
506        assert_eq!(to_value(Operation::Outside).unwrap(), json!("]["));
507    }
508
509    #[test]
510    fn test_serialize_default_contours() {
511        let contours = Contours::new();
512        let expected = json!({});
513
514        assert_eq!(to_value(contours).unwrap(), expected);
515    }
516    #[test]
517    fn test_serialize_contours() {
518        let contours = Contours::new()
519            .type_(ContoursType::Levels)
520            .start(0.0)
521            .end(10.0)
522            .size(5)
523            .coloring(Coloring::HeatMap)
524            .show_lines(true)
525            .show_labels(false)
526            .label_font(Font::new())
527            .label_format("fmt")
528            .operation(Operation::GreaterThan)
529            .value(6.0);
530
531        let expected = json!({
532            "type": "levels",
533            "start": 0.0,
534            "end": 10.0,
535            "size": 5,
536            "coloring": "heatmap",
537            "showlines": true,
538            "showlabels": false,
539            "labelformat": "fmt",
540            "labelfont": {},
541            "operation": ">",
542            "value": 6.0
543        });
544
545        assert_eq!(to_value(contours).unwrap(), expected)
546    }
547
548    #[test]
549    fn test_serialize_default_contour() {
550        let trace: Contour<f64, f64, f64> = Contour::default();
551        let expected = json!({"type": "contour"}).to_string();
552
553        assert_eq!(trace.to_json(), expected);
554    }
555
556    #[test]
557    fn test_new_z_contour() {
558        let trace = Contour::new_z(vec![1.0]);
559        let expected = json!({
560            "type": "contour",
561            "z": [1.0]
562        });
563
564        assert_eq!(to_value(trace).unwrap(), expected);
565    }
566
567    #[test]
568    fn test_serialize_contour() {
569        let trace = Contour::new(vec![0., 1.], vec![2., 3.], vec![4., 5.])
570            .auto_color_scale(true)
571            .auto_contour(true)
572            .color_bar(ColorBar::new())
573            .color_scale(ColorScale::Palette(ColorScalePalette::Blackbody))
574            .connect_gaps(true)
575            .contours(Contours::new())
576            .dx(1.)
577            .dy(1.)
578            .fill_color("#123456")
579            .hover_info(HoverInfo::XAndYAndZ)
580            .hover_label(Label::new())
581            .hover_on_gaps(false)
582            .hover_template("ok {1}")
583            .hover_template_array(vec!["ok {1}", "ok {2}"])
584            .hover_text(vec!["p3", "p4"])
585            .legend_group("group_1")
586            .legend_group_title(LegendGroupTitle::new("Legend Group Title"))
587            .line(Line::new())
588            .n_contours(5)
589            .name("contour trace")
590            .opacity(0.6)
591            .reverse_scale(true)
592            .show_scale(true)
593            .show_legend(false)
594            .text(vec!["p1", "p2"])
595            .transpose(true)
596            .visible(Visible::True)
597            .x(vec![0.0, 1.0])
598            .x_axis("x0")
599            .x_calendar(Calendar::Ethiopian)
600            .x0(0.)
601            .y(vec![2.0, 3.0])
602            .y_axis("y0")
603            .y_calendar(Calendar::Gregorian)
604            .y0(0.)
605            .zauto(false)
606            .zhover_format("fmt")
607            .zmax(10.)
608            .zmid(5.)
609            .zmin(0.);
610
611        let expected = json!({
612            "type": "contour",
613            "x": [0.0, 1.0],
614            "y": [2.0, 3.0],
615            "z": [4.0, 5.0],
616            "x0": 0.0,
617            "dx": 1.0,
618            "y0": 0.0,
619            "dy": 1.0,
620            "name": "contour trace",
621            "visible": true,
622            "showlegend": false,
623            "legendgroup": "group_1",
624            "legendgrouptitle": {"text": "Legend Group Title"},
625            "opacity": 0.6,
626            "text": ["p1", "p2"],
627            "hovertext": ["p3", "p4"],
628            "hoverinfo": "x+y+z",
629            "hovertemplate": ["ok {1}", "ok {2}"],
630            "xaxis": "x0",
631            "yaxis": "y0",
632            "line": {},
633            "colorbar": {},
634            "autocolorscale": true,
635            "colorscale": "Blackbody",
636            "showscale": true,
637            "reversescale": true,
638            "zauto": false,
639            "zhoverformat": "fmt",
640            "zmax": 10.0,
641            "zmid": 5.0,
642            "zmin": 0.0,
643            "autocontour": true,
644            "connectgaps": true,
645            "contours": {},
646            "fillcolor": "#123456",
647            "hoverlabel": {},
648            "hoverongaps": false,
649            "ncontours": 5,
650            "transpose": true,
651            "xcalendar": "ethiopian",
652            "ycalendar": "gregorian"
653        });
654
655        assert_eq!(to_value(trace).unwrap(), expected);
656    }
657}