/* * GeoTools - The Open Source Java GIS Toolkit * http://geotools.org * * (C) 2002-2008, Open Source Geospatial Foundation (OSGeo) * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. */ package org.geotools.data.wfs.v1_1_0; import static org.geotools.data.wfs.v1_1_0.DataTestSupport.CUBEWERX_GOVUNITCE; import static org.geotools.data.wfs.v1_1_0.DataTestSupport.CUBEWERX_ROADSEG; import static org.geotools.data.wfs.v1_1_0.DataTestSupport.GEOS_ARCHSITES; import static org.geotools.data.wfs.v1_1_0.DataTestSupport.GEOS_ROADS; import static org.geotools.data.wfs.v1_1_0.DataTestSupport.GEOS_STATES; import static org.geotools.data.wfs.v1_1_0.DataTestSupport.GEOS_TASMANIA_CITIES; import static org.geotools.data.wfs.v1_1_0.DataTestSupport.IONIC_STATISTICAL_UNIT; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import java.io.IOException; import java.net.URL; import javax.xml.namespace.QName; import org.geotools.data.DataUtilities; import org.geotools.data.wfs.protocol.wfs.GetFeatureParser; import org.geotools.data.wfs.v1_1_0.parsers.EmfAppSchemaParser; import org.geotools.referencing.CRS; import org.geotools.test.TestData; import org.geotools.wfs.v1_1.WFSConfiguration; import org.geotools.xml.Configuration; import org.junit.Test; import org.opengis.feature.Feature; import org.opengis.feature.FeatureVisitor; import org.opengis.feature.GeometryAttribute; import org.opengis.feature.Property; import org.opengis.feature.simple.SimpleFeature; import org.opengis.feature.simple.SimpleFeatureType; import org.opengis.feature.type.AttributeDescriptor; import org.opengis.referencing.crs.CoordinateReferenceSystem; import com.vividsolutions.jts.geom.MultiPolygon; import com.vividsolutions.jts.geom.Polygon; /** * This abstract class comprises a sort of compliance tests for {@link GetFeatureParser} * implementations. * <p> * Subclasses shall just provide an implementation for * {@link #getParser(QName, SimpleFeatureType, String)} * </p> * <p> * Note this test depends on {@link EmfAppSchemaParser} to function correctly in order to obtain the * test FeatureTypes from the DescribeFeatureType response samples under {@code test-data/}. * </p> * * @author Gabriel Roldan * @version $Id: AbstractGetFeatureParserTest.java 35310 2010-04-30 10:32:15Z jive $ * @since 2.5.x * * * @source $URL: http://svn.osgeo.org/geotools/trunk/modules/unsupported/wfs-ng/src/test/java/org/geotools/data/wfs/v1_1_0/AbstractGetFeatureParserTest.java $ * java/org/geotools/wfs/v_1_1_0/data/StreamingParserFeatureReaderTest .java $ * @see XmlSimpleFeatureParserTest * @see StreamingParserFeatureReaderTest */ @SuppressWarnings("nls") public abstract class AbstractGetFeatureParserTest { /** * Configuration object used to parse the sample schemas * * @see #getTypeView(QName, String, String, String[]) */ private static final Configuration wfsConfiguration = new WFSConfiguration(); /** * A feature visitor used to assert the parsed features * * @author Gabriel Roldan (TOPP) * @version $Id: AbstractGetFeatureParserTest.java 35310 2010-04-30 10:32:15Z jive $ * @since 2.5.x * @source $URL: http://svn.geotools.org/geotools/trunk/gt/modules/plugin/wfs /src/test * /java/org/geotools/wfs/v_1_1_0/data/AbstractGetFeatureParserTest .java $ */ private static class FeatureAssertor implements FeatureVisitor { private SimpleFeatureType featureType; /** * A featuretype which might be a subset of the actual FeatureType whose attributes will be * used to assert the features. * * @param featureType */ public FeatureAssertor(SimpleFeatureType featureType) { this.featureType = featureType; } public void visit(final Feature feature) { assertNotNull(feature); assertNotNull(feature.getIdentifier().getID()); for (AttributeDescriptor descriptor : featureType.getAttributeDescriptors()) { final String name = descriptor.getLocalName(); Property property = feature.getProperty(name); assertNotNull(name + " property was not parsed", property); assertNotNull("got null value for property " + name, property.getValue()); } } } /** * Parses the featuretype from the test file referenced by {@code schemaName} and returns a new, * subset FeatureType comprised of only the required {@code properties} * * @param featureName * the name of the Features produced for the target FeatureType (i.e. {@code * topp:states} instead of {@code topp:states_Type}) * @param schemaName * the location of the schema file under {@code test-data/} * @param epsgCrsId * the EPSG identifier for the feature type CRS (eg. {@code "EPSG:4326"}) * @param properties * the property names to include from the original schema in the one to be returned * @return a subset of the original featuretype containing only the required {@code properties} */ private SimpleFeatureType getTypeView(final QName featureName, final String schemaName, final String epsgCrsId, final String[] properties) throws Exception { URL schemaLocation = TestData.getResource(this, schemaName); CoordinateReferenceSystem crs = CRS.decode(epsgCrsId); SimpleFeatureType originalType = EmfAppSchemaParser.parseSimpleFeatureType( wfsConfiguration, featureName, schemaLocation, crs); SimpleFeatureType subsetType = DataUtilities.createSubType(originalType, properties); return subsetType; } /** * Uses a {@link StreamingParserFeatureReader} to parse the features while traversing the * feature collection in a test {@code wfs:FeatureCollection} document; {@code assertor} is a * visitor provided by the actual unit test calling this method, every feature fetched is passed * to the visitor who contains the specific assertions. * * @param featureName * the name of the features (not the feature type) expected * @param getFeatureResultTestFile * the name of the test file name to load in order to simulate the response of a * GetFeature request * @param assertor * a FeatureVisitor to assert the contents or structure of the features * @param expectedFeatureCount * the number of features there should be on the feature collection, an assertion is * made at the end of the method. * @param schemaName * @throws Exception */ private void testParseGetFeatures(final QName featureName, final SimpleFeatureType queryFeatureType, final GetFeatureParser parser, final FeatureVisitor assertor, final int expectedFeatureCount) throws Exception { int featureCount = 0; SimpleFeature feature; try { for (int i = 0; i < expectedFeatureCount; i++) { feature = parser.parse(); featureCount++; assertor.visit(feature); } feature = parser.parse(); assertNull(feature); } finally { parser.close(); } assertEquals(expectedFeatureCount, featureCount); } /** * Subclasses need to implement in order to provide a specific {@link GetFeatureParser} * implementation settled up for the given featureName and dataFile containing the test * GetFeature request response. * * @param featureName * @param schemaLocation * @param featureType * @param getFeaturesRequest * the URL representing the GetFeature request. Opening its input stream shall * suffice to get the GetFeature response. * @return * @throws IOException */ protected abstract GetFeatureParser getParser(QName featureName, String schemaLocation, SimpleFeatureType featureType, URL getFeaturesRequest) throws IOException; /** * Verifies correctness on parsing a normal geoserver WFS 1.1.0 GetFeature response. * * Test method for {@link StreamingParserFeatureReader#parse()}. * * @throws Exception */ @Test public void testParseGeoServer_ArchSites_Point() throws Exception { final QName featureName = GEOS_ARCHSITES.TYPENAME; final int expectedCount = 3; final String schemaLocation = GEOS_ARCHSITES.SCHEMA; final String[] properties = { "cat", "str1", "the_geom" }; final SimpleFeatureType featureType; featureType = getTypeView(featureName, schemaLocation, GEOS_ARCHSITES.CRS, properties); final FeatureVisitor assertor = new FeatureAssertor(featureType); URL url = TestData.getResource(this, GEOS_ARCHSITES.DATA); GetFeatureParser parser = getParser(featureName, schemaLocation, featureType, url); int nof = parser.getNumberOfFeatures(); assertEquals(expectedCount, nof); testParseGetFeatures(featureName, featureType, parser, assertor, expectedCount); } /** * Verifies correctness on parsing a normal geoserver WFS 1.1.0 GetFeature response for the * usual topp:states feature type (multipolygon). * * Test method for {@link StreamingParserFeatureReader#parse()}. * * @throws Exception */ @Test public void testParseGeoServer_States_polygon_with_hole() throws Exception { final QName featureName = GEOS_STATES.TYPENAME; final int expectedCount = 2; final String schemaLocation = GEOS_STATES.SCHEMA; final String[] properties = { "the_geom", "STATE_NAME", "STATE_FIPS", "SUB_REGION", "SAMP_POP" }; final SimpleFeatureType featureType; featureType = getTypeView(featureName, schemaLocation, GEOS_STATES.CRS, properties); final FeatureVisitor assertor = new FeatureAssertor(featureType) { @Override public void visit(final Feature feature) { super.visit(feature); final String fid = feature.getIdentifier().getID(); final int numPolygons; final int expectedHoles; if ("states.1".equals(fid)) { numPolygons = 2; expectedHoles = 1; } else if ("states.2".equals(fid)) { numPolygons = 1; expectedHoles = 2; } else { throw new IllegalArgumentException("Expected states.1 or states.2, got " + fid); } GeometryAttribute defaultGeometryProperty = feature.getDefaultGeometryProperty(); assertNotNull(defaultGeometryProperty); final Object value = defaultGeometryProperty.getValue(); assertNotNull(value); assertTrue("value: " + value, value instanceof MultiPolygon); MultiPolygon mp = (MultiPolygon) value; assertEquals(numPolygons, mp.getNumGeometries()); for (int i = 0; i < numPolygons; i++) { Polygon p = (Polygon) mp.getGeometryN(i); assertEquals(expectedHoles, p.getNumInteriorRing()); } } }; URL url = TestData.getResource(this, GEOS_STATES.DATA); GetFeatureParser parser = getParser(featureName, schemaLocation, featureType, url); int nof = parser.getNumberOfFeatures(); assertEquals(expectedCount, nof); testParseGetFeatures(featureName, featureType, parser, assertor, expectedCount); } @Test public void testParseGeoServer_roads_MultiLineString() throws Exception { final QName featureName = GEOS_ROADS.TYPENAME; final int expectedCount = 1; final String schemaLocation = GEOS_ROADS.SCHEMA; final String[] properties = { "the_geom", "label" }; final SimpleFeatureType featureType; featureType = getTypeView(featureName, schemaLocation, GEOS_ROADS.CRS, properties); final FeatureVisitor assertor = new FeatureAssertor(featureType); URL url = TestData.getResource(this, GEOS_ROADS.DATA); GetFeatureParser parser = getParser(featureName, schemaLocation, featureType, url); int nof = parser.getNumberOfFeatures(); assertEquals(expectedCount, nof); testParseGetFeatures(featureName, featureType, parser, assertor, expectedCount); } @Test public void testParseGeoServer_tasmania_cities_MultiPoint() throws Exception { final QName featureName = GEOS_TASMANIA_CITIES.TYPENAME; final int expectedCount = 1; final String schemaLocation = GEOS_TASMANIA_CITIES.SCHEMA; final String[] properties = { "the_geom", "CNTRY_NAME", "POP_CLASS" }; final SimpleFeatureType featureType; featureType = getTypeView(featureName, schemaLocation, GEOS_TASMANIA_CITIES.CRS, properties); final FeatureVisitor assertor = new FeatureAssertor(featureType); URL url = TestData.getResource(this, GEOS_TASMANIA_CITIES.DATA); GetFeatureParser parser = getParser(featureName, schemaLocation, featureType, url); int nof = parser.getNumberOfFeatures(); assertEquals(expectedCount, nof); testParseGetFeatures(featureName, featureType, parser, assertor, expectedCount); } /** * Verifies correctness on parsing a sample CubeWerx WFS 1.1.0 GetFeature response. * * @throws Exception */ @Test public void testParseCubeWerx_GovernmentalUnitCE() throws Exception { final QName featureName = CUBEWERX_GOVUNITCE.TYPENAME; final String schemaLocation = CUBEWERX_GOVUNITCE.SCHEMA; final int expectedCount = 3; final String[] properties = { "geometry", "instanceName", "instanceCode" }; final SimpleFeatureType featureType = getTypeView(featureName, schemaLocation, CUBEWERX_GOVUNITCE.CRS, properties); final FeatureVisitor assertor = new FeatureAssertor(featureType); URL url = TestData.getResource(this, CUBEWERX_GOVUNITCE.DATA); GetFeatureParser parser = getParser(featureName, schemaLocation, featureType, url); int nof = parser.getNumberOfFeatures(); assertEquals(-1, nof); testParseGetFeatures(featureName, featureType, parser, assertor, expectedCount); } @Test public void testParseCubeWerx_RoadSeg() throws Exception { final String[] properties = { "lastUpdateDate", "geometry", "status", "isAnchorSection" }; final QName featureName = CUBEWERX_ROADSEG.TYPENAME; final String schemaLocation = CUBEWERX_ROADSEG.SCHEMA; final SimpleFeatureType featureType = getTypeView(featureName, schemaLocation, CUBEWERX_ROADSEG.CRS, properties); URL url = TestData.getResource(this, CUBEWERX_ROADSEG.DATA); final GetFeatureParser parser = getParser(featureName, schemaLocation, featureType, url); int nof = parser.getNumberOfFeatures(); assertEquals(-1, nof); FeatureVisitor assertor = new FeatureAssertor(featureType); testParseGetFeatures(featureName, featureType, parser, assertor, 3); } @Test public void testParseIonic_StatisticalUnit() throws Exception { final String[] properties = { "unitId", "typeAbbreviation", "instanceName", "geometry" }; final QName featureName = IONIC_STATISTICAL_UNIT.TYPENAME; final String schemaLocation = IONIC_STATISTICAL_UNIT.SCHEMA; final SimpleFeatureType featureType = getTypeView(featureName, schemaLocation, CUBEWERX_ROADSEG.CRS, properties); URL url = TestData.getResource(this, IONIC_STATISTICAL_UNIT.DATA); final GetFeatureParser parser = getParser(featureName, schemaLocation, featureType, url); int nof = parser.getNumberOfFeatures(); assertEquals(-1, nof); FeatureVisitor assertor = new FeatureAssertor(featureType); testParseGetFeatures(featureName, featureType, parser, assertor, 2); } protected void runGetFeaturesParsing() throws Exception { GetFeatureParser reader; { final String[] properties = { "geometry", "instanceName", "instanceCode", "governmentalUnitType" }; final URL getFeatures = new URL( "http://frameworkwfs.usgs.gov/framework/wfs/wfs.cgi?DATASTORE=Framework&DATASTORE=Framework&" + "SERVICE=WFS&VERSION=1.1.0&REQUEST=GetFeature&TYPENAME=gubs:GovernmentalUnitCE&" + "PROPERTYNAME=geometry,instanceName,instanceCode,governmentalUnitType&maxFeatures=100"); // create a subtype with only the required properties final SimpleFeatureType featureType = getTypeView(CUBEWERX_GOVUNITCE.TYPENAME, CUBEWERX_GOVUNITCE.SCHEMA, CUBEWERX_GOVUNITCE.CRS, properties); System.out.println("Getting parser for " + getFeatures.toExternalForm()); reader = getParser(CUBEWERX_GOVUNITCE.TYPENAME, CUBEWERX_GOVUNITCE.SCHEMA, featureType, getFeatures); System.out.println("Got " + reader.getClass().getSimpleName()); } int count = 0; SimpleFeature feature; Object defaultGeometry; System.out.println("Parsing features..."); Runtime runtime = Runtime.getRuntime(); long initialMem = runtime.totalMemory() - runtime.freeMemory(); long startTime = System.currentTimeMillis(); while ((feature = reader.parse()) != null) { defaultGeometry = feature.getDefaultGeometry(); count++; System.out.print('.'); if (count % 100 == 0) { System.out.print('\n'); } } long endTime = System.currentTimeMillis(); long totalTime = endTime - startTime; long endMem = runtime.totalMemory() - runtime.freeMemory(); long memUsed = (endMem - initialMem) / (1024 * 1024); System.out.println("\nFetched " + count + " features " + " in " + totalTime + "ms. (avg. " + (totalTime / count) + "ms/feature) Mem. used: " + memUsed + "MB."); } }