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