/* (c) 2014 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.wms; import java.io.IOException; import java.util.ArrayList; import java.util.Calendar; import java.util.List; import java.util.logging.Logger; import net.opengis.wfs.FeatureCollectionType; import net.opengis.wfs.WfsFactory; import org.geoserver.platform.GeoServerExtensions; import org.geoserver.platform.ServiceException; import org.geoserver.wms.featureinfo.FeatureCollectionDecorator; import org.geoserver.wms.featureinfo.LayerIdentifier; import org.geotools.data.DataUtilities; import org.geotools.data.Query; import org.geotools.data.simple.SimpleFeatureCollection; import org.geotools.feature.FeatureCollection; import org.geotools.feature.NameImpl; import org.geotools.util.logging.Logging; import org.opengis.feature.simple.SimpleFeatureType; import org.opengis.feature.type.Name; import org.opengis.filter.Filter; /** * WMS GetFeatureInfo operation * * @author Gabriel Roldan */ public class GetFeatureInfo { private static final Logger LOGGER = Logging.getLogger(GetFeatureInfo.class); @SuppressWarnings({ "unchecked", "rawtypes" }) public FeatureCollectionType run(final GetFeatureInfoRequest request) throws ServiceException { List<FeatureCollection> results; try { results = execute(request); } catch (ServiceException se) { se.printStackTrace(); throw se; } catch (Exception e) { e.printStackTrace(); throw new ServiceException("Internal error occurred", e); } return buildResults(results); } @SuppressWarnings({ "unchecked", "rawtypes" }) private FeatureCollectionType buildResults(List<FeatureCollection> results) { FeatureCollectionType result = WfsFactory.eINSTANCE.createFeatureCollectionType(); result.setTimeStamp(Calendar.getInstance()); result.getFeature().addAll(results); return result; } @SuppressWarnings("rawtypes") private List<FeatureCollection> execute(GetFeatureInfoRequest request) throws Exception { final List<MapLayerInfo> requestedLayers = request.getQueryLayers(); FeatureInfoRequestParameters requestParams = new FeatureInfoRequestParameters(request); List<FeatureCollection> results = new ArrayList<FeatureCollection>(requestedLayers.size()); int maxFeatures = request.getFeatureCount(); List<LayerIdentifier> identifiers = GeoServerExtensions.extensions(LayerIdentifier.class); for (int i = 0; i < requestedLayers.size(); i++) { final MapLayerInfo layer = requestedLayers.get(i); LayerIdentifier identifier = getLayerIdentifier(layer, identifiers); List<FeatureCollection> identifiedCollections = identifier.identify(requestParams, maxFeatures); if (identifiedCollections != null) { for (FeatureCollection identifierCollection : identifiedCollections) { FeatureCollection fc = selectProperties(requestParams, identifierCollection); maxFeatures = addToResults(fc, results, layer, request, maxFeatures); } // exit when we have collected enough features if (maxFeatures <= 0) { break; } } requestParams.nextLayer(); } return results; } private LayerIdentifier getLayerIdentifier(MapLayerInfo layer, List<LayerIdentifier> identifiers) { for (LayerIdentifier identifier : identifiers) { if (identifier.canHandle(layer)) { return identifier; } } throw new ServiceException("Could not find any identifier that can handle layer " + layer.getLayerInfo().prefixedName() + " among these identifiers: " + identifiers); } private int addToResults(FeatureCollection collection, List<FeatureCollection> results, final MapLayerInfo layer, GetFeatureInfoRequest request, int maxFeatures) { if (collection != null) { if (!(collection.getSchema() instanceof SimpleFeatureType)) { // put wrapper around it with layer name Name name = new NameImpl(layer.getFeature().getNamespace().getName(), layer .getFeature().getName()); collection = new FeatureCollectionDecorator(name, collection); } int size = collection.size(); if (size != 0) { // HACK HACK HACK // For complex features, we need the targetCrs and version in scenario where we have // a top level feature that does not contain a geometry(therefore no crs) and has a // nested feature that contains geometry as its property.Furthermore it is possible // for each nested feature to have different crs hence we need to reproject on each // feature accordingly. // This is a Hack, this information should not be passed through feature type // appschema will need to remove this information from the feature type again if (!(collection instanceof SimpleFeatureCollection)) { collection.getSchema().getUserData() .put("targetCrs", request.getGetMapRequest().getCrs()); collection.getSchema().getUserData().put("targetVersion", "wms:getfeatureinfo"); } results.add(collection); // don't return more than FEATURE_COUNT maxFeatures -= size; if (maxFeatures <= 0) { return 0; } } } return maxFeatures; } protected FeatureCollection selectProperties(FeatureInfoRequestParameters params, FeatureCollection collection) throws IOException { String[] names = params.getPropertyNames(); if (names != Query.ALL_NAMES) { Query q = new Query(collection.getSchema().getName().getLocalPart(), Filter.INCLUDE, names); return DataUtilities.source(collection).getFeatures(q); } else { return collection; } } }