plotly_fork/traces/scatter_polar.rs
1//! Polar scatter trace
2
3#[cfg(feature = "plotly_ndarray")]
4use ndarray::{Array, Ix1, Ix2};
5use plotly_derive::FieldSetter;
6use serde::Serialize;
7
8#[cfg(feature = "plotly_ndarray")]
9use crate::ndarray::ArrayTraces;
10use crate::{
11 color::Color,
12 common::{
13 Dim, Fill, Font, HoverInfo, HoverOn, Label, LegendGroupTitle, Line, Marker, Mode, PlotType,
14 Position, Visible,
15 },
16 private::{NumOrString, NumOrStringCollection},
17 Trace,
18};
19
20/// Construct a polar scatter trace.
21///
22/// # Examples
23///
24/// ```
25/// use plotly::ScatterPolar;
26///
27/// let trace = ScatterPolar::new(vec![0, 1, 2], vec![2, 1, 0]);
28///
29/// let expected = serde_json::json!({
30/// "type": "scatterpolar",
31/// "theta": [0, 1, 2],
32/// "r": [2, 1, 0]
33/// });
34///
35/// assert_eq!(serde_json::to_value(trace).unwrap(), expected);
36/// ```
37#[serde_with::skip_serializing_none]
38#[derive(Serialize, Clone, Debug, FieldSetter)]
39#[field_setter(box_self, kind = "trace")]
40pub struct ScatterPolar<Theta, R>
41where
42 Theta: Serialize + Clone + 'static,
43 R: Serialize + Clone + 'static,
44{
45 #[field_setter(default = "PlotType::ScatterPolar")]
46 r#type: PlotType,
47 /// Sets the trace name. The trace name appear as the legend item and on
48 /// hover.
49 name: Option<String>,
50 /// Determines whether or not this trace is visible. If
51 /// `Visible::LegendOnly`, the trace is not drawn, but can appear as a
52 /// legend item (provided that the legend itself is visible).
53 visible: Option<Visible>,
54 /// Determines whether or not an item corresponding to this trace is shown
55 /// in the legend.
56 #[serde(rename = "showlegend")]
57 show_legend: Option<bool>,
58 /// Sets the legend group for this trace. Traces part of the same legend
59 /// group hide/show at the same time when toggling legend items.
60 #[serde(rename = "legendgroup")]
61 legend_group: Option<String>,
62 #[serde(rename = "legendgrouptitle")]
63 legend_group_title: Option<LegendGroupTitle>,
64 /// Sets the opacity of the trace.
65 opacity: Option<f64>,
66 /// Determines the drawing mode for this scatter trace. If the provided
67 /// `Mode` includes "Text" then the `text` elements appear at the
68 /// coordinates. Otherwise, the `text` elements appear on hover. If
69 /// there are less than 20 points and the trace is not stacked then the
70 /// default is `Mode::LinesMarkers`, otherwise it is `Mode::Lines`.
71 mode: Option<Mode>,
72 /// Assigns id labels to each datum. These ids for object constancy of data
73 /// points during animation. Should be an array of strings, not numbers
74 /// or any other type.
75 ids: Option<Vec<String>>,
76 /// Sets the theta coordinate step. See `theta0` for more info.
77 theta: Option<Vec<Theta>>,
78 /// Alternate to `theta`. Builds a linear space of theta coordinates. Use
79 /// with `dtheta` where `theta0` is the starting coordinate and `dtheta`
80 /// the step.
81 theta0: Option<NumOrString>,
82 dtheta: Option<f64>,
83
84 r: Option<Vec<R>>,
85 /// Alternate to `r`. Builds a linear space of r coordinates. Use with `dr`
86 /// where `r0` is the starting coordinate and `dr` the step.
87 r0: Option<NumOrString>,
88 /// Sets the r coordinate step. See `r0` for more info.
89 dr: Option<f64>,
90
91 /// Sets a reference between this trace's data coordinates and a polar
92 /// subplot. If "polar" (the default value), the data refer to
93 /// `layout.polar`. If "polar2", the data refer to `layout.polar2`, and
94 /// so on.
95 subplot: Option<String>,
96 /// Sets text elements associated with each (x,y) pair. If a single string,
97 /// the same string appears over all the data points. If an array of
98 /// string, the items are mapped in order to the this trace's (x,y)
99 /// coordinates. If trace `HoverInfo` contains a "text" flag and
100 /// `hover_text` is not set, these elements will be seen in the hover
101 /// labels.
102 text: Option<Dim<String>>,
103 /// Sets the positions of the `text` elements with respects to the (x,y)
104 /// coordinates.
105 #[serde(rename = "textposition")]
106 text_position: Option<Dim<Position>>,
107 /// Template string used for rendering the information text that appear on
108 /// points. Note that this will override `textinfo`. Variables are
109 /// inserted using %{variable}, for example "y: %{y}". Numbers are
110 /// formatted using d3-format's syntax %{variable:d3-format}, for example "Price: %{y:$.2f}". See [format](https://github.com/d3/d3-3.x-api-reference/blob/master/Formatting.md#d3)
111 /// for details on the formatting syntax. Dates are formatted using
112 /// d3-time-format's syntax %{variable|d3-time-format}, for example
113 /// "Day: %{2019-01-01|%A}". See [format](https://github.com/d3/d3-3.x-api-reference/blob/master/Time-Formatting.md#format) for details
114 /// on the date formatting syntax. Every attributes that can be specified
115 /// per-point (the ones that are `arrayOk: true`) are available.
116 #[serde(rename = "texttemplate")]
117 text_template: Option<Dim<String>>,
118 /// Sets hover text elements associated with each (x,y) pair. If a single
119 /// string, the same string appears over all the data points. If an
120 /// array of string, the items are mapped in order to the this trace's
121 /// (x,y) coordinates. To be seen, trace `HoverInfo` must contain a
122 /// "Text" flag.
123 #[serde(rename = "hovertext")]
124 hover_text: Option<Dim<String>>,
125 /// Determines which trace information appear on hover. If `HoverInfo::None`
126 /// or `HoverInfo::Skip` are set, no information is displayed upon
127 /// hovering. But, if `HoverInfo::None` is set, click and hover events
128 /// are still fired.
129 #[serde(rename = "hoverinfo")]
130 hover_info: Option<HoverInfo>,
131 /// Template string used for rendering the information that appear on hover
132 /// box. Note that this will override `HoverInfo`. Variables are
133 /// inserted using %{variable}, for example "y: %{y}". Numbers are
134 /// formatted using d3-format's syntax %{variable:d3-format}, for example
135 /// "Price: %{y:$.2f}".
136 /// https://github.com/d3/d3-3.x-api-reference/blob/master/Formatting.md#d3_format for details
137 /// on the formatting syntax. Dates are formatted using d3-time-format's
138 /// syntax %{variable|d3-time-format}, for example "Day:
139 /// %{2019-01-01|%A}". https://github.com/d3/d3-3.x-api-reference/blob/master/Time-Formatting.md#format for details
140 /// on the date formatting syntax. The variables available in
141 /// `hovertemplate` are the ones emitted as event data described at this link https://plotly.com/javascript/plotlyjs-events/#event-data.
142 /// Additionally, every attributes that can be specified per-point (the ones
143 /// that are `arrayOk: true`) are available. Anything contained in tag
144 /// `<extra>` is displayed in the secondary box, for example
145 /// "<extra>{fullData.name}</extra>". To hide the secondary box
146 /// completely, use an empty tag `<extra></extra>`.
147 #[serde(rename = "hovertemplate")]
148 hover_template: Option<Dim<String>>,
149 /// Assigns extra meta information associated with this trace that can be
150 /// used in various text attributes. Attributes such as trace `name`,
151 /// graph, axis and colorbar `title.text`, annotation `text`
152 /// `rangeselector`, `updatemenues` and `sliders` `label` text all support
153 /// `meta`. To access the trace `meta` values in an attribute in the same
154 /// trace, simply use `%{meta[i]}` where `i` is the index or key of the
155 /// `meta` item in question. To access trace `meta` in layout
156 /// attributes, use `%{data[n[.meta[i]}` where `i` is the index or key of
157 /// the `meta` and `n` is the trace index.
158 meta: Option<NumOrString>,
159 /// Assigns extra data each datum. This may be useful when listening to
160 /// hover, click and selection events. Note that, "scatter" traces also
161 /// appends customdata items in the markers DOM elements
162 #[serde(rename = "customdata")]
163 custom_data: Option<NumOrStringCollection>,
164 /// Array containing integer indices of selected points. Has an effect only
165 /// for traces that support selections. Note that an empty array means
166 /// an empty selection where the `unselected` are turned on for all
167 /// points, whereas, any other non-array values means no selection all
168 /// where the `selected` and `unselected` styles have no effect.
169 #[serde(rename = "selectedpoints")]
170 selected_points: Option<Vec<u32>>,
171 /// Determines how points are displayed and joined.
172 marker: Option<Marker>,
173 /// Line display properties.
174 line: Option<Line>,
175 /// Sets the text font.
176 #[serde(rename = "textfont")]
177 text_font: Option<Font>,
178 /// Determines whether or not markers and text nodes are clipped about the
179 /// subplot axes. To show markers and text nodes above axis lines and
180 /// tick labels, make sure to set `xaxis.layer` and `yaxis.layer` to
181 /// "below traces".
182 #[serde(rename = "cliponaxis")]
183 clip_on_axis: Option<bool>,
184 /// Determines whether or not gaps (i.e. {nan} or missing values) in the
185 /// provided data arrays are connected.
186 #[serde(rename = "connectgaps")]
187 connect_gaps: Option<bool>,
188 /// Sets the area to fill with a solid color. Defaults to "none" unless this
189 /// trace is stacked, then it gets "tonexty" ("tonextx") if
190 /// `orientation` is "v" ("h") Use with `fillcolor` if not
191 /// "none". "tozerox" and "tozeroy" fill to x=0 and y=0 respectively.
192 /// "tonextx" and "tonexty" fill between the endpoints of this trace and
193 /// the endpoints of the trace before it, connecting those endpoints
194 /// with straight lines (to make a stacked area graph); if there is
195 /// no trace before it, they behave like "tozerox" and "tozeroy". "toself"
196 /// connects the endpoints of the trace (or each segment of the trace if
197 /// it has gaps) into a closed shape. "tonext" fills the space between
198 /// two traces if one completely encloses the other (eg consecutive
199 /// contour lines), and behaves like "toself" if there is no trace before
200 /// it. "tonext" should not be used if one trace does not enclose the
201 /// other. Traces in a `stackgroup` will only fill to (or be filled to)
202 /// other traces in the same group. With multiple `stackgroup`s or some
203 /// traces stacked and some not, if fill-linked traces are not
204 /// already consecutive, the later ones will be pushed down in the drawing
205 /// order.
206 fill: Option<Fill>,
207 /// Sets the fill color. Defaults to a half-transparent variant of the line
208 /// color, marker color, or marker line color, whichever is available.
209 #[serde(rename = "fillcolor")]
210 fill_color: Option<Box<dyn Color>>,
211 /// Properties of label displayed on mouse hover.
212 #[serde(rename = "hoverlabel")]
213 hover_label: Option<Label>,
214 /// Do the hover effects highlight individual points (markers or line
215 /// points) or do they highlight filled regions? If the fill is "toself"
216 /// or "tonext" and there are no markers or text, then the default is
217 /// "fills", otherwise it is "points".
218 #[serde(rename = "hoveron")]
219 hover_on: Option<HoverOn>,
220}
221
222impl<Theta, R> ScatterPolar<Theta, R>
223where
224 Theta: Serialize + Clone + 'static,
225 R: Serialize + Clone + 'static,
226{
227 pub fn new(theta: Vec<Theta>, r: Vec<R>) -> Box<Self> {
228 Box::new(Self {
229 theta: Some(theta),
230 r: Some(r),
231 ..Default::default()
232 })
233 }
234
235 #[cfg(feature = "plotly_ndarray")]
236 pub fn from_array(theta: Array<Theta, Ix1>, r: Array<R, Ix1>) -> Box<Self> {
237 Box::new(Self {
238 theta: Some(theta.to_vec()),
239 r: Some(r.to_vec()),
240 ..Default::default()
241 })
242 }
243
244 /// Produces `ScatterPolar` traces from a 2 dimensional tensor
245 /// (`traces_matrix`) indexed by `x`. This function requires the
246 /// `ndarray` feature.
247 ///
248 /// # Arguments
249 /// * `x` - One dimensional array (or view) that represents the
250 /// `x` axis coordinates.
251 /// * `traces_matrix` - Two dimensional array (or view) containing the `y`
252 /// axis coordinates of
253 /// the traces.
254 /// * `array_traces` - Determines whether the traces are arranged in the
255 /// matrix over the
256 /// columns (`ArrayTraces::OverColumns`) or over the rows
257 /// (`ArrayTraces::OverRows`).
258 ///
259 /// # Examples
260 ///
261 /// ```
262 /// use plotly::common::Mode;
263 /// use plotly::{Plot, ScatterPolar, ArrayTraces};
264 /// use ndarray::{Array, Ix1, Ix2};
265 ///
266 /// fn ndarray_to_traces() {
267 /// let n: usize = 11;
268 /// let theta: Array<f64, Ix1> = Array::range(0., 360., 360. / n as f64);
269 /// let mut rs: Array<f64, Ix2> = Array::zeros((11, 11));
270 /// let mut count = 0.;
271 /// for mut row in rs.gencolumns_mut() {
272 /// for index in 0..row.len() {
273 /// row[index] = count + (index as f64).powf(2.);
274 /// }
275 /// count += 1.;
276 /// }
277 ///
278 /// let traces = ScatterPolar::default()
279 /// .mode(Mode::LinesMarkers)
280 /// .to_traces(theta, rs, ArrayTraces::OverColumns);
281 ///
282 /// let mut plot = Plot::new();
283 /// plot.add_traces(traces);
284 ///
285 /// # if false { // Skip running this line in doctests.
286 /// plot.show();
287 /// # }
288 /// }
289 /// fn main() -> std::io::Result<()> {
290 /// ndarray_to_traces();
291 /// Ok(())
292 /// }
293 /// ```
294 #[cfg(feature = "plotly_ndarray")]
295 pub fn to_traces(
296 &self,
297 theta: Array<Theta, Ix1>,
298 traces_matrix: Array<R, Ix2>,
299 array_traces: ArrayTraces,
300 ) -> Vec<Box<dyn Trace>> {
301 let mut traces: Vec<Box<dyn Trace>> = Vec::new();
302 let mut trace_vectors = crate::private::trace_vectors_from(traces_matrix, array_traces);
303 trace_vectors.reverse();
304 while !trace_vectors.is_empty() {
305 let mut sc = Box::new(self.clone());
306 sc.theta = Some(theta.to_vec());
307 let data = trace_vectors.pop();
308 if let Some(d) = data {
309 sc.r = Some(d);
310 traces.push(sc);
311 }
312 }
313
314 traces
315 }
316
317 /// Enables WebGL.
318 pub fn web_gl_mode(mut self, on: bool) -> Box<Self> {
319 self.r#type = if on {
320 PlotType::ScatterPolarGL
321 } else {
322 PlotType::ScatterPolar
323 };
324 Box::new(self)
325 }
326}
327
328impl<Theta, R> Trace for ScatterPolar<Theta, R>
329where
330 Theta: Serialize + Clone + 'static,
331 R: Serialize + Clone + 'static,
332{
333 fn to_json(&self) -> String {
334 serde_json::to_string(self).unwrap()
335 }
336}
337
338#[cfg(test)]
339mod tests {
340 use serde_json::{json, to_value};
341
342 use super::*;
343
344 #[test]
345 fn test_serialize_default_scatter_polar() {
346 let trace = ScatterPolar::<u32, u32>::default();
347 let expected = json!({"type": "scatterpolar"});
348
349 assert_eq!(to_value(trace).unwrap(), expected);
350 }
351
352 #[test]
353 fn test_serialize_scatter_polar() {
354 let trace = ScatterPolar::new(vec![0, 1], vec![2, 3])
355 .clip_on_axis(true)
356 .connect_gaps(false)
357 .custom_data(vec!["custom_data"])
358 .dr(1.0)
359 .dtheta(2.0)
360 .fill(Fill::ToNext)
361 .fill_color("#789456")
362 .hover_info(HoverInfo::Name)
363 .hover_label(Label::new())
364 .hover_on(HoverOn::Fills)
365 .hover_template("hover_template")
366 .hover_template_array(vec!["hover_template"])
367 .hover_text("hover_text")
368 .hover_text_array(vec!["hover_text"])
369 .ids(vec!["1"])
370 .legend_group("legend_group")
371 .legend_group_title(LegendGroupTitle::new("Legend Group Title"))
372 .line(Line::new())
373 .marker(Marker::new())
374 .meta("meta")
375 .mode(Mode::LinesMarkers)
376 .name("scatter_polar_trace")
377 .opacity(0.6)
378 .r0(0)
379 .show_legend(false)
380 .text("text")
381 .text_array(vec!["text"])
382 .text_font(Font::new())
383 .text_position(Position::MiddleCenter)
384 .text_position_array(vec![Position::MiddleLeft])
385 .text_template("text_template")
386 .text_template_array(vec!["text_template"])
387 .theta0(5)
388 .visible(Visible::True)
389 .web_gl_mode(true);
390
391 let expected = json!({
392 "type": "scatterpolargl",
393 "theta": [0, 1],
394 "r": [2, 3],
395 "cliponaxis": true,
396 "connectgaps": false,
397 "customdata": ["custom_data"],
398 "dr": 1.0,
399 "dtheta": 2.0,
400 "fill": "tonext",
401 "fillcolor": "#789456",
402 "hoverinfo": "name",
403 "hoverlabel": {},
404 "hoveron": "fills",
405 "hovertext": ["hover_text"],
406 "hovertemplate": ["hover_template"],
407 "ids": ["1"],
408 "legendgroup": "legend_group",
409 "legendgrouptitle": {"text": "Legend Group Title"},
410 "line": {},
411 "marker": {},
412 "meta": "meta",
413 "mode": "lines+markers",
414 "name": "scatter_polar_trace",
415 "opacity": 0.6,
416 "r0": 0,
417 "theta0": 5,
418 "showlegend": false,
419 "text": ["text"],
420 "textfont": {},
421 "textposition": ["middle left"],
422 "texttemplate": ["text_template"],
423 "visible": true
424 });
425
426 assert_eq!(to_value(trace).unwrap(), expected);
427 }
428}