jfree / jfreechart

A 2D chart library for Java applications (JavaFX, Swing or server-side).
http://www.jfree.org/jfreechart/
GNU Lesser General Public License v2.1
1.2k stars 459 forks source link

Rotate X-axis labels vertically #394

Open sudarshansb143 opened 6 months ago

sudarshansb143 commented 6 months ago

I want to show x-axis labels in complete vertical format just like the once mentioned in following diagram. I tried following code but none of them worked. Use case emerged because I want to fit a large date range on the x-axis


 CategoryAxis xAxis = (CategoryAxis) chart.getCategoryPlot().getDomainAxis();
 xAxis.setCategoryLabelPositions(CategoryLabelPositions.DOWN_90);
 xAxis.setCategoryLabelPositions(CategoryLabelPositions.UP_90);
 xAxis.setCategoryLabelPositions(CategoryLabelPositions.STANDARD);

image

trashgod commented 6 months ago

If CategoryLabelPositions.UP_90 is too crowded, consider a SlidingCategoryDataset, suggested here.

sudarshansb143 commented 6 months ago

@trashgod Can you provide code snippet of how can I use it ? it would be a great help

trashgod commented 6 months ago

SlidingCategoryDataset appears to be meant for interactive use: set the first index and count; the chart will update itself in response. Without context, it's hard to be more specific.

sudarshansb143 commented 6 months ago

To give you the context. I'm trying to generate a token distribution graph over the period of 24hrs of time. As per current implementation the graph looks something like this : image

The implementation of above chart is done as per following snippet


  public JFreeChart generateGraphForTokensIn24hrs(List<TokenDistributionOverTimeDto> dataList, String chartTitle, String xAxisLabel, String yAxisLabel) {
        // Create dataset
        DefaultCategoryDataset dataset = new DefaultCategoryDataset();

        // Add values to the dataset
        for (TokenDistributionOverTimeDto data : dataList) {
            dataset.addValue(data.getTokenCount(), "Tokens", data.getTime());
        }

        // Create stacked area chart
        JFreeChart chart = ChartFactory.createStackedAreaChart(
                chartTitle,
                xAxisLabel,
                yAxisLabel,
                dataset,                             // Dataset
                PlotOrientation.VERTICAL,            // Orientation
                true,                                // Include legend
                true,                                // Include tooltips
                false
        );
        // Set the background color of the chart
        chart.getPlot().setBackgroundPaint(Color.LIGHT_GRAY);
        chart.getPlot().setOutlineVisible(true);

        CategoryPlot plot = (CategoryPlot) chart.getPlot();
        CategoryAxis xAxis = (CategoryAxis) chart.getCategoryPlot().getDomainAxis();
        xAxis.setCategoryLabelPositions(CategoryLabelPositions.UP_90); // Rotate labels for better visibility
        xAxis.setTickMarksVisible(false); // Hide tick marks
        plot.setDomainGridlinesVisible(true); // Show vertical grid lines
        plot.setRangeGridlinesVisible(true);  // Show horizontal grid lines
        plot.setRangeMinorGridlinesVisible(true);

        NumberAxis rangeAxis = (NumberAxis) plot.getRangeAxis();
        rangeAxis.setNumberFormatOverride(new DecimalFormat("#,###"));
        rangeAxis.setAutoRange(true);

        // Rotate the labels
        xAxis.setCategoryLabelPositions(CategoryLabelPositions.UP_45);

        // Adjust the label font
        xAxis.setTickLabelFont(new Font("SansSerif", Font.PLAIN, 12));

        return chart;
    }

Here, I want the labels to be complete vertical manner just like the following graph

image

sudarshansb143 commented 6 months ago

@trashgod I hope this will help you to understand the usecase. I'm a bit constrained with width also. I'm using A4 page to generate charts hence I'm keeping height width as follow :

    if (!tokenDistributionOverTimeDtos.isEmpty()) {
            document.newPage();
            JFreeChart chart = generateGraphForTokensIn24hrs(tokenDistributionOverTimeDtos, "Ticket Distribution Over 24 Hour Timeline", "Time", "Number of Tokens");
            BufferedImage chartImage = chart.createBufferedImage(500, 400);
            ByteArrayOutputStream chartOut = new ByteArrayOutputStream();
            ChartUtils.writeBufferedImageAsPNG(chartOut, chartImage);
            com.lowagie.text.Element pdfImage = com.lowagie.text.Image.getInstance(chartOut.toByteArray());
            document.add(pdfImage);
        }
trashgod commented 6 months ago

It looks like you're preparing PDFs on demand. As far as appearance, why not use a larger BufferedImage for better resolution? You are setting the desired tick label orientation, but I'm confused about setting it first to UP_90 and then to UP_45. As for displaying a days worth of data, it looks like you can use either DefaultCategoryDataset or SlidingCategoryDataset, whichever is more convenient.

sudarshansb143 commented 6 months ago

@trashgod Increasing BufferedImage resulting chart overflow out of page hence I after trial and error i kept the size as 500, 400 and regarding usage of SlidingCategoryDataset can you provide a usage reference here ?

trashgod commented 6 months ago

I didn't find any SlidingCategoryDataset examples, but I usually rely on the API to set the first index and count.

sudarshansb143 commented 6 months ago

Ok. But, in this case it is managing columns very well only thing I wanted to improve is the X-axis Labels. I'm focusing on this to improve label visibility. But, if we can't do this with then it might be a limitation

trashgod commented 6 months ago

Yes, crowded charts are a perennial problem. To the extent that this is a user interface issue, SlidingCategoryDataset is the cleanest way to focus on a chosen subset of the data. In the context of PDFs, you might construct a series of charts based on user input.