1#[macro_use]
5extern crate log;
6
7use geom::{Duration, LonLat};
8use kml::ExtraShapes;
9use serde::{Deserialize, Serialize};
10
11#[derive(Serialize, Deserialize)]
13pub struct CollisionDataset {
14 pub source_url: String,
16 pub collisions: Vec<Collision>,
18}
19
20#[derive(Serialize, Deserialize)]
22pub struct Collision {
23 pub location: LonLat,
25 pub time: Duration,
28 pub severity: Severity,
30 }
34
35#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
39pub enum Severity {
40 Slight,
41 Serious,
42 Fatal,
43}
44
45pub fn import_stats19(input: ExtraShapes, source_url: &str) -> CollisionDataset {
48 let mut data = CollisionDataset {
49 source_url: source_url.to_string(),
50 collisions: Vec::new(),
51 };
52 for shape in input.shapes {
53 if shape.points.len() != 1 {
54 warn!("One row had >1 point: {:?}", shape);
55 continue;
56 }
57 let time = match Duration::parse(&format!("{}:00", shape.attributes["Time"])) {
58 Ok(time) => time,
59 Err(err) => {
60 warn!("Couldn't parse time: {}", err);
61 continue;
62 }
63 };
64 let severity = match shape.attributes["Accident_Severity"].as_ref() {
65 "1" => Severity::Slight,
67 "2" => Severity::Serious,
68 "3" => Severity::Fatal,
69 x => {
70 warn!("Unknown severity {}", x);
71 continue;
72 }
73 };
74 data.collisions.push(Collision {
75 location: shape.points[0],
76 time,
77 severity,
78 });
79 }
80 data
81}
82
83pub fn import_seattle(input: ExtraShapes, source_url: &str) -> CollisionDataset {
87 let mut data = CollisionDataset {
88 source_url: source_url.to_string(),
89 collisions: Vec::new(),
90 };
91 for shape in input.shapes {
92 if shape.points.len() != 1 {
93 warn!("One row had >1 point: {:?}", shape);
94 continue;
95 }
96 let time = match parse_incdttm(&shape.attributes["INCDTTM"]) {
97 Some(time) => time,
98 None => {
99 warn!("Couldn't parse time {}", shape.attributes["INCDTTM"]);
100 continue;
101 }
102 };
103 let severity = match shape
104 .attributes
105 .get("SEVERITYCODE")
106 .cloned()
107 .unwrap_or_else(String::new)
108 .as_ref()
109 {
110 "1" | "0" => Severity::Slight,
111 "2b" | "2" => Severity::Serious,
112 "3" => Severity::Fatal,
113 x => {
114 warn!("Unknown severity {}", x);
115 continue;
116 }
117 };
118 data.collisions.push(Collision {
119 location: shape.points[0],
120 time,
121 severity,
122 });
123 }
124 data
125}
126
127fn parse_incdttm(x: &str) -> Option<Duration> {
129 let parts = x.split(' ').collect::<Vec<_>>();
130 if parts.len() != 3 {
131 return None;
132 }
133 let time = Duration::parse(parts[1]).ok()?;
134 if parts[2] == "AM" {
135 Some(time)
136 } else if parts[2] == "PM" {
137 Some(time + Duration::hours(12))
138 } else {
139 None
140 }
141}