org.jCharts.axisChart.AxisChart Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of jcharts-gae-compatible Show documentation
Show all versions of jcharts-gae-compatible Show documentation
Library allowing to build simple charts on GAE
The newest version!
/***********************************************************************************************
* File Info: $Id: AxisChart.java,v 1.38 2003/04/19 01:40:33 nathaniel_auvil Exp $
* Copyright (C) 2002
* Author: Nathaniel G. Auvil
* Contributor(s):
*
* Copyright 2002 (C) Nathaniel G. Auvil. All Rights Reserved.
*
* Redistribution and use of this software and associated documentation ("Software"), with or
* without modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain copyright statements and notices.
* Redistributions must also contain a copy of this document.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* 3. The name "jCharts" or "Nathaniel G. Auvil" must not be used to endorse or promote
* products derived from this Software without prior written permission of Nathaniel G.
* Auvil. For written permission, please contact [email protected]
*
* 4. Products derived from this Software may not be called "jCharts" nor may "jCharts" appear
* in their names without prior written permission of Nathaniel G. Auvil. jCharts is a
* registered trademark of Nathaniel G. Auvil.
*
* 5. Due credit should be given to the jCharts Project (http://jcharts.sourceforge.net/).
*
* THIS SOFTWARE IS PROVIDED BY Nathaniel G. Auvil AND CONTRIBUTORS ``AS IS'' AND ANY
* EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
* jCharts OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
* IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE
************************************************************************************************/
package org.jCharts.axisChart;
import org.jCharts.Chart;
import org.jCharts.axisChart.axis.*;
import org.jCharts.axisChart.axis.scale.*;
import org.jCharts.chartText.NumericTagGroup;
import org.jCharts.chartText.TextTagGroup;
import org.jCharts.chartData.interfaces.*;
import org.jCharts.chartData.processors.AxisChartDataProcessor;
import org.jCharts.chartData.ChartDataException;
import org.jCharts.imageMap.ImageMap;
import org.jCharts.properties.*;
import org.jCharts.test.*;
import org.jCharts.types.ChartType;
import com.google.code.appengine.awt.*;
import com.google.code.appengine.awt.font.FontRenderContext;
import com.google.code.appengine.awt.geom.Rectangle2D;
import java.util.Iterator;
/****************************************************************************************************
* This Class is used to create all axis chart types. This class knows how to render charts based on
* the ChartType specified in on the iAxisChartDataSet.
*
*****************************************************************************************************/
public class AxisChart extends Chart implements HTMLChartTestable
{
protected XAxis xAxis;
protected YAxis yAxis;
protected AxisProperties axisProperties;
private IAxisDataSeries iAxisDataSeries;
/**************************************************************************************************
* Constructor
*
* @param iAxisDataSeries
* @param chartProperties
* @param axisProperties
* @param legendProperties if no legend is desired, pass NULL
* @param pixelWidth
* @param pixelHeight
***************************************************************************************************/
public AxisChart( IAxisDataSeries iAxisDataSeries,
ChartProperties chartProperties,
AxisProperties axisProperties,
LegendProperties legendProperties,
int pixelWidth,
int pixelHeight )
{
super( legendProperties, chartProperties, pixelWidth, pixelHeight );
this.axisProperties = axisProperties;
this.iAxisDataSeries = iAxisDataSeries;
}
/*************************************************************************************************
*
* @return IAxisDataSeries
*************************************************************************************************/
public IAxisDataSeries getIAxisDataSeries()
{
return this.iAxisDataSeries;
}
/********************************************************************************************
* ScatterPlots create a subclass of AxisChartDataProcessor so we need this method so we can
* overload it.
*
* @return AxisChartDataProcessor
********************************************************************************************/
public AxisChartDataProcessor createAxisChartDataProcessor()
{
return new AxisChartDataProcessor();
}
/************************************************************************************************
* Once we determine which axis is the data axis, the logic to set it up is the same whether it
* is a horizontal or vertical plot.
*
* @param dataAxisProperties
* @param axisChartDataProcessor
* @param fontRenderContext
* @return NumericTagGroup need to set this on the right axis
************************************************************************************************/
protected NumericTagGroup setupDataAxisProperties( Axis axis,
DataAxisProperties dataAxisProperties,
AxisChartDataProcessor axisChartDataProcessor,
FontRenderContext fontRenderContext )
{
if( dataAxisProperties.getScaleCalculator() == null )
{
ScaleCalculator s;
if( dataAxisProperties.hasUserDefinedScale() )
{
s = new UserDefinedScaleCalculator( dataAxisProperties.getUserDefinedMinimumValue(), dataAxisProperties.getUserDefinedIncrement() );
}
else
{
s = new AutomaticScaleCalculator();
s.setMaxValue( axisChartDataProcessor.getMaxValue() );
s.setMinValue( axisChartDataProcessor.getMinValue() );
}
axis.setScaleCalculator( s );
}
else
{
axis.setScaleCalculator( dataAxisProperties.getScaleCalculator() );
axis.getScaleCalculator().setMaxValue( axisChartDataProcessor.getMaxValue() );
axis.getScaleCalculator().setMinValue( axisChartDataProcessor.getMinValue() );
}
axis.getScaleCalculator().setRoundingPowerOfTen( dataAxisProperties.getRoundToNearest() );
axis.getScaleCalculator().setNumberOfScaleItems( dataAxisProperties.getNumItems() );
axis.getScaleCalculator().computeScaleValues();
// if( dataAxisProperties.showAxisLabels() )
{
//TODO what if they do not want to display axis labels?
//todo we still need to know how to size the axis
NumericTagGroup numericTagGroup = new NumericTagGroup( dataAxisProperties.getScaleChartFont(),
fontRenderContext,
dataAxisProperties.useDollarSigns(),
dataAxisProperties.usePercentSigns(),
dataAxisProperties.useCommas(),
dataAxisProperties.getRoundToNearest() );
numericTagGroup.createAxisScaleLabels( axis.getScaleCalculator() );
return numericTagGroup;
}
/*
else
{
return null;
}
*/
}
/***************************************************************************************
*
*
* @param axisChartDataProcessor
* @param fontRenderContext
**************************************************************************************/
protected void setupAxis( AxisChartDataProcessor axisChartDataProcessor, FontRenderContext fontRenderContext ) throws ChartDataException
{
IDataSeries iDataSeries= (IDataSeries) this.getIAxisDataSeries();
if( this.axisProperties.isPlotHorizontal() )
{
//---X AXIS---------------------------------------------------------------------------
DataAxisProperties dataAxisProperties = (DataAxisProperties) this.getAxisProperties().getXAxisProperties();
this.xAxis = new XAxis( this, dataAxisProperties.getNumItems() );
NumericTagGroup numericTagGroup= setupDataAxisProperties( this.xAxis, dataAxisProperties, axisChartDataProcessor, fontRenderContext );
this.xAxis.setAxisLabelsGroup( numericTagGroup );
//---Y AXIS---------------------------------------------------------------------------
AxisTypeProperties axisTypeProperties = this.getAxisProperties().getYAxisProperties();
this.yAxis = new YAxis( this, axisChartDataProcessor.getNumberOfElementsInADataSet() );
if( axisTypeProperties.showAxisLabels() )
{
TextTagGroup textTagGroup = new TextTagGroup( axisTypeProperties.getScaleChartFont(), fontRenderContext );
//LOOP
for( int i = 0; i < iDataSeries.getNumberOfAxisLabels(); i++ )
{
if( iDataSeries.getAxisLabel( i ) == null )
{
throw new ChartDataException( "None of the axis labels can be NULL." );
}
textTagGroup.addLabel( iDataSeries.getAxisLabel( i ) );
}
this.yAxis.setAxisLabelsGroup( textTagGroup );
}
}
else
{
//---X AXIS---------------------------------------------------------------------------
AxisTypeProperties axisTypeProperties = this.getAxisProperties().getXAxisProperties();
this.xAxis = new XAxis( this, axisChartDataProcessor.getNumberOfElementsInADataSet() );
if( axisTypeProperties.showAxisLabels() )
{
TextTagGroup textTagGroup = new TextTagGroup( axisTypeProperties.getScaleChartFont(), fontRenderContext );
//LOOP
for( int i = 0; i < iDataSeries.getNumberOfAxisLabels(); i++ )
{
if( iDataSeries.getAxisLabel( i ) == null )
{
throw new ChartDataException( "None of the axis labels can be NULL." );
}
textTagGroup.addLabel( iDataSeries.getAxisLabel( i ) );
}
this.xAxis.setAxisLabelsGroup( textTagGroup );
}
//---Y AXIS---------------------------------------------------------------------------
DataAxisProperties dataAxisProperties = ( DataAxisProperties ) this.getAxisProperties().getYAxisProperties();
this.yAxis = new YAxis( this, dataAxisProperties.getNumItems() );
NumericTagGroup numericTagGroup= setupDataAxisProperties( this.yAxis, dataAxisProperties, axisChartDataProcessor, fontRenderContext );
this.yAxis.setAxisLabelsGroup( numericTagGroup );
}
//---if yAxisTitle is null, do not show title
this.yAxis.computeMinimumWidthNeeded( iDataSeries.getYAxisTitle() );
this.xAxis.computeMinimumHeightNeeded( iDataSeries.getXAxisTitle() );
}
/***********************************************************************************************
* Finalizes the size of both Axis and sets the origin position
*
* @param xAxisWidth
* @param yAxisHeight
* @param chartTitleHeight
**********************************************************************************************/
private void sizeAndPositionAxis( float xAxisWidth, float yAxisHeight, float chartTitleHeight )
{
//---SUBTRACT space for axis titles, labels, ticks...
xAxisWidth -= this.yAxis.getMinimumWidthNeeded();
yAxisHeight -= this.xAxis.getMinimumHeightNeeded();
//---SET THE PIXEL LENGTH OF THE AXIS
this.xAxis.setPixelLength( xAxisWidth );
if( axisProperties.getYAxisProperties().showAxisLabels() )
{
this.yAxis.setPixelLength( yAxisHeight - this.yAxis.getAxisLabelsGroup().getTallestLabel() / 2 );
}
else
{
this.yAxis.setPixelLength( yAxisHeight );
}
if( this.getLegend() != null )
{
//---SET THE ORIGIN COORDINATES
if( ( this.getLegend().getLegendProperties().getPlacement() == LegendAreaProperties.RIGHT )
|| ( this.getLegend().getLegendProperties().getPlacement() == LegendAreaProperties.BOTTOM ) )
{
this.xAxis.setOrigin( this.yAxis.getMinimumWidthNeeded() + super.getChartProperties().getEdgePadding() );
this.yAxis.setOrigin( yAxisHeight + super.getChartProperties().getEdgePadding() + chartTitleHeight );
}
else if( this.getLegend().getLegendProperties().getPlacement() == LegendAreaProperties.LEFT )
//---else, LegendAreaProperties.LEFT, OR LegendAreaProperties.TOP
{
this.xAxis.setOrigin( super.getImageWidth() - xAxisWidth - super.getChartProperties().getEdgePadding() );
this.yAxis.setOrigin( yAxisHeight + super.getChartProperties().getEdgePadding() + chartTitleHeight );
}
else if( this.getLegend().getLegendProperties().getPlacement() == LegendAreaProperties.TOP )
{
this.xAxis.setOrigin( this.yAxis.getMinimumWidthNeeded() + super.getChartProperties().getEdgePadding() );
this.yAxis.setOrigin( super.getImageHeight() - super.getChartProperties().getEdgePadding() - this.xAxis.getMinimumHeightNeeded() );
}
}
else
{
this.xAxis.setOrigin( this.yAxis.getMinimumWidthNeeded() + super.getChartProperties().getEdgePadding() );
this.yAxis.setOrigin( yAxisHeight + super.getChartProperties().getEdgePadding() + chartTitleHeight );
}
}
/******************************************************************************************
*
*****************************************************************************************/
protected void deriveAxisValues()
{
this.xAxis.computeLabelFilter();
this.xAxis.computeShouldTickStartAtYAxis( this.iAxisDataSeries, this.axisProperties.getXAxisProperties() );
if( this.axisProperties.isPlotHorizontal() )
{
//DataAxisProperties dataAxisProperties = (DataAxisProperties) this.axisProperties.getXAxisProperties();
//LabelAxisProperties labelAxisProperties= (LabelAxisProperties) this.axisProperties.getYAxisProperties();
//---Determine how many labels will fit on the x-axis
this.xAxis.computeScalePixelWidthDataAxis();
this.yAxis.computeScalePixelWidth();
this.xAxis.computeOneUnitPixelSize( this.xAxis.getScalePixelWidth(), this.xAxis.getScaleCalculator().getIncrement() );
//---we ADD to the origin position when doing x-axis
float zeroLineCoordinate = ( float ) ( this.xAxis.getOrigin() + ( this.xAxis.getScalePixelWidth() * ( -this.xAxis.getScaleCalculator().getMinValue() ) ) / this.xAxis.getScaleCalculator().getIncrement() );
this.xAxis.setZeroLineCoordinate( zeroLineCoordinate );
}
else
{
//DataAxisProperties dataAxisProperties = ( DataAxisProperties ) this.axisProperties.getYAxisProperties();
this.xAxis.computeScalePixelWidth();
this.yAxis.computeScalePixelWidthDataAxis();
this.yAxis.computeOneUnitPixelSize( this.yAxis.getScalePixelWidth(), this.yAxis.getScaleCalculator().getIncrement() );
//---we SUBTRACT to the origin position when doing y-axis
float zeroLineCoordinate = ( float ) ( this.yAxis.getOrigin() - ( this.yAxis.getScalePixelWidth() * ( -this.yAxis.getScaleCalculator().getMinValue() ) ) / this.yAxis.getScaleCalculator().getIncrement() );
this.yAxis.setZeroLineCoordinate( zeroLineCoordinate );
}
this.xAxis.computeTickStart();
}
/********************************************************************************************
* Implement the method to render the Axis based chart
*
* @throws ChartDataException
* @throws PropertyException there are several validations done to aid in development of the
* charts.
********************************************************************************************/
protected void renderChart() throws ChartDataException, PropertyException
{
if( super.getChartProperties().validate() )
{
this.iAxisDataSeries.validate();
}
//---this is not an optional validation
this.validateHorizontalPlot();
Graphics2D g2d = super.getGraphics2D();
g2d.setRenderingHint( RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON );
FontRenderContext fontRenderContext = g2d.getFontRenderContext();
//---cache calcs used more than once
float edgePaddingTimesTwo = super.getChartProperties().getEdgePadding() * 2;
//---start off with total image width and we will subtract components from that.
float xAxisWidth = super.getImageWidth() - edgePaddingTimesTwo;
float yAxisHeight = super.getImageHeight() - edgePaddingTimesTwo;
//---render the TITLE. If no title, this will return zero.
float chartTitleHeight = super.renderChartTitle( this.getIAxisDataSeries().getChartTitle(), fontRenderContext );
yAxisHeight -= chartTitleHeight;
//---if there is a legend...
if( super.getLegend() != null )
{
//---PROCESS the size needed for drawing the legend.
super.getLegend().computeLegendXY( this.iAxisDataSeries, chartTitleHeight );
if( ( super.getLegend().getLegendProperties().getPlacement() == LegendAreaProperties.RIGHT )
|| ( super.getLegend().getLegendProperties().getPlacement() == LegendAreaProperties.LEFT ) )
{
xAxisWidth -= super.getLegend().getLegendProperties().getChartPadding();
xAxisWidth -= super.getLegend().getWidth();
}
else //LegendAreaProperties.BOTTOM, OR LegendAreaProperties.TOP
{
yAxisHeight -= this.getLegend().getLegendProperties().getChartPadding();
yAxisHeight -= this.getLegend().getHeight();
}
super.getLegend().render();
}
AxisChartDataProcessor axisChartDataProcessor = this.createAxisChartDataProcessor();
axisChartDataProcessor.processData( this, fontRenderContext );
this.setupAxis( axisChartDataProcessor, fontRenderContext );
this.sizeAndPositionAxis( xAxisWidth, yAxisHeight, chartTitleHeight );
this.deriveAxisValues();
//---PAINT THE BACKGROUND OF AXIS
if( this.getAxisProperties().getBackgroundPaint() != null )
{
Rectangle2D.Float rectangle = new Rectangle2D.Float( this.xAxis.getOrigin() + 1,
this.yAxis.getOrigin() - this.yAxis.getPixelLength(),
this.xAxis.getPixelLength(),
this.yAxis.getPixelLength() );
g2d.setPaint( this.axisProperties.getBackgroundPaint() );
g2d.fill( rectangle );
}
this.yAxis.render( g2d, this.getAxisProperties(), iAxisDataSeries.getYAxisTitle() );
this.xAxis.render( g2d, this.getAxisProperties(), iAxisDataSeries.getXAxisTitle() );
//---SCALE CLIPPING REGION
//---if the user defined the scale, chart may be off the 'screen' so set a clipping region so only draw in the chart.
Rectangle2D.Float rectangle = new Rectangle2D.Float( this.getXAxis().getOrigin(),
this.getYAxis().getOrigin() - this.getYAxis().getPixelLength() + 1,
this.xAxis.getPixelLength() + 1,
this.yAxis.getPixelLength() - 2 );
g2d.setClip( rectangle );
//---IMAGE MAP setup
//---if we are saving all the coordinates for an ImageMap, create the ImageMap Object as we
//--- know how many area elements there are.
if( super.getGenerateImageMapFlag() )
{
//---pass the size to try and avoid having the expense of resizing the ArrayList
ImageMap imageMap = new ImageMap( iAxisDataSeries.size() * iAxisDataSeries.getTotalNumberOfDataSets() );
super.setImageMap( imageMap );
}
//---draw the charts over the axis...
overlayCharts();
}
/********************************************************************************************
* Draws the charts over the axis. We have to render in a specific order so combo charts
* get drawn correctly
*
* @throws PropertyException
******************************************************************************************/
protected void overlayCharts() throws PropertyException, ChartDataException
{
IAxisPlotDataSet iAxisPlotDataSet;
iAxisPlotDataSet = this.iAxisDataSeries.getIAxisPlotDataSet( ChartType.AREA_STACKED );
if( iAxisPlotDataSet != null )
{
StackedAreaChart.render( this, ( IAxisChartDataSet ) iAxisPlotDataSet );
}
iAxisPlotDataSet = this.iAxisDataSeries.getIAxisPlotDataSet( ChartType.AREA );
if( iAxisPlotDataSet != null )
{
AreaChart.render( this, ( IAxisChartDataSet ) iAxisPlotDataSet );
}
iAxisPlotDataSet = this.iAxisDataSeries.getIAxisPlotDataSet( ChartType.BAR );
if( iAxisPlotDataSet != null )
{
BarChart.render( this, ( IAxisChartDataSet ) iAxisPlotDataSet );
}
iAxisPlotDataSet = this.iAxisDataSeries.getIAxisPlotDataSet( ChartType.BAR_STACKED );
if( iAxisPlotDataSet != null )
{
StackedBarChart.render( this, ( IAxisChartDataSet ) iAxisPlotDataSet );
}
iAxisPlotDataSet = this.iAxisDataSeries.getIAxisPlotDataSet( ChartType.BAR_CLUSTERED );
if( iAxisPlotDataSet != null )
{
ClusteredBarChart.render( this, ( IAxisChartDataSet ) iAxisPlotDataSet );
}
iAxisPlotDataSet = this.iAxisDataSeries.getIAxisPlotDataSet( ChartType.STOCK );
if( iAxisPlotDataSet != null )
{
StockChart.render( this, ( IStockChartDataSet ) iAxisPlotDataSet );
}
iAxisPlotDataSet = this.iAxisDataSeries.getIAxisPlotDataSet( ChartType.LINE );
if( iAxisPlotDataSet != null )
{
LineChart.render( this, ( IAxisChartDataSet ) iAxisPlotDataSet );
}
iAxisPlotDataSet = this.iAxisDataSeries.getIAxisPlotDataSet( ChartType.POINT );
if( iAxisPlotDataSet != null )
{
PointChart.render( this, ( IAxisChartDataSet ) iAxisPlotDataSet );
}
}
/**********************************************************************************************
* Currently, we only support the bar chart types being horizontal, and you can not have a
* horizontally plotted bar chart in a combo chart.
*
* @throws PropertyException
**********************************************************************************************/
private void validateHorizontalPlot() throws PropertyException
{
if( axisProperties.isPlotHorizontal() )
{
//---if there is only one data set, there is no need to do any validations.
if( this.iAxisDataSeries.size() > 1 )
{
throw new PropertyException( "You can not have a combo chart on a horizontal plot." );
}
if( !this.allowHorizontalPlot() )
{
throw new PropertyException( "Horizontal plots are only supported in the Bar, Stacked Bar, and Clustered Bar Chart Types." );
}
}
}
/******************************************************************************************
* We only allow horizontal plots for the Bar Chart types in this release.
*
* @return boolean
******************************************************************************************/
private boolean allowHorizontalPlot()
{
if( this.iAxisDataSeries.getIAxisPlotDataSet( ChartType.BAR ) != null )
{
return true;
}
if( this.iAxisDataSeries.getIAxisPlotDataSet( ChartType.BAR_STACKED ) != null )
{
return true;
}
if( this.iAxisDataSeries.getIAxisPlotDataSet( ChartType.BAR_CLUSTERED ) != null )
{
return true;
}
return false;
}
/**************************************************************************************************
*
***************************************************************************************************/
public AxisProperties getAxisProperties()
{
return this.axisProperties;
}
/**************************************************************************************************
*
***************************************************************************************************/
public XAxis getXAxis()
{
return this.xAxis;
}
/**************************************************************************************************
*
***************************************************************************************************/
public YAxis getYAxis()
{
return this.yAxis;
}
/**********************************************************************************************
* Enables the testing routines to display the contents of this Object. Override Chart
* implementation as PieCharts use AreaProperties directly rather than a child.
*
* @param htmlGenerator
* @param imageFileName
* @param imageMap if this is NULL we are not creating image map data in html
**********************************************************************************************/
public void toHTML( HTMLGenerator htmlGenerator, String imageFileName, ImageMap imageMap )
{
htmlGenerator.chartTableStart( this.getClass().getName(), imageFileName, imageMap );
if( this.iAxisDataSeries instanceof HTMLTestable )
{
( ( HTMLTestable ) this.iAxisDataSeries ).toHTML( htmlGenerator );
}
//---AxisProperties
htmlGenerator.chartTableRowStart();
this.axisProperties.toHTML( htmlGenerator );
htmlGenerator.chartTableRowEnd();
//---XAxis
htmlGenerator.chartTableRowStart();
this.xAxis.toHTML( htmlGenerator );
htmlGenerator.chartTableRowEnd();
//---YAxis
htmlGenerator.chartTableRowStart();
this.yAxis.toHTML( htmlGenerator );
htmlGenerator.chartTableRowEnd();
//---ChartProperties
htmlGenerator.chartTableRowStart();
super.getChartProperties().toHTML( htmlGenerator );
htmlGenerator.chartTableRowEnd();
if( super.getLegend() != null )
{
htmlGenerator.chartTableRowStart();
this.getLegend().toHTML( htmlGenerator );
htmlGenerator.chartTableRowEnd();
}
htmlGenerator.chartTableEnd();
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy