|
@@ -1,19 +1,21 @@
|
|
|
extern crate chrono;
|
|
|
extern crate quick_xml;
|
|
|
+extern crate clap;
|
|
|
|
|
|
-use std::time::Duration as stdDuration;
|
|
|
-use std::io::Cursor;
|
|
|
-use std::str;
|
|
|
-use std::io::prelude::*;
|
|
|
use std::fs::File;
|
|
|
+use std::io::prelude::*;
|
|
|
+use std::io::Cursor;
|
|
|
use std::path::Path;
|
|
|
+use std::str;
|
|
|
+use std::time::Duration as stdDuration;
|
|
|
// use chrono::DateTime;
|
|
|
use chrono::Duration;
|
|
|
use chrono::TimeZone;
|
|
|
use chrono::Utc;
|
|
|
-use quick_xml::events::{Event, BytesEnd, BytesStart};
|
|
|
+use quick_xml::events::{BytesEnd, BytesStart, Event};
|
|
|
use quick_xml::Reader;
|
|
|
use quick_xml::Writer;
|
|
|
+use clap::{Arg, App, SubCommand};
|
|
|
|
|
|
#[derive(Debug, Clone)]
|
|
|
struct Point {
|
|
@@ -56,54 +58,67 @@ impl Track {
|
|
|
}
|
|
|
|
|
|
fn to_xml(&self) {
|
|
|
-
|
|
|
let mut writer = Writer::new_with_indent(Cursor::new(Vec::new()), 32, 4); //32 is space character
|
|
|
|
|
|
-
|
|
|
let mut gpx_elem = BytesStart::owned(b"gpx".to_vec(), "gpx".len());
|
|
|
gpx_elem.push_attribute(("creator", "Pothole"));
|
|
|
assert!(writer.write_event(Event::Start(gpx_elem)).is_ok());
|
|
|
|
|
|
let trk_elem = BytesStart::owned(b"trk".to_vec(), "trk".len());
|
|
|
- assert!(writer.write_event(Event::Start(trk_elem)).is_ok());
|
|
|
+ assert!(writer.write_event(Event::Start(trk_elem)).is_ok());
|
|
|
|
|
|
- let mut prev_point = &self.points[0];
|
|
|
+ let mut prev_point = &self.points[0];
|
|
|
|
|
|
- let trkseg_elem = BytesStart::owned(b"trkseg".to_vec(), "trkseg".len());
|
|
|
- assert!(writer.write_event(Event::Start(trkseg_elem)).is_ok());
|
|
|
+ let trkseg_elem = BytesStart::owned(b"trkseg".to_vec(), "trkseg".len());
|
|
|
+ assert!(writer.write_event(Event::Start(trkseg_elem)).is_ok());
|
|
|
|
|
|
- for pt in &self.points {
|
|
|
- //segment detection
|
|
|
- let d = dist(prev_point, pt);
|
|
|
- prev_point = pt;
|
|
|
+ for pt in &self.points {
|
|
|
+ //segment detection
|
|
|
+ let d = dist(prev_point, pt);
|
|
|
+ prev_point = pt;
|
|
|
|
|
|
- if d > 0.5 {
|
|
|
- assert!(writer.write_event(Event::End(BytesEnd::borrowed(b"trkseg"))).is_ok());
|
|
|
+ if d > 0.5 {
|
|
|
+ assert!(
|
|
|
+ writer
|
|
|
+ .write_event(Event::End(BytesEnd::borrowed(b"trkseg")))
|
|
|
+ .is_ok()
|
|
|
+ );
|
|
|
let trkseg_elem = BytesStart::owned(b"trkseg".to_vec(), "trkseg".len());
|
|
|
assert!(writer.write_event(Event::Start(trkseg_elem)).is_ok());
|
|
|
- } else {
|
|
|
-
|
|
|
- }
|
|
|
-
|
|
|
- let mut pt_elem = BytesStart::owned(b"trkpt".to_vec(), "trkpt".len());
|
|
|
- pt_elem.push_attribute(("lat", pt.lat.to_string().as_str()));
|
|
|
- pt_elem.push_attribute(("lon", pt.long.to_string().as_str()));
|
|
|
- pt_elem.push_attribute(("ele", pt.ele.to_string().as_str()));
|
|
|
- assert!(writer.write_event(Event::Start(pt_elem)).is_ok());
|
|
|
- assert!(writer.write_event(Event::End(BytesEnd::borrowed(b"trkpt"))).is_ok());
|
|
|
+ } else {
|
|
|
|
|
|
}
|
|
|
|
|
|
+ let mut pt_elem = BytesStart::owned(b"trkpt".to_vec(), "trkpt".len());
|
|
|
+ pt_elem.push_attribute(("lat", pt.lat.to_string().as_str()));
|
|
|
+ pt_elem.push_attribute(("lon", pt.long.to_string().as_str()));
|
|
|
+ pt_elem.push_attribute(("ele", pt.ele.to_string().as_str()));
|
|
|
+ assert!(writer.write_event(Event::Start(pt_elem)).is_ok());
|
|
|
+ assert!(
|
|
|
+ writer
|
|
|
+ .write_event(Event::End(BytesEnd::borrowed(b"trkpt")))
|
|
|
+ .is_ok()
|
|
|
+ );
|
|
|
+ }
|
|
|
|
|
|
- assert!(writer.write_event(Event::End(BytesEnd::borrowed(b"trk"))).is_ok());
|
|
|
- assert!(writer.write_event(Event::End(BytesEnd::borrowed(b"gpx"))).is_ok());
|
|
|
-
|
|
|
+ assert!(
|
|
|
+ writer
|
|
|
+ .write_event(Event::End(BytesEnd::borrowed(b"trk")))
|
|
|
+ .is_ok()
|
|
|
+ );
|
|
|
+ assert!(
|
|
|
+ writer
|
|
|
+ .write_event(Event::End(BytesEnd::borrowed(b"gpx")))
|
|
|
+ .is_ok()
|
|
|
+ );
|
|
|
|
|
|
let result = writer.into_inner().into_inner();
|
|
|
// println!("{:?}", str::from_utf8( &result).unwrap());
|
|
|
|
|
|
- write_bytes(result, "track.gpx".to_string());
|
|
|
-
|
|
|
+
|
|
|
+ let mut name: String = self.name.clone();
|
|
|
+ name.push_str(".gpx");
|
|
|
+ write_bytes(result, name);
|
|
|
}
|
|
|
|
|
|
fn time(&self) -> chrono::Duration {
|
|
@@ -125,12 +140,11 @@ impl Track {
|
|
|
|
|
|
fn truncate_by_length(&mut self, length_km: f64) {
|
|
|
while self.len() > length_km {
|
|
|
- self.points.remove(0);
|
|
|
+ self.points.remove(0);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
fn parse(&mut self) {
|
|
|
-
|
|
|
// how far to average the track together
|
|
|
let sample_distance = 0.1;
|
|
|
// This is where a track is considered bad = min_speed_factor * your average over sample_distance
|
|
@@ -142,15 +156,16 @@ impl Track {
|
|
|
..Default::default()
|
|
|
};
|
|
|
|
|
|
- let mut analyzed_track = Track { name: "analyzed".to_string(),
|
|
|
+ let mut analyzed_track = Track {
|
|
|
+ name: "analyzed".to_string(),
|
|
|
..Default::default()
|
|
|
};
|
|
|
-
|
|
|
- let mut bad_track = Track { name: "bad".to_string(),
|
|
|
+
|
|
|
+ let mut bad_track = Track {
|
|
|
+ name: "bad".to_string(),
|
|
|
..Default::default()
|
|
|
};
|
|
|
|
|
|
-
|
|
|
for pt in &self.points {
|
|
|
// i = i + 1; if i > 30 {break};
|
|
|
fifo_track.points.push(pt.clone());
|
|
@@ -164,6 +179,84 @@ impl Track {
|
|
|
}
|
|
|
bad_track.to_xml();
|
|
|
}
|
|
|
+
|
|
|
+ fn from_xml(&mut self, filename: String) {
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ // Records for the pull parser
|
|
|
+ let mut current_point = Point::new();
|
|
|
+ let mut current_data = "".to_string();
|
|
|
+
|
|
|
+ println!("XML parse start.");
|
|
|
+
|
|
|
+ let mut reader = Reader::from_file(filename).unwrap();
|
|
|
+ reader.trim_text(true);
|
|
|
+ let mut buf = Vec::new();
|
|
|
+
|
|
|
+ // The `Reader` does not implement `Iterator` because it outputs borrowed data (`Cow`s)
|
|
|
+ loop {
|
|
|
+ match reader.read_event(&mut buf) {
|
|
|
+ Ok(Event::Start(ref e)) => {
|
|
|
+ // println!("{:?}", e.unescape_and_decode(&reader).unwrap());
|
|
|
+
|
|
|
+ match e.name() {
|
|
|
+ b"trkpt" => {
|
|
|
+ for a in e.attributes() {
|
|
|
+ let unwrapped_attr = a.unwrap();
|
|
|
+ let key = reader.decode(unwrapped_attr.key).into_owned();
|
|
|
+ let value =
|
|
|
+ unwrapped_attr.unescape_and_decode_value(&reader).unwrap();
|
|
|
+
|
|
|
+ match key.as_str() {
|
|
|
+ "lat" => current_point.lat = value.parse().unwrap(),
|
|
|
+ "lon" => current_point.long = value.parse().unwrap(),
|
|
|
+ "ele" => current_point.ele = value.parse().unwrap(),
|
|
|
+ _ => (),
|
|
|
+ }
|
|
|
+ }
|
|
|
+ ()
|
|
|
+ }
|
|
|
+ _ => (),
|
|
|
+ }
|
|
|
+ }
|
|
|
+ // current_data = text;
|
|
|
+ Ok(Event::Text(e)) => {
|
|
|
+ current_data = e.unescape_and_decode(&reader).unwrap();
|
|
|
+ // txt.push(e.unescape_and_decode(&reader).unwrap()),
|
|
|
+ }
|
|
|
+ Ok(Event::End(ref e)) => {
|
|
|
+ match e.name() {
|
|
|
+ b"ele" => {
|
|
|
+ current_point.ele = current_data.parse().unwrap();
|
|
|
+ }
|
|
|
+ b"name" => {
|
|
|
+ self.name = current_data.clone();
|
|
|
+ }
|
|
|
+ b"time" => {
|
|
|
+ match Utc.datetime_from_str(current_data.as_str(), "%FT%H:%M:%S%.3fZ") {
|
|
|
+ Ok(time) => current_point.time = time,
|
|
|
+ Err(err) => println!("=== TIME ERROR === {:?}", err),
|
|
|
+ }
|
|
|
+ }
|
|
|
+ b"trkpt" => {
|
|
|
+ // push a copy of the point to the track
|
|
|
+ self.points.push(current_point.clone());
|
|
|
+ }
|
|
|
+ _ => (),
|
|
|
+ }
|
|
|
+ }
|
|
|
+ Ok(Event::Eof) => break, // exits the loop when reaching end of file
|
|
|
+ Err(e) => panic!("Error at position {}: {:?}", reader.buffer_position(), e),
|
|
|
+ _ => (), // There are several other `Event`s we do not consider here
|
|
|
+ }
|
|
|
+
|
|
|
+ // if we don't keep a borrow elsewhere, we can clear the buffer to keep memory usage low
|
|
|
+ buf.clear();
|
|
|
+ }
|
|
|
+
|
|
|
+ println!("XML parse end.");
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
fn dist(p1: &Point, p2: &Point) -> f64 {
|
|
@@ -179,9 +272,7 @@ fn dist(p1: &Point, p2: &Point) -> f64 {
|
|
|
return r * c;
|
|
|
}
|
|
|
|
|
|
-
|
|
|
fn write_bytes(bytes_to_write: Vec<u8>, filename: String) {
|
|
|
-
|
|
|
let path = Path::new(filename.as_str());
|
|
|
let display = path.display();
|
|
|
|
|
@@ -191,105 +282,40 @@ fn write_bytes(bytes_to_write: Vec<u8>, filename: String) {
|
|
|
Ok(file) => file,
|
|
|
};
|
|
|
|
|
|
- // Write the `LOREM_IPSUM` string to `file`, returns `io::Result<()>`
|
|
|
+ // Write the `LOREM_IPSUM` string to `file`, returns `io::Result<()>`
|
|
|
match file.write_all("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n".as_bytes()) {
|
|
|
- Err(why) => {
|
|
|
- panic!("couldn't write to {}", display)
|
|
|
- },
|
|
|
+ Err(_why) => panic!("couldn't write to {}", display),
|
|
|
Ok(_) => (println!("Wrote header -> {}", display)),
|
|
|
}
|
|
|
|
|
|
match file.write_all(&bytes_to_write) {
|
|
|
- Err(why) => {
|
|
|
- panic!("couldn't write to {}", display)
|
|
|
- },
|
|
|
+ Err(_why) => panic!("couldn't write to {}", display),
|
|
|
Ok(_) => println!("Wrote data -> {}", display),
|
|
|
}
|
|
|
}
|
|
|
|
|
|
fn main() {
|
|
|
|
|
|
- let mut trk = Track {
|
|
|
- ..Default::default()
|
|
|
- };
|
|
|
|
|
|
- // Records for the pull parser
|
|
|
- let mut current_point = Point::new();
|
|
|
- let mut current_data = "".to_string();
|
|
|
-
|
|
|
- println!("XML parse start.");
|
|
|
-
|
|
|
- let mut reader = Reader::from_file("data/Day1-1.gpx").unwrap();
|
|
|
- reader.trim_text(true);
|
|
|
- let mut buf = Vec::new();
|
|
|
-
|
|
|
- // The `Reader` does not implement `Iterator` because it outputs borrowed data (`Cow`s)
|
|
|
- loop {
|
|
|
- match reader.read_event(&mut buf) {
|
|
|
- Ok(Event::Start(ref e)) => {
|
|
|
- // println!("{:?}", e.unescape_and_decode(&reader).unwrap());
|
|
|
-
|
|
|
- match e.name() {
|
|
|
- b"trkpt" => {
|
|
|
- for a in e.attributes() {
|
|
|
- let unwrapped_attr = a.unwrap();
|
|
|
- let key = reader.decode(unwrapped_attr.key).into_owned();
|
|
|
- let value = unwrapped_attr.unescape_and_decode_value(&reader).unwrap();
|
|
|
-
|
|
|
- match key.as_str() {
|
|
|
- "lat" => current_point.lat = value.parse().unwrap(),
|
|
|
- "lon" => current_point.long = value.parse().unwrap(),
|
|
|
- "ele" => current_point.ele = value.parse().unwrap(),
|
|
|
- _ => ()
|
|
|
- }
|
|
|
- }
|
|
|
- ()
|
|
|
- }
|
|
|
- _ => (),
|
|
|
- }
|
|
|
- }
|
|
|
- // current_data = text;
|
|
|
- Ok(Event::Text(e)) => {
|
|
|
- current_data = e.unescape_and_decode(&reader).unwrap();
|
|
|
- // txt.push(e.unescape_and_decode(&reader).unwrap()),
|
|
|
- }
|
|
|
- Ok(Event::End(ref e)) => {
|
|
|
-
|
|
|
-
|
|
|
- match e.name() {
|
|
|
- b"ele" => {
|
|
|
- current_point.ele = current_data.parse().unwrap();
|
|
|
- },
|
|
|
- b"name" => {
|
|
|
- trk.name = current_data.clone();
|
|
|
- },
|
|
|
- b"time" => {
|
|
|
- match Utc.datetime_from_str(current_data.as_str(), "%FT%H:%M:%S%.3fZ") {
|
|
|
- Ok(time) => current_point.time = time,
|
|
|
- Err(err) => println!("=== TIME ERROR === {:?}", err),
|
|
|
- }
|
|
|
- },
|
|
|
- b"trkpt" => {
|
|
|
- // push a copy of the point to the track
|
|
|
- trk.points.push(current_point.clone());
|
|
|
- },
|
|
|
- _ => ()
|
|
|
- }
|
|
|
+let matches = App::new("Pothole")
|
|
|
+ .arg(Arg::with_name("track")
|
|
|
+ .long("track")
|
|
|
+ .required(true)
|
|
|
+ .value_name("FILE")
|
|
|
+ .help("GPX file to process")
|
|
|
+ .takes_value(true))
|
|
|
+ .get_matches();
|
|
|
|
|
|
+ // Gets a value for config if supplied by user, or defaults to "default.conf"
|
|
|
+ let trackfile = matches.value_of("track").unwrap_or("data/Day1-1.gpx");
|
|
|
|
|
|
|
|
|
+ let mut trk = Track {
|
|
|
+ ..Default::default()
|
|
|
+ };
|
|
|
|
|
|
- }
|
|
|
- Ok(Event::Eof) => break, // exits the loop when reaching end of file
|
|
|
- Err(e) => panic!("Error at position {}: {:?}", reader.buffer_position(), e),
|
|
|
- _ => (), // There are several other `Event`s we do not consider here
|
|
|
- }
|
|
|
-
|
|
|
- // if we don't keep a borrow elsewhere, we can clear the buffer to keep memory usage low
|
|
|
- buf.clear();
|
|
|
- }
|
|
|
+ trk.from_xml(trackfile.to_string());
|
|
|
|
|
|
- println!("XML parse end.");
|
|
|
|
|
|
println!("Track name: {:?}", trk.name);
|
|
|
println!("Distance: {:?} km", trk.len());
|