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}