Scale axes to shown data

TeeChart VCL for Borland/CodeGear/Embarcadero RAD Studio, Delphi and C++ Builder.
Post Reply
RJRJ
Newbie
Newbie
Posts: 4
Joined: Mon Sep 25, 2017 12:00 am

Scale axes to shown data

Post by RJRJ » Thu Dec 14, 2017 3:24 pm

Hi, I want to create an option where with one checkbox it is possible to see last 20 minutes of the graph.

I started with

Code: Select all

  // Graph settings
  if CheckBoxGraphsLast20m.Checked then
    with ChartRaw.BottomAxis do
      begin
        Automatic := False;
        AutomaticMaximum := True;
        AutomaticMinimum := False;
        Minimum := Now-(5/86400);   // 5 secs
      end
  else
    ChartRaw.BottomAxis.Automatic := true;
But when the checkbox is checked,

Y axes do not scale to the visible data points but keep the range concerning the full data set

Which properties/methods should I look into the get the basic effect of only showing the last x minutes of data?

Yeray
Site Admin
Site Admin
Posts: 9612
Joined: Tue Dec 05, 2006 12:00 am
Location: Girona, Catalonia
Contact:

Re: Scale axes to shown data

Post by Yeray » Fri Dec 15, 2017 2:27 pm

Hello,

Here it is a simple example showing how to do what I understand you are trying to achieve:

Code: Select all

uses Series, Math;

const nLastPointsToShow=10;
const oneSecond=1.0/86400.0;

procedure TForm1.FormCreate(Sender: TObject);
var i: Integer;
    tmp: Double;
const initValues=100;
begin
  Chart1.View3D:=False;
  Chart1.Legend.Visible:=False;

  with Chart1.AddSeries(TFastLineSeries) do
  begin
    XValues.DateTime:=True;

    tmp:=Now;
    AddXY(tmp-(oneSecond*initValues), 100+random*10);
    for i:=initValues-1 downto 0 do
      AddXY(tmp-oneSecond*i, YValue[Count-1] + random*10-5);
  end;

  Chart1.Axes.Bottom.LabelStyle:=talPointValue;
  Chart1.Axes.Bottom.LabelsAngle:=90;
  Chart1.Axes.Bottom.DateTimeFormat:='hh:mm:ss';

  Timer1.Interval:=1000;
  Timer1.Enabled:=True;
end;

procedure TForm1.Timer1Timer(Sender: TObject);
begin
  with Chart1[0] do
    AddXY(Now, YValue[Count-1] + random*10-5);

  CheckAxes;
end;

procedure TForm1.CheckAxes;
var i: Integer;
    tmpMax, tmpMin: Double;
begin
  if CBShowLast.Checked then
  begin
    Chart1.Axes.Bottom.AutomaticMinimum:=False;

    with Chart1[0] do
    begin
      Chart1.Axes.Bottom.Minimum:=XValue[Count-nLastPointsToShow];
      Chart1.Repaint;

      tmpMin:=YValue[FirstDisplayedIndex];
      tmpMax:=YValue[FirstDisplayedIndex];

      for i:=Chart1[0].FirstDisplayedIndex+1 to Chart1[0].LastDisplayedIndex do
      begin
        tmpMin:=Min(tmpMin, YValue[i]);
        tmpMax:=Max(tmpMax, YValue[i]);
      end;
    end;

    Chart1.Axes.Left.SetMinMax(tmpMin, tmpMax);
  end
  else
  begin
    Chart1.Axes.Bottom.Automatic:=True;
    Chart1.Axes.Left.Automatic:=True;
  end;
end;

procedure TForm1.CBShowLastClick(Sender: TObject);
begin
  CheckAxes;
end;
Best Regards,
ImageYeray Alonso
Development & Support
Steema Software
Av. Montilivi 33, 17003 Girona, Catalonia (SP)
Image Image Image Image Image Image Please read our Bug Fixing Policy

RJRJ
Newbie
Newbie
Posts: 4
Joined: Mon Sep 25, 2017 12:00 am

Re: Scale axes to shown data

Post by RJRJ » Mon Dec 18, 2017 11:12 am

Thanks for the help.

I have one shared standard bottom ax and three custom Y-axes on the right. I do not fully understand the code because I had no time to read the docs (got to run to airport now), but it seems to work.

Code: Select all

procedure TFormCoultCell2.CheckAxes(const aChart: TChart; const nLastPointsToShow: Cardinal);
var
  I, Ax: Integer;
  tmpMax, tmpMin: Double;
begin
  // 0 and 1 give full scale
  if (nLastPointsToShow > 1) and (aChart.Series[0].Count > nLastPointsToShow) then
    begin
      aChart.Axes.Bottom.AutomaticMinimum:=False;

      with aChart[0] do // Refers to ?
      begin
        aChart.Axes.Bottom.Minimum:=XValue[max(0, Count-nLastPointsToShow)];
        aChart.Repaint;

      // Loop all custom axes (they're all Y-axes on right, sharing standard bottom axe)
      for Ax := 0 to aChart.CustomAxes.Count-1 do
        begin

          tmpMin:=YValue[FirstDisplayedIndex];
          tmpMax:=YValue[FirstDisplayedIndex];

          for I:=aChart[Ax].FirstDisplayedIndex+1 to aChart[Ax].LastDisplayedIndex do
          begin
            tmpMin:=Min(tmpMin, YValue[i]);
            tmpMax:=Max(tmpMax, YValue[i]);
          end;
        end;

        aChart.Axes[Ax].SetMinMax(tmpMin, tmpMax);

      end;
    end
  else
    begin
      aChart.Axes.Bottom.Automatic:=True;

      for Ax := 0 to aChart.CustomAxes.Count-1 do
        aChart.CustomAxes[Ax].Automatic := true;
    end;
end;

Yeray
Site Admin
Site Admin
Posts: 9612
Joined: Tue Dec 05, 2006 12:00 am
Location: Girona, Catalonia
Contact:

Re: Scale axes to shown data

Post by Yeray » Mon Dec 18, 2017 2:53 pm

Hello,

Note aChart[0] gives the first series in the Chart. So in your case, where you seem to have one series assigned to each custom axis, you should calculate the tmpMin and tmpMax using aChart[Ax] YValue. Ie:

Code: Select all

procedure TFormCoultCell2.CheckAxes(const aChart: TChart; const nLastPointsToShow: Cardinal);
var
  I, Ax: Integer;
  tmpMax, tmpMin: Double;
begin
  // 0 and 1 give full scale
  if (nLastPointsToShow > 1) and (aChart.Series[0].Count > nLastPointsToShow) then
    begin
      aChart.Axes.Bottom.AutomaticMinimum:=False;

      with aChart[0] do // Assuming all the series have the same number of XValues
      begin
        aChart.Axes.Bottom.Minimum:=XValue[max(0, Count-nLastPointsToShow)];
        aChart.Repaint;
      end;

      // Loop all custom axes (they're all Y-axes on right, sharing standard bottom axe)
      for Ax := 0 to aChart.CustomAxes.Count-1 do
        begin

          tmpMin:=aChart[Ax].YValue[aChart[Ax].FirstDisplayedIndex];
          tmpMax:=aChart[Ax].YValue[aChart[Ax].FirstDisplayedIndex];

          for I:=aChart[Ax].FirstDisplayedIndex+1 to aChart[Ax].LastDisplayedIndex do
          begin
            tmpMin:=Min(tmpMin, aChart[Ax].YValue[i]);
            tmpMax:=Max(tmpMax, aChart[Ax].YValue[i]);
          end;
        end;

        aChart.Axes[Ax].SetMinMax(tmpMin, tmpMax);
    end
  else
    begin
      aChart.Axes.Bottom.Automatic:=True;

      for Ax := 0 to aChart.CustomAxes.Count-1 do
        aChart.CustomAxes[Ax].Automatic := true;
    end;
end;
Best Regards,
ImageYeray Alonso
Development & Support
Steema Software
Av. Montilivi 33, 17003 Girona, Catalonia (SP)
Image Image Image Image Image Image Please read our Bug Fixing Policy

RJRJ
Newbie
Newbie
Posts: 4
Joined: Mon Sep 25, 2017 12:00 am

Re: Scale axes to shown data

Post by RJRJ » Wed Dec 27, 2017 10:16 am

OK this is what I ended up with.

I have one standard shared X axis, 3 custom Y axes, some of which may or may not have data.

Code: Select all

procedure TFormCoultCell2.CheckAxes(const aChart: TChart; const nLastPointsToShow: Cardinal);
var
  I, S, Ax: Integer;
  tmpMax, tmpMin: Double;
begin
  // 0 and 1 give full scale
  if (nLastPointsToShow > 1) and (aChart[0].Count-nLastPointsToShow > 0) then
    begin
      aChart.Axes.Bottom.AutomaticMinimum:=False;

      with aChart[0] do // Refers to first normal series, all series have same X-data, if data lacks, will have Y=Null
      begin
        aChart.Axes.Bottom.Minimum:=XValue[max(0, Count-nLastPointsToShow)];
        aChart.Repaint;
      end;

      // Loop each series
      for S := 0 to aChart.SeriesCount-1 do
        begin
          tmpMin := high(integer);
          tmpMax := low(integer);

          // If the series have positive amount of points
          if (aChart[S].YValues.Count > nLastPointsToShow) then
            begin

              // Loop the last n points and get min and max
              for I := (aChart[S].YValues.Count-1) downto (aChart[S].YValues.Count -nLastPointsToShow) do
                begin
                  tmpMin:=Min(tmpMin, aChart[S].YValue[I]);
                  tmpMax:=Max(tmpMax, aChart[S].YValue[I]);
                end;

              // Turn of automatic and apply min and max
              aChart[S].CustomVertAxis.AutomaticMinimum := false;
              aChart[S].CustomVertAxis.AutomaticMaximum := false;
              aChart[S].CustomVertAxis.Maximum := tmpMax;
              aChart[S].CustomVertAxis.Minimum := tmpMin;

            end;
        end;
    end

  // Set bottom and all customs to auto
  else
    begin
      aChart.Axes.Bottom.Automatic:=True;

      for Ax := 0 to aChart.CustomAxes.Count-1 do
        aChart.CustomAxes[Ax].Automatic := true;
    end;

end;

RJRJ
Newbie
Newbie
Posts: 4
Joined: Mon Sep 25, 2017 12:00 am

Re: Scale axes to shown data

Post by RJRJ » Fri Dec 29, 2017 3:46 pm

How is it that with

Code: Select all

              // Turn of automatic and apply min and max
              if tmpMax >= tmpMin then
                begin
                  aChart[S].CustomVertAxis.AutomaticMinimum := false;
                  aChart[S].CustomVertAxis.AutomaticMaximum := false;
                  aChart[S].CustomVertAxis.Maximum := tmpMax;
                  aChart[S].CustomVertAxis.Minimum := tmpMin;   // This one
                end;
I get AxisException: 'Axis Maximum Value must be >= Minimum'.

Will remove the = from comparison. I added other things as well, but I still get the same exception.

Code: Select all

              // Turn of automatic and apply min and max
              if (tmpMax > tmpMin) and (assigned(aChart[S].YValues)) and (aChart[S].YValues.Count > 0) then
                begin
                  aChart[S].CustomVertAxis.AutomaticMinimum := false;
                  aChart[S].CustomVertAxis.AutomaticMaximum := false;
                  aChart[S].CustomVertAxis.Maximum := tmpMax;
                  aChart[S].CustomVertAxis.Minimum := tmpMin;
                end;

PS. There is a serie and axis without any data, plus 3 series and 2 axes with data.

Marc
Site Admin
Site Admin
Posts: 1271
Joined: Thu Oct 16, 2003 4:00 am
Location: Girona
Contact:

Re: Scale axes to shown data

Post by Marc » Tue Jan 02, 2018 12:20 pm

Hello,

Likely at the moment that the Maximum is being set it is less than the Minimum (the actual value of Minimum at the moment you're trying to set Maximum). You could check that and set Minimum first or use:

Code: Select all

if tmpMax >= tmpMin then
begin
  aChart[S].CustomVertAxis.SetMinMax(tmpMin,tmpMax);
end;
Regards,
Marc Meumann
Steema Support

Sandra
Site Admin
Site Admin
Posts: 3132
Joined: Fri Nov 07, 2008 12:00 am

Re: Scale axes to shown data

Post by Sandra » Tue Jan 02, 2018 12:52 pm

Hello RJRJ,

To complete the Marc's explanation, you find a simple example below:

Code: Select all

uses Series, Math;

const nLastPointsToShow=10;
const oneSecond=1.0/86400.0;

procedure TForm1.FormCreate(Sender: TObject);
var i,t: Integer;
    tmp: Double;
const initValues=100;
begin
  Chart1.View3D:=False;
  Chart1.Legend.Visible:=False;

  with Chart1.AddSeries(TFastLineSeries) do
  begin
    XValues.DateTime:=True;

    tmp:=Now;
    AddXY(tmp-(oneSecond*initValues), 100+random*10);
    for i:=initValues-1 downto 0 do
      AddXY(tmp-oneSecond*i, YValue[Count-1] + random*10-5);
  end;
    with Chart1.AddSeries(TFastLineSeries) do
  begin
    XValues.DateTime:=True;

    tmp:=Now;
    AddXY(tmp-(oneSecond*initValues), 100+random*10);
    for i:=initValues-1 downto 0 do
      AddXY(tmp-oneSecond*i, YValue[Count-1] + random*10-5);
  end;
    with Chart1.AddSeries(TFastLineSeries) do
  begin
    XValues.DateTime:=True;

    tmp:=Now;
    AddXY(tmp-(oneSecond*initValues), 100+random*10);
    for i:=initValues-1 downto 0 do
      AddXY(tmp-oneSecond*i, YValue[Count-1] + random*10-5);
  end;

  Chart1.Axes.Bottom.LabelStyle:=talPointValue;
  Chart1.Axes.Bottom.LabelsAngle:=90;
  Chart1.Axes.Bottom.DateTimeFormat:='hh:mm:ss';


  Timer1.Interval:=1000;
  Timer1.Enabled:=True;

  Chart1.Draw();
  Chart1.Axes.Left.StartPosition := 0;
  Chart1.Axes.Left.EndPosition := 30;

  Chart1[0].VertAxis := aLeftAxis;
  Chart1[1].CustomVertAxis := Chart1.CustomAxes[0];
  Chart1[2].CustomVertAxis := Chart1.CustomAxes[1];

  Chart1.CustomAxes[0].StartPosition := 30;
  Chart1.CustomAxes[0].EndPosition := 60;
  Chart1.CustomAxes[0].Axis.Color := clRed;

  Chart1.CustomAxes[1].StartPosition := 60;
  Chart1.CustomAxes[1].EndPosition := 100;
  Chart1.CustomAxes[1].Axis.Color := clBlue;


end;

procedure TForm1.Timer1Timer(Sender: TObject);
begin
  with Chart1[0] do
    AddXY(Now, YValue[Count-1] + random*10-5);
  with Chart1[1] do
    AddXY(Now, YValue[Count-1] + random*10-5);
  with Chart1[2] do
    AddXY(Now, YValue[Count-1] + random*10-5);

  CheckAxes(Chart1,nLastPointsToShow);
end;

procedure TForm1.CheckAxes(const aChart: TChart; const nLastPointsToShow: Cardinal);
var
  I, Ax: Integer;
  tmpMax, tmpMin: Double;
begin
  // 0 and 1 give full scale
  if (nLastPointsToShow > 1) and (aChart.Series[0].Count > nLastPointsToShow) then
    begin
      aChart.Axes.Bottom.AutomaticMinimum:=False;

      with aChart[0] do // Assuming all the series have the same number of XValues
      begin
        aChart.Axes.Bottom.Minimum:=XValue[max(0, Count-nLastPointsToShow)];
        aChart.Repaint;
      end;

      // Loop all custom axes (they're all Y-axes on right, sharing standard bottom axe)
      for Ax := 0 to aChart.CustomAxes.Count-1 do
        begin

          tmpMin:=aChart[Ax+1].YValue[aChart[Ax+1].FirstDisplayedIndex];
          tmpMax:=aChart[Ax+1].YValue[aChart[Ax+1].FirstDisplayedIndex];

          for I:=aChart[Ax+1].FirstDisplayedIndex+1 to aChart[Ax].LastDisplayedIndex do
          begin
            tmpMin:=Min(tmpMin, aChart[Ax+1].YValue[i]);
            tmpMax:=Max(tmpMax, aChart[Ax+1].YValue[i]);
          end;
        end;
         aChart.Axes[Ax].SetMinMax(tmpMin, tmpMax);

    end
  else
    begin
      aChart.Axes.Bottom.Automatic:=True;
      for Ax := 0 to aChart.CustomAxes.Count-1 do
        aChart.CustomAxes[Ax].Automatic := true;

    end;
end;

procedure TForm1.CBShowLastClick(Sender: TObject);
begin
  CheckAxes(Chart1,nLastPointsToShow);
end;
Thanks in advance
Regards
Best Regards,
Sandra Pazos / Development & Support
Steema Software
Avinguda Montilivi 33, 17003 Girona, Catalonia
Tel: 34 972 218 797
http://www.steema.com
Image Image Image Image Image Image
Instructions - How to post in this forum

Post Reply