/* (c) 2015 Open Source Geospatial Foundation - all rights reserved * This code is licensed under the GPL 2.0 license, available at the root * application directory. */ package org.geoserver.wps.gs.download; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.util.List; import javax.media.jai.Interpolation; import org.apache.commons.io.IOUtils; import org.geoserver.data.util.CoverageUtils; import org.geoserver.wcs.CoverageCleanerCallback; import org.geotools.coverage.grid.GridCoverage2D; import org.geotools.coverage.grid.GridEnvelope2D; import org.geotools.coverage.grid.GridGeometry2D; import org.geotools.coverage.grid.io.AbstractGridFormat; import org.geotools.coverage.grid.io.GridCoverage2DReader; import org.geotools.gce.geotiff.GeoTiffReader; import org.geotools.gce.geotiff.GeoTiffWriter; import org.geotools.geometry.jts.JTS; import org.geotools.geometry.jts.ReferencedEnvelope; import org.geotools.process.raster.CropCoverage; import org.geotools.referencing.crs.DefaultGeographicCRS; import org.geotools.util.NullProgressListener; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.opengis.geometry.Envelope; import org.opengis.parameter.GeneralParameterDescriptor; import org.opengis.parameter.GeneralParameterValue; import org.opengis.parameter.ParameterValueGroup; public class ScaleToTargetTest { private static final String TEST_COVERAGE = "/org/geoserver/data/test/tazbm.tiff"; private static final double DELTA = 1E-06; private static final double[] EXP_NATIVE_RES = new double[] { 0.0041666667, 0.0041666667 }; private static final ReferencedEnvelope ROI = new ReferencedEnvelope(146.5, 148, -43.5, -43, DefaultGeographicCRS.WGS84); private File inputTempFile = null; @Before public void setUpInput() throws IOException { inputTempFile = File.createTempFile("scale2target_", "_in"); try (FileOutputStream fos = new FileOutputStream(inputTempFile)) { IOUtils.copy(getClass().getResourceAsStream(TEST_COVERAGE), fos); } } @After public void cleanUpInput() { if (inputTempFile != null) { inputTempFile.delete(); } } @Test public void testAdjustSize() throws Exception { GeoTiffReader reader = null; try { reader = new GeoTiffReader(inputTempFile); assertNotNull(reader); Envelope fullSizeEnvelope = reader.getOriginalEnvelope(); ScaleToTarget scalingFullSize = new ScaleToTarget(reader, fullSizeEnvelope); scalingFullSize.setTargetSize(160, null); Integer[] targetSize = scalingFullSize.getTargetSize(); assertEquals(160, targetSize[0].intValue()); assertEquals(160, targetSize[1].intValue()); scalingFullSize.setTargetSize(null, 200); targetSize = scalingFullSize.getTargetSize(); assertEquals(200, targetSize[0].intValue()); assertEquals(200, targetSize[1].intValue()); // test with ROI with a different aspect ratio ScaleToTarget scalingRoi = new ScaleToTarget(reader, ROI); scalingRoi.setTargetSize(150, null); targetSize = scalingRoi.getTargetSize(); assertEquals(150, targetSize[0].intValue()); assertEquals(50, targetSize[1].intValue()); scalingRoi.setTargetSize(null, 100); targetSize = scalingRoi.getTargetSize(); assertEquals(300, targetSize[0].intValue()); assertEquals(100, targetSize[1].intValue()); } finally { if (reader != null) { reader.dispose(); } } } @Test public void testNoScalingJustInterpolation() throws Exception { GeoTiffReader reader = null; GridCoverage2D gc = null; try { reader = new GeoTiffReader(inputTempFile); assertNotNull(reader); Envelope fullSizeEnvelope = reader.getOriginalEnvelope(); ScaleToTarget noScaling = new ScaleToTarget(reader, fullSizeEnvelope); // I deliberately omit setting the target size: only interpolation will be performed // set interpolation method to something other than NEAREST noScaling.setInterpolation(Interpolation.getInstance(Interpolation.INTERP_BILINEAR)); gc = noScaling.scale(reader.read(null)); assertNotNull(gc); // TODO: this only proves the code ran without throwing exceptions: how do I actually // test that the interpolation was done? } finally { if (reader != null) { reader.dispose(); } if (gc != null) { CoverageCleanerCallback.disposeCoverage(gc); } } } @Test public void testFullSizeScale2X() throws Exception { final int targetSizeX = 720, targetSizeY = 720; final double[] expectedRequestedResolution = new double[] { EXP_NATIVE_RES[0]/2, EXP_NATIVE_RES[1]/2 }; final double[] expectedReadResolution = EXP_NATIVE_RES; final int[] expectedGridSize = new int[] { 360, 360 }; // full size image testFullSize(targetSizeX, targetSizeY, expectedRequestedResolution, expectedReadResolution, expectedGridSize); } @Test public void testFullSizeTargetSizeMatchesOverview() throws Exception { final int targetSizeX = 90, targetSizeY = 90; final double[] expectedRequestedResolution = new double[] { 0.0166666667, 0.0166666667 }; final double[] expectedReadResolution = expectedRequestedResolution; final int[] expectedGridSize = new int[] { targetSizeX, targetSizeY }; // matches 90x90 overview testFullSize(targetSizeX, targetSizeY, expectedRequestedResolution, expectedReadResolution, expectedGridSize); } @Test public void testFullSizeTargetSizeDoesNotMatchOverview() throws Exception { final int targetSizeX = 110, targetSizeY = 110; final double[] expectedRequestedResolution = new double[] { 0.0136363636, 0.0136363636 }; final double[] expectedReadResolution = new double[] { 0.0166666667, 0.0166666667 }; final int[] expectedGridSize = new int[] { 90, 90 }; // closest overview: 90x90 testFullSize(targetSizeX, targetSizeY, expectedRequestedResolution, expectedReadResolution, expectedGridSize); } private void testFullSize(int targetSizeX, int targetSizeY, final double[] expectedRequestedResolution, final double[] expectedReadResolution, final int[] expectedGridSize) throws Exception { GeoTiffReader inputReader = null, outputReader = null; GeoTiffWriter writer = null; File outputTempFile = null; GridCoverage2D gc = null; try { inputReader = new GeoTiffReader(inputTempFile); assertNotNull(inputReader); // read the entire coverage Envelope fullSizeEnvelope = inputReader.getOriginalEnvelope(); ScaleToTarget oneFourth = new ScaleToTarget(inputReader, fullSizeEnvelope); oneFourth.setTargetSize(targetSizeX, targetSizeY); // check resolution computations double[] nativeResolution = oneFourth.computeNativeResolution(); checkResolution(EXP_NATIVE_RES, nativeResolution); double[] requestedResolution = oneFourth.computeRequestedResolution(); checkResolution(expectedRequestedResolution, requestedResolution); double[] readResolution = oneFourth.computeReadingResolution(requestedResolution); checkResolution(expectedReadResolution, readResolution); // check grid geometry size at the picked read resolution GridGeometry2D gridGeometry = oneFourth.getGridGeometry(); assertEquals(expectedGridSize[0], gridGeometry.getGridRange2D().width); assertEquals(expectedGridSize[1], gridGeometry.getGridRange2D().height); // do the actual scaling gc = oneFourth.scale(new GeneralParameterValue[] {}); // write scaled coverage to temp file outputTempFile = File.createTempFile("scale2target_", "_out"); writer = new GeoTiffWriter(outputTempFile); writer.write(gc, null); // verify coverage has been scaled outputReader = new GeoTiffReader(outputTempFile); GridEnvelope2D outputGrid = (GridEnvelope2D) outputReader.getOriginalGridRange(); assertEquals(targetSizeX, outputGrid.width); assertEquals(targetSizeY, outputGrid.height); assertTrue(outputReader.getOriginalEnvelope().equals(fullSizeEnvelope, DELTA, false)); } finally { // final cleanup finalCleanUp(inputReader, outputReader, writer, gc, null, outputTempFile); } } @Test public void testROITargetSizeMatchesOverview() throws Exception { final int targetSizeX = 180, targetSizeY = 60; // matches 180x180 overview final double[] expectedRequestedResolution = new double[] { 0.0083333333, 0.0083333333 }; final double[] expectedReadResolution = expectedRequestedResolution; final int[] expectedGridSize = new int[] { 180, 60 }; // matches 180x180 overview testROI(targetSizeX, targetSizeY, expectedRequestedResolution, expectedReadResolution, expectedGridSize); } @Test public void testROITargetSizeDoesNotMatchOverview() throws Exception { final int targetSizeX = 150, targetSizeY = 50; // closest overview is 180x180 final double[] expectedRequestedResolution = new double[] { 0.01, 0.01 }; final double[] expectedReadResolution = new double[] { 0.0083333333, 0.0083333333 }; final int[] expectedGridSize = new int[] { 180, 60 }; // targetSize * requestedRes / readRes testROI(targetSizeX, targetSizeY, expectedRequestedResolution, expectedReadResolution, expectedGridSize); } private void testROI(int targetSizeX, int targetSizeY, final double[] expectedRequestedResolution, final double[] expectedReadResolution, final int[] expectedGridSize) throws Exception { GeoTiffReader inputReader = null, outputReader = null; GeoTiffWriter writer = null; File outputTempFile = null; GridCoverage2D gc = null, croppedGC = null; try { inputReader = new GeoTiffReader(inputTempFile); assertNotNull(inputReader); // read ROI ScaleToTarget scaleToROI = new ScaleToTarget(inputReader, ROI); scaleToROI.setTargetSize(targetSizeX, targetSizeY); // check resolution computations double[] nativeResolution = scaleToROI.computeNativeResolution(); checkResolution(EXP_NATIVE_RES, nativeResolution); double[] requestedResolution = scaleToROI.computeRequestedResolution(); checkResolution(expectedRequestedResolution, requestedResolution); double[] readResolution = scaleToROI.computeReadingResolution(requestedResolution); checkResolution(expectedReadResolution, readResolution); // check grid geometry size at the picked read resolution GridGeometry2D gridGeometry = scaleToROI.getGridGeometry(); assertEquals(expectedGridSize[0], gridGeometry.getGridRange2D().width); assertEquals(expectedGridSize[1], gridGeometry.getGridRange2D().height); // setup reader parameters to have it exploit overviews GeneralParameterValue[] readParameters = getReaderParams(inputReader, gridGeometry); // read input gc = inputReader.read(readParameters); // crop first CropCoverage crop = new CropCoverage(); croppedGC = crop.execute(gc, JTS.toGeometry(ROI), new NullProgressListener()); // do the actual scaling: actually, no need to scale gc = scaleToROI.scale(croppedGC); // write scaled coverage to temp file outputTempFile = File.createTempFile("scale2target_", "_out"); writer = new GeoTiffWriter(outputTempFile); writer.write(gc, null); // verify coverage has been scaled outputReader = new GeoTiffReader(outputTempFile); GridEnvelope2D outputGrid = (GridEnvelope2D) outputReader.getOriginalGridRange(); assertEquals(targetSizeX, outputGrid.width); assertEquals(targetSizeY, outputGrid.height); assertTrue(outputReader.getOriginalEnvelope().equals(ROI, DELTA, false)); } finally { // final cleanup finalCleanUp(inputReader, outputReader, writer, gc, croppedGC, outputTempFile); } } private void finalCleanUp(GeoTiffReader inputReader, GeoTiffReader outputReader, GeoTiffWriter writer, GridCoverage2D gc, GridCoverage2D croppedGC, File outputTempFile) { if (inputReader != null) { inputReader.dispose(); } if (outputReader != null) { outputReader.dispose(); } if (writer != null) { writer.dispose(); } if (gc != null) { CoverageCleanerCallback.disposeCoverage(gc); } if (croppedGC != null) { CoverageCleanerCallback.disposeCoverage(croppedGC); } if (outputTempFile != null) { outputTempFile.delete(); } } private void checkResolution(double[] expectedResolution, double[] actualResolution) { assertEquals(expectedResolution[0], actualResolution[0], DELTA); assertEquals(expectedResolution[1], actualResolution[1], DELTA); } private GeneralParameterValue[] getReaderParams(GridCoverage2DReader reader, GridGeometry2D gridGeometry) { // setup reader parameters to have it exploit overviews final ParameterValueGroup readParametersDescriptor = reader.getFormat().getReadParameters(); final List<GeneralParameterDescriptor> parameterDescriptors = readParametersDescriptor .getDescriptor().descriptors(); GeneralParameterValue[] readParameters = new GeneralParameterValue[] {}; readParameters = CoverageUtils.mergeParameter(parameterDescriptors, readParameters, gridGeometry, AbstractGridFormat.READ_GRIDGEOMETRY2D.getName().getCode()); return readParameters; } }