Android Open Source - Mapyst Route Finder






From Project

Back to project page Mapyst.

License

The source code is released under:

Apache License

If you think the Android project Mapyst listed in this page is inappropriate, such as containing malicious code/tools or violating the copyright, please email info at java2s dot com, thanks.

Java Source Code

/*
 * Copyright (C) 2013 Mapyst/* w  w  w  .ja  v a2  s  .c o m*/
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.mapyst.route;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map.Entry;

import com.mapyst.campus.Building;
import com.mapyst.campus.Campus;

/*
 * Class: RouteFinder
 * Prepares to find the shortest path based on preferences and then executes to find the route
 * NOTE: Make sure you do NOT load any files twice as this will ruin the path finding
 */
public class RouteFinder {

  //the hashmap of the loaded nodes
  public HashMap<WaypointID, GraphNode<Waypoint2D>> loadedNodes;
  
  private Campus campus;

  private static final int ELEVATOR_CONSTANT = 50000; //50 seconds for getting on an elevator
  
  //Graph cost modification constants
  private static final boolean NO_REMOVE = false;
  private static final boolean REMOVE_ARC = true;
  private static final int MODIFIER_CONSTANT = 4;
  private static final double THRESHOLD_MULTIPLIER = 1.5;

  public RouteFinder(Campus campus) {
    this.campus = campus;
  }

  /*
   * Function: makeRoute
   * Calculates the most appropriate route using Dijkstra's algorithm after parsing the input
   * and loading the necessary files.
   *
   * Parameters:
   * start - user input for the starting node
   * end - user input for the ending node
   * preferences - the preferences that are selected by the user in the settings menu (use the Constants.Preferences in the array)
   * 
   * Returns:
   * Whether the route was found successfully.
   */
  public Route makeRoute(InterpretResult startResult, InterpretResult endResult, RoutePreferences prefs) {
    
    //loads the necessary graph data
    loadedNodes = new HashMap<WaypointID, GraphNode<Waypoint2D>>();
    loadGraphFiles(startResult, endResult);
    
    fixElevators();

    //modify HashMap with preferences
    for (Entry<WaypointID, GraphNode<Waypoint2D>> e: loadedNodes.entrySet()) {
      ArrayList<Arc<Waypoint2D>> a = e.getValue().arcList; 
      for (int i = 0; i < a.size(); i++) {
        if (modifyCost(a.get(i), prefs, e.getValue().arcList))
          a.remove(i--);
      }
    }
    
    //computes the shortest path
    ArrayList<WaypointID> dijkstras = ShortestPath.doDijkstras(loadedNodes, startResult.getPointID(), endResult.getWaypoints());
    
    //unmodify the time
    unmodifyCost(prefs, dijkstras);
    
    //formats the shortest path into a list of graph nodes
    ArrayList<GraphNode<Waypoint2D>> dijkstrasNodes = new ArrayList<GraphNode<Waypoint2D>>();
        for (WaypointID id : dijkstras) { //for each point in the path
            GraphNode<Waypoint2D> tempNode = loadedNodes.get(id);
            dijkstrasNodes.add(tempNode);
        }
    
    //initializes arrays that will be sent to the route object
    Waypoint2D[] points = new Waypoint2D[dijkstras.size()];
    int[] terrains = new int[dijkstras.size()-1];
    int[] times = new int[dijkstras.size()-1];

    //fills those arrays with the lists of points, terrains, and times for calculation of the route
        GraphNode<Waypoint2D> currentNode = loadedNodes.get(startResult.getPointID());
    for (int i = 0; i < dijkstrasNodes.size()-1; i++) {
      currentNode = dijkstrasNodes.get(i);
      for (Arc<Waypoint2D> arc: currentNode.arcList) {
        if (arc.getConnectedNode().data.getId().equals(dijkstrasNodes.get(i+1).data.getId())) {
          
          terrains[i] = arc.getTerrain();
          times[i] = arc.getDistance();
          points[i] = currentNode.data;
          break;
        }
      }
    }
    points[points.length-1] = dijkstrasNodes.get(dijkstrasNodes.size()-1).data;

    //computes the route
    Route route = new Route(points, terrains, times, startResult.getText(), endResult.getText(), campus);

    loadedNodes = null; //FREE THE MASSIVE AMOUNT OF MEMORY
    return route;  
  }

  private void fixElevators() {
    int roomIndex = -10;//negative room indices indicate points that were added to allow us 
              //to modify times to be appropriate for elevators 
    ArrayList<GraphNode<Waypoint2D>> nodesToAdd = new ArrayList<GraphNode<Waypoint2D>>();
    for (GraphNode<Waypoint2D> currentNode : loadedNodes.values()) {
      if (isRelevant(currentNode)) {
        nodesToAdd.add(elevatorUpdate(currentNode, roomIndex));
        roomIndex--;
      }
    }
    for (GraphNode<Waypoint2D> node : nodesToAdd)
      loadedNodes.put(node.data.getId(), node);
  }

  private boolean isRelevant(GraphNode<Waypoint2D> currentNode) {
    for (Arc<Waypoint2D> arc: currentNode.arcList) {
      if (arc.getTerrain() == Arc.Terrains.ELEVATOR) //if you found an elevator then use this
        return true;
    }
    return false;
  }

  private GraphNode<Waypoint2D> elevatorUpdate(GraphNode<Waypoint2D> currentNode, int roomIndex) {
    WaypointID currentID = currentNode.data.getId();
    LatLngPoint point = currentNode.data.getPoint().copy();
    Waypoint2D point2D = new Waypoint2D(point, currentID.getBuildingIndex(), currentID.getFloorIndex(), roomIndex, "fake");
    GraphNode<Waypoint2D> newNode = new GraphNode<Waypoint2D>(point2D);

    int size = currentNode.arcList.size();
    for (int i = 0; i < size; i++) {
      Arc<Waypoint2D> arc = currentNode.arcList.get(i);
      //if its an elevator terrain leave it
      if (arc.getTerrain() == Arc.Terrains.ELEVATOR) {
        newNode.addArc(arc.getConnectedNode(), arc.getDistance(), arc.getTerrain());
        currentNode.removeArc(arc.getConnectedNode());
        size--;
        i--;
      }
    }
    
    //add the invisible edge
    currentNode.addArc(newNode, ELEVATOR_CONSTANT, Arc.Terrains.INVISIBLE);

    return newNode;
  }
  
  //undo the cost changes that modifyCost did to the routeNodes hashMap
  //WARNING: does not undo ALL changes made (only those changes that were included in the final path)
  private void unmodifyCost(RoutePreferences prefs, ArrayList<WaypointID> dijkstras) {
    for(WaypointID key: dijkstras) {
      for (Arc<Waypoint2D> a: loadedNodes.get(key).arcList) {
        int currDistance = a.getDistance();
        //change the arc if that preference is chosen
        if (prefs.outside) {
          if (a.getTerrain() == Arc.Terrains.INSIDE || 
              a.getTerrain() == Arc.Terrains.ELEVATOR || 
              a.getTerrain() == Arc.Terrains.INSIDE_STAIRS ||
              a.getTerrain() == Arc.Terrains.CROWDED_INSIDE ||
              a.getTerrain() == Arc.Terrains.RESTRICTED_ACCESS)
            a.setDistance(currDistance / MODIFIER_CONSTANT); 
        }
        if (prefs.inside) {
          if (a.getTerrain() == Arc.Terrains.OUTSIDE || 
              a.getTerrain() == Arc.Terrains.OUTSIDE_STAIRS ||
              a.getTerrain() == Arc.Terrains.CROWDED_OUTSIDE)
            a.setDistance(currDistance / MODIFIER_CONSTANT); 
        }
        if (prefs.elevators) {
          if (a.getTerrain() == Arc.Terrains.INSIDE_STAIRS || 
              a.getTerrain() == Arc.Terrains.OUTSIDE_STAIRS)
            a.setDistance(currDistance / MODIFIER_CONSTANT);
        }
        if (prefs.stairs) {
          if (a.getTerrain() == Arc.Terrains.ELEVATOR)
            a.setDistance(currDistance * MODIFIER_CONSTANT);
        }
      }
    }
  }

  private boolean modifyCost(Arc<Waypoint2D> a, RoutePreferences prefs, ArrayList<Arc<Waypoint2D>> arcList) {
    int currDistance = a.getDistance();
    //change the arc if that preference is chosen
    if (prefs.outside) {
      if (a.getTerrain() == Arc.Terrains.INSIDE || 
          a.getTerrain() == Arc.Terrains.ELEVATOR || 
          a.getTerrain() == Arc.Terrains.INSIDE_STAIRS ||
          a.getTerrain() == Arc.Terrains.CROWDED_INSIDE ||
          a.getTerrain() == Arc.Terrains.RESTRICTED_ACCESS)
        a.setDistance(currDistance * MODIFIER_CONSTANT); 
    }
    if (prefs.inside) {
      if (a.getTerrain() == Arc.Terrains.OUTSIDE || 
          a.getTerrain() == Arc.Terrains.OUTSIDE_STAIRS ||
          a.getTerrain() == Arc.Terrains.CROWDED_OUTSIDE)
        a.setDistance(currDistance * MODIFIER_CONSTANT); 
    }
    if (prefs.elevators) {
      if (a.getTerrain() == Arc.Terrains.INSIDE_STAIRS || 
          a.getTerrain() == Arc.Terrains.OUTSIDE_STAIRS)
        //actually remove the connections of the stairs
        a.setDistance(currDistance * MODIFIER_CONSTANT);
    }
    if (prefs.stairs) {
      if (a.getTerrain() == Arc.Terrains.ELEVATOR)
        a.setDistance(currDistance * MODIFIER_CONSTANT);
    }
    if (prefs.hand) {
      if (a.getTerrain() == Arc.Terrains.INSIDE_STAIRS || 
          a.getTerrain() == Arc.Terrains.OUTSIDE_STAIRS)
        //actually remove the connections of the stairs
        return REMOVE_ARC;
    }
    return NO_REMOVE;
  }

  private void loadGraphFiles(InterpretResult startResult, InterpretResult endResult) {
    HashSet<String> floorGraphFiles = new HashSet<String>();
    HashSet<String> buildingGraphFiles = new HashSet<String>();
    
    WaypointID start = startResult.getPointID();
    WaypointID end = getFarthestEnd(startResult, endResult);
    
    //distance threshold = 1.5 distance between start and end buildings
    Building startBuilding = campus.buildings[start.getBuildingIndex()];
    Building endBuilding = campus.buildings[end.getBuildingIndex()];
    double distanceThreshold = DistanceCalculator.buildingDistance(startBuilding, endBuilding) * THRESHOLD_MULTIPLIER;
    
    //loops through buildings
    for (int buildingIndex = 0; buildingIndex < campus.buildings.length; buildingIndex++) {
      //does not load if distance between the start to the current to the end building is more than the threshold defined above
      Building building = campus.buildings[buildingIndex];
      double distance = DistanceCalculator.buildingDistance(startBuilding, building, endBuilding);
      if (distance <= distanceThreshold || campus.buildingIsOutside(start.getBuildingIndex()) || campus.buildingIsOutside(end.getBuildingIndex())) {
        boolean onlyLoadConnectionFloors = !(building == startBuilding || building == endBuilding);
        queueBuilding(buildingIndex, floorGraphFiles, buildingGraphFiles, onlyLoadConnectionFloors);
      }
    }

    queueOutsideBuildings(floorGraphFiles, buildingGraphFiles);

//    printFiles(floorGraphFiles);
//    printFiles(buildingGraphFiles);
    
    //loads the graph files determined above
    for (String fileName : floorGraphFiles) {
      DataParser.parseFile(fileName, loadedNodes, Campus.fileHandler);
    }
    for (String fileName : buildingGraphFiles) {
      DataParser.parseFile(fileName, loadedNodes, Campus.fileHandler);
    }
    DataParser.parseFile("building_connections.ncmg", loadedNodes, Campus.fileHandler);
  }
  
//  private void printFiles(HashSet<String> graphFiles) {
//    System.out.println("GRAPH FILES:");
//    for (String fileString : graphFiles) {
//      System.out.println("loaded file: " + fileString);
//    }
//  }
  
  private void queueOutsideBuildings(HashSet<String> floorGraphFiles, HashSet<String> buildingGraphFiles) {
    Building[] outsideBuildings = campus.getOutsideBuildings();
        for (Building outsideBuilding : outsideBuildings) {
            for (int j = 0; j < outsideBuilding.floors.length; j++) {
                floorGraphFiles.add(campus.getFloorFile(outsideBuilding, j, "ncmg"));
            }
            buildingGraphFiles.add(campus.getBuildingFile(outsideBuilding, "ncmg"));
        }
  }
  
  //a connection floor is one that is necessary to allow the shortest path algorithm to search all possible routes
  //this is defined by a floor's load_if_close variable
  private void queueBuilding(int building, HashSet<String> floorGraphFiles, HashSet<String> buildingGraphFiles, boolean onlyLoadConnectionFloors) {
    for (int floor = 0; floor < campus.buildings[building].floors.length; floor++) {
      if (!onlyLoadConnectionFloors || (campus.getFloor(building, floor).load_if_close))
        floorGraphFiles.add(campus.getFloorFile(building, floor, "ncmg"));
    }
    buildingGraphFiles.add(campus.getBuildingFile(building, "ncmg"));
  }
  
  private WaypointID getFarthestEnd(InterpretResult startResult, InterpretResult endResult) {
    WaypointID farthestEnd = endResult.getWaypoints().iterator().next();
    double distanceToFarthest = 0;
    
    Building startBuilding = campus.buildings[startResult.getPointID().getBuildingIndex()];
    
    for (WaypointID pointID: endResult.getWaypoints()) {
      Building endBuilding = campus.buildings[pointID.getBuildingIndex()];
      double distance = DistanceCalculator.buildingDistance(startBuilding, endBuilding); 
      if (distance > distanceToFarthest) {
        distanceToFarthest = distance;
        farthestEnd = pointID;
      }
    }
    
    return farthestEnd;
  }
  
  public static void loadOutsideBuildings(Campus campus, HashMap<WaypointID, GraphNode<Waypoint2D>> nodes) {
    Building[] outsideBuildings = campus.getOutsideBuildings();
        for (Building outsideBuilding : outsideBuildings) {
            loadBuilding(outsideBuilding, campus, nodes);
        }
  }
  
  public static void loadBuilding(Building building, Campus campus, HashMap<WaypointID, GraphNode<Waypoint2D>> nodes) {
    for (int j = 0; j < building.floors.length; j++) {
      DataParser.parseFile(campus.getFloorFile(building, j, "ncmg"), nodes, Campus.fileHandler);
    }
    DataParser.parseFile(campus.getBuildingFile(building, "ncmg"), nodes, Campus.fileHandler);
  }

  public Waypoint2D getWaypoint2D(WaypointID pointID) {
    loadedNodes = new HashMap<WaypointID, GraphNode<Waypoint2D>>();
    
    Building building = campus.buildings[pointID.getBuildingIndex()];
    loadBuilding(building, campus, loadedNodes);
    
    GraphNode<Waypoint2D> node = loadedNodes.get(pointID);
    return node.data;
  }
  
  public static void printHashMap(HashMap<WaypointID, GraphNode<Waypoint2D>> nodes) {
    System.out.println("printing hashmap size: " + nodes.size());
    for (Entry<WaypointID, GraphNode<Waypoint2D>> e: nodes.entrySet()) {
      System.out.println("node: " + e.getValue().data);
      System.out.println("arcs: from: " + e.getKey().toString());
      for (Arc<Waypoint2D> arc : e.getValue().arcList) {
        System.out.println("to: " + arc.getConnectedNode().data.getId() + " time: " + arc.getDistance() + " terrain: " + arc.getTerrain());
      }
    }
  }
  
  public static void printHashMap(HashMap<WaypointID, GraphNode<Waypoint2D>> nodes, int building, int floor) {
    System.out.println("printing hashmap size: " + nodes.size());
    for (Entry<WaypointID, GraphNode<Waypoint2D>> e: nodes.entrySet()) {
      if (e.getKey().getBuildingIndex() == building && e.getKey().getFloorIndex() == floor) {
        System.out.println("node: " + e.getValue().data);
        System.out.println("arcs: from: " + e.getKey().toString());
        for (Arc<Waypoint2D> arc : e.getValue().arcList) {
          System.out.println("to: " + arc.getConnectedNode().data.getId() + " time: " + arc.getDistance() + " terrain: " + arc.getTerrain());
        }
      }
    }
  }
}




Java Source Code List

com.mapyst.FileHandlerInterface.java
com.mapyst.android.AndroidFileHandler.java
com.mapyst.android.CampusLoader.java
com.mapyst.android.Compass.java
com.mapyst.android.DirectionsList.java
com.mapyst.android.Images.java
com.mapyst.android.LocationFinder.java
com.mapyst.android.MainScreen.java
com.mapyst.android.Mapyst.java
com.mapyst.android.Settings.java
com.mapyst.android.Splash.java
com.mapyst.android.asynctask.CampusLoaderTaskPrefs.java
com.mapyst.android.asynctask.CampusLoaderTask.java
com.mapyst.android.asynctask.RouteMakerTaskPrefs.java
com.mapyst.android.asynctask.RouteMakerTask.java
com.mapyst.android.ui.CenteredToastFactory.java
com.mapyst.android.ui.CompassOverlay.java
com.mapyst.android.ui.DirectionIcon.java
com.mapyst.android.ui.DirectionsListAdapter.java
com.mapyst.android.ui.DirectionsListItem.java
com.mapyst.android.ui.DrawingHelpers.java
com.mapyst.android.ui.Icon.java
com.mapyst.android.ui.LocationsAdapter.java
com.mapyst.android.ui.LocationsListView.java
com.mapyst.android.ui.RouteMapOverlay.java
com.mapyst.android.ui.SlidingScrollView.java
com.mapyst.android.ui.map.AnimatedMapZoomer.java
com.mapyst.android.ui.map.LocationBalloon.java
com.mapyst.android.ui.map.LocationChooserOverlay.java
com.mapyst.android.ui.map.MapUtils.java
com.mapyst.android.ui.map.MapViewLimiter.java
com.mapyst.android.ui.map.MapViewMover.java
com.mapyst.android.ui.map.OnMapTouchLimiterListener.java
com.mapyst.android.ui.map.PriorityMapView.java
com.mapyst.android.ui.map.ViewItemOverlay.java
com.mapyst.campus.Building.java
com.mapyst.campus.Campus.java
com.mapyst.campus.Floor.java
com.mapyst.campus.ListOfCampuses.java
com.mapyst.campus.Location_Type.java
com.mapyst.campus.Location.java
com.mapyst.route.Arc.java
com.mapyst.route.DataParser.java
com.mapyst.route.Direction.java
com.mapyst.route.DistanceCalculator.java
com.mapyst.route.GraphNode.java
com.mapyst.route.InterpretResult.java
com.mapyst.route.InterpretedInfo.java
com.mapyst.route.Interpreter.java
com.mapyst.route.LatLngPoint.java
com.mapyst.route.Prioritizable.java
com.mapyst.route.PriorityQ.java
com.mapyst.route.RouteFinder.java
com.mapyst.route.RoutePreferences.java
com.mapyst.route.Route.java
com.mapyst.route.ShortestPath.java
com.mapyst.route.Waypoint2D.java
com.mapyst.route.WaypointID.java
com.markupartist.android.widget.ActionBar.java
com.markupartist.android.widget.ScrollingTextView.java