0001: //$HeadURL: https://svn.wald.intevation.org/svn/deegree/base/trunk/src/org/deegree/ogcwebservices/wms/operation/GetMap.java $
0002: /*---------------- FILE HEADER ------------------------------------------
0003:
0004: This file is part of deegree.
0005: Copyright (C) 2001-2008 by:
0006: EXSE, Department of Geography, University of Bonn
0007: http://www.giub.uni-bonn.de/deegree/
0008: lat/lon GmbH
0009: http://www.lat-lon.de
0010:
0011: This library is free software; you can redistribute it and/or
0012: modify it under the terms of the GNU Lesser General Public
0013: License as published by the Free Software Foundation; either
0014: version 2.1 of the License, or (at your option) any later version.
0015:
0016: This library is distributed in the hope that it will be useful,
0017: but WITHOUT ANY WARRANTY; without even the implied warranty of
0018: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
0019: Lesser General Public License for more details.
0020:
0021: You should have received a copy of the GNU Lesser General Public
0022: License along with this library; if not, write to the Free Software
0023: Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
0024:
0025: Contact:
0026:
0027: Andreas Poth
0028: lat/lon GmbH
0029: Aennchenstr. 19
0030: 53177 Bonn
0031: Germany
0032: E-Mail: poth@lat-lon.de
0033:
0034: Prof. Dr. Klaus Greve
0035: Department of Geography
0036: University of Bonn
0037: Meckenheimer Allee 166
0038: 53115 Bonn
0039: Germany
0040: E-Mail: greve@giub.uni-bonn.de
0041:
0042:
0043: ---------------------------------------------------------------------------*/
0044: package org.deegree.ogcwebservices.wms.operation;
0045:
0046: import static org.deegree.crs.components.Unit.DEGREE;
0047:
0048: import java.awt.Color;
0049: import java.io.Serializable;
0050: import java.io.UnsupportedEncodingException;
0051: import java.net.MalformedURLException;
0052: import java.net.URL;
0053: import java.net.URLDecoder;
0054: import java.net.URLEncoder;
0055: import java.util.ArrayList;
0056: import java.util.Arrays;
0057: import java.util.HashMap;
0058: import java.util.Iterator;
0059: import java.util.List;
0060: import java.util.Map;
0061: import java.util.StringTokenizer;
0062:
0063: import org.deegree.datatypes.values.Values;
0064: import org.deegree.framework.log.ILogger;
0065: import org.deegree.framework.log.LoggerFactory;
0066: import org.deegree.framework.util.CharsetUtils;
0067: import org.deegree.framework.util.ColorUtils;
0068: import org.deegree.framework.util.IDGenerator;
0069: import org.deegree.framework.util.MimeTypeMapper;
0070: import org.deegree.framework.util.NetWorker;
0071: import org.deegree.framework.util.StringTools;
0072: import org.deegree.framework.xml.Marshallable;
0073: import org.deegree.framework.xml.NamespaceContext;
0074: import org.deegree.framework.xml.XMLFragment;
0075: import org.deegree.framework.xml.XMLParsingException;
0076: import org.deegree.framework.xml.XMLTools;
0077: import org.deegree.graphics.sld.SLDFactory;
0078: import org.deegree.graphics.sld.StyledLayerDescriptor;
0079: import org.deegree.i18n.Messages;
0080: import org.deegree.model.crs.CRSFactory;
0081: import org.deegree.model.crs.CoordinateSystem;
0082: import org.deegree.model.crs.UnknownCRSException;
0083: import org.deegree.model.spatialschema.Envelope;
0084: import org.deegree.model.spatialschema.GMLGeometryAdapter;
0085: import org.deegree.model.spatialschema.GeometryFactory;
0086: import org.deegree.ogcbase.CommonNamespaces;
0087: import org.deegree.ogcbase.InvalidGMLException;
0088: import org.deegree.ogcwebservices.InconsistentRequestException;
0089: import org.deegree.ogcwebservices.OGCWebServiceException;
0090: import org.deegree.ogcwebservices.wmps.operation.PrintMap;
0091: import org.deegree.ogcwebservices.wms.InvalidCRSException;
0092: import org.deegree.ogcwebservices.wms.InvalidFormatException;
0093: import org.deegree.ogcwebservices.wms.InvalidSRSException;
0094: import org.deegree.ogcwebservices.wms.configuration.AbstractDataSource;
0095: import org.deegree.ogcwebservices.wms.configuration.RemoteWMSDataSource;
0096: import org.w3c.dom.Document;
0097: import org.w3c.dom.Element;
0098:
0099: /**
0100: * This interface describes the access to the parameters of a GeMap request. It is excpected that
0101: * there are two kinds of request. The first is the 'normal' HTTP GET request with name-value-pair
0102: * enconding and the second is a HTTP POST request containing a SLD.
0103: * <p>
0104: * </p>
0105: * Even it is possible to access the values of a HTTP GET request throught their bean accessor
0106: * methods the request shall be mapped to a SLD data structure that is accessible using the
0107: * <tt>getSLD()</tt>.
0108: *
0109: * @author <a href="mailto:poth@lat-lon.de">Andreas Poth</a>
0110: * @author last edited by: $Author:wanhoff$
0111: *
0112: * @version $Revision: 10467 $, $Date:20.03.2007$
0113: */
0114: public class GetMap extends WMSRequestBase {
0115:
0116: private static final long serialVersionUID = 887256882709344021L;
0117:
0118: private static final ILogger LOG = LoggerFactory
0119: .getLogger(GetMap.class);
0120:
0121: private Values elevation = null;
0122:
0123: private Values time = null;
0124:
0125: private Map<String, Values> sampleDimension = null;
0126:
0127: private List<Layer> layers = null;
0128:
0129: private Color bGColor = null;
0130:
0131: private Envelope boundingBox = null;
0132:
0133: private String exceptions = null;
0134:
0135: private String format = null;
0136:
0137: private String srs = null;
0138:
0139: private StyledLayerDescriptor sld = null;
0140:
0141: private URL sLD_URL = null;
0142:
0143: private URL wFS_URL = null;
0144:
0145: private boolean transparency = false;
0146:
0147: private int height = 0;
0148:
0149: private int width = 0;
0150:
0151: /**
0152: * creates a <tt>WTSGetViewRequest</tt> from a set of parameters and builds up the complete
0153: * SLD.
0154: *
0155: * @return an instance of <tt>GetMapRequest</tt>
0156: * @param version
0157: * Request version.
0158: * @param layers
0159: * list of one or more map layers. Optional if SLD parameter is present. Contains
0160: * list of one rendering style per requested layer. Optional if SLD parameter is
0161: * present.
0162: * @param elevation
0163: * Elevation of layer desired.
0164: * @param sampleDimension
0165: * Value of other dimensions as appropriate.
0166: * @param format
0167: * Output format of map.
0168: * @param width
0169: * Width in pixels of map picture.
0170: * @param height
0171: * Height in pixels of map picture.
0172: * @param srs
0173: * the requested Spatial Reference System.
0174: * @param boundingBox
0175: * Bounding box corners (lower left, upper right) in SRS units.
0176: * @param transparency
0177: * Background transparency of map.
0178: * @param bGColor
0179: * Hexadecimal red-green-blue color value for the background color.
0180: * @param exceptions
0181: * The format in which exceptions are to be reported by the WMS.
0182: * @param time
0183: * Time value of layer desired
0184: * @param sld
0185: * Styled Layer Descriptor
0186: * @param id
0187: * an unique ID of the request
0188: * @param sldURL
0189: * @param vendorSpecificParameter
0190: * Vendor Specific Parameter
0191: */
0192: public static GetMap create(String version, String id,
0193: Layer[] layers, Values elevation,
0194: Map<String, Values> sampleDimension, String format,
0195: int width, int height, String srs, Envelope boundingBox,
0196: boolean transparency, Color bGColor, String exceptions,
0197: Values time, URL sldURL, StyledLayerDescriptor sld,
0198: Map<String, String> vendorSpecificParameter) {
0199: return new GetMap(version, id, layers, elevation,
0200: sampleDimension, format, width, height, srs,
0201: boundingBox, transparency, bGColor, exceptions, time,
0202: sldURL, sld, vendorSpecificParameter);
0203: }
0204:
0205: /**
0206: * creates a getMap request for requesting a cascaded remote WMS considering the getMap request
0207: * and the filterconditions defined in the submitted <tt>DataSource</tt> object The request
0208: * will be encapsualted within a <tt>OGCWebServiceEvent</tt>.
0209: *
0210: * @param ds
0211: * @param request
0212: * @param style
0213: * @param layer
0214: * @return GetMap request object
0215: */
0216: public static GetMap createGetMapRequest(AbstractDataSource ds,
0217: GetMap request, String style, String layer) {
0218:
0219: GetMap gmr = ((RemoteWMSDataSource) ds).getGetMapRequest();
0220:
0221: String format = request.getFormat();
0222:
0223: if (gmr != null && !"%default%".equals(gmr.getFormat())) {
0224: format = gmr.getFormat();
0225: }
0226:
0227: GetMap.Layer[] lys = null;
0228: lys = new GetMap.Layer[1];
0229:
0230: if (style != null) {
0231: lys[0] = PrintMap.createLayer(layer, style);
0232: } else {
0233: lys[0] = PrintMap.createLayer(layer, "$DEFAULT");
0234: }
0235: if (gmr != null && gmr.getLayers() != null
0236: && !(gmr.getLayers()[0].getName().equals("%default%"))) {
0237: lys = gmr.getLayers();
0238: }
0239: Color bgColor = request.getBGColor();
0240: if (gmr != null && gmr.getBGColor() != null) {
0241: bgColor = gmr.getBGColor();
0242: }
0243: Values time = request.getTime();
0244: if (gmr != null && gmr.getTime() != null) {
0245: time = gmr.getTime();
0246: }
0247:
0248: Map<String, String> vendorSpecificParameter = request
0249: .getVendorSpecificParameters();
0250: if (gmr != null && gmr.getVendorSpecificParameters() != null
0251: && gmr.getVendorSpecificParameters().size() > 0) {
0252: vendorSpecificParameter.putAll(gmr
0253: .getVendorSpecificParameters());
0254: }
0255: String version = "1.1.0";
0256: if (gmr != null && gmr.getVersion() != null) {
0257: version = gmr.getVersion();
0258: }
0259:
0260: Values elevation = request.getElevation();
0261: if (gmr != null && gmr.getElevation() != null) {
0262: elevation = gmr.getElevation();
0263: }
0264: Map<String, Values> sampleDim = null;
0265: if (gmr != null && gmr.getSampleDimension() != null) {
0266: sampleDim = gmr.getSampleDimension();
0267: }
0268:
0269: boolean tranparency = false;
0270: if (gmr != null) {
0271: tranparency = gmr.getTransparency();
0272: }
0273:
0274: // now filter out the unwanted vendor specific parameters and put in
0275: // the wanted additional ones
0276: Map<String, String> vsp = new HashMap<String, String>(10);
0277: vsp.putAll(((RemoteWMSDataSource) ds).getAddedParameters());
0278: for (String name : ((RemoteWMSDataSource) ds)
0279: .getPassedParameters()) {
0280: if (vendorSpecificParameter.containsKey(name)) {
0281: vsp.put(name, vendorSpecificParameter.get(name)
0282: .toString());
0283: }
0284: }
0285:
0286: IDGenerator idg = IDGenerator.getInstance();
0287: gmr = GetMap.create(version, "" + idg.generateUniqueID(), lys,
0288: elevation, sampleDim, format, request.getWidth(),
0289: request.getHeight(), request.getSrs(), request
0290: .getBoundingBox(), tranparency, bgColor,
0291: request.getExceptions(), time, null, null, vsp);
0292:
0293: return gmr;
0294: }
0295:
0296: /**
0297: * creates a <tt>GetMapRequest</tt> from a <tt>HashMap</tt> that contains the request
0298: * parameters as key-value-pairs. Keys are expected to be in upper case notation.
0299: *
0300: * @param model
0301: * <tt>HashMap</tt> containing the request parameters
0302: * @return an instance of <tt>GetMapRequest</tt>
0303: * @throws InconsistentRequestException
0304: * @throws XMLParsingException
0305: * @throws MalformedURLException
0306: */
0307: public static GetMap create(Map<String, String> model)
0308: throws InconsistentRequestException, XMLParsingException,
0309: MalformedURLException {
0310:
0311: LOG.logDebug("Request parameters: " + model);
0312:
0313: // use model.remove(..) so at the end of the method the vendor
0314: // specific parameters remains in the model HashMap
0315: model.remove("REQUEST");
0316:
0317: // Version
0318: String version = model.remove("VERSION");
0319:
0320: if (version == null) {
0321: version = model.remove("WMTVER");
0322: }
0323:
0324: if (version == null) {
0325: throw new InconsistentRequestException(
0326: "VERSION-value must be set");
0327: }
0328:
0329: // LAYERS & STYLES & SLD (URL, XML)
0330: StyledLayerDescriptor sld = null;
0331: String sld_body = model.remove("SLD_BODY");
0332: String sld_urlstring = model.remove("SLD");
0333:
0334: // The SLD is complete in the Maprequest
0335: URL sLD_URL = null;
0336:
0337: if (sld_body != null) {
0338: try {
0339: sld_body = URLDecoder.decode(sld_body, CharsetUtils
0340: .getSystemCharset());
0341: sld = SLDFactory.createSLD(sld_body);
0342: } catch (Exception ee) {
0343: throw new XMLParsingException(
0344: "Could not decode SLD_BODY: " + ee.toString());
0345: }
0346: } else if (sld_urlstring != null) {
0347: // The SLD is as url in the Maprequest
0348: sLD_URL = new URL(sld_urlstring);
0349:
0350: try {
0351: sld = SLDFactory.createSLD(sLD_URL);
0352: } catch (Exception ioex) {
0353: ioex.printStackTrace();
0354: LOG.logError(ioex.getMessage(), ioex);
0355: throw new InconsistentRequestException(
0356: "IOException occured during the access "
0357: + "to the SLD-URL. Wrong URL? Server down?"
0358: + ioex.getMessage());
0359: }
0360: }
0361:
0362: // LAYERS & STYLES
0363: String layersstring = model.remove("LAYERS");
0364: if ((layersstring == null || layersstring.trim().length() == 0)
0365: && (sld == null)) {
0366: throw new InconsistentRequestException(
0367: "At least one layer must be defined within a GetMap request");
0368: }
0369: String stylesstring = model.remove("STYLES");
0370:
0371: // normalize styles parameter
0372: if (stylesstring == null) {
0373: stylesstring = "";
0374: }
0375: if (stylesstring.startsWith(",")) {
0376: stylesstring = "$DEFAULT" + stylesstring;
0377: }
0378:
0379: stylesstring = StringTools.replace(stylesstring, ",,",
0380: ",$DEFAULT,", true);
0381:
0382: if (stylesstring.endsWith(",")) {
0383: stylesstring = stylesstring + "$DEFAULT";
0384: }
0385:
0386: List<String> layers = StringTools.toList(layersstring, ",",
0387: false);
0388:
0389: List<String> styles = null;
0390: if (stylesstring == null || stylesstring.length() == 0) {
0391: styles = new ArrayList<String>(layers.size());
0392: for (int i = 0; i < layers.size(); i++) {
0393: styles.add("$DEFAULT");
0394: }
0395: } else {
0396: styles = StringTools.toList(stylesstring, ",", false);
0397: }
0398:
0399: // At last, build up the Layer object
0400: GetMap.Layer[] ls = new GetMap.Layer[layers.size()];
0401:
0402: if (styles.size() != layers.size()) {
0403: throw new InconsistentRequestException(Messages
0404: .getMessage("WMS_WRONG_STYLES"));
0405: }
0406:
0407: for (int i = 0; i < layers.size(); i++) {
0408: try {
0409: String l = URLDecoder.decode(layers.get(i),
0410: CharsetUtils.getSystemCharset());
0411: ls[i] = GetMap.createLayer(l, styles.get(i));
0412: } catch (UnsupportedEncodingException e2) {
0413: e2.printStackTrace();
0414: }
0415: }
0416:
0417: // ELEVATION
0418: Values elevation = null;
0419: // TODO
0420: // read elevations
0421:
0422: // SAMPLE DIMENSION
0423: Map<String, Values> sampleDimension = null;
0424: // TODO
0425: // read sampleDimensions
0426:
0427: // FORMAT
0428: String format = model.remove("FORMAT");
0429: if (format == null) {
0430: throw new InconsistentRequestException(
0431: "FORMAT-value must be set");
0432: }
0433: try {
0434: format = URLDecoder.decode(format, CharsetUtils
0435: .getSystemCharset());
0436: } catch (UnsupportedEncodingException e1) {
0437: e1.printStackTrace();
0438: }
0439: if (!MimeTypeMapper.isKnownImageType(format)) {
0440: throw new InvalidFormatException(format
0441: + " is not a valid image/result format");
0442: }
0443:
0444: // width
0445: String tmp = model.remove("WIDTH");
0446: if (tmp == null) {
0447: throw new InconsistentRequestException("WIDTH must be set");
0448: }
0449: int width = 0;
0450: try {
0451: width = Integer.parseInt(tmp);
0452: } catch (Exception e) {
0453: throw new InconsistentRequestException(
0454: "WIDTH must be a valid integer number");
0455: }
0456:
0457: // height
0458: tmp = model.remove("HEIGHT");
0459: if (tmp == null) {
0460: throw new InconsistentRequestException("HEIGHT must be set");
0461: }
0462: int height = 0;
0463: try {
0464: height = Integer.parseInt(tmp);
0465: } catch (Exception e) {
0466: throw new InconsistentRequestException(
0467: "HEIGHT must be a valid integer number");
0468: }
0469:
0470: double minx, miny, maxx, maxy;
0471: Envelope boundingBox = null;
0472: String srs;
0473: boolean isLongLat = false;
0474:
0475: boolean is130 = false;
0476:
0477: if ("1.3.0".compareTo(version) <= 0) {
0478: is130 = true;
0479:
0480: // SRS or rather CRS
0481: srs = model.remove("CRS");
0482: if (srs == null) {
0483: throw new InvalidCRSException("CRS-value must be set");
0484: }
0485:
0486: // check for geographic coordinate system
0487: try {
0488: CoordinateSystem crs = CRSFactory.create(srs);
0489: isLongLat = crs.getAxisUnits()[0].equals(DEGREE)
0490: && srs.toLowerCase().startsWith("epsg");
0491: } catch (UnknownCRSException e) {
0492: LOG.logDebug(e.getLocalizedMessage(), e);
0493: throw new InvalidCRSException(srs);
0494: }
0495:
0496: } else {
0497: // SRS
0498: srs = model.remove("SRS");
0499: if (srs == null) {
0500: throw new InvalidSRSException("SRS-value must be set");
0501: }
0502:
0503: try {
0504: // check for crs validity - yes, this method is bad. Is there a better one?
0505: CRSFactory.create(srs);
0506: } catch (UnknownCRSException e) {
0507: LOG.logDebug(e.getLocalizedMessage(), e);
0508: if (is130) {
0509: throw new InvalidCRSException(Messages.getMessage(
0510: "WMS_UNKNOWN_CRS", srs));
0511: }
0512: throw new InvalidSRSException(Messages.getMessage(
0513: "WMS_UNKNOWN_CRS", srs));
0514: }
0515:
0516: }
0517:
0518: // BBOX
0519: String boxstring = model.remove("BBOX");
0520: if (boxstring == null) {
0521: throw new InconsistentRequestException(
0522: "BBOX-value must be set");
0523: }
0524: StringTokenizer st = new StringTokenizer(boxstring, ",");
0525:
0526: if (isLongLat) {
0527: // parse first y, then x
0528: String s = st.nextToken().replace(' ', '+');
0529: miny = Double.parseDouble(s);
0530: s = st.nextToken().replace(' ', '+');
0531: minx = Double.parseDouble(s);
0532: s = st.nextToken().replace(' ', '+');
0533: maxy = Double.parseDouble(s);
0534: s = st.nextToken().replace(' ', '+');
0535: maxx = Double.parseDouble(s);
0536: } else {
0537: // old method
0538: String s = st.nextToken().replace(' ', '+');
0539: minx = Double.parseDouble(s);
0540: s = st.nextToken().replace(' ', '+');
0541: miny = Double.parseDouble(s);
0542: s = st.nextToken().replace(' ', '+');
0543: maxx = Double.parseDouble(s);
0544: s = st.nextToken().replace(' ', '+');
0545: maxy = Double.parseDouble(s);
0546:
0547: }
0548:
0549: // check for consistency
0550: if (minx >= maxx) {
0551: throw new InvalidFormatException(
0552: "minx must be lower than maxx");
0553: }
0554:
0555: if (miny >= maxy) {
0556: throw new InvalidFormatException(
0557: "miny must be lower than maxy");
0558: }
0559:
0560: boundingBox = GeometryFactory.createEnvelope(minx, miny, maxx,
0561: maxy, null);
0562:
0563: // TRANSPARENCY
0564: boolean transparency = false;
0565: String tp = model.remove("TRANSPARENT");
0566: if (tp != null) {
0567: transparency = tp.toUpperCase().trim().equals("TRUE");
0568: }
0569:
0570: String mime = MimeTypeMapper.toMimeType(format);
0571: if (mime.equals("image/jpg") || mime.equals("image/jpeg")
0572: || mime.equals("image/bmp") || mime.equals("image/tif")
0573: || mime.equals("image/tiff")) {
0574: transparency = false;
0575: }
0576:
0577: // BGCOLOR
0578: tmp = model.remove("BGCOLOR");
0579: Color bgColor = Color.white;
0580: if (tmp != null) {
0581: bgColor = Color.decode(tmp);
0582: }
0583:
0584: // EXCEPTIONS
0585: String exceptions = model.remove("EXCEPTIONS");
0586:
0587: if (exceptions == null) {
0588: if (is130) {
0589: exceptions = "XML";
0590: } else {
0591: exceptions = "application/vnd.ogc.se_xml";
0592: }
0593: }
0594:
0595: // TIME
0596: Values time = null;
0597: // TODO read time
0598:
0599: // WFS
0600: /*
0601: * URL wFS_URL = null; if ((String)model.get( "WFS" ) != null) { wFS_URL = new
0602: * URL((String)model.remove( "WFS" )); }
0603: */
0604:
0605: // ID
0606: String id = model.remove("ID");
0607: if (id == null) {
0608: throw new InconsistentRequestException(
0609: "ID-value must be set");
0610: }
0611:
0612: // VendorSpecificParameter; because all defined parameters has been
0613: // removed
0614: // from the model the vendorSpecificParameters are what left
0615: Map<String, String> vendorSpecificParameter = new HashMap<String, String>();
0616: for (Object str : model.keySet()) {
0617: vendorSpecificParameter.put(str.toString(), model.get(str)
0618: .toString());
0619: }
0620:
0621: return new GetMap(version, id, ls, elevation, sampleDimension,
0622: format, width, height, srs, boundingBox, transparency,
0623: bgColor, exceptions, time, sLD_URL, sld,
0624: vendorSpecificParameter);
0625: }
0626:
0627: /**
0628: * creates a <tt>GetMapRequest</tt> from its XML representation as defined in SLD 1.0.0
0629: * specification
0630: *
0631: * <p>
0632: * This method does not yet cope with 1.3.0.
0633: * </p>
0634: *
0635: * @param id
0636: * an unique id of the request
0637: * @param doc
0638: * the document tree
0639: * @return an instance of <tt>GetMapRequest</tt>
0640: * @throws XMLParsingException
0641: * @throws InvalidSRSException
0642: * @throws InconsistentRequestException
0643: */
0644: public static GetMap create(String id, Document doc)
0645: throws XMLParsingException, InvalidSRSException,
0646: InconsistentRequestException {
0647: String PSLD = CommonNamespaces.SLD_PREFIX + ':';
0648:
0649: Element root = doc.getDocumentElement();
0650: NamespaceContext nsContext = CommonNamespaces
0651: .getNamespaceContext();
0652: Element sldElem = (Element) XMLTools.getRequiredNode(root, PSLD
0653: + "StyledLayerDescriptor", nsContext);
0654: XMLFragment xml = new XMLFragment();
0655: xml.setRootElement(sldElem);
0656:
0657: StyledLayerDescriptor sld = SLDFactory.createSLD(xml);
0658: String version = root.getAttribute("version");
0659:
0660: boolean is130 = false;
0661:
0662: if ("1.3.0".compareTo(version) <= 0) {
0663: is130 = true;
0664: }
0665:
0666: Element bboxElem = (Element) XMLTools.getRequiredNode(root,
0667: PSLD + "BoundingBox", nsContext);
0668:
0669: Envelope bbox;
0670: try {
0671: bbox = GMLGeometryAdapter.wrapBox(bboxElem, null);
0672: // check for consistency
0673: if (bbox.getMin().getX() >= bbox.getMax().getX()) {
0674: throw new InvalidFormatException(
0675: "minx must be lower than maxx");
0676: }
0677:
0678: if (bbox.getMin().getY() >= bbox.getMax().getY()) {
0679: throw new InvalidFormatException(
0680: "miny must be lower than maxy");
0681: }
0682: } catch (InvalidGMLException e) {
0683: LOG.logDebug(e.getLocalizedMessage(), e);
0684: if (bboxElem == null) {
0685: throw new InconsistentRequestException(Messages
0686: .getMessage("WMS_NO_BOUNDINGBOX"));
0687: }
0688: if (is130) {
0689: throw new InvalidCRSException(Messages.getMessage(
0690: "WMS_UNKNOWN_CRS", bboxElem
0691: .getAttribute("srsName")));
0692: }
0693: throw new InvalidSRSException(Messages
0694: .getMessage("WMS_UNKNOWN_CRS", bboxElem
0695: .getAttribute("srsName")));
0696: } catch (UnknownCRSException e) {
0697: LOG.logDebug(e.getLocalizedMessage(), e);
0698: if (bboxElem == null) {
0699: throw new InconsistentRequestException(Messages
0700: .getMessage("WMS_NO_BOUNDINGBOX"));
0701: }
0702: if (is130) {
0703: throw new InvalidCRSException(Messages.getMessage(
0704: "WMS_UNKNOWN_CRS", bboxElem
0705: .getAttribute("srsName")));
0706: }
0707: throw new InvalidSRSException(Messages
0708: .getMessage("WMS_UNKNOWN_CRS", bboxElem
0709: .getAttribute("srsName")));
0710: }
0711:
0712: String srs = bbox.getCoordinateSystem().getIdentifier()
0713: .toString();
0714:
0715: Element output = (Element) XMLTools.getRequiredNode(root, PSLD
0716: + "Output", nsContext);
0717:
0718: boolean transparent = XMLTools.getNodeAsBoolean(output, PSLD
0719: + "Transparent", nsContext, false);
0720:
0721: int width = 0;
0722: int height = 0;
0723: try {
0724: Element node = (Element) XMLTools.getRequiredNode(output,
0725: PSLD + "Size", nsContext);
0726: width = XMLTools.getRequiredNodeAsInt(node, PSLD + "Width",
0727: nsContext);
0728: height = XMLTools.getRequiredNodeAsInt(node, PSLD
0729: + "Height", nsContext);
0730: } catch (XMLParsingException e) {
0731: throw new InconsistentRequestException(Messages
0732: .getMessage("WMS_REQUEST_SIZE"));
0733: }
0734:
0735: String exception = XMLTools
0736: .getNodeAsString(output, PSLD + "Exceptions",
0737: nsContext, "application/vnd.ogc.se_xml");
0738: String sbgColor = XMLTools.getNodeAsString(output, PSLD
0739: + "BGColor", nsContext, "#FFFFFF");
0740: Color bgColor = Color.decode(sbgColor);
0741:
0742: String format = XMLTools.getRequiredNodeAsString(output, PSLD
0743: + "Format", nsContext);
0744: if (format == null) {
0745: throw new InconsistentRequestException(
0746: "FORMAT-value must be set");
0747: }
0748: try {
0749: format = URLDecoder.decode(format, CharsetUtils
0750: .getSystemCharset());
0751: } catch (UnsupportedEncodingException e1) {
0752: e1.printStackTrace();
0753: }
0754: if (!MimeTypeMapper.isKnownImageType(format)) {
0755: throw new InvalidFormatException(format
0756: + " is not a valid image/result format");
0757: }
0758:
0759: GetMap req = new GetMap(version, id, null, null, null, format,
0760: width, height, srs, bbox, transparent, bgColor,
0761: exception, null, null, sld, null);
0762:
0763: return req;
0764: }
0765:
0766: /**
0767: * Creates a new GetMapRequest object.
0768: *
0769: * @param version
0770: * @param id
0771: * @param layers
0772: * @param elevation
0773: * @param sampleDimension
0774: * @param format
0775: * @param width
0776: * @param height
0777: * @param srs
0778: * @param boundingBox
0779: * @param transparency
0780: * @param bGColor
0781: * @param exceptions
0782: * @param time
0783: * @param sldURL
0784: * @param sld
0785: * @param vendorSpecificParameter
0786: *
0787: */
0788: GetMap(String version, String id, Layer[] layers, Values elevation,
0789: Map<String, Values> sampleDimension, String format,
0790: int width, int height, String srs, Envelope boundingBox,
0791: boolean transparency, Color bGColor, String exceptions,
0792: Values time, URL sldURL, StyledLayerDescriptor sld,
0793: Map<String, String> vendorSpecificParameter) {
0794: super (version, id, vendorSpecificParameter);
0795:
0796: if (layers != null) {
0797: this .layers = Arrays.asList(layers);
0798: } else {
0799: this .layers = new ArrayList<Layer>();
0800: }
0801: this .sld = sld;
0802: this .elevation = elevation;
0803: this .sampleDimension = sampleDimension;
0804: this .format = format;
0805: this .width = width;
0806: this .height = height;
0807: this .srs = srs;
0808: this .boundingBox = boundingBox;
0809: this .transparency = transparency;
0810: this .bGColor = bGColor;
0811: this .exceptions = exceptions;
0812: this .time = time;
0813: this .sLD_URL = sldURL;
0814: // setWFS_URL( wFS_URL );
0815: }
0816:
0817: /**
0818: * The FORMAT parameter specifies the output format of the response to an operation.
0819: * <p>
0820: * </p>
0821: * An OGC Web CapabilitiesService may offer only a subset of the formats known for that type of
0822: * operation, but the server shall advertise in its Capabilities XML those formats it does
0823: * support and shall accept requests for any format it advertises. A CapabilitiesService
0824: * Instance may optionally offer a new format not previously offered by other instances, with
0825: * the recognition that clients are not required to accept or process an unknown format. If a
0826: * request contains a Format not offered by a particular server, the server shall throw a
0827: * CapabilitiesService Exception (with code "InvalidFormat").
0828: *
0829: * @return the output format
0830: */
0831: public String getFormat() {
0832: return format;
0833: }
0834:
0835: /**
0836: * sets the format
0837: *
0838: * @param format
0839: * the requested output-format
0840: */
0841: public void setFormat(String format) {
0842: this .format = format;
0843: }
0844:
0845: /**
0846: * The required LAYERS parameter lists the map layer(s) to be returned by this GetMap request.
0847: * The value of the LAYERS parameter is a comma-separated list of one or more valid layer names.
0848: * Allowed layer names are the character data content of any <Layer><Name> element in the
0849: * Capabilities XML.
0850: * <p>
0851: * </p>
0852: * A WMS shall render the requested layers by drawing the leftmost in the list bottommost, the
0853: * next one over that, and so on.
0854: * <p>
0855: * </p>
0856: * Each layer is associated to a style. Styles are also is encoded as a comma- seperated list
0857: * within the GetMap request.
0858: * <p>
0859: * </p>
0860: * The required STYLES parameter lists the style in which each layer is to be rendered. There is
0861: * a one-to-one correspondence between the values in the LAYERS parameter and the values in the
0862: * STYLES parameter. Because of this layer-style combinations are returned coupled within an
0863: * array of Layer- objects. Each map in the list of LAYERS is drawn using the corresponding
0864: * style in the same position in the list of STYLES. Each style Name shall be one that was
0865: * defined in the <Name> element of a <Style> element that is either directly contained within,
0866: * or inherited by, the associated <Layer> element in Capabilities XML.
0867: *
0868: * @return The required LAYERS
0869: */
0870: public Layer[] getLayers() {
0871: return layers.toArray(new Layer[layers.size()]);
0872: }
0873:
0874: /**
0875: * adds the <Layer>
0876: *
0877: * @param layers
0878: */
0879: public void addLayers(Layer layers) {
0880: this .layers.add(layers);
0881: }
0882:
0883: /**
0884: * sets the <Layer>
0885: *
0886: * @param layers
0887: * a set of layer
0888: */
0889: public void setLayers(Layer[] layers) {
0890: this .layers.clear();
0891:
0892: if (layers != null) {
0893: for (int i = 0; i < layers.length; i++) {
0894: this .layers.add(layers[i]);
0895: }
0896: }
0897: }
0898:
0899: /**
0900: * The required SRS parameter states which Spatial Reference System applies to the values in the
0901: * BBOX parameter. The value of the SRS parameter shall be one of the values defined in the
0902: * character data section of an <SRS> element defined or inherited by the requested layer. The
0903: * same SRS applies to all layers in a single request.
0904: * <p>
0905: * </p>
0906: * If the WMS server has declared SRS=NONE for a Layer, as discussed in the Basic
0907: * CapabilitiesService Elements section, then the Layer does not have a well-defined spatial
0908: * reference system and should not be shown in conjunction with other layers. The Client shall
0909: * specify SRS=NONE (case-insensitive) in the GetMap request and the Server may issue a
0910: * CapabilitiesService Exception otherwise.
0911: *
0912: * @return the spatial reference system
0913: */
0914: public String getSrs() {
0915: return srs;
0916: }
0917:
0918: /**
0919: * sets the srs
0920: *
0921: * @param srs
0922: * the spatial reference system
0923: */
0924: public void setSrs(String srs) {
0925: this .srs = srs;
0926: }
0927:
0928: /**
0929: * The required BBOX parameter allows a Client to request a particular Bounding Box. Bounding
0930: * Boxes are defined in the Basic CapabilitiesService Elements section. The value of the BBOX
0931: * parameter in a GetMap request is a list of comma-separated numbers of the form
0932: * "minx,miny,maxx,maxy".
0933: * <p>
0934: * </p>
0935: * If the WMS server has declared that a Layer is not subsettable then the Client shall specify
0936: * exactly the declared Bounding Box values in the GetMap request and the Server may issue a
0937: * CapabilitiesService Exception otherwise.
0938: *
0939: * @return the bounding box
0940: */
0941: public Envelope getBoundingBox() {
0942: return boundingBox;
0943: }
0944:
0945: /**
0946: * @see #getBoundingBox()
0947: * @param boundingBox
0948: */
0949: public void setBoundingBox(Envelope boundingBox) {
0950: this .boundingBox = boundingBox;
0951: }
0952:
0953: /**
0954: * WIDTH specifies the number of pixels to be used between the minimum and maximum X values
0955: * (inclusive) in the BBOX parameter. The returned picture, regardless of its return format,
0956: * shall have exactly the specified width and height in pixels. In the case where the aspect
0957: * ratio of the BBOX and the ratio width/height are different, the WMS shall stretch the
0958: * returned map so that the resulting pixels could themselves be rendered in the aspect ratio of
0959: * the BBOX. In other words, it should be possible using this definition to request a map for a
0960: * device whose output pixels are themselves non-square, or to stretch a map into an image area
0961: * of a different aspect ratio.
0962: *
0963: * @return the width
0964: */
0965: public int getWidth() {
0966: return width;
0967: }
0968:
0969: /**
0970: * @see #getWidth()
0971: * @param width
0972: */
0973: public void setWidth(int width) {
0974: this .width = width;
0975: }
0976:
0977: /**
0978: * HEIGHT specifies the number of pixels between the minimum and maximum Y values. The returned
0979: * picture, regardless of its return format, shall have exactly the specified width and height
0980: * in pixels. In the case where the aspect ratio of the BBOX and the ratio width/height are
0981: * different, the WMS shall stretch the returned map so that the resulting pixels could
0982: * themselves be rendered in the aspect ratio of the BBOX. In other words, it should be possible
0983: * using this definition to request a map for a device whose output pixels are themselves
0984: * non-square, or to stretch a map into an image area of a different aspect ratio.
0985: *
0986: * @return the height
0987: */
0988: public int getHeight() {
0989: return height;
0990: }
0991:
0992: /**
0993: * @see #getHeight()
0994: * @param height
0995: */
0996: public void setHeight(int height) {
0997: this .height = height;
0998: }
0999:
1000: /**
1001: * The optional TRANSPARENT parameter specifies whether the map background is to be made
1002: * transparent or not. TRANSPARENT can take on two values, "TRUE" or "FALSE". The default value
1003: * is FALSE if this parameter is absent from the request.
1004: * <p>
1005: * </p>
1006: * The ability to return pictures drawn with transparent pixels allows results of different Map
1007: * requests to be overlaid, producing a composite map. It is strongly recommended that every WMS
1008: * offer a format that provides transparency for layers which could sensibly be overlaid above
1009: * others.
1010: *
1011: * @return the transparency setting
1012: */
1013: public boolean getTransparency() {
1014: return transparency;
1015: }
1016:
1017: /**
1018: * The optional BGCOLOR parameter specifies the color to be used as the background of the map.
1019: * The general format of BGCOLOR is a hexadecimal encoding of an RGB value where two hexadecimal
1020: * characters are used for each of Red, Green, and Blue color values. The values can range
1021: * between 00 and FF for each (0 and 255, base 10). The format is 0xRRGGBB; either upper or
1022: * lower case characters are allowed for RR, GG, and BB values. The "0x" prefix shall have a
1023: * lower case 'x'. The default value is 0xFFFFFF (corresponding to the color white) if this
1024: * parameter is absent from the request.
1025: *
1026: * @return the background color
1027: */
1028: public Color getBGColor() {
1029: return bGColor;
1030: }
1031:
1032: /**
1033: * The optional EXCEPTIONS parameter states the manner in which errors are to be reported to the
1034: * client. The default value is application/vnd.ogc.se_xml if this parameter is absent from the
1035: * request.
1036: * <p>
1037: * </p>
1038: * A Web Map CapabilitiesService shall offer one or more of the following exception reporting
1039: * formats by listing them in separate <Format> elements inside the <Exceptions> element of its
1040: * Capabilities XML. The entire MIME type string in <Format> is used as the value of the
1041: * EXCEPTIONS parameter. The first of these formats is required to be offered by every WMS; the
1042: * others are optional.
1043: *
1044: * @return the exceptions parameter
1045: */
1046: public String getExceptions() {
1047: return exceptions;
1048: }
1049:
1050: /**
1051: * This specification is based on [ISO 8601:1988(E)]; it extends ISO 8601 in the following ways:
1052: * <UL>
1053: * <li>It defines a syntax for expressing the start, end and periodicity of a data collection.
1054: * <li>It defines terms to represent the 7 days of the week.
1055: * <li>It allows years before 0001 AD.
1056: * <li>It allows times in the distant geologic past (thousands, millions or billions of years
1057: * before present).
1058: * </UL>
1059: *
1060: * @return the time setting
1061: */
1062: public Values getTime() {
1063: return time;
1064: }
1065:
1066: /**
1067: * Some geospatial information may be available at multiple elevations. An OWS may announce
1068: * available elevations in its Capabilities XML, and some operations include a parameter for
1069: * requesting a particular elevation. A single elevation value is an integer or real number
1070: * whose units are declared by naming an EPSG datum. When providing elevation information,
1071: * Servers should declare a default value in Capabilities XML unless there is compelling reason
1072: * to behave otherwise, and Servers shall respond with the default value if one has been
1073: * declared and the Client request does not include a value.
1074: *
1075: * @return the elevation
1076: */
1077: public Values getElevation() {
1078: return elevation;
1079: }
1080:
1081: /**
1082: * Some geospatial information may be available at other dimensions (for example, satellite
1083: * images in different wavelength bands). The dimensions other than the four space-time
1084: * dimensions are referred to as "sample dimensions". An OWS may announce available sample
1085: * dimensions in its Capabilities XML, and some operations include a mechanism for including
1086: * dimensional parameters. Each sample dimension has a Name and one or more valid values.
1087: *
1088: * @return the map
1089: */
1090: public Map<String, Values> getSampleDimension() {
1091: return sampleDimension;
1092: }
1093:
1094: /**
1095: * @return the URL of Styled Layer Descriptor (as defined in SLD Specification). This parameter
1096: * is optional. If no sld URL is defined <tt>null</tt> will be returned.
1097: */
1098: public URL getSLD_URL() {
1099: return sLD_URL;
1100: }
1101:
1102: /**
1103: * @return the URL of Web Feature CapabilitiesService providing features to be symbolized using
1104: * SLD. This parameter is optional. If no WFS URL is defined <tt>null</tt> will be
1105: * returned.
1106: */
1107: public URL getWFS_URL() {
1108: return wFS_URL;
1109: }
1110:
1111: /**
1112: * @return the SLD the request is made of. This implies that a 'simple' HTTP GET-Request will be
1113: * transformed into a valid SLD. This is mandatory within a JaGo WMS.
1114: * <p>
1115: * </p>
1116: * This mean even if a GetMap request is send using the HTTP GET method, an implementing
1117: * class has to map the request to a SLD data structure.
1118: */
1119: public StyledLayerDescriptor getStyledLayerDescriptor() {
1120: return sld;
1121: }
1122:
1123: /**
1124: * @return the parameter of a HTTP GET request.
1125: *
1126: */
1127: @Override
1128: public String getRequestParameter() throws OGCWebServiceException {
1129:
1130: // indicates if the request parameters are decoded as SLD. deegree won't
1131: // perform SLD requests through HTTP GET
1132: if (boundingBox == null) {
1133: throw new OGCWebServiceException(
1134: "Operations can't be expressed as HTTP GET request ");
1135: }
1136:
1137: StringBuffer sb = new StringBuffer();
1138:
1139: if (getVersion().compareTo("1.0.0") <= 0) {
1140: sb.append("VERSION=").append(getVersion()).append(
1141: "&REQUEST=map");
1142: String f = StringTools.replace(getFormat(), "image/", "",
1143: false);
1144: sb.append("&FORMAT=");
1145: try {
1146: sb.append(URLEncoder.encode(f, CharsetUtils
1147: .getSystemCharset()));
1148: } catch (UnsupportedEncodingException e) {
1149: // system encoding should be supported...
1150: }
1151: } else {
1152: sb.append("&VERSION=").append(getVersion()).append(
1153: "&REQUEST=GetMap");
1154: sb.append("&FORMAT=");
1155: try {
1156: sb.append(URLEncoder.encode(getFormat(), CharsetUtils
1157: .getSystemCharset()));
1158: } catch (UnsupportedEncodingException e) {
1159: // system encoding should be supported...
1160: }
1161: }
1162:
1163: sb.append("&TRANSPARENT=").append(
1164: Boolean.toString(getTransparency()).toUpperCase());
1165: sb.append("&WIDTH=").append(getWidth());
1166: sb.append("&HEIGHT=").append(getHeight());
1167: sb.append("&EXCEPTIONS=").append(getExceptions());
1168: sb.append("&BGCOLOR=").append(
1169: ColorUtils.toHexCode("0x", bGColor));
1170:
1171: if ("1.3.0".compareTo(getVersion()) <= 0) {
1172: sb.append("&BBOX=").append(boundingBox.getMin().getY());
1173: sb.append(',').append(boundingBox.getMin().getX());
1174: sb.append(',').append(boundingBox.getMax().getY());
1175: sb.append(',').append(boundingBox.getMax().getX());
1176: } else {
1177: sb.append("&BBOX=").append(boundingBox.getMin().getX());
1178: sb.append(',').append(boundingBox.getMin().getY());
1179: sb.append(',').append(boundingBox.getMax().getX());
1180: sb.append(',').append(boundingBox.getMax().getY());
1181: }
1182:
1183: Layer[] layers = getLayers();
1184: StringBuffer l = new StringBuffer(500);
1185: StringBuffer s = new StringBuffer(500);
1186:
1187: if (sLD_URL == null) {
1188: for (int i = 0; i < layers.length; i++) {
1189: try {
1190: l.append(URLEncoder.encode(layers[i].getName(),
1191: CharsetUtils.getSystemCharset()));
1192: l.append(',');
1193: if (!layers[i].getStyleName().equals("$DEFAULT")) {
1194: s.append(URLEncoder.encode(layers[i]
1195: .getStyleName(), CharsetUtils
1196: .getSystemCharset()));
1197: }
1198: s.append(',');
1199: } catch (Exception e) {
1200: throw new OGCWebServiceException(e.toString());
1201: }
1202: }
1203:
1204: if (l.length() != 0) {
1205: sb.append("&LAYERS=").append(
1206: l.substring(0, l.length() - 1));
1207: }
1208:
1209: if (s.length() != 0) {
1210: sb.append("&STYLES=").append(
1211: s.substring(0, s.length() - 1));
1212: }
1213: } else if (sLD_URL != null) {
1214: sb.append("&SLD=").append(NetWorker.url2String(sLD_URL));
1215: } else if (sld != null) {
1216: String tmp = ((Marshallable) sld).exportAsXML();
1217: try {
1218: tmp = URLEncoder.encode(tmp, CharsetUtils
1219: .getSystemCharset());
1220: } catch (Exception e) {
1221: throw new OGCWebServiceException(e.toString());
1222: }
1223: sb.append("&SLD_BODY=").append(tmp);
1224: }
1225:
1226: if ("1.3.0".compareTo(getVersion()) <= 0) {
1227: sb.append("&CRS=").append(getSrs());
1228: } else {
1229: sb.append("&SRS=").append(getSrs());
1230: }
1231:
1232: // TODO
1233: // append time, elevation and sampleDimensions
1234:
1235: if (getVendorSpecificParameters() != null) {
1236: Iterator<String> iterator = getVendorSpecificParameters()
1237: .keySet().iterator();
1238: while (iterator.hasNext()) {
1239: String key = iterator.next();
1240: String value = getVendorSpecificParameters().get(key);
1241: try {
1242: value = URLEncoder.encode(value, CharsetUtils
1243: .getSystemCharset());
1244: } catch (UnsupportedEncodingException e) {
1245: // system encoding should be supported...
1246: }
1247: sb.append('&').append(key).append('=').append(value);
1248: }
1249: }
1250:
1251: return sb.toString();
1252: }
1253:
1254: @Override
1255: public String toString() {
1256: String s = "An unknown " + this .getClass().getName()
1257: + " request";
1258: try {
1259: s = getRequestParameter();
1260: } catch (OGCWebServiceException e) {
1261: // in that case, we just don't have the parameters...
1262: }
1263: return s;
1264: }
1265:
1266: /**
1267: * creates a Layer object beacuse of the inner class construct.
1268: *
1269: * @param name
1270: * the name of the layer
1271: * @param style
1272: * the corresponding style of the layer
1273: * @return Layer a layer object constaining name and style
1274: */
1275: public static Layer createLayer(String name, String style) {
1276: return new Layer(name, style);
1277: }
1278:
1279: /**
1280: * A Layer object. It contains the name of the layer and the corresponding style.
1281: *
1282: * @version $Revision: 10467 $
1283: * @author <a href="mailto:poth@lat-lon.de">Andreas Poth</a>
1284: */
1285: public static class Layer implements Serializable {
1286:
1287: private static final long serialVersionUID = -98575941104285931L;
1288:
1289: private String name = null;
1290:
1291: private String styleName = null;
1292:
1293: /**
1294: * constructor initializing the class with the <Layer>
1295: *
1296: * @param name
1297: * @param styleName
1298: */
1299: public Layer(String name, String styleName) {
1300: this .name = name;
1301: this .styleName = styleName;
1302: }
1303:
1304: /**
1305: * @return the <Name>
1306: */
1307: public String getName() {
1308: return name;
1309: }
1310:
1311: /**
1312: * @return the <StyleName>
1313: */
1314: public String getStyleName() {
1315: return styleName;
1316: }
1317:
1318: }
1319:
1320: }
|