1

I'm trying to create a candlestick chart, which user can zoom and pan, exacly like this TradingView chart.

// Domain axis
DateAxis dateAxis = new DateAxis();

// Range axis
NumberAxis priceAxis = new NumberAxis("Price");
priceAxis.setRangeType(RangeType.POSITIVE);
priceAxis.setAutoRange(true);
priceAxis.setAutoRangeIncludesZero(false);

// Plot
CandlestickRenderer renderer = new CandlestickRenderer();
XYPlot plot = new XYPlot(mPriceDataSet, dateAxis, priceAxis, renderer);
plot.setDomainPannable(true);
plot.setRangePannable(false);

// Chart
mChart = new JFreeChart("Price History", plot);

which mPriceDataSet is my data set and is of type DefaultOHLCDataset. And here is how I create the ChartPanel:

ChartPanel chartPanel = new ChartPanel(mChart);
chartPanel.setMouseZoomable(true);
chartPanel.setRangeZoomable(false);
chartPanel.setMouseWheelEnabled(true);

Behavior: When the chart is rendered everything is okay, however when user zooms and/or pans the view port, the range axis doesn't scale and shows the very first range.

Desired Behavior: While zooming/panning range axis should dynamically set its range. Exactly like TrandingView charts.


MCVE

I've created an MCVE based on your Sample8. To reproduce the problem, just run the app and select the candle at 12:5 using mouse dragging. As you can see, the chart after zoom/selection will not be auto ranged and user has to manually do so using the context menu.

import org.jfree.chart.ChartFactory;
import org.jfree.chart.ChartPanel;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.axis.*;
import org.jfree.chart.plot.PlotOrientation;
import org.jfree.chart.renderer.xy.CandlestickRenderer;
import org.jfree.data.time.Minute;
import org.jfree.data.time.ohlc.OHLCSeries;
import org.jfree.data.time.ohlc.OHLCSeriesCollection;

import javax.swing.*;
import java.awt.*;
import java.text.SimpleDateFormat;

public class AutoRangeTestApp {

    private final ChartPanel chartPanel;
    private final OHLCSeriesCollection seriesCollection = new OHLCSeriesCollection();

    public AutoRangeTestApp() {
        JFreeChart chart = ChartFactory.createCandlestickChart(
                "Sample8", "Time", "Price", seriesCollection, true);
        chart.getXYPlot().setOrientation(PlotOrientation.VERTICAL);
        chart.getXYPlot().setDomainPannable(true);
        CandlestickRenderer r = (CandlestickRenderer) chart.getXYPlot().getRenderer();
        r.setAutoWidthMethod(CandlestickRenderer.WIDTHMETHOD_SMALLEST);

        // Y-axis
        NumberAxis numberAxis = (NumberAxis) chart.getXYPlot().getRangeAxis();
        numberAxis.setAutoRangeIncludesZero(false);
        numberAxis.setAutoRangeStickyZero(false);

        // X-axis
        DateAxis dateAxis = (DateAxis) chart.getXYPlot().getDomainAxis();
        dateAxis.setDateFormatOverride(new SimpleDateFormat("HH:mm"));
        dateAxis.setTickUnit(new DateTickUnit(DateTickUnitType.MINUTE, 1));
        dateAxis.setTickMarkPosition(DateTickMarkPosition.MIDDLE);

        // chartPanel
        chartPanel = new ChartPanel(chart) {
            @Override
            public Dimension getPreferredSize() {
                return new Dimension(800, 400);
            }
        };
        chartPanel.setMouseWheelEnabled(true);
        chartPanel.setMouseZoomable(true);
        chartPanel.setRangeZoomable(false);

        // series
        addSeries1();
    }

    private void addSeries1() {
        OHLCSeries series = new OHLCSeries("One");
        series.add(new Minute(0, 12, 1, 1, 2014), 97.23D, 98.47D, 97.15D, 97.43);
        series.add(new Minute(1, 12, 1, 1, 2014), 96.47D, 98.81D, 96.27D, 97.49);
        series.add(new Minute(2, 12, 1, 1, 2014), 95.88D, 96.87D, 96.11D, 96.75);
        series.add(new Minute(3, 12, 1, 1, 2014), 96.05D, 96.94D, 95.89D, 96.59);
        series.add(new Minute(4, 12, 1, 1, 2014), 95.92D, 97.00D, 95.69D, 96.92);
        series.add(new Minute(5, 12, 1, 1, 2014), 96.34D, 96.71D, 95.68D, 96.70);
        series.add(new Minute(6, 12, 1, 1, 2014), 96.65D, 96.87D, 94.91D, 96.38);
        series.add(new Minute(7, 12, 1, 1, 2014), 97.75D, 98.20D, 96.90D, 97.24);
        series.add(new Minute(8, 12, 1, 1, 2014), 97.72D, 98.47D, 97.35D, 97.64);
        series.add(new Minute(9, 12, 1, 1, 2014), 98.30D, 98.77D, 97.56D, 97.99);
        seriesCollection.addSeries(series);
    }

    public ChartPanel getChartPanel() {
        return chartPanel;
    }

    public static void main(String[] args) {
        EventQueue.invokeLater(new Runnable() {

            public void run() {
                AutoRangeTestApp app = new AutoRangeTestApp();
                JFrame frame = new JFrame();
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.add(app.getChartPanel());
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }
}
frogatto
  • 28,539
  • 11
  • 83
  • 129
  • Using this complete [example](https://stackoverflow.com/a/27611246/230513), I can't reproduce this with JFreeChart 1.5. See also [*Initial Threads*](http://docs.oracle.com/javase/tutorial/uiswing/concurrency/initial.html). – trashgod Jan 31 '19 at 08:59
  • @trashgod I also tested with version 1.5, the same behavior. When I pans the chart using ctrl + drag, auto ranging on Y axis does not work and I have to manually auto range it using `ChartPanel`'s context menu. – frogatto Jan 31 '19 at 19:16
  • I use option-drag on Mac, but I don't see anything unexpected with [`Sample8`](https://stackoverflow.com/a/27611246/230513) or the demo; can you post a [mcve]? – trashgod Jan 31 '19 at 22:41
  • @trashgod I've added a MCVE to my question. – frogatto Feb 02 '19 at 10:01
  • Sorry, I don't see anything untoward on Mac 1.8.0_201, Java 1.8.0_201, JFreeChart 1.5. If I inadvertently mouse-wheel zoom as I drag, the new zoom takes effect, and I can auto range using either the context menu or the drag-left mouse gesture – trashgod Feb 02 '19 at 12:53
  • @trashgod Zoom functionality is okay. I've no problem with. I just want to when zoom takes effect, auto range function automatically run. i.e. not having the user to manually auto range through context menu. – frogatto Feb 02 '19 at 13:15
  • Maybe [`restoreAutoBounds()`](https://stackoverflow.com/a/5522583/230513) on `mouseReleased()`? – trashgod Feb 02 '19 at 13:20
  • @trashgod I just tested `restoreAutoBounds()`. It resets the zoom, however I need to preserve the zoom and auto range the portion of data that is visible. – frogatto Feb 02 '19 at 13:35
  • Sadly, I'm nonplussed; at any given zoom level, I see the domain range adjusting continually and correctly as I option-drag; new domain _ticks_ scroll into view on one end as old ones disappear on the other. To see new _data_ scroll into view, you may want to look at [`SlidingXYDataset`](https://stackoverflow.com/search?tab=newest&q=user%3a230513%20%5bjfreechart%5d%20slidingxydataset). – trashgod Feb 02 '19 at 17:04

0 Answers0