/**
*
* The MIT License
*
* Copyright (c) 2011 the original author or authors.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.googlecode.charts4j;
import static com.googlecode.charts4j.Color.BLACK;
import static com.googlecode.charts4j.collect.Preconditions.checkArgument;
import static com.googlecode.charts4j.collect.Preconditions.checkContentsNotNull;
import java.util.List;
import com.googlecode.charts4j.BarChartPlot.BarColor;
import com.googlecode.charts4j.collect.ImmutableList;
import com.googlecode.charts4j.collect.Lists;
import com.googlecode.charts4j.parameters.ChartType;
import com.googlecode.charts4j.parameters.FillAreaType;
/**
* Bar chart constructed with the {@link GCharts} static factory class.
*
* Bar chart class that supports vertical and horizontal bar charts. Bars from
* different data series can either be stacked or side-by-side.
*
* @author Julien Chastang (julien.c.chastang at gmail dot com)
*
* @see BarChartPlot
* @see GCharts
*/
public class BarChart extends AbstractAxisChart {
/** Automatically resize bars to fit space. **/
public static final int AUTO_RESIZE = 0;
/** Default space within groups of bars. **/
private static final int DEFAULT_SPACE_WINTHIN_GROUPS_OF_BARS = 4;
/** Default space between groups of bars. **/
private static final int DEFAULT_SPACE_BETWEEN_GROUPS_OF_BARS = 8;
/** Default bar width. **/
private static final int DEFAULT_BAR_WIDTH = 23;
/** Are the bars stacked or side-by-side. **/
private boolean dataStacked = false;
/** Are the bars horizontal or vertical. **/
private boolean horizontal = false;
/** The bar width. **/
private int barWidth = DEFAULT_BAR_WIDTH;
/** Space between groups of bars. **/
private int spaceBetweenGroupsOfBars = DEFAULT_SPACE_BETWEEN_GROUPS_OF_BARS;
/** Space within groups. **/
private int spaceWithinGroupsOfBars = DEFAULT_SPACE_WINTHIN_GROUPS_OF_BARS;
/** List of bars to be plotted. **/
private final ImmutableList<Plot> barChartPlots;
/**
* @see GCharts#newBarChart(List)
*/
BarChart(final ImmutableList<? extends Plot> barChartPlots) {
super();
checkContentsNotNull(barChartPlots, "barChartPlots is null or contains a null plot");
this.barChartPlots = Lists.copyOf(barChartPlots);
}
/**
* Are the bar charts stacked, or side-by-side.
*
* @param dataStacked
* If true, data from different bar chart plot will be stacked
* instead of side-by-side.
*/
public final void setDataStacked(final boolean dataStacked) {
this.dataStacked = dataStacked;
}
/**
* Is the bar chart bars horizontal or vertical.
*
* @param horizontal
* If true, the bar chart will be horizontal instead of vertical.
*/
public final void setHorizontal(final boolean horizontal) {
this.horizontal = horizontal;
}
/**
* {@inheritDoc}
*/
@Override
protected void prepareData() {
super.prepareData();
// Logic to make sure things stay in step.
boolean hasLegend = false;
boolean hasColor = false;
boolean hasZeroLine = false;
// Logic to make sure things stay in step continued.
for (Plot plot : barChartPlots) {
final PlotImpl series = (PlotImpl) plot;
hasLegend |= (series.getLegend() != null);
hasColor |= (series.getColor() != null || !series.getBarColors().isEmpty());
hasZeroLine |= (series.getZeroLine() != 0);
}
// Logic to make sure things stay in step continued.
int lineCount = 0;
for (Plot p : barChartPlots) {
final PlotImpl plot = (PlotImpl) p;
parameterManager.addData(plot.getData());
if (hasLegend) {
parameterManager.addLegend(plot.getLegend() != null ? plot.getLegend() : " ");
}
//Color logic is complicated b/c individual bars can be colored.
if (hasColor) {
if (plot.getBarColors().isEmpty()) {
parameterManager.addColor(plot.getColor() != null ? plot.getColor() : BLACK);
} else {
final List<ImmutableList<Color>> colors = Lists.newArrayList();
final List<Color> colorList = Lists.newLinkedList();
//Initialize color list.
for (int i = 0; i < plot.getData().getSize(); i++) {
colorList.add(plot.getColor() != null ? plot.getColor() : BLACK);
}
//Now set the color on inidvidual bars.
for (BarColor bColor : plot.getBarColors()) {
if (bColor.getIndex() < plot.getData().getSize()) {
colorList.set(bColor.getIndex(), bColor.getColor());
}
}
colors.add(Lists.copyOf(colorList));
parameterManager.addColors(Lists.copyOf(colors));
}
}
if (hasZeroLine) {
// Remember zero line has to be between 0 and 1
parameterManager.setBarChartZeroLineParameter(plot.getZeroLine() / Data.MAX_VALUE);
}
if (plot.getDataLine() != null) {
parameterManager.addLineStyleMarker(plot.getDataLine().getColor(), lineCount, 0, plot.getDataLine().getSize(), plot.getDataLine().getPriority());
}
for (Marker m : plot.getMarkers()) {
parameterManager.addMarkers(m, lineCount);
}
for (MarkedPoints mp : plot.getMarkedPointsList()) {
parameterManager.addMarker(mp.getMarker(), lineCount, mp.getStartIndex(), mp.getEndIndex(), mp.getN());
}
if (plot.getFillAreaColor() != null) {
parameterManager.addFillAreaMarker(FillAreaType.FULL, plot.getFillAreaColor(), lineCount, 0);
}
lineCount++;
}
parameterManager.setBarChartWidthAndSpacingParameter(barWidth, spaceWithinGroupsOfBars, spaceBetweenGroupsOfBars);
if (horizontal && dataStacked) {
parameterManager.setChartTypeParameter(ChartType.HORIZONTAL_STACKED_BAR_CHART);
}
if (!horizontal && dataStacked) {
parameterManager.setChartTypeParameter(ChartType.VERTICAL_STACKED_BAR_CHART);
}
if (!horizontal && !dataStacked) {
parameterManager.setChartTypeParameter(ChartType.VERTICAL_GROUPED_BAR_CHART);
}
if (horizontal && !dataStacked) {
parameterManager.setChartTypeParameter(ChartType.HORIZONTAL_GROUPED_BAR_CHART);
}
}
/**
* Set the bar chart width.
*
* @param barWidth
* Bar width in pixels. If not set defaults to 23. Must be > 0.
* To automatically resize bars to fit space use AUTO_RESIZE.
* Example <code>chart.setBarWidth(BarChart.AUTO_RESIZE);</code>
*/
public final void setBarWidth(final int barWidth) {
checkArgument(barWidth > -1, "barWidth must be > 0");
this.barWidth = barWidth;
}
/**
* Set the space between groups of bars.
*
* @param spaceBetweenGroupsOfBars
* The space between groups of bars. Bars within a group have
* half the specified spacing. If not set default to 8. Must be >=
* 0.
*/
public final void setSpaceBetweenGroupsOfBars(final int spaceBetweenGroupsOfBars) {
checkArgument(spaceWithinGroupsOfBars >= 0, "spaceWithinGroupsOfBars must be >= 0");
this.spaceBetweenGroupsOfBars = spaceBetweenGroupsOfBars;
}
/**
* Set the space within groups of bars.
*
* @param spaceWithinGroupsOfBars
* The space within groups of bars. Bars within a group have half
* the specified spacing. If not set default to 4. Must be >= 0.
*/
public final void setSpaceWithinGroupsOfBars(final int spaceWithinGroupsOfBars) {
checkArgument(spaceWithinGroupsOfBars >= 0, "spaceWithinGroupsOfBars must be >= 0");
this.spaceWithinGroupsOfBars = spaceWithinGroupsOfBars;
}
}