001: //$HeadURL: https://svn.wald.intevation.org/svn/deegree/base/trunk/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: mschneider $
074: *
075: * @version $Revision: 10547 $, $Date: 2008-03-11 01:40:28 -0700 (Tue, 11 Mar 2008) $
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,
111: ParameterValueType opacity, ParameterValueType size,
112: ParameterValueType rotation) {
113: setMarksAndExtGraphics(marksAndExtGraphics);
114: this .opacity = opacity;
115: this .size = size;
116: this .rotation = rotation;
117: }
118:
119: /**
120: * Creates a new <tt>Graphic</tt> instance based on the default <tt>Mark</tt>: a square.
121: * <p>
122: *
123: * @param opacity
124: * opacity that the resulting image will have
125: * @param size
126: * image height will be scaled to this value, respecting the proportions
127: * @param rotation
128: * image will be rotated clockwise for positive values, negative values result in
129: * anti-clockwise rotation
130: */
131: protected Graphic(ParameterValueType opacity,
132: ParameterValueType size, ParameterValueType rotation) {
133: Mark[] marks = new Mark[1];
134: marks[0] = new Mark("square", null, null);
135: setMarksAndExtGraphics(marks);
136: this .opacity = opacity;
137: this .size = size;
138: this .rotation = rotation;
139: }
140:
141: /**
142: * returns the ParameterValueType representation of opacity
143: *
144: * @return the ParameterValueType representation of opacity
145: */
146: public ParameterValueType getOpacity() {
147: return opacity;
148: }
149:
150: /**
151: * returns the ParameterValueType representation of rotation
152: *
153: * @return the ParameterValueType representation of rotation
154: */
155: public ParameterValueType getRotation() {
156: return rotation;
157: }
158:
159: /**
160: * returns the ParameterValueType representation of size
161: *
162: * @return the ParameterValueType representation of size
163: */
164: public ParameterValueType getSize() {
165: return size;
166: }
167:
168: /**
169: * Creates a new <tt>Graphic</tt> instance based on the default <tt>Mark</tt>: a square.
170: */
171: protected Graphic() {
172: this (null, null, null);
173: }
174:
175: /**
176: * Returns an object-array that enables the access to the stored <tt>ExternalGraphic</tt> and
177: * <tt>Mark</tt> -instances.
178: * <p>
179: *
180: * @return contains <tt>ExternalGraphic</tt> and <tt>Mark</tt> -objects
181: *
182: */
183: public Object[] getMarksAndExtGraphics() {
184: Object[] objects = new Object[marksAndExtGraphics.size()];
185: return marksAndExtGraphics.toArray(objects);
186: }
187:
188: /**
189: * Sets the <tt>ExternalGraphic</tt>/ <tt>Mark<tt>-instances that the image
190: * will be based on.
191: * <p>
192: * @param object to be used as basis for the resulting image
193: */
194: public void setMarksAndExtGraphics(Object[] object) {
195: image = null;
196: this .marksAndExtGraphics.clear();
197:
198: if (object != null) {
199: for (int i = 0; i < object.length; i++) {
200: marksAndExtGraphics.add(object[i]);
201: }
202: }
203: }
204:
205: /**
206: * Adds an Object to an object-array that enables the access to the stored
207: * <tt>ExternalGraphic</tt> and <tt>Mark</tt> -instances.
208: * <p>
209: *
210: * @param object
211: * to be used as basis for the resulting image
212: */
213: public void addMarksAndExtGraphic(Object object) {
214: marksAndExtGraphics.add(object);
215: }
216:
217: /**
218: * Removes an Object from an object-array that enables the access to the stored
219: * <tt>ExternalGraphic</tt> and <tt>Mark</tt> -instances.
220: * <p>
221: *
222: * @param object
223: * to be used as basis for the resulting image
224: */
225: public void removeMarksAndExtGraphic(Object object) {
226: marksAndExtGraphics.remove(marksAndExtGraphics.indexOf(object));
227: }
228:
229: /**
230: * The Opacity element gives the opacity to use for rendering the graphic.
231: * <p>
232: *
233: * @param feature
234: * specifies the <tt>Feature</tt> to be used for evaluation of the underlying
235: * 'sld:ParameterValueType'
236: * @return the (evaluated) value of the parameter
237: * @throws FilterEvaluationException
238: * if the evaluation fails or the value is invalid
239: */
240: public double getOpacity(Feature feature)
241: throws FilterEvaluationException {
242: double opacityVal = OPACITY_DEFAULT;
243:
244: if (opacity != null) {
245: String value = opacity.evaluate(feature);
246:
247: try {
248: opacityVal = Double.parseDouble(value);
249: } catch (NumberFormatException e) {
250: throw new FilterEvaluationException(
251: "Given value for parameter 'opacity' ('"
252: + value + "') has invalid format!");
253: }
254:
255: if ((opacityVal < 0.0) || (opacityVal > 1.0)) {
256: throw new FilterEvaluationException(
257: "Value for parameter 'opacity' (given: '"
258: + value
259: + "') must be between 0.0 and 1.0!");
260: }
261: }
262:
263: return opacityVal;
264: }
265:
266: /**
267: * The Opacity element gives the opacity of to use for rendering the graphic.
268: * <p>
269: *
270: * @param opacity
271: * Opacity to be set for the graphic
272: */
273: public void setOpacity(double opacity) {
274: ParameterValueType pvt = null;
275: pvt = StyleFactory.createParameterValueType("" + opacity);
276: this .opacity = pvt;
277: }
278:
279: /**
280: * The Size element gives the absolute size of the graphic in pixels encoded as a floating-point
281: * number. This element is also used in other contexts than graphic size and pixel units are
282: * still used even for font size. The default size for an object is context-dependent. Negative
283: * values are not allowed.
284: * <p>
285: *
286: * @param feature
287: * specifies the <tt>Feature</tt> to be used for evaluation of the underlying
288: * 'sld:ParameterValueType'
289: * @return the (evaluated) value of the parameter
290: * @throws FilterEvaluationException
291: * if the evaluation fails or the value is invalid
292: */
293: public double getSize(Feature feature)
294: throws FilterEvaluationException {
295: double sizeVal = SIZE_DEFAULT;
296:
297: if (size != null) {
298: String value = size.evaluate(feature);
299:
300: try {
301: sizeVal = Double.parseDouble(value);
302: } catch (NumberFormatException e) {
303: throw new FilterEvaluationException(
304: "Given value for parameter 'size' ('" + value
305: + "') has invalid format!");
306: }
307:
308: if (sizeVal <= 0.0) {
309: throw new FilterEvaluationException(
310: "Value for parameter 'size' (given: '" + value
311: + "') must be greater than 0!");
312: }
313: }
314:
315: return sizeVal;
316: }
317:
318: /**
319: * @see org.deegree.graphics.sld.Graphic#getSize(Feature)
320: * <p>
321: * @param size
322: * size to be set for the graphic
323: */
324: public void setSize(double size) {
325: ParameterValueType pvt = null;
326: pvt = StyleFactory.createParameterValueType("" + size);
327: this .size = pvt;
328: }
329:
330: /**
331: * The Rotation element gives the rotation of a graphic in the clockwise direction about its
332: * center point in radian, encoded as a floating- point number. Negative values mean
333: * counter-clockwise rotation. The default value is 0.0 (no rotation).
334: * <p>
335: *
336: * @param feature
337: * specifies the <tt>Feature</tt> to be used for evaluation of the underlying
338: * 'sld:ParameterValueType'
339: * @return the (evaluated) value of the parameter
340: * @throws FilterEvaluationException
341: * if the evaluation fails or the value is invalid
342: */
343: public double getRotation(Feature feature)
344: throws FilterEvaluationException {
345: double rotVal = ROTATION_DEFAULT;
346:
347: if (rotation != null) {
348: String value = rotation.evaluate(feature);
349:
350: try {
351: rotVal = Double.parseDouble(value);
352: } catch (NumberFormatException e) {
353: throw new FilterEvaluationException(
354: "Given value for parameter 'rotation' ('"
355: + value + "') has invalid format!");
356: }
357: }
358:
359: return rotVal;
360: }
361:
362: /**
363: * @see org.deegree.graphics.sld.Graphic#getRotation(Feature)
364: * <p>
365: * @param rotation
366: * rotation to be set for the graphic
367: */
368: public void setRotation(double rotation) {
369: ParameterValueType pvt = null;
370: pvt = StyleFactory.createParameterValueType("" + rotation);
371: this .rotation = pvt;
372: }
373:
374: /**
375: * Returns a <tt>BufferedImage</tt> representing this object. The image respects the
376: * 'Opacity', 'Size' and 'Rotation' parameters. If the 'Size'-parameter is omitted, the height
377: * of the first <tt>ExternalGraphic</tt> is used. If there is none, the default value of 6
378: * pixels is used.
379: * <p>
380: *
381: * @return the <tt>BufferedImage</tt> ready to be painted
382: * @throws FilterEvaluationException
383: * if the evaluation fails
384: */
385: public BufferedImage getAsImage(Feature feature)
386: throws FilterEvaluationException {
387: int intSizeX = (int) getSize(feature);
388: int intSizeY = intSizeX;
389:
390: // calculate the size of the first ExternalGraphic
391: int intSizeImgX = -1;
392: int intSizeImgY = -1;
393: for (int i = 0; i < marksAndExtGraphics.size(); i++) {
394: Object o = marksAndExtGraphics.get(i);
395: if (o instanceof ExternalGraphic) {
396: BufferedImage extImage = ((ExternalGraphic) o)
397: .getAsImage(intSizeX, intSizeY, feature);
398: intSizeImgX = extImage.getWidth();
399: intSizeImgY = extImage.getHeight();
400: break;
401: }
402: }
403:
404: if (intSizeX < 0) {
405: // if size is unspecified
406: if (intSizeImgX < 0) {
407: // if there are no ExternalGraphics, use default value of 6 pixels
408: intSizeX = 6;
409: intSizeY = 6;
410: } else {
411: // if there are ExternalGraphics, use width and height of the first
412: intSizeX = intSizeImgX;
413: intSizeY = intSizeImgY;
414: }
415: } else {
416: // if size is specified
417: if (intSizeImgY < 0) {
418: // if there are no ExternalGraphics, use default intSizeX
419: intSizeY = intSizeX;
420: } else {
421: // if there are ExternalGraphics, use the first to find the height
422: intSizeY = (int) Math
423: .round((((double) intSizeImgY) / ((double) intSizeImgX))
424: * intSizeX);
425: }
426: }
427:
428: if (intSizeX <= 0 || intSizeY <= 0 || intSizeX > 1000
429: || intSizeY > 1000) {
430: // if there are no ExternalGraphics, use default value of 1 pixel
431: System.out.println(intSizeX + " - " + intSizeY);
432: intSizeX = 1;
433: intSizeY = 1;
434: }
435:
436: image = new BufferedImage(intSizeX, intSizeY,
437: BufferedImage.TYPE_INT_ARGB);
438:
439: Graphics2D g = (Graphics2D) image.getGraphics();
440: g.rotate(Math.toRadians(getRotation(feature)), intSizeX >> 1,
441: intSizeY >> 1);
442:
443: for (int i = 0; i < marksAndExtGraphics.size(); i++) {
444: Object o = marksAndExtGraphics.get(i);
445: BufferedImage extImage = null;
446:
447: if (o instanceof ExternalGraphic) {
448: extImage = ((ExternalGraphic) o).getAsImage(intSizeX,
449: intSizeY, feature);
450: } else {
451: extImage = ((Mark) o).getAsImage(feature, intSizeX);
452: }
453:
454: g.drawImage(extImage, 0, 0, intSizeX, intSizeY, null);
455: }
456:
457: // use the default Mark if there are no Marks / ExternalGraphics
458: // specified at all
459: if (marksAndExtGraphics.size() == 0) {
460: Mark mark = new Mark();
461: BufferedImage extImage = mark.getAsImage(feature, intSizeX);
462: g.drawImage(extImage, 0, 0, intSizeX, intSizeY, null);
463: }
464:
465: return image;
466: }
467:
468: /**
469: * Sets a <tt>BufferedImage</tt> representing this object. The image respects the 'Opacity',
470: * 'Size' and 'Rotation' parameters.
471: * <p>
472: *
473: * @param bufferedImage
474: * BufferedImage to be set
475: */
476: public void setAsImage(BufferedImage bufferedImage) {
477: image = bufferedImage;
478: }
479:
480: /**
481: * exports the content of the Graphic as XML formated String
482: *
483: * @return xml representation of the Graphic
484: */
485: public String exportAsXML() {
486:
487: StringBuffer sb = new StringBuffer(1000);
488: sb.append("<Graphic>");
489: for (int i = 0; i < marksAndExtGraphics.size(); i++) {
490: sb.append(((Marshallable) marksAndExtGraphics.get(i))
491: .exportAsXML());
492: }
493: if (opacity != null) {
494: sb.append("<Opacity>");
495: sb.append(((Marshallable) opacity).exportAsXML());
496: sb.append("</Opacity>");
497: }
498: if (size != null) {
499: sb.append("<Size>");
500: sb.append(((Marshallable) size).exportAsXML());
501: sb.append("</Size>");
502: }
503: if (rotation != null) {
504: sb.append("<Rotation>");
505: sb.append(((Marshallable) rotation).exportAsXML());
506: sb.append("</Rotation>");
507: }
508: sb.append("</Graphic>");
509:
510: return sb.toString();
511: }
512:
513: }
|