# 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
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
```

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.