plotly_fork/traces/
scatter_mapbox.rs

1//! Mapbox scatter plot
2
3use plotly_derive::FieldSetter;
4use serde::Serialize;
5
6use crate::common::{
7    color::Color, Dim, Font, HoverInfo, Label, LegendGroupTitle, Line, Marker, Mode, PlotType,
8    Position, Visible,
9};
10use crate::private::{NumOrString, NumOrStringCollection};
11use crate::Trace;
12
13#[derive(Serialize, Clone, Debug)]
14#[serde(rename_all = "lowercase")]
15pub enum Fill {
16    None,
17    ToSelf,
18}
19
20#[serde_with::skip_serializing_none]
21#[derive(Serialize, Clone, Debug, Default)]
22pub struct SelectionMarker {
23    color: Option<Box<dyn Color>>,
24    opacity: Option<f64>,
25    size: Option<Dim<usize>>,
26}
27
28#[derive(Serialize, Clone, Debug, Default)]
29pub struct Selection {
30    marker: SelectionMarker,
31}
32
33impl Selection {
34    pub fn new() -> Self {
35        Default::default()
36    }
37
38    /// Sets the marker color of un/selected points.
39    pub fn color<C: Color>(mut self, color: C) -> Self {
40        self.marker.color = Some(Box::new(color));
41        self
42    }
43
44    /// Sets the marker opacity of un/selected points.
45    pub fn opacity(mut self, opacity: f64) -> Self {
46        self.marker.opacity = Some(opacity);
47        self
48    }
49
50    /// Sets the marker size of un/selected points.
51    pub fn size(mut self, size: usize) -> Self {
52        self.marker.size = Some(Dim::Scalar(size));
53        self
54    }
55}
56
57#[serde_with::skip_serializing_none]
58#[derive(Serialize, Clone, Debug, FieldSetter)]
59#[field_setter(box_self, kind = "trace")]
60pub struct ScatterMapbox<Lat, Lon>
61where
62    Lat: Serialize + Clone,
63    Lon: Serialize + Clone,
64{
65    #[field_setter(default = "PlotType::ScatterMapbox")]
66    r#type: PlotType,
67    /// Sets the trace name. The trace name appear as the legend item and on
68    /// hover.
69    name: Option<String>,
70    /// Determines whether or not this trace is visible. If
71    /// `Visible::LegendOnly`, the trace is not drawn, but can appear as a
72    /// legend item (provided that the legend itself is visible).
73    visible: Option<Visible>,
74
75    /// Determines whether or not an item corresponding to this trace is shown
76    /// in the legend.
77    #[serde(rename = "showlegend")]
78    show_legend: Option<bool>,
79    /// Sets the legend rank for this trace. Items and groups with smaller ranks
80    /// are presented on top/left side while with `"reversed"
81    /// `legend.trace_order` they are on bottom/right side. The default
82    /// legendrank is 1000, so that you can use ranks less than 1000 to
83    /// place certain items before all unranked items, and ranks greater
84    /// than 1000 to go after all unranked items.
85    #[serde(rename = "legendrank")]
86    legend_rank: Option<usize>,
87    /// Sets the legend group for this trace. Traces part of the same legend
88    /// group show/hide at the same time when toggling legend items.
89    #[serde(rename = "legendgroup")]
90    legend_group: Option<String>,
91    /// Set and style the title to appear for the legend group.
92    #[serde(rename = "legendgrouptitle")]
93    legend_group_title: Option<LegendGroupTitle>,
94
95    /// Sets the opacity of the trace.
96    opacity: Option<f64>,
97    /// Determines the drawing mode for this scatter trace. If the provided
98    /// `Mode` includes "Text" then the `text` elements appear at the
99    /// coordinates. Otherwise, the `text` elements appear on hover. If
100    /// there are less than 20 points and the trace is not stacked then the
101    /// default is `Mode::LinesMarkers`, otherwise it is `Mode::Lines`.
102    mode: Option<Mode>,
103    /// Assigns id labels to each datum. These ids for object constancy of data
104    /// points during animation. Should be an array of strings, not numbers
105    /// or any other type.
106    ids: Option<Vec<String>>,
107
108    lat: Option<Vec<Lat>>,
109    lon: Option<Vec<Lon>>,
110
111    /// Sets text elements associated with each (x,y) pair. If a single string,
112    /// the same string appears over all the data points. If an array of
113    /// strings, the items are mapped in order to the this trace's (x,y)
114    /// coordinates. If the trace `HoverInfo` contains a "text" flag and
115    /// `hover_text` is not set, these elements will be seen in the hover
116    /// labels.
117    text: Option<Dim<String>>,
118    /// Sets the positions of the `text` elements with respects to the (x,y)
119    /// coordinates.
120    #[serde(rename = "textposition")]
121    text_position: Option<Dim<Position>>,
122    /// Template string used for rendering the information text that appear on
123    /// points. Note that this will override `textinfo`. Variables are
124    /// inserted using %{variable}, for example "y: %{y}". Numbers are
125    /// 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)
126    /// for details on the formatting syntax. Dates are formatted using
127    /// d3-time-format's syntax %{variable|d3-time-format}, for example
128    /// "Day: %{2019-01-01|%A}". See [format](https://github.com/d3/d3-3.x-api-reference/blob/master/Time-Formatting.md#format) for details
129    /// on the date formatting syntax. Every attributes that can be specified
130    /// per-point (the ones that are `arrayOk: true`) are available.
131    #[serde(rename = "texttemplate")]
132    text_template: Option<Dim<String>>,
133    /// Sets hover text elements associated with each (x,y) pair. If a single
134    /// string, the same string appears over all the data points. If an
135    /// array of string, the items are mapped in order to the this trace's
136    /// (x,y) coordinates. To be seen, trace `HoverInfo` must contain a
137    /// "Text" flag.
138    #[serde(rename = "hovertext")]
139    hover_text: Option<Dim<String>>,
140    /// Determines which trace information appear on hover. If `HoverInfo::None`
141    /// or `HoverInfo::Skip` are set, no information is displayed upon
142    /// hovering. But, if `HoverInfo::None` is set, click and hover events
143    /// are still fired.
144    #[serde(rename = "hoverinfo")]
145    hover_info: Option<HoverInfo>,
146    /// Template string used for rendering the information that appear on hover
147    /// box. Note that this will override `HoverInfo`. Variables are
148    /// inserted using %{variable}, for example "y: %{y}". Numbers are
149    /// formatted using d3-format's syntax %{variable:d3-format}, for example
150    /// "Price: %{y:$.2f}".
151    /// https://github.com/d3/d3-3.x-api-reference/blob/master/Formatting.md#d3_format for details
152    /// on the formatting syntax. Dates are formatted using d3-time-format's
153    /// syntax %{variable|d3-time-format}, for example "Day:
154    /// %{2019-01-01|%A}". https://github.com/d3/d3-3.x-api-reference/blob/master/Time-Formatting.md#format for details
155    /// on the date formatting syntax. The variables available in
156    /// `hovertemplate` are the ones emitted as event data described at this link https://plotly.com/javascript/plotlyjs-events/#event-data.
157    /// Additionally, every attributes that can be specified per-point (the ones
158    /// that are `arrayOk: true`) are available. Anything contained in tag
159    /// `<extra>` is displayed in the secondary box, for example
160    /// "<extra>{fullData.name}</extra>". To hide the secondary box
161    /// completely, use an empty tag `<extra></extra>`.
162    #[serde(rename = "hovertemplate")]
163    hover_template: Option<Dim<String>>,
164
165    /// Assigns extra meta information associated with this trace that can be
166    /// used in various text attributes. Attributes such as trace `name`,
167    /// graph, axis and colorbar `title.text`, annotation `text`
168    /// `rangeselector`, `updatemenues` and `sliders` `label` text all support
169    /// `meta`. To access the trace `meta` values in an attribute in the same
170    /// trace, simply use `%{meta[i]}` where `i` is the index or key of the
171    /// `meta` item in question. To access trace `meta` in layout
172    /// attributes, use `%{data[n[.meta[i]}` where `i` is the index or key of
173    /// the `meta` and `n` is the trace index.
174    meta: Option<NumOrString>,
175    /// Assigns extra data each datum. This may be useful when listening to
176    /// hover, click and selection events. Note that, "scatter" traces also
177    /// appends customdata items in the markers DOM elements.
178    #[serde(rename = "customdata")]
179    custom_data: Option<NumOrStringCollection>,
180
181    /// Sets a reference between this trace's data coordinates and a mapbox
182    /// subplot. If "mapbox" (the default value), the data refer to
183    /// `layout.mapbox`. If "mapbox2", the data refer to `layout.mapbox2`, and
184    /// so on.
185    subplot: Option<String>,
186    /// Determines how points are displayed and joined.
187    marker: Option<Marker>,
188
189    /// Line display properties.
190    line: Option<Line>,
191
192    /// Sets the text font.
193    #[serde(rename = "textfont")]
194    text_font: Option<Font>,
195
196    /// Vector containing integer indices of selected points. Has an effect only
197    /// for traces that support selections. Note that an empty vector means
198    /// an empty selection where the `unselected` are turned on for all
199    /// points.
200    #[serde(rename = "selectedpoints")]
201    selected_points: Option<Vec<usize>>,
202
203    /// Sets the style of selected points.
204    selected: Option<Selection>,
205    /// Sets the style of unselected points.
206    unselected: Option<Selection>,
207
208    /// Determines if this scattermapbox trace's layers are to be inserted
209    /// before the layer with the specified ID. By default, scattermapbox
210    /// layers are inserted above all the base layers. To place the
211    /// scattermapbox layers above every other layer, set `below` to "''".
212    below: Option<String>,
213    /// Determines whether or not gaps (i.e. {nan} or missing values) in the
214    /// provided data arrays are connected.
215    #[serde(rename = "connectgaps")]
216    connect_gaps: Option<bool>,
217
218    /// Sets the area to fill with a solid color. Defaults to "none" unless this
219    /// trace is stacked, then it gets "tonexty" ("tonextx") if
220    /// `orientation` is "v" ("h") Use with `fillcolor` if not
221    /// "none". "tozerox" and "tozeroy" fill to x=0 and y=0 respectively.
222    /// "tonextx" and "tonexty" fill between the endpoints of this trace and
223    /// the endpoints of the trace before it, connecting those endpoints
224    /// with straight lines (to make a stacked area graph); if there is
225    /// no trace before it, they behave like "tozerox" and "tozeroy". "toself"
226    /// connects the endpoints of the trace (or each segment of the trace if
227    /// it has gaps) into a closed shape. "tonext" fills the space between
228    /// two traces if one completely encloses the other (eg consecutive
229    /// contour lines), and behaves like "toself" if there is no trace before
230    /// it. "tonext" should not be used if one trace does not enclose the
231    /// other. Traces in a `stackgroup` will only fill to (or be filled to)
232    /// other traces in the same group. With multiple `stackgroup`s or some
233    /// traces stacked and some not, if fill-linked traces are not
234    /// already consecutive, the later ones will be pushed down in the drawing
235    /// order.
236    fill: Option<Fill>,
237    /// Sets the fill color. Defaults to a half-transparent variant of the line
238    /// color, marker color, or marker line color, whichever is available.
239    #[serde(rename = "fillcolor")]
240    fill_color: Option<Box<dyn Color>>,
241    /// Properties of label displayed on mouse hover.
242    #[serde(rename = "hoverlabel")]
243    hover_label: Option<Label>,
244
245    /// Controls persistence of some user-driven changes to the trace:
246    /// `constraintrange` in `parcoords` traces, as well as some `editable:
247    /// True` modifications such as `name` and `colorbar.title`. Defaults to
248    /// `layout.uirevision`. Note that other user-driven trace attribute changes
249    /// are controlled by `layout` attributes: `trace.visible` is controlled
250    /// by `layout.legend.uirevision`, `selectedpoints` is controlled
251    /// by `layout.selectionrevision`, and `colorbar.(x|y)` (accessible with
252    /// `config: {editable: True}`) is controlled by `layout.editrevision`.
253    /// Trace changes are tracked by `uid`, which only falls back on trace
254    /// index if no `uid` is provided. So if your app can add/remove traces
255    /// before the end of the `data` array, such that the same trace has a
256    /// different index, you can still preserve user-driven changes if you give
257    /// each trace a `uid` that stays with it as it moves.
258    #[serde(rename = "uirevision")]
259    ui_revision: Option<NumOrString>,
260}
261
262impl<Lat, Lon> ScatterMapbox<Lat, Lon>
263where
264    Lat: Serialize + Clone + std::default::Default, // TODO why is "+ Default" necessary?
265    Lon: Serialize + Clone + std::default::Default,
266{
267    pub fn new(lat: Vec<Lat>, lon: Vec<Lon>) -> Box<Self> {
268        Box::new(Self {
269            lat: Some(lat),
270            lon: Some(lon),
271            ..Default::default()
272        })
273    }
274}
275
276impl<Lat, Lon> Trace for ScatterMapbox<Lat, Lon>
277where
278    Lat: Serialize + Clone,
279    Lon: Serialize + Clone,
280{
281    fn to_json(&self) -> String {
282        serde_json::to_string(&self).unwrap()
283    }
284}
285
286#[cfg(test)]
287mod tests {
288    use serde_json::{json, to_value};
289
290    use super::*;
291
292    #[test]
293    fn test_serialize_fill() {
294        assert_eq!(to_value(Fill::None).unwrap(), json!("none"));
295        assert_eq!(to_value(Fill::ToSelf).unwrap(), json!("toself"));
296    }
297
298    #[test]
299    fn test_serialize_selection() {
300        let selection = Selection::new().color("#123456").opacity(0.5).size(6);
301        let expected = json!({"marker": {"color": "#123456", "opacity": 0.5, "size": 6}});
302
303        assert_eq!(to_value(selection).unwrap(), expected);
304    }
305
306    #[test]
307    fn test_serialize_scatter_mapbox() {
308        let scatter_mapbox = ScatterMapbox::new(vec![45.5017], vec![-73.5673])
309            .name("name")
310            .visible(Visible::True)
311            .show_legend(true)
312            .legend_rank(1000)
313            .legend_group("legend group")
314            .legend_group_title(LegendGroupTitle::new("Legend Group Title"))
315            .opacity(0.5)
316            .mode(Mode::LinesText)
317            .ids(vec!["one"])
318            .text("text")
319            .text_array(vec!["text"])
320            .text_position(Position::BottomLeft)
321            .text_position_array(vec![Position::TopCenter])
322            .text_template("text_template")
323            .text_template_array(vec!["text_template"])
324            .hover_text("hover_text")
325            .hover_text_array(vec!["hover_text"])
326            .hover_info(HoverInfo::XAndYAndZ)
327            .hover_template("hover_template")
328            .hover_template_array(vec!["hover_template"])
329            .meta("meta")
330            .custom_data(vec!["custom_data"])
331            .subplot("mapbox2")
332            .marker(Marker::new())
333            .line(Line::new())
334            .text_font(Font::new())
335            .selected_points(vec![0])
336            .selected(Selection::new().color("#111111"))
337            .unselected(Selection::new().color("#777777"))
338            .below("")
339            .connect_gaps(false)
340            .fill(Fill::None)
341            .fill_color("#ff0000aa")
342            .hover_label(Label::new())
343            .ui_revision(6);
344        let expected = json!({
345            "type": "scattermapbox",
346            "lat": [45.5017],
347            "lon": [-73.5673],
348            "name": "name",
349            "visible": true,
350            "showlegend": true,
351            "legendrank": 1000,
352            "legendgroup": "legend group",
353            "legendgrouptitle": {"text": "Legend Group Title"},
354            "opacity": 0.5,
355            "mode": "lines+text",
356            "ids": ["one"],
357            "text": ["text"],
358            "textposition": ["top center"],
359            "texttemplate": ["text_template"],
360            "hovertext": ["hover_text"],
361            "hoverinfo": "x+y+z",
362            "hovertemplate": ["hover_template"],
363            "meta": "meta",
364            "customdata": ["custom_data"],
365            "subplot": "mapbox2",
366            "marker": {},
367            "line": {},
368            "textfont": {},
369            "selectedpoints": [0],
370            "selected": {"marker": {"color": "#111111"}},
371            "unselected": {"marker": {"color": "#777777"}},
372            "below": "",
373            "connectgaps": false,
374            "fill": "none",
375            "fillcolor": "#ff0000aa",
376            "hoverlabel": {},
377            "uirevision": 6,
378        });
379
380        assert_eq!(to_value(scatter_mapbox).unwrap(), expected);
381    }
382}