From d381fd15758081540e1c60e93e348a1b2ab471bc Mon Sep 17 00:00:00 2001 From: Andrei Gherghescu <8067229+andrei-ng@users.noreply.github.com> Date: Thu, 19 Jun 2025 18:44:15 +0100 Subject: [PATCH 1/2] adding extra themes Signed-off-by: Andrei Gherghescu <8067229+andrei-ng@users.noreply.github.com> --- examples/themes/Cargo.toml | 10 + examples/themes/assets/gapminder2007.csv | 143 ++++++++++++ examples/themes/src/main.rs | 154 +++++++++++++ plotly/src/layout/themes.rs | 278 ++++++++++++++++++++++- 4 files changed, 583 insertions(+), 2 deletions(-) create mode 100644 examples/themes/Cargo.toml create mode 100644 examples/themes/assets/gapminder2007.csv create mode 100644 examples/themes/src/main.rs diff --git a/examples/themes/Cargo.toml b/examples/themes/Cargo.toml new file mode 100644 index 00000000..421b4dbf --- /dev/null +++ b/examples/themes/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "themes" +version = "0.1.0" +authors = ["Andrei Gherghescu andrei-ng@protonmail.com"] +edition = "2021" + +[dependencies] +ndarray = "0.16" +csv = "1.1" +plotly = { path = "../../plotly" } diff --git a/examples/themes/assets/gapminder2007.csv b/examples/themes/assets/gapminder2007.csv new file mode 100644 index 00000000..044ac396 --- /dev/null +++ b/examples/themes/assets/gapminder2007.csv @@ -0,0 +1,143 @@ +country,pop,continent,lifeExp,gdpPercap +Afghanistan,31889923.0,Asia,43.828,974.5803384 +Albania,3600523.0,Europe,76.423,5937.029525999999 +Algeria,33333216.0,Africa,72.301,6223.367465 +Angola,12420476.0,Africa,42.731,4797.231267 +Argentina,40301927.0,Americas,75.32,12779.379640000001 +Australia,20434176.0,Oceania,81.235,34435.367439999995 +Austria,8199783.0,Europe,79.829,36126.4927 +Bahrain,708573.0,Asia,75.635,29796.048339999998 +Bangladesh,150448339.0,Asia,64.062,1391.253792 +Belgium,10392226.0,Europe,79.441,33692.60508 +Benin,8078314.0,Africa,56.728,1441.284873 +Bolivia,9119152.0,Americas,65.554,3822.1370840000004 +Bosnia and Herzegovina,4552198.0,Europe,74.852,7446.298803 +Botswana,1639131.0,Africa,50.728,12569.851770000001 +Brazil,190010647.0,Americas,72.39,9065.800825 +Bulgaria,7322858.0,Europe,73.005,10680.79282 +Burkina Faso,14326203.0,Africa,52.295,1217.032994 +Burundi,8390505.0,Africa,49.58,430.07069160000003 +Cambodia,14131858.0,Asia,59.723,1713.7786859999999 +Cameroon,17696293.0,Africa,50.43,2042.0952399999999 +Canada,33390141.0,Americas,80.653,36319.235010000004 +Central African Republic,4369038.0,Africa,44.74100000000001,706.016537 +Chad,10238807.0,Africa,50.651,1704.0637239999999 +Chile,16284741.0,Americas,78.553,13171.63885 +China,1318683096.0,Asia,72.961,4959.1148539999995 +Colombia,44227550.0,Americas,72.889,7006.580419 +Comoros,710960.0,Africa,65.152,986.1478792000001 +"Congo, Dem. Rep.",64606759.0,Africa,46.461999999999996,277.55185869999997 +"Congo, Rep.",3800610.0,Africa,55.321999999999996,3632.557798 +Costa Rica,4133884.0,Americas,78.782,9645.06142 +Cote d'Ivoire,18013409.0,Africa,48.328,1544.750112 +Croatia,4493312.0,Europe,75.748,14619.222719999998 +Cuba,11416987.0,Americas,78.273,8948.102923 +Czech Republic,10228744.0,Europe,76.486,22833.30851 +Denmark,5468120.0,Europe,78.332,35278.41874 +Djibouti,496374.0,Africa,54.791000000000004,2082.4815670000003 +Dominican Republic,9319622.0,Americas,72.235,6025.374752000001 +Ecuador,13755680.0,Americas,74.994,6873.262326000001 +Egypt,80264543.0,Africa,71.33800000000001,5581.180998 +El Salvador,6939688.0,Americas,71.878,5728.353514 +Equatorial Guinea,551201.0,Africa,51.57899999999999,12154.08975 +Eritrea,4906585.0,Africa,58.04,641.3695236000001 +Ethiopia,76511887.0,Africa,52.946999999999996,690.8055759 +Finland,5238460.0,Europe,79.313,33207.0844 +France,61083916.0,Europe,80.657,30470.0167 +Gabon,1454867.0,Africa,56.735,13206.48452 +Gambia,1688359.0,Africa,59.448,752.7497265 +Germany,82400996.0,Europe,79.406,32170.37442 +Ghana,22873338.0,Africa,60.022,1327.60891 +Greece,10706290.0,Europe,79.483,27538.41188 +Guatemala,12572928.0,Americas,70.259,5186.050003 +Guinea,9947814.0,Africa,56.007,942.6542111 +Guinea-Bissau,1472041.0,Africa,46.388000000000005,579.2317429999999 +Haiti,8502814.0,Americas,60.916000000000004,1201.637154 +Honduras,7483763.0,Americas,70.19800000000001,3548.3308460000003 +"Hong Kong, China",6980412.0,Asia,82.208,39724.97867 +Hungary,9956108.0,Europe,73.33800000000001,18008.94444 +Iceland,301931.0,Europe,81.757,36180.789189999996 +India,1110396331.0,Asia,64.69800000000001,2452.210407 +Indonesia,223547000.0,Asia,70.65,3540.6515640000002 +Iran,69453570.0,Asia,70.964,11605.71449 +Iraq,27499638.0,Asia,59.545,4471.061906 +Ireland,4109086.0,Europe,78.885,40675.99635 +Israel,6426679.0,Asia,80.745,25523.2771 +Italy,58147733.0,Europe,80.546,28569.7197 +Jamaica,2780132.0,Americas,72.567,7320.880262000001 +Japan,127467972.0,Asia,82.603,31656.06806 +Jordan,6053193.0,Asia,72.535,4519.461171 +Kenya,35610177.0,Africa,54.11,1463.249282 +"Korea, Dem. Rep.",23301725.0,Asia,67.297,1593.06548 +"Korea, Rep.",49044790.0,Asia,78.623,23348.139730000003 +Kuwait,2505559.0,Asia,77.58800000000001,47306.98978 +Lebanon,3921278.0,Asia,71.993,10461.05868 +Lesotho,2012649.0,Africa,42.592,1569.331442 +Liberia,3193942.0,Africa,45.678000000000004,414.5073415 +Libya,6036914.0,Africa,73.952,12057.49928 +Madagascar,19167654.0,Africa,59.443000000000005,1044.770126 +Malawi,13327079.0,Africa,48.303000000000004,759.3499101 +Malaysia,24821286.0,Asia,74.241,12451.6558 +Mali,12031795.0,Africa,54.467,1042.581557 +Mauritania,3270065.0,Africa,64.164,1803.1514960000002 +Mauritius,1250882.0,Africa,72.801,10956.99112 +Mexico,108700891.0,Americas,76.195,11977.57496 +Mongolia,2874127.0,Asia,66.803,3095.7722710000003 +Montenegro,684736.0,Europe,74.543,9253.896111 +Morocco,33757175.0,Africa,71.164,3820.17523 +Mozambique,19951656.0,Africa,42.082,823.6856205 +Myanmar,47761980.0,Asia,62.068999999999996,944.0 +Namibia,2055080.0,Africa,52.906000000000006,4811.060429 +Nepal,28901790.0,Asia,63.785,1091.359778 +Netherlands,16570613.0,Europe,79.762,36797.93332 +New Zealand,4115771.0,Oceania,80.204,25185.00911 +Nicaragua,5675356.0,Americas,72.899,2749.320965 +Niger,12894865.0,Africa,56.867,619.6768923999999 +Nigeria,135031164.0,Africa,46.858999999999995,2013.9773050000001 +Norway,4627926.0,Europe,80.196,49357.19017 +Oman,3204897.0,Asia,75.64,22316.19287 +Pakistan,169270617.0,Asia,65.483,2605.94758 +Panama,3242173.0,Americas,75.53699999999999,9809.185636 +Paraguay,6667147.0,Americas,71.752,4172.838464 +Peru,28674757.0,Americas,71.421,7408.905561 +Philippines,91077287.0,Asia,71.688,3190.481016 +Poland,38518241.0,Europe,75.563,15389.924680000002 +Portugal,10642836.0,Europe,78.098,20509.64777 +Puerto Rico,3942491.0,Americas,78.74600000000001,19328.70901 +Reunion,798094.0,Africa,76.442,7670.122558 +Romania,22276056.0,Europe,72.476,10808.47561 +Rwanda,8860588.0,Africa,46.242,863.0884639000001 +Sao Tome and Principe,199579.0,Africa,65.528,1598.435089 +Saudi Arabia,27601038.0,Asia,72.777,21654.83194 +Senegal,12267493.0,Africa,63.062,1712.4721359999999 +Serbia,10150265.0,Europe,74.002,9786.534714 +Sierra Leone,6144562.0,Africa,42.568000000000005,862.5407561000001 +Singapore,4553009.0,Asia,79.972,47143.179639999995 +Slovak Republic,5447502.0,Europe,74.663,18678.31435 +Slovenia,2009245.0,Europe,77.926,25768.25759 +Somalia,9118773.0,Africa,48.159,926.1410683 +South Africa,43997828.0,Africa,49.339,9269.657808 +Spain,40448191.0,Europe,80.941,28821.0637 +Sri Lanka,20378239.0,Asia,72.396,3970.0954070000003 +Sudan,42292929.0,Africa,58.556000000000004,2602.394995 +Swaziland,1133066.0,Africa,39.613,4513.480643 +Sweden,9031088.0,Europe,80.884,33859.74835 +Switzerland,7554661.0,Europe,81.70100000000001,37506.419069999996 +Syria,19314747.0,Asia,74.143,4184.548089 +Taiwan,23174294.0,Asia,78.4,28718.27684 +Tanzania,38139640.0,Africa,52.516999999999996,1107.482182 +Thailand,65068149.0,Asia,70.616,7458.3963269999995 +Togo,5701579.0,Africa,58.42,882.9699437999999 +Trinidad and Tobago,1056608.0,Americas,69.819,18008.50924 +Tunisia,10276158.0,Africa,73.923,7092.923025 +Turkey,71158647.0,Europe,71.777,8458.276384 +Uganda,29170398.0,Africa,51.542,1056.3801210000001 +United Kingdom,60776238.0,Europe,79.425,33203.26128 +United States,301139947.0,Americas,78.242,42951.65309 +Uruguay,3447496.0,Americas,76.384,10611.46299 +Venezuela,26084662.0,Americas,73.747,11415.805690000001 +Vietnam,85262356.0,Asia,74.249,2441.576404 +West Bank and Gaza,4018332.0,Asia,73.422,3025.349798 +"Yemen, Rep.",22211743.0,Asia,62.698,2280.769906 +Zambia,11746035.0,Africa,42.38399999999999,1271.211593 +Zimbabwe,12311143.0,Africa,43.486999999999995,469.70929810000007 diff --git a/examples/themes/src/main.rs b/examples/themes/src/main.rs new file mode 100644 index 00000000..f185e332 --- /dev/null +++ b/examples/themes/src/main.rs @@ -0,0 +1,154 @@ +#![allow(dead_code)] + +use plotly::{ + common::{Marker, Mode, Title}, + layout::Layout, + Plot, Scatter, +}; + +use plotly::layout::themes::BuiltinTheme; + +use std::fs::File; +use std::io::BufReader; + +use csv::ReaderBuilder; + +// Read Gapminder 2007 data from CSV +fn read_gapminder_data_from_csv() -> (Vec, Vec, Vec, Vec, Vec) { + let file = File::open("assets/gapminder2007.csv").expect("Cannot open gapminder2007.csv"); + let mut rdr = ReaderBuilder::new().has_headers(true).from_reader(BufReader::new(file)); + let mut gdp_per_capita = Vec::new(); + let mut life_expectancy = Vec::new(); + let mut population = Vec::new(); + let mut continents = Vec::new(); + let mut countries = Vec::new(); + for result in rdr.records() { + let record = result.expect("CSV record error"); + countries.push(record[0].to_string()); + population.push(record[1].parse::().unwrap_or(0.0)); + continents.push(record[2].to_string()); + life_expectancy.push(record[3].parse::().unwrap_or(0.0)); + gdp_per_capita.push(record[4].parse::().unwrap_or(0.0)); + } + (gdp_per_capita, life_expectancy, population, continents, countries) +} + +fn create_gapminder_scatter_plot(theme: BuiltinTheme, show: bool) { + let (gdp, life_exp, pop, continents, countries) = read_gapminder_data_from_csv(); + + let mut plot = Plot::new(); + let continent_colors = vec![ + ("Asia", "#1f77b4"), + ("Europe", "#ff7f0e"), + ("Africa", "#2ca02c"), + ("Americas", "#d62728"), + ("Oceania", "#9467bd"), + ]; + + for (continent, color) in &continent_colors { + let indices: Vec = continents + .iter() + .enumerate() + .filter(|(_, c)| c == continent) + .map(|(idx, _)| idx) + .collect(); + if indices.is_empty() { + continue; + } + let continent_gdp: Vec = indices.iter().map(|&idx| gdp[idx]).collect(); + let continent_life: Vec = indices.iter().map(|&idx| life_exp[idx]).collect(); + let continent_pop: Vec = indices.iter().map(|&idx| pop[idx]).collect(); + let continent_countries: Vec = indices.iter().map(|&idx| countries[idx].clone()).collect(); + let trace = Scatter::new(continent_gdp, continent_life) + .mode(Mode::Markers) + .name(continent.to_string()) + .text_array(continent_countries) + .marker( + Marker::new() + .size_array( + continent_pop + .iter() + .map(|&p| ((p / 1_000_000.0).min(60.0)) as usize) + .collect(), + ) + .color(*color) + .opacity(0.6) + .line(plotly::common::Line::new().width(1.0).color("white")), + ); + plot.add_trace(trace); + } + + let theme_template = theme.build(); + plot.set_layout( + Layout::new() + .template(theme_template) + .title(Title::from(format!( + "Gapminder 2007: '{}' theme", + theme_name(theme) + ))) + .x_axis( + plotly::layout::Axis::new() + .title("GDP per capita (log scale)") + .type_(plotly::layout::AxisType::Log), + ) + .y_axis(plotly::layout::Axis::new().title("Life Expectancy")) + .width(800) + .height(600) + .show_legend(true), + ); + + let path = write_example_to_html( + &plot, + &format!("gapminder_{}", theme_name(theme).to_lowercase()), + ); + if show { + plot.show_html(path); + } +} + +fn theme_name(theme: BuiltinTheme) -> &'static str { + match theme { + BuiltinTheme::Default => "plotly", + BuiltinTheme::PlotlyWhite => "plotly_white", + BuiltinTheme::PlotlyDark => "plotly_dark", + BuiltinTheme::Seaborn => "seaborn", + BuiltinTheme::SeabornWhitegrid => "seaborn_whitegrid", + BuiltinTheme::SeabornDark => "seaborn_dark", + BuiltinTheme::Matplotlib => "matplotlib", + BuiltinTheme::Plotnine => "plotnine", + } +} + +fn write_example_to_html(plot: &Plot, name: &str) -> String { + std::fs::create_dir_all("./output").unwrap(); + // Write inline HTML + let html = plot.to_inline_html(Some(name)); + let path = format!("./output/inline_{}.html", name); + std::fs::write(path, html).unwrap(); + // Write standalone HTML + let path = format!("./output/{}.html", name); + plot.write_html(&path); + path +} + +fn main() { + // Create Gapminder-style plots with different themes, matching the Plotly documentation + // Based on: https://plotly.com/python/templates/ + + // Create plots for each theme + let themes = vec![ + BuiltinTheme::Default, + BuiltinTheme::PlotlyWhite, + BuiltinTheme::PlotlyDark, + BuiltinTheme::Seaborn, + BuiltinTheme::Matplotlib, + BuiltinTheme::Plotnine, + ]; + + for theme in themes { + create_gapminder_scatter_plot(theme, false); + } + + // Show one example (the default theme) + create_gapminder_scatter_plot(BuiltinTheme::Default, true); +} diff --git a/plotly/src/layout/themes.rs b/plotly/src/layout/themes.rs index 6d010295..ec08c056 100644 --- a/plotly/src/layout/themes.rs +++ b/plotly/src/layout/themes.rs @@ -2,7 +2,7 @@ use once_cell::sync::Lazy; use crate::{ common::{ColorBar, ColorScale, ColorScaleElement, Font, Label, Title}, - layout::{Axis, ColorAxis, HoverMode, LayoutColorScale, LayoutTemplate, Template}, + layout::{Axis, ColorAxis, HoverMode, LayoutColorScale, LayoutTemplate, Template, TicksDirection}, }; pub static DEFAULT: Lazy