/**
** Gridlock.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:
* Gridlock.java 878 2013-03-28 18:35:54Z joey.f.harrison $
* <p/>
*
*/
package sim.app.geo.gridlock;
import com.vividsolutions.jts.geom.Coordinate;
import com.vividsolutions.jts.geom.Envelope;
import com.vividsolutions.jts.geom.GeometryFactory;
import com.vividsolutions.jts.geom.Point;
import com.vividsolutions.jts.planargraph.Node;
import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.logging.Level;
import java.util.logging.Logger;
import sim.engine.SimState;
import sim.engine.Steppable;
import sim.field.geo.GeomVectorField;
import sim.io.geo.ShapeFileImporter;
import sim.util.geo.GeomPlanarGraph;
import sim.util.geo.GeomPlanarGraphEdge;
import sim.util.geo.MasonGeometry;
/**
* The simulation core.
* <p/>
* The simulation can require a LOT of memory, so make sure the virtual machine
* has enough. Do this by adding the following to the command line, or by
* setting up your run configuration in Eclipse to include the VM argument:
* <p/>
* -Xmx2048M
* <p/>
* With smaller simulations this chunk of memory is obviously not necessary. You
* can take it down to -Xmx800M or some such. If you get an OutOfMemory error,
* push it up.
*/
public class Gridlock extends SimState
{
private static final long serialVersionUID = 1L;
/**
* Main function allows simulation to be run in stand-alone, non-GUI mode
*/
public static void main(String[] args)
{
doLoop(Gridlock.class, args);
System.exit(0);
}
public GeomVectorField roads = new GeomVectorField();
public GeomVectorField censusTracts = new GeomVectorField();
// traversable network
public GeomPlanarGraph network = new GeomPlanarGraph();
public GeomVectorField junctions = new GeomVectorField();
// mapping between unique edge IDs and edge structures themselves
HashMap<Integer, GeomPlanarGraphEdge> idsToEdges =
new HashMap<Integer, GeomPlanarGraphEdge>();
HashMap<GeomPlanarGraphEdge, ArrayList<Agent>> edgeTraffic =
new HashMap<GeomPlanarGraphEdge, ArrayList<Agent>>();
public GeomVectorField agents = new GeomVectorField();
ArrayList<Agent> agentList = new ArrayList<Agent>();
// system parameter: can force agents to go to or from work at any time
boolean goToWork = true;
// cheap, hacky, hard-coded way to identify which edges are associated with
// goal Nodes. Done because we cannot seem to read in .shp file for goal nodes because
// of an NegativeArraySize error? Any suggestions very welcome!
Integer[] goals =
{
72142, 72176, 72235, 72178
};
/**
* Constructor
*/
public Gridlock(long seed)
{
super(seed);
}
public boolean getGoToWork()
{
return goToWork;
}
public void setGoToWork(boolean val)
{
goToWork = val;
}
/**
* Initialization
*/
@Override
public void start()
{
super.start();
try
{
// read in the roads to create the transit network
System.out.println("reading roads layer...");
URL roadsFile = Gridlock.class.getResource("data/roads.shp");
ShapeFileImporter.read(roadsFile, roads);
Envelope MBR = roads.getMBR();
// read in the tracts to create the background
System.out.println("reading tracts layer...");
URL areasFile = Gridlock.class.getResource("data/areas.shp");
ShapeFileImporter.read(areasFile, censusTracts);
MBR.expandToInclude(censusTracts.getMBR());
createNetwork();
// update so that everyone knows what the standard MBR is
roads.setMBR(MBR);
censusTracts.setMBR(MBR);
// initialize agents
populate("data/roads_points_place.csv");
agents.setMBR(MBR);
// Ensure that the spatial index is updated after all the agents
// move
schedule.scheduleRepeating(agents.scheduleSpatialIndexUpdater(), Integer.MAX_VALUE, 1.0);
/**
* Steppable that flips Agent paths once everyone reaches their
* destinations
*/
Steppable flipper = new Steppable()
{
@Override
public void step(SimState state)
{
Gridlock gstate = (Gridlock) state;
// pass to check if anyone has not yet reached work
for (Agent a : gstate.agentList)
{
if (!a.reachedDestination)
{
return; // someone is still moving: let him do so
}
}
// send everyone back in the opposite direction now
boolean toWork = gstate.goToWork;
gstate.goToWork = !toWork;
// otherwise everyone has reached their latest destination:
// turn them back
for (Agent a : gstate.agentList)
{
a.flipPath();
}
}
};
schedule.scheduleRepeating(flipper, 10);
} catch (FileNotFoundException ex)
{
System.out.println("Error: missing required data file");
} catch (IOException ex)
{
Logger.getLogger(Gridlock.class.getName()).log(Level.SEVERE, null, ex);
} catch (Exception ex)
{
Logger.getLogger(Gridlock.class.getName()).log(Level.SEVERE, null, ex);
}
}
/**
* Create the road network the agents will traverse
* <p/>
*/
private void createNetwork()
{
System.out.println("creating network...");
network.createFromGeomField(roads);
for (Object o : network.getEdges())
{
GeomPlanarGraphEdge e = (GeomPlanarGraphEdge) o;
idsToEdges.put(e.getDoubleAttribute("ID_ID").intValue(), e);
e.setData(new ArrayList<Agent>());
}
addIntersectionNodes(network.nodeIterator(), junctions);
}
/**
* Read in the population file and create an appropriate pop
* <p/>
* @param filename
*/
public void populate(String filename)
{
try
{
String filePath = Gridlock.class
.getResource(filename).getPath();
FileInputStream fstream = new FileInputStream(filePath);
BufferedReader d = new BufferedReader(new InputStreamReader(fstream));
String s;
d.readLine(); // get rid of the header
while ((s = d.readLine()) != null)
{ // read in all data
String[] bits = s.split(",");
int pop = Integer.parseInt(bits[11]); // TODO: reset me if desired!
String workTract = bits[5];
String homeTract = bits[8];
String id_id = bits[13];
GeomPlanarGraphEdge startingEdge =
idsToEdges.get(
(int) Double.parseDouble(id_id));
GeomPlanarGraphEdge goalEdge = idsToEdges.get(
goals[ random.nextInt(goals.length)]);
for (int i = 0; i < 1; i++)
{//pop; i++){
Agent a = new Agent(this, homeTract, workTract, startingEdge, goalEdge);
boolean successfulStart = a.start(this);
if (!successfulStart)
{
continue; // DON'T ADD IT if it's bad
}
// MasonGeometry newGeometry = new MasonGeometry(a.getGeometry());
MasonGeometry newGeometry = a.getGeometry();
newGeometry.isMovable = true;
agents.addGeometry(newGeometry);
agentList.add(a);
schedule.scheduleRepeating(a);
}
}
// clean up
d.close();
} catch (Exception e)
{
System.out.println("ERROR: issue with population file: " + e);
}
}
/**
* adds nodes corresponding to road intersections to GeomVectorField
* <p/>
* @param nodeIterator Points to first node
* @param intersections GeomVectorField containing intersection geometry
* <p/>
* Nodes will belong to a planar graph populated from LineString network.
*/
private void addIntersectionNodes(Iterator<?> nodeIterator,
GeomVectorField intersections)
{
GeometryFactory fact = new GeometryFactory();
Coordinate coord = null;
Point point = null;
int counter = 0;
while (nodeIterator.hasNext())
{
Node node = (Node) nodeIterator.next();
coord = node.getCoordinate();
point = fact.createPoint(coord);
junctions.addGeometry(new MasonGeometry(point));
counter++;
}
}
}