001    //$HeadURL: https://svn.wald.intevation.org/svn/deegree/base/branches/2.3_testing/src/org/deegree/ogcwebservices/wmps/PrintMapHandler.java $
002    /*----------------------------------------------------------------------------
003     This file is part of deegree, http://deegree.org/
004     Copyright (C) 2001-2009 by:
005     Department of Geography, University of Bonn
006     and
007     lat/lon GmbH
008    
009     This library is free software; you can redistribute it and/or modify it under
010     the terms of the GNU Lesser General Public License as published by the Free
011     Software Foundation; either version 2.1 of the License, or (at your option)
012     any later version.
013     This library is distributed in the hope that it will be useful, but WITHOUT
014     ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
015     FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
016     details.
017     You should have received a copy of the GNU Lesser General Public License
018     along with this library; if not, write to the Free Software Foundation, Inc.,
019     59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
020    
021     Contact information:
022    
023     lat/lon GmbH
024     Aennchenstr. 19, 53177 Bonn
025     Germany
026     http://lat-lon.de/
027    
028     Department of Geography, University of Bonn
029     Prof. Dr. Klaus Greve
030     Postfach 1147, 53001 Bonn
031     Germany
032     http://www.geographie.uni-bonn.de/deegree/
033    
034     e-mail: info@deegree.org
035     ----------------------------------------------------------------------------*/
036    
037    package org.deegree.ogcwebservices.wmps;
038    
039    import java.awt.Color;
040    import java.awt.Graphics;
041    import java.awt.Graphics2D;
042    import java.awt.Image;
043    import java.awt.geom.AffineTransform;
044    import java.awt.image.AffineTransformOp;
045    import java.awt.image.BufferedImage;
046    import java.io.ByteArrayOutputStream;
047    import java.io.File;
048    import java.io.FileOutputStream;
049    import java.io.IOException;
050    import java.io.InputStream;
051    import java.io.OutputStream;
052    import java.io.StringReader;
053    import java.net.MalformedURLException;
054    import java.net.URI;
055    import java.net.URISyntaxException;
056    import java.net.URL;
057    import java.sql.Connection;
058    import java.sql.SQLException;
059    import java.text.MessageFormat;
060    import java.util.ArrayList;
061    import java.util.HashMap;
062    import java.util.List;
063    import java.util.Map;
064    
065    import net.sf.jasperreports.engine.JREmptyDataSource;
066    import net.sf.jasperreports.engine.JRException;
067    import net.sf.jasperreports.engine.JasperExportManager;
068    import net.sf.jasperreports.engine.JasperFillManager;
069    import net.sf.jasperreports.engine.JasperPrint;
070    import net.sf.jasperreports.engine.JasperPrintManager;
071    
072    import org.deegree.datatypes.values.Values;
073    import org.deegree.framework.log.ILogger;
074    import org.deegree.framework.log.LoggerFactory;
075    import org.deegree.framework.util.ImageUtils;
076    import org.deegree.framework.util.MapUtils;
077    import org.deegree.framework.util.StringTools;
078    import org.deegree.framework.xml.NamespaceContext;
079    import org.deegree.framework.xml.XMLFragment;
080    import org.deegree.framework.xml.XMLParsingException;
081    import org.deegree.framework.xml.XMLTools;
082    import org.deegree.graphics.sld.StyledLayerDescriptor;
083    import org.deegree.graphics.transformation.WorldToScreenTransform;
084    import org.deegree.i18n.Messages;
085    import org.deegree.model.coverage.grid.WorldFile;
086    import org.deegree.model.crs.CRSFactory;
087    import org.deegree.model.crs.CoordinateSystem;
088    import org.deegree.model.crs.UnknownCRSException;
089    import org.deegree.model.spatialschema.Envelope;
090    import org.deegree.model.spatialschema.GeometryFactory;
091    import org.deegree.model.spatialschema.Point;
092    import org.deegree.ogcbase.CommonNamespaces;
093    import org.deegree.ogcwebservices.InconsistentRequestException;
094    import org.deegree.ogcwebservices.OGCWebServiceException;
095    import org.deegree.ogcwebservices.wmps.configuration.PrintMapParam;
096    import org.deegree.ogcwebservices.wmps.configuration.WMPSConfiguration;
097    import org.deegree.ogcwebservices.wmps.operation.PrintMap;
098    import org.deegree.ogcwebservices.wmps.operation.PrintMapResponseDocument;
099    import org.deegree.ogcwebservices.wmps.operation.TextArea;
100    import org.deegree.ogcwebservices.wms.capabilities.Layer;
101    import org.deegree.ogcwebservices.wms.capabilities.LegendURL;
102    import org.deegree.ogcwebservices.wms.capabilities.ScaleHint;
103    import org.deegree.ogcwebservices.wms.capabilities.Style;
104    import org.deegree.ogcwebservices.wms.configuration.AbstractDataSource;
105    import org.deegree.ogcwebservices.wms.configuration.LocalWCSDataSource;
106    import org.deegree.ogcwebservices.wms.configuration.RemoteWCSDataSource;
107    import org.deegree.ogcwebservices.wms.configuration.RemoteWMSDataSource;
108    import org.deegree.ogcwebservices.wms.operation.GetMap;
109    import org.w3c.dom.Document;
110    import org.w3c.dom.Element;
111    import org.w3c.dom.Node;
112    
113    /**
114     * Handles the PrintMap request. Retrieves the request from the DB and creates a pdf file.
115     * 
116     * @author <a href="mailto:deshmukh@lat-lon.de">Anup Deshmukh</a>
117     * @author last edited by: $Author: apoth $
118     * 
119     * @version 2.0, $Revision: 20691 $, $Date: 2009-11-10 13:48:33 +0100 (Di, 10. Nov 2009) $
120     */
121    public class PrintMapHandler implements Runnable {
122    
123        private static final ILogger LOG = LoggerFactory.getLogger( PrintMapHandler.class );
124    
125        private final double TILE_MAX_SIZE = 800;
126    
127        private final String FORMAT = ".png";
128    
129        private final String MIMETYPE = "image/png";
130    
131        private final String EXCEPTION = "application/vnd.ogc.se_inimage";
132    
133        private WMPSConfiguration configuration;
134    
135        private String message;
136    
137        /**
138         * Creates a new instance of the PrintMapHandler for the current configuration.
139         * 
140         * @param configuration
141         */
142        public PrintMapHandler( WMPSConfiguration configuration ) {
143            this.configuration = configuration;
144        }
145    
146        /**
147         * Run a new thread for each PrintMap request. This Thread runs till no more PrintMap requests are available in the
148         * DB.
149         * 
150         * @see java.lang.Runnable#run()
151         */
152        public void run() {
153    
154            RequestManager manager = null;
155            PrintMapResponseDocument response = null;
156            WMPSDatabase wmpsDB = null;
157            Connection connection = null;
158            try {
159                wmpsDB = new WMPSDatabase( this.configuration.getDeegreeParams().getCacheDatabase() );
160    
161                while ( true ) {
162                    connection = wmpsDB.acquireConnection();
163                    // get request from DB
164                    PrintMap printMap = wmpsDB.selectPrintMapRequest( connection );
165                    System.out.println( 1 + " " + printMap );
166                    if ( printMap == null ) {
167                        break;
168                    }
169    
170                    try {
171                        LOG.logDebug( "Performing print map" );
172                        manager = new DefaultRequestManager( this.configuration, printMap );
173                        performPrintMap( printMap, false );
174                        System.out.println( 2 );
175                        wmpsDB.updateDB( connection, printMap.getId(), printMap.getTimestamp(), "TRUE" );
176                        response = manager.createFinalResponse( this.message, null );
177                        manager.sendEmail( response );
178                    } catch ( Exception e ) {
179                        LOG.logError( e.getMessage(), e );
180                        wmpsDB.updateDB( connection, printMap.getId(), printMap.getTimestamp(), "FAILED" );
181                        sendExceptionMail( manager, response, e );
182                    } finally {
183                        releaseConnection( wmpsDB, connection );
184                    }
185                    LOG.logDebug( "Done performing PrintMap request." );
186                }
187            } catch ( Exception e ) {
188                LOG.logError( e.getMessage(), e );
189                sendExceptionMail( manager, response, e );
190            } finally {
191                releaseConnection( wmpsDB, connection );
192            }
193        }
194    
195        private void sendExceptionMail( RequestManager manager, PrintMapResponseDocument response, Exception e ) {
196            if ( manager != null ) {
197                try {
198                    response = manager.createFinalResponse( "print map failed", e.getMessage() );
199                    manager.sendEmail( response );
200                } catch ( Exception e1 ) {
201                    // should just happen if mail server is not reachable
202                    // in this case the error just can be logged
203                    XMLFragment doc = new XMLFragment( response.getRootElement() );
204                    LOG.logDebug( doc.getAsString() );
205                    LOG.logError( e.getMessage(), e );
206                }
207            }
208        }
209    
210        private void releaseConnection( WMPSDatabase wmpsDB, Connection connection ) {
211            try {
212                if ( !connection.isClosed() ) {
213                    wmpsDB.releaseConnection( connection );
214                }
215            } catch ( SQLException e ) {
216                // should never happen
217                LOG.logError( e.getMessage(), e );
218            }
219        }
220    
221        /**
222         * performs a sychronous printMap processing
223         * 
224         * @param printMap
225         * @return byte[]
226         * @throws Exception
227         */
228        public byte[] runSynchronous( PrintMap printMap )
229                                throws Exception {
230            return performPrintMap( printMap, true );
231        }
232    
233        /**
234         * From each PrintMap request run the WMS GetMap request.
235         * 
236         * @param printMap
237         * @param synchronous
238         * @return byte[]
239         * @throws PrintMapServiceException
240         * @throws IOException
241         */
242        private byte[] performPrintMap( PrintMap printMap, boolean synchronous )
243                                throws PrintMapServiceException, IOException {
244    
245            Map<String, Layer> config_layers = retrieveLayersFromConfig( printMap );
246    
247            int[] mapParams = getMapParamsFromTemplate( printMap.getTemplate() );
248            int scaleDenominator = printMap.getScaleDenominator();
249            Envelope bbox = printMap.getBBOX();
250            if ( bbox == null ) {
251                LOG.logDebug( "BBOX not defined. Using the center and scale to calculate a new BBOX." );
252                Point center = printMap.getCenter();
253                bbox = createBBOX( center, scaleDenominator, mapParams[0], mapParams[1] );
254            } else { // Adjust aspect ratio of bbox to aspect ratio of template map area
255                double bboxAspectRatio = bbox.getWidth() / bbox.getHeight();
256                double printAspectRatio = ( (double) mapParams[0] ) / mapParams[1];
257                if ( bboxAspectRatio > printAspectRatio ) {
258                    double centerY = bbox.getMin().getY() + ( ( bbox.getMax().getY() - bbox.getMin().getY() ) / 2d );
259                    double height = bbox.getWidth() * ( 1.0 / printAspectRatio );
260                    double minY = centerY - ( height / 2d );
261                    double maxY = centerY + ( height / 2d );
262                    bbox = GeometryFactory.createEnvelope( bbox.getMin().getX(), minY, bbox.getMax().getX(), maxY,
263                                                           bbox.getCoordinateSystem() );
264                } else {
265                    double centerX = bbox.getMin().getX() + ( ( bbox.getMax().getX() - bbox.getMin().getX() ) / 2d );
266                    double width = bbox.getHeight() * printAspectRatio;
267                    double minX = centerX - ( width / 2d );
268                    double maxX = centerX + ( width / 2d );
269                    bbox = GeometryFactory.createEnvelope( minX, bbox.getMin().getY(), maxX, bbox.getMax().getY(),
270                                                           bbox.getCoordinateSystem() );
271                }
272            }
273            if ( scaleDenominator == -1 ) {
274                LOG.logDebug( "Scale not defined. Using MapUtil to calculate the scale denominator for the current bbox and map sizes" );
275                double pixelSize = WMPSConfiguration.INCH2M
276                                   / configuration.getDeegreeParams().getPrintMapParam().getTargetResolution();
277                scaleDenominator = (int) MapUtils.calcScale( mapParams[0], mapParams[1], bbox, bbox.getCoordinateSystem(),
278                                                             pixelSize );
279                LOG.logDebug( "calculated map scale denominator: ", scaleDenominator );
280            }
281            BufferedImage mapImage = null;
282            try {
283                mapImage = performBuildMapImage( config_layers, printMap, bbox, mapParams[0], mapParams[1] );
284                saveImageToDisk( printMap, mapImage, "MAP", bbox );
285                String s = StringTools.concat( 100, "Retrieved PrintMap request '", printMap.getId(), "' and saved to disk" );
286                LOG.logDebug( s );
287            } catch ( OGCWebServiceException e ) {
288                LOG.logError( e.getMessage(), e );
289                throw new PrintMapServiceException( Messages.getMessage( "WMPS_ERROR_PERFORMING_PRINTMAP" ) );
290            } catch ( IOException e ) {
291                LOG.logError( e.getMessage(), e );
292                throw new PrintMapServiceException( Messages.getMessage( "WMPS_ERROR_WRITING_MAP_TMP_FILE" ) );
293            }
294            if ( printMap.getLegend() ) {
295                try {
296                    BufferedImage legendImage = performGetLegend( config_layers, printMap, mapParams );
297                    saveImageToDisk( printMap, legendImage, "LEGEND", null );
298                    LOG.logDebug( "Saved the legend image file to disk." );
299                } catch ( IOException e ) {
300                    LOG.logError( e.getMessage(), e );
301                    throw new PrintMapServiceException( Messages.getMessage( "WMPS_ERROR_WRITING_LEGEND_TMP_FILE" ) );
302                }
303            }
304            return exportOutput( printMap, scaleDenominator, synchronous );
305        }
306    
307        /**
308         * Build the Map image for the current PrintMap request. Vector and Raster layers are handled seperately.
309         * 
310         * @param config_layers
311         * @param printMap
312         * @param bbox
313         * @param width
314         * @param height
315         * @return BufferedImage
316         * @throws OGCWebServiceException
317         */
318        private BufferedImage performBuildMapImage( Map<String, Layer> config_layers, PrintMap printMap, Envelope bbox,
319                                                    double width, double height )
320                                throws OGCWebServiceException {
321    
322            BufferedImage targetImage = new BufferedImage( (int) width, (int) height, BufferedImage.TYPE_INT_ARGB );
323            Graphics2D g = (Graphics2D) targetImage.getGraphics();
324    
325            if ( !printMap.getTransparent() ) {
326                g.setBackground( printMap.getBGColor() );
327            }
328    
329            handleLayerDatasources( config_layers, printMap, bbox, width, height, g );
330            g.dispose();
331    
332            return targetImage;
333        }
334    
335        /**
336         * Determines the datasource for each layer(vector, raster).
337         * 
338         * @param config_layers
339         * @param printMap
340         * @param bbox
341         * @param width
342         * @param height
343         * @param g
344         * @throws OGCWebServiceException
345         * @throws InconsistentRequestException
346         */
347        private void handleLayerDatasources( Map<String, Layer> config_layers, PrintMap printMap, Envelope bbox,
348                                             double width, double height, Graphics g )
349                                throws OGCWebServiceException, InconsistentRequestException {
350    
351            double px = WMPSConfiguration.INCH2M
352                        / configuration.getDeegreeParams().getPrintMapParam().getTargetResolution();
353            CoordinateSystem crs;
354            try {
355                crs = CRSFactory.create( printMap.getSRS() );
356            } catch ( UnknownCRSException e1 ) {
357                throw new InconsistentRequestException( e1.getMessage() );
358            }
359            double scale = MapUtils.calcScale( (int) width, (int) height, bbox, crs, px );
360            GetMap.Layer[] printMapLayers = printMap.getLayers();
361            for ( int i = 0; i < printMapLayers.length; i++ ) {
362                String name = printMapLayers[i].getName();
363    
364                Layer configLayer = config_layers.get( name );
365                ScaleHint scaleHint = configLayer.getScaleHint();
366                if ( LOG.getLevel() == ILogger.LOG_DEBUG ) {
367                    LOG.logDebug( "current scale", scale );
368                    LOG.logDebug( "valid layer min scale", scaleHint.getMin() );
369                    LOG.logDebug( "valid layer max scale", scaleHint.getMax() );
370                }
371                if ( scale >= scaleHint.getMin() && scale < scaleHint.getMax() ) {
372                    String type = determineDatasourceType( configLayer, scale );
373                    if ( type != null && "vector".equalsIgnoreCase( type ) ) {
374    
375                        GetMap.Layer[] lay = null;
376                        if ( ( printMapLayers.length - i ) > 1 ) {
377                            lay = getContinuousVectorLayer( config_layers, printMapLayers, scale, i );
378                        } else {
379                            lay = new GetMap.Layer[] { printMapLayers[i] };
380                        }
381                        try {
382                            for ( int j = 0; j < lay.length; j++ ) {
383                                LOG.logDebug( "handle as vector layer: ", lay[j].getName() );
384                            }
385                            handleVectorDataLayer( printMap, bbox, width, height, g, lay );
386                        } catch ( OGCWebServiceException e ) {
387                            LOG.logError( e.getMessage(), e );
388                            throw new OGCWebServiceException( Messages.getMessage( "WMPS_ERROR_HANDLING_GETMAP", name ) );
389                        }
390                        // Skip the number of layers already handled.
391                        if ( lay.length != 1 ) {
392                            i = i + ( lay.length - 1 );
393                        }
394                    } else {
395                        // must be a raster data layer
396                        GetMap.Layer[] lay = new GetMap.Layer[] { printMapLayers[i] };
397                        LOG.logDebug( "handle as raster layer: ", printMapLayers[i].getName() );
398                        try {
399                            handleRasterDataLayer( printMap, bbox, width, height, g, lay );
400                        } catch ( OGCWebServiceException e ) {
401                            LOG.logError( e.getMessage(), e );
402                            throw new OGCWebServiceException( Messages.getMessage( "WMPS_ERROR_HANDLING_GETMAP", name ) );
403                        }
404                    }
405                } else {
406                    String s = StringTools.concat( 100, "No Datasource available for layer: ", name, " at scale: ", scale );
407                    LOG.logInfo( s );
408                }
409            }
410    
411        }
412    
413        /**
414         * returns an array of layers that:
415         * <ul>
416         * <li>a) made of vector data
417         * <li>b) are continous in the requested list of layers
418         * </ul>
419         * 
420         * @param config_layers
421         * @param printMapLayers
422         * @param mapScale
423         *            scale of the entire map
424         * @param i
425         * @return Layer[]
426         */
427        private GetMap.Layer[] getContinuousVectorLayer( Map<String, Layer> config_layers, GetMap.Layer[] printMapLayers,
428                                                         double mapScale, int i ) {
429    
430            List<GetMap.Layer> layers = new ArrayList<GetMap.Layer>( printMapLayers.length );
431            int counter = 0;
432            for ( ; i < printMapLayers.length; i++ ) {
433                String name = printMapLayers[i].getName();
434                Layer configLayer = config_layers.get( name );
435                String type = determineDatasourceType( configLayer, mapScale );
436                if ( "vector".equals( type ) ) {
437                    layers.add( counter, printMapLayers[i] );
438                    counter++;
439                } else {
440                    break;
441                }
442            }
443    
444            return layers.toArray( new GetMap.Layer[layers.size()] );
445        }
446    
447        /**
448         * Perform the GetMap request for vector layers.
449         * 
450         * @param printMap
451         * @param bbox
452         * @param width
453         * @param height
454         * @param g
455         * @param lay
456         * @throws OGCWebServiceException
457         */
458        private void handleVectorDataLayer( PrintMap printMap, Envelope bbox, double width, double height, Graphics g,
459                                            GetMap.Layer[] lay )
460                                throws OGCWebServiceException {
461    
462            URL sldURL = null;
463            StyledLayerDescriptor sld = null;
464    
465            GetMap getMap = GetMap.create( printMap.getVersion(), printMap.getId(), lay, null, null, this.MIMETYPE,
466                                           (int) width, (int) height, printMap.getSRS(), bbox, printMap.getTransparent(),
467                                           printMap.getBGColor(), this.EXCEPTION, null, sldURL, sld,
468                                           printMap.getVendorSpecificParameters() );
469            DefaultGetMapHandler gmh = new DefaultGetMapHandler( this.configuration, getMap );
470            gmh.performGetMap( g );
471    
472        }
473    
474        /**
475         * Perform the GetMap request for each raster layer. Here the raster layer is divided into tiles for memory handling
476         * efficiency.
477         * 
478         * @param printMap
479         * @param bbox
480         * @param width
481         * @param height
482         * @param g
483         * @param lay
484         * @throws OGCWebServiceException
485         */
486        private void handleRasterDataLayer( PrintMap printMap, Envelope bbox, double width, double height, Graphics g,
487                                            GetMap.Layer[] lay )
488                                throws OGCWebServiceException {
489    
490            // Get Map (missing) parameters.
491            Values elevation = null;
492            Map<String, Values> sampleDimension = null;
493            Values time = null;
494            URL sldURL = null;
495            StyledLayerDescriptor sld = null;
496    
497            boolean xRemainder = false;
498            int wtx = (int) width % (int) this.TILE_MAX_SIZE;
499            int nkx = (int) width / (int) this.TILE_MAX_SIZE;
500            if ( wtx > 0 ) {
501                xRemainder = true;
502                nkx++;
503            }
504    
505            boolean yRemainder = false;
506            int wty = (int) height % (int) this.TILE_MAX_SIZE;
507            int nky = (int) height / (int) this.TILE_MAX_SIZE;
508            if ( wty > 0 ) {
509                yRemainder = true;
510                nky++;
511            }
512    
513            WorldToScreenTransform trans = new WorldToScreenTransform( bbox.getMin().getX(), bbox.getMin().getY(),
514                                                                       bbox.getMax().getX(), bbox.getMax().getY(), 0d, 0d,
515                                                                       width - 1, height - 1 );
516    
517            for ( int x = 0; x < nkx; x++ ) {
518                double tileWidth = this.TILE_MAX_SIZE;
519                if ( xRemainder ) {
520                    if ( x == nkx - 1 ) {
521                        tileWidth = wtx;
522                    }
523                }
524                for ( int y = 0; y < nky; y++ ) {
525                    double tileHeight = this.TILE_MAX_SIZE;
526                    if ( yRemainder ) {
527                        if ( y == nky - 1 ) {
528                            tileHeight = wty;
529                        }
530                    }
531                    BufferedImage bi = new BufferedImage( (int) tileWidth, (int) tileHeight, BufferedImage.TYPE_INT_ARGB );
532                    Graphics tileg = bi.getGraphics();
533                    // calc bbox
534                    Envelope bb = calculateTileBBOX( trans, x, y, tileWidth, tileHeight, bbox.getCoordinateSystem() );
535                    // create GetMap
536                    GetMap getMap = GetMap.create( printMap.getVersion(), printMap.getId(), lay, elevation,
537                                                   sampleDimension, this.MIMETYPE, (int) tileWidth, (int) tileHeight,
538                                                   printMap.getSRS(), bb, printMap.getTransparent(), printMap.getBGColor(),
539                                                   this.EXCEPTION, time, sldURL, sld,
540                                                   printMap.getVendorSpecificParameters() );
541    
542                    // performGetMap( tileg );
543                    DefaultGetMapHandler gmh = new DefaultGetMapHandler( this.configuration, getMap );
544                    gmh.performGetMap( tileg );
545                    tileg.dispose();
546                    g.drawImage( bi, (int) Math.round( x * this.TILE_MAX_SIZE ),
547                                 (int) Math.round( y * this.TILE_MAX_SIZE ), (int) Math.round( tileWidth ),
548                                 (int) Math.round( tileHeight + 1 ), null );
549                }
550            }
551    
552        }
553    
554        /**
555         * Calculate the tile BBOX for the raster datalayer.
556         * 
557         * @param trans
558         * @param x
559         * @param y
560         * @param tileWidth
561         * @param tileHeight
562         * @param crs
563         * @return Envelope
564         */
565        private Envelope calculateTileBBOX( WorldToScreenTransform trans, int x, int y, double tileWidth,
566                                            double tileHeight, CoordinateSystem crs ) {
567    
568            double x1 = x * this.TILE_MAX_SIZE;
569            double y1 = y * this.TILE_MAX_SIZE;
570            double x2 = x1 + tileWidth;
571            double y2 = y1 + tileHeight;
572    
573            double minX = trans.getSourceX( x1 );
574            double maxX = trans.getSourceX( x2 );
575            double minY = trans.getSourceY( y2 );
576            double maxY = trans.getSourceY( y1 );
577    
578            return GeometryFactory.createEnvelope( minX, minY, maxX, maxY, crs );
579        }
580    
581        /**
582         * Parses the Layer datastores to determine the type of the layer. Layers having a vector datasource as well as a
583         * raster datasource for the passed mapScale will be treated as raster layers
584         * 
585         * @param layer
586         * @param mapScale
587         *            scale of the entire map
588         * @return String either raster, vector or nodatasource
589         */
590        private String determineDatasourceType( Layer layer, double mapScale ) {
591    
592            AbstractDataSource[] ads = layer.getDataSource();
593            String type = null;
594    
595            boolean[] mixed = new boolean[] { false, false };
596            for ( int i = 0; i < ads.length; i++ ) {
597                ScaleHint scaleHint = ads[i].getScaleHint();
598                if ( LOG.getLevel() == ILogger.LOG_DEBUG ) {
599                    LOG.logDebug( "mapscale", mapScale );
600                    LOG.logDebug( "valid datasource min scale", scaleHint.getMin() );
601                    LOG.logDebug( "valid datasource max scale", scaleHint.getMax() );
602                }
603                if ( mapScale >= scaleHint.getMin() && mapScale < scaleHint.getMax() ) {
604                    if ( ( ads[i] instanceof RemoteWMSDataSource ) || ( ads[i] instanceof RemoteWCSDataSource )
605                         || ( ads[i] instanceof LocalWCSDataSource ) ) {
606                        type = "raster";
607                        mixed[0] = true;
608                    } else {
609                        type = "vector";
610                        mixed[1] = true;
611                    }
612                }
613            }
614            if ( mixed[0] && mixed[1] ) {
615                // Layers having a vector datasource as well as a raster datasource
616                // for the passed mapScale will be treated as raster layers
617                type = "raster";
618            }
619    
620            return type;
621        }
622    
623        /**
624         * Retrieve the legend images from the URL given in the configuration layers.
625         * 
626         * 
627         * @param layerDefs
628         * @param printMap
629         * @param mapParams
630         * @return BufferedImage
631         * @throws OGCWebServiceException
632         * @throws InconsistentRequestException
633         */
634        private BufferedImage performGetLegend( Map<String, Layer> layerDefs, PrintMap printMap, int[] mapParams ) {
635    
636            GetMap.Layer[] layers = printMap.getLayers();
637    
638            Map<String, Image> legendImg = new HashMap<String, Image>( layers.length );
639            int height = 0;
640            int maxWidth = 0;
641            for ( int i = 0; i < layers.length; i++ ) {
642                String name = layers[i].getName();
643                String styleName = layers[i].getStyleName();
644    
645                Layer configLayer = layerDefs.get( name );
646                Style style = null;
647                if ( "$DEFAULT".equals( styleName ) ) {
648                    style = configLayer.getStyleResource( "default:" + name );
649                } else {
650                    style = configLayer.getStyleResource( styleName );
651                }
652                LegendURL[] lu = style.getLegendURL();
653                if ( lu != null && lu.length > 0 ) {
654                    int k = 0;
655                    boolean drawn = false;
656                    while ( k < lu.length && !drawn ) {
657                        URL url = lu[k++].getOnlineResource();
658                        try {
659                            Image img = ImageUtils.loadImage( url );
660                            legendImg.put( name, img );
661                            drawn = true;
662                        } catch ( IOException e ) {
663                            // we do not throw the exception bacause there are maybe
664                            // further URLs we can try and even if not the user will
665                            // be informed by a special legend symbol that no correct
666                            // symbol can be accessed
667                            LOG.logError( "can not access LegendURL: " + url, e );
668                        } catch ( Exception e ) {
669                            LOG.logError( "can not read image from LegendURL: " + url, e );
670                        }
671                    }
672                    if ( !drawn ) {
673                        // if legend URL(s) are defined but none of them can
674                        // be accessed
675                        String s = StringTools.concat( 100, "no legend URL accessable for layer: ", name, "; style: ",
676                                                       styleName, " using dummy legend image" );
677                        LOG.logError( s );
678                        BufferedImage img = drawMissingLegendURLImage( s );
679                        legendImg.put( name, img );
680                    }
681                } else {
682                    // if no legend URL has been defined which probably is the case
683                    // for WMS no supporting GetLegendGraphic operation
684                    String s = StringTools.concat( 100, "no legend URL available for layer: ", name, "; style: ",
685                                                   styleName, " using dummy legend image" );
686                    LOG.logError( s );
687                    BufferedImage img = drawMissingLegendURLImage( s );
688                    legendImg.put( name, img );
689                }
690                // update all over legend height and width
691                BufferedImage img = (BufferedImage) legendImg.get( name );
692                if ( img.getWidth() > maxWidth ) {
693                    maxWidth = img.getWidth();
694                }
695                height += img.getHeight();
696            }
697    
698            // depending on the size of the legend all legend symbols must scaled by
699            // the same factor to fit the legend size defined in the current template
700            double dh = calcDeltaLegend( mapParams[2], mapParams[3], height, maxWidth );
701    
702            // create an empty basic image as target for painting all legend symbols
703            BufferedImage actualLegendImage = null;
704            if ( mapParams[2] > 0 && mapParams[3] > 0 ) {
705                actualLegendImage = new BufferedImage( mapParams[2], mapParams[3], BufferedImage.TYPE_INT_ARGB );
706    
707                Graphics2D g = (Graphics2D) actualLegendImage.getGraphics();
708    
709                int y = 0;
710                for ( int i = layers.length; i > 0; i-- ) {
711                    // draw all legend symbols in correct order
712                    String name = layers[i - 1].getName();
713                    BufferedImage img = scaleImage( (BufferedImage) legendImg.get( name ), dh );
714                    g.drawImage( img, 0, y, null );
715                    y += img.getHeight();
716                }
717    
718                g.dispose();
719            } else {
720                // create empty legend image if size is not valid
721                actualLegendImage = new BufferedImage( 10, 10, BufferedImage.TYPE_INT_ARGB );
722            }
723    
724            return actualLegendImage;
725        }
726    
727        private BufferedImage drawMissingLegendURLImage( String text ) {
728            BufferedImage img = new BufferedImage( 550, 50, BufferedImage.TYPE_INT_ARGB );
729            Graphics g = img.getGraphics();
730            g.setColor( Color.YELLOW );
731            g.fillRect( 0, 0, img.getWidth(), img.getHeight() );
732            g.setColor( Color.RED );
733            g.drawString( text, 10, 20 );
734            g.dispose();
735            return img;
736        }
737    
738        /**
739         * calculates factor for resizing legend images
740         * 
741         * @param legendWidth
742         *            The width of the legend area
743         * @param legendHeight
744         *            The height of the legend area
745         * @param height
746         *            The height of all legends put together
747         * @param maxWidth
748         *            The width of the wides legend
749         * @return Returns the factor for resizing legend images
750         */
751        private double calcDeltaLegend( int legendWidth, int legendHeight, int height, int maxWidth ) {
752            double dh = legendHeight / (double) height;
753            double dw = legendWidth / (double) maxWidth;
754            if ( dw < dh ) {
755                return dw;
756            }
757            return dh;
758        }
759    
760        /**
761         * Scale Image.
762         * 
763         * @param image
764         * @param ratio
765         * @return BufferedImage
766         */
767        private BufferedImage scaleImage( BufferedImage image, double ratio ) {
768    
769            AffineTransform tx = new AffineTransform();
770            tx.scale( ratio, ratio );
771            AffineTransformOp op = new AffineTransformOp( tx, AffineTransformOp.TYPE_BILINEAR );
772    
773            return op.filter( image, null );
774        }
775    
776        /**
777         * Save the GetMap image to the disk.
778         * 
779         * @param printMap
780         * @param image
781         * @param type
782         * @param bbox
783         * @throws IOException
784         */
785        private void saveImageToDisk( PrintMap printMap, BufferedImage image, String type, Envelope bbox )
786                                throws IOException {
787    
788            PrintMapParam printMapParam = this.configuration.getDeegreeParams().getPrintMapParam();
789            String fileName = null;
790            String templateName = printMap.getTemplate();
791            if ( type.equalsIgnoreCase( "MAP" ) ) {
792                fileName = StringTools.concat( 200, "Map_", templateName, '_', printMap.getId(), this.FORMAT );
793                WorldFile wf = new WorldFile( bbox.getWidth() / image.getWidth(), bbox.getHeight() / image.getHeight(), 0,
794                                              0, bbox );
795                String wffn = StringTools.concat( 200, "Map_", templateName, '_', printMap.getId(), ".wld" );
796                URL url = new URL( printMapParam.getPlotImageDir() + '/' + wffn );
797                OutputStream os = null;
798                try {
799                    os = new FileOutputStream( new File( url.toURI() ) );
800                } catch ( URISyntaxException e ) {
801                    // should never happen because each valid URL is a valid URI too
802                    LOG.logError( e.getMessage(), e );
803                }
804                WorldFile.writeWorldFile( os, wf );
805            } else if ( type.equalsIgnoreCase( "LEGEND" ) ) {
806                fileName = StringTools.concat( 200, "Legend_", templateName, '_', printMap.getId(), this.FORMAT );
807            }
808    
809            String path = printMapParam.getPlotImageDir() + '/' + fileName;
810            URL downloadDirectory = new URL( path );
811    
812            try {
813                ImageUtils.saveImage( image, new File( downloadDirectory.toURI() ), 1 );
814            } catch ( URISyntaxException e ) {
815                // should never happen because each valid URL is a valid URI too
816                LOG.logError( e.getMessage(), e );
817            }
818    
819        }
820    
821        /**
822         * Use JasperReports to create a pdf file. The Jasper Template will be loaded and a dynamic link will be created to
823         * the image on the disk.
824         * 
825         * @param printMap
826         * @param scaleDenominator
827         * @param synchronous
828         * @return byte[]
829         * @throws PrintMapServiceException
830         */
831        private byte[] exportOutput( PrintMap printMap, int scaleDenominator, boolean synchronous )
832                                throws PrintMapServiceException {
833    
834            // generate a file using JasperReports.
835            byte[] b = null;
836            try {
837                JasperPrint print = fillJasperTemplate( printMap, scaleDenominator );
838                b = doJasperPrintExport( printMap, print, synchronous );
839            } catch ( Exception e ) {
840                LOG.logError( e.getMessage(), e );
841                throw new PrintMapServiceException( Messages.getMessage( "WMPS_ERROR_SAVING_PDF" ) );
842            }
843    
844            return b;
845        }
846    
847        /**
848         * Open the JasperAPI to process the PrintMap request.
849         * 
850         * @param printMap
851         * @param scaleDenominator
852         * @return JasperPrint
853         * @throws InconsistentRequestException
854         * @throws JRException
855         * @throws MalformedURLException
856         */
857        private JasperPrint fillJasperTemplate( PrintMap printMap, int scaleDenominator )
858                                throws JRException, MalformedURLException {
859    
860            URL templatePath = null;
861            try {
862                String templateName = printMap.getTemplate();
863                templatePath = getTemplatePath( templateName, true );
864            } catch ( IOException e ) {
865                LOG.logError( e.getMessage(), e );
866                throw new MalformedURLException( Messages.getMessage( "WMPS_ERROR_CREATING_TEMPLATEPATH",
867                                                                      printMap.getTemplate() ) );
868            }
869    
870            Map<String, Object> parameters = new HashMap<String, Object>();
871            URL mapImagePath = getResultImagePath( printMap, "Map" );
872            parameters.put( "MAP", mapImagePath.getFile() );
873            if ( printMap.getLegend() ) {
874                URL legendImagePath = getResultImagePath( printMap, "Legend" );
875                parameters.put( "LEGEND", legendImagePath.getFile() );
876            }
877    
878            String scale = "1:" + scaleDenominator;
879    
880            if ( printMap.getScaleBar() == true ) {
881                parameters.put( "SCALE", scale );
882            }
883    
884            TextArea[] textAreas = printMap.getTextAreas();
885            if ( textAreas != null && textAreas.length > 0 ) {
886                for ( int i = 0; i < textAreas.length; i++ ) {
887                    TextArea textArea = textAreas[i];
888                    LOG.logDebug( "Names and text fields entered, extracted." );
889                    String name = textArea.getName();
890                    String text = textArea.getText();
891                    if ( name != null ) {
892                        LOG.logDebug( "If name is not null, allocate it to the hashmap 'parameters' in uppercase." );
893                        parameters.put( name.toUpperCase(), text );
894                    }
895                }
896            }
897            String title = printMap.getTitle();
898            if ( title != null ) {
899                parameters.put( "TITLE", title );
900            }
901            String copyright = printMap.getCopyright();
902            if ( copyright != null ) {
903                parameters.put( "COPYRIGHT", copyright );
904            }
905            String note = printMap.getNote();
906            if ( note != null ) {
907                parameters.put( "NOTE", note );
908            }
909            LOG.logDebug( "JASPER Parameter: ", parameters );
910            JasperPrint print = null;
911            try {
912                print = JasperFillManager.fillReport( templatePath.getFile(), parameters, new JREmptyDataSource() );
913            } catch ( JRException e ) {
914                LOG.logError( e.getMessage(), e );
915                throw new JRException( Messages.getMessage( "WMPS_ERROR_BUILDING_TEMPLATE", templatePath ) );
916            }
917    
918            return print;
919        }
920    
921        /**
922         * Retrieve the result map image file path for the current request.
923         * 
924         * @param printMap
925         * @param type
926         * @return URL
927         * @throws MalformedURLException
928         */
929        private URL getResultImagePath( PrintMap printMap, String type )
930                                throws MalformedURLException {
931    
932            String templateName = printMap.getTemplate();
933            String fileName = type + "_" + templateName + "_" + printMap.getId() + this.FORMAT;
934            PrintMapParam printMapParam = this.configuration.getDeegreeParams().getPrintMapParam();
935            String path = printMapParam.getPlotImageDir() + '/' + fileName;
936            URL imagePath = new URL( path );
937    
938            return imagePath;
939    
940        }
941    
942        /**
943         * Print the layer to a the specified format.
944         * 
945         * @param printMap
946         * @param print
947         * @param synchronous
948         * @return byte[]
949         * @throws JRException
950         * @throws IOException
951         */
952        private byte[] doJasperPrintExport( PrintMap printMap, JasperPrint print, boolean synchronous )
953                                throws JRException, IOException {
954    
955            String format = this.configuration.getDeegreeParams().getPrintMapParam().getFormat();
956            String templateName = printMap.getTemplate();
957            String filename = StringTools.concat( 200, format, '_', templateName, '_', printMap.getId(), '.', format );
958            String directory = this.configuration.getDeegreeParams().getPrintMapParam().getPlotDirectory();
959    
960            URL downloadFile = new URL( directory + '/' + filename );
961    
962            byte[] b = null;
963            try {
964                if ( synchronous ) {
965                    b = doSynchronousProcessing( print, format );
966                } else {
967                    doSaveResultDocument( print, format, downloadFile );
968                    createMailLink( filename );
969                }
970            } catch ( JRException e ) {
971                LOG.logError( e.getMessage(), e );
972                throw new JRException( Messages.getMessage( "WMPS_ERROR_PRINTING_REPORT", format, downloadFile.getFile() ) );
973            }
974    
975            return b;
976        }
977    
978        /**
979         * Create a mail link to be sent to the user email address. The mail link allows the user to open the pdf document
980         * for viewing and downloading purposes. Here 2 cases are taken into consideration
981         * <ul>
982         * <li>An authentification servlet link will be sent to the client.
983         * <li>A direct access to the clients data file.
984         * </ul>
985         * 
986         * @param printMap
987         * @param filename
988         */
989        private void createMailLink( String filename ) {
990            PrintMapParam pmp = this.configuration.getDeegreeParams().getPrintMapParam();
991            String onlineResource = pmp.getOnlineResource();
992            String template = pmp.getMailTextTemplate();
993            this.message = MessageFormat.format( template, new Object[] { onlineResource, filename.trim() } );
994        }
995    
996        /**
997         * Save the result document using the JasperExportManager to the file specified.
998         * 
999         * @param print
1000         * @param format
1001         * @param downloadDirectory
1002         * @throws JRException
1003         * @throws IOException
1004         */
1005        private void doSaveResultDocument( JasperPrint print, String format, URL downloadDirectory )
1006                                throws JRException, IOException {
1007            if ( format.equalsIgnoreCase( "pdf" ) ) {
1008                LOG.logDebug( "Exporting as pdf to file " + downloadDirectory.getFile() );
1009                JasperExportManager.exportReportToPdfFile( print, downloadDirectory.getFile() );
1010            } else if ( format.equalsIgnoreCase( "html" ) ) {
1011                LOG.logDebug( "Exporting as html to file " + downloadDirectory.getFile() );
1012                JasperExportManager.exportReportToHtmlFile( print, downloadDirectory.getFile() );
1013            } else if ( format.equalsIgnoreCase( "xml" ) ) {
1014                LOG.logDebug( "Exporting as xml to file " + downloadDirectory.getFile() );
1015                JasperExportManager.exportReportToXmlFile( print, downloadDirectory.getFile(), false );
1016            } else if ( format.equalsIgnoreCase( "png" ) ) {
1017                LOG.logDebug( "Exporting as xml to file " + downloadDirectory.getFile() );
1018                Image image = JasperPrintManager.printPageToImage( print, 0, 1 );
1019                ImageUtils.saveImage( (BufferedImage) image, new File( downloadDirectory.getFile() ), 1f );
1020            }
1021    
1022        }
1023    
1024        /**
1025         * Export the result document to a stream to be returend to the waiting client.
1026         * 
1027         * @param print
1028         * @param format
1029         * @return byte[]
1030         * @throws JRException
1031         */
1032        private byte[] doSynchronousProcessing( JasperPrint print, String format )
1033                                throws JRException {
1034    
1035            byte[] b;
1036            ByteArrayOutputStream bos = new ByteArrayOutputStream( 100000 );
1037            if ( format.equalsIgnoreCase( "pdf" ) ) {
1038                JasperExportManager.exportReportToPdfStream( print, bos );
1039            } else if ( format.equalsIgnoreCase( "xml" ) ) {
1040                JasperExportManager.exportReportToXmlStream( print, bos );
1041            }
1042            b = bos.toByteArray();
1043    
1044            return b;
1045        }
1046    
1047        /**
1048         * Retrieve PrintMap request layers from the WMPSConfiguration file. Counter check if the layer has been defined and
1049         * also get the type of datasource used by the layer.
1050         * 
1051         * @param printMap
1052         * @return Map key-> layer name, value -> configLayer
1053         * @throws PrintMapServiceException
1054         */
1055        private Map<String, Layer> retrieveLayersFromConfig( PrintMap printMap )
1056                                throws PrintMapServiceException {
1057    
1058            GetMap.Layer[] requestedLayers = printMap.getLayers();
1059            Map<String, Layer> layers = new HashMap<String, Layer>();
1060            for ( int i = 0; i < requestedLayers.length; i++ ) {
1061                GetMap.Layer layer = requestedLayers[i];
1062    
1063                Layer configLayer = this.configuration.getLayer( layer.getName() );
1064                if ( configLayer != null ) {
1065                    layers.put( layer.getName(), configLayer );
1066                } else {
1067                    throw new PrintMapServiceException( Messages.getMessage( "WMPS_UNKNOWN_LAYER", layer.getName() ) );
1068                }
1069            }
1070    
1071            return layers;
1072        }
1073    
1074        /**
1075         * Create a bounding box if no bounding box has been passed along with the PrintMap request.
1076         * 
1077         * @param center
1078         * @param scaleDenominator
1079         * @param mapWidth
1080         * @param mapHeight
1081         * @return Envelope
1082         */
1083        private Envelope createBBOX( Point center, int scaleDenominator, double mapWidth, double mapHeight ) {
1084            if ( LOG.getLevel() == ILogger.LOG_DEBUG ) {
1085                LOG.logDebug( "calculating BBOX using folloing parameter:" );
1086                LOG.logDebug( "map center: ", center );
1087                LOG.logDebug( "target scale: ", scaleDenominator );
1088                LOG.logDebug( "target resolution (dpi): ",
1089                              configuration.getDeegreeParams().getPrintMapParam().getTargetResolution() );
1090                LOG.logDebug( "map width: ", mapWidth );
1091                LOG.logDebug( "map height: ", mapHeight );
1092            }
1093            // screen -> world projection
1094            double pixelSize = WMPSConfiguration.INCH2M
1095                               / configuration.getDeegreeParams().getPrintMapParam().getTargetResolution();
1096            double w2 = ( scaleDenominator * pixelSize * mapWidth ) / 2d;
1097            double x1 = center.getX() - w2;
1098            double x2 = center.getX() + w2;
1099            w2 = ( scaleDenominator * pixelSize * mapHeight ) / 2d;
1100            double y1 = center.getY() - w2;
1101            double y2 = center.getY() + w2;
1102    
1103            Envelope bbox = GeometryFactory.createEnvelope( x1, y1, x2, y2, center.getCoordinateSystem() );
1104    
1105            LOG.logDebug( "calculated BBOX: ", bbox );
1106            return bbox;
1107    
1108        }
1109    
1110        /**
1111         * Returns the current configuration used to initialise the PrintMapHandler.
1112         * 
1113         * @return WMPSConfiguration
1114         */
1115        public WMPSConfiguration getConfiguration() {
1116            return this.configuration;
1117        }
1118    
1119        /**
1120         * Parse the Template and retrieve the page width, page height information.
1121         * 
1122         * @param templateName
1123         * @return int[]
1124         * @throws PrintMapServiceException
1125         * @throws IOException
1126         */
1127        private int[] getMapParamsFromTemplate( String templateName )
1128                                throws PrintMapServiceException, IOException {
1129    
1130            int[] mapParams = null;
1131            URL file = null;
1132            // try {
1133            boolean isCompiled = false;
1134            file = getTemplatePath( templateName, isCompiled );
1135            if ( file != null ) {
1136                Document dom = null;
1137                try {
1138                    InputStream is = file.openStream();
1139                    int c = 0;
1140                    StringBuffer sb = new StringBuffer( 10000 );
1141                    while ( ( c = is.read() ) > -1 ) {
1142                        sb.append( (char) c );
1143                    }
1144                    is.close();
1145                    // hack to ensure reporting engine is working even if the
1146                    // jasper-report server is not available
1147                    String s = StringTools.replace(
1148                                                    sb.toString(),
1149                                                    "<!DOCTYPE jasperReport PUBLIC \"//JasperReports//DTD Report Design//EN\" \"http://jasperreports.sourceforge.net/dtds/jasperreport.dtd\">",
1150                                                    "", false );
1151    
1152                    dom = XMLTools.parse( new StringReader( s ) );
1153                    // XMLFragment xml = new XMLFragment( file );
1154                    mapParams = parseImageNodes( dom.getDocumentElement() );
1155                } catch ( Exception e ) {
1156                    LOG.logError( e.getMessage(), e );
1157                    throw new PrintMapServiceException( Messages.getMessage( "WMPS_ERROR_PARSING_TEMPLATE", file ) );
1158                }
1159            }
1160    
1161            return mapParams;
1162        }
1163    
1164        /**
1165         * Return the url for the current (jasper reports) template.
1166         * 
1167         * @param templateName
1168         * @param isCompiled
1169         * @return URL
1170         * @throws PrintMapServiceException
1171         */
1172        private URL getTemplatePath( String templateName, boolean isCompiled )
1173                                throws IOException {
1174    
1175            if ( isCompiled ) {
1176                templateName = templateName + ".jasper";
1177            } else {
1178                templateName = templateName + ".jrxml";
1179            }
1180    
1181            PrintMapParam printMapParam = this.configuration.getDeegreeParams().getPrintMapParam();
1182            URL fileURL = new URL( printMapParam.getTemplateDirectory() + templateName );
1183    
1184            LOG.logDebug( "Retrieved the template file url. " + fileURL );
1185    
1186            return fileURL;
1187        }
1188    
1189        /**
1190         * Gets the Image node defined to hold the 'Map' and the node defined to hold the 'Legend'.
1191         * 
1192         * @param root
1193         * @return List
1194         * @throws PrintMapServiceException
1195         */
1196        private int[] parseImageNodes( Element root )
1197                                throws PrintMapServiceException {
1198    
1199            int[] mapParams = new int[4];
1200            int mapCount = 0;
1201            try {
1202                NamespaceContext nsc = CommonNamespaces.getNamespaceContext();
1203                nsc.addNamespace( "jasper", URI.create( "http://jasperreports.sourceforge.net/jasperreports" ) );
1204                List<Node> images = XMLTools.getNodes( root, "detail/band/image", null );
1205                if ( images == null || images.size() == 0 ) {
1206                    images = XMLTools.getNodes( root, "jasper:detail/jasper:band/jasper:image", nsc );
1207                }
1208                for ( int i = 0; i < images.size(); i++ ) {
1209                    Node image = images.get( i );
1210                    // e.g. $P{MAP}
1211                    String value = XMLTools.getNodeAsString( image, "imageExpression", null, null );
1212                    if ( value == null ) {
1213                        value = XMLTools.getRequiredNodeAsString( image, "jasper:imageExpression", nsc );
1214                    }
1215                    int idx = value.indexOf( "{" );
1216                    if ( idx != -1 ) {
1217    
1218                        String tmp = value.substring( idx + 1, value.length() - 1 );
1219                        Element reportElement = (Element) XMLTools.getNode( image, "reportElement", null );
1220                        if ( reportElement == null ) {
1221                            reportElement = (Element) XMLTools.getRequiredNode( image, "jasper:reportElement", nsc );
1222                        }
1223                        String width = reportElement.getAttribute( "width" );
1224                        String height = reportElement.getAttribute( "height" );
1225    
1226                        double res = configuration.getDeegreeParams().getPrintMapParam().getTargetResolution();
1227                        // Templates created by iReport assumes a resolution of 72 dpi
1228                        if ( tmp.startsWith( "MAP" ) ) {
1229                            mapParams[0] = (int) ( Integer.parseInt( width ) / 72d * res );
1230                            mapParams[1] = (int) ( Integer.parseInt( height ) / 72d * res );
1231                            mapCount = mapCount + 1;
1232                        } else if ( tmp.startsWith( "LEGEND" ) ) {
1233                            mapParams[2] = (int) ( Integer.parseInt( width ) / 72d * res );
1234                            mapParams[3] = (int) ( Integer.parseInt( height ) / 72d * res );
1235                        }
1236                    }
1237                }
1238                if ( ( mapCount == 0 ) || ( mapCount > 1 ) ) {
1239                    throw new PrintMapServiceException( Messages.getMessage( "WMPS_TOO_MANY_MAPAREAS", mapCount ) );
1240                }
1241            } catch ( XMLParsingException e ) {
1242                LOG.logError( e.getMessage(), e );
1243                throw new PrintMapServiceException( Messages.getMessage( "WMPS_INVALID_JASPER_TEMPLATE" ) );
1244            }
1245    
1246            return mapParams;
1247        }
1248    
1249    }