/*---------------- FILE HEADER ------------------------------------------ This file is part of deegree. Copyright (C) 2001-2006 by: EXSE, Department of Geography, University of Bonn http://www.giub.uni-bonn.de/deegree/ lat/lon GmbH http://www.lat-lon.de This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Contact: Andreas Poth lat/lon GmbH Aennchenstr. 19 53177 Bonn Germany E-Mail: poth@lat-lon.de Prof. Dr. Klaus Greve Department of Geography University of Bonn Meckenheimer Allee 166 53115 Bonn Germany E-Mail: greve@giub.uni-bonn.de ---------------------------------------------------------------------------*/ package org.deegree.portal.standard.wms.control; import java.awt.Color; import java.awt.Font; import java.awt.Graphics; import java.awt.Rectangle; import java.awt.geom.Rectangle2D; import java.awt.image.BufferedImage; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.UnsupportedEncodingException; import java.net.MalformedURLException; import java.net.URL; import java.net.URLDecoder; import java.util.ArrayList; import java.util.HashMap; import java.util.StringTokenizer; import javax.imageio.ImageIO; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpSession; import org.deegree.enterprise.control.FormEvent; import org.deegree.enterprise.control.RPCMember; import org.deegree.enterprise.control.RPCMethodCall; import org.deegree.enterprise.control.RPCParameter; import org.deegree.enterprise.control.RPCStruct; import org.deegree.enterprise.control.RPCWebEvent; import org.deegree.framework.log.ILogger; import org.deegree.framework.log.LoggerFactory; import org.deegree.framework.util.CharsetUtils; import org.deegree.framework.util.Debug; import org.deegree.framework.util.IDGenerator; import org.deegree.framework.util.ImageUtils; import org.deegree.framework.util.StringTools; import org.deegree.framework.xml.XMLParsingException; import org.deegree.model.crs.UnknownCRSException; import org.deegree.ogcwebservices.InconsistentRequestException; import org.deegree.ogcwebservices.OGCWebServiceException; import org.deegree.ogcwebservices.OWSUtils; import org.deegree.ogcwebservices.wms.operation.GetLegendGraphic; import org.deegree.ogcwebservices.wms.operation.GetMap; import org.deegree.portal.Constants; import org.deegree.portal.context.GeneralExtension; import org.deegree.portal.context.IOSettings; import org.deegree.portal.context.ViewContext; /** * will be called if the client forces a print action. * * @deprecated use @see org.deegree.portal.common.control.AbstractSimplePrintListener * * @author <a href="mailto:lupp@lat-lon.de">Katharina Lupp</a> * @author <a href="mailto:poth@lat-lon.de">Andreas Poth</a> * @author last edited by: $Author: mays $ * * @version $Revision: 1.18 $ $Date: 2006/12/04 10:02:46 $ */ public class PrintListener extends AbstractMapListener { private static final ILogger LOG = LoggerFactory.getLogger( PrintListener.class ); /* * the method will be called if a print action/event occurs. * * (non-Javadoc) * @see org.deegree.enterprise.control.WebListener#actionPerformed(org.deegree.enterprise.control.FormEvent) */ @Override public void actionPerformed( FormEvent event ) { Debug.debugMethodBegin(); super.actionPerformed( event ); RPCWebEvent rpc = (RPCWebEvent) event; RPCMethodCall mc = rpc.getRPCMethodCall(); RPCParameter[] para = mc.getParameters(); RPCStruct struct = (RPCStruct) para[0].getValue(); // paperFormat, resolution, orientation, format RPCStruct struct2 = (RPCStruct) para[1].getValue(); // wmsRequest_0 .. wmsRequest_i HttpSession session = ( (HttpServletRequest) this.getRequest() ).getSession( true ); ViewContext vc = null; if ( session.getAttribute( Constants.CURRENTMAPCONTEXT ) != null ) { vc = (ViewContext) session.getAttribute( Constants.CURRENTMAPCONTEXT ); } else { vc = (ViewContext) session.getAttribute( "DefaultMapContext" ); } HashMap[] model = createWMSRequestModel( struct2 ); int[] imageSize = calcImageSize( struct, model[0] ); int width = imageSize[0]; //int height = imageSize[1]; int xmap = imageSize[2]; int ymap = imageSize[3]; int mapStart = imageSize[4]; try { GetLegendGraphic legendParam = getLegendRequestParameter(); //HashMap with legend HashMap symbols = getLegend( struct, legendParam, model ); Rectangle rectLegend = calcLegendSize( struct, symbols ); Rectangle rectMap = calcMapSize( model, rectLegend, width ); //sets new size of map und creates the adequate BufferedImage model = modifyModelSize( rectMap, model, xmap, ymap ); HashMap[] copy = createCopy( model ); BufferedImage biReq = getMap( copy ); //creates BufferedImage with required size and creates graphic BufferedImage bi = createBackgroundImage( rectMap, rectLegend ); bi = drawMapToBI( biReq, bi, mapStart ); bi = drawLegendToBI( symbols, bi, ( mapStart + rectMap.width ) ); saveImage( vc, struct, bi ); } catch ( Exception e ) { e.printStackTrace(); } Debug.debugMethodEnd(); } /** * creates a background BufferedImage for the attributes (map, legend, scalebar) * * @param rectMap * @param rectLegend * @return */ private BufferedImage createBackgroundImage( Rectangle rectMap, Rectangle rectLegend ) { BufferedImage bi = null; if ( rectMap.height > rectLegend.height ) { bi = new BufferedImage( rectMap.width + rectLegend.width + 40, rectMap.height + 100, BufferedImage.TYPE_INT_RGB ); } else { bi = new BufferedImage( rectMap.width + rectLegend.width + 40, rectLegend.height + 100, BufferedImage.TYPE_INT_RGB ); } Graphics g = bi.getGraphics(); g.setColor( Color.WHITE ); g.fillRect( 1, 1, bi.getWidth() - 2, bi.getHeight() - 2 ); g.dispose(); return bi; } /** * creates copy of the model * * @param model * @return */ private HashMap[] createCopy( HashMap[] model ) { HashMap[] copy = new HashMap[model.length]; synchronized ( this ) { for ( int i = 0; i < model.length; i++ ) { copy[i] = (HashMap) model[i].clone(); } } return copy; } /** * creates model with WMS request from the RPCStruct request parameter. * * @param struc * @return */ private HashMap[] createWMSRequestModel( RPCStruct struc ) { RPCMember[] member = struc.getMembers(); HashMap[] getMR = new HashMap[member.length]; String request = ""; //for ( int i = 0; i < member.length; i++ ) { for ( int i = member.length - 1; i >= 0; i-- ) { request = (String) member[i].getValue(); getMR[i] = toMap( request ); StringTokenizer st = new StringTokenizer( request, "?" ); getMR[i].put( "URL", st.nextToken() ); } return getMR; } /** * calculates images size in dependency on chosen paperFormat, resolution * and orientation. * * @param struct * @param model * @return */ private int[] calcImageSize( RPCStruct struct, HashMap model ) { double width = 0; double height = 0; double mapSize = 0; double mapWI = Double.parseDouble( (String) model.get( "WIDTH" ) ); double mapHE = Double.parseDouble( (String) model.get( "HEIGHT" ) ); String paperFormat = (String) struct.getMember( "paperFormat" ).getValue(); String resolution = (String) struct.getMember( "resolution" ).getValue(); String orientation = (String) struct.getMember( "orientation" ).getValue(); if ( paperFormat.equals( "A4" ) ) { if ( orientation.equals( "hoch" ) ) { width = 8.2; height = 11.6; mapSize = 6.5; //legend beside } else { width = 11.6; height = 8.2; mapSize = 9.5; } } else if ( paperFormat.equals( "A3" ) ) { if ( orientation.equals( "hoch" ) ) { width = 11.6; height = 16.5; mapSize = 9.8; //legend beside } else { width = 16.5; height = 11.6; mapSize = 14; } } else if ( paperFormat.equals( "A5" ) ) { if ( orientation.equals( "hoch" ) ) { width = 5.8; height = 8.2; mapSize = 4.1; //legend beside } else { width = 8.2; height = 5.8; mapSize = 6.5; } } width = width * Double.parseDouble( resolution ); height = height * Double.parseDouble( resolution ); mapSize = mapSize * Double.parseDouble( resolution ); int xmap = (int) Math.round( mapSize ); double fac = mapWI / mapHE; int ymap = (int) Math.round( mapSize / fac ); int mapStart = 45; int[] imagesize = new int[] { (int) Math.round( width ), (int) Math.round( height ), xmap, ymap, mapStart }; return imagesize; } /** * calculates size (width and height) of legend depending on * chosen paperFormat and resolution. * * @param struct * @param map * @return */ private Rectangle calcLegendSize( RPCStruct struct, HashMap map ) { String paperFormat = (String) struct.getMember( "paperFormat" ).getValue(); String resolution = (String) struct.getMember( "resolution" ).getValue(); String[] layers = (String[]) map.get( "NAMES" ); BufferedImage[] legs = (BufferedImage[]) map.get( "IMAGES" ); int w = 0; int h = 0; int size = 12; double tmph1 = 0; double tmph2 = 0; if ( paperFormat.equals( "A4" ) ) { tmph1 = size / 3d; } else if ( paperFormat.equals( "A3" ) ) { tmph1 += 2 * size / 3; } double res = Double.parseDouble( resolution ); tmph2 = ( ( res - 150 ) / 150d ) * size; size += (int) Math.round( tmph2 + tmph1 ); for ( int i = 0; i < layers.length; i++ ) { h += ( legs[i].getHeight() + 6 ); Graphics g = legs[i].getGraphics(); Font f = new Font( g.getFont().getFontName(), g.getFont().getStyle(), size ); g.setFont( f ); Rectangle2D rect = g.getFontMetrics().getStringBounds( layers[i], g ); g.dispose(); if ( rect.getWidth() > w ) { w = (int) rect.getWidth(); } } w += 150; return new Rectangle( w, h ); } /** * calculates map size in dependency of the chosen paperFormat and resolution. * * @param model * @param rectLegend * @param width * @return */ private Rectangle calcMapSize( HashMap[] model, Rectangle rectLegend, int width ) { int w = width - rectLegend.width - 20; double wi = Double.parseDouble( (String) model[0].get( "WIDTH" ) ); double he = Double.parseDouble( (String) model[0].get( "HEIGHT" ) ); double fac = he / wi; int h = (int) Math.round( fac * w ); return new Rectangle( w, h ); } /** * draws legend with symbols and name of the layers to the background BufferedImage. * * @param map * @param bi * @param start * @return */ private BufferedImage drawLegendToBI( HashMap map, BufferedImage bi, int start ) { int h = 5; Graphics g = bi.getGraphics(); g.setColor( Color.WHITE ); String[] layers = (String[]) map.get( "NAMES" ); BufferedImage[] legs = (BufferedImage[]) map.get( "IMAGES" ); for ( int i = layers.length - 1; i >= 0; i-- ) { g.drawImage( legs[i], start + 10, h, null ); g.setColor( Color.BLACK ); if ( legs[i].getHeight() < 50 ) { g.drawString( layers[i], start + 25 + legs[i].getWidth(), h + (int) ( legs[i].getHeight() / 1.2 ) ); } h += ( legs[i].getHeight() + 5 ); } g.dispose(); return bi; } /** * modifies width and height in the WMSGetMapRequest * in dependency of the chosen paperFormat and resolution. * New width and height are calculated in the method "calcMapSize". * * @param rectMap * @param model * @param width * @param height * @return */ private HashMap[] modifyModelSize( Rectangle rectMap, HashMap[] model, int width, int height ) { int w = rectMap.width; double fac = (double) height / (double) width; double h = fac * w; for ( int i = 0; i < model.length; i++ ) { model[i].put( "HEIGHT", "" + (int) Math.round( h ) ); model[i].put( "WIDTH", "" + w ); } return model; } /** * gets the map with corresponding mapsizes built-in the urls. * @throws UnknownCRSException * * @param model * @return * @throws MalformedURLException * @throws IOException * @throws XMLParsingException * @throws InconsistentRequestException * @throws OGCWebServiceException * @throws UnknownCRSException */ private BufferedImage getMap( HashMap[] model ) throws MalformedURLException, IOException, XMLParsingException, InconsistentRequestException, OGCWebServiceException, UnknownCRSException { int w = Integer.parseInt( (String) model[0].get( "WIDTH" ) ); int h = Integer.parseInt( (String) model[0].get( "HEIGHT" ) ); BufferedImage bJ = new BufferedImage( w, h, BufferedImage.TYPE_INT_RGB ); Graphics g = bJ.getGraphics(); g.setColor( Color.WHITE ); g.fillRect( 1, 1, bJ.getWidth() - 2, bJ.getHeight() - 2 ); g.dispose(); GetMap gmr = null; for ( int i = model.length - 1; i >= 0; i-- ) { String urls = OWSUtils.validateHTTPGetBaseURL( (String) model[i].remove( "URL" ) ); String s = URLDecoder.decode( (String) model[i].get( "FORMAT" ), CharsetUtils.getSystemCharset() ); model[i].put( "FORMAT", s ); model[i].put( "ID", "1.1.1" ); gmr = GetMap.create( model[i] ); /* if ( sessionid != null ) { urls = StringTools.concat( 1000, urls, "&sessionID=", sessionid.getValue(), '&' ); } */ URL url = new URL( urls + gmr.getRequestParameter() ); InputStream is = url.openStream(); BufferedImage tmp = ImageIO.read( is ); is.close(); g = bJ.getGraphics(); g.drawImage( tmp, 0, 0, null ); g.dispose(); } return bJ; } /** * draws the map to the background BufferedImage * * @param reqIm * @param bi * @param mapStart * @return */ private BufferedImage drawMapToBI( BufferedImage reqIm, BufferedImage bi, int mapStart ) { Graphics g = bi.getGraphics(); g.drawImage( reqIm, mapStart, mapStart, null ); g.dispose(); return bi; } /** * saves image with BufferedImage including map, legend and scalebar in * chosen image format and directory set in mapcontext.xml file. * Via the attribute "ImageSource" the image can be called for. * * @param vc * @param struct * @param bg */ private void saveImage( ViewContext vc, RPCStruct struct, BufferedImage bg ) { String format = (String) struct.getMember( "format" ).getValue(); format = format.substring( format.indexOf( '/' ) + 1, format.length() ); GeneralExtension ge = vc.getGeneral().getExtension(); IOSettings ios = ge.getIOSettings(); String dir = ios.getPrintDirectory().getDirectoryName(); long l = IDGenerator.getInstance().generateUniqueID() % 100; String file = "Map" + l + '.' + format; try { String outPut = new URL( dir ).getFile() + '/' + file; File f = new File( outPut ); f.deleteOnExit(); FileOutputStream fos = new FileOutputStream( f ); ImageUtils.saveImage( bg, fos, format, 1 ); } catch ( Exception e ) { LOG.logError( "Error occurred in saving image_: ", e ); } String access = ios.getPrintDirectory().getOnlineResource().toExternalForm() + '/' + file; this.getRequest().setAttribute( "ImageSource", access ); this.getRequest().setAttribute( "ImageWidth", new Integer( bg.getWidth() ) ); this.getRequest().setAttribute( "ImageHeight", new Integer( bg.getHeight() ) ); } /** * gets parameters of the LegendRequest * * @return * @throws InconsistentRequestException */ private GetLegendGraphic getLegendRequestParameter() throws InconsistentRequestException { HashMap legend = toMap( "VERSION=1.1.1&REQUEST=GetLegendGraphic&FORMAT=image/png&WIDTH=50&HEIGHT=50&" + "EXCEPTIONS=application/vnd.ogc.se_inimage&LAYER=europe:major_rivers&STYLE=default&" + "SLD=file:///C:/Projekte/UmweltInfo/deegreewms/WEB-INF/xml/styles.xml" ); GetLegendGraphic legendReq = GetLegendGraphic.create( legend ); return legendReq; } /** * gets legend with layers and styles. The size of the symbols is adjusted to the * chosen paperFomrat and resolution. * * @param struct * @param glr * @param model * @return */ private HashMap getLegend( RPCStruct struct, GetLegendGraphic glr, HashMap[] model ) { Debug.debugMethodBegin( this, "setScaleBarURL" ); ArrayList list1 = new ArrayList(); ArrayList list2 = new ArrayList(); String format = glr.getFormat(); if ( format.equals( "image/jpg" ) ) format = "image/jpeg"; String legendURL = ""; String paperFormat = (String) struct.getMember( "paperFormat" ).getValue(); String resolution = (String) struct.getMember( "resolution" ).getValue(); int h = 15; int w = 15; double tmph1 = 0; double tmpw1 = 0; double tmph2 = 0; double tmpw2 = 0; if ( paperFormat.equals( "A4" ) ) { tmph1 = h / 3d; tmpw1 = w / 3d; } else if ( paperFormat.equals( "A3" ) ) { tmph1 = 2 * h / 3d; tmpw1 = 2 * w / 3d; } double res = Double.parseDouble( resolution ); tmph2 = ( ( res - 150 ) / 150d ) * h; tmpw2 = ( ( res - 150 ) / 150d ) * w; w += (int) Math.round( tmpw1 + tmpw2 ); h += (int) Math.round( tmph1 + tmph2 ); for ( int i = 0; i < model.length; i++ ) { if ( model[i].get( "SLD" ) != null ) { continue; } String[] lays = StringTools.toArray( (String) model[i].get( "LAYERS" ), ",", false ); String style = (String) model[i].get( "STYLES" ); String[] styles = new String[20]; for ( int j = 0; j < styles.length; j++ ) { styles[j] = "default"; } if ( style != null && !style.trim().equals( "" ) ) { //styles = StringTools.toArray(style,",",false); if ( ( StringTools.toArray( style, ",", false ) ).length != 0 ) { styles = StringTools.toArray( style, ",", false ); } } else { styles = new String[lays.length]; for ( int j = 0; j < styles.length; j++ ) { styles[j] = "default"; } } if ( styles == null ) { styles = new String[20]; for ( int j = 0; j < styles.length; j++ ) { styles[j] = "default"; } } for ( int j = 0; j < lays.length; j++ ) { String layer = lays[j]; legendURL = createLegendURL( w, h, layer, styles[j], format, glr, model[i] ); BufferedImage legendGraphic = null; try { InputStream is = new URL( legendURL ).openStream(); legendGraphic = ImageIO.read( is ); is.close(); } catch ( Exception e ) { System.out.println( "\n\nLegend graphic for layer '" + layer + "' is not available. Skipping.\n\n" ); legendGraphic = null; e.printStackTrace(); } if ( legendGraphic != null ) { list1.add( layer ); list2.add( legendGraphic ); } } } Debug.debugMethodEnd(); String[] layers = (String[]) list1.toArray( new String[list1.size()] ); BufferedImage[] legs = (BufferedImage[]) list2.toArray( new BufferedImage[list2.size()] ); HashMap map = new HashMap(); map.put( "NAMES", layers ); map.put( "IMAGES", legs ); return map; } /** * creates legend URL with corresponding key values. * * @param w * @param h * @param layer * @param style * @param format * @param glr * @param model * @return */ private String createLegendURL( int w, int h, String layer, String style, String format, GetLegendGraphic glr, HashMap model ) { String s = null; try { if ( "default".equals( style ) ) { style = ""; } String url = OWSUtils.validateHTTPGetBaseURL( (String) model.get( "URL" ) ); s = StringTools.concat( 500, url, "service=WMS", "&VERSION=", glr.getVersion(), "&REQUEST=GetLegendGraphic", "&FORMAT=", format, "&WIDTH=", w, "&HEIGHT=", h, "&EXCEPTIONS=application/vnd.ogc.se_inimage&LAYER=", URLDecoder.decode( layer, CharsetUtils.getSystemCharset() ), "&STYLE=", style ); // insufficient information available for user restrictions // TODO // HttpServletRequest req = (HttpServletRequest) getRequest(); // if ( req.getUserPrincipal() != null ) { // String user = req.getUserPrincipal().getName(); // if ( user.indexOf( "\\" ) > 1 ) { // String[] us = StringTools.toArray( user, "\\", false ); // user = us[us.length - 1]; // } // s = StringTools.concat( 500, s, "&user=", user ); // } } catch ( UnsupportedEncodingException e ) { // should never happen e.printStackTrace(); } return s; } } /* ******************************************************************** Changes to this class. What the people have been up to: $Log: PrintListener.java,v $ Revision 1.18 2006/12/04 10:02:46 mays remove user info from GetLegendGraphic request Revision 1.17 2006/12/01 14:31:36 mays clean up file header Revision 1.16 2006/12/01 14:39:08 mays try to use current map context. only if this is not available, use default map context; clean up javadoc Revision 1.10 2006/08/07 14:39:08 poth never used methods removed / class marked as deprecated Revision 1.9 2006/06/29 10:29:37 poth *** empty log message *** Revision 1.8 2006/06/25 20:35:42 poth support for forwarding UserPrincipal as part of GetCapabilitities, GetMap and GetLegendGraphicRequests Revision 1.7 2006/04/20 12:15:12 ncho SN: added line to avoid ArrayIndexOutOfBoundsException Revision 1.6 2006/04/19 09:06:04 ncho SN: fixed URL access to print Revision 1.5 2006/04/07 09:30:00 taddei sn fix for release 2006-04 Revision 1.1 2006/02/05 09:30:12 poth *** empty log message *** Revision 1.16 2005/12/06 13:45:20 poth System.out.println substituted by logging api Revision 1.15 2005/09/27 19:53:19 poth no message Revision 1.14 2005/04/06 13:44:07 poth no message Revision 1.13 2005/03/18 16:31:32 poth no message Revision 1.12 2005/03/12 10:45:03 poth no message Revision 1.11 2005/03/09 11:55:46 mschneider *** empty log message *** Revision 1.10 2005/02/25 19:03:02 poth no message Revision 1.9 2005/02/25 11:19:16 poth no message Revision 1.8 2005/02/24 20:04:04 poth no message Revision 1.7 2005/02/21 11:24:33 poth no message Revision 1.6 2005/02/18 20:54:18 poth no message Revision 1.5 2005/02/17 16:54:51 poth no message Revision 1.4 2005/02/17 11:52:11 poth no message Revision 1.3 2005/02/16 13:12:18 poth no message Revision 1.2 2005/02/16 12:39:32 poth no message Revision 1.1.1.1 2005/01/05 10:30:00 poth no message Revision 1.1 2004/08/18 06:37:01 taddei Listener with minor bug fixes: catching exceptions etc... ********************************************************************** */