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}