/*
* 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.AlternativeRoute.AlternativeBidirSearch;
import com.graphhopper.routing.util.CarFlagEncoder;
import com.graphhopper.routing.util.EncodingManager;
import com.graphhopper.routing.util.FlagEncoder;
import com.graphhopper.routing.util.TraversalMode;
import com.graphhopper.routing.weighting.FastestWeighting;
import com.graphhopper.routing.weighting.Weighting;
import com.graphhopper.storage.Graph;
import com.graphhopper.storage.GraphExtension;
import com.graphhopper.storage.GraphHopperStorage;
import com.graphhopper.storage.RAMDirectory;
import com.graphhopper.util.Helper;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import static com.graphhopper.routing.AbstractRoutingAlgorithmTester.updateDistancesFor;
import static org.junit.Assert.*;
@RunWith(Parameterized.class)
public class AlternativeRouteTest {
private final FlagEncoder carFE = new CarFlagEncoder();
private final EncodingManager em = new EncodingManager(carFE);
private final TraversalMode traversalMode;
public AlternativeRouteTest(TraversalMode tMode) {
this.traversalMode = tMode;
}
/**
* Runs the same test with each of the supported traversal modes
*/
@Parameterized.Parameters(name = "{0}")
public static Collection<Object[]> configs() {
return Arrays.asList(new Object[][]{
{TraversalMode.NODE_BASED},
{TraversalMode.EDGE_BASED_2DIR}
});
}
public GraphHopperStorage createTestGraph(boolean fullGraph, EncodingManager tmpEM) {
GraphHopperStorage graph = new GraphHopperStorage(new RAMDirectory(), tmpEM, false, new GraphExtension.NoOpExtension());
graph.create(1000);
/* 9
_/\
1 2-3-4-10
\ / \
5--6-7---8
*/
graph.edge(1, 9, 1, true);
graph.edge(9, 2, 1, true);
if (fullGraph)
graph.edge(2, 3, 1, true);
graph.edge(3, 4, 1, true);
graph.edge(4, 10, 1, true);
graph.edge(5, 6, 1, true);
graph.edge(6, 7, 1, true);
graph.edge(7, 8, 1, true);
if (fullGraph)
graph.edge(1, 5, 2, true);
graph.edge(6, 3, 1, true);
graph.edge(4, 8, 1, true);
updateDistancesFor(graph, 5, 0.00, 0.05);
updateDistancesFor(graph, 6, 0.00, 0.10);
updateDistancesFor(graph, 7, 0.00, 0.15);
updateDistancesFor(graph, 8, 0.00, 0.25);
updateDistancesFor(graph, 1, 0.05, 0.00);
updateDistancesFor(graph, 9, 0.10, 0.05);
updateDistancesFor(graph, 2, 0.05, 0.10);
updateDistancesFor(graph, 3, 0.05, 0.15);
updateDistancesFor(graph, 4, 0.05, 0.25);
updateDistancesFor(graph, 10, 0.05, 0.30);
return graph;
}
@Test
public void testCalcAlternatives() throws Exception {
Weighting weighting = new FastestWeighting(carFE);
GraphHopperStorage g = createTestGraph(true, em);
AlternativeRoute altDijkstra = new AlternativeRoute(g, weighting, traversalMode);
altDijkstra.setMaxShareFactor(0.5);
altDijkstra.setMaxWeightFactor(2);
List<AlternativeRoute.AlternativeInfo> pathInfos = altDijkstra.calcAlternatives(5, 4);
checkAlternatives(pathInfos);
assertEquals(2, pathInfos.size());
DijkstraBidirectionRef dijkstra = new DijkstraBidirectionRef(g, weighting, traversalMode);
Path bestPath = dijkstra.calcPath(5, 4);
Path bestAlt = pathInfos.get(0).getPath();
Path secondAlt = pathInfos.get(1).getPath();
assertEquals(bestPath.calcNodes(), bestAlt.calcNodes());
assertEquals(bestPath.getWeight(), bestAlt.getWeight(), 1e-3);
assertEquals(Helper.createTList(5, 6, 3, 4), bestAlt.calcNodes());
// Note: here plateau is longer, even longer than optimum, but path is longer
// so which alternative is better? longer plateau.weight with bigger path.weight or smaller path.weight with smaller plateau.weight
// assertEquals(Helper.createTList(5, 1, 9, 2, 3, 4), secondAlt.calcNodes());
assertEquals(Helper.createTList(5, 6, 7, 8, 4), secondAlt.calcNodes());
assertEquals(1667.9, secondAlt.getWeight(), .1);
}
@Test
public void testCalcAlternatives2() throws Exception {
Weighting weighting = new FastestWeighting(carFE);
Graph g = createTestGraph(true, em);
AlternativeRoute altDijkstra = new AlternativeRoute(g, weighting, traversalMode);
altDijkstra.setMaxPaths(3);
altDijkstra.setMaxShareFactor(0.7);
altDijkstra.setMinPlateauFactor(0.15);
altDijkstra.setMaxWeightFactor(2);
// edge based traversal requires a bit more exploration than the default of 1
altDijkstra.setMaxExplorationFactor(1.2);
List<AlternativeRoute.AlternativeInfo> pathInfos = altDijkstra.calcAlternatives(5, 4);
checkAlternatives(pathInfos);
assertEquals(3, pathInfos.size());
// result is sorted based on the plateau to full weight ratio
assertEquals(Helper.createTList(5, 6, 3, 4), pathInfos.get(0).getPath().calcNodes());
assertEquals(Helper.createTList(5, 6, 7, 8, 4), pathInfos.get(1).getPath().calcNodes());
assertEquals(Helper.createTList(5, 1, 9, 2, 3, 4), pathInfos.get(2).getPath().calcNodes());
assertEquals(2416.0, pathInfos.get(2).getPath().getWeight(), .1);
}
void checkAlternatives(List<AlternativeRoute.AlternativeInfo> alternativeInfos) {
assertFalse("alternativeInfos should contain alternatives", alternativeInfos.isEmpty());
AlternativeRoute.AlternativeInfo bestInfo = alternativeInfos.get(0);
for (int i = 1; i < alternativeInfos.size(); i++) {
AlternativeRoute.AlternativeInfo a = alternativeInfos.get(i);
if (a.getPath().getWeight() < bestInfo.getPath().getWeight())
assertTrue("alternative is not longer -> " + a + " vs " + bestInfo, false);
if (a.getShareWeight() > bestInfo.getPath().getWeight()
|| a.getShareWeight() > a.getPath().getWeight())
assertTrue("share or sortby incorrect -> " + a + " vs " + bestInfo, false);
}
}
@Test
public void testDisconnectedAreas() {
Graph g = createTestGraph(true, em);
// one single disconnected node
updateDistancesFor(g, 20, 0.00, -0.01);
Weighting weighting = new FastestWeighting(carFE);
AlternativeBidirSearch altDijkstra = new AlternativeBidirSearch(g, weighting, traversalMode, 1);
Path path = altDijkstra.calcPath(1, 20);
assertFalse(path.isFound());
// make sure not the full graph is traversed!
assertEquals(3, altDijkstra.getVisitedNodes());
}
}