itinero / routing

The routing core of itinero.
Apache License 2.0
221 stars 70 forks source link

"Cannot add a duplicate edge" while building RouterDB #236

Closed airbreather closed 5 years ago

airbreather commented 5 years ago

The following code works when ItineroPackageVersion is set to 1.3.2, but when I set it to 1.3.3-ed4cd5a (which is a local build of everything as of commit ed4cd5a using the provided .nuspec files), it fails with the error described in #219, reproduced here (admittedly, I set <DebugType>embedded</DebugType> so that this stack trace is more useful to you):

Unhandled Exception: System.ArgumentException: Cannot add a duplicate edge to a simple graph, there can be only one edge between any vertex-pair.
   at Itinero.Graphs.Graph.AddEdge(UInt32 vertex1, UInt32 vertex2, UInt32[] data) in <<ITINERO.ROUTING>>\src\Itinero\Graphs\Graph.cs:line 331
   at Itinero.Graphs.Geometric.GeometricGraph.AddEdge(UInt32 vertex1, UInt32 vertex2, UInt32[] data, ShapeBase shape) in <<ITINERO.ROUTING>>\src\Itinero\Graphs\Geometric\GeometricGraph.cs:line 347
   at Itinero.Data.Network.RoutingNetwork.AddEdge(UInt32 vertex1, UInt32 vertex2, EdgeData data, ShapeBase shape) in <<ITINERO.ROUTING>>\src\Itinero\Data\Network\RoutingNetwork.cs:line 200
   at Itinero.IO.Osm.Streams.RouterDbStreamTarget.AddCoreEdge(UInt32 vertex1, UInt32 vertex2, EdgeData data, List`1 shape, Int64 wayId, UInt16 nodeIdx) in <<ITINERO.ROUTING>>\src\Itinero.IO.Osm\Streams\RouterDbStreamTarget.cs:line 545
   at Itinero.IO.Osm.Streams.RouterDbStreamTarget.AddWay(Way way) in <<ITINERO.ROUTING>>\src\Itinero.IO.Osm\Streams\RouterDbStreamTarget.cs:line 475
   at OsmSharp.Streams.OsmStreamTarget.DoPull(Boolean ignoreNodes, Boolean ignoreWays, Boolean ignoreRelations)
   at Itinero.IO.Osm.Streams.RouterDbStreamTarget.OnBeforePull() in <<ITINERO.ROUTING>>\src\Itinero.IO.Osm\Streams\RouterDbStreamTarget.cs:line 218
   at OsmSharp.Streams.OsmStreamTarget.Pull()
   at Program.Main(String[] args) in <<THIS_BUG_REPRO>>\Program.cs:line 24

This issue does not appear to be sensitive to the input; every file I've pushed through this code has this problem, including a few I tried from http://files.itinero.tech/data/OSM/planet/europe/ recently. If you have problems finding an input file that reproduces the issue, I can provide the 60 MB extract.

Program.cs

using System.IO;

using Itinero;
using Itinero.Algorithms.Search.Hilbert;
using Itinero.IO.Osm.Streams;

using OsmSharp.Streams;

class Program
{
    static void Main(string[] args)
    {
        // obviously, change these as appropriate to you:
        const string InputPath = @"D:\planet-bits1.osm.pbf";
        const string OutputPath = @"D:\repro.routerdb";

        var routerDb = new RouterDb();
        var vehicles = new[] { Itinero.Osm.Vehicles.Vehicle.Car };
        var target = new RouterDbStreamTarget(routerDb, vehicles, processRestrictions: true);
        using (var inputStream = File.OpenRead(InputPath))
        {
            target.RegisterSource(new PBFOsmStreamSource(inputStream));
            target.Initialize();

            // this is the line that throws:
            target.Pull();
        }

        routerDb.Sort();
        using (var outputStream = File.Create(OutputPath))
        {
            routerDb.Serialize(outputStream);
        }
    }
}

ConsoleApp0.csproj

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>netcoreapp2.2</TargetFramework>

    <!-- Change the following line in order to see the problem -->
    <ItineroPackageVersion>1.3.2</ItineroPackageVersion>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="Itinero" Version="$(ItineroPackageVersion)" />
    <PackageReference Include="Itinero.IO.Osm" Version="$(ItineroPackageVersion)" />
  </ItemGroup>

</Project>
airbreather commented 5 years ago

With the help of git bisect, I've been able to determine that this problem was introduced in 390e676.

airbreather commented 5 years ago

With a tiny bit of investigation, I've determined that this code is using the API incorrectly. I should keep in mind that Itinero's public API is primarily intended to be used through extension methods that connect all the pieces together.

A corrected version of Program.cs looks like this, which does not exhibit the issue:

Program.cs (corrected)

using System.IO;

using Itinero;
using Itinero.Algorithms.Search.Hilbert;
using Itinero.IO.Osm;

using OsmSharp.Streams;

class Program
{
    static void Main(string[] args)
    {
        // obviously, change these as appropriate to you:
        const string InputPath = @"D:\planet-bits1.osm.pbf";
        const string OutputPath = @"D:\repro.routerdb";

        var routerDb = new RouterDb();
        using (var inputStream = File.OpenRead(InputPath))
        {
            routerDb.LoadOsmData(new PBFOsmStreamSource(inputStream), Itinero.Osm.Vehicles.Vehicle.Car);
        }

        routerDb.Sort();
        using (var outputStream = File.Create(OutputPath))
        {
            routerDb.Serialize(outputStream);
        }
    }
}