diff --git a/cave/com.raytheon.viz.core.contours/src/com/raytheon/viz/core/contours/ContourSupport.java b/cave/com.raytheon.viz.core.contours/src/com/raytheon/viz/core/contours/ContourSupport.java index 2d503e9b04..06cae7d540 100644 --- a/cave/com.raytheon.viz.core.contours/src/com/raytheon/viz/core/contours/ContourSupport.java +++ b/cave/com.raytheon.viz.core.contours/src/com/raytheon/viz/core/contours/ContourSupport.java @@ -31,8 +31,12 @@ import org.apache.commons.collections.map.LRUMap; import org.eclipse.swt.graphics.RGB; import org.geotools.coverage.grid.GeneralGridEnvelope; import org.geotools.coverage.grid.GeneralGridGeometry; +import org.geotools.coverage.grid.GridEnvelope2D; import org.geotools.coverage.grid.GridGeometry2D; +import org.geotools.geometry.DirectPosition2D; +import org.geotools.geometry.Envelope2D; import org.geotools.geometry.GeneralEnvelope; +import org.geotools.geometry.jts.ReferencedEnvelope; import org.geotools.referencing.operation.DefaultMathTransformFactory; import org.opengis.referencing.crs.CoordinateReferenceSystem; import org.opengis.referencing.datum.PixelInCell; @@ -46,6 +50,7 @@ import com.raytheon.uf.common.datastorage.records.FloatDataRecord; import com.raytheon.uf.common.datastorage.records.IDataRecord; import com.raytheon.uf.common.geospatial.CRSCache; import com.raytheon.uf.common.geospatial.MapUtil; +import com.raytheon.uf.common.geospatial.TransformFactory; import com.raytheon.uf.common.geospatial.util.WorldWrapChecker; import com.raytheon.uf.common.status.IUFStatusHandler; import com.raytheon.uf.common.status.UFStatus; @@ -632,47 +637,112 @@ public class ContourSupport { public static GeneralEnvelope calculateSubGrid(IExtent workingExtent, GeneralGridGeometry mapGridGeometry, GeneralGridGeometry imageGridGeometry) throws VizException { - GeneralEnvelope env = null; + GeneralEnvelope env = new GeneralEnvelope(2); try { - // transform screen extent to map crs - double[] screen = new double[] { workingExtent.getMinX(), - workingExtent.getMinY(), workingExtent.getMaxX(), - workingExtent.getMaxY() }; - double[] map = new double[4]; - - long t0 = System.currentTimeMillis(); - mapGridGeometry.getGridToCRS(PixelInCell.CELL_CORNER).transform( - screen, 0, map, 0, 2); - long t1 = System.currentTimeMillis(); - System.out.println("subgrid transform grid to CRS time: " - + (t1 - t0)); - - // TODO: Construct screen Grid Geometry - GeneralGridEnvelope gge = new GeneralGridEnvelope(new int[] { - (int) Math.floor(screen[0]), (int) Math.floor(screen[1]) }, - new int[] { (int) Math.ceil(screen[2]), - (int) Math.ceil(screen[3]) }, false); - GridGeometry2D screenGeometry = new GridGeometry2D(gge, - mapGridGeometry.getGridToCRS(), - mapGridGeometry.getCoordinateReferenceSystem()); - GridGeometry2D imageGeometry = new GridGeometry2D(imageGridGeometry); - - t0 = System.currentTimeMillis(); - org.opengis.geometry.Envelope[] envs = MapUtil.computeEnvelopes( - screenGeometry, imageGeometry); - t1 = System.currentTimeMillis(); - System.out.println("subgrid compute envelopes time: " + (t1 - t0)); - - double[] grid = new double[4]; - grid[0] = envs[0].getMinimum(0); - grid[1] = envs[0].getMinimum(1); - grid[2] = envs[0].getMaximum(0); - grid[3] = envs[0].getMaximum(1); - env = new GeneralEnvelope(2); - env.setRange(0, Math.min(grid[0], grid[2]), - Math.max(grid[0], grid[2])); - env.setRange(1, Math.min(grid[1], grid[3]), - Math.max(grid[1], grid[3])); + GridGeometry2D mapGeometry2D = GridGeometry2D.wrap(mapGridGeometry); + GridGeometry2D imageGeometry2D = GridGeometry2D + .wrap(imageGridGeometry); + // Start with a grid envelope in screen space0 + GridEnvelope2D screenGridEnvelope = new GridEnvelope2D( + (int) Math.floor(workingExtent.getMinX()), + (int) Math.floor(workingExtent.getMinY()), + (int) Math.ceil(workingExtent.getWidth()), + (int) Math.ceil(workingExtent.getHeight())); + // intersect with mapGeometry so we only have points on the actual + // display + screenGridEnvelope = new GridEnvelope2D( + screenGridEnvelope.intersection(mapGeometry2D + .getGridRange2D())); + // convert from screen grid space to screen crs space. + Envelope2D screenCRSEnvelope = mapGeometry2D + .gridToWorld(screenGridEnvelope); + // Use referenced envelope to go from screen crs into image crs. + ReferencedEnvelope screenRefEnvelope = new ReferencedEnvelope( + screenCRSEnvelope); + screenRefEnvelope = screenRefEnvelope + .transform( + imageGridGeometry.getCoordinateReferenceSystem(), + true, 200); + // Convert from image crs to image grid space. + GridEnvelope2D screenImageGridEnvelope = imageGeometry2D + .worldToGrid(new Envelope2D(screenRefEnvelope)); + // intersection to limit to grid cells within the image. + screenImageGridEnvelope = new GridEnvelope2D( + screenImageGridEnvelope.intersection(imageGeometry2D + .getGridRange2D())); + // Referenced envelope does an almost perfect transformation. + // Unfortunately, it has been observed that when the screen is polar + // stereographic and the image is equidistant cylindrical that the + // referenced envelope algorithm can miss several rows of data near + // the pole. An envelope expansion has been added here to + // check every edge of the subgrid to see if the next image cell is + // on the screen, if it is then additional rows or columns are + // added. + MathTransform transform = TransformFactory.gridCellToGridCell( + imageGridGeometry, PixelInCell.CELL_CENTER, + mapGridGeometry, PixelInCell.CELL_CENTER); + DirectPosition2D center = new DirectPosition2D( + screenImageGridEnvelope.getCenterX(), + screenImageGridEnvelope.getCenterY()); + // this loop checks first in the x direction then in the y direction + for (int ordinate : new int[] { 0, 1 }) { + // loop over every row or column on the top or left and check + // the center point, if it can be transformed to screen space + // and is on the screen then add the whole row or column and try + // the next one. Choosing just the center point is arbitrary, + // but it catches all known cases. + for (int i = screenImageGridEnvelope.getLow(ordinate); i > imageGeometry2D + .getGridRange2D().getLow(ordinate); i -= 1) { + DirectPosition2D imageCell = center.clone(); + imageCell.setOrdinate(ordinate, i); + DirectPosition2D screenCell = new DirectPosition2D(); + try { + transform.transform(imageCell, screenCell); + } catch (TransformException e) { + // exception probably means we are outside valid range. + break; + } + if (workingExtent.contains(screenCell.getCoordinate())) { + screenImageGridEnvelope.add(imageCell); + } else { + break; + } + } + // same thing only this time on the bottom and right. + for (int i = screenImageGridEnvelope.getHigh(ordinate); i > imageGeometry2D + .getGridRange2D().getHigh(ordinate); i -= 1) { + DirectPosition2D imageCell = center.clone(); + imageCell.setOrdinate(ordinate, i); + DirectPosition2D screenCell = new DirectPosition2D(); + try { + transform.transform(imageCell, screenCell); + } catch (TransformException e) { + // exception probably means we are outside valid range. + break; + } + if (workingExtent.contains(screenCell.getCoordinate())) { + screenImageGridEnvelope.add(imageCell); + } else { + break; + } + } + } + // Add a 1 pixel border since worldToGrid is only guaranteed to + // include a cell if the cell center is in the envelope but we want + // to include the data in the subgrid if even a tiny bit of the edge + // overlaps. + screenImageGridEnvelope.grow(1, 1); + // intersection to limit to grid cells within the image. + screenImageGridEnvelope = new GridEnvelope2D( + screenImageGridEnvelope.intersection(imageGeometry2D + .getGridRange2D())); + if (!screenImageGridEnvelope.isEmpty()) { + // Convert GridEnvelope to general envelope. + env.setRange(0, screenImageGridEnvelope.getLow(0), + screenImageGridEnvelope.getHigh(0)); + env.setRange(1, screenImageGridEnvelope.getLow(1), + screenImageGridEnvelope.getHigh(1)); + } } catch (Exception e) { throw new VizException("Error transforming extent", e); } @@ -1128,9 +1198,10 @@ public class ContourSupport { double gridPixelSize = offCenter[0] - center[0]; double gridPixelMax = 2000.; - // If gridPixelSize is large, arrows on streamline will be too small, so adjust here - if(gridPixelSize > gridPixelMax) { - gridPixelSize = gridPixelSize/5; + // If gridPixelSize is large, arrows on streamline will be too small, so + // adjust here + if (gridPixelSize > gridPixelMax) { + gridPixelSize = gridPixelSize / 5; } float arrowSize = (float) (currentMagnification * 5 / zoom / gridPixelSize);