/* * 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; import com.graphhopper.routing.util.*; import com.graphhopper.routing.weighting.FastestWeighting; import com.graphhopper.routing.weighting.TurnWeighting; import com.graphhopper.routing.weighting.Weighting; import com.graphhopper.storage.Graph; import com.graphhopper.storage.GraphBuilder; import com.graphhopper.storage.GraphHopperStorage; import com.graphhopper.storage.TurnCostExtension; import com.graphhopper.util.EdgeIteratorState; import com.graphhopper.util.Helper; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; import org.junit.runners.Parameterized.Parameters; import java.util.Arrays; import java.util.Collection; import static com.graphhopper.util.GHUtility.getEdge; import static com.graphhopper.util.Parameters.Algorithms.*; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; /** * @author Peter Karich */ @RunWith(Parameterized.class) public class EdgeBasedRoutingAlgorithmTest { private final String algoStr; private FlagEncoder carEncoder; public EdgeBasedRoutingAlgorithmTest(String algo) { this.algoStr = algo; } @Parameters(name = "{0}") public static Collection<Object[]> configs() { return Arrays.asList(new Object[][]{ {DIJKSTRA}, {DIJKSTRA_BI}, {ASTAR}, {ASTAR_BI} // TODO { AlgorithmOptions.DIJKSTRA_ONE_TO_MANY } }); } // 0---1 // | / // 2--3--4 // | | | // 5--6--7 public static void initGraph(Graph g) { g.edge(0, 1, 3, true); g.edge(0, 2, 1, true); g.edge(1, 3, 1, true); g.edge(2, 3, 1, true); g.edge(3, 4, 1, true); g.edge(2, 5, .5, true); g.edge(3, 6, 1, true); g.edge(4, 7, 1, true); g.edge(5, 6, 1, true); g.edge(6, 7, 1, true); } EncodingManager createEncodingManager(boolean restrictedOnly) { if (restrictedOnly) carEncoder = new CarFlagEncoder(5, 5, 1); else // allow for basic costs too carEncoder = new CarFlagEncoder(5, 5, 3); return new EncodingManager(carEncoder); } public RoutingAlgorithm createAlgo(Graph g, AlgorithmOptions opts) { opts = AlgorithmOptions.start(opts).algorithm(algoStr).build(); return new RoutingAlgorithmFactorySimple().createAlgo(g, opts); } protected GraphHopperStorage createStorage(EncodingManager em) { return new GraphBuilder(em).create(); } private void initTurnRestrictions(Graph g, TurnCostExtension tcs, TurnCostEncoder tEncoder) { long tflags = tEncoder.getTurnFlags(true, 0); // only forward from 2-3 to 3-4 => limit 2,3->3,6 and 2,3->3,1 tcs.addTurnInfo(getEdge(g, 2, 3).getEdge(), 3, getEdge(g, 3, 6).getEdge(), tflags); tcs.addTurnInfo(getEdge(g, 2, 3).getEdge(), 3, getEdge(g, 3, 1).getEdge(), tflags); // only right from 5-2 to 2-3 => limit 5,2->2,0 tcs.addTurnInfo(getEdge(g, 5, 2).getEdge(), 2, getEdge(g, 2, 0).getEdge(), tflags); // only right from 7-6 to 6-3 => limit 7,6->6,5 tcs.addTurnInfo(getEdge(g, 7, 6).getEdge(), 6, getEdge(g, 6, 5).getEdge(), tflags); // no 5-6 to 6-3 tcs.addTurnInfo(getEdge(g, 5, 6).getEdge(), 6, getEdge(g, 6, 3).getEdge(), tflags); // no 4-3 to 3-1 tcs.addTurnInfo(getEdge(g, 4, 3).getEdge(), 3, getEdge(g, 3, 1).getEdge(), tflags); // no 4-3 to 3-2 tcs.addTurnInfo(getEdge(g, 4, 3).getEdge(), 3, getEdge(g, 3, 2).getEdge(), tflags); // no u-turn at 6-7 tcs.addTurnInfo(getEdge(g, 6, 7).getEdge(), 7, getEdge(g, 7, 6).getEdge(), tflags); // no u-turn at 3-6 tcs.addTurnInfo(getEdge(g, 3, 6).getEdge(), 6, getEdge(g, 6, 3).getEdge(), tflags); } Weighting createWeighting(FlagEncoder encoder, TurnCostExtension tcs, double uTurnCosts) { return new TurnWeighting(new FastestWeighting(encoder), tcs).setDefaultUTurnCost(uTurnCosts); } @Test public void testBasicTurnRestriction() { GraphHopperStorage g = createStorage(createEncodingManager(true)); initGraph(g); TurnCostExtension tcs = (TurnCostExtension) g.getExtension(); initTurnRestrictions(g, tcs, carEncoder); Path p = createAlgo(g, AlgorithmOptions.start(). weighting(createWeighting(carEncoder, tcs, 40)). traversalMode(TraversalMode.EDGE_BASED_2DIR).build()). calcPath(5, 1); assertEquals(Helper.createTList(5, 2, 3, 4, 7, 6, 3, 1), p.calcNodes()); // test 7-6-5 and reverse p = createAlgo(g, AlgorithmOptions.start(). weighting(createWeighting(carEncoder, tcs, 40)). traversalMode(TraversalMode.EDGE_BASED_1DIR).build()). calcPath(5, 7); assertEquals(Helper.createTList(5, 6, 7), p.calcNodes()); p = createAlgo(g, AlgorithmOptions.start(). weighting(createWeighting(carEncoder, tcs, 40)). traversalMode(TraversalMode.EDGE_BASED_1DIR).build()). calcPath(7, 5); assertEquals(Helper.createTList(7, 6, 3, 2, 5), p.calcNodes()); } @Test public void testUTurns() { GraphHopperStorage g = createStorage(createEncodingManager(true)); initGraph(g); TurnCostExtension tcs = (TurnCostExtension) g.getExtension(); long tflags = carEncoder.getTurnFlags(true, 0); // force u-turn via lowering the cost for it EdgeIteratorState e3_6 = getEdge(g, 3, 6); e3_6.setDistance(0.1); getEdge(g, 3, 2).setDistance(864); getEdge(g, 1, 0).setDistance(864); tcs.addTurnInfo(getEdge(g, 7, 6).getEdge(), 6, getEdge(g, 6, 5).getEdge(), tflags); tcs.addTurnInfo(getEdge(g, 4, 3).getEdge(), 3, e3_6.getEdge(), tflags); AlgorithmOptions opts = AlgorithmOptions.start(). weighting(createWeighting(carEncoder, tcs, 50)). traversalMode(TraversalMode.EDGE_BASED_2DIR_UTURN).build(); Path p = createAlgo(g, opts).calcPath(7, 5); assertEquals(Helper.createTList(7, 6, 3, 6, 5), p.calcNodes()); // no u-turn for 6-3 opts = AlgorithmOptions.start(). weighting(createWeighting(carEncoder, tcs, 100)). traversalMode(TraversalMode.EDGE_BASED_2DIR_UTURN).build(); tcs.addTurnInfo(getEdge(g, 6, 3).getEdge(), 3, getEdge(g, 3, 6).getEdge(), tflags); p = createAlgo(g, opts).calcPath(7, 5); assertEquals(Helper.createTList(7, 6, 3, 2, 5), p.calcNodes()); } @Test public void testBasicTurnCosts() { GraphHopperStorage g = createStorage(createEncodingManager(false)); initGraph(g); TurnCostExtension tcs = (TurnCostExtension) g.getExtension(); Path p = createAlgo(g, AlgorithmOptions.start(). weighting(createWeighting(carEncoder, tcs, 40)). traversalMode(TraversalMode.EDGE_BASED_1DIR).build()). calcPath(5, 1); // no restriction and costs EdgeIteratorState e3_6 = getEdge(g, 5, 6); e3_6.setDistance(2); assertEquals(Helper.createTList(5, 2, 3, 1), p.calcNodes()); // now introduce some turn costs long tflags = carEncoder.getTurnFlags(false, 2); tcs.addTurnInfo(getEdge(g, 5, 2).getEdge(), 2, getEdge(g, 2, 3).getEdge(), tflags); p = createAlgo(g, AlgorithmOptions.start(). weighting(createWeighting(carEncoder, tcs, 40)). traversalMode(TraversalMode.EDGE_BASED_1DIR).build()). calcPath(5, 1); assertEquals(Helper.createTList(5, 6, 3, 1), p.calcNodes()); } @Test public void testTurnCostsBug_991() { final GraphHopperStorage g = createStorage(createEncodingManager(false)); initGraph(g); TurnCostExtension tcs = (TurnCostExtension) g.getExtension(); long tflags = carEncoder.getTurnFlags(false, 2); tcs.addTurnInfo(getEdge(g, 5, 2).getEdge(), 2, getEdge(g, 2, 3).getEdge(), tflags); tcs.addTurnInfo(getEdge(g, 2, 0).getEdge(), 0, getEdge(g, 0, 1).getEdge(), tflags); tcs.addTurnInfo(getEdge(g, 5, 6).getEdge(), 6, getEdge(g, 6, 3).getEdge(), tflags); tflags = carEncoder.getTurnFlags(false, 1); tcs.addTurnInfo(getEdge(g, 6, 7).getEdge(), 7, getEdge(g, 7, 4).getEdge(), tflags); Path p = createAlgo(g, AlgorithmOptions.start(). weighting(new TurnWeighting(new FastestWeighting(carEncoder), tcs) { @Override public double calcTurnWeight(int edgeFrom, int nodeVia, int edgeTo) { if (edgeFrom >= 0) assertNotNull("edge " + edgeFrom + " to " + nodeVia + " does not exist", g.getEdgeIteratorState(edgeFrom, nodeVia)); if (edgeTo >= 0) assertNotNull("edge " + edgeTo + " to " + nodeVia + " does not exist", g.getEdgeIteratorState(edgeTo, nodeVia)); return super.calcTurnWeight(edgeFrom, nodeVia, edgeTo); } }.setDefaultUTurnCost(40)). traversalMode(TraversalMode.EDGE_BASED_2DIR).build()). calcPath(5, 1); assertEquals(Helper.createTList(5, 6, 7, 4, 3, 1), p.calcNodes()); assertEquals(301, p.getTime(), .1); } }