In the chart factory and I use there SlidingGanttCategoryDataset. But the scrolling does not appear. What am I missing?
public JFreeChartGanttChart(String applicationTitle, String chartTitle) {
super(applicationTitle);
// based on the dataset we create the chart
Map<Integer, Map<Integer, String>> labels = new LinkedHashMap<>();
JFreeChart chart = ChartFactory.createGanttChart(chartTitle,
"Vessels",
"Time",
createDataset(labels),
true,
true,
true);
// Adding chart into a chart pane
CategoryPlot plot = (CategoryPlot) chart.getPlot();
plot.getDomainAxis().setUpperMargin(0.001);
plot.getRangeAxis().setLowerMargin(0.01);
plot.getRangeAxis().setUpperMargin(0.01);
plot.getDomainAxis().setLowerMargin(0.001);
ChartPanel chartPanel = new ChartPanel(chart);
chartPanel.setPreferredSize(new Dimension(1000, 1500));
setContentPane(chartPanel);
CategoryItemRenderer renderer = plot.getRenderer();
renderer.setDefaultItemLabelGenerator(new IntervalCategoryItemLabelGenerator(){
#Override
public String generateRowLabel(CategoryDataset dataset, int row) {
return "Text ... " + row ; // not needed here
}
#Override
public String generateColumnLabel(CategoryDataset dataset, int column) {
return " Text ... " + column; // not needed here
}
#Override
public String generateLabel(CategoryDataset dataset, int row, int column) {
return labels.get(row).get(column) ; // row+ " " + column
}
});
renderer.setDefaultItemLabelsVisible(true);
renderer.setDefaultItemLabelPaint(Color.BLACK);
renderer.setDefaultItemLabelFont(new Font("arial", Font.PLAIN, 6), false);
// setDefaultPositiveItemLabelPosition - text will be inside label
renderer.setDefaultNegativeItemLabelPosition(new ItemLabelPosition(ItemLabelAnchor.INSIDE6,
TextAnchor.BOTTOM_LEFT));
}
}
While it is technically possible to add a ChartPanel to a JScrollPane, rendering and scrolling hundreds or thousands of tasks scales poorly; the typical appearance is shown here. Instead, use a SlidingGanttCategoryDataset, which "presents a subset of the categories in an underlying dataset." Given an arbitrary number, N, of tasks to to be displayed MAX at a time, construct the corresponding dataset:
private static final int N = 1000;
private static final int MAX = 12;
…
private final SlidingGanttCategoryDataset dataset = new
SlidingGanttCategoryDataset(taskSeriesCollection, 0, MAX);
Then add a suitable control adjacent to the ChartPanel and listen for changes; update the dataset's first displayed index accordingly:
JSlider slider = new JSlider(JSlider.VERTICAL, 0, N - MAX, 0);
slider.addChangeListener((ChangeEvent e) -> {
dataset.setFirstCategoryIndex(slider.getValue());
});
Related
I have a chart where I display the values of a table (stock and date). The stock is displayed in the y axis and the dates in the x axis.
As long as the query returns 2 entries, it is shown normally as a line, but if the query returns only one entry, nothing is shown (there should be a point there).
Any suggestions on how to fix this would be highly appreciated.
2 entries: enter image description here
1 entry: enter image description here
Code (the chart is built in an action listener):
historyButton.addActionListener(e -> {
// stock list and dates list retrieved from database
int articleNr = Integer.parseInt(articleIDText.getText());
List<Integer> displayStockHistory;
List<String> displayDatesStockHistory;
try {
displayStockHistory = business.displayStockHistory(articleNr);
} catch (Exception ex) {
throw new RuntimeException(ex);
}
try {
displayDatesStockHistory = business.displayDatesStockHistory(articleNr);
} catch (Exception ex) {
throw new RuntimeException(ex);
}
DefaultCategoryDataset dataset = new DefaultCategoryDataset();
// add db values to the dataset
for(int i = 0; i < displayStockHistory.size(); i++){
dataset.addValue(displayStockHistory.get(i), "Articles in Stock", displayDatesStockHistory.get(i));
}
// compose chart
JFreeChart chart = ChartFactory.createLineChart(
"Stock History",
"Date",
"Stock",
dataset,
PlotOrientation.VERTICAL,
true,
true,
false);
chart.setBackgroundPaint(c2);
chart.getTitle().setPaint(c3);
CategoryPlot p = chart.getCategoryPlot();
p.setForegroundAlpha(0.9f);
CategoryItemRenderer renderer = p.getRenderer();
//renderer.setSeriesPaint(0, c4);
renderer.setSeriesStroke( 0, new BasicStroke( 5 ) );
chart.getCategoryPlot().setBackgroundPaint(c1);
ChartPanel chartPanel = new ChartPanel(chart);
chartPanel.setBackground(c2);
chartScrollPane.getViewport().add(chartPanel);
chartPanel.setPreferredSize(new Dimension(2000, 270));
ChartFrame frame1 = new ChartFrame("Line graph", chart);
frame1.setVisible(true);
frame1.setSize(500, 400);
});
}
Your chosen chart factory instantiates a LineAndShapeRenderer, so one approach is to make the renderer's shapes visible for all series, as shown here; note that the API has changed:
CategoryPlot plot = (CategoryPlot) chart.getPlot();
LineAndShapeRenderer r = (LineAndShapeRenderer) plot.getRenderer();
r.setDefaultShapesVisible(true);
To enable shapes only for series having a single, non-null value, you'll have to iterate through the data explicitly:
DefaultCategoryDataset data = new DefaultCategoryDataset();
data.addValue(3, "Data1", LocalDate.now());
data.addValue(2, "Data2", LocalDate.now());
data.addValue(1, "Data2", LocalDate.now().plusDays(1));
…
for (int row = 0; row < data.getRowCount(); row++) {
int count = 0;
for (int col = 0; col < data.getColumnCount(); col++) {
count += data.getValue(row, col) != null ? 1 : 0;
}
if (count == 1) {
r.setSeriesShapesVisible(row, true);
}
}
XYSeriesCollection dataset = new XYSeriesCollection();
XYSeries validKoordinates = new XYSeries("Not dangerous");
for (SimulationResult sr : validSR.values()) {
validKoordinates.add(sr.getMinTTC(), sr.getMinTHW()); //x,y
}
dataset.addSeries(validKoordinates);
JFreeChart chart = chart = ChartFactory.createScatterPlot(
"Distribution of THW and TTC Values",
"TTC", "THW", dataset);
//Changes background color
XYPlot plot = (XYPlot) chart.getPlot();
plot.setBackgroundPaint(new Color(255, 228, 196));
plot.getRendererForDataset(plot.getDataset(0)).setSeriesPaint(0, Color.GRAY);
XYSeriesCollection dataset2 = new XYSeriesCollection();
XYSeries warningKoordinates = new XYSeries("Very critical");
for (SimulationResult sr : criticalSR.values()) {
warningKoordinates.add(sr.getMinTTC(), sr.getMinTHW()); //x,y
}
dataset2.addSeries(warningKoordinates);
plot.setDataset(1, dataset2);
plot.getRendererForDataset(plot.getDataset(1)).setSeriesPaint(0, Color.RED);
So I have two different datasets (dataset 1 and dataset 2). Each dataset contains different values. My aim is to change the color of the dataset 1 to gray and the color of the second one to red. (BUT the shape should same the SAME). At the beginning I only had one dataset with 2 XYSeries. The problem at that time was that the shape was different for each XYSeries. Now I have the opposite problem. The shape stays but the color does not change.
This is what my table looks right now:
As you can see right now I cant seperate which one is very critical and whitch not.
It is found that only one dataSet is required, all series should be added to the dataSet. And using the first parameter of setSeriesPaint to configure color of the series. Since the version of jfreeChart is not provide, I used 1.0.12. The code is edited for testing.
public static void main(String[] args) throws IOException {
XYSeriesCollection dataset = new XYSeriesCollection();
int[][] sr = new int[3][2];
for (int i = 0; i < sr.length; i++) {
sr[i][0] = i + 3;
sr[i][1] = 2 * (i + 1) + 1;
}
XYSeries validKoordinates = new XYSeries("Not dangerous");
XYSeries warningKoordinates = new XYSeries("Very critical");
System.out.println(sr.length);
for (int i = 0; i < sr.length; i++) {
validKoordinates.add(sr[i][0], sr[i][1]);
}
for (int i = 0; i < sr.length; i++) {
warningKoordinates.add(sr[i][0]+1, sr[i][1]+1);
}
// Add dataset for all series
dataset.addSeries(validKoordinates);
dataset.addSeries(warningKoordinates);
// Only reference one dataset
JFreeChart chart = ChartFactory.createScatterPlot("Distribution of THW and TTC Values", "TTC", "THW", dataset,
PlotOrientation.HORIZONTAL, true, true, true);
XYPlot plot = (XYPlot) chart.getPlot();
plot.setBackgroundPaint(new Color(255, 228, 196));
// Change the index of setSeriesPaint to set color
plot.getRendererForDataset(dataset).setSeriesPaint(0, Color.GRAY);
plot.getRendererForDataset(dataset).setSeriesPaint(1, Color.RED);
BufferedImage objBufferedImage = chart.createBufferedImage(600, 800);
ByteArrayOutputStream bas = new ByteArrayOutputStream();
try {
ImageIO.write(objBufferedImage, "png", bas);
} catch (IOException e) {
e.printStackTrace();
}
byte[] byteArray = bas.toByteArray();
InputStream in = new ByteArrayInputStream(byteArray);
BufferedImage image = ImageIO.read(in);
File outputfile = new File("testimage.png");
ImageIO.write(image, "png", outputfile);
}
The image generated by the above method
Well basically what my program does is that I have a csv file that reads in the
milliseconds (which I converted) and a letter that represents a zone. I am trying to display it as a bar chart but I have different tabs that each letter (zone) goes to. My
issue is trying to send the letter (from file) to the specific zone which I made in tabs. Maybe I am missing something but I am not sure. Any help will be much appreciated.
Here is my code:
#SuppressWarnings({ "serial", "deprecation" })
public class InductionTreeGraph extends JFrame {
static TimeSeries ts = new TimeSeries("data", Millisecond.class);
public static void add(JTabbedPane jtp, String label, int mnemonic, AbstractButton button1)
{
int count = jtp.getTabCount();
JButton button = new JButton(label);
button.setBackground(Color.WHITE);
jtp.addTab(label, null, button, null);
jtp.setMnemonicAt(count, mnemonic);
}
public InductionTreeGraph() throws Exception {
final XYDataset dataset = (XYDataset) createDataset();
final JFreeChart chart = createChart(dataset);
final ChartPanel chartPanel = new ChartPanel(chart);
chartPanel.setPreferredSize(new Dimension(1000, 400));
setContentPane(chartPanel);
JFrame frame = new JFrame("Induction Zone Chart");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
final JTabbedPane jtp = new JTabbedPane();
jtp.setTabLayoutPolicy(JTabbedPane.SCROLL_TAB_LAYOUT);
String zones[] = {"Zone A", "Zone B", "Zone C", "Zone S",
"Zone SH","Zone W"};
int mnemonic[] = {KeyEvent.VK_A, KeyEvent.VK_B, KeyEvent.VK_C,
KeyEvent.VK_S, KeyEvent.VK_H,KeyEvent.VK_W};
for (int i = 0, n=zones.length; i<n; i++)
{
AbstractButton button1 = null;
InductionTreeGraph.add(jtp, zones[i], mnemonic[i], button1);
}
final JPanel p = new JPanel(new FlowLayout(FlowLayout.RIGHT));
p.add(new JButton(new AbstractAction("Update") {
public void actionPerformed(ActionEvent e) {
chartPanel.repaint();
}
}));
frame.add(jtp, BorderLayout.NORTH);
frame.add(p, BorderLayout.SOUTH);
frame.getContentPane().add(chartPanel);
frame.pack();
frame.setVisible(true);
frame.setLocationRelativeTo(null);
}
#SuppressWarnings("deprecation")
private XYDataset createDataset() {
final TimeSeriesCollection dataset = new TimeSeriesCollection();
dataset.addSeries(ts);
TreeMap<String,TreeMap<Integer,Integer[]>> zoneMap = getInductions("","");
// Iterate through all zones and print induction rates for every minute into
// every hour by zone...
Iterator<String> zoneIT = zoneMap.keySet().iterator();
while (zoneIT.hasNext())
{
String zone = zoneIT.next();
TreeMap<Integer,Integer[]> hourCountsInZoneMap = zoneMap.get(zone);
System.out.println("ZONE " + zone + " : ");
Iterator<Integer> hrIT = hourCountsInZoneMap.keySet().iterator();
while (hrIT.hasNext())
{
int hour = hrIT.next();
Integer [] indRatePerMinArray = hourCountsInZoneMap.get(hour);
for (int i=0; i< indRatePerMinArray.length; i++)
{
System.out.print(hour + ":");
System.out.print(i < 10 ? "0" + i : i);
System.out.println(" = " + indRatePerMinArray[i] + " induction(s)");
}
}
}
return dataset;
}
#SuppressWarnings("deprecation")
private JFreeChart createChart(XYDataset dataset) {
final JFreeChart chart = ChartFactory.createXYBarChart(
"Induction Zone Chart",
"Hour",
true,
"Inductions Per Minute",
(IntervalXYDataset) dataset,
PlotOrientation.VERTICAL,
false,
true,
false
);
XYPlot plot = (XYPlot)chart.getPlot();
XYBarRenderer renderer = (XYBarRenderer)plot.getRenderer();
renderer.setBarPainter(new StandardXYBarPainter());
renderer.setDrawBarOutline(false);
ValueAxis axis = plot.getDomainAxis();
axis.setAutoRange(true);
axis.setFixedAutoRange(60000.0);
// Set an Induction target of 30 per minute
Marker target = new ValueMarker(30);
target.setPaint(java.awt.Color.blue);
target.setLabel("Induction Rate Target");
plot.addRangeMarker(target);
return chart;
}
private TreeMap<String, TreeMap<Integer, Integer[]>> getInductions(String mills, String zone) {
// TreeMap of Inductions for Every Minute in Day Per Zone...
// Key = Zone
// Value = TreeMap of Inductions per Minute per Hour:
// Key = Hour
// Value = Array of 60 representing Induction Rate Per Minute
// (each element is the induction rate for that minute)
TreeMap<String, TreeMap<Integer, Integer[]>> zoneMap = new TreeMap<String, TreeMap<Integer, Integer[]>>();
// Input file name...
String fileName = "/home/a002384/ECLIPSE/IN070914.CSV";
try
{
BufferedReader br = new BufferedReader(new FileReader(fileName));
String line;
try
{
// Read a line from the csv file until it reaches to the end of the file...
while ((line = br.readLine()) != null)
{
// Parse a line of text in the CSV...
String [] indData = line.split("\\,");
long millisecond = Long.parseLong(indData[0]);
String zone1 = indData[1];
// The millisecond value is the number of milliseconds since midnight.
// From this, we can derive the hour and minute of the day as follows:
int secOfDay = (int)(millisecond / 1000);
int hrOfDay = secOfDay / 3600;
int minInHr = secOfDay % 3600 / 60;
// Obtain the induction rate TreeMap for the current zone.
// If this is a "newly-encountered" zone, create a new TreeMap.
TreeMap<Integer, Integer[]> hourCountsInZoneMap;
if (zoneMap.containsKey(zone1))
hourCountsInZoneMap = zoneMap.get(zone1);
else
hourCountsInZoneMap = new TreeMap<Integer, Integer[]>();
// Obtain the induction rate array for the current hour in the current zone.
// If this is a new hour in the current zone, create a new array,
// and initialize this array with all zeroes.
// The array is size 60, because there are 60 minutes in the hour.
// Each element in the array represents the induction rate for that minute.
Integer [] indRatePerMinArray;
if (hourCountsInZoneMap.containsKey(hrOfDay))
indRatePerMinArray = hourCountsInZoneMap.get(hrOfDay);
else
{
indRatePerMinArray = new Integer[60];
Arrays.fill(indRatePerMinArray, 0);
}
// Increment the induction rate for the current minute by one.
// Each line in the csv file represents a single induction at a
// single point in time.
indRatePerMinArray[minInHr]++;
// Add everything back into the TreeMaps if these are newly-created.
if (!hourCountsInZoneMap.containsKey(hrOfDay))
hourCountsInZoneMap.put(hrOfDay, indRatePerMinArray);
if (!zoneMap.containsKey(zone1))
zoneMap.put(zone1, hourCountsInZoneMap);
}
}
finally
{
br.close();
}
}
catch (Exception e)
{
e.printStackTrace();
}
return zoneMap;
}
#SuppressWarnings("deprecation")
public static void main(String[] args) throws Exception {
try {
InductionTreeGraph dem = new InductionTreeGraph();
dem.pack();
dem.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
while(true) {
double millisecond = 2;
double num = millisecond;
System.out.println(num);
ts.addOrUpdate(new Millisecond(), num);
try {
Thread.sleep(20);
} catch (InterruptedException ex) {
System.out.println(ex);
}
}
}
catch (Exception e)
{
e.printStackTrace();
}
}
}
My very first suggestion is about this line:
setContentPane(chartPanel);
Don't mess with content pane. Use this line instead:
getContentPane().add(chartPanel);
The prooblem I see in your code is that InductionTreeGraph extends from JFrame and you are setting this chartPanel as this frame's content pane, but then you use a new JFrame local variable (called frame) and add this chartPanel to this frame as well:
JFrame frame = new JFrame("Induction Zone Chart");
...
frame.getContentPane().add(chartPanel);
Swing components are intended to be used "as is" so it's preferable composition over inheritance. Having said this you should consider remove extends JFrame on your class declaration and add all your components (chart panel, buttons, etc) to this local frame instead.
Related
You may want to take a look to the example shown in this Q&A. This or any #trashgod's examples in Stack Overflow about JFreeChart (f.e. this one) might help you to better structure your code.
I'm trying to create a bar chart that generates a dataset from within a for loop.
String scores = scoreText.getText();
String[] data = scores.split(",");
DefaultCategoryDataset barChartDataset = null;
for (int l = 0; l < data.length; l++) {
barChartDataset = new DefaultCategoryDataset();
// barChartDataset.setValue(new Double(data[l]), "Scores", stu);
barChartDataset.addValue(new Double(data[l]), "Scores", stu);
System.out.println(data[l]);
}
JFreeChart barChart = ChartFactory.createBarChart3D("Summary", "Name", "Scores", barChartDataset, PlotOrientation.VERTICAL, false, true, false);
The data is 10,5. Now when the data goes through all of this and the graph is generated, only the bar for the value 5 is shown. Where is the separate bar for the value of 10? Does anyone know what I'm doing wrong? Any help is appreciated. Thanks
EDIT:
Here is the code for the bar graph:
String scores = scoreText.getText();
String[] data = scores.split(",");
DefaultCategoryDataset barChartDataset = new DefaultCategoryDataset();
//JFreeChart barChart = null;
for (int l = 0; l < data.length; l++) {
//barChartDataset.addValue(new Double(data[l]), "Scores", stu);
barChartDataset.setValue(new Double(data[l]), "Scores", stu);
System.out.println(new Double(data[l]));
}
JFreeChart barChart = ChartFactory.createBarChart3D("Summary", "Name", "Scores", barChartDataset, PlotOrientation.VERTICAL, false, true, false);
barChart.setBackgroundPaint(Color.YELLOW);
barChart.getTitle().setPaint(Color.RED);
final CategoryPlot categoryPlot = barChart.getCategoryPlot();
BarRenderer barRenderer = (BarRenderer) categoryPlot.getRenderer();
DecimalFormat decimalFormat = new DecimalFormat("#.##");
barRenderer.setItemLabelGenerator(new StandardCategoryItemLabelGenerator("{2}", decimalFormat));
categoryPlot.setRenderer(barRenderer);
barRenderer.setBasePositiveItemLabelPosition(new ItemLabelPosition(ItemLabelAnchor.OUTSIDE12, TextAnchor.HALF_ASCENT_CENTER));
barRenderer.setItemLabelsVisible(true);
barChart.getCategoryPlot().setRenderer(barRenderer);
ValueMarker marker = new ValueMarker(7);
marker.setLabel("Required Level");
marker.setLabelAnchor(RectangleAnchor.BOTTOM_RIGHT);
marker.setLabelTextAnchor(TextAnchor.TOP_RIGHT);
marker.setPaint(Color.BLACK);
categoryPlot.addRangeMarker(marker);
categoryPlot.setRangeGridlinePaint(Color.BLUE);
//The JFrame that the bar chart will be in.
ChartFrame chartFrame = new ChartFrame("Bar Chart for Parameters", barChart);
chartFrame.setVisible(true);
chartFrame.setSize(600, 600);
I guess you are doing a small mistake, that is with in for loop for each iteration of loop you are creating a new DefaultCategoryDataset instance. So each time each item is added to a separate DefaultCategoryDataset object and the final DefaultCategoryDataset instance having the last value is utilized to create the chart, that is the only reason you are getting only last value in your chart.
Solution is create DefaultCategoryDataset object outside and before the for loop only once like:
DefaultCategoryDataset barChartDataset = new DefaultCategoryDataset();
for (int l = 0; l < data.length; l++) {
// barChartDataset.setValue(new Double(data[l]), "Scores", stu);
barChartDataset.addValue(new Double(data[l]), "Scores", stu);
System.out.println(data[l]);
}
JFreeChart barChart = ChartFactory.createBarChart3D("Summary", "Name", "Scores", barChartDataset, PlotOrientation.VERTICAL, false, true, false);
Here is the code snippet I have in one of my application and it is working fine:
DefaultCategoryDataset dataset= new DefaultCategoryDataset();
// Get today as a Calendar....
Calendar today = Calendar.getInstance();
for(int i=0; i<15 ;i++)
{
//get util.Date class object for today date.....
java.util.Date today_date=new java.util.Date(today.getTimeInMillis());
//convert date in string format to display on chart.....
String today_string_date = new SimpleDateFormat("dd/MM/yy").format(today_date);
// set values to DefaultCategoryDataset to display on chart...
dataset.setValue(rs1.getInt("login_count"),"Login Frequency", today_string_date);
today.add(Calendar.DATE, -1);
}// for closing...
JFreeChart chart = ChartFactory.createBarChart3D("ISIS:Overall login history for last 15 days", "Date -->", "No of user(s) login per day -->", dataset, PlotOrientation.VERTICAL, true, true, false);
CategoryPlot p = chart.getCategoryPlot();
NumberAxis rangeAxis = (NumberAxis) p.getRangeAxis();
rangeAxis.setStandardTickUnits(NumberAxis.createIntegerTickUnits());
BarRenderer renderer = (BarRenderer) p.getRenderer();
DecimalFormat decimalformat1 = new DecimalFormat("##");
renderer.setItemLabelGenerator(new StandardCategoryItemLabelGenerator("{2}", decimalformat1));
renderer.setItemLabelsVisible(true);
ChartUtilities.saveChartAsPNG(new File(filePath +"/chart1.png"), chart ,1250, 400);
I hope it will solve your problem.
The program will receive data every second and draw them on time Series chart. However, once I create two series, I cannot add new value to it. It displays a straight line only.
How do I append data to a specified series? I.e. YYY. Based on this example, here's what I'm doing:
...
// Data set.
final DynamicTimeSeriesCollection dataset =
new DynamicTimeSeriesCollection( 2, COUNT, new Second() );
dataset.setTimeBase( new Second( 0, 0, 0, 1, 1, 2011 ) );
dataset.addSeries( gaussianData(), 0, "XXX" );
dataset.addSeries( gaussianData(), 1, "YYY" );
// Chart.
JFreeChart chart = createChart( dataset );
this.add( new ChartPanel( chart ), BorderLayout.CENTER );
// Timer.
timer = new Timer( 1000, new ActionListener() {
#Override
public void actionPerformed ( ActionEvent e ) {
dataset.advanceTime();
dataset.appendData( new float[] { randomValue() } );
}
} );
...
private JFreeChart createChart ( final XYDataset dataset ) {
final JFreeChart result = ChartFactory.createTimeSeriesChart(
TITLE, "", "", dataset, true, true, false );
final XYPlot plot = result.getXYPlot();
ValueAxis domain = plot.getDomainAxis();
domain.setAutoRange( true );
ValueAxis range = plot.getRangeAxis();
range.setRange( -MINMAX, MINMAX );
return result;
}
Assuming you started from here, you've specified a dataset with two series, but you're only appending one value with each tick of the Timer. You need two values for each tick. Here's how I modified the original to get the picture below:
final DynamicTimeSeriesCollection dataset =
new DynamicTimeSeriesCollection(2, COUNT, new Second());
...
dataset.addSeries(gaussianData(), 0, "Human");
dataset.addSeries(gaussianData(), 1, "Alien");
...
timer = new Timer(FAST, new ActionListener() {
// two values appended with each tick
float[] newData = new float[2];
#Override
public void actionPerformed(ActionEvent e) {
newData[0] = randomValue();
newData[1] = randomValue();
dataset.advanceTime();
dataset.appendData(newData);
}
});