/*
This file is part of jTotus.
jTotus is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
jTotus is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with jTotus. If not, see <http://www.gnu.org/licenses/>.
*
*
* http://tutorials.jenkov.com/java-collections/navigableset.html
*/
package org.jtotus.gui.graph;
import java.awt.Color;
import java.awt.Container;
import java.awt.Font;
import java.text.SimpleDateFormat;
import java.util.HashMap;
import java.util.Iterator;
import org.jfree.chart.ChartPanel;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.annotations.XYTextAnnotation;
import org.jfree.chart.axis.DateAxis;
import org.jfree.chart.axis.NumberAxis;
import org.jfree.chart.plot.CombinedDomainXYPlot;
import org.jfree.chart.plot.XYPlot;
import org.jfree.chart.renderer.xy.XYBubbleRenderer;
import org.jfree.chart.renderer.xy.XYDotRenderer;
import org.jfree.chart.renderer.xy.XYItemRenderer;
import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer;
import org.jfree.chart.renderer.xy.XYStepRenderer;
import org.jfree.data.time.Day;
import org.jfree.data.time.TimeSeries;
import org.jfree.data.time.TimeSeriesCollection;
import org.joda.time.LocalDate;
import org.jtotus.common.StockUnit;
/**
*
* @author Evgeni Kappinen
*/
public class GraphPrinter {
// Create a chart
private String mainPlotName = "MainPlot";
private JFreeChart mainChart = null;
private ChartPanel mainPanel = null;
private CombinedDomainXYPlot mainPlot = null;
private HashMap<String, TimeSeries> seriesMap = null;
private HashMap<String, XYPlot> plotMap = null;
public GraphPrinter(String reviewTarget) {
seriesMap = new HashMap<String, TimeSeries>();
plotMap = new HashMap<String, XYPlot>();
mainChart = this.createChart(reviewTarget);
if (mainChart == null) {
return;
}
mainPanel = new ChartPanel(mainChart, false);
mainPanel.setMouseZoomable(true, false);
mainPanel.setFillZoomRectangle(true);
mainPanel.setMouseWheelEnabled(true);
}
private JFreeChart createChart(String title) {
// valueAxis.setAutoRangeMinimumSize(1);
DateAxis domain = new DateAxis("Date");
domain.setDateFormatOverride(new SimpleDateFormat("dd-MM-yyyy"));
mainPlot = new CombinedDomainXYPlot(domain);
mainPlot.setGap(4.0);
//mainPlot.setOrientation(PlotOrientation.HORIZONTAL);
mainPlot.setBackgroundPaint(Color.lightGray);
mainPlot.setRangePannable(true);
mainPlot.setDomainGridlinesVisible(true);
mainPlot.setOutlineVisible(true);
mainPlot.setDomainCrosshairVisible(true);
mainPlot.setRangeMinorGridlinesVisible(true);
JFreeChart chart = new JFreeChart(title,
JFreeChart.DEFAULT_TITLE_FONT,
mainPlot,
true);
chart.setBackgroundPaint(Color.white);
return chart;
}
private XYItemRenderer getDefaultLine() {
XYLineAndShapeRenderer renderer = new XYLineAndShapeRenderer();
renderer.setSeriesLinesVisible(1, true);
renderer.setSeriesShapesVisible(1, true);
return renderer;
}
private XYItemRenderer getdDefualtBuble() {
XYBubbleRenderer renderer = new XYBubbleRenderer();
renderer.setAutoPopulateSeriesOutlineStroke(false);
return renderer;
}
private XYItemRenderer getdDefualtDot() {
XYDotRenderer renderer = new XYDotRenderer();
return renderer;
}
private XYItemRenderer getDefualtCandleStick() {
XYStepRenderer renderer = new XYStepRenderer();
return renderer;
}
public XYItemRenderer getRenderer(GraphSeriesType type) {
if (type == null) {
return this.getDefaultLine();
}
switch (type) {
case SIMPLELINE:
return this.getDefaultLine();
case SIMPLEBUBLE:
return this.getdDefualtBuble();
case SIMPLECANDLESTICK:
return this.getDefualtCandleStick();
case SIMPLEDOT:
return this.getdDefualtDot();
default:
return this.getDefaultLine();
}
}
public Container getContainer() {
return mainPanel;
}
public synchronized void cleanChart() {
Iterator<TimeSeries> serIter = seriesMap.values().iterator();
while (serIter.hasNext()) {
serIter.next().clear();
}
return;
}
private XYPlot createSubPlot(String plotName, GraphSeriesType type) {
final TimeSeriesCollection collection = new TimeSeriesCollection();
final NumberAxis range = new NumberAxis("Value");
final XYPlot newSubPlot = new XYPlot(collection,
null,
range,
this.getRenderer(type));
newSubPlot.setDomainCrosshairVisible(true);
newSubPlot.setRangeCrosshairVisible(true);
newSubPlot.setDomainGridlinesVisible(true);
if(!plotMap.containsKey(plotName)) {
plotMap.put(plotName, newSubPlot);
mainPlot.add(newSubPlot);
return newSubPlot;
}
return null;
}
private XYPlot fetchPlot(String name) {
if (name == null) {
name = this.mainPlotName;
}
return plotMap.get(name);
}
private TimeSeries createTimeSeries(GraphPacket packet) {
XYPlot addSeriesToPlot = null;
//TimeSeries with packet.seriesTitle is not found.
//Create MainPlot for the rest of the packets.
//This plot will be used if in packet.plotName is null
if (plotMap.isEmpty()) {
addSeriesToPlot = this.createSubPlot(this.mainPlotName, GraphSeriesType.SIMPLELINE);
}
//If packet.plotName is null, lets use MainPlot
if (packet.plotName != null) {
addSeriesToPlot = plotMap.get(packet.plotName);
if (addSeriesToPlot == null) {
addSeriesToPlot = this.createSubPlot(packet.plotName, packet.type);
}
}
//If we are in this function, this means that
//unique name of timeseries is not found in seriesMap.
TimeSeries newSeries = new TimeSeries(packet.seriesTitle);
seriesMap.put(packet.seriesTitle, newSeries);
return newSeries;
}
private Day localDateToDay(LocalDate date) {
Day tmpDay = new Day(date.getDayOfMonth(),
date.getMonthOfYear(),
date.getYear());
return tmpDay;
}
/*
* Creates new plot if neeed and adds values to time series
*
* @param packet Packet Object from blocking queus
*/
public void drawSeries(GraphPacket packet) {
TimeSeries series = null;
if (seriesMap.containsKey(packet.seriesTitle)) { //Series Exists
series = seriesMap.get(packet.seriesTitle);
}else {
series = this.createTimeSeries(packet);
//FIXME: is it prefereable to add series after points are added ?
TimeSeriesCollection collection = (TimeSeriesCollection) this.fetchPlot(packet.plotName).getDataset();
collection.addSeries(series);
}
Iterator<StockUnit> iter = packet.results.iterator();
while (iter.hasNext()) {
//update if already existing series
StockUnit unit = iter.next();
if(unit.annotation != null) {
final XYTextAnnotation annotation = new XYTextAnnotation(unit.annotation,
this.localDateToDay(unit.date).getMiddleMillisecond(),
unit.value);
annotation.setFont(new Font("SansSerif", Font.PLAIN, 9));
this.fetchPlot(packet.plotName).addAnnotation(annotation);
}
series.addOrUpdate(this.localDateToDay(unit.date),
unit.value);
}
}
}