This question is related to my previous question [ How to show cross lines and red color for price drop in JFreeChart? ]. Thanks to @trashgod I have solved a lot of issues from that question, I'll ask what's left in this question. My sample code looks like this :
import java.awt.*;
import java.awt.event.*;
import java.text.*;
import java.util.*;
import javax.swing.*;
import org.jfree.chart.*;
import org.jfree.chart.axis.NumberAxis;
import org.jfree.chart.entity.*;
import org.jfree.chart.plot.XYPlot;
import org.jfree.chart.renderer.xy.XYBarRenderer;
import org.jfree.data.time.*;
import org.jfree.data.xy.XYDataset;
import org.jfree.chart.labels.*;
import org.jfree.chart.panel.*;
import org.jfree.chart.plot.*;
public class PriceVolume_Chart extends JPanel implements ChartMouseListener // A demo application for price-volume chart.
{
ChartPanel panel;
TimeSeries Price_series=new TimeSeries("Price");
TimeSeries Volume_Series=new TimeSeries("Volume");
Crosshair xCrosshair,yCrosshair;
static Vector<String> Volume_Color_Vector=new Vector();
public PriceVolume_Chart(String Symbol)
{
JFreeChart chart=createChart(Symbol);
panel=new ChartPanel(chart,true,true,true,false,true);
panel.setPreferredSize(new Dimension(1000,500));
panel.addChartMouseListener(this);
CrosshairOverlay crosshairOverlay=new CrosshairOverlay();
float[] dash={2f,0f,2f};
BasicStroke bs=new BasicStroke(1,BasicStroke.CAP_BUTT,BasicStroke.JOIN_ROUND,1.0f,dash,2f);
xCrosshair=new Crosshair(Double.NaN,Color.GRAY,bs);
xCrosshair.setLabelBackgroundPaint(new Color(0f,0f,0f,1f));
xCrosshair.setLabelFont(xCrosshair.getLabelFont().deriveFont(14f));
xCrosshair.setLabelPaint(new Color(1f,1f,1f,1f));
xCrosshair.setLabelVisible(true);
yCrosshair=new Crosshair(Double.NaN,Color.GRAY,bs);
yCrosshair.setLabelBackgroundPaint(new Color(0f,0f,0f,1f));
yCrosshair.setLabelFont(xCrosshair.getLabelFont().deriveFont(14f));
yCrosshair.setLabelPaint(new Color(1f,1f,1f,1f));
yCrosshair.setLabelVisible(true);
crosshairOverlay.addDomainCrosshair(xCrosshair);
crosshairOverlay.addRangeCrosshair(yCrosshair);
panel.addOverlay(crosshairOverlay);
add(panel);
xCrosshair.setValue(1.5700752E12);
xCrosshair.setVisible(true);
yCrosshair.setValue(119.959007);
yCrosshair.setVisible(true);
}
private JFreeChart createChart(String Symbol)
{
createPriceDataset(Symbol);
XYDataset priceData=new TimeSeriesCollection(Price_series);
JFreeChart chart=ChartFactory.createTimeSeriesChart(Symbol,"Date",getYLabel("Price ( $ )"),priceData,true,true,true);
XYPlot plot=chart.getXYPlot();
plot.setBackgroundPaint(new Color(192,196,196));
NumberAxis rangeAxis1=(NumberAxis)plot.getRangeAxis();
rangeAxis1.setLowerMargin(0.40); // Leave room for volume bars
// plot.getRenderer().setDefaultToolTipGenerator(new StandardXYToolTipGenerator(StandardXYToolTipGenerator.DEFAULT_TOOL_TIP_FORMAT,DateFormat.getDateInstance(), NumberFormat.getCurrencyInstance()));
plot.getRenderer().setDefaultToolTipGenerator(new StandardXYToolTipGenerator(StandardXYToolTipGenerator.DEFAULT_TOOL_TIP_FORMAT,new SimpleDateFormat("yyyy-MM-d"),NumberFormat.getCurrencyInstance()));
// DecimalFormat format=new DecimalFormat("00.00");
// rangeAxis1.setNumberFormatOverride(format);
// rangeAxis1.setNumberFormatOverride(NumberFormat.getCurrencyInstance());
NumberAxis rangeAxis2=new NumberAxis("Volume");
rangeAxis2.setUpperMargin(1.00); // Leave room for price line
rangeAxis2.setNumberFormatOverride(NumberFormat.getNumberInstance());
plot.setRangeAxis(1,rangeAxis2);
plot.setDataset(1,new TimeSeriesCollection(Volume_Series));
plot.setRangeAxis(1,rangeAxis2);
plot.mapDatasetToRangeAxis(1,1);
plot.setRenderer(1,new MyRender());
return chart;
}
private void createPriceDataset(String Symbol)
{
String Lines[]=new String[21], Items[], Date;
int Year, Month, Day;
long Volume,Last_Volume=0;
double Price;
Lines[0]="Date,Open,High,Low,Close,Adj Close,Volume";
Lines[1]="2019-09-23,129.589996,130.710007,128.240005,129.300003,126.555969,553700";
Lines[2]="2019-09-24,129.309998,129.529999,125.500000,126.750000,124.060089,732900";
Lines[3]="2019-09-25,126.570000,128.500000,126.190002,127.879997,125.166100,422000";
Lines[4]="2019-09-26,127.849998,128.589996,127.169998,127.779999,125.068230,376100";
Lines[5]="2019-09-27,128.669998,129.289993,126.389999,126.419998,123.737083,332900";
Lines[6]="2019-09-30,126.589996,128.789993,125.849998,128.130005,125.410797,456700";
Lines[7]="2019-10-01,129.039993,130.899994,125.480003,126.040001,123.365158,322700";
Lines[8]="2019-10-02,125.059998,125.180000,121.620003,123.120003,120.507126,577100";
Lines[9]="2019-10-03,122.650002,123.320000,119.089996,122.559998,119.959007,581300";
Lines[10]="2019-10-04,122.970001,123.949997,121.320000,123.879997,121.250992,315700";
Lines[11]="2019-10-07,123.139999,124.610001,122.669998,122.879997,120.272217,510300";
Lines[12]="2019-10-08,121.720001,121.879997,118.089996,118.660004,116.141777,616600";
Lines[13]="2019-10-09,119.410004,119.610001,116.680000,118.419998,115.906868,603300";
Lines[14]="2019-10-10,119.089996,121.209999,117.080002,118.209999,115.701324,483300";
Lines[15]="2019-10-11,120.330002,123.040001,119.720001,122.550003,119.949226,700500";
Lines[16]="2019-10-14,122.550003,123.720001,120.940002,122.540001,119.939430,492900";
Lines[17]="2019-10-15,122.849998,124.220001,121.230003,123.699997,121.074814,598200";
Lines[18]="2019-10-16,123.889999,124.849998,122.800003,123.209999,120.595207,663600";
Lines[19]="2019-10-17,123.449997,124.889999,122.790001,123.360001,120.742035,563200";
Lines[20]="2019-10-18,123.050003,124.620003,122.459999,123.540001,120.918213,650300";
for (int i=1;i<Lines.length;i++)
{
Items=Lines[i].split(",");
Date=Items[0].replace("-0","-");
Price=Double.parseDouble(Items[5]);
Volume=Long.parseLong(Items[6]);
Items=Date.split("-");
Year=Integer.parseInt(Items[0]);
Month=Integer.parseInt(Items[1]);
Day=Integer.parseInt(Items[2]);
Price_series.add(new Day(Day,Month,Year),Price);
Volume_Series.add(new Day(Day,Month,Year),Volume);
Volume_Color_Vector.add(Volume>=Last_Volume?"+":"-");
Last_Volume=Volume;
}
}
@Override
public void chartMouseClicked(ChartMouseEvent event)
{
// ignore
}
public void chartMouseMoved(ChartMouseEvent cmevent)
{
ChartEntity chartentity=cmevent.getEntity();
if (chartentity instanceof XYItemEntity)
{
XYItemEntity e=(XYItemEntity)chartentity;
XYDataset d=e.getDataset();
int s=e.getSeriesIndex();
int i=e.getItem();
double x=d.getXValue(s,i);
double y=d.getYValue(s,i);
// Out("x = "+x+" y = "+y);
xCrosshair.setValue(x);
yCrosshair.setValue(y);
}
}
String getYLabel(String Text)
{
String Result="";
for (int i=0;i<Text.length();i++) Result+=Text.charAt(i)+(i<Text.length()-1?"\u2009":"");
// Out(Result);
return Result;
}
private static void out(String message) { System.out.print(message); }
private static void Out(String message) { System.out.println(message); }
// Create the GUI and show it. For thread safety, this method should be invoked from the event-dispatching thread.
static void Create_And_Show_GUI()
{
final PriceVolume_Chart demo=new PriceVolume_Chart("ADS");
JFrame frame=new JFrame("PriceVolume_Chart Frame");
frame.add(demo);
frame.addWindowListener(new WindowAdapter()
{
public void windowActivated(WindowEvent e) { }
public void windowClosed(WindowEvent e) { }
public void windowClosing(WindowEvent e) { System.exit(0); }
public void windowDeactivated(WindowEvent e) { }
public void windowDeiconified(WindowEvent e) { demo.repaint(); }
public void windowGainedFocus(WindowEvent e) { demo.repaint(); }
public void windowIconified(WindowEvent e) { }
public void windowLostFocus(WindowEvent e) { }
public void windowOpening(WindowEvent e) { demo.repaint(); }
public void windowOpened(WindowEvent e) { }
public void windowResized(WindowEvent e) { demo.repaint(); }
public void windowStateChanged(WindowEvent e) { demo.repaint(); }
});
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args)
{
// Schedule a job for the event-dispatching thread : creating and showing this application's GUI.
SwingUtilities.invokeLater(new Runnable() { public void run() { Create_And_Show_GUI(); } });
}
}
class MyRender extends XYBarRenderer
{
@Override
public Paint getItemPaint(int row,int col)
{
System.out.println(row+" "+col+" "+super.getItemPaint(row,col));
return PriceVolume_Chart.Volume_Color_Vector.elementAt(col).equals("+")?super.getItemPaint(row,col):new Color(0.56f,0.2f,0.5f,1f);
}
}
My questions are :
[1] How to correctly show Volume data in cross-hair [ it's now showing the incorrect value, yet I don't know how to fix it ] ?
[2] How to point the mouse on the N-th [ e.g. 12th ] day when the app starts ?
More details to the 2nd question, when I move the mouse, I can output the x,y value of the mouse on the screen, and I can do the following to set the mouse at the desired location at start up :
...
panel.addOverlay(crosshairOverlay);
add(panel);
xCrosshair.setValue(1.5700752E12);
yCrosshair.setValue(119.959007);
But since I don't know the [x,y] location of the N-th day, therefore I don't know how to set the mouse on that day.
The reason I want to do this is : in my app, I want to look at some data on a certain day in another panel, and user can click on a certain day and I want the JFreeChart to instantly point to that day on the chart.
So my 2nd question is asking : "How can I get the [x,y] location of the N-th day on the chart ?