/* (c) 2014 - 2015 Open Source Geospatial Foundation - all rights reserved * (c) 2013 OpenPlans * This code is licensed under the GPL 2.0 license, available at the root * application directory. */ package org.geoserver.wms.featureinfo; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; import java.util.List; import java.util.logging.Level; import java.util.logging.Logger; import net.opengis.wfs.FeatureCollectionType; import org.geoserver.catalog.WMSLayerInfo; import org.geoserver.wms.FeatureInfoRequestParameters; import org.geoserver.wms.MapLayerInfo; import org.geotools.data.ows.Layer; import org.geotools.data.simple.SimpleFeatureCollection; import org.geotools.data.store.ReTypingFeatureCollection; import org.geotools.data.wms.WebMapServer; import org.geotools.feature.FeatureCollection; import org.geotools.feature.simple.SimpleFeatureTypeBuilder; import org.geotools.geometry.jts.ReferencedEnvelope; import org.geotools.map.WMSLayer; import org.geoserver.util.EntityResolverProvider; import org.geotools.util.logging.Logging; import org.geotools.wfs.v1_0.WFSConfiguration; import org.geotools.xml.Parser; import org.opengis.feature.simple.SimpleFeatureType; import org.opengis.referencing.crs.CoordinateReferenceSystem; /** * Layer identifier specialized in WMS cascading layers * * @author Andrea Aime - GeoSolutions */ public class WMSLayerIdentifier implements LayerIdentifier { static final Logger LOGGER = Logging.getLogger(WMSLayerIdentifier.class); private EntityResolverProvider resolverProvider; public WMSLayerIdentifier(EntityResolverProvider resolverProvider) { this.resolverProvider = resolverProvider; } public List<FeatureCollection> identify(FeatureInfoRequestParameters params, int maxFeatures) throws IOException { final int x = params.getX(); final int y = params.getY(); WMSLayerInfo info = (WMSLayerInfo) params.getLayer().getResource(); WebMapServer wms = info.getStore().getWebMapServer(null); Layer layer = info.getWMSLayer(null); CoordinateReferenceSystem crs = params.getRequestedCRS(); if (crs == null) { // use the native one crs = info.getCRS(); } ReferencedEnvelope bbox = params.getRequestedBounds(); int width = params.getWidth(); int height = params.getHeight(); // we can cascade GetFeatureInfo on queryable layers and if the GML mime type is supported if (!layer.isQueryable()) { return null; } List<String> infoFormats; infoFormats = wms.getCapabilities().getRequest().getGetFeatureInfo().getFormats(); if (!infoFormats.contains("application/vnd.ogc.gml")) { return null; } // the wms layer does request in a CRS that's compatible with the WMS server srs // list, // we may need to transform WMSLayer ml = new WMSLayer(wms, layer); // delegate to the web map layer as there's quite a bit of reprojection magic // code // that we want to be consistently reproduced for GetFeatureInfo as well final InputStream is = ml.getFeatureInfo(bbox, width, height, x, y, "application/vnd.ogc.gml", maxFeatures); List<FeatureCollection> results = new ArrayList<FeatureCollection>(); try { Parser parser = new Parser(new WFSConfiguration()); parser.setStrict(false); parser.setEntityResolver(resolverProvider.getEntityResolver()); Object result = parser.parse(is); if (result instanceof FeatureCollectionType) { FeatureCollectionType fcList = (FeatureCollectionType) result; List<SimpleFeatureCollection> rawResults = fcList.getFeature(); // retyping feature collections to replace name and namespace // from cascading server with our local WMSLayerInfo for (SimpleFeatureCollection fc : rawResults) { SimpleFeatureType ft = fc.getSchema(); SimpleFeatureTypeBuilder builder = new SimpleFeatureTypeBuilder(); builder.init(ft); builder.setName(info.getName()); builder.setNamespaceURI(info.getNamespace().getURI()); SimpleFeatureType targetFeatureType = builder.buildFeatureType(); FeatureCollection rfc = new ReTypingFeatureCollection(fc, targetFeatureType); results.add(rfc); } } } catch (Throwable t) { LOGGER.log(Level.SEVERE, "Tried to parse GML2 response, but failed", t); } finally { is.close(); } return results; } public boolean canHandle(MapLayerInfo layer) { return layer.getType() == MapLayerInfo.TYPE_WMS; } }