// 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 { // How much space to leave on edge of map. public static final int EDGE_OFFSET = 10; // Height of the drawing panel. public static final int HEIGHT = 450; // Width of the drawing panel. public static final int WIDTH = 950; // Filename where the data can be found. public static final String FILENAME = "WA.txt"; // Length of time to pause (in ms) between drawing polygons. public static final int PAUSE_BETWEEN_POLYGONS = 10; // Whether to use Mercator projection. If not, use equirectangular projection. public static final boolean USE_MERCATOR_PROJECTION = true; public static void main(String[] args) throws FileNotFoundException { DrawingPanel p = new DrawingPanel(WIDTH, HEIGHT); Graphics g = p.getGraphics(); Scanner input = new Scanner(new File(FILENAME)); 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 Random rand = new Random(); // for picking the color for (int i = 1; i <= numPolygons; i++) { System.out.println("#" + i); Polygon poly = readPolygon(input, multiplier, xShift, yShift); drawPolygon(g, poly, rand); p.sleep(PAUSE_BETWEEN_POLYGONS); } } // Draw polygon with random red/blue coloring. public static void drawPolygon(Graphics g, Polygon p, Random rand) { // color state randomly between red and blue if (rand.nextInt(2) == 0) { g.setColor(Color.RED); } else { g.setColor(Color.BLUE); } g.fillPolygon(p); // draw outline g.setColor(Color.BLACK); g.drawPolygon(p); } // Read a polygon's worth of data. public static Polygon readPolygon(Scanner input, double multiplier, double xShift, double yShift) { input.nextLine(); // discard state name input.nextLine(); // discard text: USA int numPoints = input.nextInt(); // number of points Polygon p = new Polygon(); for (int i = 1; i <= numPoints; i++) { processPoint(p, input, multiplier, xShift, yShift); } input.nextLine(); // again, explicitly consume the newline character after // reading all the points return p; } // Reads a point (x, y) and adds it to the polygon. public static void processPoint(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); // add offset so that image is not flush with edge 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 p.addPoint(mapX, (HEIGHT - mapY)); } // Parameter is in degrees public static double transformY(double y) { if (USE_MERCATOR_PROJECTION) { // 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))); } else { // equirectangular projection (straight plotting of points) return 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); } }