001 //$HeadURL: https://svn.wald.intevation.org/svn/deegree/base/branches/2.3_testing/src/org/deegree/ogcwebservices/wms/operation/GetMap.java $ 002 /*---------------------------------------------------------------------------- 003 This file is part of deegree, http://deegree.org/ 004 Copyright (C) 2001-2009 by: 005 Department of Geography, University of Bonn 006 and 007 lat/lon GmbH 008 009 This library is free software; you can redistribute it and/or modify it under 010 the terms of the GNU Lesser General Public License as published by the Free 011 Software Foundation; either version 2.1 of the License, or (at your option) 012 any later version. 013 This library is distributed in the hope that it will be useful, but WITHOUT 014 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 015 FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more 016 details. 017 You should have received a copy of the GNU Lesser General Public License 018 along with this library; if not, write to the Free Software Foundation, Inc., 019 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 020 021 Contact information: 022 023 lat/lon GmbH 024 Aennchenstr. 19, 53177 Bonn 025 Germany 026 http://lat-lon.de/ 027 028 Department of Geography, University of Bonn 029 Prof. Dr. Klaus Greve 030 Postfach 1147, 53001 Bonn 031 Germany 032 http://www.geographie.uni-bonn.de/deegree/ 033 034 e-mail: info@deegree.org 035 ----------------------------------------------------------------------------*/ 036 package org.deegree.ogcwebservices.wms.operation; 037 038 import static org.deegree.crs.components.Unit.DEGREE; 039 040 import java.awt.Color; 041 import java.io.Serializable; 042 import java.io.UnsupportedEncodingException; 043 import java.net.MalformedURLException; 044 import java.net.URL; 045 import java.net.URLDecoder; 046 import java.net.URLEncoder; 047 import java.util.ArrayList; 048 import java.util.Arrays; 049 import java.util.HashMap; 050 import java.util.Iterator; 051 import java.util.LinkedList; 052 import java.util.List; 053 import java.util.Map; 054 import java.util.StringTokenizer; 055 056 import org.deegree.datatypes.values.Values; 057 import org.deegree.framework.log.ILogger; 058 import org.deegree.framework.log.LoggerFactory; 059 import org.deegree.framework.util.CharsetUtils; 060 import org.deegree.framework.util.ColorUtils; 061 import org.deegree.framework.util.IDGenerator; 062 import org.deegree.framework.util.MimeTypeMapper; 063 import org.deegree.framework.util.NetWorker; 064 import org.deegree.framework.util.StringTools; 065 import org.deegree.framework.xml.Marshallable; 066 import org.deegree.framework.xml.NamespaceContext; 067 import org.deegree.framework.xml.XMLFragment; 068 import org.deegree.framework.xml.XMLParsingException; 069 import org.deegree.framework.xml.XMLTools; 070 import org.deegree.graphics.sld.SLDFactory; 071 import org.deegree.graphics.sld.StyledLayerDescriptor; 072 import org.deegree.i18n.Messages; 073 import org.deegree.model.crs.CRSFactory; 074 import org.deegree.model.crs.CoordinateSystem; 075 import org.deegree.model.crs.UnknownCRSException; 076 import org.deegree.model.spatialschema.Envelope; 077 import org.deegree.model.spatialschema.GMLGeometryAdapter; 078 import org.deegree.model.spatialschema.GeometryFactory; 079 import org.deegree.ogcbase.CommonNamespaces; 080 import org.deegree.ogcbase.InvalidGMLException; 081 import org.deegree.ogcwebservices.InconsistentRequestException; 082 import org.deegree.ogcwebservices.OGCWebServiceException; 083 import org.deegree.ogcwebservices.wmps.operation.PrintMap; 084 import org.deegree.ogcwebservices.wms.InvalidCRSException; 085 import org.deegree.ogcwebservices.wms.InvalidFormatException; 086 import org.deegree.ogcwebservices.wms.InvalidSRSException; 087 import org.deegree.ogcwebservices.wms.configuration.AbstractDataSource; 088 import org.deegree.ogcwebservices.wms.configuration.RemoteWMSDataSource; 089 import org.w3c.dom.Document; 090 import org.w3c.dom.Element; 091 092 /** 093 * This interface describes the access to the parameters of a GeMap request. It is excpected that there are two kinds of 094 * request. The first is the 'normal' HTTP GET request with name-value-pair enconding and the second is a HTTP POST 095 * request containing a SLD. 096 * <p> 097 * </p> 098 * Even it is possible to access the values of a HTTP GET request throught their bean accessor methods the request shall 099 * be mapped to a SLD data structure that is accessible using the <tt>getSLD()</tt>. 100 * 101 * @author <a href="mailto:poth@lat-lon.de">Andreas Poth</a> 102 * @author last edited by: $Author:wanhoff$ 103 * 104 * @version $Revision: 18195 $, $Date:20.03.2007$ 105 */ 106 public class GetMap extends WMSRequestBase { 107 108 private static final long serialVersionUID = 887256882709344021L; 109 110 private static final ILogger LOG = LoggerFactory.getLogger( GetMap.class ); 111 112 private Values elevation = null; 113 114 private Values time = null; 115 116 private Map<String, Values> sampleDimension = null; 117 118 private List<Layer> layers = null; 119 120 private Color bGColor = null; 121 122 private Envelope boundingBox = null; 123 124 private String exceptions = null; 125 126 private String format = null; 127 128 private String srs = null; 129 130 private StyledLayerDescriptor sld = null; 131 132 private URL sLD_URL = null; 133 134 private URL wFS_URL = null; 135 136 private boolean transparency = false; 137 138 private int height = 0; 139 140 private int width = 0; 141 142 private DimensionValues dimTime; 143 144 private DimensionValues dimElev; 145 146 /** 147 * A list to transport messages to the WMSHandler that need to be set in HTTP headers. 148 */ 149 public LinkedList<String> warningHeaders = new LinkedList<String>(); 150 151 /** 152 * creates a <tt>WTSGetViewRequest</tt> from a set of parameters and builds up the complete SLD. 153 * 154 * @return an instance of <tt>GetMapRequest</tt> 155 * @param version 156 * Request version. 157 * @param layers 158 * list of one or more map layers. Optional if SLD parameter is present. Contains list of one rendering 159 * style per requested layer. Optional if SLD parameter is present. 160 * @param elevation 161 * Elevation of layer desired. 162 * @param sampleDimension 163 * Value of other dimensions as appropriate. 164 * @param format 165 * Output format of map. 166 * @param width 167 * Width in pixels of map picture. 168 * @param height 169 * Height in pixels of map picture. 170 * @param srs 171 * the requested Spatial Reference System. 172 * @param boundingBox 173 * Bounding box corners (lower left, upper right) in SRS units. 174 * @param transparency 175 * Background transparency of map. 176 * @param bGColor 177 * Hexadecimal red-green-blue color value for the background color. 178 * @param exceptions 179 * The format in which exceptions are to be reported by the WMS. 180 * @param time 181 * Time value of layer desired 182 * @param sld 183 * Styled Layer Descriptor 184 * @param id 185 * an unique ID of the request 186 * @param sldURL 187 * @param vendorSpecificParameter 188 * Vendor Specific Parameter 189 */ 190 public static GetMap create( String version, String id, Layer[] layers, Values elevation, 191 Map<String, Values> sampleDimension, String format, int width, int height, String srs, 192 Envelope boundingBox, boolean transparency, Color bGColor, String exceptions, 193 Values time, URL sldURL, StyledLayerDescriptor sld, 194 Map<String, String> vendorSpecificParameter ) { 195 return new GetMap( version, id, layers, elevation, sampleDimension, format, width, height, srs, boundingBox, 196 transparency, bGColor, exceptions, time, sldURL, sld, vendorSpecificParameter ); 197 } 198 199 /** 200 * creates a getMap request for requesting a cascaded remote WMS considering the getMap request and the 201 * filterconditions defined in the submitted <tt>DataSource</tt> object The request will be encapsualted within a 202 * <tt>OGCWebServiceEvent</tt>. 203 * 204 * @param ds 205 * @param request 206 * @param style 207 * @param layer 208 * @return GetMap request object 209 */ 210 public static GetMap createGetMapRequest( AbstractDataSource ds, GetMap request, String style, String layer ) { 211 212 GetMap gmr = ( (RemoteWMSDataSource) ds ).getGetMapRequest(); 213 214 String format = request.getFormat(); 215 216 if ( gmr != null && !"%default%".equals( gmr.getFormat() ) ) { 217 format = gmr.getFormat(); 218 } 219 220 GetMap.Layer[] lys = null; 221 lys = new GetMap.Layer[1]; 222 223 if ( style != null ) { 224 lys[0] = PrintMap.createLayer( layer, style ); 225 } else { 226 lys[0] = PrintMap.createLayer( layer, "$DEFAULT" ); 227 } 228 if ( gmr != null && gmr.getLayers() != null && !( gmr.getLayers()[0].getName().equals( "%default%" ) ) ) { 229 lys = gmr.getLayers(); 230 } 231 Color bgColor = request.getBGColor(); 232 if ( gmr != null && gmr.getBGColor() != null ) { 233 bgColor = gmr.getBGColor(); 234 } 235 Values time = request.getTime(); 236 if ( gmr != null && gmr.getTime() != null ) { 237 time = gmr.getTime(); 238 } 239 240 Map<String, String> vendorSpecificParameter = request.getVendorSpecificParameters(); 241 if ( gmr != null && gmr.getVendorSpecificParameters() != null && gmr.getVendorSpecificParameters().size() > 0 ) { 242 vendorSpecificParameter.putAll( gmr.getVendorSpecificParameters() ); 243 } 244 String version = "1.1.0"; 245 if ( gmr != null && gmr.getVersion() != null ) { 246 version = gmr.getVersion(); 247 } 248 249 Values elevation = request.getElevation(); 250 if ( gmr != null && gmr.getElevation() != null ) { 251 elevation = gmr.getElevation(); 252 } 253 Map<String, Values> sampleDim = null; 254 if ( gmr != null && gmr.getSampleDimension() != null ) { 255 sampleDim = gmr.getSampleDimension(); 256 } 257 258 boolean tranparency = false; 259 if ( gmr != null ) { 260 tranparency = gmr.getTransparency(); 261 } 262 263 // now filter out the unwanted vendor specific parameters and put in 264 // the wanted additional ones 265 Map<String, String> vsp = new HashMap<String, String>( 10 ); 266 vsp.putAll( ( (RemoteWMSDataSource) ds ).getAddedParameters() ); 267 for ( String name : ( (RemoteWMSDataSource) ds ).getPassedParameters() ) { 268 if ( vendorSpecificParameter.containsKey( name ) ) { 269 vsp.put( name, vendorSpecificParameter.get( name ).toString() ); 270 } 271 } 272 273 IDGenerator idg = IDGenerator.getInstance(); 274 gmr = GetMap.create( version, "" + idg.generateUniqueID(), lys, elevation, sampleDim, format, 275 request.getWidth(), request.getHeight(), request.getSrs(), request.getBoundingBox(), 276 tranparency, bgColor, request.getExceptions(), time, null, null, vsp ); 277 278 return gmr; 279 } 280 281 /** 282 * creates a <tt>GetMapRequest</tt> from a <tt>HashMap</tt> that contains the request parameters as key-value-pairs. 283 * Keys are expected to be in upper case notation. 284 * 285 * @param model 286 * <tt>HashMap</tt> containing the request parameters 287 * @return an instance of <tt>GetMapRequest</tt> 288 * @throws InconsistentRequestException 289 * @throws XMLParsingException 290 * @throws MalformedURLException 291 */ 292 public static GetMap create( Map<String, String> model ) 293 throws InconsistentRequestException, XMLParsingException, MalformedURLException { 294 295 LOG.logDebug( "Request parameters: " + model ); 296 297 // use model.remove(..) so at the end of the method the vendor 298 // specific parameters remains in the model HashMap 299 model.remove( "REQUEST" ); 300 301 // Version 302 String version = model.remove( "VERSION" ); 303 304 if ( version == null ) { 305 version = model.remove( "WMTVER" ); 306 } 307 308 if ( version == null ) { 309 throw new InconsistentRequestException( "VERSION-value must be set" ); 310 } 311 312 // LAYERS & STYLES & SLD (URL, XML) 313 StyledLayerDescriptor sld = null; 314 String sld_body = model.remove( "SLD_BODY" ); 315 String sld_urlstring = model.remove( "SLD" ); 316 317 // The SLD is complete in the Maprequest 318 URL sLD_URL = null; 319 320 if ( sld_body != null ) { 321 try { 322 sld_body = URLDecoder.decode( sld_body, CharsetUtils.getSystemCharset() ); 323 sld = SLDFactory.createSLD( sld_body ); 324 } catch ( Exception ee ) { 325 throw new XMLParsingException( "Could not decode SLD_BODY: " + ee.toString() ); 326 } 327 } else if ( sld_urlstring != null ) { 328 // The SLD is as url in the Maprequest 329 sLD_URL = new URL( sld_urlstring ); 330 331 try { 332 sld = SLDFactory.createSLD( sLD_URL ); 333 } catch ( Exception ioex ) { 334 ioex.printStackTrace(); 335 LOG.logError( ioex.getMessage(), ioex ); 336 throw new InconsistentRequestException( "IOException occured during the access " 337 + "to the SLD-URL. Wrong URL? Server down?" + ioex.getMessage() ); 338 } 339 } 340 341 // LAYERS & STYLES 342 String layersstring = model.remove( "LAYERS" ); 343 if ( ( layersstring == null || layersstring.trim().length() == 0 ) && ( sld == null ) ) { 344 throw new InconsistentRequestException( "At least one layer must be defined within a GetMap request" ); 345 } 346 String stylesstring = model.remove( "STYLES" ); 347 348 // normalize styles parameter 349 if ( stylesstring == null ) { 350 stylesstring = ""; 351 } 352 if ( stylesstring.startsWith( "," ) ) { 353 stylesstring = "$DEFAULT" + stylesstring; 354 } 355 356 stylesstring = StringTools.replace( stylesstring, ",,", ",$DEFAULT,", true ); 357 358 if ( stylesstring.endsWith( "," ) ) { 359 stylesstring = stylesstring + "$DEFAULT"; 360 } 361 362 List<String> layers = StringTools.toList( layersstring, ",", false ); 363 364 List<String> styles = null; 365 if ( stylesstring.length() == 0 ) { 366 styles = new ArrayList<String>( layers.size() ); 367 for ( int i = 0; i < layers.size(); i++ ) { 368 styles.add( "$DEFAULT" ); 369 } 370 } else { 371 styles = StringTools.toList( stylesstring, ",", false ); 372 } 373 374 // At last, build up the Layer object 375 GetMap.Layer[] ls = new GetMap.Layer[layers.size()]; 376 377 if ( styles.size() != layers.size() ) { 378 throw new InconsistentRequestException( Messages.getMessage( "WMS_WRONG_STYLES" ) ); 379 } 380 381 for ( int i = 0; i < layers.size(); i++ ) { 382 try { 383 String l = URLDecoder.decode( layers.get( i ), CharsetUtils.getSystemCharset() ); 384 ls[i] = GetMap.createLayer( l, styles.get( i ) ); 385 } catch ( UnsupportedEncodingException e2 ) { 386 e2.printStackTrace(); 387 } 388 } 389 390 // FORMAT 391 String format = model.remove( "FORMAT" ); 392 if ( format == null ) { 393 throw new InconsistentRequestException( "FORMAT-value must be set" ); 394 } 395 try { 396 format = URLDecoder.decode( format, CharsetUtils.getSystemCharset() ); 397 } catch ( UnsupportedEncodingException e1 ) { 398 e1.printStackTrace(); 399 } 400 if ( !MimeTypeMapper.isKnownImageType( format ) ) { 401 throw new InvalidFormatException( format + " is not a valid image/result format" ); 402 } 403 404 // width 405 String tmp = model.remove( "WIDTH" ); 406 if ( tmp == null ) { 407 throw new InconsistentRequestException( "WIDTH must be set" ); 408 } 409 int width = 0; 410 try { 411 width = Integer.parseInt( tmp ); 412 } catch ( Exception e ) { 413 throw new InconsistentRequestException( "WIDTH must be a valid integer number" ); 414 } 415 416 // height 417 tmp = model.remove( "HEIGHT" ); 418 if ( tmp == null ) { 419 throw new InconsistentRequestException( "HEIGHT must be set" ); 420 } 421 int height = 0; 422 try { 423 height = Integer.parseInt( tmp ); 424 } catch ( Exception e ) { 425 throw new InconsistentRequestException( "HEIGHT must be a valid integer number" ); 426 } 427 428 double minx, miny, maxx, maxy; 429 Envelope boundingBox = null; 430 String srs; 431 boolean isLongLat = false; 432 433 boolean is130 = false; 434 435 if ( "1.3.0".compareTo( version ) <= 0 ) { 436 is130 = true; 437 438 // SRS or rather CRS 439 srs = model.remove( "CRS" ); 440 if ( srs == null ) { 441 throw new InvalidCRSException( "CRS-value must be set" ); 442 } 443 444 // check for geographic coordinate system 445 try { 446 CoordinateSystem crs = CRSFactory.create( srs ); 447 isLongLat = crs.getAxisUnits()[0].equals( DEGREE ) && srs.toLowerCase().startsWith( "epsg" ); 448 } catch ( UnknownCRSException e ) { 449 LOG.logDebug( e.getLocalizedMessage(), e ); 450 throw new InvalidCRSException( srs ); 451 } 452 453 } else { 454 // SRS 455 srs = model.remove( "SRS" ); 456 if ( srs == null ) { 457 throw new InvalidSRSException( "SRS-value must be set" ); 458 } 459 460 try { 461 // check for crs validity - yes, this method is bad. Is there a better one? 462 CRSFactory.create( srs ); 463 } catch ( UnknownCRSException e ) { 464 LOG.logDebug( e.getLocalizedMessage(), e ); 465 if ( is130 ) { 466 throw new InvalidCRSException( Messages.getMessage( "WMS_UNKNOWN_CRS", srs ) ); 467 } 468 throw new InvalidSRSException( Messages.getMessage( "WMS_UNKNOWN_CRS", srs ) ); 469 } 470 471 } 472 473 // BBOX 474 String boxstring = model.remove( "BBOX" ); 475 if ( boxstring == null ) { 476 throw new InconsistentRequestException( "BBOX-value must be set" ); 477 } 478 StringTokenizer st = new StringTokenizer( boxstring, "," ); 479 480 if ( isLongLat ) { 481 // parse first y, then x 482 String s = st.nextToken().replace( ' ', '+' ); 483 miny = Double.parseDouble( s ); 484 s = st.nextToken().replace( ' ', '+' ); 485 minx = Double.parseDouble( s ); 486 s = st.nextToken().replace( ' ', '+' ); 487 maxy = Double.parseDouble( s ); 488 s = st.nextToken().replace( ' ', '+' ); 489 maxx = Double.parseDouble( s ); 490 } else { 491 // old method 492 String s = st.nextToken().replace( ' ', '+' ); 493 minx = Double.parseDouble( s ); 494 s = st.nextToken().replace( ' ', '+' ); 495 miny = Double.parseDouble( s ); 496 s = st.nextToken().replace( ' ', '+' ); 497 maxx = Double.parseDouble( s ); 498 s = st.nextToken().replace( ' ', '+' ); 499 maxy = Double.parseDouble( s ); 500 501 } 502 503 // check for consistency 504 if ( minx >= maxx ) { 505 throw new InvalidFormatException( "minx must be lower than maxx" ); 506 } 507 508 if ( miny >= maxy ) { 509 throw new InvalidFormatException( "miny must be lower than maxy" ); 510 } 511 512 boundingBox = GeometryFactory.createEnvelope( minx, miny, maxx, maxy, null ); 513 514 // TRANSPARENCY 515 boolean transparency = false; 516 String tp = model.remove( "TRANSPARENT" ); 517 if ( tp != null ) { 518 transparency = tp.toUpperCase().trim().equals( "TRUE" ); 519 } 520 521 String mime = MimeTypeMapper.toMimeType( format ); 522 if ( mime.equals( "image/jpg" ) || mime.equals( "image/jpeg" ) || mime.equals( "image/bmp" ) 523 || mime.equals( "image/tif" ) || mime.equals( "image/tiff" ) ) { 524 transparency = false; 525 } 526 527 // BGCOLOR 528 tmp = model.remove( "BGCOLOR" ); 529 Color bgColor = Color.white; 530 if ( tmp != null ) { 531 bgColor = Color.decode( tmp ); 532 } 533 534 // EXCEPTIONS 535 String exceptions = model.remove( "EXCEPTIONS" ); 536 537 if ( exceptions == null ) { 538 if ( is130 ) { 539 exceptions = "XML"; 540 } else { 541 exceptions = "application/vnd.ogc.se_xml"; 542 } 543 } 544 545 // WFS 546 /* 547 * URL wFS_URL = null; if ((String)model.get( "WFS" ) != null) { wFS_URL = new URL((String)model.remove( "WFS" 548 * )); } 549 */ 550 551 // ID 552 String id = model.remove( "ID" ); 553 if ( id == null ) { 554 throw new InconsistentRequestException( "ID-value must be set" ); 555 } 556 557 String dim = model.remove( "TIME" ); 558 DimensionValues dimTime = dim == null ? null : new DimensionValues( dim ); 559 dim = model.remove( "ELEVATION" ); 560 DimensionValues dimElev = dim == null ? null : new DimensionValues( dim ); 561 562 // VendorSpecificParameter; because all defined parameters has been 563 // removed 564 // from the model the vendorSpecificParameters are what left 565 Map<String, String> vendorSpecificParameter = new HashMap<String, String>(); 566 for ( Object str : model.keySet() ) { 567 vendorSpecificParameter.put( str.toString(), model.get( str ).toString() ); 568 } 569 570 return new GetMap( version, id, ls, format, width, height, srs, boundingBox, transparency, bgColor, exceptions, 571 sLD_URL, sld, vendorSpecificParameter, dimTime, dimElev ); 572 } 573 574 /** 575 * creates a <tt>GetMapRequest</tt> from its XML representation as defined in SLD 1.0.0 specification 576 * 577 * <p> 578 * This method does not yet cope with 1.3.0. 579 * </p> 580 * 581 * @param id 582 * an unique id of the request 583 * @param doc 584 * the document tree 585 * @return an instance of <tt>GetMapRequest</tt> 586 * @throws XMLParsingException 587 * @throws InvalidSRSException 588 * @throws InconsistentRequestException 589 */ 590 public static GetMap create( String id, Document doc ) 591 throws XMLParsingException, InvalidSRSException, InconsistentRequestException { 592 String PSLD = CommonNamespaces.SLD_PREFIX + ':'; 593 594 Element root = doc.getDocumentElement(); 595 NamespaceContext nsContext = CommonNamespaces.getNamespaceContext(); 596 Element sldElem = (Element) XMLTools.getRequiredNode( root, PSLD + "StyledLayerDescriptor", nsContext ); 597 XMLFragment xml = new XMLFragment(); 598 xml.setRootElement( sldElem ); 599 600 StyledLayerDescriptor sld = SLDFactory.createSLD( xml ); 601 String version = root.getAttribute( "version" ); 602 603 boolean is130 = false; 604 605 if ( "1.3.0".compareTo( version ) <= 0 ) { 606 is130 = true; 607 } 608 609 Element bboxElem = (Element) XMLTools.getRequiredNode( root, PSLD + "BoundingBox", nsContext ); 610 611 Envelope bbox; 612 try { 613 bbox = GMLGeometryAdapter.wrapBox( bboxElem, null ); 614 // check for consistency 615 if ( bbox.getMin().getX() >= bbox.getMax().getX() ) { 616 throw new InvalidFormatException( "minx must be lower than maxx" ); 617 } 618 619 if ( bbox.getMin().getY() >= bbox.getMax().getY() ) { 620 throw new InvalidFormatException( "miny must be lower than maxy" ); 621 } 622 } catch ( InvalidGMLException e ) { 623 LOG.logDebug( e.getLocalizedMessage(), e ); 624 if ( bboxElem == null ) { 625 throw new InconsistentRequestException( Messages.getMessage( "WMS_NO_BOUNDINGBOX" ) ); 626 } 627 if ( is130 ) { 628 throw new InvalidCRSException( Messages.getMessage( "WMS_UNKNOWN_CRS", 629 bboxElem.getAttribute( "srsName" ) ) ); 630 } 631 throw new InvalidSRSException( Messages.getMessage( "WMS_UNKNOWN_CRS", bboxElem.getAttribute( "srsName" ) ) ); 632 } catch ( UnknownCRSException e ) { 633 LOG.logDebug( e.getLocalizedMessage(), e ); 634 if ( bboxElem == null ) { 635 throw new InconsistentRequestException( Messages.getMessage( "WMS_NO_BOUNDINGBOX" ) ); 636 } 637 if ( is130 ) { 638 throw new InvalidCRSException( Messages.getMessage( "WMS_UNKNOWN_CRS", 639 bboxElem.getAttribute( "srsName" ) ) ); 640 } 641 throw new InvalidSRSException( Messages.getMessage( "WMS_UNKNOWN_CRS", bboxElem.getAttribute( "srsName" ) ) ); 642 } 643 644 String srs = bbox.getCoordinateSystem().getIdentifier().toString(); 645 646 Element output = (Element) XMLTools.getRequiredNode( root, PSLD + "Output", nsContext ); 647 648 boolean transparent = XMLTools.getNodeAsBoolean( output, PSLD + "Transparent", nsContext, false ); 649 650 int width = 0; 651 int height = 0; 652 try { 653 Element node = (Element) XMLTools.getRequiredNode( output, PSLD + "Size", nsContext ); 654 width = XMLTools.getRequiredNodeAsInt( node, PSLD + "Width", nsContext ); 655 height = XMLTools.getRequiredNodeAsInt( node, PSLD + "Height", nsContext ); 656 } catch ( XMLParsingException e ) { 657 throw new InconsistentRequestException( Messages.getMessage( "WMS_REQUEST_SIZE" ) ); 658 } 659 660 String exception = XMLTools.getNodeAsString( output, PSLD + "Exceptions", nsContext, 661 "application/vnd.ogc.se_xml" ); 662 String sbgColor = XMLTools.getNodeAsString( output, PSLD + "BGColor", nsContext, "#FFFFFF" ); 663 Color bgColor = Color.decode( sbgColor ); 664 665 String format = XMLTools.getRequiredNodeAsString( output, PSLD + "Format", nsContext ); 666 if ( format == null ) { 667 throw new InconsistentRequestException( "FORMAT-value must be set" ); 668 } 669 try { 670 format = URLDecoder.decode( format, CharsetUtils.getSystemCharset() ); 671 } catch ( UnsupportedEncodingException e1 ) { 672 e1.printStackTrace(); 673 } 674 if ( !MimeTypeMapper.isKnownImageType( format ) ) { 675 throw new InvalidFormatException( format + " is not a valid image/result format" ); 676 } 677 678 GetMap req = new GetMap( version, id, null, null, null, format, width, height, srs, bbox, transparent, bgColor, 679 exception, null, null, sld, null ); 680 681 return req; 682 } 683 684 /** 685 * Creates a new GetMapRequest object. 686 * 687 * @param version 688 * @param id 689 * @param layers 690 * @param elevation 691 * @param sampleDimension 692 * @param format 693 * @param width 694 * @param height 695 * @param srs 696 * @param boundingBox 697 * @param transparency 698 * @param bGColor 699 * @param exceptions 700 * @param time 701 * @param sldURL 702 * @param sld 703 * @param vendorSpecificParameter 704 * 705 */ 706 GetMap( String version, String id, Layer[] layers, Values elevation, Map<String, Values> sampleDimension, 707 String format, int width, int height, String srs, Envelope boundingBox, boolean transparency, 708 Color bGColor, String exceptions, Values time, URL sldURL, StyledLayerDescriptor sld, 709 Map<String, String> vendorSpecificParameter ) { 710 super( version, id, vendorSpecificParameter ); 711 712 if ( layers != null ) { 713 this.layers = Arrays.asList( layers ); 714 } else { 715 this.layers = new ArrayList<Layer>(); 716 } 717 this.sld = sld; 718 this.elevation = elevation; 719 this.sampleDimension = sampleDimension; 720 this.format = format; 721 this.width = width; 722 this.height = height; 723 this.srs = srs; 724 this.boundingBox = boundingBox; 725 this.transparency = transparency; 726 this.bGColor = bGColor; 727 this.exceptions = exceptions; 728 this.time = time; 729 this.sLD_URL = sldURL; 730 // setWFS_URL( wFS_URL ); 731 } 732 733 /** 734 * @param version 735 * @param id 736 * @param ls 737 * @param format 738 * @param width 739 * @param height 740 * @param srs 741 * @param boundingBox 742 * @param transparency 743 * @param bgColor 744 * @param exceptions 745 * @param sld_url 746 * @param sld 747 * @param vendorSpecificParameter 748 * @param dimTime 749 * @param dimElev 750 */ 751 public GetMap( String version, String id, Layer[] ls, String format, int width, int height, String srs, 752 Envelope boundingBox, boolean transparency, Color bgColor, String exceptions, URL sld_url, 753 StyledLayerDescriptor sld, Map<String, String> vendorSpecificParameter, DimensionValues dimTime, 754 DimensionValues dimElev ) { 755 this( version, id, ls, null, null, format, width, height, srs, boundingBox, transparency, bgColor, exceptions, 756 null, sld_url, sld, vendorSpecificParameter ); 757 this.dimTime = dimTime; 758 this.dimElev = dimElev; 759 } 760 761 /** 762 * The FORMAT parameter specifies the output format of the response to an operation. 763 * <p> 764 * </p> 765 * An OGC Web CapabilitiesService may offer only a subset of the formats known for that type of operation, but the 766 * server shall advertise in its Capabilities XML those formats it does support and shall accept requests for any 767 * format it advertises. A CapabilitiesService Instance may optionally offer a new format not previously offered by 768 * other instances, with the recognition that clients are not required to accept or process an unknown format. If a 769 * request contains a Format not offered by a particular server, the server shall throw a CapabilitiesService 770 * Exception (with code "InvalidFormat"). 771 * 772 * @return the output format 773 */ 774 public String getFormat() { 775 return format; 776 } 777 778 /** 779 * sets the format 780 * 781 * @param format 782 * the requested output-format 783 */ 784 public void setFormat( String format ) { 785 this.format = format; 786 } 787 788 /** 789 * The required LAYERS parameter lists the map layer(s) to be returned by this GetMap request. The value of the 790 * LAYERS parameter is a comma-separated list of one or more valid layer names. Allowed layer names are the 791 * character data content of any <Layer><Name> element in the Capabilities XML. 792 * <p> 793 * </p> 794 * A WMS shall render the requested layers by drawing the leftmost in the list bottommost, the next one over that, 795 * and so on. 796 * <p> 797 * </p> 798 * Each layer is associated to a style. Styles are also is encoded as a comma- seperated list within the GetMap 799 * request. 800 * <p> 801 * </p> 802 * The required STYLES parameter lists the style in which each layer is to be rendered. There is a one-to-one 803 * correspondence between the values in the LAYERS parameter and the values in the STYLES parameter. Because of this 804 * layer-style combinations are returned coupled within an array of Layer- objects. Each map in the list of LAYERS 805 * is drawn using the corresponding style in the same position in the list of STYLES. Each style Name shall be one 806 * that was defined in the <Name> element of a <Style> element that is either directly contained within, or 807 * inherited by, the associated <Layer> element in Capabilities XML. 808 * 809 * @return The required LAYERS 810 */ 811 public Layer[] getLayers() { 812 return layers.toArray( new Layer[layers.size()] ); 813 } 814 815 /** 816 * adds the <Layer> 817 * 818 * @param layers 819 */ 820 public void addLayers( Layer layers ) { 821 this.layers.add( layers ); 822 } 823 824 /** 825 * sets the <Layer> 826 * 827 * @param layers 828 * a set of layer 829 */ 830 public void setLayers( Layer[] layers ) { 831 this.layers.clear(); 832 833 if ( layers != null ) { 834 for ( int i = 0; i < layers.length; i++ ) { 835 this.layers.add( layers[i] ); 836 } 837 } 838 } 839 840 /** 841 * The required SRS parameter states which Spatial Reference System applies to the values in the BBOX parameter. The 842 * value of the SRS parameter shall be one of the values defined in the character data section of an <SRS> element 843 * defined or inherited by the requested layer. The same SRS applies to all layers in a single request. 844 * <p> 845 * </p> 846 * If the WMS server has declared SRS=NONE for a Layer, as discussed in the Basic CapabilitiesService Elements 847 * section, then the Layer does not have a well-defined spatial reference system and should not be shown in 848 * conjunction with other layers. The Client shall specify SRS=NONE (case-insensitive) in the GetMap request and the 849 * Server may issue a CapabilitiesService Exception otherwise. 850 * 851 * @return the spatial reference system 852 */ 853 public String getSrs() { 854 return srs; 855 } 856 857 /** 858 * sets the srs 859 * 860 * @param srs 861 * the spatial reference system 862 */ 863 public void setSrs( String srs ) { 864 this.srs = srs; 865 } 866 867 /** 868 * The required BBOX parameter allows a Client to request a particular Bounding Box. Bounding Boxes are defined in 869 * the Basic CapabilitiesService Elements section. The value of the BBOX parameter in a GetMap request is a list of 870 * comma-separated numbers of the form "minx,miny,maxx,maxy". 871 * <p> 872 * </p> 873 * If the WMS server has declared that a Layer is not subsettable then the Client shall specify exactly the declared 874 * Bounding Box values in the GetMap request and the Server may issue a CapabilitiesService Exception otherwise. 875 * 876 * @return the bounding box 877 */ 878 public Envelope getBoundingBox() { 879 return boundingBox; 880 } 881 882 /** 883 * @see #getBoundingBox() 884 * @param boundingBox 885 */ 886 public void setBoundingBox( Envelope boundingBox ) { 887 this.boundingBox = boundingBox; 888 } 889 890 /** 891 * WIDTH specifies the number of pixels to be used between the minimum and maximum X values (inclusive) in the BBOX 892 * parameter. The returned picture, regardless of its return format, shall have exactly the specified width and 893 * height in pixels. In the case where the aspect ratio of the BBOX and the ratio width/height are different, the 894 * WMS shall stretch the returned map so that the resulting pixels could themselves be rendered in the aspect ratio 895 * of the BBOX. In other words, it should be possible using this definition to request a map for a device whose 896 * output pixels are themselves non-square, or to stretch a map into an image area of a different aspect ratio. 897 * 898 * @return the width 899 */ 900 public int getWidth() { 901 return width; 902 } 903 904 /** 905 * @see #getWidth() 906 * @param width 907 */ 908 public void setWidth( int width ) { 909 this.width = width; 910 } 911 912 /** 913 * HEIGHT specifies the number of pixels between the minimum and maximum Y values. The returned picture, regardless 914 * of its return format, shall have exactly the specified width and height in pixels. In the case where the aspect 915 * ratio of the BBOX and the ratio width/height are different, the WMS shall stretch the returned map so that the 916 * resulting pixels could themselves be rendered in the aspect ratio of the BBOX. In other words, it should be 917 * possible using this definition to request a map for a device whose output pixels are themselves non-square, or to 918 * stretch a map into an image area of a different aspect ratio. 919 * 920 * @return the height 921 */ 922 public int getHeight() { 923 return height; 924 } 925 926 /** 927 * @see #getHeight() 928 * @param height 929 */ 930 public void setHeight( int height ) { 931 this.height = height; 932 } 933 934 /** 935 * The optional TRANSPARENT parameter specifies whether the map background is to be made transparent or not. 936 * TRANSPARENT can take on two values, "TRUE" or "FALSE". The default value is FALSE if this parameter is absent 937 * from the request. 938 * <p> 939 * </p> 940 * The ability to return pictures drawn with transparent pixels allows results of different Map requests to be 941 * overlaid, producing a composite map. It is strongly recommended that every WMS offer a format that provides 942 * transparency for layers which could sensibly be overlaid above others. 943 * 944 * @return the transparency setting 945 */ 946 public boolean getTransparency() { 947 return transparency; 948 } 949 950 /** 951 * The optional BGCOLOR parameter specifies the color to be used as the background of the map. The general format of 952 * BGCOLOR is a hexadecimal encoding of an RGB value where two hexadecimal characters are used for each of Red, 953 * Green, and Blue color values. The values can range between 00 and FF for each (0 and 255, base 10). The format is 954 * 0xRRGGBB; either upper or lower case characters are allowed for RR, GG, and BB values. The "0x" prefix shall have 955 * a lower case 'x'. The default value is 0xFFFFFF (corresponding to the color white) if this parameter is absent 956 * from the request. 957 * 958 * @return the background color 959 */ 960 public Color getBGColor() { 961 return bGColor; 962 } 963 964 /** 965 * The optional EXCEPTIONS parameter states the manner in which errors are to be reported to the client. The default 966 * value is application/vnd.ogc.se_xml if this parameter is absent from the request. 967 * <p> 968 * </p> 969 * A Web Map CapabilitiesService shall offer one or more of the following exception reporting formats by listing 970 * them in separate <Format> elements inside the <Exceptions> element of its Capabilities XML. The entire MIME type 971 * string in <Format> is used as the value of the EXCEPTIONS parameter. The first of these formats is required to be 972 * offered by every WMS; the others are optional. 973 * 974 * @return the exceptions parameter 975 */ 976 public String getExceptions() { 977 return exceptions; 978 } 979 980 /** 981 * This specification is based on [ISO 8601:1988(E)]; it extends ISO 8601 in the following ways: 982 * <UL> 983 * <li>It defines a syntax for expressing the start, end and periodicity of a data collection. 984 * <li>It defines terms to represent the 7 days of the week. 985 * <li>It allows years before 0001 AD. 986 * <li>It allows times in the distant geologic past (thousands, millions or billions of years before present). 987 * </UL> 988 * 989 * @return the time setting 990 */ 991 public Values getTime() { 992 return time; 993 } 994 995 /** 996 * Some geospatial information may be available at multiple elevations. An OWS may announce available elevations in 997 * its Capabilities XML, and some operations include a parameter for requesting a particular elevation. A single 998 * elevation value is an integer or real number whose units are declared by naming an EPSG datum. When providing 999 * elevation information, Servers should declare a default value in Capabilities XML unless there is compelling 1000 * reason to behave otherwise, and Servers shall respond with the default value if one has been declared and the 1001 * Client request does not include a value. 1002 * 1003 * @return the elevation 1004 */ 1005 public Values getElevation() { 1006 return elevation; 1007 } 1008 1009 /** 1010 * Some geospatial information may be available at other dimensions (for example, satellite images in different 1011 * wavelength bands). The dimensions other than the four space-time dimensions are referred to as 1012 * "sample dimensions". An OWS may announce available sample dimensions in its Capabilities XML, and some operations 1013 * include a mechanism for including dimensional parameters. Each sample dimension has a Name and one or more valid 1014 * values. 1015 * 1016 * @return the map 1017 */ 1018 public Map<String, Values> getSampleDimension() { 1019 return sampleDimension; 1020 } 1021 1022 /** 1023 * @return the URL of Styled Layer Descriptor (as defined in SLD Specification). This parameter is optional. If no 1024 * sld URL is defined <tt>null</tt> will be returned. 1025 */ 1026 public URL getSLD_URL() { 1027 return sLD_URL; 1028 } 1029 1030 /** 1031 * @return the URL of Web Feature CapabilitiesService providing features to be symbolized using SLD. This parameter 1032 * is optional. If no WFS URL is defined <tt>null</tt> will be returned. 1033 */ 1034 public URL getWFS_URL() { 1035 return wFS_URL; 1036 } 1037 1038 /** 1039 * @return the SLD the request is made of. This implies that a 'simple' HTTP GET-Request will be transformed into a 1040 * valid SLD. This is mandatory within a JaGo WMS. 1041 * <p> 1042 * </p> 1043 * This mean even if a GetMap request is send using the HTTP GET method, an implementing class has to map 1044 * the request to a SLD data structure. 1045 */ 1046 public StyledLayerDescriptor getStyledLayerDescriptor() { 1047 return sld; 1048 } 1049 1050 /** 1051 * @return the parameter of a HTTP GET request. 1052 * 1053 */ 1054 @Override 1055 public String getRequestParameter() 1056 throws OGCWebServiceException { 1057 1058 // indicates if the request parameters are decoded as SLD. deegree won't 1059 // perform SLD requests through HTTP GET 1060 if ( boundingBox == null ) { 1061 throw new OGCWebServiceException( "Operations can't be expressed as HTTP GET request " ); 1062 } 1063 1064 StringBuffer sb = new StringBuffer(); 1065 1066 if ( getVersion().compareTo( "1.0.0" ) <= 0 ) { 1067 sb.append( "VERSION=" ).append( getVersion() ).append( "&REQUEST=map" ); 1068 String f = StringTools.replace( getFormat(), "image/", "", false ); 1069 sb.append( "&FORMAT=" ); 1070 try { 1071 sb.append( URLEncoder.encode( f, CharsetUtils.getSystemCharset() ) ); 1072 } catch ( UnsupportedEncodingException e ) { 1073 // system encoding should be supported... 1074 } 1075 } else { 1076 sb.append( "&VERSION=" ).append( getVersion() ).append( "&REQUEST=GetMap" ); 1077 sb.append( "&FORMAT=" ); 1078 try { 1079 sb.append( URLEncoder.encode( getFormat(), CharsetUtils.getSystemCharset() ) ); 1080 } catch ( UnsupportedEncodingException e ) { 1081 // system encoding should be supported... 1082 } 1083 } 1084 1085 sb.append( "&TRANSPARENT=" ).append( Boolean.toString( getTransparency() ).toUpperCase() ); 1086 sb.append( "&WIDTH=" ).append( getWidth() ); 1087 sb.append( "&HEIGHT=" ).append( getHeight() ); 1088 sb.append( "&EXCEPTIONS=" ).append( getExceptions() ); 1089 sb.append( "&BGCOLOR=" ).append( ColorUtils.toHexCode( "0x", bGColor ) ); 1090 1091 if ( "1.3.0".compareTo( getVersion() ) <= 0 ) { 1092 sb.append( "&BBOX=" ).append( boundingBox.getMin().getY() ); 1093 sb.append( ',' ).append( boundingBox.getMin().getX() ); 1094 sb.append( ',' ).append( boundingBox.getMax().getY() ); 1095 sb.append( ',' ).append( boundingBox.getMax().getX() ); 1096 } else { 1097 sb.append( "&BBOX=" ).append( boundingBox.getMin().getX() ); 1098 sb.append( ',' ).append( boundingBox.getMin().getY() ); 1099 sb.append( ',' ).append( boundingBox.getMax().getX() ); 1100 sb.append( ',' ).append( boundingBox.getMax().getY() ); 1101 } 1102 1103 Layer[] layers = getLayers(); 1104 StringBuffer l = new StringBuffer( 500 ); 1105 StringBuffer s = new StringBuffer( 500 ); 1106 1107 if ( sLD_URL == null ) { 1108 for ( int i = 0; i < layers.length; i++ ) { 1109 try { 1110 l.append( URLEncoder.encode( layers[i].getName(), CharsetUtils.getSystemCharset() ) ); 1111 l.append( ',' ); 1112 if ( !layers[i].getStyleName().equals( "$DEFAULT" ) ) { 1113 s.append( URLEncoder.encode( layers[i].getStyleName(), CharsetUtils.getSystemCharset() ) ); 1114 } 1115 s.append( ',' ); 1116 } catch ( Exception e ) { 1117 throw new OGCWebServiceException( e.toString() ); 1118 } 1119 } 1120 1121 if ( l.length() != 0 ) { 1122 sb.append( "&LAYERS=" ).append( l.substring( 0, l.length() - 1 ) ); 1123 } 1124 1125 if ( s.length() != 0 ) { 1126 sb.append( "&STYLES=" ).append( s.substring( 0, s.length() - 1 ) ); 1127 } 1128 } else if ( sLD_URL != null ) { 1129 sb.append( "&SLD=" ).append( NetWorker.url2String( sLD_URL ) ); 1130 } else if ( sld != null ) { 1131 String tmp = ( (Marshallable) sld ).exportAsXML(); 1132 try { 1133 tmp = URLEncoder.encode( tmp, CharsetUtils.getSystemCharset() ); 1134 } catch ( Exception e ) { 1135 throw new OGCWebServiceException( e.toString() ); 1136 } 1137 sb.append( "&SLD_BODY=" ).append( tmp ); 1138 } 1139 1140 if ( "1.3.0".compareTo( getVersion() ) <= 0 ) { 1141 sb.append( "&CRS=" ).append( getSrs() ); 1142 } else { 1143 sb.append( "&SRS=" ).append( getSrs() ); 1144 } 1145 1146 // TODO 1147 // append time, elevation and sampleDimensions 1148 1149 if ( getVendorSpecificParameters() != null ) { 1150 Iterator<String> iterator = getVendorSpecificParameters().keySet().iterator(); 1151 while ( iterator.hasNext() ) { 1152 String key = iterator.next(); 1153 String value = getVendorSpecificParameters().get( key ); 1154 try { 1155 value = URLEncoder.encode( value, CharsetUtils.getSystemCharset() ); 1156 } catch ( UnsupportedEncodingException e ) { 1157 // system encoding should be supported... 1158 } 1159 sb.append( '&' ).append( key ).append( '=' ).append( value ); 1160 } 1161 } 1162 1163 return sb.toString(); 1164 } 1165 1166 @Override 1167 public String toString() { 1168 String s = "An unknown " + this.getClass().getName() + " request"; 1169 try { 1170 s = getRequestParameter(); 1171 } catch ( OGCWebServiceException e ) { 1172 // in that case, we just don't have the parameters... 1173 } 1174 return s; 1175 } 1176 1177 /** 1178 * creates a Layer object beacuse of the inner class construct. 1179 * 1180 * @param name 1181 * the name of the layer 1182 * @param style 1183 * the corresponding style of the layer 1184 * @return Layer a layer object constaining name and style 1185 */ 1186 public static Layer createLayer( String name, String style ) { 1187 return new Layer( name, style ); 1188 } 1189 1190 /** 1191 * A Layer object. It contains the name of the layer and the corresponding style. 1192 * 1193 * @version $Revision: 18195 $ 1194 * @author <a href="mailto:poth@lat-lon.de">Andreas Poth</a> 1195 */ 1196 public static class Layer implements Serializable { 1197 1198 private static final long serialVersionUID = -98575941104285931L; 1199 1200 private String name = null; 1201 1202 private String styleName = null; 1203 1204 /** 1205 * constructor initializing the class with the <Layer> 1206 * 1207 * @param name 1208 * @param styleName 1209 */ 1210 public Layer( String name, String styleName ) { 1211 this.name = name; 1212 this.styleName = styleName; 1213 } 1214 1215 /** 1216 * @return the <Name> 1217 */ 1218 public String getName() { 1219 return name; 1220 } 1221 1222 /** 1223 * @return the <StyleName> 1224 */ 1225 public String getStyleName() { 1226 return styleName; 1227 } 1228 1229 } 1230 1231 /** 1232 * @return the dimTime 1233 */ 1234 public DimensionValues getDimTime() { 1235 return dimTime; 1236 } 1237 1238 /** 1239 * @param dimTime 1240 * the dimTime to set 1241 */ 1242 public void setDimTime( DimensionValues dimTime ) { 1243 this.dimTime = dimTime; 1244 } 1245 1246 /** 1247 * @return the dimElev 1248 */ 1249 public DimensionValues getDimElev() { 1250 return dimElev; 1251 } 1252 1253 /** 1254 * @param dimElev 1255 * the dimElev to set 1256 */ 1257 public void setDimElev( DimensionValues dimElev ) { 1258 this.dimElev = dimElev; 1259 } 1260 1261 }