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