/* * 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.util.geo; import com.vividsolutions.jts.geom.Envelope; import java.awt.geom.AffineTransform; import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; import sim.display.Display2D; import sim.field.geo.GeomField; import sim.field.geo.GeomGridField; import sim.portrayal.DrawInfo2D; public class GeometryUtilities { /** * Determines the affine transform which converts world coordinates into * screen coordinates. Modified from GeoTools RenderUtilities.java. * * @param mapExtent * MBR in world coordinates mapped to viewport defined in 'info' * @param viewport * @return AffineTransform suitable for converting from world to screen * coordinates */ public static AffineTransform worldToScreenTransform(final Envelope mapExtent, final java.awt.geom.Rectangle2D.Double viewport) { double scaleX = viewport.width / mapExtent.getWidth(); double scaleY = viewport.height / mapExtent.getHeight(); double tx = -mapExtent.getMinX() * scaleX; double ty = (mapExtent.getMinY() * scaleY) + viewport.height; AffineTransform at = new AffineTransform(scaleX, 0.0d, 0.0d, -scaleY, tx, ty); AffineTransform originTranslation = AffineTransform.getTranslateInstance(viewport.x, viewport.y); originTranslation.concatenate(at); return originTranslation != null ? originTranslation : at; } public static com.vividsolutions.jts.geom.util.AffineTransformation getPortrayalTransform(final AffineTransform transform, final GeomField field, final Rectangle2D.Double view) { AffineTransform worldToScreen = transform; //if (worldToScreen.getScaleX() > 1 || worldToScreen.getScaleY() > 1) { // Envelope e = new Envelope(field.drawX, field.getFieldWidth(), field.drawY, field.getFieldHeight()); // worldToScreen = worldToScreenTransform(e, view); //} double m[] = new double[6]; worldToScreen.getMatrix(m); return new com.vividsolutions.jts.geom.util.AffineTransformation(m[0], m[2], m[4], m[1], m[3], m[5]); } /** * Determines the affine transform which converts world coordinates into * screen coordinates. Modified from GeoTools RenderUtilities.java. * * convenience variant of other worldToSceenTransform() * * @param mapExtent * MBR in world coordinates mapped to viewport defined in 'info' * @param info * defines the viewport dimensions * @return AffineTransform suitable for converting from world to screen * coordinates */ public static AffineTransform worldToScreenTransform(final Envelope mapExtent, final DrawInfo2D info) { return worldToScreenTransform(mapExtent, info.draw); } /** * Uses the worldToScreen transform to transform the point (x,y) in screen * coordinates to world coordinates. */ public static Point2D screenToWorldPointTransform(final AffineTransform worldToScreen, double x, double y) { // code taken from GeoTools and hacked on AffineTransform screenToWorld = null; try { screenToWorld = worldToScreen.createInverse(); } catch (Exception e) { System.out.println(e); System.exit(-1); } Point2D p = new Point2D.Double(); screenToWorld.transform(new Point2D.Double(x, y), p); return p; } /** * compute the MBR for the grid field in display coordinates * * This is used to determine the display bounds for the grid field for * Display2D.attach(). * * @param outer * denotes MBR that maps to display window in world coordinates * @param display * is the display into which the grid will be rendered * @param gridField * for which we wish to find bounds in display coordinates * * @return grid field bounds in display coordinates; will return viewport if * grid does not intersect given outer MBR */ static public java.awt.geom.Rectangle2D.Double computeBounds(final Envelope outer, final Display2D display, final GeomGridField gridField) { Display2D.InnerDisplay2D innerDisplay = display.insideDisplay; // Initialize bounds to that of display viewport java.awt.geom.Rectangle2D.Double bounds = new java.awt.geom.Rectangle2D.Double(innerDisplay.xOffset, innerDisplay.yOffset, innerDisplay.width, innerDisplay.height); AffineTransform transform = GeometryUtilities.worldToScreenTransform(outer, bounds); if (isWithinBounds(outer, gridField)) { // Pretty straightforward; just translate all the corners into // display coordinates Point2D.Double srcMinPoint = new Point2D.Double(gridField.MBR.getMinX(), gridField.MBR.getMaxY()); Point2D destMinPoint = transform.transform(srcMinPoint, null); Point2D.Double srcMaxPoint = new Point2D.Double(gridField.MBR.getMaxX(), gridField.MBR.getMinY()); Point2D destMaxPoint = transform.transform(srcMaxPoint, null); bounds.setRect(destMinPoint.getX(), destMinPoint.getY(), destMaxPoint.getX() - destMinPoint.getX(), destMaxPoint.getY() - destMinPoint.getY()); } else // badness happened { // not good if the grid isn't even within the outer MBR; this likely // means that 'outer' and 'gridField' are using different spatial // reference systems System.err.println("Warning: raster not in display"); } return bounds; } /** * @param outer * denotes MBR that maps to display window in world coordinates * @param gridField * for which we wish to find bounds in display coordinates * * @return true iff 'gridField' is within, intersects, or covers 'outer', * else returns false * * Can be used as check for computeBounds() */ static public boolean isWithinBounds(final Envelope outer, final GeomGridField gridField) { if (outer.contains(gridField.MBR) || gridField.MBR.contains(outer) || outer.intersects(gridField.MBR)) { return true; } return false; } }