/**
** WaterWorld.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.waterworld;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.zip.GZIPInputStream;
import sim.engine.SimState;
import sim.engine.Steppable;
import sim.engine.Stoppable;
import sim.field.geo.GeomGridField;
import sim.field.geo.GeomGridField.GridDataType;
import sim.field.grid.IntGrid2D;
import sim.field.grid.ObjectGrid2D;
import sim.io.geo.ArcInfoASCGridImporter;
/**
* The WaterWorld simulation core.
*/
public class WaterWorld extends SimState
{
ObjectGrid2D landscape;
ArrayList<Raindrop> drops;
// landscape parameters
int grid_width = 100;
int grid_height = 100;
private static final long serialVersionUID = 1L;
/**
* Constructor function.
* @param seed
*/
public WaterWorld(long seed)
{
super(seed);
}
/**
* Starts a new run of the simulation. Refreshes the landscape and
* schedules the addition of new Raindrops to the system.
*/
@Override
public void start()
{
super.start();
// various options for setting up the landscape
//landscape = setupLandscape(); // uniform landscape, completely flat
//landscape = setupLandscapeGradientIn(); // landscape that slopes in
landscape = setupLandscapeReadIn("data/elevation.txt.gz"); // read landscape from file
drops = new ArrayList<Raindrop>();
schedule.scheduleRepeating(new Raincloud());
// schedule.scheduleRepeating(new Printout()); // printout of depth data
}
/**
* set up the landscape to be of uniform height.
* @return new landscape
*/
ObjectGrid2D setupLandscape()
{
ObjectGrid2D result = new ObjectGrid2D(grid_width, grid_height);
// go over the landscape and set up a basin at every grid point
for (int i = 0; i < grid_width; i++)
{
for (int j = 0; j < grid_height; j++)
{
// initialize a Basin with elevation 0
Basin b = new Basin(i, j);
result.set(i, j, b);
}
}
return result;
}
/**
* Read in a landscape from a file.
* @param filename - the name of the file that holds the landscape data
* @return new landscape
*/
ObjectGrid2D setupLandscapeReadIn(final String filename)
{
ObjectGrid2D result = null;
try
{
GeomGridField elevationsField = new GeomGridField();
InputStream inputStream = WaterWorld.class.getResourceAsStream(filename);
if (inputStream == null)
{
throw new FileNotFoundException(filename);
}
GZIPInputStream compressedInputStream = new GZIPInputStream(inputStream);
ArcInfoASCGridImporter.read(compressedInputStream, GridDataType.INTEGER, elevationsField);
grid_width = elevationsField.getGridWidth();
grid_height = elevationsField.getGridHeight();
result = new ObjectGrid2D(grid_width, grid_height);
IntGrid2D elevations = (IntGrid2D) elevationsField.getGrid();
for (int x = 0; x < grid_width; x++)
{
for (int y = 0; y < grid_height; y++)
{
Basin b = new Basin(x, y, elevations.get(x, y));
result.set(x, y, b);
}
}
} // if it messes up, print out the error
catch (Exception e)
{
System.out.println(e);
}
return result;
}
/**
* set up the landscape to be shaped like a funnel, so that Raindrops move
* from the outside of the landscape to the center.
* @return new landscape
*/
ObjectGrid2D setupLandscapeGradientIn()
{
ObjectGrid2D result = new ObjectGrid2D(grid_width, grid_height);
// saved to avoid recalculating the same numbers for every basin
int hwidth = grid_width / 2;
int hheight = grid_height / 2;
// iterate over every Basin and set its height equal to its distance from the center
for (int i = 0; i < grid_width; i++)
{
for (int j = 0; j < grid_height; j++)
{
// set its height to either the distance...
int height = (int) Math.sqrt(Math.pow(i - hwidth, 2) + Math.pow(j - hheight, 2));
// of the square of its distance to the center
//int height = (int) ( Math.pow(i - hwidth, 2) + Math.pow (j - hheight, 2));
Basin b = new Basin(i, j, height);
result.set(i, j, b);
}
}
return result;
}
/**
* a Steppable that adds new Raindrops to the simulation
*/
class Raincloud implements Steppable
{
// the number of drops added to the simulation per tick
int numDropsPerTurn = 500;
public void step(SimState state)
{
// to dump all raindrops into the center of the graph, uncomment here
//int w = grid_width / 2, h = grid_height / 2;
// randomly select a tile and add a new raindrop to it
for (int i = 0; i < numDropsPerTurn; i++)
{
// select a random tile on the landscape
int x = random.nextInt(grid_width), y = random.nextInt(grid_height);
// to dump all raindrops into the center of the graph, uncomment here
//x = w; y = h;
Basin b = (Basin) landscape.get(x, y);
// set up a new raindrop and add it to the tile
Raindrop r = new Raindrop((WaterWorld) state, landscape, b);
Stoppable stopper = schedule.scheduleRepeating(r);
r.stopper = stopper; // give the raindrop the ability to stop itself
((WaterWorld) state).drops.add(r);
b.addDrop(r);
}
}
}
/**
* prints out a report about either the depth of the water or the overall elevation
*/
class Printout implements Steppable
{
public void step(SimState state)
{
String output = "";
for (int i = 0; i < grid_width; i++)
{
for (int j = 0; j < grid_height; j++)
{
// setting for measuring water depth...
output += ((Basin) landscape.get(i, j)).drops.size()
// setting for measuring overall elevation
//output += ((Basin)landscape.get(i, j)).baseheight
+ "\t";
}
output += "\n";
}
System.out.println("STEP " + schedule.getSteps() + "\n" + output);
}
}
/**
* Main function, runs the simulation without any visualization.
* @param args
*/
public static void main(String[] args)
{
doLoop(WaterWorld.class, args);
System.exit(0);
}
}