Package com.positive.charts.renderer.category

Source Code of com.positive.charts.renderer.category.GroupedStackedBarRenderer

/* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-2005, by Object Refinery Limited and Contributors.
*
* Project Info:  http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301,
* USA. 
*
* [Java is a trademark or registered trademark of Sun Microsystems, Inc.
* in the United States and other countries.]
*
* ------------------------------
* GroupedStackedBarRenderer.java
* ------------------------------
* (C) Copyright 2004, 2005, by Object Refinery Limited and Contributors.
*
* Original Author:  David Gilbert (for Object Refinery Limited);
* Contributor(s):   -;
*
* $Id: GroupedStackedBarRenderer.java,v 1.9 2007/12/25 12:05:57 mytruth Exp $
*
* Changes
* -------
* 29-Apr-2004 : Version 1 (DG);
* 08-Jul-2004 : Added equals() method (DG);
* 05-Nov-2004 : Modified drawItem() signature (DG);
* 07-Jan-2005 : Renamed getRangeExtent() --> findRangeBounds (DG);
* 20-Apr-2005 : Renamed CategoryLabelGenerator
*               --> CategoryItemLabelGenerator (DG);
* 22-Sep-2005 : Renamed getMaxBarWidth() --> getMaximumBarWidth() (DG);
*
*/

package com.positive.charts.renderer.category;

import java.io.Serializable;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.graphics.Region;

import com.positive.charts.axis.CategoryAxis;
import com.positive.charts.axis.ValueAxis;
import com.positive.charts.common.RectangleEdge;
import com.positive.charts.data.KeyToGroupMap;
import com.positive.charts.data.Range;
import com.positive.charts.data.category.CategoryDataset;
import com.positive.charts.data.category.GroupToBarBaseCalculator;
import com.positive.charts.data.util.DatasetUtilities;
import com.positive.charts.data.util.PublicCloneable;
import com.positive.charts.entity.CategoryItemEntity;
import com.positive.charts.entity.EntityCollection;
import com.positive.charts.event.RendererChangeEvent;
import com.positive.charts.labels.CategoryItemLabelGenerator;
import com.positive.charts.labels.CategoryToolTipGenerator;
import com.positive.charts.plot.CategoryPlot;
import com.positive.charts.plot.PlotOrientation;
import com.positive.charts.util.RectangleUtil;

/**
* A renderer that draws stacked bars within groups. This will probably be
* merged with the {@link StackedBarRenderer} class at some point.
*/
public class GroupedStackedBarRenderer extends StackedBarRenderer implements
    Cloneable, PublicCloneable, Serializable {

  /** For serialization. */
  private static final long serialVersionUID = -2725921399005922939L;

  public static final double barScaleFactor = 1.5;

  /** A map used to assign each series to a group. */
  private com.positive.charts.data.KeyToGroupMap seriesToGroupMap;

  // TODO: by slapukhov
  private boolean treatNullColorsAsTransparent = false;

  // TODO: by slapukhov
  private boolean placeSubcategoryLabelsAtBarLevel = false;

  private final Map rowColumn2Bar = new HashMap();

  /**
   * Creates a new renderer.
   */
  public GroupedStackedBarRenderer() {
    this(false, false);
  }

  /**
   * Creates a new renderer.
   */
  public GroupedStackedBarRenderer(
      final boolean treatNullColorsAsTransparent,
      final boolean placeSubcategoryLabelsAtBarLevel) {
    super();
    this.treatNullColorsAsTransparent = treatNullColorsAsTransparent;
    this.placeSubcategoryLabelsAtBarLevel = placeSubcategoryLabelsAtBarLevel;
    this.seriesToGroupMap = new KeyToGroupMap();
  }

  /**
   * Calculates the coordinate of the first "side" of a bar. This will be the
   * minimum x-coordinate for a vertical bar, and the minimum y-coordinate for
   * a horizontal bar.
   *
   * @param plot
   *            the plot.
   * @param orientation
   *            the plot orientation.
   * @param dataArea
   *            the data area.
   * @param domainAxis
   *            the domain axis.
   * @param state
   *            the renderer state (has the bar width precalculated).
   * @param row
   *            the row index.
   * @param column
   *            the column index.
   *
   * @return The coordinate.
   */
  protected double calculateBarW0(final CategoryPlot plot,
      final PlotOrientation orientation, final Rectangle dataArea,
      final CategoryAxis domainAxis,
      final CategoryItemRendererState state, final int row,
      final int column) {
    // calculate bar width...
    double space = 0.0;
    if (orientation == PlotOrientation.HORIZONTAL) {
      space = dataArea.height;
    } else {
      space = dataArea.width;
    }
    double barW0 = domainAxis.getCategoryStart(column, this
        .getColumnCount(), dataArea, plot.getDomainAxisEdge());
    final int groupCount = this.seriesToGroupMap.getGroupCount();
    final int groupIndex = this.seriesToGroupMap
        .getGroupIndex(this.seriesToGroupMap.getGroup(plot.getDataset()
            .getRowKey(row)));
    final int categoryCount = this.getColumnCount();
    if (groupCount > 1) {
      final double groupGap = space * this.getItemMargin()
          / (categoryCount * (groupCount - 1));
      final double groupW = this.calculateSeriesWidth(space, domainAxis,
          categoryCount, groupCount);
      barW0 = barW0 + groupIndex * (groupW + groupGap) + (groupW / 2.0)
          - (state.getBarWidth() / 2.0);
    } else {
      barW0 = domainAxis.getCategoryMiddle(column, this.getColumnCount(),
          dataArea, plot.getDomainAxisEdge())
          - state.getBarWidth() / 2.0;
    }
    return barW0;
  }

  public double calculateBarWidth(final CategoryPlot plot,
      final Rectangle dataArea, final int rendererIndex) {
    // calculate the bar width
    final CategoryAxis xAxis = plot.getDomainAxisForDataset(rendererIndex);
    final CategoryDataset data = plot.getDataset(rendererIndex);
    if (data != null) {
      final PlotOrientation orientation = plot.getOrientation();
      double space = 0.0;
      if (orientation == PlotOrientation.HORIZONTAL) {
        space = dataArea.height;
      } else if (orientation == PlotOrientation.VERTICAL) {
        space = dataArea.width;
      }
      final double maxWidth = this.calculateMaxBarWidth(plot, dataArea,
          rendererIndex);
      final int groups = this.seriesToGroupMap.getGroupCount();
      final int categories = data.getColumnCount();
      final int columns = groups * categories;
      double categoryMargin = 0.0;
      double itemMargin = 0.0;
      if (categories > 1) {
        categoryMargin = xAxis.getCategoryMargin();
      }
      if (groups > 1) {
        itemMargin = this.getItemMargin();
      }

      final double used = space
          * (1 - xAxis.getLowerMargin() - xAxis.getUpperMargin()
              - categoryMargin - itemMargin);

      double barWidth = 0.0;

      if (columns > 0) {
        barWidth = Math.min(used / columns, maxWidth);
      } else {
        barWidth = Math.min(used, maxWidth);
      }

      return barWidth * barScaleFactor;
    }

    return 0.0;
  }

  /**
   * Calculates the bar width and stores it in the renderer state. We override
   * the method in the base class to take account of the series-to-group
   * mapping.
   *
   * @param plot
   *            the plot.
   * @param dataArea
   *            the data area.
   * @param rendererIndex
   *            the renderer index.
   * @param state
   *            the renderer state.
   */
  protected void calculateBarWidth(final CategoryPlot plot,
      final Rectangle dataArea, final int rendererIndex,
      final CategoryItemRendererState state) {

    // calculate the bar width
    final CategoryDataset data = plot.getDataset(rendererIndex);
    if (data != null) {
      final double barWidth = this.calculateBarWidth(plot, dataArea,
          rendererIndex);
      state.setBarWidth(barWidth);
    }
  }

  public double calculateMaxBarWidth(final CategoryPlot plot,
      final Rectangle dataArea, final int rendererIndex) {
    final CategoryDataset data = plot.getDataset(rendererIndex);
    if (data != null) {
      final PlotOrientation orientation = plot.getOrientation();
      double space = 0.0;
      if (orientation == PlotOrientation.HORIZONTAL) {
        space = dataArea.height;
      } else if (orientation == PlotOrientation.VERTICAL) {
        space = dataArea.width;
      }
      return space * this.getMaximumBarWidth();
    }

    return 0.0;
  }

  public double calculateMinSpaceForBarWidth(final CategoryPlot plot,
      final int rendererIndex, final double givenBarWidth) {
    double space = 0.0;
    final CategoryAxis xAxis = plot.getDomainAxisForDataset(rendererIndex);
    final CategoryDataset data = plot.getDataset(rendererIndex);
    if (data != null) {
      final int groups = this.seriesToGroupMap.getGroupCount();
      final int categories = data.getColumnCount();
      final int columns = groups * categories;
      double categoryMargin = 0.0;
      double itemMargin = 0.0;
      if (categories > 1) {
        categoryMargin = xAxis.getCategoryMargin();
      }
      if (groups > 1) {
        itemMargin = this.getItemMargin();
      }

      final double denom = 1 - xAxis.getLowerMargin()
          - xAxis.getUpperMargin() - categoryMargin - itemMargin;

      if (denom == 0) {
        return 0;
      }

      space = (givenBarWidth * columns) / denom;

      return space;
    }

    return 0;
  }

  /**
   * Draws a stacked bar for a specific item.
   *
   * @param gc
   *            the graphics device.
   * @param state
   *            the renderer state.
   * @param dataArea
   *            the plot area.
   * @param plot
   *            the plot.
   * @param domainAxis
   *            the domain (category) axis.
   * @param rangeAxis
   *            the range (value) axis.
   * @param dataset
   *            the data.
   * @param row
   *            the row index (zero-based).
   * @param column
   *            the column index (zero-based).
   * @param pass
   *            the pass index.
   */
  public void drawItem(final GC gc, final CategoryItemRendererState state,
      final Rectangle dataArea, final CategoryPlot plot,
      final CategoryAxis domainAxis, final ValueAxis rangeAxis,
      final CategoryDataset dataset, final int row, final int column,
      final int pass) {

    // nothing is drawn for null values...
    final Number dataValue = dataset.getValue(row, column);
    if (dataValue == null) {
      return;
    }

    final double value = dataValue.doubleValue();
    final Comparable group = this.seriesToGroupMap.getGroup(dataset
        .getRowKey(row));
    final PlotOrientation orientation = plot.getOrientation();
    final double barW0 = this.calculateBarW0(plot, orientation, dataArea,
        domainAxis, state, row, column);

    // TODO : START : Code to position subcategory labels under bars

    final double y0 = domainAxis.getCategoryStart(column, this
        .getColumnCount(), dataArea, plot.getDomainAxisEdge());
    final double y1 = domainAxis.getCategoryEnd(column, this
        .getColumnCount(), dataArea, plot.getDomainAxisEdge());

    final int subCategoryCount = this.seriesToGroupMap.getGroupCount() - 1;
    final float height = (float) ((y1 - y0) / subCategoryCount);

    final int subCategory = this.seriesToGroupMap
        .getGroupIndex(this.seriesToGroupMap.getGroup(plot.getDataset()
            .getRowKey(row)));
    final float yy = (float) (y0 + (subCategory - 1) * height) + height / 2;

    // TODO : END Code to position subcategory labels under bars

    double positiveBase = 0.0;
    double negativeBase = 0.0;

    if (dataset instanceof GroupToBarBaseCalculator) {
      final GroupToBarBaseCalculator calc = (GroupToBarBaseCalculator) dataset;

      positiveBase = calc.getPositiveBaseByGroup(row, column);
      negativeBase = calc.getNegativeBaseByGroup(row, column);
    } else { // perform dumb n^2 calculation

      for (int i = 0; i < row; i++) {
        if (group.equals(this.seriesToGroupMap.getGroup(dataset
            .getRowKey(i)))) {
          final Number v = dataset.getValue(i, column);
          if (v != null) {
            final double d = v.doubleValue();
            if (d > 0) {
              positiveBase = positiveBase + d;
            } else {
              negativeBase = negativeBase + d;
            }
          }
        }
      }
    }

    double translatedBase;
    double translatedValue;
    final RectangleEdge location = plot.getRangeAxisEdge();
    if (value > 0.0) {
      translatedBase = rangeAxis.valueToJava2D(positiveBase, dataArea,
          location);
      translatedValue = rangeAxis.valueToJava2D(positiveBase + value,
          dataArea, location);
    } else {
      translatedBase = rangeAxis.valueToJava2D(negativeBase, dataArea,
          location);
      translatedValue = rangeAxis.valueToJava2D(negativeBase + value,
          dataArea, location);
    }
    final double barL0 = Math.min(translatedBase, translatedValue);
    final double barLength = Math.max(Math.abs(translatedValue
        - translatedBase), this.getMinimumBarLength());

    Rectangle bar = null;
    if (orientation == PlotOrientation.HORIZONTAL) {

      final double barY = (this.getColumnCount() == 1)
          && this.placeSubcategoryLabelsAtBarLevel ? yy : barW0;
      bar = RectangleUtil.Double(barL0, barY, barLength, state
          .getBarWidth());
    } else {
      bar = RectangleUtil.Double(barW0, barL0, state.getBarWidth(),
          barLength);
    }
    if (this.treatNullColorsAsTransparent) {
      final Color itemPaint = this.getItemPaintNoCheck(row);
      if (itemPaint == null) {
        gc.setAlpha(0); // absolutely transparent
      } else {
        gc.setBackground(itemPaint);
        gc.setAlpha(0xFF); // absolutely opaque
      }
    } else {
      final Color itemPaint = this.getItemPaint(row, column);
      gc.setBackground(itemPaint);
    }

    // TODO : Gradient paint transformer
    // if (getGradientPaintTransformer() != null
    // && itemPaint instanceof GradientPaint) {
    // GradientPaint gp = (GradientPaint) itemPaint;
    // itemPaint = getGradientPaintTransformer().transform(gp, bar);
    // }
    gc.fillRectangle(bar);
    this.rowColumn2Bar.put(new Point(row, column), bar);
    if (this.isDrawBarOutline()
        && (state.getBarWidth() > BAR_OUTLINE_WIDTH_THRESHOLD)) {
      this.getItemStroke(row, column).set(gc);
      gc.setForeground(this.getItemOutlinePaint(row, column));
      gc.drawRectangle(bar);
    }

    final CategoryItemLabelGenerator generator = this
        .getItemLabelGenerator(row, column);
    if ((generator != null) && this.isItemLabelVisible(row, column)) {
      this.drawItemLabel(gc, dataset, row, column, plot, generator, bar,
          (value < 0.0));
    }

    // collect entity and tool tip information...
    if (state.getInfo() != null) {
      final EntityCollection entities = state.getEntityCollection();
      if (entities != null) {
        String tip = null;
        final CategoryToolTipGenerator tipster = this
            .getToolTipGenerator(row, column);
        if (tipster != null) {
          tip = tipster.generateToolTip(dataset, row, column);
        }
        String url = null;
        if (this.getItemURLGenerator(row, column) != null) {
          url = this.getItemURLGenerator(row, column).generateURL(
              dataset, row, column);
        }
        final Region reg = new Region();
        reg.add(bar);
        final CategoryItemEntity entity = new CategoryItemEntity(reg,
            tip, url, dataset, row, dataset.getColumnKey(column),
            column);
        entities.add(entity);
      }
    }

  }

  /**
   * Tests this renderer for equality with an arbitrary object.
   *
   * @param obj
   *            the object (<code>null</code> permitted).
   *
   * @return A boolean.
   */
  public boolean equals(final Object obj) {
    if (obj == this) {
      return true;
    }
    if ((obj instanceof GroupedStackedBarRenderer) && super.equals(obj)) {
      final GroupedStackedBarRenderer r = (GroupedStackedBarRenderer) obj;
      if (!r.seriesToGroupMap.equals(this.seriesToGroupMap)) {
        return false;
      }
      return true;
    }
    return false;
  }

  /**
   * Returns the range of values the renderer requires to display all the
   * items from the specified dataset.
   *
   * @param dataset
   *            the dataset (<code>null</code> permitted).
   *
   * @return The range (or <code>null</code> if the dataset is
   *         <code>null</code> or empty).
   */
  public Range findRangeBounds(final CategoryDataset dataset) {
    final Range r = DatasetUtilities.findStackedRangeBounds(dataset,
        this.seriesToGroupMap);
    return r;
  }

  public Point getRowColumnAtPoint(final CategoryPlot plot, final Point point) {
    if (point == null) {
      return null;
    }

    for (final Iterator iter = this.rowColumn2Bar.keySet().iterator(); iter
        .hasNext();) {
      final Point rowColumn = (Point) iter.next();
      final Rectangle bar = (Rectangle) this.rowColumn2Bar.get(rowColumn);
      if (bar.contains(point)) {
        return rowColumn;
      }
    }

    return null;
  }

  public Comparable getSubcategoryAtPoint(final CategoryPlot plot,
      final Point point) {
    if (point == null) {
      return null;
    }

    for (final Iterator iter = this.rowColumn2Bar.keySet().iterator(); iter
        .hasNext();) {
      final Point rowColumn = (Point) iter.next();
      final Rectangle bar = (Rectangle) this.rowColumn2Bar.get(rowColumn);
      if (bar.contains(point)) {
        return this.seriesToGroupMap.getGroup(plot.getDataset()
            .getRowKey(rowColumn.x));
      }
    }

    return null;
  }

  /**
   * Updates the map used to assign each series to a group.
   *
   * @param map
   *            the map (<code>null</code> not permitted).
   */
  public void setSeriesToGroupMap(final KeyToGroupMap map) {
    if (map == null) {
      throw new IllegalArgumentException("Null 'map' argument.");
    }
    this.seriesToGroupMap = map;
    this.notifyListeners(new RendererChangeEvent(this));
  }

}
TOP

Related Classes of com.positive.charts.renderer.category.GroupedStackedBarRenderer

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.