/* * Copyright 2011 by Mark Coletti, Keith Sullivan, Sean Luke, and * George Mason University Mason University Licensed under the Academic * Free License version 3.0 * * See the file "LICENSE" for more information * * $Id$ */ package sim.field.geo; import com.vividsolutions.jts.geom.*; import sim.field.grid.DoubleGrid2D; import sim.field.grid.Grid2D; import sim.field.grid.IntGrid2D; /** A georeferenced area represented by a grid * * The associated GeomField.MBR defines the area the grid maps to. * */ public class GeomGridField extends GeomField { private static final long serialVersionUID = 5804948960128647172L; /** * Used to determine the GeomGridField storage type. */ public enum GridDataType { INTEGER, DOUBLE } /** * * @return the data type of the grid cells; null if there is no assigned grid */ public GridDataType getGridDataType() { if (getGrid() instanceof IntGrid2D) { return GridDataType.INTEGER; } else if (getGrid() instanceof DoubleGrid2D) { return GridDataType.DOUBLE; } return null; } /** Grid container * * Allows for the user to use an arbitrary grid of integers, doubles, or objects. * * XXX Maybe consider Abstract2DGrid instead? But that would eliminate possibly * using Sparse2DGrid. * */ private Grid2D grid = null; public GeomGridField() { super(); } public GeomGridField(Grid2D wrappedGrid) { super(); setGrid(wrappedGrid); } /** width of grid point in projection coordinate system * * @see GeomGridField.setGrid() * @see GeomGridField.setMBR() */ private double pixelWidth = 0.0; /** height of grid point in projection coordinate system * * @see GeomGridField.setGrid() * @see GeomGridField.setMBR() */ private double pixelHeight = 0.0; /** Height of pixels in units of the underlying coordinate reference system */ public double getPixelHeight() { return pixelHeight; } /** Set heigh of pixels in units of the underlying coordinate reference system */ public void setPixelHeight(double pixelHeight) { this.pixelHeight = pixelHeight; } /** Width of pixels in units of the underlying coordinate reference system */ public double getPixelWidth() { return pixelWidth; } /** Set pixel width in units of underlying coordinate reference system */ public void setPixelWidth(double pixelWidth) { this.pixelWidth = pixelWidth; } public final Grid2D getGrid() { return grid; } public final void setGrid(Grid2D newGrid) { grid = newGrid; setPixelWidth(grid.getWidth()); setPixelHeight(grid.getHeight()); } /** * Returns width of grid in pixels. * <p> * By contrast, super.getWidth() returns width in spatial reference context. * * @return Returns width in grid points * * @throws NullPointerException if grid not assigned */ public final int getGridWidth() { return grid.getWidth(); } /** * By contrast, super.getHeight() returns width in spatial reference context. * * @return height of grid in pixels * * @throws NullPointerException if grid not assigned */ public final int getGridHeight() { return grid.getHeight(); } @Override public void clear() { super.clear(); this.grid = null; } @Override public void setMBR(Envelope MBR) { super.setMBR(MBR); // update pixelWidth and pixelHeight iff grid is set if (grid != null) { setPixelWidth(MBR.getWidth() / getGridWidth()); setPixelHeight(MBR.getHeight() / getGridHeight()); } } /** * @return true if (x,y) within grid boundaries */ public boolean isInGrid(int x, int y) { if ( x < 0 || y < 0 || x >= getGridWidth() || y >= getGridHeight() ) { return false; } return true; } /** * @param p point * @return x grid coordinate for cell 'p' is in */ public int toXCoord(final Point p) { return (int) Math.floor((p.getX() - getMBR().getMinX()) / getPixelWidth()); } /** * * @param x Coordinate in base projection * @return x grid coordinate for cell 'x' */ public int toXCoord(final double x) { return (int) Math.floor((x - getMBR().getMinX()) / getPixelWidth()); } /** * @param p point * @return y grid coordinate for cell 'p' is in */ public int toYCoord(final Point p) { // Note that we have to flip the y coordinate because the origin in // MASON is in the upper left corner. return (int) Math.floor((getMBR().getMaxY() - p.getY()) / getPixelHeight()); } /** * * @param y coordinate in base projection * @return y grid coordinate for cell 'y' is in */ public int toYCoord(final double y) { // Note that we have to flip the y coordinate because the origin in // MASON is in the upper left corner. return (int) Math.floor((getMBR().getMaxY() - y) / getPixelHeight()); } /** Return a Point corresponding to center of grid cell * * @param x in pixel coordinates * @param y in pixel coordinates * @return Point for center of given grid cell */ public Point toPoint(int x, int y) { assert x >= 0 && y >= 0 && x < getGridWidth() && y < getGridHeight() : "x: " + x + "y: " + y; if ( ! isInGrid(x,y) ) { throw new IndexOutOfBoundsException(); } // Invert the y axis to map from MASON coordinate system, which has // the origin in the upper left, to real world coordinate systems that // have the origin in the lower left. int inverted_y = getGridHeight() - y - 1; Coordinate coordinate = new Coordinate(); double x_orig = getMBR().getMinX(); double y_orig = getMBR().getMinY(); coordinate.x = x_orig + (x * getPixelWidth() + 0.5 * getPixelWidth()); coordinate.y = y_orig + (inverted_y * getPixelHeight() + 0.5 * getPixelHeight()); coordinate.z = 0.0; Point point = geometryFactory.createPoint(coordinate); return point; } /** Return a Polygon corresponding to the grid cell perimeter * * @param x in pixel coordinates * @param y in pixel coordinates * @return Polygon representing grid cell area */ public Polygon toPolygon(int x, int y) { assert x >= 0 && y >= 0 && x < getGridWidth() && y < getGridHeight() : "x: " + x + "y: " + y; if ( ! isInGrid(x,y) ) { throw new IndexOutOfBoundsException(); } Coordinate[] coordinates = new Coordinate[5]; double x_orig = getMBR().getMinX(); double y_orig = getMBR().getMinY(); // XXX getMaxY() instead? // Bottom left corner coordinates[0] = new Coordinate(); coordinates[0].x = x_orig + (x * getPixelWidth()); coordinates[0].y = y_orig + (y * getPixelHeight()); coordinates[0].z = 0.0; // Upper left corner coordinates[1] = new Coordinate(); coordinates[1].x = x_orig + (x * getPixelWidth()); coordinates[1].y = y_orig + ((y + 1) * getPixelHeight()); coordinates[1].z = 0.0; // Upper right corner coordinates[2] = new Coordinate(); coordinates[2].x = x_orig + ((x + 1) * getPixelWidth()); coordinates[2].y = y_orig + ((y + 1) * getPixelHeight()); coordinates[2].z = 0.0; // Finally, lower right corner coordinates[3] = new Coordinate(); coordinates[3].x = x_orig + ((x + 1) * getPixelWidth()); coordinates[3].y = y_orig + (y * getPixelHeight()); coordinates[3].z = 0.0; // Close the ring coordinates[4] = new Coordinate(); coordinates[4].x = x_orig + (x * getPixelWidth()); coordinates[4].y = y_orig + (y * getPixelHeight()); coordinates[4].z = 0.0; LinearRing ring = geometryFactory.createLinearRing(coordinates); Polygon polygon = geometryFactory.createPolygon(ring, null); // null means no inner rings; i.e., no hollow parts return polygon; } /** Used to create geometry as needed * * @see toPoint() */ private static GeometryFactory geometryFactory = new GeometryFactory(); }