diff --git a/cdm/src/main/java/uk/ac/rdg/resc/edal/grid/cdm/CdmTransformedGrid.java b/cdm/src/main/java/uk/ac/rdg/resc/edal/grid/cdm/CdmTransformedGrid.java index 9733e5153..37712c804 100644 --- a/cdm/src/main/java/uk/ac/rdg/resc/edal/grid/cdm/CdmTransformedGrid.java +++ b/cdm/src/main/java/uk/ac/rdg/resc/edal/grid/cdm/CdmTransformedGrid.java @@ -31,18 +31,22 @@ import java.util.Collections; import java.util.List; +import java.util.Objects; import org.apache.sis.metadata.iso.extent.DefaultGeographicBoundingBox; +import org.ehcache.Cache; +import org.ehcache.config.builders.CacheConfigurationBuilder; +import org.ehcache.config.builders.ResourcePoolsBuilder; import org.opengis.metadata.extent.GeographicBoundingBox; import org.opengis.referencing.crs.CoordinateReferenceSystem; import ucar.nc2.dataset.CoordinateAxis1D; import ucar.nc2.dt.GridCoordSystem; import ucar.unidata.geoloc.LatLonPoint; -import ucar.unidata.geoloc.LatLonRect; import ucar.unidata.geoloc.Projection; import ucar.unidata.geoloc.ProjectionImpl; import ucar.unidata.geoloc.ProjectionPoint; import ucar.unidata.geoloc.projection.RotatedPole; +import uk.ac.rdg.resc.edal.cache.EdalCache; import uk.ac.rdg.resc.edal.domain.Extent; import uk.ac.rdg.resc.edal.geometry.BoundingBox; import uk.ac.rdg.resc.edal.geometry.BoundingBoxImpl; @@ -68,6 +72,11 @@ */ public class CdmTransformedGrid extends AbstractTransformedGrid { private static final long serialVersionUID = 1L; + + private static final int MAX_HEAP_ENTRIES = 50; + private static final String CACHE_NAME = "BBOX_CACHE"; + private static final Cache bboxCache; + private final ProjectionImpl proj; private final ReferenceableAxis xAxis; @@ -76,6 +85,19 @@ public class CdmTransformedGrid extends AbstractTransformedGrid { private transient Array2D domainObjs = null; + static { + Cache existing = EdalCache.cacheManager + .getCache("BBOX_CACHE", Integer.class, BoundingBox.class); + if (existing == null) { + bboxCache = EdalCache.cacheManager.createCache(CACHE_NAME, + CacheConfigurationBuilder.newCacheConfigurationBuilder(Integer.class, + BoundingBox.class, + ResourcePoolsBuilder.heap(MAX_HEAP_ENTRIES))); + } else { + bboxCache = existing; + } + } + /** * Create a new {@link CdmTransformedGrid} from a defined * {@link ProjectionImpl} and a pair of axes @@ -133,44 +155,50 @@ public CdmTransformedGrid(GridCoordSystem coordSys) { xAxis = CdmUtils.createReferenceableAxis((CoordinateAxis1D) coordSys.getXHorizAxis(), xAxisIsLongitude); yAxis = CdmUtils.createReferenceableAxis((CoordinateAxis1D) coordSys.getYHorizAxis()); - - List gridPoints = new ArrayList<>(); - for(Double x : xAxis.getCoordinateValues()) { - for(Double y : yAxis.getCoordinateValues() ) { - LatLonPoint ll = proj.projToLatLon(x, y); - gridPoints.add(new HorizontalPosition(ll.getLongitude(), ll.getLatitude())); + // create the key for the bbox cache + final int hcsHash = Objects.hash(xAxis, yAxis); + if (!bboxCache.containsKey(hcsHash)) { + // compute the bounding box (can be expensive for large grids) + System.out.println("Computing bounding box for grid with hash " + hcsHash); + double latMin = Double.MAX_VALUE; + double lonMin = Double.MAX_VALUE; + double latMax = Double.MIN_VALUE; + double lonMax = Double.MIN_VALUE; + + for (Double x : xAxis.getCoordinateValues()) { + for (Double y : yAxis.getCoordinateValues()) { + LatLonPoint ll = proj.projToLatLon(x, y); + lonMin = Math.min(lonMin, ll.getLongitude()); + lonMax = Math.max(lonMax, ll.getLongitude()); + latMin = Math.min(latMin, ll.getLatitude()); + latMax = Math.max(latMax, ll.getLatitude()); } - } + } - BoundingBox latLonBoundingBox = GISUtils.getBoundingBox(gridPoints); - /* - * Some projections do not have a well-defined lat-lon bounding box and - * return NaNs. In these cases, we fall back to using global limits - */ - double lonMin = latLonBoundingBox.getMinX(); - double lonMax = latLonBoundingBox.getMaxX(); - double latMin = latLonBoundingBox.getMinY(); - double latMax = latLonBoundingBox.getMaxY(); - if (Double.isNaN(lonMin)) { + /* + * Some projections do not have a well-defined lat-lon bounding box and + * return NaNs. In these cases, we fall back to using global limits + */ + if (Double.isNaN(lonMin)) { lonMin = -180.0; - } - if (Double.isNaN(lonMax)) { + } + if (Double.isNaN(lonMax)) { lonMax = 180.0; - } - if (Double.isNaN(latMin) || Math.abs(latMin + 90) < 1) { + } + if (Double.isNaN(latMin) || Math.abs(latMin + 90) < 1) { latMin = -90.0; - } - if (Double.isNaN(latMax) || Math.abs(latMax - 90) < 1) { + } + if (Double.isNaN(latMax) || Math.abs(latMax - 90) < 1) { latMax = 90.0; - } + } - if (latMin != latMax && lonMin != lonMax) { + if (latMin != latMax && lonMin != lonMax) { /* * The normal situation - use the bounds to create the bounding box */ bbox = new BoundingBoxImpl(lonMin, latMin, lonMax, latMax, - GISUtils.defaultGeographicCRS()); - } else { + GISUtils.defaultGeographicCRS()); + } else { /* * Some projections (MSGnavigation as returned by GRIB is the only * one I've actually found so far) end up returning a bounding box @@ -180,6 +208,11 @@ public CdmTransformedGrid(GridCoordSystem coordSys) { * It seems to be the case that the projection just doesn't work. */ bbox = BoundingBoxImpl.global(); + } + bboxCache.put(hcsHash, bbox); + } else { + System.out.println("Obtain bounding box for grid with hash " + hcsHash + " from cache"); + bbox = bboxCache.get(hcsHash); } }