Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for downhill ski routing #371

Draft
wants to merge 3 commits into
base: develop
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
64 changes: 64 additions & 0 deletions src/Itinero/Osm/Vehicles/Ski/Downhill.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
using Itinero.Attributes;
using Itinero.Profiles;

namespace Itinero.Osm.Vehicles.Ski
{
/// <inheritdoc/>
public class Downhill : Itinero.Profiles.Vehicle
{
/// <summary>
/// Creates a new instance of the <see cref="Downhill"/> class.
/// </summary>
public Downhill()
{
base.MetaWhiteList.Add("name");

base.ProfileWhiteList.Add("piste:type");
base.ProfileWhiteList.Add("aerialway");

Register(new Profile("shortest", ProfileMetric.DistanceInMeters, VehicleTypes, null, this));
Register(new Profile(string.Empty, ProfileMetric.TimeInSeconds, VehicleTypes, null, this));
}

/// <inheritdoc/>
public override string Name => "downhill";

/// <inheritdoc/>
public override string[] VehicleTypes { get; } = [ "ski", "foot" ];

/// <inheritdoc/>
public override FactorAndSpeed FactorAndSpeed(IAttributeCollection attributes, Whitelist whitelist)
{
short direction = 0;
float speedFactor = 0f, value = 0f;

if (attributes is null) return Itinero.Profiles.FactorAndSpeed.NoFactor;

if (attributes.TryGetValue("piste:type", out var piste_type) && piste_type == "downhill") {
direction = 1; // always follow piste direction
if (attributes.TryGetValue("piste:difficulty", out var piste_difficulty)) {
value = speedFactor = piste_difficulty switch
{
"easy" => 1 / (30f / 3.6f),
"intermediate" => 1 / (50f / 3.6f),
"advanced" => 1 / (30f / 3.6f),
_ => 1 / (20f / 3.6f),
};
} else {
value = speedFactor = 1 / (20f / 3.6f);
}
} else if (attributes.TryGetValue("aerialway", out var aerialway)) {
direction = 1; // forward the edge
value = speedFactor = 1 / (10f / 3.6f);
} else {
return Itinero.Profiles.FactorAndSpeed.NoFactor;
}

return new FactorAndSpeed {
SpeedFactor = speedFactor,
Value = value,
Direction = direction
};
}
}
}
14 changes: 14 additions & 0 deletions src/Itinero/Osm/Vehicles/Vehicle.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
using Itinero.Profiles.Lua;
using System.Collections.Generic;
using System.Linq;
using Itinero.Osm.Vehicles.Ski;

namespace Itinero.Osm.Vehicles
{
Expand Down Expand Up @@ -70,6 +71,17 @@ public static class Vehicle
/// </summary>
public static readonly Profiles.Vehicle Bus = new DynamicVehicle(VehicleExtensions.LoadEmbeddedResource("Itinero.Osm.Vehicles.bus.lua"));

/// <summary>
/// Ski vehicle types.
/// </summary>
public static class Ski
{
/// <summary>
/// Downhill skiing.
/// </summary>
public static readonly Profiles.Vehicle Downhill = new Downhill();
}

/// <summary>
/// Registers all default vehicles.
/// </summary>
Expand All @@ -84,6 +96,8 @@ public static void RegisterVehicles()
SmallTruck.Register();
BigTruck.Register();
Bus.Register();

Ski.Downhill.Register();
}

private static Dictionary<string, bool?> _accessValues = null;
Expand Down
16 changes: 11 additions & 5 deletions test/Itinero.Test.Functional/Staging/Download.cs
Original file line number Diff line number Diff line change
Expand Up @@ -37,13 +37,19 @@ public static class Download
/// <summary>
/// Downloads the luxembourg data.
/// </summary>
public static void DownloadLuxembourgAll()
public static void DownloadLuxembourgAll() => DownloadPBF(Download.LuxembourgPBF, Download.LuxembourgLocal);

/// <summary>
/// Download the given PBF url into a local file.
/// </summary>
/// <param name="url">URL for the PBF file.</param>
/// <param name="localPath">Local path.</param>
public static void DownloadPBF(string url, string localPath)
{
if (!File.Exists(Download.LuxembourgLocal))
if (!File.Exists(localPath))
{
var client = new WebClient();
client.DownloadFile(Download.LuxembourgPBF,
Download.LuxembourgLocal);
using var client = new WebClient();
client.DownloadFile(url, localPath);
}
}

Expand Down
24 changes: 24 additions & 0 deletions test/Itinero.Test.Functional/Tests/RoutingTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,8 @@ public static void Run(RouterDb routerDb)
// one-to-many & many-to-many.
GetTestOneToManyRoutes(router, profile, 200).TestPerf($"{profile.FullName} one-to-many routes");
GetTestManyToManyRoutes(router, profile, 20).TestPerf($"{profile.FullName} many-to-many routes");

RunDownhillSki();
}

/// <summary>
Expand Down Expand Up @@ -358,5 +360,27 @@ public static Func<PerformanceTestResult<Result<Route[][]>>> GetTestManyToManyRo
return () => new PerformanceTestResult<Result<Route[][]>>(
router.TryCalculate(profile, resolvedPoints, resolvedPoints));
}

/// <summary>
/// Runs ski resort routing tests.
/// </summary>
public static void RunDownhillSki()
{
var localPath = Path.Combine(Path.GetTempPath(), "aragon-latest.osm.pbf");
Download.DownloadPBF("https://download.geofabrik.de/europe/spain/aragon-latest.osm.pbf", localPath);

using var stream = new FileInfo(localPath).OpenRead();
var routerDb = new RouterDb();
routerDb.LoadOsmData(stream, Itinero.Osm.Vehicles.Vehicle.Ski.Downhill, Itinero.Osm.Vehicles.Vehicle.Pedestrian);
var router = new Router(routerDb);

using var routingCancellation = new CancellationTokenSource();
routingCancellation.CancelAfter(TimeSpan.FromMinutes(5));
var source = new Coordinate(42.58698f, 0.54023f); // El Molino chair lift in Cerler, Spain
var target = new Coordinate(42.55933f, 0.56834f); // Ampriu chair lift in Cerler, Spain

Assert.IsNotNull(router.Calculate(Itinero.Osm.Vehicles.Vehicle.Ski.Downhill.Shortest(),
source.Latitude, source.Longitude, target.Latitude, target.Longitude, routingCancellation.Token));
}
}
}