plotly/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<f64>,
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(
343        mut self,
344        legend_group_title: impl Into<LegendGroupTitle>,
345    ) -> Box<Self> {
346        self.legend_group_title = Some(legend_group_title.into());
347        Box::new(self)
348    }
349
350    pub fn line(mut self, line: Line) -> Box<Self> {
351        self.line = Some(line);
352        Box::new(self)
353    }
354
355    pub fn name(mut self, name: &str) -> Box<Self> {
356        self.name = Some(name.to_string());
357        Box::new(self)
358    }
359
360    pub fn n_contours(mut self, n_contours: usize) -> Box<Self> {
361        self.n_contours = Some(n_contours);
362        Box::new(self)
363    }
364
365    pub fn opacity(mut self, opacity: f64) -> Box<Self> {
366        self.opacity = Some(opacity);
367        Box::new(self)
368    }
369
370    pub fn reverse_scale(mut self, reverse_scale: bool) -> Box<Self> {
371        self.reverse_scale = Some(reverse_scale);
372        Box::new(self)
373    }
374
375    pub fn show_legend(mut self, show_legend: bool) -> Box<Self> {
376        self.show_legend = Some(show_legend);
377        Box::new(self)
378    }
379
380    pub fn show_scale(mut self, show_scale: bool) -> Box<Self> {
381        self.show_scale = Some(show_scale);
382        Box::new(self)
383    }
384
385    pub fn text<S: AsRef<str>>(mut self, text: Vec<S>) -> Box<Self> {
386        let text = private::owned_string_vector(text);
387        self.text = Some(text);
388        Box::new(self)
389    }
390
391    pub fn transpose(mut self, transpose: bool) -> Box<Self> {
392        self.transpose = Some(transpose);
393        Box::new(self)
394    }
395
396    pub fn visible(mut self, visible: Visible) -> Box<Self> {
397        self.visible = Some(visible);
398        Box::new(self)
399    }
400
401    pub fn x(mut self, x: Vec<X>) -> Box<Self> {
402        self.x = Some(x);
403        Box::new(self)
404    }
405
406    pub fn x_axis(mut self, axis: &str) -> Box<Self> {
407        self.x_axis = Some(axis.to_string());
408        Box::new(self)
409    }
410
411    pub fn x_calendar(mut self, x_calendar: Calendar) -> Box<Self> {
412        self.x_calendar = Some(x_calendar);
413        Box::new(self)
414    }
415
416    pub fn x0(mut self, x0: X) -> Box<Self> {
417        self.x0 = Some(x0);
418        Box::new(self)
419    }
420
421    pub fn y_axis(mut self, axis: &str) -> Box<Self> {
422        self.y_axis = Some(axis.to_string());
423        Box::new(self)
424    }
425
426    pub fn y(mut self, y: Vec<Y>) -> Box<Self> {
427        self.y = Some(y);
428        Box::new(self)
429    }
430
431    pub fn y_calendar(mut self, y_calendar: Calendar) -> Box<Self> {
432        self.y_calendar = Some(y_calendar);
433        Box::new(self)
434    }
435
436    pub fn y0(mut self, y0: Y) -> Box<Self> {
437        self.y0 = Some(y0);
438        Box::new(self)
439    }
440
441    pub fn zauto(mut self, zauto: bool) -> Box<Self> {
442        self.zauto = Some(zauto);
443        Box::new(self)
444    }
445
446    pub fn zhover_format(mut self, zhover_format: &str) -> Box<Self> {
447        self.zhover_format = Some(zhover_format.to_string());
448        Box::new(self)
449    }
450
451    pub fn zmax(mut self, zmax: Z) -> Box<Self> {
452        self.zmax = Some(zmax);
453        Box::new(self)
454    }
455
456    pub fn zmid(mut self, zmid: Z) -> Box<Self> {
457        self.zmid = Some(zmid);
458        Box::new(self)
459    }
460
461    pub fn zmin(mut self, zmin: Z) -> Box<Self> {
462        self.zmin = Some(zmin);
463        Box::new(self)
464    }
465}
466
467impl<X, Y, Z> Trace for Contour<X, Y, Z>
468where
469    X: Serialize + Clone,
470    Y: Serialize + Clone,
471    Z: Serialize + Clone,
472{
473    fn to_json(&self) -> String {
474        serde_json::to_string(self).unwrap()
475    }
476}
477
478#[cfg(test)]
479mod tests {
480    use serde_json::{json, to_value};
481
482    use super::*;
483    use crate::common::ColorScalePalette;
484
485    #[test]
486    #[rustfmt::skip]
487    fn serialize_contours_type() {
488        assert_eq!(to_value(ContoursType::Levels).unwrap(), json!("levels"));
489        assert_eq!(to_value(ContoursType::Constraint).unwrap(), json!("constraint"));
490    }
491
492    #[test]
493    fn serialize_coloring() {
494        assert_eq!(to_value(Coloring::Fill).unwrap(), json!("fill"));
495        assert_eq!(to_value(Coloring::HeatMap).unwrap(), json!("heatmap"));
496        assert_eq!(to_value(Coloring::Lines).unwrap(), json!("lines"));
497        assert_eq!(to_value(Coloring::None).unwrap(), json!("none"));
498    }
499
500    #[test]
501    #[rustfmt::skip]
502    fn serialize_operation() {
503        assert_eq!(to_value(Operation::Equals).unwrap(), json!("="));
504        assert_eq!(to_value(Operation::LessThan).unwrap(), json!("<"));
505        assert_eq!(to_value(Operation::LessThanOrEqual).unwrap(), json!("<="));
506        assert_eq!(to_value(Operation::GreaterThan).unwrap(), json!(">"));
507        assert_eq!(to_value(Operation::GreaterThanOrEqual).unwrap(), json!(">="));
508        assert_eq!(to_value(Operation::Inside).unwrap(), json!("[]"));
509        assert_eq!(to_value(Operation::Outside).unwrap(), json!("]["));
510    }
511
512    #[test]
513    fn serialize_default_contours() {
514        let contours = Contours::new();
515        let expected = json!({});
516
517        assert_eq!(to_value(contours).unwrap(), expected);
518    }
519    #[test]
520    fn serialize_contours() {
521        let contours = Contours::new()
522            .type_(ContoursType::Levels)
523            .start(0.0)
524            .end(10.0)
525            .size(5.0)
526            .coloring(Coloring::HeatMap)
527            .show_lines(true)
528            .show_labels(false)
529            .label_font(Font::new())
530            .label_format("fmt")
531            .operation(Operation::GreaterThan)
532            .value(6.0);
533
534        let expected = json!({
535            "type": "levels",
536            "start": 0.0,
537            "end": 10.0,
538            "size": 5.0,
539            "coloring": "heatmap",
540            "showlines": true,
541            "showlabels": false,
542            "labelformat": "fmt",
543            "labelfont": {},
544            "operation": ">",
545            "value": 6.0
546        });
547
548        assert_eq!(to_value(contours).unwrap(), expected)
549    }
550
551    #[test]
552    fn serialize_default_contour() {
553        let trace: Contour<f64, f64, f64> = Contour::default();
554        let expected = json!({"type": "contour"}).to_string();
555
556        assert_eq!(trace.to_json(), expected);
557    }
558
559    #[test]
560    fn new_z_contour() {
561        let trace = Contour::new_z(vec![1.0]);
562        let expected = json!({
563            "type": "contour",
564            "z": [1.0]
565        });
566
567        assert_eq!(to_value(trace).unwrap(), expected);
568    }
569
570    #[test]
571    fn serialize_contour() {
572        let trace = Contour::new(vec![0., 1.], vec![2., 3.], vec![4., 5.])
573            .auto_color_scale(true)
574            .auto_contour(true)
575            .color_bar(ColorBar::new())
576            .color_scale(ColorScale::Palette(ColorScalePalette::Blackbody))
577            .connect_gaps(true)
578            .contours(Contours::new())
579            .dx(1.)
580            .dy(1.)
581            .fill_color("#123456")
582            .hover_info(HoverInfo::XAndYAndZ)
583            .hover_label(Label::new())
584            .hover_on_gaps(false)
585            .hover_template("ok {1}")
586            .hover_template_array(vec!["ok {1}", "ok {2}"])
587            .hover_text(vec!["p3", "p4"])
588            .legend_group("group_1")
589            .legend_group_title("Legend Group Title")
590            .line(Line::new())
591            .n_contours(5)
592            .name("contour trace")
593            .opacity(0.6)
594            .reverse_scale(true)
595            .show_scale(true)
596            .show_legend(false)
597            .text(vec!["p1", "p2"])
598            .transpose(true)
599            .visible(Visible::True)
600            .x(vec![0.0, 1.0])
601            .x_axis("x0")
602            .x_calendar(Calendar::Ethiopian)
603            .x0(0.)
604            .y(vec![2.0, 3.0])
605            .y_axis("y0")
606            .y_calendar(Calendar::Gregorian)
607            .y0(0.)
608            .zauto(false)
609            .zhover_format("fmt")
610            .zmax(10.)
611            .zmid(5.)
612            .zmin(0.);
613
614        let expected = json!({
615            "type": "contour",
616            "x": [0.0, 1.0],
617            "y": [2.0, 3.0],
618            "z": [4.0, 5.0],
619            "x0": 0.0,
620            "dx": 1.0,
621            "y0": 0.0,
622            "dy": 1.0,
623            "name": "contour trace",
624            "visible": true,
625            "showlegend": false,
626            "legendgroup": "group_1",
627            "legendgrouptitle": {"text": "Legend Group Title"},
628            "opacity": 0.6,
629            "text": ["p1", "p2"],
630            "hovertext": ["p3", "p4"],
631            "hoverinfo": "x+y+z",
632            "hovertemplate": ["ok {1}", "ok {2}"],
633            "xaxis": "x0",
634            "yaxis": "y0",
635            "line": {},
636            "colorbar": {},
637            "autocolorscale": true,
638            "colorscale": "Blackbody",
639            "showscale": true,
640            "reversescale": true,
641            "zauto": false,
642            "zhoverformat": "fmt",
643            "zmax": 10.0,
644            "zmid": 5.0,
645            "zmin": 0.0,
646            "autocontour": true,
647            "connectgaps": true,
648            "contours": {},
649            "fillcolor": "#123456",
650            "hoverlabel": {},
651            "hoverongaps": false,
652            "ncontours": 5,
653            "transpose": true,
654            "xcalendar": "ethiopian",
655            "ycalendar": "gregorian"
656        });
657
658        assert_eq!(to_value(trace).unwrap(), expected);
659    }
660}