Issue #2185 Cache computed grid reprojections.

Former-commit-id: 891f2241dd238b2901f8c1daee8f2236558f42e0
This commit is contained in:
Ben Steffensmeier 2013-07-17 17:32:38 -05:00
parent 08a90aa293
commit ae8110acf6
5 changed files with 173 additions and 94 deletions

View file

@ -20,13 +20,8 @@
package com.raytheon.uf.viz.kml.export.graphics.ext; package com.raytheon.uf.viz.kml.export.graphics.ext;
import java.awt.image.RenderedImage; import java.awt.image.RenderedImage;
import java.lang.ref.Reference;
import java.lang.ref.SoftReference;
import java.nio.FloatBuffer; 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.eclipse.swt.graphics.RGB;
import org.geotools.coverage.grid.GridEnvelope2D; import org.geotools.coverage.grid.GridEnvelope2D;
import org.geotools.coverage.grid.GridGeometry2D; 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.GridReprojection;
import com.raytheon.uf.common.geospatial.interpolation.Interpolation; import com.raytheon.uf.common.geospatial.interpolation.Interpolation;
import com.raytheon.uf.common.geospatial.interpolation.NearestNeighborInterpolation; 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.DataSource;
import com.raytheon.uf.common.geospatial.interpolation.data.FloatArrayWrapper; import com.raytheon.uf.common.geospatial.interpolation.data.FloatArrayWrapper;
import com.raytheon.uf.viz.core.DrawableImage; 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 * 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.
* *
* </pre> * </pre>
* *
@ -72,8 +69,6 @@ import de.micromata.opengis.kml.v_2_2_0.LatLonBox;
public abstract class KmlGroundOverlayGenerator extends KmlFeatureGenerator { public abstract class KmlGroundOverlayGenerator extends KmlFeatureGenerator {
private static Map<MultiKey, Reference<GridReprojection>> reprojCache = new HashMap<MultiKey, Reference<GridReprojection>>();
protected final double alpha; protected final double alpha;
protected final DrawableImage[] images; protected final DrawableImage[] images;
@ -133,27 +128,7 @@ public abstract class KmlGroundOverlayGenerator extends KmlFeatureGenerator {
} }
protected GridReprojection getReprojection(GridGeometry2D src, protected GridReprojection getReprojection(GridGeometry2D src,
GridGeometry2D dest) throws FactoryException, TransformException { GridGeometry2D dest) throws TransformException {
MultiKey key = new MultiKey(src, dest); return PrecomputedGridReprojection.getReprojection(src, dest);
GridReprojection reproj = null;
boolean needsCompute = false;
synchronized (reprojCache) {
Reference<GridReprojection> 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<GridReprojection>(reproj));
}
}
synchronized (reproj) {
if (needsCompute) {
reproj.computeTransformTable();
}
}
return reproj;
} }
} }

View file

@ -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.GridReprojection;
import com.raytheon.uf.common.geospatial.interpolation.GridSampler; import com.raytheon.uf.common.geospatial.interpolation.GridSampler;
import com.raytheon.uf.common.geospatial.interpolation.Interpolation; 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.FloatArrayWrapper;
import com.raytheon.uf.common.geospatial.interpolation.data.FloatBufferWrapper; import com.raytheon.uf.common.geospatial.interpolation.data.FloatBufferWrapper;
import com.vividsolutions.jts.geom.Coordinate; import com.vividsolutions.jts.geom.Coordinate;
@ -56,7 +57,8 @@ import com.vividsolutions.jts.geom.Coordinate;
* *
* Date Ticket# Engineer Description * 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.
* *
* </pre> * </pre>
* *
@ -217,7 +219,8 @@ public class GeneralGridData {
Interpolation interpolation) throws FactoryException, Interpolation interpolation) throws FactoryException,
TransformException { TransformException {
GridGeometry2D newGeom = GridGeometry2D.wrap(newGridGeometry); GridGeometry2D newGeom = GridGeometry2D.wrap(newGridGeometry);
GridReprojection reproj = new GridReprojection(gridGeometry, newGeom); GridReprojection reproj = PrecomputedGridReprojection.getReprojection(
gridGeometry, newGeom);
GridSampler sampler = new GridSampler(interpolation); GridSampler sampler = new GridSampler(interpolation);
if (isVector()) { if (isVector()) {
sampler.setSource(new FloatBufferWrapper(getUComponent(), sampler.setSource(new FloatBufferWrapper(getUComponent(),

View file

@ -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.BilinearInterpolation;
import com.raytheon.uf.common.geospatial.interpolation.GridReprojection; import com.raytheon.uf.common.geospatial.interpolation.GridReprojection;
import com.raytheon.uf.common.geospatial.interpolation.NearestNeighborInterpolation; 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.ByteBufferWrapper;
import com.raytheon.uf.common.geospatial.interpolation.data.DataSource; import com.raytheon.uf.common.geospatial.interpolation.data.DataSource;
import com.raytheon.uf.common.geospatial.interpolation.data.FloatArrayWrapper; import com.raytheon.uf.common.geospatial.interpolation.data.FloatArrayWrapper;
@ -60,9 +61,11 @@ import com.vividsolutions.jts.geom.Coordinate;
* SOFTWARE HISTORY * SOFTWARE HISTORY
* Date Ticket# Engineer Description * Date Ticket# Engineer Description
* ------------ ---------- ----------- -------------------------- * ------------ ---------- ----------- --------------------------
* 5/16/08 875 bphillip Initial Creation. * May 16, 2008 875 bphillip Initial Creation.
* 10/10/12 #1260 randerso Added getters for source and destination glocs * Oct 10, 2012 1260 randerso Added getters for source and destination
* 02/19/13 #1637 randerso Fixed remapping of byte grids * glocs
* Feb 19, 2013 1637 randerso Fixed remapping of byte grids
* Jul 17, 2013 2185 bsteffen Cache computed grid reprojections.
* *
* </pre> * </pre>
* *
@ -462,8 +465,8 @@ public class RemapGrid {
GridGeometry2D destGeometry = MapUtil.getGridGeometry(destinationGloc); GridGeometry2D destGeometry = MapUtil.getGridGeometry(destinationGloc);
synchronized (this) { synchronized (this) {
if (interp == null) { if (interp == null) {
interp = new GridReprojection(sourceGeometry, destGeometry); interp = PrecomputedGridReprojection.getReprojection(
interp.computeTransformTable(); sourceGeometry, destGeometry);
} }
} }
DataSource source = new ByteBufferWrapper(data, sourceGeometry); DataSource source = new ByteBufferWrapper(data, sourceGeometry);
@ -540,8 +543,8 @@ public class RemapGrid {
.getGridGeometry(destinationGloc); .getGridGeometry(destinationGloc);
synchronized (this) { synchronized (this) {
if (interp == null) { if (interp == null) {
interp = new GridReprojection(sourceGeometry, destGeometry); interp = PrecomputedGridReprojection.getReprojection(
interp.computeTransformTable(); sourceGeometry, destGeometry);
} }
} }
DataSource source = new FloatArrayWrapper(data, sourceGeometry); DataSource source = new FloatArrayWrapper(data, sourceGeometry);

View file

@ -46,7 +46,8 @@ import com.raytheon.uf.common.geospatial.interpolation.data.DataSource;
* *
* Date Ticket# Engineer Description * 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.
* *
* </pre> * </pre>
* *
@ -66,8 +67,6 @@ public class GridReprojection {
protected MathTransform transform; protected MathTransform transform;
protected float[] transformTable = null;
public GridReprojection(GeneralGridGeometry sourceGeometry, public GridReprojection(GeneralGridGeometry sourceGeometry,
GeneralGridGeometry targetGeometry) { GeneralGridGeometry targetGeometry) {
this.sourceGeometry = sourceGeometry; this.sourceGeometry = sourceGeometry;
@ -128,63 +127,11 @@ public class GridReprojection {
protected Point2D.Double getReprojectDataPoint(int x, int y) protected Point2D.Double getReprojectDataPoint(int x, int y)
throws TransformException, FactoryException { throws TransformException, FactoryException {
initTransforms(); 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); DirectPosition2D dp = new DirectPosition2D(x, y);
transform.transform(dp, dp); transform.transform(dp, dp);
return 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() { public GeneralGridGeometry getSourceGeometry() {
return sourceGeometry; return sourceGeometry;
} }

View file

@ -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.
*
* <pre>
*
* SOFTWARE HISTORY
*
* Date Ticket# Engineer Description
* ------------ ---------- ----------- --------------------------
* Jul 17, 2013 2185 bsteffen Initial creation
*
* </pre>
*
* @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<MultiKey, Reference<PrecomputedGridReprojection>> cache = new HashMap<MultiKey, Reference<PrecomputedGridReprojection>>();
/**
* 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<PrecomputedGridReprojection> 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<PrecomputedGridReprojection>(
reprojection));
}
}
synchronized (reprojection) {
if (created) {
reprojection.computeTransformTable();
}
}
return reprojection;
}
}