gpx-stabilizer
v0.7.0
Published
Clean GPS noise/outliers out of a GPX recording — zero runtime dependencies, a pluggable module pipeline with core/ski modes.
Maintainers
Readme
gpx-stabilizer
Clean noise out of a GPX recording, and view the result. Zero runtime dependencies, Node ESM.
Two features: stabilize (drop the noise points and write a cleaned .gpx) and a viewer (render
one or more tracks to a standalone, browser-viewable HTML/SVG document).
Scope: "stabilize" currently means noise/outlier removal (survivors land at ~1 Hz; positions are not yet smoothed/repositioned). Trajectory smoothing is the next tier.
Part of the gpx-stabilizer monorepo; to pull GPS out of a
GoPro video, see the companion gpx-from-gopro package.
Install
npm install gpx-stabilizerStabilize
import { readGpx, stabilizeGpx, stabilize } from "gpx-stabilizer";
// file → cleaned file (preserves track metadata)
stabilizeGpx("in.gpx", "out.gpx");
// or work with points directly
const { segments } = readGpx("in.gpx");
const clean = stabilize(segments[0]); // array of { lat, lon, ele, time }, noise removedNoise removal is a pipeline of built-in modules (all on by default): dequantizeTime (spread
duplicate-second timestamps), noTime, oversample, outlier (position / acceleration spikes),
stray (points far outside the track's spatial bulk — cold-start / wild-fix teleport clusters that
outlier's single-point detour misses), activity, drift (stationary satellite drift),
despike (heading + segment-length spikes), and fixQuality (non-3D fixes the GPS chip itself
distrusts).
activity classifies each point into the human-movement activities whose kinematic envelope it fits
(walking · running · cycling · driving · rail · skiing · flight) and drops anything no enabled activity
can explain. Enable a special activity:
stabilizeGpx("in.gpx", "out.gpx", { activities: ["walking", "skiing", "flight", "skydive"] });Modes (core / ski)
A mode bundles per-use-case thresholds. core (the default) is tuned for general tracks: its
despike keeps real sharp corners (walking/driving turn legitimately up to ~150°), so it only removes
clear spikes. ski opts into ski-tuned despike thresholds (which also catch the gentler-carve
spikes), the ski-only carve signal, the kink overlay, and the lift machinery —
segment/liftConfirm/liftSnap/liftBoardingEle/segmentBoundaryEle/tangleSnap — plus the
gradeBound slope-stable ele rewrite. Select it via the CLI --mode ski, or programmatically:
stabilizeGpx("in.gpx", "out.gpx", { mode: "ski" });CLI
No install needed:
npx gpx-stabilizer FILE.gpx [...] # → <name>.stabilized.gpx per input
npx gpx-stabilizer FILE.gpx [...] --html [out.html] # → one interactive HTML viewer
npx gpx-stabilizer FILE.gpx [...] --png # → one PNG per input (needs @resvg/resvg-js)Once installed (npm install [-g] gpx-stabilizer), drop the npx prefix and just run
gpx-stabilizer ....
Options: --out DIR · --mode core|ski (default core; ski = ski-tuned despike + carve + kink +
lift confirm/reconstruction + slope-stable ele) ·
--config FILE.json (a full analyze config) · --disable name,... (skip built-in modules).
Viewer
import { readGpx, toHtmlFiles } from "gpx-stabilizer";
import { writeFileSync } from "node:fs";
const files = ["a.gpx", "b.gpx"].map((name) => ({ name, points: readGpx(name).segments.flat() }));
writeFileSync("view.html", toHtmlFiles(files)); // open in a browserEach track renders as one full-viewport panel (sticky title + legend) that zooms to fit the real
window via CSS. A track shows as markers by default; pass { width } for a line, or
pointColor/size to draw both.
License
MIT
