1 // Data is pulled from https://covid.ourworldindata.org/data/owid-covid-data.json
2 use plotters::prelude::*;
3 use std::fs::File;
4 use std::io::BufReader;
5 
6 #[derive(serde_derive::Deserialize)]
7 struct DailyData {
8     #[serde(default)]
9     new_cases: f64,
10     #[serde(default)]
11     total_cases: f64,
12 }
13 
14 #[derive(serde_derive::Deserialize)]
15 struct CountryData {
16     data: Vec<DailyData>,
17 }
18 
19 const OUT_FILE_NAME: &'static str = "plotters-doc-data/tick_control.svg";
main() -> Result<(), Box<dyn std::error::Error>>20 fn 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]
entry_point()87 fn entry_point() {
88     main().unwrap()
89 }
90