Sharposrm 0.5.0

dotnet add package Sharposrm --version 0.5.0
                    
NuGet\Install-Package Sharposrm -Version 0.5.0
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Sharposrm" Version="0.5.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Sharposrm" Version="0.5.0" />
                    
Directory.Packages.props
<PackageReference Include="Sharposrm" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add Sharposrm --version 0.5.0
                    
#r "nuget: Sharposrm, 0.5.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package Sharposrm@0.5.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=Sharposrm&version=0.5.0
                    
Install as a Cake Addin
#tool nuget:?package=Sharposrm&version=0.5.0
                    
Install as a Cake Tool

SharpOSRM

⚠️ Disclaimer: This project is entirely vibe coded.

A .NET 8 wrapper for OSRM (Open Source Routing Machine) using native C interop. SharpOSRM exposes OSRM's routing engine and data pipeline as idiomatic C# APIs, distributed as a cross-platform NuGet package that bundles the native libsharposrm shared library for Linux (x64), macOS (x64/arm64), and Windows (x64).

⚠️ TBB malloc proxy notice: The native library links TBB's tbbmalloc_proxy, which replaces the process's malloc/free/realloc with TBB's scalable allocator globally. This improves OSRM's routing performance but can cause crashes and memory corruption in other native libraries (e.g. SQLite, custom native dependencies) loaded in the same process. If your application uses other native libraries that are incompatible with a replaced malloc, build from source with -DSTRIP_TBBMALLOC=ON to disable the proxy at the cost of reduced routing performance.


What it does

OSRM is a high-performance routing engine that operates on OpenStreetMap data. SharpOSRM gives you:

  1. Data pipeline — prepare raw OSM data (.osm.pbf) into a routable dataset using the four OSRM pipeline stages: Extract, Partition, Customize (MLD), Contract (CH).
  2. Routing engine — load a prepared dataset and issue routing queries in-process without any HTTP server.
  3. Seven routing services — Route, Table, Nearest, Match, Trip, Tile, and a FlatBuffers variant of each.

All native calls cross a thin C bridge (libsharposrm) that wraps the OSRM C++ API. The C# side uses LibraryImport P/Invoke with SafeHandle-based lifetime management. All blocking OSRM calls have corresponding *Async overloads that offload to the thread pool.


Requirements

  • .NET 8
  • The Sharposrm NuGet package (includes native libraries for all supported platforms — no separate OSRM installation needed)

For building from source, see CI/CD and Testing Guide.


Installation

dotnet add package Sharposrm

The NuGet package includes prebuilt native libraries for:

Runtime ID Library Platform
linux-x64 libsharposrm.so Ubuntu 22.04+
osx-x64 libsharposrm.dylib macOS 13+ (Intel)
osx-arm64 libsharposrm.dylib macOS 15+ (Apple Silicon)
win-x64 sharposrm.dll Windows Server 2025

Quick start

1. Prepare your data

OSRM cannot route until OSM data has been processed through the pipeline. You need an .osm.pbf file (download from Geofabrik or OSM extracts) and a routing profile Lua file.

Contraction Hierarchies (CH) — faster queries, longer upfront processing:

using Sharposrm;
using Sharposrm.Pipeline;

// Extract
OsrmPipeline.Extract(new ExtractorConfig
{
    InputPath  = "/data/monaco.osm.pbf",
    ProfilePath = "/profiles/car.lua",
    OutputPath  = "/data/monaco",        // base path, OSRM appends extensions
    RequestedThreads = 4,
});

// Contract (CH)
// Note: run on a thread with a large stack — OSRM's contractor is deeply recursive.
var thread = new Thread(() =>
{
    OsrmPipeline.Contract(new ContractorConfig
    {
        BasePath         = "/data/monaco",
        RequestedThreads = 4,
    });
}, maxStackSize: 8 * 1024 * 1024);
thread.Start();
thread.Join();

Multi-Level Dijkstra (MLD) — more flexible, faster preprocessing:

OsrmPipeline.Extract(new ExtractorConfig
{
    InputPath        = "/data/monaco.osm.pbf",
    ProfilePath      = "/profiles/car.lua",
    OutputPath       = "/data/monaco-mld",
    RequestedThreads = 4,
});

OsrmPipeline.Partition(new PartitionerConfig
{
    BasePath         = "/data/monaco-mld",
    RequestedThreads = 4,
});

OsrmPipeline.Customize(new CustomizerConfig
{
    BasePath         = "/data/monaco-mld",
    RequestedThreads = 4,
});

Important: All pipeline RequestedThreads must be ≥ 1. Setting 0 crashes OSRM's TBB backend.

2. Create an engine

await using var engine = await OsrmEngine.CreateAsync(new EngineConfig
{
    StoragePath = "/data/monaco",    // base path produced by the pipeline
    Algorithm   = Algorithm.CH,      // or Algorithm.MLD
});

OsrmEngine is IAsyncDisposable. Engine creation loads the dataset into memory — for large datasets this can take several seconds. CreateAsync offloads this to the thread pool so it doesn't block an async context.

3. Call routing services

Route

Compute a route between two or more waypoints:

using Sharposrm.Route;

var response = engine.Route(new RouteParameters
{
    Coordinates = new[]
    {
        (7.41337, 43.72956),   // (longitude, latitude)
        (7.41983, 43.73115),
    },
    Steps       = true,
    Annotations = true,
    AnnotationTypes = AnnotationsType.Duration | AnnotationsType.Speed,
    Geometries  = GeometriesType.Polyline,
    Overview    = OverviewType.Simplified,
    Alternatives = false,
    GenerateHints = true,
});

Console.WriteLine($"Distance: {response.Routes![0].Distance}m");
Console.WriteLine($"Duration: {response.Routes[0].Duration}s");
Table

Compute a duration/distance matrix between multiple origins and destinations:

using Sharposrm.Table;

var response = engine.Table(new TableParameters
{
    Coordinates = new[]
    {
        (7.41337, 43.72956),
        (7.41983, 43.73115),
        (7.42500, 43.73500),
    },
    // Optional: separate Sources/Destinations index lists for one-to-many queries
});

// Durations[i][j] is null when the pair cannot be routed
double? duration = response.Durations![0][1];
Nearest

Snap a coordinate to the nearest road network node:

using Sharposrm.Nearest;

var response = engine.Nearest(new NearestParameters
{
    Coordinates = new[] { (7.41337, 43.72956) },
    Number = 3,    // return 3 nearest snapping candidates
});
Match

Map-match a GPS trace to the road network:

using Sharposrm.Match;

var response = engine.Match(new MatchParameters
{
    Coordinates = gpsTrace,       // IReadOnlyList<(double, double)>
    Timestamps  = timestamps,     // optional, Unix epoch seconds
    Radiuses    = radiuses,       // optional, per-point search radius in metres
});
Trip

Solve a Travelling Salesman Problem (shortest round trip through all waypoints):

using Sharposrm.Trip;

var response = engine.Trip(new TripParameters
{
    Coordinates  = waypoints,
    RoundTrip    = true,
    Source       = TripSourceType.First,
    Destination  = TripDestinationType.Last,
});
Tile

Fetch a Mapbox Vector Tile for a given tile coordinate (returns raw MVT bytes):

using Sharposrm.Tile;

byte[] mvtData = engine.Tile(new TileParameters
{
    X    = 33660,
    Y    = 22961,
    Zoom = 15,
});
FlatBuffers variants

Every service except Tile has a *Flatbuffers() method that returns raw byte[] instead of a deserialized response object, for use cases where you need the FlatBuffers wire format directly:

byte[] fbBytes = engine.RouteFlatbuffers(routeParameters);
byte[] fbBytes = engine.TableFlatbuffers(tableParameters);
// etc.

EngineConfig reference

Property Type Default Description
StoragePath string (required) Base path to the prepared .osrm dataset
Algorithm Algorithm CH CH or MLD
UseSharedMemory bool false Load from shared memory (requires osrm-datastore)
UseMmap bool true Use memory-mapped file I/O
MaxLocationsTrip int -1 (unlimited) Per-engine cap for Trip service
MaxLocationsViaroute int -1 Per-engine cap for Route service
MaxLocationsDistanceTable int -1 Per-engine cap for Table service
MaxLocationsMapMatching int -1 Per-engine cap for Match service
MaxResultsNearest int -1 Per-engine cap for Nearest service
MaxAlternatives int 3 Maximum alternative routes
DisableRouteSteps bool false Strip step data from all responses
DisableRouteGeometry bool false Strip geometry from all responses

Error handling

All routing services throw OsrmException on failure. The exception message contains OSRM's error code and message (e.g. "OSRM error: NoRoute — No route found between input coordinates").

try
{
    var response = engine.Route(parameters);
}
catch (OsrmException ex)
{
    Console.WriteLine(ex.Message);
}

Threading

  • OsrmEngine instances are not thread-safe. Use one engine per thread, or synchronise externally.
  • Each *Async method calls Task.Run(...) — it offloads the blocking native call to the thread pool.
  • OSRM's CH contraction is deeply recursive. Always run OsrmPipeline.Contract on a thread with a stack of at least 8 MB (see example above).

License

SharpOSRM is licensed under the MIT License.

This package statically links OSRM, which is licensed under the BSD 2-Clause License. See THIRD-PARTY-NOTICES.txt for the full OSRM copyright notice.

Product Compatible and additional computed target framework versions.
.NET net8.0 is compatible.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 was computed.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 was computed.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.
  • net8.0

    • No dependencies.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
0.5.0 119 4/10/2026
0.4.0 111 4/10/2026
0.3.0 119 4/10/2026
0.2.0 107 4/10/2026
0.1.1 106 4/9/2026