Telemetry for every way. Sliced.
English | 简体中文 | 繁體中文 | 日本語 | 한국어
Français | Deutsch | Español | Italiano
WaySlice Terra 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 Terra 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:
Track → Select Sector → Analyze Sector
Click https://waysliceterra.com or local deploy, import your activity data.
New to the terms? Start with the Terminology section — every other section uses those words exactly as defined there.
- Terminology
- Features
- Supported formats
- Getting started
- Privacy
- Unit systems
- Design notes
- Tests
- Contributing
- Improvements
- License
- Special Thanks
- Supporters
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 Terra. 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). |
| 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 <wpt>, KML <Point> placemarks). |
| Basemap | The map tile source drawn underneath your track. Defaults to OpenStreetMap. |
| Terrain (DEM) | The elevation model that lifts the map into 3D terrain. Independent from the basemap — any basemap pairs with the DEM. Defaults to Mapterhorn (Copernicus DEM data). |
| Road network overlay | Roads, place names, park areas and names, and natural landmarks (peaks, saddles, springs, waterfalls…) drawn over satellite imagery (the "hybrid" view); on every other basemap the button carries the natural landmarks and the park names — and on the standard raster maps in the 3D view also the place names |
| (OSM, OpenTopoMap, CyclOSM ride the 3D view only, since their tiles draw natural markers of their own). |
- 3D terrain view. A single button at the map's top-left — or a right-click on empty map area on desktop — switches between the flat north-up map and a DEM terrain view, its label always naming the mode you switch to. Track, sector handles and waypoints drape onto the terrain, and the layer toggles under the switch (hillshade, contour lines) work in either view mode — one shared pair, remembered across visits (contours on and hillshade off by default). The camera keeps your position across the switch. DEM tiles come from a keyless provider, contour lines are generated in the browser from that same DEM with round elevations that follow your unit system — on any basemap, contour-less ones included — and the basemap stays independent.
- Hybrid satellite view. On a satellite basemap the map's top-left stack gains a road-network toggle (the map icon between the 2D/3D switch and hillshade): it draws roads, road names, place names, park areas with their names and natural landmarks over the imagery so the picture stays readable as a map. The overlay works in both view modes, its labels and lines draping the 3D terrain like everything else. Natural landmarks — peaks, saddles, volcanoes, cave entrances, springs, waterfalls, rocks and stones — are labeled with their name and an elevation that follows your unit system, appearing progressively as you zoom in. Park areas get a translucent green wash that stays under the roads. It is off by default and remembers the choice — leaving a satellite basemap tears the overlay down, and switching back restores it the way you left it. On every non-satellite basemap the same button becomes the natural-landmarks toggle (the map-pinned icon): the eight landmark classes and the park name labels ride on top — and on the standard raster maps in the 3D view the place names join them — lifted from the planet source the OpenFreeMap styles already load, or from the app's own keyless planet source on the raster maps; the standard raster maps (OSM, OpenTopoMap, CyclOSM) carry it in the 3D view only, since their tiles draw natural markers of their own — on by default, with its own remembered choice.
- 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 (
<wpt>) and KML (<Point>) 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 work from the keyboard too: Tab moves between them, Enter or Space centers the map, and focusing a pin shows its name tooltip the same way 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 in 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 — distance, 3D distance, effort distance, gain/loss, grade, GAP, VAM/VDM, average/maximum heart rate and the heart-rate zones — 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, and every quantity's values come from the metrics panel's own mechanism: heart rate, cadence and power drop paused readings and get the panel's 5-point smoothing, temperature stays raw (paused readings included), grade uses 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. English, 日本語, 한국어, 简体中文, 繁體中文, Français, Deutsch, Español and Italiano are built in. Each additional language is a single data file (see Contributing).
- 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.
| Format | Geometry | Elevation | Timestamps |
|---|---|---|---|
.gpx |
<trk><trkseg><trkpt> (multi-segment); <rte><rtept> as a degraded route source |
✅ | ✅ |
.fit |
Garmin FIT binary activity files | ✅ | ✅ |
.tcx |
TrainingCenterDatabase XML | ✅ | ✅ |
.kml |
LineString + gx:Track |
✅ (from coordinates) | ✅ (gx:Track / <when>) |
.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 <power>, 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 <rte> route instead
of a recorded <trk> track. WaySlice Terra 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.
Try it: https://waysliceterra.com
Any static file server works. There is nothing to build:
# Python
python -m http.server 8080
# or Node
npx serve .Then open http://localhost:8080 and drop a GPX/FIT/TCX/KML/KMZ file.
Opening
index.htmldirectly viafile://will not work because browsers block ES-module imports onfile://URLs.
- Import your track. The whole track is selected by default.
- 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.
- Every change re-computes the sector instantly: distance, 3D distance, gain/loss, grades, pace/speed, fastest/slowest kilometer.
- Keyboard: focus a handle and use the arrow keys (Shift = ×10, Home/End = jump).
Esccloses menus. - Touch devices use cooperative gestures: one finger scrolls the page, two fingers pan and zoom the map (a hint appears on the map).
- Ready-made sectors: the
Auto sectorbutton in the header builds a sector list for the whole track — by waypoint, fixed distance or grade.
- Your track is processed only in your browser: parse → analyze → render, all local.
- The only network requests are map and terrain (DEM) tiles from the basemap and terrain providers you choose. Your track data is never transmitted, and there is no analytics.
- Theme, language, basemap, terrain and unit choices are stored in
localStorageon your device only. - This statement is also available in the app: About → Privacy in the
Settingsdrawer (gear button in the header).
WaySlice Terra 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.
- 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 (an evenly spaced sample of the recorded track for the map; the profile renders the complete raw series — no per-pixel sampling) 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.
Open tests/index.html on the same server:
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 and the degraded <rtept> route source), 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).
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/ MapLibre view, basemap catalog, sector handles, 3D terrain + 2D/3D view mode
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.
The translation system has no framework behind it: a language is one data file. If you can edit a JavaScript file, you can translate the app.
- Copy
js/language/lang-template.jstolang-xx.js(xx= a BCP 47 code such ases,pt-BR). - Translate the values on the right side. Keep the keys and
{placeholders}as they are, and keep technical terms consistent (Sector, 3D Distance, Elevation Gain/Loss, Grade, Pace, VAM). - Add one entry to the catalog in
js/language/langs.js— language code, native name and a lazy loader. Packs load on demand: at boot only the visitor's language plus English are fetched. - Open the app, switch to your language, and check the console. WaySlice Terra validates the loaded packs against the English keys and warns about missing or unknown ones.
- Submit a pull request.
Missing keys fall back to English (and then to the key itself), so the UI never shows
undefined. Language, theme and basemap choices survive reloads in localStorage.
The basemap catalog lives in one file: js/map/sources.js. The map menu,
the settings sheet, persistence and tile-layer creation all read from it — adding or editing a
source needs no UI code.
Add a source — append an object to MAP_SOURCES:
{
id: 'esri-topo', // unique; doubles as the persistence key
labelKey: 'srcEsriTopo', // translation key of the display name
group: 'outdoor', // street | outdoor | satellite | minimal
url: 'https://server.arcgisonline.com/ArcGIS/rest/services/World_Topo_Map/MapServer/tile/{z}/{y}/{x}',
maxZoom: 19,
attribution: 'Tiles © Esri',
},| Field | Meaning |
|---|---|
id |
Unique id and the persistence key. Changing it acts as a new source (saved selections fall back to the default); removing an entry also falls back safely. |
labelKey |
i18n key of the display name. |
group |
One of street, outdoor, satellite, minimal. |
url |
Tile template: {z} {x} {y}, optional {s} (needs subdomains) and {r} (retina). Mind the provider's coordinate order — Esri uses {z}/{y}/{x}. |
overlayUrl |
Optional transparent label overlay drawn above the base tiles. |
maxZoom |
Highest zoom the provider serves. |
attribution |
Legally required by OSM, OpenTopoMap, OpenFreeMap, EOX and Esri — keep it. |
subdomains, crossOrigin, hintKey |
Optional: subdomain rotation; crossOrigin: false for tile servers that send no CORS headers; grey hint line in the selector. |
Then add the display name to every language pack — srcEsriTopo: 'Esri Topo', in
lang-en.js, lang-zh-CN.js, lang-zh-TW.js, lang-fr.js, lang-ko.js,
lang-ja.js, lang-de.js,
lang-es.js, lang-it.js and lang-template.js. The
language-completeness test stays red until all packs have the key; that is on purpose.
Edit a source by changing url, maxZoom, names or attribution in place. For a new
group, add it to the GROUP_ORDER array, use it on your sources, and add a group<Name> key
(e.g. groupTopo). Services that need an API key (Maptiler, Mapbox) work with the key baked
into url, but do not commit your own key. Prefer HTTPS tiles: on HTTPS-hosted pages browsers
block plain-HTTP tiles or force them through HTTPS. To verify: refresh, open the
Map menu, and check tiles, max zoom and attribution.
3D terrain has its own provider seam, deliberately separate from the basemap:
js/map/providers/terrain.js holds the DEM catalog,
js/map/terrain.js hangs the raster-dem source and hillshade on the map,
and js/map/viewMode.js owns the 2D/3D state machine
(one map button labeled with the destination mode, plus the desktop right-click on empty map area). The default provider is
Mapterhorn — keyless, registration-free Copernicus DEM data, terrarium-encoded 512 px WebP
tiles up to zoom 12. Adding a provider means appending one entry to
TERRAIN_PROVIDERS; no China providers ship in v1, but this seam is their swap point.
Contour lines are generated in the browser by the vendored
maplibre-contour plugin, from the same Mapterhorn DEM as the 3D terrain — one shared tile cache,
the isoline computation in a Web Worker. js/map/providers/contours.js
holds four hand-tuned recommendation ranges (metric / imperial × 2D / 3D — the flat view and the 3D view tune their own ranges independently, and the contours are the primary terrain information in the flat view), and js/map/terrain.js renders the
line layers over the basemap, under the track. Metric and imperial are separate systems: round
meter elevations and round foot elevations — a contour is never converted across units. The
interval itself is selected per view: after each pan settles, js/map/terrain-analysis.js
samples the DEM already in view and adjusts each zoom band's target, and the nearest value of
the unit system's fixed candidate ladder wins — 20/50/100/200/500/1000 m or
50/100/200/500/1000/2000 ft, never anything in between. Gentle high plateaus and flat plains
read the coarser intervals, strongly dissected mountains keep fine ones at any altitude. Lines follow the MapLibre official contour example (index
1 px, minor 0.5 px), and index contours carry elevation labels that run along the line in each
system's own paper-map convention — bare elevation numbers in metric, feet
flagged with a prime (3300') in imperial, USGS-style. While the contour toggle is on, the DEM credit shows in the map's attribution control.
The catalog's satellite basemaps are imagery-only, so a "hybrid" view needs its own layer stack:
js/map/hybrid.js hangs a vector source on OpenFreeMap's keyless planet
tiles (the same provider family the vector basemaps and the label glyphs come from) and draws a
hand-picked layer set over the imagery — road lines with casings, dashed tracks and trails,
road-name and place-name labels. Since the 2026-10-09 scope extension it also draws a translucent
park-area fill with its standing name labels (the park labels ride the natural face on every
non-satellite basemap too). Eight natural landmark point classes ride the same stack:
peaks, saddles and volcanoes come straight from the planet tiles' mountain_peak layer, while cave
entrances, springs, waterfalls, rocks and stones are fetched per viewport from the public Overpass API once the
view zooms in (waterfalls ride their waterway=waterfall tag; a failed fetch merely leaves those five out). Labels pair the localized name with an
integer elevation in the app's unit system; icons appear from z10, names a tier later, elevations
last, so the dense small classes never clutter low zooms. No buildings, no POI labels apart from
the natural landmarks, no second
map state: the overlay is
strictly satellite-dependent (pruned the moment another basemap is picked), toggles by layer
visibility rather than rebuilds, re-hangs after every style swap through the same mapView rehang
seam as the terrain module, and loads nothing until you first enable it on a satellite basemap.
Its layers sit between the imagery and the hillshade/contour stack, so the terrain modules keep
their documented order, and the track always draws on top.
Outside the satellite group the button wears a second face: the eight landmark classes, the
park names and — on the standard raster maps in the 3D view — the place names, persisted under
its own wayslice-natural-overlay key and ON by default on a first
visit, independent of the satellite roads toggle. On the OpenFreeMap vector styles the peaks
lift from the planet source the style already loads; on the raster basemaps the face
provisions the app's own keyless planet source for the lift (an invisible feeder layer makes
MapLibre fetch its tiles). The standard raster maps (OSM, OpenTopoMap, CyclOSM) draw natural
markers of their own into the tiles, so they carry the overlay in the 3D view only — in 2D
the button is disabled and nothing mounts; a catalog entry can also opt out entirely
(naturalOverlay: false).
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 GitHub link is the one exception: it loads the
GitHub logo files in icons/ as images (trademarks of GitHub, Inc., 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.
The existing translations (简体中文, 繁體中文, 日本語, 한국어, Français, Deutsch, Español, Italiano) are your examples. To add one:
- Copy this
README.md(or any existing translation) toREADME.xx.md(xx= a BCP 47 code, e.g.README.es.md). - Translate the prose. Keep the structure, section order, tables, code blocks and file paths unchanged, so all versions stay easy to compare and maintain.
- Update the language switcher line under the title in every README: add your language as a
link elsewhere, and keep the format
A | B | C | D. In your own version, your language is the plain-text entry. - Submit a pull request.
- 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)
Released under the MIT License. Free for commercial and private use — please keep the map providers' attributions intact:
- MapLibre GL JS (BSD-3-Clause)
- © OpenStreetMap contributors (ODbL)
- OpenTopoMap (CC-BY-SA), CyclOSM
- OpenFreeMap, EOX, Esri World Imagery basemaps
- Mapterhorn terrain (DEM) tiles, maplibre-contour (BSD-3-Clause)
- Icons by Lucide (ISC)
- The project is loosely inspired by the GPS data analysis article from Trail Running Movement.
Thanks to everyone who contributed their GPX, TCX, FIT and other data used in development and testing of WaySlice Terra.
- 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.
Thanks to everyone who helps keep WaySlice Terra maintained and running.
