/*---------------- 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 53115 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.graphics.sld; import java.awt.Graphics2D; import java.awt.image.BufferedImage; import java.util.ArrayList; import org.deegree.framework.xml.Marshallable; import org.deegree.model.feature.Feature; import org.deegree.model.filterencoding.FilterEvaluationException; /** * A Graphic is a "graphic symbol" with an inherent shape, color, and size. * Graphics can either be referenced from an external URL in a common format * (such as GIF or SVG) or may be derived from a Mark. Multiple external URLs * may be referenced with the semantic that they all provide the same graphic in * different formats. The "hot spot" to use for rendering at a point or the * start and finish handle points to use for rendering a graphic along a line * must either be inherent in the external format or are system- dependent. The * default size of an image format (such as GIF) is the inherent size of the * image. The default size of a format without an inherent size is 16 pixels in * height and the corresponding aspect in width. If a size is specified, the * height of the graphic will be scaled to that size and the corresponding * aspect will be used for the width. The default if neither an ExternalURL nor * a Mark is specified is to use the default Mark with a size of 6 pixels. The * size is in pixels and the rotation is in degrees clockwise, with 0 (default) * meaning no rotation. In the case that a Graphic is derived from a font-glyph * Mark, the Size specified here will be used for the final rendering. Allowed * CssParameters are "opacity", "size", and "rotation". * * @author <a href="mailto:k.lupp@web.de">Katharina Lupp </a> * @author <a href="mailto:mschneider@lat-lon.de">Markus Schneider </a> * @version $Revision: 1.11 $ $Date: 2006/10/11 07:58:41 $ */ public class Graphic implements Marshallable { // default values public static final double OPACITY_DEFAULT = 1.0; public static final double SIZE_DEFAULT = -1; public static final double ROTATION_DEFAULT = 0.0; private ArrayList marksAndExtGraphics = new ArrayList(); private BufferedImage image = null; private ParameterValueType opacity = null; private ParameterValueType rotation = null; private ParameterValueType size = null; /** * Creates a new <tt>Graphic</tt> instance. * <p> * * @param marksAndExtGraphics * the image will be based upon these * @param opacity * opacity that the resulting image will have * @param size * image height will be scaled to this value, respecting the * proportions * @param rotation * image will be rotated clockwise for positive values, negative * values result in anti-clockwise rotation */ protected Graphic( Object[] marksAndExtGraphics, ParameterValueType opacity, ParameterValueType size, ParameterValueType rotation ) { setMarksAndExtGraphics( marksAndExtGraphics ); this.opacity = opacity; this.size = size; this.rotation = rotation; } /** * Creates a new <tt>Graphic</tt> instance based on the default * <tt>Mark</tt>: a square. * <p> * * @param opacity * opacity that the resulting image will have * @param size * image height will be scaled to this value, respecting the * proportions * @param rotation * image will be rotated clockwise for positive values, negative * values result in anti-clockwise rotation */ protected Graphic( ParameterValueType opacity, ParameterValueType size, ParameterValueType rotation ) { Mark[] marks = new Mark[1]; marks[0] = new Mark( "square", null, null ); setMarksAndExtGraphics( marks ); this.opacity = opacity; this.size = size; this.rotation = rotation; } /** * returns the ParameterValueType representation of opacity * @return */ public ParameterValueType getOpacity() { return opacity; } /** * returns the ParameterValueType representation of rotation * @return */ public ParameterValueType getRotation() { return rotation; } /** * returns the ParameterValueType representation of size * @return */ public ParameterValueType getSize() { return size; } /** * Creates a new <tt>Graphic</tt> instance based on the default * <tt>Mark</tt>: a square. */ protected Graphic() { this( null, null, null ); } /** * Returns an object-array that enables the access to the stored * <tt>ExternalGraphic</tt> and <tt>Mark</tt> -instances. * <p> * * @return contains <tt>ExternalGraphic</tt> and <tt>Mark</tt> -objects * * @uml.property name="marksAndExtGraphics" */ public Object[] getMarksAndExtGraphics() { Object[] objects = new Object[marksAndExtGraphics.size()]; return marksAndExtGraphics.toArray( objects ); } /** * Sets the <tt>ExternalGraphic</tt>/ * <tt>Mark<tt>-instances that the image * will be based on. * <p> * @param object to be used as basis for the resulting image */ public void setMarksAndExtGraphics( Object[] object ) { image = null; this.marksAndExtGraphics.clear(); if ( object != null ) { for ( int i = 0; i < object.length; i++ ) { marksAndExtGraphics.add( object[i] ); } } } /** * Adds an Object to an object-array that enables the access to the stored * <tt>ExternalGraphic</tt> and <tt>Mark</tt> -instances. * <p> * * @param object * to be used as basis for the resulting image */ public void addMarksAndExtGraphic( Object object ) { marksAndExtGraphics.add( object ); } /** * Removes an Object from an object-array that enables the access to the * stored <tt>ExternalGraphic</tt> and <tt>Mark</tt> -instances. * <p> * * @param object * to be used as basis for the resulting image */ public void removeMarksAndExtGraphic( Object object ) { marksAndExtGraphics.remove( marksAndExtGraphics.indexOf( object ) ); } /** * The Opacity element gives the opacity to use for rendering the graphic. * <p> * * @param feature * specifies the <tt>Feature</tt> to be used for evaluation of * the underlying 'sld:ParameterValueType' * @return the (evaluated) value of the parameter * @throws FilterEvaluationException * if the evaluation fails or the value is invalid */ public double getOpacity( Feature feature ) throws FilterEvaluationException { double opacityVal = OPACITY_DEFAULT; if ( opacity != null ) { String value = opacity.evaluate( feature ); try { opacityVal = Double.parseDouble( value ); } catch ( NumberFormatException e ) { throw new FilterEvaluationException( "Given value for parameter 'opacity' ('" + value + "') has invalid format!" ); } if ( ( opacityVal < 0.0 ) || ( opacityVal > 1.0 ) ) { throw new FilterEvaluationException( "Value for parameter 'opacity' (given: '" + value + "') must be between 0.0 and 1.0!" ); } } return opacityVal; } /** * The Opacity element gives the opacity of to use for rendering the * graphic. * <p> * * @param opacity * Opacity to be set for the graphic */ public void setOpacity( double opacity ) { ParameterValueType pvt = null; pvt = StyleFactory.createParameterValueType( "" + opacity ); this.opacity = pvt; } /** * The Size element gives the absolute size of the graphic in pixels encoded * as a floating-point number. This element is also used in other contexts * than graphic size and pixel units are still used even for font size. The * default size for an object is context-dependent. Negative values are not * allowed. * <p> * * @param feature * specifies the <tt>Feature</tt> to be used for evaluation of * the underlying 'sld:ParameterValueType' * @return the (evaluated) value of the parameter * @throws FilterEvaluationException * if the evaluation fails or the value is invalid */ public double getSize( Feature feature ) throws FilterEvaluationException { double sizeVal = SIZE_DEFAULT; if ( size != null ) { String value = size.evaluate( feature ); try { sizeVal = Double.parseDouble( value ); } catch ( NumberFormatException e ) { throw new FilterEvaluationException( "Given value for parameter 'size' ('" + value + "') has invalid format!" ); } if ( sizeVal <= 0.0 ) { throw new FilterEvaluationException( "Value for parameter 'size' (given: '" + value + "') must be greater than 0!" ); } } return sizeVal; } /** * @see org.deegree.graphics.sld.Graphic#getSize(Feature) * <p> * @param size * size to be set for the graphic */ public void setSize( double size ) { ParameterValueType pvt = null; pvt = StyleFactory.createParameterValueType( "" + size ); this.size = pvt; } /** * The Rotation element gives the rotation of a graphic in the clockwise * direction about its center point in radian, encoded as a floating- point * number. Negative values mean counter-clockwise rotation. The default * value is 0.0 (no rotation). * <p> * * @param feature * specifies the <tt>Feature</tt> to be used for evaluation of * the underlying 'sld:ParameterValueType' * @return the (evaluated) value of the parameter * @throws FilterEvaluationException * if the evaluation fails or the value is invalid */ public double getRotation( Feature feature ) throws FilterEvaluationException { double rotVal = ROTATION_DEFAULT; if ( rotation != null ) { String value = rotation.evaluate( feature ); try { rotVal = Double.parseDouble( value ); } catch ( NumberFormatException e ) { throw new FilterEvaluationException( "Given value for parameter 'rotation' ('" + value + "') has invalid format!" ); } } return rotVal; } /** * @see org.deegree.graphics.sld.Graphic#getRotation(Feature) * <p> * @param rotation * rotation to be set for the graphic */ public void setRotation( double rotation ) { ParameterValueType pvt = null; pvt = StyleFactory.createParameterValueType( "" + rotation ); this.rotation = pvt; } /** * Returns a <tt>BufferedImage</tt> representing this object. The image * respects the 'Opacity', 'Size' and 'Rotation' parameters. If the * 'Size'-parameter is omitted, the height of the first * <tt>ExternalGraphic</tt> is used. If there is none, the default value * of 6 pixels is used. * <p> * * @return the <tt>BufferedImage</tt> ready to be painted * @throws FilterEvaluationException * if the evaluation fails */ public BufferedImage getAsImage( Feature feature ) throws FilterEvaluationException { int intSizeX = (int) getSize( feature ); int intSizeY = intSizeX; //calculate the size of the first ExternalGraphic int intSizeImgX = -1; int intSizeImgY = -1; for ( int i = 0; i < marksAndExtGraphics.size(); i++ ) { Object o = marksAndExtGraphics.get( i ); if ( o instanceof ExternalGraphic ) { BufferedImage extImage = ( (ExternalGraphic) o ).getAsImage( intSizeX, intSizeY, feature ); intSizeImgX = extImage.getWidth(); intSizeImgY = extImage.getHeight(); break; } } if ( intSizeX < 0 ) { // if size is unspecified if ( intSizeImgX < 0 ) { // if there are no ExternalGraphics, use default value of 6 pixels intSizeX = 6; intSizeY = 6; } else { // if there are ExternalGraphics, use width and height of the first intSizeX = intSizeImgX; intSizeY = intSizeImgY; } } else { //if size is specified if ( intSizeImgX < 0 ) { // if there are no ExternalGraphics, use default intSizeX intSizeY = intSizeX; } else { // if there are ExternalGraphics, use the first to find the height intSizeY = (int) Math.round( ( ( (double) intSizeImgY ) / ( (double) intSizeImgX ) ) * intSizeX ); } } image = new BufferedImage( intSizeX, intSizeY, BufferedImage.TYPE_INT_ARGB ); Graphics2D g = (Graphics2D) image.getGraphics(); g.rotate( Math.toRadians( getRotation( feature ) ), intSizeX >> 1, intSizeY >> 1 ); for ( int i = 0; i < marksAndExtGraphics.size(); i++ ) { Object o = marksAndExtGraphics.get( i ); BufferedImage extImage = null; if ( o instanceof ExternalGraphic ) { extImage = ( (ExternalGraphic) o ).getAsImage( intSizeX, intSizeY, feature ); } else { extImage = ( (Mark) o ).getAsImage( feature, intSizeX ); } g.drawImage( extImage, 0, 0, intSizeX, intSizeY, null ); } // use the default Mark if there are no Marks / ExternalGraphics // specified at all if ( marksAndExtGraphics.size() == 0 ) { Mark mark = new Mark(); BufferedImage extImage = mark.getAsImage( feature, intSizeX ); g.drawImage( extImage, 0, 0, intSizeX, intSizeY, null ); } return image; } /** * Sets a <tt>BufferedImage</tt> representing this object. The image * respects the 'Opacity', 'Size' and 'Rotation' parameters. * <p> * * @param bufferedImage * BufferedImage to be set */ public void setAsImage( BufferedImage bufferedImage ) { image = bufferedImage; } /** * exports the content of the Graphic as XML formated String * * @return xml representation of the Graphic */ public String exportAsXML() { StringBuffer sb = new StringBuffer( 1000 ); sb.append( "<Graphic>" ); for ( int i = 0; i < marksAndExtGraphics.size(); i++ ) { sb.append( ( (Marshallable) marksAndExtGraphics.get( i ) ).exportAsXML() ); } if ( opacity != null ) { sb.append( "<Opacity>" ); sb.append( ( (Marshallable) opacity ).exportAsXML() ); sb.append( "</Opacity>" ); } if ( size != null ) { sb.append( "<Size>" ); sb.append( ( (Marshallable) size ).exportAsXML() ); sb.append( "</Size>" ); } if ( rotation != null ) { sb.append( "<Rotation>" ); sb.append( ( (Marshallable) rotation ).exportAsXML() ); sb.append( "</Rotation>" ); } sb.append( "</Graphic>" ); return sb.toString(); } } /* ******************************************************************** Changes to this class. What the people have been up to: $Log: Graphic.java,v $ Revision 1.11 2006/10/11 07:58:41 poth getter for ParameterValueType representations of rotation, opacity and size added Revision 1.10 2006/07/29 08:51:12 poth references to deprecated classes removed Revision 1.9 2006/07/12 14:46:14 poth comment footer added ********************************************************************** */