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;
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.
*
* </pre>
*
@ -72,8 +69,6 @@ import de.micromata.opengis.kml.v_2_2_0.LatLonBox;
public abstract class KmlGroundOverlayGenerator extends KmlFeatureGenerator {
private static Map<MultiKey, Reference<GridReprojection>> reprojCache = new HashMap<MultiKey, Reference<GridReprojection>>();
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<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;
GridGeometry2D dest) throws TransformException {
return PrecomputedGridReprojection.getReprojection(src, dest);
}
}

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.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.
*
* </pre>
*
@ -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(),

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.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.
*
* </pre>
*
@ -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);

View file

@ -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.
*
* </pre>
*
@ -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;
}

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;
}
}