/* (c) 2014 - 2015 Open Source Geospatial Foundation - all rights reserved * (c) 2001 - 2013 OpenPlans * This code is licensed under the GPL 2.0 license, available at the root * application directory. */ package org.geoserver.wfs.xml.v1_1_0; import java.util.Iterator; import javax.xml.namespace.QName; import net.opengis.wfs.FeatureCollectionType; import net.opengis.wfs.WfsFactory; import org.geoserver.catalog.Catalog; import org.geoserver.catalog.FeatureTypeInfo; import org.geoserver.feature.CompositeFeatureCollection; import org.geotools.data.simple.SimpleFeatureCollection; import org.geotools.feature.FeatureCollection; import org.geotools.geometry.jts.ReferencedEnvelope; import org.geotools.gml3.GML; import org.geotools.gml3.GMLConfiguration; import org.geotools.gml3.simple.GML3FeatureCollectionEncoderDelegate; import org.geotools.xml.AbstractComplexEMFBinding; import org.geotools.xml.Configuration; import org.geotools.xml.ElementInstance; import org.geotools.xml.Encoder; import org.geotools.xml.Node; import org.opengis.referencing.crs.CoordinateReferenceSystem; /** * Binding object for the type http://www.opengis.net/wfs:FeatureCollectionType. * * <p> * <pre> * <code> * <xsd:complexType name="FeatureCollectionType"> * <xsd:annotation> * <xsd:documentation> * This type defines a container for the response to a * GetFeature or GetFeatureWithLock request. If the * request is GetFeatureWithLock, the lockId attribute * must be populated. The lockId attribute can otherwise * be safely ignored. * </xsd:documentation> * </xsd:annotation> * <xsd:complexContent> * <xsd:extension base="gml:AbstractFeatureCollectionType"> * <xsd:attribute name="lockId" type="xsd:string" use="optional"> * <xsd:annotation> * <xsd:documentation> * The value of the lockId attribute is an identifier * that a Web Feature Service generates when responding * to a GetFeatureWithLock request. A client application * can use this value in subsequent operations (such as a * Transaction request) to reference the set of locked * features. * </xsd:documentation> * </xsd:annotation> * </xsd:attribute> * <xsd:attribute name="timeStamp" type="xsd:dateTime" use="optional"> * <xsd:annotation> * <xsd:documentation> * The timeStamp attribute should contain the date and time * that the response was generated. * </xsd:documentation> * </xsd:annotation> * </xsd:attribute> * <xsd:attribute name="numberOfFeatures" * type="xsd:nonNegativeInteger" use="optional"> * <xsd:annotation> * <xsd:documentation> * The numberOfFeatures attribute should contain a * count of the number of features in the response. * That is a count of all features elements dervied * from gml:AbstractFeatureType. * </xsd:documentation> * </xsd:annotation> * </xsd:attribute> * </xsd:extension> * </xsd:complexContent> * </xsd:complexType> * * </code> * </pre> * @generated */ public class FeatureCollectionTypeBinding extends AbstractComplexEMFBinding { WfsFactory wfsfactory; Catalog catalog; boolean generateBounds; /** * Boolean property which controls whether the FeatureCollection should be encoded with multiple featureMember * as opposed to a single featureMembers */ boolean encodeFeatureMember; private Encoder encoder; public FeatureCollectionTypeBinding(WfsFactory wfsfactory, Catalog catalog, Configuration configuration) { this(wfsfactory, catalog, configuration, null); } public FeatureCollectionTypeBinding(WfsFactory wfsfactory, Catalog catalog, Configuration configuration, Encoder encoder) { this.wfsfactory = wfsfactory; this.catalog = catalog; this.encoder = encoder; this.generateBounds = !configuration.getProperties().contains(GMLConfiguration.NO_FEATURE_BOUNDS); this.encodeFeatureMember = configuration.getProperties().contains(GMLConfiguration.ENCODE_FEATURE_MEMBER); } public int getExecutionMode() { return OVERRIDE; } /** * @generated */ public QName getTarget() { return WFS.FEATURECOLLECTIONTYPE; } /** * <!-- begin-user-doc --> * <!-- end-user-doc --> * * @generated modifiable */ public Class getType() { return FeatureCollectionType.class; } /** * <!-- begin-user-doc --> * <!-- end-user-doc --> * * @generated modifiable */ public Object parse(ElementInstance instance, Node node, Object value) throws Exception { return value; } public Object getProperty(Object object, QName name) throws Exception { //check for feature collection members if (GML.featureMembers.equals(name)) { // check the WFS configuration, if encode featureMember is selected on WFS configuration // page, return null; if (encodeFeatureMember) { return null; } FeatureCollectionType featureCollection = (FeatureCollectionType) object; if (!featureCollection.getFeature().isEmpty()) { return handleFeatureCollection(featureCollection); } } else if (GML.featureMember.equals(name)) { // check the WFS configuration, if encode featureMembers is selected on WFS // configuration page, return null; if (!encodeFeatureMember) { return null; } FeatureCollectionType featureCollection = (FeatureCollectionType) object; if (!featureCollection.getFeature().isEmpty()) { return handleFeatureCollection(featureCollection); } } else if (GML.boundedBy.equals(name) && generateBounds) { FeatureCollectionType featureCollection = (FeatureCollectionType) object; ReferencedEnvelope env = null; for (Iterator it = featureCollection.getFeature().iterator(); it.hasNext();) { FeatureCollection fc = (FeatureCollection) it.next(); if(env == null) { env = fc.getBounds(); } else { env.expandToInclude(fc.getBounds()); } // workaround bogus collection implementation that won't return the crs if(env != null && env.getCoordinateReferenceSystem() == null) { CoordinateReferenceSystem crs = fc.getSchema().getCoordinateReferenceSystem(); if ( crs == null ) { //fall back on catalog FeatureTypeInfo info = catalog.getFeatureTypeByName(fc.getSchema().getName()); if ( info != null ) { crs = info.getCRS(); } } env = new ReferencedEnvelope(env, crs); } if ( env != null ) { //JD: here we don't return the envelope if it is null or empty, this is to work // around an issue with validation in the cite engine. I have opened a jira task // to track this, and hopefully eventually fix the cite engine // https://osgeo-org.atlassian.net/browse/GEOS-2700 return !( env.isNull() || env.isEmpty() ) ? env : null; } } } //delegate to parent lookup return super.getProperty(object, name); } private Object handleFeatureCollection(FeatureCollectionType featureCollection) { FeatureCollection result = null; if (featureCollection.getFeature().size() > 1) { // wrap in a single result = new CompositeFeatureCollection(featureCollection.getFeature()); } else { // just return the single result = (FeatureCollection) featureCollection.getFeature().iterator().next(); } if (isSimpleFeatureCollection(result) && encoder.getConfiguration().hasProperty( GMLConfiguration.OPTIMIZED_ENCODING)) { return new GML3FeatureCollectionEncoderDelegate( (SimpleFeatureCollection) result, encoder); } else { return result; } } private boolean isSimpleFeatureCollection(FeatureCollection result) { // CompositeFeatureCollection is a simple one, but that's a lie, it might // contain complex sub-collections if (result instanceof CompositeFeatureCollection) { CompositeFeatureCollection composite = (CompositeFeatureCollection) result; for (FeatureCollection collection : composite.getCollections()) { if (!(collection instanceof SimpleFeatureCollection)) { return false; } } return true; } else { return result instanceof SimpleFeatureCollection; } } }