[ Previous document | Content Table | Next document ]

# 10 Charts

## 10.1 Overview

Chart documents produce graphical representations of numeric data. They are always embedded objects inside other OpenOffice.org documents. The chart document is a document model similar to Writer, Calc and Draw document models, and it can be edited using this document model.

## 10.2 Handling Chart Documents

### 10.2.1 Creating Charts

The com.sun.star.table.XTableChartsSupplier interface of the com.sun.star.sheet.Spreadsheet service is used to create and insert a new chart into a Calc document. This creates a chart that uses data from the com.sun.star.chart.XChartDataArray interface of the underlying cell range. A generic way to create charts is to insert an OLE-Shape into a draw page and transform it into a chart setting a class-id.

#### Creating and Adding a Chart to a Spreadsheet

Charts are used in spreadsheet documents to visualize the data that they contain. A spreadsheet is one single sheet in a spreadsheet document and offers a com.sun.star.table.XTableChartsSupplier interface, that is required by the service com.sun.star.sheet.Spreadsheet. With this interface, a collection of table charts that are a container for the actual charts can be accessed. To retrieve the chart document model from a table chart object, use the method getEmbeddedObject().

The following example shows how to insert a chart into a spreadsheet document and retrieve its chart document model. The example assumes that there is a com.sun.star.sheet.XSpreadsheet to insert the chart and an array of cell range addresses that contain the regions in which the data for the chart can be found. Refer to 8 Spreadsheet Documents for more information about how to get or create these objects. The following snippet shows how to insert a chart into a Calc document.

import com.sun.star.chart.*;

import com.sun.star.uno.UnoRuntime;

import com.sun.star.container.XNameAccess;

import com.sun.star.document.XEmbeddedObjectSupplier;

final String sChartName = "MyChart";

com.sun.star.table.XTableChartsSupplier aSheet;

com.sun.star.table.CellRangeAddress[] aAddresses;

// get the sheet in which you want to insert a chart

// and query it for XTableChartsSupplier and store it in aSheet

//

// also get an array of CellRangeAddresses containing

// the data you want to visualize and store them in aAddresses

//

// for details see documentation of Spreadsheets

// ...

XChartDocument aChartDocument = null;

com.sun.star.table.XTableCharts aChartCollection = aSheet.getCharts();

XNameAccess aChartCollectionNA = (XNameAccess) UnoRuntime.queryInterface(

XNameAccess.class, aChartCollection );

// only insert the chart if it does not already exist

if (aChartCollectionNA != null && !aChartCollectionNA.hasByName(sChartName)) {

// following rectangle parameters are measured in 1/100 mm

com.sun.star.awt.Rectangle aRect = new com.sun.star.awt.Rectangle(1000, 1000, 15000, 9271);

// first bool: ColumnHeaders

// second bool: RowHeaders

aChartCollection.addNewByName(sChartName, aRect, aAddresses, true, false);

try {

com.sun.star.table.XTableChart aTableChart = (com.sun.star.table.XTableChart)

UnoRuntime.queryInterface(

com.sun.star.table.XTableChart.class,

aChartCollectionNA.getByName(sChartName));

// the table chart is an embedded object which contains the chart document

aChartDocument = (XChartDocument) UnoRuntime.queryInterface(

XChartDocument.class,

((XEmbeddedObjectSupplier) UnoRuntime.queryInterface(

XEmbeddedObjectSupplier.class,

aTableChart )).getEmbeddedObject());

} catch (com.sun.star.container.NoSuchElementException ex) {

System.out.println("Couldn't find chart with name " + sChartName + ": " + ex);

}

}

// now aChartDocument should contain an XChartDocument representing the newly inserted chart

#### Creating a Chart OLE Object in Draw and Impress

The alternative is to create an OLE shape and insert it into a draw page. Writer, Spreadsheet documents and Draw/Impress documents have access to a draw page. The shape is told to be a chart by setting the unique class-id.

| The unique Class-Id string for charts is “12dcae26-281f-416f-a234-c3086127382e”.
--- | ---

A draw page collection is obtained from the com.sun.star.drawing.XDrawPagesSupplier of a draw or presentation document. To retrieve a single draw page, use com.sun.star.container.XIndexAccess.

A spreadsheet document is also a com.sun.star.drawing.XDrawPagesSupplier that provides draw pages for all sheets, that is, the draw page for the third sheet is obtained by calling getByIndex(2) on the interface com.sun.star.container.XIndexAccess of the container, returned by com.sun.star.drawing.XDrawPagesSupplier:getDrawPages().

A spreadsheet draw page can be acquired directly at the corresponding sheet object. A single sheet supports the service com.sun.star.sheet.Spreadsheet that supplies a single page, com.sun.star.drawing.XDrawPageSupplier, where the page is acquired using the method getDrawPage().

The OpenOffice.org Writer currently does not support the creation of OLE Charts or Charts based on a text table in a Writer document using the API.

The document model is required once a chart is inserted. In charts inserted as OLE2Shape, the document model is available at the property Model of the OLE2Shape after setting the CLSID.

| Note, that the mechanism described for OLE objects seems to work in Writer, that is, you can see the OLE-Chart inside the Text document after executing the API calls described, but it is not treated as a real OLE object by the Writer. Thus, you can not activate it by double-clicking, because it puts the document into an inconsistent state.
--- | ---

The following example assumes a valid drawing document in the variable aDrawDoc and creates a chart in a Draw document. See 9 Drawing for an example of how to create a drawing document. (Charts/ChartHelper.java)

...

final String msChartClassID = "12dcae26-281f-416f-a234-c3086127382e";

com.sun.star.frame.XModel aDrawDoc;

// get a draw document into aDrawDoc

// ...

// this will become the resulting chart

XChartDocument aChartDoc;

com.sun.star.drawing.XDrawPagesSupplier aSupplier = (com.sun.star.drawing.XDrawPagesSupplier)

UnoRuntime.queryInterface(com.sun.star.drawing.XDrawPagesSupplier.class, aDrawDoc);

com.sun.star.drawing.XShapes aPage = null;

try {

// get first page

aPage = (com.sun.star.drawing.XShapes) aSupplier.getDrawPages().getByIndex(0);

} catch (com.sun.star.lang.IndexOutOfBoundsException ex) {

System.out.println("Document has no pages: " + ex);

}

if (aPage != null) {

// the document should be a factory that can create shapes

XMultiServiceFactory aFact = (XMultiServiceFactory) UnoRuntime.queryInterface(

XMultiServiceFactory.class, aDrawDoc);

if (aFact != null) {

try {

// create an OLE 2 shape

com.sun.star.drawing.XShape aShape = (com.sun.star.drawing.XShape)

UnoRuntime.queryInterface(

com.sun.star.drawing.XShape.class,

aFact.createInstance("com.sun.star.drawing.OLE2Shape"));

// insert the shape into the page

aPage.add(aShape);

aShape.setPosition(new com.sun.star.awt.Point(1000, 1000));

aShape.setSize(new com.sun.star.awt.Size(15000, 9271));

// make the OLE shape a chart

XPropertySet aShapeProp = (XPropertySet) UnoRuntime.queryInterface(

XPropertySet.class, aShape );

// set the class id for charts

aShapeProp.setPropertyValue("CLSID", msChartClassID);

// retrieve the chart document as model of the OLE shape

aChartDoc = (XChartDocument) UnoRuntime.queryInterface(

XChartDocument.class,

aShapeProp.getPropertyValue("Model"));

} catch(com.sun.star.uno.Exception ex) {

System.out.println("Couldn't change the OLE shape into a chart: " + ex);

}

}

}

#### Setting the Chart Type

The default when creating a chart is a bar diagram with vertical bars. If a different chart type is required, apply a different diagram type to this initial chart. For example, to create a pie chart, insert the default bar chart and change it to a pie chart.

To change the type of a chart, create an instance of the required diagram service by its service name using the service factory provided by the com.sun.star.chart.XChartDocument. This interface is available at the chart document model. After this service instance is created, set it using the setDiagram() method of the chart document.

In the following example, we change the chart type to a com.sun.star.chart.XYDiagram, also known as a scatter chart. We have assumed that there is a chart document in the variable aChartDoc already. The previous sections described how to create a document.

// let aChartDoc be a valid XChartDocument

// get the factory that can create diagrams

XMultiServiceFactory aFact = (XMultiServiceFactory) UnoRuntime.queryInterface(

XMultiServiceFactory.class, aChartDoc);

XDiagram aDiagram = aFact.createInstance("com.sun.star.chart.XYDiagram");

aChartDoc.setDiagram(aDiagram);

// now we have an xy-chart

Diagram Service Names
com.sun.star.chart.BarDiagram
com.sun.star.chart.AreaDiagram
com.sun.star.chart.LineDiagram
com.sun.star.chart.PieDiagram
com.sun.star.chart.DonutDiagram
com.sun.star.chart.NetDiagram
com.sun.star.chart.XYDiagram
com.sun.star.chart.StockDiagram

### 10.2.2 Accessing Existing Chart Documents

To get a container of all charts contained in a spreadsheet document, use the com.sun.star.table.XTableChartsSupplier of the service com.sun.star.sheet.Spreadsheet, which is available at single spreadsheets.

To get all OLE-shapes of a draw page, use the interface com.sun.star.drawing.XDrawPage, that is based on com.sun.star.container.XIndexAccess. You can iterate over all shapes on the draw page and check their CLSID property to find out, whether the found object is a chart.

## 10.3 Working with Charts

### 10.3.1 Document Structure

The important service for charts is com.sun.star.chart.ChartDocument. The chart document contains all the top-level graphic objects, such as a legend, up to two titles called Title and Subtitle,an axis title object for each primary axis if the chart supports axis. The com.sun.star.chart.ChartArea always exists. This is the rectangular region covering the complete chart documents background. The important graphical object is the diagram that actually contains the visualized data.

Apart from the graphical objects, the chart document is linked to some data. The required service for the data component is com.sun.star.chart.ChartData. It is used for attaching a data change listener and querying general properties of the data source, such as the number to be interpreted as NaN (“not a number”), that is, a missing value. The derived class com.sun.star.chart.ChartDataArray allows access to the actual values. Every component providing the ChartData service should also support ChartDataArray.

The following diagram shows the services contained in a chart and their relationships.

Illustration 1.1: ChartDocument

The name spaces in the diagram have been omitted to improve readability. The services are all in the name space com.sun.star.chart. The interfaces in this diagram are com.sun.star.chart.XChartDocument, com.sun.star.drawing.XShape, com.sun.star.lang.XComponent, and com.sun.star.beans.XPropertySet.

The chart document model passes its elements through the interface com.sun.star.chart.XChartDocument. This interface consists of the following methods:

com::sun::star::chart::XChartData getData()

void attachData( [in] com::sun::star::chart::XChartData xData)

com::sun::star::drawing::XShape getTitle()

com::sun::star::drawing::XShape getSubTitle()

com::sun::star::drawing::XShape getLegend()

com::sun::star::beans::XPropertySet getArea()

com::sun::star::chart::XDiagram getDiagram()

void setDiagram( [in] com::sun::star::chart::XDiagram xDiagram)

void dispose()

void addEventListener( [in] com::sun::star::lang::XEventListener xListener)

void removeEventListener( [in] com::sun::star::lang::XEventListener aListener)

boolean attachResource( [in] string aURL,

[in] sequence <com::sun::star::beans::PropertyValue aArgs)

string getURL()

sequence <com::sun::star::beans::PropertyValue > getArgs()

void connectController( [in] com::sun::star::frame::XController xController)

void disconnectController( [in] com::sun::star::frame::XController xController)

void lockControllers()

void unlockControllers()

boolean hasControllersLocked()

com::sun::star::frame::XController getCurrentController()

void setCurrentController( [in] com::sun::star::frame::XController xController)

com::sun::star::uno::XInterface getCurrentSelection()

### 10.3.2 Data Access

Data can only be accessed for reading when a chart resides inside a spreadsheet document and was inserted as a table chart, that is, the table chart obtains its data from cell ranges of spreadsheets. To change the underlying data, modify the content of the spreadsheet cells. For OLE charts, that is, charts that were inserted as OLE2Shape objects, modify the data.

The data of a chart is acquired from the com.sun.star.chart.XChartDocument interface of the chart document model using the method com.sun.star.chart.XChartDocument:getData(). The current implementation of OpenOffice.org charts provides a com.sun.star.chart.XChartDataArray interface, derived from com.sun.star.chart.XChartData and supports the service com.sun.star.chart.ChartDataArray.

| Note that the interface definition of com.sun.star.chart.XChartDocument does not require XChartDocument.getData() to return an XChartDataArray, but XChartData, so there may be implementations that do not offer access to the values.
--- | ---

The methods of XChartDataArray are:

sequence <sequence < double > > getData()

void setData( [in] sequence <sequence < double > > aData)

sequence < string > getRowDescriptions ()

void setRowDescriptions(sequence < string aRowDescriptions >)

sequence < string > getColumnDescriptions()

void setColumnDescriptions(sequence < string aColumnDescriptions >)

Included are the following methods from XChartData:

void addChartDataChangeEventListener(

[in] com::sun::star::chart::XChartDataChangeEventListener aListener)

void removeChartDataChangeEventListener (

[in] com::sun::star::chart::XChartDataChangeEventListener aListener)

double getNotANumber()

boolean isNotANumber( [in] double nNumber)

The com.sun.star.chart.XChartDataArray interface offers several methods to access data. A sequence of sequences is obtained containing double values by calling getData(). With setData(), such an array of values can be applied to the XChartDataArray.

The arrays are a nested array, not two-dimensional. Java has only nested arrays, but in Basic a nested array must be used instead of a multidimensional array. The following example shows how to apply values to a chart in Basic:

' create data with dimensions 2 x 3

' 2 is called outer dimension and 3 inner dimension

' assume that oChartDocument contains a valid

' com.sun.star.chart.XChartDocument

Dim oData As Object

Dim oDataArray( 0 To 1 ) As Object

Dim oSeries1( 0 To 2 ) As Double

Dim oSeries2( 0 To 2 ) As Double

oSeries1( 0 ) = 3.141

oSeries1( 1 ) = 2.718

oSeries1( 2 ) = 1.0

oSeries2( 0 ) = 17.0

oSeries2( 1 ) = 23.0

oSeries2( 2 ) = 42.0

oDataArray( 0 ) = oSeries1()

oDataArray( 1 ) = oSeries2()

' call getData() method of XChartDocument to get the XChartDataArray

oData = oChartDocument.Data

' call setData() method of XChartDataArray to apply the data

oData.Data = oDataArray()

' Note: to use the series arrays here as values for the series in the chart

' you have to set the DataRowSource of the XDiagram object to

' com.sun.star.chart.ChartDataRowSource.ROWS (default is COLUMNS)

| The Data obtianed is a sequence that contains one sequence for each row. If you want to interpret the inner sequences as data for the series, set the DataRowSource of your XDiagram to com.sun.star.chart.ChartDataRowSource.ROWS, otherwise, the data for the n th series is in the n th element of each inner series.
--- | ---

With the methods of the XChartData interface, check if a number from the chart has to be interpreted as non-existent, that is, the number is not a number (NaN).

| In the current implementation of OpenOffice.org Chart, the value of NaN is not the standard ISO value for NaN of the C++ double type, but instead DBL_MIN which is 2.2250738585072014 -308 .
--- | ---

Additionally, the XChartData interface is used to connect a component as a listener on data changes. For example, to use a spreadsheet as the source of the data of a chart that resides inside a presentation. It can also be used in the opposite direction: the spreadsheet could display the data from a chart that resides in a presentation document. To achieve this, the com.sun.star.table.CellRange service also points to the XChartData interface, so that a listener can be attached to update the chart OLE object.

The following class ListenAtCalcRangeInDraw demonstrates how to synchronize the data of a spreadsheet range with a chart residing in another document. Here the chart is placed into a drawing document.

The class ListenAtCalcRangeInDraw in the example below implements a com.sun.star.lang.XEventListener to get notified when the spreadsheet document or drawing document are closed.

It also implements a com.sun.star.chart.XChartDataChangeEventListener that listens for changes in the underlying XchartData . In this case, it is the range in the spreadsheet.

import com.sun.star.uno.UnoRuntime;

import com.sun.star.lang.XEventListener;

import com.sun.star.beans.XPropertySet;

import com.sun.star.lang.XComponent;

import com.sun.star.chart.*;

import com.sun.star.sheet.XSpreadsheetDocument;

// implement an XEventListener for listening to the disposing

// of documents. Also implement XChartDataChangeEventListener

// to get informed about changes of data in a chart

public class ListenAtCalcRangeInDraw implements XChartDataChangeEventListener {

public ListenAtCalcRangeInDraw(String args[]) {

// create a spreadsheet document in maSheetDoc

// create a drawing document in maDrawDoc

// put a chart into the drawing document

// and store it in maChartDocument

// ...

com.sun.star.table.XCellRange aRange;

// assign a range from the spreadsheet to aRange

// ...

// attach the data coming from the cell range to the chart

maChartData = (XChartData) UnoRuntime.queryInterface(XChartData.class, aRange);

maChartDocument.attachData(maChartData);

}

public void run() {

try {

// show a sub title to inform about last update

((XPropertySet) UnoRuntime.queryInterface(

XPropertySet.class, maChartDocument)).setPropertyValue(

"HasSubTitle", new Boolean(true));

// start listening for death of spreadsheet

((XComponent) UnoRuntime.queryInterface(XComponent.class,

maSheetDoc)).addEventListener(this);

// start listening for death of chart

((XComponent) UnoRuntime.queryInterface(XComponent.class,

maChartDocument)).addEventListener(this);

//start listening for change of data

maChartData.addChartDataChangeEventListener(this);

} catch (com.sun.star.uno.Exception ex) {

System.out.println("Oops: " + ex);

}

// call listener once for initialization

ChartDataChangeEvent aEvent = new ChartDataChangeEvent();

aEvent.Type = ChartDataChangeType.ALL;

chartDataChanged(aEvent);

}

// XEventListener (base interface of XChartDataChangeEventListener)

public void disposing(com.sun.star.lang.EventObject aSourceObj)

{

// test if the Source object is a chart document

if( UnoRuntime.queryInterface(XChartDocument.class, aSourceObj.Source) != null)

System.out.println("Disconnecting Listener because Chart was shut down");

// test if the Source object is a spreadsheet document

if (UnoRuntime.queryInterface(XSpreadsheetDocument.class, aSourceObj.Source) != null)

System.out.println("Disconnecting Listener because Spreadsheet was shut down");

// remove data change listener

maChartData.removeChartDataChangeEventListener(this);

// remove dispose listeners

((XComponent) UnoRuntime.queryInterface(XComponent.class,

maSheetDoc)).removeEventListener(this);

((XComponent) UnoRuntime.queryInterface(XComponent.class,

maChartDocument)).removeEventListener(this);

// this program cannot do anything any more

System.exit(0);

}

// XChartDataChangeEventListener

public void chartDataChanged(ChartDataChangeEvent aEvent)

{

// update subtitle with current date

String aTitle = new String("Last Update: " + new java.util.Date(System.currentTimeMillis()));

try {

((XPropertySet) UnoRuntime.queryInterface(XPropertySet.class,

maChartDocument.getSubTitle())).setPropertyValue(

"String", aTitle);

maChartDocument.attachData(maChartData);

} catch(Exception ex) {

System.out.println("Oops: " + ex);

}

System.out.println("Data has changed");

}

// private

private com.sun.star.sheet.XSpreadsheetDocument maSheetDoc;

private com.sun.star.frame.XModel maDrawDoc;

private com.sun.star.chart.XChartDocument maChartDocument;

private com.sun.star.chart.XChartData maChartData;

}

### 10.3.3 Chart Document Parts

In this section, the parts that most diagram types have in common are discussed. Specific chart types are discussed later.

#### Common Parts of all Chart Types

##### Diagram

The diagram object is an important object of a chart document. The diagram represents the visualization of the underlying data. The diagram object is a graphic object group that lies on the same level as the titles and the legend. From the diagram, data rows and data points are obtain that are also graphic objects that represent the respective data. Several properties can be set at a diagram to influence its appearance. Note that the term data series is used instead of data rows.

Some diagrams support the service com.sun.star.chart.Dim3DDiagram that contains the property Dim3D. If this is set to true, you get a three-dimensional view of the chart, which in OpenOffice.org is usually rendered in OpenGL. In 3-D charts, you can access the z-axis, which is not available in 2-D.

The service com.sun.star.chart.StackableDiagram offers the properties Percent and Stacked. With these properties, accumulated values can be stacked for viewing. When setting Percent to true, all slices through the series are summed up to 100 percent, so that for an AreaDiagram the whole diagram space would be filled with the series. Note that setting the Percent property also sets the Stacked property, because Percent is an addition to Stacked.

There is a third service that extends a base diagram type for displaying statistical elements called com.sun.star.chart.ChartStatistics. With this service, error indicators or a mean value line are added. The mean value line represents the mean of all values of a series. The regression curve is only available for the XYDiagram, because a numeric x-axis, is required.

Illustration 1.2: Diagram

The illustration above shows that there are eight base types of diagrams. The three services, StackableDiagram, Dim3DDiagram and ChartStatistics are also supported for several diagram types and allows extensions of the base types as discussed. For instance, a three-dimensional pie chart can be created, because the com.sun.star.chart.PieDiagram service points to the com.sun.star.chart.Dim3DDiagram service.

The services com.sun.star.chart.AreaDiagram, com.sun.star.chart.LineDiagram, and com.sun.star.chart.BarDiagram support all three feature services.

##### Axis

All charts can have one or more axis, except for pie charts. A typical two-dimensional chart has two axis, an x- and y-axis. Secondary x- or y-axis can be added to have up to four axis. In a three-dimensional chart, there are typically three axis, x-, y- and z-axis. There are no secondary axis in 3-dimensional charts.

An axis combines two types of properties:

- Scaling properties that affect other objects in the chart. A minimum and maximum values are set that spans the visible area for the displayed data. A step value can also be set that determines the distance between two tick-marks, and the distance between two grid-lines if grids are switched on for the corresponding axis.

- Graphical properties that influence the visual impression. These are character properties (see com.sun.star.style.CharacterProperties) affecting the labels and line properties (see com.sun.star.drawing.LineProperties) that are applied to the axis line and the tick-marks.

Illustration 1.3: Axis

Different diagram types support a different number of axis. In the above illustration, a com.sun.star.chart.XYDiagram, also known as a scatter diagram, is shown. The scatter diagram supports x- and y-axis, but not a z-axis as there is no 3-dimensional mode. The com.sun.star.chart.PieDiagram supports no axis at all. The com.sun.star.chart.BarDiagram supports all kinds of axis. Note that the z-Axis is only supported in a three-dimensional chart. Note that there is a com.sun.star.chart.ChartTwoAxisXSupplier that includes the com.sun.star.chart.ChartAxisXSupplier and is supported by all diagrams in OpenOffice.org required to support the service ChartAxisXSupplier.

The following example shows how to obtain an axis and how to change the number format.

import com.sun.star.chart.*;

import com.sun.star.beans.XPropertySet;

import com.sun.star.util.XNumberFormatsSupplier;

...

// class members

XChartDocument aChartDocument;

XDiagram aDiagram;

...

// get an XChartDocument and assign it to aChartDocument

// get the diagram from the document and assign it to aDiagram

// ...

// check whether the current chart supports a y-axis

XAxisYSupplier aYAxisSupplier = (XAxisYSupplier) UnoRuntime.queryInterface(

XAxisYSupplier.class, aDiagram);

if (aYAxisSupplier != null) {

XPropertySet aAxisProp = aYAxisSupplier.getYAxis();

// initialize new format with value for standard

int nNewNumberFormat = 0;

XNumberFormatsSupplier aNumFmtSupp = (XNumberFormatsSupplier)

UnoRuntime.queryInterface(XNumberFormatsSupplier.class,

aChartDocument);

if (aNumFmtSupp != null) {

com.sun.star.util.XNumberFormats aFormats = aNumFmtSupp.getNumberFormats();

Locale aLocale = new Locale("de", "DE", "de");

String aFormatStr = aFormats.generateFormat(

0, // base key

aLocale, // locale

true, // thousands separator on

true, // negative values in red

(short)3, // number of decimal places

(short)1 // number of leading ciphers

);

nNewNumberFormat = aFormats.addNew(aFormatStr, aLocale);

}

aAxisProp.setPropertyValue("NumberFormat", new Integer(nNewNumberFormat));

}

##### Data Series and Data Points

The objects that visualize the actual data are data series. The API calls them data rows that are not rows in a two-dimensional spreadsheet table, but as sets of data, because the data for a data row can reside in a column of a spreadsheet table.

The data rows contain data points. The following two methods at the com.sun.star.chart.XDiagram interface allow changes to the properties of a whole series or single data point:

com::sun::star::beans::XPropertySet getDataRowProperties( [in] long nRow)

com::sun::star::beans::XPropertySet getDataPointProperties( [in] long nCol,

[in] long nRow)

The index provided in these methods is 0-based, that is, 0 is the first series. In XYDiagrams, the first series has an index 1, because the first array of values contains the x-values of the diagram that is not visualized. This behavior exists for historical reasons.

In a spreadsheet context, the indexes for getDataPointProperties() are called nCol and nRow and are misleading. The nRow parameter gives the data row, that is, the series index. The nCol gives the index of the data point inside the series, regardless if the series is taken from rows or columns in the underlying table. To get the sixth point of the third series, write getDataPointProperties(5, 2).

Data rows and data points have com.sun.star.drawing.LineProperties and com.sun.star.drawing.FillProperties. They also support com.sun.star.style.CharacterProperties for text descriptions that can be displayed next to data points.

Properties can be set for symbols and the type of descriptive text desired. With the SymbolType property, one of several predefined symbols can be set. With SymbolBitmapURL, a URL that points to a graphic in a format known by OpenOffice.org can be set that is then used as a symbol in a com.sun.star.chart.LineDiagram or com.sun.star.chart.XYDiagram.

The following example demonstrates how to set properties of a data point. Before implementing this example, create a chart document and diagram of the type XYDiagram.

com.sun.star.chart.XChartDocument aChartDocument;

com.sun.star.chart.XDiagram aXYDiagram;

// get a chart document and assign it to aChartDocument

// set an "XYDiagram" and remember the diagram in aXYDiagram

// ...

// get the properties of the fifth data point of the first series

// note that index 1 is the first series only in XYDiagrams

try {

com.sun.star.beans.XPropertySet aPointProp = maDiagram.getDataPointProperties(4, 1);

} catch (com.sun.star.lang.IndexOutOfBoundsException ex) {

System.out.println("Index is out of bounds: " + ex);

System.exit(0);

}

try {

// set a bitmap via URL as symbol for the first series

aPointProp.setPropertyValue("SymbolType", new Integer(ChartSymbolType.BITMAPURL));

aPointProp.setPropertyValue("SymbolBitmapURL",

"http://graphics.openoffice.org/chart/bullet1.gif");

// add a label text with bold font, bordeaux red 14pt

aPointProp.setPropertyValue("DataCaption", new Integer(ChartDataCaption.VALUE));

aPointProp.setPropertyValue("CharHeight", new Float(14.0));

aPointProp.setPropertyValue("CharColor", new Integer(0x993366));

aPointProp.setPropertyValue("CharWeight", new Float(FontWeight.BOLD));

} catch (com.sun.star.uno.Exception ex) {

System.out.println("Exception caught: " + ex);

}

#### Features of Special Chart Types

Examples of some of the services that are not available for all chart types are discussed in this section. Only examples that can be changed in specific chart types only are discussed.

##### Statistics

Statistical properties like error indicators or regression curves can be applied. The regression curves can only be used for xy-diagrams that have tuples of values for each data point. The following example shows how to add a linear regression curve to an xy-diagram.

Additionally, the mean value line is displayed and error indicators for the standard deviation are applied.

// get the diagram

// ...

// get the properties of a single series

XPropertySet aProp = maDiagram.getDataRowProperties(1)

// set a linear regression

aProp.setPropertyValue("RegressionCurves", ChartRegressionCurveType.LINEAR);

// show a line at y = mean of the series' values

aProp.setPropertyValue("MeanValue", new Boolean(true));

// add error indicators in both directions

// with the length of the standard deviation

aProp.setPropertyValue("ErrorCategory", ChartErrorCategory.STANDARD_DEVIATION);

aProp.setPropertyValue("ErrorIndicator", ChartErrorIndicatorType.TOP_AND_BOTTOM);

##### 3-D Charts

Some chart types can display a 3-dimensional representation. To switch a chart to 3-dimensional, set the boolean property Dim3D of the service com.sun.star.chart.Dim3DDiagram.

In addition to this property, bar charts support a property called Deep (see service com.sun.star.chart.BarDiagram) that arranges the series of a bar chart along the z-axis, which in a chart, points away from the spectator. The service com.sun.star.chart.Chart3DBarProperties offers a property SolidType to set the style of the data point solids. The solid styles can be selected from cuboids, cylinders, cones, and pyramids with a square base (see constants in com.sun.star.chart.ChartSolidType).

The XDiagram of a 3-dimensional chart is also a scene object that supports modification of the rotation and light sources. The example below shows how to rotate the scene object and add another light source.

// prerequisite: maDiagram contains a valid bar diagram

// ...

import com.sun.star.drawing.HomogenMatrix;

import com.sun.star.drawing.HomogenMatrixLine;

import com.sun.star.chart.X3DDisplay;

import com.sun.star.beans.XPropertySet;

XPropertySet aDiaProp = (XPropertySet) UnoRuntime.queryInterface(XPropertySet.class, maDiagram);

Boolean aTrue = new Boolean(true);

aDiaProp.setPropertyValue("Dim3D", aTrue);

aDiaProp.setPropertyValue("Deep", aTrue);

// from service Chart3DBarProperties:

aDiaProp.setPropertyValue("SolidType", new Integer(

com.sun.star.chart.ChartSolidType.CYLINDER));

// change floor color to Magenta6

XPropertySet aFloor = ((X3DDisplay) UnoRuntime.queryInterface(

X3DDisplay.class, maDiagram)).getFloor();

aFloor.setPropertyValue("FillColor", new Integer(0x6b2394));

// rotate the scene using a homogen 4x4 matrix

// -------------------------------------------

HomogenMatrix aMatrix = new HomogenMatrix();

// initialize matrix with identity

HomogenMatrixLine aLines[] = new HomogenMatrixLine[] {

new HomogenMatrixLine(1.0, 0.0, 0.0, 0.0),

new HomogenMatrixLine(0.0, 1.0, 0.0, 0.0),

new HomogenMatrixLine(0.0, 0.0, 1.0, 0.0),

new HomogenMatrixLine(0.0, 0.0, 0.0, 1.0)

};

aMatrix.Line1 = aLines[0];

aMatrix.Line2 = aLines[1];

aMatrix.Line3 = aLines[2];

aMatrix.Line4 = aLines[3];

// rotate 10 degrees along the x axis

double fAngle = 10.0;

double fCosX = java.lang.Math.cos(java.lang.Math.PI / 180.0 * fAngle);

double fSinX = java.lang.Math.sin(java.lang.Math.PI / 180.0 * fAngle);

// rotate -20 degrees along the y axis

fAngle = -20.0;

double fCosY = java.lang.Math.cos(java.lang.Math.PI / 180.0 * fAngle);

double fSinY = java.lang.Math.sin(java.lang.Math.PI / 180.0 * fAngle);

// rotate -5 degrees along the z axis

fAngle = -5.0;

double fCosZ = java.lang.Math.cos(java.lang.Math.PI / 180.0 * fAngle);

double fSinZ = java.lang.Math.sin(java.lang.Math.PI / 180.0 * fAngle);

// set the matrix such that it represents all three rotations in the order

// rotate around x axis then around y axis and finally around the z axis

aMatrix.Line1.Column1 = fCosY * fCosZ;

aMatrix.Line1.Column2 = fCosY * -fSinZ;

aMatrix.Line1.Column3 = fSinY;

aMatrix.Line2.Column1 = fSinX * fSinY * fCosZ + fCosX * fSinZ;

aMatrix.Line2.Column2 = -fSinX * fSinY * fSinZ + fCosX * fCosZ;

aMatrix.Line2.Column3 = -fSinX * fCosY;

aMatrix.Line3.Column1 = -fCosX * fSinY * fCosZ + fSinX * fSinZ;

aMatrix.Line3.Column2 = fCosX * fSinY * fSinZ + fSinX * fCosZ;

aMatrix.Line3.Column3 = fCosX * fCosY;

aDiaProp.setPropertyValue("D3DTransformMatrix", aMatrix);

// add a red light source

// ----------------------

// in a chart by default only the second (non-specular) light source is switched on

// light source 1 is the only specular light source that is used here

// set direction

com.sun.star.drawing.Direction3D aDirection = new com.sun.star.drawing.Direction3D();

aDirection.DirectionX = -0.75;

aDirection.DirectionY = 0.5;

aDirection.DirectionZ = 0.5;

aDiaProp.setPropertyValue("D3DSceneLightDirection1", aDirection);

aDiaProp.setPropertyValue("D3DSceneLightColor1", new Integer(0xff3333));

aDiaProp.setPropertyValue("D3DSceneLightOn1", new Boolean(true));

Refer to 9 Drawing for additional details about three-dimensional properties.

##### Pie Charts

Pie charts support the offset of pie segments with the service com.sun.star.chart.ChartPieSegmentProperties that has a property SegmentOffset to drag pie slices radially from the center up to an amount equal to the radius of the pie. This property reflects a percentage, that is, values can go from 0 to 100.

// ...

// drag the fourth segment 50% out

aPointProp = maDiagram.getDataPointProperties(3, 0)

aPointProp.setPropertyValue("SegmentOffset", 50)

Note that the SegmentOffset property is not available for donut charts and three-dimensional pie charts.

##### Stock Charts

A special data structure must be provided when creating stock charts. When acom.sun.star.chart.StockDiagram is set as the current chart type, the data is interpreted in a specific manner depending on the properties Volume and UpDown. The following table shows what semantics are used for the data series.

Volume | UpDown | Series 1 | Series 2 | Series 3 | Series 4 | Series 5
--- | --- | --- | --- | --- | --- | ---
false | false | Low | High | Close | - | -
true | false | Volume | Low | High | Close | -
false | true | Open | Low | High | Close | -
true | true | Volume | Open | Low | High | Close

For example, if the property Volume is set to false and UpDown to true, the first series is interpreted as the value of the stock when the stock exchange opened, and the fourth series represents the value when the stock exchange closed. The lowest and highest value during the day is represented in series two and three, respectively.

## 10.4 Chart Document Controller

Although chart document models have a method getCurrentController(), this method currently returns null, therefore the chart controller can not be used.

## 10.5 Chart Add-Ins

Chart types can also be created by developing components that serve as chart types. Existing chart types can be extended by adding additional shapes or modifying the existing shapes. Alternatively, a chart can be created from scratch. If drawing from scratch, it is an empty canvas and all shapes would have to be drawn from scratch.

Chart Add-Ins are actually UNO components, thus, you should be familiar with the chapter 4 Writing UNO Components.

### 10.5.1 Prerequisites

The following interfaces must be supported for a component to serve as a chart add-in:

- com.sun.star.lang.XInitialization

- com.sun.star.util.XRefreshable

- com.sun.star.lang.XServiceName

- com.sun.star.lang.XServiceInfo

- com.sun.star.lang.XTypeProvider to access the add-in interfaces from OpenOffice.org Basic and other interpreted programming languages (optional).

In addition to these interfaces, the following services must be supported and returned in the getSupportedServiceNames() method of com.sun.star.lang.XServiceInfo):

- com.sun.star.chart.Diagram

- A unique service name that identifies the component. This service name has to be returned in the getServiceName() method of com.sun.star.lang.XServiceName.

### 10.5.2 How Add-Ins work

The method initialize () from the com.sun.star.lang.XInitialization interface is the first method that is called for an add-in. It is called directly after it is created by the com.sun.star.lang.XMultiServiceFactory provided by the chart document. This method gets the XChartDocument object.

When initialize() is called, the argument returned is the chart document. Store this as a member to that it can be called later in the refresh() call to access all elements of the chart. The following is an example for the initialize () method of an add-in written in Java:

// XInitialization

public void initialize(Object[] aArguments) throws Exception, RuntimeException {

if (aArguments.length > 0) {

// maChartDocument is a member

// which is set to the parent chart document

// that is given as first argument

maChartDocument = (XChartDocument) UnoRuntime.queryInterface(

XChartDocument.class, aArguments[0]);

XPropertySet aDocProp = (XPropertySet) UnoRuntime.queryInterface(

XPropertySet.class, maChartDocument);

if (aDocProp != null) {

// set base diagram which will be extended in refresh()

aDocProp.setPropertyValue("BaseDiagram", "com.sun.star.chart.XYDiagram");

}

// remember the draw page, as it is frequently used by refresh()

// (this is not necessary but convenient)

XDrawPageSupplier aPageSupp = (XDrawPageSupplier) UnoRuntime.queryInterface(

XDrawPageSupplier.class, maChartDocument);

if( aPageSupp != null )

maDrawPage = (XDrawPage) UnoRuntime.queryInterface(

XDrawPage.class, aPageSupp.getDrawPage());

}

}

An important method of an add-in component is refresh() from the com.sun.star.util.XRefreshable. This method is called every time the chart is rebuilt. A change of data results in a refresh, but also a resizing or changing of a property that affects the layout calls the refresh() method. For example, the property HasLegend that switches the legend on and off.

To add shapes to the chart, create them once and modify them later during the refresh calls. In the following example, a line is created in initialize() and modified during refresh():

// XInitialization

public void initialize(Object[] aArguments) throws Exception, RuntimeException {

// get document and page -- see above

// ...

// get a shape factory

maShapeFactory = ...;

// create top line

maTopLine = (XShape) UnoRuntime.queryInterface(

XShape.class, maShapeFactory.createInstance("com.sun.star.drawing.LineShape"));

maDrawPage.add(maTopLine);

// make line red and thicker

XPropertySet aShapeProp = (XPropertySet)UnoRuntime.queryInterface(

XPropertySet.class, maTopLine);

aShapeProp.setPropertyValue("LineColor", new Integer(0xe01010));

aShapeProp.setPropertyValue("LineWidth", new Integer(50));

// create bottom line

maBottomLine = (XShape) UnoRuntime.queryInterface(

XShape.class, maShapeFactory.createInstance("com.sun.star.drawing.LineShape"));

maDrawPage.add(maBottomLine);

// make line green and thicker

aShapeProp = (XPropertySet) UnoRuntime.queryInterface(

XPropertySet.class, maBottomLine);

aShapeProp.setPropertyValue("LineColor", new Integer(0x10e010));

aShapeProp.setPropertyValue("LineWidth", new Integer(50));

}

}

// XRefreshable

public void refresh() throws RuntimeException {

// position lines

// --------------

// get data

XChartDataArray aDataArray = (XChartDataArray) UnoRuntime.queryInterface(

XChartDataArray.class, maChartDocument.getData());

double aData[][] = aDataArray.getData();

// get axes

XDiagram aDiagram = maChartDocument.getDiagram();

XShape aXAxis = (XShape) UnoRuntime.queryInterface(

XShape.class, ((XAxisXSupplier) UnoRuntime.queryInterface(

XAxisXSupplier.class, aDiagram)).getXAxis());

XShape aYAxis = (XShape) UnoRuntime.queryInterface(

XShape.class, ((XAxisYSupplier) UnoRuntime.queryInterface(

XAxisYSupplier.class, aDiagram)).getYAxis());

// calculate points for hull

final int nLength = aData.length;

int i, j;

double fMax, fMin;

Point aMaxPtSeq[][] = new Point[1][];

aMaxPtSeq[0] = new Point[nLength];

Point aMinPtSeq[][] = new Point[1][];

aMinPtSeq[0] = new Point[nLength];

for (i = 0; i < nLength; i++) {

fMin = fMax = aData[i][1];

for (j = 1; j < aData[i].length; j++) {

if (aData[i][j] > fMax)

fMax = aData[i][j];

else if (aData[i][j] < fMin)

fMin = aData[i][j];

}

aMaxPtSeq[0][i] = new Point(getAxisPosition(aXAxis, aData[i][0], false),

getAxisPosition(aYAxis, fMax, true));

aMinPtSeq[0][i] = new Point(getAxisPosition(aXAxis, aData[i][0], false),

getAxisPosition(aYAxis, fMin, true));

}

// apply point sequences to lines

try {

XPropertySet aShapeProp = (XPropertySet) UnoRuntime.queryInterface(

XPropertySet.class, maTopLine);

aShapeProp.setPropertyValue("PolyPolygon", aMaxPtSeq);

aShapeProp = (XPropertySet) UnoRuntime.queryInterface(

XPropertySet.class, maBottomLine);

aShapeProp.setPropertyValue("PolyPolygon", aMinPtSeq);

} catch (Exception ex) {

}

}

// determine the position of a value along an axis

// bVertical is true for the y-axis and false for the x-axis

private int getAxisPosition(XShape aAxis, double fValue, boolean bVertical) {

int nResult = 0;

if (aAxis != null) {

XPropertySet aAxisProp = (XPropertySet) UnoRuntime.queryInterface(

XPropertySet.class, aAxis);

try {

double fMin, fMax;

fMin = ((Double) aAxisProp.getPropertyValue("Min")).doubleValue();

fMax = ((Double) aAxisProp.getPropertyValue("Max")).doubleValue();

double fRange = fMax - fMin;

if (fMin <= fValue && fValue <= fMax && fRange != 0) {

if (bVertical) {

// y==0 is at the top, thus take 1.0 - ...

nResult = aAxis.getPosition().Y +

(int)((double)(aAxis.getSize().Height) * (1.0 - ((fValue - fMin) / fRange)));

} else {

nResult = aAxis.getPosition().X +

(int)((double)(aAxis.getSize().Width) * ((fValue - fMin) / fRange));

}

}

} catch (Exception ex) {

}

}

return nResult;

}

The subroutine getAxisPosition() is a helper to determine the position of a point inside the diagram coordinates. This add-in calculates the maximum and minimum values for each slice of data points, and creates two polygons based on these points.

For an add-in example written in C++, look at the sample addin of the graphics/sch project on www.openoffice.org.

### 10.5.3 How to Apply an Add-In to a Chart Document

There is no method to set an add-in as a chart type for an existing chart in the graphical user interface. To set the chart type, use an API script, for instance, in OpenOffice.org Basic. The following example sets the add-in with service name “com.sun.star.comp.Chart.JavaSampleChartAddIn” at the current document. To avoid problems, it is advisable to create a chart that has the same type as the one that the add-in sets as BaseDiagram type.

Sub SetAddIn

Dim oDoc As Object

Dim oSheet As Object

Dim oTableChart As Object

Dim oChart As Object

Dim oAddIn As Object

' assume that the current document is a spreadsheet

oDoc = ThisComponent

oSheet = oDoc.Sheets( 0 )

' assume also that you already added a chart

' named "MyChart" on the first sheet

oTableChart = oSheet.Charts.getByName( "MyChart" )

If Not IsNull( oTableChart ) Then

oChart = oTableChart.EmbeddedObject

If Not IsNull( oChart ) Then

oAddIn = oChart.createInstance( "com.sun.star.comp.Chart.JavaSampleChartAddIn" )

If Not IsNull( oAddIn ) Then

oChart.setDiagram( oAddIn )

End If

End If

End If

End Sub

| If you want to create an XML-File on your own and want to activate your add-in for a chart; set the attribute chart:class of the chart:chart element to “add-in” and the attribute chart:add-in-name to the service name that uniquely identifies your component.
--- | ---

[ Previous document | Content Table | Next document ]