Black and white top view of a forest

With today’s presence of wearables and tracking Apps, there is a good chance that any outdoor activity produces data that can be downloaded as a GPX file. While there are various programs for displaying such files, a tool for performing quick modifications on such files was missing – until now.

The command-line tool GPSplit aims to solve this issue! It offers commands for splitting, merging, filtering, and simplifying GPX files.

GPSplit logo, containing a stylized track segment with multiple circular waypoints.

GPSplit is available at GitHub . Its functionality will be described in the sections below.

Background#

GPX is a XML schema that defines a list of GPS coordinates (waypoints) that can be summarized in routes or track segments. The schema allows the definition of multiple routes, tracks, or individual waypoints within a single file. Additionally, a track can hold multiple track segments.

The following example visualizes the GPX format. It holds a GPX object with one track. The track contains two segments with three and two waypoints, respectively.

<?xml version='1.0' encoding='UTF-8'?>
<gpx version="1.1" xmlns="http://www.topografix.com/GPX/1/1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.topografix.com/GPX/1/1 http://www.topografix.com/GPX/1/1/gpx.xsd">
  <metadata>
    <name>A GPX Track</name>
    <author><name>abzicht</name></author>
  </metadata>
  <trk>
    <name>Track #1</name>
    <trkseg>
      <trkpt lat="49.842945" lon="9.902103">
        <ele>262.454418</ele>
        <time>2025-08-30T13:22:00.000Z</time>
      </trkpt>
      <trkpt lat="49.843061" lon="9.902099">
        <ele>262.454418</ele>
        <time>2025-08-30T13:22:40.000Z</time>
      </trkpt>
      <trkpt lat="49.844422" lon="9.902045">
        <ele>260.863955</ele>
        <time>2025-08-30T13:23:20.000Z</time>
      </trkpt>
    </trkseg>
    <trkseg>
      <trkpt lat="49.844340" lon="9.901424">
        <ele>259.739450</ele>
        <time>2025-08-30T13:24:00.000Z</time>
      </trkpt>
      <trkpt lat="49.844110" lon="9.899744">
        <ele>257.148924</ele>
        <time>2025-08-30T13:24:40.000Z</time>
      </trkpt>
    </trkseg>
  </trk>
</gpx>

In this example, it would be straightforward to, e.g., merge the two segments into one. However, it is not untypical for GPX files to become unreasonably large. Modifying such files manually is not only tiresome, but also prone to error.

The Solution#

The idea for GPSplit was born when I found myself tackling a GPX file that spans recordings of over two years, with 134,443 waypoints that translate to 1,613,334 lines. The file defines two tracks with one track segment each(!).

The GIF below shows those two tracks – and the postprocessing after I split those into reasonable chunks using GPSplit.

Example showing before and after treatment with gpsplit of a huge collection of tracks

The command(s) I used for splitting the two tracks into over 100 individual ones is the following:

gpsplit -i ./my-recording.gpx split --distance 5000 --duration 8h \
    --pause-split 200,1h | gpsplit filter --trim 50 | \
    gpsplit -o ./gpx remove --simplify 40 \
    --min-radius 1000 --min-points 80

The purpose of the utilized flags will be described in the features section below.

For now, note that GPSplit expects to be provided GPX files via -i or via STDIN. The output of GPSplit is written to files or folders defined via -o. If this flag is not present, all output is written to STDOUT. This property allows multiple gpsplit commands to be chained together, as shown above.

Features#

Besides straightforward tasks such as analyzing and merging GPX data, the more interesting features offered by GPSplit are its options for filtering and splitting files / tracks / track segments based on various conditions.

In the GIF shown above, one of the main goals for cleaning up the file was to split segments whenever a pause was detected (min. number of consecutive waypoints lie within a given radius for a given min. duration) and to trim the start and end of segments. With the following list of features offered by GPSplit, this is easily achieved.

For simplicity, GPSplit assumes waypoints to be contained within track segments. Alternative options such as routes are not (yet) supported.

Merge#

gpsplit merge \
    --merge-segments \
    --merge-tracks \
    --merge-files

The merge command offers the unconditional merging of multiple files, tracks, and track segments.

Filter#

gpsplit filter \
    --trim=100 \
    --trim-start=100 \
    --trim-end=100

The filter command allows waypoints to be removed at the start and/or end of a track segment. This is useful, if the start/stop of a recording does not match the begin/end of the activity that was supposed to be recorded. The command removes all waypoints that lie within a customizable distance to the first/last waypoint of the segment.

Split#

gpsplit split \
    --tracks \
    --segments \
    --distance=10000 \
    --duration=5h \
    --pause-split=100,30m

This command allows tracks with multiple track segments to be split into multiple tracks with one track segment each. Likewise, files with multiple tracks can be split into multiple files with one track each.

In addition, track segments can be split, if consecutive waypoints exceed a given distance or duration. Furthermore, the pause-split option allows track segments to be split, if waypoints lie within a given radius for a given min. duration.

Remove#

gpsplit remove \
    --simplify=50 \
    --remove-stops \
    --min-points=100 \
    --min-radius=100 \
    --min-distance=100 \
    --min-duration=1h \
    --max-duration=1d

The remove command is capable of removing entire track segments, if they

  • undercut a given number of waypoints / radius / distance / duration, or
  • exceed a given duration.

It also offers options for removing waypoints, in order to

  • remove pauses where no significant movement takes place, or
  • simplify the path, reducing the number of waypoints.

Analyze#

gpsplit analyze \
    --count

The analyze command prints (statistical) information of GPX files, tracks, and track segments. By using the counts option, only the number of tracks, track segments, and waypoints is printed.

Library#

GPSplit can be used as a Go library (github.com/abzicht/gpsplit/gpxtransform). It is, itself, built on top of gpxgo . It can be used to apply the features described above directly in Go and can also be extended with, e.g., custom filters or splitting rules.

The github.com/abzicht/gpsplit/gpxtransform package provides functions for transforming GPX files, tracks, and track segments. Transformations are performed based on a config.TransformConfig object that holds transformation functions for every desired granularity.

The following example prepares a corresponding object with a track segment transformer that splits tracks with multiple segments into multiple tracks with one segment each:

import (
	"github.com/abzicht/gpsplit/gpxtransform"
	"github.com/abzicht/gpsplit/gpxtransform/config"
	"github.com/abzicht/gpsplit/gpxtransform/options"
)
tc := config.NewTransformConfig(config.WithTrackTransform(gpxtransform.SplitTrackBySegment()))
// corresponding functions also exist for splitting files
// based on tracks (cf. gpxtransform.SplitFileByTrack)

We can also choose to subsequently extend the tc object with segment splitting functionality:

duration := 8 * time.Hour
distance := 5000 * unit.Metre
tc = config.WithSegmentTransform(
        gpxtransform.Split(
            options.TimeSplit(duration),
            options.DistanceSplit(distance),
            ),
        )(tc)
// tc now splits segments into multiple segments, if there are duration /
// distance jumps between two consecutive points that exceed the limits

Finally, we can apply the transformation on a GPX file /track / segment:

import (
	"github.com/tkrajina/gpxgo/gpx"
)
reader, err := os.Open("./my-recording.gpx")
if err != nil {panic(err)}
gpxFile, err := gpx.Parse(reader)
if err != nil {panic(err)}

processedFiles, err := gpxtransform.TransformFile(*gpxFile, tc)
if err != nil {panic(err)}
// processedFiles now holds the (potentially multiple) files that we obtained
// from the transformation.

Installation#

Install Go v1.22 and run the following command:

go install github.com/abzicht/gpsplit