001 //$HeadURL: svn+ssh://rbezema@svn.wald.intevation.org/deegree/base/branches/2.2_testing/src/org/deegree/graphics/sld/Graphic.java $ 002 /*---------------- FILE HEADER ------------------------------------------ 003 004 This file is part of deegree. 005 Copyright (C) 2001-2008 by: 006 EXSE, Department of Geography, University of Bonn 007 http://www.giub.uni-bonn.de/deegree/ 008 lat/lon GmbH 009 http://www.lat-lon.de 010 011 This library is free software; you can redistribute it and/or 012 modify it under the terms of the GNU Lesser General Public 013 License as published by the Free Software Foundation; either 014 version 2.1 of the License, or (at your option) any later version. 015 016 This library is distributed in the hope that it will be useful, 017 but WITHOUT ANY WARRANTY; without even the implied warranty of 018 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 019 Lesser General Public License for more details. 020 021 You should have received a copy of the GNU Lesser General Public 022 License along with this library; if not, write to the Free Software 023 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 024 025 Contact: 026 027 Andreas Poth 028 lat/lon GmbH 029 Aennchenstr. 19 030 53115 Bonn 031 Germany 032 E-Mail: poth@lat-lon.de 033 034 Prof. Dr. Klaus Greve 035 Department of Geography 036 University of Bonn 037 Meckenheimer Allee 166 038 53115 Bonn 039 Germany 040 E-Mail: greve@giub.uni-bonn.de 041 042 043 ---------------------------------------------------------------------------*/ 044 package org.deegree.graphics.sld; 045 046 import java.awt.Graphics2D; 047 import java.awt.image.BufferedImage; 048 import java.util.ArrayList; 049 import java.util.List; 050 051 import org.deegree.framework.xml.Marshallable; 052 import org.deegree.model.feature.Feature; 053 import org.deegree.model.filterencoding.FilterEvaluationException; 054 055 /** 056 * A Graphic is a "graphic symbol" with an inherent shape, color, and size. Graphics can either be 057 * referenced from an external URL in a common format (such as GIF or SVG) or may be derived from a 058 * Mark. Multiple external URLs may be referenced with the semantic that they all provide the same 059 * graphic in different formats. The "hot spot" to use for rendering at a point or the start and 060 * finish handle points to use for rendering a graphic along a line must either be inherent in the 061 * external format or are system- dependent. The default size of an image format (such as GIF) is 062 * the inherent size of the image. The default size of a format without an inherent size is 16 063 * pixels in height and the corresponding aspect in width. If a size is specified, the height of the 064 * graphic will be scaled to that size and the corresponding aspect will be used for the width. The 065 * default if neither an ExternalURL nor a Mark is specified is to use the default Mark with a size 066 * of 6 pixels. The size is in pixels and the rotation is in degrees clockwise, with 0 (default) 067 * meaning no rotation. In the case that a Graphic is derived from a font-glyph Mark, the Size 068 * specified here will be used for the final rendering. Allowed CssParameters are "opacity", "size", 069 * and "rotation". 070 * 071 * 072 * @author <a href="mailto:poth@lat-lon.de">Andreas Poth</a> 073 * @author last edited by: $Author: apoth $ 074 * 075 * @version. $Revision: 9340 $, $Date: 2007-12-27 13:32:12 +0100 (Do, 27 Dez 2007) $ 076 */ 077 public class Graphic implements Marshallable { 078 079 // default values 080 public static final double OPACITY_DEFAULT = 1.0; 081 082 public static final double SIZE_DEFAULT = -1; 083 084 public static final double ROTATION_DEFAULT = 0.0; 085 086 private List<Object> marksAndExtGraphics = new ArrayList<Object>(); 087 088 private BufferedImage image = null; 089 090 private ParameterValueType opacity = null; 091 092 private ParameterValueType rotation = null; 093 094 private ParameterValueType size = null; 095 096 /** 097 * Creates a new <tt>Graphic</tt> instance. 098 * <p> 099 * 100 * @param marksAndExtGraphics 101 * the image will be based upon these 102 * @param opacity 103 * opacity that the resulting image will have 104 * @param size 105 * image height will be scaled to this value, respecting the proportions 106 * @param rotation 107 * image will be rotated clockwise for positive values, negative values result in 108 * anti-clockwise rotation 109 */ 110 protected Graphic( Object[] marksAndExtGraphics, ParameterValueType opacity, ParameterValueType size, 111 ParameterValueType rotation ) { 112 setMarksAndExtGraphics( marksAndExtGraphics ); 113 this.opacity = opacity; 114 this.size = size; 115 this.rotation = rotation; 116 } 117 118 /** 119 * Creates a new <tt>Graphic</tt> instance based on the default <tt>Mark</tt>: a square. 120 * <p> 121 * 122 * @param opacity 123 * opacity that the resulting image will have 124 * @param size 125 * image height will be scaled to this value, respecting the proportions 126 * @param rotation 127 * image will be rotated clockwise for positive values, negative values result in 128 * anti-clockwise rotation 129 */ 130 protected Graphic( ParameterValueType opacity, ParameterValueType size, ParameterValueType rotation ) { 131 Mark[] marks = new Mark[1]; 132 marks[0] = new Mark( "square", null, null ); 133 setMarksAndExtGraphics( marks ); 134 this.opacity = opacity; 135 this.size = size; 136 this.rotation = rotation; 137 } 138 139 /** 140 * returns the ParameterValueType representation of opacity 141 * 142 * @return the ParameterValueType representation of opacity 143 */ 144 public ParameterValueType getOpacity() { 145 return opacity; 146 } 147 148 /** 149 * returns the ParameterValueType representation of rotation 150 * 151 * @return the ParameterValueType representation of rotation 152 */ 153 public ParameterValueType getRotation() { 154 return rotation; 155 } 156 157 /** 158 * returns the ParameterValueType representation of size 159 * 160 * @return the ParameterValueType representation of size 161 */ 162 public ParameterValueType getSize() { 163 return size; 164 } 165 166 /** 167 * Creates a new <tt>Graphic</tt> instance based on the default <tt>Mark</tt>: a square. 168 */ 169 protected Graphic() { 170 this( null, null, null ); 171 } 172 173 /** 174 * Returns an object-array that enables the access to the stored <tt>ExternalGraphic</tt> and 175 * <tt>Mark</tt> -instances. 176 * <p> 177 * 178 * @return contains <tt>ExternalGraphic</tt> and <tt>Mark</tt> -objects 179 * 180 */ 181 public Object[] getMarksAndExtGraphics() { 182 Object[] objects = new Object[marksAndExtGraphics.size()]; 183 return marksAndExtGraphics.toArray( objects ); 184 } 185 186 /** 187 * Sets the <tt>ExternalGraphic</tt>/ <tt>Mark<tt>-instances that the image 188 * will be based on. 189 * <p> 190 * @param object to be used as basis for the resulting image 191 */ 192 public void setMarksAndExtGraphics( Object[] object ) { 193 image = null; 194 this.marksAndExtGraphics.clear(); 195 196 if ( object != null ) { 197 for ( int i = 0; i < object.length; i++ ) { 198 marksAndExtGraphics.add( object[i] ); 199 } 200 } 201 } 202 203 /** 204 * Adds an Object to an object-array that enables the access to the stored 205 * <tt>ExternalGraphic</tt> and <tt>Mark</tt> -instances. 206 * <p> 207 * 208 * @param object 209 * to be used as basis for the resulting image 210 */ 211 public void addMarksAndExtGraphic( Object object ) { 212 marksAndExtGraphics.add( object ); 213 } 214 215 /** 216 * Removes an Object from an object-array that enables the access to the stored 217 * <tt>ExternalGraphic</tt> and <tt>Mark</tt> -instances. 218 * <p> 219 * 220 * @param object 221 * to be used as basis for the resulting image 222 */ 223 public void removeMarksAndExtGraphic( Object object ) { 224 marksAndExtGraphics.remove( marksAndExtGraphics.indexOf( object ) ); 225 } 226 227 /** 228 * The Opacity element gives the opacity to use for rendering the graphic. 229 * <p> 230 * 231 * @param feature 232 * specifies the <tt>Feature</tt> to be used for evaluation of the underlying 233 * 'sld:ParameterValueType' 234 * @return the (evaluated) value of the parameter 235 * @throws FilterEvaluationException 236 * if the evaluation fails or the value is invalid 237 */ 238 public double getOpacity( Feature feature ) 239 throws FilterEvaluationException { 240 double opacityVal = OPACITY_DEFAULT; 241 242 if ( opacity != null ) { 243 String value = opacity.evaluate( feature ); 244 245 try { 246 opacityVal = Double.parseDouble( value ); 247 } catch ( NumberFormatException e ) { 248 throw new FilterEvaluationException( "Given value for parameter 'opacity' ('" + value 249 + "') has invalid format!" ); 250 } 251 252 if ( ( opacityVal < 0.0 ) || ( opacityVal > 1.0 ) ) { 253 throw new FilterEvaluationException( "Value for parameter 'opacity' (given: '" + value 254 + "') must be between 0.0 and 1.0!" ); 255 } 256 } 257 258 return opacityVal; 259 } 260 261 /** 262 * The Opacity element gives the opacity of to use for rendering the graphic. 263 * <p> 264 * 265 * @param opacity 266 * Opacity to be set for the graphic 267 */ 268 public void setOpacity( double opacity ) { 269 ParameterValueType pvt = null; 270 pvt = StyleFactory.createParameterValueType( "" + opacity ); 271 this.opacity = pvt; 272 } 273 274 /** 275 * The Size element gives the absolute size of the graphic in pixels encoded as a floating-point 276 * number. This element is also used in other contexts than graphic size and pixel units are 277 * still used even for font size. The default size for an object is context-dependent. Negative 278 * values are not allowed. 279 * <p> 280 * 281 * @param feature 282 * specifies the <tt>Feature</tt> to be used for evaluation of the underlying 283 * 'sld:ParameterValueType' 284 * @return the (evaluated) value of the parameter 285 * @throws FilterEvaluationException 286 * if the evaluation fails or the value is invalid 287 */ 288 public double getSize( Feature feature ) 289 throws FilterEvaluationException { 290 double sizeVal = SIZE_DEFAULT; 291 292 if ( size != null ) { 293 String value = size.evaluate( feature ); 294 295 try { 296 sizeVal = Double.parseDouble( value ); 297 } catch ( NumberFormatException e ) { 298 throw new FilterEvaluationException( "Given value for parameter 'size' ('" + value 299 + "') has invalid format!" ); 300 } 301 302 if ( sizeVal <= 0.0 ) { 303 throw new FilterEvaluationException( "Value for parameter 'size' (given: '" + value 304 + "') must be greater than 0!" ); 305 } 306 } 307 308 return sizeVal; 309 } 310 311 /** 312 * @see org.deegree.graphics.sld.Graphic#getSize(Feature) 313 * <p> 314 * @param size 315 * size to be set for the graphic 316 */ 317 public void setSize( double size ) { 318 ParameterValueType pvt = null; 319 pvt = StyleFactory.createParameterValueType( "" + size ); 320 this.size = pvt; 321 } 322 323 /** 324 * The Rotation element gives the rotation of a graphic in the clockwise direction about its 325 * center point in radian, encoded as a floating- point number. Negative values mean 326 * counter-clockwise rotation. The default value is 0.0 (no rotation). 327 * <p> 328 * 329 * @param feature 330 * specifies the <tt>Feature</tt> to be used for evaluation of the underlying 331 * 'sld:ParameterValueType' 332 * @return the (evaluated) value of the parameter 333 * @throws FilterEvaluationException 334 * if the evaluation fails or the value is invalid 335 */ 336 public double getRotation( Feature feature ) 337 throws FilterEvaluationException { 338 double rotVal = ROTATION_DEFAULT; 339 340 if ( rotation != null ) { 341 String value = rotation.evaluate( feature ); 342 343 try { 344 rotVal = Double.parseDouble( value ); 345 } catch ( NumberFormatException e ) { 346 throw new FilterEvaluationException( "Given value for parameter 'rotation' ('" + value 347 + "') has invalid format!" ); 348 } 349 } 350 351 return rotVal; 352 } 353 354 /** 355 * @see org.deegree.graphics.sld.Graphic#getRotation(Feature) 356 * <p> 357 * @param rotation 358 * rotation to be set for the graphic 359 */ 360 public void setRotation( double rotation ) { 361 ParameterValueType pvt = null; 362 pvt = StyleFactory.createParameterValueType( "" + rotation ); 363 this.rotation = pvt; 364 } 365 366 /** 367 * Returns a <tt>BufferedImage</tt> representing this object. The image respects the 368 * 'Opacity', 'Size' and 'Rotation' parameters. If the 'Size'-parameter is omitted, the height 369 * of the first <tt>ExternalGraphic</tt> is used. If there is none, the default value of 6 370 * pixels is used. 371 * <p> 372 * 373 * @return the <tt>BufferedImage</tt> ready to be painted 374 * @throws FilterEvaluationException 375 * if the evaluation fails 376 */ 377 public BufferedImage getAsImage( Feature feature ) 378 throws FilterEvaluationException { 379 int intSizeX = (int) getSize( feature ); 380 int intSizeY = intSizeX; 381 382 // calculate the size of the first ExternalGraphic 383 int intSizeImgX = -1; 384 int intSizeImgY = -1; 385 for ( int i = 0; i < marksAndExtGraphics.size(); i++ ) { 386 Object o = marksAndExtGraphics.get( i ); 387 if ( o instanceof ExternalGraphic ) { 388 BufferedImage extImage = ( (ExternalGraphic) o ).getAsImage( intSizeX, intSizeY, feature ); 389 intSizeImgX = extImage.getWidth(); 390 intSizeImgY = extImage.getHeight(); 391 break; 392 } 393 } 394 395 if ( intSizeX < 0 ) { 396 // if size is unspecified 397 if ( intSizeImgX < 0 ) { 398 // if there are no ExternalGraphics, use default value of 6 pixels 399 intSizeX = 6; 400 intSizeY = 6; 401 } else { 402 // if there are ExternalGraphics, use width and height of the first 403 intSizeX = intSizeImgX; 404 intSizeY = intSizeImgY; 405 } 406 } else { 407 // if size is specified 408 if ( intSizeImgY < 0 ) { 409 // if there are no ExternalGraphics, use default intSizeX 410 intSizeY = intSizeX; 411 } else { 412 // if there are ExternalGraphics, use the first to find the height 413 intSizeY = (int) Math.round( ( ( (double) intSizeImgY ) / ( (double) intSizeImgX ) ) * intSizeX ); 414 } 415 } 416 417 if ( intSizeX <= 0 || intSizeY <= 0 || intSizeX > 1000 || intSizeY > 1000 ) { 418 // if there are no ExternalGraphics, use default value of 1 pixel 419 System.out.println(intSizeX + " - " + intSizeY ); 420 intSizeX = 1; 421 intSizeY = 1; 422 } 423 424 image = new BufferedImage( intSizeX, intSizeY, BufferedImage.TYPE_INT_ARGB ); 425 426 Graphics2D g = (Graphics2D) image.getGraphics(); 427 g.rotate( Math.toRadians( getRotation( feature ) ), intSizeX >> 1, intSizeY >> 1 ); 428 429 for ( int i = 0; i < marksAndExtGraphics.size(); i++ ) { 430 Object o = marksAndExtGraphics.get( i ); 431 BufferedImage extImage = null; 432 433 if ( o instanceof ExternalGraphic ) { 434 extImage = ( (ExternalGraphic) o ).getAsImage( intSizeX, intSizeY, feature ); 435 } else { 436 extImage = ( (Mark) o ).getAsImage( feature, intSizeX ); 437 } 438 439 g.drawImage( extImage, 0, 0, intSizeX, intSizeY, null ); 440 } 441 442 // use the default Mark if there are no Marks / ExternalGraphics 443 // specified at all 444 if ( marksAndExtGraphics.size() == 0 ) { 445 Mark mark = new Mark(); 446 BufferedImage extImage = mark.getAsImage( feature, intSizeX ); 447 g.drawImage( extImage, 0, 0, intSizeX, intSizeY, null ); 448 } 449 450 return image; 451 } 452 453 /** 454 * Sets a <tt>BufferedImage</tt> representing this object. The image respects the 'Opacity', 455 * 'Size' and 'Rotation' parameters. 456 * <p> 457 * 458 * @param bufferedImage 459 * BufferedImage to be set 460 */ 461 public void setAsImage( BufferedImage bufferedImage ) { 462 image = bufferedImage; 463 } 464 465 /** 466 * exports the content of the Graphic as XML formated String 467 * 468 * @return xml representation of the Graphic 469 */ 470 public String exportAsXML() { 471 472 StringBuffer sb = new StringBuffer( 1000 ); 473 sb.append( "<Graphic>" ); 474 for ( int i = 0; i < marksAndExtGraphics.size(); i++ ) { 475 sb.append( ( (Marshallable) marksAndExtGraphics.get( i ) ).exportAsXML() ); 476 } 477 if ( opacity != null ) { 478 sb.append( "<Opacity>" ); 479 sb.append( ( (Marshallable) opacity ).exportAsXML() ); 480 sb.append( "</Opacity>" ); 481 } 482 if ( size != null ) { 483 sb.append( "<Size>" ); 484 sb.append( ( (Marshallable) size ).exportAsXML() ); 485 sb.append( "</Size>" ); 486 } 487 if ( rotation != null ) { 488 sb.append( "<Rotation>" ); 489 sb.append( ( (Marshallable) rotation ).exportAsXML() ); 490 sb.append( "</Rotation>" ); 491 } 492 sb.append( "</Graphic>" ); 493 494 return sb.toString(); 495 } 496 497 }