1 48 49 package org.jfree.chart.renderer.category; 50 51 import java.awt.GradientPaint ; 52 import java.awt.Graphics2D ; 53 import java.awt.Paint ; 54 import java.awt.geom.Rectangle2D ; 55 import java.io.Serializable ; 56 57 import org.jfree.chart.axis.CategoryAxis; 58 import org.jfree.chart.axis.ValueAxis; 59 import org.jfree.chart.entity.CategoryItemEntity; 60 import org.jfree.chart.entity.EntityCollection; 61 import org.jfree.chart.event.RendererChangeEvent; 62 import org.jfree.chart.labels.CategoryItemLabelGenerator; 63 import org.jfree.chart.labels.CategoryToolTipGenerator; 64 import org.jfree.chart.plot.CategoryPlot; 65 import org.jfree.chart.plot.PlotOrientation; 66 import org.jfree.data.KeyToGroupMap; 67 import org.jfree.data.Range; 68 import org.jfree.data.category.CategoryDataset; 69 import org.jfree.data.general.DatasetUtilities; 70 import org.jfree.ui.RectangleEdge; 71 import org.jfree.util.PublicCloneable; 72 73 77 public class GroupedStackedBarRenderer extends StackedBarRenderer 78 implements Cloneable , PublicCloneable, 79 Serializable { 80 81 82 private static final long serialVersionUID = -2725921399005922939L; 83 84 85 private KeyToGroupMap seriesToGroupMap; 86 87 90 public GroupedStackedBarRenderer() { 91 super(); 92 this.seriesToGroupMap = new KeyToGroupMap(); 93 } 94 95 100 public void setSeriesToGroupMap(KeyToGroupMap map) { 101 if (map == null) { 102 throw new IllegalArgumentException ("Null 'map' argument."); 103 } 104 this.seriesToGroupMap = map; 105 notifyListeners(new RendererChangeEvent(this)); 106 } 107 108 117 public Range findRangeBounds(CategoryDataset dataset) { 118 Range r = DatasetUtilities.findStackedRangeBounds( 119 dataset, this.seriesToGroupMap 120 ); 121 return r; 122 } 123 124 134 protected void calculateBarWidth(CategoryPlot plot, 135 Rectangle2D dataArea, 136 int rendererIndex, 137 CategoryItemRendererState state) { 138 139 CategoryAxis xAxis = plot.getDomainAxisForDataset(rendererIndex); 141 CategoryDataset data = plot.getDataset(rendererIndex); 142 if (data != null) { 143 PlotOrientation orientation = plot.getOrientation(); 144 double space = 0.0; 145 if (orientation == PlotOrientation.HORIZONTAL) { 146 space = dataArea.getHeight(); 147 } 148 else if (orientation == PlotOrientation.VERTICAL) { 149 space = dataArea.getWidth(); 150 } 151 double maxWidth = space * getMaximumBarWidth(); 152 int groups = this.seriesToGroupMap.getGroupCount(); 153 int categories = data.getColumnCount(); 154 int columns = groups * categories; 155 double categoryMargin = 0.0; 156 double itemMargin = 0.0; 157 if (categories > 1) { 158 categoryMargin = xAxis.getCategoryMargin(); 159 } 160 if (groups > 1) { 161 itemMargin = getItemMargin(); 162 } 163 164 double used = space * (1 - xAxis.getLowerMargin() 165 - xAxis.getUpperMargin() 166 - categoryMargin - itemMargin); 167 if (columns > 0) { 168 state.setBarWidth(Math.min(used / columns, maxWidth)); 169 } 170 else { 171 state.setBarWidth(Math.min(used, maxWidth)); 172 } 173 } 174 175 } 176 177 192 protected double calculateBarW0(CategoryPlot plot, 193 PlotOrientation orientation, 194 Rectangle2D dataArea, 195 CategoryAxis domainAxis, 196 CategoryItemRendererState state, 197 int row, 198 int column) { 199 double space = 0.0; 201 if (orientation == PlotOrientation.HORIZONTAL) { 202 space = dataArea.getHeight(); 203 } 204 else { 205 space = dataArea.getWidth(); 206 } 207 double barW0 = domainAxis.getCategoryStart( 208 column, getColumnCount(), dataArea, plot.getDomainAxisEdge() 209 ); 210 int groupCount = this.seriesToGroupMap.getGroupCount(); 211 int groupIndex = this.seriesToGroupMap.getGroupIndex( 212 this.seriesToGroupMap.getGroup(plot.getDataset().getRowKey(row)) 213 ); 214 int categoryCount = getColumnCount(); 215 if (groupCount > 1) { 216 double groupGap = space * getItemMargin() 217 / (categoryCount * (groupCount - 1)); 218 double groupW = calculateSeriesWidth( 219 space, domainAxis, categoryCount, groupCount 220 ); 221 barW0 = barW0 + groupIndex * (groupW + groupGap) 222 + (groupW / 2.0) - (state.getBarWidth() / 2.0); 223 } 224 else { 225 barW0 = domainAxis.getCategoryMiddle( 226 column, getColumnCount(), dataArea, plot.getDomainAxisEdge() 227 ) - state.getBarWidth() / 2.0; 228 } 229 return barW0; 230 } 231 232 246 public void drawItem(Graphics2D g2, 247 CategoryItemRendererState state, 248 Rectangle2D dataArea, 249 CategoryPlot plot, 250 CategoryAxis domainAxis, 251 ValueAxis rangeAxis, 252 CategoryDataset dataset, 253 int row, 254 int column, 255 int pass) { 256 257 Number dataValue = dataset.getValue(row, column); 259 if (dataValue == null) { 260 return; 261 } 262 263 double value = dataValue.doubleValue(); 264 Comparable group 265 = this.seriesToGroupMap.getGroup(dataset.getRowKey(row)); 266 PlotOrientation orientation = plot.getOrientation(); 267 double barW0 = calculateBarW0( 268 plot, orientation, dataArea, domainAxis, 269 state, row, column 270 ); 271 272 double positiveBase = 0.0; 273 double negativeBase = 0.0; 274 275 for (int i = 0; i < row; i++) { 276 if (group.equals( 277 this.seriesToGroupMap.getGroup(dataset.getRowKey(i)) 278 )) { 279 Number v = dataset.getValue(i, column); 280 if (v != null) { 281 double d = v.doubleValue(); 282 if (d > 0) { 283 positiveBase = positiveBase + d; 284 } 285 else { 286 negativeBase = negativeBase + d; 287 } 288 } 289 } 290 } 291 292 double translatedBase; 293 double translatedValue; 294 RectangleEdge location = plot.getRangeAxisEdge(); 295 if (value > 0.0) { 296 translatedBase 297 = rangeAxis.valueToJava2D(positiveBase, dataArea, location); 298 translatedValue = rangeAxis.valueToJava2D( 299 positiveBase + value, dataArea, location 300 ); 301 } 302 else { 303 translatedBase = rangeAxis.valueToJava2D( 304 negativeBase, dataArea, location 305 ); 306 translatedValue = rangeAxis.valueToJava2D( 307 negativeBase + value, dataArea, location 308 ); 309 } 310 double barL0 = Math.min(translatedBase, translatedValue); 311 double barLength = Math.max( 312 Math.abs(translatedValue - translatedBase), getMinimumBarLength() 313 ); 314 315 Rectangle2D bar = null; 316 if (orientation == PlotOrientation.HORIZONTAL) { 317 bar = new Rectangle2D.Double ( 318 barL0, barW0, barLength, state.getBarWidth() 319 ); 320 } 321 else { 322 bar = new Rectangle2D.Double ( 323 barW0, barL0, state.getBarWidth(), barLength 324 ); 325 } 326 Paint itemPaint = getItemPaint(row, column); 327 if (getGradientPaintTransformer() != null 328 && itemPaint instanceof GradientPaint ) { 329 GradientPaint gp = (GradientPaint ) itemPaint; 330 itemPaint = getGradientPaintTransformer().transform(gp, bar); 331 } 332 g2.setPaint(itemPaint); 333 g2.fill(bar); 334 if (isDrawBarOutline() 335 && state.getBarWidth() > BAR_OUTLINE_WIDTH_THRESHOLD) { 336 g2.setStroke(getItemStroke(row, column)); 337 g2.setPaint(getItemOutlinePaint(row, column)); 338 g2.draw(bar); 339 } 340 341 CategoryItemLabelGenerator generator 342 = getItemLabelGenerator(row, column); 343 if (generator != null && isItemLabelVisible(row, column)) { 344 drawItemLabel( 345 g2, dataset, row, column, plot, generator, bar, 346 (value < 0.0) 347 ); 348 } 349 350 if (state.getInfo() != null) { 352 EntityCollection entities = state.getEntityCollection(); 353 if (entities != null) { 354 String tip = null; 355 CategoryToolTipGenerator tipster 356 = getToolTipGenerator(row, column); 357 if (tipster != null) { 358 tip = tipster.generateToolTip(dataset, row, column); 359 } 360 String url = null; 361 if (getItemURLGenerator(row, column) != null) { 362 url = getItemURLGenerator(row, column).generateURL( 363 dataset, row, column 364 ); 365 } 366 CategoryItemEntity entity = new CategoryItemEntity( 367 bar, tip, url, dataset, row, 368 dataset.getColumnKey(column), column 369 ); 370 entities.add(entity); 371 } 372 } 373 374 } 375 376 383 public boolean equals(Object obj) { 384 if (obj == this) { 385 return true; 386 } 387 if (obj instanceof GroupedStackedBarRenderer && super.equals(obj)) { 388 GroupedStackedBarRenderer r = (GroupedStackedBarRenderer) obj; 389 if (!r.seriesToGroupMap.equals(this.seriesToGroupMap)) { 390 return false; 391 } 392 return true; 393 } 394 return false; 395 } 396 397 } 398 | Popular Tags |