Add gpx file generation
This commit is contained in:
parent
3e535df8a8
commit
b5c93017a4
1
.gitignore
vendored
1
.gitignore
vendored
@ -1,2 +1,3 @@
|
|||||||
/target
|
/target
|
||||||
secret.toml
|
secret.toml
|
||||||
|
*.gpx
|
101
Cargo.lock
generated
101
Cargo.lock
generated
@ -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"
|
||||||
|
@ -10,3 +10,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 = "*"
|
@ -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>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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
86
src/gpx.rs
Normal 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,
|
||||||
|
})
|
||||||
|
}
|
12
src/main.rs
12
src/main.rs
@ -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 {
|
||||||
|
Loading…
Reference in New Issue
Block a user