164 Commits
Author SHA1 Message Date
vcoppe 8d878fb0e1 deploy to cloudflare 2026-10-31 18:05:29 +01:00
vcoppe 972510bfbc improve typings 2026-10-31 17:47:26 +01:00
vcoppe 46563ef283 each keys 2026-10-31 15:30:11 +01:00
vcoppe c778766293 fix last type error 2026-10-31 15:21:11 +01:00
vcoppe fbd8775528 fix layer typings 2026-10-31 15:17:29 +01:00
vcoppe b3fe640575 fix type errors 2026-10-31 14:42:59 +01:00
vcoppe c3ace59527 change base to resolve 2026-10-31 14:37:25 +01:00
vcoppe 50348e7c5c fix multidrag 2026-10-31 14:16:57 +01:00
vcoppe c3ce577e6d fix import modules 2026-10-31 14:01:27 +01:00
vcoppe 957f0c35ec fix linting issues 2026-10-31 13:06:44 +01:00
vcoppe fbfdc1933c fix display of additional data with gaps, closes #361 2026-10-28 23:09:18 +01:00
vcoppe 6956c19410 lint fixes 2026-10-28 22:40:48 +01:00
vcoppe f5a2db0565 fix power format, closes #378 2026-10-28 21:47:31 +01:00
vcoppe 39454f0b83 update docs 2026-10-28 18:22:00 +01:00
vcoppe 68eacc76d1 routes continued 2026-10-28 18:14:08 +01:00
vcoppe 603b73b99d move hide & center to view 2026-10-28 09:15:46 +01:00
vcoppe 3f8e2a0c56 clarify name handling 2026-10-27 17:44:59 +01:00
vcoppe 45f877ec78 route export, filter out anchors at high zoom level 2026-10-26 19:27:12 +01:00
vcoppe 0f957fb1f2 create route file action 2026-10-26 19:22:14 +01:00
vcoppe 764d0b233a route - track conversion 2026-10-26 19:06:19 +01:00
vcoppe b6c0f5190a route full support 2026-10-26 18:51:56 +01:00
vcoppe c842da8dc1 reorder context menu items 2026-10-18 19:09:06 +02:00
vcoppe 5bc35f30d9 strip native menu 2026-10-18 18:43:12 +02:00
vcoppe 873e2e1654 Merge branch 'dev' into tauri 2026-10-18 18:15:37 +02:00
vcoppe 6c2d2879f4 use new endpoint for openhikingmap 2026-10-18 17:20:09 +02:00
vcoppe f5670cdd8e remove custom ign styles 2026-10-18 17:14:50 +02:00
vcoppe 741d35db1e sparse desktop build 2026-10-18 16:53:39 +02:00
vcoppe 6556c29492 fix 2026-10-18 16:38:59 +02:00
vcoppe 7fa850e4c8 fix routing control size 2026-10-18 16:34:03 +02:00
vcoppe f9e8d4c644 show popup once data is loaded 2026-10-18 16:27:51 +02:00
vcoppe a07da16163 sparser data transfers 2026-10-18 16:20:32 +02:00
vcoppe 4583874e7e sparser data transfers 2026-10-18 12:35:32 +02:00
vcoppe 48325e6016 performance improvements 2026-10-18 10:50:34 +02:00
vcoppe 9033401094 improved preview 2026-10-18 09:32:18 +02:00
vcoppe b64c192c4f consistent highlight color 2026-10-18 09:17:01 +02:00
vcoppe 9dd4af98a5 small fix 2026-10-18 09:09:00 +02:00
vcoppe 05510ece92 full screen 2026-10-18 00:38:53 +02:00
vcoppe 8bbcbdc097 remove focus-ring 2026-10-18 00:33:03 +02:00
vcoppe 3012f6bf73 fixes 2026-10-18 00:24:16 +02:00
vcoppe 5373d0f0d4 cleaning 2026-10-17 23:55:46 +02:00
vcoppe 8fd47d8b18 drop anywhere in folder 2026-10-17 23:39:44 +02:00
vcoppe a5c1f6ad33 fix folder styling 2026-10-17 23:35:56 +02:00
vcoppe 3ef2bf135a explorer actions 2026-10-17 23:23:29 +02:00
vcoppe adbf89e318 clarify filename 2026-10-17 22:17:17 +02:00
vcoppe 11709316d2 avoid selecting on resize 2026-10-17 20:02:09 +02:00
vcoppe d52b61f707 commit missing changes 2026-10-17 20:01:59 +02:00
vcoppe 358253b7a9 open links in browser 2026-10-17 19:57:50 +02:00
vcoppe 8d6885d58b remember language 2026-10-17 19:36:57 +02:00
vcoppe 128338aa05 fix sorting 2026-10-17 19:10:50 +02:00
vcoppe c84e1c9a06 fix routing on other file 2026-10-17 19:03:33 +02:00
vcoppe 9f49119eec no file delete on desktop 2026-10-17 18:57:40 +02:00
vcoppe 67a47cf065 fix tab size 2026-10-17 18:50:23 +02:00
vcoppe e57949dc01 fix 2026-10-17 18:33:50 +02:00
vcoppe da92fd15d2 fix sortable 2026-10-17 18:28:32 +02:00
vcoppe 01443943b3 fix sortable 2026-10-17 17:21:56 +02:00
vcoppe 970cf7d1e1 improved sticky folders 2026-10-17 16:39:41 +02:00
vcoppe 593b43e433 sticky folders 2026-10-17 16:24:19 +02:00
vcoppe 8fa88680b3 fix 2026-10-17 16:17:45 +02:00
vcoppe b2495d1466 progress 2026-10-17 16:14:17 +02:00
vcoppe ed90ba6a1f file explorer 2026-10-17 14:42:49 +02:00
vcoppe b0a3c3a9ba progress 2026-10-16 18:14:59 +02:00
vcoppe 005cc7b305 fix 2026-10-16 09:05:45 +02:00
vcoppe 1bfd319758 use history variant 2026-10-16 09:02:31 +02:00
vcoppe 187296a277 keep original flag 2026-10-16 08:54:40 +02:00
vcoppe 55a8b020e0 per file history 2026-10-16 08:42:45 +02:00
vcoppe 1ba5436af3 progress 2026-10-14 19:15:02 +02:00
vcoppe 2e1ca00afa fix 2026-10-14 09:41:07 +02:00
vcoppe 97d597e139 progress 2026-10-14 09:22:07 +02:00
vcoppe 41b9116c6a progress 2026-10-14 08:51:38 +02:00
vcoppe ce8398f749 renaming 2026-10-14 08:17:51 +02:00
vcoppe 3c2055b50e improve flow 2026-10-14 08:07:58 +02:00
vcoppe 1d13972014 fix 2026-10-11 11:19:57 +02:00
vcoppe 890bcd765a refactoring 2026-10-11 11:18:36 +02:00
vcoppe 4795513344 reorder methods 2026-10-11 10:58:15 +02:00
vcoppe d84ca47745 refactoring 2026-10-11 10:54:06 +02:00
vcoppe 9adb8e7221 outdated state 2026-10-11 10:48:43 +02:00
vcoppe a2b4d89150 cross-tab sync 2026-10-10 11:07:07 +02:00
vcoppe 77929cfa04 per-chunk categories storage 2026-10-09 17:55:07 +02:00
vcoppe dc36de2a48 import and export of routes, ref #192 2026-10-09 09:26:38 +02:00
vcoppe da9abbae5e refactoring and tests 2026-10-09 08:58:00 +02:00
vcoppe 3bb553d6b5 remove unused fields 2026-10-08 21:19:53 +02:00
vcoppe 83b76bb838 fix link fields 2026-10-08 21:13:44 +02:00
vcoppe 42a55e6759 progress 2026-10-08 21:04:29 +02:00
vcoppe b085a1d476 cleaning 2026-10-08 19:04:46 +02:00
vcoppe 840e943626 update readme 2026-10-08 18:41:27 +02:00
vcoppe 16e2e0f679 remove todo 2026-10-08 18:37:46 +02:00
vcoppe 0f9557ea82 remove gpx js package 2026-10-08 18:37:40 +02:00
vcoppe 67a4c090bd progress 2026-10-08 18:34:19 +02:00
vcoppe 606a0e974a export 2026-10-08 09:24:15 +02:00
vcoppe e8c4eb92f9 handle corrupt time data 2026-10-08 08:59:53 +02:00
vcoppe f0851086c0 fix reactivity 2026-10-08 08:47:14 +02:00
vcoppe bec1ce4c9b reduce 2026-10-08 08:36:29 +02:00
vcoppe b29c4cdb08 fix bounds for waypoints 2026-10-08 08:15:22 +02:00
vcoppe 63ba8596a7 time tool 2026-10-08 08:12:44 +02:00
vcoppe 82d2f79da0 progress 2026-10-07 22:20:40 +02:00
vcoppe f642d60f42 merge 2026-10-07 19:57:27 +02:00
vcoppe 24b877c4ee split 2026-10-07 19:38:21 +02:00
vcoppe e07ec97e12 crop 2026-10-07 18:54:00 +02:00
vcoppe 290f4d8c44 reverse 2026-10-07 18:45:56 +02:00
vcoppe a48608840f reverse 2026-10-07 18:36:00 +02:00
vcoppe df07892e76 refactoring 2026-10-07 18:22:30 +02:00
vcoppe 594dd8979a routing 2026-10-07 17:58:17 +02:00
vcoppe 29850c1c8f speed fix 2026-10-07 09:35:27 +02:00
vcoppe adbe4a2693 copy on wasm side to avoid any data corruption 2026-10-07 08:57:37 +02:00
vcoppe b843a18441 reduce data exchanges across boundary 2026-10-07 08:45:50 +02:00
vcoppe da8a9e3d84 handle self closing elements 2026-10-04 22:01:23 +02:00
vcoppe c6bd5b06e8 clean tool 2026-10-04 20:38:46 +02:00
vcoppe c04367dd86 progress 2026-10-04 19:19:53 +02:00
vcoppe 5e2f506120 progress 2026-10-04 19:14:41 +02:00
vcoppe c536e6247b progress 2026-10-04 17:29:57 +02:00
vcoppe 594d714b7f small fix 2026-10-04 17:19:35 +02:00
vcoppe 543fecadb8 fix popups 2026-10-04 17:09:41 +02:00
vcoppe 017d8521d6 use engine info 2026-10-04 16:27:03 +02:00
vcoppe 78e9d45072 small fix 2026-10-04 16:21:48 +02:00
vcoppe 7651d859c4 progress 2026-10-04 16:11:44 +02:00
vcoppe ec448b72e2 progress 2026-10-04 15:47:09 +02:00
vcoppe bf946f179a progress 2026-10-04 15:26:58 +02:00
vcoppe 9ff9b5475a progress 2026-10-04 15:03:37 +02:00
vcoppe 55f1d275b7 progress 2026-10-04 14:49:53 +02:00
vcoppe 1d534881f8 progress 2026-10-04 13:18:18 +02:00
vcoppe 03cbacfbb4 fix 2026-10-04 12:33:15 +02:00
vcoppe 9d0905959b fix markers 2026-10-04 12:23:42 +02:00
vcoppe b7ca462f58 progress 2026-10-04 11:52:46 +02:00
vcoppe 9015da4876 fixes 2026-10-04 11:07:12 +02:00
vcoppe f458a1104f progress 2026-10-04 11:00:46 +02:00
vcoppe caba9388af progress 2026-10-04 10:29:39 +02:00
vcoppe 763a7406ce progress 2026-10-04 10:15:51 +02:00
vcoppe 68c8933c35 progress 2026-10-04 09:55:08 +02:00
vcoppe 84b1558b2e progress 2026-10-04 09:50:34 +02:00
vcoppe cd870f0186 visibility 2026-10-04 09:40:17 +02:00
vcoppe 9ac2a2b69c progress 2026-10-04 08:53:00 +02:00
vcoppe 86aba8f4b1 progress 2026-10-04 08:26:15 +02:00
vcoppe 74b674e8e8 progress 2026-10-04 08:21:39 +02:00
vcoppe f1904bc12f cleaning 2026-10-03 20:54:05 +02:00
vcoppe 9afba53e5d progress 2026-10-03 19:50:38 +02:00
vcoppe fd76e0fa46 progress 2026-10-03 19:17:58 +02:00
vcoppe 438a87f06f progress 2026-10-03 19:08:56 +02:00
vcoppe a994e16f51 splice 2026-10-03 18:03:13 +02:00
vcoppe 639e7491eb merge methods into 1 2026-10-03 17:47:04 +02:00
vcoppe 891a08a3cc progress 2026-10-03 17:42:26 +02:00
vcoppe 0c07164dec remove (copy) in name 2026-10-03 17:26:53 +02:00
vcoppe f133d55889 progress 2026-10-03 17:21:16 +02:00
vcoppe fbb636cb06 progress 2026-10-03 16:52:21 +02:00
vcoppe bddceb28aa keep style info 2026-10-03 16:43:53 +02:00
vcoppe bff84fa24c progress 2026-10-03 16:40:17 +02:00
vcoppe 64914f9aa0 progress 2026-10-03 16:16:47 +02:00
vcoppe 2abf46c747 progress 2026-10-03 15:33:44 +02:00
vcoppe 888f41e979 refactoring 2026-10-03 13:56:16 +02:00
vcoppe 17533586ef progress 2026-10-03 13:41:58 +02:00
vcoppe c6ad088947 fix 2026-10-03 13:40:49 +02:00
vcoppe 87d6d5f30e change type 2026-10-03 11:49:10 +02:00
vcoppe dbe894b3a3 reorganize 2026-10-03 10:03:22 +02:00
vcoppe a921d4b9cd fixes 2026-10-02 18:50:10 +02:00
vcoppe b7db73cf77 progress 2026-10-02 18:06:45 +02:00
vcoppe 5868595a6c progress 2026-10-01 18:42:31 +02:00
vcoppe eb8e09b835 progress 2026-09-30 19:19:23 +02:00
vcoppe 41dc853f65 progress 2026-09-25 12:09:58 +02:00
vcoppe 27c0a57cc1 progress 2026-09-24 20:55:30 +02:00
vcoppe 497ac120ec progress 2026-09-23 19:53:39 +02:00
vcoppe a486016d96 progress 2026-09-21 23:49:57 +02:00
vcoppe cc5f5653ef progress 2026-09-20 13:58:27 +02:00
vcoppe 20222c450d parsing continued 2026-09-18 09:50:04 +02:00
vcoppe 69049e1345 parsing start 2026-09-18 09:03:51 +02:00
vcoppe ed6264444f wasm first steps 2026-07-14 18:36:30 +02:00
343 changed files with 2043 additions and 4774 deletions

No files matched your search

+1 -1
View File
@@ -63,5 +63,5 @@ jobs:
with:
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
command: pages deploy website/build --project-name=${{ vars.CLOUDFLARE_PROJECT_NAME || 'gpxstudio' }} --branch=${{ github.ref_name }} --commit-dirty=true
command: pages deploy website/build --project-name=${{ vars.CLOUDFLARE_PROJECT_NAME || 'gpxstudio' }} --branch=${{ github.ref_name }}
gitHubToken: ${{ secrets.GITHUB_TOKEN }}
-124
View File
@@ -1,124 +0,0 @@
name: Desktop beta
on:
push:
branches: 'dev'
workflow_dispatch:
concurrency:
group: desktop-beta-${{ github.ref }}
cancel-in-progress: true
jobs:
build:
strategy:
fail-fast: false
matrix:
include:
- platform: macos-latest
rust_targets: aarch64-apple-darwin,x86_64-apple-darwin,wasm32-unknown-unknown
args: --target universal-apple-darwin
bundles: target/universal-apple-darwin/release/bundle/dmg/*.dmg
# the oldest supported Ubuntu, so that the AppImage runs on older systems too
- platform: ubuntu-22.04
rust_targets: wasm32-unknown-unknown
args: ''
bundles: |
target/release/bundle/deb/*.deb
target/release/bundle/rpm/*.rpm
target/release/bundle/appimage/*.AppImage
- platform: windows-latest
rust_targets: wasm32-unknown-unknown
args: ''
bundles: |
target/release/bundle/msi/*.msi
target/release/bundle/nsis/*.exe
runs-on: ${{ matrix.platform }}
steps:
- name: Checkout
uses: actions/checkout@v5
- name: Install Linux dependencies
if: runner.os == 'Linux'
run: |
sudo apt-get update
sudo apt-get install -y libwebkit2gtk-4.1-dev libappindicator3-dev librsvg2-dev patchelf
- name: Install Node.js
uses: actions/setup-node@v6
with:
node-version: 24
cache: npm
cache-dependency-path: website/package-lock.json
- name: Install Rust
uses: dtolnay/rust-toolchain@stable
with:
targets: ${{ matrix.rust_targets }}
- name: Cache Rust build
uses: Swatinem/rust-cache@v2
- name: Install wasm-pack
uses: taiki-e/install-action@v2
with:
tool: wasm-pack
# the desktop app does not use it, but the website depends on its package
- name: Build wasm
run: wasm-pack build --target bundler
working-directory: wasm
- name: Install dependencies for website
run: npm install --prefix website
# the key would be readable in the app: it is left empty, so only the MapTiler layer fails
- name: Create env file
run: echo "PUBLIC_MAPTILER_KEY=" >> website/.env
- name: Build app
run: npx --yes @tauri-apps/cli@2 build ${{ matrix.args }}
working-directory: tauri
# after the build, which gives the app the website it embeds
- name: Test engine and app
run: cargo test -p gpx-engine -p gpx-studio-desktop
- name: Upload bundles
uses: actions/upload-artifact@v4
with:
name: bundles-${{ matrix.platform }}
path: ${{ matrix.bundles }}
if-no-files-found: error
# replaces the beta release only once every platform is built
release:
needs: build
runs-on: ubuntu-latest
permissions:
contents: write
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GH_REPO: ${{ github.repository }}
steps:
- name: Download bundles
uses: actions/download-artifact@v5
with:
pattern: bundles-*
path: bundles
merge-multiple: true
- name: Publish beta release
# the bundles of a platform are in a folder per format (deb, msi…)
run: |
mapfile -t files < <(find bundles -type f)
gh release delete desktop-beta --cleanup-tag --yes || true
gh release create desktop-beta "${files[@]}" \
--prerelease \
--target ${{ github.sha }} \
--title "Desktop beta" \
--notes "Beta of the desktop app, built from ${{ github.sha }} on the dev branch. The builds are not signed yet: macOS and Windows will warn before opening them."
-9
View File
@@ -7,15 +7,6 @@ edition = "2024"
[lib]
name = "gpx_studio_desktop_lib"
crate-type = ["staticlib", "cdylib", "rlib"]
# the tests are in `tests/app`: on Windows, a test binary of the library or of the app could not
# start without the manifest, which `build.rs` only gives to the binaries of `tests`
test = false
doctest = false
[[bin]]
name = "gpx-studio-desktop"
path = "src/main.rs"
test = false
[dev-dependencies]
tauri = { version = "2", features = ["test"] }
-17
View File
@@ -1,20 +1,3 @@
fn main() {
// On Windows, the dialogs need Common Controls v6, which a binary only gets with a manifest that
// asks for it: tauri-build gives one to the app, but not to the test binaries, which would stop
// with STATUS_ENTRYPOINT_NOT_FOUND before running any test. This gives them the same manifest
// (`-tests` only reaches the binaries of `tests`, hence the tests are there).
if std::env::var("CARGO_CFG_TARGET_OS").as_deref() == Ok("windows")
&& std::env::var("CARGO_CFG_TARGET_ENV").as_deref() == Ok("msvc")
{
let manifest = std::path::Path::new(env!("CARGO_MANIFEST_DIR"))
.join("tests")
.join("windows-test.manifest");
println!("cargo:rerun-if-changed={}", manifest.display());
println!("cargo:rustc-link-arg-tests=/MANIFEST:EMBED");
println!(
"cargo:rustc-link-arg-tests=/MANIFESTINPUT:{}",
manifest.display()
);
}
tauri_build::build()
}
+9 -6
View File
@@ -3,20 +3,20 @@
mod bytes;
mod commands;
pub mod state;
pub mod store;
pub mod workspace;
mod state;
mod store;
mod workspace;
use commands::*;
use tauri::{Emitter, Manager};
/// The engine of the app: each file has its own history, so that undoing only concerns the
/// selected files, and opening or closing a file is not something to undo.
pub type Engine = gpx_engine::Engine<gpx_engine::PerFileHistory>;
pub(crate) type Engine = gpx_engine::Engine<gpx_engine::PerFileHistory>;
/// The GPX files among what the system gave to the app (the arguments of the command line, when a
/// file is opened with it).
pub fn gpx_paths(arguments: impl IntoIterator<Item = String>) -> Vec<std::path::PathBuf> {
fn gpx_paths(arguments: impl IntoIterator<Item = String>) -> Vec<std::path::PathBuf> {
arguments
.into_iter()
.map(std::path::PathBuf::from)
@@ -108,7 +108,7 @@ fn menu<R: tauri::Runtime>(app: &tauri::AppHandle<R>) -> tauri::Result<tauri::me
}
/// Gives the builder the commands that reach the engine (which it has to be given as `AppState`).
pub fn register_commands<R: tauri::Runtime>(builder: tauri::Builder<R>) -> tauri::Builder<R> {
fn register_commands<R: tauri::Runtime>(builder: tauri::Builder<R>) -> tauri::Builder<R> {
builder.invoke_handler(tauri::generate_handler![
// selection
selection::select,
@@ -234,3 +234,6 @@ pub fn register_commands<R: tauri::Runtime>(builder: tauri::Builder<R>) -> tauri
system::reveal_path,
])
}
#[cfg(test)]
mod tests;
+7
View File
@@ -280,6 +280,13 @@ impl AppState {
})
}
/// An engine that keeps its data in a directory of its own, and has nobody to tell.
#[cfg(test)]
pub fn temporary() -> Self {
let directory = std::env::temp_dir().join(format!("gpx-studio-{}", uuid::Uuid::new_v4()));
Self::open(directory, Arc::new(|_, _| {})).expect("the storage can be opened")
}
/// Opens files that the system gave to the app, see `Core::open_external`.
pub fn open_external(&self, paths: Vec<PathBuf>) {
self.core(move |core| core.open_external(&paths));
+66
View File
@@ -217,3 +217,69 @@ impl Storage for FileStorage {
Ok(())
}
}
#[cfg(test)]
mod tests {
use crate::Engine;
use gpx_engine::{Action, Command, Load, LoadFiles, Persister};
use super::*;
fn directory() -> PathBuf {
std::env::temp_dir().join(format!("gpx-studio-store-{}", Uuid::new_v4()))
}
fn engine_with_a_file() -> Engine {
let mut engine = Engine::default();
let data = std::fs::read("../gpx-rs/data/simple.gpx").unwrap();
engine
.execute(Action::Edit(Command::LoadFiles(LoadFiles {
files: vec![Load {
data: &data,
name: "simple",
}],
})))
.unwrap();
engine
}
#[test]
fn keeps_the_files_and_the_settings_between_sessions() {
let root = directory();
let engine = engine_with_a_file();
let order = engine.order().to_vec();
let storage = FileStorage::open(&root).unwrap();
pollster::block_on(Persister::default().save(&storage, engine.snapshot())).unwrap();
pollster::block_on(storage.put_setting("units", "\"metric\"")).unwrap();
// another session
let storage = FileStorage::open(&root).unwrap();
let restored = pollster::block_on(Persister::default().restore(&storage)).unwrap();
let mut other = Engine::default();
other.restore(restored);
assert_eq!(other.order(), order);
assert_eq!(
other.file_statistics(&order[0]).unwrap().total_distance,
engine.file_statistics(&order[0]).unwrap().total_distance
);
assert_eq!(storage.settings()["units"], "\"metric\"");
fs::remove_dir_all(root).unwrap();
}
#[test]
fn refuses_a_commit_that_is_not_based_on_the_head() {
let root = directory();
let storage = FileStorage::open(&root).unwrap();
let batch = Batch {
order: Some(vec![]),
..Default::default()
};
assert_eq!(pollster::block_on(storage.commit(&batch)), Ok(Revision(1)));
assert_eq!(
pollster::block_on(storage.commit(&batch)),
Err(CommitError::Stale { head: Revision(1) })
);
fs::remove_dir_all(root).unwrap();
}
}
@@ -6,23 +6,14 @@ use tauri::test::{INVOKE_KEY, get_ipc_response, mock_builder, mock_context, noop
use tauri::webview::InvokeRequest;
use tauri::{WebviewWindow, ipc::CallbackFn, ipc::InvokeBody, ipc::InvokeResponseBody};
/// The origin of the pages of the app, the one Tauri allows the commands from: Windows has no
/// custom protocols, the pages come from `http://tauri.localhost` there.
const ORIGIN: &str = if cfg!(windows) {
"http://tauri.localhost"
} else {
"tauri://localhost"
};
struct App(WebviewWindow<tauri::test::MockRuntime>);
impl App {
fn new() -> Self {
let app = gpx_studio_desktop_lib::register_commands(
mock_builder().manage(crate::temporary_state()),
)
.build(mock_context(noop_assets()))
.unwrap();
let app =
crate::register_commands(mock_builder().manage(crate::state::AppState::temporary()))
.build(mock_context(noop_assets()))
.unwrap();
let window = tauri::WebviewWindowBuilder::new(&app, "main", Default::default())
.build()
.unwrap();
@@ -43,7 +34,7 @@ impl App {
cmd: cmd.into(),
callback: CallbackFn(0),
error: CallbackFn(1),
url: ORIGIN.parse().unwrap(),
url: "tauri://localhost".parse().unwrap(),
body,
headers,
invoke_key: INVOKE_KEY.to_string(),
@@ -65,7 +56,7 @@ impl App {
cmd: cmd.into(),
callback: CallbackFn(0),
error: CallbackFn(1),
url: ORIGIN.parse().unwrap(),
url: "tauri://localhost".parse().unwrap(),
body: InvokeBody::Json(args),
headers: Default::default(),
invoke_key: INVOKE_KEY.to_string(),
@@ -320,8 +311,8 @@ fn keeps_settings_for_the_session() {
#[test]
fn the_files_are_still_there_in_the_next_session() {
use crate::state::AppState;
use gpx_engine::api;
use gpx_studio_desktop_lib::state::AppState;
let directory =
std::env::temp_dir().join(format!("gpx-studio-session-{}", uuid::Uuid::new_v4()));
@@ -356,14 +347,14 @@ fn only_gpx_files_that_exist_are_taken_from_what_the_system_gives() {
text.to_string_lossy().into_owned(),
directory.join("missing.gpx").to_string_lossy().into_owned(),
];
assert_eq!(gpx_studio_desktop_lib::gpx_paths(arguments), [track]);
assert_eq!(crate::gpx_paths(arguments), [track]);
std::fs::remove_dir_all(directory).unwrap();
}
#[test]
fn a_file_opened_with_the_app_is_opened_selected_and_announced() {
use crate::state::AppState;
use gpx_engine::api;
use gpx_studio_desktop_lib::state::AppState;
use std::sync::{Arc, Mutex};
let directory = std::env::temp_dir().join(format!("gpx-studio-open-{}", uuid::Uuid::new_v4()));
@@ -450,7 +441,7 @@ fn a_file_outside_the_open_folder_is_read_again_when_the_disk_changes() {
let (events, received) = mpsc::channel();
let directory = std::env::temp_dir().join(format!("gpx-store-{}", uuid::Uuid::new_v4()));
let state = gpx_studio_desktop_lib::state::AppState::open(
let state = crate::state::AppState::open(
directory.clone(),
std::sync::Arc::new(move |name, _| {
let _ = events.send(name.to_string());
@@ -470,8 +461,8 @@ fn a_file_outside_the_open_folder_is_read_again_when_the_disk_changes() {
});
let id = uuid::Uuid::parse_str(&id).unwrap();
// the name comes from the path, which does not change: the content does
let signature = |state: &gpx_studio_desktop_lib::state::AppState| {
state.core(move |core| gpx_studio_desktop_lib::workspace::signature(&core.engine, id))
let signature = |state: &crate::state::AppState| {
state.core(move |core| crate::workspace::signature(&core.engine, id))
};
let before = signature(&state);
@@ -492,7 +483,7 @@ fn a_file_outside_the_open_folder_is_read_again_when_the_disk_changes() {
#[test]
fn files_made_from_a_file_of_the_open_folder_are_written_next_to_it_at_once() {
let state = crate::temporary_state();
let state = crate::state::AppState::temporary();
let folder = std::env::temp_dir().join(format!("gpx-tests-{}", uuid::Uuid::new_v4()));
std::fs::create_dir_all(folder.join("sub")).unwrap();
std::fs::copy("../gpx-rs/data/simple.gpx", folder.join("sub/track.gpx")).unwrap();
@@ -525,23 +516,19 @@ fn files_made_from_a_file_of_the_open_folder_are_written_next_to_it_at_once() {
.collect();
// the copy is in the subfolder, named after its name (the one in the file, not of the file)
assert!(
paths
.iter()
.any(|path| std::path::Path::new(path).ends_with("sub/simple.gpx")),
paths.iter().any(|path| path.ends_with("sub/simple.gpx")),
"{paths:?}"
);
// the new file went where the selected file is
assert!(
paths
.iter()
.any(|path| std::path::Path::new(path).ends_with("sub/fresh.gpx")),
paths.iter().any(|path| path.ends_with("sub/fresh.gpx")),
"{paths:?}"
);
}
#[test]
fn the_parts_of_a_split_are_numbered_from_one() {
let state = crate::temporary_state();
let state = crate::state::AppState::temporary();
let folder = std::env::temp_dir().join(format!("gpx-tests-{}", uuid::Uuid::new_v4()));
std::fs::create_dir_all(&folder).unwrap();
std::fs::copy("../gpx-rs/data/simple.gpx", folder.join("track.gpx")).unwrap();
@@ -662,7 +649,7 @@ fn a_file_keeps_its_name_and_its_file_is_renamed_apart() {
// the name of another file is not taken
let error = app.error("rename_file", json!({ "fileId": id, "name": "taken" }));
assert_eq!(error, gpx_studio_desktop_lib::workspace::NAME_TAKEN);
assert_eq!(error, crate::workspace::NAME_TAKEN);
assert!(folder.join("moved.gpx").exists());
// a name is one that a file can have
+812 -2
View File
@@ -148,7 +148,7 @@ pub struct Entry {
pub is_dir: bool,
}
pub fn is_gpx(path: &Path) -> bool {
fn is_gpx(path: &Path) -> bool {
path.extension()
.and_then(|extension| extension.to_str())
.is_some_and(|extension| extension.eq_ignore_ascii_case("gpx"))
@@ -156,7 +156,7 @@ pub fn is_gpx(path: &Path) -> bool {
/// Compares names the way people read them: the numbers in them by their value ("track 2" before
/// "track 10"), the rest regardless of case.
pub fn natural_cmp(a: &str, b: &str) -> Ordering {
fn natural_cmp(a: &str, b: &str) -> Ordering {
let (mut a, mut b) = (a.chars().peekable(), b.chars().peekable());
loop {
let order = match (a.peek().copied(), b.peek().copied()) {
@@ -1040,3 +1040,813 @@ impl Workspace {
})
}
}
#[cfg(test)]
mod tests {
use gpx_engine::SelectMode;
use super::*;
fn gpx(name: &str) -> String {
format!(
r#"<?xml version="1.0"?><gpx version="1.1" creator="test" xmlns="http://www.topografix.com/GPX/1/1"><metadata><name>{name}</name></metadata><trk><name>{name}</name><trkseg><trkpt lat="50.0" lon="4.0"><ele>10</ele></trkpt><trkpt lat="50.1" lon="4.1"><ele>20</ele></trkpt></trkseg></trk></gpx>"#
)
}
/// The GPX files of a folder and of the folders in it.
pub(super) fn gpx_files(folder: &Path) -> Vec<PathBuf> {
let mut found = vec![];
for entry in fs::read_dir(folder).unwrap().flatten() {
let path = entry.path();
if path.is_dir() {
found.extend(gpx_files(&path));
} else if is_gpx(&path) {
found.push(path);
}
}
found.sort();
found
}
struct Fixture {
folder: PathBuf,
engine: Engine,
workspace: Workspace,
}
impl Fixture {
fn new() -> Self {
let folder =
canonical(&std::env::temp_dir()).join(format!("gpx-ws-{}", Uuid::new_v4()));
fs::create_dir_all(&folder).unwrap();
Self {
folder,
engine: Engine::default(),
workspace: Workspace::default(),
}
}
fn write(&self, name: &str, title: &str) -> PathBuf {
let path = self.folder.join(name);
fs::write(&path, gpx(title)).unwrap();
path
}
/// Opens the folder, and all the files that are in it.
fn open_folder(&mut self) {
let folder = self.folder.clone();
self.workspace.open_folder(&mut self.engine, &folder, false);
let files = gpx_files(&folder);
self.workspace.open_files(&mut self.engine, &files, false);
}
fn reconcile(&mut self, changed: Option<&[PathBuf]>) -> Option<api::Outcome> {
self.workspace.reconcile(&mut self.engine, changed)
}
fn save(&mut self, ids: &[Uuid]) {
self.workspace
.save(&mut self.engine, ids, &BTreeMap::new(), None)
.unwrap();
}
fn ids(&self) -> Vec<Uuid> {
self.engine.order().iter().map(|id| id.0).collect()
}
fn id_named(&self, name: &str) -> Uuid {
self.ids()
.into_iter()
.find(|id| self.name(*id) == name)
.unwrap()
}
fn name(&self, id: Uuid) -> String {
self.engine
.file_structure(&FileId(id))
.unwrap()
.name
.clone()
}
/// What the file holds: the name of its track (its own name is the one of the file).
fn content(&self, id: Uuid) -> String {
self.engine.file_structure(&FileId(id)).unwrap().tracks[0]
.info
.name
.clone()
.unwrap_or_default()
}
fn rename(&mut self, id: Uuid, name: &str) {
api::select(&mut self.engine, &[id], SelectMode::Replace);
assert!(!api::metadata(&mut self.engine, name, "").has_error());
}
fn status(&self, id: Uuid) -> FileStatus {
self.workspace
.state(&self.engine)
.files
.remove(&id.to_string())
.unwrap()
}
}
impl Drop for Fixture {
fn drop(&mut self) {
let _ = fs::remove_dir_all(&self.folder);
}
}
#[test]
fn an_opened_file_is_clean_until_it_is_edited_and_clean_again_once_saved() {
let mut fx = Fixture::new();
let path = fx.write("a.gpx", "a");
fx.workspace
.open_files(&mut fx.engine, std::slice::from_ref(&path), true);
let id = fx.ids()[0];
assert!(!fx.workspace.is_dirty(&fx.engine, id));
assert_eq!(fx.status(id).path.as_deref(), path.to_str());
fx.rename(id, "renamed");
assert!(fx.workspace.is_dirty(&fx.engine, id));
// nothing is written before it is saved
assert!(fs::read_to_string(&path).unwrap().contains(">a<"));
fx.save(&[id]);
assert!(!fx.workspace.is_dirty(&fx.engine, id));
assert!(fs::read_to_string(&path).unwrap().contains(">renamed<"));
// going back before the save is an edit again
api::select(&mut fx.engine, &[id], SelectMode::Replace);
api::undo(&mut fx.engine);
assert!(fx.workspace.is_dirty(&fx.engine, id));
api::redo(&mut fx.engine);
assert!(!fx.workspace.is_dirty(&fx.engine, id));
}
#[test]
fn a_file_that_is_not_on_the_disk_is_unsaved_until_it_is_given_a_place() {
let mut fx = Fixture::new();
api::new_file(&mut fx.engine, "fresh", None, None, None, false);
let id = fx.ids()[0];
assert!(fx.workspace.is_dirty(&fx.engine, id));
assert_eq!(
fx.workspace.unlinked(&fx.engine, &[id]),
[(id, "fresh".to_string())]
);
// nowhere to put it without being told
fx.save(&[id]);
assert!(fx.workspace.is_dirty(&fx.engine, id));
let path = fx.folder.join("fresh.gpx");
let paths = BTreeMap::from([(id, path.clone())]);
fx.workspace
.save(&mut fx.engine, &[id], &paths, None)
.unwrap();
assert!(path.exists());
assert!(!fx.workspace.is_dirty(&fx.engine, id));
assert!(fx.workspace.unlinked(&fx.engine, &[id]).is_empty());
}
#[test]
fn several_files_without_a_place_go_in_a_folder_with_names_that_are_free() {
let mut fx = Fixture::new();
fx.write("fresh.gpx", "already there");
api::new_file(&mut fx.engine, "fresh", None, None, None, false);
api::new_file(&mut fx.engine, "a/b: c", None, None, None, false);
api::new_file(&mut fx.engine, "fresh", None, None, None, false);
let ids = fx.ids();
let folder = fx.folder.clone();
fx.workspace
.save(&mut fx.engine, &ids, &BTreeMap::new(), Some(&folder))
.unwrap();
for name in ["fresh (2).gpx", "fresh (3).gpx", "a_b_ c.gpx"] {
assert!(fx.folder.join(name).exists(), "{name}");
}
assert!(ids.iter().all(|id| !fx.workspace.is_dirty(&fx.engine, *id)));
}
#[test]
fn what_this_app_writes_is_not_a_change_of_the_disk() {
let mut fx = Fixture::new();
let path = fx.write("a.gpx", "a");
fx.open_folder();
let id = fx.ids()[0];
api::select(&mut fx.engine, &[id], SelectMode::Replace);
assert!(!api::style(&mut fx.engine, Some("#ff0000"), None, None).has_error());
fx.save(&[id]);
assert!(fx.reconcile(Some(&[path])).is_none());
assert!(!fx.status(id).replaced && !fx.status(id).dirty);
}
#[test]
fn the_folders_of_the_files_outside_the_open_folder_are_watched_too() {
let mut fx = Fixture::new();
let inside = fx.write("in.gpx", "in");
let elsewhere = canonical(&std::env::temp_dir()).join(format!("gpx-ws-{}", Uuid::new_v4()));
fs::create_dir_all(&elsewhere).unwrap();
let (one, two) = (elsewhere.join("one.gpx"), elsewhere.join("two.gpx"));
fs::write(&one, gpx("one")).unwrap();
fs::write(&two, gpx("two")).unwrap();
let folder = fx.folder.clone();
// no folder: only the folder of the files
fx.workspace.open_files(&mut fx.engine, &[one, two], false);
assert_eq!(fx.workspace.watched(), [(elsewhere.clone(), false)]);
// the open folder as a whole, and the file in it needs nothing more
fx.workspace.open_folder(&mut fx.engine, &folder, false);
fx.workspace.open_files(&mut fx.engine, &[inside], false);
assert_eq!(
fx.workspace.watched(),
[(folder, true), (elsewhere.clone(), false)]
);
// closing the files stops it
let ids = fx.ids();
fx.workspace.close_files(&mut fx.engine, &ids, true);
assert_eq!(fx.workspace.watched(), [(fx.folder.clone(), true)]);
fs::remove_dir_all(elsewhere).unwrap();
}
#[test]
fn opening_a_folder_does_not_open_its_files() {
let mut fx = Fixture::new();
fx.write("a.gpx", "a");
let folder = fx.folder.clone();
let outcome = fx.workspace.open_folder(&mut fx.engine, &folder, false);
assert!(!outcome.changed);
assert!(fx.ids().is_empty());
assert_eq!(fx.workspace.folder.as_deref(), Some(folder.as_path()));
}
#[test]
fn folders_are_made_renamed_and_moved_and_the_open_files_follow() {
let mut fx = Fixture::new();
let a = fx.write("a.gpx", "a");
fx.open_folder();
let id = fx.ids()[0];
let root = fx.folder.clone();
let path_of = |fx: &Fixture| PathBuf::from(fx.status(id).path.unwrap());
let first = fx.workspace.create_folder(&root, "New folder").unwrap();
let second = fx.workspace.create_folder(&root, "New folder").unwrap();
assert_eq!(first, root.join("New folder"));
assert_eq!(second, root.join("New folder 2"));
fx.workspace.move_paths(&[a], &first).unwrap();
assert_eq!(path_of(&fx), first.join("a.gpx"));
assert!(first.join("a.gpx").exists() && !fx.status(id).dirty);
let trips = fx.workspace.rename_path(&first, "Trips").unwrap();
assert_eq!(path_of(&fx), root.join("Trips/a.gpx"));
// a folder does not go into itself, nor onto something else
assert!(
fx.workspace
.move_paths(std::slice::from_ref(&trips), &trips)
.is_err()
);
assert_eq!(
fx.workspace.rename_path(&trips, "New folder 2"),
Err(NAME_TAKEN.to_owned())
);
fx.workspace.move_paths(&[trips], &second).unwrap();
assert_eq!(path_of(&fx), second.join("Trips/a.gpx"));
// a file keeps its extension
let closed = fx.write("c.gpx", "c");
assert_eq!(
fx.workspace.rename_path(&closed, "d").unwrap(),
root.join("d.gpx")
);
// the open folder and what is not in it are left alone
assert!(fx.workspace.rename_path(&root, "x").is_err());
let elsewhere = canonical(&std::env::temp_dir());
assert!(fx.workspace.rename_path(&elsewhere, "x").is_err());
assert!(fx.workspace.create_folder(&elsewhere, "x").is_err());
}
#[test]
fn deleted_files_are_closed_unless_they_have_edits() {
let mut fx = Fixture::new();
let a = fx.write("a.gpx", "a");
let b = fx.write("b.gpx", "b");
fx.open_folder();
let (id_a, id_b) = (fx.id_named("a"), fx.id_named("b"));
api::select(&mut fx.engine, &[id_b], SelectMode::Replace);
assert!(!api::style(&mut fx.engine, Some("#ff0000"), None, None).has_error());
let outcome = fx
.workspace
.delete_paths(&mut fx.engine, &[a.clone(), b.clone()], |paths| {
paths.iter().for_each(|path| fs::remove_file(path).unwrap());
Ok(())
})
.unwrap();
assert!(outcome.changed);
assert_eq!(fx.ids(), [id_b]);
assert!(!fx.ids().contains(&id_a));
let status = fx.status(id_b);
assert!(status.dirty && status.missing);
assert!(!a.exists() && !b.exists());
}
#[test]
fn the_tree_of_the_folder_shows_folders_first_and_only_gpx_files() {
let mut fx = Fixture::new();
fx.write("b.gpx", "b");
fx.write("A.GPX", "a");
fx.write("b 10.gpx", "b 10");
fx.write("b 2.gpx", "b 2");
fx.write("notes.txt", "not a track");
fx.write(".hidden.gpx", "hidden");
fs::create_dir(fx.folder.join("zoo")).unwrap();
fs::create_dir(fx.folder.join("Alps")).unwrap();
fs::create_dir(fx.folder.join(".git")).unwrap();
fx.write("zoo/c.gpx", "c");
let folder = fx.folder.clone();
fx.workspace.open_folder(&mut fx.engine, &folder, false);
let names = |entries: Vec<Entry>| -> Vec<(String, bool)> {
entries.into_iter().map(|e| (e.name, e.is_dir)).collect()
};
assert_eq!(
names(fx.workspace.list_folder(&folder).unwrap()),
[
("Alps".to_string(), true),
("zoo".to_string(), true),
("A.GPX".to_string(), false),
("b.gpx".to_string(), false),
("b 2.gpx".to_string(), false),
("b 10.gpx".to_string(), false),
]
);
assert_eq!(
names(fx.workspace.list_folder(&folder.join("zoo")).unwrap()),
[("c.gpx".to_string(), false)]
);
// only what is in the open folder can be listed
let elsewhere = canonical(&std::env::temp_dir());
assert!(fx.workspace.list_folder(&elsewhere).is_err());
}
#[test]
fn names_are_sorted_by_the_numbers_they_hold() {
let mut names = vec![
"track 10",
"track 2",
"Track 1",
"track",
"track 02b",
"track 2a",
"b",
"A",
];
names.sort_by(|a, b| natural_cmp(a, b).then_with(|| a.cmp(b)));
assert_eq!(
names,
[
"A",
"b",
"track",
"Track 1",
"track 2",
"track 2a",
"track 02b",
"track 10",
]
);
}
#[test]
fn a_file_without_edits_is_read_again_when_the_disk_changes() {
let mut fx = Fixture::new();
let a = fx.write("a.gpx", "a");
fx.open_folder();
let id = fx.ids()[0];
fx.write("a.gpx", "theirs");
let outcome = fx.reconcile(Some(std::slice::from_ref(&a))).unwrap();
assert!(outcome.changed);
// the same file, now the one on the disk
assert_eq!(fx.ids(), [id]);
assert_eq!(fx.content(id), "theirs");
let status = fx.status(id);
assert!(!status.dirty && !status.replaced);
// looking again changes nothing
assert!(fx.reconcile(Some(std::slice::from_ref(&a))).is_none());
}
#[test]
fn a_file_with_edits_is_flagged_when_the_disk_changes_and_not_read_again() {
let mut fx = Fixture::new();
let a = fx.write("a.gpx", "a");
fx.open_folder();
let id = fx.ids()[0];
fx.rename(id, "mine");
fx.write("a.gpx", "theirs");
let outcome = fx.reconcile(Some(std::slice::from_ref(&a))).unwrap();
assert!(!outcome.changed);
assert_eq!(fx.name(id), "mine");
let status = fx.status(id);
assert!(status.dirty && status.replaced && !status.missing);
// looking again changes nothing
assert!(fx.reconcile(Some(std::slice::from_ref(&a))).is_none());
// the disk goes back to what was read: only the edits are left
fx.write("a.gpx", "a");
assert!(fx.reconcile(Some(std::slice::from_ref(&a))).is_some());
let status = fx.status(id);
assert!(status.dirty && !status.replaced);
}
#[test]
fn the_user_can_take_the_disk_instead_of_the_edits() {
let mut fx = Fixture::new();
fx.write("a.gpx", "a");
fx.open_folder();
let id = fx.ids()[0];
fx.rename(id, "mine");
fx.write("a.gpx", "theirs");
fx.reconcile(None);
assert!(fx.status(id).replaced);
fx.workspace.reload(&mut fx.engine, &[id]).unwrap();
assert_eq!(fx.content(id), "theirs");
let status = fx.status(id);
assert!(!status.dirty && !status.replaced);
// and it is one undo step
api::select(&mut fx.engine, &[id], SelectMode::Replace);
api::undo(&mut fx.engine);
assert_eq!(fx.content(id), "mine");
}
#[test]
fn saving_overwrites_what_replaced_the_file() {
let mut fx = Fixture::new();
let a = fx.write("a.gpx", "a");
fx.open_folder();
let id = fx.ids()[0];
fx.rename(id, "mine");
fx.write("a.gpx", "theirs");
fx.reconcile(None);
assert!(fx.status(id).replaced);
fx.save(&[id]);
assert!(fs::read_to_string(a).unwrap().contains(">mine<"));
assert!(!fx.status(id).replaced && !fx.status(id).dirty);
}
#[test]
fn a_file_that_is_gone_from_the_disk_stays_open_and_dirty_until_it_is_saved_again() {
let mut fx = Fixture::new();
let a = fx.write("a.gpx", "a");
fx.open_folder();
let id = fx.ids()[0];
fs::remove_file(&a).unwrap();
assert!(fx.reconcile(Some(std::slice::from_ref(&a))).is_some());
assert_eq!(fx.ids(), [id]);
let status = fx.status(id);
assert!(status.dirty && status.missing && status.path.is_some());
// saving puts it back
fx.save(&[id]);
assert!(a.exists());
let status = fx.status(id);
assert!(!status.dirty && !status.missing);
}
#[test]
fn a_file_that_comes_back_different_is_read_again() {
let mut fx = Fixture::new();
let a = fx.write("a.gpx", "a");
fx.open_folder();
let id = fx.ids()[0];
fs::remove_file(&a).unwrap();
fx.reconcile(None);
assert!(fx.status(id).missing);
fx.write("a.gpx", "back");
fx.reconcile(None);
assert_eq!(fx.content(id), "back");
let status = fx.status(id);
assert!(!status.dirty && !status.missing);
}
#[test]
fn what_changed_while_the_app_was_closed_is_found_when_it_starts() {
let mut fx = Fixture::new();
let a = fx.write("a.gpx", "a");
fx.open_folder();
let id = fx.ids()[0];
fx.write("a.gpx", "changed");
assert!(fx.reconcile(None).is_some());
assert_eq!(fx.content(id), "changed");
fs::remove_file(a).unwrap();
assert!(fx.reconcile(None).is_some());
assert!(fx.status(id).missing);
}
#[test]
fn new_files_made_from_a_file_of_the_folder_are_saved_next_to_it() {
let mut fx = Fixture::new();
fs::create_dir(fx.folder.join("sub")).unwrap();
fx.write("sub/b.gpx", "b");
fx.write("a.gpx", "a");
fx.open_folder();
let b = fx.id_named("b");
let sub = fx.folder.join("sub");
assert_eq!(fx.workspace.source_dir(&[b]), Some(canonical(&sub)));
let before = fx.ids();
api::select(&mut fx.engine, &[b], SelectMode::Replace);
let outcome = api::duplicate(&mut fx.engine);
let copy = fx
.ids()
.into_iter()
.find(|id| !before.contains(id))
.unwrap();
fx.workspace.inherit(outcome.added(), &canonical(&sub));
assert_eq!(fx.status(copy).folder.as_deref(), canonical(&sub).to_str());
assert!(fx.status(copy).path.is_none() && fx.status(copy).dirty);
// nobody needs to be asked where it goes
assert!(fx.workspace.unlinked(&fx.engine, &[copy]).is_empty());
fx.save(&[copy]);
assert!(sub.join("b (2).gpx").exists());
let status = fx.status(copy);
assert!(!status.dirty && status.folder.is_none());
assert_eq!(
status.path.as_deref(),
canonical(&sub.join("b (2).gpx")).to_str()
);
// the copy of the copy is next to it as well
assert_eq!(fx.workspace.source_dir(&[copy]), Some(canonical(&sub)));
}
#[test]
fn a_file_outside_the_open_folder_hands_nothing_down() {
let mut fx = Fixture::new();
let elsewhere = canonical(&std::env::temp_dir()).join(format!("gpx-ws-{}", Uuid::new_v4()));
fs::create_dir_all(&elsewhere).unwrap();
let outside = elsewhere.join("o.gpx");
fs::write(&outside, gpx("o")).unwrap();
let folder = fx.folder.clone();
fx.workspace.open_folder(&mut fx.engine, &folder, false);
fx.workspace
.open_files(&mut fx.engine, std::slice::from_ref(&outside), true);
let id = fx.ids()[0];
assert_eq!(fx.workspace.source_dir(&[id]), None);
// a new file with no source is not linked to a folder by `source_dir`: the caller falls back
fs::remove_dir_all(elsewhere).unwrap();
}
#[test]
fn closing_files_asks_what_to_do_with_the_unsaved_ones() {
let mut fx = Fixture::new();
fx.write("a.gpx", "a");
fx.write("b.gpx", "b");
fx.open_folder();
let (a, b) = (fx.id_named("a"), fx.id_named("b"));
fx.rename(a, "edited");
assert_eq!(fx.workspace.unsaved(&fx.engine, &[a, b]), [a]);
// without a choice what has edits stays
let outcome = fx.workspace.close_files(&mut fx.engine, &[a, b], false);
assert!(outcome.changed);
assert_eq!(fx.ids(), [a]);
// saved first
fx.save(&[a]);
fx.workspace.close_files(&mut fx.engine, &[a], false);
assert!(fx.ids().is_empty());
assert!(
fs::read_to_string(fx.folder.join("a.gpx"))
.unwrap()
.contains("edited")
);
assert!(fx.workspace.links.is_empty());
}
#[test]
fn closing_files_can_discard_their_edits() {
let mut fx = Fixture::new();
fx.write("a.gpx", "a");
fx.open_folder();
let a = fx.ids()[0];
fx.rename(a, "edited");
api::new_file(&mut fx.engine, "fresh", None, None, None, false);
let fresh = fx.id_named("fresh");
fx.workspace.close_files(&mut fx.engine, &[a, fresh], true);
assert!(fx.ids().is_empty());
assert!(
fs::read_to_string(fx.folder.join("a.gpx"))
.unwrap()
.contains(">a<")
);
}
#[test]
fn closing_the_folder_closes_every_file() {
let mut fx = Fixture::new();
fx.write("a.gpx", "a");
fx.open_folder();
api::new_file(&mut fx.engine, "fresh", None, None, None, false);
assert_eq!(fx.ids().len(), 2);
// the new file has nowhere to go: it has to be discarded
let outcome = fx.workspace.close_folder(&mut fx.engine, false);
assert!(outcome.changed);
assert_eq!(fx.ids().len(), 1);
assert!(fx.workspace.folder.is_none());
let outcome = fx.workspace.close_folder(&mut fx.engine, true);
assert!(outcome.changed);
assert!(fx.ids().is_empty());
}
#[test]
fn opening_another_folder_closes_the_first_with_all_its_files() {
let mut fx = Fixture::new();
fx.write("a.gpx", "a");
fx.open_folder();
let a = fx.ids()[0];
fx.rename(a, "edited");
let other = canonical(&std::env::temp_dir()).join(format!("gpx-ws-{}", Uuid::new_v4()));
fs::create_dir_all(&other).unwrap();
// the edits stay, nothing is lost without a choice
fx.workspace.open_folder(&mut fx.engine, &other, false);
assert_eq!(fx.ids(), [a]);
assert_eq!(fx.workspace.folder.as_deref(), Some(other.as_path()));
let first = fx.folder.clone();
fx.workspace.open_folder(&mut fx.engine, &first, true);
assert!(fx.ids().is_empty());
assert!(
fs::read_to_string(fx.folder.join("a.gpx"))
.unwrap()
.contains(">a<")
);
fs::remove_dir_all(other).unwrap();
}
#[test]
fn opening_the_same_folder_again_changes_nothing() {
let mut fx = Fixture::new();
fx.write("a.gpx", "a");
fx.open_folder();
let folder = fx.folder.clone();
let outcome = fx.workspace.open_folder(&mut fx.engine, &folder, true);
assert!(!outcome.changed);
assert_eq!(fx.ids().len(), 1);
}
#[test]
fn a_failed_save_stops_at_the_file_and_leaves_it_dirty() {
let mut fx = Fixture::new();
fx.write("a.gpx", "a");
fx.open_folder();
let id = fx.ids()[0];
fx.rename(id, "edited");
// the place of the file is taken by a folder
fs::remove_file(fx.folder.join("a.gpx")).unwrap();
fs::create_dir(fx.folder.join("a.gpx")).unwrap();
let result = fx
.workspace
.save(&mut fx.engine, &[id], &BTreeMap::new(), None);
assert!(result.is_err());
assert!(fx.workspace.is_dirty(&fx.engine, id));
}
#[test]
fn saving_as_gives_the_file_another_place() {
let mut fx = Fixture::new();
let a = fx.write("a.gpx", "a");
fx.write("b.gpx", "b");
fx.open_folder();
let id = fx.id_named("a");
let other = fx.id_named("b");
fx.rename(id, "edited");
let target = fx.folder.join("copy.gpx");
fx.workspace.save_as(&mut fx.engine, id, &target).unwrap();
assert!(fs::read_to_string(&target).unwrap().contains("edited"));
assert!(fs::read_to_string(a).unwrap().contains(">a<"));
let status = fx.status(id);
assert!(!status.dirty);
assert_eq!(status.path.as_deref(), canonical(&target).to_str());
// the place of another open file is not available
let taken = fx.folder.join("b.gpx");
assert!(fx.workspace.save_as(&mut fx.engine, id, &taken).is_err());
assert!(!fx.workspace.is_dirty(&fx.engine, other));
}
}
#[cfg(test)]
mod restart_tests {
use gpx_engine::Persister;
use super::*;
use crate::store::FileStorage;
/// What the app does when it starts again: the engine and the links are read from what was
/// kept.
fn restart(engine: &Engine, workspace: &Workspace, root: &Path) -> (Engine, Workspace) {
let storage = FileStorage::open(root).unwrap();
pollster::block_on(Persister::default().save(&storage, engine.snapshot())).unwrap();
let mut restarted = Engine::default();
let restored = pollster::block_on(Persister::default().restore(&storage)).unwrap();
restarted.restore(restored);
let links = serde_json::to_string(workspace).unwrap();
(restarted, serde_json::from_str(&links).unwrap())
}
fn setup() -> (PathBuf, Engine, Workspace, Vec<Uuid>) {
let root = std::env::temp_dir().join(format!("gpx-restart-{}", Uuid::new_v4()));
let folder = root.join("tracks");
fs::create_dir_all(&folder).unwrap();
for name in ["with_tracks", "with_time", "with_waypoint"] {
let _ = fs::copy(
format!("../gpx-rs/data/{name}.gpx"),
folder.join(format!("{name}.gpx")),
);
}
let mut engine = Engine::default();
let mut workspace = Workspace::default();
workspace.open_folder(&mut engine, &folder, false);
workspace.open_files(&mut engine, &tests::gpx_files(&folder), false);
let ids = engine.order().iter().map(|id| id.0).collect();
(root, engine, workspace, ids)
}
#[test]
fn files_that_were_not_edited_are_not_unsaved_after_a_restart() {
let (root, engine, workspace, ids) = setup();
assert!(ids.len() >= 2);
let (restarted, mut workspace) = restart(&engine, &workspace, &root.join("store"));
// the files are the same, but the engine knows their contents by new identities
assert!(ids.iter().any(|id| workspace.is_dirty(&restarted, *id)));
workspace.verify(&restarted);
assert!(ids.iter().all(|id| !workspace.is_dirty(&restarted, *id)));
fs::remove_dir_all(root).unwrap();
}
#[test]
fn files_that_were_edited_are_still_unsaved_after_a_restart() {
let (root, mut engine, workspace, ids) = setup();
api::select(&mut engine, &[ids[0]], gpx_engine::SelectMode::Replace);
api::metadata(&mut engine, "edited", "");
assert!(workspace.is_dirty(&engine, ids[0]));
let (restarted, mut workspace) = restart(&engine, &workspace, &root.join("store"));
workspace.verify(&restarted);
assert!(workspace.is_dirty(&restarted, ids[0]));
assert!(
ids[1..]
.iter()
.all(|id| !workspace.is_dirty(&restarted, *id))
);
fs::remove_dir_all(root).unwrap();
}
#[test]
fn links_made_before_the_content_was_kept_are_checked_against_the_disk() {
let (root, mut engine, mut workspace, ids) = setup();
api::select(&mut engine, &[ids[0]], gpx_engine::SelectMode::Replace);
api::metadata(&mut engine, "edited", "");
for link in workspace.links.values_mut() {
link.export_hash = 0;
}
let (restarted, mut workspace) = restart(&engine, &workspace, &root.join("store"));
workspace.verify(&restarted);
assert!(workspace.is_dirty(&restarted, ids[0]));
assert!(
ids[1..]
.iter()
.all(|id| !workspace.is_dirty(&restarted, *id))
);
fs::remove_dir_all(root).unwrap();
}
#[test]
fn the_folders_of_new_files_and_the_old_flag_name_survive_a_restart() {
let (root, engine, mut workspace, ids) = setup();
let folder = workspace.folder.clone().unwrap();
let fresh = Uuid::new_v4();
workspace.inherit(&[fresh.to_string()], &folder);
workspace.links.get_mut(&ids[0]).unwrap().replaced = true;
let json = serde_json::to_string(&workspace).unwrap();
// links written by the previous version had `conflict`, which is the same thing
let json = json.replace("\"replaced\"", "\"conflict\"");
assert!(json.contains("\"conflict\":true"));
let read: Workspace = serde_json::from_str(&json).unwrap();
assert!(read.links[&ids[0]].replaced);
assert_eq!(read.dirs.get(&fresh), Some(&folder));
let _ = engine;
fs::remove_dir_all(root).unwrap();
}
}
-15
View File
@@ -1,15 +0,0 @@
//! The tests of the desktop app. They are integration tests, in a single binary, rather than unit
//! tests: on Windows, only test binaries can be given the manifest that the app needs to start
//! (see `build.rs`).
mod commands;
mod store;
mod workspace;
use gpx_studio_desktop_lib::state::AppState;
/// An engine that keeps its data in a directory of its own, and has nobody to tell.
fn temporary_state() -> AppState {
let directory = std::env::temp_dir().join(format!("gpx-studio-{}", uuid::Uuid::new_v4()));
AppState::open(directory, std::sync::Arc::new(|_, _| {})).expect("the storage can be opened")
}
-69
View File
@@ -1,69 +0,0 @@
//! The storage of the engine on the file system (`store`).
use std::fs;
use std::path::PathBuf;
use gpx_engine::{
Action, Batch, Command, CommitError, Load, LoadFiles, Persister, Revision, Storage,
};
use gpx_studio_desktop_lib::Engine;
use gpx_studio_desktop_lib::store::FileStorage;
use uuid::Uuid;
fn directory() -> PathBuf {
std::env::temp_dir().join(format!("gpx-studio-store-{}", Uuid::new_v4()))
}
fn engine_with_a_file() -> Engine {
let mut engine = Engine::default();
let data = std::fs::read("../gpx-rs/data/simple.gpx").unwrap();
engine
.execute(Action::Edit(Command::LoadFiles(LoadFiles {
files: vec![Load {
data: &data,
name: "simple",
}],
})))
.unwrap();
engine
}
#[test]
fn keeps_the_files_and_the_settings_between_sessions() {
let root = directory();
let engine = engine_with_a_file();
let order = engine.order().to_vec();
let storage = FileStorage::open(&root).unwrap();
pollster::block_on(Persister::default().save(&storage, engine.snapshot())).unwrap();
pollster::block_on(storage.put_setting("units", "\"metric\"")).unwrap();
// another session
let storage = FileStorage::open(&root).unwrap();
let restored = pollster::block_on(Persister::default().restore(&storage)).unwrap();
let mut other = Engine::default();
other.restore(restored);
assert_eq!(other.order(), order);
assert_eq!(
other.file_statistics(&order[0]).unwrap().total_distance,
engine.file_statistics(&order[0]).unwrap().total_distance
);
assert_eq!(storage.settings()["units"], "\"metric\"");
fs::remove_dir_all(root).unwrap();
}
#[test]
fn refuses_a_commit_that_is_not_based_on_the_head() {
let root = directory();
let storage = FileStorage::open(&root).unwrap();
let batch = Batch {
order: Some(vec![]),
..Default::default()
};
assert_eq!(pollster::block_on(storage.commit(&batch)), Ok(Revision(1)));
assert_eq!(
pollster::block_on(storage.commit(&batch)),
Err(CommitError::Stale { head: Revision(1) })
);
fs::remove_dir_all(root).unwrap();
}
-811
View File
@@ -1,811 +0,0 @@
//! The places of the files of the engine on the disk (`workspace`).
use std::collections::BTreeMap;
use std::fs;
use std::path::{Path, PathBuf};
use gpx_engine::{FileId, SelectMode, api};
use gpx_studio_desktop_lib::Engine;
use gpx_studio_desktop_lib::workspace::*;
use uuid::Uuid;
fn gpx(name: &str) -> String {
format!(
r#"<?xml version="1.0"?><gpx version="1.1" creator="test" xmlns="http://www.topografix.com/GPX/1/1"><metadata><name>{name}</name></metadata><trk><name>{name}</name><trkseg><trkpt lat="50.0" lon="4.0"><ele>10</ele></trkpt><trkpt lat="50.1" lon="4.1"><ele>20</ele></trkpt></trkseg></trk></gpx>"#
)
}
/// The GPX files of a folder and of the folders in it.
pub fn gpx_files(folder: &Path) -> Vec<PathBuf> {
let mut found = vec![];
for entry in fs::read_dir(folder).unwrap().flatten() {
let path = entry.path();
if path.is_dir() {
found.extend(gpx_files(&path));
} else if is_gpx(&path) {
found.push(path);
}
}
found.sort();
found
}
struct Fixture {
folder: PathBuf,
engine: Engine,
workspace: Workspace,
}
impl Fixture {
fn new() -> Self {
let folder = canonical(&std::env::temp_dir()).join(format!("gpx-ws-{}", Uuid::new_v4()));
fs::create_dir_all(&folder).unwrap();
Self {
folder,
engine: Engine::default(),
workspace: Workspace::default(),
}
}
fn write(&self, name: &str, title: &str) -> PathBuf {
let path = self.folder.join(name);
fs::write(&path, gpx(title)).unwrap();
path
}
/// Opens the folder, and all the files that are in it.
fn open_folder(&mut self) {
let folder = self.folder.clone();
self.workspace.open_folder(&mut self.engine, &folder, false);
let files = gpx_files(&folder);
self.workspace.open_files(&mut self.engine, &files, false);
}
fn reconcile(&mut self, changed: Option<&[PathBuf]>) -> Option<api::Outcome> {
self.workspace.reconcile(&mut self.engine, changed)
}
fn save(&mut self, ids: &[Uuid]) {
self.workspace
.save(&mut self.engine, ids, &BTreeMap::new(), None)
.unwrap();
}
fn ids(&self) -> Vec<Uuid> {
self.engine.order().iter().map(|id| id.0).collect()
}
fn id_named(&self, name: &str) -> Uuid {
self.ids()
.into_iter()
.find(|id| self.name(*id) == name)
.unwrap()
}
fn name(&self, id: Uuid) -> String {
self.engine
.file_structure(&FileId(id))
.unwrap()
.name
.clone()
}
/// What the file holds: the name of its track (its own name is the one of the file).
fn content(&self, id: Uuid) -> String {
self.engine.file_structure(&FileId(id)).unwrap().tracks[0]
.info
.name
.clone()
.unwrap_or_default()
}
fn rename(&mut self, id: Uuid, name: &str) {
api::select(&mut self.engine, &[id], SelectMode::Replace);
assert!(!api::metadata(&mut self.engine, name, "").has_error());
}
fn status(&self, id: Uuid) -> FileStatus {
self.workspace
.state(&self.engine)
.files
.remove(&id.to_string())
.unwrap()
}
}
impl Drop for Fixture {
fn drop(&mut self) {
let _ = fs::remove_dir_all(&self.folder);
}
}
#[test]
fn an_opened_file_is_clean_until_it_is_edited_and_clean_again_once_saved() {
let mut fx = Fixture::new();
let path = fx.write("a.gpx", "a");
fx.workspace
.open_files(&mut fx.engine, std::slice::from_ref(&path), true);
let id = fx.ids()[0];
assert!(!fx.workspace.is_dirty(&fx.engine, id));
assert_eq!(fx.status(id).path.as_deref(), path.to_str());
fx.rename(id, "renamed");
assert!(fx.workspace.is_dirty(&fx.engine, id));
// nothing is written before it is saved
assert!(fs::read_to_string(&path).unwrap().contains(">a<"));
fx.save(&[id]);
assert!(!fx.workspace.is_dirty(&fx.engine, id));
assert!(fs::read_to_string(&path).unwrap().contains(">renamed<"));
// going back before the save is an edit again
api::select(&mut fx.engine, &[id], SelectMode::Replace);
api::undo(&mut fx.engine);
assert!(fx.workspace.is_dirty(&fx.engine, id));
api::redo(&mut fx.engine);
assert!(!fx.workspace.is_dirty(&fx.engine, id));
}
#[test]
fn a_file_that_is_not_on_the_disk_is_unsaved_until_it_is_given_a_place() {
let mut fx = Fixture::new();
api::new_file(&mut fx.engine, "fresh", None, None, None, false);
let id = fx.ids()[0];
assert!(fx.workspace.is_dirty(&fx.engine, id));
assert_eq!(
fx.workspace.unlinked(&fx.engine, &[id]),
[(id, "fresh".to_string())]
);
// nowhere to put it without being told
fx.save(&[id]);
assert!(fx.workspace.is_dirty(&fx.engine, id));
let path = fx.folder.join("fresh.gpx");
let paths = BTreeMap::from([(id, path.clone())]);
fx.workspace
.save(&mut fx.engine, &[id], &paths, None)
.unwrap();
assert!(path.exists());
assert!(!fx.workspace.is_dirty(&fx.engine, id));
assert!(fx.workspace.unlinked(&fx.engine, &[id]).is_empty());
}
#[test]
fn several_files_without_a_place_go_in_a_folder_with_names_that_are_free() {
let mut fx = Fixture::new();
fx.write("fresh.gpx", "already there");
api::new_file(&mut fx.engine, "fresh", None, None, None, false);
api::new_file(&mut fx.engine, "a/b: c", None, None, None, false);
api::new_file(&mut fx.engine, "fresh", None, None, None, false);
let ids = fx.ids();
let folder = fx.folder.clone();
fx.workspace
.save(&mut fx.engine, &ids, &BTreeMap::new(), Some(&folder))
.unwrap();
for name in ["fresh (2).gpx", "fresh (3).gpx", "a_b_ c.gpx"] {
assert!(fx.folder.join(name).exists(), "{name}");
}
assert!(ids.iter().all(|id| !fx.workspace.is_dirty(&fx.engine, *id)));
}
#[test]
fn what_this_app_writes_is_not_a_change_of_the_disk() {
let mut fx = Fixture::new();
let path = fx.write("a.gpx", "a");
fx.open_folder();
let id = fx.ids()[0];
api::select(&mut fx.engine, &[id], SelectMode::Replace);
assert!(!api::style(&mut fx.engine, Some("#ff0000"), None, None).has_error());
fx.save(&[id]);
assert!(fx.reconcile(Some(&[path])).is_none());
assert!(!fx.status(id).replaced && !fx.status(id).dirty);
}
#[test]
fn the_folders_of_the_files_outside_the_open_folder_are_watched_too() {
let mut fx = Fixture::new();
let inside = fx.write("in.gpx", "in");
let elsewhere = canonical(&std::env::temp_dir()).join(format!("gpx-ws-{}", Uuid::new_v4()));
fs::create_dir_all(&elsewhere).unwrap();
let (one, two) = (elsewhere.join("one.gpx"), elsewhere.join("two.gpx"));
fs::write(&one, gpx("one")).unwrap();
fs::write(&two, gpx("two")).unwrap();
let folder = fx.folder.clone();
// no folder: only the folder of the files
fx.workspace.open_files(&mut fx.engine, &[one, two], false);
assert_eq!(fx.workspace.watched(), [(elsewhere.clone(), false)]);
// the open folder as a whole, and the file in it needs nothing more
fx.workspace.open_folder(&mut fx.engine, &folder, false);
fx.workspace.open_files(&mut fx.engine, &[inside], false);
assert_eq!(
fx.workspace.watched(),
[(folder, true), (elsewhere.clone(), false)]
);
// closing the files stops it
let ids = fx.ids();
fx.workspace.close_files(&mut fx.engine, &ids, true);
assert_eq!(fx.workspace.watched(), [(fx.folder.clone(), true)]);
fs::remove_dir_all(elsewhere).unwrap();
}
#[test]
fn opening_a_folder_does_not_open_its_files() {
let mut fx = Fixture::new();
fx.write("a.gpx", "a");
let folder = fx.folder.clone();
let outcome = fx.workspace.open_folder(&mut fx.engine, &folder, false);
assert!(!outcome.changed);
assert!(fx.ids().is_empty());
assert_eq!(fx.workspace.folder.as_deref(), Some(folder.as_path()));
}
#[test]
fn folders_are_made_renamed_and_moved_and_the_open_files_follow() {
let mut fx = Fixture::new();
let a = fx.write("a.gpx", "a");
fx.open_folder();
let id = fx.ids()[0];
let root = fx.folder.clone();
let path_of = |fx: &Fixture| PathBuf::from(fx.status(id).path.unwrap());
let first = fx.workspace.create_folder(&root, "New folder").unwrap();
let second = fx.workspace.create_folder(&root, "New folder").unwrap();
assert_eq!(first, root.join("New folder"));
assert_eq!(second, root.join("New folder 2"));
fx.workspace.move_paths(&[a], &first).unwrap();
assert_eq!(path_of(&fx), first.join("a.gpx"));
assert!(first.join("a.gpx").exists() && !fx.status(id).dirty);
let trips = fx.workspace.rename_path(&first, "Trips").unwrap();
assert_eq!(path_of(&fx), root.join("Trips/a.gpx"));
// a folder does not go into itself, nor onto something else
assert!(
fx.workspace
.move_paths(std::slice::from_ref(&trips), &trips)
.is_err()
);
assert_eq!(
fx.workspace.rename_path(&trips, "New folder 2"),
Err(NAME_TAKEN.to_owned())
);
fx.workspace.move_paths(&[trips], &second).unwrap();
assert_eq!(path_of(&fx), second.join("Trips/a.gpx"));
// a file keeps its extension
let closed = fx.write("c.gpx", "c");
assert_eq!(
fx.workspace.rename_path(&closed, "d").unwrap(),
root.join("d.gpx")
);
// the open folder and what is not in it are left alone
assert!(fx.workspace.rename_path(&root, "x").is_err());
let elsewhere = canonical(&std::env::temp_dir());
assert!(fx.workspace.rename_path(&elsewhere, "x").is_err());
assert!(fx.workspace.create_folder(&elsewhere, "x").is_err());
}
#[test]
fn deleted_files_are_closed_unless_they_have_edits() {
let mut fx = Fixture::new();
let a = fx.write("a.gpx", "a");
let b = fx.write("b.gpx", "b");
fx.open_folder();
let (id_a, id_b) = (fx.id_named("a"), fx.id_named("b"));
api::select(&mut fx.engine, &[id_b], SelectMode::Replace);
assert!(!api::style(&mut fx.engine, Some("#ff0000"), None, None).has_error());
let outcome = fx
.workspace
.delete_paths(&mut fx.engine, &[a.clone(), b.clone()], |paths| {
paths.iter().for_each(|path| fs::remove_file(path).unwrap());
Ok(())
})
.unwrap();
assert!(outcome.changed);
assert_eq!(fx.ids(), [id_b]);
assert!(!fx.ids().contains(&id_a));
let status = fx.status(id_b);
assert!(status.dirty && status.missing);
assert!(!a.exists() && !b.exists());
}
#[test]
fn the_tree_of_the_folder_shows_folders_first_and_only_gpx_files() {
let mut fx = Fixture::new();
fx.write("b.gpx", "b");
fx.write("A.GPX", "a");
fx.write("b 10.gpx", "b 10");
fx.write("b 2.gpx", "b 2");
fx.write("notes.txt", "not a track");
fx.write(".hidden.gpx", "hidden");
fs::create_dir(fx.folder.join("zoo")).unwrap();
fs::create_dir(fx.folder.join("Alps")).unwrap();
fs::create_dir(fx.folder.join(".git")).unwrap();
fx.write("zoo/c.gpx", "c");
let folder = fx.folder.clone();
fx.workspace.open_folder(&mut fx.engine, &folder, false);
let names = |entries: Vec<Entry>| -> Vec<(String, bool)> {
entries.into_iter().map(|e| (e.name, e.is_dir)).collect()
};
assert_eq!(
names(fx.workspace.list_folder(&folder).unwrap()),
[
("Alps".to_string(), true),
("zoo".to_string(), true),
("A.GPX".to_string(), false),
("b.gpx".to_string(), false),
("b 2.gpx".to_string(), false),
("b 10.gpx".to_string(), false),
]
);
assert_eq!(
names(fx.workspace.list_folder(&folder.join("zoo")).unwrap()),
[("c.gpx".to_string(), false)]
);
// only what is in the open folder can be listed
let elsewhere = canonical(&std::env::temp_dir());
assert!(fx.workspace.list_folder(&elsewhere).is_err());
}
#[test]
fn names_are_sorted_by_the_numbers_they_hold() {
let mut names = vec![
"track 10",
"track 2",
"Track 1",
"track",
"track 02b",
"track 2a",
"b",
"A",
];
names.sort_by(|a, b| natural_cmp(a, b).then_with(|| a.cmp(b)));
assert_eq!(
names,
[
"A",
"b",
"track",
"Track 1",
"track 2",
"track 2a",
"track 02b",
"track 10",
]
);
}
#[test]
fn a_file_without_edits_is_read_again_when_the_disk_changes() {
let mut fx = Fixture::new();
let a = fx.write("a.gpx", "a");
fx.open_folder();
let id = fx.ids()[0];
fx.write("a.gpx", "theirs");
let outcome = fx.reconcile(Some(std::slice::from_ref(&a))).unwrap();
assert!(outcome.changed);
// the same file, now the one on the disk
assert_eq!(fx.ids(), [id]);
assert_eq!(fx.content(id), "theirs");
let status = fx.status(id);
assert!(!status.dirty && !status.replaced);
// looking again changes nothing
assert!(fx.reconcile(Some(std::slice::from_ref(&a))).is_none());
}
#[test]
fn a_file_with_edits_is_flagged_when_the_disk_changes_and_not_read_again() {
let mut fx = Fixture::new();
let a = fx.write("a.gpx", "a");
fx.open_folder();
let id = fx.ids()[0];
fx.rename(id, "mine");
fx.write("a.gpx", "theirs");
let outcome = fx.reconcile(Some(std::slice::from_ref(&a))).unwrap();
assert!(!outcome.changed);
assert_eq!(fx.name(id), "mine");
let status = fx.status(id);
assert!(status.dirty && status.replaced && !status.missing);
// looking again changes nothing
assert!(fx.reconcile(Some(std::slice::from_ref(&a))).is_none());
// the disk goes back to what was read: only the edits are left
fx.write("a.gpx", "a");
assert!(fx.reconcile(Some(std::slice::from_ref(&a))).is_some());
let status = fx.status(id);
assert!(status.dirty && !status.replaced);
}
#[test]
fn the_user_can_take_the_disk_instead_of_the_edits() {
let mut fx = Fixture::new();
fx.write("a.gpx", "a");
fx.open_folder();
let id = fx.ids()[0];
fx.rename(id, "mine");
fx.write("a.gpx", "theirs");
fx.reconcile(None);
assert!(fx.status(id).replaced);
fx.workspace.reload(&mut fx.engine, &[id]).unwrap();
assert_eq!(fx.content(id), "theirs");
let status = fx.status(id);
assert!(!status.dirty && !status.replaced);
// and it is one undo step
api::select(&mut fx.engine, &[id], SelectMode::Replace);
api::undo(&mut fx.engine);
assert_eq!(fx.content(id), "mine");
}
#[test]
fn saving_overwrites_what_replaced_the_file() {
let mut fx = Fixture::new();
let a = fx.write("a.gpx", "a");
fx.open_folder();
let id = fx.ids()[0];
fx.rename(id, "mine");
fx.write("a.gpx", "theirs");
fx.reconcile(None);
assert!(fx.status(id).replaced);
fx.save(&[id]);
assert!(fs::read_to_string(a).unwrap().contains(">mine<"));
assert!(!fx.status(id).replaced && !fx.status(id).dirty);
}
#[test]
fn a_file_that_is_gone_from_the_disk_stays_open_and_dirty_until_it_is_saved_again() {
let mut fx = Fixture::new();
let a = fx.write("a.gpx", "a");
fx.open_folder();
let id = fx.ids()[0];
fs::remove_file(&a).unwrap();
assert!(fx.reconcile(Some(std::slice::from_ref(&a))).is_some());
assert_eq!(fx.ids(), [id]);
let status = fx.status(id);
assert!(status.dirty && status.missing && status.path.is_some());
// saving puts it back
fx.save(&[id]);
assert!(a.exists());
let status = fx.status(id);
assert!(!status.dirty && !status.missing);
}
#[test]
fn a_file_that_comes_back_different_is_read_again() {
let mut fx = Fixture::new();
let a = fx.write("a.gpx", "a");
fx.open_folder();
let id = fx.ids()[0];
fs::remove_file(&a).unwrap();
fx.reconcile(None);
assert!(fx.status(id).missing);
fx.write("a.gpx", "back");
fx.reconcile(None);
assert_eq!(fx.content(id), "back");
let status = fx.status(id);
assert!(!status.dirty && !status.missing);
}
#[test]
fn what_changed_while_the_app_was_closed_is_found_when_it_starts() {
let mut fx = Fixture::new();
let a = fx.write("a.gpx", "a");
fx.open_folder();
let id = fx.ids()[0];
fx.write("a.gpx", "changed");
assert!(fx.reconcile(None).is_some());
assert_eq!(fx.content(id), "changed");
fs::remove_file(a).unwrap();
assert!(fx.reconcile(None).is_some());
assert!(fx.status(id).missing);
}
#[test]
fn new_files_made_from_a_file_of_the_folder_are_saved_next_to_it() {
let mut fx = Fixture::new();
fs::create_dir(fx.folder.join("sub")).unwrap();
fx.write("sub/b.gpx", "b");
fx.write("a.gpx", "a");
fx.open_folder();
let b = fx.id_named("b");
let sub = fx.folder.join("sub");
assert_eq!(fx.workspace.source_dir(&[b]), Some(canonical(&sub)));
let before = fx.ids();
api::select(&mut fx.engine, &[b], SelectMode::Replace);
let outcome = api::duplicate(&mut fx.engine);
let copy = fx
.ids()
.into_iter()
.find(|id| !before.contains(id))
.unwrap();
fx.workspace.inherit(outcome.added(), &canonical(&sub));
assert_eq!(fx.status(copy).folder.as_deref(), canonical(&sub).to_str());
assert!(fx.status(copy).path.is_none() && fx.status(copy).dirty);
// nobody needs to be asked where it goes
assert!(fx.workspace.unlinked(&fx.engine, &[copy]).is_empty());
fx.save(&[copy]);
assert!(sub.join("b (2).gpx").exists());
let status = fx.status(copy);
assert!(!status.dirty && status.folder.is_none());
assert_eq!(
status.path.as_deref(),
canonical(&sub.join("b (2).gpx")).to_str()
);
// the copy of the copy is next to it as well
assert_eq!(fx.workspace.source_dir(&[copy]), Some(canonical(&sub)));
}
#[test]
fn a_file_outside_the_open_folder_hands_nothing_down() {
let mut fx = Fixture::new();
let elsewhere = canonical(&std::env::temp_dir()).join(format!("gpx-ws-{}", Uuid::new_v4()));
fs::create_dir_all(&elsewhere).unwrap();
let outside = elsewhere.join("o.gpx");
fs::write(&outside, gpx("o")).unwrap();
let folder = fx.folder.clone();
fx.workspace.open_folder(&mut fx.engine, &folder, false);
fx.workspace
.open_files(&mut fx.engine, std::slice::from_ref(&outside), true);
let id = fx.ids()[0];
assert_eq!(fx.workspace.source_dir(&[id]), None);
// a new file with no source is not linked to a folder by `source_dir`: the caller falls back
fs::remove_dir_all(elsewhere).unwrap();
}
#[test]
fn closing_files_asks_what_to_do_with_the_unsaved_ones() {
let mut fx = Fixture::new();
fx.write("a.gpx", "a");
fx.write("b.gpx", "b");
fx.open_folder();
let (a, b) = (fx.id_named("a"), fx.id_named("b"));
fx.rename(a, "edited");
assert_eq!(fx.workspace.unsaved(&fx.engine, &[a, b]), [a]);
// without a choice what has edits stays
let outcome = fx.workspace.close_files(&mut fx.engine, &[a, b], false);
assert!(outcome.changed);
assert_eq!(fx.ids(), [a]);
// saved first
fx.save(&[a]);
fx.workspace.close_files(&mut fx.engine, &[a], false);
assert!(fx.ids().is_empty());
assert!(
fs::read_to_string(fx.folder.join("a.gpx"))
.unwrap()
.contains("edited")
);
assert!(fx.workspace.links.is_empty());
}
#[test]
fn closing_files_can_discard_their_edits() {
let mut fx = Fixture::new();
fx.write("a.gpx", "a");
fx.open_folder();
let a = fx.ids()[0];
fx.rename(a, "edited");
api::new_file(&mut fx.engine, "fresh", None, None, None, false);
let fresh = fx.id_named("fresh");
fx.workspace.close_files(&mut fx.engine, &[a, fresh], true);
assert!(fx.ids().is_empty());
assert!(
fs::read_to_string(fx.folder.join("a.gpx"))
.unwrap()
.contains(">a<")
);
}
#[test]
fn closing_the_folder_closes_every_file() {
let mut fx = Fixture::new();
fx.write("a.gpx", "a");
fx.open_folder();
api::new_file(&mut fx.engine, "fresh", None, None, None, false);
assert_eq!(fx.ids().len(), 2);
// the new file has nowhere to go: it has to be discarded
let outcome = fx.workspace.close_folder(&mut fx.engine, false);
assert!(outcome.changed);
assert_eq!(fx.ids().len(), 1);
assert!(fx.workspace.folder.is_none());
let outcome = fx.workspace.close_folder(&mut fx.engine, true);
assert!(outcome.changed);
assert!(fx.ids().is_empty());
}
#[test]
fn opening_another_folder_closes_the_first_with_all_its_files() {
let mut fx = Fixture::new();
fx.write("a.gpx", "a");
fx.open_folder();
let a = fx.ids()[0];
fx.rename(a, "edited");
let other = canonical(&std::env::temp_dir()).join(format!("gpx-ws-{}", Uuid::new_v4()));
fs::create_dir_all(&other).unwrap();
// the edits stay, nothing is lost without a choice
fx.workspace.open_folder(&mut fx.engine, &other, false);
assert_eq!(fx.ids(), [a]);
assert_eq!(fx.workspace.folder.as_deref(), Some(other.as_path()));
let first = fx.folder.clone();
fx.workspace.open_folder(&mut fx.engine, &first, true);
assert!(fx.ids().is_empty());
assert!(
fs::read_to_string(fx.folder.join("a.gpx"))
.unwrap()
.contains(">a<")
);
fs::remove_dir_all(other).unwrap();
}
#[test]
fn opening_the_same_folder_again_changes_nothing() {
let mut fx = Fixture::new();
fx.write("a.gpx", "a");
fx.open_folder();
let folder = fx.folder.clone();
let outcome = fx.workspace.open_folder(&mut fx.engine, &folder, true);
assert!(!outcome.changed);
assert_eq!(fx.ids().len(), 1);
}
#[test]
fn a_failed_save_stops_at_the_file_and_leaves_it_dirty() {
let mut fx = Fixture::new();
fx.write("a.gpx", "a");
fx.open_folder();
let id = fx.ids()[0];
fx.rename(id, "edited");
// the place of the file is taken by a folder
fs::remove_file(fx.folder.join("a.gpx")).unwrap();
fs::create_dir(fx.folder.join("a.gpx")).unwrap();
let result = fx
.workspace
.save(&mut fx.engine, &[id], &BTreeMap::new(), None);
assert!(result.is_err());
assert!(fx.workspace.is_dirty(&fx.engine, id));
}
#[test]
fn saving_as_gives_the_file_another_place() {
let mut fx = Fixture::new();
let a = fx.write("a.gpx", "a");
fx.write("b.gpx", "b");
fx.open_folder();
let id = fx.id_named("a");
let other = fx.id_named("b");
fx.rename(id, "edited");
let target = fx.folder.join("copy.gpx");
fx.workspace.save_as(&mut fx.engine, id, &target).unwrap();
assert!(fs::read_to_string(&target).unwrap().contains("edited"));
assert!(fs::read_to_string(a).unwrap().contains(">a<"));
let status = fx.status(id);
assert!(!status.dirty);
assert_eq!(status.path.as_deref(), canonical(&target).to_str());
// the place of another open file is not available
let taken = fx.folder.join("b.gpx");
assert!(fx.workspace.save_as(&mut fx.engine, id, &taken).is_err());
assert!(!fx.workspace.is_dirty(&fx.engine, other));
}
mod restart {
use gpx_engine::Persister;
use super::*;
use gpx_studio_desktop_lib::store::FileStorage;
/// What the app does when it starts again: the engine and the links are read from what was
/// kept.
fn restart(engine: &Engine, workspace: &Workspace, root: &Path) -> (Engine, Workspace) {
let storage = FileStorage::open(root).unwrap();
pollster::block_on(Persister::default().save(&storage, engine.snapshot())).unwrap();
let mut restarted = Engine::default();
let restored = pollster::block_on(Persister::default().restore(&storage)).unwrap();
restarted.restore(restored);
let links = serde_json::to_string(workspace).unwrap();
(restarted, serde_json::from_str(&links).unwrap())
}
fn setup() -> (PathBuf, Engine, Workspace, Vec<Uuid>) {
let root = std::env::temp_dir().join(format!("gpx-restart-{}", Uuid::new_v4()));
let folder = root.join("tracks");
fs::create_dir_all(&folder).unwrap();
for name in ["with_tracks", "with_time", "with_waypoint"] {
let _ = fs::copy(
format!("../gpx-rs/data/{name}.gpx"),
folder.join(format!("{name}.gpx")),
);
}
let mut engine = Engine::default();
let mut workspace = Workspace::default();
workspace.open_folder(&mut engine, &folder, false);
workspace.open_files(&mut engine, &gpx_files(&folder), false);
let ids = engine.order().iter().map(|id| id.0).collect();
(root, engine, workspace, ids)
}
#[test]
fn files_that_were_not_edited_are_not_unsaved_after_a_restart() {
let (root, engine, workspace, ids) = setup();
assert!(ids.len() >= 2);
let (restarted, mut workspace) = restart(&engine, &workspace, &root.join("store"));
// the files are the same, but the engine knows their contents by new identities
assert!(ids.iter().any(|id| workspace.is_dirty(&restarted, *id)));
workspace.verify(&restarted);
assert!(ids.iter().all(|id| !workspace.is_dirty(&restarted, *id)));
fs::remove_dir_all(root).unwrap();
}
#[test]
fn files_that_were_edited_are_still_unsaved_after_a_restart() {
let (root, mut engine, workspace, ids) = setup();
api::select(&mut engine, &[ids[0]], gpx_engine::SelectMode::Replace);
api::metadata(&mut engine, "edited", "");
assert!(workspace.is_dirty(&engine, ids[0]));
let (restarted, mut workspace) = restart(&engine, &workspace, &root.join("store"));
workspace.verify(&restarted);
assert!(workspace.is_dirty(&restarted, ids[0]));
assert!(
ids[1..]
.iter()
.all(|id| !workspace.is_dirty(&restarted, *id))
);
fs::remove_dir_all(root).unwrap();
}
#[test]
fn links_made_before_the_content_was_kept_are_checked_against_the_disk() {
let (root, mut engine, mut workspace, ids) = setup();
api::select(&mut engine, &[ids[0]], gpx_engine::SelectMode::Replace);
api::metadata(&mut engine, "edited", "");
for link in workspace.links.values_mut() {
link.export_hash = 0;
}
let (restarted, mut workspace) = restart(&engine, &workspace, &root.join("store"));
workspace.verify(&restarted);
assert!(workspace.is_dirty(&restarted, ids[0]));
assert!(
ids[1..]
.iter()
.all(|id| !workspace.is_dirty(&restarted, *id))
);
fs::remove_dir_all(root).unwrap();
}
#[test]
fn the_folders_of_new_files_and_the_old_flag_name_survive_a_restart() {
let (root, engine, mut workspace, ids) = setup();
let folder = workspace.folder.clone().unwrap();
let fresh = Uuid::new_v4();
workspace.inherit(&[fresh.to_string()], &folder);
workspace.links.get_mut(&ids[0]).unwrap().replaced = true;
let json = serde_json::to_string(&workspace).unwrap();
// links written by the previous version had `conflict`, which is the same thing
let json = json.replace("\"replaced\"", "\"conflict\"");
assert!(json.contains("\"conflict\":true"));
let read: Workspace = serde_json::from_str(&json).unwrap();
assert!(read.links[&ids[0]].replaced);
assert_eq!(read.dirs.get(&fresh), Some(&folder));
let _ = engine;
fs::remove_dir_all(root).unwrap();
}
}
-14
View File
@@ -1,14 +0,0 @@
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
<dependency>
<dependentAssembly>
<assemblyIdentity
type="win32"
name="Microsoft.Windows.Common-Controls"
version="6.0.0.0"
processorArchitecture="*"
publicKeyToken="6595b64144ccf1df"
language="*"
/>
</dependentAssembly>
</dependency>
</assembly>
-26
View File
@@ -44,7 +44,6 @@
"@typescript-eslint/parser": "^8.33.1",
"bits-ui": "^2.17.2",
"clsx": "^2.1.1",
"cross-env": "^10.1.0",
"eslint": "^9.28.0",
"eslint-config-prettier": "^10.1.5",
"eslint-plugin-svelte": "^3.9.1",
@@ -355,13 +354,6 @@
"tslib": "^2.4.0"
}
},
"node_modules/@epic-web/invariant": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/@epic-web/invariant/-/invariant-1.0.0.tgz",
"integrity": "sha512-lrTPqgvfFQtR/eY/qkIzp98OGdNJu0m5ji3q/nJI8v3SXkRKEnWiOxMmbvcSoAIzv/cGiuvRy57k4suKQSAdwA==",
"dev": true,
"license": "MIT"
},
"node_modules/@esbuild/aix-ppc64": {
"version": "0.27.7",
"resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.7.tgz",
@@ -3283,24 +3275,6 @@
"version": "1.0.3",
"license": "MIT"
},
"node_modules/cross-env": {
"version": "10.1.0",
"resolved": "https://registry.npmjs.org/cross-env/-/cross-env-10.1.0.tgz",
"integrity": "sha512-GsYosgnACZTADcmEyJctkJIoqAhHjttw7RsFrVoJNXbsWWqaq6Ym+7kZjq6mS45O0jij6vtiReppKQEtqWy6Dw==",
"dev": true,
"license": "MIT",
"dependencies": {
"@epic-web/invariant": "^1.0.0",
"cross-spawn": "^7.0.6"
},
"bin": {
"cross-env": "dist/bin/cross-env.js",
"cross-env-shell": "dist/bin/cross-env-shell.js"
},
"engines": {
"node": ">=20"
}
},
"node_modules/cross-spawn": {
"version": "7.0.6",
"dev": true,
+1 -3
View File
@@ -5,12 +5,11 @@
"scripts": {
"dev": "vite dev",
"build": "vite build",
"build:desktop": "cross-env BUILD_TARGET=desktop BASE_PATH= vite build",
"build:desktop": "BUILD_TARGET=desktop BASE_PATH='' vite build",
"prebuild": "npx tsx src/lib/scripts/pwa-manifest.ts",
"postbuild": "npx tsx src/lib/scripts/sitemap.ts",
"preview": "vite preview",
"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
"pretest": "svelte-kit sync",
"test": "node --import tsx --import ./src/test/register.mjs --experimental-wasm-modules --disable-warning=ExperimentalWarning --test \"src/**/*.test.ts\"",
"generate:example": "node --import tsx src/lib/scripts/example-statistics.ts",
"check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
@@ -39,7 +38,6 @@
"@typescript-eslint/parser": "^8.33.1",
"bits-ui": "^2.17.2",
"clsx": "^2.1.1",
"cross-env": "^10.1.0",
"eslint": "^9.28.0",
"eslint-config-prettier": "^10.1.5",
"eslint-plugin-svelte": "^3.9.1",
+1 -1
View File
@@ -51,7 +51,7 @@ By right-clicking on a file tab, you can access the same actions as in the [edit
As mentioned in the [view options section](./menu/view), you can switch to a tree layout for the files list.
This layout is ideal for managing a large number of open files, as it organizes them into a vertical list on the right side of the map.
In addition, the file tree view enables you to inspect the [tracks, segments, routes, and points of interest](./gpx) contained inside the files through collapsible sections.
In addition, the file tree view enables you to inspect the [tracks, segments, and points of interest](./gpx) contained inside the files through collapsible sections.
You can also apply [edit actions](./menu/edit) and [tools](./toolbar) to internal file items.
Furthermore, you can drag and drop the inner items to reorder them, or move them in the hierarchy or even to another file.
+1 -17
View File
@@ -3,15 +3,13 @@ title: Фармат файла GPX
---
<script>
import DocsNote from '$lib/components/docs/DocsNote.svelte';
import { Waypoints, Route, MapPin } from '@lucide/svelte';
import { Waypoints, MapPin } from '@lucide/svelte';
</script>
# { title }
<a href="https://www.topografix.com/gpx.asp" target="_blank">Фармат файла GPX</a> - гэта адкрыты стандарт для абмену дадзенымі GPS паміж праграмамі і прыладамі GPS.
Па сутнасці, ён складаецца з серыі кропак GPS, якія кадзіруюць адну або некалькі слядоў GPS, і, па жаданні, некаторыя кропкі цікавасці.
These traces can be stored either as tracks or as routes.
Файлы GPX могуць таксама ўтрымліваць метададзеныя, з якіх палі **імя** і **апісанне** найбольш карысныя для карыстальнікаў.
@@ -29,20 +27,6 @@ These traces can be stored either as tracks or as routes.
У большасці выпадкаў файлы GPX утрымліваюць адзін трэк з адным сегментам.
Аднак іерархія, апісаная вышэй, дазваляе выкарыстоўваць больш складаныя выпадкі, напрыклад, планаваць шматдзённыя паездкі з некалькімі варыянтамі на кожны дзень.
### <Route size="16" class="inline-block" style="margin-bottom: 2px" /> Routes
A **route** is a single continuous path made of a sequence of **route points**.
These are mandatory checkpoints, which correspond to the anchor points of the [routing tool](./toolbar/routing).
The points in between are only a suggested path connecting the route points.
Like a track, a route can have a **name**, a **description**, and **appearance properties**.
<DocsNote>
Nowadays, most apps and GPS devices work perfectly well with tracks, even for navigation.
However, some of them only work with routes: in that case, you can [convert](./menu/edit) your tracks to routes, or [create](./menu/file) a route file from the start.
</DocsNote>
### <MapPin size="16" class="inline-block" style="margin-bottom: 2px" /> Кропкі цікавасці
**Кропкі цікавасці** (тэхнічна званыя _маршрутнымі кропкамі_) уяўляюць цікавыя месцы, якія можна паказаць альбо на прыладзе GPS, альбо на лічбавай карце.
+10 -20
View File
@@ -3,14 +3,14 @@ title: Edit actions
---
<script lang="ts">
import { Undo2, Redo2, Info, PaintBucket, ArrowRightLeft, FileStack, ClipboardCopy, Scissors, ClipboardPaste, Trash2, Plus } from '@lucide/svelte';
import { Undo2, Redo2, Info, PaintBucket, EyeOff, FileStack, ClipboardCopy, Scissors, ClipboardPaste, Trash2, Maximize, Plus } from '@lucide/svelte';
import DocsNote from '$lib/components/docs/DocsNote.svelte';
</script>
# { title }
Unlike the file actions, the edit actions can potentially modify the content of the currently selected files.
Moreover, when the tree layout of the files list is enabled (see [Files and statistics](../files-and-stats)), they can also be applied to [tracks, segments, routes, and points of interest](../gpx).
Moreover, when the tree layout of the files list is enabled (see [Files and statistics](../files-and-stats)), they can also be applied to [tracks, segments, and points of interest](../gpx).
Therefore, we will refer to the elements that can be modified by these actions as _file items_.
Note that except for the undo and redo actions, the edit actions are also accessible through the context menu (right-click) of the file items.
@@ -27,6 +27,10 @@ Open the information dialog of the currently selected file item, where you can s
Open the appearance dialog, where you can change the color, opacity, and width of the selected file items on the map.
### <EyeOff size="16" class="inline-block" style="margin-bottom: 2px" /> Hide/unhide
Toggle the visibility of the selected file items on the map.
### <Plus size="16" class="inline-block" style="margin-bottom: 2px" /> New track
Create a new track in the selected file.
@@ -38,17 +42,6 @@ Additionally, the selection must be a single file.
</DocsNote>
### <Plus size="16" class="inline-block" style="margin-bottom: 2px" /> New route
Create a new route in the selected file.
<DocsNote>
This action is only available when the tree layout of the files list is enabled.
Additionally, the selection must be a single file.
</DocsNote>
### <Plus size="16" class="inline-block" style="margin-bottom: 2px" /> New segment
Create a new segment in the selected track.
@@ -60,17 +53,14 @@ Additionally, the selection must be a single track.
</DocsNote>
### <ArrowRightLeft size="16" class="inline-block" style="margin-bottom: 2px" /> Convert to route/track
Convert the selected routes to tracks, or the selected tracks or segments to routes.
Since a route has a single segment, a track with multiple segments is converted into one route per segment.
When the selected files contain only routes, or only tracks, you can also convert all of them at once.
### <FileStack size="16" class="inline-block" style="margin-bottom: 2px" /> Select all
Add all file items in the current hierarchy level to the selection.
### <Maximize size="16" class="inline-block" style="margin-bottom: 2px" /> Center
Center the map on the selected file items.
### <ClipboardCopy size="16" class="inline-block" style="margin-bottom: 2px" /> Copy
Copy the selected file items to the clipboard.
-4
View File
@@ -15,10 +15,6 @@ The file actions menu contains a set of pretty self-explanatory file operations.
Create a new empty file.
### <Plus size="16" class="inline-block" style="margin-bottom: 2px" /> New route file
Create a new file containing an empty [route](../gpx).
### <FolderOpen size="16" class="inline-block" style="margin-bottom: 2px" /> Open...
Open files from your computer.
+2 -10
View File
@@ -3,7 +3,7 @@ title: View options
---
<script lang="ts">
import { ChartArea, ListTree, Map, Layers2, Coins, Milestone, EyeOff, Maximize, Box, Maximize2 } from '@lucide/svelte';
import { ChartArea, ListTree, Map, Layers2, Coins, Milestone, Box, Maximize2 } from '@lucide/svelte';
import DocsNote from '$lib/components/docs/DocsNote.svelte';
</script>
@@ -19,7 +19,7 @@ Hide the elevation profile to make room for the map, or show it to inspect the c
Toggle the tree layout for the [file list](../files-and-stats).
This layout is ideal for managing a large number of open files, as it organizes them into a vertical list on the right side of the map.
In addition, the file tree view enables you to inspect the [tracks, segments, routes, and points of interest](../gpx) contained inside the files through collapsible sections.
In addition, the file tree view enables you to inspect the [tracks, segments, and points of interest](../gpx) contained inside the files through collapsible sections.
### <Map size="16" class="inline-block" style="margin-bottom: 2px" /> Switch to previous basemap
@@ -38,14 +38,6 @@ They are displayed for the current selection, like the [elevation profile](../fi
Toggle the visibility of direction arrows on the map.
### <EyeOff size="16" class="inline-block" style="margin-bottom: 2px" /> Hide/unhide
Toggle the visibility of the selected file items on the map.
### <Maximize size="16" class="inline-block" style="margin-bottom: 2px" /> Center
Center the map on the selected file items.
### <Box size="16" class="inline-block" style="margin-bottom: 2px" /> Toggle 3D
Enter or exit the 3D map view.
+1 -1
View File
@@ -16,7 +16,7 @@ This tool allows you to extract [tracks (or segments)](../gpx) from files (or tr
<Extract class="text-foreground p-3 border rounded-md shadow-lg" />
</div>
Applying the tool to a file containing multiple [tracks or routes](../gpx) will create a new file for each of the tracks and routes it contains.
Applying the tool to a file containing multiple tracks will create a new file for each of the tracks it contains.
Similarly, applying the tool to a track containing multiple segments will create (in the same file) a new track for each of the segments it contains.
<DocsNote>
+3 -3
View File
@@ -10,11 +10,11 @@ title: Merge
# <Group size="24" class="inline-block" style="margin-bottom: 5px" /> { title }
To use this tool, you need to [select](../files-and-stats) multiple files, [tracks, segments or routes](../gpx).
To use this tool, you need to [select](../files-and-stats) multiple files, [tracks, or segments](../gpx).
- If your goal is to create a single continuous trace from your selection, use the **Connect the traces** option and validate.
- The second option can be used to create or manage files with multiple [tracks, segments or routes](../gpx).
Merging files (or tracks) will result in a single file (or track) containing all tracks and routes (or segments) from the selection.
- The second option can be used to create or manage files with multiple [tracks or segments](../gpx).
Merging files (or tracks) will result in a single file (or track) containing all tracks (or segments) from the selection.
<DocsNote>
+1 -1
View File
@@ -27,6 +27,6 @@ To split the selected trace into two parts, click on one of the split markers di
To split at a specific point of your choice, hover over the trace on the map.
Scissors will appear at the cursor position, showing that you can split the trace at that point.
You can choose to split the trace into two GPX files, or to keep the split parts in the same file as [tracks, segments or routes](../gpx).
You can choose to split the trace into two GPX files, or to keep the split parts in the same file as [tracks or segments](../gpx).
<DocsImage src="tools/split" alt="Hovering over the selected trace turns your cursor into scissors." />
+1 -1
View File
@@ -51,7 +51,7 @@ Clicant el botó dret en la pestanya d'un arxiu, pots accedir a les mateixes acc
Tal i com es menciona en la [secció d'opcions de vista](./menu/view), pots canviar a un esquema d'arbre per a la llista d'arxius.
Aquesta disposició és ideal per tal d'administrar un gran nombre d'arxius oberts, ja que els organitza en una llista vertical en la part dreta del mapa.
In addition, the file tree view enables you to inspect the [tracks, segments, routes, and points of interest](./gpx) contained inside the files through collapsible sections.
A més, la vista d'arbre d'arxius permet inspeccionar [tracs, segments i punts d'interès](./gpx) continguts dins dels arxius a través de seccions plegables.
També pots aplicar [accions d'edició](./menu/edit) i [eines](./toolbar) a elements interns de l'arxiu.
A més, pots arrossegar i deixar anar els elements interns per reordenar-los, o moure'ls en la jerarquia o fins i tot a un altre arxiu.
+1 -17
View File
@@ -3,15 +3,13 @@ title: Format d'arxiu GPX
---
<script>
import DocsNote from '$lib/components/docs/DocsNote.svelte';
import { Waypoints, Route, MapPin } from '@lucide/svelte';
import { Waypoints, MapPin } from '@lucide/svelte';
</script>
# { title }
El <a href="https://www.topografix.com/gpx.asp" target="_blank">format d'arxiu GPX</a> és un estàndard obert per intercanviar dades GPS entre aplicacions i aparells GPS.
Bàsicament consisteix en una sèrie de punts GPS que codifiquen una o diverses traces GPS i, opcionalment, alguns punts d'interès.
These traces can be stored either as tracks or as routes.
Els arxius GPX poden contenir també metadades, de les quals els camps **nom** i **descripció** són els més útils pels usuaris.
@@ -29,20 +27,6 @@ Aquests estan organitzats en una estructura jeràrquica, amb els tracs en el niv
En la majoria dels casos, els arxius GPX contenen un sol trac amb un sol segment.
Tanmateix, la jerarquia descrita a dalt permet casos d'ús més avançat, com la planificació de viatges de diversos dies amb variants per cada dia.
### <Route size="16" class="inline-block" style="margin-bottom: 2px" /> Routes
A **route** is a single continuous path made of a sequence of **route points**.
These are mandatory checkpoints, which correspond to the anchor points of the [routing tool](./toolbar/routing).
The points in between are only a suggested path connecting the route points.
Like a track, a route can have a **name**, a **description**, and **appearance properties**.
<DocsNote>
Nowadays, most apps and GPS devices work perfectly well with tracks, even for navigation.
However, some of them only work with routes: in that case, you can [convert](./menu/edit) your tracks to routes, or [create](./menu/file) a route file from the start.
</DocsNote>
### <MapPin size="16" class="inline-block" style="margin-bottom: 2px" /> Punts d'interès
**Punts d'interès** (tècnicament anomenats _waypoints_) representen ubicacions d'interès per mostrar en un dispositiu GPS o en mapa digital.
+10 -20
View File
@@ -3,14 +3,14 @@ title: Accions d'edició
---
<script lang="ts">
import { Undo2, Redo2, Info, PaintBucket, ArrowRightLeft, FileStack, ClipboardCopy, Scissors, ClipboardPaste, Trash2, Plus } from '@lucide/svelte';
import { Undo2, Redo2, Info, PaintBucket, EyeOff, FileStack, ClipboardCopy, Scissors, ClipboardPaste, Trash2, Maximize, Plus } from '@lucide/svelte';
import DocsNote from '$lib/components/docs/DocsNote.svelte';
</script>
# { title }
A diferència de les accions de l'arxiu, les accions d'edició poden potencialment modificar el contingut dels arxius seleccionats actualment.
Moreover, when the tree layout of the files list is enabled (see [Files and statistics](../files-and-stats)), they can also be applied to [tracks, segments, routes, and points of interest](../gpx).
A més a més, quan està habilitada la disposició de l'arbre de la llista d'arxius (veure [Arxius i estadístiques](../files-and-stats)), també es poden aplicar a [tracs, segments i punts d'interès](../gpx).
Per tant, ens referirem als elements que poden ser modificats per aquestes accions com a _elements d'arxiu_.
Tingui en compte que excepte per a les accions de desfer i refer, les accions d'edició també són accessibles a través del menú contextual (clic dret) dels elements de l'arxiu.
@@ -27,6 +27,10 @@ Obre el quadre de diàleg d'informació de l'element de l'arxiu seleccionat, on
Obre el diàleg d'aparença, on pots canviar el color, l'opacitat i l'amplada del mapa dels elements de l'arxiu seleccionat.
### <EyeOff size="16" class="inline-block" style="margin-bottom: 2px" /> Amaga/mostra
Alterna la visibilitat dels elements de l'arxiu seleccionat en el mapa.
### <Plus size="16" class="inline-block" style="margin-bottom: 2px" /> Nou trac
Crear nova ruta en l'arxiu seleccionat.
@@ -38,17 +42,6 @@ A més, la selecció ha de ser un únic arxiu.
</DocsNote>
### <Plus size="16" class="inline-block" style="margin-bottom: 2px" /> New route
Create a new route in the selected file.
<DocsNote>
Aquesta acció només està disponible quan està habilitada la disposició de l'arbre de la llista d'arxius.
A més, la selecció ha de ser un únic arxiu.
</DocsNote>
### <Plus size="16" class="inline-block" style="margin-bottom: 2px" /> Nou segment
Crear un nou segment en el trac seleccionat.
@@ -60,17 +53,14 @@ A més, la selecció ha de ser un trac únic.
</DocsNote>
### <ArrowRightLeft size="16" class="inline-block" style="margin-bottom: 2px" /> Convert to route/track
Convert the selected routes to tracks, or the selected tracks or segments to routes.
Since a route has a single segment, a track with multiple segments is converted into one route per segment.
When the selected files contain only routes, or only tracks, you can also convert all of them at once.
### <FileStack size="16" class="inline-block" style="margin-bottom: 2px" /> Selecciona tot
Afegeix a la selecció tots els elements de l'arxiu en el nivell de jerarquia actual.
### <Maximize size="16" class="inline-block" style="margin-bottom: 2px" /> Centrar
Centrar el mapa en els elements de l'arxiu seleccionat.
### <ClipboardCopy size="16" class="inline-block" style="margin-bottom: 2px" /> Copia
Copia els elements de l'arxiu seleccionat en el porta-retalls.
-4
View File
@@ -15,10 +15,6 @@ El menú d'accions d'arxiu conté un conjunt d'operacions força autoexplicative
Crea un arxiu nou buit.
### <Plus size="16" class="inline-block" style="margin-bottom: 2px" /> New route file
Create a new file containing an empty [route](../gpx).
### <FolderOpen size="16" class="inline-block" style="margin-bottom: 2px" /> Obrir...
Obrir arxius del teu equip.
+2 -10
View File
@@ -3,7 +3,7 @@ title: Opcions de vista
---
<script lang="ts">
import { ChartArea, ListTree, Map, Layers2, Coins, Milestone, EyeOff, Maximize, Box, Maximize2 } from '@lucide/svelte';
import { ChartArea, ListTree, Map, Layers2, Coins, Milestone, Box, Maximize2 } from '@lucide/svelte';
import DocsNote from '$lib/components/docs/DocsNote.svelte';
</script>
@@ -19,7 +19,7 @@ Amagar el perfil d'elevació per fer espai al mapa o mostra-lo per inspeccionar
Alterna el disseny de l'arbre per la [llista d'arxius](../files-and-stats).
Aquesta disposició és ideal per tal d'administrar un gran nombre d'arxius oberts, ja que els organitza en una llista vertical en la part dreta del mapa.
In addition, the file tree view enables you to inspect the [tracks, segments, routes, and points of interest](../gpx) contained inside the files through collapsible sections.
A més, la vista de l'arbre d'arxius permet inspeccionar [tracs, segments i punts d'interès](../gpx) continguts en els arxius a través de les seccions plegables.
### <Map size="16" class="inline-block" style="margin-bottom: 2px" /> Canvia la mapa base anterior
@@ -38,14 +38,6 @@ Es mostren per a la selecció actual, com el [perfil d'elevació](../files-and-s
Alterna la visibilitat de les fletxes de direcció en el mapa.
### <EyeOff size="16" class="inline-block" style="margin-bottom: 2px" /> Hide/unhide
Toggle the visibility of the selected file items on the map.
### <Maximize size="16" class="inline-block" style="margin-bottom: 2px" /> Center
Center the map on the selected file items.
### <Box size="16" class="inline-block" style="margin-bottom: 2px" /> Alternar 3D
Entra o surt de la vista de mapa en 3D.
+1 -1
View File
@@ -16,7 +16,7 @@ Aquesta eina permet extreure [tracs (o segments)](../gpx) des d'arxius (o tracs)
<Extract class="text-foreground p-3 border rounded-md shadow-lg" />
</div>
Applying the tool to a file containing multiple [tracks or routes](../gpx) will create a new file for each of the tracks and routes it contains.
Aplicant l'eina a un arxiu que contingui múltiples tracs, crearà un arxiu nou per cada trac que contingui.
Igualment, aplicant l'eina a un trac que contingui múltiples segments, crearà (en el mateix arxiu) un nou trac per cada un dels segments que contingui.
<DocsNote>
+3 -3
View File
@@ -10,11 +10,11 @@ title: Fusionar
# <Group size="24" class="inline-block" style="margin-bottom: 5px" /> { title }
To use this tool, you need to [select](../files-and-stats) multiple files, [tracks, segments or routes](../gpx).
Per utilitzar aquesta eina, s'ha de [seleccionar](../files-and-stats) múltiples arxius, [tracs o segments](../gpx).
- Si el teu objectiu és crear una traça contínua a partir de la selecció, utilitza l'opció **Connectar les traces** i valida-ho.
- The second option can be used to create or manage files with multiple [tracks, segments or routes](../gpx).
Merging files (or tracks) will result in a single file (or track) containing all tracks and routes (or segments) from the selection.
- La segona opció es pot utilitzar per a crear o gestionar arxius amb múltiples [tracs o segments](../gpx).
Fusionar arxius (o tracs) crearà un sol arxiu (o trac) que contindrà tots els tracs (o segments) seleccionats.
<DocsNote>
+1 -1
View File
@@ -27,6 +27,6 @@ Per dividir la traça seleccionada en dues parts, clica sobre un dels marcadors
Per dividir en un punt triat específicament, situat sobre la traça del mapa.
Les tisores apareixeran en la posició del cursor, mostrant que pots dividir el traçat en aquest punt.
You can choose to split the trace into two GPX files, or to keep the split parts in the same file as [tracks, segments or routes](../gpx).
Pots triar dividir el traçat en dos arxius GPX o mantenir les parts dividides en el mateix arxiu com a [tracs o segments](../gpx).
<DocsImage src="tools/split" alt="Situar el cursor sobre el traçat seleccionat el converteix en tisores." />
+1 -1
View File
@@ -51,7 +51,7 @@ Kliknutím pravým tlačítkem myši na kartu souboru můžete provést stejné
Jak je uvedeno v části [možnosti zobrazení](./menu/view), můžete přepnout na stromové rozvržení seznamu souborů.
Toto zobrazení je ideální pro správu velkého počtu otevřených souborů, protože je uspořádá do svislého seznamu na pravé straně mapy.
In addition, the file tree view enables you to inspect the [tracks, segments, routes, and points of interest](./gpx) contained inside the files through collapsible sections.
Stromové rozvržení souborů navíc umožňuje zkontrolovat [trasy, úseky a body zájmu](./gpx) obsažené v souborech prostřednictvím sbalitelných sekcí.
Rovněž můžete využít [akce úprav](./menu/edit) a [nástroje](./toolbar) na prvky uvnitř souborů.
Navíc přetažením prvků uvnitř souborů můžete změnit jejich pořadí nebo je přesunout v hierarchii či dokonce do jiného souboru.
+1 -17
View File
@@ -3,15 +3,13 @@ title: Formát souboru GPX
---
<script>
import DocsNote from '$lib/components/docs/DocsNote.svelte';
import { Waypoints, Route, MapPin } from '@lucide/svelte';
import { Waypoints, MapPin } from '@lucide/svelte';
</script>
# { title }
<a href="https://www.topografix.com/gpx.asp" target="_blank">GPX formát</a> je otevřeným standardem pro výměnu GPS dat mezi aplikacemi a GPS zařízeními.
V podstatě se skládá z řady GPS bodů kódujících jednu nebo více GPS stop a volitelně i nějaké body zájmu.
These traces can be stored either as tracks or as routes.
GPX soubory mohou obsahovat také metadata, z nichž pole **název** a **popis** jsou pro uživatele nejužitečnější.
@@ -29,20 +27,6 @@ Ty jsou uspořádány v hierarchické struktuře, přičemž trasy jsou na nejvy
Ve většině případů obsahují GPX soubory pouze jednu trasu s jedním úsekem.
Nicméně výše popsaná hierarchie umožňuje pokročilejší případy použití, například plánování vícedenních výletů s několika variantami pro každý den.
### <Route size="16" class="inline-block" style="margin-bottom: 2px" /> Routes
A **route** is a single continuous path made of a sequence of **route points**.
These are mandatory checkpoints, which correspond to the anchor points of the [routing tool](./toolbar/routing).
The points in between are only a suggested path connecting the route points.
Like a track, a route can have a **name**, a **description**, and **appearance properties**.
<DocsNote>
Nowadays, most apps and GPS devices work perfectly well with tracks, even for navigation.
However, some of them only work with routes: in that case, you can [convert](./menu/edit) your tracks to routes, or [create](./menu/file) a route file from the start.
</DocsNote>
### <MapPin size="16" class="inline-block" style="margin-bottom: 2px" /> Body zájmu
**Body zájmu** (odborně nazývané _waypoints_) představují zajímavá místa, která se zobrazí buď na zařízení GPS, nebo na digitální mapě.
+10 -20
View File
@@ -3,14 +3,14 @@ title: Akce úprav
---
<script lang="ts">
import { Undo2, Redo2, Info, PaintBucket, ArrowRightLeft, FileStack, ClipboardCopy, Scissors, ClipboardPaste, Trash2, Plus } from '@lucide/svelte';
import { Undo2, Redo2, Info, PaintBucket, EyeOff, FileStack, ClipboardCopy, Scissors, ClipboardPaste, Trash2, Maximize, Plus } from '@lucide/svelte';
import DocsNote from '$lib/components/docs/DocsNote.svelte';
</script>
# { title }
Na rozdíl od akcí v menu Soubor, mohou akce úprav potenciálně změnit obsah aktuálně vybraných souborů.
Moreover, when the tree layout of the files list is enabled (see [Files and statistics](../files-and-stats)), they can also be applied to [tracks, segments, routes, and points of interest](../gpx).
Navíc pokud je povoleno rozvržení stromu seznamu souborů (viz [Soubory a statistiky](../files-and-stats)), mohou být také použity na [trasy, segmenty a body zájmu](../gpx).
Proto budeme všechny prvky, které mohou být těmito akcemi upraveny, označovat jako _prvky souborů_.
Všechny akce kromě zpět a opakovat jsou také dostupné z kontextového menu (klik pravým tlačítkem) prvků souborů.
@@ -27,6 +27,10 @@ Otevře informační dialog zvoleného prvku souborů, kde lze zobrazit a změni
Otevře dialog vzhledu, kde můžete změnit barvu, neprůhlednost a tloušťku zvolených prvků souborů v mapě.
### <EyeOff size="16" class="inline-block" style="margin-bottom: 2px" /> Skrýt/zobrazit
Přepnout viditelnost zvolených prvků souborů v mapě.
### <Plus size="16" class="inline-block" style="margin-bottom: 2px" /> Nová trasa
Vytvořit novou trasu ve zvoleném souboru.
@@ -38,17 +42,6 @@ Kromě toho musí být vybrán právě jeden soubor.
</DocsNote>
### <Plus size="16" class="inline-block" style="margin-bottom: 2px" /> New route
Create a new route in the selected file.
<DocsNote>
Tato akce je dostupná pouze v případě, že je povoleno rozvržení stromu seznamu souborů.
Kromě toho musí být vybrán právě jeden soubor.
</DocsNote>
### <Plus size="16" class="inline-block" style="margin-bottom: 2px" /> Nový úsek
Vytvořit nový úsek na zvolené trase.
@@ -60,17 +53,14 @@ Kromě toho musí být zvolena právě jedna trasa.
</DocsNote>
### <ArrowRightLeft size="16" class="inline-block" style="margin-bottom: 2px" /> Convert to route/track
Convert the selected routes to tracks, or the selected tracks or segments to routes.
Since a route has a single segment, a track with multiple segments is converted into one route per segment.
When the selected files contain only routes, or only tracks, you can also convert all of them at once.
### <FileStack size="16" class="inline-block" style="margin-bottom: 2px" /> Vybrat vše
Přidejte k výběru všechny prvky souborů v dané úrovni hierarchie.
### <Maximize size="16" class="inline-block" style="margin-bottom: 2px" /> Vycentrovat
Vystředí mapu na označené prvky souborů.
### <ClipboardCopy size="16" class="inline-block" style="margin-bottom: 2px" /> Kopírovat
Zkopíruje zvolené prvky souborů do schránky.
-4
View File
@@ -15,10 +15,6 @@ Menu akce souboru obsahuje sadu poměrně samovysvětlujících operací.
Vytvořit nový prázdný soubor.
### <Plus size="16" class="inline-block" style="margin-bottom: 2px" /> New route file
Create a new file containing an empty [route](../gpx).
### <FolderOpen size="16" class="inline-block" style="margin-bottom: 2px" /> Otevřít...
Otevřít soubor z vašeho počítače.
+2 -10
View File
@@ -3,7 +3,7 @@ title: Možnosti zobrazení
---
<script lang="ts">
import { ChartArea, ListTree, Map, Layers2, Coins, Milestone, EyeOff, Maximize, Box, Maximize2 } from '@lucide/svelte';
import { ChartArea, ListTree, Map, Layers2, Coins, Milestone, Box, Maximize2 } from '@lucide/svelte';
import DocsNote from '$lib/components/docs/DocsNote.svelte';
</script>
@@ -19,7 +19,7 @@ Skrýt výškový profil, aby se vytvořilo místo pro mapu, nebo ho zobrazit a
Přepnout rozvržení stromu pro [seznam souborů](../files-and-stats).
Toto zobrazení je ideální pro správu velkého počtu otevřených souborů, protože je uspořádá do svislého seznamu na pravé straně mapy.
In addition, the file tree view enables you to inspect the [tracks, segments, routes, and points of interest](../gpx) contained inside the files through collapsible sections.
Kromě jiného umožňuje zobrazení stromu souborů zkontrolovat [trasy, segmenty, a body zájmu](../gpx) obsažené uvnitř souborů skrze sbalitelné sekce.
### <Map size="16" class="inline-block" style="margin-bottom: 2px" /> Přepnout na předchozí základní mapu
@@ -38,14 +38,6 @@ Zobrazují se pro aktuální výběr, jako je [výškový profil](../files-and-s
Přepnout viditelnost směrových šipek na mapě.
### <EyeOff size="16" class="inline-block" style="margin-bottom: 2px" /> Hide/unhide
Toggle the visibility of the selected file items on the map.
### <Maximize size="16" class="inline-block" style="margin-bottom: 2px" /> Center
Center the map on the selected file items.
### <Box size="16" class="inline-block" style="margin-bottom: 2px" /> Přepnout 3D
Zapnout nebo vypnout 3D zobrazení mapy.
+1 -1
View File
@@ -16,7 +16,7 @@ Tento nástroj umožňuje extrahovat [trasy (nebo úseky)](../gpx) ze souborů (
<Extract class="text-foreground p-3 border rounded-md shadow-lg" />
</div>
Applying the tool to a file containing multiple [tracks or routes](../gpx) will create a new file for each of the tracks and routes it contains.
Použití nástroje na soubor obsahující více tras vytvoří nový soubor pro každou z tras, kterou obsahuje.
Obdobně použití nástroje na trasu obsahující více úseků vytvoří (ve stejném souboru) novou trasu pro každý úsek, který obsahuje.
<DocsNote>
+3 -3
View File
@@ -10,11 +10,11 @@ title: Sloučit
# <Group size="24" class="inline-block" style="margin-bottom: 5px" /> { title }
To use this tool, you need to [select](../files-and-stats) multiple files, [tracks, segments or routes](../gpx).
Chcete-li použít tento nástroj, musíte [vybrat](../files-and-stats) více souborů, [tras nebo segmentů](../gpx).
- Pokud je vaším cílem vytvořit z vašeho výběru jednu spojitou stopu, použijte možnost **Připojit stopy** a potvrďte.
- The second option can be used to create or manage files with multiple [tracks, segments or routes](../gpx).
Merging files (or tracks) will result in a single file (or track) containing all tracks and routes (or segments) from the selection.
- Druhá možnost může být použita k vytvoření nebo správě souborů s více [trasami nebo segmenty](../gpx).
Sloučením souborů (nebo tras) vznikne jeden soubor (nebo trasa) obsahující všechny trasy (nebo úseky) z výběru.
<DocsNote>
+1 -1
View File
@@ -27,6 +27,6 @@ Chcete-li rozdělit vybranou stopu na dvě části, klikněte na jednu z rozděl
Chcete-li rozdělit stopu v určitém bodě dle vašeho výběru, najeďte nad stopu na mapě.
Na pozici kurzoru se objeví nůžky, což znázorňuje, že v tomto bodě můžete stopu rozdělit.
You can choose to split the trace into two GPX files, or to keep the split parts in the same file as [tracks, segments or routes](../gpx).
Můžete zvolit rozdělení stopy na dva GPX soubory, nebo uchovat rozdělené části ve stejném souboru jako [trasy nebo segmenty](../gpx).
<DocsImage src="tools/split" alt="Najetí kurzorem nad vybranou stopu změní kurzor v nůžky." />
+1 -1
View File
@@ -51,7 +51,7 @@ By right-clicking on a file tab, you can access the same actions as in the [edit
As mentioned in the [view options section](./menu/view), you can switch to a tree layout for the files list.
This layout is ideal for managing a large number of open files, as it organizes them into a vertical list on the right side of the map.
In addition, the file tree view enables you to inspect the [tracks, segments, routes, and points of interest](./gpx) contained inside the files through collapsible sections.
In addition, the file tree view enables you to inspect the [tracks, segments, and points of interest](./gpx) contained inside the files through collapsible sections.
You can also apply [edit actions](./menu/edit) and [tools](./toolbar) to internal file items.
Furthermore, you can drag and drop the inner items to reorder them, or move them in the hierarchy or even to another file.
+1 -17
View File
@@ -3,15 +3,13 @@ title: GPX file format
---
<script>
import DocsNote from '$lib/components/docs/DocsNote.svelte';
import { Waypoints, Route, MapPin } from '@lucide/svelte';
import { Waypoints, MapPin } from '@lucide/svelte';
</script>
# { title }
The <a href="https://www.topografix.com/gpx.asp" target="_blank">GPX file format</a> is an open standard for exchanging GPS data between applications and GPS devices.
It essentially consists of a series of GPS points encoding one or multiple GPS traces, and, optionally, some points of interest.
These traces can be stored either as tracks or as routes.
GPX files may also contain metadata, of which the **name** and **description** fields are the most useful for users.
@@ -29,20 +27,6 @@ These are organized in a hierarchical structure, with tracks at the top level.
In most cases, GPX files contain a single track with a single segment.
However, the hierarchy described above allows for more advanced use cases, such as planning multi-day trips with several variants for each day.
### <Route size="16" class="inline-block" style="margin-bottom: 2px" /> Routes
A **route** is a single continuous path made of a sequence of **route points**.
These are mandatory checkpoints, which correspond to the anchor points of the [routing tool](./toolbar/routing).
The points in between are only a suggested path connecting the route points.
Like a track, a route can have a **name**, a **description**, and **appearance properties**.
<DocsNote>
Nowadays, most apps and GPS devices work perfectly well with tracks, even for navigation.
However, some of them only work with routes: in that case, you can [convert](./menu/edit) your tracks to routes, or [create](./menu/file) a route file from the start.
</DocsNote>
### <MapPin size="16" class="inline-block" style="margin-bottom: 2px" /> Points of interest
**Points of interest** (technically called _waypoints_) represent locations of interest to show either on a GPS device or on a digital map.
+10 -20
View File
@@ -3,14 +3,14 @@ title: Edit actions
---
<script lang="ts">
import { Undo2, Redo2, Info, PaintBucket, ArrowRightLeft, FileStack, ClipboardCopy, Scissors, ClipboardPaste, Trash2, Plus } from '@lucide/svelte';
import { Undo2, Redo2, Info, PaintBucket, EyeOff, FileStack, ClipboardCopy, Scissors, ClipboardPaste, Trash2, Maximize, Plus } from '@lucide/svelte';
import DocsNote from '$lib/components/docs/DocsNote.svelte';
</script>
# { title }
Unlike the file actions, the edit actions can potentially modify the content of the currently selected files.
Moreover, when the tree layout of the files list is enabled (see [Files and statistics](../files-and-stats)), they can also be applied to [tracks, segments, routes, and points of interest](../gpx).
Moreover, when the tree layout of the files list is enabled (see [Files and statistics](../files-and-stats)), they can also be applied to [tracks, segments, and points of interest](../gpx).
Therefore, we will refer to the elements that can be modified by these actions as _file items_.
Note that except for the undo and redo actions, the edit actions are also accessible through the context menu (right-click) of the file items.
@@ -27,6 +27,10 @@ Open the information dialog of the currently selected file item, where you can s
Open the appearance dialog, where you can change the color, opacity, and width of the selected file items on the map.
### <EyeOff size="16" class="inline-block" style="margin-bottom: 2px" /> Hide/unhide
Toggle the visibility of the selected file items on the map.
### <Plus size="16" class="inline-block" style="margin-bottom: 2px" /> New track
Create a new track in the selected file.
@@ -38,17 +42,6 @@ Additionally, the selection must be a single file.
</DocsNote>
### <Plus size="16" class="inline-block" style="margin-bottom: 2px" /> New route
Create a new route in the selected file.
<DocsNote>
This action is only available when the tree layout of the files list is enabled.
Additionally, the selection must be a single file.
</DocsNote>
### <Plus size="16" class="inline-block" style="margin-bottom: 2px" /> New segment
Create a new segment in the selected track.
@@ -60,17 +53,14 @@ Additionally, the selection must be a single track.
</DocsNote>
### <ArrowRightLeft size="16" class="inline-block" style="margin-bottom: 2px" /> Convert to route/track
Convert the selected routes to tracks, or the selected tracks or segments to routes.
Since a route has a single segment, a track with multiple segments is converted into one route per segment.
When the selected files contain only routes, or only tracks, you can also convert all of them at once.
### <FileStack size="16" class="inline-block" style="margin-bottom: 2px" /> Select all
Add all file items in the current hierarchy level to the selection.
### <Maximize size="16" class="inline-block" style="margin-bottom: 2px" /> Center
Center the map on the selected file items.
### <ClipboardCopy size="16" class="inline-block" style="margin-bottom: 2px" /> Kopier
Copy the selected file items to the clipboard.
-4
View File
@@ -15,10 +15,6 @@ The file actions menu contains a set of pretty self-explanatory file operations.
Create a new empty file.
### <Plus size="16" class="inline-block" style="margin-bottom: 2px" /> New route file
Create a new file containing an empty [route](../gpx).
### <FolderOpen size="16" class="inline-block" style="margin-bottom: 2px" /> Open...
Open files from your computer.
+2 -10
View File
@@ -3,7 +3,7 @@ title: View options
---
<script lang="ts">
import { ChartArea, ListTree, Map, Layers2, Coins, Milestone, EyeOff, Maximize, Box, Maximize2 } from '@lucide/svelte';
import { ChartArea, ListTree, Map, Layers2, Coins, Milestone, Box, Maximize2 } from '@lucide/svelte';
import DocsNote from '$lib/components/docs/DocsNote.svelte';
</script>
@@ -19,7 +19,7 @@ Hide the elevation profile to make room for the map, or show it to inspect the c
Toggle the tree layout for the [file list](../files-and-stats).
This layout is ideal for managing a large number of open files, as it organizes them into a vertical list on the right side of the map.
In addition, the file tree view enables you to inspect the [tracks, segments, routes, and points of interest](../gpx) contained inside the files through collapsible sections.
In addition, the file tree view enables you to inspect the [tracks, segments, and points of interest](../gpx) contained inside the files through collapsible sections.
### <Map size="16" class="inline-block" style="margin-bottom: 2px" /> Switch to previous basemap
@@ -38,14 +38,6 @@ They are displayed for the current selection, like the [elevation profile](../fi
Toggle the visibility of direction arrows on the map.
### <EyeOff size="16" class="inline-block" style="margin-bottom: 2px" /> Hide/unhide
Toggle the visibility of the selected file items on the map.
### <Maximize size="16" class="inline-block" style="margin-bottom: 2px" /> Center
Center the map on the selected file items.
### <Box size="16" class="inline-block" style="margin-bottom: 2px" /> Toggle 3D
Enter or exit the 3D map view.
+1 -1
View File
@@ -16,7 +16,7 @@ This tool allows you to extract [tracks (or segments)](../gpx) from files (or tr
<Extract class="text-foreground p-3 border rounded-md shadow-lg" />
</div>
Applying the tool to a file containing multiple [tracks or routes](../gpx) will create a new file for each of the tracks and routes it contains.
Applying the tool to a file containing multiple tracks will create a new file for each of the tracks it contains.
Similarly, applying the tool to a track containing multiple segments will create (in the same file) a new track for each of the segments it contains.
<DocsNote>
+3 -3
View File
@@ -10,11 +10,11 @@ title: Merge
# <Group size="24" class="inline-block" style="margin-bottom: 5px" /> { title }
To use this tool, you need to [select](../files-and-stats) multiple files, [tracks, segments or routes](../gpx).
To use this tool, you need to [select](../files-and-stats) multiple files, [tracks, or segments](../gpx).
- If your goal is to create a single continuous trace from your selection, use the **Connect the traces** option and validate.
- The second option can be used to create or manage files with multiple [tracks, segments or routes](../gpx).
Merging files (or tracks) will result in a single file (or track) containing all tracks and routes (or segments) from the selection.
- The second option can be used to create or manage files with multiple [tracks or segments](../gpx).
Merging files (or tracks) will result in a single file (or track) containing all tracks (or segments) from the selection.
<DocsNote>
+1 -1
View File
@@ -27,6 +27,6 @@ To split the selected trace into two parts, click on one of the split markers di
To split at a specific point of your choice, hover over the trace on the map.
Scissors will appear at the cursor position, showing that you can split the trace at that point.
You can choose to split the trace into two GPX files, or to keep the split parts in the same file as [tracks, segments or routes](../gpx).
You can choose to split the trace into two GPX files, or to keep the split parts in the same file as [tracks or segments](../gpx).
<DocsImage src="tools/split" alt="Hovering over the selected trace turns your cursor into scissors." />
+1 -1
View File
@@ -51,7 +51,7 @@ Wenn du mit der rechten Maustaste auf einen Datei-Tab klickst, kannst du auf die
Wie schon in den Anzeigeoptionen (/Menü/Ansicht) erwähnt, kann man das Layout des Dateibaums für die Dateienliste anpassen.
Dieses Layout eignet sich am besten um für eine große Anzahl von Dateien, da es diese in einer vertikalen Liste auf der rechten Seite der Karte anordnet.
In addition, the file tree view enables you to inspect the [tracks, segments, routes, and points of interest](./gpx) contained inside the files through collapsible sections.
Darüber hinaus ermöglicht die Dateibaumansicht die Prüfung der [Tracks, Segmente, und Punkte von Interesse](./gpx), die in den Dateien durch zusammenklappbare Abschnitte enthalten sind.
Du kannst auch [Bearbeiten von Aktionen](./menu/edit) und [tools](./toolbar) auf interne Dateielemente anwenden.
Außerdem können Sie die inneren Elemente per Drag & Drop verschieben, um sie neu zu ordnen oder in die Hierarchie oder sogar in eine andere Datei zu verschieben.
+1 -17
View File
@@ -3,15 +3,13 @@ title: GPX Dateiformat
---
<script>
import DocsNote from '$lib/components/docs/DocsNote.svelte';
import { Waypoints, Route, MapPin } from '@lucide/svelte';
import { Waypoints, MapPin } from '@lucide/svelte';
</script>
# { title }
Das <a href="https://www.topografix.com/gpx.asp" target="_blank">GPX-Dateiformat</a> ist ein offener Standard für den Austausch von GPS-Daten zwischen Anwendungen und GPS-Geräten.
Es besteht im Wesentlichen aus einer Serie von GPS-Punkten, die einen oder mehrere GPS-Spuren kodieren, und optional aus einigen interessanten Punkten.
These traces can be stored either as tracks or as routes.
GPX-Dateien können auch Metadaten enthalten, von denen die Felder **Name** und **Beschreibung** für Benutzer am nützlichsten sind.
@@ -29,20 +27,6 @@ Diese sind in einer hierarchischen Struktur mit Spuren auf der obersten Ebene or
In den meisten Fällen enthalten GPX-Dateien einen einzigen Track mit einem Segment.
Die oben beschriebene Hierarchie erlaubt jedoch weitergehende Anwendungsfälle, wie zum Beispiel die Planung von Mehrtagestouren mit mehreren Varianten pro Tag.
### <Route size="16" class="inline-block" style="margin-bottom: 2px" /> Routes
A **route** is a single continuous path made of a sequence of **route points**.
These are mandatory checkpoints, which correspond to the anchor points of the [routing tool](./toolbar/routing).
The points in between are only a suggested path connecting the route points.
Like a track, a route can have a **name**, a **description**, and **appearance properties**.
<DocsNote>
Nowadays, most apps and GPS devices work perfectly well with tracks, even for navigation.
However, some of them only work with routes: in that case, you can [convert](./menu/edit) your tracks to routes, or [create](./menu/file) a route file from the start.
</DocsNote>
### <MapPin size="16" class="inline-block" style="margin-bottom: 2px" /> Punkte von Interesse
**Points of Interessive** (technisch genannt _Wegpunkte_) stellen Orte dar, die auf einem GPS-Gerät oder auf einer digitalen Karte angezeigt werden.
+10 -20
View File
@@ -3,14 +3,14 @@ title: Bearbeite Aktionen
---
<script lang="ts">
import { Undo2, Redo2, Info, PaintBucket, ArrowRightLeft, FileStack, ClipboardCopy, Scissors, ClipboardPaste, Trash2, Plus } from '@lucide/svelte';
import { Undo2, Redo2, Info, PaintBucket, EyeOff, FileStack, ClipboardCopy, Scissors, ClipboardPaste, Trash2, Maximize, Plus } from '@lucide/svelte';
import DocsNote from '$lib/components/docs/DocsNote.svelte';
</script>
# { title }
Im Gegensatz zu den Datei-Aktionen können die Bearbeitungs-Aktionen möglicherweise den Inhalt der aktuell ausgewählten Dateien verändern.
Moreover, when the tree layout of the files list is enabled (see [Files and statistics](../files-and-stats)), they can also be applied to [tracks, segments, routes, and points of interest](../gpx).
Wenn das vertikale Layout der Dateiliste aktiviert ist (siehe [Dateien und Statistik](.. /files-and-stats)), können dieses auch auf [Tracks, Segmente und Points-of-Interest](../gpx) angewendet werden.
Daher bezeichnen wir die Elemente, die durch diese Aktionen geändert werden können, als _file items_.
Beachte, dass die Bearbeitungsaktionen auch über das Kontextmenü (Rechtsklick) der Dateielemente zugänglich sind, ausgenommen von "rückgängig" und "wiederholen".
@@ -27,6 +27,10 @@ Dies gilt für alle Aktionen der Bedienoberfläche, aber nicht für Optionen, An
Öffnen Sie das Dialogfeld Darstellung, in der Sie die Farbe, Deckkraft und Breite der ausgewählten Dateielemente auf der Karte ändern können.
### <EyeOff size="16" class="inline-block" style="margin-bottom: 2px" /> Einblenden/Ausblenden
Konfigurieren Sie die Sichtbarkeit der ausgewählten Dateielemente auf der Karte.
### <Plus size="16" class="inline-block" style="margin-bottom: 2px" /> Neuer Track
Erstelle ein neues Segment im ausgewählten Track.
@@ -38,17 +42,6 @@ Außerdem muss die Auswahl aus einem einzelnen Track bestehen.
</DocsNote>
### <Plus size="16" class="inline-block" style="margin-bottom: 2px" /> New route
Create a new route in the selected file.
<DocsNote>
Diese Aktion ist nur verfügbar, wenn das vertikale Layout der Dateiliste aktiviert ist.
Außerdem muss die Auswahl aus einem einzelnen Track bestehen.
</DocsNote>
### <Plus size="16" class="inline-block" style="margin-bottom: 2px" /> Neues Segment
Erstelle ein neues Segment im ausgewählten Track.
@@ -60,17 +53,14 @@ Außerdem muss die Auswahl aus einem einzelnen Track bestehen.
</DocsNote>
### <ArrowRightLeft size="16" class="inline-block" style="margin-bottom: 2px" /> Convert to route/track
Convert the selected routes to tracks, or the selected tracks or segments to routes.
Since a route has a single segment, a track with multiple segments is converted into one route per segment.
When the selected files contain only routes, or only tracks, you can also convert all of them at once.
### <FileStack size="16" class="inline-block" style="margin-bottom: 2px" /> Alle auswählen
Alle Dateielemente in der aktuellen Hierarchieebene zur Auswahl hinzufügen.
### <Maximize size="16" class="inline-block" style="margin-bottom: 2px" /> Zentrieren
Zentrieren der Karte auf den ausgewählten Dateielementen.
### <ClipboardCopy size="16" class="inline-block" style="margin-bottom: 2px" /> Kopieren
Kopieren Sie die ausgewählten Dateielemente in die Zwischenablage.
-4
View File
@@ -15,10 +15,6 @@ Das Aktionsmenü Datei enthält eine Reihe von selbsterklärenden Dateioperation
Neue leere Datei erstellen.
### <Plus size="16" class="inline-block" style="margin-bottom: 2px" /> New route file
Create a new file containing an empty [route](../gpx).
### <FolderOpen size="16" class="inline-block" style="margin-bottom: 2px" /> Öffnen...
Öffne Dateien von Ihrem Computer.
+2 -10
View File
@@ -3,7 +3,7 @@ title: Anzeigeoptionen
---
<script lang="ts">
import { ChartArea, ListTree, Map, Layers2, Coins, Milestone, EyeOff, Maximize, Box, Maximize2 } from '@lucide/svelte';
import { ChartArea, ListTree, Map, Layers2, Coins, Milestone, Box, Maximize2 } from '@lucide/svelte';
import DocsNote from '$lib/components/docs/DocsNote.svelte';
</script>
@@ -19,7 +19,7 @@ Das Höhenprofil ausblenden, um Platz für die Karte zu schaffen, oder um die ak
Umschalten des vertikalen Layouts für die [Dateiliste](../files-and-stats).
Dieses Layout eignet sich am besten um für eine große Anzahl von Dateien, da es diese in einer vertikalen Liste auf der rechten Seite der Karte anordnet.
In addition, the file tree view enables you to inspect the [tracks, segments, routes, and points of interest](../gpx) contained inside the files through collapsible sections.
Darüber hinaus ermöglicht die Dateibaumansicht die Prüfung der [Tracks, Segmente, und Punkte von Interesse](../gpx), die in den Dateien durch zusammenklappbare Abschnitte enthalten sind.
### <Map size="16" class="inline-block" style="margin-bottom: 2px" /> Wechseln zur vorherigen Basiskarte
@@ -38,14 +38,6 @@ Sie werden für die aktuelle Auswahl angezeigt, wie in [elevation profile](../fi
Schaltet die Sichtbarkeit der Richtungspfeile auf der Karte um.
### <EyeOff size="16" class="inline-block" style="margin-bottom: 2px" /> Hide/unhide
Toggle the visibility of the selected file items on the map.
### <Maximize size="16" class="inline-block" style="margin-bottom: 2px" /> Center
Center the map on the selected file items.
### <Box size="16" class="inline-block" style="margin-bottom: 2px" /> 3D Ein/Aus
Aufrufen oder Beenden der 3D-Kartenansicht.
@@ -19,6 +19,6 @@ Mit diesem Tool kannst du Höhendaten zu Routen und [Points of Interest] (../gpx
<DocsNote>
Elevation data is provided by <a href="https://mapterhorn.com" target="_blank">Mapterhorn</a>.
Mehr über die Herkunft und Genauigkeit erfährst du in der <a href="https://mapterhorn.com/attribution/" target="_blank">Dokumentation</a>.
You can learn more about its origin and accuracy in the <a href="https://mapterhorn.com/attribution/" target="_blank">documentation</a>.
</DocsNote>
+1 -1
View File
@@ -16,7 +16,7 @@ Dieses Werkzeug ermöglicht Ihnen [Tracks (oder Segmente)](../gpx) von Dateien (
<Extract class="text-foreground p-3 border rounded-md shadow-lg" />
</div>
Applying the tool to a file containing multiple [tracks or routes](../gpx) will create a new file for each of the tracks and routes it contains.
Wenn Sie das Werkzeug auf eine Datei mit mehreren enthaltenen Tracks anwenden, wird für jeden der darin enthaltenen Tracks eine neue Datei erstellt.
Gleichermaßen gilt: wenn du das Werkzeug auf einen Track anwendest, der mehrere Segmente enthält, wird (in derselben Datei) ein neuer Track für jedes der darin enthaltenen Segmente erstellt.
<DocsNote>
+3 -3
View File
@@ -10,11 +10,11 @@ title: Zusammenfügen
# <Group size="24" class="inline-block" style="margin-bottom: 5px" /> { title }
To use this tool, you need to [select](../files-and-stats) multiple files, [tracks, segments or routes](../gpx).
Um dieses Tool nutzen zu können, musst du [select](../files-and-stats) mehrere Dateien, [Tracks oder Segmente](../gpx).
- Wenn Ihr Ziel darin besteht, eine einzige kontinuierliche Spur aus Ihrer Auswahl zu erstellen, verwenden Sie die Option **Verbinden Sie die Spuren** und validieren Sie sie.
- The second option can be used to create or manage files with multiple [tracks, segments or routes](../gpx).
Merging files (or tracks) will result in a single file (or track) containing all tracks and routes (or segments) from the selection.
- Die zweite Option kann verwendet werden, um Dateien mit mehreren [Tracks oder Segments](../gpx) zu erstellen oder zu verwalten.
Das Zusammenführen von Dateien (oder Tracks) führt zu einer einzigen Datei (oder Track), die alle Tracks (oder Segmente) aus der Auswahl enthält.
<DocsNote>
+1 -1
View File
@@ -27,6 +27,6 @@ Um die ausgewählte Spur in zwei Teile aufzuteilen, klicken Sie auf einen der ge
Um sich an einem bestimmten Punkt Ihrer Wahl zu teilen, fahren Sie über die Spur auf der Karte.
Schere werden an der Cursorposition angezeigt, so dass Sie die Spur an diesem Punkt teilen können.
You can choose to split the trace into two GPX files, or to keep the split parts in the same file as [tracks, segments or routes](../gpx).
Du kannst die Trace in zwei GPX-Dateien aufteilen oder die Teile in der gleichen Datei wie [Tracks oder Segmente](../gpx).
<DocsImage src="tools/split" alt="Über dem ausgewählten Pfad schweben verwandelt den Cursor in Scheren." />
+1 -1
View File
@@ -51,7 +51,7 @@ By right-clicking on a file tab, you can access the same actions as in the [edit
As mentioned in the [view options section](./menu/view), you can switch to a tree layout for the files list.
This layout is ideal for managing a large number of open files, as it organizes them into a vertical list on the right side of the map.
In addition, the file tree view enables you to inspect the [tracks, segments, routes, and points of interest](./gpx) contained inside the files through collapsible sections.
In addition, the file tree view enables you to inspect the [tracks, segments, and points of interest](./gpx) contained inside the files through collapsible sections.
You can also apply [edit actions](./menu/edit) and [tools](./toolbar) to internal file items.
Furthermore, you can drag and drop the inner items to reorder them, or move them in the hierarchy or even to another file.
+1 -17
View File
@@ -3,15 +3,13 @@ title: GPX file format
---
<script>
import DocsNote from '$lib/components/docs/DocsNote.svelte';
import { Waypoints, Route, MapPin } from '@lucide/svelte';
import { Waypoints, MapPin } from '@lucide/svelte';
</script>
# { title }
The <a href="https://www.topografix.com/gpx.asp" target="_blank">GPX file format</a> is an open standard for exchanging GPS data between applications and GPS devices.
It essentially consists of a series of GPS points encoding one or multiple GPS traces, and, optionally, some points of interest.
These traces can be stored either as tracks or as routes.
GPX files may also contain metadata, of which the **name** and **description** fields are the most useful for users.
@@ -29,20 +27,6 @@ These are organized in a hierarchical structure, with tracks at the top level.
In most cases, GPX files contain a single track with a single segment.
However, the hierarchy described above allows for more advanced use cases, such as planning multi-day trips with several variants for each day.
### <Route size="16" class="inline-block" style="margin-bottom: 2px" /> Routes
A **route** is a single continuous path made of a sequence of **route points**.
These are mandatory checkpoints, which correspond to the anchor points of the [routing tool](./toolbar/routing).
The points in between are only a suggested path connecting the route points.
Like a track, a route can have a **name**, a **description**, and **appearance properties**.
<DocsNote>
Nowadays, most apps and GPS devices work perfectly well with tracks, even for navigation.
However, some of them only work with routes: in that case, you can [convert](./menu/edit) your tracks to routes, or [create](./menu/file) a route file from the start.
</DocsNote>
### <MapPin size="16" class="inline-block" style="margin-bottom: 2px" /> Points of interest
**Points of interest** (technically called _waypoints_) represent locations of interest to show either on a GPS device or on a digital map.
+10 -20
View File
@@ -3,14 +3,14 @@ title: Edit actions
---
<script lang="ts">
import { Undo2, Redo2, Info, PaintBucket, ArrowRightLeft, FileStack, ClipboardCopy, Scissors, ClipboardPaste, Trash2, Plus } from '@lucide/svelte';
import { Undo2, Redo2, Info, PaintBucket, EyeOff, FileStack, ClipboardCopy, Scissors, ClipboardPaste, Trash2, Maximize, Plus } from '@lucide/svelte';
import DocsNote from '$lib/components/docs/DocsNote.svelte';
</script>
# { title }
Unlike the file actions, the edit actions can potentially modify the content of the currently selected files.
Moreover, when the tree layout of the files list is enabled (see [Files and statistics](../files-and-stats)), they can also be applied to [tracks, segments, routes, and points of interest](../gpx).
Moreover, when the tree layout of the files list is enabled (see [Files and statistics](../files-and-stats)), they can also be applied to [tracks, segments, and points of interest](../gpx).
Therefore, we will refer to the elements that can be modified by these actions as _file items_.
Note that except for the undo and redo actions, the edit actions are also accessible through the context menu (right-click) of the file items.
@@ -27,6 +27,10 @@ Open the information dialog of the currently selected file item, where you can s
Open the appearance dialog, where you can change the color, opacity, and width of the selected file items on the map.
### <EyeOff size="16" class="inline-block" style="margin-bottom: 2px" /> Hide/unhide
Toggle the visibility of the selected file items on the map.
### <Plus size="16" class="inline-block" style="margin-bottom: 2px" /> New track
Create a new track in the selected file.
@@ -38,17 +42,6 @@ Additionally, the selection must be a single file.
</DocsNote>
### <Plus size="16" class="inline-block" style="margin-bottom: 2px" /> New route
Create a new route in the selected file.
<DocsNote>
This action is only available when the tree layout of the files list is enabled.
Additionally, the selection must be a single file.
</DocsNote>
### <Plus size="16" class="inline-block" style="margin-bottom: 2px" /> New segment
Create a new segment in the selected track.
@@ -60,17 +53,14 @@ Additionally, the selection must be a single track.
</DocsNote>
### <ArrowRightLeft size="16" class="inline-block" style="margin-bottom: 2px" /> Convert to route/track
Convert the selected routes to tracks, or the selected tracks or segments to routes.
Since a route has a single segment, a track with multiple segments is converted into one route per segment.
When the selected files contain only routes, or only tracks, you can also convert all of them at once.
### <FileStack size="16" class="inline-block" style="margin-bottom: 2px" /> Select all
Add all file items in the current hierarchy level to the selection.
### <Maximize size="16" class="inline-block" style="margin-bottom: 2px" /> Center
Center the map on the selected file items.
### <ClipboardCopy size="16" class="inline-block" style="margin-bottom: 2px" /> Copy
Copy the selected file items to the clipboard.
-4
View File
@@ -15,10 +15,6 @@ The file actions menu contains a set of pretty self-explanatory file operations.
Create a new empty file.
### <Plus size="16" class="inline-block" style="margin-bottom: 2px" /> New route file
Create a new file containing an empty [route](../gpx).
### <FolderOpen size="16" class="inline-block" style="margin-bottom: 2px" /> Open...
Open files from your computer.
+2 -10
View File
@@ -3,7 +3,7 @@ title: View options
---
<script lang="ts">
import { ChartArea, ListTree, Map, Layers2, Coins, Milestone, EyeOff, Maximize, Box, Maximize2 } from '@lucide/svelte';
import { ChartArea, ListTree, Map, Layers2, Coins, Milestone, Box, Maximize2 } from '@lucide/svelte';
import DocsNote from '$lib/components/docs/DocsNote.svelte';
</script>
@@ -19,7 +19,7 @@ Hide the elevation profile to make room for the map, or show it to inspect the c
Toggle the tree layout for the [file list](../files-and-stats).
This layout is ideal for managing a large number of open files, as it organizes them into a vertical list on the right side of the map.
In addition, the file tree view enables you to inspect the [tracks, segments, routes, and points of interest](../gpx) contained inside the files through collapsible sections.
In addition, the file tree view enables you to inspect the [tracks, segments, and points of interest](../gpx) contained inside the files through collapsible sections.
### <Map size="16" class="inline-block" style="margin-bottom: 2px" /> Switch to previous basemap
@@ -38,14 +38,6 @@ They are displayed for the current selection, like the [elevation profile](../fi
Toggle the visibility of direction arrows on the map.
### <EyeOff size="16" class="inline-block" style="margin-bottom: 2px" /> Hide/unhide
Toggle the visibility of the selected file items on the map.
### <Maximize size="16" class="inline-block" style="margin-bottom: 2px" /> Center
Center the map on the selected file items.
### <Box size="16" class="inline-block" style="margin-bottom: 2px" /> Toggle 3D
Enter or exit the 3D map view.
+1 -1
View File
@@ -16,7 +16,7 @@ This tool allows you to extract [tracks (or segments)](../gpx) from files (or tr
<Extract class="text-foreground p-3 border rounded-md shadow-lg" />
</div>
Applying the tool to a file containing multiple [tracks or routes](../gpx) will create a new file for each of the tracks and routes it contains.
Applying the tool to a file containing multiple tracks will create a new file for each of the tracks it contains.
Similarly, applying the tool to a track containing multiple segments will create (in the same file) a new track for each of the segments it contains.
<DocsNote>
+3 -3
View File
@@ -10,11 +10,11 @@ title: Merge
# <Group size="24" class="inline-block" style="margin-bottom: 5px" /> { title }
To use this tool, you need to [select](../files-and-stats) multiple files, [tracks, segments or routes](../gpx).
To use this tool, you need to [select](../files-and-stats) multiple files, [tracks, or segments](../gpx).
- If your goal is to create a single continuous trace from your selection, use the **Connect the traces** option and validate.
- The second option can be used to create or manage files with multiple [tracks, segments or routes](../gpx).
Merging files (or tracks) will result in a single file (or track) containing all tracks and routes (or segments) from the selection.
- The second option can be used to create or manage files with multiple [tracks or segments](../gpx).
Merging files (or tracks) will result in a single file (or track) containing all tracks (or segments) from the selection.
<DocsNote>
+1 -1
View File
@@ -27,6 +27,6 @@ To split the selected trace into two parts, click on one of the split markers di
To split at a specific point of your choice, hover over the trace on the map.
Scissors will appear at the cursor position, showing that you can split the trace at that point.
You can choose to split the trace into two GPX files, or to keep the split parts in the same file as [tracks, segments or routes](../gpx).
You can choose to split the trace into two GPX files, or to keep the split parts in the same file as [tracks or segments](../gpx).
<DocsImage src="tools/split" alt="Hovering over the selected trace turns your cursor into scissors." />
+6
View File
@@ -67,6 +67,12 @@ Since a route has a single segment, a track with multiple segments is converted
When the selected files contain only routes, or only tracks, you can also convert all of them at once.
<DocsNote>
Except for whole files, this action is only available when the tree layout of the files list is enabled.
</DocsNote>
### <FileStack size="16" class="inline-block" style="margin-bottom: 2px" /> Select all
Add all file items in the current hierarchy level to the selection.
+1 -1
View File
@@ -51,7 +51,7 @@ Al hacer clic con el botón derecho en una pestaña de archivo, puede acceder a
Como se mencionó en la [sección de opciones de vista](./menu/view), puede cambiar a un esquema de árbol para la lista de archivos.
Esta disposición es ideal para administrar un gran número de archivos abiertos ya que los organiza en una lista vertical en la parte derecha del mapa.
In addition, the file tree view enables you to inspect the [tracks, segments, routes, and points of interest](./gpx) contained inside the files through collapsible sections.
Además, la vista de árbol de archivos permite inspeccionar [pistas, segmentos y puntos de interés](./gpx) contenidos dentro de los archivos a través de secciones plegables.
También puede aplicar [acciones de edición](./menu/edit) y [herramientas](./toolbar) a los elementos internos del archivo.
Además, puede arrastrar y soltar los elementos internos para reordenarlos, o moverlos dentro de la jerarquía o incluso a otro archivo.
+1 -17
View File
@@ -3,15 +3,13 @@ title: Formato de archivo GPX
---
<script>
import DocsNote from '$lib/components/docs/DocsNote.svelte';
import { Waypoints, Route, MapPin } from '@lucide/svelte';
import { Waypoints, MapPin } from '@lucide/svelte';
</script>
# { title }
El <a href="https://www.topografix.com/gpx.asp" target="_blank">formato de archivo GPX</a> es un estándar abierto para el intercambio de datos GPS entre aplicaciones y dispositivos GPS.
Consiste esencialmente en una serie de puntos GPS que codifican uno o varios trazados GPS y, opcionalmente, algunos puntos de interés.
These traces can be stored either as tracks or as routes.
Los archivos GPX también pueden contener metadatos, de los cuales los campos **nombre** y **descripción** son los más útiles para los usuarios.
@@ -29,20 +27,6 @@ Como se mencionó anteriormente, un archivo GPX puede contener múltiples trazad
En la mayoría de los casos, los archivos GPX contienen una sola pista con un solo segmento.
Sin embargo, la jerarquía descrita anteriormente permite casos de uso más avanzados, como la planificación de viajes de varios días con varias variantes para cada día.
### <Route size="16" class="inline-block" style="margin-bottom: 2px" /> Routes
A **route** is a single continuous path made of a sequence of **route points**.
These are mandatory checkpoints, which correspond to the anchor points of the [routing tool](./toolbar/routing).
The points in between are only a suggested path connecting the route points.
Like a track, a route can have a **name**, a **description**, and **appearance properties**.
<DocsNote>
Nowadays, most apps and GPS devices work perfectly well with tracks, even for navigation.
However, some of them only work with routes: in that case, you can [convert](./menu/edit) your tracks to routes, or [create](./menu/file) a route file from the start.
</DocsNote>
### <MapPin size="16" class="inline-block" style="margin-bottom: 2px" /> Puntos de interés
**Puntos de interés** (técnicamente llamados _waypoints_) representan ubicaciones de interés para mostrar en un dispositivo GPS o en un mapa digital.
+10 -20
View File
@@ -3,14 +3,14 @@ title: Acciones de edición
---
<script lang="ts">
import { Undo2, Redo2, Info, PaintBucket, ArrowRightLeft, FileStack, ClipboardCopy, Scissors, ClipboardPaste, Trash2, Plus } from '@lucide/svelte';
import { Undo2, Redo2, Info, PaintBucket, EyeOff, FileStack, ClipboardCopy, Scissors, ClipboardPaste, Trash2, Maximize, Plus } from '@lucide/svelte';
import DocsNote from '$lib/components/docs/DocsNote.svelte';
</script>
# { title }
A diferencia de las acciones de archivo, las acciones de edición pueden potencialmente modificar el contenido de los archivos seleccionados actualmente.
Moreover, when the tree layout of the files list is enabled (see [Files and statistics](../files-and-stats)), they can also be applied to [tracks, segments, routes, and points of interest](../gpx).
Además, cuando está habilitada la disposición del árbol de la lista de archivos (ver [Archivos y estadísticas](../files-and-stats)), se pueden aplicar a [pistas, segmentos y puntos de interés](../gpx).
Por lo tanto, nos referiremos a los elementos que pueden ser modificados por estas acciones como _elementos de archivo_.
Tenga en cuenta que, excepto para las acciones de deshacer y rehacer, las acciones de edición también son accesibles a través del menú contextual (clic derecho) de los elementos del archivo.
@@ -27,6 +27,10 @@ Abre el cuadro de diálogo de información del elemento del archivo seleccionado
Abre el diálogo de apariencia, donde puede cambiar el color, la opacidad y el ancho en el mapa de los elementos de archivo seleccionados.
### <EyeOff size="16" class="inline-block" style="margin-bottom: 2px" /> Ocultar/mostrar
Alterna la visibilidad de los elementos del archivo seleccionado en el mapa.
### <Plus size="16" class="inline-block" style="margin-bottom: 2px" /> Nueva pista
Crear una nueva pista en el archivo seleccionado.
@@ -38,17 +42,6 @@ Además, la selección debe ser un solo archivo.
</DocsNote>
### <Plus size="16" class="inline-block" style="margin-bottom: 2px" /> New route
Create a new route in the selected file.
<DocsNote>
Esta acción sólo está disponible cuando está habilitada la disposición del árbol de la lista de archivos.
Además, la selección debe ser un solo archivo.
</DocsNote>
### <Plus size="16" class="inline-block" style="margin-bottom: 2px" /> Nuevo segmento
Crear un nuevo segmento en la pista seleccionada.
@@ -60,17 +53,14 @@ Además, la selección debe ser una sola pista.
</DocsNote>
### <ArrowRightLeft size="16" class="inline-block" style="margin-bottom: 2px" /> Convert to route/track
Convert the selected routes to tracks, or the selected tracks or segments to routes.
Since a route has a single segment, a track with multiple segments is converted into one route per segment.
When the selected files contain only routes, or only tracks, you can also convert all of them at once.
### <FileStack size="16" class="inline-block" style="margin-bottom: 2px" /> Seleccionar todo
Añade todos los elementos del archivo en el nivel de jerarquía actual a la selección.
### <Maximize size="16" class="inline-block" style="margin-bottom: 2px" /> Centrar
Centrar el mapa en los elementos de archivo seleccionados.
### <ClipboardCopy size="16" class="inline-block" style="margin-bottom: 2px" /> Copiar
Copia los elementos del archivo seleccionado al portapapeles.
-4
View File
@@ -15,10 +15,6 @@ El menú de acciones de archivo contiene un conjunto de operaciones bastante aut
Crea un nuevo archivo vacío.
### <Plus size="16" class="inline-block" style="margin-bottom: 2px" /> New route file
Create a new file containing an empty [route](../gpx).
### <FolderOpen size="16" class="inline-block" style="margin-bottom: 2px" /> Abrir...
Abre archivos desde su equipo.
+2 -10
View File
@@ -3,7 +3,7 @@ title: Opciones de vista
---
<script lang="ts">
import { ChartArea, ListTree, Map, Layers2, Coins, Milestone, EyeOff, Maximize, Box, Maximize2 } from '@lucide/svelte';
import { ChartArea, ListTree, Map, Layers2, Coins, Milestone, Box, Maximize2 } from '@lucide/svelte';
import DocsNote from '$lib/components/docs/DocsNote.svelte';
</script>
@@ -19,7 +19,7 @@ Oculta el perfil de elevación para dejar espacio para el mapa, o lo muestra par
Alterna el diseño de árbol para la [lista de archivos](../files-and-stats).
Esta disposición es ideal para administrar un gran número de archivos abiertos ya que los organiza en una lista vertical en la parte derecha del mapa.
In addition, the file tree view enables you to inspect the [tracks, segments, routes, and points of interest](../gpx) contained inside the files through collapsible sections.
Además, la vista de árbol de archivos le permite inspeccionar [pistas, segmentos y puntos de interés](../gpx) contenidos dentro de los archivos a través de secciones plegables.
### <Map size="16" class="inline-block" style="margin-bottom: 2px" /> Cambiar al mapa base anterior
@@ -38,14 +38,6 @@ Se muestran para la selección actual, como el [perfil de elevación](../files-a
Alterna la visibilidad de las flechas de dirección en el mapa.
### <EyeOff size="16" class="inline-block" style="margin-bottom: 2px" /> Hide/unhide
Toggle the visibility of the selected file items on the map.
### <Maximize size="16" class="inline-block" style="margin-bottom: 2px" /> Center
Center the map on the selected file items.
### <Box size="16" class="inline-block" style="margin-bottom: 2px" /> Alternar 3D
Entra o sale de la vista 3D del mapa.
+1 -1
View File
@@ -16,7 +16,7 @@ Esta herramienta le permite extraer [pistas (o segmentos)](../gpx) de archivos (
<Extract class="text-foreground p-3 border rounded-md shadow-lg" />
</div>
Applying the tool to a file containing multiple [tracks or routes](../gpx) will create a new file for each of the tracks and routes it contains.
Aplicar la herramienta a un archivo que contenga múltiples pistas creará un nuevo archivo para cada una de las pistas.
Del mismo modo, la aplicación de la herramienta a una pista que contenga varios segmentos creará (en el mismo archivo) una nueva pista para cada uno de los segmentos.
<DocsNote>
+3 -3
View File
@@ -10,11 +10,11 @@ title: Combinar
# <Group size="24" class="inline-block" style="margin-bottom: 5px" /> { title }
To use this tool, you need to [select](../files-and-stats) multiple files, [tracks, segments or routes](../gpx).
Para usar esta herramienta, necesita [seleccionar](../files-and-stats) múltiples archivos, [pistas o segmentos](../gpx).
- Si su objetivo es crear un único trazado continuo a partir de su selección, use la opción **Conectar los trazados** y valide.
- The second option can be used to create or manage files with multiple [tracks, segments or routes](../gpx).
Merging files (or tracks) will result in a single file (or track) containing all tracks and routes (or segments) from the selection.
- La segunda opción se puede usar para crear o gestionar archivos con múltiples [pistas o segmentos](../gpx).
Combinar archivos (o pistas) dará como resultado un único archivo (o pista) que contendrá todas las pistas (o segmentos) de la selección.
<DocsNote>
+1 -1
View File
@@ -27,6 +27,6 @@ Para dividir el trazado seleccionado en dos partes, haga clic en uno de los marc
Para dividir en un punto específico de su elección, sitúe el cursor sobre el trazado en el mapa.
Las tijeras aparecerán en la posición del cursor, mostrando que puede dividir el trazado en ese punto.
You can choose to split the trace into two GPX files, or to keep the split parts in the same file as [tracks, segments or routes](../gpx).
Puede elegir dividir el trazado en dos archivos GPX o mantener las partes divididas en el mismo archivo como [pistas o segmentos](../gpx).
<DocsImage src="tools/split" alt="Situar el cursor sobre el trazado seleccionado lo convierte en tijeras." />
+1 -1
View File
@@ -50,7 +50,7 @@ Fitxategi fitxa batean eskuin-klik eginez, [editatu menuko](./menu/edit) ekintza
Aipatu bezala, [ikusi atalean](./menu/view), fitxategien zerrendaren zuhaitz diseinura alda dezakezu.
Diseinu hau aproposa da irekitako fitxategi ugari kudeatzeko, maparen eskuinaldean zerrenda bertikal batean antolatzen baitu.
In addition, the file tree view enables you to inspect the [tracks, segments, routes, and points of interest](./gpx) contained inside the files through collapsible sections.
Gainera, fitxategi-zuhaitzaren ikuspegiak aukera ematen du [arrastoak, segmentuak eta interesguneak](./gpx) ikuskatzeko ataletan antolatutako fitxategietan.
[Edizio ekintzak](./menu/edit) eta [tresnak](./toolbar) fitxategien barneko elementuetara ere aplikatu ditzakezu.
Gainera, barruko elementuak arrastatu eta jaregin ditzakezu berrantolatzeko edo hierarkian edo beste fitxategi batera eraman.
+1 -17
View File
@@ -3,15 +3,13 @@ title: GPX fitxategi formatua
---
<script>
import DocsNote from '$lib/components/docs/DocsNote.svelte';
import { Waypoints, Route, MapPin } from '@lucide/svelte';
import { Waypoints, MapPin } from '@lucide/svelte';
</script>
# { title }
<a href="https://www.topografix.com/gpx.asp" target="_blank">GPX fitxategi formatua</a> GPS datuak aplikazioen eta GPS gailuen artean trukatzeko formatu ireki estandarra da.
GPS puntu serie bat GPS arrasto bat edo gehiagotan bildua, eta, aukeran, interesgune batzuk.
These traces can be stored either as tracks or as routes.
GPX fitxategiek metadatuak ere izan ditzakete, horien artean **izena** eta **deskribapena** eremuak erabiltzaileentzat erabilgarrienak izaten dira.
@@ -29,20 +27,6 @@ Egitura hierarkiko batean antolatuta daude, goiko mailan:
Gehienetan, GPX fitxategiek segmentu bakarraz osatutako arrasto edo ibilbide bakarra dute.
Hala ere, goian deskribatutako hierarkiak erabilera kasu aurreratuagoak egiteko aukera ematen du, hala nola egun anitzeko bidaiak antolatzea egun bakoitzeko hainbat aldaerekin.
### <Route size="16" class="inline-block" style="margin-bottom: 2px" /> Routes
A **route** is a single continuous path made of a sequence of **route points**.
These are mandatory checkpoints, which correspond to the anchor points of the [routing tool](./toolbar/routing).
The points in between are only a suggested path connecting the route points.
Like a track, a route can have a **name**, a **description**, and **appearance properties**.
<DocsNote>
Nowadays, most apps and GPS devices work perfectly well with tracks, even for navigation.
However, some of them only work with routes: in that case, you can [convert](./menu/edit) your tracks to routes, or [create](./menu/file) a route file from the start.
</DocsNote>
### <MapPin size="16" class="inline-block" style="margin-bottom: 2px" /> Interesguneak
**Interesguneak** (_bide-puntu_ gisa ere ezagunak) interesgarriak diren kokapenak GPS gailu batean edo mapa digital batean erakusteko erabiltzen dira.
+2 -2
View File
@@ -13,8 +13,8 @@ title: Integrazioa
Behar duzun guztia hau da:
1. Zure zerbitzarian edo Google Drive-n ostatatutako GPX fitxategiak, edo URL publiko baten bidez eskura daitezke;
2. _Aukerakoa:_ <a href="https://cloud.maptiler.com/auth/widget?next=https://cloud.maptiler.com/maps/" target="_blank">MapTiler tekla</a> MapTiler mapak kargatzeko.
1. GPX files hosted on your server or on Google Drive, or accessible via a public URL;
2. _Optional:_ a <a href="https://cloud.maptiler.com/auth/widget?next=https://cloud.maptiler.com/maps/" target="_blank">MapTiler key</a> to load MapTiler maps.
Ondoren, konfiguratzailearekin jolastu dezakezu zure mapa pertsonalizatzeko eta dagokion HTML kodea sortzeko.
+2 -2
View File
@@ -59,7 +59,7 @@ Maparen geruzen botoiak mapa-oinarri desberdinen artean aldatzeko aukera ematen
<DocsLayers />
<span class="text-sm text-center mt-2">
Pasa ezazu maparen gainean <a href="https://hiking.waymarkedtrails.org" target="_blank">markatutako mendi-ibiliak</a> gainjartzeko oinarrizko mapa topografikoaren gainean.
Hover over the map to show the <a href="https://hiking.waymarkedtrails.org" target="_blank">Waymarked Trails hiking</a> overlay on top of the topographic basemap.
</span>
</div>
@@ -69,4 +69,4 @@ Global eta tokiko mapa oinarri bilduma handia dago **gpx.studio**-n eskuragarri,
Ezarpen horietan, gainjartzeen opakutasuna ere kudeatu dezakezu.
Erabiltzaile aurreratuentzat, oinarrizko mapa pertsonalizatuak eta gainjartzeak gehi daitezke <a href="https://en.wikipedia.org/wiki/Web_Map_Tile_Service" target="_blank">WMTS</a>, <a href="https://en.wikipedia.org/wiki/Web_Map_Service" target="_blank">WMS</a> edo <a href="https://maplibre.org/maplibre-style-spec/" target="_blank">MapLibre estiloko JSON</a> URLak.
For advanced users, it is possible to add custom basemaps and overlays by providing <a href="https://en.wikipedia.org/wiki/Web_Map_Tile_Service" target="_blank">WMTS</a>, <a href="https://en.wikipedia.org/wiki/Web_Map_Service" target="_blank">WMS</a>, or <a href="https://maplibre.org/maplibre-style-spec/" target="_blank">MapLibre style JSON</a> URLs.
+10 -20
View File
@@ -3,14 +3,14 @@ title: Edizio ekintzak
---
<script lang="ts">
import { Undo2, Redo2, Info, PaintBucket, ArrowRightLeft, FileStack, ClipboardCopy, Scissors, ClipboardPaste, Trash2, Plus } from '@lucide/svelte';
import { Undo2, Redo2, Info, PaintBucket, EyeOff, FileStack, ClipboardCopy, Scissors, ClipboardPaste, Trash2, Maximize, Plus } from '@lucide/svelte';
import DocsNote from '$lib/components/docs/DocsNote.svelte';
</script>
# { title }
Fitxategi-ekintzak ez bezala, edizio-ekintzek hautatutako fitxategien edukia alda dezakete.
Moreover, when the tree layout of the files list is enabled (see [Files and statistics](../files-and-stats)), they can also be applied to [tracks, segments, routes, and points of interest](../gpx).
Are gehiago, fitxategien zerrendaren zuhaitzaren diseinua gaituta dagoenean (Ikusi [Fitxategiak eta estatistikak](../files-and-stats)), ekintzek [arrasto, segmentu eta interesgunetan](../gpx) ere aplikatu daitezke.
Hori dela eta, ekintza hauek alda dezaketen elementuei _fitxategi-elementuak_ izendatzen ditugu.
Ohar zaitez, desegin eta berregin ekintzak ez, beste guztiak fitxategi-elementu hauen alboko menuaren bidez (eskuin-klik) ere erabili daitezke.
@@ -27,6 +27,10 @@ Ireki hautatutako fitxategiaren elementuaren elkarrizketa-koadroa, non bere izen
Ireki Itxuraren elkarrizketa-koadroa, eta bertan mapan hautatutako elementuaren kolorea, opakotasuna eta zabalera alda ditzakezu.
### <EyeOff size="16" class="inline-block" style="margin-bottom: 2px" /> Ezkutatu/erakutsi
Aldatu mapan hautatutako elementuen ikusgarritasuna.
### <Plus size="16" class="inline-block" style="margin-bottom: 2px" /> Arrasto berria
Sortu arrasto berri bat hautatutako fitxategian.
@@ -38,17 +42,6 @@ Gainera, fitxategi bakarra egon behar du hautatuta.
</DocsNote>
### <Plus size="16" class="inline-block" style="margin-bottom: 2px" /> New route
Create a new route in the selected file.
<DocsNote>
Ekintza hau soilik aktibatuko da fitxategien zerrendako zuhaitz-geruza gaituta dagoenean.
Gainera, fitxategi bakarra egon behar du hautatuta.
</DocsNote>
### <Plus size="16" class="inline-block" style="margin-bottom: 2px" /> Segmentu berria
Sortu segmentu berri bat hautatutako arrastoan.
@@ -60,17 +53,14 @@ Gainera, arrasto bakarra egon behar du hautatuta.
</DocsNote>
### <ArrowRightLeft size="16" class="inline-block" style="margin-bottom: 2px" /> Convert to route/track
Convert the selected routes to tracks, or the selected tracks or segments to routes.
Since a route has a single segment, a track with multiple segments is converted into one route per segment.
When the selected files contain only routes, or only tracks, you can also convert all of them at once.
### <FileStack size="16" class="inline-block" style="margin-bottom: 2px" /> Hautatu denak
Gehitu hautapenera uneko hierarkia mailako fitxategi elementu guztiak.
### <Maximize size="16" class="inline-block" style="margin-bottom: 2px" /> Erdiratu
Erdiratu mapan hautatutako fitxategi-elementuak.
### <ClipboardCopy size="16" class="inline-block" style="margin-bottom: 2px" /> Kopiatu
Kopiatu arbelean hautatutako fitxategi-elementuak.
-4
View File
@@ -15,10 +15,6 @@ Fitxategi ekintzen menuan fitxategien eragiketa multzoa dago.
Sortu fitxategi huts berri bat.
### <Plus size="16" class="inline-block" style="margin-bottom: 2px" /> New route file
Create a new file containing an empty [route](../gpx).
### <FolderOpen size="16" class="inline-block" style="margin-bottom: 2px" /> Ireki...
Ireki zure ordenagailuko fitxategiak.
+5 -12
View File
@@ -3,7 +3,7 @@ title: Ikusteko aukerak
---
<script lang="ts">
import { ChartArea, ListTree, Map, Layers2, Coins, Milestone, EyeOff, Maximize, Box, Maximize2 } from '@lucide/svelte';
import { ChartArea, ListTree, Map, Layers2, Coins, Milestone, Box, Maximize2 } from '@lucide/svelte';
import DocsNote from '$lib/components/docs/DocsNote.svelte';
</script>
@@ -19,7 +19,7 @@ Ezkutatu altueren profila mapari lekua egiteko, edo erakutsi uneko hautapena iku
Aldatu [fitxategi zerrendaren](../files-and-stats) zuhaitza ikuspegira.
Diseinu hau aproposa da irekitako fitxategi ugari kudeatzeko, maparen eskuinaldean zerrenda bertikal batean antolatzen baitu.
In addition, the file tree view enables you to inspect the [tracks, segments, routes, and points of interest](../gpx) contained inside the files through collapsible sections.
Fitxategien zuhaitza ikuspegiak fitxategien barneko [ibilbideak, segmentuak eta interesguneak](../gpx) atal gisa erakusten ditu.
### <Map size="16" class="inline-block" style="margin-bottom: 2px" /> Aldatu erabilitako aurreko geruzara
@@ -38,14 +38,6 @@ Unean hautatutako ibilbidean ikusten dira, [koten profila bezala](../files-and-s
Aldatu ibilbidearen noranzko gezien ikusgarritasuna.
### <EyeOff size="16" class="inline-block" style="margin-bottom: 2px" /> Hide/unhide
Toggle the visibility of the selected file items on the map.
### <Maximize size="16" class="inline-block" style="margin-bottom: 2px" /> Center
Center the map on the selected file items.
### <Box size="16" class="inline-block" style="margin-bottom: 2px" /> Aldatu 3D
Aktibatu edo desaktibatu maparen 3D ikuspegia.
@@ -56,6 +48,7 @@ Maparen orientazioa eta okertzea kontrolatzeko, mapa ere arrasta dezakezu <kbd>
</DocsNote>
### <Maximize2 size="16" class="inline-block" style="margin-bottom: 2px" /> Pantaila osoa
### <Maximize2 size="16" class="inline-block" style="margin-bottom: 2px" /> Full screen
Sartu edo irten pantaila osoko moduan.<kbd>F11</kbd> ere saka dezakezu txandakatzeko, edo <kbd>Ihes</kbd> irteteko.
Enter or exit full screen mode.
You can also press <kbd>F11</kbd> to toggle, or <kbd>Escape</kbd> to exit.
@@ -18,7 +18,7 @@ Tresna honen bidez elebazio datuak gehitzen ahal dira ibilbidetan eta [interesgu
<DocsNote>
Altueraren datuak <a href="https://mapterhorn.com" target="_blank">Mapterhorn</a>k ematen ditu.
Haren jatorriari eta zehaztasunari buruz gehiago jakin dezakezu <a href="https://mapterhorn.com/attribution/" target="_blank">dokumentazioan</a>.
Elevation data is provided by <a href="https://mapterhorn.com" target="_blank">Mapterhorn</a>.
You can learn more about its origin and accuracy in the <a href="https://mapterhorn.com/attribution/" target="_blank">documentation</a>.
</DocsNote>
+1 -1
View File
@@ -16,7 +16,7 @@ Tresna honen bidez [ibilbideak (edo segmentuak)](../gpx) erauzi ditzakezu fitxat
<Extract class="text-foreground p-3 border rounded-md shadow-lg" />
</div>
Applying the tool to a file containing multiple [tracks or routes](../gpx) will create a new file for each of the tracks and routes it contains.
Hainbat ibilbide dituen fitxategi bati aplikatuz gero dituen ibilbide bakoitzarentzat fitxategi bana sortuko du.
Era berean, hainbat segmentu duen ibilbide bati aplikatuz gero (fitxategi berean) duen segmentu bakoitzeko ibilbide bana sortuko du.
<DocsNote>
+5 -5
View File
@@ -10,16 +10,16 @@ title: Batu
# <Group size="24" class="inline-block" style="margin-bottom: 5px" /> { title }
To use this tool, you need to [select](../files-and-stats) multiple files, [tracks, segments or routes](../gpx).
Tresna hau erabiltzeko [hautatu](../files-and-stats) behar duzu hainbat fitxategi [ibilbidea, edo segmentuak](../gpx).
- Zure helburua hautaketatik abiatuta etengabeko arrasto bakarra sortzea bada, erabili **Konektatu trazak ** aukera eta balioztatu.
- The second option can be used to create or manage files with multiple [tracks, segments or routes](../gpx).
Merging files (or tracks) will result in a single file (or track) containing all tracks and routes (or segments) from the selection.
- Bigarren aukera bat izan daiteke hainbat [arrasto edo segmentu](../gpx) biltzen duen fitxategiak sortzea edo kudeatzea.
Fitxategiak (edo arrastoak) batzearen emaitza fitxategi (edo arrasto) bakarra sortzea da zeinak hautapenaren arrastoak (edo segmentuak) biltzen duena.
<DocsNote>
Hautatutako elementuak fitxategien zerrendan agertzen diren ordenan batzen dira.
Berrantolatu elementuak arrastatu eta jareginez, behar izanez gero.
Selected items are merged in the order they appear in the files list.
Reorder items by drag-and-drop if needed.
</DocsNote>
+1 -1
View File
@@ -27,6 +27,6 @@ Hautatutako ibilbidea bi zatitan banatzeko, egin klik arrastoko zatiketa markatz
Puntu zehatz batean banatzeko, pasa ezazu maparen arrastoaren gainean.
Artaziak agertuko dira kurtsorearen posizioan, puntu horretan ebaki dezakezula erakutsiz.
You can choose to split the trace into two GPX files, or to keep the split parts in the same file as [tracks, segments or routes](../gpx).
Arrastoa bi GPX fitxategietan zatitzea edo fitxategi berean zatiak [arrastoak edo segmentuak](../gpx) gisa gordetzea aukeratu dezakezu.
<DocsImage src="tools/split" alt="Aukeratutako trazaren gainean kokatzean, zure kurtsorea guraize bihurtuko da." />
+1 -1
View File
@@ -51,7 +51,7 @@ By right-clicking on a file tab, you can access the same actions as in the [edit
As mentioned in the [view options section](./menu/view), you can switch to a tree layout for the files list.
This layout is ideal for managing a large number of open files, as it organizes them into a vertical list on the right side of the map.
In addition, the file tree view enables you to inspect the [tracks, segments, routes, and points of interest](./gpx) contained inside the files through collapsible sections.
In addition, the file tree view enables you to inspect the [tracks, segments, and points of interest](./gpx) contained inside the files through collapsible sections.
You can also apply [edit actions](./menu/edit) and [tools](./toolbar) to internal file items.
Furthermore, you can drag and drop the inner items to reorder them, or move them in the hierarchy or even to another file.
+1 -17
View File
@@ -3,15 +3,13 @@ title: GPX file format
---
<script>
import DocsNote from '$lib/components/docs/DocsNote.svelte';
import { Waypoints, Route, MapPin } from '@lucide/svelte';
import { Waypoints, MapPin } from '@lucide/svelte';
</script>
# { title }
The <a href="https://www.topografix.com/gpx.asp" target="_blank">GPX file format</a> is an open standard for exchanging GPS data between applications and GPS devices.
It essentially consists of a series of GPS points encoding one or multiple GPS traces, and, optionally, some points of interest.
These traces can be stored either as tracks or as routes.
GPX files may also contain metadata, of which the **name** and **description** fields are the most useful for users.
@@ -29,20 +27,6 @@ These are organized in a hierarchical structure, with tracks at the top level.
In most cases, GPX files contain a single track with a single segment.
However, the hierarchy described above allows for more advanced use cases, such as planning multi-day trips with several variants for each day.
### <Route size="16" class="inline-block" style="margin-bottom: 2px" /> Routes
A **route** is a single continuous path made of a sequence of **route points**.
These are mandatory checkpoints, which correspond to the anchor points of the [routing tool](./toolbar/routing).
The points in between are only a suggested path connecting the route points.
Like a track, a route can have a **name**, a **description**, and **appearance properties**.
<DocsNote>
Nowadays, most apps and GPS devices work perfectly well with tracks, even for navigation.
However, some of them only work with routes: in that case, you can [convert](./menu/edit) your tracks to routes, or [create](./menu/file) a route file from the start.
</DocsNote>
### <MapPin size="16" class="inline-block" style="margin-bottom: 2px" /> Points of interest
**Points of interest** (technically called _waypoints_) represent locations of interest to show either on a GPS device or on a digital map.
+10 -20
View File
@@ -3,14 +3,14 @@ title: Edit actions
---
<script lang="ts">
import { Undo2, Redo2, Info, PaintBucket, ArrowRightLeft, FileStack, ClipboardCopy, Scissors, ClipboardPaste, Trash2, Plus } from '@lucide/svelte';
import { Undo2, Redo2, Info, PaintBucket, EyeOff, FileStack, ClipboardCopy, Scissors, ClipboardPaste, Trash2, Maximize, Plus } from '@lucide/svelte';
import DocsNote from '$lib/components/docs/DocsNote.svelte';
</script>
# { title }
Unlike the file actions, the edit actions can potentially modify the content of the currently selected files.
Moreover, when the tree layout of the files list is enabled (see [Files and statistics](../files-and-stats)), they can also be applied to [tracks, segments, routes, and points of interest](../gpx).
Moreover, when the tree layout of the files list is enabled (see [Files and statistics](../files-and-stats)), they can also be applied to [tracks, segments, and points of interest](../gpx).
Therefore, we will refer to the elements that can be modified by these actions as _file items_.
Note that except for the undo and redo actions, the edit actions are also accessible through the context menu (right-click) of the file items.
@@ -27,6 +27,10 @@ Open the information dialog of the currently selected file item, where you can s
Open the appearance dialog, where you can change the color, opacity, and width of the selected file items on the map.
### <EyeOff size="16" class="inline-block" style="margin-bottom: 2px" /> Hide/unhide
Toggle the visibility of the selected file items on the map.
### <Plus size="16" class="inline-block" style="margin-bottom: 2px" /> New track
Create a new track in the selected file.
@@ -38,17 +42,6 @@ Additionally, the selection must be a single file.
</DocsNote>
### <Plus size="16" class="inline-block" style="margin-bottom: 2px" /> New route
Create a new route in the selected file.
<DocsNote>
This action is only available when the tree layout of the files list is enabled.
Additionally, the selection must be a single file.
</DocsNote>
### <Plus size="16" class="inline-block" style="margin-bottom: 2px" /> New segment
Create a new segment in the selected track.
@@ -60,17 +53,14 @@ Additionally, the selection must be a single track.
</DocsNote>
### <ArrowRightLeft size="16" class="inline-block" style="margin-bottom: 2px" /> Convert to route/track
Convert the selected routes to tracks, or the selected tracks or segments to routes.
Since a route has a single segment, a track with multiple segments is converted into one route per segment.
When the selected files contain only routes, or only tracks, you can also convert all of them at once.
### <FileStack size="16" class="inline-block" style="margin-bottom: 2px" /> Select all
Add all file items in the current hierarchy level to the selection.
### <Maximize size="16" class="inline-block" style="margin-bottom: 2px" /> Center
Center the map on the selected file items.
### <ClipboardCopy size="16" class="inline-block" style="margin-bottom: 2px" /> Copy
Copy the selected file items to the clipboard.
-4
View File
@@ -15,10 +15,6 @@ The file actions menu contains a set of pretty self-explanatory file operations.
Create a new empty file.
### <Plus size="16" class="inline-block" style="margin-bottom: 2px" /> New route file
Create a new file containing an empty [route](../gpx).
### <FolderOpen size="16" class="inline-block" style="margin-bottom: 2px" /> Open...
Open files from your computer.
+2 -10
View File
@@ -3,7 +3,7 @@ title: View options
---
<script lang="ts">
import { ChartArea, ListTree, Map, Layers2, Coins, Milestone, EyeOff, Maximize, Box, Maximize2 } from '@lucide/svelte';
import { ChartArea, ListTree, Map, Layers2, Coins, Milestone, Box, Maximize2 } from '@lucide/svelte';
import DocsNote from '$lib/components/docs/DocsNote.svelte';
</script>
@@ -19,7 +19,7 @@ Hide the elevation profile to make room for the map, or show it to inspect the c
Toggle the tree layout for the [file list](../files-and-stats).
This layout is ideal for managing a large number of open files, as it organizes them into a vertical list on the right side of the map.
In addition, the file tree view enables you to inspect the [tracks, segments, routes, and points of interest](../gpx) contained inside the files through collapsible sections.
In addition, the file tree view enables you to inspect the [tracks, segments, and points of interest](../gpx) contained inside the files through collapsible sections.
### <Map size="16" class="inline-block" style="margin-bottom: 2px" /> Switch to previous basemap
@@ -38,14 +38,6 @@ They are displayed for the current selection, like the [elevation profile](../fi
Toggle the visibility of direction arrows on the map.
### <EyeOff size="16" class="inline-block" style="margin-bottom: 2px" /> Hide/unhide
Toggle the visibility of the selected file items on the map.
### <Maximize size="16" class="inline-block" style="margin-bottom: 2px" /> Center
Center the map on the selected file items.
### <Box size="16" class="inline-block" style="margin-bottom: 2px" /> Toggle 3D
Enter or exit the 3D map view.
+1 -1
View File
@@ -16,7 +16,7 @@ This tool allows you to extract [tracks (or segments)](../gpx) from files (or tr
<Extract class="text-foreground p-3 border rounded-md shadow-lg" />
</div>
Applying the tool to a file containing multiple [tracks or routes](../gpx) will create a new file for each of the tracks and routes it contains.
Applying the tool to a file containing multiple tracks will create a new file for each of the tracks it contains.
Similarly, applying the tool to a track containing multiple segments will create (in the same file) a new track for each of the segments it contains.
<DocsNote>
+3 -3
View File
@@ -10,11 +10,11 @@ title: Merge
# <Group size="24" class="inline-block" style="margin-bottom: 5px" /> { title }
To use this tool, you need to [select](../files-and-stats) multiple files, [tracks, segments or routes](../gpx).
To use this tool, you need to [select](../files-and-stats) multiple files, [tracks, or segments](../gpx).
- If your goal is to create a single continuous trace from your selection, use the **Connect the traces** option and validate.
- The second option can be used to create or manage files with multiple [tracks, segments or routes](../gpx).
Merging files (or tracks) will result in a single file (or track) containing all tracks and routes (or segments) from the selection.
- The second option can be used to create or manage files with multiple [tracks or segments](../gpx).
Merging files (or tracks) will result in a single file (or track) containing all tracks (or segments) from the selection.
<DocsNote>
+1 -1
View File
@@ -27,6 +27,6 @@ To split the selected trace into two parts, click on one of the split markers di
To split at a specific point of your choice, hover over the trace on the map.
Scissors will appear at the cursor position, showing that you can split the trace at that point.
You can choose to split the trace into two GPX files, or to keep the split parts in the same file as [tracks, segments or routes](../gpx).
You can choose to split the trace into two GPX files, or to keep the split parts in the same file as [tracks or segments](../gpx).
<DocsImage src="tools/split" alt="Hovering over the selected trace turns your cursor into scissors." />
+1 -1
View File
@@ -51,7 +51,7 @@ En faisant un clic droit sur un onglet de fichier, vous pouvez accéder aux mêm
Comme mentionné dans la [section des options d'affichage](./menu/view), vous pouvez basculer vers une présentation en arborescence pour la liste des fichiers.
Cette disposition est idéale pour gérer un grand nombre de fichiers ouverts, car elle les organise en une liste verticale à droite de la carte.
De plus, la vue en arborescence des fichiers vous permet d'inspecter les [traces, segments, routes, et points d'intérêt](./gpx) contenus dans les fichiers grâce à des sections pliables.
De plus, la vue en arborescence des fichiers vous permet d'inspecter les [traces, segments, et points d'intérêt](./gpx) contenus dans les fichiers grâce à des sections pliables.
Vous pouvez également appliquer les [actions d'édition](./menu/edit) et les [outils](./toolbar) aux éléments intérieurs aux fichiers.
De plus, vous pouvez glisser-déposer les éléments intérieurs pour les réorganiser, ou les déplacer dans la hiérarchie ou même dans un autre fichier.
+1 -17
View File
@@ -3,15 +3,13 @@ title: Format de fichier GPX
---
<script>
import DocsNote from '$lib/components/docs/DocsNote.svelte';
import { Waypoints, Route, MapPin } from '@lucide/svelte';
import { Waypoints, MapPin } from '@lucide/svelte';
</script>
# { title }
Le format de fichier <a href="https://www.topografix.com/gpx.asp" target="_blank">GPX</a> est un standard ouvert pour l'échange de données GPS entre applications et appareils GPS.
Il consiste principalement en une série de points GPS encodant une ou plusieurs traces GPS, et, éventuellement, certains points d'intérêt.
Ces traces peuvent être stockées sous forme de pistes ou de routes.
Les fichiers GPX peuvent aussi contenir des métadonnées, dont les champs **nom** et **description** sont les plus utiles pour les utilisateurs.
@@ -29,20 +27,6 @@ Celles-ci sont organisées en une structure hiérarchique, dont les pistes const
Dans la plupart des cas, les fichiers GPX contiennent une seule piste avec un seul segment.
Cependant, la hiérarchie décrite ci-dessus permet des cas d'utilisation plus avancés, comme la planification de voyages de plusieurs jours avec plusieurs variantes pour chaque jour.
### <Route size="16" class="inline-block" style="margin-bottom: 2px" /> Routes
Une **route** est un chemin continu constitué d'une séquence de **points de passage**.
Ces points de passage correspondent aux points d'ancrage de [l'outil de routage](./toolbar/routing).
Les points intermédiaires entre les points de passage ne sont qu'un chemin suggéré pour les relier.
Comme une piste, une route peut avoir un **nom**, une **description**, et des **propriétés d'apparence**.
<DocsNote>
De nos jours, la plupart des applications et des appareils GPS fonctionnent parfaitement avec les pistes, même pour la navigation.
Cependant, certains d'entre eux ne fonctionnent qu'avec des routes : dans ce cas, vous pouvez [convertir](./menu/edit) vos pistes en routes, ou [créer](./menu/file) un fichier route dès le début.
</DocsNote>
### <MapPin size="16" class="inline-block" style="margin-bottom: 2px" /> Points d'intérêt
Les **points d'intérêt** (techniquement appelés _waypoints_) représentent des lieux d'intérêt à afficher sur un appareil GPS ou sur une carte numérique.
+10 -20
View File
@@ -3,14 +3,14 @@ title: Actions d'édition
---
<script lang="ts">
import { Undo2, Redo2, Info, PaintBucket, ArrowRightLeft, FileStack, ClipboardCopy, Scissors, ClipboardPaste, Trash2, Plus } from '@lucide/svelte';
import { Undo2, Redo2, Info, PaintBucket, EyeOff, FileStack, ClipboardCopy, Scissors, ClipboardPaste, Trash2, Maximize, Plus } from '@lucide/svelte';
import DocsNote from '$lib/components/docs/DocsNote.svelte';
</script>
# { title }
Contrairement aux actions sur les fichiers, les actions d'édition peuvent potentiellement modifier le contenu des fichiers sélectionnés.
De plus, lorsque la présentation en arborescence de la liste de fichiers est activée (voir [Fichiers et statistiques](../files-and-stats)), elles peuvent également être appliquées aux [pistes, segments, routes, et points d'intérêt](../gpx).
De plus, lorsque la présentation en arborescence de la liste de fichiers est activée (voir [Fichiers et statistiques](../files-and-stats)), elles peuvent également être appliquées aux [pistes, segments, et points d'intérêt](../gpx).
Par conséquent, nous appellerons les éléments qui peuvent être modifiés par ces actions des _éléments de fichier_.
Notez que, à part les actions annuler et rétablir, les actions d'édition sont également accessibles via le menu contextuel (clic droit) des éléments de fichier.
@@ -27,6 +27,10 @@ Ouvrir la boîte de dialogue d'information de l'élément de fichier actuellemen
Ouvrir la boîte de dialogue d'apparence, où vous pouvez changer la couleur, l'opacité et l'épaisseur des éléments de fichier sélectionnés sur la carte.
### <EyeOff size="16" class="inline-block" style="margin-bottom: 2px" /> Cacher/afficher
Activer/désactiver la visibilité des éléments de fichier sélectionnés sur la carte.
### <Plus size="16" class="inline-block" style="margin-bottom: 2px" /> Nouvelle piste
Créer une nouvelle piste dans le fichier sélectionné.
@@ -38,17 +42,6 @@ De plus, la sélection doit contenir un seul fichier.
</DocsNote>
### <Plus size="16" class="inline-block" style="margin-bottom: 2px" /> Nouvelle route
Créer une nouvelle route dans le fichier sélectionné.
<DocsNote>
Cette action n'est disponible que lorsque la présentation en arborescence de la liste de fichiers est activée.
De plus, la sélection doit contenir un seul fichier.
</DocsNote>
### <Plus size="16" class="inline-block" style="margin-bottom: 2px" /> Nouveau segment
Créer un nouveau segment dans la piste sélectionnée.
@@ -60,17 +53,14 @@ De plus, la sélection doit contenir une seule piste.
</DocsNote>
### <ArrowRightLeft size="16" class="inline-block" style="margin-bottom: 2px" /> Convertir en fichier route/piste
Convertir les routes sélectionnées en pistes, ou les pistes ou segments sélectionnés en routes.
Comme une route a un seul segment, une piste avec plusieurs segments est convertie en une route par segment.
Lorsque les fichiers sélectionnés ne contiennent que des routes, ou seulement des pistes, vous pouvez également les convertir tous en même temps.
### <FileStack size="16" class="inline-block" style="margin-bottom: 2px" /> Sélectionner tout
Ajouter tous les éléments de fichier du niveau de hiérarchie actuel à la sélection.
### <Maximize size="16" class="inline-block" style="margin-bottom: 2px" /> Centrer
Centrer la carte sur les éléments de fichier sélectionnés.
### <ClipboardCopy size="16" class="inline-block" style="margin-bottom: 2px" /> Copier
Copier les éléments de fichier sélectionnés dans le presse-papiers.
-4
View File
@@ -15,10 +15,6 @@ Le menu des actions sur les fichiers contient un ensemble d'opérations de fichi
Créer un nouveau fichier vide.
### <Plus size="16" class="inline-block" style="margin-bottom: 2px" />Nouveau fichier route
Créer un nouveau fichier contenant une [route](../gpx) vide.
### <FolderOpen size="16" class="inline-block" style="margin-bottom: 2px" /> Ouvrir...
Ouvrir des fichiers depuis votre ordinateur.
+2 -10
View File
@@ -3,7 +3,7 @@ title: Options d'affichage
---
<script lang="ts">
import { ChartArea, ListTree, Map, Layers2, Coins, Milestone, EyeOff, Maximize, Box, Maximize2 } from '@lucide/svelte';
import { ChartArea, ListTree, Map, Layers2, Coins, Milestone, Box, Maximize2 } from '@lucide/svelte';
import DocsNote from '$lib/components/docs/DocsNote.svelte';
</script>
@@ -19,7 +19,7 @@ Masquer le profil altimétrique pour faire de la place pour la carte, ou l'affic
Activer/désactiver la présentation en arborescence pour la [liste de fichiers](../files-and-stats).
Cette disposition est idéale pour gérer un grand nombre de fichiers ouverts, car elle les organise en une liste verticale à droite de la carte.
De plus, la vue en arborescence des fichiers vous permet d'inspecter les [traces, segments, routes, et points d'intérêt](../gpx) contenus dans les fichiers grâce à des sections pliables.
De plus, la vue en arborescence des fichiers vous permet d'inspecter les [traces, segments, et points d'intérêt](../gpx) contenus dans les fichiers grâce à des sections pliables.
### <Map size="16" class="inline-block" style="margin-bottom: 2px" /> Basculer au fond de carte précédent
@@ -38,14 +38,6 @@ Ceux-ci sont affichés pour la sélection courante, comme le [profil altimétriq
Activer/désactiver la visibilité des flèches de direction sur la carte.
### <EyeOff size="16" class="inline-block" style="margin-bottom: 2px" /> Cacher/afficher
Activer/désactiver la visibilité des éléments de fichier sélectionnés sur la carte.
### <Maximize size="16" class="inline-block" style="margin-bottom: 2px" /> Centrer
Centrer la carte sur les éléments de fichier sélectionnés.
### <Box size="16" class="inline-block" style="margin-bottom: 2px" /> Activer/désactiver la 3D
Entrer ou sortir de la vue 3D de la carte.
+1 -1
View File
@@ -16,7 +16,7 @@ Cet outil vous permet d'extraire [des pistes (ou des segments)](../gpx) de fichi
<Extract class="text-foreground p-3 border rounded-md shadow-lg" />
</div>
Appliquer l'outil à un fichier contenant plusieurs [pistes ou routes](../gpx) créera un nouveau fichier pour chacune des pistes et routes qu'il contient.
Appliquer l'outil à un fichier contenant plusieurs pistes créera un nouveau fichier pour chacune des pistes qu'il contient.
De même, appliquer l'outil à une piste contenant plusieurs segments créera (dans le même fichier) une nouvelle piste pour chacun des segments qu'elle contient.
<DocsNote>
+3 -3
View File
@@ -10,11 +10,11 @@ title: Fusionner
# <Group size="24" class="inline-block" style="margin-bottom: 5px" /> { title }
Pour utiliser cet outil, vous devez [sélectionner](../files-and-stats) plusieurs fichiers, [pistes, segments ou routes](../gpx).
Pour utiliser cet outil, vous devez [sélectionner](../files-and-stats) plusieurs fichiers, [pistes ou segments](../gpx).
- Si votre objectif est de créer une seule trace continue à partir de votre sélection, utilisez l'option **Connecter les traces** et validez.
- La deuxième option peut être utilisée pour créer ou gérer des fichiers avec plusieurs [pistes, segments ou routes](../gpx).
Fusionner des fichiers (ou des pistes) résultera en un seul fichier (ou piste) contenant toutes les pistes et routes (ou tous les segments) de la sélection.
- La deuxième option peut être utilisée pour créer ou gérer des fichiers avec plusieurs [pistes ou segments](../gpx).
Fusionner des fichiers (ou des pistes) résultera en un seul fichier (ou piste) contenant toutes les pistes (ou tous les segments) de la sélection.
<DocsNote>
+1 -1
View File
@@ -27,6 +27,6 @@ Pour diviser la trace sélectionnée en deux parties, cliquez sur l'un des marqu
Pour diviser à un point précis de votre choix, survolez la trace sur la carte.
Des ciseaux apparaîtront à la position du curseur, montrant que vous pouvez diviser la trace à ce point.
Vous pouvez choisir de diviser la trace en deux fichiers GPX, ou de créer [une piste, un segment ou une route](../gpx) pour chaque partie dans le même fichier.
Vous pouvez choisir de diviser la trace en deux fichiers GPX, ou de créer une [piste ou un segment](../gpx) pour chaque partie dans le même fichier.
<DocsImage src="tools/split" alt="Survoler la trace sélectionnée transforme votre curseur en ciseaux." />
Loaded 100 of 343 files, more files were not shown because too many files have changed in this diff. Show more