001 //$HeadURL: svn+ssh://rbezema@svn.wald.intevation.org/deegree/base/tags/2.1/src/org/deegree/graphics/sld/Stroke.java $ 002 /*---------------- FILE HEADER ------------------------------------------ 003 004 This file is part of deegree. 005 Copyright (C) 2001-2006 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: 6698 $ $Date: 2007-04-26 11:40:11 +0200 (Do, 26 Apr 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 }