import java.io.*; import java.util.*; import java.awt.*; import java.lang.Integer; import java.awt.Graphics2D.*; import java.awt.geom.Line2D; import java.awt.geom.Line2D.Float; /** Grid will take a file name from the GridGIS class. It opens the file * and reads the header, storing the number of columns and rows, then reads * the data points and stores them in an array and a Vector of elevations. * It determines the quartiles and assigns those certain colors. It then * draws the map on the JFrame by going through each point and determining * where it can send lines, and what the colors should be. * * @author Elizabeth French * @version 04/04/08 */ public class Grid { // number of header lines to sort through before you hit the "data" private static final int HEADER_LINES = 6; //r,g,b values for the lowest group on the grid private static final int R_LOW = 0, G_LOW = 100, B_LOW = 0; //Changes in values of r,g,b as the grouping changes private static final int R_CHANGE = 50, G_CHANGE = -20, B_CHANGE = 15; //Colors assigned to quartiles. Use a formula based on above def'ns. private static final Color COLOR_Q1 = new Color(R_LOW, G_LOW, B_LOW); private static final Color COLOR_Q2 = new Color(R_LOW+R_CHANGE, G_LOW+G_CHANGE, B_LOW+B_CHANGE); private static final Color COLOR_Q3 = new Color(R_LOW+2*R_CHANGE, G_LOW+2*G_CHANGE, B_LOW+2*B_CHANGE); private static final Color COLOR_Q4 = new Color(R_LOW+3*R_CHANGE, G_LOW+3*G_CHANGE, B_LOW+3*B_CHANGE); //Color to return if elevation is negative private static final Color WATER = Color.blue; //Elevations stored in correct place with int[][] private int[][] grid; //Number of rows, cols, and total points in the grid private int rows, cols, numPts; //5 number summary of the elevations private int min, max, q1, q2, q3; //Stretch needed to fit the grid coords to the window private float stretchHoriz, stretchVert; /** Method to load, open and read a file. It uses a scanner to read the * file and save the relevant information (ncols, nrows, and elevations) * @param fileName, the name of the file to open * squareWindowSize the size of the square window */ public Grid(String fileName, int squareWindowSize) { Scanner sc; File gridFile; Vector allElevs; // open the file gridFile = new File(fileName); sc = null; try { sc = new Scanner(gridFile); } catch (FileNotFoundException e) { System.err.println("File not found!"); System.exit(0); } //go through the header one line at a time for (int i=0; i(); //The points in the grid and in a vector of elevations int pt = 0; while (sc.hasNextInt()) { int nextOne = sc.nextInt(); //Store elevations in grid coords grid[pt/cols][pt%cols] = nextOne; allElevs.add(nextOne); pt++; } //Calculate the stretch values and elevation range of the file stretchHoriz = (float)(squareWindowSize/(float)(cols-1)); stretchVert = (float)(squareWindowSize/(float)(rows-1)); quickSort(allElevs, 0, numPts-1); min = allElevs.elementAt(0); q1 = allElevs.elementAt(numPts/4); q2 = allElevs.elementAt(numPts/2); q3 = allElevs.elementAt(3*numPts/4); max = allElevs.elementAt(numPts - 1); //Legend to the map System.out.println("Q1 is from " +min + " to " + q1 +" and is green" +"\nQ2 is from " + q1 + " to " + q2 +"\nQ3 is from " + q2 + " to " + q3 +"\nQ4 is from " + q3 + " to " + max + " and is deep red"); } /** Method to return the average height of the two endpoints. Will * @param pt1, pt2 the endpoints * @return double ave the average height of the endpoints */ public double aveHeight(int pt1, int pt2) { //Calculate average double ave = ( (double)( grid[pt1/cols][pt1%cols] + grid[pt2/cols][pt2%cols]) ) /2.0; return ave; } /** Method to determine what color a line should be, given the average * height of the endpoints. Uses int defined colors rather than floats, * because it made more sense with my method. Sorry. * @param aveHeight the average height of the endpoints * @return color the appropriate color for the average elevation */ public Color getColor(double aveElev) { if (aveElev <= 0) return WATER; Color color; //If there is no variation, just show a gray page if (min == max) return Color.GRAY; //Return colors based on the quartile of the average height if ( aveElev < q1 ) return COLOR_Q1; if ( aveElev < q2 ) return COLOR_Q2; if ( aveElev < q3 ) return COLOR_Q3; else return COLOR_Q4; } /** Method to draw a line from start to end with the appropriate color * uses "getColor" and relies on the global "start" and "end" variables * Lines are floats. * @param g Graphics * @return void */ public void draw(Graphics2D g2, float startX, float startY, int end) { //"Triangulating" line Line2D.Float ln; //float coords of the end float endX = (end%cols)*stretchHoriz; float endY = (end/cols)*stretchVert; ln = new Line2D.Float(startX, startY, endX, endY); g2.draw(ln); } /** Method to be called from GridGIS. It will take the 2D graphics * and then go through each point and determine which lines (across, * down, and diag) it can go to, depending on if the points are valid * If it determines that it is legal to draw a line, it calls on * draw() to get color and draw it * @param g2 Graohics2d * @return void */ public void drawGrid(Graphics2D g2) { int end; for (int i = 0; i < cols*rows-1; i++) { float startX = (i%cols)*stretchHoriz; float startY = (i/cols)*stretchVert; //Draw a line to next in row if not in last col if (isNotLastCol(i)) { end = i + 1; g2.setColor( getColor(aveHeight(i, end)) ); draw(g2, startX, startY, end); } //Draw line to point beneath if not in last row if (isNotBottom(i)) { end = i + cols; g2.setColor( getColor(aveHeight(i, end)) ); draw(g2, startX, startY, end); // for points not in first col, connect diag down/left if (isNotFirstCol(i)) { end = i + cols - 1; g2.setColor( getColor(aveHeight(i, end)) ); draw(g2, startX, startY, end); } } /* if (isNotBottom(i)) */ } } /** Methods to help determine which lines are valid * for the points to create */ //Return true if the pt is not in the first column public boolean isNotFirstCol(int pt) { boolean notFirst = (pt % cols != 0); return notFirst; } //Return true if the pt is not in the last column public boolean isNotLastCol(int pt) { boolean notLast = (pt % cols != (cols-1)); return notLast; } //Return true if the pt is in the last row public boolean isNotBottom(int pt) { boolean notBottom = (pt/cols < rows-1); return notBottom; } /** These methods were taken from the Boggle Lab ( @BogglePlayer) * Authors and copyright below. * * These methods will take the vector with all of the elevation data * and sort it into increasing order. This helps to find the quartiles * for the elevations. * * Method info: * @author Michael Green * @author Paul Kube * @author Eric Chown * @author Laura Toma * * Copyright (C) 2002 Michael Green * Copyright (C) 2002 Paul Kube * Copyright (C) 2003/5 Eric Chown * Copyright (C) 2004 Pat Costello */ /* This method sorts a vector of Integers using Quicksort. * @param data the Vector to be sorted * low the leftmost boundary of the Vector to be sorted * high the rightmost boundary of the Vector to be sorted * @return void */ public void quickSort(Vector dat, int low, int high) { // Base case if (low >= high) return; // partition the Vector into two halves int pivot = partition(dat, low, high); // recursive calls to sort the two parts quickSort(dat, low, pivot - 1); quickSort(dat, pivot + 1, high); } /* Quicksort helper method that partitions a Vector into two * halves based on a "pivot." All the elements less than the * pivot are placed in the left half, all the rest are in the * right half. * @param data The Vector being sorted * left The leftmost boundary * right The rightmost boundary * @return the location of the pivot in the Vector */ public int partition(Vector data, int left, int right) { // left and right represent the boundaries of elements we // haven't partitioned Our goal is to get to all of them // moving partitioned elements to either side as necessary. while (true) { // move right "pointer" toward left while (left < right &&((Integer)data.elementAt(left)) .compareTo(((Integer)data.elementAt(right))) < 0) { right--; } if (left < right) swap(data,left++,right); else return left; // move left pointer toward right while (left < right &&((Integer)data.elementAt(left)) .compareTo(((Integer)data.elementAt(right))) < 0) { left++; } if (left < right) swap(data,left,right--); else return right; } } /* This method swaps two elements in a Vector (regardless of their type) * @param data The vector * i The position of one element * j The position of the other element * @return void */ public void swap(Vector data, int i, int j) { int temp; temp = (int)data.elementAt(i); data.setElementAt(data.elementAt(j), i); data.setElementAt(temp, j); } }