ASE Home Page Products Download Purchase Support About ASE
ChartDirector Support
Forum HomeForum Home   SearchSearch

Message ListMessage List     Post MessagePost Message

  Need to get Bar or Segment Size
Posted by Ivaylo on Jul-12-2016 03:06
I am trying to implement some logic for a stacked bar chart which will allow me to render the data label as a callout in cases where the data segment is too small for a data label to fit inside it.

To do this, I need the following pieces of information:

- Whether the label can be rendered in the data segment.  Ideally, I would leave that logic to ChartDirector but I need to know whether CD decided to render the label or not.  If not, I will render the callout programatically.  If I cannot get this piece of information out of CD, I would need, either:
  -- (preferrably) the height (or width, for axis swapped charts) of the segments so I can "guess" whether CD will render the label, or
  -- the height of the bar so I can calculate the size of each segment and "guess" based on that.

In cases where the segment is too small and I need to render a callout, it is important to position the callout perfectly.  Some of the segments can be very small (practically just a single pixel line), so the callout has to "bubble out" of the correct data segment.  It would therefore be very helpful to be able to retrieve the coordinates of at least one corner of the data segment.  If not, I can probably try to calculate the position of the callout accurately enouhg if I have at least the coordinates of one corner of the bar.

Could you please suggest how I could achieve that?  I am working in PHP.

Thank you,
Ivaylo

  Re: Need to get Bar or Segment Size
Posted by Peter Kwan on Jul-13-2016 00:27
Hi Ivaylo,

It is possible to accurately determine the position of the bar segment. ChartDirector can also automatically hide the data labels if the bar segment is too short. Exactly what is "too short" is configurable using BarLayer.setMinLabelSize. However, there is no API to tell you which label ChartDirector eventually would hide.

So for your case, since you would like to use your own code to draw the "callout" case, may be you can just use your code to draw for the standard case as well. This should be an easy extension of the your code. In brief, your code can ask ChartDirector for the position of a segment. If your code thinks the segment is too short, it draws the labels as a "callout", otherwise it draws the same label in the center of the segment.

The code to determine the position of a segment is as follows (in C#):

.... create chart as usual .....

//auto-scale the axis so we can know the position of the segments
c.layout();

//all the data arrays
double[][] allData = { data0, data1, data2, ... };

//loop to iterate all the segments in all the bars
for (int i = 0; i < data0.Length; ++i)
{
    double sum = 0;
    int centerX = c.getXCoor(i);

    for (int j = 0; j < allData.Length; ++j)
    {
        int lowerY = c.getYCoor(sum);
        int upperY = c.getYCoor(sum += allData[j][i]);
        drawMyLabel(centerX, lowerY, upperY, .......);
    }
}

Hope this can help.

Regards
Peter Kwan

  Re: Need to get Bar or Segment Size
Posted by Ivaylo on Jul-13-2016 01:19
Thank you, Peter, much appreciated!

I might not have much of a choice - your solution may be the only way to go.  Thank you very much for the detailed explanation and code sample!

Kind Regards,
Ivaylo

  Re: Need to get Bar or Segment Size
Posted by Ivaylo on Jul-16-2016 04:26
Hi, Peter!

Thanks again for your help.  So, now I have coordinates, but I am still far from a good solution.  The other half of the problem is the actual rendering of the callouts.  I figure I have two options:

- insert an image of a callout balloon at the desired coordinates and then place the data label inside that.
- somehow draw a callout balloon and place the data label inside.

The first approach seems simpler.  However, whenever I attempt to:

$barChart->getDrawArea()->loadGIF('../images/callout.gif');

... it replaces the entire chart with the image.  The behavoiur seems consistent with the API documentation which says that loadGIF() "will overwrite the current DrawArea. "  However, that prevents me from placing the images on top of the bar chart.  How can I prevent that?

In my mind there would be a "layer" (layer is used loosely here just to illustrate something like a CSS z-index layering of the elements) order like this:

- bar chart with the stacked bars, axis, axis and aggregate labels, legend, etc.
- callout balloon images placed at the coordinates of specific data points (coordinates determined by the bar chart layer)
- data labels centered inside the callout balloons

Can you give me an idea how I would accomplish something like this?  My experiments so far have proved frustrating... :(

Thank you!

Kind Regards,
Ivaylo

  Re: Need to get Bar or Segment Size
Posted by Peter Kwan on Jul-17-2016 01:06
Hi Ivaylo,

There are several methods to draw on the chart surface.

If you want to load an image icon onto the chart surface, you may just add it as CDML text. It is like:

$barChart->addText($xCoor, $yCoor, "<*img=/path/to/callout.gif*>", ......);

Note that the path ChartDirector uses are file system paths, not URLs. So the leading "/" means the root of the file system, not the root of the web document directory.

Also, from experience, using "relative paths" are not reliable. For file system path, the path is relative to the "current working directory". The "current working directory" depends on the OS, web server, PHP configuration, etc., and may or may not be the directory of the script. If you would like to use a relative path, it is suggested you use BaseChart.setSearchPath to set the search path to be the absolute file system path of the running script, which can be obtained as "dirname(__FILE__)". See the "Icon Pie Chart" sample code for an example.

Instead of using BaseChart.addText to add text and images, you can also use the DrawArea objects to add text, images or draw polygons. It is like:

#use makeChart3 to obtain a DrawArea after the chart is drawn. This ensures the
#additional drawings will be on top of the chart
$d = $barChart->makeChart3();

#You can use text to add text. The text can be CDML so it can also add images.
$d->text(....);

#You can also draw polygons directly
$d->polygon(....);

Yet another method to draw shapes on the chart is to add using scatter layers. You can use various shapes for the symbols, and you can put text on the symbols (eg. use Layer.addCustomDataLabel). Note that the scatter layer uses data values for positions, not pixel coordinates, and you can only add a scatter layer before you call "BaseChart.layout" to auto-scale the axis. (ChartDirector needs all the data to auto-scale the axis, so it does not support adding more data after auto-scaling the axis.)

Regards
Peter Kwan

  Re: Need to get Bar or Segment Size
Posted by Ivaylo on Jul-18-2016 22:46
Thank you, Peter, these are some very valuable pointers!

I think I will probably go with the polygon drawing since I realized that the data label can vary in width and a polygon will allow me to adjust for that.

The callouts quickly made charting quite a bit more complicated but now I think I have what I need to tackle it.  Thanks again!

Cheers,
Ivaylo

  Re: Need to get Bar or Segment Size
Posted by Ivaylo on Jul-22-2016 04:13
I am happy to report that, using your suggestions, this worked out quite nicely!

Thank you very much for the timely and precise information!

Kind Regards,
Ivaylo