Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/scripts/build-book-examples.sh
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ BOOK_EXAMPLES=(
"basic_charts"
"custom_controls"
"financial_charts"
"maps"
"scientific_charts"
"shapes"
"static_export"
Expand Down
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ https://github.com/plotly/plotly.rs/pull/350

- [[#NNN](https://github.com/plotly/plotly.rs/pull/NNN)] Add `Treemap` trace type, with `Tiling`/`PathBar` helpers, a dedicated `treemap::Marker` (`pad`/`corner_radius`/`depth_fade`), and `treemapcolorway`/`extendtreemapcolors` layout options
- [[#NNN](https://github.com/plotly/plotly.rs/pull/NNN)] Add `Sunburst` trace type
- [[#NNN](https://github.com/plotly/plotly.rs/pull/NNN)] Add `Choropleth` (geo subplot) and `ChoroplethMap` (MapLibre `map` subplot) trace types, with a `LocationMode` enum and a dedicated `choropleth::Marker`; add the MapLibre `map` subplot via `LayoutMap`/`MapStyle`/`MapBounds`
- [[#NNN](https://github.com/plotly/plotly.rs/pull/NNN)] Add `LayoutGeo` `fitbounds` (`GeoFitBounds`) and `resolution` (`GeoResolution`, 1:110M/1:50M base-layer detail) options
- [[#407](https://github.com/plotly/plotly.rs/issues/407)] Expose plotly.js 3.1–3.6 attributes:
- `Layout`: `hoversort`, `hoveranywhere`, `clickanywhere`
- `Axis`: `zerolinelayer` (`ZeroLineLayer`), `minorloglabels`, `modebardisable` (`ModeBarDisable`), `ticklabelposition` (`TickLabelPosition`), `unifiedhovertitle` (`UnifiedHoverTitle`), and `ExponentFormat::SIExtended`
Expand Down
2 changes: 2 additions & 0 deletions docs/book/src/SUMMARY.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@
- [Rangebreaks](./recipes/financial_charts/rangebreaks.md)
- [3D Charts](./recipes/3dcharts.md)
- [Scatter 3D](./recipes/3dcharts/3dcharts.md)
- [Maps](./recipes/maps.md)
- [Choropleth Maps](./recipes/maps/choropleth_maps.md)
- [Subplots](./recipes/subplots.md)
- [Subplots](./recipes/subplots/subplots.md)
- [Multiple Axes](./recipes/subplots/multiple_axes.md)
Expand Down
7 changes: 7 additions & 0 deletions docs/book/src/recipes/maps.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# Maps

The source code for the following examples can also be found [here](https://github.com/plotly/plotly.rs/tree/main/examples/maps).

Kind | Link
:---|:----:
Choropleth Maps | [Choropleth Maps](./maps/choropleth_maps.md)
47 changes: 47 additions & 0 deletions docs/book/src/recipes/maps/choropleth_maps.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
# Choropleth Maps

Choropleth maps color geographic regions (countries, states, custom GeoJSON
areas) according to a data value. Two trace types are available:

- [`Choropleth`](https://docs.rs/plotly/latest/plotly/struct.Choropleth.html) —
drawn on the built-in `geo` subplot
([`LayoutGeo`](https://docs.rs/plotly/latest/plotly/layout/struct.LayoutGeo.html)).
Regions are matched by `location_mode` (ISO-3 codes, USA state codes, country
names, or a GeoJSON id).
- [`ChoroplethMap`](https://docs.rs/plotly/latest/plotly/struct.ChoroplethMap.html) —
drawn on the MapLibre `map` subplot
([`LayoutMap`](https://docs.rs/plotly/latest/plotly/layout/struct.LayoutMap.html)).
Regions are always matched against a GeoJSON feature collection via
`feature_id_key`.

The following imports are used in the examples below:

```rust,no_run
use plotly::{
choropleth::{LocationMode, Marker as ChoroplethMarker},
color::Rgb,
common::{ColorBar, ColorScale, ColorScalePalette, Line},
layout::{Center, DragMode, LayoutGeo, LayoutMap, MapStyle},
Choropleth, ChoroplethMap, Configuration, Layout, Plot,
};
```

The `to_inline_html` method is used to produce the html plots displayed in this
page. The rendered maps require an internet connection (the MapLibre basemap and,
for the second example, the remote GeoJSON are fetched in the browser).

## Choropleth on a geo subplot

```rust,no_run
{{#include ../../../../../examples/maps/src/main.rs:choropleth}}
```

{{#include ../../../../../examples/maps/output/inline_choropleth.html}}

## Choropleth on a MapLibre map subplot

```rust,no_run
{{#include ../../../../../examples/maps/src/main.rs:choropleth_map}}
```

{{#include ../../../../../examples/maps/output/inline_choropleth_map.html}}
1 change: 1 addition & 0 deletions examples/maps/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,5 @@ plotly = { path = "../../plotly" }
plotly_utils = { path = "../plotly_utils" }
csv = "1.3"
reqwest = { version = "0.11", features = ["blocking"] }
serde_json = "1"

145 changes: 125 additions & 20 deletions examples/maps/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,15 @@
#![allow(dead_code)]

use plotly::{
choropleth::{LocationMode, Marker as ChoroplethMarker},
color::Rgb,
common::{Line, Marker, Mode},
layout::{Axis, Center, DragMode, LayoutGeo, Mapbox, MapboxStyle, Projection, Rotation},
Configuration, DensityMapbox, Layout, Plot, ScatterGeo, ScatterMapbox,
common::{ColorBar, ColorScale, ColorScalePalette, Line, Marker, Mode},
layout::{
Axis, Center, DragMode, GeoResolution, LayoutGeo, LayoutMap, MapStyle, Mapbox, MapboxStyle,
Projection, Rotation,
},
Choropleth, ChoroplethMap, Configuration, DensityMapbox, Layout, Plot, ScatterGeo,
ScatterMapbox,
};
use plotly_utils::write_example_to_html;

Expand Down Expand Up @@ -35,15 +40,38 @@ fn scatter_geo(show: bool, file_name: &str) {
use csv;
use reqwest;

// Download and parse the CSV
// Download and parse the CSV. If the fetch fails (e.g. no network during a
// book/CI build), warn and skip this example rather than panicking.
let url = "https://raw.githubusercontent.com/plotly/datasets/master/globe_contours.csv";
let req = reqwest::blocking::get(url).unwrap().text().unwrap();
let req = match reqwest::blocking::get(url)
.and_then(|resp| resp.error_for_status())
.and_then(|resp| resp.text())
{
Ok(body) => body,
Err(err) => {
eprintln!("warning: skipping scatter_geo example; failed to fetch {url}: {err}");
return;
}
};
let mut rdr = csv::Reader::from_reader(req.as_bytes());
let headers = rdr.headers().unwrap().clone();
let headers = match rdr.headers() {
Ok(headers) => headers.clone(),
Err(err) => {
eprintln!("warning: skipping scatter_geo example; failed to read CSV headers: {err}");
return;
}
};
let mut rows = vec![];
for result in rdr.records() {
let record = result.unwrap();
rows.push(record);
match result {
Ok(record) => rows.push(record),
Err(err) => {
eprintln!(
"warning: skipping scatter_geo example; failed to parse CSV record: {err}"
);
return;
}
}
}

// Color scale
Expand All @@ -65,23 +93,26 @@ fn scatter_geo(show: bool, file_name: &str) {
for i in 0..scl.len() {
let lat_head = format!("lat-{}", i + 1);
let lon_head = format!("lon-{}", i + 1);
let (lat_idx, lon_idx) = match (
headers.iter().position(|h| h == lat_head),
headers.iter().position(|h| h == lon_head),
) {
(Some(lat_idx), Some(lon_idx)) => (lat_idx, lon_idx),
_ => {
eprintln!(
"warning: skipping scatter_geo example; missing expected columns \
{lat_head}/{lon_head}"
);
return;
}
};
let lat: Vec<f64> = rows
.iter()
.map(|row| {
row.get(headers.iter().position(|h| h == lat_head).unwrap())
.unwrap()
.parse()
.unwrap_or(f64::NAN)
})
.map(|row| row.get(lat_idx).unwrap_or("").parse().unwrap_or(f64::NAN))
.collect();
let lon: Vec<f64> = rows
.iter()
.map(|row| {
row.get(headers.iter().position(|h| h == lon_head).unwrap())
.unwrap()
.parse()
.unwrap_or(f64::NAN)
})
.map(|row| row.get(lon_idx).unwrap_or("").parse().unwrap_or(f64::NAN))
.collect();
all_lats.push(lat);
all_lons.push(lon);
Expand Down Expand Up @@ -152,9 +183,83 @@ fn density_mapbox(show: bool, file_name: &str) {
}
}

/// Classic choropleth on the `geo` subplot, coloring countries by value using
/// ISO-3 country codes.
// ANCHOR: choropleth
fn choropleth(show: bool, file_name: &str) {
let trace = Choropleth::new(
vec![
"USA", "CAN", "MEX", "BRA", "ARG", "FRA", "DEU", "CHN", "IND", "AUS",
],
vec![10.0, 8.0, 6.0, 7.0, 4.0, 9.0, 9.5, 12.0, 11.0, 5.0],
)
.location_mode(LocationMode::Iso3)
.color_scale(ColorScale::Palette(ColorScalePalette::Viridis))
.color_bar(ColorBar::new().title("Score"))
.marker(ChoroplethMarker::new().line(Line::new().width(0.5).color(Rgb::new(80, 80, 80))));

let layout = Layout::new()
.drag_mode(DragMode::Zoom)
.geo(
LayoutGeo::new()
.showcountries(true)
.showland(true)
.resolution(GeoResolution::OneOverFiftyMillion),
);

let mut plot = Plot::new();
plot.add_trace(trace);
plot.set_layout(layout);
plot.set_configuration(Configuration::default().responsive(true).fill_frame(true));

let path = write_example_to_html(&plot, file_name);
if show {
plot.show_html(path);
}
}
// ANCHOR_END: choropleth

/// Choropleth on the MapLibre `map` subplot. Regions are matched against a
/// GeoJSON feature collection (referenced here by URL) via `feature_id_key`.
// ANCHOR: choropleth_map
fn choropleth_map(show: bool, file_name: &str) {
let geojson_url =
"https://raw.githubusercontent.com/python-visualization/folium/main/tests/us-states.json";

let trace = ChoroplethMap::new(
vec!["AL", "AK", "AZ", "CA", "NY", "TX"],
vec![1.0, 2.0, 3.0, 4.0, 5.0, 6.0],
)
.geojson(serde_json::json!(geojson_url))
.feature_id_key("id")
.color_scale(ColorScale::Palette(ColorScalePalette::Bluered))
.show_scale(true)
.marker(ChoroplethMarker::new().opacity(0.7));

let layout = Layout::new().drag_mode(DragMode::Zoom).map(
LayoutMap::new()
.style(MapStyle::CartoPositron)
.center(Center::new(38.0, -96.0))
.zoom(3.0),
);

let mut plot = Plot::new();
plot.add_trace(trace);
plot.set_layout(layout);
plot.set_configuration(Configuration::default().responsive(true).fill_frame(true));

let path = write_example_to_html(&plot, file_name);
if show {
plot.show_html(path);
}
}
// ANCHOR_END: choropleth_map

fn main() {
// Change false to true on any of these lines to display the example.
scatter_mapbox(false, "scatter_mapbox");
scatter_geo(false, "scatter_geo");
density_mapbox(false, "density_mapbox");
choropleth(false, "choropleth");
choropleth_map(false, "choropleth_map");
}
2 changes: 2 additions & 0 deletions plotly/src/common/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,8 @@ pub enum PlotType {
Pie,
Treemap,
Sunburst,
Choropleth,
ChoroplethMap,
}

#[derive(Serialize, Clone, Debug)]
Expand Down
Loading