Page 1 of 1

Scale axes to shown data

Posted: Thu Dec 14, 2017 3:24 pm
by 16581937
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?

Re: Scale axes to shown data

Posted: Fri Dec 15, 2017 2:27 pm
by yeray
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;

Re: Scale axes to shown data

Posted: Mon Dec 18, 2017 11:12 am
by 16581937
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;

Re: Scale axes to shown data

Posted: Mon Dec 18, 2017 2:53 pm
by yeray
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;

Re: Scale axes to shown data

Posted: Wed Dec 27, 2017 10:16 am
by 16581937
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;

Re: Scale axes to shown data

Posted: Fri Dec 29, 2017 3:46 pm
by 16581937
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.

Re: Scale axes to shown data

Posted: Tue Jan 02, 2018 12:20 pm
by Marc
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

Re: Scale axes to shown data

Posted: Tue Jan 02, 2018 12:52 pm
by 10050769
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