/* (c) 2014 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.eo;
import java.io.IOException;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import org.geoserver.catalog.CoverageInfo;
import org.geoserver.catalog.LayerInfo;
import org.geoserver.catalog.MetadataMap;
import org.geoserver.ows.AbstractDispatcherCallback;
import org.geoserver.ows.DispatcherCallback;
import org.geoserver.ows.Request;
import org.geoserver.platform.Operation;
import org.geoserver.platform.ServiceException;
import org.geoserver.wms.GetFeatureInfoRequest;
import org.geoserver.wms.MapLayerInfo;
import org.geotools.coverage.grid.io.GridCoverage2DReader;
import org.geotools.factory.GeoTools;
import org.geotools.gce.imagemosaic.ImageMosaicFormat;
import org.geotools.gce.imagemosaic.MergeBehavior;
import org.geotools.util.NullProgressListener;
import org.opengis.coverage.grid.GridCoverageReader;
import org.opengis.parameter.ParameterDescriptor;
import org.opengis.referencing.ReferenceIdentifier;
/**
* {@link EOGetFeatureInfoChecker} is supposed to manipulate the GetFeatureInfo request in
* order to comply with WMS-EO Requirements.
*
* @author Simone Giannecchini, GeoSolutions SAS
*
*/
public class EOGetFeatureInfoChecker extends AbstractDispatcherCallback implements
DispatcherCallback {
@SuppressWarnings({ "rawtypes", "unchecked" })
@Override
public Operation operationDispatched(Request request, Operation operation) {
// paranoiac checks
if(operation==null||operation.getParameters().length<=0){
return super.operationDispatched(request, operation);
}
// === get the underlying operation and check if this is a feature info or not
final Object o=operation.getParameters()[0];
if(!(o instanceof GetFeatureInfoRequest)){
return super.operationDispatched(request, operation);
}
// === k, this is a getfeatureinfo request let's check if it is 1.3.0
org.geoserver.wms.GetFeatureInfoRequest featureinfoReq = (GetFeatureInfoRequest) o;
if(!featureinfoReq.getVersion().equalsIgnoreCase("1.3.0")){
return super.operationDispatched(request, operation);
}
// === ok, it is a getfeatureinfo and 1.3.0, let's do the magic!
// inspect the incoming request for custom dimensions
final Map<String, String> rawKvpMap = request.getRawKvp();
Map<String,List> customDomains = new HashMap<String, List>();
if (rawKvpMap != null) {
for (Map.Entry<String, String> kvp : rawKvpMap.entrySet()) {
String name = kvp.getKey();
if (name.startsWith("DIM_")) {
name = name.substring(4);
if(name.length()>0&&name != null) {
final ArrayList<String> val = new ArrayList<String>(1);
if(kvp.getValue().indexOf(",")>0){
String[] elements = kvp.getValue().split(",");
val.addAll(Arrays.asList(elements));
}else{
val.add(kvp.getValue());
}
customDomains.put(name, val);
}
}
}
}
// cycle on all the requested layers and make sure we check the custom dimensions
final List<MapLayerInfo> queryLayers = featureinfoReq.getQueryLayers();
for(MapLayerInfo queryLayerInfo:queryLayers){
// geoserver info objects
final CoverageInfo cInfo=queryLayerInfo.getCoverage();
final LayerInfo layerInfo=queryLayerInfo.getLayerInfo();
// is it a BANDS Layer?
final MetadataMap metadata = layerInfo.getMetadata();
if(metadata.containsKey(EoLayerType.KEY)&&metadata.get(EoLayerType.KEY).equals(EoLayerType.BAND_COVERAGE.name())){
// check the MERGE_BEHAVIOR as flat (this is harmless anyway)
Map<String, Serializable> params = cInfo.getParameters();
for(Entry<String, Serializable> entry:params.entrySet()){
if(entry.getKey().equalsIgnoreCase(ImageMosaicFormat.MERGE_BEHAVIOR.getName().getCode())){
entry.setValue(MergeBehavior.STACK.toString());
break;
}
}
try{
// check the #of requested values
// get the read parameters for this reader
final GridCoverageReader gridCoverageReader = cInfo.getGridCoverageReader(new NullProgressListener(), GeoTools.getDefaultHints());
final Set<ParameterDescriptor<List>> dynamicParameters = ((GridCoverage2DReader)gridCoverageReader).getDynamicParameters();
if(dynamicParameters.isEmpty()){
throw new IllegalStateException("Layer "+cInfo.getTitle()+ " has no additional dimensions which are required for an EO BANDS layer");
}
final Set<ReferenceIdentifier> dynamicParametersNames= new HashSet<ReferenceIdentifier>();
for(ParameterDescriptor<List> param:dynamicParameters){
dynamicParametersNames.add(param.getName());
}
// inspect the incoming request
for(ParameterDescriptor<List> readParam:dynamicParameters){
final String name=readParam.getName().getCode();
// ok, do we have this one in the request? If so, do we have 1 or 3 values?
if(customDomains.containsKey(name)){
final List values=customDomains.get(name);
// 1 or 3, 0 to leave default kick in
if(values.size()!=0&&values.size()!=1&& values.size()!=3){
throw new ServiceException("Dimension DIM_"+name+" has been request with wrong number of values: "+values.size(), "InvalidDimensionValue");
}
// NOTICE that implicitly we leave the default do its magic if no val
}
}
} catch (IOException e) {
throw new IllegalStateException("Unable to acquire a reader for CoverageInfo: "+cInfo,e);
}
}
}
return super.operationDispatched(request, operation);
}
}