Page 1 of 2

Bottom axis labels

Posted: Wed Apr 05, 2006 12:45 pm
by 9340420
Is there a property or a way for me to check whether all labels on the bottom axis of a barchart are visible.

I would like to automatically adjust the display of those labels so that they are all visible. For example, if they are not all visible, try to set the LabelAlternate to True, if still not all visible, set the LabelAngle to 90.

Any idea how I can do this?

Posted: Wed Apr 05, 2006 12:53 pm
by narcis
Hi Normand,

You may check it in the OnGetAxisLabel event. There you have the ValueIndex parameter so that you can check if all points in a series had their labels drawn.

Posted: Wed Apr 05, 2006 1:38 pm
by 9340420
I tried the following code:

IF (Sender = BarChart.BottomAxis) AND (ValueIndex > -1) and (LabelText <> '') then begin
Inc(NbLabels);
end;

But NbLabels is always equal to the number of values, and does not correspond to the number of labels drawn on the bottom axis.

Posted: Tue Apr 11, 2006 11:56 am
by Pep
Hi,

you could use similar code to the following, cheking how many labels has been displayed, and if they are less than the Series count set as Alternate, and the same should work when angle is necessary :

Code: Select all

Function IsFloatingPoint(const S : string): boolean;
  Var
    iCount  : integer;
    fNumber : extended;
  begin
     Val(s, fNumber, iCount);
     result := (iCount>0);
  end;

procedure TForm1.Chart1GetAxisLabel(Sender: TChartAxis;
  Series: TChartSeries; ValueIndex: Integer; var LabelText: String);
begin
  if (Sender = Chart1.Axes.Bottom) and  not IsFloatingPoint(labeltext) then
    inc(i);
end;

procedure TForm1.BitBtn1Click(Sender: TObject);
begin
  if i<Series1.Count then
    Chart1.Axes.Bottom.LabelsAlternate:=True;
end;

Posted: Thu Apr 13, 2006 12:19 am
by 9340420
Sorry but I have no idea what you are talking about.

Suppose I have 20 labels associated with 20 values and I need to draw a barchart. If the barchart is too narrow, it may draw only 8 or 9 labels. None of them are numerical values. Even if I implement your method, I will always get back a value of 20. What I want is to know, if only 8 labels were drawn (because there was only space for 8), I want to get this number of 8.

I may then compare this number with the actual number of labels to be displayed and take corrective action (reducing font size, changing the angle, etc.).

Posted: Tue Apr 18, 2006 10:54 am
by Pep
Hi,
back a value of 20. What I want is to know, if only 8 labels were drawn (because there was only space for , I want to get this number of 8.
This value is not calculated automatically, the only way to know how many labels has been displayed in the Axis would be by checking each axis label size and compare the sum of all with the axis size.

If you are source code customer you can add a var which saves this value in the TeEngine.pas unit.

I've added this feature on our wish list to be considered for further releases.

Posted: Tue Apr 18, 2006 11:25 am
by 9340420
the only way to know how many labels has been displayed in the Axis would be by checking each axis label size and compare the sum of all with the axis size..
Actually, it should be the size of the longest one multiplied by the total number of labels, since while most labels may be short, and may be displayed, a longer label may still not be displayed because of its size. And I would also have to take into account the minimum separation property.
I've added this feature on our wish list to be considered for further releases.
A function that returns either the number of labels displayed or simply a boolean value indicating whether some labels were not drawn would be good enough.

Thanks!

Posted: Mon Jun 16, 2008 1:59 pm
by 9336299
Pep wrote:I've added this feature on our wish list to be considered for further releases.
Hi Pep,

Sorry for bumping this topic. Is this feature available in 7.12?
If not, please tell me how to know if all labels are displayed before drawing chart?

I'm try to set the LabelAngle to 90 like Normand, but I afraid using OnGetAxisLabel will make chart blinked.

Thanks in advance!

Posted: Mon Jun 16, 2008 2:33 pm
by narcis
Hi SiA,

No, this feature hasn't been implemented.

To achieve what you request you may be interested on reading this thread where almost all options about this subject were being discussed. This is a TeeChart for .NET thread but the same applies to the VCL version. Notice this is a long thread (several pages) and what you are looking for may not appear until last pages.

Hope this helps!

Posted: Tue Jun 17, 2008 5:15 am
by 9336299
Hi Narcís,

Thanks for the interesting thread. It took me a lot of time to read :?
I can see why you remember it for such a long time :wink:

Here are some results:
1/ I have many charts without using custom labels. Does the method you and Werner mentioned at the end still work?

2/ I found the Bitmap() method is very helpful. I think what it does is drawing the chart in memory, not the real GUI. Is it right? What is the equivalent method in VCL? I found TeeCreateBitmap, but not sure if it's right.

3/ Actually, I have my own solution to calculate angle:

Code: Select all

function CalculateAngle(
  ALabelSeries: TChartSeries): integer;
var
  i, ItemCount: Integer;
  OldLabelPos, OldLabelSize, tmpLabelPos, tmpLabelSize: Integer;
  CanDraw, NoLabelSeries: Boolean;
  BottomAxis: TChartAxis;
  CurrentValue: Double;
  CurrentLabel: String;
begin
  OldLabelPos := -1;
  OldLabelSize := 0;
  CanDraw := True;
  NoLabelSeries := ALabelSeries = nil;
  Chart.Repaint; //Chart.TeeCreateBitmap;
  BottomAxis := Chart.BottomAxis;
  if NoLabelSeries then
    ItemCount := BottomAxis.Items.Count
  else
    ItemCount := ALabelSeries.Count;


  for i := 0 to ItemCount-1 do
  begin
    if NoLabelSeries then
    begin
      CurrentValue := BottomAxis.Items[i].Value;
      CurrentLabel := BottomAxis.Items[i].Text;
    end
    else
    begin
      CurrentValue := ALabelSeries.XValues[i];
      CurrentLabel := ALabelSeries.XLabel[i];
    end;

    if (CurrentValue >= BottomAxis.Minimum) and
       (CurrentValue <= BottomAxis.Maximum) then
    begin
      tmpLabelPos := BottomAxis.CalcPosValue(CurrentValue);
      tmpLabelSize := Chart.Canvas.TextWidth(CurrentLabel);

      if (OldLabelPos<>-1) then
      begin
        tmpLabelSize := tmpLabelSize div 2;

        if tmpLabelPos >= OldLabelPos then
          CanDraw := (tmpLabelPos-tmpLabelSize-20) >= (OldLabelPos+OldLabelSize)
        else
          CanDraw := (tmpLabelPos+tmpLabelSize+20) <= (OldLabelPos-OldLabelSize);

        if not CanDraw then break;

        OldLabelPos := tmpLabelPos;
        OldLabelSize := tmpLabelSize;
      end
      else
      begin
        OldLabelPos := tmpLabelPos;
        OldLabelSize := tmpLabelSize div 2;
      end;
    end;
  end;

  if not CanDraw
    then Result := 90
    else Result := 0;
end;
When I use the normal labels, I input the series that generates labels in bottom axis.
When I use custom labels, I put nil as an input.
It works quite well.
However, I don't want to call Chart.Repaint as it draw chart in GUI and give blinking effect. Changing it to Chart.TeeCreateBitmap gives me the same result (most of the times), but it crash sometime.
I want to know what it really does.

4/ By the way, I think:

Code: Select all

      tmpLabelPos := BottomAxis.CalcPosValue(CurrentValue);
      tmpLabelSize := Chart.Canvas.TextWidth(CurrentLabel);
do not give right values every times. Am I using it properly in this case?

Posted: Tue Jun 17, 2008 7:25 am
by narcis
Hi SiA,
1/ I have many charts without using custom labels. Does the method you and Werner mentioned at the end still work?
Yes, I think this should still work.
2/ I found the Bitmap() method is very helpful. I think what it does is drawing the chart in memory, not the real GUI. Is it right? What is the equivalent method in VCL? I found TeeCreateBitmap, but not sure if it's right.
Yes, the VCL equivalent is this:

Code: Select all

Chart1.Draw;
4/ By the way, I think:
Code:
tmpLabelPos := BottomAxis.CalcPosValue(CurrentValue);
tmpLabelSize := Chart.Canvas.TextWidth(CurrentLabel);
do not give right values every times. Am I using it properly in this case?
You may need to use them after calling the Draw method.

Posted: Tue Jun 17, 2008 8:38 am
by 9336299
Hi Narcís,
Yes, the VCL equivalent is this:

Code: Select all

Chart1.Draw;
This method draws the chart to GUI. I don't want to draw chart.
You may need to use them after calling the Draw method.
As I said, this method just draw chart to GUI and doesn't update anything. I always get wrong values when call:

Code: Select all

tmpLabelPos := BottomAxis.CalcPosValue(CurrentValue);
tmpLabelSize := Chart.Canvas.TextWidth(CurrentLabel); 
Let me summary what I need:
I want to update chart (all chart values) without drawing it to GUI

I cannot use your solution because it uses custom labels. I don't want to use custom labels in some charts of my project.
Please, could you tell a way to get right length values without doing anything to GUI?

Posted: Tue Jun 17, 2008 8:46 am
by narcis
Hi SiA,

I'm not sure if you want to get maximum label width or the number of visible labels. For the first option you could use MaxLabelsWidth property:

Code: Select all

Chart1.Axes.Bottom.MaxLabelsWidth
If this doesn't help don't hesitate to let us know.

Posted: Tue Jun 17, 2008 10:09 am
by 9336299
Hi Narcís,

Chart.Axes.Bottom.MaxLabelsWidth is the width of longest label or the width of all visible labels?

Anyway, I don't want to get either of them.

I want to get the width of each visible label
-and-
I want to get the position of each visible label

Posted: Tue Jun 17, 2008 11:12 am
by yeray
Hi SiA,

I'm not able to reproduce it here. could you try the following code?
Note that there's a line series, a memo and a button added at design time.

Code: Select all

procedure TForm1.Button1Click(Sender: TObject);
var i: integer;
begin
  Memo1.Clear;

  for i:=0 to Chart1.Axes.Left.Items.Count-1 do
  begin
    if Chart1.Axes.Left.Items.Item[i].Visible then
    Memo1.Lines.Add('label: ' + floattostr(Chart1.Axes.Left.Items.Item[i].Value) +
    '   width: ' + inttostr(Chart1.Axes.Left.Items.Item[i].Width) + '   Left: ' +
    inttostr(Chart1.Axes.Left.Items.Item[i].ShapeBounds.Left) + '   Top: ' +
    inttostr(Chart1.Axes.Left.Items.Item[i].ShapeBounds.Top));

  end;
end;