/*
* Copyright 2011 by Mark Coletti, Keith Sullivan, Sean Luke, and
* George Mason University Mason University Licensed under the Academic
* Free License version 3.0
*
* See the file "LICENSE" for more information
*
* $Id$
*/
package sim.app.geo.campusworld;
import com.vividsolutions.jts.geom.Coordinate;
import com.vividsolutions.jts.geom.GeometryFactory;
import com.vividsolutions.jts.geom.LineString;
import com.vividsolutions.jts.geom.Point;
import com.vividsolutions.jts.linearref.LengthIndexedLine;
import com.vividsolutions.jts.planargraph.DirectedEdgeStar;
import com.vividsolutions.jts.planargraph.Node;
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 continues in a random direction.
*
*/
public class Agent implements Steppable
{
private static final long serialVersionUID = -1113018274619047013L;
// point that denotes agent's position
private MasonGeometry location;
// The base speed of the agent.
private double basemoveRate = 1.0;
// How much to move the agent by in each step(); may become negative if
// agent is moving from the end to the start of current line.
private double moveRate = basemoveRate;
// Used by agent to walk along line segment; assigned in setNewRoute()
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
PointMoveTo pointMoveTo = new PointMoveTo();
static private GeometryFactory fact = new GeometryFactory();
public Agent(CampusWorld state)
{
location = new MasonGeometry(fact.createPoint(new Coordinate(10, 10))); // magic numbers
location.isMovable = true;
// Find the first line segment and set our position over the start coordinate.
int walkway = state.random.nextInt(state.walkways.getGeometries().numObjs);
MasonGeometry mg = (MasonGeometry) state.walkways.getGeometries().objs[walkway];
setNewRoute((LineString) mg.getGeometry(), true);
// Now set up attributes for this agent
if (state.random.nextBoolean())
{
location.addStringAttribute("TYPE", "STUDENT");
int age = (int) (20.0 + 2.0 * state.random.nextGaussian());
location.addIntegerAttribute("AGE", age);
}
else
{
location.addStringAttribute("TYPE", "FACULTY");
int age = (int) (40.0 + 9.0 * state.random.nextGaussian());
location.addIntegerAttribute("AGE", age);
}
// Not everyone walks at the same speed
basemoveRate *= Math.abs(state.random.nextGaussian());
location.addDoubleAttribute("MOVE RATE", basemoveRate);
}
/**
* @return geometry representing agent location
*/
public MasonGeometry getGeometry()
{
return location;
}
/** true if the agent has arrived at the target intersection
*/
private boolean arrived()
{
// If we have a negative move rate the agent is moving from the end to
// the start, else the agent is moving in the opposite direction.
if ((moveRate > 0 && currentIndex >= endIndex)
|| (moveRate < 0 && currentIndex <= startIndex))
{
return true;
}
return false;
}
/** @return string indicating whether we are "FACULTY" or a "STUDENT" */
public String getType()
{
return location.getStringAttribute("TYPE");
}
/** randomly selects an adjacent route to traverse
*/
private void findNewPath(CampusWorld geoTest)
{
// find all the adjacent junctions
Node currentJunction = geoTest.network.findNode(location.getGeometry().getCoordinate());
if (currentJunction != null)
{
DirectedEdgeStar directedEdgeStar = currentJunction.getOutEdges();
Object[] edges = directedEdgeStar.getEdges().toArray();
if (edges.length > 0)
{
// pick one randomly
int i = geoTest.random.nextInt(edges.length);
GeomPlanarGraphDirectedEdge directedEdge = (GeomPlanarGraphDirectedEdge) edges[i];
GeomPlanarGraphEdge edge = (GeomPlanarGraphEdge) directedEdge.getEdge();
// and start moving along it
LineString newRoute = edge.getLine();
Point startPoint = newRoute.getStartPoint();
Point endPoint = newRoute.getEndPoint();
if (startPoint.equals(location.geometry))
{
setNewRoute(newRoute, true);
} else
{
if (endPoint.equals(location.geometry))
{
setNewRoute(newRoute, false);
} else
{
System.err.println("Where am I?");
}
}
}
}
}
/**
* have the agent move along new route
*
* @param line defining new route
* @param start true if agent at start of line else agent placed at end
*/
private void setNewRoute(LineString line, boolean start)
{
segment = new LengthIndexedLine(line);
startIndex = segment.getStartIndex();
endIndex = segment.getEndIndex();
Coordinate startCoord = null;
if (start)
{
startCoord = segment.extractPoint(startIndex);
currentIndex = startIndex;
moveRate = basemoveRate; // ensure we move forward along segment
} else
{
startCoord = segment.extractPoint(endIndex);
currentIndex = endIndex;
moveRate = -basemoveRate; // ensure we move backward along segment
}
moveTo(startCoord);
}
// move the agent to the given coordinates
public void moveTo(Coordinate c)
{
pointMoveTo.setCoordinate(c);
location.getGeometry().apply(pointMoveTo);
getGeometry().geometry.geometryChanged();
}
public void step(SimState state)
{
CampusWorld campState = (CampusWorld) state;
move(campState);
// campState.agents.setGeometryLocation(getGeometry(), pointMoveTo);
}
/**
* moves the agent along the grid
*
* @param geoTest handle on the base SimState
*
* The agent will randomly select an adjacent junction and then move along
* the line segment to it. Then it will repeat.
*/
private void move(CampusWorld geoTest)
{
// if we're not at a junction move along the current segment
if (!arrived())
{
moveAlongPath();
} else
{
findNewPath(geoTest);
}
}
// move agent along current line segment
private void moveAlongPath()
{
currentIndex += moveRate;
// Truncate movement to end of line segment
if (moveRate < 0)
{ // moving from endIndex to startIndex
if (currentIndex < startIndex)
{
currentIndex = startIndex;
}
} else
{ // moving from startIndex to endIndex
if (currentIndex > endIndex)
{
currentIndex = endIndex;
}
}
Coordinate currentPos = segment.extractPoint(currentIndex);
moveTo(currentPos);
}
}