/*
* Geotoolkit - An Open Source Java GIS Toolkit
* http://www.geotoolkit.org
*
* (C) 2009-2011, Geomatys
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*/
package org.geotoolkit.wms;
import java.awt.Dimension;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.TimeZone;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.geotoolkit.client.AbstractRequest;
import org.geotoolkit.client.CapabilitiesException;
import org.geotoolkit.security.ClientSecurity;
import org.geotoolkit.util.StringUtilities;
import org.apache.sis.util.logging.Logging;
import org.geotoolkit.wms.xml.AbstractDimension;
import org.geotoolkit.wms.xml.AbstractLayer;
import org.geotoolkit.wms.xml.AbstractWMSCapabilities;
import org.geotoolkit.wms.xml.Style;
import org.opengis.geometry.Envelope;
import org.opengis.referencing.cs.AxisDirection;
import org.opengis.referencing.cs.CoordinateSystem;
import org.opengis.referencing.cs.CoordinateSystemAxis;
/**
* Abstract implementation of {@link GetMapRequest}, which defines the parameters for
* a GetMap request.
*
* @author Johann Sorel (Geomatys)
* @module
*/
public abstract class AbstractGetMap extends AbstractRequest implements GetMapRequest {
private static final SimpleDateFormat ISO_FORMAT = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'");
static {
ISO_FORMAT.setTimeZone(TimeZone.getTimeZone("GMT+0"));
}
/**
* Default logger for all GetMap requests.
*/
protected static final Logger LOGGER = Logging.getLogger("org.geotoolkit.wms");
/**
* The version to use for this webservice request.
*/
protected final String version;
protected final Map<String, String> dims = new HashMap<String, String>();
protected String format = "image/png";
protected String exceptions = null;
protected String[] layers = null;
protected String[] styles = null;
protected Envelope envelope = null;
protected Dimension dimension = null;
protected String sld = null;
protected String sldVersion = null;
protected String sldBody = null;
protected Boolean transparent = null;
protected WebMapClient server = null;
/**
* Defines the server url and the service version for this kind of request.
*
* @param serverURL The server url.
* @param version The version of the request.
*/
protected AbstractGetMap(final String serverURL, final String version, final ClientSecurity security) {
super(serverURL,security,null);
this.version = version;
}
protected AbstractGetMap(final WebMapClient server, final String version, final ClientSecurity security) {
super(server.getURL().toString(),security,null);
this.server = server;
this.version = version;
}
/**
* {@inheritDoc }
* @return
*/
@Override
public String[] getLayers() {
return layers;
}
/**
* {@inheritDoc }
*/
@Override
public void setLayers(final String... layers) {
this.layers = layers;
}
/**
* {@inheritDoc }
*/
@Override
public Envelope getEnvelope() {
return envelope;
}
/**
* {@inheritDoc }
*/
@Override
public void setEnvelope(final Envelope env) {
this.envelope = env;
}
/**
* {@inheritDoc }
*/
@Override
public Dimension getDimension() {
return dimension;
}
/**
* {@inheritDoc }
*/
@Override
public void setDimension(final Dimension dim) {
this.dimension = dim;
}
/**
* {@inheritDoc }
*/
@Override
public String getFormat() {
return format;
}
/**
* {@inheritDoc }
*/
@Override
public void setFormat(final String format) {
this.format = format;
}
/**
* {@inheritDoc }
*/
@Override
public String getExceptions() {
return exceptions;
}
/**
* {@inheritDoc }
*/
@Override
public void setExceptions(final String ex) {
this.exceptions = ex;
}
/**
* {@inheritDoc }
*/
@Override
public String[] getStyles() {
return styles;
}
/**
* {@inheritDoc }
*/
@Override
public void setStyles(final String... styles) {
this.styles = styles;
}
/**
* {@inheritDoc }
*/
@Override
public String getSld() {
return sld;
}
/**
* {@inheritDoc }
*/
@Override
public void setSld(final String sld) {
this.sld = sld;
}
/**
* {@inheritDoc }
*/
@Override
public String getSldBody() {
return sldBody;
}
/**
* {@inheritDoc }
*/
@Override
public void setSldBody(final String sldBody) {
this.sldBody = sldBody;
}
@Override
public String getSldVersion() {
return sldVersion;
}
@Override
public void setSldVersion(final String sldVersion) {
this.sldVersion = sldVersion;
}
/**
* {@inheritDoc }
*/
@Override
public Boolean isTransparent() {
return transparent;
}
/**
* {@inheritDoc }
*/
@Override
public void setTransparent(final Boolean transparent) {
this.transparent = transparent;
}
/**
* {@inheritDoc }
*/
@Override
public Map<String, String> dimensions() {
return dims;
}
@Override
protected void prepareParameters() {
super.prepareParameters();
// Tests if the mandatory parameters are available
if (layers == null || layers.length == 0) {
throw new IllegalArgumentException("LAYERS are not defined");
}
if (version == null) {
throw new IllegalArgumentException("VERSION parameter is not defined");
}
if (format == null) {
throw new IllegalArgumentException("FORMAT parameter is not defined");
}
if (dimension == null) {
throw new IllegalArgumentException("WIDTH or HEIGHT parameter is not defined");
}
if (envelope == null) {
throw new IllegalArgumentException("Envelope is not defined");
}
// Add mandatory parameters
requestParameters.put("SERVICE", "WMS");
requestParameters.put("REQUEST", "GetMap");
requestParameters.put("VERSION", version);
requestParameters.put("FORMAT", format);
requestParameters.put("WIDTH", String.valueOf(dimension.width));
requestParameters.put("HEIGHT", String.valueOf(dimension.height));
requestParameters.put("LAYERS", StringUtilities.toCommaSeparatedValues((Object[])layers));
requestParameters.putAll(toString(envelope));
// Add optional parameters
if (dims != null && !dims.isEmpty()) {
requestParameters.putAll(dims);
}
if (exceptions != null) {
requestParameters.put("EXCEPTIONS", exceptions);
}
if (transparent != null) {
requestParameters.put("TRANSPARENT", Boolean.toString(transparent).toUpperCase());
}
if (sldVersion != null) {
requestParameters.put("SLD_VERSION", sldVersion);
}
// Add one style parameter
String styleParam = "STYLES";
String styleValue = "";
if (sldBody != null) {
styleParam = "SLD_BODY";
styleValue = sldBody;
} else if (sld != null) {
styleParam = "SLD";
styleValue = sld;
} else if (styles != null && styles.length > 0 && styles[0] != null) {
styleValue = StringUtilities.toCommaSeparatedValues((Object[])styles);
} else {
//try to found the default style name in the capabilities
//some server implementation do not like when the style is left empty
if(server != null && layers != null){
try{
final StringBuilder sb = new StringBuilder();
for(int i=0;i<layers.length;i++){
final String ln = layers[i];
if(i!=0){
sb.append(',');
}
final List<? extends Style> styles = WMSUtilities.findStyleCandidates(server, ln);
if(styles != null && !styles.isEmpty()){
final String name = styles.get(0).getName();
final String title = styles.get(0).getTitle();
if(name!=null){
sb.append(name);
}else if(title!=null){
sb.append(title);
}
}
}
styleValue = sb.toString();
}catch(CapabilitiesException ex){
LOGGER.log(Level.FINE, ex.getMessage(),ex);
}
}
}
requestParameters.put(styleParam, styleValue);
}
/**
* Return a map containing BBOX, SRS (or CRS), TIME and ELEVATION parameters.
*
* @param env
* @return
*/
protected abstract Map<String, String> toString(Envelope env);
/**
* Encode other additional parameters, like TIME, ELEVATION or others which will be put
* in a filter, if they are defined in the envelope.
*
* @param env Current Envelope
* @param map map containing GetMap parameters
*/
protected void encodeNDParameters(final Envelope env, final Map<String, String> map) {
final CoordinateSystem cs = env.getCoordinateReferenceSystem().getCoordinateSystem();
for (int i = 0, n = cs.getDimension(); i < n; i++) {
final CoordinateSystemAxis axis = cs.getAxis(i);
final AxisDirection ad = axis.getDirection();
if (ad.equals(AxisDirection.FUTURE) || ad.equals(AxisDirection.PAST)) {
//found a temporal axis
final double minT = env.getMinimum(i);
final double maxT = env.getMaximum(i);
if (Double.isNaN(minT) || Double.isInfinite(minT)) {
if (Double.isNaN(maxT) || Double.isInfinite(maxT)) {
//both null, do nothing
} else {
//only max limit
map.put("TIME", toDateString(maxT));
}
} else if (Double.isNaN(maxT) || Double.isInfinite(maxT)) {
if (Double.isNaN(minT) || Double.isInfinite(minT)) {
//both null, do nothing
} else {
//only min limit
map.put("TIME", toDateString(minT));
}
} else {
//both ok, calculate median
final double median = (minT + maxT) / 2;
map.put("TIME", toDateString(median));
}
} else if (ad.equals(AxisDirection.UP) || ad.equals(AxisDirection.DOWN)) {
//found a vertical axis
final double minV = env.getMinimum(i);
final double maxV = env.getMaximum(i);
if (Double.isNaN(minV) || Double.isInfinite(minV)) {
if (Double.isNaN(maxV) || Double.isInfinite(maxV)) {
//both null, do nothing
} else {
//only max limit
map.put("ELEVATION", String.valueOf(maxV));
}
} else if (Double.isNaN(maxV) || Double.isInfinite(maxV)) {
if (Double.isNaN(minV) || Double.isInfinite(minV)) {
//both null, do nothing
} else {
//only min limit
map.put("ELEVATION", String.valueOf(minV));
}
} else {
//both ok, calculate median
final double median = (minV + maxV) / 2;
map.put("ELEVATION", String.valueOf(median));
}
} else if ((!ad.equals(AxisDirection.EAST)) && (!ad.equals(AxisDirection.WEST)) &&
(!ad.equals(AxisDirection.SOUTH)) && (!ad.equals(AxisDirection.NORTH))) {
/*
* If other dimension is present in requested CRS, check if current layer capabilities
* support this dimension before add CQL filter on request.
*/
if (server != null && layers.length == 1) {
try {
final AbstractWMSCapabilities capa = server.getCapabilities();
final AbstractLayer layer = capa.getLayerFromName(layers[0]);
final List capaDims = layer.getDimension();
boolean dimensionSupported = false;
for (Object capaDim : capaDims) {
if (capaDim instanceof AbstractDimension) {
AbstractDimension absDim = (AbstractDimension) capaDim;
if (absDim.getName().equals(axis.getName().getCode())) {
dimensionSupported = true;
}
}
}
if(dimensionSupported) {
final String newFilter = axis.getName().getCode() +"="+ env.getMedian(i);
if (map.containsKey("cql_filter")) {
final String previousFilter = map.get("cql_filter");
map.put("cql_filter", previousFilter + " AND " + newFilter);
} else {
map.put("cql_filter", newFilter);
}
}
} catch (CapabilitiesException ex) {
// no nothing
}
}
}
}
}
/**
* Encode ELEVATION parameters if defined in the envelope.
*
* @param env Current Envelope
* @param map map containing GetMap parameters
*/
protected void encodeElevation(final Envelope env, final Map<String, String> map) {
//append time and elevation parameter
final CoordinateSystem cs = env.getCoordinateReferenceSystem().getCoordinateSystem();
for (int i = 0, n = cs.getDimension(); i < n; i++) {
final CoordinateSystemAxis axis = cs.getAxis(i);
final AxisDirection ad = axis.getDirection();
if (ad.equals(AxisDirection.UP) || ad.equals(AxisDirection.DOWN)) {
//found a vertical axis
final double minV = env.getMinimum(i);
final double maxV = env.getMaximum(i);
if (Double.isNaN(minV) || Double.isInfinite(minV)) {
if (Double.isNaN(maxV) || Double.isInfinite(maxV)) {
//both null, do nothing
} else {
//only max limit
map.put("ELEVATION", String.valueOf(maxV));
}
} else if (Double.isNaN(maxV) || Double.isInfinite(maxV)) {
if (Double.isNaN(minV) || Double.isInfinite(minV)) {
//both null, do nothing
} else {
//only min limit
map.put("ELEVATION", String.valueOf(minV));
}
} else {
//both ok, calculate median
final double median = (minV + maxV) / 2;
map.put("ELEVATION", String.valueOf(median));
}
}
}
}
/**
* Transform a double representing a Date to a String
*/
private static String toDateString(final double value) {
return ISO_FORMAT.format(new Date((long) value));
}
}