routx 1.0.5

Simple routing over OpenStreetMap data
Documentation
# routx

[GitHub](https://github.com/MKuranowski/routx) |
[Documentation](https://docs.rs/routx/latest/routx/) |
[Issue Tracker](https://github.com/MKuranowski/routx/issues) |
[crates.io](https://crates.io/crates/routx)

Simple routing over [OpenStreetMap](https://www.openstreetmap.org/) 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](crate::osm::Profile). Routx supports one-way streets,
access tags (on ways only) and turn restrictions.

## Usage

routx is written in [Rust](https://www.rust-lang.org/) and uses [Cargo](https://doc.rust-lang.org/cargo/) for dependency management and compilation.

### Rust

Add dependency with `cargo add routx`.

```rust
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](https://en.cppreference.com/w/cpp/container/span.html).

`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](https://github.com/mkuranowski/routx/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](https://mesonbuild.com/Subprojects.html)
and [meson wraps](https://mesonbuild.com/Wrap-dependency-system-manual.html).
Add the wrap file from below as `subprojects/routx.wrap` and get the [dependency object](https://mesonbuild.com/Reference-manual_returned_dep.html)
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.

<details>
<summary>Example C program</summary>

```c
#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;
}
```
</details>

<details>
<summary>Example C++ program</summary>

```cpp
#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';
    }
}
```

</details>

<details>
<summary>Meson wrap file</summary>

```ini
[wrap-git]
url = https://github.com/mkuranowski/routx.git
revision = v1.0.5
depth = 1

[provides]
dependency_names = routx
```

</details>

### Python

Python bindings are kept in a [separate repository](https://github.com/MKuranowski/routx-python)
and [published on PyPI](https://pypi.org/project/routx/).

## Cross-Compiling

It's recommended to use [cargo-zigbuild](https://github.com/rust-cross/cargo-zigbuild) and [cargo-xwin](https://github.com/rust-cross/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 of `cargo build`. Example values include `cargo zigbuild`,
    `cargo xwin build` or `cross build`. It's processed using Python's [shlex.split]https://docs.python.org/3/library/shlex.html#shlex.split,
    and the first argument is assumed to be an executable, searched using [shutil.which]https://docs.python.org/3/library/shutil.html#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 in `target/${cargo_build_target}/`
    instead of the usual `target/`.

## Release Checklist

Note that routx is supposed to use [semantic versioning](https://semver.org/).

1. Make sure the working directory is clean, all the tests and formatting checks pass.
2. 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 latest `main` to GitHub.
3. `cargo publish`
4. 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.
5. Notify and prepare any out-of-tree bindings.

## License

routx is made available under the MIT license.