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