1 34 35 36 package org.krysalis.jcharts; 37 38 39 import java.awt.Graphics2D ; 40 import java.awt.Paint ; 41 import java.awt.Shape ; 42 import java.awt.geom.AffineTransform ; 43 import java.awt.geom.Line2D ; 44 import java.awt.geom.Rectangle2D ; 45 import java.io.Serializable ; 46 import java.util.ArrayList ; 47 import java.util.Iterator ; 48 49 import org.krysalis.jcharts.Chart; 50 import org.krysalis.jcharts.chartData.interfaces.IAxisDataSeries; 51 import org.krysalis.jcharts.chartData.interfaces.IAxisPlotDataSet; 52 import org.krysalis.jcharts.chartData.interfaces.IData; 53 import org.krysalis.jcharts.chartData.interfaces.IPieChartDataSet; 54 import org.krysalis.jcharts.chartData.processors.TextProcessor; 55 import org.krysalis.jcharts.properties.LegendAreaProperties; 56 import org.krysalis.jcharts.properties.LegendProperties; 57 import org.krysalis.jcharts.properties.LineChartProperties; 58 import org.krysalis.jcharts.properties.PointChartProperties; 59 import org.krysalis.jcharts.test.HTMLGenerator; 60 import org.krysalis.jcharts.test.HTMLTestable; 61 import org.krysalis.jcharts.types.ChartType; 62 63 64 69 final public class Legend implements HTMLTestable, Serializable 70 { 71 private Chart chart; 72 private LegendProperties legendProperties; 73 74 private float iconSide; 75 76 77 private float widestLabelAndColumnPadding; 79 private int numColumns; 80 private int numRows; 81 82 83 private TextProcessor textProcessor; 84 85 private float x; 86 private float y; 87 private float width = 0; 88 private float height = 0; 89 90 91 private ArrayList labels; 93 private ArrayList paints; 94 private ArrayList shapes = new ArrayList (); 95 private ArrayList fillPointsFlags = new ArrayList (); 96 private ArrayList pointOutlinePaints = new ArrayList (); 97 private ChartType chartType; 98 private PointChartProperties pointChartProperties; 99 private LineChartProperties lineChartProperties; 100 101 102 107 public Legend( Chart chart ) 108 { 109 this.chart = chart; 110 } 111 112 113 118 public Legend( Chart chart, LegendProperties legendProperties ) 119 { 120 this.chart = chart; 121 this.legendProperties = legendProperties; 122 } 123 124 125 public void setX( float x ) 126 { 127 this.x = x; 128 } 129 130 131 public void setY( float y ) 132 { 133 this.y = y; 134 } 135 136 137 142 public void computeLegendXY( IAxisDataSeries iAxisDataSeries, float chartTitleHeight ) 143 { 144 this.calculateDrawingValues( iAxisDataSeries ); 146 147 if( (this.getLegendProperties().getPlacement() == LegendAreaProperties.RIGHT) 148 || (this.getLegendProperties().getPlacement() == LegendAreaProperties.LEFT) ) 149 { 150 if( this.getHeight() > this.chart.getImageHeight() - this.chart.getChartProperties().getEdgePadding() * 2 ) 151 { 152 this.setY( this.chart.getChartProperties().getEdgePadding() ); 153 } 154 else 155 { 156 this.setY( (this.chart.getImageHeight() / 2) - (this.getHeight() / 2) ); 157 } 158 159 if( this.getLegendProperties().getPlacement() == LegendAreaProperties.RIGHT ) 160 { 161 this.setX( this.chart.getImageWidth() - this.getWidth() - this.chart.getChartProperties().getEdgePadding() ); 162 } 163 else { 165 this.setX( this.chart.getChartProperties().getEdgePadding() ); 166 } 167 } 168 else { 170 if( this.getWidth() + this.chart.getChartProperties().getEdgePadding() * 2 > this.chart.getImageWidth() ) 171 { 172 this.setX( this.chart.getChartProperties().getEdgePadding() ); 173 } 174 else 175 { 176 this.setX( (this.chart.getImageWidth() / 2) - (this.getWidth() / 2) ); 177 } 178 179 if( this.getLegendProperties().getPlacement() == LegendAreaProperties.BOTTOM ) 180 { 181 this.setY( this.chart.getImageHeight() - this.getHeight() - this.chart.getChartProperties().getEdgePadding() ); 182 } 183 else { 185 this.setY( this.chart.getChartProperties().getEdgePadding() + chartTitleHeight ); 186 } 187 } 188 } 189 190 191 198 private void processData( IAxisDataSeries iAxisDataSeries ) 199 { 200 this.textProcessor = new TextProcessor(); 201 202 Iterator iterator = iAxisDataSeries.getIAxisPlotDataSetIterator(); 203 204 while( iterator.hasNext() ) 206 { 207 this.processLegendLabels( (IAxisPlotDataSet) iterator.next() ); 208 } 209 } 210 211 212 219 private void processData( IPieChartDataSet iPieChartDataSet ) 220 { 221 this.textProcessor = new TextProcessor(); 222 this.processLegendLabels( iPieChartDataSet ); 223 } 224 225 226 234 private void processLegendLabels( IAxisPlotDataSet iAxisPlotDataSet ) 235 { 236 for( int i = 0; i < iAxisPlotDataSet.getNumberOfLegendLabels(); i++ ) 237 { 238 if( iAxisPlotDataSet.getLegendLabel( i ) != null ) 240 { 241 this.textProcessor.addLabel( iAxisPlotDataSet.getLegendLabel( i ), this.legendProperties.getChartFont().getFont(), this.chart.getGraphics2D().getFontRenderContext() ); 242 243 this.labels.add( iAxisPlotDataSet.getLegendLabel( i ) ); 245 this.paints.add( iAxisPlotDataSet.getPaint( i ) ); 246 247 if( iAxisPlotDataSet.getChartType().equals( ChartType.POINT ) ) 248 { 249 this.chartType = ChartType.POINT; 250 this.pointChartProperties = (PointChartProperties) iAxisPlotDataSet.getChartTypeProperties(); 251 this.shapes.add( pointChartProperties.getShape( i ) ); 252 this.fillPointsFlags.add( new Boolean ( pointChartProperties.getFillPointsFlag( i ) ) ); 253 this.pointOutlinePaints.add( pointChartProperties.getPointOutlinePaints( i ) ); 254 } 255 if( iAxisPlotDataSet.getChartType().equals( ChartType.LINE ) ) 256 { 257 this.chartType = ChartType.LINE; 258 this.lineChartProperties = (LineChartProperties) iAxisPlotDataSet.getChartTypeProperties(); 259 if( lineChartProperties.getShapes() != null ) 260 { 261 this.shapes.add( lineChartProperties.getShapes()[i] ); 262 } 263 } 264 } 265 } 266 } 267 268 269 275 private void processLegendLabels( IPieChartDataSet iPieChartDataSet ) 276 { 277 for( int i = 0; i < iPieChartDataSet.getNumberOfLegendLabels(); i++ ) 278 { 279 if( iPieChartDataSet.getLegendLabel( i ) != null ) 281 { 282 this.textProcessor.addLabel( iPieChartDataSet.getLegendLabel( i ), this.legendProperties.getChartFont().getFont(), this.chart.getGraphics2D().getFontRenderContext() ); 283 284 this.labels.add( iPieChartDataSet.getLegendLabel( i ) ); 286 this.paints.add( iPieChartDataSet.getPaint( i ) ); 287 } 288 } 289 } 290 291 292 295 public LegendProperties getLegendProperties() 296 { 297 return this.legendProperties; 298 } 299 300 301 307 public void calculateDrawingValues( IData iData ) 308 { 309 int numberOfLabels; 310 this.labels = new ArrayList (); 311 this.paints = new ArrayList (); 312 313 314 if( iData instanceof IAxisDataSeries ) 315 { 316 IAxisDataSeries iAxisDataSeries = (IAxisDataSeries) iData; 317 this.processData( iAxisDataSeries ); 318 numberOfLabels = iAxisDataSeries.getTotalNumberOfDataSets(); 319 } 320 else 321 { 322 IPieChartDataSet iPieChartDataSet = (IPieChartDataSet) iData; 323 this.processData( iPieChartDataSet ); 324 numberOfLabels = iPieChartDataSet.getNumberOfLegendLabels(); 325 } 326 327 328 this.iconSide = (float) .50 * this.textProcessor.getTallestLabel(); 330 if( (chartType == ChartType.POINT) || (chartType == ChartType.LINE) ) 332 { 333 for( int i = 0; i < this.shapes.size(); i++ ) 335 { 336 try 338 { 339 Double shapeWidthDouble = new Double ( ( ( (Shape ) this.shapes.get( i ) ).getBounds2D().getWidth() ) ); 340 float shapeWidth = shapeWidthDouble.floatValue(); 341 this.iconSide = Math.max(this.iconSide, shapeWidth); 342 } 343 catch (NullPointerException npe) 344 { 345 System.err.println("Warning: legend shape is null"); 348 npe.printStackTrace(); 349 } 350 } 351 } 352 353 this.determineWidthAndHeight( numberOfLabels ); 354 } 355 356 357 360 public float getWidth() 361 { 362 return this.width; 363 } 364 365 366 369 public int getHeight() 370 { 371 return ((int) Math.ceil( this.height )); 373 } 374 375 376 380 private void determineWidthAndHeight( int numberOfLabels ) 381 { 382 width = this.legendProperties.getEdgePadding() * 2; 384 height = width; 385 386 387 if( this.legendProperties.getNumColumns() == LegendAreaProperties.COLUMNS_AS_MANY_AS_NEEDED 389 || this.legendProperties.getNumColumns() >= numberOfLabels ) 390 { 391 this.numColumns = numberOfLabels; 392 width += this.textProcessor.getTotalLabelWidths(); 393 394 this.numRows = 1; 395 } 396 else 398 { 399 this.widestLabelAndColumnPadding = this.textProcessor.getWidestLabel() + this.legendProperties.getColumnPadding(); 401 402 if( legendProperties.getNumColumns() == LegendAreaProperties.COLUMNS_FIT_TO_IMAGE ) 403 { 404 float actualWidth = legendProperties.getSize().width; 406 407 float widestLabelColumnAndIcon = 408 widestLabelAndColumnPadding + 409 iconSide + 410 legendProperties.getIconPadding() + 411 legendProperties.getColumnPadding(); 412 413 numColumns = (int) (actualWidth / widestLabelColumnAndIcon); 414 numColumns = Math.min( numColumns, numberOfLabels ); 415 } 416 else 417 { 418 numColumns = this.legendProperties.getNumColumns(); 419 } 420 421 width += this.textProcessor.getWidestLabel() * this.numColumns; 422 423 this.numRows = (int) Math.ceil( (double) numberOfLabels / (double) this.numColumns ); 424 } 425 426 427 width += (this.iconSide + this.legendProperties.getIconPadding()) * this.numColumns; 429 430 width += this.legendProperties.getColumnPadding() * (this.numColumns - 1); 432 433 if( chartType == ChartType.LINE) 435 { 436 width += this.legendProperties.getIconLineStrokeLength() * 2 * this.numColumns; 437 } 438 439 height += (this.textProcessor.getTallestLabel() * this.numRows); 441 442 height += (this.legendProperties.getRowPadding() * (this.numRows - 1)); 444 } 445 446 447 451 public void render() 452 { 453 Graphics2D g2d = this.chart.getGraphics2D(); 454 455 Rectangle2D.Float rectangle = new Rectangle2D.Float ( this.x, this.y, width - 1, this.height - 1 ); 457 458 if( this.legendProperties.getBackgroundPaint() != null ) 460 { 461 g2d.setPaint( this.legendProperties.getBackgroundPaint() ); 462 g2d.fill( rectangle ); 463 } 464 465 if( this.legendProperties.getBorderStroke() != null ) 467 { 468 this.legendProperties.getBorderStroke().draw( g2d, rectangle ); 469 } 470 471 474 g2d.setFont( this.legendProperties.getChartFont().getFont() ); 476 477 rectangle.y += this.legendProperties.getEdgePadding() + (this.textProcessor.getTallestLabel() / 2) - (this.iconSide / 2); 479 rectangle.width = this.iconSide; 480 rectangle.height = this.iconSide; 481 482 483 float posX = this.x + this.legendProperties.getEdgePadding(); 484 float fontY = rectangle.y + rectangle.height; 485 486 487 float yIncrement = this.textProcessor.getTallestLabel() + this.legendProperties.getRowPadding(); 489 float iconAndPaddingWidth = this.iconSide + this.legendProperties.getIconPadding(); 490 491 int labelIndex = 0; 492 493 for( int j = 0; j < this.numRows; j++ ) 495 { 496 for( int i = 0; i < this.numColumns; i++ ) 498 { 499 rectangle.x = posX; 500 501 g2d.setPaint( (Paint ) this.paints.get( labelIndex ) ); 503 504 506 if( this.shapes.size() > 0 && this.shapes.size() > labelIndex ) 507 { 508 510 AffineTransform affineTransform = g2d.getTransform(); 512 513 g2d.translate( rectangle.x, rectangle.y ); 515 516 if( this.fillPointsFlags.size() > 0 ) 517 { 518 if( ((Boolean ) this.fillPointsFlags.get( labelIndex )).booleanValue() ) 519 { 520 g2d.fill( (Shape ) this.shapes.get( labelIndex ) ); 521 522 if( this.pointOutlinePaints.get( labelIndex ) != null ) 525 { 526 g2d.setPaint( (Paint ) this.pointOutlinePaints.get( labelIndex ) ); 527 g2d.draw( (Shape ) this.shapes.get( labelIndex ) ); 528 } 529 } 530 } 531 else 532 { 533 if( chartType == ChartType.POINT) 535 { 536 g2d.draw( (Shape ) this.shapes.get( labelIndex ) ); 537 } else 538 { 541 Rectangle2D shapeBounds = ( (Shape ) this.shapes.get( labelIndex ) ).getBounds2D(); 543 double XOffset = shapeBounds.getWidth() / 2; 544 double YOffset = shapeBounds.getHeight() / 2; 545 546 g2d.setStroke(this.lineChartProperties.getLineStrokes()[ labelIndex]); 547 548 Line2D.Double line = new Line2D.Double (0, YOffset, this.legendProperties.getIconLineStrokeLength(), YOffset); 549 g2d.draw( line ); 550 posX += this.legendProperties.getIconLineStrokeLength(); 552 g2d.translate( this.legendProperties.getIconLineStrokeLength() - XOffset , 0 ); 554 555 556 line.x1 = XOffset; 557 g2d.draw( line ); 558 559 560 g2d.fill( (Shape ) this.shapes.get( labelIndex ) ); 561 562 if( this.legendProperties.getIconBorderStroke() != null && this.pointOutlinePaints.size() != 0 ) 564 { 565 if( this.pointOutlinePaints != null ) { 566 g2d.setStroke( this.legendProperties.getIconBorderStroke() ); 567 g2d.setPaint( (Paint ) this.pointOutlinePaints.get( labelIndex ) ); 568 g2d.draw( (Shape ) this.shapes.get( labelIndex ) ); 569 } 570 } 571 572 573 posX += this.legendProperties.getIconLineStrokeLength(); 575 } 576 } 577 g2d.setTransform( affineTransform ); 579 } 580 else 582 { 583 g2d.fill( rectangle ); 584 585 if( this.legendProperties.getIconBorderStroke() != null ) 587 { 588 g2d.setStroke( this.legendProperties.getIconBorderStroke() ); 589 g2d.setPaint( this.legendProperties.getIconBorderPaint() ); 590 g2d.draw( rectangle ); 591 } 592 } 593 594 595 g2d.setPaint( this.legendProperties.getChartFont().getPaint() ); 597 posX += iconAndPaddingWidth; 598 g2d.drawString( (String ) this.labels.get( labelIndex ), posX, fontY ); 599 600 601 if( this.legendProperties.getNumColumns() == LegendAreaProperties.COLUMNS_AS_MANY_AS_NEEDED 602 || this.legendProperties.getNumColumns() >= this.labels.size() ) 603 { 604 posX += this.textProcessor.getTextTag( labelIndex ).getWidth() + this.legendProperties.getColumnPadding(); 606 } 607 else 608 { 609 posX += this.widestLabelAndColumnPadding; 611 } 612 613 labelIndex++; 614 615 if( labelIndex == this.labels.size() ) break; 617 } 618 619 posX = this.x + this.legendProperties.getEdgePadding(); 620 fontY += yIncrement; 621 rectangle.y += yIncrement; 622 } 623 } 624 625 626 631 public void toHTML( HTMLGenerator htmlGenerator ) 632 { 633 htmlGenerator.legendTableStart(); 634 635 htmlGenerator.addTableRow( "Width", Float.toString( this.width ) ); 636 htmlGenerator.addTableRow( "Height", Float.toString( this.height ) ); 637 htmlGenerator.addTableRow( "Icon Side", Float.toString( this.iconSide ) ); 638 639 htmlGenerator.innerTableRowStart(); 640 this.legendProperties.toHTML( htmlGenerator ); 641 htmlGenerator.innerTableRowEnd(); 642 643 htmlGenerator.legendTableEnd(); 644 } 645 646 } 647 | Popular Tags |