I'm trying to to make a chart that takes data from serial port and plot them in y axes and i want current time in x axes.. I think that i set my code correctly because i managed to run it as XY chart now in TimeSeries chart my only issue is that in method series.add(TIME, SERIALDATA); i dont know how to initialize TIME , i know that i want an object RegularTimePeriod but i dont know how to do that..
here is the code.. i know that only some lines are missing please help me to find them...
void initialize() {
frame = new JFrame();
frame.setBounds(100, 100, 817, 525);
final TimeSeries series = new TimeSeries("Charts");
final SerialDataReceived serialdataprint = new SerialDataReceived();
final TimeSeriesCollection data = new TimeSeriesCollection(series);
final JFreeChart chart = ChartFactory.createXYLineChart(
"Tmperature IN",
"Time",
"C",
data,
PlotOrientation.VERTICAL,
true,
true,
false
);
final ChartPanel chartPanel = new ChartPanel(chart);
chartPanel.setBounds(10, 11, 477, 224);
chartPanel.setPreferredSize(new java.awt.Dimension(500, 270));
chartPanel.setVisible(true);
frame.getContentPane().setLayout(null);
frame.getContentPane().add(chartPanel);
chartPanel.setLayout(null);
Thread outtempthread=new Thread() { //THREAD THAT RUNS ALL THE TIME
public void run() {
try {
while (true){
Thread.sleep(2000);
double intemp = serialdataprint.getintemp(); //THIS WHERE I TAKE MY SERIAL DATA
series.add(I WANT TO DISPLAY HERE LETS SAY 13:23:15, intemp); //HERE IS MY PROBLEM
}}
catch (InterruptedException ie) {}
}
};
outtempthread.start();
}
I've only ever used TimeSeries measured in days so I used the org.jfree.data.time.Day class.
Here's the jfreechart javadoc for all the different time classes : http://www.jfree.org/jfreechart/api/javadoc/org/jfree/data/time/package-summary.html
Try out a few and see what's right for you.
Since you appear to only need hour,minute second of a single day, you might be able to use the Second class.
Here is how you would make a TimeSeries that way:
int todaysDay =...
int todaysMonth =...
int todaysYear =...
TimeSeries series = new TimeSeries(name, Second.class);
//this should mark 'inTemp' as 13:23:15
series.add(new Second(15,23,13,todaysDay, todaysMonth, todaysYear),
inTemp);
ok!! finally i found the solution! i don't know if is the correct way but it works an now i have real time in my chart every time my serial port updates here is the fix code!
String timeStamp1 = new SimpleDateFormat("mm").format(Calendar.getInstance().getTime());
int minute = Integer.parseInt(timeStamp1);
double intemp = serialdataprint.getintemp();
series.addOrUpdate(new Minute(minute,hour), intemp);
A couple of pointers:
The ChartFactory.createXYLineChart() method will create a line chart where both the X and Y axes are numerical. Try the createTimeSeriesChart() to get a chart that shows dates on the X axis (or create a new DateAxis() instance and call plot.setDomainAxis() to change the X axis);
The TimeSeriesCollection class is a good dataset to use for time series data if you need the structure that it provides (it enforces a regular time period and prevents duplicates among other things). However, bear in mind that it is simply an implementation of the XYDataset interface where the x-values returned are "milliseconds since 1-Jan-1970" (the standard encoding of "dates" in Java). You can simplify your code by using an XYSeriesCollection (which also implements the XYDataset interface), and call System.currentTimeInMillis() to get the current x-value when new data comes in. The date axis on your chart will take care of presenting a date scale for this data.
Related
I'm trying to write a method to create a simple graph of a normal distribution in JFreeChart and save it as a file. Here's an example of an output image that's pretty much exactly what I want
Notice that there are exactly 9 tick marks on the x axis. The center one is the mean of the distribution, and the rest of the ticks indicate standard deviations. There is one tick for each standard deviation from the mean.
Here's an example of another chart showing a normal distribution with a mean of 7 and a standard deviation of 5 and no other code changes.
This is not what I want. Suddenly there are only 8 tick marks, and there is no tick in the center to mark the mean. It appears that JFreeChart wants to only use nice round numbers instead of the odd 7 as the center tick.
I've tried reading other StackOverflow questions on forcing axis labels, but it appears everyone else wants to do this with some form of dates. It would help if I could simply specify 9 exactly values to put on the axis instead of them being autogenerated, but I don't know how to do that.
There's also one other problem. If you look at the curve near the sides of the graph, it is clipping below the frame of the plot and running into the tick marks. I want to add padding between the curve and the tick marks. I tried using something like plot.getRangeAxis().setRange(-0.01, 0.09); but I ran into a bizarre problem where it appears that the height of the normal distribution is impacted by its width. Large means and standard deviations cause this to break miserably. (That makes zero sense from a statistics standpoint and I'm starting to question this normal distribution method.)
Anyway I basically need a way to force the chart to (a) add padding around the curve and (b) use exactly nine tick marks corresponding to the mean and four standard deviations out.
Here's my current code, which was mostly stolen online and trimmed to what appeared to be actually necessary:
static double mean = 7.0, sd = 5.0;
static Color line = new Color(0x6AA2A3);
static Color grey = new Color(0x555555);
public static void main(String[] args) throws IOException {
// Create the normal distribution
double minX = mean - (4 * sd), maxX = mean + (4 * sd);
Function2D normal = new NormalDistributionFunction2D(mean, sd);
XYDataset dataset = DatasetUtils.sampleFunction2D(normal, minX, maxX, 100, "Normal");
JFreeChart chart = ChartFactory.createXYLineChart(null, null, null, dataset, PlotOrientation.VERTICAL, false, false, false);
chart.setBorderVisible(true);
// Create and format the Plot
XYPlot plot = chart.getXYPlot();
plot.setBackgroundPaint(Color.WHITE);
plot.getRangeAxis().setVisible(false);
plot.setOutlineVisible(false);
// Format the X axis to look pretty
NumberAxis domain = (NumberAxis) plot.getDomainAxis();
domain.setRange(minX, maxX);
domain.setAxisLineVisible(false);
domain.setAutoRangeStickyZero(false);
domain.setTickUnit(new NumberTickUnit(sd));
domain.setTickLabelFont(new Font("Roboto", Font.PLAIN, 20));
domain.setTickLabelPaint(grey);
domain.setTickMarkStroke(new BasicStroke(2, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND));
domain.setTickMarkInsideLength(8);
domain.setTickMarkPaint(grey);
// Create a renderer to turn the chart into an image
XYLineAndShapeRenderer render = (XYLineAndShapeRenderer) plot.getRenderer(0);
render.setSeriesStroke(0, new BasicStroke(4));
render.setSeriesPaint(0, line);
// Output the final image
chart.setPadding(new RectangleInsets(5, 20, 20, 20));
BufferedImage image = chart.createBufferedImage(600,400);
File outFile = new File("graph.png");
outFile.createNewFile();
ImageIO.write(image, "png", outFile);
}
for request a),
plot.setAxisOffset(new RectangleInsets(5,5,5,5));
should do the trick.
For request b), the general recommendation is to override
refreshTicks(Graphics2D g2, AxisState state,Rectangle2D dataArea,RectangleEdge edge)
of the
ValueAxis
class and return a suitable List of ticks. Though doing so may look a bit intimidating, it is not if your logic is simple. You could try to simply add a NumberTick for the mean to the auto-generated tick list.
Rendering a chart over several days, with a dataset that has 24 hour data, but it's only useful during M-F, 7AM to 5PM. If I setup a time series with the code below, I get a chart that contains all 24 hours, 7 days a week. Makes sense, but not for my use case.
Is there a way to define what interval the time series displays? Or do I need to use a different chart type and attempt to fit my data into regular periods? I hope not the latter, while the data I receive is usually in 30 second intervals, there can easily be gaps.
It's pretty impossible to post an SSCE of a working UI with a chart dynamically asking for data from a server, but some highlights are below to get an idea of the chart types I'm using.
Some of the plot.add, CombinedDomainXY, index 0 code may seem strange. I have three subplots with the shared time values, I've pared it down to one here to keep it short. I assume there is a way to do what I need for one plot it would work for a chart with multiple subplots.
public ChartPanel extends JPanel
{
private final MyDataset _myDataset = new MyDataset();
private final XYPlot _myPlot = new XYPlot();
_chartPanel = new ChartPanel( createChart() );
private JFreeChart createChart()
{
CombinedDomainXYPlot plot = new CombinedDomainXYPlot(
timeAxis );
plot.setGap( 10.0 );
plot.setDomainPannable( true );
plot.setDataset( index, dataset );
NumberAxis axis = new NumberAxis();
axis.setAutoRangeIncludesZero( false );
plot.setRangeAxis( 0, axis );
plot.setRangeAxisLocation( 0, axisLocation );
plot.setRenderer( 0, new StandardXYItemRenderer() );
plot.mapDatasetToRangeAxis( 0, index );
// add the subplots...
plot.add( _myPlot, 1 );
}
}
public class MyDataset implements XYDataset
{
#Override
public double getYValue( int series, int item )
{
return getMyData(item);
}
#Override
public double getXValue( int series, int item )
{
return _bars.get( item ).DateTime.toInstant().toEpochMilli();
}
//other basic overloaded methods left out for brevity
}
You may be able to use a DateAxis with a custom Timeline. SegmentedTimeline, examined here, is a concrete implementation; although deprecated, it may serve as a guide. Based on this example, your notional newWorkdayTimeline() might look something like this:
public static SegmentedTimeline newWorkdayTimeline() {
SegmentedTimeline timeline = new SegmentedTimeline(
SegmentedTimeline.HOUR_SEGMENT_SIZE, 10, 14);
timeline.setStartTime(SegmentedTimeline.firstMondayAfter1900()
+ 7 * timeline.getSegmentSize());
timeline.setBaseTimeline(SegmentedTimeline.newMondayThroughFridayTimeline());
return timeline;
}
This example illustrates one way to mitigate any rendering artifacts you encounter.
I am using JFreeChart for the first time and I am using a TimeSeriesCollection() to create a TimeSeriesChart.
My reslutset from the DB query is app. aroung 1000 records. I am using org.jfree.date.time.Minute.Minute(int min.....) object to add it to a TimeSeries object.
I have a JFrame on which I add the ChartPanel directly. The user will provide new input parameters and reload the chart data with new dataset. So I clean up before every reload by calling the following in a method
dataset.removeAllSeries();
chart.removeLegend();
chart.getRenderingHints().clear();
cp.getChartRenderingInfo().setEntityCollection(null);
cp.removeAll();
cp.revalidate();
The output is perfect. But I noticed that after running the program 'several times in Eclipse' I see the below error message about Java heap space. Sometimes I also see in the Task Manager that the program hogs on the PC memory even though the dataset is very small (100 records).
Exception occurred during event dispatching:
java.lang.OutOfMemoryError: Java heap space
at sun.util.calendar.Gregorian.newCalendarDate(Gregorian.java:67)
at java.util.GregorianCalendar.<init>(GregorianCalendar.java:575)
at java.util.Calendar.createCalendar(Calendar.java:1012)
at java.util.Calendar.getInstance(Calendar.java:964)
at org.jfree.chart.axis.DateTickUnit.addToDate(DateTickUnit.java:238)
at org.jfree.chart.axis.DateAxis.refreshTicksHorizontal(DateAxis.java:1685)
at org.jfree.chart.axis.DateAxis.refreshTicks(DateAxis.java:1556)
at org.jfree.chart.axis.ValueAxis.reserveSpace(ValueAxis.java:809)
at org.jfree.chart.plot.XYPlot.calculateDomainAxisSpace(XYPlot.java:3119)
at org.jfree.chart.plot.XYPlot.calculateAxisSpace(XYPlot.java:3077)
at org.jfree.chart.plot.XYPlot.draw(XYPlot.java:3220)
at org.jfree.chart.JFreeChart.draw(JFreeChart.java:1237)
at org.jfree.chart.ChartPanel.paintComponent(ChartPanel.java:1677)
at javax.swing.JComponent.paint(JComponent.java:1029)
at javax.swing.JComponent.paintToOffscreen(JComponent.java:5124)
at javax.swing.RepaintManager$PaintManager.paintDoubleBuffered(RepaintManager.java:1491)
at javax.swing.RepaintManager$PaintManager.paint(RepaintManager.java:1422)
at javax.swing.BufferStrategyPaintManager.paint(BufferStrategyPaintManager.java:294)
at javax.swing.RepaintManager.paint(RepaintManager.java:1225)
at javax.swing.JComponent._paintImmediately(JComponent.java:5072)
at javax.swing.JComponent.paintImmediately(JComponent.java:4882)
at javax.swing.RepaintManager.paintDirtyRegions(RepaintManager.java:786)
at javax.swing.RepaintManager.paintDirtyRegions(RepaintManager.java:714)
at javax.swing.RepaintManager.prePaintDirtyRegions(RepaintManager.java:694)
at javax.swing.RepaintManager.access$700(RepaintManager.java:41)
at javax.swing.RepaintManager$ProcessingRunnable.run(RepaintManager.java:1636)
at java.awt.event.InvocationEvent.dispatch(InvocationEvent.java:209)
at java.awt.EventQueue.dispatchEventImpl(EventQueue.java:646)
at java.awt.EventQueue.access$000(EventQueue.java:84)
at java.awt.EventQueue$1.run(EventQueue.java:607)
at java.awt.EventQueue$1.run(EventQueue.java:605)
at java.security.AccessController.doPrivileged(Native Method)
My application is as follows:
I have a JFrame on which I directly add the ChartPanel after passing a Chart to it.
chart = ChartFactory.createTimeSeriesChart("Peak monitor", , "Time: Zoom in", "# of Requests Logged", createDataset(from,to), true, false, false);
chartpanel = new ChartPanel(chart);
FramePanel.this.add(cp);
validate();
Here createDataset(from, to) is a method
private TimeSeriesCollection createDataset(Date from, Date to) {
dataset.addSeries(controller.getStuff(from, to));
return dataset;
}
getStuff is called within a SwingWorker thread (DIBkgd method)
public TimeSeries getStuff(Date from, Date to) {
s1 = new TimeSeries("Log Requests");
final Date from1 = from;
final Date to1 = to;
progressDialog.setVisible(true);
sw = new SwingWorker<Void, Integer>() {
#Override
protected Void doInBackground() throws Exception {
if (db.getCon() == null) {
db.connect();
}
Arrlst2.clear();
Arrlst2= db.getDataDB(from1, to1);
for (Qryobjects x : Arrlst2) {
s1.add(new Minute(x.getMinute(), x.getHour(), x.getDay(), x.getMonth(), x.getYear()), x.getCount());
}
System.out.println("finished fetching data");
return null;
}
#Override
protected void done() {
progressDialog.setVisible(false);
}
};
sw.execute();
return s1;
}
Within my Database class the getDataDB is executed:
public List<Qryobjects> getDataDB(Date from, Date to) {
PreparedStatement select;
ResultSet rs;
String selectSql = "Select Sum(Cnt) Cid, Hr, Min, Dat from (Select count(H.Request_Id) Cnt , To_Char(H.Timestamp,'HH24') HR, To_Char(H.Timestamp,'mm') MIN, To_Char(H.Timestamp,'MM-dd-yyyy') DAT From Status_History H Where H.Timestamp Between ? And ? Group By H.Request_Id, H.Timestamp Order By H.Timestamp Asc) Group By Hr, Min, Dat order by Dat asc";
try {
select = con.prepareStatement(selectSql);
select.setDate(1, from);
select.setDate(2, to);
rs = select.executeQuery();
System.setProperty("true", "true");
while (rs.next()) {
int cnt = rs.getInt("cid");
int hour = Integer.parseInt(rs.getString("Hr"));
int min = Integer.parseInt(rs.getString("Min"));
int month = Integer.parseInt(rs.getString("dat").substring(0, 2));
int day = Integer.parseInt(rs.getString("dat").substring(3, 5));
int year = Integer.parseInt(rs.getString("dat").substring(6, 10));
Arrlst1.add(new Qryobjects(cnt, hour, min, day, month,year));
}
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
return Arrlst1;
}
For reference, I profiled two long running time series DTSCTest and MemoryUsageDemo. To exaggerate the scale, I used an artificially small heap, as shown below. In each case, I saw the typical saw-tooth pattern of periodic garbage collection return to baseline, as shown here. In contrast, this pathological example shows a secular rise in consumed memory from unrecoverable resources.
$ java -Xms32m -Xmx80m -cp build/classes:dist/lib/* chart.DTSCTest
$ java -Xms32m -Xmx80m -jar jfreechart-1.0.14-demo.jar
I resolved my problem.
I took the clue from #TrashGod to use dispose(). But it does not work directly for me.
I was adding the chart panel directly to my main JFrame container. And in my case, I wanted to keep creating the charts in the same JFrame container over and over.
I first tried clearing the dataset and called removeall() on the chart panel, but it did not help.
Then the solution I found was to create another JFrame and add the chart panel to it. And when I closed this JFrame, I again clear the dataset and called removeall() on the chart panel and also called dispose(). So everytime, I create a new chart, this JFrame and its children componenets are created and are completely disposed when I exit this JFrame.
So, when a chart is created a new JFrame is created and then disposed.
I should also add that after making this change I started to see the Saw Tooth pattern in the Java VisualVM profiler. I also used Jprofiler and I was shocked to see more than 100,000 objects were created while I was running my program. Now, I see 9000 objects created and it remains constant for the JFree package and based on my resultset retrieved the number of objects in my database package increases or decreases.
One more thing I did was to make my SQL do the parsing and convert it to a number. I wanted to reduce the number of objects created and also reduce the processing done by my program for each retrieved record.
Your solution is great! :)) Thanks to you, I have fixed my heap overflow problem. But, your solution can be even better. :)) Before you draw your graph onto panel, just call method panel.RemoveAll();
and everything that was before on your panel will be disposed. No other
JFrame
instances are necessary...
In my case, solution was:
for(...)
{
panel.RemoveAll();
drawData(listOfData);
}
Have a nice day! :)
In the method org.jfree.chart.axis.DateAxis.refreshTicksHorizontal, I added following extra lines to successfully avoided the OutOfmemoryError. the reason is for some circumstance, the variable tickDate is not increased, so the loop of "while (tickDate.before(upperDate))" becomes an infinite loop.
protected List refreshTicksHorizontal(Graphics2D g2,
Rectangle2D dataArea, RectangleEdge edge) {
List result = new java.util.ArrayList();
Font tickLabelFont = getTickLabelFont();
g2.setFont(tickLabelFont);
if (isAutoTickUnitSelection()) {
selectAutoTickUnit(g2, dataArea, edge);
}
DateTickUnit unit = getTickUnit();
Date tickDate = calculateLowestVisibleTickValue(unit);
Date upperDate = getMaximumDate();
boolean hasRolled = false;
Date previousTickDate=null; //added
while (tickDate.before(upperDate)) {
if(previousTickDate!=null && tickDate.getTime()<=previousTickDate.getTime()){ //added
tickDate=new Date(tickDate.getTime()+100L); //added
} //added
previousTickDate=tickDate; //added
//System.out.println("tickDate="+tickDate+" upperDate="+upperDate);** //add to see infinite loop
Please try by removing the tooltips and the legend from the chart (making them 'false' in the constructor). It should reduce the memory footprint
I'm trying to make a pretty simple chart which graphs measured forces over the period of a second. I have plain-jane example which is outputting the following graph:
Here's my code:
TimeSeries series1 = new TimeSeries("Force", "Domain", "Range");
//generate values
for (int i = 0; i < 1000; i++) {
series1.add(new TimeSeriesDataItem(new Millisecond(i, 0, 0, 0, 1, 1, 2012), i));
}
TimeSeriesCollection dataset = new TimeSeriesCollection();
dataset.addSeries(series1);
JFreeChart chart = ChartFactory.createXYLineChart("Measured Forces",
"Time", "Force in Pounds", dataset, PlotOrientation.VERTICAL,
false, false, false);
ChartUtilities.saveChartAsPNG(new File("/home/rfkrocktk/Desktop/chart.png"), chart, 1280, 720);
How can I transform those ugly 1,325,404,800,000 values into simple 0-1,000 values representing the overall time measured instead of the exact system time?
I've been looking for constructors that match my requirements, but I can't seem to find them.
ChartFactory.createXYLineChart() creates two instances of NumberAxis, one for each of the domain and range. As an alternative, consider ChartFactory.createTimeSeriesChart(), which uses a DateAxis for the domain. In either case, you can use setXxxFormatOverride() on the axis to get any desired format. The example shown here specifies a factory DateFormat.
As far as I know, TimeSeries does not support the concept of 'relative time' - all of the x value types (like Millisecond) are based on java.util.Date, which yields an absolute time.
I think you might be better off using XYSeries instead of TimeSeries. Then the x values can be integers.
The trick is to use RelativeDateFormat for the domain axis formatting with ChartFactory.createTimeSeriesChart(). This prints labels in the format of #h#m#.###s. For example, you might see "0h0m2.529s".
JFreeChart chart;
XYPlot plot;
DateAxis axis;
TimeSeriesCollection dataset;
TimeSeries series;
RelativeDateFormat format;
long startTime;
series = new TimeSeries("Time");
dataset = new TimeSeriesCollection(series);
chart = ChartFactory.createTimeSeriesChart("Relative", "Elapsed Time", "Value", dataset, true, true, true);
plot = chart.getXYPlot();
axis = (DateAxis) plot.getDomainAxis();
format = new RelativeDateFormat();
startTime = System.currentTimeMillis();
axis.setDateFormatOverride(format);
format.setBaseMillis(startTime);
I have a requirement to show time-series data as layered bar chart. Is it possible with JFreeChart? Any pointers would be really helpful.
The data would be a list of: (TS, X1, X2), where I've to plot X1 for a given Timestamp (TS) and X2 would basically serve as the label for the given value of X1.
Edit: Also, for the same TS, there might exist different X1 values. The idea is to denote all these X1 values as layered bars against the same TS.
Here's somewhat of an example of what I want:
.
(so instead of category, I'll have TS in X-axis)
It sounds like you want a BarChart (with x-axis determined by time) with the bars labelled with their values. You don't need to add a new data series for the labels, but modify the rendering of the plot.
Here's a simple example:
public class LabelledBarChartTest {
public static void main(String[] args) {
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
DefaultCategoryDataset dataset = new DefaultCategoryDataset();
dataset.addValue(10.0, "Series", new Integer(2010));
dataset.addValue(20.0, "Series", new Integer(2011));
dataset.addValue(30.0, "Series", new Integer(2012));
JFreeChart chart = ChartFactory.createBarChart(null,null,null,dataset,
PlotOrientation.VERTICAL,true,true,false);
CategoryPlot plot = (CategoryPlot) chart.getPlot();
CategoryItemRenderer renderer = plot.getRenderer();
// label the points
NumberFormat format = NumberFormat.getNumberInstance();
format.setMaximumFractionDigits(2);
CategoryItemLabelGenerator generator = new StandardCategoryItemLabelGenerator(
StandardXYItemLabelGenerator.DEFAULT_ITEM_LABEL_FORMAT, format, format);
renderer.setBaseItemLabelGenerator(generator);
renderer.setBaseItemLabelsVisible(true);
frame.setContentPane(new ChartPanel(chart));
frame.pack();
frame.setVisible(true);
}
}
Credit where credit is due - I got the labelling example from this example.