Chapter 4:
The Data Interface


 

The library supports these methods of providing data for charting:

 

Callback functions

 

SEND_DATA Interface

 

Your application can use only one of these methods in a chart.

 

CALLBACK FUNCTIONS INTERFACE

 

 

Initializing the Data Callback Functions

 

 

Data Callback Function

 

 

Label Callback Function

 

 

String Callback Function

 

 

Page Callback Function

 

 

Real-to-String Callback Function

 

 

Raw Limits Callback Function

 

 

Picture Callback Function

 

SEND DATA INTERFACE

 

 

Initializing the Send Data Interface

 

 

Send Data API Functions

CALLBACK FUNCTIONS INTERFACE

 

The callback configuration of the DLL requires your application to provide a callback function. The function is called by the DLL with a row and column number to provide the data for charting. This configuration is best when your application is already buffering the data to be graphed. For example, your application collects the data from a database, a mainframe, a network, proprietary file format, etc., and buffers it for other purposes. Each time the graph is drawn the DLL calls your function to provide each data point and string for the chart. The following call-back functions are defined in the header file:

 

CALLBACK *LPFNGetDataCallBack

 

CALLBACK *LPFNGetLabelCallBack

 

CALLBACK *LPFNGetStringCallBack

 

CALLBACK *LPFNSetPageCallBack

 

CALLBACK *LPFNRealToStringCallBack

 

CALLBACK *LPFNGetRawLimits

 

CALLBACK *LPFNPictureCallBack

 

This data interface calls a function in your application. A parameter points to a location for your function to load the requested data. The library does not buffer the data; once the data is charted, the data is overwritten with the next piece of data for charting. This data interface is best when your application already buffers the data. This prevents double buffering of the data by the library. Each time the chart is drawn by DrawTheGraph(), the data is requested from your application.

Initializing the Data Callback Functions

 

The library requires pointers to the data callback functions in your application. The following example demonstrates how to initialize the callback functions:

 

BOOL PUBLIC InitDataCallBacks(
     HANDLE hInst, GraphPtr pGraph,
     INT16 nSeries, INT16 nGroups)

 

{
     GraphDataInfo graphData;
     /*Look up data information connected to this graph*/
     GetGraphDataInfo(pGraph, &graphData);

 

     /* If the data information doesn't */
     /* exist, create thunks as necessary*/
     if (!graphData.lpfnGetData)

 

     {
     graphData.lpfnGetString = (LPFNGetStringCallBack)
          myGetStringCallback;
     graphData.lpfnGetLabel = (LPFNGetLabelCallBack)
          myGetLabelCallback;
     graphData.lpfnGetData = (LPFNGetDataCallBack)
          myGetDataCallback;
     /* Set to null when not used */
     graphData.lpfnSetPage = NULL;      // Optional
     graphData.lpfnRealToString = NULL; // Optional
     graphData.lpfnGetRawLimits = NULL; // Optional
     graphData.lpfnPicture = NULL;      // Optional
     }

 

     /* Good practice to explicitly */
     /* set Data Interface Mode  */
     graphData.nDataIFMode = DATAIF_CALLBACK;

 

     /* always set datalook-related info */
     /* nPages should always be set to 1 */
     graphData.nPages = 1;

 

     /* Set to number of rows to graph */
     graphData.nRows = nSeries;

 

     /* Set to number of columns to graph */
     graphData.nCols = nGroups;

 

     /* PUT HANDLE TO THIS GRAPH INTO A FIELD THAT IS
     PASSED TO EVERY DATA CALLBACK FUNCTION. THIS LETS
     YOUR CALLBACK FUNCTION KNOW WHICH GRAPH
     IS DOING THE CALLING. */
     graphData.lClientData = (INT32) pGraph;
     /* Register callback functions with PG lib */
     return SetGraphDataInfo(pGraph, &graphData);
}

 

Note that the graphData structure contains the number of rows and columns. That is because the data is organized like a spreadsheet. Do not confuse the rows and columns with series and groups.

Data Callback Function

 

This function provides the actual data for charting. The library resolves series and groups to the way your application data is stored as rows and columns. This means your application should NOT try to swap rows and columns when the data axes are reversed. Your application data model should be similar to a spreadsheet, with data stored in rows and columns. The illustration below shows the spreadsheet style data model:

 

 

The prototype for the data callback function pointer describes each of the parameters passed to your application callback.

 

typedef INT16 (CALLBACK *LPFNGetDataCallBack)
(
     INT16 nDataPt, /* Row # of requested value */
     INT16 nSeries, /* Column # of requested value */
     /* Caller provided buffer for REAL64 assignment */
     REAL64 FAR * rVal,
     INT32 lClientData /* Client data value */
);

 

Your application data value must be a REAL64 when passed to the library. If necessary, cast the value to a REAL64. Sample code for the data callback function is shown below:

 

int NumData1 [4][3] =
{
     {200,300,400},
     {300,400,500},
     {400,500,600},
     {500,600,700}
};
INT16 FAR PASCAL LOCAL_GetDataCallback(
     INT16 nRow, /* Row # of requested value */
     /* Column # of requested value */
     /* Put answer here */
     INT16 nCol, REAL64 FAR *prVal,
     INT32 lClientData) /* Client data value */
{
     /* return data value for row, col requested */
     *prVal = (REAL64) NumData [nRow][nCol];
     return TRUE;
}

 

If you want to change the size of an existing chart, your application should get the GraphDataInfo structure using GetGraphDataInfo(). Use SetGraphDataInfo() to modify the number of rows and columns in the chart. You do NOT need to reinitialize the callback function pointers if you are using the same callbacks to provide data to the library, only change the number of rows and columns. Return FALSE if the data is missing or if you want to skip this data point.

Label Callback Function

 

This function provides the strings used to draw the charts group labels and the legend series labels. The library resolves series and groups to the way your application data is stored as rows and columns. This means your application should NOT try to swap rows and columns when the data axes are reversed. Your application data model should be similar to a spreadsheet, with data stored in rows and columns.

 

 

The prototype for the label callback function pointer describes each of the parameters passed to your application callback:

 

typedef INT16 (CALLBACK *LPFNGetLabelCallBack)
(
     INT16 nCol, /* Col # of requested label */
     INT16 nRow, /* Row # of requested label. */
     char FAR * lpBuffer, /* Caller provided buffer */
     INT16 nDimBuffer, /* Buffer length. */
     INT32 lClientData /* client data value */
);

 

A row label is requested when nCol is equal to -1. A column label is requested when nRow is equal to -1. The example of the label callback function shown below uses a global array to store the strings. Make sure you check the string length before copying a string into the buffer area. Return FALSE if the label is missing or if you want to skip this label.

 

char StringData[12][64] = {
" Sample Chart",
"by Three |D| Graphics",
"Dan", "Elmer","Jon","Mark",
"May", "June", "July",
"The Row Title", "The Column Title",
"A footnote"};
INT16 FAR PASCAL LOCAL_GetLabelCallback(
     INT16 nCol, /* -1 when traversing Row headers */
     INT16 nRow, /* -1 when traversing Col headers */
     char FAR *lpBuffer, /* Put answer here. */
     INT16 nDimBuffer, /* Max.size of answer. */
     INT32 lClientData ) /* client data value */
{
     int nOffset;
     if (nCol == -1)
     {     /* if -1, traversing row headers */
          nOffset = 2 + nRow;
     }
     else
     {
          /* else traversing col headers */
          if (nRow == -1) nOffset = 6 + nCol;
          else
          /* any other gets the footnote string */
          nOffset = 11;
     }
     /* return the correct header */
     if(lstrlen(StringData[nOffset])< nDimBuffer)
          lstrcpy (lpBuffer,StringData[nOffset]);
     else lstrcpy (lpBuffer,"XXXX");
     return TRUE;
}

String Callback Function

 

The string callback function provides the strings used to draw the following chart titles:

 

title

 

subtitle

 

footnote

 

series title

 

group title

 

X, Y1, Y2, and Z-axis titles

 

 

The prototype for the string callback function pointer describes each of the parameters passed to your application callback.

 

typedef INT16 (CALLBACK *LPFNGetStringCallBack)
(
     /* Ordinal identifier of requested string */
     INT16 nWhich,
     /* Caller provided buffer for string */
     char FAR * lpBuffer,
     /* Buffer length */
     INT16 nDimBuffer,
     /* Client data value */
     INT32 lClientData
)

 

The constants used for the ordinal ID number (nWhich) are defined in the enum structure GraphDataStringType in the header file:

 

typedef enum _GraphDataStringType {
     gsTITLE,
     gsSUBTITLE,
     gsFOOTNOTE,
     gsSERIESTITLE,
     gsGROUPSTITLE,
     gsXAXISTITLE,
     gsY1AXISTITLE,
     gsY2AXISTITLE,
     gsZAXISTITLE,
     gsY3AXISTITLE (reserved for future use)
     gsY4AXISTITLE (reserved for future use)
     MAX_GraphDataStringType
} GraphDataStringType;

 

The example of the string callback function shown below uses a global array to store the strings. Make sure you check the string length before copying a string into the buffer area. Return FALSE if the requested string is missing or you do not want to set a particular string.

 

char StringData[12][64] =
{
     " Sample Chart","by Three |D| Graphics",
     "Dan", "Elmer","Jon","Mark",
     "May", "June", "July",
     "The Row Title","The Column Title","A footnote"};
}

 

INT16 FAR PASCAL LOCAL_GetStringCallback(
     INT16 nWhich,       /* Which title */
     char FAR *lpBuffer, /* Put answer here */
     INT16 nDimBuffer,   /* Max.size of answer */
     INT32 lClientData   /* client data */
)

 

{
     switch (nWhich)
     {

 

     case gsTITLE:
          /* return the title for the chart */
          if(lstrlen(StringData[0] < nDimBuffer)
               lstrcpy (lpBuffer,StringData[0]);
          else lstrcpy (lpBuffer,"XXXX");
          break;

 

     case gsSUBTITLE:
          /* return the subtitle for the chart */
          if(lstrlen(StringData[1] < nDimBuffer)
               lstrcpy (lpBuffer,StringData[1]);
          else lstrcpy (lpBuffer,"XXXX");
          break;

 

     case gsFOOTNOTE:
          /* return the footnote for the chart */
          if(lstrlen(StringData[11] < nDimBuffer)
               lstrcpy (lpBuffer,StringData[11]);
          else lstrcpy (lpBuffer,"XXXX");
          break;

 

     case gsSERIESTITLE:
          /* return the series title */

 

     case gsY1AXISTITLE:
          /* return the Y1 axis title */
          if(lstrlen(StringData[10] < nDimBuffer)
               lstrcpy (lpBuffer,StringData[10]);
          else lstrcpy (lpBuffer,"XXXX");
          break;

 

     case gsGROUPSTITLE:
          /* return the group title */

 

     case gsXAXISTITLE:
          /* return the X axis title */
          if(lstrlen(StringData[9] < nDimBuffer)
               lstrcpy (lpBuffer,StringData[9]);
          else lstrcpy (lpBuffer,"XXXX");
          break;

 

     default:
          /* If unknown, put XXXX */
          lstrcpy (lpBuffer,"XXXX");
     }
     return TRUE;
}

Page Callback Function

 

This call back function provides the current graph page:

 

typedef INT16 (CALLBACK *LPFNSetPageCallBack)
(
     INT16 nPage, /* New current page */
     INT32 lClientData /* Client data value */  
);

 

This function is only used for 3D stacked charts.

Real-to-String Callback Function

 

This callback function passes object, series, and group information:

 

typedef INT16 (CALLBACK *LPFNRealToStringCallBack)
(
     REAL64 rVal, /* Value to be formatted */     
     char FAR * lpResult, /* Caller provided buffer */
     INT16 nDimBuffer, /* Buffer length */
     INT16 nFmtCode, /* Format Conversion Identifier */
     INT16 nObjectID,/* Object ID */
     INT16 nSeriesID, /* Series ID */
    INT16 nGroupID, /* Group ID */
     INT32 lClientData /* Client Data Value */
);

 

The following example is a simple utility function that formats a numeric value of type REAL64 into a string. If any piece of numeric data has its nformat code (A2D_FORMAT_xx) set equal to or greater than 60, this callback is triggered. This lets you do your own custom numeric formatting instead of using built-in PGSDK logic.

 

INT16 FAR PASCAL LOCAL_RealToStrCallback(
     REAL64 rVal, /* Value to be formatted */
     char FAR * lpszResult, /* Caller provided buffer */
     INT16 nDimBuffer, /* Buffer length */
     INT16 nFmtCode, /* Format conversion ID */
     INT16 nObjectID, INT16 nSeriesID, INT16 nGroupID,
     INT32 lClientData) /* Client Data Value */
{     /* JUST DO SIMPLE INT FORMAT */
     wsprintf(lpszResult,"%d",(int) rVal);
     return TRUE;
}

Raw Limits Callback Function

 

This function provides the raw limits of the chart. The raw limits represent the actual range of values in the chart data set.

 

typedef BOOLEAN16 (CALLBACK *LPFNGetRawLimits)
(
     INT16 nWhichAxis, /* Axis limits requested */
     REAL64 FAR * prMin, /* Axis raw minimum */
    REAL64 FAR * prMax, /* Axis raw maximum. */
    INT32 lClientData /* Client data value */ 
);

 

Normally, PGSDK does a complete scan through the data before imaging a chart. This allows the system to determine the minimum and maximum values for any axis. If you want to optimize performance or disable the minimum/maximum logic, you can register this call back. If registered, PGSDK will NOT scan the data. Instead, it will call this function and use the minimum/maximum values that are provided.

Picture Callback Function

 

This callback function can be used when the user has a specified a handle for a picture:

 

typedef INT16 (CALLBACK *LPFNPictureCallBack)
(
     INT32 lClientData, /* Client data value */
     /* String that identifies handle */
     char FAR * lppictName,
     /* Caller supplied picture handle */
     INT32 FAR * lphPicture,     
     /* Caller supplied flag; TRUE = APM Metafile*/
     BOOLEAN16 FAR * lpbIsAPM,
     /* Caller supplied World Coord box for APM Metafile*/
     RECT FAR * lpAPMbox
);

 

Set lpbIsAPM to TRUE for an APM metafile. For an APM metafile, use lpAPMbox, to specify a rectangle in world coordinates.

 

This function is only used when special affects are created with A_AREASFX.

SEND DATA INTERFACE

 

The Send Data Interface provides API functions for your application to load the strings and data directly into the chart object for buffering. This configuration is best when your application can get the data but does not keep it buffered. The graph uses the same data for drawing until your application explicitly loads new data. When a graph is saved to an output file both the graph and the data can be saved in the output file.

 

Using the send data interface, your application must send all the charting data to the chart for buffering. The data is sent to the chart using API function calls. This interface is best when your application has access to the data but does not buffer it. For example, when the data is stored in a disk file, your application could retrieve the data once, and send it to the library for buffering. Each time the chart is drawn, the library retrieves the data from its own internal storage and draws the chart.

Initializing the Send Data Interface

 

The library GraphDataInfo structure must be initialized to set the number of rows and columns for data and to initialize the library for the send data interface. The example shown below demonstrates how to initialize the library.

 

BOOL PUBLIC InitDataCallBacks(
     HANDLE hInst, GraphPtr pGraph,
     INT16 nSeries, INT16 nGroups)
{
     GraphDataInfo graphData;
     /* PG - Look up data information
     ** connected to this graph */
     GetGraphDataInfo(pGraph, &graphData);
     /* always set datalook-related info */
     /* # Pages should always be 1 */
     graphData.nPages = 1;
     /* # of Rows to Graph */
     graphData.nRows = nSeries;
     /* # of Columns to Graph */
     graphData.nCols = nGroups;
     /* PUT HANDLE TO THIS GRAPH INTO A FIELD THAT IS */
     /* PASSED TO EVERY DATA CALLBACK FUNCTION. THIS */
     /* LETS THE FUNCTION KNOW WHICH GRAPH */
     /* IS DOING THE CALLING. */
     graphData.lClientData = (INT32) pGraph;
     graphData.nDataIFMode = DATAIF_SENDDATA;
/* To be safe, we always set unused callbacks to NULL.*/
/* Since this sample uses the SEND_DATA/DM method, */
/* ALL of the callbacks should be set to NULL */
     graphData.lpfnGetString = NULL;
     graphData.lpfnGetLabel = NULL;
     graphData.lpfnGetData = NULL;
     graphData.lpfnSetPage = NULL;
     graphData.lpfnRealToString = NULL;
     graphData.lpfnGetRawLimits = NULL;
     graphData.lpfnPicture = NULL;
     /* PG - Register the callback
     * functions with the PG library */
     return SetGraphDataInfo(pGraph, &graphData);
}

 

Note that the structure contains the number of rows and columns. That is because the data is organized like a spreadsheet. Do not confuse the rows and columns with series and groups. Do not initialize the callback function pointers in the data structure, leave them set to NULL.

Send Data API Functions

 

These API functions are provided to send data to the library for buffering.

 

GetGraphFootNote()/SetGraphFootNote()

 

GetGraphGroupsLabel()/SetGraphGroupsLabel()

 

GetGraphGroupsTitle()/SetGraphGroupsTitle()

 

GetGraphSeriesLabel()/SetGraphSeriesLabel()

 

GetGraphSeriesTitle()/SetGraphSeriesTitle()

 

GetGraphSubTitle()/SetGraphSubTitle()

 

GetGraphTitle()/SetGraphTitle()

 

GetGraphY1AxisTitle()/SetGraphY1AxisTitle()

 

GetGraphY2AxisTitle()/SetGraphY2AxisTitle()

 

GetGraphDataInfo()/SetGraphData()

 

See the PGSDK API Guide for a description of these API functions that support the send data interface.