content: package aimax.osm.routing;
  
  import aimax.osm.data.MapWayFilter;
  import aimax.osm.data.entities.MapNode;
  import aimax.osm.data.entities.MapWay;
  import aimax.osm.data.entities.WayRef;
  
  import java.util.ArrayList;
  import java.util.List;
  import java.util.function.Function;
  
  /**
   * Contains useful functions for OSM routing problems.
   *
   * @author Ruediger Lunde
   */
  public class OsmFunctions {
  
      public enum OneWayMode {IGNORE, TRAVEL_FORWARD, TRAVEL_BACKWARDS}
  
      public static Function<MapNode, List<OsmMoveAction>> createActionFunction
              (MapWayFilter filter, OneWayMode oneWayMode, MapNode goal) {
          return new OsmActionsFunction(filter, oneWayMode, goal);
      }
  
      public static MapNode getResult(MapNode state, OsmMoveAction action) {
          return action.getTo();
      }
  
      public static double getDistanceStepCosts(MapNode state, OsmMoveAction action, MapNode statePrimed) {
          return action.getTravelDistance();
      }
  
  
      /**
       * Generates {@link aimax.osm.routing.OsmMoveAction}s for states. If a goal is
       * specified, all generated actions lead to road crossings, road ends, or the
       * specified goal. Otherwise, they lead to directly linked neighbor nodes.
       */
      public static class OsmActionsFunction implements Function<MapNode, List<OsmMoveAction>> {
  
          protected MapWayFilter filter;
          private OneWayMode oneWayMode;
          /**
           * Goal node, possibly null. If a goal is specified, travel actions will
           * include paths with size greater one.
           */
          protected MapNode goal;
  
          public OsmActionsFunction(MapWayFilter filter, OneWayMode oneWayMode, MapNode goal) {
              this.filter = filter;
              this.oneWayMode = oneWayMode;
              this.goal = goal;
          }
  
          @Override
          public List<OsmMoveAction> apply(MapNode state) {
              List<OsmMoveAction> result = new ArrayList<>();
              for (WayRef wref : state.getWayRefs()) {
                  if (filter == null || filter.isAccepted(wref.getWay())) {
                      MapWay way = wref.getWay();
                      int nodeIdx = wref.getNodeIdx();
                      List<MapNode> wayNodes = way.getNodes();
                      MapNode to;
                      if (oneWayMode != OneWayMode.TRAVEL_BACKWARDS || !way.isOneway())
                          for (int idx = nodeIdx + 1; idx < wayNodes.size(); idx++) {
                              to = wayNodes.get(idx);
                              if (goal == null || goal == to
                                      || to.getWayRefs().size() > 1
                                      || idx == wayNodes.size() - 1) {
                                  result.add(new OsmMoveAction(way, nodeIdx, idx));
                                  break;
                              }
                          }
                      if (oneWayMode != OneWayMode.TRAVEL_FORWARD || !way.isOneway()) {
                          for (int idx = nodeIdx - 1; idx >= 0; idx--) {
                              to = wayNodes.get(idx);
                              if (goal == null || goal == to
                                      || to.getWayRefs().size() > 1 || idx == 0) {
                                  result.add(new OsmMoveAction(way, nodeIdx, idx));
                                  break;
                              }
                          }
                      }
                  }
              }
              return result;
          }
      }
  }
  