public Map<Integer, Integer> findSubnetworks() {
    Map<Integer, Integer> map = new HashMap<Integer, Integer>();
    final AtomicInteger integ = new AtomicInteger(0);
    int locs = g.nodes();
    final GHBitSet bs = new GHBitSetImpl(locs);
    for (int start = 0; start < locs; start++) {
      if (g.isNodeRemoved(start) || bs.contains(start)) continue;

      new XFirstSearch() {
        @Override
        protected GHBitSet createBitSet(int size) {
          return bs;
        }

        @Override
        protected boolean goFurther(int nodeId) {
          boolean ret = super.goFurther(nodeId);
          if (ret) integ.incrementAndGet();
          return ret;
        }
      }.start(g, start, false);
      // System.out.println(start + " MAP "+map.size());
      map.put(start, integ.get());
      integ.set(0);
    }
    return map;
  }
  @Test
  public void testDifferentEdgeFilter() {
    Graph g = new GraphBuilder(encodingManager).levelGraphCreate();
    g.edge(4, 3, 10, true);
    g.edge(3, 6, 10, true);

    g.edge(4, 5, 10, true);
    g.edge(5, 6, 10, true);

    AlgorithmPreparation prep = prepareGraph(g);
    DijkstraOneToMany algo = (DijkstraOneToMany) prep.createAlgo();
    algo.setEdgeFilter(
        new EdgeFilter() {
          @Override
          public boolean accept(EdgeIteratorState iter) {
            return iter.getAdjNode() != 5;
          }
        });
    Path p = algo.calcPath(4, 6);
    assertEquals(Helper.createTList(4, 3, 6), p.calcNodes());

    // important call!
    algo.clear();
    algo.setEdgeFilter(
        new EdgeFilter() {
          @Override
          public boolean accept(EdgeIteratorState iter) {
            return iter.getAdjNode() != 3;
          }
        });
    p = algo.calcPath(4, 6);
    assertEquals(Helper.createTList(4, 5, 6), p.calcNodes());
  }
  @Test
  public void testCopy() {
    Graph g = initUnsorted(createGraph());
    EdgeIteratorState eb = g.edge(6, 5, 11, true);
    eb.setWayGeometry(Helper.createPointList(12, 10, -1, 3));
    LevelGraph lg = new GraphBuilder(encodingManager).levelGraphCreate();
    GHUtility.copyTo(g, lg);
    eb = lg.getEdgeProps(eb.getEdge(), 6);
    assertEquals(Helper.createPointList(-1, 3, 12, 10), eb.getWayGeometry());
    assertEquals(0, lg.getLevel(0));
    assertEquals(0, lg.getLevel(1));
    assertEquals(0, lg.getLatitude(0), 1e-6);
    assertEquals(1, lg.getLongitude(0), 1e-6);
    assertEquals(2.5, lg.getLatitude(1), 1e-6);
    assertEquals(4.5, lg.getLongitude(1), 1e-6);
    assertEquals(9, lg.getNodes());
    EdgeIterator iter = lg.createEdgeExplorer().setBaseNode(8);
    iter.next();
    assertEquals(2.05, iter.getDistance(), 1e-6);
    assertEquals("11", BitUtil.toBitString(iter.getFlags(), 2));
    iter.next();
    assertEquals(0.5, iter.getDistance(), 1e-6);
    assertEquals("11", BitUtil.toBitString(iter.getFlags(), 2));

    iter = lg.createEdgeExplorer().setBaseNode(7);
    iter.next();
    assertEquals(2.1, iter.getDistance(), 1e-6);
    assertEquals("01", BitUtil.toBitString(iter.getFlags(), 2));
    assertTrue(iter.next());
    assertEquals(0.7, iter.getDistance(), 1e-6);
  }
 @Test
 public void testMediumRead() throws IOException {
   Graph graph = new GraphBuilder().create();
   new PrinctonReader(graph)
       .stream(new GZIPInputStream(PrinctonReader.class.getResourceAsStream("mediumEWD.txt.gz")))
       .read();
   assertEquals(250, graph.nodes());
   assertEquals(13, count(graph.getOutgoing(244)));
   assertEquals(11, count(graph.getOutgoing(16)));
 }
 @Test
 public void testRead() {
   Graph graph = new GraphBuilder().create();
   new PrinctonReader(graph)
       .stream(PrinctonReader.class.getResourceAsStream("tinyEWD.txt"))
       .read();
   assertEquals(8, graph.nodes());
   assertEquals(2, count(graph.getOutgoing(0)));
   assertEquals(3, count(graph.getOutgoing(6)));
 }
 @Test
 public void testSortDirected() {
   Graph g = createGraph();
   g.setNode(0, 0, 1);
   g.setNode(1, 2.5, 2);
   g.setNode(2, 3.5, 3);
   g.edge(0, 1, 1.1, false);
   g.edge(2, 1, 1.1, false);
   GHUtility.sortDFS(g, createGraph());
 }
Beispiel #7
0
  public void plotNode(Graphics2D g2, int loc, Color c) {
    double lat = g.getLatitude(loc);
    double lon = g.getLongitude(loc);
    if (lat < bounds.minLat || lat > bounds.maxLat || lon < bounds.minLon || lon > bounds.maxLon)
      return;

    Color old = g2.getColor();
    g2.setColor(c);
    plot(g2, lat, lon, 4);
    g2.setColor(old);
  }
  @Test
  public void testCannotCalculateSP2() {
    Graph g = createGraph(10);
    g.edge(0, 1, 1, false);
    g.edge(1, 2, 1, false);

    DijkstraBidirectionRef db = new DijkstraBidirectionRef(g);
    db.addSkipNode(1);
    Path p = db.calcPath(0, 2);
    assertFalse(p.found());
  }
 /**
  * To avoid large processing and a large HashMap remove nodes with no edges up front
  *
  * @return removed nodes
  */
 int removeZeroDegreeNodes() {
   int removed = 0;
   int locs = g.nodes();
   for (int start = 0; start < locs; start++) {
     if (!g.getEdges(start).next()) {
       removed++;
       g.markNodeRemoved(start);
     }
   }
   return removed;
 }
 @Test
 public void testSort2() {
   Graph g = initUnsorted(createGraph());
   Graph newG = GHUtility.sortDFS(g, createGraph());
   // TODO does not handle subnetworks
   // assertEquals(g.nodes(), newG.nodes());
   assertEquals(0, newG.getLatitude(0), 1e-4); // 0
   assertEquals(2.5, newG.getLatitude(1), 1e-4); // 1
   assertEquals(4.5, newG.getLatitude(2), 1e-4); // 2
   assertEquals(4.6, newG.getLatitude(3), 1e-4); // 8
 }
 @Test
 public void testNoErrorOnEdgeCase_lastIndex() {
   final EncodingManager encodingManager = new EncodingManager("car");
   int locs = 10000;
   Graph g =
       AbstractLocationIndexTester.this.createGHStorage(
           new MMapDirectory(location), encodingManager, false);
   NodeAccess na = g.getNodeAccess();
   Random rand = new Random(12);
   for (int i = 0; i < locs; i++) {
     na.setNode(i, (float) rand.nextDouble() * 10 + 10, (float) rand.nextDouble() * 10 + 10);
   }
   idx = createIndex(g, 200);
   Helper.close((Closeable) g);
 }
Beispiel #12
0
 @Test
 public void testExtract() {
   Graph g = createGraph();
   g.edge(1, 2, 10, true);
   PathBidirRef pw = new PathBidirRef(g, carEncoder);
   EdgeExplorer explorer = g.createEdgeExplorer(carOutEdges);
   EdgeIterator iter = explorer.setBaseNode(1);
   iter.next();
   pw.edgeEntry = new EdgeEntry(iter.getEdge(), 2, 0);
   pw.edgeEntry.parent = new EdgeEntry(EdgeIterator.NO_EDGE, 1, 10);
   pw.edgeTo = new EdgeEntry(EdgeIterator.NO_EDGE, 2, 0);
   Path p = pw.extract();
   assertEquals(Helper.createTList(1, 2), p.calcNodes());
   assertEquals(10, p.getDistance(), 1e-4);
 }
  int addEdge(int fromIndex, int toIndex, PointList pointList, int flags) {
    double towerNodeDistance = 0;
    double prevLat = pointList.latitude(0);
    double prevLon = pointList.longitude(0);
    double lat;
    double lon;
    PointList pillarNodes = new PointList(pointList.size() - 2);
    int nodes = pointList.size();
    for (int i = 1; i < nodes; i++) {
      lat = pointList.latitude(i);
      lon = pointList.longitude(i);
      towerNodeDistance += callback.calcDist(prevLat, prevLon, lat, lon);
      prevLat = lat;
      prevLon = lon;
      if (nodes > 2 && i < nodes - 1) pillarNodes.add(lat, lon);
    }
    if (towerNodeDistance == 0) {
      // As investigation shows often two paths should have crossed via one identical point
      // but end up in two very close points. later this will be removed/fixed while
      // removing short edges where one node is of degree 2
      zeroCounter++;
      towerNodeDistance = 0.0001;
    }

    EdgeIterator iter = g.edge(fromIndex, toIndex, towerNodeDistance, flags);
    if (nodes > 2) iter.wayGeometry(pillarNodes);
    return nodes;
  }
Beispiel #14
0
 public MyGraphics(Graph g) {
   this.g = g;
   BBox b = g.bounds();
   scaleX = scaleY = 0.002 * (b.maxLat - b.minLat);
   offsetY = b.maxLat - 90;
   offsetX = -b.minLon;
 }
 //       8
 //       |
 //    6->0->1->3->7
 //    |        |
 //    |        v
 // 10<-2---4<---5
 //    9
 public static void initDirected1(Graph g) {
   g.edge(0, 8, 1, true);
   g.edge(0, 1, 1, false);
   g.edge(1, 3, 1, false);
   g.edge(3, 7, 1, false);
   g.edge(3, 5, 1, false);
   g.edge(5, 4, 1, false);
   g.edge(4, 2, 1, true);
   g.edge(2, 9, 1, false);
   g.edge(2, 10, 1, false);
   g.edge(2, 6, 1, true);
   g.edge(6, 0, 1, false);
 }
 public void doWork() {
   int del = removeZeroDegreeNodes();
   Map<Integer, Integer> map = findSubnetworks();
   keepLargeNetwork(map);
   logger.info(
       "optimize to remove subnetworks(" + map.size() + "), zero-degree-nodes(" + del + ")");
   g.optimize();
   subNetworks = map.size();
 }
 @Test
 public void testSort() {
   Graph g = initUnsorted(createGraph());
   Graph newG = GHUtility.sortDFS(g, createGraph());
   assertEquals(g.getNodes(), newG.getNodes());
   assertEquals(0, newG.getLatitude(0), 1e-4); // 0
   assertEquals(2.5, newG.getLatitude(1), 1e-4); // 1
   assertEquals(4.5, newG.getLatitude(2), 1e-4); // 2
   assertEquals(4.6, newG.getLatitude(3), 1e-4); // 8
   assertEquals(3.0, newG.getLatitude(4), 1e-4); // 3
   assertEquals(5.0, newG.getLatitude(5), 1e-4); // 7
   assertEquals(4.2, newG.getLatitude(6), 1e-4); // 5
 }
  @Test
  public void testDifferentVehicles() {
    final EncodingManager encodingManager = new EncodingManager("car,foot");
    Graph g = AbstractLocationIndexTester.this.createGHStorage(encodingManager);
    initSimpleGraph(g);
    idx = createIndex(g, -1);
    assertEquals(1, findID(idx, 1, -1));

    // now make all edges from node 1 accessible for CAR only
    EdgeIterator iter = g.createEdgeExplorer().setBaseNode(1);
    CarFlagEncoder carEncoder = (CarFlagEncoder) encodingManager.getEncoder("car");
    while (iter.next()) {
      iter.setFlags(carEncoder.setProperties(50, true, true));
    }
    idx.close();

    idx = createIndex(g, -1);
    FootFlagEncoder footEncoder = (FootFlagEncoder) encodingManager.getEncoder("foot");
    assertEquals(2, idx.findClosest(1, -1, new DefaultEdgeFilter(footEncoder)).getClosestNode());
    Helper.close((Closeable) g);
  }
 public void initSimpleGraph(Graph g) {
   //  6 |       4
   //  5 |
   //    |     6
   //  4 |              5
   //  3 |
   //  2 |    1
   //  1 |          3
   //  0 |    2
   // -1 | 0
   // ---|-------------------
   //    |-2 -1 0 1 2 3 4
   //
   NodeAccess na = g.getNodeAccess();
   na.setNode(0, -1, -2);
   na.setNode(1, 2, -1);
   na.setNode(2, 0, 1);
   na.setNode(3, 1, 2);
   na.setNode(4, 6, 1);
   na.setNode(5, 4, 4);
   na.setNode(6, 4.5, -0.5);
   g.edge(0, 1, 3.5, true);
   g.edge(0, 2, 2.5, true);
   g.edge(2, 3, 1, true);
   g.edge(3, 4, 3.2, true);
   g.edge(1, 4, 2.4, true);
   g.edge(3, 5, 1.5, true);
   // make sure 6 is connected
   g.edge(6, 4, 1.2, true);
 }
 public static RoutingAlgorithm[] createAlgos(final Graph g) {
   LevelGraph graphSimpleSC =
       (LevelGraphStorage) g.copyTo(new LevelGraphStorage(new RAMDirectory()).createNew(10));
   PrepareSimpleShortcuts prepare = new PrepareSimpleShortcuts().setGraph(graphSimpleSC);
   prepare.doWork();
   AStarBidirection astarSimpleSC = (AStarBidirection) prepare.createAStar();
   astarSimpleSC.setApproximation(false);
   LevelGraph graphCH =
       (LevelGraphStorage) g.copyTo(new LevelGraphStorage(new RAMDirectory()).createNew(10));
   PrepareContractionHierarchies prepareCH = new PrepareContractionHierarchies().setGraph(graphCH);
   prepareCH.doWork();
   return new RoutingAlgorithm[] {
     new AStar(g),
     new AStarBidirection(g),
     new DijkstraBidirectionRef(g),
     new DijkstraBidirection(g),
     new DijkstraSimple(g),
     prepare.createAlgo(),
     astarSimpleSC,
     prepareCH.createAlgo()
   };
 }
 void testIndex() {
   // query outside
   double qLat = 49.4000;
   double qLon = 9.9690;
   int id = idx.findID(qLat, qLon);
   double foundLat = unterfrankenGraph.getLatitude(id);
   double foundLon = unterfrankenGraph.getLongitude(id);
   double dist = new DistanceCalc().calcDistKm(qLat, qLon, foundLat, foundLon);
   double expectedDist = 5.5892;
   if (Math.abs(dist - expectedDist) > 1e-4)
     System.out.println(
         "ERROR in test index. queried lat,lon="
             + (float) qLat
             + ","
             + (float) qLon
             + ", but was "
             + (float) foundLat
             + ","
             + (float) foundLon
             + "\n   expected distance:"
             + expectedDist
             + ", but was:"
             + dist);
 }
 // 0-1-2-3-4
 // |     / |
 // |    8  |
 // \   /   /
 //  7-6-5-/
 void initBiGraph(Graph graph) {
   graph.edge(0, 1, 100, true);
   graph.edge(1, 2, 1, true);
   graph.edge(2, 3, 1, true);
   graph.edge(3, 4, 1, true);
   graph.edge(4, 5, 25, true);
   graph.edge(5, 6, 25, true);
   graph.edge(6, 7, 5, true);
   graph.edge(7, 0, 5, true);
   graph.edge(3, 8, 20, true);
   graph.edge(8, 6, 20, true);
 }
  /** Deletes all but the larges subnetworks. */
  void keepLargeNetwork(Map<Integer, Integer> map) {
    if (map.size() < 2) return;

    int biggestStart = -1;
    int maxCount = -1;
    GHBitSetImpl bs = new GHBitSetImpl(g.nodes());
    for (Entry<Integer, Integer> e : map.entrySet()) {
      if (biggestStart < 0) {
        biggestStart = e.getKey();
        maxCount = e.getValue();
        continue;
      }

      if (maxCount < e.getValue()) {
        // new biggest area found. remove old
        removeNetwork(biggestStart, maxCount, bs);

        biggestStart = e.getKey();
        maxCount = e.getValue();
      } else removeNetwork(e.getKey(), e.getValue(), bs);
    }
  }
  @Test
  public void testGrid() {
    Graph g = createSampleGraph(new EncodingManager("car"));
    int locs = g.getNodes();

    idx = createIndex(g, -1);
    // if we would use less array entries then some points gets the same key so avoid that for this
    // test
    // e.g. for 16 we get "expected 6 but was 9" i.e 6 was overwritten by node j9 which is a bit
    // closer to the grid center
    // go through every point of the graph if all points are reachable
    NodeAccess na = g.getNodeAccess();
    for (int i = 0; i < locs; i++) {
      double lat = na.getLatitude(i);
      double lon = na.getLongitude(i);
      assertEquals("nodeId:" + i + " " + (float) lat + "," + (float) lon, i, findID(idx, lat, lon));
    }

    // hit random lat,lon and compare result to full index
    Random rand = new Random(12);
    LocationIndex fullIndex;
    if (hasEdgeSupport()) fullIndex = new Location2IDFullWithEdgesIndex(g);
    else fullIndex = new Location2IDFullIndex(g);

    DistanceCalc dist = new DistanceCalcEarth();
    for (int i = 0; i < 100; i++) {
      double lat = rand.nextDouble() * 5;
      double lon = rand.nextDouble() * 5;
      int fullId = findID(fullIndex, lat, lon);
      double fullLat = na.getLatitude(fullId);
      double fullLon = na.getLongitude(fullId);
      float fullDist = (float) dist.calcDist(lat, lon, fullLat, fullLon);
      int newId = findID(idx, lat, lon);
      double newLat = na.getLatitude(newId);
      double newLon = na.getLongitude(newId);
      float newDist = (float) dist.calcDist(lat, lon, newLat, newLon);

      if (testGridIgnore(i)) {
        continue;
      }

      assertTrue(
          i
              + " orig:"
              + (float) lat
              + ","
              + (float) lon
              + " full:"
              + fullLat
              + ","
              + fullLon
              + " fullDist:"
              + fullDist
              + " found:"
              + newLat
              + ","
              + newLon
              + " foundDist:"
              + newDist,
          Math.abs(fullDist - newDist) < 50000);
    }
    fullIndex.close();
    Helper.close((Closeable) g);
  }
 // 0-1-.....-9-10
 // |         ^   \
 // |         |    |
 // 17-16-...-11<-/
 public static void initDirected2(Graph g) {
   g.edge(0, 1, 1, true);
   g.edge(1, 2, 1, true);
   g.edge(2, 3, 1, true);
   g.edge(3, 4, 1, true);
   g.edge(4, 5, 1, true);
   g.edge(5, 6, 1, true);
   g.edge(6, 7, 1, true);
   g.edge(7, 8, 1, true);
   g.edge(8, 9, 1, true);
   g.edge(9, 10, 1, true);
   g.edge(10, 11, 1, false);
   g.edge(11, 12, 1, true);
   g.edge(11, 9, 3, false);
   g.edge(12, 13, 1, true);
   g.edge(13, 14, 1, true);
   g.edge(14, 15, 1, true);
   g.edge(15, 16, 1, true);
   g.edge(16, 17, 1, true);
   g.edge(17, 0, 1, true);
 }
  void initRoundaboutGraph(Graph g) {
    //              roundabout:
    // 16-0-9-10--11   12<-13
    //    \       \  /      \
    //    17       \|        7-8-..
    // -15-1--2--3--4       /     /
    //     /         \-5->6/     /
    //  -14            \________/

    g.edge(16, 0, 1, true);
    g.edge(0, 9, 1, true);
    g.edge(0, 17, 1, true);
    g.edge(9, 10, 1, true);
    g.edge(10, 11, 1, true);
    g.edge(11, 28, 1, true);
    g.edge(28, 29, 1, true);
    g.edge(29, 30, 1, true);
    g.edge(30, 31, 1, true);
    g.edge(31, 4, 1, true);

    g.edge(17, 1, 1, true);
    g.edge(15, 1, 1, true);
    g.edge(14, 1, 1, true);
    g.edge(14, 18, 1, true);
    g.edge(18, 19, 1, true);
    g.edge(19, 20, 1, true);
    g.edge(20, 15, 1, true);
    g.edge(19, 21, 1, true);
    g.edge(21, 16, 1, true);
    g.edge(1, 2, 1, true);
    g.edge(2, 3, 1, true);
    g.edge(3, 4, 1, true);

    g.edge(4, 5, 1, false);
    g.edge(5, 6, 1, false);
    g.edge(6, 7, 1, false);
    g.edge(7, 13, 1, false);
    g.edge(13, 12, 1, false);
    g.edge(12, 4, 1, false);

    g.edge(7, 8, 1, true);
    g.edge(8, 22, 1, true);
    g.edge(22, 23, 1, true);
    g.edge(23, 24, 1, true);
    g.edge(24, 25, 1, true);
    g.edge(25, 27, 1, true);
    g.edge(27, 5, 1, true);
    g.edge(25, 26, 1, false);
    g.edge(26, 25, 1, false);
  }
  public void runShortestPathPerf(int runs, RoutingAlgorithm algo) throws Exception {
    BBox bbox = unterfrankenGraph.getBounds();
    double minLat = bbox.minLat, minLon = bbox.minLon;
    double maxLat = bbox.maxLat, maxLon = bbox.maxLon;
    if (unterfrankenGraph instanceof LevelGraph) {
      if (algo instanceof DijkstraBidirectionRef)
        algo = new PrepareContractionHierarchies().setGraph(unterfrankenGraph).createAlgo();
      //                algo = new
      // PrepareSimpleShortcuts().setGraph(unterfrankenGraph).createAlgo();
      else if (algo instanceof AStarBidirection)
        algo = new PrepareSimpleShortcuts().setGraph(unterfrankenGraph).createAStar();
      else
        // level graph accepts all algorithms but normally we want to use an optimized one
        throw new IllegalStateException(
            "algorithm which boosts query time for levelgraph not found " + algo);
      logger.info("[experimental] using shortcuts with " + algo);
    } else logger.info("running " + algo);

    Random rand = new Random(123);
    StopWatch sw = new StopWatch();

    // System.out.println("cap:" + ((GraphStorage) unterfrankenGraph).capacity());
    for (int i = 0; i < runs; i++) {
      double fromLat = rand.nextDouble() * (maxLat - minLat) + minLat;
      double fromLon = rand.nextDouble() * (maxLon - minLon) + minLon;
      //            sw.start();
      int from = idx.findID(fromLat, fromLon);
      double toLat = rand.nextDouble() * (maxLat - minLat) + minLat;
      double toLon = rand.nextDouble() * (maxLon - minLon) + minLon;
      int to = idx.findID(toLat, toLon);
      //            sw.stop();
      //                logger.info(i + " " + sw + " from (" + from + ")" + fromLat + ", " + fromLon
      // + " to (" + to + ")" + toLat + ", " + toLon);
      if (from == to) {
        logger.warn("skipping i " + i + " from==to " + from);
        continue;
      }

      algo.clear();
      sw.start();
      Path p = algo.calcPath(from, to);
      sw.stop();
      if (!p.found()) {
        // there are still paths not found cause of oneway motorways => only routable in one
        // direction
        // e.g. unterfrankenGraph.getLatitude(798809) + "," + unterfrankenGraph.getLongitude(798809)
        logger.warn(
            "no route found for i="
                + i
                + " !? "
                + "graph-from "
                + from
                + "("
                + fromLat
                + ","
                + fromLon
                + "), "
                + "graph-to "
                + to
                + "("
                + toLat
                + ","
                + toLon
                + ")");
        continue;
      }
      if (i % 20 == 0)
        logger.info(i + " " + sw.getSeconds() / (i + 1) + " secs/run"); // (" + p + ")");
    }
  }
 Graph initUnsorted(Graph g) {
   g.setNode(0, 0, 1);
   g.setNode(1, 2.5, 4.5);
   g.setNode(2, 4.5, 4.5);
   g.setNode(3, 3, 0.5);
   g.setNode(4, 2.8, 2.8);
   g.setNode(5, 4.2, 1.6);
   g.setNode(6, 2.3, 2.2);
   g.setNode(7, 5, 1.5);
   g.setNode(8, 4.6, 4);
   g.edge(8, 2, 0.5, true);
   g.edge(7, 3, 2.1, false);
   g.edge(1, 0, 3.9, true);
   g.edge(7, 5, 0.7, true);
   g.edge(1, 2, 1.9, true);
   g.edge(8, 1, 2.05, true);
   return g;
 }
  public Graph createSampleGraph(EncodingManager encodingManager) {
    Graph graph = AbstractLocationIndexTester.this.createGHStorage(encodingManager);
    // length does not matter here but lat,lon and outgoing edges do!

    //
    //   lat             /--------\
    //    5   o-        p--------\ q
    //          \  /-----\-----n | |
    //    4       k    /--l--    m/
    //           / \  j      \   |
    //    3     |   g  \  h---i  /
    //          |       \    /  /
    //    2     e---------f--  /
    //                   /  \-d
    //    1        /--b--      \
    //            |    \--------c
    //    0       a
    //
    //   lon: 0   1   2   3   4   5
    int a0 = 0;
    NodeAccess na = graph.getNodeAccess();
    na.setNode(0, 0, 1.0001f);
    int b1 = 1;
    na.setNode(1, 1, 2);
    int c2 = 2;
    na.setNode(2, 0.5f, 4.5f);
    int d3 = 3;
    na.setNode(3, 1.5f, 3.8f);
    int e4 = 4;
    na.setNode(4, 2.01f, 0.5f);
    int f5 = 5;
    na.setNode(5, 2, 3);
    int g6 = 6;
    na.setNode(6, 3, 1.5f);
    int h7 = 7;
    na.setNode(7, 2.99f, 3.01f);
    int i8 = 8;
    na.setNode(8, 3, 4);
    int j9 = 9;
    na.setNode(9, 3.3f, 2.2f);
    int k10 = 10;
    na.setNode(10, 4, 1);
    int l11 = 11;
    na.setNode(11, 4.1f, 3);
    int m12 = 12;
    na.setNode(12, 4, 4.5f);
    int n13 = 13;
    na.setNode(13, 4.5f, 4.1f);
    int o14 = 14;
    na.setNode(14, 5, 0);
    int p15 = 15;
    na.setNode(15, 4.9f, 2.5f);
    int q16 = 16;
    na.setNode(16, 5, 5);
    // => 17 locations

    graph.edge(a0, b1, 1, true);
    graph.edge(c2, b1, 1, true);
    graph.edge(c2, d3, 1, true);
    graph.edge(f5, b1, 1, true);
    graph.edge(e4, f5, 1, true);
    graph.edge(m12, d3, 1, true);
    graph.edge(e4, k10, 1, true);
    graph.edge(f5, d3, 1, true);
    graph.edge(f5, i8, 1, true);
    graph.edge(f5, j9, 1, true);
    graph.edge(k10, g6, 1, true);
    graph.edge(j9, l11, 1, true);
    graph.edge(i8, l11, 1, true);
    graph.edge(i8, h7, 1, true);
    graph.edge(k10, n13, 1, true);
    graph.edge(k10, o14, 1, true);
    graph.edge(l11, p15, 1, true);
    graph.edge(m12, p15, 1, true);
    graph.edge(q16, p15, 1, true);
    graph.edge(q16, m12, 1, true);
    return graph;
  }