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 }