Add gpx file generation

This commit is contained in:
Sofia 2020-08-24 23:24:26 +03:00
parent 3e535df8a8
commit b5c93017a4
7 changed files with 210 additions and 8 deletions

3
.gitignore vendored
View File

@ -1,2 +1,3 @@
/target /target
secret.toml secret.toml
*.gpx

101
Cargo.lock generated
View File

@ -1,5 +1,20 @@
# This file is automatically @generated by Cargo. # This file is automatically @generated by Cargo.
# It is not intended for manual editing. # It is not intended for manual editing.
[[package]]
name = "addr2line"
version = "0.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1b6a2d3371669ab3ca9797670853d61402b03d0b4b9ebf33d677dfa720203072"
dependencies = [
"gimli",
]
[[package]]
name = "adler"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ee2a4ec343196209d6594e19543ae87a39f96d5534d7174822a3ad825dd6ed7e"
[[package]] [[package]]
name = "argh" name = "argh"
version = "0.1.3" version = "0.1.3"
@ -29,12 +44,32 @@ version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e1ba68f4276a778591e36a0c348a269888f3a177c8d2054969389e3b59611ff5" checksum = "e1ba68f4276a778591e36a0c348a269888f3a177c8d2054969389e3b59611ff5"
[[package]]
name = "assert_approx_eq"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3c07dab4369547dbe5114677b33fbbf724971019f3818172d59a97a61c774ffd"
[[package]] [[package]]
name = "autocfg" name = "autocfg"
version = "1.0.1" version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a"
[[package]]
name = "backtrace"
version = "0.3.50"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "46254cf2fdcdf1badb5934448c1bcbe046a56537b3987d96c51a7afc5d03f293"
dependencies = [
"addr2line",
"cfg-if",
"libc",
"miniz_oxide",
"object",
"rustc-demangle",
]
[[package]] [[package]]
name = "base64" name = "base64"
version = "0.10.1" version = "0.10.1"
@ -79,6 +114,43 @@ dependencies = [
"time", "time",
] ]
[[package]]
name = "error-chain"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ff511d5dc435d703f4971bc399647c9bc38e20cb41452e3b9feb4765419ed3f3"
dependencies = [
"backtrace",
]
[[package]]
name = "geo-types"
version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "866e8f6dbd2218b05ea8a25daa1bfac32b0515fe7e0a37cb6a7b9ed0ed82a07e"
dependencies = [
"num-traits",
]
[[package]]
name = "gimli"
version = "0.22.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "aaf91faf136cb47367fa430cd46e37a788775e7fa104f8b4bcb3861dc389b724"
[[package]]
name = "gpx"
version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d96f48f1635cee5d19d368d8142fb38b3ca5a6ae8b805f0cf42590751f3b33be"
dependencies = [
"assert_approx_eq",
"chrono",
"error-chain",
"geo-types",
"xml-rs",
]
[[package]] [[package]]
name = "heck" name = "heck"
version = "0.3.1" version = "0.3.1"
@ -124,6 +196,15 @@ dependencies = [
"cfg-if", "cfg-if",
] ]
[[package]]
name = "miniz_oxide"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4d7559a8a40d0f97e1edea3220f698f78b1c5ab67532e49f68fde3910323b722"
dependencies = [
"adler",
]
[[package]] [[package]]
name = "minreq" name = "minreq"
version = "2.2.1" version = "2.2.1"
@ -157,6 +238,12 @@ dependencies = [
"autocfg", "autocfg",
] ]
[[package]]
name = "object"
version = "0.20.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1ab52be62400ca80aa00285d25253d7f7c437b7375c4de678f5405d3afe82ca5"
[[package]] [[package]]
name = "once_cell" name = "once_cell"
version = "1.4.1" version = "1.4.1"
@ -196,6 +283,12 @@ dependencies = [
"winapi", "winapi",
] ]
[[package]]
name = "rustc-demangle"
version = "0.1.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4c691c0e608126e00913e33f0ccf3727d5fc84573623b8d65b2df340b5201783"
[[package]] [[package]]
name = "rustls" name = "rustls"
version = "0.16.0" version = "0.16.0"
@ -279,6 +372,8 @@ version = "0.1.0"
dependencies = [ dependencies = [
"argh", "argh",
"chrono", "chrono",
"geo-types",
"gpx",
"minreq", "minreq",
"serde", "serde",
"toml", "toml",
@ -425,3 +520,9 @@ name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0" version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "xml-rs"
version = "0.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b07db065a5cf61a7e4ba64f29e67db906fb1787316516c4e6e5ff0fea1efcd8a"

View File

@ -9,4 +9,6 @@ serde = { version = "1.0", features = ["derive"] }
toml = "0.5" toml = "0.5"
minreq = { version = "2.2.0", features = ["https", "json-using-serde"] } minreq = { version = "2.2.0", features = ["https", "json-using-serde"] }
chrono = "0.4" chrono = "0.4"
argh = "0.1" argh = "0.1"
gpx = "0.8"
geo-types = "*"

View File

@ -109,9 +109,9 @@ pub struct LocationModel {
#[serde(rename(deserialize = "type"))] #[serde(rename(deserialize = "type"))]
pub loc_type: Option<String>, pub loc_type: Option<String>,
#[serde(rename(deserialize = "coordinateLat"))] #[serde(rename(deserialize = "coordinateLat"))]
pub lat: Option<f32>, pub lat: Option<f64>,
#[serde(rename(deserialize = "coordinateLng"))] #[serde(rename(deserialize = "coordinateLng"))]
pub lon: Option<f32>, pub lon: Option<f64>,
pub accuracy: Option<f32>, pub accuracy: Option<f32>,
} }

View File

@ -10,6 +10,7 @@ pub enum GenericError {
ChronoParseError(chrono::ParseError), ChronoParseError(chrono::ParseError),
IOError(io::Error), IOError(io::Error),
FromUTF8Error(std::string::FromUtf8Error), FromUTF8Error(std::string::FromUtf8Error),
GPXError(gpx::errors::Error),
MessagedError(String, Option<Box<GenericError>>), MessagedError(String, Option<Box<GenericError>>),
} }
@ -49,6 +50,12 @@ impl From<std::string::FromUtf8Error> for GenericError {
} }
} }
impl From<gpx::errors::Error> for GenericError {
fn from(error: gpx::errors::Error) -> Self {
GenericError::GPXError(error)
}
}
impl Display for GenericError { impl Display for GenericError {
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> { fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> {
let err = match self { let err = match self {
@ -66,6 +73,7 @@ impl Display for GenericError {
msg, msg,
err.as_ref().map(|e| e.to_string()).unwrap_or(String::new()) err.as_ref().map(|e| e.to_string()).unwrap_or(String::new())
), ),
GenericError::GPXError(e) => format!("GPX error: {}", e),
}; };
write!(f, "{}", err) write!(f, "{}", err)
} }

86
src/gpx.rs Normal file
View File

@ -0,0 +1,86 @@
use super::{Config, GenericError, LocationModel, TagModel};
use chrono::offset::{LocalResult, TimeZone, Utc as UTC_ZONE};
use chrono::{DateTime, NaiveDateTime, Utc};
use geo_types::Point;
use gpx::{Gpx, GpxVersion, Metadata, Person, Route, Track, TrackSegment, Waypoint};
use std::fs::File;
pub fn generate_gpx(
tag: TagModel,
locations: Vec<(NaiveDateTime, LocationModel)>,
config: &Config,
) -> Result<Gpx, GenericError> {
let route = Route::default();
let mut points = Vec::new();
for location in locations {
points.push(get_waypoint(location)?);
}
let segment = TrackSegment { points };
let track = Track {
name: None,
comment: None,
description: None,
source: None,
links: Vec::new(),
_type: None,
segments: vec![segment],
};
let gpx = Gpx {
version: GpxVersion::Gpx11,
metadata: Some(get_metadata(&tag, config)?),
waypoints: Vec::new(),
tracks: vec![track],
route: route,
};
Ok(gpx)
}
pub fn write_gpx(gpx: &Gpx) -> Result<(), GenericError> {
let file = File::create("test.gpx").unwrap();
gpx::write(&gpx, file)?;
Ok(())
}
fn get_waypoint(point: (NaiveDateTime, LocationModel)) -> Result<Waypoint, GenericError> {
let (naive_time, location) = point;
let now = Utc::now();
let time: DateTime<UTC_ZONE> = DateTime::from_utc(naive_time, now.offset().clone());
if let (Some(lat), Some(lon)) = (location.lat, location.lon) {
let mut waypoint = Waypoint::new(Point::new(lon, lat));
waypoint.time = Some(time);
waypoint.source = location.loc_type;
Ok(waypoint)
} else {
Err(GenericError::MessagedError(
"No coordinates for a given point".to_owned(),
None,
))
}
}
fn get_metadata(tag: &TagModel, config: &Config) -> Result<Metadata, GenericError> {
let nick_text = match tag.get_nick(config) {
Some(nick) => format!(" - {}", nick),
None => String::new(),
};
Ok(Metadata {
name: Some(format!("{}{}", tag.get_id()?, nick_text)),
description: None,
author: Some(Person {
name: Some("Sofia".to_owned()),
email: Some("sofia.talarmo@gmail.com".to_owned()),
link: None,
}),
links: Vec::new(),
time: Some(Utc::now()),
keywords: None,
bounds: None,
})
}

View File

@ -2,6 +2,7 @@ mod api;
mod cmd; mod cmd;
mod config; mod config;
mod errors; mod errors;
mod gpx;
use api::{LocationModel, TagModel, API}; use api::{LocationModel, TagModel, API};
use chrono::offset::Local; use chrono::offset::Local;
@ -36,11 +37,13 @@ fn from_env(env: EnvOpt) -> Result<(), GenericError> {
None => None, None => None,
}; };
let locs = run(&config, opt.device, since, until)?; let (tag, locs) = run(&config, opt.device, since, until)?;
dbg!( dbg!(
&locs.iter().map(|loc| loc.0).collect::<Vec<NaiveDateTime>>(), &locs.iter().map(|loc| loc.0).collect::<Vec<NaiveDateTime>>(),
locs.len(), locs.len(),
); );
let gpx = gpx::generate_gpx(tag, locs, &config)?;
gpx::write_gpx(&gpx)?;
Ok(()) Ok(())
} }
Subcommand::Init(opt) => { Subcommand::Init(opt) => {
@ -100,11 +103,12 @@ fn run(
tag_str: String, tag_str: String,
from: Option<NaiveDateTime>, from: Option<NaiveDateTime>,
to: Option<NaiveDateTime>, to: Option<NaiveDateTime>,
) -> Result<Vec<(NaiveDateTime, LocationModel)>, GenericError> { ) -> Result<(TagModel, Vec<(NaiveDateTime, LocationModel)>), GenericError> {
let mut api = API::new(config.clone()); let mut api = API::new(config.clone());
let tags = api.get_tags()?; let tags = api.get_tags()?;
let tag_id = find_tag(tag_str, &tags, config)?.get_id()?; let tag = find_tag(tag_str, &tags, config)?;
let tag_id = tag.get_id()?;
print!("Preparing..\r"); print!("Preparing..\r");
std::io::stdout().lock().flush().ok(); std::io::stdout().lock().flush().ok();
@ -152,7 +156,7 @@ fn run(
locations.sort_by(|loc1, loc2| loc2.timestamp.cmp(&loc1.timestamp)); locations.sort_by(|loc1, loc2| loc2.timestamp.cmp(&loc1.timestamp));
let locs = API::get_between(&locations, from, to, false, &config); let locs = API::get_between(&locations, from, to, false, &config);
Ok(locs) Ok((tag, locs))
} }
fn exp_time(api: &API, reqs_left: u64) -> Duration { fn exp_time(api: &API, reqs_left: u64) -> Duration {