Home » Archived » BIRT » [Chart] AutoScaling and y-Labels
| |
Re: [Chart] AutoScaling and y-Labels [message #939924 is a reply to message #939849] |
Thu, 11 October 2012 06:33 |
moritz du Messages: 102 Registered: February 2010 |
Senior Member |
|
|
here is the code:
package chart;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.List;
import org.eclipse.birt.chart.api.ChartEngine;
import org.eclipse.birt.chart.device.EmptyUpdateNotifier;
import org.eclipse.birt.chart.device.IDeviceRenderer;
import org.eclipse.birt.chart.device.IDisplayServer;
import org.eclipse.birt.chart.device.IImageMapEmitter;
import org.eclipse.birt.chart.exception.ChartException;
import org.eclipse.birt.chart.factory.GeneratedChartState;
import org.eclipse.birt.chart.factory.IGenerator;
import org.eclipse.birt.chart.model.ChartWithAxes;
import org.eclipse.birt.chart.model.attribute.ActionType;
import org.eclipse.birt.chart.model.attribute.AxisType;
import org.eclipse.birt.chart.model.attribute.Bounds;
import org.eclipse.birt.chart.model.attribute.IntersectionType;
import org.eclipse.birt.chart.model.attribute.LegendItemType;
import org.eclipse.birt.chart.model.attribute.Position;
import org.eclipse.birt.chart.model.attribute.TickStyle;
import org.eclipse.birt.chart.model.attribute.TriggerCondition;
import org.eclipse.birt.chart.model.attribute.impl.BoundsImpl;
import org.eclipse.birt.chart.model.attribute.impl.ColorDefinitionImpl;
import org.eclipse.birt.chart.model.attribute.impl.TextImpl;
import org.eclipse.birt.chart.model.attribute.impl.TooltipValueImpl;
import org.eclipse.birt.chart.model.component.Axis;
import org.eclipse.birt.chart.model.component.Series;
import org.eclipse.birt.chart.model.component.impl.LabelImpl;
import org.eclipse.birt.chart.model.component.impl.SeriesImpl;
import org.eclipse.birt.chart.model.data.NumberDataSet;
import org.eclipse.birt.chart.model.data.SeriesDefinition;
import org.eclipse.birt.chart.model.data.TextDataSet;
import org.eclipse.birt.chart.model.data.impl.ActionImpl;
import org.eclipse.birt.chart.model.data.impl.NumberDataSetImpl;
import org.eclipse.birt.chart.model.data.impl.SeriesDefinitionImpl;
import org.eclipse.birt.chart.model.data.impl.TextDataSetImpl;
import org.eclipse.birt.chart.model.data.impl.TriggerImpl;
import org.eclipse.birt.chart.model.impl.ChartWithAxesImpl;
import org.eclipse.birt.chart.model.layout.Plot;
import org.eclipse.birt.chart.model.type.BarSeries;
import org.eclipse.birt.chart.model.type.impl.BarSeriesImpl;
import org.eclipse.birt.core.framework.PlatformConfig;
import base64.Base64;
/**
* A bar chart. The given data determines details like labeling and stacking/ grouping. The given data will be grouped
* by columns and colored by lines. If {@link ChartDataSource#transpose()} is true this will be inverted.
*
* Most of the code is copied from chart example code. There may be unnecessary lines of code.
*
* @author me
*
*/
public class BarChart {
/**
* Delay for mouse hover tool tips.
*/
private static final int TOOL_TIP_DELAY = 200;
/**
* Rotation of x axis labels.
*/
private static final int X_ROTATION = 0;
/**
* Specifies png output.
*/
private static final String PNG_F = "dv.PNG";
/**
* Specifies svg output.
*/
private static final String SVG_F = "dv.SVG";
/**
* Images are rendered with this resolution. This is needed to scale them to a given pixel width and height.
*/
private static final double DEFAULT_DPI = 72d;
/**
* The birt chart object.
*/
private ChartWithAxes cwaBar = ChartWithAxesImpl.create();
/**
* Hold all needed data.
*/
private ChartDataSource data;
/**
* x-axis.
*/
private Axis xAxisPrimary;
/**
* y-axis.
*/
private Axis yAxisPrimary;
/**
* For general settings.
*/
private PlatformConfig pf = new PlatformConfig();
/**
* Instance of chart engine.
*/
private ChartEngine ce;
/**
* Used to render all output formats.
*/
private IDeviceRenderer dRenderer;
/**
* The bar series.
*/
private List<BarSeries> barSeries;
/**
* Creates a chart from given data.
*
* @param data
* contains all needed.
*/
public BarChart(final ChartDataSource data) {
this.data = data;
// Standalone means - the chart is not embedded in a birt report.
pf.setProperty("STANDALONE", true);
ce = ChartEngine.instance(pf);
prepareChart();
setupAxes();
setupData();
setupTriggers();
// set script to rearrange labels
cwaBar.setScript("chart.AxisScript");
}
/**
* Set ups mouse hover and other events.
*
*/
private void setupTriggers() {
for (BarSeries bs : barSeries) {
bs.getDataPoint().setPrefix((String) bs.getSeriesIdentifier() + ": ");
bs.getTriggers()
.add(TriggerImpl.create(TriggerCondition.ONMOUSEOVER_LITERAL,
ActionImpl.create(ActionType.SHOW_TOOLTIP_LITERAL,
TooltipValueImpl.create(TOOL_TIP_DELAY,
null))));
}
}
/**
* Creates the "series" - the bars of the chart.
*/
private void setupData() {
TextDataSet categoryValues;
List<NumberDataSet> dataSets = new ArrayList<NumberDataSet>();
// normal
if (data.transpose() == null || !data.transpose()) {
categoryValues = TextDataSetImpl.create(data.columnLabels());
// each line is one data set
for (int i = 0; i < data.lineLabels().length; i++) {
dataSets.add(NumberDataSetImpl.create(data.data()[i]));
}
} else {
// grouped by lines not columns
categoryValues = TextDataSetImpl.create(data.lineLabels());
// iterate over columns
for (int i = 0; i < data.columnLabels().length; i++) {
// create array for each column
Double[] column = new Double[data.lineLabels().length];
// iterate over lines
for (int j = 0; j < data.lineLabels().length; j++) {
column[j] = data.data()[j][i];
}
dataSets.add(NumberDataSetImpl.create(column));
}
}
// X-Series
Series seCategory = SeriesImpl.create();
seCategory.setDataSet(categoryValues);
SeriesDefinition sdX = SeriesDefinitionImpl.create();
xAxisPrimary.getSeriesDefinitions().add(sdX);
sdX.getSeries().add(seCategory);
// sdX.getSeriesPalette().shift(0);
barSeries = new ArrayList<BarSeries>();
// creates a series definition for each group
for (List<Integer> group : data.stackedLines()) {
if (group != null && !group.isEmpty()) {
SeriesDefinition groupSeries = SeriesDefinitionImpl.create();
groupSeries.getSeriesPalette().shift(0);
yAxisPrimary.getSeriesDefinitions().add(groupSeries);
for (Integer integer : group) {
// new series for this stacked bar
BarSeries bs = (BarSeries) BarSeriesImpl.create();
barSeries.add(bs);
// add to series definition
groupSeries.getSeries().add(bs);
bs.setDataSet(dataSets.get(integer));
bs.setStacked(true);
if (data.transpose() == null || !data.transpose()) {
bs.setSeriesIdentifier(data.lineLabels()[integer]);
// mark line as added
data.lineLabels()[integer] = null;
} else {
bs.setSeriesIdentifier(data.columnLabels()[integer]);
// mark line as added
data.columnLabels()[integer] = null;
}
}
}
}
// now handle all not "used" lines or columns and add them to a unstacked series definition
String[] rest = (data.transpose() == null || !data.transpose()) ? data.lineLabels() : data.columnLabels();
SeriesDefinition unstackedSeries = null;
for (int i = 0; i < rest.length; i++) {
// check if added already
if (rest[i] != null) {
if (unstackedSeries == null) {
unstackedSeries = SeriesDefinitionImpl.create();
unstackedSeries.getSeriesPalette().shift(0);
yAxisPrimary.getSeriesDefinitions().add(unstackedSeries);
}
BarSeries bs = (BarSeries) BarSeriesImpl.create();
barSeries.add(bs);
bs.setSeriesIdentifier(rest[i]);
bs.setDataSet(dataSets.get(i));
unstackedSeries.getSeries().add(bs);
// stacked is false by default
}
}
}
/**
* Sets up x and y axis, including style of labels and caption.
*/
private void setupAxes() {
xAxisPrimary = cwaBar.getPrimaryBaseAxes()[0];
xAxisPrimary.setType(AxisType.TEXT_LITERAL);
xAxisPrimary.getMajorGrid().setTickStyle(TickStyle.BELOW_LITERAL);
xAxisPrimary.getOrigin().setType(IntersectionType.MIN_LITERAL);
xAxisPrimary.getLabel().getCaption().getFont().setRotation(X_ROTATION);
yAxisPrimary = cwaBar.getPrimaryOrthogonalAxis(xAxisPrimary);
yAxisPrimary.getMajorGrid().setTickStyle(TickStyle.LEFT_LITERAL);
yAxisPrimary.setType(AxisType.LINEAR_LITERAL);
if (data.relative() != null && data.relative()) {
yAxisPrimary.setPercent(true);
}
org.eclipse.birt.chart.model.component.Label percent = LabelImpl.create();
percent.setCaption(TextImpl.create(data.unit()));
yAxisPrimary.setTitle(percent);
yAxisPrimary.setLabelPosition(Position.LEFT_LITERAL);
yAxisPrimary.getLabel().getCaption().getFont().setRotation(0);
}
/**
* Sets up some chart layout stuff.
*/
private void prepareChart() {
// plot area
cwaBar.getBlock().setBackground(ColorDefinitionImpl.WHITE());
cwaBar.getBlock().getOutline().setVisible(true);
Plot p = cwaBar.getPlot();
p.getClientArea().setBackground(ColorDefinitionImpl.WHITE());
// title
cwaBar.getTitle().getLabel().getCaption().setValue(data.label());
// legend
cwaBar.getLegend().setItemType(LegendItemType.SERIES_LITERAL);
}
/**
* Renders chart to {@link OutputStream} in given format.
*
* @param out
* output stream
* @param width
* in pixels
* @param height
* in pixels
* @param format
* desired output format
*/
private void renderAsStream(final OutputStream out, final int width, final int height, final String format) {
// Returns a singleton instance of the Generator
IGenerator gr = ce.getGenerator();
dRenderer = null;
IDisplayServer dServer = null;
GeneratedChartState gcs;
try {
// device renderers for dv.SWT, dv.PNG, dv.JPG
// dv.PDF, dv.SVG, dv.SWING, dv.PNG24, div.BMP
dRenderer = ce.getRenderer(format);
dServer = dRenderer.getDisplayServer();
dRenderer.setProperty(IDeviceRenderer.FILE_IDENTIFIER, out);
Bounds bo = BoundsImpl.create(0, 0, width, height);
// if not called the resolution will not be the set one.
bo.scale(DEFAULT_DPI / dRenderer.getDisplayServer().getDpiResolution());
gcs = gr.build(dServer, cwaBar, bo, null, null, null);
// sets interactivity
gcs.getRunTimeContext().setActionRenderer(new ChartActionRenderer(null));
dRenderer.setProperty(IDeviceRenderer.UPDATE_NOTIFIER,
new EmptyUpdateNotifier(cwaBar, gcs.getChartModel()));
gr.render(dRenderer, gcs);
} catch (ChartException e) {
throw new IllegalStateException("Problem on rendering the chart. Cause: ", e);
} catch (Exception ex) {
throw new IllegalStateException("Problem on rendering the chart. Cause: ", ex);
}
}
/**
* Renders a svg graphic.
*
* @param output
* output stream
* @param width
* in pixels
* @param height
* in pixels
*/
public final void renderSVG(final OutputStream output, final int width, final int height) {
renderAsStream(output, width, height, SVG_F);
}
/**
* Renders png image.
*
* @param output
* output stream
* @param width
* in pixels
* @param height
* in pixels
*/
public final void renderPng(final OutputStream output, final Integer width, final Integer height) {
renderAsStream(output, width, height, PNG_F);
}
/**
* Same as {@link BarChart#renderPng(OutputStream, Integer, Integer)} but it returns a html imagemap for the
* rendered image.
*
* @param output
* will be filled with image
* @param width
* in pixels
* @param height
* in pixels
* @return html image map for image
*/
public final String renderPngWithImageMap(final OutputStream output, final Integer width, final Integer height) {
renderAsStream(output, width, height, PNG_F);
return ((IImageMapEmitter) dRenderer).getImageMap();
}
/**
* Generates a html-div Element with an image map and the image embedded base64 encoded.
*
* @param width
* in pixels
* @param height
* in pixels
* @return div with image map and embedded image.
*/
public final String renderPngAsEmbeddedHtml(final Integer width, final Integer height) {
StringBuilder result = new StringBuilder();
ByteArrayOutputStream output = new ByteArrayOutputStream();
renderPng(output, width, height);
String imageMap = ((IImageMapEmitter) dRenderer).getImageMap();
result.append("<div>");
result.append("\r\n");
result.append("<map name='pngmap'>");
result.append(imageMap);
result.append("</map>");
result.append("\r\n");
result.append("<img usemap='#pngmap' src=\"data:image/png;base64,");
result.append(Base64.encodeToString(output.toByteArray(), true));
result.append("\" >");
result.append("\r\n");
result.append("</div>");
return result.toString();
}
/**
* Renders a png image as BufferedImage.
*
* @param width
* in pixels
* @param height
* in pixels
* @return png as an image
*/
public final BufferedImage renderPng(final Integer width, final Integer height) {
BufferedImage result = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
IGenerator gr = ce.getGenerator();
IDisplayServer dServer = null;
GeneratedChartState gcs;
try {
dRenderer = ce.getRenderer("dv.PNG");
dRenderer.setProperty(IDeviceRenderer.GRAPHICS_CONTEXT, (Graphics2D) result.getGraphics());
dRenderer.setProperty(IDeviceRenderer.CACHED_IMAGE, result);
dServer = dRenderer.getDisplayServer();
Bounds bo = BoundsImpl.create(0, 0, width, height);
// if not called the resolution will not be the set one.
bo.scale(DEFAULT_DPI / dRenderer.getDisplayServer().getDpiResolution());
gcs = gr.build(dServer, cwaBar, bo, null, null, null);
dRenderer.setProperty(IDeviceRenderer.UPDATE_NOTIFIER,
new EmptyUpdateNotifier(cwaBar, gcs.getChartModel()));
gr.render(dRenderer, gcs);
} catch (ChartException e) {
throw new IllegalStateException("Problem on rendering the chart. Cause: ", e);
}
return result;
}
}
its the main part of a self made chart api for my use case i hope you understand the parts you need to answer the question.
[Updated on: Thu, 11 October 2012 06:34] Report message to a moderator
|
|
| |
Re: [Chart] AutoScaling and y-Labels [message #944984 is a reply to message #941651] |
Mon, 15 October 2012 07:30 |
moritz du Messages: 102 Registered: February 2010 |
Senior Member |
|
|
it is very simple to reproduce. i just took your "StandaloneChart"-Example and changed the data to
NumberDataSet orthoValues1 = NumberDataSetImpl.create(new double[] {1, 2, 1.5, 2, 1.3});
NumberDataSet orthoValues2 = NumberDataSetImpl.create(new double[] {1, 1.1, 1.2, 1, 1.4});
it produces:
In general: if difference between min and max is very short and chart has enough height it will display interim values (that is good) but without decimal places (wrong format, that is bad).
I think the ticks should always be labeled with their exact value (with same accuracy as the given values). So if data contains doubles with 3 decimal places the labels of ticks should also contain 3 decimal places?!
Or the other way around: i thought this kind of auto formatting and labeling is part of "auto axis scaling" i read in faq?!
If not i have to write code that checks given values and creates a label format (i found example for it).
|
|
| | |
Re: [Chart] AutoScaling and y-Labels [message #947397 is a reply to message #946674] |
Wed, 17 October 2012 03:45 |
|
You can in script/event handler, but before doing that can you verify the code I posted works? I believe this is most likely a auto formatting issue not an auto scale issue. I assume the values are correct, just not showing the fractional parts.
Jason
|
|
| | | |
Goto Forum:
Current Time: Thu Apr 25 22:39:55 GMT 2024
Powered by FUDForum. Page generated in 0.03327 seconds
|