/** Build the weight table, parallelized according to the number of processors */
  public void buildTable() {
    ArrayList<TransitStop> stopVertices;

    LOG.debug("Number of vertices: " + g.getVertices().size());
    stopVertices = new ArrayList<TransitStop>();
    for (Vertex gv : g.getVertices())
      if (gv instanceof TransitStop) stopVertices.add((TransitStop) gv);
    int nStops = stopVertices.size();

    stopIndices = new IdentityHashMap<Vertex, Integer>(nStops);
    for (int i = 0; i < nStops; i++) stopIndices.put(stopVertices.get(i), i);
    LOG.debug("Number of stops: " + nStops);

    table = new float[nStops][nStops];
    for (float[] row : table) Arrays.fill(row, Float.POSITIVE_INFINITY);

    LOG.debug("Performing search at each transit stop.");

    int nThreads = Runtime.getRuntime().availableProcessors();
    LOG.debug("number of threads: " + nThreads);
    ArrayBlockingQueue<Runnable> taskQueue = new ArrayBlockingQueue<Runnable>(nStops);
    ThreadPoolExecutor threadPool =
        new ThreadPoolExecutor(nThreads, nThreads, 10, TimeUnit.SECONDS, taskQueue);
    GenericObjectPool heapPool =
        new GenericObjectPool(new PoolableBinHeapFactory<State>(g.getVertices().size()), nThreads);

    // make one heap and recycle it
    TraverseOptions options = new TraverseOptions();
    options.speed = maxWalkSpeed;
    final double MAX_WEIGHT = 60 * 60 * options.walkReluctance;
    final double OPTIMISTIC_BOARD_COST = options.boardCost;

    // create a task for each transit stop in the graph
    ArrayList<Callable<Void>> tasks = new ArrayList<Callable<Void>>();
    for (TransitStop origin : stopVertices) {
      SPTComputer task =
          new SPTComputer(heapPool, options, MAX_WEIGHT, OPTIMISTIC_BOARD_COST, origin);
      tasks.add(task);
    }
    try {
      // invoke all of tasks.
      threadPool.invokeAll(tasks);
      threadPool.shutdown();
    } catch (InterruptedException e) {
      throw new RuntimeException(e);
    }
    floyd();
  }
    /**
     * The safest bike lane should have a safety weight no lower than the time weight of a flat
     * street. This method divides the safety lengths by the length ratio of the safest street,
     * ensuring this property.
     *
     * @param graph
     */
    private void applyBikeSafetyFactor(Graph graph) {
      _log.info(
          GraphBuilderAnnotation.register(
              graph,
              Variety.GRAPHWIDE,
              "Multiplying all bike safety values by " + (1 / bestBikeSafety)));
      HashSet<Edge> seenEdges = new HashSet<Edge>();
      for (Vertex vertex : graph.getVertices()) {
        for (Edge e : vertex.getOutgoing()) {
          if (!(e instanceof PlainStreetEdge)) {
            continue;
          }
          PlainStreetEdge pse = (PlainStreetEdge) e;

          if (!seenEdges.contains(e)) {
            seenEdges.add(e);
            pse.setBicycleSafetyEffectiveLength(
                pse.getBicycleSafetyEffectiveLength() / bestBikeSafety);
          }
        }
        for (Edge e : vertex.getIncoming()) {
          if (!(e instanceof PlainStreetEdge)) {
            continue;
          }
          PlainStreetEdge pse = (PlainStreetEdge) e;

          if (!seenEdges.contains(e)) {
            seenEdges.add(e);
            pse.setBicycleSafetyEffectiveLength(
                pse.getBicycleSafetyEffectiveLength() / bestBikeSafety);
          }
        }
      }
    }
 public synchronized void initIndexes() {
   if (vertexIndex != null) {
     return;
   }
   graphService.setLoadLevel(LoadLevel.DEBUG);
   Graph graph = graphService.getGraph();
   vertexIndex = new STRtree();
   edgeIndex = new STRtree();
   for (Vertex v : graph.getVertices()) {
     Envelope vertexEnvelope = new Envelope(v.getCoordinate());
     vertexIndex.insert(vertexEnvelope, v);
     for (Edge e : v.getOutgoing()) {
       Envelope envelope;
       Geometry geometry = e.getGeometry();
       if (geometry == null) {
         envelope = vertexEnvelope;
       } else {
         envelope = geometry.getEnvelopeInternal();
       }
       edgeIndex.insert(envelope, e);
     }
   }
   vertexIndex.build();
   edgeIndex.build();
 }
  @BeforeClass
  public static void setUp() throws Exception {

    context = GtfsLibrary.readGtfs(new File(ConstantsForTests.FAKE_GTFS));
    graph = new Graph();

    GTFSPatternHopFactory factory = new GTFSPatternHopFactory(context);
    factory.run(graph);
    graph.putService(
        CalendarServiceData.class, GtfsLibrary.createCalendarServiceData(context.getDao()));

    patternIndex = new HashMap<AgencyAndId, TripPattern>();
    for (TransitStopDepart tsd : filter(graph.getVertices(), TransitStopDepart.class)) {
      for (TransitBoardAlight tba : filter(tsd.getOutgoing(), TransitBoardAlight.class)) {
        if (!tba.isBoarding()) continue;
        TripPattern pattern = tba.getPattern();
        for (Trip trip : pattern.getTrips()) {
          patternIndex.put(trip.getId(), pattern);
        }
      }
    }

    pattern = patternIndex.get(new AgencyAndId("agency", "1.1"));
    timetable = pattern.scheduledTimetable;
  }
  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;
  }
 /*
  * Iterate through all vertices and their (outgoing) edges. If they are of 'interesting' types, add them to the corresponding spatial index.
  */
 public synchronized void buildSpatialIndex() {
   vertexIndex = new STRtree();
   edgeIndex = new STRtree();
   Envelope env;
   // int xminx, xmax, ymin, ymax;
   for (Vertex v : graph.getVertices()) {
     Coordinate c = v.getCoordinate();
     env = new Envelope(c);
     vertexIndex.insert(env, v);
     for (Edge e : v.getOutgoing()) {
       if (e.getGeometry() == null) continue;
       if (e instanceof PatternEdge || e instanceof StreetTransitLink || e instanceof StreetEdge) {
         env = e.getGeometry().getEnvelopeInternal();
         edgeIndex.insert(env, e);
       }
     }
   }
   vertexIndex.build();
   edgeIndex.build();
 }
  /**
   * 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;
  }
  @Override
  public void buildGraph(Graph graph, HashMap<Class<?>, Object> extra) {
    final Parser parser[] = new Parser[] {new Parser()};
    GeometryFactory geometryFactory = GeometryUtils.getGeometryFactory();
    EarliestArrivalSPTService earliestArrivalSPTService = new EarliestArrivalSPTService();
    earliestArrivalSPTService.maxDuration = (maxDuration);

    for (TransitStop ts : Iterables.filter(graph.getVertices(), TransitStop.class)) {
      // Only link street linkable stops
      if (!ts.isStreetLinkable()) continue;
      LOG.trace("linking stop '{}' {}", ts.getStop(), ts);

      // Determine the set of pathway/transfer destinations
      Set<TransitStop> pathwayDestinations = new HashSet<TransitStop>();
      for (Edge e : ts.getOutgoing()) {
        if (e instanceof PathwayEdge || e instanceof SimpleTransfer) {
          if (e.getToVertex() instanceof TransitStop) {
            TransitStop to = (TransitStop) e.getToVertex();
            pathwayDestinations.add(to);
          }
        }
      }

      int n = 0;
      RoutingRequest routingRequest = new RoutingRequest(TraverseMode.WALK);
      routingRequest.clampInitialWait = (0L);
      routingRequest.setRoutingContext(graph, ts, null);
      routingRequest.rctx.pathParsers = parser;
      ShortestPathTree spt = earliestArrivalSPTService.getShortestPathTree(routingRequest);

      if (spt != null) {
        for (State state : spt.getAllStates()) {
          Vertex vertex = state.getVertex();
          if (ts == vertex) continue;

          if (vertex instanceof TransitStop) {
            TransitStop other = (TransitStop) vertex;
            if (!other.isStreetLinkable()) continue;
            if (pathwayDestinations.contains(other)) {
              LOG.trace("Skipping '{}', {}, already connected.", other.getStop(), other);
              continue;
            }
            double distance = 0.0;
            GraphPath graphPath = new GraphPath(state, false);
            CoordinateArrayListSequence coordinates = new CoordinateArrayListSequence();

            for (Edge edge : graphPath.edges) {
              if (edge instanceof StreetEdge) {
                LineString geometry = edge.getGeometry();

                if (geometry != null) {
                  if (coordinates.size() == 0) {
                    coordinates.extend(geometry.getCoordinates());
                  } else {
                    coordinates.extend(geometry.getCoordinates(), 1);
                  }
                }

                distance += edge.getDistance();
              }
            }

            if (coordinates.size() < 2) { // Otherwise the walk step generator breaks.
              ArrayList<Coordinate> coordinateList = new ArrayList<Coordinate>(2);
              coordinateList.add(graphPath.states.get(1).getVertex().getCoordinate());
              State lastState = graphPath.states.getLast().getBackState();
              coordinateList.add(lastState.getVertex().getCoordinate());
              coordinates = new CoordinateArrayListSequence(coordinateList);
            }

            LineString geometry =
                geometryFactory.createLineString(
                    new PackedCoordinateSequence.Double(coordinates.toCoordinateArray()));
            LOG.trace("  to stop: '{}' {} ({}m) [{}]", other.getStop(), other, distance, geometry);
            new SimpleTransfer(ts, other, distance, geometry);
            n++;
          }
        }
      }

      LOG.trace("linked to {} others.", n);
      if (n == 0) {
        LOG.warn(graph.addBuilderAnnotation(new StopNotLinkedForTransfers(ts)));
      }
    }
  }