Eclipse Community Forums
Forum Search:

Search      Help    Register    Login    Home
Home » Eclipse Projects » BIRT » [Chart] AutoScaling and y-Labels
[Chart] AutoScaling and y-Labels [message #939116] Wed, 10 October 2012 08:49 Go to next message
moritz du is currently offline moritz du
Messages: 98
Registered: February 2010
Member
See y-Labels here index.php/fa/11798/0/

I guess the auto scaling feature is meant to maximize a charts readability for given size and input values but in this case it obviously fails?!
Either some of the ticks should be omitted or the y-label format should be switched to display some decimal places, shouldn't it?
(Displaying a value between 4 and 5 as "4" can't be right?!)

Or the other way around: It is possible to avoid this behavior in general? I think the best behavior would be:
If input contains fractional numbers and there are more ticks than integer values (min to max) then label format should be switched to display interim values as 4 , 4.5 , 5 ...
  • Attachment: out.png
    (Size: 21.12KB, Downloaded 460 times)
Re: [Chart] AutoScaling and y-Labels [message #939849 is a reply to message #939116] Thu, 11 October 2012 00:57 Go to previous messageGo to next message
Jason Weathersby is currently offline Jason Weathersby
Messages: 9167
Registered: July 2009
Senior Member

How was the chart produced? Can you post the code or report?

Jason
Re: [Chart] AutoScaling and y-Labels [message #939924 is a reply to message #939849] Thu, 11 October 2012 02:33 Go to previous messageGo to next message
moritz du is currently offline moritz du
Messages: 98
Registered: February 2010
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 02:34]

Report message to a moderator

Re: [Chart] AutoScaling and y-Labels [message #941651 is a reply to message #939924] Fri, 12 October 2012 14:55 Go to previous messageGo to next message
Jason Weathersby is currently offline Jason Weathersby
Messages: 9167
Registered: July 2009
Senior Member

Can you post an example set of data that you are using with this so I can run it? BTW if you only add one series to the y-axis does this work?

Jason
Re: [Chart] AutoScaling and y-Labels [message #944984 is a reply to message #941651] Mon, 15 October 2012 03:30 Go to previous messageGo to next message
moritz du is currently offline moritz du
Messages: 98
Registered: February 2010
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:
index.php/fa/11851/0/

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 #946297 is a reply to message #944984] Tue, 16 October 2012 01:02 Go to previous messageGo to next message
Jason Weathersby is currently offline Jason Weathersby
Messages: 9167
Registered: July 2009
Senior Member

Have you tried setting the format on the y axis?
yAxisPrimary.setFormatSpecifier( JavaNumberFormatSpecifierImpl.create( "###,###" ) );

Jason
Re: [Chart] AutoScaling and y-Labels [message #946674 is a reply to message #946297] Tue, 16 October 2012 07:45 Go to previous messageGo to next message
moritz du is currently offline moritz du
Messages: 98
Registered: February 2010
Member
no i didn't tried it, because i thought that the chart's auto scaling should produce correct results: in this case by either omitting some ticks (for floating point values) or by automatically changing the format to produce ticks that are correctly labeled.

The problem in my case is that the Format is not fix. It depends on min/max value and the size of the chart (all the stuff that auto scaling is using and calculating already).

To correctly calculate the format i have to know if there are ticks with floating values (then format is something like ##....##) or if all ticks have integer values the format should be (####...).

Are there any means to get all tick values? and to set label Format afterwards?
Re: [Chart] AutoScaling and y-Labels [message #947397 is a reply to message #946674] Tue, 16 October 2012 23:45 Go to previous messageGo to next message
Jason Weathersby is currently offline Jason Weathersby
Messages: 9167
Registered: July 2009
Senior Member

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
Re: [Chart] AutoScaling and y-Labels [message #947524 is a reply to message #947397] Wed, 17 October 2012 02:50 Go to previous messageGo to next message
moritz du is currently offline moritz du
Messages: 98
Registered: February 2010
Member
Thx for the hint Jason,

I just tried it with "StandaloneChart". It works as supposed:

yAxisPrimary.setFormatSpecifier(JavaNumberFormatSpecifierImpl.create("#.##"));

this yields:

index.php/fa/11949/0/

so i guess your assumption is correct. will there be a fix for this issue?

because setting the format to #.## displays decimal places only if needed (there is no 1.0...), it is a good workaround for me.

[Updated on: Wed, 17 October 2012 05:58]

Report message to a moderator

Re: [Chart] AutoScaling and y-Labels [message #949341 is a reply to message #947524] Thu, 18 October 2012 19:14 Go to previous messageGo to next message
Jason Weathersby is currently offline Jason Weathersby
Messages: 9167
Registered: July 2009
Senior Member

I thought there was a bug for this, but I can not locate it. You may want to see if you can find it and if not log one. You can use script to set the formatting conditionally as well.

Jason
Re: [Chart] AutoScaling and y-Labels [message #949693 is a reply to message #949341] Fri, 19 October 2012 03:44 Go to previous message
moritz du is currently offline moritz du
Messages: 98
Registered: February 2010
Member
thx jason i will do so
Previous Topic:Aggregation with filter in question
Next Topic:GC overhead limit exceeded problem
Goto Forum:
  


Current Time: Tue Jul 22 11:35:01 EDT 2014

Powered by FUDForum. Page generated in 0.01846 seconds