start with chatgpt
This commit is contained in:
commit
c78520ee65
7 changed files with 2086 additions and 0 deletions
330
README.md
Normal file
330
README.md
Normal file
|
|
@ -0,0 +1,330 @@
|
|||
# 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 <BOUNDARIES>
|
||||
|
||||
Options:
|
||||
-t, --track <TRACK>
|
||||
Input GPX track. Reads from stdin when omitted or set to '-'.
|
||||
|
||||
-b, --boundaries <BOUNDARIES>
|
||||
Boundary GeoJSON.
|
||||
|
||||
The GeoJSON must contain Polygon/MultiPolygon geometries
|
||||
in WGS84 / EPSG:4326.
|
||||
|
||||
Required.
|
||||
|
||||
--name-field <NAME_FIELD>
|
||||
Property containing the county name.
|
||||
|
||||
[default: GEN]
|
||||
|
||||
--id-field <ID_FIELD>
|
||||
Property containing the administrative identifier.
|
||||
|
||||
[default: AGS]
|
||||
|
||||
-o, --output <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
|
||||
Loading…
Reference in a new issue