Exemplo n.º 1
0
 public StateEditor(State parent, Edge e) {
   child = parent.clone();
   child.backState = parent;
   child.backEdge = e;
   // We clear child.next here, since it could have already been set in the
   // parent
   child.next = null;
   if (e == null) {
     child.backState = null;
     child.hops = 0;
     child.vertex = parent.vertex;
     child.stateData = child.stateData.clone();
   } else {
     child.hops = parent.hops + 1;
     // be clever
     // Note that we use equals(), not ==, here to allow for dynamically
     // created vertices
     if (e.getFromVertex().equals(e.getToVertex()) && parent.vertex.equals(e.getFromVertex())) {
       // TODO LG: We disable this test: the assumption that
       // the from and to vertex of an edge are not the same
       // is not true anymore: bike rental on/off edges.
       traversingBackward = parent.getOptions().isArriveBy();
       child.vertex = e.getToVertex();
     } else if (parent.vertex.equals(e.getFromVertex())) {
       traversingBackward = false;
       child.vertex = e.getToVertex();
     } else if (parent.vertex.equals(e.getToVertex())) {
       traversingBackward = true;
       child.vertex = e.getFromVertex();
     } else {
       // Parent state is not at either end of edge.
       LOG.warn("Edge is not connected to parent state: {}", e);
       LOG.warn("   from   vertex: {}", e.getFromVertex());
       LOG.warn("   to     vertex: {}", e.getToVertex());
       LOG.warn("   parent vertex: {}", parent.vertex);
       defectiveTraversal = true;
     }
     if (traversingBackward != parent.getOptions().isArriveBy()) {
       LOG.error(
           "Actual traversal direction does not match traversal direction in TraverseOptions.");
       defectiveTraversal = true;
     }
     if (parent.stateData.noThruTrafficState == NoThruTrafficState.INIT
         && !(e instanceof FreeEdge)) {
       setNoThruTrafficState(NoThruTrafficState.BETWEEN_ISLANDS);
     }
   }
 }
Exemplo n.º 2
0
  public static int getPreviousArriveTime(
      RoutingRequest request, int arrivalTime, Vertex stopVertex) {

    int bestArrivalTime = -1;

    request.arriveBy = true;

    // find the alights
    for (Edge prealight : stopVertex.getIncoming()) {
      if (prealight instanceof PreAlightEdge) {
        Vertex arrival = prealight.getFromVertex(); // this is the arrival vertex
        for (Edge alight : arrival.getIncoming()) {
          if (alight instanceof TransitBoardAlight) {
            State state = new State(alight.getToVertex(), arrivalTime, request);
            State result = alight.traverse(state);
            if (result == null) continue;
            int time = (int) result.getTime();
            if (time > bestArrivalTime) {
              bestArrivalTime = time;
            }
          }
        }
      }
    }

    request.arriveBy = false;
    return bestArrivalTime;
  }
Exemplo n.º 3
0
  public static int getNextDepartTime(
      RoutingRequest request, int departureTime, Vertex stopVertex) {

    int bestArrivalTime = Integer.MAX_VALUE;

    request.arriveBy = false;

    // find the boards
    for (Edge preboard : stopVertex.getOutgoing()) {
      if (preboard instanceof PreBoardEdge) {
        Vertex departure = preboard.getToVertex(); // this is the departure vertex
        for (Edge board : departure.getOutgoing()) {
          if (board instanceof TransitBoardAlight) {
            State state = new State(board.getFromVertex(), departureTime, request);
            State result = board.traverse(state);
            if (result == null) continue;
            int time = (int) result.getTime();
            if (time < bestArrivalTime) {
              bestArrivalTime = time;
            }
          }
        }
      }
    }

    request.arriveBy = true;
    return bestArrivalTime;
  }
  public void drawAnotation(GraphBuilderAnnotation anno) {
    Envelope env = new Envelope();

    Edge e = anno.getReferencedEdge();
    if (e != null) {
      this.enqueueHighlightedEdge(e);
      env.expandToInclude(e.getFromVertex().getCoordinate());
      env.expandToInclude(e.getToVertex().getCoordinate());
    }

    ArrayList<Vertex> vertices = new ArrayList<Vertex>();
    Vertex v = anno.getReferencedVertex();
    if (v != null) {
      env.expandToInclude(v.getCoordinate());
      vertices.add(v);
    }

    if (e == null && v == null) return;

    // make it a little bigger, especially needed for STOP_UNLINKED
    env.expandBy(0.02);

    // highlight relevant things
    this.clearHighlights();
    this.setHighlightedVertices(vertices);

    // zoom the graph display
    this.zoomToEnvelope(env);

    // and draw
    this.draw();
  }
Exemplo n.º 5
0
  public static DisjointSet<Vertex> getConnectedComponents(Graph graph) {
    DisjointSet<Vertex> components = new DisjointSet<Vertex>();

    for (Vertex v : graph.getVertices()) {
      for (Edge e : v.getOutgoing()) {
        components.union(e.getFromVertex(), e.getToVertex());
      }
    }
    return components;
  }
Exemplo n.º 6
0
  /**
   * Get vertices connected to an edge
   *
   * @return
   */
  @Secured({"ROLE_USER"})
  @GET
  @Path("/verticesForEdge")
  @Produces({MediaType.APPLICATION_JSON})
  public Object getVerticesForEdge(@QueryParam("edge") int edgeId) {

    Graph graph = graphService.getGraph();
    Edge edge = graph.getEdgeById(edgeId);

    VertexSet out = new VertexSet();
    out.vertices = new ArrayList<Vertex>(2);
    out.vertices.add(edge.getFromVertex());
    out.vertices.add(edge.getToVertex());

    return out.withGraph(graph);
  }
 private WalkStep createWalkStep(State s) {
   Edge en = s.getBackEdge();
   WalkStep step;
   step = new WalkStep();
   step.streetName = en.getName();
   step.lon = en.getFromVertex().getX();
   step.lat = en.getFromVertex().getY();
   step.elevation = encodeElevationProfile(s.getBackEdge(), 0);
   step.bogusName = en.hasBogusName();
   step.addAlerts(s.getBackAlerts());
   step.angle = DirectionUtils.getFirstAngle(s.getBackEdge().getGeometry());
   if (s.getBackEdge() instanceof AreaEdge) {
     step.area = true;
   }
   return step;
 }
  public InferredEdge(@Nonnull Edge edge, @Nonnull Integer edgeId, @Nonnull OtpGraph graph) {
    Preconditions.checkNotNull(edge);
    Preconditions.checkNotNull(graph);
    Preconditions.checkNotNull(edgeId);

    assert !(edge instanceof TurnEdge || edge == null);

    this.graph = graph;
    this.edgeId = edgeId;
    this.edge = edge;

    /*
     * Warning: this geometry is in lon/lat and may contain more than one
     * straight line.
     */
    this.geometry = edge.getGeometry();

    this.locationIndexedLine = new LocationIndexedLine(geometry);
    this.lengthIndexedLine = new LengthIndexedLine(geometry);
    this.lengthLocationMap = new LengthLocationMap(geometry);

    this.startVertex = edge.getFromVertex();
    this.endVertex = edge.getToVertex();

    final Coordinate startPointCoord =
        this.locationIndexedLine.extractPoint(this.locationIndexedLine.getStartIndex());

    this.startPoint =
        VectorFactory.getDefault().createVector2D(startPointCoord.x, startPointCoord.y);

    final Coordinate endPointCoord =
        this.locationIndexedLine.extractPoint(this.locationIndexedLine.getEndIndex());
    this.endPoint = VectorFactory.getDefault().createVector2D(endPointCoord.x, endPointCoord.y);

    this.velocityPrecisionDist =
        // ~4.4 m/s, std. dev ~ 30 m/s, Gamma with exp. value = 30 m/s
        // TODO perhaps variance of velocity should be in m/s^2. yeah...
        new NormalInverseGammaDistribution(
            4.4d, 1d / Math.pow(30d, 2d), 1d / Math.pow(30d, 2d) + 1d, Math.pow(30d, 2d));
    this.velocityEstimator =
        new UnivariateGaussianMeanVarianceBayesianEstimator(velocityPrecisionDist);
  }
Exemplo n.º 9
0
 /**
  * Internals of getRegionsForVertex; keeps track of seen vertices to avoid loops.
  *
  * @param regionData
  * @param vertex
  * @param seen
  * @param depth
  * @return
  */
 private static HashSet<Integer> getRegionsForVertex(
     RegionData regionData, Vertex vertex, HashSet<Vertex> seen, int depth) {
   seen.add(vertex);
   HashSet<Integer> regions = new HashSet<Integer>();
   int region = vertex.getGroupIndex();
   if (region >= 0) {
     regions.add(region);
   } else {
     for (Edge e : vertex.getOutgoing()) {
       final Vertex tov = e.getToVertex();
       if (!seen.contains(tov))
         regions.addAll(getRegionsForVertex(regionData, tov, seen, depth + 1));
     }
     for (Edge e : vertex.getIncoming()) {
       final Vertex fromv = e.getFromVertex();
       if (!seen.contains(fromv))
         regions.addAll(getRegionsForVertex(regionData, fromv, seen, depth + 1));
     }
   }
   return regions;
 }
  @Test
  public final void testOnBoardDepartureTime() {
    Coordinate[] coordinates = new Coordinate[5];
    coordinates[0] = new Coordinate(0.0, 0.0);
    coordinates[1] = new Coordinate(0.0, 1.0);
    coordinates[2] = new Coordinate(2.0, 1.0);
    coordinates[3] = new Coordinate(5.0, 1.0);
    coordinates[4] = new Coordinate(5.0, 5.0);

    PatternDepartVertex depart = mock(PatternDepartVertex.class);
    PatternArriveVertex dwell = mock(PatternArriveVertex.class);
    PatternArriveVertex arrive = mock(PatternArriveVertex.class);
    Graph graph = mock(Graph.class);
    RoutingRequest routingRequest = mock(RoutingRequest.class);
    ServiceDay serviceDay = mock(ServiceDay.class);

    when(graph.getTimeZone()).thenReturn(TimeZone.getTimeZone("GMT"));

    GeometryFactory geometryFactory = GeometryUtils.getGeometryFactory();
    CoordinateSequenceFactory coordinateSequenceFactory =
        geometryFactory.getCoordinateSequenceFactory();
    CoordinateSequence coordinateSequence = coordinateSequenceFactory.create(coordinates);
    LineString geometry = new LineString(coordinateSequence, geometryFactory);
    ArrayList<Edge> hops = new ArrayList<Edge>(2);
    RoutingContext routingContext = new RoutingContext(routingRequest, graph, null, arrive);
    AgencyAndId agencyAndId = new AgencyAndId("Agency", "ID");
    Route route = new Route();
    ArrayList<StopTime> stopTimes = new ArrayList<StopTime>(3);
    StopTime stopDepartTime = new StopTime();
    StopTime stopDwellTime = new StopTime();
    StopTime stopArriveTime = new StopTime();
    Stop stopDepart = new Stop();
    Stop stopDwell = new Stop();
    Stop stopArrive = new Stop();
    Trip trip = new Trip();

    routingContext.serviceDays = new ArrayList<ServiceDay>(Collections.singletonList(serviceDay));
    route.setId(agencyAndId);
    stopDepart.setId(agencyAndId);
    stopDwell.setId(agencyAndId);
    stopArrive.setId(agencyAndId);
    stopDepartTime.setStop(stopDepart);
    stopDepartTime.setDepartureTime(0);
    stopDwellTime.setArrivalTime(20);
    stopDwellTime.setStop(stopDwell);
    stopDwellTime.setDepartureTime(40);
    stopArriveTime.setArrivalTime(60);
    stopArriveTime.setStop(stopArrive);
    stopTimes.add(stopDepartTime);
    stopTimes.add(stopDwellTime);
    stopTimes.add(stopArriveTime);
    trip.setId(agencyAndId);
    trip.setTripHeadsign("The right");

    TripTimes tripTimes = new TripTimes(trip, stopTimes, new Deduplicator());
    StopPattern stopPattern = new StopPattern(stopTimes);
    TripPattern tripPattern = new TripPattern(route, stopPattern);

    when(depart.getTripPattern()).thenReturn(tripPattern);
    when(dwell.getTripPattern()).thenReturn(tripPattern);

    PatternHop patternHop0 = new PatternHop(depart, dwell, stopDepart, stopDwell, 0);
    PatternHop patternHop1 = new PatternHop(dwell, arrive, stopDwell, stopArrive, 1);

    hops.add(patternHop0);
    hops.add(patternHop1);

    when(graph.getEdges()).thenReturn(hops);
    when(depart.getCoordinate()).thenReturn(new Coordinate(0, 0));
    when(dwell.getCoordinate()).thenReturn(new Coordinate(0, 0));
    when(arrive.getCoordinate()).thenReturn(new Coordinate(0, 0));
    when(routingRequest.getFrom()).thenReturn(new GenericLocation());
    when(routingRequest.getStartingTransitTripId()).thenReturn(agencyAndId);
    when(serviceDay.secondsSinceMidnight(anyInt())).thenReturn(9);

    patternHop0.setGeometry(geometry);
    tripPattern.add(tripTimes);
    graph.index = new GraphIndex(graph);

    coordinates = new Coordinate[3];
    coordinates[0] = new Coordinate(3.5, 1.0);
    coordinates[1] = new Coordinate(5.0, 1.0);
    coordinates[2] = new Coordinate(5.0, 5.0);

    coordinateSequence = coordinateSequenceFactory.create(coordinates);
    geometry = new LineString(coordinateSequence, geometryFactory);

    Vertex vertex = onBoardDepartServiceImpl.setupDepartOnBoard(routingContext);
    Edge edge = vertex.getOutgoing().toArray(new Edge[1])[0];

    assertEquals(vertex, edge.getFromVertex());
    assertEquals(dwell, edge.getToVertex());
    assertEquals("The right", edge.getDirection());
    assertEquals(geometry, edge.getGeometry());

    assertEquals(coordinates[0].x, vertex.getX(), 0.0);
    assertEquals(coordinates[0].y, vertex.getY(), 0.0);
  }
Exemplo n.º 11
0
  /**
   * Filters all input edges and returns all those as LineString geometries, that have at least one
   * end point within the time limits. If they have only one end point inside, then the sub-edge is
   * returned.
   *
   * @param maxTime the time limit in seconds that defines the size of the walkshed
   * @param allConnectingStateEdges all Edges that have been found to connect all states < maxTime
   * @param spt the ShortestPathTree generated for the pushpin drop point as origin
   * @param angleLimit the angle tolerance to detect roads with u-shapes, i.e. Pi/2 angles, in
   *     Radiant.
   * @param distanceTolerance in percent (e.g. 1.1 = 110%) for u-shape detection based on distance
   *     criteria
   * @param hasCar is travel mode by CAR?
   * @param performSpeedTest if true applies a test to each edge to check if the edge can be
   *     traversed in time. The test can detect u-shaped roads.
   * @return
   */
  ArrayList<LineString> getLinesAndSubEdgesWithinMaxTime(
      long maxTime,
      ArrayList<Edge> allConnectingStateEdges,
      ShortestPathTree spt,
      double angleLimit,
      double distanceTolerance,
      double userSpeed,
      boolean hasCar,
      boolean performSpeedTest) {

    LOG.debug("maximal userSpeed set to: " + userSpeed + " m/sec ");
    if (hasCar) {
      LOG.debug("travel mode is set to CAR, hence the given speed may be adjusted for each edge");
    }

    ArrayList<LineString> walkShedEdges = new ArrayList<LineString>();
    ArrayList<LineString> otherEdges = new ArrayList<LineString>();
    ArrayList<LineString> borderEdges = new ArrayList<LineString>();
    ArrayList<LineString> uShapes = new ArrayList<LineString>();
    int countEdgesOutside = 0;
    // -- determination of walkshed edges via edge states
    for (Iterator iterator = allConnectingStateEdges.iterator(); iterator.hasNext(); ) {
      Edge edge = (Edge) iterator.next();
      State sFrom = spt.getState(edge.getFromVertex());
      State sTo = spt.getState(edge.getToVertex());
      if ((sFrom != null) && (sTo != null)) {
        long fromTime = sFrom.getElapsedTimeSeconds();
        long toTime = sTo.getElapsedTimeSeconds();
        long dt = Math.abs(toTime - fromTime);
        Geometry edgeGeom = edge.getGeometry();
        if ((edgeGeom != null) && (edgeGeom instanceof LineString)) {
          LineString ls = (LineString) edgeGeom;
          // detect u-shape roads/crescents - they need to be treated separately
          boolean uShapeOrLonger =
              testForUshape(
                  edge,
                  maxTime,
                  fromTime,
                  toTime,
                  angleLimit,
                  distanceTolerance,
                  userSpeed,
                  hasCar,
                  performSpeedTest);
          if (uShapeOrLonger) {
            uShapes.add(ls);
          }

          // evaluate if an edge is completely within the time or only with one end
          if ((fromTime < maxTime) && (toTime < maxTime)) {
            // this one is within the time limit on both ends, however we need to do
            // a second test if we have a u-shaped road.
            if (uShapeOrLonger) {
              treatAndAddUshapeWithinTimeLimits(
                  maxTime, userSpeed, walkShedEdges, edge, fromTime, toTime, ls, hasCar);
            } else {
              walkShedEdges.add(ls);
            }
          } // end if:fromTime & toTime < maxTime
          else {
            // check if at least one end is inside, because then we need to
            // create the sub edge
            if ((fromTime < maxTime) || (toTime < maxTime)) {
              double lineDist = edge.getDistance();
              LineString inputLS = ls;
              double fraction = 1.0;
              if (fromTime < toTime) {
                double distanceToWalkInTimeMissing =
                    distanceToMoveInRemainingTime(
                        maxTime, fromTime, dt, userSpeed, edge, hasCar, uShapeOrLonger);
                fraction = (double) distanceToWalkInTimeMissing / (double) lineDist;
              } else {
                // toTime < fromTime : invert the edge direction
                inputLS = (LineString) ls.reverse();
                double distanceToWalkInTimeMissing =
                    distanceToMoveInRemainingTime(
                        maxTime, toTime, dt, userSpeed, edge, hasCar, uShapeOrLonger);
                fraction = (double) distanceToWalkInTimeMissing / (double) lineDist;
              }
              // get the subedge
              LineString subLine = this.getSubLineString(inputLS, fraction);
              borderEdges.add(subLine);
            } else {
              // this edge is completely outside - this should actually not happen
              // we will not do anything, just count
              countEdgesOutside++;
            }
          } // end else: fromTime & toTime < maxTime
        } // end if: edge instance of LineString
        else {
          // edge is not instance of LineString
          LOG.debug("edge not instance of LineString");
        }
      } // end if(sFrom && sTo != null) start Else
      else {
        // LOG.debug("could not retrieve state for edge-endpoint"); //for a 6min car ride, there can
        // be (too) many of such messages
        Geometry edgeGeom = edge.getGeometry();
        if ((edgeGeom != null) && (edgeGeom instanceof LineString)) {
          otherEdges.add((LineString) edgeGeom);
        }
      } // end else: sFrom && sTo != null
    } // end for loop over edges
    walkShedEdges.addAll(borderEdges);
    this.debugGeoms.addAll(uShapes);
    LOG.debug("number of detected u-shapes/crescents: " + uShapes.size());
    return walkShedEdges;
  }
Exemplo n.º 12
0
 private State getStateArriveBy(RaptorData data, ArrayList<RaptorState> states) {
   RoutingRequest options = states.get(0).getRequest();
   State state = new State(options.rctx.origin, options);
   for (int i = states.size() - 1; i >= 0; --i) {
     RaptorState cur = states.get(i);
     if (cur.walkPath != null) {
       GraphPath path = new GraphPath(cur.walkPath, false);
       for (ListIterator<Edge> it = path.edges.listIterator(path.edges.size());
           it.hasPrevious(); ) {
         Edge e = it.previous();
         State oldState = state;
         state = e.traverse(state);
         if (state == null) {
           e.traverse(oldState);
         }
       }
     } else {
       // so, cur is at this point at a transit stop; we have a route to alight from
       if (cur.getParent() == null || !cur.getParent().interlining) {
         for (Edge e : state.getVertex().getIncoming()) {
           if (e instanceof PreAlightEdge) {
             state = e.traverse(state);
           }
         }
         TransitBoardAlight alight =
             cur.getRoute().alights[cur.boardStopSequence - 1][cur.patternIndex];
         State oldState = state;
         state = alight.traverse(state);
         if (state == null) {
           state = alight.traverse(oldState);
         }
       }
       // now traverse the hops and dwells until we find the board we're looking for
       HOP:
       while (true) {
         for (Edge e : state.getVertex().getIncoming()) {
           if (e instanceof PatternDwell) {
             state = e.traverse(state);
           } else if (e instanceof PatternHop) {
             state = e.traverse(state);
             if (cur.interlining) {
               for (Edge e2 : state.getVertex().getIncoming()) {
                 RaptorState next = states.get(i - 1);
                 if (e2 instanceof PatternInterlineDwell) {
                   Stop fromStop = ((TransitVertex) e2.getFromVertex()).getStop();
                   Stop expectedStop = next.boardStop.stopVertex.getStop();
                   if (fromStop.equals(expectedStop)) {
                     State newState = e2.traverse(state);
                     if (newState == null) continue;
                     if (newState.getTripId() != next.tripId) continue;
                     state = newState;
                     break HOP;
                   }
                 }
               }
             } else {
               for (Edge e2 : state.getVertex().getIncoming()) {
                 if (e2 instanceof TransitBoardAlight) {
                   for (Edge e3 : e2.getFromVertex().getIncoming()) {
                     if (e3 instanceof PreBoardEdge) {
                       if (data.raptorStopsForStopId.get(
                               ((TransitStop) e3.getFromVertex()).getStopId())
                           == cur.stop) {
                         state = e2.traverse(state);
                         state = e3.traverse(state);
                         break HOP;
                       }
                     }
                   }
                 }
               }
             }
           }
         }
       }
     }
   }
   return state;
 }
Exemplo n.º 13
0
  /**
   * Get polygons covering the components of the graph. The largest component (in terms of number of
   * nodes) will not overlap any other components (it will have holes); the others may overlap each
   * other.
   *
   * @param dateTime
   */
  public static List<Geometry> getComponentPolygons(
      Graph graph, RoutingRequest options, long time) {
    DisjointSet<Vertex> components = getConnectedComponents(graph);

    LinkedListMultimap<Integer, Coordinate> componentCoordinates = LinkedListMultimap.create();
    options.setDummyRoutingContext(graph);
    for (Vertex v : graph.getVertices()) {
      for (Edge e : v.getOutgoing()) {
        State s0 = new State(v, time, options);
        State s1 = e.traverse(s0);
        if (s1 != null) {
          Integer component = components.find(e.getFromVertex());
          Geometry geometry = s1.getBackEdge().getGeometry();
          if (geometry != null) {
            List<Coordinate> coordinates =
                new ArrayList<Coordinate>(Arrays.asList(geometry.getCoordinates()));
            for (int i = 0; i < coordinates.size(); ++i) {
              Coordinate coordinate = new Coordinate(coordinates.get(i));
              coordinate.x = Math.round(coordinate.x * PRECISION) / PRECISION;
              coordinate.y = Math.round(coordinate.y * PRECISION) / PRECISION;
              coordinates.set(i, coordinate);
            }
            componentCoordinates.putAll(component, coordinates);
          }
        }
      }
    }

    // generate convex hull of each component
    List<Geometry> geoms = new ArrayList<Geometry>();
    int mainComponentSize = 0;
    int mainComponentIndex = -1;
    int component = 0;
    for (Integer key : componentCoordinates.keySet()) {
      List<Coordinate> coords = componentCoordinates.get(key);
      Coordinate[] coordArray = new Coordinate[coords.size()];
      ConvexHull hull =
          new ConvexHull(coords.toArray(coordArray), GeometryUtils.getGeometryFactory());
      Geometry geom = hull.getConvexHull();
      // buffer components which are mere lines so that they do not disappear.
      if (geom instanceof LineString) {
        geom = geom.buffer(0.01); // ~10 meters
      } else if (geom instanceof Point) {
        geom = geom.buffer(0.05); // ~50 meters, so that it shows up
      }

      geoms.add(geom);
      if (mainComponentSize < coordArray.length) {
        mainComponentIndex = component;
        mainComponentSize = coordArray.length;
      }
      ++component;
    }

    // subtract small components out of main component
    // (small components are permitted to overlap each other)
    Geometry mainComponent = geoms.get(mainComponentIndex);
    for (int i = 0; i < geoms.size(); ++i) {
      Geometry geom = geoms.get(i);
      if (i == mainComponentIndex) {
        continue;
      }
      mainComponent = mainComponent.difference(geom);
    }
    geoms.set(mainComponentIndex, mainComponent);

    return geoms;
  }
Exemplo n.º 14
0
 protected boolean carsCanTraverse(Edge edge) {
   // should be done with a method on edge (canTraverse already exists on turnEdge)
   State s0 = new State(edge.getFromVertex(), traverseOptions);
   State s1 = edge.traverse(s0);
   return s1 != null;
 }