001 //$HeadURL: svn+ssh://rbezema@svn.wald.intevation.org/deegree/base/tags/2.1/src/org/deegree/ogcwebservices/wms/DefaultGetMapHandler.java $ 002 /*---------------- FILE HEADER ------------------------------------------ 003 004 This file is part of deegree. 005 Copyright (C) 2001-2007 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 package org.deegree.ogcwebservices.wms; 044 045 import java.awt.Color; 046 import java.awt.Font; 047 import java.awt.Graphics; 048 import java.awt.Graphics2D; 049 import java.awt.RenderingHints; 050 import java.awt.image.BufferedImage; 051 import java.util.ArrayList; 052 import java.util.List; 053 import java.util.concurrent.Callable; 054 055 import org.apache.batik.svggen.SVGGraphics2D; 056 import org.deegree.framework.concurrent.ExecutionFinishedEvent; 057 import org.deegree.framework.concurrent.ExecutionFinishedListener; 058 import org.deegree.framework.concurrent.Executor; 059 import org.deegree.framework.log.ILogger; 060 import org.deegree.framework.log.LoggerFactory; 061 import org.deegree.framework.util.ImageUtils; 062 import org.deegree.framework.util.MapUtils; 063 import org.deegree.framework.util.MimeTypeMapper; 064 import org.deegree.graphics.MapFactory; 065 import org.deegree.graphics.Theme; 066 import org.deegree.graphics.optimizers.LabelOptimizer; 067 import org.deegree.graphics.sld.AbstractLayer; 068 import org.deegree.graphics.sld.AbstractStyle; 069 import org.deegree.graphics.sld.NamedLayer; 070 import org.deegree.graphics.sld.NamedStyle; 071 import org.deegree.graphics.sld.StyledLayerDescriptor; 072 import org.deegree.graphics.sld.UserLayer; 073 import org.deegree.graphics.sld.UserStyle; 074 import org.deegree.i18n.Messages; 075 import org.deegree.model.crs.CRSFactory; 076 import org.deegree.model.crs.CoordinateSystem; 077 import org.deegree.model.crs.GeoTransformer; 078 import org.deegree.model.crs.IGeoTransformer; 079 import org.deegree.model.spatialschema.Envelope; 080 import org.deegree.model.spatialschema.Geometry; 081 import org.deegree.model.spatialschema.GeometryFactory; 082 import org.deegree.ogcbase.InvalidSRSException; 083 import org.deegree.ogcwebservices.InconsistentRequestException; 084 import org.deegree.ogcwebservices.OGCWebServiceException; 085 import org.deegree.ogcwebservices.OGCWebServiceResponse; 086 import org.deegree.ogcwebservices.wms.capabilities.ScaleHint; 087 import org.deegree.ogcwebservices.wms.configuration.AbstractDataSource; 088 import org.deegree.ogcwebservices.wms.configuration.WMSConfigurationType; 089 import org.deegree.ogcwebservices.wms.configuration.WMSConfiguration_1_3_0; 090 import org.deegree.ogcwebservices.wms.configuration.WMSDeegreeParams; 091 import org.deegree.ogcwebservices.wms.operation.GetMap; 092 import org.deegree.ogcwebservices.wms.operation.GetMapResult; 093 import org.deegree.ogcwebservices.wms.operation.WMSProtocolFactory; 094 import org.deegree.ogcwebservices.wms.operation.GetMap.Layer; 095 import org.w3c.dom.Element; 096 097 /** 098 * 099 * 100 * @version $Revision: 6846 $ 101 * @author <a href="mailto:poth@lat-lon.de">Andreas Poth</a> 102 */ 103 public class DefaultGetMapHandler implements GetMapHandler, ExecutionFinishedListener<Object[]> { 104 105 private static final ILogger LOG = LoggerFactory.getLogger( DefaultGetMapHandler.class ); 106 107 private GetMap request = null; 108 109 private Object[] themes = null; 110 111 private double scale = 0; 112 113 private int count = 0; 114 115 private CoordinateSystem reqCRS = null; 116 117 private WMSConfigurationType configuration = null; 118 119 private BufferedImage copyrightImg = null; 120 121 boolean version130 = false; 122 123 /** 124 * Creates a new GetMapHandler object. 125 * 126 * @param configuration 127 * @param request 128 * request to perform 129 */ 130 public DefaultGetMapHandler( WMSConfigurationType configuration, GetMap request ) { 131 this.request = request; 132 this.configuration = configuration; 133 134 try { 135 // get copyright image if possible 136 copyrightImg = ImageUtils.loadImage( configuration.getDeegreeParams().getCopyRight() ); 137 } catch ( Exception e ) { 138 // don't use copyright 139 } 140 141 } 142 143 /** 144 * returns the configuration used by the handler 145 * 146 * @return the configuration document 147 */ 148 public WMSConfigurationType getConfiguration() { 149 return configuration; 150 } 151 152 /** 153 * increases the counter variable that holds the number of services that has sent a response. 154 * All data are available if the counter value equals the number of requested layers. 155 */ 156 protected synchronized void increaseCounter() { 157 count++; 158 } 159 160 /** 161 * performs a GetMap request and retruns the result encapsulated within a <tt>GetMapResult</tt> 162 * object. 163 * <p> 164 * The method throws an WebServiceException that only shall be thrown if an fatal error occurs 165 * that makes it imposible to return a result. If something wents wrong performing the request 166 * (none fatal error) The exception shall be encapsulated within the response object to be 167 * returned to the client as requested (GetMap-Request EXCEPTION-Parameter). 168 * 169 * @return response to the GetMap response 170 */ 171 public OGCWebServiceResponse performGetMap() 172 throws OGCWebServiceException { 173 174 // some initialization is done here because the constructor is called by reflection 175 // and the exceptions won't be properly handled in that case 176 try { 177 reqCRS = CRSFactory.create( request.getSrs().toLowerCase() ); 178 } catch ( Exception e ) { 179 throw new InvalidSRSException( Messages.getMessage( "WMS_UNKNOWN_CRS", request.getSrs() ) ); 180 } 181 182 version130 = "1.3.0".equals( request.getVersion() ); 183 184 // exceeds the max allowed map width ? 185 int maxWidth = configuration.getDeegreeParams().getMaxMapWidth(); 186 if ( ( maxWidth != 0 ) && ( request.getWidth() > maxWidth ) ) { 187 throw new InconsistentRequestException( Messages.getMessage( "WMS_EXCEEDS_WIDTH", new Integer( maxWidth ) ) ); 188 } 189 190 // exceeds the max allowed map height ? 191 int maxHeight = configuration.getDeegreeParams().getMaxMapHeight(); 192 if ( ( maxHeight != 0 ) && ( request.getHeight() > maxHeight ) ) { 193 throw new InconsistentRequestException( 194 Messages.getMessage( "WMS_EXCEEDS_HEIGHT", new Integer( maxHeight ) ) ); 195 } 196 197 try { 198 double pixelSize = 1; 199 if ( version130 ) { 200 // required because for WMS 1.3.0 'scale' represents the ScaleDenominator 201 // and for WMS < 1.3.0 it represents the size of a pixel diagonal in meter 202 pixelSize = MapUtils.DEFAULT_PIXEL_SIZE; 203 } 204 205 scale = MapUtils.calcScale( request.getWidth(), request.getHeight(), request.getBoundingBox(), reqCRS, 206 pixelSize ); 207 208 LOG.logInfo( "OGC WMS scale: " + scale ); 209 } catch ( Exception e ) { 210 LOG.logError( e.getMessage(), e ); 211 throw new OGCWebServiceException( Messages.getMessage( "WMS_SCALECALC" ) ); 212 } 213 214 GetMap.Layer[] ls = request.getLayers(); 215 216 // if 1.3.0, check for maximum allowed layers 217 if ( version130 ) { 218 WMSConfiguration_1_3_0 cfg = (WMSConfiguration_1_3_0) configuration; 219 if ( ls.length > cfg.getLayerLimit() ) { 220 String ms = Messages.getMessage( "WMS_EXCEEDS_NUMBER", new Integer( cfg.getLayerLimit() ) ); 221 throw new InconsistentRequestException( ms ); 222 } 223 } 224 225 ls = validateLayers( ls ); 226 227 LOG.logDebug( "Validated " + ls.length + " layers." ); 228 229 StyledLayerDescriptor sld = toSLD( ls, request.getStyledLayerDescriptor() ); 230 231 AbstractLayer[] layers = sld.getLayers(); 232 233 LOG.logDebug( "After SLD consideration, found " + layers.length + " layers." ); 234 235 // get the number of themes assigned to the selected layers 236 // notice that there maybe more themes as there are layers because 237 // 1 .. n datasources can be assigned to one layer. 238 int cntTh = countNumberOfThemes( layers ); 239 240 themes = new Object[cntTh]; 241 // invokes the data supplyer for each layer in an independ thread 242 int kk = 0; 243 244 for ( int i = 0; i < layers.length; i++ ) { 245 246 if ( layers[i] instanceof NamedLayer ) { 247 String styleName = null; 248 if ( i < request.getLayers().length ) { 249 styleName = request.getLayers()[i].getStyleName(); 250 } 251 // please note that this may be undesirable behaviour, I (schmitz) just added 252 // the safety 'if' because it will throw nasty exceptions otherwise 253 // (I don't know what this code actually does) 254 kk = invokeNamedLayer( layers[i], kk, styleName ); 255 // if ( kk < cntTh ) { 256 // kk = invokeNamedLayer( layers[i], kk, styleName ); 257 // } 258 } else { 259 double sc = scale; 260 if ( !version130 ) { 261 // required because for WMS 1.3.0 'scale' represents the ScaleDenominator 262 // and for WMS < 1.3.0 it represents the size of a pixel diagonal in meter 263 sc = scale / MapUtils.DEFAULT_PIXEL_SIZE; 264 } 265 GetMapServiceInvokerForUL si = new GetMapServiceInvokerForUL( this, (UserLayer) layers[i], sc, kk++ ); 266 new Thread( si ).start(); 267 } 268 } 269 270 LOG.logDebug( "Invoked " + kk + " layers." ); 271 272 // TODO 273 // substitue by an event based approach 274 waitForFinished(); 275 276 GetMapResult res = renderMap(); 277 278 return res; 279 } 280 281 /** 282 * this methods validates layer in two ways:<br> 283 * a) are layers available from the current WMS<br> 284 * b) If a layer is selected that includes other layers determine all its sublayers having 285 * <Name>s and return them instead 286 * 287 * @param ls 288 * @return the layers 289 * @throws LayerNotDefinedException 290 * @throws InvalidSRSException 291 */ 292 private Layer[] validateLayers( Layer[] ls ) 293 throws LayerNotDefinedException, InvalidSRSException { 294 295 List<Layer> layer = new ArrayList<Layer>( ls.length ); 296 for ( int i = 0; i < ls.length; i++ ) { 297 org.deegree.ogcwebservices.wms.capabilities.Layer l = configuration.getLayer( ls[i].getName() ); 298 299 if ( l == null ) { 300 throw new LayerNotDefinedException( Messages.getMessage( "WMS_UNKNOWNLAYER", ls[i].getName() ) ); 301 } 302 303 validateSRS( l.getSrs(), ls[i].getName() ); 304 305 layer.add( ls[i] ); 306 if ( l.getLayer() != null ) { 307 layer = addNestedLayers( l.getLayer(), ls[i].getStyleName(), layer ); 308 } 309 } 310 311 return layer.toArray( new Layer[layer.size()] ); 312 } 313 314 /** 315 * adds all direct and none direct sub-layers of the passed WMS capabilities layer as 316 * 317 * @see GetMap.Layer to the passed list. 318 * @param layer 319 * @param reqLayer 320 * @param list 321 * @return all sublayers 322 * @throws InvalidSRSException 323 */ 324 private List<Layer> addNestedLayers( org.deegree.ogcwebservices.wms.capabilities.Layer[] ll, String styleName, 325 List<Layer> list ) 326 throws InvalidSRSException { 327 328 for ( int j = 0; j < ll.length; j++ ) { 329 if ( ll[j].getName() != null ) { 330 String name = ll[j].getName(); 331 validateSRS( ll[j].getSrs(), name ); 332 list.add( GetMap.createLayer( name, styleName ) ); 333 } 334 if ( ll[j].getLayer() != null ) { 335 list = addNestedLayers( ll[j].getLayer(), styleName, list ); 336 } 337 338 } 339 return list; 340 } 341 342 /** 343 * throws an exception if the requested SRS is not be supported by the passed layer (name) 344 * 345 * @param srs 346 * @param name 347 * @throws InvalidSRSException 348 */ 349 private void validateSRS( String[] srs, String name ) 350 throws InvalidSRSException { 351 boolean validSRS = false; 352 for ( int k = 0; k < srs.length; k++ ) { 353 validSRS = srs[k].equalsIgnoreCase( reqCRS.getName() ); 354 if ( validSRS ) 355 break; 356 } 357 if ( !validSRS ) { 358 String s = Messages.getMessage( "WMS_UNKNOWN_CRS_FOR_LAYER", reqCRS.getName(), name ); 359 throw new InvalidSRSException( s ); 360 } 361 } 362 363 /** 364 * @param layers 365 * @param kk 366 * @param i 367 * @return a counter, kk + 1 I guess 368 * @throws OGCWebServiceException 369 */ 370 private int invokeNamedLayer( AbstractLayer layer, int kk, String styleName ) 371 throws OGCWebServiceException { 372 373 org.deegree.ogcwebservices.wms.capabilities.Layer lay = configuration.getLayer( layer.getName() ); 374 375 LOG.logDebug( "Invoked layer " + layer.getName() ); 376 377 if ( validate( lay, layer.getName() ) ) { 378 379 UserStyle us = getStyles( (NamedLayer) layer, styleName ); 380 AbstractDataSource[] ds = lay.getDataSource(); 381 382 if ( ds.length == 0 ) { 383 increaseCounter(); 384 LOG.logDebug( "No datasources for layer " + layer.getName() ); 385 } else { 386 for ( int j = 0; j < ds.length; j++ ) { 387 388 LOG.logDebug( "Invoked datasource " + ds[j].getClass() + " for layer " + layer.getName() ); 389 390 ScaleHint scaleHint = ds[j].getScaleHint(); 391 if ( scale >= scaleHint.getMin() && scale < scaleHint.getMax() 392 && isValidArea( ds[j].getValidArea() ) ) { 393 double sc = scale; 394 if ( !version130 ) { 395 // required because for WMS 1.3.0 'scale' represents the 396 // ScaleDenominator 397 // and for WMS < 1.3.0 it represents the size of a pixel diagonal in 398 // meter 399 sc = scale / MapUtils.DEFAULT_PIXEL_SIZE; 400 } 401 402 GetMapServiceInvokerForNL si = new GetMapServiceInvokerForNL( this, (NamedLayer) layer, ds[j], 403 us, sc, kk++ ); 404 MapServiceTask task = new MapServiceTask( si ); 405 try { 406 Executor.getInstance().performAsynchronously( task, this ); 407 } catch ( Exception e ) { 408 LOG.logError( e.getMessage(), e ); 409 throw new OGCWebServiceException( getClass().getName(), e.getMessage() ); 410 } 411 } else { 412 increaseCounter(); 413 LOG.logDebug( "Not showing layer " + layer.getName() + " due to scale" ); 414 } 415 } 416 } 417 } else { 418 // set theme to null if no data are available for the requested 419 // area and/or scale. This will cause this index position will be ignored 420 // when creating the final result 421 themes[kk++] = null; 422 AbstractDataSource[] ds = lay.getDataSource(); 423 for ( int i = 0; i < ds.length; ++i ) { 424 increaseCounter(); 425 } 426 } 427 return kk; 428 } 429 430 /** 431 * returns the number of <code>DataSource</code>s involved in a GetMap request 432 * 433 * @param layers 434 * @return the number of themes 435 * @throws OGCWebServiceException 436 * @throws OGCWebServiceException 437 */ 438 private int countNumberOfThemes( AbstractLayer[] layers ) 439 throws OGCWebServiceException { 440 int cnt = 0; 441 for ( int i = 0; i < layers.length; i++ ) { 442 if ( layers[i] instanceof NamedLayer ) { 443 org.deegree.ogcwebservices.wms.capabilities.Layer lay = configuration.getLayer( layers[i].getName() ); 444 validate( lay, layers[i].getName() ); 445 446 if ( lay != null ) { 447 AbstractDataSource[] ds = lay.getDataSource(); 448 if ( ds.length == 0 ) { 449 ++cnt; 450 } else { 451 for ( int j = 0; j < ds.length; j++ ) { 452 cnt++; 453 } 454 } 455 } 456 } else { 457 cnt++; 458 } 459 } 460 461 LOG.logDebug( "Counted " + cnt + " themes." ); 462 return cnt; 463 } 464 465 /** 466 * returns true if the requested boundingbox intersects with the valid area of a datasource 467 * 468 * @param validArea 469 */ 470 private boolean isValidArea( Geometry validArea ) { 471 472 if ( validArea != null ) { 473 try { 474 Envelope env = request.getBoundingBox(); 475 Geometry geom = GeometryFactory.createSurface( env, reqCRS ); 476 if ( !reqCRS.getName().equals( validArea.getCoordinateSystem().getName() ) ) { 477 // if requested CRS is not identical to the CRS of the valid area 478 // a transformation must be performed before intersection can 479 // be checked 480 IGeoTransformer gt = new GeoTransformer( validArea.getCoordinateSystem() ); 481 geom = gt.transform( geom ); 482 } 483 return geom.intersects( validArea ); 484 } catch ( Exception e ) { 485 // should never happen 486 LOG.logError( "Could not validate WMS datasource area", e ); 487 } 488 } 489 return true; 490 } 491 492 /** 493 * runs a loop until all sub requestes (one for each layer) has been finished or the maximum 494 * time limit has been exceeded. 495 * 496 * @throws OGCWebServiceException 497 */ 498 private void waitForFinished() 499 throws OGCWebServiceException { 500 if ( count < themes.length ) { 501 // waits until the requested layers are available as <tt>DisplayElements</tt> 502 // or the time limit has been reached. 503 // if count == themes.length then no request must be performed 504 long timeStamp = System.currentTimeMillis(); 505 long lapse = 0; 506 long timeout = configuration.getDeegreeParams().getRequestTimeLimit(); 507 LOG.logDebug( "Timeout is set to " + timeout + " ms." ); 508 do { 509 try { 510 Thread.sleep( 50 ); 511 lapse += 50; 512 LOG.logDebug( "Counter is now " + count + "." ); 513 } catch ( InterruptedException e ) { 514 LOG.logError( e.getMessage(), e ); 515 String s = Messages.getMessage( "WMS_WAITING" ); 516 throw new OGCWebServiceException( getClass().getName(), s ); 517 } 518 } while ( count < themes.length && lapse < timeout ); 519 if ( System.currentTimeMillis() - timeStamp >= timeout ) { 520 String s = Messages.getMessage( "WMS_TIMEOUT" ); 521 LOG.logError( s ); 522 throw new OGCWebServiceException( getClass().getName(), s ); 523 } 524 } 525 LOG.logDebug( "Counter is now " + count ); 526 } 527 528 /** 529 * creates a StyledLayerDocument containing all requested layer, nested layers if required and 530 * assigend styles. Not considered are nested layers for mixed requests (LAYERS- and SLD(_BODY)- 531 * parameter has been defined) 532 * 533 * @param layers 534 * @param inSLD 535 * @return a combined SLD object 536 * @throws InvalidSRSException 537 */ 538 private StyledLayerDescriptor toSLD( GetMap.Layer[] layers, StyledLayerDescriptor inSLD ) 539 throws InvalidSRSException { 540 StyledLayerDescriptor sld = null; 541 542 if ( layers != null && layers.length > 0 && inSLD == null ) { 543 // if just a list of layers has been requested 544 545 // create a SLD from the requested LAYERS and assigned STYLES 546 List<AbstractLayer> al = new ArrayList<AbstractLayer>( layers.length * 2 ); 547 for ( int i = 0; i < layers.length; i++ ) { 548 AbstractStyle[] as = new AbstractStyle[] { new NamedStyle( layers[i].getStyleName() ) }; 549 al.add( new NamedLayer( layers[i].getName(), null, as ) ); 550 551 // collect all named nested layers 552 org.deegree.ogcwebservices.wms.capabilities.Layer lla; 553 lla = configuration.getLayer( layers[i].getName() ); 554 List<GetMap.Layer> list = new ArrayList<GetMap.Layer>(); 555 addNestedLayers( lla.getLayer(), layers[i].getStyleName(), list ); 556 557 // add nested layers to list of layers to be handled 558 for ( int j = 0; j < list.size(); j++ ) { 559 GetMap.Layer nestedLayer = list.get( j ); 560 as = new AbstractStyle[] { new NamedStyle( nestedLayer.getStyleName() ) }; 561 al.add( new NamedLayer( nestedLayer.getName(), null, as ) ); 562 } 563 } 564 sld = new StyledLayerDescriptor( al.toArray( new AbstractLayer[al.size()] ), "1.0.0" ); 565 } else if ( layers != null && layers.length > 0 && inSLD != null ) { 566 // if layers not null and sld is not null then SLD layers just be 567 // considered if present in the layers list 568 // TODO 569 // layer with nested layers are not handled correctly and I think 570 // it really causes a lot of problems to use them in such a way 571 // because the style assigned to the mesting layer must be 572 // applicable for all nested layers. 573 List<String> list = new ArrayList<String>(); 574 for ( int i = 0; i < layers.length; i++ ) { 575 list.add( layers[i].getName() ); 576 } 577 578 List<AbstractLayer> newList = new ArrayList<AbstractLayer>( 20 ); 579 AbstractLayer[] al = inSLD.getLayers(); 580 for ( int i = 0; i < al.length; i++ ) { 581 if ( list.contains( al[i].getName() ) ) { 582 newList.add( al[i] ); 583 } 584 } 585 al = new AbstractLayer[newList.size()]; 586 sld = new StyledLayerDescriptor( newList.toArray( al ), inSLD.getVersion() ); 587 588 // add nested layers for mixed case, nested from original sld 589 AbstractLayer[] as = inSLD.getLayers(); 590 for ( AbstractLayer l : as ) { 591 addNestedLayers( l, sld ); 592 } 593 } else { 594 // if no layers but a SLD is defined ... 595 AbstractLayer[] as = inSLD.getLayers(); 596 for ( AbstractLayer l : as ) { 597 addNestedLayers( l, inSLD ); 598 } 599 600 sld = inSLD; 601 } 602 603 return sld; 604 } 605 606 // adds the nested layers to the sld 607 private void addNestedLayers( AbstractLayer l, StyledLayerDescriptor sld ) { 608 if ( !( l instanceof NamedLayer ) ) { 609 return; 610 } 611 if ( configuration.getLayer( l.getName() ) == null ) { 612 return; 613 } 614 615 org.deegree.ogcwebservices.wms.capabilities.Layer[] ls; 616 ls = configuration.getLayer( l.getName() ).getLayer(); 617 for ( org.deegree.ogcwebservices.wms.capabilities.Layer lay : ls ) { 618 NamedStyle sty = new NamedStyle( lay.getStyles()[0].getName() ); 619 AbstractStyle[] newSty = new AbstractStyle[] { sty }; 620 NamedLayer newLay = new NamedLayer( lay.getName(), null, newSty ); 621 sld.addLayer( newLay ); 622 } 623 } 624 625 /** 626 * returns the <tt>UserStyle</tt>s assigned to a named layer 627 * 628 * @param sldLayer 629 * layer to get the styles for 630 * @param styleName 631 * requested stylename (from the KVP encoding) 632 */ 633 private UserStyle getStyles( NamedLayer sldLayer, String styleName ) 634 throws OGCWebServiceException { 635 636 AbstractStyle[] styles = sldLayer.getStyles(); 637 UserStyle us = null; 638 639 // to avoid retrieving the layer again for each style 640 org.deegree.ogcwebservices.wms.capabilities.Layer layer = null; 641 layer = configuration.getLayer( sldLayer.getName() ); 642 int i = 0; 643 while ( us == null && i < styles.length ) { 644 if ( styles[i] instanceof NamedStyle ) { 645 // styles will be taken from the WMS's style repository 646 us = getPredefinedStyle( styles[i].getName(), sldLayer.getName(), layer ); 647 } else { 648 // if the requested style fits the name of the defined style or 649 // if the defined style is marked as default and the requested 650 // style if 'default' the condition is true. This includes that 651 // if more than one style with the same name or more than one 652 // style is marked as default always the first will be choosen 653 if ( styleName == null || ( styles[i].getName() != null && styles[i].getName().equals( styleName ) ) 654 || ( styleName.equalsIgnoreCase( "$DEFAULT" ) && ( (UserStyle) styles[i] ).isDefault() ) ) { 655 us = (UserStyle) styles[i]; 656 } 657 } 658 i++; 659 } 660 if ( us == null ) { 661 // this may happens if the SLD contains a named layer but not 662 // a style! yes this is valid according to SLD spec 1.0.0 663 us = getPredefinedStyle( styleName, sldLayer.getName(), layer ); 664 } 665 return us; 666 } 667 668 /** 669 * 670 * @param styleName 671 * @param layerName 672 * @param layer 673 * @return the style 674 * @throws StyleNotDefinedException 675 */ 676 private UserStyle getPredefinedStyle( String styleName, String layerName, 677 org.deegree.ogcwebservices.wms.capabilities.Layer layer ) 678 throws StyleNotDefinedException { 679 UserStyle us = null; 680 if ( "default".equals( styleName ) ) { 681 us = layer.getStyle( styleName ); 682 } 683 684 if ( us == null ) { 685 if ( styleName == null || styleName.length() == 0 || styleName.equals( "$DEFAULT" ) 686 || styleName.equals( "default" ) ) { 687 styleName = "default:" + layerName; 688 } 689 } 690 691 us = layer.getStyle( styleName ); 692 693 if ( us == null && !( styleName.startsWith( "default" ) ) && !( styleName.startsWith( "$DEFAULT" ) ) ) { 694 String s = Messages.getMessage( "WMS_STYLENOTDEFINED", styleName, layer ); 695 throw new StyleNotDefinedException( s ); 696 } 697 return us; 698 } 699 700 /** 701 * validates if the requested layer matches the conditions of the request if not a 702 * <tt>WebServiceException</tt> will be thrown. If the layer matches the request, but isn't 703 * able to deviever data for the requested area and/or scale false will be returned. If the 704 * layer matches the request and contains data for the requested area and/or scale true will be 705 * returned. 706 * 707 * @param layer 708 * layer as defined at the capabilities/configuration 709 * @param name 710 * name of the layer (must be submitted seperatly because the layer parameter can be 711 * <tt>null</tt> 712 */ 713 private boolean validate( org.deegree.ogcwebservices.wms.capabilities.Layer layer, String name ) 714 throws OGCWebServiceException { 715 716 // check if layer is available 717 if ( layer == null ) { 718 throw new LayerNotDefinedException( Messages.getMessage( "WMS_UNKNOWNLAYER", name ) ); 719 } 720 721 // check bounding box 722 try { 723 Envelope bbox = request.getBoundingBox(); 724 Envelope layerBbox = layer.getLatLonBoundingBox(); 725 if ( !request.getSrs().equalsIgnoreCase( "EPSG:4326" ) ) { 726 // transform the bounding box of the request to EPSG:4326 727 IGeoTransformer gt = new GeoTransformer( CRSFactory.create( "epsg:4326" ) ); 728 bbox = gt.transform( bbox, reqCRS ); 729 } 730 if ( !bbox.intersects( layerBbox ) ) { 731 return false; 732 } 733 734 } catch ( Exception e ) { 735 LOG.logError( e.getMessage(), e ); 736 throw new OGCWebServiceException( Messages.getMessage( "WMS_BBOXCOMPARSION" ) ); 737 } 738 739 return true; 740 } 741 742 /** 743 * put a theme to the passed index of the themes array. The second param passed is a 744 * <tt>Theme</tt> or an exception 745 */ 746 747 protected synchronized void putTheme( int index, Object o ) { 748 themes[index] = o; 749 } 750 751 /** 752 * will be called each time a datasource has been read 753 * 754 * @param finishedEvent 755 */ 756 public synchronized void executionFinished( ExecutionFinishedEvent<Object[]> finishedEvent ) { 757 Object[] o = null; 758 try { 759 o = finishedEvent.getResult(); 760 } catch ( Throwable t ) { 761 String msg = Messages.getMessage( "WMS_ASYNC_TASK_ERROR", t.getMessage() ); 762 LOG.logError( msg, t ); 763 } 764 themes[( (Integer) o[0] ).intValue()] = o[1]; 765 increaseCounter(); 766 } 767 768 /** 769 * renders the map from the <tt>DisplayElement</tt>s 770 */ 771 private synchronized GetMapResult renderMap() { 772 773 GetMapResult response = null; 774 OGCWebServiceException exce = null; 775 776 ArrayList<Object> list = new ArrayList<Object>( 50 ); 777 for ( int i = 0; i < themes.length; i++ ) { 778 if ( themes[i] instanceof Exception ) { 779 exce = new OGCWebServiceException( getClass().getName(), themes[i].toString() ); 780 } 781 if ( themes[i] instanceof OGCWebServiceException ) { 782 exce = (OGCWebServiceException) themes[i]; 783 break; 784 } 785 if ( themes[i] != null ) { 786 list.add( themes[i] ); 787 } 788 } 789 790 String mime = MimeTypeMapper.toMimeType( request.getFormat() ); 791 792 // get target object for rendering 793 Object target = GraphicContextFactory.createGraphicTarget( mime, request.getWidth(), request.getHeight() ); 794 795 // get graphic context of the target 796 Graphics g = GraphicContextFactory.createGraphicContext( mime, target ); 797 if ( exce == null ) { 798 // only if no exception occured 799 try { 800 Theme[] th = list.toArray( new Theme[list.size()] ); 801 org.deegree.graphics.MapView map = null; 802 if ( th.length > 0 ) { 803 map = MapFactory.createMapView( "deegree WMS", request.getBoundingBox(), reqCRS, th ); 804 } 805 g.setClip( 0, 0, request.getWidth(), request.getHeight() ); 806 807 if ( !request.getTransparency() ) { 808 if ( g instanceof Graphics2D ) { 809 // this ensures real clearing (rendering modifies the color ever so 810 // slightly) 811 ( (Graphics2D) g ).setBackground( request.getBGColor() ); 812 g.clearRect( 0, 0, request.getWidth(), request.getHeight() ); 813 } else { 814 g.setColor( request.getBGColor() ); 815 g.fillRect( 0, 0, request.getWidth(), request.getHeight() ); 816 } 817 } 818 819 if ( map != null ) { 820 Theme[] thms = map.getAllThemes(); 821 map.addOptimizer( new LabelOptimizer( thms ) ); 822 // antialiasing must be switched of for gif output format 823 // because the antialiasing may create more than 255 colors 824 // in the map/image, even just a few colors are defined in 825 // the styles 826 if ( !request.getFormat().equalsIgnoreCase( "image/gif" ) ) { 827 if ( configuration.getDeegreeParams().isAntiAliased() ) { 828 ( (Graphics2D) g ).setRenderingHint( RenderingHints.KEY_ANTIALIASING, 829 RenderingHints.VALUE_ANTIALIAS_ON ); 830 ( (Graphics2D) g ).setRenderingHint( RenderingHints.KEY_TEXT_ANTIALIASING, 831 RenderingHints.VALUE_TEXT_ANTIALIAS_ON ); 832 } 833 } 834 map.paint( g ); 835 } 836 } catch ( Exception e ) { 837 LOG.logError( e.getMessage(), e ); 838 exce = new OGCWebServiceException( "GetMapHandler_Impl: renderMap", e.toString() ); 839 } 840 } 841 842 // print a copyright note at the left lower corner of the map 843 printCopyright( g, request.getHeight() ); 844 845 if ( mime.equals( "image/svg+xml" ) || mime.equals( "image/svg xml" ) ) { 846 Element root = ( (SVGGraphics2D) g ).getRoot(); 847 root.setAttribute( "xmlns:xlink", "http://www.w3.org/1999/xlink" ); 848 response = WMSProtocolFactory.createGetMapResponse( request, exce, root ); 849 } else { 850 response = WMSProtocolFactory.createGetMapResponse( request, exce, target ); 851 } 852 g.dispose(); 853 854 return response; 855 } 856 857 /** 858 * prints a copyright note at left side of the map bottom. The copyright note will be extracted 859 * from the WMS capabilities/configuration 860 * 861 * @param g 862 * graphic context of the map 863 * @param heigth 864 * height of the map in pixel 865 */ 866 private void printCopyright( Graphics g, int heigth ) { 867 WMSDeegreeParams dp = configuration.getDeegreeParams(); 868 String copyright = dp.getCopyRight(); 869 if ( copyrightImg != null ) { 870 g.drawImage( copyrightImg, 8, heigth - copyrightImg.getHeight() - 5, null ); 871 } else { 872 if ( copyright != null ) { 873 g.setFont( new Font( "SANSSERIF", Font.PLAIN, 14 ) ); 874 g.setColor( Color.BLACK ); 875 g.drawString( copyright, 8, heigth - 15 ); 876 g.drawString( copyright, 10, heigth - 15 ); 877 g.drawString( copyright, 8, heigth - 13 ); 878 g.drawString( copyright, 10, heigth - 13 ); 879 g.setColor( Color.WHITE ); 880 g.setFont( new Font( "SANSSERIF", Font.PLAIN, 14 ) ); 881 g.drawString( copyright, 9, heigth - 14 ); 882 } 883 } 884 885 } 886 887 /** 888 * @return the request that is being handled 889 */ 890 protected GetMap getRequest() { 891 return request; 892 } 893 894 /** 895 * @return the requests coordinate system 896 */ 897 protected CoordinateSystem getRequestCRS() { 898 return reqCRS; 899 } 900 901 // /////////////////////////////////////////////////////////////////////////// 902 // inner classes // 903 // /////////////////////////////////////////////////////////////////////////// 904 905 private class MapServiceTask implements Callable<Object[]> { 906 907 GetMapServiceInvokerForNL invoker; 908 909 MapServiceTask( GetMapServiceInvokerForNL invoker ) { 910 this.invoker = invoker; 911 } 912 913 public Object[] call() 914 throws Exception { 915 return (Object[]) this.invoker.run(); 916 } 917 } 918 919 920 }