0001: /* Copyright (c) 2001 - 2007 TOPP - www.openplans.org. All rights reserved.
0002: * This code is licensed under the GPL 2.0 license, availible at the root
0003: * application directory.
0004: */
0005: package org.vfny.geoserver.wms.requests;
0006:
0007: import java.awt.Color;
0008: import java.awt.geom.Point2D;
0009: import java.io.IOException;
0010: import java.io.InputStream;
0011: import java.io.Reader;
0012: import java.io.StringReader;
0013: import java.net.HttpURLConnection;
0014: import java.net.MalformedURLException;
0015: import java.net.URL;
0016: import java.net.URLConnection;
0017: import java.util.ArrayList;
0018: import java.util.Collections;
0019: import java.util.Iterator;
0020: import java.util.List;
0021: import java.util.Map;
0022: import java.util.logging.Level;
0023: import java.util.logging.Logger;
0024: import java.util.zip.GZIPInputStream;
0025: import java.util.zip.Inflater;
0026: import java.util.zip.InflaterInputStream;
0027:
0028: import javax.servlet.http.HttpServletRequest;
0029:
0030: import org.geotools.data.DefaultQuery;
0031: import org.geotools.data.FeatureReader;
0032: import org.geotools.data.Query;
0033: import org.geotools.data.Transaction;
0034: import org.geotools.data.crs.ForceCoordinateSystemFeatureReader;
0035: import org.geotools.data.memory.MemoryDataStore;
0036: import org.geotools.factory.CommonFactoryFinder;
0037: import org.geotools.feature.FeatureType;
0038: import org.geotools.referencing.CRS;
0039: import org.geotools.referencing.crs.DefaultGeographicCRS;
0040: import org.geotools.styling.FeatureTypeConstraint;
0041: import org.geotools.styling.NamedLayer;
0042: import org.geotools.styling.NamedStyle;
0043: import org.geotools.styling.SLDParser;
0044: import org.geotools.styling.Style;
0045: import org.geotools.styling.StyleAttributeExtractor;
0046: import org.geotools.styling.StyleFactory;
0047: import org.geotools.styling.StyledLayer;
0048: import org.geotools.styling.StyledLayerDescriptor;
0049: import org.geotools.styling.UserLayer;
0050: import org.opengis.filter.Filter;
0051: import org.opengis.referencing.crs.CoordinateReferenceSystem;
0052: import org.vfny.geoserver.Request;
0053: import org.vfny.geoserver.ServiceException;
0054: import org.vfny.geoserver.config.PaletteManager;
0055: import org.vfny.geoserver.global.CoverageInfo;
0056: import org.vfny.geoserver.global.Data;
0057: import org.vfny.geoserver.global.FeatureTypeInfo;
0058: import org.vfny.geoserver.global.MapLayerInfo;
0059: import org.vfny.geoserver.global.TemporaryFeatureTypeInfo;
0060: import org.vfny.geoserver.global.WMS;
0061: import org.vfny.geoserver.util.SLDValidator;
0062: import org.vfny.geoserver.wms.WmsException;
0063: import org.vfny.geoserver.wms.responses.palette.InverseColorMapOp;
0064: import org.vfny.geoserver.wms.servlets.WMService;
0065: import org.xml.sax.InputSource;
0066:
0067: import com.vividsolutions.jts.geom.Envelope;
0068:
0069: /**
0070: * Builds a GetMapRequest object given by a set of CGI parameters supplied in
0071: * the constructor.
0072: *
0073: * <p>
0074: * Mandatory parameters:
0075: *
0076: * <ul>
0077: * <li> LAYERS layer names, as exposed by the capabilities document, to compose
0078: * a map with, in the order they may appear, being the first layer the one at
0079: * the bottom of the layer stack and the last one the one at the top. </li>
0080: * <li> STYLES list of named styles known by this server and applicable to the
0081: * requested layers. It can be empty or contain exactly as many style names as
0082: * layers was requested, in which case empty strings could be used to denote
0083: * that the default layer style should be used. (exaple:
0084: * <code>LAYERS=buildings,roads,railroads&STYLES=,centerline,</code>. This
0085: * example says create a map with roads layer using its default style, roads
0086: * with "centerline" style, and railroads with its default style. </li>
0087: * <li> BBOX Area of interest for which to contruct the map image, in the
0088: * Coordinate Reference System given by the SRS parameter. </li>
0089: * <li> FORMAT MIME type of the resulting map, must be one of the advertised in
0090: * the capabilities document. </li>
0091: * <li> WIDTH desired map witdth in output units (pixels). UNITS support should
0092: * be added to the spec, and UNITS and DPI parameters added. </li>
0093: * <li> HEIGHT desired map height in output units (pixels). UNITS support should
0094: * be added to the spec, and UNITS and DPI parameters added. </li>
0095: * </ul>
0096: * </p>
0097: *
0098: * <p>
0099: * Optional parameters:
0100: *
0101: * <ul>
0102: * <li> SRS </li>
0103: * <li> TRANSPARENT boolean indicatin wether to create a map with transparent
0104: * background or not (if transparency is supported by the requested output
0105: * format). </li>
0106: * <li> EXCEPTIONS MIME type of the exception report. </li>
0107: * <li> BGCOLOR map background color, in <code>0xRRGGBB</code> format. </li>
0108: * <li> SLD client supplies a URL for a remote SLD document through this
0109: * parameter. This parameter takes precedence over STYLES. If present, replaces
0110: * the LAYERS and STYLES parameters, since they're defined in the remote
0111: * document itself. The document send by this way will be used in "literal" or
0112: * "library" mode, see explanation bellow. </li>
0113: * <li> SLD_BODY client spplies the SLD document itself through this parameter,
0114: * properly encoded for an HTTP query string. This parameter takes precendence
0115: * over STYLES and SLD. If present, replaces the LAYERS and STYLES parameters,
0116: * since they're defined in the inline document itself. The document send by
0117: * this way will be used in "literal" or "library" mode, see explanation bellow.
0118: * </li>
0119: * </ul>
0120: * </p>
0121: *
0122: * <p>
0123: * As defined by the Styled Layer Descriptor specification, version 1.0.0, the
0124: * SLD document supplied by the SLD or SLD_BODY parameter can be used in
0125: * "literal" or "library" mode, depending on whether the <strong>LAYERS=</strong>
0126: * parameter is present.
0127: * </p>
0128: *
0129: * <p>
0130: * Here is the explanation from the spec, section 6.4, page 10: "the SLD can
0131: * also be used in one of two different modes depending on whether the LAYERS
0132: * parameter is present in the request. If it is not present, then all layers
0133: * identified in the SLD document are rendered with all defined styles, which is
0134: * equivalent to the XML-POST method of usage. If the LAYERS parameter is
0135: * present, then only the layers identified by that parameter are rendered and
0136: * the SLD is used as a style library . "
0137: * </p>
0138: *
0139: * @author Gabriel Roldan, Axios Engineering
0140: * @author Simone Giannecchini, GeoSolutions
0141: * @version $Id: GetMapKvpReader.java 7749 2007-11-13 20:52:54Z jdeolive $
0142: *
0143: * @task TODO: parse and respect SRS parameter (needs reprojection support)
0144: * @deprecated
0145: */
0146: public class GetMapKvpReader extends WmsKvpRequestReader {
0147: /** DOCUMENT ME! */
0148: private static final Logger LOGGER = org.geotools.util.logging.Logging
0149: .getLogger("org.vfny.geoserver.requests.readers.wms");
0150:
0151: /** Used to parse SLD documents from SLD and SLD_BODY parameters */
0152: private static final StyleFactory styleFactory = CommonFactoryFinder
0153: .getStyleFactory(null);
0154:
0155: /**
0156: * Indicates wether STYLES parameter must be parsed. Defaults to
0157: * <code>true</code>, but can be set to false, for example, when parsing
0158: * a GetFeatureInfo request, which shares most of the getmap parameter but
0159: * not STYLES.
0160: *
0161: * @task TODO: refactor this so it dont stay _so_ ugly
0162: */
0163: private boolean stylesRequired = true;
0164:
0165: /**
0166: * Creates a new GetMapKvpReader object.
0167: *
0168: * @param kvpPairs
0169: * Key Values pairs of the request
0170: * @param service
0171: * The service handling the request
0172: */
0173: public GetMapKvpReader(Map kvpPairs, WMService service) {
0174: super (kvpPairs, service);
0175: }
0176:
0177: /**
0178: * Sets wether the STYLES parameter must be parsed
0179: *
0180: * @param parseStyles
0181: */
0182: public void setStylesRequired(boolean parseStyles) {
0183: this .stylesRequired = parseStyles;
0184: }
0185:
0186: /**
0187: * DOCUMENT ME!
0188: *
0189: * @return DOCUMENT ME!
0190: */
0191: public boolean isStylesRquired() {
0192: return this .stylesRequired;
0193: }
0194:
0195: /**
0196: * Produces a <code>GetMapRequest</code> instance by parsing the GetMap
0197: * mandatory, optional and custom parameters.
0198: *
0199: * @param httpRequest
0200: * the servlet request who's application object holds the server
0201: * configuration
0202: *
0203: * @return a <code>GetMapRequest</code> completely setted up upon the
0204: * parameters passed to this reader
0205: *
0206: * @throws ServiceException
0207: * DOCUMENT ME!
0208: */
0209: public Request getRequest(HttpServletRequest httpRequest)
0210: throws ServiceException {
0211: GetMapRequest request = new GetMapRequest((WMService) service);
0212: request.setHttpServletRequest(httpRequest);
0213:
0214: String version = getRequestVersion();
0215: request.setVersion(version);
0216:
0217: parseMandatoryParameters(request, true);
0218: parseOptionalParameters(request);
0219:
0220: return request;
0221: }
0222:
0223: /**
0224: * Parses the optional parameters:
0225: *
0226: * <ul>
0227: * <li> SRS </li>
0228: * <li> TRANSPARENT </li>
0229: * <li> EXCEPTIONS </li>
0230: * <li> BGCOLOR </li>
0231: * </ul>
0232: *
0233: *
0234: * @param request
0235: * DOCUMENT ME!
0236: *
0237: * @throws WmsException
0238: * DOCUMENT ME!
0239: *
0240: * @task TODO: implement parsing of transparent, exceptions and bgcolor
0241: */
0242: public void parseOptionalParameters(GetMapRequest request)
0243: throws WmsException {
0244: // SRS
0245: String epsgCode = getValue("SRS");
0246:
0247: if (epsgCode != null) {
0248: try {
0249: CoordinateReferenceSystem mapcrs = CRS.decode(epsgCode);
0250: request.setCrs(mapcrs);
0251: request.setSRS(epsgCode);
0252: } catch (Exception e) {
0253: // couldnt make it - we send off a service exception with the
0254: // correct info
0255: throw new WmsException(e.getLocalizedMessage(),
0256: "InvalidSRS");
0257: }
0258: }
0259:
0260: // transparency
0261: String transparentValue = getValue("TRANSPARENT");
0262: boolean transparent = (transparentValue == null) ? false
0263: : Boolean.valueOf(transparentValue).booleanValue();
0264: request.setTransparent(transparent);
0265:
0266: // background
0267: String bgcolor = getValue("BGCOLOR");
0268:
0269: if (bgcolor != null) {
0270: try {
0271: request.setBgColor(Color.decode(bgcolor));
0272: } catch (NumberFormatException nfe) {
0273: throw new WmsException(
0274: "BGCOLOR "
0275: + bgcolor
0276: + " incorrectly specified (0xRRGGBB format expected)");
0277: }
0278: }
0279:
0280: // filter parsing
0281: parseFilterParam(request);
0282:
0283: // buffer
0284: String bufferValue = getValue("BUFFER");
0285: int buffer = 0;
0286:
0287: if (bufferValue != null) {
0288: try {
0289: buffer = Integer.parseInt(bufferValue);
0290: } catch (NumberFormatException nfe) {
0291: throw new WmsException(
0292: "BUFFER "
0293: + bufferValue
0294: + " incorrectly specified (expected an integer)");
0295: }
0296: }
0297:
0298: request.setBuffer(buffer);
0299:
0300: // palette
0301: String paletteValue = getValue("PALETTE");
0302:
0303: if (paletteValue != null) {
0304: try {
0305: final InverseColorMapOp eicm = PaletteManager
0306: .getPalette(paletteValue);
0307: if (eicm == null) {
0308: throw new WmsException(
0309: "Palette "
0310: + paletteValue
0311: + " could not be found "
0312: + "in $GEOSERVER_DATA_DIR/palettes directory");
0313: }
0314:
0315: request.setPalette(eicm);
0316: } catch (Exception e) {
0317: throw new WmsException(e, "Palette " + paletteValue
0318: + " could not be loaded", null);
0319: }
0320: }
0321:
0322: // tiling hint
0323: String tiledValue = getValue("TILED");
0324: request.setTiled("TRUE".equalsIgnoreCase(tiledValue));
0325:
0326: // tiling origin
0327: String origin = getValue("TILESORIGIN");
0328:
0329: if (origin != null) {
0330: request.setTilesOrigin(parseTilesOrigin(origin));
0331: }
0332:
0333: // feature version (for versioned requests)
0334: String featureVersion = getValue("FEATUREVERSION");
0335: request.setFeatureVersion(featureVersion);
0336:
0337: /** KML/KMZ score value */
0338: String KMScore = getValue("KMSCORE");
0339:
0340: if (KMScore != null) {
0341: try {
0342: // handle special string cases of "vector" or "raster"
0343: if (KMScore.equalsIgnoreCase("vector")) {
0344: KMScore = "100"; // vector default
0345: } else if (KMScore.equalsIgnoreCase("raster")) {
0346: KMScore = "0"; // raster default
0347: }
0348:
0349: Integer s = new Integer(KMScore);
0350: int score = s.intValue();
0351:
0352: if ((score < 0) || (score > 100)) {
0353: throw new NumberFormatException(
0354: "KMScore not between 0 and 100. "
0355: + "If you wish not to use it, do not specify KMScore as a parameter.");
0356: }
0357:
0358: request.setKMScore(score);
0359:
0360: if (LOGGER.isLoggable(Level.INFO)) {
0361: LOGGER.info("Set KMScore: " + score);
0362: }
0363: } catch (NumberFormatException e) {
0364: throw new WmsException(
0365: "KMScore parameter ("
0366: + KMScore
0367: + ") incorrectly specified. "
0368: + "Expecting an integer value between between 0 and 100");
0369: }
0370: }
0371:
0372: /** KMattr: 'full' or 'no' attribution for KML placemark <description> */
0373: String KMAttr = getValue("KMATTR");
0374:
0375: if (KMAttr != null) {
0376: if (KMAttr.equalsIgnoreCase("no")
0377: || KMAttr.equalsIgnoreCase("false")
0378: || KMAttr.equalsIgnoreCase("0")) {
0379: request.setKMattr(false);
0380: } else {
0381: request.setKMattr(true); // default to true
0382: }
0383: }
0384:
0385: /** KML super overlay */
0386: String super Overlay = getValue("SUPEROVERLAY");
0387:
0388: if (super Overlay != null) {
0389: request.setSuperOverlay("TRUE"
0390: .equalsIgnoreCase(super Overlay));
0391: }
0392:
0393: /** KML legend */
0394: String legend = getValue("LEGEND");
0395:
0396: if (legend != null) {
0397: request.setLegend("TRUE".equalsIgnoreCase(legend)
0398: || "ON".equalsIgnoreCase(legend));
0399: }
0400:
0401: // /** TIME: a time stamp for multidim coverages <description> */
0402: // String time = getValue("TIME");
0403: //
0404: // if (time != null) {
0405: // request.setTime(Integer.valueOf(time));
0406: //
0407: // if (LOGGER.isLoggable(Level.INFO)) {
0408: // LOGGER.info("Set TIME: " + time);
0409: // }
0410: // }
0411:
0412: /**
0413: * ELEVATION: elevation (or depth) valu for multidim coverages
0414: * <description>
0415: */
0416: String elev = getValue("ELEVATION");
0417:
0418: if (elev != null) {
0419: request.setElevation(Integer.valueOf(elev));
0420:
0421: if (LOGGER.isLoggable(Level.INFO)) {
0422: LOGGER.info("Set ELEVATION: " + elev);
0423: }
0424: }
0425: }
0426:
0427: private Point2D parseTilesOrigin(String origin) {
0428: Object[] coordValues = readFlat(origin, INNER_DELIMETER)
0429: .toArray();
0430:
0431: if (coordValues.length != 2) {
0432: throw new ServiceException(origin
0433: + " is not a valid coordinate", getClass()
0434: .getName());
0435: }
0436:
0437: try {
0438: double minx = Double.parseDouble(coordValues[0].toString());
0439: double miny = Double.parseDouble(coordValues[1].toString());
0440:
0441: return new Point2D.Double(minx, miny);
0442: } catch (NumberFormatException ex) {
0443: throw new ServiceException(ex,
0444: "Illegal value for TILESORIGIN parameter: "
0445: + origin, getClass().getName()
0446: + "::parseTilesOrigin()");
0447: }
0448: }
0449:
0450: /**
0451: * Parses the mandatory GetMap request parameters:
0452: *
0453: * <p>
0454: * Mandatory parameters:
0455: *
0456: * <ul>
0457: * <li> LAYERS </li>
0458: * <li> STYLES ommited if SLD or SLD_BODY parameters are supplied </li>
0459: * <li> BBOX </li>
0460: * <li> FORMAT </li>
0461: * <li> WIDTH </li>
0462: * <li> HEIGHT </li>
0463: * </ul>
0464: * </p>
0465: *
0466: * @param request
0467: * DOCUMENT ME!
0468: * @parseStylesLayers true = normal operation, false = dont parse the styles
0469: * and layers (used by the SLD GET/POST)
0470: *
0471: * @throws WmsException
0472: * DOCUMENT ME!
0473: */
0474: public void parseMandatoryParameters(GetMapRequest request,
0475: boolean parseStylesLayers) throws WmsException {
0476: try {
0477: int width = Integer.parseInt(getValue("WIDTH"));
0478: int height = Integer.parseInt(getValue("HEIGHT"));
0479: request.setWidth(width);
0480: request.setHeight(height);
0481: } catch (NumberFormatException ex) {
0482: throw new WmsException(
0483: "WIDTH and HEIGHT incorrectly specified");
0484: }
0485:
0486: String format = getValue("FORMAT");
0487:
0488: if (format == null) {
0489: throw new WmsException("parameter FORMAT is required");
0490: }
0491:
0492: request.setFormat(format);
0493:
0494: Envelope bbox = parseBbox(getValue("BBOX"));
0495: request.setBbox(bbox);
0496:
0497: // let styles and layers parsing for the end to give more trivial
0498: // parameters
0499: // a chance to fail before incurring in retrieving the SLD or SLD_BODY
0500: if (parseStylesLayers) {
0501: parseLayersAndStyles(request);
0502: }
0503: }
0504:
0505: protected Envelope parseBbox(String bboxParam) throws WmsException {
0506: // overridden to throw the right exception for this context
0507: try {
0508: return super .parseBbox(bboxParam);
0509: } catch (ServiceException e) {
0510: throw new WmsException(e);
0511: }
0512: }
0513:
0514: /**
0515: * creates a list of requested attributes, wich must be a valid attribute
0516: * name or one of the following special attributes:
0517: *
0518: * <ul>
0519: * <li> <b>#FID</b>: a map producer capable of handling attributes (such as
0520: * SVGMapResponse), will write the feature id of each feature </li>
0521: * <li> <b>#BOUNDS</b>: a map producer capable of handling attributes (such
0522: * as SVGMapResponse), will write the bounding box of each feature </li>
0523: * </ul>
0524: *
0525: *
0526: * @param layers
0527: * info about the requested map layers
0528: *
0529: * @return an empty list if no attributes was requested, or a
0530: * <code>List<List<String>></code> with an entry for
0531: * each requested layer, where each of them consists of a List of
0532: * the attribute names requested
0533: *
0534: * @throws WmsException
0535: * if:
0536: * <ul>
0537: * <li>the number of attribute sets requested is not equal to
0538: * the number of layers requested.</li>
0539: * <li>an illegal attribute name was requested</li>
0540: * <li>an IOException occurs while fetching a FeatureType
0541: * schema to ask it for propper attribute names</li>
0542: * </ul>
0543: */
0544: private List parseAttributes(FeatureTypeInfo[] layers)
0545: throws WmsException {
0546: String rawAtts = getValue("ATTRIBUTES");
0547:
0548: if (LOGGER.isLoggable(Level.FINER)) {
0549: LOGGER.finer(new StringBuffer("parsing attributes ")
0550: .append(rawAtts).toString());
0551: }
0552:
0553: if ((rawAtts == null) || "".equals(rawAtts)) {
0554: return Collections.EMPTY_LIST;
0555: }
0556:
0557: // raw list of attributes for each feature type requested
0558: List byFeatureTypes = readFlat(rawAtts, "|");
0559: int nLayers = layers.length;
0560:
0561: if (byFeatureTypes.size() != nLayers) {
0562: throw new WmsException(byFeatureTypes.size()
0563: + " lists of attributes specified, expected "
0564: + layers.length, getClass().getName()
0565: + "::parseAttributes()");
0566: }
0567:
0568: // fill byFeatureTypes with the split of its raw attributes requested
0569: // separated by commas, and check for the validity of each att name
0570: FeatureType schema;
0571: List atts;
0572: String attName;
0573:
0574: for (int i = 0; i < nLayers; i++) {
0575: rawAtts = (String) byFeatureTypes.get(i);
0576:
0577: atts = readFlat(rawAtts, ",");
0578: byFeatureTypes.set(i, atts);
0579:
0580: // FeatureType schema = layers[i].getSchema();
0581: try {
0582: schema = layers[i].getFeatureType();
0583:
0584: // verify that propper attributes has been requested
0585: for (Iterator attIt = atts.iterator(); attIt.hasNext();) {
0586: attName = (String) attIt.next();
0587:
0588: if (attName.length() > 0) {
0589: if (LOGGER.isLoggable(Level.FINER)) {
0590: LOGGER.finer(new StringBuffer(
0591: "checking that ").append(attName)
0592: .append(" is valid").toString());
0593: }
0594:
0595: if ("#FID".equalsIgnoreCase(attName)
0596: || "#BOUNDS".equalsIgnoreCase(attName)) {
0597: if (LOGGER.isLoggable(Level.FINER)) {
0598: LOGGER
0599: .finer(new StringBuffer(
0600: "special attribute name requested: ")
0601: .append(attName)
0602: .toString());
0603: }
0604:
0605: continue;
0606: }
0607:
0608: if (schema.getAttributeType(attName) == null) {
0609: throw new WmsException("Attribute '"
0610: + attName
0611: + "' requested for layer "
0612: + schema.getTypeName()
0613: + " does not exists");
0614: }
0615: } else {
0616: if (LOGGER.isLoggable(Level.FINEST)) {
0617: LOGGER
0618: .finest("removing empty attribute name from request");
0619: }
0620:
0621: attIt.remove();
0622: }
0623: }
0624:
0625: if (LOGGER.isLoggable(Level.FINEST)) {
0626: LOGGER.finest(new StringBuffer(
0627: "attributes requested for ").append(
0628: schema.getTypeName()).append(" checked: ")
0629: .append(rawAtts).toString());
0630: }
0631: } catch (java.io.IOException e) {
0632: throw new WmsException(e);
0633: }
0634: }
0635:
0636: return byFeatureTypes;
0637: }
0638:
0639: /**
0640: * Parses the list of style names requested for each requested layer and
0641: * looks up the actual Style objects, which are returned in an ordered list.
0642: *
0643: * <p>
0644: * A client _may_ request teh default Style using a null value (as in
0645: * "STYLES="). If several layers are requested with a mixture of named and
0646: * default styles, the STYLES parameter includes null values between commas
0647: * (as in "STYLES=style1,,style2,,"). If all layers are to be shown using
0648: * the default style, either the form "STYLES=" or "STYLES=,,," is valid.
0649: * </p>
0650: *
0651: * @param request
0652: * DOCUMENT ME!
0653: * @param layers
0654: * the requested feature types
0655: *
0656: * @return a full <code>List</code> of the style names requested for the
0657: * requiered layers with no null style names.
0658: *
0659: * @throws WmsException
0660: * if some of the requested styles does not exist or its number
0661: * if greater than zero and distinct of the number of requested
0662: * layers
0663: */
0664: protected List parseStylesParam(GetMapRequest request,
0665: MapLayerInfo[] layers) throws WmsException {
0666: String rawStyles = getValue("STYLES");
0667: List styles = new ArrayList(layers.length);
0668:
0669: int numLayers = layers.length;
0670:
0671: if ("".equals(rawStyles)) {
0672: if (LOGGER.isLoggable(Level.FINER)) {
0673: LOGGER
0674: .finer("Assigning default style to all the requested layers");
0675: }
0676:
0677: for (int i = 0; i < numLayers; i++)
0678: if (layers[i].getType() == MapLayerInfo.TYPE_VECTOR) {
0679: styles
0680: .add(layers[i].getFeature()
0681: .getDefaultStyle());
0682: } else if (layers[i].getType() == MapLayerInfo.TYPE_RASTER) {
0683: styles.add(layers[i].getCoverage()
0684: .getDefaultStyle());
0685: }
0686: } else {
0687: List styleNames = readFlat(rawStyles, INNER_DELIMETER);
0688:
0689: if (numLayers != styleNames.size()) {
0690: String msg = numLayers
0691: + " layers requested, but found "
0692: + styleNames.size()
0693: + " styles specified. "
0694: + "Since SLD parameter is not yet implemented, the STYLES parameter "
0695: + "is mandatory and MUST have exactly one value per requested layer";
0696: throw new WmsException(msg, getClass().getName());
0697: }
0698:
0699: String currStyleName;
0700: Style currStyle;
0701: MapLayerInfo currLayer;
0702:
0703: for (int i = 0; i < numLayers; i++) {
0704: currStyleName = (String) styleNames.get(i);
0705: currLayer = layers[i];
0706:
0707: if (currLayer.getType() == MapLayerInfo.TYPE_VECTOR) {
0708: if ((null == currStyleName)
0709: || "".equals(currStyleName)) {
0710: currStyle = currLayer.getFeature()
0711: .getDefaultStyle();
0712: } else {
0713: currStyle = findStyle(request, currStyleName);
0714:
0715: if (currStyle == null) {
0716: String msg = "No default style has been defined for "
0717: + currLayer.getName();
0718: throw new WmsException(msg,
0719: "StyleNotDefined");
0720: }
0721: }
0722:
0723: try {
0724: checkStyle(currStyle, layers[i].getFeature()
0725: .getFeatureType());
0726: } catch (IOException e) {
0727: throw new WmsException(
0728: "Error obtaining FeatureType for layer "
0729: + layers[i].getName());
0730: }
0731:
0732: if (LOGGER.isLoggable(Level.FINE)) {
0733: LOGGER
0734: .fine(new StringBuffer("establishing ")
0735: .append(currStyleName).append(
0736: " style for ").append(
0737: layers[i].getName())
0738: .toString());
0739: }
0740:
0741: styles.add(currStyle);
0742: } else if (currLayer.getType() == MapLayerInfo.TYPE_RASTER) {
0743: if ((null == currStyleName)
0744: || "".equals(currStyleName)) {
0745: currStyle = currLayer.getCoverage()
0746: .getDefaultStyle();
0747: } else {
0748: currStyle = findStyle(request, currStyleName);
0749:
0750: if (currStyle == null) {
0751: String msg = "No default style has been defined for "
0752: + currLayer.getName();
0753: throw new WmsException(msg,
0754: "GetMapKvpReader::parseStyles()");
0755: }
0756: }
0757:
0758: /**
0759: * @task TODO: Check for Style Coverage Compatibility ...
0760: */
0761: styles.add(currStyle);
0762: }
0763: }
0764: }
0765:
0766: return styles;
0767: }
0768:
0769: /**
0770: * DOCUMENT ME!
0771: *
0772: * @param request
0773: * @param currStyleName
0774: *
0775: * @return the configured style named <code>currStyleName</code> or
0776: * <code>null</code> if such a style does not exists on this
0777: * server.
0778: */
0779: public static Style findStyle(GetMapRequest request,
0780: String currStyleName) {
0781: Style currStyle;
0782: Map configuredStyles = request.getWMS().getData().getStyles();
0783:
0784: currStyle = (Style) configuredStyles.get(currStyleName);
0785:
0786: return currStyle;
0787: }
0788:
0789: /**
0790: * Method to initialize a user layer which contains inline features.
0791: *
0792: * @param request
0793: * The request
0794: * @param mapLayer
0795: * The map layer.
0796: *
0797: * @throws Exception
0798: */
0799:
0800: // JD: the reason this method is static is to share logic among the xml
0801: // and kvp reader, ugh...
0802: public static void initializeInlineFeatureLayer(
0803: GetMapRequest getMapRequest, UserLayer ul,
0804: MapLayerInfo currLayer) throws Exception {
0805: // SPECIAL CASE - we make the temporary version
0806: currLayer.setFeature(new TemporaryFeatureTypeInfo(ul
0807: .getInlineFeatureDatastore()));
0808:
0809: // what if they didn't put an "srsName" on their geometry in their
0810: // inlinefeature?
0811: // I guess we should assume they mean their geometry to exist in the
0812: // output SRS of the
0813: // request they're making.
0814: if (ul.getInlineFeatureType().getDefaultGeometry()
0815: .getCoordinateSystem() == null) {
0816: LOGGER
0817: .warning("No CRS set on inline features default geometry. Assuming the requestor has their inlinefeatures in the boundingbox CRS.");
0818:
0819: FeatureType currFt = ul.getInlineFeatureType();
0820: Query q = new DefaultQuery(currFt.getTypeName(),
0821: Filter.INCLUDE);
0822: FeatureReader ilReader = ul.getInlineFeatureDatastore()
0823: .getFeatureReader(q, Transaction.AUTO_COMMIT);
0824: CoordinateReferenceSystem crs = (getMapRequest.getCrs() == null) ? DefaultGeographicCRS.WGS84
0825: : getMapRequest.getCrs();
0826: MemoryDataStore reTypedDS = new MemoryDataStore(
0827: new ForceCoordinateSystemFeatureReader(ilReader,
0828: crs));
0829: currLayer
0830: .setFeature(new TemporaryFeatureTypeInfo(reTypedDS));
0831: }
0832: }
0833:
0834: /**
0835: * Checks to make sure that the style passed in can process the FeatureType.
0836: *
0837: * @param style
0838: * The style to check
0839: * @param fType
0840: * The source requested.
0841: *
0842: * @throws WmsException
0843: * DOCUMENT ME!
0844: */
0845: private void checkStyle(Style style, FeatureType fType)
0846: throws WmsException {
0847: StyleAttributeExtractor sae = new StyleAttributeExtractor();
0848: sae.visit(style);
0849:
0850: String[] styleAttributes = sae.getAttributeNames();
0851: String attName;
0852: final int length = styleAttributes.length;
0853:
0854: for (int i = 0; i < length; i++) {
0855: attName = styleAttributes[i];
0856:
0857: if (fType.getAttributeType(attName) == null) {
0858: throw new WmsException(
0859: "The requested Style can not be used with "
0860: + "this featureType. The style specifies an attribute of "
0861: + attName
0862: + " and the featureType definition is: "
0863: + fType);
0864: }
0865: }
0866: }
0867:
0868: /**
0869: * DOCUMENT ME!
0870: *
0871: * @param request
0872: * DOCUMENT ME!
0873: *
0874: * @throws WmsException
0875: * DOCUMENT ME!
0876: */
0877: protected void parseLayersAndStyles(GetMapRequest request)
0878: throws WmsException {
0879: String sldParam = getValue("SLD");
0880: String sldBodyParam = getValue("SLD_BODY");
0881:
0882: if (sldBodyParam != null) {
0883: if (LOGGER.isLoggable(Level.FINE)) {
0884: LOGGER.fine("Getting layers and styles from SLD_BODY");
0885: }
0886:
0887: parseSldBodyParam(request);
0888: } else if (sldParam != null) {
0889: if (LOGGER.isLoggable(Level.FINE)) {
0890: LOGGER
0891: .fine("Getting layers and styles from reomte SLD");
0892: }
0893:
0894: parseSldParam(request);
0895: } else {
0896: MapLayerInfo[] featureTypes = null;
0897: List styles = null;
0898: featureTypes = parseLayersParam(request);
0899:
0900: request.setLayers(featureTypes);
0901:
0902: if (isStylesRquired()) {
0903: styles = parseStylesParam(request, featureTypes);
0904:
0905: if (isStylesRquired()) {
0906: request.setStyles(styles);
0907: }
0908: }
0909: }
0910: }
0911:
0912: /**
0913: * Takes the SLD_BODY parameter value and parses it to a geotools'
0914: * <code>StyledLayerDescriptor</code>, then takes the layers and styles
0915: * to use in the map composition from there.
0916: *
0917: * @param request
0918: * DOCUMENT ME!
0919: *
0920: * @throws WmsException
0921: * DOCUMENT ME!
0922: */
0923: protected void parseSldBodyParam(GetMapRequest request)
0924: throws WmsException {
0925: final String sldBody = getValue("SLD_BODY");
0926:
0927: if (LOGGER.isLoggable(Level.FINE)) {
0928: LOGGER.fine(new StringBuffer("About to parse SLD body: ")
0929: .append(sldBody).toString());
0930: }
0931:
0932: if (getValue("VALIDATESCHEMA") != null) {
0933: // Get a reader from the given string
0934: Reader reader = getReaderFromString(sldBody);
0935:
0936: // -InputStream in = new StringBufferInputStream(sldBody);
0937: // user requested to validate the schema.
0938: SLDValidator validator = new SLDValidator();
0939: List errors = null;
0940:
0941: // Create a sax input source from the reader
0942: InputSource in = new InputSource(reader);
0943: errors = validator.validateSLD(in, request
0944: .getHttpServletRequest().getSession()
0945: .getServletContext());
0946:
0947: if (errors.size() != 0) {
0948: reader = getReaderFromString(sldBody);
0949: throw new WmsException(SLDValidator.getErrorMessage(
0950: reader, errors));
0951: }
0952:
0953: // - errors = validator.validateSLD(in,
0954: // request.getHttpServletRequest().getSession().getServletContext());
0955: // - try{
0956: // - in.close();
0957: // - }
0958: // - catch(Exception e)
0959: // - {
0960: // - // do nothing
0961: // - }
0962: // - if (errors.size() != 0)
0963: // - {
0964: // - in = new StringBufferInputStream(sldBody);
0965: // - throw new
0966: // WmsException(SLDValidator.getErrorMessage(in,errors));
0967: // - }
0968: }
0969:
0970: // - InputStream in = new StringBufferInputStream(sldBody);
0971: // - SLDParser parser = new SLDParser(styleFactory, in);
0972: Reader reader = getReaderFromString(sldBody);
0973: SLDParser parser = new SLDParser(styleFactory, reader);
0974: StyledLayerDescriptor sld = parser.parseSLD();
0975: parseStyledLayerDescriptor(request, sld);
0976: }
0977:
0978: /**
0979: * Create a reader of the given String. This reader will be used in the
0980: * InputSource for the sld parser. The advantage with a reader over a input
0981: * stream is that we don't have to consider encoding. The xml declaration
0982: * with encoding is ignored using a Reader in parser. The encoding of the
0983: * string has been appropiate handled by the servlet when streaming in.
0984: *
0985: * @param sldBody
0986: * the sldbody to create a reader of.
0987: * @return The created reader
0988: * @see Reader
0989: */
0990: private Reader getReaderFromString(String sldBody) {
0991: return new StringReader(sldBody);
0992: }
0993:
0994: /**
0995: * Gets a sequence of url encoded filters and parses them into Filter
0996: * objects that will be set into the request object
0997: *
0998: * @param request
0999: * @throws WmsException
1000: */
1001: protected void parseFilterParam(GetMapRequest request)
1002: throws WmsException {
1003: String rawFilter = getValue("FILTER");
1004: String rawCqlFilter = getValue("CQL_FILTER");
1005: String rawIdFilter = getValue("FEATUREID");
1006:
1007: // in case of a mixed request, get with sld in post body, layers
1008: // are not parsed, so we can't parse filters neither...
1009: if (request.getLayers() == null) {
1010: return;
1011: }
1012:
1013: int numLayers = request.getLayers().length;
1014:
1015: if (numLayers == 0) {
1016: throw new RuntimeException(
1017: "parseFilterParam must be called after the layer list has been built!");
1018: }
1019:
1020: List filters = null;
1021:
1022: // if no filter, no need to proceed
1023: if ((rawFilter != null) && !rawFilter.equals("")) {
1024: try {
1025: filters = readOGCFilter(rawFilter);
1026: } catch (ServiceException e) {
1027: throw new WmsException(e);
1028: }
1029: }
1030:
1031: if ((rawIdFilter != null) && !rawIdFilter.equals("")) {
1032: if (filters != null) {
1033: throw new WmsException("GetMap KVP request contained "
1034: + "conflicting filters. Filter: " + rawFilter
1035: + ", fid: " + rawFilter);
1036: }
1037:
1038: filters = readFidFilters(rawIdFilter);
1039: }
1040:
1041: if ((rawCqlFilter != null) && !rawCqlFilter.equals("")) {
1042: if (filters != null) {
1043: throw new WmsException("GetMap KVP request contained "
1044: + "conflicting filters. Filter: " + rawFilter
1045: + ", fid: " + rawFilter + ", cql: "
1046: + rawCqlFilter);
1047: }
1048:
1049: try {
1050: filters = readCQLFilter(rawCqlFilter);
1051: } catch (ServiceException e) {
1052: throw new WmsException(e);
1053: }
1054: }
1055:
1056: if (filters == null) {
1057: return;
1058: }
1059:
1060: if (numLayers != filters.size()) {
1061: // as in wfs getFeatures, perform lenient parsing, if just one
1062: // filter, it gets
1063: // applied to all layers
1064: if (filters.size() == 1) {
1065: Filter f = (Filter) filters.get(0);
1066: filters = new ArrayList(numLayers);
1067:
1068: for (int i = 0; i < numLayers; i++) {
1069: filters.add(f);
1070: }
1071: } else {
1072: String msg = numLayers
1073: + " layers requested, but found "
1074: + filters.size()
1075: + " filters specified. "
1076: + "When you specify the FILTER parameter, you must provide just one, \n"
1077: + " that will be applied to all layers, or exactly one for each requested layer";
1078: throw new WmsException(msg, getClass().getName());
1079: }
1080: }
1081:
1082: request.setFilters(filters);
1083: }
1084:
1085: /**
1086: * DOCUMENT ME!
1087: *
1088: * @param request
1089: * DOCUMENT ME!
1090: *
1091: * @throws WmsException
1092: * DOCUMENT ME!
1093: */
1094: protected void parseSldParam(GetMapRequest request)
1095: throws WmsException {
1096: String urlValue = getValue("SLD");
1097:
1098: if (LOGGER.isLoggable(Level.FINE)) {
1099: LOGGER.fine(new StringBuffer(
1100: "about to load remote SLD document: '").append(
1101: urlValue).append("'").toString());
1102: }
1103:
1104: URL sldUrl;
1105:
1106: try {
1107: sldUrl = new URL(fixURL(urlValue));
1108: } catch (MalformedURLException e) {
1109: String msg = new StringBuffer("Creating remote SLD url: ")
1110: .append(e.getMessage()).toString();
1111:
1112: if (LOGGER.isLoggable(Level.WARNING)) {
1113: LOGGER.log(Level.WARNING, msg, e);
1114: }
1115:
1116: throw new WmsException(e, msg, "parseSldParam");
1117: }
1118:
1119: if (getValue("VALIDATESCHEMA") != null) {
1120: // user requested to validate the schema.
1121: SLDValidator validator = new SLDValidator();
1122: List errors = null;
1123:
1124: try {
1125: // JD: GEOS-420, Wrap the sldUrl in getINputStream method in
1126: // order
1127: // to do compression
1128: InputStream in = getInputStream(sldUrl);
1129: errors = validator.validateSLD(in, request
1130: .getHttpServletRequest().getSession()
1131: .getServletContext());
1132: in.close();
1133:
1134: if (errors.size() != 0) {
1135: throw new WmsException(SLDValidator
1136: .getErrorMessage(sldUrl.openStream(),
1137: errors));
1138: }
1139: } catch (IOException e) {
1140: String msg = new StringBuffer(
1141: "Creating remote SLD url: ").append(
1142: e.getMessage()).toString();
1143:
1144: if (LOGGER.isLoggable(Level.WARNING)) {
1145: LOGGER.log(Level.WARNING, msg, e);
1146: }
1147:
1148: throw new WmsException(e, msg, "parseSldParam");
1149: }
1150: }
1151:
1152: SLDParser parser;
1153:
1154: try {
1155: // JD: GEOS-420, Wrap the sldUrl in getINputStream method in order
1156: // to do compression
1157: parser = new SLDParser(styleFactory, getInputStream(sldUrl));
1158: } catch (IOException e) {
1159: String msg = new StringBuffer("Creating remote SLD url: ")
1160: .append(e.getMessage()).toString();
1161:
1162: if (LOGGER.isLoggable(Level.WARNING)) {
1163: LOGGER.log(Level.WARNING, msg, e);
1164: }
1165:
1166: throw new WmsException(e, msg, "parseSldParam");
1167: }
1168:
1169: StyledLayerDescriptor sld = parser.parseSLD();
1170: parseStyledLayerDescriptor(request, sld);
1171: }
1172:
1173: /**
1174: * URLEncoder.encode does not respect the RFC 2396, so we rolled our own
1175: * little encoder. It's not complete, but should work in most cases
1176: *
1177: * @param url
1178: * @return
1179: */
1180: static String fixURL(String url) {
1181: StringBuffer sb = new StringBuffer();
1182:
1183: for (int i = 0; i < url.length(); i++) {
1184: char c = url.charAt(i);
1185:
1186: // From RFC, "Only alphanumerics [0-9a-zA-Z], the special
1187: // characters "$-_.+!*'(),", and reserved characters used
1188: // for their reserved purposes may be used unencoded within a URL
1189: // Here we keep all the good ones, and remove the few uneeded in
1190: // their
1191: // ascii range. We also keep / and : to make sure basic URL elements
1192: // don't get encoded
1193: if ((c > ' ') && (c < '{')
1194: && ("\"\\<>%^[]`+$,".indexOf(c) == -1)) {
1195: sb.append(c);
1196: } else {
1197: sb.append("%").append(Integer.toHexString(c));
1198: }
1199: }
1200:
1201: return sb.toString();
1202: }
1203:
1204: /**
1205: * Looks in <code>sld</code> for the layers and styles to use in the map
1206: * composition and sets them to the <code>request</code>
1207: *
1208: * <p>
1209: * If <code>sld</code> is used in "library" mode, that is, the LAYERS
1210: * param is also present, saying what layers must be taken in count, then
1211: * only the layers from the LAYERS parameter are used and <code>sld</code>
1212: * is used as a style library, which means that for each layer requested
1213: * through LAYERS=..., if a style if found in it for that layer it is used,
1214: * and if not, the layers default is used.
1215: * </p>
1216: *
1217: * <p>
1218: * By the other hand, if the LAYERS parameter is not present all the layers
1219: * found in <code>sld</code> are setted to <code>request</code>.
1220: * </p>
1221: *
1222: * @param request
1223: * the GetMap request to which to set the layers and styles
1224: * @param sld
1225: * a SLD document to take layers and styles from, following the
1226: * "literal" or "library" rule.
1227: *
1228: * @throws WmsException
1229: * if anything goes wrong
1230: * @throws RuntimeException
1231: * DOCUMENT ME!
1232: */
1233: private void parseStyledLayerDescriptor(
1234: final GetMapRequest request, final StyledLayerDescriptor sld)
1235: throws WmsException {
1236: MapLayerInfo[] libraryModeLayers = null;
1237:
1238: if (null != getValue("LAYERS")) {
1239: if (LOGGER.isLoggable(Level.INFO)) {
1240: LOGGER.info("request comes in \"library\" mode");
1241: }
1242:
1243: libraryModeLayers = parseLayersParam(request);
1244: }
1245:
1246: final StyledLayer[] styledLayers = sld.getStyledLayers();
1247: final int slCount = styledLayers.length;
1248:
1249: if (slCount == 0) {
1250: throw new WmsException("SLD document contains no layers");
1251: }
1252:
1253: final List layers = new ArrayList();
1254: final List styles = new ArrayList();
1255:
1256: MapLayerInfo currLayer = null;
1257: Style currStyle = null;
1258:
1259: if (null != libraryModeLayers) {
1260: int lCount = libraryModeLayers.length;
1261:
1262: for (int i = 0; i < lCount; i++) {
1263: currLayer = libraryModeLayers[i];
1264:
1265: if (currLayer.getType() == MapLayerInfo.TYPE_VECTOR) {
1266: currStyle = findStyleOf(request, currLayer
1267: .getFeature(), styledLayers);
1268: } else if (currLayer.getType() == MapLayerInfo.TYPE_RASTER) {
1269: try {
1270: currStyle = findStyleOf(request, currLayer
1271: .getFeature(), styledLayers);
1272: } catch (WmsException wm) {
1273: currStyle = findStyleOf(request, currLayer
1274: .getFeature(), styledLayers);
1275: if (currStyle == null) {
1276: // nope, there's no default raster style. Give up.
1277: throw new WmsException(
1278: wm.getMessage()
1279: + " Also tried to use "
1280: + "the generic raster style 'raster', but it wasn't available.");
1281: }
1282: }
1283: }
1284:
1285: layers.add(currLayer);
1286: styles.add(currStyle);
1287: }
1288: } else {
1289: StyledLayer sl = null;
1290: String layerName;
1291: UserLayer ul;
1292:
1293: for (int i = 0; i < slCount; i++) {
1294: sl = styledLayers[i];
1295: layerName = sl.getName();
1296:
1297: if (null == layerName) {
1298: throw new WmsException(
1299: "A UserLayer without layer name was passed");
1300: }
1301:
1302: currLayer = new MapLayerInfo();
1303:
1304: // handle the InLineFeature stuff
1305: // TODO: add support for remote WFS here
1306: if ((sl instanceof UserLayer)
1307: && ((((UserLayer) sl))
1308: .getInlineFeatureDatastore() != null)) {
1309: // SPECIAL CASE - we make the temporary version
1310: ul = ((UserLayer) sl);
1311:
1312: try {
1313: initializeInlineFeatureLayer(request, ul,
1314: currLayer);
1315: } catch (Exception e) {
1316: throw new WmsException(e);
1317: }
1318: } else {
1319: try {
1320: currLayer.setFeature(GetMapKvpReader
1321: .findFeatureLayer(request, layerName));
1322: } catch (WmsException e) {
1323: currLayer.setCoverage(GetMapKvpReader
1324: .findCoverageLayer(request, layerName));
1325: }
1326: }
1327:
1328: if (currLayer.getType() == MapLayerInfo.TYPE_VECTOR) {
1329: // currStyle = findStyleOf(request, currLayer,
1330: // styledLayers); // DJB: this looks like a bug, we should
1331: // get the style from styledLayers[i]
1332:
1333: // the correct thing to do its grab the style from
1334: // styledLayers[i]
1335: // inside the styledLayers[i] will either be :
1336: // a) nothing - in which case grab the layer's default style
1337: // b) a set of:
1338: // i) NameStyle -- grab it from the pre-loaded styles
1339: // ii)UserStyle -- grab it from the sld the user uploaded
1340: //
1341: // NOTE: we're going to get a set of layer->style pairs for
1342: // (b).
1343: addStyles(request, currLayer, styledLayers[i],
1344: layers, styles);
1345: } else if (currLayer.getType() == MapLayerInfo.TYPE_RASTER) {
1346: try {
1347: addStyles(request, currLayer, styledLayers[i],
1348: layers, styles);
1349: } catch (WmsException wm) {
1350: // hmm, well, the style they specified in the wms
1351: // request
1352: // wasn't found. Let's try the default raster style
1353: // named 'raster'
1354: currStyle = findStyle(request, "raster");
1355: if (currStyle == null) {
1356: // nope, there's no default raster style. Give up.
1357: throw new WmsException(
1358: wm.getMessage()
1359: + " Also tried to use "
1360: + "the generic raster style 'raster', but it wasn't available.");
1361: }
1362: layers.add(currLayer);
1363: styles.add(currStyle);
1364: }
1365: }
1366: }
1367: }
1368:
1369: request.setLayers((MapLayerInfo[]) layers
1370: .toArray(new MapLayerInfo[layers.size()]));
1371: request.setStyles(styles);
1372: }
1373:
1374: /**
1375: * the correct thing to do its grab the style from styledLayers[i] inside
1376: * the styledLayers[i] will either be : a) nothing - in which case grab the
1377: * layer's default style b) a set of: i) NameStyle -- grab it from the
1378: * pre-loaded styles ii)UserStyle -- grab it from the sld the user uploaded
1379: *
1380: * NOTE: we're going to get a set of layer->style pairs for (b). these are
1381: * added to layers,styles
1382: *
1383: * NOTE: we also handle some featuretypeconstraints
1384: *
1385: * @param request
1386: * @param currLayer
1387: * @param layer
1388: * @param layers
1389: * @param styles
1390: */
1391: public static void addStyles(GetMapRequest request,
1392: MapLayerInfo currLayer, StyledLayer layer, List layers,
1393: List styles) throws WmsException {
1394: if (currLayer == null) {
1395: return; // protection
1396: }
1397:
1398: Style[] layerStyles = null;
1399: FeatureTypeConstraint[] ftcs = null;
1400:
1401: if (layer instanceof NamedLayer) {
1402: ftcs = ((NamedLayer) layer).getLayerFeatureConstraints();
1403: layerStyles = ((NamedLayer) layer).getStyles();
1404: } else if (layer instanceof UserLayer) {
1405: ftcs = ((UserLayer) layer).getLayerFeatureConstraints();
1406: layerStyles = ((UserLayer) layer).getUserStyles();
1407: }
1408:
1409: // DJB: TODO: this needs to do the whole thing, not just names
1410: if (ftcs != null) {
1411: FeatureTypeConstraint ftc;
1412: final int length = ftcs.length;
1413:
1414: for (int t = 0; t < length; t++) {
1415: ftc = ftcs[t];
1416:
1417: if (ftc.getFeatureTypeName() != null) {
1418: String ftc_name = ftc.getFeatureTypeName();
1419:
1420: // taken from lite renderer
1421: boolean matches;
1422:
1423: try {
1424: matches = currLayer.getFeature()
1425: .getFeatureType().isDescendedFrom(null,
1426: ftc_name)
1427: || currLayer.getFeature()
1428: .getFeatureType().getTypeName()
1429: .equalsIgnoreCase(ftc_name);
1430: } catch (Exception e) {
1431: matches = false; // bad news
1432: }
1433:
1434: if (!matches) {
1435: continue; // this layer is fitered out
1436: }
1437: }
1438: }
1439: }
1440:
1441: // handle no styles -- use default
1442: if ((layerStyles == null) || (layerStyles.length == 0)) {
1443: layers.add(currLayer);
1444: styles.add(currLayer.getDefaultStyle());
1445:
1446: return;
1447: }
1448:
1449: final int length = layerStyles.length;
1450: Style s;
1451:
1452: for (int t = 0; t < length; t++) {
1453: if (layerStyles[t] instanceof NamedStyle) {
1454: layers.add(currLayer);
1455: s = findStyle(request, ((NamedStyle) layerStyles[t])
1456: .getName());
1457:
1458: if (s == null) {
1459: throw new WmsException("couldnt find style named '"
1460: + ((NamedStyle) layerStyles[t]).getName()
1461: + "'");
1462: }
1463:
1464: styles.add(s);
1465: } else {
1466: layers.add(currLayer);
1467: styles.add(layerStyles[t]);
1468: }
1469: }
1470: }
1471:
1472: /**
1473: * Finds the style for <code>layer</code> in <code>styledLayers</code>
1474: * or the layer's default style if <code>styledLayers</code> has no a
1475: * UserLayer or a NamedLayer with the same name than <code>layer</code>
1476: * <p>
1477: * This method is used to parse the style of a layer for SLD and SLD_BODY
1478: * parameters, both in library and literal mode. Thus, once the declared
1479: * style for the given layer is found, it is checked for validity of
1480: * appliance for that layer (i.e., whether the featuretype contains the
1481: * attributes needed for executing the style filters).
1482: * </p>
1483: *
1484: * @param request
1485: * used to find out an internally configured style when
1486: * referenced by name by a NamedLayer
1487: *
1488: * @param layer
1489: * one of the internal FeatureType that was requested through the
1490: * LAYERS parameter or through and SLD document when the request
1491: * is in literal mode.
1492: * @param styledLayers
1493: * a set of StyledLayers from where to find the SLD layer with
1494: * the same name as <code>layer</code> and extract the style to
1495: * apply.
1496: *
1497: * @return the Style applicable to <code>layer</code> extracted from
1498: * <code>styledLayers</code>.
1499: *
1500: * @throws RuntimeException
1501: * if one of the StyledLayers is neither a UserLayer nor a
1502: * NamedLayer. This shuoldn't happen, since the only allowed
1503: * subinterfaces of StyledLayer are NamedLayer and UserLayer.
1504: * @throws WmsException
1505: */
1506: private Style findStyleOf(GetMapRequest request,
1507: FeatureTypeInfo layer, StyledLayer[] styledLayers)
1508: throws WmsException {
1509: Style style = null;
1510: String layerName = layer.getName();
1511: StyledLayer sl;
1512:
1513: for (int i = 0; i < styledLayers.length; i++) {
1514: sl = styledLayers[i];
1515:
1516: if (layerName.equals(sl.getName())) {
1517: if (sl instanceof UserLayer) {
1518: Style[] styles = ((UserLayer) sl).getUserStyles();
1519:
1520: if ((null != styles) && (0 < styles.length)) {
1521: style = styles[0];
1522: }
1523: } else if (sl instanceof NamedLayer) {
1524: Style[] styles = ((NamedLayer) sl).getStyles();
1525:
1526: if ((null != styles) && (0 < styles.length)) {
1527: style = styles[0];
1528: }
1529:
1530: if (style instanceof NamedStyle) {
1531: style = findStyle(request, style.getName());
1532: }
1533: } else {
1534: throw new RuntimeException("Unknown layer type: "
1535: + sl);
1536: }
1537:
1538: break;
1539: }
1540: }
1541:
1542: if (null == style) {
1543: style = layer.getDefaultStyle();
1544: }
1545:
1546: FeatureType type;
1547:
1548: try {
1549: type = layer.getFeatureType();
1550: } catch (IOException ioe) {
1551: throw new RuntimeException(
1552: "Error getting FeatureType, this should never happen!");
1553: }
1554:
1555: checkStyle(style, type);
1556:
1557: return style;
1558: }
1559:
1560: /**
1561: * Parses a list of layers in a layer grouping.
1562: *
1563: * @param layerGroup The layer group.
1564: *
1565: * @return List of String.
1566: */
1567: public static List parseLayerGroup(String layerGroup) {
1568: return readFlat(layerGroup, INNER_DELIMETER);
1569: }
1570:
1571: /**
1572: * Parses the requested layers given by the LAYERS request parameter and
1573: * looks up their corresponding FeatureTypeInfo objects in the server.
1574: *
1575: * @param request
1576: *
1577: * @return
1578: *
1579: * @throws WmsException
1580: */
1581: protected MapLayerInfo[] parseLayersParam(GetMapRequest request)
1582: throws WmsException {
1583: MapLayerInfo[] layers;
1584: String layersParam = getValue("LAYERS");
1585: List layerNames = readFlat(layersParam, INNER_DELIMETER);
1586: List realLayerNames = new ArrayList();
1587:
1588: // expand base layers, if there is any
1589: WMS wms = request.getWMS();
1590: if (wms.getBaseMapLayers() != null) {
1591: for (int i = 0; i < layerNames.size(); i++) {
1592: String layerGroup = (String) wms.getBaseMapLayers()
1593: .get(layerNames.get(i));
1594: if (layerGroup != null) {
1595: List layerGroupExpanded = parseLayerGroup(layerGroup);
1596: layerNames.remove(i);
1597: layerNames.addAll(i, layerGroupExpanded);
1598: }
1599: }
1600: }
1601:
1602: String layerName = null;
1603: Data catalog = request.getWMS().getData();
1604:
1605: String rawStyles = getValue("STYLES");
1606: List styleNames = readFlat(rawStyles, INNER_DELIMETER);
1607:
1608: int l_counter = 0;
1609: int s_counter = styleNames.size();
1610:
1611: // //
1612: // Expand the eventually WMS grouped layers into the same WMS Path
1613: // element
1614: // //
1615: for (Iterator it = layerNames.iterator(); it.hasNext();) {
1616: layerName = (String) it.next();
1617:
1618: Integer layerType = catalog.getLayerType(layerName);
1619:
1620: if (layerType == null) {
1621: // //
1622: // Search for grouped layers (attention: heavy process)
1623: // //
1624: String catalogLayerName = null;
1625:
1626: for (Iterator c_keys = catalog.getLayerNames()
1627: .iterator(); c_keys.hasNext();) {
1628: catalogLayerName = (String) c_keys.next();
1629:
1630: try {
1631: FeatureTypeInfo ftype = findFeatureLayer(
1632: request, catalogLayerName);
1633: String wmsPath = ftype.getWmsPath();
1634:
1635: if ((wmsPath != null)
1636: && wmsPath.matches(".*/" + layerName)) {
1637: realLayerNames.add(catalogLayerName);
1638: l_counter++;
1639:
1640: if (l_counter > s_counter) {
1641: rawStyles = ((rawStyles.length() > 0) ? (rawStyles + ",")
1642: : rawStyles)
1643: + ftype.getDefaultStyle()
1644: .getName();
1645: }
1646: }
1647: } catch (WmsException e_1) {
1648: try {
1649: CoverageInfo cv = findCoverageLayer(
1650: request, catalogLayerName);
1651: String wmsPath = cv.getWmsPath();
1652:
1653: if ((wmsPath != null)
1654: && wmsPath.matches(".*/"
1655: + layerName)) {
1656: realLayerNames.add(catalogLayerName);
1657: l_counter++;
1658:
1659: if (l_counter > s_counter) {
1660: rawStyles = ((rawStyles.length() > 0) ? (rawStyles + ",")
1661: : rawStyles)
1662: + cv.getDefaultStyle()
1663: .getName();
1664: }
1665: }
1666: } catch (WmsException e_2) {
1667: }
1668: }
1669: }
1670: } else {
1671: realLayerNames.add(layerName);
1672: l_counter++;
1673: }
1674: }
1675:
1676: // JD: only set if non-null since if the layer is an actual layer
1677: // (ie. not something matching wms path) with no style specified we
1678: // dont want to create an empty "STYLES" entry
1679: if ((rawStyles != null) && !"".equals(rawStyles.trim())) {
1680: kvpPairs.put("STYLES", rawStyles);
1681: }
1682:
1683: int layerCount = realLayerNames.size();
1684:
1685: if (layerCount == 0) {
1686: throw new WmsException("No LAYERS has been requested",
1687: getClass().getName());
1688: }
1689:
1690: layers = new MapLayerInfo[layerCount];
1691:
1692: for (int i = 0; i < layerCount; i++) {
1693: layerName = (String) layerNames.get(i);
1694: layers[i] = new MapLayerInfo();
1695:
1696: try {
1697: FeatureTypeInfo ftype = findFeatureLayer(request,
1698: layerName);
1699:
1700: layers[i].setFeature(ftype);
1701: } catch (WmsException e) {
1702: CoverageInfo cv = findCoverageLayer(request, layerName);
1703:
1704: layers[i].setCoverage(cv);
1705: }
1706: }
1707:
1708: return layers;
1709: }
1710:
1711: /**
1712: * DOCUMENT ME!
1713: *
1714: * @param request
1715: * @param layerName
1716: *
1717: * @return
1718: *
1719: * @throws WmsException
1720: * DOCUMENT ME!
1721: */
1722: public static FeatureTypeInfo findFeatureLayer(
1723: GetMapRequest request, String layerName)
1724: throws WmsException {
1725: Data catalog = request.getWMS().getData();
1726: FeatureTypeInfo ftype = null;
1727: Integer layerType = catalog.getLayerType(layerName);
1728:
1729: if (Data.TYPE_VECTOR != layerType) {
1730: throw new WmsException(new StringBuffer(layerName).append(
1731: ": no such layer on this server").toString(),
1732: "LayerNotDefined");
1733: } else {
1734: ftype = catalog.getFeatureTypeInfo(layerName);
1735: }
1736:
1737: return ftype;
1738: }
1739:
1740: public static CoverageInfo findCoverageLayer(GetMapRequest request,
1741: String layerName) throws WmsException {
1742: Data catalog = request.getWMS().getData();
1743: CoverageInfo cv = null;
1744: Integer layerType = catalog.getLayerType(layerName);
1745:
1746: if (Data.TYPE_RASTER != layerType) {
1747: throw new WmsException(new StringBuffer(layerName).append(
1748: ": no such layer on this server").toString(),
1749: "LayerNotDefined");
1750: } else {
1751: cv = catalog.getCoverageInfo(layerName);
1752: }
1753:
1754: return cv;
1755: }
1756:
1757: /**
1758: * This method gets the correct input stream for a URL. If the URL is a
1759: * http/https connection, the Accept-Encoding: gzip, deflate is added. It
1760: * the paramter is added, the response is checked to see if the response is
1761: * encoded in gzip, deflate or plain bytes. The correct input stream wrapper
1762: * is then selected and returned.
1763: *
1764: * This method was added as part of GEOS-420
1765: *
1766: * @param sldUrl
1767: * The url to the sld file
1768: * @return The InputStream used to validate and parse the SLD xml.
1769: * @throws IOException
1770: */
1771: private InputStream getInputStream(URL sldUrl) throws IOException {
1772: // Open the connection
1773: URLConnection conn = sldUrl.openConnection();
1774:
1775: // If it is the http or https scheme, then ask for gzip if the server
1776: // supports it.
1777: if (conn instanceof HttpURLConnection) {
1778: // Send the requested encoding to the remote server.
1779: conn.setRequestProperty("Accept-Encoding", "gzip, deflate");
1780: }
1781:
1782: // Conect to get the response headers
1783: conn.connect();
1784:
1785: // Return the correct inputstream
1786: // If the connection is a url, connection, check the response encoding.
1787: if (conn instanceof HttpURLConnection) {
1788: // Get the content encoding of the server response
1789: String encoding = conn.getContentEncoding();
1790:
1791: // If null, set it to a emtpy string
1792: if (encoding == null) {
1793: encoding = "";
1794: }
1795:
1796: if (encoding.equalsIgnoreCase("gzip")) {
1797: // For gzip input stream, use a GZIPInputStream
1798: return new GZIPInputStream(conn.getInputStream());
1799: } else if (encoding.equalsIgnoreCase("deflate")) {
1800: // If it is encoded as deflate, then select the inflater
1801: // inputstream.
1802: return new InflaterInputStream(conn.getInputStream(),
1803: new Inflater(true));
1804: } else {
1805: // Else read the raw bytes
1806: return conn.getInputStream();
1807: }
1808: } else {
1809: // Else read the raw bytes.
1810: return conn.getInputStream();
1811: }
1812: }
1813:
1814: /**
1815: * Filters the layers and styles if the user specified "layers=basemap".
1816: *
1817: *
1818: * @param layers
1819: * @param styles
1820: */
1821: public void filterBaseMap(Map layers, Map styles) {
1822: List currentLayers = null;
1823:
1824: try {
1825: // read flat can return non modifiable lists
1826: currentLayers = new ArrayList(readFlat(getValue("LAYERS"),
1827: ","));
1828: } catch (NullPointerException e) {
1829: // No layers defined. This is either wrong or they are listing the
1830: // layers
1831: // in an SLD document specified with the SLD= parameter
1832: LOGGER
1833: .fine("No layers defined. This is either wrong or they are listing the layers"
1834: + " in an SLD document specified with the SLD= parameter");
1835:
1836: return; // just continue ignoring the basemap option
1837: }
1838:
1839: List currentStyles = null;
1840:
1841: try {
1842: currentStyles = new ArrayList(readFlat(getValue("STYLES"),
1843: ","));
1844: } catch (NullPointerException e) {
1845: currentStyles = new ArrayList();
1846: }
1847:
1848: while (currentStyles.size() < currentLayers.size())
1849: currentStyles.add("");
1850:
1851: String[] baseLayers = (String[]) layers.keySet().toArray(
1852: new String[0]);
1853: boolean replacedOne = false;
1854:
1855: for (int i = 0; i < baseLayers.length; i++) {
1856: String blTitle = baseLayers[i];
1857: int index = currentLayers.indexOf(blTitle);
1858:
1859: if (index > -1) {
1860: replacedOne = true;
1861: LOGGER.info("Using BASEMAP layer: " + baseLayers[i]);
1862:
1863: // remove the 'basemap layer' from the currentLayers list
1864: currentLayers.remove(index);
1865:
1866: List blLayers = new ArrayList(readFlat((String) layers
1867: .get(blTitle), ","));
1868: currentLayers.addAll(index, blLayers);
1869: List blStyles = new ArrayList(readFlat((String) styles
1870: .get(blTitle), ","));
1871:
1872: while (blStyles.size() < blLayers.size())
1873: blStyles.add("");
1874:
1875: currentStyles.remove(index);
1876: currentStyles.addAll(index, blStyles);
1877: }
1878: }
1879:
1880: if (replacedOne) {
1881: kvpPairs.remove("LAYERS");
1882: kvpPairs.put("LAYERS", toStringList(currentLayers, ","));
1883: kvpPairs.remove("STYLES");
1884: kvpPairs.put("STYLES", toStringList(currentStyles, ","));
1885: }
1886: }
1887:
1888: private String toStringList(List currentLayers, String separator) {
1889: StringBuffer sb = new StringBuffer();
1890:
1891: for (Iterator it = currentLayers.iterator(); it.hasNext();) {
1892: String item = (String) it.next();
1893: sb.append(item);
1894:
1895: if (it.hasNext()) {
1896: sb.append(separator);
1897: }
1898: }
1899:
1900: return sb.toString();
1901: }
1902: }
|