tools overlay

Discussion about JFreeChart related to stockmarket charts.
Locked
uls
Posts: 25
Joined: Fri Oct 17, 2003 10:46 am

tools overlay

Post by uls » Fri Oct 17, 2003 11:32 am

hi,
can i somehow overlay graphic elements without changing the source code? i think of cursors, lines and other things ...
thanks in advance

kingguppy
Posts: 37
Joined: Mon Apr 07, 2003 9:52 pm
Location: Wuppertal, Germany

Post by kingguppy » Tue Nov 04, 2003 6:58 pm

Yes, you can.

XYPlot allows you to set secondary datasets, with their own renderers. For example, to add a line to the plot, you could add a secondary dataset and set its renderer to StandardXYItemRenderer. Then add a series with two items (one for the start- and one for the end-point) for each line you want overlaid.

uls
Posts: 25
Joined: Fri Oct 17, 2003 10:46 am

performance would be toooo slow

Post by uls » Mon Nov 10, 2003 2:26 pm

this solution is ok to add lines in a static chart, but what if i want to ta add lines underneath the cursor? adding and removing lines whenever i move the mouse is no solution ... auto-scaling and all those (annoying and cool at the same time) features make it very slow.

I need to add items directly into the rendering pipeline of jfreechart ...

kingguppy
Posts: 37
Joined: Mon Apr 07, 2003 9:52 pm
Location: Wuppertal, Germany

Post by kingguppy » Mon Nov 10, 2003 2:41 pm

Basically, JFreeChart always redraws everything. I've been working on some optimisations to make "redrawing everything" happen significantly faster, but when a large set of data is being shown it still happens too slowly for real-trime drawing of tools (lines, etc.) to be feasible. One thing you could try is xoring shapes onto the component's Graphics, so you don't need to redraw the whole chart. This is the approach taken for the trace lines and the zoom rectangle in ChartPanel.

uls
Posts: 25
Joined: Fri Oct 17, 2003 10:46 am

Post by uls » Mon Nov 10, 2003 5:30 pm

i thought about drawing the ChartPanel manually onto a graphics object. but right now i have another task ... i want to specify which timespan to draw ... but can't seem to find anything on this ...

kingguppy
Posts: 37
Joined: Mon Apr 07, 2003 9:52 pm
Location: Wuppertal, Germany

Post by kingguppy » Mon Nov 10, 2003 5:52 pm

The ChartPanel already does the rendering of the chart directly to the Graphics object. For a simple XYPlot, it goes something like this (simplified):

<code>
ChartPanel.paintComponent() ->
|- Create the Graphics2D object
|- Handle buffering
|- Work out scaling
|- JFreeChart.draw() ->
|- Set rendering hints
|- Handle clipping
|- Render chart background, border and title
|- XYPlot.draw() ->
|- Render plot background
|- Render axes
|- XYRenderer.draw(Domain|Range)TickBands()
|- XYRenderer.draw(Domain|Range)Gridlines()
|- for each data item: XYRenderer.drawItem()
</code>

As for specifying which timespan to draw, use "setRange()" on your domain axis (which presumably is a DateAxis).

kingguppy
Posts: 37
Joined: Mon Apr 07, 2003 9:52 pm
Location: Wuppertal, Germany

Post by kingguppy » Mon Nov 10, 2003 5:55 pm

(Reposted since I messed up the BBCode, sorry - Preview is your friend)

The ChartPanel already does the rendering of the chart directly to the Graphics object. For a simple XYPlot, it goes something like this (simplified):

Code: Select all

ChartPanel.paintComponent() ->
|- Create the Graphics2D object
|- Handle buffering
|- Work out scaling
|- JFreeChart.draw() ->
    |- Set rendering hints
    |- Handle clipping
    |- Render chart background, border and title
    |- XYPlot.draw() ->
        |- Render plot background
        |- Render axes
        |- XYRenderer.draw(Domain|Range)TickBands()
        |- XYRenderer.draw(Domain|Range)Gridlines()
        |- for each data item: XYRenderer.drawItem()
As for specifying which timespan to draw, use "setRange()" on your domain axis (which presumably is a DateAxis).

uls
Posts: 25
Joined: Fri Oct 17, 2003 10:46 am

Post by uls » Mon Nov 10, 2003 6:02 pm

kingguppy wrote:(Reposted since I messed up the BBCode, sorry - Preview is your friend)


As for specifying which timespan to draw, use "setRange()" on your domain axis (which presumably is a DateAxis).
setRange() is a member of org.jfree.chart.axis.ValueAxis (i.e.) - sorry to be so lame, but i can't get a grip on how to set the range for my data or chart. i can't find a setAxis/setRange in
JFreeChart chart; TimeSeriesCollection xyseries=new TimeSeriesCollection(); TimeSeries x1; or ChartPanel chartPanel;


can you tell me where i can find it?

about the other thing of drawing, i guess i'll simply enable the markers and draw the chart onto a jpanel manually - with grabbing the mouse events and handing them (if necesary) to the underlying chartPanel - it makes most sense to me - the clicks i think i can convert through this convert function i found in chartpanel ....

kingguppy
Posts: 37
Joined: Mon Apr 07, 2003 9:52 pm
Location: Wuppertal, Germany

Post by kingguppy » Mon Nov 10, 2003 6:12 pm

Can you please post your chart creation code? I guess you're using some constructors that automatically create axes, etc. (which is fine, but I don't know where your Plot is created).

The ChartPanel does some useful things with the mouse events (such as zooming and crosshairs). It also determines which "ChartEntity" is under the pointer, which can be very useful (once you've drawn your lines, I'm guessing you're going to want to be able to click on them to manipulate them). Subclassing ChartPanel might be the way to go. Indidently, I extended the mouse input support throughout JFreeChart, I'm getting a load of patches together to submit to David in the near future.

uls
Posts: 25
Joined: Fri Oct 17, 2003 10:46 am

Post by uls » Mon Nov 10, 2003 9:18 pm

that's the main chart class ...

Code: Select all


import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.*;
import java.text.*;


import javax.swing.*;
import javax.swing.event.*;
import java.awt.event.*;


import org.jfree.chart.ChartFactory;
import org.jfree.chart.ChartPanel;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.plot.PlotOrientation;
import org.jfree.data.time.FixedMillisecond;
import org.jfree.data.time.TimeSeries;
import org.jfree.data.time.TimeSeriesCollection;
import org.jfree.data.time.*;

import plugin.*;

public class ChartWindow extends JInternalFrame implements DataRecipient{
	String symbol=""; //contains the symbol for this chartwindow
	Starter s; // the pointer to Start
	JPanel toolbar; //a panel supposed to be a toolbar
	JComboBox jcb; //the dropdown containing the symbols
	JComboBox timerange; //the dropdown for the complete timerange
	JComboBox compression; //the dropdown for the duration
	
	JFreeChart chart;
	//JFreeChart datasets
	TimeSeriesCollection xyseries=new TimeSeriesCollection(); // XYSeriesCollection (see ttp://www.jfree.org/jfreechart/javadoc/org/jfree/data/XYSeriesCollection.html)
	TimeSeries x1;
	TimeSeries bollinger_top;
	TimeSeries bollinger_bottom;
	
	TimeSeries average1;
	TimeSeries average2;

	Vector components=new Vector(); //contains plugin components
	
	//helper variables
	int av1=5;
	
	/**
	 * constructor. Constructs the chart window. 
	 * @param s
	 * @param symbol
	 */
	
	public ChartWindow(Starter s, String symbol){
		super(symbol, true, true, true, true);
		this.symbol=symbol;
		this.s=s;
		//build a toolbar
		toolbar=new JPanel();
		this.getContentPane().add(toolbar, BorderLayout.NORTH);
		buildToolbar(toolbar);

		//
		x1=new TimeSeries("symbol", FixedMillisecond.class);
		average1=new TimeSeries("av1", FixedMillisecond.class);
		xyseries.addSeries(x1);
		xyseries.addSeries(average1);
		System.out.println("Populated.");
		//
		JFreeChart chart = createChart(xyseries);
		System.out.println("constr.");		
		chart.getXYPlot().setOrientation(PlotOrientation.VERTICAL);
		chart.setAntiAlias(false);
		chart.setBackgroundPaint(new Color(255,255,255));
		
		ChartPanel chartPanel = new ChartPanel(chart);
		chartPanel.setPreferredSize(new java.awt.Dimension(500, 270));
		//
		this.getContentPane().add(chartPanel, BorderLayout.CENTER);	
		s.desktop.add(this);
		System.out.println("add");
		setVisible(true);
		setSize(new Dimension(400,300));
				
		chartPanel.setPopupMenu(buildPopupMenu());
		chartPanel.setMouseZoomable(true);
		chartPanel.setHorizontalAxisTrace(true);
		chartPanel.setVerticalAxisTrace(true);
		
		//initialize some plugins
		RSI rsi=new RSI();
		components.add(rsi);
		this.getContentPane().add(rsi.visualComponent(), BorderLayout.SOUTH);
		
		
			
	}
	
	
	/**
	 * builds and returns the popup menu
	 * @return
	 */
	public JPopupMenu buildPopupMenu(){
		JPopupMenu m=new JPopupMenu();
		
		//the averages menu
		JMenu a=new JMenu("Averages");
		
		JMenu a1=new JMenu("Average 1");
		
		JMenuItem a11=a1.add("5 units");
		a11.addActionListener(new ActionListener(){
			public void actionPerformed(ActionEvent e){
				setAV1(5);
			}
		});
		JMenuItem a12=a1.add("10 units");
		a12.addActionListener(new ActionListener(){
					public void actionPerformed(ActionEvent e){
						setAV1(10);
					}
				});
		JMenuItem a13=a1.add("50 units");
		a13.addActionListener(new ActionListener(){
					public void actionPerformed(ActionEvent e){
						setAV1(50);
					}
				});
		a.add(a1);
		
		JMenu a2=new JMenu("Average 2");
		
		JMenuItem a21=a2.add("5 units");
		JMenuItem a22=a2.add("10 units");
		JMenuItem a23=a2.add("50 units");
		a.add(a2);
		
		m.add(a);
		//Indicator menu
		
		
		return m;
	}
	
	public void setAV1(int d){
		av1=d;
	}
	
	/**
	 * temporal helper function
	 * 
	 * @param dataset
	 * @return
	 */
	private JFreeChart createChart(TimeSeriesCollection dataset) {
		chart = ChartFactory.createTimeSeriesChart(
			symbol,
			"", 
			"",
			dataset, 
			false, false, false
		);
		
		return chart;        
	}
	
	/**
	 * populates toolbar
	 * 
	 *  @author uls
	 *
	 */
	void buildToolbar(JPanel t){
		//set up the layout ground
		GridBagLayout gbl=new GridBagLayout();
		GridBagConstraints gbc=new GridBagConstraints();
		t.setLayout(gbl);
		int i=0;
		gbc.gridy=0;
		gbc.fill=GridBagConstraints.BOTH;
		gbc.anchor=GridBagConstraints.NORTHWEST;
		//populate the bar
		//add the float/defloat button
		gbc.gridx=i;
		gbc.weightx=0;
		JButton floa=new JButton("float");
		gbl.setConstraints(floa, gbc);
		toolbar.add(floa);
		//add the cymbol chooser
		i++;
		gbc.gridx=i;
		gbc.weightx=1;
		jcb=new JComboBox(new Object[]{"^GDAXI", "MUVGN.DE"});
		jcb.setEditable(true);
		gbl.setConstraints(jcb, gbc);
		toolbar.add(jcb);
		//add the set button
		i++;
		JButton set=new JButton("set");
		gbc.gridx=i;
		gbc.weightx=0;
		gbl.setConstraints(set, gbc);
		toolbar.add(set);
		set.addActionListener(new ActionListener(){
			public void actionPerformed(ActionEvent e){
				String s=(String)jcb.getSelectedItem();
				chart.setTitle(s);
				symbol=s;
				populate();						
			}
		});
		//add the print button
	
		i++;
		JButton print=new JButton("print");
		gbc.gridx=i;
		gbc.weightx=0;
		gbl.setConstraints(print, gbc);
		toolbar.add(print);
	
		//add a drop down with to set the time range
		timerange=new JComboBox(new Object[]{"2 d", "5 d", "10 d", "1 m", "3 m", "6 m", "1 y", "2 y", "5 y", "max"});
		i++;
		gbc.gridx=i;
		gbc.weightx=0;
		gbl.setConstraints(timerange, gbc);
		toolbar.add(timerange);
		
		
	
		//add a drop down with to set the compression
		compression=new JComboBox(new Object[]{"ticks", "1 min", "5 min", "15 min", "30 min", "60 min", "daily", "weekly", "monthly"});
		 i++;
		gbc.gridx=i;
		gbc.weightx=0;
		gbl.setConstraints(compression, gbc);
		toolbar.add(compression);
		
	}
		
	// data recipient interfaces
	/**w
	 * implements DataRecipients data function
	  * @see jahangir.DataRecipient#data(jahangir.Tick) 
	 */
	int i=0;
	public void data(Tick t){
//		Date d=new Date();
		x1.add(new FixedMillisecond(t.date), t.value);
		
		calcAverage1(0,t.date);
		
		for(int i=0;i<components.size();i++){
			Plugin p=(Plugin)components.elementAt(i);
			p.data(t);
		}
		
		i++;
	}
	
	/**
	 * implements DataRecipient's getSymbol function
	 * @see jahangir.DataRecipient#getSymbol()
	 * 
	 */
	public String getSymbol(){
		return symbol;
	}
	
	/**
	 * reads the settings from the jcomboboxes timerange and compression and
	 * populates the chart. 
	 *
	 */
	
	public void populate(){
		int n=timerange.getSelectedIndex();
		int comp=compression.getSelectedIndex();	
		String startdate="";
		String symbol=(String)jcb.getSelectedItem();
		SimpleDateFormat sdf=new SimpleDateFormat("yyyyMMddHHmmss");

		GregorianCalendar cal = new GregorianCalendar() ;

		switch(n){
			
			case 0:
							//two day range
							cal.add(cal.DATE, -2) ;									
							break;
							
			case 1:
							//five day range
							cal.add(cal.DATE, -5) ;
							break;
			case 2:
							//ten day range
							cal.add(cal.DATE, -10) ;
							break;
			case 3:
							//one month
							cal.add(cal.MONTH, -1);
							break;
			case 4:
							//three month
							cal.add(cal.MONTH, -3); 
							break;
			case 5:
							//six month
							cal.add(cal.MONTH, -6);
							break;
			case 6:
							//one year
							cal.add(cal.YEAR, -1);
							break;
			case 7:
							//two year
							cal.add(cal.YEAR, -2); 
							break;
			case 8:
							//five year
							cal.add(cal.YEAR, -5);
							break;
			case 9:
							//max range
							cal.add(cal.YEAR, -1000);
							break;
			
			default:
			break;
		}
	
		startdate=sdf.format(cal.getTime());
		
		Vector v=s.dal.getTicks(symbol, startdate, "");
		Tick t;
		try{
			for(int i=0;i<v.size();i++){
				t=(Tick)v.elementAt(i);
				//System.out.println(t.timestamp);
				x1.add(new FixedMillisecond(sdf.parse(t.timestamp)), t.value);
				calcAverage1(0, sdf.parse(t.timestamp));		
			}
		}
		catch(Exception e){
		}	
	}
	
	public void calcAverage1(int d, Date timestamp){	
		try{
			double v=0;
			
			List l1=x1.getItems();
			
			for(int i=d;i<d+av1;i++){
				v+=x1.getValue(x1.getItemCount()-(av1-i)).doubleValue();
				
			}
			average1.add(new FixedMillisecond(timestamp), v/av1);

			
		}
		catch(Exception e){
			e.printStackTrace();
			//System.out.println(timestamp.toString());
		}
	}

	
	/**
	 * actually recalculates the first average (necessary when we change the average settings)
	 * @param d
	 */
	public void recalculateAverage1(int d){
			
		average1.delete(0, average1.getItemCount());
		
	}
	
	public void addMiniChart(Plugin p){
		
	}
	
	
	
}


kingguppy
Posts: 37
Joined: Mon Apr 07, 2003 9:52 pm
Location: Wuppertal, Germany

Post by kingguppy » Wed Nov 12, 2003 10:13 am

You can use chart.getXYPlot().getDomainAxis() to get the DateAxis (which is a subclass of ValueAxis, and is the one you want to do a setRange() on).

Locked