/* (c) 2014 - 2015 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.awt.Color;
import java.awt.geom.Point2D;
import java.awt.image.IndexColorModel;
import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import javax.media.jai.Interpolation;
import org.geoserver.catalog.SLDHandler;
import org.geoserver.ows.util.CaseInsensitiveMap;
import org.geotools.styling.Style;
import org.geotools.util.DateRange;
import org.geotools.util.NumberRange;
import org.geotools.util.Version;
import org.opengis.filter.Filter;
import org.opengis.referencing.crs.CoordinateReferenceSystem;
import com.vividsolutions.jts.geom.Envelope;
/**
* Represents a WMS GetMap request. as a extension to the WMS spec 1.1.
*
* @author Gabriel Roldan
* @author Simone Giannecchini
* @version $Id$
*/
public class GetMapRequest extends WMSRequest implements Cloneable {
static final Color DEFAULT_BG = Color.white;
public static final String SE_XML = "SE_XML";
/** set of mandatory request's parameters */
private MandatoryParameters mandatoryParams = new MandatoryParameters();
/** set of optionals request's parameters */
private OptionalParameters optionalParams = new OptionalParameters();
/** format options */
private Map<String, Object> formatOptions = new CaseInsensitiveMap(new HashMap());
/** SLD replacement */
private Map /* <String,Object> */env = new HashMap();
/** sql view parameters */
private List<Map<String, String>> viewParams = null;
private Map<String, String> httpRequestHeaders;
public GetMapRequest() {
super("GetMap");
}
public Envelope getBbox() {
return this.mandatoryParams.bbox;
}
public java.awt.Color getBgColor() {
return this.optionalParams.bgColor;
}
/**
* DJB: spec says SRS is *required*, so if they dont specify one, we should throw an error
* instead we use "NONE" - which is no-projection. Previous behavior was to the WSG84 lat/long
* (4326)
*
* @return request CRS, or <code>null</code> if not set. TODO: make CRS manditory as for spec
* conformance
*/
public CoordinateReferenceSystem getCrs() {
return this.optionalParams.crs;
}
public String getSRS() {
return this.optionalParams.srs;
}
public String getExceptions() {
return this.optionalParams.exceptions;
}
public String getFormat() {
return this.mandatoryParams.format;
}
/**
* Map of String,Object which contains kvp's which are specific to a particular output format.
*/
public Map getFormatOptions() {
return formatOptions == null ? Collections.EMPTY_MAP : formatOptions;
}
/**
* Map of strings that make up the SLD enviroment for variable substitution
*
*
*/
public Map getEnv() {
return env;
}
/**
* Map of strings that contain the parameter values for SQL views
*
*
*/
public List<Map<String, String>> getViewParams() {
return viewParams;
}
public int getHeight() {
return this.mandatoryParams.height;
}
/**
* @return the non null list of layers, may be empty
*/
public List<MapLayerInfo> getLayers() {
List<MapLayerInfo> layers = mandatoryParams.layers;
return layers;
}
/**
* Gets a list of the styles to be returned by the server.
*
* @return A list of {@link Style}
*/
public List<Style> getStyles() {
return this.mandatoryParams.styles;
}
/**
* Gets a list of the interpolation methods to be returned by the server.
*
* @return A list of {@link Interpolation}
*/
public List<Interpolation> getInterpolations() {
return this.optionalParams.interpolationMethods;
}
/**
* Gets the url specified by the "SLD" parameter.
* <p>
* This parameter is an alias for "STYLE_URL".
* </p>
*/
public URL getSld() {
return getStyleUrl();
}
/**
* Gets the url specified by the "STYLE_URL" parameter.
* <p>
* This parameter is used to point to a remote style via url.
* </p>
*/
public URL getStyleUrl() {
return this.optionalParams.styleUrl;
}
/**
* Gets the string specified the "SLD_BODY" parameter.
* <p>
* This parameter is an alias for "STYLE_BODY".
* </p>
*/
public String getSldBody() {
return getStyleBody();
}
/**
* Gets the String specified by the "STYLE_BODY" parameter.
* <p>
* This parameter is used to directly supply a complete style in the request.
* </p>
*/
public String getStyleBody() {
return this.optionalParams.styleBody;
}
/**
* Gets the string specified by the "SLD_VERSION" parameter.
* <p>
* This parameter is an alias for "STYLE_VERSION".
* </p>
*/
public String getSldVersion() {
return getStyleVersion();
}
/**
* Gets the String specified by the "STYLE_VERSION" parameter.
* <p>
* This parameter is used to supply a version of the style language being specified.
* It only applies when the style is being supplied directly in the request with one
* of the "STYLE_URL", "STYLE_BODY" parameters.
*
* </p>
*/
public String getStyleVersion() {
return this.optionalParams.styleVersion;
}
/**
* Returns {@link #getStyleVersion()} as a Version object, or null if no version is set.
*/
public Version styleVersion() {
return getStyleVersion() != null ? new Version(getStyleVersion()) : null;
}
/**
* Gets the string specified by the "STYLE_FORMAT" parameter.
*/
public String getStyleFormat() {
return this.optionalParams.styleFormat;
}
/**
* Gets the value of the "VALIDATESCHEMA" parameter which controls wether the value of the "SLD
* paramter is schema validated.
*/
public Boolean getValidateSchema() {
return this.optionalParams.validateSLD;
}
/**
* Gets a list of the the filters that will be applied to each layer before rendering
*
* @return -
* @deprecated use {@link #getFilter()}.
*/
public List getFilters() {
return this.optionalParams.filters;
}
/**
* Gets a list of the the filters that will be applied to each layer before rendering
*
* @return A list of {@link Filter}.
*
*/
public List getFilter() {
return this.optionalParams.filters;
}
/**
* Gets a list of the cql filtesr that will be applied to each layer before rendering.
*
* @return A list of {@link Filter}.
*
*/
public List getCQLFilter() {
return this.optionalParams.cqlFilters;
}
/**
* Gets a list of the feature ids that will be used to filter each layer before rendering.
*
* @return A list of {@link String}.
*/
public List getFeatureId() {
return this.optionalParams.featureIds;
}
public boolean isTransparent() {
return this.optionalParams.transparent;
}
/**
* <a href="http://wiki.osgeo.org/index.php/WMS_Tiling_Client_Recommendation">WMS-C
* specification</a> tiling hint
*
*/
public boolean isTiled() {
return this.optionalParams.tiled;
}
public Point2D getTilesOrigin() {
return this.optionalParams.tilesOrigin;
}
public int getBuffer() {
return this.optionalParams.buffer;
}
public IndexColorModel getPalette() {
return this.optionalParams.icm;
}
public int getWidth() {
return this.mandatoryParams.width;
}
/**
* @return The time request parameter. The list may contain {@link Date} or {@link DateRange} objects,
* or null to indicate the default value
*/
public List<Object> getTime() {
return this.optionalParams.time;
}
/**
* Returns the chosen elevations. The list may contain {@link Date} or {@link NumberRange} objects,
* or null to indicate the default value
*
*/
public List<Object> getElevation() {
return this.optionalParams.elevation;
}
/**
* Returs the feature version optional parameter
*
*
*/
public String getFeatureVersion() {
return this.optionalParams.featureVersion;
}
/**
* Returns the remote OWS type
*
*
*/
public String getRemoteOwsType() {
return optionalParams.remoteOwsType;
}
/**
* Returs the remote OWS URL
*
*
*/
public URL getRemoteOwsURL() {
return optionalParams.remoteOwsURL;
}
public void setBbox(Envelope bbox) {
this.mandatoryParams.bbox = bbox;
}
public void setBgColor(java.awt.Color bgColor) {
this.optionalParams.bgColor = bgColor;
}
public void setCrs(CoordinateReferenceSystem crs) {
this.optionalParams.crs = crs;
}
public void setSRS(String srs) {
this.optionalParams.srs = srs;
}
public void setExceptions(String exceptions) {
this.optionalParams.exceptions = exceptions;
}
/**
* Sets the GetMap request value for the FORMAT parameter, which is the MIME type for the kind
* of image required.
*/
public void setFormat(String format) {
this.mandatoryParams.format = format;
}
/**
* Sets the format options.
*
* @param formatOptions
* A map of String,Object
* @see #getFormatOptions()
*/
public void setFormatOptions(Map formatOptions) {
this.formatOptions = formatOptions;
}
/**
* Sets the SLD environment substitution
*
* @param enviroment
*/
public void setEnv(Map enviroment) {
this.env = enviroment;
}
/**
* Sets the SQL views parameters
*
* @param viewParams
*/
public void setViewParams(List<Map<String, String>> viewParams) {
this.viewParams = viewParams;
}
public void setHeight(int height) {
this.mandatoryParams.height = height;
}
public void setHeight(Integer height) {
this.mandatoryParams.height = height.intValue();
}
public void setLayers(List<MapLayerInfo> layers) {
this.mandatoryParams.layers = layers == null ? Collections.EMPTY_LIST : layers;
}
public void setStyles(List<Style> styles) {
this.mandatoryParams.styles = styles == null ? Collections.EMPTY_LIST
: new ArrayList<Style>(styles);
}
/**
* Sets interpolations methods for layers.
*
* @param interpolations
*/
public void setInterpolations(List<Interpolation> interpolations) {
this.optionalParams.interpolationMethods = interpolations == null ? Collections.EMPTY_LIST
: interpolations;
}
/**
* Sets the url specified by the "SLD" parameter.
*/
public void setSld(URL sld) {
setStyleUrl(sld);
}
/**
* Sets the url specified by the "STYLE_URL" parameter.
*/
public void setStyleUrl(URL styleUrl) {
this.optionalParams.styleUrl = styleUrl;
}
/**
* Sets the string specified by the "SLD_BODY" parameter
*/
public void setSldBody(String sldBody) {
setStyleBody(sldBody);
}
/**
* Sets the url specified by the "STYLE_BODY" parameter.
*/
public void setStyleBody(String styleBody) {
this.optionalParams.styleBody = styleBody;
}
/**
* Sets the string specified by the "SLD_VERSION" parameter
*/
public void setSldVersion(String sldVersion) {
setStyleVersion(sldVersion);
}
/**
* Sets the url specified by the "STYLE_VERSION" parameter.
*/
public void setStyleVersion(String styleVersion) {
this.optionalParams.styleVersion = styleVersion;
}
/**
* Sets the string specified by the "STYLE_FORMAT" parameter
*/
public void setStyleFormat(String styleFormat) {
this.optionalParams.styleFormat = styleFormat;
}
/**
* Sets the flag to validate the "SLD" parameter or not. //TODO
*/
public void setValidateSchema(Boolean validateSLD) {
this.optionalParams.validateSLD = validateSLD;
}
/**
* Sets a list of filters, one for each layer
*
* @param filters
* A list of {@link Filter}.
* @deprecated use {@link #setFilter(List)}.
*/
public void setFilters(List filters) {
setFilter(filters);
}
/**
* Sets a list of filters, one for each layer
*
* @param filters
* A list of {@link Filter}.
*/
public void setFilter(List filters) {
this.optionalParams.filters = filters;
}
/**
* Sets a list of filters ( cql ), one for each layer.
*
* @param cqlFilters
* A list of {@link Filter}.
*/
public void setCQLFilter(List cqlFilters) {
this.optionalParams.cqlFilters = cqlFilters;
}
/**
* Sets a list of feature ids, one for each layer.
*
* @param featureIds
* A list of {@link String}.
*/
public void setFeatureId(List featureIds) {
this.optionalParams.featureIds = featureIds;
}
public void setTransparent(boolean transparent) {
this.optionalParams.transparent = transparent;
}
public void setTransparent(Boolean transparent) {
this.optionalParams.transparent = (transparent != null) ? transparent.booleanValue()
: false;
}
public void setBuffer(int buffer) {
this.optionalParams.buffer = buffer;
}
public void setPalette(IndexColorModel icm) {
this.optionalParams.icm = icm;
}
public void setBuffer(Integer buffer) {
this.optionalParams.buffer = (buffer != null) ? buffer.intValue() : 0;
}
public void setTiled(boolean tiled) {
this.optionalParams.tiled = tiled;
}
public void setTiled(Boolean tiled) {
this.optionalParams.tiled = (tiled != null) ? tiled.booleanValue() : false;
}
public void setTilesOrigin(Point2D origin) {
this.optionalParams.tilesOrigin = origin;
}
public void setWidth(int width) {
this.mandatoryParams.width = width;
}
public void setWidth(Integer width) {
this.mandatoryParams.width = width.intValue();
}
/**
* Sets the time request parameter (a list of Date or DateRange objects)
*
*/
public void setTime(List<Object> time) {
this.optionalParams.time = new ArrayList<Object>(time);
}
/**
* Sets the elevation request parameter.
*/
public void setElevation(double elevation) {
this.optionalParams.elevation = new ArrayList<Object>();
this.optionalParams.elevation.add(elevation);
}
/**
* Sets the elevation set as a request parameter.
*/
public void setElevation(List<Object> elevation) {
this.optionalParams.elevation = new ArrayList<Object>(elevation);
}
/**
* Sets the feature version optional param
*
* @param featureVersion
*/
public void setFeatureVersion(String featureVersion) {
this.optionalParams.featureVersion = featureVersion;
}
public void setRemoteOwsType(String remoteOwsType) {
this.optionalParams.remoteOwsType = remoteOwsType;
}
public void setRemoteOwsURL(URL remoteOwsURL) {
this.optionalParams.remoteOwsURL = remoteOwsURL;
}
/**
* Sets the maximum number of features to fetch in this request.
* <p>
* This property only applies if the reqeust is for a vector layer.
* </p>
*/
public void setMaxFeatures(Integer maxFeatures) {
this.optionalParams.maxFeatures = maxFeatures;
}
/**
* The maximum number of features to fetch in this request.
*/
public Integer getMaxFeatures() {
return this.optionalParams.maxFeatures;
}
/**
* Sets the offset or start index at which to start returning features in the request.
* <p>
* It is used in conjunction with {@link #getMaxFeatures()} to page through a feature set. This
* property only applies if the request is for a vector layer.
* </p>
*/
public void setStartIndex(Integer startIndex) {
this.optionalParams.startIndex = startIndex;
}
/**
* The offset or start index at which to start returning features in the request.
*/
public Integer getStartIndex() {
return this.optionalParams.startIndex;
}
public double getAngle() {
return this.optionalParams.angle;
}
/**
* Sets the map rotation
*
* @param rotation
*/
public void setAngle(double rotation) {
this.optionalParams.angle = rotation;
}
public ScaleComputationMethod getScaleMethod() {
return this.optionalParams.scaleMethod;
}
/**
* Sets the scale computation method ({@link ScaleComputationMethod#OGC} by default)
*
* @param rotation
*/
public void setScaleMethod(ScaleComputationMethod scaleMethod) {
this.optionalParams.scaleMethod = scaleMethod;
}
private class MandatoryParameters implements Cloneable {
/** ordered list of requested layers */
List<MapLayerInfo> layers = Collections.emptyList();
/**
* ordered list of requested layers' styles, in a one to one relationship with
* <code>layers</code>
*/
List<Style> styles = Collections.emptyList();
Envelope bbox;
int width;
int height;
String format;
@Override
public Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
private class OptionalParameters implements Cloneable {
/**
* Tells us whether or not we should loop forever in an ani,mated gif
* <p>
* Defaults to true
*
*/
Boolean animatedGIFLoopContinuosly;
/**
* Tells us the delay in ms between two frames of an animated gif.
* <p>
* Default to 1s
*/
Integer animatedGIFDelay;
/**
* the map's background color requested, or the default (white) if not specified
*/
Color bgColor = DEFAULT_BG;
/** from SRS (1.1) or CRS (1.2) param */
CoordinateReferenceSystem crs;
/** EPSG code for the SRS */
String srs;
/** vendor extensions, allows to filter each layer with a user defined filter */
List filters;
/** cql filters */
List cqlFilters;
/** feature id filters */
List featureIds;
String exceptions = SE_XML;
boolean transparent = false;
/**
* Tiling hint, according to the <a
* href="http://wiki.osgeo.org/index.php/WMS_Tiling_Client_Recommendation">WMS-C
* specification</a>
*/
boolean tiled;
/**
* Temporary hack since finding a good tiling origin would require us to compute the bbox on
* the fly TODO: remove this once we cache the real bbox of vector layers
*/
public Point2D tilesOrigin;
/** the rendering buffer, in pixels **/
int buffer;
/** The palette used for rendering, if any */
IndexColorModel icm;
/**
* time parameter, a list since many pattern setup can be possible, see for
* example http://mapserver.gis.umn.edu/docs/howto/wms_time_support/#time-patterns.
* Can contain {@link Date} or {@link DateRange} objects.
*/
List<Object> time = Collections.emptyList();
/** elevation parameter, can also be a list, can contain {@link Double} or {@link NumberRange} */
List<Object> elevation = Collections.emptyList();
/**
* STYLE_URL parameter
*/
URL styleUrl;
/**
* STYLE_BODY parameter
*/
String styleBody;
/**
* STYLE_VERSION parameter
*/
String styleVersion;
/**
* STYLE_FORMAT parameter
*/
String styleFormat = SLDHandler.FORMAT;
/** flag to validate SLD parameter */
Boolean validateSLD = Boolean.FALSE;
/** feature version (for versioned requests) */
String featureVersion;
/** Remote OWS type */
String remoteOwsType;
/** Remote OWS url */
URL remoteOwsURL;
/** paging parameters */
Integer maxFeatures;
Integer startIndex;
/** map rotation */
double angle;
/** scale computation method */
ScaleComputationMethod scaleMethod;
/** by layer interpolation methods **/
List<Interpolation> interpolationMethods = Collections.EMPTY_LIST;
@Override
public Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
/**
* Standard override of toString()
*
* @return a String representation of this request.
*/
public String toString() {
StringBuffer returnString = new StringBuffer("\nGetMap Request");
returnString.append("\n version: " + version);
returnString.append("\n output format: " + mandatoryParams.format);
returnString.append("\n width height: " + mandatoryParams.width + ","
+ mandatoryParams.height);
returnString.append("\n bbox: " + mandatoryParams.bbox);
returnString.append("\n layers: ");
for (Iterator<MapLayerInfo> i = mandatoryParams.layers.iterator(); i.hasNext();) {
returnString.append(i.next().getName());
if (i.hasNext()) {
returnString.append(",");
}
}
returnString.append("\n styles: ");
for (Iterator it = mandatoryParams.styles.iterator(); it.hasNext();) {
Style s = (Style) it.next();
returnString.append(s.getName());
if (it.hasNext()) {
returnString.append(",");
}
}
// returnString.append("\n inside: " + filter.toString());
return returnString.toString();
}
public String getHttpRequestHeader(String headerName) {
return httpRequestHeaders == null ? null : httpRequestHeaders.get(headerName);
}
@SuppressWarnings("unchecked")
public void putHttpRequestHeader(String headerName, String value) {
if (httpRequestHeaders == null) {
httpRequestHeaders = new CaseInsensitiveMap(new HashMap<String, String>());
}
httpRequestHeaders.put(headerName, value);
}
@Override
public Object clone() {
try {
GetMapRequest copy = (GetMapRequest) super.clone();
copy.mandatoryParams = (MandatoryParameters) mandatoryParams.clone();
copy.optionalParams = (OptionalParameters) optionalParams.clone();
return copy;
} catch (CloneNotSupportedException e) {
throw new RuntimeException("Unexpected, could not clone GetMapRequest", e);
}
}
public List<String> getCustomDimension(String dimensionName) {
if (getRawKvp() != null) {
String key = "DIM_" + dimensionName;
String value = getRawKvp().get(key);
if (value != null) {
final ArrayList<String> values = new ArrayList<String>(1);
if (value.indexOf(",") > 0) {
String[] elements = value.split("\\s*,\\s*");
values.addAll(Arrays.asList(elements));
} else {
values.add(value);
}
return values;
}
}
return null;
}
}