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