/** ** Agent.java ** ** Copyright 2011 by Sarah Wise, Mark Coletti, Andrew Crooks, and ** George Mason University. ** ** Licensed under the Academic Free License version 3.0 ** ** See the file "LICENSE" for more information * * $Id$ ** **/ package sim.app.geo.gridlock; import com.vividsolutions.jts.geom.*; import com.vividsolutions.jts.linearref.LengthIndexedLine; import com.vividsolutions.jts.planargraph.Node; import java.util.ArrayList; import sim.engine.SimState; import sim.engine.Steppable; import sim.util.geo.GeomPlanarGraphDirectedEdge; import sim.util.geo.GeomPlanarGraphEdge; import sim.util.geo.MasonGeometry; import sim.util.geo.PointMoveTo; /** * Our simple agent for the CampusWorld GeoMASON example. The agent randomly wanders * around the campus walkways. When * the agent reaches an intersection, it chooses a random direction and continues on. * */ @SuppressWarnings("restriction") public final class Agent implements Steppable { private static final long serialVersionUID = -1113018274619047013L; Gridlock world; // Residence/Work Attributes String homeTract = ""; String workTract = ""; Node homeNode = null; Node workNode = null; // point that denotes agent's position // private Point location; private MasonGeometry location; // How much to move the agent by in each step() private double moveRate = .001; // Used by agent to walk along line segment private LengthIndexedLine segment = null; double startIndex = 0.0; // start position of current line double endIndex = 0.0; // end position of current line double currentIndex = 0.0; // current location along line GeomPlanarGraphEdge currentEdge = null; int linkDirection = 1; double speed = 0; // useful for graph ArrayList<GeomPlanarGraphDirectedEdge> pathFromHomeToWork = new ArrayList<GeomPlanarGraphDirectedEdge>(); int indexOnPath = 0; int pathDirection = 1; boolean reachedDestination = false; PointMoveTo pointMoveTo = new PointMoveTo(); /** This is the wrapper object in the agents layer. We need a handle on * it so that we can update our location with each step(). */ // private MasonGeometry renderedGeometry; // // // // public MasonGeometry getRenderedGeometry() // { // return renderedGeometry; // } // // // // public void setRenderedGeometry(MasonGeometry renderedGeometry) // { // this.renderedGeometry = renderedGeometry; // } /** Constructor Function */ public Agent(Gridlock g, String home, String work, GeomPlanarGraphEdge startingEdge, GeomPlanarGraphEdge goalEdge) { world = g; // set up information about where the node is and where it's going homeNode = startingEdge.getDirEdge(0).getFromNode(); workNode = goalEdge.getDirEdge(0).getToNode(); homeTract = home; workTract = work; // set the location to be displayed GeometryFactory fact = new GeometryFactory(); location = new MasonGeometry(fact.createPoint(new Coordinate(10, 10))) ; Coordinate startCoord = null; startCoord = homeNode.getCoordinate(); updatePosition(startCoord); } /** Initialization of an Agent: find an A* path to work! * * @param state * @return whether or not the agent successfully found a path to work */ public boolean start(Gridlock state) { findNewAStarPath(state); if (pathFromHomeToWork.isEmpty()) { System.out.println("Initialization of agent failed: it is located in a part " + "of the network that cannot access the given goal node"); return false; } else { return true; } } /** Plots a path between the Agent's home Node and its work Node */ private void findNewAStarPath(Gridlock geoTest) { // get the home and work Nodes with which this Agent is associated Node currentJunction = geoTest.network.findNode(location.geometry.getCoordinate()); Node destinationJunction = workNode; if (currentJunction == null) { return; // just a check } // find the appropriate A* path between them AStar pathfinder = new AStar(); ArrayList<GeomPlanarGraphDirectedEdge> path = pathfinder.astarPath(currentJunction, destinationJunction); // if the path works, lay it in if (path != null && path.size() > 0) { // save it pathFromHomeToWork = path; // set up how to traverse this first link GeomPlanarGraphEdge edge = (GeomPlanarGraphEdge) path.get(0).getEdge(); setupEdge(edge); // update the current position for this link updatePosition(segment.extractPoint(currentIndex)); } } double progress(double val) { double edgeLength = currentEdge.getLine().getLength(); double traffic = world.edgeTraffic.get(currentEdge).size(); double factor = 1000 * edgeLength / (traffic * 5); factor = Math.min(1, factor); return val * linkDirection * factor; } /** Called every tick by the scheduler */ /** moves the agent along the path */ public void step(SimState state) { // check that we've been placed on an Edge if (segment == null) { return; } // check that we haven't already reached our destination else if (reachedDestination) { return; } // make sure that we're heading in the right direction boolean toWork = ((Gridlock) state).goToWork; if ((toWork && pathDirection < 0) || (!toWork && pathDirection > 0)) { flipPath(); } // move along the current segment speed = progress(moveRate); currentIndex += speed; // check to see if the progress has taken the current index beyond its goal // given the direction of movement. If so, proceed to the next edge if (linkDirection == 1 && currentIndex > endIndex) { Coordinate currentPos = segment.extractPoint(endIndex); updatePosition(currentPos); transitionToNextEdge(currentIndex - endIndex); } else if (linkDirection == -1 && currentIndex < startIndex) { Coordinate currentPos = segment.extractPoint(startIndex); updatePosition(currentPos); transitionToNextEdge(startIndex - currentIndex); } else { // just update the position! Coordinate currentPos = segment.extractPoint(currentIndex); updatePosition(currentPos); } } /** Flip the agent's path around */ void flipPath() { reachedDestination = false; pathDirection = -pathDirection; linkDirection = -linkDirection; } /** * Transition to the next edge in the path * @param residualMove the amount of distance the agent can still travel * this turn */ void transitionToNextEdge(double residualMove) { // update the counter for where the index on the path is indexOnPath += pathDirection; // check to make sure the Agent has not reached the end // of the path already if ((pathDirection > 0 && indexOnPath >= pathFromHomeToWork.size()) || (pathDirection < 0 && indexOnPath < 0))// depends on where you're going! { System.out.println(this + " has reached its destination"); reachedDestination = true; indexOnPath -= pathDirection; // make sure index is correct return; } // move to the next edge in the path GeomPlanarGraphEdge edge = (GeomPlanarGraphEdge) pathFromHomeToWork.get(indexOnPath).getEdge(); setupEdge(edge); speed = progress(residualMove); currentIndex += speed; // check to see if the progress has taken the current index beyond its goal // given the direction of movement. If so, proceed to the next edge if (linkDirection == 1 && currentIndex > endIndex) { transitionToNextEdge(currentIndex - endIndex); } else if (linkDirection == -1 && currentIndex < startIndex) { transitionToNextEdge(startIndex - currentIndex); } } ///////////// HELPER FUNCTIONS //////////////////////////// /** Sets the Agent up to proceed along an Edge * @param edge the GeomPlanarGraphEdge to traverse next * */ void setupEdge(GeomPlanarGraphEdge edge) { // clean up on old edge if (currentEdge != null) { ArrayList<Agent> traffic = world.edgeTraffic.get(currentEdge); traffic.remove(this); } currentEdge = edge; // update new edge traffic if (world.edgeTraffic.get(currentEdge) == null) { world.edgeTraffic.put(currentEdge, new ArrayList<Agent>()); } world.edgeTraffic.get(currentEdge).add(this); // set up the new segment and index info LineString line = edge.getLine(); segment = new LengthIndexedLine(line); startIndex = segment.getStartIndex(); endIndex = segment.getEndIndex(); linkDirection = 1; // check to ensure that Agent is moving in the right direction double distanceToStart = line.getStartPoint().distance(location.geometry), distanceToEnd = line.getEndPoint().distance(location.geometry); if (distanceToStart <= distanceToEnd) { // closer to start currentIndex = startIndex; linkDirection = 1; } else if (distanceToEnd < distanceToStart) { // closer to end currentIndex = endIndex; linkDirection = -1; } } /** move the agent to the given coordinates */ public void updatePosition(Coordinate c) { pointMoveTo.setCoordinate(c); // location.geometry.apply(pointMoveTo); world.agents.setGeometryLocation(location, pointMoveTo); } /** return geometry representing agent location */ public MasonGeometry getGeometry() { return location; } }