/* * 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.reader.ReaderWay; import com.graphhopper.routing.util.*; import com.graphhopper.routing.weighting.FastestWeighting; import com.graphhopper.routing.weighting.GenericWeighting; import com.graphhopper.routing.weighting.ShortestWeighting; import com.graphhopper.storage.*; import com.graphhopper.util.*; import org.junit.Test; import java.util.*; import static com.graphhopper.storage.AbstractGraphStorageTester.assertPList; import static org.junit.Assert.*; /** * @author Peter Karich */ public class PathTest { private final FlagEncoder encoder = new CarFlagEncoder(); private final DataFlagEncoder dataFlagEncoder = new DataFlagEncoder(); private final EncodingManager carManager = new EncodingManager(encoder); private final EncodingManager dataFlagManager = new EncodingManager(dataFlagEncoder); private final EncodingManager mixedEncoders = new EncodingManager(new CarFlagEncoder()); private final TranslationMap trMap = TranslationMapTest.SINGLETON; private final Translation tr = trMap.getWithFallBack(Locale.US); private final RoundaboutGraph roundaboutGraph = new RoundaboutGraph(); @Test public void testFound() { GraphHopperStorage g = new GraphBuilder(carManager).create(); Path p = new Path(g, new FastestWeighting(encoder)); assertFalse(p.isFound()); assertEquals(0, p.getDistance(), 1e-7); assertEquals(0, p.calcNodes().size()); g.close(); } @Test public void testWayList() { GraphHopperStorage g = new GraphBuilder(carManager).create(); NodeAccess na = g.getNodeAccess(); na.setNode(0, 0.0, 0.1); na.setNode(1, 1.0, 0.1); na.setNode(2, 2.0, 0.1); EdgeIteratorState edge1 = g.edge(0, 1).setDistance(1000).setFlags(encoder.setProperties(10, true, true)); edge1.setWayGeometry(Helper.createPointList(8, 1, 9, 1)); EdgeIteratorState edge2 = g.edge(2, 1).setDistance(2000).setFlags(encoder.setProperties(50, true, true)); edge2.setWayGeometry(Helper.createPointList(11, 1, 10, 1)); Path path = new Path(g, new FastestWeighting(encoder)); SPTEntry e1 = new SPTEntry(edge2.getEdge(), 2, 1); e1.parent = new SPTEntry(edge1.getEdge(), 1, 1); e1.parent.parent = new SPTEntry(-1, 0, 1); path.setSPTEntry(e1); path.extract(); // 0-1-2 assertPList(Helper.createPointList(0, 0.1, 8, 1, 9, 1, 1, 0.1, 10, 1, 11, 1, 2, 0.1), path.calcPoints()); InstructionList instr = path.calcInstructions(tr); List<Map<String, Object>> res = instr.createJson(); Map<String, Object> tmp = res.get(0); assertEquals(3000.0, tmp.get("distance")); assertEquals(504000L, tmp.get("time")); assertEquals("Continue", tmp.get("text")); assertEquals("[0, 6]", tmp.get("interval").toString()); tmp = res.get(1); assertEquals(0.0, tmp.get("distance")); assertEquals(0L, tmp.get("time")); assertEquals("Finish!", tmp.get("text")); assertEquals("[6, 6]", tmp.get("interval").toString()); int lastIndex = (Integer) ((List) res.get(res.size() - 1).get("interval")).get(0); assertEquals(path.calcPoints().size() - 1, lastIndex); // force minor change for instructions edge2.setName("2"); na.setNode(3, 1.0, 1.0); EdgeIteratorState edge3 = g.edge(1, 3).setDistance(1000).setFlags(encoder.setProperties(10, true, true)); path = new Path(g, new FastestWeighting(encoder)); e1 = new SPTEntry(edge2.getEdge(), 2, 1); e1.parent = new SPTEntry(edge1.getEdge(), 1, 1); e1.parent.parent = new SPTEntry(-1, 0, 1); path.setSPTEntry(e1); path.extract(); instr = path.calcInstructions(tr); res = instr.createJson(); tmp = res.get(0); assertEquals(1000.0, tmp.get("distance")); assertEquals(360000L, tmp.get("time")); assertEquals("Continue", tmp.get("text")); assertEquals("[0, 3]", tmp.get("interval").toString()); tmp = res.get(1); assertEquals(2000.0, tmp.get("distance")); assertEquals(144000L, tmp.get("time")); assertEquals("Turn sharp right onto 2", tmp.get("text")); assertEquals("[3, 6]", tmp.get("interval").toString()); lastIndex = (Integer) ((List) res.get(res.size() - 1).get("interval")).get(0); assertEquals(path.calcPoints().size() - 1, lastIndex); // now reverse order path = new Path(g, new FastestWeighting(encoder)); e1 = new SPTEntry(edge1.getEdge(), 0, 1); e1.parent = new SPTEntry(edge2.getEdge(), 1, 1); e1.parent.parent = new SPTEntry(-1, 2, 1); path.setSPTEntry(e1); path.extract(); // 2-1-0 assertPList(Helper.createPointList(2, 0.1, 11, 1, 10, 1, 1, 0.1, 9, 1, 8, 1, 0, 0.1), path.calcPoints()); instr = path.calcInstructions(tr); res = instr.createJson(); tmp = res.get(0); assertEquals(2000.0, tmp.get("distance")); assertEquals(144000L, tmp.get("time")); assertEquals("Continue onto 2", tmp.get("text")); assertEquals("[0, 3]", tmp.get("interval").toString()); tmp = res.get(1); assertEquals(1000.0, tmp.get("distance")); assertEquals(360000L, tmp.get("time")); assertEquals("Turn sharp left", tmp.get("text")); assertEquals("[3, 6]", tmp.get("interval").toString()); lastIndex = (Integer) ((List) res.get(res.size() - 1).get("interval")).get(0); assertEquals(path.calcPoints().size() - 1, lastIndex); } @Test public void testFindInstruction() { Graph g = new GraphBuilder(carManager).create(); NodeAccess na = g.getNodeAccess(); na.setNode(0, 0.0, 0.0); na.setNode(1, 5.0, 0.0); na.setNode(2, 5.0, 0.5); na.setNode(3, 10.0, 0.5); na.setNode(4, 7.5, 0.25); na.setNode(5, 5.0, 1.0); EdgeIteratorState edge1 = g.edge(0, 1).setDistance(1000).setFlags(encoder.setProperties(50, true, true)); edge1.setWayGeometry(Helper.createPointList()); edge1.setName("Street 1"); EdgeIteratorState edge2 = g.edge(1, 2).setDistance(1000).setFlags(encoder.setProperties(50, true, true)); edge2.setWayGeometry(Helper.createPointList()); edge2.setName("Street 2"); EdgeIteratorState edge3 = g.edge(2, 3).setDistance(1000).setFlags(encoder.setProperties(50, true, true)); edge3.setWayGeometry(Helper.createPointList()); edge3.setName("Street 3"); EdgeIteratorState edge4 = g.edge(3, 4).setDistance(500).setFlags(encoder.setProperties(50, true, true)); edge4.setWayGeometry(Helper.createPointList()); edge4.setName("Street 4"); g.edge(1, 5).setDistance(10000).setFlags(encoder.setProperties(50, true, true)); g.edge(2, 5).setDistance(10000).setFlags(encoder.setProperties(50, true, true)); g.edge(3, 5).setDistance(100000).setFlags(encoder.setProperties(50, true, true)); Path path = new Path(g, new FastestWeighting(encoder)); SPTEntry e1 = new SPTEntry(edge4.getEdge(), 4, 1); e1.parent = new SPTEntry(edge3.getEdge(), 3, 1); e1.parent.parent = new SPTEntry(edge2.getEdge(), 2, 1); e1.parent.parent.parent = new SPTEntry(edge1.getEdge(), 1, 1); e1.parent.parent.parent.parent = new SPTEntry(-1, 0, 1); path.setSPTEntry(e1); path.extract(); InstructionList il = path.calcInstructions(tr); Instruction nextInstr0 = il.find(-0.001, 0.0, 1000); assertEquals(Instruction.CONTINUE_ON_STREET, nextInstr0.getSign()); Instruction nextInstr1 = il.find(0.001, 0.001, 1000); assertEquals(Instruction.TURN_RIGHT, nextInstr1.getSign()); Instruction nextInstr2 = il.find(5.0, 0.004, 1000); assertEquals(Instruction.TURN_LEFT, nextInstr2.getSign()); Instruction nextInstr3 = il.find(9.99, 0.503, 1000); assertEquals(Instruction.TURN_SHARP_LEFT, nextInstr3.getSign()); // a bit far away ... Instruction nextInstr4 = il.find(7.40, 0.25, 20000); assertEquals(Instruction.FINISH, nextInstr4.getSign()); // too far away assertNull(il.find(50.8, 50.25, 1000)); } /** * Test roundabout instructions for different profiles */ @Test public void testCalcInstructionsRoundabout() { for (FlagEncoder encoder : mixedEncoders.fetchEdgeEncoders()) { Path p = new Dijkstra(roundaboutGraph.g, new ShortestWeighting(encoder), TraversalMode.NODE_BASED) .calcPath(1, 8); assertTrue(p.isFound()); InstructionList wayList = p.calcInstructions(tr); // Test instructions List<String> tmpList = pick("text", wayList.createJson()); assertEquals(Arrays.asList("Continue onto MainStreet 1 2", "At roundabout, take exit 3 onto 5-8", "Finish!"), tmpList); // Test Radian double delta = roundaboutGraph.getAngle(1, 2, 5, 8); RoundaboutInstruction instr = (RoundaboutInstruction) wayList.get(1); assertEquals(delta, instr.getTurnAngle(), 0.01); // case of continuing a street through a roundabout p = new Dijkstra(roundaboutGraph.g, new ShortestWeighting(encoder), TraversalMode.NODE_BASED). calcPath(1, 7); wayList = p.calcInstructions(tr); tmpList = pick("text", wayList.createJson()); assertEquals(Arrays.asList("Continue onto MainStreet 1 2", "At roundabout, take exit 2 onto MainStreet 4 7", "Finish!"), tmpList); // Test Radian delta = roundaboutGraph.getAngle(1, 2, 4, 7); instr = (RoundaboutInstruction) wayList.get(1); assertEquals(delta, instr.getTurnAngle(), 0.01); } } /** * case starting in Roundabout */ @Test public void testCalcInstructionsRoundaboutBegin() { Path p = new Dijkstra(roundaboutGraph.g, new ShortestWeighting(encoder), TraversalMode.NODE_BASED) .calcPath(2, 8); assertTrue(p.isFound()); InstructionList wayList = p.calcInstructions(tr); List<String> tmpList = pick("text", wayList.createJson()); assertEquals(Arrays.asList("At roundabout, take exit 3 onto 5-8", "Finish!"), tmpList); } /** * case with one node being containig already exit */ @Test public void testCalcInstructionsRoundaboutDirectExit() { roundaboutGraph.inverse3to9(); Path p = new Dijkstra(roundaboutGraph.g, new ShortestWeighting(encoder), TraversalMode.NODE_BASED) .calcPath(6, 8); assertTrue(p.isFound()); InstructionList wayList = p.calcInstructions(tr); List<String> tmpList = pick("text", wayList.createJson()); assertEquals(Arrays.asList("Continue onto 3-6", "At roundabout, take exit 3 onto 5-8", "Finish!"), tmpList); roundaboutGraph.inverse3to9(); } /** * case with one edge being not an exit */ @Test public void testCalcInstructionsRoundabout2() { roundaboutGraph.inverse3to6(); Path p = new Dijkstra(roundaboutGraph.g, new ShortestWeighting(encoder), TraversalMode.NODE_BASED) .calcPath(1, 8); assertTrue(p.isFound()); InstructionList wayList = p.calcInstructions(tr); List<String> tmpList = pick("text", wayList.createJson()); assertEquals(Arrays.asList("Continue onto MainStreet 1 2", "At roundabout, take exit 2 onto 5-8", "Finish!"), tmpList); // Test Radian double delta = roundaboutGraph.getAngle(1, 2, 5, 8); RoundaboutInstruction instr = (RoundaboutInstruction) wayList.get(1); assertEquals(delta, instr.getTurnAngle(), 0.01); roundaboutGraph.inverse3to6(); } @Test public void testCalcInstructionsRoundaboutIssue353() { final Graph g = new GraphBuilder(carManager).create(); final NodeAccess na = g.getNodeAccess(); // // 8 // \ // 5 // / \ // 11- 1 - 2 4 - 7 // | \ / // 10 -9 -3 // \ | // --- 6 na.setNode(1, 52.514, 13.348); na.setNode(2, 52.514, 13.349); na.setNode(3, 52.5135, 13.35); na.setNode(4, 52.514, 13.351); na.setNode(5, 52.5145, 13.351); na.setNode(6, 52.513, 13.35); na.setNode(7, 52.514, 13.352); na.setNode(8, 52.515, 13.351); na.setNode(9, 52.5135, 13.349); na.setNode(10, 52.5135, 13.348); na.setNode(11, 52.514, 13.347); g.edge(2, 1, 5, false).setName("MainStreet 2 1"); g.edge(1, 11, 5, false).setName("MainStreet 1 11"); // roundabout EdgeIteratorState tmpEdge; tmpEdge = g.edge(3, 9, 2, false).setName("3-9"); tmpEdge.setFlags(encoder.setBool(tmpEdge.getFlags(), FlagEncoder.K_ROUNDABOUT, true)); tmpEdge = g.edge(9, 10, 2, false).setName("9-10"); tmpEdge.setFlags(encoder.setBool(tmpEdge.getFlags(), FlagEncoder.K_ROUNDABOUT, true)); tmpEdge = g.edge(6, 10, 2, false).setName("6-10"); tmpEdge.setFlags(encoder.setBool(tmpEdge.getFlags(), FlagEncoder.K_ROUNDABOUT, true)); tmpEdge = g.edge(10, 1, 2, false).setName("10-1"); tmpEdge.setFlags(encoder.setBool(tmpEdge.getFlags(), FlagEncoder.K_ROUNDABOUT, true)); tmpEdge = g.edge(3, 2, 5, false).setName("2-3"); tmpEdge.setFlags(encoder.setBool(tmpEdge.getFlags(), FlagEncoder.K_ROUNDABOUT, true)); tmpEdge = g.edge(4, 3, 5, false).setName("3-4"); tmpEdge.setFlags(encoder.setBool(tmpEdge.getFlags(), FlagEncoder.K_ROUNDABOUT, true)); tmpEdge = g.edge(5, 4, 5, false).setName("4-5"); tmpEdge.setFlags(encoder.setBool(tmpEdge.getFlags(), FlagEncoder.K_ROUNDABOUT, true)); tmpEdge = g.edge(2, 5, 5, false).setName("5-2"); tmpEdge.setFlags(encoder.setBool(tmpEdge.getFlags(), FlagEncoder.K_ROUNDABOUT, true)); g.edge(4, 7, 5, true).setName("MainStreet 4 7"); g.edge(5, 8, 5, true).setName("5-8"); g.edge(3, 6, 5, true).setName("3-6"); Path p = new Dijkstra(g, new ShortestWeighting(encoder), TraversalMode.NODE_BASED) .calcPath(6, 11); assertTrue(p.isFound()); InstructionList wayList = p.calcInstructions(tr); List<String> tmpList = pick("text", wayList.createJson()); assertEquals(Arrays.asList("At roundabout, take exit 1 onto MainStreet 1 11", "Finish!"), tmpList); } /** * clockwise roundabout */ @Test public void testCalcInstructionsRoundaboutClockwise() { roundaboutGraph.setRoundabout(true); Path p = new Dijkstra(roundaboutGraph.g, new ShortestWeighting(encoder), TraversalMode.NODE_BASED) .calcPath(1, 8); assertTrue(p.isFound()); InstructionList wayList = p.calcInstructions(tr); List<String> tmpList = pick("text", wayList.createJson()); assertEquals(Arrays.asList("Continue onto MainStreet 1 2", "At roundabout, take exit 1 onto 5-8", "Finish!"), tmpList); // Test Radian double delta = roundaboutGraph.getAngle(1, 2, 5, 8); RoundaboutInstruction instr = (RoundaboutInstruction) wayList.get(1); assertEquals(delta, instr.getTurnAngle(), 0.01); } List<String> pick(String key, List<Map<String, Object>> instructionJson) { List<String> list = new ArrayList<String>(); for (Map<String, Object> json : instructionJson) { list.add(json.get(key).toString()); } return list; } @Test public void testCalcInstructionsIgnoreContinue() { // Follow a couple of straight edges, including a name change Path p = new Dijkstra(roundaboutGraph.g, new ShortestWeighting(encoder), TraversalMode.NODE_BASED) .calcPath(4, 11); assertTrue(p.isFound()); InstructionList wayList = p.calcInstructions(tr); // Contain only start and finish instruction, no CONTINUE assertEquals(2, wayList.size()); } @Test public void testCalcInstructionsIgnoreTurnIfNoAlternative() { // The street turns left, but there is not turn Path p = new Dijkstra(roundaboutGraph.g, new ShortestWeighting(encoder), TraversalMode.NODE_BASED) .calcPath(10, 12); assertTrue(p.isFound()); InstructionList wayList = p.calcInstructions(tr); // Contain only start and finish instruction assertEquals(2, wayList.size()); } @Test public void testCalcInstructionForForkWithSameName() { final Graph g = new GraphBuilder(carManager).create(); final NodeAccess na = g.getNodeAccess(); // Actual example: point=48.982618%2C13.122021&point=48.982336%2C13.121002 // 1-2 & 2-4 have the same Street name, but other from that, it would be hard to see the difference // We have to enforce a turn instruction here // 3 // \ // 2 -- 1 // / // 4 na.setNode(1, 48.982618, 13.122021); na.setNode(2, 48.982565, 13.121597); na.setNode(3, 48.982611, 13.121012); na.setNode(4, 48.982336, 13.121002); g.edge(1, 2, 5, true).setName("Regener Weg"); g.edge(2, 4, 5, true).setName("Regener Weg"); g.edge(2, 3, 5, true); Path p = new Dijkstra(g, new ShortestWeighting(encoder), TraversalMode.NODE_BASED) .calcPath(1, 4); assertTrue(p.isFound()); InstructionList wayList = p.calcInstructions(tr); assertEquals(3, wayList.size()); assertEquals(-1, wayList.get(1).getSign()); } @Test public void testCalcInstructionContinueLeavingStreet() { final Graph g = new GraphBuilder(carManager).create(); final NodeAccess na = g.getNodeAccess(); // When leaving the current street via a Continue, we should show it // 3 // \ // 4 - 2 -- 1 na.setNode(1, 48.982618, 13.122021); na.setNode(2, 48.982565, 13.121597); na.setNode(3, 48.982611, 13.121012); na.setNode(4, 48.982565, 13.121002); g.edge(1, 2, 5, true).setName("Regener Weg"); g.edge(2, 4, 5, true); g.edge(2, 3, 5, true).setName("Regener Weg"); Path p = new Dijkstra(g, new ShortestWeighting(encoder), TraversalMode.NODE_BASED) .calcPath(1, 4); assertTrue(p.isFound()); InstructionList wayList = p.calcInstructions(tr); assertEquals(3, wayList.size()); assertEquals(-1, wayList.get(1).getSign()); } @Test public void testCalcInstructionSlightTurn() { final Graph g = new GraphBuilder(carManager).create(); final NodeAccess na = g.getNodeAccess(); // Real Situation: point=48.411927%2C15.599197&point=48.412094%2C15.598816 // When reaching this Crossing, you cannot know if you should turn left or right // Google Maps and Bing show a turn, OSRM does not // 1 ---2--- 3 // \ // 4 na.setNode(1, 48.412094,15.598816); na.setNode(2, 48.412055,15.599068); na.setNode(3, 48.412034,15.599411); na.setNode(4, 48.411927,15.599197); g.edge(1, 2, 5, true).setName("Stöhrgasse"); g.edge(2, 3, 5, true); g.edge(2, 4, 5, true).setName("Stöhrgasse"); Path p = new Dijkstra(g, new ShortestWeighting(encoder), TraversalMode.NODE_BASED) .calcPath(4, 1); assertTrue(p.isFound()); InstructionList wayList = p.calcInstructions(tr); assertEquals(3, wayList.size()); assertEquals(-1, wayList.get(1).getSign()); } @Test public void testCalcInstructionsForTurn() { // The street turns left, but there is not turn Path p = new Dijkstra(roundaboutGraph.g, new ShortestWeighting(encoder), TraversalMode.NODE_BASED) .calcPath(11, 13); assertTrue(p.isFound()); InstructionList wayList = p.calcInstructions(tr); // Contain start, turn, and finish instruction assertEquals(3, wayList.size()); // Assert turn right assertEquals(2, wayList.get(1).getSign()); } @Test public void testCalcInstructionsForDataFlagEncoder() { final Graph g = new GraphBuilder(dataFlagManager).create(); final NodeAccess na = g.getNodeAccess(); na.setNode(1, 48.982618, 13.122021); na.setNode(2, 48.982565, 13.121597); na.setNode(3, 48.982611, 13.121012); na.setNode(4, 48.982336, 13.121002); ReaderWay w = new ReaderWay(1); w.setTag("highway", "tertiary"); g.edge(1, 2, 5, true).setFlags(dataFlagEncoder.handleWayTags(w,1,0)); g.edge(2, 4, 5, true).setFlags(dataFlagEncoder.handleWayTags(w,1,0)); g.edge(2, 3, 5, true).setFlags(dataFlagEncoder.handleWayTags(w,1,0)); ConfigMap cMap = dataFlagEncoder.readStringMap(new PMap()); Path p = new Dijkstra(g, new GenericWeighting(dataFlagEncoder, cMap), TraversalMode.NODE_BASED).calcPath(1, 3); assertTrue(p.isFound()); InstructionList wayList = p.calcInstructions(tr); assertEquals(3, wayList.size()); } @Test public void testCalcInstructionsForSlightTurnWithOtherSlightTurn() { // Test for a fork with two sligh turns. Since there are two sligh turns, show the turn instruction Path p = new Dijkstra(roundaboutGraph.g, new ShortestWeighting(encoder), TraversalMode.NODE_BASED) .calcPath(12, 16); assertTrue(p.isFound()); InstructionList wayList = p.calcInstructions(tr); // Contain start, turn, and finish instruction assertEquals(3, wayList.size()); // Assert turn right assertEquals(1, wayList.get(1).getSign()); } @Test public void testCalcInstructionsForSlightTurnOntoDifferentStreet() { final Graph g = new GraphBuilder(carManager).create(); final NodeAccess na = g.getNodeAccess(); // Actual example: point=48.76445%2C8.679054&point=48.764152%2C8.678722 // 1 // / // 2 - 3 - 4 // na.setNode(1, 48.76423, 8.679103); na.setNode(2, 48.76417, 8.678647); na.setNode(3, 48.764149, 8.678926); na.setNode(4, 48.764085, 8.679183); g.edge(1, 3, 5, true).setName("Talstraße, K 4313"); g.edge(2, 3, 5, true).setName("Calmbacher Straße, K 4312"); g.edge(3, 4, 5, true).setName("Calmbacher Straße, K 4312"); Path p = new Dijkstra(g, new ShortestWeighting(encoder), TraversalMode.NODE_BASED) .calcPath(1, 2); assertTrue(p.isFound()); InstructionList wayList = p.calcInstructions(tr); assertEquals(3, wayList.size()); assertEquals(Instruction.TURN_SLIGHT_RIGHT, wayList.get(1).getSign()); } @Test public void testIgnoreInstructionsForSlightTurnWithOtherTurn() { // Test for a fork with one sligh turn and one actual turn. We are going along the slight turn. No turn instruction needed in this case Path p = new Dijkstra(roundaboutGraph.g, new ShortestWeighting(encoder), TraversalMode.NODE_BASED) .calcPath(16, 19); assertTrue(p.isFound()); InstructionList wayList = p.calcInstructions(tr); // Contain start, and finish instruction assertEquals(2, wayList.size()); } private class RoundaboutGraph { final public Graph g = new GraphBuilder(mixedEncoders).create(); final public NodeAccess na = g.getNodeAccess(); private final EdgeIteratorState edge3to6, edge3to9; boolean clockwise = false; List<EdgeIteratorState> roundaboutEdges = new LinkedList<EdgeIteratorState>(); private RoundaboutGraph() { // 18 // 8 14 | // \ | / 16 - 17 // 5 12 - 13 \-- 19 // / \ | \ 15 // 1 - 2 4 - 7 - 10 - 11 // \ / // 3 // | \ // 6 [ 9 ] edge 9 is turned off in default mode na.setNode(1, 52.514, 13.348); na.setNode(2, 52.514, 13.349); na.setNode(3, 52.5135, 13.35); na.setNode(4, 52.514, 13.351); na.setNode(5, 52.5145, 13.351); na.setNode(6, 52.513, 13.35); na.setNode(7, 52.514, 13.352); na.setNode(8, 52.515, 13.351); na.setNode(9, 52.513, 13.351); na.setNode(10, 52.514, 13.353); na.setNode(11, 52.514, 13.354); na.setNode(12, 52.515, 13.354); na.setNode(13, 52.515, 13.355); na.setNode(14, 52.516, 13.354); na.setNode(15, 52.516, 13.360); na.setNode(16, 52.514, 13.360); na.setNode(17, 52.514, 13.361); na.setNode(18, 52.513, 13.361); na.setNode(19, 52.515, 13.368); g.edge(1, 2, 5, true).setName("MainStreet 1 2"); // roundabout roundaboutEdges.add(g.edge(3, 2, 5, false).setName("2-3")); roundaboutEdges.add(g.edge(4, 3, 5, false).setName("3-4")); roundaboutEdges.add(g.edge(5, 4, 5, false).setName("4-5")); roundaboutEdges.add(g.edge(2, 5, 5, false).setName("5-2")); g.edge(4, 7, 5, true).setName("MainStreet 4 7"); g.edge(5, 8, 5, true).setName("5-8"); edge3to6 = g.edge(3, 6, 5, true).setName("3-6"); edge3to9 = g.edge(3, 9, 5, false).setName("3-9"); // Don't set names g.edge(7, 10, 5, true); g.edge(10, 11, 5, true); g.edge(11, 12, 5, true); g.edge(12, 13, 5, true); g.edge(12, 14, 5, true); g.edge(13, 15, 5, true); g.edge(13, 16, 5, true); g.edge(16, 17, 5, true); g.edge(17, 18, 5, true); g.edge(17, 19, 5, true); setRoundabout(clockwise); inverse3to9(); } public void setRoundabout(boolean clockwise) { for (FlagEncoder encoder : mixedEncoders.fetchEdgeEncoders()) { for (EdgeIteratorState edge : roundaboutEdges) { edge.setFlags(encoder.setAccess(edge.getFlags(), clockwise, !clockwise)); edge.setFlags(encoder.setBool(edge.getFlags(), FlagEncoder.K_ROUNDABOUT, true)); } } this.clockwise = clockwise; } public void inverse3to9() { for (FlagEncoder encoder : mixedEncoders.fetchEdgeEncoders()) { long flags = edge3to9.getFlags(); edge3to9.setFlags(encoder.setAccess(flags, !edge3to9.isForward(encoder), false)); } } public void inverse3to6() { for (FlagEncoder encoder : mixedEncoders.fetchEdgeEncoders()) { long flags = edge3to6.getFlags(); edge3to6.setFlags(encoder.setAccess(flags, !edge3to6.isForward(encoder), true)); } } private double getAngle(int n1, int n2, int n3, int n4) { double inOrientation = Helper.ANGLE_CALC.calcOrientation(na.getLat(n1), na.getLon(n1), na.getLat(n2), na.getLon(n2)); double outOrientation = Helper.ANGLE_CALC.calcOrientation(na.getLat(n3), na.getLon(n3), na.getLat(n4), na.getLon(n4)); outOrientation = Helper.ANGLE_CALC.alignOrientation(inOrientation, outOrientation); double delta = (inOrientation - outOrientation); delta = clockwise ? (Math.PI + delta) : -1 * (Math.PI - delta); return delta; } } }