/* * GeoTools - The Open Source Java GIS Toolkit * http://geotools.org * * (C) 2016, 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.gce.imagemosaic.egr; import java.awt.Rectangle; import java.awt.geom.AffineTransform; import java.awt.geom.NoninvertibleTransformException; import java.util.logging.Level; import java.util.logging.Logger; import org.geotools.coverage.grid.GridEnvelope2D; import org.geotools.geometry.Envelope2D; import org.geotools.geometry.jts.ReferencedEnvelope; import org.geotools.referencing.operation.builder.GridToEnvelopeMapper; import org.opengis.geometry.MismatchedDimensionException; import org.opengis.referencing.datum.PixelInCell; import com.vividsolutions.jts.geom.Envelope; /** * This is a reduced copy of RenderUtilities found in the render module, to avoid adding a dependency on it while using only a few methods */ public final class RendererUtilities { private final static Logger LOGGER = org.geotools.util.logging.Logging .getLogger(RendererUtilities.class.getName()); /** * Helper class for building affine transforms. We use one instance per thread, in order to avoid the need for {@code synchronized} statements. */ private static final ThreadLocal<GridToEnvelopeMapper> gridToEnvelopeMappers = new ThreadLocal<GridToEnvelopeMapper>() { @Override protected GridToEnvelopeMapper initialValue() { final GridToEnvelopeMapper mapper = new GridToEnvelopeMapper(); mapper.setPixelAnchor(PixelInCell.CELL_CORNER); return mapper; } }; /** * Utilities classes should not be instantiated. * */ private RendererUtilities() { }; /** * Sets up the affine transform * <p/> * * NOTE It is worth to note that here we do not take into account the half a pixel translation stated by ogc for coverages bounds. One reason is * that WMS 1.1.1 does not follow it!!! * * @param mapExtent the map extent * @param paintArea the size of the rendering output area * @return a transform that maps from real world coordinates to the screen */ public static AffineTransform worldToScreenTransform(ReferencedEnvelope mapExtent, Rectangle paintArea) { // // // // Convert the JTS envelope and get the transform // // // final Envelope2D genvelope = new Envelope2D(mapExtent); // // // // Get the transform // // // final GridToEnvelopeMapper m = (GridToEnvelopeMapper) gridToEnvelopeMappers.get(); try { m.setGridRange(new GridEnvelope2D(paintArea)); m.setEnvelope(genvelope); return m.createAffineTransform().createInverse(); } catch (MismatchedDimensionException e) { LOGGER.log(Level.WARNING, e.getLocalizedMessage(), e); return null; } catch (NoninvertibleTransformException e) { LOGGER.log(Level.WARNING, e.getLocalizedMessage(), e); return null; } } /** * Creates the map's bounding box in real world coordinates. * * @param worldToScreen a transform which converts World coordinates to screen pixel coordinates. No assumptions are done on axis order as this is * assumed to be pre-calculated. The affine transform may specify an rotation, in case the envelope will encompass the complete (rotated) * world polygon. * @param paintArea the size of the rendering output area * @return the envelope in world coordinates corresponding to the screen rectangle. */ public static Envelope createMapEnvelope(Rectangle paintArea, AffineTransform worldToScreen) throws NoninvertibleTransformException { // // (X1,Y1) (X2,Y1) // // (X1,Y2) (X2,Y2) double[] pts = new double[8]; pts[0] = paintArea.getMinX(); pts[1] = paintArea.getMinY(); pts[2] = paintArea.getMaxX(); pts[3] = paintArea.getMinY(); pts[4] = paintArea.getMaxX(); pts[5] = paintArea.getMaxY(); pts[6] = paintArea.getMinX(); pts[7] = paintArea.getMaxY(); worldToScreen.inverseTransform(pts, 0, pts, 0, 4); double xMin = Double.MAX_VALUE; double yMin = Double.MAX_VALUE; double xMax = -Double.MAX_VALUE; double yMax = -Double.MAX_VALUE; for (int i = 0; i < 4; i++) { xMin = Math.min(xMin, pts[2 * i]); yMin = Math.min(yMin, pts[2 * i + 1]); xMax = Math.max(xMax, pts[2 * i]); yMax = Math.max(yMax, pts[2 * i + 1]); } return new Envelope(xMin, xMax, yMin, yMax); } }