From ae8110acf60b21b2b09a252dfe54f5d54fe293fd Mon Sep 17 00:00:00 2001 From: Ben Steffensmeier Date: Wed, 17 Jul 2013 17:32:38 -0500 Subject: [PATCH] Issue #2185 Cache computed grid reprojections. Former-commit-id: 891f2241dd238b2901f8c1daee8f2236558f42e0 --- .../ext/KmlGroundOverlayGenerator.java | 35 +--- .../viz/grid/rsc/general/GeneralGridData.java | 7 +- .../uf/common/dataplugin/gfe/RemapGrid.java | 17 +- .../interpolation/GridReprojection.java | 57 +------ .../PrecomputedGridReprojection.java | 151 ++++++++++++++++++ 5 files changed, 173 insertions(+), 94 deletions(-) create mode 100644 edexOsgi/com.raytheon.uf.common.geospatial/src/com/raytheon/uf/common/geospatial/interpolation/PrecomputedGridReprojection.java diff --git a/cave/com.raytheon.uf.viz.kml.export/src/com/raytheon/uf/viz/kml/export/graphics/ext/KmlGroundOverlayGenerator.java b/cave/com.raytheon.uf.viz.kml.export/src/com/raytheon/uf/viz/kml/export/graphics/ext/KmlGroundOverlayGenerator.java index 6efe5367aa..b7b3a580d7 100644 --- a/cave/com.raytheon.uf.viz.kml.export/src/com/raytheon/uf/viz/kml/export/graphics/ext/KmlGroundOverlayGenerator.java +++ b/cave/com.raytheon.uf.viz.kml.export/src/com/raytheon/uf/viz/kml/export/graphics/ext/KmlGroundOverlayGenerator.java @@ -20,13 +20,8 @@ package com.raytheon.uf.viz.kml.export.graphics.ext; import java.awt.image.RenderedImage; -import java.lang.ref.Reference; -import java.lang.ref.SoftReference; import java.nio.FloatBuffer; -import java.util.HashMap; -import java.util.Map; -import org.apache.commons.collections.keyvalue.MultiKey; import org.eclipse.swt.graphics.RGB; import org.geotools.coverage.grid.GridEnvelope2D; import org.geotools.coverage.grid.GridGeometry2D; @@ -41,6 +36,7 @@ import com.raytheon.uf.common.geospatial.interpolation.BilinearInterpolation; import com.raytheon.uf.common.geospatial.interpolation.GridReprojection; import com.raytheon.uf.common.geospatial.interpolation.Interpolation; import com.raytheon.uf.common.geospatial.interpolation.NearestNeighborInterpolation; +import com.raytheon.uf.common.geospatial.interpolation.PrecomputedGridReprojection; import com.raytheon.uf.common.geospatial.interpolation.data.DataSource; import com.raytheon.uf.common.geospatial.interpolation.data.FloatArrayWrapper; import com.raytheon.uf.viz.core.DrawableImage; @@ -62,7 +58,8 @@ import de.micromata.opengis.kml.v_2_2_0.LatLonBox; * * Date Ticket# Engineer Description * ------------ ---------- ----------- -------------------------- - * Jun 14, 2012 bsteffen Initial creation + * Jun 14, 2012 bsteffen Initial creation + * Jul 17, 2013 2185 bsteffen Cache computed grid reprojections. * * * @@ -72,8 +69,6 @@ import de.micromata.opengis.kml.v_2_2_0.LatLonBox; public abstract class KmlGroundOverlayGenerator extends KmlFeatureGenerator { - private static Map> reprojCache = new HashMap>(); - protected final double alpha; protected final DrawableImage[] images; @@ -133,27 +128,7 @@ public abstract class KmlGroundOverlayGenerator extends KmlFeatureGenerator { } protected GridReprojection getReprojection(GridGeometry2D src, - GridGeometry2D dest) throws FactoryException, TransformException { - MultiKey key = new MultiKey(src, dest); - GridReprojection reproj = null; - boolean needsCompute = false; - synchronized (reprojCache) { - Reference reprojRef = reprojCache.get(key); - if (reprojRef != null) { - reproj = reprojRef.get(); - } - if (reproj == null) { - reproj = new GridReprojection(src, dest); - needsCompute = true; - reprojCache.put(key, - new SoftReference(reproj)); - } - } - synchronized (reproj) { - if (needsCompute) { - reproj.computeTransformTable(); - } - } - return reproj; + GridGeometry2D dest) throws TransformException { + return PrecomputedGridReprojection.getReprojection(src, dest); } } diff --git a/cave/com.raytheon.viz.grid/src/com/raytheon/viz/grid/rsc/general/GeneralGridData.java b/cave/com.raytheon.viz.grid/src/com/raytheon/viz/grid/rsc/general/GeneralGridData.java index a199f7f0e7..63b34bc948 100644 --- a/cave/com.raytheon.viz.grid/src/com/raytheon/viz/grid/rsc/general/GeneralGridData.java +++ b/cave/com.raytheon.viz.grid/src/com/raytheon/viz/grid/rsc/general/GeneralGridData.java @@ -41,6 +41,7 @@ import com.raytheon.uf.common.geospatial.MapUtil; import com.raytheon.uf.common.geospatial.interpolation.GridReprojection; import com.raytheon.uf.common.geospatial.interpolation.GridSampler; import com.raytheon.uf.common.geospatial.interpolation.Interpolation; +import com.raytheon.uf.common.geospatial.interpolation.PrecomputedGridReprojection; import com.raytheon.uf.common.geospatial.interpolation.data.FloatArrayWrapper; import com.raytheon.uf.common.geospatial.interpolation.data.FloatBufferWrapper; import com.vividsolutions.jts.geom.Coordinate; @@ -56,7 +57,8 @@ import com.vividsolutions.jts.geom.Coordinate; * * Date Ticket# Engineer Description * ------------ ---------- ----------- -------------------------- - * Mar 9, 2011 bsteffen Initial creation + * Mar 09, 2011 bsteffen Initial creation + * Jul 17, 2013 2185 bsteffen Cache computed grid reprojections. * * * @@ -217,7 +219,8 @@ public class GeneralGridData { Interpolation interpolation) throws FactoryException, TransformException { GridGeometry2D newGeom = GridGeometry2D.wrap(newGridGeometry); - GridReprojection reproj = new GridReprojection(gridGeometry, newGeom); + GridReprojection reproj = PrecomputedGridReprojection.getReprojection( + gridGeometry, newGeom); GridSampler sampler = new GridSampler(interpolation); if (isVector()) { sampler.setSource(new FloatBufferWrapper(getUComponent(), diff --git a/edexOsgi/com.raytheon.uf.common.dataplugin.gfe/src/com/raytheon/uf/common/dataplugin/gfe/RemapGrid.java b/edexOsgi/com.raytheon.uf.common.dataplugin.gfe/src/com/raytheon/uf/common/dataplugin/gfe/RemapGrid.java index eb7e0e1cf5..72ea0cddd8 100644 --- a/edexOsgi/com.raytheon.uf.common.dataplugin.gfe/src/com/raytheon/uf/common/dataplugin/gfe/RemapGrid.java +++ b/edexOsgi/com.raytheon.uf.common.dataplugin.gfe/src/com/raytheon/uf/common/dataplugin/gfe/RemapGrid.java @@ -46,6 +46,7 @@ import com.raytheon.uf.common.geospatial.MapUtil; import com.raytheon.uf.common.geospatial.interpolation.BilinearInterpolation; import com.raytheon.uf.common.geospatial.interpolation.GridReprojection; import com.raytheon.uf.common.geospatial.interpolation.NearestNeighborInterpolation; +import com.raytheon.uf.common.geospatial.interpolation.PrecomputedGridReprojection; import com.raytheon.uf.common.geospatial.interpolation.data.ByteBufferWrapper; import com.raytheon.uf.common.geospatial.interpolation.data.DataSource; import com.raytheon.uf.common.geospatial.interpolation.data.FloatArrayWrapper; @@ -60,9 +61,11 @@ import com.vividsolutions.jts.geom.Coordinate; * SOFTWARE HISTORY * Date Ticket# Engineer Description * ------------ ---------- ----------- -------------------------- - * 5/16/08 875 bphillip Initial Creation. - * 10/10/12 #1260 randerso Added getters for source and destination glocs - * 02/19/13 #1637 randerso Fixed remapping of byte grids + * May 16, 2008 875 bphillip Initial Creation. + * Oct 10, 2012 1260 randerso Added getters for source and destination + * glocs + * Feb 19, 2013 1637 randerso Fixed remapping of byte grids + * Jul 17, 2013 2185 bsteffen Cache computed grid reprojections. * * * @@ -462,8 +465,8 @@ public class RemapGrid { GridGeometry2D destGeometry = MapUtil.getGridGeometry(destinationGloc); synchronized (this) { if (interp == null) { - interp = new GridReprojection(sourceGeometry, destGeometry); - interp.computeTransformTable(); + interp = PrecomputedGridReprojection.getReprojection( + sourceGeometry, destGeometry); } } DataSource source = new ByteBufferWrapper(data, sourceGeometry); @@ -540,8 +543,8 @@ public class RemapGrid { .getGridGeometry(destinationGloc); synchronized (this) { if (interp == null) { - interp = new GridReprojection(sourceGeometry, destGeometry); - interp.computeTransformTable(); + interp = PrecomputedGridReprojection.getReprojection( + sourceGeometry, destGeometry); } } DataSource source = new FloatArrayWrapper(data, sourceGeometry); diff --git a/edexOsgi/com.raytheon.uf.common.geospatial/src/com/raytheon/uf/common/geospatial/interpolation/GridReprojection.java b/edexOsgi/com.raytheon.uf.common.geospatial/src/com/raytheon/uf/common/geospatial/interpolation/GridReprojection.java index a3fc1aaef6..cb4272d2b5 100644 --- a/edexOsgi/com.raytheon.uf.common.geospatial/src/com/raytheon/uf/common/geospatial/interpolation/GridReprojection.java +++ b/edexOsgi/com.raytheon.uf.common.geospatial/src/com/raytheon/uf/common/geospatial/interpolation/GridReprojection.java @@ -46,7 +46,8 @@ import com.raytheon.uf.common.geospatial.interpolation.data.DataSource; * * Date Ticket# Engineer Description * ------------ ---------- ----------- -------------------------- - * Jun 18, 2012 bsteffen Initial creation + * Jun 18, 2012 bsteffen Initial creation + * Jul 17, 2013 2185 bsteffen Cache computed grid reprojections. * * * @@ -66,8 +67,6 @@ public class GridReprojection { protected MathTransform transform; - protected float[] transformTable = null; - public GridReprojection(GeneralGridGeometry sourceGeometry, GeneralGridGeometry targetGeometry) { this.sourceGeometry = sourceGeometry; @@ -128,63 +127,11 @@ public class GridReprojection { protected Point2D.Double getReprojectDataPoint(int x, int y) throws TransformException, FactoryException { initTransforms(); - if (transformTable != null && x >= 0 && x < targetNx && y >= 0 - && y < targetNy) { - int index = (y * targetNx + x) * 2; - float xVal = transformTable[index]; - float yVal = transformTable[index + 1]; - if (!Float.isNaN(xVal) && !Float.isNaN(yVal)) { - return new Point2D.Double(xVal, yVal); - } - } DirectPosition2D dp = new DirectPosition2D(x, y); transform.transform(dp, dp); return dp; } - /** - * This function precomputes the math transform for all grid cells in the - * target grid range. This method is recommended when you are reprojecting - * multiple datasets using the same interpolation. Precalculating this table - * takes time and uses more memory but cuts the time to perform - * interpolation significantly. - * - * - * @return the size in bytes of the extra memory used by the transform - * table. - * @throws FactoryException - * @throws TransformException - */ - public int computeTransformTable() throws FactoryException, - TransformException { - initTransforms(); - float[] transformTable = new float[targetNy * targetNx * 2]; - int index = 0; - for (int j = 0; j < targetNy; j++) { - for (int i = 0; i < targetNx; i++) { - transformTable[index++] = i; - transformTable[index++] = j; - } - } - try { - transform.transform(transformTable, 0, transformTable, 0, targetNy - * targetNx); - } catch (ProjectionException e) { - ;// Ignore, the points in the transformTable that are invalid are - // set to NaN, no other action is necessary. - } - this.transformTable = transformTable; - return transformTable.length * 4; - } - - /** - * delete the transform table, freeing up memory but slowing down any future - * reprojections - */ - public void clearTransformTable() { - transformTable = null; - } - public GeneralGridGeometry getSourceGeometry() { return sourceGeometry; } diff --git a/edexOsgi/com.raytheon.uf.common.geospatial/src/com/raytheon/uf/common/geospatial/interpolation/PrecomputedGridReprojection.java b/edexOsgi/com.raytheon.uf.common.geospatial/src/com/raytheon/uf/common/geospatial/interpolation/PrecomputedGridReprojection.java new file mode 100644 index 0000000000..cec7292399 --- /dev/null +++ b/edexOsgi/com.raytheon.uf.common.geospatial/src/com/raytheon/uf/common/geospatial/interpolation/PrecomputedGridReprojection.java @@ -0,0 +1,151 @@ +/** + * This software was developed and / or modified by Raytheon Company, + * pursuant to Contract DG133W-05-CQ-1067 with the US Government. + * + * U.S. EXPORT CONTROLLED TECHNICAL DATA + * This software product contains export-restricted data whose + * export/transfer/disclosure is restricted by U.S. law. Dissemination + * to non-U.S. persons whether in the United States or abroad requires + * an export license or other authorization. + * + * Contractor Name: Raytheon Company + * Contractor Address: 6825 Pine Street, Suite 340 + * Mail Stop B8 + * Omaha, NE 68106 + * 402.291.0100 + * + * See the AWIPS II Master Rights File ("Master Rights File.pdf") for + * further licensing information. + **/ +package com.raytheon.uf.common.geospatial.interpolation; + +import java.awt.geom.Point2D; +import java.lang.ref.Reference; +import java.lang.ref.SoftReference; +import java.util.HashMap; +import java.util.Map; + +import org.apache.commons.collections.keyvalue.MultiKey; +import org.geotools.coverage.grid.GeneralGridGeometry; +import org.geotools.referencing.operation.projection.ProjectionException; +import org.opengis.referencing.FactoryException; +import org.opengis.referencing.operation.TransformException; + +/** + * A GridReprojection which precomputes the coordinates of all the grid cells so + * that multiple reprojects will be much faster. This implements a memory/time + * tradeoff, using much more memory than an ordinary GridReprojection so that it + * is able to reproject much faster. Because of the high memory usage all + * instances are cached so they can be shared for identical reprojections. + * + * The current caching implementation uses soft references. When the + * reprojection is no longer referenced then the memory will be reclaimed by the + * JVM as needed. + * + *
+ * 
+ * SOFTWARE HISTORY
+ * 
+ * Date         Ticket#    Engineer    Description
+ * ------------ ---------- ----------- --------------------------
+ * Jul 17, 2013 2185       bsteffen    Initial creation
+ * 
+ * 
+ * + * @author bsteffen + * @version 1.0 + */ +public class PrecomputedGridReprojection extends GridReprojection { + + protected float[] transformTable; + + protected PrecomputedGridReprojection(GeneralGridGeometry sourceGeometry, + GeneralGridGeometry targetGeometry) { + super(sourceGeometry, targetGeometry); + } + + /** + * This function precomputes the math transform for all grid cells in the + * target grid range. Precalculating this table takes time and uses more + * memory but cuts the time to perform interpolation significantly. + * + * @throws TransformException + */ + protected void computeTransformTable() throws TransformException { + try { + initTransforms(); + } catch (FactoryException e) { + throw new TransformException("Error preparing transform.", e); + } + float[] transformTable = new float[targetNy * targetNx * 2]; + int index = 0; + for (int j = 0; j < targetNy; j++) { + for (int i = 0; i < targetNx; i++) { + transformTable[index++] = i; + transformTable[index++] = j; + } + } + try { + transform.transform(transformTable, 0, transformTable, 0, targetNy + * targetNx); + } catch (ProjectionException e) { + ;// Ignore the points in the transformTable that are + // invalid are set to NaN, no other action is necessary. + } + this.transformTable = transformTable; + } + + @Override + protected Point2D.Double getReprojectDataPoint(int x, int y) + throws TransformException, FactoryException { + if (x >= 0 && x < targetNx && y >= 0 && y < targetNy) { + int index = (y * targetNx + x) * 2; + float xVal = transformTable[index]; + float yVal = transformTable[index + 1]; + if (!Float.isNaN(xVal) && !Float.isNaN(yVal)) { + return new Point2D.Double(xVal, yVal); + } + } + return super.getReprojectDataPoint(x, y); + } + + private static final Map> cache = new HashMap>(); + + /** + * Get a shared GridReprojection. This reprojection will have the transform + * table computed. + * + * @param sourceGeometry + * @param targetGeometry + * @return + * @throws FactoryException + * @throws TransformException + */ + public static PrecomputedGridReprojection getReprojection( + GeneralGridGeometry sourceGeometry, + GeneralGridGeometry targetGeometry) throws TransformException { + PrecomputedGridReprojection reprojection = null; + boolean created = false; + MultiKey key = new MultiKey(sourceGeometry, targetGeometry); + synchronized (cache) { + Reference ref = cache.get(key); + if (ref != null) { + reprojection = ref.get(); + } + if (reprojection == null) { + reprojection = new PrecomputedGridReprojection(sourceGeometry, + targetGeometry); + created = true; + cache.put(key, new SoftReference( + reprojection)); + } + } + synchronized (reprojection) { + if (created) { + reprojection.computeTransformTable(); + } + } + return reprojection; + } + +}