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