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