From bfb713803b4bdcfb4af024ffe9843eb1fdcdae1a Mon Sep 17 00:00:00 2001 From: "Guillaume W. Bres" Date: Thu, 21 Sep 2023 09:32:08 +0200 Subject: [PATCH 1/5] Introducing DensityMapbox Signed-off-by: Guillaume W. Bres --- examples/maps/src/main.rs | 24 ++++- plotly/src/common/mod.rs | 1 + plotly/src/lib.rs | 4 +- plotly/src/traces/density_mapbox.rs | 146 ++++++++++++++++++++++++++++ plotly/src/traces/mod.rs | 2 + 5 files changed, 174 insertions(+), 3 deletions(-) create mode 100644 plotly/src/traces/density_mapbox.rs diff --git a/examples/maps/src/main.rs b/examples/maps/src/main.rs index 10312ced..cf150815 100644 --- a/examples/maps/src/main.rs +++ b/examples/maps/src/main.rs @@ -3,7 +3,7 @@ use plotly::{ common::Marker, layout::{Center, DragMode, Mapbox, MapboxStyle, Margin}, - Layout, Plot, ScatterMapbox, + Layout, Plot, ScatterMapbox, DensityMapbox, }; fn scatter_mapbox() { @@ -27,8 +27,30 @@ fn scatter_mapbox() { plot.show(); } +fn density_mapbox() { + let trace = DensityMapbox::new(vec![45.5017], vec![-73.5673], vec![0.75]) + .zauto(true); + + let layout = Layout::new() + .drag_mode(DragMode::Zoom) + .margin(Margin::new().top(0).left(0).bottom(0).right(0)) + .mapbox( + Mapbox::new() + .style(MapboxStyle::OpenStreetMap) + .center(Center::new(45.5017, -73.5673)) + .zoom(5), + ); + + let mut plot = Plot::new(); + plot.add_trace(trace); + plot.set_layout(layout); + + plot.show(); +} + fn main() { // Uncomment any of these lines to display the example. // scatter_mapbox(); + // density_mapbox(); } diff --git a/plotly/src/common/mod.rs b/plotly/src/common/mod.rs index 44fece3a..d6cc3082 100644 --- a/plotly/src/common/mod.rs +++ b/plotly/src/common/mod.rs @@ -201,6 +201,7 @@ pub enum PlotType { Ohlc, Sankey, Surface, + DensityMapbox, } #[derive(Serialize, Clone, Debug)] diff --git a/plotly/src/lib.rs b/plotly/src/lib.rs index 2d0a1e15..729e0a9d 100644 --- a/plotly/src/lib.rs +++ b/plotly/src/lib.rs @@ -33,8 +33,8 @@ pub use plot::{ImageFormat, Plot, Trace}; pub use traces::{box_plot, contour, histogram, image, mesh3d, sankey, scatter_mapbox, surface}; // Bring the different trace types into the top-level scope pub use traces::{ - Bar, BoxPlot, Candlestick, Contour, HeatMap, Histogram, Image, Mesh3D, Ohlc, Sankey, Scatter, - Scatter3D, ScatterMapbox, ScatterPolar, Surface, + Bar, BoxPlot, Candlestick, Contour, DensityMapbox, HeatMap, Histogram, Image, Mesh3D, Ohlc, + Sankey, Scatter, Scatter3D, ScatterMapbox, ScatterPolar, Surface, }; pub trait Restyle: serde::Serialize {} diff --git a/plotly/src/traces/density_mapbox.rs b/plotly/src/traces/density_mapbox.rs new file mode 100644 index 00000000..dd66ce67 --- /dev/null +++ b/plotly/src/traces/density_mapbox.rs @@ -0,0 +1,146 @@ +//! Density mapbox scatter plot + +use plotly_derive::FieldSetter; +use serde::Serialize; + +use crate::common::{LegendGroupTitle, Line, PlotType, Visible}; +use crate::Trace; + +#[serde_with::skip_serializing_none] +#[derive(Serialize, Clone, Debug, FieldSetter)] +#[field_setter(box_self, kind = "trace")] +pub struct DensityMapbox +where + Lat: Serialize + Clone, + Lon: Serialize + Clone, + Z: Serialize + Clone, +{ + #[field_setter(default = "PlotType::DensityMapbox")] + r#type: PlotType, + /// Sets the trace name. The trace name appear as the legend item and on + /// hover. + name: Option, + /// Determines whether or not this trace is visible. If + /// `Visible::LegendOnly`, the trace is not drawn, but can appear as a + /// legend item (provided that the legend itself is visible). + visible: Option, + + /// Determines whether or not an item corresponding to this trace is shown + /// in the legend. + #[serde(rename = "showlegend")] + show_legend: Option, + + /// Sets the legend rank for this trace. Items and groups with smaller ranks + /// are presented on top/left side while with `"reversed" + /// `legend.trace_order` they are on bottom/right side. The default + /// legendrank is 1000, so that you can use ranks less than 1000 to + /// place certain items before all unranked items, and ranks greater + /// than 1000 to go after all unranked items. + #[serde(rename = "legendrank")] + legend_rank: Option, + /// Sets the legend group for this trace. Traces part of the same legend + /// group show/hide at the same time when toggling legend items. + #[serde(rename = "legendgroup")] + legend_group: Option, + /// Set and style the title to appear for the legend group. + #[serde(rename = "legendgrouptitle")] + legend_group_title: Option, + + /// Line display properties. + line: Option, + + lat: Option>, + lon: Option>, + z: Option>, + + /// Sets the opacity of the trace. + opacity: Option, + + /// Sets a reference between this trace's data coordinates and a mapbox + /// subplot. If "mapbox" (the default value), the data refer to + /// `layout.mapbox`. If "mapbox2", the data refer to `layout.mapbox2`, and + /// so on. + subplot: Option, + + /// Determines whether or not the color domain is computed + /// with respect to the input data (here in `z`) or the bounds set + /// in `zmin` and `zmax`. Defaults to false when `zmin` and `zmax` are + /// set by the user. + zauto: Option, + + /// Sets the upper bound of the color domain. Value should have the + /// same units as in `z` and if set, `zmin` must be set as well. + zmax: Option, + + zmid: Option, + + zmin: Option, + + zoom: Option, + + radius: Option, + //color_continuous_scale: Option>, + //color_continuous_midpoint: Option, +} + +impl DensityMapbox +where + Lat: Serialize + Clone + std::default::Default, // TODO why is "+ Default" necessary? + Lon: Serialize + Clone + std::default::Default, + Z: Serialize + Clone + std::default::Default, +{ + pub fn new(lat: Vec, lon: Vec, z: Vec) -> Box { + Box::new(Self { + lat: Some(lat), + lon: Some(lon), + z: Some(z), + ..Default::default() + }) + } +} + +impl Trace for DensityMapbox +where + Lat: Serialize + Clone, + Lon: Serialize + Clone, + Z: Serialize + Clone, +{ + fn to_json(&self) -> String { + serde_json::to_string(&self).unwrap() + } +} + +#[cfg(test)] +mod tests { + use serde_json::{json, to_value}; + + use super::*; + + #[test] + fn test_serialize_density_mapbox() { + let density_mapbox = DensityMapbox::new(vec![45.5017], vec![-73.5673], vec![1.0]) + .name("name") + .visible(Visible::True) + .show_legend(true) + .legend_rank(1000) + .legend_group("legend group") + .zoom(5) + .radius(20) + .opacity(0.5); + let expected = json!({ + "type": "densitymapbox", + "lat": [45.5017], + "lon": [-73.5673], + "z": [1.0], + "name": "name", + "visible": true, + "showlegend": true, + "legendrank": 1000, + "legendgroup": "legend group", + "opacity": 0.5, + "zoom": 5, + "radius": 20, + }); + assert_eq!(to_value(density_mapbox.clone()).unwrap(), expected); + } +} diff --git a/plotly/src/traces/mod.rs b/plotly/src/traces/mod.rs index 28f6079e..e6ca3179 100644 --- a/plotly/src/traces/mod.rs +++ b/plotly/src/traces/mod.rs @@ -4,6 +4,7 @@ pub mod bar; pub mod box_plot; mod candlestick; pub mod contour; +mod density_mapbox; mod heat_map; pub mod histogram; pub mod image; @@ -20,6 +21,7 @@ pub use bar::Bar; pub use box_plot::BoxPlot; pub use candlestick::Candlestick; pub use contour::Contour; +pub use density_mapbox::DensityMapbox; pub use heat_map::HeatMap; pub use histogram::Histogram; pub use mesh3d::Mesh3D; From ce8e8061c5b5a1b2a6d342255ca4abbbbbb65b12 Mon Sep 17 00:00:00 2001 From: Andrei Gherghescu <8067229+andrei-ng@users.noreply.github.com> Date: Fri, 10 May 2024 11:57:29 +0200 Subject: [PATCH 2/5] reference PR in CHANGELOG Signed-off-by: Andrei Gherghescu <8067229+andrei-ng@users.noreply.github.com> --- CHANGELOG.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e0a39420..38f0d83b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,9 +3,10 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -## [0.8.5] - 2023-xx-xx +## [0.8.5] - 2024-xx-xx ### Added -- [[#153](https://github.com/igiagkiozis/plotly/pull/153)] Added `LayoutScene` +- [[#163](https://github.com/plotly/plotly.rs/pull/163)] Added `DensityMapbox`. +- [[#153](https://github.com/igiagkiozis/plotly/pull/153)] Added `LayoutScene`. ## [0.8.4] - 2023-07-09 ### Added @@ -46,7 +47,7 @@ Version 0.8.0 represents a significant release which refactors a lot of the code - Support for `Sankey` diagrams - Support for `Plot3D` - 3D plots for scatter, line and surface data ### Changed -- Improve implementation of `private::NumOrString` to support more primitive types ([Issue +- Improve implementation of `private::NumOrString` to support more primitive types ([Issue #47](https://github.com/igiagkiozis/plotly/issues/47)) - Remove `private::TruthyEnum` in favour of a more robust way of serializing to `String` or `bool` - Refactor `Color` module From ade2563119bed400d291b3d23eefd65ffd9cd104 Mon Sep 17 00:00:00 2001 From: Andrei Gherghescu <8067229+andrei-ng@users.noreply.github.com> Date: Fri, 10 May 2024 12:00:09 +0200 Subject: [PATCH 3/5] apply cargo fmt Signed-off-by: Andrei Gherghescu <8067229+andrei-ng@users.noreply.github.com> --- examples/maps/src/main.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/examples/maps/src/main.rs b/examples/maps/src/main.rs index cf150815..1258e6ae 100644 --- a/examples/maps/src/main.rs +++ b/examples/maps/src/main.rs @@ -3,7 +3,7 @@ use plotly::{ common::Marker, layout::{Center, DragMode, Mapbox, MapboxStyle, Margin}, - Layout, Plot, ScatterMapbox, DensityMapbox, + DensityMapbox, Layout, Plot, ScatterMapbox, }; fn scatter_mapbox() { @@ -28,8 +28,7 @@ fn scatter_mapbox() { } fn density_mapbox() { - let trace = DensityMapbox::new(vec![45.5017], vec![-73.5673], vec![0.75]) - .zauto(true); + let trace = DensityMapbox::new(vec![45.5017], vec![-73.5673], vec![0.75]).zauto(true); let layout = Layout::new() .drag_mode(DragMode::Zoom) From 94f8016f6b6c222462c3438ecc936a518494b01e Mon Sep 17 00:00:00 2001 From: Andrei Gherghescu <8067229+andrei-ng@users.noreply.github.com> Date: Fri, 10 May 2024 10:55:02 +0200 Subject: [PATCH 4/5] fix clippy warnings Signed-off-by: Andrei Gherghescu <8067229+andrei-ng@users.noreply.github.com> --- plotly/src/layout/update_menu.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plotly/src/layout/update_menu.rs b/plotly/src/layout/update_menu.rs index 4823385e..eea463f0 100644 --- a/plotly/src/layout/update_menu.rs +++ b/plotly/src/layout/update_menu.rs @@ -100,7 +100,7 @@ impl ButtonBuilder { pub fn new() -> Self { Default::default() } - pub fn push_restyle(mut self, restyle: impl Restyle + Serialize) -> Self { + pub fn push_restyle(mut self, restyle: impl Restyle) -> Self { let restyle = serde_json::to_value(&restyle).unwrap(); for (k, v) in restyle.as_object().unwrap() { self.restyles.insert(k.clone(), v.clone()); From 6b958eb8851e46a5e56facd23aef060c3f12a919 Mon Sep 17 00:00:00 2001 From: Andrei Gherghescu <8067229+andrei-ng@users.noreply.github.com> Date: Fri, 10 May 2024 11:01:27 +0200 Subject: [PATCH 5/5] fix clippy warning on unsued type - apply target configuration to StaticPlotTemplate Signed-off-by: Andrei Gherghescu <8067229+andrei-ng@users.noreply.github.com> --- plotly/src/plot.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/plotly/src/plot.rs b/plotly/src/plot.rs index a7d175ea..a2dab216 100644 --- a/plotly/src/plot.rs +++ b/plotly/src/plot.rs @@ -20,6 +20,7 @@ struct PlotTemplate<'a> { #[derive(Template)] #[template(path = "static_plot.html", escape = "none")] +#[cfg(not(target_family = "wasm"))] struct StaticPlotTemplate<'a> { plot: &'a Plot, format: ImageFormat,