# borderpoi-rs `borderpoi-rs` finds administrative border crossings along a GPX track. It is designed for long-distance cycling, bikepacking and ultracycling. The program reads a GPX track and a GeoJSON file containing administrative boundary polygons. It detects every county/district transition, preserves repeated visits, and adds a GPX waypoint for every border crossing. ## Features - GPX input - GPX output - stdin/stdout support - GeoJSON boundary input - R*-tree spatial index - repeated county visits are preserved - one GPX POI per border crossing - no distance calculation - no GDAL dependency - no native GIS library dependency ## Input boundary data The boundary GeoJSON must contain Polygon or MultiPolygon features. Coordinates must be WGS84 / EPSG:4326. For BKG VG250 data, the relevant layer is normally: VG250_KRS The default properties expected by `borderpoi-rs` are: GEN AGS `GEN` is used as the displayed county name. `AGS` is used as the administrative identifier. ## Usage ### GPX file to GPX file borderpoi-rs \ --track route.gpx \ --boundaries VG250_KRS.geojson \ --output route-with-borders.gpx ### stdin to stdout cat route.gpx \ | borderpoi-rs \ --boundaries VG250_KRS.geojson \ > route-with-borders.gpx ### Explicit stdin/stdout `-` can be used explicitly: borderpoi-rs \ --track - \ --boundaries VG250_KRS.geojson \ --output - ### Unix pipeline The program writes diagnostic information to stderr and GPX data to stdout. Therefore this works: borderpoi-rs \ --boundaries VG250_KRS.geojson \ < route.gpx \ > route-with-borders.gpx The county report can still be seen in the terminal. ## Command line Usage: borderpoi-rs [OPTIONS] --boundaries Options: -t, --track Input GPX track. Reads from stdin when omitted or set to '-'. -b, --boundaries Boundary GeoJSON. The GeoJSON must contain Polygon/MultiPolygon geometries in WGS84 / EPSG:4326. Required. --name-field Property containing the county name. [default: GEN] --id-field Property containing the administrative identifier. [default: AGS] -o, --output Output GPX. Writes to stdout when omitted or set to '-'. -h, --help Print help. -V, --version Print version. ## County sequence The output preserves the order in which counties are visited. Repeated visits are intentionally preserved. For example: Roth Neumarkt i.d.OPf. Regensburg Neumarkt i.d.OPf. Roth is reported as exactly that. It is not reduced to: Roth Neumarkt i.d.OPf. Regensburg This is important for bikepacking and ultracycling routes that cross administrative boundaries multiple times. ## Border POIs For every transition, a waypoint is added to the output GPX. Example: Border 01: Landkreis Roth -> Landkreis Neumarkt i.d.OPf. Border 02: Landkreis Neumarkt i.d.OPf. -> Landkreis Regensburg Border 03: Landkreis Regensburg -> Landkreis Neumarkt i.d.OPf. Border 04: Landkreis Neumarkt i.d.OPf. -> Landkreis Roth Each waypoint contains: - crossing number - source county - destination county - source AGS - destination AGS - `administrative_boundary` as GPX waypoint type ## Spatial index All county geometries are inserted into an R*-tree. For every GPX track segment the algorithm performs: GPX segment | v segment bounding box | v R*-tree lookup | v candidate county polygons | v exact point-in-polygon test | v county transition | v exact boundary intersection | v GPX waypoint This avoids testing every GPX segment against every county polygon. This is particularly useful for long GPX tracks. ## Coordinate reference system `borderpoi-rs` expects the boundary GeoJSON to use: EPSG:4326 / WGS84 GPX coordinates are also WGS84. No CRS transformation is performed inside the program. This is intentional: it removes the GDAL dependency and keeps the application entirely Rust-native. ## Why no GDAL? The previous implementation used GDAL to read the BKG GeoPackage. That caused the Rust build to depend on the system GDAL version. For example: gdal 0.19.0 gdal-sys 0.12.0 system GDAL 3.13.2 and required generated GDAL bindings. `borderpoi-rs` does not actually need GDAL for its runtime operation. The recommended workflow is therefore: BKG VG250 | | one-time conversion v WGS84 GeoJSON | v borderpoi-rs The conversion from the original BKG dataset can be performed with GDAL/QGIS once, but the resulting command line tool has no GDAL dependency. ## Building with Nix Enter the development environment: nix develop Then: cargo build --release Run: cargo run --release -- \ --boundaries VG250_KRS.geojson \ < route.gpx \ > route-with-borders.gpx ## Building entirely with Nix First generate the lock file: cargo generate-lockfile Then: nix build The resulting executable is: ./result/bin/borderpoi-rs Example: ./result/bin/borderpoi-rs \ --boundaries VG250_KRS.geojson \ --track route.gpx \ --output route-with-borders.gpx ## Formatting and linting Format: cargo fmt Check: cargo check Run Clippy: cargo clippy --all-targets --all-features -- -D warnings ## Input assumptions The current implementation assumes: 1. GPX coordinates are WGS84. 2. Boundary coordinates are WGS84. 3. Boundary features are Polygon or MultiPolygon. 4. The GPX track normally lies inside a county polygon. 5. Administrative boundary geometries are topologically valid. Tracks that run exactly along a county boundary are inherently ambiguous. Such a section is deliberately not interpreted as a sequence of crossings. ## Output contract stdout: output GPX only stderr: diagnostic messages county sequence border crossing report This makes the program suitable for Unix pipelines. Example: borderpoi-rs \ --boundaries counties.geojson \ < route.gpx \ > result.gpx while the report remains visible on the terminal. ## License MIT