plotly/traces/
pie.rs

1//! Pie chart plot
2
3use plotly_derive::FieldSetter;
4use serde::Serialize;
5
6use crate::private::{NumOrString, NumOrStringCollection};
7use crate::{
8    common::{
9        Dim, Domain, Font, HoverInfo, Label, LegendGroupTitle, Marker, Orientation, PlotType,
10        Position, Visible,
11    },
12    Trace,
13};
14
15#[derive(Debug, Clone, Serialize)]
16pub enum PieDirection {
17    Clockwise,
18    CounterClockwise,
19}
20
21/// Construct a Pie Chart trace.
22///
23/// # Examples
24///
25/// ```
26/// use plotly::Pie;
27///
28/// let trace = Pie::new(
29///     vec![2, 3, 5]);
30///
31/// let expected = serde_json::json!({
32///     "type": "pie",
33///     "values": [2, 3, 5],
34/// });
35///
36/// assert_eq!(serde_json::to_value(trace).unwrap(), expected);
37/// ```
38/// # Using only labels
39///
40/// Build a new Pie Chart by only assigning the labels field. The Pie chart
41/// will be generated by counting the number of unique labels, see [Pie::labels]
42/// field description. Note that to create a Pie chart by using this
43/// function, the type parameter `P` needs to be specialized, this can be
44/// done by doing
45///
46/// ```
47/// use plotly::Pie;
48///
49/// let labels = ["giraffes", "giraffes", "orangutans", "monkeys"];
50///
51/// let trace = Pie::<u32>::from_labels(&labels);
52///
53/// let expected = serde_json::json!({
54///     "type": "pie",
55///     "labels": ["giraffes", "giraffes", "orangutans", "monkeys"],
56/// });
57///
58/// assert_eq!(serde_json::to_value(trace).unwrap(), expected);
59/// ```
60#[serde_with::skip_serializing_none]
61#[derive(Serialize, Clone, Debug, FieldSetter)]
62#[field_setter(box_self, kind = "trace")]
63pub struct Pie<P>
64where
65    P: Serialize + Clone,
66{
67    #[field_setter(default = "PlotType::Pie")]
68    r#type: PlotType,
69    domain: Option<Domain>,
70    /// Determines whether outside text labels can push the margins.
71    automargin: Option<bool>,
72    /// Assigns extra data each datum. This may be useful when listening to
73    /// hover, click and selection events. Note that, “scatter” traces also
74    /// appends customdata items in the markers DOM elements
75    #[serde(rename = "customdata")]
76    custom_data: Option<NumOrStringCollection>,
77    /// Specifies the direction at which succeeding sectors follow one another.
78    /// The 'direction' property is an enumeration that may be specified as
79    /// One of the following enumeration values: ['clockwise',
80    /// 'counterclockwise']
81    direction: Option<PieDirection>,
82    /// Sets the label step. See label0 for more info.
83    dlabel: Option<f64>,
84    /// Sets the fraction of the radius to cut out of the pie. Use this to make
85    /// a donut chart. The 'hole' property is a number and may be specified
86    /// as a value in the interval [0, 1]
87    hole: Option<f64>,
88    /// Determines which trace information appear on hover. If none or skip are
89    /// set, no information is displayed upon hovering. But, if none is set,
90    /// click and hover events are still fired.
91    #[serde(rename = "hoverinfo")]
92    hover_info: Option<HoverInfo>,
93    #[serde(rename = "hoverlabel")]
94    hover_label: Option<Label>,
95    /// Template string used for rendering the information that appear on hover
96    /// box. Note that this will override hoverinfo. Variables are inserted
97    /// using %{variable}, for example “y: %{y}” as well as %{xother},
98    /// {%_xother}, {%_xother_}, {%xother_}. When showing info for several
99    /// points, “xother” will be added to those with different x positions from
100    /// the first point. An underscore before or after “(x|y)other” will add
101    /// a space on that side, only when this field is shown. Numbers are
102    /// formatted using d3-format’s syntax %{variable:d3-format}, for example
103    /// “Price: %{y:$.2f}”.  <https://github.com/d3/d3-format/tree/v1.4.5#d3-format> for details on the formatting syntax.
104    /// Dates are formatted using d3-time-format’s syntax
105    /// %{variable|d3-time-format}, for example “Day: %{2019-01-01|%A}”. <https://github.com/d3/d3-time-format/tree/v2.2.3#locale_format> for
106    /// details on the date formatting syntax. The variables available in
107    /// hovertemplate are the ones  emitted as event data described at this link <https://plotly.com/javascript/plotlyjs-events/#event-data>.
108    /// Additionally, every attributes that can be specified per-point (the
109    /// ones that are arrayOk: true) are available.  Finally, the template
110    /// string has access to variables label, color, value, percent and text.
111    /// Anything contained in tag \<extra\> is displayed in the secondary box,
112    /// for example “<extra>{fullData.name}</extra>”. To hide the secondary
113    /// box completely, use an empty tag <extra></extra>.
114    #[serde(rename = "hovertemplate")]
115    hover_template: Option<Dim<String>>,
116    /// Sets hover text elements associated with each sector. If a single
117    /// string, the same string appears for all data points. If an array of
118    /// string, the items are mapped in order of this trace’s sectors. To be
119    /// seen, trace hoverinfo must contain a “text” flag.
120    #[serde(rename = "hovertext")]
121    hover_text: Option<Dim<String>>,
122    /// Assigns id labels to each datum. These ids for object constancy of data
123    /// points during animation. Should be an array of strings, not numbers or
124    /// any other type.
125    ids: Option<Vec<String>>,
126    /// Sets the font used for textinfo lying inside the sector.
127    #[serde(rename = "insidetextfont")]
128    inside_text_font: Option<Font>,
129    /// Controls the orientation of the text inside chart sectors. When set to
130    /// “auto”, text may be oriented in any direction in order to be as big as
131    /// possible in the middle of a sector. The “horizontal” option orients text
132    /// to be parallel with the bottom of the chart, and may make text smaller
133    /// in order to achieve that goal. The “radial” option orients text along
134    /// the radius of the sector. The “tangential” option orients text
135    /// perpendicular to the radius of the sector.
136    #[serde(rename = "insidetextorientation")]
137    inside_text_orientation: Option<Orientation>,
138    /// Alternate to labels. Builds a numeric set of labels. Use with dlabel
139    /// where label0 is the starting label and dlabel the step.
140    label0: Option<f64>,
141    /// Sets the sector labels. If labels entries are duplicated, we sum
142    /// associated values or simply count occurrences if values is not provided.
143    /// For other array attributes (including color) we use the first non-empty
144    /// entry among all occurrences of the label.
145    labels: Option<Vec<String>>,
146    /// Sets the legend group for this trace. Traces part of the same legend
147    /// group show/hide at the same time when toggling legend items.
148    #[serde(rename = "legendgroup")]
149    legend_group: Option<String>,
150    /// Set and style the title to appear for the legend group.
151    #[serde(rename = "legendgrouptitle")]
152    legend_group_title: Option<LegendGroupTitle>,
153    /// Sets the legend rank for this trace. Items and groups with smaller ranks
154    /// are presented on top/left side while with “reversed” legend.traceorder
155    /// they are on bottom/right side. The default legendrank is 1000, so that
156    /// you can use ranks less than 1000 to place certain items before all
157    /// unranked items, and ranks greater than 1000 to go after all unranked
158    /// items. When having unranked or equal rank items shapes would be
159    /// displayed after traces i.e. according to their order in data and layout.
160    #[serde(rename = "legendrank")]
161    legend_rank: Option<usize>,
162    //
163    marker: Option<Marker>,
164    /// Assigns extra meta information associated with this trace that can be
165    /// used in various text attributes. Attributes such as trace name, graph,
166    /// axis and colorbar title.text, annotation text rangeselector,
167    /// updatemenues and sliders label text all support meta. To access the
168    /// trace meta values in an attribute in the same trace, simply use
169    /// %{meta\[i\]} where i is the index or key of the meta item in question.
170    /// To access trace meta in layout attributes, use %{data[n[.meta\[i\]}
171    /// where i is the index or key of the meta and n is the trace index.
172    meta: Option<NumOrString>,
173    /// Sets the trace name. The trace name appears as the legend item and on
174    /// hover.
175    name: Option<String>,
176    /// Sets the opacity of the trace.
177    opacity: Option<f64>,
178    /// Sets the font used for textinfo lying outside the sector.
179    #[serde(rename = "outsidetextfont")]
180    outside_text_font: Option<Font>,
181    /// Instead of the first slice starting at 12 o’clock, rotate to some other
182    /// angle. The 'rotation' property is a angle (in degrees) that may be
183    /// specified as a number between -180 and 180.  Numeric values outside this
184    /// range are converted to the equivalent value (e.g. 270 is converted to
185    /// -90).
186    rotation: Option<f64>,
187    /// Determines whether or not an item corresponding to this trace is shown
188    /// in the legend.
189    #[serde(rename = "showlegend")]
190    show_legend: Option<bool>,
191    /// Determines whether or not the sectors are reordered from largest to
192    /// smallest.
193    sort: Option<bool>,
194    /// Sets text elements associated with each sector. If trace textinfo
195    /// contains a “text” flag, these elements will be seen on the chart. If
196    /// trace hoverinfo contains a “text” flag and “hovertext” is not set, these
197    /// elements will be seen in the hover labels.
198    text: Option<Dim<String>>,
199    /// Determines which trace information appear on the graph.
200    #[serde(rename = "textinfo")]
201    text_info: Option<String>,
202    /// Sets the font used for textinfo.
203    #[serde(rename = "textfont")]
204    text_font: Option<Font>,
205    /// Specifies the location of the textinfo.
206    #[serde(rename = "textposition")]
207    text_position: Option<Dim<Position>>,
208    /// Template string used for rendering the information text that appear on
209    /// points. Note that this will override textinfo. Variables are
210    /// inserted using %{variable}, for example “y: %{y}”. Numbers are formatted
211    /// using d3-format’s syntax %{variable:d3-format},  for example “Price:
212    /// %{y:$.2f}”. <https://github.com/d3/d3-format/tree/v1.4.5#d3-format> for details on the formatting syntax.
213    /// Dates are formatted using d3-time-format’s syntax
214    /// %{variable|d3-time-format}, for example “Day: %{2019-01-01|%A}”. <https://github.com/d3/d3-time-format/tree/v2.2.3#locale_format> for details on the date formatting syntax.
215    /// Every attributes that can be specified per-point (the ones that are
216    /// arrayOk: true) are available. Finally, the template string has
217    /// access to variables label, color, value, percent and text.
218    #[serde(rename = "texttemplate")]
219    text_template: Option<Dim<String>>,
220    /// Controls persistence of some user-driven changes to the trace:
221    /// constraintrange in parcoords traces, as well as some editable: true
222    /// modifications such as name and colorbar.title. Defaults to
223    /// layout.uirevision. Note that other user-driven trace attribute changes
224    /// are controlled by layout attributes: trace.visible is controlled by
225    /// layout.legend.uirevision, selectedpoints is controlled by
226    /// layout.selectionrevision, and colorbar.(x|y) (accessible with config:
227    /// {editable: true}) is controlled by layout.editrevision. Trace changes
228    /// are tracked by uid, which only falls back on trace index if no uid is
229    /// provided. So if your app can add/remove traces before the end of the
230    /// data array, such that the same trace has a different index, you can
231    /// still preserve user-driven changes if you give each trace a uid that
232    /// stays with it as it moves.
233    #[serde(rename = "uirevision")]
234    ui_revision: Option<NumOrString>,
235    /// Sets the values of the sectors. If omitted, we count occurrences of each
236    /// label.
237    values: Option<Vec<P>>,
238    /// Determines whether or not this trace is visible. If “legendonly”, the
239    /// trace is not drawn, but can appear as a legend item (provided that the
240    /// legend itself is visible).
241    visible: Option<Visible>,
242    /// Sets the width (in px or fraction) of the legend for this trace.
243    #[serde(rename = "legendwidth")]
244    legend_width: Option<f64>,
245    /// Sets the fraction of larger radius to pull the sectors out from the
246    /// center. This can be a constant to pull all slices apart from each other
247    /// equally or an array to highlight one or more slices.
248    pull: Option<f64>,
249    /// Sets the source reference on Chart Studio Cloud for customdata.
250    #[serde(rename = "customdatasrc")]
251    custom_data_src: Option<Dim<String>>,
252    /// Sets the source reference on Chart Studio Cloud for hoverinfo.
253    #[serde(rename = "hoverinfosrc")]
254    hover_info_src: Option<Dim<String>>,
255    /// Sets the source reference on Chart Studio Cloud for hovertemplate.
256    #[serde(rename = "hovertemplatesrc")]
257    hover_template_src: Option<Dim<String>>,
258    /// Sets the source reference on Chart Studio Cloud for hovertext.
259    #[serde(rename = "hovertextsrc")]
260    hover_text_src: Option<Dim<String>>,
261    /// Sets the source reference on Chart Studio Cloud for ids.
262    #[serde(rename = "idssrc")]
263    idssrc: Option<Dim<String>>,
264    /// Sets the source reference on Chart Studio Cloud for labels.
265    #[serde(rename = "labelssrc")]
266    labelssrc: Option<Dim<String>>,
267    /// metasrc – Sets the source reference on Chart Studio Cloud for meta.
268    #[serde(rename = "metasrc")]
269    metasrc: Option<Dim<String>>,
270    /// Sets the source reference on Chart Studio Cloud for values.
271    #[serde(rename = "valuessrc")]
272    values_src: Option<Dim<String>>,
273    /// Sets the source reference on Chart Studio Cloud for pull.
274    #[serde(rename = "pullsrc")]
275    pull_src: Option<Dim<String>>,
276    /// Sets the source reference on Chart Studio Cloud for textposition.
277    #[serde(rename = "textpositionsrc")]
278    text_position_src: Option<Dim<String>>,
279    /// Sets the source reference on Chart Studio Cloud for text.
280    #[serde(rename = "textsrc")]
281    text_src: Option<Dim<String>>,
282    /// Sets the source reference on Chart Studio Cloud for texttemplate.
283    #[serde(rename = "texttemplatesrc")]
284    text_template_src: Option<Dim<String>>,
285    /// Assign an id to this trace, Use this to provide object constancy between
286    /// traces during animations and transitions.
287    uid: Option<String>,
288    /// If there are multiple pie charts that should be sized according to their
289    /// totals, link them by providing a non-empty group id here shared by every
290    /// trace in the same group.
291    #[serde(rename = "scalegroup")]
292    scale_group: Option<String>,
293    //
294    // stream – plotly.graph_objects.pie.Stream instance or dict with compatible properties
295    //
296    // legend – Sets the reference to a legend to show this trace in. References to these legends
297    // are “legend”, “legend2”, “legend3”, etc. Settings for these legends are set in the layout,
298    // under layout.legend, layout.legend2, etc.
299    //
300    // textinfo – Determines which trace information appear on the graph.
301}
302
303impl<P> Pie<P>
304where
305    P: Serialize + Clone + 'static,
306{
307    /// Build a new Pie Chart by only assigning the values field
308    pub fn new(values: Vec<P>) -> Box<Self> {
309        Box::new(Self {
310            values: Some(values),
311            ..Default::default()
312        })
313    }
314
315    /// Same as [Pie::new()]
316    pub fn from_values(values: Vec<P>) -> Box<Self> {
317        Box::new(Self {
318            values: Some(values),
319            ..Default::default()
320        })
321    }
322
323    /// Build a new Pie Chart by only assigning the labels field. The Pie chart
324    /// will be generated by counting the number of unique labels, see
325    /// [Pie::labels] field description. Note that to create a Pie chart by
326    /// using this function, the type parameter `P` needs to be specialized,
327    /// this can be done by doing
328    /// ```
329    /// use plotly::Pie;
330    ///
331    /// let labels = ["giraffes", "giraffes", "orangutans", "monkeys"];
332    /// let trace = Pie::<u32>::from_labels(&labels);
333    /// ```
334    pub fn from_labels<T: AsRef<str> + ToString>(labels: &[T]) -> Box<Self> {
335        let l = labels.iter().map(|s| s.to_string()).collect();
336        Box::new(Self {
337            labels: Some(l),
338            ..Default::default()
339        })
340    }
341}
342
343impl<P> Trace for Pie<P>
344where
345    P: Serialize + Clone,
346{
347    fn to_json(&self) -> String {
348        serde_json::to_string(self).unwrap()
349    }
350}
351
352#[cfg(test)]
353mod tests {
354    use serde_json::{json, to_value};
355
356    use super::*;
357
358    #[test]
359    fn serialize_pie() {
360        let pie_trace = Pie::new(vec![45, 55])
361            .name("pie")
362            .automargin(true)
363            .direction(PieDirection::Clockwise)
364            .hole(0.2)
365            .inside_text_font(Font::new().color("#ff7f0e"))
366            .inside_text_orientation(Orientation::Tangential)
367            .labels(vec!["a", "b"])
368            .sort(true)
369            .visible(Visible::True)
370            .show_legend(true)
371            .legend_rank(1000)
372            .legend_group("legend group")
373            .legend_group_title("Legend Group Title")
374            .opacity(0.5)
375            .ids(vec!["one"])
376            .text("text")
377            .text_info("label+percent")
378            .text_array(vec!["text"])
379            .text_template("text_template")
380            .text_template_array(vec!["text_template"])
381            .text_font(Font::new())
382            .text_position(Position::TopCenter)
383            .text_position_array(vec![Position::MiddleLeft])
384            .hover_text("hover_text")
385            .hover_text_array(vec!["hover_text"])
386            .hover_info(HoverInfo::XAndYAndZ)
387            .hover_template("hover_template")
388            .hover_template_array(vec!["hover_template"])
389            .meta("meta")
390            .custom_data(vec!["custom_data"])
391            .marker(Marker::new())
392            .hover_label(Label::new())
393            .ui_revision(6);
394        let expected = json!({
395            "values": [45, 55],
396            "type": "pie",
397            "name": "pie",
398            "automargin": true,
399            "direction" : "Clockwise",
400            "hole": 0.2,
401            "insidetextfont": {"color": "#ff7f0e"},
402            "insidetextorientation": "t",
403            "labels": ["a", "b"],
404            "sort": true,
405            "visible": true,
406            "showlegend": true,
407            "legendrank": 1000,
408            "legendgroup": "legend group",
409            "legendgrouptitle": {"text": "Legend Group Title"},
410            "opacity": 0.5,
411            "ids": ["one"],
412            "text": ["text"],
413            "textinfo": "label+percent",
414            "textfont": {},
415            "texttemplate": ["text_template"],
416            "textposition": ["middle left"],
417            "hovertext": ["hover_text"],
418            "hoverinfo": "x+y+z",
419            "hovertemplate": ["hover_template"],
420            "meta": "meta",
421            "customdata": ["custom_data"],
422            "marker": {},
423            "hoverlabel": {},
424            "uirevision": 6,
425        });
426
427        assert_eq!(to_value(pie_trace).unwrap(), expected);
428    }
429
430    #[test]
431    fn new_from_values() {
432        let values = vec![2.2, 3.3, 4.4];
433        let trace = Pie::from_values(values);
434
435        let expected = serde_json::json!({
436            "type": "pie",
437            "values": [2.2, 3.3, 4.4],
438        });
439
440        assert_eq!(to_value(trace).unwrap(), expected);
441    }
442
443    #[test]
444    fn new_from_labels() {
445        let labels = ["giraffes", "giraffes", "orangutans", "monkeys"];
446
447        let trace = Pie::<u32>::from_labels(&labels);
448
449        let expected = serde_json::json!({
450            "type": "pie",
451            "labels": ["giraffes", "giraffes", "orangutans", "monkeys"],
452        });
453
454        assert_eq!(to_value(trace).unwrap(), expected);
455    }
456}