# WaySliceCN **Repository Path**: GSUI5051/WaySliceCN ## Basic Information - **Project Name**: WaySliceCN - **Description**: 将你的运动记录转化成地图上的轨迹和遥测数据图表,并分段分析。 如果你想分担服务器费用,请查看:https://afdian.com/a/gsui5051 - **Primary Language**: Unknown - **License**: MIT - **Default Branch**: master - **Homepage**: https://wayslice.cn - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2026-09-15 - **Last Updated**: 2026-10-22 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README

WaySlice

WaySlice

Telemetry for every way. Sliced.
所经之路,皆成遥测;所成遥测,皆可切析。

English | 简体中文

WaySlice is an open-source, pure-frontend analyzer for GPX/FIT/TCX/KML/KMZ activity data. Whole-track statistics tell you little: what you want to know is how you did on the long climb, on the technical descent, in the last 5 km of the race. WaySlice lets you slice out any sector and analyze it on its own, the way you would read racing telemetry or aviation QAR (flight-data recorder) data: ```text Track → Select Sector → Analyze Sector ```

image

More screenshots

## How to use Intro video: [WaySlice Explainer](https://youtu.be/yBy7z0QS8CA) Click [https://wayslice.cn](https://wayslice.cn) or [local deploy](#getting-started), import your activity data. New to the terms? Start with the [Terminology](#terminology) section — every other section uses those words exactly as defined there. ## Table of contents - [Terminology](#terminology) - [Features](#features) - [Supported formats](#supported-formats) - [Getting started](#getting-started) - [How to analyze a sector](#how-to-analyze-a-sector) - [How to apply for a Tianditu or GeoVis Earth API key](#how-to-apply-for-a-tianditu-or-geovis-earth-api-key) - [Privacy](#privacy) - [Unit systems](#unit-systems) - [Design notes](#design-notes) - [Tests](#tests) - [Contributing](#contributing) - [Project structure](#project-structure) - [Replacing the site icons](#replacing-the-site-icons) - [Improvements](#improvements) - [License](#license) - [Special Thanks](#special-thanks) - [Supporters](#supporters) ## Terminology These terms have fixed meanings in this README and in the app UI. When a word appears with a capital letter, it means exactly this: | Term | Meaning | |------|---------| | Track | One recorded route, loaded from a single file: an ordered list of track points. | | Track point | A single measurement along the track: position, plus elevation, timestamp, heart rate, cadence, power and temperature where the file provides them. | | **Sector** | The core concept of WaySlice. A sector is the part of a track between a start point and an end point that you choose. Boundaries can sit anywhere between two track points (interpolated). The whole track is a sector too: the default "Entire track". Every metric is computed for the current sector. | | Sector handle | The draggable dot that sets one sector boundary: green = start, red = end. Handles appear on both the map and the elevation profile and stay in sync. | | Elevation gain / loss | Total ascent / descent inside the sector, after the 3 m noise filter (see [Design notes](#design-notes)). | | VAM / VDM | Vertical Ascent / Descent Meters per hour: ascent / descent speed in m/h, computed only from the time spent actually climbing / descending. | | Moving time | Elapsed time minus pauses: a pause is speed below 0.5 km/h sustained for 10 s or more. | | GAP | Grade-adjusted pace: the sector pace divided by the Minetti (2002) slope factor — the flat-ground pace at the same effort. | | Effort distance | Horizontal distance + elevation gain ÷ 100: each 100 m of climbing counts as 1 km. | | 3D distance | Distance that follows the terrain: the segment-wise sum of √(horizontal² + vertical²), reported separately from horizontal distance. | | Grade | Steepness at a point or over a stretch: vertical change ÷ horizontal distance, shown in % (positive = uphill, negative = downhill). | | Pace | Time per unit of distance: min/km or min/mi. | | Waypoint | A named place stored in the track file itself (GPX ``, KML `` placemarks). | | Basemap | The map tile source drawn underneath your track. Defaults to the Tianditu street map. | ## Features - **Map + elevation profile.** Drag the sector handles on either surface. Both stay in sync in real time, and a boundary can sit anywhere between two track points, interpolation included. - **Waypoints on map and profile.** GPX (``) and KML (``) waypoints render as pins with name tooltips — toggle them with the pin button below "zoom to track". Each waypoint is matched to its nearest spot on the track and shown in track order; one lying more than 50 m from the track is not shown at all. Hovering a pin marks the matching spot on the elevation profile, and clicking one centers the map on it without changing the zoom level. The pins are keyboard-reachable too: Tab moves between them, Enter or Space acts like a click, and focusing a pin shows its name tooltip just as hovering does. - **Profile wheel zoom (desktop), pinch zoom (touch).** Hover the elevation profile and scroll to zoom its distance/time axis around the cursor; Shift-drag to pan; on touch, one finger pans and a two-finger pinch zooms the same way. Double-click anywhere on the profile — or double-tap on touch — restores the full track. - **Fit to sector.** The focus button at the right end of the profile header zooms the x axis to the selected sector: the sector fills about 82% of the chart with ≈9% of context beyond each handle, centered and clamped to the data. A sector near the full track restores the whole view, and a tiny sector lands at the max-zoom window — the selection itself never moves. - **A profile header that fits its pane.** The elevation-profile toolbar keeps to a single line at every width: as the pane narrows, the action buttons turn icon-only (keeping their tooltips and accessible names), then the lower-priority ones — analysis, overlays, waypoint snap — move into a "More" (⋯) menu as the very same buttons, Distance/Time holds its labels longest, and only a very narrow pane folds the title into its icon. Nothing is squeezed, truncated or unreachable, in any language. - **Auto sector.** The scissors button in the header builds a sector list for the whole track: split at waypoints (CP to CP), by fixed distance (1 km / 5 km / custom — miles when Imperial is active), or by grade into climb/descent stretches. Each row reads number, range, type capsule (climb, descent, flat or mixed, from its relief), then time, pace and heart rate; on narrow screens the row splits in two, the capsule gains its text label and the stats line up with the range's start. Expanding a row reveals that sector's details — 3D distance, effort distance, gain/loss, GAP, VAM/VDM — and moves the main sector onto it. - **Sector metrics.** Horizontal and 3D distance, effort distance, elevation gain/loss, grades, elapsed and moving time, pace, speed, GAP, plus heart rate, cadence, power and temperature when the file carries them. When input data is missing you get *Unavailable*, not 0. - **Dual-variable analysis.** The scatter icon beside the profile controls opens a 2D density heatmap of any valid pair of quantities — heart rate, speed, pace, GAP, cadence, power, temperature, grade, elevation. The analysis follows the currently selected sector. Every quantity's values come from the series the metrics panel itself uses: heart rate, cadence and power are pause-stripped and 5-point smoothed like the panel's averages, temperature stays raw (pauses included), grade takes the panel's 50 m gradient windows, and the speed family is the panel's Maximum Speed series for the sector. Hovering reads the cell's X, Y and relative density. Themes, units and languages apply live, and a dedicated chart module keeps even 100k-point tracks fast and smooth. On touch, two fingers zoom and pan the chart, one finger scrubs the reading, and a double-tap brings the full range back. - **Export.** Download the current sector — its metrics as csv, txt or md, its track points as a GPX file. Exports follow the language and unit system active when you click. - **Metric / Imperial units.** Switch display units at any time. Metric is the default, and your choice is stored locally. - **Multilingual.** Simplified Chinese, Traditional Chinese and English are built in. Browser languages outside this set fall back to the English text, with number formats unaffected. - **Privacy by architecture.** There is no upload code. Your file is read with the File API, then parsed, analyzed and rendered in your browser. - **No build step.** Plain ES modules. Read them, run them, change them. ## Supported formats | Format | Geometry | Elevation | Timestamps | |--------|----------|-----------|------------| | `.gpx` | `` (multi-segment); `` as a degraded route source | ✅ | ✅ | | `.fit` | Garmin FIT binary activity files | ✅ | ✅ | | `.tcx` | TrainingCenterDatabase XML | ✅ | ✅ | | `.kml` | `LineString` + `gx:Track` | ✅ (from coordinates) | ✅ (`gx:Track` / ``) | | `.kmz` | ZIP → KML (native `DecompressionStream`, no library) | ✅ | ✅ | FIT decoding is done by the vendored fit-parser library (MIT) in `vendor/fit-parser/`; TCX is read by a built-in DOM parser. GPX sensor extensions are read namespace-agnostically: Garmin TrackPointExtension (heart rate, cadence, temperature, speed, distance) and the three common power flavors (bare ``, `PowerInWatts`, `ns3:Watts`). Every format funnels into the same track-point model, so sector metrics work identically regardless of the source format. Race websites often publish GPX files whose geometry is a `` route instead of a recorded `` track. WaySlice loads those as a degraded route source: map, distance and elevation analysis work, while the time, speed and heart-rate metrics read Unavailable instead of showing invented numbers. When a file carries both, the recorded track wins and the route is ignored. ## Getting started Any static file server works. There is nothing to build: ```bash # Python python -m http.server 8080 # or Node npx serve . ``` Then open and drop a GPX/FIT/TCX/KML/KMZ file. > Opening `index.html` directly via `file://` will not work because browsers block ES-module > imports on `file://` URLs. ### How to analyze a sector 1. Import your track. The whole track is selected by default. 2. **Drag the green/red sector handles** on the map or the elevation profile. You can also click the track or profile to move the nearest boundary, or drag on the profile to select a new range. On touch, one finger pans the zoomed profile and a pinch zooms — sector changes are made with the handles. 3. Every change re-computes the sector instantly: distance, 3D distance, gain/loss, grades, pace/speed, fastest/slowest kilometer. 4. Keyboard: focus a handle and use the arrow keys (Shift = ×10, Home/End = jump). `Esc` closes menus. 5. Touch devices use cooperative gestures: one finger scrolls the page, two fingers pan and zoom the map (a hint appears on the map). 6. Ready-made sectors: the `Auto sector` button in the header builds a sector list for the whole track — by waypoint, fixed distance or grade. ### How to apply for a Tianditu or GeoVis Earth API key Map tiles come from two providers, Tianditu and GeoVis Earth, and both need an API key. The keys built into the repository are for online previews only: individual developer quotas are limited. Tianditu `tk` shares one daily quota between the site and every self-hosted deployment, and GeoVis Earth `token` is bound to the main site's domain whitelist, so local deployments get no tiles. For self-hosting, apply for your own keys. Both are free for individual developers: - **Tianditu**: register at [tianditu.gov.cn](https://www.tianditu.gov.cn) and apply as a developer (individual verification), then create a "Browser"-type application in the [console](https://console.tianditu.gov.cn/api/key) to get a `tk`. Replace the `tk=` value at the end of the 4 Tianditu URLs in `js/map/sources.js` — the street and satellite sources each carry a base and a label URL. - **GeoVis Earth**: register at the [GeoVis Earth open platform](https://open.geovisearth.com), verify as an individual developer, and create an application to get a `token`. Replace the `token=` value in the 3 GeoVis URLs in `js/map/sources.js` — the satellite source carries a base and a label URL. Tokens support a domain whitelist; when debugging on localhost only, make sure it does not lock out your local address. ## Privacy - Your track is processed **only** in your browser: parse → analyze → render, all local. - The only network requests are **map tiles** from the basemap you choose. Your track data is never transmitted, and there is no analytics. - Theme, language, basemap and unit choices are stored in `localStorage` on your device only. - This statement is also available in the app: **About → Privacy** in the `Settings` drawer (gear button in the header). ## Unit systems WaySlice defaults to **Metric** and also supports **Imperial**. Open the `Settings` drawer from the gear button in the header — units live next to language and theme: | Display | Metric | Imperial | |---|---|---| | Long distance | km | mi | | Short distance / elevation | m | ft | | Speed | km/h | mph | | Pace | min/km | min/mi | | Gradient | % | % | All parsing and calculations stay in SI units (meters, meters/second, seconds/kilometer). Switching units only re-formats the numbers you see: the file is not re-parsed, the sector is not recalculated, and the track geometry does not change. The preference is stored as `wayslice-units` and survives reloads. Language and units are independent settings, so any language works with either system. ## Design notes - **3D distance** is computed segment-wise as `√(horizontal² + vertical²)` and reported separately from horizontal distance. - **Effort distance** = horizontal distance + elevation gain ÷ 100 (100 m of climbing counts as 1 km). - **Average grade** is the sector's accumulated ascent divided by horizontal distance, not the mean of per-point grades; a sector that only descends has no ascent to average and shows the unavailable marker (—). Max/min grades use ~50 m windows so GPS noise cannot fake a record slope. - **Elevation gain/loss** apply a 3 m threshold filter: elevation change accumulates until it passes ±3 m and only then counts, so sub-3 m noise never inflates the totals. - **VAM/VDM** divide gain/loss by the time actually spent climbing/descending (the filtered elevation trend), not by the whole sector time. - **Moving time** counts segments outside pauses (speed below 0.5 km/h sustained for 10 s or more). - **Average speed and average pace** are computed from per-segment moving speeds and cleaned like the profile curve: with recorded speeds a 5-point sliding window dilutes the spike into its neighborhood; without recorded speeds only the 3σ rule applies. **Maximum speed** reads the profile's cleaned per-point curve, so the list always matches the chart. - **Average GAP** divides each segment pace by the Minetti (2002) slope factor (clamped to ±45 % grade) before averaging: the flat-ground pace at the same effort. In the profile, speed/pace/GAP share one overlay slot. - **Profile speed plots** clean spikes by source: tracks with recorded speeds first cross-check every reading against the concurrent dd/dt (readings more than 50 % above it are dropped), then smooth the rest with a 5-point sliding window (each value = the mean of up to five points centered on it); tracks without recorded speeds derive speeds from position deltas and apply the 3σ rule directly. Rest-stop zeros are data and take part in the window mean like any other value. - **Display simplification** (Douglas–Peucker for the map, per-pixel min–max sampling for the profile) never touches the original points. Metrics always run on the full data. - **Profile zoom** works down to a 100 m window in distance mode and 1 minute in time mode; touch devices zoom with a two-finger pinch — there is no wheel. Fit to sector shares that floor: it never zooms deeper than the wheel/pinch max and never reveals blank space past the track ends. - Large tracks (100k+ points) work fine. While you drag a handle, the boundary lookup starts from the previous match and expands its search window as needed. ## Tests Open `tests/index.html` on the same server: ```text http://localhost:8080/tests/ ``` The suite covers distance math, interpolation between points, gradient windows, time metrics, GPX/FIT/TCX/KML/KMZ parsing (including malformed files and archives, plus the GPX power and sensor-extension flavors), the theme preference matrix (system/manual × OS light/dark), the language fallback chain, and Metric/Imperial conversions (including pace rounding, VAM, persistence and unit-independent gradients), plus the sector export (csv/txt/md/gpx content and file names). ## Contributing ### Project structure ```text index.html shell + theme boot script (no flash of wrong theme) icons/ site icon assets (favicon.svg + PNG/ICO sizes, preview page) site.webmanifest PWA install metadata (name, theme colors, icons) css/ design tokens (light/dark), base, layout, components js/ parsers/ GPX / FIT / TCX / KML / KMZ → one unified track-point array geo/ haversine + 3D distance, interpolation, simplification metrics/ sector metrics (pure functions, independently testable) sector/ sector selection state (single source of truth) map/ Leaflet view, basemap catalog, sector handles charts/ canvas charts (elevation profile, dual-variable analysis) theme/ system / light / dark with live OS sync language/ language core + lang-*.js packs + template units/ Metric / Imperial preference, SI display conversions, localized unit labels ui/ metrics panel, settings drawer, auto split, menus, sheets, upload, icons core/ tiny event bus + stores utils/ locale-aware formatting (Intl) tests/ browser-runnable test suite (tests/index.html) vendor/fit-parser/ vendored fit-parser toolkit (FIT decode, MIT) + buffer shim ``` Calculation logic lives apart from the UI: `computeSectorMetrics()` and the other metric functions never touch the DOM, and the test suite covers them. ### Replacing the site icons All site icons derive from one source file, `icons/favicon.svg` — a 1024×1024 rounded tile with two orange mountain ridges on a dark gradient, a white GPX track line, and green/red dots matching the in-app sector handles. From it, regenerate `favicon.ico`, the 16–512 px PNG sizes, `apple-touch-icon.png`, the Android Chrome icons and `site.webmanifest`. Search engines have requirements of their own (Google needs a 48 px+ PNG, the manifest, and crawlable robots.txt — hence the `robots.txt` in the repo root). `icons/icon-preview.html` shows every size locally. Inside the app, the header and empty state reuse the `icons/favicon.svg` brand mark; the functional icons come from the Lucide icon library (ISC license) and are collected in `js/ui/icons.js`. The header's Gitee link is the one exception: it loads the Gitee logo files in `icons/` as images (the Gitee marks are trademarks of their respective owner, used only to link to this repository). To swap in your own mark, edit `icons/favicon.svg`, regenerate the other sizes from it, and keep the links in `index.html` and `site.webmanifest` in sync. ## Improvements - Better spike-cleaning algorithm when considering GPS drift and sparse-sampling. - Better slope-detection algorithm for auto sector. (climb/descend/flat/mixed) - Optional elevation enhancement from external DEM services (opt-in only — the default stays fully local) ## License Released under the [MIT License](LICENSE). Free for commercial and private use — please keep the map providers' attributions intact: - Leaflet - Basemap tiles from [Tianditu](https://www.tianditu.gov.cn) (National Geomatics Center of China) and the [GeoVis Earth](https://open.geovisearth.com) open platform, approval numbers shown in the map attribution - Icons by Lucide ## Special Thanks - The project is loosely inspired by the GPS data analysis in trail running: how to interpret it article from Trail Running Movement. Thanks to everyone who contributed their GPX, TCX, FIT and other data used in development and testing of WaySlice. - Ken Zemach for his Bruksleden 100 Miler (Sweden) GPX track. - polyvertex for his FIT files. - ToolElewaut for Tour de France 2020 - 2023 TCX files. - tingard for his TCX files. - pherris for his long distance riding TCX file with power. - Tommi/山人 for his Kai Kung Leng - Tai To Yan - Tai Mo Shan hiking GPX track in Hong Kong. ## Supporters Thanks to everyone who helps keep WaySlice maintained and running.