5 stable releases
| 1.0.5 | Dec 2, 2025 |
|---|---|
| 1.0.4 | Nov 24, 2025 |
| 1.0.3 | Nov 13, 2025 |
| 1.0.2 | Nov 6, 2025 |
| 1.0.0 | Nov 4, 2025 |
#72 in Geospatial
430KB
9K
SLoC
routx
GitHub | Documentation | Issue Tracker | crates.io
Simple routing over OpenStreetMap data.
It converts OSM data into a standard weighted directed graph representation, and runs A* to find shortest paths between nodes. Interpretation of OSM data is customizable via profiles. Routx supports one-way streets, access tags (on ways only) and turn restrictions.
Usage
routx is written in Rust and uses Cargo for dependency management and compilation.
Rust
Add dependency with cargo add routx.
pub fn main() {
let mut g = routx::Graph::new();
let osm_options = routx::osm::Options {
profile: &routx::osm::CAR_PROFILE,
file_format: routx::osm::FileFormat::Unknown,
bbox: [0.0; 4],
};
routx::osm::add_features_from_file(
&mut g,
&osm_options,
"path/to/monaco.osm.pbf",
).expect("failed to load monaco.osm");
let start_node = g.find_nearest_node(43.7384, 7.4246).unwrap();
let end_node = g.find_nearest_node(43.7478, 7.4323).unwrap();
let route = routx::find_route_without_turn_around(&g, start_node.id, end_node.id, routx::DEFAULT_STEP_LIMIT)
.expect("failed to find route");
println!("Route: {:?}", route);
}
C/C++
The C interface is included in the <bindings/include/routx.h> header file. The C++ OOP interface builds on top of that and is included in the <bindings/include/routx.hpp> header. C++20 is required as the bindings make use of std::span.
cargo build --release compiles the static and shared library. Compiled libraries are placed in target/release.
For prototyping it might be easier to simply download a compiled static library and headers
from GitHub Releases and simply cc -o main main.c routx.a.
A Meson wrapper (which simply calls into cargo) is provided to make it easier for C/C++
projects to use routx with the help of meson subprojects
and meson wraps.
Add the wrap file from below as subprojects/routx.wrap and get the dependency object
with routx_dep = dependency('routx', fallback: ['routx', 'routx_dep']).
In principle, any other build system that can execute cargo and copy the files can be used.
Consult the manual for your build system on how to do that.
Example C program
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <routx.h>
void log_handler(void* f, int level, char const* target, char const* message) {
(void)f; // unused
char const* level_str = "";
if (level >= 50) level_str = "CRITICAL";
else if (level >= 40) level_str = "ERROR";
else if (level >= 30) level_str = "WARNING";
else if (level >= 20) level_str = "INFO";
else if (level >= 10) level_str = "DEBUG";
else level_str = "TRACE";
fprintf(stderr, "[%s] %s: %s\n", level_str, target, message);
}
int main(void) {
int status = 1;
RoutxGraph* graph = NULL;
RoutxRouteResult result = {0};
// Set logging handler to show any errors
routx_set_logging_callback(log_handler, NULL, NULL, 30);
// Create a graph and load data into it
graph = routx_graph_new();
RoutxOsmOptions options = {
.profile = ROUTX_OSM_PROFILE_CAR,
.file_format = RoutxOsmFormatUnknown,
.bbox = {0},
};
if (!routx_graph_add_from_osm_file(graph, &options, "path/to/monaco.osm.pbf")) goto cleanup;
// Find the start and end nodes
RoutxNode start_node = routx_graph_find_nearest_node(graph, 43.7384, 7.4246);
RoutxNode end_node = routx_graph_find_nearest_node(graph, 43.7478, 7.4323);
// Find the route
result = routx_find_route(graph, start_node.id, end_node.id, ROUTX_DEFAULT_STEP_LIMIT);
// Print the route or any error
switch (result.type) {
case RoutxRouteResultTypeOk:
for (uint32_t i = 0; i < result.as_ok.len; ++i) {
RoutxNode node = routx_graph_get_node(graph, result.as_ok.nodes[i]);
printf("%f %f\n", node.lat, node.lon);
}
status = 0; // success
break;
case RoutxRouteResultTypeInvalidReference:
fprintf(stderr, "[ERROR] find_route: invalid node reference to %d\n", result.as_invalid_reference.invalid_node_id);
break;
case RoutxRouteResultTypeStepLimitExceeded:
fprintf(stderr, "[ERROR] find_route: step limit exceeded while searching for route\n");
break;
}
// Free used memory
cleanup:
routx_route_result_delete(result);
routx_graph_delete(graph);
return status;
}
Example C++ program
#include <routx.hpp>
#include <iostream>
#include <cstdint>
void log_handler([[maybe_unused]] void* f, int level, char const* target, char const* message) {
char const* level_str = "";
if (level >= 50) level_str = "CRITICAL";
else if (level >= 40) level_str = "ERROR";
else if (level >= 30) level_str = "WARNING";
else if (level >= 20) level_str = "INFO";
else if (level >= 10) level_str = "DEBUG";
else level_str = "TRACE";
std::cerr << '[' << level_str << "] " << target << ": " << message << std::endl;
}
int main(void) {
// Set logging handler to show any errors
routx::set_logging_callback(log_handler, nullptr, nullptr, 30);
// Create a graph and load data into it
routx::Graph g = {};
routx::osm::Options options = {
.profile = routx::osm::ProfileCar,
.file_format = routx::osm::Format::RoutxOsmFormatUnknown,
.bbox = {0},
};
g.add_from_osm_file(&options, "path/to/monaco.osm.pbf");
// Find the start and end nodes
routx::Node start_node = g.find_nearest_node(43.7384, 7.4246);
routx::Node end_node = g.find_nearest_node(43.7478, 7.4323);
// Find the route
routx::Route route = g.find_route_without_turn_around(start_node.id, end_node.id);
// Print the route
for (int64_t node_id : route) {
routx::Node node = g.get_node(node_id);
std::cout << node.lat << ' ' << node.lon << '\n';
}
}
Meson wrap file
[wrap-git]
url = https://github.com/mkuranowski/routx.git
revision = v1.0.5
depth = 1
[provides]
dependency_names = routx
Python
Python bindings are kept in a separate repository and published on PyPI.
Cross-Compiling
It's recommended to use cargo-zigbuild and cargo-xwin.
The Meson wrapper project also supports cross-compilation, through the use of 2 external properties:
cargo_build_command- the command to use in place ofcargo build. Example values includecargo zigbuild,cargo xwin buildorcross build. It's processed using Python's shlex.split, and the first argument is assumed to be an executable, searched using shutil.which.cargo_build_target- if set, append a--target ${cargo_build_target}to the argument list when executing${cargo_build_command}. Causes the wrapper to search for the built library intarget/${cargo_build_target}/instead of the usualtarget/.
Release Checklist
Note that routx is supposed to use semantic versioning.
- Make sure the working directory is clean, all the tests and formatting checks pass.
- Bump version numbers in Cargo.toml, meson.build and README.md. Commit that change and
tag it with
vX.Y.Z. Push that tag along the latestmainto GitHub. cargo publish- Cross-compile static and dynamic versions of the library for most common platforms (
./cross_compile.py). Attach them as artifacts to a new GitHub release, along with the routx.h and routx.hpp headers. - Notify and prepare any out-of-tree bindings.
License
routx is made available under the MIT license.
Dependencies
~4–15MB
~144K SLoC