001: //$HeadURL: https://svn.wald.intevation.org/svn/deegree/base/trunk/src/org/deegree/graphics/sld/Stroke.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.Color;
047: import java.util.HashMap;
048: import java.util.Iterator;
049: import java.util.StringTokenizer;
050:
051: import org.deegree.framework.util.ColorUtils;
052: import org.deegree.framework.xml.Marshallable;
053: import org.deegree.model.feature.Feature;
054: import org.deegree.model.filterencoding.Expression;
055: import org.deegree.model.filterencoding.FilterEvaluationException;
056:
057: /**
058: * A Stroke allows a string of line segments (or any linear geometry) to be rendered. There are
059: * three basic types of strokes: solid Color, GraphicFill (stipple), and repeated GraphicStroke. A
060: * repeated graphic is plotted linearly and has its graphic symbol bended around the curves of the
061: * line string. The default is a solid black line (Color "#000000").
062: * <p>
063: * The supported CSS-Parameter names are:
064: * <ul>
065: * <li>stroke (color)
066: * <li>stroke-opacity
067: * <li>stroke-width
068: * <li>stroke-linejoin
069: * <li>stroke-linecap
070: * <li>stroke-dasharray
071: * <li>stroke-dashoffset
072: * <p>
073: *
074: * @author <a href="mailto:poth@lat-lon.de">Andreas Poth</a>
075: * @author <a href="mailto:mschneider@lat-lon.de">Markus Schneider</a>
076: * @version $Revision: 9340 $ $Date: 2007-12-27 04:32:12 -0800 (Thu, 27 Dec 2007) $
077: */
078:
079: public class Stroke extends Drawing implements Marshallable {
080:
081: public static final int LJ_MITRE = java.awt.BasicStroke.JOIN_MITER;
082:
083: public static final int LJ_ROUND = java.awt.BasicStroke.JOIN_ROUND;
084:
085: public static final int LJ_BEVEL = java.awt.BasicStroke.JOIN_BEVEL;
086:
087: public static final int LC_BUTT = java.awt.BasicStroke.CAP_BUTT;
088:
089: public static final int LC_ROUND = java.awt.BasicStroke.CAP_ROUND;
090:
091: public static final int LC_SQUARE = java.awt.BasicStroke.CAP_SQUARE;
092:
093: // default values
094: public static final Color COLOR_DEFAULT = Color.decode("#000000");
095:
096: public static final double OPACITY_DEFAULT = 1.0;
097:
098: public static final double WIDTH_DEFAULT = 1.0;
099:
100: public static final int LJ_DEFAULT = LJ_MITRE;
101:
102: public static final int LC_DEFAULT = LC_BUTT;
103:
104: private GraphicStroke graphicStroke = null;
105:
106: private Color color = null;
107:
108: private double smplOpacity = -1;
109:
110: private double smplWidth = -1;
111:
112: private int smplLineJoin = -1;
113:
114: private int smplLineCap = -1;
115:
116: private float[] smplDashArray = null;
117:
118: private float smplDashOffset = -1;
119:
120: /**
121: * Constructs a new <tt>Stroke<tt>.
122: */
123: protected Stroke() {
124: super (new HashMap<String, Object>(), null);
125: }
126:
127: /**
128: * Constructs a new <tt>Stroke<tt>.
129: * <p>
130: * @param cssParams keys are <tt>Strings<tt> (see above), values are
131: * <tt>CssParameters</tt>
132: * @param graphicStroke
133: * @param graphicFill
134: */
135: protected Stroke(HashMap<String, Object> cssParams,
136: GraphicStroke graphicStroke, GraphicFill graphicFill) {
137: super (cssParams, graphicFill);
138: this .graphicStroke = graphicStroke;
139: try {
140: extractSimpleColor();
141: extractSimpleOpacity();
142: extractSimpleWidth();
143: extractSimpleLineJoin();
144: extractSimpleLineCap();
145: extractSimpleDasharray();
146: extractSimpleDashOffset();
147: } catch (Exception e) {
148: e.printStackTrace();
149: }
150: }
151:
152: /**
153: * extracts the color of the stroke if it is simple (nor Expression) to avoid new calculation
154: * for each call of getStroke(Feature feature)
155: */
156: private void extractSimpleColor() throws FilterEvaluationException {
157: CssParameter cssParam = (CssParameter) cssParams.get("stroke");
158: if (cssParam != null) {
159: Object[] o = cssParam.getValue().getComponents();
160: for (int i = 0; i < o.length; i++) {
161: if (o[i] instanceof Expression) {
162: color = null;
163: break;
164: }
165: try {
166: // trimming the String to avoid parsing errors with newlines
167: color = Color.decode(o[i].toString().trim());
168: } catch (NumberFormatException e) {
169: throw new FilterEvaluationException(
170: "Given value ('" + o[i]
171: + "') for CSS-Parameter 'stroke' "
172: + "does not denote a valid color!");
173: }
174: }
175: }
176: }
177:
178: /**
179: * returns true if the passed CssParameter contain a simple value
180: */
181: private boolean isSimple(CssParameter cssParam) {
182: boolean simple = true;
183: Object[] o = cssParam.getValue().getComponents();
184: for (int i = 0; i < o.length; i++) {
185: if (o[i] instanceof Expression) {
186: simple = false;
187: break;
188: }
189: }
190: return simple;
191: }
192:
193: /**
194: * extracts the opacity of the stroke if it is simple (no Expression) to avoid new calculation
195: * for each call of getStroke(Feature feature)
196: */
197: private void extractSimpleOpacity()
198: throws FilterEvaluationException {
199: CssParameter cssParam = (CssParameter) cssParams
200: .get("stroke-opacity");
201: if (cssParam != null) {
202: if (isSimple(cssParam)) {
203: Object[] o = cssParam.getValue().getComponents();
204: try {
205: smplOpacity = Double.parseDouble((String) o[0]);
206: } catch (NumberFormatException e) {
207: throw new FilterEvaluationException(
208: "Given value for parameter 'stroke-opacity' ('"
209: + o[0] + "') has invalid format!");
210: }
211:
212: if ((smplOpacity < 0.0) || (smplOpacity > 1.0)) {
213: throw new FilterEvaluationException(
214: "Value for parameter 'stroke-opacity' (given: '"
215: + o[0]
216: + "') must be between 0.0 and 1.0!");
217: }
218: }
219: }
220: }
221:
222: /**
223: * extracts the width of the stroke if it is simple (no Expression) to avoid new calculation for
224: * each call of getStroke(Feature feature)
225: */
226: private void extractSimpleWidth() throws FilterEvaluationException {
227: CssParameter cssParam = (CssParameter) cssParams
228: .get("stroke-width");
229: if (cssParam != null) {
230: if (isSimple(cssParam)) {
231: Object[] o = cssParam.getValue().getComponents();
232: try {
233: smplWidth = Double.parseDouble((String) o[0]);
234: } catch (NumberFormatException e) {
235: throw new FilterEvaluationException(
236: "Given value for parameter 'stroke-width' ('"
237: + o[0] + "') has invalid format!");
238: }
239: if (smplWidth < 0.0) {
240: throw new FilterEvaluationException(
241: "Value for parameter 'stroke-width' (given: '"
242: + smplWidth + "') must be > 0.0!");
243: }
244: }
245: }
246: }
247:
248: /**
249: * extracts the line join of the stroke if it is simple (no Expression) to avoid new calculation
250: * for each call of getStroke(Feature feature)
251: */
252: private void extractSimpleLineJoin()
253: throws FilterEvaluationException {
254: CssParameter cssParam = (CssParameter) cssParams
255: .get("stroke-linejoin");
256: if (cssParam != null) {
257: if (isSimple(cssParam)) {
258: Object[] o = cssParam.getValue().getComponents();
259: String value = (String) o[0];
260: if (value.equals("mitre")) {
261: smplLineJoin = Stroke.LJ_MITRE;
262: } else if (value.equals("round")) {
263: smplLineJoin = Stroke.LJ_ROUND;
264: } else if (value.equals("bevel")) {
265: smplLineJoin = Stroke.LJ_BEVEL;
266: } else {
267: throw new FilterEvaluationException(
268: "Given value for parameter 'stroke-linejoin' ('"
269: + value
270: + "') is unsupported. Supported values are: "
271: + "'mitre', 'round' or 'bevel'!");
272: }
273: }
274: }
275: }
276:
277: /**
278: * extracts the line cap of the stroke if it is simple (no Expression) to avoid new calculation
279: * for each call of getStroke(Feature feature)
280: */
281: private void extractSimpleLineCap()
282: throws FilterEvaluationException {
283: CssParameter cssParam = (CssParameter) cssParams
284: .get("stroke-linecap");
285: if (cssParam != null) {
286: if (isSimple(cssParam)) {
287: Object[] o = cssParam.getValue().getComponents();
288: String value = (String) o[0];
289: if (value.equals("butt")) {
290: smplLineCap = Stroke.LC_BUTT;
291: } else if (value.equals("round")) {
292: smplLineCap = Stroke.LC_ROUND;
293: } else if (value.equals("square")) {
294: smplLineCap = Stroke.LC_SQUARE;
295: } else {
296: throw new FilterEvaluationException(
297: "Given value for parameter 'stroke-linecap' ('"
298: + value
299: + "') is unsupported. Supported values are: "
300: + "'butt', 'round' or 'square'!");
301: }
302: }
303: }
304: }
305:
306: /**
307: * extracts the dasharray of the stroke if it is simple (no Expression) to avoid new calculation
308: * for each call of getStroke(Feature feature)
309: */
310: private void extractSimpleDasharray()
311: throws FilterEvaluationException {
312: CssParameter cssParam = (CssParameter) cssParams
313: .get("stroke-dasharray");
314: if (cssParam != null) {
315: if (isSimple(cssParam)) {
316: Object[] o = cssParam.getValue().getComponents();
317: String value = (String) o[0];
318: StringTokenizer st = new StringTokenizer(value, ",; ");
319: int count = st.countTokens();
320: float[] dashArray;
321:
322: if ((count % 2) == 0) {
323: dashArray = new float[count];
324: } else {
325: dashArray = new float[count * 2];
326: }
327:
328: int k = 0;
329: while (st.hasMoreTokens()) {
330: String s = st.nextToken();
331: try {
332: dashArray[k++] = Float.parseFloat(s);
333: } catch (NumberFormatException e) {
334: throw new FilterEvaluationException(
335: "List of values for parameter 'stroke-dashoffset' "
336: + "contains an invalid token: '"
337: + s + "'!");
338: }
339: }
340:
341: // odd number of values -> the pattern must be repeated twice
342: if ((count % 2) == 1) {
343: int j = 0;
344: while (k < ((count * 2) - 1)) {
345: dashArray[k++] = dashArray[j++];
346: }
347: }
348: smplDashArray = dashArray;
349: }
350: }
351: }
352:
353: /**
354: * extracts the dash offset of the stroke if it is simple (no Expression) to avoid new
355: * calculation for each call of getStroke(Feature feature)
356: */
357: private void extractSimpleDashOffset()
358: throws FilterEvaluationException {
359: CssParameter cssParam = (CssParameter) cssParams
360: .get("stroke-dashoffset");
361: if (cssParam != null) {
362: if (isSimple(cssParam)) {
363: Object[] o = cssParam.getValue().getComponents();
364: String value = (String) o[0];
365: try {
366: smplDashOffset = Float.parseFloat(value);
367: } catch (NumberFormatException e) {
368: throw new FilterEvaluationException(
369: "Given value for parameter 'stroke-dashoffset' ('"
370: + value + "') has invalid format!");
371: }
372: }
373: }
374: }
375:
376: /**
377: * The GraphicStroke element both indicates that a repeated-linear-graphic stroke type will be
378: * used.
379: * <p>
380: *
381: * @return the underlying <tt>GraphicStroke</tt> instance (may be null)
382: *
383: */
384: public GraphicStroke getGraphicStroke() {
385: return graphicStroke;
386: }
387:
388: /**
389: * The GraphicStroke element both indicates that a repeated-linear-graphic stroke type will be
390: * used.
391: *
392: * @param graphicStroke
393: * the graphicStroke element
394: * <p>
395: *
396: */
397: public void setGraphicStroke(GraphicStroke graphicStroke) {
398: this .graphicStroke = graphicStroke;
399: }
400:
401: /**
402: * The stroke CssParameter element gives the solid color that will be used for a stroke. The
403: * color value is RGB-encoded using two hexadecimal digits per primary-color component, in the
404: * order Red, Green, Blue, prefixed with a hash (#) sign. The hexadecimal digits between A and F
405: * may be in either uppercase or lowercase. For example, full red is encoded as #ff0000 (with no
406: * quotation marks). The default color is defined to be black (#000000) in the context of the
407: * LineSymbolizer, if the stroke CssParameter element is absent.
408: * <p>
409: *
410: * @param feature
411: * specifies the <tt>Feature</tt> to be used for evaluation of the underlying
412: * 'sld:ParameterValueType'
413: * @return the (evaluated) value of the parameter
414: * @throws FilterEvaluationException
415: * if the evaluation fails
416: */
417: public Color getStroke(Feature feature)
418: throws FilterEvaluationException {
419: Color awtColor = COLOR_DEFAULT;
420:
421: if (color == null) {
422: // evaluate color depending on the passed feature's properties
423: CssParameter cssParam = (CssParameter) cssParams
424: .get("stroke");
425:
426: if (cssParam != null) {
427: String s = cssParam.getValue(feature);
428:
429: try {
430: awtColor = Color.decode(s);
431: } catch (NumberFormatException e) {
432: throw new FilterEvaluationException(
433: "Given value ('" + s
434: + "') for CSS-Parameter 'stroke' "
435: + "does not denote a valid color!");
436: }
437: }
438: } else {
439: awtColor = color;
440: }
441:
442: return awtColor;
443: }
444:
445: /**
446: * @see org.deegree.graphics.sld.Stroke#getStroke(Feature)
447: * <p>
448: * @param stroke
449: * the stroke to be set
450: */
451: public void setStroke(Color stroke) {
452: this .color = stroke;
453: CssParameter strokeColor = StyleFactory.createCssParameter(
454: "stroke", ColorUtils.toHexCode("#", stroke));
455: cssParams.put("stroke", strokeColor);
456: }
457:
458: /**
459: * The stroke-opacity CssParameter element specifies the level of translucency to use when
460: * rendering the stroke. The value is encoded as a floating-point value (float) between 0.0 and
461: * 1.0 with 0.0 representing completely transparent and 1.0 representing completely opaque, with
462: * a linear scale of translucency for intermediate values. For example, 0.65 would represent 65%
463: * opacity. The default value is 1.0 (opaque).
464: * <p>
465: *
466: * @param feature
467: * specifies the <tt>Feature</tt> to be used for evaluation of the underlying
468: * 'sld:ParameterValueType'
469: * @return the (evaluated) value of the parameter
470: * @throws FilterEvaluationException
471: * if the evaluation fails
472: */
473: public double getOpacity(Feature feature)
474: throws FilterEvaluationException {
475: double opacity = OPACITY_DEFAULT;
476:
477: if (smplOpacity < 0) {
478: CssParameter cssParam = (CssParameter) cssParams
479: .get("stroke-opacity");
480:
481: if (cssParam != null) {
482: // evaluate opacity depending on the passed feature's properties
483: String value = cssParam.getValue(feature);
484:
485: try {
486: opacity = Double.parseDouble(value);
487: } catch (NumberFormatException e) {
488: throw new FilterEvaluationException(
489: "Given value for parameter 'stroke-opacity' ('"
490: + value + "') has invalid format!");
491: }
492:
493: if ((opacity < 0.0) || (opacity > 1.0)) {
494: throw new FilterEvaluationException(
495: "Value for parameter 'stroke-opacity' (given: '"
496: + value
497: + "') must be between 0.0 and 1.0!");
498: }
499: }
500: } else {
501: opacity = smplOpacity;
502: }
503:
504: return opacity;
505: }
506:
507: /**
508: * @see org.deegree.graphics.sld.Stroke#getOpacity(Feature)
509: * <p>
510: * @param opacity
511: * the opacity to be set for the stroke
512: */
513: public void setOpacity(double opacity) {
514: if (opacity > 1) {
515: opacity = 1;
516: } else if (opacity < 0) {
517: opacity = 0;
518: }
519: this .smplOpacity = opacity;
520: CssParameter strokeOp = StyleFactory.createCssParameter(
521: "stroke-opacity", "" + opacity);
522: cssParams.put("stroke-opacity", strokeOp);
523: }
524:
525: /**
526: * The stroke-width CssParameter element gives the absolute width (thickness) of a stroke in
527: * pixels encoded as a float. (Arguably, more units could be provided for encoding sizes, such
528: * as millimeters or typesetter's points.) The default is 1.0. Fractional numbers are allowed
529: * (with a system-dependent interpretation) but negative numbers are not.
530: * <p>
531: *
532: * @param feature
533: * specifies the <tt>Feature</tt> to be used for evaluation of the underlying
534: * 'sld:ParameterValueType'
535: * @return the (evaluated) value of the parameter
536: * @throws FilterEvaluationException
537: * if the evaluation fails
538: */
539: public double getWidth(Feature feature)
540: throws FilterEvaluationException {
541: double width = WIDTH_DEFAULT;
542:
543: if (smplWidth < 0) {
544: // evaluate smplWidth depending on the passed feature's properties
545: CssParameter cssParam = (CssParameter) cssParams
546: .get("stroke-width");
547:
548: if (cssParam != null) {
549: String value = cssParam.getValue(feature);
550:
551: try {
552: width = Double.parseDouble(value);
553: } catch (NumberFormatException e) {
554: throw new FilterEvaluationException(
555: "Given value for parameter 'stroke-width' ('"
556: + value + "') has invalid format!");
557: }
558:
559: if (width <= 0.0) {
560: throw new FilterEvaluationException(
561: "Value for parameter 'stroke-width' (given: '"
562: + value
563: + "') must be greater than 0!");
564: }
565: }
566: } else {
567: width = smplWidth;
568: }
569:
570: return width;
571: }
572:
573: /**
574: * @see org.deegree.graphics.sld.Stroke#getWidth(Feature)
575: * <p>
576: * @param width
577: * the width to be set for the stroke
578: */
579: public void setWidth(double width) {
580: if (width <= 0)
581: width = 1;
582: this .smplWidth = width;
583: CssParameter strokeWi = StyleFactory.createCssParameter(
584: "stroke-width", "" + width);
585: cssParams.put("stroke-width", strokeWi);
586: }
587:
588: /**
589: * The stroke-linejoin CssParameter element encode enumerated values telling how line strings
590: * should be joined (between line segments). The values are represented as content strings. The
591: * allowed values for line join are mitre, round, and bevel.
592: * <p>
593: *
594: * @param feature
595: * specifies the <tt>Feature</tt> to be used for evaluation of the underlying
596: * 'sld:ParameterValueType'
597: * @return the (evaluated) value of the parameter
598: * @throws FilterEvaluationException
599: * if the evaluation fails
600: */
601: public int getLineJoin(Feature feature)
602: throws FilterEvaluationException {
603: int lineJoin = LJ_DEFAULT;
604:
605: if (smplLineJoin < 0) {
606: CssParameter cssParam = (CssParameter) cssParams
607: .get("stroke-linejoin");
608:
609: if (cssParam != null) {
610: String value = cssParam.getValue(feature);
611:
612: if (value.equals("mitre")) {
613: lineJoin = Stroke.LJ_MITRE;
614: } else if (value.equals("round")) {
615: lineJoin = Stroke.LJ_ROUND;
616: } else if (value.equals("bevel")) {
617: lineJoin = Stroke.LJ_BEVEL;
618: } else {
619: throw new FilterEvaluationException(
620: "Given value for parameter 'stroke-linejoin' ('"
621: + value
622: + "') is unsupported. Supported values are: "
623: + "'mitre', 'round' or 'bevel'!");
624: }
625: }
626: } else {
627: lineJoin = smplLineJoin;
628: }
629:
630: return lineJoin;
631: }
632:
633: /**
634: * @see org.deegree.graphics.sld.Stroke#getLineJoin(Feature)
635: * <p>
636: * @param lineJoin
637: * the lineJoin to be set for the stroke
638: */
639: public void setLineJoin(int lineJoin) {
640: String join = null;
641: if (lineJoin == Stroke.LJ_MITRE) {
642: join = "mitre";
643: } else if (lineJoin == Stroke.LJ_ROUND) {
644: join = "round";
645: } else if (lineJoin == Stroke.LJ_BEVEL) {
646: join = "bevel";
647: } else {
648: // default
649: lineJoin = Stroke.LJ_BEVEL;
650: join = "bevel";
651: }
652: smplLineJoin = lineJoin;
653: CssParameter strokeLJ = StyleFactory.createCssParameter(
654: "stroke-linejoin", join);
655: cssParams.put("stroke-linejoin", strokeLJ);
656: }
657:
658: /**
659: * Thestroke-linecap CssParameter element encode enumerated values telling how line strings
660: * should be capped (at the two ends of the line string). The values are represented as content
661: * strings. The allowed values for line cap are butt, round, and square. The default values are
662: * system-dependent.
663: * <p>
664: *
665: * @param feature
666: * specifies the <tt>Feature</tt> to be used for evaluation of the underlying
667: * 'sld:ParameterValueType'
668: * @return the (evaluated) value of the parameter
669: * @throws FilterEvaluationException
670: * if the evaluation fails
671: */
672: public int getLineCap(Feature feature)
673: throws FilterEvaluationException {
674: int lineCap = LC_DEFAULT;
675:
676: if (smplLineJoin < 0) {
677:
678: CssParameter cssParam = (CssParameter) cssParams
679: .get("stroke-linecap");
680:
681: if (cssParam != null) {
682: String value = cssParam.getValue(feature);
683:
684: if (value.equals("butt")) {
685: lineCap = Stroke.LC_BUTT;
686: } else if (value.equals("round")) {
687: lineCap = Stroke.LC_ROUND;
688: } else if (value.equals("square")) {
689: lineCap = Stroke.LC_SQUARE;
690: } else {
691: throw new FilterEvaluationException(
692: "Given value for parameter 'stroke-linecap' ('"
693: + value
694: + "') is unsupported. Supported values are: "
695: + "'butt', 'round' or 'square'!");
696: }
697: }
698: } else {
699: lineCap = smplLineCap;
700: }
701:
702: return lineCap;
703: }
704:
705: /**
706: * @see org.deegree.graphics.sld.Stroke#getLineCap(Feature)
707: * <p>
708: * @param lineCap
709: * lineCap to be set for the stroke
710: */
711: public void setLineCap(int lineCap) {
712: String cap = null;
713: if (lineCap == Stroke.LC_BUTT) {
714: cap = "butt";
715: } else if (lineCap == Stroke.LC_ROUND) {
716: cap = "round";
717: } else if (lineCap == Stroke.LC_SQUARE) {
718: cap = "square";
719: } else {
720: // default;
721: cap = "round";
722: lineCap = Stroke.LC_SQUARE;
723: }
724: smplLineCap = lineCap;
725: CssParameter strokeCap = StyleFactory.createCssParameter(
726: "stroke-linecap", cap);
727: cssParams.put("stroke-linecap", strokeCap);
728: }
729:
730: /**
731: * Evaluates the 'stroke-dasharray' parameter as defined in OGC 02-070. The stroke-dasharray
732: * CssParameter element encodes a dash pattern as a series of space separated floats. The first
733: * number gives the length in pixels of dash to draw, the second gives the amount of space to
734: * leave, and this pattern repeats. If an odd number of values is given, then the pattern is
735: * expanded by repeating it twice to give an even number of values. Decimal values have a
736: * system-dependent interpretation (usually depending on whether antialiasing is being used).
737: * The default is to draw an unbroken line.
738: * <p>
739: *
740: * @param feature
741: * the encoded pattern
742: * @throws FilterEvaluationException
743: * if the eevaluation fails or the encoded pattern is erroneous
744: * @return the decoded pattern as an array of float-values (null if the parameter was not
745: * specified)
746: */
747: public float[] getDashArray(Feature feature)
748: throws FilterEvaluationException {
749: CssParameter cssParam = (CssParameter) cssParams
750: .get("stroke-dasharray");
751:
752: float[] dashArray = null;
753: if (smplDashArray == null) {
754: if (cssParam == null) {
755: return null;
756: }
757:
758: String value = cssParam.getValue(feature);
759:
760: StringTokenizer st = new StringTokenizer(value, ",; ");
761: int count = st.countTokens();
762:
763: if ((count % 2) == 0) {
764: dashArray = new float[count];
765: } else {
766: dashArray = new float[count * 2];
767: }
768:
769: int i = 0;
770: while (st.hasMoreTokens()) {
771: String s = st.nextToken();
772: try {
773: dashArray[i++] = Float.parseFloat(s);
774: } catch (NumberFormatException e) {
775: throw new FilterEvaluationException(
776: "List of values for parameter 'stroke-dashoffset' "
777: + "contains an invalid token: '"
778: + s + "'!");
779: }
780: }
781:
782: // odd number of values -> the pattern must be repeated twice
783: if ((count % 2) == 1) {
784: int j = 0;
785: while (i < ((count * 2) - 1)) {
786: dashArray[i++] = dashArray[j++];
787: }
788: }
789: } else {
790: dashArray = smplDashArray;
791: }
792:
793: return dashArray;
794: }
795:
796: /**
797: * @see org.deegree.graphics.sld.Stroke#getDashArray(Feature)
798: * <p>
799: * @param dashArray
800: * the dashArray to be set for the Stroke
801: */
802: public void setDashArray(float[] dashArray) {
803: if (dashArray != null) {
804: String s = "";
805: for (int i = 0; i < dashArray.length - 1; i++) {
806: s = s + dashArray[i] + ",";
807: }
808: s = s + dashArray[dashArray.length - 1];
809: smplDashArray = dashArray;
810: CssParameter strokeDash = StyleFactory.createCssParameter(
811: "stroke-dasharray", s);
812: cssParams.put("stroke-dasharray", strokeDash);
813: }
814: }
815:
816: /**
817: * The stroke-dashoffset CssParameter element specifies the distance as a float into the
818: * stroke-dasharray pattern at which to start drawing.
819: * <p>
820: *
821: * @param feature
822: * specifies the <tt>Feature</tt> to be used for evaluation of the underlying
823: * 'sld:ParameterValueType'
824: * @return the (evaluated) value of the parameter
825: * @throws FilterEvaluationException
826: * if the evaluation fails
827: */
828: public float getDashOffset(Feature feature)
829: throws FilterEvaluationException {
830: float dashOffset = 0;
831:
832: if (smplDashOffset < 0) {
833: CssParameter cssParam = (CssParameter) cssParams
834: .get("stroke-dashoffset");
835: if (cssParam != null) {
836: String value = cssParam.getValue(feature);
837:
838: try {
839: dashOffset = Float.parseFloat(value);
840: } catch (NumberFormatException e) {
841: throw new FilterEvaluationException(
842: "Given value for parameter 'stroke-dashoffset' ('"
843: + value + "') has invalid format!");
844: }
845: }
846: } else {
847: dashOffset = smplDashOffset;
848: }
849:
850: return dashOffset;
851: }
852:
853: /**
854: * The stroke-dashoffset CssParameter element specifies the distance as a float into the
855: * stroke-dasharray pattern at which to start drawing.
856: * <p>
857: *
858: * @param dashOffset
859: * the dashOffset to be set for the Stroke
860: */
861: public void setDashOffset(float dashOffset) {
862: if (dashOffset < 0)
863: dashOffset = 0;
864: smplDashOffset = dashOffset;
865: CssParameter strokeDashOff = StyleFactory.createCssParameter(
866: "stroke-dashoffset", "" + dashOffset);
867: cssParams.put("stroke-dashoffset", strokeDashOff);
868: }
869:
870: /**
871: * exports the content of the Stroke as XML formated String
872: *
873: * @return xml representation of the Stroke
874: */
875: public String exportAsXML() {
876:
877: StringBuffer sb = new StringBuffer(1000);
878: sb.append("<Stroke>");
879:
880: if (graphicFill != null) {
881: sb.append(((Marshallable) graphicFill).exportAsXML());
882: } else if (graphicStroke != null) {
883: sb.append(((Marshallable) graphicStroke).exportAsXML());
884: }
885: Iterator iterator = cssParams.values().iterator();
886: while (iterator.hasNext()) {
887: sb.append(((Marshallable) iterator.next()).exportAsXML());
888: }
889:
890: sb.append("</Stroke>");
891:
892: return sb.toString();
893: }
894:
895: }
|