import java.awt.*; import java.io.*; import java.util.*; import javax.swing.*; import java.lang.Math.*; /* * A skeleton class for handling a grid. A grid knows how to * initialize from a file or from a 2D array given as argument, render * and compute flow. * * The grid constructor can be called with a file name as argument, in * which case it reads the grid from the file and sets up all local * variables. * * If the user wants to compute the flow corresponding to a grid, he/she * must first create a grid from a file, then call the method * computeFlow on the respective grid. This method returns a 2D array * that stores the flow for each cell. * * The intended use is to first create a grid from a file, then call * computeFlow on it, then create another grid by passing the resulting * 2D array of values as an argument. The two grids, the original one * and the flow, will render in the same time. * * Fill in the code for computeFlow() method. A couple of getters and * other helper methods are given, which you may find helpful (or not). * * * * * Laura Toma, based on original code by Jack Morrison * May 2008 * * */ public class Grid extends JFrame { // Array to store image data private int[][] data; // Header data private int cols,rows, noData; private float cellSize; // range of data values and separators used to determine the color // buckets when drawing private int highVal,lowVal; private float val1, val2, val3, val4; private static final Color DEFAULT_COLOR = Color.BLACK; private static final int HEADER_LINES=6; //this value is used by paint to determine what color scheme to //use. If the grid represents a flow, the intervals for colors //need to be much more aggressive, because there are very few //cells with highvalues of cells. ELEV_MODE and FLOW_MODE private static final int ELEV_MODE = 0, FLOW_MODE = 1; private int RENDER_MODE; private boolean DEBUG = true; /**************************************************************/ //reads a grid from a file and initializes all data; the default //value for RENDER_MODE is ELEV_MODE public Grid(String fileName) { // Makes a JFrame super("GIS Grid"); initGrid(fileName); RENDER_MODE = ELEV_MODE; //set cell so that the grid fits into a 300x300 window cellSize = ((float)(300)/(float)Math.min(rows,cols)); // Sets the size of the JFrame to be big enough to accomodate // the entire image setSize((int)(cols*cellSize),(int)(rows*cellSize)); setVisible(true); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); repaint(); } /**************************************************************/ //the grid is set from the arguments. Note that the argument array //is "stolen", not copied. public Grid(int[][] grid, int rows, int cols, float cellSize, int noData, int mode) { super("GIS Grid"); this.data = grid; this.rows = rows; this.cols = cols; this.cellSize = cellSize; this.noData = noData; this.RENDER_MODE = mode; //set range values this.lowVal= noData; this.highVal = noData; for (int i=0; i< rows; i++) { for (int j=0; j this.highVal) this.highVal = data[i][j]; } } System.out.println("range is: [" + lowVal + ", " + highVal + "]"); //set separators for color buckets if (mode == ELEV_MODE) { // Computes distance a fifth of the way from the high point to // the low; used for drawing boundaries float valSpread = ((float)highVal-(float)lowVal)/5; // Sets boundary ranges this.val1 = lowVal+valSpread; this.val2 = this.val1 + valSpread; this.val3 = this.val2 + valSpread; this.val4 = this.val3 + valSpread; System.out.println("color range is: " + lowVal + ", " + val1 + ", " +val2 + ", " +val3+ ", " +val4 + ", " +highVal ); } else { //MODE==FLOW_MODE //in this case we only have two color buckets float valSpread = ((float)highVal-(float)lowVal); this.val1 = lowVal + (float).05 * valSpread; this.val2 = this.val3=this.val4 = this.val1; } // Sets the size of the JFrame to be big enough to accomodate // the entire image setSize((int)(cols*this.cellSize),(int)(rows*this.cellSize)); setVisible(true); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); repaint(); } /**************************************************************/ public int getRows() {return rows;} /**************************************************************/ public int getCols() {return cols;} /**************************************************************/ public int getNoData() {return noData;} /**************************************************************/ public float getCellSize() {return cellSize;} /**************************************************************/ // reads a grid from a file and initializes all data private void initGrid(String fileName) { // Load File File f = new File(fileName); // Scanners and scanner variables String nextLine; int nextVal; Scanner lineScan = null; Scanner valScan = null; // Initialize high variable highVal = 0; // A giant number, so that the first number // is sure to be less than it. lowVal = 10000000; // Initialize our current line int currentLine = 1; // Create the line scanner try {lineScan = new Scanner(f);} catch(FileNotFoundException fnfex) {return;} // Scan through all the lines while (lineScan.hasNextLine()){ // Get then next line nextLine = lineScan.nextLine(); // We're starting at the first column int currentCol = 0; // Try to make a scanner for this line try {valScan = new Scanner(nextLine);} catch(IllegalArgumentException iaex) {return;} // Store the number of columns if (currentLine == 1 && valScan.next().equals("ncols")){ cols = valScan.nextInt(); } // Store the number of rows else if (currentLine == 2 && valScan.next().equals("nrows")) { rows = valScan.nextInt(); // Initialize the data array data = new int[rows][cols]; } // We skip lines 3 and 4 of the header, because we have // no need for the data contained in them // Get the cell size from the header else if (currentLine == 5 && valScan.next().equals("cellsize")){ cellSize = (int)(valScan.nextFloat());} // Get the header value for NODATA else if (currentLine == 6){ valScan.next(); noData = (int)valScan.nextFloat(); } // When we leave the header, copy each new number from the // file into the data array. All numbers are cast as ints // to save some space, because the precision of floats is // not needed for a representation of this type. else if (currentLine>HEADER_LINES ) { while (valScan.hasNext()){ nextVal = (int)valScan.nextFloat(); data[currentLine-HEADER_LINES-1][currentCol] = nextVal; // Store new high or low values if (nextVal>highVal) highVal = nextVal; else if (nextVal=lowVal && avgElev<=val1) return new Color(0,(int)(Math.abs(avgElev%40)+100),31); if (avgElev>val1 && avgElev<=val2) return new Color(0,(int)(Math.abs(avgElev%40)+160),48); if (avgElev>val2 && avgElev<=val3) return new Color(162,(int)(Math.abs(avgElev%40)+211),80); if (avgElev>val3 && avgElev<=val4) return new Color(211,(int)(Math.abs(avgElev%40)+179),80); if (avgElev>val4 && avgElev<=highVal) return new Color(121,(int)(Math.abs(avgElev%40)+92),0); return DEFAULT_COLOR; } else { //RENDER_MODE == FLOW_MODE if (avgElev < lowVal || avgElev> highVal) return DEFAULT_COLOR; else if (avgElev>=lowVal && avgElev<= val1) { //this is a point with low flow //convert elevation into a value x in [lowVal, val1] float x = (avgElev - lowVal)/(val1-lowVal); //now scale x to [0,255] int c = (int) (x * 256); //play abit with color c = (c*2 + 100)% 256; return new Color(c, c, c); } else { //this is a point with high flow float x; //convert elevation into a value x within [val1, highVal] x = (avgElev - val1)/ (highVal-val1); //now scale x to [0,255] int c = (int)(x * 255); //play abit with color c = c % 256; return new Color(0,0,255-c); } } } /**************************************************************/ //renders a grid public void paint(Graphics g) { Color paintColor; for (int i=0; i=1){ paintColor = checkColor(data[i][j],data[i-1][j]); g.setColor(paintColor); g.drawLine((int)(j*cellSize),(int)(i*cellSize), (int)(j*cellSize),(int)((i-1)*cellSize)); } }//for j } //for i } //paint /**************************************************************/ //prints the grid values public void print() { System.out.println("The grid is: "); for (int i=0; i= rows || j< 0 || j >= cols) return false; else return true; } /**************************************************************/ //returns trus if (i,j) is within grid boundaries and not equal to //NODATA public boolean isValid(int i, int j) { return (withinGrid(i,j) && (data[i][j] != noData)); } /**************************************************************/ //return true if (i,j) flow into (k, l); this is, if (k,l) is the //steepest downslope neighbor of (i,j) public boolean flowsInto(int i, int j, int k, int l) { //if either cell is not valid, then return false if (!isValid(i,j) || !isValid(k,l)) return false; int lowest_ni = -1; int lowest_nj = -1; int lowest_nh = -1; for (int ni=-1; ni<= 1; ni++) { for (int nj=-1; nj<=1; nj++) { //if this neighbor is valid if (ni == 0 && nj==0) continue; if (isValid(i+ni, j+nj)) //if this neighbor is lower than the lowest so //far, update lowest so far if (lowest_ni == -1 || lowest_nh > data[i+ni][j+nj]) { lowest_ni = i+ni; lowest_nj = j+nj; lowest_nh = data[lowest_ni][lowest_nj]; } } //for j } //for i //(lowest_ni, lowest_nj) is the lowest valid neighbor of the //current cell // if (lowest_nh < data[i][j] && lowest_ni == k && lowest_nj == l) { //System.out.println("lowest neighbor of (" + i + "," + j + ") is (" + // lowest_ni + "," + lowest_nj + ")"); return true; } return false; } /**************************************************************/ public int[][] computeFlow() { // create and initialize flow grid int[][] flow = new int[rows][cols]; for (int i=0; i< rows; i++) { for (int j=0; j"); System.exit(1); } Grid elev = new Grid(args[0]); Grid flow = new Grid(elev.computeFlow(), elev.getRows(),elev.getCols(), elev.getCellSize(), elev.getNoData(), FLOW_MODE); //elev.print(); //flow.print(); } } //class Grid