package sim.app.geo.haiti; import com.vividsolutions.jts.geom.CoordinateSequence; import com.vividsolutions.jts.geom.Envelope; import com.vividsolutions.jts.geom.LineString; import com.vividsolutions.jts.geom.MultiLineString; import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileWriter; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.net.URL; import java.util.ArrayList; import java.util.logging.Level; import java.util.logging.Logger; import java.util.zip.GZIPInputStream; import sim.engine.RandomSequence; import sim.engine.SimState; import sim.engine.Steppable; import sim.engine.Stoppable; import sim.engine.TentativeStep; import sim.field.geo.GeomGridField; import sim.field.geo.GeomVectorField; import sim.field.grid.IntGrid2D; import sim.field.grid.ObjectGrid2D; import sim.field.grid.SparseGrid2D; import sim.field.network.Edge; import sim.field.network.Network; import sim.io.geo.ArcInfoASCGridImporter; import sim.io.geo.ShapeFileImporter; import sim.util.Bag; import sim.util.geo.MasonGeometry; public class HaitiFood extends SimState { private static final long serialVersionUID = 1L; public static int noRoadValue = 15; public static int maximumDensity = 20; public static int riotDensity = 18; public static int reportOrder = 5; /** * Main function allows simulation to be run in stand-alone, non-GUI mode */ public static void main(String[] args) { doLoop(HaitiFood.class, args); System.exit(0); } public GeomGridField roads; public GeomGridField destruction; public SparseGrid2D centers; public SparseGrid2D population; ArrayList<IntGrid2D> distanceGradients = new ArrayList<IntGrid2D>(); public ObjectGrid2D locations; public SparseGrid2D nodes; /** the road nodes closest to each of the locations */ public ObjectGrid2D closestNodes; // Network roadNetwork = new Network(); ArrayList<Center> centersList = new ArrayList<Center>(); ArrayList<Agent> peopleList = new ArrayList<Agent>(); int centersInitialFood = -1; int energyPerFood = -1; int gridWidth; int gridHeight; // the scheduling order in which processes fire int resetOrder = 1; int centerOrder = 2; int personOrder = 3; int rumorOrder = 4; int deaths_total = 0; int deaths_this_tick = 0; int rioting = 0; // agent parameters double enToStay = -1, enWalkPaved = -1, enWalkUnpav = -1, enRiot = -1; int interval = -1; // Relief File Settings /// String reliefFile = null; String[] reliefFiles = new String[] { "data/relief1.asc.gz", "data/reliefBETTA.asc.gz", "data/reliefOKBETTA.asc.gz", "data/reliefBAD.asc.gz", "data/reliefSingle.asc.gz" }; String[] reliefFilesNames = new String[] { "Neutral", "Good", "Better", "Bad", "Single" }; // making the Relief file modifiable int reliefFileIndex = 0; String roadsFile, roadVectorFile, destructionFile, popFile; /** * Constructor */ public HaitiFood(long seed) { super(seed); roadsFile = "data/roads1.asc.gz"; roadVectorFile = "data/Haiti_all_roads_Clip.shp"; destructionFile = "data/destruction.asc.gz"; if (reliefFile == null) { reliefFile = "data/relief1.asc.gz"; } popFile = "data/pop.asc.gz"; } public HaitiFood(long seed, int maxDen, int riotDen, int initFood, int energyFood, double enToStay, double enWalkPaved, double enWalkUnpav, double enRiot, int interval) { super(seed); maximumDensity = maxDen; riotDensity = riotDen; centersInitialFood = initFood; energyPerFood = energyFood; this.enToStay = enToStay; this.enWalkPaved = enWalkPaved; this.enWalkUnpav = enWalkUnpav; this.enRiot = enRiot; this.interval = interval; roadsFile = "data/roads1.asc.gz"; roadVectorFile = "data/Haiti_all_roads_Clip.shp"; destructionFile = "data/destruction.asc.gz"; popFile = "data/pop.asc.gz"; if (reliefFile == null) { reliefFile = reliefFiles[0]; // pick the default } } public int getReliefFileIndex() { return reliefFileIndex; } public void setReliefFileIndex(int r) { reliefFileIndex = r; reliefFile = reliefFiles[r]; } public Object domReliefFileIndex() { return reliefFilesNames; } /** * Initialization */ @Override public void start() { super.start(); // ---- read in data ---- try { // --- ROADS --- // read in the raw roads raster (the situation "on the ground") System.out.println("reading roads layer..."); roads = readIntGridDataWithDefaultValue(roadsFile, noRoadValue); // store the information about the size of the simulation space gridWidth = roads.getGridWidth(); gridHeight = roads.getGridHeight(); // read in the road vector information (the way people *think* about // the road network) GeomVectorField roadLinks = new GeomVectorField(); URL roadVectorFileURL = HaitiFood.class.getResource(roadVectorFile); ShapeFileImporter.read(roadVectorFileURL, roadLinks); nodes = new SparseGrid2D(gridWidth, gridHeight); extractFromRoadLinks(roadLinks); // construct a network of roads // set up the locations and nearest node capability initializeLocations(); closestNodes = setupNearestNodes(); // --- DESTRUCTION --- // read in the destruction information System.out.println("reading destruction layer..."); destruction = setupDestructionFromFile(destructionFile); // --- DISTRIBUTION CENTERS --- // read in the information about food distribution centers System.out.println("reading distribution centers layer..."); centers = setupCentersFromFile(reliefFile); // set them up for (Center c : centersList) { c.loc = (Location) locations.get(c.loc.x, c.loc.y); } // ---- AGENTS ---- System.out.println("reading population layer..."); GeomGridField tempPop = readIntGridDataWithDefaultValue(popFile, 0); population = new SparseGrid2D(tempPop.getGridWidth(), tempPop.getGridHeight()); populate(tempPop); // set it up } catch (IOException ex) { Logger.getLogger(HaitiFood.class.getName()).log(Level.SEVERE, null, ex); } catch (Exception ex) { Logger.getLogger(HaitiFood.class.getName()).log(Level.SEVERE, null, ex); } // ---- set up rumors/information spread ---- RumorMill rumorMill = new RumorMill(); rumorMill.gridWidth = gridWidth; rumorMill.gridHeight = gridHeight; schedule.scheduleRepeating(rumorMill, rumorOrder, 1); // spread initial information triggerRumors(); schedule.scheduleRepeating(new Steppable() { public void step(SimState state) { deaths_this_tick = 0; rioting = 0; } }, resetOrder, 1); } /** * Converts information extracted from the shapefile into links determined * by LineString subsequences */ void extractFromRoadLinks(GeomVectorField roadLinks) { Bag geoms = roadLinks.getGeometries(); Envelope e = roadLinks.getMBR(); double xmin = e.getMinX(), ymin = e.getMinY(), xmax = e.getMaxX(), ymax = e .getMaxY(); int xcols = gridWidth - 1, ycols = gridHeight - 1; // extract each edge for (Object o : geoms) { MasonGeometry gm = (MasonGeometry) o; if (gm.getGeometry() instanceof LineString) { readLineString((LineString) gm.getGeometry(), xcols, ycols, xmin, ymin, xmax, ymax); } else if (gm.getGeometry() instanceof MultiLineString) { MultiLineString mls = (MultiLineString) gm.getGeometry(); for (int i = 0; i < mls.getNumGeometries(); i++) { readLineString((LineString) mls.getGeometryN(i), xcols, ycols, xmin, ymin, xmax, ymax); } } } } /** * Converts an individual linestring into a series of links and nodes in the * network * <p/> * @param geometry * @param xcols - number of columns in the field * @param ycols - number of rows in the field * @param xmin - minimum x value in shapefile * @param ymin - minimum y value in shapefile * @param xmax - maximum x value in shapefile * @param ymax - maximum y value in shapefile */ void readLineString(LineString geometry, int xcols, int ycols, double xmin, double ymin, double xmax, double ymax) { CoordinateSequence cs = geometry.getCoordinateSequence(); // iterate over each pair of coordinates and establish a link between // them Node oldNode = null; // used to keep track of the last node referenced for (int i = 0; i < cs.size(); i++) { // calculate the location of the node in question double x = cs.getX(i), y = cs.getY(i); int xint = (int) Math.floor(xcols * (x - xmin) / (xmax - xmin)), yint = (int) (ycols - Math .floor(ycols * (y - ymin) / (ymax - ymin))); // REMEMBER TO FLIP THE Y VALUE if (xint >= gridWidth) { continue; } else if (yint >= gridHeight) { continue; } // find that node or establish it if it doesn't yet exist Bag ns = nodes.getObjectsAtLocation(xint, yint); Node n; if (ns == null) { n = new Node(new Location(xint, yint)); nodes.setObjectLocation(n, xint, yint); } else { n = (Node) ns.get(0); } if (oldNode == n) // don't link a node to itself { continue; } // attach the node to the previous node in the chain (or continue if // this is the first node in the chain of links) if (i == 0) { // can't connect previous link to anything oldNode = n; // save this node for reference in the next link continue; } int weight = (int) n.loc.distanceTo(oldNode.loc); // weight is just // distance // create the new link and save it Edge e = new Edge(oldNode, n, weight); roadNetwork.addEdge(e); oldNode.links.add(e); n.links.add(e); oldNode = n; // save this node for reference in the next link } } /** set up locations on the location grid for ease of reference */ void initializeLocations() { locations = new ObjectGrid2D(gridWidth, gridHeight); for (int i = 0; i < gridWidth; i++) { for (int j = 0; j < gridHeight; j++) { Location l = new Location(i, j); locations.set(i, j, l); } } } /** * Trigger the initial rumors in a 5 tile area around the centers */ void triggerRumors() { int infoRadius = 5; for (int i = 0; i < centersList.size(); i++) { Center c = centersList.get(i); Bag seeFoodAtCenter = new Bag(); population.getNeighborsMaxDistance(c.loc.x, c.loc.y, infoRadius, false, seeFoodAtCenter, null, null); for (Object o : seeFoodAtCenter) { Agent a = (Agent) o; a.centerInfo += Math.pow(2, i); } } } void populate(GeomGridField pop) { long forReference = 0; // initialize all pop holders for (int i = 0; i < pop.getGridWidth(); i++) { System.out.println("finished: " + i); for (int j = 0; j < pop.getGridHeight(); j++) { ArrayList<Agent> ps = new ArrayList<Agent>(); // get the population int tile = ((IntGrid2D)pop.getGrid()).get(i, j); double tilePop = tile / 10000.0; //this is a sample drawn from landscan data if (tilePop > 0) { forReference += tile; int intPop; // with equal probability, round up or down if (this.random.nextBoolean()) { intPop = (int) Math.floor(tilePop); } else { intPop = (int) Math.ceil(tilePop); } Location here = new Location(i, j); int destructionLevel = ((IntGrid2D)destruction.getGrid()).get(i, j); for (int x = 0; x < Math.min(intPop, 12); x++) { Agent a; if (interval < 0) { a = new Agent(here, here.copy(), destructionLevel); } else { a = new Agent(here, here.copy(), destructionLevel, enToStay, enWalkPaved, enWalkUnpav, enRiot, interval); } population.setObjectLocation(a, i, j); // a.stopper = schedule.scheduleRepeating(a, personOrder, 1); peopleList.add(a); } } } } Steppable[] steppers = new Steppable[peopleList.size()]; for (int i = 0; i < steppers.length; i++) { steppers[i] = (Steppable) (peopleList.get(i)); Agent a = (Agent) steppers[i]; steppers[i] = new TentativeStep(a); a.stopper = (Stoppable) steppers[i]; } RandomSequence seq = new RandomSequence(steppers); schedule.scheduleRepeating(seq, personOrder); System.out.println("Population Size: " + peopleList.size()); System.out.println("Sum of tiles: " + forReference); if (interval < 0) { enToStay = Agent.ENERGY_TO_STAY; enWalkPaved = Agent.ENERGY_TO_WALK_PAVED; enWalkUnpav = Agent.ENERGY_TO_WALK_UNPAVED; enRiot = Agent.ENERGY_TO_RIOT; } } /** * @param filename is the name of the file that holds the raster data * @param defaultValue is what the NO DATA values are set to */ GeomGridField readIntGridDataWithDefaultValue(String filename, int defaultValue) { GZIPInputStream fstream = null; GeomGridField myGrid = new GeomGridField(); try { InputStream inputStream = HaitiFood.class.getResourceAsStream(filename); if (inputStream == null) { throw new FileNotFoundException(filename); } fstream = new GZIPInputStream(inputStream); ArcInfoASCGridImporter.read(fstream, GeomGridField.GridDataType.INTEGER, myGrid); } catch(FileNotFoundException ex) { Logger.getLogger(HaitiFood.class.getName()).log(Level.SEVERE, null, ex); } catch (IOException ex) { Logger.getLogger(HaitiFood.class.getName()).log(Level.SEVERE, null, ex); } finally { try { fstream.close(); } catch (IOException ex) { Logger.getLogger(HaitiFood.class.getName()).log(Level.SEVERE, null, ex); } } // -9999 is the NO DATA value; so we just replace that with the desired // default value. Utilities.gridReplace((IntGrid2D)myGrid.getGrid(), -9999, defaultValue); return myGrid; } /** * @param filename - the name of the file that holds the data */ GeomGridField readIntGridData(String filename) { GZIPInputStream fstream = null; GeomGridField myGrid = new GeomGridField(); try { InputStream inputStream = HaitiFood.class.getResourceAsStream(filename); if (inputStream == null) { throw new FileNotFoundException(filename); } fstream = new GZIPInputStream(inputStream); ArcInfoASCGridImporter.read(fstream, GeomGridField.GridDataType.INTEGER, myGrid); } catch(FileNotFoundException ex) { Logger.getLogger(HaitiFood.class.getName()).log(Level.SEVERE, null, ex); } catch (IOException ex) { Logger.getLogger(HaitiFood.class.getName()).log(Level.SEVERE, null, ex); } finally { try { fstream.close(); } catch (IOException ex) { Logger.getLogger(HaitiFood.class.getName()).log(Level.SEVERE, null, ex); } } return myGrid; } /** * @param filename - the name of the file that holds the data */ GeomGridField setupDestructionFromFile(String filename) { GeomGridField field = readIntGridData(filename); for (int x = 0; x < field.getGridWidth(); x++) { for (int y = 0; y < field.getGridHeight(); y++) { switch( ((IntGrid2D)field.getGrid()).get(x, y) ) { case -9999 : // no data, ignore case 51 : default : ((IntGrid2D)field.getGrid()).set(x, y, 0); break; case 102 : // light damage ((IntGrid2D)field.getGrid()).set(x, y, 1); break; case 153 : ((IntGrid2D)field.getGrid()).set(x, y, 2); break; case 204 : ((IntGrid2D)field.getGrid()).set(x, y, 3); break; case 255 : // severe damage ((IntGrid2D)field.getGrid()).set(x, y, 4); break; } } } return field; } /** * @param filename - the name of the file that holds the data * @param field - the field to be populated */ SparseGrid2D setupCentersFromFile(String filename) { SparseGrid2D field = null; try { // to read in a file // Open the file URL inputFile = HaitiFood.class.getResource(filename); FileInputStream fstream = new FileInputStream(inputFile.getFile()); GZIPInputStream zippedInputStream = new GZIPInputStream(fstream); // Convert our input stream to a BufferedReader BufferedReader d = new BufferedReader( new InputStreamReader(zippedInputStream)); // get the parameters from the file String s; int width = 0, height = 0; double nodata = -1; for (int i = 0; i < 6; i++) { s = d.readLine(); // format the line appropriately String[] parts = s.split(" ", 2); String trimmed = parts[1].trim(); // save the data in the appropriate place if (i == 1) { height = Integer.parseInt(trimmed); } else if (i == 0) { width = Integer.parseInt(trimmed); } else if (i == 5) { nodata = Double.parseDouble(trimmed); } else { continue; } } // set up the field to hold the data field = new SparseGrid2D(width, height); // read in the data from the file and store it in tiles int i = 0, j = 0; while ((s = d.readLine()) != null) { String[] parts = s.split(" "); for (String p : parts) { int value = Integer.parseInt(p); if (value == 1) { // update the field Bag otherCenters = field.getNeighborsHamiltonianDistance( j, i, 5, false, new Bag(), null, null); if (otherCenters.size() > 0) // there is already another center established here: we're describing the same center { continue; } Center c; if (centersInitialFood < 0) { c = new Center(j, i, 100); // IF YOU WANT TO INCREASE INITIAL FOOD ALLOCATION FOR CENTERS, HERE } else { c = new Center(j, i, centersInitialFood, energyPerFood); } field.setObjectLocation(c, j, i); centersList.add(c); schedule.scheduleRepeating(c, centerOrder, 1); } j++; // increase the column count } j = 0; // reset the column count i++; // increase the row count } if (centersInitialFood < 0) { centersInitialFood = 100; energyPerFood = Center.ENERGY_FROM_FOOD; } } // if it messes up, print out the error catch (Exception e) { System.out.println(e); } return field; } /** * Calculate the nodes nearest to each location and store the information * <p/> * @param closestNodes - the field to populate */ ObjectGrid2D setupNearestNodes() { ObjectGrid2D myClosestNodes = new ObjectGrid2D(gridWidth, gridHeight); ArrayList<Crawler> crawlers = new ArrayList<Crawler>(); for (Object o : roadNetwork.allNodes) { Node n = (Node) o; Crawler c = new Crawler(n, n.loc); crawlers.add(c); } // while there is unexplored space, continue! while (crawlers.size() > 0) { ArrayList<Crawler> nextGeneration = new ArrayList<Crawler>(); // randomize the order in which cralwers are considered int size = crawlers.size(); for (int i = 0; i < size; i++) { // randomly pick a remaining crawler int index = random.nextInt(crawlers.size()); Crawler c = crawlers.remove(index); // check if the location has already been claimed Node n = (Node) myClosestNodes.get(c.loc.x, c.loc.y); if (n == null) { // found something new! Mark it and reproduce // set it myClosestNodes.set(c.loc.x, c.loc.y, c.node); // reproduce Bag neighbors = new Bag(); locations.getNeighborsHamiltonianDistance(c.loc.x, c.loc.y, 1, false, neighbors, null, null); for (Object o : neighbors) { Location l = (Location) o; if (l == c.loc) { continue; } Crawler newc = new Crawler(c.node, l); nextGeneration.add(newc); } } // otherwise just die } crawlers = nextGeneration; } return myClosestNodes; } /** * Write the results out to file and clean up after the model */ public void finish() { String report = "haitiResults.txt"; try { // Convert our stream to a BufferedWriter BufferedWriter w = new BufferedWriter(new FileWriter(report, true)); int totalNumberPeople = peopleList.size() + deaths_total; long totalEnergyInSystem = 0L; for (Agent a : peopleList) { totalEnergyInSystem += a.energyLevel; } int totalFoodLeft = 0; for (Center c : centersList) { totalFoodLeft += c.foodLevel; } // make a csv by replacing each '\t' with ',' // popfile is output to tell you which scale you're using String output = popFile + "\t" + reliefFile + "\t" + maximumDensity + "\t" + riotDensity + "\t" + centersInitialFood + "\t" + energyPerFood + "\t" + enToStay + "\t" + enWalkPaved + "\t" + enWalkUnpav + "\t" + enRiot + "\t" + interval + "\t" + schedule.getSteps() + "\t" + totalNumberPeople + "\t" + deaths_total + "\t" + totalEnergyInSystem + "\t" + centersList.size() + "\t" + totalFoodLeft; w.write(output); w.newLine(); w.flush(); w.close(); } catch (Exception e) { System.err.println("File input error"); } kill(); } /** * Used to store information about the road network */ class Node { Location loc; ArrayList<Edge> links; public Node(Location l) { loc = l; links = new ArrayList<Edge>(); } } /** * Used to find the nearest node for each space * <p/> */ class Crawler { Node node; Location loc; public Crawler(Node n, Location l) { node = n; loc = l; } } }