Recent

Author Topic: [solved] Best way to automatically change axis time unit  (Read 6846 times)

Muso

  • Sr. Member
  • ****
  • Posts: 347
[solved] Best way to automatically change axis time unit
« on: December 17, 2021, 10:21:50 pm »
I wrote a program that collects and display values in a TChart LineSeries.

Because most measurements are done within few hours, I chose minutes as unit for the time axis.

Now I need have to perform also measurements running for weeks. Therefore I got the feature request to automatically change the time axis unit from minutes to hours after 600 minutes have passed. Then to days after 720 hours passed.

I don't have an idea what the best strategy is in this case. I write the data to a textfile since the data must be archived this way anyway. There the unit must remain minutes. So only in the UI the unit change must happen.

Has anyone had the same issue and thus a hint for me for a good strategy?
« Last Edit: December 20, 2021, 07:42:45 pm by Muso »

wp

  • Hero Member
  • *****
  • Posts: 9902
Re: Best way to automatically change axis time unit
« Reply #1 on: December 18, 2021, 12:10:50 am »
If you want to display the elapsed time as normal decimal values rather than in "days hour:minute:seconds" notation your task is relatively easy when you take advantage of a LinearAxisTransformation. This allows to multiply or divide numbers by a constant "scale" factor and to shift them by a constant "offset" - we don't need the latter one.

Add a ChartAxisTransformations component to the form and link it to the Transformations property of the chart's BottomAxis. Doubleclick on the component and add a LinearAxisTransformation.

Then load your data file which contains the x values in minutes. Determine the largest x value (Series.GetXMax). When it is less than 600 leave the Scale of the LinearAxisTransform at its default 1.0. When it is less than 10*24 = 43200 set Scale to 60 - this will divide the minutes in the chartsource by 60 and the axis will display hours. And when the max is greater than 43200 set the Scale to 24*60 to display the elapsed minutes in terms of days.

How do I know that Scale must be 60 and not 1/60? I don't... I just play with the numbers, one of the two possibilities must produce the correct axis labels...

Unlike working with the LogAxisTransform and AutoScaleAxisTransform do not set the AxisIndexX of the series to the index of the x axis because we only want to transform the axis mark values, but not the data x values.

Play with the attached demo to understand the principle of this idea.
« Last Edit: December 18, 2021, 12:12:45 am by wp »

Muso

  • Sr. Member
  • ****
  • Posts: 347
Re: Best way to automatically change axis time unit
« Reply #2 on: December 20, 2021, 03:18:22 am »
If you want to display the elapsed time as normal decimal values rather than in "days hour:minute:seconds" notation your task is relatively easy when you take advantage of a LinearAxisTransformation. This allows to multiply or divide numbers by a constant "scale" factor and to shift them by a constant "offset" - we don't need the latter one.

Many thanks, yes this is my task.

Unlike working with the LogAxisTransform and AutoScaleAxisTransform do not set the AxisIndexX of the series to the index of the x axis because we only want to transform the axis mark values, but not the data x values.

Thanks! This was the point I missed. I already had an axis transformation but bound the AxisIndexX.
Now it is working close to what I want, there is only one issue I cannot resolve:

Play with the attached demo to understand the principle of this idea.

- start your demo
- zoom in
- change the unit
result: it jumps out of the zoom

My application case: I let a measurement run today and something happened 5 hours ago, I want to zoom in to this time and since then the time scale can be minutes, I change the unit to minutes.
So I want somehow prevent that the unit change leaves the zoom, or a way to stare the zoom to reset it after the AxisTransformationScale was changed.

Can this be done and if yes, how?

wp

  • Hero Member
  • *****
  • Posts: 9902
Re: Best way to automatically change axis time unit
« Reply #3 on: December 20, 2021, 05:33:09 pm »
- start your demo
- zoom in
- change the unit
result: it jumps out of the zoom

I think the place where the zoom rect is lost is the TChartAxis.StyleChanged method which calls the chart's ZoomFull method:
Code: Pascal  [Select][+][-]
  1. procedure TChartAxis.StyleChanged(ASender: TObject);
  2. begin
  3.   with GetChart do begin
  4.     if (ASender is TAxisTransform) or (ASender is TChartAxisTransformations) then
  5.       ZoomFull;
  6.     Invalidate;
  7.   end;
  8. end;
In principle this could be prevented by excluding from the "if" the case that ASender is a TLinearAxisTransform. But I am rather sure that this will have side-effects somewhere...

Therefore, I offer you a different solution which affects only your application. It is not nice, but works. Extend the RadioGroup OnClick handler of my demo as follows:
Code: Pascal  [Select][+][-]
  1. procedure TForm1.RadioGroup1Click(Sender: TObject);
  2. var
  3.   ext: TDoubleRect;
  4. begin
  5.   ext := Chart1.LogicalExtent;
  6.  
  7.   ScaleAxis;
  8.  
  9.   Chart1.Prepare;
  10.   Chart1.LogicalExtent := ext;
  11. end;
The idea is to store the chart's extent before and restoring it after the scaling operation. The problem is that the way the ZoomFull is handled by the chart does not change the internal LogicalExtent variable, and therefore, the setter of the LogicalExtent property does not execute all the code necessary. But you can call Chart1.Prepare which resets the internal variable and makes the entire SetLogicalExtent code available for execution.

Muso

  • Sr. Member
  • ****
  • Posts: 347
Re: Best way to automatically change axis time unit
« Reply #4 on: December 20, 2021, 07:42:25 pm »

Code: Pascal  [Select][+][-]
  1. procedure TForm1.RadioGroup1Click(Sender: TObject);
  2. var
  3.   ext: TDoubleRect;
  4. begin
  5.   ext := Chart1.LogicalExtent;
  6.   ScaleAxis;
  7.   Chart1.Prepare;
  8.   Chart1.LogicalExtent := ext;
  9. end;

Many thanks. This was also my first thought but did not know that I need the Prepare procedure.
It's a pity that I struggle quite often at such small things. Therefore I am very grateful for the forum and your help in particular.

Allow me this way again big thanks for your amazing new chart features in Laz 2.2. For example the ChartLiveView feature is meanwhile part of most of my programs since measuring time series is a basic task for many chart applications.

 

TinyPortal © 2005-2018