XYDataSource and jsp code

A discussion forum for JFreeChart (a 2D chart library for the Java platform).
Locked
Angela

XYDataSource and jsp code

Post by Angela » Tue May 22, 2001 2:19 pm

Hi,

I am having real trouble simply visuallising a three dimensional array for and XYdataSource. The 'help' in the API docs confuses even more;
I quote "public DefaultXYDataSource(Object[][][] data);
The default constructor – this method constructs an XY data source from the specified data. The first
array dimension corresponds to the series, and the second array dimension corresponds to the item
within a series, and the final array dimension is 0 for the x-value and 1 for the y-value.".

What does 0 for x and 1 for y mean?? And what is an item?

How does this fit with this example array declaration that seems to produce a single series line plot.

public XYDataSource createXYDataSource()
{
Object data[][][] = {
{
{
new Integer(0), new Integer(1)
}, {
new Integer(5), new Integer(6)
}, {
new Integer(9), new Integer(7)
}
}
};
return new DefaultXYDataSource(data);
}

finally some of you were asking for jsp code I am currently testing on this - you are welcome to try it out too!

<%@page contentType="text/html"%>

<head><title>JSP Page</title></head>
<body>

<%@ page import="javax.servlet.*,javax.servlet.http.*, com.jrefinery.chart.*,java.io.*,java.awt.*,java.awt.image.*,java.awt.geom.*,com.sun.image.codec.jpeg.*,java.util.*" %>
<%

response.setContentType("image/jpeg");
%>
<%!
int type = 1;
int initGradColor= 0;
int finalGradColor= 0;
%>
<%
XYDataSource data = createXYDataSource();
com.jrefinery.chart.JFreeChart chart = com.jrefinery.chart.JFreeChart.createXYChart(data);
%>
<%
chart.setChartBackgroundPaint(new GradientPaint(0, 0, getColor( initGradColor ), 1000, 0, getColor( finalGradColor )));
chart.setTitle("Angela's Test Chart");
int width = 400;
int height = 300;
BufferedImage img = draw( chart, width, height );
JPEGImageEncoder encoder =
JPEGCodec.createJPEGEncoder(response.getOutputStream());
// following code commented out as it produced runtime errors. Reports socket being dropped by peer.
//JPEGEncodeParam param =
// encoder.getDefaultJPEGEncodeParam(img);
// param.setQuality(1.0f,true);
encoder.encode(img); // was (img,param)
%>
<%!
public Color getColor(int color)
{
switch (color % 11) {
case 0: return Color.white;
case 1: return Color.black;
case 2: return Color.blue;
case 3: return Color.green;
case 4: return Color.red;
case 5: return Color.yellow;
case 6: return Color.gray;
case 7 : return Color.orange;
case 8: return Color.cyan;
case 9: return Color.magenta;
case 10: return Color.pink;
default: return Color.white;
}

}

public BufferedImage draw(JFreeChart chart, int width, int height)
{
BufferedImage img =
new BufferedImage(width , height,
BufferedImage.TYPE_INT_RGB);
Graphics2D g2 = img.createGraphics();
chart.draw(g2, new Rectangle2D.Double(0, 0, width, height));
g2.dispose();
return img;
}
%>
<%! public XYDataSource createTestXYDataSource() {

// I tried creating an array automatically
// rename this method to createXYDataSource and the
// orgianl to createTestXYDataSource if you want to try this

Integer[][][] M;
M = new Integer[2][3][4];

for (int row=0; row < 2; row++) {
for (int col=0; col < 3; col++) {
for (int ver=0; ver < 4; ver++) {
M[row][col][ver] = new Integer(row+col+ver);
}
}
}
return new DefaultXYDataSource(M);
}
%>




<%!

public XYDataSource createXYDataSource()
{
Object data[][][] = {
{
{
new Integer(0), new Integer(1)
}, {
new Integer(5), new Integer(6)
}, {
new Integer(9), new Integer(7)
}
}
};
return new DefaultXYDataSource(data);
}
%>


<head><title>JSP Page</title></head>
<body>

<%-- <jsp:useBean id="beanInstanceName" scope="session" class="package.class" /> --%>
<%-- <jsp:getProperty name="beanInstanceName" property="propertyName" /> --%>

<</body>
<

Angela

David Gilbert

RE: XYDataSource and jsp code

Post by David Gilbert » Tue May 22, 2001 9:41 pm

Hi Angela,

You are right, the DefaultXYDataSource class is not intuitive - I'd recommend never ever using it, just implement the XYDataSource yourself based on whatever data you have.

But just to explain the "logic", you should populate your array so that the following would work for one data pair (x, y):

x = yourArray[series, item, 0];
y = yourArray[series, item, 1];

For example, if you have 5 series, each containing 10 data items, the following would return the seventh (x, y) pair for series three:

x = yourArray[2, 6, 0];
y = yourArray[2, 6, 1];

...keeping in mind that the array indices are zero based.

Hope that helps...

DG.

P.S. It is my plan to improve the data source code in a future version of JFreeChart.

Dino

RE: XYDataSource and jsp code

Post by Dino » Fri Jun 01, 2001 1:49 am

I have created a simple class to do the above which I found more intuitive:

There are two classes: SingleXYDataSource & XYDataItem:

Note: I extracted class XYDataItem from class DefaultXYDataSource (ie. remove the XYDataItem class from class DefaultXYDataSource):

class XYDataItem
=================


package com.jrefinery.chart;

import java.util.*;

public class XYDataItem implements Comparable {

final Number x;
final Number y;

public XYDataItem(Number x, Number y) {
this.x = x;
this.y = y;
}

public final Number getX() {
return x;
}

public final Number getY() {
return y;
}

public XYDataItem(Date x, Number y) {
this.x = new Long(x.getTime());
this.y = y;
}

public int compareTo(Object object) {
if (object instanceof XYDataItem) {
XYDataItem item = (XYDataItem)object;
if (this.x.doubleValue()>item.x.doubleValue()) {
return 1;
}
else if (this.x.equals(item.x)) {
return 0;
}
else return -1;
}
else throw new ClassCastException("XYDataItem.compareTo(error)");
}

}


class SingleXYDataSource
========================



package com.jrefinery.chart;
/**
* A convenience class that provides a default implementation of a single XYDataSource interface.
*/
public class SingleXYDataSource extends AbstractDataSource implements XYDataSource {

/** A series name */
protected String seriesName;

/** Data of this series. */
protected XYDataItem[] data;

/**
* Builds an empty SingleXYDataSource.
*/
public SingleXYDataSource() {
this("", new XYDataItem[0]);
}

/**
* Builds a SingleXYDataSource with the specified data.
* Creates a single series with an array of data.
*/
public SingleXYDataSource(XYDataItem[] data) {
this("Series 1", data);
}

/**
* Builds a SingleXYDataSource with the specified data and series names.
*/
public SingleXYDataSource(String seriesName, XYDataItem[] data) {
this(seriesName, data, true);
}

/**
* Builds a SingleXYDataSource with the specified data and series name.
*/
public SingleXYDataSource(String seriesName, XYDataItem[] data, boolean requiresSort) {
this.seriesName = seriesName;
if (requiresSort) {
java.util.Arrays.sort(data); // Makes sure it is sorted
}
this.data = data;
}

/**
* Returns the number of series.
*/
public int getSeriesCount() {
return 1;
}

/**
* Returns the number of items in the specified series.
* @param seriesIndex The index of the series (zero-based).
*/
public int getItemCount(int seriesIndex) {
// assert series index == 0;
return data.length;
}

/**
* Returns the name of the specified series.
* @param seriesIndex The index of the required series (zero-based).
*/
public String getSeriesName(int seriesIndex) {
// assert series index == 0;
return seriesName;
}

/**
* Sets the names of the series in the data source.
* @param seriesNames The names of the series in the data source.
*/
public void setSeriesName(String seriesName) {
this.seriesName = seriesName;
fireDataSourceChanged();
}

/**
* Returns the x value for the specified series and index (zero-based indices).
* Supports the XYDataSource interface.
* @param seriesIndex The index of the series (zero-based);
* @param itemIndex The index of the item (zero-based).
*/
public Number getXValue(int seriesIndex, int itemIndex) {
// assert series index == 0;
return data[itemIndex].x;
}

/**
* Returns the y value for the specified series and index (zero-based indices).
* Supports the XYDataSource interface.
* @param seriesIndex The index of the series (zero-based);
* @param itemIndex The index of the item (zero-based).
*/
public Number getYValue(int seriesIndex, int itemIndex) {
// assert series index == 0;
return data[itemIndex].y;
}

}


TO use it:

XYDataItem[] data = new XYDataItem[2];
data[0] = new XYDataItem(System.currentTimeMillis()-30000L), new Double(10.51d));
data[1] = new XYDataItem(new Date(), new Double(10.66d));

JFreeChart chart = createChart(...);
SingleXYDataSource dataXY = new SingleXYDataSource("Hi", data);
chart.setDataSource(dataXY);

I have also implemented a class called MultipleXYDataSource. I could
post this as well (but I haven't tested it).

dino

David Gilbert

RE: XYDataSource

Post by David Gilbert » Fri Jun 01, 2001 8:11 am

Dino,

Thanks for your post - your code is definitely more intuitive, and it would be great to see your MultipleXYDataSource. Even if it is not fully tested, others will spot any problems quick enough.

I've started writing a class XYSeries that represents a single set of (x, y) values that you can dynamically update (I'm working on using the TreeMap class to store the data).

Next, I plan to write another class XYSeriesDataSource which stores a list of XYSeries (and you can add series to the list or delete them). This class will implement XYDataSource and then JFreeChart will have a dynamic data source. I'm guessing this will be roughly equivalent to your MultipleXYDataSource, except with the ability to update the datasource dynamically.

I've thought about implementing the XYDataSource interface in XYSeries also (to make it easy to create a chart with just one series - the equivalent of your SingleXYDataSource), but I wonder if this would confuse the purpose of XYSeries and XYSeriesDataSource.

Some further ideas:
- I would like to implement a few wrapper classes for XYSeries that transform one series into something different (like a moving average series derived from a price series);
- a subclass of XYSeries called TimeSeries is in the pipeline. Instead of (x, y) pairs, this will have (period, value) pairs where the period can be any of: Year, Quarter, Month, Day, Hour, Minute, Second or Millisecond.

Regards,

DG.

Dino

RE: XYDataSource

Post by Dino » Fri Jun 01, 2001 5:39 pm

Okay, here's the multipleXYDataSource. By all means, change the class name etc. I wasn't sure about the listeners callbacks and how they tie in (i.e I haven't looked into the callback mechanism that closely). I haven't bothered to add more contructors to take List as an argument rather than an array.

Comments from your response: I don't think XYSeries should implement XYDataSource. It should be a supplier (I like to separate implementation from abstraction).

Maybe the XYSeries should be abstract with different decendants: one for dynamic (e.g. using a sorted map) and the other using static (e.g. using an array). This means you would not need to define XYSeriesDataSource.

However, I like the way you're going.


package com.jrefinery.chart;



/**
* A convenience class that provides a default implementation for Multiple XYDataSource interface.
*/
public class MultipleXYDataSource extends AbstractDataSource implements XYDataSource {

/** Multiple series data. */
protected SingleXYDataSource[] data;

/**
* Default constructor - builds an empty MultipleXYDataSource.
*/
public MultipleXYDataSource() {
this(new SingleXYDataSource[0]);
}

/**
* Builds a MultipleXYDataSource with the specified data.
* Creates a single series with an array of data.
*/
public MultipleXYDataSource(SingleXYDataSource[] data) {
this.data = data;
}

/**
* Returns the number of series.
*/
public int getSeriesCount() {
return data.length;
}

/**
* Returns the number of items in the specified series.
* @param seriesIndex The index of the series (zero-based).
*/
public int getItemCount(int seriesIndex) {
return data[seriesIndex].getItemCount(0);
}

/**
* Returns the name of the specified series.
* @param seriesIndex The index of the required series (zero-based).
*/
public String getSeriesName(int seriesIndex) {
// assert series index == 0;
return data[seriesIndex].getSeriesName(0);
}

/**
* Returns the x value for the specified series and index (zero-based indices).
* Supports the XYDataSource interface.
* @param seriesIndex The index of the series (zero-based);
* @param itemIndex The index of the item (zero-based).
*/
public Number getXValue(int seriesIndex, int itemIndex) {
return data[seriesIndex].getXValue(0, itemIndex);
}

/**
* Returns the y value for the specified series and index (zero-based indices).
* Supports the XYDataSource interface.
* @param seriesIndex The index of the series (zero-based);
* @param itemIndex The index of the item (zero-based).
*/
public Number getYValue(int seriesIndex, int itemIndex) {
// assert series index == 0;
return data[seriesIndex].getYValue(0, itemIndex);
}

}

Roger

RE: XYDataSource

Post by Roger » Thu Jul 05, 2001 9:14 pm

Hi Dino.

Have you done any further work on MultipleXYDataSource? I've gotten your SingleXYDataSource to work but have not been able to get MultipleXYDataSource to work.

Thanks,

R

Dino

RE: XYDataSource

Post by Dino » Fri Jul 06, 2001 12:02 am

Sorry Roger, I haven't even tried the implementation (I warned you guys:). Give me a day or so and I'll try to find out what the problem is.

dino

Dino

RE: XYDataSource

Post by Dino » Fri Jul 06, 2001 12:25 am

I did basic testing and it seemed to work. Could you describe what is not working?

dino

Roger

RE: XYDataSource

Post by Roger » Fri Jul 06, 2001 9:17 pm

I have a feeling the solution must be painfully obvious.

I just have not been able to correctly implement the MultipleXYDataSource. I can create an empty MultipleXYDataSource but have not been able to add SingleXYDataSources to it without compile errors.

Could you please push me in the right direction?

Thanks you.

Roger

Dino

RE: XYDataSource

Post by Dino » Fri Jul 06, 2001 9:31 pm

To use the MultipleXYDataSource:

SingleXYDataSource dataXY1 = ...;
SingleXYDataSource dataXY2 = ...;

MultipleXYDataSource multiXY;

///
SingleXYDataSource[] dataXYs = { dataXY1, dataXY2 };
multiXY = new MultipleXYDataSource(dataXYs);
/// OR
multiXY = new MultipleXYDataSource(
new SingleXYDataSource[] {dataXY1, dataXY2});
///


chart.setDataSource(multiXY);

Roger

RE: XYDataSource

Post by Roger » Fri Jul 06, 2001 10:10 pm

Thanks Dino -- it works!

Locked