/* * 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 org.geotools.data.store.ContentEntry; import org.geotools.data.store.ContentFeatureSource; import org.geotools.data.wfs.WFSServiceInfo; import static org.geotools.data.wfs.protocol.wfs.WFSOperationType.GET_CAPABILITIES; import static org.geotools.data.wfs.protocol.wfs.WFSOperationType.GET_FEATURE; import java.io.IOException; import java.net.URI; import java.net.URL; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; import java.util.logging.Level; import javax.xml.namespace.QName; import org.geotools.data.DataAccess; import org.geotools.data.DataSourceException; import org.geotools.data.DataUtilities; import org.geotools.data.DefaultQuery; import org.geotools.data.FeatureWriter; import org.geotools.data.Query; import org.geotools.data.SchemaNotFoundException; import org.geotools.data.Transaction; import org.geotools.data.store.ContentDataStore; import org.geotools.data.wfs.protocol.wfs.GetFeature; import org.geotools.data.wfs.protocol.wfs.GetFeatureParser; import org.geotools.data.wfs.protocol.wfs.WFSExtensions; import org.geotools.data.wfs.protocol.wfs.WFSOperationType; import org.geotools.data.wfs.protocol.wfs.WFSProtocol; import org.geotools.data.wfs.protocol.wfs.WFSResponse; import org.geotools.data.wfs.protocol.wfs.GetFeature.ResultType; import org.geotools.factory.CommonFactoryFinder; import org.geotools.feature.NameImpl; import org.geotools.feature.SchemaException; import org.geotools.geometry.jts.ReferencedEnvelope; import org.geotools.gml2.bindings.GML2EncodingUtils; import org.geotools.referencing.CRS; import org.geotools.referencing.crs.DefaultEngineeringCRS; import org.opengis.feature.simple.SimpleFeature; import org.opengis.feature.simple.SimpleFeatureType; import org.opengis.feature.type.Name; import org.opengis.filter.Filter; import org.opengis.filter.FilterFactory2; import org.opengis.referencing.FactoryException; import org.opengis.referencing.NoSuchAuthorityCodeException; import org.opengis.referencing.crs.CoordinateReferenceSystem; import org.opengis.referencing.operation.TransformException; /** * A WFS 1.1 DataStore implementation. * <p> * Note with the current design, this class is meant to be pulled up as the single WFS DataStore * implementation regardless of the WFS version, since the protocol version specifics is meant to be * handled by the {@link WFSProtocol} implementation provided to this class. For the time being, * while there are no resources to spend on porting the WFS 1.0.0 datastore to the new design, this * keeps here in this 1.1 specific package. * </p> * * @author Gabriel Roldan * @version $Id: WFS_1_1_0_DataStore.java 35372 2010-05-05 11:50:22Z mbedward $ * @since 2.5.x * * * @source $URL: http://svn.osgeo.org/geotools/trunk/modules/unsupported/wfs-ng/src/main/java/org/geotools/data/wfs/v1_1_0/WFSNGDataStore.java $ * http://svn.geotools.org/geotools/trunk/gt/modules/plugin/wfs/src/main/java/org/geotools * /wfs/v_1_1_0/data/WFSDataStore.java $ */ @SuppressWarnings( { "nls" }) public final class WFSNGDataStore extends ContentDataStore { /** * Whether to use POST as default HTTP method is not explicitly set */ private static final boolean DEFAULT_HTTP_METHOD = true; private final WFSProtocol wfs; private Map<String, SimpleFeatureType> byTypeNameTypes; private Integer maxFeaturesHardLimit; private boolean preferPostOverGet = false; /** * The WFS capabilities document. * * @param capabilities */ public WFSNGDataStore(final WFSProtocol wfs) { if (wfs == null) { throw new NullPointerException("wfs protocol"); } this.wfs = wfs; byTypeNameTypes = Collections.synchronizedMap(new HashMap<String, SimpleFeatureType>()); maxFeaturesHardLimit = Integer.valueOf(0); // not set } /** * @see WFSDataStore#setMaxFeatures(Integer) */ public void setMaxFeatures(Integer maxFeatures) { this.maxFeaturesHardLimit = Integer.valueOf(maxFeatures.intValue()); } /** * @see WFSDataStore#getMaxFeatures() */ public Integer getMaxFeatures() { return this.maxFeaturesHardLimit; } /** * @see WFSDataStore#isPreferPostOverGet() */ public boolean isPreferPostOverGet() { return preferPostOverGet; } /** * @see WFSDataStore#setPreferPostOverGet(boolean) */ public void setPreferPostOverGet(Boolean booleanValue) { this.preferPostOverGet = booleanValue == null ? DEFAULT_HTTP_METHOD : booleanValue .booleanValue(); } /** * Makes a {@code DescribeFeatureType} request for {@code typeName} feature type, parses the * server response into a {@link SimpleFeatureType} and returns it. * <p> * Due to a current limitation widely spread through the GeoTools library, the parsed * FeatureType will be adapted to share the same name than the Features produced for it. For * example, if the actual feature type name is {@code Streams_Type} and the features name (i.e. * which is the FeatureType name as stated in the WFS capabilities document) is {@code Stream}, * the returned feature type name will also be {@code Stream}. * </p> * * @param prefixedTypeName * the type name as stated in the WFS capabilities document * @return the GeoTools FeatureType for the {@code typeName} as stated on the capabilities * document. * @see org.geotools.data.DataStore#getSchema(java.lang.String) */ /*public SimpleFeatureType getSchema(final String prefixedTypeName) throws IOException { SimpleFeatureType ftype = byTypeNameTypes.get(prefixedTypeName); if (ftype == null) { // String outputFormat = DEFAULT_OUTPUT_FORMAT; // WFSResponse response; // if (useHttpPostFor(DESCRIBE_FEATURETYPE)) { // response = wfs.describeFeatureTypePOST(prefixedTypeName, outputFormat); // } else { // response = wfs.describeFeatureTypeGET(prefixedTypeName, outputFormat); // } // // WFSResponseParser parser = WFSExtensions.findParser(response); final QName featureDescriptorName; try { featureDescriptorName = wfs.getFeatureTypeName(prefixedTypeName); } catch (IllegalArgumentException e) { throw new SchemaNotFoundException(prefixedTypeName); } final URL describeUrl = wfs.getDescribeFeatureTypeURLGet(prefixedTypeName); // @TODO remove this System.err.println("DecribeFT URL for " + prefixedTypeName + ": " + describeUrl); final SimpleFeatureType featureType; CoordinateReferenceSystem crs = getFeatureTypeCRS(prefixedTypeName); featureType = EmfAppSchemaParser.parseSimpleFeatureType(featureDescriptorName, describeUrl, crs); // adapt the feature type name SimpleFeatureTypeBuilder builder = new SimpleFeatureTypeBuilder(); builder.init(featureType); builder.setName(prefixedTypeName); builder.setNamespaceURI(featureDescriptorName.getNamespaceURI()); GeometryDescriptor defaultGeometry = featureType.getGeometryDescriptor(); if (defaultGeometry != null) { builder.setDefaultGeometry(defaultGeometry.getLocalName()); builder.setCRS(defaultGeometry.getCoordinateReferenceSystem()); } final SimpleFeatureType adaptedFeatureType = builder.buildFeatureType(); ftype = adaptedFeatureType; byTypeNameTypes.put(prefixedTypeName, ftype); } return ftype; }*/ @Override public ContentFeatureSource getFeatureSource(String typeName) throws IOException { if (typeName.indexOf(":")>0){ //Set<Qname> types=wfs.getFeatureTypeNames(); QName name=wfs.getFeatureTypeName(typeName); return getFeatureSource(new NameImpl(name),Transaction.AUTO_COMMIT); } return super.getFeatureSource(typeName); } protected List<Name> createTypeNames() throws IOException { Set<QName> featureTypeNames = wfs.getFeatureTypeNames(); List<Name> names = new ArrayList<Name>(featureTypeNames.size()); String namespaceURI; String localPart; for (QName name : featureTypeNames) { namespaceURI = name.getNamespaceURI(); localPart = name.getLocalPart(); names.add(new NameImpl(namespaceURI, localPart)); } return names; } /** * @see DataAccess#getNames() */ /*public List<Name> getNames() throws IOException { }*/ /** * @see org.geotools.data.DataStore#getTypeNames() */ /*public String[] getTypeNames() throws IOException { Set<QName> featureTypeNames = wfs.getFeatureTypeNames(); List<String> sorted = new ArrayList<String>(featureTypeNames.size()); for (QName name : featureTypeNames) { sorted.add(name.getPrefix() + ":" + name.getLocalPart()); } Collections.sort(sorted); return sorted.toArray(new String[sorted.size()]); }*/ /** * @see org.geotools.data.DataStore#dispose() */ /* public void dispose() { wfs.dispose(); }*/ private WFSResponse executeGetFeatures(final Query query, final Transaction transaction, final ResultType resultType) throws IOException { // TODO: handle output format preferences final String outputFormat = wfs.getDefaultOutputFormat(GET_FEATURE); String srsName = adaptQueryForSupportedCrs((DefaultQuery) query); GetFeature request = new GetFeatureQueryAdapter(query, outputFormat, srsName, resultType); final WFSResponse response = sendGetFeatures(request); return response; } /** * @see org.geotools.data.DataStore#getFeatureReader(org.geotools.data.Query, * org.geotools.data.Transaction) */ /*public FeatureReader<SimpleFeatureType, SimpleFeature> getFeatureReader(Query query, final Transaction transaction) throws IOException { if (Filter.EXCLUDE.equals(query.getFilter())) { return new EmptyFeatureReader<SimpleFeatureType, SimpleFeature>(getQueryType(query)); } query = new DefaultQuery(query); Filter[] filters = wfs.splitFilters(query.getFilter()); Filter supportedFilter = filters[0]; Filter postFilter = filters[1]; System.out.println("Supported filter: " + supportedFilter); System.out.println("Unupported filter: " + postFilter); ((DefaultQuery) query).setFilter(supportedFilter); ((DefaultQuery) query).setMaxFeatures(getMaxFeatures(query)); final CoordinateReferenceSystem queryCrs = query.getCoordinateSystem(); WFSResponse response = executeGetFeatures(query, transaction, ResultType.RESULTS); Object result = WFSExtensions.process(this, response); GetFeatureParser parser; if (result instanceof WFSException) { // try to recover from common server implementation errors throw (WFSException) result; } else if (result instanceof GetFeatureParser) { parser = (GetFeatureParser) result; } else { throw new IllegalStateException("Unknown response result for GetFeature: " + result); } final SimpleFeatureType contentType = getQueryType(query); FeatureReader<SimpleFeatureType, SimpleFeature> reader; reader = new WFSFeatureReader((GetFeatureParser) parser); if (!reader.hasNext()) { return new EmptyFeatureReader<SimpleFeatureType, SimpleFeature>(contentType); } final SimpleFeatureType readerType = reader.getFeatureType(); CoordinateReferenceSystem readerCrs = readerType.getCoordinateReferenceSystem(); if (queryCrs != null && !queryCrs.equals(readerCrs)) { try { reader = new ReprojectFeatureReader(reader, queryCrs); } catch (Exception e) { throw new DataSourceException(e); } } if (Filter.INCLUDE != postFilter) { reader = new FilteringFeatureReader<SimpleFeatureType, SimpleFeature>(reader, postFilter); } if (!contentType.equals(readerType)) { final boolean cloneContents = false; reader = new ReTypeFeatureReader(reader, contentType, cloneContents); } if (this.maxFeaturesHardLimit.intValue() > 0 || query.getMaxFeatures() != Integer.MAX_VALUE) { int maxFeatures = maxFeaturesHardLimit.intValue() > 0 ? Math.min(maxFeaturesHardLimit.intValue(), query.getMaxFeatures()) : query.getMaxFeatures(); reader = new MaxFeatureReader<SimpleFeatureType, SimpleFeature>(reader, maxFeatures); } return reader; }*/ /** * Sends the GetFeature request using the appropriate HTTP method depending on the * {@link #isPreferPostOverGet()} preference and what the server supports. * * @param request * the request to send * @param map * @return the server response handle * @throws IOException * if a communication error occurs. If a server returns an exception report that's a * normal response, no exception will be thrown here. */ private WFSResponse sendGetFeatures(GetFeature request) throws IOException { final WFSResponse response; if (useHttpPostFor(GET_FEATURE)) { response = wfs.issueGetFeaturePOST(request); } else { response = wfs.issueGetFeatureGET(request); } return response; } /** * Returns the feature type that shall result of issueing the given request, adapting the * original feature type for the request's type name in terms of the query CRS and requested * attributes. * * @param query * @return * @throws IOException */ SimpleFeatureType getQueryType(final Query query) throws IOException { final String typeName = query.getTypeName(); final SimpleFeatureType featureType = getSchema(typeName); final CoordinateReferenceSystem coordinateSystemReproject = query .getCoordinateSystemReproject(); String[] propertyNames = query.getPropertyNames(); SimpleFeatureType queryType = featureType; if (propertyNames != null && propertyNames.length > 0) { try { queryType = DataUtilities.createSubType(queryType, propertyNames); } catch (SchemaException e) { throw new DataSourceException(e); } } else { propertyNames = DataUtilities.attributeNames(featureType); } if (coordinateSystemReproject != null) { try { queryType = DataUtilities.createSubType(queryType, propertyNames, coordinateSystemReproject); } catch (SchemaException e) { throw new DataSourceException(e); } } return queryType; } /** * @see org.geotools.data.DataStore#getFeatureSource(java.lang.String) */ /*public WFSFeatureSource getFeatureSource(final String typeName) throws IOException { return new WFSFeatureSource(this, typeName); }*/ /** * Not supported. * * @see org.geotools.data.DataStore#getFeatureWriter(java.lang.String, * org.opengis.filter.Filter, org.geotools.data.Transaction) * @throws UnsupportedOperationException * always since this operation does not apply to a WFS backend */ @Override public FeatureWriter<SimpleFeatureType, SimpleFeature> getFeatureWriter(String typeName, Filter filter, Transaction transaction) throws IOException { throw new UnsupportedOperationException("This is a read only DataStore"); } /** * Not supported. * * @see org.geotools.data.DataStore#getFeatureWriter(java.lang.String, * org.geotools.data.Transaction) * @throws UnsupportedOperationException * always since this operation does not apply to a WFS backend */ /*public FeatureWriter<SimpleFeatureType, SimpleFeature> getFeatureWriter(String typeName, Transaction transaction) throws IOException { throw new UnsupportedOperationException("This is a read only DataStore"); }*/ /** * @see org.geotools.data.DataAccess#getFeatureSource(org.opengis.feature.type.Name) */ /* public SimpleFeatureSource getFeatureSource(Name typeName) throws IOException { Set<QName> featureTypeNames = wfs.getFeatureTypeNames(); final String namespaceURI = typeName.getNamespaceURI(); final String localPart = typeName.getLocalPart(); for (QName qname : featureTypeNames) { if (namespaceURI.equals(qname.getNamespaceURI()) && localPart.equals(qname.getLocalPart())) { String prefixedName = qname.getPrefix() + ":" + localPart; return getFeatureSource(prefixedName); } } throw new SchemaNotFoundException(typeName.getURI()); }*/ /** * @see DataAccess#updateSchema(Name, org.opengis.feature.type.FeatureType) * @throws UnsupportedOperationException * always since this operation does not apply to a WFS backend */ public void updateSchema(Name typeName, SimpleFeatureType featureType) throws IOException { throw new UnsupportedOperationException("WFS does not support update schema"); } /** * @see org.geotools.data.DataStore#updateSchema(java.lang.String, * org.opengis.feature.simple.SimpleFeatureType) * @throws UnsupportedOperationException * always since this operation does not apply to a WFS backend */ /*public void updateSchema(String typeName, SimpleFeatureType featureType) throws IOException { throw new UnsupportedOperationException("WFS does not support update schema"); }*/ /** * @see org.geotools.data.DataStore#createSchema(org.opengis.feature.simple.SimpleFeatureType) * @throws UnsupportedOperationException * always since this operation does not apply to a WFS backend */ public void createSchema(SimpleFeatureType featureType) throws IOException { throw new UnsupportedOperationException("WFS DataStore does not support createSchema"); } /** * @see WFSDataStore#getFeatureTypeName */ public QName getFeatureTypeName(String typeName) { return wfs.getFeatureTypeName(typeName); } /** * @see WFSDataStore#getFeatureTypeTitle(String) */ public String getFeatureTypeTitle(String typeName) { return wfs.getFeatureTypeTitle(typeName); } /** * @see WFSDataStore#getFeatureTypeAbstract(String) */ public String getFeatureTypeAbstract(String typeName) { return wfs.getFeatureTypeAbstract(typeName); } /** * @see WFSDataStore#getFeatureTypeWGS84Bounds(String) */ public ReferencedEnvelope getFeatureTypeWGS84Bounds(String typeName) { return wfs.getFeatureTypeWGS84Bounds(typeName); } /** * @see WFSDataStore#getFeatureTypeBounds(String) */ public ReferencedEnvelope getFeatureTypeBounds(String typeName) { final ReferencedEnvelope wgs84Bounds = wfs.getFeatureTypeWGS84Bounds(typeName); final CoordinateReferenceSystem ftypeCrs = getFeatureTypeCRS(typeName); ReferencedEnvelope nativeBounds; try { nativeBounds = wgs84Bounds.transform(ftypeCrs, true); } catch (TransformException e) { LOGGER.log(Level.WARNING, "Can't transform bounds of " + typeName + " to " + wfs.getDefaultCRS(typeName), e); nativeBounds = new ReferencedEnvelope(ftypeCrs); } catch (FactoryException e) { LOGGER.log(Level.WARNING, "Can't transform bounds of " + typeName + " to " + wfs.getDefaultCRS(typeName), e); nativeBounds = new ReferencedEnvelope(ftypeCrs); } return nativeBounds; } /** * @return a non null CRS for the feature type, if the actual CRS can't be determined, * {@link DefaultEngineeringCRS#GENERIC_2D} is returned * @see WFSDataStore#getFeatureTypeCRS(String) */ public CoordinateReferenceSystem getFeatureTypeCRS(String typeName) { final String defaultCRS = wfs.getDefaultCRS(typeName); CoordinateReferenceSystem crs = null; try { crs = CRS.decode(defaultCRS); } catch (NoSuchAuthorityCodeException e) { LOGGER.info("Authority not found for " + typeName + " CRS: " + defaultCRS); // HACK HACK HACK!: remove when // http://jira.codehaus.org/browse/GEOT-1659 is fixed if (defaultCRS.toUpperCase().startsWith("URN")) { String code = defaultCRS.substring(defaultCRS.lastIndexOf(":") + 1); String epsgCode = "EPSG:" + code; try { crs = CRS.decode(epsgCode); } catch (Exception e1) { LOGGER.log(Level.WARNING, "can't decode CRS " + epsgCode + " for " + typeName + ". Assigning DefaultEngineeringCRS.GENERIC_2D: " + DefaultEngineeringCRS.GENERIC_2D); crs = DefaultEngineeringCRS.GENERIC_2D; } } } catch (FactoryException e) { LOGGER.log(Level.WARNING, "Error creating CRS " + typeName + ": " + defaultCRS, e); } return crs; } /** * @see WFSDataStore#getFeatureTypeKeywords(String) */ public Set<String> getFeatureTypeKeywords(String typeName) { return wfs.getFeatureTypeKeywords(typeName); } /** * @see WFSDataStore#getDescribeFeatureTypeURL(String) */ public URL getDescribeFeatureTypeURL(String typeName) { return wfs.getDescribeFeatureTypeURLGet(typeName); } /** * @see WFSDataStore#getServiceAbstract() */ public String getServiceAbstract() { return wfs.getServiceAbstract(); } /** * @see WFSDataStore#getServiceKeywords() */ public Set<String> getServiceKeywords() { return wfs.getServiceKeywords(); } /** * @see WFSDataStore#getServiceProviderUri() */ public URI getServiceProviderUri() { return wfs.getServiceProviderUri(); } /** * @see WFSDataStore#getCapabilitiesURL() */ public URL getCapabilitiesURL() { URL capsUrl = wfs.getOperationURL(GET_CAPABILITIES, false); if (capsUrl == null) { capsUrl = wfs.getOperationURL(GET_CAPABILITIES, true); } return capsUrl; } /** * @see WFSDataStore#getserviceTitle() */ public String getServiceTitle() { return wfs.getServiceTitle(); } /** * @see WFSDataStore#getServiceVersion() */ public String getServiceVersion() { return wfs.getServiceVersion().toString(); } /** * Returns the WFSProtocol this Datastore uses */ public WFSProtocol getWFSProtocol(){ return wfs; } /** * Only returns the bounds of the query (ie, the bounds of the whole feature type) if the query * has no filter set, otherwise the bounds may be too expensive to acquire. * * @param query * @return The bounding box of the datasource in the CRS required by the query, or {@code null} * if unknown and too expensive for the method to calculate or any errors occur. */ public ReferencedEnvelope getBounds(final Query query) throws IOException { if (!Filter.INCLUDE.equals(query.getFilter())) { return null; } final String typeName = query.getTypeName(); ReferencedEnvelope featureTypeBounds; featureTypeBounds = getFeatureTypeBounds(typeName); final CoordinateReferenceSystem featureTypeCrs = featureTypeBounds .getCoordinateReferenceSystem(); final CoordinateReferenceSystem queryCrs = query.getCoordinateSystem(); if (queryCrs != null && !CRS.equalsIgnoreMetadata(queryCrs, featureTypeCrs)) { try { featureTypeBounds = featureTypeBounds.transform(queryCrs, true); } catch (TransformException e) { LOGGER.log(Level.INFO, "Error transforming bounds for " + typeName, e); featureTypeBounds = null; } catch (FactoryException e) { LOGGER.log(Level.INFO, "Error transforming bounds for " + typeName, e); featureTypeBounds = null; } } return featureTypeBounds; } /** * If the query is fully supported, makes a {@code GetFeature} request with {@code * resultType=hits} and returns the counts returned by the server, otherwise returns {@code -1} * as the result is too expensive to calculate. * * @param query * @return the number of features returned by a GetFeature?resultType=hits request, or {@code * -1} if not supported */ public int getCount(final Query query) throws IOException { Filter[] filters = wfs.splitFilters(query.getFilter()); Filter postFilter = filters[1]; if (!Filter.INCLUDE.equals(postFilter)) { // Filter not fully supported, can't know without a full scan of the results return -1; } WFSResponse response = executeGetFeatures(query, Transaction.AUTO_COMMIT, ResultType.HITS); Object process = WFSExtensions.process(this, response); if (!(process instanceof GetFeatureParser)) { LOGGER.info("GetFeature with resultType=hits resulted in " + process); } int hits = ((GetFeatureParser) process).getNumberOfFeatures(); if (hits != -1 && getMaxFeatures().intValue() > 0) { hits = Math.min(hits, getMaxFeatures().intValue()); } return hits; } @Override public String toString() { StringBuilder sb = new StringBuilder("WFSDataStore["); sb.append("version=").append(getServiceVersion()); URL capabilitiesUrl = getCapabilitiesURL(); sb.append(", URL=").append(capabilitiesUrl); sb.append(", max features=").append( maxFeaturesHardLimit.intValue() == 0 ? "not set" : String .valueOf(maxFeaturesHardLimit)); sb.append(", prefer POST over GET=").append(preferPostOverGet); sb.append("]"); return sb.toString(); } /** * @return * <code>true<code> if HTTP POST method should be used to issue the given WFS operation, <code>false</code> * if HTTP GET method should be used instead */ private boolean useHttpPostFor(final WFSOperationType operation) { if (preferPostOverGet) { if (wfs.supportsOperation(operation, true)) { return true; } } if (wfs.supportsOperation(operation, false)) { return false; } throw new IllegalArgumentException("Neither POST nor GET method is supported for the " + operation + " operation by the server"); } /** * Checks if the query requested CRS is supported by the query feature type and if not, adapts * the query to the feature type default CRS, returning the CRS identifier to use for the WFS * query. * <p> * If the query CRS is not advertised as supported in the WFS capabilities for the requested * feature type, the query filter is modified so that any geometry literal is reprojected to the * default CRS for the feature type, otherwise the query is not modified at all. In any case, * the crs identifier to actually use in the WFS GetFeature operation is returned. * </p> * * @param query * @return * @throws IOException */ private String adaptQueryForSupportedCrs(DefaultQuery query) throws IOException { // The CRS the query is performed in final String typeName = query.getTypeName(); final CoordinateReferenceSystem queryCrs = query.getCoordinateSystem(); final String defaultCrs = wfs.getDefaultCRS(typeName); if (queryCrs == null) { LOGGER.log(Level.WARNING, "Query does not provide a CRS, using default: {0}", query); return defaultCrs; } String epsgCode; final CoordinateReferenceSystem crsNative = getFeatureTypeCRS(typeName); if (CRS.equalsIgnoreMetadata(queryCrs, crsNative)) { epsgCode = defaultCrs; LOGGER.fine("request and native crs for " + typeName + " are the same: " + epsgCode); } else { boolean transform = false; epsgCode = GML2EncodingUtils.epsgCode(queryCrs); if (epsgCode == null) { LOGGER.fine("Can't find the identifier for the request CRS, " + "query will be performed in native CRS"); transform = true; } else { epsgCode = "EPSG:" + epsgCode; LOGGER.fine("Request CRS is " + epsgCode + ", checking if its supported for " + typeName); Set<String> supportedCRSIdentifiers = wfs.getSupportedCRSIdentifiers(typeName); if (supportedCRSIdentifiers.contains(epsgCode)) { LOGGER.fine(epsgCode + " is supported, request will be performed asking " + "for reprojection over it"); } else { LOGGER.fine(epsgCode + " is not supported for " + typeName + ". Query will be adapted to default CRS " + defaultCrs); transform = true; } if (transform) { epsgCode = defaultCrs; FilterFactory2 ff = CommonFactoryFinder.getFilterFactory2(null); SimpleFeatureType ftype = getSchema(typeName); ReprojectingFilterVisitor visitor = new ReprojectingFilterVisitor(ff, ftype); Filter filter = query.getFilter(); Filter reprojectedFilter = (Filter) filter.accept(visitor, null); if (LOGGER.isLoggable(Level.FINER)) { LOGGER.finer("Original Filter: " + filter + "\nReprojected filter: " + reprojectedFilter); } LOGGER.fine("Query filter reprojected to native CRS for " + typeName); query.setFilter(reprojectedFilter); } } } return epsgCode; } protected int getMaxFeatures(Query query) { int maxFeaturesDataStoreLimit = getMaxFeatures().intValue(); int queryMaxFeatures = query.getMaxFeatures(); int maxFeatures = Query.DEFAULT_MAX; if (Query.DEFAULT_MAX != queryMaxFeatures) { maxFeatures = queryMaxFeatures; } if (maxFeaturesDataStoreLimit > 0) { maxFeatures = Math.min(maxFeaturesDataStoreLimit, maxFeatures); } return maxFeatures; } protected ContentFeatureSource createFeatureSource(ContentEntry entry) throws IOException { // grab the schema, it carries a flag telling us if the feature type is read only SimpleFeatureType schema = entry.getState(Transaction.AUTO_COMMIT).getFeatureType(); if (schema == null) { // if the schema still haven't been computed, force its computation so // that we can decide if the feature type is read only schema = new WFSFeatureSource(entry, null).buildFeatureType(); entry.getState(Transaction.AUTO_COMMIT).setFeatureType(schema); } return new WFSFeatureSource(entry, null); } public WFSServiceInfo getInfo() { return new CapabilitiesServiceInfo(this); } }