/* * GeoTools - The Open Source Java GIS Toolkit * http://geotools.org * * (C) 2010, Open Source Geospatial Foundation (OSGeo) * * This library 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; * version 2.1 of the License. * * This library 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. */ package org.geotools.grid; import org.geotools.geometry.jts.ReferencedEnvelope; /** * A helper class to create bounding envelopes with width and height that are * simple multiples of a given resolution. * <pre><code> * // Example of use: creating an envlope with 'neat' lat-lon bounds * // that encompasses a set of features * * SimpleFeatureSource featureSource = ... * ReferencedEnvelope featureBounds = featureSource.getBounds(); * ReferencedEnvelope latLonEnv = featureBounds.transform(DefaultGeographicCRS.WGS84, true); * * // 1 degree grid resoluation * final double gridSize = 1.0; * ReferencedEnvelope gridBounds = Envelopes.expandToInclude(latLonEnv, gridSize); * </code></pre> * * @todo move this class or its methods to a more general module * * @author mbedward * @since 2.7 * * * @source $URL$ * @version $Id$ */ public class Envelopes { private static final double EPS = 1.0E-8; /** * Include the provided envelope, expanding as necessary and rounding * the bounding coordinates such that they are multiples of the * specified resolution. For example, if {@code resolution} is 100 then the * min and max bounding coordinates of this envelope will set to mutliples * of 100 by rounding down the min values and rounding up the max values * if required. * <pre><code> * // Example, create a new envelope that cntains an input envlope and * // whose boundind coordinates are multiples of 100 * // * ReferencedEnvelope inputEnv = ... * ReferencedEnvelope roundedEnv = new ReferencedEnvelope(); * roundedEnv.expandToInclude(inputEnv, 100); * </code></pre> * * @param srcEnv the envelope to include * @param resolution resolution (in world distance units) of the resulting * boundary coordinates * * @return a new envelope with 'rounded' bounding coordinates */ public static ReferencedEnvelope expandToInclude(ReferencedEnvelope srcEnv, double resolution) { double minX = roundOrdinate(srcEnv.getMinX(), resolution, false); double maxX = roundOrdinate(srcEnv.getMaxX(), resolution, true); double minY = roundOrdinate(srcEnv.getMinY(), resolution, false); double maxY = roundOrdinate(srcEnv.getMaxY(), resolution, true); ReferencedEnvelope expanded = new ReferencedEnvelope(srcEnv); expanded.expandToInclude(minX, minY); expanded.expandToInclude(maxX, maxY); return expanded; } /** * Helper method to round ordinate values up or down to a specified resolution. * The returned value will be a multiple of the specified resolution. * <pre><code> * double ordinate = 1234.56; * double resolution = 100; * double rounded; * * // this will return 1200 * rounded = roundOrdinate(ordinate, resolution, false); * * // this will return 1300 * rounded = roundOrdinate(ordinate, resolution, true); * </code></pre> * @param ordinate the ordinate to round up or down. * @param resolution the desired resolution * @param roundUp true to round up; false to round down * * @return the rounded ordinate value */ private static double roundOrdinate(double ordinate, double resolution, boolean roundUp) { double unsigned = Math.abs(ordinate); boolean negative = ordinate < 0.0; if (negative) { roundUp = !roundUp; } double rounded; if (roundUp) { double x = unsigned / resolution; int up = (x - (long) x) > EPS ? 1 : 0; rounded = resolution * (up + (long) x); } else { rounded = resolution * (long) (unsigned / resolution); } return negative ? -rounded : rounded; } }