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