mirror of
https://github.com/gpxstudio/gpx.studio.git
synced 2026-10-27 02:27:18 +00:00
progress
This commit is contained in:
1 parent
41b9116c6a
commit
97d597e139
181 files changed
+1970
-2643
No files matched your search
+98
-126
@@ -1,22 +1,15 @@
|
||||
//! Editing the metadata, the style, the structure and the waypoints of the files, and the tools:
|
||||
//! reversing, cropping, reducing, timestamps, merging, cleaning...
|
||||
|
||||
use gpx_engine::{self as engine, Command, LngLat, LngLatBounds};
|
||||
use gpx_engine::{api, CleanType, MergeType, SplitType};
|
||||
use tauri::State;
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::dto::*;
|
||||
use crate::state::{act, edit, AppState};
|
||||
use crate::state::AppState;
|
||||
|
||||
#[tauri::command(async)]
|
||||
pub fn metadata(state: State<AppState>, name: String, desc: String) -> Outcome {
|
||||
edit!(
|
||||
state,
|
||||
Command::Metadata(engine::Metadata {
|
||||
name: &name,
|
||||
desc: &desc,
|
||||
})
|
||||
)
|
||||
pub fn metadata(state: State<AppState>, name: String, desc: String) -> api::Outcome {
|
||||
state.run(move |e| api::metadata(e, &name, &desc))
|
||||
}
|
||||
|
||||
#[tauri::command(async)]
|
||||
@@ -25,25 +18,18 @@ pub fn style(
|
||||
color: Option<String>,
|
||||
opacity: Option<f64>,
|
||||
width: Option<f64>,
|
||||
) -> Outcome {
|
||||
edit!(
|
||||
state,
|
||||
Command::Style(engine::Style {
|
||||
color: color.as_deref(),
|
||||
opacity,
|
||||
width,
|
||||
})
|
||||
)
|
||||
) -> api::Outcome {
|
||||
state.run(move |e| api::style(e, color.as_deref(), opacity, width))
|
||||
}
|
||||
|
||||
#[tauri::command(async)]
|
||||
pub fn new_track(state: State<AppState>) -> Outcome {
|
||||
edit!(state, Command::NewTrack(engine::NewTrack))
|
||||
pub fn new_track(state: State<AppState>) -> api::Outcome {
|
||||
state.run(api::new_track)
|
||||
}
|
||||
|
||||
#[tauri::command(async)]
|
||||
pub fn new_track_segment(state: State<AppState>) -> Outcome {
|
||||
edit!(state, Command::NewTrackSegment(engine::NewTrackSegment))
|
||||
pub fn new_track_segment(state: State<AppState>) -> api::Outcome {
|
||||
state.run(api::new_track_segment)
|
||||
}
|
||||
|
||||
// Waypoints
|
||||
@@ -59,19 +45,8 @@ pub fn new_waypoint(
|
||||
desc: String,
|
||||
icon: String,
|
||||
link: String,
|
||||
) -> Outcome {
|
||||
edit!(
|
||||
state,
|
||||
Command::NewWaypoint(engine::NewWaypoint {
|
||||
lng,
|
||||
lat,
|
||||
ele,
|
||||
name: &name,
|
||||
desc: &desc,
|
||||
icon: &icon,
|
||||
link: &link,
|
||||
})
|
||||
)
|
||||
) -> api::Outcome {
|
||||
state.run(move |e| api::new_waypoint(e, lng, lat, ele, &name, &desc, &icon, &link))
|
||||
}
|
||||
|
||||
#[tauri::command(async)]
|
||||
@@ -82,28 +57,13 @@ pub fn move_waypoint(
|
||||
lng: f64,
|
||||
lat: f64,
|
||||
ele: f64,
|
||||
) -> Outcome {
|
||||
edit!(
|
||||
state,
|
||||
Command::MoveWaypoint(engine::MoveWaypoint {
|
||||
file_id: engine::FileId(file_id),
|
||||
waypoint_id: engine::WaypointId(waypoint_id),
|
||||
lng,
|
||||
lat,
|
||||
ele,
|
||||
})
|
||||
)
|
||||
) -> api::Outcome {
|
||||
state.run(move |e| api::move_waypoint(e, file_id, waypoint_id, lng, lat, ele))
|
||||
}
|
||||
|
||||
#[tauri::command(async)]
|
||||
pub fn delete_waypoint(state: State<AppState>, file_id: Uuid, waypoint_id: Uuid) -> Outcome {
|
||||
edit!(
|
||||
state,
|
||||
Command::DeleteWaypoint(engine::DeleteWaypoint {
|
||||
file_id: engine::FileId(file_id),
|
||||
waypoint_id: engine::WaypointId(waypoint_id),
|
||||
})
|
||||
)
|
||||
pub fn delete_waypoint(state: State<AppState>, file_id: Uuid, waypoint_id: Uuid) -> api::Outcome {
|
||||
state.run(move |e| api::delete_waypoint(e, file_id, waypoint_id))
|
||||
}
|
||||
|
||||
#[tauri::command(async)]
|
||||
@@ -119,46 +79,43 @@ pub fn update_waypoint(
|
||||
desc: String,
|
||||
icon: String,
|
||||
link: String,
|
||||
) -> Outcome {
|
||||
edit!(
|
||||
state,
|
||||
Command::EditWaypoint(engine::EditWaypoint {
|
||||
file_id: engine::FileId(file_id),
|
||||
waypoint_id: engine::WaypointId(waypoint_id),
|
||||
) -> api::Outcome {
|
||||
state.run(move |e| {
|
||||
api::update_waypoint(
|
||||
e,
|
||||
file_id,
|
||||
waypoint_id,
|
||||
lng,
|
||||
lat,
|
||||
ele,
|
||||
name: &name,
|
||||
desc: &desc,
|
||||
icon: &icon,
|
||||
link: &link,
|
||||
})
|
||||
)
|
||||
&name,
|
||||
&desc,
|
||||
&icon,
|
||||
&link,
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
// Tools
|
||||
|
||||
#[tauri::command(async)]
|
||||
pub fn reverse(state: State<AppState>) -> Outcome {
|
||||
edit!(state, Command::Reverse(engine::Reverse))
|
||||
pub fn reverse(state: State<AppState>) -> api::Outcome {
|
||||
state.run(api::reverse)
|
||||
}
|
||||
|
||||
#[tauri::command(async)]
|
||||
pub fn round_trip(state: State<AppState>) -> Outcome {
|
||||
edit!(state, Command::RoundTrip(engine::RoundTrip))
|
||||
pub fn round_trip(state: State<AppState>) -> api::Outcome {
|
||||
state.run(api::round_trip)
|
||||
}
|
||||
|
||||
#[tauri::command(async)]
|
||||
pub fn crop(state: State<AppState>, start: u32, end: u32) -> Outcome {
|
||||
if start > end {
|
||||
return Outcome::failed(format!("the range {start}..{end} is reversed"));
|
||||
}
|
||||
edit!(state, Command::Crop(engine::Crop { start, end }))
|
||||
pub fn crop(state: State<AppState>, start: u32, end: u32) -> api::Outcome {
|
||||
state.run(move |e| api::crop(e, start, end))
|
||||
}
|
||||
|
||||
#[tauri::command(async)]
|
||||
pub fn reduce(state: State<AppState>, tolerance: f64) -> Outcome {
|
||||
edit!(state, Command::Reduce(engine::Reduce { tolerance }))
|
||||
pub fn reduce(state: State<AppState>, tolerance: f64) -> api::Outcome {
|
||||
state.run(move |e| api::reduce(e, tolerance))
|
||||
}
|
||||
|
||||
#[tauri::command(async)]
|
||||
@@ -167,14 +124,8 @@ pub fn change_timestamps(
|
||||
start_time: f64,
|
||||
speed: f64,
|
||||
ratio: f64,
|
||||
) -> Outcome {
|
||||
edit!(
|
||||
state,
|
||||
Command::Time(engine::Time {
|
||||
start_time: start_time as i64,
|
||||
kind: engine::TimeKind::Change { speed, ratio },
|
||||
})
|
||||
)
|
||||
) -> api::Outcome {
|
||||
state.run(move |e| api::change_timestamps(e, start_time, speed, ratio))
|
||||
}
|
||||
|
||||
#[tauri::command(async)]
|
||||
@@ -182,35 +133,23 @@ pub fn create_artificial_timestamps(
|
||||
state: State<AppState>,
|
||||
start_time: f64,
|
||||
total_time: f64,
|
||||
) -> Outcome {
|
||||
edit!(
|
||||
state,
|
||||
Command::Time(engine::Time {
|
||||
start_time: start_time as i64,
|
||||
kind: engine::TimeKind::Artificial { total_time },
|
||||
})
|
||||
)
|
||||
) -> api::Outcome {
|
||||
state.run(move |e| api::create_artificial_timestamps(e, start_time, total_time))
|
||||
}
|
||||
|
||||
#[tauri::command(async)]
|
||||
pub fn merge(state: State<AppState>, merge_type: MergeType, remove_gaps: bool) -> Outcome {
|
||||
edit!(
|
||||
state,
|
||||
Command::Merge(engine::Merge {
|
||||
type_: merge_type.into(),
|
||||
remove_gaps,
|
||||
})
|
||||
)
|
||||
pub fn merge(state: State<AppState>, merge_type: MergeType, remove_gaps: bool) -> api::Outcome {
|
||||
state.run(move |e| api::merge(e, merge_type, remove_gaps))
|
||||
}
|
||||
|
||||
#[tauri::command(async)]
|
||||
pub fn extract(state: State<AppState>) -> Outcome {
|
||||
edit!(state, Command::Extract(engine::Extract))
|
||||
pub fn extract(state: State<AppState>) -> api::Outcome {
|
||||
state.run(api::extract)
|
||||
}
|
||||
|
||||
#[tauri::command(async)]
|
||||
pub fn elevation(state: State<AppState>, ele: Vec<f64>) -> Outcome {
|
||||
edit!(state, Command::Elevation(engine::Elevation { ele: &ele }))
|
||||
pub fn elevation(state: State<AppState>, ele: Vec<f64>) -> api::Outcome {
|
||||
state.run(move |e| api::elevation(e, &ele))
|
||||
}
|
||||
|
||||
#[tauri::command(async)]
|
||||
@@ -224,23 +163,56 @@ pub fn clean(
|
||||
clean_type: CleanType,
|
||||
trkpt: bool,
|
||||
wpt: bool,
|
||||
) -> Outcome {
|
||||
edit!(
|
||||
state,
|
||||
Command::Clean(engine::Clean {
|
||||
bounds: LngLatBounds {
|
||||
sw: LngLat {
|
||||
lng: west,
|
||||
lat: south,
|
||||
},
|
||||
ne: LngLat {
|
||||
lng: east,
|
||||
lat: north,
|
||||
},
|
||||
},
|
||||
type_: clean_type.into(),
|
||||
trkpt,
|
||||
wpt,
|
||||
})
|
||||
)
|
||||
) -> api::Outcome {
|
||||
state.run(move |e| api::clean(e, west, south, east, north, clean_type, trkpt, wpt))
|
||||
}
|
||||
|
||||
// Routing
|
||||
|
||||
#[tauri::command(async)]
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn route(
|
||||
state: State<AppState>,
|
||||
revision: u32,
|
||||
start: u32,
|
||||
end: u32,
|
||||
lng: Vec<f64>,
|
||||
lat: Vec<f64>,
|
||||
ele: Vec<f64>,
|
||||
attributes: api::RouteAttributes,
|
||||
anchors: Vec<u32>,
|
||||
) -> api::Outcome {
|
||||
state.run(move |e| {
|
||||
api::route(
|
||||
e,
|
||||
revision,
|
||||
start,
|
||||
end,
|
||||
&lng,
|
||||
&lat,
|
||||
&ele,
|
||||
&attributes,
|
||||
&anchors,
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
#[tauri::command(async)]
|
||||
pub fn insert_anchor(state: State<AppState>, revision: u32, lng: f64, lat: f64) -> api::Outcome {
|
||||
state.run(move |e| api::insert_anchor(e, revision, lng, lat))
|
||||
}
|
||||
|
||||
#[tauri::command(async)]
|
||||
pub fn change_loop_start(state: State<AppState>, revision: u32, index: u32) -> api::Outcome {
|
||||
state.run(move |e| api::change_loop_start(e, revision, index))
|
||||
}
|
||||
|
||||
#[tauri::command(async)]
|
||||
pub fn split(
|
||||
state: State<AppState>,
|
||||
revision: u32,
|
||||
at: u32,
|
||||
split_type: SplitType,
|
||||
) -> api::Outcome {
|
||||
state.run(move |e| api::split(e, revision, at, split_type))
|
||||
}
|
||||
+37
-84
@@ -1,12 +1,11 @@
|
||||
//! Creating, loading, deleting, moving, copying and pasting files and their elements, and undoing.
|
||||
|
||||
use gpx_engine::{self as engine, Action, Command};
|
||||
use gpx_engine::api;
|
||||
use tauri::ipc::{InvokeBody, Request};
|
||||
use tauri::State;
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::dto::*;
|
||||
use crate::state::{act, edit, AppState};
|
||||
use crate::state::AppState;
|
||||
|
||||
#[tauri::command(async)]
|
||||
pub fn new_file(
|
||||
@@ -15,73 +14,35 @@ pub fn new_file(
|
||||
lng: Option<f64>,
|
||||
lat: Option<f64>,
|
||||
ele: Option<f64>,
|
||||
) -> Outcome {
|
||||
let trackpoint = lng.zip(lat).map(|(lng, lat)| engine::NewTrackpoint {
|
||||
lng,
|
||||
lat,
|
||||
ele: ele.unwrap_or_default(),
|
||||
});
|
||||
edit!(
|
||||
state,
|
||||
Command::New(engine::New {
|
||||
name: &name,
|
||||
trackpoint,
|
||||
})
|
||||
)
|
||||
) -> api::Outcome {
|
||||
state.run(move |e| api::new_file(e, &name, lng, lat, ele))
|
||||
}
|
||||
|
||||
/// The files are in the body, one after the other, the headers `lengths` and `names` (JSON) telling
|
||||
/// the number of bytes and the name of each one.
|
||||
/// The files are in the body, one after the other, the headers `lengths` and `names` (JSON,
|
||||
/// percent-encoded) telling the number of bytes and the name of each one.
|
||||
#[tauri::command(async)]
|
||||
pub fn load_files(state: State<AppState>, request: Request<'_>) -> Outcome {
|
||||
pub fn load_files(state: State<AppState>, request: Request<'_>) -> api::Outcome {
|
||||
let InvokeBody::Raw(data) = request.body() else {
|
||||
return Outcome::failed("the files are missing");
|
||||
return api::Outcome::failed("the files are missing");
|
||||
};
|
||||
let header = |key: &str| {
|
||||
request
|
||||
.headers()
|
||||
.get(key)
|
||||
.and_then(|value| value.to_str().ok())
|
||||
.and_then(urlencoding_decode)
|
||||
.and_then(percent_decode)
|
||||
};
|
||||
let (Some(lengths), Some(names)) = (
|
||||
header("lengths").and_then(|json| serde_json::from_str::<Vec<usize>>(&json).ok()),
|
||||
header("names").and_then(|json| serde_json::from_str::<Vec<String>>(&json).ok()),
|
||||
) else {
|
||||
return Outcome::failed("the lengths or the names of the files are not valid");
|
||||
return api::Outcome::failed("the lengths or the names of the files are not valid");
|
||||
};
|
||||
if lengths.len() != names.len() {
|
||||
return Outcome::failed(format!(
|
||||
"{} lengths for {} names",
|
||||
lengths.len(),
|
||||
names.len()
|
||||
));
|
||||
}
|
||||
let (data, mut offset) = (data.clone(), 0);
|
||||
let mut ranges = Vec::with_capacity(names.len());
|
||||
for (length, name) in lengths.iter().zip(&names) {
|
||||
let end = offset + length;
|
||||
if end > data.len() {
|
||||
return Outcome::failed(format!("the data of {name} is shorter than its length"));
|
||||
}
|
||||
ranges.push(offset..end);
|
||||
offset = end;
|
||||
}
|
||||
edit!(state, {
|
||||
let files = ranges
|
||||
.into_iter()
|
||||
.zip(&names)
|
||||
.map(|(range, name)| engine::Load {
|
||||
data: &data[range],
|
||||
name,
|
||||
})
|
||||
.collect();
|
||||
Command::LoadFiles(engine::LoadFiles { files })
|
||||
})
|
||||
let data = data.clone();
|
||||
state.run(move |e| api::load_files(e, &data, &lengths, &names))
|
||||
}
|
||||
|
||||
/// Headers are ASCII: the frontend percent-encodes the JSON.
|
||||
fn urlencoding_decode(value: &str) -> Option<String> {
|
||||
fn percent_decode(value: &str) -> Option<String> {
|
||||
let bytes = value.as_bytes();
|
||||
let mut decoded = Vec::with_capacity(bytes.len());
|
||||
let mut i = 0;
|
||||
@@ -99,63 +60,55 @@ fn urlencoding_decode(value: &str) -> Option<String> {
|
||||
}
|
||||
|
||||
#[tauri::command(async)]
|
||||
pub fn duplicate(state: State<AppState>) -> Outcome {
|
||||
edit!(state, Command::Duplicate(engine::Duplicate))
|
||||
pub fn duplicate(state: State<AppState>) -> api::Outcome {
|
||||
state.run(api::duplicate)
|
||||
}
|
||||
|
||||
#[tauri::command(async)]
|
||||
pub fn delete(state: State<AppState>, whole_files: bool) -> Outcome {
|
||||
edit!(state, Command::Delete(engine::Delete { whole_files }))
|
||||
pub fn delete(state: State<AppState>, whole_files: bool) -> api::Outcome {
|
||||
state.run(move |e| api::delete(e, whole_files))
|
||||
}
|
||||
|
||||
#[tauri::command(async)]
|
||||
pub fn delete_all(state: State<AppState>) -> Outcome {
|
||||
edit!(state, Command::DeleteAll(engine::DeleteAll))
|
||||
pub fn delete_all(state: State<AppState>) -> api::Outcome {
|
||||
state.run(api::delete_all)
|
||||
}
|
||||
|
||||
#[tauri::command(async)]
|
||||
pub fn reorder(state: State<AppState>, file_ids: Vec<Uuid>, index: usize) -> Outcome {
|
||||
act!(
|
||||
state,
|
||||
Action::Reorder {
|
||||
file_ids: file_ids.into_iter().map(engine::FileId).collect(),
|
||||
index,
|
||||
}
|
||||
)
|
||||
pub fn reorder(state: State<AppState>, file_ids: Vec<Uuid>, index: usize) -> api::Outcome {
|
||||
state.run(move |e| api::reorder(e, &file_ids, index))
|
||||
}
|
||||
|
||||
#[tauri::command(async)]
|
||||
pub fn copy(state: State<AppState>) -> Outcome {
|
||||
act!(state, Action::Copy)
|
||||
pub fn copy(state: State<AppState>) -> api::Outcome {
|
||||
state.run(api::copy)
|
||||
}
|
||||
|
||||
#[tauri::command(async)]
|
||||
pub fn cut(state: State<AppState>) -> Outcome {
|
||||
act!(state, Action::Cut)
|
||||
pub fn cut(state: State<AppState>) -> api::Outcome {
|
||||
state.run(api::cut)
|
||||
}
|
||||
|
||||
#[tauri::command(async)]
|
||||
pub fn paste(state: State<AppState>) -> Outcome {
|
||||
edit!(state, Command::Paste(engine::Paste))
|
||||
pub fn paste(state: State<AppState>) -> api::Outcome {
|
||||
state.run(api::paste)
|
||||
}
|
||||
|
||||
#[tauri::command(async)]
|
||||
pub fn move_elements(state: State<AppState>, what: Selection, to: MoveTarget) -> Outcome {
|
||||
edit!(
|
||||
state,
|
||||
Command::Move(engine::Move {
|
||||
what: what.into(),
|
||||
to: to.into(),
|
||||
})
|
||||
)
|
||||
pub fn move_elements(
|
||||
state: State<AppState>,
|
||||
what: api::Selection,
|
||||
to: api::MoveTarget,
|
||||
) -> api::Outcome {
|
||||
state.run(move |e| api::move_elements(e, what, to))
|
||||
}
|
||||
|
||||
#[tauri::command(async)]
|
||||
pub fn undo(state: State<AppState>) -> Outcome {
|
||||
act!(state, Action::Undo)
|
||||
pub fn undo(state: State<AppState>) -> api::Outcome {
|
||||
state.run(api::undo)
|
||||
}
|
||||
|
||||
#[tauri::command(async)]
|
||||
pub fn redo(state: State<AppState>) -> Outcome {
|
||||
act!(state, Action::Redo)
|
||||
pub fn redo(state: State<AppState>) -> api::Outcome {
|
||||
state.run(api::redo)
|
||||
}
|
||||
@@ -8,7 +8,6 @@
|
||||
pub mod edit;
|
||||
pub mod files;
|
||||
pub mod queries;
|
||||
pub mod routing;
|
||||
pub mod selection;
|
||||
pub mod statistics;
|
||||
pub mod storage;
|
||||
@@ -1,54 +1,51 @@
|
||||
//! The files, their structure and statistics, the selection, the clipboard, and the coordinates of
|
||||
//! the trackpoints and waypoints (flat `[lng, lat, ...]` little-endian `f64` arrays).
|
||||
|
||||
use gpx_engine::{self as engine, FileId};
|
||||
use gpx_engine::{api, ExportOptions};
|
||||
use tauri::State;
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::dto::*;
|
||||
use crate::bytes::*;
|
||||
use crate::state::AppState;
|
||||
|
||||
#[tauri::command(async)]
|
||||
pub fn file_order(state: State<AppState>) -> Vec<String> {
|
||||
state.read(|e| ids(e.order()))
|
||||
state.read(api::file_order)
|
||||
}
|
||||
|
||||
#[tauri::command(async)]
|
||||
pub fn file_structure(state: State<AppState>, file_id: Uuid) -> Option<FileStructure> {
|
||||
state.read(move |e| e.file_structure(&FileId(file_id)).map(FileStructure::new))
|
||||
pub fn file_structure(state: State<AppState>, file_id: Uuid) -> Option<api::FileStructure> {
|
||||
state.read(move |e| api::file_structure(e, file_id))
|
||||
}
|
||||
|
||||
#[tauri::command(async)]
|
||||
pub fn file_statistics(state: State<AppState>, file_id: Uuid) -> Option<GlobalStatistics> {
|
||||
state.read(move |e| {
|
||||
e.file_statistics(&FileId(file_id))
|
||||
.map(|s| GlobalStatistics::new(&s))
|
||||
})
|
||||
pub fn file_statistics(state: State<AppState>, file_id: Uuid) -> Option<api::GlobalStatistics> {
|
||||
state.read(move |e| api::file_statistics(e, file_id))
|
||||
}
|
||||
|
||||
#[tauri::command(async)]
|
||||
pub fn selection(state: State<AppState>) -> Selection {
|
||||
state.read(|e| e.selection().into())
|
||||
pub fn selection(state: State<AppState>) -> api::Selection {
|
||||
state.read(api::selection)
|
||||
}
|
||||
|
||||
#[tauri::command(async)]
|
||||
pub fn clipboard(state: State<AppState>) -> Option<Clipboard> {
|
||||
state.read(|e| e.clipboard().map(Clipboard::new))
|
||||
pub fn clipboard(state: State<AppState>) -> Option<api::Clipboard> {
|
||||
state.read(api::clipboard)
|
||||
}
|
||||
|
||||
#[tauri::command(async)]
|
||||
pub fn can_undo(state: State<AppState>) -> bool {
|
||||
state.read(|e| e.can_undo())
|
||||
state.read(api::can_undo)
|
||||
}
|
||||
|
||||
#[tauri::command(async)]
|
||||
pub fn can_redo(state: State<AppState>) -> bool {
|
||||
state.read(|e| e.can_redo())
|
||||
state.read(api::can_redo)
|
||||
}
|
||||
|
||||
#[tauri::command(async)]
|
||||
pub fn can_paste(state: State<AppState>) -> bool {
|
||||
state.read(|e| e.can_paste())
|
||||
state.read(api::can_paste)
|
||||
}
|
||||
|
||||
#[tauri::command(async)]
|
||||
@@ -56,11 +53,8 @@ pub fn waypoint(
|
||||
state: State<AppState>,
|
||||
file_id: Uuid,
|
||||
waypoint_id: Uuid,
|
||||
) -> Option<WaypointDetails> {
|
||||
state.read(move |e| {
|
||||
e.waypoint(&FileId(file_id), &engine::WaypointId(waypoint_id))
|
||||
.map(WaypointDetails::new)
|
||||
})
|
||||
) -> Option<api::WaypointDetails> {
|
||||
state.read(move |e| api::waypoint(e, file_id, waypoint_id))
|
||||
}
|
||||
|
||||
#[tauri::command(async)]
|
||||
@@ -69,11 +63,8 @@ pub fn trackpoint(
|
||||
file_id: Uuid,
|
||||
segment_id: Uuid,
|
||||
index: usize,
|
||||
) -> Option<TrackpointDetails> {
|
||||
state.read(move |e| {
|
||||
e.trackpoint(&FileId(file_id), &engine::TrackSegmentId(segment_id), index)
|
||||
.map(TrackpointDetails::new)
|
||||
})
|
||||
) -> Option<api::TrackpointDetails> {
|
||||
state.read(move |e| api::trackpoint(e, file_id, segment_id, index))
|
||||
}
|
||||
|
||||
/// A file as GPX (UTF-8 bytes), the first byte telling whether the file exists.
|
||||
@@ -90,7 +81,7 @@ pub fn export_file(
|
||||
osm: bool,
|
||||
as_route: bool,
|
||||
) -> Bytes {
|
||||
let options = engine::ExportOptions {
|
||||
let options = ExportOptions {
|
||||
time,
|
||||
hr,
|
||||
cad,
|
||||
@@ -99,38 +90,25 @@ pub fn export_file(
|
||||
osm,
|
||||
as_route,
|
||||
};
|
||||
optional(state.read(move |e| e.export(&FileId(file_id), options)))
|
||||
optional(state.read(move |e| api::export_file(e, file_id, options)))
|
||||
}
|
||||
|
||||
/// The data that some files have: a bit for the time (1), heart rate (2), cadence (4), temperature
|
||||
/// (8), power (16) and the OpenStreetMap data (32).
|
||||
#[tauri::command(async)]
|
||||
pub fn exportable_data(state: State<AppState>, file_ids: Vec<Uuid>) -> u8 {
|
||||
state.read(move |e| {
|
||||
let ids: Vec<FileId> = file_ids.into_iter().map(FileId).collect();
|
||||
let data = e.exportable_data(&ids);
|
||||
[
|
||||
data.time, data.hr, data.cad, data.atemp, data.power, data.osm,
|
||||
]
|
||||
.iter()
|
||||
.enumerate()
|
||||
.fold(0, |bits, (i, on)| bits | (u8::from(*on) << i))
|
||||
})
|
||||
state.read(move |e| api::exportable_data(e, &file_ids))
|
||||
}
|
||||
|
||||
#[tauri::command(async)]
|
||||
pub fn reduction_distances(state: State<AppState>) -> Bytes {
|
||||
Bytes::new(state.read(|e| f64s(&e.reduction_distances())))
|
||||
f64s(&state.read(api::reduction_distances))
|
||||
}
|
||||
|
||||
#[tauri::command(async)]
|
||||
pub fn segment_coordinates(state: State<AppState>, segment_id: Uuid) -> Bytes {
|
||||
Bytes::new(
|
||||
state.read(move |e| f64s(e.segment_coordinates(&engine::TrackSegmentId(segment_id)))),
|
||||
)
|
||||
f64s(&state.read(move |e| api::segment_coordinates(e, segment_id).to_vec()))
|
||||
}
|
||||
|
||||
#[tauri::command(async)]
|
||||
pub fn waypoint_coordinates(state: State<AppState>, file_id: Uuid) -> Bytes {
|
||||
Bytes::new(state.read(move |e| f64s(e.waypoint_coordinates(&FileId(file_id)))))
|
||||
f64s(&state.read(move |e| api::waypoint_coordinates(e, file_id).to_vec()))
|
||||
}
|
||||
@@ -1,73 +0,0 @@
|
||||
//! The commands that refer to trackpoints by their index in the routing buffers, which only mean
|
||||
//! something for the `revision` they were read with: the commands do nothing if the selection is
|
||||
//! not the one of that revision anymore.
|
||||
|
||||
use gpx_engine::{self as engine, Command};
|
||||
use tauri::State;
|
||||
|
||||
use crate::dto::*;
|
||||
use crate::state::{edit_at, AppState};
|
||||
|
||||
#[tauri::command(async)]
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn route(
|
||||
state: State<AppState>,
|
||||
revision: u32,
|
||||
start: u32,
|
||||
end: u32,
|
||||
lng: Vec<f64>,
|
||||
lat: Vec<f64>,
|
||||
ele: Vec<f64>,
|
||||
attributes: RouteAttributes,
|
||||
anchors: Vec<u32>,
|
||||
) -> Outcome {
|
||||
if lng.len() != lat.len() || lat.len() != ele.len() {
|
||||
return Outcome::failed("lng, lat and ele differ in length");
|
||||
}
|
||||
edit_at!(
|
||||
state,
|
||||
revision,
|
||||
Command::Route(engine::Route {
|
||||
start,
|
||||
end,
|
||||
lng: &lng,
|
||||
lat: &lat,
|
||||
ele: &ele,
|
||||
surface: attributes.surface.view(),
|
||||
highway: attributes.highway.view(),
|
||||
sac_scale: attributes.sac_scale.view(),
|
||||
mtb_scale: attributes.mtb_scale.view(),
|
||||
anchors: &anchors,
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
#[tauri::command(async)]
|
||||
pub fn insert_anchor(state: State<AppState>, revision: u32, lng: f64, lat: f64) -> Outcome {
|
||||
edit_at!(
|
||||
state,
|
||||
revision,
|
||||
Command::InsertAnchor(engine::InsertAnchor { lng, lat })
|
||||
)
|
||||
}
|
||||
|
||||
#[tauri::command(async)]
|
||||
pub fn change_loop_start(state: State<AppState>, revision: u32, index: u32) -> Outcome {
|
||||
edit_at!(
|
||||
state,
|
||||
revision,
|
||||
Command::ChangeLoopStart(engine::ChangeLoopStart { index })
|
||||
)
|
||||
}
|
||||
|
||||
#[tauri::command(async)]
|
||||
pub fn split(state: State<AppState>, revision: u32, at: u32, split_type: SplitType) -> Outcome {
|
||||
edit_at!(
|
||||
state,
|
||||
revision,
|
||||
Command::Split(engine::Split {
|
||||
at,
|
||||
split_type: split_type.into(),
|
||||
})
|
||||
)
|
||||
}
|
||||
@@ -1,49 +1,24 @@
|
||||
//! Changing what is selected.
|
||||
|
||||
use gpx_engine::{self as engine, Action};
|
||||
use gpx_engine::{api, SelectMode};
|
||||
use tauri::State;
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::dto::*;
|
||||
use crate::state::{act, AppState};
|
||||
use crate::state::AppState;
|
||||
|
||||
fn select_elements(
|
||||
state: &AppState,
|
||||
selection: Option<engine::Selection>,
|
||||
mode: SelectMode,
|
||||
) -> Outcome {
|
||||
match selection {
|
||||
Some(selection) => act!(
|
||||
state,
|
||||
Action::Select {
|
||||
selection,
|
||||
mode: mode.into(),
|
||||
}
|
||||
),
|
||||
None => Outcome::failed("the ids are not UUIDs"),
|
||||
}
|
||||
}
|
||||
|
||||
fn to_file_ids(ids: Vec<Uuid>) -> Vec<engine::FileId> {
|
||||
ids.into_iter().map(engine::FileId).collect()
|
||||
#[tauri::command(async)]
|
||||
pub fn select(state: State<AppState>, file_ids: Vec<Uuid>, mode: SelectMode) -> api::Outcome {
|
||||
state.run(move |e| api::select(e, &file_ids, mode))
|
||||
}
|
||||
|
||||
#[tauri::command(async)]
|
||||
pub fn select(state: State<AppState>, file_ids: Vec<Uuid>, mode: SelectMode) -> Outcome {
|
||||
let selection = engine::Selection::File {
|
||||
file_ids: to_file_ids(file_ids).into_iter().collect(),
|
||||
};
|
||||
select_elements(&state, Some(selection), mode)
|
||||
pub fn select_all(state: State<AppState>) -> api::Outcome {
|
||||
state.run(api::select_all)
|
||||
}
|
||||
|
||||
#[tauri::command(async)]
|
||||
pub fn select_all(state: State<AppState>) -> Outcome {
|
||||
act!(state, Action::SelectAll)
|
||||
}
|
||||
|
||||
#[tauri::command(async)]
|
||||
pub fn arrow_select(state: State<AppState>, down: bool, add: bool) -> Outcome {
|
||||
act!(state, Action::ArrowSelect { down, add })
|
||||
pub fn arrow_select(state: State<AppState>, down: bool, add: bool) -> api::Outcome {
|
||||
state.run(move |e| api::arrow_select(e, down, add))
|
||||
}
|
||||
|
||||
#[tauri::command(async)]
|
||||
@@ -52,12 +27,8 @@ pub fn select_tracks(
|
||||
file_id: Uuid,
|
||||
track_ids: Vec<Uuid>,
|
||||
mode: SelectMode,
|
||||
) -> Outcome {
|
||||
let selection = engine::Selection::Track {
|
||||
file_id: engine::FileId(file_id),
|
||||
trk_ids: track_ids.into_iter().map(engine::TrackId).collect(),
|
||||
};
|
||||
select_elements(&state, Some(selection), mode)
|
||||
) -> api::Outcome {
|
||||
state.run(move |e| api::select_tracks(e, file_id, &track_ids, mode))
|
||||
}
|
||||
|
||||
#[tauri::command(async)]
|
||||
@@ -67,24 +38,17 @@ pub fn select_segments(
|
||||
track_id: Uuid,
|
||||
segment_ids: Vec<Uuid>,
|
||||
mode: SelectMode,
|
||||
) -> Outcome {
|
||||
let selection = engine::Selection::TrackSegment {
|
||||
file_id: engine::FileId(file_id),
|
||||
trk_id: engine::TrackId(track_id),
|
||||
trkseg_ids: segment_ids
|
||||
.into_iter()
|
||||
.map(engine::TrackSegmentId)
|
||||
.collect(),
|
||||
};
|
||||
select_elements(&state, Some(selection), mode)
|
||||
) -> api::Outcome {
|
||||
state.run(move |e| api::select_segments(e, file_id, track_id, &segment_ids, mode))
|
||||
}
|
||||
|
||||
#[tauri::command(async)]
|
||||
pub fn select_waypoint_group(state: State<AppState>, file_id: Uuid, mode: SelectMode) -> Outcome {
|
||||
let selection = engine::Selection::Waypoints {
|
||||
file_id: engine::FileId(file_id),
|
||||
};
|
||||
select_elements(&state, Some(selection), mode)
|
||||
pub fn select_waypoint_group(
|
||||
state: State<AppState>,
|
||||
file_id: Uuid,
|
||||
mode: SelectMode,
|
||||
) -> api::Outcome {
|
||||
state.run(move |e| api::select_waypoint_group(e, file_id, mode))
|
||||
}
|
||||
|
||||
#[tauri::command(async)]
|
||||
@@ -93,10 +57,6 @@ pub fn select_waypoints(
|
||||
file_id: Uuid,
|
||||
waypoint_ids: Vec<Uuid>,
|
||||
mode: SelectMode,
|
||||
) -> Outcome {
|
||||
let selection = engine::Selection::Waypoint {
|
||||
file_id: engine::FileId(file_id),
|
||||
wpt_ids: waypoint_ids.into_iter().map(engine::WaypointId).collect(),
|
||||
};
|
||||
select_elements(&state, Some(selection), mode)
|
||||
) -> api::Outcome {
|
||||
state.run(move |e| api::select_waypoints(e, file_id, &waypoint_ids, mode))
|
||||
}
|
||||
@@ -2,16 +2,17 @@
|
||||
//! (one per call, copies of the buffers of the engine). Optional ones start with a byte telling
|
||||
//! whether they are there.
|
||||
|
||||
use gpx_engine::api;
|
||||
use tauri::State;
|
||||
|
||||
use crate::dto::*;
|
||||
use crate::bytes::*;
|
||||
use crate::state::AppState;
|
||||
|
||||
macro_rules! f64_getters {
|
||||
($($name:ident),*) => {$(
|
||||
#[tauri::command(async)]
|
||||
pub fn $name(state: State<AppState>) -> Bytes {
|
||||
Bytes::new(state.read(|e| f64s(&e.statistics().$name)))
|
||||
f64s(&state.read(|e| api::$name(e).to_vec()))
|
||||
}
|
||||
)*};
|
||||
}
|
||||
@@ -30,7 +31,7 @@ macro_rules! optional_f64_getters {
|
||||
($($name:ident),*) => {$(
|
||||
#[tauri::command(async)]
|
||||
pub fn $name(state: State<AppState>) -> Bytes {
|
||||
optional(state.read(|e| e.statistics().$name.as_deref().map(f64s)))
|
||||
optional(state.read(|e| api::$name(e).map(f64_bytes)))
|
||||
}
|
||||
)*};
|
||||
}
|
||||
@@ -40,12 +41,12 @@ optional_f64_getters!(speed, hr, cad, atemp, power);
|
||||
/// Timestamps in milliseconds since the epoch (`NO_TIME` when missing), as `i64`.
|
||||
#[tauri::command(async)]
|
||||
pub fn timestamps(state: State<AppState>) -> Bytes {
|
||||
optional(state.read(|e| e.statistics().time.as_deref().map(i64s)))
|
||||
optional(state.read(|e| api::timestamps(e).map(i64_bytes)))
|
||||
}
|
||||
|
||||
#[tauri::command(async)]
|
||||
pub fn selection_statistics(state: State<AppState>) -> GlobalStatistics {
|
||||
state.read(|e| GlobalStatistics::new(&e.statistics().global))
|
||||
pub fn selection_statistics(state: State<AppState>) -> api::GlobalStatistics {
|
||||
state.read(api::selection_statistics)
|
||||
}
|
||||
|
||||
#[tauri::command(async)]
|
||||
@@ -53,83 +54,55 @@ pub fn slice_statistics(
|
||||
state: State<AppState>,
|
||||
start: usize,
|
||||
end: usize,
|
||||
) -> Option<GlobalStatistics> {
|
||||
state.read(move |e| {
|
||||
e.statistics()
|
||||
.slice(start, end)
|
||||
.map(|s| GlobalStatistics::new(&s))
|
||||
})
|
||||
) -> Option<api::GlobalStatistics> {
|
||||
state.read(move |e| api::slice_statistics(e, start, end))
|
||||
}
|
||||
|
||||
macro_rules! intervals_getters {
|
||||
($starts:ident, $values:ident, $field:ident) => {
|
||||
macro_rules! category_getters {
|
||||
($starts:ident, $values:ident, $names:ident) => {
|
||||
#[tauri::command(async)]
|
||||
pub fn $starts(state: State<AppState>) -> Bytes {
|
||||
Bytes::new(state.read(|e| u32s(&e.statistics().$field.starts)))
|
||||
u32s(&state.read(|e| api::$starts(e).to_vec()))
|
||||
}
|
||||
|
||||
#[tauri::command(async)]
|
||||
pub fn $values(state: State<AppState>) -> Bytes {
|
||||
Bytes::new(state.read(|e| e.statistics().$field.values.clone()))
|
||||
u8s(&state.read(|e| api::$values(e).to_vec()))
|
||||
}
|
||||
|
||||
#[tauri::command(async)]
|
||||
pub fn $names(state: State<AppState>) -> Vec<String> {
|
||||
state.read(|e| api::$names(e).to_vec())
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
intervals_getters!(surface_starts, surface_values, surface);
|
||||
intervals_getters!(highway_starts, highway_values, highway);
|
||||
intervals_getters!(sac_scale_starts, sac_scale_values, sac_scale);
|
||||
intervals_getters!(mtb_scale_starts, mtb_scale_values, mtb_scale);
|
||||
|
||||
/// The names of the categories, in the order of their codes. The lists only grow.
|
||||
#[tauri::command(async)]
|
||||
pub fn surfaces(state: State<AppState>) -> Vec<String> {
|
||||
state.read(|e| e.categories().surface.names().to_vec())
|
||||
}
|
||||
|
||||
#[tauri::command(async)]
|
||||
pub fn highways(state: State<AppState>) -> Vec<String> {
|
||||
state.read(|e| e.categories().highway.names().to_vec())
|
||||
}
|
||||
|
||||
#[tauri::command(async)]
|
||||
pub fn sac_scales(state: State<AppState>) -> Vec<String> {
|
||||
state.read(|e| e.categories().sac_scale.names().to_vec())
|
||||
}
|
||||
|
||||
#[tauri::command(async)]
|
||||
pub fn mtb_scales(state: State<AppState>) -> Vec<String> {
|
||||
state.read(|e| e.categories().mtb_scale.names().to_vec())
|
||||
}
|
||||
|
||||
// Routing buffers
|
||||
category_getters!(surface_starts, surface_values, surfaces);
|
||||
category_getters!(highway_starts, highway_values, highways);
|
||||
category_getters!(sac_scale_starts, sac_scale_values, sac_scales);
|
||||
category_getters!(mtb_scale_starts, mtb_scale_values, mtb_scales);
|
||||
|
||||
#[tauri::command(async)]
|
||||
pub fn anchor_indices(state: State<AppState>) -> Bytes {
|
||||
Bytes::new(state.read(|e| u32s(&e.routing().anchor_indices)))
|
||||
u32s(&state.read(|e| api::anchor_indices(e).to_vec()))
|
||||
}
|
||||
|
||||
#[tauri::command(async)]
|
||||
pub fn anchor_zooms(state: State<AppState>) -> Bytes {
|
||||
Bytes::new(state.read(|e| e.routing().anchor_zooms.clone()))
|
||||
u8s(&state.read(|e| api::anchor_zooms(e).to_vec()))
|
||||
}
|
||||
|
||||
#[tauri::command(async)]
|
||||
pub fn segment_starts(state: State<AppState>) -> Bytes {
|
||||
Bytes::new(state.read(|e| u32s(&e.routing().segment_starts)))
|
||||
u32s(&state.read(|e| api::segment_starts(e).to_vec()))
|
||||
}
|
||||
|
||||
#[tauri::command(async)]
|
||||
pub fn segment_ids(state: State<AppState>) -> Vec<String> {
|
||||
state.read(|e| {
|
||||
e.routing()
|
||||
.segment_ids
|
||||
.iter()
|
||||
.map(|id| id.0.to_string())
|
||||
.collect()
|
||||
})
|
||||
state.read(api::segment_ids)
|
||||
}
|
||||
|
||||
#[tauri::command(async)]
|
||||
pub fn routing_revision(state: State<AppState>) -> u32 {
|
||||
state.read(|e| e.routing().revision)
|
||||
state.read(api::routing_revision)
|
||||
}
|
||||
@@ -6,8 +6,8 @@
|
||||
use serde::Serialize;
|
||||
use tauri::State;
|
||||
|
||||
use crate::dto::Outcome;
|
||||
use crate::state::AppState;
|
||||
use gpx_engine::api::Outcome;
|
||||
|
||||
/// What opening the storage found.
|
||||
#[derive(Serialize)]
|
||||
|
||||
Reference in new issue
Block a user