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 }