Hello,
Both of these sites have nice charting features of "animation" when the mouse moves over the chart itself. Can jfreechart support this type of implementation/feature?
An example is here. Move the mouse over chart itself and values are updated as the mouse moves.
finance.google.com/finance?q=msft
Thanks,
Jim
finance.yahoo and finance.google charting
As ChartPanel is a swing component you should be able to add almost any interactivity. JFreeChart does not provide this out of the box but it is possible. The best way is to extend ChartPanel and add your own animation. The example below just adds the mouse move animation (it is very rough, doesn't extend ChartPanel but should show what is possible). It is also possible to implement the mouse drag and animated zooming but thats a lot of code.
Code: Select all
import org.jfree.chart.ChartPanel;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.axis.DateAxis;
import org.jfree.chart.axis.NumberAxis;
import org.jfree.chart.axis.SegmentedTimeline;
import org.jfree.chart.entity.ChartEntity;
import org.jfree.chart.entity.EntityCollection;
import org.jfree.chart.entity.XYItemEntity;
import org.jfree.chart.labels.HighLowItemLabelGenerator;
import org.jfree.chart.plot.XYPlot;
import org.jfree.chart.renderer.xy.XYItemRenderer;
import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer;
import org.jfree.data.xy.AbstractXYDataset;
import org.jfree.data.xy.DefaultOHLCDataset;
import org.jfree.data.xy.OHLCDataItem;
import org.jfree.data.xy.XYDataset;
import javax.swing.*;
import java.awt.*;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionListener;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.URL;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.StringTokenizer;
public class MouseMoveDemo extends JFrame {
JLabel descriptionLabel = new JLabel(" ");
public MouseMoveDemo(String symbol) {
super("MouseMoveDemo");
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
DateAxis domainAxis = new DateAxis("Date");
NumberAxis rangeAxis = new NumberAxis("Price");
XYItemRenderer renderer = new XYLineAndShapeRenderer(true, false);
XYDataset dataset = getDataSet(symbol);
XYPlot mainPlot = new XYPlot(dataset, domainAxis, rangeAxis, renderer);
renderer.setSeriesPaint(0, Color.BLACK);
rangeAxis.setAutoRangeIncludesZero(false);
domainAxis.setTimeline( SegmentedTimeline.newMondayThroughFridayTimeline() );
JFreeChart chart = new JFreeChart(symbol, null, mainPlot, false);
ChartPanel chartPanel = new ChartPanel(chart);
chartPanel.setPreferredSize(new Dimension(600, 300));
this.add(chartPanel);
this.add(descriptionLabel, BorderLayout.NORTH);
this.pack();
addListener(chartPanel);
}
protected AbstractXYDataset getDataSet(String symbol) {
DefaultOHLCDataset result = null;
OHLCDataItem[] data;
data = getData(symbol);
return new DefaultOHLCDataset(symbol, data);
}
//This method uses yahoo finance to get the OHLC data
protected OHLCDataItem[] getData(String symbol) {
java.util.List<OHLCDataItem> dataItems = new ArrayList<OHLCDataItem>();
try {
String strUrl= "http://ichart.finance.yahoo.com/table.csv?s="+symbol+"&a=0&b=1&c=2008&d=3&e=30&f=2008&ignore=.csv";
URL url = new URL(strUrl);
BufferedReader in = new BufferedReader(new InputStreamReader(url.openStream()));
DateFormat df = new SimpleDateFormat("y-M-d");
String inputLine;
in.readLine();
while ((inputLine = in.readLine()) != null) {
StringTokenizer st = new StringTokenizer(inputLine, ",");
Date date = df.parse( st.nextToken() );
double open = Double.parseDouble( st.nextToken() );
double high = Double.parseDouble( st.nextToken() );
double low = Double.parseDouble( st.nextToken() );
double close = Double.parseDouble( st.nextToken() );
double volume = Double.parseDouble( st.nextToken() );
double adjClose = Double.parseDouble( st.nextToken() );
OHLCDataItem item = new OHLCDataItem(date, open, high, low, close, volume);
dataItems.add(item);
}
in.close();
}
catch (Exception e) {
e.printStackTrace();
}
Collections.reverse(dataItems);
return dataItems.toArray(new OHLCDataItem[dataItems.size()]);
}
////////////////////////////////////////////
//This is the code for the animation. It is very rough, for an idea only
//It does not take in to account chart orientation or scaling. (or repaint on zoom)
/////////////////////////////////////////////
protected ChartEntity prev = null;
protected void addListener(ChartPanel chartPanel){
chartPanel.addMouseMotionListener(new MouseMotionListener() {
public void mouseDragged(MouseEvent e) {
}
public void mouseMoved(MouseEvent e) {
ChartPanel chartPanel = (ChartPanel) e.getSource();
Insets insets = chartPanel.getInsets();
int x = (int) ((e.getX() - insets.left) / chartPanel.getScaleX());
int y = (int) ((e.getY() - insets.top) / chartPanel.getScaleY());
if (chartPanel.getChartRenderingInfo() != null) {
EntityCollection entities = chartPanel.getChartRenderingInfo().getEntityCollection();
if (entities != null) {
int entityCount = entities.getEntityCount();
for (int i = entityCount - 1; i >= 0; i--) {
ChartEntity entity = (ChartEntity) entities.getEntity(i);
if( !(entity instanceof XYItemEntity))
continue;
Rectangle r = entity.getArea().getBounds();
//Check to see if the x-value intersects an entity
if (r.getMinX() < x && x < r.getMaxX()) {
//Yes it intersects, make sure it is not the previously found entity
if(prev == null || prev != entity){
Graphics2D g2 = (Graphics2D) chartPanel.getGraphics();
g2.setXORMode(Color.cyan);
if(prev != null)
g2.fill(prev.getArea());//Erase previous marker
g2.fill(entity.getArea()); //Draw new marker
g2.setPaintMode();
prev = entity;
//This is where you extract the data from the entity and display it.
displayData((XYItemEntity) entity);
return;
}
}
}
}
}
}
});
}
protected void displayData(XYItemEntity entity){
DefaultOHLCDataset dataset = (DefaultOHLCDataset) entity.getDataset();
int series = entity.getSeriesIndex();
int item = entity.getItem();
HighLowItemLabelGenerator labelGenerator = new HighLowItemLabelGenerator();
String description = labelGenerator.generateToolTip(dataset, series, item);
descriptionLabel.setText(description);
}
public static void main(String[] args) {
new MouseMoveDemo("MSFT").setVisible(true);
}
}
The answer does not come from thinking outside the box, rather the answer comes from realizing the truth; There is no Box. my js site
Maximize not work.
When window maximize, the mark point is not correctly showed. Any idea how to fix?
The key thing to note is this was for an idea only.RoyW wrote: ////////////////////////////////////////////
//This is the code for the animation. It is very rough, for an idea only
//It does not take in to account chart orientation or scaling. (or repaint on zoom)
////////////////////////////////////////////
The ideal way to do this is extend ChartPanel then you can have more control because you will have to deal with not only resizing but repaints (due to the window being resized or the chart being zoomed or a right click menu popping up).
The way I did all the interactions for my chart project was to create my own InteractiveChartPanel that extended JComponent. That way I could have complete control over the drawing of the chart and the interaction with it.
The answer does not come from thinking outside the box, rather the answer comes from realizing the truth; There is no Box. my js site
This demo shows how you have to use an AffineTransform to take in to acount the scaling of ChartPanel when the window is resized. In my chart panel I took out the scaling all together.
Code: Select all
import org.jfree.chart.ChartPanel;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.axis.DateAxis;
import org.jfree.chart.axis.NumberAxis;
import org.jfree.chart.axis.SegmentedTimeline;
import org.jfree.chart.entity.ChartEntity;
import org.jfree.chart.entity.EntityCollection;
import org.jfree.chart.entity.XYItemEntity;
import org.jfree.chart.labels.HighLowItemLabelGenerator;
import org.jfree.chart.plot.XYPlot;
import org.jfree.chart.renderer.xy.XYItemRenderer;
import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer;
import org.jfree.data.xy.AbstractXYDataset;
import org.jfree.data.xy.DefaultOHLCDataset;
import org.jfree.data.xy.OHLCDataItem;
import org.jfree.data.xy.XYDataset;
import javax.swing.*;
import java.awt.*;
import java.awt.geom.AffineTransform;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionListener;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.URL;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.StringTokenizer;
public class MouseMoveDemo extends JFrame {
JLabel descriptionLabel = new JLabel(" ");
public MouseMoveDemo(String symbol) {
super("MouseMoveDemo");
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
DateAxis domainAxis = new DateAxis("Date");
NumberAxis rangeAxis = new NumberAxis("Price");
XYItemRenderer renderer = new XYLineAndShapeRenderer(true, false);
XYDataset dataset = getDataSet(symbol);
XYPlot mainPlot = new XYPlot(dataset, domainAxis, rangeAxis, renderer);
renderer.setSeriesPaint(0, Color.BLACK);
rangeAxis.setAutoRangeIncludesZero(false);
domainAxis.setTimeline( SegmentedTimeline.newMondayThroughFridayTimeline() );
JFreeChart chart = new JFreeChart(symbol, null, mainPlot, false);
ChartPanel chartPanel = new ChartPanel(chart);
chartPanel.setPreferredSize(new Dimension(600, 300));
this.add(chartPanel);
this.add(descriptionLabel, BorderLayout.NORTH);
this.pack();
addListener(chartPanel);
}
protected AbstractXYDataset getDataSet(String symbol) {
DefaultOHLCDataset result = null;
OHLCDataItem[] data;
data = getData(symbol);
return new DefaultOHLCDataset(symbol, data);
}
//This method uses yahoo finance to get the OHLC data
protected OHLCDataItem[] getData(String symbol) {
java.util.List<OHLCDataItem> dataItems = new ArrayList<OHLCDataItem>();
try {
String strUrl= "http://ichart.finance.yahoo.com/table.csv?s="+symbol+"&a=0&b=1&c=2008&d=3&e=30&f=2008&ignore=.csv";
URL url = new URL(strUrl);
BufferedReader in = new BufferedReader(new InputStreamReader(url.openStream()));
DateFormat df = new SimpleDateFormat("y-M-d");
String inputLine;
in.readLine();
while ((inputLine = in.readLine()) != null) {
StringTokenizer st = new StringTokenizer(inputLine, ",");
Date date = df.parse( st.nextToken() );
double open = Double.parseDouble( st.nextToken() );
double high = Double.parseDouble( st.nextToken() );
double low = Double.parseDouble( st.nextToken() );
double close = Double.parseDouble( st.nextToken() );
double volume = Double.parseDouble( st.nextToken() );
double adjClose = Double.parseDouble( st.nextToken() );
OHLCDataItem item = new OHLCDataItem(date, open, high, low, close, volume);
dataItems.add(item);
}
in.close();
}
catch (Exception e) {
e.printStackTrace();
}
Collections.reverse(dataItems);
return dataItems.toArray(new OHLCDataItem[dataItems.size()]);
}
////////////////////////////////////////////
//This is the code for the animation. It is very rough, for an idea only
//It does not take in to account chart orientation or repaints due to scaling/resizing
/////////////////////////////////////////////
protected ChartEntity prev = null;
protected void addListener(ChartPanel chartPanel){
chartPanel.addMouseMotionListener(new MouseMotionListener() {
public void mouseDragged(MouseEvent e) {
}
public void mouseMoved(MouseEvent e) {
ChartPanel chartPanel = (ChartPanel) e.getSource();
Insets insets = chartPanel.getInsets();
int x = (int) ((e.getX() - insets.left) / chartPanel.getScaleX());
int y = (int) ((e.getY() - insets.top) / chartPanel.getScaleY());
if (chartPanel.getChartRenderingInfo() != null) {
EntityCollection entities = chartPanel.getChartRenderingInfo().getEntityCollection();
if (entities != null) {
int entityCount = entities.getEntityCount();
for (int i = entityCount - 1; i >= 0; i--) {
ChartEntity entity = (ChartEntity) entities.getEntity(i);
if( !(entity instanceof XYItemEntity))
continue;
Rectangle r = entity.getArea().getBounds();
//Check to see if the x-value intersects an entity
if (r.getMinX() < x && x < r.getMaxX()) {
//Yes it intersects, make sure it is not the previously found entity
if(prev == null || prev != entity){
Graphics2D g2 = (Graphics2D) chartPanel.getGraphics();
AffineTransform saved = g2.getTransform();
AffineTransform st = AffineTransform.getScaleInstance(chartPanel.getScaleX(), chartPanel.getScaleY());
g2.transform(st);
g2.setXORMode(Color.cyan);
if(prev != null)
g2.fill(prev.getArea());//Erase previous marker
g2.fill(entity.getArea()); //Draw new marker
g2.setPaintMode();
g2.setTransform(saved);
prev = entity;
//This is where you extract the data from the entity and display it.
displayData((XYItemEntity) entity);
return;
}
}
}
}
}
}
});
}
protected void displayData(XYItemEntity entity){
DefaultOHLCDataset dataset = (DefaultOHLCDataset) entity.getDataset();
int series = entity.getSeriesIndex();
int item = entity.getItem();
HighLowItemLabelGenerator labelGenerator = new HighLowItemLabelGenerator();
String description = labelGenerator.generateToolTip(dataset, series, item);
descriptionLabel.setText(description);
}
public static void main(String[] args) {
new MouseMoveDemo("MSFT").setVisible(true);
}
}
The answer does not come from thinking outside the box, rather the answer comes from realizing the truth; There is no Box. my js site