/* * CartogramMapping.java * * Copyright (c) 2002-2015 Alexei Drummond, Andrew Rambaut and Marc Suchard * * This file is part of BEAST. * See the NOTICE file distributed with this work for additional * information regarding copyright ownership and licensing. * * BEAST is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * BEAST is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with BEAST; if not, write to the * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, * Boston, MA 02110-1301 USA */ package dr.geo.cartogram; import dr.util.FileHelpers; import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; import java.io.*; import java.util.StringTokenizer; import java.util.zip.GZIPInputStream; /** * Provides basic functionality for output from Mark Newman's 'cartogram' * - Reads resulting flat text file * - Uses bilinear interpolation to map (real space x, real space y) -> (cartogram x, cartogram y) * * @author Marc A. Suchard */ public class CartogramMapping { public static final String CARTOGRAM_OUTPUT = "cartogram"; public CartogramMapping(int gridXSize, int gridYSize, Rectangle2D boundingBox) { this.gridXSize = gridXSize; this.gridYSize = gridYSize; this.boundingBox = boundingBox; gridPt = new Point2D[gridXSize + 1][gridYSize + 1]; dX = (boundingBox.getMaxX() - boundingBox.getMinX()) / gridXSize; dY = (boundingBox.getMaxY() - boundingBox.getMinY()) / gridYSize; } public double getAverageDensity() { return averageDensity; } public void setAverageDensity(double d) { averageDensity = d; } public void readCartogramOutput(String fileName) throws IOException { readCartogramOutput(FileHelpers.getFile(fileName)); } public void readCartogramOutput(File file) throws IOException { BufferedReader reader; if (file.getName().endsWith("gz")) reader = new BufferedReader (new InputStreamReader (new GZIPInputStream (new FileInputStream(file)))); else reader = new BufferedReader(new FileReader(file)); // Newman stores values in column-major for (int y = 0; y <= gridYSize; y++) { for (int x = 0; x <= gridXSize; x++) { String line = reader.readLine(); if (line == null) throw new IOException("Premature end of file in '" + file + "'"); StringTokenizer st = new StringTokenizer(line); try { double mappedX = Double.parseDouble(st.nextToken()); double mappedY = Double.parseDouble(st.nextToken()); gridPt[x][y] = new Point2D.Double(mappedX, mappedY); } catch (NumberFormatException e) { throw new IOException("Unable to parse line: " + line + " in '" + file + "'"); } } } reader.close(); loaded = true; } public String toString() { StringBuilder sb = new StringBuilder(); sb.append("grid: [x=" + gridXSize + ",y=" + gridYSize + "], boundingBox: " + boundingBox.toString()); return sb.toString(); } public Point2D map(Point2D inPt) { if (!boundingBox.contains(inPt)) return null; if (!loaded) return inPt; final double offsetX = (inPt.getX() - boundingBox.getMinX()) / dX; final double offsetY = (inPt.getY() - boundingBox.getMinY()) / dY; final int iX = (int) offsetX; final int iY = (int) offsetY; final double rX = offsetX - iX; final double rY = offsetY - iY; assert (rX >= 0 && rY < 1.0 && rY >= 0 && rY < 1.0); final Point2D gridiXiY = gridPt[iX][iY]; final Point2D gridiX1iY = gridPt[iX + 1][iY]; final Point2D gridiXiY1 = gridPt[iX][iY + 1]; final Point2D gridiX1iY1 = gridPt[iX + 1][iY + 1]; final double outX = (1 - rX) * (1 - rY) * gridiXiY.getX() + rX * (1 - rY) * gridiX1iY.getX() + (1 - rX) * rY * gridiXiY1.getX() + rX * rY * gridiX1iY1.getX(); final double outY = (1 - rX) * (1 - rY) * gridiXiY.getY() + rX * (1 - rY) * gridiX1iY.getY() + (1 - rX) * rY * gridiXiY1.getY() + rX * rY * gridiX1iY1.getY(); return new Point2D.Double(outX, outY); } /* * This should replicate the behavoir of Newman's 'interp' program */ public static void main(String[] args) { int gridXSize = Integer.parseInt(args[0]); int gridYSize = Integer.parseInt(args[1]); String fileName = args[2]; CartogramMapping mapping = new CartogramMapping(gridXSize, gridYSize, new Rectangle2D.Double(0, 0, gridXSize, gridYSize)); try { mapping.readCartogramOutput(fileName); BufferedReader reader = new BufferedReader(new InputStreamReader(System.in)); while (true) { String line = reader.readLine(); if (line == null) { // end of file break; } else if (line.length() == 0) { break; } else { // not a blank line StringTokenizer st = new StringTokenizer(line); Point2D inPt = new Point2D.Double( Double.parseDouble(st.nextToken()), Double.parseDouble(st.nextToken()) ); System.out.println(mapping.map(inPt)); } } } catch (IOException e) { System.err.println(e); System.exit(-1); } } private Rectangle2D boundingBox; private int gridXSize, gridYSize; private double dX, dY; private Point2D[][] gridPt; private boolean loaded = false; private double averageDensity = 1.0; }