// Author: Benson Limketkai (bensonl@cs.washington.edu) import java.awt.*; import java.util.*; import java.io.*; // draws the good ol' U-S-of-A with random red/blue coloring public class USA { public static final int EDGE_OFFSET = 10; public static final int HEIGHT = 450; public static final int WIDTH = 700; public static void main(String[] args) throws FileNotFoundException { DrawingPanel panel = new DrawingPanel(WIDTH, HEIGHT); Graphics g = panel.getGraphics(); Scanner input = new Scanner(new File("USA.txt")); String bounds = input.nextLine(); // minX, minY, maxX, maxY Scanner lineScan = new Scanner(bounds); double minX = lineScan.nextDouble(); double minY = lineScan.nextDouble(); double maxX = lineScan.nextDouble(); double maxY = lineScan.nextDouble(); // compute optimal multiplier and shifts double multiplier = computeOptimalMultiplier(minX, maxX, minY, maxY); double xShift = Math.toRadians(minX); double yShift = transformY(minY); int numPolygons = input.nextInt(); input.nextLine(); // in general, mixing line-based and token-based methods are // not a good idea; here, we need to explicitly consume the // newline character to go to the next line // for picking the color Random rand = new Random(); for (int i = 1; i <= numPolygons; i++) { System.out.println("#" + i); processPolygon(panel, g, rand, input, multiplier, xShift, yShift); } } // draws a polygon from the data file public static void processPolygon(DrawingPanel panel, Graphics g, Random rand, Scanner input, double multiplier, double xShift, double yShift) { input.nextLine(); // state name input.nextLine(); // text: USA int numPoints = input.nextInt(); // number of points Polygon p = new Polygon(); for (int i = 1; i <= numPoints; i++) { addPoint(p, input, multiplier, xShift, yShift); } input.nextLine(); // again, explicitly consume the newline character after // reading all the points // color state randomly between red and blue if (rand.nextInt(2) == 0) { g.setColor(Color.RED); } else { g.setColor(Color.BLUE); } panel.sleep(150); g.fillPolygon(p); // draw outline g.setColor(Color.BLACK); g.drawPolygon(p); } // adds a point (x, y) to the polygon public static void addPoint(Polygon p, Scanner input, double multiplier, double xShift, double yShift) { // in degrees double longitude = input.nextDouble(); double latitude = input.nextDouble(); double x = Math.toRadians(longitude); double y = transformY(latitude); int mapX = (int)((x - xShift) * multiplier) + EDGE_OFFSET; int mapY = (int)((y - yShift) * multiplier) + EDGE_OFFSET; // flip y-coordinate so country is not upside-down // add offset so that image is not flush with edge p.addPoint(mapX, (HEIGHT - mapY)); } // parameter is in degrees public static double transformY(double y) { // equirectangular projection (straight plotting of points) //return Math.toRadians(y); // Mercator projection (what people are used to seeing) // for more details: http://en.wikipedia.org/wiki/Mercator_projection return Math.log(Math.tan(0.25 * Math.PI + 0.5 * Math.toRadians(y))); } // returns the best multiplier to make the map take up as much of // the screen space as possible without any distortion public static double computeOptimalMultiplier(double minX, double maxX, double minY, double maxY) { double deltaX = Math.toRadians(maxX - minX); double xMultiplier = (WIDTH - 2 * EDGE_OFFSET) / deltaX; double deltaY = transformY(maxY) - transformY(minY); double yMultiplier = (HEIGHT - 2 * EDGE_OFFSET) / deltaY; System.out.println("minX = " + Math.toRadians(minX)); System.out.println("maxX = " + Math.toRadians(maxX)); System.out.println("minY = " + transformY(minY)); System.out.println("maxY = " + transformY(maxY)); System.out.println("xMultiplier = " + xMultiplier); System.out.println("yMultiplier = " + yMultiplier); return Math.min(xMultiplier, yMultiplier); } }