1use 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#[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 #[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}