/* (c) 2014 - 2017 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.map;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.logging.Level;
import javax.media.jai.Interpolation;
import javax.servlet.http.HttpServletRequest;
import org.apache.commons.collections.EnumerationUtils;
import org.geoserver.catalog.DimensionInfo;
import org.geoserver.catalog.LayerGroupInfo;
import org.geoserver.catalog.LayerInfo;
import org.geoserver.catalog.MetadataMap;
import org.geoserver.catalog.ResourceInfo;
import org.geoserver.catalog.SLDHandler;
import org.geoserver.catalog.StyleInfo;
import org.geoserver.catalog.Styles;
import org.geoserver.catalog.WMSLayerInfo;
import org.geoserver.catalog.util.ReaderDimensionsAccessor;
import org.geoserver.ows.HttpServletRequestAware;
import org.geoserver.ows.KvpRequestReader;
import org.geoserver.ows.LocalHttpServletRequest;
import org.geoserver.ows.util.KvpUtils;
import org.geoserver.platform.ServiceException;
import org.geoserver.wms.GetMapRequest;
import org.geoserver.wms.MapLayerInfo;
import org.geoserver.wms.WMS;
import org.geoserver.wms.WMSErrorCode;
import org.geotools.coverage.grid.io.GridCoverage2DReader;
import org.geotools.data.DataStore;
import org.geotools.data.FeatureReader;
import org.geotools.data.Query;
import org.geotools.data.Transaction;
import org.geotools.data.crs.ForceCoordinateSystemFeatureReader;
import org.geotools.data.memory.MemoryDataStore;
import org.geotools.data.simple.SimpleFeatureSource;
import org.geotools.data.wfs.WFSDataStoreFactory;
import org.geotools.factory.CommonFactoryFinder;
import org.geotools.feature.FeatureTypes;
import org.geotools.referencing.CRS;
import org.geotools.referencing.crs.DefaultGeographicCRS;
import org.geotools.styling.FeatureTypeConstraint;
import org.geotools.styling.FeatureTypeStyle;
import org.geotools.styling.NamedLayer;
import org.geotools.styling.NamedStyle;
import org.geotools.styling.RemoteOWS;
import org.geotools.styling.Style;
import org.geotools.styling.StyleAttributeExtractor;
import org.geotools.styling.StyleFactory;
import org.geotools.styling.StyledLayer;
import org.geotools.styling.StyledLayerDescriptor;
import org.geotools.styling.UserLayer;
import org.geoserver.util.EntityResolverProvider;
import org.opengis.feature.simple.SimpleFeature;
import org.opengis.feature.simple.SimpleFeatureType;
import org.opengis.feature.type.FeatureType;
import org.opengis.filter.Filter;
import org.opengis.filter.FilterFactory;
import org.opengis.filter.Id;
import org.opengis.filter.expression.PropertyName;
import org.opengis.referencing.crs.CoordinateReferenceSystem;
import org.vfny.geoserver.util.Requests;
import org.vfny.geoserver.util.SLDValidator;
import org.xml.sax.EntityResolver;
import org.xml.sax.SAXException;
public class GetMapKvpRequestReader extends KvpRequestReader implements HttpServletRequestAware {
private static Map<String, Integer> interpolationMethods;
static {
interpolationMethods = new HashMap<String, Integer>();
interpolationMethods.put("NEAREST NEIGHBOR", Interpolation.INTERP_NEAREST);
interpolationMethods.put("BILINEAR", Interpolation.INTERP_BILINEAR);
interpolationMethods.put("BICUBIC", Interpolation.INTERP_BICUBIC);
}
/**
* style factory
*/
private StyleFactory styleFactory = CommonFactoryFinder.getStyleFactory(null);
/**
* filter factory
*/
private FilterFactory filterFactory = CommonFactoryFinder.getFilterFactory(null);
/**
* Flag to control wether styles shall be parsed.
*/
private boolean parseStyles = true;
/**
* The WMS configuration facade, that we use to pick up base layer definitions
*/
private WMS wms;
/**
* EntityResolver provider, used in SLD parsing
*/
EntityResolverProvider entityResolverProvider;
/**
* This flags allows the kvp reader to go beyond the SLD library mode specification and match
* the first style that can be applied to a given layer. This is for backwards compatibility
*/
private boolean laxStyleMatchAllowed = true;
public GetMapKvpRequestReader(WMS wms) {
super(GetMapRequest.class);
this.wms = wms;
this.entityResolverProvider = new EntityResolverProvider(wms.getGeoServer());
}
/**
* Implements {@link HttpServletRequestAware#setHttpRequest(HttpServletRequest)} to gather
* request information for some properties like {@link GetMapRequest#isGet()} and
* {@link GetMapRequest#getRequestCharset()}.
*
* @see org.geoserver.ows.HttpServletRequestAware#setHttpRequest(javax.servlet.http.HttpServletRequest)
*/
public void setHttpRequest(HttpServletRequest httpRequest) {
LocalHttpServletRequest.set(httpRequest);
}
public void setStyleFactory(StyleFactory styleFactory) {
this.styleFactory = styleFactory;
}
public void setFilterFactory(FilterFactory filterFactory) {
this.filterFactory = filterFactory;
}
public boolean isParseStyle() {
return parseStyles;
}
public void setParseStyle(boolean styleRequired) {
this.parseStyles = styleRequired;
}
@SuppressWarnings("unchecked")
@Override
public GetMapRequest createRequest() throws Exception {
GetMapRequest request = new GetMapRequest();
HttpServletRequest httpRequest = LocalHttpServletRequest.get();
if (httpRequest != null) {
request.setRequestCharset(httpRequest.getCharacterEncoding());
request.setGet("GET".equalsIgnoreCase(httpRequest.getMethod()));
List<String> headerNames = (List<String>) EnumerationUtils.toList(httpRequest
.getHeaderNames());
for (String headerName : headerNames) {
request.putHttpRequestHeader(headerName, httpRequest.getHeader(headerName));
}
}
return request;
}
/**
* Returns whether the specified resource must be skipped in the context of the current request.
*/
protected boolean skipResource(Object theResource) {
return false;
}
@SuppressWarnings("rawtypes")
@Override
public GetMapRequest read(Object request, Map kvp, Map rawKvp) throws Exception {
GetMapRequest getMap = (GetMapRequest) super.read(request, kvp, rawKvp);
// set the raw params used to create the request
getMap.setRawKvp(rawKvp);
// wms 1.3, srs changed to crs
if (kvp.containsKey("crs")) {
getMap.setSRS((String)kvp.get("crs"));
}
// do some additional checks
// srs
String epsgCode = getMap.getSRS();
epsgCode = WMS.toInternalSRS(epsgCode, WMS.version(getMap.getVersion()));
getMap.setSRS(epsgCode);
if (epsgCode != null) {
try {
// set the crs as well
CoordinateReferenceSystem mapcrs = CRS.decode(epsgCode);
getMap.setCrs(mapcrs);
} catch (Exception e) {
// couldnt make it - we send off a service exception with the
// correct info
throw new ServiceException("Error occurred decoding the espg code " + epsgCode, e,
WMSErrorCode.INVALID_CRS.get(getMap.getVersion()));
}
}
// remote OWS
String remoteOwsType = getMap.getRemoteOwsType();
remoteOwsType = remoteOwsType != null ? remoteOwsType.toUpperCase() : null;
if (remoteOwsType != null && !"WFS".equals(remoteOwsType)) {
throw new ServiceException("Unsupported remote OWS type '" + remoteOwsType + "'");
}
// remote OWS url
URL remoteOwsUrl = getMap.getRemoteOwsURL();
if (remoteOwsUrl != null && remoteOwsType == null) {
throw new ServiceException("REMOTE_OWS_URL specified, but REMOTE_OWS_TYPE is missing");
}
final List<Object> requestedLayerInfos = new ArrayList<Object>();
// layers
String layerParam = (String) rawKvp.get("LAYERS");
if (layerParam != null) {
List<String> layerNames = KvpUtils.readFlat(layerParam);
requestedLayerInfos.addAll(parseLayers(layerNames, remoteOwsUrl, remoteOwsType));
}
// raw styles parameter
String stylesParam = (String) kvp.get("STYLES");
List<String> styleNameList = new ArrayList<String>();
if (stylesParam != null) {
styleNameList.addAll(KvpUtils.readFlat(stylesParam));
}
// raw interpolations parameter
String interpolationParam = (String) kvp.get("INTERPOLATIONS");
List<String> interpolationList = new ArrayList<String>();
if (interpolationParam != null) {
interpolationList.addAll(KvpUtils.readFlat(interpolationParam));
}
// raw filter and cql_filter parameters
List<Filter> rawFilters = ((getMap.getFilter() != null) ?
new ArrayList<Filter>(getMap.getFilter()) : Collections.emptyList());
List<Filter> cqlFilters = ((getMap.getCQLFilter() != null) ?
new ArrayList<Filter>(getMap.getCQLFilter()) : Collections.emptyList());
// remove skipped resources along with their corresponding parameters
List<MapLayerInfo> newLayers = new ArrayList<>();
for (int i = 0; i < requestedLayerInfos.size(); ) {
Object o = requestedLayerInfos.get(i);
if (skipResource(o)) {
// remove the layer, style, interpolation, filter and cql_filter
requestedLayerInfos.remove(i);
if (i < styleNameList.size()) {
styleNameList.remove(i);
}
if (i < interpolationList.size()) {
interpolationList.remove(i);
}
if (i < rawFilters.size()) {
rawFilters.remove(i);
}
if (i < cqlFilters.size()) {
cqlFilters.remove(i);
}
} else {
if (o instanceof LayerInfo) {
newLayers.add(new MapLayerInfo((LayerInfo) o));
} else if (o instanceof LayerGroupInfo) {
for (LayerInfo l : ((LayerGroupInfo) o).layers()) {
newLayers.add(new MapLayerInfo(l));
}
} else if (o instanceof MapLayerInfo) {
// it was a remote OWS layer, add it directly
newLayers.add((MapLayerInfo) o);
}
i++;
}
}
getMap.setLayers(newLayers);
if(interpolationList.size() > 0) {
getMap.setInterpolations(parseInterpolations(requestedLayerInfos, interpolationList));
}
// pre parse filters
List<Filter> filters = parseFilters(getMap, rawFilters, cqlFilters);
if ((getMap.getSldBody() != null || getMap.getSld() != null) && wms.isDynamicStylingDisabled()) {
throw new ServiceException("Dynamic style usage is forbidden");
}
// styles
// process SLD_BODY, SLD, then STYLES parameter
if (getMap.getSldBody() != null) {
if (LOGGER.isLoggable(Level.FINE)) {
LOGGER.fine("Getting layers and styles from SLD_BODY");
}
if (getMap.getValidateSchema().booleanValue()) {
ByteArrayInputStream stream = new ByteArrayInputStream(getMap.getSldBody()
.getBytes());
List errors = validateStyle(stream, getMap);
if (errors.size() != 0) {
throw new ServiceException(SLDValidator.getErrorMessage(
new ByteArrayInputStream(getMap.getSldBody().getBytes()), errors));
}
}
InputStream input = new ByteArrayInputStream(getMap.getSldBody().getBytes());
StyledLayerDescriptor sld = parseStyle(getMap, input);
processSld(getMap, requestedLayerInfos, sld, styleNameList);
// set filter in, we'll check consistency later
getMap.setFilter(filters);
} else if (getMap.getSld() != null) {
if (LOGGER.isLoggable(Level.FINE)) {
LOGGER.fine("Getting layers and styles from reomte SLD");
}
URL styleUrl = getMap.getStyleUrl();
if (getMap.getValidateSchema().booleanValue()) {
InputStream input = Requests.getInputStream(styleUrl);
List errors = null;
try {
errors = validateStyle(input, getMap);
} finally {
input.close();
}
if ((errors != null) && (errors.size() != 0)) {
input = Requests.getInputStream(styleUrl);
try {
throw new ServiceException(SLDValidator.getErrorMessage(input, errors));
} finally {
input.close();
}
}
}
// JD: GEOS-420, Wrap the sldUrl in getINputStream method in order
// to do compression
try(InputStream input = Requests.getInputStream(styleUrl);){
StyledLayerDescriptor sld = parseStyle(getMap, input);
processSld(getMap, requestedLayerInfos, sld, styleNameList);
} catch (Exception ex) {
final Level l = Level.WARNING;
// KMS: Kludge here to allow through certain exceptions without being hidden.
if(ex.getCause() instanceof SAXException) {
if(ex.getCause().getMessage().contains("Entity resolution disallowed")) {
throw ex;
}
}
LOGGER.log(l, "Exception while getting SLD.", ex);
// KMS: Replace with a generic exception so it can't be used to port scan the local
// network.
if(LOGGER.isLoggable(l)){
throw new ServiceException("Error while getting SLD. See the log for details.");
}
else
{
throw new ServiceException("Error while getting SLD.");
}
}
// set filter in, we'll check consistency later
getMap.setFilter(filters);
} else {
if (LOGGER.isLoggable(Level.FINE)) {
LOGGER.fine("Getting layers and styles from LAYERS and STYLES");
}
// ok, parse the styles parameter in isolation
if (styleNameList.size() > 0) {
List<Style> parseStyles = parseStyles(styleNameList);
getMap.setStyles(parseStyles);
}
// first, expand base layers and default styles
if (isParseStyle() && requestedLayerInfos.size() > 0) {
List<Style> oldStyles = getMap.getStyles() != null ? new ArrayList(
getMap.getStyles()) : new ArrayList();
List<Style> newStyles = new ArrayList<Style>();
List<Filter> newFilters = filters == null ? null : new ArrayList<Filter>();
for (int i = 0; i < requestedLayerInfos.size(); i++) {
Object o = requestedLayerInfos.get(i);
Style style = oldStyles.isEmpty() ? null : (Style) oldStyles.get(i);
if (o instanceof LayerGroupInfo) {
LayerGroupInfo groupInfo = (LayerGroupInfo) o;
List<LayerInfo> layers = groupInfo.layers();
List<StyleInfo> styles = groupInfo.styles();
for (int j = 0; j < styles.size(); j++) {
StyleInfo si = styles.get(j);
if (si != null){
newStyles.add(si.getStyle());
} else {
LayerInfo layer = layers.get(j);
newStyles.add(getDefaultStyle(layer));
}
}
// expand the filter on the layer group to all its sublayers
if (filters != null) {
for (int j = 0; j < layers.size(); j++) {
newFilters.add(getFilter(filters, i));
}
}
} else if (o instanceof LayerInfo) {
style = oldStyles.size() > 0 ? oldStyles.get(i) : null;
if (style != null) {
newStyles.add(style);
} else {
LayerInfo layer = (LayerInfo) o;
newStyles.add(getDefaultStyle(layer));
}
// add filter if needed
if (filters != null)
newFilters.add(getFilter(filters, i));
} else if (o instanceof MapLayerInfo) {
style = oldStyles.size() > 0 ? oldStyles.get(i) : null;
if (style != null) {
newStyles.add(style);
} else {
throw new ServiceException("no style requested for layer "
+ ((MapLayerInfo) o).getName(), "NoDefaultStyle");
}
// add filter if needed
if (filters != null)
newFilters.add(getFilter(filters, i));
}
}
getMap.setStyles(newStyles);
getMap.setFilter(newFilters);
}
// then proceed with standard processing
List<MapLayerInfo> layers = getMap.getLayers();
if (isParseStyle() && (layers != null) && (layers.size() > 0)) {
final List styles = getMap.getStyles();
if (layers.size() != styles.size()) {
String msg = layers.size() + " layers requested, but found " + styles.size()
+ " styles specified. ";
throw new ServiceException(msg, getClass().getName());
}
for (int i = 0; i < styles.size(); i++) {
Style currStyle = (Style) getMap.getStyles().get(i);
if (currStyle == null)
throw new ServiceException(
"Could not find a style for layer "
+ getMap.getLayers().get(i).getName()
+ ", either none was specified or no default style is available for it",
"NoDefaultStyle");
checkStyle(currStyle, layers.get(i));
if (LOGGER.isLoggable(Level.FINE)) {
LOGGER.fine(new StringBuffer("establishing ").append(currStyle.getName())
.append(" style for ").append(layers.get(i).getName()).toString());
}
}
}
// check filter size matches with the layer list size
List mapFilters = getMap.getFilter();
List<MapLayerInfo> mapLayers = getMap.getLayers();
if (mapFilters != null && mapFilters.size() != mapLayers.size()) {
String msg = mapLayers.size() + " layers requested, but found " + mapFilters.size()
+ " filters specified. ";
throw new ServiceException(msg, getClass().getName());
}
}
// check the view params
List<Map<String, String>> viewParams = getMap.getViewParams();
if(viewParams != null && viewParams.size() > 0) {
int layerCount = getMap.getLayers().size();
// if we have just one replicate over all layers
if(viewParams.size() == 1 && layerCount > 1) {
List<Map<String, String>> replacement = new ArrayList<Map<String,String>>();
for (int i = 0; i < layerCount; i++) {
replacement.add(viewParams.get(0));
}
getMap.setViewParams(replacement);
} else if(viewParams.size() != layerCount) {
String msg = layerCount + " layers requested, but found " + viewParams.size()
+ " view params specified. ";
throw new ServiceException(msg, getClass().getName());
}
}
// check if layers have time/elevation support
boolean hasTime = false;
boolean hasElevation = false;
for (MapLayerInfo layer : getMap.getLayers()) {
if (layer.getType() == MapLayerInfo.TYPE_VECTOR) {
MetadataMap metadata = layer.getResource().getMetadata();
DimensionInfo elevationInfo = metadata.get(ResourceInfo.ELEVATION, DimensionInfo.class);
hasElevation |= elevationInfo != null && elevationInfo.isEnabled();
DimensionInfo timeInfo = metadata.get(ResourceInfo.TIME, DimensionInfo.class);
hasTime |= timeInfo != null && timeInfo.isEnabled();
} else if (layer.getType() == MapLayerInfo.TYPE_RASTER) {
MetadataMap metadata = layer.getResource().getMetadata();
//
// Adding a coverage layer
//
GridCoverage2DReader reader;
try {
reader = (GridCoverage2DReader) layer.getCoverageReader();
} catch (IOException e) {
throw new ServiceException(e);
}
if (reader != null) {
ReaderDimensionsAccessor dimensions = new ReaderDimensionsAccessor(reader);
DimensionInfo elevationInfo = metadata.get(ResourceInfo.ELEVATION, DimensionInfo.class);
hasElevation |= elevationInfo != null && elevationInfo.isEnabled() && dimensions.hasElevation();
DimensionInfo timeInfo = metadata.get(ResourceInfo.TIME, DimensionInfo.class);
hasTime |= timeInfo != null && timeInfo.isEnabled() && dimensions.hasTime();
}
}
}
// force in the default if nothing was requested
if(hasTime && (getMap.getTime() == null || getMap.getTime().isEmpty())) {
// ask for "CURRENT"
getMap.setTime(Arrays.asList((Object) null));
}
if(hasElevation && (getMap.getElevation() == null || getMap.getElevation().isEmpty())) {
// ask for "DEFAULT"
getMap.setElevation(Arrays.asList((Object) null));
}
// check that we don't have double dimensions listing
if((getMap.getElevation() != null && getMap.getElevation().size() > 1) &&
(getMap.getTime() != null && getMap.getTime().size() > 1)) {
throw new ServiceException("TIME and ELEVATION values cannot be both multivalued");
}
return getMap;
}
private List<Interpolation> parseInterpolations(List<Object> requestedLayers,
List<String> interpolationList) {
List<Interpolation> interpolations = new ArrayList<Interpolation>();
for (int i = 0; i < requestedLayers.size(); i++) {
// null interpolation means:
// use the default WMS one
Interpolation interpolation = null;
if(i < interpolationList.size()) {
String interpolationName = interpolationList.get(i);
if(!interpolationName.trim().equals("")) {
interpolation = getInterpolationObject(interpolationName);
}
}
Object o = requestedLayers.get(i);
if (o instanceof LayerInfo) {
interpolations.add(interpolation);
} else if (o instanceof LayerGroupInfo) {
List<LayerInfo> subLayers = ((LayerGroupInfo) o).layers();
for (LayerInfo layer : subLayers) {
interpolations.add(interpolation);
}
} else {
throw new IllegalArgumentException("Unknown layer info type: " + o);
}
}
return interpolations;
}
private Interpolation getInterpolationObject(String interpolation) {
return Interpolation.getInstance(interpolationMethods.get(interpolation
.toUpperCase()));
}
private Style getDefaultStyle (LayerInfo layer) throws IOException{
if (layer.getResource() instanceof WMSLayerInfo) {
// NamedStyle is a subclass of Style -> we use it as a way to convey
// cascaded WMS layer styles
NamedStyle namedStyle = CommonFactoryFinder.getStyleFactory(null)
.createNamedStyle();
namedStyle.setName(null);
return namedStyle;
} else {
StyleInfo defaultStyle = layer.getDefaultStyle();
return defaultStyle.getStyle();
}
}
Filter getFilter(List<Filter> filters, int index) {
if (filters.size() == 1 && filters.get(0) instanceof Id) {
// feature id filters must be expanded to all layers
return filters.get(0);
} else if (index < filters.size()) {
return filters.get(index);
} else {
throw new ServiceException("Layers and filters are mismatched, "
+ "you need to provide one filter for each layer");
}
}
/**
* Checks the various options, OGC filter, fid filter, CQL filter, and returns a list of parsed
* filters
*
* @param getMap
* @param rawFilters
* @param cqlFilters
* @return the list of parsed filters, or null if none was found
*/
private List<Filter> parseFilters(GetMapRequest getMap, List<Filter> rawFilters, List<Filter> cqlFilters) {
List<Filter> filters = rawFilters;
List featureId = (getMap.getFeatureId() != null) ? getMap.getFeatureId()
: Collections.EMPTY_LIST;
if (!featureId.isEmpty()) {
if (!filters.isEmpty()) {
throw new ServiceException("GetMap KVP request contained "
+ "conflicting filters. Filter: " + rawFilters + ", fid: " + featureId);
}
Set ids = new HashSet();
for (Iterator i = featureId.iterator(); i.hasNext();) {
ids.add(filterFactory.featureId((String) i.next()));
}
filters = Collections.singletonList((Filter) filterFactory.id(ids));
}
if (!cqlFilters.isEmpty()) {
if (!filters.isEmpty()) {
throw new ServiceException("GetMap KVP request contained "
+ "conflicting filters. Filter: " + rawFilters + ", fid: " + featureId
+ ", cql: " + cqlFilters);
}
filters = cqlFilters;
}
// return null in case we found no filters
if (filters.size() == 0) {
filters = null;
}
return filters;
}
/**
* validates an style document.
*
*/
private List validateStyle(InputStream stream, GetMapRequest getMap) {
try {
String language = getStyleFormat(getMap);
EntityResolver entityResolver = entityResolverProvider.getEntityResolver();
return Styles.handler(language).validate(stream, getMap.styleVersion(), entityResolver);
}
catch (IOException e) {
throw new ServiceException("Error validating style", e);
}
}
/**
* Parses an style document.
*/
private StyledLayerDescriptor parseStyle(GetMapRequest getMap, InputStream stream) {
try {
String format = getStyleFormat(getMap);
EntityResolver entityResolver = entityResolverProvider.getEntityResolver();
return Styles.handler(format).parse(stream, getMap.styleVersion(), null, entityResolver);
}
catch(IOException e) {
throw new ServiceException("Error parsing style", e);
}
}
/*
* Get style language from request, falling back on SLD as default.
*/
private String getStyleFormat(GetMapRequest request) {
return request.getStyleFormat() != null ? request.getStyleFormat() : SLDHandler.FORMAT;
}
private void processSld(final GetMapRequest request, final List<?> requestedLayers,
final StyledLayerDescriptor sld, final List styleNames) throws ServiceException,
IOException {
if (requestedLayers.size() == 0) {
processStandaloneSld(wms, request, sld);
} else {
processLibrarySld(request, sld, requestedLayers, styleNames);
}
}
/**
* Looks in <code>sld</code> for the layers and styles to use in the map composition and sets
* them to the <code>request</code>
*
* <p>
* This method processes SLD in library mode Library mode engages when "SLD" or "SLD_BODY" are
* used in conjuction with LAYERS and STYLES. From the spec: <br>
* <cite> When an SLD is used as a style library, the STYLES CGI parameter is interpreted in the
* usual way in the GetMap request, except that the handling of the style names is organized so
* that the styles defined in the SLD take precedence over the named styles stored within the
* map server. The user-defined SLD styles can be given names and they can be marked as being
* the default style for a layer. To be more specific, if a style named �CenterLine� is
* referenced for a layer and a style with that name is defined for the corresponding layer in
* the SLD, then the SLD style definition is used. Otherwise, the standard named-style mechanism
* built into the map server is used. If the use of a default style is specified and a style is
* marked as being the default for the corresponding layer in the SLD, then the default style
* from the SLD is used; otherwise, the standard default style in the map server is used.
* </cite>
*
* @param request
* the GetMap request to which to set the layers and styles
* @param sld
* a SLD document to take layers and styles from, following the "literal" or
* "library" rule.
* @param requestedLayers
* the list of {@link LayerInfo} and {@link LayerGroupInfo} as requested by the
* LAYERS param
* @param styleNames
* the list of requested style names
*/
private void processLibrarySld(final GetMapRequest request, final StyledLayerDescriptor sld,
final List<?> requestedLayers, List<String> styleNames) throws ServiceException,
IOException {
final StyledLayer[] styledLayers = sld.getStyledLayers();
final int slCount = styledLayers.length;
if (slCount == 0) {
throw new ServiceException("SLD document contains no layers");
}
final List<MapLayerInfo> layers = new ArrayList<MapLayerInfo>();
final List<Style> styles = new ArrayList<Style>();
MapLayerInfo currLayer = null;
String styleName = null;
for (int i = 0; i < requestedLayers.size(); i++) {
if (styleNames != null && styleNames.size() > 0) {
styleName = styleNames.get(i);
}
Object o = requestedLayers.get(i);
if (o instanceof LayerInfo) {
currLayer = new MapLayerInfo((LayerInfo) o);
if (styledLayers[i] instanceof NamedLayer) {
NamedLayer namedLayer = ((NamedLayer) styledLayers[i]);
currLayer.setLayerFeatureConstraints(namedLayer.getLayerFeatureConstraints());
}
layers.add(currLayer);
Style style = findStyleOf(request, currLayer, styleName, styledLayers);
styles.add(style);
} else if (o instanceof LayerGroupInfo) {
List<LayerInfo> subLayers = ((LayerGroupInfo) o).layers();
for (LayerInfo layer : subLayers) {
currLayer = new MapLayerInfo(layer);
layers.add(currLayer);
Style style = findStyleOf(request, currLayer, styleName, styledLayers);
styles.add(style);
}
} else {
throw new IllegalArgumentException("Unknown layer info type: " + o);
}
}
request.setLayers(layers);
request.setStyles(styles);
}
/**
* This one processes an SLD in non library mode, that is, it assumes it's the definition of the
* map
*
* @param request
* @param sld
* @throws IOException
*/
public static void processStandaloneSld(final WMS wms, final GetMapRequest request,
final StyledLayerDescriptor sld) throws IOException {
final StyledLayer[] styledLayers = sld.getStyledLayers();
final int slCount = styledLayers.length;
if (slCount == 0) {
throw new ServiceException("SLD document contains no layers");
}
final List<MapLayerInfo> layers = new ArrayList<MapLayerInfo>();
final List<Style> styles = new ArrayList<Style>();
MapLayerInfo currLayer = null;
Style currStyle = null;
String layerName;
UserLayer ul;
for (StyledLayer sl : styledLayers) {
layerName = sl.getName();
if (null == layerName) {
throw new ServiceException("A UserLayer without layer name was passed");
}
if (sl instanceof UserLayer && ((((UserLayer) sl)).getRemoteOWS() != null)) {
// this beast can define multiple feature sources and multiple styles, we'll
// have to mix and match them (ugh)
ul = ((UserLayer) sl);
try {
addRemoteLayersFromUserLayer(request, ul, layers, styles);
} catch (IOException e) {
throw new ServiceException("Error accessing remote layers", e,
"RemoteAccessFailed");
}
} else {
// simpler case, one layer, eventually multiple styles
currLayer = null;
// handle the InLineFeature stuff
if ((sl instanceof UserLayer)
&& ((((UserLayer) sl)).getInlineFeatureDatastore() != null)) {
// SPECIAL CASE - we make the temporary version
ul = ((UserLayer) sl);
try {
currLayer = initializeInlineFeatureLayer(request, ul);
} catch (Exception e) {
throw new ServiceException(e);
}
} else {
if (wms.getLayerGroupByName(layerName) != null) {
LayerGroupInfo group = wms.getLayerGroupByName(layerName);
List<LayerInfo> groupLayers = group.layers();
List<StyleInfo> groupStyles = group.styles();
for (int i = 0; i < groupLayers.size(); i++) {
LayerInfo layer = groupLayers.get(i);
layers.add(new MapLayerInfo(layer));
StyleInfo style = groupStyles.get(i);
if (style != null) {
styles.add(style.getStyle());
} else {
styles.add(layer.getDefaultStyle().getStyle());
}
}
// move to the next named layer
continue;
} else {
LayerInfo layerInfo = wms.getLayerByName(layerName);
if (layerInfo == null)
throw new ServiceException("Unknown layer: " + layerName);
currLayer = new MapLayerInfo(layerInfo);
if (sl instanceof NamedLayer) {
NamedLayer namedLayer = ((NamedLayer) sl);
currLayer.setLayerFeatureConstraints(namedLayer
.getLayerFeatureConstraints());
}
}
}
if (currLayer.getType() == MapLayerInfo.TYPE_RASTER) {
try {
addStyles(wms, request, currLayer, sl, layers, styles);
} catch (ServiceException wm) {
// hmm, well, the style they specified in the wms
// request
// wasn't found. Let's try the default raster style
// named 'raster'
currStyle = findStyle(wms, request, "raster");
if (currStyle == null) {
// nope, no default raster style either. Give up.
throw new ServiceException(wm.getMessage() + " Also tried to use "
+ "the generic raster style 'raster', but it wasn't available.");
}
layers.add(currLayer);
styles.add(currStyle);
}
} else {
addStyles(wms, request, currLayer, sl, layers, styles);
}
}
}
request.setLayers(layers);
request.setStyles(styles);
}
private static void addRemoteLayersFromUserLayer(GetMapRequest request, UserLayer ul,
List layers, List styles) throws ServiceException, IOException {
RemoteOWS service = ul.getRemoteOWS();
if (!service.getService().equalsIgnoreCase("WFS"))
throw new ServiceException("GeoServer only supports WFS as remoteOWS service");
if (service.getOnlineResource() == null)
throw new ServiceException("OnlineResource for remote WFS not specified in SLD");
final FeatureTypeConstraint[] featureConstraints = ul.getLayerFeatureConstraints();
if (featureConstraints == null || featureConstraints.length == 0)
throw new ServiceException(
"No FeatureTypeConstraint specified, no layer can be loaded for this UserStyle");
DataStore remoteWFS = null;
List remoteTypeNames = null;
try {
URL url = new URL(service.getOnlineResource());
remoteWFS = connectRemoteWFS(url);
remoteTypeNames = new ArrayList(Arrays.asList(remoteWFS.getTypeNames()));
Collections.sort(remoteTypeNames);
} catch (MalformedURLException e) {
throw new ServiceException("Invalid online resource url: '"
+ service.getOnlineResource() + "'");
}
Style[] layerStyles = ul.getUserStyles();
if (request.getFilter() == null)
request.setFilter(new ArrayList());
for (int i = 0; i < featureConstraints.length; i++) {
// make sure the layer is there
String name = featureConstraints[i].getFeatureTypeName();
if (Collections.binarySearch(remoteTypeNames, name) < 0) {
throw new ServiceException("Could not find layer feature type '" + name
+ "' on remote WFS '" + service.getOnlineResource());
}
// grab the filter
Filter filter = featureConstraints[i].getFilter();
if (filter == null)
filter = Filter.INCLUDE;
// connect the layer
SimpleFeatureSource fs = remoteWFS.getFeatureSource(name);
// this is messy, why the spec allows for multiple constraints and multiple
// styles is beyond me... we'll style each remote layer with all possible
// styles, feauture type style matching will do the rest during rendering
for (int j = 0; j < layerStyles.length; j++) {
Style style = layerStyles[i];
MapLayerInfo info = new MapLayerInfo(fs);
layers.add(info);
styles.add(style);
// treat it like an externally provided filter... ugly I know, but
// the sane thing (adding a filter as a MapLayerInfo field) would
// break havoc in GetFeatureInfo
request.getFilter().add(filter);
}
}
}
/**
* the correct thing to do its grab the style from styledLayers[i] inside the styledLayers[i]
* will either be : a) nothing - in which case grab the layer's default style b) a set of: i)
* NameStyle -- grab it from the pre-loaded styles ii)UserStyle -- grab it from the sld the user
* uploaded
*
* NOTE: we're going to get a set of layer->style pairs for (b). these are added to
* layers,styles
*
* NOTE: we also handle some featuretypeconstraints
*
* @param request
* @param currLayer
* @param layer
* @param layers
* @param styles
* @throws IOException
*/
public static void addStyles(WMS wms, GetMapRequest request, MapLayerInfo currLayer,
StyledLayer layer, List layers, List styles) throws ServiceException, IOException {
if (currLayer == null) {
return; // protection
}
Style[] layerStyles = null;
FeatureTypeConstraint[] ftcs = null;
if (layer instanceof NamedLayer) {
ftcs = ((NamedLayer) layer).getLayerFeatureConstraints();
layerStyles = ((NamedLayer) layer).getStyles();
} else if (layer instanceof UserLayer) {
ftcs = ((UserLayer) layer).getLayerFeatureConstraints();
layerStyles = ((UserLayer) layer).getUserStyles();
}
// DJB: TODO: this needs to do the whole thing, not just names
if (ftcs != null) {
FeatureTypeConstraint ftc;
final int length = ftcs.length;
for (int t = 0; t < length; t++) {
ftc = ftcs[t];
if (ftc.getFeatureTypeName() != null) {
String ftc_name = ftc.getFeatureTypeName();
// taken from lite renderer
boolean matches;
try {
final FeatureType featureType = currLayer.getFeature().getFeatureType();
matches = FeatureTypes.isDecendedFrom(featureType, null, ftc_name)
|| featureType.getName().getLocalPart().equalsIgnoreCase(ftc_name);
} catch (Exception e) {
matches = false; // bad news
}
if (!matches) {
continue; // this layer is fitered out
}
}
}
}
// handle no styles -- use default
if ((layerStyles == null) || (layerStyles.length == 0)) {
layers.add(currLayer);
styles.add(currLayer.getDefaultStyle());
return;
}
final int length = layerStyles.length;
Style s;
for (int t = 0; t < length; t++) {
if (layerStyles[t] instanceof NamedStyle) {
layers.add(currLayer);
s = findStyle(wms, request, ((NamedStyle) layerStyles[t]).getName());
if (s == null) {
throw new ServiceException("couldnt find style named '"
+ ((NamedStyle) layerStyles[t]).getName() + "'");
}
styles.add(s);
} else {
layers.add(currLayer);
styles.add(layerStyles[t]);
}
}
}
/**
* @param request
* @param currStyleName
*
* @return the configured style named <code>currStyleName</code> or <code>null</code> if such a
* style does not exist on this server.
* @throws IOException
*/
private static Style findStyle(final WMS wms, GetMapRequest request, String currStyleName)
throws IOException {
// Style currStyle;
// Map configuredStyles = request.getWMS().getData().getStyles();
//
// currStyle = (Style) configuredStyles.get(currStyleName);
//
// return currStyle;
return wms.getStyleByName(currStyleName);
}
/**
* Finds the style for <code>layer</code> in <code>styledLayers</code> or the layer's default
* style if <code>styledLayers</code> has no a UserLayer or a NamedLayer with the same name than
* <code>layer</code>
* <p>
* This method is used to parse the style of a layer for SLD and SLD_BODY parameters, both in
* library and literal mode. Thus, once the declared style for the given layer is found, it is
* checked for validity of appliance for that layer (i.e., whether the featuretype contains the
* attributes needed for executing the style filters).
* </p>
*
* @param request
* used to find out an internally configured style when referenced by name by a
* NamedLayer
*
* @param layer
* one of the layers that was requested through the LAYERS parameter or through and
* SLD document when the request is in literal mode.
* @param styledLayers
* a set of StyledLayers from where to find the SLD layer with the same name as
* <code>layer</code> and extract the style to apply.
*
* @return the Style applicable to <code>layer</code> extracted from <code>styledLayers</code>.
*
* @throws RuntimeException
* if one of the StyledLayers is neither a UserLayer nor a NamedLayer. This
* shuoldn't happen, since the only allowed subinterfaces of StyledLayer are
* NamedLayer and UserLayer.
* @throws ServiceException
* @throws IOException
*/
private Style findStyleOf(GetMapRequest request, MapLayerInfo layer, String styleName,
StyledLayer[] styledLayers) throws ServiceException, IOException {
Style style = null;
String layerName = layer.getName();
StyledLayer sl;
for (int i = 0; i < styledLayers.length; i++) {
sl = styledLayers[i];
if (layerName.equals(sl.getName())) {
if (sl instanceof UserLayer) {
Style[] styles = ((UserLayer) sl).getUserStyles();
// if the style name has not been specified, look it up
// the default style, otherwise lookup the one requested
for (int j = 0; style == null && styles != null && j < styles.length; j++) {
if (styleName == null || styleName.equals("") && styles[j].isDefault())
style = styles[j];
else if (styleName != null && styleName.equals(styles[j].getName()))
style = styles[j];
}
} else if (sl instanceof NamedLayer) {
Style[] styles = ((NamedLayer) sl).getStyles();
// if the style name has not been specified, look it up
// the default style, otherwise lookup the one requested
for (int j = 0; style == null && styles != null && j < styles.length; j++) {
if ((styleName == null || styleName.equals("")) && styles[j].isDefault())
style = styles[j];
else if (styleName != null && styleName.equals(styles[j].getName()))
style = styles[j];
}
if (style instanceof NamedStyle) {
style = findStyle(wms, request, style.getName());
}
} else {
throw new RuntimeException("Unknown layer type: " + sl);
}
break;
}
}
// fallback on the old GeoServer behaviour, if the style is not found find
// the first style that matches the type name
// TODO: would be nice to have a switch to turn this off since it's out of the spec
if (style == null && laxStyleMatchAllowed) {
for (int i = 0; i < styledLayers.length; i++) {
sl = styledLayers[i];
if (layerName.equals(sl.getName())) {
if (sl instanceof UserLayer) {
Style[] styles = ((UserLayer) sl).getUserStyles();
if ((null != styles) && (0 < styles.length)) {
style = styles[0];
}
} else if (sl instanceof NamedLayer) {
Style[] styles = ((NamedLayer) sl).getStyles();
if ((null != styles) && (0 < styles.length)) {
style = styles[0];
}
if (style instanceof NamedStyle) {
style = findStyle(wms, request, style.getName());
}
} else {
throw new RuntimeException("Unknown layer type: " + sl);
}
break;
}
}
}
// still not found? Fall back on the server default ones
if (style == null) {
if (styleName == null || "".equals(styleName)) {
style = layer.getDefaultStyle();
if (style == null)
throw new ServiceException("Could not find a default style for "
+ layer.getName());
} else {
style = wms.getStyleByName(styleName);
if (style == null) {
String msg = "No such style: " + styleName;
throw new ServiceException(msg, "StyleNotDefined");
}
}
}
checkStyle(style, layer);
return style;
}
/**
* Checks to make sure that the style passed in can process the FeatureType.
*
* @param style
* The style to check
* @param mapLayerInfo
* The source requested.
*
* @throws ServiceException
*/
private static void checkStyle(Style style, MapLayerInfo mapLayerInfo) throws ServiceException {
if (mapLayerInfo.getType() == mapLayerInfo.TYPE_RASTER) {
// REVISIT: hey, don't we have to check it for rasters now that we support raster
// symbolizer?
return;
}
// if a rendering transform is present don't check the attributes, since they may be changed
if (hasTransformation(style))
return;
// extract attributes used in the style
StyleAttributeExtractor sae = new StyleAttributeExtractor();
sae.visit(style);
Set<PropertyName> styleAttributes = sae.getAttributes();
// see if we can collect any attribute out of the provided layer
// Set attributes = new HashSet();
FeatureType type = null;
if (mapLayerInfo.getType() == MapLayerInfo.TYPE_VECTOR
|| mapLayerInfo.getType() == MapLayerInfo.TYPE_REMOTE_VECTOR) {
try {
if (mapLayerInfo.getType() == MapLayerInfo.TYPE_VECTOR)
type = mapLayerInfo.getFeature().getFeatureType();
else
type = mapLayerInfo.getRemoteFeatureSource().getSchema();
} catch (IOException ioe) {
throw new RuntimeException("Error getting FeatureType, this should never happen!",
ioe);
}
}
// check all attributes required by the style are available
for (PropertyName attName : styleAttributes) {
if ( attName.evaluate(type) == null ) {
throw new ServiceException(
"The requested Style can not be used with this layer. The style specifies "
+ "an attribute of " + attName + " and the layer is: "
+ mapLayerInfo.getName());
}
}
}
/**
* Tests whether a style contains a Rendering Transformation.
*
* @param style the style to check
* @return true if the style contains a rendering transformation
*/
private static boolean hasTransformation(Style style)
{
for (FeatureTypeStyle fs : style.featureTypeStyles()) {
if (fs.getTransformation() != null)
return true;
}
return false;
}
/**
* Method to initialize a user layer which contains inline features.
*
* @param httpRequest
* The request
* @param mapLayer
* The map layer.
*
*/
// JD: the reason this method is static is to share logic among the xml
// and kvp reader, ugh...
private static MapLayerInfo initializeInlineFeatureLayer(GetMapRequest getMapRequest,
UserLayer ul) throws Exception {
SimpleFeatureSource featureSource;
// what if they didn't put an "srsName" on their geometry in their
// inlinefeature?
// I guess we should assume they mean their geometry to exist in the
// output SRS of the
// request they're making.
if (ul.getInlineFeatureType().getCoordinateReferenceSystem() == null) {
LOGGER.warning("No CRS set on inline features default geometry. Assuming the requestor has their inlinefeatures in the boundingbox CRS.");
SimpleFeatureType currFt = ul.getInlineFeatureType();
Query q = new Query(currFt.getTypeName(), Filter.INCLUDE);
FeatureReader<SimpleFeatureType, SimpleFeature> ilReader;
DataStore inlineFeatureDatastore = ul.getInlineFeatureDatastore();
ilReader = inlineFeatureDatastore.getFeatureReader(q, Transaction.AUTO_COMMIT);
CoordinateReferenceSystem crs = (getMapRequest.getCrs() == null) ? DefaultGeographicCRS.WGS84
: getMapRequest.getCrs();
String typeName = inlineFeatureDatastore.getTypeNames()[0];
MemoryDataStore reTypedDS = new MemoryDataStore(new ForceCoordinateSystemFeatureReader(
ilReader, crs));
featureSource = reTypedDS.getFeatureSource(typeName);
} else {
DataStore inlineFeatureDatastore = ul.getInlineFeatureDatastore();
String typeName = inlineFeatureDatastore.getTypeNames()[0];
featureSource = inlineFeatureDatastore.getFeatureSource(typeName);
}
return new MapLayerInfo(featureSource);
}
/**
* Returns the list of, possibly mixed, {@link MapLayerInfo} objects of a requested layer is a
* registered {@link LayerInfo} or a remoteOWS one, or {@link LayerGroupInfo} objects for a
* requested layer name that refers to a layer group.
*/
protected List<?> parseLayers(final List<String> requestedLayerNames, final URL remoteOwsUrl,
final String remoteOwsType) throws Exception {
List<Object> layersOrGroups = new ArrayList<Object>();
// Grab remote OWS data store if needed
DataStore remoteWFS = null;
final List<String> remoteTypeNames = new ArrayList<String>();
if ("WFS".equals(remoteOwsType) && remoteOwsUrl != null) {
remoteWFS = connectRemoteWFS(remoteOwsUrl);
remoteTypeNames.addAll(Arrays.asList(remoteWFS.getTypeNames()));
Collections.sort(remoteTypeNames);
}
// //
// Layer lookup requires to:
// * Look into the remote OWS first
// * Look among the local layers
// * expand local grouped layers (flatten them)
// //
for (String layerName : requestedLayerNames) {
// search into the remote WFS if there is any
if (remoteTypeNames.contains(layerName)) {
SimpleFeatureSource remoteSource;
remoteSource = remoteWFS.getFeatureSource(layerName);
if (remoteSource != null) {
layersOrGroups.add(new MapLayerInfo(remoteSource));
continue;
}
}
// not a remote layer, lets look up for a registered one
LayerInfo layerInfo = wms.getLayerByName(layerName);
if (layerInfo != null) {
layersOrGroups.add(layerInfo);
} else {
LayerGroupInfo layerGroup = wms.getLayerGroupByName(layerName);
if (layerGroup == null || LayerGroupInfo.Mode.CONTAINER.equals(layerGroup.getMode())) {
throw new ServiceException("Could not find layer " + layerName,
"LayerNotDefined", "layers");
}
layersOrGroups.add(layerGroup);
}
}
// pre GEOS-2652
// Integer layerType = catalog.getLayerType(layerName);
// if (layerType != null) {
// layers.add(buildMapLayerInfo(layerName));
// } else {
// if(wms.getBaseMapLayers().containsKey(layerName)) {
// layers.add(buildMapLayerInfo(layerName));
// } else {
// ////
// // Search for grouped layers (attention: heavy process)
// ////
// boolean found = false;
// String catalogLayerName = null;
//
// for (Iterator c_keys = catalog.getLayerNames().iterator(); c_keys.hasNext();) {
// catalogLayerName = (String) c_keys.next();
//
// try {
// FeatureTypeInfo ftype = findFeatureLayer(catalogLayerName);
// String wmsPath = ftype.getWmsPath();
//
// if ((wmsPath != null) && wmsPath.matches(".*/" + layerName)) {
// layers.add(buildMapLayerInfo(catalogLayerName));
// found = true;
// }
// } catch (Exception e_1) {
// try {
// CoverageInfo cv = findCoverageLayer(catalogLayerName);
// String wmsPath = cv.getWmsPath();
//
// if ((wmsPath != null) && wmsPath.matches(".*/" + layerName)) {
// layers.add(buildMapLayerInfo(catalogLayerName));
// found = true;
// }
// } catch (Exception e_2) {
// }
// }
// }
// if(!found)
// throw new ServiceException("Could not find layer " + layerName,"LayerNotDefined");
// }
// }
// }
if (layersOrGroups.size() == 0) {
throw new ServiceException("No LAYERS has been requested", getClass().getName());
}
return layersOrGroups;
}
private static DataStore connectRemoteWFS(URL remoteOwsUrl) throws ServiceException {
try {
WFSDataStoreFactory factory = new WFSDataStoreFactory();
Map params = new HashMap(factory.getImplementationHints());
params.put(WFSDataStoreFactory.URL.key, remoteOwsUrl
+ "&request=GetCapabilities&service=WFS");
params.put(WFSDataStoreFactory.TRY_GZIP.key, Boolean.TRUE);
return factory.createDataStore(params);
} catch (Exception e) {
throw new ServiceException("Could not connect to remote OWS", e, "RemoteOWSFailure");
}
}
// pre GEOS-2652:
// private MapLayerInfo buildMapLayerInfo(String layerName) throws Exception {
// MapLayerInfo li = new MapLayerInfo();
//
// FeatureTypeInfo ftype = findFeatureLayer(layerName);
// if (ftype != null) {
// li.setFeature(ftype);
// } else {
// CoverageInfo cv = findCoverageLayer(layerName);
// if (cv != null) {
// li.setCoverage(cv);
// } else {
// if (wms.getBaseMapLayers().containsKey(layerName)) {
// String styleCsl = (String) wms.getBaseMapStyles().get(layerName);
// String layerCsl = (String) wms.getBaseMapLayers().get(layerName);
// MapLayerInfo[] layerArray = (MapLayerInfo[]) parseLayers(KvpUtils
// .readFlat(layerCsl), null, null);
// List styleList = (List) parseStyles(KvpUtils.readFlat(styleCsl));
// li.setBase(layerName, new ArrayList(Arrays.asList(layerArray)), styleList);
// } else {
// throw new ServiceException("Layer " + layerName + " could not be found");
// }
// }
// }
// return li;
// }
// FeatureTypeInfo findFeatureLayer(String layerName) throws ServiceException {
// FeatureTypeInfo ftype = null;
// Integer layerType = catalog.getLayerType(layerName);
//
// if (Data.TYPE_VECTOR != layerType) {
// return null;
// } else {
// ftype = catalog.getFeatureTypeInfo(layerName);
// }
//
// return ftype;
// }
//
// CoverageInfo findCoverageLayer(String layerName) throws ServiceException {
// CoverageInfo cv = null;
// Integer layerType = catalog.getLayerType(layerName);
//
// if (Data.TYPE_RASTER != layerType) {
// return null;
// } else {
// cv = catalog.getCoverageInfo(layerName);
// }
//
// return cv;
// }
protected List<Style> parseStyles(List<String> styleNames) throws Exception {
List<Style> styles = new ArrayList<Style>();
for (String styleName : styleNames) {
if ("".equals(styleName)) {
// return null, this should flag request reader to use default for
// the associated layer
styles.add(null);
} else {
final Style style = wms.getStyleByName(styleName);
if (style == null) {
String msg = "No such style: " + styleName;
throw new ServiceException(msg, "StyleNotDefined");
}
styles.add(style);
}
}
return styles;
}
/**
* This flags allows the kvp reader to go beyond the SLD library mode specification and match
* the first style that can be applied to a given layer. This is for backwards compatibility
*/
public boolean isLaxStyleMatchAllowed() {
return laxStyleMatchAllowed;
}
public void setLaxStyleMatchAllowed(boolean laxStyleMatchAllowed) {
this.laxStyleMatchAllowed = laxStyleMatchAllowed;
}
}