package org.geoserver.kml; import static org.junit.Assert.assertEquals; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.FilterOutputStream; import java.io.IOException; import java.util.Map; import java.util.NoSuchElementException; import java.util.concurrent.atomic.AtomicInteger; import net.opengis.wfs.FeatureCollectionType; import net.opengis.wfs.WfsFactory; import org.custommonkey.xmlunit.XMLAssert; import org.geoserver.catalog.FeatureTypeInfo; import org.geoserver.data.test.MockData; import org.geoserver.platform.GeoServerExtensions; import org.geoserver.platform.ServiceException; import org.geoserver.wfs.WFSTestSupport; import org.geoserver.wfs.request.FeatureCollectionResponse; import org.geotools.data.DataUtilities; import org.geotools.data.FeatureSource; import org.geotools.data.simple.SimpleFeatureCollection; import org.geotools.data.simple.SimpleFeatureIterator; import org.geotools.feature.FeatureCollection; import org.geotools.feature.collection.DecoratingSimpleFeatureIterator; import org.junit.Test; import org.opengis.feature.simple.SimpleFeature; import org.w3c.dom.Document; import org.springframework.mock.web.MockHttpServletResponse; public class KMLWFSTest extends WFSTestSupport { @Override protected void setUpNamespaces(Map<String, String> namespaces) { namespaces.put("kml", "http://www.opengis.net/kml/2.2"); } @Test public void testGetCapabilities() throws Exception { Document doc = getAsDOM("wfs?request=GetCapabilities&version=1.1.0"); // print(doc); // check the new output format is part of the caps document XMLAssert.assertXpathEvaluatesTo("1", "count(//ows:Operation[@name='GetFeature']/" + "ows:Parameter[@name='outputFormat']/ows:Value[text() = '" + KMLMapOutputFormat.MIME_TYPE + "'])", doc); } @Test public void testGetFeature() throws Exception { MockHttpServletResponse response = getAsServletResponse("wfs?service=WFS&version=1.1.0&request=GetFeature&typeName=" + getLayerId(MockData.AGGREGATEGEOFEATURE) + "&outputFormat=" + KMLMapOutputFormat.MIME_TYPE.replace("+", "%2B")); assertEquals(200, response.getStatus()); assertEquals("inline; filename=" + MockData.AGGREGATEGEOFEATURE.getLocalPart() + ".kml", response.getHeader("Content-Disposition")); Document doc = dom(new ByteArrayInputStream( response.getContentAsString().getBytes())); checkAggregateGeoFeatureKmlContents(doc); } @Test public void testGetFeatureKMLAlias() throws Exception { Document doc = getAsDOM("wfs?service=WFS&version=1.1.0&request=GetFeature&typeName=" + getLayerId(MockData.AGGREGATEGEOFEATURE) + "&outputFormat=KML"); checkAggregateGeoFeatureKmlContents(doc); } private void checkAggregateGeoFeatureKmlContents(Document doc) throws Exception { // 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 testForceWGS84() throws Exception { Document doc = getAsDOM("wfs?service=WFS&version=1.1.0&request=GetFeature&typeName=" + getLayerId(MockData.MPOINTS) + "&outputFormat=KML"); // print(doc); XMLAssert.assertXpathEvaluatesTo("1", "count(//kml:Folder)", doc); XMLAssert.assertXpathEvaluatesTo("-92.99707024070754,4.523788746085423", "//kml:Placemark/kml:MultiGeometry/kml:Point[1]/kml:coordinates", doc); XMLAssert.assertXpathEvaluatesTo("-92.99661950641159,4.524241081543828", "//kml:Placemark/kml:MultiGeometry/kml:Point[2]/kml:coordinates", doc); } @Test public void testCloseIterators() throws ServiceException, IOException { // build a wfs response with an iterator that will mark if close has been called, or not FeatureTypeInfo fti = getCatalog().getFeatureTypeByName(getLayerId(MockData.POLYGONS)); FeatureSource fs = fti.getFeatureSource(null, null); SimpleFeatureCollection fc = (SimpleFeatureCollection) fs.getFeatures(); final AtomicInteger openIterators = new AtomicInteger(0); FeatureCollection decorated = new org.geotools.feature.collection.DecoratingSimpleFeatureCollection( fc) { @Override public SimpleFeatureIterator features() { openIterators.incrementAndGet(); final SimpleFeature f = DataUtilities.first(delegate); return new SimpleFeatureIterator() { @Override public SimpleFeature next() throws NoSuchElementException { return f; } @Override public boolean hasNext() { return true; } @Override public void close() { openIterators.decrementAndGet(); } }; } }; FeatureCollectionType response = WfsFactory.eINSTANCE.createFeatureCollectionType(); response.getFeature().add(decorated); FeatureCollectionResponse fcResponse = FeatureCollectionResponse.adapt(response); WFSKMLOutputFormat outputFormat = GeoServerExtensions.bean(WFSKMLOutputFormat.class); FilterOutputStream fos = new FilterOutputStream(new ByteArrayOutputStream()) { int count = 0; @Override public void write(byte[] b) throws IOException { write(b, 0, b.length); } @Override public void write(byte[] b, int off, int len) throws IOException { for (int i = off; i < len; i++) { write(b[i]); } } @Override public void write(int b) throws IOException { count++; if(count > 100) { throw new IOException("Simularing client shutting down connection"); } super.write(b); } }; try { outputFormat.write(fcResponse, fos, null); } catch(Exception e) { // fine, it's expected } assertEquals(0, openIterators.get()); } }