/* (c) 2014 - 2017 Open Source Geospatial Foundation - all rights reserved * (c) 2001 - 2014 OpenPlans * This code is licensed under the GPL 2.0 license, available at the root * application directory. */ package org.geoserver.wms.wms_1_1_1; import static org.hamcrest.CoreMatchers.containsString; import static org.junit.Assert.*; import java.awt.Color; import java.awt.image.BufferedImage; import java.io.File; import java.util.Collections; import java.util.HashMap; import java.util.Map; import java.util.logging.Level; import javax.xml.namespace.QName; import org.custommonkey.xmlunit.XMLAssert; import org.geoserver.catalog.Catalog; import org.geoserver.catalog.CatalogBuilder; import org.geoserver.catalog.CatalogFactory; import org.geoserver.catalog.CoverageInfo; import org.geoserver.catalog.FeatureTypeInfo; import org.geoserver.catalog.LayerGroupInfo; import org.geoserver.catalog.LayerInfo; import org.geoserver.catalog.ProjectionPolicy; import org.geoserver.catalog.PublishedInfo; import org.geoserver.catalog.StyleInfo; import org.geoserver.catalog.WMSLayerInfo; import org.geoserver.catalog.WMSStoreInfo; import org.geoserver.catalog.WorkspaceInfo; import org.geoserver.data.test.MockData; import org.geoserver.data.test.SystemTestData; import org.geoserver.data.test.SystemTestData.LayerProperty; import org.geoserver.test.RemoteOWSTestSupport; import org.geoserver.wfs.WFSInfo; import org.geoserver.wms.WMSInfo; import org.geoserver.wms.WMSTestSupport; import org.geoserver.wms.featureinfo.*; import org.geotools.factory.CommonFactoryFinder; import org.geotools.geometry.jts.ReferencedEnvelope; import org.geotools.geometry.jts.ReferencedEnvelope3D; import org.geotools.image.test.ImageAssert; import org.geotools.referencing.CRS; import org.geotools.styling.FeatureTypeStyle; import org.geotools.styling.Rule; import org.geotools.styling.Style; import org.geotools.styling.StyleFactory; import org.geotools.util.logging.Logging; import org.junit.Test; import org.opengis.filter.Filter; import org.opengis.filter.FilterFactory; import org.w3c.dom.Document; import org.springframework.mock.web.MockHttpServletResponse; public class GetFeatureInfoTest extends WMSTestSupport { public static String WCS_PREFIX = "wcs"; public static String WCS_URI = "http://www.opengis.net/wcs/1.1.1"; public static QName TASMANIA_BM = new QName(WCS_URI, "BlueMarble", WCS_PREFIX); public static QName SQUARES = new QName(MockData.CITE_URI, "squares", MockData.CITE_PREFIX); public static QName CUSTOM = new QName(MockData.CITE_URI, "custom", MockData.CITE_PREFIX); public static QName POINT_TEST_2D = new QName(MockData.CITE_URI, "point_test_2d", MockData.CITE_PREFIX); public static QName POINT_TEST_3D = new QName(MockData.CITE_URI, "point_test_3d", MockData.CITE_PREFIX); public static QName STATES = new QName(MockData.SF_URI, "states", MockData.SF_PREFIX); @Override protected void onSetUp(SystemTestData testData) throws Exception { super.onSetUp(testData); Logging.getLogger("org.geoserver.ows").setLevel(Level.OFF); setupOpaqueGroup(getCatalog()); // setup buffer WMSInfo wmsInfo = getGeoServer().getService(WMSInfo.class); wmsInfo.setMaxBuffer(50); getGeoServer().save(wmsInfo); // force feature bounding in WFS WFSInfo wfsInfo = getGeoServer().getService(WFSInfo.class); wfsInfo.setFeatureBounding(true); getGeoServer().save(wfsInfo); // add a wms store too, if possible if (RemoteOWSTestSupport.isRemoteWMSStatesAvailable(LOGGER)) { // setup the wms store, resource and layer CatalogBuilder cb = new CatalogBuilder(getCatalog()); WMSStoreInfo wms = cb.buildWMSStore("demo"); wms.setCapabilitiesURL(RemoteOWSTestSupport.WMS_SERVER_URL + "service=WMS&request=GetCapabilities"); getCatalog().save(wms); cb.setStore(wms); WMSLayerInfo states = cb.buildWMSLayer("topp:states"); states.setName("rstates"); getCatalog().add(states); LayerInfo layer = cb.buildLayer(states); getCatalog().add(layer); } Catalog catalog = getCatalog(); testData.addStyle("thickStroke","thickStroke.sld",GetFeatureInfoTest.class,catalog); testData.addStyle("paramStroke","paramStroke.sld",GetFeatureInfoTest.class,catalog); testData.addStyle("raster","raster.sld",GetFeatureInfoTest.class,catalog); testData.addStyle("rasterScales","rasterScales.sld",GetFeatureInfoTest.class,catalog); testData.addStyle("squares","squares.sld",GetFeatureInfoTest.class,catalog); testData.addStyle("point_test","point_test.sld",GetFeatureInfoTest.class,catalog); testData.addStyle("scaleBased","scaleBased.sld",GetFeatureInfoTest.class,catalog); testData.addStyle("stacker","stacker.sld",GetFeatureInfoTest.class,catalog); testData.addVectorLayer(SQUARES,Collections.EMPTY_MAP,"squares.properties", GetFeatureInfoTest.class,catalog); Map propertyMap = new HashMap<SystemTestData.LayerProperty, Object>(); propertyMap.put(LayerProperty.STYLE,"raster"); testData.addRasterLayer(TASMANIA_BM, "tazbm.tiff","tiff",propertyMap, SystemTestData.class,catalog); testData.addRasterLayer(new QName(MockData.SF_URI, "mosaic", MockData.SF_PREFIX), "raster-filter-test.zip",null, propertyMap,SystemTestData.class, catalog); testData.addRasterLayer(CUSTOM, "custom.zip", null,propertyMap,GetFeatureInfoTest.class, catalog); Map<LayerProperty, Object> properties = new HashMap<SystemTestData.LayerProperty, Object>(); properties.put(LayerProperty.LATLON_ENVELOPE, new ReferencedEnvelope(130.875825803896, 130.898939990319, -16.4491956225999, -16.4338185791628, CRS.decode("EPSG:4326"))); properties.put(LayerProperty.ENVELOPE, new ReferencedEnvelope(130.875825803896, 130.898939990319, -16.4491956225999, -16.4338185791628, CRS.decode("EPSG:4326"))); properties.put(LayerProperty.SRS, 4326); testData.addVectorLayer(POINT_TEST_2D, properties, "point_test_2d.properties", GetFeatureInfoTest.class, catalog); properties = new HashMap<SystemTestData.LayerProperty, Object>(); properties.put(LayerProperty.LATLON_ENVELOPE, new ReferencedEnvelope(130.875825803896, 130.898939990319, -16.4491956225999, -16.4338185791628, CRS.decode("EPSG:4326"))); properties.put(LayerProperty.ENVELOPE, new ReferencedEnvelope3D(130.875825803896, 130.898939990319, -16.4491956225999, -16.4338185791628, 95.1442741322517, 98.1069524121285, CRS.decode("EPSG:4326"))); properties.put(LayerProperty.SRS, 4939); testData.addVectorLayer(POINT_TEST_3D, properties, "point_test_3d.properties", GetFeatureInfoTest.class, catalog); // set up a non-querable layer. testData.addStyle("Population", "Population.sld", GetFeatureInfoTest.class, catalog); testData.addVectorLayer(STATES, Collections.emptyMap(), "states.properties", GetFeatureInfoTest.class, catalog); LayerInfo layer = catalog.getLayerByName(getLayerId(STATES)); layer.setQueryable(false); catalog.save(layer); } /** * Test GetFeatureInfo with 3D content, and the result returns the expected point. */ @Test public void testPoint3d() throws Exception { FeatureTypeInfo info = getCatalog() .getFeatureTypeByName(MockData.CITE_URI, "point_test_3d"); ReferencedEnvelope b = info.getLatLonBoundingBox(); String bbox = b.getMinX() + "," + b.getMinY() + "," + b.getMaxX() + "," + b.getMaxY() + "&srs=EPSG:4326"; // first request against 2D dataset with the stacker transformation String layer2d = getLayerId(POINT_TEST_2D); String base2d = "wms?version=1.1.1&format=png&info_format=text/html&request=GetFeatureInfo&layers=" + layer2d + "&query_layers=" + layer2d + "&styles=point_test&bbox=" + bbox + "&feature_count=10"; Document dom2d = getAsDOM(base2d + "&width=" + 10 + "&height=" + 10 + "&x=" + 5 + "&y=" + 5); // print(dom2d); XMLAssert.assertXpathEvaluatesTo("11", "count(/html/body/table/tr)", dom2d); // second request against 3D dataset String layer3d = getLayerId(POINT_TEST_3D); String base3d = "wms?version=1.1.1&format=png&info_format=text/html&request=GetFeatureInfo&layers=" + layer3d + "&query_layers=" + layer3d + "&styles=point_test&bbox=" + bbox + "&feature_count=10"; Document dom3d = getAsDOM(base3d + "&width=" + 10 + "&height=" + 10 + "&x=" + 5 + "&y=" + 5); // print(dom3d); XMLAssert.assertXpathEvaluatesTo("11", "count(/html/body/table/tr)", dom3d); } @Test public void testPointStacker() throws Exception { String layerName = getLayerId(MockData.BRIDGES); // first request against 2D dataset String base2d = "wms?version=1.1.1&format=png&info_format=text/html&request=GetFeatureInfo&layers=" + layerName + "&query_layers=" + layerName + "&styles=stacker&bbox=-1,-1,1,1&srs=EPSG:4326&feature_count=10"; Document dom2d = getAsDOM( base2d + "&width=" + 100 + "&height=" + 100 + "&x=" + 50 + "&y=" + 50); print(dom2d); // used to throw an exception and fail XMLAssert.assertXpathEvaluatesTo("2", "count(/html/body/table/tr)", dom2d); } /** * Tests GML output does not break when asking for an area that has no data with * GML feature bounding enabled * * @param contentType Content-type (MIME-type) to test on. * @throws Exception When an XPath Exception occurs. */ private void testGMLNoData(String contentType) throws Exception { String layer = getLayerId(MockData.PONDS); String request = "wms?version=1.1.1&bbox=-0.002,-0.002,0.002,0.002&styles=&format=jpeg" + "&info_format=" + contentType +"&request=GetFeatureInfo&layers=" + layer + "&query_layers=" + layer + "&width=20&height=20&x=20&y=20"; Document dom = getAsDOM(request); XMLAssert.assertXpathEvaluatesTo("1", "count(//wfs:FeatureCollection)", dom); XMLAssert.assertXpathEvaluatesTo("0", "count(//gml:featureMember)", dom); } /** * Tests GML output does not break when asking for an area that has no data with * GML feature bounding enabled. This method tests GML 2 with Content-Type: * <code>application/vnd.ogc.gml</code>. * */ @Test public void testGMLNoData() throws Exception { this.testGMLNoData(GML2FeatureInfoOutputFormat.FORMAT); } /** * Tests GML output does not break when asking for an area that has no data with * GML feature bounding enabled. This method tests GML 2 with Content-Type: * <code>text/xml</code>. * */ @Test public void testXMLNoData() throws Exception { this.testGMLNoData(XML2FeatureInfoOutputFormat.FORMAT); } /** * Tests GML output does not break when asking for an area that has no data with * GML feature bounding enabled. This method tests GML 3.1.1 with Content-Type: * <code>text/xml; subtype=gml/3.1.1</code>. * */ @Test public void testXML311NoData() throws Exception { this.testGMLNoData(XML311FeatureInfoOutputFormat.FORMAT); } /** * Tests GML outside of * expected polygon * */ @Test public void testSimple() throws Exception { String layer = getLayerId(MockData.FORESTS); String request = "wms?version=1.1.1&bbox=-0.002,-0.002,0.002,0.002&styles=&format=jpeg" + "&info_format=text/plain&request=GetFeatureInfo&layers=" + layer + "&query_layers=" + layer + "&width=20&height=20&x=10&y=10"; String result = getAsString(request); //System.out.println(result); assertNotNull(result); assertTrue(result.indexOf("Green Forest") > 0); } @Test public void testAllowedMimeTypes() throws Exception { WMSInfo wms = getWMS().getServiceInfo(); GetFeatureInfoOutputFormat format = new TextFeatureInfoOutputFormat(getWMS()); wms.getGetFeatureInfoMimeTypes().add(format.getContentType()); wms.setGetFeatureInfoMimeTypeCheckingEnabled(true); getGeoServer().save(wms); // check mime type allowed String layer = getLayerId(MockData.FORESTS); String request = "wms?version=1.1.1&bbox=-0.002,-0.002,0.002,0.002&styles=&format=jpeg" + "&info_format=text/plain&request=GetFeatureInfo&layers=" + layer + "&query_layers=" + layer + "&width=20&height=20&x=10&y=10"; String result = getAsString(request); // System.out.println(result); assertNotNull(result); assertTrue(result.indexOf("Green Forest") > 0); // check mime type not allowed request = "wms?version=1.1.1&bbox=-0.002,-0.002,0.002,0.002&styles=&format=jpeg" + "&info_format="+GML3FeatureInfoOutputFormat.FORMAT+"&request=GetFeatureInfo&layers=" + layer + "&query_layers=" + layer + "&width=20&height=20&x=10&y=10"; result = getAsString(request); assertTrue(result.indexOf("ForbiddenFormat") > 0); wms.getGetFeatureInfoMimeTypes().clear(); wms.setGetFeatureInfoMimeTypeCheckingEnabled(false); getGeoServer().save(wms); request = "wms?version=1.1.1&bbox=-0.002,-0.002,0.002,0.002&styles=&format=jpeg" + "&info_format="+GML3FeatureInfoOutputFormat.FORMAT+"&request=GetFeatureInfo&layers=" + layer + "&query_layers=" + layer + "&width=20&height=20&x=10&y=10"; result = getAsString(request); assertTrue(result.indexOf("Green Forest") > 0); } /** * Tests property selection * expected polygon * */ @Test public void testSelectPropertiesVector() throws Exception { String layer = getLayerId(MockData.FORESTS); String request = "wms?version=1.1.1&bbox=-0.002,-0.002,0.002,0.002&styles=&format=jpeg&service=wms" + "&info_format=text/plain&request=GetFeatureInfo&layers=" + layer + "&query_layers=" + layer + "&width=20&height=20&x=10&y=10&propertyName=NAME,FID"; String result = getAsString(request); System.out.println(result); assertNotNull(result); int idxGeom = result.indexOf("the_geom"); int idxFid = result.indexOf("FID"); int idxName = result.indexOf("NAME"); assertEquals(-1, idxGeom); // geometry filtered out assertTrue(idxFid > 0); assertTrue(idxName > 0); assertTrue(idxName < idxFid); // properties got reordered as expected } /** * Tests a simple GetFeatureInfo works, and that the result contains the * expected polygon * */ @Test public void testSimpleHtml() throws Exception { String layer = getLayerId(MockData.FORESTS); String request = "wms?version=1.1.1&bbox=-0.002,-0.002,0.002,0.002&styles=&format=jpeg" + "&info_format=text/html&request=GetFeatureInfo&layers=" + layer + "&query_layers=" + layer + "&width=20&height=20&x=10&y=10"; Document dom = getAsDOM(request); // count lines that do contain a forest reference XMLAssert.assertXpathEvaluatesTo("1", "count(/html/body/table/tr/td[starts-with(.,'Forests.')])", dom); MockHttpServletResponse response = getAsServletResponse(request,""); // Check if the character encoding is the one expected assertTrue("UTF-8".equals(response.getCharacterEncoding())); } /** * Tests GetFeatureInfo with a buffer specified works, and that the result contains the * expected polygon * */ @Test public void testBuffer() throws Exception { // to setup the request and the buffer I rendered BASIC_POLYGONS using GeoServer, then played // against the image coordinates String layer = getLayerId(MockData.BASIC_POLYGONS); String base = "wms?version=1.1.1&bbox=-4.5,-2.,4.5,7&styles=&format=jpeg&info_format=text/html" + "&request=GetFeatureInfo&layers=" + layer + "&query_layers=" + layer + "&width=300&height=300"; Document dom = getAsDOM(base + "&x=85&y=230"); // make sure the document is empty, as we chose an area with no features inside XMLAssert.assertXpathEvaluatesTo("0", "count(/html/body/table/tr)", dom); // another request that will catch one feature due to the extended buffer, make sure it's in dom = getAsDOM(base + "&x=85&y=230&buffer=40"); XMLAssert.assertXpathEvaluatesTo("1", "count(/html/body/table/tr/td[starts-with(.,'BasicPolygons.')])", dom); XMLAssert.assertXpathEvaluatesTo("1", "count(/html/body/table/tr/td[. = 'BasicPolygons.1107531493630'])", dom); // this one would end up catching everything (3 features) if it wasn't that we say the max buffer at 50 // in the WMS configuration dom = getAsDOM(base + "&x=85&y=230&buffer=300"); XMLAssert.assertXpathEvaluatesTo("1", "count(/html/body/table/tr/td[starts-with(.,'BasicPolygons.')])", dom); XMLAssert.assertXpathEvaluatesTo("1", "count(/html/body/table/tr/td[. = 'BasicPolygons.1107531493630'])", dom); } /** * Tests GetFeatureInfo with a buffer specified works, and that the result contains the * expected polygon * */ @Test public void testAutoBuffer() throws Exception { String layer = getLayerId(MockData.BASIC_POLYGONS); String base = "wms?version=1.1.1&bbox=-4.5,-2.,4.5,7&format=jpeg&info_format=text/html" + "&request=GetFeatureInfo&layers=" + layer + "&query_layers=" + layer + "&width=300&height=300&x=111&y=229"; String url = base + "&styles="; Document dom = getAsDOM(url); print(dom); // make sure the document is empty, the style we chose has thin lines XMLAssert.assertXpathEvaluatesTo("0", "count(/html/body/table/tr)", dom); // another request that will catch one feature due to the style with a thick stroke, make sure it's in dom = getAsDOM(base + "&styles=thickStroke"); XMLAssert.assertXpathEvaluatesTo("1", "count(/html/body/table/tr/td[starts-with(.,'BasicPolygons.')])", dom); XMLAssert.assertXpathEvaluatesTo("1", "count(/html/body/table/tr/td[. = 'BasicPolygons.1107531493630'])", dom); } /** * Tests GetFeatureInfo uses the env params * */ @Test public void testParameterizedStyle() throws Exception { String layer = getLayerId(MockData.BASIC_POLYGONS); String base = "wms?version=1.1.1&bbox=-4.5,-2.,4.5,7&format=jpeg&info_format=text/html" + "&request=GetFeatureInfo&layers=" + layer + "&query_layers=" + layer + "&width=300&height=300&x=111&y=229&styles=paramStroke"; Document dom = getAsDOM(base); // make sure the document is empty, the style we chose has thin lines XMLAssert.assertXpathEvaluatesTo("0", "count(/html/body/table/tr)", dom); // another request that will catch one feature due to the style with a thick stroke, make sure it's in dom = getAsDOM(base + "&env=thickness:12"); // print(dom); XMLAssert.assertXpathEvaluatesTo("1", "count(/html/body/table/tr/td[starts-with(.,'BasicPolygons.')])", dom); XMLAssert.assertXpathEvaluatesTo("1", "count(/html/body/table/tr/td[. = 'BasicPolygons.1107531493630'])", dom); } /** * Tests GetFeatureInfo with a buffer specified works, and that the result contains the * expected polygon * */ @Test public void testBufferScales() throws Exception { String layer = getLayerId(SQUARES); String base = "wms?version=1.1.1&format=png&info_format=text/html&request=GetFeatureInfo&layers=" + layer + "&query_layers=" + layer + "&styles=squares&bbox=0,0,10000,10000&feature_count=10&srs=EPSG:32632"; // first request, should provide no result, scale is 1:100 int w = (int) (100.0 / 0.28 * 1000); // dpi compensation Document dom = getAsDOM(base + "&width=" + w + "&height=" + w + "&x=20&y=" + (w - 20)); // print(dom); // make sure the document is empty, the style we chose has thin lines XMLAssert.assertXpathEvaluatesTo("0", "count(/html/body/table/tr)", dom); // second request, should provide oe result, scale is 1:50 w = (int) (200.0 / 0.28 * 1000); // dpi compensation dom = getAsDOM(base + "&width=" + w + "&height=" + w + "&x=20&y=" + (w - 20)); print(dom); XMLAssert.assertXpathEvaluatesTo("1", "count(/html/body/table/tr/td[starts-with(.,'squares.')])", dom); XMLAssert .assertXpathEvaluatesTo("1", "count(/html/body/table/tr/td[. = 'squares.1'])", dom); // third request, should provide two result, scale is 1:10 w = (int) (1000.0 / 0.28 * 1000); // dpi compensation dom = getAsDOM(base + "&width=" + w + "&height=" + w + "&x=20&y=" + (w - 20)); // print(dom); XMLAssert.assertXpathEvaluatesTo("2", "count(/html/body/table/tr/td[starts-with(.,'squares.')])", dom); XMLAssert .assertXpathEvaluatesTo("1", "count(/html/body/table/tr/td[. = 'squares.1'])", dom); XMLAssert .assertXpathEvaluatesTo("1", "count(/html/body/table/tr/td[. = 'squares.2'])", dom); } /** * Tests a GetFeatureInfo again works, and that the result contains the * expected polygon * */ @Test public void testTwoLayers() throws Exception { String layer = getLayerId(MockData.FORESTS) + "," + getLayerId(MockData.LAKES); String request = "wms?version=1.1.1&bbox=-0.002,-0.002,0.002,0.002&styles=&format=jpeg" + "&info_format=text/html&request=GetFeatureInfo&layers=" + layer + "&query_layers=" + layer + "&width=20&height=20&x=10&y=10"; String result = getAsString(request); assertNotNull(result); assertTrue(result.indexOf("Green Forest") > 0); // GEOS-2603 GetFeatureInfo returns html tables without css style if more than one layer is selected assertTrue(result.indexOf("<style type=\"text/css\">") > 0); } /** * Tests a GetFeatureInfo again works, and that the result contains the * expected polygon * */ @Test public void testSelectPropertiesTwoVectorLayers() throws Exception { String layer = getLayerId(MockData.FORESTS) + "," + getLayerId(MockData.LAKES); String request = "wms?version=1.1.1&bbox=-0.002,-0.002,0.002,0.002&styles=&format=jpeg" + "&info_format=text/plain&request=GetFeatureInfo&layers=" + layer + "&query_layers=" + layer + "&width=20&height=20&x=10&y=10&buffer=10&service=wms" + "&feature_count=2&propertyName=(FID)(NAME)"; String result = getAsString(request); assertNotNull(result); int idxGeom = result.indexOf("the_geom"); int idxLakes= result.indexOf("Lakes"); int idxFid = result.indexOf("FID"); int idxName = result.indexOf("NAME"); assertEquals(-1, idxGeom); // geometry filtered out assertTrue(idxFid > 0); assertTrue(idxName > 0); assertTrue(idxLakes > 0); assertTrue(idxFid < idxLakes); // fid only for the first features assertTrue(idxName > idxLakes); // name only for the second features } /** * Tests a GetFeatureInfo again works, and that the result contains the * expected polygon * */ @Test public void testSelectPropertiesTwoVectorLayersOneList() throws Exception { String layer = getLayerId(MockData.FORESTS) + "," + getLayerId(MockData.LAKES); String request = "wms?version=1.1.1&bbox=-0.002,-0.002,0.002,0.002&styles=&format=jpeg" + "&info_format=text/plain&request=GetFeatureInfo&layers=" + layer + "&query_layers=" + layer + "&width=20&height=20&x=10&y=10&buffer=10&service=wms" + "&feature_count=2&propertyName=NAME"; String result = getAsString(request); // System.out.println(result); assertNotNull(result); int idxGeom = result.indexOf("the_geom"); int idxLakes= result.indexOf("Lakes"); int idxName1 = result.indexOf("NAME"); int idxName2 = result.indexOf("NAME", idxLakes); assertEquals(-1, idxGeom); // geometry filtered out assertTrue(idxName1 > 0); assertTrue(idxName2 > 0); assertTrue(idxLakes > 0); // name in both features assertTrue(idxName1 < idxLakes); assertTrue(idxName2 > idxLakes); } /** * Tests that FEATURE_COUNT is respected globally, not just per layer * */ @Test public void testTwoLayersFeatureCount() throws Exception { // this request hits on two overlapping features, a lake and a forest String layer = getLayerId(MockData.FORESTS) + "," + getLayerId(MockData.LAKES); String request = "wms?REQUEST=GetFeatureInfo&EXCEPTIONS=application%2Fvnd.ogc.se_xml&" + "BBOX=-0.002356%2C-0.004819%2C0.005631%2C0.004781&SERVICE=WMS&VERSION=1.1.0&X=267&Y=325" + "&INFO_FORMAT=application/vnd.ogc.gml" + "&QUERY_LAYERS=" + layer + "&Layers=" + layer + " &Styles=&WIDTH=426&HEIGHT=512" + "&format=image%2Fpng&srs=EPSG%3A4326"; // no feature count, just one should be returned Document dom = getAsDOM(request); XMLAssert.assertXpathEvaluatesTo("1", "count(//gml:featureMember)", dom); XMLAssert.assertXpathEvaluatesTo("1", "count(//cite:Forests)", dom); // feature count set to 2, both features should be there dom = getAsDOM(request + "&FEATURE_COUNT=2"); // print(dom); XMLAssert.assertXpathEvaluatesTo("2", "count(//gml:featureMember)", dom); XMLAssert.assertXpathEvaluatesTo("1", "count(//cite:Forests)", dom); XMLAssert.assertXpathEvaluatesTo("1", "count(//cite:Lakes)", dom); } /** * Check GetFeatureInfo returns an error if the format is not known, instead * of returning the text format as in * https://osgeo-org.atlassian.net/browse/GEOS-1924 * */ @Test public void testUknownFormat() throws Exception { String layer = MockData.FORESTS.getPrefix() + ":" + MockData.FORESTS.getLocalPart(); String request = "wms?version=1.1.1&bbox=-0.002,-0.002,0.002,0.002&styles=&format=jpeg&info_format=unknown/format&request=GetFeatureInfo&layers=" + layer + "&query_layers=" + layer + "&width=20&height=20&x=10&y=10"; Document doc = dom(get(request), true); // print(doc); XMLAssert.assertXpathEvaluatesTo("1", "count(//ServiceExceptionReport/ServiceException)", doc); XMLAssert.assertXpathEvaluatesTo("InvalidFormat", "/ServiceExceptionReport/ServiceException/@code", doc); XMLAssert.assertXpathEvaluatesTo("info_format", "/ServiceExceptionReport/ServiceException/@locator", doc); } @Test public void testCoverage() throws Exception { // https://osgeo-org.atlassian.net/browse/GEOS-2574 String layer = getLayerId(TASMANIA_BM); String request = "wms?service=wms&request=GetFeatureInfo&version=1.1.1" + "&layers=" + layer + "&styles=&bbox=146.5,-44.5,148,-43&width=600&height=600" + "&info_format=text/html&query_layers=" + layer + "&x=300&y=300&srs=EPSG:4326"; Document dom = getAsDOM(request); // we also have the charset which may be platf. dep. XMLAssert.assertXpathEvaluatesTo("1", "count(/html/body/table/tr/th[. = 'RED_BAND'])", dom); XMLAssert.assertXpathEvaluatesTo("1", "count(/html/body/table/tr/th[. = 'GREEN_BAND'])", dom); XMLAssert .assertXpathEvaluatesTo("1", "count(/html/body/table/tr/th[. = 'BLUE_BAND'])", dom); } @Test public void testCoveragePropertySelection() throws Exception { // https://osgeo-org.atlassian.net/browse/GEOS-2574 String layer = getLayerId(TASMANIA_BM); String request = "wms?service=wms&request=GetFeatureInfo&version=1.1.1" + "&layers=" + layer + "&styles=&bbox=146.5,-44.5,148,-43&width=600&height=600" + "&info_format=text/html&query_layers=" + layer + "&x=300&y=300&srs=EPSG:4326&propertyName=RED_BAND"; Document dom = getAsDOM(request); // we also have the charset which may be platf. dep. XMLAssert.assertXpathEvaluatesTo("1", "count(/html/body/table/tr/th[. = 'RED_BAND'])", dom); XMLAssert.assertXpathEvaluatesTo("0", "count(/html/body/table/tr/th[. = 'GREEN_BAND'])", dom); XMLAssert .assertXpathEvaluatesTo("0", "count(/html/body/table/tr/th[. = 'BLUE_BAND'])", dom); } @Test public void testCoverageGML() throws Exception { // https://osgeo-org.atlassian.net/browse/GEOS-3996 String layer = getLayerId(TASMANIA_BM); String request = "wms?service=wms&request=GetFeatureInfo&version=1.1.1" + "&layers=" + layer + "&styles=&bbox=146.5,-44.5,148,-43&width=600&height=600" + "&info_format=application/vnd.ogc.gml&query_layers=" + layer + "&x=300&y=300&srs=EPSG:4326"; Document dom = getAsDOM(request); //print(dom); XMLAssert.assertXpathEvaluatesTo("26.0", "//wfs:FeatureCollection/gml:featureMember/wcs:BlueMarble/wcs:RED_BAND", dom); XMLAssert.assertXpathEvaluatesTo("70.0", "//wfs:FeatureCollection/gml:featureMember/wcs:BlueMarble/wcs:GREEN_BAND", dom); XMLAssert.assertXpathEvaluatesTo("126.0", "//wfs:FeatureCollection/gml:featureMember/wcs:BlueMarble/wcs:BLUE_BAND", dom); } @Test public void testCoverageScales() throws Exception { String layer = getLayerId(TASMANIA_BM); String request = "wms?service=wms&request=GetFeatureInfo&version=1.1.1" + "&layers=" + layer + "&styles=rasterScales&bbox=146.5,-44.5,148,-43" + "&info_format=text/html&query_layers=" + layer + "&x=300&y=300&srs=EPSG:4326"; // this one should be blank Document dom = getAsDOM(request + "&width=300&height=300"); XMLAssert.assertXpathEvaluatesTo("0", "count(/html/body/table/tr/th)", dom); // this one should draw the coverage dom = getAsDOM(request + "&width=600&height=600"); // we also have the charset which may be platf. dep. XMLAssert.assertXpathEvaluatesTo("1", "count(/html/body/table/tr/th[. = 'RED_BAND'])", dom); XMLAssert.assertXpathEvaluatesTo("1", "count(/html/body/table/tr/th[. = 'GREEN_BAND'])", dom); XMLAssert .assertXpathEvaluatesTo("1", "count(/html/body/table/tr/th[. = 'BLUE_BAND'])", dom); } @Test public void testOutsideCoverage() throws Exception { // a request which is way large on the west side, lots of blank space String layer = getLayerId(TASMANIA_BM); String request = "wms?service=wms&request=GetFeatureInfo&version=1.1.1" + "&layers=" + layer + "&styles=raster&bbox=0,-90,148,-43" + "&info_format=text/html&query_layers=" + layer + "&width=300&height=300&x=10&y=150&srs=EPSG:4326"; // this one should be blank, but not be a service exception Document dom = getAsDOM(request + ""); XMLAssert.assertXpathEvaluatesTo("1", "count(/html)", dom); XMLAssert.assertXpathEvaluatesTo("0", "count(/html/body/table/tr/th)", dom); } /** * Check we report back an exception when query_layer contains layers not part of LAYERS */ @Test public void testUnkonwnQueryLayer() throws Exception { String layers1 = getLayerId(MockData.FORESTS) + "," + getLayerId(MockData.LAKES); String layers2 = getLayerId(MockData.FORESTS) + "," + getLayerId(MockData.BRIDGES); String request = "wms?version=1.1.1&bbox=-0.002,-0.002,0.002,0.002&styles=&format=jpeg&info_format=text/html&request=GetFeatureInfo&layers=" + layers1 + "&query_layers=" + layers2 + "&width=20&height=20&x=10&y=10&info"; Document dom = getAsDOM(request + ""); XMLAssert.assertXpathEvaluatesTo("1", "count(/ServiceExceptionReport)", dom); } @Test public void testLayerQualified() throws Exception { String layer = "Forests"; String q = "?version=1.1.1&bbox=-0.002,-0.002,0.002,0.002&styles=&format=jpeg" + "&info_format=text/plain&request=GetFeatureInfo&layers=" + layer + "&query_layers=" + layer + "&width=20&height=20&x=10&y=10"; String request = "cite/Ponds/wms" + q; Document dom = getAsDOM(request); assertEquals("ServiceExceptionReport", dom.getDocumentElement().getNodeName()); request = "cite/Forests/wms" + q; String result = getAsString(request); //System.out.println(result); assertNotNull(result); assertTrue(result.indexOf("Green Forest") > 0); } @Test public void testGroupWorkspaceQualified() throws Exception { // check the group works without workspace qualification String url = "wms?service=wms&version=1.1.1" + "&layers=nature&width=100&height=100&format=image/png" + "&srs=epsg:4326&bbox=-0.002,-0.003,0.005,0.002&info_format=text/plain" + "&request=GetFeatureInfo&query_layers=nature&x=50&y=50&feature_count=2"; String result = getAsString(url); assertTrue(result.indexOf("Blue Lake") > 0); assertTrue(result.indexOf("Green Forest") > 0); // check that it still works when workspace qualified result = getAsString("cite/" + url); assertTrue(result.indexOf("Blue Lake") > 0); assertTrue(result.indexOf("Green Forest") > 0); // but we have nothing if the workspace Document dom = getAsDOM("cdf/" + url); assertEquals("ServiceExceptionReport", dom.getDocumentElement().getNodeName()); } @Test public void testNonExactVersion() throws Exception { String layer = getLayerId(MockData.FORESTS); String request = "wms?version=1.0.0&bbox=-0.002,-0.002,0.002,0.002&styles=&format=jpeg" + "&info_format=text/plain&request=GetFeatureInfo&layers=" + layer + "&query_layers=" + layer + "&width=20&height=20&x=10&y=10"; String result = getAsString(request); assertNotNull(result); assertTrue(result.indexOf("Green Forest") > 0); request = "wms?version=1.1.0&bbox=-0.002,-0.002,0.002,0.002&styles=&format=jpeg" + "&info_format=text/plain&request=GetFeatureInfo&layers=" + layer + "&query_layers=" + layer + "&width=20&height=20&x=10&y=10"; result = getAsString(request); assertNotNull(result); assertTrue(result.indexOf("Green Forest") > 0); } @Test public void testRasterFilterRed() throws Exception { String response = getAsString("wms?bgcolor=0x000000&LAYERS=sf:mosaic&STYLES=" + "&FORMAT=image/png&SERVICE=WMS&VERSION=1.1.1" + "&REQUEST=GetFeatureInfo&SRS=EPSG:4326&BBOX=0,0,1,1&WIDTH=150&HEIGHT=150" + "&transparent=false&CQL_FILTER=location like 'red%25' + " + "&query_layers=sf:mosaic&x=10&y=10"); assertTrue(response.indexOf("RED_BAND = 255.0") > 0); assertTrue(response.indexOf("GREEN_BAND = 0.0") > 0); assertTrue(response.indexOf("BLUE_BAND = 0.0") > 0); } @Test public void testRasterFilterGreen() throws Exception { String response = getAsString("wms?bgcolor=0x000000&LAYERS=sf:mosaic&STYLES=" + "&FORMAT=image/png&SERVICE=WMS&VERSION=1.1.1" + "&REQUEST=GetFeatureInfo&SRS=EPSG:4326&BBOX=0,0,1,1&WIDTH=150&HEIGHT=150" + "&transparent=false&CQL_FILTER=location like 'green%25' + " + "&query_layers=sf:mosaic&x=10&y=10"); assertTrue(response.indexOf("RED_BAND = 0.0") > 0); assertTrue(response.indexOf("GREEN_BAND = 255.0") > 0); assertTrue(response.indexOf("BLUE_BAND = 0.0") > 0); } @Test public void testPropertySelectionWmsCascade() throws Exception { if (!RemoteOWSTestSupport.isRemoteWMSStatesAvailable(LOGGER)) { LOGGER.log(Level.WARNING, "Skipping testPropertySelectionWmsCascade"); return; } String result = getAsString("wms?REQUEST=GetFeatureInfo" + "&BBOX=-132.835937%2C21.132813%2C-64.867187%2C55.117188" + "&SERVICE=WMS&INFO_FORMAT=text/plain" + "&QUERY_LAYERS=rstates&FEATURE_COUNT=50&Layers=rstates&WIDTH=300&HEIGHT=150" + "&format=image%2Fpng&styles=&srs=EPSG%3A4326&version=1.1.1&x=149&y=70&propertyName=STATE_ABBR,STATE_NAME"); // System.out.println(result); int idxGeom = result.indexOf("the_geom"); int idxName = result.indexOf("STATE_NAME"); int idxFips = result.indexOf("STATE_FIPS"); int idxAbbr = result.indexOf("STATE_ABBR"); assertEquals(-1, idxGeom); assertEquals(-1, idxFips); assertTrue(idxAbbr > 0); assertTrue(idxName > 0); assertTrue(idxAbbr < idxName); } @Test public void testRasterKeepNative() throws Exception { // force it to "keep native" CoverageInfo ci = getCatalog().getCoverageByName(getLayerId(CUSTOM)); ci.setProjectionPolicy(ProjectionPolicy.NONE); getCatalog().save(ci); // make a first reprojected request on a pixel that's black (0) String result = getAsString("wms?REQUEST=GetFeatureInfo&EXCEPTIONS=application%2Fvnd.ogc.se_xml" + "&BBOX=-887430.34934%2C4467316.30601%2C-885862.361705%2C4468893.535223&SERVICE=WMS" + "&INFO_FORMAT=text%2Fplain&QUERY_LAYERS=cite%3Acustom&FEATURE_COUNT=50&Layers=custom" + "&WIDTH=509&HEIGHT=512&format=image%2Fjpeg&styles=&srs=epsg%3A900913&version=1.1.1&x=177&y=225"); assertTrue(result.contains("0.0")); // and now one with actual data, 2 result = getAsString("wms?REQUEST=GetFeatureInfo&EXCEPTIONS=application%2Fvnd.ogc.se_xml" + "&BBOX=-887430.34934%2C4467316.30601%2C-885862.361705%2C4468893.535223&SERVICE=WMS" + "&INFO_FORMAT=text%2Fplain&QUERY_LAYERS=cite%3Acustom&FEATURE_COUNT=50&Layers=custom" + "&WIDTH=509&HEIGHT=512&format=image%2Fjpeg&styles=&srs=epsg%3A900913&version=1.1.1&x=135&y=223"); assertTrue(result.contains("2.0")); } @Test public void testGMLWithPostFilter() throws Exception { //we need to create a situation where a post filter is setup, simple way is to change the // style so that its filter is an or with more than 20 children Catalog cat = getCatalog(); LayerInfo l = cat.getLayerByName(getLayerId(MockData.NAMED_PLACES)); StyleInfo style = l.getDefaultStyle(); Style s = style.getStyle(); FeatureTypeStyle fts = s.featureTypeStyles().get(0); FilterFactory ff = CommonFactoryFinder.getFilterFactory(null); StyleFactory sf = CommonFactoryFinder.getStyleFactory(); for (int i = 0; i < 21; i++) { Filter f = ff.equals(ff.literal(1), ff.literal(1)); Rule r = sf.createRule(); r.setFilter(f); r.symbolizers().add(sf.createPolygonSymbolizer()); fts.rules().add(r); } cat.getResourcePool().writeStyle(style, s); cat.save(style); String layer = getLayerId(MockData.NAMED_PLACES); String request = "wms?service=wms&request=GetFeatureInfo&version=1.1.1" + "&layers=" + layer + "&styles=&bbox=0.000004,-0.00285,0.005596,0.00415&width=409&height=512" + "&info_format=application/vnd.ogc.gml&query_layers=" + layer + "&x=194&y=229&srs=EPSG:4326"; Document dom = getAsDOM(request); assertEquals("wfs:FeatureCollection", dom.getDocumentElement().getNodeName()); } /** * The rendering engine has a 10-6 tolerance when evaluating rule scale activation, GetFeatureInfo did not * @throws Exception */ @Test public void testScaleTolerance() throws Exception { String layer = getLayerId(MockData.BASIC_POLYGONS); String getMap = "wms?version=1.1.1&bbox=-10000,20000,10000,40000&srs=EPSG:900913&styles=scaleBased&format=image/png&info_format=text/html" + "&request=GetMap&layers=" + layer + "&query_layers=" + layer + "&width=2041&height=2041"; BufferedImage image = getAsImage(getMap, "image/png"); // ImageIO.write(image, "png", new File("/tmp/test.png")); assertPixel(image, 150, 150, Color.BLUE); } /** * Test GetFeatureInfo on a group layer with some no-queryable layers * @throws Exception */ @Test public void testGroupLayerWithNotQueryableLayers() throws Exception { Catalog catalog = getCatalog(); CatalogFactory factory = catalog.getFactory(); WorkspaceInfo workspace = catalog.getWorkspaceByName(MockData.CITE_PREFIX); String groupLayer = "glqueryable"; // Only last layer will be queryable. LayerInfo buildingsLayer = catalog.getLayerByName(getLayerId(MockData.BUILDINGS)); buildingsLayer.setQueryable(false); catalog.save(buildingsLayer); LayerInfo bridgesLayer = catalog.getLayerByName(getLayerId(MockData.BRIDGES)); bridgesLayer.setQueryable(false); catalog.save(bridgesLayer); LayerInfo forestLayer = catalog.getLayerByName(getLayerId(MockData.FORESTS)); forestLayer.setQueryable(true); catalog.save(forestLayer); LayerGroupInfo layerGroup = factory.createLayerGroup(); layerGroup.setName(groupLayer); layerGroup.setWorkspace(workspace); layerGroup.getLayers().add(buildingsLayer); layerGroup.getLayers().add(bridgesLayer); layerGroup.getLayers().add(forestLayer); new CatalogBuilder(catalog).calculateLayerGroupBounds(layerGroup); catalog.add(layerGroup); String name = MockData.CITE_PREFIX+":"+groupLayer; String request = "wms?bbox=-0.002,-0.002,0.002,0.002&styles=&format=jpeg" + "&info_format=text/plain&request=GetFeatureInfo&layers=" + name + "&query_layers=" + name + "&width=20&height=20&x=10&y=10"; String result = getAsString(request); assertNotNull(result); assertTrue(result.indexOf("Green Forest") > 0); buildingsLayer.setQueryable(true); catalog.save(buildingsLayer); bridgesLayer.setQueryable(true); catalog.save(bridgesLayer); catalog.remove(layerGroup); } /** * Test GetFeatureInfo on a group layer with no-queryable flag activated * @throws Exception */ @Test public void testNotQueryableGroupLayer() throws Exception { Catalog catalog = getCatalog(); CatalogFactory factory = catalog.getFactory(); WorkspaceInfo workspace = catalog.getWorkspaceByName(MockData.CITE_PREFIX); String groupLayer = "glnotqueryable"; LayerGroupInfo layerGroup = factory.createLayerGroup(); layerGroup.setName(groupLayer); layerGroup.setWorkspace(workspace); layerGroup.getLayers().add( catalog.getLayerByName(getLayerId(MockData.FORESTS)) ); new CatalogBuilder(catalog).calculateLayerGroupBounds(layerGroup); catalog.add(layerGroup); String name = MockData.CITE_PREFIX+":"+groupLayer; String request = "wms?bbox=-0.002,-0.002,0.002,0.002&styles=&format=jpeg" + "&info_format=text/plain&request=GetFeatureInfo&layers=" + name + "&query_layers=" + name + "&width=20&height=20&x=10&y=10"; String result = getAsString(request); assertNotNull(result); assertTrue(result.indexOf("Green Forest") > 0); // Test no-queryable flag activated layerGroup.setQueryDisabled(true); result = getAsString(request); assertNotNull(result); assertTrue(result.indexOf("no layer was queryable") > 0); catalog.remove(layerGroup); } @Test public void testGetFeatureInfoOpaqueGroup() throws Exception { String url = "wms?LAYERS=" + OPAQUE_GROUP + "&STYLES=&FORMAT=image%2Fpng" + "&SERVICE=WMS&VERSION=1.1.1&REQUEST=GetFeatureInfo&SRS=EPSG%3A4326&WIDTH=256&HEIGHT=256&BBOX=-0.0043,-0.0025,0.0043,0.0025" + "&info_format=text/plain&request=GetFeatureInfo&&query_layers=" + OPAQUE_GROUP + "&x=105&y=107"; String response = getAsString(url); assertThat(response, containsString("FID = 102")); } @Test public void testFeatureInfoLayersInOpaqueGroup() throws Exception { LayerGroupInfo group = getCatalog().getLayerGroupByName(OPAQUE_GROUP); for (PublishedInfo pi : group.layers()) { final String layerName = pi.prefixedName(); String url = "wms?LAYERS=" + layerName + "&STYLES=&FORMAT=image%2Fpng" + "&SERVICE=WMS&VERSION=1.1.1&REQUEST=GetFeatureInfo&SRS=EPSG%3A4326&WIDTH=256&HEIGHT=256&BBOX=-0.0043,-0.0025,0.0043,0.0025" + "&info_format=text/plain&request=GetFeatureInfo&&query_layers=" + layerName + "&x=105&y=107"; Document dom = getAsDOM(url); //print(dom); // should not be found XMLAssert.assertXpathEvaluatesTo("1", "count(/ServiceExceptionReport)", dom); XMLAssert.assertXpathEvaluatesTo("layers", "//ServiceException/@locator", dom); XMLAssert.assertXpathEvaluatesTo("LayerNotDefined", "//ServiceException/@code", dom); } } @Test public void testQueryableAndNonQueryableLayersWithStyles() throws Exception { String states = getLayerId(STATES); String forests = getLayerId(MockData.FORESTS); String request = "wms?version=1.1.1&bbox=-0.002,-0.002,0.002,0.002&format=jpeg" + "&info_format=text/plain&request=GetFeatureInfo&width=20&height=20&x=10&y=10" + "&layers=" + states + "," + forests + "&query_layers=" + states + "," + forests + "&styles=Population,Forests"; String result = getAsString(request); // System.out.println(result); assertNotNull(result); assertTrue(result.indexOf("Green Forest") > 0); } @Test public void testQueryableAndNonQueryableLayersWithCqlFilter() throws Exception { String states = getLayerId(STATES); String forests = getLayerId(MockData.FORESTS); String request = "wms?version=1.1.1&bbox=-0.002,-0.002,0.002,0.002&format=jpeg" + "&info_format=text/plain&request=GetFeatureInfo&width=20&height=20&x=10&y=10" + "&layers=" + states + "," + forests + "&query_layers=" + states + "," + forests + "&styles=&cql_filter=PERSONS>25000000;NAME='Green Forest'"; String result = getAsString(request); // System.out.println(result); assertNotNull(result); assertTrue(result.indexOf("Green Forest") > 0); } @Test public void testQueryableAndNonQueryableLayersWithFilter() throws Exception { String states = getLayerId(STATES); String forests = getLayerId(MockData.FORESTS); String request = "wms?version=1.1.1&bbox=-0.002,-0.002,0.002,0.002&format=jpeg" + "&info_format=text/plain&request=GetFeatureInfo&width=20&height=20&x=10&y=10" + "&layers=" + states + "," + forests + "&query_layers=" + states + "," + forests + "&styles=&filter=" + "(%3CFilter%3E%3CPropertyIsGreaterThan%3E%3CPropertyName%3EPERSONS%3C/PropertyName%3E%3CLiteral%3E25000000%3C/Literal%3E%3C/PropertyIsGreaterThan%3E%3C/Filter%3E)" + "(%3CFilter%3E%3CPropertyIsEqualTo%3E%3CPropertyName%3ENAME%3C/PropertyName%3E%3CLiteral%3EGreen%20Forest%3C/Literal%3E%3C/PropertyIsEqualTo%3E%3C/Filter%3E)"; String result = getAsString(request); // System.out.println(result); assertNotNull(result); assertTrue(result.indexOf("Green Forest") > 0); } }