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}