1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
|
// Data is pulled from https://covid.ourworldindata.org/data/owid-covid-data.json
use plotters::prelude::*;
use std::fs::File;
use std::io::BufReader;
#[derive(serde_derive::Deserialize)]
struct DailyData {
#[serde(default)]
new_cases: f64,
#[serde(default)]
total_cases: f64,
}
#[derive(serde_derive::Deserialize)]
struct CountryData {
data: Vec<DailyData>,
}
const OUT_FILE_NAME: &'static str = "plotters-doc-data/tick_control.svg";
fn main() -> Result<(), Box<dyn std::error::Error>> {
let root = SVGBackend::new(OUT_FILE_NAME, (1024, 768)).into_drawing_area();
root.fill(&WHITE)?;
let (upper, lower) = root.split_vertically(750);
lower.titled(
"Data Source: https://covid.ourworldindata.org/data/owid-covid-data.json",
("sans-serif", 10).into_font().color(&BLACK.mix(0.5)),
)?;
let mut chart = ChartBuilder::on(&upper)
.caption("World COVID-19 Cases", ("sans-serif", (5).percent_height()))
.set_label_area_size(LabelAreaPosition::Left, (8).percent())
.set_label_area_size(LabelAreaPosition::Bottom, (4).percent())
.margin((1).percent())
.build_cartesian_2d(
(20u32..5000_0000u32)
.log_scale()
.with_key_points(vec![50, 100, 1000, 10000, 100000, 1000000, 10000000]),
(0u32..50_0000u32)
.log_scale()
.with_key_points(vec![10, 50, 100, 1000, 10000, 100000, 200000]),
)?;
chart
.configure_mesh()
.x_desc("Total Cases")
.y_desc("New Cases")
.draw()?;
let data: std::collections::HashMap<String, CountryData> = serde_json::from_reader(
BufReader::new(File::open("plotters-doc-data/covid-data.json")?),
)?;
for (idx, &series) in ["CHN", "USA", "RUS", "JPN", "DEU", "IND", "OWID_WRL"]
.iter()
.enumerate()
{
let color = Palette99::pick(idx).mix(0.9);
chart
.draw_series(LineSeries::new(
data[series].data.iter().map(
|&DailyData {
new_cases,
total_cases,
..
}| (total_cases as u32, new_cases as u32),
),
color.stroke_width(3),
))?
.label(series)
.legend(move |(x, y)| Rectangle::new([(x, y - 5), (x + 10, y + 5)], color.filled()));
}
chart
.configure_series_labels()
.border_style(&BLACK)
.draw()?;
// To avoid the IO failure being ignored silently, we manually call the present function
root.present().expect("Unable to write result to file, please make sure 'plotters-doc-data' dir exists under current dir");
println!("Result has been saved to {}", OUT_FILE_NAME);
Ok(())
}
#[test]
fn entry_point() {
main().unwrap()
}
|