001 //$HeadURL: svn+ssh://rbezema@svn.wald.intevation.org/deegree/base/branches/2.2_testing/src/org/deegree/ogcwebservices/wms/DefaultGetMapHandler.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 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.LinkedList;
053 import java.util.List;
054 import java.util.concurrent.Callable;
055 import java.util.concurrent.CancellationException;
056
057 import org.apache.batik.svggen.SVGGraphics2D;
058 import org.deegree.framework.concurrent.ExecutionFinishedEvent;
059 import org.deegree.framework.concurrent.Executor;
060 import org.deegree.framework.log.ILogger;
061 import org.deegree.framework.log.LoggerFactory;
062 import org.deegree.framework.util.ImageUtils;
063 import org.deegree.framework.util.MapUtils;
064 import org.deegree.framework.util.MimeTypeMapper;
065 import org.deegree.graphics.MapFactory;
066 import org.deegree.graphics.Theme;
067 import org.deegree.graphics.optimizers.LabelOptimizer;
068 import org.deegree.graphics.sld.AbstractLayer;
069 import org.deegree.graphics.sld.AbstractStyle;
070 import org.deegree.graphics.sld.NamedLayer;
071 import org.deegree.graphics.sld.NamedStyle;
072 import org.deegree.graphics.sld.StyledLayerDescriptor;
073 import org.deegree.graphics.sld.UserLayer;
074 import org.deegree.graphics.sld.UserStyle;
075 import org.deegree.i18n.Messages;
076 import org.deegree.model.crs.CRSFactory;
077 import org.deegree.model.crs.CoordinateSystem;
078 import org.deegree.model.crs.GeoTransformer;
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: 9697 $
101 * @author <a href="mailto:poth@lat-lon.de">Andreas Poth</a>
102 */
103 public class DefaultGetMapHandler implements GetMapHandler {
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 CoordinateSystem reqCRS = null;
114
115 private WMSConfigurationType configuration = null;
116
117 private BufferedImage copyrightImg = null;
118
119 boolean version130 = false;
120
121 /**
122 * Creates a new GetMapHandler object.
123 *
124 * @param configuration
125 * @param request
126 * request to perform
127 */
128 public DefaultGetMapHandler( WMSConfigurationType configuration, GetMap request ) {
129 this.request = request;
130 this.configuration = configuration;
131
132 try {
133 // get copyright image if possible
134 copyrightImg = ImageUtils.loadImage( configuration.getDeegreeParams().getCopyRight() );
135 } catch ( Exception e ) {
136 // don't use copyright
137 }
138
139 }
140
141 /**
142 * returns the configuration used by the handler
143 *
144 * @return the configuration document
145 */
146 public WMSConfigurationType getConfiguration() {
147 return configuration;
148 }
149
150 /**
151 * performs a GetMap request and retruns the result encapsulated within a <tt>GetMapResult</tt>
152 * object.
153 * <p>
154 * The method throws an WebServiceException that only shall be thrown if an fatal error occurs
155 * that makes it imposible to return a result. If something wents wrong performing the request
156 * (none fatal error) The exception shall be encapsulated within the response object to be
157 * returned to the client as requested (GetMap-Request EXCEPTION-Parameter).
158 *
159 * @return response to the GetMap response
160 */
161 public OGCWebServiceResponse performGetMap()
162 throws OGCWebServiceException {
163
164 // some initialization is done here because the constructor is called by reflection
165 // and the exceptions won't be properly handled in that case
166 try {
167 reqCRS = CRSFactory.create( request.getSrs().toLowerCase() );
168 } catch ( Exception e ) {
169 throw new InvalidSRSException( Messages.getMessage( "WMS_UNKNOWN_CRS", request.getSrs() ) );
170 }
171
172 version130 = "1.3.0".equals( request.getVersion() );
173
174 // exceeds the max allowed map width ?
175 int maxWidth = configuration.getDeegreeParams().getMaxMapWidth();
176 if ( ( maxWidth != 0 ) && ( request.getWidth() > maxWidth ) ) {
177 throw new InconsistentRequestException( Messages.getMessage( "WMS_EXCEEDS_WIDTH", new Integer( maxWidth ) ) );
178 }
179
180 // exceeds the max allowed map height ?
181 int maxHeight = configuration.getDeegreeParams().getMaxMapHeight();
182 if ( ( maxHeight != 0 ) && ( request.getHeight() > maxHeight ) ) {
183 throw new InconsistentRequestException(
184 Messages.getMessage( "WMS_EXCEEDS_HEIGHT", new Integer( maxHeight ) ) );
185 }
186
187 try {
188 double pixelSize = 1;
189 if ( version130 ) {
190 // required because for WMS 1.3.0 'scale' represents the ScaleDenominator
191 // and for WMS < 1.3.0 it represents the size of a pixel diagonal in meter
192 pixelSize = MapUtils.DEFAULT_PIXEL_SIZE;
193 }
194
195 scale = MapUtils.calcScale( request.getWidth(), request.getHeight(), request.getBoundingBox(), reqCRS,
196 pixelSize );
197
198 LOG.logInfo( "OGC WMS scale: " + scale );
199 } catch ( Exception e ) {
200 LOG.logError( e.getMessage(), e );
201 throw new OGCWebServiceException( Messages.getMessage( "WMS_SCALECALC" ) );
202 }
203
204 GetMap.Layer[] ls = request.getLayers();
205
206 // if 1.3.0, check for maximum allowed layers
207 if ( version130 ) {
208 WMSConfiguration_1_3_0 cfg = (WMSConfiguration_1_3_0) configuration;
209 if ( ls.length > cfg.getLayerLimit() ) {
210 String ms = Messages.getMessage( "WMS_EXCEEDS_NUMBER", new Integer( cfg.getLayerLimit() ) );
211 throw new InconsistentRequestException( ms );
212 }
213 }
214
215 Layer[] oldLayers = ls;
216 ls = validateLayers( ls );
217
218 LOG.logDebug( "Validated " + ls.length + " layers." );
219
220 StyledLayerDescriptor sld = toSLD( oldLayers, request.getStyledLayerDescriptor() );
221
222 AbstractLayer[] layers = sld.getLayers();
223
224 LOG.logDebug( "After SLD consideration, found " + layers.length + " layers." );
225
226 List<Callable<Object>> themes = new LinkedList<Callable<Object>>();
227
228 for ( int i = 0; i < layers.length; i++ ) {
229
230 if ( layers[i] instanceof NamedLayer ) {
231 String styleName = null;
232 if ( i < request.getLayers().length ) {
233 styleName = request.getLayers()[i].getStyleName();
234 }
235 invokeNamedLayer( layers[i], styleName, themes );
236 } else {
237 double sc = scale;
238 if ( !version130 ) {
239 // required because for WMS 1.3.0 'scale' represents the ScaleDenominator
240 // and for WMS < 1.3.0 it represents the size of a pixel diagonal in meter
241 sc = scale / MapUtils.DEFAULT_PIXEL_SIZE;
242 }
243 themes.add( new GetMapServiceInvokerForUL( this, (UserLayer) layers[i], sc ) );
244 }
245 }
246
247 Executor executor = Executor.getInstance();
248 try {
249 List<ExecutionFinishedEvent<Object>> results;
250 results = executor.performSynchronously( themes, configuration.getDeegreeParams().getRequestTimeLimit() );
251
252 GetMapResult res = renderMap( results );
253 return res;
254 } catch ( InterruptedException e ) {
255 LOG.logError( e.getMessage(), e );
256 String s = Messages.getMessage( "WMS_WAITING" );
257 throw new OGCWebServiceException( getClass().getName(), s );
258 }
259 }
260
261 /**
262 * this methods validates layer in two ways:<br>
263 * a) are layers available from the current WMS<br>
264 * b) If a layer is selected that includes other layers determine all its sublayers having
265 * <Name>s and return them instead
266 *
267 * @param ls
268 * @return the layers
269 * @throws LayerNotDefinedException
270 * @throws InvalidSRSException
271 */
272 private Layer[] validateLayers( Layer[] ls )
273 throws LayerNotDefinedException, InvalidSRSException {
274
275 List<Layer> layer = new ArrayList<Layer>( ls.length );
276 for ( int i = 0; i < ls.length; i++ ) {
277 org.deegree.ogcwebservices.wms.capabilities.Layer l = configuration.getLayer( ls[i].getName() );
278
279 if ( l == null ) {
280 throw new LayerNotDefinedException( Messages.getMessage( "WMS_UNKNOWNLAYER", ls[i].getName() ) );
281 }
282
283 validateSRS( l.getSrs(), ls[i].getName() );
284
285 layer.add( ls[i] );
286 if ( l.getLayer() != null ) {
287 layer = addNestedLayers( l.getLayer(), ls[i].getStyleName(), layer );
288 }
289 }
290
291 return layer.toArray( new Layer[layer.size()] );
292 }
293
294 /**
295 * adds all direct and none direct sub-layers of the passed WMS capabilities layer as
296 *
297 * @see GetMap.Layer to the passed list.
298 * @param layer
299 * @param reqLayer
300 * @param list
301 * @return all sublayers
302 * @throws InvalidSRSException
303 */
304 private List<Layer> addNestedLayers( org.deegree.ogcwebservices.wms.capabilities.Layer[] ll, String styleName,
305 List<Layer> list )
306 throws InvalidSRSException {
307
308 for ( int j = 0; j < ll.length; j++ ) {
309 if ( ll[j].getName() != null ) {
310 String name = ll[j].getName();
311 validateSRS( ll[j].getSrs(), name );
312 list.add( GetMap.createLayer( name, styleName ) );
313 }
314 if ( ll[j].getLayer() != null ) {
315 list = addNestedLayers( ll[j].getLayer(), styleName, list );
316 }
317
318 }
319 return list;
320 }
321
322 /**
323 * throws an exception if the requested SRS is not be supported by the passed layer (name)
324 *
325 * @param srs
326 * @param name
327 * @throws InvalidSRSException
328 */
329 private void validateSRS( String[] srs, String name )
330 throws InvalidSRSException {
331 boolean validSRS = false;
332 for ( int k = 0; k < srs.length; k++ ) {
333 validSRS = srs[k].equalsIgnoreCase( reqCRS.getIdentifier() );
334 if ( validSRS )
335 break;
336 }
337 if ( !validSRS ) {
338 String s = Messages.getMessage( "WMS_UNKNOWN_CRS_FOR_LAYER", reqCRS.getIdentifier(), name );
339 throw new InvalidSRSException( s );
340 }
341 }
342
343 private void invokeNamedLayer( AbstractLayer layer, String styleName, List<Callable<Object>> tasks )
344 throws OGCWebServiceException {
345
346 org.deegree.ogcwebservices.wms.capabilities.Layer lay = configuration.getLayer( layer.getName() );
347
348 LOG.logDebug( "Invoked layer " + layer.getName() );
349
350 if ( validate( lay, layer.getName() ) ) {
351
352 UserStyle us = getStyles( (NamedLayer) layer, styleName );
353 AbstractDataSource[] ds = lay.getDataSource();
354
355 if ( ds.length == 0 ) {
356 LOG.logDebug( "No datasources for layer " + layer.getName() );
357 } else {
358 for ( int j = 0; j < ds.length; j++ ) {
359
360 LOG.logDebug( "Invoked datasource " + ds[j].getClass() + " for layer " + layer.getName() );
361
362 ScaleHint scaleHint = ds[j].getScaleHint();
363 if ( scale >= scaleHint.getMin() && scale < scaleHint.getMax()
364 && isValidArea( ds[j].getValidArea() ) ) {
365 double sc = scale;
366 if ( !version130 ) {
367 // required because for WMS 1.3.0 'scale' represents the
368 // ScaleDenominator
369 // and for WMS < 1.3.0 it represents the size of a pixel diagonal in
370 // meter
371 sc = scale / MapUtils.DEFAULT_PIXEL_SIZE;
372 }
373
374 GetMapServiceInvokerForNL si = new GetMapServiceInvokerForNL( this, (NamedLayer) layer, ds[j],
375 us, sc );
376 tasks.add( si );
377 } else {
378 LOG.logDebug( "Not showing layer " + layer.getName() + " due to scale" );
379 }
380 }
381 }
382 }
383 }
384
385 /**
386 * returns true if the requested boundingbox intersects with the valid area of a datasource
387 *
388 * @param validArea
389 */
390 private boolean isValidArea( Geometry validArea ) {
391
392 if ( validArea != null ) {
393 try {
394 Envelope env = request.getBoundingBox();
395 Geometry geom = GeometryFactory.createSurface( env, reqCRS );
396 if ( !reqCRS.getIdentifier().equals( validArea.getCoordinateSystem().getIdentifier() ) ) {
397 // if requested CRS is not identical to the CRS of the valid area
398 // a transformation must be performed before intersection can
399 // be checked
400 GeoTransformer gt = new GeoTransformer( validArea.getCoordinateSystem() );
401 geom = gt.transform( geom );
402 }
403 return geom.intersects( validArea );
404 } catch ( Exception e ) {
405 // should never happen
406 LOG.logError( "Could not validate WMS datasource area", e );
407 }
408 }
409 return true;
410 }
411
412 /**
413 * creates a StyledLayerDocument containing all requested layer, nested layers if required and
414 * assigend styles. Not considered are nested layers for mixed requests (LAYERS- and SLD(_BODY)-
415 * parameter has been defined)
416 *
417 * @param layers
418 * @param inSLD
419 * @return a combined SLD object
420 * @throws InvalidSRSException
421 */
422 private StyledLayerDescriptor toSLD( GetMap.Layer[] layers, StyledLayerDescriptor inSLD )
423 throws InvalidSRSException {
424 StyledLayerDescriptor sld = null;
425
426 if ( layers != null && layers.length > 0 && inSLD == null ) {
427 // if just a list of layers has been requested
428
429 // create a SLD from the requested LAYERS and assigned STYLES
430 List<AbstractLayer> al = new ArrayList<AbstractLayer>( layers.length * 2 );
431 for ( int i = 0; i < layers.length; i++ ) {
432 AbstractStyle[] as = new AbstractStyle[] { new NamedStyle( layers[i].getStyleName() ) };
433 al.add( new NamedLayer( layers[i].getName(), null, as ) );
434
435 // collect all named nested layers
436 org.deegree.ogcwebservices.wms.capabilities.Layer lla;
437 lla = configuration.getLayer( layers[i].getName() );
438 List<GetMap.Layer> list = new ArrayList<GetMap.Layer>();
439 addNestedLayers( lla.getLayer(), layers[i].getStyleName(), list );
440
441 // add nested layers to list of layers to be handled
442 for ( int j = 0; j < list.size(); j++ ) {
443 GetMap.Layer nestedLayer = list.get( j );
444 as = new AbstractStyle[] { new NamedStyle( nestedLayer.getStyleName() ) };
445 al.add( new NamedLayer( nestedLayer.getName(), null, as ) );
446 }
447 }
448 sld = new StyledLayerDescriptor( al.toArray( new AbstractLayer[al.size()] ), "1.0.0" );
449 } else if ( layers != null && layers.length > 0 && inSLD != null ) {
450 // if layers not null and sld is not null then SLD layers just be
451 // considered if present in the layers list
452 // TODO
453 // layer with nested layers are not handled correctly and I think
454 // it really causes a lot of problems to use them in such a way
455 // because the style assigned to the mesting layer must be
456 // applicable for all nested layers.
457 List<String> list = new ArrayList<String>();
458 for ( int i = 0; i < layers.length; i++ ) {
459 list.add( layers[i].getName() );
460 }
461
462 List<AbstractLayer> newList = new ArrayList<AbstractLayer>( 20 );
463 AbstractLayer[] al = inSLD.getLayers();
464 for ( int i = 0; i < al.length; i++ ) {
465 if ( list.contains( al[i].getName() ) ) {
466 newList.add( al[i] );
467 }
468 }
469 al = new AbstractLayer[newList.size()];
470 sld = new StyledLayerDescriptor( newList.toArray( al ), inSLD.getVersion() );
471
472 // add nested layers for mixed case, nested from original sld
473 AbstractLayer[] as = inSLD.getLayers();
474 for ( AbstractLayer l : as ) {
475 addNestedLayers( l, sld );
476 }
477 } else {
478 // if no layers but a SLD is defined ...
479 AbstractLayer[] as = inSLD.getLayers();
480 for ( AbstractLayer l : as ) {
481 addNestedLayers( l, inSLD );
482 }
483
484 sld = inSLD;
485 }
486
487 return sld;
488 }
489
490 // adds the nested layers to the sld
491 private void addNestedLayers( AbstractLayer l, StyledLayerDescriptor sld ) {
492 if ( !( l instanceof NamedLayer ) ) {
493 return;
494 }
495 if ( configuration.getLayer( l.getName() ) == null ) {
496 return;
497 }
498
499 org.deegree.ogcwebservices.wms.capabilities.Layer[] ls;
500 ls = configuration.getLayer( l.getName() ).getLayer();
501 for ( org.deegree.ogcwebservices.wms.capabilities.Layer lay : ls ) {
502 NamedStyle sty = new NamedStyle( lay.getStyles()[0].getName() );
503 AbstractStyle[] newSty = new AbstractStyle[] { sty };
504 NamedLayer newLay = new NamedLayer( lay.getName(), null, newSty );
505 sld.addLayer( newLay );
506 }
507 }
508
509 /**
510 * returns the <tt>UserStyle</tt>s assigned to a named layer
511 *
512 * @param sldLayer
513 * layer to get the styles for
514 * @param styleName
515 * requested stylename (from the KVP encoding)
516 */
517 private UserStyle getStyles( NamedLayer sldLayer, String styleName )
518 throws OGCWebServiceException {
519
520 AbstractStyle[] styles = sldLayer.getStyles();
521 UserStyle us = null;
522
523 // to avoid retrieving the layer again for each style
524 org.deegree.ogcwebservices.wms.capabilities.Layer layer = null;
525 layer = configuration.getLayer( sldLayer.getName() );
526 int i = 0;
527 while ( us == null && i < styles.length ) {
528 if ( styles[i] instanceof NamedStyle ) {
529 // styles will be taken from the WMS's style repository
530 us = getPredefinedStyle( styles[i].getName(), sldLayer.getName(), layer );
531 } else {
532 // if the requested style fits the name of the defined style or
533 // if the defined style is marked as default and the requested
534 // style if 'default' the condition is true. This includes that
535 // if more than one style with the same name or more than one
536 // style is marked as default always the first will be choosen
537 if ( styleName == null || ( styles[i].getName() != null && styles[i].getName().equals( styleName ) )
538 || ( styleName.equalsIgnoreCase( "$DEFAULT" ) && ( (UserStyle) styles[i] ).isDefault() ) ) {
539 us = (UserStyle) styles[i];
540 }
541 }
542 i++;
543 }
544 if ( us == null ) {
545 // this may happens if the SLD contains a named layer but not
546 // a style! yes this is valid according to SLD spec 1.0.0
547 us = getPredefinedStyle( styleName, sldLayer.getName(), layer );
548 }
549 return us;
550 }
551
552 /**
553 *
554 * @param styleName
555 * @param layerName
556 * @param layer
557 * @return the style
558 * @throws StyleNotDefinedException
559 */
560 private UserStyle getPredefinedStyle( String styleName, String layerName,
561 org.deegree.ogcwebservices.wms.capabilities.Layer layer )
562 throws StyleNotDefinedException {
563 UserStyle us = null;
564 if ( "default".equals( styleName ) ) {
565 us = layer.getStyle( styleName );
566 }
567
568 if ( us == null ) {
569 if ( styleName == null || styleName.length() == 0 || styleName.equals( "$DEFAULT" )
570 || styleName.equals( "default" ) ) {
571 styleName = "default:" + layerName;
572 }
573 }
574
575 us = layer.getStyle( styleName );
576
577 if ( us == null && !( styleName.startsWith( "default" ) ) && !( styleName.startsWith( "$DEFAULT" ) ) ) {
578 String s = Messages.getMessage( "WMS_STYLENOTDEFINED", styleName, layer );
579 throw new StyleNotDefinedException( s );
580 }
581 return us;
582 }
583
584 /**
585 * validates if the requested layer matches the conditions of the request if not a
586 * <tt>WebServiceException</tt> will be thrown. If the layer matches the request, but isn't
587 * able to deviever data for the requested area and/or scale false will be returned. If the
588 * layer matches the request and contains data for the requested area and/or scale true will be
589 * returned.
590 *
591 * @param layer
592 * layer as defined at the capabilities/configuration
593 * @param name
594 * name of the layer (must be submitted seperatly because the layer parameter can be
595 * <tt>null</tt>
596 */
597 private boolean validate( org.deegree.ogcwebservices.wms.capabilities.Layer layer, String name )
598 throws OGCWebServiceException {
599
600 // check if layer is available
601 if ( layer == null ) {
602 throw new LayerNotDefinedException( Messages.getMessage( "WMS_UNKNOWNLAYER", name ) );
603 }
604
605 // check bounding box
606 try {
607 Envelope bbox = request.getBoundingBox();
608 Envelope layerBbox = layer.getLatLonBoundingBox();
609 if ( !request.getSrs().equalsIgnoreCase( "EPSG:4326" ) ) {
610 // transform the bounding box of the request to EPSG:4326
611 GeoTransformer gt = new GeoTransformer( CRSFactory.create( "epsg:4326" ) );
612 bbox = gt.transform( bbox, reqCRS );
613 }
614 if ( !bbox.intersects( layerBbox ) ) {
615 LOG.logDebug( "Not showing layer because the request is out of the bounding box." );
616 return false;
617 }
618
619 } catch ( Exception e ) {
620 LOG.logError( e.getMessage(), e );
621 throw new OGCWebServiceException( Messages.getMessage( "WMS_BBOXCOMPARSION" ) );
622 }
623
624 return true;
625 }
626
627 /**
628 * renders the map from the <tt>DisplayElement</tt>s
629 */
630 private GetMapResult renderMap( List<ExecutionFinishedEvent<Object>> results ) {
631
632 GetMapResult response = null;
633 OGCWebServiceException exce = null;
634
635 ArrayList<Object> list = new ArrayList<Object>( 50 );
636 for ( ExecutionFinishedEvent<Object> evt : results ) {
637 Object o = null;
638
639 // exception handling might be handled in a better way
640 try {
641 o = evt.getResult();
642 } catch ( CancellationException e ) {
643 exce = new OGCWebServiceException( getClass().getName(), e.toString() );
644 } catch ( Throwable e ) {
645 exce = new OGCWebServiceException( getClass().getName(), e.toString() );
646 }
647
648 if ( o instanceof Exception ) {
649 exce = new OGCWebServiceException( getClass().getName(), o.toString() );
650 }
651 if ( o instanceof OGCWebServiceException ) {
652 exce = (OGCWebServiceException) o;
653 break;
654 }
655 if ( o != null ) {
656 list.add( o );
657 }
658 }
659
660 String mime = MimeTypeMapper.toMimeType( request.getFormat() );
661
662 // get target object for rendering
663 Object target = GraphicContextFactory.createGraphicTarget( mime, request.getWidth(), request.getHeight() );
664
665 // get graphic context of the target
666 Graphics g = GraphicContextFactory.createGraphicContext( mime, target );
667 if ( exce == null ) {
668 // only if no exception occured
669 try {
670 Theme[] th = list.toArray( new Theme[list.size()] );
671 org.deegree.graphics.MapView map = null;
672 if ( th.length > 0 ) {
673 map = MapFactory.createMapView( "deegree WMS", request.getBoundingBox(), reqCRS, th,
674 MapUtils.DEFAULT_PIXEL_SIZE );
675 }
676 g.setClip( 0, 0, request.getWidth(), request.getHeight() );
677
678 if ( !request.getTransparency() ) {
679 if ( g instanceof Graphics2D ) {
680 // this ensures real clearing (rendering modifies the color ever so
681 // slightly)
682 ( (Graphics2D) g ).setBackground( request.getBGColor() );
683 g.clearRect( 0, 0, request.getWidth(), request.getHeight() );
684 } else {
685 g.setColor( request.getBGColor() );
686 g.fillRect( 0, 0, request.getWidth(), request.getHeight() );
687 }
688 }
689
690 if ( map != null ) {
691 Theme[] thms = map.getAllThemes();
692 map.addOptimizer( new LabelOptimizer( thms ) );
693 // antialiasing must be switched of for gif output format
694 // because the antialiasing may create more than 255 colors
695 // in the map/image, even just a few colors are defined in
696 // the styles
697 if ( !request.getFormat().equalsIgnoreCase( "image/gif" ) ) {
698 if ( configuration.getDeegreeParams().isAntiAliased() ) {
699 ( (Graphics2D) g ).setRenderingHint( RenderingHints.KEY_ANTIALIASING,
700 RenderingHints.VALUE_ANTIALIAS_ON );
701 ( (Graphics2D) g ).setRenderingHint( RenderingHints.KEY_TEXT_ANTIALIASING,
702 RenderingHints.VALUE_TEXT_ANTIALIAS_ON );
703 }
704 }
705 map.paint( g );
706 }
707 } catch ( Exception e ) {
708 LOG.logError( e.getMessage(), e );
709 exce = new OGCWebServiceException( "GetMapHandler_Impl: renderMap", e.toString() );
710 }
711 }
712
713 // print a copyright note at the left lower corner of the map
714 printCopyright( g, request.getHeight() );
715
716 if ( mime.equals( "image/svg+xml" ) || mime.equals( "image/svg xml" ) ) {
717 Element root = ( (SVGGraphics2D) g ).getRoot();
718 root.setAttribute( "xmlns:xlink", "http://www.w3.org/1999/xlink" );
719 response = WMSProtocolFactory.createGetMapResponse( request, exce, root );
720 } else {
721 response = WMSProtocolFactory.createGetMapResponse( request, exce, target );
722 }
723 g.dispose();
724
725 return response;
726 }
727
728 /**
729 * prints a copyright note at left side of the map bottom. The copyright note will be extracted
730 * from the WMS capabilities/configuration
731 *
732 * @param g
733 * graphic context of the map
734 * @param heigth
735 * height of the map in pixel
736 */
737 private void printCopyright( Graphics g, int heigth ) {
738 WMSDeegreeParams dp = configuration.getDeegreeParams();
739 String copyright = dp.getCopyRight();
740 if ( copyrightImg != null ) {
741 g.drawImage( copyrightImg, 8, heigth - copyrightImg.getHeight() - 5, null );
742 } else {
743 if ( copyright != null ) {
744 g.setFont( new Font( "SANSSERIF", Font.PLAIN, 14 ) );
745 g.setColor( Color.BLACK );
746 g.drawString( copyright, 8, heigth - 15 );
747 g.drawString( copyright, 10, heigth - 15 );
748 g.drawString( copyright, 8, heigth - 13 );
749 g.drawString( copyright, 10, heigth - 13 );
750 g.setColor( Color.WHITE );
751 g.setFont( new Font( "SANSSERIF", Font.PLAIN, 14 ) );
752 g.drawString( copyright, 9, heigth - 14 );
753 }
754 }
755
756 }
757
758 /**
759 * @return the request that is being handled
760 */
761 protected GetMap getRequest() {
762 return request;
763 }
764
765 /**
766 * @return the requests coordinate system
767 */
768 protected CoordinateSystem getRequestCRS() {
769 return reqCRS;
770 }
771
772 }