SharpNinja.Valhalla 1.1.0

dotnet add package SharpNinja.Valhalla --version 1.1.0
                    
NuGet\Install-Package SharpNinja.Valhalla -Version 1.1.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="SharpNinja.Valhalla" Version="1.1.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="SharpNinja.Valhalla" Version="1.1.0" />
                    
Directory.Packages.props
<PackageReference Include="SharpNinja.Valhalla" />
                    
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 SharpNinja.Valhalla --version 1.1.0
                    
#r "nuget: SharpNinja.Valhalla, 1.1.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 SharpNinja.Valhalla@1.1.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=SharpNinja.Valhalla&version=1.1.0
                    
Install as a Cake Addin
#tool nuget:?package=SharpNinja.Valhalla&version=1.1.0
                    
Install as a Cake Tool

SharpNinja.Valhalla

NuGet

Embedded, in-process C# port of the Valhalla OSM routing engine: Baldr graph reader, Sif costing, Thor route engine, Odin directions, and a Mjolnir tile builder, exposed behind a provider-neutral routing client.

There is no server process and no HTTP hop. The library reads Valhalla tiles from a local directory and computes routes entirely in-process, which makes it a fit for embedding routing directly into a desktop or mobile app.

Why

Valhalla is normally run as a standalone service (valhalla_service + a tile directory built by valhalla_build_tiles) that clients call over HTTP. SharpNinja.Valhalla ports the parts of that pipeline needed to go from "a .osm.pbf extract" to "a route between two points" as plain, dependency-light C#, so a .NET app can:

  • Read tiles built by stock Valhalla, or build its own tiles on-device from an OSM extract.
  • Compute a route (auto or truck costing) without a network call.
  • Get maneuver/shape output usable for turn-by-turn UI.

Modules

Each module is a fairly direct port of the corresponding Valhalla C++ module, with the same responsibilities and name:

Module Ported from Responsibility Source Tests
Baldr valhalla/baldr Graph tile reader: tiles, directed edges, nodes, admin areas, traffic, restrictions, sign/street name info src/SharpNinja.Valhalla/Baldr/ (39 files) 32 files
Midgard valhalla/midgard Geometry primitives: points, polylines, tiling math, distance approximation, encoded-shape helpers src/SharpNinja.Valhalla/Midgard/ (18 files) 10 files
Loki valhalla/loki Location correlation: snapping input coordinates onto the graph, closest-edge search src/SharpNinja.Valhalla/Loki/ (4 files) 3 files
Sif valhalla/sif Costing models: DynamicCost, AutoCost, TruckCost, edge labels src/SharpNinja.Valhalla/Sif/ (8 files) 3 files
Thor valhalla/thor Path algorithms: unidirectional and bidirectional A*, trip-leg building src/SharpNinja.Valhalla/Thor/ (10 files) 8 files
Odin valhalla/odin Maneuver building, directions-leg assembly, and en-US narrative prose (NarrativeBuilder + embedded locale dictionaries; see Known gaps for remaining parity depth) src/SharpNinja.Valhalla/Odin/ tests under Odin/
Mjolnir valhalla/mjolnir Tile builder: OSM PBF parsing, graph construction, enhancement, shortcuts, restrictions src/SharpNinja.Valhalla/Mjolnir/ (33 files) 14 files
Osm - This package's own on-device provisioning seam (tile-set building, extract retrieval abstractions) src/SharpNinja.Valhalla/Osm/ (4 files) -

On top of these, the package root exposes the public, provider-neutral surface: IOsmRoutingClient, EmbeddedValhallaRoutingClient, EmbeddedValhallaGraphReaderFactory, GeoCoordinate, IEncodedPolylineDecoder, ValhallaPolylineDecoder, and OsmRoutingErrorCodes.

Requirements

  • .NET 10 SDK
  • A local Valhalla tile directory (built by stock Valhalla's valhalla_build_tiles, or built on-device from a .osm.pbf extract with this package's Mjolnir/Osm types)

Install

dotnet add package SharpNinja.Valhalla

Quickstart

The routing client is provider-neutral by design (IOsmRoutingClient) so callers do not depend on Valhalla-specific DTOs. EmbeddedValhallaRoutingClient is the in-process implementation that drives the ported engine directly against a local tile directory.

using Microsoft.Extensions.DependencyInjection;
using SharpNinja.Valhalla;

var services = new ServiceCollection();
services.AddLogging();
services.AddSingleton<EmbeddedValhallaGraphReaderFactory>();
services.AddSingleton<IOsmTileDirectoryProvider>(new FixedTileDirectoryProvider("/path/to/valhalla_tiles"));
services.AddSingleton<IOsmRoutingClient, EmbeddedValhallaRoutingClient>();

using var provider = services.BuildServiceProvider();
var client = provider.GetRequiredService<IOsmRoutingClient>();

var request = new OsmRouteRequest(
    Endpoint: null,
    Origin: new GeoCoordinate(47.6062, -122.3321),      // Seattle
    Destination: new GeoCoordinate(45.5152, -122.6784),  // Portland
    Costing: OsmRouteCostings.Auto);

var result = await client.CalculateRouteAsync(request);

if (result.Error is not null)
{
    Console.WriteLine($"Routing failed: {result.Error}");
}
else
{
    var route = result.Routes[0];
    Console.WriteLine($"{route.DistanceMeters / 1000:F1} km, {route.DurationSeconds / 60} min, {route.Maneuvers.Count} maneuvers");
}

// A minimal IOsmTileDirectoryProvider for a directory that never changes.
sealed class FixedTileDirectoryProvider(string path) : IOsmTileDirectoryProvider
{
    public Task<string?> GetTileDirectoryAsync(CancellationToken cancellationToken = default)
        => Task.FromResult<string?>(path);
}

Notes on the shapes above:

  • EmbeddedValhallaGraphReaderFactory caches one GraphReader per tile directory (tile loading is expensive) and hands out a Lease with a lock gate, because the ported tile cache is not thread-safe. EmbeddedValhallaRoutingClient already serializes on that gate internally, so callers just await CalculateRouteAsync as normal.
  • IOsmTileDirectoryProvider is host-supplied on purpose: the package does not care whether the tile directory comes from settings, is downloaded on demand, or is bundled with the app.
  • Truck routing is available via OsmRouteCostings.Truck and OsmRouteRequest.TruckOptions (height/width/length/weight/axle count); omitting TruckOptions falls back to stock Valhalla's default truck profile.
  • OsmRouteRequest.ComputeAlternativeRoutes is currently a no-op (see Known gaps).

Decoding an encoded shape

Route candidates already include decoded RoutePoints, but the raw encoded polyline6 string is also available (OsmRouteCandidate.EncodedPolyline) if you need to decode it yourself, e.g. for a cached/serialized route:

IEncodedPolylineDecoder decoder = new ValhallaPolylineDecoder();
IReadOnlyList<GeoCoordinate> points = decoder.Decode(route.EncodedPolyline);

Building tiles on-device

If you don't already have a Valhalla tile directory, the Osm/Mjolnir types can build one on-device from an .osm.pbf extract:

  • IOsmExtractSource - retrieves the extract (the only network step; entirely opt-in)
  • ITileSetBuilder / MjolnirTileSetBuilder - builds tiles from the extract via the ported Mjolnir.TileBuilder
  • IOnDeviceTileProvisioner - orchestrates the two: returns the existing tile directory if tiles are already present, otherwise retrieves the extract and builds tiles, propagating the extract source's error verbatim on failure
var result = await provisioner.EnsureTilesAsync();
if (result.Success)
{
    // result.TileDirectory now has usable tiles
}
else
{
    // result.Error is one of OsmRoutingErrorCodes
}

Error codes

IOsmRoutingClient implementations report failures as one of the canonical OsmRoutingErrorCodes string constants:

Code Meaning
not_configured No tile directory configured, or it's missing/empty on disk
auth_error Authentication failure (reserved for HTTP-backed clients)
rate_limit Rate limited (reserved for HTTP-backed clients)
transport Tile I/O or access failure reading the local tile directory
parse No route found (no snap, no path, or an engine-internal failure building directions)
http_error Generic HTTP failure (reserved for HTTP-backed clients)
invalid_source The configured OSM extract source is present but invalid (e.g. non-HTTPS URL)

Known gaps

Both original behavior gaps versus a full Valhalla HTTP service are now closed for the surfaced routing behavior:

  • Maneuver narrative text. OsmRouteManeuver.Instruction carries en-US written turn-by-turn prose produced by the ported Odin NarrativeBuilder (all driving maneuver families). Remaining upstream-parity depth that the current DTO does not surface - spoken/verbal strings, localized length/time, additional-locale grammar, and the transit/pedestrian/bike-share/indoor maneuver families - is being ported in later slices.
  • Alternate routes. When OsmRouteRequest.ComputeAlternativeRoutes is set and no via/through points are supplied, the engine computes multiple distinct routes (bidirectional A* with the ported alternates.h sharing/stretch viability filters and the recost.h forward recost pass) and OsmRouteResult.Routes carries them primary-first, then by ascending cost. Via routes stay on the single-leg axis. Small maps may still yield a single route when no viable alternate exists.

Build

Run from the repo root; both scripts self-locate so they work regardless of caller cwd.

.\build.ps1 Pack
./build.sh Pack

The build is a Nuke target chain: Restore -> Compile -> Test -> Pack -> Publish.

  • Local default target: Pack (produces artifacts/nuget/*.nupkg)
  • CI (Azure Pipelines) runs Publish, which chains through the rest and pushes to nuget.org using the NUGET_API_KEY pipeline secret

Common targets:

.\build.ps1 Test                        # run the test suite (trx output under artifacts/test-results)
.\build.ps1 Pack -Configuration Release
.\build.ps1 Clean                       # delete bin/obj and artifacts output

Testing

The test suite is xUnit and mirrors the src/ module layout 1:1 (76 test files across Baldr, Loki, Midgard, Mjolnir, Odin, Sif, and Thor), plus a BaldrMonacoParityTests suite that checks the ported Baldr reader against reference behavior.

dotnet test tests/SharpNinja.Valhalla.Tests/SharpNinja.Valhalla.Tests.csproj

or via the Nuke build (.\build.ps1 Test), which also drops .trx results under artifacts/test-results for CI publishing.

Repository layout

src/SharpNinja.Valhalla/     the engine (packable library)
  Baldr/    Midgard/  Loki/  Sif/  Thor/  Odin/  Mjolnir/  Osm/   ported modules (see table above)
  *.cs                                                            public routing surface (client, factory, coordinate, polyline decoder, error codes)
tests/SharpNinja.Valhalla.Tests/   xUnit test suite, same module layout
build/                             Nuke build project (Build.cs)
SharpNinja.Valhalla.slnx           solution file
build.ps1 / build.sh                bootstrap scripts that invoke the Nuke build

License

MIT, matching upstream Valhalla's license. See LICENSE and ACKNOWLEDGMENTS.md for upstream and third-party attribution.

Product Compatible and additional computed target framework versions.
.NET net10.0 is compatible.  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.

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
1.1.0 47 7/10/2026
1.0.0 53 7/8/2026