//$Header: /home/deegree/jail/deegreerepository/deegree/src/org/deegree/ogcwebservices/wms/DefaultGetMapHandler.java,v 1.74 2006/11/30 20:04:27 poth Exp $
/*---------------- FILE HEADER ------------------------------------------
This file is part of deegree.
Copyright (C) 2001-2006 by:
EXSE, Department of Geography, University of Bonn
http://www.giub.uni-bonn.de/deegree/
lat/lon GmbH
http://www.lat-lon.de
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Contact:
Andreas Poth
lat/lon GmbH
Aennchenstr. 19
53177 Bonn
Germany
E-Mail: poth@lat-lon.de
Prof. Dr. Klaus Greve
Department of Geography
University of Bonn
Meckenheimer Allee 166
53115 Bonn
Germany
E-Mail: greve@giub.uni-bonn.de
---------------------------------------------------------------------------*/
package org.deegree.ogcwebservices.wms;
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.image.BufferedImage;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Callable;
import org.apache.batik.svggen.SVGGraphics2D;
import org.deegree.framework.concurrent.ExecutionFinishedEvent;
import org.deegree.framework.concurrent.ExecutionFinishedListener;
import org.deegree.framework.concurrent.Executor;
import org.deegree.framework.log.ILogger;
import org.deegree.framework.log.LoggerFactory;
import org.deegree.framework.util.ImageUtils;
import org.deegree.framework.util.MapUtils;
import org.deegree.framework.util.MimeTypeMapper;
import org.deegree.graphics.MapFactory;
import org.deegree.graphics.Theme;
import org.deegree.graphics.optimizers.LabelOptimizer;
import org.deegree.graphics.sld.AbstractLayer;
import org.deegree.graphics.sld.AbstractStyle;
import org.deegree.graphics.sld.NamedLayer;
import org.deegree.graphics.sld.NamedStyle;
import org.deegree.graphics.sld.StyledLayerDescriptor;
import org.deegree.graphics.sld.UserLayer;
import org.deegree.graphics.sld.UserStyle;
import org.deegree.i18n.Messages;
import org.deegree.model.crs.CRSFactory;
import org.deegree.model.crs.CoordinateSystem;
import org.deegree.model.crs.GeoTransformer;
import org.deegree.model.crs.IGeoTransformer;
import org.deegree.model.spatialschema.Envelope;
import org.deegree.model.spatialschema.Geometry;
import org.deegree.model.spatialschema.GeometryFactory;
import org.deegree.ogcbase.InvalidSRSException;
import org.deegree.ogcwebservices.InconsistentRequestException;
import org.deegree.ogcwebservices.OGCWebServiceException;
import org.deegree.ogcwebservices.OGCWebServiceResponse;
import org.deegree.ogcwebservices.wms.capabilities.ScaleHint;
import org.deegree.ogcwebservices.wms.configuration.AbstractDataSource;
import org.deegree.ogcwebservices.wms.configuration.WMSConfigurationType;
import org.deegree.ogcwebservices.wms.configuration.WMSConfiguration_1_3_0;
import org.deegree.ogcwebservices.wms.configuration.WMSDeegreeParams;
import org.deegree.ogcwebservices.wms.operation.GetMap;
import org.deegree.ogcwebservices.wms.operation.GetMapResult;
import org.deegree.ogcwebservices.wms.operation.WMSProtocolFactory;
import org.deegree.ogcwebservices.wms.operation.GetMap.Layer;
import org.w3c.dom.Element;
/**
*
*
* @version $Revision: 1.74 $
* @author <a href="mailto:poth@lat-lon.de">Andreas Poth</a>
*/
public class DefaultGetMapHandler implements GetMapHandler, ExecutionFinishedListener<Object[]> {
private static final ILogger LOG = LoggerFactory.getLogger( DefaultGetMapHandler.class );
private static final double DEFAULT_PIXEL_SIZE = 0.00028;
private GetMap request = null;
private Object[] themes = null;
private double scale = 0;
private int count = 0;
private CoordinateSystem reqCRS = null;
private WMSConfigurationType configuration = null;
private BufferedImage copyrightImg = null;
boolean version130 = false;
/**
* Creates a new GetMapHandler object.
*
* @param configuration
* @param request
* request to perform
*/
public DefaultGetMapHandler( WMSConfigurationType configuration, GetMap request ) {
this.request = request;
this.configuration = configuration;
try {
// get copyright image if possible
copyrightImg = ImageUtils.loadImage( configuration.getDeegreeParams().getCopyRight() );
} catch ( Exception e ) {
}
}
/**
* returns the configuration used by the handler
*
* @return the configuration document
*/
public WMSConfigurationType getConfiguration() {
return configuration;
}
/**
* increases the counter variable that holds the number of services that has sent a response.
* All data are available if the counter value equals the number of requested layers.
*/
protected synchronized void increaseCounter() {
count++;
}
/**
* performs a GetMap request and retruns the result encapsulated within a <tt>GetMapResult</tt>
* object.
* <p>
* The method throws an WebServiceException that only shall be thrown if an fatal error occurs
* that makes it imposible to return a result. If something wents wrong performing the request
* (none fatal error) The exception shall be encapsulated within the response object to be
* returned to the client as requested (GetMap-Request EXCEPTION-Parameter).
*
* @return response to the GetMap response
*/
public OGCWebServiceResponse performGetMap()
throws OGCWebServiceException {
// some initialization is done here because the constructor is called by reflection
// and the exceptions won't be properly handled in that case
try {
reqCRS = CRSFactory.create( request.getSrs().toLowerCase() );
} catch ( Exception e ) {
throw new InvalidSRSException(
Messages.getMessage( "WMS_UNKNOWN_CRS", request.getSrs() ) );
}
version130 = "1.3.0".equals( request.getVersion() );
// exceeds the max allowed map width ?
int maxWidth = configuration.getDeegreeParams().getMaxMapWidth();
if ( ( maxWidth != 0 ) && ( request.getWidth() > maxWidth ) ) {
throw new InconsistentRequestException( Messages.getMessage( "WMS_EXCEEDS_WIDTH",
new Integer( maxWidth ) ) );
}
// exceeds the max allowed map height ?
int maxHeight = configuration.getDeegreeParams().getMaxMapHeight();
if ( ( maxHeight != 0 ) && ( request.getHeight() > maxHeight ) ) {
throw new InconsistentRequestException( Messages.getMessage( "WMS_EXCEEDS_HEIGHT",
new Integer( maxHeight ) ) );
}
try {
double pixelSize = 1;
if ( version130 ) {
// required because for WMS 1.3.0 'scale' represents the ScaleDenominator
// and for WMS < 1.3.0 it represents the size of a pixel diagonal in meter
pixelSize = DEFAULT_PIXEL_SIZE;
}
scale = MapUtils.calcScale( request.getWidth(), request.getHeight(),
request.getBoundingBox(), reqCRS, pixelSize );
LOG.logInfo( "OGC WMS scale: " + scale );
} catch ( Exception e ) {
LOG.logError( e.getMessage(), e );
throw new OGCWebServiceException( Messages.getMessage( "WMS_SCALECALC" ) );
}
GetMap.Layer[] ls = request.getLayers();
// if 1.3.0, check for maximum allowed layers
if ( version130 ) {
WMSConfiguration_1_3_0 cfg = (WMSConfiguration_1_3_0) configuration;
if ( ls.length > cfg.getLayerLimit() ) {
String ms = Messages.getMessage( "WMS_EXCEEDS_NUMBER", cfg.getLayerLimit() );
throw new InconsistentRequestException( ms );
}
}
ls = validateLayers( ls );
StyledLayerDescriptor sld = toSLD( ls, request.getStyledLayerDescriptor() );
AbstractLayer[] layers = sld.getLayers();
// get the number of themes assigned to the selected layers
// notice that there maybe more themes as there are layers because
// 1 .. n datasources can be assigned to one layer.
int cntTh = countNumberOfThemes( layers, scale );
themes = new Object[cntTh];
// invokes the data supplyer for each layer in an independ thread
int kk = 0;
for ( int i = 0; i < layers.length; i++ ) {
if ( layers[i] instanceof NamedLayer ) {
String styleName = null;
if ( i < request.getLayers().length ) {
styleName = request.getLayers()[i].getStyleName();
}
// please note that this may be undesirable behaviour, I (schmitz) just added
// the safety 'if' because it will throw nasty exceptions otherwise
// (I don't know what this code actually does)
if ( kk < cntTh ) {
kk = invokeNamedLayer( layers[i], kk, styleName );
}
} else {
double sc = scale;
if ( !version130 ) {
// required because for WMS 1.3.0 'scale' represents the ScaleDenominator
// and for WMS < 1.3.0 it represents the size of a pixel diagonal in meter
sc = scale / DEFAULT_PIXEL_SIZE;
}
GetMapServiceInvokerForUL si = new GetMapServiceInvokerForUL( this, (UserLayer) layers[i],
sc, kk++ );
new Thread( si ).start();
}
}
// TODO
// substitue by an event based approach
waitForFinished();
GetMapResult res = renderMap();
return res;
}
/**
* this methods validates layer in two ways:<br>
* a) are layers available from the current WMS<br>
* b) If a layer is selected that includes other layers determine all its sublayers having
* <Name>s and return them instead
*
* @param ls
* @return the layers
* @throws LayerNotDefinedException
*/
private Layer[] validateLayers( Layer[] ls )
throws LayerNotDefinedException {
List<Layer> layer = new ArrayList<Layer>( ls.length );
for ( int i = 0; i < ls.length; i++ ) {
org.deegree.ogcwebservices.wms.capabilities.Layer l =
configuration.getLayer( ls[i].getName() );
if ( l == null ) {
throw new LayerNotDefinedException( Messages.getMessage( "WMS_UNKNOWNLAYER",
ls[i].getName() ) );
}
layer.add( ls[i] );
if ( l.getLayer() != null ) {
layer = addNestedLayers( l.getLayer(), ls[i].getStyleName(), layer );
}
}
return layer.toArray( new Layer[layer.size()] );
}
/**
* adds all direct and none direct sub-layers of the passed WMS capabilities layer as
*
* @see GetMap.Layer to the passed list.
* @param layer
* @param reqLayer
* @param list
* @return all sublayers
*/
private List<Layer> addNestedLayers( org.deegree.ogcwebservices.wms.capabilities.Layer[] ll,
String styleName, List<Layer> list ) {
for ( int j = 0; j < ll.length; j++ ) {
if ( ll[j].getName() != null ) {
list.add( GetMap.createLayer( ll[j].getName(), styleName ) );
}
if ( ll[j].getLayer() != null ) {
list = addNestedLayers( ll[j].getLayer(), styleName, list );
}
}
return list;
}
/**
* @param layers
* @param kk
* @param i
* @return a counter, kk + 1 I guess
* @throws OGCWebServiceException
*/
private int invokeNamedLayer( AbstractLayer layer, int kk, String styleName )
throws OGCWebServiceException {
org.deegree.ogcwebservices.wms.capabilities.Layer lay = configuration.getLayer( layer.getName() );
if ( validate( lay, layer.getName() ) ) {
UserStyle us = getStyles( (NamedLayer) layer, styleName );
AbstractDataSource[] ds = lay.getDataSource();
for ( int j = 0; j < ds.length; j++ ) {
ScaleHint scaleHint = ds[j].getScaleHint();
if ( scale >= scaleHint.getMin() && scale < scaleHint.getMax()
&& isValidArea( ds[j].getValidArea() ) ) {
double sc = scale;
if ( !version130 ) {
// required because for WMS 1.3.0 'scale' represents the ScaleDenominator
// and for WMS < 1.3.0 it represents the size of a pixel diagonal in meter
sc = scale / DEFAULT_PIXEL_SIZE;
}
GetMapServiceInvokerForNL si = new GetMapServiceInvokerForNL(
this,
(NamedLayer) layer,
ds[j], us, sc,
kk++ );
MapServiceTask task = new MapServiceTask( si );
try {
Executor.getInstance().performAsynchronously( task, this );
} catch ( Exception e ) {
LOG.logDebug( e.getMessage(), e );
throw new OGCWebServiceException( getClass().getName(), e.getMessage() );
}
} else {
LOG.logDebug( Messages.getMessage( "WMS_LAYER_NOT_SHOWN", layer.getName() ) );
}
}
} else {
// set theme to null if no data are available for the requested
// area and/or scale. This will cause this index position will be ignored
// when creating the final result
themes[kk++] = null;
increaseCounter();
}
return kk;
}
/**
* returns the number of <code>DataSource</code>s involved in a GetMap request
*
* @param layers
* @param scale
* @return the number of themes
* @throws OGCWebServiceException
* @throws OGCWebServiceException
*/
private int countNumberOfThemes( AbstractLayer[] layers, double scale )
throws OGCWebServiceException {
int cnt = 0;
for ( int i = 0; i < layers.length; i++ ) {
if ( layers[i] instanceof NamedLayer ) {
org.deegree.ogcwebservices.wms.capabilities.Layer lay = configuration.getLayer( layers[i].getName() );
validate( lay, layers[i].getName() );
if ( lay != null ) {
AbstractDataSource[] ds = lay.getDataSource();
for ( int j = 0; j < ds.length; j++ ) {
ScaleHint scaleHint = ds[j].getScaleHint();
if ( scale >= scaleHint.getMin() && scale < scaleHint.getMax()
&& isValidArea( ds[j].getValidArea() ) ) {
cnt++;
}
}
}
} else {
cnt++;
}
}
return cnt;
}
/**
* returns true if the requested boundingbox intersects with the valid area of a datasource
*
* @param validArea
*/
private boolean isValidArea( Geometry validArea ) {
if ( validArea != null ) {
try {
Envelope env = request.getBoundingBox();
Geometry geom = GeometryFactory.createSurface( env, reqCRS );
if ( !reqCRS.getName().equals( validArea.getCoordinateSystem().getName() ) ) {
// if requested CRS is not identical to the CRS of the valid area
// a transformation must be performed before intersection can
// be checked
IGeoTransformer gt = new GeoTransformer( validArea.getCoordinateSystem() );
geom = gt.transform( geom );
}
return geom.intersects( validArea );
} catch ( Exception e ) {
// should never happen
LOG.logError( Messages.getMessage( "WMS_VALIDATE_DATASOURCE" ), e );
}
}
return true;
}
/**
* runs a loop until all sub requestes (one for each layer) has been finished or the maximum
* time limit has been exceeded.
*
* @throws OGCWebServiceException
*/
private void waitForFinished()
throws OGCWebServiceException {
if ( count < themes.length ) {
// waits until the requested layers are available as <tt>DisplayElements</tt>
// or the time limit has been reached.
// if count == themes.length then no request must be performed
long timeStamp = System.currentTimeMillis();
long lapse = 0;
long timeout = 1000 * ( configuration.getDeegreeParams().getRequestTimeLimit() - 1 );
do {
try {
Thread.sleep( 50 );
lapse += 50;
} catch ( InterruptedException e ) {
LOG.logError( e.getMessage(), e );
String s = Messages.getMessage( "WMS_WAITING" );
throw new OGCWebServiceException( getClass().getName(), s );
}
} while ( count < themes.length && lapse < timeout );
if ( System.currentTimeMillis() - timeStamp >= timeout ) {
String s = Messages.getMessage( "WMS_TIMEOUT" );
LOG.logError( s );
throw new OGCWebServiceException( getClass().getName(), s );
}
}
}
/**
* creates a StyledLayerDocument containing all requested layer,
* nested layers if required and assigend styles. Not considered
* are nested layers for mixed requests (LAYERS- and SLD(_BODY)-
* parameter has been defined)
*
* @param layers
* @param inSLD
* @return
*/
private StyledLayerDescriptor toSLD( GetMap.Layer[] layers, StyledLayerDescriptor inSLD ) {
StyledLayerDescriptor sld = null;
if ( layers != null && layers.length > 0 && inSLD == null ) {
// if just a list of layers has been requested
// create a SLD from the requested LAYERS and assigned STYLES
List<AbstractLayer> al = new ArrayList<AbstractLayer>( layers.length * 2 );
for ( int i = 0; i < layers.length; i++ ) {
AbstractStyle[] as = new AbstractStyle[] { new NamedStyle( layers[i].getStyleName() ) };
al.add( new NamedLayer( layers[i].getName(), null, as ) );
// collect all named nested layers
org.deegree.ogcwebservices.wms.capabilities.Layer lla =
configuration.getLayer( layers[i].getName() );
List<GetMap.Layer> list = new ArrayList<GetMap.Layer>();
addNestedLayers( lla.getLayer(), layers[i].getStyleName(), list );
// add nested layers to list of layers to be handled
for ( int j = 0; j < list.size(); j++ ) {
GetMap.Layer nestedLayer = list.get( j );
as = new AbstractStyle[] { new NamedStyle( nestedLayer.getStyleName() ) };
al.add( new NamedLayer( nestedLayer.getName(), null, as ) );
}
}
sld = new StyledLayerDescriptor( al.toArray( new AbstractLayer[al.size()] ), "1.0.0" );
} else if ( layers != null && layers.length > 0 && inSLD != null ) {
// if layers not null and sld is not null then SLD layers just be
// considered if present in the layers list
// TODO
// layer with nested layers are not handled correctly and I think
// it really causes a lot of problems to use them in such a way
// because the style assigned to the mesting layer must be
// applicable for all nested layers.
List<String> list = new ArrayList<String>();
for ( int i = 0; i < layers.length; i++ ) {
list.add( layers[i].getName() );
}
List<AbstractLayer> newList = new ArrayList<AbstractLayer>( 20 );
AbstractLayer[] al = inSLD.getLayers();
for ( int i = 0; i < al.length; i++ ) {
if ( list.contains( al[i].getName() ) ) {
newList.add( al[i] );
}
}
al = new AbstractLayer[newList.size()];
sld = new StyledLayerDescriptor( newList.toArray( al ), inSLD.getVersion() );
} else {
// if no layers but a SLD is defined ...
sld = inSLD;
}
return sld;
}
/**
* returns the <tt>UserStyle</tt>s assigned to a named layer
*
* @param sldLayer
* layer to get the styles for
* @param styleName
* requested stylename (from the KVP encoding)
*/
private UserStyle getStyles( NamedLayer sldLayer, String styleName )
throws OGCWebServiceException {
AbstractStyle[] styles = sldLayer.getStyles();
UserStyle us = null;
// to avoid retrieving the layer again for each style
org.deegree.ogcwebservices.wms.capabilities.Layer layer = null;
layer = configuration.getLayer( sldLayer.getName() );
int i = 0;
while ( us == null && i < styles.length ) {
if ( styles[i] instanceof NamedStyle ) {
// styles will be taken from the WMS's style repository
us = getPredefinedStyle( styles[i].getName(), sldLayer.getName(), layer );
} else {
// if the requested style fits the name of the defined style or
// if the defined style is marked as default and the requested
// style if 'default' the condition is true. This includes that
// if more than one style with the same name or more than one
// style is marked as default always the first will be choosen
if ( styleName == null
|| ( styles[i].getName() != null && styles[i].getName().equals( styleName ) )
|| ( styleName.equalsIgnoreCase( "$DEFAULT" ) && ( (UserStyle) styles[i] ).isDefault() ) ) {
us = (UserStyle) styles[i];
}
}
i++;
}
if ( us == null ) {
// this may happens if the SLD contains a named layer but not
// a style! yes this is valid according to SLD spec 1.0.0
us = getPredefinedStyle( styleName, sldLayer.getName(), layer );
}
return us;
}
/**
*
* @param styleName
* @param layerName
* @param layer
* @return the style
* @throws StyleNotDefinedException
*/
private UserStyle getPredefinedStyle( String styleName, String layerName,
org.deegree.ogcwebservices.wms.capabilities.Layer layer )
throws StyleNotDefinedException {
UserStyle us = null;
if ( "default".equals( styleName ) ) {
us = layer.getStyle( styleName );
}
if ( us == null ) {
if ( styleName == null || styleName.length() == 0 || styleName.equals( "$DEFAULT" )
|| styleName.equals( "default" ) ) {
styleName = "default:" + layerName;
}
}
us = layer.getStyle( styleName );
if ( us == null && !( styleName.startsWith( "default" ) )
&& !( styleName.startsWith( "$DEFAULT" ) ) ) {
String s = Messages.getMessage( "WMS_STYLENOTDEFINED", styleName, layer );
throw new StyleNotDefinedException( s );
}
return us;
}
/**
* validates if the requested layer matches the conditions of the request if not a
* <tt>WebServiceException</tt> will be thrown. If the layer matches the request, but isn't
* able to deviever data for the requested area and/or scale false will be returned. If the
* layer matches the request and contains data for the requested area and/or scale true will be
* returned.
*
* @param layer
* layer as defined at the capabilities/configuration
* @param name
* name of the layer (must be submitted seperatly because the layer parameter can be
* <tt>null</tt>
*/
private boolean validate( org.deegree.ogcwebservices.wms.capabilities.Layer layer, String name )
throws OGCWebServiceException {
// check if layer is available
if ( layer == null ) {
throw new LayerNotDefinedException( Messages.getMessage( "WMS_UNKNOWNLAYER", name ) );
}
// check scale
ScaleHint scaleHint = layer.getScaleHint();
if ( scale < scaleHint.getMin() || scale > scaleHint.getMax() ) {
LOG.logDebug( Messages.getMessage( "WMS_LAYER_NOT_SHOWN", layer.getName() ) );
return false;
}
// check bounding box
try {
Envelope bbox = request.getBoundingBox();
Envelope layerBbox = layer.getLatLonBoundingBox();
if ( !request.getSrs().equalsIgnoreCase( "EPSG:4326" ) ) {
// transform the bounding box of the request to EPSG:4326
IGeoTransformer gt = new GeoTransformer( CRSFactory.create( "epsg:4326" ) );
bbox = gt.transform( bbox, reqCRS );
}
if ( !bbox.intersects( layerBbox ) ) {
return false;
}
} catch ( Exception e ) {
LOG.logError( e.getMessage(), e );
throw new OGCWebServiceException( Messages.getMessage( "WMS_BBOXCOMPARSION" ) );
}
return true;
}
/**
* put a theme to the passed index of the themes array. The second param passed is a
* <tt>Theme</tt> or an exception
*/
protected synchronized void putTheme( int index, Object o ) {
themes[index] = o;
}
/**
* will be called each time a datasource has been read
*
* @param finishedEvent
*/
public synchronized void executionFinished( ExecutionFinishedEvent<Object[]> finishedEvent ) {
Object[] o = null;
try {
o = finishedEvent.getResult();
} catch ( Throwable t ) {
String msg = Messages.getMessage( "WMS_ASYNC_TASK_ERROR", t.getMessage() );
LOG.logError( msg, t );
}
themes[( (Integer) o[0] ).intValue()] = o[1];
increaseCounter();
}
/**
* renders the map from the <tt>DisplayElement</tt>s
*/
private GetMapResult renderMap() {
GetMapResult response = null;
OGCWebServiceException exce = null;
ArrayList<Object> list = new ArrayList<Object>( 50 );
for ( int i = 0; i < themes.length; i++ ) {
if ( themes[i] instanceof Exception ) {
exce = new OGCWebServiceException( getClass().getName(), themes[i].toString() );
}
if ( themes[i] instanceof OGCWebServiceException ) {
exce = (OGCWebServiceException) themes[i];
break;
}
if ( themes[i] != null ) {
list.add( themes[i] );
}
}
String mime = MimeTypeMapper.toMimeType( request.getFormat() );
// get target object for rendering
Object target = GraphicContextFactory.createGraphicTarget( mime, request.getWidth(),
request.getHeight() );
// get graphic context of the target
Graphics g = GraphicContextFactory.createGraphicContext( mime, target );
if ( exce == null ) {
// only if no exception occured
try {
Theme[] th = list.toArray( new Theme[list.size()] );
org.deegree.graphics.MapView map = null;
if ( th.length > 0 ) {
map = MapFactory.createMapView( "deegree WMS", request.getBoundingBox(),
reqCRS, th );
}
g.setClip( 0, 0, request.getWidth(), request.getHeight() );
if ( !request.getTransparency() ) {
g.setColor( request.getBGColor() );
g.fillRect( 0, 0, request.getWidth(), request.getHeight() );
}
if ( map != null ) {
Theme[] themes = map.getAllThemes();
map.addOptimizer( new LabelOptimizer( themes ) );
// antialiasing must be switched of for gif output format
// because the antialiasing may create more than 255 colors
// in the map/image, even just a few colors are defined in
// the styles
if ( !request.getFormat().equalsIgnoreCase( "image/gif" ) ) {
if ( configuration.getDeegreeParams().isAntiAliased() ) {
( (Graphics2D) g ).setRenderingHint( RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON );
( (Graphics2D) g ).setRenderingHint(
RenderingHints.KEY_TEXT_ANTIALIASING,
RenderingHints.VALUE_TEXT_ANTIALIAS_ON );
}
}
map.paint( g );
}
} catch ( Exception e ) {
LOG.logError( e.getMessage(), e );
exce = new OGCWebServiceException( "GetMapHandler_Impl: renderMap", e.toString() );
}
}
// print a copyright note at the left lower corner of the map
printCopyright( g, request.getHeight() );
if ( mime.equals( "image/svg+xml" ) || mime.equals( "image/svg xml" ) ) {
Element root = ( (SVGGraphics2D) g ).getRoot();
root.setAttribute( "xmlns:xlink", "http://www.w3.org/1999/xlink" );
response = WMSProtocolFactory.createGetMapResponse( request, exce, root );
} else {
response = WMSProtocolFactory.createGetMapResponse( request, exce, target );
}
g.dispose();
return response;
}
/**
* prints a copyright note at left side of the map bottom. The copyright note will be extracted
* from the WMS capabilities/configuration
*
* @param g
* graphic context of the map
* @param heigth
* height of the map in pixel
*/
private void printCopyright( Graphics g, int heigth ) {
WMSDeegreeParams dp = configuration.getDeegreeParams();
String copyright = dp.getCopyRight();
if ( copyrightImg != null ) {
g.drawImage( copyrightImg, 8, heigth - copyrightImg.getHeight() - 5, null );
} else {
if ( copyright != null ) {
g.setFont( new Font( "SANSSERIF", Font.PLAIN, 14 ) );
g.setColor( Color.BLACK );
g.drawString( copyright, 8, heigth - 15 );
g.drawString( copyright, 10, heigth - 15 );
g.drawString( copyright, 8, heigth - 13 );
g.drawString( copyright, 10, heigth - 13 );
g.setColor( Color.WHITE );
g.setFont( new Font( "SANSSERIF", Font.PLAIN, 14 ) );
g.drawString( copyright, 9, heigth - 14 );
}
}
}
// ///////////////////////////////////////////////////////////////////////////
// inner classes //
// ///////////////////////////////////////////////////////////////////////////
private class MapServiceTask implements Callable<Object[]> {
GetMapServiceInvokerForNL invoker;
MapServiceTask( GetMapServiceInvokerForNL invoker ) {
this.invoker = invoker;
}
public Object[] call()
throws Exception {
return (Object[]) this.invoker.run();
}
}
/**
* @return the request that is being handled
*/
protected GetMap getRequest() {
return request;
}
/**
* @return the requests coordinate system
*/
protected CoordinateSystem getRequestCRS() {
return reqCRS;
}
}
/* *************************************************************************************************
$Log: DefaultGetMapHandler.java,v $
Revision 1.74 2006/11/30 20:04:27 poth
bug fix - handling SLD GetMap requests
Revision 1.73 2006/11/29 21:28:30 poth
bug fixing - SLD GetMap requests containing user layers with featuretypeconstraints
Revision 1.72 2006/11/29 14:10:24 schmitz
Added more messages. The Default handlers should now be fully externalized.
Revision 1.71 2006/11/29 13:00:36 schmitz
Cleaned up WMS messages.
Revision 1.70 2006/11/29 12:28:19 schmitz
Removed unnecessary test for CRS in validate().
Revision 1.69 2006/11/27 09:07:52 poth
JNI integration of proj4 has been removed. The CRS functionality now will be done by native deegree code.
Revision 1.67 2006/11/24 10:52:40 poth
proj reference removed
Revision 1.66 2006/11/24 09:33:12 schmitz
Fixed a bug concerning layer specific scale hints.
Using the central i18n mechanism.
Changed the localwfs mechanism to just use one WFS and not recreate them.
Revision 1.65 2006/11/17 16:44:27 schmitz
Removed debugging output.
Revision 1.64 2006/11/17 16:38:31 schmitz
Added support for GetMap POST requests.
Sublayers of requested layers specified in SLD are now added as well.
Revision 1.63 2006/10/27 09:52:23 schmitz
Brought the WMS up to date regarding 1.1.1 and 1.3.0 conformance.
Fixed a bug while creating the default GetLegendGraphics URLs.
Revision 1.62 2006/10/17 20:31:17 poth
*** empty log message ***
Revision 1.61 2006/10/10 13:46:02 schmitz
Updated the WMS GetMap and GetFeatureInfo requests to handle sublayers of layers as well.
Revision 1.60 2006/10/07 15:06:08 poth
infomation about requested wms version made global
Revision 1.59 2006/10/04 07:43:38 poth
bug fix
Revision 1.58 2006/09/27 16:46:41 poth
transformation method signature changed
Revision 1.57 2006/09/26 14:22:04 poth
bug fix
Revision 1.56 2006/09/25 20:32:08 poth
pass scale to GetMapServiceInvokerNL
Revision 1.55 2006/09/25 12:47:00 poth
bug fixes - map scale calculation
Revision 1.54 2006/09/15 09:38:51 schmitz
Removed debugging output.
Revision 1.53 2006/09/15 09:18:29 schmitz
Updated WMS to use SLD or SLD_BODY sld documents as default when also giving
LAYERS and STYLES parameters at the same time.
Revision 1.52 2006/09/08 08:42:01 schmitz
Updated the WMS to be 1.1.1 conformant once again.
Cleaned up the WMS code.
Added cite WMS test data.
Revision 1.51 2006/08/09 15:26:55 poth
bug fix - case sensitivity of crs corrected
Revision 1.50 2006/08/08 09:57:11 mschneider
Added generics for type safety - constrained the return type of tasks.
Revision 1.49 2006/08/07 13:52:05 mschneider
Refactored due to changes in
deegree.framework.concurrent package.
Revision 1.48 2006/07/29 08:55:06 poth
implementation changed to new org.deegree.framework.concurrent packeage
Revision 1.47 2006/07/28 08:01:27 schmitz
Updated the WMS for 1.1.1 compliance. Fixed some
documentation.
Revision 1.46 2006/07/12 14:46:17 poth comment footer added
************************************************************************************************ */