/** ** SchellingSpace.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.schellingspace; import com.vividsolutions.jts.geom.*; import java.io.FileNotFoundException; import java.net.URL; import java.util.ArrayList; import sim.engine.SimState; import sim.field.geo.GeomVectorField; import sim.io.geo.ShapeFileImporter; import sim.util.Bag; import sim.util.geo.MasonGeometry; //@SuppressWarnings("restriction") public class SchellingSpace extends SimState { private static final long serialVersionUID = 1L; /** Contains polygons defining DC ward boundaries */ public GeomVectorField world = new GeomVectorField(); /** The agents moving through DC wards * */ public GeomVectorField agents = new GeomVectorField(); /** * */ ArrayList<SchellingGeometry> polys = new ArrayList<SchellingGeometry>(); /** * */ ArrayList<Person> people = new ArrayList<Person>(); // used by PolySchellingWithUI to keep track of the percent of unhappy Persons public int totalReds = 0; public int totalBlues = 0; /** * constructor function */ public SchellingSpace(long seed) { super(seed); } /** * Takes the geometries after they have been read in and constructs each Polygon's * list of neighbors. Also extracts information about the mobile agents from the * Polygons and sets up the list of Persons. */ void setup() { // copy over the geometries into a list of Polygons Bag ps = world.getGeometries(); polys.addAll(ps); GeometryFactory geometryFactory = new GeometryFactory(); System.out.println("Computing adjacencies and populating polygons"); // process the polygons for neighbor and Person info for (int i = 0; i < polys.size(); i++) { if ( i % 10 == 0 ) { System.out.print("."); } SchellingGeometry p1 = polys.get(i); p1.init(); // add all neighbors for (int j = i + 1; j < polys.size(); j++) { SchellingGeometry p2 = polys.get(j); if (p1.geometry.touches(p2.geometry)) { p1.neighbors.add(p2); p2.neighbors.add(p1); } } // add all of the Red People in this SchellingGeometry for (int k = 0; k < p1.initRed; k++) { // initialize the Person Person p = new Person(Person.Affiliation.RED); p.region = p1; p.location = randomPointInsidePolygon((Polygon) p1.geometry, geometryFactory); p.location.isMovable = true; p.location.setUserData(p); // place the Person in the GeomVectorField // store information agents.addGeometry(p.location); people.add(p); p1.residents.add(p); } // add all of the blue People in this SchellingGeometry for (int k = 0; k < p1.initBlue; k++) { // initialize the Person Person p = new Person(Person.Affiliation.BLUE); p.region = p1; p.location = randomPointInsidePolygon((Polygon) p1.geometry, geometryFactory); p.location.isMovable = true; p.location.setUserData(p); // place the Person in the GeomVectorField // store information agents.addGeometry(p.location); people.add(p); p1.residents.add(p); } // update the total population counts totalReds += p1.initRed; totalBlues += p1.initBlue; } // schedule all of the Persons to update every tick. By default, they are called // in random order System.out.println("\nScheduling agents"); int i = 0; for (Person p : people) { schedule.scheduleRepeating(p); i++; } } /** * returns a Point inside the polygon * @param p the Polygon within which the point should lie * @param gfact the GeometryFactory that will create new points * @return */ MasonGeometry randomPointInsidePolygon(Polygon p, GeometryFactory gfact) { if (p == null) { return null; } // nothing here if (p.isEmpty()) { return null; } // can never find anything inside this empty geometry! Envelope e = p.getEnvelopeInternal(); // calcuate where the point can be double xmin = e.getMinX(), ymin = e.getMinY(), xmax = e.getMaxX(), ymax = e.getMaxY(); double addX = random.nextDouble() * (xmax - xmin) + xmin; // the proposed x value double addY = random.nextDouble() * (ymax - ymin) + ymin; // the proposed y value Point pnt = gfact.createPoint(new Coordinate(addX, addY)); // continue searching until the point found is within the polygon while (!p.covers(pnt)) {//p.contains(pnt) ){ addX = random.nextDouble() * (xmax - xmin) + xmin; // the proposed x value addY = random.nextDouble() * (ymax - ymin) + ymin; // the proposed y value pnt = gfact.createPoint(new Coordinate(addX, addY)); } // return the found point return new MasonGeometry(pnt); } /** Import the data and then set up the simulation */ @Override public void start() { super.start(); try // to import the data from the shapefile { System.out.print("Reading boundary data ... "); URL wardsFile = SchellingSpace.class.getResource("data/DCreprojected.shp"); ShapeFileImporter.read( wardsFile, world, SchellingGeometry.class); } catch (Exception ex) { System.out.println("Error opening shapefile!" + ex); System.exit(-1); } // Sync MBRs agents.setMBR(world.getMBR()); System.out.println("done"); System.out.print("Computing convex hull ... "); world.computeConvexHull(); System.out.print("done.\nComputing union ... "); world.computeUnion(); System.out.println("done"); // once the data is read in, set up the Polygons and Persons setup(); } /** * Called to run PolySchelling without the GUI * @param args */ public static void main(String[] args) { doLoop(SchellingSpace.class, args); System.exit(0); } }