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
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
#[macro_use]
extern crate failure;
#[macro_use]
extern crate serde_derive;
use kdtree::{distance::squared_euclidean, ErrorKind, KdTree};
use failure::Error;
use std::fmt;
use std::path::PathBuf;
#[derive(Debug, Clone, RustcDecodable, RustcEncodable, Serialize, Deserialize)]
pub struct Record {
pub lat: f64,
pub lon: f64,
pub name: String,
pub admin1: String,
pub admin2: String,
pub admin3: String,
}
pub struct Locations {
records: Vec<([f64; 2], Record)>,
}
impl Locations {
pub fn from_memory() -> Locations {
let mut records = Vec::new();
let my_str = include_str!("../cities.csv");
let reader = quick_csv::Csv::from_string(my_str).has_header(true);
for read_record in reader {
let record: Record = read_record.unwrap().decode().unwrap();
records.push(([record.lat, record.lon], record));
}
Locations { records: records }
}
pub fn from_path(path: Option<PathBuf>) -> Result<Locations, Error> {
let mut records = Vec::new();
let path = match path {
Some(path) => path,
None => PathBuf::from("cities.csv"),
};
let reader = quick_csv::Csv::from_file(path).unwrap().has_header(true);
for read_record in reader {
let record: Record = read_record?.decode()?;
records.push(([record.lat, record.lon], record));
}
Ok(Locations { records: records })
}
}
pub struct ReverseGeocoder<'a> {
tree: KdTree<f64, &'a Record, &'a [f64; 2]>,
}
impl<'a> ReverseGeocoder<'a> {
pub fn new(loc: &'a Locations) -> ReverseGeocoder<'a> {
let mut reverse_geocoder = ReverseGeocoder::<'a> {
tree: KdTree::with_capacity(2, loc.records.len()),
};
reverse_geocoder.initialize(loc);
reverse_geocoder
}
fn initialize(&mut self, loc: &'a Locations) {
for record in &loc.records {
self.tree.add(&record.0, &record.1).unwrap();
}
}
pub fn search(&self, loc: &[f64; 2]) -> Result<Vec<(f64, &&Record)>, ErrorKind> {
self.tree.nearest(loc, 1, &squared_euclidean)
}
}
impl fmt::Display for Record {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"({}, {}): {}, {}, {}, {}",
self.lat, self.lon, self.name, self.admin1, self.admin2, self.admin3
)
}
}
#[cfg(test)]
mod tests {
#[test]
fn it_finds_3_places() {
use super::*;
let loc = Locations::from_memory();
let geocoder = ReverseGeocoder::new(&loc);
let y = geocoder.search(&[44.962786, -93.344722]);
assert_eq!(y.is_ok(), true);
let slp = y.unwrap();
assert_eq!(slp.get(0).unwrap().1.name, "Saint Louis Park");
let mpls = geocoder.search(&[44.894519, -93.308702]).unwrap();
assert_eq!(mpls.get(0).unwrap().1.name, "Richfield");
let edina = geocoder.search(&[44.887055, -93.334204]).unwrap();
assert_eq!(edina.get(0).unwrap().1.name, "Edina");
}
}