public Map<RouteDirectionStopKey, String> getStopMatches(
      Map<NBRoute, Route> routeMatches,
      Map<NBStop, List<Stop>> potentialStopMatches,
      GtfsRelationalDao dao) {

    Map<RouteDirectionStopKey, String> stopIdMappings =
        new HashMap<RouteDirectionStopKey, String>();

    for (Map.Entry<NBRoute, Route> entry : routeMatches.entrySet()) {

      NBRoute nbRoute = entry.getKey();
      Route gtfsRoute = entry.getValue();

      Set<List<Stop>> stopSequences = getStopSequencesForRoute(dao, gtfsRoute);

      List<Map<Stop, Integer>> stopSequenceIndices = new ArrayList<Map<Stop, Integer>>();
      for (List<Stop> stopSequence : stopSequences) {
        Map<Stop, Integer> index = new HashMap<Stop, Integer>();
        for (int i = 0; i < stopSequence.size(); ++i) {
          index.put(stopSequence.get(i), i);
        }
        stopSequenceIndices.add(index);
      }

      for (NBDirection direction : nbRoute.getDirections()) {

        List<Match> matches = new ArrayList<Match>();
        for (NBStop fromStop : direction.getStops()) {
          List<Stop> toStops = potentialStopMatches.get(fromStop);
          if (toStops.isEmpty()) {
            continue;
          }
          Match m = new Match(fromStop, toStops);
          matches.add(m);
        }

        Min<Assignment> m = new Min<Assignment>();
        Assignment assignment = new Assignment(direction.getStops(), stopSequenceIndices);
        matches = applyDirectMatchesToAssignment(matches, assignment);
        recursivelyBuildAndScoreAssignment(matches, 0, assignment, m);
        Assignment bestAssignment = m.getMinElement();

        if (bestAssignment == null) {
          throw new IllegalStateException();
        }

        for (NBStop stop : direction.getStops()) {
          Stop gtfsStop = bestAssignment.getGtfsStopForNBStop(stop);
          if (gtfsStop == null) {
            continue;
          }
          String stopId = gtfsStop.getId().getId();
          RouteDirectionStopKey key =
              new RouteDirectionStopKey(nbRoute.getTag(), direction.getTag(), stop.getTag());
          stopIdMappings.put(key, stopId);
        }
      }
    }
    return stopIdMappings;
  }
 public void applyMatch(Match match, int index) {
   NBStop from = match.from;
   Stop to = match.to.get(index);
   if (from.getTag() == null) {
     throw new IllegalStateException();
   }
   _stops.put(from, to);
 }
 /** ** Private Methods ** */
 private Map<String, NBStop> getStopsByTag(List<NBRoute> routes) {
   Map<String, NBStop> stopsByTag = new HashMap<String, NBStop>();
   for (NBRoute route : routes) {
     for (NBStop stop : route.getStops()) {
       stopsByTag.put(stop.getTag(), stop);
     }
   }
   return stopsByTag;
 }
  @SuppressWarnings("unchecked")
  public Map<NBStop, List<Stop>> getPotentialStopMatches(
      List<NBRoute> nbRoutes, Collection<Stop> gtfsStops) {

    Map<String, NBStop> nbStopsByTag = getStopsByTag(nbRoutes);

    STRtree tree = new STRtree(gtfsStops.size());
    for (Stop stop : gtfsStops) {
      tree.insert(new Envelope(new Coordinate(stop.getLon(), stop.getLat())), stop);
    }
    tree.build();

    Map<NBStop, List<Stop>> potentialMatches = new HashMap<NBStop, List<Stop>>();
    int stopsWithNoMatches = 0;
    for (NBStop nbStop : nbStopsByTag.values()) {
      CoordinateBounds b =
          SphericalGeometryLibrary.bounds(
              nbStop.getLat(), nbStop.getLon(), _stopMatchingDistanceThreshold);
      Envelope env = new Envelope(b.getMinLon(), b.getMaxLon(), b.getMinLat(), b.getMaxLat());
      List<Stop> stopsInEnvelope = tree.query(env);
      if (stopsInEnvelope.isEmpty()) {
        _log.warn(
            "stop with no match: tag="
                + nbStop.getTag()
                + " lat="
                + nbStop.getLat()
                + " lon="
                + nbStop.getLon());
        stopsWithNoMatches++;
      }
      potentialMatches.put(nbStop, stopsInEnvelope);
    }

    if (stopsWithNoMatches > 0) {
      _log.warn("stops without matches: " + stopsWithNoMatches + "/" + nbStopsByTag.size());
    }

    return potentialMatches;
  }
 @Override
 public String toString() {
   return from.getTag() + " " + to;
 }