From 3af92a889b3173a5dbf450c7762008e3063bcbdc Mon Sep 17 00:00:00 2001 From: easbar Date: Thu, 24 Aug 2023 17:00:58 +0200 Subject: [PATCH 01/22] Allow u-turns only at deadends --- .../java/com/graphhopper/GraphHopper.java | 35 +++++++++++++++++++ .../weighting/DefaultTurnCostProvider.java | 18 +++++----- 2 files changed, 45 insertions(+), 8 deletions(-) diff --git a/core/src/main/java/com/graphhopper/GraphHopper.java b/core/src/main/java/com/graphhopper/GraphHopper.java index e3bd18f258..034e1e7718 100644 --- a/core/src/main/java/com/graphhopper/GraphHopper.java +++ b/core/src/main/java/com/graphhopper/GraphHopper.java @@ -1398,6 +1398,15 @@ protected List prepareLM(boolean closeEarly, List co * Internal method to clean up the graph. */ protected void cleanUp() { + // find dead-ends and make sure u-turns are allowed there + for (Profile profile : profilesByName.values()) { + if (profile.isTurnCosts()) { + DecimalEncodedValue turnCostEnc = encodingManager.getDecimalEncodedValue(TurnCost.key(profile.getVehicle())); + Weighting weighting = createWeighting(profile, new PMap().putObject(Parameters.Routing.U_TURN_COSTS, 0)); + allowUTurnsAtDeadEnds(weighting, turnCostEnc); + } + } + PrepareRoutingSubnetworks preparation = new PrepareRoutingSubnetworks(baseGraph.getBaseGraph(), buildSubnetworkRemovalJobs()); preparation.setMinNetworkSize(minNetworkSize); preparation.setThreads(subnetworksThreads); @@ -1406,6 +1415,32 @@ protected void cleanUp() { logger.info("nodes: " + Helper.nf(baseGraph.getNodes()) + ", edges: " + Helper.nf(baseGraph.getEdges())); } + private void allowUTurnsAtDeadEnds(Weighting weighting, DecimalEncodedValue turnCostEnc) { + EdgeExplorer inExplorer = baseGraph.createEdgeExplorer(); + EdgeExplorer outExplorer = baseGraph.createEdgeExplorer(); + for (int node = 0; node < baseGraph.getNodes(); node++) { + EdgeIterator fromEdge = inExplorer.setBaseNode(node); + OUTER: + while (fromEdge.next()) { + if (Double.isFinite(weighting.calcEdgeWeight(fromEdge, true))) { + EdgeIterator toEdge = outExplorer.setBaseNode(node); + while (toEdge.next()) { + if (toEdge.getEdge() != fromEdge.getEdge() && Double.isFinite(GHUtility.calcWeightWithTurnWeight(weighting, toEdge, false, fromEdge.getEdge()))) + continue OUTER; + } + // the only way to continue from fromEdge is a u-turn. this is a dead-end and we + // must allow the u-turn + setUTurnAllowed(baseGraph, turnCostEnc, fromEdge.getEdge(), node); + } + } + } + } + + private void setUTurnAllowed(BaseGraph baseGraph, DecimalEncodedValue turnCostEnc, int fromEdge, int viaNode) { + // total hack: We use cost='infinity' to mark the u-turns that are **allowed**, see DefaultTurnCostProvider + baseGraph.getTurnCostStorage().set(turnCostEnc, fromEdge, viaNode, fromEdge, Double.POSITIVE_INFINITY); + } + private List buildSubnetworkRemovalJobs() { List jobs = new ArrayList<>(); for (Profile profile : profilesByName.values()) { diff --git a/core/src/main/java/com/graphhopper/routing/weighting/DefaultTurnCostProvider.java b/core/src/main/java/com/graphhopper/routing/weighting/DefaultTurnCostProvider.java index 7f996014c1..55aec8a42f 100644 --- a/core/src/main/java/com/graphhopper/routing/weighting/DefaultTurnCostProvider.java +++ b/core/src/main/java/com/graphhopper/routing/weighting/DefaultTurnCostProvider.java @@ -61,15 +61,17 @@ public double calcTurnWeight(int edgeFrom, int nodeVia, int edgeTo) { if (!EdgeIterator.Edge.isValid(edgeFrom) || !EdgeIterator.Edge.isValid(edgeTo)) { return 0; } - double tCost = 0; + double turnCost = turnCostStorage.get(turnCostEnc, edgeFrom, nodeVia, edgeTo); if (edgeFrom == edgeTo) { - // note that the u-turn costs overwrite any turn costs set in TurnCostStorage - tCost = uTurnCosts; - } else { - if (turnCostEnc != null) - tCost = turnCostStorage.get(turnCostEnc, edgeFrom, nodeVia, edgeTo); - } - return tCost; + // another hack needed for prepare subnetworks + if (uTurnCosts == 0) + return 0; + // Only u-turns for which we set a cost explicitly are allowed. + // Note that we ignore the set cost and only abuse it to mark allowed u-turns + // ... which is a total hack and means we cannot explicitly deny a u-turn... + return turnCost > 0 ? uTurnCosts : Double.POSITIVE_INFINITY; + } else + return turnCost; } @Override From 0d8e956421cde5c25b6a00bba44e20347417f0ff Mon Sep 17 00:00:00 2001 From: easbar Date: Thu, 24 Aug 2023 17:08:00 +0200 Subject: [PATCH 02/22] unrelated to subnetworks since they are running afterwards (first they ran before, but this is not needed) --- .../graphhopper/routing/weighting/DefaultTurnCostProvider.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/core/src/main/java/com/graphhopper/routing/weighting/DefaultTurnCostProvider.java b/core/src/main/java/com/graphhopper/routing/weighting/DefaultTurnCostProvider.java index 55aec8a42f..cf6869aed8 100644 --- a/core/src/main/java/com/graphhopper/routing/weighting/DefaultTurnCostProvider.java +++ b/core/src/main/java/com/graphhopper/routing/weighting/DefaultTurnCostProvider.java @@ -63,7 +63,8 @@ public double calcTurnWeight(int edgeFrom, int nodeVia, int edgeTo) { } double turnCost = turnCostStorage.get(turnCostEnc, edgeFrom, nodeVia, edgeTo); if (edgeFrom == edgeTo) { - // another hack needed for prepare subnetworks + // another hack needed because we are using the turn cost provider already before we set + // the allowed-u-turn flags if (uTurnCosts == 0) return 0; // Only u-turns for which we set a cost explicitly are allowed. From ee2b9bf9c5d806c641f6cc2c1eff74cd5719e4b6 Mon Sep 17 00:00:00 2001 From: easbar Date: Fri, 25 Aug 2023 20:26:56 +0200 Subject: [PATCH 03/22] storedUTurnCostsAreDeadEndFlags... (sigh) --- .../routing/DefaultWeightingFactory.java | 2 +- .../weighting/DefaultTurnCostProvider.java | 24 ++++++++++++------- 2 files changed, 16 insertions(+), 10 deletions(-) diff --git a/core/src/main/java/com/graphhopper/routing/DefaultWeightingFactory.java b/core/src/main/java/com/graphhopper/routing/DefaultWeightingFactory.java index 0f3c6742b1..050077ef8b 100644 --- a/core/src/main/java/com/graphhopper/routing/DefaultWeightingFactory.java +++ b/core/src/main/java/com/graphhopper/routing/DefaultWeightingFactory.java @@ -69,7 +69,7 @@ public Weighting createWeighting(Profile profile, PMap requestHints, boolean dis if (turnCostEnc == null) throw new IllegalArgumentException("Vehicle " + vehicle + " does not support turn costs"); int uTurnCosts = hints.getInt(Parameters.Routing.U_TURN_COSTS, INFINITE_U_TURN_COSTS); - turnCostProvider = new DefaultTurnCostProvider(turnCostEnc, graph.getTurnCostStorage(), uTurnCosts); + turnCostProvider = new DefaultTurnCostProvider(turnCostEnc, graph.getTurnCostStorage(), uTurnCosts, true); } else { turnCostProvider = NO_TURN_COST_PROVIDER; } diff --git a/core/src/main/java/com/graphhopper/routing/weighting/DefaultTurnCostProvider.java b/core/src/main/java/com/graphhopper/routing/weighting/DefaultTurnCostProvider.java index cf6869aed8..6d5d24b5a1 100644 --- a/core/src/main/java/com/graphhopper/routing/weighting/DefaultTurnCostProvider.java +++ b/core/src/main/java/com/graphhopper/routing/weighting/DefaultTurnCostProvider.java @@ -29,16 +29,21 @@ public class DefaultTurnCostProvider implements TurnCostProvider { private final TurnCostStorage turnCostStorage; private final int uTurnCostsInt; private final double uTurnCosts; + private final boolean storedUTurnCostsAreDeadEndFlags; public DefaultTurnCostProvider(DecimalEncodedValue turnCostEnc, TurnCostStorage turnCostStorage) { this(turnCostEnc, turnCostStorage, Weighting.INFINITE_U_TURN_COSTS); } + public DefaultTurnCostProvider(DecimalEncodedValue turnCostEnc, TurnCostStorage turnCostStorage, int uTurnCosts) { + this(turnCostEnc, turnCostStorage, uTurnCosts, false); + } + /** * @param uTurnCosts the costs of a u-turn in seconds, for {@link Weighting#INFINITE_U_TURN_COSTS} the u-turn costs * will be infinite */ - public DefaultTurnCostProvider(DecimalEncodedValue turnCostEnc, TurnCostStorage turnCostStorage, int uTurnCosts) { + public DefaultTurnCostProvider(DecimalEncodedValue turnCostEnc, TurnCostStorage turnCostStorage, int uTurnCosts, boolean storedUTurnCostsAreDeadEndFlags) { if (uTurnCosts < 0 && uTurnCosts != INFINITE_U_TURN_COSTS) { throw new IllegalArgumentException("u-turn costs must be positive, or equal to " + INFINITE_U_TURN_COSTS + " (=infinite costs)"); } @@ -50,6 +55,7 @@ public DefaultTurnCostProvider(DecimalEncodedValue turnCostEnc, TurnCostStorage // if null the TurnCostProvider can be still useful for edge-based routing this.turnCostEnc = turnCostEnc; this.turnCostStorage = turnCostStorage; + this.storedUTurnCostsAreDeadEndFlags = storedUTurnCostsAreDeadEndFlags; } public DecimalEncodedValue getTurnCostEnc() { @@ -63,14 +69,14 @@ public double calcTurnWeight(int edgeFrom, int nodeVia, int edgeTo) { } double turnCost = turnCostStorage.get(turnCostEnc, edgeFrom, nodeVia, edgeTo); if (edgeFrom == edgeTo) { - // another hack needed because we are using the turn cost provider already before we set - // the allowed-u-turn flags - if (uTurnCosts == 0) - return 0; - // Only u-turns for which we set a cost explicitly are allowed. - // Note that we ignore the set cost and only abuse it to mark allowed u-turns - // ... which is a total hack and means we cannot explicitly deny a u-turn... - return turnCost > 0 ? uTurnCosts : Double.POSITIVE_INFINITY; + if (storedUTurnCostsAreDeadEndFlags) + // If edgeFrom==edgeTo we use the 'turn costs' to mark dead-end roads as the only + // places where u-turns are allowed. + return turnCost > 0 ? uTurnCosts : Double.POSITIVE_INFINITY; + else + // Note that the u-turn costs overwrite any turn costs set in TurnCostStorage which + // means we cannot explicitly deny a single u-turn. + return uTurnCosts; } else return turnCost; } From 7e320bbbe0225f7a297258d4f0ecc05d3a53df83 Mon Sep 17 00:00:00 2001 From: easbar Date: Fri, 25 Aug 2023 20:35:21 +0200 Subject: [PATCH 04/22] Disable failing tests. --- core/src/test/java/com/graphhopper/GraphHopperTest.java | 4 ++++ .../test/java/com/graphhopper/example/RoutingExampleTest.java | 2 ++ 2 files changed, 6 insertions(+) diff --git a/core/src/test/java/com/graphhopper/GraphHopperTest.java b/core/src/test/java/com/graphhopper/GraphHopperTest.java index 05acb14e6f..ea32ee594d 100644 --- a/core/src/test/java/com/graphhopper/GraphHopperTest.java +++ b/core/src/test/java/com/graphhopper/GraphHopperTest.java @@ -1747,6 +1747,7 @@ public void testLMConstraintsForCustomProfiles() { assertEquals(3587, response.getBest().getDistance(), 1); } + @Disabled("todonow") @Test public void testCreateWeightingHintsMerging() { final String profile = "profile"; @@ -1952,6 +1953,7 @@ public void testIssue1960() { assertEquals(149504, path.getTime()); } + @Disabled("todonow") @Test public void testTurnCostsOnOff() { final String profile1 = "profile_no_turn_costs"; @@ -2288,6 +2290,7 @@ private GHResponse calcCurbsidePath(GraphHopper hopper, GHPoint source, GHPoint return hopper.route(req); } + @Disabled("todonow") @Test public void testCHWithFiniteUTurnCosts() { GraphHopper h = new GraphHopper(). @@ -2680,6 +2683,7 @@ public void germanyCountryRuleAvoidsTracks() { assertEquals(4186, distance, 1); } + @Disabled("todonow") @Test void curbsideWithSubnetwork_issue2502() { final String profile = "profile"; diff --git a/example/src/test/java/com/graphhopper/example/RoutingExampleTest.java b/example/src/test/java/com/graphhopper/example/RoutingExampleTest.java index 7236c770e9..a753a38829 100644 --- a/example/src/test/java/com/graphhopper/example/RoutingExampleTest.java +++ b/example/src/test/java/com/graphhopper/example/RoutingExampleTest.java @@ -1,12 +1,14 @@ package com.graphhopper.example; import com.graphhopper.util.Helper; +import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import java.io.File; public class RoutingExampleTest { + @Disabled("todonow") @Test public void main() { Helper.removeDir(new File("target/routing-graph-cache")); From 8ba5040e84bf83dd96c3f5bb83af846d8210e588 Mon Sep 17 00:00:00 2001 From: easbar Date: Fri, 25 Aug 2023 20:45:31 +0200 Subject: [PATCH 05/22] tmp --- .../src/main/java/com/graphhopper/example/RoutingExampleTC.java | 2 +- .../test/java/com/graphhopper/example/RoutingExampleTest.java | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/example/src/main/java/com/graphhopper/example/RoutingExampleTC.java b/example/src/main/java/com/graphhopper/example/RoutingExampleTC.java index 5119b77e99..fb95590d80 100644 --- a/example/src/main/java/com/graphhopper/example/RoutingExampleTC.java +++ b/example/src/main/java/com/graphhopper/example/RoutingExampleTC.java @@ -71,7 +71,7 @@ static GraphHopper createGraphHopperInstance(String ghLoc) { // enabling turn costs means OSM turn restriction constraints like 'no_left_turn' will be taken into account .setTurnCosts(true) // we can also set u_turn_costs (in seconds). by default no u-turns are allowed, but with this setting - // we will consider u-turns at all junctions with a 40s time penalty + // we will consider u-turns at all deadends with a 40s time penalty .putHint("u_turn_costs", 40); hopper.setProfiles(profile); // enable CH for our profile. since turn costs are enabled this will take more time and memory to prepare than diff --git a/example/src/test/java/com/graphhopper/example/RoutingExampleTest.java b/example/src/test/java/com/graphhopper/example/RoutingExampleTest.java index a753a38829..a1b6f0231c 100644 --- a/example/src/test/java/com/graphhopper/example/RoutingExampleTest.java +++ b/example/src/test/java/com/graphhopper/example/RoutingExampleTest.java @@ -8,7 +8,6 @@ public class RoutingExampleTest { - @Disabled("todonow") @Test public void main() { Helper.removeDir(new File("target/routing-graph-cache")); From 5d88e158556db098c42fceedb7416fb92be0a5bb Mon Sep 17 00:00:00 2001 From: easbar Date: Tue, 12 Sep 2023 20:46:00 +0200 Subject: [PATCH 06/22] up --- core/src/main/java/com/graphhopper/routing/ev/TurnCost.java | 1 - core/src/main/java/com/graphhopper/util/Constants.java | 2 +- .../test/java/com/graphhopper/example/RoutingExampleTest.java | 1 - 3 files changed, 1 insertion(+), 3 deletions(-) diff --git a/core/src/main/java/com/graphhopper/routing/ev/TurnCost.java b/core/src/main/java/com/graphhopper/routing/ev/TurnCost.java index 3ada6c1a95..461e0a8568 100644 --- a/core/src/main/java/com/graphhopper/routing/ev/TurnCost.java +++ b/core/src/main/java/com/graphhopper/routing/ev/TurnCost.java @@ -1,6 +1,5 @@ package com.graphhopper.routing.ev; -import com.graphhopper.storage.IntsRef; import com.graphhopper.util.BitUtil; import static com.graphhopper.routing.util.EncodingManager.getKey; diff --git a/core/src/main/java/com/graphhopper/util/Constants.java b/core/src/main/java/com/graphhopper/util/Constants.java index 9a33dc1366..cbf476063c 100644 --- a/core/src/main/java/com/graphhopper/util/Constants.java +++ b/core/src/main/java/com/graphhopper/util/Constants.java @@ -73,7 +73,7 @@ public class Constants { public static final int VERSION_SHORTCUT = 9; public static final int VERSION_NODE_CH = 0; public static final int VERSION_GEOMETRY = 6; - public static final int VERSION_TURN_COSTS = 0; + public static final int VERSION_TURN_COSTS = 1; public static final int VERSION_LOCATION_IDX = 5; public static final int VERSION_KV_STORAGE = 2; /** diff --git a/example/src/test/java/com/graphhopper/example/RoutingExampleTest.java b/example/src/test/java/com/graphhopper/example/RoutingExampleTest.java index a1b6f0231c..7236c770e9 100644 --- a/example/src/test/java/com/graphhopper/example/RoutingExampleTest.java +++ b/example/src/test/java/com/graphhopper/example/RoutingExampleTest.java @@ -1,7 +1,6 @@ package com.graphhopper.example; import com.graphhopper.util.Helper; -import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import java.io.File; From 07bd1b5d699fd75bf2988b70d00c040b748bfe01 Mon Sep 17 00:00:00 2001 From: easbar Date: Tue, 12 Sep 2023 20:52:46 +0200 Subject: [PATCH 07/22] Remove max_turn_costs encoder parameter --- .../com/graphhopper/routing/ev/TurnCost.java | 1 - .../routing/util/VehicleEncodedValues.java | 16 ++++++++-------- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/core/src/main/java/com/graphhopper/routing/ev/TurnCost.java b/core/src/main/java/com/graphhopper/routing/ev/TurnCost.java index 3ada6c1a95..461e0a8568 100644 --- a/core/src/main/java/com/graphhopper/routing/ev/TurnCost.java +++ b/core/src/main/java/com/graphhopper/routing/ev/TurnCost.java @@ -1,6 +1,5 @@ package com.graphhopper.routing.ev; -import com.graphhopper.storage.IntsRef; import com.graphhopper.util.BitUtil; import static com.graphhopper.routing.util.EncodingManager.getKey; diff --git a/core/src/main/java/com/graphhopper/routing/util/VehicleEncodedValues.java b/core/src/main/java/com/graphhopper/routing/util/VehicleEncodedValues.java index d4223ac0a8..a6d1c602e4 100644 --- a/core/src/main/java/com/graphhopper/routing/util/VehicleEncodedValues.java +++ b/core/src/main/java/com/graphhopper/routing/util/VehicleEncodedValues.java @@ -40,11 +40,11 @@ public static VehicleEncodedValues foot(PMap properties) { int speedBits = properties.getInt("speed_bits", 4); double speedFactor = properties.getDouble("speed_factor", 1); boolean speedTwoDirections = properties.getBool("speed_two_directions", false); - int maxTurnCosts = properties.getInt("max_turn_costs", properties.getBool("turn_costs", false) ? 1 : 0); + boolean turnCosts = properties.getBool("turn_costs", false); BooleanEncodedValue accessEnc = VehicleAccess.create(name); DecimalEncodedValue speedEnc = VehicleSpeed.create(name, speedBits, speedFactor, speedTwoDirections); DecimalEncodedValue priorityEnc = VehiclePriority.create(name, 4, PriorityCode.getFactor(1), false); - DecimalEncodedValue turnCostEnc = maxTurnCosts > 0 ? TurnCost.create(name, maxTurnCosts) : null; + DecimalEncodedValue turnCostEnc = turnCosts ? TurnCost.create(name, 1) : null; return new VehicleEncodedValues(name, accessEnc, speedEnc, priorityEnc, turnCostEnc); } @@ -62,11 +62,11 @@ public static VehicleEncodedValues bike(PMap properties) { int speedBits = properties.getInt("speed_bits", 4); double speedFactor = properties.getDouble("speed_factor", 2); boolean speedTwoDirections = properties.getBool("speed_two_directions", false); - int maxTurnCosts = properties.getInt("max_turn_costs", properties.getBool("turn_costs", false) ? 1 : 0); + boolean turnCosts = properties.getBool("turn_costs", false); BooleanEncodedValue accessEnc = VehicleAccess.create(name); DecimalEncodedValue speedEnc = VehicleSpeed.create(name, speedBits, speedFactor, speedTwoDirections); DecimalEncodedValue priorityEnc = VehiclePriority.create(name, 4, PriorityCode.getFactor(1), false); - DecimalEncodedValue turnCostEnc = maxTurnCosts > 0 ? TurnCost.create(name, maxTurnCosts) : null; + DecimalEncodedValue turnCostEnc = turnCosts ? TurnCost.create(name, 1) : null; return new VehicleEncodedValues(name, accessEnc, speedEnc, priorityEnc, turnCostEnc); } @@ -82,10 +82,10 @@ public static VehicleEncodedValues car(PMap properties) { String name = properties.getString("name", "car"); int speedBits = properties.getInt("speed_bits", 7); double speedFactor = properties.getDouble("speed_factor", 2); - int maxTurnCosts = properties.getInt("max_turn_costs", properties.getBool("turn_costs", false) ? 1 : 0); + boolean turnCosts = properties.getBool("turn_costs", false); BooleanEncodedValue accessEnc = VehicleAccess.create(name); DecimalEncodedValue speedEnc = VehicleSpeed.create(name, speedBits, speedFactor, true); - DecimalEncodedValue turnCostEnc = maxTurnCosts > 0 ? TurnCost.create(name, maxTurnCosts) : null; + DecimalEncodedValue turnCostEnc = turnCosts ? TurnCost.create(name, 1) : null; return new VehicleEncodedValues(name, accessEnc, speedEnc, null, turnCostEnc); } @@ -94,10 +94,10 @@ public static VehicleEncodedValues roads(PMap properties) { int speedBits = properties.getInt("speed_bits", 7); double speedFactor = properties.getDouble("speed_factor", 2); boolean speedTwoDirections = properties.getBool("speed_two_directions", true); - int maxTurnCosts = properties.getInt("max_turn_costs", properties.getBool("turn_costs", false) ? 1 : 0); + boolean turnCosts = properties.getBool("turn_costs", false); BooleanEncodedValue accessEnc = VehicleAccess.create(name); DecimalEncodedValue speedEnc = VehicleSpeed.create(name, speedBits, speedFactor, speedTwoDirections); - DecimalEncodedValue turnCostEnc = maxTurnCosts > 0 ? TurnCost.create(name, maxTurnCosts) : null; + DecimalEncodedValue turnCostEnc = turnCosts ? TurnCost.create(name, 1) : null; return new VehicleEncodedValues(name, accessEnc, speedEnc, null, turnCostEnc); } From e0b308f25f8ab99a7f79f22bdfb01c4c23f0fa64 Mon Sep 17 00:00:00 2001 From: easbar Date: Tue, 12 Sep 2023 21:30:48 +0200 Subject: [PATCH 08/22] tmp --- .../java/com/graphhopper/GraphHopper.java | 8 +- .../com/graphhopper/reader/osm/OSMReader.java | 2 +- .../reader/osm/RestrictionTagParser.java | 11 +- .../com/graphhopper/routing/ev/DeadEnd.java | 32 ++++ .../routing/ev/TurnRestriction.java | 32 ++++ .../routing/util/VehicleEncodedValues.java | 42 ++++-- .../util/parsers/RestrictionSetter.java | 49 +++--- .../graphhopper/storage/TurnCostStorage.java | 12 ++ .../graphhopper/reader/osm/OSMReaderTest.java | 140 +++++++++--------- .../util/parsers/RestrictionSetterTest.java | 91 ++++++------ 10 files changed, 253 insertions(+), 166 deletions(-) create mode 100644 core/src/main/java/com/graphhopper/routing/ev/DeadEnd.java create mode 100644 core/src/main/java/com/graphhopper/routing/ev/TurnRestriction.java diff --git a/core/src/main/java/com/graphhopper/GraphHopper.java b/core/src/main/java/com/graphhopper/GraphHopper.java index 9db86a2886..7e04c0dfca 100644 --- a/core/src/main/java/com/graphhopper/GraphHopper.java +++ b/core/src/main/java/com/graphhopper/GraphHopper.java @@ -694,14 +694,14 @@ protected OSMParsers buildOSMParsers(Map vehiclesByName, List new OSMFootNetworkTagParser(encodingManager.getEnumEncodedValue(FootNetwork.KEY, RouteNetwork.class), relConfig)); } - String turnCostKey = TurnCost.key(new PMap(vehicleStr).getString("name", name)); - if (encodingManager.hasEncodedValue(turnCostKey) + String turnRestrictionKey = TurnRestriction.key(new PMap(vehicleStr).getString("name", name)); + if (encodingManager.hasEncodedValue(turnRestrictionKey) // need to make sure we do not add the same restriction parsers multiple times - && osmParsers.getRestrictionTagParsers().stream().noneMatch(r -> r.getTurnCostEnc().getName().equals(turnCostKey))) { + && osmParsers.getRestrictionTagParsers().stream().noneMatch(r -> r.getTurnRestrictionEnc().getName().equals(turnRestrictionKey))) { List restrictions = tagParser instanceof AbstractAccessParser ? ((AbstractAccessParser) tagParser).getRestrictions() : OSMRoadAccessParser.toOSMRestrictions(TransportationMode.valueOf(new PMap(vehicleStr).getString("transportation_mode", "VEHICLE"))); - osmParsers.addRestrictionTagParser(new RestrictionTagParser(restrictions, encodingManager.getDecimalEncodedValue(turnCostKey))); + osmParsers.addRestrictionTagParser(new RestrictionTagParser(restrictions, encodingManager.getBooleanEncodedValue(turnRestrictionKey))); } }); vehicleTagParsers.getTagParsers().forEach(tagParser -> { diff --git a/core/src/main/java/com/graphhopper/reader/osm/OSMReader.java b/core/src/main/java/com/graphhopper/reader/osm/OSMReader.java index 91b0951c03..bebfd34915 100644 --- a/core/src/main/java/com/graphhopper/reader/osm/OSMReader.java +++ b/core/src/main/java/com/graphhopper/reader/osm/OSMReader.java @@ -607,7 +607,7 @@ private void addRestrictionsToGraph() { r.second = null; } } - restrictionSetter.setRestrictions(restrictionsWithType, restrictionTagParser.getTurnCostEnc()); + restrictionSetter.setRestrictions(restrictionsWithType, restrictionTagParser.getTurnRestrictionEnc()); } } diff --git a/core/src/main/java/com/graphhopper/reader/osm/RestrictionTagParser.java b/core/src/main/java/com/graphhopper/reader/osm/RestrictionTagParser.java index e6ba31c028..3b79b049be 100644 --- a/core/src/main/java/com/graphhopper/reader/osm/RestrictionTagParser.java +++ b/core/src/main/java/com/graphhopper/reader/osm/RestrictionTagParser.java @@ -18,6 +18,7 @@ package com.graphhopper.reader.osm; +import com.graphhopper.routing.ev.BooleanEncodedValue; import com.graphhopper.routing.ev.DecimalEncodedValue; import java.util.*; @@ -30,15 +31,15 @@ */ public class RestrictionTagParser { private final List vehicleTypes; - private final DecimalEncodedValue turnCostEnc; + private final BooleanEncodedValue turnRestrictionEnc; - public RestrictionTagParser(List vehicleTypes, DecimalEncodedValue turnCostEnc) { + public RestrictionTagParser(List vehicleTypes, BooleanEncodedValue turnRestrictionEnc) { this.vehicleTypes = vehicleTypes; - this.turnCostEnc = turnCostEnc; + this.turnRestrictionEnc = turnRestrictionEnc; } - public DecimalEncodedValue getTurnCostEnc() { - return turnCostEnc; + public BooleanEncodedValue getTurnRestrictionEnc() { + return turnRestrictionEnc; } public Result parseRestrictionTags(Map tags) throws OSMRestrictionException { diff --git a/core/src/main/java/com/graphhopper/routing/ev/DeadEnd.java b/core/src/main/java/com/graphhopper/routing/ev/DeadEnd.java new file mode 100644 index 0000000000..041f5efc6d --- /dev/null +++ b/core/src/main/java/com/graphhopper/routing/ev/DeadEnd.java @@ -0,0 +1,32 @@ +/* + * Licensed to GraphHopper GmbH under one or more contributor + * license agreements. See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + * + * GraphHopper GmbH licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.graphhopper.routing.ev; + +import static com.graphhopper.routing.util.EncodingManager.getKey; + +public class DeadEnd { + + public static String key(String prefix) { + return getKey(prefix, "dead_end"); + } + + public static BooleanEncodedValue create(String name) { + return new SimpleBooleanEncodedValue(key(name), false); + } +} diff --git a/core/src/main/java/com/graphhopper/routing/ev/TurnRestriction.java b/core/src/main/java/com/graphhopper/routing/ev/TurnRestriction.java new file mode 100644 index 0000000000..d7b8e371db --- /dev/null +++ b/core/src/main/java/com/graphhopper/routing/ev/TurnRestriction.java @@ -0,0 +1,32 @@ +/* + * Licensed to GraphHopper GmbH under one or more contributor + * license agreements. See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + * + * GraphHopper GmbH licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.graphhopper.routing.ev; + +import static com.graphhopper.routing.util.EncodingManager.getKey; + +public class TurnRestriction { + + public static String key(String prefix) { + return getKey(prefix, "turn_restriction"); + } + + public static BooleanEncodedValue create(String name) { + return new SimpleBooleanEncodedValue(key(name), false); + } +} diff --git a/core/src/main/java/com/graphhopper/routing/util/VehicleEncodedValues.java b/core/src/main/java/com/graphhopper/routing/util/VehicleEncodedValues.java index a6d1c602e4..675fc716e6 100644 --- a/core/src/main/java/com/graphhopper/routing/util/VehicleEncodedValues.java +++ b/core/src/main/java/com/graphhopper/routing/util/VehicleEncodedValues.java @@ -33,7 +33,8 @@ public class VehicleEncodedValues { private final BooleanEncodedValue accessEnc; private final DecimalEncodedValue avgSpeedEnc; private final DecimalEncodedValue priorityEnc; - private final DecimalEncodedValue turnCostEnc; + private final BooleanEncodedValue turnRestrictionEnc; + private final BooleanEncodedValue deadEndEnc; public static VehicleEncodedValues foot(PMap properties) { String name = properties.getString("name", "foot"); @@ -44,8 +45,9 @@ public static VehicleEncodedValues foot(PMap properties) { BooleanEncodedValue accessEnc = VehicleAccess.create(name); DecimalEncodedValue speedEnc = VehicleSpeed.create(name, speedBits, speedFactor, speedTwoDirections); DecimalEncodedValue priorityEnc = VehiclePriority.create(name, 4, PriorityCode.getFactor(1), false); - DecimalEncodedValue turnCostEnc = turnCosts ? TurnCost.create(name, 1) : null; - return new VehicleEncodedValues(name, accessEnc, speedEnc, priorityEnc, turnCostEnc); + BooleanEncodedValue turnRestrictionEnc = turnCosts ? TurnRestriction.create(name) : null; + BooleanEncodedValue deadEndEnc = turnCosts ? DeadEnd.create(name) : null; + return new VehicleEncodedValues(name, accessEnc, speedEnc, priorityEnc, turnRestrictionEnc, deadEndEnc); } public static VehicleEncodedValues wheelchair(PMap properties) { @@ -66,8 +68,9 @@ public static VehicleEncodedValues bike(PMap properties) { BooleanEncodedValue accessEnc = VehicleAccess.create(name); DecimalEncodedValue speedEnc = VehicleSpeed.create(name, speedBits, speedFactor, speedTwoDirections); DecimalEncodedValue priorityEnc = VehiclePriority.create(name, 4, PriorityCode.getFactor(1), false); - DecimalEncodedValue turnCostEnc = turnCosts ? TurnCost.create(name, 1) : null; - return new VehicleEncodedValues(name, accessEnc, speedEnc, priorityEnc, turnCostEnc); + BooleanEncodedValue turnRestrictionEnc = turnCosts ? TurnRestriction.create(name) : null; + BooleanEncodedValue deadEndEnc = turnCosts ? DeadEnd.create(name) : null; + return new VehicleEncodedValues(name, accessEnc, speedEnc, priorityEnc, turnRestrictionEnc, deadEndEnc); } public static VehicleEncodedValues racingbike(PMap properties) { @@ -85,8 +88,9 @@ public static VehicleEncodedValues car(PMap properties) { boolean turnCosts = properties.getBool("turn_costs", false); BooleanEncodedValue accessEnc = VehicleAccess.create(name); DecimalEncodedValue speedEnc = VehicleSpeed.create(name, speedBits, speedFactor, true); - DecimalEncodedValue turnCostEnc = turnCosts ? TurnCost.create(name, 1) : null; - return new VehicleEncodedValues(name, accessEnc, speedEnc, null, turnCostEnc); + BooleanEncodedValue turnRestrictionEnc = turnCosts ? TurnRestriction.create(name) : null; + BooleanEncodedValue deadEndEnc = turnCosts ? DeadEnd.create(name) : null; + return new VehicleEncodedValues(name, accessEnc, speedEnc, null, turnRestrictionEnc, deadEndEnc); } public static VehicleEncodedValues roads(PMap properties) { @@ -97,17 +101,19 @@ public static VehicleEncodedValues roads(PMap properties) { boolean turnCosts = properties.getBool("turn_costs", false); BooleanEncodedValue accessEnc = VehicleAccess.create(name); DecimalEncodedValue speedEnc = VehicleSpeed.create(name, speedBits, speedFactor, speedTwoDirections); - DecimalEncodedValue turnCostEnc = turnCosts ? TurnCost.create(name, 1) : null; - return new VehicleEncodedValues(name, accessEnc, speedEnc, null, turnCostEnc); + BooleanEncodedValue turnRestrictionEnc = turnCosts ? TurnRestriction.create(name) : null; + BooleanEncodedValue deadEndEnc = turnCosts ? DeadEnd.create(name) : null; + return new VehicleEncodedValues(name, accessEnc, speedEnc, null, turnRestrictionEnc, deadEndEnc); } public VehicleEncodedValues(String name, BooleanEncodedValue accessEnc, DecimalEncodedValue avgSpeedEnc, - DecimalEncodedValue priorityEnc, DecimalEncodedValue turnCostEnc) { + DecimalEncodedValue priorityEnc, BooleanEncodedValue turnRestrictionEnc, BooleanEncodedValue deadEndEnc) { this.name = name; this.accessEnc = accessEnc; this.avgSpeedEnc = avgSpeedEnc; this.priorityEnc = priorityEnc; - this.turnCostEnc = turnCostEnc; + this.turnRestrictionEnc = turnRestrictionEnc; + this.deadEndEnc = deadEndEnc; } public void createEncodedValues(List registerNewEncodedValue) { @@ -120,8 +126,10 @@ public void createEncodedValues(List registerNewEncodedValue) { } public void createTurnCostEncodedValues(List registerNewTurnCostEncodedValues) { - if (turnCostEnc != null) - registerNewTurnCostEncodedValues.add(turnCostEnc); + if (turnRestrictionEnc != null) + registerNewTurnCostEncodedValues.add(turnRestrictionEnc); + if (deadEndEnc != null) + registerNewTurnCostEncodedValues.add(deadEndEnc); } public BooleanEncodedValue getAccessEnc() { @@ -136,8 +144,12 @@ public DecimalEncodedValue getPriorityEnc() { return priorityEnc; } - public DecimalEncodedValue getTurnCostEnc() { - return turnCostEnc; + public BooleanEncodedValue getTurnRestrictionEnc() { + return turnRestrictionEnc; + } + + public BooleanEncodedValue getDeadEndEnc() { + return deadEndEnc; } public String getName() { diff --git a/core/src/main/java/com/graphhopper/routing/util/parsers/RestrictionSetter.java b/core/src/main/java/com/graphhopper/routing/util/parsers/RestrictionSetter.java index 16f22e2625..211227a8c2 100644 --- a/core/src/main/java/com/graphhopper/routing/util/parsers/RestrictionSetter.java +++ b/core/src/main/java/com/graphhopper/routing/util/parsers/RestrictionSetter.java @@ -23,6 +23,7 @@ import com.graphhopper.reader.osm.GraphRestriction; import com.graphhopper.reader.osm.Pair; import com.graphhopper.reader.osm.RestrictionType; +import com.graphhopper.routing.ev.BooleanEncodedValue; import com.graphhopper.routing.ev.DecimalEncodedValue; import com.graphhopper.storage.BaseGraph; import com.graphhopper.util.EdgeExplorer; @@ -52,14 +53,14 @@ public RestrictionSetter(BaseGraph baseGraph) { * Since we keep track of the added artificial edges here it is important to only use one RestrictionSetter instance * for **all** turn restrictions and vehicle types. */ - public void setRestrictions(List> restrictions, DecimalEncodedValue turnCostEnc) { + public void setRestrictions(List> restrictions, BooleanEncodedValue turnRestrictionEnc) { // we first need to add all the artificial edges, because we might need to restrict turns between artificial // edges created for different restrictions (when restrictions are overlapping) addArtificialEdges(restrictions); // now we can add all the via-way restrictions - addViaWayRestrictions(restrictions, turnCostEnc); + addViaWayRestrictions(restrictions, turnRestrictionEnc); // ... and finally all the via-node restrictions - addViaNodeRestrictions(restrictions, turnCostEnc); + addViaNodeRestrictions(restrictions, turnRestrictionEnc); } private void addArtificialEdges(List> restrictions) { @@ -82,7 +83,7 @@ private void addArtificialEdges(List> re } } - private void addViaWayRestrictions(List> restrictions, DecimalEncodedValue turnCostEnc) { + private void addViaWayRestrictions(List> restrictions, BooleanEncodedValue turnRestrictionEnc) { IntSet viaEdgesUsedByOnlyRestrictions = new IntHashSet(); for (Pair p : restrictions) { if (!p.first.isViaWayRestriction()) continue; @@ -101,17 +102,17 @@ private void addViaWayRestrictions(List> final int viaToToNode = p.first.getViaNodes().get(1); // never turn between an artificial edge and its corresponding real edge - restrictTurn(turnCostEnc, artificialVia, fromToViaNode, viaEdge); - restrictTurn(turnCostEnc, viaEdge, fromToViaNode, artificialVia); - restrictTurn(turnCostEnc, artificialVia, viaToToNode, viaEdge); - restrictTurn(turnCostEnc, viaEdge, viaToToNode, artificialVia); + restrictTurn(turnRestrictionEnc, artificialVia, fromToViaNode, viaEdge); + restrictTurn(turnRestrictionEnc, viaEdge, fromToViaNode, artificialVia); + restrictTurn(turnRestrictionEnc, artificialVia, viaToToNode, viaEdge); + restrictTurn(turnRestrictionEnc, viaEdge, viaToToNode, artificialVia); if (p.second == NO) { // This is how we implement via-way NO restrictions: we deny turning from the from-edge onto the via-edge, // but allow turning onto the artificial edge instead. Then we deny turning from the artificial edge onto // the to edge. - restrictTurn(turnCostEnc, fromEdge, fromToViaNode, viaEdge); - restrictTurn(turnCostEnc, artificialVia, viaToToNode, toEdge); + restrictTurn(turnRestrictionEnc, fromEdge, fromToViaNode, viaEdge); + restrictTurn(turnRestrictionEnc, artificialVia, viaToToNode, toEdge); } else if (p.second == ONLY) { // For via-way ONLY restrictions we have to turn from the from-edge onto the via-edge and from the via-edge // onto the to-edge, but only if we actually start at the from-edge. Therefore we enforce turning onto @@ -120,24 +121,24 @@ private void addViaWayRestrictions(List> EdgeIterator iter = edgeExplorer.setBaseNode(fromToViaNode); while (iter.next()) if (iter.getEdge() != fromEdge && iter.getEdge() != artificialVia) - restrictTurn(turnCostEnc, fromEdge, fromToViaNode, iter.getEdge()); + restrictTurn(turnRestrictionEnc, fromEdge, fromToViaNode, iter.getEdge()); iter = edgeExplorer.setBaseNode(viaToToNode); while (iter.next()) if (iter.getEdge() != artificialVia && iter.getEdge() != toEdge) - restrictTurn(turnCostEnc, artificialVia, viaToToNode, iter.getEdge()); + restrictTurn(turnRestrictionEnc, artificialVia, viaToToNode, iter.getEdge()); } else { throw new IllegalArgumentException("Unexpected restriction type: " + p.second); } // this is important for overlapping restrictions if (artificialFrom != fromEdge) - restrictTurn(turnCostEnc, artificialFrom, fromToViaNode, artificialVia); + restrictTurn(turnRestrictionEnc, artificialFrom, fromToViaNode, artificialVia); if (artificialTo != toEdge) - restrictTurn(turnCostEnc, artificialVia, viaToToNode, artificialTo); + restrictTurn(turnRestrictionEnc, artificialVia, viaToToNode, artificialTo); } } - private void addViaNodeRestrictions(List> restrictions, DecimalEncodedValue turnCostEnc) { + private void addViaNodeRestrictions(List> restrictions, BooleanEncodedValue turnRestrictionEnc) { for (Pair p : restrictions) { if (p.first.isViaWayRestriction()) continue; final int viaNode = p.first.getViaNodes().get(0); @@ -148,14 +149,14 @@ private void addViaNodeRestrictions(List final int artificialFrom = artificialEdgesByEdges.getOrDefault(fromEdge, fromEdge); final int artificialTo = artificialEdgesByEdges.getOrDefault(toEdge, toEdge); if (p.second == NO) { - restrictTurn(turnCostEnc, fromEdge, viaNode, toEdge); + restrictTurn(turnRestrictionEnc, fromEdge, viaNode, toEdge); // we also need to restrict this term in case there are artificial edges for the from- and/or to-edge if (artificialFrom != fromEdge) - restrictTurn(turnCostEnc, artificialFrom, viaNode, toEdge); + restrictTurn(turnRestrictionEnc, artificialFrom, viaNode, toEdge); if (artificialTo != toEdge) - restrictTurn(turnCostEnc, fromEdge, viaNode, artificialTo); + restrictTurn(turnRestrictionEnc, fromEdge, viaNode, artificialTo); if (artificialFrom != fromEdge && artificialTo != toEdge) - restrictTurn(turnCostEnc, artificialFrom, viaNode, artificialTo); + restrictTurn(turnRestrictionEnc, artificialFrom, viaNode, artificialTo); } else if (p.second == ONLY) { // we need to restrict all turns except the one, but that also means not restricting the // artificial counterparts of these turns, if they exist. @@ -163,10 +164,10 @@ private void addViaNodeRestrictions(List EdgeIterator iter = edgeExplorer.setBaseNode(viaNode); while (iter.next()) { if (iter.getEdge() != fromEdge && iter.getEdge() != toEdge && iter.getEdge() != artificialTo) - restrictTurn(turnCostEnc, fromEdge, viaNode, iter.getEdge()); + restrictTurn(turnRestrictionEnc, fromEdge, viaNode, iter.getEdge()); // and the same for the artificial edge belonging to the from-edge if it exists if (fromEdge != artificialFrom && iter.getEdge() != artificialFrom && iter.getEdge() != toEdge && iter.getEdge() != artificialTo) - restrictTurn(turnCostEnc, artificialFrom, viaNode, iter.getEdge()); + restrictTurn(turnRestrictionEnc, artificialFrom, viaNode, iter.getEdge()); } } else { throw new IllegalArgumentException("Unexpected restriction type: " + p.second); @@ -180,10 +181,10 @@ public IntIntMap getArtificialEdgesByEdges() { return artificialEdgesByEdges; } - private void restrictTurn(DecimalEncodedValue turnCostEnc, int fromEdge, int viaNode, int toEdge) { + private void restrictTurn(BooleanEncodedValue turnRestrictionEnc, int fromEdge, int viaNode, int toEdge) { if (fromEdge < 0 || toEdge < 0 || viaNode < 0) throw new IllegalArgumentException("from/toEdge and viaNode must be >= 0"); - baseGraph.getTurnCostStorage().set(turnCostEnc, fromEdge, viaNode, toEdge, Double.POSITIVE_INFINITY); + baseGraph.getTurnCostStorage().set(turnRestrictionEnc, fromEdge, viaNode, toEdge, true); } private static boolean ignoreViaWayRestriction(Pair p) { @@ -197,4 +198,4 @@ private static boolean ignoreViaWayRestriction(Pair(3-4) only_straight_on = (2-3)->(3-8) restricted // (4-3)->(3-8) no_right_turn = (4-3)->(3-8) restricted // (2-3)->(3-8) no_entry = (2-3)->(3-8) restricted - DecimalEncodedValue carTCEnc = hopper.getEncodingManager().getDecimalEncodedValue(TurnCost.key("car")); - assertTrue(tcStorage.get(carTCEnc, edge2_3, n3, edge3_8) > 0); - assertTrue(tcStorage.get(carTCEnc, edge4_3, n3, edge3_8) > 0); - assertTrue(tcStorage.get(carTCEnc, edge2_3, n3, edge3_8) > 0); - assertTrue(tcStorage.get(carTCEnc, edge2_3, n3, edge3_4) == 0); - assertTrue(tcStorage.get(carTCEnc, edge2_3, n3, edge3_2) == 0); - assertTrue(tcStorage.get(carTCEnc, edge2_3, n3, edge3_4) == 0); - assertTrue(tcStorage.get(carTCEnc, edge4_3, n3, edge3_2) == 0); - assertTrue(tcStorage.get(carTCEnc, edge8_3, n3, edge3_2) == 0); + BooleanEncodedValue carTCEnc = hopper.getEncodingManager().getBooleanEncodedValue(TurnRestriction.key("car")); + assertTrue(tcStorage.get(carTCEnc, edge2_3, n3, edge3_8)); + assertTrue(tcStorage.get(carTCEnc, edge4_3, n3, edge3_8)); + assertTrue(tcStorage.get(carTCEnc, edge2_3, n3, edge3_8)); + assertFalse(tcStorage.get(carTCEnc, edge2_3, n3, edge3_4)); + assertFalse(tcStorage.get(carTCEnc, edge2_3, n3, edge3_2)); + assertFalse(tcStorage.get(carTCEnc, edge2_3, n3, edge3_4)); + assertFalse(tcStorage.get(carTCEnc, edge4_3, n3, edge3_2)); + assertFalse(tcStorage.get(carTCEnc, edge8_3, n3, edge3_2)); // u-turn restriction for (6-1)->(1-6) but not for (1-6)->(6-1) - assertTrue(tcStorage.get(carTCEnc, edge1_6, n1, edge1_6) > 0); - assertTrue(tcStorage.get(carTCEnc, edge1_6, n6, edge1_6) == 0); + assertTrue(tcStorage.get(carTCEnc, edge1_6, n1, edge1_6)); + assertFalse(tcStorage.get(carTCEnc, edge1_6, n6, edge1_6)); int edge4_5 = GHUtility.getEdge(graph, n4, n5).getEdge(); int edge5_6 = GHUtility.getEdge(graph, n5, n6).getEdge(); int edge5_1 = GHUtility.getEdge(graph, n5, n1).getEdge(); // (4-5)->(5-1) right_turn_only = (4-5)->(5-6) restricted - assertTrue(tcStorage.get(carTCEnc, edge4_5, n5, edge5_6) == 0); - assertTrue(tcStorage.get(carTCEnc, edge4_5, n5, edge5_1) > 0); + assertFalse(tcStorage.get(carTCEnc, edge4_5, n5, edge5_6)); + assertTrue(tcStorage.get(carTCEnc, edge4_5, n5, edge5_1)); - DecimalEncodedValue bikeTCEnc = hopper.getEncodingManager().getDecimalEncodedValue(TurnCost.key("bike")); - assertTrue(tcStorage.get(bikeTCEnc, edge4_5, n5, edge5_6) == 0); + BooleanEncodedValue bikeTCEnc = hopper.getEncodingManager().getBooleanEncodedValue(TurnRestriction.key("bike")); + assertFalse(tcStorage.get(bikeTCEnc, edge4_5, n5, edge5_6)); int n10 = AbstractGraphStorageTester.getIdOf(graph, 40, 10); int n11 = AbstractGraphStorageTester.getIdOf(graph, 40, 11); @@ -577,12 +577,12 @@ public void testTurnRestrictionsFromXML() { int edge10_11 = GHUtility.getEdge(graph, n10, n11).getEdge(); int edge11_14 = GHUtility.getEdge(graph, n11, n14).getEdge(); - assertTrue(tcStorage.get(carTCEnc, edge11_14, n11, edge10_11) == 0); - assertTrue(tcStorage.get(bikeTCEnc, edge11_14, n11, edge10_11) == 0); + assertFalse(tcStorage.get(carTCEnc, edge11_14, n11, edge10_11)); + assertFalse(tcStorage.get(bikeTCEnc, edge11_14, n11, edge10_11)); // the turn is restricted for car even though it turns into a one-way, but we treat this separately now - assertTrue(tcStorage.get(carTCEnc, edge10_11, n11, edge11_14) > 0); - assertTrue(tcStorage.get(bikeTCEnc, edge10_11, n11, edge11_14) > 0); + assertTrue(tcStorage.get(carTCEnc, edge10_11, n11, edge11_14)); + assertTrue(tcStorage.get(bikeTCEnc, edge10_11, n11, edge11_14)); } @Test @@ -603,11 +603,11 @@ public void testTurnRestrictionsViaHgvTransportationMode() { int edge9_3 = GHUtility.getEdge(graph, n9, n3).getEdge(); int edge3_8 = GHUtility.getEdge(graph, n3, n8).getEdge(); - DecimalEncodedValue carTCEnc = hopper.getEncodingManager().getDecimalEncodedValue(TurnCost.key("car")); - DecimalEncodedValue roadsTCEnc = hopper.getEncodingManager().getDecimalEncodedValue(TurnCost.key("roads")); + BooleanEncodedValue carTCEnc = hopper.getEncodingManager().getBooleanEncodedValue(TurnRestriction.key("car")); + BooleanEncodedValue roadsTCEnc = hopper.getEncodingManager().getBooleanEncodedValue(TurnRestriction.key("roads")); - assertTrue(tcStorage.get(carTCEnc, edge9_3, n3, edge3_8) == 0); - assertTrue(tcStorage.get(roadsTCEnc, edge9_3, n3, edge3_8) > 0); + assertFalse(tcStorage.get(carTCEnc, edge9_3, n3, edge3_8)); + assertTrue(tcStorage.get(roadsTCEnc, edge9_3, n3, edge3_8)); } @Test @@ -711,9 +711,9 @@ public void testTurnFlagCombination() { ). importOrLoad(); EncodingManager manager = hopper.getEncodingManager(); - DecimalEncodedValue carTCEnc = manager.getDecimalEncodedValue(TurnCost.key("car")); - DecimalEncodedValue truckTCEnc = manager.getDecimalEncodedValue(TurnCost.key("truck")); - DecimalEncodedValue bikeTCEnc = manager.getDecimalEncodedValue(TurnCost.key("bike")); + BooleanEncodedValue carTCEnc = manager.getBooleanEncodedValue(TurnRestriction.key("car")); + BooleanEncodedValue truckTCEnc = manager.getBooleanEncodedValue(TurnRestriction.key("truck")); + BooleanEncodedValue bikeTCEnc = manager.getBooleanEncodedValue(TurnRestriction.key("bike")); Graph graph = hopper.getBaseGraph(); TurnCostStorage tcStorage = graph.getTurnCostStorage(); @@ -721,22 +721,22 @@ public void testTurnFlagCombination() { int edge1 = GHUtility.getEdge(graph, 1, 0).getEdge(); int edge2 = GHUtility.getEdge(graph, 0, 2).getEdge(); // the 2nd entry provides turn flags for bike only - assertTrue(Double.isInfinite(tcStorage.get(carTCEnc, edge1, 0, edge2))); - assertTrue(Double.isInfinite(tcStorage.get(truckTCEnc, edge1, 0, edge2))); - assertEquals(0, tcStorage.get(bikeTCEnc, edge1, 0, edge2), .1); + assertTrue(tcStorage.get(carTCEnc, edge1, 0, edge2)); + assertTrue(tcStorage.get(truckTCEnc, edge1, 0, edge2)); + assertFalse(tcStorage.get(bikeTCEnc, edge1, 0, edge2)); edge1 = GHUtility.getEdge(graph, 2, 0).getEdge(); edge2 = GHUtility.getEdge(graph, 0, 3).getEdge(); // the first entry provides turn flags for car and foot only - assertEquals(0, tcStorage.get(carTCEnc, edge1, 0, edge2), .1); - assertEquals(0, tcStorage.get(truckTCEnc, edge1, 0, edge2), .1); - assertTrue(Double.isInfinite(tcStorage.get(bikeTCEnc, edge1, 0, edge2))); + assertFalse(tcStorage.get(carTCEnc, edge1, 0, edge2)); + assertFalse(tcStorage.get(truckTCEnc, edge1, 0, edge2)); + assertTrue(tcStorage.get(bikeTCEnc, edge1, 0, edge2)); edge1 = GHUtility.getEdge(graph, 3, 0).getEdge(); edge2 = GHUtility.getEdge(graph, 0, 2).getEdge(); - assertEquals(0, tcStorage.get(carTCEnc, edge1, 0, edge2), .1); - assertTrue(Double.isInfinite(tcStorage.get(truckTCEnc, edge1, 0, edge2))); - assertEquals(0, tcStorage.get(bikeTCEnc, edge1, 0, edge2), .1); + assertFalse(tcStorage.get(carTCEnc, edge1, 0, edge2)); + assertTrue(tcStorage.get(truckTCEnc, edge1, 0, edge2)); + assertFalse(tcStorage.get(bikeTCEnc, edge1, 0, edge2)); } @Test @@ -775,37 +775,37 @@ public void testConditionalTurnRestriction() { // (2-3)->(3-4) only_straight_on except bicycle = (2-3)->(3-8) restricted for car // (4-3)->(3-8) no_right_turn dedicated to motorcar = (4-3)->(3-8) restricted for car - DecimalEncodedValue carTCEnc = hopper.getEncodingManager().getDecimalEncodedValue(TurnCost.key("car")); - assertTrue(tcStorage.get(carTCEnc, edge2_3, n3, edge3_8) > 0); - assertTrue(tcStorage.get(carTCEnc, edge4_3, n3, edge3_8) > 0); - assertEquals(0, tcStorage.get(carTCEnc, edge2_3, n3, edge3_4), .1); - assertEquals(0, tcStorage.get(carTCEnc, edge2_3, n3, edge3_2), .1); - assertEquals(0, tcStorage.get(carTCEnc, edge2_3, n3, edge3_4), .1); - assertEquals(0, tcStorage.get(carTCEnc, edge4_3, n3, edge3_2), .1); - assertEquals(0, tcStorage.get(carTCEnc, edge8_3, n3, edge3_2), .1); - - DecimalEncodedValue bikeTCEnc = hopper.getEncodingManager().getDecimalEncodedValue(TurnCost.key("bike")); - assertEquals(0, tcStorage.get(bikeTCEnc, edge2_3, n3, edge3_8), .1); - assertEquals(0, tcStorage.get(bikeTCEnc, edge4_3, n3, edge3_8), .1); - assertEquals(0, tcStorage.get(bikeTCEnc, edge2_3, n3, edge3_4), .1); - assertEquals(0, tcStorage.get(bikeTCEnc, edge2_3, n3, edge3_2), .1); - assertEquals(0, tcStorage.get(bikeTCEnc, edge2_3, n3, edge3_4), .1); - assertEquals(0, tcStorage.get(bikeTCEnc, edge4_3, n3, edge3_2), .1); - assertEquals(0, tcStorage.get(bikeTCEnc, edge8_3, n3, edge3_2), .1); + BooleanEncodedValue carTCEnc = hopper.getEncodingManager().getBooleanEncodedValue(TurnRestriction.key("car")); + assertTrue(tcStorage.get(carTCEnc, edge2_3, n3, edge3_8)); + assertTrue(tcStorage.get(carTCEnc, edge4_3, n3, edge3_8)); + assertFalse(tcStorage.get(carTCEnc, edge2_3, n3, edge3_4)); + assertFalse(tcStorage.get(carTCEnc, edge2_3, n3, edge3_2)); + assertFalse(tcStorage.get(carTCEnc, edge2_3, n3, edge3_4)); + assertFalse(tcStorage.get(carTCEnc, edge4_3, n3, edge3_2)); + assertFalse(tcStorage.get(carTCEnc, edge8_3, n3, edge3_2)); + + BooleanEncodedValue bikeTCEnc = hopper.getEncodingManager().getBooleanEncodedValue(TurnRestriction.key("bike")); + assertFalse(tcStorage.get(bikeTCEnc, edge2_3, n3, edge3_8)); + assertFalse(tcStorage.get(bikeTCEnc, edge4_3, n3, edge3_8)); + assertFalse(tcStorage.get(bikeTCEnc, edge2_3, n3, edge3_4)); + assertFalse(tcStorage.get(bikeTCEnc, edge2_3, n3, edge3_2)); + assertFalse(tcStorage.get(bikeTCEnc, edge2_3, n3, edge3_4)); + assertFalse(tcStorage.get(bikeTCEnc, edge4_3, n3, edge3_2)); + assertFalse(tcStorage.get(bikeTCEnc, edge8_3, n3, edge3_2)); // u-turn except bus;bicycle restriction for (6-1)->(1-6) but not for (1-6)->(6-1) - assertTrue(tcStorage.get(carTCEnc, edge1_6, n1, edge1_6) > 0); - assertEquals(0, tcStorage.get(carTCEnc, edge1_6, n6, edge1_6), .1); + assertTrue(tcStorage.get(carTCEnc, edge1_6, n1, edge1_6)); + assertFalse(tcStorage.get(carTCEnc, edge1_6, n6, edge1_6)); - assertEquals(0, tcStorage.get(bikeTCEnc, edge1_6, n1, edge1_6), .1); - assertEquals(0, tcStorage.get(bikeTCEnc, edge1_6, n6, edge1_6), .1); + assertFalse(tcStorage.get(bikeTCEnc, edge1_6, n1, edge1_6)); + assertFalse(tcStorage.get(bikeTCEnc, edge1_6, n6, edge1_6)); // (4-5)->(5-6) right_turn_only dedicated to motorcar = (4-5)->(5-1) restricted - assertEquals(0, tcStorage.get(carTCEnc, edge4_5, n5, edge5_6), .1); - assertTrue(tcStorage.get(carTCEnc, edge4_5, n5, edge5_1) > 0); + assertFalse(tcStorage.get(carTCEnc, edge4_5, n5, edge5_6)); + assertTrue(tcStorage.get(carTCEnc, edge4_5, n5, edge5_1)); - assertEquals(0, tcStorage.get(bikeTCEnc, edge4_5, n5, edge5_6), .1); - assertEquals(0, tcStorage.get(bikeTCEnc, edge4_5, n5, edge5_1), .1); + assertFalse(tcStorage.get(bikeTCEnc, edge4_5, n5, edge5_6)); + assertFalse(tcStorage.get(bikeTCEnc, edge4_5, n5, edge5_1)); } @Test @@ -831,24 +831,24 @@ public void testMultipleTurnRestrictions() { int edge4_5 = GHUtility.getEdge(graph, n4, n5).getEdge(); int edge5_1 = GHUtility.getEdge(graph, n5, n1).getEdge(); - DecimalEncodedValue carTCEnc = hopper.getEncodingManager().getDecimalEncodedValue(TurnCost.key("car")); - DecimalEncodedValue bikeTCEnc = hopper.getEncodingManager().getDecimalEncodedValue(TurnCost.key("bike")); + BooleanEncodedValue carTCEnc = hopper.getEncodingManager().getBooleanEncodedValue(TurnRestriction.key("car")); + BooleanEncodedValue bikeTCEnc = hopper.getEncodingManager().getBooleanEncodedValue(TurnRestriction.key("bike")); // (1-2)->(2-3) no_right_turn for motorcar and bus - assertTrue(tcStorage.get(carTCEnc, edge1_2, n2, edge2_3) > 0); - assertEquals(0, tcStorage.get(bikeTCEnc, edge1_2, n2, edge2_3), .1); + assertTrue(tcStorage.get(carTCEnc, edge1_2, n2, edge2_3)); + assertFalse(tcStorage.get(bikeTCEnc, edge1_2, n2, edge2_3)); // (3-4)->(4-5) no_right_turn for motorcycle and motorcar - assertTrue(Double.isInfinite(tcStorage.get(carTCEnc, edge3_4, n4, edge4_5))); - assertEquals(0, tcStorage.get(bikeTCEnc, edge3_4, n4, edge4_5), .1); + assertTrue(tcStorage.get(carTCEnc, edge3_4, n4, edge4_5)); + assertFalse(tcStorage.get(bikeTCEnc, edge3_4, n4, edge4_5)); // (5-1)->(1-2) no_right_turn for bus and psv except for motorcar and bicycle - assertEquals(0, tcStorage.get(carTCEnc, edge4_5, n5, edge5_1), .1); - assertEquals(0, tcStorage.get(bikeTCEnc, edge4_5, n5, edge5_1), .1); + assertFalse(tcStorage.get(carTCEnc, edge4_5, n5, edge5_1)); + assertFalse(tcStorage.get(bikeTCEnc, edge4_5, n5, edge5_1)); // (5-1)->(1-2) no_right_turn for motorcar and motorcycle except for bus and bicycle - assertTrue(Double.isInfinite(tcStorage.get(carTCEnc, edge5_1, n1, edge1_2))); - assertEquals(0, tcStorage.get(bikeTCEnc, edge5_1, n1, edge1_2), .1); + assertTrue(tcStorage.get(carTCEnc, edge5_1, n1, edge1_2)); + assertFalse(tcStorage.get(bikeTCEnc, edge5_1, n1, edge1_2)); } @Test diff --git a/core/src/test/java/com/graphhopper/routing/util/parsers/RestrictionSetterTest.java b/core/src/test/java/com/graphhopper/routing/util/parsers/RestrictionSetterTest.java index 10ef0a2bed..ce946ac0c8 100644 --- a/core/src/test/java/com/graphhopper/routing/util/parsers/RestrictionSetterTest.java +++ b/core/src/test/java/com/graphhopper/routing/util/parsers/RestrictionSetterTest.java @@ -5,10 +5,7 @@ import com.graphhopper.reader.osm.Pair; import com.graphhopper.reader.osm.RestrictionType; import com.graphhopper.routing.Dijkstra; -import com.graphhopper.routing.ev.DecimalEncodedValue; -import com.graphhopper.routing.ev.DecimalEncodedValueImpl; -import com.graphhopper.routing.ev.EncodedValue; -import com.graphhopper.routing.ev.TurnCost; +import com.graphhopper.routing.ev.*; import com.graphhopper.routing.util.EncodingManager; import com.graphhopper.routing.util.TraversalMode; import com.graphhopper.routing.weighting.SpeedWeighting; @@ -46,9 +43,9 @@ void viaNode_no() { edge(2, 4); edge(3, 4); GraphRestriction graphRestriction = GraphRestriction.node(a, 1, b); - DecimalEncodedValue turnCostEnc = createTurnCostEnc("car"); - r.setRestrictions(Arrays.asList(new Pair<>(graphRestriction, RestrictionType.NO)), turnCostEnc); - assertEquals(nodes(0, 1, 3, 4, 2), calcPath(0, 2, turnCostEnc)); + BooleanEncodedValue turnRestrictionEnc = createTurnRestrictionEnc("car"); + r.setRestrictions(Arrays.asList(new Pair<>(graphRestriction, RestrictionType.NO)), turnRestrictionEnc); + assertEquals(nodes(0, 1, 3, 4, 2), calcPath(0, 2, turnRestrictionEnc)); } @Test @@ -62,9 +59,9 @@ void viaNode_only() { edge(2, 4); edge(3, 4); GraphRestriction graphRestriction = GraphRestriction.node(a, 1, b); - DecimalEncodedValue turnCostEnc = createTurnCostEnc("car"); - r.setRestrictions(Arrays.asList(new Pair<>(graphRestriction, RestrictionType.ONLY)), turnCostEnc); - assertEquals(nodes(0, 1, 2, 4, 3), calcPath(0, 3, turnCostEnc)); + BooleanEncodedValue turnRestrictionEnc = createTurnRestrictionEnc("car"); + r.setRestrictions(Arrays.asList(new Pair<>(graphRestriction, RestrictionType.ONLY)), turnRestrictionEnc); + assertEquals(nodes(0, 1, 2, 4, 3), calcPath(0, 3, turnRestrictionEnc)); } @Test @@ -86,15 +83,15 @@ void viaWay_no() { edge(6, 9); edge(8, 9); GraphRestriction graphRestriction = GraphRestriction.way(a, b, c, nodes(1, 2)); - DecimalEncodedValue turnCostEnc = createTurnCostEnc("car"); + BooleanEncodedValue turnRestrictionEnc = createTurnRestrictionEnc("car"); r.setRestrictions(Arrays.asList( new Pair<>(graphRestriction, RestrictionType.NO) - ), turnCostEnc); + ), turnRestrictionEnc); // turning from a to b and then to c is not allowed - assertEquals(nodes(0, 1, 5, 8, 9, 6, 2, 3), calcPath(0, 3, turnCostEnc)); + assertEquals(nodes(0, 1, 5, 8, 9, 6, 2, 3), calcPath(0, 3, turnRestrictionEnc)); // turning from a to b, or b to c is still allowed - assertEquals(nodes(0, 1, 2, 4), calcPath(0, 4, turnCostEnc)); - assertEquals(nodes(5, 1, 2, 3), calcPath(5, 3, turnCostEnc)); + assertEquals(nodes(0, 1, 2, 4), calcPath(0, 4, turnRestrictionEnc)); + assertEquals(nodes(5, 1, 2, 3), calcPath(5, 3, turnRestrictionEnc)); } @Test @@ -111,21 +108,21 @@ void viaWay_no_withOverlap() { int t = edge(2, 6); int u = edge(3, 7); - DecimalEncodedValue turnCostEnc = createTurnCostEnc("car"); + BooleanEncodedValue turnRestrictionEnc = createTurnRestrictionEnc("car"); r.setRestrictions(Arrays.asList( new Pair<>(GraphRestriction.way(a, b, c, nodes(1, 2)), RestrictionType.NO), new Pair<>(GraphRestriction.way(b, c, d, nodes(2, 3)), RestrictionType.NO) - ), turnCostEnc); + ), turnRestrictionEnc); - assertEquals(NO_PATH, calcPath(0, 3, turnCostEnc)); // a-b-c - assertEquals(nodes(0, 1, 2, 6), calcPath(0, 6, turnCostEnc)); // a-b-t - assertEquals(nodes(5, 1, 2, 3), calcPath(5, 3, turnCostEnc)); // s-b-c - assertEquals(nodes(5, 1, 2, 6), calcPath(5, 6, turnCostEnc)); // s-b-t + assertEquals(NO_PATH, calcPath(0, 3, turnRestrictionEnc)); // a-b-c + assertEquals(nodes(0, 1, 2, 6), calcPath(0, 6, turnRestrictionEnc)); // a-b-t + assertEquals(nodes(5, 1, 2, 3), calcPath(5, 3, turnRestrictionEnc)); // s-b-c + assertEquals(nodes(5, 1, 2, 6), calcPath(5, 6, turnRestrictionEnc)); // s-b-t - assertEquals(NO_PATH, calcPath(1, 4, turnCostEnc)); // b-c-d - assertEquals(nodes(1, 2, 3, 7), calcPath(1, 7, turnCostEnc)); // b-c-u - assertEquals(nodes(6, 2, 3, 4), calcPath(6, 4, turnCostEnc)); // t-c-d - assertEquals(nodes(6, 2, 3, 7), calcPath(6, 7, turnCostEnc)); // t-c-u + assertEquals(NO_PATH, calcPath(1, 4, turnRestrictionEnc)); // b-c-d + assertEquals(nodes(1, 2, 3, 7), calcPath(1, 7, turnRestrictionEnc)); // b-c-u + assertEquals(nodes(6, 2, 3, 4), calcPath(6, 4, turnRestrictionEnc)); // t-c-d + assertEquals(nodes(6, 2, 3, 7), calcPath(6, 7, turnRestrictionEnc)); // t-c-u } @Test @@ -150,7 +147,7 @@ void viaWay_no_withOverlap_more_complex() { edge(7, 10); edge(8, 11); edge(10, 11); - DecimalEncodedValue turnCostEnc = createTurnCostEnc("car"); + BooleanEncodedValue turnRestrictionEnc = createTurnRestrictionEnc("car"); r.setRestrictions(Arrays.asList( new Pair<>(GraphRestriction.node(t, 4, d), RestrictionType.NO), new Pair<>(GraphRestriction.node(s, 3, a), RestrictionType.NO), @@ -158,13 +155,13 @@ void viaWay_no_withOverlap_more_complex() { new Pair<>(GraphRestriction.way(b, c, d, nodes(7, 8)), RestrictionType.NO), new Pair<>(GraphRestriction.way(c, d, a, nodes(8, 4)), RestrictionType.NO), new Pair<>(GraphRestriction.way(d, a, b, nodes(4, 3)), RestrictionType.NO) - ), turnCostEnc); + ), turnRestrictionEnc); - assertEquals(nodes(0, 3, 7, 8, 9), calcPath(0, 9, turnCostEnc)); - assertEquals(nodes(5, 4, 3, 7, 10, 11, 8, 9), calcPath(5, 9, turnCostEnc)); - assertEquals(nodes(5, 4, 3, 2), calcPath(5, 2, turnCostEnc)); - assertEquals(nodes(0, 3, 7, 10), calcPath(0, 10, turnCostEnc)); - assertEquals(nodes(6, 7, 8, 9), calcPath(6, 9, turnCostEnc)); + assertEquals(nodes(0, 3, 7, 8, 9), calcPath(0, 9, turnRestrictionEnc)); + assertEquals(nodes(5, 4, 3, 7, 10, 11, 8, 9), calcPath(5, 9, turnRestrictionEnc)); + assertEquals(nodes(5, 4, 3, 2), calcPath(5, 2, turnRestrictionEnc)); + assertEquals(nodes(0, 3, 7, 10), calcPath(0, 10, turnRestrictionEnc)); + assertEquals(nodes(6, 7, 8, 9), calcPath(6, 9, turnRestrictionEnc)); } @Test @@ -183,22 +180,22 @@ void viaWay_only() { int e = edge(4, 5); int f = edge(5, 7); int g = edge(5, 6); - DecimalEncodedValue turnCostEnc = createTurnCostEnc("car"); + BooleanEncodedValue turnRestrictionEnc = createTurnRestrictionEnc("car"); r.setRestrictions(Arrays.asList( new Pair<>(GraphRestriction.way(a, d, f, nodes(2, 5)), RestrictionType.ONLY), // we add a few more restrictions, because that happens a lot in real data new Pair<>(GraphRestriction.way(c, d, g, nodes(2, 5)), RestrictionType.NO), new Pair<>(GraphRestriction.node(e, 5, f), RestrictionType.NO) - ), turnCostEnc); + ), turnRestrictionEnc); // following the restriction is allowed of course - assertEquals(nodes(1, 2, 5, 7), calcPath(1, 7, turnCostEnc)); + assertEquals(nodes(1, 2, 5, 7), calcPath(1, 7, turnRestrictionEnc)); // taking another turn at the beginning is not allowed - assertEquals(nodes(), calcPath(1, 3, turnCostEnc)); + assertEquals(nodes(), calcPath(1, 3, turnRestrictionEnc)); // taking another turn after the first turn is not allowed either - assertEquals(nodes(), calcPath(1, 4, turnCostEnc)); + assertEquals(nodes(), calcPath(1, 4, turnRestrictionEnc)); // coming from somewhere we can go anywhere - assertEquals(nodes(0, 2, 5, 6), calcPath(0, 6, turnCostEnc)); - assertEquals(nodes(0, 2, 5, 7), calcPath(0, 7, turnCostEnc)); + assertEquals(nodes(0, 2, 5, 6), calcPath(0, 6, turnRestrictionEnc)); + assertEquals(nodes(0, 2, 5, 7), calcPath(0, 7, turnRestrictionEnc)); } @Test @@ -212,7 +209,7 @@ void viaWay_only_twoRestrictionsSharingSameVia() { int c = edge(1, 2); int d = edge(2, 3); int e = edge(2, 4); - DecimalEncodedValue turnCostEnc = createTurnCostEnc("car"); + BooleanEncodedValue turnRestrictionEnc = createTurnRestrictionEnc("car"); assertThrows(IllegalStateException.class, () -> r.setRestrictions(Arrays.asList( // These are two 'only' via-way restrictions that share the same via way. A real-world example can // be found in Rüdesheim am Rhein where vehicles either have to go straight or enter the ferry depending @@ -220,18 +217,18 @@ void viaWay_only_twoRestrictionsSharingSameVia() { // We have to make sure such cases are ignored already when we parse the OSM data. new Pair<>(GraphRestriction.way(a, c, d, nodes(1, 2)), RestrictionType.ONLY), new Pair<>(GraphRestriction.way(b, c, e, nodes(1, 2)), RestrictionType.ONLY) - ), turnCostEnc) + ), turnRestrictionEnc) ); } - private static DecimalEncodedValue createTurnCostEnc(String name) { - DecimalEncodedValue turnCostEnc = TurnCost.create(name, 1); - turnCostEnc.init(new EncodedValue.InitializerConfig()); - return turnCostEnc; + private static BooleanEncodedValue createTurnRestrictionEnc(String name) { + BooleanEncodedValue turnRestrictionEnc = TurnRestriction.create(name); + turnRestrictionEnc.init(new EncodedValue.InitializerConfig()); + return turnRestrictionEnc; } - private IntArrayList calcPath(int from, int to, DecimalEncodedValue turnCostEnc) { - return new IntArrayList(new Dijkstra(graph, new SpeedWeighting(speedEnc, turnCostEnc, graph.getTurnCostStorage(), Double.POSITIVE_INFINITY), TraversalMode.EDGE_BASED).calcPath(from, to).calcNodes()); + private IntArrayList calcPath(int from, int to, BooleanEncodedValue turnRestrictionEnc) { + return new IntArrayList(new Dijkstra(graph, new SpeedWeighting(speedEnc, turnRestrictionEnc, graph.getTurnCostStorage(), Double.POSITIVE_INFINITY), TraversalMode.EDGE_BASED).calcPath(from, to).calcNodes()); } private IntArrayList nodes(int... nodes) { From e9c8f2c3ad4f9451ae638db36da66e12b05e4dcf Mon Sep 17 00:00:00 2001 From: easbar Date: Wed, 13 Sep 2023 17:16:18 +0200 Subject: [PATCH 09/22] continue --- .../java/com/graphhopper/GraphHopper.java | 6 +- .../routing/DefaultWeightingFactory.java | 6 +- .../routing/ch/CHPreparationGraph.java | 65 ++----------------- .../weighting/DefaultTurnCostProvider.java | 20 +++--- .../routing/weighting/SpeedWeighting.java | 40 ++++++++---- .../graphhopper/storage/TurnCostStorage.java | 15 +++-- .../java/com/graphhopper/util/GHUtility.java | 7 +- .../routing/AlternativeRouteTest.java | 4 +- .../PrepareRoutingSubnetworksTest.java | 2 +- .../util/parsers/CarTagParserTest.java | 1 - .../util/parsers/RestrictionSetterTest.java | 14 +++- .../weighting/FastestWeightingTest.java | 30 ++++----- .../weighting/custom/CustomWeightingTest.java | 30 ++++----- .../com/graphhopper/tools/Measurement.java | 7 +- .../graphhopper/resources/InfoResource.java | 2 +- 15 files changed, 109 insertions(+), 140 deletions(-) diff --git a/core/src/main/java/com/graphhopper/GraphHopper.java b/core/src/main/java/com/graphhopper/GraphHopper.java index 7e04c0dfca..9833c2334b 100644 --- a/core/src/main/java/com/graphhopper/GraphHopper.java +++ b/core/src/main/java/com/graphhopper/GraphHopper.java @@ -1098,10 +1098,10 @@ private void checkProfilesConsistency() { if (!encodingManager.getVehicles().contains(profile.getVehicle())) throw new IllegalArgumentException("Unknown vehicle '" + profile.getVehicle() + "' in profile: " + profile + ". " + "Available vehicles: " + String.join(",", encodingManager.getVehicles())); - DecimalEncodedValue turnCostEnc = encodingManager.hasEncodedValue(TurnCost.key(profile.getVehicle())) - ? encodingManager.getDecimalEncodedValue(TurnCost.key(profile.getVehicle())) + BooleanEncodedValue turnRestrictionEnc = encodingManager.hasEncodedValue(TurnRestriction.key(profile.getVehicle())) + ? encodingManager.getBooleanEncodedValue(TurnRestriction.key(profile.getVehicle())) : null; - if (profile.isTurnCosts() && turnCostEnc == null) { + if (profile.isTurnCosts() && turnRestrictionEnc == null) { throw new IllegalArgumentException("The profile '" + profile.getName() + "' was configured with " + "'turn_costs=true', but the corresponding vehicle '" + profile.getVehicle() + "' does not support turn costs." + "\nYou need to add `|turn_costs=true` to the vehicle in `graph.vehicles`"); diff --git a/core/src/main/java/com/graphhopper/routing/DefaultWeightingFactory.java b/core/src/main/java/com/graphhopper/routing/DefaultWeightingFactory.java index b2fa69e8a3..f881810b39 100644 --- a/core/src/main/java/com/graphhopper/routing/DefaultWeightingFactory.java +++ b/core/src/main/java/com/graphhopper/routing/DefaultWeightingFactory.java @@ -58,11 +58,11 @@ public Weighting createWeighting(Profile profile, PMap requestHints, boolean dis final String vehicle = profile.getVehicle(); TurnCostProvider turnCostProvider; if (profile.isTurnCosts() && !disableTurnCosts) { - DecimalEncodedValue turnCostEnc = encodingManager.getDecimalEncodedValue(TurnCost.key(vehicle)); - if (turnCostEnc == null) + BooleanEncodedValue turnRestrictionEnc = encodingManager.getBooleanEncodedValue(TurnRestriction.key(vehicle)); + if (turnRestrictionEnc == null) throw new IllegalArgumentException("Vehicle " + vehicle + " does not support turn costs"); int uTurnCosts = hints.getInt(Parameters.Routing.U_TURN_COSTS, INFINITE_U_TURN_COSTS); - turnCostProvider = new DefaultTurnCostProvider(turnCostEnc, graph.getTurnCostStorage(), uTurnCosts); + turnCostProvider = new DefaultTurnCostProvider(turnRestrictionEnc, graph.getTurnCostStorage(), uTurnCosts); } else { turnCostProvider = NO_TURN_COST_PROVIDER; } diff --git a/core/src/main/java/com/graphhopper/routing/ch/CHPreparationGraph.java b/core/src/main/java/com/graphhopper/routing/ch/CHPreparationGraph.java index a8adb44a6a..cfd8743065 100644 --- a/core/src/main/java/com/graphhopper/routing/ch/CHPreparationGraph.java +++ b/core/src/main/java/com/graphhopper/routing/ch/CHPreparationGraph.java @@ -18,19 +18,15 @@ package com.graphhopper.routing.ch; -import com.carrotsearch.hppc.*; +import com.carrotsearch.hppc.IntArrayList; +import com.carrotsearch.hppc.IntContainer; +import com.carrotsearch.hppc.IntScatterSet; +import com.carrotsearch.hppc.IntSet; import com.carrotsearch.hppc.sorting.IndirectComparator; import com.carrotsearch.hppc.sorting.IndirectSort; -import com.graphhopper.routing.ev.DecimalEncodedValue; import com.graphhopper.routing.util.AllEdgesIterator; -import com.graphhopper.routing.weighting.AbstractWeighting; -import com.graphhopper.routing.weighting.DefaultTurnCostProvider; -import com.graphhopper.routing.weighting.TurnCostProvider; import com.graphhopper.routing.weighting.Weighting; import com.graphhopper.storage.Graph; -import com.graphhopper.storage.TurnCostStorage; -import com.graphhopper.util.BitUtil; -import com.graphhopper.util.EdgeIterator; import com.graphhopper.util.GHUtility; import static com.graphhopper.util.ArrayUtil.zero; @@ -113,57 +109,8 @@ public static void buildFromGraph(CHPreparationGraph prepareGraph, Graph graph, * that storing all turn costs in separate arrays upfront speeds up edge-based CH preparation by about 25%. See #2084 */ public static TurnCostFunction buildTurnCostFunctionFromTurnCostStorage(Graph graph, Weighting weighting) { - if (!(weighting instanceof AbstractWeighting)) - return weighting::calcTurnWeight; - TurnCostProvider turnCostProvider = ((AbstractWeighting) weighting).getTurnCostProvider(); - if (!(turnCostProvider instanceof DefaultTurnCostProvider)) - return weighting::calcTurnWeight; - DecimalEncodedValue turnCostEnc = ((DefaultTurnCostProvider) turnCostProvider).getTurnCostEnc(); - TurnCostStorage turnCostStorage = graph.getTurnCostStorage(); - // we maintain a list of inEdge/outEdge/turn-cost triples (we use two arrays for this) that is sorted by nodes - LongArrayList turnCostEdgePairs = new LongArrayList(); - DoubleArrayList turnCosts = new DoubleArrayList(); - // for each node we store the index of the first turn cost entry/triple in the list - final int[] turnCostNodes = new int[graph.getNodes() + 1]; - TurnCostStorage.Iterator tcIter = turnCostStorage.getAllTurnCosts(); - int lastNode = -1; - while (tcIter.next()) { - int viaNode = tcIter.getViaNode(); - if (viaNode < lastNode) - throw new IllegalStateException(); - long edgePair = BitUtil.LITTLE.toLong(tcIter.getFromEdge(), tcIter.getToEdge()); - // note that as long as we only use OSM turn restrictions all the turn costs are infinite anyway - double turnCost = tcIter.getCost(turnCostEnc); - int index = turnCostEdgePairs.size(); - turnCostEdgePairs.add(edgePair); - turnCosts.add(turnCost); - if (viaNode != lastNode) { - for (int i = lastNode + 1; i <= viaNode; i++) { - turnCostNodes[i] = index; - } - } - lastNode = viaNode; - } - for (int i = lastNode + 1; i <= turnCostNodes.length - 1; i++) { - turnCostNodes[i] = turnCostEdgePairs.size(); - } - turnCostNodes[turnCostNodes.length - 1] = turnCostEdgePairs.size(); - - // currently the u-turn costs are the same for all junctions, so for now we just get them for one of them - double uTurnCosts = weighting.calcTurnWeight(1, 0, 1); - return (inEdge, viaNode, outEdge) -> { - if (!EdgeIterator.Edge.isValid(inEdge) || !EdgeIterator.Edge.isValid(outEdge)) - return 0; - else if (inEdge == outEdge) - return uTurnCosts; - // traverse all turn cost entries we have for this viaNode and return the turn costs if we find a match - for (int i = turnCostNodes[viaNode]; i < turnCostNodes[viaNode + 1]; i++) { - long l = turnCostEdgePairs.get(i); - if (inEdge == BitUtil.LITTLE.getIntLow(l) && outEdge == BitUtil.LITTLE.getIntHigh(l)) - return turnCosts.get(i); - } - return 0; - }; + // todonow: removed this optimization for now. it would be much better if we could do this internally in turn cost storage + return weighting::calcTurnWeight; } public int getNodes() { diff --git a/core/src/main/java/com/graphhopper/routing/weighting/DefaultTurnCostProvider.java b/core/src/main/java/com/graphhopper/routing/weighting/DefaultTurnCostProvider.java index 7f996014c1..299dd04a65 100644 --- a/core/src/main/java/com/graphhopper/routing/weighting/DefaultTurnCostProvider.java +++ b/core/src/main/java/com/graphhopper/routing/weighting/DefaultTurnCostProvider.java @@ -18,27 +18,27 @@ package com.graphhopper.routing.weighting; -import com.graphhopper.routing.ev.DecimalEncodedValue; +import com.graphhopper.routing.ev.BooleanEncodedValue; import com.graphhopper.storage.TurnCostStorage; import com.graphhopper.util.EdgeIterator; import static com.graphhopper.routing.weighting.Weighting.INFINITE_U_TURN_COSTS; public class DefaultTurnCostProvider implements TurnCostProvider { - private final DecimalEncodedValue turnCostEnc; + private final BooleanEncodedValue turnRestrictionEnc; private final TurnCostStorage turnCostStorage; private final int uTurnCostsInt; private final double uTurnCosts; - public DefaultTurnCostProvider(DecimalEncodedValue turnCostEnc, TurnCostStorage turnCostStorage) { - this(turnCostEnc, turnCostStorage, Weighting.INFINITE_U_TURN_COSTS); + public DefaultTurnCostProvider(BooleanEncodedValue turnRestrictionEnc, TurnCostStorage turnCostStorage) { + this(turnRestrictionEnc, turnCostStorage, Weighting.INFINITE_U_TURN_COSTS); } /** * @param uTurnCosts the costs of a u-turn in seconds, for {@link Weighting#INFINITE_U_TURN_COSTS} the u-turn costs * will be infinite */ - public DefaultTurnCostProvider(DecimalEncodedValue turnCostEnc, TurnCostStorage turnCostStorage, int uTurnCosts) { + public DefaultTurnCostProvider(BooleanEncodedValue turnRestrictionEnc, TurnCostStorage turnCostStorage, int uTurnCosts) { if (uTurnCosts < 0 && uTurnCosts != INFINITE_U_TURN_COSTS) { throw new IllegalArgumentException("u-turn costs must be positive, or equal to " + INFINITE_U_TURN_COSTS + " (=infinite costs)"); } @@ -48,12 +48,12 @@ public DefaultTurnCostProvider(DecimalEncodedValue turnCostEnc, TurnCostStorage throw new IllegalArgumentException("No storage set to calculate turn weight"); } // if null the TurnCostProvider can be still useful for edge-based routing - this.turnCostEnc = turnCostEnc; + this.turnRestrictionEnc = turnRestrictionEnc; this.turnCostStorage = turnCostStorage; } - public DecimalEncodedValue getTurnCostEnc() { - return turnCostEnc; + public BooleanEncodedValue getTurnRestrictionEnc() { + return turnRestrictionEnc; } @Override @@ -66,8 +66,8 @@ public double calcTurnWeight(int edgeFrom, int nodeVia, int edgeTo) { // note that the u-turn costs overwrite any turn costs set in TurnCostStorage tCost = uTurnCosts; } else { - if (turnCostEnc != null) - tCost = turnCostStorage.get(turnCostEnc, edgeFrom, nodeVia, edgeTo); + if (turnRestrictionEnc != null) + tCost = turnCostStorage.get(turnRestrictionEnc, edgeFrom, nodeVia, edgeTo) ? Double.POSITIVE_INFINITY : 0; } return tCost; } diff --git a/core/src/main/java/com/graphhopper/routing/weighting/SpeedWeighting.java b/core/src/main/java/com/graphhopper/routing/weighting/SpeedWeighting.java index 403311ad79..9e2f7cd19a 100644 --- a/core/src/main/java/com/graphhopper/routing/weighting/SpeedWeighting.java +++ b/core/src/main/java/com/graphhopper/routing/weighting/SpeedWeighting.java @@ -25,20 +25,37 @@ public class SpeedWeighting implements Weighting { private final DecimalEncodedValue speedEnc; - private final DecimalEncodedValue turnCostEnc; - private final TurnCostStorage turnCostStorage; - private final double uTurnCosts; + private final TurnCostProvider turnCostProvider; public SpeedWeighting(DecimalEncodedValue speedEnc) { - this(speedEnc, null, null, 0); + this(speedEnc, TurnCostProvider.NO_TURN_COST_PROVIDER); } public SpeedWeighting(DecimalEncodedValue speedEnc, DecimalEncodedValue turnCostEnc, TurnCostStorage turnCostStorage, double uTurnCosts) { + if (turnCostStorage == null || turnCostEnc == null) + throw new IllegalArgumentException("This SpeedWeighting constructor expects turnCostEnc and turnCostStorage to be != null"); if (uTurnCosts < 0) throw new IllegalArgumentException("u-turn costs must be positive"); this.speedEnc = speedEnc; - this.turnCostEnc = turnCostEnc; - this.turnCostStorage = turnCostStorage; - this.uTurnCosts = uTurnCosts; + this.turnCostProvider = new TurnCostProvider() { + @Override + public double calcTurnWeight(int inEdge, int viaNode, int outEdge) { + if (!EdgeIterator.Edge.isValid(inEdge) || !EdgeIterator.Edge.isValid(outEdge)) + return 0; + if (inEdge == outEdge) + return Math.max(turnCostStorage.get(turnCostEnc, inEdge, viaNode, outEdge), uTurnCosts); + else return turnCostStorage.get(turnCostEnc, inEdge, viaNode, outEdge); + } + + @Override + public long calcTurnMillis(int inEdge, int viaNode, int outEdge) { + return 0; + } + }; + } + + public SpeedWeighting(DecimalEncodedValue speedEnc, TurnCostProvider turnCostProvider) { + this.speedEnc = speedEnc; + this.turnCostProvider = turnCostProvider; } @Override @@ -60,12 +77,7 @@ public long calcEdgeMillis(EdgeIteratorState edgeState, boolean reverse) { @Override public double calcTurnWeight(int inEdge, int viaNode, int outEdge) { - if (turnCostEnc == null) return 0; - if (!EdgeIterator.Edge.isValid(inEdge) || !EdgeIterator.Edge.isValid(outEdge)) - return 0; - if (inEdge == outEdge) - return Math.max(turnCostStorage.get(turnCostEnc, inEdge, viaNode, outEdge), uTurnCosts); - else return turnCostStorage.get(turnCostEnc, inEdge, viaNode, outEdge); + return turnCostProvider.calcTurnWeight(inEdge, viaNode, outEdge); } @Override @@ -75,7 +87,7 @@ public long calcTurnMillis(int inEdge, int viaNode, int outEdge) { @Override public boolean hasTurnCosts() { - return turnCostEnc != null; + return turnCostProvider != TurnCostProvider.NO_TURN_COST_PROVIDER; } @Override diff --git a/core/src/main/java/com/graphhopper/storage/TurnCostStorage.java b/core/src/main/java/com/graphhopper/storage/TurnCostStorage.java index 7b2da19419..8d694bcf3f 100644 --- a/core/src/main/java/com/graphhopper/storage/TurnCostStorage.java +++ b/core/src/main/java/com/graphhopper/storage/TurnCostStorage.java @@ -116,11 +116,8 @@ private long findOrCreateTurnCostEntry(int fromEdge, int viaNode, int toEdge) { return pointer; } - /** - * @return the turn cost of the viaNode when going from "fromEdge" to "toEdge" - */ - public double get(DecimalEncodedValue turnCostEnc, int fromEdge, int viaNode, int toEdge) { - return turnCostEnc.getDecimal(false, -1, createIntAccess(findPointer(fromEdge, viaNode, toEdge))); + public double get(DecimalEncodedValue dev, int fromEdge, int viaNode, int toEdge) { + return dev.getDecimal(false, -1, createIntAccess(findPointer(fromEdge, viaNode, toEdge))); } public boolean get(BooleanEncodedValue bev, int fromEdge, int viaNode, int toEdge) { @@ -193,6 +190,8 @@ public interface Iterator { int getToEdge(); + boolean get(BooleanEncodedValue booleanEncodedValue); + double getCost(DecimalEncodedValue encodedValue); boolean next(); @@ -223,6 +222,12 @@ public int getToEdge() { return turnCosts.getInt(turnCostPtr() + TC_TO); } + @Override + public boolean get(BooleanEncodedValue booleanEncodedValue) { + intsRef.ints[0] = turnCosts.getInt(turnCostPtr() + TC_FLAGS); + return booleanEncodedValue.getBool(false, -1, edgeIntAccess); + } + @Override public double getCost(DecimalEncodedValue encodedValue) { intsRef.ints[0] = turnCosts.getInt(turnCostPtr() + TC_FLAGS); diff --git a/core/src/main/java/com/graphhopper/util/GHUtility.java b/core/src/main/java/com/graphhopper/util/GHUtility.java index d73054b170..e43ef73a4e 100644 --- a/core/src/main/java/com/graphhopper/util/GHUtility.java +++ b/core/src/main/java/com/graphhopper/util/GHUtility.java @@ -567,9 +567,10 @@ public static double calcWeightWithTurnWeight(Weighting weighting, EdgeIteratorS */ public static long calcMillisWithTurnMillis(Weighting weighting, EdgeIteratorState edgeState, boolean reverse, int prevOrNextEdgeId) { long edgeMillis = weighting.calcEdgeMillis(edgeState, reverse); - if (!EdgeIterator.Edge.isValid(prevOrNextEdgeId)) { + if (edgeMillis == Long.MAX_VALUE) + return edgeMillis; + if (!EdgeIterator.Edge.isValid(prevOrNextEdgeId)) return edgeMillis; - } // should we also separate weighting vs. time for turn? E.g. a fast but dangerous turn - is this common? // todo: why no first/last orig edge here as in calcWeight ? // final int origEdgeId = reverse ? edgeState.getOrigEdgeLast() : edgeState.getOrigEdgeFirst(); @@ -577,6 +578,8 @@ public static long calcMillisWithTurnMillis(Weighting weighting, EdgeIteratorSta long turnMillis = reverse ? weighting.calcTurnMillis(origEdgeId, edgeState.getBaseNode(), prevOrNextEdgeId) : weighting.calcTurnMillis(prevOrNextEdgeId, edgeState.getBaseNode(), origEdgeId); + if (turnMillis == Long.MAX_VALUE) + return turnMillis; return edgeMillis + turnMillis; } diff --git a/core/src/test/java/com/graphhopper/routing/AlternativeRouteTest.java b/core/src/test/java/com/graphhopper/routing/AlternativeRouteTest.java index cd8df5b96c..ed4196b784 100644 --- a/core/src/test/java/com/graphhopper/routing/AlternativeRouteTest.java +++ b/core/src/test/java/com/graphhopper/routing/AlternativeRouteTest.java @@ -56,7 +56,9 @@ public Fixture(TraversalMode tMode) { EncodingManager em = EncodingManager.start().add(speedEnc).add(turnCostEnc).build(); graph = new BaseGraph.Builder(em).withTurnCosts(true).create(); - weighting = new SpeedWeighting(speedEnc, tMode.isEdgeBased() ? turnCostEnc : null, graph.getTurnCostStorage(), Double.POSITIVE_INFINITY); + weighting = tMode.isEdgeBased() + ? new SpeedWeighting(speedEnc, turnCostEnc, graph.getTurnCostStorage(), Double.POSITIVE_INFINITY) + : new SpeedWeighting(speedEnc); } @Override diff --git a/core/src/test/java/com/graphhopper/routing/subnetwork/PrepareRoutingSubnetworksTest.java b/core/src/test/java/com/graphhopper/routing/subnetwork/PrepareRoutingSubnetworksTest.java index cce4e66efc..2a8c65a1c3 100644 --- a/core/src/test/java/com/graphhopper/routing/subnetwork/PrepareRoutingSubnetworksTest.java +++ b/core/src/test/java/com/graphhopper/routing/subnetwork/PrepareRoutingSubnetworksTest.java @@ -249,7 +249,7 @@ private static IntArrayList getSubnetworkEdges(BaseGraph graph, BooleanEncodedVa } private static PrepareRoutingSubnetworks.PrepareJob createJob(BooleanEncodedValue subnetworkEnc, DecimalEncodedValue speedEnc) { - return createJob(subnetworkEnc, speedEnc, null, null, 0); + return new PrepareRoutingSubnetworks.PrepareJob(subnetworkEnc, new SpeedWeighting(speedEnc)); } private static PrepareRoutingSubnetworks.PrepareJob createJob(BooleanEncodedValue subnetworkEnc, DecimalEncodedValue speedEnc, diff --git a/core/src/test/java/com/graphhopper/routing/util/parsers/CarTagParserTest.java b/core/src/test/java/com/graphhopper/routing/util/parsers/CarTagParserTest.java index 7236c0bd2e..6cb932e56d 100644 --- a/core/src/test/java/com/graphhopper/routing/util/parsers/CarTagParserTest.java +++ b/core/src/test/java/com/graphhopper/routing/util/parsers/CarTagParserTest.java @@ -690,7 +690,6 @@ public void testIssue_1256() { EncodingManager lowFactorEm = new EncodingManager.Builder() .add(new SimpleBooleanEncodedValue(VehicleAccess.key("car"), true)) .add(lowFactorSpeedEnc) - .addTurnCostEncodedValue(TurnCost.create(TurnCost.key("car"), 1)) .build(); edgeIntAccess = new ArrayEdgeIntAccess(lowFactorEm.getIntsForFlags()); new CarAverageSpeedParser(lowFactorEm, new PMap()).handleWayTags(edgeId, edgeIntAccess, way); diff --git a/core/src/test/java/com/graphhopper/routing/util/parsers/RestrictionSetterTest.java b/core/src/test/java/com/graphhopper/routing/util/parsers/RestrictionSetterTest.java index ce946ac0c8..2166e9d027 100644 --- a/core/src/test/java/com/graphhopper/routing/util/parsers/RestrictionSetterTest.java +++ b/core/src/test/java/com/graphhopper/routing/util/parsers/RestrictionSetterTest.java @@ -9,6 +9,7 @@ import com.graphhopper.routing.util.EncodingManager; import com.graphhopper.routing.util.TraversalMode; import com.graphhopper.routing.weighting.SpeedWeighting; +import com.graphhopper.routing.weighting.TurnCostProvider; import com.graphhopper.storage.BaseGraph; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -228,7 +229,18 @@ private static BooleanEncodedValue createTurnRestrictionEnc(String name) { } private IntArrayList calcPath(int from, int to, BooleanEncodedValue turnRestrictionEnc) { - return new IntArrayList(new Dijkstra(graph, new SpeedWeighting(speedEnc, turnRestrictionEnc, graph.getTurnCostStorage(), Double.POSITIVE_INFINITY), TraversalMode.EDGE_BASED).calcPath(from, to).calcNodes()); + return new IntArrayList(new Dijkstra(graph, new SpeedWeighting(speedEnc, new TurnCostProvider() { + @Override + public double calcTurnWeight(int inEdge, int viaNode, int outEdge) { + if (inEdge == outEdge) return Double.POSITIVE_INFINITY; + return graph.getTurnCostStorage().get(turnRestrictionEnc, inEdge, viaNode, outEdge) ? Double.POSITIVE_INFINITY : 0; + } + + @Override + public long calcTurnMillis(int inEdge, int viaNode, int outEdge) { + return Double.isInfinite(calcTurnWeight(inEdge, viaNode, outEdge)) ? Long.MAX_VALUE : 0L; + } + }), TraversalMode.EDGE_BASED).calcPath(from, to).calcNodes()); } private IntArrayList nodes(int... nodes) { diff --git a/core/src/test/java/com/graphhopper/routing/weighting/FastestWeightingTest.java b/core/src/test/java/com/graphhopper/routing/weighting/FastestWeightingTest.java index 4aeff12c77..5011517e23 100644 --- a/core/src/test/java/com/graphhopper/routing/weighting/FastestWeightingTest.java +++ b/core/src/test/java/com/graphhopper/routing/weighting/FastestWeightingTest.java @@ -39,8 +39,8 @@ public class FastestWeightingTest { private final BooleanEncodedValue accessEnc = new SimpleBooleanEncodedValue("access", true); private final DecimalEncodedValue speedEnc = new DecimalEncodedValueImpl("speed", 5, 5, false); - private final DecimalEncodedValue turnCostEnc = TurnCost.create("car", 10); - private final EncodingManager encodingManager = EncodingManager.start().add(accessEnc).add(speedEnc).addTurnCostEncodedValue(turnCostEnc).build(); + private final BooleanEncodedValue turnRestrictionEnc = TurnRestriction.create("car"); + private final EncodingManager encodingManager = EncodingManager.start().add(accessEnc).add(speedEnc).addTurnCostEncodedValue(turnRestrictionEnc).build(); private final BaseGraph graph = new BaseGraph.Builder(encodingManager).create(); @Test @@ -108,19 +108,18 @@ public void testTime() { @Test public void calcWeightAndTime_withTurnCosts() { BaseGraph graph = new BaseGraph.Builder(encodingManager).withTurnCosts(true).create(); - Weighting weighting = new FastestWeighting(accessEnc, speedEnc, new DefaultTurnCostProvider(turnCostEnc, graph.getTurnCostStorage())); + Weighting weighting = new FastestWeighting(accessEnc, speedEnc, new DefaultTurnCostProvider(turnRestrictionEnc, graph.getTurnCostStorage())); GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(0, 1).setDistance(100)); EdgeIteratorState edge = GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(1, 2).setDistance(100)); - // turn costs are given in seconds - setTurnCost(graph, 0, 1, 2, 5); - assertEquals(6 + 5, GHUtility.calcWeightWithTurnWeight(weighting, edge, false, 0), 1.e-6); - assertEquals(6000 + 5000, GHUtility.calcMillisWithTurnMillis(weighting, edge, false, 0), 1.e-6); + setTurnRestriction(graph, 0, 1, 2); + assertTrue(Double.isInfinite(GHUtility.calcWeightWithTurnWeight(weighting, edge, false, 0))); + assertEquals(Long.MAX_VALUE, GHUtility.calcMillisWithTurnMillis(weighting, edge, false, 0)); } @Test public void calcWeightAndTime_uTurnCosts() { BaseGraph graph = new BaseGraph.Builder(encodingManager).withTurnCosts(true).create(); - Weighting weighting = new FastestWeighting(accessEnc, speedEnc, new DefaultTurnCostProvider(turnCostEnc, graph.getTurnCostStorage(), 40)); + Weighting weighting = new FastestWeighting(accessEnc, speedEnc, new DefaultTurnCostProvider(turnRestrictionEnc, graph.getTurnCostStorage(), 40)); EdgeIteratorState edge = GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(0, 1).setDistance(100)); assertEquals(6 + 40, GHUtility.calcWeightWithTurnWeight(weighting, edge, false, 0), 1.e-6); assertEquals((6 + 40) * 1000, GHUtility.calcMillisWithTurnMillis(weighting, edge, false, 0), 1.e-6); @@ -130,15 +129,12 @@ public void calcWeightAndTime_uTurnCosts() { public void calcWeightAndTime_withTurnCosts_shortest() { BaseGraph graph = new BaseGraph.Builder(encodingManager).withTurnCosts(true).create(); Weighting weighting = new ShortestWeighting(accessEnc, speedEnc, - new DefaultTurnCostProvider(turnCostEnc, graph.getTurnCostStorage())); + new DefaultTurnCostProvider(turnRestrictionEnc, graph.getTurnCostStorage())); GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(0, 1).setDistance(100)); EdgeIteratorState edge = GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(1, 2).setDistance(100)); - // turn costs are given in seconds - setTurnCost(graph, 0, 1, 2, 5); - // todo: for the shortest weighting turn costs cannot be interpreted as seconds? at least when they are added - // to the weight? how much should they contribute ? -// assertEquals(105, AbstractWeighting.calcWeightWithTurnWeight(weighting, edge, false, 0), 1.e-6); - assertEquals(6000 + 5000, GHUtility.calcMillisWithTurnMillis(weighting, edge, false, 0), 1.e-6); + setTurnRestriction(graph, 0, 1, 2); + assertTrue(Double.isInfinite(GHUtility.calcWeightWithTurnWeight(weighting, edge, false, 0))); + assertEquals(Long.MAX_VALUE, GHUtility.calcMillisWithTurnMillis(weighting, edge, false, 0)); } @Test @@ -204,8 +200,8 @@ public void testPrivateTag() { assertEquals(240, bikeWeighting.calcEdgeWeight(edge, false), 1.e-6); } - private void setTurnCost(Graph graph, int from, int via, int to, double turnCost) { - graph.getTurnCostStorage().set(turnCostEnc, getEdge(graph, from, via).getEdge(), via, getEdge(graph, via, to).getEdge(), turnCost); + private void setTurnRestriction(Graph graph, int from, int via, int to) { + graph.getTurnCostStorage().set(turnRestrictionEnc, getEdge(graph, from, via).getEdge(), via, getEdge(graph, via, to).getEdge(), true); } } diff --git a/core/src/test/java/com/graphhopper/routing/weighting/custom/CustomWeightingTest.java b/core/src/test/java/com/graphhopper/routing/weighting/custom/CustomWeightingTest.java index 655257c1fc..942715ac2e 100644 --- a/core/src/test/java/com/graphhopper/routing/weighting/custom/CustomWeightingTest.java +++ b/core/src/test/java/com/graphhopper/routing/weighting/custom/CustomWeightingTest.java @@ -31,7 +31,7 @@ class CustomWeightingTest { DecimalEncodedValue maxSpeedEnc; EnumEncodedValue roadClassEnc; EncodingManager encodingManager; - DecimalEncodedValue turnCostEnc = TurnCost.create("car", 10); + BooleanEncodedValue turnRestrictionEnc = TurnRestriction.create("car"); @BeforeEach public void setup() { @@ -41,7 +41,7 @@ public void setup() { .add(Toll.create()) .add(Hazmat.create()) .add(RouteNetwork.create(BikeNetwork.KEY)) - .addTurnCostEncodedValue(turnCostEnc) + .addTurnCostEncodedValue(turnRestrictionEnc) .build(); maxSpeedEnc = encodingManager.getDecimalEncodedValue(MaxSpeed.KEY); roadClassEnc = encodingManager.getEnumEncodedValue(KEY, RoadClass.class); @@ -409,20 +409,19 @@ public void testTime() { public void calcWeightAndTime_withTurnCosts() { BaseGraph graph = new BaseGraph.Builder(encodingManager).withTurnCosts(true).create(); Weighting weighting = CustomModelParser.createWeighting(accessEnc, avSpeedEnc, null, encodingManager, - new DefaultTurnCostProvider(turnCostEnc, graph.getTurnCostStorage()), new CustomModel()); + new DefaultTurnCostProvider(turnRestrictionEnc, graph.getTurnCostStorage()), new CustomModel()); GHUtility.setSpeed(60, true, true, accessEnc, avSpeedEnc, graph.edge(0, 1).setDistance(100)); EdgeIteratorState edge = GHUtility.setSpeed(60, true, true, accessEnc, avSpeedEnc, graph.edge(1, 2).setDistance(100)); - // turn costs are given in seconds - setTurnCost(graph, 0, 1, 2, 5); - assertEquals(6 + 5, GHUtility.calcWeightWithTurnWeight(weighting, edge, false, 0), 1.e-6); - assertEquals(6000 + 5000, GHUtility.calcMillisWithTurnMillis(weighting, edge, false, 0), 1.e-6); + setTurnRestriction(graph, 0, 1, 2); + assertTrue(Double.isInfinite(GHUtility.calcWeightWithTurnWeight(weighting, edge, false, 0))); + assertEquals(Long.MAX_VALUE, GHUtility.calcMillisWithTurnMillis(weighting, edge, false, 0)); } @Test public void calcWeightAndTime_uTurnCosts() { BaseGraph graph = new BaseGraph.Builder(encodingManager).withTurnCosts(true).create(); Weighting weighting = CustomModelParser.createWeighting(accessEnc, avSpeedEnc, null, - encodingManager, new DefaultTurnCostProvider(turnCostEnc, graph.getTurnCostStorage(), 40), new CustomModel()); + encodingManager, new DefaultTurnCostProvider(turnRestrictionEnc, graph.getTurnCostStorage(), 40), new CustomModel()); EdgeIteratorState edge = GHUtility.setSpeed(60, true, true, accessEnc, avSpeedEnc, graph.edge(0, 1).setDistance(100)); assertEquals(6 + 40, GHUtility.calcWeightWithTurnWeight(weighting, edge, false, 0), 1.e-6); assertEquals((6 + 40) * 1000, GHUtility.calcMillisWithTurnMillis(weighting, edge, false, 0), 1.e-6); @@ -432,15 +431,12 @@ public void calcWeightAndTime_uTurnCosts() { public void calcWeightAndTime_withTurnCosts_shortest() { BaseGraph graph = new BaseGraph.Builder(encodingManager).withTurnCosts(true).create(); Weighting weighting = new ShortestWeighting(accessEnc, avSpeedEnc, - new DefaultTurnCostProvider(turnCostEnc, graph.getTurnCostStorage())); + new DefaultTurnCostProvider(turnRestrictionEnc, graph.getTurnCostStorage())); GHUtility.setSpeed(60, true, true, accessEnc, avSpeedEnc, graph.edge(0, 1).setDistance(100)); EdgeIteratorState edge = GHUtility.setSpeed(60, true, true, accessEnc, avSpeedEnc, graph.edge(1, 2).setDistance(100)); - // turn costs are given in seconds - setTurnCost(graph, 0, 1, 2, 5); - // todo: for the shortest weighting turn costs cannot be interpreted as seconds? at least when they are added - // to the weight? how much should they contribute ? -// assertEquals(105, AbstractWeighting.calcWeightWithTurnWeight(weighting, edge, false, 0), 1.e-6); - assertEquals(6000 + 5000, GHUtility.calcMillisWithTurnMillis(weighting, edge, false, 0), 1.e-6); + setTurnRestriction(graph, 0, 1, 2); + assertTrue(Double.isInfinite(GHUtility.calcWeightWithTurnWeight(weighting, edge, false, 0))); + assertEquals(Long.MAX_VALUE, GHUtility.calcMillisWithTurnMillis(weighting, edge, false, 0)); } @Test @@ -508,7 +504,7 @@ public void testPrivateTag() { assertEquals(240, bikeWeighting.calcEdgeWeight(edge, false), .01); } - private void setTurnCost(Graph graph, int from, int via, int to, double turnCost) { - graph.getTurnCostStorage().set(turnCostEnc, getEdge(graph, from, via).getEdge(), via, getEdge(graph, via, to).getEdge(), turnCost); + private void setTurnRestriction(Graph graph, int from, int via, int to) { + graph.getTurnCostStorage().set(turnRestrictionEnc, getEdge(graph, from, via).getEdge(), via, getEdge(graph, via, to).getEdge(), true); } } diff --git a/tools/src/main/java/com/graphhopper/tools/Measurement.java b/tools/src/main/java/com/graphhopper/tools/Measurement.java index 40e35494f3..6cfe627789 100644 --- a/tools/src/main/java/com/graphhopper/tools/Measurement.java +++ b/tools/src/main/java/com/graphhopper/tools/Measurement.java @@ -27,10 +27,7 @@ import com.graphhopper.config.Profile; import com.graphhopper.jackson.Jackson; import com.graphhopper.routing.ch.PrepareContractionHierarchies; -import com.graphhopper.routing.ev.BooleanEncodedValue; -import com.graphhopper.routing.ev.Subnetwork; -import com.graphhopper.routing.ev.TurnCost; -import com.graphhopper.routing.ev.VehicleAccess; +import com.graphhopper.routing.ev.*; import com.graphhopper.routing.lm.LMConfig; import com.graphhopper.routing.lm.PrepareLandmarks; import com.graphhopper.routing.util.*; @@ -178,7 +175,7 @@ protected void importOSM() { BaseGraph g = hopper.getBaseGraph(); EncodingManager encodingManager = hopper.getEncodingManager(); BooleanEncodedValue accessEnc = encodingManager.getBooleanEncodedValue(VehicleAccess.key(vehicle)); - boolean withTurnCosts = encodingManager.hasEncodedValue(TurnCost.key(vehicle)); + boolean withTurnCosts = encodingManager.hasEncodedValue(TurnRestriction.key(vehicle)); StopWatch sw = new StopWatch().start(); try { diff --git a/web-bundle/src/main/java/com/graphhopper/resources/InfoResource.java b/web-bundle/src/main/java/com/graphhopper/resources/InfoResource.java index bd4400353c..f59725ad30 100644 --- a/web-bundle/src/main/java/com/graphhopper/resources/InfoResource.java +++ b/web-bundle/src/main/java/com/graphhopper/resources/InfoResource.java @@ -94,7 +94,7 @@ public Info getInfo() { Info.ProfileData profileData = new Info.ProfileData(p.getName()); info.profiles.add(profileData); defaultHiddenEVs.addAll(Arrays.asList(VehiclePriority.key(p.getName()), VehicleAccess.key(p.getName()), - VehicleSpeed.key(p.getName()), TurnCost.key(p.getName()), Subnetwork.key(p.getName()))); + VehicleSpeed.key(p.getName()), TurnRestriction.key(p.getName()), Subnetwork.key(p.getName()))); } if (config.has("gtfs.file")) info.profiles.add(new Info.ProfileData("pt")); From 564ab4d080176f6ade8fa325fc82f74f1614759f Mon Sep 17 00:00:00 2001 From: easbar Date: Wed, 13 Sep 2023 17:33:17 +0200 Subject: [PATCH 10/22] update --- .../com/graphhopper/reader/osm/RestrictionTagParser.java | 1 - .../graphhopper/routing/util/parsers/RestrictionSetter.java | 6 ++++-- .../com/graphhopper/routing/weighting/SpeedWeighting.java | 5 +++-- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/core/src/main/java/com/graphhopper/reader/osm/RestrictionTagParser.java b/core/src/main/java/com/graphhopper/reader/osm/RestrictionTagParser.java index 3b79b049be..05ac5c4907 100644 --- a/core/src/main/java/com/graphhopper/reader/osm/RestrictionTagParser.java +++ b/core/src/main/java/com/graphhopper/reader/osm/RestrictionTagParser.java @@ -19,7 +19,6 @@ package com.graphhopper.reader.osm; import com.graphhopper.routing.ev.BooleanEncodedValue; -import com.graphhopper.routing.ev.DecimalEncodedValue; import java.util.*; import java.util.stream.Collectors; diff --git a/core/src/main/java/com/graphhopper/routing/util/parsers/RestrictionSetter.java b/core/src/main/java/com/graphhopper/routing/util/parsers/RestrictionSetter.java index 211227a8c2..37b00dda72 100644 --- a/core/src/main/java/com/graphhopper/routing/util/parsers/RestrictionSetter.java +++ b/core/src/main/java/com/graphhopper/routing/util/parsers/RestrictionSetter.java @@ -18,13 +18,15 @@ package com.graphhopper.routing.util.parsers; -import com.carrotsearch.hppc.*; +import com.carrotsearch.hppc.IntHashSet; +import com.carrotsearch.hppc.IntIntHashMap; +import com.carrotsearch.hppc.IntIntMap; +import com.carrotsearch.hppc.IntSet; import com.carrotsearch.hppc.cursors.IntCursor; import com.graphhopper.reader.osm.GraphRestriction; import com.graphhopper.reader.osm.Pair; import com.graphhopper.reader.osm.RestrictionType; import com.graphhopper.routing.ev.BooleanEncodedValue; -import com.graphhopper.routing.ev.DecimalEncodedValue; import com.graphhopper.storage.BaseGraph; import com.graphhopper.util.EdgeExplorer; import com.graphhopper.util.EdgeIterator; diff --git a/core/src/main/java/com/graphhopper/routing/weighting/SpeedWeighting.java b/core/src/main/java/com/graphhopper/routing/weighting/SpeedWeighting.java index 9e2f7cd19a..4868be96bf 100644 --- a/core/src/main/java/com/graphhopper/routing/weighting/SpeedWeighting.java +++ b/core/src/main/java/com/graphhopper/routing/weighting/SpeedWeighting.java @@ -43,12 +43,13 @@ public double calcTurnWeight(int inEdge, int viaNode, int outEdge) { return 0; if (inEdge == outEdge) return Math.max(turnCostStorage.get(turnCostEnc, inEdge, viaNode, outEdge), uTurnCosts); - else return turnCostStorage.get(turnCostEnc, inEdge, viaNode, outEdge); + else + return turnCostStorage.get(turnCostEnc, inEdge, viaNode, outEdge); } @Override public long calcTurnMillis(int inEdge, int viaNode, int outEdge) { - return 0; + throw new UnsupportedOperationException(); } }; } From ff9a8d89ef8a9b996aab998515f98b7282b53244 Mon Sep 17 00:00:00 2001 From: easbar Date: Wed, 13 Sep 2023 17:44:03 +0200 Subject: [PATCH 11/22] Use boolean turn restriction enc instead of decimal turn cost enc --- .../java/com/graphhopper/GraphHopper.java | 14 +- .../com/graphhopper/reader/osm/OSMReader.java | 2 +- .../reader/osm/RestrictionTagParser.java | 12 +- .../routing/DefaultWeightingFactory.java | 6 +- .../routing/ch/CHPreparationGraph.java | 65 +------- .../routing/ev/TurnRestriction.java | 32 ++++ .../routing/util/VehicleEncodedValues.java | 30 ++-- .../util/parsers/RestrictionSetter.java | 55 +++---- .../weighting/DefaultTurnCostProvider.java | 20 +-- .../routing/weighting/SpeedWeighting.java | 43 ++++-- .../graphhopper/storage/TurnCostStorage.java | 27 +++- .../java/com/graphhopper/util/GHUtility.java | 7 +- .../graphhopper/reader/osm/OSMReaderTest.java | 140 +++++++++--------- .../routing/AlternativeRouteTest.java | 4 +- .../PrepareRoutingSubnetworksTest.java | 2 +- .../util/parsers/CarTagParserTest.java | 1 - .../util/parsers/RestrictionSetterTest.java | 103 +++++++------ .../weighting/FastestWeightingTest.java | 30 ++-- .../weighting/custom/CustomWeightingTest.java | 30 ++-- .../com/graphhopper/tools/Measurement.java | 7 +- .../graphhopper/resources/InfoResource.java | 2 +- 21 files changed, 323 insertions(+), 309 deletions(-) create mode 100644 core/src/main/java/com/graphhopper/routing/ev/TurnRestriction.java diff --git a/core/src/main/java/com/graphhopper/GraphHopper.java b/core/src/main/java/com/graphhopper/GraphHopper.java index 9db86a2886..9833c2334b 100644 --- a/core/src/main/java/com/graphhopper/GraphHopper.java +++ b/core/src/main/java/com/graphhopper/GraphHopper.java @@ -694,14 +694,14 @@ protected OSMParsers buildOSMParsers(Map vehiclesByName, List new OSMFootNetworkTagParser(encodingManager.getEnumEncodedValue(FootNetwork.KEY, RouteNetwork.class), relConfig)); } - String turnCostKey = TurnCost.key(new PMap(vehicleStr).getString("name", name)); - if (encodingManager.hasEncodedValue(turnCostKey) + String turnRestrictionKey = TurnRestriction.key(new PMap(vehicleStr).getString("name", name)); + if (encodingManager.hasEncodedValue(turnRestrictionKey) // need to make sure we do not add the same restriction parsers multiple times - && osmParsers.getRestrictionTagParsers().stream().noneMatch(r -> r.getTurnCostEnc().getName().equals(turnCostKey))) { + && osmParsers.getRestrictionTagParsers().stream().noneMatch(r -> r.getTurnRestrictionEnc().getName().equals(turnRestrictionKey))) { List restrictions = tagParser instanceof AbstractAccessParser ? ((AbstractAccessParser) tagParser).getRestrictions() : OSMRoadAccessParser.toOSMRestrictions(TransportationMode.valueOf(new PMap(vehicleStr).getString("transportation_mode", "VEHICLE"))); - osmParsers.addRestrictionTagParser(new RestrictionTagParser(restrictions, encodingManager.getDecimalEncodedValue(turnCostKey))); + osmParsers.addRestrictionTagParser(new RestrictionTagParser(restrictions, encodingManager.getBooleanEncodedValue(turnRestrictionKey))); } }); vehicleTagParsers.getTagParsers().forEach(tagParser -> { @@ -1098,10 +1098,10 @@ private void checkProfilesConsistency() { if (!encodingManager.getVehicles().contains(profile.getVehicle())) throw new IllegalArgumentException("Unknown vehicle '" + profile.getVehicle() + "' in profile: " + profile + ". " + "Available vehicles: " + String.join(",", encodingManager.getVehicles())); - DecimalEncodedValue turnCostEnc = encodingManager.hasEncodedValue(TurnCost.key(profile.getVehicle())) - ? encodingManager.getDecimalEncodedValue(TurnCost.key(profile.getVehicle())) + BooleanEncodedValue turnRestrictionEnc = encodingManager.hasEncodedValue(TurnRestriction.key(profile.getVehicle())) + ? encodingManager.getBooleanEncodedValue(TurnRestriction.key(profile.getVehicle())) : null; - if (profile.isTurnCosts() && turnCostEnc == null) { + if (profile.isTurnCosts() && turnRestrictionEnc == null) { throw new IllegalArgumentException("The profile '" + profile.getName() + "' was configured with " + "'turn_costs=true', but the corresponding vehicle '" + profile.getVehicle() + "' does not support turn costs." + "\nYou need to add `|turn_costs=true` to the vehicle in `graph.vehicles`"); diff --git a/core/src/main/java/com/graphhopper/reader/osm/OSMReader.java b/core/src/main/java/com/graphhopper/reader/osm/OSMReader.java index 91b0951c03..bebfd34915 100644 --- a/core/src/main/java/com/graphhopper/reader/osm/OSMReader.java +++ b/core/src/main/java/com/graphhopper/reader/osm/OSMReader.java @@ -607,7 +607,7 @@ private void addRestrictionsToGraph() { r.second = null; } } - restrictionSetter.setRestrictions(restrictionsWithType, restrictionTagParser.getTurnCostEnc()); + restrictionSetter.setRestrictions(restrictionsWithType, restrictionTagParser.getTurnRestrictionEnc()); } } diff --git a/core/src/main/java/com/graphhopper/reader/osm/RestrictionTagParser.java b/core/src/main/java/com/graphhopper/reader/osm/RestrictionTagParser.java index e6ba31c028..05ac5c4907 100644 --- a/core/src/main/java/com/graphhopper/reader/osm/RestrictionTagParser.java +++ b/core/src/main/java/com/graphhopper/reader/osm/RestrictionTagParser.java @@ -18,7 +18,7 @@ package com.graphhopper.reader.osm; -import com.graphhopper.routing.ev.DecimalEncodedValue; +import com.graphhopper.routing.ev.BooleanEncodedValue; import java.util.*; import java.util.stream.Collectors; @@ -30,15 +30,15 @@ */ public class RestrictionTagParser { private final List vehicleTypes; - private final DecimalEncodedValue turnCostEnc; + private final BooleanEncodedValue turnRestrictionEnc; - public RestrictionTagParser(List vehicleTypes, DecimalEncodedValue turnCostEnc) { + public RestrictionTagParser(List vehicleTypes, BooleanEncodedValue turnRestrictionEnc) { this.vehicleTypes = vehicleTypes; - this.turnCostEnc = turnCostEnc; + this.turnRestrictionEnc = turnRestrictionEnc; } - public DecimalEncodedValue getTurnCostEnc() { - return turnCostEnc; + public BooleanEncodedValue getTurnRestrictionEnc() { + return turnRestrictionEnc; } public Result parseRestrictionTags(Map tags) throws OSMRestrictionException { diff --git a/core/src/main/java/com/graphhopper/routing/DefaultWeightingFactory.java b/core/src/main/java/com/graphhopper/routing/DefaultWeightingFactory.java index b2fa69e8a3..f881810b39 100644 --- a/core/src/main/java/com/graphhopper/routing/DefaultWeightingFactory.java +++ b/core/src/main/java/com/graphhopper/routing/DefaultWeightingFactory.java @@ -58,11 +58,11 @@ public Weighting createWeighting(Profile profile, PMap requestHints, boolean dis final String vehicle = profile.getVehicle(); TurnCostProvider turnCostProvider; if (profile.isTurnCosts() && !disableTurnCosts) { - DecimalEncodedValue turnCostEnc = encodingManager.getDecimalEncodedValue(TurnCost.key(vehicle)); - if (turnCostEnc == null) + BooleanEncodedValue turnRestrictionEnc = encodingManager.getBooleanEncodedValue(TurnRestriction.key(vehicle)); + if (turnRestrictionEnc == null) throw new IllegalArgumentException("Vehicle " + vehicle + " does not support turn costs"); int uTurnCosts = hints.getInt(Parameters.Routing.U_TURN_COSTS, INFINITE_U_TURN_COSTS); - turnCostProvider = new DefaultTurnCostProvider(turnCostEnc, graph.getTurnCostStorage(), uTurnCosts); + turnCostProvider = new DefaultTurnCostProvider(turnRestrictionEnc, graph.getTurnCostStorage(), uTurnCosts); } else { turnCostProvider = NO_TURN_COST_PROVIDER; } diff --git a/core/src/main/java/com/graphhopper/routing/ch/CHPreparationGraph.java b/core/src/main/java/com/graphhopper/routing/ch/CHPreparationGraph.java index a8adb44a6a..cfd8743065 100644 --- a/core/src/main/java/com/graphhopper/routing/ch/CHPreparationGraph.java +++ b/core/src/main/java/com/graphhopper/routing/ch/CHPreparationGraph.java @@ -18,19 +18,15 @@ package com.graphhopper.routing.ch; -import com.carrotsearch.hppc.*; +import com.carrotsearch.hppc.IntArrayList; +import com.carrotsearch.hppc.IntContainer; +import com.carrotsearch.hppc.IntScatterSet; +import com.carrotsearch.hppc.IntSet; import com.carrotsearch.hppc.sorting.IndirectComparator; import com.carrotsearch.hppc.sorting.IndirectSort; -import com.graphhopper.routing.ev.DecimalEncodedValue; import com.graphhopper.routing.util.AllEdgesIterator; -import com.graphhopper.routing.weighting.AbstractWeighting; -import com.graphhopper.routing.weighting.DefaultTurnCostProvider; -import com.graphhopper.routing.weighting.TurnCostProvider; import com.graphhopper.routing.weighting.Weighting; import com.graphhopper.storage.Graph; -import com.graphhopper.storage.TurnCostStorage; -import com.graphhopper.util.BitUtil; -import com.graphhopper.util.EdgeIterator; import com.graphhopper.util.GHUtility; import static com.graphhopper.util.ArrayUtil.zero; @@ -113,57 +109,8 @@ public static void buildFromGraph(CHPreparationGraph prepareGraph, Graph graph, * that storing all turn costs in separate arrays upfront speeds up edge-based CH preparation by about 25%. See #2084 */ public static TurnCostFunction buildTurnCostFunctionFromTurnCostStorage(Graph graph, Weighting weighting) { - if (!(weighting instanceof AbstractWeighting)) - return weighting::calcTurnWeight; - TurnCostProvider turnCostProvider = ((AbstractWeighting) weighting).getTurnCostProvider(); - if (!(turnCostProvider instanceof DefaultTurnCostProvider)) - return weighting::calcTurnWeight; - DecimalEncodedValue turnCostEnc = ((DefaultTurnCostProvider) turnCostProvider).getTurnCostEnc(); - TurnCostStorage turnCostStorage = graph.getTurnCostStorage(); - // we maintain a list of inEdge/outEdge/turn-cost triples (we use two arrays for this) that is sorted by nodes - LongArrayList turnCostEdgePairs = new LongArrayList(); - DoubleArrayList turnCosts = new DoubleArrayList(); - // for each node we store the index of the first turn cost entry/triple in the list - final int[] turnCostNodes = new int[graph.getNodes() + 1]; - TurnCostStorage.Iterator tcIter = turnCostStorage.getAllTurnCosts(); - int lastNode = -1; - while (tcIter.next()) { - int viaNode = tcIter.getViaNode(); - if (viaNode < lastNode) - throw new IllegalStateException(); - long edgePair = BitUtil.LITTLE.toLong(tcIter.getFromEdge(), tcIter.getToEdge()); - // note that as long as we only use OSM turn restrictions all the turn costs are infinite anyway - double turnCost = tcIter.getCost(turnCostEnc); - int index = turnCostEdgePairs.size(); - turnCostEdgePairs.add(edgePair); - turnCosts.add(turnCost); - if (viaNode != lastNode) { - for (int i = lastNode + 1; i <= viaNode; i++) { - turnCostNodes[i] = index; - } - } - lastNode = viaNode; - } - for (int i = lastNode + 1; i <= turnCostNodes.length - 1; i++) { - turnCostNodes[i] = turnCostEdgePairs.size(); - } - turnCostNodes[turnCostNodes.length - 1] = turnCostEdgePairs.size(); - - // currently the u-turn costs are the same for all junctions, so for now we just get them for one of them - double uTurnCosts = weighting.calcTurnWeight(1, 0, 1); - return (inEdge, viaNode, outEdge) -> { - if (!EdgeIterator.Edge.isValid(inEdge) || !EdgeIterator.Edge.isValid(outEdge)) - return 0; - else if (inEdge == outEdge) - return uTurnCosts; - // traverse all turn cost entries we have for this viaNode and return the turn costs if we find a match - for (int i = turnCostNodes[viaNode]; i < turnCostNodes[viaNode + 1]; i++) { - long l = turnCostEdgePairs.get(i); - if (inEdge == BitUtil.LITTLE.getIntLow(l) && outEdge == BitUtil.LITTLE.getIntHigh(l)) - return turnCosts.get(i); - } - return 0; - }; + // todonow: removed this optimization for now. it would be much better if we could do this internally in turn cost storage + return weighting::calcTurnWeight; } public int getNodes() { diff --git a/core/src/main/java/com/graphhopper/routing/ev/TurnRestriction.java b/core/src/main/java/com/graphhopper/routing/ev/TurnRestriction.java new file mode 100644 index 0000000000..d7b8e371db --- /dev/null +++ b/core/src/main/java/com/graphhopper/routing/ev/TurnRestriction.java @@ -0,0 +1,32 @@ +/* + * Licensed to GraphHopper GmbH under one or more contributor + * license agreements. See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + * + * GraphHopper GmbH licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.graphhopper.routing.ev; + +import static com.graphhopper.routing.util.EncodingManager.getKey; + +public class TurnRestriction { + + public static String key(String prefix) { + return getKey(prefix, "turn_restriction"); + } + + public static BooleanEncodedValue create(String name) { + return new SimpleBooleanEncodedValue(key(name), false); + } +} diff --git a/core/src/main/java/com/graphhopper/routing/util/VehicleEncodedValues.java b/core/src/main/java/com/graphhopper/routing/util/VehicleEncodedValues.java index a6d1c602e4..3b4ac26ed4 100644 --- a/core/src/main/java/com/graphhopper/routing/util/VehicleEncodedValues.java +++ b/core/src/main/java/com/graphhopper/routing/util/VehicleEncodedValues.java @@ -33,7 +33,7 @@ public class VehicleEncodedValues { private final BooleanEncodedValue accessEnc; private final DecimalEncodedValue avgSpeedEnc; private final DecimalEncodedValue priorityEnc; - private final DecimalEncodedValue turnCostEnc; + private final BooleanEncodedValue turnRestrictionEnc; public static VehicleEncodedValues foot(PMap properties) { String name = properties.getString("name", "foot"); @@ -44,8 +44,8 @@ public static VehicleEncodedValues foot(PMap properties) { BooleanEncodedValue accessEnc = VehicleAccess.create(name); DecimalEncodedValue speedEnc = VehicleSpeed.create(name, speedBits, speedFactor, speedTwoDirections); DecimalEncodedValue priorityEnc = VehiclePriority.create(name, 4, PriorityCode.getFactor(1), false); - DecimalEncodedValue turnCostEnc = turnCosts ? TurnCost.create(name, 1) : null; - return new VehicleEncodedValues(name, accessEnc, speedEnc, priorityEnc, turnCostEnc); + BooleanEncodedValue turnRestrictionEnc = turnCosts ? TurnRestriction.create(name) : null; + return new VehicleEncodedValues(name, accessEnc, speedEnc, priorityEnc, turnRestrictionEnc); } public static VehicleEncodedValues wheelchair(PMap properties) { @@ -66,8 +66,8 @@ public static VehicleEncodedValues bike(PMap properties) { BooleanEncodedValue accessEnc = VehicleAccess.create(name); DecimalEncodedValue speedEnc = VehicleSpeed.create(name, speedBits, speedFactor, speedTwoDirections); DecimalEncodedValue priorityEnc = VehiclePriority.create(name, 4, PriorityCode.getFactor(1), false); - DecimalEncodedValue turnCostEnc = turnCosts ? TurnCost.create(name, 1) : null; - return new VehicleEncodedValues(name, accessEnc, speedEnc, priorityEnc, turnCostEnc); + BooleanEncodedValue turnRestrictionEnc = turnCosts ? TurnRestriction.create(name) : null; + return new VehicleEncodedValues(name, accessEnc, speedEnc, priorityEnc, turnRestrictionEnc); } public static VehicleEncodedValues racingbike(PMap properties) { @@ -85,8 +85,8 @@ public static VehicleEncodedValues car(PMap properties) { boolean turnCosts = properties.getBool("turn_costs", false); BooleanEncodedValue accessEnc = VehicleAccess.create(name); DecimalEncodedValue speedEnc = VehicleSpeed.create(name, speedBits, speedFactor, true); - DecimalEncodedValue turnCostEnc = turnCosts ? TurnCost.create(name, 1) : null; - return new VehicleEncodedValues(name, accessEnc, speedEnc, null, turnCostEnc); + BooleanEncodedValue turnRestrictionEnc = turnCosts ? TurnRestriction.create(name) : null; + return new VehicleEncodedValues(name, accessEnc, speedEnc, null, turnRestrictionEnc); } public static VehicleEncodedValues roads(PMap properties) { @@ -97,17 +97,17 @@ public static VehicleEncodedValues roads(PMap properties) { boolean turnCosts = properties.getBool("turn_costs", false); BooleanEncodedValue accessEnc = VehicleAccess.create(name); DecimalEncodedValue speedEnc = VehicleSpeed.create(name, speedBits, speedFactor, speedTwoDirections); - DecimalEncodedValue turnCostEnc = turnCosts ? TurnCost.create(name, 1) : null; - return new VehicleEncodedValues(name, accessEnc, speedEnc, null, turnCostEnc); + BooleanEncodedValue turnRestrictionEnc = turnCosts ? TurnRestriction.create(name) : null; + return new VehicleEncodedValues(name, accessEnc, speedEnc, null, turnRestrictionEnc); } public VehicleEncodedValues(String name, BooleanEncodedValue accessEnc, DecimalEncodedValue avgSpeedEnc, - DecimalEncodedValue priorityEnc, DecimalEncodedValue turnCostEnc) { + DecimalEncodedValue priorityEnc, BooleanEncodedValue turnRestrictionEnc) { this.name = name; this.accessEnc = accessEnc; this.avgSpeedEnc = avgSpeedEnc; this.priorityEnc = priorityEnc; - this.turnCostEnc = turnCostEnc; + this.turnRestrictionEnc = turnRestrictionEnc; } public void createEncodedValues(List registerNewEncodedValue) { @@ -120,8 +120,8 @@ public void createEncodedValues(List registerNewEncodedValue) { } public void createTurnCostEncodedValues(List registerNewTurnCostEncodedValues) { - if (turnCostEnc != null) - registerNewTurnCostEncodedValues.add(turnCostEnc); + if (turnRestrictionEnc != null) + registerNewTurnCostEncodedValues.add(turnRestrictionEnc); } public BooleanEncodedValue getAccessEnc() { @@ -136,8 +136,8 @@ public DecimalEncodedValue getPriorityEnc() { return priorityEnc; } - public DecimalEncodedValue getTurnCostEnc() { - return turnCostEnc; + public BooleanEncodedValue getTurnRestrictionEnc() { + return turnRestrictionEnc; } public String getName() { diff --git a/core/src/main/java/com/graphhopper/routing/util/parsers/RestrictionSetter.java b/core/src/main/java/com/graphhopper/routing/util/parsers/RestrictionSetter.java index 16f22e2625..37b00dda72 100644 --- a/core/src/main/java/com/graphhopper/routing/util/parsers/RestrictionSetter.java +++ b/core/src/main/java/com/graphhopper/routing/util/parsers/RestrictionSetter.java @@ -18,12 +18,15 @@ package com.graphhopper.routing.util.parsers; -import com.carrotsearch.hppc.*; +import com.carrotsearch.hppc.IntHashSet; +import com.carrotsearch.hppc.IntIntHashMap; +import com.carrotsearch.hppc.IntIntMap; +import com.carrotsearch.hppc.IntSet; import com.carrotsearch.hppc.cursors.IntCursor; import com.graphhopper.reader.osm.GraphRestriction; import com.graphhopper.reader.osm.Pair; import com.graphhopper.reader.osm.RestrictionType; -import com.graphhopper.routing.ev.DecimalEncodedValue; +import com.graphhopper.routing.ev.BooleanEncodedValue; import com.graphhopper.storage.BaseGraph; import com.graphhopper.util.EdgeExplorer; import com.graphhopper.util.EdgeIterator; @@ -52,14 +55,14 @@ public RestrictionSetter(BaseGraph baseGraph) { * Since we keep track of the added artificial edges here it is important to only use one RestrictionSetter instance * for **all** turn restrictions and vehicle types. */ - public void setRestrictions(List> restrictions, DecimalEncodedValue turnCostEnc) { + public void setRestrictions(List> restrictions, BooleanEncodedValue turnRestrictionEnc) { // we first need to add all the artificial edges, because we might need to restrict turns between artificial // edges created for different restrictions (when restrictions are overlapping) addArtificialEdges(restrictions); // now we can add all the via-way restrictions - addViaWayRestrictions(restrictions, turnCostEnc); + addViaWayRestrictions(restrictions, turnRestrictionEnc); // ... and finally all the via-node restrictions - addViaNodeRestrictions(restrictions, turnCostEnc); + addViaNodeRestrictions(restrictions, turnRestrictionEnc); } private void addArtificialEdges(List> restrictions) { @@ -82,7 +85,7 @@ private void addArtificialEdges(List> re } } - private void addViaWayRestrictions(List> restrictions, DecimalEncodedValue turnCostEnc) { + private void addViaWayRestrictions(List> restrictions, BooleanEncodedValue turnRestrictionEnc) { IntSet viaEdgesUsedByOnlyRestrictions = new IntHashSet(); for (Pair p : restrictions) { if (!p.first.isViaWayRestriction()) continue; @@ -101,17 +104,17 @@ private void addViaWayRestrictions(List> final int viaToToNode = p.first.getViaNodes().get(1); // never turn between an artificial edge and its corresponding real edge - restrictTurn(turnCostEnc, artificialVia, fromToViaNode, viaEdge); - restrictTurn(turnCostEnc, viaEdge, fromToViaNode, artificialVia); - restrictTurn(turnCostEnc, artificialVia, viaToToNode, viaEdge); - restrictTurn(turnCostEnc, viaEdge, viaToToNode, artificialVia); + restrictTurn(turnRestrictionEnc, artificialVia, fromToViaNode, viaEdge); + restrictTurn(turnRestrictionEnc, viaEdge, fromToViaNode, artificialVia); + restrictTurn(turnRestrictionEnc, artificialVia, viaToToNode, viaEdge); + restrictTurn(turnRestrictionEnc, viaEdge, viaToToNode, artificialVia); if (p.second == NO) { // This is how we implement via-way NO restrictions: we deny turning from the from-edge onto the via-edge, // but allow turning onto the artificial edge instead. Then we deny turning from the artificial edge onto // the to edge. - restrictTurn(turnCostEnc, fromEdge, fromToViaNode, viaEdge); - restrictTurn(turnCostEnc, artificialVia, viaToToNode, toEdge); + restrictTurn(turnRestrictionEnc, fromEdge, fromToViaNode, viaEdge); + restrictTurn(turnRestrictionEnc, artificialVia, viaToToNode, toEdge); } else if (p.second == ONLY) { // For via-way ONLY restrictions we have to turn from the from-edge onto the via-edge and from the via-edge // onto the to-edge, but only if we actually start at the from-edge. Therefore we enforce turning onto @@ -120,24 +123,24 @@ private void addViaWayRestrictions(List> EdgeIterator iter = edgeExplorer.setBaseNode(fromToViaNode); while (iter.next()) if (iter.getEdge() != fromEdge && iter.getEdge() != artificialVia) - restrictTurn(turnCostEnc, fromEdge, fromToViaNode, iter.getEdge()); + restrictTurn(turnRestrictionEnc, fromEdge, fromToViaNode, iter.getEdge()); iter = edgeExplorer.setBaseNode(viaToToNode); while (iter.next()) if (iter.getEdge() != artificialVia && iter.getEdge() != toEdge) - restrictTurn(turnCostEnc, artificialVia, viaToToNode, iter.getEdge()); + restrictTurn(turnRestrictionEnc, artificialVia, viaToToNode, iter.getEdge()); } else { throw new IllegalArgumentException("Unexpected restriction type: " + p.second); } // this is important for overlapping restrictions if (artificialFrom != fromEdge) - restrictTurn(turnCostEnc, artificialFrom, fromToViaNode, artificialVia); + restrictTurn(turnRestrictionEnc, artificialFrom, fromToViaNode, artificialVia); if (artificialTo != toEdge) - restrictTurn(turnCostEnc, artificialVia, viaToToNode, artificialTo); + restrictTurn(turnRestrictionEnc, artificialVia, viaToToNode, artificialTo); } } - private void addViaNodeRestrictions(List> restrictions, DecimalEncodedValue turnCostEnc) { + private void addViaNodeRestrictions(List> restrictions, BooleanEncodedValue turnRestrictionEnc) { for (Pair p : restrictions) { if (p.first.isViaWayRestriction()) continue; final int viaNode = p.first.getViaNodes().get(0); @@ -148,14 +151,14 @@ private void addViaNodeRestrictions(List final int artificialFrom = artificialEdgesByEdges.getOrDefault(fromEdge, fromEdge); final int artificialTo = artificialEdgesByEdges.getOrDefault(toEdge, toEdge); if (p.second == NO) { - restrictTurn(turnCostEnc, fromEdge, viaNode, toEdge); + restrictTurn(turnRestrictionEnc, fromEdge, viaNode, toEdge); // we also need to restrict this term in case there are artificial edges for the from- and/or to-edge if (artificialFrom != fromEdge) - restrictTurn(turnCostEnc, artificialFrom, viaNode, toEdge); + restrictTurn(turnRestrictionEnc, artificialFrom, viaNode, toEdge); if (artificialTo != toEdge) - restrictTurn(turnCostEnc, fromEdge, viaNode, artificialTo); + restrictTurn(turnRestrictionEnc, fromEdge, viaNode, artificialTo); if (artificialFrom != fromEdge && artificialTo != toEdge) - restrictTurn(turnCostEnc, artificialFrom, viaNode, artificialTo); + restrictTurn(turnRestrictionEnc, artificialFrom, viaNode, artificialTo); } else if (p.second == ONLY) { // we need to restrict all turns except the one, but that also means not restricting the // artificial counterparts of these turns, if they exist. @@ -163,10 +166,10 @@ private void addViaNodeRestrictions(List EdgeIterator iter = edgeExplorer.setBaseNode(viaNode); while (iter.next()) { if (iter.getEdge() != fromEdge && iter.getEdge() != toEdge && iter.getEdge() != artificialTo) - restrictTurn(turnCostEnc, fromEdge, viaNode, iter.getEdge()); + restrictTurn(turnRestrictionEnc, fromEdge, viaNode, iter.getEdge()); // and the same for the artificial edge belonging to the from-edge if it exists if (fromEdge != artificialFrom && iter.getEdge() != artificialFrom && iter.getEdge() != toEdge && iter.getEdge() != artificialTo) - restrictTurn(turnCostEnc, artificialFrom, viaNode, iter.getEdge()); + restrictTurn(turnRestrictionEnc, artificialFrom, viaNode, iter.getEdge()); } } else { throw new IllegalArgumentException("Unexpected restriction type: " + p.second); @@ -180,10 +183,10 @@ public IntIntMap getArtificialEdgesByEdges() { return artificialEdgesByEdges; } - private void restrictTurn(DecimalEncodedValue turnCostEnc, int fromEdge, int viaNode, int toEdge) { + private void restrictTurn(BooleanEncodedValue turnRestrictionEnc, int fromEdge, int viaNode, int toEdge) { if (fromEdge < 0 || toEdge < 0 || viaNode < 0) throw new IllegalArgumentException("from/toEdge and viaNode must be >= 0"); - baseGraph.getTurnCostStorage().set(turnCostEnc, fromEdge, viaNode, toEdge, Double.POSITIVE_INFINITY); + baseGraph.getTurnCostStorage().set(turnRestrictionEnc, fromEdge, viaNode, toEdge, true); } private static boolean ignoreViaWayRestriction(Pair p) { @@ -197,4 +200,4 @@ private static boolean ignoreViaWayRestriction(Pair(3-4) only_straight_on = (2-3)->(3-8) restricted // (4-3)->(3-8) no_right_turn = (4-3)->(3-8) restricted // (2-3)->(3-8) no_entry = (2-3)->(3-8) restricted - DecimalEncodedValue carTCEnc = hopper.getEncodingManager().getDecimalEncodedValue(TurnCost.key("car")); - assertTrue(tcStorage.get(carTCEnc, edge2_3, n3, edge3_8) > 0); - assertTrue(tcStorage.get(carTCEnc, edge4_3, n3, edge3_8) > 0); - assertTrue(tcStorage.get(carTCEnc, edge2_3, n3, edge3_8) > 0); - assertTrue(tcStorage.get(carTCEnc, edge2_3, n3, edge3_4) == 0); - assertTrue(tcStorage.get(carTCEnc, edge2_3, n3, edge3_2) == 0); - assertTrue(tcStorage.get(carTCEnc, edge2_3, n3, edge3_4) == 0); - assertTrue(tcStorage.get(carTCEnc, edge4_3, n3, edge3_2) == 0); - assertTrue(tcStorage.get(carTCEnc, edge8_3, n3, edge3_2) == 0); + BooleanEncodedValue carTCEnc = hopper.getEncodingManager().getBooleanEncodedValue(TurnRestriction.key("car")); + assertTrue(tcStorage.get(carTCEnc, edge2_3, n3, edge3_8)); + assertTrue(tcStorage.get(carTCEnc, edge4_3, n3, edge3_8)); + assertTrue(tcStorage.get(carTCEnc, edge2_3, n3, edge3_8)); + assertFalse(tcStorage.get(carTCEnc, edge2_3, n3, edge3_4)); + assertFalse(tcStorage.get(carTCEnc, edge2_3, n3, edge3_2)); + assertFalse(tcStorage.get(carTCEnc, edge2_3, n3, edge3_4)); + assertFalse(tcStorage.get(carTCEnc, edge4_3, n3, edge3_2)); + assertFalse(tcStorage.get(carTCEnc, edge8_3, n3, edge3_2)); // u-turn restriction for (6-1)->(1-6) but not for (1-6)->(6-1) - assertTrue(tcStorage.get(carTCEnc, edge1_6, n1, edge1_6) > 0); - assertTrue(tcStorage.get(carTCEnc, edge1_6, n6, edge1_6) == 0); + assertTrue(tcStorage.get(carTCEnc, edge1_6, n1, edge1_6)); + assertFalse(tcStorage.get(carTCEnc, edge1_6, n6, edge1_6)); int edge4_5 = GHUtility.getEdge(graph, n4, n5).getEdge(); int edge5_6 = GHUtility.getEdge(graph, n5, n6).getEdge(); int edge5_1 = GHUtility.getEdge(graph, n5, n1).getEdge(); // (4-5)->(5-1) right_turn_only = (4-5)->(5-6) restricted - assertTrue(tcStorage.get(carTCEnc, edge4_5, n5, edge5_6) == 0); - assertTrue(tcStorage.get(carTCEnc, edge4_5, n5, edge5_1) > 0); + assertFalse(tcStorage.get(carTCEnc, edge4_5, n5, edge5_6)); + assertTrue(tcStorage.get(carTCEnc, edge4_5, n5, edge5_1)); - DecimalEncodedValue bikeTCEnc = hopper.getEncodingManager().getDecimalEncodedValue(TurnCost.key("bike")); - assertTrue(tcStorage.get(bikeTCEnc, edge4_5, n5, edge5_6) == 0); + BooleanEncodedValue bikeTCEnc = hopper.getEncodingManager().getBooleanEncodedValue(TurnRestriction.key("bike")); + assertFalse(tcStorage.get(bikeTCEnc, edge4_5, n5, edge5_6)); int n10 = AbstractGraphStorageTester.getIdOf(graph, 40, 10); int n11 = AbstractGraphStorageTester.getIdOf(graph, 40, 11); @@ -577,12 +577,12 @@ public void testTurnRestrictionsFromXML() { int edge10_11 = GHUtility.getEdge(graph, n10, n11).getEdge(); int edge11_14 = GHUtility.getEdge(graph, n11, n14).getEdge(); - assertTrue(tcStorage.get(carTCEnc, edge11_14, n11, edge10_11) == 0); - assertTrue(tcStorage.get(bikeTCEnc, edge11_14, n11, edge10_11) == 0); + assertFalse(tcStorage.get(carTCEnc, edge11_14, n11, edge10_11)); + assertFalse(tcStorage.get(bikeTCEnc, edge11_14, n11, edge10_11)); // the turn is restricted for car even though it turns into a one-way, but we treat this separately now - assertTrue(tcStorage.get(carTCEnc, edge10_11, n11, edge11_14) > 0); - assertTrue(tcStorage.get(bikeTCEnc, edge10_11, n11, edge11_14) > 0); + assertTrue(tcStorage.get(carTCEnc, edge10_11, n11, edge11_14)); + assertTrue(tcStorage.get(bikeTCEnc, edge10_11, n11, edge11_14)); } @Test @@ -603,11 +603,11 @@ public void testTurnRestrictionsViaHgvTransportationMode() { int edge9_3 = GHUtility.getEdge(graph, n9, n3).getEdge(); int edge3_8 = GHUtility.getEdge(graph, n3, n8).getEdge(); - DecimalEncodedValue carTCEnc = hopper.getEncodingManager().getDecimalEncodedValue(TurnCost.key("car")); - DecimalEncodedValue roadsTCEnc = hopper.getEncodingManager().getDecimalEncodedValue(TurnCost.key("roads")); + BooleanEncodedValue carTCEnc = hopper.getEncodingManager().getBooleanEncodedValue(TurnRestriction.key("car")); + BooleanEncodedValue roadsTCEnc = hopper.getEncodingManager().getBooleanEncodedValue(TurnRestriction.key("roads")); - assertTrue(tcStorage.get(carTCEnc, edge9_3, n3, edge3_8) == 0); - assertTrue(tcStorage.get(roadsTCEnc, edge9_3, n3, edge3_8) > 0); + assertFalse(tcStorage.get(carTCEnc, edge9_3, n3, edge3_8)); + assertTrue(tcStorage.get(roadsTCEnc, edge9_3, n3, edge3_8)); } @Test @@ -711,9 +711,9 @@ public void testTurnFlagCombination() { ). importOrLoad(); EncodingManager manager = hopper.getEncodingManager(); - DecimalEncodedValue carTCEnc = manager.getDecimalEncodedValue(TurnCost.key("car")); - DecimalEncodedValue truckTCEnc = manager.getDecimalEncodedValue(TurnCost.key("truck")); - DecimalEncodedValue bikeTCEnc = manager.getDecimalEncodedValue(TurnCost.key("bike")); + BooleanEncodedValue carTCEnc = manager.getBooleanEncodedValue(TurnRestriction.key("car")); + BooleanEncodedValue truckTCEnc = manager.getBooleanEncodedValue(TurnRestriction.key("truck")); + BooleanEncodedValue bikeTCEnc = manager.getBooleanEncodedValue(TurnRestriction.key("bike")); Graph graph = hopper.getBaseGraph(); TurnCostStorage tcStorage = graph.getTurnCostStorage(); @@ -721,22 +721,22 @@ public void testTurnFlagCombination() { int edge1 = GHUtility.getEdge(graph, 1, 0).getEdge(); int edge2 = GHUtility.getEdge(graph, 0, 2).getEdge(); // the 2nd entry provides turn flags for bike only - assertTrue(Double.isInfinite(tcStorage.get(carTCEnc, edge1, 0, edge2))); - assertTrue(Double.isInfinite(tcStorage.get(truckTCEnc, edge1, 0, edge2))); - assertEquals(0, tcStorage.get(bikeTCEnc, edge1, 0, edge2), .1); + assertTrue(tcStorage.get(carTCEnc, edge1, 0, edge2)); + assertTrue(tcStorage.get(truckTCEnc, edge1, 0, edge2)); + assertFalse(tcStorage.get(bikeTCEnc, edge1, 0, edge2)); edge1 = GHUtility.getEdge(graph, 2, 0).getEdge(); edge2 = GHUtility.getEdge(graph, 0, 3).getEdge(); // the first entry provides turn flags for car and foot only - assertEquals(0, tcStorage.get(carTCEnc, edge1, 0, edge2), .1); - assertEquals(0, tcStorage.get(truckTCEnc, edge1, 0, edge2), .1); - assertTrue(Double.isInfinite(tcStorage.get(bikeTCEnc, edge1, 0, edge2))); + assertFalse(tcStorage.get(carTCEnc, edge1, 0, edge2)); + assertFalse(tcStorage.get(truckTCEnc, edge1, 0, edge2)); + assertTrue(tcStorage.get(bikeTCEnc, edge1, 0, edge2)); edge1 = GHUtility.getEdge(graph, 3, 0).getEdge(); edge2 = GHUtility.getEdge(graph, 0, 2).getEdge(); - assertEquals(0, tcStorage.get(carTCEnc, edge1, 0, edge2), .1); - assertTrue(Double.isInfinite(tcStorage.get(truckTCEnc, edge1, 0, edge2))); - assertEquals(0, tcStorage.get(bikeTCEnc, edge1, 0, edge2), .1); + assertFalse(tcStorage.get(carTCEnc, edge1, 0, edge2)); + assertTrue(tcStorage.get(truckTCEnc, edge1, 0, edge2)); + assertFalse(tcStorage.get(bikeTCEnc, edge1, 0, edge2)); } @Test @@ -775,37 +775,37 @@ public void testConditionalTurnRestriction() { // (2-3)->(3-4) only_straight_on except bicycle = (2-3)->(3-8) restricted for car // (4-3)->(3-8) no_right_turn dedicated to motorcar = (4-3)->(3-8) restricted for car - DecimalEncodedValue carTCEnc = hopper.getEncodingManager().getDecimalEncodedValue(TurnCost.key("car")); - assertTrue(tcStorage.get(carTCEnc, edge2_3, n3, edge3_8) > 0); - assertTrue(tcStorage.get(carTCEnc, edge4_3, n3, edge3_8) > 0); - assertEquals(0, tcStorage.get(carTCEnc, edge2_3, n3, edge3_4), .1); - assertEquals(0, tcStorage.get(carTCEnc, edge2_3, n3, edge3_2), .1); - assertEquals(0, tcStorage.get(carTCEnc, edge2_3, n3, edge3_4), .1); - assertEquals(0, tcStorage.get(carTCEnc, edge4_3, n3, edge3_2), .1); - assertEquals(0, tcStorage.get(carTCEnc, edge8_3, n3, edge3_2), .1); - - DecimalEncodedValue bikeTCEnc = hopper.getEncodingManager().getDecimalEncodedValue(TurnCost.key("bike")); - assertEquals(0, tcStorage.get(bikeTCEnc, edge2_3, n3, edge3_8), .1); - assertEquals(0, tcStorage.get(bikeTCEnc, edge4_3, n3, edge3_8), .1); - assertEquals(0, tcStorage.get(bikeTCEnc, edge2_3, n3, edge3_4), .1); - assertEquals(0, tcStorage.get(bikeTCEnc, edge2_3, n3, edge3_2), .1); - assertEquals(0, tcStorage.get(bikeTCEnc, edge2_3, n3, edge3_4), .1); - assertEquals(0, tcStorage.get(bikeTCEnc, edge4_3, n3, edge3_2), .1); - assertEquals(0, tcStorage.get(bikeTCEnc, edge8_3, n3, edge3_2), .1); + BooleanEncodedValue carTCEnc = hopper.getEncodingManager().getBooleanEncodedValue(TurnRestriction.key("car")); + assertTrue(tcStorage.get(carTCEnc, edge2_3, n3, edge3_8)); + assertTrue(tcStorage.get(carTCEnc, edge4_3, n3, edge3_8)); + assertFalse(tcStorage.get(carTCEnc, edge2_3, n3, edge3_4)); + assertFalse(tcStorage.get(carTCEnc, edge2_3, n3, edge3_2)); + assertFalse(tcStorage.get(carTCEnc, edge2_3, n3, edge3_4)); + assertFalse(tcStorage.get(carTCEnc, edge4_3, n3, edge3_2)); + assertFalse(tcStorage.get(carTCEnc, edge8_3, n3, edge3_2)); + + BooleanEncodedValue bikeTCEnc = hopper.getEncodingManager().getBooleanEncodedValue(TurnRestriction.key("bike")); + assertFalse(tcStorage.get(bikeTCEnc, edge2_3, n3, edge3_8)); + assertFalse(tcStorage.get(bikeTCEnc, edge4_3, n3, edge3_8)); + assertFalse(tcStorage.get(bikeTCEnc, edge2_3, n3, edge3_4)); + assertFalse(tcStorage.get(bikeTCEnc, edge2_3, n3, edge3_2)); + assertFalse(tcStorage.get(bikeTCEnc, edge2_3, n3, edge3_4)); + assertFalse(tcStorage.get(bikeTCEnc, edge4_3, n3, edge3_2)); + assertFalse(tcStorage.get(bikeTCEnc, edge8_3, n3, edge3_2)); // u-turn except bus;bicycle restriction for (6-1)->(1-6) but not for (1-6)->(6-1) - assertTrue(tcStorage.get(carTCEnc, edge1_6, n1, edge1_6) > 0); - assertEquals(0, tcStorage.get(carTCEnc, edge1_6, n6, edge1_6), .1); + assertTrue(tcStorage.get(carTCEnc, edge1_6, n1, edge1_6)); + assertFalse(tcStorage.get(carTCEnc, edge1_6, n6, edge1_6)); - assertEquals(0, tcStorage.get(bikeTCEnc, edge1_6, n1, edge1_6), .1); - assertEquals(0, tcStorage.get(bikeTCEnc, edge1_6, n6, edge1_6), .1); + assertFalse(tcStorage.get(bikeTCEnc, edge1_6, n1, edge1_6)); + assertFalse(tcStorage.get(bikeTCEnc, edge1_6, n6, edge1_6)); // (4-5)->(5-6) right_turn_only dedicated to motorcar = (4-5)->(5-1) restricted - assertEquals(0, tcStorage.get(carTCEnc, edge4_5, n5, edge5_6), .1); - assertTrue(tcStorage.get(carTCEnc, edge4_5, n5, edge5_1) > 0); + assertFalse(tcStorage.get(carTCEnc, edge4_5, n5, edge5_6)); + assertTrue(tcStorage.get(carTCEnc, edge4_5, n5, edge5_1)); - assertEquals(0, tcStorage.get(bikeTCEnc, edge4_5, n5, edge5_6), .1); - assertEquals(0, tcStorage.get(bikeTCEnc, edge4_5, n5, edge5_1), .1); + assertFalse(tcStorage.get(bikeTCEnc, edge4_5, n5, edge5_6)); + assertFalse(tcStorage.get(bikeTCEnc, edge4_5, n5, edge5_1)); } @Test @@ -831,24 +831,24 @@ public void testMultipleTurnRestrictions() { int edge4_5 = GHUtility.getEdge(graph, n4, n5).getEdge(); int edge5_1 = GHUtility.getEdge(graph, n5, n1).getEdge(); - DecimalEncodedValue carTCEnc = hopper.getEncodingManager().getDecimalEncodedValue(TurnCost.key("car")); - DecimalEncodedValue bikeTCEnc = hopper.getEncodingManager().getDecimalEncodedValue(TurnCost.key("bike")); + BooleanEncodedValue carTCEnc = hopper.getEncodingManager().getBooleanEncodedValue(TurnRestriction.key("car")); + BooleanEncodedValue bikeTCEnc = hopper.getEncodingManager().getBooleanEncodedValue(TurnRestriction.key("bike")); // (1-2)->(2-3) no_right_turn for motorcar and bus - assertTrue(tcStorage.get(carTCEnc, edge1_2, n2, edge2_3) > 0); - assertEquals(0, tcStorage.get(bikeTCEnc, edge1_2, n2, edge2_3), .1); + assertTrue(tcStorage.get(carTCEnc, edge1_2, n2, edge2_3)); + assertFalse(tcStorage.get(bikeTCEnc, edge1_2, n2, edge2_3)); // (3-4)->(4-5) no_right_turn for motorcycle and motorcar - assertTrue(Double.isInfinite(tcStorage.get(carTCEnc, edge3_4, n4, edge4_5))); - assertEquals(0, tcStorage.get(bikeTCEnc, edge3_4, n4, edge4_5), .1); + assertTrue(tcStorage.get(carTCEnc, edge3_4, n4, edge4_5)); + assertFalse(tcStorage.get(bikeTCEnc, edge3_4, n4, edge4_5)); // (5-1)->(1-2) no_right_turn for bus and psv except for motorcar and bicycle - assertEquals(0, tcStorage.get(carTCEnc, edge4_5, n5, edge5_1), .1); - assertEquals(0, tcStorage.get(bikeTCEnc, edge4_5, n5, edge5_1), .1); + assertFalse(tcStorage.get(carTCEnc, edge4_5, n5, edge5_1)); + assertFalse(tcStorage.get(bikeTCEnc, edge4_5, n5, edge5_1)); // (5-1)->(1-2) no_right_turn for motorcar and motorcycle except for bus and bicycle - assertTrue(Double.isInfinite(tcStorage.get(carTCEnc, edge5_1, n1, edge1_2))); - assertEquals(0, tcStorage.get(bikeTCEnc, edge5_1, n1, edge1_2), .1); + assertTrue(tcStorage.get(carTCEnc, edge5_1, n1, edge1_2)); + assertFalse(tcStorage.get(bikeTCEnc, edge5_1, n1, edge1_2)); } @Test diff --git a/core/src/test/java/com/graphhopper/routing/AlternativeRouteTest.java b/core/src/test/java/com/graphhopper/routing/AlternativeRouteTest.java index cd8df5b96c..ed4196b784 100644 --- a/core/src/test/java/com/graphhopper/routing/AlternativeRouteTest.java +++ b/core/src/test/java/com/graphhopper/routing/AlternativeRouteTest.java @@ -56,7 +56,9 @@ public Fixture(TraversalMode tMode) { EncodingManager em = EncodingManager.start().add(speedEnc).add(turnCostEnc).build(); graph = new BaseGraph.Builder(em).withTurnCosts(true).create(); - weighting = new SpeedWeighting(speedEnc, tMode.isEdgeBased() ? turnCostEnc : null, graph.getTurnCostStorage(), Double.POSITIVE_INFINITY); + weighting = tMode.isEdgeBased() + ? new SpeedWeighting(speedEnc, turnCostEnc, graph.getTurnCostStorage(), Double.POSITIVE_INFINITY) + : new SpeedWeighting(speedEnc); } @Override diff --git a/core/src/test/java/com/graphhopper/routing/subnetwork/PrepareRoutingSubnetworksTest.java b/core/src/test/java/com/graphhopper/routing/subnetwork/PrepareRoutingSubnetworksTest.java index cce4e66efc..2a8c65a1c3 100644 --- a/core/src/test/java/com/graphhopper/routing/subnetwork/PrepareRoutingSubnetworksTest.java +++ b/core/src/test/java/com/graphhopper/routing/subnetwork/PrepareRoutingSubnetworksTest.java @@ -249,7 +249,7 @@ private static IntArrayList getSubnetworkEdges(BaseGraph graph, BooleanEncodedVa } private static PrepareRoutingSubnetworks.PrepareJob createJob(BooleanEncodedValue subnetworkEnc, DecimalEncodedValue speedEnc) { - return createJob(subnetworkEnc, speedEnc, null, null, 0); + return new PrepareRoutingSubnetworks.PrepareJob(subnetworkEnc, new SpeedWeighting(speedEnc)); } private static PrepareRoutingSubnetworks.PrepareJob createJob(BooleanEncodedValue subnetworkEnc, DecimalEncodedValue speedEnc, diff --git a/core/src/test/java/com/graphhopper/routing/util/parsers/CarTagParserTest.java b/core/src/test/java/com/graphhopper/routing/util/parsers/CarTagParserTest.java index 7236c0bd2e..6cb932e56d 100644 --- a/core/src/test/java/com/graphhopper/routing/util/parsers/CarTagParserTest.java +++ b/core/src/test/java/com/graphhopper/routing/util/parsers/CarTagParserTest.java @@ -690,7 +690,6 @@ public void testIssue_1256() { EncodingManager lowFactorEm = new EncodingManager.Builder() .add(new SimpleBooleanEncodedValue(VehicleAccess.key("car"), true)) .add(lowFactorSpeedEnc) - .addTurnCostEncodedValue(TurnCost.create(TurnCost.key("car"), 1)) .build(); edgeIntAccess = new ArrayEdgeIntAccess(lowFactorEm.getIntsForFlags()); new CarAverageSpeedParser(lowFactorEm, new PMap()).handleWayTags(edgeId, edgeIntAccess, way); diff --git a/core/src/test/java/com/graphhopper/routing/util/parsers/RestrictionSetterTest.java b/core/src/test/java/com/graphhopper/routing/util/parsers/RestrictionSetterTest.java index 10ef0a2bed..2166e9d027 100644 --- a/core/src/test/java/com/graphhopper/routing/util/parsers/RestrictionSetterTest.java +++ b/core/src/test/java/com/graphhopper/routing/util/parsers/RestrictionSetterTest.java @@ -5,13 +5,11 @@ import com.graphhopper.reader.osm.Pair; import com.graphhopper.reader.osm.RestrictionType; import com.graphhopper.routing.Dijkstra; -import com.graphhopper.routing.ev.DecimalEncodedValue; -import com.graphhopper.routing.ev.DecimalEncodedValueImpl; -import com.graphhopper.routing.ev.EncodedValue; -import com.graphhopper.routing.ev.TurnCost; +import com.graphhopper.routing.ev.*; import com.graphhopper.routing.util.EncodingManager; import com.graphhopper.routing.util.TraversalMode; import com.graphhopper.routing.weighting.SpeedWeighting; +import com.graphhopper.routing.weighting.TurnCostProvider; import com.graphhopper.storage.BaseGraph; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -46,9 +44,9 @@ void viaNode_no() { edge(2, 4); edge(3, 4); GraphRestriction graphRestriction = GraphRestriction.node(a, 1, b); - DecimalEncodedValue turnCostEnc = createTurnCostEnc("car"); - r.setRestrictions(Arrays.asList(new Pair<>(graphRestriction, RestrictionType.NO)), turnCostEnc); - assertEquals(nodes(0, 1, 3, 4, 2), calcPath(0, 2, turnCostEnc)); + BooleanEncodedValue turnRestrictionEnc = createTurnRestrictionEnc("car"); + r.setRestrictions(Arrays.asList(new Pair<>(graphRestriction, RestrictionType.NO)), turnRestrictionEnc); + assertEquals(nodes(0, 1, 3, 4, 2), calcPath(0, 2, turnRestrictionEnc)); } @Test @@ -62,9 +60,9 @@ void viaNode_only() { edge(2, 4); edge(3, 4); GraphRestriction graphRestriction = GraphRestriction.node(a, 1, b); - DecimalEncodedValue turnCostEnc = createTurnCostEnc("car"); - r.setRestrictions(Arrays.asList(new Pair<>(graphRestriction, RestrictionType.ONLY)), turnCostEnc); - assertEquals(nodes(0, 1, 2, 4, 3), calcPath(0, 3, turnCostEnc)); + BooleanEncodedValue turnRestrictionEnc = createTurnRestrictionEnc("car"); + r.setRestrictions(Arrays.asList(new Pair<>(graphRestriction, RestrictionType.ONLY)), turnRestrictionEnc); + assertEquals(nodes(0, 1, 2, 4, 3), calcPath(0, 3, turnRestrictionEnc)); } @Test @@ -86,15 +84,15 @@ void viaWay_no() { edge(6, 9); edge(8, 9); GraphRestriction graphRestriction = GraphRestriction.way(a, b, c, nodes(1, 2)); - DecimalEncodedValue turnCostEnc = createTurnCostEnc("car"); + BooleanEncodedValue turnRestrictionEnc = createTurnRestrictionEnc("car"); r.setRestrictions(Arrays.asList( new Pair<>(graphRestriction, RestrictionType.NO) - ), turnCostEnc); + ), turnRestrictionEnc); // turning from a to b and then to c is not allowed - assertEquals(nodes(0, 1, 5, 8, 9, 6, 2, 3), calcPath(0, 3, turnCostEnc)); + assertEquals(nodes(0, 1, 5, 8, 9, 6, 2, 3), calcPath(0, 3, turnRestrictionEnc)); // turning from a to b, or b to c is still allowed - assertEquals(nodes(0, 1, 2, 4), calcPath(0, 4, turnCostEnc)); - assertEquals(nodes(5, 1, 2, 3), calcPath(5, 3, turnCostEnc)); + assertEquals(nodes(0, 1, 2, 4), calcPath(0, 4, turnRestrictionEnc)); + assertEquals(nodes(5, 1, 2, 3), calcPath(5, 3, turnRestrictionEnc)); } @Test @@ -111,21 +109,21 @@ void viaWay_no_withOverlap() { int t = edge(2, 6); int u = edge(3, 7); - DecimalEncodedValue turnCostEnc = createTurnCostEnc("car"); + BooleanEncodedValue turnRestrictionEnc = createTurnRestrictionEnc("car"); r.setRestrictions(Arrays.asList( new Pair<>(GraphRestriction.way(a, b, c, nodes(1, 2)), RestrictionType.NO), new Pair<>(GraphRestriction.way(b, c, d, nodes(2, 3)), RestrictionType.NO) - ), turnCostEnc); + ), turnRestrictionEnc); - assertEquals(NO_PATH, calcPath(0, 3, turnCostEnc)); // a-b-c - assertEquals(nodes(0, 1, 2, 6), calcPath(0, 6, turnCostEnc)); // a-b-t - assertEquals(nodes(5, 1, 2, 3), calcPath(5, 3, turnCostEnc)); // s-b-c - assertEquals(nodes(5, 1, 2, 6), calcPath(5, 6, turnCostEnc)); // s-b-t + assertEquals(NO_PATH, calcPath(0, 3, turnRestrictionEnc)); // a-b-c + assertEquals(nodes(0, 1, 2, 6), calcPath(0, 6, turnRestrictionEnc)); // a-b-t + assertEquals(nodes(5, 1, 2, 3), calcPath(5, 3, turnRestrictionEnc)); // s-b-c + assertEquals(nodes(5, 1, 2, 6), calcPath(5, 6, turnRestrictionEnc)); // s-b-t - assertEquals(NO_PATH, calcPath(1, 4, turnCostEnc)); // b-c-d - assertEquals(nodes(1, 2, 3, 7), calcPath(1, 7, turnCostEnc)); // b-c-u - assertEquals(nodes(6, 2, 3, 4), calcPath(6, 4, turnCostEnc)); // t-c-d - assertEquals(nodes(6, 2, 3, 7), calcPath(6, 7, turnCostEnc)); // t-c-u + assertEquals(NO_PATH, calcPath(1, 4, turnRestrictionEnc)); // b-c-d + assertEquals(nodes(1, 2, 3, 7), calcPath(1, 7, turnRestrictionEnc)); // b-c-u + assertEquals(nodes(6, 2, 3, 4), calcPath(6, 4, turnRestrictionEnc)); // t-c-d + assertEquals(nodes(6, 2, 3, 7), calcPath(6, 7, turnRestrictionEnc)); // t-c-u } @Test @@ -150,7 +148,7 @@ void viaWay_no_withOverlap_more_complex() { edge(7, 10); edge(8, 11); edge(10, 11); - DecimalEncodedValue turnCostEnc = createTurnCostEnc("car"); + BooleanEncodedValue turnRestrictionEnc = createTurnRestrictionEnc("car"); r.setRestrictions(Arrays.asList( new Pair<>(GraphRestriction.node(t, 4, d), RestrictionType.NO), new Pair<>(GraphRestriction.node(s, 3, a), RestrictionType.NO), @@ -158,13 +156,13 @@ void viaWay_no_withOverlap_more_complex() { new Pair<>(GraphRestriction.way(b, c, d, nodes(7, 8)), RestrictionType.NO), new Pair<>(GraphRestriction.way(c, d, a, nodes(8, 4)), RestrictionType.NO), new Pair<>(GraphRestriction.way(d, a, b, nodes(4, 3)), RestrictionType.NO) - ), turnCostEnc); + ), turnRestrictionEnc); - assertEquals(nodes(0, 3, 7, 8, 9), calcPath(0, 9, turnCostEnc)); - assertEquals(nodes(5, 4, 3, 7, 10, 11, 8, 9), calcPath(5, 9, turnCostEnc)); - assertEquals(nodes(5, 4, 3, 2), calcPath(5, 2, turnCostEnc)); - assertEquals(nodes(0, 3, 7, 10), calcPath(0, 10, turnCostEnc)); - assertEquals(nodes(6, 7, 8, 9), calcPath(6, 9, turnCostEnc)); + assertEquals(nodes(0, 3, 7, 8, 9), calcPath(0, 9, turnRestrictionEnc)); + assertEquals(nodes(5, 4, 3, 7, 10, 11, 8, 9), calcPath(5, 9, turnRestrictionEnc)); + assertEquals(nodes(5, 4, 3, 2), calcPath(5, 2, turnRestrictionEnc)); + assertEquals(nodes(0, 3, 7, 10), calcPath(0, 10, turnRestrictionEnc)); + assertEquals(nodes(6, 7, 8, 9), calcPath(6, 9, turnRestrictionEnc)); } @Test @@ -183,22 +181,22 @@ void viaWay_only() { int e = edge(4, 5); int f = edge(5, 7); int g = edge(5, 6); - DecimalEncodedValue turnCostEnc = createTurnCostEnc("car"); + BooleanEncodedValue turnRestrictionEnc = createTurnRestrictionEnc("car"); r.setRestrictions(Arrays.asList( new Pair<>(GraphRestriction.way(a, d, f, nodes(2, 5)), RestrictionType.ONLY), // we add a few more restrictions, because that happens a lot in real data new Pair<>(GraphRestriction.way(c, d, g, nodes(2, 5)), RestrictionType.NO), new Pair<>(GraphRestriction.node(e, 5, f), RestrictionType.NO) - ), turnCostEnc); + ), turnRestrictionEnc); // following the restriction is allowed of course - assertEquals(nodes(1, 2, 5, 7), calcPath(1, 7, turnCostEnc)); + assertEquals(nodes(1, 2, 5, 7), calcPath(1, 7, turnRestrictionEnc)); // taking another turn at the beginning is not allowed - assertEquals(nodes(), calcPath(1, 3, turnCostEnc)); + assertEquals(nodes(), calcPath(1, 3, turnRestrictionEnc)); // taking another turn after the first turn is not allowed either - assertEquals(nodes(), calcPath(1, 4, turnCostEnc)); + assertEquals(nodes(), calcPath(1, 4, turnRestrictionEnc)); // coming from somewhere we can go anywhere - assertEquals(nodes(0, 2, 5, 6), calcPath(0, 6, turnCostEnc)); - assertEquals(nodes(0, 2, 5, 7), calcPath(0, 7, turnCostEnc)); + assertEquals(nodes(0, 2, 5, 6), calcPath(0, 6, turnRestrictionEnc)); + assertEquals(nodes(0, 2, 5, 7), calcPath(0, 7, turnRestrictionEnc)); } @Test @@ -212,7 +210,7 @@ void viaWay_only_twoRestrictionsSharingSameVia() { int c = edge(1, 2); int d = edge(2, 3); int e = edge(2, 4); - DecimalEncodedValue turnCostEnc = createTurnCostEnc("car"); + BooleanEncodedValue turnRestrictionEnc = createTurnRestrictionEnc("car"); assertThrows(IllegalStateException.class, () -> r.setRestrictions(Arrays.asList( // These are two 'only' via-way restrictions that share the same via way. A real-world example can // be found in Rüdesheim am Rhein where vehicles either have to go straight or enter the ferry depending @@ -220,18 +218,29 @@ void viaWay_only_twoRestrictionsSharingSameVia() { // We have to make sure such cases are ignored already when we parse the OSM data. new Pair<>(GraphRestriction.way(a, c, d, nodes(1, 2)), RestrictionType.ONLY), new Pair<>(GraphRestriction.way(b, c, e, nodes(1, 2)), RestrictionType.ONLY) - ), turnCostEnc) + ), turnRestrictionEnc) ); } - private static DecimalEncodedValue createTurnCostEnc(String name) { - DecimalEncodedValue turnCostEnc = TurnCost.create(name, 1); - turnCostEnc.init(new EncodedValue.InitializerConfig()); - return turnCostEnc; + private static BooleanEncodedValue createTurnRestrictionEnc(String name) { + BooleanEncodedValue turnRestrictionEnc = TurnRestriction.create(name); + turnRestrictionEnc.init(new EncodedValue.InitializerConfig()); + return turnRestrictionEnc; } - private IntArrayList calcPath(int from, int to, DecimalEncodedValue turnCostEnc) { - return new IntArrayList(new Dijkstra(graph, new SpeedWeighting(speedEnc, turnCostEnc, graph.getTurnCostStorage(), Double.POSITIVE_INFINITY), TraversalMode.EDGE_BASED).calcPath(from, to).calcNodes()); + private IntArrayList calcPath(int from, int to, BooleanEncodedValue turnRestrictionEnc) { + return new IntArrayList(new Dijkstra(graph, new SpeedWeighting(speedEnc, new TurnCostProvider() { + @Override + public double calcTurnWeight(int inEdge, int viaNode, int outEdge) { + if (inEdge == outEdge) return Double.POSITIVE_INFINITY; + return graph.getTurnCostStorage().get(turnRestrictionEnc, inEdge, viaNode, outEdge) ? Double.POSITIVE_INFINITY : 0; + } + + @Override + public long calcTurnMillis(int inEdge, int viaNode, int outEdge) { + return Double.isInfinite(calcTurnWeight(inEdge, viaNode, outEdge)) ? Long.MAX_VALUE : 0L; + } + }), TraversalMode.EDGE_BASED).calcPath(from, to).calcNodes()); } private IntArrayList nodes(int... nodes) { diff --git a/core/src/test/java/com/graphhopper/routing/weighting/FastestWeightingTest.java b/core/src/test/java/com/graphhopper/routing/weighting/FastestWeightingTest.java index 4aeff12c77..5011517e23 100644 --- a/core/src/test/java/com/graphhopper/routing/weighting/FastestWeightingTest.java +++ b/core/src/test/java/com/graphhopper/routing/weighting/FastestWeightingTest.java @@ -39,8 +39,8 @@ public class FastestWeightingTest { private final BooleanEncodedValue accessEnc = new SimpleBooleanEncodedValue("access", true); private final DecimalEncodedValue speedEnc = new DecimalEncodedValueImpl("speed", 5, 5, false); - private final DecimalEncodedValue turnCostEnc = TurnCost.create("car", 10); - private final EncodingManager encodingManager = EncodingManager.start().add(accessEnc).add(speedEnc).addTurnCostEncodedValue(turnCostEnc).build(); + private final BooleanEncodedValue turnRestrictionEnc = TurnRestriction.create("car"); + private final EncodingManager encodingManager = EncodingManager.start().add(accessEnc).add(speedEnc).addTurnCostEncodedValue(turnRestrictionEnc).build(); private final BaseGraph graph = new BaseGraph.Builder(encodingManager).create(); @Test @@ -108,19 +108,18 @@ public void testTime() { @Test public void calcWeightAndTime_withTurnCosts() { BaseGraph graph = new BaseGraph.Builder(encodingManager).withTurnCosts(true).create(); - Weighting weighting = new FastestWeighting(accessEnc, speedEnc, new DefaultTurnCostProvider(turnCostEnc, graph.getTurnCostStorage())); + Weighting weighting = new FastestWeighting(accessEnc, speedEnc, new DefaultTurnCostProvider(turnRestrictionEnc, graph.getTurnCostStorage())); GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(0, 1).setDistance(100)); EdgeIteratorState edge = GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(1, 2).setDistance(100)); - // turn costs are given in seconds - setTurnCost(graph, 0, 1, 2, 5); - assertEquals(6 + 5, GHUtility.calcWeightWithTurnWeight(weighting, edge, false, 0), 1.e-6); - assertEquals(6000 + 5000, GHUtility.calcMillisWithTurnMillis(weighting, edge, false, 0), 1.e-6); + setTurnRestriction(graph, 0, 1, 2); + assertTrue(Double.isInfinite(GHUtility.calcWeightWithTurnWeight(weighting, edge, false, 0))); + assertEquals(Long.MAX_VALUE, GHUtility.calcMillisWithTurnMillis(weighting, edge, false, 0)); } @Test public void calcWeightAndTime_uTurnCosts() { BaseGraph graph = new BaseGraph.Builder(encodingManager).withTurnCosts(true).create(); - Weighting weighting = new FastestWeighting(accessEnc, speedEnc, new DefaultTurnCostProvider(turnCostEnc, graph.getTurnCostStorage(), 40)); + Weighting weighting = new FastestWeighting(accessEnc, speedEnc, new DefaultTurnCostProvider(turnRestrictionEnc, graph.getTurnCostStorage(), 40)); EdgeIteratorState edge = GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(0, 1).setDistance(100)); assertEquals(6 + 40, GHUtility.calcWeightWithTurnWeight(weighting, edge, false, 0), 1.e-6); assertEquals((6 + 40) * 1000, GHUtility.calcMillisWithTurnMillis(weighting, edge, false, 0), 1.e-6); @@ -130,15 +129,12 @@ public void calcWeightAndTime_uTurnCosts() { public void calcWeightAndTime_withTurnCosts_shortest() { BaseGraph graph = new BaseGraph.Builder(encodingManager).withTurnCosts(true).create(); Weighting weighting = new ShortestWeighting(accessEnc, speedEnc, - new DefaultTurnCostProvider(turnCostEnc, graph.getTurnCostStorage())); + new DefaultTurnCostProvider(turnRestrictionEnc, graph.getTurnCostStorage())); GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(0, 1).setDistance(100)); EdgeIteratorState edge = GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(1, 2).setDistance(100)); - // turn costs are given in seconds - setTurnCost(graph, 0, 1, 2, 5); - // todo: for the shortest weighting turn costs cannot be interpreted as seconds? at least when they are added - // to the weight? how much should they contribute ? -// assertEquals(105, AbstractWeighting.calcWeightWithTurnWeight(weighting, edge, false, 0), 1.e-6); - assertEquals(6000 + 5000, GHUtility.calcMillisWithTurnMillis(weighting, edge, false, 0), 1.e-6); + setTurnRestriction(graph, 0, 1, 2); + assertTrue(Double.isInfinite(GHUtility.calcWeightWithTurnWeight(weighting, edge, false, 0))); + assertEquals(Long.MAX_VALUE, GHUtility.calcMillisWithTurnMillis(weighting, edge, false, 0)); } @Test @@ -204,8 +200,8 @@ public void testPrivateTag() { assertEquals(240, bikeWeighting.calcEdgeWeight(edge, false), 1.e-6); } - private void setTurnCost(Graph graph, int from, int via, int to, double turnCost) { - graph.getTurnCostStorage().set(turnCostEnc, getEdge(graph, from, via).getEdge(), via, getEdge(graph, via, to).getEdge(), turnCost); + private void setTurnRestriction(Graph graph, int from, int via, int to) { + graph.getTurnCostStorage().set(turnRestrictionEnc, getEdge(graph, from, via).getEdge(), via, getEdge(graph, via, to).getEdge(), true); } } diff --git a/core/src/test/java/com/graphhopper/routing/weighting/custom/CustomWeightingTest.java b/core/src/test/java/com/graphhopper/routing/weighting/custom/CustomWeightingTest.java index 655257c1fc..942715ac2e 100644 --- a/core/src/test/java/com/graphhopper/routing/weighting/custom/CustomWeightingTest.java +++ b/core/src/test/java/com/graphhopper/routing/weighting/custom/CustomWeightingTest.java @@ -31,7 +31,7 @@ class CustomWeightingTest { DecimalEncodedValue maxSpeedEnc; EnumEncodedValue roadClassEnc; EncodingManager encodingManager; - DecimalEncodedValue turnCostEnc = TurnCost.create("car", 10); + BooleanEncodedValue turnRestrictionEnc = TurnRestriction.create("car"); @BeforeEach public void setup() { @@ -41,7 +41,7 @@ public void setup() { .add(Toll.create()) .add(Hazmat.create()) .add(RouteNetwork.create(BikeNetwork.KEY)) - .addTurnCostEncodedValue(turnCostEnc) + .addTurnCostEncodedValue(turnRestrictionEnc) .build(); maxSpeedEnc = encodingManager.getDecimalEncodedValue(MaxSpeed.KEY); roadClassEnc = encodingManager.getEnumEncodedValue(KEY, RoadClass.class); @@ -409,20 +409,19 @@ public void testTime() { public void calcWeightAndTime_withTurnCosts() { BaseGraph graph = new BaseGraph.Builder(encodingManager).withTurnCosts(true).create(); Weighting weighting = CustomModelParser.createWeighting(accessEnc, avSpeedEnc, null, encodingManager, - new DefaultTurnCostProvider(turnCostEnc, graph.getTurnCostStorage()), new CustomModel()); + new DefaultTurnCostProvider(turnRestrictionEnc, graph.getTurnCostStorage()), new CustomModel()); GHUtility.setSpeed(60, true, true, accessEnc, avSpeedEnc, graph.edge(0, 1).setDistance(100)); EdgeIteratorState edge = GHUtility.setSpeed(60, true, true, accessEnc, avSpeedEnc, graph.edge(1, 2).setDistance(100)); - // turn costs are given in seconds - setTurnCost(graph, 0, 1, 2, 5); - assertEquals(6 + 5, GHUtility.calcWeightWithTurnWeight(weighting, edge, false, 0), 1.e-6); - assertEquals(6000 + 5000, GHUtility.calcMillisWithTurnMillis(weighting, edge, false, 0), 1.e-6); + setTurnRestriction(graph, 0, 1, 2); + assertTrue(Double.isInfinite(GHUtility.calcWeightWithTurnWeight(weighting, edge, false, 0))); + assertEquals(Long.MAX_VALUE, GHUtility.calcMillisWithTurnMillis(weighting, edge, false, 0)); } @Test public void calcWeightAndTime_uTurnCosts() { BaseGraph graph = new BaseGraph.Builder(encodingManager).withTurnCosts(true).create(); Weighting weighting = CustomModelParser.createWeighting(accessEnc, avSpeedEnc, null, - encodingManager, new DefaultTurnCostProvider(turnCostEnc, graph.getTurnCostStorage(), 40), new CustomModel()); + encodingManager, new DefaultTurnCostProvider(turnRestrictionEnc, graph.getTurnCostStorage(), 40), new CustomModel()); EdgeIteratorState edge = GHUtility.setSpeed(60, true, true, accessEnc, avSpeedEnc, graph.edge(0, 1).setDistance(100)); assertEquals(6 + 40, GHUtility.calcWeightWithTurnWeight(weighting, edge, false, 0), 1.e-6); assertEquals((6 + 40) * 1000, GHUtility.calcMillisWithTurnMillis(weighting, edge, false, 0), 1.e-6); @@ -432,15 +431,12 @@ public void calcWeightAndTime_uTurnCosts() { public void calcWeightAndTime_withTurnCosts_shortest() { BaseGraph graph = new BaseGraph.Builder(encodingManager).withTurnCosts(true).create(); Weighting weighting = new ShortestWeighting(accessEnc, avSpeedEnc, - new DefaultTurnCostProvider(turnCostEnc, graph.getTurnCostStorage())); + new DefaultTurnCostProvider(turnRestrictionEnc, graph.getTurnCostStorage())); GHUtility.setSpeed(60, true, true, accessEnc, avSpeedEnc, graph.edge(0, 1).setDistance(100)); EdgeIteratorState edge = GHUtility.setSpeed(60, true, true, accessEnc, avSpeedEnc, graph.edge(1, 2).setDistance(100)); - // turn costs are given in seconds - setTurnCost(graph, 0, 1, 2, 5); - // todo: for the shortest weighting turn costs cannot be interpreted as seconds? at least when they are added - // to the weight? how much should they contribute ? -// assertEquals(105, AbstractWeighting.calcWeightWithTurnWeight(weighting, edge, false, 0), 1.e-6); - assertEquals(6000 + 5000, GHUtility.calcMillisWithTurnMillis(weighting, edge, false, 0), 1.e-6); + setTurnRestriction(graph, 0, 1, 2); + assertTrue(Double.isInfinite(GHUtility.calcWeightWithTurnWeight(weighting, edge, false, 0))); + assertEquals(Long.MAX_VALUE, GHUtility.calcMillisWithTurnMillis(weighting, edge, false, 0)); } @Test @@ -508,7 +504,7 @@ public void testPrivateTag() { assertEquals(240, bikeWeighting.calcEdgeWeight(edge, false), .01); } - private void setTurnCost(Graph graph, int from, int via, int to, double turnCost) { - graph.getTurnCostStorage().set(turnCostEnc, getEdge(graph, from, via).getEdge(), via, getEdge(graph, via, to).getEdge(), turnCost); + private void setTurnRestriction(Graph graph, int from, int via, int to) { + graph.getTurnCostStorage().set(turnRestrictionEnc, getEdge(graph, from, via).getEdge(), via, getEdge(graph, via, to).getEdge(), true); } } diff --git a/tools/src/main/java/com/graphhopper/tools/Measurement.java b/tools/src/main/java/com/graphhopper/tools/Measurement.java index 40e35494f3..6cfe627789 100644 --- a/tools/src/main/java/com/graphhopper/tools/Measurement.java +++ b/tools/src/main/java/com/graphhopper/tools/Measurement.java @@ -27,10 +27,7 @@ import com.graphhopper.config.Profile; import com.graphhopper.jackson.Jackson; import com.graphhopper.routing.ch.PrepareContractionHierarchies; -import com.graphhopper.routing.ev.BooleanEncodedValue; -import com.graphhopper.routing.ev.Subnetwork; -import com.graphhopper.routing.ev.TurnCost; -import com.graphhopper.routing.ev.VehicleAccess; +import com.graphhopper.routing.ev.*; import com.graphhopper.routing.lm.LMConfig; import com.graphhopper.routing.lm.PrepareLandmarks; import com.graphhopper.routing.util.*; @@ -178,7 +175,7 @@ protected void importOSM() { BaseGraph g = hopper.getBaseGraph(); EncodingManager encodingManager = hopper.getEncodingManager(); BooleanEncodedValue accessEnc = encodingManager.getBooleanEncodedValue(VehicleAccess.key(vehicle)); - boolean withTurnCosts = encodingManager.hasEncodedValue(TurnCost.key(vehicle)); + boolean withTurnCosts = encodingManager.hasEncodedValue(TurnRestriction.key(vehicle)); StopWatch sw = new StopWatch().start(); try { diff --git a/web-bundle/src/main/java/com/graphhopper/resources/InfoResource.java b/web-bundle/src/main/java/com/graphhopper/resources/InfoResource.java index bd4400353c..f59725ad30 100644 --- a/web-bundle/src/main/java/com/graphhopper/resources/InfoResource.java +++ b/web-bundle/src/main/java/com/graphhopper/resources/InfoResource.java @@ -94,7 +94,7 @@ public Info getInfo() { Info.ProfileData profileData = new Info.ProfileData(p.getName()); info.profiles.add(profileData); defaultHiddenEVs.addAll(Arrays.asList(VehiclePriority.key(p.getName()), VehicleAccess.key(p.getName()), - VehicleSpeed.key(p.getName()), TurnCost.key(p.getName()), Subnetwork.key(p.getName()))); + VehicleSpeed.key(p.getName()), TurnRestriction.key(p.getName()), Subnetwork.key(p.getName()))); } if (config.has("gtfs.file")) info.profiles.add(new Info.ProfileData("pt")); From 1a251bcff0f2ed027cc9069d614a056532b43cd3 Mon Sep 17 00:00:00 2001 From: easbar Date: Wed, 13 Sep 2023 18:53:05 +0200 Subject: [PATCH 12/22] Use dead end enc --- .../java/com/graphhopper/GraphHopper.java | 16 ++++------ .../routing/DefaultWeightingFactory.java | 5 +-- .../weighting/DefaultTurnCostProvider.java | 32 ++++++++++--------- .../weighting/FastestWeightingTest.java | 7 ++-- .../weighting/custom/CustomWeightingTest.java | 8 +++-- .../graphhopper/example/RoutingExampleTC.java | 2 +- 6 files changed, 37 insertions(+), 33 deletions(-) diff --git a/core/src/main/java/com/graphhopper/GraphHopper.java b/core/src/main/java/com/graphhopper/GraphHopper.java index 85b6e15631..6a9e6c24f0 100644 --- a/core/src/main/java/com/graphhopper/GraphHopper.java +++ b/core/src/main/java/com/graphhopper/GraphHopper.java @@ -1398,9 +1398,9 @@ protected void cleanUp() { // find dead-ends and make sure u-turns are allowed there for (Profile profile : profilesByName.values()) { if (profile.isTurnCosts()) { - DecimalEncodedValue turnCostEnc = encodingManager.getDecimalEncodedValue(TurnCost.key(profile.getVehicle())); + BooleanEncodedValue deadEndEnc = encodingManager.getBooleanEncodedValue(DeadEnd.key(profile.getVehicle())); Weighting weighting = createWeighting(profile, new PMap().putObject(Parameters.Routing.U_TURN_COSTS, 0)); - allowUTurnsAtDeadEnds(weighting, turnCostEnc); + findDeadEndUTurns(weighting, deadEndEnc); } } @@ -1412,7 +1412,7 @@ protected void cleanUp() { logger.info("nodes: " + Helper.nf(baseGraph.getNodes()) + ", edges: " + Helper.nf(baseGraph.getEdges())); } - private void allowUTurnsAtDeadEnds(Weighting weighting, DecimalEncodedValue turnCostEnc) { + private void findDeadEndUTurns(Weighting weighting, BooleanEncodedValue deadEndEnc) { EdgeExplorer inExplorer = baseGraph.createEdgeExplorer(); EdgeExplorer outExplorer = baseGraph.createEdgeExplorer(); for (int node = 0; node < baseGraph.getNodes(); node++) { @@ -1425,17 +1425,15 @@ private void allowUTurnsAtDeadEnds(Weighting weighting, DecimalEncodedValue turn if (toEdge.getEdge() != fromEdge.getEdge() && Double.isFinite(GHUtility.calcWeightWithTurnWeight(weighting, toEdge, false, fromEdge.getEdge()))) continue OUTER; } - // the only way to continue from fromEdge is a u-turn. this is a dead-end and we - // must allow the u-turn - setUTurnAllowed(baseGraph, turnCostEnc, fromEdge.getEdge(), node); + // the only way to continue from fromEdge is a u-turn. this is a dead-end u-turn + setDeadEndUTurn(baseGraph, deadEndEnc, fromEdge.getEdge(), node); } } } } - private void setUTurnAllowed(BaseGraph baseGraph, DecimalEncodedValue turnCostEnc, int fromEdge, int viaNode) { - // total hack: We use cost='infinity' to mark the u-turns that are **allowed**, see DefaultTurnCostProvider - baseGraph.getTurnCostStorage().set(turnCostEnc, fromEdge, viaNode, fromEdge, Double.POSITIVE_INFINITY); + private void setDeadEndUTurn(BaseGraph baseGraph, BooleanEncodedValue deadEndEnc, int fromEdge, int viaNode) { + baseGraph.getTurnCostStorage().set(deadEndEnc, fromEdge, viaNode, fromEdge, true); } private List buildSubnetworkRemovalJobs() { diff --git a/core/src/main/java/com/graphhopper/routing/DefaultWeightingFactory.java b/core/src/main/java/com/graphhopper/routing/DefaultWeightingFactory.java index f881810b39..815fd93c38 100644 --- a/core/src/main/java/com/graphhopper/routing/DefaultWeightingFactory.java +++ b/core/src/main/java/com/graphhopper/routing/DefaultWeightingFactory.java @@ -59,10 +59,11 @@ public Weighting createWeighting(Profile profile, PMap requestHints, boolean dis TurnCostProvider turnCostProvider; if (profile.isTurnCosts() && !disableTurnCosts) { BooleanEncodedValue turnRestrictionEnc = encodingManager.getBooleanEncodedValue(TurnRestriction.key(vehicle)); - if (turnRestrictionEnc == null) + BooleanEncodedValue deadEndEnc = encodingManager.getBooleanEncodedValue(DeadEnd.key(vehicle)); + if (turnRestrictionEnc == null || deadEndEnc == null) throw new IllegalArgumentException("Vehicle " + vehicle + " does not support turn costs"); int uTurnCosts = hints.getInt(Parameters.Routing.U_TURN_COSTS, INFINITE_U_TURN_COSTS); - turnCostProvider = new DefaultTurnCostProvider(turnRestrictionEnc, graph.getTurnCostStorage(), uTurnCosts); + turnCostProvider = new DefaultTurnCostProvider(turnRestrictionEnc, deadEndEnc, graph.getTurnCostStorage(), uTurnCosts); } else { turnCostProvider = NO_TURN_COST_PROVIDER; } diff --git a/core/src/main/java/com/graphhopper/routing/weighting/DefaultTurnCostProvider.java b/core/src/main/java/com/graphhopper/routing/weighting/DefaultTurnCostProvider.java index 299dd04a65..ccdabcac4b 100644 --- a/core/src/main/java/com/graphhopper/routing/weighting/DefaultTurnCostProvider.java +++ b/core/src/main/java/com/graphhopper/routing/weighting/DefaultTurnCostProvider.java @@ -26,19 +26,20 @@ public class DefaultTurnCostProvider implements TurnCostProvider { private final BooleanEncodedValue turnRestrictionEnc; + private final BooleanEncodedValue deadEndEnc; private final TurnCostStorage turnCostStorage; private final int uTurnCostsInt; private final double uTurnCosts; - public DefaultTurnCostProvider(BooleanEncodedValue turnRestrictionEnc, TurnCostStorage turnCostStorage) { - this(turnRestrictionEnc, turnCostStorage, Weighting.INFINITE_U_TURN_COSTS); + public DefaultTurnCostProvider(BooleanEncodedValue turnRestrictionEnc, BooleanEncodedValue deadEndEnc, TurnCostStorage turnCostStorage) { + this(turnRestrictionEnc, deadEndEnc, turnCostStorage, Weighting.INFINITE_U_TURN_COSTS); } /** * @param uTurnCosts the costs of a u-turn in seconds, for {@link Weighting#INFINITE_U_TURN_COSTS} the u-turn costs - * will be infinite + * will be infinite. u-turns are only allowed at dead ends. */ - public DefaultTurnCostProvider(BooleanEncodedValue turnRestrictionEnc, TurnCostStorage turnCostStorage, int uTurnCosts) { + public DefaultTurnCostProvider(BooleanEncodedValue turnRestrictionEnc, BooleanEncodedValue deadEndEnc, TurnCostStorage turnCostStorage, int uTurnCosts) { if (uTurnCosts < 0 && uTurnCosts != INFINITE_U_TURN_COSTS) { throw new IllegalArgumentException("u-turn costs must be positive, or equal to " + INFINITE_U_TURN_COSTS + " (=infinite costs)"); } @@ -49,6 +50,7 @@ public DefaultTurnCostProvider(BooleanEncodedValue turnRestrictionEnc, TurnCostS } // if null the TurnCostProvider can be still useful for edge-based routing this.turnRestrictionEnc = turnRestrictionEnc; + this.deadEndEnc = deadEndEnc; this.turnCostStorage = turnCostStorage; } @@ -56,20 +58,20 @@ public BooleanEncodedValue getTurnRestrictionEnc() { return turnRestrictionEnc; } + public BooleanEncodedValue getDeadEndEnc() { + return deadEndEnc; + } + @Override public double calcTurnWeight(int edgeFrom, int nodeVia, int edgeTo) { - if (!EdgeIterator.Edge.isValid(edgeFrom) || !EdgeIterator.Edge.isValid(edgeTo)) { + if (!EdgeIterator.Edge.isValid(edgeFrom) || !EdgeIterator.Edge.isValid(edgeTo)) + return 0; + if (turnCostStorage.get(turnRestrictionEnc, edgeFrom, nodeVia, edgeTo)) + return Double.POSITIVE_INFINITY; + else if (edgeFrom == edgeTo) + return turnCostStorage.get(deadEndEnc, edgeFrom, nodeVia, edgeTo) ? uTurnCosts : Double.POSITIVE_INFINITY; + else return 0; - } - double tCost = 0; - if (edgeFrom == edgeTo) { - // note that the u-turn costs overwrite any turn costs set in TurnCostStorage - tCost = uTurnCosts; - } else { - if (turnRestrictionEnc != null) - tCost = turnCostStorage.get(turnRestrictionEnc, edgeFrom, nodeVia, edgeTo) ? Double.POSITIVE_INFINITY : 0; - } - return tCost; } @Override diff --git a/core/src/test/java/com/graphhopper/routing/weighting/FastestWeightingTest.java b/core/src/test/java/com/graphhopper/routing/weighting/FastestWeightingTest.java index 5011517e23..5ad7165d59 100644 --- a/core/src/test/java/com/graphhopper/routing/weighting/FastestWeightingTest.java +++ b/core/src/test/java/com/graphhopper/routing/weighting/FastestWeightingTest.java @@ -40,6 +40,7 @@ public class FastestWeightingTest { private final BooleanEncodedValue accessEnc = new SimpleBooleanEncodedValue("access", true); private final DecimalEncodedValue speedEnc = new DecimalEncodedValueImpl("speed", 5, 5, false); private final BooleanEncodedValue turnRestrictionEnc = TurnRestriction.create("car"); + private final BooleanEncodedValue deadEndEnc = DeadEnd.create("car"); private final EncodingManager encodingManager = EncodingManager.start().add(accessEnc).add(speedEnc).addTurnCostEncodedValue(turnRestrictionEnc).build(); private final BaseGraph graph = new BaseGraph.Builder(encodingManager).create(); @@ -108,7 +109,7 @@ public void testTime() { @Test public void calcWeightAndTime_withTurnCosts() { BaseGraph graph = new BaseGraph.Builder(encodingManager).withTurnCosts(true).create(); - Weighting weighting = new FastestWeighting(accessEnc, speedEnc, new DefaultTurnCostProvider(turnRestrictionEnc, graph.getTurnCostStorage())); + Weighting weighting = new FastestWeighting(accessEnc, speedEnc, new DefaultTurnCostProvider(turnRestrictionEnc, deadEndEnc, graph.getTurnCostStorage())); GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(0, 1).setDistance(100)); EdgeIteratorState edge = GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(1, 2).setDistance(100)); setTurnRestriction(graph, 0, 1, 2); @@ -119,7 +120,7 @@ public void calcWeightAndTime_withTurnCosts() { @Test public void calcWeightAndTime_uTurnCosts() { BaseGraph graph = new BaseGraph.Builder(encodingManager).withTurnCosts(true).create(); - Weighting weighting = new FastestWeighting(accessEnc, speedEnc, new DefaultTurnCostProvider(turnRestrictionEnc, graph.getTurnCostStorage(), 40)); + Weighting weighting = new FastestWeighting(accessEnc, speedEnc, new DefaultTurnCostProvider(turnRestrictionEnc, deadEndEnc, graph.getTurnCostStorage(), 40)); EdgeIteratorState edge = GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(0, 1).setDistance(100)); assertEquals(6 + 40, GHUtility.calcWeightWithTurnWeight(weighting, edge, false, 0), 1.e-6); assertEquals((6 + 40) * 1000, GHUtility.calcMillisWithTurnMillis(weighting, edge, false, 0), 1.e-6); @@ -129,7 +130,7 @@ public void calcWeightAndTime_uTurnCosts() { public void calcWeightAndTime_withTurnCosts_shortest() { BaseGraph graph = new BaseGraph.Builder(encodingManager).withTurnCosts(true).create(); Weighting weighting = new ShortestWeighting(accessEnc, speedEnc, - new DefaultTurnCostProvider(turnRestrictionEnc, graph.getTurnCostStorage())); + new DefaultTurnCostProvider(turnRestrictionEnc, deadEndEnc, graph.getTurnCostStorage())); GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(0, 1).setDistance(100)); EdgeIteratorState edge = GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(1, 2).setDistance(100)); setTurnRestriction(graph, 0, 1, 2); diff --git a/core/src/test/java/com/graphhopper/routing/weighting/custom/CustomWeightingTest.java b/core/src/test/java/com/graphhopper/routing/weighting/custom/CustomWeightingTest.java index 942715ac2e..40ffdcb017 100644 --- a/core/src/test/java/com/graphhopper/routing/weighting/custom/CustomWeightingTest.java +++ b/core/src/test/java/com/graphhopper/routing/weighting/custom/CustomWeightingTest.java @@ -32,6 +32,7 @@ class CustomWeightingTest { EnumEncodedValue roadClassEnc; EncodingManager encodingManager; BooleanEncodedValue turnRestrictionEnc = TurnRestriction.create("car"); + BooleanEncodedValue deadEndEnc = DeadEnd.create("car"); @BeforeEach public void setup() { @@ -42,6 +43,7 @@ public void setup() { .add(Hazmat.create()) .add(RouteNetwork.create(BikeNetwork.KEY)) .addTurnCostEncodedValue(turnRestrictionEnc) + .addTurnCostEncodedValue(deadEndEnc) .build(); maxSpeedEnc = encodingManager.getDecimalEncodedValue(MaxSpeed.KEY); roadClassEnc = encodingManager.getEnumEncodedValue(KEY, RoadClass.class); @@ -409,7 +411,7 @@ public void testTime() { public void calcWeightAndTime_withTurnCosts() { BaseGraph graph = new BaseGraph.Builder(encodingManager).withTurnCosts(true).create(); Weighting weighting = CustomModelParser.createWeighting(accessEnc, avSpeedEnc, null, encodingManager, - new DefaultTurnCostProvider(turnRestrictionEnc, graph.getTurnCostStorage()), new CustomModel()); + new DefaultTurnCostProvider(turnRestrictionEnc, deadEndEnc, graph.getTurnCostStorage()), new CustomModel()); GHUtility.setSpeed(60, true, true, accessEnc, avSpeedEnc, graph.edge(0, 1).setDistance(100)); EdgeIteratorState edge = GHUtility.setSpeed(60, true, true, accessEnc, avSpeedEnc, graph.edge(1, 2).setDistance(100)); setTurnRestriction(graph, 0, 1, 2); @@ -421,7 +423,7 @@ public void calcWeightAndTime_withTurnCosts() { public void calcWeightAndTime_uTurnCosts() { BaseGraph graph = new BaseGraph.Builder(encodingManager).withTurnCosts(true).create(); Weighting weighting = CustomModelParser.createWeighting(accessEnc, avSpeedEnc, null, - encodingManager, new DefaultTurnCostProvider(turnRestrictionEnc, graph.getTurnCostStorage(), 40), new CustomModel()); + encodingManager, new DefaultTurnCostProvider(turnRestrictionEnc, deadEndEnc, graph.getTurnCostStorage(), 40), new CustomModel()); EdgeIteratorState edge = GHUtility.setSpeed(60, true, true, accessEnc, avSpeedEnc, graph.edge(0, 1).setDistance(100)); assertEquals(6 + 40, GHUtility.calcWeightWithTurnWeight(weighting, edge, false, 0), 1.e-6); assertEquals((6 + 40) * 1000, GHUtility.calcMillisWithTurnMillis(weighting, edge, false, 0), 1.e-6); @@ -431,7 +433,7 @@ public void calcWeightAndTime_uTurnCosts() { public void calcWeightAndTime_withTurnCosts_shortest() { BaseGraph graph = new BaseGraph.Builder(encodingManager).withTurnCosts(true).create(); Weighting weighting = new ShortestWeighting(accessEnc, avSpeedEnc, - new DefaultTurnCostProvider(turnRestrictionEnc, graph.getTurnCostStorage())); + new DefaultTurnCostProvider(turnRestrictionEnc, deadEndEnc, graph.getTurnCostStorage())); GHUtility.setSpeed(60, true, true, accessEnc, avSpeedEnc, graph.edge(0, 1).setDistance(100)); EdgeIteratorState edge = GHUtility.setSpeed(60, true, true, accessEnc, avSpeedEnc, graph.edge(1, 2).setDistance(100)); setTurnRestriction(graph, 0, 1, 2); diff --git a/example/src/main/java/com/graphhopper/example/RoutingExampleTC.java b/example/src/main/java/com/graphhopper/example/RoutingExampleTC.java index 3cc2429d0a..9e75369dbe 100644 --- a/example/src/main/java/com/graphhopper/example/RoutingExampleTC.java +++ b/example/src/main/java/com/graphhopper/example/RoutingExampleTC.java @@ -71,7 +71,7 @@ static GraphHopper createGraphHopperInstance(String ghLoc) { // enabling turn costs means OSM turn restriction constraints like 'no_left_turn' will be taken into account .setTurnCosts(true) // we can also set u_turn_costs (in seconds). by default no u-turns are allowed, but with this setting - // we will consider u-turns at all deadends with a 40s time penalty + // we will consider u-turns at all dead-ends with a 40s time penalty .putHint("u_turn_costs", 40); hopper.setProfiles(profile); // enable CH for our profile. since turn costs are enabled this will take more time and memory to prepare than From 60a6ba3ac0f2de90e453b187bd0496ab97bcd9cb Mon Sep 17 00:00:00 2001 From: easbar Date: Mon, 25 Sep 2023 13:48:22 +0200 Subject: [PATCH 13/22] fix some tests --- .../java/com/graphhopper/GraphHopperTest.java | 33 +++++++++---------- 1 file changed, 16 insertions(+), 17 deletions(-) diff --git a/core/src/test/java/com/graphhopper/GraphHopperTest.java b/core/src/test/java/com/graphhopper/GraphHopperTest.java index 0ff388236d..92811013d4 100644 --- a/core/src/test/java/com/graphhopper/GraphHopperTest.java +++ b/core/src/test/java/com/graphhopper/GraphHopperTest.java @@ -23,10 +23,7 @@ import com.graphhopper.reader.ReaderWay; import com.graphhopper.reader.dem.SRTMProvider; import com.graphhopper.reader.dem.SkadiProvider; -import com.graphhopper.routing.ev.EdgeIntAccess; -import com.graphhopper.routing.ev.EncodedValueLookup; -import com.graphhopper.routing.ev.RoadEnvironment; -import com.graphhopper.routing.ev.Subnetwork; +import com.graphhopper.routing.ev.*; import com.graphhopper.routing.util.AllEdgesIterator; import com.graphhopper.routing.util.DefaultSnapFilter; import com.graphhopper.routing.util.EdgeFilter; @@ -1723,11 +1720,10 @@ public void testLMConstraints() { assertEquals(3587, response.getBest().getDistance(), 1); } - @Disabled("todonow") @Test public void testCreateWeightingHintsMerging() { final String profile = "profile"; - final String vehicle = "mtb"; + final String vehicle = "car"; GraphHopper hopper = new GraphHopper(). setGraphHopperLocation(GH_LOCATION). @@ -1735,13 +1731,19 @@ public void testCreateWeightingHintsMerging() { setProfiles(new Profile(profile).setVehicle(vehicle).setTurnCosts(true).putHint(U_TURN_COSTS, 123)); hopper.importOrLoad(); + BooleanEncodedValue deadEndEnc = hopper.getEncodingManager().getBooleanEncodedValue(DeadEnd.key(vehicle)); + final int edge = 389; + final int node = 57; + // make sure we chose an edge that is a dead-end for this test + assertTrue(hopper.getBaseGraph().getEdgeIteratorState(edge, node).get(deadEndEnc)); + // if we do not pass u_turn_costs with the request hints we get those from the profile Weighting w = hopper.createWeighting(hopper.getProfiles().get(0), new PMap()); - assertEquals(123.0, w.calcTurnWeight(5, 6, 5)); + assertEquals(123.0, w.calcTurnWeight(edge, node, edge)); // we can overwrite the u_turn_costs given in the profile w = hopper.createWeighting(hopper.getProfiles().get(0), new PMap().putObject(U_TURN_COSTS, 46)); - assertEquals(46.0, w.calcTurnWeight(5, 6, 5)); + assertEquals(46.0, w.calcTurnWeight(edge, node, edge)); } @Test @@ -1925,7 +1927,6 @@ public void testIssue1960() { assertEquals(149504, path.getTime()); } - @Disabled("todonow") @Test public void testTurnCostsOnOff() { final String profile1 = "profile_no_turn_costs"; @@ -1952,7 +1953,7 @@ public void testTurnCostsOnOff() { req.setProfile("profile_turn_costs"); best = hopper.route(req).getBest(); - assertEquals(476, best.getDistance(), 1); + assertEquals(1043, best.getDistance(), 1); consistenceCheck(best); } @@ -2262,7 +2263,6 @@ private GHResponse calcCurbsidePath(GraphHopper hopper, GHPoint source, GHPoint return hopper.route(req); } - @Disabled("todonow") @Test public void testCHWithFiniteUTurnCosts() { GraphHopper h = new GraphHopper(). @@ -2278,14 +2278,14 @@ public void testCHWithFiniteUTurnCosts() { GHPoint q = new GHPoint(43.73222, 7.415557); GHRequest req = new GHRequest(p, q); req.setProfile("my_profile"); - // we force the start/target directions such that there are u-turns right after we start and right before - // we reach the target. at the start location we do a u-turn at the crossing with the *steps* ('ghost junction') + // we force the start/target directions such that we need to turn around after the start and + // before we reach the target req.setCurbsides(Arrays.asList("right", "right")); GHResponse res = h.route(req); assertFalse(res.hasErrors(), "routing should not fail"); - assertEquals(242.5, res.getBest().getRouteWeight(), 0.1); - assertEquals(1917, res.getBest().getDistance(), 1); - assertEquals(243000, res.getBest().getTime(), 1000); + assertEquals(292.6, res.getBest().getRouteWeight(), 0.1); + assertEquals(2667, res.getBest().getDistance(), 1); + assertEquals(292000, res.getBest().getTime(), 1000); } @Test @@ -2658,7 +2658,6 @@ public void germanyCountryRuleAvoidsTracks() { assertEquals(4186, distance, 1); } - @Disabled("todonow") @Test void curbsideWithSubnetwork_issue2502() { final String profile = "profile"; From 750388f2f97e0422796e2ca2f92737eb4a4f4147 Mon Sep 17 00:00:00 2001 From: easbar Date: Mon, 25 Sep 2023 14:44:31 +0200 Subject: [PATCH 14/22] tmp --- core/src/main/java/com/graphhopper/GraphHopper.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/core/src/main/java/com/graphhopper/GraphHopper.java b/core/src/main/java/com/graphhopper/GraphHopper.java index c55c845d62..ced3931b3b 100644 --- a/core/src/main/java/com/graphhopper/GraphHopper.java +++ b/core/src/main/java/com/graphhopper/GraphHopper.java @@ -1407,7 +1407,8 @@ protected void cleanUp() { for (Profile profile : profilesByName.values()) { if (profile.isTurnCosts()) { BooleanEncodedValue deadEndEnc = encodingManager.getBooleanEncodedValue(DeadEnd.key(profile.getVehicle())); - Weighting weighting = createWeighting(profile, new PMap().putObject(Parameters.Routing.U_TURN_COSTS, 0)); + // we must disable turn costs for the dead-end search + Weighting weighting = createWeighting(profile, new PMap(), true); findDeadEndUTurns(weighting, deadEndEnc); } } From 1b7ea5d86a210acedc97724ffaf55f9579b78947 Mon Sep 17 00:00:00 2001 From: easbar Date: Mon, 25 Sep 2023 17:07:12 +0200 Subject: [PATCH 15/22] reverse order / green --- .../java/com/graphhopper/GraphHopper.java | 55 ++++++++++++++++--- .../weighting/FastestWeightingTest.java | 6 +- .../weighting/custom/CustomWeightingTest.java | 3 + 3 files changed, 54 insertions(+), 10 deletions(-) diff --git a/core/src/main/java/com/graphhopper/GraphHopper.java b/core/src/main/java/com/graphhopper/GraphHopper.java index ced3931b3b..66f5907326 100644 --- a/core/src/main/java/com/graphhopper/GraphHopper.java +++ b/core/src/main/java/com/graphhopper/GraphHopper.java @@ -1403,6 +1403,13 @@ protected List prepareLM(boolean closeEarly, List co * Internal method to clean up the graph. */ protected void cleanUp() { + PrepareRoutingSubnetworks preparation = new PrepareRoutingSubnetworks(baseGraph.getBaseGraph(), buildSubnetworkRemovalJobs()); + preparation.setMinNetworkSize(minNetworkSize); + preparation.setThreads(subnetworksThreads); + preparation.doWork(); + properties.put("profiles", getProfilesString()); + logger.info("nodes: " + Helper.nf(baseGraph.getNodes()) + ", edges: " + Helper.nf(baseGraph.getEdges())); + // find dead-ends and make sure u-turns are allowed there for (Profile profile : profilesByName.values()) { if (profile.isTurnCosts()) { @@ -1412,13 +1419,6 @@ protected void cleanUp() { findDeadEndUTurns(weighting, deadEndEnc); } } - - PrepareRoutingSubnetworks preparation = new PrepareRoutingSubnetworks(baseGraph.getBaseGraph(), buildSubnetworkRemovalJobs()); - preparation.setMinNetworkSize(minNetworkSize); - preparation.setThreads(subnetworksThreads); - preparation.doWork(); - properties.put("profiles", getProfilesString()); - logger.info("nodes: " + Helper.nf(baseGraph.getNodes()) + ", edges: " + Helper.nf(baseGraph.getEdges())); } private void findDeadEndUTurns(Weighting weighting, BooleanEncodedValue deadEndEnc) { @@ -1449,8 +1449,45 @@ private List buildSubnetworkRemovalJobs() { List jobs = new ArrayList<>(); for (Profile profile : profilesByName.values()) { // if turn costs are enabled use u-turn costs of zero as we only want to make sure the graph is fully connected assuming finite u-turn costs - Weighting weighting = createWeighting(profile, new PMap().putObject(Parameters.Routing.U_TURN_COSTS, 0)); - jobs.add(new PrepareJob(encodingManager.getBooleanEncodedValue(Subnetwork.key(profile.getName())), weighting)); + Weighting weighting = createWeighting(profile, new PMap()); + Weighting w = new Weighting() { + @Override + public double getMinWeight(double distance) { + return weighting.getMinWeight(distance); + } + + @Override + public double calcEdgeWeight(EdgeIteratorState edgeState, boolean reverse) { + return weighting.calcEdgeWeight(edgeState, reverse); + } + + @Override + public long calcEdgeMillis(EdgeIteratorState edgeState, boolean reverse) { + return weighting.calcEdgeMillis(edgeState, reverse); + } + + @Override + public double calcTurnWeight(int inEdge, int viaNode, int outEdge) { + if (inEdge == outEdge) return 0; + return weighting.calcTurnWeight(inEdge, viaNode, outEdge); + } + + @Override + public long calcTurnMillis(int inEdge, int viaNode, int outEdge) { + return weighting.calcTurnMillis(inEdge, viaNode, outEdge); + } + + @Override + public boolean hasTurnCosts() { + return weighting.hasTurnCosts(); + } + + @Override + public String getName() { + return weighting.getName(); + } + }; + jobs.add(new PrepareJob(encodingManager.getBooleanEncodedValue(Subnetwork.key(profile.getName())), w)); } return jobs; } diff --git a/core/src/test/java/com/graphhopper/routing/weighting/FastestWeightingTest.java b/core/src/test/java/com/graphhopper/routing/weighting/FastestWeightingTest.java index 5ad7165d59..b830799188 100644 --- a/core/src/test/java/com/graphhopper/routing/weighting/FastestWeightingTest.java +++ b/core/src/test/java/com/graphhopper/routing/weighting/FastestWeightingTest.java @@ -41,7 +41,8 @@ public class FastestWeightingTest { private final DecimalEncodedValue speedEnc = new DecimalEncodedValueImpl("speed", 5, 5, false); private final BooleanEncodedValue turnRestrictionEnc = TurnRestriction.create("car"); private final BooleanEncodedValue deadEndEnc = DeadEnd.create("car"); - private final EncodingManager encodingManager = EncodingManager.start().add(accessEnc).add(speedEnc).addTurnCostEncodedValue(turnRestrictionEnc).build(); + private final EncodingManager encodingManager = EncodingManager.start().add(accessEnc).add(speedEnc) + .addTurnCostEncodedValue(turnRestrictionEnc).addTurnCostEncodedValue(deadEndEnc).build(); private final BaseGraph graph = new BaseGraph.Builder(encodingManager).create(); @Test @@ -122,6 +123,9 @@ public void calcWeightAndTime_uTurnCosts() { BaseGraph graph = new BaseGraph.Builder(encodingManager).withTurnCosts(true).create(); Weighting weighting = new FastestWeighting(accessEnc, speedEnc, new DefaultTurnCostProvider(turnRestrictionEnc, deadEndEnc, graph.getTurnCostStorage(), 40)); EdgeIteratorState edge = GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(0, 1).setDistance(100)); + assertEquals(Double.POSITIVE_INFINITY, GHUtility.calcWeightWithTurnWeight(weighting, edge, false, 0), 1.e-6); + assertEquals(Long.MAX_VALUE, GHUtility.calcMillisWithTurnMillis(weighting, edge, false, 0), 1.e-6); + graph.getTurnCostStorage().set(deadEndEnc, 0, 0, 0, true); assertEquals(6 + 40, GHUtility.calcWeightWithTurnWeight(weighting, edge, false, 0), 1.e-6); assertEquals((6 + 40) * 1000, GHUtility.calcMillisWithTurnMillis(weighting, edge, false, 0), 1.e-6); } diff --git a/core/src/test/java/com/graphhopper/routing/weighting/custom/CustomWeightingTest.java b/core/src/test/java/com/graphhopper/routing/weighting/custom/CustomWeightingTest.java index 40ffdcb017..b844206f4f 100644 --- a/core/src/test/java/com/graphhopper/routing/weighting/custom/CustomWeightingTest.java +++ b/core/src/test/java/com/graphhopper/routing/weighting/custom/CustomWeightingTest.java @@ -425,6 +425,9 @@ public void calcWeightAndTime_uTurnCosts() { Weighting weighting = CustomModelParser.createWeighting(accessEnc, avSpeedEnc, null, encodingManager, new DefaultTurnCostProvider(turnRestrictionEnc, deadEndEnc, graph.getTurnCostStorage(), 40), new CustomModel()); EdgeIteratorState edge = GHUtility.setSpeed(60, true, true, accessEnc, avSpeedEnc, graph.edge(0, 1).setDistance(100)); + assertEquals(Double.POSITIVE_INFINITY, GHUtility.calcWeightWithTurnWeight(weighting, edge, false, 0), 1.e-6); + assertEquals(Long.MAX_VALUE, GHUtility.calcMillisWithTurnMillis(weighting, edge, false, 0), 1.e-6); + graph.getTurnCostStorage().set(deadEndEnc, 0, 0, 0, true); assertEquals(6 + 40, GHUtility.calcWeightWithTurnWeight(weighting, edge, false, 0), 1.e-6); assertEquals((6 + 40) * 1000, GHUtility.calcMillisWithTurnMillis(weighting, edge, false, 0), 1.e-6); } From 103b09811168905d15bef47d8311c1f9493e46d7 Mon Sep 17 00:00:00 2001 From: easbar Date: Mon, 25 Sep 2023 17:34:05 +0200 Subject: [PATCH 16/22] green --- .../main/java/com/graphhopper/example/RoutingExampleTC.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/example/src/main/java/com/graphhopper/example/RoutingExampleTC.java b/example/src/main/java/com/graphhopper/example/RoutingExampleTC.java index 9e75369dbe..6e639ab608 100644 --- a/example/src/main/java/com/graphhopper/example/RoutingExampleTC.java +++ b/example/src/main/java/com/graphhopper/example/RoutingExampleTC.java @@ -40,14 +40,14 @@ public static void routeWithTurnCostsAndCurbsides(GraphHopper hopper) { } public static void routeWithTurnCostsAndOtherUTurnCosts(GraphHopper hopper) { - GHRequest req = new GHRequest(42.50822, 1.533966, 42.506899, 1.525372) + GHRequest req = new GHRequest(42.497644, 1.500471, 42.498669, 1.501275) .setCurbsides(Arrays.asList(CURBSIDE_ANY, CURBSIDE_RIGHT)) // to change u-turn costs per request we have to disable CH. otherwise the u-turn costs we set per request // will be ignored and those set for our profile will be used. .putHint(Parameters.CH.DISABLE, true) .setProfile("car"); - route(hopper, req.putHint(Parameters.Routing.U_TURN_COSTS, 10), 1370, 98_700); - route(hopper, req.putHint(Parameters.Routing.U_TURN_COSTS, 15), 1370, 103_700); + route(hopper, req.putHint(Parameters.Routing.U_TURN_COSTS, 10), 443, 63_100); + route(hopper, req.putHint(Parameters.Routing.U_TURN_COSTS, 15), 443, 68_100); } private static void route(GraphHopper hopper, GHRequest req, int expectedDistance, int expectedTime) { From c6dd1d0ebc1019b467e07bc577f2a379d066c674 Mon Sep 17 00:00:00 2001 From: easbar Date: Mon, 25 Sep 2023 18:27:15 +0200 Subject: [PATCH 17/22] Fix dead ends at subnetwork boundaries --- .../java/com/graphhopper/GraphHopper.java | 49 +++++++------------ .../java/com/graphhopper/GraphHopperTest.java | 14 ++++++ 2 files changed, 31 insertions(+), 32 deletions(-) diff --git a/core/src/main/java/com/graphhopper/GraphHopper.java b/core/src/main/java/com/graphhopper/GraphHopper.java index 66f5907326..07bfb067a9 100644 --- a/core/src/main/java/com/graphhopper/GraphHopper.java +++ b/core/src/main/java/com/graphhopper/GraphHopper.java @@ -40,6 +40,7 @@ import com.graphhopper.routing.util.*; import com.graphhopper.routing.util.countryrules.CountryRuleFactory; import com.graphhopper.routing.util.parsers.*; +import com.graphhopper.routing.weighting.AbstractAdjustedWeighting; import com.graphhopper.routing.weighting.Weighting; import com.graphhopper.routing.weighting.custom.CustomWeighting; import com.graphhopper.storage.*; @@ -1410,18 +1411,21 @@ protected void cleanUp() { properties.put("profiles", getProfilesString()); logger.info("nodes: " + Helper.nf(baseGraph.getNodes()) + ", edges: " + Helper.nf(baseGraph.getEdges())); - // find dead-ends and make sure u-turns are allowed there + logger.info("Start marking dead-ends"); + StopWatch sw = StopWatch.started(); for (Profile profile : profilesByName.values()) { if (profile.isTurnCosts()) { BooleanEncodedValue deadEndEnc = encodingManager.getBooleanEncodedValue(DeadEnd.key(profile.getVehicle())); - // we must disable turn costs for the dead-end search + BooleanEncodedValue subnetworkEnc = encodingManager.getBooleanEncodedValue(Subnetwork.key(profile.getName())); + // We disable turn costs for the dead-end search. Weighting weighting = createWeighting(profile, new PMap(), true); - findDeadEndUTurns(weighting, deadEndEnc); + findDeadEndUTurns(weighting, deadEndEnc, subnetworkEnc); } } + logger.info("Finished marking dead-ends, took: " + sw.stop().getSeconds() + "s"); } - private void findDeadEndUTurns(Weighting weighting, BooleanEncodedValue deadEndEnc) { + private void findDeadEndUTurns(Weighting weighting, BooleanEncodedValue deadEndEnc, BooleanEncodedValue subnetworkEnc) { EdgeExplorer inExplorer = baseGraph.createEdgeExplorer(); EdgeExplorer outExplorer = baseGraph.createEdgeExplorer(); for (int node = 0; node < baseGraph.getNodes(); node++) { @@ -1429,9 +1433,12 @@ private void findDeadEndUTurns(Weighting weighting, BooleanEncodedValue deadEndE OUTER: while (fromEdge.next()) { if (Double.isFinite(weighting.calcEdgeWeight(fromEdge, true))) { + boolean subnetworkFrom = fromEdge.get(subnetworkEnc); EdgeIterator toEdge = outExplorer.setBaseNode(node); while (toEdge.next()) { - if (toEdge.getEdge() != fromEdge.getEdge() && Double.isFinite(GHUtility.calcWeightWithTurnWeight(weighting, toEdge, false, fromEdge.getEdge()))) + if (toEdge.getEdge() != fromEdge.getEdge() + && Double.isFinite(GHUtility.calcWeightWithTurnWeight(weighting, toEdge, false, fromEdge.getEdge())) + && subnetworkFrom == toEdge.get(subnetworkEnc)) continue OUTER; } // the only way to continue from fromEdge is a u-turn. this is a dead-end u-turn @@ -1448,40 +1455,18 @@ private void setDeadEndUTurn(BaseGraph baseGraph, BooleanEncodedValue deadEndEnc private List buildSubnetworkRemovalJobs() { List jobs = new ArrayList<>(); for (Profile profile : profilesByName.values()) { - // if turn costs are enabled use u-turn costs of zero as we only want to make sure the graph is fully connected assuming finite u-turn costs Weighting weighting = createWeighting(profile, new PMap()); - Weighting w = new Weighting() { - @Override - public double getMinWeight(double distance) { - return weighting.getMinWeight(distance); - } - - @Override - public double calcEdgeWeight(EdgeIteratorState edgeState, boolean reverse) { - return weighting.calcEdgeWeight(edgeState, reverse); - } - - @Override - public long calcEdgeMillis(EdgeIteratorState edgeState, boolean reverse) { - return weighting.calcEdgeMillis(edgeState, reverse); - } - + Weighting w = new AbstractAdjustedWeighting(weighting) { @Override public double calcTurnWeight(int inEdge, int viaNode, int outEdge) { + // We only want to make sure the graph is fully connected assuming finite u-turn + // costs. Here we have to set it to zero explicitly, because otherwise the + // u-turn costs would be infinite everywhere except at dead-ends. But we run the + // dead-end detection after the subnetwork search. if (inEdge == outEdge) return 0; return weighting.calcTurnWeight(inEdge, viaNode, outEdge); } - @Override - public long calcTurnMillis(int inEdge, int viaNode, int outEdge) { - return weighting.calcTurnMillis(inEdge, viaNode, outEdge); - } - - @Override - public boolean hasTurnCosts() { - return weighting.hasTurnCosts(); - } - @Override public String getName() { return weighting.getName(); diff --git a/core/src/test/java/com/graphhopper/GraphHopperTest.java b/core/src/test/java/com/graphhopper/GraphHopperTest.java index 92811013d4..83d47f384c 100644 --- a/core/src/test/java/com/graphhopper/GraphHopperTest.java +++ b/core/src/test/java/com/graphhopper/GraphHopperTest.java @@ -2670,6 +2670,7 @@ void curbsideWithSubnetwork_issue2502() { hopper.importOrLoad(); GHPoint pointA = new GHPoint(28.77428, -81.61593); GHPoint pointB = new GHPoint(28.773038, -81.611595); + GHPoint pointC = new GHPoint(28.773235, -81.613038); { // A->B GHRequest request = new GHRequest(pointA, pointB); @@ -2693,6 +2694,19 @@ void curbsideWithSubnetwork_issue2502() { double distance = response.getBest().getDistance(); assertEquals(2318, distance, 1); } + { + // A->C + // Stoneybrook Hills is only a 'dead-end' because of the subnetwork exclusion, but it + // still is a dead-end and needs to be marked as such, otherwise there would be a + // connection-not-found error. + GHRequest request = new GHRequest(pointA, pointC); + request.setProfile(profile); + request.setCurbsides(Arrays.asList("right", "right")); + GHResponse response = hopper.route(request); + assertFalse(response.hasErrors(), response.getErrors().toString()); + double distance = response.getBest().getDistance(); + assertEquals(488, distance, 1); + } } @Test From 50a607b2231b85957f0181537e89b93b428ad148 Mon Sep 17 00:00:00 2001 From: easbar Date: Mon, 25 Sep 2023 18:52:10 +0200 Subject: [PATCH 18/22] move + test --- .../java/com/graphhopper/GraphHopper.java | 29 +---- .../reader/osm/PrepareDeadEnds.java | 61 +++++++++ .../reader/osm/PrepareDeadEndsTest.java | 117 ++++++++++++++++++ 3 files changed, 181 insertions(+), 26 deletions(-) create mode 100644 core/src/main/java/com/graphhopper/reader/osm/PrepareDeadEnds.java create mode 100644 core/src/test/java/com/graphhopper/reader/osm/PrepareDeadEndsTest.java diff --git a/core/src/main/java/com/graphhopper/GraphHopper.java b/core/src/main/java/com/graphhopper/GraphHopper.java index 07bfb067a9..b3f43c8079 100644 --- a/core/src/main/java/com/graphhopper/GraphHopper.java +++ b/core/src/main/java/com/graphhopper/GraphHopper.java @@ -25,6 +25,7 @@ import com.graphhopper.jackson.Jackson; import com.graphhopper.reader.dem.*; import com.graphhopper.reader.osm.OSMReader; +import com.graphhopper.reader.osm.PrepareDeadEnds; import com.graphhopper.reader.osm.RestrictionTagParser; import com.graphhopper.reader.osm.conditional.DateRangeParser; import com.graphhopper.routing.*; @@ -1413,44 +1414,20 @@ protected void cleanUp() { logger.info("Start marking dead-ends"); StopWatch sw = StopWatch.started(); + PrepareDeadEnds prepareDeadEnds = new PrepareDeadEnds(baseGraph); for (Profile profile : profilesByName.values()) { if (profile.isTurnCosts()) { BooleanEncodedValue deadEndEnc = encodingManager.getBooleanEncodedValue(DeadEnd.key(profile.getVehicle())); BooleanEncodedValue subnetworkEnc = encodingManager.getBooleanEncodedValue(Subnetwork.key(profile.getName())); // We disable turn costs for the dead-end search. Weighting weighting = createWeighting(profile, new PMap(), true); - findDeadEndUTurns(weighting, deadEndEnc, subnetworkEnc); + prepareDeadEnds.findDeadEndUTurns(weighting, deadEndEnc, subnetworkEnc); } } logger.info("Finished marking dead-ends, took: " + sw.stop().getSeconds() + "s"); } - private void findDeadEndUTurns(Weighting weighting, BooleanEncodedValue deadEndEnc, BooleanEncodedValue subnetworkEnc) { - EdgeExplorer inExplorer = baseGraph.createEdgeExplorer(); - EdgeExplorer outExplorer = baseGraph.createEdgeExplorer(); - for (int node = 0; node < baseGraph.getNodes(); node++) { - EdgeIterator fromEdge = inExplorer.setBaseNode(node); - OUTER: - while (fromEdge.next()) { - if (Double.isFinite(weighting.calcEdgeWeight(fromEdge, true))) { - boolean subnetworkFrom = fromEdge.get(subnetworkEnc); - EdgeIterator toEdge = outExplorer.setBaseNode(node); - while (toEdge.next()) { - if (toEdge.getEdge() != fromEdge.getEdge() - && Double.isFinite(GHUtility.calcWeightWithTurnWeight(weighting, toEdge, false, fromEdge.getEdge())) - && subnetworkFrom == toEdge.get(subnetworkEnc)) - continue OUTER; - } - // the only way to continue from fromEdge is a u-turn. this is a dead-end u-turn - setDeadEndUTurn(baseGraph, deadEndEnc, fromEdge.getEdge(), node); - } - } - } - } - private void setDeadEndUTurn(BaseGraph baseGraph, BooleanEncodedValue deadEndEnc, int fromEdge, int viaNode) { - baseGraph.getTurnCostStorage().set(deadEndEnc, fromEdge, viaNode, fromEdge, true); - } private List buildSubnetworkRemovalJobs() { List jobs = new ArrayList<>(); diff --git a/core/src/main/java/com/graphhopper/reader/osm/PrepareDeadEnds.java b/core/src/main/java/com/graphhopper/reader/osm/PrepareDeadEnds.java new file mode 100644 index 0000000000..dbedd93440 --- /dev/null +++ b/core/src/main/java/com/graphhopper/reader/osm/PrepareDeadEnds.java @@ -0,0 +1,61 @@ +/* + * Licensed to GraphHopper GmbH under one or more contributor + * license agreements. See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + * + * GraphHopper GmbH licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.graphhopper.reader.osm; + +import com.graphhopper.routing.ev.BooleanEncodedValue; +import com.graphhopper.routing.weighting.Weighting; +import com.graphhopper.storage.BaseGraph; +import com.graphhopper.util.EdgeExplorer; +import com.graphhopper.util.EdgeIterator; +import com.graphhopper.util.GHUtility; + +public class PrepareDeadEnds { + private final BaseGraph baseGraph; + + public PrepareDeadEnds(BaseGraph baseGraph) { + this.baseGraph = baseGraph; + } + + public void findDeadEndUTurns(Weighting weighting, BooleanEncodedValue deadEndEnc, BooleanEncodedValue subnetworkEnc) { + EdgeExplorer inExplorer = baseGraph.createEdgeExplorer(); + EdgeExplorer outExplorer = baseGraph.createEdgeExplorer(); + for (int node = 0; node < baseGraph.getNodes(); node++) { + EdgeIterator fromEdge = inExplorer.setBaseNode(node); + OUTER: + while (fromEdge.next()) { + if (Double.isFinite(weighting.calcEdgeWeight(fromEdge, true))) { + boolean subnetworkFrom = fromEdge.get(subnetworkEnc); + EdgeIterator toEdge = outExplorer.setBaseNode(node); + while (toEdge.next()) { + if (toEdge.getEdge() != fromEdge.getEdge() + && Double.isFinite(GHUtility.calcWeightWithTurnWeight(weighting, toEdge, false, fromEdge.getEdge())) + && subnetworkFrom == toEdge.get(subnetworkEnc)) + continue OUTER; + } + // the only way to continue from fromEdge is a u-turn. this is a dead-end u-turn + setDeadEndUTurn(baseGraph, deadEndEnc, fromEdge.getEdge(), node); + } + } + } + } + + private void setDeadEndUTurn(BaseGraph baseGraph, BooleanEncodedValue deadEndEnc, int fromEdge, int viaNode) { + baseGraph.getTurnCostStorage().set(deadEndEnc, fromEdge, viaNode, fromEdge, true); + } +} diff --git a/core/src/test/java/com/graphhopper/reader/osm/PrepareDeadEndsTest.java b/core/src/test/java/com/graphhopper/reader/osm/PrepareDeadEndsTest.java new file mode 100644 index 0000000000..f94ac00a86 --- /dev/null +++ b/core/src/test/java/com/graphhopper/reader/osm/PrepareDeadEndsTest.java @@ -0,0 +1,117 @@ +/* + * Licensed to GraphHopper GmbH under one or more contributor + * license agreements. See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + * + * GraphHopper GmbH licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.graphhopper.reader.osm; + +import com.graphhopper.routing.ev.*; +import com.graphhopper.routing.util.EncodingManager; +import com.graphhopper.routing.weighting.SpeedWeighting; +import com.graphhopper.routing.weighting.Weighting; +import com.graphhopper.storage.BaseGraph; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +class PrepareDeadEndsTest { + + private BooleanEncodedValue subnetworkEnc; + private BooleanEncodedValue deadEndEnc; + private DecimalEncodedValue speedEnc; + private BaseGraph graph; + private Weighting weighting; + private PrepareDeadEnds prepareDeadEnds; + + @BeforeEach + void setup() { + subnetworkEnc = Subnetwork.create("car"); + deadEndEnc = DeadEnd.create("car"); + speedEnc = new DecimalEncodedValueImpl("speed", 5, 5, true); + EncodingManager encodingManager = EncodingManager.start() + .add(speedEnc) + .add(subnetworkEnc) + .addTurnCostEncodedValue(deadEndEnc) + .build(); + graph = new BaseGraph.Builder(encodingManager).withTurnCosts(true).create(); + prepareDeadEnds = new PrepareDeadEnds(graph); + weighting = new SpeedWeighting(speedEnc); + } + + + @Test + void basic() { + // 0 - 1 - 2 - 3 + graph.edge(0, 1).set(speedEnc, 10, 10); + graph.edge(1, 2).set(speedEnc, 10, 10); + graph.edge(2, 3).set(speedEnc, 10, 10); + prepareDeadEnds.findDeadEndUTurns(weighting, deadEndEnc, subnetworkEnc); + assertFalse(graph.getTurnCostStorage().get(deadEndEnc, 1, 2, 1)); + assertTrue(graph.getTurnCostStorage().get(deadEndEnc, 2, 3, 2)); + assertFalse(graph.getTurnCostStorage().get(deadEndEnc, 2, 2, 2)); + } + + @Test + void oneway() { + // 0 - 1 - 2 - 3 + // | | + // --------4 + graph.edge(0, 1).set(speedEnc, 10, 10); + graph.edge(1, 2).set(speedEnc, 10, 10); + graph.edge(2, 3).set(speedEnc, 10, 10); + graph.edge(1, 4).set(speedEnc, 10, 10); + graph.edge(4, 3).set(speedEnc, 10, 0); + prepareDeadEnds.findDeadEndUTurns(weighting, deadEndEnc, subnetworkEnc); + // arriving at 3 coming from 2 is a dead-end bc 4-3 is a one-way + // but arriving at 3 coming from 4 is not + assertTrue(graph.getTurnCostStorage().get(deadEndEnc, 2, 3, 2)); + assertFalse(graph.getTurnCostStorage().get(deadEndEnc, 4, 3, 4)); + } + + @Test + void inaccessibleEdge() { + // 0 - 1 - 2 + // \-- | + // \---3 + graph.edge(0, 1).set(speedEnc, 10, 10); + graph.edge(1, 2).set(speedEnc, 10, 10); + // 2-3 is not accessible, so there is a dead-end at node 2. This is often the case when a + // residential road ends in a path for example + graph.edge(2, 3).set(speedEnc, 0, 0); + graph.edge(0, 3).set(speedEnc, 10, 10); + prepareDeadEnds.findDeadEndUTurns(weighting, deadEndEnc, subnetworkEnc); + assertTrue(graph.getTurnCostStorage().get(deadEndEnc, 1, 2, 1)); + assertFalse(graph.getTurnCostStorage().get(deadEndEnc, 1, 1, 1)); + } + + @Test + void subnetwork() { + // 0 - 1 - 2 - 3 + graph.edge(0, 1).set(speedEnc, 10, 10); + graph.edge(1, 2).set(speedEnc, 10, 10); + // Here edge 2->3 is a oneway dead-end and thus forms another subnetwork. + // In this case there is also a dead-end at node 2, because going to 2-3 is not + // an option. + graph.edge(2, 3).set(speedEnc, 10, 0).set(subnetworkEnc, true); + prepareDeadEnds.findDeadEndUTurns(weighting, deadEndEnc, subnetworkEnc); + assertTrue(graph.getTurnCostStorage().get(deadEndEnc, 1, 2, 1)); + assertFalse(graph.getTurnCostStorage().get(deadEndEnc, 1, 1, 1)); + assertFalse(graph.getTurnCostStorage().get(deadEndEnc, 2, 2, 2)); + } + +} From eadc81e762f2ca2d511c8d96d9d7172620b76037 Mon Sep 17 00:00:00 2001 From: easbar Date: Mon, 25 Sep 2023 19:13:57 +0200 Subject: [PATCH 19/22] Try lower u-turn costs --- tools/src/main/java/com/graphhopper/tools/Measurement.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/src/main/java/com/graphhopper/tools/Measurement.java b/tools/src/main/java/com/graphhopper/tools/Measurement.java index 6cfe627789..0f3a5a7b7e 100644 --- a/tools/src/main/java/com/graphhopper/tools/Measurement.java +++ b/tools/src/main/java/com/graphhopper/tools/Measurement.java @@ -296,7 +296,7 @@ private GraphHopperConfig createConfigFromArgs(PMap args) { GraphHopperConfig ghConfig = new GraphHopperConfig(args); vehicle = args.getString("measurement.vehicle", "car"); boolean turnCosts = args.getBool("measurement.turn_costs", false); - int uTurnCosts = args.getInt("measurement.u_turn_costs", 40); + int uTurnCosts = args.getInt("measurement.u_turn_costs", 20); String weighting = args.getString("measurement.weighting", "custom"); boolean useCHEdge = args.getBool("measurement.ch.edge", true); boolean useCHNode = args.getBool("measurement.ch.node", true); From 6a2e14343053149ff8c7e14be44d58570f8942ad Mon Sep 17 00:00:00 2001 From: easbar Date: Mon, 25 Sep 2023 19:14:24 +0200 Subject: [PATCH 20/22] Try higher u-turn costs --- tools/src/main/java/com/graphhopper/tools/Measurement.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/src/main/java/com/graphhopper/tools/Measurement.java b/tools/src/main/java/com/graphhopper/tools/Measurement.java index 0f3a5a7b7e..da028d951e 100644 --- a/tools/src/main/java/com/graphhopper/tools/Measurement.java +++ b/tools/src/main/java/com/graphhopper/tools/Measurement.java @@ -296,7 +296,7 @@ private GraphHopperConfig createConfigFromArgs(PMap args) { GraphHopperConfig ghConfig = new GraphHopperConfig(args); vehicle = args.getString("measurement.vehicle", "car"); boolean turnCosts = args.getBool("measurement.turn_costs", false); - int uTurnCosts = args.getInt("measurement.u_turn_costs", 20); + int uTurnCosts = args.getInt("measurement.u_turn_costs", 80); String weighting = args.getString("measurement.weighting", "custom"); boolean useCHEdge = args.getBool("measurement.ch.edge", true); boolean useCHNode = args.getBool("measurement.ch.node", true); From ae0cf7757c4ddef5963d186efc1e60a15ac07801 Mon Sep 17 00:00:00 2001 From: easbar Date: Mon, 25 Sep 2023 19:16:53 +0200 Subject: [PATCH 21/22] try zero u-turn costs --- tools/src/main/java/com/graphhopper/tools/Measurement.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/src/main/java/com/graphhopper/tools/Measurement.java b/tools/src/main/java/com/graphhopper/tools/Measurement.java index da028d951e..66a44fd624 100644 --- a/tools/src/main/java/com/graphhopper/tools/Measurement.java +++ b/tools/src/main/java/com/graphhopper/tools/Measurement.java @@ -296,7 +296,7 @@ private GraphHopperConfig createConfigFromArgs(PMap args) { GraphHopperConfig ghConfig = new GraphHopperConfig(args); vehicle = args.getString("measurement.vehicle", "car"); boolean turnCosts = args.getBool("measurement.turn_costs", false); - int uTurnCosts = args.getInt("measurement.u_turn_costs", 80); + int uTurnCosts = args.getInt("measurement.u_turn_costs", 0); String weighting = args.getString("measurement.weighting", "custom"); boolean useCHEdge = args.getBool("measurement.ch.edge", true); boolean useCHNode = args.getBool("measurement.ch.node", true); From 8db91a79abfed1a67f13ece25c66058d98ddb76a Mon Sep 17 00:00:00 2001 From: Peter Date: Tue, 26 Sep 2023 11:32:47 +0200 Subject: [PATCH 22/22] minor fixes --- core/src/main/java/com/graphhopper/GraphHopper.java | 2 +- core/src/test/java/com/graphhopper/GraphHopperTest.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/core/src/main/java/com/graphhopper/GraphHopper.java b/core/src/main/java/com/graphhopper/GraphHopper.java index 27d765278b..f34b285020 100644 --- a/core/src/main/java/com/graphhopper/GraphHopper.java +++ b/core/src/main/java/com/graphhopper/GraphHopper.java @@ -1417,7 +1417,7 @@ protected void cleanUp() { PrepareDeadEnds prepareDeadEnds = new PrepareDeadEnds(baseGraph); for (Profile profile : profilesByName.values()) { if (profile.isTurnCosts()) { - BooleanEncodedValue deadEndEnc = encodingManager.getBooleanEncodedValue(DeadEnd.key(profile.getVehicle())); + BooleanEncodedValue deadEndEnc = encodingManager.getTurnBooleanEncodedValue(DeadEnd.key(profile.getVehicle())); BooleanEncodedValue subnetworkEnc = encodingManager.getBooleanEncodedValue(Subnetwork.key(profile.getName())); // We disable turn costs for the dead-end search. Weighting weighting = createWeighting(profile, new PMap(), true); diff --git a/core/src/test/java/com/graphhopper/GraphHopperTest.java b/core/src/test/java/com/graphhopper/GraphHopperTest.java index 83d47f384c..841783f8dd 100644 --- a/core/src/test/java/com/graphhopper/GraphHopperTest.java +++ b/core/src/test/java/com/graphhopper/GraphHopperTest.java @@ -1731,7 +1731,7 @@ public void testCreateWeightingHintsMerging() { setProfiles(new Profile(profile).setVehicle(vehicle).setTurnCosts(true).putHint(U_TURN_COSTS, 123)); hopper.importOrLoad(); - BooleanEncodedValue deadEndEnc = hopper.getEncodingManager().getBooleanEncodedValue(DeadEnd.key(vehicle)); + BooleanEncodedValue deadEndEnc = hopper.getEncodingManager().getTurnBooleanEncodedValue(DeadEnd.key(vehicle)); final int edge = 389; final int node = 57; // make sure we chose an edge that is a dead-end for this test