Java tutorial
/* Copyright (c) 2001 - 2013 OpenPlans - www.openplans.org. All rights reserved. * This code is licensed under the GPL 2.0 license, available at the root * application directory. */ package org.geoserver.kml; import static org.custommonkey.xmlunit.XMLAssert.assertXpathEvaluatesTo; import static org.custommonkey.xmlunit.XMLAssert.assertXpathExists; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertThat; import static org.junit.Assert.assertTrue; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.InputStream; import java.io.UnsupportedEncodingException; import java.net.URL; import java.net.URLDecoder; import java.util.HashMap; import java.util.Map; import java.util.regex.Pattern; import java.util.zip.ZipEntry; import java.util.zip.ZipFile; import java.util.zip.ZipInputStream; import javax.xml.namespace.QName; import javax.xml.xpath.XPath; import javax.xml.xpath.XPathConstants; import javax.xml.xpath.XPathFactory; import org.apache.commons.io.FileUtils; import org.apache.commons.io.IOUtils; import org.custommonkey.xmlunit.XMLAssert; import org.custommonkey.xmlunit.XMLUnit; import org.custommonkey.xmlunit.XpathEngine; import org.geoserver.catalog.Catalog; import org.geoserver.catalog.FeatureTypeInfo; import org.geoserver.config.GeoServer; import org.geoserver.config.GeoServerInfo; import org.geoserver.data.test.MockData; import org.geoserver.data.test.SystemTestData; import org.geoserver.ows.kvp.FormatOptionsKvpParser; import org.geoserver.ows.util.KvpUtils; import org.geoserver.wms.GetMapRequest; import org.geoserver.wms.WMSMapContent; import org.geoserver.wms.WMSTestSupport; import org.geotools.geometry.jts.ReferencedEnvelope; import org.geotools.referencing.crs.DefaultGeographicCRS; import org.junit.Test; import org.springframework.util.xml.SimpleNamespaceContext; import org.w3c.dom.Document; import org.xml.sax.InputSource; import com.mockrunner.mock.web.MockHttpServletResponse; /** * Some functional tests for kml reflector * * @author David Winslow (OpenGeo) * @author Gabriel Roldan (OpenGeo) * @version $Id$ */ public class KMLReflectorTest extends WMSTestSupport { @Override protected void onSetUp(SystemTestData testData) throws Exception { super.onSetUp(testData); Catalog catalog = getCatalog(); testData.addStyle("Bridge", "bridge.sld", getClass(), catalog); testData.addStyle("allsymbolizers", "allsymbolizers.sld", getClass(), catalog); testData.addStyle("labels", "labels.sld", getClass(), catalog); testData.addStyle("SingleFeature", "singlefeature.sld", getClass(), catalog); testData.addStyle("BridgeSubdir", "bridgesubdir.sld", getClass(), catalog); testData.addStyle("dynamicsymbolizer", "dynamicsymbolizer.sld", getClass(), catalog); testData.addStyle("relativeds", "relativeds.sld", getClass(), catalog); testData.addStyle("big-local-image", "big-local-image.sld", getClass(), catalog); testData.addStyle("big-mark", "big-mark.sld", getClass(), catalog); testData.copyTo(getClass().getResourceAsStream("bridge.png"), "styles/bridge.png"); testData.copyTo(getClass().getResourceAsStream("planet-42.png"), "styles/planet-42.png"); File stylesDir = new File(testData.getDataDirectoryRoot(), "styles"); new File(stylesDir, "graphics").mkdir(); testData.copyTo(getClass().getResourceAsStream("bridge.png"), "styles/graphics/bridgesubdir.png"); } /** * Verify that NetworkLink's generated by the reflector do not include a BBOX parameter, since * that would override the BBOX provided by Google Earth. * * @see http://jira.codehaus.org/browse/GEOS-2185 */ @Test public void testNoBBOXInHREF() throws Exception { final String layerName = MockData.BASIC_POLYGONS.getPrefix() + ":" + MockData.BASIC_POLYGONS.getLocalPart(); final XpathEngine xpath = XMLUnit.newXpathEngine(); String requestURL = "wms/kml?mode=refresh&layers=" + layerName; Document dom = getAsDOM(requestURL); // print(dom); assertXpathEvaluatesTo("1", "count(kml:kml/kml:Document)", dom); assertXpathEvaluatesTo("1", "count(kml:kml/kml:Document/kml:NetworkLink)", dom); assertXpathEvaluatesTo("1", "count(kml:kml/kml:Document/kml:LookAt)", dom); assertXpathEvaluatesTo(layerName, "kml:kml/kml:Document/kml:NetworkLink[1]/kml:name", dom); assertXpathEvaluatesTo("1", "kml:kml/kml:Document/kml:NetworkLink[1]/kml:open", dom); assertXpathEvaluatesTo("1", "kml:kml/kml:Document/kml:NetworkLink[1]/kml:visibility", dom); assertXpathEvaluatesTo("onStop", "kml:kml/kml:Document/kml:NetworkLink[1]/kml:Url/kml:viewRefreshMode", dom); assertXpathEvaluatesTo("1.0", "kml:kml/kml:Document/kml:NetworkLink[1]/kml:Url/kml:viewRefreshTime", dom); assertXpathEvaluatesTo("1.0", "kml:kml/kml:Document/kml:NetworkLink[1]/kml:Url/kml:viewBoundScale", dom); Map<String, Object> expectedKVP = KvpUtils.parseQueryString( "http://localhost:80/geoserver/wms?format_options=autofit%3Atrue%3BKMPLACEMARK%3Afalse%3BKMATTR%3Atrue%3BKMSCORE%3A40%3BSUPEROVERLAY%3Afalse%3B&service=wms&srs=EPSG%3A4326&width=2048&styles=BasicPolygons&height=2048&transparent=false&request=GetMap&layers=cite%3ABasicPolygons&format=application%2Fvnd.google-earth.kml+xml&version=1.1.1"); Map<String, Object> resultedKVP = KvpUtils .parseQueryString(xpath.evaluate("kml:kml/kml:Document/kml:NetworkLink[1]/kml:Url/kml:href", dom)); assertMapsEqual(expectedKVP, resultedKVP); String href = xpath.evaluate("kml:kml/kml:Document/kml:NetworkLink/kml:Link/kml:href", dom); Pattern badPattern = Pattern.compile("&bbox=", Pattern.CASE_INSENSITIVE); assertFalse(badPattern.matcher(href).matches()); } /** * Verify that NetworkLink's generated by the reflector do not include a BBOX parameter, since * that would override the BBOX provided by Google Earth. * * @see http://jira.codehaus.org/browse/GEOS-2185 */ @Test public void testBBOXInHREF() throws Exception { final XpathEngine xpath = XMLUnit.newXpathEngine(); String requestURL = "wms/kml?layers=" + getLayerId(MockData.BASIC_POLYGONS) + "&bbox=-1,-1,-0.5,-0.5&mode=download"; Document dom = getAsDOM(requestURL); // print(dom); assertEquals(1, xpath.getMatchingNodes("//kml:Placemark", dom).getLength()); } @Test public void testDownloadMultiLayer() throws Exception { String requestURL = "wms/kml?&layers=" + getLayerId(MockData.LAKES) + "," + getLayerId(MockData.FORESTS); MockHttpServletResponse response = getAsServletResponse(requestURL); assertEquals(KMLMapOutputFormat.MIME_TYPE, response.getContentType()); assertEquals("attachment; filename=cite-Lakes_cite-Forests.kml", response.getHeader("Content-Disposition")); Document dom = dom(getBinaryInputStream(response)); // print(dom); assertXpathEvaluatesTo("1", "count(kml:kml/kml:Document)", dom); assertXpathEvaluatesTo("2", "count(kml:kml/kml:Document/kml:NetworkLink)", dom); assertXpathEvaluatesTo("2", "count(kml:kml/kml:Document/kml:NetworkLink/kml:LookAt)", dom); } /** * Do some spot checks on the KML generated when an overlay hierarchy is requested. */ @Test public void testSuperOverlayReflection() throws Exception { final String layerName = MockData.BASIC_POLYGONS.getPrefix() + ":" + MockData.BASIC_POLYGONS.getLocalPart(); final String requestUrl = "wms/kml?layers=" + layerName + "&styles=&mode=superoverlay"; Document dom = getAsDOM(requestUrl); // print(dom); assertEquals("kml", dom.getDocumentElement().getLocalName()); assertXpathExists("kml:kml/kml:Document/kml:Folder/kml:NetworkLink/kml:Link/kml:href", dom); assertXpathExists("kml:kml/kml:Document/kml:LookAt/kml:longitude", dom); } @Test public void testWmsRepeatedLayerWithNonStandardStyleAndCqlFiler() throws Exception { final String layerName = MockData.BASIC_POLYGONS.getPrefix() + ":" + MockData.BASIC_POLYGONS.getLocalPart(); String requestUrl = "wms/kml?mode=refresh&layers=" + layerName + "," + layerName + "&styles=Default,Default&cql_filter=att1<10;att1>1000"; Document dom = getAsDOM(requestUrl); assertEquals("kml", dom.getDocumentElement().getLocalName()); assertXpathEvaluatesTo("2", "count(kml:kml/kml:Document/kml:NetworkLink)", dom); assertXpathEvaluatesTo(layerName, "kml:kml/kml:Document/kml:NetworkLink[1]/kml:name", dom); assertXpathEvaluatesTo(layerName, "kml:kml/kml:Document/kml:NetworkLink[2]/kml:name", dom); XpathEngine xpath = XMLUnit.newXpathEngine(); String url1 = xpath.evaluate("/kml:kml/kml:Document/kml:NetworkLink[1]/kml:Url/kml:href", dom); String url2 = xpath.evaluate("/kml:kml/kml:Document/kml:NetworkLink[2]/kml:Url/kml:href", dom); assertNotNull(url1); assertNotNull(url2); Map<String, Object> kvp1 = KvpUtils.parseQueryString(url1); Map<String, Object> kvp2 = KvpUtils.parseQueryString(url2); assertEquals(layerName, kvp1.get("layers")); assertEquals(layerName, kvp2.get("layers")); assertEquals("Default", kvp1.get("styles")); assertEquals("Default", kvp2.get("styles")); assertEquals("att1<10", kvp1.get("cql_filter")); assertEquals("att1>1000", kvp2.get("cql_filter")); } /** * @see {@link KMLReflector#organizeFormatOptionsParams(Map, Map)} * @throws Exception */ @Test public void testKmlFormatOptionsAsKVP() throws Exception { final String layerName = MockData.BASIC_POLYGONS.getPrefix() + ":" + MockData.BASIC_POLYGONS.getLocalPart(); final String baseUrl = "wms/kml?layers=" + layerName + "&styles=&mode=superoverlay"; final String requestUrl = baseUrl + "&kmltitle=myCustomLayerTitle&kmscore=10&legend=true&kmattr=true"; Document dom = getAsDOM(requestUrl); XpathEngine xpath = XMLUnit.newXpathEngine(); // print(dom); // all the kvp parameters (which should be set as format_options now are correctly parsed) String result = xpath.evaluate("//kml:NetworkLink/kml:Link/kml:href", dom); Map<String, Object> kvp = KvpUtils.parseQueryString(result); String formatOptions = (String) kvp.get("format_options"); assertEquals( "LEGEND:true;SUPEROVERLAY:true;AUTOFIT:true;KMPLACEMARK:false;OVERLAYMODE:auto;KMSCORE:10;KMATTR:true;KMLTITLE:myCustomLayerTitle;", formatOptions); } @Test public void testKmlTitleFormatOption() throws Exception { final String layerName = MockData.BASIC_POLYGONS.getPrefix() + ":" + MockData.BASIC_POLYGONS.getLocalPart(); final String requestUrl = "wms/kml?layers=" + layerName + "&styles=&mode=superoverlay&format_options=kmltitle:myCustomLayerTitle"; // System.out.println(getAsServletResponse(requestUrl).getContentType()); Document dom = getAsDOM(requestUrl); // print(dom); assertEquals("kml", dom.getDocumentElement().getLocalName()); assertXpathEvaluatesTo("myCustomLayerTitle", "/kml:kml/kml:Document/kml:name", dom); } /** * See http://jira.codehaus.org/browse/GEOS-1947 * * @throws Exception */ @Test public void testExternalGraphicBackround() throws Exception { final String requestUrl = "wms/kml?layers=" + getLayerId(MockData.BRIDGES) + "&styles=Bridge&mode=download"; Document dom = getAsDOM(requestUrl); // print(dom); // make sure we are generating icon styles, but that we're not sticking a color onto them XMLAssert.assertXpathEvaluatesTo("1", "count(//kml:Style/kml:IconStyle/kml:Icon/kml:href)", dom); XMLAssert.assertXpathEvaluatesTo("0", "count(//kml:Style/kml:IconStyle/kml:Icon/kml:color)", dom); } /** * See http://jira.codehaus.org/browse/GEOS-3994 * * @throws Exception */ @Test public void testExternalGraphicSubdir() throws Exception { final String requestUrl = "wms/kml?layers=" + getLayerId(MockData.BRIDGES) + "&styles=BridgeSubdir&mode=download"; Document dom = getAsDOM(requestUrl); // print(dom); // make sure we are generating icon styles with the subdir path XMLAssert.assertXpathEvaluatesTo("http://localhost:8080/geoserver/styles/graphics/bridgesubdir.png", "//kml:Style[1]/kml:IconStyle/kml:Icon/kml:href", dom); } /** * See http://jira.codehaus.org/browse/GEOS-3965 * * @throws Exception */ @Test public void testProxyBaseURL() throws Exception { GeoServer gs = getGeoServer(); try { GeoServerInfo info = gs.getGlobal(); info.getSettings().setProxyBaseUrl("http://myhost:9999/gs"); gs.save(info); final String requestUrl = "wms/kml?layers=" + getLayerId(MockData.BRIDGES) + "&styles=Bridge&mode=download"; Document dom = getAsDOM(requestUrl); // make sure we are using the proxy base URL XMLAssert.assertXpathEvaluatesTo("http://myhost:9999/gs/styles/bridge.png", "//kml:Style/kml:IconStyle/kml:Icon/kml:href", dom); } finally { GeoServerInfo info = gs.getGlobal(); info.getSettings().setProxyBaseUrl(null); gs.save(info); } } @Test public void testFilteredData() throws Exception { // the style selects a single feature final String requestUrl = "wms/kml?layers=" + getLayerId(MockData.BASIC_POLYGONS) + "&styles=SingleFeature&mode=download"; Document dom = getAsDOM(requestUrl); // print(dom); // check we have indeed a single feature assertXpathEvaluatesTo("1", "count(//kml:Placemark)", dom); } @Test public void testForceRasterKml() throws Exception { final String requestUrl = "wms/kml?layers=" + getLayerId(MockData.BASIC_POLYGONS) + "&styles=&mode=download&KMSCORE=0"; Document dom = getAsDOM(requestUrl); // print(dom); assertXpathEvaluatesTo("1", "count(//kml:Folder/kml:GroundOverlay)", dom); String href = XMLUnit.newXpathEngine().evaluate("//kml:Folder/kml:GroundOverlay/kml:Icon/kml:href", dom); assertTrue(href.startsWith("http://localhost:8080/geoserver/wms")); assertTrue(href.contains("request=GetMap")); assertTrue(href.contains("format=image%2Fpng")); } @Test public void testForceRasterKmz() throws Exception { final String requestUrl = "wms/kml?layers=" + getLayerId(MockData.BASIC_POLYGONS) + "&styles=&mode=download&KMSCORE=0&format=" + KMZMapOutputFormat.MIME_TYPE; MockHttpServletResponse response = getAsServletResponse(requestUrl); assertEquals(KMZMapOutputFormat.MIME_TYPE, response.getContentType()); assertEquals("attachment; filename=cite-BasicPolygons.kmz", response.getHeader("Content-Disposition")); ZipInputStream zis = new ZipInputStream(getBinaryInputStream(response)); try { // first entry, the kml document itself ZipEntry entry = zis.getNextEntry(); assertEquals("wms.kml", entry.getName()); // we need to clone the input stream, as dom(is) closes the stream byte[] data = IOUtils.toByteArray(zis); Document dom = dom(new ByteArrayInputStream(data)); assertXpathEvaluatesTo("1", "count(//kml:Folder/kml:GroundOverlay)", dom); String href = XMLUnit.newXpathEngine().evaluate("//kml:Folder/kml:GroundOverlay/kml:Icon/kml:href", dom); assertEquals("images/layers_0.png", href); zis.closeEntry(); // the images folder entry = zis.getNextEntry(); assertEquals("images/", entry.getName()); zis.closeEntry(); // the ground overlay for the raster layer entry = zis.getNextEntry(); assertEquals("images/layers_0.png", entry.getName()); zis.closeEntry(); assertNull(zis.getNextEntry()); } finally { zis.close(); } } @Test public void testRasterTransformerSLD() throws Exception { URL url = getClass().getResource("allsymbolizers.sld"); final String requestUrl = "wms/kml?layers=" + getLayerId(MockData.BASIC_POLYGONS) + "&sld=" + url.toExternalForm() + "&mode=download&KMSCORE=0"; Document dom = getAsDOM(requestUrl); // print(dom); assertXpathEvaluatesTo("1", "count(//kml:Folder/kml:GroundOverlay)", dom); String href = XMLUnit.newXpathEngine().evaluate("//kml:Folder/kml:GroundOverlay/kml:Icon/kml:href", dom); href = URLDecoder.decode(href, "UTF-8"); assertTrue(href.startsWith("http://localhost:8080/geoserver/wms")); assertTrue(href.contains("request=GetMap")); assertTrue(href.contains("format=image/png")); assertTrue(href.contains("&sld=" + url.toExternalForm())); } @Test public void testRasterPlacemarkTrue() throws Exception { doTestRasterPlacemark(true); } @Test public void testRasterPlacemarkFalse() throws Exception { doTestRasterPlacemark(false); } protected void doTestRasterPlacemark(boolean doPlacemarks) throws Exception { // the style selects a single feature final String requestUrl = "wms/kml?layers=" + getLayerId(MockData.BASIC_POLYGONS) + "&styles=&mode=download&kmscore=0&format_options=kmplacemark:" + doPlacemarks + "&format=" + KMZMapOutputFormat.MIME_TYPE; MockHttpServletResponse response = getAsServletResponse(requestUrl); assertEquals(KMZMapOutputFormat.MIME_TYPE, response.getContentType()); ZipFile zipFile = null; try { // create the kmz File tempDir = org.geoserver.data.util.IOUtils.createRandomDirectory("./target", "kmplacemark", "test"); tempDir.deleteOnExit(); File zip = new File(tempDir, "kmz.zip"); zip.deleteOnExit(); FileOutputStream output = new FileOutputStream(zip); FileUtils.writeByteArrayToFile(zip, getBinary(response)); output.flush(); output.close(); assertTrue(zip.exists()); // unzip and test it zipFile = new ZipFile(zip); ZipEntry entry = zipFile.getEntry("wms.kml"); assertNotNull(entry); assertNotNull(zipFile.getEntry("images/layers_0.png")); // unzip the wms.kml to file byte[] buffer = new byte[1024]; int len; InputStream inStream = zipFile.getInputStream(entry); File temp = File.createTempFile("test_out", "kmz", tempDir); temp.deleteOnExit(); BufferedOutputStream outStream = new BufferedOutputStream(new FileOutputStream(temp)); while ((len = inStream.read(buffer)) >= 0) outStream.write(buffer, 0, len); inStream.close(); outStream.close(); // read in the wms.kml and check its contents Document document = dom(new BufferedInputStream(new FileInputStream(temp))); // print(document); assertEquals("kml", document.getDocumentElement().getNodeName()); if (doPlacemarks) { assertEquals(getFeatureSource(MockData.BASIC_POLYGONS).getFeatures().size(), document.getElementsByTagName("Placemark").getLength()); XMLAssert.assertXpathEvaluatesTo("3", "count(//kml:Placemark//kml:Point)", document); } else { assertEquals(0, document.getElementsByTagName("Placemark").getLength()); } } finally { if (zipFile != null) { zipFile.close(); } } } @Test public void testStyleConverter() throws Exception { // the style selects a single feature final String requestUrl = "wms/kml?layers=" + getLayerId(MockData.BASIC_POLYGONS) + "&styles=allsymbolizers&mode=download"; Document doc = getAsDOM(requestUrl); // print(doc); XMLAssert.assertXpathEvaluatesTo("1", "count(//kml:Placemark[1]/kml:Style)", doc); XMLAssert.assertXpathEvaluatesTo("0", "count(//kml:Placemark[1]/kml:Style/kml:IconStyle/kml:Icon/kml:color)", doc); XMLAssert.assertXpathEvaluatesTo("http://localhost:8080/geoserver/kml/icon/allsymbolizers?0.0.0=", "//kml:Placemark[1]/kml:Style/kml:IconStyle/kml:Icon/kml:href", doc); XMLAssert.assertXpathEvaluatesTo("b24d4dff", "//kml:Placemark[1]/kml:Style/kml:PolyStyle/kml:color", doc); XMLAssert.assertXpathEvaluatesTo("ffba3e00", "//kml:Placemark[1]/kml:Style/kml:LineStyle/kml:color", doc); XMLAssert.assertXpathEvaluatesTo("2.0", "//kml:Placemark[1]/kml:Style/kml:LineStyle/kml:width", doc); XMLAssert.assertXpathEvaluatesTo("1.4", "//kml:Placemark[1]/kml:Style/kml:LabelStyle/kml:scale", doc); } @Test public void testLabelFromTextSymbolizer() throws Exception { // the style selects a single feature final String requestUrl = "wms/kml?layers=" + getLayerId(MockData.NAMED_PLACES) + "&styles=labels&mode=download"; Document doc = getAsDOM(requestUrl); // print(doc); XMLAssert.assertXpathEvaluatesTo("2", "count(//kml:Placemark)", doc); XMLAssert.assertXpathEvaluatesTo("1", "count(//kml:Placemark[kml:name='Ashton'])", doc); XMLAssert.assertXpathEvaluatesTo("1", "count(//kml:Placemark[kml:name='Goose Island'])", doc); } /** * See http://jira.codehaus.org/browse/GEOS-2670 */ @Test public void testDynamicSymbolizer() throws Exception { final String requestUrl = "wms/kml?layers=" + getLayerId(MockData.STREAMS) + "&styles=dynamicsymbolizer&mode=download"; Document document = getAsDOM(requestUrl); assertEquals("kml", document.getDocumentElement().getNodeName()); XMLAssert.assertXpathEvaluatesTo("http://example.com/Cam Stream", "//kml:Style[1]/kml:IconStyle/kml:Icon/kml:href", document); } @Test public void testRelativeDynamicSymbolizer() throws Exception { final String requestUrl = "wms/kml?layers=" + getLayerId(MockData.STREAMS) + "&styles=relativeds&mode=download"; Document document = getAsDOM(requestUrl); assertEquals("kml", document.getDocumentElement().getNodeName()); XMLAssert.assertXpathEvaluatesTo("http://localhost:8080/geoserver/styles/icons/Cam%20Stream", "//kml:Style[1]/kml:IconStyle/kml:Icon/kml:href", document); } @Test public void testLegend() throws Exception { String layerId = getLayerId(MockData.BASIC_POLYGONS); final String requestUrl = "wms/kml?layers=" + layerId + "&styles=polygon&mode=download&format_options=legend:true" // + "&legend_options=fontStyle:bold;fontColor:ff0000;fontSize:18"; Document doc = getAsDOM(requestUrl); // print(doc); assertEquals("kml", doc.getDocumentElement().getNodeName()); // the icon itself XpathEngine xpath = XMLUnit.newXpathEngine(); String href = xpath.evaluate("//kml:ScreenOverlay/kml:Icon/kml:href", doc); assertTrue(href.contains("request=GetLegendGraphic")); assertTrue(href.contains("layer=cite%3ABasicPolygons")); assertTrue(href.contains("style=polygon")); assertTrue(href.contains("LEGEND_OPTIONS=fontStyle%3Abold%3BfontColor%3Aff0000%3BfontSize%3A18")); // overlay location XMLAssert.assertXpathEvaluatesTo("0.0", "//kml:ScreenOverlay/kml:overlayXY/@x", doc); XMLAssert.assertXpathEvaluatesTo("0.0", "//kml:ScreenOverlay/kml:overlayXY/@y", doc); XMLAssert.assertXpathEvaluatesTo("pixels", "//kml:ScreenOverlay/kml:overlayXY/@xunits", doc); XMLAssert.assertXpathEvaluatesTo("pixels", "//kml:ScreenOverlay/kml:overlayXY/@yunits", doc); XMLAssert.assertXpathEvaluatesTo("10.0", "//kml:ScreenOverlay/kml:screenXY/@x", doc); XMLAssert.assertXpathEvaluatesTo("20.0", "//kml:ScreenOverlay/kml:screenXY/@y", doc); XMLAssert.assertXpathEvaluatesTo("pixels", "//kml:ScreenOverlay/kml:screenXY/@xunits", doc); XMLAssert.assertXpathEvaluatesTo("pixels", "//kml:ScreenOverlay/kml:screenXY/@yunits", doc); } @Test public void testLookatOptions() throws Exception { String layerId = getLayerId(MockData.BASIC_POLYGONS); final String requestUrl = "wms/kml?layers=" + layerId + "&styles=polygon&mode=download" + "&format_options=lookatbbox:-20,-20,20,20;altitude:10;heading:0;tilt:30;range:100;altitudemode:absolute"; Document doc = getAsDOM(requestUrl); // print(doc); // overlay location XMLAssert.assertXpathEvaluatesTo("0.0", "//kml:Document/kml:LookAt/kml:longitude", doc); XMLAssert.assertXpathEvaluatesTo("0.0", "//kml:Document/kml:LookAt/kml:latitude", doc); XMLAssert.assertXpathEvaluatesTo("10.0", "//kml:Document/kml:LookAt/kml:altitude", doc); XMLAssert.assertXpathEvaluatesTo("0.0", "//kml:Document/kml:LookAt/kml:heading", doc); XMLAssert.assertXpathEvaluatesTo("30.0", "//kml:Document/kml:LookAt/kml:tilt", doc); XMLAssert.assertXpathEvaluatesTo("100.0", "//kml:Document/kml:LookAt/kml:range", doc); XMLAssert.assertXpathEvaluatesTo("absolute", "//kml:Document/kml:LookAt/kml:altitudeMode", doc); } @Test public void testExtendedData() throws Exception { String layerId = getLayerId(MockData.AGGREGATEGEOFEATURE); final String requestUrl = "wms/kml?layers=" + layerId + "&mode=download&extendedData=true&kmattr=false&kmscore=100"; Document doc = getAsDOM(requestUrl); // print(doc); // there is one schema XMLAssert.assertXpathEvaluatesTo("1", "count(//kml:Document/kml:Schema)", doc); // check we only have the non geom properties XMLAssert.assertXpathEvaluatesTo("6", "count(//kml:Document/kml:Schema/kml:SimpleField)", doc); XMLAssert.assertXpathEvaluatesTo("0", "count(//kml:Document/kml:Schema/kml:SimpleField[@name='multiPointProperty'])", doc); XMLAssert.assertXpathEvaluatesTo("0", "count(//kml:Document/kml:Schema/kml:SimpleField[@name='multiCurveProperty'])", doc); XMLAssert.assertXpathEvaluatesTo("0", "count(//kml:Document/kml:Schema/kml:SimpleField[@name='multiSurfaceProperty'])", doc); // check the type mapping XMLAssert.assertXpathEvaluatesTo("string", "//kml:Document/kml:Schema/kml:SimpleField[@name='description']/@type", doc); XMLAssert.assertXpathEvaluatesTo("double", "//kml:Document/kml:Schema/kml:SimpleField[@name='doubleProperty']/@type", doc); XMLAssert.assertXpathEvaluatesTo("int", "//kml:Document/kml:Schema/kml:SimpleField[@name='intRangeProperty']/@type", doc); XMLAssert.assertXpathEvaluatesTo("string", "//kml:Document/kml:Schema/kml:SimpleField[@name='strProperty']/@type", doc); XMLAssert.assertXpathEvaluatesTo("string", "//kml:Document/kml:Schema/kml:SimpleField[@name='featureCode']/@type", doc); // check the extended data of one feature String sd = "//kml:Placemark[@id='AggregateGeoFeature.f005']/kml:ExtendedData/kml:SchemaData/kml:SimpleData"; XMLAssert.assertXpathEvaluatesTo("description-f005", sd + "[@name='description']", doc); XMLAssert.assertXpathEvaluatesTo("name-f005", sd + "[@name='name']", doc); XMLAssert.assertXpathEvaluatesTo("2012.78", sd + "[@name='doubleProperty']", doc); XMLAssert.assertXpathEvaluatesTo( "Ma quande lingues coalesce, li grammatica del resultant " + "lingue es plu simplic e regulari quam ti del coalescent lingues. Li nov lingua " + "franca va esser plu simplic e regulari quam li existent Europan lingues.", sd + "[@name='strProperty']", doc); XMLAssert.assertXpathEvaluatesTo("BK030", sd + "[@name='featureCode']", doc); } @Test public void testHeightTemplate() throws Exception { File template = null; try { String layerId = getLayerId(MockData.LAKES); FeatureTypeInfo resource = getCatalog().getResourceByName(layerId, FeatureTypeInfo.class); File parent = getDataDirectory().findOrCreateResourceDir(resource); template = new File(parent, "height.ftl"); FileUtils.write(template, "${FID.value}"); final String requestUrl = "wms/kml?layers=" + layerId + "&mode=download"; Document doc = getAsDOM(requestUrl); // print(doc); String base = "//kml:Placemark[@id='Lakes.1107531835962']/kml:MultiGeometry"; XMLAssert.assertXpathEvaluatesTo("1", "count(" + base + ")", doc); XMLAssert.assertXpathEvaluatesTo("1", base + "/kml:Point/kml:extrude", doc); XMLAssert.assertXpathEvaluatesTo("relativeToGround", base + "/kml:Point/kml:altitudeMode", doc); XMLAssert.assertXpathEvaluatesTo("0.0017851936218678816,-0.0010838268792710709,101.0", base + "/kml:Point/kml:coordinates", doc); XMLAssert.assertXpathEvaluatesTo("1", base + "/kml:Polygon/kml:extrude", doc); XMLAssert.assertXpathEvaluatesTo("relativeToGround", base + "/kml:Polygon/kml:altitudeMode", doc); XMLAssert.assertXpathEvaluatesTo( "6.0E-4,-0.0018,101.0 0.0010,-6.0E-4,101.0 0.0024,-1.0E-4,101.0 0.0031,-0.0015,101.0 6.0E-4,-0.0018,101.0", base + "/kml:Polygon/kml:outerBoundaryIs/kml:LinearRing/kml:coordinates", doc); } finally { if (template != null) { template.delete(); } } } @Test public void testHeightTemplateNoExtrude() throws Exception { File template = null; try { String layerId = getLayerId(MockData.LAKES); FeatureTypeInfo resource = getCatalog().getResourceByName(layerId, FeatureTypeInfo.class); File parent = getDataDirectory().findOrCreateResourceDir(resource); template = new File(parent, "height.ftl"); FileUtils.write(template, "${FID.value}"); final String requestUrl = "wms/kml?layers=" + layerId + "&mode=download&extrude=false"; Document doc = getAsDOM(requestUrl); // print(doc); String base = "//kml:Placemark[@id='Lakes.1107531835962']/kml:MultiGeometry"; XMLAssert.assertXpathEvaluatesTo("1", "count(" + base + ")", doc); XMLAssert.assertXpathEvaluatesTo("0", base + "/kml:Point/kml:extrude", doc); XMLAssert.assertXpathEvaluatesTo("relativeToGround", base + "/kml:Point/kml:altitudeMode", doc); XMLAssert.assertXpathEvaluatesTo("0.0017851936218678816,-0.0010838268792710709,101.0", base + "/kml:Point/kml:coordinates", doc); XMLAssert.assertXpathEvaluatesTo("0", base + "/kml:Polygon/kml:extrude", doc); XMLAssert.assertXpathEvaluatesTo("relativeToGround", base + "/kml:Polygon/kml:altitudeMode", doc); XMLAssert.assertXpathEvaluatesTo( "6.0E-4,-0.0018,101.0 0.0010,-6.0E-4,101.0 0.0024,-1.0E-4,101.0 0.0031,-0.0015,101.0 6.0E-4,-0.0018,101.0", base + "/kml:Polygon/kml:outerBoundaryIs/kml:LinearRing/kml:coordinates", doc); } finally { if (template != null) { template.delete(); } } } /** * Verify that when GE asks for coordinates larger than 180 we still manage gracefully */ @Test public void testCoordinateShift() throws Exception { Document document = getAsDOM( "wms/kml?layers=" + getLayerId(MockData.BASIC_POLYGONS) + "&mode=download&bbox=150,-90,380,90"); // print(document); assertEquals(3, document.getElementsByTagName("Placemark").getLength()); } @Test public void testExternalImageSize() throws Exception { GetMapRequest req = createGetMapRequest(MockData.STREAMS); req.setWidth(256); req.setHeight(256); WMSMapContent mapContent = new WMSMapContent(req); mapContent.addLayer(createMapLayer(MockData.STREAMS, "big-local-image")); mapContent.getViewport().setBounds(new ReferencedEnvelope(-180, 0, -90, 90, DefaultGeographicCRS.WGS84)); mapContent.setMapHeight(256); mapContent.setMapWidth(256); KMLMapOutputFormat of = new KMLMapOutputFormat(getWMS()); KMLMap map = of.produceMap(mapContent); ByteArrayOutputStream bout = new ByteArrayOutputStream(); new KMLEncoder().encode(map.getKml(), bout); Document document = dom(new ByteArrayInputStream(bout.toByteArray())); assertEquals("kml", document.getDocumentElement().getNodeName()); assertEquals(1, document.getElementsByTagName("Style").getLength()); XMLAssert.assertXpathExists("//kml:IconStyle/kml:scale", document); XPath xPath = XPathFactory.newInstance().newXPath(); initXPath(xPath); Double scale = (Double) xPath.evaluate("//kml:IconStyle/kml:scale", document.getDocumentElement(), XPathConstants.NUMBER); assertEquals(42d / 16d, scale, 0.01); } @Test public void testKmzEmbededPointImageSize() throws Exception { WMSMapContent mapContent = createMapContext(MockData.POINTS, "big-mark"); File temp = File.createTempFile("test", "kmz", new File("target")); temp.delete(); temp.mkdir(); temp.deleteOnExit(); File zip = new File(temp, "kmz.zip"); zip.deleteOnExit(); // create hte map producer KMZMapOutputFormat mapProducer = new KMZMapOutputFormat(getWMS()); KMLMap map = mapProducer.produceMap(mapContent); FileOutputStream output = new FileOutputStream(zip); new KMLMapResponse(new KMLEncoder(), getWMS()).write(map, output, null); output.flush(); output.close(); assertTrue(zip.exists()); // unzip and test it ZipFile zipFile = new ZipFile(zip); ZipEntry kmlEntry = zipFile.getEntry("wms.kml"); InputStream kmlStream = zipFile.getInputStream(kmlEntry); Document kmlResult = XMLUnit.buildTestDocument(new InputSource(kmlStream)); Double scale = Double.parseDouble(XMLUnit.newXpathEngine() .getMatchingNodes("(//kml:Style)[1]/kml:IconStyle/kml:scale", kmlResult).item(0).getTextContent()); assertEquals(49d / 16d, scale, 0.01); zipFile.close(); } WMSMapContent createMapContext(QName layer, String style) throws Exception { // create a map context WMSMapContent mapContent = new WMSMapContent(); mapContent.addLayer(createMapLayer(layer, style)); mapContent.setMapHeight(256); mapContent.setMapWidth(256); GetMapRequest getMapRequest = createGetMapRequest(new QName[] { layer }); getMapRequest.setWidth(256); getMapRequest.setHeight(256); mapContent.setRequest(getMapRequest); mapContent.getViewport().setBounds(new ReferencedEnvelope(-180, 180, -90, 90, DefaultGeographicCRS.WGS84)); return mapContent; } void initXPath(XPath xpath) { SimpleNamespaceContext ctx = new SimpleNamespaceContext(); ctx.bindNamespaceUri("kml", "http://www.opengis.net/kml/2.2"); xpath.setNamespaceContext(ctx); } /** * Creates a key/value pair map from the cgi parameters in the provided url * * @param url an url where all the cgi parameter values are url encoded * @return a map with the key value pairs from the url with all the parameter names in upper * case */ static Map<String, String> toKvp(String url) { if (url.indexOf('?') > 0) { url = url.substring(url.indexOf('?') + 1); } Map<String, String> kvpMap = new HashMap<String, String>(); String[] tuples = url.split("&"); for (String tuple : tuples) { String[] kvp = tuple.split("="); String key = kvp[0].toUpperCase(); String value = kvp.length > 1 ? kvp[1] : null; if (value != null) { try { value = URLDecoder.decode(value, "UTF-8"); } catch (UnsupportedEncodingException e) { throw new RuntimeException(e); } } kvpMap.put(key, value); } return kvpMap; } static void assertMapsEqual(Map<String, Object> expected, Map<String, Object> actual) throws Exception { for (Map.Entry<String, Object> entry : expected.entrySet()) { if (entry.getKey().equalsIgnoreCase("format_options")) { FormatOptionsKvpParser parser = new FormatOptionsKvpParser(); Map expectedFormatOptions = (Map) parser.parse((String) entry.getValue()); Map actualFormatOptions = (Map) parser.parse((String) actual.get(entry.getKey())); for (Object o : expectedFormatOptions.entrySet()) { Map.Entry formatOption = (Map.Entry) o; assertEquals(formatOption.getValue(), actualFormatOptions.get(formatOption.getKey())); } for (Object key : actualFormatOptions.keySet()) { assertTrue("found unexpected key " + key + " in format options", expectedFormatOptions.containsKey(key)); } // special treatment for the format options } else { assertEquals(entry.getValue(), actual.get(entry.getKey())); } } for (String key : actual.keySet()) { assertTrue(expected.containsKey(key)); } } }