/*
* Copyright 2010 Tom Castle (www.tc33.org)
* Licensed under GNU Lesser General Public License
*
* This file is part of JHeatChart - the heat maps charting api for Java.
*
* JHeatChart 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 3 of the License, or
* (at your option) any later version.
*
* JHeatChart 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 General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with JHeatChart. If not, see HeatChart class describes a chart which can display
* 3-dimensions of values - x,y and z, where x and y are the usual 2-dimensional
* axis and z is portrayed by colour intensity. Heat charts are sometimes known
* as heat maps.
*
*
* Use of this chart would typically involve 3 steps: *
getChartImage() or saveToFile(String).
* Construction of a new HeatChart instance is through its one
* constructor which takes a 2-dimensional array of doubles which
* should contain the z-values for the chart. Consider this array to be
* the grid of values which will instead be represented as colours in the chart.
*
*
* Setting of the x-values and y-values which are displayed along the
* appropriate axis is optional, and by default will simply display the values
* 0 to n-1, where n is the number of rows or columns. Otherwise, the x/y axis
* values can be set with the setXValues and setYValues
* methods. Both methods are overridden with two forms:
*
*
* The simplist way to set the axis values is to use the methods which take an * array of Object[]. This array must have the same length as the number of * columns for setXValues and same as the number of rows for setYValues. The * string representation of the objects will then be used as the axis values. * *
* This is convenient way of defining numerical values along the axis. One of * the two methods takes an interval and an offset for either the * x or y axis. These parameters supply the necessary information to describe * the values based upon the z-value indexes. The quantity of x-values and * y-values is already known from the lengths of the z-values array dimensions. * Then the offset parameters indicate what the first value will be, with the * intervals providing the increment from one column or row to the next. * *
* Consider an example: *
* double[][] zValues = new double[][]{
* {1.2, 1.3, 1.5},
* {1.0, 1.1, 1.6},
* {0.7, 0.9, 1.3}
* };
*
* double xOffset = 1.0;
* double yOffset = 0.0;
* double xInterval = 1.0;
* double yInterval = 2.0;
*
* chart.setXValues(xOffset, xInterval);
* chart.setYValues(yOffset, yInterval);
*
*
* In this example, the z-values range from 0.7 to 1.6. The x-values range * from the xOffset value 1.0 to 4.0, which is calculated as the number of x-values * multiplied by the xInterval, shifted by the xOffset of 1.0. The y-values are * calculated in the same way to give a range of values from 0.0 to 6.0. * *
* This step is optional. By default the heat chart will be generated without a * title or labels on the axis, and the colouring of the heat map will be in * grayscale. A large range of configuration options are available to customise * the chart. All customisations are available through simple accessor methods. * See the javadoc of each of the methods for more information. * *
* The generated heat chart can be obtained in two forms, using the following * methods: *
BufferedImage object that can be used in any number of ways,
* most notably it can be inserted into a Swing component, for use in a GUI
* application.
* new double[][]{
* {1.0,1.2,1.4},
* {1.2,1.3,1.5},
* {0.9,1.3,1.2},
* {0.8,1.6,1.1}
* };
* | 1.0 | *1.2 | *1.4 | *|
| 1.2 | *1.3 | *1.5 | *|
| 0.9 | *1.3 | *1.2 | *|
| 0.8 | *1.6 | *1.1 | *|
| * | |||
* x-value = x-offset + (column-index * x-interval) *
The x-interval defines the gap between each x-value and the x-offset * is applied to each value to offset them all from zero. * *
Alternatively the x-values can be set more directly with the
* The y-interval defines the gap between each y-value and the y-offset
* is applied to each value to offset them all from zero.
*
* Alternatively the y-values can be set more directly with the
*
* If the title is set to null then no title will be displayed.
*
*
* Defaults to null.
*
* @param title the chart title to set.
*/
public void setTitle(String title) {
this.title = title;
}
/**
* Returns the String that will be displayed as a description of the
* x-axis in any generated charts.
*
* @return the display label describing the x-axis.
*/
public String getXAxisLabel() {
return xAxisLabel;
}
/**
* Sets the String that will be displayed as a description of the
* x-axis in any generated charts. The label will be displayed
* horizontally central of the x-axis bar.
*
*
* If the xAxisLabel is set to null then no label will be
* displayed.
*
*
* Defaults to null.
*
* @param xAxisLabel the label to be displayed describing the x-axis.
*/
public void setXAxisLabel(String xAxisLabel) {
this.xAxisLabel = xAxisLabel;
}
/**
* Returns the String that will be displayed as a description of the
* y-axis in any generated charts.
*
* @return the display label describing the y-axis.
*/
public String getYAxisLabel() {
return yAxisLabel;
}
/**
* Sets the String that will be displayed as a description of the
* y-axis in any generated charts. The label will be displayed
* horizontally central of the y-axis bar.
*
*
* If the yAxisLabel is set to null then no label will be
* displayed.
*
*
* Defaults to null.
*
* @param yAxisLabel the label to be displayed describing the y-axis.
*/
public void setYAxisLabel(String yAxisLabel) {
this.yAxisLabel = yAxisLabel;
}
/**
* Returns the width of the margin in pixels to be left as empty space
* around the heat map element.
*
* @return the size of the margin to be left blank around the edge of the
* chart.
*/
public int getChartMargin() {
return margin;
}
/**
* Sets the width of the margin in pixels to be left as empty space around
* the heat map element. If a title is set then half the margin will be
* directly above the title and half directly below it. Where axis labels
* are set then the axis labels may sit partially in the margin.
*
*
* Defaults to 20 pixels.
*
* @param margin the new margin to be left as blank space around the heat
* map.
*/
public void setChartMargin(int margin) {
this.margin = margin;
}
/**
* Returns an object that represents the colour to be used as the
* background for the whole chart.
*
* @return the colour to be used to fill the chart background.
*/
public Color getBackgroundColour() {
return backgroundColour;
}
/**
* Sets the colour to be used on the background of the chart. A transparent
* background can be set by setting a background colour with an alpha value.
* The transparency will only be effective when the image is saved as a png
* or gif.
*
*
* Defaults to
* Defaults to Sans-Serif, BOLD, 16 pixels.
*
* @param titleFont the Font that should be used when rendering the chart
* title.
*/
public void setTitleFont(Font titleFont) {
this.titleFont = titleFont;
}
/**
* Returns the
* Defaults to
* Defaults to 2 pixels.
*
* @param axisThickness the thickness to use for the axis bars in any newly
* generated charts.
*/
public void setAxisThickness(int axisThickness) {
this.axisThickness = axisThickness;
}
/**
* Returns the colour that is set to be used for the axis bars. Both axis
* bars use the same colour.
*
* @return the colour in use for the axis bars.
*/
public Color getAxisColour() {
return axisColour;
}
/**
* Sets the colour to be used on the axis bars. Both axis bars use the same
* colour.
*
*
* Defaults to
* Defaults to Sans-Serif, PLAIN, 12 pixels.
*
* @param axisLabelsFont the font to be used to define the visual style of
* the axis labels.
*/
public void setAxisLabelsFont(Font axisLabelsFont) {
this.axisLabelsFont = axisLabelsFont;
}
/**
* Returns the current colour of the axis labels. Both labels use the same
* colour.
*
* @return the colour of the axis label text.
*/
public Color getAxisLabelColour() {
return axisLabelColour;
}
/**
* Sets the colour of the text displayed as axis labels. Both labels use
* the same colour.
*
*
* Defaults to Color.BLACK.
*
* @param axisLabelColour the colour to use for the axis label text.
*/
public void setAxisLabelColour(Color axisLabelColour) {
this.axisLabelColour = axisLabelColour;
}
/**
* Returns the font which describes the visual style of the axis values.
* The axis values are those values displayed alongside the axis bars at
* regular intervals. Both axis use the same font.
*
* @return the font in use for the axis values.
*/
public Font getAxisValuesFont() {
return axisValuesFont;
}
/**
* Sets the font which describes the visual style of the axis values. The
* axis values are those values displayed alongside the axis bars at
* regular intervals. Both axis use the same font.
*
*
* Defaults to Sans-Serif, PLAIN, 10 pixels.
*
* @param axisValuesFont the font that should be used for the axis values.
*/
public void setAxisValuesFont(Font axisValuesFont) {
this.axisValuesFont = axisValuesFont;
}
/**
* Returns the colour of the axis values as they will be painted along the
* axis bars. Both axis use the same colour.
*
* @return the colour of the values displayed along the axis bars.
*/
public Color getAxisValuesColour() {
return axisValuesColour;
}
/**
* Sets the colour to be used for the axis values as they will be painted
* along the axis bars. Both axis use the same colour.
*
*
* Defaults to Color.BLACK.
*
* @param axisValuesColour the new colour to be used for the axis bar values.
*/
public void setAxisValuesColour(Color axisValuesColour) {
this.axisValuesColour = axisValuesColour;
}
/**
* Returns the frequency of the values displayed along the x-axis. The
* frequency is how many columns in the x-dimension have their value
* displayed. A frequency of 2 would mean every other column has a value
* shown and a frequency of 3 would mean every third column would be given a
* value.
*
* @return the frequency of the values displayed against columns.
*/
public int getXAxisValuesFrequency() {
return xAxisValuesFrequency;
}
/**
* Sets the frequency of the values displayed along the x-axis. The
* frequency is how many columns in the x-dimension have their value
* displayed. A frequency of 2 would mean every other column has a value and
* a frequency of 3 would mean every third column would be given a value.
*
*
* Defaults to 1. Every column is given a value.
*
* @param axisValuesFrequency the frequency of the values displayed against
* columns, where 1 is every column and 2 is every other column.
*/
public void setXAxisValuesFrequency(int axisValuesFrequency) {
this.xAxisValuesFrequency = axisValuesFrequency;
}
/**
* Returns the frequency of the values displayed along the y-axis. The
* frequency is how many rows in the y-dimension have their value displayed.
* A frequency of 2 would mean every other row has a value and a frequency
* of 3 would mean every third row would be given a value.
*
* @return the frequency of the values displayed against rows.
*/
public int getYAxisValuesFrequency() {
return yAxisValuesFrequency;
}
/**
* Sets the frequency of the values displayed along the y-axis. The
* frequency is how many rows in the y-dimension have their value displayed.
* A frequency of 2 would mean every other row has a value and a frequency
* of 3 would mean every third row would be given a value.
*
*
* Defaults to 1. Every row is given a value.
*
* @param axisValuesFrequency the frequency of the values displayed against
* rows, where 1 is every row and 2 is every other row.
*/
public void setYAxisValuesFrequency(int axisValuesFrequency) {
yAxisValuesFrequency = axisValuesFrequency;
}
/**
* Returns whether axis values are to be shown at all for the x-axis.
*
*
* If axis values are not shown then more space is allocated to the heat
* map.
*
* @return true if the x-axis values will be displayed, false otherwise.
*/
public boolean isShowXAxisValues() {
//TODO Could get rid of these flags and use a frequency of -1 to signal no values.
return showXAxisValues;
}
/**
* Sets whether axis values are to be shown at all for the x-axis.
*
*
* If axis values are not shown then more space is allocated to the heat
* map.
*
*
* Defaults to true.
*
* @param showXAxisValues true if x-axis values should be displayed, false
* if they should be hidden.
*/
public void setShowXAxisValues(boolean showXAxisValues) {
this.showXAxisValues = showXAxisValues;
}
/**
* Returns whether axis values are to be shown at all for the y-axis.
*
*
* If axis values are not shown then more space is allocated to the heat
* map.
*
* @return true if the y-axis values will be displayed, false otherwise.
*/
public boolean isShowYAxisValues() {
return showYAxisValues;
}
/**
* Sets whether axis values are to be shown at all for the y-axis.
*
*
* If axis values are not shown then more space is allocated to the heat
* map.
*
*
* Defaults to true.
*
* @param showYAxisValues true if y-axis values should be displayed, false
* if they should be hidden.
*/
public void setShowYAxisValues(boolean showYAxisValues) {
this.showYAxisValues = showYAxisValues;
}
/**
* Returns the colour that is currently to be displayed for the heat map
* cells with the highest z-value in the dataset.
*
*
* The full colour range will go through each RGB step between the high
* value colour and the low value colour.
*
* @return the colour in use for cells of the highest z-value.
*/
public Color getHighValueColour() {
return highValueColour;
}
/**
* Sets the colour to be used to fill cells of the heat map with the
* highest z-values in the dataset.
*
*
* The full colour range will go through each RGB step between the high
* value colour and the low value colour.
*
*
* Defaults to Color.BLACK.
*
* @param highValueColour the colour to use for cells of the highest
* z-value.
*/
public void setHighValueColour(Color highValueColour) {
this.highValueColour = highValueColour;
updateColourDistance();
}
/**
* Returns the colour that is currently to be displayed for the heat map
* cells with the lowest z-value in the dataset.
*
*
* The full colour range will go through each RGB step between the high
* value colour and the low value colour.
*
* @return the colour in use for cells of the lowest z-value.
*/
public Color getLowValueColour() {
return lowValueColour;
}
/**
* Sets the colour to be used to fill cells of the heat map with the
* lowest z-values in the dataset.
*
*
* The full colour range will go through each RGB step between the high
* value colour and the low value colour.
*
*
* Defaults to Color.WHITE.
*
* @param lowValueColour the colour to use for cells of the lowest
* z-value.
*/
public void setLowValueColour(Color lowValueColour) {
this.lowValueColour = lowValueColour;
updateColourDistance();
}
/**
* Returns the scale that is currently in use to map z-value to colour. A
* value of 1.0 will give a linear scale, which will
* spread the distribution of colours evenly amoungst the full range of
* represented z-values. A value of greater than 1.0 will give an
* exponential scale that will produce greater emphasis
* for the separation between higher values and a value between 0.0 and 1.0
* will provide a logarithmic scale, with greater
* separation of low values.
*
* @return the scale factor that is being used to map from z-value to colour.
*/
public double getColourScale() {
return colourScale;
}
/**
* Sets the scale that is currently in use to map z-value to colour. A
* value of 1.0 will give a linear scale, which will
* spread the distribution of colours evenly amoungst the full range of
* represented z-values. A value of greater than 1.0 will give an
* exponential scale that will produce greater emphasis
* for the separation between higher values and a value between 0.0 and 1.0
* will provide a logarithmic scale, with greater
* separation of low values. Values of 0.0 or less are illegal.
*
*
* Defaults to a linear scale value of 1.0.
*
* @param colourScale the scale that should be used to map from z-value to
* colour.
*/
public void setColourScale(double colourScale) {
this.colourScale = colourScale;
}
/*
* Calculate and update the field for the distance between the low colour
* and high colour. The distance is the number of steps between one colour
* and the other using an RGB coding with 0-255 values for each of red,
* green and blue. So the maximum colour distance is 255 + 255 + 255.
*/
private void updateColourDistance() {
int r1 = lowValueColour.getRed();
int g1 = lowValueColour.getGreen();
int b1 = lowValueColour.getBlue();
int r2 = highValueColour.getRed();
int g2 = highValueColour.getGreen();
int b2 = highValueColour.getBlue();
colourValueDistance = Math.abs(r1 - r2);
colourValueDistance += Math.abs(g1 - g2);
colourValueDistance += Math.abs(b1 - b2);
}
/**
* Generates a new chart
* All supported
* No chart will be generated until this or the related
*
* No chart will be generated until this or the related
*
* No chart will be generated until this or the related
* setXValues(Object[]) method.
*
* @param xOffset an offset value to be applied to the index of each z-value
* element.
* @param xInterval an interval that will separate each x-value item.
*/
public void setXValues(double xOffset, double xInterval) {
// Update the x-values according to the offset and interval.
xValues = new Object[zValues[0].length];
for (int i=0; i
* y-value = y-offset + (column-index * y-interval)
*
*
* setYValues(Object[]) method.
*
* @param yOffset an offset value to be applied to the index of each z-value
* element.
* @param yInterval an interval that will separate each y-value item.
*/
public void setYValues(double yOffset, double yInterval) {
// Update the y-values according to the offset and interval.
yValues = new Object[zValues.length];
for (int i=0; isetXValues(double, double),
* in which case the object type of each element will be Double.
*
* @return an array of the values that are to be displayed along the x-axis.
*/
public Object[] getXValues() {
return xValues;
}
/**
* Returns the y-values which are currently set to display along the y-axis.
* The array that is returned is either that which was explicitly set with
* setYValues(Object[]) or that was generated from the offset
* and interval that were given to setYValues(double, double),
* in which case the object type of each element will be Double.
*
* @return an array of the values that are to be displayed along the y-axis.
*/
public Object[] getYValues() {
return yValues;
}
/**
* Sets whether the text of the values along the x-axis should be drawn
* horizontally left-to-right, or vertically top-to-bottom.
*
* @param xValuesHorizontal true if x-values should be drawn horizontally,
* false if they should be drawn vertically.
*/
public void setXValuesHorizontal(boolean xValuesHorizontal) {
this.xValuesHorizontal = xValuesHorizontal;
}
/**
* Returns whether the text of the values along the x-axis are to be drawn
* horizontally left-to-right, or vertically top-to-bottom.
*
* @return true if the x-values will be drawn horizontally, false if they
* will be drawn vertically.
*/
public boolean isXValuesHorizontal() {
return xValuesHorizontal;
}
/**
* Sets whether the text of the values along the y-axis should be drawn
* horizontally left-to-right, or vertically top-to-bottom.
*
* @param yValuesHorizontal true if y-values should be drawn horizontally,
* false if they should be drawn vertically.
*/
public void setYValuesHorizontal(boolean yValuesHorizontal) {
this.yValuesHorizontal = yValuesHorizontal;
}
/**
* Returns whether the text of the values along the y-axis are to be drawn
* horizontally left-to-right, or vertically top-to-bottom.
*
* @return true if the y-values will be drawn horizontally, false if they
* will be drawn vertically.
*/
public boolean isYValuesHorizontal() {
return yValuesHorizontal;
}
/**
* Sets the width of each individual cell that constitutes a value in x,y,z
* data space. By setting the cell width, any previously set chart width
* will be overwritten with a value calculated based upon this value and the
* number of cells in there are along the x-axis.
*
* @param cellWidth the new width to use for each individual data cell.
* @deprecated As of release 0.6, replaced by {@link #setCellSize(Dimension)}
*/
@Deprecated
public void setCellWidth(int cellWidth) {
setCellSize(new Dimension(cellWidth, cellSize.height));
}
/**
* Returns the width of each individual data cell that constitutes a value
* in the x,y,z space.
*
* @return the width of each cell.
* @deprecated As of release 0.6, replaced by {@link #getCellSize}
*/
@Deprecated
public int getCellWidth() {
return cellSize.width;
}
/**
* Sets the height of each individual cell that constitutes a value in x,y,z
* data space. By setting the cell height, any previously set chart height
* will be overwritten with a value calculated based upon this value and the
* number of cells in there are along the y-axis.
*
* @param cellHeight the new height to use for each individual data cell.
* @deprecated As of release 0.6, replaced by {@link #setCellSize(Dimension)}
*/
@Deprecated
public void setCellHeight(int cellHeight) {
setCellSize(new Dimension(cellSize.width, cellHeight));
}
/**
* Returns the height of each individual data cell that constitutes a value
* in the x,y,z space.
*
* @return the height of each cell.
* @deprecated As of release 0.6, replaced by {@link #getCellSize()}
*/
@Deprecated
public int getCellHeight() {
return cellSize.height;
}
/**
* Sets the size of each individual cell that constitutes a value in x,y,z
* data space. By setting the cell size, any previously set chart size will
* be overwritten with a value calculated based upon this value and the
* number of cells along each axis.
*
* @param cellSize the new size to use for each individual data cell.
* @since 0.6
*/
public void setCellSize(Dimension cellSize) {
this.cellSize = cellSize;
}
/**
* Returns the size of each individual data cell that constitutes a value in
* the x,y,z space.
*
* @return the size of each individual data cell.
* @since 0.6
*/
public Dimension getCellSize() {
return cellSize;
}
/**
* Returns the width of the chart in pixels as calculated according to the
* cell dimensions, chart margin and other size settings.
*
* @return the width in pixels of the chart image to be generated.
* @deprecated As of release 0.6, replaced by {@link #getChartSize()}
*/
@Deprecated
public int getChartWidth() {
return chartSize.width;
}
/**
* Returns the height of the chart in pixels as calculated according to the
* cell dimensions, chart margin and other size settings.
*
* @return the height in pixels of the chart image to be generated.
* @deprecated As of release 0.6, replaced by {@link #getChartSize()}
*/
@Deprecated
public int getChartHeight() {
return chartSize.height;
}
/**
* Returns the size of the chart in pixels as calculated according to the
* cell dimensions, chart margin and other size settings.
*
* @return the size in pixels of the chart image to be generated.
* @since 0.6
*/
public Dimension getChartSize() {
return chartSize;
}
/**
* Returns the String that will be used as the title of any successive
* calls to generate a chart.
*
* @return the title of the chart.
*/
public String getTitle() {
return title;
}
/**
* Sets the String that will be used as the title of any successive
* calls to generate a chart. The title will be displayed centralised
* horizontally at the top of any generated charts.
*
* Color.WHITE.
*
* @param backgroundColour the new colour to be set as the background fill.
*/
public void setBackgroundColour(Color backgroundColour) {
if (backgroundColour == null) {
backgroundColour = Color.WHITE;
}
this.backgroundColour = backgroundColour;
}
/**
* Returns the Font that describes the visual style of the
* title.
*
* @return the Font that will be used to render the title.
*/
public Font getTitleFont() {
return titleFont;
}
/**
* Sets a new Font to be used in rendering the chart's title
* String.
*
* Color that represents the colour the title text
* should be painted in.
*
* @return the currently set colour to be used in painting the chart title.
*/
public Color getTitleColour() {
return titleColour;
}
/**
* Sets the Color that describes the colour to be used for the
* chart title String.
*
* Color.BLACK.
*
* @param titleColour the colour to paint the chart's title String.
*/
public void setTitleColour(Color titleColour) {
this.titleColour = titleColour;
}
/**
* Returns the width of the axis bars in pixels. Both axis bars have the
* same thickness.
*
* @return the thickness of the axis bars in pixels.
*/
public int getAxisThickness() {
return axisThickness;
}
/**
* Sets the width of the axis bars in pixels. Both axis bars use the same
* thickness.
*
* Color.BLACK.
*
* @param axisColour the colour to be set for use on the axis bars.
*/
public void setAxisColour(Color axisColour) {
this.axisColour = axisColour;
}
/**
* Returns the font that describes the visual style of the labels of the
* axis. Both axis' labels use the same font.
*
* @return the font used to define the visual style of the axis labels.
*/
public Font getAxisLabelsFont() {
return axisLabelsFont;
}
/**
* Sets the font that describes the visual style of the axis labels. Both
* axis' labels use the same font.
*
* Image based upon the currently held
* settings and then attempts to save that image to disk, to the location
* provided as a File parameter. The image type of the saved file will
* equal the extension of the filename provided, so it is essential that a
* suitable extension be included on the file name.
*
* ImageIO file types are supported, including
* PNG, JPG and GIF.
*
* getChartImage() method are called. All successive calls
* will result in the generation of a new chart image, no caching is used.
*
* @param outputFile the file location that the generated image file should
* be written to. The File must have a suitable filename, with an extension
* of a valid image format (as supported by ImageIO).
* @throws IOException if the output file's filename has no extension or
* if there the file is unable to written to. Reasons for this include a
* non-existant file location (check with the File exists() method on the
* parent directory), or the permissions of the write location may be
* incorrect.
*/
public void saveToFile(File outputFile) throws IOException {
String filename = outputFile.getName();
int extPoint = filename.lastIndexOf('.');
if (extPoint < 0) {
throw new IOException("Illegal filename, no extension used.");
}
// Determine the extension of the filename.
String ext = filename.substring(extPoint + 1);
// Handle jpg without transparency.
if (ext.toLowerCase().equals("jpg") || ext.toLowerCase().equals("jpeg")) {
BufferedImage chart = (BufferedImage) getChartImage(false);
// Save our graphic.
saveGraphicJpeg(chart, outputFile, 1.0f);
} else {
BufferedImage chart = (BufferedImage) getChartImage(true);
ImageIO.write(chart, ext, outputFile);
}
}
private void saveGraphicJpeg(BufferedImage chart, File outputFile, float quality) throws IOException {
// Setup correct compression for jpeg.
IteratorImage configured
* according to this object's currently held settings. The given parameter
* determines whether transparency should be enabled for the generated
* image.
*
* saveToFile(File) method are called. All successive calls
* will result in the generation of a new chart image, no caching is used.
*
* @param alpha whether to enable transparency.
* @return A newly generated chart Image. The returned image
* is a BufferedImage.
*/
public Image getChartImage(boolean alpha) {
// Calculate all unknown dimensions.
measureComponents();
updateCoordinates();
// Determine image type based upon whether require alpha or not.
// Using BufferedImage.TYPE_INT_ARGB seems to break on jpg.
int imageType = (alpha ? BufferedImage.TYPE_4BYTE_ABGR : BufferedImage.TYPE_3BYTE_BGR);
// Create our chart image which we will eventually draw everything on.
BufferedImage chartImage = new BufferedImage(chartSize.width, chartSize.height, imageType);
Graphics2D chartGraphics = chartImage.createGraphics();
// Use anti-aliasing where ever possible.
chartGraphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
// Set the background.
chartGraphics.setColor(backgroundColour);
chartGraphics.fillRect(0, 0, chartSize.width, chartSize.height);
// Draw the title.
drawTitle(chartGraphics);
// Draw the heatmap image.
drawHeatMap(chartGraphics, zValues);
// Draw the axis labels.
drawXLabel(chartGraphics);
drawYLabel(chartGraphics);
// Draw the axis bars.
drawAxisBars(chartGraphics);
// Draw axis values.
drawXValues(chartGraphics);
drawYValues(chartGraphics);
return chartImage;
}
/**
* Generates and returns a new chart Image configured
* according to this object's currently held settings. By default the image
* is generated with no transparency.
*
* saveToFile(File) method are called. All successive calls
* will result in the generation of a new chart image, no caching is used.
*
* @return A newly generated chart Image. The returned image
* is a BufferedImage.
*/
public Image getChartImage() {
return getChartImage(false);
}
/*
* Calculates all unknown component dimensions.
*/
private void measureComponents() {
//TODO This would be a good place to check that all settings have sensible values or throw illegal state exception.
//TODO Put this somewhere so it only gets created once.
BufferedImage chartImage = new BufferedImage(1, 1, BufferedImage.TYPE_INT_ARGB);
Graphics2D tempGraphics = chartImage.createGraphics();
// Calculate title dimensions.
if (title != null) {
tempGraphics.setFont(titleFont);
FontMetrics metrics = tempGraphics.getFontMetrics();
titleSize = new Dimension(metrics.stringWidth(title), metrics.getHeight());
titleAscent = metrics.getAscent();
} else {
titleSize = new Dimension(0, 0);
}
// Calculate x-axis label dimensions.
if (xAxisLabel != null) {
tempGraphics.setFont(axisLabelsFont);
FontMetrics metrics = tempGraphics.getFontMetrics();
xAxisLabelSize = new Dimension(metrics.stringWidth(xAxisLabel), metrics.getHeight());
xAxisLabelDescent = metrics.getDescent();
} else {
xAxisLabelSize = new Dimension(0, 0);
}
// Calculate y-axis label dimensions.
if (yAxisLabel != null) {
tempGraphics.setFont(axisLabelsFont);
FontMetrics metrics = tempGraphics.getFontMetrics();
yAxisLabelSize = new Dimension(metrics.stringWidth(yAxisLabel), metrics.getHeight());
yAxisLabelAscent = metrics.getAscent();
} else {
yAxisLabelSize = new Dimension(0, 0);
}
// Calculate x-axis value dimensions.
if (showXAxisValues) {
tempGraphics.setFont(axisValuesFont);
FontMetrics metrics = tempGraphics.getFontMetrics();
xAxisValuesHeight = metrics.getHeight();
xAxisValuesWidthMax = 0;
for (Object o: xValues) {
int w = metrics.stringWidth(o.toString());
if (w > xAxisValuesWidthMax) {
xAxisValuesWidthMax = w;
}
}
} else {
xAxisValuesHeight = 0;
}
// Calculate y-axis value dimensions.
if (showYAxisValues) {
tempGraphics.setFont(axisValuesFont);
FontMetrics metrics = tempGraphics.getFontMetrics();
yAxisValuesHeight = metrics.getHeight();
yAxisValuesAscent = metrics.getAscent();
yAxisValuesWidthMax = 0;
for (Object o: yValues) {
int w = metrics.stringWidth(o.toString());
if (w > yAxisValuesWidthMax) {
yAxisValuesWidthMax = w;
}
}
} else {
yAxisValuesHeight = 0;
}
// Calculate heatmap dimensions.
int heatMapWidth = (zValues[0].length * cellSize.width);
int heatMapHeight = (zValues.length * cellSize.height);
heatMapSize = new Dimension(heatMapWidth, heatMapHeight);
int yValuesHorizontalSize = 0;
if (yValuesHorizontal) {
yValuesHorizontalSize = yAxisValuesWidthMax;
} else {
yValuesHorizontalSize = yAxisValuesHeight;
}
int xValuesVerticalSize = 0;
if (xValuesHorizontal) {
xValuesVerticalSize = xAxisValuesHeight;
} else {
xValuesVerticalSize = xAxisValuesWidthMax;
}
// Calculate chart dimensions.
int chartWidth = heatMapWidth + (2 * margin) + yAxisLabelSize.height + yValuesHorizontalSize + axisThickness;
int chartHeight = heatMapHeight + (2 * margin) + xAxisLabelSize.height + xValuesVerticalSize + titleSize.height + axisThickness;
chartSize = new Dimension(chartWidth, chartHeight);
}
/*
* Calculates the co-ordinates of some key positions.
*/
private void updateCoordinates() {
// Top-left of heat map.
int x = margin + axisThickness + yAxisLabelSize.height;
x += (yValuesHorizontal ? yAxisValuesWidthMax : yAxisValuesHeight);
int y = titleSize.height + margin + yAxisOffset;
heatMapTL = new Point(x, y);
// Top-right of heat map.
x = heatMapTL.x + heatMapSize.width;
y = heatMapTL.y + heatMapSize.height;
heatMapBR = new Point(x, y);
// Centre of heat map.
x = heatMapTL.x + (heatMapSize.width / 2);
y = heatMapTL.y + (heatMapSize.height / 2);
heatMapC = new Point(x, y);
}
/*
* Draws the title String on the chart if title is not null.
*/
private void drawTitle(Graphics2D chartGraphics) {
if (title != null) {
// Strings are drawn from the baseline position of the leftmost char.
int yTitle = (margin/2) + titleAscent;
int xTitle = (chartSize.width/2) - (titleSize.width/2);
chartGraphics.setFont(titleFont);
chartGraphics.setColor(titleColour);
chartGraphics.drawString(title, xTitle, yTitle);
}
}
/*
* Creates the actual heatmap element as an image, that can then be drawn
* onto a chart.
*/
private void drawHeatMap(Graphics2D chartGraphics, double[][] data) {
// Calculate the available size for the heatmap.
int noYCells = data.length;
int noXCells = data[0].length;
//double dataMin = min(data);
//double dataMax = max(data);
BufferedImage heatMapImage = new BufferedImage(heatMapSize.width, heatMapSize.height, BufferedImage.TYPE_INT_ARGB);
Graphics2D heatMapGraphics = heatMapImage.createGraphics();
for (int x=0; x