package com.dgex.offspring.application.ui.home; import java.awt.BasicStroke; import java.text.DecimalFormat; import java.text.SimpleDateFormat; import java.util.Date; import java.util.List; import org.apache.log4j.Logger; import org.eclipse.e4.ui.di.UISynchronize; import org.eclipse.swt.SWT; import org.eclipse.swt.layout.FillLayout; import org.eclipse.swt.widgets.Composite; import org.jfree.chart.ChartFactory; import org.jfree.chart.JFreeChart; import org.jfree.chart.axis.NumberAxis; import org.jfree.chart.labels.StandardXYToolTipGenerator; import org.jfree.chart.plot.XYPlot; import org.jfree.chart.renderer.xy.XYBarRenderer; import org.jfree.chart.renderer.xy.XYItemRenderer; import org.jfree.data.time.Hour; import org.jfree.data.time.TimeSeries; import org.jfree.data.time.TimeSeriesCollection; import org.jfree.data.xy.IntervalXYDataset; import org.jfree.data.xy.XYDataset; import org.jfree.experimental.chart.swt.ChartComposite; import com.dgex.offspring.dataprovider.service.IDataProviderListener; import com.dgex.offspring.providers.dgex.DGEXCurrentRateProvider; import com.dgex.offspring.providers.service.Currencies; import com.dgex.offspring.providers.service.ICurrency; import com.dgex.offspring.providers.service.IRate; import com.dgex.offspring.providers.service.IRateProvider; // CategoryDataset dataset = new CategoryDataset(); // // Multi axes chart (price + volume) // https://code.google.com/p/grape/source/browse/trunk/code/dealWithExcel/src/shai/jfreechart/PriceVolumeDemo1.java?r=50 // // Time series seems to show price and time // https://github.com/anilbharadia/jFreeChart-Examples/blob/master/src/TimeSeriesDemo1.java // // Add a moving avarage line to time series // http://www.java2s.com/Code/Java/Chart/JFreeChartTimeSeriesDemo8.htm // // Stacked Area chart (two area charts) // https://code.google.com/p/socr/source/browse/trunk/SOCR2.0/src/edu/ucla/stat/SOCR/chart/demo/StackedXYAreaChartDemo1.java?spec=svn70&r=67 public class OffspringChartComposite extends Composite { private static Logger logger = Logger .getLogger(OffspringChartComposite.class); private IRateProvider rateProvider; private IDataProviderListener listener; private JFreeChart chart; private final ChartComposite chartComposite; private final UISynchronize sync; private ICurrency base; private ICurrency quote; public OffspringChartComposite(Composite parent, int style, UISynchronize sync) { super(parent, style); setLayout(new FillLayout()); this.sync = sync; chartComposite = new ChartComposite(this, SWT.BORDER, null, true); chartComposite.setDisplayToolTips(true); chartComposite.setHorizontalAxisTrace(true); chartComposite.setVerticalAxisTrace(true); } public JFreeChart getChart() { return chart; } private String createChartTitle() { String title = ""; List<IRate> rates = DGEXCurrentRateProvider.getInstance().getRates( Currencies.BTC, Currencies.NXT); if (rates.size() > 0) { IRate rate = rates.get(0); String price = new DecimalFormat("0.00000000").format(rate.getPrice()); title = "DGEX.com BTC / NXT 3hr moving average\n" + "Last: " + price + " " + new Date().toLocaleString(); } return title; } public void refresh(final IRateProvider provider, final ICurrency base, final ICurrency quote) { if (listener != null && rateProvider != null) { rateProvider.removeDataProviderListener(listener); listener = null; } this.rateProvider = provider; this.base = base; this.quote = quote; String temp = createChartTitle(); final String title = temp.isEmpty() ? (provider.getLabel() + " " + base.getId() + "/" + quote.getId()) : temp; chart = createChart(title); chartComposite.setChart(chart); chart.fireChartChanged(); listener = new IDataProviderListener() { @Override public void update() { sync.asyncExec(new Runnable() { @Override public void run() { String temp = createChartTitle(); final String title = temp.isEmpty() ? "DGEX.com BTC / NXT 3hr moving average" : temp; if (chartComposite != null && !chartComposite.isDisposed()) { chart = createChart(title); chartComposite.setChart(chart); chart.fireChartChanged(); } } }); } }; rateProvider.addDataProviderListener(listener); } public void setTitle(String title) { chart.setTitle(title); chart.fireChartChanged(); } private JFreeChart createChart(String title) { XYDataset priceDataset = createPriceDataset(); XYDataset volumeDataset = createVolumeDataset(); JFreeChart jfreechart = ChartFactory.createTimeSeriesChart(title, // title "Date", // x title "Price", // y title priceDataset, // dataset false, // legend true, // tooltips false); XYPlot xyplot = (XYPlot) jfreechart.getPlot(); NumberAxis numberaxis = (NumberAxis) xyplot.getRangeAxis(); numberaxis.setLowerMargin(0.40000000000000002D); DecimalFormat decimalformat = new DecimalFormat(quote.getDecimalFormat()); numberaxis.setNumberFormatOverride(decimalformat); if (volumeDataset != null) { XYItemRenderer xyitemrenderer = xyplot.getRenderer(); xyitemrenderer.setBaseToolTipGenerator(new StandardXYToolTipGenerator( "{0}: ({1}, {2})", new SimpleDateFormat("d-MMM-yyyy"), new DecimalFormat("0.00"))); xyitemrenderer.setSeriesStroke(0, new BasicStroke(3)); NumberAxis numberaxis1 = new NumberAxis("Volume"); numberaxis1.setUpperMargin(1.0D); xyplot.setRangeAxis(1, numberaxis1); xyplot.setDataset(1, volumeDataset); xyplot.setRangeAxis(1, numberaxis1); xyplot.mapDatasetToRangeAxis(1, 1); XYBarRenderer xybarrenderer = new XYBarRenderer(0.20000000000000001D); xybarrenderer.setBaseToolTipGenerator(new StandardXYToolTipGenerator( "{0}: ({1}, {2})", new SimpleDateFormat("d-MMM-yyyy"), new DecimalFormat("0,000.00"))); xyplot.setRenderer(1, xybarrenderer); } return jfreechart; } private XYDataset createPriceDataset() { List<IRate> rates = rateProvider.getRates(base, quote); TimeSeries series = new TimeSeries("Price"); if (rates != null) { for (IRate rate : rates) { Date date = new Date(rate.getTimestamp()); series.addOrUpdate(new Hour(date), rate.getPrice()); } } TimeSeriesCollection dataset = new TimeSeriesCollection(series); dataset.setDomainIsPointsInTime(true); return dataset; } private IntervalXYDataset createVolumeDataset() { List<IRate> rates = rateProvider.getRates(base, quote); TimeSeries series = new TimeSeries("Volume"); boolean non_zero_found = false; if (rates != null) { for (IRate rate : rates) { Date date = new Date(rate.getTimestamp()); double vol = rate.getTotalVol(); series.addOrUpdate(new Hour(date), vol); if (vol > 0) non_zero_found = true; } } if (non_zero_found == false) return null; TimeSeriesCollection dataset = new TimeSeriesCollection(series); dataset.setDomainIsPointsInTime(true); return dataset; } }