Recent

Author Topic: [Solved] Chart DateTime Axis with Hours only has too many values  (Read 1865 times)

Wilko500

  • Full Member
  • ***
  • Posts: 180
I have been experimenting with TChart for a little while and have managed to figure out most of what I need but how to reduce the number of marks on the bottom axis has me stumped.

The chart is created with all defaults plus the following code.  The function GetChartDate ensures that the time portion of a date runs from 00:00:00 to 23:59:59 ie from beginning to end of a single day
Code: Pascal  [Select][+][-]
  1. Chart2.LeftAxis.Range.Min:=0;
  2. Chart2.LeftAxis.Range.Max:=4200;
  3. Chart2.LeftAxis.Range.UseMax:=True;
  4. Chart2.LeftAxis.Range.UseMin:=True;
  5. Chart2.LeftAxis.Grid.Color:=clTeal;
  6. Chart2.LeftAxis.Grid.Style:=psSolid;
  7. Chart2.LeftAxis.Marks.LabelFont.Name:='Tahoma';
  8. Chart2.LeftAxis.Marks.LabelFont.Size:=8;
  9. Chart2.LeftAxis.Title.LabelFont.Name:='Tahoma';
  10. Chart2.LeftAxis.Title.LabelFont.Size:=8;
  11. Chart2.LeftAxis.Title.Distance:=8;
  12. Chart2.LeftAxis.Title.Caption:='Watt';
  13. Chart2.LeftAxis.Title.Visible:=True;
  14. Chart2.LeftAxis.ZPosition:=0;
  15.  
  16. Chart2.BottomAxis.Range.Min:=GetChartDate(Now, '00:00:00');
  17. Chart2.BottomAxis.Range.Max:=GetChartDate(Now, '23:59:59');
  18. Chart2.BottomAxis.Range.UseMin:=True;
  19. Chart2.BottomAxis.Range.UseMax:=True;
  20. Chart2.BottomAxis.Grid.Color:=clTeal;
  21. Chart2.BottomAxis.Grid.Style:=psSolid;
  22. Chart2.BottomAxis.Marks.LabelFont.Name:='Tahoma';
  23. Chart2.BottomAxis.Marks.LabelFont.Size:=8;
  24. Chart2.BottomAxis.Title.LabelFont.Name:='Tahoma';
  25. Chart2.BottomAxis.Title.LabelFont.Size:=8;
  26. Chart2.BottomAxis.Title.Caption:='Time on ' + FormatDateTime('dd mmm yyyy', Now);
  27. Chart2.BottomAxis.Title.Visible:=True;
  28. Chart2.BottomAxis.ZPosition:=1;
  29. Chart2.BottomAxis.Marks.Source:=DateTimeIntervalChartSource1;
  30. Chart2.BottomAxis.Marks.Style:=smslabel;
  31.  
  32. Chart2.Margins.Left:=0;
  33. Chart2.Margins.Right:=0;
  34. Chart2.Margins.Top:=0;
  35. Chart2.Margins.Bottom:=1;
  36.  
  37. Chart2.BackColor:=ClBlack;
  38. Power.SeriesColor:=clYellow;
  39. Power.ZPosition:=2;
  40.  
  41. DateTimeIntervalChartSource1.DateTimeFormat:='h'; //'hh:nn' + LineEnding + 'dd-mm';
  42.  
I would like the hours axis to be 0 4 8 12 etc instead of 0 2 4 6.  I have experimented with changing interval values and options but can't get anything to change to bottom axis. I feel that I'm missing something obvious?

« Last Edit: September 26, 2025, 06:08:10 pm by Wilko500 »
MacBook Pro mid 2015 with OS Monterey 12.7.6
FPC 3.2.3 Lazarus 3.7
FPC 3.2.2 Lazarus 3.4

wp

  • Hero Member
  • *****
  • Posts: 13221
Re: Chart DateTime Axis with Hours only has too many values
« Reply #1 on: August 19, 2025, 12:52:11 am »
Yes, it's difficult to find the correct settings if you have special requirements in mind. In such a case, and in particular, if you do not want to zoom into the data, it is easier to remove the DateTimeChartSource and replace it by a TListChartSource populated such that it only contains the desired data points and labels:

Code: Pascal  [Select][+][-]
  1. procedure TForm1.PopulateTimeLabels(ARange: TChartRange);
  2. const
  3.   EPS = 0.001 / (24 * 60 * 60);   // 1 ms tolerance
  4. var
  5.   dt: TDateTime;
  6. begin
  7.   ListChartSource1.BeginUpdate;
  8.   try
  9.     ListChartSource1.Clear;
  10.     // Iterate over the range
  11.     dt := ARange.Min;
  12.     while dt <= ARange.Max do
  13.     begin
  14.       if SameValue(dt, ARange.Max, EPS) then
  15.         ListChartSource1.Add(dt, 0, '24')     // Without this the last time label would be '0' which looks strange to me under these circumstances
  16.       else
  17.         ListChartSource1.Add(dt, 0, FormatDateTime('h', dt));
  18.       dt := dt + 4/24;    // Advance dt in 4-hour steps
  19.     end;
  20.   finally
  21.     ListChartSource1.EndUpdate;
  22.   end;
  23. end;

Call this after setting the axis range:
Code: Pascal  [Select][+][-]
  1.   [...]
  2.   Chart2.BottomAxis.Range.Min:=GetChartDate(Now, '00:00:00');
  3.   Chart2.BottomAxis.Range.Max:=GetChartDate(Now, '23:59:59');
  4.   Chart2.BottomAxis.Range.UseMin:=True;
  5.   Chart2.BottomAxis.Range.UseMax:=True;
  6.   PopulateTimeLabels(Chart2.BottomAxis.Range);
  7.   Chart2.BottomAxis.Grid.Color:=clTeal;
  8.   Chart2.BottomAxis.Grid.Style:=psSolid;
  9.   Chart2.BottomAxis.Marks.LabelFont.Name:='Tahoma';
  10.   Chart2.BottomAxis.Marks.LabelFont.Size:=8;
  11.   Chart2.BottomAxis.Title.LabelFont.Name:='Tahoma';
  12.   Chart2.BottomAxis.Title.LabelFont.Size:=8;
  13.   Chart2.BottomAxis.Title.Caption:='Time on ' + FormatDateTime('dd mmm yyyy', Now);
  14.   Chart2.BottomAxis.Title.Visible:=True;
  15.   Chart2.BottomAxis.ZPosition:=1;
  16.   Chart2.BottomAxis.Marks.Source:=ListChartSource1;
  17.   Chart2.BottomAxis.Marks.Style:=smslabel;
  18.   [...]
  19.  

Wilko500

  • Full Member
  • ***
  • Posts: 180
Re: Chart DateTime Axis with Hours only has too many values
« Reply #2 on: August 19, 2025, 11:42:52 am »
Thank you. As it happens I do not want to zoom onto the data so that is a promising start. However my data is collected and displayed on the chart in real time. Does that mean this approach will not work? 

To clarify, the blank chart is created on program start, cleared and reset at midnight. My program monitors the inverter 24/7, and will provide either zero when it is offline or the power value. The chart is updated by a timer every 1 minute.
I’m away from just now so I can’t try out your code yet but I will do asap.

However the untidy “0” at the end of the axis was another question waiting on the wings and I agree it looks untidy. Secondly I also have other charts in my project that are using historical data sets and those charts have lots of issues with d, m, y labels which are somewhat unpredictable and seem to vary with the number of data points and data range. The historical data varies from 1 day to inverter lifetime (12 years) and for charts up to data range of 1 year I can “manage” the labels by using a calculated interval min/max based on the number of points and the width of the chart.  Always though, last label is missing. I think your approach will be very helpful for those
MacBook Pro mid 2015 with OS Monterey 12.7.6
FPC 3.2.3 Lazarus 3.7
FPC 3.2.2 Lazarus 3.4

Wilko500

  • Full Member
  • ***
  • Posts: 180
Re: Chart DateTime Axis with Hours only has too many values
« Reply #3 on: August 19, 2025, 10:18:37 pm »
Ok WP, your code snippet tested and I'm pleased to say that it works. However for the benefit of any others reading this thread I did have trouble with TChartRange and procedure SameValue.  After a bit of searching I found the necessary units, TATypes for TChartRange and Math for SameValue.

A further small tweak needs for my code to get the 24 entry on the bottom axis. I had set the range max time element as GetChartDate(Now, '23:59:59')  and then the 24 label was missing, blank instead.  On reflection this was actually correct because my range max did not include 24, it stopped one second short.  I changed it to GetChartDate(IncDay(Now), '00:00:00')  and label 24 appeared.

I am now running a test on my live system that will take at least 24 hours.  I have a suspicion that when the chart is cleared and reset at midnight the 24 label will vanish.  I will post again when I get test results.

Thank you for your help
MacBook Pro mid 2015 with OS Monterey 12.7.6
FPC 3.2.3 Lazarus 3.7
FPC 3.2.2 Lazarus 3.4

wp

  • Hero Member
  • *****
  • Posts: 13221
Re: Chart DateTime Axis with Hours only has too many values
« Reply #4 on: August 19, 2025, 10:36:53 pm »
After a bit of searching I found the necessary units, TATypes for TChartRange and Math for SameValue.
This is what I am doing when I run into this: your code contains a line "Chart2.BottomAxis.Range.Min:=GetChartDate(Now, '00:00:00')" - this is where the Range is accessed in a way accepted by the compiler. I hold the CTRL key down and click on the word "Range" --> the IDE opens unit TAChartAxis in which the property TChartAxis.Range is declared (line "property Range: TChartRange read FRange write SetRange"). It tells me that the property Range is of type TChartRange. Now I do the same with" TChartRange": CTRL+click on it, and the IDE will open the unit in which TChartRange is declared. In this case, this is unit TATypes - and this is what is missing from your uses clause.

As for the function SameValue, you can do the same when you know a unit in which it is called (for example in unit TAMath of TAChart). If you don't know such a unit this method does not work. There is some unit-finding feature in the Cody package which many users love. But I never used it... My standard way then is to go to the browser and type the search word, together with "Lazarus" or "Delphi", in the seach bar. Usually the missing unit can be found in one of the first hits.

Wilko500

  • Full Member
  • ***
  • Posts: 180
Re: Chart DateTime Axis with Hours only has too many values
« Reply #5 on: August 19, 2025, 10:58:43 pm »
Thank you for that tip, ctrl click in IDE. That will get used in the future.  I got Math for procedure SameValue from Google. It's often first point of call.  I note that I am often offered Google's AI attempt at answering programming questions.  While it is not perfect and its code snippets are often lacking it will usually give a good idea of how to fix a problem and that often leads to further search clues. 
MacBook Pro mid 2015 with OS Monterey 12.7.6
FPC 3.2.3 Lazarus 3.7
FPC 3.2.2 Lazarus 3.4

Wilko500

  • Full Member
  • ***
  • Posts: 180
Re: Chart DateTime Axis with Hours only has too many values
« Reply #6 on: September 26, 2025, 06:05:26 pm »
@wp, an update.  It's taken me a while to get back to this having been sidetracked by memory leaks.  Your code snippet was very helpful.  I decided to try it out with values other than 4 and found that for example with 2 the last label was not displayed.  I spent a while trying to figure out why but did not find a complete solution.  I think it was the loop test that didn't actually find the last value so label not displayed.  I also tried changing the precision value but that didn't help me. 
Then I changed the loop test and added an additional line after the loop finished.  The result was code that will do any value between 2 and 23.  The last label is always displayed, see pic with hour interval = 5.  In the code aChtRec is a record containing chart parameters so a single CreateCharts procedure can create all 7 of my charts. Thank you :)

As a side note being able to force the last label to always be drawn was on my todo list so you actually solved two problems for me.
Code: Pascal  [Select][+][-]
  1. Procedure PopulateTimeLabels(ARange: TChartRange; AListChartSource:TListChartSource; AChtRec: ChartRec);
  2. var
  3.   dt: TDateTime;
  4. begin
  5.   AListChartSource.BeginUpdate;
  6.   try
  7.     AListChartSource.Clear;
  8.     // Iterate over the range
  9.     dt := ARange.Min;
  10.     while dt <= ARange.Max do
  11.     begin
  12.       If SameDateTime(dt, ARange.Max) Then
  13.        // Form1.ListChartSource1.Add(dt, 0, '24')     // Without this the last time label would be '0' which looks strange to me under these circumstances
  14.       else
  15.         AListChartSource.Add(dt, 0, FormatDateTime('h', dt));
  16. //      dt := dt + 2/24;    // Advance dt in 4-hour steps
  17.       dt := dt + AChtRec.HrSteps/24;    // Advance dt in 4-hour steps
  18.     end;
  19.     AListChartSource.Add(ARange.Max, 0, '24')   //Needs this for 2 hr steps else no label
  20.   finally
  21.     AListChartSource.EndUpdate;
  22.   end;
  23.  
  24. End;{Procedure PopulateTimeLabels}  
MacBook Pro mid 2015 with OS Monterey 12.7.6
FPC 3.2.3 Lazarus 3.7
FPC 3.2.2 Lazarus 3.4

 

TinyPortal © 2005-2018