plotly_fork/traces/
heat_map.rs

1//! Heat map trace
2
3use plotly_derive::FieldSetter;
4use serde::Serialize;
5
6use crate::{
7    common::{
8        Calendar, ColorBar, ColorScale, Dim, HoverInfo, Label, LegendGroupTitle, PlotType, Visible,
9    },
10    Trace,
11};
12
13#[derive(Debug, Clone)]
14pub enum Smoothing {
15    Fast,
16    Best,
17    False,
18}
19
20impl Serialize for Smoothing {
21    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
22    where
23        S: serde::Serializer,
24    {
25        match *self {
26            Self::Fast => serializer.serialize_str("fast"),
27            Self::Best => serializer.serialize_str("best"),
28            Self::False => serializer.serialize_bool(false),
29        }
30    }
31}
32
33/// Construct a heat map trace.
34///
35/// # Examples
36///
37/// ```
38/// use plotly::HeatMap;
39///
40/// let trace = HeatMap::new(
41///     vec![0.0, 1.0],
42///     vec![2.0, 3.0],
43///     vec![vec![0.25, 0.75], vec![0.0, 0.5]]
44/// );
45///
46/// let expected = serde_json::json!({
47///     "type": "heatmap",
48///     "x": [0.0, 1.0],
49///     "y": [2.0, 3.0],
50///     "z": [[0.25, 0.75], [0.0, 0.5]]
51/// });
52///
53/// assert_eq!(serde_json::to_value(trace).unwrap(), expected);
54/// ```
55#[serde_with::skip_serializing_none]
56#[derive(Serialize, Debug, Clone, FieldSetter)]
57#[field_setter(box_self, kind = "trace")]
58pub struct HeatMap<X, Y, Z>
59where
60    X: Serialize + Clone,
61    Y: Serialize + Clone,
62    Z: Serialize + Clone,
63{
64    #[field_setter(default = "PlotType::HeatMap")]
65    r#type: PlotType,
66    #[serde(rename = "autocolorscale")]
67    auto_color_scale: Option<bool>,
68    #[serde(rename = "colorbar")]
69    color_bar: Option<ColorBar>,
70    #[serde(rename = "colorscale")]
71    color_scale: Option<ColorScale>,
72    #[serde(rename = "connectgaps")]
73    connect_gaps: Option<bool>,
74    #[serde(rename = "hoverinfo")]
75    hover_info: Option<HoverInfo>,
76    #[serde(rename = "hoverlabel")]
77    hover_label: Option<Label>,
78    #[serde(rename = "hoverongaps")]
79    hover_on_gaps: Option<bool>,
80    #[serde(rename = "hovertemplate")]
81    hover_template: Option<Dim<String>>,
82    #[serde(rename = "hovertext")]
83    hover_text: Option<Vec<String>>,
84    #[serde(rename = "legendgroup")]
85    legend_group: Option<String>,
86    #[serde(rename = "legendgrouptitle")]
87    legend_group_title: Option<LegendGroupTitle>,
88    name: Option<String>,
89    opacity: Option<f64>,
90    #[serde(rename = "reversescale")]
91    reverse_scale: Option<bool>,
92    #[serde(rename = "showlegend")]
93    show_legend: Option<bool>,
94    #[serde(rename = "showscale")]
95    show_scale: Option<bool>,
96    text: Option<Vec<String>>,
97    transpose: Option<bool>,
98    visible: Option<Visible>,
99    x: Option<Vec<X>>,
100    #[serde(rename = "xaxis")]
101    x_axis: Option<String>,
102    #[serde(rename = "xcalendar")]
103    x_calendar: Option<Calendar>,
104    y: Option<Vec<Y>>,
105    #[serde(rename = "yaxis")]
106    y_axis: Option<String>,
107    #[serde(rename = "ycalendar")]
108    y_calendar: Option<Calendar>,
109    z: Option<Vec<Z>>,
110    zauto: Option<bool>,
111    #[serde(rename = "zhoverformat")]
112    zhover_format: Option<String>,
113    zmax: Option<Z>,
114    zmid: Option<Z>,
115    zmin: Option<Z>,
116    zsmooth: Option<Smoothing>,
117}
118
119impl<Z> HeatMap<f64, f64, Z>
120where
121    Z: Serialize + Clone,
122{
123    pub fn new_z(z: Vec<Z>) -> Box<Self> {
124        Box::new(Self {
125            z: Some(z),
126            ..Default::default()
127        })
128    }
129}
130
131impl<X, Y, Z> HeatMap<X, Y, Z>
132where
133    X: Serialize + Clone,
134    Y: Serialize + Clone,
135    Z: Serialize + Clone,
136{
137    pub fn new(x: Vec<X>, y: Vec<Y>, z: Vec<Z>) -> Box<Self> {
138        Box::new(Self {
139            x: Some(x),
140            y: Some(y),
141            z: Some(z),
142            ..Default::default()
143        })
144    }
145}
146
147impl<X, Y, Z> Trace for HeatMap<X, Y, Z>
148where
149    X: Serialize + Clone,
150    Y: Serialize + Clone,
151    Z: Serialize + Clone,
152{
153    fn to_json(&self) -> String {
154        serde_json::to_string(self).unwrap()
155    }
156}
157
158#[cfg(test)]
159mod tests {
160    use serde_json::{json, to_value};
161
162    use super::*;
163    use crate::common::ColorScalePalette;
164
165    #[test]
166    fn test_serialize_smoothing() {
167        assert_eq!(to_value(Smoothing::Fast).unwrap(), json!("fast"));
168        assert_eq!(to_value(Smoothing::Best).unwrap(), json!("best"));
169        assert_eq!(to_value(Smoothing::False).unwrap(), json!(false));
170    }
171
172    #[test]
173    fn test_serialize_default_heat_map() {
174        let trace = HeatMap::<f64, f64, f64>::default();
175        let expected = json!({"type": "heatmap"}).to_string();
176
177        assert_eq!(trace.to_json(), expected);
178    }
179
180    #[test]
181    fn test_serialize_heat_map_z() {
182        let trace = HeatMap::new_z(vec![1.0]);
183        let expected = json!({
184            "type": "heatmap",
185            "z": [1.0],
186        });
187
188        assert_eq!(to_value(trace).unwrap(), expected);
189    }
190
191    #[test]
192    fn test_serialize_heat_map() {
193        let trace = HeatMap::new(vec![0.0, 1.0], vec![2.0, 3.0], vec![4.0, 5.0])
194            .auto_color_scale(true)
195            .color_bar(ColorBar::new())
196            .color_scale(ColorScale::Palette(ColorScalePalette::Picnic))
197            .connect_gaps(false)
198            .hover_info(HoverInfo::None)
199            .hover_label(Label::new())
200            .hover_on_gaps(true)
201            .hover_template("tmpl")
202            .hover_template_array(vec!["tmpl1", "tmpl2"])
203            .hover_text(vec!["hov", "er"])
204            .legend_group("1")
205            .legend_group_title(LegendGroupTitle::new("Legend Group Title"))
206            .name("name")
207            .opacity(0.99)
208            .reverse_scale(false)
209            .show_legend(true)
210            .show_scale(false)
211            .text(vec!["te", "xt"])
212            .transpose(true)
213            .visible(Visible::LegendOnly)
214            .x_axis("x")
215            .x_calendar(Calendar::Hebrew)
216            .y_axis("y")
217            .y_calendar(Calendar::Islamic)
218            .zauto(true)
219            .zhover_format("fmt")
220            .zmax(10.0)
221            .zmid(5.0)
222            .zmin(0.0)
223            .zsmooth(Smoothing::Fast);
224
225        let expected = json!({
226            "type": "heatmap",
227            "autocolorscale": true,
228            "colorbar": {},
229            "colorscale": "Picnic",
230            "connectgaps": false,
231            "hoverinfo": "none",
232            "hoverlabel": {},
233            "hoverongaps": true,
234            "hovertemplate": ["tmpl1", "tmpl2"],
235            "hovertext": ["hov", "er"],
236            "legendgroup": "1",
237            "legendgrouptitle": {"text": "Legend Group Title"},
238            "name": "name",
239            "opacity": 0.99,
240            "reversescale": false,
241            "showlegend": true,
242            "showscale": false,
243            "text": ["te", "xt"],
244            "transpose": true,
245            "visible": "legendonly",
246            "x": [0.0, 1.0],
247            "xcalendar": "hebrew",
248            "xaxis": "x",
249            "y": [2.0, 3.0],
250            "yaxis": "y",
251            "ycalendar": "islamic",
252            "z": [4.0, 5.0],
253            "zauto": true,
254            "zhoverformat": "fmt",
255            "zmax": 10.0,
256            "zmid": 5.0,
257            "zmin": 0.0,
258            "zsmooth": "fast"
259        });
260
261        assert_eq!(to_value(trace).unwrap(), expected);
262    }
263}