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

Message ListMessage List     Post MessagePost Message

  Keeping the x and y-axis in same scale in cartesian coordinate system
Posted by Seppo on Aug-18-2012 16:23
Hi,
is there an option which allows one to keep x and y axis in same scale when drawing a
chart using cartesian coordinate system?

As an example, when one creates lines between x,y points (-2,2), (2,2), (2,-2), (-2,-2)
would produce a perfect square regardless of the size or shape of the plot area.

--
Seppo

  Re: Keeping the x and y-axis in same scale in cartesian coordinate system
Posted by Peter Kwan on Aug-20-2012 23:58
Hi Seppo,

Normally, ChartDirector will automatically scale the x-axis based on your x-data values, and the y-axis based on your y-data values. In your case, you would like the x and y axes to be scaled so that both axis has the same pixel scale (that is, the value represented by each pixel is the same for both axes). To do this, you would need to inform ChartDirector how to scale the axes. I think you may try something like (in PHP 5):

$xValuePerPixel = ($maxXData - $minXData) / $c->getPlotArea()->getWidth();
$yValuePerPixel = ($maxYData - $minYData) / $c->getPlotArea()->getHeight();
$valuePerPixel = max($xValuePerPixel, $yValuePerPixel);
if ($valuePerPixel > $xValuePerPixel) {
    $delta = ($valuePerlPixel - $xValuePerPixel) * $c->getPlotArea()->getWidth();
    $maxXData = $maxXData + $delta / 2;
    $minXData = $minXData - $delta / 2;
}
if ($valuePerPixel > $yValuePerPixel ) {
    $delta = ($valuePerlPixel - $yValuePerPixel) * $c->getPlotArea()->getHeight();
    $maxYData = $maxYData + $delta / 2;
    $minYData= $minYData- $delta / 2;
}

$c->xAxis->setLinearScale($minXData, $maxXData);
$c->xAxis->setRounding(false, false);

$c->yAxis->setLinearScale($minYData, $maxYData);
$c->yAxis->setRounding(false, false);

In the above code, $maxData, $minXData, $maxYData, $minYData are the max and min x and y values you would like to use as the axis scale. The code extends one of the scale so that the value per pixel of both axes become the same. Note that the above code assumes the x-axis is the horizonal axis, and the y-axis is the vertical axis.

Hope this can help.

Regards
Peter Kwan

  Re: Keeping the x and y-axis in same scale in cartesian coordinate system
Posted by Seppo on Aug-21-2012 01:42
Attachments:
Peter,
thanks for the answer.

I was actually trying exactly the same. The problem is that for some reason the $c-
>xAxis-
>setLinearScale() does not do anything unless I give value for the majorTickInc-
parameter. Even then the axis labels are not recalculated.

Here is the result how the chart looks like with majorTickInc set to 2 (original scale was
from -6 to 24.

--
Seppo
mydbr_chart.png

  Re: Keeping the x and y-axis in same scale in cartesian coordinate system
Posted by Seppo on Aug-21-2012 15:53
Peter,
took a closer look at this. Calling packPlotArea before setLinearScale was blocking the
setLinearScale to work.

I'm closer, but not yet there.

Your algorithm taking min and max values from the data and scales the chart based on
that. Unfortunately, the min(DataX) cannot be used to scale the axis as there is padding
inside the chart and the padding from x and y are different resulting distorted scaling.

I originally tried to take getMinValue() and getMaxValue() from both axis and then use
your formula to recalculate the axis. This would give correct scaling, but unfortunately
getMinValue() is only available after the axis are layed out (layoutAxes/packPlotArea) and
then setLinearScale does no longer work.

Any help would be appreciated.

--
Seppo

P.S A feature request for an option to say that "keep axis in same scale".

  Re: Keeping the x and y-axis in same scale in cartesian coordinate system
Posted by Peter Kwan on Aug-22-2012 20:30
Attachments:
Hi Seppo,

I have just tested my code based on the "4 Quadrant Chart" sample code, and it appears to work in my case.

You mentioned that there are "padding" inside the chart. By "padding", I assume you mean that the scale can be wider than the min and max values passed to setLinearScale, because ChartDirector automatically extends the scale to the nearest label position. But in my code, the scale extension is already disabled using setRounding(false, false). So there is no scale extension.

Also, in my original code, the $maxXData, $minXData, $maxYData, $minYData are the max and min x and y values "you would like to use as the axis scale". They are not necessarily the min and max data values. If you would like to have paddings (as my code already disable automatic padding), you can always set the $maxXData, $minXData, $maxYData, $minYData to allow for the paddings. For example:

$maxXData = max($xData);
$minXData = min($xData);
//extend by 10% for the padding on both side of the scale
$xExtension = ($maxXData - $minXData) * 0.1;
$maxXData += $xExtension;
$minXData -= $xExtension;

We will enter your suggestion into our wish list for future versions of ChartDirector.

Hope this can help.

Regards
Peter Kwan
fourq.php
<?php
require_once("../lib/phpchartdir.php");

# XY points for the scatter chart
$dataX0 = array(100, 150, 60, -120, 140, -80, 130, -130, 160, 120, 105);
$dataY0 = array(130, 150, 80, 110, -110, -105, -130, 115, -170, 125, 125);

# Create a XYChart object of size 600 x 300 pixels, with a light blue (ccccff)
# background, a black border, and 1 pixel 3D border effect
$c = new XYChart(600, 480, 0xccccff, 0x000000, 1);

# Add a title box to the chart using 16 pts Arial Bold Italic font, with white text
# on deep blue background
$textBoxObj = $c->addTitle("Four Quadrant Chart Demonstration", "arialbi.ttf", 16,
    0xffffff);
$textBoxObj->setBackground(0x000080);

# Set the plotarea at (20, 60) and of size 560 x 360 pixels, with grey (808080)
# border, and light grey (c0c0c0) horizontal and vertical grid lines. Set 4 quadrant
# coloring, where the colors of the quadrants alternate between lighter and deeper
# grey (dddddd/eeeeee)
$plotAreaObj = $c->setPlotArea(20, 60, 560, 360, -1, -1, 0x808080, 0xc0c0c0, 0xc0c0c0
    );
$plotAreaObj->set4QBgColor(0xdddddd, 0xeeeeee, 0xdddddd, 0xeeeeee);

# Set 4 quadrant mode, with both x and y axes symetrical around the origin
$c->setAxisAtOrigin(XYAxisAtOrigin, XAxisSymmetric + YAxisSymmetric);

# Add a legend box at (300, 460) (bottom center of the chart) with horizontal layout.
# Use 8 pts Arial Bold font.
$legendBox = $c->addLegend(300, 460, false, "arialbd.ttf", 8);
$legendBox->setAlignment(BottomCenter);

# Set legend box background to light grey (dddddd) with a black border
$legendBox->setBackground(0xdddddd, 0);

# Set left/right margin to 20 pixels and top/bottom margin to 5 pixels
$legendBox->setMargin2(20, 20, 5, 5);

# Add a titles to axes
$c->xAxis->setTitle("Alpha Index");
$c->yAxis->setTitle("Beta Index");

# Set axes width to 2 pixels
$c->xAxis->setWidth(2);
$c->yAxis->setWidth(2);

# Add scatter layer, using 15 pixels red (ff33333) X shape symbols
$c->addScatterLayer($dataX0, $dataY0, "Group A", Cross2Shape(), 15, 0xff3333);

$minXData = min($dataX0);
$maxXData = max($dataX0);
$minYData = min($dataY0);
$maxYData = max($dataY0);

$xValuePerPixel = ($maxXData - $minXData) / $c->getPlotArea()->getWidth();
$yValuePerPixel = ($maxYData - $minYData) / $c->getPlotArea()->getHeight();
$valuePerPixel = max($xValuePerPixel, $yValuePerPixel);
if ($valuePerPixel > $xValuePerPixel) {
    $delta = ($valuePerPixel - $xValuePerPixel) * $c->getPlotArea()->getWidth();
    $maxXData = $maxXData + $delta / 2;
    $minXData = $minXData - $delta / 2;
}
if ($valuePerPixel > $yValuePerPixel ) {
    $delta = ($valuePerlPixel - $yValuePerPixel) * $c->getPlotArea()->getHeight();
    $maxYData = $maxYData + $delta / 2;
    $minYData= $minYData- $delta / 2;
}

$c->xAxis->setLinearScale($minXData, $maxXData);
$c->xAxis->setRounding(false, false);

$c->yAxis->setLinearScale($minYData, $maxYData);
$c->yAxis->setRounding(false, false);

# Output the chart
header("Content-type: image/png");
print($c->makeChart2(PNG));
?>

  Re: Keeping the x and y-axis in same scale in cartesian coordinate system
Posted by Seppo on Aug-23-2012 14:57
Peter,
you are right about the ability to calculate the parameters to setLinearScale in a
predefined chart.

The problem is that when a chart is a dynamic chart featuring lagend and possibly rotated
labels on axis, laying out the chart without using packPlotArea becomes almost possible.
This is only a problem with cartesian coordinates as packPlotArea does not keep scale of
the axis and setLinearScale does not work after packPlotArea call has been issued.

Having an option to define a chart to use cartesian coordinates would be a welcomed
addition to already exellent product.

--
Seppo

  Re: Keeping the x and y-axis in same scale in cartesian coordinate system
Posted by Peter Kwan on Aug-23-2012 23:47
Hi Seppo,

I can see the root cause of the issue now.

To pack the plot area, ChartDirector must know the axis labels first. (The pack plot area is to adjust the plot area size so that the axis labels stay within a given bound.) However, to keep the aspect ratio 1:1, the axis scale (and hence the labels) must be adjusted based on the plot area size. This creates circular dependencies.

For the option to keep the aspect ratio 1:1, it seems it is not easy to implement if there is also packPlotArea due to the circular dependencies. The only method I can think of is by "trying" and "guessing".

For your case, may be you can consider the following approach:

(a) Try to use a layout method that does not require resizing the plot area. For example, you may resize the chart to make more room for the legend box, instead of shrinking the plot area.

(b) Try to pack the plot area to preserve the aspect ratio. For example, suppose in the original code, the width:height ratio is 2:1. After pack plot area, you find out the ratio is no longer 2:1. You can pack the plot area again using different parameters to make it 2:1. (The "packPlotArea" can be called multiple times.)

(c) Create the chart in two pass. In the first pass, the chart is created and then packPlotArea is called. This allows you to determine the plot area size. In the second pass, the chart is created using the plot area size determined in the first pass, and no packPlotArea is called. The axis can then be scaled to ensure the aspect ratio is 1:1.

Hope this can help.

Regards
Peter Kwan