package org.geoserver.wcs;
import static org.geoserver.data.test.MockData.TASMANIA_BM;
import java.awt.image.RenderedImage;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.StringReader;
import java.util.HashMap;
import java.util.Map;
import javax.imageio.ImageIO;
import javax.imageio.ImageReader;
import javax.servlet.ServletResponse;
import javax.xml.namespace.QName;
import junit.framework.Test;
import junit.textui.TestRunner;
import net.opengis.wcs10.GetCoverageType;
import org.apache.commons.io.IOUtils;
import org.geoserver.catalog.Catalog;
import org.geoserver.catalog.DimensionPresentation;
import org.geoserver.catalog.ResourceInfo;
import org.geoserver.config.GeoServer;
import org.geoserver.data.test.MockData;
import org.geoserver.wcs.kvp.Wcs10GetCoverageRequestReader;
import org.geoserver.wcs.test.WCSTestSupport;
import org.geoserver.wcs.xml.v1_0_0.WcsXmlReader;
import org.geotools.coverage.grid.GeneralGridEnvelope;
import org.geotools.coverage.grid.GridCoverage2D;
import org.geotools.coverage.grid.io.AbstractGridCoverage2DReader;
import org.geotools.data.DataSourceException;
import org.geotools.gce.geotiff.GeoTiffFormat;
import org.geotools.gce.geotiff.GeoTiffReader;
import org.geotools.geometry.GeneralEnvelope;
import org.geotools.metadata.iso.spatial.PixelTranslation;
import org.geotools.referencing.CRS;
import org.geotools.referencing.operation.transform.AffineTransform2D;
import org.geotools.wcs.WCSConfiguration;
import org.opengis.coverage.grid.GridCoverage;
import org.opengis.coverage.grid.GridEnvelope;
import org.opengis.referencing.datum.PixelInCell;
import org.opengis.referencing.operation.MathTransform;
import org.w3c.dom.Document;
import com.mockrunner.mock.web.MockHttpServletResponse;
/**
* Tests for GetCoverage operation on WCS.
*
* @author Simone Giannecchini, GeoSolutions SAS
*
*/
public class GetCoverageTest extends WCSTestSupport {
private Wcs10GetCoverageRequestReader kvpreader;
private WebCoverageService100 service;
private WCSConfiguration configuration;
private WcsXmlReader xmlReader;
private Catalog catalog;
private static final QName MOSAIC = new QName(MockData.SF_URI, "rasterFilter", MockData.SF_PREFIX);
/**
* This is a READ ONLY TEST so we can use one time setup
*/
public static Test suite() {
return new OneTimeTestSetup(new GetCoverageTest());
}
@Override
protected void setUpInternal() throws Exception {
super.setUpInternal();
kvpreader = (Wcs10GetCoverageRequestReader) applicationContext.getBean("wcs100GetCoverageRequestReader");
service = (WebCoverageService100) applicationContext.getBean("wcs100ServiceTarget");
configuration = new WCSConfiguration();
catalog=(Catalog)applicationContext.getBean("catalog");
xmlReader = new WcsXmlReader("GetCoverage", "1.0.0", configuration);
// enable dimensions on the water temperature layer
setupRasterDimension(ResourceInfo.TIME, DimensionPresentation.LIST, null);
setupRasterDimension(ResourceInfo.ELEVATION, DimensionPresentation.LIST, null);
}
@Override
protected void populateDataDirectory(MockData dataDirectory) throws Exception {
super.populateDataDirectory(dataDirectory);
// this also adds the raster style
dataDirectory.addCoverage(MOSAIC,
MockData.class.getResource("raster-filter-test.zip"), null, "raster");
}
@Override
protected String getLogConfiguration() {
return "/DEFAULT_LOGGING.properties";
}
private Map<String, Object> baseMap() {
Map<String, Object> raw = new HashMap<String, Object>();
raw.put("service", "WCS");
raw.put("version", "1.0.0");
raw.put("request", "GetCoverage");
return raw;
}
public void testDomainSubsetRxRy() throws Exception {
// get base coverage
final GridCoverage baseCoverage = catalog.getCoverageByName(TASMANIA_BM.getLocalPart()).getGridCoverage(null, null);
final AffineTransform2D expectedTx = (AffineTransform2D) baseCoverage.getGridGeometry().getGridToCRS();
final GeneralEnvelope originalEnvelope = (GeneralEnvelope) baseCoverage.getEnvelope();
final GeneralEnvelope newEnvelope=new GeneralEnvelope(originalEnvelope);
newEnvelope.setEnvelope(
originalEnvelope.getMinimum(0),
originalEnvelope.getMaximum(1)-originalEnvelope.getSpan(1)/2,
originalEnvelope.getMinimum(0)+originalEnvelope.getSpan(0)/2,
originalEnvelope.getMaximum(1)
);
final MathTransform cornerWorldToGrid = PixelTranslation.translate(expectedTx,PixelInCell.CELL_CENTER,PixelInCell.CELL_CORNER);
final GeneralGridEnvelope expectedGridEnvelope = new GeneralGridEnvelope(CRS.transform(cornerWorldToGrid.inverse(), newEnvelope),PixelInCell.CELL_CORNER,false);
final StringBuilder envelopeBuilder= new StringBuilder();
envelopeBuilder.append(newEnvelope.getMinimum(0)).append(",");
envelopeBuilder.append(newEnvelope.getMinimum(1)).append(",");
envelopeBuilder.append(newEnvelope.getMaximum(0)).append(",");
envelopeBuilder.append(newEnvelope.getMaximum(1));
Map<String, Object> raw = baseMap();
final String layerID = getLayerId(TASMANIA_BM);
raw.put("sourcecoverage", layerID);
raw.put("version", "1.0.0");
raw.put("format", "image/geotiff");
raw.put("BBox", envelopeBuilder.toString());
raw.put("crs", "EPSG:4326");
raw.put("resx", Double.toString(expectedTx.getScaleX()));
raw.put("resy", Double.toString(Math.abs(expectedTx.getScaleY())));
final GridCoverage[] coverages = executeGetCoverageKvp(raw);
final GridCoverage2D result=(GridCoverage2D) coverages[0];
assertTrue(coverages.length==1);
final AffineTransform2D tx = (AffineTransform2D) result.getGridGeometry().getGridToCRS();
assertEquals("resx",expectedTx.getScaleX(),tx.getScaleX(),1E-6);
assertEquals("resx",Math.abs(expectedTx.getScaleY()),Math.abs(tx.getScaleY()),1E-6);
final GridEnvelope gridEnvelope = result.getGridGeometry().getGridRange();
assertEquals("w",180,gridEnvelope.getSpan(0));
assertEquals("h",180,gridEnvelope.getSpan(1));
assertEquals("grid envelope",expectedGridEnvelope, gridEnvelope);
// dispose
((GridCoverage2D)coverages[0]).dispose(true);
}
/**
* Compare two grid to world transformations
* @param expectedTx
* @param tx
*/
private static void compareGrid2World(AffineTransform2D expectedTx,
AffineTransform2D tx) {
assertEquals("scalex",tx.getScaleX(), expectedTx.getScaleX(), 1E-6);
assertEquals("scaley",tx.getScaleY(), expectedTx.getScaleY(), 1E-6);
assertEquals("shearx",tx.getShearX(), expectedTx.getShearX(), 1E-6);
assertEquals("sheary",tx.getShearY(), expectedTx.getShearY(), 1E-6);
assertEquals("translatex",tx.getTranslateX(), expectedTx.getTranslateX(), 1E-6);
assertEquals("translatey",tx.getTranslateY(), expectedTx.getTranslateY(), 1E-6);
}
public void testWorkspaceQualified() throws Exception {
String queryString ="&request=getcoverage&service=wcs&version=1.0.0&format=image/geotiff&bbox=146,-45,147,-42"+
"&crs=EPSG:4326&width=150&height=150";
ServletResponse response = getAsServletResponse(
"wcs?sourcecoverage="+TASMANIA_BM.getLocalPart()+queryString);
assertTrue(response.getContentType().startsWith("image/tiff"));
Document dom = getAsDOM(
"cdf/wcs?sourcecoverage="+TASMANIA_BM.getLocalPart()+queryString);
assertEquals("ServiceExceptionReport", dom.getDocumentElement().getNodeName());
}
public void testLayerQualified() throws Exception {
String queryString ="&request=getcoverage&service=wcs&version=1.0.0&format=image/geotiff&bbox=146,-45,147,-42"+
"&crs=EPSG:4326&width=150&height=150";
ServletResponse response = getAsServletResponse(
"wcs/BlueMarble/wcs?sourcecoverage=BlueMarble"+queryString);
assertTrue(response.getContentType().startsWith("image/tiff"));
Document dom = getAsDOM(
"wcs/DEM/wcs?sourcecoverage=BlueMarble"+queryString);
assertEquals("ServiceExceptionReport", dom.getDocumentElement().getNodeName());
}
/**
* Runs GetCoverage on the specified parameters and returns an array of coverages
*/
GridCoverage[] executeGetCoverageKvp(Map<String, Object> raw) throws Exception {
final GetCoverageType getCoverage = (GetCoverageType) kvpreader.read(kvpreader.createRequest(),parseKvp(raw), raw);
return service.getCoverage(getCoverage);
}
/**
* Runs GetCoverage on the specified parameters and returns an array of coverages
*/
GridCoverage[] executeGetCoverageXml(String request) throws Exception {
GetCoverageType getCoverage = (GetCoverageType) xmlReader.read(null, new StringReader(
request), null);
return service.getCoverage(getCoverage);
}
public void testInputLimits() throws Exception {
try {
// ridicolous limit, just one byte
setInputLimit(1);
String queryString = "&request=getcoverage&service=wcs&version=1.0.0&format=image/geotiff&bbox=146,-45,147,-42"
+ "&crs=EPSG:4326&width=150&height=150";
Document dom = getAsDOM("wcs/BlueMarble/wcs?sourcecoverage=" + getLayerId(TASMANIA_BM)
+ queryString);
// print(dom);
// check it's an error, check we're getting it because of the input limits
assertEquals("ServiceExceptionReport", dom.getDocumentElement().getNodeName());
String error = xpath.evaluate(
"/ServiceExceptionReport/ServiceException/text()", dom).trim();
assertTrue(error.matches(".*read too much data.*"));
} finally {
setInputLimit(0);
}
}
public void testOutputLimits() throws Exception {
try {
// ridicolous limit, just one byte
setOutputLimit(1);
String queryString = "&request=getcoverage&service=wcs&version=1.0.0&format=image/geotiff&bbox=146,-45,147,-42"
+ "&crs=EPSG:4326&width=150&height=150";
Document dom = getAsDOM("wcs/BlueMarble/wcs?sourcecoverage=" + getLayerId(TASMANIA_BM)
+ queryString);
// print(dom);
// check it's an error, check we're getting it because of the output limits
assertEquals("ServiceExceptionReport", dom.getDocumentElement().getNodeName());
String error = xpath.evaluate(
"/ServiceExceptionReport/ServiceException/text()", dom).trim();
assertTrue(error.matches(".*generate too much data.*"));
} finally {
setOutputLimit(0);
}
}
public void testReproject() throws Exception {
String xml = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" +
"<GetCoverage version=\"1.0.0\" service=\"WCS\" " +
"xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" " +
"xmlns=\"http://www.opengis.net/wcs\" " +
"xmlns:ows=\"http://www.opengis.net/ows/1.1\" " +
"xmlns:gml=\"http://www.opengis.net/gml\" " +
"xmlns:ogc=\"http://www.opengis.net/ogc\" " +
"xsi:schemaLocation=\"http://www.opengis.net/wcs http://schemas.opengis.net/wcs/1.0.0/getCoverage.xsd\">\n" +
" <sourceCoverage>" + getLayerId(TASMANIA_BM) + "</sourceCoverage>\n" +
" <domainSubset>\n" +
" <spatialSubset>\n" +
" <gml:Envelope srsName=\"EPSG:4326\">\n" +
" <gml:pos>146 -45</gml:pos>\n" +
" <gml:pos>147 42</gml:pos>\n" +
" </gml:Envelope>\n" +
" <gml:Grid dimension=\"2\">\n" +
" <gml:limits>\n" +
" <gml:GridEnvelope>\n" +
" <gml:low>0 0</gml:low>\n" +
" <gml:high>150 150</gml:high>\n" +
" </gml:GridEnvelope>\n" +
" </gml:limits>\n" +
" <gml:axisName>x</gml:axisName>\n" +
" <gml:axisName>y</gml:axisName>\n" +
" </gml:Grid>\n" +
" </spatialSubset>\n" +
" </domainSubset>\n" +
" <output>\n" +
" <crs>EPSG:3857</crs>\n" +
" <format>image/geotiff</format>\n" +
" </output>\n" +
"</GetCoverage>";
MockHttpServletResponse response = postAsServletResponse("wcs", xml);
assertEquals("image/tiff;subtype=\"geotiff\"", response.getContentType());
GeoTiffFormat format = new GeoTiffFormat();
AbstractGridCoverage2DReader reader = format.getReader(getBinaryInputStream(response));
assertEquals(CRS.decode("EPSG:3857"), reader.getOriginalEnvelope().getCoordinateReferenceSystem());
}
public void testRasterFilterGreen() throws Exception {
String queryString = "wcs?sourcecoverage=" + getLayerId(MOSAIC) + "&request=getcoverage" +
"&service=wcs&version=1.0.0&&format=image/tiff&crs=EPSG:4326" +
"&bbox=0,0,1,1&CQL_FILTER=location like 'green%25'&width=150&height=150";
MockHttpServletResponse response = getAsServletResponse(queryString);
// make sure we can read the coverage back
RenderedImage image = readTiff(response);
// check the pixel
int[] pixel = new int[3];
image.getData().getPixel(0, 0, pixel);
assertEquals(0, pixel[0]);
assertEquals(255, pixel[1]);
assertEquals(0, pixel[2]);
}
/**
* Parses teh TIFF contained in the response as a {@link RenderedImage}
* @param response
* @return
* @throws IOException
*/
RenderedImage readTiff(MockHttpServletResponse response) throws IOException {
ImageReader reader = ImageIO.getImageReadersByFormatName("tiff").next();
reader.setInput(ImageIO.createImageInputStream(getBinaryInputStream(response)));
return reader.read(0);
}
public void testTimeFirstPOST() throws Exception {
String request = getWaterTempTimeRequest("2008-10-31T00:00:00.000Z");
MockHttpServletResponse response = postAsServletResponse("wcs", request);
checkTimeFirst(response);
}
public void testTimeFirstKVP() throws Exception {
setupRasterDimension(ResourceInfo.ELEVATION, DimensionPresentation.LIST, null);
setupRasterDimension(ResourceInfo.ELEVATION, DimensionPresentation.LIST, null);
String queryString ="request=getcoverage&service=wcs&version=1.0.0&format=image/geotiff" +
"&bbox=0.237,40.562,14.593,44.558&crs=EPSG:4326&width=25&height=25&time=2008-10-31T00:00:00.000Z" +
"&coverage=" + getLayerId(WATTEMP);
MockHttpServletResponse response = getAsServletResponse("wcs?" + queryString);
checkTimeFirst(response);
}
private void checkTimeFirst(MockHttpServletResponse response) throws IOException,
FileNotFoundException, DataSourceException {
// save
File tiffFile = File.createTempFile("wcs", "", new File("target"));
IOUtils.copy(getBinaryInputStream(response), new FileOutputStream(tiffFile));
// make sure we can read the coverage back
GeoTiffReader reader = new GeoTiffReader(tiffFile);
GridCoverage2D result = reader.read(null);
/*
gdallocationinfo NCOM_wattemp_000_20081031T0000000_12.tiff 10 10
Report:
Location: (10P,10L)
Band 1:
Value: 18.2659999176394
*/
// check a pixel
double[] pixel = new double[1];
result.getRenderedImage().getData().getPixel(10, 10, pixel);
assertEquals(18.2659999176394, pixel[0], 1e-6);
tiffFile.delete();
}
private void checkTimeCurrent(MockHttpServletResponse response) throws IOException,
FileNotFoundException, DataSourceException {
// save
File tiffFile = File.createTempFile("wcs", "", new File("target"));
IOUtils.copy(getBinaryInputStream(response), new FileOutputStream(tiffFile));
// make sure we can read the coverage back
GeoTiffReader reader = new GeoTiffReader(tiffFile);
GridCoverage2D result = reader.read(null);
/*
gdallocationinfo NCOM_wattemp_000_20081101T0000000_12.tiff 10 10
Report:
Location: (10P,10L)
Band 1:
Value: 18.2849999185419
*/
// check a pixel
double[] pixel = new double[1];
result.getRenderedImage().getData().getPixel(10, 10, pixel);
assertEquals(18.2849999185419, pixel[0], 1e-6);
tiffFile.delete();
}
public void testTimeSecond() throws Exception {
String request = getWaterTempTimeRequest("2008-11-01T00:00:00.000Z");
MockHttpServletResponse response = postAsServletResponse("wcs", request);
checkTimeCurrent(response);
}
public void testTimeKVPNow() throws Exception {
String queryString ="request=getcoverage&service=wcs&version=1.0.0&format=image/geotiff" +
"&bbox=0.237,40.562,14.593,44.558&crs=EPSG:4326&width=25&height=25&time=now" +
"&coverage=" + getLayerId(WATTEMP);
MockHttpServletResponse response = getAsServletResponse("wcs?" + queryString);
checkTimeCurrent(response);
}
public void testElevationFirst() throws Exception {
String request = getWaterTempElevationRequest("0.0");
MockHttpServletResponse response = postAsServletResponse("wcs", request);
assertEquals("image/tiff;subtype=\"geotiff\"", response.getContentType());
// same result as time first
checkTimeCurrent(response);
}
public void testElevationSecond() throws Exception {
String request = getWaterTempElevationRequest("100.0");
MockHttpServletResponse response = postAsServletResponse("wcs", request);
assertEquals("image/tiff;subtype=\"geotiff\"", response.getContentType());
// save
File tiffFile = File.createTempFile("wcs", "", new File("target"));
IOUtils.copy(getBinaryInputStream(response), new FileOutputStream(tiffFile));
// make sure we can read the coverage back
GeoTiffReader reader = new GeoTiffReader(tiffFile);
GridCoverage2D result = reader.read(null);
/*
gdallocationinfo NCOM_wattemp_100_20081101T0000000_12.tiff 10 10
Report:
Location: (10P,10L)
Band 1:
Value: 13.337999683572
*/
// check a pixel
double[] pixel = new double[1];
result.getRenderedImage().getData().getPixel(10, 10, pixel);
assertEquals(13.337999683572, pixel[0], 1e-6);
tiffFile.delete();
}
private String getWaterTempElevationRequest(String elevation) {
String request =
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" +
"<GetCoverage version=\"1.0.0\" service=\"WCS\"\n" +
" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns=\"http://www.opengis.net/wcs\"\n" +
" xmlns:ows=\"http://www.opengis.net/ows/1.1\" xmlns:gml=\"http://www.opengis.net/gml\"\n" +
" xmlns:ogc=\"http://www.opengis.net/ogc\"\n" +
" xsi:schemaLocation=\"http://www.opengis.net/wcs http://schemas.opengis.net/wcs/1.0.0/getCoverage.xsd\">\n" +
" <sourceCoverage>" + getLayerId(WATTEMP) + "</sourceCoverage>\n" +
" <domainSubset>\n" +
" <spatialSubset>\n" +
" <gml:Envelope srsName=\"EPSG:4326\">\n" +
" <gml:pos>0.237 40.562</gml:pos>\n" +
" <gml:pos>14.593 44.558</gml:pos>\n" +
" </gml:Envelope>\n" +
" <gml:Grid dimension=\"2\">\n" +
" <gml:limits>\n" +
" <gml:GridEnvelope>\n" +
" <gml:low>0 0</gml:low>\n" +
" <gml:high>25 24</gml:high>\n" +
" </gml:GridEnvelope>\n" +
" </gml:limits>\n" +
" <gml:axisName>x</gml:axisName>\n" +
" <gml:axisName>y</gml:axisName>\n" +
" </gml:Grid>\n" +
" </spatialSubset>\n" +
" </domainSubset>\n" +
" <rangeSubset>\n" +
" <axisSubset name=\"ELEVATION\">\n" +
" <singleValue>" + elevation + "</singleValue>\n" +
" </axisSubset>\n" +
" </rangeSubset>\n" +
" <output>\n" +
" <crs>EPSG:4326</crs>\n" +
" <format>GeoTIFF</format>\n" +
" </output>\n" +
"</GetCoverage>";
return request;
}
private String getWaterTempTimeRequest(String date) {
String request =
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" +
"<GetCoverage version=\"1.0.0\" service=\"WCS\"\n" +
" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns=\"http://www.opengis.net/wcs\"\n" +
" xmlns:ows=\"http://www.opengis.net/ows/1.1\" xmlns:gml=\"http://www.opengis.net/gml\"\n" +
" xmlns:ogc=\"http://www.opengis.net/ogc\"\n" +
" xsi:schemaLocation=\"http://www.opengis.net/wcs http://schemas.opengis.net/wcs/1.0.0/getCoverage.xsd\">\n" +
" <sourceCoverage>" + getLayerId(WATTEMP) + "</sourceCoverage>\n" +
" <domainSubset>\n" +
" <spatialSubset>\n" +
" <gml:Envelope srsName=\"EPSG:4326\">\n" +
" <gml:pos>0.237 40.562</gml:pos>\n" +
" <gml:pos>14.593 44.558</gml:pos>\n" +
" </gml:Envelope>\n" +
" <gml:Grid dimension=\"2\">\n" +
" <gml:limits>\n" +
" <gml:GridEnvelope>\n" +
" <gml:low>0 0</gml:low>\n" +
" <gml:high>25 25</gml:high>\n" +
" </gml:GridEnvelope>\n" +
" </gml:limits>\n" +
" <gml:axisName>x</gml:axisName>\n" +
" <gml:axisName>y</gml:axisName>\n" +
" </gml:Grid>\n" +
" </spatialSubset>\n" +
" <temporalSubset>\n" +
" <gml:timePosition>" + date + "</gml:timePosition>\n" +
" </temporalSubset>\n" +
" </domainSubset>\n" +
" <output>\n" +
" <crs>EPSG:4326</crs>\n" +
" <format>geotiff</format>\n" +
" </output>\n" +
"</GetCoverage>";
return request;
}
public void testRasterFilterRed() throws Exception {
String queryString = "wcs?sourcecoverage=" + getLayerId(MOSAIC) + "&request=getcoverage" +
"&service=wcs&version=1.0.0&format=image/tiff&crs=EPSG:4326" +
"&bbox=0,0,1,1&CQL_FILTER=location like 'red%25'&width=150&height=150";
MockHttpServletResponse response = getAsServletResponse(queryString);
RenderedImage image = readTiff(response);
// check the pixel
int[] pixel = new int[3];
image.getData().getPixel(0, 0, pixel);
assertEquals(255, pixel[0]);
assertEquals(0, pixel[1]);
assertEquals(0, pixel[2]);
}
private void setInputLimit(int kbytes) {
GeoServer gs = getGeoServer();
WCSInfo info = gs.getService(WCSInfo.class);
info.setMaxInputMemory(kbytes);
gs.save(info);
}
private void setOutputLimit(int kbytes) {
GeoServer gs = getGeoServer();
WCSInfo info = gs.getService(WCSInfo.class);
info.setMaxOutputMemory(kbytes);
gs.save(info);
}
public static void main(String[] args) {
TestRunner.run(suite());
}
}