Add gpx file generation
This commit is contained in:
parent
3e535df8a8
commit
b5c93017a4
1
.gitignore
vendored
1
.gitignore
vendored
@ -1,2 +1,3 @@
|
||||
/target
|
||||
secret.toml
|
||||
*.gpx
|
101
Cargo.lock
generated
101
Cargo.lock
generated
@ -1,5 +1,20 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# 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]]
|
||||
name = "argh"
|
||||
version = "0.1.3"
|
||||
@ -29,12 +44,32 @@ version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e1ba68f4276a778591e36a0c348a269888f3a177c8d2054969389e3b59611ff5"
|
||||
|
||||
[[package]]
|
||||
name = "assert_approx_eq"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3c07dab4369547dbe5114677b33fbbf724971019f3818172d59a97a61c774ffd"
|
||||
|
||||
[[package]]
|
||||
name = "autocfg"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
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]]
|
||||
name = "base64"
|
||||
version = "0.10.1"
|
||||
@ -79,6 +114,43 @@ dependencies = [
|
||||
"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]]
|
||||
name = "heck"
|
||||
version = "0.3.1"
|
||||
@ -124,6 +196,15 @@ dependencies = [
|
||||
"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]]
|
||||
name = "minreq"
|
||||
version = "2.2.1"
|
||||
@ -157,6 +238,12 @@ dependencies = [
|
||||
"autocfg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "object"
|
||||
version = "0.20.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1ab52be62400ca80aa00285d25253d7f7c437b7375c4de678f5405d3afe82ca5"
|
||||
|
||||
[[package]]
|
||||
name = "once_cell"
|
||||
version = "1.4.1"
|
||||
@ -196,6 +283,12 @@ dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustc-demangle"
|
||||
version = "0.1.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4c691c0e608126e00913e33f0ccf3727d5fc84573623b8d65b2df340b5201783"
|
||||
|
||||
[[package]]
|
||||
name = "rustls"
|
||||
version = "0.16.0"
|
||||
@ -279,6 +372,8 @@ version = "0.1.0"
|
||||
dependencies = [
|
||||
"argh",
|
||||
"chrono",
|
||||
"geo-types",
|
||||
"gpx",
|
||||
"minreq",
|
||||
"serde",
|
||||
"toml",
|
||||
@ -425,3 +520,9 @@ name = "winapi-x86_64-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
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"] }
|
||||
chrono = "0.4"
|
||||
argh = "0.1"
|
||||
gpx = "0.8"
|
||||
geo-types = "*"
|
@ -109,9 +109,9 @@ pub struct LocationModel {
|
||||
#[serde(rename(deserialize = "type"))]
|
||||
pub loc_type: Option<String>,
|
||||
#[serde(rename(deserialize = "coordinateLat"))]
|
||||
pub lat: Option<f32>,
|
||||
pub lat: Option<f64>,
|
||||
#[serde(rename(deserialize = "coordinateLng"))]
|
||||
pub lon: Option<f32>,
|
||||
pub lon: Option<f64>,
|
||||
pub accuracy: Option<f32>,
|
||||
}
|
||||
|
||||
|
@ -10,6 +10,7 @@ pub enum GenericError {
|
||||
ChronoParseError(chrono::ParseError),
|
||||
IOError(io::Error),
|
||||
FromUTF8Error(std::string::FromUtf8Error),
|
||||
GPXError(gpx::errors::Error),
|
||||
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 {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> {
|
||||
let err = match self {
|
||||
@ -66,6 +73,7 @@ impl Display for GenericError {
|
||||
msg,
|
||||
err.as_ref().map(|e| e.to_string()).unwrap_or(String::new())
|
||||
),
|
||||
GenericError::GPXError(e) => format!("GPX error: {}", e),
|
||||
};
|
||||
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 config;
|
||||
mod errors;
|
||||
mod gpx;
|
||||
|
||||
use api::{LocationModel, TagModel, API};
|
||||
use chrono::offset::Local;
|
||||
@ -36,11 +37,13 @@ fn from_env(env: EnvOpt) -> Result<(), GenericError> {
|
||||
None => None,
|
||||
};
|
||||
|
||||
let locs = run(&config, opt.device, since, until)?;
|
||||
let (tag, locs) = run(&config, opt.device, since, until)?;
|
||||
dbg!(
|
||||
&locs.iter().map(|loc| loc.0).collect::<Vec<NaiveDateTime>>(),
|
||||
locs.len(),
|
||||
);
|
||||
let gpx = gpx::generate_gpx(tag, locs, &config)?;
|
||||
gpx::write_gpx(&gpx)?;
|
||||
Ok(())
|
||||
}
|
||||
Subcommand::Init(opt) => {
|
||||
@ -100,11 +103,12 @@ fn run(
|
||||
tag_str: String,
|
||||
from: Option<NaiveDateTime>,
|
||||
to: Option<NaiveDateTime>,
|
||||
) -> Result<Vec<(NaiveDateTime, LocationModel)>, GenericError> {
|
||||
) -> Result<(TagModel, Vec<(NaiveDateTime, LocationModel)>), GenericError> {
|
||||
let mut api = API::new(config.clone());
|
||||
|
||||
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");
|
||||
std::io::stdout().lock().flush().ok();
|
||||
@ -152,7 +156,7 @@ fn run(
|
||||
locations.sort_by(|loc1, loc2| loc2.timestamp.cmp(&loc1.timestamp));
|
||||
|
||||
let locs = API::get_between(&locations, from, to, false, &config);
|
||||
Ok(locs)
|
||||
Ok((tag, locs))
|
||||
}
|
||||
|
||||
fn exp_time(api: &API, reqs_left: u64) -> Duration {
|
||||
|
Loading…
Reference in New Issue
Block a user